Work on compass
This commit is contained in:
parent
b94151bc01
commit
63fc0d1187
4 changed files with 176 additions and 65 deletions
|
@ -25,8 +25,8 @@ export default function Home() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex min-h-0 h-full">
|
<div className="flex-1 flex min-h-0 h-full">
|
||||||
<div className="flex-1">
|
<div className="relative flex-1">
|
||||||
<div className="hidden sm:block absolute top-2 left-2">
|
<div className="hidden sm:block absolute top-2 right-2">
|
||||||
<ResetView></ResetView>
|
<ResetView></ResetView>
|
||||||
</div>
|
</div>
|
||||||
<Map></Map>
|
<Map></Map>
|
||||||
|
|
|
@ -19,7 +19,10 @@ import {
|
||||||
Orientation,
|
Orientation,
|
||||||
buildClippingplanes,
|
buildClippingplanes,
|
||||||
} from "./utils/build-clipping-planes";
|
} from "./utils/build-clipping-planes";
|
||||||
import { buildCoordinateGrid } from "./utils/build-coordinate-grid";
|
import {
|
||||||
|
buildCoordinateGrid,
|
||||||
|
buildHeightGrid,
|
||||||
|
} from "./utils/build-coordinate-grid";
|
||||||
import {
|
import {
|
||||||
DragControls,
|
DragControls,
|
||||||
OBJExporter,
|
OBJExporter,
|
||||||
|
@ -371,9 +374,15 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||||
|
|
||||||
// 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 annotationsGroup = new Group();
|
const annotationsGroup = new Group();
|
||||||
annotationsGroup.name = "coordinate-grid";
|
annotationsGroup.name = "coordinate-grid";
|
||||||
annotationsGroup.add(...annotations, gridHelper);
|
annotationsGroup.add(
|
||||||
|
...annotations,
|
||||||
|
gridHelper,
|
||||||
|
...heightAnnotations,
|
||||||
|
heightGridHelper
|
||||||
|
);
|
||||||
annotationsGroup.visible = false;
|
annotationsGroup.visible = false;
|
||||||
scene.add(annotationsGroup);
|
scene.add(annotationsGroup);
|
||||||
|
|
||||||
|
|
|
@ -1,118 +1,157 @@
|
||||||
import {
|
import {
|
||||||
|
BufferGeometry,
|
||||||
CanvasTexture,
|
CanvasTexture,
|
||||||
GridHelper,
|
Group,
|
||||||
|
Line,
|
||||||
|
LineBasicMaterial,
|
||||||
Sprite,
|
Sprite,
|
||||||
SpriteMaterial,
|
SpriteMaterial,
|
||||||
Vector4,
|
Vector3,
|
||||||
} from "three";
|
} from "three";
|
||||||
import { Extent } from "./build-scene";
|
import { Extent } from "./build-scene";
|
||||||
import { getCenter3D } from "./utils";
|
|
||||||
|
|
||||||
enum Orientation {
|
enum Orientation {
|
||||||
Horizontal,
|
X,
|
||||||
Vertical,
|
Y,
|
||||||
|
Z,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildCoordinateGrid(extent: Extent) {
|
export function buildCoordinateGrid(extent: Extent) {
|
||||||
const center = getCenter3D(extent);
|
|
||||||
// Calculate the width and height of the grid
|
// Calculate the width and height of the grid
|
||||||
const gridWidth = extent.xmax - extent.xmin;
|
const gridWidth = extent.xmax - extent.xmin;
|
||||||
const gridHeight = extent.ymax - extent.ymin;
|
const gridHeight = extent.ymax - extent.ymin;
|
||||||
|
|
||||||
// Decide on the number of divisions (e.g., 20 divisions along each axis)
|
// Decide on the number of divisions
|
||||||
const divisions = 20;
|
const divisions = 10;
|
||||||
|
|
||||||
// Create a grid helper with the calculated grid size and divisions
|
const xOffset = gridWidth / divisions;
|
||||||
const gridHelper = new GridHelper(Math.max(gridWidth, gridHeight), divisions);
|
let x = extent.xmin - xOffset;
|
||||||
|
const xPairs = [];
|
||||||
// Position the grid in the scene to match the given extent
|
for (let i = 0; i < divisions + 1; i++) {
|
||||||
gridHelper.position.set(center.x, center.y, 0);
|
x += xOffset;
|
||||||
|
const start = new Vector3(x, extent.ymin, extent.zmin);
|
||||||
// Rotate the grid to align with the XY-plane
|
const end = new Vector3(x, extent.ymax, extent.zmin);
|
||||||
gridHelper.rotation.x = Math.PI / 2;
|
xPairs.push([start, end]);
|
||||||
|
|
||||||
// Retrieve the geometry of the grid helper
|
|
||||||
const geometry = gridHelper.geometry;
|
|
||||||
|
|
||||||
const positionAttr = geometry.getAttribute("position");
|
|
||||||
const startingPointsHorizontal = [];
|
|
||||||
const startingPointsVertical = [];
|
|
||||||
for (let i = 0; i < positionAttr.count; i++) {
|
|
||||||
const x = positionAttr.getX(i);
|
|
||||||
const z = positionAttr.getZ(i);
|
|
||||||
const v = new Vector4(x + center.x, z + center.y, 0, 1);
|
|
||||||
|
|
||||||
if (i % 4 === 0) {
|
|
||||||
startingPointsVertical.push(v);
|
|
||||||
} else if (i % 2 == 0) {
|
|
||||||
startingPointsHorizontal.push(v);
|
|
||||||
}
|
}
|
||||||
|
const xLines = createLines(xPairs);
|
||||||
|
|
||||||
|
const yOffset = gridHeight / divisions;
|
||||||
|
let y = extent.ymin - yOffset;
|
||||||
|
const yPairs = [];
|
||||||
|
for (let i = 0; i < divisions + 1; i++) {
|
||||||
|
y += yOffset;
|
||||||
|
const start = new Vector3(extent.xmin, y, extent.zmin);
|
||||||
|
const end = new Vector3(extent.xmax, y, extent.zmin);
|
||||||
|
yPairs.push([start, end]);
|
||||||
}
|
}
|
||||||
|
const yLines = createLines(yPairs);
|
||||||
|
|
||||||
const annotations = [];
|
const annotations = [];
|
||||||
for (const point of startingPointsHorizontal) {
|
for (let i = 0; i < xPairs.length - 1; i++) {
|
||||||
const label = createLabel(
|
const [start, _] = xPairs[i];
|
||||||
`${point.x.toFixed(2)}`,
|
const label = createLabel(`${start.x.toFixed(0)}m`, start, Orientation.X);
|
||||||
point,
|
|
||||||
Orientation.Horizontal
|
|
||||||
);
|
|
||||||
annotations.push(label);
|
annotations.push(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const point of startingPointsVertical) {
|
for (let i = 0; i < yPairs.length - 1; i++) {
|
||||||
const label = createLabel(
|
const [start, _] = yPairs[i];
|
||||||
`${point.y.toFixed(2)}`,
|
const label = createLabel(`${start.y.toFixed(0)}m`, start, Orientation.Y);
|
||||||
point,
|
|
||||||
Orientation.Vertical
|
|
||||||
);
|
|
||||||
annotations.push(label);
|
annotations.push(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gridHelper = new Group();
|
||||||
|
gridHelper.add(...xLines, ...yLines);
|
||||||
|
|
||||||
return { gridHelper, annotations };
|
return { gridHelper, annotations };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildHeightGrid(extent: Extent) {
|
||||||
|
const gridHeight = extent.zmax - extent.zmin;
|
||||||
|
|
||||||
|
const divisions = 5;
|
||||||
|
const offset = gridHeight / divisions;
|
||||||
|
|
||||||
|
let z = extent.zmin - offset;
|
||||||
|
const pointPairs = [];
|
||||||
|
for (let i = 0; i < divisions + 1; i++) {
|
||||||
|
z += offset;
|
||||||
|
const start = new Vector3(extent.xmax, extent.ymin, z);
|
||||||
|
const end = new Vector3(extent.xmax, extent.ymax, z);
|
||||||
|
pointPairs.push([start, end]);
|
||||||
|
}
|
||||||
|
const lines = createLines(pointPairs);
|
||||||
|
|
||||||
|
const annotations = [];
|
||||||
|
for (const pointPair of pointPairs) {
|
||||||
|
const start = pointPair[0];
|
||||||
|
const label = createLabel(`${start.z.toFixed(0)}m`, start, Orientation.Z);
|
||||||
|
annotations.push(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gridHelper = new Group();
|
||||||
|
gridHelper.add(...lines);
|
||||||
|
|
||||||
|
return { heightGridHelper: gridHelper, heightAnnotations: annotations };
|
||||||
|
}
|
||||||
|
|
||||||
// Function to create annotation (sprite with text)
|
// Function to create annotation (sprite with text)
|
||||||
function createLabel(
|
function createLabel(
|
||||||
text: string,
|
text: string,
|
||||||
position: Vector4,
|
position: Vector3,
|
||||||
orientation: Orientation
|
orientation: Orientation
|
||||||
) {
|
) {
|
||||||
const spriteMaterial = new SpriteMaterial({
|
const spriteMaterial = new SpriteMaterial({
|
||||||
map: new CanvasTexture(generateTextCanvas(text, orientation)), // Create text texture
|
map: new CanvasTexture(generateTextCanvas(text)), // Create text texture
|
||||||
transparent: true,
|
transparent: true,
|
||||||
});
|
});
|
||||||
const sprite = new Sprite(spriteMaterial);
|
const sprite = new Sprite(spriteMaterial);
|
||||||
|
|
||||||
// Set position according to axis orientation
|
// Set position according to axis orientation
|
||||||
if (orientation === Orientation.Horizontal) {
|
if (orientation === Orientation.X) {
|
||||||
sprite.position.set(position.x + 1000, position.y - 1500, position.z + 500);
|
sprite.position.set(position.x + 500, position.y - 1500, position.z + 500);
|
||||||
|
} else if (orientation === Orientation.Y) {
|
||||||
|
sprite.position.set(position.x - 3000, position.y + 500, position.z + 500);
|
||||||
} else {
|
} else {
|
||||||
sprite.position.set(position.x, position.y - 500, position.z + 500);
|
sprite.position.set(position.x + 3000, position.y, position.z + 500);
|
||||||
}
|
}
|
||||||
sprite.scale.set(5000, 2500, 1); // Scale the sprite to make the text readable
|
sprite.scale.set(5000, 2500, 1);
|
||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a text canvas for the annotation
|
// Function to generate a text canvas for the annotation
|
||||||
function generateTextCanvas(text: string, orientation: Orientation) {
|
function generateTextCanvas(text: string) {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
const context = canvas.getContext("2d");
|
const context = canvas.getContext("2d");
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
canvas.width = 800;
|
const width = 800;
|
||||||
canvas.height = 160;
|
const height = 200;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
// Set the text style
|
// Set the text style
|
||||||
context.font = "45px Arial";
|
context.font = `${height - 30}px Arial`;
|
||||||
context.fillStyle = "black";
|
context.fillStyle = "black";
|
||||||
|
|
||||||
if (orientation === Orientation.Horizontal) {
|
context.fillText(text, 0, height - 15);
|
||||||
//context.fillText(text, 300, 160);
|
|
||||||
context.fillText(text, 100, 90);
|
|
||||||
} else {
|
|
||||||
context.fillText(text, 100, 90);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createLines(pointPairs: Vector3[][]) {
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
for (const pair of pointPairs) {
|
||||||
|
const geometry = new BufferGeometry().setFromPoints(pair);
|
||||||
|
|
||||||
|
// Line material
|
||||||
|
const material = new LineBasicMaterial({ color: 0x444444 });
|
||||||
|
|
||||||
|
// Create line
|
||||||
|
const line = new Line(geometry, material);
|
||||||
|
lines.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,13 @@ import {
|
||||||
DirectionalLight,
|
DirectionalLight,
|
||||||
Group,
|
Group,
|
||||||
Object3D,
|
Object3D,
|
||||||
|
AxesHelper,
|
||||||
|
OrthographicCamera,
|
||||||
|
Camera,
|
||||||
|
CanvasTexture,
|
||||||
|
SpriteMaterial,
|
||||||
|
Sprite,
|
||||||
|
Euler,
|
||||||
} from "three";
|
} from "three";
|
||||||
|
|
||||||
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
||||||
|
@ -24,6 +31,9 @@ let controls: OrbitControls;
|
||||||
let renderer: WebGLRenderer;
|
let renderer: WebGLRenderer;
|
||||||
let camera: PerspectiveCamera;
|
let camera: PerspectiveCamera;
|
||||||
let scene: Scene;
|
let scene: Scene;
|
||||||
|
let axesHelper: AxesHelper;
|
||||||
|
let uiCamera: Camera;
|
||||||
|
let uiScene: Scene;
|
||||||
export function buildScene(container: HTMLElement, extent: Extent) {
|
export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
const maxSize = getMaxSize(extent);
|
const maxSize = getMaxSize(extent);
|
||||||
const center = getCenter3D(extent);
|
const center = getCenter3D(extent);
|
||||||
|
@ -61,6 +71,7 @@ export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
controls = new OrbitControls(camera, renderer.domElement);
|
controls = new OrbitControls(camera, renderer.domElement);
|
||||||
controls.target.set(center.x, center.y, center.z); // Focus on the center
|
controls.target.set(center.x, center.y, center.z); // Focus on the center
|
||||||
controls.enableDamping = true; // Smooth camera movement
|
controls.enableDamping = true; // Smooth camera movement
|
||||||
|
controls.dampingFactor = 0.1;
|
||||||
controls.maxDistance = maxSize * 3;
|
controls.maxDistance = maxSize * 3;
|
||||||
controls.minDistance = maxSize / 5;
|
controls.minDistance = maxSize / 5;
|
||||||
controls.update();
|
controls.update();
|
||||||
|
@ -73,6 +84,27 @@ export function buildScene(container: HTMLElement, extent: Extent) {
|
||||||
// Add lights to the scene
|
// Add lights to the scene
|
||||||
buildDefaultLights(scene, extent);
|
buildDefaultLights(scene, extent);
|
||||||
|
|
||||||
|
uiScene = new Scene();
|
||||||
|
|
||||||
|
// Create an orthographic camera
|
||||||
|
uiCamera = new OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
|
||||||
|
uiCamera.up.set(0, 0, 1);
|
||||||
|
uiCamera.position.z = 2;
|
||||||
|
|
||||||
|
// Create the AxesHelper (small size)
|
||||||
|
axesHelper = new AxesHelper(0.1);
|
||||||
|
axesHelper.position.set(-0.9, -0.8, 0);
|
||||||
|
|
||||||
|
const xLabel = createTextSprite("X", "red");
|
||||||
|
const yLabel = createTextSprite("Y", "green");
|
||||||
|
const zLabel = createTextSprite("Z", "blue");
|
||||||
|
xLabel.position.set(0.125, 0, 0);
|
||||||
|
yLabel.position.set(0, 0.125, 0);
|
||||||
|
zLabel.position.set(0, 0, 0.125);
|
||||||
|
|
||||||
|
axesHelper.add(xLabel, yLabel, zLabel);
|
||||||
|
uiScene.add(axesHelper);
|
||||||
|
|
||||||
return { renderer, scene, camera, controls };
|
return { renderer, scene, camera, controls };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +120,16 @@ function onWindowResize(container: HTMLElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
|
// axesHelper.quaternion.copy(camera.quaternion);
|
||||||
|
let rot = new Euler();
|
||||||
|
rot.x = -camera.rotation.x;
|
||||||
|
rot.y = camera.rotation.y;
|
||||||
|
rot.z = camera.rotation.z;
|
||||||
|
axesHelper.setRotationFromEuler(rot);
|
||||||
|
renderer.autoClear = true;
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
|
renderer.autoClear = false;
|
||||||
|
renderer.render(uiScene, uiCamera); // Render UI scene
|
||||||
|
|
||||||
// required if controls.enableDamping or controls.autoRotate are set to true
|
// required if controls.enableDamping or controls.autoRotate are set to true
|
||||||
controls.update();
|
controls.update();
|
||||||
|
@ -131,3 +172,25 @@ function buildDefaultLights(scene: Scene, extent: Extent) {
|
||||||
lightsGroup.add(...lights);
|
lightsGroup.add(...lights);
|
||||||
scene.add(lightsGroup);
|
scene.add(lightsGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTextSprite(text: string, color = "white") {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
canvas.width = 256;
|
||||||
|
canvas.height = 128;
|
||||||
|
|
||||||
|
if (ctx) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const texture = new CanvasTexture(canvas);
|
||||||
|
const material = new SpriteMaterial({ map: texture });
|
||||||
|
const sprite = new Sprite(material);
|
||||||
|
sprite.scale.set(0.3, 0.15, 1);
|
||||||
|
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue