@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.
Files changed (36) hide show
  1. package/README.md +18 -13
  2. package/bld/chunks/{chunk-JBVIAMJ6.js → chunk-CQKOD54C.js} +552 -25
  3. package/bld/chunks/chunk-CQKOD54C.js.map +7 -0
  4. package/bld/chunks/{chunk-CL3E2DE4.js → chunk-JF6QRICU.js} +2 -2
  5. package/bld/chunks/{chunk-HZRQUHM2.js → chunk-L4V6X7RH.js} +2 -2
  6. package/bld/chunks/{chunk-QK6ZET47.js → chunk-QJVTVAYM.js} +3 -3
  7. package/bld/library/extrude.d.ts +5 -0
  8. package/bld/library/extrude.d.ts.map +1 -1
  9. package/bld/library/index.d.ts +2 -2
  10. package/bld/library/index.d.ts.map +1 -1
  11. package/bld/library/index.js +11 -1
  12. package/bld/library/inspect.d.ts +3 -1
  13. package/bld/library/inspect.d.ts.map +1 -1
  14. package/bld/library/loft.d.ts +7 -1
  15. package/bld/library/loft.d.ts.map +1 -1
  16. package/bld/library/replicad.js +2 -2
  17. package/bld/library/runtime.d.ts +68 -7
  18. package/bld/library/runtime.d.ts.map +1 -1
  19. package/bld/node/index.js +13 -3
  20. package/bld/node/replicad.js +4 -4
  21. package/bld/tooling/index.js +2 -2
  22. package/docs/api.md +134 -13
  23. package/docs/local-coordinates.md +2 -0
  24. package/docs/runtime.md +42 -2
  25. package/docs/topology.md +6 -0
  26. package/docs/values.md +9 -0
  27. package/package.json +1 -1
  28. package/src/library/extrude.ts +129 -2
  29. package/src/library/index.ts +6 -0
  30. package/src/library/inspect.ts +4 -2
  31. package/src/library/loft.ts +67 -2
  32. package/src/library/runtime.ts +599 -7
  33. package/bld/chunks/chunk-JBVIAMJ6.js.map +0 -7
  34. /package/bld/chunks/{chunk-CL3E2DE4.js.map → chunk-JF6QRICU.js.map} +0 -0
  35. /package/bld/chunks/{chunk-HZRQUHM2.js.map → chunk-L4V6X7RH.js.map} +0 -0
  36. /package/bld/chunks/{chunk-QK6ZET47.js.map → chunk-QJVTVAYM.js.map} +0 -0
@@ -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 LineAnchor, GeometryQueryCapabilities, EdgeTopologyCapabilities {
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 FaceAnchor, GeometryQueryCapabilities, SurfaceTopologyCapabilities {
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 & {reverse(): Edge} & Elements;
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(