@fulate/core 1.0.5 → 1.0.7
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
CHANGED
|
@@ -3,12 +3,12 @@ export type { EventName, EventCallback, FulateEvent, FulateEventDetail, CustomEv
|
|
|
3
3
|
export { Node, NodeLifecycleEvent } from "./node/node";
|
|
4
4
|
export type { NodeCreationOptions } from "./node/node";
|
|
5
5
|
export { ElementTransform } from "./node/element-transform";
|
|
6
|
-
export type { ElementTransformClass,
|
|
6
|
+
export type { ElementTransformClass, GeometryBoundsView } from "./node/element-transform";
|
|
7
7
|
export { ChangeImpact, NodeProperties, resolveOrigin, TransformProperties } from "./node/properties";
|
|
8
8
|
export type { OriginX, OriginY, PropertyChanges, PropertyUpdates, TransformableOptions } from "./node/properties";
|
|
9
9
|
export { Element, ElementProperties } from "./node/element";
|
|
10
10
|
export type { AnyElement, BaseElementOption, ElementJSON, ElementOptionPatch, AnimateOptions, AnimateTarget } from "./node/element";
|
|
11
|
-
export { isReparentTransformIntent, resolveReferenceTransform, resolveReferenceTransformTarget } from "./node/reference-transform";
|
|
11
|
+
export { isReparentTransformIntent, resolveAuthoredGeometryFramePatch, resolveReferenceTransform, resolveReferenceTransformTarget } from "./node/reference-transform";
|
|
12
12
|
export type { ElementTransformBaseline, ReferenceTransformBaseline, ReferenceTransformFrame, ReferenceTransformResolution, ReferenceTransformTarget, ReparentTransformIntent, TransformIntent } from "./node/reference-transform";
|
|
13
13
|
export { DEFAULT_CONNECTION_PORT_SCHEMA, getElementConnectionPort, getElementConnectionPortPoint, getElementConnectionPortPoints, projectConnectionPortPoint } from "./utils/connection-port";
|
|
14
14
|
export type { ConnectionPortId, ConnectionPortConfiguration, ConnectionPortDefinition, ConnectionPortLabelStyle, ConnectionPortDirection, ConnectionPortLimit, ConnectionPortLimits, ConnectionPortRef, DerivedConnectionPort, EdgeConnectionPortPosition, ResolvedConnectionPort, ResolvedConnectionPortSlot } from "./utils/connection-port";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clamp, isEqual, isFunction, isNull, isNumber, isObjectLike, isString, isUndefined } from "lodash-es";
|
|
2
|
-
import { Bound, Intersection, clipPolygonToRect, createCanvasGradient, createPointView, formatPremultipliedColor, isGradient, makeBoundingBoxFromRects, parsePremultipliedColor, qrDecompose, rectEdgePosition, transformPoint } from "@fulate/share";
|
|
2
|
+
import { Bound, Intersection, clipPolygonToRect, createCanvasGradient, createPointView, formatPremultipliedColor, isGradient, isIdentityMatrix, makeBoundingBoxFromRects, parsePremultipliedColor, qrDecompose, rectEdgePosition, transformPoint } from "@fulate/share";
|
|
3
3
|
import RBush from "rbush";
|
|
4
4
|
import { Easing, Group, Tween } from "@tweenjs/tween.js";
|
|
5
5
|
//#region packages/core/src/event.ts
|
|
@@ -1790,6 +1790,36 @@ function createGeometryQuery(snapshot) {
|
|
|
1790
1790
|
return snapshot instanceof RectangleGeometrySnapshot ? new RectangleSnapshotGeometryQuery(snapshot) : new SnapshotGeometryQuery(snapshot);
|
|
1791
1791
|
}
|
|
1792
1792
|
//#endregion
|
|
1793
|
+
//#region packages/core/src/node/element-transform-geometry.ts
|
|
1794
|
+
/** Project a local rectangle into a matrix space and return its AABB. */
|
|
1795
|
+
function projectBounds(matrix, bounds) {
|
|
1796
|
+
if (!bounds) return new Bound();
|
|
1797
|
+
const left = bounds.left;
|
|
1798
|
+
const top = bounds.top;
|
|
1799
|
+
const right = left + bounds.width;
|
|
1800
|
+
const bottom = top + bounds.height;
|
|
1801
|
+
if (matrix.b === 0 && matrix.c === 0) {
|
|
1802
|
+
const firstX = matrix.a * left + matrix.e;
|
|
1803
|
+
const secondX = matrix.a * right + matrix.e;
|
|
1804
|
+
const firstY = matrix.d * top + matrix.f;
|
|
1805
|
+
const secondY = matrix.d * bottom + matrix.f;
|
|
1806
|
+
return new Bound(Math.min(firstX, secondX), Math.min(firstY, secondY), Math.max(firstX, secondX), Math.max(firstY, secondY));
|
|
1807
|
+
}
|
|
1808
|
+
const x0 = matrix.a * left + matrix.c * top + matrix.e;
|
|
1809
|
+
const x1 = matrix.a * right + matrix.c * top + matrix.e;
|
|
1810
|
+
const x2 = matrix.a * right + matrix.c * bottom + matrix.e;
|
|
1811
|
+
const x3 = matrix.a * left + matrix.c * bottom + matrix.e;
|
|
1812
|
+
const y0 = matrix.b * left + matrix.d * top + matrix.f;
|
|
1813
|
+
const y1 = matrix.b * right + matrix.d * top + matrix.f;
|
|
1814
|
+
const y2 = matrix.b * right + matrix.d * bottom + matrix.f;
|
|
1815
|
+
const y3 = matrix.b * left + matrix.d * bottom + matrix.f;
|
|
1816
|
+
return new Bound(Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3));
|
|
1817
|
+
}
|
|
1818
|
+
/** Compare a committed matrix with a previous scalar matrix sample. */
|
|
1819
|
+
function matrixChanged(matrix, previousA, previousB, previousC, previousD, previousE, previousF) {
|
|
1820
|
+
return matrix.a !== previousA || matrix.b !== previousB || matrix.c !== previousC || matrix.d !== previousD || matrix.e !== previousE || matrix.f !== previousF;
|
|
1821
|
+
}
|
|
1822
|
+
//#endregion
|
|
1793
1823
|
//#region packages/core/src/node/element-transform.ts
|
|
1794
1824
|
var VISUAL_GEOMETRY_IMPACT$1 = ChangeImpact.LocalGeometry | ChangeImpact.WorldTransform | ChangeImpact.VisualBounds;
|
|
1795
1825
|
var GEOMETRY_CACHE_IMPACT = ChangeImpact.LocalGeometry | ChangeImpact.WorldTransform | ChangeImpact.VisualBounds | ChangeImpact.HitBounds;
|
|
@@ -2051,7 +2081,7 @@ var ElementTransform = class {
|
|
|
2051
2081
|
/** 清除 world 投影;world-dependent local geometry 只在矩阵实际变化时失效。 */
|
|
2052
2082
|
finishWorldMatrixCommit(previousA, previousB, previousC, previousD, previousE, previousF) {
|
|
2053
2083
|
const matrix = this.worldMatrix;
|
|
2054
|
-
const changed = matrix
|
|
2084
|
+
const changed = matrixChanged(matrix, previousA, previousB, previousC, previousD, previousE, previousF);
|
|
2055
2085
|
if (this.geometrySnapshot && this.worldMatrixAffectsLocalGeometry() && changed) this.invalidateCache(ChangeImpact.LocalGeometry);
|
|
2056
2086
|
else this.clearWorldProjection();
|
|
2057
2087
|
return changed;
|
|
@@ -2224,27 +2254,7 @@ var ElementTransform = class {
|
|
|
2224
2254
|
}
|
|
2225
2255
|
/** 将 local AABB 四角投影到指定 reference matrix。 */
|
|
2226
2256
|
toBounds(matrix, bounds) {
|
|
2227
|
-
|
|
2228
|
-
const left = bounds.left;
|
|
2229
|
-
const top = bounds.top;
|
|
2230
|
-
const right = left + bounds.width;
|
|
2231
|
-
const bottom = top + bounds.height;
|
|
2232
|
-
if (matrix.b === 0 && matrix.c === 0) {
|
|
2233
|
-
const firstX = matrix.a * left + matrix.e;
|
|
2234
|
-
const secondX = matrix.a * right + matrix.e;
|
|
2235
|
-
const firstY = matrix.d * top + matrix.f;
|
|
2236
|
-
const secondY = matrix.d * bottom + matrix.f;
|
|
2237
|
-
return new Bound(Math.min(firstX, secondX), Math.min(firstY, secondY), Math.max(firstX, secondX), Math.max(firstY, secondY));
|
|
2238
|
-
}
|
|
2239
|
-
const x0 = matrix.a * left + matrix.c * top + matrix.e;
|
|
2240
|
-
const x1 = matrix.a * right + matrix.c * top + matrix.e;
|
|
2241
|
-
const x2 = matrix.a * right + matrix.c * bottom + matrix.e;
|
|
2242
|
-
const x3 = matrix.a * left + matrix.c * bottom + matrix.e;
|
|
2243
|
-
const y0 = matrix.b * left + matrix.d * top + matrix.f;
|
|
2244
|
-
const y1 = matrix.b * right + matrix.d * top + matrix.f;
|
|
2245
|
-
const y2 = matrix.b * right + matrix.d * bottom + matrix.f;
|
|
2246
|
-
const y3 = matrix.b * left + matrix.d * bottom + matrix.f;
|
|
2247
|
-
return new Bound(Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3));
|
|
2257
|
+
return projectBounds(matrix, bounds);
|
|
2248
2258
|
}
|
|
2249
2259
|
/** 惰性构建当前快照对应的 local 绘制轮廓。 */
|
|
2250
2260
|
getVisualOutline() {
|
|
@@ -2484,7 +2494,7 @@ var ElementTransform = class {
|
|
|
2484
2494
|
const element = this.element;
|
|
2485
2495
|
const cacheable = !this.needsCommit && !this.hasDirtyDescendant && element.activeRoot?.frameRuntime.isStabilizingGeometry !== true;
|
|
2486
2496
|
if (cacheable && !isUndefined(this.currentRenderCoverageBounds)) return this.currentRenderCoverageBounds;
|
|
2487
|
-
const ownBounds = this.hasGeometry() ? this.
|
|
2497
|
+
const ownBounds = this.hasGeometry() ? this.getWorldVisualBoundsView() : null;
|
|
2488
2498
|
let descendantBounds = null;
|
|
2489
2499
|
for (const child of element.children) {
|
|
2490
2500
|
if (!child.visible) continue;
|
|
@@ -2506,7 +2516,7 @@ var ElementTransform = class {
|
|
|
2506
2516
|
descendantBounds.maxY = Math.min(descendantBounds.maxY, clipBounds.maxY);
|
|
2507
2517
|
if (descendantBounds.minX >= descendantBounds.maxX || descendantBounds.minY >= descendantBounds.maxY) descendantBounds = null;
|
|
2508
2518
|
}
|
|
2509
|
-
const bounds = ownBounds && descendantBounds ? ownBounds.merge(descendantBounds) : ownBounds ?? descendantBounds;
|
|
2519
|
+
const bounds = ownBounds && descendantBounds ? new Bound(ownBounds.minX, ownBounds.minY, ownBounds.maxX, ownBounds.maxY).merge(descendantBounds) : ownBounds ?? descendantBounds;
|
|
2510
2520
|
if (cacheable) this.currentRenderCoverageBounds = bounds;
|
|
2511
2521
|
return bounds;
|
|
2512
2522
|
}
|
|
@@ -3046,7 +3056,7 @@ var ElementProperties = class extends TransformProperties {
|
|
|
3046
3056
|
}
|
|
3047
3057
|
};
|
|
3048
3058
|
/** 可变换、可绘制、可命中并支持事件和动画的场景节点。 */
|
|
3049
|
-
var Element = class extends Node {
|
|
3059
|
+
var Element = class Element extends Node {
|
|
3050
3060
|
type = "element";
|
|
3051
3061
|
/** 当前 Element 唯一的变换与几何运行时。 */
|
|
3052
3062
|
transform;
|
|
@@ -3158,6 +3168,7 @@ var Element = class extends Node {
|
|
|
3158
3168
|
* @param value `false`、`true` 或 custom definitions。
|
|
3159
3169
|
*/
|
|
3160
3170
|
replacePorts(value) {
|
|
3171
|
+
if (this.ports === value && Element.getConfiguredPropertyValue(this, "ports") === value) return this;
|
|
3161
3172
|
this.commitConfiguredOptions({ ports: value });
|
|
3162
3173
|
return this;
|
|
3163
3174
|
}
|
|
@@ -3509,11 +3520,27 @@ var Element = class extends Node {
|
|
|
3509
3520
|
}
|
|
3510
3521
|
/** @internal 一次产出 custom Port position、法线与 label edge 长度。 */
|
|
3511
3522
|
getConnectionPortGeometry(edge, ratio) {
|
|
3512
|
-
const
|
|
3523
|
+
const position = this.getConnectionPortPosition(edge, ratio);
|
|
3524
|
+
let nx = 0;
|
|
3525
|
+
let ny = -1;
|
|
3526
|
+
switch (edge) {
|
|
3527
|
+
case "right":
|
|
3528
|
+
nx = 1;
|
|
3529
|
+
ny = 0;
|
|
3530
|
+
break;
|
|
3531
|
+
case "bottom":
|
|
3532
|
+
nx = 0;
|
|
3533
|
+
ny = 1;
|
|
3534
|
+
break;
|
|
3535
|
+
case "left":
|
|
3536
|
+
nx = -1;
|
|
3537
|
+
ny = 0;
|
|
3538
|
+
break;
|
|
3539
|
+
}
|
|
3513
3540
|
return {
|
|
3514
|
-
position
|
|
3515
|
-
nx
|
|
3516
|
-
ny
|
|
3541
|
+
position,
|
|
3542
|
+
nx,
|
|
3543
|
+
ny,
|
|
3517
3544
|
edgeLength: edge === "top" || edge === "bottom" ? Math.abs(this.width ?? 0) : Math.abs(this.height ?? 0)
|
|
3518
3545
|
};
|
|
3519
3546
|
}
|
|
@@ -3665,7 +3692,7 @@ var IDENTITY_REFERENCE_TRANSFORM = new DOMMatrix();
|
|
|
3665
3692
|
*/
|
|
3666
3693
|
function resolveSparseReferenceTranslation(baseline, intent, targetParentWorldMatrix) {
|
|
3667
3694
|
const delta = intent.worldDelta;
|
|
3668
|
-
if (delta
|
|
3695
|
+
if (!sameLinearMatrix(delta, IDENTITY_REFERENCE_TRANSFORM) || !sameLinearMatrix(baseline.startParentWorldMatrix, targetParentWorldMatrix)) return null;
|
|
3669
3696
|
const worldX = delta.e + baseline.startParentWorldMatrix.e - targetParentWorldMatrix.e;
|
|
3670
3697
|
const worldY = delta.f + baseline.startParentWorldMatrix.f - targetParentWorldMatrix.f;
|
|
3671
3698
|
const parentInverse = baseline.startParentWorldInverseMatrix;
|
|
@@ -3683,6 +3710,107 @@ function sameMatrix(left, right) {
|
|
|
3683
3710
|
function sameLinearMatrix(left, right) {
|
|
3684
3711
|
return left.a === right.a && left.b === right.b && left.c === right.c && left.d === right.d;
|
|
3685
3712
|
}
|
|
3713
|
+
function createFrameLinearMatrix(frame) {
|
|
3714
|
+
return new DOMMatrix().rotate(0, 0, frame.angle).skewX(frame.skewX).skewY(frame.skewY).scale(frame.scaleX, frame.scaleY);
|
|
3715
|
+
}
|
|
3716
|
+
function transformReferenceBounds(bounds, matrix) {
|
|
3717
|
+
const right = bounds.left + bounds.width;
|
|
3718
|
+
const bottom = bounds.top + bounds.height;
|
|
3719
|
+
const corners = [
|
|
3720
|
+
transformPoint(matrix, {
|
|
3721
|
+
x: bounds.left,
|
|
3722
|
+
y: bounds.top
|
|
3723
|
+
}),
|
|
3724
|
+
transformPoint(matrix, {
|
|
3725
|
+
x: right,
|
|
3726
|
+
y: bounds.top
|
|
3727
|
+
}),
|
|
3728
|
+
transformPoint(matrix, {
|
|
3729
|
+
x: right,
|
|
3730
|
+
y: bottom
|
|
3731
|
+
}),
|
|
3732
|
+
transformPoint(matrix, {
|
|
3733
|
+
x: bounds.left,
|
|
3734
|
+
y: bottom
|
|
3735
|
+
})
|
|
3736
|
+
];
|
|
3737
|
+
let minX = corners[0].x;
|
|
3738
|
+
let minY = corners[0].y;
|
|
3739
|
+
let maxX = corners[0].x;
|
|
3740
|
+
let maxY = corners[0].y;
|
|
3741
|
+
for (let index = 1; index < corners.length; index++) {
|
|
3742
|
+
const point = corners[index];
|
|
3743
|
+
minX = Math.min(minX, point.x);
|
|
3744
|
+
minY = Math.min(minY, point.y);
|
|
3745
|
+
maxX = Math.max(maxX, point.x);
|
|
3746
|
+
maxY = Math.max(maxY, point.y);
|
|
3747
|
+
}
|
|
3748
|
+
return {
|
|
3749
|
+
left: minX,
|
|
3750
|
+
top: minY,
|
|
3751
|
+
width: maxX - minX,
|
|
3752
|
+
height: maxY - minY
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
/**
|
|
3756
|
+
* Factor a target local affine transform through the existing authored frame.
|
|
3757
|
+
* The frame keeps its current pose; the complete linear delta is written into
|
|
3758
|
+
* the vector's authored points. Translation remains a frame left/top patch so
|
|
3759
|
+
* no synthetic point offset is introduced into the canonical geometry.
|
|
3760
|
+
*/
|
|
3761
|
+
function resolveBakedAuthoredGeometryTransform(baseline, target) {
|
|
3762
|
+
const frameLinear = createFrameLinearMatrix(baseline.frame);
|
|
3763
|
+
const determinant = frameLinear.a * frameLinear.d - frameLinear.b * frameLinear.c;
|
|
3764
|
+
if (!Number.isFinite(determinant) || determinant === 0) return null;
|
|
3765
|
+
const targetLocal = target.parentInverseMatrix.multiply(target.worldMatrix);
|
|
3766
|
+
const targetLinear = new DOMMatrix([
|
|
3767
|
+
targetLocal.a,
|
|
3768
|
+
targetLocal.b,
|
|
3769
|
+
targetLocal.c,
|
|
3770
|
+
targetLocal.d,
|
|
3771
|
+
0,
|
|
3772
|
+
0
|
|
3773
|
+
]);
|
|
3774
|
+
const authoredLocalDelta = frameLinear.inverse().multiply(targetLinear);
|
|
3775
|
+
const nextBounds = transformReferenceBounds(baseline.referenceBounds, authoredLocalDelta);
|
|
3776
|
+
const offsetX = nextBounds.left + (resolveOrigin(baseline.frame.originX) + .5) * nextBounds.width;
|
|
3777
|
+
const offsetY = nextBounds.top + (resolveOrigin(baseline.frame.originY) + .5) * nextBounds.height;
|
|
3778
|
+
return {
|
|
3779
|
+
framePatch: {
|
|
3780
|
+
left: targetLocal.e - offsetX + frameLinear.a * offsetX + frameLinear.c * offsetY,
|
|
3781
|
+
top: targetLocal.f - offsetY + frameLinear.b * offsetX + frameLinear.d * offsetY,
|
|
3782
|
+
angle: baseline.frame.angle,
|
|
3783
|
+
scaleX: baseline.frame.scaleX,
|
|
3784
|
+
scaleY: baseline.frame.scaleY,
|
|
3785
|
+
skewX: baseline.frame.skewX,
|
|
3786
|
+
skewY: baseline.frame.skewY
|
|
3787
|
+
},
|
|
3788
|
+
authoredLocalDelta
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* 用 owner 已算出的精确 authored bounds 修正 bake frame 的位置。
|
|
3793
|
+
* AABB 经过旋转/错切后不能由原 bounds 四角精确推出;owner 在生成
|
|
3794
|
+
* 新几何后把真实 bounds 交回这里,保持 frame world matrix 与目标一致。
|
|
3795
|
+
*/
|
|
3796
|
+
function resolveAuthoredGeometryFramePatch(baseline, resolution, authoredBounds) {
|
|
3797
|
+
if (isIdentityMatrix(resolution.authoredLocalDelta)) return resolution.framePatch;
|
|
3798
|
+
const approximateBounds = transformReferenceBounds(baseline.referenceBounds, resolution.authoredLocalDelta);
|
|
3799
|
+
const originX = resolveOrigin(baseline.frame.originX) + .5;
|
|
3800
|
+
const originY = resolveOrigin(baseline.frame.originY) + .5;
|
|
3801
|
+
const approximateOffsetX = approximateBounds.left + originX * approximateBounds.width;
|
|
3802
|
+
const approximateOffsetY = approximateBounds.top + originY * approximateBounds.height;
|
|
3803
|
+
const authoredOffsetX = authoredBounds.left + originX * authoredBounds.width;
|
|
3804
|
+
const authoredOffsetY = authoredBounds.top + originY * authoredBounds.height;
|
|
3805
|
+
const deltaX = authoredOffsetX - approximateOffsetX;
|
|
3806
|
+
const deltaY = authoredOffsetY - approximateOffsetY;
|
|
3807
|
+
const frameLinear = createFrameLinearMatrix(baseline.frame);
|
|
3808
|
+
return {
|
|
3809
|
+
...resolution.framePatch,
|
|
3810
|
+
left: (resolution.framePatch.left ?? baseline.frame.left) + -deltaX + frameLinear.a * deltaX + frameLinear.c * deltaY,
|
|
3811
|
+
top: (resolution.framePatch.top ?? baseline.frame.top) + -deltaY + frameLinear.b * deltaX + frameLinear.d * deltaY
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3686
3814
|
/** 判断变换意图是否包含跨父级重挂载所需的目标父级矩阵。 */
|
|
3687
3815
|
function isReparentTransformIntent(intent) {
|
|
3688
3816
|
return "targetParentWorldMatrix" in intent;
|
|
@@ -3694,10 +3822,14 @@ function resolveTargetParentWorldMatrix(baseline, intent) {
|
|
|
3694
3822
|
function targetsFrozenReference(baseline, intent) {
|
|
3695
3823
|
const delta = intent.worldDelta;
|
|
3696
3824
|
const targetParentWorldMatrix = resolveTargetParentWorldMatrix(baseline, intent);
|
|
3697
|
-
return delta
|
|
3825
|
+
return isIdentityMatrix(delta) && sameMatrix(baseline.startParentWorldMatrix, targetParentWorldMatrix);
|
|
3698
3826
|
}
|
|
3699
3827
|
/** 从已持有的最终 world pose 纯计算 frame/authored delta。 */
|
|
3700
|
-
function resolveReferenceTransformTargetWithMode(baseline, target, preserveOwnedGeometry, unwrappedRotationDelta) {
|
|
3828
|
+
function resolveReferenceTransformTargetWithMode(baseline, target, preserveOwnedGeometry, unwrappedRotationDelta, bakeGeometry = false) {
|
|
3829
|
+
if (bakeGeometry && baseline.writeMode === "authored-geometry" && !preserveOwnedGeometry) {
|
|
3830
|
+
const baked = resolveBakedAuthoredGeometryTransform(baseline, target);
|
|
3831
|
+
if (baked) return baked;
|
|
3832
|
+
}
|
|
3701
3833
|
const targetDecomposition = qrDecompose(target.parentInverseMatrix.multiply(target.worldMatrix));
|
|
3702
3834
|
const targetAngle = unwrappedRotationDelta === void 0 ? targetDecomposition.angle : targetDecomposition.angle + 360 * Math.round((baseline.frame.angle + unwrappedRotationDelta - targetDecomposition.angle) / 360);
|
|
3703
3835
|
const authoredScaleX = preserveOwnedGeometry ? 1 : baseline.frame.scaleX === 0 ? 1 : Math.abs(targetDecomposition.scaleX / baseline.frame.scaleX);
|
|
@@ -3792,7 +3924,7 @@ function resolveReferenceTransform(baseline, intent) {
|
|
|
3792
3924
|
worldMatrix: intent.worldDelta.multiply(baseline.startWorldMatrix),
|
|
3793
3925
|
worldCenter: transformPoint(intent.worldDelta, startWorldCenter),
|
|
3794
3926
|
parentInverseMatrix: sameMatrix(baseline.startParentWorldMatrix, targetParentWorldMatrix) ? baseline.startParentWorldInverseMatrix : targetParentWorldMatrix.inverse()
|
|
3795
|
-
}, preserveOwnedGeometry, intent.unwrappedRotationDelta);
|
|
3927
|
+
}, preserveOwnedGeometry, intent.unwrappedRotationDelta, intent.bakeGeometry);
|
|
3796
3928
|
}
|
|
3797
3929
|
//#endregion
|
|
3798
3930
|
//#region packages/core/src/node/shape.ts
|
|
@@ -4143,20 +4275,24 @@ var Shape = class extends Element {
|
|
|
4143
4275
|
//#region packages/core/src/root/hit-test.ts
|
|
4144
4276
|
/** Fixed screen-space broad-phase aperture for scene point affordances. */
|
|
4145
4277
|
var POINTER_QUERY_RADIUS = 8;
|
|
4146
|
-
/**
|
|
4147
|
-
function
|
|
4278
|
+
/** 合并 pointer ancestor 状态与 child clip 的单次祖先遍历。 */
|
|
4279
|
+
function isPointerTargetWithinAncestorClips(root, element, point) {
|
|
4280
|
+
if (element.pickable === false || !isActiveVisibleNode(root, element) || element.silent) return false;
|
|
4148
4281
|
let ancestor = element.parent instanceof Element ? element.parent : void 0;
|
|
4149
4282
|
while (ancestor) {
|
|
4283
|
+
if (!isActiveVisibleNode(root, ancestor) || ancestor.silent) return false;
|
|
4150
4284
|
if (ancestor.getChildClipRect()) {
|
|
4151
4285
|
const localPoint = ancestor.transform.toLocal(point);
|
|
4152
4286
|
if (!localPoint || !ancestor.containsChildClipPoint(localPoint)) return false;
|
|
4153
4287
|
}
|
|
4288
|
+
if (ancestor.isLayerNode()) break;
|
|
4154
4289
|
ancestor = ancestor.parent instanceof Element ? ancestor.parent : void 0;
|
|
4155
4290
|
}
|
|
4156
4291
|
return true;
|
|
4157
4292
|
}
|
|
4158
4293
|
/** 判断查询区域与元素 bounds 的交集是否仍通过 ancestor clips。 */
|
|
4159
|
-
function hasAreaIntersection(element, area, boundsKind) {
|
|
4294
|
+
function hasAreaIntersection(root, element, area, boundsKind) {
|
|
4295
|
+
if (!isActiveVisibleNode(root, element)) return false;
|
|
4160
4296
|
const bounds = boundsKind === "visual" ? element.transform.getWorldVisualBoundsView() : element.transform.getHitTestBounds();
|
|
4161
4297
|
if (!Intersection.isAABBIntersect(area, bounds)) return false;
|
|
4162
4298
|
const left = Math.max(area.left, bounds.minX);
|
|
@@ -4183,6 +4319,7 @@ function hasAreaIntersection(element, area, boundsKind) {
|
|
|
4183
4319
|
];
|
|
4184
4320
|
let ancestor = element.parent instanceof Element ? element.parent : void 0;
|
|
4185
4321
|
while (ancestor) {
|
|
4322
|
+
if (!isActiveVisibleNode(root, ancestor)) return false;
|
|
4186
4323
|
const clip = ancestor.getChildClipRect();
|
|
4187
4324
|
if (clip) {
|
|
4188
4325
|
const localIntersection = [];
|
|
@@ -4195,6 +4332,7 @@ function hasAreaIntersection(element, area, boundsKind) {
|
|
|
4195
4332
|
if (clipped.length === 0) return false;
|
|
4196
4333
|
intersection = clipped.map((point) => ancestor.transform.toWorld(point));
|
|
4197
4334
|
}
|
|
4335
|
+
if (ancestor.isLayerNode()) break;
|
|
4198
4336
|
ancestor = ancestor.parent instanceof Element ? ancestor.parent : void 0;
|
|
4199
4337
|
}
|
|
4200
4338
|
return true;
|
|
@@ -4202,16 +4340,6 @@ function hasAreaIntersection(element, area, boundsKind) {
|
|
|
4202
4340
|
function isActiveVisibleNode(root, element) {
|
|
4203
4341
|
return element.isActive && !element.isUnmounted && element.activeRoot === root && element.visible;
|
|
4204
4342
|
}
|
|
4205
|
-
/** 判断元素及其祖先是否参与当前 Root 的区域选择。 */
|
|
4206
|
-
function isAreaTarget(root, element) {
|
|
4207
|
-
let node = element;
|
|
4208
|
-
while (node) {
|
|
4209
|
-
if (!isActiveVisibleNode(root, node)) return false;
|
|
4210
|
-
if (node.isLayerNode()) break;
|
|
4211
|
-
node = node.parent instanceof Element ? node.parent : void 0;
|
|
4212
|
-
}
|
|
4213
|
-
return true;
|
|
4214
|
-
}
|
|
4215
4343
|
/** 判断元素是否符合当前 Root 的 pointer target 语义。@internal */
|
|
4216
4344
|
function isPointerTarget(root, element) {
|
|
4217
4345
|
if (element.pickable === false) return false;
|
|
@@ -4248,8 +4376,7 @@ function* iteratePointElements(root, point, options = {}) {
|
|
|
4248
4376
|
if (!element.isActive || element.isUnmounted || element.activeRoot !== root || element.layer !== layer || root.getPaintOrder(element) === void 0) continue;
|
|
4249
4377
|
if (options.exclude?.has(element.key)) continue;
|
|
4250
4378
|
if (scope && !isStrictDescendant(element, scope)) continue;
|
|
4251
|
-
if (!
|
|
4252
|
-
if (!isPointWithinAncestorClips(element, point)) continue;
|
|
4379
|
+
if (!isPointerTargetWithinAncestorClips(root, element, point)) continue;
|
|
4253
4380
|
if (element.transform.hasPointHint(point)) yield element;
|
|
4254
4381
|
}
|
|
4255
4382
|
}
|
|
@@ -4338,7 +4465,7 @@ function queryAreaElements(root, area, options = {}) {
|
|
|
4338
4465
|
for (let index = root.layers.length - 1; index >= 0; index--) {
|
|
4339
4466
|
const hitElements = root.layers[index].queryCommittedElements(area, order);
|
|
4340
4467
|
for (const element of hitElements) {
|
|
4341
|
-
if (!
|
|
4468
|
+
if (!isSelectable(element) || !hasAreaIntersection(root, element, area, bounds)) continue;
|
|
4342
4469
|
result.push(element);
|
|
4343
4470
|
}
|
|
4344
4471
|
}
|
|
@@ -6805,4 +6932,4 @@ var Layer = class Layer extends Element {
|
|
|
6805
6932
|
}
|
|
6806
6933
|
};
|
|
6807
6934
|
//#endregion
|
|
6808
|
-
export { ChangeImpact, CustomEvent, DEFAULT_CONNECTION_PORT_SCHEMA, EVENT_NAMES, Element, ElementProperties, ElementTransform, EventEmitter, FrameStabilizationError, Layer, LayerProperties, LayerTransform, Node, NodeLifecycleEvent, NodeProperties, Root, RootFrameRuntime, RootInputController, SKIN_DEFAULTS, SceneRegistry, Shape, ShapeProperties, ShapeTransform, Skin, TransformProperties, Viewport, createAuthoredLineSubpath, createGeometryQuery, createGeometrySnapshot, createLineSubpath, createPathLocalId, evaluatePathSegmentPoint, getElementConnectionPort, getElementConnectionPortPoint, getElementConnectionPortPoints, isFormControlTarget, isReparentTransformIntent, projectConnectionPortPoint, resolveAuthoredSubpath, resolveOrigin, resolveReferenceTransform, resolveReferenceTransformTarget, splitPathSegment };
|
|
6935
|
+
export { ChangeImpact, CustomEvent, DEFAULT_CONNECTION_PORT_SCHEMA, EVENT_NAMES, Element, ElementProperties, ElementTransform, EventEmitter, FrameStabilizationError, Layer, LayerProperties, LayerTransform, Node, NodeLifecycleEvent, NodeProperties, Root, RootFrameRuntime, RootInputController, SKIN_DEFAULTS, SceneRegistry, Shape, ShapeProperties, ShapeTransform, Skin, TransformProperties, Viewport, createAuthoredLineSubpath, createGeometryQuery, createGeometrySnapshot, createLineSubpath, createPathLocalId, evaluatePathSegmentPoint, getElementConnectionPort, getElementConnectionPortPoint, getElementConnectionPortPoints, isFormControlTarget, isReparentTransformIntent, projectConnectionPortPoint, resolveAuthoredGeometryFramePatch, resolveAuthoredSubpath, resolveOrigin, resolveReferenceTransform, resolveReferenceTransformTarget, splitPathSegment };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Bound, type Rect } from "@fulate/share";
|
|
2
|
+
/** Project a local rectangle into a matrix space and return its AABB. */
|
|
3
|
+
export declare function projectBounds(matrix: DOMMatrixReadOnly, bounds: Readonly<Rect> | null): Bound;
|
|
4
|
+
/** Compare a committed matrix with a previous scalar matrix sample. */
|
|
5
|
+
export declare function matrixChanged(matrix: DOMMatrixReadOnly, previousA: number, previousB: number, previousC: number, previousD: number, previousE: number, previousF: number): boolean;
|
|
@@ -7,10 +7,11 @@ import { ChangeImpact } from "./properties";
|
|
|
7
7
|
export type GeometryBoundsView = Readonly<Rect & BoundingBox>;
|
|
8
8
|
/** 一次 Layer damage 收集共享的子树 visual bounds memo。 @internal */
|
|
9
9
|
export type DirtyBoundsMemo = Map<ElementTransform, Bound | null>;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Element 继承构造链内部使用的 companion constructor。默认参数保留
|
|
12
|
+
* existential 入口;具体 Element 子类可以同时声明 owner 与 Transform。
|
|
13
|
+
*/
|
|
14
|
+
export type ElementTransformClass<E extends Element = Element, T extends ElementTransform<any> = ElementTransform<any>> = new (element: E) => T;
|
|
14
15
|
/**
|
|
15
16
|
* 一个 Element 唯一的变换、几何、damage 与子树派生状态 owner。
|
|
16
17
|
*
|
|
@@ -34,6 +34,11 @@ export interface TransformIntent {
|
|
|
34
34
|
readonly worldDelta: DOMMatrixReadOnly;
|
|
35
35
|
/** 连续旋转手势从 frozen baseline 累积的未折叠 world 角度。 */
|
|
36
36
|
readonly unwrappedRotationDelta?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Resize/flip 这类矢量编辑操作把线性变换写回 authored geometry,
|
|
39
|
+
* 而不是把它留在容器的 scale 上。普通 move/rotate 保持原有 pose owner。
|
|
40
|
+
*/
|
|
41
|
+
readonly bakeGeometry?: boolean;
|
|
37
42
|
}
|
|
38
43
|
/** 只有显式 reparent 才携带的新 parent world reference。 */
|
|
39
44
|
export interface ReparentTransformIntent extends TransformIntent {
|
|
@@ -50,6 +55,12 @@ export interface ReferenceTransformResolution {
|
|
|
50
55
|
readonly framePatch: Readonly<ElementOptionPatch>;
|
|
51
56
|
readonly authoredLocalDelta: DOMMatrixReadOnly;
|
|
52
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* 用 owner 已算出的精确 authored bounds 修正 bake frame 的位置。
|
|
60
|
+
* AABB 经过旋转/错切后不能由原 bounds 四角精确推出;owner 在生成
|
|
61
|
+
* 新几何后把真实 bounds 交回这里,保持 frame world matrix 与目标一致。
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveAuthoredGeometryFramePatch(baseline: Readonly<Pick<ReferenceTransformBaseline, "referenceBounds" | "frame">>, resolution: Readonly<ReferenceTransformResolution>, authoredBounds: Readonly<Rect>): Readonly<ElementOptionPatch>;
|
|
53
64
|
/** 判断变换意图是否包含跨父级重挂载所需的目标父级矩阵。 */
|
|
54
65
|
export declare function isReparentTransformIntent(intent: Readonly<TransformIntent | ReparentTransformIntent>): intent is Readonly<ReparentTransformIntent>;
|
|
55
66
|
/**
|