From e36535f2a08a50372e5e78aa04fda40be7ad7994 Mon Sep 17 00:00:00 2001 From: Lucas Dower Date: Sat, 29 Oct 2022 22:34:00 +0100 Subject: [PATCH] Added per-axis constraint options --- src/app_context.ts | 5 ++-- src/ui/layout.ts | 19 +++++++++++++-- src/util/type_util.ts | 1 + .../bvh-ray-voxeliser-plus-thickness.ts | 22 +++++++++++++++--- src/voxelisers/bvh-ray-voxeliser.ts | 22 +++++++++++++++--- .../normal-corrected-ray-voxeliser.ts | 23 +++++++++++++++---- src/voxelisers/ray-voxeliser.ts | 19 +++++++++++++-- src/worker_types.ts | 4 +++- styles.css | 1 + tests/normal-corrected-ray-voxeliser.test.ts | 3 ++- tests/objlitematic.test.ts | 3 ++- tests/objobj.test.ts | 3 ++- tests/objschem.test.ts | 3 ++- tests/objschematic.test.ts | 3 ++- tools/headless-config.ts | 3 ++- tools/headless.ts | 4 ++-- 16 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 src/util/type_util.ts diff --git a/src/app_context.ts b/src/app_context.ts index 9436c2b..fbae3a8 100644 --- a/src/app_context.ts +++ b/src/app_context.ts @@ -244,8 +244,9 @@ export class AppContext { const payload: TToWorkerMessage = { action: 'Voxelise', params: { + constraintAxis: uiElements.constraintAxis.getCachedValue(), voxeliser: uiElements.voxeliser.getCachedValue(), - desiredHeight: uiElements.desiredHeight.getCachedValue(), + size: uiElements.size.getCachedValue(), useMultisampleColouring: uiElements.multisampleColouring.getCachedValue() === 'on', textureFiltering: uiElements.textureFiltering.getCachedValue() === 'linear' ? TextureFiltering.Linear : TextureFiltering.Nearest, enableAmbientOcclusion: uiElements.ambientOcclusion.getCachedValue() === 'on', @@ -273,7 +274,7 @@ export class AppContext { action: 'RenderNextVoxelMeshChunk', params: { enableAmbientOcclusion: uiElements.ambientOcclusion.getCachedValue() === 'on', - desiredHeight: uiElements.desiredHeight.getCachedValue(), + desiredHeight: uiElements.size.getCachedValue(), }, }; diff --git a/src/ui/layout.ts b/src/ui/layout.ts index 907aab6..2fa610a 100644 --- a/src/ui/layout.ts +++ b/src/ui/layout.ts @@ -10,6 +10,7 @@ import { EAction } from '../util'; import { ASSERT } from '../util/error_util'; import { LOG } from '../util/log_util'; import { AppPaths } from '../util/path_util'; +import { TAxis } from '../util/type_util'; import { TVoxelOverlapRule } from '../voxel_mesh'; import { TVoxelisers } from '../voxelisers/voxelisers'; import { BaseUIElement } from './elements/base'; @@ -52,7 +53,21 @@ export class UI { 'voxelise': { label: 'Voxelise', elements: { - 'desiredHeight': new SliderElement('Desired height', 3, 380, 0, 80, 1), + 'constraintAxis': new ComboBoxElement('Constraint axis', [ + { + id: 'y', + displayText: 'Y (height) (green)', + }, + { + id: 'x', + displayText: 'X (width) (red)', + }, + { + id: 'z', + displayText: 'Z (depth) (blue)', + }, + ]), + 'size': new SliderElement('Size', 3, 380, 0, 80, 1), 'voxeliser': new ComboBoxElement('Algorithm', [ { id: 'bvh-ray', @@ -114,7 +129,7 @@ export class UI { }, ]), }, - elementsOrder: ['desiredHeight', 'voxeliser', 'ambientOcclusion', 'multisampleColouring', 'textureFiltering', 'voxelOverlapRule'], + elementsOrder: ['constraintAxis', 'size', 'voxeliser', 'ambientOcclusion', 'multisampleColouring', 'textureFiltering', 'voxelOverlapRule'], submitButton: new ButtonElement('Voxelise mesh', () => { this._appContext.do(EAction.Voxelise); }), diff --git a/src/util/type_util.ts b/src/util/type_util.ts new file mode 100644 index 0000000..2f3f6be --- /dev/null +++ b/src/util/type_util.ts @@ -0,0 +1 @@ +export type TAxis = 'x' | 'y' | 'z'; diff --git a/src/voxelisers/bvh-ray-voxeliser-plus-thickness.ts b/src/voxelisers/bvh-ray-voxeliser-plus-thickness.ts index a41fd2b..47a77b2 100644 --- a/src/voxelisers/bvh-ray-voxeliser-plus-thickness.ts +++ b/src/voxelisers/bvh-ray-voxeliser-plus-thickness.ts @@ -17,8 +17,24 @@ const bvhtree = require('bvh-tree'); export class BVHRayVoxeliserPlusThickness extends IVoxeliser { protected override _voxelise(mesh: Mesh, voxeliseParams: VoxeliseParams.Input): VoxelMesh { const voxelMesh = new VoxelMesh(voxeliseParams); - const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight; - const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); + + const meshDimensions = mesh.getBounds().getDimensions(); + let scale: number; + let offset = new Vector3(0.0, 0.0, 0.0); + switch (voxeliseParams.constraintAxis) { + case 'x': + scale = (voxeliseParams.size - 1) / meshDimensions.x; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.5, 0.0, 0.0) : new Vector3(0.0, 0.0, 0.0); + break; + case 'y': + scale = (voxeliseParams.size - 1) / meshDimensions.y; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); + break; + case 'z': + scale = (voxeliseParams.size - 1) / meshDimensions.z; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.0, 0.5) : new Vector3(0.0, 0.0, 0.0); + break; + } mesh.setTransform((vertex: Vector3) => { return vertex.copy().mulScalar(scale).add(offset); @@ -87,7 +103,7 @@ export class BVHRayVoxeliserPlusThickness extends IVoxeliser { for (const intersection of intersections) { const point = intersection.intersectionPoint; const position = new Vector3(point.x, point.y, point.z); - + // Shrinking towards the perpendicular vector const triangle = mesh.getUVTriangle(intersection.triangleIndex); const v0 = Vector3.sub(triangle.v1, triangle.v0); diff --git a/src/voxelisers/bvh-ray-voxeliser.ts b/src/voxelisers/bvh-ray-voxeliser.ts index 28237ba..1debeee 100644 --- a/src/voxelisers/bvh-ray-voxeliser.ts +++ b/src/voxelisers/bvh-ray-voxeliser.ts @@ -2,7 +2,7 @@ import { Mesh } from '../mesh'; import { ProgressManager } from '../progress'; import { Axes, axesToDirection, Ray } from '../ray'; import { ASSERT } from '../util/error_util'; -import { LOG } from '../util/log_util'; +import { LOG, LOGF } from '../util/log_util'; import { Vector3 } from '../vector'; import { VoxelMesh } from '../voxel_mesh'; import { VoxeliseParams } from '../worker_types'; @@ -17,8 +17,24 @@ const bvhtree = require('bvh-tree'); export class BVHRayVoxeliser extends IVoxeliser { protected override _voxelise(mesh: Mesh, voxeliseParams: VoxeliseParams.Input): VoxelMesh { const voxelMesh = new VoxelMesh(voxeliseParams); - const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight; - const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); + + const meshDimensions = mesh.getBounds().getDimensions(); + let scale: number; + let offset = new Vector3(0.0, 0.0, 0.0); + switch (voxeliseParams.constraintAxis) { + case 'x': + scale = (voxeliseParams.size - 1) / meshDimensions.x; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.5, 0.0, 0.0) : new Vector3(0.0, 0.0, 0.0); + break; + case 'y': + scale = (voxeliseParams.size - 1) / meshDimensions.y; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); + break; + case 'z': + scale = (voxeliseParams.size - 1) / meshDimensions.z; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.0, 0.5) : new Vector3(0.0, 0.0, 0.0); + break; + } mesh.setTransform((vertex: Vector3) => { return vertex.copy().mulScalar(scale).add(offset); diff --git a/src/voxelisers/normal-corrected-ray-voxeliser.ts b/src/voxelisers/normal-corrected-ray-voxeliser.ts index 139eccb..ef735ae 100644 --- a/src/voxelisers/normal-corrected-ray-voxeliser.ts +++ b/src/voxelisers/normal-corrected-ray-voxeliser.ts @@ -27,17 +27,30 @@ export class NormalCorrectedRayVoxeliser extends IVoxeliser { this._voxelMesh = new VoxelMesh(voxeliseParams); this._voxeliseParams = voxeliseParams; - const scale = (voxeliseParams.desiredHeight) / Mesh.desiredHeight; + const meshDimensions = mesh.getBounds().getDimensions(); + let scale: number; + switch (voxeliseParams.constraintAxis) { + case 'x': + scale = voxeliseParams.size / meshDimensions.x; + break; + case 'y': + scale = voxeliseParams.size / meshDimensions.y; + break; + case 'z': + scale = voxeliseParams.size / meshDimensions.z; + break; + } + mesh.setTransform((vertex: Vector3) => { return vertex.copy().mulScalar(scale); }); const bounds = mesh.getBounds(); - this._size = Vector3.sub(bounds.max, bounds.min); + this._size = Vector3.sub(bounds.max.copy().ceil(), bounds.min.copy().floor()); this._offset = new Vector3( - this._size.x % 2 < 0.001 ? 0.5 : 0.0, - this._size.y % 2 < 0.001 ? 0.5 : 0.0, - this._size.z % 2 < 0.001 ? 0.5 : 0.0, + this._size.x % 2 === 0 ? 0.5 : 0.0, + this._size.y % 2 === 0 ? 0.5 : 0.0, + this._size.z % 2 === 0 ? 0.5 : 0.0, ); const numTris = mesh.getTriangleCount(); diff --git a/src/voxelisers/ray-voxeliser.ts b/src/voxelisers/ray-voxeliser.ts index 40c9545..419cc90 100644 --- a/src/voxelisers/ray-voxeliser.ts +++ b/src/voxelisers/ray-voxeliser.ts @@ -25,8 +25,23 @@ export class RayVoxeliser extends IVoxeliser { this._voxelMesh = new VoxelMesh(voxeliseParams); this._voxeliseParams = voxeliseParams; - const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight; - const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); + const meshDimensions = mesh.getBounds().getDimensions(); + let scale: number; + let offset = new Vector3(0.0, 0.0, 0.0); + switch (voxeliseParams.constraintAxis) { + case 'x': + scale = (voxeliseParams.size - 1) / meshDimensions.x; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.5, 0.0, 0.0) : new Vector3(0.0, 0.0, 0.0); + break; + case 'y': + scale = (voxeliseParams.size - 1) / meshDimensions.y; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); + break; + case 'z': + scale = (voxeliseParams.size - 1) / meshDimensions.z; + offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.0, 0.5) : new Vector3(0.0, 0.0, 0.0); + break; + } mesh.setTransform((vertex: Vector3) => { return vertex.copy().mulScalar(scale).add(offset); diff --git a/src/worker_types.ts b/src/worker_types.ts index 48a7f16..c58653a 100644 --- a/src/worker_types.ts +++ b/src/worker_types.ts @@ -7,6 +7,7 @@ import { StatusMessage } from './status'; import { TextureFiltering } from './texture'; import { ColourSpace } from './util'; import { AppError } from './util/error_util'; +import { TAxis } from './util/type_util'; import { Vector3 } from './vector'; import { TVoxelOverlapRule } from './voxel_mesh'; import { TVoxelisers } from './voxelisers/voxelisers'; @@ -42,8 +43,9 @@ export namespace RenderMeshParams { export namespace VoxeliseParams { export type Input = { + constraintAxis: TAxis, voxeliser: TVoxelisers, - desiredHeight: number, + size: number, useMultisampleColouring: boolean, textureFiltering: TextureFiltering, enableAmbientOcclusion: boolean, diff --git a/styles.css b/styles.css index 5ab776e..942ed1c 100644 --- a/styles.css +++ b/styles.css @@ -98,6 +98,7 @@ canvas { align-items: center; /*border: 1px solid yellow;*/ height: var(--property-height); + width: 0px; } .item-body-sunken { diff --git a/tests/normal-corrected-ray-voxeliser.test.ts b/tests/normal-corrected-ray-voxeliser.test.ts index defb78e..087d0bc 100644 --- a/tests/normal-corrected-ray-voxeliser.test.ts +++ b/tests/normal-corrected-ray-voxeliser.test.ts @@ -18,7 +18,8 @@ test('Voxelise solid 2x2 cube', () => { const voxeliser = new NormalCorrectedRayVoxeliser(); const voxelMesh = voxeliser.voxelise(mesh, { - desiredHeight: 2, + constraintAxis: 'y', + size: 2, useMultisampleColouring: false, textureFiltering: TextureFiltering.Nearest, enableAmbientOcclusion: false, diff --git a/tests/objlitematic.test.ts b/tests/objlitematic.test.ts index 6791d4e..b63396e 100644 --- a/tests/objlitematic.test.ts +++ b/tests/objlitematic.test.ts @@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = { }, voxelise: { voxeliser: 'bvh-ray', - desiredHeight: 80, + constraintAxis: 'y', + size: 80, useMultisampleColouring: false, textureFiltering: TextureFiltering.Linear, voxelOverlapRule: 'average', diff --git a/tests/objobj.test.ts b/tests/objobj.test.ts index 537e67e..6fda3c3 100644 --- a/tests/objobj.test.ts +++ b/tests/objobj.test.ts @@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = { }, voxelise: { voxeliser: 'bvh-ray', - desiredHeight: 80, + constraintAxis: 'y', + size: 80, useMultisampleColouring: false, textureFiltering: TextureFiltering.Linear, voxelOverlapRule: 'average', diff --git a/tests/objschem.test.ts b/tests/objschem.test.ts index a9a9340..9ad3a94 100644 --- a/tests/objschem.test.ts +++ b/tests/objschem.test.ts @@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = { }, voxelise: { voxeliser: 'bvh-ray', - desiredHeight: 80, + constraintAxis: 'y', + size: 80, useMultisampleColouring: false, textureFiltering: TextureFiltering.Linear, voxelOverlapRule: 'average', diff --git a/tests/objschematic.test.ts b/tests/objschematic.test.ts index 4499513..6dadae3 100644 --- a/tests/objschematic.test.ts +++ b/tests/objschematic.test.ts @@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = { }, voxelise: { voxeliser: 'bvh-ray', - desiredHeight: 80, + constraintAxis: 'y', + size: 80, useMultisampleColouring: false, textureFiltering: TextureFiltering.Linear, voxelOverlapRule: 'average', diff --git a/tools/headless-config.ts b/tools/headless-config.ts index d210ea2..9bd3878 100644 --- a/tools/headless-config.ts +++ b/tools/headless-config.ts @@ -7,8 +7,9 @@ export const headlessConfig: THeadlessConfig = { filepath: '/Users/lucasdower/ObjToSchematic/res/samples/skull.obj', // Must be an absolute path }, voxelise: { + constraintAxis: 'y', voxeliser: 'bvh-ray', - desiredHeight: 80, + size: 80, useMultisampleColouring: false, textureFiltering: TextureFiltering.Linear, voxelOverlapRule: 'average', diff --git a/tools/headless.ts b/tools/headless.ts index 1553fc2..ba4400b 100644 --- a/tools/headless.ts +++ b/tools/headless.ts @@ -51,7 +51,7 @@ export function runHeadless(headlessConfig: THeadlessConfig) { { TIME_START('[TIMER] Exporter'); LOG_MAJOR('\nExporting...'); - + /** * The OBJExporter is unique in that it uses the actual render buffer used by WebGL * to create its data, in headless mode this render buffer is not created so we must @@ -62,7 +62,7 @@ export function runHeadless(headlessConfig: THeadlessConfig) { do { result = worker.renderChunkedVoxelMesh({ enableAmbientOcclusion: headlessConfig.voxelise.enableAmbientOcclusion, - desiredHeight: headlessConfig.voxelise.desiredHeight, + desiredHeight: headlessConfig.voxelise.size, }); } while (result.moreVoxelsToBuffer); }