@code3d/core 0.0.1-alpha.11 → 0.0.1-alpha.12
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/README.md +18 -13
- package/bld/chunks/{chunk-JBVIAMJ6.js → chunk-CQKOD54C.js} +552 -25
- package/bld/chunks/chunk-CQKOD54C.js.map +7 -0
- package/bld/chunks/{chunk-CL3E2DE4.js → chunk-JF6QRICU.js} +2 -2
- package/bld/chunks/{chunk-HZRQUHM2.js → chunk-L4V6X7RH.js} +2 -2
- package/bld/chunks/{chunk-QK6ZET47.js → chunk-QJVTVAYM.js} +3 -3
- package/bld/library/extrude.d.ts +5 -0
- package/bld/library/extrude.d.ts.map +1 -1
- package/bld/library/index.d.ts +2 -2
- package/bld/library/index.d.ts.map +1 -1
- package/bld/library/index.js +11 -1
- package/bld/library/inspect.d.ts +3 -1
- package/bld/library/inspect.d.ts.map +1 -1
- package/bld/library/loft.d.ts +7 -1
- package/bld/library/loft.d.ts.map +1 -1
- package/bld/library/replicad.js +2 -2
- package/bld/library/runtime.d.ts +68 -7
- package/bld/library/runtime.d.ts.map +1 -1
- package/bld/node/index.js +13 -3
- package/bld/node/replicad.js +4 -4
- package/bld/tooling/index.js +2 -2
- package/docs/api.md +134 -13
- package/docs/local-coordinates.md +2 -0
- package/docs/runtime.md +42 -2
- package/docs/topology.md +6 -0
- package/docs/values.md +9 -0
- package/package.json +1 -1
- package/src/library/extrude.ts +129 -2
- package/src/library/index.ts +6 -0
- package/src/library/inspect.ts +4 -2
- package/src/library/loft.ts +67 -2
- package/src/library/runtime.ts +599 -7
- package/bld/chunks/chunk-JBVIAMJ6.js.map +0 -7
- /package/bld/chunks/{chunk-CL3E2DE4.js.map → chunk-JF6QRICU.js.map} +0 -0
- /package/bld/chunks/{chunk-HZRQUHM2.js.map → chunk-L4V6X7RH.js.map} +0 -0
- /package/bld/chunks/{chunk-QK6ZET47.js.map → chunk-QJVTVAYM.js.map} +0 -0
package/src/library/runtime.ts
CHANGED
|
@@ -14,6 +14,9 @@ import {
|
|
|
14
14
|
makeSphere,
|
|
15
15
|
makeThreePointArc,
|
|
16
16
|
makeVertex,
|
|
17
|
+
measureShapeLinearProperties,
|
|
18
|
+
measureShapeSurfaceProperties,
|
|
19
|
+
measureShapeVolumeProperties,
|
|
17
20
|
sketchCircle,
|
|
18
21
|
sketchEllipse,
|
|
19
22
|
sketchPolysides,
|
|
@@ -35,7 +38,7 @@ import {
|
|
|
35
38
|
type AlignmentGeometry,
|
|
36
39
|
} from './alignment-geometry.js';
|
|
37
40
|
import {cache, cachedArtifact} from './cached.js';
|
|
38
|
-
import {extrudeWithTopology} from './extrude.js';
|
|
41
|
+
import {extrudeWithTopology, revolveWithTopology} from './extrude.js';
|
|
39
42
|
import {font, googleFont, type Font} from './font.js';
|
|
40
43
|
import {
|
|
41
44
|
anchorAnnotation,
|
|
@@ -66,7 +69,7 @@ import {
|
|
|
66
69
|
shapeSubshapes,
|
|
67
70
|
transformShape,
|
|
68
71
|
} from './kernel-shapes.js';
|
|
69
|
-
import {loftWithTopology} from './loft.js';
|
|
72
|
+
import {loftWithTopology, sweepWithTopology} from './loft.js';
|
|
70
73
|
import {captureModelMaterial, type ModelMaterialSnapshot} from './material.js';
|
|
71
74
|
import {
|
|
72
75
|
axisRotation,
|
|
@@ -277,6 +280,8 @@ export type ModelOperationKind =
|
|
|
277
280
|
| 'sketchFace'
|
|
278
281
|
| 'text'
|
|
279
282
|
| 'extrude'
|
|
283
|
+
| 'revolve'
|
|
284
|
+
| 'sweep'
|
|
280
285
|
| 'primitive'
|
|
281
286
|
| 'material'
|
|
282
287
|
| 'scaled'
|
|
@@ -851,8 +856,33 @@ export interface Vertex
|
|
|
851
856
|
readonly id: VertexId;
|
|
852
857
|
}
|
|
853
858
|
|
|
859
|
+
interface LengthMeasurement {
|
|
860
|
+
/** Actual arc length of this finite edge.
|
|
861
|
+
* @code3d.inspect inspectLength
|
|
862
|
+
*/
|
|
863
|
+
readonly length: number;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
interface AreaMeasurement {
|
|
867
|
+
/** Finite surface area; solids include all boundary faces.
|
|
868
|
+
* @code3d.inspect inspectArea
|
|
869
|
+
*/
|
|
870
|
+
readonly area: number;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
interface VolumeMeasurement {
|
|
874
|
+
/** Volume occupied by the solid, excluding cavities.
|
|
875
|
+
* @code3d.inspect inspectVolume
|
|
876
|
+
*/
|
|
877
|
+
readonly volume: number;
|
|
878
|
+
}
|
|
879
|
+
|
|
854
880
|
export interface Edge
|
|
855
|
-
extends
|
|
881
|
+
extends
|
|
882
|
+
LineAnchor,
|
|
883
|
+
GeometryQueryCapabilities,
|
|
884
|
+
EdgeTopologyCapabilities,
|
|
885
|
+
LengthMeasurement {
|
|
856
886
|
readonly kind: 'edge';
|
|
857
887
|
readonly id: EdgeId;
|
|
858
888
|
readonly start: PointAnchor;
|
|
@@ -861,7 +891,11 @@ export interface Edge
|
|
|
861
891
|
}
|
|
862
892
|
|
|
863
893
|
export interface Surface
|
|
864
|
-
extends
|
|
894
|
+
extends
|
|
895
|
+
FaceAnchor,
|
|
896
|
+
GeometryQueryCapabilities,
|
|
897
|
+
SurfaceTopologyCapabilities,
|
|
898
|
+
AreaMeasurement {
|
|
865
899
|
readonly kind: 'surface';
|
|
866
900
|
readonly id: SurfaceId;
|
|
867
901
|
}
|
|
@@ -870,7 +904,9 @@ export interface Solid
|
|
|
870
904
|
extends
|
|
871
905
|
Anchor<'frame'>,
|
|
872
906
|
GeometryQueryCapabilities,
|
|
873
|
-
SurfaceTopologyCapabilities
|
|
907
|
+
SurfaceTopologyCapabilities,
|
|
908
|
+
AreaMeasurement,
|
|
909
|
+
VolumeMeasurement {
|
|
874
910
|
readonly kind: 'solid';
|
|
875
911
|
}
|
|
876
912
|
|
|
@@ -1099,12 +1135,14 @@ export type VertexModel<Elements extends NamedElements = {}> =
|
|
|
1099
1135
|
export type EdgeModel<Elements extends NamedElements = CurveElements> =
|
|
1100
1136
|
ModelCapabilities<Elements, 'edge'> &
|
|
1101
1137
|
GeometryCapabilities<Elements, 'edge'> &
|
|
1102
|
-
EdgeTopologyCapabilities &
|
|
1138
|
+
EdgeTopologyCapabilities &
|
|
1139
|
+
LengthMeasurement & {reverse(): Edge} & Elements;
|
|
1103
1140
|
|
|
1104
1141
|
export type FaceModel<Elements extends NamedElements = PlanarElements> =
|
|
1105
1142
|
ModelCapabilities<Elements, 'face'> &
|
|
1106
1143
|
GeometryCapabilities<Elements, 'face'> &
|
|
1107
|
-
SurfaceTopologyCapabilities &
|
|
1144
|
+
SurfaceTopologyCapabilities &
|
|
1145
|
+
AreaMeasurement & {
|
|
1108
1146
|
flip(): Surface;
|
|
1109
1147
|
/**
|
|
1110
1148
|
* Extrudes along the face's local plane normal. Signed distance; no recentering.
|
|
@@ -1113,13 +1151,35 @@ export type FaceModel<Elements extends NamedElements = PlanarElements> =
|
|
|
1113
1151
|
* @code3d.param distance {kind: 'length', default: 10, label: 'Extrusion distance'}
|
|
1114
1152
|
*/
|
|
1115
1153
|
extrude(distance: number): SolidModel;
|
|
1154
|
+
/**
|
|
1155
|
+
* Rotate this face about a straight directed axis.
|
|
1156
|
+
* @code3d.inspect axis revolve.inspectMethodAxis
|
|
1157
|
+
* @code3d.inspect config revolve.inspectMethodProfile
|
|
1158
|
+
* @code3d.param config.angle {kind: 'angle', default: 360, label: 'Revolution angle'}
|
|
1159
|
+
* @code3d.param config.advance {kind: 'length', default: 0, label: 'Axial advance'}
|
|
1160
|
+
*/
|
|
1161
|
+
revolve(axis: LineAnchor, config: RevolveConfig): SolidModel;
|
|
1162
|
+
/**
|
|
1163
|
+
* Sweep this face along an open curve beginning at its local origin.
|
|
1164
|
+
* @code3d.inspect this sweep.inspectMethodProfile
|
|
1165
|
+
* @code3d.inspect spine sweep.inspectMethodSpine
|
|
1166
|
+
*/
|
|
1167
|
+
sweep(spine: EdgeModel<{}>): SolidModel;
|
|
1116
1168
|
} & Elements;
|
|
1117
1169
|
|
|
1170
|
+
/** Core parameters for rotational and screw-motion solids. */
|
|
1171
|
+
export type RevolveConfig = Readonly<{
|
|
1172
|
+
angle: number;
|
|
1173
|
+
advance?: number;
|
|
1174
|
+
}>;
|
|
1175
|
+
|
|
1118
1176
|
export type SolidModel<Elements extends NamedElements = CanonicalElements> =
|
|
1119
1177
|
ModelCapabilities<Elements, 'solid'> &
|
|
1120
1178
|
GeometryCapabilities<Elements, 'solid'> &
|
|
1121
1179
|
SurfaceTopologyCapabilities &
|
|
1122
1180
|
SolidModificationCapabilities<Elements> &
|
|
1181
|
+
AreaMeasurement &
|
|
1182
|
+
VolumeMeasurement &
|
|
1123
1183
|
Elements;
|
|
1124
1184
|
|
|
1125
1185
|
type RuntimeModel<
|
|
@@ -1261,6 +1321,21 @@ class ModelTopologyElement extends ModelAnchor {
|
|
|
1261
1321
|
);
|
|
1262
1322
|
}
|
|
1263
1323
|
|
|
1324
|
+
/** @code3d.inspect inspectLength */
|
|
1325
|
+
get length(): number {
|
|
1326
|
+
return measureFiniteGeometry(this, 'length');
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/** @code3d.inspect inspectArea */
|
|
1330
|
+
get area(): number {
|
|
1331
|
+
return measureFiniteGeometry(this, 'area');
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
/** @code3d.inspect inspectVolume */
|
|
1335
|
+
get volume(): number {
|
|
1336
|
+
return measureFiniteGeometry(this, 'volume');
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1264
1339
|
get start(): PointAnchor {
|
|
1265
1340
|
return this.#curvePoint('start', 0);
|
|
1266
1341
|
}
|
|
@@ -3037,6 +3112,21 @@ export class ModelObject<
|
|
|
3037
3112
|
return new ModelObject<Elements, Kind>(init);
|
|
3038
3113
|
}
|
|
3039
3114
|
|
|
3115
|
+
/** @code3d.inspect inspectLength */
|
|
3116
|
+
get length(): number {
|
|
3117
|
+
return measureFiniteGeometry(this, 'length');
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
/** @code3d.inspect inspectArea */
|
|
3121
|
+
get area(): number {
|
|
3122
|
+
return measureFiniteGeometry(this, 'area');
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
/** @code3d.inspect inspectVolume */
|
|
3126
|
+
get volume(): number {
|
|
3127
|
+
return measureFiniteGeometry(this, 'volume');
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3040
3130
|
get origin(): PointAnchor {
|
|
3041
3131
|
return this.frame.origin;
|
|
3042
3132
|
}
|
|
@@ -3584,6 +3674,145 @@ export class ModelObject<
|
|
|
3584
3674
|
return result as unknown as SolidModel;
|
|
3585
3675
|
}
|
|
3586
3676
|
|
|
3677
|
+
revolve(
|
|
3678
|
+
this: ModelObject<Elements, 'face'>,
|
|
3679
|
+
axis: LineAnchor,
|
|
3680
|
+
config: RevolveConfig = {angle: 360},
|
|
3681
|
+
): SolidModel {
|
|
3682
|
+
if (this.kind !== 'face')
|
|
3683
|
+
throw new Error('revolve requires a single face model.');
|
|
3684
|
+
const {angle = 360, advance = 0} = config;
|
|
3685
|
+
if (!Number.isFinite(angle) || angle === 0)
|
|
3686
|
+
throw new Error('Revolution angle must be finite and non-zero.');
|
|
3687
|
+
if (!Number.isFinite(advance))
|
|
3688
|
+
throw new Error('Revolution advance must be finite.');
|
|
3689
|
+
if (advance === 0 && Math.abs(angle) > 360)
|
|
3690
|
+
throw new Error('A revolution without advance cannot exceed one turn.');
|
|
3691
|
+
const axisReference = straightAxisReference(axis, 'revolve() axis');
|
|
3692
|
+
const context = ModelObject.createSolveContext([this, axisReference.model]);
|
|
3693
|
+
const facePose = this.solvePose(context);
|
|
3694
|
+
const axisFrame = relativeTransform(
|
|
3695
|
+
composeTransforms(
|
|
3696
|
+
axisReference.model.solvePose(context),
|
|
3697
|
+
axisReference.transform,
|
|
3698
|
+
),
|
|
3699
|
+
facePose,
|
|
3700
|
+
);
|
|
3701
|
+
const direction = rotateVector(
|
|
3702
|
+
[0, axisReference.direction ?? 1, 0],
|
|
3703
|
+
axisFrame.quaternion,
|
|
3704
|
+
);
|
|
3705
|
+
const source = this.requireGeometry();
|
|
3706
|
+
const axisModel = isModelObject(axisReference.model)
|
|
3707
|
+
? axisReference.model
|
|
3708
|
+
: undefined;
|
|
3709
|
+
ModelObject.recordCompositionInspection(context, []);
|
|
3710
|
+
const geometry = evaluateSolidGeometry(
|
|
3711
|
+
'revolve',
|
|
3712
|
+
[axisFrame.position, direction, angle, advance],
|
|
3713
|
+
[source],
|
|
3714
|
+
() =>
|
|
3715
|
+
revolveWithTopology(
|
|
3716
|
+
{
|
|
3717
|
+
shape: source.value.shape,
|
|
3718
|
+
topology: source.value.topology,
|
|
3719
|
+
namespace: 1,
|
|
3720
|
+
},
|
|
3721
|
+
axisFrame.position,
|
|
3722
|
+
direction,
|
|
3723
|
+
angle,
|
|
3724
|
+
advance,
|
|
3725
|
+
),
|
|
3726
|
+
);
|
|
3727
|
+
const result = ModelObject.create<CanonicalElements, 'solid'>({
|
|
3728
|
+
kind: 'solid',
|
|
3729
|
+
name: 'Revolve',
|
|
3730
|
+
geometry,
|
|
3731
|
+
material: this.materialSnapshot,
|
|
3732
|
+
placements: this.placements,
|
|
3733
|
+
sourceRefs: [...this.sourceRefs, ...(axisModel?.sourceRefs ?? [])],
|
|
3734
|
+
parameters: uniqueParameters([
|
|
3735
|
+
...this.allParameters(),
|
|
3736
|
+
...(axisModel?.allParameters() ?? []),
|
|
3737
|
+
]),
|
|
3738
|
+
meshTolerance: this.meshTolerance,
|
|
3739
|
+
operation: storedOperation('revolve', [
|
|
3740
|
+
{model: this, role: 'receiver', index: 0},
|
|
3741
|
+
...(axisModel
|
|
3742
|
+
? [{model: axisModel, role: 'reference' as const, index: 1}]
|
|
3743
|
+
: []),
|
|
3744
|
+
]),
|
|
3745
|
+
});
|
|
3746
|
+
ModelObject.recordCompositionInspection(context, [[result, this]]);
|
|
3747
|
+
return result as unknown as SolidModel;
|
|
3748
|
+
}
|
|
3749
|
+
|
|
3750
|
+
sweep(this: ModelObject<Elements, 'face'>, spine: EdgeModel<{}>): SolidModel {
|
|
3751
|
+
if (this.kind !== 'face')
|
|
3752
|
+
throw new Error('sweep requires a single face model.');
|
|
3753
|
+
const path = requireModelKind(
|
|
3754
|
+
spine,
|
|
3755
|
+
'edge',
|
|
3756
|
+
'sweep requires an edge model as its spine.',
|
|
3757
|
+
);
|
|
3758
|
+
const context = ModelObject.createSolveContext([this, path]);
|
|
3759
|
+
const profilePose = this.solvePose(context);
|
|
3760
|
+
const pathTransform = relativeTransform(
|
|
3761
|
+
path.solvePose(context),
|
|
3762
|
+
profilePose,
|
|
3763
|
+
);
|
|
3764
|
+
const source = this.requireGeometry();
|
|
3765
|
+
const pathGeometry = path.requireGeometry();
|
|
3766
|
+
const normal = rotateVector(
|
|
3767
|
+
[0, 1, 0],
|
|
3768
|
+
this.geometryAnchor.transform.quaternion,
|
|
3769
|
+
);
|
|
3770
|
+
ModelObject.recordCompositionInspection(context, []);
|
|
3771
|
+
const geometry = evaluateSolidGeometry(
|
|
3772
|
+
'sweep',
|
|
3773
|
+
[pathTransform.position, pathTransform.quaternion, normal],
|
|
3774
|
+
[source, pathGeometry],
|
|
3775
|
+
() => {
|
|
3776
|
+
const positionedPath = shapeWithTransform(
|
|
3777
|
+
pathGeometry.value.shape as ReplicadEdge,
|
|
3778
|
+
pathTransform,
|
|
3779
|
+
);
|
|
3780
|
+
try {
|
|
3781
|
+
return sweepWithTopology(
|
|
3782
|
+
{
|
|
3783
|
+
shape: source.value.shape,
|
|
3784
|
+
topology: source.value.topology,
|
|
3785
|
+
namespace: 1,
|
|
3786
|
+
},
|
|
3787
|
+
positionedPath,
|
|
3788
|
+
normal,
|
|
3789
|
+
);
|
|
3790
|
+
} finally {
|
|
3791
|
+
positionedPath.delete();
|
|
3792
|
+
}
|
|
3793
|
+
},
|
|
3794
|
+
);
|
|
3795
|
+
const result = ModelObject.create<CanonicalElements, 'solid'>({
|
|
3796
|
+
kind: 'solid',
|
|
3797
|
+
name: 'Sweep',
|
|
3798
|
+
geometry,
|
|
3799
|
+
material: this.materialSnapshot,
|
|
3800
|
+
placements: this.placements,
|
|
3801
|
+
sourceRefs: [...this.sourceRefs, ...path.sourceRefs],
|
|
3802
|
+
parameters: uniqueParameters([
|
|
3803
|
+
...this.allParameters(),
|
|
3804
|
+
...path.allParameters(),
|
|
3805
|
+
]),
|
|
3806
|
+
meshTolerance: Math.min(this.meshTolerance, path.meshTolerance),
|
|
3807
|
+
operation: storedOperation('sweep', [
|
|
3808
|
+
{model: this, role: 'receiver', index: 0},
|
|
3809
|
+
{model: path, role: 'spine', index: 1},
|
|
3810
|
+
]),
|
|
3811
|
+
});
|
|
3812
|
+
ModelObject.recordCompositionInspection(context, [[result, this]]);
|
|
3813
|
+
return result as unknown as SolidModel;
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3587
3816
|
cut(
|
|
3588
3817
|
this: ModelObject<Elements, 'solid'>,
|
|
3589
3818
|
tools: readonly SolidModel<{}>[],
|
|
@@ -4532,6 +4761,46 @@ export class ModelObject<
|
|
|
4532
4761
|
};
|
|
4533
4762
|
}
|
|
4534
4763
|
|
|
4764
|
+
/** @internal */
|
|
4765
|
+
static recordRevolve(
|
|
4766
|
+
profile: ModelObject,
|
|
4767
|
+
axis: LineAnchor,
|
|
4768
|
+
result?: ModelObject,
|
|
4769
|
+
): void {
|
|
4770
|
+
if (!isRecordingInspection()) return;
|
|
4771
|
+
const reference = straightAxisReference(axis, 'revolve() axis');
|
|
4772
|
+
const context = this.createSolveContext([profile, reference.model]);
|
|
4773
|
+
this.recordCompositionInspection(
|
|
4774
|
+
context,
|
|
4775
|
+
result ? [[result, profile]] : [],
|
|
4776
|
+
);
|
|
4777
|
+
}
|
|
4778
|
+
|
|
4779
|
+
/** @internal */
|
|
4780
|
+
static inspectRevolve(
|
|
4781
|
+
profile: FaceModel<{}>,
|
|
4782
|
+
axis: LineAnchor,
|
|
4783
|
+
result: SolidModel | undefined,
|
|
4784
|
+
data: CompositionInspectData,
|
|
4785
|
+
focus: 'profile' | 'axis',
|
|
4786
|
+
): InspectResult {
|
|
4787
|
+
const frame = this.inspectionFrame(data.poses);
|
|
4788
|
+
const shownProfile = frame.positioned(profile)!;
|
|
4789
|
+
const shownAxis = anchorAnnotation(frame.positioned(axis)! as LineAnchor, {
|
|
4790
|
+
direction: 'forward',
|
|
4791
|
+
});
|
|
4792
|
+
const shownResult = result ? frame.positioned(result)! : undefined;
|
|
4793
|
+
return focus === 'axis'
|
|
4794
|
+
? {
|
|
4795
|
+
ambient: [shownProfile, ...(shownResult ? [shownResult] : [])],
|
|
4796
|
+
target: [shownAxis],
|
|
4797
|
+
}
|
|
4798
|
+
: {
|
|
4799
|
+
ambient: [shownAxis, ...(shownResult ? [shownResult] : [])],
|
|
4800
|
+
target: [shownProfile],
|
|
4801
|
+
};
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4535
4804
|
/** @internal Derive the focused cut volume using the original solved operands. */
|
|
4536
4805
|
static inspectCutTools(
|
|
4537
4806
|
stock: SolidModel<{}>,
|
|
@@ -5820,6 +6089,136 @@ export namespace relate {
|
|
|
5820
6089
|
}
|
|
5821
6090
|
}
|
|
5822
6091
|
|
|
6092
|
+
type MeasurementInspectData = Readonly<{
|
|
6093
|
+
owner: Model;
|
|
6094
|
+
target: Model | Anchor;
|
|
6095
|
+
placement: DimensionSegment | Readonly<{at: Vec3}>;
|
|
6096
|
+
}>;
|
|
6097
|
+
|
|
6098
|
+
function measureFiniteGeometry(
|
|
6099
|
+
value: ModelObject | ModelTopologyElement,
|
|
6100
|
+
measure: 'length' | 'area' | 'volume',
|
|
6101
|
+
): number {
|
|
6102
|
+
const topology =
|
|
6103
|
+
value instanceof ModelTopologyElement
|
|
6104
|
+
? value[anchorReferenceValue].topology
|
|
6105
|
+
: undefined;
|
|
6106
|
+
const owner =
|
|
6107
|
+
value instanceof ModelObject ? value : value[anchorReferenceValue].model;
|
|
6108
|
+
const geometry = (topology?.source ?? (value as ModelObject))[
|
|
6109
|
+
modelGeometry
|
|
6110
|
+
]()!.value;
|
|
6111
|
+
const scale = topology?.scale ?? 1;
|
|
6112
|
+
const position = (point: Vec3): Vec3 =>
|
|
6113
|
+
topology ? topologyTransform(topology, translation(point)).position : point;
|
|
6114
|
+
const read = (shape: AnyShape): number => {
|
|
6115
|
+
const properties =
|
|
6116
|
+
measure === 'length'
|
|
6117
|
+
? measureShapeLinearProperties(shape)
|
|
6118
|
+
: measure === 'area'
|
|
6119
|
+
? measureShapeSurfaceProperties(shape as ReplicadFace | Shape3D)
|
|
6120
|
+
: measureShapeVolumeProperties(shape as Shape3D);
|
|
6121
|
+
try {
|
|
6122
|
+
const magnitude =
|
|
6123
|
+
'length' in properties
|
|
6124
|
+
? properties.length
|
|
6125
|
+
: 'area' in properties
|
|
6126
|
+
? properties.area
|
|
6127
|
+
: properties.volume;
|
|
6128
|
+
const power = measure === 'length' ? 1 : measure === 'area' ? 2 : 3;
|
|
6129
|
+
const result = magnitude * scale ** power;
|
|
6130
|
+
if (isRecordingInspection()) {
|
|
6131
|
+
const edge = shape as ReplicadEdge;
|
|
6132
|
+
const edgePoint = (parameter: number): Vec3 => {
|
|
6133
|
+
const point = edge.pointAt(parameter);
|
|
6134
|
+
try {
|
|
6135
|
+
return position(point.toTuple());
|
|
6136
|
+
} finally {
|
|
6137
|
+
point.delete();
|
|
6138
|
+
}
|
|
6139
|
+
};
|
|
6140
|
+
const placement =
|
|
6141
|
+
measure === 'length' && edge.geomType === 'LINE'
|
|
6142
|
+
? {start: edgePoint(0), end: edgePoint(1)}
|
|
6143
|
+
: {
|
|
6144
|
+
at:
|
|
6145
|
+
measure === 'length'
|
|
6146
|
+
? edgePoint(0.5)
|
|
6147
|
+
: position(properties.centerOfMass),
|
|
6148
|
+
};
|
|
6149
|
+
const target =
|
|
6150
|
+
value instanceof ModelObject
|
|
6151
|
+
? value.kind === 'edge'
|
|
6152
|
+
? value.edges()[0]
|
|
6153
|
+
: value.kind === 'face'
|
|
6154
|
+
? value.surfaces()[0]
|
|
6155
|
+
: value
|
|
6156
|
+
: value;
|
|
6157
|
+
captureInspectData({owner, target, placement});
|
|
6158
|
+
}
|
|
6159
|
+
return result;
|
|
6160
|
+
} finally {
|
|
6161
|
+
properties.delete();
|
|
6162
|
+
}
|
|
6163
|
+
};
|
|
6164
|
+
return topology
|
|
6165
|
+
? withTopologyShape(
|
|
6166
|
+
geometry.shape,
|
|
6167
|
+
geometry.topology,
|
|
6168
|
+
topology.selection,
|
|
6169
|
+
read,
|
|
6170
|
+
)
|
|
6171
|
+
: read(geometry.shape);
|
|
6172
|
+
}
|
|
6173
|
+
|
|
6174
|
+
/** @internal */
|
|
6175
|
+
export function inspectLength(
|
|
6176
|
+
_args: readonly [],
|
|
6177
|
+
context: InspectContext<number, unknown, MeasurementInspectData | undefined>,
|
|
6178
|
+
): InspectResult | undefined {
|
|
6179
|
+
return inspectMeasurement(context, 'length');
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
/** @internal */
|
|
6183
|
+
export function inspectArea(
|
|
6184
|
+
_args: readonly [],
|
|
6185
|
+
context: InspectContext<number, unknown, MeasurementInspectData | undefined>,
|
|
6186
|
+
): InspectResult | undefined {
|
|
6187
|
+
return inspectMeasurement(context, 'area');
|
|
6188
|
+
}
|
|
6189
|
+
|
|
6190
|
+
/** @internal */
|
|
6191
|
+
export function inspectVolume(
|
|
6192
|
+
_args: readonly [],
|
|
6193
|
+
context: InspectContext<number, unknown, MeasurementInspectData | undefined>,
|
|
6194
|
+
): InspectResult | undefined {
|
|
6195
|
+
return inspectMeasurement(context, 'volume');
|
|
6196
|
+
}
|
|
6197
|
+
|
|
6198
|
+
function inspectMeasurement(
|
|
6199
|
+
context: InspectContext<number, unknown, MeasurementInspectData | undefined>,
|
|
6200
|
+
kind: 'length' | 'area' | 'volume',
|
|
6201
|
+
): InspectResult | undefined {
|
|
6202
|
+
if (context.return === undefined || !context.data) return;
|
|
6203
|
+
const {owner, target, placement} = context.data;
|
|
6204
|
+
return {
|
|
6205
|
+
target: [
|
|
6206
|
+
target,
|
|
6207
|
+
dimension({
|
|
6208
|
+
owner,
|
|
6209
|
+
value: context.return,
|
|
6210
|
+
...placement,
|
|
6211
|
+
axisLabel:
|
|
6212
|
+
kind === 'length'
|
|
6213
|
+
? 'at' in placement
|
|
6214
|
+
? 'arc length'
|
|
6215
|
+
: undefined
|
|
6216
|
+
: kind,
|
|
6217
|
+
}),
|
|
6218
|
+
],
|
|
6219
|
+
};
|
|
6220
|
+
}
|
|
6221
|
+
|
|
5823
6222
|
/** @internal */
|
|
5824
6223
|
export function inspectTopologyReference(
|
|
5825
6224
|
_args: readonly unknown[],
|
|
@@ -5927,6 +6326,57 @@ export function extrude(
|
|
|
5927
6326
|
}
|
|
5928
6327
|
}
|
|
5929
6328
|
|
|
6329
|
+
/**
|
|
6330
|
+
* @code3d.inspect profile revolve.inspectProfile
|
|
6331
|
+
* @code3d.inspect axis revolve.inspectAxis
|
|
6332
|
+
* @code3d.inspect config revolve.inspectProfile
|
|
6333
|
+
* @code3d.param config.angle {kind: 'angle', default: 360, label: 'Revolution angle'}
|
|
6334
|
+
* @code3d.param config.advance {kind: 'length', default: 0, label: 'Axial advance'}
|
|
6335
|
+
*/
|
|
6336
|
+
export function revolve(
|
|
6337
|
+
profile: FaceModel<{}>,
|
|
6338
|
+
axis: LineAnchor,
|
|
6339
|
+
config: RevolveConfig,
|
|
6340
|
+
): SolidModel;
|
|
6341
|
+
export function revolve(
|
|
6342
|
+
profile: FaceModel<{}>,
|
|
6343
|
+
axis: LineAnchor,
|
|
6344
|
+
config: RevolveConfig = {angle: 360},
|
|
6345
|
+
): SolidModel {
|
|
6346
|
+
const runtimeProfile = requireModelKind(
|
|
6347
|
+
profile,
|
|
6348
|
+
'face',
|
|
6349
|
+
'revolve requires a face model.',
|
|
6350
|
+
);
|
|
6351
|
+
let result: SolidModel | undefined;
|
|
6352
|
+
try {
|
|
6353
|
+
result = runtimeProfile.revolve(axis, config);
|
|
6354
|
+
return result;
|
|
6355
|
+
} finally {
|
|
6356
|
+
ModelObject.recordRevolve(
|
|
6357
|
+
runtimeProfile,
|
|
6358
|
+
axis,
|
|
6359
|
+
result as ModelObject | undefined,
|
|
6360
|
+
);
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
|
|
6364
|
+
/**
|
|
6365
|
+
* Sweeps one planar profile along an open curve.
|
|
6366
|
+
* @code3d.inspect profile sweep.inspectProfile
|
|
6367
|
+
* @code3d.inspect spine sweep.inspectSpine
|
|
6368
|
+
*/
|
|
6369
|
+
export function sweep(
|
|
6370
|
+
profile: FaceModel<{}>,
|
|
6371
|
+
spine: EdgeModel<{}>,
|
|
6372
|
+
): SolidModel {
|
|
6373
|
+
return requireModelKind(
|
|
6374
|
+
profile,
|
|
6375
|
+
'face',
|
|
6376
|
+
'sweep requires a face model.',
|
|
6377
|
+
).sweep(spine);
|
|
6378
|
+
}
|
|
6379
|
+
|
|
5930
6380
|
/**
|
|
5931
6381
|
* Creates connected text faces on the XZ plane: +X right, -Z up, normal +Y.
|
|
5932
6382
|
* All faces share the baseline origin. Size is the font em in model units.
|
|
@@ -6474,6 +6924,8 @@ export const authoringApi = Object.freeze({
|
|
|
6474
6924
|
bezier,
|
|
6475
6925
|
spline,
|
|
6476
6926
|
loft,
|
|
6927
|
+
revolve,
|
|
6928
|
+
sweep,
|
|
6477
6929
|
box,
|
|
6478
6930
|
cylinder,
|
|
6479
6931
|
tube,
|
|
@@ -7504,6 +7956,146 @@ export namespace extrude {
|
|
|
7504
7956
|
}
|
|
7505
7957
|
}
|
|
7506
7958
|
|
|
7959
|
+
/** @internal */
|
|
7960
|
+
export namespace revolve {
|
|
7961
|
+
export function inspectProfile(
|
|
7962
|
+
[profile, axis]: [FaceModel<{}>, LineAnchor, RevolveConfig],
|
|
7963
|
+
context: InspectContext<
|
|
7964
|
+
SolidModel,
|
|
7965
|
+
unknown,
|
|
7966
|
+
CompositionInspectData | undefined
|
|
7967
|
+
>,
|
|
7968
|
+
): InspectResult | undefined {
|
|
7969
|
+
return (
|
|
7970
|
+
context.data &&
|
|
7971
|
+
ModelObject.inspectRevolve(
|
|
7972
|
+
profile,
|
|
7973
|
+
axis,
|
|
7974
|
+
context.return,
|
|
7975
|
+
context.data,
|
|
7976
|
+
'profile',
|
|
7977
|
+
)
|
|
7978
|
+
);
|
|
7979
|
+
}
|
|
7980
|
+
|
|
7981
|
+
export function inspectAxis(
|
|
7982
|
+
[profile, axis]: [FaceModel<{}>, LineAnchor, RevolveConfig],
|
|
7983
|
+
context: InspectContext<
|
|
7984
|
+
SolidModel,
|
|
7985
|
+
unknown,
|
|
7986
|
+
CompositionInspectData | undefined
|
|
7987
|
+
>,
|
|
7988
|
+
): InspectResult | undefined {
|
|
7989
|
+
return (
|
|
7990
|
+
context.data &&
|
|
7991
|
+
ModelObject.inspectRevolve(
|
|
7992
|
+
profile,
|
|
7993
|
+
axis,
|
|
7994
|
+
context.return,
|
|
7995
|
+
context.data,
|
|
7996
|
+
'axis',
|
|
7997
|
+
)
|
|
7998
|
+
);
|
|
7999
|
+
}
|
|
8000
|
+
|
|
8001
|
+
export function inspectMethodProfile(
|
|
8002
|
+
[axis]: [LineAnchor, RevolveConfig],
|
|
8003
|
+
context: InspectContext<
|
|
8004
|
+
SolidModel,
|
|
8005
|
+
FaceModel<{}>,
|
|
8006
|
+
CompositionInspectData | undefined
|
|
8007
|
+
>,
|
|
8008
|
+
): InspectResult | undefined {
|
|
8009
|
+
return (
|
|
8010
|
+
context.data &&
|
|
8011
|
+
ModelObject.inspectRevolve(
|
|
8012
|
+
context.receiver,
|
|
8013
|
+
axis,
|
|
8014
|
+
context.return,
|
|
8015
|
+
context.data,
|
|
8016
|
+
'profile',
|
|
8017
|
+
)
|
|
8018
|
+
);
|
|
8019
|
+
}
|
|
8020
|
+
|
|
8021
|
+
export function inspectMethodAxis(
|
|
8022
|
+
[axis]: [LineAnchor, RevolveConfig],
|
|
8023
|
+
context: InspectContext<
|
|
8024
|
+
SolidModel,
|
|
8025
|
+
FaceModel<{}>,
|
|
8026
|
+
CompositionInspectData | undefined
|
|
8027
|
+
>,
|
|
8028
|
+
): InspectResult | undefined {
|
|
8029
|
+
return (
|
|
8030
|
+
context.data &&
|
|
8031
|
+
ModelObject.inspectRevolve(
|
|
8032
|
+
context.receiver,
|
|
8033
|
+
axis,
|
|
8034
|
+
context.return,
|
|
8035
|
+
context.data,
|
|
8036
|
+
'axis',
|
|
8037
|
+
)
|
|
8038
|
+
);
|
|
8039
|
+
}
|
|
8040
|
+
}
|
|
8041
|
+
|
|
8042
|
+
/** @internal */
|
|
8043
|
+
export namespace sweep {
|
|
8044
|
+
export function inspectProfile(
|
|
8045
|
+
[profile, spine]: [FaceModel<{}>, EdgeModel<{}>],
|
|
8046
|
+
context: InspectContext<
|
|
8047
|
+
SolidModel,
|
|
8048
|
+
unknown,
|
|
8049
|
+
CompositionInspectData | undefined
|
|
8050
|
+
>,
|
|
8051
|
+
): InspectResult | undefined {
|
|
8052
|
+
if (!context.data) return undefined;
|
|
8053
|
+
return ModelObject.inspectComposition(
|
|
8054
|
+
context.data,
|
|
8055
|
+
[spine, ...(context.return ? [context.return] : [])],
|
|
8056
|
+
[profile],
|
|
8057
|
+
);
|
|
8058
|
+
}
|
|
8059
|
+
|
|
8060
|
+
export function inspectSpine(
|
|
8061
|
+
[profile, spine]: [FaceModel<{}>, EdgeModel<{}>],
|
|
8062
|
+
context: InspectContext<
|
|
8063
|
+
SolidModel,
|
|
8064
|
+
unknown,
|
|
8065
|
+
CompositionInspectData | undefined
|
|
8066
|
+
>,
|
|
8067
|
+
): InspectResult | undefined {
|
|
8068
|
+
if (!context.data) return undefined;
|
|
8069
|
+
return ModelObject.inspectComposition(
|
|
8070
|
+
context.data,
|
|
8071
|
+
[profile, ...(context.return ? [context.return] : [])],
|
|
8072
|
+
[spine],
|
|
8073
|
+
);
|
|
8074
|
+
}
|
|
8075
|
+
|
|
8076
|
+
export function inspectMethodProfile(
|
|
8077
|
+
[spine]: [EdgeModel<{}>],
|
|
8078
|
+
context: InspectContext<
|
|
8079
|
+
SolidModel,
|
|
8080
|
+
FaceModel<{}>,
|
|
8081
|
+
CompositionInspectData | undefined
|
|
8082
|
+
>,
|
|
8083
|
+
): InspectResult | undefined {
|
|
8084
|
+
return inspectProfile([context.receiver, spine], context);
|
|
8085
|
+
}
|
|
8086
|
+
|
|
8087
|
+
export function inspectMethodSpine(
|
|
8088
|
+
[spine]: [EdgeModel<{}>],
|
|
8089
|
+
context: InspectContext<
|
|
8090
|
+
SolidModel,
|
|
8091
|
+
FaceModel<{}>,
|
|
8092
|
+
CompositionInspectData | undefined
|
|
8093
|
+
>,
|
|
8094
|
+
): InspectResult | undefined {
|
|
8095
|
+
return inspectSpine([context.receiver, spine], context);
|
|
8096
|
+
}
|
|
8097
|
+
}
|
|
8098
|
+
|
|
7507
8099
|
/** @internal */
|
|
7508
8100
|
export namespace union {
|
|
7509
8101
|
export function inspectOperands(
|