mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2026-06-12 11:01:37 -07:00
Squash all skewer_selection commits to date
This commit is contained in:
committed by
Matthieu Baumann
parent
44fdfad61c
commit
6ce7e9365d
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<path d="M.7,12.3l11.3-4.9,11.3,4.9-11.1,7.1L.7,12.3ZM12,16.6l7.4-4.1-7.1-3.4-7.4,3.4s7.1,4.1,7.1,4.1Z"/>
|
||||
<polygon points="4.9 1.8 5 8 6.3 9.1 7.6 8 7.6 1.8 4.9 1.8"/>
|
||||
<polygon points="24 24 20.4 24 24 20.4 24 24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 453 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<path d="M.7,12.3l11.3-4.9,11.3,4.9-11.1,7.1L.7,12.3ZM12,16.6l7.4-4.1-7.1-3.4-7.4,3.4,7.1,4.1Z"/>
|
||||
<polygon points="4.9 1.8 5 8 6.3 9.1 7.6 8 7.6 1.8 4.9 1.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 395 B |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<polygon points="11.6 7.9 14 7.9 14 4.4 14 4.4 14 2.9 14 2.9 14 .8 11.6 .8 11.6 2.4 11.6 2.4 11.6 4 11.6 4 11.6 7.9"/>
|
||||
<path d="M22.5,7.5l-7.5-3.2v1.7l3.5,1.7-6,3.6-6.5-3.5,4.7-2.2v-1.7L2.2,7.5l4.2,2.5-5,2.6,11.1,6.5,10.8-6.6-5.1-2.4,4.2-2.6s.1,0,.1,0ZM19.8,12.4l-7.2,4-7.7-4.1,2.9-1.5,4.7,2.8,4.3-2.6,3,1.4Z"/>
|
||||
<polygon points="11.3 19.6 11.3 22.1 12.6 23.3 14 22.1 14 19.5 12.5 20.3 11.3 19.6"/>
|
||||
<polygon points="24 24 20.4 24 24 20.4 24 24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 685 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<polygon points="11.6 7.9 14 7.9 14 4.4 14 4.4 14 2.9 14 2.9 14 .8 11.6 .8 11.6 2.4 11.6 2.4 11.6 4 11.6 4 11.6 7.9"/>
|
||||
<path d="M22.5,7.5l-7.5-3.2v1.7c0,0,3.5,1.7,3.5,1.7l-6,3.6-6.5-3.5,4.7-2.2v-1.7S2.2,7.5,2.2,7.5l4.2,2.5-5,2.6,11.1,6.5,10.8-6.6-5.1-2.4,4.2-2.6h.1ZM19.8,12.4l-7.2,4-7.7-4.1,2.9-1.5,4.7,2.8,4.3-2.6s3,1.4,3,1.4Z"/>
|
||||
<polygon points="11.3 19.6 11.3 22.1 12.6 23.3 14 22.1 14 19.5 12.5 20.3 11.3 19.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 655 B |
+13
-3
@@ -74,6 +74,7 @@ import { SimbadPointer } from "./gui/Button/SimbadPointer";
|
||||
import { ColorPicker } from "./gui/Button/ColorPicker";
|
||||
import { GridEnabler } from "./gui/Button/GridEnabler";
|
||||
import { CooFrame } from "./gui/Input/CooFrame";
|
||||
import { SelectionMode } from "./gui/Button/SelectionMode";
|
||||
import { Circle } from "./shapes/Circle";
|
||||
import { Ellipse } from "./shapes/Ellipse";
|
||||
import { Polyline } from "./shapes/Polyline";
|
||||
@@ -110,6 +111,8 @@ import { Polyline } from "./shapes/Polyline";
|
||||
* CSS class for that button is `aladin-grid-control`
|
||||
* @property {boolean} [showSettingsControl=false] - Whether to show the settings control toolbar.
|
||||
* CSS class for that button is `aladin-settings-control`
|
||||
* @property {boolean} [showSelectionModeControl=false] - Whether to show the selection mode menu opener button.
|
||||
* CSS class for that button is `aladin-selectionMode-control`
|
||||
* @property {boolean} [showColorPickerControl=false] - Whether to show the color picker tool.
|
||||
* CSS class for that button is `aladin-colorPicker-control`
|
||||
* @property {boolean} [showShareControl=false] - Whether to show the share control toolbar.
|
||||
@@ -155,6 +158,7 @@ import { Polyline } from "./shapes/Polyline";
|
||||
* @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.
|
||||
* @property {string} [mode] - Interface theme, can be either 'dark' or 'light'. If not set, the mode will be retrieved from your browser preference or your localStorage.
|
||||
* @property {string} [selectionMode="edge"] - When set to 'skewer' footprints are selected by clicking inside them. For 'edge' footprints are selected by clicking on their edges.
|
||||
* @property {Object} [selector] - More options for the the selector.
|
||||
* @property {string} [selector.color] - Color of the selector, defaults to the color of the reticle. Can be a hex color or a function returning a hex color.
|
||||
* @property {number} [selector.lineWidth=2] - Width of the selector line.
|
||||
@@ -674,15 +678,19 @@ export let Aladin = (function () {
|
||||
widgets["simbad"] = simbad
|
||||
}
|
||||
|
||||
// Add the projection control
|
||||
// Add the coo grid control
|
||||
if (options.showCooGridControl) {
|
||||
let grid = new GridEnabler(this);
|
||||
widgets["grid"] = grid;
|
||||
}
|
||||
|
||||
// Add the projection control
|
||||
// Add the coo grid control
|
||||
// Show selection mode control
|
||||
if (options.showSelectionModeControl) {
|
||||
let selectionMode = new SelectionMode(this);
|
||||
widgets["selectionMode"] = selectionMode
|
||||
}
|
||||
|
||||
// Add the color picker control
|
||||
if (options.showColorPickerControl) {
|
||||
let picker = new ColorPicker(this);
|
||||
widgets["picker"] = picker;
|
||||
@@ -698,6 +706,7 @@ export let Aladin = (function () {
|
||||
this.toolbar.add(name, widget);
|
||||
}
|
||||
|
||||
// Add the projection control
|
||||
if (options.showProjectionControl) {
|
||||
this.projBtn = new ProjectionActionButton(this);
|
||||
this.addUI(this.projBtn);
|
||||
@@ -784,6 +793,7 @@ export let Aladin = (function () {
|
||||
showSimbadPointerControl: false,
|
||||
showCooGridControl: false,
|
||||
showSettingsControl: false,
|
||||
showSelectionModeControl: false,
|
||||
showColorPickerControl: false,
|
||||
// Share toolbar
|
||||
showShareControl: false,
|
||||
|
||||
+62
-5
@@ -27,15 +27,16 @@ import { PolySelect } from "./FiniteStateMachine/PolySelect";
|
||||
import { LineSelect } from "./FiniteStateMachine/LineSelect";
|
||||
import { RectSelect } from "./FiniteStateMachine/RectSelect";
|
||||
import { ALEvent } from "./events/ALEvent";
|
||||
import { Utils } from './Utils';
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
*
|
||||
* Class Selector
|
||||
*
|
||||
*
|
||||
* A selector
|
||||
*
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
export class Selector {
|
||||
@@ -121,7 +122,7 @@ export class Selector {
|
||||
continue;
|
||||
}
|
||||
sources = cat.getSources();
|
||||
|
||||
|
||||
for (var l = 0; l < sources.length; l++) {
|
||||
s = sources[l];
|
||||
|
||||
@@ -172,4 +173,60 @@ export class Selector {
|
||||
|
||||
return objList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves objects skewered by the cursor position or specified coordinates. An object is
|
||||
* skewered if it is a shape that contains the specified coordinate, or is a catalog object within 3 pixels
|
||||
* of the specified coordinate.
|
||||
*
|
||||
* If e is a mouse event (as opposed to an object with x and y values), the mouse coordinates
|
||||
* of the event are used.
|
||||
*
|
||||
* This is implemented by simulating the interactive selection of a circle region with a 3 pixel radius)
|
||||
* around the given coordinates and returns all catalog sources and overlay items intersecting with it.
|
||||
*
|
||||
* @param {Event|Object} e - Mouse coordinate via mouse event or object with x and y properties
|
||||
* @param {Object} view - The Aladin View instance containing catalogs and overlays
|
||||
* @returns {Array<Array>} Array of object lists, where each subarray contains objects
|
||||
* from a single catalog or overlay that intersect with the selection region.
|
||||
* Returns empty array if no objects are found.
|
||||
*/
|
||||
static getSkewerObjects(e, view) {
|
||||
// Get the xy from the event
|
||||
let xymouse;
|
||||
if (e instanceof Event) {
|
||||
xymouse = Utils.relMouseCoords(e);
|
||||
} else {
|
||||
xymouse = e;
|
||||
}
|
||||
const x = xymouse.x;
|
||||
const y = xymouse.y;
|
||||
|
||||
// Perform a selection using a circle around x, y as if drawn by dragging 3 pixels.
|
||||
const r2 = 9;
|
||||
const r = Math.sqrt(r2);
|
||||
|
||||
let selectorObject = {
|
||||
x, y, r,
|
||||
label: 'circle',
|
||||
contains(s) {
|
||||
let dx = (s.x - x)
|
||||
let dy = (s.y - y);
|
||||
|
||||
return dx*dx + dy*dy <= r2;
|
||||
},
|
||||
bbox() {
|
||||
return {
|
||||
x: x - r,
|
||||
y: y - r,
|
||||
w: 2*r,
|
||||
h: 2*r
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let objList = Selector.getObjects(selectorObject, view);
|
||||
|
||||
return objList;
|
||||
}
|
||||
}
|
||||
+277
-66
@@ -273,6 +273,13 @@ export let View = (function () {
|
||||
this.selector = new Selector(this, this.options.selector);
|
||||
this.manualSelection = (this.options && this.options.manualSelection) || false;
|
||||
|
||||
// Selection mode
|
||||
this.selectionMode = View.SELECTION_MODE_EDGE;
|
||||
if (this.options.selectionMode === 'skewer') {
|
||||
this.selectionMode = View.SELECTION_MODE_SKEWER;
|
||||
}
|
||||
|
||||
|
||||
// current reference image survey displayed
|
||||
this.imageLayers = new Map();
|
||||
|
||||
@@ -359,6 +366,10 @@ export let View = (function () {
|
||||
View.TOOL_SIMBAD_POINTER = 2;
|
||||
View.TOOL_COLOR_PICKER = 3;
|
||||
|
||||
// Selection modes
|
||||
View.SELECTION_MODE_EDGE = 0;
|
||||
View.SELECTION_MODE_SKEWER = 1;
|
||||
|
||||
// TODO: should be put as an option at layer level
|
||||
View.DRAW_SOURCES_WHILE_DRAGGING = true;
|
||||
View.DRAW_MOCS_WHILE_DRAGGING = true;
|
||||
@@ -514,6 +525,13 @@ export let View = (function () {
|
||||
}
|
||||
|
||||
View.prototype.setMode = function (mode, params) {
|
||||
|
||||
// Undo the specialized cursors, if any.
|
||||
const prevMode = this.mode;
|
||||
if (prevMode == View.TOOL_SIMBAD_POINTER) {
|
||||
this.catalogCanvas.classList.remove('aladin-sp-cursor');
|
||||
}
|
||||
|
||||
// hide the picker tooltip
|
||||
this.colorPickerTool.domElement.style.display = "none";
|
||||
// in case we are in the selection mode
|
||||
@@ -546,6 +564,14 @@ export let View = (function () {
|
||||
ALEvent.MODE.dispatchedTo(this.aladin.aladinDiv, {mode});
|
||||
};
|
||||
|
||||
View.prototype.setSelectionMode = function (selectionMode) {
|
||||
this.selectionMode = selectionMode;
|
||||
};
|
||||
|
||||
View.prototype.getSelectionMode = function () {
|
||||
return this.selectionMode;
|
||||
};
|
||||
|
||||
View.prototype.setCursor = function (cursor) {
|
||||
if (this.catalogCanvas.style.cursor == cursor) {
|
||||
return;
|
||||
@@ -713,12 +739,10 @@ export let View = (function () {
|
||||
var showContextMenu = true;
|
||||
var xystart;
|
||||
|
||||
var handleSelect = function(xy, tolerance) {
|
||||
var handleSelect = function(xy, tolerance, withModifierKey=false) {
|
||||
tolerance = tolerance || 5;
|
||||
var objs = view.closestObjects(xy.x, xy.y, tolerance);
|
||||
|
||||
view.unselectObjects();
|
||||
|
||||
if (objs) {
|
||||
var objClickedFunction = view.aladin.callbacksByEventName['objectClicked'];
|
||||
var footprintClickedFunction = view.aladin.callbacksByEventName['footprintClicked'];
|
||||
@@ -757,11 +781,14 @@ export let View = (function () {
|
||||
if (shapes.length > 0) {
|
||||
objs.push(shapes)
|
||||
}
|
||||
view.selectObjects(objs);
|
||||
view.selectObjects(objs, withModifierKey);
|
||||
|
||||
view.lastClickedObject = objs;
|
||||
|
||||
} else {
|
||||
|
||||
view.unselectObjects();
|
||||
|
||||
// If there is a past clicked object
|
||||
if (view.lastClickedObject) {
|
||||
// TODO: do we need to keep that triggering ?
|
||||
@@ -772,6 +799,95 @@ export let View = (function () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a skewer-based selection of objects at the specified mouse position.
|
||||
*
|
||||
* @param {Event|Object} e - Mouse coordinate via mouse event or object with x and y properties
|
||||
* @param {boolean} withModifierKey - If true, toggles selection (adds/removes from existing selection);
|
||||
* if false, replaces current selection
|
||||
*/
|
||||
var handleSkewerSelect = function(e, withModifierKey) {
|
||||
|
||||
const objList = Selector.getSkewerObjects(e, view);
|
||||
view.selectObjects(objList, withModifierKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the selected objects appear hovered and make all other objects appear unhovered,
|
||||
* firing the appropriate callbacks for both cases.
|
||||
*
|
||||
* The also sets the cursor to a pointer to indicate that some object(s) would be
|
||||
* select on click in the current mouse position.
|
||||
*
|
||||
* @param {Array} objects - Array of objects to apply hover state to
|
||||
* @param {Object} xymouse - Mouse coordinates with x and y properties
|
||||
*/
|
||||
var hoverObjects = function(objects, xymouse) {
|
||||
var objHoveredFunction = view.aladin.callbacksByEventName['objectHovered'];
|
||||
var footprintHoveredFunction = view.aladin.callbacksByEventName['footprintHovered'];
|
||||
|
||||
view.setCursor('pointer');
|
||||
|
||||
for (let o of objects) {
|
||||
|
||||
if (typeof objHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
|
||||
var ret = objHoveredFunction(o, xymouse);
|
||||
}
|
||||
|
||||
if (o.isFootprint()) {
|
||||
if (typeof footprintHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
|
||||
var ret = footprintHoveredFunction(o, xymouse);
|
||||
}
|
||||
}
|
||||
|
||||
if (!view.lastHoveredObject || !view.lastHoveredObject.includes(o)) {
|
||||
o.hover();
|
||||
}
|
||||
}
|
||||
|
||||
// unhover the objects in lastHoveredObjects that are not in closest anymore
|
||||
if (view.lastHoveredObject) {
|
||||
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
|
||||
|
||||
for (let lho of view.lastHoveredObject) {
|
||||
if (!objects.includes(lho)) {
|
||||
lho.unhover();
|
||||
|
||||
if (typeof objHoveredStopFunction === 'function') {
|
||||
objHoveredStopFunction(lho, xymouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
view.lastHoveredObject = objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes hover state from all previously hovered objects and fires the
|
||||
* apropriate callbacks.
|
||||
*
|
||||
* The also resets the cursor to the default to indicate that no objects would be
|
||||
* selected on click in the current mouse position.
|
||||
*
|
||||
* @param {Object} xymouse - Mouse coordinates with x and y properties
|
||||
*/
|
||||
var unhoverObjects = function(xymouse) {
|
||||
view.setCursor('default');
|
||||
if (view.lastHoveredObject) {
|
||||
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
|
||||
for (let lho of view.lastHoveredObject) {
|
||||
lho.unhover();
|
||||
|
||||
if (typeof objHoveredStopFunction === 'function') {
|
||||
objHoveredStopFunction(lho, xymouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view.lastHoveredObject = null;
|
||||
}
|
||||
|
||||
var touchStartTime;
|
||||
Utils.on(view.catalogCanvas, "mousedown touchstart", function (e) {
|
||||
e.stopPropagation();
|
||||
@@ -918,6 +1034,7 @@ export let View = (function () {
|
||||
// reacting on 'click' rather on 'mouseup' is more reliable when panning the view
|
||||
Utils.on(view.catalogCanvas, "mouseup mouseout touchend touchcancel", function (e) {
|
||||
const xymouse = Utils.relMouseCoords(e);
|
||||
const withModifierKey = e.ctrlKey || e.metaKey;
|
||||
|
||||
ALEvent.CANVAS_EVENT.dispatchedTo(view.aladinDiv, {
|
||||
state: {
|
||||
@@ -1006,11 +1123,19 @@ export let View = (function () {
|
||||
const elapsedTime = Date.now() - touchStartTime;
|
||||
if (elapsedTime < 100) {
|
||||
view.updateObjectsLookup();
|
||||
handleSelect(xymouse, 15);
|
||||
if (view.selectionMode === View.SELECTION_MODE_SKEWER) {
|
||||
handleSkewerSelect(e, withModifierKey)
|
||||
} else {
|
||||
handleSelect(xymouse, 15, withModifierKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handleSelect(xymouse);
|
||||
if (view.selectionMode === View.SELECTION_MODE_EDGE) {
|
||||
handleSelect(xymouse, 5, withModifierKey);
|
||||
} else {
|
||||
handleSkewerSelect(e, withModifierKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1193,71 +1318,31 @@ export let View = (function () {
|
||||
lastMouseMovePos = pos;
|
||||
}
|
||||
|
||||
// closestObjects is very costly, we would like to not do it
|
||||
// especially if the objectHovered function is not defined.
|
||||
var closests = view.closestObjects(xymouse.x, xymouse.y, 5);
|
||||
if (view.selectionMode === View.SELECTION_MODE_EDGE) {
|
||||
// We're in edge selection mode for footprints. closestObjects() will find those footprints by closeness to a footprint edge.
|
||||
// closestObjects is very costly, we would like to not do it
|
||||
// especially if the objectHovered function is not defined.
|
||||
var closests = view.closestObjects(xymouse.x, xymouse.y, 5);
|
||||
|
||||
if (closests) {
|
||||
var objHoveredFunction = view.aladin.callbacksByEventName['objectHovered'];
|
||||
var footprintHoveredFunction = view.aladin.callbacksByEventName['footprintHovered'];
|
||||
if (closests) {
|
||||
hoverObjects(closests, xymouse);
|
||||
} else {
|
||||
unhoverObjects(view, xymouse);
|
||||
}
|
||||
} else if (view.selectionMode === View.SELECTION_MODE_SKEWER) {
|
||||
// We're in skewer mode. Let's see what would be selected.
|
||||
const skewerTargetsByLayer = Selector.getSkewerObjects(e, view);
|
||||
const skewerObjects = skewerTargetsByLayer.flat();
|
||||
|
||||
view.setCursor('pointer');
|
||||
|
||||
for (let o of closests) {
|
||||
|
||||
if (typeof objHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
|
||||
var ret = objHoveredFunction(o, xymouse);
|
||||
}
|
||||
|
||||
if (o.isFootprint()) {
|
||||
if (typeof footprintHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
|
||||
var ret = footprintHoveredFunction(o, xymouse);
|
||||
}
|
||||
}
|
||||
|
||||
if (!view.lastHoveredObject || !view.lastHoveredObject.includes(o)) {
|
||||
o.hover();
|
||||
}
|
||||
if (skewerObjects.length > 0) {
|
||||
hoverObjects(skewerObjects, xymouse);
|
||||
} else {
|
||||
unhoverObjects(view, xymouse);
|
||||
}
|
||||
|
||||
// unhover the objects in lastHoveredObjects that are not in closest anymore
|
||||
if (view.lastHoveredObject) {
|
||||
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
|
||||
|
||||
for (let lho of view.lastHoveredObject) {
|
||||
if (!closests.includes(lho)) {
|
||||
lho.unhover();
|
||||
|
||||
if (typeof objHoveredStopFunction === 'function') {
|
||||
objHoveredStopFunction(lho, xymouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
view.lastHoveredObject = closests;
|
||||
} else {
|
||||
view.setCursor('default');
|
||||
if (view.lastHoveredObject) {
|
||||
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
|
||||
|
||||
/*if (typeof objHoveredStopFunction === 'function') {
|
||||
// call callback function to notify we left the hovered object
|
||||
var ret = objHoveredStopFunction(view.lastHoveredObject, xymouse);
|
||||
}
|
||||
|
||||
view.lastHoveredObject.unhover();*/
|
||||
for (let lho of view.lastHoveredObject) {
|
||||
lho.unhover();
|
||||
|
||||
if (typeof objHoveredStopFunction === 'function') {
|
||||
objHoveredStopFunction(lho, xymouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view.lastHoveredObject = null;
|
||||
}
|
||||
|
||||
|
||||
if (e.type === "mousemove") {
|
||||
return;
|
||||
}
|
||||
@@ -1391,6 +1476,7 @@ export let View = (function () {
|
||||
|
||||
view.displayHpxGrid = false;
|
||||
view.displayCatalog = false;
|
||||
view.skewerEnabled = false;
|
||||
};
|
||||
|
||||
View.prototype.requestRedrawAtDate = function (date) {
|
||||
@@ -1636,6 +1722,9 @@ export let View = (function () {
|
||||
return imageData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unselects all currently selected objects.
|
||||
*/
|
||||
View.prototype.unselectObjects = function() {
|
||||
if (this.manualSelection) {
|
||||
return;
|
||||
@@ -1654,11 +1743,24 @@ export let View = (function () {
|
||||
this.requestRedraw();
|
||||
}
|
||||
|
||||
View.prototype.selectObjects = function(selection) {
|
||||
/**
|
||||
* Selects the specified objects in the view.
|
||||
*
|
||||
* If withModifierKey is true, it modifies the existing selection (adds/removes).
|
||||
* Otherwise, it replaces the current selection.
|
||||
*
|
||||
* @param {Array|Object} selection - The objects to select, either an array or a selector object.
|
||||
* @param {boolean} [withModifierKey=false] - Whether to modify (versus replace) the existing selections.
|
||||
*/
|
||||
View.prototype.selectObjects = function(selection, withModifierKey=false) {
|
||||
if (this.manualSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(selection) && withModifierKey) {
|
||||
selection = this.computeModifiedSelection(selection)
|
||||
}
|
||||
|
||||
// unselect the previous selection
|
||||
this.unselectObjects();
|
||||
|
||||
@@ -1736,6 +1838,113 @@ export let View = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
View.prototype._getLayerForObj = function(obj) {
|
||||
let layer = null;
|
||||
if (obj.getCatalog) {
|
||||
layer = obj.getCatalog()
|
||||
} else {
|
||||
layer = obj.overlay
|
||||
}
|
||||
return layer
|
||||
}
|
||||
|
||||
View.prototype._copySelectionsToStage = function(selections, stage, overlays, exclude) {
|
||||
for (const group of selections) {
|
||||
for (const obj of group) {
|
||||
const objExcluded = exclude.includes(obj)
|
||||
if (!objExcluded) {
|
||||
const layer = this._getLayerForObj(obj)
|
||||
const idx = overlays.findIndex(item => item.uuid === layer.uuid);
|
||||
if (idx >= 0) {
|
||||
stage[idx].push(obj)
|
||||
} else {
|
||||
console.warn("Layer not found for selected obj: " + obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the full set of selections that should result if the specified (pending) objects were
|
||||
* selected with a modifier key pressed.
|
||||
*
|
||||
* If there are existing selections, it adds pending items that aren't selected,
|
||||
* or removes all pending items if they are all already selected.
|
||||
*
|
||||
* Organizes selections by overlay layers as expected by selectObjects.
|
||||
*
|
||||
* @param {Array<Array>} pending - Array of array of objects to potentially add/remove from selection.
|
||||
* @returns {Array<Array>} The modified selection array.
|
||||
*/
|
||||
View.prototype.computeModifiedSelection = function(pending) {
|
||||
const current = this.selection
|
||||
let modSelection = pending
|
||||
if (current && current.length > 0) {
|
||||
// There are some items already selected.
|
||||
// We will be adding all the pending selections that are not already selected,
|
||||
// UNLESS all of the pending selections are already selected, in which case
|
||||
// they will all be unselected.
|
||||
const toAdd = []
|
||||
let mightRemove = []
|
||||
modSelection = [] // We will build a new selection list from the current and pending selections
|
||||
|
||||
// stage will have one row for each existing overlay in which to collect all desired selections.
|
||||
const overlays = this.aladin.getOverlays()
|
||||
const stage = new Array(overlays.length).fill(null).map(() => []);
|
||||
|
||||
// Put already-selected items in mightRemove and not-yet-selected items in toAdd.
|
||||
for (const group of pending) {
|
||||
for (const obj of group) {
|
||||
if (obj.isSelected) {
|
||||
mightRemove.push(obj)
|
||||
} else {
|
||||
toAdd.push(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is anything in toAdd, then clear mightRemove since they will be left selected.
|
||||
if (toAdd.length > 0) {
|
||||
mightRemove = []
|
||||
}
|
||||
|
||||
// Copy current selections to stage except for anything in mightRemove
|
||||
this._copySelectionsToStage(current, stage, overlays, mightRemove)
|
||||
|
||||
// Copy toAdd selections to stage
|
||||
this._copySelectionsToStage([toAdd], stage, overlays, [])
|
||||
|
||||
// Build new modified selections list from stage.
|
||||
// I can preserve the layer order, but I don't know how to preserve the order within
|
||||
// layers without looping through all objects. Hopefully that order doesn't matter.
|
||||
for (let i=0; i<stage.length; i++) {
|
||||
if (stage[i].length > 0) {
|
||||
// We have selected objects in this layer so will add the layer (or list of overlays) to modSelection
|
||||
|
||||
if (overlays[i].type === 'catalog') {
|
||||
// The layer is a catalog so we add one entry for all its selections
|
||||
const catLayer = []
|
||||
modSelection.push(catLayer)
|
||||
for (const obj of stage[i]) {
|
||||
catLayer.push(obj)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Assume it's a graphicalOverlay and add separate entries for each selected obj
|
||||
// (That is the way objects in graphicalOverlays are currently selected. If they start
|
||||
// being selected all in one list per overlay, then that can change here.)
|
||||
for (const obj of stage[i]) {
|
||||
modSelection.push([obj])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return modSelection;
|
||||
}
|
||||
|
||||
View.prototype.getVisibleCells = function (norder) {
|
||||
return this.wasm.getVisibleCells(norder);
|
||||
};
|
||||
@@ -2429,3 +2638,5 @@ export let View = (function () {
|
||||
|
||||
return View;
|
||||
})();
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU Lesser General Public License version 3
|
||||
// or (at your option) any later version.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with Aladin Lite. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import { CtxMenuActionButtonOpener } from "./CtxMenuOpener";
|
||||
import skewerSelectionIconArrow from '../../../../assets/icons/skewer_selection-arrow.svg';
|
||||
import skewerSelectionIcon from '../../../../assets/icons/skewer_selection_black.svg';
|
||||
import edgeSelectionIconArrow from '../../../../assets/icons/edge_selection-arrow.svg';
|
||||
import edgeSelectionIcon from '../../../../assets/icons/edge_selection.svg';
|
||||
import { View } from "../../View.js";
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/Button/SelectionMode.js
|
||||
*
|
||||
* Class representing a button for bringing up a menu for choosing selection mode.
|
||||
* The appearance of the button changes depending on which selection mode (View.getSelectionMode())
|
||||
* is active.
|
||||
*
|
||||
* There are two possible selection modes, Edge and Skewer, which affect how footprints are
|
||||
* interactively selected.
|
||||
*
|
||||
* In Edge mode (View.SELECTION_MODE_EDGE), footprints are selected by clicking on their edges.
|
||||
*
|
||||
* In Skewer mode (View.SELECTION_MODE_SKEWER), footprints are selecting by clicking anywhere
|
||||
* inside the footprint.
|
||||
*
|
||||
* Using a modifier key (Cmd on Mac, Ctrl otherwise) during select toggles the potential selections:
|
||||
* - If any of the potential selections are not already selected, those objects are added to the current selections.
|
||||
* - If all of the potential selections are already selected, then they are deselected.
|
||||
*
|
||||
* This uses the CSS class aladin-selectionMode-control.
|
||||
*
|
||||
* Author: Tom Donaldson (STScI)
|
||||
*
|
||||
*****************************************************************************/
|
||||
export class SelectionMode extends CtxMenuActionButtonOpener {
|
||||
/**
|
||||
* Class representing a button for bringing up a menu for choosing selection mode.
|
||||
* @param {Aladin} aladin - The aladin instance.
|
||||
*/
|
||||
constructor(aladin, options) {
|
||||
|
||||
// If we're on Mac, the modifier key will be Cmd instead of Ctrl.
|
||||
let modifierKey = 'Ctrl';
|
||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||
if (userAgent.indexOf('mac') > -1) {
|
||||
modifierKey = 'Cmd';
|
||||
}
|
||||
|
||||
// Set the initial button icon based on the current View selection mode.
|
||||
const initialMode = aladin.view.getSelectionMode();
|
||||
let initialIcon = edgeSelectionIconArrow;
|
||||
if (initialMode === View.SELECTION_MODE_SKEWER) {
|
||||
initialIcon = skewerSelectionIconArrow;
|
||||
}
|
||||
|
||||
super({
|
||||
icon: {
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: initialIcon,
|
||||
},
|
||||
classList: ['aladin-selectionMode-control'],
|
||||
tooltip: {
|
||||
content: 'Choose the selection mode<br />(' + modifierKey + ' for multiselect)',
|
||||
position: { direction: 'top right', top: '10%', left: '80%' },
|
||||
},
|
||||
ctxMenu: undefined,
|
||||
...options
|
||||
}, aladin);
|
||||
|
||||
this.aladin = aladin;
|
||||
this.modifierKey = modifierKey;
|
||||
let ctxMenu = this._buildLayout()
|
||||
this.update({ctxMenu})
|
||||
}
|
||||
|
||||
setCustomIcon(icon) {
|
||||
this.update({icon: {
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: icon
|
||||
}})
|
||||
}
|
||||
|
||||
_buildLayout() {
|
||||
let self = this;
|
||||
let aladin = this.aladin;
|
||||
|
||||
return [
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: skewerSelectionIcon,
|
||||
monochrome: true,
|
||||
},
|
||||
tooltip: {
|
||||
content: 'Click inside shapes to select.<br />Multiselect with ' + self.modifierKey + '.',
|
||||
position: { direction: 'top right', left: '50%' },
|
||||
},
|
||||
content: "Skewer Selection",
|
||||
},
|
||||
action: (e) => {
|
||||
aladin.view.setSelectionMode(View.SELECTION_MODE_SKEWER);
|
||||
self.setCustomIcon(skewerSelectionIconArrow);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: edgeSelectionIcon,
|
||||
monochrome: true,
|
||||
},
|
||||
tooltip: {
|
||||
content: 'Click on objects to select.<br />Multiselect with ' + self.modifierKey + '.',
|
||||
position: { direction: 'top right', left: '60%' },
|
||||
},
|
||||
content: "Edge Selection",
|
||||
},
|
||||
action: (e) => {
|
||||
aladin.view.setSelectionMode(View.SELECTION_MODE_EDGE);
|
||||
self.setCustomIcon(edgeSelectionIconArrow);
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ There are distincts CSS class names for users wanting to personnalize the defaul
|
||||
* `aladin-simbadPointer-control` targets the Simbad pointer control button
|
||||
* `aladin-grid-control` targets the coordinate grid trigger button
|
||||
* `aladin-settings-control` targets the settings menu opener button
|
||||
* `aladin-selectionMode-control` targets the selection mode menu opener button
|
||||
* `aladin-share-control` targets the share menu opener button
|
||||
* `aladin-projection-control` targets the projection selector button
|
||||
* `aladin-stack-box` targets the stack box
|
||||
|
||||
Reference in New Issue
Block a user