Skip to content

Commit

Permalink
Camera update (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
slimbuck authored Jul 9, 2024
1 parent 557fe6c commit e18d79c
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 188 deletions.
12 changes: 9 additions & 3 deletions src/controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,17 @@ class PointerController {
x = event.offsetX;
y = event.offsetY;

if (buttons[0]) {
// right button can be used to orbit with ctrl key and to zoom with alt | meta key
const mod = buttons[2] ?
(event.shiftKey || event.ctrlKey ? 'orbit' :
(event.altKey || event.metaKey ? 'zoom' : null)) :
null;

if (mod === 'orbit' || (mod === null && buttons[0])) {
orbit(dx, dy);
} else if (buttons[1]) {
} else if (mod === 'zoom' || (mod === null && buttons[1])) {
zoom(dy * -0.02);
} else if (buttons[2]) {
} else if (mod === 'pan' || (mod === null && buttons[2])) {
pan(x, y, dx, dy);
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const registerEditorEvents = (events: Events, editHistory: EditHistory, scene: S
// get the list of selected splats (currently limited to just a single one)
const selectedSplats = () => {
const selected = events.invoke('selection') as Splat;
return [selected];
return selected ? [selected] : [];
};

const debugSphereCenter = new Vec3();
Expand Down
18 changes: 9 additions & 9 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ const initShortcuts = (events: Events) => {
shortcuts.register(['Delete', 'Backspace'], { event: 'select.delete' });
shortcuts.register(['Escape'], { event: 'tool.deactivate' });
shortcuts.register(['Tab'], { event: 'selection.next' });
shortcuts.register(['1'], { event: 'tool.move' });
shortcuts.register(['2'], { event: 'tool.rotate' });
shortcuts.register(['3'], { event: 'tool.scale' });
shortcuts.register(['1'], { event: 'tool.move', sticky: true });
shortcuts.register(['2'], { event: 'tool.rotate', sticky: true });
shortcuts.register(['3'], { event: 'tool.scale', sticky: true });
shortcuts.register(['G', 'g'], { event: 'show.gridToggle' });
shortcuts.register(['C', 'c'], { event: 'tool.toggleCoordSpace' });
shortcuts.register(['F', 'f'], { event: 'camera.focus' });
shortcuts.register(['B', 'b'], { event: 'tool.brushSelection' });
shortcuts.register(['R', 'r'], { event: 'tool.rectSelection' });
shortcuts.register(['P', 'p'], { event: 'tool.pickerSelection' });
shortcuts.register(['B', 'b'], { event: 'tool.brushSelection', sticky: true });
shortcuts.register(['R', 'r'], { event: 'tool.rectSelection', sticky: true });
shortcuts.register(['P', 'p'], { event: 'tool.pickerSelection', sticky: true });
shortcuts.register(['A', 'a'], { event: 'select.all' });
shortcuts.register(['A', 'a'], { event: 'select.none', shift: true });
shortcuts.register(['I', 'i'], { event: 'select.invert' });
Expand Down Expand Up @@ -153,9 +153,9 @@ const main = async () => {
toolManager.register('move', new MoveTool(events, editHistory, scene));
toolManager.register('rotate', new RotateTool(events, editHistory, scene));
toolManager.register('scale', new ScaleTool(events, editHistory, scene));
toolManager.register('rectSelection', new RectSelection(events, editorUI.canvasContainer.dom));
toolManager.register('brushSelection', new BrushSelection(events, editorUI.canvasContainer.dom));
toolManager.register('pickerSelection', new PickerSelection(events, editorUI.canvasContainer.dom));
toolManager.register('rectSelection', new RectSelection(events, editorUI.canvasContainer.dom, editorUI.canvas));
toolManager.register('brushSelection', new BrushSelection(events, editorUI.canvasContainer.dom, editorUI.canvas));
toolManager.register('pickerSelection', new PickerSelection(events, editorUI.canvasContainer.dom, editorUI.canvas));

window.scene = scene;

Expand Down
42 changes: 35 additions & 7 deletions src/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,66 @@ import { Events } from "./events";
interface ShortcutOptions {
ctrl?: boolean;
shift?: boolean;
sticky?: boolean;
func?: () => void;
event?: string;
}

class Shortcuts {
shortcuts: { keys: string[], options: ShortcutOptions }[] = [];
shortcuts: { keys: string[], options: ShortcutOptions, toggled: boolean }[] = [];

constructor(events: Events) {
const shortcuts = this.shortcuts;

// register keyboard handler
document.addEventListener('keydown', (e) => {
const handleEvent = (e: KeyboardEvent, down: boolean) => {
// skip keys in input fields
if (e.target !== document.body) return;

for (let i = 0; i < shortcuts.length; i++) {
if (shortcuts[i].keys.includes(e.key) &&
!!shortcuts[i].options.ctrl === !!(e.ctrlKey || e.metaKey) &&
!!shortcuts[i].options.shift === !!e.shiftKey) {
const shortcut = shortcuts[i];
const options = shortcut.options;

if (shortcut.keys.includes(e.key) &&
!!options.ctrl === !!(e.ctrlKey || e.metaKey) &&
!!options.shift === !!e.shiftKey) {

// handle sticky shortcuts
if (options.sticky) {
if (down) {
shortcut.toggled = e.repeat;
}

if (down === shortcut.toggled) {
return;
}
} else {
// ignore up events on non-sticky shortcuts
if (!down) return;
}

if (shortcuts[i].options.event) {
events.fire(shortcuts[i].options.event);
} else {
shortcuts[i].options.func();
}

break;
}
}
};

// register keyboard handler
document.addEventListener('keydown', (e) => {
handleEvent(e, true);
});

document.addEventListener('keyup', (e) => {
handleEvent(e, false);
});
}

register(keys: string[], options: ShortcutOptions) {
this.shortcuts.push({ keys, options });
this.shortcuts.push({ keys, options, toggled: false });
}
}

Expand Down
16 changes: 4 additions & 12 deletions src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -201,27 +201,19 @@ body {
background-color: #f60 !important;
}

#select-root {
.select-svg {
display: none;
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}

#select-svg {
display: none;
width: 100%;
height: 100%;
}

#select-canvas {
#brush-select-canvas {
display: none;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.4;
pointer-events: none;
}

#canvas-container {
Expand Down
134 changes: 55 additions & 79 deletions src/tools/brush-selection.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,33 @@
import { Events } from "../events";

class BrushSelection {
events: Events;
root: HTMLElement;
canvas: HTMLCanvasElement;
context: CanvasRenderingContext2D;
svg: SVGElement;
circle: SVGCircleElement;
radius = 40;
prev = { x: 0, y: 0 };

constructor(events: Events, parent: HTMLElement) {
// create input dom
const root = document.createElement('div');
root.id = 'select-root';
activate: () => void;
deactivate: () => void;

constructor(events: Events, parent: HTMLElement, canvas: HTMLCanvasElement) {
let radius = 40;

// create svg
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.id = 'select-svg';
svg.style.display = 'inline';
root.style.touchAction = 'none';
svg.id = 'brush-select-svg';
svg.classList.add('select-svg');

// create circle element
const circle = document.createElementNS(svg.namespaceURI, 'circle') as SVGCircleElement;
circle.setAttribute('r', this.radius.toString());
circle.setAttribute('r', radius.toString());
circle.setAttribute('fill', 'rgba(255, 102, 0, 0.2)');
circle.setAttribute('stroke', '#f60');
circle.setAttribute('stroke-width', '1');
circle.setAttribute('stroke-dasharray', '5, 5');

// create canvas
const canvas = document.createElement('canvas');
canvas.id = 'select-canvas';
const selectCanvas = document.createElement('canvas');
selectCanvas.id = 'brush-select-canvas';

const context = canvas.getContext('2d');
const context = selectCanvas.getContext('2d');
context.globalCompositeOperation = 'copy';

const prev = { x: 0, y: 0 };
let dragId: number | undefined;

const update = (e: PointerEvent) => {
Expand All @@ -49,114 +41,98 @@ class BrushSelection {
context.beginPath();
context.strokeStyle = '#f60';
context.lineCap = 'round';
context.lineWidth = this.radius * 2;
context.moveTo(this.prev.x, this.prev.y);
context.lineWidth = radius * 2;
context.moveTo(prev.x, prev.y);
context.lineTo(x, y);
context.stroke();

this.prev.x = x;
this.prev.y = y;
prev.x = x;
prev.y = y;
}
};

root.addEventListener('contextmenu', (e) => {
e.preventDefault();
});

root.addEventListener('pointerdown', (e) => {
const pointerdown = (e: PointerEvent) => {
if (dragId === undefined && (e.pointerType === 'mouse' ? e.button === 0 : e.isPrimary)) {
e.preventDefault();
e.stopPropagation();

dragId = e.pointerId;
root.setPointerCapture(dragId);
canvas.setPointerCapture(dragId);

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

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

// display it
canvas.style.display = 'inline';
selectCanvas.style.display = 'inline';

this.prev.x = e.offsetX;
this.prev.y = e.offsetY;
prev.x = e.offsetX;
prev.y = e.offsetY;

update(e);
}
});
};

root.addEventListener('pointermove', (e) => {
const pointermove = (e: PointerEvent) => {
if (dragId !== undefined) {
e.preventDefault();
e.stopPropagation();
}

update(e);
});
};

root.addEventListener('pointerup', (e) => {
const pointerup = (e: PointerEvent) => {
if (e.pointerId === dragId) {
e.preventDefault();
e.stopPropagation();

root.releasePointerCapture(dragId);
canvas.releasePointerCapture(dragId);
dragId = undefined;

canvas.style.display = 'none';
selectCanvas.style.display = 'none';

this.events.fire(
events.fire(
'select.byMask',
e.shiftKey ? 'add' : (e.ctrlKey ? 'remove' : 'set'),
context.getImageData(0, 0, canvas.width, canvas.height)
context.getImageData(0, 0, selectCanvas.width, selectCanvas.height)
);
}
});
};

parent.appendChild(root);
root.appendChild(svg);
svg.appendChild(circle);
root.appendChild(canvas);
this.activate = () => {
svg.style.display = 'inline';
canvas.addEventListener('pointerdown', pointerdown, true);
canvas.addEventListener('pointermove', pointermove, true);
canvas.addEventListener('pointerup', pointerup, true);

};

this.deactivate = () => {
svg.style.display = 'none';
canvas.removeEventListener('pointerdown', pointerdown, true);
canvas.removeEventListener('pointermove', pointermove, true);
canvas.removeEventListener('pointerup', pointerup, true);
};

events.on('tool.brushSelection.smaller', () => {
this.smaller();
radius = Math.max(1, radius / 1.05);
circle.setAttribute('r', radius.toString());
});

events.on('tool.brushSelection.bigger', () => {
this.bigger();
radius = Math.min(500, radius * 1.05);
circle.setAttribute('r', radius.toString());
});

this.events = events;
this.root = root;
this.svg = svg;
this.circle = circle;
this.canvas = canvas;
this.context = context;

canvas.width = parent.clientWidth;
canvas.height = parent.clientHeight;
}

activate() {
this.root.style.display = 'block';
}

deactivate() {
this.root.style.display = 'none';
}

smaller() {
this.radius = Math.max(1, this.radius / 1.05);
this.circle.setAttribute('r', this.radius.toString());
}

bigger() {
this.radius = Math.min(500, this.radius * 1.05);
this.circle.setAttribute('r', this.radius.toString());
svg.appendChild(circle);
parent.appendChild(svg);
parent.appendChild(selectCanvas);
}
}

Expand Down
Loading

0 comments on commit e18d79c

Please sign in to comment.