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 0.1.0 to 0.2.0

.github/workflows/backstopjs.yml

4

package.json
{
"name": "@mathicsorg/mathics-threejs-backend",
"version": "0.1.0",
"description": "Mathics three.js backend for easily build 3d graphics",
"version": "0.2.0",
"description": "Mathics 3D Graphics backend using three.js",
"main": "src/index.js",

@@ -6,0 +6,0 @@ "repository": {

# mathics-threejs-backend
Example:
A node.js/Javascript library for rendering [Mathics](https://mathics.org) (and eventually Wolfram-Language) [Graphics3D](https://reference.wolfram.com/language/ref/Graphics3D.html) objects.
This can be used in Mathics front ends like [Mathics-Django](https://pypi.org/project/Mathics-Django/) to handle 3D Graphics. The code may also be useful as a guide for other kinds of Mathics/WL frontends to other kinds of Javascript graphics engines.
## Example:
```js

@@ -12,20 +16,14 @@ import drawGraphics3D from 'mathics-threejs-backend/src/index.js';

{
type: 'sphere',
coords: [[[0, 0, 0]]],
faceColor: [1, 1, 1]
type: 'Sphere',
Coords: [
[[0, 0, 0]]
],
RBGColor: [1, 1, 1]
}
],
axes: {},
extent: {
xmin: -1,
xmax: 1,
ymin: -1,
ymax: 1,
zmin: -1,
zmax: 1
},
lighting: [
{
type: 'Ambient',
color: [1, 1, 1]
RBGColor: [1, 1, 1]
}

@@ -32,0 +30,0 @@ ],

@@ -1,648 +0,5 @@

import {
BoxGeometry,
Color,
DirectionalLight,
EdgesGeometry,
Geometry,
Line,
LineBasicMaterial,
LineSegments,
Matrix4,
Mesh,
PerspectiveCamera,
Scene,
Vector2,
Vector3,
WebGLRenderer
} from '../vendors/threejs/three.min.js';
import drawGraphics3d from './graphics3d.js';
import primitiveFunctions from './primitives.js';
import lightFunctions from './lights.js';
window.drawGraphics3d = drawGraphics3d;
export default function (
container,
{ axes, elements, extent, lighting, viewpoint },
maxSize,
innerWidthMultiplier
) {
// TODO: add a mechanism to update the enclosing <mspace>
// TODO: shading, handling of VertexNormals
maxSize ||= 400;
innerWidthMultiplier ||= 0.6;
let isCtrlDown, isShiftDown, onMouseDownFocus, onCtrlDownFov;
let canvasSize = Math.min(maxSize, window.innerWidth * innerWidthMultiplier);
container.style.width = canvasSize + 'px';
// to avoid overflow when a tick numbers is out of the parent element
container.style.height = canvasSize + 10 + 'px';
let hasAxes, isMouseDown = false,
theta, onMouseDownTheta, phi, onMouseDownPhi;
// where the camera is looking (initialized on center of the scene)
const focus = new Vector3(
0.5 * (extent.xmin + extent.xmax),
0.5 * (extent.ymin + extent.ymax),
0.5 * (extent.zmin + extent.zmax)
);
const viewPoint = new Vector3(...viewpoint).sub(focus);
const radius = viewPoint.length();
onMouseDownTheta = theta = Math.acos(viewPoint.z / radius);
onMouseDownPhi = phi = (Math.atan2(viewPoint.y, viewPoint.x) + 2 * Math.PI) % (2 * Math.PI);
const scene = new Scene();
const camera = new PerspectiveCamera(
35, // field of view
1, // aspect ratio
0.1 * radius, // near plane
1000 * radius // far plane
);
function updateCameraPosition() {
camera.position.set(
radius * Math.sin(theta) * Math.cos(phi),
radius * Math.sin(theta) * Math.sin(phi),
radius * Math.cos(theta)
).add(focus);
camera.lookAt(focus);
}
updateCameraPosition();
camera.up.copy(new Vector3(0, 0, 1));
scene.add(camera);
function getInitialLightPosition(element) {
// initial light position in spherical polar coordinates
if (element.position instanceof Array) {
const temporaryPosition = new Vector3(...element.position);
const result = {
radius: radius * temporaryPosition.length(),
phi: 0,
theta: 0
};
if (temporaryPosition.lenght !== 0) {
result.phi = (Math.atan2(temporaryPosition.y, temporaryPosition.x) + 2 * Math.PI) % (2 * Math.PI);
result.theta = Math.asin(temporaryPosition.z / result.radius);
}
return result;
}
}
function positionLights() {
lights.forEach((light, i) => {
if (light instanceof DirectionalLight) {
light.position.set(
initialLightPosition[i].radius * Math.sin(theta + initialLightPosition[i].theta) * Math.cos(phi + initialLightPosition[i].phi),
initialLightPosition[i].radius * Math.sin(theta + initialLightPosition[i].theta) * Math.sin(phi + initialLightPosition[i].phi),
initialLightPosition[i].radius * Math.cos(theta + initialLightPosition[i].theta)
).add(focus);
}
});
}
const lights = new Array(lighting.length);
const initialLightPosition = new Array(lighting.length);
lighting.forEach((light, i) => {
initialLightPosition[i] = getInitialLightPosition(light);
lights[i] = lightFunctions[light.type](light, radius);
scene.add(lights[i]);
});
const boundingBox = new Mesh(new BoxGeometry(
extent.xmax - extent.xmin,
extent.ymax - extent.ymin,
extent.zmax - extent.zmin
));
boundingBox.position.copy(focus);
const boundingBoxEdges = new LineSegments(
new EdgesGeometry(boundingBox.geometry),
new LineBasicMaterial({ color: 0x666666 })
);
boundingBoxEdges.position.copy(focus);
scene.add(boundingBoxEdges);
// draw the axes
if (axes.hasaxes instanceof Array) {
hasAxes = new Array(axes.hasaxes[0], axes.hasaxes[1], axes.hasaxes[2]);
} else if (axes.hasaxes instanceof Boolean) {
if (axes) {
hasAxes = new Array(true, true, true);
} else {
hasAxes = new Array(false, false, false);
}
} else {
hasAxes = new Array(false, false, false);
}
const axesGeometry = [];
const axesIndexes = [
[[0, 5], [1, 4], [2, 7], [3, 6]],
[[0, 2], [1, 3], [4, 6], [5, 7]],
[[0, 1], [2, 3], [4, 5], [6, 7]]
];
const axesLines = new Array(3);
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
axesGeometry[i] = new Geometry();
axesGeometry[i].vertices.push(new Vector3().addVectors(
boundingBox.geometry.vertices[axesIndexes[i][0][0]], boundingBox.position
));
axesGeometry[i].vertices.push(new Vector3().addVectors(
boundingBox.geometry.vertices[axesIndexes[i][0][1]], boundingBox.position
));
axesLines[i] = new Line(
axesGeometry[i],
new LineBasicMaterial({
color: 0x000000,
linewidth: 1.5
})
);
scene.add(axesLines[i]);
}
}
function positionAxes() {
// automatic axes placement
let nearJ, nearLenght = 10 * radius, farJ, farLenght = 0;
const temporaryVector = new Vector3();
for (let i = 0; i < 8; i++) {
temporaryVector.addVectors(
boundingBox.geometry.vertices[i],
boundingBox.position
).sub(camera.position);
const temporaryLenght = temporaryVector.length();
if (temporaryLenght < nearLenght) {
nearLenght = temporaryLenght;
nearJ = i;
} else if (temporaryLenght > farLenght) {
farLenght = temporaryLenght;
farJ = i;
}
}
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
let maxJ, maxLenght = 0;
for (let j = 0; j < 4; j++) {
if (axesIndexes[i][j][0] !== nearJ &&
axesIndexes[i][j][1] !== nearJ &&
axesIndexes[i][j][0] !== farJ &&
axesIndexes[i][j][1] !== farJ
) {
const edge = new Vector3().subVectors(
toCanvasCoords(boundingBox.geometry.vertices[axesIndexes[i][j][0]]),
toCanvasCoords(boundingBox.geometry.vertices[axesIndexes[i][j][1]])
);
edge.z = 0;
if (edge.length() > maxLenght) {
maxLenght = edge.length();
maxJ = j;
}
}
}
axesLines[i].geometry.vertices[0].addVectors(
boundingBox.geometry.vertices[axesIndexes[i][maxJ][0]],
boundingBox.position
);
axesLines[i].geometry.vertices[1].addVectors(
boundingBox.geometry.vertices[axesIndexes[i][maxJ][1]],
boundingBox.position
);
axesLines[i].geometry.verticesNeedUpdate = true;
}
}
updateAxes();
}
// axes ticks
const tickMaterial = new LineBasicMaterial({
color: 0x000000,
linewidth: 1.2
});
const ticks = new Array(3),
ticksSmall = new Array(3),
tickLength = 0.005 * radius;
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
ticks[i] = [];
for (let j = 0; j < axes.ticks[i][0].length; j++) {
const tickGeometry = new Geometry();
tickGeometry.vertices.push(new Vector3());
tickGeometry.vertices.push(new Vector3());
ticks[i].push(new Line(tickGeometry, tickMaterial));
scene.add(ticks[i][j]);
}
ticksSmall[i] = [];
for (let j = 0; j < axes.ticks[i][1].length; j++) {
const tickGeometry = new Geometry();
tickGeometry.vertices.push(new Vector3());
tickGeometry.vertices.push(new Vector3());
ticksSmall[i].push(new Line(tickGeometry, tickMaterial));
scene.add(ticksSmall[i][j]);
}
}
}
function getTickDir(i) {
const tickDir = new Vector3();
if (i === 0) {
if (0.25 * Math.PI < theta && theta < 0.75 * Math.PI) {
if (axesGeometry[0].vertices[0].z > boundingBox.position.z) {
tickDir.setZ(-tickLength);
} else {
tickDir.setZ(tickLength);
}
} else {
if (axesGeometry[0].vertices[0].y > boundingBox.position.y) {
tickDir.setY(-tickLength);
} else {
tickDir.setY(tickLength);
}
}
} else if (i === 1) {
if (0.25 * Math.PI < theta && theta < 0.75 * Math.PI) {
if (axesGeometry[1].vertices[0].z > boundingBox.position.z) {
tickDir.setZ(-tickLength);
} else {
tickDir.setZ(tickLength);
}
} else {
if (axesGeometry[1].vertices[0].x > boundingBox.position.x) {
tickDir.setX(-tickLength);
} else {
tickDir.setX(tickLength);
}
}
} else if (i === 2) {
if ((0.25 * Math.PI < phi && phi < 0.75 * Math.PI) || (1.25 * Math.PI < phi && phi < 1.75 * Math.PI)) {
if (axesGeometry[2].vertices[0].x > boundingBox.position.x) {
tickDir.setX(-tickLength);
} else {
tickDir.setX(tickLength);
}
} else {
if (axesGeometry[2].vertices[0].y > boundingBox.position.y) {
tickDir.setY(-tickLength);
} else {
tickDir.setY(tickLength);
}
}
}
return tickDir;
}
function updateAxes() {
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
let tickDir = getTickDir(i);
for (let j = 0; j < axes.ticks[i][0].length; j++) {
let value = axes.ticks[i][0][j];
ticks[i][j].geometry.vertices[0].copy(axesGeometry[i].vertices[0]);
ticks[i][j].geometry.vertices[1].addVectors(
axesGeometry[i].vertices[0],
tickDir
);
if (i === 0) {
ticks[i][j].geometry.vertices[0].x = value;
ticks[i][j].geometry.vertices[1].x = value;
} else if (i === 1) {
ticks[i][j].geometry.vertices[0].y = value;
ticks[i][j].geometry.vertices[1].y = value;
} else if (i === 2) {
ticks[i][j].geometry.vertices[0].z = value;
ticks[i][j].geometry.vertices[1].z = value;
}
ticks[i][j].geometry.verticesNeedUpdate = true;
}
for (let j = 0; j < axes.ticks[i][1].length; j++) {
let value = axes.ticks[i][1][j];
ticksSmall[i][j].geometry.vertices[0].copy(axesGeometry[i].vertices[0]);
ticksSmall[i][j].geometry.vertices[1].addVectors(
axesGeometry[i].vertices[0],
tickDir.clone().multiplyScalar(0.5)
);
if (i === 0) {
ticksSmall[i][j].geometry.vertices[0].x = value;
ticksSmall[i][j].geometry.vertices[1].x = value;
} else if (i === 1) {
ticksSmall[i][j].geometry.vertices[0].y = value;
ticksSmall[i][j].geometry.vertices[1].y = value;
} else if (i === 2) {
ticksSmall[i][j].geometry.vertices[0].z = value;
ticksSmall[i][j].geometry.vertices[1].z = value;
}
ticksSmall[i][j].geometry.verticesNeedUpdate = true;
}
}
}
}
updateAxes();
// axes numbering using divs
const tickNumbers = new Array(3);
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
tickNumbers[i] = new Array(axes.ticks[i][0].length);
for (let j = 0; j < tickNumbers[i].length; j++) {
let color = 'black';
if (i < axes.ticks_style.length) {
color = new Color(...axes.ticks_style[i]).getStyle();
}
tickNumbers[i][j] = document.createElement('div');
tickNumbers[i][j].innerHTML = axes.ticks[i][2][j]
.replace('0.', '.');
// handle minus signs
if (axes.ticks[i][0][j] >= 0) {
tickNumbers[i][j].style.paddingLeft = '0.5em';
} else {
tickNumbers[i][j].style.paddingLeft = 0;
}
tickNumbers[i][j].style.position = 'absolute';
tickNumbers[i][j].style.fontSize = '0.8em';
tickNumbers[i][j].style.color = color;
container.appendChild(tickNumbers[i][j]);
}
}
}
function toCanvasCoords(position) {
const temporaryPosition = position.clone().applyMatrix4(
new Matrix4().multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
)
);
return new Vector3(
(temporaryPosition.x + 1) * 200,
(1 - temporaryPosition.y) * 200,
(temporaryPosition.z + 1) * 200
);
}
function positionTickNumbers() {
for (let i = 0; i < 3; i++) {
if (hasAxes[i]) {
for (let j = 0; j < tickNumbers[i].length; j++) {
const tickPosition = toCanvasCoords(
ticks[i][j].geometry.vertices[0].clone().add(
new Vector3().subVectors(
ticks[i][j].geometry.vertices[0],
ticks[i][j].geometry.vertices[1]
).multiplyScalar(6)
)
).multiplyScalar(canvasSize / maxSize);
// distance of the bounding box
tickPosition.setX(tickPosition.x - 10);
tickPosition.setY(tickPosition.y + 8);
tickNumbers[i][j].style.position = `absolute`;
tickNumbers[i][j].style.left = `${tickPosition.x}px`;
tickNumbers[i][j].style.top = `${tickPosition.y}px`;
if (tickPosition.x < 5 || tickPosition.x > 395 || tickPosition.y < 5 || tickPosition.y > 395) {
tickNumbers[i][j].style.display = 'none';
}
else {
tickNumbers[i][j].style.display = '';
}
}
}
}
}
// plot the primatives
elements.forEach((element) => {
scene.add(primitiveFunctions[element.type](element, canvasSize));
});
// renderer (set preserveDrawingBuffer to deal with issue of weird canvas content after switching windows)
const renderer = new WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true,
alpha: true
});
renderer.setSize(canvasSize, canvasSize);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
function render() {
positionLights();
renderer.render(scene, camera);
}
function scaleInView() {
const proj2d = new Vector3();
let temporaryFOV = 0;
for (let i = 0; i < 8; i++) {
proj2d.addVectors(
boundingBox.geometry.vertices[i],
boundingBox.position
).applyMatrix4(camera.matrixWorldInverse);
temporaryFOV = Math.max(
temporaryFOV,
114.59 * Math.max(
Math.abs(Math.atan(proj2d.x / proj2d.z) / camera.aspect),
Math.abs(Math.atan(proj2d.y / proj2d.z))
)
);
}
camera.fov = temporaryFOV + 5;
camera.updateProjectionMatrix();
}
function onDocumentMouseDown(event) {
event.preventDefault();
isMouseDown = true;
isShiftDown = false;
isCtrlDown = false;
onMouseDownTheta = theta;
onMouseDownPhi = phi;
onMouseDownPosition.x = event.clientX;
onMouseDownPosition.y = event.clientY;
onMouseDownFocus = new Vector3().copy(focus);
}
function onDocumentMouseMove(event) {
event.preventDefault();
if (isMouseDown) {
positionTickNumbers();
if (event.shiftKey) { // pan
if (!isShiftDown) {
isShiftDown = true;
onMouseDownPosition.x = event.clientX;
onMouseDownPosition.y = event.clientY;
autoRescale = false;
container.style.cursor = 'move';
}
const cameraX = new Vector3(
- radius * Math.cos(theta) * Math.sin(phi) * (theta < 0.5 * Math.PI ? 1 : -1),
radius * Math.cos(theta) * Math.cos(phi) * (theta < 0.5 * Math.PI ? 1 : -1),
0
).normalize();
const cameraY = new Vector3().crossVectors(
new Vector3()
.subVectors(focus, camera.position)
.normalize(),
cameraX
);
focus.x = onMouseDownFocus.x + (radius / canvasSize) * (cameraX.x * (onMouseDownPosition.x - event.clientX) + cameraY.x * (onMouseDownPosition.y - event.clientY));
focus.y = onMouseDownFocus.y + (radius / canvasSize) * (cameraX.y * (onMouseDownPosition.x - event.clientX) + cameraY.y * (onMouseDownPosition.y - event.clientY));
focus.z = onMouseDownFocus.z + (radius / canvasSize) * (cameraY.z * (onMouseDownPosition.y - event.clientY));
updateCameraPosition();
} else if (event.ctrlKey) { // zoom
if (!isCtrlDown) {
isCtrlDown = true;
onCtrlDownFov = camera.fov;
onMouseDownPosition.x = event.clientX;
onMouseDownPosition.y = event.clientY;
autoRescale = false;
container.style.cursor = 'crosshair';
}
camera.fov = Math.max(
1,
Math.min(
onCtrlDownFov + 20 * Math.atan((event.clientY - onMouseDownPosition.y) / 50),
150
)
);
camera.updateProjectionMatrix();
} else { // spin
if (isCtrlDown || isShiftDown) {
onMouseDownPosition.x = event.clientX;
onMouseDownPosition.y = event.clientY;
isShiftDown = false;
isCtrlDown = false;
container.style.cursor = 'pointer';
}
phi = 2 * Math.PI * (onMouseDownPosition.x - event.clientX) / canvasSize + onMouseDownPhi;
phi = (phi + 2 * Math.PI) % (2 * Math.PI);
theta = 2 * Math.PI * (onMouseDownPosition.y - event.clientY) / canvasSize + onMouseDownTheta;
const epsilon = 1e-12; // prevents spinnging from getting stuck
theta = Math.max(Math.min(Math.PI - epsilon, theta), epsilon);
updateCameraPosition();
}
render();
} else {
container.style.cursor = 'pointer';
}
}
function onDocumentMouseUp(event) {
event.preventDefault();
isMouseDown = false;
container.style.cursor = 'pointer';
if (autoRescale) {
scaleInView();
render();
}
positionAxes();
render();
positionTickNumbers();
}
// bind mouse events
container.addEventListener('mousemove', onDocumentMouseMove);
container.addEventListener('mousedown', onDocumentMouseDown);
container.addEventListener('mouseup', onDocumentMouseUp);
window.addEventListener('resize', () => {
canvasSize = Math.min(maxSize, window.innerWidth * innerWidthMultiplier);
container.style.width = canvasSize + 'px';
// to avoid overflow when a tick numbers is out of the parent element
container.style.height = canvasSize + 10 + 'px';
renderer.setSize(canvasSize, canvasSize);
renderer.setPixelRatio(window.devicePixelRatio);
positionTickNumbers();
});
const onMouseDownPosition = new Vector2();
let autoRescale = true;
updateCameraPosition();
positionAxes();
render(); // rendering twice updates camera.matrixWorldInverse so that scaleInView works properly
scaleInView();
render();
positionTickNumbers();
}
export default drawGraphics3d;

@@ -13,29 +13,34 @@ import {

import scaleCoordinate from './scaleCoordinate.js';
export default {
ambient: ({ color }) => {
return new AmbientLight(new Color(...color).getHex());
Ambient: ({ RGBColor }) => {
return new AmbientLight(new Color(...RGBColor).getHex());
},
directional: ({ color }) => {
return new DirectionalLight(new Color(...color).getHex(), 1);
Directional: ({ RGBColor }) => {
return new DirectionalLight(new Color(...RGBColor).getHex(), 1);
},
spot: ({ angle, color, position, target }) => {
const group = new Group();
Spot: ({ Angle, Coords, RGBColor, Target }, extent) => {
const light = new SpotLight(new Color(...RGBColor).getHex());
light.position.set(
...(Coords[0] ?? scaleCoordinate(Coords[1], extent))
);
light.angle = Angle;
const light = new SpotLight(new Color(...color).getHex());
light.position.set(...position);
light.angle = angle;
group.add(light);
light.target.position.set(
...(Target[0] ?? scaleCoordinate(Target[1], extent))
);
light.target.updateMatrixWorld();
light.target.position.set(...target);
group.add(light.target);
return group;
return light;
},
point: ({ color, position }, radius) => {
Point: ({ Coords, RGBColor }, extent, radius) => {
const group = new Group();
const colorHex = new Color(...color).getHex();
const color = new Color(...RGBColor).getHex();
const light = new PointLight(colorHex);
light.position.set(...position);
const light = new PointLight(color);
light.position.set(
...(Coords[0] ?? scaleCoordinate(Coords[1], extent))
);
group.add(light);

@@ -46,3 +51,3 @@

new SphereGeometry(0.007 * radius, 16, 8),
new MeshBasicMaterial({ color: colorHex })
new MeshBasicMaterial({ color })
);

@@ -49,0 +54,0 @@ lightSphere.position.copy(light.position);

import {
BoxGeometry,
BoxBufferGeometry,
BufferAttribute,
BufferGeometry,
Color,
CylinderGeometry,
CylinderBufferGeometry,
DoubleSide,
Face3,
Geometry,
Group,

@@ -15,8 +14,10 @@ InstancedMesh,

Mesh,
MeshBasicMaterial,
MeshLambertMaterial,
Points,
Quaternion,
ShaderLib,
ShaderMaterial,
SphereGeometry,
Shape,
ShapeGeometry,
SphereBufferGeometry,
Vector3,

@@ -27,47 +28,68 @@ Vector4

import earcut from '../vendors/earcut/earcut.min.js';
import scaleCoordinate from './scaleCoordinate.js';
// TODO: the one-element arrays should be two-element arrays, where the 2nd element is the "scaled" part of the coordinates that depend on the size of the final graphics (see Mathematica's Scaled)
export default {
arrow: ({ color, coords }) => {
const group = new THREE.Group();
Arrow: ({ Coords, Opacity, RGBColor }, extent) => {
const group = new Group();
const colorHex = new THREE.Color(...color).getHex();
const color = new Color(...RGBColor).getHex();
const startCoordinate = new THREE.Vector3(
...coords[coords.length - 2][0]
const startCoordinate = new Vector3(
...(Coords[Coords.length - 2][0] ?? scaleCoordinate(Coords[Coords.length - 2][1], extent))
);
const endCoordinate = new THREE.Vector3(
...coords[coords.length - 1][0]
const endCoordinate = new Vector3(
...(Coords[Coords.length - 1][0] ?? scaleCoordinate(Coords[Coords.length - 1][1], extent))
);
group.add(
new THREE.ArrowHelper(
endCoordinate.clone().sub(startCoordinate).normalize(),
startCoordinate,
startCoordinate.distanceTo(endCoordinate),
colorHex
)
const arrowHead = new Mesh(
new CylinderBufferGeometry(
0,
0.04 * startCoordinate.distanceTo(endCoordinate),
0.2 * startCoordinate.distanceTo(endCoordinate)
).rotateX(Math.PI / 2),
new MeshBasicMaterial({
color,
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1
})
);
const points = new Float32Array(coords.length * 3 - 3);
// set the position to 1/10 far from the end coordinate so lookAt work
arrowHead.position.copy(
endCoordinate.clone()
.multiplyScalar(9)
.add(startCoordinate)
.multiplyScalar(0.1)
);
arrowHead.lookAt(endCoordinate);
group.add(arrowHead);
const points = new Float32Array(Coords.length * 3);
for (let i = 0; i < points.length / 3; i++) {
points[i * 3] = coords[i][0][0];
points[i * 3 + 1] = coords[i][0][1];
points[i * 3 + 2] = coords[i][0][2];
Coords[i][0] ??= scaleCoordinate(Coords[i][1], extent);
points[i * 3] = Coords[i][0][0];
points[i * 3 + 1] = Coords[i][0][1];
points[i * 3 + 2] = Coords[i][0][2];
}
const linesGeometry = new THREE.BufferGeometry();
const linesGeometry = new BufferGeometry();
linesGeometry.setAttribute(
'position',
new THREE.BufferAttribute(points, 3)
new BufferAttribute(points, 3)
);
group.add(
new THREE.Line(
new Line(
linesGeometry,
new THREE.LineBasicMaterial({ color: colorHex })
new LineBasicMaterial({
color,
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1
})
)

@@ -78,30 +100,49 @@ );

},
cube: ({ color, position, size }) => {
const cube = new Mesh(
new BoxGeometry(...size[0]),
new MeshLambertMaterial({
color: new Color(...color).getHex()
})
);
Cuboid: ({ Coords, Opacity, RGBColor }, extent) => {
const group = new Group();
cube.position.set(...position[0]);
for (let i = 0; i < Coords.length / 2; i++) {
const startCoordinate = new Vector3(
...(Coords[i * 2][0] ?? scaleCoordinate(Coords[i * 2][1], extent))
);
const endCoordinate = new Vector3(
...(Coords[i * 2 + 1][0] ?? scaleCoordinate(Coords[i * 2 + 1][1], extent))
);
return cube;
const cuboid = new Mesh(
new BoxBufferGeometry(
...endCoordinate.clone().sub(startCoordinate).toArray()
),
new MeshLambertMaterial({
color: new Color(...RGBColor).getHex(),
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1
})
);
// mean of the start and end coordinates, the center of the cuboid
cuboid.position.copy(
startCoordinate.add(endCoordinate).multiplyScalar(0.5)
);
group.add(cuboid);
}
return group;
},
cylinder: ({ coords, color, radius }) => {
Cylinder: ({ Coords, Opacity, Radius, RGBColor }, extent) => {
const group = new Group();
for (let i = 0; i < coords.length / 2; i++) {
for (let i = 0; i < Coords.length / 2; i++) {
const startCoordinate = new Vector3(
...coords[i * 2][0]
...(Coords[i * 2][0] ?? scaleCoordinate(Coords[i * 2][1], extent))
);
const endCoordinate = new Vector3(
...coords[i * 2 + 1][0]
...(Coords[i * 2 + 1][0] ?? scaleCoordinate(Coords[i * 2][1], extent))
);
const cylinder = new Mesh(
new CylinderGeometry(
radius,
radius,
new CylinderBufferGeometry(
Radius,
Radius,
startCoordinate.distanceTo(endCoordinate), // the height of the cylinder

@@ -114,7 +155,9 @@ 24

new MeshLambertMaterial({
color: new Color(...color).getHex()
color: new Color(...RGBColor).getHex(),
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1
})
);
// mean of the start and end vectors, the center of the cylinder
// mean of the start and end coordinates, the center of the cylinder
cylinder.position.addVectors(startCoordinate, endCoordinate)

@@ -130,21 +173,40 @@ .multiplyScalar(0.5);

},
line: ({ color, coords }) => {
Line: ({ Coords, Opacity, RGBColor }, extent) => {
const geometry = new BufferGeometry();
const points = new Float32Array(Coords.length * 3);
Coords.forEach((coordinate, i) => {
coordinate[0] ??= scaleCoordinate(coordinate[1], extent);
points[i * 3] = coordinate[0][0];
points[i * 3 + 1] = coordinate[0][1];
points[i * 3 + 2] = coordinate[0][2];
});
geometry.setAttribute('position', new BufferAttribute(points, 3));
return new Line(
new BufferGeometry().setFromPoints(
coords.map(
(coordinate) => new Vector3(...coordinate[0])
)
),
geometry,
new LineBasicMaterial({
color: new Color(...color).getHex()
color: new Color(...RGBColor).getHex(),
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1
})
);
},
point: ({ color, coords, pointSize }, canvasSize) => {
const geometry = new Geometry();
Point: ({ Coords, Opacity, PointSize, RGBColor }, extent, canvasSize) => {
const geometry = new BufferGeometry();
geometry.vertices = coords.map(
(coordinate) => new Vector3(...coordinate[0])
);
const points = new Float32Array(Coords.length * 3);
Coords.forEach((coordinate, i) => {
coordinate[0] ??= scaleCoordinate(coordinate[1], extent);
points[i * 3] = coordinate[0][0];
points[i * 3 + 1] = coordinate[0][1];
points[i * 3 + 2] = coordinate[0][2];
});
geometry.setAttribute('position', new BufferAttribute(points, 3));
return new Points(

@@ -155,6 +217,15 @@ geometry,

uniforms: {
size: { value: pointSize * canvasSize * 0.5 },
color: { value: new Vector4(...color, 1) },
size: { value: PointSize * canvasSize * 0.5 },
color: { value: new Vector4(...RGBColor, Opacity) },
},
vertexShader: ShaderLib.points.vertexShader,
vertexShader: `
uniform float size;
void main() {
#include <begin_vertex>
#include <project_vertex>
gl_PointSize = size;
}
`,
fragmentShader: `

@@ -172,13 +243,18 @@ uniform vec4 color;

},
polygon: ({ coords, color }) => {
Polygon: ({ Coords, Opacity, RGBColor }, extent) => {
let geometry;
if (coords.length === 3) { // triangle
geometry = new Geometry();
if (Coords.length === 3) { // triangle
geometry = new BufferGeometry();
geometry.vertices = coords.map(
(coordinate) => new Vector3(...coordinate[0])
geometry.setAttribute(
'position',
new BufferAttribute(new Float32Array([
...(Coords[0][0] ?? scaleCoordinate(Coords[0][1], extent)),
...(Coords[1][0] ?? scaleCoordinate(Coords[1][1], extent)),
...(Coords[2][0] ?? scaleCoordinate(Coords[2][1], extent))
]), 3)
);
geometry.faces.push(new Face3(0, 1, 2));
geometry.computeVertexNormals();
} else {

@@ -188,10 +264,12 @@ // boolean variables

coords.forEach((coordinate) => {
if (coordinate[0][0] !== coords[0][0][0]) {
Coords.forEach((coordinate) => {
coordinate[0] ??= scaleCoordinate(coordinate[1], extent);
if (coordinate[0][0] !== Coords[0][0][0]) {
isXCoplanar = 0;
}
if (coordinate[0][1] !== coords[0][0][1]) {
if (coordinate[0][1] !== Coords[0][0][1]) {
isYCoplanar = 0;
}
if (coordinate[0][2] !== coords[0][0][2]) {
if (coordinate[0][2] !== Coords[0][0][2]) {
isZCoplanar = 0;

@@ -210,16 +288,15 @@ }

const points = coords.map((coordinate) =>
new Vector3(...coordinate[0])
.applyQuaternion(
new Quaternion().setFromUnitVectors(
normalVector,
normalZVector
)
const points = Coords.map((coordinate) =>
new Vector3(
...(coordinate[0] ?? scaleCoordinate(coordinate[1], extent))
).applyQuaternion(
new Quaternion().setFromUnitVectors(
normalVector,
normalZVector
)
)
);
const polygonShape = new Shape(points);
geometry = new ShapeGeometry(new Shape(points));
geometry = new ShapeGeometry(polygonShape);
geometry.vertices = geometry.vertices.map(

@@ -233,45 +310,52 @@ (vertex) => vertex.applyQuaternion(

);
geometry.computeFaceNormals();
} else {
geometry = new Geometry();
geometry = new BufferGeometry();
const coordinates = [];
const coordinates = new Float32Array(Coords.length * 3);
coords.forEach((coordinate) => {
coordinates.push(...coordinate[0]);
geometry.vertices.push(new Vector3(...coordinate[0]));
Coords.forEach((coordinate, i) => {
coordinate[0] ??= scaleCoordinate(coordinate[1], extent);
coordinates[i * 3] = coordinate[0][0];
coordinates[i * 3 + 1] = coordinate[0][1];
coordinates[i * 3 + 2] = coordinate[0][2];
});
const triangles = earcut(coordinates, null, 3);
geometry.setAttribute(
'position',
new BufferAttribute(coordinates, 3)
);
for (let i = 0; i < triangles.length; i += 3) {
geometry.faces.push(new Face3(
triangles[i],
triangles[i + 1],
triangles[i + 2]
));
}
geometry.setIndex(earcut(coordinates));
geometry.computeVertexNormals();
}
};
geometry.computeFaceNormals();
return new Mesh(geometry, new MeshLambertMaterial({
color: new Color(...color).getHex(),
color: new Color(...RGBColor).getHex(),
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1,
side: DoubleSide
}));
},
sphere: ({ coords, color, radius }) => {
Sphere: ({ Coords, Opacity, Radius, RGBColor }, extent) => {
const spheres = new InstancedMesh(
new SphereGeometry(radius, 48, 48),
new SphereBufferGeometry(Radius, 48, 48),
new MeshLambertMaterial({
color: new Color(...color).getHex()
color: new Color(...RGBColor).getHex(),
opacity: Opacity ?? 1,
transparent: (Opacity ?? 1) !== 1
}),
coords.length
Coords.length
);
coords.forEach((coordinate, i) => spheres.setMatrixAt(
i,
new Matrix4()
.setPosition(new Vector3(...coordinate[0]))
));
Coords.forEach((coordinate, i) =>
spheres.setMatrixAt(
i,
new Matrix4().setPosition(...(coordinate[0] ?? scaleCoordinate(coordinate[1], extent)))
)
);

@@ -278,0 +362,0 @@ return spheres;

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