@math.gl/geometry 4.2.0-alpha.5
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/LICENSE +140 -0
- package/README.md +15 -0
- package/dist/geometries/box-geometry.d.ts +17 -0
- package/dist/geometries/box-geometry.d.ts.map +1 -0
- package/dist/geometries/box-geometry.js +60 -0
- package/dist/geometries/box-geometry.js.map +1 -0
- package/dist/geometries/capsule-geometry.d.ts +15 -0
- package/dist/geometries/capsule-geometry.d.ts.map +1 -0
- package/dist/geometries/capsule-geometry.js +92 -0
- package/dist/geometries/capsule-geometry.js.map +1 -0
- package/dist/geometries/cylinder-geometry.d.ts +17 -0
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -0
- package/dist/geometries/cylinder-geometry.js +27 -0
- package/dist/geometries/cylinder-geometry.js.map +1 -0
- package/dist/geometries/geometry-helpers.d.ts +16 -0
- package/dist/geometries/geometry-helpers.d.ts.map +1 -0
- package/dist/geometries/geometry-helpers.js +19 -0
- package/dist/geometries/geometry-helpers.js.map +1 -0
- package/dist/geometries/ico-sphere-geometry.d.ts +11 -0
- package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -0
- package/dist/geometries/ico-sphere-geometry.js +87 -0
- package/dist/geometries/ico-sphere-geometry.js.map +1 -0
- package/dist/geometries/plane-geometry.d.ts +13 -0
- package/dist/geometries/plane-geometry.d.ts.map +1 -0
- package/dist/geometries/plane-geometry.js +51 -0
- package/dist/geometries/plane-geometry.js.map +1 -0
- package/dist/geometries/sphere-geometry.d.ts +12 -0
- package/dist/geometries/sphere-geometry.d.ts.map +1 -0
- package/dist/geometries/sphere-geometry.js +35 -0
- package/dist/geometries/sphere-geometry.js.map +1 -0
- package/dist/geometries/surface-of-revolution.d.ts +11 -0
- package/dist/geometries/surface-of-revolution.d.ts.map +1 -0
- package/dist/geometries/surface-of-revolution.js +37 -0
- package/dist/geometries/surface-of-revolution.js.map +1 -0
- package/dist/geometries/truncated-cone-geometry.d.ts +17 -0
- package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -0
- package/dist/geometries/truncated-cone-geometry.js +92 -0
- package/dist/geometries/truncated-cone-geometry.js.map +1 -0
- package/dist/index.cjs +601 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/geometry-utils.d.ts +9 -0
- package/dist/lib/geometry-utils.d.ts.map +1 -0
- package/dist/lib/geometry-utils.js +29 -0
- package/dist/lib/geometry-utils.js.map +1 -0
- package/dist/lib/geometry.d.ts +36 -0
- package/dist/lib/geometry.d.ts.map +1 -0
- package/dist/lib/geometry.js +89 -0
- package/dist/lib/geometry.js.map +1 -0
- package/package.json +40 -0
- package/src/geometries/box-geometry.ts +70 -0
- package/src/geometries/capsule-geometry.ts +120 -0
- package/src/geometries/cylinder-geometry.ts +41 -0
- package/src/geometries/geometry-helpers.ts +34 -0
- package/src/geometries/ico-sphere-geometry.ts +96 -0
- package/src/geometries/plane-geometry.ts +64 -0
- package/src/geometries/sphere-geometry.ts +42 -0
- package/src/geometries/surface-of-revolution.ts +43 -0
- package/src/geometries/truncated-cone-geometry.ts +129 -0
- package/src/index.ts +28 -0
- package/src/lib/geometry-utils.ts +35 -0
- package/src/lib/geometry.ts +140 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GeometryAttribute } from "./geometry.js";
|
|
2
|
+
type GeometryLike = {
|
|
3
|
+
indices?: GeometryAttribute;
|
|
4
|
+
attributes: Record<string, GeometryAttribute | undefined>;
|
|
5
|
+
};
|
|
6
|
+
/** Expands indexed attributes into a non-indexed geometry-like object. */
|
|
7
|
+
export declare function unpackIndexedGeometry<T extends GeometryLike>(geometry: T): GeometryLike;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=geometry-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry-utils.d.ts","sourceRoot":"","sources":["../../src/lib/geometry-utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,iBAAiB,EAAC,sBAAmB;AAElD,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC,CAAC;CAC3D,CAAC;AAEF,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,YAAY,EAAE,QAAQ,EAAE,CAAC,GAAG,YAAY,CAqBvF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
/** Expands indexed attributes into a non-indexed geometry-like object. */
|
|
5
|
+
export function unpackIndexedGeometry(geometry) {
|
|
6
|
+
if (!geometry.indices)
|
|
7
|
+
return geometry;
|
|
8
|
+
const indices = geometry.indices.value;
|
|
9
|
+
const attributes = {};
|
|
10
|
+
for (const [name, attribute] of Object.entries(geometry.attributes)) {
|
|
11
|
+
if (!attribute)
|
|
12
|
+
continue;
|
|
13
|
+
if (attribute.constant || !attribute.size) {
|
|
14
|
+
attributes[name] = attribute;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const ArrayType = attribute.value.constructor;
|
|
18
|
+
const value = new ArrayType(indices.length * attribute.size);
|
|
19
|
+
for (let i = 0; i < indices.length; i++) {
|
|
20
|
+
for (let component = 0; component < attribute.size; component++) {
|
|
21
|
+
value[i * attribute.size + component] =
|
|
22
|
+
attribute.value[Number(indices[i]) * attribute.size + component];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
attributes[name] = { ...attribute, value };
|
|
26
|
+
}
|
|
27
|
+
return { attributes };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=geometry-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry-utils.js","sourceRoot":"","sources":["../../src/lib/geometry-utils.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,+BAA+B;AAC/B,oCAAoC;AAUpC,0EAA0E;AAC1E,MAAM,UAAU,qBAAqB,CAAyB,QAAW;IACvE,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,QAAQ,CAAC;IACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC;IACvC,MAAM,UAAU,GAAsC,EAAE,CAAC;IACzD,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC,SAAS;YAAE,SAAS;QACzB,IAAI,SAAS,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YAC1C,UAAU,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,WAAoC,CAAC;QACvE,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,IAAI,CAAe,CAAC;QAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;gBAChE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC;oBACnC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,GAAG,EAAC,GAAG,SAAS,EAAE,KAAK,EAAC,CAAC;IAC3C,CAAC;IACD,OAAO,EAAC,UAAU,EAAC,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { TypedArray } from '@math.gl/types';
|
|
2
|
+
export type PrimitiveTopology = 'point-list' | 'line-list' | 'line-strip' | 'triangle-list' | 'triangle-strip';
|
|
3
|
+
export type GeometryAttribute = {
|
|
4
|
+
size?: number;
|
|
5
|
+
value: TypedArray;
|
|
6
|
+
constant?: boolean;
|
|
7
|
+
normalized?: boolean;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
};
|
|
10
|
+
export type GeometryAttributeInput = GeometryAttribute | TypedArray;
|
|
11
|
+
export type GeometryAttributes = Record<string, GeometryAttribute | undefined> & {
|
|
12
|
+
indices?: GeometryAttribute & {
|
|
13
|
+
size: 1;
|
|
14
|
+
value: Uint16Array | Uint32Array;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type GeometryProps = {
|
|
18
|
+
id?: string;
|
|
19
|
+
topology: PrimitiveTopology;
|
|
20
|
+
vertexCount?: number;
|
|
21
|
+
attributes: Record<string, GeometryAttributeInput>;
|
|
22
|
+
indices?: GeometryAttributeInput;
|
|
23
|
+
};
|
|
24
|
+
/** A renderer-independent, typed-array-backed geometry container. */
|
|
25
|
+
export declare class Geometry {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly topology: PrimitiveTopology;
|
|
28
|
+
readonly vertexCount: number;
|
|
29
|
+
readonly indices?: GeometryAttribute;
|
|
30
|
+
readonly attributes: Record<string, GeometryAttribute>;
|
|
31
|
+
userData: Record<string, unknown>;
|
|
32
|
+
constructor(props: GeometryProps);
|
|
33
|
+
getVertexCount(): number;
|
|
34
|
+
getAttributes(): GeometryAttributes;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/lib/geometry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAE/C,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,eAAe,GACf,gBAAgB,CAAC;AAErB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,UAAU,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC,GAAG;IAC/E,OAAO,CAAC,EAAE,iBAAiB,GAAG;QAAC,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAAA;KAAC,CAAC;CAC3E,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACnD,OAAO,CAAC,EAAE,sBAAsB,CAAC;CAClC,CAAC;AAIF,qEAAqE;AACrE,qBAAa,QAAQ;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACvD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;gBAE3B,KAAK,EAAE,aAAa;IAkChC,cAAc,IAAI,MAAM;IAIxB,aAAa,IAAI,kBAAkB;CAKpC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
let geometryId = 0;
|
|
5
|
+
/** A renderer-independent, typed-array-backed geometry container. */
|
|
6
|
+
export class Geometry {
|
|
7
|
+
constructor(props) {
|
|
8
|
+
this.userData = {};
|
|
9
|
+
this.id = props.id || `geometry-${++geometryId}`;
|
|
10
|
+
this.topology = props.topology;
|
|
11
|
+
this.attributes = {};
|
|
12
|
+
let indices = props.indices ? normalizeAttribute(props.indices) : undefined;
|
|
13
|
+
for (const [name, input] of Object.entries(props.attributes || {})) {
|
|
14
|
+
const attribute = normalizeAttribute(input);
|
|
15
|
+
if (name === 'POSITION' || name === 'positions') {
|
|
16
|
+
attribute.size || (attribute.size = 3);
|
|
17
|
+
}
|
|
18
|
+
if (name === 'indices') {
|
|
19
|
+
if (indices)
|
|
20
|
+
throw new Error('Geometry has multiple index attributes');
|
|
21
|
+
indices = attribute;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.attributes[name] = attribute;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (indices) {
|
|
28
|
+
if (!(indices.value instanceof Uint16Array || indices.value instanceof Uint32Array)) {
|
|
29
|
+
throw new Error('Geometry indices must be a Uint16Array or Uint32Array');
|
|
30
|
+
}
|
|
31
|
+
indices.size = 1;
|
|
32
|
+
this.indices = indices;
|
|
33
|
+
validateIndices(indices, this.attributes);
|
|
34
|
+
}
|
|
35
|
+
this.vertexCount = props.vertexCount ?? calculateVertexCount(this.attributes, this.indices);
|
|
36
|
+
if (!Number.isInteger(this.vertexCount) || this.vertexCount < 0) {
|
|
37
|
+
throw new Error('Geometry vertexCount must be a non-negative integer');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getVertexCount() {
|
|
41
|
+
return this.vertexCount;
|
|
42
|
+
}
|
|
43
|
+
getAttributes() {
|
|
44
|
+
return (this.indices ? { indices: this.indices, ...this.attributes } : this.attributes);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function normalizeAttribute(input) {
|
|
48
|
+
const attribute = isTypedArray(input) ? { value: input } : { ...input };
|
|
49
|
+
if (!isTypedArray(attribute.value)) {
|
|
50
|
+
throw new Error('Geometry attributes must contain a typed-array value');
|
|
51
|
+
}
|
|
52
|
+
if (attribute.size !== undefined && (!Number.isInteger(attribute.size) || attribute.size < 1)) {
|
|
53
|
+
throw new Error('Geometry attribute size must be a positive integer');
|
|
54
|
+
}
|
|
55
|
+
if (attribute.size && attribute.value.length % attribute.size !== 0) {
|
|
56
|
+
throw new Error('Geometry attribute length must be divisible by its size');
|
|
57
|
+
}
|
|
58
|
+
return attribute;
|
|
59
|
+
}
|
|
60
|
+
function isTypedArray(value) {
|
|
61
|
+
return ArrayBuffer.isView(value) && !(value instanceof DataView);
|
|
62
|
+
}
|
|
63
|
+
function validateIndices(indices, attributes) {
|
|
64
|
+
let maximumIndex = -1;
|
|
65
|
+
for (const index of indices.value)
|
|
66
|
+
maximumIndex = Math.max(maximumIndex, Number(index));
|
|
67
|
+
for (const [name, attribute] of Object.entries(attributes)) {
|
|
68
|
+
if (!attribute.constant && attribute.size) {
|
|
69
|
+
const attributeVertexCount = attribute.value.length / attribute.size;
|
|
70
|
+
if (maximumIndex >= attributeVertexCount) {
|
|
71
|
+
throw new Error(`Geometry index ${maximumIndex} exceeds ${name} vertex count ${attributeVertexCount}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function calculateVertexCount(attributes, indices) {
|
|
77
|
+
if (indices)
|
|
78
|
+
return indices.value.length;
|
|
79
|
+
let count = Infinity;
|
|
80
|
+
for (const attribute of Object.values(attributes)) {
|
|
81
|
+
if (!attribute.constant && attribute.size) {
|
|
82
|
+
count = Math.min(count, attribute.value.length / attribute.size);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!Number.isFinite(count))
|
|
86
|
+
throw new Error('Geometry has no countable attributes');
|
|
87
|
+
return count;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/lib/geometry.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@math.gl/geometry",
|
|
3
|
+
"description": "CPU geometry primitives and tessellation for math.gl",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"version": "4.2.0-alpha.5",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"geometry",
|
|
12
|
+
"gltf",
|
|
13
|
+
"math",
|
|
14
|
+
"mesh",
|
|
15
|
+
"tessellation",
|
|
16
|
+
"3d"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/visgl/math.gl.git"
|
|
21
|
+
},
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"main": "dist/index.cjs",
|
|
24
|
+
"module": "dist/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"require": "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"src"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@math.gl/types": "4.2.0-alpha.5"
|
|
38
|
+
},
|
|
39
|
+
"gitHead": "9c13785761867748e2edb167b5f2749dce803039"
|
|
40
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Geometry, type GeometryAttributeInput} from '../lib/geometry';
|
|
6
|
+
import {assertPositive, makeIndices, type PrimitiveGeometryProps} from './geometry-helpers';
|
|
7
|
+
|
|
8
|
+
export type BoxGeometryProps = PrimitiveGeometryProps & {size?: readonly [number, number, number]};
|
|
9
|
+
|
|
10
|
+
/** Tessellates the glTF box shape: centered at the origin with total axis lengths `size`. */
|
|
11
|
+
export class BoxGeometry extends Geometry {
|
|
12
|
+
constructor(props: BoxGeometryProps = {}) {
|
|
13
|
+
const size = props.size || [1, 1, 1];
|
|
14
|
+
assertPositive(size[0], 'size[0]');
|
|
15
|
+
assertPositive(size[1], 'size[1]');
|
|
16
|
+
assertPositive(size[2], 'size[2]');
|
|
17
|
+
const half = [size[0] / 2, size[1] / 2, size[2] / 2];
|
|
18
|
+
const positions: number[] = [];
|
|
19
|
+
const normals: number[] = [];
|
|
20
|
+
const texCoords: number[] = [];
|
|
21
|
+
const indices: number[] = [];
|
|
22
|
+
const faces = [
|
|
23
|
+
{n: [1, 0, 0], u: [0, 0, -1], v: [0, 1, 0], h1: half[2], h2: half[1]},
|
|
24
|
+
{n: [-1, 0, 0], u: [0, 0, 1], v: [0, 1, 0], h1: half[2], h2: half[1]},
|
|
25
|
+
{n: [0, 1, 0], u: [1, 0, 0], v: [0, 0, -1], h1: half[0], h2: half[2]},
|
|
26
|
+
{n: [0, -1, 0], u: [1, 0, 0], v: [0, 0, 1], h1: half[0], h2: half[2]},
|
|
27
|
+
{n: [0, 0, 1], u: [1, 0, 0], v: [0, 1, 0], h1: half[0], h2: half[1]},
|
|
28
|
+
{n: [0, 0, -1], u: [-1, 0, 0], v: [0, 1, 0], h1: half[0], h2: half[1]}
|
|
29
|
+
];
|
|
30
|
+
for (const face of faces) {
|
|
31
|
+
const base = positions.length / 3;
|
|
32
|
+
for (const [s, t, u, v] of [
|
|
33
|
+
[-1, -1, 0, 0],
|
|
34
|
+
[1, -1, 1, 0],
|
|
35
|
+
[1, 1, 1, 1],
|
|
36
|
+
[-1, 1, 0, 1]
|
|
37
|
+
]) {
|
|
38
|
+
positions.push(
|
|
39
|
+
face.n[0] * half[0] + face.u[0] * face.h1 * s + face.v[0] * face.h2 * t,
|
|
40
|
+
face.n[1] * half[1] + face.u[1] * face.h1 * s + face.v[1] * face.h2 * t,
|
|
41
|
+
face.n[2] * half[2] + face.u[2] * face.h1 * s + face.v[2] * face.h2 * t
|
|
42
|
+
);
|
|
43
|
+
normals.push(...face.n);
|
|
44
|
+
texCoords.push(u, v);
|
|
45
|
+
}
|
|
46
|
+
indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
|
|
47
|
+
}
|
|
48
|
+
super({
|
|
49
|
+
id: props.id,
|
|
50
|
+
topology: 'triangle-list',
|
|
51
|
+
indices: makeIndices(24, indices),
|
|
52
|
+
attributes: {
|
|
53
|
+
POSITION: {size: 3, value: new Float32Array(positions)},
|
|
54
|
+
NORMAL: {size: 3, value: new Float32Array(normals)},
|
|
55
|
+
TEXCOORD_0: {size: 2, value: new Float32Array(texCoords)},
|
|
56
|
+
...props.attributes
|
|
57
|
+
} as Record<string, GeometryAttributeInput>
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type CubeGeometryProps = PrimitiveGeometryProps & {size?: number};
|
|
63
|
+
|
|
64
|
+
/** Convenience alias for an equal-sided box. */
|
|
65
|
+
export class CubeGeometry extends BoxGeometry {
|
|
66
|
+
constructor(props: CubeGeometryProps = {}) {
|
|
67
|
+
const size = props.size ?? 1;
|
|
68
|
+
super({...props, size: [size, size, size]});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Geometry} from '../lib/geometry';
|
|
6
|
+
import {
|
|
7
|
+
assertNonNegative,
|
|
8
|
+
assertPositive,
|
|
9
|
+
assertSegments,
|
|
10
|
+
type PrimitiveGeometryProps
|
|
11
|
+
} from './geometry-helpers';
|
|
12
|
+
import {makeSurfaceOfRevolution, type ProfileRing} from './surface-of-revolution';
|
|
13
|
+
|
|
14
|
+
export type CapsuleGeometryProps = PrimitiveGeometryProps & {
|
|
15
|
+
height?: number;
|
|
16
|
+
radiusBottom?: number;
|
|
17
|
+
radiusTop?: number;
|
|
18
|
+
nradial?: number;
|
|
19
|
+
ncap?: number;
|
|
20
|
+
nvertical?: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Tessellates the convex hull of two endpoint spheres described by the glTF capsule shape. */
|
|
24
|
+
export class CapsuleGeometry extends Geometry {
|
|
25
|
+
constructor(props: CapsuleGeometryProps = {}) {
|
|
26
|
+
const {
|
|
27
|
+
height = 1,
|
|
28
|
+
radiusBottom = 0.5,
|
|
29
|
+
radiusTop = 0.5,
|
|
30
|
+
nradial = 10,
|
|
31
|
+
ncap = 5,
|
|
32
|
+
nvertical = 1
|
|
33
|
+
} = props;
|
|
34
|
+
assertPositive(height, 'height');
|
|
35
|
+
assertNonNegative(radiusBottom, 'radiusBottom');
|
|
36
|
+
assertNonNegative(radiusTop, 'radiusTop');
|
|
37
|
+
if (radiusBottom === 0 && radiusTop === 0)
|
|
38
|
+
throw new Error('At least one radius must be greater than zero');
|
|
39
|
+
assertSegments(nradial, 'nradial', 3);
|
|
40
|
+
assertSegments(ncap, 'ncap', 1);
|
|
41
|
+
assertSegments(nvertical, 'nvertical', 1);
|
|
42
|
+
|
|
43
|
+
const rings: ProfileRing[] = [];
|
|
44
|
+
const deltaRadius = radiusTop - radiusBottom;
|
|
45
|
+
if (Math.abs(deltaRadius) >= height) {
|
|
46
|
+
const topWins = radiusTop >= radiusBottom;
|
|
47
|
+
const radius = topWins ? radiusTop : radiusBottom;
|
|
48
|
+
const center = topWins ? height / 2 : -height / 2;
|
|
49
|
+
addSphereRings(rings, center, radius, ncap * 2);
|
|
50
|
+
} else {
|
|
51
|
+
const sinSlope = deltaRadius / height;
|
|
52
|
+
const radialNormal = Math.sqrt(1 - sinSlope * sinSlope);
|
|
53
|
+
const sideNormalY = -sinSlope;
|
|
54
|
+
const bottomCenter = -height / 2;
|
|
55
|
+
const topCenter = height / 2;
|
|
56
|
+
for (let i = 0; i <= ncap; i++) {
|
|
57
|
+
const normalY = -1 + ((sideNormalY + 1) * i) / ncap;
|
|
58
|
+
const normalRadius = Math.sqrt(Math.max(0, 1 - normalY * normalY));
|
|
59
|
+
rings.push({
|
|
60
|
+
radius: radiusBottom * normalRadius,
|
|
61
|
+
y: bottomCenter + radiusBottom * normalY,
|
|
62
|
+
normalRadius,
|
|
63
|
+
normalY
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
for (let i = 1; i <= nvertical; i++) {
|
|
67
|
+
const t = i / nvertical;
|
|
68
|
+
const radius = radiusBottom + deltaRadius * t;
|
|
69
|
+
const center = bottomCenter + height * t;
|
|
70
|
+
rings.push({
|
|
71
|
+
radius: radius * radialNormal,
|
|
72
|
+
y: center + radius * sideNormalY,
|
|
73
|
+
normalRadius: radialNormal,
|
|
74
|
+
normalY: sideNormalY
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
for (let i = 1; i <= ncap; i++) {
|
|
78
|
+
const normalY = sideNormalY + ((1 - sideNormalY) * i) / ncap;
|
|
79
|
+
const normalRadius = Math.sqrt(Math.max(0, 1 - normalY * normalY));
|
|
80
|
+
rings.push({
|
|
81
|
+
radius: radiusTop * normalRadius,
|
|
82
|
+
y: topCenter + radiusTop * normalY,
|
|
83
|
+
normalRadius,
|
|
84
|
+
normalY
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const mesh = makeSurfaceOfRevolution(rings, nradial);
|
|
89
|
+
super({
|
|
90
|
+
id: props.id,
|
|
91
|
+
topology: 'triangle-list',
|
|
92
|
+
indices: mesh.indexArray,
|
|
93
|
+
attributes: {
|
|
94
|
+
POSITION: {size: 3, value: new Float32Array(mesh.positions)},
|
|
95
|
+
NORMAL: {size: 3, value: new Float32Array(mesh.normals)},
|
|
96
|
+
TEXCOORD_0: {size: 2, value: new Float32Array(mesh.texCoords)},
|
|
97
|
+
...props.attributes
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function addSphereRings(
|
|
104
|
+
rings: ProfileRing[],
|
|
105
|
+
center: number,
|
|
106
|
+
radius: number,
|
|
107
|
+
segments: number
|
|
108
|
+
): void {
|
|
109
|
+
for (let i = 0; i <= segments; i++) {
|
|
110
|
+
const angle = -Math.PI / 2 + (i / segments) * Math.PI;
|
|
111
|
+
const normalRadius = Math.cos(angle);
|
|
112
|
+
const normalY = Math.sin(angle);
|
|
113
|
+
rings.push({
|
|
114
|
+
radius: radius * normalRadius,
|
|
115
|
+
y: center + radius * normalY,
|
|
116
|
+
normalRadius,
|
|
117
|
+
normalY
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {TruncatedConeGeometry, type TruncatedConeGeometryProps} from './truncated-cone-geometry';
|
|
6
|
+
|
|
7
|
+
export type CylinderGeometryProps = Omit<
|
|
8
|
+
TruncatedConeGeometryProps,
|
|
9
|
+
'topRadius' | 'bottomRadius' | 'topCap' | 'bottomCap' | 'verticalAxis'
|
|
10
|
+
> & {
|
|
11
|
+
radiusTop?: number;
|
|
12
|
+
radiusBottom?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** Tessellates the closed, Y-aligned glTF cylinder shape, including tapered cylinders. */
|
|
16
|
+
export class CylinderGeometry extends TruncatedConeGeometry {
|
|
17
|
+
constructor(props: CylinderGeometryProps = {}) {
|
|
18
|
+
const {height = 2, radiusTop = 0.5, radiusBottom = 0.5} = props;
|
|
19
|
+
super({
|
|
20
|
+
...props,
|
|
21
|
+
height,
|
|
22
|
+
topRadius: radiusTop,
|
|
23
|
+
bottomRadius: radiusBottom,
|
|
24
|
+
topCap: true,
|
|
25
|
+
bottomCap: true,
|
|
26
|
+
verticalAxis: 'y'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ConeGeometryProps = Omit<TruncatedConeGeometryProps, 'topRadius' | 'bottomRadius'> & {
|
|
32
|
+
radius?: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Convenience closed cone with its point at +Y. */
|
|
36
|
+
export class ConeGeometry extends TruncatedConeGeometry {
|
|
37
|
+
constructor(props: ConeGeometryProps = {}) {
|
|
38
|
+
const {radius = 0.5, topCap = false, bottomCap = true} = props;
|
|
39
|
+
super({...props, topRadius: 0, bottomRadius: radius, topCap, bottomCap});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {GeometryAttributeInput} from '../lib/geometry';
|
|
6
|
+
|
|
7
|
+
export type PrimitiveGeometryProps = {
|
|
8
|
+
id?: string;
|
|
9
|
+
attributes?: Record<string, GeometryAttributeInput>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function assertPositive(value: number, name: string): void {
|
|
13
|
+
if (!Number.isFinite(value) || value <= 0) throw new Error(`${name} must be greater than zero`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function assertNonNegative(value: number, name: string): void {
|
|
17
|
+
if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be non-negative`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function assertSegments(value: number, name: string, minimum = 1): void {
|
|
21
|
+
if (!Number.isInteger(value) || value < minimum)
|
|
22
|
+
throw new Error(`${name} must be an integer >= ${minimum}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function makeIndices(vertexCount: number, values: number[]): Uint16Array | Uint32Array {
|
|
26
|
+
return vertexCount > 0xffff ? new Uint32Array(values) : new Uint16Array(values);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type MeshData = {
|
|
30
|
+
positions: number[];
|
|
31
|
+
normals: number[];
|
|
32
|
+
texCoords: number[];
|
|
33
|
+
indices: number[];
|
|
34
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Geometry} from '../lib/geometry';
|
|
6
|
+
import {assertPositive, assertSegments, type PrimitiveGeometryProps} from './geometry-helpers';
|
|
7
|
+
|
|
8
|
+
export type IcoSphereGeometryProps = PrimitiveGeometryProps & {
|
|
9
|
+
radius?: number;
|
|
10
|
+
iterations?: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Subdivided icosahedron projected onto a sphere. Returned as a non-indexed triangle list. */
|
|
14
|
+
export class IcoSphereGeometry extends Geometry {
|
|
15
|
+
constructor(props: IcoSphereGeometryProps = {}) {
|
|
16
|
+
const {radius = 0.5, iterations = 0} = props;
|
|
17
|
+
assertPositive(radius, 'radius');
|
|
18
|
+
assertSegments(iterations, 'iterations', 0);
|
|
19
|
+
if (iterations > 8) throw new Error('iterations must be <= 8');
|
|
20
|
+
const t = (1 + Math.sqrt(5)) / 2;
|
|
21
|
+
const vertices = [
|
|
22
|
+
[-1, t, 0],
|
|
23
|
+
[1, t, 0],
|
|
24
|
+
[-1, -t, 0],
|
|
25
|
+
[1, -t, 0],
|
|
26
|
+
[0, -1, t],
|
|
27
|
+
[0, 1, t],
|
|
28
|
+
[0, -1, -t],
|
|
29
|
+
[0, 1, -t],
|
|
30
|
+
[t, 0, -1],
|
|
31
|
+
[t, 0, 1],
|
|
32
|
+
[-t, 0, -1],
|
|
33
|
+
[-t, 0, 1]
|
|
34
|
+
].map(normalize);
|
|
35
|
+
let faces = [
|
|
36
|
+
[0, 11, 5],
|
|
37
|
+
[0, 5, 1],
|
|
38
|
+
[0, 1, 7],
|
|
39
|
+
[0, 7, 10],
|
|
40
|
+
[0, 10, 11],
|
|
41
|
+
[1, 5, 9],
|
|
42
|
+
[5, 11, 4],
|
|
43
|
+
[11, 10, 2],
|
|
44
|
+
[10, 7, 6],
|
|
45
|
+
[7, 1, 8],
|
|
46
|
+
[3, 9, 4],
|
|
47
|
+
[3, 4, 2],
|
|
48
|
+
[3, 2, 6],
|
|
49
|
+
[3, 6, 8],
|
|
50
|
+
[3, 8, 9],
|
|
51
|
+
[4, 9, 5],
|
|
52
|
+
[2, 4, 11],
|
|
53
|
+
[6, 2, 10],
|
|
54
|
+
[8, 6, 7],
|
|
55
|
+
[9, 8, 1]
|
|
56
|
+
].map(face => face.map(index => vertices[index]));
|
|
57
|
+
for (let iteration = 0; iteration < iterations; iteration++) {
|
|
58
|
+
const subdivided: number[][][] = [];
|
|
59
|
+
for (const [a, b, c] of faces) {
|
|
60
|
+
const ab = normalize([a[0] + b[0], a[1] + b[1], a[2] + b[2]]);
|
|
61
|
+
const bc = normalize([b[0] + c[0], b[1] + c[1], b[2] + c[2]]);
|
|
62
|
+
const ca = normalize([c[0] + a[0], c[1] + a[1], c[2] + a[2]]);
|
|
63
|
+
subdivided.push([a, ab, ca], [b, bc, ab], [c, ca, bc], [ab, bc, ca]);
|
|
64
|
+
}
|
|
65
|
+
faces = subdivided;
|
|
66
|
+
}
|
|
67
|
+
const positions: number[] = [];
|
|
68
|
+
const normals: number[] = [];
|
|
69
|
+
const texCoords: number[] = [];
|
|
70
|
+
for (const face of faces) {
|
|
71
|
+
for (const normal of face) {
|
|
72
|
+
positions.push(normal[0] * radius, normal[1] * radius, normal[2] * radius);
|
|
73
|
+
normals.push(...normal);
|
|
74
|
+
texCoords.push(
|
|
75
|
+
0.5 + Math.atan2(normal[0], normal[2]) / (2 * Math.PI),
|
|
76
|
+
0.5 - Math.asin(normal[1]) / Math.PI
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
super({
|
|
81
|
+
id: props.id,
|
|
82
|
+
topology: 'triangle-list',
|
|
83
|
+
attributes: {
|
|
84
|
+
POSITION: {size: 3, value: new Float32Array(positions)},
|
|
85
|
+
NORMAL: {size: 3, value: new Float32Array(normals)},
|
|
86
|
+
TEXCOORD_0: {size: 2, value: new Float32Array(texCoords)},
|
|
87
|
+
...props.attributes
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalize(vector: number[]): number[] {
|
|
94
|
+
const length = Math.hypot(...vector);
|
|
95
|
+
return vector.map(component => component / length);
|
|
96
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Geometry} from '../lib/geometry';
|
|
6
|
+
import {
|
|
7
|
+
assertPositive,
|
|
8
|
+
assertSegments,
|
|
9
|
+
makeIndices,
|
|
10
|
+
type PrimitiveGeometryProps
|
|
11
|
+
} from './geometry-helpers';
|
|
12
|
+
|
|
13
|
+
export type PlaneGeometryProps = PrimitiveGeometryProps & {
|
|
14
|
+
sizeX: number;
|
|
15
|
+
sizeZ: number;
|
|
16
|
+
nx?: number;
|
|
17
|
+
nz?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Tessellates a finite glTF plane in XZ with its front face and normal pointing +Y. */
|
|
21
|
+
export class PlaneGeometry extends Geometry {
|
|
22
|
+
constructor(props: PlaneGeometryProps) {
|
|
23
|
+
const {sizeX, sizeZ, nx = 1, nz = 1} = props;
|
|
24
|
+
assertPositive(sizeX, 'sizeX');
|
|
25
|
+
assertPositive(sizeZ, 'sizeZ');
|
|
26
|
+
assertSegments(nx, 'nx');
|
|
27
|
+
assertSegments(nz, 'nz');
|
|
28
|
+
const positions: number[] = [];
|
|
29
|
+
const normals: number[] = [];
|
|
30
|
+
const texCoords: number[] = [];
|
|
31
|
+
const indexValues: number[] = [];
|
|
32
|
+
for (let z = 0; z <= nz; z++) {
|
|
33
|
+
const v = z / nz;
|
|
34
|
+
for (let x = 0; x <= nx; x++) {
|
|
35
|
+
const u = x / nx;
|
|
36
|
+
positions.push((u - 0.5) * sizeX, 0, (0.5 - v) * sizeZ);
|
|
37
|
+
normals.push(0, 1, 0);
|
|
38
|
+
texCoords.push(u, v);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const stride = nx + 1;
|
|
42
|
+
for (let z = 0; z < nz; z++) {
|
|
43
|
+
for (let x = 0; x < nx; x++) {
|
|
44
|
+
const a = z * stride + x;
|
|
45
|
+
const b = a + 1;
|
|
46
|
+
const c = a + stride;
|
|
47
|
+
const d = c + 1;
|
|
48
|
+
indexValues.push(a, b, d, a, d, c);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const vertexCount = positions.length / 3;
|
|
52
|
+
super({
|
|
53
|
+
id: props.id,
|
|
54
|
+
topology: 'triangle-list',
|
|
55
|
+
indices: makeIndices(vertexCount, indexValues),
|
|
56
|
+
attributes: {
|
|
57
|
+
POSITION: {size: 3, value: new Float32Array(positions)},
|
|
58
|
+
NORMAL: {size: 3, value: new Float32Array(normals)},
|
|
59
|
+
TEXCOORD_0: {size: 2, value: new Float32Array(texCoords)},
|
|
60
|
+
...props.attributes
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|