Anlegen vom Raum-Scanner
This commit is contained in:
120
node_modules/three/src/extras/Controls.js
generated
vendored
Normal file
120
node_modules/three/src/extras/Controls.js
generated
vendored
Normal 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
217
node_modules/three/src/extras/DataUtils.js
generated
vendored
Normal 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
28
node_modules/three/src/extras/Earcut.js
generated
vendored
Normal 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
137
node_modules/three/src/extras/ImageUtils.js
generated
vendored
Normal 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
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
114
node_modules/three/src/extras/ShapeUtils.js
generated
vendored
Normal 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
297
node_modules/three/src/extras/TextureUtils.js
generated
vendored
Normal 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
517
node_modules/three/src/extras/core/Curve.js
generated
vendored
Normal 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
296
node_modules/three/src/extras/core/CurvePath.js
generated
vendored
Normal 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
111
node_modules/three/src/extras/core/Interpolations.js
generated
vendored
Normal 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
329
node_modules/three/src/extras/core/Path.js
generated
vendored
Normal 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
165
node_modules/three/src/extras/core/Shape.js
generated
vendored
Normal 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
367
node_modules/three/src/extras/core/ShapePath.js
generated
vendored
Normal 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
39
node_modules/three/src/extras/curves/ArcCurve.js
generated
vendored
Normal 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 };
|
||||
326
node_modules/three/src/extras/curves/CatmullRomCurve3.js
generated
vendored
Normal file
326
node_modules/three/src/extras/curves/CatmullRomCurve3.js
generated
vendored
Normal 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 };
|
||||
145
node_modules/three/src/extras/curves/CubicBezierCurve.js
generated
vendored
Normal file
145
node_modules/three/src/extras/curves/CubicBezierCurve.js
generated
vendored
Normal 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 };
|
||||
129
node_modules/three/src/extras/curves/CubicBezierCurve3.js
generated
vendored
Normal file
129
node_modules/three/src/extras/curves/CubicBezierCurve3.js
generated
vendored
Normal 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
10
node_modules/three/src/extras/curves/Curves.js
generated
vendored
Normal 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
258
node_modules/three/src/extras/curves/EllipseCurve.js
generated
vendored
Normal 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
128
node_modules/three/src/extras/curves/LineCurve.js
generated
vendored
Normal 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
128
node_modules/three/src/extras/curves/LineCurve3.js
generated
vendored
Normal 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 };
|
||||
133
node_modules/three/src/extras/curves/QuadraticBezierCurve.js
generated
vendored
Normal file
133
node_modules/three/src/extras/curves/QuadraticBezierCurve.js
generated
vendored
Normal 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 };
|
||||
118
node_modules/three/src/extras/curves/QuadraticBezierCurve3.js
generated
vendored
Normal file
118
node_modules/three/src/extras/curves/QuadraticBezierCurve3.js
generated
vendored
Normal 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
145
node_modules/three/src/extras/curves/SplineCurve.js
generated
vendored
Normal 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
685
node_modules/three/src/extras/lib/earcut.js
generated
vendored
Normal 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};
|
||||
}
|
||||
Reference in New Issue
Block a user