diff --git a/resources/openbim-components.js b/resources/openbim-components.js index 142513c45..f052e7438 100644 --- a/resources/openbim-components.js +++ b/resources/openbim-components.js @@ -23022,6 +23022,9 @@ class Infinite2dGrid { * with all the power of Three.js. */ class Simple2DScene extends Component { + get size() { + return this._size.clone(); + } get scaleX() { return this._scaleX; } @@ -121857,4 +121860,130 @@ class DXFExporter extends Component { DXFExporter.uuid = "568f2167-24a3-4519-b552-3b04cc74a6a6"; ToolComponent.libraryUUIDs.add(DXFExporter.uuid); -export { AngleMeasurement, AreaMeasurement, ArrowAnnotation, AttributeSet, BaseRenderer, BaseSVGAnnotation, Button, Canvas, CheckboxInput, CircleAnnotation, CloudStorage, ColorInput, CommandsMenu, Component, Components, CubeMap, DXFExporter, DimensionLabelClassName, DimensionPreviewClassName, Disposer, DragAndDropInput, DrawManager, Drawer, Dropdown, EdgeMeasurement, EdgesClipper, EdgesPlane, Event, FaceMeasurement, FloatingWindow, FragmentBoundingBox, FragmentClassifier, FragmentClipStyler, FragmentExploder, FragmentHider, FragmentHighlighter, FragmentIfcLoader, FragmentIfcStreamConverter, FragmentManager, FragmentPlans, FragmentPropsStreamConverter, FragmentStreamLoader, FragmentTree, GeometryVerticesMarker, IfcCategories, IfcCategoryMap, IfcElements, IfcJsonExporter, IfcPropertiesFinder, IfcPropertiesManager, IfcPropertiesProcessor, IfcPropertiesUtils, IfcStreamingSettings, LengthMeasurement, LineIntersectionPicker, MaterialManager, MiniMap, Modal, Mouse, OrthoPerspectiveCamera, PostproductionRenderer, PropertiesStreamingSettings, PropertyTag, RangeInput, RectangleAnnotation, ScreenCuller, ShadowDropper, Simple2DMarker, Simple2DScene, SimpleCamera, SimpleClipper, SimpleDimensionLine, SimpleGrid, SimplePlane, SimpleRaycaster, SimpleRenderer, SimpleSVGViewport, SimpleScene, SimpleUICard, SimpleUIComponent, Spinner, TextAnnotation, TextArea, TextInput, ToastNotification, ToolComponent, Toolbar, TreeView, UIElement, UIManager, VertexPicker, ViewpointsManager, VolumeMeasurement, bufferGeometryToIndexed, distanceFromPointToLine, generateExpressIDFragmentIDMap, generateIfcGUID, getIndexAndPos, getIndices, getPlane, getRaycastedFace, getVertices, isPointInFrontOfPlane, isTransparent, obbFromPoints, roundVector }; +class RoadNavigator extends Component { + constructor(components) { + super(components); + this.enabled = true; + this.caster = new THREE$1.Raycaster(); + this._curves = new Set(); + this.caster.params.Line = { threshold: 5 }; + this.scene = new Simple2DScene(this.components, false); + } + get() { + return null; + } + draw(model, ids) { + if (!model.civilData) { + throw new Error("The provided model doesn't have civil data!"); + } + const { alignments } = model.civilData; + const allIDs = ids || alignments.keys(); + const scene = this.scene.get(); + for (const id of allIDs) { + const alignment = alignments.get(id); + if (!alignment) { + throw new Error("Alignment not found!"); + } + let firstCurve = true; + for (const curve of alignment[this.view]) { + this._curves.add(curve); + scene.add(curve.mesh); + if (firstCurve) { + const pos = curve.mesh.geometry.attributes.position.array; + const [x, y, z] = pos; + this.scene.controls.target.set(x, y, z); + this.scene.camera.position.set(x, y, z + 10); + firstCurve = false; + } + } + } + } + clear() { + for (const curve of this._curves) { + curve.mesh.removeFromParent(); + } + this._curves.clear(); + } +} + +class RoadPlanNavigator extends RoadNavigator { + constructor(components) { + super(components); + this.view = "horizontal"; + this.uiElement = new UIElement(); + this.setUI(); + } + setUI() { + const floatingWindow = new FloatingWindow(this.components); + this.components.ui.add(floatingWindow); + floatingWindow.visible = false; + const hContainer = this.scene.uiElement.get("container"); + floatingWindow.addChild(hContainer); + floatingWindow.onResized.add(() => this.scene.grid.regenerate()); + floatingWindow.slots.content.domElement.style.padding = "0"; + floatingWindow.slots.content.domElement.style.overflow = "hidden"; + floatingWindow.onResized.add(() => { + const { width, height } = floatingWindow.containerSize; + this.scene.setSize(height, width); + }); + floatingWindow.domElement.style.width = "20rem"; + floatingWindow.domElement.style.height = "20rem"; + floatingWindow.onVisible.add(() => { + if (floatingWindow.visible) { + this.scene.grid.regenerate(); + } + }); + if (this.components.renderer.isUpdateable()) { + this.components.renderer.onAfterUpdate.add(async () => { + if (floatingWindow.visible) { + await this.scene.update(); + } + }); + } + this.uiElement.set({ floatingWindow }); + } +} +RoadPlanNavigator.uuid = "3096dea0-5bc2-41c7-abce-9089b6c9431b"; + +class RoadElevationNavigator extends RoadNavigator { + constructor(components) { + super(components); + this.view = "vertical"; + this.uiElement = new UIElement(); + this.setUI(); + } + get() { + return null; + } + setUI() { + const drawer = new Drawer(this.components); + this.components.ui.add(drawer); + drawer.alignment = "top"; + drawer.onVisible.add(() => { + this.scene.grid.regenerate(); + }); + drawer.visible = false; + drawer.slots.content.domElement.style.padding = "0"; + drawer.slots.content.domElement.style.overflow = "hidden"; + const { clientWidth, clientHeight } = drawer.domElement; + this.scene.setSize(clientHeight, clientWidth); + const vContainer = this.scene.uiElement.get("container"); + drawer.addChild(vContainer); + this.uiElement.set({ drawer }); + drawer.onResized.add(() => { + const width = window.innerWidth; + const height = this.scene.size.y; + this.scene.setSize(height, width); + }); + if (this.components.renderer.isUpdateable()) { + this.components.renderer.onAfterUpdate.add(async () => { + if (drawer.visible) { + await this.scene.update(); + } + }); + } + } +} +RoadElevationNavigator.uuid = "097eea29-2d5a-431a-a247-204d44670621"; + +export { AngleMeasurement, AreaMeasurement, ArrowAnnotation, AttributeSet, BaseRenderer, BaseSVGAnnotation, Button, Canvas, CheckboxInput, CircleAnnotation, CloudStorage, ColorInput, CommandsMenu, Component, Components, CubeMap, DXFExporter, DimensionLabelClassName, DimensionPreviewClassName, Disposer, DragAndDropInput, DrawManager, Drawer, Dropdown, EdgeMeasurement, EdgesClipper, EdgesPlane, Event, FaceMeasurement, FloatingWindow, FragmentBoundingBox, FragmentClassifier, FragmentClipStyler, FragmentExploder, FragmentHider, FragmentHighlighter, FragmentIfcLoader, FragmentIfcStreamConverter, FragmentManager, FragmentPlans, FragmentPropsStreamConverter, FragmentStreamLoader, FragmentTree, GeometryVerticesMarker, IfcCategories, IfcCategoryMap, IfcElements, IfcJsonExporter, IfcPropertiesFinder, IfcPropertiesManager, IfcPropertiesProcessor, IfcPropertiesUtils, IfcStreamingSettings, LengthMeasurement, LineIntersectionPicker, MaterialManager, MiniMap, Modal, Mouse, OrthoPerspectiveCamera, PostproductionRenderer, PropertiesStreamingSettings, PropertyTag, RangeInput, RectangleAnnotation, RoadElevationNavigator, RoadNavigator, RoadPlanNavigator, ScreenCuller, ShadowDropper, Simple2DMarker, Simple2DScene, SimpleCamera, SimpleClipper, SimpleDimensionLine, SimpleGrid, SimplePlane, SimpleRaycaster, SimpleRenderer, SimpleSVGViewport, SimpleScene, SimpleUICard, SimpleUIComponent, Spinner, TextAnnotation, TextArea, TextInput, ToastNotification, ToolComponent, Toolbar, TreeView, UIElement, UIManager, VertexPicker, ViewpointsManager, VolumeMeasurement, bufferGeometryToIndexed, distanceFromPointToLine, generateExpressIDFragmentIDMap, generateIfcGUID, getIndexAndPos, getIndices, getPlane, getRaycastedFace, getVertices, isPointInFrontOfPlane, isTransparent, obbFromPoints, roundVector }; diff --git a/src/civil/RoadElevationNavigator/index.html b/src/civil/RoadElevationNavigator/index.html index f5407d047..57a72fc58 100644 --- a/src/civil/RoadElevationNavigator/index.html +++ b/src/civil/RoadElevationNavigator/index.html @@ -84,20 +84,37 @@ const data = await file.arrayBuffer(); const buffer = new Uint8Array(data); const model = await fragments.load(buffer); - const properties = await fetch("../../../resources/asdf.json"); - model.setLocalProperties(await properties.json()); - console.log(model); + // const properties = await fetch("../../../resources/asdf.json"); + // model.setLocalProperties(await properties.json()); + // console.log(model); const mainToolbar = new OBC.Toolbar(components, {name: 'Main Toolbar', position: 'bottom'}); components.ui.addToolbar(mainToolbar); mainToolbar.addChild(fragmentIfcLoader.uiElement.get("main")); + console.log(model); + // Set up road navigator - // const navigator = new OBC.RoadElevationNavigator(components); - // const horizontalWindow = navigator.uiElement.get("drawer"); - // horizontalWindow.visible = true; - // navigator.draw(model); + const navigator = new OBC.RoadElevationNavigator(components); + const drawer = navigator.uiElement.get("drawer"); + drawer.visible = true; + navigator.draw(model); + + let first = true; + const mat = new THREE.Matrix4(); + for(const [id, alignment] of model.civilData.alignments) { + for(const curve of alignment.absolute) { + if(first) { + const pos = curve.mesh.geometry.attributes.position.array; + const [x, y, z] = pos; + mat.makeTranslation(-x, -y, -z); + first = false; + } + curve.mesh.applyMatrix4(mat); + scene.add(curve.mesh); + } + } // Set up stats diff --git a/src/civil/RoadElevationNavigator/index.ts b/src/civil/RoadElevationNavigator/index.ts index c97d96daf..edb1674e6 100644 --- a/src/civil/RoadElevationNavigator/index.ts +++ b/src/civil/RoadElevationNavigator/index.ts @@ -1,86 +1,59 @@ -// import * as THREE from "three"; -// import { FragmentsGroup } from "bim-fragment"; -// import { Component, UI, UIElement } from "../../base-types"; -// import { Drawer } from "../../ui"; -// import { Components, Simple2DScene } from "../../core"; -// import { CivilUtils } from "../CivilUtils"; -// -// export class RoadElevationNavigator extends Component implements UI { -// static readonly uuid = "097eea29-2d5a-431a-a247-204d44670621" as const; -// -// enabled = true; -// -// uiElement = new UIElement<{ -// drawer: Drawer; -// }>(); -// -// scene: Simple2DScene; -// -// caster = new THREE.Raycaster(); -// -// private readonly _alignment: THREE.LineSegments; -// -// constructor(components: Components) { -// super(components); -// -// this.caster.params.Line = { threshold: 5 }; -// -// this.components.tools.add(RoadElevationNavigator.uuid, this); -// -// this.scene = new Simple2DScene(this.components, false); -// -// this._alignment = new THREE.LineSegments( -// new THREE.BufferGeometry(), -// new THREE.LineBasicMaterial() -// ); -// -// const scene = this.scene.get(); -// scene.add(this._alignment); -// -// this.setUI(); -// } -// -// get() { -// return this._alignment; -// } -// -// draw(model: FragmentsGroup) { -// if (!model.ifcCivil) { -// console.warn("The provided model doesn't have civil data!"); -// return; -// } -// const alignment = model.ifcCivil.verticalAlignments; -// const { geometry } = this._alignment; -// CivilUtils.getAlignmentGeometry(alignment, geometry, false); -// } -// -// private setUI() { -// const drawer = new Drawer(this.components); -// this.components.ui.add(drawer); -// drawer.alignment = "top"; -// -// drawer.onVisible.add(() => { -// this.scene.grid.regenerate(); -// }); -// drawer.visible = false; -// -// drawer.slots.content.domElement.style.padding = "0"; -// drawer.slots.content.domElement.style.overflow = "hidden"; -// -// const { clientWidth, clientHeight } = drawer.domElement; -// this.scene.setSize(clientHeight, clientWidth); -// -// const vContainer = this.scene.uiElement.get("container"); -// drawer.addChild(vContainer); -// -// this.uiElement.set({ drawer }); -// -// if (this.components.renderer.isUpdateable()) { -// this.components.renderer.onAfterUpdate.add(async () => { -// if (drawer.visible) { -// await this.scene.update(); -// } -// }); -// } -// } -// } +import { UI, UIElement } from "../../base-types"; +import { Drawer } from "../../ui"; +import { Components } from "../../core"; +import { RoadNavigator } from "../RoadNavigator"; + +export class RoadElevationNavigator extends RoadNavigator implements UI { + static readonly uuid = "097eea29-2d5a-431a-a247-204d44670621" as const; + + readonly view = "vertical"; + + uiElement = new UIElement<{ + drawer: Drawer; + }>(); + + constructor(components: Components) { + super(components); + this.setUI(); + } + + get() { + return null as any; + } + + private setUI() { + const drawer = new Drawer(this.components); + this.components.ui.add(drawer); + drawer.alignment = "top"; + + drawer.onVisible.add(() => { + this.scene.grid.regenerate(); + }); + drawer.visible = false; + + drawer.slots.content.domElement.style.padding = "0"; + drawer.slots.content.domElement.style.overflow = "hidden"; + + const { clientWidth, clientHeight } = drawer.domElement; + this.scene.setSize(clientHeight, clientWidth); + + const vContainer = this.scene.uiElement.get("container"); + drawer.addChild(vContainer); + + this.uiElement.set({ drawer }); + + drawer.onResized.add(() => { + const width = window.innerWidth; + const height = this.scene.size.y; + this.scene.setSize(height, width); + }); + + if (this.components.renderer.isUpdateable()) { + this.components.renderer.onAfterUpdate.add(async () => { + if (drawer.visible) { + await this.scene.update(); + } + }); + } + } +} diff --git a/src/civil/RoadNavigator/index.ts b/src/civil/RoadNavigator/index.ts index dbfd09412..73c6d57cc 100644 --- a/src/civil/RoadNavigator/index.ts +++ b/src/civil/RoadNavigator/index.ts @@ -1,382 +1,66 @@ -// import * as THREE from "three"; -// import { FragmentsGroup, IfcAlignmentData } from "bim-fragment"; -// import { Components, Simple2DScene, ToolComponent } from "../../core"; -// import { Component, UI, UIElement } from "../../base-types"; -// import { Drawer, FloatingWindow } from "../../ui"; -// -// export class RoadNavigator extends Component implements UI { -// /** {@link Component.uuid} */ -// static readonly uuid = "85f2c89c-4c6b-4c7d-bc20-5b675874b228" as const; -// -// enabled = true; -// -// uiElement = new UIElement<{ -// horizontalAlignment: FloatingWindow; -// verticalAlignment: Drawer; -// }>(); -// -// private _selected: FragmentsGroup | null = null; -// -// private _anchor = new THREE.Vector3(); -// private _anchorID = "thatopen-roadnavigator-anchor"; -// -// private _anchors = { -// horizontal: new THREE.Vector2(), -// horizontalIndex: 0, -// real: new THREE.Vector3(), -// }; -// -// private _points: { -// horizontal: THREE.Points; -// }; -// -// private _scenes: { -// horizontal: Simple2DScene; -// vertical: Simple2DScene; -// }; -// -// private _alignments: { -// horizontal: THREE.LineSegments; -// vertical: THREE.LineSegments; -// real: THREE.LineSegments; -// }; -// -// private _caster = new THREE.Raycaster(); -// -// constructor(components: Components) { -// super(components); -// -// const threshold = 5; -// this._caster.params.Line = { threshold }; -// -// this.components.tools.add(RoadNavigator.uuid, this); -// -// this._scenes = { -// horizontal: new Simple2DScene(this.components, false), -// vertical: new Simple2DScene(this.components, false), -// }; -// -// this._points = { -// horizontal: new THREE.Points( -// new THREE.BufferGeometry(), -// new THREE.PointsMaterial({ -// size: 10, -// }) -// ), -// }; -// -// this._points.horizontal.frustumCulled = false; -// this._scenes.horizontal.scene.add(this._points.horizontal); -// -// this._alignments = { -// horizontal: new THREE.LineSegments( -// new THREE.BufferGeometry(), -// new THREE.LineBasicMaterial() -// ), -// vertical: new THREE.LineSegments( -// new THREE.BufferGeometry(), -// new THREE.LineBasicMaterial() -// ), -// real: new THREE.LineSegments( -// new THREE.BufferGeometry(), -// new THREE.LineBasicMaterial() -// ), -// }; -// -// this._alignments.real.frustumCulled = false; -// -// this._scenes.vertical.get().add(this._alignments.vertical); -// this._scenes.horizontal.get().add(this._alignments.horizontal); -// -// const scene = this.components.scene.get(); -// scene.add(this._alignments.real); -// -// const hRenderer = this._scenes.horizontal.renderer.get(); -// const hCamera = this._scenes.horizontal.camera; -// -// hRenderer.domElement.addEventListener("click", (event) => { -// if (!this._selected || !this._selected.ifcCivil) return; -// const lim = hRenderer.domElement.getBoundingClientRect(); -// const y = -((event.clientY - lim.top) / (lim.bottom - lim.top)) * 2 + 1; -// const x = ((event.clientX - lim.left) / (lim.right - lim.left)) * 2 - 1; -// const position = new THREE.Vector2(x, y); -// this._caster.setFromCamera(position, hCamera); -// const result = this._caster.intersectObject(this._alignments.horizontal); -// if (result.length) { -// const { index, point } = result[0]; -// if (index === undefined) return; -// -// const geom = this._alignments.horizontal.geometry; -// if (!geom.index) return; -// -// const pos = geom.attributes.position; -// const pointIndex1 = geom.index.array[index]; -// const pointIndex2 = geom.index.array[index + 1]; -// const x1 = pos.getX(pointIndex1); -// const y1 = pos.getY(pointIndex1); -// const x2 = pos.getX(pointIndex2); -// const y2 = pos.getY(pointIndex2); -// const dist1 = new THREE.Vector3(x1, y1, 0).distanceTo(point); -// const dist2 = new THREE.Vector3(x2, y2, 0).distanceTo(point); -// -// const isFirst = dist1 < dist2; -// const x = isFirst ? x1 : x2; -// const y = isFirst ? y1 : y2; -// -// this._anchors.horizontal.set(x, y); -// this._anchors.horizontalIndex = isFirst ? pointIndex1 : pointIndex2; -// -// const { horizontal } = this._points; -// const coordsBuffer = new Float32Array([x, y, 0]); -// const coordsAttr = new THREE.BufferAttribute(coordsBuffer, 3); -// horizontal.geometry.setAttribute("position", coordsAttr); -// -// let verticalIndex = -1; -// const alignmentIndex = -// this._selected.ifcCivil.horizontalAlignments.alignmentIndex; -// if (pointIndex1 >= alignmentIndex[alignmentIndex.length - 1]) { -// verticalIndex = alignmentIndex.length - 1; -// } else { -// for (let i = 0; i < alignmentIndex.length - 1; i++) { -// const start = alignmentIndex[i]; -// const end = alignmentIndex[i + 1]; -// if (pointIndex1 >= start && pointIndex1 < end) { -// verticalIndex = i; -// } -// } -// } -// -// this.getAlignmentGeometry( -// this._selected.ifcCivil.verticalAlignments, -// this._alignments.vertical.geometry, -// false, -// verticalIndex -// ); -// -// // const { alignmentIndex } = this._selected.ifcCivil.horizontalAlignments; -// // let counter = 0; -// // for (let i = 0; i < alignmentIndex.length; i++) { -// // const currentAlignment = alignmentIndex[i]; -// // if (currentAlignment > index) { -// // console.log(`Selected alignment: ${counter - 1}`); -// // break; -// // } -// // counter++; -// // } -// } -// }); -// -// if (this.components.uiEnabled) { -// this.setUI(); -// } -// } -// -// get() {} -// -// select(model: FragmentsGroup) { -// if (!model.ifcCivil) { -// console.warn("The provided model doesn't have civil data!"); -// return; -// } -// -// this._selected = model; -// -// this.getAlignmentGeometry( -// model.ifcCivil.horizontalAlignments, -// this._alignments.horizontal.geometry, -// false -// ); -// -// this.getAlignmentGeometry( -// model.ifcCivil.realAlignments, -// this._alignments.real.geometry, -// true -// ); -// } -// -// setAnchor() { -// if (!this._selected || !this._selected.ifcCivil) return; -// const result = this.components.raycaster.castRay([this._selected]); -// if (result === null) return; -// this._anchors.real.copy(result.point); -// const { horizontal, real, horizontalIndex } = this._anchors; -// -// const geom = this._alignments.real.geometry; -// const yPosition3D = geom.attributes.position.getY(horizontalIndex); -// -// this._anchor.x = real.x - horizontal.x; -// this._anchor.z = real.z + horizontal.y; -// this._anchor.y = real.y - yPosition3D; -// -// this.updateAnchor(); -// } -// -// saveAnchor() { -// const { x, y, z } = this._anchor; -// localStorage.setItem(this._anchorID, `${x}_${y}_${z}`); -// } -// -// loadAnchor() { -// const serialized = localStorage.getItem(this._anchorID); -// if (!serialized) return; -// const [x, y, z] = serialized.split("_").map((item) => parseFloat(item)); -// this._anchor.set(x, y, z); -// this.updateAnchor(); -// } -// -// private updateAnchor() { -// const position = this._alignments.real.position; -// position.copy(this._anchor); -// } -// -// private getAlignmentGeometry( -// alignment: IfcAlignmentData, -// geometry: THREE.BufferGeometry, -// is3D: boolean, -// selectedIndex: number = -1 -// ) { -// const data = this.getAlignmentData(alignment, is3D, selectedIndex); -// const coordsBuffer = new Float32Array(data.coords); -// const coordsAttr = new THREE.BufferAttribute(coordsBuffer, 3); -// geometry.setAttribute("position", coordsAttr); -// geometry.setIndex(data.index); -// } -// -// private getAlignmentData( -// alignment: IfcAlignmentData, -// is3D: boolean, -// selectedIndex: number = -1 -// ) { -// const coords: number[] = []; -// const index: number[] = []; -// const { coordinates, curveIndex } = alignment; -// const offsetX = coordinates[0]; -// const offsetY = coordinates[1]; -// const offsetZ = is3D ? coordinates[2] : 0; -// let isSegmentStart = true; -// const factor = is3D ? 3 : 2; -// const last = coordinates.length / factor - 1; -// if (selectedIndex === -1) { -// for (let i = 0; i < curveIndex.length; i++) { -// const start = curveIndex[i]; -// const isLast = i === curveIndex.length - 1; -// const end = isLast ? last : curveIndex[i + 1]; -// isSegmentStart = true; -// for (let j = start; j < end; j++) { -// const x = coordinates[j * factor] - offsetX; -// const y = coordinates[j * factor + 1] - offsetY; -// const z = is3D ? coordinates[j * factor + 2] - offsetZ : 0; -// coords.push(x, y, z); -// if (isSegmentStart) { -// isSegmentStart = false; -// } else { -// index.push(j - 1, j); -// } -// } -// } -// } else { -// let counter = 0; -// for (let i = 0; i < curveIndex.length; i++) { -// if (selectedIndex === this.currentAlignment(alignment, curveIndex[i])) { -// const start = curveIndex[i]; -// const isLast = i === curveIndex.length - 1; -// const end = isLast ? last : curveIndex[i + 1]; -// isSegmentStart = true; -// for (let j = start; j < end; j++) { -// const x = coordinates[j * factor] - offsetX; -// const y = coordinates[j * factor + 1] - offsetY; -// const z = is3D ? coordinates[j * factor + 2] - offsetZ : 0; -// coords.push(x, y, z); -// if (isSegmentStart) { -// isSegmentStart = false; -// } else { -// index.push(counter - 1, counter); -// } -// counter++; -// } -// } -// } -// } -// return { coords, index }; -// } -// -// private currentAlignment(alignment: IfcAlignmentData, curveIndex: number) { -// const last = alignment.alignmentIndex.length - 1; -// if (curveIndex >= alignment.alignmentIndex[last]) { -// return last; -// } -// for (let i = 0; i < alignment.alignmentIndex.length - 1; i++) { -// const start = alignment.alignmentIndex[i]; -// const end = alignment.alignmentIndex[i + 1]; -// if (curveIndex >= start && curveIndex < end) { -// return i; -// } -// } -// return -1; -// } -// -// private setUI() { -// const horizontalAlignment = new FloatingWindow(this.components); -// this.components.ui.add(horizontalAlignment); -// horizontalAlignment.visible = false; -// const hContainer = this._scenes.horizontal.uiElement.get("container"); -// horizontalAlignment.addChild(hContainer); -// -// horizontalAlignment.onResized.add(() => -// this._scenes.horizontal.grid.regenerate() -// ); -// -// horizontalAlignment.slots.content.domElement.style.padding = "0"; -// horizontalAlignment.slots.content.domElement.style.overflow = "hidden"; -// -// horizontalAlignment.onResized.add(() => { -// const { width, height } = horizontalAlignment.containerSize; -// this._scenes.horizontal.setSize(height, width); -// }); -// -// horizontalAlignment.domElement.style.width = "20rem"; -// horizontalAlignment.domElement.style.height = "20rem"; -// -// horizontalAlignment.onVisible.add(() => { -// if (horizontalAlignment.visible) { -// this._scenes.horizontal.grid.regenerate(); -// } -// }); -// -// const verticalAlignment = new Drawer(this.components); -// this.components.ui.add(verticalAlignment); -// verticalAlignment.alignment = "top"; -// -// verticalAlignment.onVisible.add(() => { -// this._scenes.vertical.grid.regenerate(); -// }); -// verticalAlignment.visible = false; -// -// verticalAlignment.slots.content.domElement.style.padding = "0"; -// verticalAlignment.slots.content.domElement.style.overflow = "hidden"; -// -// const { clientWidth, clientHeight } = verticalAlignment.domElement; -// this._scenes.vertical.setSize(clientHeight, clientWidth); -// -// const vContainer = this._scenes.vertical.uiElement.get("container"); -// verticalAlignment.addChild(vContainer); -// -// if (this.components.renderer.isUpdateable()) { -// this.components.renderer.onAfterUpdate.add(async () => { -// if (horizontalAlignment.visible) { -// await this._scenes.horizontal.update(); -// } -// if (verticalAlignment.visible) { -// await this._scenes.vertical.update(); -// } -// }); -// } -// -// this.uiElement.set({ -// horizontalAlignment, -// verticalAlignment, -// }); -// } -// } -// -// ToolComponent.libraryUUIDs.add(RoadNavigator.uuid); +import * as THREE from "three"; +import * as FRAGS from "bim-fragment"; +import { FragmentsGroup } from "bim-fragment"; +import { Component } from "../../base-types"; +import { Components, Simple2DScene } from "../../core"; + +export abstract class RoadNavigator extends Component { + enabled = true; + + caster = new THREE.Raycaster(); + + scene: Simple2DScene; + + abstract view: "horizontal" | "vertical"; + + protected _curves = new Set(); + + protected constructor(components: Components) { + super(components); + this.caster.params.Line = { threshold: 5 }; + this.scene = new Simple2DScene(this.components, false); + } + + get() { + return null as any; + } + + draw(model: FragmentsGroup, ids?: Iterable) { + if (!model.civilData) { + throw new Error("The provided model doesn't have civil data!"); + } + const { alignments } = model.civilData; + const allIDs = ids || alignments.keys(); + + const scene = this.scene.get(); + + for (const id of allIDs) { + const alignment = alignments.get(id); + if (!alignment) { + throw new Error("Alignment not found!"); + } + + let firstCurve = true; + + for (const curve of alignment[this.view]) { + this._curves.add(curve); + scene.add(curve.mesh); + + if (firstCurve) { + const pos = curve.mesh.geometry.attributes.position.array; + const [x, y, z] = pos; + this.scene.controls.target.set(x, y, z); + this.scene.camera.position.set(x, y, z + 10); + firstCurve = false; + } + } + } + } + + clear() { + for (const curve of this._curves) { + curve.mesh.removeFromParent(); + } + this._curves.clear(); + } +} diff --git a/src/civil/RoadNavigator/index.html b/src/civil/RoadPlanNavigator/index.html similarity index 82% rename from src/civil/RoadNavigator/index.html rename to src/civil/RoadPlanNavigator/index.html index e1a5114b5..56becbca8 100644 --- a/src/civil/RoadNavigator/index.html +++ b/src/civil/RoadPlanNavigator/index.html @@ -84,43 +84,38 @@ const data = await file.arrayBuffer(); const buffer = new Uint8Array(data); const model = await fragments.load(buffer); - const properties = await fetch("../../../resources/asdf.json"); - model.setLocalProperties(await properties.json()); - console.log(model); + // const properties = await fetch("../../../resources/asdf.json"); + // model.setLocalProperties(await properties.json()); + // console.log(model); const mainToolbar = new OBC.Toolbar(components, {name: 'Main Toolbar', position: 'bottom'}); components.ui.addToolbar(mainToolbar); mainToolbar.addChild(fragmentIfcLoader.uiElement.get("main")); - // Set up road navigator + console.log(model); - const navigator = new OBC.RoadNavigator(components); + // Set up road navigator - const horizontalWindow = navigator.uiElement.get("horizontalAlignment"); + const navigator = new OBC.RoadPlanNavigator(components); + const horizontalWindow = navigator.uiElement.get("floatingWindow"); horizontalWindow.visible = true; - - const verticalWindow = navigator.uiElement.get("verticalAlignment"); - verticalWindow.visible = true; - - navigator.select(model); - - window.addEventListener("keydown", (event) => { - if(event.code === "KeyS") { - navigator.saveAnchor(); - } - if(event.code === "KeyP") { - navigator.setAnchor(); + navigator.draw(model); + + let first = true; + const mat = new THREE.Matrix4(); + for(const [id, alignment] of model.civilData.alignments) { + for(const curve of alignment.absolute) { + if(first) { + const pos = curve.mesh.geometry.attributes.position.array; + const [x, y, z] = pos; + mat.makeTranslation(-x, -y, -z); + first = false; + } + curve.mesh.applyMatrix4(mat); + scene.add(curve.mesh); } - }) - - window.ondblclick = () => { - const result = components.raycaster.castRay(); - console.log(result); } - navigator.loadAnchor(); - - // Set up stats const stats = new Stats(); diff --git a/src/civil/RoadPlanNavigator/index.ts b/src/civil/RoadPlanNavigator/index.ts new file mode 100644 index 000000000..0e88f9586 --- /dev/null +++ b/src/civil/RoadPlanNavigator/index.ts @@ -0,0 +1,56 @@ +import { UI, UIElement } from "../../base-types"; +import { FloatingWindow } from "../../ui"; +import { Components } from "../../core"; +import { RoadNavigator } from "../RoadNavigator"; + +export class RoadPlanNavigator extends RoadNavigator implements UI { + static readonly uuid = "3096dea0-5bc2-41c7-abce-9089b6c9431b" as const; + + readonly view = "horizontal"; + + uiElement = new UIElement<{ + floatingWindow: FloatingWindow; + }>(); + + constructor(components: Components) { + super(components); + this.setUI(); + } + + private setUI() { + const floatingWindow = new FloatingWindow(this.components); + this.components.ui.add(floatingWindow); + floatingWindow.visible = false; + const hContainer = this.scene.uiElement.get("container"); + floatingWindow.addChild(hContainer); + + floatingWindow.onResized.add(() => this.scene.grid.regenerate()); + + floatingWindow.slots.content.domElement.style.padding = "0"; + floatingWindow.slots.content.domElement.style.overflow = "hidden"; + + floatingWindow.onResized.add(() => { + const { width, height } = floatingWindow.containerSize; + this.scene.setSize(height, width); + }); + + floatingWindow.domElement.style.width = "20rem"; + floatingWindow.domElement.style.height = "20rem"; + + floatingWindow.onVisible.add(() => { + if (floatingWindow.visible) { + this.scene.grid.regenerate(); + } + }); + + if (this.components.renderer.isUpdateable()) { + this.components.renderer.onAfterUpdate.add(async () => { + if (floatingWindow.visible) { + await this.scene.update(); + } + }); + } + + this.uiElement.set({ floatingWindow }); + } +} diff --git a/src/civil/index.ts b/src/civil/index.ts index 6b2bfd218..4077cda9d 100644 --- a/src/civil/index.ts +++ b/src/civil/index.ts @@ -1,2 +1,3 @@ -// export * from "./RoadNavigator"; -// export * from "./RoadElevationNavigator"; +export * from "./RoadPlanNavigator"; +export * from "./RoadElevationNavigator"; +export * from "./RoadNavigator"; diff --git a/src/core/Simple2DScene/index.ts b/src/core/Simple2DScene/index.ts index 4eaa0a07c..b4e8010f8 100644 --- a/src/core/Simple2DScene/index.ts +++ b/src/core/Simple2DScene/index.ts @@ -66,6 +66,10 @@ export class Simple2DScene private readonly _size = new THREE.Vector2(); private readonly _frustumSize = 50; + get size() { + return this._size.clone(); + } + get scaleX() { return this._scaleX; } diff --git a/src/index.ts b/src/index.ts index 3d1cccd35..7e2ac1e49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,4 +9,4 @@ export * from "./utils"; export * from "./ifc"; export * from "./measurement"; export * from "./import-export"; -// export * from "./civil"; +export * from "./civil";