trid3d 0.3.1 → 0.5.0
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 -7
- package/package.json +1 -1
- package/trid/index.d.ts +565 -11
- package/trid/index.js +18 -1
- package/trid/modeling/bevel.js +596 -0
- package/trid/modeling/blobs.js +465 -0
- package/trid/modeling/cleanup.js +471 -0
- package/trid/modeling/curves.js +156 -0
- package/trid/modeling/deform.js +191 -0
- package/trid/modeling/frames.js +97 -0
- package/trid/modeling/loft.js +363 -0
- package/trid/modeling/subdivide.js +215 -0
- package/trid/modeling/sweep.js +169 -0
- package/trid/modeling/vec.js +35 -0
- package/trid/render/canvasRenderer.js +99 -48
- package/trid/render/effects.js +16 -9
- package/trid/render/grid.js +177 -0
- package/trid/render/lights.js +162 -0
- package/trid/render/post.js +325 -0
- package/trid/render/shapes.js +91 -3
- package/trid/render/webglRenderer.js +341 -19
- package/trid/scene.js +48 -6
- package/trid/tools/helpers.js +107 -9
- package/trid/tools/index.js +1 -1
- package/trid/tools/transformGizmo.js +40 -6
package/trid/index.d.ts
CHANGED
|
@@ -162,6 +162,8 @@ export interface SceneObjectInput {
|
|
|
162
162
|
draggable?: boolean;
|
|
163
163
|
/** Set to false to make `HoverHighlight` ignore this object */
|
|
164
164
|
hoverable?: boolean;
|
|
165
|
+
/** Set to false so `pick` / `pickHit` never find this object or its children (helpers like a grid) */
|
|
166
|
+
pickable?: boolean;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/**
|
|
@@ -198,10 +200,17 @@ export interface SceneOptions {
|
|
|
198
200
|
lineWidth?: number;
|
|
199
201
|
/** Camera (default { z: -1 }) */
|
|
200
202
|
camera?: Camera;
|
|
201
|
-
/** Direction light comes from (default: above, left and in front) */
|
|
203
|
+
/** Direction light comes from (default: above, left and in front). Ignored when `lights` is given */
|
|
202
204
|
light?: Vec3;
|
|
203
|
-
/**
|
|
205
|
+
/**
|
|
206
|
+
* Brightness of faces turned away from the light, 0..1 (default 0.25).
|
|
207
|
+
* The directional light gets `1 - ambient`. Ignored when `lights` is given
|
|
208
|
+
*/
|
|
204
209
|
ambient?: number;
|
|
210
|
+
/** Every light, instead of `light` and `ambient` */
|
|
211
|
+
lights?: Light[];
|
|
212
|
+
/** Post effects run on the picture, in order (default none) */
|
|
213
|
+
post?: PostEffect[];
|
|
205
214
|
/** What draws the scene (default: a new CanvasRenderer) */
|
|
206
215
|
renderer?: Renderer;
|
|
207
216
|
}
|
|
@@ -216,7 +225,19 @@ export class Scene {
|
|
|
216
225
|
color: string;
|
|
217
226
|
lineWidth: number;
|
|
218
227
|
camera: Camera;
|
|
219
|
-
light
|
|
228
|
+
/** Every light. Edit it freely; it's read every frame */
|
|
229
|
+
lights: Light[];
|
|
230
|
+
/** Post effects, run in order on the picture before tools draw */
|
|
231
|
+
post: PostEffect[];
|
|
232
|
+
/**
|
|
233
|
+
* Shortcut for the first directional light's direction (null if there's none).
|
|
234
|
+
* Setting it when there's none adds one.
|
|
235
|
+
*/
|
|
236
|
+
light: Vec3 | null;
|
|
237
|
+
/**
|
|
238
|
+
* Shortcut for the first ambient light's intensity (0 if there's none).
|
|
239
|
+
* Setting it when there's none adds one; the directional light keeps its intensity.
|
|
240
|
+
*/
|
|
220
241
|
ambient: number;
|
|
221
242
|
renderer: Renderer;
|
|
222
243
|
objects: SceneObject[];
|
|
@@ -263,8 +284,10 @@ export interface ShapeOptions {
|
|
|
263
284
|
r?: number;
|
|
264
285
|
/** Sphere latitude bands (default 12) */
|
|
265
286
|
rings?: number;
|
|
266
|
-
/** Points around: sphere (24), cylinder / cone (32), torus (48) */
|
|
287
|
+
/** Points around: sphere (24), cylinder / cone (32), torus (48); rows per rounded edge of a cube / box with `radius` (4) */
|
|
267
288
|
segments?: number;
|
|
289
|
+
/** Cube / box: round the edges and corners by this much (default 0: sharp) */
|
|
290
|
+
radius?: number;
|
|
268
291
|
/** Cone top radius: 0 = pointed, > 0 = tapered cylinder (default 0) */
|
|
269
292
|
rTop?: number;
|
|
270
293
|
/** Torus: distance from the center to the middle of the tube (default 0.35) */
|
|
@@ -384,6 +407,106 @@ export interface Bounds {
|
|
|
384
407
|
*/
|
|
385
408
|
export function meshBounds(input: Mesh | SceneObjectInput | (Mesh | SceneObjectInput)[] | Scene): Bounds | null;
|
|
386
409
|
|
|
410
|
+
export type GridPlane = "xz" | "xy" | "yz";
|
|
411
|
+
|
|
412
|
+
/** A square grid of lines centered on the origin, as a line-only mesh. */
|
|
413
|
+
export function gridMesh(opts?: { size?: number; divisions?: number; plane?: GridPlane }): Mesh;
|
|
414
|
+
|
|
415
|
+
export interface GridHelperOptions {
|
|
416
|
+
/** Width of the whole grid (default 4) */
|
|
417
|
+
size?: number;
|
|
418
|
+
/** Cells along each side (default 40) */
|
|
419
|
+
divisions?: number;
|
|
420
|
+
/** A major line every this many cells, 0 for none (default 10) */
|
|
421
|
+
major?: number;
|
|
422
|
+
/** The plane it lies in (default "xz", a floor) */
|
|
423
|
+
plane?: GridPlane;
|
|
424
|
+
/** Minor lines (default "rgba(255, 255, 255, 0.12)") */
|
|
425
|
+
color?: string;
|
|
426
|
+
/** Major lines (default "rgba(255, 255, 255, 0.25)") */
|
|
427
|
+
majorColor?: string;
|
|
428
|
+
/** Draw the axes through the origin (default true), or their colors like `{ x: "#f00" }` */
|
|
429
|
+
axes?: boolean | { x?: string; y?: string; z?: string };
|
|
430
|
+
/** Fade toward the edge (default true); a number is where the fade starts (0 = center, 1 = edge) */
|
|
431
|
+
fade?: boolean | number;
|
|
432
|
+
/** Line width in pixels (default 1) */
|
|
433
|
+
lineWidth?: number;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** A floor grid with major lines, colored axes and a fade: a group to `scene.add`, never picked. */
|
|
437
|
+
export function gridHelper(opts?: GridHelperOptions): SceneObjectInput & { pickable: false; children: SceneObjectInput[] };
|
|
438
|
+
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
// Lights
|
|
441
|
+
// ---------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
/** Light from everywhere: lights every side of every face the same. */
|
|
444
|
+
export interface AmbientLight {
|
|
445
|
+
type: "ambient";
|
|
446
|
+
/** Any CSS color (default white) */
|
|
447
|
+
color?: string;
|
|
448
|
+
/** Multiplies the color (default 1) */
|
|
449
|
+
intensity?: number;
|
|
450
|
+
/** Set to false to turn it off without removing it */
|
|
451
|
+
enabled?: boolean;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Light from far away in one direction, like the sun. */
|
|
455
|
+
export interface DirectionalLight {
|
|
456
|
+
type: "directional";
|
|
457
|
+
/** Direction light comes from (any length) */
|
|
458
|
+
direction: Vec3;
|
|
459
|
+
color?: string;
|
|
460
|
+
intensity?: number;
|
|
461
|
+
enabled?: boolean;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Light from a point, like a bulb. */
|
|
465
|
+
export interface PointLight {
|
|
466
|
+
type: "point";
|
|
467
|
+
/** World position */
|
|
468
|
+
position: Vec3;
|
|
469
|
+
color?: string;
|
|
470
|
+
intensity?: number;
|
|
471
|
+
/** Fades out smoothly and reaches nothing past this distance (default: no fading) */
|
|
472
|
+
range?: number;
|
|
473
|
+
enabled?: boolean;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export type Light = AmbientLight | DirectionalLight | PointLight;
|
|
477
|
+
|
|
478
|
+
/** Most directional + point lights the renderers use; extra ones are ignored. */
|
|
479
|
+
export const MAX_LIGHTS: number;
|
|
480
|
+
|
|
481
|
+
/** The default lights: ambient at `ambient`, white directional from `direction` at `1 - ambient`. */
|
|
482
|
+
export function defaultLights(direction?: Vec3, ambient?: number): Light[];
|
|
483
|
+
|
|
484
|
+
/** A scene's lights ready for shading (used by the renderers). */
|
|
485
|
+
export interface SceneLights {
|
|
486
|
+
/** Summed ambient color [r, g, b] */
|
|
487
|
+
ambient: [number, number, number];
|
|
488
|
+
/** Brightest channel of `ambient` */
|
|
489
|
+
ambientLevel: number;
|
|
490
|
+
lights: { point: boolean; dir: Vec3 | null; pos: Vec3 | null; color: [number, number, number]; range: number }[];
|
|
491
|
+
/** Unit direction of the first directional light */
|
|
492
|
+
mainDir: Vec3;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** A scene's lights, colors parsed and multiplied by intensity, directions unit length. */
|
|
496
|
+
export function sceneLights(
|
|
497
|
+
scene: { lights?: Light[]; light?: Vec3 | null; ambient?: number },
|
|
498
|
+
rgba: (css: string) => number[],
|
|
499
|
+
): SceneLights;
|
|
500
|
+
|
|
501
|
+
/** The light reaching a point, [r, g, b] (can go above 1). */
|
|
502
|
+
export function lightAt(lights: SceneLights, normal: Vec3, position: Vec3): [number, number, number];
|
|
503
|
+
|
|
504
|
+
/** Brightness (brightest channel) and diffuse share (0..1) of a light color. */
|
|
505
|
+
export function lightLevels(color: number[], ambientLevel: number): { light: number; diffuse: number };
|
|
506
|
+
|
|
507
|
+
/** How much of a point light reaches `dist` away (1 up close, 0 at `range`; range 0 = no fading). */
|
|
508
|
+
export function rangeFade(dist: number, range: number): number;
|
|
509
|
+
|
|
387
510
|
// ---------------------------------------------------------------------------
|
|
388
511
|
// Renderers
|
|
389
512
|
// ---------------------------------------------------------------------------
|
|
@@ -464,13 +587,15 @@ export interface Surface {
|
|
|
464
587
|
local: Vec3;
|
|
465
588
|
/** Unit direction from the point to the camera */
|
|
466
589
|
view: Vec3;
|
|
467
|
-
/** Unit direction light comes from */
|
|
590
|
+
/** Unit direction the scene's first directional light comes from */
|
|
468
591
|
lightDir: Vec3;
|
|
469
|
-
/** How much the
|
|
592
|
+
/** How much of the light comes from the lights rather than the ambient, 0..1 (one light: how much the point faces it) */
|
|
470
593
|
diffuse: number;
|
|
471
|
-
/** Brightness Trid used: ambient + (1 - ambient) × diffuse */
|
|
594
|
+
/** Brightness Trid used (brightest channel of `lightColor`): ambient + (1 - ambient) × diffuse with the default lights */
|
|
472
595
|
light: number;
|
|
473
|
-
/** The
|
|
596
|
+
/** The light reaching the point, [r, g, b] (every light added up; can go above 1). Trid's color is `base × lightColor` */
|
|
597
|
+
lightColor: [number, number, number];
|
|
598
|
+
/** The scene's ambient level (brightest channel) */
|
|
474
599
|
ambient: number;
|
|
475
600
|
/** Distance to the camera */
|
|
476
601
|
depth: number;
|
|
@@ -563,6 +688,89 @@ export function uniformType(value: unknown): "float" | "vec2" | "vec3" | "vec4"
|
|
|
563
688
|
/** The GLSL declaration of `Surface`, shared by every effect. */
|
|
564
689
|
export const SURFACE_GLSL: string;
|
|
565
690
|
|
|
691
|
+
// ---------------------------------------------------------------------------
|
|
692
|
+
// Post effects
|
|
693
|
+
// ---------------------------------------------------------------------------
|
|
694
|
+
|
|
695
|
+
/** What a post effect's `canvas` function knows about the picture. */
|
|
696
|
+
export interface PostFrame {
|
|
697
|
+
width: number;
|
|
698
|
+
height: number;
|
|
699
|
+
/** Seconds since the renderer started */
|
|
700
|
+
time: number;
|
|
701
|
+
/** The scene's background color */
|
|
702
|
+
background: RGBA;
|
|
703
|
+
/** The canvas being changed */
|
|
704
|
+
canvas: HTMLCanvasElement;
|
|
705
|
+
/** Turns a uniform value (CSS color, array) into RGBA 0..1 */
|
|
706
|
+
rgba(value: UniformValue): RGBA;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/** One pass of a post effect. */
|
|
710
|
+
export interface PostPass {
|
|
711
|
+
/** GLSL defining `vec4 post(vec4 color, Frame f)` */
|
|
712
|
+
glsl: string;
|
|
713
|
+
/** Size of this pass's picture, relative to the canvas (default 1) */
|
|
714
|
+
scale?: number;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/** A shader run on the whole picture after the scene is drawn. */
|
|
718
|
+
export interface PostEffect<U extends Record<string, UniformValue> = Record<string, UniformValue>> {
|
|
719
|
+
name?: string;
|
|
720
|
+
/** GLSL defining `vec4 post(vec4 color, Frame f)`: one pass (WebGL renderer) */
|
|
721
|
+
glsl?: string;
|
|
722
|
+
/** Instead of `glsl`: passes run in order (each can read the last with `frame(uv)`, the start with `source(uv)`) */
|
|
723
|
+
passes?: PostPass[];
|
|
724
|
+
/** Changes the 2D canvas directly: used by the canvas renderer (and by WebGL for effects without GLSL) */
|
|
725
|
+
canvas?(ctx: CanvasRenderingContext2D, frame: PostFrame, uniforms: U): void;
|
|
726
|
+
/** Settings read every frame */
|
|
727
|
+
uniforms?: U;
|
|
728
|
+
/** Set to false to turn it off without removing it */
|
|
729
|
+
enabled?: boolean;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/** Darkens the corners of the picture. */
|
|
733
|
+
export function vignette(opts?: { strength?: number; radius?: number; softness?: number; color?: string }): PostEffect<{
|
|
734
|
+
uStrength: number;
|
|
735
|
+
uRadius: number;
|
|
736
|
+
uSoftness: number;
|
|
737
|
+
uColor: string;
|
|
738
|
+
}>;
|
|
739
|
+
|
|
740
|
+
/** Glow around the bright parts. */
|
|
741
|
+
export function bloom(opts?: { threshold?: number; strength?: number; radius?: number }): PostEffect<{
|
|
742
|
+
uThreshold: number;
|
|
743
|
+
uStrength: number;
|
|
744
|
+
uRadius: number;
|
|
745
|
+
}>;
|
|
746
|
+
|
|
747
|
+
/** Brightness, contrast, saturation and a tint, in that order. */
|
|
748
|
+
export function grade(opts?: { brightness?: number; contrast?: number; saturation?: number; tint?: string }): PostEffect<{
|
|
749
|
+
uBrightness: number;
|
|
750
|
+
uContrast: number;
|
|
751
|
+
uSaturation: number;
|
|
752
|
+
uTint: string;
|
|
753
|
+
}>;
|
|
754
|
+
|
|
755
|
+
/** Film grain: random noise over the picture. */
|
|
756
|
+
export function grain(opts?: { amount?: number; size?: number; animated?: boolean }): PostEffect<{
|
|
757
|
+
uAmount: number;
|
|
758
|
+
uGrain: number;
|
|
759
|
+
uAnimated: boolean;
|
|
760
|
+
}>;
|
|
761
|
+
|
|
762
|
+
/** A post effect's passes (empty when it only has a canvas version). */
|
|
763
|
+
export function postPasses(effect: PostEffect): { glsl: string; scale: number }[];
|
|
764
|
+
|
|
765
|
+
/** The effects of a list that are on. */
|
|
766
|
+
export function activePost(list: PostEffect[] | undefined): PostEffect[];
|
|
767
|
+
|
|
768
|
+
/** Runs post effects' `canvas` functions on a scene's canvas (used by the renderers). */
|
|
769
|
+
export function runCanvasPost(scene: Scene, list: PostEffect[] | undefined, start: number): void;
|
|
770
|
+
|
|
771
|
+
/** The GLSL declaration of `Frame` and the `frame` / `source` helpers. */
|
|
772
|
+
export const FRAME_GLSL: string;
|
|
773
|
+
|
|
566
774
|
// ---------------------------------------------------------------------------
|
|
567
775
|
// Texture mapping
|
|
568
776
|
// ---------------------------------------------------------------------------
|
|
@@ -1107,6 +1315,8 @@ export interface TransformGizmoOptions {
|
|
|
1107
1315
|
size?: number;
|
|
1108
1316
|
/** Change the targets' transforms while dragging (default true) */
|
|
1109
1317
|
apply?: boolean;
|
|
1318
|
+
/** Where the handles sit and what rotate / scale go around (default "bounds") */
|
|
1319
|
+
pivot?: GizmoPivot;
|
|
1110
1320
|
/** A handle was grabbed */
|
|
1111
1321
|
onGrab?: (targets: SceneObject[]) => void;
|
|
1112
1322
|
/** While dragging: the operation since the grab */
|
|
@@ -1116,8 +1326,17 @@ export interface TransformGizmoOptions {
|
|
|
1116
1326
|
}
|
|
1117
1327
|
|
|
1118
1328
|
/**
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1329
|
+
* Where a `TransformGizmo` puts its handles and turns / scales around:
|
|
1330
|
+
* - `"bounds"`: the middle of the targets' bounding box
|
|
1331
|
+
* - `"origin"`: the targets' origins in the world (averaged)
|
|
1332
|
+
* - a world point (moved along when the gizmo moves the targets)
|
|
1333
|
+
* - a function asked every frame
|
|
1334
|
+
*/
|
|
1335
|
+
export type GizmoPivot = "bounds" | "origin" | Vec3 | ((targets: SceneObject[]) => Vec3 | null);
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Move arrows, rotate rings or scale handles on one or more objects, around a
|
|
1339
|
+
* pivot (default: the middle of their bounding box). Add it after the camera tool.
|
|
1121
1340
|
*/
|
|
1122
1341
|
export class TransformGizmo implements Tool {
|
|
1123
1342
|
constructor(opts?: TransformGizmoOptions);
|
|
@@ -1125,6 +1344,7 @@ export class TransformGizmo implements Tool {
|
|
|
1125
1344
|
targets: SceneObject[];
|
|
1126
1345
|
size: number;
|
|
1127
1346
|
apply: boolean;
|
|
1347
|
+
pivot: GizmoPivot;
|
|
1128
1348
|
onGrab?: (targets: SceneObject[]) => void;
|
|
1129
1349
|
onChange?: (op: TransformOp, targets: SceneObject[]) => void;
|
|
1130
1350
|
onDrop?: (op: TransformOp, targets: SceneObject[]) => void;
|
|
@@ -1132,7 +1352,7 @@ export class TransformGizmo implements Tool {
|
|
|
1132
1352
|
visible: boolean;
|
|
1133
1353
|
/** True while a handle is being dragged. */
|
|
1134
1354
|
readonly dragging: boolean;
|
|
1135
|
-
/**
|
|
1355
|
+
/** The pivot in world space, or null when there are no targets. */
|
|
1136
1356
|
center(): Vec3 | null;
|
|
1137
1357
|
draw(ctx: CanvasRenderingContext2D, scene: Scene): void;
|
|
1138
1358
|
pointerDown(e: ToolPointerEvent, scene: Scene): boolean;
|
|
@@ -1166,8 +1386,25 @@ export interface PickHit {
|
|
|
1166
1386
|
depth: number;
|
|
1167
1387
|
/** The object, then its groups up to the top: `[part, group, ..., root]` */
|
|
1168
1388
|
path: SceneObject[];
|
|
1389
|
+
/** Where the hit is, in the world */
|
|
1390
|
+
point: Vec3;
|
|
1391
|
+
/** Where the hit is, in the mesh's own space */
|
|
1392
|
+
local: Vec3;
|
|
1393
|
+
/** The 3 vertex indices of the triangle hit (polygons are fans from their first corner); null for a line */
|
|
1394
|
+
triangle: [number, number, number] | null;
|
|
1395
|
+
/** Weights of those vertices at the hit (they add up to 1); null for a line */
|
|
1396
|
+
barycentric: [number, number, number] | null;
|
|
1397
|
+
/** Unit normal of the face hit, in the world, on its front side; null for a line */
|
|
1398
|
+
normal: Vec3 | null;
|
|
1399
|
+
/** Texture coordinates at the hit (the material's mapping, before repeat / offset / rotation); null without any */
|
|
1400
|
+
uv: UV | null;
|
|
1401
|
+
/** Where the texture is read: `uv` with the material's repeat, offset and rotation; null without texture coordinates */
|
|
1402
|
+
textureUV: UV | null;
|
|
1169
1403
|
}
|
|
1170
1404
|
|
|
1405
|
+
/** Whether picking can find an object (not when it or a group above it has `pickable: false`). */
|
|
1406
|
+
export function isPickable(obj: SceneObjectInput & { parent?: SceneObjectInput | null }): boolean;
|
|
1407
|
+
|
|
1171
1408
|
/**
|
|
1172
1409
|
* Exact picking: the nearest object whose triangles (or, without faces, lines
|
|
1173
1410
|
* within `padding` pixels) are under a canvas pixel.
|
|
@@ -1397,3 +1634,320 @@ export function noise3(x: number, y: number, z: number, opts?: NoiseOptions): nu
|
|
|
1397
1634
|
export function fbm2(x: number, y: number, opts?: FbmOptions): number;
|
|
1398
1635
|
/** Fractal 3D noise: octaves of noise3, each twice as fine. About [0, 1), averaging 0.5. */
|
|
1399
1636
|
export function fbm3(x: number, y: number, z: number, opts?: FbmOptions): number;
|
|
1637
|
+
|
|
1638
|
+
// Modeling: curves
|
|
1639
|
+
|
|
1640
|
+
export interface SplineOptions {
|
|
1641
|
+
/** Points made between two control points (default 8) */
|
|
1642
|
+
segments?: number;
|
|
1643
|
+
/** The last point joins the first (a loop); the result doesn't repeat the first point (default false) */
|
|
1644
|
+
closed?: boolean;
|
|
1645
|
+
/** 0 = uniform, 0.5 = centripetal: no loops or overshoot (default), 1 = chordal */
|
|
1646
|
+
alpha?: number;
|
|
1647
|
+
/** 0 = smooth (default), 1 = straight lines between the points */
|
|
1648
|
+
tension?: number;
|
|
1649
|
+
/** Instead of `segments`: this many points in all, evenly spaced by length */
|
|
1650
|
+
count?: number;
|
|
1651
|
+
/** Instead of `segments`: points about this far apart, evenly spaced */
|
|
1652
|
+
spacing?: number;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* A smooth curve through `points` (Catmull-Rom: it passes through every
|
|
1657
|
+
* point). Works with 2D, 3D and lathe `{ r, y }` points: every number field
|
|
1658
|
+
* of the first point is a coordinate, and the result has the same fields.
|
|
1659
|
+
*/
|
|
1660
|
+
export function spline<P extends Point2 | Vec3 | ProfilePoint>(points: P[], opts?: SplineOptions): P[];
|
|
1661
|
+
|
|
1662
|
+
export interface ResampleOptions {
|
|
1663
|
+
/** Number of points in all */
|
|
1664
|
+
count?: number;
|
|
1665
|
+
/** Distance between points (used when `count` isn't given) */
|
|
1666
|
+
spacing?: number;
|
|
1667
|
+
/** The points form a loop (default false) */
|
|
1668
|
+
closed?: boolean;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/** Points along a polyline, evenly spaced by length. The ends stay where they are. */
|
|
1672
|
+
export function resamplePath<P extends Point2 | Vec3 | ProfilePoint>(points: P[], opts?: ResampleOptions): P[];
|
|
1673
|
+
|
|
1674
|
+
// Modeling: sweep
|
|
1675
|
+
|
|
1676
|
+
/** A size along a path: a number, numbers spread evenly from start to end, or a function of t (0 at the start, 1 at the end). */
|
|
1677
|
+
export type SizeAlong = number | number[] | ((t: number) => number);
|
|
1678
|
+
|
|
1679
|
+
export interface SweepOptions {
|
|
1680
|
+
/** The cross-section: x to the path's side, y to its "up" (default: a circle of `radius`) */
|
|
1681
|
+
profile?: Point2[];
|
|
1682
|
+
/** The profile is a closed outline (default true); false = an open line, for ribbons, leaves and roads */
|
|
1683
|
+
profileClosed?: boolean;
|
|
1684
|
+
/** Radius of the default circle (default 0.1). Ignored with a `profile` */
|
|
1685
|
+
radius?: SizeAlong;
|
|
1686
|
+
/** Points around the default circle (default 16) */
|
|
1687
|
+
segments?: number;
|
|
1688
|
+
/** Size of the section along the path (default 1). 0 at an end closes it into a point */
|
|
1689
|
+
scale?: SizeAlong;
|
|
1690
|
+
/** Turn of the section from start to end, in radians (default 0). Whole turns on a closed path */
|
|
1691
|
+
twist?: number;
|
|
1692
|
+
/** The path is a loop: its end joins its start (default false) */
|
|
1693
|
+
closed?: boolean;
|
|
1694
|
+
/** Close the ends with flat caps (default true; open paths with closed profiles) */
|
|
1695
|
+
caps?: boolean;
|
|
1696
|
+
/** Keep the section's y pointed this way (roads, rails, frames). Default: carried along the path without spinning */
|
|
1697
|
+
up?: Vec3;
|
|
1698
|
+
/** Stretch the section at sharp corners so the tube keeps its thickness (default true) */
|
|
1699
|
+
miter?: boolean;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* Moves a cross-section along a 3D path: tails, horns, tentacles, handles,
|
|
1704
|
+
* pipes, cables, rails. Without `profile`, a round tube of `radius`.
|
|
1705
|
+
* Clockwise faces; uvs: u around the section, v along the path.
|
|
1706
|
+
*/
|
|
1707
|
+
export function sweep(path: Vec3[], opts?: SweepOptions): Mesh;
|
|
1708
|
+
|
|
1709
|
+
// Modeling: loft
|
|
1710
|
+
|
|
1711
|
+
/** A 2D section, placed along the loft's axis or path. */
|
|
1712
|
+
export interface LoftOutline {
|
|
1713
|
+
/** Closed outline: x to the side, y up (either direction; one point = a tip) */
|
|
1714
|
+
outline: Point2[];
|
|
1715
|
+
/** Where it is: the position on the axis, or 0 (start) to 1 (end) along a path (default: evenly spread) */
|
|
1716
|
+
at?: number;
|
|
1717
|
+
/** Size, the same both ways or per direction (default 1); 0 = a tip */
|
|
1718
|
+
scale?: number | { x?: number; y?: number };
|
|
1719
|
+
/** Turn in its own plane, in radians (default 0) */
|
|
1720
|
+
rotate?: number;
|
|
1721
|
+
/** Move in its own plane (default none) */
|
|
1722
|
+
offset?: Partial<Point2>;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/** A section as a ring of 3D points, used where it is. */
|
|
1726
|
+
export interface LoftRing {
|
|
1727
|
+
points: Vec3[];
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
export interface LoftOptions {
|
|
1731
|
+
/** Straight axis the outlines are placed along (default "z"). x/y of an outline: "z" → X/Y, "y" → X/Z, "x" → Z/Y */
|
|
1732
|
+
axis?: "x" | "y" | "z";
|
|
1733
|
+
/** A 3D path to place the outlines along instead of the axis */
|
|
1734
|
+
path?: Vec3[];
|
|
1735
|
+
/** With `path`: keep the outlines' y pointed this way (default: carried along without spinning) */
|
|
1736
|
+
up?: Vec3;
|
|
1737
|
+
/** The surface loops back to its first section (a closed path, or 3D rings in a loop; default false) */
|
|
1738
|
+
closed?: boolean;
|
|
1739
|
+
/** Close the ends with flat caps (default true) */
|
|
1740
|
+
caps?: boolean;
|
|
1741
|
+
/** Rows between two sections, on average (default 6) */
|
|
1742
|
+
segments?: number;
|
|
1743
|
+
/** Points around each section (default: kept when every section has the same count, else the most any has, at least 24) */
|
|
1744
|
+
points?: number;
|
|
1745
|
+
/** Smooth curves between sections, never past them (default true); false = straight */
|
|
1746
|
+
smooth?: boolean;
|
|
1747
|
+
/** Line sections up so the surface doesn't twist (default true) */
|
|
1748
|
+
align?: boolean;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* A surface through cross-sections: bodies, bottles, hulls, car bodies,
|
|
1753
|
+
* heads. Every section is an `outline` (2D, placed along an axis or a path)
|
|
1754
|
+
* or every section is `points` (a 3D ring). Clockwise faces; uvs: u around,
|
|
1755
|
+
* v along. Throws when the two kinds are mixed.
|
|
1756
|
+
*/
|
|
1757
|
+
export function loft(sections: LoftOutline[] | LoftRing[], opts?: LoftOptions): Mesh;
|
|
1758
|
+
|
|
1759
|
+
// Modeling: deformers
|
|
1760
|
+
|
|
1761
|
+
export interface AxisRange {
|
|
1762
|
+
/** Axis the effect goes along (default "y") */
|
|
1763
|
+
axis?: "x" | "y" | "z";
|
|
1764
|
+
/** Start of the part that changes: 0 = the mesh's low end on the axis, 1 = its high end (default 0) */
|
|
1765
|
+
from?: number;
|
|
1766
|
+
/** End of the part that changes, same measure (default 1). Past it, the full effect carries on */
|
|
1767
|
+
to?: number;
|
|
1768
|
+
/** A point the axis line goes through (default: the middle of the mesh's box) */
|
|
1769
|
+
center?: Partial<Vec3>;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
export interface TaperOptions extends AxisRange {
|
|
1773
|
+
/** Size at the end of the range (default 0.5), or a function of how far into the range (0 to 1) giving the size */
|
|
1774
|
+
amount?: number | ((w: number) => number);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
export interface TwistOptions extends AxisRange {
|
|
1778
|
+
/** Turn at the end of the range, in radians (default π/2), or a function of how far into the range (0 to 1) */
|
|
1779
|
+
angle?: number | ((w: number) => number);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
export interface BendOptions extends AxisRange {
|
|
1783
|
+
/** Angle of the arc, in radians (default π/2); negative curves the other way */
|
|
1784
|
+
angle?: number;
|
|
1785
|
+
/** Axis it curves toward (default: "z" for a "y" axis, "z" for "x", "y" for "z") */
|
|
1786
|
+
toward?: "x" | "y" | "z";
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
export interface BulgeOptions {
|
|
1790
|
+
/** Middle of the bulge */
|
|
1791
|
+
center: Vec3;
|
|
1792
|
+
/** Distance where it fades to nothing */
|
|
1793
|
+
radius: number;
|
|
1794
|
+
/** How far the middle moves (default 0.1); negative = a dent */
|
|
1795
|
+
amount?: number;
|
|
1796
|
+
/** "normal": along the surface, works on flat sides (default); "radial": away from `center` (put it inside the part) */
|
|
1797
|
+
mode?: "normal" | "radial";
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/** Narrows or widens a mesh along an axis, in place. Returns the same mesh. */
|
|
1801
|
+
export function taperMesh<M extends Mesh>(mesh: M, opts?: TaperOptions): M;
|
|
1802
|
+
/** Turns a mesh around an axis, more and more along it, in place (same direction as rotateX / Y / Z). Returns the same mesh. */
|
|
1803
|
+
export function twistMesh<M extends Mesh>(mesh: M, opts?: TwistOptions): M;
|
|
1804
|
+
/** Bends a mesh into an arc along an axis, in place; past the range it goes on straight. Returns the same mesh. */
|
|
1805
|
+
export function bendMesh<M extends Mesh>(mesh: M, opts?: BendOptions): M;
|
|
1806
|
+
/** Pushes the surface out (or in) around a point, fading at `radius`, in place. Returns the same mesh. */
|
|
1807
|
+
export function bulgeMesh<M extends Mesh>(mesh: M, opts: BulgeOptions): M;
|
|
1808
|
+
|
|
1809
|
+
// Modeling: cleanup and checks
|
|
1810
|
+
|
|
1811
|
+
/**
|
|
1812
|
+
* Merges vertices closer than `epsilon` (default: a ten-millionth of the
|
|
1813
|
+
* mesh's size), in place. Texture coordinates and normals (per corner) are
|
|
1814
|
+
* kept; corners that meet are removed. Returns the same mesh.
|
|
1815
|
+
*/
|
|
1816
|
+
export function weldVertices<M extends Mesh>(mesh: M, epsilon?: number): M;
|
|
1817
|
+
|
|
1818
|
+
export interface CleanOptions {
|
|
1819
|
+
/** Merge vertices at the same place first: true, or the distance (default false) */
|
|
1820
|
+
weld?: boolean | number;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* Removes what's broken, in place: faces to missing or bad vertices, repeated
|
|
1825
|
+
* corners, faces with fewer than 3 corners, crossed or of no area, repeated
|
|
1826
|
+
* faces, bad corner lists (set to null), bad edges, and unused vertices,
|
|
1827
|
+
* texture coordinates and normals. Returns the same mesh.
|
|
1828
|
+
*/
|
|
1829
|
+
export function cleanMesh<M extends Mesh>(mesh: M, opts?: CleanOptions): M;
|
|
1830
|
+
|
|
1831
|
+
export interface MeshProblem {
|
|
1832
|
+
/** What's wrong, e.g. "bad-index", "no-area", "turned-face", "inside-out", "open-edge" */
|
|
1833
|
+
kind: string;
|
|
1834
|
+
/** "error": breaks drawing or export; "warning": draws wrong; "info": worth knowing */
|
|
1835
|
+
level: "error" | "warning" | "info";
|
|
1836
|
+
/** A short sentence, with the first few faces / vertices / edges concerned */
|
|
1837
|
+
message: string;
|
|
1838
|
+
faces?: number[];
|
|
1839
|
+
vertices?: number[];
|
|
1840
|
+
edges?: number[];
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/** Lists what's wrong with a mesh, without changing it (one entry per kind of problem; empty when fine). */
|
|
1844
|
+
export function validateMesh(mesh: Mesh): MeshProblem[];
|
|
1845
|
+
|
|
1846
|
+
/**
|
|
1847
|
+
* Turns faces in place so neighbors agree and each piece points outward
|
|
1848
|
+
* (clockwise from outside). Works without welding. Returns the same mesh.
|
|
1849
|
+
*/
|
|
1850
|
+
export function fixWinding<M extends Mesh>(mesh: M): M;
|
|
1851
|
+
|
|
1852
|
+
// Modeling: subdivision
|
|
1853
|
+
|
|
1854
|
+
export interface SubdivideOptions {
|
|
1855
|
+
/** Times to subdivide (default 1, at most 6); each level makes about 4 times the faces */
|
|
1856
|
+
levels?: number;
|
|
1857
|
+
/** false: only split the faces, keep the shape (default true) */
|
|
1858
|
+
smooth?: boolean;
|
|
1859
|
+
/** Edges where faces meet sharper than this (degrees) stay hard (default: none) */
|
|
1860
|
+
creaseAngle?: number;
|
|
1861
|
+
/** Edges that stay hard: pairs or polylines of vertex indices, e.g. [[0, 1], [4, 5, 6, 7, 4]] */
|
|
1862
|
+
creases?: number[][];
|
|
1863
|
+
/** "smooth": texture coordinates smoothed with the surface, seams kept (default); "linear": only split */
|
|
1864
|
+
uvs?: "smooth" | "linear";
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
/**
|
|
1868
|
+
* Smooths a mesh by subdividing it (Catmull-Clark): a rough cage becomes a
|
|
1869
|
+
* smooth surface. Returns a new mesh of quads (the original isn't changed),
|
|
1870
|
+
* welded, with texture coordinates and materials; own normals are dropped.
|
|
1871
|
+
*/
|
|
1872
|
+
export function subdivideMesh(mesh: Mesh, opts?: SubdivideOptions): Mesh;
|
|
1873
|
+
|
|
1874
|
+
// Modeling: blobs (distance fields)
|
|
1875
|
+
|
|
1876
|
+
/** How a blob shape joins what's before it: melts in (default), carves it, or keeps only the overlap. */
|
|
1877
|
+
export type BlobOp = "add" | "subtract" | "intersect";
|
|
1878
|
+
|
|
1879
|
+
interface BlobBase {
|
|
1880
|
+
/** How it joins what's before it (default "add") */
|
|
1881
|
+
op?: BlobOp;
|
|
1882
|
+
/** Same as op: "subtract" */
|
|
1883
|
+
subtract?: boolean;
|
|
1884
|
+
/** How far it melts into what's before it, as a distance (default: the blobMesh / blobField `blend`) */
|
|
1885
|
+
blend?: number;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
export type BlobShape = BlobBase & (
|
|
1889
|
+
| { kind: "sphere"; center: Vec3; radius: number }
|
|
1890
|
+
/** Turned by `rot` (radians, X then Y then Z) */
|
|
1891
|
+
| { kind: "ellipsoid"; center: Vec3; radii: Vec3; rot?: Vec3 }
|
|
1892
|
+
/** A capsule between two points; `ra` / `rb` instead of `radius` make a rounded cone */
|
|
1893
|
+
| { kind: "capsule"; a: Vec3; b: Vec3; radius?: number; ra?: number; rb?: number }
|
|
1894
|
+
/** A capped cylinder between two points, edges rounded by `round` */
|
|
1895
|
+
| { kind: "cylinder"; a: Vec3; b: Vec3; radius: number; round?: number }
|
|
1896
|
+
/** A box of `size`, edges rounded by `round`, turned by `rot` */
|
|
1897
|
+
| { kind: "box"; center: Vec3; size: Vec3; round?: number; rot?: Vec3 }
|
|
1898
|
+
/** A ring lying flat in XZ (like makeShape("torus")), turned by `rot` */
|
|
1899
|
+
| { kind: "torus"; center: Vec3; R: number; tube: number; rot?: Vec3 }
|
|
1900
|
+
);
|
|
1901
|
+
|
|
1902
|
+
export interface SdfMeshOptions {
|
|
1903
|
+
/** Grid cells along the longest side (default 64, 4 to 256); more is finer and slower */
|
|
1904
|
+
resolution?: number;
|
|
1905
|
+
/** "nets": surface nets, even quads (default); "cubes": marching cubes, triangles. Both are watertight */
|
|
1906
|
+
method?: "nets" | "cubes";
|
|
1907
|
+
/** Pull vertices exactly onto the surface (default true) */
|
|
1908
|
+
project?: boolean;
|
|
1909
|
+
/** Normals from the field, for smooth shading (default true) */
|
|
1910
|
+
normals?: boolean;
|
|
1911
|
+
/** Texture mapping for the uvs, as for computeUVs (default "box"); false: none */
|
|
1912
|
+
uvs?: MappingType | Mapping | false;
|
|
1913
|
+
/** Skip grid blocks far from the surface, trusting the field to be about the distance (default true) */
|
|
1914
|
+
sparse?: boolean;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
export interface BlobMeshOptions extends Omit<SdfMeshOptions, "sparse"> {
|
|
1918
|
+
/** How far shapes melt into each other, as a distance (default: a tenth of the model's size). A shape's own `blend` wins */
|
|
1919
|
+
blend?: number;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
/** One smooth mesh of shapes that melt together (added, subtracted or intersected, in order). */
|
|
1923
|
+
export function blobMesh(shapes: BlobShape[], opts?: BlobMeshOptions): Mesh;
|
|
1924
|
+
|
|
1925
|
+
/** The distance field of blob shapes (negative inside), to mix with your own math and mesh with sdfMesh. */
|
|
1926
|
+
export function blobField(shapes: BlobShape[], opts?: { blend?: number }): (p: Vec3) => number;
|
|
1927
|
+
|
|
1928
|
+
/** The surface where `field(p)` is 0 (negative inside), within `bounds`, as a mesh. */
|
|
1929
|
+
export function sdfMesh(field: (p: Vec3) => number, bounds: { min: Vec3; max: Vec3 }, opts?: SdfMeshOptions): Mesh;
|
|
1930
|
+
|
|
1931
|
+
// Modeling: bevel
|
|
1932
|
+
|
|
1933
|
+
export interface BevelOptions {
|
|
1934
|
+
/** Size of the rounding: the radius of the ball rolled along the edges (default: a twentieth of the mesh's size). Shrinks where bevels would overlap */
|
|
1935
|
+
radius?: number;
|
|
1936
|
+
/** Rows across each rounded edge (default 3); 1 = a flat cut (chamfer) */
|
|
1937
|
+
segments?: number;
|
|
1938
|
+
/** Edges where faces meet sharper than this (degrees) are beveled (default 30) */
|
|
1939
|
+
angle?: number;
|
|
1940
|
+
/** "edges": round the edges, corners become pieces of a sphere (default); "vertices": cut only the corners */
|
|
1941
|
+
mode?: "edges" | "vertices";
|
|
1942
|
+
/** Instead of `angle`: the edges to bevel, as pairs or polylines of vertex indices */
|
|
1943
|
+
edges?: number[][];
|
|
1944
|
+
/** With mode "vertices", instead of `angle`: the corners to cut, as vertex indices */
|
|
1945
|
+
vertices?: number[];
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
/**
|
|
1949
|
+
* Rounds (or cuts flat) the sharp edges of a mesh, like a bevel modifier.
|
|
1950
|
+
* Returns a new mesh, welded, with texture coordinates and materials kept.
|
|
1951
|
+
* Edges along an open border stay sharp.
|
|
1952
|
+
*/
|
|
1953
|
+
export function bevelMesh(mesh: Mesh, opts?: BevelOptions): Mesh;
|