@cyclonium/canvas-3d 0.0.100
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/lib/canvas-3d.d.ts +58 -0
- package/lib/canvas-3d.js +254 -0
- package/lib/geometry.d.ts +117 -0
- package/lib/geometry.js +1503 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/types.d.ts +248 -0
- package/lib/types.js +19 -0
- package/lib/w2.d.ts +58 -0
- package/lib/w2.js +256 -0
- package/lib/w3.d.ts +17 -0
- package/lib/w3.js +269 -0
- package/package.json +32 -0
package/lib/geometry.js
ADDED
|
@@ -0,0 +1,1503 @@
|
|
|
1
|
+
import { Color, gfx, Mat4, RenderingSubMesh, Vec2, Vec3 } from 'cc';
|
|
2
|
+
import { Canvas3DLineCap, Canvas3DLineJoin } from './types.js';
|
|
3
|
+
export var PrimitiveEntryType;
|
|
4
|
+
(function (PrimitiveEntryType) {
|
|
5
|
+
PrimitiveEntryType["line"] = "line";
|
|
6
|
+
PrimitiveEntryType["box"] = "box";
|
|
7
|
+
PrimitiveEntryType["quad"] = "quad";
|
|
8
|
+
PrimitiveEntryType["cylinder"] = "cylinder";
|
|
9
|
+
PrimitiveEntryType["disc"] = "disc";
|
|
10
|
+
PrimitiveEntryType["ring"] = "ring";
|
|
11
|
+
PrimitiveEntryType["sphere"] = "sphere";
|
|
12
|
+
PrimitiveEntryType["capsule"] = "capsule";
|
|
13
|
+
})(PrimitiveEntryType || (PrimitiveEntryType = {}));
|
|
14
|
+
export const epsilon = 1e-6;
|
|
15
|
+
const defaultCircleSegments = 64;
|
|
16
|
+
const defaultSphereLatitudeSegments = 12;
|
|
17
|
+
const defaultSphereLongitudeSegments = 24;
|
|
18
|
+
const defaultCylinderRadialSegments = defaultSphereLongitudeSegments;
|
|
19
|
+
const defaultCapsuleCapSegments = 6;
|
|
20
|
+
const minCircleSegments = 3;
|
|
21
|
+
const minSphereLatitudeSegments = 2;
|
|
22
|
+
const minCapsuleCapSegments = 1;
|
|
23
|
+
const lineCapLatitudeSegments = 6;
|
|
24
|
+
const lineCapLongitudeSegments = 12;
|
|
25
|
+
export const roundSegmentRadians = Math.PI / 16;
|
|
26
|
+
const vertexStrideF = 7;
|
|
27
|
+
const strokeSegmentVertexCount = 8;
|
|
28
|
+
const vec3Caches_appendDashedLine3DStroke = createVec3Cache(3);
|
|
29
|
+
const vec3Caches_appendSolidLine3DStroke = createVec3Cache(4);
|
|
30
|
+
const vec3Caches_appendDiscFill = createVec3Cache(2);
|
|
31
|
+
const vec3Caches_appendRingFill = createVec3Cache(8);
|
|
32
|
+
const vec3Caches_appendSphereFill = createVec3Cache(2);
|
|
33
|
+
const vec3Caches_appendHemisphereWireStroke = createVec3Cache(4);
|
|
34
|
+
const vec3Caches_appendHemisphereFill = createVec3Cache(2);
|
|
35
|
+
const vec3Caches_appendCylinderFill = createVec3Cache(4);
|
|
36
|
+
export const defaultDrawOptions = {
|
|
37
|
+
depthTest: false,
|
|
38
|
+
depthWrite: false,
|
|
39
|
+
};
|
|
40
|
+
const circleTopologyCache = new Map();
|
|
41
|
+
const sphereTopologyCache = new Map();
|
|
42
|
+
const cylinderIndexTopologyCache = new Map();
|
|
43
|
+
const hemisphereTopologyCache = new Map();
|
|
44
|
+
export function createMeshGeometry() {
|
|
45
|
+
return {
|
|
46
|
+
vertices: [],
|
|
47
|
+
indices: [],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function hasGeometry(geometry) {
|
|
51
|
+
return geometry.vertices.length > 0 && geometry.indices.length > 0;
|
|
52
|
+
}
|
|
53
|
+
export function appendMeshGeometry(out, geometry) {
|
|
54
|
+
if (!hasGeometry(geometry)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const baseVertex = out.vertices.length / vertexStrideF;
|
|
58
|
+
out.vertices.push(...geometry.vertices);
|
|
59
|
+
for (const index of geometry.indices) {
|
|
60
|
+
out.indices.push(baseVertex + index);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function resolvePoint(pointOrX, y, z) {
|
|
64
|
+
if (typeof pointOrX === 'number') {
|
|
65
|
+
return new Vec3(pointOrX, y ?? 0, z ?? 0);
|
|
66
|
+
}
|
|
67
|
+
return pointOrX.clone();
|
|
68
|
+
}
|
|
69
|
+
export function setColor(out, value) {
|
|
70
|
+
if (typeof value === 'string') {
|
|
71
|
+
Color.fromHEX(out, value);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
out.set(value);
|
|
75
|
+
}
|
|
76
|
+
function getDrawOptions(state) {
|
|
77
|
+
return {
|
|
78
|
+
depthTest: state.depthTest,
|
|
79
|
+
depthWrite: state.depthWrite,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function getMaterialKey(options) {
|
|
83
|
+
return `${options.depthTest ? 1 : 0}:${options.depthWrite ? 1 : 0}`;
|
|
84
|
+
}
|
|
85
|
+
export function drawPrimitive(enqueueGeometry, baseState, entries, options) {
|
|
86
|
+
const state = createPrimitiveDrawStateWithOptions(baseState, options);
|
|
87
|
+
const mode = options?.mode ?? 'wireframe';
|
|
88
|
+
if (shouldDrawPrimitiveSolid(mode)) {
|
|
89
|
+
const geometry = createMeshGeometry();
|
|
90
|
+
appendPrimitiveEntriesFill(geometry, entries, state);
|
|
91
|
+
enqueueGeometry(geometry, getDrawOptions(state));
|
|
92
|
+
}
|
|
93
|
+
if (shouldDrawPrimitiveWireframe(mode) && state.lineWidth > 0) {
|
|
94
|
+
const geometry = createMeshGeometry();
|
|
95
|
+
appendPrimitiveEntriesStroke(geometry, entries, state);
|
|
96
|
+
enqueueGeometry(geometry, getDrawOptions(state));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function shouldDrawPrimitiveSolid(mode) {
|
|
100
|
+
return mode === 'solid' || mode === 'both';
|
|
101
|
+
}
|
|
102
|
+
function shouldDrawPrimitiveWireframe(mode) {
|
|
103
|
+
return mode === 'wireframe' || mode === 'both';
|
|
104
|
+
}
|
|
105
|
+
export function createPrimitiveDrawState() {
|
|
106
|
+
const transform = new Mat4();
|
|
107
|
+
Mat4.identity(transform);
|
|
108
|
+
return {
|
|
109
|
+
lineWidth: 1,
|
|
110
|
+
lineJoin: Canvas3DLineJoin.miter,
|
|
111
|
+
lineCap: Canvas3DLineCap.butt,
|
|
112
|
+
miterLimit: 10,
|
|
113
|
+
lineDash: [],
|
|
114
|
+
lineDashOffset: 0,
|
|
115
|
+
fillColor: Color.WHITE.clone(),
|
|
116
|
+
strokeColor: Color.WHITE.clone(),
|
|
117
|
+
transform,
|
|
118
|
+
depthTest: true,
|
|
119
|
+
depthWrite: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function clonePrimitiveDrawState(state) {
|
|
123
|
+
return copyPrimitiveDrawState(createPrimitiveDrawState(), state);
|
|
124
|
+
}
|
|
125
|
+
export function copyPrimitiveDrawState(out, state) {
|
|
126
|
+
out.lineWidth = state.lineWidth;
|
|
127
|
+
out.lineJoin = state.lineJoin;
|
|
128
|
+
out.lineCap = state.lineCap;
|
|
129
|
+
out.miterLimit = state.miterLimit;
|
|
130
|
+
out.lineDash = state.lineDash.length === 0 ? state.lineDash : state.lineDash.slice();
|
|
131
|
+
out.lineDashOffset = state.lineDashOffset;
|
|
132
|
+
out.fillColor.set(state.fillColor);
|
|
133
|
+
out.strokeColor.set(state.strokeColor);
|
|
134
|
+
out.transform.set(state.transform);
|
|
135
|
+
out.depthTest = state.depthTest;
|
|
136
|
+
out.depthWrite = state.depthWrite;
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
function createPrimitiveDrawStateWithOptions(state, options) {
|
|
140
|
+
const result = clonePrimitiveDrawState(state);
|
|
141
|
+
if (!options) {
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
if (options.color) {
|
|
145
|
+
setColor(result.fillColor, options.color);
|
|
146
|
+
setColor(result.strokeColor, options.color);
|
|
147
|
+
}
|
|
148
|
+
if (options.fillColor) {
|
|
149
|
+
setColor(result.fillColor, options.fillColor);
|
|
150
|
+
}
|
|
151
|
+
if (options.strokeColor) {
|
|
152
|
+
setColor(result.strokeColor, options.strokeColor);
|
|
153
|
+
}
|
|
154
|
+
if (options.lineWidth !== undefined) {
|
|
155
|
+
result.lineWidth = Math.max(0, options.lineWidth);
|
|
156
|
+
}
|
|
157
|
+
if (options.lineJoin !== undefined) {
|
|
158
|
+
result.lineJoin = options.lineJoin;
|
|
159
|
+
}
|
|
160
|
+
if (options.lineCap !== undefined) {
|
|
161
|
+
result.lineCap = options.lineCap;
|
|
162
|
+
}
|
|
163
|
+
if (options.lineDash !== undefined) {
|
|
164
|
+
result.lineDash = normalizeLineDash(options.lineDash);
|
|
165
|
+
}
|
|
166
|
+
if (options.lineDashOffset !== undefined) {
|
|
167
|
+
result.lineDashOffset = Number.isFinite(options.lineDashOffset) ? options.lineDashOffset : 0;
|
|
168
|
+
}
|
|
169
|
+
if (options.transform) {
|
|
170
|
+
Mat4.multiply(result.transform, result.transform, options.transform);
|
|
171
|
+
}
|
|
172
|
+
if (options.depthTest !== undefined) {
|
|
173
|
+
result.depthTest = options.depthTest;
|
|
174
|
+
}
|
|
175
|
+
if (options.depthWrite !== undefined) {
|
|
176
|
+
result.depthWrite = options.depthWrite;
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
export function resolveCapsule(options) {
|
|
181
|
+
const radius = Math.max(0, options.radius);
|
|
182
|
+
const height = Math.max(0, options.height);
|
|
183
|
+
const up = options.up?.clone() ?? Vec3.UP.clone();
|
|
184
|
+
if (up.lengthSqr() <= epsilon) {
|
|
185
|
+
up.set(Vec3.UP);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
up.normalize();
|
|
189
|
+
}
|
|
190
|
+
const halfSegmentLength = Math.max(0, height * 0.5 - radius);
|
|
191
|
+
const offset = up.multiplyScalar(halfSegmentLength);
|
|
192
|
+
return {
|
|
193
|
+
from: Vec3.subtract(new Vec3(), options.center, offset),
|
|
194
|
+
to: Vec3.add(new Vec3(), options.center, offset),
|
|
195
|
+
radius,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function resolveCylinder(options) {
|
|
199
|
+
const radius = Math.max(0, options.radius);
|
|
200
|
+
const height = Math.max(0, options.height);
|
|
201
|
+
const up = resolveDirection(options.up ?? Vec3.UP);
|
|
202
|
+
const offset = up.multiplyScalar(height * 0.5);
|
|
203
|
+
return {
|
|
204
|
+
from: Vec3.subtract(new Vec3(), options.center, offset),
|
|
205
|
+
to: Vec3.add(new Vec3(), options.center, offset),
|
|
206
|
+
radius,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
export function createQuadCorners(options) {
|
|
210
|
+
const halfExtents = resolveQuadHalfExtents(options.halfExtents);
|
|
211
|
+
const basis = createNormalBasis(options.normal);
|
|
212
|
+
const axisA = basis.axisA.multiplyScalar(halfExtents.x);
|
|
213
|
+
const axisB = basis.axisB.multiplyScalar(halfExtents.y);
|
|
214
|
+
return [
|
|
215
|
+
Vec3.subtract(new Vec3(), Vec3.subtract(new Vec3(), options.center, axisA), axisB),
|
|
216
|
+
Vec3.subtract(new Vec3(), Vec3.add(new Vec3(), options.center, axisA), axisB),
|
|
217
|
+
Vec3.add(new Vec3(), Vec3.add(new Vec3(), options.center, axisA), axisB),
|
|
218
|
+
Vec3.add(new Vec3(), Vec3.subtract(new Vec3(), options.center, axisA), axisB),
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
export function resolveDiscRadius(options) {
|
|
222
|
+
return Math.max(0, options.radius);
|
|
223
|
+
}
|
|
224
|
+
export function resolveCircleSegments(value) {
|
|
225
|
+
return clampInteger(value, defaultCircleSegments, minCircleSegments);
|
|
226
|
+
}
|
|
227
|
+
export function resolveCylinderSegments(options) {
|
|
228
|
+
const manualSegments = normalizeInteger(options.radialSegments, minCircleSegments);
|
|
229
|
+
return {
|
|
230
|
+
radialSegments: manualSegments ?? defaultCylinderRadialSegments,
|
|
231
|
+
wireSegments: manualSegments ?? defaultCircleSegments,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
export function resolveDiscSegments(options) {
|
|
235
|
+
return resolveCircleSegments(options.segments);
|
|
236
|
+
}
|
|
237
|
+
export function resolveRingSegments(options) {
|
|
238
|
+
return resolveCircleSegments(options.segments);
|
|
239
|
+
}
|
|
240
|
+
export function resolveSphereSegments(options) {
|
|
241
|
+
const manualLongitudeSegments = normalizeInteger(options.longitudeSegments ?? options.segments, minCircleSegments);
|
|
242
|
+
const longitudeSegments = manualLongitudeSegments ?? defaultSphereLongitudeSegments;
|
|
243
|
+
return {
|
|
244
|
+
latitudeSegments: clampInteger(options.latitudeSegments ?? (options.segments === undefined ? defaultSphereLatitudeSegments : Math.ceil(longitudeSegments * 0.5)), defaultSphereLatitudeSegments, minSphereLatitudeSegments),
|
|
245
|
+
longitudeSegments,
|
|
246
|
+
wireSegments: manualLongitudeSegments ?? defaultCircleSegments,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export function resolveCapsuleSegments(options) {
|
|
250
|
+
const manualSegments = normalizeInteger(options.radialSegments, minCircleSegments);
|
|
251
|
+
return {
|
|
252
|
+
radialSegments: manualSegments ?? defaultCylinderRadialSegments,
|
|
253
|
+
wireSegments: manualSegments ?? defaultCircleSegments,
|
|
254
|
+
capSegments: clampInteger(options.capSegments, defaultCapsuleCapSegments, minCapsuleCapSegments),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
export function resolveRingRadii(options) {
|
|
258
|
+
return {
|
|
259
|
+
innerRadius: Math.max(0, options.innerRadius),
|
|
260
|
+
outerRadius: Math.max(0, options.outerRadius),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export function resolveQuadHalfExtents(halfExtents) {
|
|
264
|
+
if (typeof halfExtents === 'number') {
|
|
265
|
+
const value = Math.max(0, halfExtents);
|
|
266
|
+
return new Vec2(value, value);
|
|
267
|
+
}
|
|
268
|
+
return new Vec2(Math.max(0, halfExtents.x), Math.max(0, halfExtents.y));
|
|
269
|
+
}
|
|
270
|
+
function clampInteger(value, fallback, min) {
|
|
271
|
+
return normalizeInteger(value, min) ?? fallback;
|
|
272
|
+
}
|
|
273
|
+
function normalizeInteger(value, min) {
|
|
274
|
+
if (value === undefined || !Number.isFinite(value)) {
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
return Math.max(min, Math.floor(value));
|
|
278
|
+
}
|
|
279
|
+
export function normalizeLineDash(value) {
|
|
280
|
+
const lineDash = value.filter((entry) => {
|
|
281
|
+
return Number.isFinite(entry) && entry > epsilon;
|
|
282
|
+
});
|
|
283
|
+
if (lineDash.length === 0 || sum(lineDash) <= epsilon) {
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
286
|
+
if (lineDash.length % 2 === 1) {
|
|
287
|
+
lineDash.push(...lineDash);
|
|
288
|
+
}
|
|
289
|
+
return lineDash;
|
|
290
|
+
}
|
|
291
|
+
export function appendSubPathStroke(geometry, subPath, style, color) {
|
|
292
|
+
const points = normalizedSubPathPoints(subPath);
|
|
293
|
+
if (appendPlanarPolylineStroke(geometry, points, subPath.closed, style, color)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const segmentCount = countSubPathStrokeSegments(points, subPath.closed);
|
|
297
|
+
const writer = createStrokeSegmentWriter(geometry, segmentCount);
|
|
298
|
+
if (!writer) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
for (let iPoint = 1; iPoint < points.length; iPoint++) {
|
|
302
|
+
const from = points[iPoint - 1];
|
|
303
|
+
const to = points[iPoint];
|
|
304
|
+
if (hasStrokeSegmentLength(from, to)) {
|
|
305
|
+
appendStrokeSegment(writer, from, to, style.lineWidth, color);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (subPath.closed && points.length > 2) {
|
|
309
|
+
const from = points[points.length - 1];
|
|
310
|
+
const to = points[0];
|
|
311
|
+
if (hasStrokeSegmentLength(from, to)) {
|
|
312
|
+
appendStrokeSegment(writer, from, to, style.lineWidth, color);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
export function appendSubPathFill(geometry, subPath, color) {
|
|
317
|
+
const points = normalizedSubPathPoints(subPath);
|
|
318
|
+
if (points.length < 3) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
322
|
+
for (const point of points) {
|
|
323
|
+
appendVertex(geometry, point, color);
|
|
324
|
+
}
|
|
325
|
+
for (let iPoint = 1; iPoint < points.length - 1; iPoint++) {
|
|
326
|
+
geometry.indices.push(baseVertex, baseVertex + iPoint, baseVertex + iPoint + 1);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
export function appendBoxStroke(geometry, center, halfExtents, style, color) {
|
|
330
|
+
const corners = createBoxCorners(center, halfExtents);
|
|
331
|
+
if (Math.abs(halfExtents.z) <= epsilon && appendPlanarPolylineStroke(geometry, corners.slice(0, 4), true, style, color)) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const segmentCount = countBoxStrokeSegments(corners);
|
|
335
|
+
const writer = createStrokeSegmentWriter(geometry, segmentCount);
|
|
336
|
+
if (!writer) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
for (const [fromIndex, toIndex] of boxEdges) {
|
|
340
|
+
const from = corners[fromIndex];
|
|
341
|
+
const to = corners[toIndex];
|
|
342
|
+
if (hasStrokeSegmentLength(from, to)) {
|
|
343
|
+
appendStrokeSegment(writer, from, to, style.lineWidth, color);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
export function appendBoxFill(geometry, center, halfExtents, color) {
|
|
348
|
+
const corners = createBoxCorners(center, halfExtents);
|
|
349
|
+
appendBoxCornersFill(geometry, corners, color);
|
|
350
|
+
}
|
|
351
|
+
function appendBoxCornersStroke(geometry, corners, style, color) {
|
|
352
|
+
const segmentCount = countBoxStrokeSegments(corners);
|
|
353
|
+
const writer = createStrokeSegmentWriter(geometry, segmentCount);
|
|
354
|
+
if (!writer) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
for (const [fromIndex, toIndex] of boxEdges) {
|
|
358
|
+
const from = corners[fromIndex];
|
|
359
|
+
const to = corners[toIndex];
|
|
360
|
+
if (hasStrokeSegmentLength(from, to)) {
|
|
361
|
+
appendStrokeSegment(writer, from, to, style.lineWidth, color);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function appendBoxCornersFill(geometry, corners, color) {
|
|
366
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
367
|
+
for (const corner of corners) {
|
|
368
|
+
appendVertex(geometry, corner, color);
|
|
369
|
+
}
|
|
370
|
+
for (const index of boxTriangleIndices) {
|
|
371
|
+
geometry.indices.push(baseVertex + index);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function appendPrimitiveEntriesStroke(geometry, entries, state) {
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
switch (entry.type) {
|
|
377
|
+
case PrimitiveEntryType.line:
|
|
378
|
+
appendLine3DStroke(geometry, transformPoint(entry.from, state.transform), transformPoint(entry.to, state.transform), state, state.strokeColor);
|
|
379
|
+
break;
|
|
380
|
+
case PrimitiveEntryType.box:
|
|
381
|
+
appendBoxCornersStroke(geometry, transformPoints(createBoxCorners(entry.center, entry.halfExtents), state.transform), state, state.strokeColor);
|
|
382
|
+
break;
|
|
383
|
+
case PrimitiveEntryType.quad:
|
|
384
|
+
appendQuadCornersStroke(geometry, transformPoints(entry.corners, state.transform), state, state.strokeColor);
|
|
385
|
+
break;
|
|
386
|
+
case PrimitiveEntryType.cylinder:
|
|
387
|
+
appendCylinderWireStroke(geometry, entry.from, entry.to, entry.radius, entry.wireSegments, state, state.strokeColor);
|
|
388
|
+
break;
|
|
389
|
+
case PrimitiveEntryType.disc:
|
|
390
|
+
appendDiscWireStroke(geometry, entry.center, entry.normal, entry.radius, entry.segments, state, state.strokeColor);
|
|
391
|
+
break;
|
|
392
|
+
case PrimitiveEntryType.ring:
|
|
393
|
+
appendRingWireStroke(geometry, entry.center, entry.normal, entry.innerRadius, entry.outerRadius, entry.segments, state, state.strokeColor);
|
|
394
|
+
break;
|
|
395
|
+
case PrimitiveEntryType.sphere:
|
|
396
|
+
appendSphereWireStroke(geometry, entry.center, entry.radius, entry.wireSegments, state, state.strokeColor);
|
|
397
|
+
break;
|
|
398
|
+
case PrimitiveEntryType.capsule:
|
|
399
|
+
appendCapsuleWireStroke(geometry, entry.from, entry.to, entry.radius, entry.wireSegments, entry.capSegments, state, state.strokeColor);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function appendPrimitiveEntriesFill(geometry, entries, state) {
|
|
405
|
+
for (const entry of entries) {
|
|
406
|
+
switch (entry.type) {
|
|
407
|
+
case PrimitiveEntryType.line:
|
|
408
|
+
break;
|
|
409
|
+
case PrimitiveEntryType.box:
|
|
410
|
+
appendBoxCornersFill(geometry, transformPoints(createBoxCorners(entry.center, entry.halfExtents), state.transform), state.fillColor);
|
|
411
|
+
break;
|
|
412
|
+
case PrimitiveEntryType.quad:
|
|
413
|
+
appendQuadCornersFill(geometry, transformPoints(entry.corners, state.transform), state.fillColor);
|
|
414
|
+
break;
|
|
415
|
+
case PrimitiveEntryType.cylinder:
|
|
416
|
+
appendCylinderFill(geometry, entry.from, entry.to, entry.radius, entry.radialSegments, state.transform, state.fillColor);
|
|
417
|
+
break;
|
|
418
|
+
case PrimitiveEntryType.disc:
|
|
419
|
+
appendDiscFill(geometry, entry.center, entry.normal, entry.radius, entry.segments, state.transform, state.fillColor);
|
|
420
|
+
break;
|
|
421
|
+
case PrimitiveEntryType.ring:
|
|
422
|
+
appendRingFill(geometry, entry.center, entry.normal, entry.innerRadius, entry.outerRadius, entry.segments, state.transform, state.fillColor);
|
|
423
|
+
break;
|
|
424
|
+
case PrimitiveEntryType.sphere:
|
|
425
|
+
appendSphereFill(geometry, entry.center, entry.radius, entry.latitudeSegments, entry.longitudeSegments, state.transform, state.fillColor);
|
|
426
|
+
break;
|
|
427
|
+
case PrimitiveEntryType.capsule:
|
|
428
|
+
appendCapsuleFill(geometry, entry.from, entry.to, entry.radius, entry.radialSegments, entry.capSegments, state.transform, state.fillColor);
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function appendLine3DStroke(geometry, from, to, style, color) {
|
|
434
|
+
if (!hasStrokeSegmentLength(from, to)) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
if (style.lineDash.length > 0) {
|
|
438
|
+
appendDashedLine3DStroke(geometry, from, to, style, color);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
appendSolidLine3DStroke(geometry, from, to, style, color);
|
|
442
|
+
}
|
|
443
|
+
function appendDashedLine3DStroke(geometry, from, to, style, color) {
|
|
444
|
+
const segmentLength = Vec3.distance(from, to);
|
|
445
|
+
if (segmentLength <= epsilon) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendDashedLine3DStroke);
|
|
449
|
+
let pVec3Cache = 0;
|
|
450
|
+
const direction = Vec3.subtract(vec3Cache[pVec3Cache++], to, from).normalize();
|
|
451
|
+
const dashState = createDashState(style.lineDash, style.lineDashOffset);
|
|
452
|
+
const startPoint = vec3Cache[pVec3Cache++];
|
|
453
|
+
const endPoint = vec3Cache[pVec3Cache++];
|
|
454
|
+
let traveled = 0;
|
|
455
|
+
while (segmentLength - traveled > epsilon) {
|
|
456
|
+
const step = Math.min(segmentLength - traveled, dashState.remaining);
|
|
457
|
+
if (dashState.drawing) {
|
|
458
|
+
Vec3.scaleAndAdd(startPoint, from, direction, traveled);
|
|
459
|
+
Vec3.scaleAndAdd(endPoint, from, direction, traveled + step);
|
|
460
|
+
appendSolidLine3DStroke(geometry, startPoint, endPoint, style, color);
|
|
461
|
+
}
|
|
462
|
+
traveled += step;
|
|
463
|
+
dashState.remaining -= step;
|
|
464
|
+
if (dashState.remaining <= epsilon) {
|
|
465
|
+
advanceDashState(dashState);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
releaseVec3ScratchCache(vec3Caches_appendDashedLine3DStroke);
|
|
469
|
+
}
|
|
470
|
+
function appendSolidLine3DStroke(geometry, from, to, style, color) {
|
|
471
|
+
if (!hasStrokeSegmentLength(from, to)) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const writer = createStrokeSegmentWriter(geometry, 1);
|
|
475
|
+
if (!writer) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendSolidLine3DStroke);
|
|
479
|
+
let pVec3Cache = 0;
|
|
480
|
+
const start = vec3Cache[pVec3Cache++].set(from);
|
|
481
|
+
const end = vec3Cache[pVec3Cache++].set(to);
|
|
482
|
+
const direction = Vec3.subtract(vec3Cache[pVec3Cache++], end, start).normalize();
|
|
483
|
+
const halfWidth = style.lineWidth * 0.5;
|
|
484
|
+
if (style.lineCap === Canvas3DLineCap.square) {
|
|
485
|
+
Vec3.scaleAndAdd(start, start, direction, -halfWidth);
|
|
486
|
+
Vec3.scaleAndAdd(end, end, direction, halfWidth);
|
|
487
|
+
}
|
|
488
|
+
appendStrokeSegment(writer, start, end, style.lineWidth, color);
|
|
489
|
+
releaseVec3ScratchCache(vec3Caches_appendSolidLine3DStroke);
|
|
490
|
+
if (style.lineCap === Canvas3DLineCap.round) {
|
|
491
|
+
const transform = new Mat4();
|
|
492
|
+
Mat4.identity(transform);
|
|
493
|
+
appendSphereFill(geometry, from, halfWidth, lineCapLatitudeSegments, lineCapLongitudeSegments, transform, color);
|
|
494
|
+
appendSphereFill(geometry, to, halfWidth, lineCapLatitudeSegments, lineCapLongitudeSegments, transform, color);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function appendQuadCornersStroke(geometry, corners, style, color) {
|
|
498
|
+
if (corners.length < 4) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
appendSubPathStroke(geometry, {
|
|
502
|
+
points: corners.slice(0, 4),
|
|
503
|
+
closed: true,
|
|
504
|
+
}, style, color);
|
|
505
|
+
}
|
|
506
|
+
function appendQuadCornersFill(geometry, corners, color) {
|
|
507
|
+
if (corners.length < 4) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
appendQuad(geometry, corners[0], corners[1], corners[2], corners[3], color);
|
|
511
|
+
}
|
|
512
|
+
function appendDiscWireStroke(geometry, center, normal, radius, segments, state, color) {
|
|
513
|
+
if (radius <= 0) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const basis = createNormalBasis(normal);
|
|
517
|
+
appendPlaneCircleStroke(geometry, center, radius, basis.axisA, basis.axisB, segments, state, color);
|
|
518
|
+
}
|
|
519
|
+
function appendDiscFill(geometry, center, normal, radius, segments, transform, color) {
|
|
520
|
+
if (radius <= 0) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendDiscFill);
|
|
524
|
+
let pVec3Cache = 0;
|
|
525
|
+
const point = vec3Cache[pVec3Cache++];
|
|
526
|
+
const transformedPoint = vec3Cache[pVec3Cache++];
|
|
527
|
+
const basis = createNormalBasis(normal);
|
|
528
|
+
const centerVertex = appendVertex(geometry, transformPointInto(transformedPoint, center, transform), color);
|
|
529
|
+
const firstRingVertex = geometry.vertices.length / vertexStrideF;
|
|
530
|
+
for (let iPoint = 0; iPoint < segments; iPoint++) {
|
|
531
|
+
setPlaneCirclePoint(point, center, radius, basis.axisA, basis.axisB, iPoint, segments);
|
|
532
|
+
appendVertex(geometry, transformPointInto(transformedPoint, point, transform), color);
|
|
533
|
+
}
|
|
534
|
+
releaseVec3ScratchCache(vec3Caches_appendDiscFill);
|
|
535
|
+
for (let iPoint = 0; iPoint < segments; iPoint++) {
|
|
536
|
+
geometry.indices.push(centerVertex, firstRingVertex + iPoint, firstRingVertex + ((iPoint + 1) % segments));
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function appendRingWireStroke(geometry, center, normal, innerRadius, outerRadius, segments, state, color) {
|
|
540
|
+
if (outerRadius <= 0) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const basis = createNormalBasis(normal);
|
|
544
|
+
appendPlaneCircleStroke(geometry, center, outerRadius, basis.axisA, basis.axisB, segments, state, color);
|
|
545
|
+
if (innerRadius > epsilon && innerRadius < outerRadius) {
|
|
546
|
+
appendPlaneCircleStroke(geometry, center, innerRadius, basis.axisA, basis.axisB, segments, state, color);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
function appendRingFill(geometry, center, normal, innerRadius, outerRadius, segments, transform, color) {
|
|
550
|
+
if (outerRadius <= 0) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (innerRadius <= epsilon) {
|
|
554
|
+
appendDiscFill(geometry, center, normal, outerRadius, segments, transform, color);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (innerRadius >= outerRadius) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendRingFill);
|
|
561
|
+
const basis = createNormalBasis(normal);
|
|
562
|
+
let pVec3Cache = 0;
|
|
563
|
+
const outerPoint = vec3Cache[pVec3Cache++];
|
|
564
|
+
const innerPoint = vec3Cache[pVec3Cache++];
|
|
565
|
+
const innerNextPoint = vec3Cache[pVec3Cache++];
|
|
566
|
+
const outerNextPoint = vec3Cache[pVec3Cache++];
|
|
567
|
+
const transformedOuterPoint = vec3Cache[pVec3Cache++];
|
|
568
|
+
const transformedInnerPoint = vec3Cache[pVec3Cache++];
|
|
569
|
+
const transformedInnerNextPoint = vec3Cache[pVec3Cache++];
|
|
570
|
+
const transformedOuterNextPoint = vec3Cache[pVec3Cache++];
|
|
571
|
+
for (let iPoint = 0; iPoint < segments; iPoint++) {
|
|
572
|
+
const nextPoint = (iPoint + 1) % segments;
|
|
573
|
+
setPlaneCirclePoint(outerPoint, center, outerRadius, basis.axisA, basis.axisB, iPoint, segments);
|
|
574
|
+
setPlaneCirclePoint(innerPoint, center, innerRadius, basis.axisA, basis.axisB, iPoint, segments);
|
|
575
|
+
setPlaneCirclePoint(innerNextPoint, center, innerRadius, basis.axisA, basis.axisB, nextPoint, segments);
|
|
576
|
+
setPlaneCirclePoint(outerNextPoint, center, outerRadius, basis.axisA, basis.axisB, nextPoint, segments);
|
|
577
|
+
appendQuad(geometry, transformPointInto(transformedOuterPoint, outerPoint, transform), transformPointInto(transformedInnerPoint, innerPoint, transform), transformPointInto(transformedInnerNextPoint, innerNextPoint, transform), transformPointInto(transformedOuterNextPoint, outerNextPoint, transform), color);
|
|
578
|
+
}
|
|
579
|
+
releaseVec3ScratchCache(vec3Caches_appendRingFill);
|
|
580
|
+
}
|
|
581
|
+
function appendSphereWireStroke(geometry, center, radius, segments, state, color) {
|
|
582
|
+
if (radius <= 0) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
appendPlaneCircleStroke(geometry, center, radius, new Vec3(1, 0, 0), new Vec3(0, 1, 0), segments, state, color);
|
|
586
|
+
appendPlaneCircleStroke(geometry, center, radius, new Vec3(1, 0, 0), new Vec3(0, 0, 1), segments, state, color);
|
|
587
|
+
appendPlaneCircleStroke(geometry, center, radius, new Vec3(0, 1, 0), new Vec3(0, 0, 1), segments, state, color);
|
|
588
|
+
}
|
|
589
|
+
function appendSphereFill(geometry, center, radius, latitudeSegments, longitudeSegments, transform, color) {
|
|
590
|
+
if (radius <= 0) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendSphereFill);
|
|
594
|
+
let pVec3Cache = 0;
|
|
595
|
+
const point = vec3Cache[pVec3Cache++];
|
|
596
|
+
const transformedPoint = vec3Cache[pVec3Cache++];
|
|
597
|
+
const topology = getSphereTopology(latitudeSegments, longitudeSegments);
|
|
598
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
599
|
+
for (let pPosition = 0; pPosition < topology.positions.length; pPosition += 3) {
|
|
600
|
+
point.set(center.x + topology.positions[pPosition] * radius, center.y + topology.positions[pPosition + 1] * radius, center.z + topology.positions[pPosition + 2] * radius);
|
|
601
|
+
appendVertex(geometry, transformPointInto(transformedPoint, point, transform), color);
|
|
602
|
+
}
|
|
603
|
+
releaseVec3ScratchCache(vec3Caches_appendSphereFill);
|
|
604
|
+
appendTopologyIndices(geometry, baseVertex, topology.indices);
|
|
605
|
+
}
|
|
606
|
+
function appendCapsuleWireStroke(geometry, from, to, radius, radialSegments, capSegments, state, color) {
|
|
607
|
+
if (radius <= 0) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (!hasStrokeSegmentLength(from, to)) {
|
|
611
|
+
appendSphereWireStroke(geometry, from, radius, radialSegments, state, color);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const direction = Vec3.subtract(new Vec3(), to, from).normalize();
|
|
615
|
+
appendCylinderWireStroke(geometry, from, to, radius, radialSegments, state, color);
|
|
616
|
+
appendHemisphereWireStroke(geometry, from, direction.clone().multiplyScalar(-1), radius, radialSegments, capSegments, state, color);
|
|
617
|
+
appendHemisphereWireStroke(geometry, to, direction, radius, radialSegments, capSegments, state, color);
|
|
618
|
+
}
|
|
619
|
+
function appendCapsuleFill(geometry, from, to, radius, radialSegments, capSegments, transform, color) {
|
|
620
|
+
if (radius <= 0) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (!hasStrokeSegmentLength(from, to)) {
|
|
624
|
+
appendSphereFill(geometry, from, radius, capSegments * 2, radialSegments, transform, color);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const direction = Vec3.subtract(new Vec3(), to, from).normalize();
|
|
628
|
+
appendCylinderFill(geometry, from, to, radius, radialSegments, transform, color, false);
|
|
629
|
+
appendHemisphereFill(geometry, from, direction.clone().multiplyScalar(-1), radius, radialSegments, capSegments, transform, color);
|
|
630
|
+
appendHemisphereFill(geometry, to, direction, radius, radialSegments, capSegments, transform, color);
|
|
631
|
+
}
|
|
632
|
+
function appendHemisphereWireStroke(geometry, center, axis, radius, radialSegments, capSegments, state, color) {
|
|
633
|
+
const basis = createDirectionBasis(axis);
|
|
634
|
+
if (!basis) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendHemisphereWireStroke);
|
|
638
|
+
let pVec3Cache = 0;
|
|
639
|
+
const radialDirection = vec3Cache[pVec3Cache++];
|
|
640
|
+
const point = vec3Cache[pVec3Cache++];
|
|
641
|
+
const transformedPoint = vec3Cache[pVec3Cache++];
|
|
642
|
+
const ringCenter = vec3Cache[pVec3Cache++];
|
|
643
|
+
for (let iMeridian = 0; iMeridian < 4; iMeridian++) {
|
|
644
|
+
const phi = iMeridian / 4 * Math.PI * 2;
|
|
645
|
+
setBasisDirection(radialDirection, basis.normalA, basis.normalB, phi);
|
|
646
|
+
const points = [];
|
|
647
|
+
for (let iCap = 0; iCap <= capSegments; iCap++) {
|
|
648
|
+
const theta = iCap / capSegments * Math.PI * 0.5;
|
|
649
|
+
setHemispherePoint(point, center, radius, axis, radialDirection, theta);
|
|
650
|
+
points.push(transformPointInto(transformedPoint, point, state.transform).clone());
|
|
651
|
+
}
|
|
652
|
+
appendSubPathStroke(geometry, {
|
|
653
|
+
points,
|
|
654
|
+
closed: false,
|
|
655
|
+
}, state, color);
|
|
656
|
+
}
|
|
657
|
+
if (capSegments > 1) {
|
|
658
|
+
for (let iCap = 1; iCap < capSegments; iCap++) {
|
|
659
|
+
const theta = iCap / capSegments * Math.PI * 0.5;
|
|
660
|
+
Vec3.scaleAndAdd(ringCenter, center, axis, Math.cos(theta) * radius);
|
|
661
|
+
appendPlaneCircleStroke(geometry, ringCenter, Math.sin(theta) * radius, basis.normalA, basis.normalB, radialSegments, state, color);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
releaseVec3ScratchCache(vec3Caches_appendHemisphereWireStroke);
|
|
665
|
+
}
|
|
666
|
+
function appendHemisphereFill(geometry, center, axis, radius, radialSegments, capSegments, transform, color) {
|
|
667
|
+
const basis = createDirectionBasis(axis);
|
|
668
|
+
if (!basis) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendHemisphereFill);
|
|
672
|
+
let pVec3Cache = 0;
|
|
673
|
+
const point = vec3Cache[pVec3Cache++];
|
|
674
|
+
const transformedPoint = vec3Cache[pVec3Cache++];
|
|
675
|
+
const topology = getHemisphereTopology(radialSegments, capSegments);
|
|
676
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
677
|
+
for (let pPosition = 0; pPosition < topology.positions.length; pPosition += 3) {
|
|
678
|
+
const radialX = topology.positions[pPosition];
|
|
679
|
+
const radialY = topology.positions[pPosition + 1];
|
|
680
|
+
const axisScale = topology.positions[pPosition + 2];
|
|
681
|
+
point.set(center.x + (basis.normalA.x * radialX + basis.normalB.x * radialY + axis.x * axisScale) * radius, center.y + (basis.normalA.y * radialX + basis.normalB.y * radialY + axis.y * axisScale) * radius, center.z + (basis.normalA.z * radialX + basis.normalB.z * radialY + axis.z * axisScale) * radius);
|
|
682
|
+
appendVertex(geometry, transformPointInto(transformedPoint, point, transform), color);
|
|
683
|
+
}
|
|
684
|
+
releaseVec3ScratchCache(vec3Caches_appendHemisphereFill);
|
|
685
|
+
appendTopologyIndices(geometry, baseVertex, topology.indices);
|
|
686
|
+
}
|
|
687
|
+
function appendCylinderWireStroke(geometry, from, to, radius, radialSegments, state, color) {
|
|
688
|
+
const basis = createAxisBasis(from, to);
|
|
689
|
+
if (!basis) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
appendPlaneCircleStroke(geometry, from, radius, basis.normalA, basis.normalB, radialSegments, state, color);
|
|
693
|
+
appendPlaneCircleStroke(geometry, to, radius, basis.normalA, basis.normalB, radialSegments, state, color);
|
|
694
|
+
for (let iSide = 0; iSide < 4; iSide++) {
|
|
695
|
+
const iPoint = Math.floor(iSide * radialSegments / 4);
|
|
696
|
+
appendLine3DStroke(geometry, transformPoint(createPlaneCirclePoint(from, radius, basis.normalA, basis.normalB, iPoint, radialSegments), state.transform), transformPoint(createPlaneCirclePoint(to, radius, basis.normalA, basis.normalB, iPoint, radialSegments), state.transform), state, color);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
function appendCylinderFill(geometry, from, to, radius, radialSegments, transform, color, includeCaps = true) {
|
|
700
|
+
const basis = createAxisBasis(from, to);
|
|
701
|
+
if (!basis) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const vec3Cache = borrowVec3ScratchCache(vec3Caches_appendCylinderFill);
|
|
705
|
+
let pVec3Cache = 0;
|
|
706
|
+
const point = vec3Cache[pVec3Cache++];
|
|
707
|
+
const transformedPoint = vec3Cache[pVec3Cache++];
|
|
708
|
+
const topology = getCircleTopology(radialSegments);
|
|
709
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
710
|
+
for (let iPoint = 0; iPoint < radialSegments; iPoint++) {
|
|
711
|
+
setPlaneCirclePoint(point, from, radius, basis.normalA, basis.normalB, iPoint, radialSegments);
|
|
712
|
+
appendVertex(geometry, transformPointInto(transformedPoint, point, transform), color);
|
|
713
|
+
}
|
|
714
|
+
for (let iPoint = 0; iPoint < radialSegments; iPoint++) {
|
|
715
|
+
point.set(to.x + (basis.normalA.x * topology.cos[iPoint] + basis.normalB.x * topology.sin[iPoint]) * radius, to.y + (basis.normalA.y * topology.cos[iPoint] + basis.normalB.y * topology.sin[iPoint]) * radius, to.z + (basis.normalA.z * topology.cos[iPoint] + basis.normalB.z * topology.sin[iPoint]) * radius);
|
|
716
|
+
appendVertex(geometry, transformPointInto(transformedPoint, point, transform), color);
|
|
717
|
+
}
|
|
718
|
+
if (includeCaps) {
|
|
719
|
+
appendVertex(geometry, transformPointInto(transformedPoint, from, transform), color);
|
|
720
|
+
appendVertex(geometry, transformPointInto(transformedPoint, to, transform), color);
|
|
721
|
+
}
|
|
722
|
+
releaseVec3ScratchCache(vec3Caches_appendCylinderFill);
|
|
723
|
+
appendTopologyIndices(geometry, baseVertex, getCylinderIndexTopology(radialSegments, includeCaps).indices);
|
|
724
|
+
}
|
|
725
|
+
function appendPlaneCircleStroke(geometry, center, radius, axisA, axisB, segments, state, color) {
|
|
726
|
+
const points = [];
|
|
727
|
+
for (let iPoint = 0; iPoint < segments; iPoint++) {
|
|
728
|
+
points.push(transformPoint(createPlaneCirclePoint(center, radius, axisA, axisB, iPoint, segments), state.transform));
|
|
729
|
+
}
|
|
730
|
+
appendSubPathStroke(geometry, {
|
|
731
|
+
points,
|
|
732
|
+
closed: true,
|
|
733
|
+
}, state, color);
|
|
734
|
+
}
|
|
735
|
+
export function appendCircleStroke(geometry, center, radius, style, color) {
|
|
736
|
+
if (radius <= 0) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (style.lineDash.length > 0) {
|
|
740
|
+
appendPlanarPolylineStroke(geometry, createCirclePoints(center, radius), true, style, color);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const innerRadius = Math.max(0, radius - style.lineWidth * 0.5);
|
|
744
|
+
const outerRadius = radius + style.lineWidth * 0.5;
|
|
745
|
+
appendCircleRingStroke(geometry, center, innerRadius, outerRadius, color);
|
|
746
|
+
}
|
|
747
|
+
function appendCircleRingStroke(geometry, center, innerRadius, outerRadius, color) {
|
|
748
|
+
if (outerRadius <= 0) {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (innerRadius <= epsilon) {
|
|
752
|
+
appendCircleFill(geometry, center, outerRadius, color);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const outerPoint = new Vec3();
|
|
756
|
+
const innerPoint = new Vec3();
|
|
757
|
+
for (let iPoint = 0; iPoint < defaultCircleSegments; iPoint++) {
|
|
758
|
+
setCirclePoint(outerPoint, center, outerRadius, iPoint);
|
|
759
|
+
setCirclePoint(innerPoint, center, innerRadius, iPoint);
|
|
760
|
+
const nextPoint = (iPoint + 1) % defaultCircleSegments;
|
|
761
|
+
const outerNext = createCirclePoint(center, outerRadius, nextPoint);
|
|
762
|
+
const innerNext = createCirclePoint(center, innerRadius, nextPoint);
|
|
763
|
+
appendQuad(geometry, outerPoint, innerPoint, innerNext, outerNext, color);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
export function appendCircleFill(geometry, center, radius, color) {
|
|
767
|
+
if (radius <= 0) {
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const centerVertex = appendVertex(geometry, center, color);
|
|
771
|
+
const firstRingVertex = geometry.vertices.length / vertexStrideF;
|
|
772
|
+
for (let iPoint = 0; iPoint < defaultCircleSegments; iPoint++) {
|
|
773
|
+
appendVertex(geometry, createCirclePoint(center, radius, iPoint), color);
|
|
774
|
+
}
|
|
775
|
+
for (let iPoint = 0; iPoint < defaultCircleSegments; iPoint++) {
|
|
776
|
+
geometry.indices.push(centerVertex, firstRingVertex + iPoint, firstRingVertex + ((iPoint + 1) % defaultCircleSegments));
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function createStrokeSegmentWriter(geometry, segmentCount) {
|
|
780
|
+
if (segmentCount <= 0) {
|
|
781
|
+
return undefined;
|
|
782
|
+
}
|
|
783
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
784
|
+
const vertexOffsetF = geometry.vertices.length;
|
|
785
|
+
const indexOffset = geometry.indices.length;
|
|
786
|
+
geometry.vertices.length += segmentCount * strokeSegmentVertexCount * vertexStrideF;
|
|
787
|
+
geometry.indices.length += segmentCount * strokeBoxTriangleIndices.length;
|
|
788
|
+
return {
|
|
789
|
+
geometry,
|
|
790
|
+
direction: new Vec3(),
|
|
791
|
+
normalA: new Vec3(),
|
|
792
|
+
normalB: new Vec3(),
|
|
793
|
+
vertexOffsetF,
|
|
794
|
+
indexOffset,
|
|
795
|
+
baseVertex,
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function appendStrokeSegment(writer, from, to, strokeWidth, color) {
|
|
799
|
+
const direction = Vec3.subtract(writer.direction, to, from);
|
|
800
|
+
if (direction.lengthSqr() <= epsilon) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
direction.normalize();
|
|
804
|
+
const halfWidth = strokeWidth * 0.5;
|
|
805
|
+
const referenceAxis = Math.abs(Vec3.dot(direction, Vec3.UP)) > 0.95 ? Vec3.RIGHT : Vec3.UP;
|
|
806
|
+
const normalA = Vec3.cross(writer.normalA, direction, referenceAxis).normalize();
|
|
807
|
+
const normalB = Vec3.cross(writer.normalB, direction, normalA).normalize();
|
|
808
|
+
normalA.multiplyScalar(halfWidth);
|
|
809
|
+
normalB.multiplyScalar(halfWidth);
|
|
810
|
+
const ax = normalA.x;
|
|
811
|
+
const ay = normalA.y;
|
|
812
|
+
const az = normalA.z;
|
|
813
|
+
const bx = normalB.x;
|
|
814
|
+
const by = normalB.y;
|
|
815
|
+
const bz = normalB.z;
|
|
816
|
+
writeStrokeVertex(writer, from, ax + bx, ay + by, az + bz, color);
|
|
817
|
+
writeStrokeVertex(writer, from, ax - bx, ay - by, az - bz, color);
|
|
818
|
+
writeStrokeVertex(writer, from, -ax - bx, -ay - by, -az - bz, color);
|
|
819
|
+
writeStrokeVertex(writer, from, bx - ax, by - ay, bz - az, color);
|
|
820
|
+
writeStrokeVertex(writer, to, ax + bx, ay + by, az + bz, color);
|
|
821
|
+
writeStrokeVertex(writer, to, ax - bx, ay - by, az - bz, color);
|
|
822
|
+
writeStrokeVertex(writer, to, -ax - bx, -ay - by, -az - bz, color);
|
|
823
|
+
writeStrokeVertex(writer, to, bx - ax, by - ay, bz - az, color);
|
|
824
|
+
for (const index of strokeBoxTriangleIndices) {
|
|
825
|
+
writer.geometry.indices[writer.indexOffset++] = writer.baseVertex + index;
|
|
826
|
+
}
|
|
827
|
+
writer.baseVertex += strokeSegmentVertexCount;
|
|
828
|
+
}
|
|
829
|
+
function writeStrokeVertex(writer, center, offsetX, offsetY, offsetZ, color) {
|
|
830
|
+
const vertices = writer.geometry.vertices;
|
|
831
|
+
let offset = writer.vertexOffsetF;
|
|
832
|
+
vertices[offset++] = center.x + offsetX;
|
|
833
|
+
vertices[offset++] = center.y + offsetY;
|
|
834
|
+
vertices[offset++] = center.z + offsetZ;
|
|
835
|
+
vertices[offset++] = color.x;
|
|
836
|
+
vertices[offset++] = color.y;
|
|
837
|
+
vertices[offset++] = color.z;
|
|
838
|
+
vertices[offset++] = color.w;
|
|
839
|
+
writer.vertexOffsetF = offset;
|
|
840
|
+
}
|
|
841
|
+
function countSubPathStrokeSegments(points, closed) {
|
|
842
|
+
let segmentCount = 0;
|
|
843
|
+
for (let iPoint = 1; iPoint < points.length; iPoint++) {
|
|
844
|
+
if (hasStrokeSegmentLength(points[iPoint - 1], points[iPoint])) {
|
|
845
|
+
segmentCount++;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (closed && points.length > 2 && hasStrokeSegmentLength(points[points.length - 1], points[0])) {
|
|
849
|
+
segmentCount++;
|
|
850
|
+
}
|
|
851
|
+
return segmentCount;
|
|
852
|
+
}
|
|
853
|
+
function countBoxStrokeSegments(corners) {
|
|
854
|
+
let segmentCount = 0;
|
|
855
|
+
for (const [fromIndex, toIndex] of boxEdges) {
|
|
856
|
+
if (hasStrokeSegmentLength(corners[fromIndex], corners[toIndex])) {
|
|
857
|
+
segmentCount++;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return segmentCount;
|
|
861
|
+
}
|
|
862
|
+
function hasStrokeSegmentLength(from, to) {
|
|
863
|
+
const dx = to.x - from.x;
|
|
864
|
+
const dy = to.y - from.y;
|
|
865
|
+
const dz = to.z - from.z;
|
|
866
|
+
return dx * dx + dy * dy + dz * dz > epsilon;
|
|
867
|
+
}
|
|
868
|
+
function appendPlanarPolylineStroke(geometry, points, closed, style, color) {
|
|
869
|
+
const strokePoints = normalizedStrokePoints(points, closed);
|
|
870
|
+
if (!isPlanarXY(strokePoints)) {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
if (strokePoints.length < (closed ? 3 : 2)) {
|
|
874
|
+
return true;
|
|
875
|
+
}
|
|
876
|
+
if (style.lineDash.length > 0) {
|
|
877
|
+
appendDashedPlanarPolylineStroke(geometry, strokePoints, closed, style, color);
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
appendSolidPlanarPolylineStroke(geometry, strokePoints, closed, style, color);
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
function appendSolidPlanarPolylineStroke(geometry, strokePoints, closed, style, color) {
|
|
884
|
+
const segments = createPlanarStrokeSegments(strokePoints, closed);
|
|
885
|
+
if (!segments) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const halfWidth = style.lineWidth * 0.5;
|
|
889
|
+
for (let iSegment = 0; iSegment < segments.length; iSegment++) {
|
|
890
|
+
const segment = segments[iSegment];
|
|
891
|
+
const isFirst = iSegment === 0;
|
|
892
|
+
const isLast = iSegment === segments.length - 1;
|
|
893
|
+
const startExtension = !closed && isFirst && style.lineCap === Canvas3DLineCap.square ? halfWidth : 0;
|
|
894
|
+
const endExtension = !closed && isLast && style.lineCap === Canvas3DLineCap.square ? halfWidth : 0;
|
|
895
|
+
appendPlanarSegmentBody(geometry, strokePoints[segment.fromIndex], strokePoints[segment.toIndex], segment.direction, segment.normal, halfWidth, startExtension, endExtension, color);
|
|
896
|
+
}
|
|
897
|
+
if (closed) {
|
|
898
|
+
for (let iPoint = 0; iPoint < strokePoints.length; iPoint++) {
|
|
899
|
+
appendPlanarStrokeJoin(geometry, strokePoints[iPoint], segments[(iPoint - 1 + segments.length) % segments.length], segments[iPoint], style, color);
|
|
900
|
+
}
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
for (let iPoint = 1; iPoint < strokePoints.length - 1; iPoint++) {
|
|
904
|
+
appendPlanarStrokeJoin(geometry, strokePoints[iPoint], segments[iPoint - 1], segments[iPoint], style, color);
|
|
905
|
+
}
|
|
906
|
+
appendPlanarCaps(geometry, strokePoints, segments, style, color);
|
|
907
|
+
}
|
|
908
|
+
function appendDashedPlanarPolylineStroke(geometry, strokePoints, closed, style, color) {
|
|
909
|
+
const segments = createPlanarStrokeSegments(strokePoints, closed);
|
|
910
|
+
if (!segments) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const dashState = createDashState(style.lineDash, style.lineDashOffset);
|
|
914
|
+
const fragment = [];
|
|
915
|
+
const flushFragment = () => {
|
|
916
|
+
if (fragment.length >= 2) {
|
|
917
|
+
appendSolidPlanarPolylineStroke(geometry, fragment, false, style, color);
|
|
918
|
+
}
|
|
919
|
+
fragment.length = 0;
|
|
920
|
+
};
|
|
921
|
+
const appendFragmentPoint = (point) => {
|
|
922
|
+
const previous = fragment[fragment.length - 1];
|
|
923
|
+
if (!previous || hasStrokeSegmentLength(previous, point)) {
|
|
924
|
+
fragment.push(point.clone());
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
const startPoint = new Vec3();
|
|
928
|
+
const endPoint = new Vec3();
|
|
929
|
+
for (const segment of segments) {
|
|
930
|
+
const from = strokePoints[segment.fromIndex];
|
|
931
|
+
const to = strokePoints[segment.toIndex];
|
|
932
|
+
const segmentLength = Vec3.distance(from, to);
|
|
933
|
+
let traveled = 0;
|
|
934
|
+
while (segmentLength - traveled > epsilon) {
|
|
935
|
+
const step = Math.min(segmentLength - traveled, dashState.remaining);
|
|
936
|
+
setLerpPoint(startPoint, from, to, traveled / segmentLength);
|
|
937
|
+
setLerpPoint(endPoint, from, to, (traveled + step) / segmentLength);
|
|
938
|
+
if (dashState.drawing) {
|
|
939
|
+
appendFragmentPoint(startPoint);
|
|
940
|
+
appendFragmentPoint(endPoint);
|
|
941
|
+
}
|
|
942
|
+
traveled += step;
|
|
943
|
+
dashState.remaining -= step;
|
|
944
|
+
if (dashState.remaining <= epsilon) {
|
|
945
|
+
if (dashState.drawing) {
|
|
946
|
+
flushFragment();
|
|
947
|
+
}
|
|
948
|
+
advanceDashState(dashState);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
flushFragment();
|
|
953
|
+
}
|
|
954
|
+
function createPlanarStrokeSegments(strokePoints, closed) {
|
|
955
|
+
const segmentCount = closed ? strokePoints.length : strokePoints.length - 1;
|
|
956
|
+
const segments = [];
|
|
957
|
+
for (let iSegment = 0; iSegment < segmentCount; iSegment++) {
|
|
958
|
+
const from = strokePoints[iSegment];
|
|
959
|
+
const to = strokePoints[(iSegment + 1) % strokePoints.length];
|
|
960
|
+
const dx = to.x - from.x;
|
|
961
|
+
const dy = to.y - from.y;
|
|
962
|
+
const length = Math.hypot(dx, dy);
|
|
963
|
+
if (length <= epsilon) {
|
|
964
|
+
return undefined;
|
|
965
|
+
}
|
|
966
|
+
const dirX = dx / length;
|
|
967
|
+
const dirY = dy / length;
|
|
968
|
+
segments.push({
|
|
969
|
+
fromIndex: iSegment,
|
|
970
|
+
toIndex: (iSegment + 1) % strokePoints.length,
|
|
971
|
+
direction: new Vec3(dirX, dirY, 0),
|
|
972
|
+
normal: new Vec3(-dirY, dirX, 0),
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
return segments;
|
|
976
|
+
}
|
|
977
|
+
function appendPlanarSegmentBody(geometry, from, to, direction, normal, halfWidth, startExtension, endExtension, color) {
|
|
978
|
+
const start = new Vec3(from.x - direction.x * startExtension, from.y - direction.y * startExtension, from.z);
|
|
979
|
+
const end = new Vec3(to.x + direction.x * endExtension, to.y + direction.y * endExtension, to.z);
|
|
980
|
+
const leftStart = new Vec3(start.x + normal.x * halfWidth, start.y + normal.y * halfWidth, start.z);
|
|
981
|
+
const rightStart = new Vec3(start.x - normal.x * halfWidth, start.y - normal.y * halfWidth, start.z);
|
|
982
|
+
const rightEnd = new Vec3(end.x - normal.x * halfWidth, end.y - normal.y * halfWidth, end.z);
|
|
983
|
+
const leftEnd = new Vec3(end.x + normal.x * halfWidth, end.y + normal.y * halfWidth, end.z);
|
|
984
|
+
appendQuad(geometry, leftStart, rightStart, rightEnd, leftEnd, color);
|
|
985
|
+
}
|
|
986
|
+
function appendPlanarStrokeJoin(geometry, point, prevSegment, nextSegment, style, color) {
|
|
987
|
+
const turn = cross2D(prevSegment.direction.x, prevSegment.direction.y, nextSegment.direction.x, nextSegment.direction.y);
|
|
988
|
+
if (Math.abs(turn) <= epsilon) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const halfWidth = style.lineWidth * 0.5;
|
|
992
|
+
const outerSign = turn > 0 ? -1 : 1;
|
|
993
|
+
const offset = outerSign * halfWidth;
|
|
994
|
+
const prevOuter = createPlanarOffsetPoint(point, prevSegment.normal, offset);
|
|
995
|
+
const nextOuter = createPlanarOffsetPoint(point, nextSegment.normal, offset);
|
|
996
|
+
switch (style.lineJoin) {
|
|
997
|
+
case Canvas3DLineJoin.round:
|
|
998
|
+
appendRoundJoin(geometry, point, prevOuter, nextOuter, turn, color);
|
|
999
|
+
break;
|
|
1000
|
+
case Canvas3DLineJoin.bevel:
|
|
1001
|
+
appendTriangle(geometry, point, prevOuter, nextOuter, color);
|
|
1002
|
+
break;
|
|
1003
|
+
case Canvas3DLineJoin.miter:
|
|
1004
|
+
appendMiterJoin(geometry, point, prevSegment, nextSegment, prevOuter, nextOuter, offset, style.miterLimit, color);
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function appendMiterJoin(geometry, point, prevSegment, nextSegment, prevOuter, nextOuter, offset, miterLimit, color) {
|
|
1009
|
+
const miter = calculatePlanarMiterPoint(point, prevSegment.direction, prevSegment.normal, nextSegment.direction, nextSegment.normal, offset, miterLimit);
|
|
1010
|
+
if (!miter) {
|
|
1011
|
+
appendTriangle(geometry, point, prevOuter, nextOuter, color);
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
appendTriangle(geometry, point, prevOuter, miter, color);
|
|
1015
|
+
appendTriangle(geometry, point, miter, nextOuter, color);
|
|
1016
|
+
}
|
|
1017
|
+
function calculatePlanarMiterPoint(point, prevDirection, prevNormal, nextDirection, nextNormal, offset, miterLimit) {
|
|
1018
|
+
const prevX = point.x + prevNormal.x * offset;
|
|
1019
|
+
const prevY = point.y + prevNormal.y * offset;
|
|
1020
|
+
const nextX = point.x + nextNormal.x * offset;
|
|
1021
|
+
const nextY = point.y + nextNormal.y * offset;
|
|
1022
|
+
const denominator = cross2D(prevDirection.x, prevDirection.y, nextDirection.x, nextDirection.y);
|
|
1023
|
+
if (Math.abs(denominator) > epsilon) {
|
|
1024
|
+
const t = cross2D(nextX - prevX, nextY - prevY, nextDirection.x, nextDirection.y) / denominator;
|
|
1025
|
+
const x = prevX + prevDirection.x * t;
|
|
1026
|
+
const y = prevY + prevDirection.y * t;
|
|
1027
|
+
const maxMiterLength = Math.abs(offset) * miterLimit;
|
|
1028
|
+
const dx = x - point.x;
|
|
1029
|
+
const dy = y - point.y;
|
|
1030
|
+
if (dx * dx + dy * dy <= maxMiterLength * maxMiterLength) {
|
|
1031
|
+
return new Vec3(x, y, point.z);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return undefined;
|
|
1035
|
+
}
|
|
1036
|
+
function appendRoundJoin(geometry, center, startPoint, endPoint, turn, color) {
|
|
1037
|
+
const startAngle = Math.atan2(startPoint.y - center.y, startPoint.x - center.x);
|
|
1038
|
+
const endAngle = Math.atan2(endPoint.y - center.y, endPoint.x - center.x);
|
|
1039
|
+
let delta = normalizeAngle(endAngle - startAngle);
|
|
1040
|
+
if (turn > 0 && delta < 0) {
|
|
1041
|
+
delta += Math.PI * 2;
|
|
1042
|
+
}
|
|
1043
|
+
else if (turn < 0 && delta > 0) {
|
|
1044
|
+
delta -= Math.PI * 2;
|
|
1045
|
+
}
|
|
1046
|
+
appendArcFan(geometry, center, startAngle, delta, Vec3.distance(center, startPoint), color);
|
|
1047
|
+
}
|
|
1048
|
+
function appendPlanarCaps(geometry, strokePoints, segments, style, color) {
|
|
1049
|
+
if (style.lineCap !== Canvas3DLineCap.round) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
const halfWidth = style.lineWidth * 0.5;
|
|
1053
|
+
appendCircleFill(geometry, strokePoints[0], halfWidth, color);
|
|
1054
|
+
appendCircleFill(geometry, strokePoints[segments[segments.length - 1].toIndex], halfWidth, color);
|
|
1055
|
+
}
|
|
1056
|
+
function createPlanarOffsetPoint(point, normal, offset) {
|
|
1057
|
+
return new Vec3(point.x + normal.x * offset, point.y + normal.y * offset, point.z);
|
|
1058
|
+
}
|
|
1059
|
+
function appendArcFan(geometry, center, startAngle, delta, radius, color) {
|
|
1060
|
+
if (radius <= epsilon || Math.abs(delta) <= epsilon) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const segmentCount = Math.max(1, Math.ceil(Math.abs(delta) / roundSegmentRadians));
|
|
1064
|
+
let previousPoint = new Vec3(center.x + Math.cos(startAngle) * radius, center.y + Math.sin(startAngle) * radius, center.z);
|
|
1065
|
+
for (let iSegment = 1; iSegment <= segmentCount; iSegment++) {
|
|
1066
|
+
const angle = startAngle + delta * iSegment / segmentCount;
|
|
1067
|
+
const point = new Vec3(center.x + Math.cos(angle) * radius, center.y + Math.sin(angle) * radius, center.z);
|
|
1068
|
+
appendTriangle(geometry, center, previousPoint, point, color);
|
|
1069
|
+
previousPoint = point;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
function createDashState(pattern, lineDashOffset) {
|
|
1073
|
+
const patternLength = sum(pattern);
|
|
1074
|
+
let offset = lineDashOffset % patternLength;
|
|
1075
|
+
if (offset < 0) {
|
|
1076
|
+
offset += patternLength;
|
|
1077
|
+
}
|
|
1078
|
+
let patternIndex = 0;
|
|
1079
|
+
while (offset >= pattern[patternIndex] && pattern[patternIndex] > 0) {
|
|
1080
|
+
offset -= pattern[patternIndex];
|
|
1081
|
+
patternIndex = (patternIndex + 1) % pattern.length;
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
pattern,
|
|
1085
|
+
patternIndex,
|
|
1086
|
+
remaining: pattern[patternIndex] - offset,
|
|
1087
|
+
drawing: patternIndex % 2 === 0,
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function advanceDashState(state) {
|
|
1091
|
+
state.patternIndex = (state.patternIndex + 1) % state.pattern.length;
|
|
1092
|
+
state.remaining = state.pattern[state.patternIndex];
|
|
1093
|
+
state.drawing = state.patternIndex % 2 === 0;
|
|
1094
|
+
}
|
|
1095
|
+
function setLerpPoint(out, from, to, t) {
|
|
1096
|
+
return out.set(from.x + (to.x - from.x) * t, from.y + (to.y - from.y) * t, from.z + (to.z - from.z) * t);
|
|
1097
|
+
}
|
|
1098
|
+
function normalizedStrokePoints(points, closed) {
|
|
1099
|
+
const strokePoints = [];
|
|
1100
|
+
for (const point of points) {
|
|
1101
|
+
if (strokePoints.length === 0 || hasStrokeSegmentLength(strokePoints[strokePoints.length - 1], point)) {
|
|
1102
|
+
strokePoints.push(point);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
if (closed && strokePoints.length > 1 && !hasStrokeSegmentLength(strokePoints[strokePoints.length - 1], strokePoints[0])) {
|
|
1106
|
+
strokePoints.pop();
|
|
1107
|
+
}
|
|
1108
|
+
return strokePoints;
|
|
1109
|
+
}
|
|
1110
|
+
function isPlanarXY(points) {
|
|
1111
|
+
const firstPoint = points[0];
|
|
1112
|
+
if (!firstPoint) {
|
|
1113
|
+
return true;
|
|
1114
|
+
}
|
|
1115
|
+
for (const point of points) {
|
|
1116
|
+
if (Math.abs(point.z - firstPoint.z) > epsilon) {
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
return true;
|
|
1121
|
+
}
|
|
1122
|
+
function cross2D(ax, ay, bx, by) {
|
|
1123
|
+
return ax * by - ay * bx;
|
|
1124
|
+
}
|
|
1125
|
+
function appendVertex(geometry, point, color) {
|
|
1126
|
+
const vertexIndex = geometry.vertices.length / vertexStrideF;
|
|
1127
|
+
geometry.vertices.push(point.x, point.y, point.z, color.x, color.y, color.z, color.w);
|
|
1128
|
+
return vertexIndex;
|
|
1129
|
+
}
|
|
1130
|
+
function appendTriangle(geometry, a, b, c, color) {
|
|
1131
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
1132
|
+
appendVertex(geometry, a, color);
|
|
1133
|
+
appendVertex(geometry, b, color);
|
|
1134
|
+
appendVertex(geometry, c, color);
|
|
1135
|
+
geometry.indices.push(baseVertex, baseVertex + 1, baseVertex + 2);
|
|
1136
|
+
}
|
|
1137
|
+
function appendQuad(geometry, a, b, c, d, color) {
|
|
1138
|
+
const baseVertex = geometry.vertices.length / vertexStrideF;
|
|
1139
|
+
appendVertex(geometry, a, color);
|
|
1140
|
+
appendVertex(geometry, b, color);
|
|
1141
|
+
appendVertex(geometry, c, color);
|
|
1142
|
+
appendVertex(geometry, d, color);
|
|
1143
|
+
geometry.indices.push(baseVertex, baseVertex + 1, baseVertex + 2, baseVertex, baseVertex + 2, baseVertex + 3);
|
|
1144
|
+
}
|
|
1145
|
+
export function createOrUpdateRenderingSubMeshRecord(device, geometry, record) {
|
|
1146
|
+
const vertexCount = geometry.vertices.length / vertexStrideF;
|
|
1147
|
+
if (vertexCount === 0 || geometry.indices.length === 0) {
|
|
1148
|
+
return undefined;
|
|
1149
|
+
}
|
|
1150
|
+
const vertexBufferData = Float32Array.from(geometry.vertices);
|
|
1151
|
+
const indexBufferData = createIndexBufferData(geometry.indices, vertexCount);
|
|
1152
|
+
if (record && record.indexBytesPerElement === indexBufferData.BYTES_PER_ELEMENT) {
|
|
1153
|
+
updateRenderingSubMeshRecord(record, vertexBufferData, indexBufferData);
|
|
1154
|
+
return record;
|
|
1155
|
+
}
|
|
1156
|
+
record?.renderingSubMesh.destroy();
|
|
1157
|
+
const vertexBuffer = device.createBuffer(new gfx.BufferInfo(gfx.BufferUsageBit.VERTEX, gfx.MemoryUsageBit.DEVICE, vertexBufferData.byteLength, Float32Array.BYTES_PER_ELEMENT * vertexStrideF, gfx.BufferFlagBit.NONE));
|
|
1158
|
+
updateGfxBuffer(vertexBuffer, vertexBufferData);
|
|
1159
|
+
const indexBuffer = device.createBuffer(new gfx.BufferInfo(gfx.BufferUsageBit.INDEX, gfx.MemoryUsageBit.DEVICE, indexBufferData.byteLength, indexBufferData.BYTES_PER_ELEMENT, gfx.BufferFlagBit.NONE));
|
|
1160
|
+
updateGfxBuffer(indexBuffer, indexBufferData);
|
|
1161
|
+
return {
|
|
1162
|
+
renderingSubMesh: new RenderingSubMesh([vertexBuffer], canvas3DVertexAttributes, gfx.PrimitiveMode.TRIANGLE_LIST, indexBuffer, null, true),
|
|
1163
|
+
vertexBuffer,
|
|
1164
|
+
indexBuffer,
|
|
1165
|
+
vertexBufferBytes: vertexBufferData.byteLength,
|
|
1166
|
+
indexBufferBytes: indexBufferData.byteLength,
|
|
1167
|
+
indexBytesPerElement: indexBufferData.BYTES_PER_ELEMENT,
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
function updateRenderingSubMeshRecord(record, vertexBufferData, indexBufferData) {
|
|
1171
|
+
resizeGfxBuffer(record.vertexBuffer, vertexBufferData.byteLength);
|
|
1172
|
+
updateGfxBuffer(record.vertexBuffer, vertexBufferData);
|
|
1173
|
+
record.vertexBufferBytes = vertexBufferData.byteLength;
|
|
1174
|
+
resizeGfxBuffer(record.indexBuffer, indexBufferData.byteLength);
|
|
1175
|
+
updateGfxBuffer(record.indexBuffer, indexBufferData);
|
|
1176
|
+
record.indexBufferBytes = indexBufferData.byteLength;
|
|
1177
|
+
}
|
|
1178
|
+
function resizeGfxBuffer(buffer, byteLength) {
|
|
1179
|
+
const resizableBuffer = buffer;
|
|
1180
|
+
if (resizableBuffer.size !== byteLength) {
|
|
1181
|
+
resizableBuffer.resize(byteLength);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
function updateGfxBuffer(buffer, source) {
|
|
1185
|
+
buffer.update(source);
|
|
1186
|
+
}
|
|
1187
|
+
function createIndexBufferData(indices, vertexCount) {
|
|
1188
|
+
if (vertexCount > 2 ** 16) {
|
|
1189
|
+
return Uint32Array.from(indices);
|
|
1190
|
+
}
|
|
1191
|
+
return Uint16Array.from(indices);
|
|
1192
|
+
}
|
|
1193
|
+
function normalizedSubPathPoints(subPath) {
|
|
1194
|
+
const points = subPath.points;
|
|
1195
|
+
if (points.length > 1 && Vec3.equals(points[0], points[points.length - 1], epsilon)) {
|
|
1196
|
+
return points.slice(0, -1);
|
|
1197
|
+
}
|
|
1198
|
+
return points;
|
|
1199
|
+
}
|
|
1200
|
+
export function resolveHalfExtents(halfExtents) {
|
|
1201
|
+
if (typeof halfExtents === 'number') {
|
|
1202
|
+
return new Vec3(halfExtents, halfExtents, halfExtents);
|
|
1203
|
+
}
|
|
1204
|
+
return halfExtents.clone();
|
|
1205
|
+
}
|
|
1206
|
+
function createBoxCorners(center, halfExtents) {
|
|
1207
|
+
const { x, y, z } = halfExtents;
|
|
1208
|
+
return [
|
|
1209
|
+
new Vec3(center.x - x, center.y - y, center.z - z),
|
|
1210
|
+
new Vec3(center.x + x, center.y - y, center.z - z),
|
|
1211
|
+
new Vec3(center.x + x, center.y + y, center.z - z),
|
|
1212
|
+
new Vec3(center.x - x, center.y + y, center.z - z),
|
|
1213
|
+
new Vec3(center.x - x, center.y - y, center.z + z),
|
|
1214
|
+
new Vec3(center.x + x, center.y - y, center.z + z),
|
|
1215
|
+
new Vec3(center.x + x, center.y + y, center.z + z),
|
|
1216
|
+
new Vec3(center.x - x, center.y + y, center.z + z),
|
|
1217
|
+
];
|
|
1218
|
+
}
|
|
1219
|
+
function transformPoint(point, transform) {
|
|
1220
|
+
return transformPointInto(new Vec3(), point, transform);
|
|
1221
|
+
}
|
|
1222
|
+
function transformPointInto(out, point, transform) {
|
|
1223
|
+
return Vec3.transformMat4(out, point, transform);
|
|
1224
|
+
}
|
|
1225
|
+
function transformPoints(points, transform) {
|
|
1226
|
+
return points.map((point) => {
|
|
1227
|
+
return transformPoint(point, transform);
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
function createAxisBasis(from, to) {
|
|
1231
|
+
const direction = Vec3.subtract(new Vec3(), to, from);
|
|
1232
|
+
return createDirectionBasis(direction);
|
|
1233
|
+
}
|
|
1234
|
+
function createDirectionBasis(direction) {
|
|
1235
|
+
const normalizedDirection = direction.clone();
|
|
1236
|
+
if (normalizedDirection.lengthSqr() <= epsilon) {
|
|
1237
|
+
return undefined;
|
|
1238
|
+
}
|
|
1239
|
+
normalizedDirection.normalize();
|
|
1240
|
+
const referenceAxis = Math.abs(Vec3.dot(normalizedDirection, Vec3.UP)) > 0.95 ? Vec3.RIGHT : Vec3.UP;
|
|
1241
|
+
const normalA = Vec3.cross(new Vec3(), normalizedDirection, referenceAxis).normalize();
|
|
1242
|
+
const normalB = Vec3.cross(new Vec3(), normalizedDirection, normalA).normalize();
|
|
1243
|
+
return {
|
|
1244
|
+
normalA,
|
|
1245
|
+
normalB,
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
function createNormalBasis(normal) {
|
|
1249
|
+
const direction = resolveDirection(normal, new Vec3(0, 0, 1));
|
|
1250
|
+
const referenceAxis = Math.abs(Vec3.dot(direction, Vec3.UP)) > 0.95 ? Vec3.RIGHT : Vec3.UP;
|
|
1251
|
+
const axisA = Vec3.cross(new Vec3(), referenceAxis, direction).normalize();
|
|
1252
|
+
const axisB = Vec3.cross(new Vec3(), direction, axisA).normalize();
|
|
1253
|
+
return {
|
|
1254
|
+
axisA,
|
|
1255
|
+
axisB,
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
function resolveDirection(value, fallback = Vec3.UP) {
|
|
1259
|
+
const result = value.clone();
|
|
1260
|
+
if (result.lengthSqr() <= epsilon) {
|
|
1261
|
+
result.set(fallback);
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
result.normalize();
|
|
1265
|
+
}
|
|
1266
|
+
return result;
|
|
1267
|
+
}
|
|
1268
|
+
function setBasisDirection(out, axisA, axisB, angle) {
|
|
1269
|
+
return out.set(axisA.x * Math.cos(angle) + axisB.x * Math.sin(angle), axisA.y * Math.cos(angle) + axisB.y * Math.sin(angle), axisA.z * Math.cos(angle) + axisB.z * Math.sin(angle));
|
|
1270
|
+
}
|
|
1271
|
+
function setHemispherePoint(out, center, radius, axis, radialDirection, theta) {
|
|
1272
|
+
const radialScale = Math.sin(theta) * radius;
|
|
1273
|
+
const axisScale = Math.cos(theta) * radius;
|
|
1274
|
+
return out.set(center.x + axis.x * axisScale + radialDirection.x * radialScale, center.y + axis.y * axisScale + radialDirection.y * radialScale, center.z + axis.z * axisScale + radialDirection.z * radialScale);
|
|
1275
|
+
}
|
|
1276
|
+
function createVec3Cache(size) {
|
|
1277
|
+
return {
|
|
1278
|
+
values: createVec3ScratchValues(size),
|
|
1279
|
+
borrowed: false,
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
function borrowVec3ScratchCache(cache) {
|
|
1283
|
+
if (cache.borrowed) {
|
|
1284
|
+
throw new Error('Canvas3D Vec3 scratch cache is already borrowed.');
|
|
1285
|
+
}
|
|
1286
|
+
cache.borrowed = true;
|
|
1287
|
+
return cache.values;
|
|
1288
|
+
}
|
|
1289
|
+
function releaseVec3ScratchCache(cache) {
|
|
1290
|
+
cache.borrowed = false;
|
|
1291
|
+
}
|
|
1292
|
+
function createVec3ScratchValues(size) {
|
|
1293
|
+
return Array.from({ length: size }, () => {
|
|
1294
|
+
return new Vec3();
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
function getCircleTopology(segments) {
|
|
1298
|
+
let topology = circleTopologyCache.get(segments);
|
|
1299
|
+
if (!topology) {
|
|
1300
|
+
const cos = [];
|
|
1301
|
+
const sin = [];
|
|
1302
|
+
for (let iPoint = 0; iPoint < segments; iPoint++) {
|
|
1303
|
+
const angle = iPoint / segments * Math.PI * 2;
|
|
1304
|
+
cos.push(Math.cos(angle));
|
|
1305
|
+
sin.push(Math.sin(angle));
|
|
1306
|
+
}
|
|
1307
|
+
topology = { cos, sin };
|
|
1308
|
+
circleTopologyCache.set(segments, topology);
|
|
1309
|
+
}
|
|
1310
|
+
return topology;
|
|
1311
|
+
}
|
|
1312
|
+
function getSphereTopology(latitudeSegments, longitudeSegments) {
|
|
1313
|
+
const key = `${latitudeSegments}:${longitudeSegments}`;
|
|
1314
|
+
let topology = sphereTopologyCache.get(key);
|
|
1315
|
+
if (!topology) {
|
|
1316
|
+
const positions = [];
|
|
1317
|
+
const indices = [];
|
|
1318
|
+
for (let iLatitude = 0; iLatitude <= latitudeSegments; iLatitude++) {
|
|
1319
|
+
const theta = iLatitude / latitudeSegments * Math.PI;
|
|
1320
|
+
const z = Math.cos(theta);
|
|
1321
|
+
const ringRadius = Math.sin(theta);
|
|
1322
|
+
for (let iLongitude = 0; iLongitude <= longitudeSegments; iLongitude++) {
|
|
1323
|
+
const phi = iLongitude / longitudeSegments * Math.PI * 2;
|
|
1324
|
+
positions.push(Math.cos(phi) * ringRadius, Math.sin(phi) * ringRadius, z);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
const ringStride = longitudeSegments + 1;
|
|
1328
|
+
for (let iLatitude = 0; iLatitude < latitudeSegments; iLatitude++) {
|
|
1329
|
+
for (let iLongitude = 0; iLongitude < longitudeSegments; iLongitude++) {
|
|
1330
|
+
const a = iLatitude * ringStride + iLongitude;
|
|
1331
|
+
const b = a + ringStride;
|
|
1332
|
+
const c = b + 1;
|
|
1333
|
+
const d = a + 1;
|
|
1334
|
+
indices.push(a, b, d, d, b, c);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
topology = { positions, indices };
|
|
1338
|
+
sphereTopologyCache.set(key, topology);
|
|
1339
|
+
}
|
|
1340
|
+
return topology;
|
|
1341
|
+
}
|
|
1342
|
+
function getCylinderIndexTopology(radialSegments, includeCaps) {
|
|
1343
|
+
const key = `${radialSegments}:${includeCaps ? 1 : 0}`;
|
|
1344
|
+
let topology = cylinderIndexTopologyCache.get(key);
|
|
1345
|
+
if (!topology) {
|
|
1346
|
+
const indices = [];
|
|
1347
|
+
for (let iPoint = 0; iPoint < radialSegments; iPoint++) {
|
|
1348
|
+
const nextPoint = (iPoint + 1) % radialSegments;
|
|
1349
|
+
const start = iPoint;
|
|
1350
|
+
const startNext = nextPoint;
|
|
1351
|
+
const end = radialSegments + iPoint;
|
|
1352
|
+
const endNext = radialSegments + nextPoint;
|
|
1353
|
+
indices.push(start, end, startNext, startNext, end, endNext);
|
|
1354
|
+
}
|
|
1355
|
+
if (includeCaps) {
|
|
1356
|
+
const fromCenter = radialSegments * 2;
|
|
1357
|
+
const toCenter = fromCenter + 1;
|
|
1358
|
+
for (let iPoint = 0; iPoint < radialSegments; iPoint++) {
|
|
1359
|
+
const nextPoint = (iPoint + 1) % radialSegments;
|
|
1360
|
+
indices.push(fromCenter, nextPoint, iPoint, toCenter, radialSegments + iPoint, radialSegments + nextPoint);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
topology = { indices };
|
|
1364
|
+
cylinderIndexTopologyCache.set(key, topology);
|
|
1365
|
+
}
|
|
1366
|
+
return topology;
|
|
1367
|
+
}
|
|
1368
|
+
function getHemisphereTopology(radialSegments, capSegments) {
|
|
1369
|
+
const key = `${radialSegments}:${capSegments}`;
|
|
1370
|
+
let topology = hemisphereTopologyCache.get(key);
|
|
1371
|
+
if (!topology) {
|
|
1372
|
+
const positions = [];
|
|
1373
|
+
const indices = [];
|
|
1374
|
+
for (let iCap = 0; iCap <= capSegments; iCap++) {
|
|
1375
|
+
const theta = iCap / capSegments * Math.PI * 0.5;
|
|
1376
|
+
const radialScale = Math.sin(theta);
|
|
1377
|
+
const axisScale = Math.cos(theta);
|
|
1378
|
+
for (let iRadial = 0; iRadial <= radialSegments; iRadial++) {
|
|
1379
|
+
const phi = iRadial / radialSegments * Math.PI * 2;
|
|
1380
|
+
positions.push(Math.cos(phi) * radialScale, Math.sin(phi) * radialScale, axisScale);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
const ringStride = radialSegments + 1;
|
|
1384
|
+
for (let iCap = 0; iCap < capSegments; iCap++) {
|
|
1385
|
+
for (let iRadial = 0; iRadial < radialSegments; iRadial++) {
|
|
1386
|
+
const a = iCap * ringStride + iRadial;
|
|
1387
|
+
const b = a + ringStride;
|
|
1388
|
+
const c = b + 1;
|
|
1389
|
+
const d = a + 1;
|
|
1390
|
+
indices.push(a, b, d, d, b, c);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
topology = { positions, indices };
|
|
1394
|
+
hemisphereTopologyCache.set(key, topology);
|
|
1395
|
+
}
|
|
1396
|
+
return topology;
|
|
1397
|
+
}
|
|
1398
|
+
function appendTopologyIndices(geometry, baseVertex, indices) {
|
|
1399
|
+
for (const index of indices) {
|
|
1400
|
+
geometry.indices.push(baseVertex + index);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
function createCirclePoint(center, radius, pointIndex) {
|
|
1404
|
+
return setCirclePoint(new Vec3(), center, radius, pointIndex);
|
|
1405
|
+
}
|
|
1406
|
+
function createCirclePoints(center, radius) {
|
|
1407
|
+
const points = [];
|
|
1408
|
+
for (let iPoint = 0; iPoint < defaultCircleSegments; iPoint++) {
|
|
1409
|
+
points.push(createCirclePoint(center, radius, iPoint));
|
|
1410
|
+
}
|
|
1411
|
+
return points;
|
|
1412
|
+
}
|
|
1413
|
+
function setCirclePoint(out, center, radius, pointIndex) {
|
|
1414
|
+
const topology = getCircleTopology(defaultCircleSegments);
|
|
1415
|
+
return out.set(center.x + topology.cos[pointIndex] * radius, center.y + topology.sin[pointIndex] * radius, center.z);
|
|
1416
|
+
}
|
|
1417
|
+
function createPlaneCirclePoint(center, radius, axisA, axisB, pointIndex, segmentCount) {
|
|
1418
|
+
return setPlaneCirclePoint(new Vec3(), center, radius, axisA, axisB, pointIndex, segmentCount);
|
|
1419
|
+
}
|
|
1420
|
+
function setPlaneCirclePoint(out, center, radius, axisA, axisB, pointIndex, segmentCount) {
|
|
1421
|
+
const topology = getCircleTopology(segmentCount);
|
|
1422
|
+
const cos = topology.cos[pointIndex];
|
|
1423
|
+
const sin = topology.sin[pointIndex];
|
|
1424
|
+
return out.set(center.x + (axisA.x * cos + axisB.x * sin) * radius, center.y + (axisA.y * cos + axisB.y * sin) * radius, center.z + (axisA.z * cos + axisB.z * sin) * radius);
|
|
1425
|
+
}
|
|
1426
|
+
export function normalizeArcDelta(startAngle, endAngle, counterclockwise) {
|
|
1427
|
+
const fullTurn = Math.PI * 2;
|
|
1428
|
+
let delta = endAngle - startAngle;
|
|
1429
|
+
if (counterclockwise) {
|
|
1430
|
+
while (delta > 0) {
|
|
1431
|
+
delta -= fullTurn;
|
|
1432
|
+
}
|
|
1433
|
+
return Math.max(delta, -fullTurn);
|
|
1434
|
+
}
|
|
1435
|
+
while (delta < 0) {
|
|
1436
|
+
delta += fullTurn;
|
|
1437
|
+
}
|
|
1438
|
+
return Math.min(delta, fullTurn);
|
|
1439
|
+
}
|
|
1440
|
+
function normalizeAngle(value) {
|
|
1441
|
+
let result = value;
|
|
1442
|
+
while (result <= -Math.PI) {
|
|
1443
|
+
result += Math.PI * 2;
|
|
1444
|
+
}
|
|
1445
|
+
while (result > Math.PI) {
|
|
1446
|
+
result -= Math.PI * 2;
|
|
1447
|
+
}
|
|
1448
|
+
return result;
|
|
1449
|
+
}
|
|
1450
|
+
function sum(values) {
|
|
1451
|
+
let result = 0;
|
|
1452
|
+
for (const value of values) {
|
|
1453
|
+
result += value;
|
|
1454
|
+
}
|
|
1455
|
+
return result;
|
|
1456
|
+
}
|
|
1457
|
+
const canvas3DVertexAttributes = [
|
|
1458
|
+
new gfx.Attribute(gfx.AttributeName.ATTR_POSITION, gfx.Format.RGB32F, false, undefined, false, undefined),
|
|
1459
|
+
new gfx.Attribute(gfx.AttributeName.ATTR_COLOR, gfx.Format.RGBA32F, false, undefined, false, undefined),
|
|
1460
|
+
];
|
|
1461
|
+
const boxEdges = [
|
|
1462
|
+
[0, 1],
|
|
1463
|
+
[1, 2],
|
|
1464
|
+
[2, 3],
|
|
1465
|
+
[3, 0],
|
|
1466
|
+
[4, 5],
|
|
1467
|
+
[5, 6],
|
|
1468
|
+
[6, 7],
|
|
1469
|
+
[7, 4],
|
|
1470
|
+
[0, 4],
|
|
1471
|
+
[1, 5],
|
|
1472
|
+
[2, 6],
|
|
1473
|
+
[3, 7],
|
|
1474
|
+
];
|
|
1475
|
+
const boxTriangleIndices = [
|
|
1476
|
+
0, 2, 1,
|
|
1477
|
+
0, 3, 2,
|
|
1478
|
+
4, 5, 6,
|
|
1479
|
+
4, 6, 7,
|
|
1480
|
+
0, 4, 7,
|
|
1481
|
+
0, 7, 3,
|
|
1482
|
+
1, 2, 6,
|
|
1483
|
+
1, 6, 5,
|
|
1484
|
+
0, 1, 5,
|
|
1485
|
+
0, 5, 4,
|
|
1486
|
+
3, 7, 6,
|
|
1487
|
+
3, 6, 2,
|
|
1488
|
+
];
|
|
1489
|
+
const strokeBoxTriangleIndices = [
|
|
1490
|
+
0, 1, 5,
|
|
1491
|
+
0, 5, 4,
|
|
1492
|
+
1, 2, 6,
|
|
1493
|
+
1, 6, 5,
|
|
1494
|
+
2, 3, 7,
|
|
1495
|
+
2, 7, 6,
|
|
1496
|
+
3, 0, 4,
|
|
1497
|
+
3, 4, 7,
|
|
1498
|
+
0, 2, 1,
|
|
1499
|
+
0, 3, 2,
|
|
1500
|
+
4, 5, 6,
|
|
1501
|
+
4, 6, 7,
|
|
1502
|
+
];
|
|
1503
|
+
//# sourceMappingURL=geometry.js.map
|