Skip to content

Commit

Permalink
Merge pull request #26 from andrewisen-tikab/feature
Browse files Browse the repository at this point in the history
Add `labels`
  • Loading branch information
andrewisen-tikab authored Jul 19, 2023
2 parents 9a76fa3 + 9756422 commit 206f7e9
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 5 deletions.
10 changes: 10 additions & 0 deletions example/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import './style.css';
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';

import CameraControls from 'camera-controls';

Expand All @@ -23,6 +24,7 @@ const example = (): void => {
let gridHelperSize: number;

let renderer: THREE.WebGLRenderer;
let cssRenderer: CSS2DRenderer;

// Setup the GUI
const gui = new GUI();
Expand Down Expand Up @@ -71,6 +73,13 @@ const example = (): void => {
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

cssRenderer = new CSS2DRenderer();
cssRenderer.setSize(window.innerWidth, window.innerHeight);
cssRenderer.domElement.style.position = 'absolute';
cssRenderer.domElement.style.top = '0px';
cssRenderer.domElement.className = 'shape-3d-css-renderer';
document.body.appendChild(cssRenderer.domElement);

// We'll use the `camera-controls` library to handle our camera.
const clock = new THREE.Clock();
const camera = new THREE.PerspectiveCamera(
Expand Down Expand Up @@ -109,6 +118,7 @@ const example = (): void => {

const render = (): void => {
renderer.render(scene, camera);
cssRenderer.render(scene, camera);
};

const animate = (_currentTime: number = 0): void => {
Expand Down
18 changes: 18 additions & 0 deletions example/style.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
body {
margin: 0;
}

.shape-3d-css-renderer {
pointer-events: none;
}

.shape-3d-label-container {
pointer-events: none;
position: relative;
}

.shape-3d-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: auto !important;
z-index: 2;
}
2 changes: 2 additions & 0 deletions src/Shape3D.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ export default class Shape3D extends Shape3DCore {
setCloseLine(closeLine: boolean) {
this.closeLine = closeLine;
this.update();

this.dispatchEvent({ type: 'close-line-changed' });
}

getCloseLine(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
THREE.Mesh.prototype.raycast = acceleratedRaycast;

import Shape3D from '../Shape3D';
import { getMidpoint } from '../utils';
import Shape3D from '../../Shape3D';
import { getMidpoint } from '../../utils';
import { generateLabel } from './labels';
import { Vertex } from '../../types';

const _raycaster = new THREE.Raycaster();
// @ts-ignore
Expand Down Expand Up @@ -37,6 +39,7 @@ type TransformShapeControlsGizmoParams = {
centerGizmo: boolean;
dragVertices: boolean;
allowCreatingNewVertices: boolean;
showLengthLabels: boolean;
};

type VertexMetadata = {
Expand All @@ -49,6 +52,8 @@ type Mode = 'translate' | 'rotate' | 'scale';
class TransformShapeControls extends THREE.Object3D {
private vertexGroup!: THREE.Group;

private labelsGroup!: THREE.Group;

public object?: Shape3D;

public isTransformControls: boolean;
Expand Down Expand Up @@ -165,6 +170,7 @@ class TransformShapeControls extends THREE.Object3D {
centerGizmo: true,
dragVertices: true,
allowCreatingNewVertices: true,
showLengthLabels: true,
};

this.vertexCenter = new THREE.Vector3();
Expand Down Expand Up @@ -257,9 +263,14 @@ class TransformShapeControls extends THREE.Object3D {
this.vertexHoverMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
this.add(vertexGroup);

const labelsGroup = new THREE.Group();
this.add(labelsGroup);

defineProperty('camera', camera);
defineProperty('object', undefined);
defineProperty('vertexGroup', vertexGroup);
defineProperty('labelsGroup', labelsGroup);

defineProperty('enabled', true);
defineProperty('axis', null);
defineProperty('mode', 'translate');
Expand Down Expand Up @@ -387,12 +398,20 @@ class TransformShapeControls extends THREE.Object3D {
this._gizmo.position.set(0, 0, 0);
}

// Update vertex group
if (this.params.dragVertices) {
this.vertexGroup.position.copy(this.worldPosition);
this.vertexGroup.quaternion.copy(this.worldQuaternion);
this.vertexGroup.scale.copy(this._worldScale);
}

// Update vertex group
if (this.params.dragVertices) {
this.labelsGroup.position.copy(this.worldPosition);
this.labelsGroup.quaternion.copy(this.worldQuaternion);
this.labelsGroup.scale.copy(this._worldScale);
}

this._parentQuaternionInv.copy(this._parentQuaternion).invert();
this._worldQuaternionInv.copy(this.worldQuaternion).invert();
}
Expand Down Expand Up @@ -827,7 +846,10 @@ class TransformShapeControls extends THREE.Object3D {

private onVertexChanged() {
this.updateHandles();

this.updateOffset();

this.updateLabels();
}

private updateHandles() {
Expand Down Expand Up @@ -920,8 +942,38 @@ class TransformShapeControls extends THREE.Object3D {
// this.position.copy(center);
}

private updateLabels() {
if (this.params.showLengthLabels === false) return;
this.addLabels();
}

private addLabels() {
this.labelsGroup.clear();
const vertices = this.object!.getVertices();

// Get distance from camera to center of object
const offsetDistance = this.vertexCenter.distanceTo(this.camera.position) / 100 + 1.1;

const center: Vertex = [this.vertexCenter.x, this.vertexCenter.y, this.vertexCenter.z];
for (let index = 0; index < vertices.length; index++) {
const vertex = vertices[index];
if (index === 0) continue;
const previousVertex = vertices[index - 1];
generateLabel(this.labelsGroup, vertex, previousVertex, center, offsetDistance);
}

if (this.object!.getCloseLine()) {
const vertex = vertices[0];
const previousVertex = vertices[vertices.length - 1];

generateLabel(this.labelsGroup, vertex, previousVertex, center, offsetDistance);
}

this.labelsGroup.position.set(-this.position.x, -this.position.y, -this.position.z);
}

private onCloseLineChanged = (_e: any) => {
this.updateHandles();
this.onVertexChanged();
};

// Detach from object
Expand Down
36 changes: 36 additions & 0 deletions src/controls/TransformShapeControls/labels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
import { Vertex } from '../../types';
import { getLength2D, getMidpointOffsetFromLine } from '../../utils';

function addPrefix(this: HTMLInputElement, _ev: Event) {
this.setAttribute('size', `${this.value!.length}`);
}

export const generateLabel = (
parent: THREE.Object3D,
firstVertex: Vertex,
secondVertex: Vertex,
center: Vertex,
offsetDistance: number,
) => {
const offset = getMidpointOffsetFromLine(firstVertex, secondVertex, center, offsetDistance);
const length = getLength2D(firstVertex, secondVertex);

const divElement = document.createElement('div');
divElement.className = 'shape-3d-label-container';

const inputElement = document.createElement('input');
inputElement.type = 'text';

inputElement.id = 'myInput';
inputElement.className = 'shape-3d-label';
inputElement.placeholder = `${length.toFixed(2)}`;
inputElement.setAttribute('size', `${inputElement.getAttribute('placeholder')!.length}`);
inputElement.oninput = addPrefix.bind(inputElement);

divElement.appendChild(inputElement);
const label = new CSS2DObject(divElement);
label.position.set(offset[0], offset[1], offset[2]);

parent.add(label);
};
2 changes: 1 addition & 1 deletion src/core/Shape3DCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class Shape3DCore extends THREE.Object3D {
// Insert the midpoint at index and shift the rest of the vertices
this.vertices.splice(index, 0, midpoint);

this.updateGeometry();
this.update();
}

public removeVertex(index: number) {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default as Shape3D } from './Shape3D';
export * from './controls/TransformShapeControls';
export * from './controls/TransformShapeControls/TransformShapeControls';
71 changes: 71 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,78 @@
import * as THREE from 'three';
import type { Vertex } from './types';

export const getMidpoint = (firstVertex: Vertex, secondVertex: Vertex): Vertex => [
(firstVertex[0] + secondVertex[0]) / 2,
(firstVertex[1] + secondVertex[1]) / 2,
(firstVertex[2] + secondVertex[2]) / 2,
];

const _midpoint = new THREE.Vector3();
const _center = new THREE.Vector3();

/**
* Offset a midpoint from a center.
* @param midpoint
* @param center
* @param offsetDistance
* @returns
*/
export const getMidpointOffsetFromCenter = (
midpoint: Vertex,
center: Vertex,
offsetDistance = 1,
): Vertex => {
_midpoint.fromArray(midpoint);
_center.fromArray(center);
const displacement = _midpoint.clone().sub(_center).normalize().multiplyScalar(offsetDistance);
return _midpoint.clone().add(displacement).toArray();
};

const _firstVertex = new THREE.Vector3();
const _secondVertex = new THREE.Vector3();
const _line = new THREE.Vector3();
const _perpendicular = new THREE.Vector3();
const _up = new THREE.Vector3(0, 1, 0);

export const getMidpointOffsetFromLine = (
firstVertex: Vertex,
secondVertex: Vertex,
center: Vertex,
offsetDistance = 1,
) => {
_firstVertex.fromArray(firstVertex);
_secondVertex.fromArray(secondVertex);
_center.fromArray(center);

_line.subVectors(_secondVertex, _firstVertex);
_midpoint.addVectors(_firstVertex, _secondVertex).multiplyScalar(0.5);

_perpendicular.crossVectors(_up, _line).normalize();

const distance1 = _center.distanceTo(
_midpoint.clone().add(_perpendicular.multiplyScalar(offsetDistance)),
);

const distance2 = _center.distanceTo(
_midpoint.clone().add(_perpendicular.multiplyScalar(-offsetDistance)),
);

_midpoint.add(
_perpendicular.multiplyScalar(distance1 > distance2 ? -offsetDistance : offsetDistance),
);

return _midpoint.toArray();
};

/**
* Return the length of the line segment in 2D space.
* @param firstVertex
* @param secondVertex
* @returns
*/
export const getLength2D = (firstVertex: Vertex, secondVertex: Vertex): number => {
return Math.sqrt(
Math.pow(firstVertex[0] - secondVertex[0], 2) +
Math.pow(firstVertex[2] - secondVertex[2], 2),
);
};

0 comments on commit 206f7e9

Please sign in to comment.