@configura/babylon-view 2.2.0-alpha.1 → 2.2.0-alpha.3
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/.eslintrc.json +5 -5
- package/LICENSE +201 -201
- package/README.md +1 -1
- package/dist/animation/AnimatableObject.d.ts +8 -8
- package/dist/animation/AnimatableObject.js +3 -3
- package/dist/animation/animator/Animator.d.ts +33 -33
- package/dist/animation/animator/Animator.js +58 -58
- package/dist/animation/animator/AnimatorEasing.d.ts +16 -16
- package/dist/animation/animator/AnimatorEasing.js +31 -31
- package/dist/animation/animator/AnimatorEasingMatrix.d.ts +14 -14
- package/dist/animation/animator/AnimatorEasingMatrix.js +16 -16
- package/dist/animation/animator/AnimatorHighlight.d.ts +16 -16
- package/dist/animation/animator/AnimatorHighlight.js +32 -32
- package/dist/animation/animator/AnimatorPointToPoint.d.ts +8 -8
- package/dist/animation/animator/AnimatorPointToPoint.js +14 -14
- package/dist/animation/animator/AnimatorQueue.d.ts +13 -13
- package/dist/animation/animator/AnimatorQueue.js +57 -57
- package/dist/animation/animator/AnimatorScale.d.ts +8 -8
- package/dist/animation/animator/AnimatorScale.js +13 -13
- package/dist/animation/animator/AnimatorSpin.d.ts +10 -10
- package/dist/animation/animator/AnimatorSpin.js +13 -13
- package/dist/animation/animator/EasingFunctions.d.ts +35 -35
- package/dist/animation/animator/EasingFunctions.js +137 -137
- package/dist/animation/coordinator/Coordinator.d.ts +28 -28
- package/dist/animation/coordinator/Coordinator.js +53 -53
- package/dist/animation/coordinator/CoordinatorDropAndSpin.d.ts +22 -22
- package/dist/animation/coordinator/CoordinatorDropAndSpin.js +138 -138
- package/dist/animation/coordinator/CoordinatorIdentity.d.ts +11 -11
- package/dist/animation/coordinator/CoordinatorIdentity.js +14 -14
- package/dist/animation/coordinator/CoordinatorNodeQueues.d.ts +18 -18
- package/dist/animation/coordinator/CoordinatorNodeQueues.js +50 -50
- package/dist/animation/coordinator/CoordinatorPulse.d.ts +21 -21
- package/dist/animation/coordinator/CoordinatorPulse.js +47 -47
- package/dist/animation/coordinator/CoordinatorPulseBounce.d.ts +14 -14
- package/dist/animation/coordinator/CoordinatorPulseBounce.js +35 -35
- package/dist/animation/coordinator/CoordinatorPulseHighlight.d.ts +13 -13
- package/dist/animation/coordinator/CoordinatorPulseHighlight.js +29 -29
- package/dist/animation/coordinator/CoordinatorPulseInflate.d.ts +14 -14
- package/dist/animation/coordinator/CoordinatorPulseInflate.js +23 -23
- package/dist/animation/index.d.ts +5 -0
- package/dist/animation/index.js +4 -0
- package/dist/camera/CameraCreator.d.ts +5 -5
- package/dist/camera/CameraCreator.js +4 -4
- package/dist/camera/CfgOrbitalCamera.d.ts +76 -76
- package/dist/camera/CfgOrbitalCamera.js +277 -277
- package/dist/camera/CfgOrbitalCameraControlProps.d.ts +14 -14
- package/dist/camera/CfgOrbitalCameraControlProps.js +7 -7
- package/dist/camera/GradingApplier.d.ts +3 -3
- package/dist/camera/GradingApplier.js +48 -48
- package/dist/camera/SSAO2PipelineCreator.d.ts +28 -0
- package/dist/camera/SSAO2PipelineCreator.js +23 -0
- package/dist/camera/index.d.ts +6 -0
- package/dist/camera/index.js +5 -0
- package/dist/engine/EngineCreator.d.ts +3 -3
- package/dist/engine/EngineCreator.js +10 -10
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.js +1 -0
- package/dist/geometry/CfgGeometry.d.ts +29 -29
- package/dist/geometry/CfgGeometry.js +146 -146
- package/dist/geometry/CfgMesh.d.ts +10 -10
- package/dist/geometry/CfgMesh.js +38 -38
- package/dist/geometry/geoSplitter.d.ts +8 -8
- package/dist/geometry/geoSplitter.js +192 -192
- package/dist/geometry/stretch/CfgMorphTarget.d.ts +15 -15
- package/dist/geometry/stretch/CfgMorphTarget.js +65 -65
- package/dist/geometry/stretch/CfgStretchData.d.ts +116 -116
- package/dist/geometry/stretch/CfgStretchData.js +347 -347
- package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +16 -16
- package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -95
- package/dist/index.d.ts +8 -16
- package/dist/index.js +8 -16
- package/dist/io/CfgHistoryToCameraConfConnector.d.ts +31 -31
- package/dist/io/CfgHistoryToCameraConfConnector.js +90 -90
- package/dist/io/CfgIOCameraConfConnector.d.ts +35 -35
- package/dist/io/CfgIOCameraConfConnector.js +81 -81
- package/dist/io/CfgObservableStateToCameraConfConnector.d.ts +10 -10
- package/dist/io/CfgObservableStateToCameraConfConnector.js +11 -11
- package/dist/io/CfgWindowMessageToCameraConfConnector.d.ts +10 -10
- package/dist/io/CfgWindowMessageToCameraConfConnector.js +11 -11
- package/dist/io/index.d.ts +4 -0
- package/dist/io/index.js +3 -0
- package/dist/light/CfgDirectionalLight.d.ts +8 -8
- package/dist/light/CfgDirectionalLight.js +18 -18
- package/dist/light/CfgHemisphericLight.d.ts +7 -7
- package/dist/light/CfgHemisphericLight.js +17 -17
- package/dist/light/CfgPointLight.d.ts +8 -8
- package/dist/light/CfgPointLight.js +18 -18
- package/dist/light/DefaultLightRig.d.ts +23 -19
- package/dist/light/DefaultLightRig.js +82 -77
- package/dist/light/LightRigCreator.d.ts +9 -9
- package/dist/light/LightRigCreator.js +3 -3
- package/dist/light/index.d.ts +6 -0
- package/dist/light/index.js +5 -0
- package/dist/material/CfgMaterial.d.ts +68 -68
- package/dist/material/CfgMaterial.js +497 -482
- package/dist/material/DummyMaterialCreator.d.ts +4 -4
- package/dist/material/DummyMaterialCreator.js +15 -15
- package/dist/material/material.d.ts +18 -18
- package/dist/material/material.js +128 -128
- package/dist/material/texture.d.ts +14 -14
- package/dist/material/texture.js +316 -306
- package/dist/nodes/CfgContentRootNode.d.ts +19 -19
- package/dist/nodes/CfgContentRootNode.js +75 -75
- package/dist/nodes/CfgDeferredMeshNode.d.ts +55 -55
- package/dist/nodes/CfgDeferredMeshNode.js +378 -378
- package/dist/nodes/CfgProductNode.d.ts +127 -127
- package/dist/nodes/CfgProductNode.js +598 -598
- package/dist/nodes/CfgSymNode.d.ts +50 -50
- package/dist/nodes/CfgSymNode.js +249 -249
- package/dist/nodes/CfgSymRootNode.d.ts +45 -45
- package/dist/nodes/CfgSymRootNode.js +240 -240
- package/dist/nodes/CfgTransformNode.d.ts +33 -33
- package/dist/nodes/CfgTransformNode.js +83 -83
- package/dist/scene/SceneCreator.d.ts +6 -6
- package/dist/scene/SceneCreator.js +22 -22
- package/dist/scene/index.d.ts +2 -0
- package/dist/scene/index.js +1 -0
- package/dist/utilities/CfgBoundingBox.d.ts +21 -21
- package/dist/utilities/CfgBoundingBox.js +81 -81
- package/dist/utilities/anchor/anchor.d.ts +50 -50
- package/dist/utilities/anchor/anchor.js +133 -133
- package/dist/utilities/anchor/anchorMap.d.ts +20 -20
- package/dist/utilities/anchor/anchorMap.js +111 -111
- package/dist/utilities/index.d.ts +4 -0
- package/dist/utilities/index.js +3 -0
- package/dist/utilities/utilities3D.d.ts +70 -70
- package/dist/utilities/utilities3D.js +259 -259
- package/dist/utilities/utilitiesColor.d.ts +18 -18
- package/dist/utilities/utilitiesColor.js +50 -50
- package/dist/utilities/utilitiesImage.d.ts +6 -6
- package/dist/utilities/utilitiesImage.js +107 -107
- package/dist/utilities/utilitiesSymRootIdentifier.d.ts +7 -7
- package/dist/utilities/utilitiesSymRootIdentifier.js +26 -26
- package/dist/view/BaseView.d.ts +80 -78
- package/dist/view/BaseView.js +321 -303
- package/dist/view/BaseViewConfiguration.d.ts +38 -32
- package/dist/view/BaseViewConfiguration.js +10 -10
- package/dist/view/RenderEnv.d.ts +45 -43
- package/dist/view/RenderEnv.js +7 -7
- package/dist/view/SingleProductDefaultCameraView.d.ts +38 -38
- package/dist/view/SingleProductDefaultCameraView.js +149 -149
- package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +44 -44
- package/dist/view/SingleProductDefaultCameraViewConfiguration.js +11 -11
- package/dist/view/SingleProductView.d.ts +44 -44
- package/dist/view/SingleProductView.js +212 -212
- package/dist/view/SingleProductViewConfiguration.d.ts +32 -32
- package/dist/view/SingleProductViewConfiguration.js +19 -19
- package/dist/view/index.d.ts +8 -0
- package/dist/view/index.js +7 -0
- package/package.json +5 -5
|
@@ -1,347 +1,347 @@
|
|
|
1
|
-
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
|
|
2
|
-
import { SymGfxMode } from "@configura/web-core/dist/cm/format/cmsym/components/SymComponent.js";
|
|
3
|
-
import { convertLength, hashCode } from "@configura/web-utilities";
|
|
4
|
-
import { MESH_UNIT } from "../../nodes/CfgSymRootNode.js";
|
|
5
|
-
import { cmPointToVector3 } from "../../utilities/utilities3D.js";
|
|
6
|
-
function calculateNormals(sp, ep) {
|
|
7
|
-
const normal = ep.subtract(sp);
|
|
8
|
-
const spToEpLength = normal.length();
|
|
9
|
-
const normalizedNormal = normal.normalizeToNew();
|
|
10
|
-
return {
|
|
11
|
-
spToEpLength,
|
|
12
|
-
normal,
|
|
13
|
-
normalizedNormal,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* How far along from sp to ep is a point on a plane crossing the sp to ep line?
|
|
18
|
-
* 1 means at ep, 0 means at sp.
|
|
19
|
-
*/
|
|
20
|
-
export function calculateAlongness(p, sp, spToEpLength, normalizedNormal) {
|
|
21
|
-
const towardTp = p.subtract(sp);
|
|
22
|
-
return ((normalizedNormal.x * towardTp.x +
|
|
23
|
-
normalizedNormal.y * towardTp.y +
|
|
24
|
-
normalizedNormal.z * towardTp.z) /
|
|
25
|
-
spToEpLength);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Where does the point p end up given the CfgStretchDatas and the StretchMap.
|
|
29
|
-
* This is the same equations that Babylon uses for stretching, only Babylon does it in the
|
|
30
|
-
* shaders during rendering and is thus GPU accelerated.
|
|
31
|
-
* This version is much slower, but that is okay as long as this function is sparingly used.
|
|
32
|
-
*/
|
|
33
|
-
export const toStretchedPoint = (p, stretchDatas, stretchReferenceLengthsByMeasureParamCode) => stretchDatas.reduce((acc, stretchData) => {
|
|
34
|
-
const referenceLength = stretchReferenceLengthsByMeasureParamCode.get(stretchData.measureParam);
|
|
35
|
-
if (referenceLength === undefined) {
|
|
36
|
-
return acc;
|
|
37
|
-
}
|
|
38
|
-
return acc.add(stretchData
|
|
39
|
-
.stretchPointToTwice(p)
|
|
40
|
-
.scale(stretchData.calculateInfluenceGivenTwiceMorphTarget(referenceLength.current)));
|
|
41
|
-
}, p);
|
|
42
|
-
function spOrEpToPoint(node) {
|
|
43
|
-
const trans = node.symTransform(true);
|
|
44
|
-
if (trans.scale.x !== 1 || trans.scale.y !== 1 || trans.scale.z !== 1) {
|
|
45
|
-
throw new Error("We do not expect scale on sp or ep");
|
|
46
|
-
}
|
|
47
|
-
if (trans.rot !== undefined) {
|
|
48
|
-
throw new Error("We do not expect rotation on sp or ep");
|
|
49
|
-
}
|
|
50
|
-
if (trans.pivot !== undefined) {
|
|
51
|
-
throw new Error("We do not expect pivot on sp or ep");
|
|
52
|
-
}
|
|
53
|
-
const pos = trans.pos;
|
|
54
|
-
if (pos === undefined) {
|
|
55
|
-
return new Vector3();
|
|
56
|
-
}
|
|
57
|
-
return cmPointToVector3(pos);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Please note that stretch is not bound to be contained between sp and ep.
|
|
61
|
-
*
|
|
62
|
-
* The thinking in the equations in CfgStretchData is that we want to support MorphTargets in an as
|
|
63
|
-
* efficient manner as possible. As MorphTargets work by morphing between pre-compiled meshes we
|
|
64
|
-
* calculate what happens if you stretch to twice the referenceLength, to make MorphTargets for
|
|
65
|
-
* twice the referenceLength, and then calculate how these MorphTargets should be applied to
|
|
66
|
-
* achieve to requested referenceLength.
|
|
67
|
-
*
|
|
68
|
-
* This adaption to MorphTargets might make the code for stretching points for other purposes look
|
|
69
|
-
* (be) a bit complicated. However, as a mesh often contains millions of vertices that needs to be
|
|
70
|
-
* moved and calculating for bounding boxes, and anchor points and such moves very few vertices,
|
|
71
|
-
* this is okay.
|
|
72
|
-
*
|
|
73
|
-
* Using "Twice" as the precomputed target is arbitrarily, we just chose it because it makes
|
|
74
|
-
* equations simple.
|
|
75
|
-
*/
|
|
76
|
-
export class CfgStretchData {
|
|
77
|
-
/**
|
|
78
|
-
* It is assumed that the symNode passed really is stretch (symNodeIsStretch has been run)
|
|
79
|
-
* so any data missing for stretch will be an Exception.
|
|
80
|
-
*
|
|
81
|
-
* @param detailLevel
|
|
82
|
-
* @param measureParam Name of the parameter controlling the stretch
|
|
83
|
-
* @param stretchSymNodeId Id of the SymNode. This is used for anchoring.
|
|
84
|
-
* @param originalReferenceLength Server-side calculated original referenceLength. We do not
|
|
85
|
-
* actually use this for anything, but it can be handy for debug.
|
|
86
|
-
* @param sp Start Point, the start anchor of the stretch line.
|
|
87
|
-
* @param ep End Point, the end anchor of the stretch line.
|
|
88
|
-
* @param spToEpLength Distance from sp to ep.
|
|
89
|
-
* @param originalSpToEpLength spToEpLength before transforms.
|
|
90
|
-
* @param normal The normal is from sp to ep and is the normal of every plane that divides this
|
|
91
|
-
* stretch.
|
|
92
|
-
* @param normalNormalized Normalized version of normal
|
|
93
|
-
* @param sections The slices the model is sliced into
|
|
94
|
-
*/
|
|
95
|
-
constructor(detailLevel, measureParam, stretchSymNodeId, originalReferenceLength, sp, ep, spToEpLength, originalSpToEpLength, normal, normalNormalized, sections) {
|
|
96
|
-
this.detailLevel = detailLevel;
|
|
97
|
-
this.measureParam = measureParam;
|
|
98
|
-
this.stretchSymNodeId = stretchSymNodeId;
|
|
99
|
-
this.originalReferenceLength = originalReferenceLength;
|
|
100
|
-
this.sp = sp;
|
|
101
|
-
this.ep = ep;
|
|
102
|
-
this.spToEpLength = spToEpLength;
|
|
103
|
-
this.originalSpToEpLength = originalSpToEpLength;
|
|
104
|
-
this.normal = normal;
|
|
105
|
-
this.normalNormalized = normalNormalized;
|
|
106
|
-
this.sections = sections;
|
|
107
|
-
/** How far along between sp and ep is p? */
|
|
108
|
-
this.getAlongness = (p) => calculateAlongness(p, this.sp, this.spToEpLength, this.normalNormalized);
|
|
109
|
-
}
|
|
110
|
-
static make(logger, symNode, detailLevel) {
|
|
111
|
-
var _a;
|
|
112
|
-
const symMeasure = symNode.getMeasure();
|
|
113
|
-
if (symMeasure === undefined) {
|
|
114
|
-
throw new Error("No symMeasure found");
|
|
115
|
-
}
|
|
116
|
-
const { measureParam, sections, originalLength } = symMeasure;
|
|
117
|
-
if (measureParam === undefined) {
|
|
118
|
-
throw new Error("No measureParam");
|
|
119
|
-
}
|
|
120
|
-
const stretchSymNodeId = symNode.id;
|
|
121
|
-
if (stretchSymNodeId !== measureParam) {
|
|
122
|
-
logger.warn(`measureParam (${measureParam}) has different ID from parent symNode (${stretchSymNodeId})`);
|
|
123
|
-
}
|
|
124
|
-
const children = symNode.children(logger, true, true);
|
|
125
|
-
let sp;
|
|
126
|
-
let ep;
|
|
127
|
-
const detailLevelToDividerNameToPos = new Map();
|
|
128
|
-
let detailLevelNodeName3D;
|
|
129
|
-
let detailLevelNodeName2D;
|
|
130
|
-
for (const [name, node] of children) {
|
|
131
|
-
const symReps = node.symReps(true);
|
|
132
|
-
const details = symReps._details;
|
|
133
|
-
// We relax the requirements on detail-level a bit when it comes to sp and ep.
|
|
134
|
-
// If we can not find sp and/or ep that is the right detail-level we take the
|
|
135
|
-
// first one we found.
|
|
136
|
-
if (sp === undefined && name === "sp") {
|
|
137
|
-
sp = spOrEpToPoint(node);
|
|
138
|
-
}
|
|
139
|
-
else if (ep === undefined && name === "ep") {
|
|
140
|
-
ep = spOrEpToPoint(node);
|
|
141
|
-
}
|
|
142
|
-
// We only handle children which either has no detail-level or has an acceptable
|
|
143
|
-
// detail-level
|
|
144
|
-
if (details !== undefined) {
|
|
145
|
-
const detailMask = details.values().next().value;
|
|
146
|
-
if (!detailMask.includes(detailLevel)) {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (name.startsWith("divider")) {
|
|
151
|
-
const nodeChildren = node.children(logger, true, true);
|
|
152
|
-
for (const [subLevelNodeName, subNode] of nodeChildren) {
|
|
153
|
-
const pos = (_a = subNode.symTransform()) === null || _a === void 0 ? void 0 : _a.pos;
|
|
154
|
-
if (pos === undefined) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
let dividerNameToPos = detailLevelToDividerNameToPos.get(subLevelNodeName);
|
|
158
|
-
if (dividerNameToPos === undefined) {
|
|
159
|
-
dividerNameToPos = new Map();
|
|
160
|
-
detailLevelToDividerNameToPos.set(subLevelNodeName, dividerNameToPos);
|
|
161
|
-
}
|
|
162
|
-
dividerNameToPos.set(name, cmPointToVector3(pos));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else if (name.startsWith("3D")) {
|
|
166
|
-
detailLevelNodeName3D = name;
|
|
167
|
-
}
|
|
168
|
-
else if (name.startsWith("2D")) {
|
|
169
|
-
detailLevelNodeName2D = name;
|
|
170
|
-
}
|
|
171
|
-
else if (name === "sp") {
|
|
172
|
-
sp = spOrEpToPoint(node);
|
|
173
|
-
}
|
|
174
|
-
else if (name === "ep") {
|
|
175
|
-
ep = spOrEpToPoint(node);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (sp === undefined) {
|
|
179
|
-
throw new Error("Start point missing");
|
|
180
|
-
}
|
|
181
|
-
if (ep === undefined) {
|
|
182
|
-
throw new Error("End point missing");
|
|
183
|
-
}
|
|
184
|
-
if (sp.equals(ep)) {
|
|
185
|
-
throw new Error("Start point and End point are equal");
|
|
186
|
-
}
|
|
187
|
-
const { spToEpLength, normal, normalizedNormal } = calculateNormals(sp, ep);
|
|
188
|
-
if (detailLevelToDividerNameToPos.size === 0) {
|
|
189
|
-
throw new Error("Dividers missing");
|
|
190
|
-
}
|
|
191
|
-
if (detailLevelNodeName3D === undefined && detailLevelNodeName2D === undefined) {
|
|
192
|
-
throw new Error("Details level node name missing");
|
|
193
|
-
}
|
|
194
|
-
let detailLevelGfxMode = SymGfxMode.undefined;
|
|
195
|
-
let dividerNameToPos;
|
|
196
|
-
if (detailLevelNodeName3D !== undefined) {
|
|
197
|
-
dividerNameToPos = detailLevelToDividerNameToPos.get(detailLevelNodeName3D);
|
|
198
|
-
if (dividerNameToPos !== undefined) {
|
|
199
|
-
detailLevelGfxMode = SymGfxMode.x3D;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if (dividerNameToPos === undefined && detailLevelNodeName2D !== undefined) {
|
|
203
|
-
dividerNameToPos = detailLevelToDividerNameToPos.get(detailLevelNodeName2D);
|
|
204
|
-
if (dividerNameToPos !== undefined) {
|
|
205
|
-
detailLevelGfxMode = SymGfxMode.x2D;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
if (dividerNameToPos === undefined) {
|
|
209
|
-
throw new Error("Dividers for detail level missing");
|
|
210
|
-
}
|
|
211
|
-
const processedSections = CfgStretchData.processSections(sections, detailLevel, detailLevelGfxMode, dividerNameToPos, sp, spToEpLength, normalizedNormal);
|
|
212
|
-
return new this(detailLevel, measureParam, stretchSymNodeId, originalLength, sp, ep, spToEpLength, spToEpLength, normal, normalizedNormal, processedSections);
|
|
213
|
-
}
|
|
214
|
-
static processSections(sections, detailLevel, detailLevelGfxMode, dividerNameToPos, sp, spToEpLength, normalizedNormal) {
|
|
215
|
-
// The morph target shall morph to making the morphed thing
|
|
216
|
-
// twice as long as before morph, that is, 100% increase.
|
|
217
|
-
// The sizeChange parameters are assumed to aggregate to 1.
|
|
218
|
-
// The alongness goes from 0 at sp to 1 at ep. So summing these
|
|
219
|
-
// should make the postStretchEndAlongness of the last one be 2.
|
|
220
|
-
let accumulatedSizeChange = 0;
|
|
221
|
-
let startAlongness = 0;
|
|
222
|
-
let atTwiceStartAlongness = 0;
|
|
223
|
-
return sections.map((section, sectionIndex) => {
|
|
224
|
-
const { move, sizeChange, gfxModeToDetailLevelToMultiSelector } = section;
|
|
225
|
-
const sectionsLength = sections.length;
|
|
226
|
-
if (sectionsLength < 1) {
|
|
227
|
-
throw new Error("At lease one section needed for stretch");
|
|
228
|
-
}
|
|
229
|
-
const first = sectionIndex === 0;
|
|
230
|
-
const last = sectionIndex === sectionsLength - 1;
|
|
231
|
-
const detailLevelToMultiSelector = gfxModeToDetailLevelToMultiSelector.get(detailLevelGfxMode);
|
|
232
|
-
if (detailLevelToMultiSelector === undefined) {
|
|
233
|
-
throw new Error("No x3D or x2D for section");
|
|
234
|
-
}
|
|
235
|
-
const multiSelector = detailLevelToMultiSelector.get(detailLevel);
|
|
236
|
-
if (multiSelector === undefined) {
|
|
237
|
-
throw new Error("No multiSelector for detail level");
|
|
238
|
-
}
|
|
239
|
-
const selectors = multiSelector.selectors;
|
|
240
|
-
const selectorsLength = selectors.length;
|
|
241
|
-
if ((first || last ? 1 : 2) !== selectorsLength) {
|
|
242
|
-
throw new Error("Divider plane count expected to be 1 for first or last and 2 for others");
|
|
243
|
-
}
|
|
244
|
-
let endAlongness;
|
|
245
|
-
if (last) {
|
|
246
|
-
endAlongness = 1;
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
const dividerId = selectors[selectorsLength - 1].dividerId;
|
|
250
|
-
const pos = dividerNameToPos.get(dividerId);
|
|
251
|
-
if (pos === undefined) {
|
|
252
|
-
throw new Error("No position found for symDividerSelector");
|
|
253
|
-
}
|
|
254
|
-
endAlongness = calculateAlongness(pos, sp, spToEpLength, normalizedNormal);
|
|
255
|
-
}
|
|
256
|
-
accumulatedSizeChange += sizeChange;
|
|
257
|
-
const atTwiceEndAlongness = endAlongness + accumulatedSizeChange;
|
|
258
|
-
const result = {
|
|
259
|
-
last,
|
|
260
|
-
move,
|
|
261
|
-
sizeChange,
|
|
262
|
-
startAlongness,
|
|
263
|
-
endAlongness,
|
|
264
|
-
atTwiceTranslation: move && 0 < sectionIndex
|
|
265
|
-
? atTwiceEndAlongness - endAlongness
|
|
266
|
-
: atTwiceStartAlongness - startAlongness,
|
|
267
|
-
atTwiceScale: move
|
|
268
|
-
? 0
|
|
269
|
-
: (atTwiceEndAlongness - atTwiceStartAlongness) /
|
|
270
|
-
(endAlongness - startAlongness) -
|
|
271
|
-
1,
|
|
272
|
-
};
|
|
273
|
-
startAlongness = endAlongness;
|
|
274
|
-
atTwiceStartAlongness = atTwiceEndAlongness;
|
|
275
|
-
return result;
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
get hash() {
|
|
279
|
-
if (this._hash === undefined) {
|
|
280
|
-
this._hash = hashCode(`${this.detailLevel}-${this.measureParam}-${this.sp.asArray().join(",")}-${this.ep
|
|
281
|
-
.asArray()
|
|
282
|
-
.join(",")}-${this.originalSpToEpLength}-${this.sections
|
|
283
|
-
.map((section) => `${section.startAlongness}-${section.endAlongness}-${section.move ? "Y" : "N"}-${section.sizeChange}`)
|
|
284
|
-
.join(",")}`);
|
|
285
|
-
}
|
|
286
|
-
return this._hash;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* If the Matrix is identity the original CfgStretchData is returned.
|
|
290
|
-
* If not a new CfgStretchData will be created.
|
|
291
|
-
* sp, ep, spToEpLength, normal, normalNormalized, scale will be cloned and transformed
|
|
292
|
-
* The rest will still reference the original objects
|
|
293
|
-
*/
|
|
294
|
-
applyMatrix(matrix) {
|
|
295
|
-
if (matrix.isIdentity()) {
|
|
296
|
-
return this;
|
|
297
|
-
}
|
|
298
|
-
const sp = Vector3.TransformCoordinates(this.sp, matrix);
|
|
299
|
-
const ep = Vector3.TransformCoordinates(this.ep, matrix);
|
|
300
|
-
const { spToEpLength, normal, normalizedNormal } = calculateNormals(sp, ep);
|
|
301
|
-
return new CfgStretchData(this.detailLevel, this.measureParam, this.stretchSymNodeId, this.originalReferenceLength, sp, ep, spToEpLength, this.spToEpLength, normal, normalizedNormal, this.sections);
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Calculates the translation of a point which moves it from its original position to where it
|
|
305
|
-
* would be if we stretched to twice the reference length. Please note: this gives the
|
|
306
|
-
* translation, not the new location. The new location will be p.add(stretchPointToTwice(p))
|
|
307
|
-
*/
|
|
308
|
-
stretchPointToTwice(p) {
|
|
309
|
-
const alongness = this.getAlongness(p);
|
|
310
|
-
const { sections, normal } = this;
|
|
311
|
-
for (const section of sections) {
|
|
312
|
-
const endAlongness = section.endAlongness;
|
|
313
|
-
if (section.last || alongness <= endAlongness) {
|
|
314
|
-
let translation = section.atTwiceTranslation;
|
|
315
|
-
if (!section.move) {
|
|
316
|
-
translation += (alongness - section.startAlongness) * section.atTwiceScale;
|
|
317
|
-
}
|
|
318
|
-
return normal.scale(translation);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
throw new Error("Should not be possible");
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Given a reference length calculates the influence that should be applied to a MorphTarget to
|
|
325
|
-
* achieve the requested length, given that these MorphTargets are constructed so that they
|
|
326
|
-
* will double the length when the influence 1 is applied.
|
|
327
|
-
*
|
|
328
|
-
* Please note that the referenceLength is the wanted length between sp and ep, that is, the
|
|
329
|
-
* stretch line. This line does not have to be span the entire model. Also, there can be
|
|
330
|
-
* multiple stretch lines that are not orthogonal to this, and the will also affect the actual
|
|
331
|
-
* size. Finally there can be transforms applied in the tree. So, the referenceLength will only
|
|
332
|
-
* be the actual length of the model in specific cases. However, we believe that models with
|
|
333
|
-
* stretch are often constructed in such a way that in real use cases the referenceLength is
|
|
334
|
-
* the actual real length.
|
|
335
|
-
*/
|
|
336
|
-
calculateInfluenceGivenTwiceMorphTarget(referenceLength) {
|
|
337
|
-
const { originalSpToEpLength } = this;
|
|
338
|
-
if (referenceLength.length === 0) {
|
|
339
|
-
// CET considers 0 as a no-value and will return the stretch to the default value.
|
|
340
|
-
// If this changes, remove this workaround.
|
|
341
|
-
return 0;
|
|
342
|
-
}
|
|
343
|
-
return ((convertLength(referenceLength.length, referenceLength.unit, MESH_UNIT) -
|
|
344
|
-
originalSpToEpLength) /
|
|
345
|
-
originalSpToEpLength);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
1
|
+
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
|
|
2
|
+
import { SymGfxMode } from "@configura/web-core/dist/cm/format/cmsym/components/SymComponent.js";
|
|
3
|
+
import { convertLength, hashCode } from "@configura/web-utilities";
|
|
4
|
+
import { MESH_UNIT } from "../../nodes/CfgSymRootNode.js";
|
|
5
|
+
import { cmPointToVector3 } from "../../utilities/utilities3D.js";
|
|
6
|
+
function calculateNormals(sp, ep) {
|
|
7
|
+
const normal = ep.subtract(sp);
|
|
8
|
+
const spToEpLength = normal.length();
|
|
9
|
+
const normalizedNormal = normal.normalizeToNew();
|
|
10
|
+
return {
|
|
11
|
+
spToEpLength,
|
|
12
|
+
normal,
|
|
13
|
+
normalizedNormal,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How far along from sp to ep is a point on a plane crossing the sp to ep line?
|
|
18
|
+
* 1 means at ep, 0 means at sp.
|
|
19
|
+
*/
|
|
20
|
+
export function calculateAlongness(p, sp, spToEpLength, normalizedNormal) {
|
|
21
|
+
const towardTp = p.subtract(sp);
|
|
22
|
+
return ((normalizedNormal.x * towardTp.x +
|
|
23
|
+
normalizedNormal.y * towardTp.y +
|
|
24
|
+
normalizedNormal.z * towardTp.z) /
|
|
25
|
+
spToEpLength);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Where does the point p end up given the CfgStretchDatas and the StretchMap.
|
|
29
|
+
* This is the same equations that Babylon uses for stretching, only Babylon does it in the
|
|
30
|
+
* shaders during rendering and is thus GPU accelerated.
|
|
31
|
+
* This version is much slower, but that is okay as long as this function is sparingly used.
|
|
32
|
+
*/
|
|
33
|
+
export const toStretchedPoint = (p, stretchDatas, stretchReferenceLengthsByMeasureParamCode) => stretchDatas.reduce((acc, stretchData) => {
|
|
34
|
+
const referenceLength = stretchReferenceLengthsByMeasureParamCode.get(stretchData.measureParam);
|
|
35
|
+
if (referenceLength === undefined) {
|
|
36
|
+
return acc;
|
|
37
|
+
}
|
|
38
|
+
return acc.add(stretchData
|
|
39
|
+
.stretchPointToTwice(p)
|
|
40
|
+
.scale(stretchData.calculateInfluenceGivenTwiceMorphTarget(referenceLength.current)));
|
|
41
|
+
}, p);
|
|
42
|
+
function spOrEpToPoint(node) {
|
|
43
|
+
const trans = node.symTransform(true);
|
|
44
|
+
if (trans.scale.x !== 1 || trans.scale.y !== 1 || trans.scale.z !== 1) {
|
|
45
|
+
throw new Error("We do not expect scale on sp or ep");
|
|
46
|
+
}
|
|
47
|
+
if (trans.rot !== undefined) {
|
|
48
|
+
throw new Error("We do not expect rotation on sp or ep");
|
|
49
|
+
}
|
|
50
|
+
if (trans.pivot !== undefined) {
|
|
51
|
+
throw new Error("We do not expect pivot on sp or ep");
|
|
52
|
+
}
|
|
53
|
+
const pos = trans.pos;
|
|
54
|
+
if (pos === undefined) {
|
|
55
|
+
return new Vector3();
|
|
56
|
+
}
|
|
57
|
+
return cmPointToVector3(pos);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Please note that stretch is not bound to be contained between sp and ep.
|
|
61
|
+
*
|
|
62
|
+
* The thinking in the equations in CfgStretchData is that we want to support MorphTargets in an as
|
|
63
|
+
* efficient manner as possible. As MorphTargets work by morphing between pre-compiled meshes we
|
|
64
|
+
* calculate what happens if you stretch to twice the referenceLength, to make MorphTargets for
|
|
65
|
+
* twice the referenceLength, and then calculate how these MorphTargets should be applied to
|
|
66
|
+
* achieve to requested referenceLength.
|
|
67
|
+
*
|
|
68
|
+
* This adaption to MorphTargets might make the code for stretching points for other purposes look
|
|
69
|
+
* (be) a bit complicated. However, as a mesh often contains millions of vertices that needs to be
|
|
70
|
+
* moved and calculating for bounding boxes, and anchor points and such moves very few vertices,
|
|
71
|
+
* this is okay.
|
|
72
|
+
*
|
|
73
|
+
* Using "Twice" as the precomputed target is arbitrarily, we just chose it because it makes
|
|
74
|
+
* equations simple.
|
|
75
|
+
*/
|
|
76
|
+
export class CfgStretchData {
|
|
77
|
+
/**
|
|
78
|
+
* It is assumed that the symNode passed really is stretch (symNodeIsStretch has been run)
|
|
79
|
+
* so any data missing for stretch will be an Exception.
|
|
80
|
+
*
|
|
81
|
+
* @param detailLevel
|
|
82
|
+
* @param measureParam Name of the parameter controlling the stretch
|
|
83
|
+
* @param stretchSymNodeId Id of the SymNode. This is used for anchoring.
|
|
84
|
+
* @param originalReferenceLength Server-side calculated original referenceLength. We do not
|
|
85
|
+
* actually use this for anything, but it can be handy for debug.
|
|
86
|
+
* @param sp Start Point, the start anchor of the stretch line.
|
|
87
|
+
* @param ep End Point, the end anchor of the stretch line.
|
|
88
|
+
* @param spToEpLength Distance from sp to ep.
|
|
89
|
+
* @param originalSpToEpLength spToEpLength before transforms.
|
|
90
|
+
* @param normal The normal is from sp to ep and is the normal of every plane that divides this
|
|
91
|
+
* stretch.
|
|
92
|
+
* @param normalNormalized Normalized version of normal
|
|
93
|
+
* @param sections The slices the model is sliced into
|
|
94
|
+
*/
|
|
95
|
+
constructor(detailLevel, measureParam, stretchSymNodeId, originalReferenceLength, sp, ep, spToEpLength, originalSpToEpLength, normal, normalNormalized, sections) {
|
|
96
|
+
this.detailLevel = detailLevel;
|
|
97
|
+
this.measureParam = measureParam;
|
|
98
|
+
this.stretchSymNodeId = stretchSymNodeId;
|
|
99
|
+
this.originalReferenceLength = originalReferenceLength;
|
|
100
|
+
this.sp = sp;
|
|
101
|
+
this.ep = ep;
|
|
102
|
+
this.spToEpLength = spToEpLength;
|
|
103
|
+
this.originalSpToEpLength = originalSpToEpLength;
|
|
104
|
+
this.normal = normal;
|
|
105
|
+
this.normalNormalized = normalNormalized;
|
|
106
|
+
this.sections = sections;
|
|
107
|
+
/** How far along between sp and ep is p? */
|
|
108
|
+
this.getAlongness = (p) => calculateAlongness(p, this.sp, this.spToEpLength, this.normalNormalized);
|
|
109
|
+
}
|
|
110
|
+
static make(logger, symNode, detailLevel) {
|
|
111
|
+
var _a;
|
|
112
|
+
const symMeasure = symNode.getMeasure();
|
|
113
|
+
if (symMeasure === undefined) {
|
|
114
|
+
throw new Error("No symMeasure found");
|
|
115
|
+
}
|
|
116
|
+
const { measureParam, sections, originalLength } = symMeasure;
|
|
117
|
+
if (measureParam === undefined) {
|
|
118
|
+
throw new Error("No measureParam");
|
|
119
|
+
}
|
|
120
|
+
const stretchSymNodeId = symNode.id;
|
|
121
|
+
if (stretchSymNodeId !== measureParam) {
|
|
122
|
+
logger.warn(`measureParam (${measureParam}) has different ID from parent symNode (${stretchSymNodeId})`);
|
|
123
|
+
}
|
|
124
|
+
const children = symNode.children(logger, true, true);
|
|
125
|
+
let sp;
|
|
126
|
+
let ep;
|
|
127
|
+
const detailLevelToDividerNameToPos = new Map();
|
|
128
|
+
let detailLevelNodeName3D;
|
|
129
|
+
let detailLevelNodeName2D;
|
|
130
|
+
for (const [name, node] of children) {
|
|
131
|
+
const symReps = node.symReps(true);
|
|
132
|
+
const details = symReps._details;
|
|
133
|
+
// We relax the requirements on detail-level a bit when it comes to sp and ep.
|
|
134
|
+
// If we can not find sp and/or ep that is the right detail-level we take the
|
|
135
|
+
// first one we found.
|
|
136
|
+
if (sp === undefined && name === "sp") {
|
|
137
|
+
sp = spOrEpToPoint(node);
|
|
138
|
+
}
|
|
139
|
+
else if (ep === undefined && name === "ep") {
|
|
140
|
+
ep = spOrEpToPoint(node);
|
|
141
|
+
}
|
|
142
|
+
// We only handle children which either has no detail-level or has an acceptable
|
|
143
|
+
// detail-level
|
|
144
|
+
if (details !== undefined) {
|
|
145
|
+
const detailMask = details.values().next().value;
|
|
146
|
+
if (!detailMask.includes(detailLevel)) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (name.startsWith("divider")) {
|
|
151
|
+
const nodeChildren = node.children(logger, true, true);
|
|
152
|
+
for (const [subLevelNodeName, subNode] of nodeChildren) {
|
|
153
|
+
const pos = (_a = subNode.symTransform()) === null || _a === void 0 ? void 0 : _a.pos;
|
|
154
|
+
if (pos === undefined) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
let dividerNameToPos = detailLevelToDividerNameToPos.get(subLevelNodeName);
|
|
158
|
+
if (dividerNameToPos === undefined) {
|
|
159
|
+
dividerNameToPos = new Map();
|
|
160
|
+
detailLevelToDividerNameToPos.set(subLevelNodeName, dividerNameToPos);
|
|
161
|
+
}
|
|
162
|
+
dividerNameToPos.set(name, cmPointToVector3(pos));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (name.startsWith("3D")) {
|
|
166
|
+
detailLevelNodeName3D = name;
|
|
167
|
+
}
|
|
168
|
+
else if (name.startsWith("2D")) {
|
|
169
|
+
detailLevelNodeName2D = name;
|
|
170
|
+
}
|
|
171
|
+
else if (name === "sp") {
|
|
172
|
+
sp = spOrEpToPoint(node);
|
|
173
|
+
}
|
|
174
|
+
else if (name === "ep") {
|
|
175
|
+
ep = spOrEpToPoint(node);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (sp === undefined) {
|
|
179
|
+
throw new Error("Start point missing");
|
|
180
|
+
}
|
|
181
|
+
if (ep === undefined) {
|
|
182
|
+
throw new Error("End point missing");
|
|
183
|
+
}
|
|
184
|
+
if (sp.equals(ep)) {
|
|
185
|
+
throw new Error("Start point and End point are equal");
|
|
186
|
+
}
|
|
187
|
+
const { spToEpLength, normal, normalizedNormal } = calculateNormals(sp, ep);
|
|
188
|
+
if (detailLevelToDividerNameToPos.size === 0) {
|
|
189
|
+
throw new Error("Dividers missing");
|
|
190
|
+
}
|
|
191
|
+
if (detailLevelNodeName3D === undefined && detailLevelNodeName2D === undefined) {
|
|
192
|
+
throw new Error("Details level node name missing");
|
|
193
|
+
}
|
|
194
|
+
let detailLevelGfxMode = SymGfxMode.undefined;
|
|
195
|
+
let dividerNameToPos;
|
|
196
|
+
if (detailLevelNodeName3D !== undefined) {
|
|
197
|
+
dividerNameToPos = detailLevelToDividerNameToPos.get(detailLevelNodeName3D);
|
|
198
|
+
if (dividerNameToPos !== undefined) {
|
|
199
|
+
detailLevelGfxMode = SymGfxMode.x3D;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (dividerNameToPos === undefined && detailLevelNodeName2D !== undefined) {
|
|
203
|
+
dividerNameToPos = detailLevelToDividerNameToPos.get(detailLevelNodeName2D);
|
|
204
|
+
if (dividerNameToPos !== undefined) {
|
|
205
|
+
detailLevelGfxMode = SymGfxMode.x2D;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (dividerNameToPos === undefined) {
|
|
209
|
+
throw new Error("Dividers for detail level missing");
|
|
210
|
+
}
|
|
211
|
+
const processedSections = CfgStretchData.processSections(sections, detailLevel, detailLevelGfxMode, dividerNameToPos, sp, spToEpLength, normalizedNormal);
|
|
212
|
+
return new this(detailLevel, measureParam, stretchSymNodeId, originalLength, sp, ep, spToEpLength, spToEpLength, normal, normalizedNormal, processedSections);
|
|
213
|
+
}
|
|
214
|
+
static processSections(sections, detailLevel, detailLevelGfxMode, dividerNameToPos, sp, spToEpLength, normalizedNormal) {
|
|
215
|
+
// The morph target shall morph to making the morphed thing
|
|
216
|
+
// twice as long as before morph, that is, 100% increase.
|
|
217
|
+
// The sizeChange parameters are assumed to aggregate to 1.
|
|
218
|
+
// The alongness goes from 0 at sp to 1 at ep. So summing these
|
|
219
|
+
// should make the postStretchEndAlongness of the last one be 2.
|
|
220
|
+
let accumulatedSizeChange = 0;
|
|
221
|
+
let startAlongness = 0;
|
|
222
|
+
let atTwiceStartAlongness = 0;
|
|
223
|
+
return sections.map((section, sectionIndex) => {
|
|
224
|
+
const { move, sizeChange, gfxModeToDetailLevelToMultiSelector } = section;
|
|
225
|
+
const sectionsLength = sections.length;
|
|
226
|
+
if (sectionsLength < 1) {
|
|
227
|
+
throw new Error("At lease one section needed for stretch");
|
|
228
|
+
}
|
|
229
|
+
const first = sectionIndex === 0;
|
|
230
|
+
const last = sectionIndex === sectionsLength - 1;
|
|
231
|
+
const detailLevelToMultiSelector = gfxModeToDetailLevelToMultiSelector.get(detailLevelGfxMode);
|
|
232
|
+
if (detailLevelToMultiSelector === undefined) {
|
|
233
|
+
throw new Error("No x3D or x2D for section");
|
|
234
|
+
}
|
|
235
|
+
const multiSelector = detailLevelToMultiSelector.get(detailLevel);
|
|
236
|
+
if (multiSelector === undefined) {
|
|
237
|
+
throw new Error("No multiSelector for detail level");
|
|
238
|
+
}
|
|
239
|
+
const selectors = multiSelector.selectors;
|
|
240
|
+
const selectorsLength = selectors.length;
|
|
241
|
+
if ((first || last ? 1 : 2) !== selectorsLength) {
|
|
242
|
+
throw new Error("Divider plane count expected to be 1 for first or last and 2 for others");
|
|
243
|
+
}
|
|
244
|
+
let endAlongness;
|
|
245
|
+
if (last) {
|
|
246
|
+
endAlongness = 1;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const dividerId = selectors[selectorsLength - 1].dividerId;
|
|
250
|
+
const pos = dividerNameToPos.get(dividerId);
|
|
251
|
+
if (pos === undefined) {
|
|
252
|
+
throw new Error("No position found for symDividerSelector");
|
|
253
|
+
}
|
|
254
|
+
endAlongness = calculateAlongness(pos, sp, spToEpLength, normalizedNormal);
|
|
255
|
+
}
|
|
256
|
+
accumulatedSizeChange += sizeChange;
|
|
257
|
+
const atTwiceEndAlongness = endAlongness + accumulatedSizeChange;
|
|
258
|
+
const result = {
|
|
259
|
+
last,
|
|
260
|
+
move,
|
|
261
|
+
sizeChange,
|
|
262
|
+
startAlongness,
|
|
263
|
+
endAlongness,
|
|
264
|
+
atTwiceTranslation: move && 0 < sectionIndex
|
|
265
|
+
? atTwiceEndAlongness - endAlongness
|
|
266
|
+
: atTwiceStartAlongness - startAlongness,
|
|
267
|
+
atTwiceScale: move
|
|
268
|
+
? 0
|
|
269
|
+
: (atTwiceEndAlongness - atTwiceStartAlongness) /
|
|
270
|
+
(endAlongness - startAlongness) -
|
|
271
|
+
1,
|
|
272
|
+
};
|
|
273
|
+
startAlongness = endAlongness;
|
|
274
|
+
atTwiceStartAlongness = atTwiceEndAlongness;
|
|
275
|
+
return result;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
get hash() {
|
|
279
|
+
if (this._hash === undefined) {
|
|
280
|
+
this._hash = hashCode(`${this.detailLevel}-${this.measureParam}-${this.sp.asArray().join(",")}-${this.ep
|
|
281
|
+
.asArray()
|
|
282
|
+
.join(",")}-${this.originalSpToEpLength}-${this.sections
|
|
283
|
+
.map((section) => `${section.startAlongness}-${section.endAlongness}-${section.move ? "Y" : "N"}-${section.sizeChange}`)
|
|
284
|
+
.join(",")}`);
|
|
285
|
+
}
|
|
286
|
+
return this._hash;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* If the Matrix is identity the original CfgStretchData is returned.
|
|
290
|
+
* If not a new CfgStretchData will be created.
|
|
291
|
+
* sp, ep, spToEpLength, normal, normalNormalized, scale will be cloned and transformed
|
|
292
|
+
* The rest will still reference the original objects
|
|
293
|
+
*/
|
|
294
|
+
applyMatrix(matrix) {
|
|
295
|
+
if (matrix.isIdentity()) {
|
|
296
|
+
return this;
|
|
297
|
+
}
|
|
298
|
+
const sp = Vector3.TransformCoordinates(this.sp, matrix);
|
|
299
|
+
const ep = Vector3.TransformCoordinates(this.ep, matrix);
|
|
300
|
+
const { spToEpLength, normal, normalizedNormal } = calculateNormals(sp, ep);
|
|
301
|
+
return new CfgStretchData(this.detailLevel, this.measureParam, this.stretchSymNodeId, this.originalReferenceLength, sp, ep, spToEpLength, this.spToEpLength, normal, normalizedNormal, this.sections);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Calculates the translation of a point which moves it from its original position to where it
|
|
305
|
+
* would be if we stretched to twice the reference length. Please note: this gives the
|
|
306
|
+
* translation, not the new location. The new location will be p.add(stretchPointToTwice(p))
|
|
307
|
+
*/
|
|
308
|
+
stretchPointToTwice(p) {
|
|
309
|
+
const alongness = this.getAlongness(p);
|
|
310
|
+
const { sections, normal } = this;
|
|
311
|
+
for (const section of sections) {
|
|
312
|
+
const endAlongness = section.endAlongness;
|
|
313
|
+
if (section.last || alongness <= endAlongness) {
|
|
314
|
+
let translation = section.atTwiceTranslation;
|
|
315
|
+
if (!section.move) {
|
|
316
|
+
translation += (alongness - section.startAlongness) * section.atTwiceScale;
|
|
317
|
+
}
|
|
318
|
+
return normal.scale(translation);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
throw new Error("Should not be possible");
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Given a reference length calculates the influence that should be applied to a MorphTarget to
|
|
325
|
+
* achieve the requested length, given that these MorphTargets are constructed so that they
|
|
326
|
+
* will double the length when the influence 1 is applied.
|
|
327
|
+
*
|
|
328
|
+
* Please note that the referenceLength is the wanted length between sp and ep, that is, the
|
|
329
|
+
* stretch line. This line does not have to be span the entire model. Also, there can be
|
|
330
|
+
* multiple stretch lines that are not orthogonal to this, and the will also affect the actual
|
|
331
|
+
* size. Finally there can be transforms applied in the tree. So, the referenceLength will only
|
|
332
|
+
* be the actual length of the model in specific cases. However, we believe that models with
|
|
333
|
+
* stretch are often constructed in such a way that in real use cases the referenceLength is
|
|
334
|
+
* the actual real length.
|
|
335
|
+
*/
|
|
336
|
+
calculateInfluenceGivenTwiceMorphTarget(referenceLength) {
|
|
337
|
+
const { originalSpToEpLength } = this;
|
|
338
|
+
if (referenceLength.length === 0) {
|
|
339
|
+
// CET considers 0 as a no-value and will return the stretch to the default value.
|
|
340
|
+
// If this changes, remove this workaround.
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
return ((convertLength(referenceLength.length, referenceLength.unit, MESH_UNIT) -
|
|
344
|
+
originalSpToEpLength) /
|
|
345
|
+
originalSpToEpLength);
|
|
346
|
+
}
|
|
347
|
+
}
|