@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.
Files changed (65) hide show
  1. package/LICENSE +140 -0
  2. package/README.md +15 -0
  3. package/dist/geometries/box-geometry.d.ts +17 -0
  4. package/dist/geometries/box-geometry.d.ts.map +1 -0
  5. package/dist/geometries/box-geometry.js +60 -0
  6. package/dist/geometries/box-geometry.js.map +1 -0
  7. package/dist/geometries/capsule-geometry.d.ts +15 -0
  8. package/dist/geometries/capsule-geometry.d.ts.map +1 -0
  9. package/dist/geometries/capsule-geometry.js +92 -0
  10. package/dist/geometries/capsule-geometry.js.map +1 -0
  11. package/dist/geometries/cylinder-geometry.d.ts +17 -0
  12. package/dist/geometries/cylinder-geometry.d.ts.map +1 -0
  13. package/dist/geometries/cylinder-geometry.js +27 -0
  14. package/dist/geometries/cylinder-geometry.js.map +1 -0
  15. package/dist/geometries/geometry-helpers.d.ts +16 -0
  16. package/dist/geometries/geometry-helpers.d.ts.map +1 -0
  17. package/dist/geometries/geometry-helpers.js +19 -0
  18. package/dist/geometries/geometry-helpers.js.map +1 -0
  19. package/dist/geometries/ico-sphere-geometry.d.ts +11 -0
  20. package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -0
  21. package/dist/geometries/ico-sphere-geometry.js +87 -0
  22. package/dist/geometries/ico-sphere-geometry.js.map +1 -0
  23. package/dist/geometries/plane-geometry.d.ts +13 -0
  24. package/dist/geometries/plane-geometry.d.ts.map +1 -0
  25. package/dist/geometries/plane-geometry.js +51 -0
  26. package/dist/geometries/plane-geometry.js.map +1 -0
  27. package/dist/geometries/sphere-geometry.d.ts +12 -0
  28. package/dist/geometries/sphere-geometry.d.ts.map +1 -0
  29. package/dist/geometries/sphere-geometry.js +35 -0
  30. package/dist/geometries/sphere-geometry.js.map +1 -0
  31. package/dist/geometries/surface-of-revolution.d.ts +11 -0
  32. package/dist/geometries/surface-of-revolution.d.ts.map +1 -0
  33. package/dist/geometries/surface-of-revolution.js +37 -0
  34. package/dist/geometries/surface-of-revolution.js.map +1 -0
  35. package/dist/geometries/truncated-cone-geometry.d.ts +17 -0
  36. package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -0
  37. package/dist/geometries/truncated-cone-geometry.js +92 -0
  38. package/dist/geometries/truncated-cone-geometry.js.map +1 -0
  39. package/dist/index.cjs +601 -0
  40. package/dist/index.cjs.map +6 -0
  41. package/dist/index.d.ts +18 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +13 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/lib/geometry-utils.d.ts +9 -0
  46. package/dist/lib/geometry-utils.d.ts.map +1 -0
  47. package/dist/lib/geometry-utils.js +29 -0
  48. package/dist/lib/geometry-utils.js.map +1 -0
  49. package/dist/lib/geometry.d.ts +36 -0
  50. package/dist/lib/geometry.d.ts.map +1 -0
  51. package/dist/lib/geometry.js +89 -0
  52. package/dist/lib/geometry.js.map +1 -0
  53. package/package.json +40 -0
  54. package/src/geometries/box-geometry.ts +70 -0
  55. package/src/geometries/capsule-geometry.ts +120 -0
  56. package/src/geometries/cylinder-geometry.ts +41 -0
  57. package/src/geometries/geometry-helpers.ts +34 -0
  58. package/src/geometries/ico-sphere-geometry.ts +96 -0
  59. package/src/geometries/plane-geometry.ts +64 -0
  60. package/src/geometries/sphere-geometry.ts +42 -0
  61. package/src/geometries/surface-of-revolution.ts +43 -0
  62. package/src/geometries/truncated-cone-geometry.ts +129 -0
  63. package/src/index.ts +28 -0
  64. package/src/lib/geometry-utils.ts +35 -0
  65. package/src/lib/geometry.ts +140 -0
@@ -0,0 +1,42 @@
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
+ import {makeSurfaceOfRevolution, type ProfileRing} from './surface-of-revolution';
8
+
9
+ export type SphereGeometryProps = PrimitiveGeometryProps & {
10
+ radius?: number;
11
+ nlat?: number;
12
+ nlong?: number;
13
+ };
14
+
15
+ /** Tessellates the glTF sphere shape. */
16
+ export class SphereGeometry extends Geometry {
17
+ constructor(props: SphereGeometryProps = {}) {
18
+ const {radius = 0.5, nlat = 10, nlong = 10} = props;
19
+ assertPositive(radius, 'radius');
20
+ assertSegments(nlat, 'nlat', 2);
21
+ assertSegments(nlong, 'nlong', 3);
22
+ const rings: ProfileRing[] = [];
23
+ for (let latitude = 0; latitude <= nlat; latitude++) {
24
+ const angle = -Math.PI / 2 + (latitude / nlat) * Math.PI;
25
+ const normalRadius = Math.cos(angle);
26
+ const normalY = Math.sin(angle);
27
+ rings.push({radius: radius * normalRadius, y: radius * normalY, normalRadius, normalY});
28
+ }
29
+ const mesh = makeSurfaceOfRevolution(rings, nlong);
30
+ super({
31
+ id: props.id,
32
+ topology: 'triangle-list',
33
+ indices: mesh.indexArray,
34
+ attributes: {
35
+ POSITION: {size: 3, value: new Float32Array(mesh.positions)},
36
+ NORMAL: {size: 3, value: new Float32Array(mesh.normals)},
37
+ TEXCOORD_0: {size: 2, value: new Float32Array(mesh.texCoords)},
38
+ ...props.attributes
39
+ }
40
+ });
41
+ }
42
+ }
@@ -0,0 +1,43 @@
1
+ // math.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {makeIndices, type MeshData} from './geometry-helpers';
6
+
7
+ export type ProfileRing = {radius: number; y: number; normalRadius: number; normalY: number};
8
+
9
+ export function makeSurfaceOfRevolution(
10
+ rings: ProfileRing[],
11
+ nradial: number
12
+ ): MeshData & {indexArray: Uint16Array | Uint32Array} {
13
+ const positions: number[] = [];
14
+ const normals: number[] = [];
15
+ const texCoords: number[] = [];
16
+ const indices: number[] = [];
17
+ const stride = nradial + 1;
18
+ for (let ringIndex = 0; ringIndex < rings.length; ringIndex++) {
19
+ const ring = rings[ringIndex];
20
+ for (let radial = 0; radial <= nradial; radial++) {
21
+ const u = radial / nradial;
22
+ const angle = u * Math.PI * 2;
23
+ const sin = Math.sin(angle);
24
+ const cos = Math.cos(angle);
25
+ positions.push(sin * ring.radius, ring.y, cos * ring.radius);
26
+ normals.push(sin * ring.normalRadius, ring.normalY, cos * ring.normalRadius);
27
+ texCoords.push(u, ringIndex / (rings.length - 1));
28
+ }
29
+ }
30
+ for (let ring = 0; ring < rings.length - 1; ring++) {
31
+ for (let radial = 0; radial < nradial; radial++) {
32
+ const a = ring * stride + radial;
33
+ const b = a + 1;
34
+ const c = a + stride;
35
+ const d = c + 1;
36
+ indices.push(a, b, d, a, d, c);
37
+ }
38
+ }
39
+ return {
40
+ ...{positions, normals, texCoords, indices},
41
+ indexArray: makeIndices(positions.length / 3, indices)
42
+ };
43
+ }
@@ -0,0 +1,129 @@
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
+ makeIndices,
11
+ type PrimitiveGeometryProps
12
+ } from './geometry-helpers';
13
+
14
+ export type TruncatedConeGeometryProps = PrimitiveGeometryProps & {
15
+ topRadius?: number;
16
+ bottomRadius?: number;
17
+ topCap?: boolean;
18
+ bottomCap?: boolean;
19
+ height?: number;
20
+ nradial?: number;
21
+ nvertical?: number;
22
+ verticalAxis?: 'x' | 'y' | 'z';
23
+ };
24
+
25
+ /** General truncated cone primitive, adapted from luma.gl's CPU tessellator. */
26
+ export class TruncatedConeGeometry extends Geometry {
27
+ constructor(props: TruncatedConeGeometryProps = {}) {
28
+ const {
29
+ topRadius = 0,
30
+ bottomRadius = 0,
31
+ topCap = false,
32
+ bottomCap = false,
33
+ height = 1,
34
+ nradial = 10,
35
+ nvertical = 1,
36
+ verticalAxis = 'y'
37
+ } = props;
38
+ assertNonNegative(topRadius, 'topRadius');
39
+ assertNonNegative(bottomRadius, 'bottomRadius');
40
+ if (topRadius === 0 && bottomRadius === 0)
41
+ throw new Error('At least one radius must be greater than zero');
42
+ assertPositive(height, 'height');
43
+ assertSegments(nradial, 'nradial', 3);
44
+ assertSegments(nvertical, 'nvertical');
45
+
46
+ const positions: number[] = [];
47
+ const normals: number[] = [];
48
+ const texCoords: number[] = [];
49
+ const indices: number[] = [];
50
+ const stride = nradial + 1;
51
+ const slope = (bottomRadius - topRadius) / height;
52
+ const normalScale = 1 / Math.hypot(1, slope);
53
+
54
+ const addRing = (
55
+ radius: number,
56
+ y: number,
57
+ normalRadius: number,
58
+ normalY: number,
59
+ v: number
60
+ ): number => {
61
+ const ring = positions.length / 3 / stride;
62
+ for (let radial = 0; radial <= nradial; radial++) {
63
+ const u = radial / nradial;
64
+ const angle = u * Math.PI * 2;
65
+ const sin = Math.sin(angle);
66
+ const cos = Math.cos(angle);
67
+ const point = orient([sin * radius, y, cos * radius], verticalAxis);
68
+ const normal = orient([sin * normalRadius, normalY, cos * normalRadius], verticalAxis);
69
+ positions.push(...point);
70
+ normals.push(...normal);
71
+ texCoords.push(u, v);
72
+ }
73
+ return ring;
74
+ };
75
+ const connect = (lower: number, upper: number): void => {
76
+ for (let radial = 0; radial < nradial; radial++) {
77
+ const a = lower * stride + radial;
78
+ const b = a + 1;
79
+ const c = upper * stride + radial;
80
+ const d = c + 1;
81
+ indices.push(a, b, d, a, d, c);
82
+ }
83
+ };
84
+
85
+ let previous: number | undefined;
86
+ if (bottomCap) {
87
+ const center = addRing(0, -height / 2, 0, -1, 0);
88
+ const edge = addRing(bottomRadius, -height / 2, 0, -1, 1);
89
+ connect(center, edge);
90
+ previous = addRing(bottomRadius, -height / 2, normalScale, slope * normalScale, 0);
91
+ }
92
+ for (let vertical = 0; vertical <= nvertical; vertical++) {
93
+ const v = vertical / nvertical;
94
+ const radius = bottomRadius + (topRadius - bottomRadius) * v;
95
+ const ring = addRing(radius, (v - 0.5) * height, normalScale, slope * normalScale, v);
96
+ if (previous !== undefined && !(bottomCap && vertical === 0)) connect(previous, ring);
97
+ previous = ring;
98
+ }
99
+ if (topCap && previous !== undefined) {
100
+ const edge = addRing(topRadius, height / 2, 0, 1, 1);
101
+ const center = addRing(0, height / 2, 0, 1, 0);
102
+ connect(edge, center);
103
+ }
104
+
105
+ const vertexCount = positions.length / 3;
106
+ super({
107
+ id: props.id,
108
+ topology: 'triangle-list',
109
+ indices: makeIndices(vertexCount, indices),
110
+ attributes: {
111
+ POSITION: {size: 3, value: new Float32Array(positions)},
112
+ NORMAL: {size: 3, value: new Float32Array(normals)},
113
+ TEXCOORD_0: {size: 2, value: new Float32Array(texCoords)},
114
+ ...props.attributes
115
+ }
116
+ });
117
+ }
118
+ }
119
+
120
+ function orient(vector: readonly number[], axis: 'x' | 'y' | 'z'): number[] {
121
+ switch (axis) {
122
+ case 'x':
123
+ return [vector[1], vector[2], vector[0]];
124
+ case 'z':
125
+ return [vector[2], vector[0], vector[1]];
126
+ default:
127
+ return [vector[0], vector[1], vector[2]];
128
+ }
129
+ }
package/src/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ // math.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ export {Geometry} from './lib/geometry';
6
+ export type {
7
+ GeometryProps,
8
+ GeometryAttribute,
9
+ GeometryAttributeInput,
10
+ GeometryAttributes,
11
+ PrimitiveTopology
12
+ } from './lib/geometry';
13
+ export {unpackIndexedGeometry} from './lib/geometry-utils';
14
+
15
+ export {BoxGeometry, CubeGeometry} from './geometries/box-geometry';
16
+ export type {BoxGeometryProps, CubeGeometryProps} from './geometries/box-geometry';
17
+ export {CapsuleGeometry} from './geometries/capsule-geometry';
18
+ export type {CapsuleGeometryProps} from './geometries/capsule-geometry';
19
+ export {CylinderGeometry, ConeGeometry} from './geometries/cylinder-geometry';
20
+ export type {CylinderGeometryProps, ConeGeometryProps} from './geometries/cylinder-geometry';
21
+ export {PlaneGeometry} from './geometries/plane-geometry';
22
+ export type {PlaneGeometryProps} from './geometries/plane-geometry';
23
+ export {SphereGeometry} from './geometries/sphere-geometry';
24
+ export type {SphereGeometryProps} from './geometries/sphere-geometry';
25
+ export {TruncatedConeGeometry} from './geometries/truncated-cone-geometry';
26
+ export type {TruncatedConeGeometryProps} from './geometries/truncated-cone-geometry';
27
+ export {IcoSphereGeometry} from './geometries/ico-sphere-geometry';
28
+ export type {IcoSphereGeometryProps} from './geometries/ico-sphere-geometry';
@@ -0,0 +1,35 @@
1
+ // math.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {TypedArray, TypedArrayConstructor} from '@math.gl/types';
6
+ import type {GeometryAttribute} from './geometry';
7
+
8
+ type GeometryLike = {
9
+ indices?: GeometryAttribute;
10
+ attributes: Record<string, GeometryAttribute | undefined>;
11
+ };
12
+
13
+ /** Expands indexed attributes into a non-indexed geometry-like object. */
14
+ export function unpackIndexedGeometry<T extends GeometryLike>(geometry: T): GeometryLike {
15
+ if (!geometry.indices) return geometry;
16
+ const indices = geometry.indices.value;
17
+ const attributes: Record<string, GeometryAttribute> = {};
18
+ for (const [name, attribute] of Object.entries(geometry.attributes)) {
19
+ if (!attribute) continue;
20
+ if (attribute.constant || !attribute.size) {
21
+ attributes[name] = attribute;
22
+ continue;
23
+ }
24
+ const ArrayType = attribute.value.constructor as TypedArrayConstructor;
25
+ const value = new ArrayType(indices.length * attribute.size) as TypedArray;
26
+ for (let i = 0; i < indices.length; i++) {
27
+ for (let component = 0; component < attribute.size; component++) {
28
+ value[i * attribute.size + component] =
29
+ attribute.value[Number(indices[i]) * attribute.size + component];
30
+ }
31
+ }
32
+ attributes[name] = {...attribute, value};
33
+ }
34
+ return {attributes};
35
+ }
@@ -0,0 +1,140 @@
1
+ // math.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {TypedArray} from '@math.gl/types';
6
+
7
+ export type PrimitiveTopology =
8
+ | 'point-list'
9
+ | 'line-list'
10
+ | 'line-strip'
11
+ | 'triangle-list'
12
+ | 'triangle-strip';
13
+
14
+ export type GeometryAttribute = {
15
+ size?: number;
16
+ value: TypedArray;
17
+ constant?: boolean;
18
+ normalized?: boolean;
19
+ [key: string]: unknown;
20
+ };
21
+
22
+ export type GeometryAttributeInput = GeometryAttribute | TypedArray;
23
+ export type GeometryAttributes = Record<string, GeometryAttribute | undefined> & {
24
+ indices?: GeometryAttribute & {size: 1; value: Uint16Array | Uint32Array};
25
+ };
26
+
27
+ export type GeometryProps = {
28
+ id?: string;
29
+ topology: PrimitiveTopology;
30
+ vertexCount?: number;
31
+ attributes: Record<string, GeometryAttributeInput>;
32
+ indices?: GeometryAttributeInput;
33
+ };
34
+
35
+ let geometryId = 0;
36
+
37
+ /** A renderer-independent, typed-array-backed geometry container. */
38
+ export class Geometry {
39
+ readonly id: string;
40
+ readonly topology: PrimitiveTopology;
41
+ readonly vertexCount: number;
42
+ readonly indices?: GeometryAttribute;
43
+ readonly attributes: Record<string, GeometryAttribute>;
44
+ userData: Record<string, unknown> = {};
45
+
46
+ constructor(props: GeometryProps) {
47
+ this.id = props.id || `geometry-${++geometryId}`;
48
+ this.topology = props.topology;
49
+ this.attributes = {};
50
+
51
+ let indices = props.indices ? normalizeAttribute(props.indices) : undefined;
52
+ for (const [name, input] of Object.entries(props.attributes || {})) {
53
+ const attribute = normalizeAttribute(input);
54
+ if (name === 'POSITION' || name === 'positions') {
55
+ attribute.size ||= 3;
56
+ }
57
+ if (name === 'indices') {
58
+ if (indices) throw new Error('Geometry has multiple index attributes');
59
+ indices = attribute;
60
+ } else {
61
+ this.attributes[name] = attribute;
62
+ }
63
+ }
64
+
65
+ if (indices) {
66
+ if (!(indices.value instanceof Uint16Array || indices.value instanceof Uint32Array)) {
67
+ throw new Error('Geometry indices must be a Uint16Array or Uint32Array');
68
+ }
69
+ indices.size = 1;
70
+ this.indices = indices;
71
+ validateIndices(indices, this.attributes);
72
+ }
73
+
74
+ this.vertexCount = props.vertexCount ?? calculateVertexCount(this.attributes, this.indices);
75
+ if (!Number.isInteger(this.vertexCount) || this.vertexCount < 0) {
76
+ throw new Error('Geometry vertexCount must be a non-negative integer');
77
+ }
78
+ }
79
+
80
+ getVertexCount(): number {
81
+ return this.vertexCount;
82
+ }
83
+
84
+ getAttributes(): GeometryAttributes {
85
+ return (
86
+ this.indices ? {indices: this.indices, ...this.attributes} : this.attributes
87
+ ) as GeometryAttributes;
88
+ }
89
+ }
90
+
91
+ function normalizeAttribute(input: GeometryAttributeInput): GeometryAttribute {
92
+ const attribute = isTypedArray(input) ? {value: input} : {...input};
93
+ if (!isTypedArray(attribute.value)) {
94
+ throw new Error('Geometry attributes must contain a typed-array value');
95
+ }
96
+ if (attribute.size !== undefined && (!Number.isInteger(attribute.size) || attribute.size < 1)) {
97
+ throw new Error('Geometry attribute size must be a positive integer');
98
+ }
99
+ if (attribute.size && attribute.value.length % attribute.size !== 0) {
100
+ throw new Error('Geometry attribute length must be divisible by its size');
101
+ }
102
+ return attribute;
103
+ }
104
+
105
+ function isTypedArray(value: unknown): value is TypedArray {
106
+ return ArrayBuffer.isView(value) && !(value instanceof DataView);
107
+ }
108
+
109
+ function validateIndices(
110
+ indices: GeometryAttribute,
111
+ attributes: Record<string, GeometryAttribute>
112
+ ): void {
113
+ let maximumIndex = -1;
114
+ for (const index of indices.value) maximumIndex = Math.max(maximumIndex, Number(index));
115
+ for (const [name, attribute] of Object.entries(attributes)) {
116
+ if (!attribute.constant && attribute.size) {
117
+ const attributeVertexCount = attribute.value.length / attribute.size;
118
+ if (maximumIndex >= attributeVertexCount) {
119
+ throw new Error(
120
+ `Geometry index ${maximumIndex} exceeds ${name} vertex count ${attributeVertexCount}`
121
+ );
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ function calculateVertexCount(
128
+ attributes: Record<string, GeometryAttribute>,
129
+ indices?: GeometryAttribute
130
+ ): number {
131
+ if (indices) return indices.value.length;
132
+ let count = Infinity;
133
+ for (const attribute of Object.values(attributes)) {
134
+ if (!attribute.constant && attribute.size) {
135
+ count = Math.min(count, attribute.value.length / attribute.size);
136
+ }
137
+ }
138
+ if (!Number.isFinite(count)) throw new Error('Geometry has no countable attributes');
139
+ return count;
140
+ }