UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

335 lines (334 loc) 10.2 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { Color } from "../../core/math/color.js"; const LINECAP_BUTT = 0; const LINECAP_SQUARE = 1; const LINECAP_ROUND = 2; const LINEJOIN_MITER = 0; const LINEJOIN_BEVEL = 1; const LINEJOIN_ROUND = 2; const validateStyle = (value, min, max, name) => { if (!Number.isInteger(value) || value < min || value > max) { throw new RangeError(`${name} must be an integer between ${min} and ${max}.`); } }; const validateNumber = (value, min, name) => { if (!Number.isFinite(value) || value < min) { throw new RangeError(`${name} must be a finite number greater than or equal to ${min}.`); } }; class WideLine { constructor() { /** @type {Float32Array} @ignore */ __publicField(this, "_positions", new Float32Array(0)); /** @type {Float32Array} @ignore */ __publicField(this, "_colors", new Float32Array([1, 1, 1])); /** @type {Float32Array} @ignore */ __publicField(this, "_widths", new Float32Array([1])); /** @type {WideLineRenderer|null} @ignore */ __publicField(this, "_renderer", null); /** @ignore */ __publicField(this, "_rendererIndex", -1); /** @ignore */ __publicField(this, "_join", LINEJOIN_MITER); /** @ignore */ __publicField(this, "_cap", LINECAP_BUTT); /** @ignore */ __publicField(this, "_closed", false); /** @ignore */ __publicField(this, "_dashLength", 0); /** @ignore */ __publicField(this, "_gapLength", 0); /** @ignore */ __publicField(this, "_dashOffset", 0); } /** * Atomically replaces all point data. This is the only operation that can change the number of * points. The colors and widths can each be supplied either per point or as a single uniform * value. * * @param {ArrayLike<number>} positions - Packed xyz coordinates. The length must be a multiple * of three and contain at least two points. * @param {ArrayLike<number>|Color} [colors] - Packed rgb values with one color per point, or a * single color used by every point. The alpha component of a {@link Color} is ignored. * @param {ArrayLike<number>|number} [widths] - One width per point, or a single width used by * every point. Width units are selected by the owning {@link WideLineRenderer} and values must * be non-negative. * @returns {WideLine} This line. */ set(positions, colors = Color.WHITE, widths = 1) { const pointCount = this._validatePositions(positions, 0); this._validateColors(colors, pointCount); this._validateWidths(widths, pointCount); const packedPositions = this._copyPositions(positions, 0, this._positions); const packedColors = this._copyColors(colors, pointCount, this._colors); const packedWidths = this._copyWidths(widths, pointCount, this._widths); this._positions = packedPositions; this._colors = packedColors; this._widths = packedWidths; this._changed(); return this; } /** * Replaces positions without changing the number of points. * * @param {ArrayLike<number>} positions - Packed xyz coordinates containing exactly * `pointCount * 3` values. * @returns {WideLine} This line. */ setPositions(positions) { this._positions = this._copyPositions(positions, this.pointCount, this._positions); this._changed(); return this; } /** * Replaces colors without changing the number of points. * * @param {ArrayLike<number>|Color} colors - Packed rgb values containing exactly * `pointCount * 3` values, or a single color used by every point. The alpha component of a * {@link Color} is ignored. * @returns {WideLine} This line. */ setColors(colors) { this._colors = this._copyColors(colors, this.pointCount, this._colors); this._changed(); return this; } /** * Replaces widths without changing the number of points. * * @param {ArrayLike<number>|number} widths - An array containing exactly `pointCount` values, * or a single width used by every point. Width units are selected by the owning * {@link WideLineRenderer} and values must be non-negative. * @returns {WideLine} This line. */ setWidths(widths) { this._widths = this._copyWidths(widths, this.pointCount, this._widths); this._changed(); return this; } /** * The number of points in the line. This is zero until {@link WideLine#set} is called. * * @type {number} */ get pointCount() { return this._positions.length / 3; } /** * The renderer that owns this line, or null when the line is not being rendered. * * @type {WideLineRenderer|null} */ get renderer() { return this._renderer; } /** * The join style. Can be {@link LINEJOIN_MITER}, {@link LINEJOIN_BEVEL} or * {@link LINEJOIN_ROUND}. Defaults to {@link LINEJOIN_MITER}. * * @type {number} */ set join(value) { validateStyle(value, LINEJOIN_MITER, LINEJOIN_ROUND, "join"); if (this._join !== value) { this._join = value; this._changed(); } } /** * Gets the join style. * * @type {number} */ get join() { return this._join; } /** * The cap style. Can be {@link LINECAP_BUTT}, {@link LINECAP_SQUARE} or * {@link LINECAP_ROUND}. The style also applies to the ends of visible dashes. Defaults to * {@link LINECAP_BUTT}. * * @type {number} */ set cap(value) { validateStyle(value, LINECAP_BUTT, LINECAP_ROUND, "cap"); if (this._cap !== value) { this._cap = value; this._changed(); } } /** * Gets the cap style. * * @type {number} */ get cap() { return this._cap; } /** * Whether the last point connects back to the first point. Defaults to false. * * @type {boolean} */ set closed(value) { value = !!value; if (this._closed !== value) { this._closed = value; this._changed(); } } /** * Gets whether the last point connects back to the first point. * * @type {boolean} */ get closed() { return this._closed; } /** * The length of each visible dash in world units. A value of zero makes the line solid. * Defaults to zero. * * @type {number} */ set dashLength(value) { validateNumber(value, 0, "dashLength"); if (this._dashLength !== value) { this._dashLength = value; this._changed(); } } /** * Gets the length of each visible dash in world units. * * @type {number} */ get dashLength() { return this._dashLength; } /** * The length of each gap in world units. A value of zero makes the line solid. Defaults to * zero. * * @type {number} */ set gapLength(value) { validateNumber(value, 0, "gapLength"); if (this._gapLength !== value) { this._gapLength = value; this._changed(); } } /** * Gets the length of each gap in world units. * * @type {number} */ get gapLength() { return this._gapLength; } /** * Offset of the dash pattern in world units. Defaults to zero. * * @type {number} */ set dashOffset(value) { if (!Number.isFinite(value)) { throw new RangeError("dashOffset must be a finite number."); } if (this._dashOffset !== value) { this._dashOffset = value; this._changed(); } } /** * Gets the offset of the dash pattern in world units. * * @type {number} */ get dashOffset() { return this._dashOffset; } /** @ignore */ _validatePositions(positions, expectedPointCount) { const length = positions?.length; if (!Number.isInteger(length) || length % 3 !== 0 || length < 6) { throw new RangeError("positions must contain packed xyz values for at least two points."); } const pointCount = length / 3; if (expectedPointCount && pointCount !== expectedPointCount) { throw new RangeError(`positions must contain exactly ${expectedPointCount * 3} values.`); } return pointCount; } /** @ignore */ _copyPositions(positions, expectedPointCount, target) { this._validatePositions(positions, expectedPointCount); const result = target.length === positions.length ? target : new Float32Array(positions.length); result.set(positions); return result; } /** @ignore */ _validateColors(colors, pointCount) { if (colors instanceof Color) { return; } if (colors?.length !== pointCount * 3) { throw new RangeError(`colors must contain exactly ${pointCount * 3} values.`); } } /** @ignore */ _copyColors(colors, pointCount, target) { this._validateColors(colors, pointCount); if (colors instanceof Color) { const result2 = target.length === 3 ? target : new Float32Array(3); result2[0] = colors.r; result2[1] = colors.g; result2[2] = colors.b; return result2; } const result = target.length === colors.length ? target : new Float32Array(colors.length); result.set(colors); return result; } /** @ignore */ _validateWidths(widths, pointCount) { if (typeof widths === "number") { validateNumber(widths, 0, "width"); return; } if (widths?.length !== pointCount) { throw new RangeError(`widths must contain exactly ${pointCount} values.`); } for (let i = 0; i < widths.length; i++) { validateNumber(widths[i], 0, `widths[${i}]`); } } /** @ignore */ _copyWidths(widths, pointCount, target) { this._validateWidths(widths, pointCount); if (typeof widths === "number") { const result2 = target.length === 1 ? target : new Float32Array(1); result2[0] = widths; return result2; } const result = target.length === widths.length ? target : new Float32Array(widths.length); result.set(widths); return result; } /** @ignore */ _changed() { this._renderer?._lineChanged(this); } } export { LINECAP_BUTT, LINECAP_ROUND, LINECAP_SQUARE, LINEJOIN_BEVEL, LINEJOIN_MITER, LINEJOIN_ROUND, WideLine };