New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@mathicsorg/mathics-threejs-backend

Package Overview
Dependencies
Maintainers
2
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mathicsorg/mathics-threejs-backend - npm Package Compare versions

Comparing version 1.2.1 to 1.2.2

tsconfig.json

10

CHANGES.md
CHANGES
=======
1.2.2
-----
Improvements:
- Type check the code with TypeScript in JSDocs (#103)
- Update jest to version 28
Bug fixes:
- Fix coplanar polygons
1.2.1

@@ -5,0 +15,0 @@ -----

9

package.json
{
"name": "@mathicsorg/mathics-threejs-backend",
"version": "1.2.1",
"version": "1.2.2",
"threejs_revision": 138,

@@ -32,3 +32,3 @@ "description": "Mathics 3D Graphics backend using three.js",

"start": "node scripts/server.js",
"test": "(npm start &) && eslint src && jest src && fuser -k 8080/tcp"
"test": "(npm start &) && eslint src && tsc && jest src && fuser -k 8080/tcp"
},

@@ -42,8 +42,9 @@ "dependencies": {

"eslint-plugin-jest": "^26",
"jest": "^27",
"jest": "^28",
"jest-image-snapshot": "^4",
"jest-puppeteer": "^6",
"minify": "^8",
"rollup": "^2"
"rollup": "^2",
"typescript": "^4"
}
}

@@ -1,9 +0,14 @@

import {
Matrix4,
Vector3
} from '../vendors/three.js';
// @ts-check
import { Matrix4, Vector3 } from '../vendors/three.js';
import { scalePartialCoordinate } from './coordinateUtils.js';
// This changes the value of position.
/**
* Get information about a tick given its position, camera, and container.
* For a better performance, this changes the value of position.
* @param {Vector3} position
* @param {import('../vendors/three.js').Camera} camera
* @param {HTMLElement} container
*/
function getTickInformation(position, camera, container) {

@@ -33,10 +38,16 @@ position.applyMatrix4(

|| tickPosition[1] < 5
|| tickPosition[0] > width - 5
|| tickPosition[1] > height - 5
|| tickPosition[0] > parseInt(width) - 5
|| tickPosition[1] > parseInt(height) - 5
};
}
// i is 0, 1 or 2.
// This is a 3d coordinate, but as the z value of this function is
// always 0, we can return just the 2 first values.
/**
* Get the tick direction given the radius of the scene.
* i is 0 for x, 1 for y, and 2 for z.
* This is a 3d coordinate, but as the z value of this function is
* always 0, we can return just the 2 first values.
* @param {0 | 1 | 2} i
* @param {number} radius
* @returns [x, y]
*/
function getTickDirection(i, radius) {

@@ -52,3 +63,20 @@ const tickLength = 0.005 * radius;

export function positionTickNumbers(hasAxes, tickNumbers, ticks, camera, container) {
/** @typedef {import('../vendors/three.js').LineSegments} LineSegments */
/**
* Re-position the tick numbers after its initial position has been set.
* @todo re-position ticksSmall too.
* @param {[boolean, boolean, boolean]} hasAxes
* @param {[HTMLElement[], HTMLElement[], HTMLElement[]]} tickNumbers
* @param {[LineSegments, LineSegments, LineSegments]} ticks
* @param {import('../vendors/three.js').Camera} camera
* @param {HTMLElement} container
*/
export function positionTickNumbers(
hasAxes,
tickNumbers,
ticks,
camera,
container
) {
for (let i = 0; i < 3; i++) {

@@ -61,4 +89,6 @@ if (hasAxes[i]) {

new Vector3(
// @ts-expect-error: we are sure this attribute is there
ticks[i].geometry.attributes.position.array[j * 6] * 7 - ticks[i].geometry.attributes.position.array[j * 6 + 3] * 6,
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 1] * 7 - ticks[i].geometry.attributes.position.array[j * 6 + 4] * 6,

@@ -70,2 +100,3 @@

// x * 7 - x * 6 = x
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 2]

@@ -89,2 +120,11 @@ ),

/**
* @param {[boolean, boolean, boolean]} hasAxes
* @param {import('./index.js').ConcreteAxes} axes
* @param {[LineSegments, LineSegments, LineSegments]} ticks
* @param {[LineSegments, LineSegments, LineSegments]} ticksSmall
* @param {{ [index: number]: number }} axesVerticesPosition
* @param {number} radius
* @param {import('./extent.js').Extent} extent
*/
export function setTicksInitialPosition(

@@ -101,29 +141,48 @@ hasAxes,

if (hasAxes[i]) {
const tickDirection = getTickDirection(i, radius);
const tickDirection = getTickDirection(
/** @type {0 | 1 | 2} */(i),
radius
);
axes.ticks[i][0].forEach((value, j) => {
const partialCoordinate = scalePartialCoordinate(value, i, extent);
const partialCoordinate = scalePartialCoordinate(
value,
/** @type {0 | 1 | 2} */(i),
extent
);
// Initialize the "position" buffer.
// @ts-expect-error: we are sure this attribute is there
ticks[i].geometry.attributes.position.array[j * 6] = axesVerticesPosition[0];
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 1] = axesVerticesPosition[1];
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 2] = axesVerticesPosition[2];
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 3] = axesVerticesPosition[0] + tickDirection[0];
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 4] = axesVerticesPosition[1] + tickDirection[1];
// tickDirection.z is always 0.
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 5] = axesVerticesPosition[2];
if (i === 0) {
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6] = partialCoordinate;
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 3] = partialCoordinate;
} else if (i === 1) {
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 1] = partialCoordinate;
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 4] = partialCoordinate;
} else {
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 2] = partialCoordinate;
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 5] = partialCoordinate;

@@ -134,26 +193,42 @@ }

axes.ticks[i][1].forEach((value, j) => {
const partialCoordinate = scalePartialCoordinate(value, i, extent);
const partialCoordinate = scalePartialCoordinate(
value,
/** @type {0 | 1 | 2} */(i),
extent
);
// Initialize the "position" buffer.
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6] = axesVerticesPosition[0];
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 1] = axesVerticesPosition[1];
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 2] = axesVerticesPosition[2];
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 3] = axesVerticesPosition[0] + tickDirection[0] / 2;
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 4] = axesVerticesPosition[1] + tickDirection[1] / 2;
// tickDirection.z is always 0.
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 5] = axesVerticesPosition[2];
if (i === 0) {
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6] = partialCoordinate;
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 3] = partialCoordinate;
} else if (i === 1) {
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 1] = partialCoordinate;
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 4] = partialCoordinate;
} else {
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 2] = partialCoordinate;
// @ts-expect-error: same as above
ticksSmall[i].geometry.attributes.position.array[j * 6 + 5] = partialCoordinate;

@@ -160,0 +235,0 @@ }

@@ -0,9 +1,20 @@

// @ts-check
import { scaleCoordinate } from './coordinateUtils.js';
// This is usually used to copy a coordinate into a coordinate buffer.
// The coordinates buffers are preallocated for efficiency on GPUs.
// Also, WebGL only accepts a typed array as an attribute.
// array is a 3-elements array
// index is the element index, the biggest index in a 3n-elements buffer is
// n - 1 (the first element is 0)
/** @typedef {import('./coordinateUtils.js').Coordinate} Coordinate */
/**
* Copy a 3-element array into a buffer given an index.
* The 1st element will go into index * 3.
* The 2nd element will go into index * 3 + 1.
* The 3rd element will go into index * 3 + 2.
* This is usually used to copy a coordinate into a coordinate buffer.
* The coordinates buffers are preallocated for efficiency on GPUs.
* Also, WebGL only accepts a typed array as an attribute.
*
* @param {{ [index: number]: number }} buffer
* @param {import('./coordinateUtils.js').Coordinate} array
* @param {number} index
*/
export function copyArray3IntoBuffer(buffer, array, index) {

@@ -15,4 +26,13 @@ buffer[index * 3] = array[0];

// The same as copyArray3IntoBuffer, but with
// a Vector3 instead of a 3-element array.
/**
* Copy a Vector3 into a buffer given an index.
* The x value will go into index * 3.
* The y value will go into index * 3 + 1.
* The z value will go into index * 3 + 2.
* The same as {@link copyArray3IntoBuffer}, but with
* a Vector3 instead of a 3-element array.
* @param {{ [index: number]: number }} buffer
* @param {import('../vendors/three.js').Vector3} vector
* @param {number} index
*/
export function copyVector3IntoBuffer(buffer, vector, index) {

@@ -24,9 +44,18 @@ buffer[index * 3] = vector.x;

// Create a coordinate buffer and copy the coordinates from coords to it.
// The coordinates are in the form [[x, y, z]] or [null, [x, y, z]] and copyIntoCoordinateBuffer receives a list, so we need to:
// - transform [null, [x, y, z]] into [x, y, z]. This is done through scaleCoordinate.
// - transform [[x, y, z]] into [x, y, z]. This is done taking the first element of the list.
/**
* Create a coordinate buffer and copy the coordinates from coords to it.
* @param {Array<[Coordinate, null] | [null, Coordinate]>} coords
* @param {import('./extent.js').Extent} extent
* @returns a coordinate buffer populated with the coordinates from coords
*/
export function getPopulatedCoordinateBuffer(coords, extent) {
const coordinateBuffer = new Float32Array(coords.length * 3);
// The coordinates are in the form [[x, y, z]] or [null, [x, y, z]]
// and copyIntoCoordinateBuffer receives a list, so we need to:
// - transform [null, [x, y, z]] into [x, y, z].
// This is done through scaleCoordinate.
// - transform [[x, y, z]] into [x, y, z].
// This is done taking the first element of the list.
coords.forEach((coordinate, i) =>

@@ -43,5 +72,11 @@ copyArray3IntoBuffer(

// Create 2 coordinate buffers and copy the even-numbered coordinates from coords to the 1st coordinate buffer and the odd-numbered ones to the 2nd.
// This is usuful when the primitive have a begin and a end coordinate. Both can't be in the same BufferAttribute.
// Returns an array with the 2 coordinate buffers
/**
* Create 2 coordinate buffers and copy the even-numbered coordinates from
* coords to the 1st coordinate buffer and the odd-numbered ones to the 2nd.
* This is usuful when the primitive have a begin and a end coordinate.
* Both can't be in the same BufferAttribute.
* @param {Array<[Coordinate, null] | [null, Coordinate]>} coords
* @param {import('./extent.js').Extent} extent
* @returns an array with the 2 coordinate buffers
*/
export function get2PopulatedCoordinateBuffers(coords, extent) {

@@ -55,3 +90,6 @@ // number of vertices per coordinate / number of coordinates per primitive = 3 / 2

coordinateBuffer1,
coords[i * 2][0] ?? scaleCoordinate(coords[i * 2][1], extent),
coords[i * 2][0] ?? scaleCoordinate(
/** @type {Coordinate} */(coords[i * 2][1]),
extent
),
i

@@ -62,3 +100,6 @@ );

coordinateBuffer2,
coords[i * 2 + 1][0] ?? scaleCoordinate(coords[i * 2 + 1][1], extent),
coords[i * 2 + 1][0] ?? scaleCoordinate(
/** @type {Coordinate} */(coords[i * 2 + 1][1]),
extent
),
i

@@ -65,0 +106,0 @@ );

@@ -0,1 +1,14 @@

// @ts-check
/** @typedef {[number, number, number]} Coordinate */
/**
* Scale a coordinate (usually the 2nd element of a higher-order coordinate)
* to the size of the bounding box. e.g.:
* (0, 0, 0) is the lower-front-left of the bounding box.
* (1, 1, 1) is the upper-back-right of the bounding box.
* @param {Coordinate} coordinate
* @param {import('./extent').Extent} extent
* @returns {Coordinate}
*/
export function scaleCoordinate(coordinate, extent) {

@@ -9,5 +22,9 @@ return [

// Multiply partialCoordinate by (extent.`i`max - extent.`i`min) and add
// extent.`i`min.
// i is 0 for x, 1 for y and 2 for z.
/**
* i is 0 for x, 1 for y and 2 for z.
* @param {number} partialCoordinate
* @param {0 | 1 | 2} i
* @param {import('./extent').Extent} extent
* @returns partialCoordinate * (extent.imax - extent.imin) + extent.imin
*/
export function scalePartialCoordinate(partialCoordinate, i, extent) {

@@ -14,0 +31,0 @@ if (i === 0) {

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -11,3 +13,9 @@ Matrix4,

// Modified from three.js CatmullRomCurve3
/** @typedef {import('./coordinateUtils.js').Coordinate} Coordinate */
/**
* Modified from three.js CatmullRomCurve3
* @param {Array<[Coordinate, null] | [null, Coordinate]>} coordinates
* @param {import('./extent.js').Extent} extent
*/
export function getCentripetalCurve(coordinates, extent) {

@@ -30,4 +38,7 @@ const points = coordinates.map((coordinate) => new Vector3(

/**
* @param {number} t
*/
function getPoint(t) {
const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly();
const px = CubicPoly(), py = CubicPoly(), pz = CubicPoly();

@@ -89,6 +100,9 @@ const p = (points.length - 1) * t;

// Returns a unit vector tangent at t.
// In case any sub curve does not implement its tangent derivation,
// 2 points a small delta apart will be used to find its gradient
// which seems to give a reasonable approximation.
/**
* In case any sub curve does not implement its tangent derivation,
* 2 points a small delta apart will be used to find its gradient
* which seems to give a reasonable approximation.
* @param {number} t
* @returns a unit vector tangent at t
*/
function getTangent(t) {

@@ -109,2 +123,5 @@ const delta = 0.0001;

/**
* @param {number} u
*/
function getUtoTmapping(u) {

@@ -156,5 +173,11 @@ let i = 0;

getPoint,
/**
* @param {number} u
*/
getPointAt(u) {
return getPoint(getUtoTmapping(u));
},
/**
* @param {number} segments
*/
computeFrenetFrames(segments) {

@@ -161,0 +184,0 @@ const vector = new Vector3();

@@ -0,4 +1,14 @@

// @ts-check
import { scaleCoordinate } from './coordinateUtils.js';
export default function (elements) {
/** @typedef {import('./coordinateUtils.js').Coordinate} Coordinate */
/** @typedef {ReturnType<extent>} Extent */
/**
* Get the extent (bounding box size) for the elements
* @param {import('./primitives/index.js').PrimitiveElement[]} elements
*/
export default function extent(elements) {
const extent = {

@@ -119,23 +129,62 @@ xmin: 0,

if (!coordinate[0]) {
coordinate[0] = scaleCoordinate(coordinate[1], extent);
// We are changing the type of coordinate[0] from null
// to Coordinate, TypeScript gives a warning here if we
// don't cast coordinate[0] to unknown.
/** @type {unknown} */(coordinate[0]) = scaleCoordinate(coordinate[1], extent);
if (coordinate[0][0] - radius < extent.xmin) {
extent.xmin = coordinate[0][0] - radius;
if (
/** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[0] - radius < extent.xmin
) {
extent.xmin = /** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[0] - radius;
}
if (coordinate[0][0] + radius > extent.xmax) {
extent.xmax = coordinate[0][0] + radius;
if (
/** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[0] + radius > extent.xmax
) {
extent.xmax = /** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[0] + radius;
}
if (coordinate[0][1] - radius < extent.ymin) {
extent.ymin = coordinate[0][1] - radius;
if (
/** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[1] - radius < extent.ymin
) {
extent.ymin = /** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[1] - radius;
}
if (coordinate[0][1] + radius > extent.ymax) {
extent.ymax = coordinate[0][1] + radius;
if (
/** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[1] + radius > extent.ymax
) {
extent.ymax = /** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[1] + radius;
}
if (coordinate[0][2] - radius < extent.zmin) {
extent.zmin = coordinate[0][2] - radius;
if (
/** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[2] - radius < extent.zmin
) {
extent.zmin = /** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[2] - radius;
}
if (coordinate[0][2] + radius > extent.zmax) {
extent.zmax = coordinate[0][2] + radius;
if (
/** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[2] + radius > extent.zmax
) {
extent.zmax = /** @type {Coordinate} */(
/** @type {unknown} */(coordinate[0])
)[2] + radius;
}

@@ -142,0 +191,0 @@ }

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -10,3 +12,7 @@ BufferAttribute,

// Modified from three.js' BufferGeometryUtils.
/**
* Modified from three.js' BufferGeometryUtils.
* @param {BufferAttribute[]} attributes
* @returns a new attribute with the values from all attributes.
*/
function mergeBufferAttributes(attributes) {

@@ -30,5 +36,9 @@ let arrayLength = 0;

// Modified from three.js' BufferGeometryUtils.
/**
* Modified from three.js' BufferGeometryUtils.
* @param {BufferGeometry[]} geometries
*/
export function mergeBufferGeometries(geometries) {
const mergedIndex = [];
/** @type {{ [key: string]: BufferAttribute[] }} */
const attributes = {};

@@ -41,2 +51,4 @@ const mergedGeometry = new BufferGeometry();

// @ts-expect-error: name is in attributes, so we can use it as an
// index.
attributes[name].push(geometries[i].attributes[name]);

@@ -49,2 +61,4 @@ }

// @ts-expect-error: we expect the geometry to have the
// position attribute.
indexOffset += geometries[i].attributes.position.count;

@@ -65,3 +79,8 @@ }

// This is used in spheres and in tubes' end caps.
/**
* This is used in spheres and in tubes' end caps.
* @param {number} radius
* @param {boolean} instanced
* @param {boolean} halfSphere
*/
export function getSphereGeometry(radius, instanced = false, halfSphere = false) {

@@ -136,2 +155,7 @@ const phiEnd = halfSphere ? Math.PI : Math.PI * 2;

// TODO: add cache
/**
* @param {number} radius
* @param {ReturnType<import('curve.js').getCentripetalCurve>} path
* @returns {BufferGeometry} the geometry of a tube passing through the path.
*/
export function getTubeGeometry(radius, path) {

@@ -138,0 +162,0 @@ // tubularSegments

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -17,3 +19,15 @@ BufferAttribute,

import { getUniformsBuffer } from './uniforms.js';
import { clamp } from './math.js';
/**
* @typedef {import('./index.js').AxesTicks} AxesTicks
* @typedef {import('./index.js').Axes} Axes
* @typedef {import('./index.js').ConcreteAxes} ConcreteAxes
* @typedef {import('./index.js').TicksStyle} TicksStyle
*/
/**
* Set the default CSS for the container
* @param {HTMLElement} container
*/
function setDefaultContainerStyle(container) {

@@ -41,2 +55,14 @@ const style = getComputedStyle(container);

/**
* Plot the data
* @param {HTMLElement} container
* @param {{
* axes?: Axes,
* autoRescale?: boolean,
* extent?: import('./extent.js').Extent,
* elements?: import('./primitives/index.js').PrimitiveElement[],
* lighting?: { type: import('./lights.js').LightType }[],
* viewpoint?: import('./coordinateUtils.js').Coordinate
* }} data
*/
export default function (

@@ -56,13 +82,13 @@ container,

let isCtrlDown,
isShiftDown,
onMouseDownFocus,
onCtrlDownFov,
hasAxes,
isMouseDown = false,
theta,
onMouseDownTheta,
phi,
onMouseDownPhi,
onTouchStartFingersDistance;
let /** @type {boolean} */ isCtrlDown,
/** @type {boolean} */ isShiftDown,
/** @type {Vector3} */ onMouseDownFocus,
/** @type {number} */ onCtrlDownFov,
/** @type {[boolean, boolean, boolean]} */ hasAxes,
/** @type {boolean} */ isMouseDown = false,
/** @type {number} */ theta,
/** @type {number} */ onMouseDownTheta,
/** @type {number} */ phi,
/** @type {number} */ onMouseDownPhi,
/** @type {number} */ onTouchStartFingersDistance;

@@ -107,2 +133,3 @@ const onMouseDownPosition = new Int16Array(2);

function updateCameraPosition() {
// @ts-expect-error: bad three.js typing
camera.position.set(

@@ -185,3 +212,4 @@ radius * Math.sin(theta) * Math.cos(phi),

// axes ticks
const ticks = new Array(3), ticksSmall = new Array(3);
const ticks = /** @type {[LineSegments, LineSegments, LineSegments]} */(new Array(3)),
ticksSmall = /** @type {[LineSegments, LineSegments, LineSegments]} */(new Array(3));

@@ -194,3 +222,5 @@ for (let i = 0; i < 3; i++) {

new BufferAttribute(
new Float32Array(6 * axes.ticks[i][0].length),
new Float32Array(
6 * /** @type {AxesTicks} */(axes.ticks)[i][0].length
),
3

@@ -208,3 +238,5 @@ )

new BufferAttribute(
new Float32Array(6 * axes.ticks[i][1].length),
new Float32Array(
6 * /** @type {AxesTicks} */(axes.ticks)[i][1].length
),
3

@@ -222,5 +254,6 @@ )

hasAxes,
axes,
/** @type {ConcreteAxes} */(axes),
ticks,
ticksSmall,
// @ts-expect-error: we are sure this attribute is in there
boundingBox.geometry.attributes.position.array,

@@ -231,8 +264,9 @@ radius,

// axes numbering using divs
const tickNumbers = new Array(3);
const tickNumbers = /** @type {[HTMLElement[], HTMLElement[], HTMLElement[]]} */(new Array(3));
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
tickNumbers[i] = new Array(axes.ticks[i][0].length);
tickNumbers[i] = new Array(
/** @type {AxesTicks} */(axes.ticks)[i][0].length
);

@@ -242,16 +276,22 @@ for (let j = 0; j < tickNumbers[i].length; j++) {

if (i < axes.ticks_style?.length) {
color = `rgb(${axes.ticks_style[i][0] * 255}, ${axes.ticks_style[i][1] * 255}, ${axes.ticks_style[i][2] * 255})`;
if (i < (axes.ticks_style?.length ?? 0)) {
color = `rgb(
${/** @type {TicksStyle} */(axes.ticks_style)[i][0] * 255},
${/** @type {TicksStyle} */(axes.ticks_style)[i][1] * 255},
${/** @type {TicksStyle} */(axes.ticks_style)[i][2] * 255}
)`;
}
tickNumbers[i][j] = document.createElement('div');
tickNumbers[i][j].innerHTML = axes.ticks[i][2][j].startsWith('0.')
? axes.ticks[i][2][j].replace('0.', '.')
: axes.ticks[i][2][j];
tickNumbers[i][j].innerHTML =
/** @type {AxesTicks} */(axes.ticks)[i][2][j]
.startsWith('0.')
? /** @type {AxesTicks} */(axes.ticks)[i][2][j].replace('0.', '.')
: /** @type {AxesTicks} */(axes.ticks)[i][2][j];
// handle minus signs
if (axes.ticks[i][0][j] >= 0) {
if (/** @type {AxesTicks} */(axes.ticks)[i][0][j] >= 0) {
tickNumbers[i][j].style.paddingLeft = '0.5em';
} else {
tickNumbers[i][j].style.paddingLeft = 0;
tickNumbers[i][j].style.paddingLeft = '0px';
}

@@ -270,3 +310,8 @@

elements.forEach(
(element) => scene.add(primitiveFunctions[element.type](element, uniforms, extent, container))
(element) => scene.add(primitiveFunctions[element.type](
element,
uniforms,
/** @type {import('./extent.js').Extent} */(extent),
container
))
);

@@ -312,4 +357,7 @@

proj2d.set(
// @ts-expect-error: we are sure this attribute is in there
boundingBox.geometry.attributes.position.array[i * 3],
// @ts-expect-error: the same as above
boundingBox.geometry.attributes.position.array[i * 3 + 1],
// @ts-expect-error: the same as above
boundingBox.geometry.attributes.position.array[i * 3 + 2]

@@ -328,2 +376,6 @@ ).applyMatrix4(camera.matrixWorldInverse);

/**
* @param {MouseEvent | TouchEvent} event
* @param {boolean} touch whether the event is with touch instead of mouse.
*/
function onMouseDown(event, touch) {

@@ -339,4 +391,8 @@ event.preventDefault();

onMouseDownPosition[0] = touch ? event.touches[0].clientX : event.clientX;
onMouseDownPosition[1] = touch ? event.touches[0].clientY : event.clientY;
onMouseDownPosition[0] = touch
? /** @type {TouchEvent} */(event).touches[0].clientX
: /** @type {MouseEvent} */(event).clientX;
onMouseDownPosition[1] = touch
? /** @type {TouchEvent} */(event).touches[0].clientY
: /** @type {MouseEvent} */(event).clientY;

@@ -346,2 +402,6 @@ onMouseDownFocus = focus.clone();

/**
* @param {MouseEvent | TouchEvent} event
* @param {boolean} touch whether the event is with touch instead of mouse.
*/
function onMouseMove(event, touch) {

@@ -352,4 +412,8 @@ event.preventDefault();

const clientX = touch ? event.touches[0].clientX : event.clientX;
const clientY = touch ? event.touches[0].clientY : event.clientY;
const clientX = touch
? /** @type {TouchEvent} */(event).touches[0].clientX
: /** @type {MouseEvent} */(event).clientX;
const clientY = touch
? /** @type {TouchEvent} */(event).touches[0].clientY
: /** @type {MouseEvent} */(event).clientY;

@@ -374,2 +438,3 @@ positionTickNumbers(hasAxes, tickNumbers, ticks, camera, container);

const cameraY = new Vector3()
// @ts-expect-error: bad three.js typing
.subVectors(focus, camera.position)

@@ -386,3 +451,6 @@ .normalize()

updateCameraPosition();
} else if (event.ctrlKey || (touch && event.touches.length === 2)) { // zoom
} else if (
event.ctrlKey
|| (touch && /** @type {TouchEvent} */(event).touches.length === 2)
) { // zoom
if (!isCtrlDown) {

@@ -398,4 +466,4 @@ isCtrlDown = true;

onTouchStartFingersDistance = Math.hypot(
clientX - event.touches[1].clientX,
clientY - event.touches[1].clientY
clientX - /** @type {TouchEvent} */(event).touches[1].clientX,
clientY - /** @type {TouchEvent} */(event).touches[1].clientY
);

@@ -409,4 +477,4 @@ }

fov -= (Math.hypot(
clientX - event.touches[1].clientX,
clientY - event.touches[1].clientY
clientX - /** @type {TouchEvent} */(event).touches[1].clientX,
clientY - /** @type {TouchEvent} */(event).touches[1].clientY
) - onTouchStartFingersDistance) / 25;

@@ -433,5 +501,9 @@ } else {

phi = onMouseDownPhi + 2 * Math.PI * (onMouseDownPosition[0] - event.clientX) / parseInt(width);
phi = onMouseDownPhi + 2 * Math.PI * (
onMouseDownPosition[0] - /** @type {MouseEvent} */(event).clientX
) / parseInt(width);
theta = onMouseDownTheta + 2 * Math.PI * (onMouseDownPosition[1] - event.clientY) / parseInt(height);
theta = onMouseDownTheta + 2 * Math.PI * (
onMouseDownPosition[1] - /** @type {MouseEvent} */(event).clientY
) / parseInt(height);

@@ -441,6 +513,3 @@ // This prevents spinning over the poles by keeping the angle

// Angles too close to 0 or Pi problems.
theta = Math.max(
Math.min(theta, Math.PI - 1e-12),
1e-12
);
theta = clamp(theta, 1e-12, Math.PI - 1e-12);

@@ -453,2 +522,5 @@ updateCameraPosition();

/**
* @param {MouseEvent | TouchEvent} event
*/
function onMouseUp(event) {

@@ -455,0 +527,0 @@ event.preventDefault();

@@ -0,20 +1,54 @@

// @ts-check
import drawGraphics3d from './graphics3d.js';
function translationLayer(container, object, maxSize, innerWidthMultiplier) {
if (object.protocol) {
/**
* @typedef {[number[], number[], string[]]} AxesTick
* @typedef {[AxesTick, AxesTick, AxesTick]} AxesTicks
* @typedef {[number, number, number]} TickStyle
* @typedef {[TickStyle, TickStyle, TickStyle]} TicksStyle
* @typedef {{
* hasaxes?: boolean | [boolean, boolean, boolean],
* ticks?: AxesTicks,
* ticks_style?: TicksStyle
* }} Axes
* @typedef {{
* hasaxes: boolean | [boolean, boolean, boolean],
* ticks: AxesTicks,
* ticks_style?: TicksStyle
* }} ConcreteAxes
*/
/**
* Translate deprecated code into running code.
* @param {HTMLElement} container
* @param {{
* axes?: Axes,
* autoRescale?: boolean,
* extent?: import('./extent.js').Extent,
* elements?: any[],
* lighting?: any[],
* protocol?: string,
* viewpoint?: import('./coordinateUtils.js').Coordinate
* }} data data
* @param {number} maxSize
* @param {number} innerWidthMultiplier
*/
function translationLayer(container, data, maxSize, innerWidthMultiplier) {
if (data.protocol) {
// protocol version is X.Y, so it is an array of two elements: major version and minor version
const versionArray = object.protocol.match(/\d/g);
const versionArray = data.protocol.match(/\d/g);
if (parseInt(versionArray[0]) !== 1) {
if (parseInt(/** @type {RegExpMatchArray} */(versionArray)[0]) !== 1) {
const warning = document.createElement('p');
warning.style.color = 'yellow';
warning.innerText = `The major revision version of mathics-threejs-backend is 1, but it was expected to be ${versionArray[0]}. Trying to draw the graphics.`;
warning.innerText = `The major revision version of mathics-threejs-backend is 1, but it was expected to be ${/** @type {RegExpMatchArray} */(versionArray)[0]}. Trying to draw the graphics.`;
container.appendChild(warning);
} else if (parseInt(versionArray[1]) > 2) {
} else if (parseInt(/** @type {RegExpMatchArray} */(versionArray)[1]) > 2) {
const warning = document.createElement('p');
warning.style.color = 'yellow';
warning.innerText = `The minor revision version of mathics-threejs-backend is 1, but it was expected to be at least ${versionArray[1]}. Trying to draw the graphics.`;
warning.innerText = `The minor revision version of mathics-threejs-backend is 1, but it was expected to be at least ${/** @type {RegExpMatchArray} */(versionArray)[1]}. Trying to draw the graphics.`;

@@ -25,3 +59,3 @@ container.appendChild(warning);

object.elements?.forEach((primitive) => {
data.elements?.forEach((/** @type {*} */primitive) => {
if (primitive.faceColor) {

@@ -34,3 +68,3 @@ primitive.color = primitive.faceColor;

object.lighting?.forEach((light) => {
data.lighting?.forEach((/** @type {*} */light) => {
if (light.position) {

@@ -48,7 +82,8 @@ light.coords = [light.position];

return drawGraphics3d(container, object);
return drawGraphics3d(container, data);
}
// @ts-expect-error: drawGraphics3d ins't a property of window.
window.drawGraphics3d = translationLayer;
export default drawGraphics3d;

@@ -0,1 +1,3 @@

// @ts-check
// Unlike the primitives, all lights are in the same file.

@@ -22,13 +24,30 @@ // Each light function takes 2 parameters and returns a three.js object.

/** @typedef {import('./coordinateUtils.js').Coordinate} Coordinate */
/** @typedef {'ambient' | 'directional' | 'point' | 'spot'} LightType */
/** @type {{ [key in LightType]: Function }} */
export default {
// See https://mathics3.github.io/mathics-threejs-backend/lights/ambient
// for the high-level description of what is being rendered.
ambient: ({ color = [1, 1, 1] }, lights) => {
lights.ambientLightColor.value[0] += color[0];
lights.ambientLightColor.value[1] += color[1];
lights.ambientLightColor.value[2] += color[2];
/**
* See {@link https://mathics3.github.io/mathics-threejs-backend/lights/ambient}
* for the high-level description of what is being rendered.
* @param {{ color: [number, number, number] }} light
* @param {import('./uniforms.js').UniformsBuffer} uniforms
*/
ambient: ({ color = [1, 1, 1] }, uniforms) => {
uniforms.ambientLightColor.value[0] += color[0];
uniforms.ambientLightColor.value[1] += color[1];
uniforms.ambientLightColor.value[2] += color[2];
},
// See https://mathics3.github.io/mathics-threejs-backend/lights/directional
// for the high-level description of what is being rendered.
directional: ({ color = [1, 1, 1], coords }, lights, extent) => {
/**
* See {@link https://mathics3.github.io/mathics-threejs-backend/lights/directional}
* for the high-level description of what is being rendered.
* @param {{
* color: [number, number, number],
* coords: [Coordinate, null] | [null, Coordinate]
* }} light
* @param {import('./uniforms.js').UniformsBuffer} uniforms
* @param {import('./extent.js').Extent} extent
*/
directional: ({ color = [1, 1, 1], coords }, uniforms, extent) => {
const direction = new Vector3(

@@ -42,3 +61,3 @@ ...(coords[0] ?? scaleCoordinate(coords[1], extent))

lights.directionalLights.value.push({
uniforms.directionalLights.value.push({
color,

@@ -48,6 +67,14 @@ direction

},
// See https://mathics3.github.io/mathics-threejs-backend/lights/point
// for the high-level description of what is being rendered.
point: ({ color = [1, 1, 1], coords }, lights, extent) => {
lights.pointLights.value.push({
/**
* See {@link https://mathics3.github.io/mathics-threejs-backend/lights/point}
* for the high-level description of what is being rendered.
* @param {{
* color: [number, number, number],
* coords: [Coordinate, null] | [null, Coordinate]
* }} light
* @param {import('./uniforms.js').UniformsBuffer} uniforms
* @param {import('./extent.js').Extent} extent
*/
point: ({ color = [1, 1, 1], coords }, uniforms, extent) => {
uniforms.pointLights.value.push({
color,

@@ -57,6 +84,16 @@ basePosition: new Vector3(...coords[0] ?? scaleCoordinate(coords[1], extent))

},
// See https://mathics3.github.io/mathics-threejs-backend/lights/spot
// for the high-level description of what is being rendered.
// The default angle is π/2.
spot: ({ angle = 1.57079632679, color = [1, 1, 1], coords, target }, lights, extent) => {
/**
* See {@link https://mathics3.github.io/mathics-threejs-backend/lights/spot}
* for the high-level description of what is being rendered.
* The default angle is π/2.
* @param {{
* angle: number,
* color: [number, number, number],
* coords: [Coordinate, null] | [null, Coordinate],
* target: [Coordinate, null] | [null, Coordinate]
* }} light
* @param {import('./uniforms.js').UniformsBuffer} uniforms
* @param {import('./extent.js').Extent} extent
*/
spot: ({ angle = 1.57079632679, color = [1, 1, 1], coords, target }, uniforms, extent) => {
const basePosition = new Vector3(

@@ -70,3 +107,3 @@ ...(coords[0] ?? scaleCoordinate(coords[1], extent))

lights.spotLights.value.push({
uniforms.spotLights.value.push({
color,

@@ -73,0 +110,0 @@ baseDirection,

@@ -0,1 +1,9 @@

// @ts-check
/**
* @param {number} value
* @param {number} min
* @param {number} max
* @returns a clamped value kept between min and max.
*/
export function clamp(value, min, max) {

@@ -8,8 +16,14 @@ return Math.max(min, Math.min(max, value));

// 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.
/**
* 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.
* @param {number} x0
* @param {number} x1
* @param {number} t0
* @param {number} t1
*/
function init(x0, x1, t0, t1) {

@@ -23,2 +37,11 @@ c0 = x0;

return {
/**
* @param {number} x0
* @param {number} x1
* @param {number} x2
* @param {number} x3
* @param {number} dt0
* @param {number} dt1
* @param {number} dt2
*/
initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) {

@@ -35,2 +58,5 @@ // compute tangents when parameterized in [t1, t2]

},
/**
* @param {number} t
*/
calc: function (t) {

@@ -37,0 +63,0 @@ const t2 = t * t;

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -15,6 +17,11 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/arrow
// for the high-level description of what is being rendered.
/** @typedef {import('../coordinateUtils.js').Coordinate} Coordinate */
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/arrow}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [0, 0, 0], coords, opacity = 1 }, uniforms, extent) {

@@ -27,3 +34,6 @@ const material = getBasicMaterial(color, opacity);

const startCoordinate = new Vector3(
...(coords[coords.length - 2][0] ?? scaleCoordinate(coords[coords.length - 2][1], extent))
...(coords[coords.length - 2][0] ?? scaleCoordinate(
/** @type {Coordinate} */(coords[coords.length - 2][1]),
extent
))
);

@@ -33,3 +43,6 @@

const endCoordinate = new Vector3(
...(coords[coords.length - 1][0] ?? scaleCoordinate(coords[coords.length - 1][1], extent))
...(coords[coords.length - 1][0] ?? scaleCoordinate(
/** @type {Coordinate} */(coords[coords.length - 1][1]),
extent
))
);

@@ -90,2 +103,3 @@

]),
// @ts-expect-error: bad three.js typing
material

@@ -104,2 +118,3 @@ ));

),
// @ts-expect-error: bad three.js typing
material

@@ -106,0 +121,0 @@ )

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -14,6 +16,9 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/cone
// for the high-level description of what is being rendered.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/cone}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, edgeForm = {}, opacity = 1, radius = 1 }, uniforms, extent) {

@@ -184,2 +189,3 @@ const [coneBases, coneTips] = get2PopulatedCoordinateBuffers(coords, extent);

coneGeometry,
// @ts-expect-error: bad three.js typing
get2CoordinatesMaterial(color, opacity, uniforms)

@@ -255,2 +261,3 @@ );

edgesGeometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -257,0 +264,0 @@ vertexShader: `#version 300 es

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -11,6 +13,9 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/cuboid
// for the high-level description of what is being rendered.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/cuboid}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, edgeForm = {}, opacity = 1 }, uniforms, extent) {

@@ -109,2 +114,3 @@ // The edges of the cuboids are drawn in the fragment shader; doing this is faster than putting the edges in a different object.

cuboidGeometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -111,0 +117,0 @@ transparent: opacity !== 1,

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -14,6 +16,9 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/cylinder
// for the high-level description of what is being rendered.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/cylinder}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, edgeForm = {}, opacity = 1, radius = 1 }, uniforms, extent) {

@@ -380,2 +385,3 @@ const [cylindersBegin, cylindersEnd] = get2PopulatedCoordinateBuffers(coords, extent);

cylinderGeometry,
// @ts-expect-error: bad three.js typing
get2CoordinatesMaterial(color, opacity, uniforms)

@@ -484,2 +490,3 @@ );

edgesGeometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -486,0 +493,0 @@ vertexShader: `#version 300 es

@@ -0,1 +1,3 @@

// @ts-check
// This file exports implmentations using three.js of Mathematica and

@@ -7,17 +9,57 @@ // Mathics Graphics3D primitives like "Sphere", or "Cuboid, etc.

// Each primitive function takes 4 parameters and returns a three.js
// object.
/**
* Each primitive function takes 4 parameters and returns a three.js object
*
* The 1st parameter is the primitive object (an element of
* the elements array).
*
* The 2nd parameter is the uniforms buffer, @see {@link src/uniforms.js}
* for more information.
*
* The 3rd parameter is the extent, it is used in scaleCoordinate,
* but it can be used for calculating border size, radius, ...
*
* The 4th parameter is the container, its style (CSS) is used for e.g.
* calculating the points size.
*
* @typedef { 'arrow'
* | 'cone'
* | 'cuboid'
* | 'cylinder'
* | 'line'
* | 'point'
* | 'polygon'
* | 'sphere'
* | 'tube'
* | 'uniformPolyhedron'
* } PrimitiveType
*
* @typedef {import('../coordinateUtils.js').Coordinate} Coordinate
*
* @typedef {{
* type: PrimitiveType,
* color?: [number, number, number],
* coords: Array<[Coordinate, null] | [null, Coordinate]>,
* dashed?: boolean,
* edgeForm?: {
* color?: [number, number, number],
* showEdges?: boolean
* },
* edgeLength?: number,
* gapSize?: number,
* opacity?: number,
* pointSize?: number,
* radius?: number,
* subType?: 'tetrahedron' | 'octahedron' | 'dodecahedron' | 'icosahedron',
* vertexNormals?: [number, number, number][]
* }} PrimitiveElement
*
* @typedef {(
* primitive: PrimitiveElement,
* uniforms: import('../uniforms.js').UniformsBuffer,
* extent: import('../extent.js').Extent,
* container: HTMLElement
* ) => import('../../vendors/three.js').Object3D } PrimitiveFunction
*/
// The 1st parameter is the primitive object (an element of
// the elements array).
// The 2nd parameter is the uniforms buffer, read the comments from
// src/uniforms.js for more information.
// The 3rd parameter is the extent, it is used in scaleCoordinate,
// but it can be used for calculating border size, radius, ...
// The 4th parameter is the canvasSize, it is used for e.g. calculating the
// points size.
// Note that Graphics3D includes a number of 1D and 2D kinds of

@@ -24,0 +66,0 @@ // objects, like Point, Line, Arrow, or Polygon which are extended

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -11,8 +13,12 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/line
// for the high-level description of what is being rendered.
// Differently from WL's Line, our lines aren't affected by
// lightning and therefore don't have VertexNormals.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/line}
* for the high-level description of what is being rendered.
* Differently from WL's Line, our lines aren't affected by
* lightning and therefore don't have VertexNormals.
*
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [0, 0, 0], coords, dashed = false, gapSize = 10, opacity = 1 }, uniforms, extent, container) {

@@ -27,2 +33,3 @@ return new Line(

),
// @ts-expect-error: bad three.js typing
dashed

@@ -29,0 +36,0 @@ ? new RawShaderMaterial({

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -10,9 +12,13 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/point
// for the high-level description of what is being rendered.
// Differently from WL's Point, our points aren't affected by
// lightning and therefore don't have VertexNormals.
export default function ({ color = [0, 0, 0], coords, opacity = 1, pointSize }, uniforms, extent, container) {
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/point}
* for the high-level description of what is being rendered.
* Differently from WL's Point, our points aren't affected by
* lightning and therefore don't have VertexNormals.
*
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [0, 0, 0], coords, opacity = 1, pointSize = 1 }, uniforms, extent, container) {
return new Points(

@@ -26,2 +32,3 @@ new BufferGeometry().setAttribute(

),
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -28,0 +35,0 @@ transparent: true,

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -7,2 +9,3 @@ BufferAttribute,

Mesh,
Quaternion,
RawShaderMaterial,

@@ -20,9 +23,15 @@ Vector3

// Get the unit normal vector from the 1st, 2nd and last coordinate
// (these numbers were choosen because the vectors 1st->2nd and last->2nd
// have different directions, what is necessary for some calculations)
// Note: a "better" way to do this is compute an approximation plane
// by taking linear least squares, but that would be way slower and
// there would be only difference for very specific polygons.
// (see https://en.wikipedia.org/wiki/Linear_least_squares)
/** @typedef {import('../coordinateUtils.js').Coordinate} Coordinate */
/**
* Get the unit normal vector from the 1st, 2nd and last coordinate
* (these numbers were choosen because the vectors 1st->2nd and last->2nd
* have different directions, what is necessary for some calculations)
* Note: a "better" way to do this is compute an approximation plane
* by taking linear least squares, but that would be way slower and
* there would be only difference for very specific polygons.
* (see https://en.wikipedia.org/wiki/Linear_least_squares)
* @param {Array<[Coordinate, null] | [null, Coordinate]>} coordinates
* @param {import('../extent.js').Extent} extent
*/
function getNormalVector(coordinates, extent) {

@@ -36,3 +45,6 @@ const vectorA = new Vector3(

const vectorC = new Vector3(
...coordinates[coordinates.length - 1][0] ?? scaleCoordinate(coordinates[coordinates.length - 1][1], extent)
...coordinates[coordinates.length - 1][0] ?? scaleCoordinate(
/** @type {Coordinate} */(coordinates[coordinates.length - 1][1]),
extent
)
);

@@ -45,7 +57,12 @@

// Test if the coordinates are coplanar by checking if the distance
// of each coordinate to the plane is less than a threshold.
// We don't need to do `coordinate -= distance to the plane`
// because earcut returns the same indices for small differences.
// (the indices of different objects are the same if they have the same shape)
/**
* Test if the coordinates are coplanar by checking if the distance
* of each coordinate to the plane is less than a threshold.
* We don't need to do `coordinate -= distance to the plane`
* because earcut returns the same indices for small differences.
* (the indices of different objects are the same if they have the same shape)
* @param {Array<[Coordinate, null] | [null, Coordinate]>} coordinates
* @param {import('../extent.js').Extent} extent
* @returns {boolean} whether the coordinates are coplanar.
*/
function isCoplanar(coordinates, extent) {

@@ -66,3 +83,6 @@ // normal = ⟨A, B, C⟩

for (let i = 0; i < coordinates.length; i++) {
const [x, y, z] = coordinates[i][0] ?? scaleCoordinate(coordinates[i][1], extent);
const [x, y, z] = coordinates[i][0] ?? scaleCoordinate(
/** @type {Coordinate} */(coordinates[i][1]),
extent
);

@@ -81,7 +101,10 @@ // Given a point ⟨x, y, z⟩, the distance between the point

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/polygon
// for the high-level description of what is being rendered.
export default function ({ color = [1, 1, 1], coords, edgeForm = {}, opacity = 1, vertexNormals = {} }, uniforms, extent) {
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/polygon}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, edgeForm = {}, opacity = 1, vertexNormals = [] }, uniforms, extent) {
let geometry;

@@ -105,12 +128,20 @@

// The good news is that it has a 2d mode.
// The really good news is that if we pass just pass the x and y
// values from the coordinates earcut returns the correct indices.
const quaternion = new Quaternion().setFromUnitVectors(
getNormalVector(coords, extent),
new Vector3(0, 0, 1)
);
const coordinates2d = new Float32Array(coords.length * 2);
for (let i = 0; i < coords.length; i++) {
coordinates2d[i * 2] = coords[i * 3];
coordinates2d[i * 2 + 1] = coords[i * 3 + 1];
}
coords.forEach((coordinate, index) => {
// apply the quaternion "zero" all z values, we can't draw a shape with non-zero z values
const vector = new Vector3(
...(coordinate[0] ?? scaleCoordinate(coordinate[1], extent))
).applyQuaternion(quaternion);
coordinates2d[index * 2] = vector.x;
coordinates2d[index * 2 + 1] = vector.y;
});
geometry = new BufferGeometry()

@@ -147,2 +178,4 @@ .setAttribute(

// may have a different normal value).
// @ts-expect-error: we already set the position attribute, so we are
// sure it is there.
const normals = new Float32Array(geometry.attributes.position.count * 3);

@@ -162,2 +195,3 @@

geometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -296,2 +330,3 @@ side: DoubleSide,

geometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -298,0 +333,0 @@ wireframe: true,

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -10,6 +12,9 @@ InstancedBufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/sphere
// for the high-level description of what is being rendered.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/sphere}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, opacity = 1, radius = 1 }, uniforms, extent) {

@@ -26,2 +31,3 @@ const sphereGeometry = getSphereGeometry(radius, true)

// @ts-expect-error: InstancedBufferGeometry have that attribute
sphereGeometry.instanceCount = coords.length;

@@ -31,2 +37,3 @@

sphereGeometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -33,0 +40,0 @@ transparent: opacity !== 1,

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -16,6 +18,9 @@ Matrix4,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/tube
// for the high-level description of what is being rendered.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/tube}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, opacity = 1, radius = 1 }, uniforms, extent) {

@@ -54,3 +59,10 @@ // We use getCentripetalCurve to convert an list of coordinates

curve.getPoint(1),
new Vector3(...(coords[coords.length - 2][0] ?? scaleCoordinate(coords[coords.length - 2][1], extent))),
new Vector3(
...(coords[coords.length - 2][0]
?? scaleCoordinate(
/** @type {import('../bufferUtils.js').Coordinate} */(coords[coords.length - 2][1]),
extent
)
)
),
new Vector3(1, 0, 0)

@@ -63,2 +75,3 @@ )

mergeBufferGeometries(geometries),
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -65,0 +78,0 @@ transparent: opacity !== 1,

@@ -0,1 +1,3 @@

// @ts-check
import {

@@ -13,6 +15,9 @@ BufferAttribute,

// See the comments from primitives/index.js for more information about the
// shape of a primitive function.
// See https://mathics3.github.io/mathics-threejs-backend/primitives/uniformPolyhedron
// for the high-level description of what is being rendered.
/**
* See {@link PrimitiveFunction} for more information about the
* shape of a primitive function.
* See {@link https://mathics3.github.io/mathics-threejs-backend/primitives/uniformPolyhedron}
* for the high-level description of what is being rendered.
* @type {import('./index.js').PrimitiveFunction}
*/
export default function ({ color = [1, 1, 1], coords, edgeForm = {}, edgeLength = 1, opacity = 1, subType }, uniforms, extent) {

@@ -355,2 +360,3 @@ const polyhedronGeometry = new InstancedBufferGeometry();

polyhedronGeometry,
// @ts-expect-error: bad three.js typing
new RawShaderMaterial({

@@ -357,0 +363,0 @@ transparent: opacity !== 1,

@@ -0,6 +1,14 @@

// @ts-check
import { RawShaderMaterial } from '../vendors/three.js';
// returns a material with a shader that uses 2 attributes:
// objectBegin and objectEnd.
// In the case of the cones, objectEnd must be the cone tip.
/**
* Create a material with a shader that uses 2 attributes:
* objectBegin and objectEnd.
* In the case of the cones, objectEnd must be the cone tip.
* @param {[number, number, number]} color
* @param {number} opacity
* @param {import('./uniforms.js').UniformsBuffer} uniforms
* @returns the created material
*/
export function get2CoordinatesMaterial(color, opacity, uniforms) {

@@ -138,4 +146,8 @@ return new RawShaderMaterial({

// returns a material that ignores lighting and
// has the same color for all its pixels.
/**
* @param {[number, number, number]} color
* @param {number} opacity
* @returns a material that ignores lighting so have all pixels
* of the same color.
*/
export function getBasicMaterial(color, opacity) {

@@ -142,0 +154,0 @@ return new RawShaderMaterial({

@@ -1,29 +0,38 @@

// TODO: convert it to a real uniforms buffer
// @ts-check
/**
* @typedef {{
* ambientLightColor: { value: [number, number, number] }
* directionalLights: { value: {
* color: [number, number, number],
* direction: import('../vendors/three.js').Vector3
* }[] },
* pointLights: { value: {
* color: [number, number, number],
* basePosition: import('../vendors/three.js').Vector3,
* position?: import('../vendors/three.js').Vector3
* }[] },
* spotLights: { value: {
* color: [number, number, number],
* baseDirection: import('../vendors/three.js').Vector3,
* basePosition: import('../vendors/three.js').Vector3,
* coneCos: number,
* direction?: import('../vendors/three.js').Vector3,
* position?: import('../vendors/three.js').Vector3
* }[] }
* }} UniformsBuffer
*/
/**
* Factory method for uniforms buffer.
* @todo convert this to a real uniforms buffer.
* @returns {UniformsBuffer}
*/
export function getUniformsBuffer() {
return {
ambientLightColor: { value: [0, 0, 0] },
directionalLights: {
value: [],
properties: {
color: {}, // vec3
direction: {} // vec3
}
},
pointLights: {
value: [],
properties: {
color: {}, // vec3
position: {} // vec3
}
},
spotLights: {
value: [],
properties: {
color: {}, // vec3
coneCos: {}, // float
direction: {}, // vec3
position: {} // vec3
}
}
directionalLights: { value: [] },
pointLights: { value: [] },
spotLights: { value: [] }
};
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc