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

Magic contour selection #1704

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion src/fontra/client/core/path-hit-tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class PathHitTester {
}
}

results = results.filter((hit) => hit.t != 0 && hit.t != 1);
// results = results.filter((hit) => hit.t != 0 && hit.t != 1);
results.sort((a, b) => a.d - b.d);
return results[0];
}
Expand Down
101 changes: 79 additions & 22 deletions src/fontra/views/editor/edit-tools-pointer.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class PointerTool extends BaseTool {
sceneController.hoverSelection = selection;
sceneController.hoveredGlyph = undefined;
sceneController.hoverPathHit = pathHit;
sceneController.magicSelectionHit = undefined;

if (!sceneController.hoverSelection.size && !sceneController.hoverPathHit) {
sceneController.hoveredGlyph = this.sceneModel.glyphAtPoint(point);
Expand Down Expand Up @@ -113,6 +114,44 @@ export class PointerTool extends BaseTool {
}
}

selectContour(sceneController, contourIndex, selectModeFunction) {
const instance = this.sceneModel.getSelectedPositionedGlyph().glyph.instance;
const startPoint = instance.path.getAbsolutePointIndex(contourIndex, 0);
const endPoint = instance.path.contourInfo[contourIndex].endPoint;
const newSelection = new Set();
for (const i of range(startPoint, endPoint + 1)) {
const pointType = instance.path.pointTypes[i] & VarPackedPath.POINT_TYPE_MASK;
if (pointType === VarPackedPath.ON_CURVE) {
newSelection.add(`point/${i}`);
}
}
const selection = this._selectionBeforeSingleClick || sceneController.selection;
this._selectionBeforeSingleClick = undefined;
const modeFunc = selectModeFunction(event);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While refactoring this bit of code, you forgot to pass event down. This is now falling back to window.event, which is probably just accidentally working. (window.event is a deprecated property.)

sceneController.selection = modeFunc(selection, newSelection);
}

getNearestHit(sceneController, event, glyphController) {
const positionedGlyph = this.sceneModel.getSelectedPositionedGlyph();
const point = sceneController.localPoint(event);
point.x -= positionedGlyph.x;
point.y -= positionedGlyph.y;
const pathHitTester = glyphController.pathHitTester;
const nearestHit = pathHitTester.findNearest(point);
if (nearestHit) {
sceneController.magicSelectionHit = [
point.x,
point.y,
nearestHit.x,
nearestHit.y,
];
const contourIndex = nearestHit.contourIndex;
this.selectContour(sceneController, contourIndex, getMagicSelectModeFunction);
} else {
sceneController.magicSelectionHit = undefined;
}
}

async handleDrag(eventStream, initialEvent) {
const sceneController = this.sceneController;
const initialSelection = sceneController.selection;
Expand Down Expand Up @@ -201,15 +240,23 @@ export class PointerTool extends BaseTool {
}
}

sceneController.hoveredGlyph = undefined;
if (initiateRectSelect) {
return await this.handleRectSelect(eventStream, initialEvent, initialSelection);
} else if (initiateDrag) {
this.sceneController.sceneModel.initialClickedPointIndex =
initialClickedPointIndex;
const result = await this.handleDragSelection(eventStream, initialEvent);
delete this.sceneController.sceneModel.initialClickedPointIndex;
return result;
if (initialEvent.metaKey && !initialClickedPointIndex) {
const glyphController = await this.sceneModel.getSelectedStaticGlyphController();
this.getNearestHit(sceneController, initialEvent, glyphController);
if (initiateRectSelect) {
return await this.handleMagicSelect(eventStream, sceneController);
}
} else {
sceneController.hoveredGlyph = undefined;
if (initiateRectSelect) {
return await this.handleRectSelect(eventStream, initialEvent, initialSelection);
} else if (initiateDrag) {
this.sceneController.sceneModel.initialClickedPointIndex =
initialClickedPointIndex;
const result = await this.handleDragSelection(eventStream, initialEvent);
delete this.sceneController.sceneModel.initialClickedPointIndex;
return result;
}
}
}

Expand Down Expand Up @@ -259,19 +306,7 @@ export class PointerTool extends BaseTool {
await this.handlePointsDoubleClick(pointIndices);
} else if (sceneController.hoverPathHit) {
const contourIndex = sceneController.hoverPathHit.contourIndex;
const startPoint = instance.path.getAbsolutePointIndex(contourIndex, 0);
const endPoint = instance.path.contourInfo[contourIndex].endPoint;
const newSelection = new Set();
for (const i of range(startPoint, endPoint + 1)) {
const pointType = instance.path.pointTypes[i] & VarPackedPath.POINT_TYPE_MASK;
if (pointType === VarPackedPath.ON_CURVE) {
newSelection.add(`point/${i}`);
}
}
const selection = this._selectionBeforeSingleClick || sceneController.selection;
this._selectionBeforeSingleClick = undefined;
const modeFunc = getSelectModeFunction(event);
sceneController.selection = modeFunc(selection, newSelection);
this.selectContour(sceneController, contourIndex, getSelectModeFunction);
}
}
}
Expand Down Expand Up @@ -319,6 +354,18 @@ export class PointerTool extends BaseTool {
this._selectionBeforeSingleClick = undefined;
}

async handleMagicSelect(eventStream, sceneController) {
const glyphController = await this.sceneModel.getSelectedStaticGlyphController();
for await (const event of eventStream) {
if (event.metaKey) {
this.getNearestHit(sceneController, event, glyphController);
} else {
sceneController.magicSelectionHit = undefined;
}
}
sceneController.magicSelectionHit = undefined;
}

async handleDragSelection(eventStream, initialEvent) {
this.sceneController.sceneModel.showTransformSelection = false;
this._selectionBeforeSingleClick = undefined;
Expand Down Expand Up @@ -695,6 +742,16 @@ function getSelectModeFunction(event) {
: replace;
}

function getMagicSelectModeFunction(event) {
return event.shiftKey
? event[commandKeyProperty]
? union
: symmetricDifference
: event[commandKeyProperty]
? replace
: union;
}

registerVisualizationLayerDefinition({
identifier: "fontra.transform.selection",
name: "Transform selection",
Expand Down
9 changes: 9 additions & 0 deletions src/fontra/views/editor/scene-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,15 @@ export class SceneController {
this.canvasController.requestUpdate();
}

get magicSelectionHit() {
return this.sceneModel.magicSelectionHit;
}

set magicSelectionHit(magicSelHit) {
this.sceneModel.magicSelectionHit = magicSelHit;
this.canvasController.requestUpdate();
}

get backgroundLayers() {
return this.sceneModel.backgroundLayers || [];
}
Expand Down
23 changes: 23 additions & 0 deletions src/fontra/views/editor/visualization-layer-definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,29 @@ registerVisualizationLayerDefinition({
},
});

registerVisualizationLayerDefinition({
identifier: "fontra.magic.select",
name: "Magic select",
selectionMode: "editing",
zIndex: 500,
screenParameters: {
strokeWidth: 1,
lineDash: [10, 10],
},
draw: (context, positionedGlyph, parameters, model, controller) => {
if (model.magicSelectionHit === undefined) {
return;
}
const magicSelHit = model.magicSelectionHit;
const p1x = magicSelHit[0];
const p1y = magicSelHit[1];
const p2x = magicSelHit[2];
const p2y = magicSelHit[3];
context.lineWidth = parameters.strokeWidth;
strokeLineDashed(context, p1x, p1y, p2x, p2y);
},
});

registerVisualizationLayerDefinition({
identifier: "fontra.rect.select",
name: "Rect select",
Expand Down