@mocanvas/editor 4.2.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +18 -4
- package/dist/index.js +45 -14
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -943,6 +943,24 @@ declare abstract class Geometry2d {
|
|
|
943
943
|
ignoreHit(_point: VecLike): boolean;
|
|
944
944
|
nearestPoint(point: VecLike, _filters?: Geometry2dFilters): Vec;
|
|
945
945
|
distanceToPoint(point: VecLike, hitInside?: boolean, filters?: Geometry2dFilters): number;
|
|
946
|
+
/**
|
|
947
|
+
* Whether `point` hits this geometry, within `margin`.
|
|
948
|
+
*
|
|
949
|
+
* The interior counts when the shape is **filled**, or when the caller asks
|
|
950
|
+
* for `hitInside` — the two are alternatives, not a requirement and a
|
|
951
|
+
* refinement. `hitInside` means "count the interior of a HOLLOW shape too",
|
|
952
|
+
* which is what a marquee or a drop target wants; it is not the only way the
|
|
953
|
+
* interior is ever reachable.
|
|
954
|
+
*
|
|
955
|
+
* This used to test `hitInside` alone, so a solid rectangle could not be
|
|
956
|
+
* selected by clicking its middle — only its outline answered. Nothing failed
|
|
957
|
+
* and nothing warned: every feature built on hit testing simply returned
|
|
958
|
+
* nothing, which reads as several unrelated bugs rather than one.
|
|
959
|
+
*
|
|
960
|
+
* An unfilled shape still misses in the middle. A hollow rectangle is a frame
|
|
961
|
+
* around empty space, and clicking the space inside it should reach whatever
|
|
962
|
+
* is behind.
|
|
963
|
+
*/
|
|
946
964
|
hitTestPoint(point: VecLike, margin?: number, hitInside?: boolean, _filters?: Geometry2dFilters): boolean;
|
|
947
965
|
/** Whether `point` lies within the bounding box, which is the cheap pre-test. */
|
|
948
966
|
isPointInBounds(point: VecLike, margin?: number): boolean;
|
|
@@ -8273,10 +8291,6 @@ declare const COLLABORATOR_INACTIVE_TIMEOUT = 60000;
|
|
|
8273
8291
|
* all in a raw-ESM browser page, hence the guard rather than a bare read.
|
|
8274
8292
|
*/
|
|
8275
8293
|
declare function warnOnce(key: string, message: string): void;
|
|
8276
|
-
/**
|
|
8277
|
-
* The editor: document access, selection, camera, tool dispatch, and the
|
|
8278
|
-
* bridge that mirrors the current page into the WASM engine.
|
|
8279
|
-
*/
|
|
8280
8294
|
declare class Editor extends EventEmitter<EditorEvents> {
|
|
8281
8295
|
readonly store: EditorStore;
|
|
8282
8296
|
readonly engine: EngineBridge;
|
package/dist/index.js
CHANGED
|
@@ -1696,8 +1696,26 @@ var Geometry2d = class {
|
|
|
1696
1696
|
if (hitInside && this.isClosed && this.isFilled && pointInPolygon(point, this.vertices)) return -d;
|
|
1697
1697
|
return d;
|
|
1698
1698
|
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Whether `point` hits this geometry, within `margin`.
|
|
1701
|
+
*
|
|
1702
|
+
* The interior counts when the shape is **filled**, or when the caller asks
|
|
1703
|
+
* for `hitInside` — the two are alternatives, not a requirement and a
|
|
1704
|
+
* refinement. `hitInside` means "count the interior of a HOLLOW shape too",
|
|
1705
|
+
* which is what a marquee or a drop target wants; it is not the only way the
|
|
1706
|
+
* interior is ever reachable.
|
|
1707
|
+
*
|
|
1708
|
+
* This used to test `hitInside` alone, so a solid rectangle could not be
|
|
1709
|
+
* selected by clicking its middle — only its outline answered. Nothing failed
|
|
1710
|
+
* and nothing warned: every feature built on hit testing simply returned
|
|
1711
|
+
* nothing, which reads as several unrelated bugs rather than one.
|
|
1712
|
+
*
|
|
1713
|
+
* An unfilled shape still misses in the middle. A hollow rectangle is a frame
|
|
1714
|
+
* around empty space, and clicking the space inside it should reach whatever
|
|
1715
|
+
* is behind.
|
|
1716
|
+
*/
|
|
1699
1717
|
hitTestPoint(point, margin = 0, hitInside = false, _filters) {
|
|
1700
|
-
if (hitInside && this.isClosed && pointInPolygon(point, this.vertices)) return true;
|
|
1718
|
+
if ((hitInside || this.isFilled) && this.isClosed && pointInPolygon(point, this.vertices)) return true;
|
|
1701
1719
|
return Vec.Dist(point, this.nearestPoint(point)) <= margin;
|
|
1702
1720
|
}
|
|
1703
1721
|
/** Whether `point` lies within the bounding box, which is the cheap pre-test. */
|
|
@@ -1886,7 +1904,7 @@ var TransformedGeometry2d = class extends Geometry2d {
|
|
|
1886
1904
|
return d;
|
|
1887
1905
|
}
|
|
1888
1906
|
hitTestPoint(point, margin = 0, hitInside = false, filters) {
|
|
1889
|
-
if (hitInside && this.isClosed && pointInPolygon(point, this.vertices)) return true;
|
|
1907
|
+
if ((hitInside || this.isFilled) && this.isClosed && pointInPolygon(point, this.vertices)) return true;
|
|
1890
1908
|
return Vec.Dist(point, this.nearestPoint(point, filters)) <= margin;
|
|
1891
1909
|
}
|
|
1892
1910
|
ignoreHit(point) {
|
|
@@ -7834,6 +7852,12 @@ function warnOnce(key, message) {
|
|
|
7834
7852
|
warnedKeys.add(key);
|
|
7835
7853
|
console.warn(message);
|
|
7836
7854
|
}
|
|
7855
|
+
function assertUtilMapPresent(map, name) {
|
|
7856
|
+
if (map) return;
|
|
7857
|
+
throw new Error(
|
|
7858
|
+
`editor.${name} is undefined, which the constructor never leaves it. The usual cause is a subclass redeclaring \`${name}\` without an initializer: at ES2022 that DEFINES the field as undefined after the base constructor ran. Write \`declare readonly ${name}: ...\` instead, or drop the redeclaration.`
|
|
7859
|
+
);
|
|
7860
|
+
}
|
|
7837
7861
|
var Editor = class extends EventEmitter {
|
|
7838
7862
|
store;
|
|
7839
7863
|
engine;
|
|
@@ -8415,6 +8439,7 @@ var Editor = class extends EventEmitter {
|
|
|
8415
8439
|
*/
|
|
8416
8440
|
getShapeUtil(shape) {
|
|
8417
8441
|
const type = typeof shape === "string" ? shape : shape.type;
|
|
8442
|
+
assertUtilMapPresent(this.shapeUtils, "shapeUtils");
|
|
8418
8443
|
const util = this.shapeUtils[type];
|
|
8419
8444
|
if (!util) throw new Error(`No ShapeUtil registered for type "${type}"`);
|
|
8420
8445
|
return util;
|
|
@@ -8677,29 +8702,34 @@ var Editor = class extends EventEmitter {
|
|
|
8677
8702
|
getShapeAtPoint(point, opts = {}) {
|
|
8678
8703
|
this.flushEngine();
|
|
8679
8704
|
const margin = (opts.margin ?? this.getHitTestMargin()) / this.getZoomLevel();
|
|
8680
|
-
const bits = this.hitFilterBits(opts) | (opts.hitInside ? 0 : 4);
|
|
8681
8705
|
const filter = this.hitFilter(opts);
|
|
8682
|
-
if (!filter) {
|
|
8683
|
-
const h = this.engine.hitTest(point.x, point.y, margin,
|
|
8706
|
+
if (!filter && !opts.hitInside) {
|
|
8707
|
+
const h = this.engine.hitTest(point.x, point.y, margin, this.hitFilterBits(opts));
|
|
8684
8708
|
const id = this.handles.id(h);
|
|
8685
8709
|
return id ? this.getShape(id) : void 0;
|
|
8686
8710
|
}
|
|
8687
|
-
|
|
8688
|
-
if (filter(shape)) return shape;
|
|
8689
|
-
}
|
|
8690
|
-
return void 0;
|
|
8711
|
+
return this.getShapesAtPoint(point, opts)[0];
|
|
8691
8712
|
}
|
|
8692
8713
|
/** Shapes under a point, topmost first. */
|
|
8693
8714
|
getShapesAtPoint(point, opts = {}) {
|
|
8694
8715
|
this.flushEngine();
|
|
8695
8716
|
const margin = (opts.margin ?? this.getHitTestMargin()) / this.getZoomLevel();
|
|
8696
|
-
const handles = this.engine.queryBox(point.x - margin, point.y - margin, point.x + margin, point.y + margin, 0, this.hitFilterBits(opts));
|
|
8697
8717
|
const filter = this.hitFilter(opts);
|
|
8698
8718
|
const out = [];
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8719
|
+
let candidates;
|
|
8720
|
+
if (opts.hitInside) {
|
|
8721
|
+
candidates = this.getCurrentPageShapesSorted().filter((s) => this.getShapePageBounds(s)?.containsPoint(point, margin));
|
|
8722
|
+
candidates.reverse();
|
|
8723
|
+
} else {
|
|
8724
|
+
const handles = this.engine.queryBox(point.x - margin, point.y - margin, point.x + margin, point.y + margin, 0, this.hitFilterBits(opts));
|
|
8725
|
+
candidates = [];
|
|
8726
|
+
for (let i = handles.length - 1; i >= 0; i--) {
|
|
8727
|
+
const id = this.handles.id(handles[i]);
|
|
8728
|
+
const shape = id ? this.getShape(id) : void 0;
|
|
8729
|
+
if (shape) candidates.push(shape);
|
|
8730
|
+
}
|
|
8731
|
+
}
|
|
8732
|
+
for (const shape of candidates) {
|
|
8703
8733
|
if (filter && !filter(shape)) continue;
|
|
8704
8734
|
const local = this.getPointInShapeSpace(shape, point);
|
|
8705
8735
|
const geo = this.getShapeGeometry(shape);
|
|
@@ -8886,6 +8916,7 @@ var Editor = class extends EventEmitter {
|
|
|
8886
8916
|
);
|
|
8887
8917
|
getBindingUtil(binding) {
|
|
8888
8918
|
const type = typeof binding === "string" ? binding : binding.type;
|
|
8919
|
+
assertUtilMapPresent(this.bindingUtils, "bindingUtils");
|
|
8889
8920
|
const util = this.bindingUtils[type];
|
|
8890
8921
|
if (!util) throw new Error(`No BindingUtil registered for type "${type}"`);
|
|
8891
8922
|
return util;
|