Skip to content

Commit

Permalink
added(editor): It is now possible to select multiple LineShapes/Navli…
Browse files Browse the repository at this point in the history
…nkShapes and drag them simultaneously to change position. See: [shape.select()](https://heremaps.github.io/xyz-maps/docs/classes/editor.lineshape.html#select), [Playground](https://heremaps.github.io/xyz-maps/playground/#Editor-Drag_multiple_Shapes)

Signed-off-by: Tim Deubler <tim.deubler@here.com>
  • Loading branch information
TerminalTim committed Aug 18, 2023
1 parent ce50c2e commit f2a3438
Show file tree
Hide file tree
Showing 11 changed files with 445 additions and 38 deletions.
2 changes: 1 addition & 1 deletion packages/editor/src/features/feature/Feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class Feature extends GeoJSONFeature {
__: {
b?: { [behavior: string]: any };
[privateProperty: string]: any
}
};

constructor(geojsonFeature, provider?: EditableProvider) {
super(geojsonFeature, provider);
Expand Down
18 changes: 18 additions & 0 deletions packages/editor/src/features/line/Line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ class Line extends Feature {
[number, number, number?][] | [number, number, number?][][] {
return super.coord(coordinates);
}

/**
* Returns an array of Boolean values indicating whether the corresponding shape is selected at index or not.
*/
getSelectedShapes(): boolean[] | boolean[][] {
const line = this;
const selectedShapes = tools.private(line, 'selectedShapes');
const coordinates = tools.getCoordinates(line);
const selected = [];

for (let ls = 0; ls < coordinates.length; ls++) {
selected[ls] = [];
for (let i = 0; i < coordinates[ls].length; i++) {
selected[ls][i] = !!(selectedShapes[ls]?.[i]);
}
}
return tools.isMultiLineString(line) ? selected : selected[0];
}
}

(<any>Line).prototype.class = 'LINE';
Expand Down
134 changes: 120 additions & 14 deletions packages/editor/src/features/line/LineShape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import {Feature} from '@here/xyz-maps-core';
import {Line} from './Line';
import LineTools, {Coordinate} from './LineTools';
import {dragFeatureCoordinate} from '../oTools';
import oTools from '../area/PolygonTools';
import {Navlink} from '@here/xyz-maps-editor';
import {vec3} from '@here/xyz-maps-common';


let lineTools: typeof LineTools;
Expand All @@ -34,7 +33,14 @@ type DefaultBehavior = {
}

export const defaultBehavior: DefaultBehavior = {
dragPlane: 'XY'
'dragPlane': 'XY'
};

const EDITOR_NS = '@ns:com:here:editor';

const getPrivateData = (shape: LineShape, prop?: string) => {
const data = shape.__ ||= {b: {...defaultBehavior}};
return prop ? data[prop] : data;
};

/**
Expand All @@ -50,7 +56,7 @@ class LineShape extends Feature {
* @hidden
* @internal
*/
private __: {
__: {
b?: { [behavior: string]: any };
[privateProperty: string]: any
};
Expand Down Expand Up @@ -86,6 +92,7 @@ class LineShape extends Feature {
const _editor = line._e();
const style = _editor.getStyle(line);


super({
type: 'Feature',
properties: {
Expand All @@ -104,6 +111,12 @@ class LineShape extends Feature {
});

this._l = line;

this.properties[EDITOR_NS] = {
selected: this.isSelected()
};

console.log('CONSTRUCTOR SHAPE', lineStringIndex, index, '->', this.properties[EDITOR_NS]);
}

/**
Expand Down Expand Up @@ -150,7 +163,7 @@ class LineShape extends Feature {
};

behavior(options?: any, value?: boolean) {
let behavior = oTools.private(this, 'b') || {...defaultBehavior};
let behavior = getPrivateData(this, 'b');

switch (arguments.length) {
case 0:
Expand Down Expand Up @@ -224,6 +237,55 @@ class LineShape extends Feature {
lineTools.markAsModified(line);
}

/**
* Select the LineShape add it to the current selection.
* Multiple LineShapes can be selected at the same time.
* When a selected shape is dragged, all other shapes in the current selection are dragged as well.
*/
select() {
const shape = this;
const line = shape.getLine();
const editor = line._e();

const selectedShapes = lineTools.private(line, 'selectedShapes');
const {lineStringIndex, index} = shape.properties;

selectedShapes[lineStringIndex] ||= [];
selectedShapes[lineStringIndex][index] = true;

shape.properties[EDITOR_NS].selected = true;

lineTools.displayShapes(line);
}

/**
* Unselect the LineShape and remove from current selection.
*/
unselect() {
const shape = this;
const line = shape.getLine();
const editor = line._e();
shape.properties[EDITOR_NS].selected = false;
const selectedShapes = lineTools.private(line, 'selectedShapes');
const {lineStringIndex, index} = shape.properties;

if (selectedShapes[lineStringIndex]) {
selectedShapes[lineStringIndex][index] = false;
}
// editor.setStyle(shape, UNDEF);
lineTools.displayShapes(line);
}

/**
* Will return true or false whether the Shape is currently selected.
*/
isSelected(): boolean {
const shape = this;
const selectedShapes = lineTools.private(shape.getLine(), 'selectedShapes');
const {lineStringIndex, index} = shape.properties;
return !!selectedShapes[lineStringIndex]?.[index];
}

pointerdown(ev) {
const shape = this;
const properties = shape.properties;
Expand All @@ -246,30 +308,73 @@ class LineShape extends Feature {
line._e().listeners.trigger(ev, this, 'pointerdown');
}


getSelectedShapes(ingoreIndex?: number, ignoreLineStringIndex?: number) {
const line = this.getLine();
let selectedShapes = lineTools.private(line, 'selectedShapes');
let shapes = lineTools.private(line, 'shps');
let selected = [];

for (let lineStringIndex = 0; lineStringIndex < selectedShapes.length; lineStringIndex++) {
for (let index = 0; index < selectedShapes[lineStringIndex]?.length; index++) {
if (!selectedShapes[lineStringIndex][index]) continue;
if (!arguments.length || ignoreLineStringIndex != lineStringIndex || ingoreIndex != index) {
selected.push(shapes[lineStringIndex][index]);
}
}
}

return selected;
}


pressmove(ev, dx, dy) {
const shape = this;
const properties = this.properties;
const {lineStringIndex} = properties;
const {lineStringIndex, index} = properties;
const line = shape.getLine();
const editor = line._e();
const coordinates = <Coordinate[]>lineTools.getCoordinates(line, lineStringIndex);
const coordinates = <Coordinate[][]>lineTools.getCoordinates(line);
const lineStringCoordinates = coordinates[lineStringIndex];
const ignoreZ = !editor.getStyleProperty(line, 'altitude');
const orgCoord: number[] = coordinates[properties.index];
let coord = ignoreZ ? orgCoord.slice(0, 2) : orgCoord;
const orgAltitude: number = lineStringCoordinates[index][2];
const coord: number[] = shape.geometry.coordinates;

if (!properties.moved) {
properties.moved = true;
editor.listeners.trigger(ev, this, 'dragStart');
}
coord = coordinates[properties.index] = <Coordinate>dragFeatureCoordinate(ev.mapX, ev.mapY, shape, coord, editor);
let movedCoord = lineStringCoordinates[index] = <Coordinate>dragFeatureCoordinate(ev.mapX, ev.mapY, shape, coord, editor);

if (ignoreZ && typeof orgCoord[2] == 'number') {
if (ignoreZ && typeof orgAltitude == 'number') {
// restore original altitude if 2d edit mode is forced.
coord[2] = orgCoord[2];
movedCoord[2] = orgAltitude;
}
shape.getProvider().setFeatureCoordinates(shape, coord.slice());

lineTools._setCoords(line, coordinates, lineStringIndex);
if (this.isSelected()) {
const display = editor.display;
const orgCoordWorldPx = display._g2w(coord);
const movedCoordWorldPx = display._g2w(movedCoord);
const offsetWorldPx = vec3.sub(movedCoordWorldPx, movedCoordWorldPx, orgCoordWorldPx);

for (let selectedShape of this.getSelectedShapes(properties.index, lineStringIndex)) {
const {properties} = selectedShape;
const positionWorldPx = display._g2w(selectedShape.geometry.coordinates);

vec3.add(positionWorldPx, positionWorldPx, offsetWorldPx);
const movedPosition = display._w2g(positionWorldPx);


// linkTools.moveShapeAtIndexTo(link, getPrivate(selectedShape).index, movedPosition);

coordinates[properties.lineStringIndex][properties.index] = <Coordinate>movedPosition;
selectedShape.getProvider().setFeatureCoordinates(selectedShape, movedPosition);
}
}

shape.getProvider().setFeatureCoordinates(shape, movedCoord.slice());

lineTools._setCoords(line, coordinates);
}

pointerup(ev) {
Expand All @@ -279,6 +384,7 @@ class LineShape extends Feature {
if (moved) {
lineTools.markAsModified(line);
}

lineTools.displayShapes(line);

line._e().listeners.trigger(ev, this, moved ? 'dragStop' : UNDEF);
Expand Down
31 changes: 23 additions & 8 deletions packages/editor/src/features/line/LineTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function getPrivate(feature: Line, name?: string): any {
moved: false,
shps: [],
vShps: [],
selectedShapes: [],
isGeoMod: false
};
}
Expand Down Expand Up @@ -106,6 +107,7 @@ const tools = {

coordinates.splice(index, 0, position);
tools._setCoords(line, coordinates, lineStringIndex);
getPrivate(line, 'selectedShapes')[lineStringIndex]?.splice(index, 0, UNDEF);

return index;
},
Expand All @@ -116,6 +118,7 @@ const tools = {

if (length > 2 && index >= 0 && index < length) {
removed = coordinates.splice(index, 1);
getPrivate(line, 'selectedShapes')[lineStringIndex]?.splice(index, 1);
tools._setCoords(line, coordinates, lineStringIndex);
tools.displayShapes(line);
}
Expand All @@ -126,10 +129,18 @@ const tools = {
const lineStrings = getPrivate(line, type);
const shapes = lineStrings[lineStringIndex] = lineStrings[lineStringIndex] || [];
const _editor = line._e();
const zLayer = _editor.getZLayer(line) -1;
const zLayer = _editor.getZLayer(line) - 1;
const altitude = _editor.getStyleProperty(line, 'altitude');


for (let i = 0; i < path.length; i++) {
shapes[i] = new Shape(line, path[i].slice(), lineStringIndex, i, zLayer, tools);
let coordinate = path[i].slice();
if (typeof altitude == 'number') {
coordinate[2] = altitude;
} else if (!altitude) {
coordinate[2] = 0;
}
shapes[i] = new Shape(line, coordinate, lineStringIndex, i, zLayer, tools);
_editor.objects.overlay.addFeature(shapes[i]);
}
},
Expand Down Expand Up @@ -211,19 +222,23 @@ const tools = {
return index >= 0 ? coordinates[index] : coordinates;
},

_setCoords: function(line: Line, lineString: Coordinate[], lineStringIndex?: number) {
_setCoords: function(line: Line, lineString: Coordinate[] | Coordinate[][], lineStringIndex?: number) {
line._e().objects.history.origin(line);

getPrivate(line).isGeoMod = true;

let coordinates: Coordinate[] | Coordinate[][] = lineString;


if (tools.isMultiLineString(line) && lineStringIndex >= 0) {
coordinates = line.coord();
coordinates[lineStringIndex] = lineString;
if (tools.isMultiLineString(line)) {
if (lineStringIndex >= 0) {
coordinates = line.coord();
coordinates[lineStringIndex] = <Coordinate[]>lineString;
}
} else {
if (typeof coordinates[0][0] != 'number') {
coordinates = <Coordinate[]>coordinates[0];
}
}

line.getProvider().setFeatureCoordinates(line, coordinates);
},

Expand Down
16 changes: 15 additions & 1 deletion packages/editor/src/features/link/Navlink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import DirectionHint from '../../tools/DirectionHint';
import oTools from './NavlinkTools';
import {Feature} from '../feature/Feature';
import {JSUtils} from '@here/xyz-maps-common';
import {GeoPoint, PixelPoint, Style} from '@here/xyz-maps-core';
import {GeoJSONCoordinate, GeoPoint, PixelPoint, Style} from '@here/xyz-maps-core';

let UNDEF;

Expand Down Expand Up @@ -315,6 +315,20 @@ export class Navlink extends Feature {
return typeof index == 'number' ? zLevels[index] : zLevels.slice(0);
};

/**
* Returns an array of Boolean values indicating whether the corresponding shape is selected at index or not.
*/
getSelectedShapes(): boolean[] | boolean[][] {
const line = this;
const selectedShapes = oTools.private(line, 'selectedShapes');
const coordinates = <GeoJSONCoordinate[]>line.geometry.coordinates;
const selected = [];
for (let i = 0; i < coordinates.length; i++) {
selected[i] = !!(selectedShapes[i]);
}

return selected;
}

/**
* Set the z-levels for the coordinates of the Navlink Feature.
Expand Down
Loading

0 comments on commit f2a3438

Please sign in to comment.