Skip to content

Commit

Permalink
Merge pull request #4 from andrewisen-tikab/feature
Browse files Browse the repository at this point in the history
Add `TunnelControls`
  • Loading branch information
andrewisen-tikab authored Oct 9, 2023
2 parents 10371e8 + 1fb62fc commit eae89f4
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 16 deletions.
70 changes: 54 additions & 16 deletions example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ 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 { Tunnel3D } from '../src';
import { Tunnel3D, TunnelControls } from '../src';

const stats = new Stats();
document.body.appendChild(stats.dom);
Expand All @@ -22,7 +22,15 @@ const scene = new THREE.Scene();
const group = new THREE.Group();
scene.add(group);

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const { innerWidth: width, innerHeight: height } = window;
const camera = new THREE.OrthographicCamera(
width / -2,
width / 2,
height / 2,
height / -2,
1,
1_000,
);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
Expand All @@ -35,13 +43,18 @@ const cameraControls = new CameraControls(camera, renderer.domElement);
const tunnel = new Tunnel3D();
group.add(tunnel);

const size = 10;
const divisions = 10;
const tunnelControls = new TunnelControls();
tunnelControls.attach(tunnel);
const grout = tunnelControls.addGrout();

const size = 200;
const divisions = 200;

const gridHelper = new THREE.GridHelper(size, divisions);

scene.add(gridHelper);

cameraControls.setPosition(-10, 10, 10);
cameraControls.setPosition(-100, 100, 100);

const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
Expand Down Expand Up @@ -70,33 +83,57 @@ tunnelFolder
.add(tunnel, 'tunnelLength', 1, 100)
.name('Length [L]')
.onChange((value) => {
tunnel.tunnelLength = value;
tunnel.update();
tunnelControls.setTunnelParams({ tunnelLength: value });
})
.disable();

tunnelFolder
.add(tunnel, 'tunnelWidth', 1, 100)
.name('Width [W]')
.name('Width [W] (m)')
.onChange((value) => {
tunnel.tunnelWidth = value;
tunnel.update();
tunnelControls.setTunnelParams({ tunnelWidth: value });
});

tunnelFolder
.add(tunnel, 'tunnelHeight', 1, 20)
.name('Height [H]')
.name('Height [H] (m)')
.onChange((value) => {
tunnel.tunnelHeight = value;
tunnel.update();
tunnelControls.setTunnelParams({ tunnelHeight: value });
});

tunnelFolder
.add(tunnel, 'tunnelRoofHeight', 1, 10)
.name('Roof [f]')
.name('Roof [f] (m)')
.onChange((value) => {
tunnel.tunnelRoofHeight = value;
tunnel.update();
tunnelControls.setTunnelParams({ tunnelRoofHeight: value });
});

const groutFolder = gui.addFolder('Grout');
const groutParams = {
visible: true,
angle: 5,
holeLength: 10,
};

groutFolder
.add(groutParams, 'visible')
.name('Visible')
.onChange((value) => {
if (grout == null) return;
grout.visible = value;
});
groutFolder
.add(groutParams, 'angle', 1, 20, 1)
.name('Angle [α] (degrees)')
.onChange((value) => {
tunnelControls.setGroutParams({ angle: value * THREE.MathUtils.DEG2RAD });
});

groutFolder
.add(groutParams, 'holeLength', 1, 90)
.name('Hole Length [L] (m)')
.onChange((value) => {
tunnelControls.setGroutParams({ holeLength: value });
});

const params = {
Expand All @@ -111,6 +148,7 @@ const params = {
cameraControls.fitToBox(tunnel, true, { paddingTop: 1, paddingBottom: 1 });
},
};

const cameraFolder = gui.addFolder('Camera');
cameraFolder.add(params, 'fit').name('Zoom to Tunnel');
cameraFolder.add(params, 'fitProfile').name('Profile');
Expand Down
78 changes: 78 additions & 0 deletions src/Grout3D.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as THREE from 'three';
import { AbstractGrout3D, AbstractGrout3DParams } from './core';
import Tunnel3D from './Tunnel3D';

// const doSomething = () => {};

export default class Grout3D extends THREE.Object3D implements AbstractGrout3D {
public isGrout3D: boolean = true;

public screenLength: number = 1;

public angle: number = 5 * THREE.MathUtils.DEG2RAD;

public cutDepth: number = 1;

public overlap: number = 0;

public holeLength: number = 10;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
private _params: AbstractGrout3DParams | null = null;

private _tunnel: Tunnel3D;

constructor(tunnel: Tunnel3D, params?: AbstractGrout3DParams) {
super();
this._tunnel = tunnel;
if (params) this._params = params;
this._build();
}

// private _calculateNewParams(): boolean {
// if (!this._params) throw new Error('Grout3D: params is are defined');

// const { holeLength, angle, cutDepth } = this._params;

// if (angle != null && holeLength != null) {
// doSomething();
// } else if (holeLength != null && cutDepth != null) {
// doSomething();
// } else if (angle != null && cutDepth != null) {
// doSomething();
// } else {
// console.error('params', this._params);
// return false;
// }
// return true;
// }

private _build() {
// if (!this._calculateNewParams())
// throw new Error('Grout3D: params are not correctly defined');

const { holeLength } = this;

const geometry = new THREE.CylinderGeometry(1 / 3, 1 / 3, holeLength, 32);
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,
depthTest: false,
depthWrite: false,
});
const cylinder = new THREE.Mesh(geometry, material);
cylinder.rotateX(Math.PI / 2);
cylinder.position.set(0, 0, holeLength / 2);
this.add(cylinder);

const { tunnelHeight, tunnelRoofHeight } = this._tunnel;
this.position.set(0, tunnelHeight + tunnelRoofHeight, 0);
this.rotation.set(-this.angle, 0, 0);
}

public update(params?: AbstractGrout3DParams): void {
if (params) this._params = params;
this.clear();
this._build();
}
}
7 changes: 7 additions & 0 deletions src/Tunnel3D.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ export default class Tunnel3D extends THREE.Object3D implements AbstractTunnel3D
public tunnelHeight: number = 10;
public tunnelRoofHeight: number = 3;

public groutGroup: THREE.Group;

constructor(params?: Partial<AbstractTunnel3DParams>) {
super();

this.groutGroup = new THREE.Group();

if (params) Object.assign(this, params);
this._build();
}
Expand Down Expand Up @@ -65,6 +70,8 @@ export default class Tunnel3D extends THREE.Object3D implements AbstractTunnel3D

group.translateY(tunnelHeight / 2);
this.add(group);

this.add(this.groutGroup);
}

update(): void {
Expand Down
87 changes: 87 additions & 0 deletions src/controls/EventDispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
export type Listener = (event?: DispatcherEvent) => void;

export interface DispatcherEvent {
type: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
}

export class EventDispatcher {
private _listeners: { [type: string]: Listener[] } = {};

/**
* Adds the specified event listener.
* @param type event name
* @param listener handler function
* @category Methods
*/
addEventListener(type: string, listener: Listener): void {
const listeners = this._listeners;

if (listeners[type] === undefined) listeners[type] = [];

if (listeners[type].indexOf(listener) === -1) listeners[type].push(listener);
}

/**
* Presence of the specified event listener.
* @param type event name
* @param listener handler function
* @category Methods
*/
hasEventListener(type: string, listener: Listener): boolean {
const listeners = this._listeners;

return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1;
}

/**
* Removes the specified event listener
* @param type event name
* @param listener handler function
* @category Methods
*/
removeEventListener(type: string, listener: Listener): void {
const listeners = this._listeners;
const listenerArray = listeners[type];

if (listenerArray !== undefined) {
const index = listenerArray.indexOf(listener);

if (index !== -1) listenerArray.splice(index, 1);
}
}

/**
* Removes all event listeners
* @param type event name
* @category Methods
*/
removeAllEventListeners(type?: string): void {
if (!type) {
this._listeners = {};
return;
}

if (Array.isArray(this._listeners[type])) this._listeners[type].length = 0;
}

/**
* Fire an event type.
* @param event DispatcherEvent
* @category Methods
*/
dispatchEvent(event: DispatcherEvent): void {
const listeners = this._listeners;
const listenerArray = listeners[event.type];

if (listenerArray !== undefined) {
event.target = this;
const array = listenerArray.slice(0);

for (let i = 0, l = array.length; i < l; i++) {
array[i].call(this, event);
}
}
}
}
49 changes: 49 additions & 0 deletions src/controls/TunnelControls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Grout3D from '../Grout3D';
import Tunnel3D from '../Tunnel3D';
import { AbstractGrout3DParams, AbstractTunnel3DParams } from '../core';
import { EventDispatcher } from './EventDispatcher';

export default class TunnelControls extends EventDispatcher {
private _tunnel: Tunnel3D | null = null;
private _grout: Grout3D | null = null;

constructor() {
super();
}

attach(tunnel: Tunnel3D) {
this._tunnel = tunnel;
}

detach() {
this._tunnel = null;
}

getTunnel() {
return this._tunnel;
}

setTunnelParams(params: Partial<AbstractTunnel3DParams>) {
if (this._tunnel == null) return;
Object.assign(this._tunnel, params);
this._tunnel.update();

if (this._grout) {
this._grout.update();
}
}

addGrout() {
if (this._tunnel == null) return;
const grout = new Grout3D(this._tunnel);
this._grout = grout;
this._tunnel.groutGroup.add(grout);
return grout;
}

setGroutParams(params: Partial<AbstractGrout3DParams>) {
if (this._grout == null) return;
Object.assign(this._grout, params);
this._grout.update();
}
}
Loading

0 comments on commit eae89f4

Please sign in to comment.