@@ -107734,12 +107734,194 @@ class MiniMap extends Component {
107734107734MiniMap.uuid = "39ad6aad-84c8-4adf-a1e0-7f25313a9e7f";
107735107735ToolComponent.libraryUUIDs.add(MiniMap.uuid);
107736107736
107737+ /**
107738+ * An infinite lightweight 2D grid that can be used for any
107739+ * kind of 2d viewports.
107740+ */
107741+ class Infinite2dGrid {
107742+ constructor(camera, container) {
107743+ this.numbers = new THREE$1.Group();
107744+ this.maxRegenerateRetrys = 4;
107745+ this.gridsFactor = 5;
107746+ this.scaleX = 1;
107747+ this.scaleY = 1;
107748+ this._group = new THREE$1.Group();
107749+ this._frustum = new THREE$1.Frustum();
107750+ this._frustumMat = new THREE$1.Matrix4();
107751+ this._regenerateDelay = 200;
107752+ this._regenerateCounter = 0;
107753+ this._camera = camera;
107754+ this._container = container;
107755+ const main = this.newGrid(0x222222, -1);
107756+ const secondary = this.newGrid(0x111111, -2);
107757+ this.grids = { main, secondary };
107758+ this._group.add(secondary, main, this.numbers);
107759+ }
107760+ get() {
107761+ return this._group;
107762+ }
107763+ dispose() {
107764+ const { main, secondary } = this.grids;
107765+ main.removeFromParent();
107766+ secondary.removeFromParent();
107767+ main.geometry.dispose();
107768+ const mMat = main.material;
107769+ mMat.dispose();
107770+ secondary.geometry.dispose();
107771+ const sMat = secondary.material;
107772+ sMat.dispose();
107773+ }
107774+ regenerate() {
107775+ const isReady = this.isGridReady();
107776+ if (!isReady) {
107777+ this._regenerateCounter++;
107778+ if (this._regenerateCounter > this.maxRegenerateRetrys) {
107779+ throw new Error("Grid could not be regenerated");
107780+ }
107781+ setTimeout(() => this.regenerate, this._regenerateDelay);
107782+ return;
107783+ }
107784+ this._regenerateCounter = 0;
107785+ const matrix = this._frustumMat.multiplyMatrices(this._camera.projectionMatrix, this._camera.matrixWorldInverse);
107786+ this._frustum.setFromProjectionMatrix(matrix);
107787+ // Step 1: find out the distance of the visible area of the 2D scene
107788+ // and the translation pixel / 3d unit
107789+ const { planes } = this._frustum;
107790+ const right = planes[0].constant * -planes[0].normal.x;
107791+ const left = planes[1].constant * -planes[1].normal.x;
107792+ const bottom = planes[2].constant * -planes[2].normal.y;
107793+ const top = planes[3].constant * -planes[3].normal.y;
107794+ const horizontalDistance = Math.abs(right - left);
107795+ const verticalDistance = Math.abs(top - bottom);
107796+ const { clientWidth, clientHeight } = this._container;
107797+ const maxPixelDist = Math.max(clientWidth, clientHeight);
107798+ const maxUnit3dDist = Math.max(horizontalDistance, verticalDistance);
107799+ const unit3dPixelRel = maxUnit3dDist / maxPixelDist;
107800+ // Step 2: find out its order of magnitude
107801+ const magnitudeX = Math.ceil(Math.log10(horizontalDistance / this.scaleX));
107802+ const magnitudeY = Math.ceil(Math.log10(verticalDistance / this.scaleY));
107803+ // Step 3: represent main grid
107804+ const sDistanceHor = 10 ** (magnitudeX - 2) * this.scaleX;
107805+ const sDistanceVert = 10 ** (magnitudeY - 2) * this.scaleY;
107806+ const mDistanceHor = sDistanceHor * this.gridsFactor;
107807+ const mDistanceVert = sDistanceVert * this.gridsFactor;
107808+ const mainGridCountVert = Math.ceil(verticalDistance / mDistanceVert);
107809+ const mainGridCountHor = Math.ceil(horizontalDistance / mDistanceHor);
107810+ const secondaryGridCountVert = Math.ceil(verticalDistance / sDistanceVert);
107811+ const secondaryGridCountHor = Math.ceil(horizontalDistance / sDistanceHor);
107812+ // Step 4: find out position of first lines
107813+ const sTrueLeft = sDistanceHor * Math.ceil(left / sDistanceHor);
107814+ const sTrueBottom = sDistanceVert * Math.ceil(bottom / sDistanceVert);
107815+ const mTrueLeft = mDistanceHor * Math.ceil(left / mDistanceHor);
107816+ const mTrueBottom = mDistanceVert * Math.ceil(bottom / mDistanceVert);
107817+ // Step 5: draw lines and texts
107818+ const numbers = [...this.numbers.children];
107819+ for (const number of numbers) {
107820+ number.removeFromParent();
107821+ }
107822+ this.numbers.children = [];
107823+ const mPoints = [];
107824+ for (let i = 0; i < mainGridCountHor; i++) {
107825+ const offset = mTrueLeft + i * mDistanceHor;
107826+ mPoints.push(offset, top, 0, offset, bottom, 0);
107827+ const sign = this.newNumber(offset / this.scaleX);
107828+ const textOffsetPixels = 12;
107829+ const textOffset = textOffsetPixels * unit3dPixelRel;
107830+ sign.position.set(offset, bottom + textOffset, 0);
107831+ }
107832+ for (let i = 0; i < mainGridCountVert; i++) {
107833+ const offset = mTrueBottom + i * mDistanceVert;
107834+ mPoints.push(left, offset, 0, right, offset, 0);
107835+ const sign = this.newNumber(offset / this.scaleY);
107836+ let textOffsetPixels = 12;
107837+ if (sign.element.textContent) {
107838+ textOffsetPixels += 4 * sign.element.textContent.length;
107839+ }
107840+ const textOffset = textOffsetPixels * unit3dPixelRel;
107841+ sign.position.set(left + textOffset, offset, 0);
107842+ }
107843+ const sPoints = [];
107844+ for (let i = 0; i < secondaryGridCountHor; i++) {
107845+ const offset = sTrueLeft + i * sDistanceHor;
107846+ sPoints.push(offset, top, 0, offset, bottom, 0);
107847+ }
107848+ for (let i = 0; i < secondaryGridCountVert; i++) {
107849+ const offset = sTrueBottom + i * sDistanceVert;
107850+ sPoints.push(left, offset, 0, right, offset, 0);
107851+ }
107852+ const mIndices = [];
107853+ const sIndices = [];
107854+ this.fillIndices(mPoints, mIndices);
107855+ this.fillIndices(sPoints, sIndices);
107856+ const mBuffer = new THREE$1.BufferAttribute(new Float32Array(mPoints), 3);
107857+ const sBuffer = new THREE$1.BufferAttribute(new Float32Array(sPoints), 3);
107858+ const { main, secondary } = this.grids;
107859+ main.geometry.setAttribute("position", mBuffer);
107860+ main.geometry.setIndex(mIndices);
107861+ secondary.geometry.setAttribute("position", sBuffer);
107862+ secondary.geometry.setIndex(sIndices);
107863+ }
107864+ fillIndices(points, indices) {
107865+ for (let i = 0; i < points.length / 2 - 1; i += 2) {
107866+ indices.push(i, i + 1);
107867+ }
107868+ }
107869+ newNumber(offset) {
107870+ const text = document.createElement("div");
107871+ text.textContent = `${offset}`;
107872+ if (text.textContent.length > 6) {
107873+ text.textContent = text.textContent.slice(0, 6);
107874+ }
107875+ text.style.height = "24px";
107876+ text.style.fontSize = "12px";
107877+ const sign = new CSS2DObject(text);
107878+ this.numbers.add(sign);
107879+ return sign;
107880+ }
107881+ newGrid(color, renderOrder) {
107882+ const geometry = new THREE$1.BufferGeometry();
107883+ const material = new THREE$1.LineBasicMaterial({ color });
107884+ const grid = new THREE$1.LineSegments(geometry, material);
107885+ grid.frustumCulled = false;
107886+ grid.renderOrder = renderOrder;
107887+ return grid;
107888+ }
107889+ isGridReady() {
107890+ const nums = this._camera.projectionMatrix.elements;
107891+ for (let i = 0; i < nums.length; i++) {
107892+ const num = nums[i];
107893+ if (Number.isNaN(num)) {
107894+ return false;
107895+ }
107896+ }
107897+ return true;
107898+ }
107899+ }
107900+
107737107901// TODO: Make a scene manager as a Tool (so that it as an UUID)
107738107902/**
107739107903 * A simple floating 2D scene that you can use to easily draw 2D graphics
107740107904 * with all the power of Three.js.
107741107905 */
107742107906class Simple2DScene extends Component {
107907+ get scaleX() {
107908+ return this._scaleX;
107909+ }
107910+ set scaleX(value) {
107911+ this._scaleX = value;
107912+ this._root.scale.x = value;
107913+ this.grid.scaleX = value;
107914+ this.grid.regenerate();
107915+ }
107916+ get scaleY() {
107917+ return this._scaleY;
107918+ }
107919+ set scaleY(value) {
107920+ this._scaleY = value;
107921+ this._root.scale.y = value;
107922+ this.grid.scaleY = value;
107923+ this.grid.regenerate();
107924+ }
107743107925 constructor(components, postproduction = false) {
107744107926 super(components);
107745107927 /** {@link Updateable.onAfterUpdate} */
@@ -107752,6 +107934,9 @@ class Simple2DScene extends Component {
107752107934 this.enabled = true;
107753107935 /** {@link UI.uiElement} */
107754107936 this.uiElement = new UIElement();
107937+ this._scaleX = 1;
107938+ this._scaleY = 1;
107939+ this._root = new THREE$1.Group();
107755107940 this._size = new THREE$1.Vector2();
107756107941 this._frustumSize = 50;
107757107942 /** {@link Resizeable.resize} */
@@ -107769,55 +107954,49 @@ class Simple2DScene extends Component {
107769107954 if (!components.uiEnabled) {
107770107955 throw new Error("The Simple2DScene component needs to use UI elements (TODO: Decouple from them).");
107771107956 }
107772- const canvas = new Canvas (components);
107773- canvas .domElement.classList.remove("absolute ");
107774- this.uiElement.set({ canvas });
107775- this._scene = new THREE$1.Scene();
107957+ const container = new SimpleUIComponent (components);
107958+ container .domElement.classList.add("relative ");
107959+ this.uiElement.set({ container });
107960+ this.scene = new THREE$1.Scene();
107776107961 this._size.set(window.innerWidth, window.innerHeight);
107777107962 const { width, height } = this._size;
107778107963 // Creates the camera (point of view of the user)
107779107964 this.camera = new THREE$1.OrthographicCamera(75, width / height);
107780- this._scene .add(this.camera);
107965+ this.scene .add(this.camera);
107781107966 this.camera.position.z = 10;
107967+ const domContainer = container.domElement;
107968+ this.scene.add(this._root);
107969+ this.grid = new Infinite2dGrid(this.camera, domContainer);
107970+ const gridObject = this.grid.get();
107971+ this.scene.add(gridObject);
107782107972 if (postproduction) {
107783- this.renderer = new PostproductionRenderer(this.components, undefined, {
107784- canvas: canvas.get(),
107785- });
107973+ this.renderer = new PostproductionRenderer(this.components, domContainer);
107786107974 }
107787107975 else {
107788- this.renderer = new SimpleRenderer(this.components, undefined, {
107789- canvas: canvas.get(),
107790- });
107976+ this.renderer = new SimpleRenderer(this.components, domContainer);
107791107977 }
107792107978 const renderer = this.renderer.get();
107793107979 renderer.localClippingEnabled = false;
107794107980 this.renderer.setupEvents(false);
107795- this.renderer.overrideScene = this._scene ;
107981+ this.renderer.overrideScene = this.scene ;
107796107982 this.renderer.overrideCamera = this.camera;
107797107983 this.controls = new OrbitControls(this.camera, renderer.domElement);
107798107984 this.controls.target.set(0, 0, 0);
107799107985 this.controls.enableRotate = false;
107800107986 this.controls.enableZoom = true;
107801- const parent = this.uiElement.get("canvas").parent;
107802- if (parent) {
107803- parent.domElement.classList.remove("p-4");
107804- parent.domElement.classList.remove("overflow-auto");
107805- parent.domElement.classList.add("overflow-hidden");
107806- parent.domElement.classList.add("h-full");
107807- }
107808- // Creates the orbit controls (to navigate the scene)
107987+ this.controls.addEventListener("change", () => this.grid.regenerate());
107809107988 }
107810107989 /**
107811107990 * {@link Component.get}
107812107991 * @returns the 2D scene.
107813107992 */
107814107993 get() {
107815- return this._scene ;
107994+ return this._root ;
107816107995 }
107817107996 /** {@link Disposable.dispose} */
107818107997 async dispose() {
107819107998 const disposer = await this.components.tools.get(Disposer);
107820- for (const child of this._scene .children) {
107999+ for (const child of this.scene .children) {
107821108000 const item = child;
107822108001 if (item instanceof THREE$1.Object3D) {
107823108002 disposer.destroy(item);
@@ -115150,7 +115329,7 @@ class RoadNavigator extends Component {
115150115329 const { scene2d } = this.newFloating2DScene("Floorplan", true);
115151115330 const { postproduction } = scene2d.renderer;
115152115331 postproduction.overrideClippingPlanes = true;
115153- postproduction.overrideScene = scene2d.get() ;
115332+ postproduction.overrideScene = scene2d.scene ;
115154115333 postproduction.overrideCamera = scene2d.camera;
115155115334 postproduction.enabled = true;
115156115335 scene2d.camera.position.set(0, 20, 0);
@@ -115167,7 +115346,7 @@ class RoadNavigator extends Component {
115167115346 this.components.ui.add(floatingWindow);
115168115347 floatingWindow.title = title;
115169115348 const scene2d = new Simple2DScene(this.components, postproduction);
115170- const canvasUIElement = scene2d.uiElement.get("canvas ");
115349+ const canvasUIElement = scene2d.uiElement.get("container ");
115171115350 floatingWindow.addChild(canvasUIElement);
115172115351 const style = floatingWindow.slots.content.domElement.style;
115173115352 style.padding = "0";
@@ -115179,7 +115358,7 @@ class RoadNavigator extends Component {
115179115358 scene2d.setSize(clientHeight, clientWidth);
115180115359 await scene2d.update();
115181115360 });
115182- const canvas = scene2d.uiElement.get("canvas ");
115361+ const canvas = scene2d.uiElement.get("container ");
115183115362 canvas.domElement.addEventListener("mousemove", async () => {
115184115363 await scene2d.update();
115185115364 });
@@ -115196,7 +115375,7 @@ class RoadNavigator extends Component {
115196115375 this.components.ui.add(drawer);
115197115376 drawer.alignment = "top";
115198115377 const scene2d = new Simple2DScene(this.components);
115199- const canvasUIElement = scene2d.uiElement.get("canvas ");
115378+ const canvasUIElement = scene2d.uiElement.get("container ");
115200115379 drawer.addChild(canvasUIElement);
115201115380 const { clientHeight, clientWidth } = drawer.domElement;
115202115381 const windowStyle = drawer.slots.content.domElement.style;
0 commit comments