Skip to content

Commit

Permalink
feat: rectangle hitTest optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
F-star committed Sep 30, 2024
1 parent 811a3ba commit e1636c1
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 13 deletions.
32 changes: 31 additions & 1 deletion packages/core/src/graphics/graphics/graphics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ import { generateKeyBetween, generateNKeysBetween } from 'fractional-indexing';
import { HALF_PI } from '../../constant';
import { type ControlHandle } from '../../control_handle_manager';
import { type ImgManager } from '../../Img_manager';
import { DEFAULT_IMAGE, type PaintImage, PaintType } from '../../paint';
import {
DEFAULT_IMAGE,
type IPaint,
type PaintImage,
PaintType,
} from '../../paint';
import {
GraphicsType,
type IFillStrokeSVGAttrs,
Expand Down Expand Up @@ -282,6 +287,31 @@ export class SuikaGraphics<ATTRS extends GraphicsAttrs = GraphicsAttrs> {
});
}

private isPaintNoRender(paints: IPaint[] | undefined) {
if (!paints || paints.length === 0) {
return true;
}
let isNoRender = true;
for (const paint of paints) {
if (
(paint.type === PaintType.Solid && paint.attrs.a !== 0) ||
(paint.type === PaintType.Image && paint.attrs.opacity !== 0)
) {
isNoRender = false;
break;
}
}
return isNoRender;
}

protected isStrokeNoRender() {
return this.isPaintNoRender(this.attrs.stroke);
}

protected isFillNoRender() {
return this.isPaintNoRender(this.attrs.fill);
}

hitTest(point: IPoint, tol = 0) {
return isPointInTransformedRect(
point,
Expand Down
42 changes: 30 additions & 12 deletions packages/core/src/graphics/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,41 @@ export class SuikaRect extends SuikaGraphics<RectAttrs> {
);
}

override hitTest(point: IPoint, padding = 0): boolean {
override hitTest(point: IPoint, tol = 0): boolean {
const tf = new Matrix(...this.getWorldTransform());
const pt = tf.applyInverse(point);
const maxCornerRadius = this.getMaxCornerRadius();
return isPointInRoundRect(
const cornerRadii = new Array(4).fill(
Math.min(this.attrs.cornerRadius ?? 0, maxCornerRadius),
);
const strokeWidth = this.attrs.strokeWidth ?? 0;
const halfStrokeWidth = strokeWidth / 2;
const rect = {
x: 0,
y: 0,
width: this.attrs.width,
height: this.attrs.height,
};

const hit = isPointInRoundRect(
pt,
{
x: 0,
y: 0,
width: this.attrs.width,
height: this.attrs.height,
},
new Array(4).fill(
Math.min(this.attrs.cornerRadius ?? 0, maxCornerRadius),
),
padding + (this.attrs.strokeWidth ?? 0) / 2,
rect,
cornerRadii,
tol + halfStrokeWidth,
);
if (this.isFillNoRender()) {
if (!hit) {
return false;
}
return !isPointInRoundRect(
pt,
rect,
cornerRadii.map((r) => Math.max(0, r - halfStrokeWidth)),
-halfStrokeWidth - tol,
);
} else {
return hit;
}
}

override getControlHandles(zoom: number, initial?: boolean) {
Expand Down

0 comments on commit e1636c1

Please sign in to comment.