@code3d/core 0.0.1-alpha.0 → 0.0.1-alpha.10

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 (154) hide show
  1. package/README.md +43 -807
  2. package/THIRD_PARTY.md +40 -0
  3. package/bld/THIRD_PARTY_NOTICES.txt +182 -0
  4. package/bld/chunks/chunk-5IP37Z5U.js +30 -0
  5. package/bld/chunks/chunk-5IP37Z5U.js.map +7 -0
  6. package/bld/chunks/chunk-7FIFPADJ.js +1 -0
  7. package/bld/chunks/chunk-7FIFPADJ.js.map +7 -0
  8. package/bld/chunks/chunk-HBRLIGQN.js +31 -0
  9. package/bld/chunks/chunk-HBRLIGQN.js.map +7 -0
  10. package/bld/chunks/chunk-IJHQ57VL.js +19 -0
  11. package/bld/chunks/chunk-IJHQ57VL.js.map +7 -0
  12. package/bld/chunks/chunk-TTJOM4DJ.js +26458 -0
  13. package/bld/chunks/chunk-TTJOM4DJ.js.map +7 -0
  14. package/bld/library/bound-solver.d.ts +29 -5
  15. package/bld/library/bound-solver.d.ts.map +1 -1
  16. package/bld/library/cached.d.ts +35 -0
  17. package/bld/library/cached.d.ts.map +1 -0
  18. package/bld/library/font.d.ts +39 -0
  19. package/bld/library/font.d.ts.map +1 -0
  20. package/bld/library/google-font.d.ts +14 -0
  21. package/bld/library/google-font.d.ts.map +1 -0
  22. package/bld/library/index.d.ts +10 -2
  23. package/bld/library/index.d.ts.map +1 -1
  24. package/bld/library/index.js +94 -3
  25. package/bld/library/index.js.map +7 -1
  26. package/bld/library/inspect.d.ts +98 -0
  27. package/bld/library/inspect.d.ts.map +1 -0
  28. package/bld/library/kernel-artifact-codec.d.ts.map +1 -1
  29. package/bld/library/kernel-cache.d.ts +28 -7
  30. package/bld/library/kernel-cache.d.ts.map +1 -1
  31. package/bld/library/loft.d.ts.map +1 -1
  32. package/bld/library/material.d.ts +1 -1
  33. package/bld/library/material.d.ts.map +1 -1
  34. package/bld/library/open-cascade.d.ts +4 -0
  35. package/bld/library/open-cascade.d.ts.map +1 -0
  36. package/bld/library/relation-solver.d.ts +19 -8
  37. package/bld/library/relation-solver.d.ts.map +1 -1
  38. package/bld/library/replicad.d.ts +3 -2
  39. package/bld/library/replicad.d.ts.map +1 -1
  40. package/bld/library/replicad.js +10 -24
  41. package/bld/library/replicad.js.map +7 -1
  42. package/bld/library/retained-memory.d.ts.map +1 -1
  43. package/bld/library/runtime.d.ts +518 -155
  44. package/bld/library/runtime.d.ts.map +1 -1
  45. package/bld/library/sketch-solver.d.ts +1 -1
  46. package/bld/library/sketch-solver.d.ts.map +1 -1
  47. package/bld/library/sketch.d.ts +21 -1
  48. package/bld/library/sketch.d.ts.map +1 -1
  49. package/bld/library/text.d.ts +53 -0
  50. package/bld/library/text.d.ts.map +1 -0
  51. package/bld/library/three.js +3 -3
  52. package/bld/library/three.js.map +7 -1
  53. package/bld/library/topology.d.ts +6 -2
  54. package/bld/library/topology.d.ts.map +1 -1
  55. package/bld/node/index.d.ts.map +1 -1
  56. package/bld/node/index.js +96 -14
  57. package/bld/node/index.js.map +7 -1
  58. package/bld/node/replicad.js +13 -3
  59. package/bld/node/replicad.js.map +7 -1
  60. package/bld/tooling/index.d.ts +18 -15
  61. package/bld/tooling/index.d.ts.map +1 -1
  62. package/bld/tooling/index.js +165 -19
  63. package/bld/tooling/index.js.map +7 -1
  64. package/docs/api.md +764 -0
  65. package/docs/custom-primitives.mdx +58 -0
  66. package/docs/local-coordinates.md +187 -0
  67. package/docs/origins-and-rotation.mdx +105 -0
  68. package/docs/relations.mdx +450 -0
  69. package/docs/runtime.md +297 -0
  70. package/docs/shells.mdx +70 -0
  71. package/docs/sketches.md +83 -0
  72. package/docs/text.md +23 -0
  73. package/docs/topology.md +152 -0
  74. package/docs/values.md +119 -0
  75. package/package.json +20 -7
  76. package/src/library/bound-solver.ts +103 -73
  77. package/src/library/cached.ts +153 -0
  78. package/src/library/font.ts +186 -0
  79. package/src/library/google-font.ts +97 -0
  80. package/src/library/index.ts +53 -5
  81. package/src/library/inspect.ts +176 -0
  82. package/src/library/kernel-artifact-codec.ts +129 -20
  83. package/src/library/kernel-cache.ts +175 -39
  84. package/src/library/loft.ts +9 -1
  85. package/src/library/material.ts +3 -4
  86. package/src/library/open-cascade.ts +13 -0
  87. package/src/library/relation-solver.ts +230 -113
  88. package/src/library/replicad.ts +5 -5
  89. package/src/library/retained-memory.ts +6 -1
  90. package/src/library/runtime.ts +3753 -1181
  91. package/src/library/shell.ts +3 -3
  92. package/src/library/sketch-solver.ts +3 -1
  93. package/src/library/sketch.ts +112 -13
  94. package/src/library/text.ts +477 -0
  95. package/src/library/topology.ts +61 -17
  96. package/src/library/tsconfig.json +2 -1
  97. package/src/node/index.ts +14 -3
  98. package/src/node/tsconfig.json +2 -1
  99. package/src/tooling/index.ts +64 -51
  100. package/src/tooling/tsconfig.json +2 -1
  101. package/bld/library/alignment-geometry.js +0 -322
  102. package/bld/library/alignment-geometry.js.map +0 -1
  103. package/bld/library/bound-solver.js +0 -252
  104. package/bld/library/bound-solver.js.map +0 -1
  105. package/bld/library/extrude.js +0 -28
  106. package/bld/library/extrude.js.map +0 -1
  107. package/bld/library/kernel-artifact-codec.js +0 -148
  108. package/bld/library/kernel-artifact-codec.js.map +0 -1
  109. package/bld/library/kernel-cache.js +0 -245
  110. package/bld/library/kernel-cache.js.map +0 -1
  111. package/bld/library/kernel-shapes.js +0 -108
  112. package/bld/library/kernel-shapes.js.map +0 -1
  113. package/bld/library/loft.js +0 -161
  114. package/bld/library/loft.js.map +0 -1
  115. package/bld/library/material.js +0 -176
  116. package/bld/library/material.js.map +0 -1
  117. package/bld/library/model-color.js +0 -36
  118. package/bld/library/model-color.js.map +0 -1
  119. package/bld/library/open-cascade-error.js +0 -22
  120. package/bld/library/open-cascade-error.js.map +0 -1
  121. package/bld/library/relation-solver.js +0 -258
  122. package/bld/library/relation-solver.js.map +0 -1
  123. package/bld/library/retained-memory.js +0 -27
  124. package/bld/library/retained-memory.js.map +0 -1
  125. package/bld/library/runtime.js +0 -2954
  126. package/bld/library/runtime.js.map +0 -1
  127. package/bld/library/shell.js +0 -221
  128. package/bld/library/shell.js.map +0 -1
  129. package/bld/library/sketch-curve-intersections.js +0 -117
  130. package/bld/library/sketch-curve-intersections.js.map +0 -1
  131. package/bld/library/sketch-curves.js +0 -89
  132. package/bld/library/sketch-curves.js.map +0 -1
  133. package/bld/library/sketch-drag-rules.js +0 -522
  134. package/bld/library/sketch-drag-rules.js.map +0 -1
  135. package/bld/library/sketch-face.js +0 -72
  136. package/bld/library/sketch-face.js.map +0 -1
  137. package/bld/library/sketch-incidence.js +0 -114
  138. package/bld/library/sketch-incidence.js.map +0 -1
  139. package/bld/library/sketch-precision.js +0 -55
  140. package/bld/library/sketch-precision.js.map +0 -1
  141. package/bld/library/sketch-regions.js +0 -193
  142. package/bld/library/sketch-regions.js.map +0 -1
  143. package/bld/library/sketch-solver.js +0 -578
  144. package/bld/library/sketch-solver.js.map +0 -1
  145. package/bld/library/sketch.js +0 -650
  146. package/bld/library/sketch.js.map +0 -1
  147. package/bld/library/spatial.js +0 -195
  148. package/bld/library/spatial.js.map +0 -1
  149. package/bld/library/topology-id.js +0 -67
  150. package/bld/library/topology-id.js.map +0 -1
  151. package/bld/library/topology-inspection.js +0 -267
  152. package/bld/library/topology-inspection.js.map +0 -1
  153. package/bld/library/topology.js +0 -455
  154. package/bld/library/topology.js.map +0 -1
@@ -0,0 +1,297 @@
1
+ ---
2
+ title: Runtime and integration
3
+ description: Cache computations, integrate native materials and embed the modeling runtime.
4
+ sidebar:
5
+ order: 11
6
+ ---
7
+
8
+ ## Cached computations and custom primitives
9
+
10
+ `cache(fn)` memoizes synchronous, deterministic data computations;
11
+ `cache(fn, args)` immediately returns the cached result for an argument tuple.
12
+ Both forms share the same function identity and argument keys. Supply custom
13
+ codecs in the third argument: `cache(fn, undefined, options)` for a function or
14
+ `cache(fn, args, options)` for a value.
15
+ Pass changing captured state as arguments and treat returned data as immutable.
16
+ Memory hits reuse the retained result; optional `encoder` / `decoder` pairs only
17
+ run when saving to disk or restoring it. Newly computed entries are eligible
18
+ for disk storage when computation reaches the configured threshold (1 ms by
19
+ default). Faster results remain in memory and are not encoded or written on later
20
+ memory hits. Change the threshold in **Settings → Cache** in the App; it applies
21
+ to new computations and preserves existing cache entries. Existing
22
+ disk records can still be restored. The App fingerprints static definitions
23
+ and their dependencies for persistent reuse; dynamic closures and ordinary Node
24
+ calls use function identity for memory reuse. No author cache IDs are needed.
25
+
26
+ `definePrimitive(builder)` from `@code3d/core/replicad` also caches construction,
27
+ normalization and geometry analysis. Each call still creates fresh model metadata
28
+ and independently owned geometry handles. The builder transfers its returned
29
+ solid to Core and owns its intermediate resources. Screws uses this shared cache.
30
+ Read [cached computations](api.md#cached-computations)
31
+ and [custom primitives](custom-primitives.mdx)
32
+ for supported data, resource ownership and examples.
33
+
34
+ ## Materials and entry points
35
+
36
+ `.material()` accepts a color or a native Three.js material. Use
37
+ `@code3d/core/three` when constructing native materials, and
38
+ [@code3d/materials](../../materials/README.md) for common presets. A model captures
39
+ its material value; changing the original Three.js object later does not change
40
+ that model. The renderer supplies lighting and environment reflections.
41
+
42
+ | Import | Responsibility |
43
+ | ----------------------- | ------------------------------------------------------------------------ |
44
+ | `@code3d/core` | Public model authoring API; Node entry initializes the kernel |
45
+ | `@code3d/core/three` | Shared Three.js exports for material and geometry integration |
46
+ | `@code3d/core/replicad` | Replicad access for custom primitive builders |
47
+ | `@code3d/core/tooling` | Evaluation, inspection and resource lifetime integration used by the App |
48
+
49
+ Tooling integrations own evaluation lifetimes and disposal. Follow the existing
50
+ [tooling entry](../src/tooling/index.ts), [evaluation tests](../test/model-test.ts), and
51
+ [App compiler](../../app/src/model/compiler.ts) when embedding the runtime. Ordinary
52
+ model files should stay on the authoring API.
53
+
54
+ ## Source inspection
55
+
56
+ Ordinary expression values preview directly, including models, anchors and collections.
57
+ Model values retain their authored material opacity: an opaque color stays opaque,
58
+ explicit alpha stays unchanged, and unstyled surfaces keep the default translucency.
59
+ This also applies to ordinary call results and parameter fallbacks. Inspection
60
+ scenes apply the target/focused/ambient opacity limits; an anchor's owner remains
61
+ context behind the reference in both kinds of preview.
62
+ Use `@code3d.inspect parameter callback` when a parameter needs additional context;
63
+ use `@code3d.inspect callback` for an exceptional call-result view. A parameter
64
+ first tries its parameter inspector, then the call inspector, then the ordinary
65
+ call result. A declared closure body has its own scope: declining it continues
66
+ outward without re-entering that same call's parameter or call inspector. The callback
67
+ receives the original argument tuple and `InspectContext`; method receivers are
68
+ in `context.receiver`. Its `target` and `ambient` arrays own the complete scene.
69
+ Returning `undefined` declines the scope; returning `{}` intentionally displays
70
+ an empty scene. Target values matching `context.focused.values` receive focus;
71
+ generated geometry does not inherit focus from its inputs.
72
+
73
+ Selecting an array member focuses that value while keeping the other inspection
74
+ targets visible at a weaker level; selecting the whole array focuses its members.
75
+ For cut tools and intersect operands, selected inputs are targets and other inputs
76
+ are ambient. The generated cut volume (orange) or intersection (cyan) is a separate
77
+ target, including when inspecting a single input. A failed intersection still
78
+ shows the selected inputs and ambient operands without inventing a result.
79
+ These region inspectors use ordinary unlit materials with depth testing disabled,
80
+ so their colors remain visible through the translucent inputs.
81
+
82
+ Core uses this mechanism for distance measurements, relate calls and their
83
+ closures, on/align references, relative transformation stages, group children,
84
+ expose sources, Boolean operands and loft sections/spines, plus box and extrusion
85
+ dimensions. Selecting a normal constructor or Boolean function name
86
+ still previews its return value. Inspectors retain the original operation frame,
87
+ so a later relation or a different consumer cannot move its inspection.
88
+
89
+ A closure can independently declare a context factory with
90
+ `@code3d.inspect.context parameter callback` and a renderer with
91
+ `@code3d.inspect.closure parameter callback`. Context factories run lazily once
92
+ per actual execution. Inner inspectors can read `context.closure.data` and its
93
+ parent contexts regardless of which renderer is selected. The first non-undefined
94
+ inspector wins, searching from the selected expression outward.
95
+
96
+ For relate, context includes only actual consumed relation participants. Unrelated
97
+ values use ordinary preview. A collection is handled only when every member is
98
+ related; mixed collections fall through as a whole, preserving every previewable
99
+ member. Selecting an individual member tests that member independently.
100
+ Relative transformation functions and pivot/axis chains share a call inspector:
101
+ numeric and reference arguments inspect the consumed relation stage. Unconsumed
102
+ chains do not invent a stage. The tool can edit an ambient participant without
103
+ promoting its display tier.
104
+
105
+ Topology accessors (`vertex`, `edge`, `surface` and their plural forms) keep
106
+ ordinary anchor preview when they return references. Their inspector returns
107
+ the owner as `ambient` when the call fails or the reference collection is empty.
108
+ The owner therefore has the same background appearance before and after a
109
+ selection; missing or invalid IDs still produce their normal modeling errors.
110
+
111
+ ### Call data
112
+
113
+ The App runs JSDoc inspection callbacks when the corresponding source is selected.
114
+ Callbacks return `target` and `ambient` preview values. The viewport retains its
115
+ previous scene until the new inspection is ready; an inspection error preserves
116
+ that scene and is reported separately from model evaluation errors.
117
+
118
+ Models, anchors, sketches, sketch points and passive annotations share this scene.
119
+ Sketches show their points and curves at their actual 3D placement, including open
120
+ curves, inherited layers and multiple noncoplanar sketches. They do not need to
121
+ form a face. The App's **Edit sketch** tool opens the selected authored sketch in
122
+ its 2D plane; **Finish sketch** returns to the 3D preview. Inspection callbacks
123
+ control passive appearance; editing and selection remain tool responsibilities.
124
+
125
+ Use `captureInspectData(data)` inside a modeling function to retain facts from
126
+ that invocation for its inspector. The function keeps its ordinary return type.
127
+ `InspectContext<Return, Receiver, Data>` describes the callback's return value,
128
+ receiver and recorded data. For example, a scalar query can retain its bounds:
129
+
130
+ ```ts
131
+ import {
132
+ captureInspectData,
133
+ dimension,
134
+ type InspectContext,
135
+ type InspectResult,
136
+ type Model,
137
+ type Vec3,
138
+ } from '@code3d/core';
139
+
140
+ type SpanData = {owner: Model; minimum: Vec3; maximum: Vec3};
141
+
142
+ /** @code3d.inspect spanX.inspect */
143
+ export function spanX(owner: Model): number {
144
+ const {minimum, maximum} = owner.bounds();
145
+ captureInspectData({owner, minimum, maximum} satisfies SpanData);
146
+ return maximum[0] - minimum[0];
147
+ }
148
+
149
+ /** @internal */
150
+ export namespace spanX {
151
+ export function inspect(
152
+ _args: [Model],
153
+ context: InspectContext<number, unknown, SpanData | undefined>,
154
+ ): InspectResult | undefined {
155
+ const data = context.data;
156
+ if (!data || context.return === undefined) return undefined;
157
+ return {
158
+ target: [
159
+ data.owner,
160
+ dimension({
161
+ owner: data.owner,
162
+ start: data.minimum,
163
+ end: [data.maximum[0], data.minimum[1], data.minimum[2]],
164
+ value: context.return,
165
+ }),
166
+ ],
167
+ };
168
+ }
169
+ }
170
+ ```
171
+
172
+ Data belongs to the actual call, including nested calls with equal return values.
173
+ Without a record, `context.data` is `undefined`; multiple records in one call use
174
+ the last value. Closure context factories can read their owning call's data from
175
+ `execution.call.data`. The executor retains references without cloning them, so
176
+ capture immutable facts when later changes must not alter the inspection.
177
+ Inspectors do not run the modeling function again.
178
+
179
+ A modeling function that throws can still be inspected if it was actually invoked.
180
+ Its original evaluated arguments and last captured data remain available, with
181
+ `context.return === undefined`. Argument evaluation failures and optional-chain
182
+ short circuits do not create an invocation of the outer function. Entered closure
183
+ callbacks follow the same rule. The original modeling diagnostic remains separate
184
+ from any inspection error; inspectors decide what to render when no result exists.
185
+
186
+ Without an inspection recording session, `captureInspectData` does nothing.
187
+ Calls from an inspector also do not overwrite modeling records. Published
188
+ callbacks use normal runtime exports; `@internal` with TypeScript's
189
+ `stripInternal` can hide their declarations while keeping those exports.
190
+
191
+ A dimension can provide fixed `start`/`end` points or a nonempty `candidates` list.
192
+ All points use the owner's local frame. For candidates, the renderer picks the
193
+ nearest segment when inspection begins, then retains it while orbiting or
194
+ rechecking the same parameter. Leaving that inspection resets the choice.
195
+ Both forms show a number, endpoint ticks and a screen-sized dashed line.
196
+
197
+ ```ts
198
+ import {dimension, type Model} from '@code3d/core';
199
+
200
+ function showLength(owner: Model) {
201
+ return {
202
+ target: [
203
+ owner,
204
+ dimension({
205
+ owner,
206
+ value: 10,
207
+ candidates: [
208
+ {start: [-5, -2, -3], end: [5, -2, -3]},
209
+ {start: [-5, 2, 3], end: [5, 2, 3]},
210
+ ],
211
+ }),
212
+ ],
213
+ };
214
+ }
215
+ ```
216
+
217
+ `boundsAnnotation` represents a finite range without adding CAD geometry. Its
218
+ `frame` is relative to `owner`, centered on the box; `size` measures its three
219
+ local axes. The App draws the existing bounds corners at a fixed screen width.
220
+ It can represent the exact support range of an `on` relation, including flat or
221
+ linear ranges, instead of substituting the whole owner's bounding box.
222
+
223
+ ```ts
224
+ import {boundsAnnotation, type Model} from '@code3d/core';
225
+
226
+ function showRange(owner: Model) {
227
+ return {
228
+ target: [
229
+ owner,
230
+ boundsAnnotation({
231
+ owner,
232
+ size: [20, 10, 5],
233
+ frame: {position: [0, 0, 0], quaternion: [0, 0, 0, 1]},
234
+ }),
235
+ ],
236
+ };
237
+ }
238
+ ```
239
+
240
+ Use `anchorAnnotation(reference, {direction})` to choose `'none'`, `'forward'`
241
+ or `'both'` for direction arrows while keeping the reference's geometry and
242
+ placement. For curves, arrows follow the real endpoint tangents; reversing the
243
+ reference reverses its authored direction. The annotation retains the reference's
244
+ focus identity. Returning an ordinary Anchor keeps its default object preview.
245
+
246
+ ```ts
247
+ import {anchorAnnotation, type SolidModel} from '@code3d/core';
248
+
249
+ function showAxis(owner: SolidModel) {
250
+ return {
251
+ ambient: [owner],
252
+ target: [anchorAnnotation(owner.axis, {direction: 'forward'})],
253
+ };
254
+ }
255
+ ```
256
+
257
+ ## Source and development
258
+
259
+ For changes to Core itself, start with the [modeling architecture](../../../.agents/docs/architecture/modeling.md)
260
+ and shared [development guide](../../../.agents/docs/development.md), then follow
261
+ the implementation and tests below.
262
+
263
+ - [Public exports](../src/library/index.ts), [model runtime](../src/library/runtime.ts),
264
+ and [public type tests](../test/public-types.ts).
265
+ - [Spatial values](../src/library/spatial.ts), [relation solving](../src/library/relation-solver.ts),
266
+ and [topology](../src/library/topology.ts).
267
+ - [Cached computations](../src/library/cached.ts), [fonts](../src/library/font.ts),
268
+ [text geometry](../src/library/text.ts) and their [tests](../test).
269
+ - [Material values](../src/library/material.ts), [kernel cache](../src/library/kernel-cache.ts),
270
+ and [Node entry](../src/node/index.ts).
271
+ - [Executable App examples](../../app/examples) and [runtime tests](../test).
272
+
273
+ Public JavaScript entries are prebundled ESM with shared chunks. Node, browser,
274
+ tooling and interop entries share the same kernel and cache instances. TypeScript
275
+ declarations, declaration maps and their sources remain available for editor
276
+ navigation. The build and npm `prepack` use the same package build script; see the
277
+ [development guide](../../../.agents/docs/development.md#公开包产物) for installed
278
+ tarball verification and CI publishing.
279
+
280
+ For a standalone TypeScript project, include `ESNext` and `DOM` in `compilerOptions.lib`.
281
+ Use `module: "ESNext"` and `moduleResolution: "Bundler"` when esbuild or another
282
+ bundler handles execution. Code3D's App uses this resolution mode, supports
283
+ extensionless relative imports and selects browser package exports.
284
+ The public packages are built and verified with `skipLibCheck: false`. NodeNext
285
+ currently needs `skipLibCheck` because the `manifold-3d@3.0.1` declarations omit
286
+ relative `.js` extensions. Core includes the declaration dependencies needed by
287
+ its HarfBuzz and Replicad integrations.
288
+
289
+ From the repository root:
290
+
291
+ ```sh
292
+ npm run build:packages
293
+ npm test --workspace @code3d/core
294
+ ```
295
+
296
+ Use the [agent entry](../../../docs/agents.md) to work on a project through the CLI,
297
+ or the [App README](../../app/README.md) to develop the editor and visualization.
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: Making hollow parts
3
+ description: Create uniform walls and choose openings directly on a solid.
4
+ sidebar:
5
+ order: 8
6
+ ---
7
+
8
+ import ModelExample from '../../web/src/components/ModelExample.astro';
9
+
10
+ Start with a solid that describes the outside of your part. Use
11
+ `shell(thickness, removedSurfaceIds?)` to hollow it and choose which faces
12
+ become openings.
13
+
14
+ <ModelExample id="shell" />
15
+
16
+ ## Set the wall thickness
17
+
18
+ Positive thickness offsets inward, preserving the outside dimensions.
19
+ The example keeps its 40 × 24 × 30 outside size with walls 1.5 units thick.
20
+ Negative thickness offsets outward, keeping the original boundary on the
21
+ inside; neighboring offset faces use rounded joins where they separate.
22
+
23
+ Place the cursor inside `shell(1.5, [4])` to edit **Wall thickness** in the
24
+ panel. Thickness must be finite and nonzero.
25
+
26
+ ## Pick the openings
27
+
28
+ S4 is the input box's +Y face. In the **Openings** tool, click another face to
29
+ open it, or click a selected face again to restore the wall. The viewport
30
+ keeps the input faces available while showing the hollow result, so a removed
31
+ face can still be selected.
32
+
33
+ Selections write back immediately. **Close all openings** removes the surface
34
+ array. Omitting it, or passing `[]`, creates a sealed cavity:
35
+
36
+ ```ts
37
+ const sealed = box(40, 24, 30).shell(1.5);
38
+ ```
39
+
40
+ At least one face must remain as a wall. Use Undo to revert a committed
41
+ selection; moving the source cursor out of the call leaves the tool.
42
+
43
+ ## Select faces from the input
44
+
45
+ An opening ID belongs to the solid before `shell`. For a two-section loft,
46
+ the cap IDs carry their source-section paths:
47
+
48
+ ```ts
49
+ const openEnds = body.shell(1, [
50
+ [1, 1],
51
+ [2, 1],
52
+ ]);
53
+ ```
54
+
55
+ The outer array lists two openings; each inner array is one surface ID.
56
+ After shelling, one-to-one inherited topology keeps its complete ID.
57
+ Offset walls receive fresh numeric IDs without reusing retired ones. Pick later fillet or chamfer edges
58
+ from the shell result. See [topology paths](topology.md#ids-belong-to-a-model)
59
+ and the [loft cap example](/examples/topology-paths/).
60
+
61
+ ## Correct a failed shell
62
+
63
+ Shelling requires one connected solid. Tight curves, narrow features, and
64
+ complex intersections can prevent a valid offset or opening. The tool keeps
65
+ the input available so you can change the thickness or selected faces.
66
+
67
+ On some lofts between different profile shapes, a sealed cavity succeeds
68
+ while opening an end fails, even at smaller thicknesses. The
69
+ [bent loft example](/examples/loft/) has this known
70
+ limitation. See [geometric operation limits](../../web/src/content/docs/docs/getting-started/limitations.md#what-to-account-for).
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: Editable sketches
3
+ description: Create constrained sketches and relate their planes to model geometry.
4
+ sidebar:
5
+ order: 7
6
+ ---
7
+
8
+ ## Editable sketches
9
+
10
+ Sketches are immutable 2D definitions, separate from B-Rep model values. Entries
11
+ carry positive IDs within a layer; constraints express what should stay true.
12
+ A derived layer can reference its upstream geometry.
13
+
14
+ ```ts
15
+ import {sketch} from '@code3d/core';
16
+
17
+ const profile = sketch(
18
+ [
19
+ ['point', 1, [0, 0]],
20
+ ['circle', 2, [1, 8]],
21
+ ],
22
+ {
23
+ constraints: [
24
+ ['fixed', 1],
25
+ ['radius', 2, 8],
26
+ ],
27
+ },
28
+ );
29
+
30
+ export const part = profile.face().extrude(3);
31
+ ```
32
+
33
+ Closed regions bridge sketches to ordinary face and solid modeling. Read the
34
+ [sketch reference](api.md#editable-sketch-regions)
35
+ and [agent sketch workflow](../../../docs/agents/sketches.md) for constraints,
36
+ derived layers, observations, and failure diagnostics. Exact tuple types and
37
+ solver behavior live in [sketch.ts](../src/library/sketch.ts) and
38
+ [sketch-solver.ts](../src/library/sketch-solver.ts).
39
+
40
+ ### Relating a sketch to a model plane
41
+
42
+ `s.relate(self => self.plane.align(target))` returns a new sketch with spatial
43
+ relations, leaving its shared 2D definition and the original sketch unchanged.
44
+ It works before a face exists, including `sketch()` and open contours.
45
+
46
+ ```ts
47
+ import {box, sketch} from '@code3d/core';
48
+
49
+ const host = box(40, 20, 30).rotate(0, 0, 25);
50
+ const profile = sketch([
51
+ ['point', 1, [0, 0]],
52
+ ['circle', 2, [1, 4]],
53
+ ]);
54
+ const opening = profile.relate(s => s.plane.align(host.surface(4)));
55
+ const result = host.cut([opening.face().extrude(-20)]);
56
+ const draft = sketch().relate(s => s.plane.align(host.surface(2)));
57
+ ```
58
+
59
+ The target may be a named plane or a planar `host.surface(id)`. The sketch plane
60
+ normal is local `+Y`; alignment uses the same directed-plane alignment, composition-axis offset
61
+ and rotation semantics as model relations. It does not implicitly center the
62
+ sketch on a trimmed surface. An unbounded sketch plane cannot use `on()` to place
63
+ finite geometry against a bound; use `align()`. Topology-only pivots such as
64
+ `pivotVertex()` require a geometric model, not an empty sketch frame.
65
+
66
+ `derive()`, `face()` / `faces()` and extrusion inherit the relations. Boolean
67
+ operations and loft resolve their inputs in the shared composition context;
68
+ placement is not baked into tuple coordinates. A spatial copy shares point
69
+ identities with its original, so a derived layer can still use `profile.point(id)`.
70
+ References target the actual immutable model value: creating a later rotated or
71
+ repositioned model does not redirect existing sketch relations.
72
+ The `relate` callback parameter represents the new sketch frame; every returned
73
+ constraint must involve it. References to the original sketch's plane stay on
74
+ the original frame.
75
+
76
+ In the App, select the related value (`opening`) and choose **Edit sketch** to edit against read-only model
77
+ outlines projected into the sketch's local plane. Select `profile` and choose **Edit sketch** for its original
78
+ local view. Both edit the same source array, with ordinary undo; separate placements
79
+ of that geometry are not separate authoring definitions. Context outlines are
80
+ visual references only, not snapping targets or imported geometry constraints.
81
+ The select-surface-and-create UI is tracked separately within
82
+ [#114](https://github.com/vilicvane/code3d/issues/114).
83
+ Try [mounting-plate.ts](../../app/examples/sketches/mounting-plate.ts).
package/docs/text.md ADDED
@@ -0,0 +1,23 @@
1
+ ---
2
+ title: Text and fonts
3
+ description: Build solid text with Google Fonts or local font files.
4
+ sidebar:
5
+ order: 9
6
+ ---
7
+
8
+ ## Text and fonts
9
+
10
+ ```ts
11
+ import {googleFont, text, extrude, group} from '@code3d/core';
12
+
13
+ const face = googleFont('Play');
14
+ export default group(extrude(text('Hello', face, 10), 1));
15
+ ```
16
+
17
+ In the App, `googleFont()` uses a static family name and optional weight/italic
18
+ settings; `font()` accepts a static font-file URL or TTF/OTF bytes. The engine
19
+ prepares remote resources before synchronous model execution. Text returns
20
+ ordinary planar faces with a common baseline; `extrude(faces, distance)` preserves
21
+ their order and placement. Node can read local file URLs or use downloaded,
22
+ decoded font bytes. See the [text reference](api.md#text),
23
+ [runnable example](../../app/examples/text.ts) and [font notices](../THIRD_PARTY.md).
@@ -0,0 +1,152 @@
1
+ ---
2
+ title: Selecting vertices, edges, and faces
3
+ description: Pick topology for fillets, chamfers, shells, origins, and relation anchors.
4
+ sidebar:
5
+ order: 6
6
+ ---
7
+
8
+ Start with a box:
9
+
10
+ ```ts
11
+ import {box} from '@code3d/core';
12
+
13
+ const base = box(36, 4, 24).fillet(1);
14
+ ```
15
+
16
+ ## Round all edges
17
+
18
+ `fillet(radius)` rounds every edge. `chamfer(distance)` bevels every edge.
19
+ The number must fit the geometry: an operation can fail when its size consumes
20
+ a small face or creates an invalid intersection.
21
+
22
+ ## Choose edges visually
23
+
24
+ Place the cursor inside the `fillet` argument area. The edge tool lets you
25
+ select the edges to round. Selecting specific edges adds their IDs as the
26
+ second argument, for example `fillet(1, [2, 4])`.
27
+
28
+ The tool keeps the operation's input edges available for selection while
29
+ showing its result. Use the panel to adjust the radius or distance; those
30
+ edits return to the source as you type.
31
+
32
+ **Use all edges** removes the second argument. Deselecting every explicit edge
33
+ also returns to all-edge mode; an empty array is not an explicit no-op.
34
+
35
+ Selections write back immediately. Pressing `Esc` does not discard them or
36
+ close the tool; move the editor cursor away from the call to leave it, and
37
+ use Undo to revert an edit.
38
+
39
+ ## Hollow a solid
40
+
41
+ `shell(thickness, removedSurfaceIds?)` creates uniform walls in a connected solid.
42
+ Positive thickness offsets inward and preserves its outside boundary; negative
43
+ thickness offsets outward and preserves the original boundary on the inside.
44
+ Outward offsets use rounded joins where neighboring offset faces separate.
45
+
46
+ ```ts
47
+ const enclosure = box(40, 24, 30).shell(1.5, [4]);
48
+ const sealed = box(40, 24, 30).shell(1.5);
49
+ ```
50
+
51
+ For this box, S4 is its +Y face. Removing it makes an open enclosure. Follow the
52
+ [hollow-part guide](shells.mdx) for the runnable example. Place the
53
+ cursor inside `shell(...)` to adjust **Wall thickness** and toggle **Openings**
54
+ on the input model while viewing the result. IDs belong to that input model.
55
+ The picker retains removed faces so you can close an opening again.
56
+
57
+ Omitting the surface array, or passing `[]`, creates an enclosed cavity.
58
+ **Close all openings** removes the array. Selecting every face is an error:
59
+ at least one surface must remain as a wall. Thickness must be finite and nonzero.
60
+ Offsets can fail around narrow features and complex curves; the input remains
61
+ available to correct the thickness or openings.
62
+
63
+ ## IDs belong to a model
64
+
65
+ Edge, face, and vertex IDs have separate namespaces within each model. An ID
66
+ is not a position in a JavaScript array or a globally unique identifier.
67
+
68
+ Primitives use numbers starting at `1`. Constructors such as loft, extrusion and
69
+ Boolean operations create a new namespace. Inherited elements receive paths
70
+ `[inputIndex, ...previousId]`, with input indices starting at `1`; new elements
71
+ receive numeric IDs starting at `1` in that result.
72
+
73
+ For `loft([start, end])`, the two cap faces are `surface([1, 1])` and
74
+ `surface([2, 1])`; side faces are `surface(1)`, `surface(2)`, and so on. Changing
75
+ the number of side faces does not move the cap IDs. Boolean operations use the
76
+ same rule for every input, including cutting tools. Edges and vertices follow
77
+ the same rules in their own namespaces.
78
+
79
+ Local edits (`fillet`, `chamfer`, and `shell`) keep the input namespace. A
80
+ one-to-one original `E10` remains `E10`, so a later chamfer can select it with
81
+ `rounded.chamfer(0.5, [10])`. Existing paths also remain unchanged: a loft cap
82
+ `S[2,1]` keeps that ID after a fillet. New elements use fresh numeric IDs above
83
+ all numbers previously allocated in this namespace, including retired IDs.
84
+
85
+ For example, `box(50, 3000, 100).fillet(5, [2, 4, 6, 8])` retains faces
86
+ `S1`–`S6` and adds four rounded faces `S7`–`S10`. The two end faces are still
87
+ `S3` and `S4`; rounding their corners does not break their one-to-one identity.
88
+
89
+ In the [topology paths example](/examples/topology-paths/), inspect
90
+ `inlet`, `outlet`, and `side` to compare inherited cap IDs with a new side
91
+ face. The viewport labels paths as S[1,1] or E[1,10]; selections write the
92
+ corresponding arrays into source.
93
+
94
+ Only one-to-one descendants inherit an ID. Deleted elements have no descendant;
95
+ ambiguous splits and merges receive new numeric IDs. A middle loft section is
96
+ not a cap, and a section edge split by loft compatibility does not retain a
97
+ single edge identity. New-element numbering follows deterministic construction
98
+ and can change when that construction changes.
99
+
100
+ Rotation, scaling, placement, and exposed references keep complete IDs. Always
101
+ select IDs from the model passed into the operation being edited; IDs are not
102
+ interchangeable between a source and its result.
103
+
104
+ Shelling follows the same history rules: an unchanged boundary retains its ID,
105
+ and offset walls get new numeric IDs. An opening rim can
106
+ inherit its former cap's surface ID when the kernel records a one-to-one
107
+ modification. To open both ends of a two-section loft, use
108
+ `body.shell(1, [[1, 1], [2, 1]])`.
109
+
110
+ ## Use topology as a relation anchor
111
+
112
+ `model.surface(id)`, `model.edge(id)`, and `model.vertex(id)` return
113
+ face, line, and point anchors. Use their plural forms to obtain a collection;
114
+ omitting the ID array returns all elements of that kind.
115
+
116
+ Place the cursor in one of these calls to pick its topology in the viewport.
117
+ Singular forms select one ID; plural forms let you toggle multiple IDs and
118
+ can explicitly use `[]` for no elements. That is different from fillet and
119
+ chamfer's all-edge behavior.
120
+
121
+ Continue querying a selected face or edge:
122
+
123
+ ```ts
124
+ const face = base.surface(1);
125
+ const boundary = face.edges();
126
+ const corners = boundary[0].vertices();
127
+ const center = face.center;
128
+ ```
129
+
130
+ Queries use the original model's IDs and stay within the selected element.
131
+ `face.edge(id)` reports an error if that edge is outside the face. The viewport
132
+ picker offers only the eligible edges or vertices, including when the geometry
133
+ is exposed from an assembly.
134
+
135
+ `center` is the selected geometry's local bounding-box center, carried through
136
+ transforms. Edges also provide `start`, `midpoint`, and `end` point anchors at
137
+ curve parameters 0, 0.5, and 1. A curve midpoint can differ from its center or
138
+ half-length point. Calculated points do not acquire vertex IDs; `.vertices()`
139
+ returns the actual topology, including a single vertex on a closed edge.
140
+
141
+ To choose a vertex as a rotation pivot, use `.originVertex(id)`. See
142
+ [origins and rotation](origins-and-rotation.mdx).
143
+
144
+ A named element exposed by a reusable part usually communicates intent better
145
+ than a caller maintaining its internal topology IDs. See
146
+ [reusable models](../../web/src/content/docs/docs/guides/reusable-models.mdx).
147
+
148
+ ## Recover from an operation failure
149
+
150
+ Check the input edges first, then reduce the radius or distance. A failed
151
+ fillet or chamfer can still expose its input model to the contextual tool,
152
+ so you can correct the operation without deleting it.