Skip to content

Commit

Permalink
feat: add path/bezier hitTest algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
F-star committed Aug 23, 2024
1 parent 644f5db commit 8906c75
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 9 deletions.
3 changes: 1 addition & 2 deletions packages/core/src/graphs/path/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,7 @@ export class SuikaPath extends SuikaGraphics<PathAttrs> {
const tf = new Matrix(...this.getWorldTransform());
const point = tf.applyInverse({ x, y });
const geoPath = this.getGeoPath();
const { dist } = geoPath.project(point);
return dist <= tol + this.getStrokeWidth() / 2;
return geoPath.hitTest(point, tol + this.getStrokeWidth() / 2);
}

override toJSON() {
Expand Down
44 changes: 37 additions & 7 deletions packages/geo/src/geo/geo_bezier_class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,45 @@ export class GeoBezier {
return this._bbox;
}

private checkInBbox(point: IPoint, tol = 0) {
return isPointInBox(this.getBbox(), point, tol);
}

// TODO:
hitTest(point: IPoint, tol: number) {
if (!this.checkInBbox(point, tol)) {
hitTest(point: IPoint, tol: number): boolean {
if (!isPointInBox(this.getBbox(), point, tol)) {
return false;
}

// based on the modification of "project algorithm"
const lookupTable = this.getLookupTable();

let minDist = Number.MAX_SAFE_INTEGER;
let minIndex = -1;

for (let i = 0; i < lookupTable.length; i++) {
const item = lookupTable[i];
const dist = distance(point, item.pt); // TODO: optimize, no sqrt
if (dist <= tol) {
return true;
}
if (dist < minDist) {
minDist = dist;
minIndex = i;
}
}

const minT = lookupTable[minIndex].t;

const t1 = minIndex > 0 ? lookupTable[minIndex - 1].t : minT;
const t2 =
minIndex < lookupTable.length - 1 ? lookupTable[minIndex + 1].t : minT;

const step = 0.001;
for (let t = t1; t <= t2; t += step) {
const pt = this.compute(t);
const dist = distance(point, pt); // TODO: optimize, no sqrt
if (dist <= tol) {
return true;
}
}

return false;
}

getLookupTable() {
Expand Down
11 changes: 11 additions & 0 deletions packages/geo/src/geo/geo_path_class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ export class GeoPath {
};
}

hitTest(point: IPoint, tol: number) {
for (const bezierList of this.bezierLists) {
for (const bezier of bezierList) {
if (bezier.hitTest(point, tol)) {
return true;
}
}
}
return false;
}

project(point: IPoint) {
let minDist = Infinity;
let minDistPoint: IPoint | null = null;
Expand Down

0 comments on commit 8906c75

Please sign in to comment.