Fix bugs related to z scaling
This commit is contained in:
parent
e7ff4c5493
commit
e3a4018582
6 changed files with 156 additions and 110 deletions
|
@ -140,10 +140,14 @@ export function Form() {
|
||||||
const [emptyProfile, setEmptyProfile] = useState<boolean>(false);
|
const [emptyProfile, setEmptyProfile] = useState<boolean>(false);
|
||||||
const { sceneView } = useContext(SceneViewContext) as SceneViewContextType;
|
const { sceneView } = useContext(SceneViewContext) as SceneViewContextType;
|
||||||
|
|
||||||
function handleChangeSlicingBox() {
|
function handleChangeSlicingBox(e: ChangeEvent<HTMLInputElement>) {
|
||||||
if (!sceneView) return;
|
if (!sceneView) return;
|
||||||
|
|
||||||
sceneView.toggleClippingBox();
|
if (e.target.checked) {
|
||||||
|
sceneView.toggleClippingBox(true);
|
||||||
|
} else {
|
||||||
|
sceneView.toggleClippingBox(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChangeCG() {
|
function handleChangeCG() {
|
||||||
|
@ -217,10 +221,8 @@ export function Form() {
|
||||||
|
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
sceneView.explode(true);
|
sceneView.explode(true);
|
||||||
// setExploded(true);
|
|
||||||
} else {
|
} else {
|
||||||
sceneView.explode(false);
|
sceneView.explode(false);
|
||||||
// setExploded(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ export function RangeSlider() {
|
||||||
const z = parseFloat(t.value);
|
const z = parseFloat(t.value);
|
||||||
if (!isNaN(z)) {
|
if (!isNaN(z)) {
|
||||||
setScale(z);
|
setScale(z);
|
||||||
sceneView.scene.scale.set(1, 1, z);
|
sceneView.setZScale(z);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -45,7 +45,6 @@ export type CustomEvent = CustomEventInit<{
|
||||||
|
|
||||||
export class SceneView extends EventTarget {
|
export class SceneView extends EventTarget {
|
||||||
private _scene: Scene;
|
private _scene: Scene;
|
||||||
private _dragControls: DragControls;
|
|
||||||
private _model: Group;
|
private _model: Group;
|
||||||
private _camera: PerspectiveCamera;
|
private _camera: PerspectiveCamera;
|
||||||
private _container: HTMLElement;
|
private _container: HTMLElement;
|
||||||
|
@ -57,12 +56,13 @@ export class SceneView extends EventTarget {
|
||||||
private static _DRAG_THRESHOLD = 5;
|
private static _DRAG_THRESHOLD = 5;
|
||||||
private _callback: EventListenerOrEventListenerObject | null = null;
|
private _callback: EventListenerOrEventListenerObject | null = null;
|
||||||
private _orbitControls: OrbitControls;
|
private _orbitControls: OrbitControls;
|
||||||
|
private _dragControls: DragControls | null = null;
|
||||||
private _renderer: WebGLRenderer;
|
private _renderer: WebGLRenderer;
|
||||||
|
private static _DISPLACEMENT = 2000;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
model: Group,
|
model: Group,
|
||||||
dragControls: DragControls,
|
|
||||||
camera: PerspectiveCamera,
|
camera: PerspectiveCamera,
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
extent: Extent,
|
extent: Extent,
|
||||||
|
@ -71,7 +71,6 @@ export class SceneView extends EventTarget {
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._scene = scene;
|
this._scene = scene;
|
||||||
this._dragControls = dragControls;
|
|
||||||
this._model = model;
|
this._model = model;
|
||||||
this._camera = camera;
|
this._camera = camera;
|
||||||
this._container = container;
|
this._container = container;
|
||||||
|
@ -84,13 +83,11 @@ export class SceneView extends EventTarget {
|
||||||
static async create(container: HTMLElement, modelId: string) {
|
static async create(container: HTMLElement, modelId: string) {
|
||||||
const data = await init(container, modelId);
|
const data = await init(container, modelId);
|
||||||
if (data) {
|
if (data) {
|
||||||
const { scene, model, dragControls, camera, extent, controls, renderer } =
|
const { scene, model, camera, extent, controls, renderer } = data;
|
||||||
data;
|
|
||||||
|
|
||||||
return new SceneView(
|
return new SceneView(
|
||||||
scene,
|
scene,
|
||||||
model,
|
model,
|
||||||
dragControls,
|
|
||||||
camera,
|
camera,
|
||||||
container,
|
container,
|
||||||
extent,
|
extent,
|
||||||
|
@ -110,17 +107,11 @@ export class SceneView extends EventTarget {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleClippingBox() {
|
toggleClippingBox(on = true) {
|
||||||
const box = this._scene?.getObjectByName("clipping-box");
|
if (on) {
|
||||||
if (box) {
|
this._resetClippingBox();
|
||||||
// Set DragControls
|
} else {
|
||||||
if (box.visible) {
|
this._removeClippingBoxObjects();
|
||||||
this._dragControls.enabled = false;
|
|
||||||
} else {
|
|
||||||
this._dragControls.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
box.visible = !box.visible;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,57 +335,12 @@ export class SceneView extends EventTarget {
|
||||||
this._orbitControls.reset();
|
this._orbitControls.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
explode(explode: boolean) {
|
private _removeClippingBoxObjects() {
|
||||||
const DISPLACEMENT = 2000;
|
// Remove existing boxes
|
||||||
for (let i = 1; i < this._model.children.length; i++) {
|
|
||||||
const mesh = this._model.children[i];
|
|
||||||
|
|
||||||
if (explode) {
|
|
||||||
const displacement =
|
|
||||||
(this._model.children.length - i - 1) * DISPLACEMENT;
|
|
||||||
mesh.userData.originalPosition = mesh.position.clone();
|
|
||||||
mesh.translateZ(displacement);
|
|
||||||
|
|
||||||
if (i === 1) {
|
|
||||||
this._model.userData.zmax = this._extent.zmax;
|
|
||||||
this._extent.zmax += displacement;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (mesh.userData.originalPosition) {
|
|
||||||
mesh.position.copy(mesh.userData.originalPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset extent
|
|
||||||
if (!explode && this._model.userData.zmax) {
|
|
||||||
this._extent.zmax = this._model.userData.zmax;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset clipping box
|
|
||||||
const box = this._scene.getObjectByName("clipping-box");
|
const box = this._scene.getObjectByName("clipping-box");
|
||||||
let visible = false;
|
|
||||||
if (box) {
|
if (box) {
|
||||||
visible = box.visible;
|
|
||||||
this._scene.remove(box);
|
this._scene.remove(box);
|
||||||
}
|
}
|
||||||
const { planes, dragControls } = buildClippingplanes(
|
|
||||||
this._renderer,
|
|
||||||
this._camera,
|
|
||||||
this._orbitControls,
|
|
||||||
this._extent,
|
|
||||||
this._model.children as Mesh[],
|
|
||||||
this._scene,
|
|
||||||
visible
|
|
||||||
);
|
|
||||||
|
|
||||||
this._dragControls.dispose();
|
|
||||||
this._dragControls = dragControls;
|
|
||||||
|
|
||||||
// Add clipping planes to the meshes
|
|
||||||
for (const mesh of this._model.children) {
|
|
||||||
((mesh as Mesh).material as Material).clippingPlanes = planes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove existing cap meshes
|
// Remove existing cap meshes
|
||||||
for (const o in Orientation) {
|
for (const o in Orientation) {
|
||||||
|
@ -405,11 +351,101 @@ export class SceneView extends EventTarget {
|
||||||
capMeshGroup = this._scene.getObjectByName(capMeshGroupName);
|
capMeshGroup = this._scene.getObjectByName(capMeshGroupName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove drag controls
|
||||||
|
if (this._dragControls) {
|
||||||
|
this._dragControls.dispose();
|
||||||
|
this._dragControls = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove clipping planes
|
||||||
|
for (const mesh of this._model.children) {
|
||||||
|
((mesh as Mesh).material as Material).clippingPlanes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clipping box
|
||||||
|
private _resetClippingBox() {
|
||||||
|
this._removeClippingBoxObjects();
|
||||||
|
|
||||||
|
const { planes, dragControls } = buildClippingplanes(
|
||||||
|
this._renderer,
|
||||||
|
this._camera,
|
||||||
|
this._orbitControls,
|
||||||
|
this._extent,
|
||||||
|
this._model.children as Mesh[],
|
||||||
|
this._scene
|
||||||
|
);
|
||||||
|
|
||||||
|
this._dragControls = dragControls;
|
||||||
|
|
||||||
|
// Add clipping planes to the meshes
|
||||||
|
for (const mesh of this._model.children) {
|
||||||
|
((mesh as Mesh).material as Material).clippingPlanes = planes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explode meshes
|
||||||
|
explode(explode: boolean) {
|
||||||
|
if (explode) {
|
||||||
|
// Save previous zmax value
|
||||||
|
this._scene.userData.zmax = this._extent.zmax;
|
||||||
|
|
||||||
|
const maxDisplacement =
|
||||||
|
this._model.children.length * SceneView._DISPLACEMENT;
|
||||||
|
|
||||||
|
this._extent.zmax += maxDisplacement;
|
||||||
|
} else {
|
||||||
|
// Reset extent
|
||||||
|
this._extent.zmax = this._scene.userData.zmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clipping box
|
||||||
|
const box = this._scene.getObjectByName("clipping-box");
|
||||||
|
if (box && box.visible) {
|
||||||
|
this._resetClippingBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i < this._model.children.length; i++) {
|
||||||
|
const mesh = this._model.children[i];
|
||||||
|
|
||||||
|
if (explode) {
|
||||||
|
const displacement =
|
||||||
|
(this._model.children.length - i - 1) * SceneView._DISPLACEMENT;
|
||||||
|
mesh.userData.originalPosition = mesh.position.clone();
|
||||||
|
mesh.translateZ(displacement);
|
||||||
|
} else {
|
||||||
|
if (mesh.userData.originalPosition) {
|
||||||
|
mesh.position.copy(mesh.userData.originalPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set z scaling factor
|
||||||
|
setZScale(scale: number) {
|
||||||
|
// Update extent
|
||||||
|
//this._extent = {
|
||||||
|
// ...this._extent,
|
||||||
|
// zmin: (scale * this._extent.zmin) / this._scene.scale.z,
|
||||||
|
// zmax: (scale * this._extent.zmax) / this._scene.scale.z,
|
||||||
|
//};
|
||||||
|
|
||||||
|
// Set scale factor
|
||||||
|
this._scene.scale.set(1, 1, scale);
|
||||||
|
|
||||||
|
// Reset clipping box
|
||||||
|
const box = this._scene.getObjectByName("clipping-box");
|
||||||
|
if (box && box.visible) {
|
||||||
|
this._resetClippingBox();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init(container: HTMLElement, modelId = MODEL_ID) {
|
async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
const modelData = await getMetadata(SERVICE_URL + modelId);
|
const modelData = await getMetadata(SERVICE_URL + modelId);
|
||||||
|
if (!modelData) return null;
|
||||||
|
|
||||||
const mappedFeatures = modelData.mappedfeatures;
|
const mappedFeatures = modelData.mappedfeatures;
|
||||||
const modelarea = modelData.modelarea;
|
const modelarea = modelData.modelarea;
|
||||||
|
|
||||||
|
@ -436,23 +472,6 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
model.name = "geologic-model";
|
model.name = "geologic-model";
|
||||||
scene.add(model);
|
scene.add(model);
|
||||||
|
|
||||||
// Build the clipping planes and add them to the scene
|
|
||||||
const visible = false;
|
|
||||||
const { planes, dragControls } = buildClippingplanes(
|
|
||||||
renderer,
|
|
||||||
camera,
|
|
||||||
controls,
|
|
||||||
extent,
|
|
||||||
meshes,
|
|
||||||
scene,
|
|
||||||
visible
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add clipping planes to the meshes
|
|
||||||
for (const mesh of meshes) {
|
|
||||||
mesh.material.clippingPlanes = planes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a coordinate grid to the scene
|
// Add a coordinate grid to the scene
|
||||||
const { gridHelper, annotations } = buildCoordinateGrid(extent);
|
const { gridHelper, annotations } = buildCoordinateGrid(extent);
|
||||||
const { heightGridHelper, heightAnnotations } = buildHeightGrid(extent);
|
const { heightGridHelper, heightAnnotations } = buildHeightGrid(extent);
|
||||||
|
@ -489,5 +508,12 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
map.visible = false;
|
map.visible = false;
|
||||||
scene.add(map);
|
scene.add(map);
|
||||||
|
|
||||||
return { scene, model, dragControls, camera, extent, controls, renderer };
|
return {
|
||||||
|
scene,
|
||||||
|
model,
|
||||||
|
camera,
|
||||||
|
extent,
|
||||||
|
controls,
|
||||||
|
renderer,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,7 @@ export function buildClippingplanes(
|
||||||
orbitControls: OrbitControls,
|
orbitControls: OrbitControls,
|
||||||
extent: Extent,
|
extent: Extent,
|
||||||
meshes: Mesh[],
|
meshes: Mesh[],
|
||||||
scene: Scene,
|
scene: Scene
|
||||||
visible: boolean
|
|
||||||
) {
|
) {
|
||||||
// Set current extent to given extent
|
// Set current extent to given extent
|
||||||
currentExtent = { ...extent };
|
currentExtent = { ...extent };
|
||||||
|
@ -137,7 +136,12 @@ export function buildClippingplanes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plane is given in Hesse normal form: a * x + b* y + c * y + d = 0, where normal = (a, b, c) and d = d
|
// Plane is given in Hesse normal form: a * x + b* y + c * y + d = 0, where normal = (a, b, c) and d = d
|
||||||
const plane = new Plane(p.normal, p.d);
|
const plane = new Plane(
|
||||||
|
p.normal,
|
||||||
|
p.orientation === Orientation.Z || p.orientation === Orientation.NZ
|
||||||
|
? scene.scale.z * p.d
|
||||||
|
: p.d
|
||||||
|
);
|
||||||
|
|
||||||
// Visual representation of the clipping plane
|
// Visual representation of the clipping plane
|
||||||
const planeGeometry = new PlaneGeometry(width, height);
|
const planeGeometry = new PlaneGeometry(width, height);
|
||||||
|
@ -197,7 +201,6 @@ export function buildClippingplanes(
|
||||||
const clippingBox = new Group();
|
const clippingBox = new Group();
|
||||||
clippingBox.add(planeMeshGroup, edgeMeshGroup);
|
clippingBox.add(planeMeshGroup, edgeMeshGroup);
|
||||||
clippingBox.name = "clipping-box";
|
clippingBox.name = "clipping-box";
|
||||||
clippingBox.visible = visible;
|
|
||||||
scene.add(clippingBox);
|
scene.add(clippingBox);
|
||||||
|
|
||||||
// Enable DragControls for the clipping planes
|
// Enable DragControls for the clipping planes
|
||||||
|
@ -207,8 +210,6 @@ export function buildClippingplanes(
|
||||||
renderer.domElement
|
renderer.domElement
|
||||||
);
|
);
|
||||||
|
|
||||||
dragControls.enabled = visible;
|
|
||||||
|
|
||||||
dragControls.addEventListener("dragstart", () => {
|
dragControls.addEventListener("dragstart", () => {
|
||||||
// Disable OrbitControls when dragging starts
|
// Disable OrbitControls when dragging starts
|
||||||
orbitControls.enabled = false;
|
orbitControls.enabled = false;
|
||||||
|
@ -251,6 +252,7 @@ export function buildClippingplanes(
|
||||||
|
|
||||||
// Reset position of plane
|
// Reset position of plane
|
||||||
plane.constant = orientation === Orientation.Z ? -newZ : newZ;
|
plane.constant = orientation === Orientation.Z ? -newZ : newZ;
|
||||||
|
plane.constant = scene.scale.z * plane.constant;
|
||||||
|
|
||||||
// Update current extent
|
// Update current extent
|
||||||
if (orientation === Orientation.Z) {
|
if (orientation === Orientation.Z) {
|
||||||
|
@ -578,6 +580,7 @@ function generateCapMeshes(
|
||||||
scene: Scene
|
scene: Scene
|
||||||
) {
|
) {
|
||||||
const capMeshes: Mesh[] = [];
|
const capMeshes: Mesh[] = [];
|
||||||
|
const scaleFactor = scene.scale.z;
|
||||||
|
|
||||||
// Iterate over the list of geologic meshes
|
// Iterate over the list of geologic meshes
|
||||||
for (const mesh of meshes) {
|
for (const mesh of meshes) {
|
||||||
|
@ -598,17 +601,17 @@ function generateCapMeshes(
|
||||||
const v1 = new Vector3(
|
const v1 = new Vector3(
|
||||||
position[i1],
|
position[i1],
|
||||||
position[i1 + 1],
|
position[i1 + 1],
|
||||||
position[i1 + 2] + mesh.position.z
|
scaleFactor * (position[i1 + 2] + mesh.position.z)
|
||||||
);
|
);
|
||||||
const v2 = new Vector3(
|
const v2 = new Vector3(
|
||||||
position[i2],
|
position[i2],
|
||||||
position[i2 + 1],
|
position[i2 + 1],
|
||||||
position[i2 + 2] + mesh.position.z
|
scaleFactor * (position[i2 + 2] + mesh.position.z)
|
||||||
);
|
);
|
||||||
const v3 = new Vector3(
|
const v3 = new Vector3(
|
||||||
position[i3],
|
position[i3],
|
||||||
position[i3 + 1],
|
position[i3 + 1],
|
||||||
position[i3 + 2] + mesh.position.z
|
scaleFactor * (position[i3 + 2] + mesh.position.z)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if the triangle is cut by the plane
|
// Check if the triangle is cut by the plane
|
||||||
|
@ -624,7 +627,12 @@ function generateCapMeshes(
|
||||||
if (d3 * d1 < 0) intersections.push(intersectEdge(v3, v1, d3, d1));
|
if (d3 * d1 < 0) intersections.push(intersectEdge(v3, v1, d3, d1));
|
||||||
|
|
||||||
if (intersections.length === 2) {
|
if (intersections.length === 2) {
|
||||||
edges.push([intersections[0], intersections[1]]);
|
// Rescale local coordinates and push to edges
|
||||||
|
const start = intersections[0];
|
||||||
|
const end = intersections[1];
|
||||||
|
start.z = start.z / scaleFactor;
|
||||||
|
end.z = end.z / scaleFactor;
|
||||||
|
edges.push([start, end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,7 +642,13 @@ function generateCapMeshes(
|
||||||
// Clip cap surfaces with clipping planes
|
// Clip cap surfaces with clipping planes
|
||||||
const clippingPlanes = planes.filter((p) => !p.normal.equals(plane.normal));
|
const clippingPlanes = planes.filter((p) => !p.normal.equals(plane.normal));
|
||||||
|
|
||||||
const offset = orientation === Orientation.Z ? 1 : -1;
|
const offset =
|
||||||
|
orientation === Orientation.NX ||
|
||||||
|
orientation === Orientation.NY ||
|
||||||
|
orientation === Orientation.NZ
|
||||||
|
? 1
|
||||||
|
: -1;
|
||||||
|
|
||||||
const material = new MeshStandardMaterial({
|
const material = new MeshStandardMaterial({
|
||||||
color: (mesh.material as MeshStandardMaterial).color,
|
color: (mesh.material as MeshStandardMaterial).color,
|
||||||
side: DoubleSide,
|
side: DoubleSide,
|
||||||
|
|
|
@ -73,7 +73,7 @@ export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
controls.target.set(center.x, center.y, center.z);
|
controls.target.set(center.x, center.y, center.z);
|
||||||
controls.enableDamping = true;
|
controls.enableDamping = true;
|
||||||
controls.dampingFactor = 0.1;
|
controls.dampingFactor = 0.1;
|
||||||
controls.maxDistance = maxSize * 3;
|
controls.maxDistance = maxSize * 5;
|
||||||
controls.minDistance = maxSize / 5;
|
controls.minDistance = maxSize / 5;
|
||||||
controls.update();
|
controls.update();
|
||||||
controls.saveState();
|
controls.saveState();
|
||||||
|
|
|
@ -20,18 +20,22 @@ export function getCenter3D(extent: Extent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMetadata(serviceUrl: string) {
|
export async function getMetadata(serviceUrl: string) {
|
||||||
const response = await fetch(serviceUrl, {
|
try {
|
||||||
method: "GET",
|
const response = await fetch(serviceUrl, {
|
||||||
mode: "cors",
|
method: "GET",
|
||||||
headers: {
|
mode: "cors",
|
||||||
"Content-Type": "application/json",
|
headers: {
|
||||||
},
|
"Content-Type": "application/json",
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
} else {
|
} else {
|
||||||
throw new Error("HTTP error status: " + response.status);
|
throw new Error("HTTP error status: " + response.status);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue