Anlegen vom Raum-Scanner

This commit is contained in:
chk
2026-06-16 10:06:05 +02:00
commit 792022d1fb
1836 changed files with 773869 additions and 0 deletions

120
node_modules/three/src/extras/Controls.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
import { EventDispatcher } from '../core/EventDispatcher.js';
import { warn } from '../utils.js';
/**
* Abstract base class for controls.
*
* @abstract
* @augments EventDispatcher
*/
class Controls extends EventDispatcher {
/**
* Constructs a new controls instance.
*
* @param {Object3D} object - The object that is managed by the controls.
* @param {?HTMLElement} domElement - The HTML element used for event listeners.
*/
constructor( object, domElement = null ) {
super();
/**
* The object that is managed by the controls.
*
* @type {Object3D}
*/
this.object = object;
/**
* The HTML element used for event listeners.
*
* @type {?HTMLElement}
* @default null
*/
this.domElement = domElement;
/**
* Whether the controls responds to user input or not.
*
* @type {boolean}
* @default true
*/
this.enabled = true;
/**
* The internal state of the controls.
*
* @type {number}
* @default -1
*/
this.state = - 1;
/**
* This object defines the keyboard input of the controls.
*
* @type {Object}
*/
this.keys = {};
/**
* This object defines what type of actions are assigned to the available mouse buttons.
* It depends on the control implementation what kind of mouse buttons and actions are supported.
*
* @type {{LEFT: ?number, MIDDLE: ?number, RIGHT: ?number}}
*/
this.mouseButtons = { LEFT: null, MIDDLE: null, RIGHT: null };
/**
* This object defines what type of actions are assigned to what kind of touch interaction.
* It depends on the control implementation what kind of touch interaction and actions are supported.
*
* @type {{ONE: ?number, TWO: ?number}}
*/
this.touches = { ONE: null, TWO: null };
}
/**
* Connects the controls to the DOM. This method has so called "side effects" since
* it adds the module's event listeners to the DOM.
*
* @param {HTMLElement} element - The DOM element to connect to.
*/
connect( element ) {
if ( element === undefined ) {
warn( 'Controls: connect() now requires an element.' ); // @deprecated, the warning can be removed with r185
return;
}
if ( this.domElement !== null ) this.disconnect();
this.domElement = element;
}
/**
* Disconnects the controls from the DOM.
*/
disconnect() {}
/**
* Call this method if you no longer want use to the controls. It frees all internal
* resources and removes all event listeners.
*/
dispose() {}
/**
* Controls should implement this method if they have to update their internal state
* per simulation step.
*
* @param {number} [delta] - The time delta in seconds.
*/
update( /* delta */ ) {}
}
export { Controls };

217
node_modules/three/src/extras/DataUtils.js generated vendored Normal file
View File

@@ -0,0 +1,217 @@
import { clamp } from '../math/MathUtils.js';
import { warn } from '../utils.js';
// Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
const _tables = /*@__PURE__*/ _generateTables();
function _generateTables() {
// float32 to float16 helpers
const buffer = new ArrayBuffer( 4 );
const floatView = new Float32Array( buffer );
const uint32View = new Uint32Array( buffer );
const baseTable = new Uint32Array( 512 );
const shiftTable = new Uint32Array( 512 );
for ( let i = 0; i < 256; ++ i ) {
const e = i - 127;
// very small number (0, -0)
if ( e < - 27 ) {
baseTable[ i ] = 0x0000;
baseTable[ i | 0x100 ] = 0x8000;
shiftTable[ i ] = 24;
shiftTable[ i | 0x100 ] = 24;
// small number (denorm)
} else if ( e < - 14 ) {
baseTable[ i ] = 0x0400 >> ( - e - 14 );
baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000;
shiftTable[ i ] = - e - 1;
shiftTable[ i | 0x100 ] = - e - 1;
// normal number
} else if ( e <= 15 ) {
baseTable[ i ] = ( e + 15 ) << 10;
baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000;
shiftTable[ i ] = 13;
shiftTable[ i | 0x100 ] = 13;
// large number (Infinity, -Infinity)
} else if ( e < 128 ) {
baseTable[ i ] = 0x7c00;
baseTable[ i | 0x100 ] = 0xfc00;
shiftTable[ i ] = 24;
shiftTable[ i | 0x100 ] = 24;
// stay (NaN, Infinity, -Infinity)
} else {
baseTable[ i ] = 0x7c00;
baseTable[ i | 0x100 ] = 0xfc00;
shiftTable[ i ] = 13;
shiftTable[ i | 0x100 ] = 13;
}
}
// float16 to float32 helpers
const mantissaTable = new Uint32Array( 2048 );
const exponentTable = new Uint32Array( 64 );
const offsetTable = new Uint32Array( 64 );
for ( let i = 1; i < 1024; ++ i ) {
let m = i << 13; // zero pad mantissa bits
let e = 0; // zero exponent
// normalized
while ( ( m & 0x00800000 ) === 0 ) {
m <<= 1;
e -= 0x00800000; // decrement exponent
}
m &= ~ 0x00800000; // clear leading 1 bit
e += 0x38800000; // adjust bias
mantissaTable[ i ] = m | e;
}
for ( let i = 1024; i < 2048; ++ i ) {
mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 );
}
for ( let i = 1; i < 31; ++ i ) {
exponentTable[ i ] = i << 23;
}
exponentTable[ 31 ] = 0x47800000;
exponentTable[ 32 ] = 0x80000000;
for ( let i = 33; i < 63; ++ i ) {
exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 );
}
exponentTable[ 63 ] = 0xc7800000;
for ( let i = 1; i < 64; ++ i ) {
if ( i !== 32 ) {
offsetTable[ i ] = 1024;
}
}
return {
floatView: floatView,
uint32View: uint32View,
baseTable: baseTable,
shiftTable: shiftTable,
mantissaTable: mantissaTable,
exponentTable: exponentTable,
offsetTable: offsetTable
};
}
/**
* Returns a half precision floating point value (FP16) from the given single
* precision floating point value (FP32).
*
* @param {number} val - A single precision floating point value.
* @return {number} The FP16 value.
*/
function toHalfFloat( val ) {
if ( Math.abs( val ) > 65504 ) warn( 'DataUtils.toHalfFloat(): Value out of range.' );
val = clamp( val, - 65504, 65504 );
_tables.floatView[ 0 ] = val;
const f = _tables.uint32View[ 0 ];
const e = ( f >> 23 ) & 0x1ff;
return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] );
}
/**
* Returns a single precision floating point value (FP32) from the given half
* precision floating point value (FP16).
*
* @param {number} val - A half precision floating point value.
* @return {number} The FP32 value.
*/
function fromHalfFloat( val ) {
const m = val >> 10;
_tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ];
return _tables.floatView[ 0 ];
}
/**
* A class containing utility functions for data.
*
* @hideconstructor
*/
class DataUtils {
/**
* Returns a half precision floating point value (FP16) from the given single
* precision floating point value (FP32).
*
* @param {number} val - A single precision floating point value.
* @return {number} The FP16 value.
*/
static toHalfFloat( val ) {
return toHalfFloat( val );
}
/**
* Returns a single precision floating point value (FP32) from the given half
* precision floating point value (FP16).
*
* @param {number} val - A half precision floating point value.
* @return {number} The FP32 value.
*/
static fromHalfFloat( val ) {
return fromHalfFloat( val );
}
}
export {
toHalfFloat,
fromHalfFloat,
DataUtils
};

28
node_modules/three/src/extras/Earcut.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
import earcut from './lib/earcut.js';
/**
* An implementation of the earcut polygon triangulation algorithm.
* The code is a port of [mapbox/earcut](https://github.com/mapbox/earcut).
*
* @see https://github.com/mapbox/earcut
*/
class Earcut {
/**
* Triangulates the given shape definition by returning an array of triangles.
*
* @param {Array<number>} data - An array with 2D points.
* @param {Array<number>} holeIndices - An array with indices defining holes.
* @param {number} [dim=2] - The number of coordinates per vertex in the input array.
* @return {Array<number>} An array representing the triangulated faces. Each face is defined by three consecutive numbers
* representing vertex indices.
*/
static triangulate( data, holeIndices, dim = 2 ) {
return earcut( data, holeIndices, dim );
}
}
export { Earcut };

137
node_modules/three/src/extras/ImageUtils.js generated vendored Normal file
View File

@@ -0,0 +1,137 @@
import { createElementNS, warn } from '../utils.js';
import { SRGBToLinear } from '../math/ColorManagement.js';
let _canvas;
/**
* A class containing utility functions for images.
*
* @hideconstructor
*/
class ImageUtils {
/**
* Returns a data URI containing a representation of the given image.
*
* @param {(HTMLImageElement|HTMLCanvasElement)} image - The image object.
* @param {string} [type='image/png'] - Indicates the image format.
* @return {string} The data URI.
*/
static getDataURL( image, type = 'image/png' ) {
if ( /^data:/i.test( image.src ) ) {
return image.src;
}
if ( typeof HTMLCanvasElement === 'undefined' ) {
return image.src;
}
let canvas;
if ( image instanceof HTMLCanvasElement ) {
canvas = image;
} else {
if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' );
_canvas.width = image.width;
_canvas.height = image.height;
const context = _canvas.getContext( '2d' );
if ( image instanceof ImageData ) {
context.putImageData( image, 0, 0 );
} else {
context.drawImage( image, 0, 0, image.width, image.height );
}
canvas = _canvas;
}
return canvas.toDataURL( type );
}
/**
* Converts the given sRGB image data to linear color space.
*
* @param {(HTMLImageElement|HTMLCanvasElement|ImageBitmap|Object)} image - The image object.
* @return {HTMLCanvasElement|Object} The converted image.
*/
static sRGBToLinear( image ) {
if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
const canvas = createElementNS( 'canvas' );
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext( '2d' );
context.drawImage( image, 0, 0, image.width, image.height );
const imageData = context.getImageData( 0, 0, image.width, image.height );
const data = imageData.data;
for ( let i = 0; i < data.length; i ++ ) {
data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;
}
context.putImageData( imageData, 0, 0 );
return canvas;
} else if ( image.data ) {
const data = image.data.slice( 0 );
for ( let i = 0; i < data.length; i ++ ) {
if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {
data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );
} else {
// assuming float
data[ i ] = SRGBToLinear( data[ i ] );
}
}
return {
data: data,
width: image.width,
height: image.height
};
} else {
warn( 'ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );
return image;
}
}
}
export { ImageUtils };

1167
node_modules/three/src/extras/PMREMGenerator.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

114
node_modules/three/src/extras/ShapeUtils.js generated vendored Normal file
View File

@@ -0,0 +1,114 @@
import { Earcut } from './Earcut.js';
/**
* A class containing utility functions for shapes.
*
* @hideconstructor
*/
class ShapeUtils {
/**
* Calculate area of a ( 2D ) contour polygon.
*
* @param {Array<Vector2>} contour - An array of 2D points.
* @return {number} The area.
*/
static area( contour ) {
const n = contour.length;
let a = 0.0;
for ( let p = n - 1, q = 0; q < n; p = q ++ ) {
a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
}
return a * 0.5;
}
/**
* Returns `true` if the given contour uses a clockwise winding order.
*
* @param {Array<Vector2>} pts - An array of 2D points defining a polygon.
* @return {boolean} Whether the given contour uses a clockwise winding order or not.
*/
static isClockWise( pts ) {
return ShapeUtils.area( pts ) < 0;
}
/**
* Triangulates the given shape definition.
*
* @param {Array<Vector2>} contour - An array of 2D points defining the contour.
* @param {Array<Array<Vector2>>} holes - An array that holds arrays of 2D points defining the holes.
* @return {Array<Array<number>>} An array that holds for each face definition an array with three indices.
*/
static triangulateShape( contour, holes ) {
const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]
const holeIndices = []; // array of hole indices
const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]
removeDupEndPts( contour );
addContour( vertices, contour );
//
let holeIndex = contour.length;
holes.forEach( removeDupEndPts );
for ( let i = 0; i < holes.length; i ++ ) {
holeIndices.push( holeIndex );
holeIndex += holes[ i ].length;
addContour( vertices, holes[ i ] );
}
//
const triangles = Earcut.triangulate( vertices, holeIndices );
//
for ( let i = 0; i < triangles.length; i += 3 ) {
faces.push( triangles.slice( i, i + 3 ) );
}
return faces;
}
}
function removeDupEndPts( points ) {
const l = points.length;
if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {
points.pop();
}
}
function addContour( vertices, contour ) {
for ( let i = 0; i < contour.length; i ++ ) {
vertices.push( contour[ i ].x );
vertices.push( contour[ i ].y );
}
}
export { ShapeUtils };

297
node_modules/three/src/extras/TextureUtils.js generated vendored Normal file
View File

@@ -0,0 +1,297 @@
import { AlphaFormat, RedFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBFormat, RGBAFormat, RGBAIntegerFormat, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, RG11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, UnsignedByteType, ByteType, UnsignedShortType, ShortType, HalfFloatType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedIntType, IntType, FloatType, UnsignedInt5999Type, UnsignedInt101111Type } from '../constants.js';
/**
* Scales the texture as large as possible within its surface without cropping
* or stretching the texture. The method preserves the original aspect ratio of
* the texture. Akin to CSS `object-fit: contain`
*
* @param {Texture} texture - The texture.
* @param {number} aspect - The texture's aspect ratio.
* @return {Texture} The updated texture.
*/
function contain( texture, aspect ) {
const imageAspect = ( texture.image && texture.image.width ) ? texture.image.width / texture.image.height : 1;
if ( imageAspect > aspect ) {
texture.repeat.x = 1;
texture.repeat.y = imageAspect / aspect;
texture.offset.x = 0;
texture.offset.y = ( 1 - texture.repeat.y ) / 2;
} else {
texture.repeat.x = aspect / imageAspect;
texture.repeat.y = 1;
texture.offset.x = ( 1 - texture.repeat.x ) / 2;
texture.offset.y = 0;
}
return texture;
}
/**
* Scales the texture to the smallest possible size to fill the surface, leaving
* no empty space. The method preserves the original aspect ratio of the texture.
* Akin to CSS `object-fit: cover`.
*
* @param {Texture} texture - The texture.
* @param {number} aspect - The texture's aspect ratio.
* @return {Texture} The updated texture.
*/
function cover( texture, aspect ) {
const imageAspect = ( texture.image && texture.image.width ) ? texture.image.width / texture.image.height : 1;
if ( imageAspect > aspect ) {
texture.repeat.x = aspect / imageAspect;
texture.repeat.y = 1;
texture.offset.x = ( 1 - texture.repeat.x ) / 2;
texture.offset.y = 0;
} else {
texture.repeat.x = 1;
texture.repeat.y = imageAspect / aspect;
texture.offset.x = 0;
texture.offset.y = ( 1 - texture.repeat.y ) / 2;
}
return texture;
}
/**
* Configures the texture to the default transformation. Akin to CSS `object-fit: fill`.
*
* @param {Texture} texture - The texture.
* @return {Texture} The updated texture.
*/
function fill( texture ) {
texture.repeat.x = 1;
texture.repeat.y = 1;
texture.offset.x = 0;
texture.offset.y = 0;
return texture;
}
/**
* Determines how many bytes must be used to represent the texture.
*
* @param {number} width - The width of the texture.
* @param {number} height - The height of the texture.
* @param {number} format - The texture's format.
* @param {number} type - The texture's type.
* @return {number} The byte length.
*/
function getByteLength( width, height, format, type ) {
const typeByteLength = getTextureTypeByteLength( type );
switch ( format ) {
// https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml
case AlphaFormat:
return width * height;
case RedFormat:
return ( ( width * height ) / typeByteLength.components ) * typeByteLength.byteLength;
case RedIntegerFormat:
return ( ( width * height ) / typeByteLength.components ) * typeByteLength.byteLength;
case RGFormat:
return ( ( width * height * 2 ) / typeByteLength.components ) * typeByteLength.byteLength;
case RGIntegerFormat:
return ( ( width * height * 2 ) / typeByteLength.components ) * typeByteLength.byteLength;
case RGBFormat:
return ( ( width * height * 3 ) / typeByteLength.components ) * typeByteLength.byteLength;
case RGBAFormat:
return ( ( width * height * 4 ) / typeByteLength.components ) * typeByteLength.byteLength;
case RGBAIntegerFormat:
return ( ( width * height * 4 ) / typeByteLength.components ) * typeByteLength.byteLength;
// https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_s3tc_srgb/
case RGB_S3TC_DXT1_Format:
case RGBA_S3TC_DXT1_Format:
return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 8;
case RGBA_S3TC_DXT3_Format:
case RGBA_S3TC_DXT5_Format:
return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16;
// https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_pvrtc/
case RGB_PVRTC_2BPPV1_Format:
case RGBA_PVRTC_2BPPV1_Format:
return ( Math.max( width, 16 ) * Math.max( height, 8 ) ) / 4;
case RGB_PVRTC_4BPPV1_Format:
case RGBA_PVRTC_4BPPV1_Format:
return ( Math.max( width, 8 ) * Math.max( height, 8 ) ) / 2;
// https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_etc/
case RGB_ETC1_Format:
case RGB_ETC2_Format:
case R11_EAC_Format:
case SIGNED_R11_EAC_Format:
return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 8;
case RGBA_ETC2_EAC_Format:
case RG11_EAC_Format:
case SIGNED_RG11_EAC_Format:
return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16;
// https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_astc/
case RGBA_ASTC_4x4_Format:
return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16;
case RGBA_ASTC_5x4_Format:
return Math.floor( ( width + 4 ) / 5 ) * Math.floor( ( height + 3 ) / 4 ) * 16;
case RGBA_ASTC_5x5_Format:
return Math.floor( ( width + 4 ) / 5 ) * Math.floor( ( height + 4 ) / 5 ) * 16;
case RGBA_ASTC_6x5_Format:
return Math.floor( ( width + 5 ) / 6 ) * Math.floor( ( height + 4 ) / 5 ) * 16;
case RGBA_ASTC_6x6_Format:
return Math.floor( ( width + 5 ) / 6 ) * Math.floor( ( height + 5 ) / 6 ) * 16;
case RGBA_ASTC_8x5_Format:
return Math.floor( ( width + 7 ) / 8 ) * Math.floor( ( height + 4 ) / 5 ) * 16;
case RGBA_ASTC_8x6_Format:
return Math.floor( ( width + 7 ) / 8 ) * Math.floor( ( height + 5 ) / 6 ) * 16;
case RGBA_ASTC_8x8_Format:
return Math.floor( ( width + 7 ) / 8 ) * Math.floor( ( height + 7 ) / 8 ) * 16;
case RGBA_ASTC_10x5_Format:
return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 4 ) / 5 ) * 16;
case RGBA_ASTC_10x6_Format:
return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 5 ) / 6 ) * 16;
case RGBA_ASTC_10x8_Format:
return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 7 ) / 8 ) * 16;
case RGBA_ASTC_10x10_Format:
return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 9 ) / 10 ) * 16;
case RGBA_ASTC_12x10_Format:
return Math.floor( ( width + 11 ) / 12 ) * Math.floor( ( height + 9 ) / 10 ) * 16;
case RGBA_ASTC_12x12_Format:
return Math.floor( ( width + 11 ) / 12 ) * Math.floor( ( height + 11 ) / 12 ) * 16;
// https://registry.khronos.org/webgl/extensions/EXT_texture_compression_bptc/
case RGBA_BPTC_Format:
case RGB_BPTC_SIGNED_Format:
case RGB_BPTC_UNSIGNED_Format:
return Math.ceil( width / 4 ) * Math.ceil( height / 4 ) * 16;
// https://registry.khronos.org/webgl/extensions/EXT_texture_compression_rgtc/
case RED_RGTC1_Format:
case SIGNED_RED_RGTC1_Format:
return Math.ceil( width / 4 ) * Math.ceil( height / 4 ) * 8;
case RED_GREEN_RGTC2_Format:
case SIGNED_RED_GREEN_RGTC2_Format:
return Math.ceil( width / 4 ) * Math.ceil( height / 4 ) * 16;
}
throw new Error(
`Unable to determine texture byte length for ${format} format.`,
);
}
function getTextureTypeByteLength( type ) {
switch ( type ) {
case UnsignedByteType:
case ByteType:
return { byteLength: 1, components: 1 };
case UnsignedShortType:
case ShortType:
case HalfFloatType:
return { byteLength: 2, components: 1 };
case UnsignedShort4444Type:
case UnsignedShort5551Type:
return { byteLength: 2, components: 4 };
case UnsignedIntType:
case IntType:
case FloatType:
return { byteLength: 4, components: 1 };
case UnsignedInt5999Type:
case UnsignedInt101111Type:
return { byteLength: 4, components: 3 };
}
throw new Error( `Unknown texture type ${type}.` );
}
/**
* A class containing utility functions for textures.
*
* @hideconstructor
*/
class TextureUtils {
/**
* Scales the texture as large as possible within its surface without cropping
* or stretching the texture. The method preserves the original aspect ratio of
* the texture. Akin to CSS `object-fit: contain`
*
* @param {Texture} texture - The texture.
* @param {number} aspect - The texture's aspect ratio.
* @return {Texture} The updated texture.
*/
static contain( texture, aspect ) {
return contain( texture, aspect );
}
/**
* Scales the texture to the smallest possible size to fill the surface, leaving
* no empty space. The method preserves the original aspect ratio of the texture.
* Akin to CSS `object-fit: cover`.
*
* @param {Texture} texture - The texture.
* @param {number} aspect - The texture's aspect ratio.
* @return {Texture} The updated texture.
*/
static cover( texture, aspect ) {
return cover( texture, aspect );
}
/**
* Configures the texture to the default transformation. Akin to CSS `object-fit: fill`.
*
* @param {Texture} texture - The texture.
* @return {Texture} The updated texture.
*/
static fill( texture ) {
return fill( texture );
}
/**
* Determines how many bytes must be used to represent the texture.
*
* @param {number} width - The width of the texture.
* @param {number} height - The height of the texture.
* @param {number} format - The texture's format.
* @param {number} type - The texture's type.
* @return {number} The byte length.
*/
static getByteLength( width, height, format, type ) {
return getByteLength( width, height, format, type );
}
}
export { contain, cover, fill, getByteLength, TextureUtils };

517
node_modules/three/src/extras/core/Curve.js generated vendored Normal file
View File

@@ -0,0 +1,517 @@
import { clamp } from '../../math/MathUtils.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { warn } from '../../utils.js';
/**
* An abstract base class for creating an analytic curve object that contains methods
* for interpolation.
*
* @abstract
*/
class Curve {
/**
* Constructs a new curve.
*/
constructor() {
/**
* The type property is used for detecting the object type
* in context of serialization/deserialization.
*
* @type {string}
* @readonly
*/
this.type = 'Curve';
/**
* This value determines the amount of divisions when calculating the
* cumulative segment lengths of a curve via {@link Curve#getLengths}. To ensure
* precision when using methods like {@link Curve#getSpacedPoints}, it is
* recommended to increase the value of this property if the curve is very large.
*
* @type {number}
* @default 200
*/
this.arcLengthDivisions = 200;
/**
* Must be set to `true` if the curve parameters have changed.
*
* @type {boolean}
* @default false
*/
this.needsUpdate = false;
/**
* An internal cache that holds precomputed curve length values.
*
* @private
* @type {?Array<number>}
* @default null
*/
this.cacheArcLengths = null;
}
/**
* This method returns a vector in 2D or 3D space (depending on the curve definition)
* for the given interpolation factor.
*
* @abstract
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to.
* @return {(Vector2|Vector3)} The position on the curve. It can be a 2D or 3D vector depending on the curve definition.
*/
getPoint( /* t, optionalTarget */ ) {
warn( 'Curve: .getPoint() not implemented.' );
}
/**
* This method returns a vector in 2D or 3D space (depending on the curve definition)
* for the given interpolation factor. Unlike {@link Curve#getPoint}, this method honors the length
* of the curve which equidistant samples.
*
* @param {number} u - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to.
* @return {(Vector2|Vector3)} The position on the curve. It can be a 2D or 3D vector depending on the curve definition.
*/
getPointAt( u, optionalTarget ) {
const t = this.getUtoTmapping( u );
return this.getPoint( t, optionalTarget );
}
/**
* This method samples the curve via {@link Curve#getPoint} and returns an array of points representing
* the curve shape.
*
* @param {number} [divisions=5] - The number of divisions.
* @return {Array<(Vector2|Vector3)>} An array holding the sampled curve values. The number of points is `divisions + 1`.
*/
getPoints( divisions = 5 ) {
const points = [];
for ( let d = 0; d <= divisions; d ++ ) {
points.push( this.getPoint( d / divisions ) );
}
return points;
}
// Get sequence of points using getPointAt( u )
/**
* This method samples the curve via {@link Curve#getPointAt} and returns an array of points representing
* the curve shape. Unlike {@link Curve#getPoints}, this method returns equi-spaced points across the entire
* curve.
*
* @param {number} [divisions=5] - The number of divisions.
* @return {Array<(Vector2|Vector3)>} An array holding the sampled curve values. The number of points is `divisions + 1`.
*/
getSpacedPoints( divisions = 5 ) {
const points = [];
for ( let d = 0; d <= divisions; d ++ ) {
points.push( this.getPointAt( d / divisions ) );
}
return points;
}
/**
* Returns the total arc length of the curve.
*
* @return {number} The length of the curve.
*/
getLength() {
const lengths = this.getLengths();
return lengths[ lengths.length - 1 ];
}
/**
* Returns an array of cumulative segment lengths of the curve.
*
* @param {number} [divisions=this.arcLengthDivisions] - The number of divisions.
* @return {Array<number>} An array holding the cumulative segment lengths.
*/
getLengths( divisions = this.arcLengthDivisions ) {
if ( this.cacheArcLengths &&
( this.cacheArcLengths.length === divisions + 1 ) &&
! this.needsUpdate ) {
return this.cacheArcLengths;
}
this.needsUpdate = false;
const cache = [];
let current, last = this.getPoint( 0 );
let sum = 0;
cache.push( 0 );
for ( let p = 1; p <= divisions; p ++ ) {
current = this.getPoint( p / divisions );
sum += current.distanceTo( last );
cache.push( sum );
last = current;
}
this.cacheArcLengths = cache;
return cache; // { sums: cache, sum: sum }; Sum is in the last element.
}
/**
* Update the cumulative segment distance cache. The method must be called
* every time curve parameters are changed. If an updated curve is part of a
* composed curve like {@link CurvePath}, this method must be called on the
* composed curve, too.
*/
updateArcLengths() {
this.needsUpdate = true;
this.getLengths();
}
/**
* Given an interpolation factor in the range `[0,1]`, this method returns an updated
* interpolation factor in the same range that can be ued to sample equidistant points
* from a curve.
*
* @param {number} u - The interpolation factor.
* @param {?number} distance - An optional distance on the curve.
* @return {number} The updated interpolation factor.
*/
getUtoTmapping( u, distance = null ) {
const arcLengths = this.getLengths();
let i = 0;
const il = arcLengths.length;
let targetArcLength; // The targeted u distance value to get
if ( distance ) {
targetArcLength = distance;
} else {
targetArcLength = u * arcLengths[ il - 1 ];
}
// binary search for the index with largest value smaller than target u distance
let low = 0, high = il - 1, comparison;
while ( low <= high ) {
i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
comparison = arcLengths[ i ] - targetArcLength;
if ( comparison < 0 ) {
low = i + 1;
} else if ( comparison > 0 ) {
high = i - 1;
} else {
high = i;
break;
// DONE
}
}
i = high;
if ( arcLengths[ i ] === targetArcLength ) {
return i / ( il - 1 );
}
// we could get finer grain at lengths, or use simple interpolation between two points
const lengthBefore = arcLengths[ i ];
const lengthAfter = arcLengths[ i + 1 ];
const segmentLength = lengthAfter - lengthBefore;
// determine where we are between the 'before' and 'after' points
const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
// add that fractional amount to t
const t = ( i + segmentFraction ) / ( il - 1 );
return t;
}
/**
* Returns a unit vector tangent for the given interpolation factor.
* If the derived curve does not implement its tangent derivation,
* two points a small delta apart will be used to find its gradient
* which seems to give a reasonable approximation.
*
* @param {number} t - The interpolation factor.
* @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to.
* @return {(Vector2|Vector3)} The tangent vector.
*/
getTangent( t, optionalTarget ) {
const delta = 0.0001;
let t1 = t - delta;
let t2 = t + delta;
// Capping in case of danger
if ( t1 < 0 ) t1 = 0;
if ( t2 > 1 ) t2 = 1;
const pt1 = this.getPoint( t1 );
const pt2 = this.getPoint( t2 );
const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() );
tangent.copy( pt2 ).sub( pt1 ).normalize();
return tangent;
}
/**
* Same as {@link Curve#getTangent} but with equidistant samples.
*
* @param {number} u - The interpolation factor.
* @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to.
* @return {(Vector2|Vector3)} The tangent vector.
* @see {@link Curve#getPointAt}
*/
getTangentAt( u, optionalTarget ) {
const t = this.getUtoTmapping( u );
return this.getTangent( t, optionalTarget );
}
/**
* Generates the Frenet Frames. Requires a curve definition in 3D space. Used
* in geometries like {@link TubeGeometry} or {@link ExtrudeGeometry}.
*
* @param {number} segments - The number of segments.
* @param {boolean} [closed=false] - Whether the curve is closed or not.
* @return {{tangents: Array<Vector3>, normals: Array<Vector3>, binormals: Array<Vector3>}} The Frenet Frames.
*/
computeFrenetFrames( segments, closed = false ) {
// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
const normal = new Vector3();
const tangents = [];
const normals = [];
const binormals = [];
const vec = new Vector3();
const mat = new Matrix4();
// compute the tangent vectors for each segment on the curve
for ( let i = 0; i <= segments; i ++ ) {
const u = i / segments;
tangents[ i ] = this.getTangentAt( u, new Vector3() );
}
// select an initial normal vector perpendicular to the first tangent vector,
// and in the direction of the minimum tangent xyz component
normals[ 0 ] = new Vector3();
binormals[ 0 ] = new Vector3();
let min = Number.MAX_VALUE;
const tx = Math.abs( tangents[ 0 ].x );
const ty = Math.abs( tangents[ 0 ].y );
const tz = Math.abs( tangents[ 0 ].z );
if ( tx <= min ) {
min = tx;
normal.set( 1, 0, 0 );
}
if ( ty <= min ) {
min = ty;
normal.set( 0, 1, 0 );
}
if ( tz <= min ) {
normal.set( 0, 0, 1 );
}
vec.crossVectors( tangents[ 0 ], normal ).normalize();
normals[ 0 ].crossVectors( tangents[ 0 ], vec );
binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
// compute the slowly-varying normal and binormal vectors for each segment on the curve
for ( let i = 1; i <= segments; i ++ ) {
normals[ i ] = normals[ i - 1 ].clone();
binormals[ i ] = binormals[ i - 1 ].clone();
vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
if ( vec.length() > Number.EPSILON ) {
vec.normalize();
const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
}
binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
}
// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
if ( closed === true ) {
let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );
theta /= segments;
if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {
theta = - theta;
}
for ( let i = 1; i <= segments; i ++ ) {
// twist a little...
normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
}
}
return {
tangents: tangents,
normals: normals,
binormals: binormals
};
}
/**
* Returns a new curve with copied values from this instance.
*
* @return {Curve} A clone of this instance.
*/
clone() {
return new this.constructor().copy( this );
}
/**
* Copies the values of the given curve to this instance.
*
* @param {Curve} source - The curve to copy.
* @return {Curve} A reference to this curve.
*/
copy( source ) {
this.arcLengthDivisions = source.arcLengthDivisions;
return this;
}
/**
* Serializes the curve into JSON.
*
* @return {Object} A JSON object representing the serialized curve.
* @see {@link ObjectLoader#parse}
*/
toJSON() {
const data = {
metadata: {
version: 4.7,
type: 'Curve',
generator: 'Curve.toJSON'
}
};
data.arcLengthDivisions = this.arcLengthDivisions;
data.type = this.type;
return data;
}
/**
* Deserializes the curve from the given JSON.
*
* @param {Object} json - The JSON holding the serialized curve.
* @return {Curve} A reference to this curve.
*/
fromJSON( json ) {
this.arcLengthDivisions = json.arcLengthDivisions;
return this;
}
}
export { Curve };

296
node_modules/three/src/extras/core/CurvePath.js generated vendored Normal file
View File

@@ -0,0 +1,296 @@
import { Curve } from './Curve.js';
import * as Curves from '../curves/Curves.js';
/**
* A base class extending {@link Curve}. `CurvePath` is simply an
* array of connected curves, but retains the API of a curve.
*
* @augments Curve
*/
class CurvePath extends Curve {
/**
* Constructs a new curve path.
*/
constructor() {
super();
this.type = 'CurvePath';
/**
* An array of curves defining the
* path.
*
* @type {Array<Curve>}
*/
this.curves = [];
/**
* Whether the path should automatically be closed
* by a line curve.
*
* @type {boolean}
* @default false
*/
this.autoClose = false;
}
/**
* Adds a curve to this curve path.
*
* @param {Curve} curve - The curve to add.
*/
add( curve ) {
this.curves.push( curve );
}
/**
* Adds a line curve to close the path.
*
* @return {CurvePath} A reference to this curve path.
*/
closePath() {
// Add a line curve if start and end of lines are not connected
const startPoint = this.curves[ 0 ].getPoint( 0 );
const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
if ( ! startPoint.equals( endPoint ) ) {
const lineType = ( startPoint.isVector2 === true ) ? 'LineCurve' : 'LineCurve3';
this.curves.push( new Curves[ lineType ]( endPoint, startPoint ) );
}
return this;
}
/**
* This method returns a vector in 2D or 3D space (depending on the curve definitions)
* for the given interpolation factor.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to.
* @return {?(Vector2|Vector3)} The position on the curve. It can be a 2D or 3D vector depending on the curve definition.
*/
getPoint( t, optionalTarget ) {
// To get accurate point with reference to
// entire path distance at time t,
// following has to be done:
// 1. Length of each sub path have to be known
// 2. Locate and identify type of curve
// 3. Get t for the curve
// 4. Return curve.getPointAt(t')
const d = t * this.getLength();
const curveLengths = this.getCurveLengths();
let i = 0;
// To think about boundaries points.
while ( i < curveLengths.length ) {
if ( curveLengths[ i ] >= d ) {
const diff = curveLengths[ i ] - d;
const curve = this.curves[ i ];
const segmentLength = curve.getLength();
const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;
return curve.getPointAt( u, optionalTarget );
}
i ++;
}
return null;
// loop where sum != 0, sum > d , sum+1 <d
}
getLength() {
// We cannot use the default THREE.Curve getPoint() with getLength() because in
// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
// getPoint() depends on getLength
const lens = this.getCurveLengths();
return lens[ lens.length - 1 ];
}
updateArcLengths() {
// cacheLengths must be recalculated.
this.needsUpdate = true;
this.cacheLengths = null;
this.getCurveLengths();
}
/**
* Returns list of cumulative curve lengths of the defined curves.
*
* @return {Array<number>} The curve lengths.
*/
getCurveLengths() {
// Compute lengths and cache them
// We cannot overwrite getLengths() because UtoT mapping uses it.
// We use cache values if curves and cache array are same length
if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {
return this.cacheLengths;
}
// Get length of sub-curve
// Push sums into cached array
const lengths = [];
let sums = 0;
for ( let i = 0, l = this.curves.length; i < l; i ++ ) {
sums += this.curves[ i ].getLength();
lengths.push( sums );
}
this.cacheLengths = lengths;
return lengths;
}
getSpacedPoints( divisions = 40 ) {
const points = [];
for ( let i = 0; i <= divisions; i ++ ) {
points.push( this.getPoint( i / divisions ) );
}
if ( this.autoClose ) {
points.push( points[ 0 ] );
}
return points;
}
getPoints( divisions = 12 ) {
const points = [];
let last;
for ( let i = 0, curves = this.curves; i < curves.length; i ++ ) {
const curve = curves[ i ];
const resolution = curve.isEllipseCurve ? divisions * 2
: ( curve.isLineCurve || curve.isLineCurve3 ) ? 1
: curve.isSplineCurve ? divisions * curve.points.length
: divisions;
const pts = curve.getPoints( resolution );
for ( let j = 0; j < pts.length; j ++ ) {
const point = pts[ j ];
if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates
points.push( point );
last = point;
}
}
if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {
points.push( points[ 0 ] );
}
return points;
}
copy( source ) {
super.copy( source );
this.curves = [];
for ( let i = 0, l = source.curves.length; i < l; i ++ ) {
const curve = source.curves[ i ];
this.curves.push( curve.clone() );
}
this.autoClose = source.autoClose;
return this;
}
toJSON() {
const data = super.toJSON();
data.autoClose = this.autoClose;
data.curves = [];
for ( let i = 0, l = this.curves.length; i < l; i ++ ) {
const curve = this.curves[ i ];
data.curves.push( curve.toJSON() );
}
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.autoClose = json.autoClose;
this.curves = [];
for ( let i = 0, l = json.curves.length; i < l; i ++ ) {
const curve = json.curves[ i ];
this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );
}
return this;
}
}
export { CurvePath };

111
node_modules/three/src/extras/core/Interpolations.js generated vendored Normal file
View File

@@ -0,0 +1,111 @@
/**
* Interpolations contains spline and Bézier functions internally used by concrete curve classes.
*
* Bezier Curves formulas obtained from: https://en.wikipedia.org/wiki/B%C3%A9zier_curve
*
* @module Interpolations
*/
/**
* Computes a point on a Catmull-Rom spline.
*
* @param {number} t - The interpolation factor.
* @param {number} p0 - The first control point.
* @param {number} p1 - The second control point.
* @param {number} p2 - The third control point.
* @param {number} p3 - The fourth control point.
* @return {number} The calculated point on a Catmull-Rom spline.
*/
function CatmullRom( t, p0, p1, p2, p3 ) {
const v0 = ( p2 - p0 ) * 0.5;
const v1 = ( p3 - p1 ) * 0.5;
const t2 = t * t;
const t3 = t * t2;
return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
}
//
function QuadraticBezierP0( t, p ) {
const k = 1 - t;
return k * k * p;
}
function QuadraticBezierP1( t, p ) {
return 2 * ( 1 - t ) * t * p;
}
function QuadraticBezierP2( t, p ) {
return t * t * p;
}
/**
* Computes a point on a Quadratic Bezier curve.
*
* @param {number} t - The interpolation factor.
* @param {number} p0 - The first control point.
* @param {number} p1 - The second control point.
* @param {number} p2 - The third control point.
* @return {number} The calculated point on a Quadratic Bezier curve.
*/
function QuadraticBezier( t, p0, p1, p2 ) {
return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +
QuadraticBezierP2( t, p2 );
}
//
function CubicBezierP0( t, p ) {
const k = 1 - t;
return k * k * k * p;
}
function CubicBezierP1( t, p ) {
const k = 1 - t;
return 3 * k * k * t * p;
}
function CubicBezierP2( t, p ) {
return 3 * ( 1 - t ) * t * t * p;
}
function CubicBezierP3( t, p ) {
return t * t * t * p;
}
/**
* Computes a point on a Cubic Bezier curve.
*
* @param {number} t - The interpolation factor.
* @param {number} p0 - The first control point.
* @param {number} p1 - The second control point.
* @param {number} p2 - The third control point.
* @param {number} p3 - The fourth control point.
* @return {number} The calculated point on a Cubic Bezier curve.
*/
function CubicBezier( t, p0, p1, p2, p3 ) {
return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +
CubicBezierP3( t, p3 );
}
export { CatmullRom, QuadraticBezier, CubicBezier };

329
node_modules/three/src/extras/core/Path.js generated vendored Normal file
View File

@@ -0,0 +1,329 @@
import { Vector2 } from '../../math/Vector2.js';
import { CurvePath } from './CurvePath.js';
import { EllipseCurve } from '../curves/EllipseCurve.js';
import { SplineCurve } from '../curves/SplineCurve.js';
import { CubicBezierCurve } from '../curves/CubicBezierCurve.js';
import { QuadraticBezierCurve } from '../curves/QuadraticBezierCurve.js';
import { LineCurve } from '../curves/LineCurve.js';
/**
* A 2D path representation. The class provides methods for creating paths
* and contours of 2D shapes similar to the 2D Canvas API.
*
* ```js
* const path = new THREE.Path();
*
* path.lineTo( 0, 0.8 );
* path.quadraticCurveTo( 0, 1, 0.2, 1 );
* path.lineTo( 1, 1 );
*
* const points = path.getPoints();
*
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
* const material = new THREE.LineBasicMaterial( { color: 0xffffff } );
*
* const line = new THREE.Line( geometry, material );
* scene.add( line );
* ```
*
* @augments CurvePath
*/
class Path extends CurvePath {
/**
* Constructs a new path.
*
* @param {Array<Vector2>} [points] - An array of 2D points defining the path.
*/
constructor( points ) {
super();
this.type = 'Path';
/**
* The current offset of the path. Any new curve added will start here.
*
* @type {Vector2}
*/
this.currentPoint = new Vector2();
if ( points ) {
this.setFromPoints( points );
}
}
/**
* Creates a path from the given list of points. The points are added
* to the path as instances of {@link LineCurve}.
*
* @param {Array<Vector2>} points - An array of 2D points.
* @return {Path} A reference to this path.
*/
setFromPoints( points ) {
this.moveTo( points[ 0 ].x, points[ 0 ].y );
for ( let i = 1, l = points.length; i < l; i ++ ) {
this.lineTo( points[ i ].x, points[ i ].y );
}
return this;
}
/**
* Moves {@link Path#currentPoint} to the given point.
*
* @param {number} x - The x coordinate.
* @param {number} y - The y coordinate.
* @return {Path} A reference to this path.
*/
moveTo( x, y ) {
this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?
return this;
}
/**
* Adds an instance of {@link LineCurve} to the path by connecting
* the current point with the given one.
*
* @param {number} x - The x coordinate of the end point.
* @param {number} y - The y coordinate of the end point.
* @return {Path} A reference to this path.
*/
lineTo( x, y ) {
const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) );
this.curves.push( curve );
this.currentPoint.set( x, y );
return this;
}
/**
* Adds an instance of {@link QuadraticBezierCurve} to the path by connecting
* the current point with the given one.
*
* @param {number} aCPx - The x coordinate of the control point.
* @param {number} aCPy - The y coordinate of the control point.
* @param {number} aX - The x coordinate of the end point.
* @param {number} aY - The y coordinate of the end point.
* @return {Path} A reference to this path.
*/
quadraticCurveTo( aCPx, aCPy, aX, aY ) {
const curve = new QuadraticBezierCurve(
this.currentPoint.clone(),
new Vector2( aCPx, aCPy ),
new Vector2( aX, aY )
);
this.curves.push( curve );
this.currentPoint.set( aX, aY );
return this;
}
/**
* Adds an instance of {@link CubicBezierCurve} to the path by connecting
* the current point with the given one.
*
* @param {number} aCP1x - The x coordinate of the first control point.
* @param {number} aCP1y - The y coordinate of the first control point.
* @param {number} aCP2x - The x coordinate of the second control point.
* @param {number} aCP2y - The y coordinate of the second control point.
* @param {number} aX - The x coordinate of the end point.
* @param {number} aY - The y coordinate of the end point.
* @return {Path} A reference to this path.
*/
bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
const curve = new CubicBezierCurve(
this.currentPoint.clone(),
new Vector2( aCP1x, aCP1y ),
new Vector2( aCP2x, aCP2y ),
new Vector2( aX, aY )
);
this.curves.push( curve );
this.currentPoint.set( aX, aY );
return this;
}
/**
* Adds an instance of {@link SplineCurve} to the path by connecting
* the current point with the given list of points.
*
* @param {Array<Vector2>} pts - An array of points in 2D space.
* @return {Path} A reference to this path.
*/
splineThru( pts ) {
const npts = [ this.currentPoint.clone() ].concat( pts );
const curve = new SplineCurve( npts );
this.curves.push( curve );
this.currentPoint.copy( pts[ pts.length - 1 ] );
return this;
}
/**
* Adds an arc as an instance of {@link EllipseCurve} to the path, positioned relative
* to the current point.
*
* @param {number} [aX=0] - The x coordinate of the center of the arc offsetted from the previous curve.
* @param {number} [aY=0] - The y coordinate of the center of the arc offsetted from the previous curve.
* @param {number} [aRadius=1] - The radius of the arc.
* @param {number} [aStartAngle=0] - The start angle in radians.
* @param {number} [aEndAngle=Math.PI*2] - The end angle in radians.
* @param {boolean} [aClockwise=false] - Whether to sweep the arc clockwise or not.
* @return {Path} A reference to this path.
*/
arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
const x0 = this.currentPoint.x;
const y0 = this.currentPoint.y;
this.absarc( aX + x0, aY + y0, aRadius,
aStartAngle, aEndAngle, aClockwise );
return this;
}
/**
* Adds an absolutely positioned arc as an instance of {@link EllipseCurve} to the path.
*
* @param {number} [aX=0] - The x coordinate of the center of the arc.
* @param {number} [aY=0] - The y coordinate of the center of the arc.
* @param {number} [aRadius=1] - The radius of the arc.
* @param {number} [aStartAngle=0] - The start angle in radians.
* @param {number} [aEndAngle=Math.PI*2] - The end angle in radians.
* @param {boolean} [aClockwise=false] - Whether to sweep the arc clockwise or not.
* @return {Path} A reference to this path.
*/
absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
return this;
}
/**
* Adds an ellipse as an instance of {@link EllipseCurve} to the path, positioned relative
* to the current point
*
* @param {number} [aX=0] - The x coordinate of the center of the ellipse offsetted from the previous curve.
* @param {number} [aY=0] - The y coordinate of the center of the ellipse offsetted from the previous curve.
* @param {number} [xRadius=1] - The radius of the ellipse in the x axis.
* @param {number} [yRadius=1] - The radius of the ellipse in the y axis.
* @param {number} [aStartAngle=0] - The start angle in radians.
* @param {number} [aEndAngle=Math.PI*2] - The end angle in radians.
* @param {boolean} [aClockwise=false] - Whether to sweep the ellipse clockwise or not.
* @param {number} [aRotation=0] - The rotation angle of the ellipse in radians, counterclockwise from the positive X axis.
* @return {Path} A reference to this path.
*/
ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
const x0 = this.currentPoint.x;
const y0 = this.currentPoint.y;
this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
return this;
}
/**
* Adds an absolutely positioned ellipse as an instance of {@link EllipseCurve} to the path.
*
* @param {number} [aX=0] - The x coordinate of the absolute center of the ellipse.
* @param {number} [aY=0] - The y coordinate of the absolute center of the ellipse.
* @param {number} [xRadius=1] - The radius of the ellipse in the x axis.
* @param {number} [yRadius=1] - The radius of the ellipse in the y axis.
* @param {number} [aStartAngle=0] - The start angle in radians.
* @param {number} [aEndAngle=Math.PI*2] - The end angle in radians.
* @param {boolean} [aClockwise=false] - Whether to sweep the ellipse clockwise or not.
* @param {number} [aRotation=0] - The rotation angle of the ellipse in radians, counterclockwise from the positive X axis.
* @return {Path} A reference to this path.
*/
absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
if ( this.curves.length > 0 ) {
// if a previous curve is present, attempt to join
const firstPoint = curve.getPoint( 0 );
if ( ! firstPoint.equals( this.currentPoint ) ) {
this.lineTo( firstPoint.x, firstPoint.y );
}
}
this.curves.push( curve );
const lastPoint = curve.getPoint( 1 );
this.currentPoint.copy( lastPoint );
return this;
}
copy( source ) {
super.copy( source );
this.currentPoint.copy( source.currentPoint );
return this;
}
toJSON() {
const data = super.toJSON();
data.currentPoint = this.currentPoint.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.currentPoint.fromArray( json.currentPoint );
return this;
}
}
export { Path };

165
node_modules/three/src/extras/core/Shape.js generated vendored Normal file
View File

@@ -0,0 +1,165 @@
import { Path } from './Path.js';
import { generateUUID } from '../../math/MathUtils.js';
/**
* Defines an arbitrary 2d shape plane using paths with optional holes. It
* can be used with {@link ExtrudeGeometry}, {@link ShapeGeometry}, to get
* points, or to get triangulated faces.
*
* ```js
* const heartShape = new THREE.Shape();
*
* heartShape.moveTo( 25, 25 );
* heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 );
* heartShape.bezierCurveTo( - 30, 0, - 30, 35, - 30, 35 );
* heartShape.bezierCurveTo( - 30, 55, - 10, 77, 25, 95 );
* heartShape.bezierCurveTo( 60, 77, 80, 55, 80, 35 );
* heartShape.bezierCurveTo( 80, 35, 80, 0, 50, 0 );
* heartShape.bezierCurveTo( 35, 0, 25, 25, 25, 25 );
*
* const extrudeSettings = {
* depth: 8,
* bevelEnabled: true,
* bevelSegments: 2,
* steps: 2,
* bevelSize: 1,
* bevelThickness: 1
* };
*
* const geometry = new THREE.ExtrudeGeometry( heartShape, extrudeSettings );
* const mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial() );
* ```
*
* @augments Path
*/
class Shape extends Path {
/**
* Constructs a new shape.
*
* @param {Array<Vector2>} [points] - An array of 2D points defining the shape.
*/
constructor( points ) {
super( points );
/**
* The UUID of the shape.
*
* @type {string}
* @readonly
*/
this.uuid = generateUUID();
this.type = 'Shape';
/**
* Defines the holes in the shape. Hole definitions must use the
* opposite winding order (CW/CCW) than the outer shape.
*
* @type {Array<Path>}
* @readonly
*/
this.holes = [];
}
/**
* Returns an array representing each contour of the holes
* as a list of 2D points.
*
* @param {number} divisions - The fineness of the result.
* @return {Array<Array<Vector2>>} The holes as a series of 2D points.
*/
getPointsHoles( divisions ) {
const holesPts = [];
for ( let i = 0, l = this.holes.length; i < l; i ++ ) {
holesPts[ i ] = this.holes[ i ].getPoints( divisions );
}
return holesPts;
}
// get points of shape and holes (keypoints based on segments parameter)
/**
* Returns an object that holds contour data for the shape and its holes as
* arrays of 2D points.
*
* @param {number} divisions - The fineness of the result.
* @return {{shape:Array<Vector2>,holes:Array<Array<Vector2>>}} An object with contour data.
*/
extractPoints( divisions ) {
return {
shape: this.getPoints( divisions ),
holes: this.getPointsHoles( divisions )
};
}
copy( source ) {
super.copy( source );
this.holes = [];
for ( let i = 0, l = source.holes.length; i < l; i ++ ) {
const hole = source.holes[ i ];
this.holes.push( hole.clone() );
}
return this;
}
toJSON() {
const data = super.toJSON();
data.uuid = this.uuid;
data.holes = [];
for ( let i = 0, l = this.holes.length; i < l; i ++ ) {
const hole = this.holes[ i ];
data.holes.push( hole.toJSON() );
}
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.uuid = json.uuid;
this.holes = [];
for ( let i = 0, l = json.holes.length; i < l; i ++ ) {
const hole = json.holes[ i ];
this.holes.push( new Path().fromJSON( hole ) );
}
return this;
}
}
export { Shape };

367
node_modules/three/src/extras/core/ShapePath.js generated vendored Normal file
View File

@@ -0,0 +1,367 @@
import { Color } from '../../math/Color.js';
import { Path } from './Path.js';
import { Shape } from './Shape.js';
import { ShapeUtils } from '../ShapeUtils.js';
/**
* This class is used to convert a series of paths to an array of
* shapes. It is specifically used in context of fonts and SVG.
*/
class ShapePath {
/**
* Constructs a new shape path.
*/
constructor() {
this.type = 'ShapePath';
/**
* The color of the shape.
*
* @type {Color}
*/
this.color = new Color();
/**
* The paths that have been generated for this shape.
*
* @type {Array<Path>}
* @default null
*/
this.subPaths = [];
/**
* The current path that is being generated.
*
* @type {?Path}
* @default null
*/
this.currentPath = null;
}
/**
* Creates a new path and moves it current point to the given one.
*
* @param {number} x - The x coordinate.
* @param {number} y - The y coordinate.
* @return {ShapePath} A reference to this shape path.
*/
moveTo( x, y ) {
this.currentPath = new Path();
this.subPaths.push( this.currentPath );
this.currentPath.moveTo( x, y );
return this;
}
/**
* Adds an instance of {@link LineCurve} to the path by connecting
* the current point with the given one.
*
* @param {number} x - The x coordinate of the end point.
* @param {number} y - The y coordinate of the end point.
* @return {ShapePath} A reference to this shape path.
*/
lineTo( x, y ) {
this.currentPath.lineTo( x, y );
return this;
}
/**
* Adds an instance of {@link QuadraticBezierCurve} to the path by connecting
* the current point with the given one.
*
* @param {number} aCPx - The x coordinate of the control point.
* @param {number} aCPy - The y coordinate of the control point.
* @param {number} aX - The x coordinate of the end point.
* @param {number} aY - The y coordinate of the end point.
* @return {ShapePath} A reference to this shape path.
*/
quadraticCurveTo( aCPx, aCPy, aX, aY ) {
this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
return this;
}
/**
* Adds an instance of {@link CubicBezierCurve} to the path by connecting
* the current point with the given one.
*
* @param {number} aCP1x - The x coordinate of the first control point.
* @param {number} aCP1y - The y coordinate of the first control point.
* @param {number} aCP2x - The x coordinate of the second control point.
* @param {number} aCP2y - The y coordinate of the second control point.
* @param {number} aX - The x coordinate of the end point.
* @param {number} aY - The y coordinate of the end point.
* @return {ShapePath} A reference to this shape path.
*/
bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
return this;
}
/**
* Adds an instance of {@link SplineCurve} to the path by connecting
* the current point with the given list of points.
*
* @param {Array<Vector2>} pts - An array of points in 2D space.
* @return {ShapePath} A reference to this shape path.
*/
splineThru( pts ) {
this.currentPath.splineThru( pts );
return this;
}
/**
* Converts the paths into an array of shapes.
*
* @param {boolean} isCCW - By default solid shapes are defined clockwise (CW) and holes are defined counterclockwise (CCW).
* If this flag is set to `true`, then those are flipped.
* @return {Array<Shape>} An array of shapes.
*/
toShapes( isCCW ) {
function toShapesNoHoles( inSubpaths ) {
const shapes = [];
for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) {
const tmpPath = inSubpaths[ i ];
const tmpShape = new Shape();
tmpShape.curves = tmpPath.curves;
shapes.push( tmpShape );
}
return shapes;
}
function isPointInsidePolygon( inPt, inPolygon ) {
const polyLen = inPolygon.length;
// inPt on polygon contour => immediate success or
// toggling of inside/outside at every single! intersection point of an edge
// with the horizontal line through inPt, left of inPt
// not counting lowerY endpoints of edges and whole edges on that line
let inside = false;
for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
let edgeLowPt = inPolygon[ p ];
let edgeHighPt = inPolygon[ q ];
let edgeDx = edgeHighPt.x - edgeLowPt.x;
let edgeDy = edgeHighPt.y - edgeLowPt.y;
if ( Math.abs( edgeDy ) > Number.EPSILON ) {
// not parallel
if ( edgeDy < 0 ) {
edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
}
if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
if ( inPt.y === edgeLowPt.y ) {
if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
// continue; // no intersection or edgeLowPt => doesn't count !!!
} else {
const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
if ( perpEdge === 0 ) return true; // inPt is on contour ?
if ( perpEdge < 0 ) continue;
inside = ! inside; // true intersection left of inPt
}
} else {
// parallel or collinear
if ( inPt.y !== edgeLowPt.y ) continue; // parallel
// edge lies on the same horizontal line as inPt
if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
// continue;
}
}
return inside;
}
const isClockWise = ShapeUtils.isClockWise;
const subPaths = this.subPaths;
if ( subPaths.length === 0 ) return [];
let solid, tmpPath, tmpShape;
const shapes = [];
if ( subPaths.length === 1 ) {
tmpPath = subPaths[ 0 ];
tmpShape = new Shape();
tmpShape.curves = tmpPath.curves;
shapes.push( tmpShape );
return shapes;
}
let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
holesFirst = isCCW ? ! holesFirst : holesFirst;
// log("Holes first", holesFirst);
const betterShapeHoles = [];
const newShapes = [];
let newShapeHoles = [];
let mainIdx = 0;
let tmpPoints;
newShapes[ mainIdx ] = undefined;
newShapeHoles[ mainIdx ] = [];
for ( let i = 0, l = subPaths.length; i < l; i ++ ) {
tmpPath = subPaths[ i ];
tmpPoints = tmpPath.getPoints();
solid = isClockWise( tmpPoints );
solid = isCCW ? ! solid : solid;
if ( solid ) {
if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };
newShapes[ mainIdx ].s.curves = tmpPath.curves;
if ( holesFirst ) mainIdx ++;
newShapeHoles[ mainIdx ] = [];
//log('cw', i);
} else {
newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
//log('ccw', i);
}
}
// only Holes? -> probably all Shapes with wrong orientation
if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
if ( newShapes.length > 1 ) {
let ambiguous = false;
let toChange = 0;
for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
betterShapeHoles[ sIdx ] = [];
}
for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
const sho = newShapeHoles[ sIdx ];
for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) {
const ho = sho[ hIdx ];
let hole_unassigned = true;
for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
if ( sIdx !== s2Idx ) toChange ++;
if ( hole_unassigned ) {
hole_unassigned = false;
betterShapeHoles[ s2Idx ].push( ho );
} else {
ambiguous = true;
}
}
}
if ( hole_unassigned ) {
betterShapeHoles[ sIdx ].push( ho );
}
}
}
if ( toChange > 0 && ambiguous === false ) {
newShapeHoles = betterShapeHoles;
}
}
let tmpHoles;
for ( let i = 0, il = newShapes.length; i < il; i ++ ) {
tmpShape = newShapes[ i ].s;
shapes.push( tmpShape );
tmpHoles = newShapeHoles[ i ];
for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
tmpShape.holes.push( tmpHoles[ j ].h );
}
}
//log("shape", shapes);
return shapes;
}
}
export { ShapePath };

39
node_modules/three/src/extras/curves/ArcCurve.js generated vendored Normal file
View File

@@ -0,0 +1,39 @@
import { EllipseCurve } from './EllipseCurve.js';
/**
* A curve representing an arc.
*
* @augments EllipseCurve
*/
class ArcCurve extends EllipseCurve {
/**
* Constructs a new arc curve.
*
* @param {number} [aX=0] - The X center of the ellipse.
* @param {number} [aY=0] - The Y center of the ellipse.
* @param {number} [aRadius=1] - The radius of the ellipse in the x direction.
* @param {number} [aStartAngle=0] - The start angle of the curve in radians starting from the positive X axis.
* @param {number} [aEndAngle=Math.PI*2] - The end angle of the curve in radians starting from the positive X axis.
* @param {boolean} [aClockwise=false] - Whether the ellipse is drawn clockwise or not.
*/
constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isArcCurve = true;
this.type = 'ArcCurve';
}
}
export { ArcCurve };

View File

@@ -0,0 +1,326 @@
import { Vector3 } from '../../math/Vector3.js';
import { Curve } from '../core/Curve.js';
function CubicPoly() {
/**
* Centripetal CatmullRom Curve - which is useful for avoiding
* cusps and self-intersections in non-uniform catmull rom curves.
* http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
*
* curve.type accepts centripetal(default), chordal and catmullrom
* curve.tension is used for catmullrom which defaults to 0.5
*/
/*
Based on an optimized c++ solution in
- http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
- http://ideone.com/NoEbVM
This CubicPoly class could be used for reusing some variables and calculations,
but for three.js curve use, it could be possible inlined and flatten into a single function call
which can be placed in CurveUtils.
*/
let c0 = 0, c1 = 0, c2 = 0, c3 = 0;
/*
* Compute coefficients for a cubic polynomial
* p(s) = c0 + c1*s + c2*s^2 + c3*s^3
* such that
* p(0) = x0, p(1) = x1
* and
* p'(0) = t0, p'(1) = t1.
*/
function init( x0, x1, t0, t1 ) {
c0 = x0;
c1 = t0;
c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
c3 = 2 * x0 - 2 * x1 + t0 + t1;
}
return {
initCatmullRom: function ( x0, x1, x2, x3, tension ) {
init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
},
initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {
// compute tangents when parameterized in [t1,t2]
let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
// rescale tangents for parametrization in [0,1]
t1 *= dt1;
t2 *= dt1;
init( x1, x2, t1, t2 );
},
calc: function ( t ) {
const t2 = t * t;
const t3 = t2 * t;
return c0 + c1 * t + c2 * t2 + c3 * t3;
}
};
}
//
const tmp = /*@__PURE__*/ new Vector3();
const px = /*@__PURE__*/ new CubicPoly();
const py = /*@__PURE__*/ new CubicPoly();
const pz = /*@__PURE__*/ new CubicPoly();
/**
* A curve representing a Catmull-Rom spline.
*
* ```js
* //Create a closed wavey loop
* const curve = new THREE.CatmullRomCurve3( [
* new THREE.Vector3( -10, 0, 10 ),
* new THREE.Vector3( -5, 5, 5 ),
* new THREE.Vector3( 0, 0, 0 ),
* new THREE.Vector3( 5, -5, 5 ),
* new THREE.Vector3( 10, 0, 10 )
* ] );
*
* const points = curve.getPoints( 50 );
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
*
* const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
*
* // Create the final object to add to the scene
* const curveObject = new THREE.Line( geometry, material );
* ```
*
* @augments Curve
*/
class CatmullRomCurve3 extends Curve {
/**
* Constructs a new Catmull-Rom curve.
*
* @param {Array<Vector3>} [points] - An array of 3D points defining the curve.
* @param {boolean} [closed=false] - Whether the curve is closed or not.
* @param {('centripetal'|'chordal'|'catmullrom')} [curveType='centripetal'] - The curve type.
* @param {number} [tension=0.5] - Tension of the curve.
*/
constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isCatmullRomCurve3 = true;
this.type = 'CatmullRomCurve3';
/**
* An array of 3D points defining the curve.
*
* @type {Array<Vector3>}
*/
this.points = points;
/**
* Whether the curve is closed or not.
*
* @type {boolean}
* @default false
*/
this.closed = closed;
/**
* The curve type.
*
* @type {('centripetal'|'chordal'|'catmullrom')}
* @default 'centripetal'
*/
this.curveType = curveType;
/**
* Tension of the curve.
*
* @type {number}
* @default 0.5
*/
this.tension = tension;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector3} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector3} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector3() ) {
const point = optionalTarget;
const points = this.points;
const l = points.length;
const p = ( l - ( this.closed ? 0 : 1 ) ) * t;
let intPoint = Math.floor( p );
let weight = p - intPoint;
if ( this.closed ) {
intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l;
} else if ( weight === 0 && intPoint === l - 1 ) {
intPoint = l - 2;
weight = 1;
}
let p0, p3; // 4 points (p1 & p2 defined below)
if ( this.closed || intPoint > 0 ) {
p0 = points[ ( intPoint - 1 ) % l ];
} else {
// extrapolate first point
tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
p0 = tmp;
}
const p1 = points[ intPoint % l ];
const p2 = points[ ( intPoint + 1 ) % l ];
if ( this.closed || intPoint + 2 < l ) {
p3 = points[ ( intPoint + 2 ) % l ];
} else {
// extrapolate last point
tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );
p3 = tmp;
}
if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
// init Centripetal / Chordal Catmull-Rom
const pow = this.curveType === 'chordal' ? 0.5 : 0.25;
let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
// safety check for repeated points
if ( dt1 < 1e-4 ) dt1 = 1.0;
if ( dt0 < 1e-4 ) dt0 = dt1;
if ( dt2 < 1e-4 ) dt2 = dt1;
px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
} else if ( this.curveType === 'catmullrom' ) {
px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );
py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );
pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );
}
point.set(
px.calc( weight ),
py.calc( weight ),
pz.calc( weight )
);
return point;
}
copy( source ) {
super.copy( source );
this.points = [];
for ( let i = 0, l = source.points.length; i < l; i ++ ) {
const point = source.points[ i ];
this.points.push( point.clone() );
}
this.closed = source.closed;
this.curveType = source.curveType;
this.tension = source.tension;
return this;
}
toJSON() {
const data = super.toJSON();
data.points = [];
for ( let i = 0, l = this.points.length; i < l; i ++ ) {
const point = this.points[ i ];
data.points.push( point.toArray() );
}
data.closed = this.closed;
data.curveType = this.curveType;
data.tension = this.tension;
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.points = [];
for ( let i = 0, l = json.points.length; i < l; i ++ ) {
const point = json.points[ i ];
this.points.push( new Vector3().fromArray( point ) );
}
this.closed = json.closed;
this.curveType = json.curveType;
this.tension = json.tension;
return this;
}
}
export { CatmullRomCurve3 };

View File

@@ -0,0 +1,145 @@
import { Curve } from '../core/Curve.js';
import { CubicBezier } from '../core/Interpolations.js';
import { Vector2 } from '../../math/Vector2.js';
/**
* A curve representing a 2D Cubic Bezier curve.
*
* ```js
* const curve = new THREE.CubicBezierCurve(
* new THREE.Vector2( - 0, 0 ),
* new THREE.Vector2( - 5, 15 ),
* new THREE.Vector2( 20, 15 ),
* new THREE.Vector2( 10, 0 )
* );
*
* const points = curve.getPoints( 50 );
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
*
* const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
*
* // Create the final object to add to the scene
* const curveObject = new THREE.Line( geometry, material );
* ```
*
* @augments Curve
*/
class CubicBezierCurve extends Curve {
/**
* Constructs a new Cubic Bezier curve.
*
* @param {Vector2} [v0] - The start point.
* @param {Vector2} [v1] - The first control point.
* @param {Vector2} [v2] - The second control point.
* @param {Vector2} [v3] - The end point.
*/
constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isCubicBezierCurve = true;
this.type = 'CubicBezierCurve';
/**
* The start point.
*
* @type {Vector2}
*/
this.v0 = v0;
/**
* The first control point.
*
* @type {Vector2}
*/
this.v1 = v1;
/**
* The second control point.
*
* @type {Vector2}
*/
this.v2 = v2;
/**
* The end point.
*
* @type {Vector2}
*/
this.v3 = v3;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector2} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector2} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector2() ) {
const point = optionalTarget;
const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;
point.set(
CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),
CubicBezier( t, v0.y, v1.y, v2.y, v3.y )
);
return point;
}
copy( source ) {
super.copy( source );
this.v0.copy( source.v0 );
this.v1.copy( source.v1 );
this.v2.copy( source.v2 );
this.v3.copy( source.v3 );
return this;
}
toJSON() {
const data = super.toJSON();
data.v0 = this.v0.toArray();
data.v1 = this.v1.toArray();
data.v2 = this.v2.toArray();
data.v3 = this.v3.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.v0.fromArray( json.v0 );
this.v1.fromArray( json.v1 );
this.v2.fromArray( json.v2 );
this.v3.fromArray( json.v3 );
return this;
}
}
export { CubicBezierCurve };

View File

@@ -0,0 +1,129 @@
import { Curve } from '../core/Curve.js';
import { CubicBezier } from '../core/Interpolations.js';
import { Vector3 } from '../../math/Vector3.js';
/**
* A curve representing a 3D Cubic Bezier curve.
*
* @augments Curve
*/
class CubicBezierCurve3 extends Curve {
/**
* Constructs a new Cubic Bezier curve.
*
* @param {Vector3} [v0] - The start point.
* @param {Vector3} [v1] - The first control point.
* @param {Vector3} [v2] - The second control point.
* @param {Vector3} [v3] - The end point.
*/
constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isCubicBezierCurve3 = true;
this.type = 'CubicBezierCurve3';
/**
* The start point.
*
* @type {Vector3}
*/
this.v0 = v0;
/**
* The first control point.
*
* @type {Vector3}
*/
this.v1 = v1;
/**
* The second control point.
*
* @type {Vector3}
*/
this.v2 = v2;
/**
* The end point.
*
* @type {Vector3}
*/
this.v3 = v3;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector3} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector3} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector3() ) {
const point = optionalTarget;
const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;
point.set(
CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),
CubicBezier( t, v0.y, v1.y, v2.y, v3.y ),
CubicBezier( t, v0.z, v1.z, v2.z, v3.z )
);
return point;
}
copy( source ) {
super.copy( source );
this.v0.copy( source.v0 );
this.v1.copy( source.v1 );
this.v2.copy( source.v2 );
this.v3.copy( source.v3 );
return this;
}
toJSON() {
const data = super.toJSON();
data.v0 = this.v0.toArray();
data.v1 = this.v1.toArray();
data.v2 = this.v2.toArray();
data.v3 = this.v3.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.v0.fromArray( json.v0 );
this.v1.fromArray( json.v1 );
this.v2.fromArray( json.v2 );
this.v3.fromArray( json.v3 );
return this;
}
}
export { CubicBezierCurve3 };

10
node_modules/three/src/extras/curves/Curves.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
export { ArcCurve } from './ArcCurve.js';
export { CatmullRomCurve3 } from './CatmullRomCurve3.js';
export { CubicBezierCurve } from './CubicBezierCurve.js';
export { CubicBezierCurve3 } from './CubicBezierCurve3.js';
export { EllipseCurve } from './EllipseCurve.js';
export { LineCurve } from './LineCurve.js';
export { LineCurve3 } from './LineCurve3.js';
export { QuadraticBezierCurve } from './QuadraticBezierCurve.js';
export { QuadraticBezierCurve3 } from './QuadraticBezierCurve3.js';
export { SplineCurve } from './SplineCurve.js';

258
node_modules/three/src/extras/curves/EllipseCurve.js generated vendored Normal file
View File

@@ -0,0 +1,258 @@
import { Curve } from '../core/Curve.js';
import { Vector2 } from '../../math/Vector2.js';
/**
* A curve representing an ellipse.
*
* ```js
* const curve = new THREE.EllipseCurve(
* 0, 0,
* 10, 10,
* 0, 2 * Math.PI,
* false,
* 0
* );
*
* const points = curve.getPoints( 50 );
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
*
* const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
*
* // Create the final object to add to the scene
* const ellipse = new THREE.Line( geometry, material );
* ```
*
* @augments Curve
*/
class EllipseCurve extends Curve {
/**
* Constructs a new ellipse curve.
*
* @param {number} [aX=0] - The X center of the ellipse.
* @param {number} [aY=0] - The Y center of the ellipse.
* @param {number} [xRadius=1] - The radius of the ellipse in the x direction.
* @param {number} [yRadius=1] - The radius of the ellipse in the y direction.
* @param {number} [aStartAngle=0] - The start angle of the curve in radians starting from the positive X axis.
* @param {number} [aEndAngle=Math.PI*2] - The end angle of the curve in radians starting from the positive X axis.
* @param {boolean} [aClockwise=false] - Whether the ellipse is drawn clockwise or not.
* @param {number} [aRotation=0] - The rotation angle of the ellipse in radians, counterclockwise from the positive X axis.
*/
constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isEllipseCurve = true;
this.type = 'EllipseCurve';
/**
* The X center of the ellipse.
*
* @type {number}
* @default 0
*/
this.aX = aX;
/**
* The Y center of the ellipse.
*
* @type {number}
* @default 0
*/
this.aY = aY;
/**
* The radius of the ellipse in the x direction.
* Setting the this value equal to the {@link EllipseCurve#yRadius} will result in a circle.
*
* @type {number}
* @default 1
*/
this.xRadius = xRadius;
/**
* The radius of the ellipse in the y direction.
* Setting the this value equal to the {@link EllipseCurve#xRadius} will result in a circle.
*
* @type {number}
* @default 1
*/
this.yRadius = yRadius;
/**
* The start angle of the curve in radians starting from the positive X axis.
*
* @type {number}
* @default 0
*/
this.aStartAngle = aStartAngle;
/**
* The end angle of the curve in radians starting from the positive X axis.
*
* @type {number}
* @default Math.PI*2
*/
this.aEndAngle = aEndAngle;
/**
* Whether the ellipse is drawn clockwise or not.
*
* @type {boolean}
* @default false
*/
this.aClockwise = aClockwise;
/**
* The rotation angle of the ellipse in radians, counterclockwise from the positive X axis.
*
* @type {number}
* @default 0
*/
this.aRotation = aRotation;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector2} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector2} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector2() ) {
const point = optionalTarget;
const twoPi = Math.PI * 2;
let deltaAngle = this.aEndAngle - this.aStartAngle;
const samePoints = Math.abs( deltaAngle ) < Number.EPSILON;
// ensures that deltaAngle is 0 .. 2 PI
while ( deltaAngle < 0 ) deltaAngle += twoPi;
while ( deltaAngle > twoPi ) deltaAngle -= twoPi;
if ( deltaAngle < Number.EPSILON ) {
if ( samePoints ) {
deltaAngle = 0;
} else {
deltaAngle = twoPi;
}
}
if ( this.aClockwise === true && ! samePoints ) {
if ( deltaAngle === twoPi ) {
deltaAngle = - twoPi;
} else {
deltaAngle = deltaAngle - twoPi;
}
}
const angle = this.aStartAngle + t * deltaAngle;
let x = this.aX + this.xRadius * Math.cos( angle );
let y = this.aY + this.yRadius * Math.sin( angle );
if ( this.aRotation !== 0 ) {
const cos = Math.cos( this.aRotation );
const sin = Math.sin( this.aRotation );
const tx = x - this.aX;
const ty = y - this.aY;
// Rotate the point about the center of the ellipse.
x = tx * cos - ty * sin + this.aX;
y = tx * sin + ty * cos + this.aY;
}
return point.set( x, y );
}
copy( source ) {
super.copy( source );
this.aX = source.aX;
this.aY = source.aY;
this.xRadius = source.xRadius;
this.yRadius = source.yRadius;
this.aStartAngle = source.aStartAngle;
this.aEndAngle = source.aEndAngle;
this.aClockwise = source.aClockwise;
this.aRotation = source.aRotation;
return this;
}
toJSON() {
const data = super.toJSON();
data.aX = this.aX;
data.aY = this.aY;
data.xRadius = this.xRadius;
data.yRadius = this.yRadius;
data.aStartAngle = this.aStartAngle;
data.aEndAngle = this.aEndAngle;
data.aClockwise = this.aClockwise;
data.aRotation = this.aRotation;
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.aX = json.aX;
this.aY = json.aY;
this.xRadius = json.xRadius;
this.yRadius = json.yRadius;
this.aStartAngle = json.aStartAngle;
this.aEndAngle = json.aEndAngle;
this.aClockwise = json.aClockwise;
this.aRotation = json.aRotation;
return this;
}
}
export { EllipseCurve };

128
node_modules/three/src/extras/curves/LineCurve.js generated vendored Normal file
View File

@@ -0,0 +1,128 @@
import { Vector2 } from '../../math/Vector2.js';
import { Curve } from '../core/Curve.js';
/**
* A curve representing a 2D line segment.
*
* @augments Curve
*/
class LineCurve extends Curve {
/**
* Constructs a new line curve.
*
* @param {Vector2} [v1] - The start point.
* @param {Vector2} [v2] - The end point.
*/
constructor( v1 = new Vector2(), v2 = new Vector2() ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isLineCurve = true;
this.type = 'LineCurve';
/**
* The start point.
*
* @type {Vector2}
*/
this.v1 = v1;
/**
* The end point.
*
* @type {Vector2}
*/
this.v2 = v2;
}
/**
* Returns a point on the line.
*
* @param {number} t - A interpolation factor representing a position on the line. Must be in the range `[0,1]`.
* @param {Vector2} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector2} The position on the line.
*/
getPoint( t, optionalTarget = new Vector2() ) {
const point = optionalTarget;
if ( t === 1 ) {
point.copy( this.v2 );
} else {
point.copy( this.v2 ).sub( this.v1 );
point.multiplyScalar( t ).add( this.v1 );
}
return point;
}
// Line curve is linear, so we can overwrite default getPointAt
getPointAt( u, optionalTarget ) {
return this.getPoint( u, optionalTarget );
}
getTangent( t, optionalTarget = new Vector2() ) {
return optionalTarget.subVectors( this.v2, this.v1 ).normalize();
}
getTangentAt( u, optionalTarget ) {
return this.getTangent( u, optionalTarget );
}
copy( source ) {
super.copy( source );
this.v1.copy( source.v1 );
this.v2.copy( source.v2 );
return this;
}
toJSON() {
const data = super.toJSON();
data.v1 = this.v1.toArray();
data.v2 = this.v2.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.v1.fromArray( json.v1 );
this.v2.fromArray( json.v2 );
return this;
}
}
export { LineCurve };

128
node_modules/three/src/extras/curves/LineCurve3.js generated vendored Normal file
View File

@@ -0,0 +1,128 @@
import { Vector3 } from '../../math/Vector3.js';
import { Curve } from '../core/Curve.js';
/**
* A curve representing a 3D line segment.
*
* @augments Curve
*/
class LineCurve3 extends Curve {
/**
* Constructs a new line curve.
*
* @param {Vector3} [v1] - The start point.
* @param {Vector3} [v2] - The end point.
*/
constructor( v1 = new Vector3(), v2 = new Vector3() ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isLineCurve3 = true;
this.type = 'LineCurve3';
/**
* The start point.
*
* @type {Vector3}
*/
this.v1 = v1;
/**
* The end point.
*
* @type {Vector2}
*/
this.v2 = v2;
}
/**
* Returns a point on the line.
*
* @param {number} t - A interpolation factor representing a position on the line. Must be in the range `[0,1]`.
* @param {Vector3} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector3} The position on the line.
*/
getPoint( t, optionalTarget = new Vector3() ) {
const point = optionalTarget;
if ( t === 1 ) {
point.copy( this.v2 );
} else {
point.copy( this.v2 ).sub( this.v1 );
point.multiplyScalar( t ).add( this.v1 );
}
return point;
}
// Line curve is linear, so we can overwrite default getPointAt
getPointAt( u, optionalTarget ) {
return this.getPoint( u, optionalTarget );
}
getTangent( t, optionalTarget = new Vector3() ) {
return optionalTarget.subVectors( this.v2, this.v1 ).normalize();
}
getTangentAt( u, optionalTarget ) {
return this.getTangent( u, optionalTarget );
}
copy( source ) {
super.copy( source );
this.v1.copy( source.v1 );
this.v2.copy( source.v2 );
return this;
}
toJSON() {
const data = super.toJSON();
data.v1 = this.v1.toArray();
data.v2 = this.v2.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.v1.fromArray( json.v1 );
this.v2.fromArray( json.v2 );
return this;
}
}
export { LineCurve3 };

View File

@@ -0,0 +1,133 @@
import { Curve } from '../core/Curve.js';
import { QuadraticBezier } from '../core/Interpolations.js';
import { Vector2 } from '../../math/Vector2.js';
/**
* A curve representing a 2D Quadratic Bezier curve.
*
* ```js
* const curve = new THREE.QuadraticBezierCurve(
* new THREE.Vector2( - 10, 0 ),
* new THREE.Vector2( 20, 15 ),
* new THREE.Vector2( 10, 0 )
* )
*
* const points = curve.getPoints( 50 );
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
*
* const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
*
* // Create the final object to add to the scene
* const curveObject = new THREE.Line( geometry, material );
* ```
*
* @augments Curve
*/
class QuadraticBezierCurve extends Curve {
/**
* Constructs a new Quadratic Bezier curve.
*
* @param {Vector2} [v0] - The start point.
* @param {Vector2} [v1] - The control point.
* @param {Vector2} [v2] - The end point.
*/
constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isQuadraticBezierCurve = true;
this.type = 'QuadraticBezierCurve';
/**
* The start point.
*
* @type {Vector2}
*/
this.v0 = v0;
/**
* The control point.
*
* @type {Vector2}
*/
this.v1 = v1;
/**
* The end point.
*
* @type {Vector2}
*/
this.v2 = v2;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector2} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector2} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector2() ) {
const point = optionalTarget;
const v0 = this.v0, v1 = this.v1, v2 = this.v2;
point.set(
QuadraticBezier( t, v0.x, v1.x, v2.x ),
QuadraticBezier( t, v0.y, v1.y, v2.y )
);
return point;
}
copy( source ) {
super.copy( source );
this.v0.copy( source.v0 );
this.v1.copy( source.v1 );
this.v2.copy( source.v2 );
return this;
}
toJSON() {
const data = super.toJSON();
data.v0 = this.v0.toArray();
data.v1 = this.v1.toArray();
data.v2 = this.v2.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.v0.fromArray( json.v0 );
this.v1.fromArray( json.v1 );
this.v2.fromArray( json.v2 );
return this;
}
}
export { QuadraticBezierCurve };

View File

@@ -0,0 +1,118 @@
import { Curve } from '../core/Curve.js';
import { QuadraticBezier } from '../core/Interpolations.js';
import { Vector3 } from '../../math/Vector3.js';
/**
* A curve representing a 3D Quadratic Bezier curve.
*
* @augments Curve
*/
class QuadraticBezierCurve3 extends Curve {
/**
* Constructs a new Quadratic Bezier curve.
*
* @param {Vector3} [v0] - The start point.
* @param {Vector3} [v1] - The control point.
* @param {Vector3} [v2] - The end point.
*/
constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isQuadraticBezierCurve3 = true;
this.type = 'QuadraticBezierCurve3';
/**
* The start point.
*
* @type {Vector3}
*/
this.v0 = v0;
/**
* The control point.
*
* @type {Vector3}
*/
this.v1 = v1;
/**
* The end point.
*
* @type {Vector3}
*/
this.v2 = v2;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector3} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector3} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector3() ) {
const point = optionalTarget;
const v0 = this.v0, v1 = this.v1, v2 = this.v2;
point.set(
QuadraticBezier( t, v0.x, v1.x, v2.x ),
QuadraticBezier( t, v0.y, v1.y, v2.y ),
QuadraticBezier( t, v0.z, v1.z, v2.z )
);
return point;
}
copy( source ) {
super.copy( source );
this.v0.copy( source.v0 );
this.v1.copy( source.v1 );
this.v2.copy( source.v2 );
return this;
}
toJSON() {
const data = super.toJSON();
data.v0 = this.v0.toArray();
data.v1 = this.v1.toArray();
data.v2 = this.v2.toArray();
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.v0.fromArray( json.v0 );
this.v1.fromArray( json.v1 );
this.v2.fromArray( json.v2 );
return this;
}
}
export { QuadraticBezierCurve3 };

145
node_modules/three/src/extras/curves/SplineCurve.js generated vendored Normal file
View File

@@ -0,0 +1,145 @@
import { Curve } from '../core/Curve.js';
import { CatmullRom } from '../core/Interpolations.js';
import { Vector2 } from '../../math/Vector2.js';
/**
* A curve representing a 2D spline curve.
*
* ```js
* // Create a sine-like wave
* const curve = new THREE.SplineCurve( [
* new THREE.Vector2( -10, 0 ),
* new THREE.Vector2( -5, 5 ),
* new THREE.Vector2( 0, 0 ),
* new THREE.Vector2( 5, -5 ),
* new THREE.Vector2( 10, 0 )
* ] );
*
* const points = curve.getPoints( 50 );
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
*
* const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
*
* // Create the final object to add to the scene
* const splineObject = new THREE.Line( geometry, material );
* ```
*
* @augments Curve
*/
class SplineCurve extends Curve {
/**
* Constructs a new 2D spline curve.
*
* @param {Array<Vector2>} [points] - An array of 2D points defining the curve.
*/
constructor( points = [] ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isSplineCurve = true;
this.type = 'SplineCurve';
/**
* An array of 2D points defining the curve.
*
* @type {Array<Vector2>}
*/
this.points = points;
}
/**
* Returns a point on the curve.
*
* @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`.
* @param {Vector2} [optionalTarget] - The optional target vector the result is written to.
* @return {Vector2} The position on the curve.
*/
getPoint( t, optionalTarget = new Vector2() ) {
const point = optionalTarget;
const points = this.points;
const p = ( points.length - 1 ) * t;
const intPoint = Math.floor( p );
const weight = p - intPoint;
const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
const p1 = points[ intPoint ];
const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
point.set(
CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ),
CatmullRom( weight, p0.y, p1.y, p2.y, p3.y )
);
return point;
}
copy( source ) {
super.copy( source );
this.points = [];
for ( let i = 0, l = source.points.length; i < l; i ++ ) {
const point = source.points[ i ];
this.points.push( point.clone() );
}
return this;
}
toJSON() {
const data = super.toJSON();
data.points = [];
for ( let i = 0, l = this.points.length; i < l; i ++ ) {
const point = this.points[ i ];
data.points.push( point.toArray() );
}
return data;
}
fromJSON( json ) {
super.fromJSON( json );
this.points = [];
for ( let i = 0, l = json.points.length; i < l; i ++ ) {
const point = json.points[ i ];
this.points.push( new Vector2().fromArray( point ) );
}
return this;
}
}
export { SplineCurve };

685
node_modules/three/src/extras/lib/earcut.js generated vendored Normal file
View File

@@ -0,0 +1,685 @@
/* eslint-disable */
// copy of mapbox/earcut version 3.0.2
// https://github.com/mapbox/earcut/tree/v3.0.2
export default function earcut(data, holeIndices, dim = 2) {
const hasHoles = holeIndices && holeIndices.length;
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
let outerNode = linkedList(data, 0, outerLen, dim, true);
const triangles = [];
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
let minX, minY, invSize;
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
if (data.length > 80 * dim) {
minX = data[0];
minY = data[1];
let maxX = minX;
let maxY = minY;
for (let i = dim; i < outerLen; i += dim) {
const x = data[i];
const y = data[i + 1];
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
}
// minX, minY and invSize are later used to transform coords into integers for z-order calculation
invSize = Math.max(maxX - minX, maxY - minY);
invSize = invSize !== 0 ? 32767 / invSize : 0;
}
earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
return triangles;
}
// create a circular doubly linked list from polygon points in the specified winding order
function linkedList(data, start, end, dim, clockwise) {
let last;
if (clockwise === (signedArea(data, start, end, dim) > 0)) {
for (let i = start; i < end; i += dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
} else {
for (let i = end - dim; i >= start; i -= dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
}
if (last && equals(last, last.next)) {
removeNode(last);
last = last.next;
}
return last;
}
// eliminate colinear or duplicate points
function filterPoints(start, end) {
if (!start) return start;
if (!end) end = start;
let p = start,
again;
do {
again = false;
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
removeNode(p);
p = end = p.prev;
if (p === p.next) break;
again = true;
} else {
p = p.next;
}
} while (again || p !== end);
return end;
}
// main ear slicing loop which triangulates a polygon (given as a linked list)
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
if (!ear) return;
// interlink polygon nodes in z-order
if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
let stop = ear;
// iterate through ears, slicing them one by one
while (ear.prev !== ear.next) {
const prev = ear.prev;
const next = ear.next;
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
triangles.push(prev.i, ear.i, next.i); // cut off the triangle
removeNode(ear);
// skipping the next vertex leads to less sliver triangles
ear = next.next;
stop = next.next;
continue;
}
ear = next;
// if we looped through the whole remaining polygon and can't find any more ears
if (ear === stop) {
// try filtering points and slicing again
if (!pass) {
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
// if this didn't work, try curing all small self-intersections locally
} else if (pass === 1) {
ear = cureLocalIntersections(filterPoints(ear), triangles);
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
// as a last resort, try splitting the remaining polygon into two
} else if (pass === 2) {
splitEarcut(ear, triangles, dim, minX, minY, invSize);
}
break;
}
}
}
// check whether a polygon node forms a valid ear with adjacent nodes
function isEar(ear) {
const a = ear.prev,
b = ear,
c = ear.next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// now make sure we don't have other points inside the potential ear
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
// triangle bbox
const x0 = Math.min(ax, bx, cx),
y0 = Math.min(ay, by, cy),
x1 = Math.max(ax, bx, cx),
y1 = Math.max(ay, by, cy);
let p = c.next;
while (p !== a) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) &&
area(p.prev, p, p.next) >= 0) return false;
p = p.next;
}
return true;
}
function isEarHashed(ear, minX, minY, invSize) {
const a = ear.prev,
b = ear,
c = ear.next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
// triangle bbox
const x0 = Math.min(ax, bx, cx),
y0 = Math.min(ay, by, cy),
x1 = Math.max(ax, bx, cx),
y1 = Math.max(ay, by, cy);
// z-order range for the current triangle bbox;
const minZ = zOrder(x0, y0, minX, minY, invSize),
maxZ = zOrder(x1, y1, minX, minY, invSize);
let p = ear.prevZ,
n = ear.nextZ;
// look for points inside the triangle in both directions
while (p && p.z >= minZ && n && n.z <= maxZ) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
p = p.prevZ;
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
}
// look for remaining points in decreasing z-order
while (p && p.z >= minZ) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
p = p.prevZ;
}
// look for remaining points in increasing z-order
while (n && n.z <= maxZ) {
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
}
return true;
}
// go through all polygon nodes and cure small local self-intersections
function cureLocalIntersections(start, triangles) {
let p = start;
do {
const a = p.prev,
b = p.next.next;
if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
triangles.push(a.i, p.i, b.i);
// remove two nodes involved
removeNode(p);
removeNode(p.next);
p = start = b;
}
p = p.next;
} while (p !== start);
return filterPoints(p);
}
// try splitting polygon into two and triangulate them independently
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
// look for a valid diagonal that divides the polygon into two
let a = start;
do {
let b = a.next.next;
while (b !== a.prev) {
if (a.i !== b.i && isValidDiagonal(a, b)) {
// split the polygon in two by the diagonal
let c = splitPolygon(a, b);
// filter colinear points around the cuts
a = filterPoints(a, a.next);
c = filterPoints(c, c.next);
// run earcut on each half
earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
return;
}
b = b.next;
}
a = a.next;
} while (a !== start);
}
// link every hole into the outer loop, producing a single-ring polygon without holes
function eliminateHoles(data, holeIndices, outerNode, dim) {
const queue = [];
for (let i = 0, len = holeIndices.length; i < len; i++) {
const start = holeIndices[i] * dim;
const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
const list = linkedList(data, start, end, dim, false);
if (list === list.next) list.steiner = true;
queue.push(getLeftmost(list));
}
queue.sort(compareXYSlope);
// process holes from left to right
for (let i = 0; i < queue.length; i++) {
outerNode = eliminateHole(queue[i], outerNode);
}
return outerNode;
}
function compareXYSlope(a, b) {
let result = a.x - b.x;
// when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find
// the bridge to the outer shell is always the point that they meet at.
if (result === 0) {
result = a.y - b.y;
if (result === 0) {
const aSlope = (a.next.y - a.y) / (a.next.x - a.x);
const bSlope = (b.next.y - b.y) / (b.next.x - b.x);
result = aSlope - bSlope;
}
}
return result;
}
// find a bridge between vertices that connects hole with an outer ring and link it
function eliminateHole(hole, outerNode) {
const bridge = findHoleBridge(hole, outerNode);
if (!bridge) {
return outerNode;
}
const bridgeReverse = splitPolygon(bridge, hole);
// filter collinear points around the cuts
filterPoints(bridgeReverse, bridgeReverse.next);
return filterPoints(bridge, bridge.next);
}
// David Eberly's algorithm for finding a bridge between hole and outer polygon
function findHoleBridge(hole, outerNode) {
let p = outerNode;
const hx = hole.x;
const hy = hole.y;
let qx = -Infinity;
let m;
// find a segment intersected by a ray from the hole's leftmost point to the left;
// segment's endpoint with lesser x will be potential connection point
// unless they intersect at a vertex, then choose the vertex
if (equals(hole, p)) return p;
do {
if (equals(hole, p.next)) return p.next;
else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
if (x <= hx && x > qx) {
qx = x;
m = p.x < p.next.x ? p : p.next;
if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint
}
}
p = p.next;
} while (p !== outerNode);
if (!m) return null;
// look for points inside the triangle of hole point, segment intersection and endpoint;
// if there are no points found, we have a valid connection;
// otherwise choose the point of the minimum angle with the ray as connection point
const stop = m;
const mx = m.x;
const my = m.y;
let tanMin = Infinity;
p = m;
do {
if (hx >= p.x && p.x >= mx && hx !== p.x &&
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
if (locallyInside(p, hole) &&
(tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
m = p;
tanMin = tan;
}
}
p = p.next;
} while (p !== stop);
return m;
}
// whether sector in vertex m contains sector in vertex p in the same coordinates
function sectorContainsSector(m, p) {
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
}
// interlink polygon nodes in z-order
function indexCurve(start, minX, minY, invSize) {
let p = start;
do {
if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
p.prevZ = p.prev;
p.nextZ = p.next;
p = p.next;
} while (p !== start);
p.prevZ.nextZ = null;
p.prevZ = null;
sortLinked(p);
}
// Simon Tatham's linked list merge sort algorithm
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
function sortLinked(list) {
let numMerges;
let inSize = 1;
do {
let p = list;
let e;
list = null;
let tail = null;
numMerges = 0;
while (p) {
numMerges++;
let q = p;
let pSize = 0;
for (let i = 0; i < inSize; i++) {
pSize++;
q = q.nextZ;
if (!q) break;
}
let qSize = inSize;
while (pSize > 0 || (qSize > 0 && q)) {
if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
e = p;
p = p.nextZ;
pSize--;
} else {
e = q;
q = q.nextZ;
qSize--;
}
if (tail) tail.nextZ = e;
else list = e;
e.prevZ = tail;
tail = e;
}
p = q;
}
tail.nextZ = null;
inSize *= 2;
} while (numMerges > 1);
return list;
}
// z-order of a point given coords and inverse of the longer side of data bbox
function zOrder(x, y, minX, minY, invSize) {
// coords are transformed into non-negative 15-bit integer range
x = (x - minX) * invSize | 0;
y = (y - minY) * invSize | 0;
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
}
// find the leftmost node of a polygon ring
function getLeftmost(start) {
let p = start,
leftmost = start;
do {
if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p;
p = p.next;
} while (p !== start);
return leftmost;
}
// check if a point lies within a convex triangle
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
(ax - px) * (by - py) >= (bx - px) * (ay - py) &&
(bx - px) * (cy - py) >= (cx - px) * (by - py);
}
// check if a point lies within a convex triangle but false if its equal to the first point of the triangle
function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) {
return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py);
}
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
function isValidDiagonal(a, b) {
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // doesn't intersect other edges
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
}
// signed area of a triangle
function area(p, q, r) {
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
}
// check if two points are equal
function equals(p1, p2) {
return p1.x === p2.x && p1.y === p2.y;
}
// check if two segments intersect
function intersects(p1, q1, p2, q2) {
const o1 = sign(area(p1, q1, p2));
const o2 = sign(area(p1, q1, q2));
const o3 = sign(area(p2, q2, p1));
const o4 = sign(area(p2, q2, q1));
if (o1 !== o2 && o3 !== o4) return true; // general case
if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
return false;
}
// for collinear points p, q, r, check if point q lies on segment pr
function onSegment(p, q, r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
}
function sign(num) {
return num > 0 ? 1 : num < 0 ? -1 : 0;
}
// check if a polygon diagonal intersects any polygon segments
function intersectsPolygon(a, b) {
let p = a;
do {
if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
intersects(p, p.next, a, b)) return true;
p = p.next;
} while (p !== a);
return false;
}
// check if a polygon diagonal is locally inside the polygon
function locallyInside(a, b) {
return area(a.prev, a, a.next) < 0 ?
area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
}
// check if the middle point of a polygon diagonal is inside the polygon
function middleInside(a, b) {
let p = a;
let inside = false;
const px = (a.x + b.x) / 2;
const py = (a.y + b.y) / 2;
do {
if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
(px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
inside = !inside;
p = p.next;
} while (p !== a);
return inside;
}
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
// if one belongs to the outer ring and another to a hole, it merges it into a single ring
function splitPolygon(a, b) {
const a2 = createNode(a.i, a.x, a.y),
b2 = createNode(b.i, b.x, b.y),
an = a.next,
bp = b.prev;
a.next = b;
b.prev = a;
a2.next = an;
an.prev = a2;
b2.next = a2;
a2.prev = b2;
bp.next = b2;
b2.prev = bp;
return b2;
}
// create a node and optionally link it with previous one (in a circular doubly linked list)
function insertNode(i, x, y, last) {
const p = createNode(i, x, y);
if (!last) {
p.prev = p;
p.next = p;
} else {
p.next = last.next;
p.prev = last;
last.next.prev = p;
last.next = p;
}
return p;
}
function removeNode(p) {
p.next.prev = p.prev;
p.prev.next = p.next;
if (p.prevZ) p.prevZ.nextZ = p.nextZ;
if (p.nextZ) p.nextZ.prevZ = p.prevZ;
}
function createNode(i, x, y) {
return {
i, // vertex index in coordinates array
x, y, // vertex coordinates
prev: null, // previous and next vertex nodes in a polygon ring
next: null,
z: 0, // z-order curve value
prevZ: null, // previous and next nodes in z-order
nextZ: null,
steiner: false // indicates whether this is a steiner point
};
}
// return a percentage difference between the polygon area and its triangulation area;
// used to verify correctness of triangulation
export function deviation(data, holeIndices, dim, triangles) {
const hasHoles = holeIndices && holeIndices.length;
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
let polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
if (hasHoles) {
for (let i = 0, len = holeIndices.length; i < len; i++) {
const start = holeIndices[i] * dim;
const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
polygonArea -= Math.abs(signedArea(data, start, end, dim));
}
}
let trianglesArea = 0;
for (let i = 0; i < triangles.length; i += 3) {
const a = triangles[i] * dim;
const b = triangles[i + 1] * dim;
const c = triangles[i + 2] * dim;
trianglesArea += Math.abs(
(data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
(data[a] - data[b]) * (data[c + 1] - data[a + 1]));
}
return polygonArea === 0 && trianglesArea === 0 ? 0 :
Math.abs((trianglesArea - polygonArea) / polygonArea);
}
function signedArea(data, start, end, dim) {
let sum = 0;
for (let i = start, j = end - dim; i < end; i += dim) {
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
j = i;
}
return sum;
}
// turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
export function flatten(data) {
const vertices = [];
const holes = [];
const dimensions = data[0][0].length;
let holeIndex = 0;
let prevLen = 0;
for (const ring of data) {
for (const p of ring) {
for (let d = 0; d < dimensions; d++) vertices.push(p[d]);
}
if (prevLen) {
holeIndex += prevLen;
holes.push(holeIndex);
}
prevLen = ring.length;
}
return {vertices, holes, dimensions};
}