@configura/babylon-view 1.3.0-alpha.2 → 1.3.0-alpha.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/animation/coordinator/CoordinatorDropAndSpin.js +1 -1
- package/dist/animation/coordinator/CoordinatorPulseBounce.js +1 -1
- package/dist/animation/coordinator/CoordinatorPulseHighlight.js +1 -1
- package/dist/animation/coordinator/CoordinatorPulseInflate.js +1 -1
- package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +16 -0
- package/dist/camera/CfgArcRotateCameraPointersInput.js +17 -15
- package/dist/geometry/CfgGeometry.d.ts +22 -5
- package/dist/geometry/CfgGeometry.js +131 -102
- package/dist/geometry/CfgMesh.d.ts +4 -1
- package/dist/geometry/CfgMesh.js +32 -2
- package/dist/geometry/stretch/CfgMorphTarget.d.ts +16 -0
- package/dist/geometry/stretch/CfgMorphTarget.js +65 -0
- package/dist/geometry/stretch/CfgStretchData.d.ts +115 -0
- package/dist/geometry/stretch/CfgStretchData.js +340 -0
- package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +17 -0
- package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -0
- package/dist/material/CfgMaterial.d.ts +19 -4
- package/dist/material/CfgMaterial.js +38 -30
- package/dist/material/material.js +3 -3
- package/dist/material/texture.js +10 -8
- package/dist/nodes/CfgDeferredMeshNode.d.ts +8 -1
- package/dist/nodes/CfgDeferredMeshNode.js +48 -18
- package/dist/nodes/CfgProductNode.d.ts +68 -3
- package/dist/nodes/CfgProductNode.js +130 -38
- package/dist/nodes/CfgSymNode.d.ts +14 -6
- package/dist/nodes/CfgSymNode.js +50 -17
- package/dist/nodes/CfgSymRootNode.d.ts +18 -6
- package/dist/nodes/CfgSymRootNode.js +62 -17
- package/dist/nodes/CfgTransformNode.d.ts +4 -0
- package/dist/nodes/CfgTransformNode.js +4 -2
- package/dist/utilities/CfgBoundingBox.d.ts +5 -0
- package/dist/utilities/CfgBoundingBox.js +18 -1
- package/dist/utilities/anchor/anchor.d.ts +52 -0
- package/dist/utilities/anchor/anchor.js +136 -0
- package/dist/utilities/anchor/anchorMap.d.ts +21 -0
- package/dist/utilities/anchor/anchorMap.js +111 -0
- package/dist/utilities/utilities3D.d.ts +44 -0
- package/dist/utilities/utilities3D.js +97 -19
- package/dist/utilities/utilitiesSymRootIdentifier.d.ts +3 -1
- package/dist/utilities/utilitiesSymRootIdentifier.js +10 -4
- package/dist/view/BaseView.d.ts +9 -1
- package/dist/view/BaseView.js +16 -10
- package/dist/view/RenderEnv.d.ts +6 -1
- package/dist/view/SingleProductDefaultCameraView.js +1 -1
- package/dist/view/SingleProductView.d.ts +8 -1
- package/dist/view/SingleProductView.js +1 -0
- package/package.json +5 -5
|
@@ -14,9 +14,13 @@ import { SymGfxMode } from "@configura/web-core/dist/cm/format/cmsym/components/
|
|
|
14
14
|
import { loadSymFile, makeSymFromDex, } from "@configura/web-core/dist/cm/format/cmsym/SymNode.js";
|
|
15
15
|
import { DetailLevel } from "@configura/web-core/dist/cm/geometry/DetailMask.js";
|
|
16
16
|
import { readFileToArrayBuffer } from "@configura/web-utilities";
|
|
17
|
+
import { CfgStretchData } from "../geometry/stretch/CfgStretchData.js";
|
|
18
|
+
import { CfgAnchorRef, updatedStretchedAnchorPointMatrix, } from "../utilities/anchor/anchor.js";
|
|
17
19
|
import { modelTransformToSymTransform, symTransformToMatrix } from "../utilities/utilities3D.js";
|
|
18
|
-
import {
|
|
20
|
+
import { isSameIdentifierTransformAndAnchor, makeIdentifier, makeIdentifierFromRootNodeSource, } from "../utilities/utilitiesSymRootIdentifier.js";
|
|
19
21
|
import { CfgSymNode } from "./CfgSymNode.js";
|
|
22
|
+
// It is currently assumed that all meshes are in meters
|
|
23
|
+
export const MESH_UNIT = "m";
|
|
20
24
|
export function isSymRootNode(value) {
|
|
21
25
|
return value instanceof CfgSymRootNode;
|
|
22
26
|
}
|
|
@@ -46,23 +50,24 @@ function getBestMatchingDetailLevel(logger, symNode, allowedLevels) {
|
|
|
46
50
|
return detailLevel || DetailLevel.undefined;
|
|
47
51
|
}
|
|
48
52
|
export class CfgSymRootNode extends CfgSymNode {
|
|
49
|
-
constructor(renderEnvironment, _isForDebug, detailLevel, symNode, _identifier, modelTransform) {
|
|
50
|
-
super(renderEnvironment, detailLevel, symNode);
|
|
53
|
+
constructor(renderEnvironment, cfgProductNodeParent, _isForDebug, detailLevel, symNode, _identifier, modelTransform, anchorRef) {
|
|
54
|
+
super(renderEnvironment, cfgProductNodeParent, undefined, detailLevel, symNode);
|
|
51
55
|
this._isForDebug = _isForDebug;
|
|
52
56
|
this._identifier = _identifier;
|
|
57
|
+
this.anchorRef = anchorRef;
|
|
53
58
|
this._destroyed = false;
|
|
54
59
|
this.name = "(SymRoot) " + symNode.id;
|
|
55
60
|
this.modelTransform = modelTransform;
|
|
56
61
|
this.initTransform();
|
|
57
62
|
}
|
|
58
|
-
static makeCfgSymRootFromRootNodeSource(logger, isForDebug, renderEnvironment, rootNodeSource) {
|
|
63
|
+
static makeCfgSymRootFromRootNodeSource(logger, isForDebug, renderEnvironment, cfgProductNodeParent, rootNodeSource) {
|
|
59
64
|
return __awaiter(this, void 0, void 0, function* () {
|
|
60
65
|
return isModel(rootNodeSource)
|
|
61
|
-
?
|
|
62
|
-
:
|
|
66
|
+
? this.makeCfgSymRootFromUrl(logger, isForDebug, renderEnvironment, cfgProductNodeParent, rootNodeSource.uri, rootNodeSource.t, CfgAnchorRef.make(rootNodeSource.anchor))
|
|
67
|
+
: this.makeCfgSymRootFromFile(logger, isForDebug, renderEnvironment, cfgProductNodeParent, rootNodeSource);
|
|
63
68
|
});
|
|
64
69
|
}
|
|
65
|
-
static makeCfgSymRootFromUrl(logger, isForDebug, renderEnvironment, symUrl, transform) {
|
|
70
|
+
static makeCfgSymRootFromUrl(logger, isForDebug, renderEnvironment, cfgProductNodeParent, symUrl, transform, anchorRef) {
|
|
66
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
67
72
|
if (!/.cmsym$/i.test(symUrl)) {
|
|
68
73
|
renderEnvironment.notifyError(logger.errorAsObject("Unsupported model URL (not cmsym format): ", symUrl));
|
|
@@ -70,7 +75,7 @@ export class CfgSymRootNode extends CfgSymNode {
|
|
|
70
75
|
}
|
|
71
76
|
try {
|
|
72
77
|
const symNode = yield loadCachedSymNode(logger, symUrl, renderEnvironment);
|
|
73
|
-
return this.makeCfgSymRootFromSymNode(logger, renderEnvironment, isForDebug, makeIdentifier("uri", symUrl), transform, symNode);
|
|
78
|
+
return this.makeCfgSymRootFromSymNode(logger, renderEnvironment, cfgProductNodeParent, isForDebug, makeIdentifier("uri", symUrl), transform, anchorRef, symNode);
|
|
74
79
|
}
|
|
75
80
|
catch (e) {
|
|
76
81
|
logger.errorFromCaught(e);
|
|
@@ -78,34 +83,34 @@ export class CfgSymRootNode extends CfgSymNode {
|
|
|
78
83
|
}
|
|
79
84
|
});
|
|
80
85
|
}
|
|
81
|
-
static makeCfgSymRootFromFile(logger, isForDebug, renderEnvironment, file) {
|
|
86
|
+
static makeCfgSymRootFromFile(logger, isForDebug, renderEnvironment, cfgProductNodeParent, file) {
|
|
82
87
|
return __awaiter(this, void 0, void 0, function* () {
|
|
83
88
|
const arrayBuffer = yield readFileToArrayBuffer(file);
|
|
84
89
|
const dexObj = renderEnvironment.dexManager.arrayBufferToDexObj(logger, "", arrayBuffer.buffer);
|
|
85
90
|
const symNode = makeSymFromDex(logger, dexObj);
|
|
86
|
-
return this.makeCfgSymRootFromSymNode(logger, renderEnvironment, isForDebug, makeIdentifier("file", file.name), undefined, symNode);
|
|
91
|
+
return this.makeCfgSymRootFromSymNode(logger, renderEnvironment, cfgProductNodeParent, isForDebug, makeIdentifier("file", file.name), undefined, undefined, symNode);
|
|
87
92
|
});
|
|
88
93
|
}
|
|
89
94
|
get cfgClassName() {
|
|
90
95
|
return "CfgSymRootNode";
|
|
91
96
|
}
|
|
92
|
-
static makeCfgSymRootFromSymNode(logger, renderEnvironment, isForDebug, identifier, transform, symNode) {
|
|
97
|
+
static makeCfgSymRootFromSymNode(logger, renderEnvironment, cfgProductNodeParent, isForDebug, identifier, transform, anchorRef, symNode) {
|
|
93
98
|
return __awaiter(this, void 0, void 0, function* () {
|
|
94
99
|
if (symNode === undefined) {
|
|
95
100
|
logger.warn("No symNode");
|
|
96
101
|
return;
|
|
97
102
|
}
|
|
98
103
|
const detailLevel = getBestMatchingDetailLevel(logger, symNode, renderEnvironment.allowedDetailLevels);
|
|
99
|
-
const node = new
|
|
100
|
-
yield
|
|
104
|
+
const node = new this(renderEnvironment, cfgProductNodeParent, isForDebug, detailLevel, symNode, identifier, transform, anchorRef);
|
|
105
|
+
yield this.initCfgSymNode(node);
|
|
101
106
|
return node;
|
|
102
107
|
});
|
|
103
108
|
}
|
|
104
109
|
get isForDebug() {
|
|
105
110
|
return this._isForDebug;
|
|
106
111
|
}
|
|
107
|
-
|
|
108
|
-
return
|
|
112
|
+
isSameIdentifierTransformAndAnchor(rootNodeSource) {
|
|
113
|
+
return isSameIdentifierTransformAndAnchor(this._identifier, this.modelTransform, this.anchorRef, makeIdentifierFromRootNodeSource(rootNodeSource), isModel(rootNodeSource) ? rootNodeSource.t : undefined, isModel(rootNodeSource) ? CfgAnchorRef.make(rootNodeSource.anchor) : undefined);
|
|
109
114
|
}
|
|
110
115
|
destroy() {
|
|
111
116
|
this._destroyed = true;
|
|
@@ -145,17 +150,57 @@ export class CfgSymRootNode extends CfgSymNode {
|
|
|
145
150
|
}
|
|
146
151
|
get originalMatrix() {
|
|
147
152
|
if (this._originalMatrixWithModelTransform === undefined) {
|
|
148
|
-
|
|
153
|
+
let originalMatrix = super.originalMatrix;
|
|
149
154
|
let modelMatrix = Matrix.Identity();
|
|
150
155
|
const modelTransform = this.modelTransform;
|
|
151
156
|
if (modelTransform !== undefined) {
|
|
152
157
|
const modelSymTransform = modelTransformToSymTransform(modelTransform);
|
|
153
158
|
modelMatrix = symTransformToMatrix(modelSymTransform.transform());
|
|
154
159
|
}
|
|
155
|
-
|
|
160
|
+
originalMatrix = originalMatrix.multiply(modelMatrix);
|
|
161
|
+
const stretchedAnchorPointMatrix = this._stretchedAnchorPointMatrix;
|
|
162
|
+
if (stretchedAnchorPointMatrix !== undefined) {
|
|
163
|
+
originalMatrix = originalMatrix.multiply(stretchedAnchorPointMatrix);
|
|
164
|
+
}
|
|
165
|
+
this._originalMatrixWithModelTransform = originalMatrix;
|
|
156
166
|
}
|
|
157
167
|
return this._originalMatrixWithModelTransform;
|
|
158
168
|
}
|
|
169
|
+
get stretchDatas() {
|
|
170
|
+
if (this._stretchDatas === undefined) {
|
|
171
|
+
const logger = this.logger;
|
|
172
|
+
const detailLevel = this._detailLevel;
|
|
173
|
+
const stretchDatas = [];
|
|
174
|
+
for (const childNode of this._symNode.children(logger, true, true).values()) {
|
|
175
|
+
if (!childNode.isStretch(logger, detailLevel)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const stretchData = CfgStretchData.make(logger, childNode, detailLevel);
|
|
179
|
+
if (stretchData === undefined) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
stretchDatas.push(stretchData);
|
|
183
|
+
}
|
|
184
|
+
this._stretchDatas = stretchDatas;
|
|
185
|
+
}
|
|
186
|
+
return this._stretchDatas;
|
|
187
|
+
}
|
|
188
|
+
get accumulatedStretchDatas() {
|
|
189
|
+
return this.stretchDatas;
|
|
190
|
+
}
|
|
191
|
+
setAnchorTarget(anchorTarget) {
|
|
192
|
+
this._anchorTarget = anchorTarget;
|
|
193
|
+
}
|
|
194
|
+
refreshStretch() {
|
|
195
|
+
super.refreshStretch();
|
|
196
|
+
const updated = updatedStretchedAnchorPointMatrix(this.anchorRef, this._anchorTarget, this._stretchedAnchorPointMatrix, false);
|
|
197
|
+
if (updated === undefined) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this._stretchedAnchorPointMatrix = updated;
|
|
201
|
+
this._originalMatrixWithModelTransform = undefined; // Reset the matrix
|
|
202
|
+
this.initTransform();
|
|
203
|
+
}
|
|
159
204
|
addInspectorProperties() {
|
|
160
205
|
super.addInspectorProperties();
|
|
161
206
|
this.addInspectableCustomProperty({
|
|
@@ -15,6 +15,10 @@ export declare abstract class CfgTransformNode extends TransformNode {
|
|
|
15
15
|
clear(dispose: boolean): void;
|
|
16
16
|
add(...objects: Node[]): void;
|
|
17
17
|
abstract get originalMatrix(): Matrix;
|
|
18
|
+
/**
|
|
19
|
+
* Recursively calculates the world matrix based on originalMatrix on nodes which inherits
|
|
20
|
+
* CfgTransformNode.
|
|
21
|
+
*/
|
|
18
22
|
get worldOriginalMatrix(): Matrix;
|
|
19
23
|
remove(dispose: boolean, ...objects: Node[]): this;
|
|
20
24
|
/**
|
|
@@ -37,8 +37,10 @@ export class CfgTransformNode extends TransformNode {
|
|
|
37
37
|
obj.parent = this;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Recursively calculates the world matrix based on originalMatrix on nodes which inherits
|
|
42
|
+
* CfgTransformNode.
|
|
43
|
+
*/
|
|
42
44
|
get worldOriginalMatrix() {
|
|
43
45
|
const o = this.originalMatrix;
|
|
44
46
|
const p = this.parent;
|
|
@@ -3,6 +3,9 @@ export declare class CfgBoundingBox {
|
|
|
3
3
|
minimum: Vector3;
|
|
4
4
|
maximum: Vector3;
|
|
5
5
|
constructor(minimum?: Vector3, maximum?: Vector3);
|
|
6
|
+
/**
|
|
7
|
+
* Means that the bounding box should be seen as "undefined", which is not the same as "zero"
|
|
8
|
+
*/
|
|
6
9
|
private _isEmpty;
|
|
7
10
|
reConstruct(minimum: Vector3, maximum: Vector3): CfgBoundingBox;
|
|
8
11
|
copyFrom(otherBoundingBox: CfgBoundingBox): CfgBoundingBox;
|
|
@@ -10,8 +13,10 @@ export declare class CfgBoundingBox {
|
|
|
10
13
|
get center(): Vector3;
|
|
11
14
|
translate(vec: Vector3): CfgBoundingBox;
|
|
12
15
|
expand(otherBoundingBox: CfgBoundingBox): CfgBoundingBox;
|
|
16
|
+
expandWithPoint(point: Vector3): CfgBoundingBox;
|
|
13
17
|
applyMatrix(matrix: Matrix): CfgBoundingBox;
|
|
14
18
|
get spaceDiagonal(): number;
|
|
15
19
|
get isEmpty(): boolean;
|
|
20
|
+
get corners(): Vector3[];
|
|
16
21
|
}
|
|
17
22
|
//# sourceMappingURL=CfgBoundingBox.d.ts.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
|
|
2
2
|
export class CfgBoundingBox {
|
|
3
3
|
constructor(minimum, maximum) {
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Means that the bounding box should be seen as "undefined", which is not the same as "zero"
|
|
6
|
+
*/
|
|
5
7
|
this._isEmpty = true;
|
|
6
8
|
this.minimum = (minimum === null || minimum === void 0 ? void 0 : minimum.clone()) || Vector3.Zero();
|
|
7
9
|
this.maximum = (maximum === null || maximum === void 0 ? void 0 : maximum.clone()) || Vector3.Zero();
|
|
@@ -40,6 +42,14 @@ export class CfgBoundingBox {
|
|
|
40
42
|
this.reConstruct(this.minimum.minimizeInPlace(otherBoundingBox.minimum), this.maximum.maximizeInPlace(otherBoundingBox.maximum));
|
|
41
43
|
return this;
|
|
42
44
|
}
|
|
45
|
+
expandWithPoint(point) {
|
|
46
|
+
if (this.isEmpty) {
|
|
47
|
+
this.reConstruct(point, point);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
this.reConstruct(this.minimum.minimizeInPlace(point), this.maximum.maximizeInPlace(point));
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
43
53
|
applyMatrix(matrix) {
|
|
44
54
|
if (this.isEmpty) {
|
|
45
55
|
return this;
|
|
@@ -61,4 +71,11 @@ export class CfgBoundingBox {
|
|
|
61
71
|
get isEmpty() {
|
|
62
72
|
return this._isEmpty;
|
|
63
73
|
}
|
|
74
|
+
get corners() {
|
|
75
|
+
const corners = [];
|
|
76
|
+
for (let a = 0; a < 8; a++) {
|
|
77
|
+
corners.push(new Vector3(((a & 1) === 0 ? this.minimum : this.maximum).x, ((a & 2) === 0 ? this.minimum : this.maximum).y, ((a & 4) === 0 ? this.minimum : this.maximum).z));
|
|
78
|
+
}
|
|
79
|
+
return corners;
|
|
80
|
+
}
|
|
64
81
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Matrix, Vector3 } from "@babylonjs/core";
|
|
2
|
+
import { MeasureParam } from "@configura/web-api";
|
|
3
|
+
import { CfgMeasureDefinition, CfgMeasurePriority } from "@configura/web-api/dist/CfgMeasure";
|
|
4
|
+
import { CfgProductNode } from "../../nodes/CfgProductNode.js";
|
|
5
|
+
import { CfgSymRootNode } from "../../nodes/CfgSymRootNode.js";
|
|
6
|
+
export declare type CfgAnchorTargetNode = CfgSymRootNode;
|
|
7
|
+
export declare type CfgAnchorableNode = CfgSymRootNode | CfgProductNode;
|
|
8
|
+
export declare function nodeEqualsMeasurePriorityNodeReference(node: CfgAnchorTargetNode, ref: CfgMeasurePriority): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The measureParamCode points to Measures inside Models. A Model can be used multiple times in a Product.
|
|
11
|
+
* More than one Model can use the same measureParamCode. Therefore what Measure an AnchorRef points to
|
|
12
|
+
* can be ambiguous. To help resolve the ambiguities, an anchorRef can have measurePriorities that
|
|
13
|
+
* governs which target it prefers to be anchored to.
|
|
14
|
+
*
|
|
15
|
+
* This method sorts the targets so that the highest priority (lowest index) one is first.
|
|
16
|
+
* Sorting is done in place on the original array.
|
|
17
|
+
*/
|
|
18
|
+
export declare const getAnchorTargetPriorityComparer: (measureDefinitions: CfgMeasureDefinition[]) => (measureParamCode: string, l: CfgAnchorTargetNode, r: CfgAnchorTargetNode) => number;
|
|
19
|
+
/**
|
|
20
|
+
* It might be possible in the future to anchor to other things than Measures (which are primarily)
|
|
21
|
+
* used for stretch, but for now we can only support anchoring to the ends of a Measure.
|
|
22
|
+
* toSp means anchor to start point, else end point.
|
|
23
|
+
* The MeasureParam class is used in several different contexts. To make the intent inside Stage
|
|
24
|
+
* clear CfgAnchorRef exists, which including only what is relevant for when used for anchoring.
|
|
25
|
+
*/
|
|
26
|
+
export declare class CfgAnchorRef {
|
|
27
|
+
readonly measureParamCode: string;
|
|
28
|
+
readonly toSp: boolean;
|
|
29
|
+
static make(m: MeasureParam | undefined): CfgAnchorRef | undefined;
|
|
30
|
+
private constructor();
|
|
31
|
+
equal(other: CfgAnchorRef): boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Gets the coordinates of the anchor point relative the anchor target. */
|
|
34
|
+
export declare function getRawAnchorPoint(anchorTarget: CfgAnchorTargetNode | undefined, anchorRef: CfgAnchorRef | undefined): Vector3 | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Calculate a matrix which is the translation that makes a point move as if it was a child of
|
|
37
|
+
* anchorTarget and then moved to the end of the anchorPoint.
|
|
38
|
+
*
|
|
39
|
+
* It is assumed that all anchoring happens on symRoot/additionalProduct-level and therefore all anchorees are
|
|
40
|
+
* siblings to the anchorers.
|
|
41
|
+
*
|
|
42
|
+
* Application of anchoring happens from anchor tree root and out, so that originalMatrix of
|
|
43
|
+
* anchorTarget will already have any anchoring and stretch applied.
|
|
44
|
+
*
|
|
45
|
+
* @param ignoreTargetTransformation If true, ignore anchorTarget's originalMatrix. This is a
|
|
46
|
+
* workaround for that CET (as of this writing) somewhat unexpectedly mostly does not apply the
|
|
47
|
+
* transformation when anchoring Additional Products. We have noticed some cases when it actually
|
|
48
|
+
* apply the transform as you would expect. When the bug is solved this should be removed.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getStretchedAnchorPointMatrix(anchorRef: CfgAnchorRef | undefined, anchorTarget: CfgAnchorTargetNode | undefined, ignoreTargetTransformation: boolean): Matrix | undefined;
|
|
51
|
+
export declare function updatedStretchedAnchorPointMatrix(anchorRef: CfgAnchorRef | undefined, anchorTarget: CfgAnchorTargetNode | undefined, currentStretchedAnchorPointMatrix: Matrix | undefined, ignoreTargetTransformation: boolean): Matrix | undefined;
|
|
52
|
+
//# sourceMappingURL=anchor.d.ts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Matrix, Vector3 } from "@babylonjs/core";
|
|
2
|
+
import { toStretchedPoint } from "../../geometry/stretch/CfgStretchData.js";
|
|
3
|
+
import { identifierIsUri } from "../utilitiesSymRootIdentifier.js";
|
|
4
|
+
/*
|
|
5
|
+
* MeasurePriorityNodeReferences only uses the url of a Model as identifier. Therefore it
|
|
6
|
+
* can not differentiate between the same Model/CfgSymRootNode used multiple time in the same
|
|
7
|
+
* Product. Future development in Catalogues might make this change.
|
|
8
|
+
*/
|
|
9
|
+
export function nodeEqualsMeasurePriorityNodeReference(node, ref) {
|
|
10
|
+
const nodeIdentifier = node._identifier;
|
|
11
|
+
// The identifier is made from URL when the node was made from an URL
|
|
12
|
+
if (!identifierIsUri(nodeIdentifier)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// The last segment of the URL is the Windows-style formatted relative file path to the model
|
|
16
|
+
const pathFromIdentifier = decodeURIComponent(nodeIdentifier.substr(nodeIdentifier.lastIndexOf("/") + 1));
|
|
17
|
+
return pathFromIdentifier === ref.modelLocalFilePath;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The measureParamCode points to Measures inside Models. A Model can be used multiple times in a Product.
|
|
21
|
+
* More than one Model can use the same measureParamCode. Therefore what Measure an AnchorRef points to
|
|
22
|
+
* can be ambiguous. To help resolve the ambiguities, an anchorRef can have measurePriorities that
|
|
23
|
+
* governs which target it prefers to be anchored to.
|
|
24
|
+
*
|
|
25
|
+
* This method sorts the targets so that the highest priority (lowest index) one is first.
|
|
26
|
+
* Sorting is done in place on the original array.
|
|
27
|
+
*/
|
|
28
|
+
export const getAnchorTargetPriorityComparer = (measureDefinitions) => (measureParamCode, l, r) => {
|
|
29
|
+
const measureDefinition = measureDefinitions.find((m) => m.measureParamCode === measureParamCode);
|
|
30
|
+
if (measureDefinition === undefined) {
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
const measurePriorities = measureDefinition.measurePriorities;
|
|
34
|
+
const lP = measurePriorities.find((m) => nodeEqualsMeasurePriorityNodeReference(l, m));
|
|
35
|
+
const rP = measurePriorities.find((m) => nodeEqualsMeasurePriorityNodeReference(r, m));
|
|
36
|
+
const lIndex = lP === undefined ? Number.POSITIVE_INFINITY : lP.index;
|
|
37
|
+
const rIndex = rP === undefined ? Number.POSITIVE_INFINITY : rP.index;
|
|
38
|
+
const prioDiff = lIndex - rIndex;
|
|
39
|
+
if (prioDiff !== 0) {
|
|
40
|
+
return prioDiff;
|
|
41
|
+
}
|
|
42
|
+
return l._identifier.localeCompare(r._identifier);
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* It might be possible in the future to anchor to other things than Measures (which are primarily)
|
|
46
|
+
* used for stretch, but for now we can only support anchoring to the ends of a Measure.
|
|
47
|
+
* toSp means anchor to start point, else end point.
|
|
48
|
+
* The MeasureParam class is used in several different contexts. To make the intent inside Stage
|
|
49
|
+
* clear CfgAnchorRef exists, which including only what is relevant for when used for anchoring.
|
|
50
|
+
*/
|
|
51
|
+
export class CfgAnchorRef {
|
|
52
|
+
constructor(measureParamCode, toSp) {
|
|
53
|
+
this.measureParamCode = measureParamCode;
|
|
54
|
+
this.toSp = toSp;
|
|
55
|
+
}
|
|
56
|
+
static make(m) {
|
|
57
|
+
if (m === undefined) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const { anchorPoint, code } = m;
|
|
61
|
+
if (anchorPoint === undefined) {
|
|
62
|
+
console.error("No anchorPoint for measureParam intended to be used as anchorRef");
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const isSp = anchorPoint === "sp";
|
|
66
|
+
const isEp = anchorPoint === "ep";
|
|
67
|
+
if (!isSp && !isEp) {
|
|
68
|
+
console.error("AnchorPoint is neither sp nor ep");
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return new CfgAnchorRef(code, isSp);
|
|
72
|
+
}
|
|
73
|
+
equal(other) {
|
|
74
|
+
return this.measureParamCode === other.measureParamCode && this.toSp === other.toSp;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Gets the coordinates of the anchor point relative the anchor target. */
|
|
78
|
+
export function getRawAnchorPoint(anchorTarget, anchorRef) {
|
|
79
|
+
if (anchorTarget === undefined) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
if (anchorRef === undefined) {
|
|
83
|
+
throw new Error("AnchorTo not set even though we got an anchorTarget");
|
|
84
|
+
}
|
|
85
|
+
const { measureParamCode, toSp } = anchorRef;
|
|
86
|
+
const { stretchDatas } = anchorTarget;
|
|
87
|
+
const targetStretchData = stretchDatas.find((stretchData) => measureParamCode === stretchData.measureParam);
|
|
88
|
+
if (targetStretchData === undefined) {
|
|
89
|
+
throw new Error("No target stretch data for measureParamCode found");
|
|
90
|
+
}
|
|
91
|
+
return toSp ? targetStretchData.sp : targetStretchData.ep;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Calculate a matrix which is the translation that makes a point move as if it was a child of
|
|
95
|
+
* anchorTarget and then moved to the end of the anchorPoint.
|
|
96
|
+
*
|
|
97
|
+
* It is assumed that all anchoring happens on symRoot/additionalProduct-level and therefore all anchorees are
|
|
98
|
+
* siblings to the anchorers.
|
|
99
|
+
*
|
|
100
|
+
* Application of anchoring happens from anchor tree root and out, so that originalMatrix of
|
|
101
|
+
* anchorTarget will already have any anchoring and stretch applied.
|
|
102
|
+
*
|
|
103
|
+
* @param ignoreTargetTransformation If true, ignore anchorTarget's originalMatrix. This is a
|
|
104
|
+
* workaround for that CET (as of this writing) somewhat unexpectedly mostly does not apply the
|
|
105
|
+
* transformation when anchoring Additional Products. We have noticed some cases when it actually
|
|
106
|
+
* apply the transform as you would expect. When the bug is solved this should be removed.
|
|
107
|
+
*/
|
|
108
|
+
export function getStretchedAnchorPointMatrix(anchorRef, anchorTarget, ignoreTargetTransformation) {
|
|
109
|
+
const rawAnchorPoint = getRawAnchorPoint(anchorTarget, anchorRef);
|
|
110
|
+
if (rawAnchorPoint === undefined || anchorTarget === undefined) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
// Apply current stretch to the anchor point
|
|
114
|
+
const stretchedAnchorPoint = toStretchedPoint(rawAnchorPoint, anchorTarget.stretchDatas, anchorTarget.cfgProductNode.product.configuration._internal
|
|
115
|
+
.stretchReferenceLengthsByMeasureParamCode);
|
|
116
|
+
// Sometimes, apply the anchorTarget matrix, as if the point was a child to the anchorTarget.
|
|
117
|
+
const anchorTargetMatrixApplied = ignoreTargetTransformation
|
|
118
|
+
? stretchedAnchorPoint
|
|
119
|
+
: Vector3.TransformCoordinates(stretchedAnchorPoint, anchorTarget.originalMatrix);
|
|
120
|
+
// We use translation as when we anchor things we do not change their orientation,
|
|
121
|
+
// we only move them to snap to this point
|
|
122
|
+
return Matrix.Identity().setTranslation(anchorTargetMatrixApplied);
|
|
123
|
+
}
|
|
124
|
+
export function updatedStretchedAnchorPointMatrix(anchorRef, anchorTarget, currentStretchedAnchorPointMatrix, ignoreTargetTransformation) {
|
|
125
|
+
const stretchedAnchorPointMatrix = getStretchedAnchorPointMatrix(anchorRef, anchorTarget, ignoreTargetTransformation);
|
|
126
|
+
if (currentStretchedAnchorPointMatrix === undefined &&
|
|
127
|
+
stretchedAnchorPointMatrix === undefined) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
if (currentStretchedAnchorPointMatrix !== undefined &&
|
|
131
|
+
stretchedAnchorPointMatrix !== undefined &&
|
|
132
|
+
stretchedAnchorPointMatrix.equals(currentStretchedAnchorPointMatrix)) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
return stretchedAnchorPointMatrix;
|
|
136
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a map from child to parent guaranteed to be loop free.
|
|
3
|
+
* The anchor children (keys in the map) will all have a "anchorRef" and there is what
|
|
4
|
+
* measureParam they are linked on is found.
|
|
5
|
+
* Sorted so that parents are always before their children
|
|
6
|
+
* This function is a bit of a "best effort". These things are not well defined in CET - you
|
|
7
|
+
* can anchor A on B and B on A at the same time for example. You can also anchor B to A
|
|
8
|
+
* while there exists multiple A:s. The code below is a naive implementation as in it will
|
|
9
|
+
* not make the solution which allows the most things to be linked. But that is fine as you
|
|
10
|
+
* should never create a catalogue with strange anchoring anyhow.
|
|
11
|
+
*/
|
|
12
|
+
export declare function makeAnchoredToAnchorMap<A extends {
|
|
13
|
+
anchorRef: {
|
|
14
|
+
measureParamCode: string;
|
|
15
|
+
} | undefined;
|
|
16
|
+
}, T extends {
|
|
17
|
+
stretchDatas: {
|
|
18
|
+
measureParam: string;
|
|
19
|
+
}[];
|
|
20
|
+
}>(nodes: (A | T)[], targetCandidateComparer: (measureParamCode: string, l: T, r: T) => number): Map<A, T>;
|
|
21
|
+
//# sourceMappingURL=anchorMap.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Tests do not work if we have Babylon-imports in the same file, so this has to be a separate file
|
|
2
|
+
/**
|
|
3
|
+
* Returns a map from child to parent guaranteed to be loop free.
|
|
4
|
+
* The anchor children (keys in the map) will all have a "anchorRef" and there is what
|
|
5
|
+
* measureParam they are linked on is found.
|
|
6
|
+
* Sorted so that parents are always before their children
|
|
7
|
+
* This function is a bit of a "best effort". These things are not well defined in CET - you
|
|
8
|
+
* can anchor A on B and B on A at the same time for example. You can also anchor B to A
|
|
9
|
+
* while there exists multiple A:s. The code below is a naive implementation as in it will
|
|
10
|
+
* not make the solution which allows the most things to be linked. But that is fine as you
|
|
11
|
+
* should never create a catalogue with strange anchoring anyhow.
|
|
12
|
+
*/
|
|
13
|
+
export function makeAnchoredToAnchorMap(nodes, targetCandidateComparer) {
|
|
14
|
+
function isAnchoree(n) {
|
|
15
|
+
return "anchorRef" in n;
|
|
16
|
+
}
|
|
17
|
+
function isTarget(n) {
|
|
18
|
+
return "stretchDatas" in n;
|
|
19
|
+
}
|
|
20
|
+
const anchoredToAnchorMap = new Map();
|
|
21
|
+
// Step 1: build a legal tree
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
if (!isAnchoree(node)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const anchorRef = node.anchorRef;
|
|
27
|
+
if (anchorRef === undefined) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const measureParam = anchorRef.measureParamCode;
|
|
31
|
+
const targetCandidates = nodes
|
|
32
|
+
.filter((n) => {
|
|
33
|
+
// Ignore self
|
|
34
|
+
if (node === n) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (!isTarget(n)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Can we anchor to this node?
|
|
41
|
+
return n.stretchDatas.some((stretchData) => measureParam === stretchData.measureParam);
|
|
42
|
+
})
|
|
43
|
+
.map((t, i) => ({ t, i }))
|
|
44
|
+
.sort((l, r) => {
|
|
45
|
+
// As sort is not stable in JS-versions older than EcmaScript 2019 we add the
|
|
46
|
+
// array original index to have as a fallback when there is a draw
|
|
47
|
+
const comp = targetCandidateComparer(anchorRef.measureParamCode, l.t, r.t);
|
|
48
|
+
if (comp === 0) {
|
|
49
|
+
return l.i - r.i;
|
|
50
|
+
}
|
|
51
|
+
return comp;
|
|
52
|
+
})
|
|
53
|
+
.map((tt) => tt.t);
|
|
54
|
+
let okay = true;
|
|
55
|
+
for (const otherNode of targetCandidates) {
|
|
56
|
+
okay = true;
|
|
57
|
+
// Loop control, the candidate can not be a child, then we would get a loop
|
|
58
|
+
let loopCandidate = otherNode;
|
|
59
|
+
while (loopCandidate !== undefined) {
|
|
60
|
+
if (!isAnchoree(loopCandidate)) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
loopCandidate = anchoredToAnchorMap.get(loopCandidate);
|
|
64
|
+
if (loopCandidate === node) {
|
|
65
|
+
okay = false;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (okay) {
|
|
70
|
+
anchoredToAnchorMap.set(node, otherNode);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!okay) {
|
|
75
|
+
console.warn("No SymRootNode to anchor to found.");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// From anchored to anchor
|
|
79
|
+
const unplaced = Array.from(anchoredToAnchorMap);
|
|
80
|
+
// Step 2: Build a sorted map so that parents are before their children
|
|
81
|
+
const result = [];
|
|
82
|
+
while (true) {
|
|
83
|
+
const candidate = unplaced.pop();
|
|
84
|
+
if (candidate === undefined) {
|
|
85
|
+
//done
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
const parent = candidate[1];
|
|
89
|
+
const indexInResult = result.findIndex((otherAnchoredToAnchor) => parent === otherAnchoredToAnchor[0]);
|
|
90
|
+
if (indexInResult === -1) {
|
|
91
|
+
// Candidates parent is not in the result, see if it in those waiting
|
|
92
|
+
const indexInUnplaced = unplaced.findIndex((item) => item[0] === parent);
|
|
93
|
+
if (indexInUnplaced === -1) {
|
|
94
|
+
// The parent was not part of the original nodes, so we just add this node.
|
|
95
|
+
result.push(candidate);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const toMoveUp = unplaced.splice(indexInUnplaced, 1)[0];
|
|
99
|
+
// Put back to candidate
|
|
100
|
+
unplaced.push(candidate);
|
|
101
|
+
// Move up the parent
|
|
102
|
+
unplaced.push(toMoveUp);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// Parent is in result
|
|
107
|
+
result.splice(indexInResult + 1, 0, candidate);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return new Map(result);
|
|
111
|
+
}
|
|
@@ -6,9 +6,24 @@ import { Point } from "@configura/web-core/dist/cm/geometry/Point.js";
|
|
|
6
6
|
import { Transform as GeoTransform } from "@configura/web-core/dist/cm/geometry/Transform.js";
|
|
7
7
|
import { CfgBoundingBox } from "./CfgBoundingBox.js";
|
|
8
8
|
export declare function vector3Equals(left: Vector3, right: Vector3): boolean;
|
|
9
|
+
export declare function cmPointToVector3(point: Point): Vector3;
|
|
9
10
|
declare type Axis = "x" | "y" | "z";
|
|
11
|
+
/**
|
|
12
|
+
* Calculates the change in one component for min or max when moving from one bounding box to
|
|
13
|
+
* another. Negative result is shrink, positive is grow. forMax is accounted for.
|
|
14
|
+
*/
|
|
10
15
|
export declare const getBoundingBoxChangeForAxis: (bbOld: CfgBoundingBox, bbNew: CfgBoundingBox, forMax: boolean, axis: Axis) => number;
|
|
16
|
+
/**
|
|
17
|
+
* Given an old bounding box and a new tells if the size difference in one component is significant.
|
|
18
|
+
*/
|
|
11
19
|
export declare const boundingBoxChangeIsSignificantForAxis: (bbOld: CfgBoundingBox, bbNew: CfgBoundingBox, forMax: boolean, axis: Axis, acceptableDifferenceShrink: number, acceptableDifferenceGrow: number) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Returns true if either:
|
|
22
|
+
* - One bounding box was empty and the other not.
|
|
23
|
+
* - There is a size change in any of the six primary directions which is larger than
|
|
24
|
+
* acceptableDifferenceShrink or acceptableDifferenceGrow depending if the change from bbOld to
|
|
25
|
+
* bbNew in that direction is shrink or grow.
|
|
26
|
+
*/
|
|
12
27
|
export declare function boundingBoxesAreSignificantlyDifferent(bbOld: CfgBoundingBox, bbNew: CfgBoundingBox, acceptableDifferenceShrink: number, acceptableDifferenceGrow: number): boolean;
|
|
13
28
|
export declare function unifyBoundingRect(boundingRect: ClientRect | DOMRect): {
|
|
14
29
|
x: number;
|
|
@@ -21,7 +36,36 @@ export declare function modelTransformsEqual(left: ModelTransform, right: ModelT
|
|
|
21
36
|
export declare function modelTransformToSymTransform(modelTransform: ModelTransform): SymTransform;
|
|
22
37
|
export declare function cloneName(name: string): string;
|
|
23
38
|
export declare function measureLongestDistanceToCorner(bb: CfgBoundingBox, measureComponents: number[]): number;
|
|
39
|
+
/**
|
|
40
|
+
* Creates an ATriMeshF instance containing a mesh of a sphere of the given radius (default 1)
|
|
41
|
+
* centered on the given position (default <0,0,0>). The "resolution" of the mesh is set by the
|
|
42
|
+
* segments value (default 32).
|
|
43
|
+
*
|
|
44
|
+
* UV coordinates are generated by wrapping the texture around the sphere like a world map.
|
|
45
|
+
*/
|
|
24
46
|
export declare function aTriMeshSphere(radius?: number, center?: Point, segments?: number): ATriMeshF;
|
|
47
|
+
/**
|
|
48
|
+
* Creates an ATriMeshF instance containing a mesh of a box between the two points.
|
|
49
|
+
* Default value of p0 is <0,0,0> and p1 is <1,1,1>.
|
|
50
|
+
*
|
|
51
|
+
* UV coordinates are generated, repeating the texture on each side.
|
|
52
|
+
*/
|
|
25
53
|
export declare function aTriMeshBox(p0?: Point, p1?: Point): ATriMeshF;
|
|
54
|
+
/**
|
|
55
|
+
* Creates an ATriMeshF instance containing a mesh representing an infinite plane with the given
|
|
56
|
+
* normal, offset by the given distance along the normal.
|
|
57
|
+
*
|
|
58
|
+
* Due to automatic bounding box calculations, the infinite plane is represented by a circular disc
|
|
59
|
+
* with the given radius centered on a point offset from the origin along the normal vector.
|
|
60
|
+
*
|
|
61
|
+
* The number of triangles in the disc is controlled by the refine parameter. The minimum number of
|
|
62
|
+
* triangles is 4, each refine step doubles the triangle count.
|
|
63
|
+
*
|
|
64
|
+
* Default normal: 1, 0, 0
|
|
65
|
+
* Default offset: 0
|
|
66
|
+
* Default radius: 1
|
|
67
|
+
* Default refinement: 4 (64 triangles)
|
|
68
|
+
*/
|
|
69
|
+
export declare function aTriMeshPlane(normal?: Point, offset?: number, radius?: number, refine?: number): ATriMeshF;
|
|
26
70
|
export {};
|
|
27
71
|
//# sourceMappingURL=utilities3D.d.ts.map
|