Compare commits

...

11 Commits

Author SHA1 Message Date
Matthieu Baumann
0c9c4cf9a9 wip 2024-09-25 19:05:37 +02:00
Philip Matsson
ea8af8acb3 Cleaning up 2024-09-25 11:23:40 +02:00
Philip Matsson
fac6c045f4 Add option for manual selection logic 2024-09-25 10:47:56 +02:00
Philip Matsson
b778ce380a Push overlayItems in selector getObjects as an array for consistency 2024-09-24 17:33:32 +02:00
Philip Matsson
d3e0bb4fbc Update select logic 2024-09-24 16:59:41 +02:00
Philip Matsson
81e1eaddee Allow color as function for select types 2024-09-24 16:58:19 +02:00
Philip Matsson
51af4fa2f4 Customize select logic 2024-09-24 16:58:17 +02:00
Philip Matsson
ef86dbd06d Merge branch 'cds-astro:master' into select-impl 2024-09-24 16:34:10 +02:00
Philip Matsson
45f77feeb1 Update polyline select implementation 2024-07-11 12:09:45 +02:00
Philip Matsson
b8820d6f19 Fix typo in polyline BB intersect function 2024-06-19 11:59:05 +02:00
Philip Matsson
98d877b937 Modify objects returned from selector and finish poly select impl 2024-06-19 10:26:49 +02:00
9 changed files with 133 additions and 26 deletions

View File

@@ -146,6 +146,7 @@ import { Polyline } from "./shapes/Polyline";
* @property {boolean} [samp=false] - Whether to enable SAMP (Simple Application Messaging Protocol). * @property {boolean} [samp=false] - Whether to enable SAMP (Simple Application Messaging Protocol).
* @property {boolean} [realFullscreen=false] - Whether to use real fullscreen mode. * @property {boolean} [realFullscreen=false] - Whether to use real fullscreen mode.
* @property {boolean} [pixelateCanvas=true] - Whether to pixelate the canvas. * @property {boolean} [pixelateCanvas=true] - Whether to pixelate the canvas.
* @property {boolean} [manualSelection=false] - When set to true, no selection will be performed, only events will be generated.
* @example * @example
* let aladin = A.aladin({ * let aladin = A.aladin({
target: 'galactic center', target: 'galactic center',
@@ -713,6 +714,7 @@ export let Aladin = (function () {
samp: false, samp: false,
realFullscreen: false, realFullscreen: false,
pixelateCanvas: true, pixelateCanvas: true,
manualSelection: false
}; };
// realFullscreen: AL div expands not only to the size of its parent, but takes the whole available screen estate // realFullscreen: AL div expands not only to the size of its parent, but takes the whole available screen estate

View File

@@ -206,6 +206,12 @@ export let DefaultActionsForContextMenu = (function () {
action(o) { action(o) {
a.select('rect', selectObjects) a.select('rect', selectObjects)
} }
},
{
label: 'Polygon',
action(o) {
a.select('poly', selectObjects)
}
} }
] ]
}, },

View File

@@ -57,8 +57,9 @@ export class CircleSelect extends FSM {
let ctx = view.catalogCtx; let ctx = view.catalogCtx;
// draw the selection // draw the selection
ctx.fillStyle = options.color + '7f'; let colorValue = (typeof options.color === 'function') ? options.color(this.startCoo, this.coo) : options.color;
ctx.strokeStyle = options.color; ctx.fillStyle = colorValue;
ctx.strokeStyle = colorValue;
ctx.lineWidth = options.lineWidth; ctx.lineWidth = options.lineWidth;
var r2 = (this.coo.x - this.startCoo.x) * (this.coo.x - this.startCoo.x) + (this.coo.y - this.startCoo.y) * (this.coo.y - this.startCoo.y); var r2 = (this.coo.x - this.startCoo.x) * (this.coo.x - this.startCoo.x) + (this.coo.y - this.startCoo.y) * (this.coo.y - this.startCoo.y);
@@ -112,7 +113,7 @@ export class CircleSelect extends FSM {
} }
// execute selection callback only // execute selection callback only
(typeof this.callback === 'function') && this.callback(s); (typeof this.callback === 'function') && this.callback(s, Selector.getObjects(s, view));
// TODO: remove these modes in the future // TODO: remove these modes in the future
view.aladin.showReticle(true) view.aladin.showReticle(true)

View File

@@ -22,6 +22,7 @@ import { ActionButton } from "../gui/Widgets/ActionButton";
import { View } from "../View"; import { View } from "../View";
import finishIconUrl from '../../../assets/icons/finish.svg'; import finishIconUrl from '../../../assets/icons/finish.svg';
import { Utils } from "../Utils"; import { Utils } from "../Utils";
import { Selector } from "../Selector";
/****************************************************************************** /******************************************************************************
* Aladin Lite project * Aladin Lite project
@@ -118,11 +119,12 @@ export class PolySelect extends FSM {
let draw = () => { let draw = () => {
let ctx = view.catalogCtx; let ctx = view.catalogCtx;
// draw the selection // draw the selection
ctx.save(); ctx.save();
ctx.fillStyle = options.color + '7f'; let colorValue = (typeof options.color === 'function') ? options.color() : options.color;
ctx.strokeStyle = options.color; ctx.fillStyle = colorValue;
ctx.strokeStyle = colorValue;
ctx.lineWidth = options.lineWidth; ctx.lineWidth = options.lineWidth;
ctx.beginPath(); ctx.beginPath();
@@ -144,6 +146,11 @@ export class PolySelect extends FSM {
let finish = () => { let finish = () => {
// finish the selection // finish the selection
const numCoo = this.coos.length;
if (numCoo <= 1) {
this.dispatch('off');
return;
}
let xMin = this.coos[0].x let xMin = this.coos[0].x
let yMin = this.coos[0].y let yMin = this.coos[0].y
let xMax = this.coos[0].x let xMax = this.coos[0].x
@@ -163,17 +170,32 @@ export class PolySelect extends FSM {
let s = { let s = {
vertices: this.coos, vertices: this.coos,
label: 'polygon', label: 'polygon',
contains(s) {
let x = s.x;
let y = s.y;
let inside = false;
for (let i = 0, j = this.vertices.length - 1; i < this.vertices.length; j = i++) {
let xi = this.vertices[i].x, yi = this.vertices[i].y;
let xj = this.vertices[j].x, yj = this.vertices[j].y;
let intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
},
bbox() { bbox() {
return {x, y, w, h} return {x, y, w, h}
} }
}; };
(typeof this.callback === 'function') && this.callback(s); (typeof this.callback === 'function') && this.callback(s, Selector.getObjects(s, view));
// execute general callback // execute general callback
if (view.aladin.callbacksByEventName) { if (view.aladin.callbacksByEventName) {
var callback = view.aladin.callbacksByEventName['objectsSelected'] || view.aladin.callbacksByEventName['select']; var callback = view.aladin.callbacksByEventName['objectsSelected'] || view.aladin.callbacksByEventName['select'];
if (typeof callback === "function") { if (typeof callback === "function") {
console.warn('polygon selection is not fully implemented, PolySelect.contains is needed for finding sources inside a polygon') let objList = Selector.getObjects(s, view);
callback(objList);
} }
} }
@@ -202,7 +224,6 @@ export class PolySelect extends FSM {
mousedown mousedown
}, },
mousedown: { mousedown: {
//mouseout,
mousemove, mousemove,
draw, draw,
}, },
@@ -246,6 +267,7 @@ export class PolySelect extends FSM {
//mouseout, //mouseout,
mousemove, mousemove,
draw, draw,
finish,
}, },
mouseout: { mouseout: {
start, start,

View File

@@ -58,8 +58,10 @@ export class RectSelect extends FSM {
let ctx = view.catalogCtx; let ctx = view.catalogCtx;
// draw the selection // draw the selection
ctx.fillStyle = options.color + '7f'; let colorValue = (typeof options.color === 'function') ? options.color(this.startCoo, this.coo) : options.color;
ctx.strokeStyle = options.color;
ctx.fillStyle = colorValue;
ctx.strokeStyle = colorValue;
ctx.lineWidth = options.lineWidth; ctx.lineWidth = options.lineWidth;
var w = this.coo.x - this.startCoo.x; var w = this.coo.x - this.startCoo.x;
@@ -99,7 +101,7 @@ export class RectSelect extends FSM {
} }
}; };
(typeof this.callback === 'function') && this.callback(s); (typeof this.callback === 'function') && this.callback(s, Selector.getObjects(s, view));
// TODO: remove these modes in the future // TODO: remove these modes in the future
view.aladin.showReticle(true) view.aladin.showReticle(true)

View File

@@ -94,7 +94,7 @@ export class Selector {
var objList = []; var objList = [];
var cat, sources, s; var cat, sources, s;
var footprints, f; var overlayItems, f;
var objListPerCatalog = []; var objListPerCatalog = [];
if (view.catalogs) { if (view.catalogs) {
for (var k = 0; k < view.catalogs.length; k++) { for (var k = 0; k < view.catalogs.length; k++) {
@@ -114,11 +114,11 @@ export class Selector {
} }
} }
// footprints // footprints
footprints = cat.getFootprints(); overlayItems = cat.getFootprints();
if (footprints) { if (overlayItems) {
const {x, y, w, h} = selection.bbox(); const {x, y, w, h} = selection.bbox();
for (var l = 0; l < footprints.length; l++) { for (var l = 0; l < overlayItems.length; l++) {
f = footprints[l]; f = overlayItems[l];
if (f.intersectsBBox(x, y, w, h, view)) { if (f.intersectsBBox(x, y, w, h, view)) {
objListPerCatalog.push(f); objListPerCatalog.push(f);
} }
@@ -132,6 +132,27 @@ export class Selector {
} }
} }
if (view.overlays) {
const {x, y, w, h} = selection.bbox();
for (var k = 0; k < view.overlays.length; k++) {
let overlay = view.overlays[k];
if (!overlay.isShowing) {
continue;
}
var overlayItems = overlay.overlayItems;
for (var l = 0; l < overlayItems.length; l++) {
let o = overlayItems[l];
if (!o.isShowing) {
continue;
}
if (o.intersectsBBox && o.intersectsBBox(x, y, w, h, view)) {
objList.push([o]);
}
}
}
}
return objList; return objList;
} }
} }

View File

@@ -205,7 +205,8 @@ export let View = (function () {
const cooFrame = CooFrameEnum.fromString(this.options.cooFrame, CooFrameEnum.J2000); const cooFrame = CooFrameEnum.fromString(this.options.cooFrame, CooFrameEnum.J2000);
this.changeFrame(cooFrame); this.changeFrame(cooFrame);
this.selector = new Selector(this); this.selector = new Selector(this, this.options.selector);
this.manualSelection = (this.options && this.options.manualSelection) || false;
// current reference image survey displayed // current reference image survey displayed
this.imageLayers = new Map(); this.imageLayers = new Map();
@@ -575,7 +576,7 @@ export let View = (function () {
const xymouse = Utils.relMouseCoords(e); const xymouse = Utils.relMouseCoords(e);
// deselect all the selected sources with Select panel // deselect all the selected sources with Select panel
view.unselectObjects() view.unselectObjects();
try { try {
const [lon, lat] = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs'); const [lon, lat] = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs');
@@ -618,7 +619,7 @@ export let View = (function () {
var handleSelect = function(xy, tolerance) { var handleSelect = function(xy, tolerance) {
tolerance = tolerance || 5; tolerance = tolerance || 5;
var objs = view.closestObjects(xy.x, xy.y, tolerance); var objs = view.closestObjects(xy.x, xy.y, tolerance);
// Deselect objects if any
view.unselectObjects(); view.unselectObjects();
if (objs) { if (objs) {
@@ -640,7 +641,7 @@ export let View = (function () {
(typeof objClickedFunction === 'function') && objClickedFunction(o, xy); (typeof objClickedFunction === 'function') && objClickedFunction(o, xy);
if (o.isFootprint()) { if (o.isFootprint()) {
if (typeof footprintClickedFunction === 'function' && (!view.lastClickedObject || !view.lastClickedObject.includes(o))) { if (typeof footprintClickedFunction === 'function') {
footprintClickedFunction(o, xy); footprintClickedFunction(o, xy);
} }
} }
@@ -650,6 +651,7 @@ export let View = (function () {
objs = Array.from(Object.values(objsByCats)); objs = Array.from(Object.values(objsByCats));
view.selectObjects(objs); view.selectObjects(objs);
view.lastClickedObject = objs; view.lastClickedObject = objs;
} else { } else {
// If there is a past clicked object // If there is a past clicked object
if (view.lastClickedObject) { if (view.lastClickedObject) {
@@ -1430,6 +1432,10 @@ export let View = (function () {
}; };
View.prototype.unselectObjects = function() { View.prototype.unselectObjects = function() {
if (this.manualSelection) {
return;
}
this.aladin.measurementTable.hide(); this.aladin.measurementTable.hide();
if (this.selection) { if (this.selection) {
@@ -1449,9 +1455,13 @@ export let View = (function () {
} }
View.prototype.selectObjects = function(selection) { View.prototype.selectObjects = function(selection) {
if (this.manualSelection) {
return;
}
// unselect the previous selection // unselect the previous selection
this.unselectObjects(); this.unselectObjects();
if (Array.isArray(selection)) { if (Array.isArray(selection)) {
this.selection = selection; this.selection = selection;
} else { } else {

View File

@@ -288,8 +288,8 @@ export let Circle = (function() {
// From StackOverflow: https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection // From StackOverflow: https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection
Circle.prototype.intersectsBBox = function(x, y, w, h) { Circle.prototype.intersectsBBox = function(x, y, w, h) {
const circleDistance = { const circleDistance = {
x: abs(this.center.x - x), x: Math.abs(this.center.x - x),
y: abs(this.center.y - y) y: Math.abs(this.center.y - y)
}; };
if (circleDistance.x > (w/2 + this.radius)) { return false; } if (circleDistance.x > (w/2 + this.radius)) { return false; }

View File

@@ -464,8 +464,51 @@ export let Polyline = (function() {
return false; return false;
}; };
Polyline.prototype.intersectsBBox = function(x, y, w, h) { Polyline.prototype.intersectsBBox = function(x, y, w, h, view) {
// todo for (let i = 0; i < this.raDecArray.length - 1; i++) {
let p1 = this.raDecArray[i];
let p2 = this.raDecArray[i + 1];
let xy1 = view.aladin.world2pix(p1[0], p1[1]);
let xy2 = view.aladin.world2pix(p2[0], p2[1]);
if (!xy1 || !xy2) {
return false;
}
xy1 = {x: xy1[0], y: xy1[1]};
xy2 = {x: xy2[0], y: xy2[1]};
// Check if line segment intersects with the bounding box
if (this.lineIntersectsBox(xy1, xy2, x, y, w, h)) {
return true;
}
}
return false;
};
Polyline.prototype.lineIntersectsBox = function(p1, p2, x, y, w, h) {
// Check if line segment is completely outside the box
if ((p1.x < x && p2.x < x) ||
(p1.y < y && p2.y < y) ||
(p1.x > x + w && p2.x > x + w) ||
(p1.y > y + h && p2.y > y + h)) {
return false;
}
let m = (p2.y - p1.y) / (p2.x - p1.x); // Slope of the line
let c = p1.y - m * p1.x; // y-intercept of the line
// Check if line intersects with the sides of the box
if ((p1.y >= y && p1.y <= y + h) ||
(p2.y >= y && p2.y <= y + h) ||
(m * x + c >= y && m * x + c <= y + h) ||
(m * (x + w) + c >= y && m * (x + w) + c <= y + h)) {
return true;
}
return false;
}; };
// static methods // static methods