Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .minify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"js": {
"type": "esbuild"
}
}
4,942 changes: 4,936 additions & 6 deletions docs/build.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The main function of mathics-threejs-backend is `drawGraphics3d`, takes the foll
- `hasaxes` (type: bool\|bool[3]) — default: `false`
- `ticks` (type: [number[], number[], string[]][3]) — array containing the ticks' information for, respectively, x, y and z axes. The ticks' information is an array of three elements: big ticks' 0-1 coordinates, small ticks' 0-1 coordinates, big ticks' labels. Default: `[]`
- `ticks_style` (type: [color[3]](/mathics-threejs-backend/types/color)) — array containing the ticks' colors for, respectively, x, y and z axes. Default: `[[0, 0, 0], [0, 0, 0], [0, 0, 0]]` (all ticks are black)
- `elements` (type: [element[]](/mathics-threejs-backend/types/color) — array of primitives, default: `[]`
- `elements` (type: [element[]](/mathics-threejs-backend/types/color) — array of primitives, default: `[]`)
- `extent` (type: object) — the size of the bounding box. This is optional, only use this if you don't want the default behavior. This is an object with the following properties:
- `xmin` (type: number)
- `ymin` (type: number)
Expand Down
6 changes: 4 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module.exports = {
preset: 'jest-puppeteer',
transform: { '^.+\\.js$': 'babel-jest' }
maxWorkers: 2,
testEnvironment: 'node',
transform: { '^.+\\.js$': 'babel-jest' },
watchman: false
};
18 changes: 15 additions & 3 deletions jest.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@ import { toMatchImageSnapshot } from 'jest-image-snapshot';

expect.extend({ toMatchImageSnapshot });

const browserLaunchOptions = {
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding'
]
};

export function testScreenshot(name, file) {
test(name, async () => {
const browser = await puppeteer.launch();
const browser = await puppeteer.launch(browserLaunchOptions);

try {
const page = await browser.newPage();
await page.goto(`http://localhost:8080/tests/${file}`);
const image = await page.screenshot();

expect(image).toMatchImageSnapshot({
customSnapshotsDir: resolve(__dirname, 'reference_images')
customSnapshotsDir: resolve(__dirname, 'reference_images'),
failureThreshold: 0.05,
failureThresholdType: 'percent'
});

await browser.close();
Expand All @@ -23,5 +35,5 @@ export function testScreenshot(name, file) {

throw error;
}
});
}, 30000);
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@mathicsorg/mathics-threejs-backend",
"version": "1.3.3",
"threejs_revision": 146,
"threejs_revision": 184,
"description": "Mathics 3D Graphics backend using three.js",
"source": "src/index.js",
"main": "docs/build.js",
Expand Down Expand Up @@ -32,17 +32,17 @@
"test": "(npm start &) && eslint src && tsc && jest src && fuser -k 8080/tcp"
},
"dependencies": {
"express": "^4"
"express": "^5"
},
"devDependencies": {
"@babel/preset-env": "^7",
"eslint": "^8",
"eslint-plugin-jest": "^27",
"jest": "^29",
"eslint-plugin-jest": "^29",
"jest": "^30",
"jest-image-snapshot": "^6",
"jest-puppeteer": "^11",
"minify": "^9",
"rollup": "^3",
"typescript": "^4"
"minify": "^15",
"rollup": "^4",
"typescript": "^6"
}
}
Binary file modified reference_images/axes-test-js-test-scene-with-axes-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified reference_images/polygon-test-js-test-polygon-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified reference_images/text-test-js-test-text-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified reference_images/tube-test-js-test-tube-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 0 additions & 35 deletions src/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,14 @@ export function positionTickNumbers(
// The code bellow moves the tick numbers so they aren't
// over the tick marks.
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,

// tickDirection.z is always 0, so
// position.array[j * 6] is the same as
// position.array[j * 6 + 5].
// x * 7 - x * 6 = x
// @ts-expect-error: same as above
ticks[i].geometry.attributes.position.array[j * 6 + 2]
),
camera,
Expand Down Expand Up @@ -146,39 +143,23 @@ export function setTicksInitialPosition(
);

// 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;
}
});
Expand All @@ -191,39 +172,23 @@ export function setTicksInitialPosition(
);

// 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;
}
});
Expand Down
6 changes: 3 additions & 3 deletions src/curve.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,13 @@ export function getCentripetalCurve(coordinates, extent) {
* @param {number} u
*/
function getUtoTmapping(u) {
let i = 0;

// the targeted u distance value to get
const targetArcLength = u * arcLengths[arcLengths.length - 1];

// Binary search for the index with largest value
// smaller than target u distance.

let low = 0, high = arcLengths.length - 1, comparison;
let i, low = 0, high = arcLengths.length - 1, comparison;

while (low <= high) {
i = Math.floor((low + high) / 2);
Expand All @@ -146,6 +144,8 @@ export function getCentripetalCurve(coordinates, extent) {
}
}

i = high;

if (arcLengths[i] === targetArcLength) {
return i / (arcLengths.length - 1);
}
Expand Down
20 changes: 12 additions & 8 deletions src/geometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,21 @@ export function mergeBufferGeometries(geometries) {
for (const name in geometries[i].attributes) {
if (attributes[name] === undefined) attributes[name] = [];

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

const geometryIndex = geometries[i].index;

if (geometryIndex === null) {
throw new Error('mergeBufferGeometries expects indexed geometries.');
}

for (let j = 0; j < geometries[i].index.count; ++j) {
mergedIndex.push(geometries[i].index.getX(j) + indexOffset);
for (let j = 0; j < geometryIndex.count; ++j) {
mergedIndex.push(geometryIndex.getX(j) + indexOffset);
}

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

Expand Down Expand Up @@ -149,7 +153,7 @@ export function getSphereGeometry(radius, instanced = false, halfSphere = false)
// TODO: add cache
/**
* @param {number} radius
* @param {ReturnType<import('curve.js').getCentripetalCurve>} path
* @param {ReturnType<import('./curve.js').getCentripetalCurve>} path
* @returns {BufferGeometry} the geometry of a tube passing through the path.
*/
export function getTubeGeometry(radius, path) {
Expand Down
28 changes: 13 additions & 15 deletions src/graphics3d.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import { getUniformsBuffer } from './uniforms.js';
* @typedef {import('./index.js').Axes} Axes
* @typedef {import('./index.js').ConcreteAxes} ConcreteAxes
* @typedef {import('./index.js').TicksStyle} TicksStyle
* @typedef {PerspectiveCamera & {
* position: import('../vendors/three.js').Vector3
* }} GraphicsCamera
*/

/**
Expand Down Expand Up @@ -132,15 +135,14 @@ export default function (
phi = Math.atan2(viewPoint.y, viewPoint.x);

const scene = new Scene(),
camera = new PerspectiveCamera(
camera = /** @type {GraphicsCamera} */(new PerspectiveCamera(
35, // field of view
1, // aspect ratio
0.1 * radius, // near plane
1000 * radius // far plane
);
));

function updateCameraPosition() {
// @ts-expect-error: bad three.js typing
camera.position.set(
radius * Math.sin(theta) * Math.cos(phi),
radius * Math.sin(theta) * Math.sin(phi),
Expand Down Expand Up @@ -211,7 +213,7 @@ export default function (
grayBasicMaterial
);

scene.add(boundingBox);
scene.add(/** @type {import('../vendors/three.js').Object3D} **/(/**@type {unknown}**/(boundingBox)));

// draw the axes
if (axes.hasaxes instanceof Array) {
Expand Down Expand Up @@ -239,7 +241,7 @@ export default function (
grayBasicMaterial
);

scene.add(ticks[i]);
scene.add(/** @type {import('../vendors/three.js').Object3D} **/(/**@type {unknown}**/(ticks[i])));

ticksSmall[i] = new LineSegments(
new BufferGeometry().setAttribute(
Expand All @@ -254,7 +256,7 @@ export default function (
grayBasicMaterial
);

scene.add(ticksSmall[i]);
scene.add(/** @type {import('../vendors/three.js').Object3D} **/(/**@type {unknown}**/(ticksSmall[i])));
}
}

Expand All @@ -263,7 +265,6 @@ export default function (
/** @type {ConcreteAxes} */(axes),
ticks,
ticksSmall,
// @ts-expect-error: we are sure this attribute is in there
boundingBox.geometry.attributes.position.array,
radius,
extent
Expand Down Expand Up @@ -321,19 +322,20 @@ export default function (
);
// Primitives like https://mathics3.github.io/mathics-threejs-backend/primitives/text
// don't add anything to the scene, but to the container.
if (sceneElement) scene.add(sceneElement);
if (sceneElement) scene.add(/** @type {import('../vendors/three.js').Object3D} **/(/**@type {unknown}**/(sceneElement)));
});

const renderer = new WebGLRenderer({
antialias: true,
alpha: true
});
const canvas = /** @type {HTMLCanvasElement} */(renderer.domElement);

renderer.setSize(containerSize.width, containerSize.height);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.domElement.style.width = '100%';
renderer.domElement.style.height = '100%';
container.appendChild(renderer.domElement);
canvas.style.width = '100%';
canvas.style.height = '100%';
container.appendChild(canvas);

function render() {
uniforms.pointLights.value.forEach((light) => {
Expand All @@ -359,11 +361,8 @@ export default function (
// bounding box to be fully shown.
for (let i = 0; i < 8; i++) {
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]
).applyMatrix4(camera.matrixWorldInverse);

Expand Down Expand Up @@ -435,7 +434,6 @@ export default function (
).normalize();

const cameraY = new Vector3()
// @ts-expect-error: bad three.js typing
.subVectors(focus, camera.position)
.normalize()
.cross(cameraX);
Expand Down
Loading
Loading