Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganization of the selection-classes #268

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { RotateTool } from './tools/rotate-tool';
import { ScaleTool } from './tools/scale-tool';
import { Shortcuts } from './shortcuts';
import { Events } from './events';
import { LassoSelection } from './tools/lasso-selection';

declare global {
interface LaunchParams {
Expand Down Expand Up @@ -72,6 +73,7 @@ const initShortcuts = (events: Events) => {
shortcuts.register(['F', 'f'], { event: 'camera.focus' });
shortcuts.register(['R', 'r'], { event: 'tool.rectSelection', sticky: true });
shortcuts.register(['P', 'p'], { event: 'tool.polygonSelection', sticky: true });
shortcuts.register(['L', 'l'], { event: 'tool.lassoSelection', sticky: true });
shortcuts.register(['B', 'b'], { event: 'tool.brushSelection', sticky: true });
shortcuts.register(['A', 'a'], { event: 'select.all' });
shortcuts.register(['A', 'a'], { event: 'select.none', shift: true });
Expand Down Expand Up @@ -189,6 +191,7 @@ const main = async () => {
toolManager.register('rectSelection', new RectSelection(events, editorUI.toolsContainer.dom));
toolManager.register('brushSelection', new BrushSelection(events, editorUI.toolsContainer.dom, mask));
toolManager.register('polygonSelection', new PolygonSelection(events, editorUI.toolsContainer.dom, mask));
toolManager.register('lassoSelection', new LassoSelection(events, editorUI.toolsContainer.dom, mask));
toolManager.register('sphereSelection', new SphereSelection(events, scene, editorUI.canvasContainer));
toolManager.register('move', new MoveTool(events, scene));
toolManager.register('rotate', new RotateTool(events, scene));
Expand Down
159 changes: 159 additions & 0 deletions src/tools/lasso-selection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Events } from "../events";
import { Mask, Point, ScreenspaceSelection } from "./screenspace-selection";

class LassoSelection extends ScreenspaceSelection{
private points: Point[] = [];
private currentPoint: Point = null;
private lastPointTime = 0;
private polyline: SVGPolylineElement;

constructor(events: Events, parent: HTMLElement, mask: Mask) {
super(events, parent, mask);

this.initSVG();

this.eventHandlers = {
pointerdown: this.pointerdown.bind(this),
pointermove: this.pointermove.bind(this),
pointerup: this.pointerup.bind(this)
}
}

protected initSVG(){
// create svg
this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGSVGElement;
this.svg.id = 'lasso-select-svg';
this.svg.classList.add('select-svg');

// create polyline element
this.polyline = document.createElementNS(this.svg.namespaceURI, 'polyline') as SVGPolylineElement;
this.polyline.setAttribute('fill', 'none');
this.polyline.setAttribute('stroke-width', '1');
this.polyline.setAttribute('stroke-dasharray', '5, 5');
this.polyline.setAttribute('stroke-dashoffset', '0');

this.svg.appendChild(this.polyline);
this.parent.appendChild(this.svg);
}

protected updateSVG() {
this.polyline.setAttribute('points', [...this.points, this.currentPoint].reduce((prev, current) => prev + `${current.x}, ${current.y} `, ""));
this.polyline.setAttribute('stroke', this.isClosed() ? '#fa6' : '#f60');
};

private isClosed() {
return this.points.length > 1 && this.dist(this.currentPoint, this.points[0]) < 8;
};

private update(e: PointerEvent){
this.currentPoint = {x: e.offsetX, y: e.offsetY};

const distance = this.points.length === 0 ? 0 : this.dist(this.currentPoint, this.points[this.points.length - 1]);
const millis = Date.now() - this.lastPointTime;
const preventCorners = distance > 20;
const slowNarrowSpacing = millis > 500 && distance > 2;
const fasterMediumSpacing = millis > 200 && distance > 10;
const firstPoints = this.points.length === 0;


if (this.dragId !== undefined && (preventCorners || slowNarrowSpacing || fasterMediumSpacing || firstPoints)) {
this.points.push(this.currentPoint);
this.lastPointTime = Date.now();
this.updateSVG();
}
};

private pointerdown(e: PointerEvent){
if (this.dragId === undefined && (e.pointerType === 'mouse' ? e.button === 0 : e.isPrimary)) {
e.preventDefault();
e.stopPropagation();

this.dragId = e.pointerId;
this.parent.setPointerCapture(this.dragId);

// initialize canvas
if (this.mask.canvas.width !== this.parent.clientWidth || this.mask.canvas.height !== this.parent.clientHeight) {
this.mask.canvas.width = this.parent.clientWidth;
this.mask.canvas.height = this.parent.clientHeight;
}

// clear canvas
this.mask.context.clearRect(0, 0, this.mask.canvas.width, this.mask.canvas.height);

// display it
this.mask.canvas.style.display = 'inline';

this.update(e);
}
};

private pointermove(e: PointerEvent){
if (this.dragId !== undefined) {
e.preventDefault();
e.stopPropagation();
}

this.update(e);
};

protected dragEnd(){
this.parent.releasePointerCapture(this.dragId);
this.dragId = undefined;
this.mask.canvas.style.display = 'none';
};

private pointerup(e: PointerEvent) {
if (e.pointerId === this.dragId) {
e.preventDefault();
e.stopPropagation();

this.dragEnd();

this.commitSelection(e);

this.events.fire(
'select.byMask',
e.shiftKey ? 'add' : (e.ctrlKey ? 'remove' : 'set'),
this.mask.canvas,
this.mask.context
);
}
};

private commitSelection(e: PointerEvent) {
// initialize canvas
if (this.mask.canvas.width !== this.parent.clientWidth || this.mask.canvas.height !== this.parent.clientHeight) {
this.mask.canvas.width = this.parent.clientWidth;
this.mask.canvas.height = this.parent.clientHeight;
}

// clear canvas
this.mask.context.clearRect(0, 0, this.mask.canvas.width, this.mask.canvas.height);

this.mask.context.beginPath();
this.mask.context.fillStyle = '#f60';
this.mask.context.beginPath();
this.points.forEach((p, idx) => {
if (idx === 0) {
this.mask.context.moveTo(p.x, p.y);
}
else {
this.mask.context.lineTo(p.x, p.y);
}
});
this.mask.context.closePath();
this.mask.context.fill();

this.events.fire(
'select.byMask',
e.shiftKey ? 'add' : (e.ctrlKey ? 'remove' : 'set'),
this.mask.canvas,
this.mask.context
);

this.points = [];
this.updateSVG();
};
}

export { LassoSelection };
Loading
Loading