diff --git a/src/00-welcome-and-example/Example.ts b/src/00-welcome-and-example/Example.ts index a5a6f7d4c0488d025800ebdec65f738beab1cd43..2985b2d41620a2544523f45fad7e117ec5731fc7 100644 --- a/src/00-welcome-and-example/Example.ts +++ b/src/00-welcome-and-example/Example.ts @@ -22,11 +22,11 @@ class Example extends PipelineData { const line2d = new Line2d(pointsB, .01, new THREE.Color(0xffffff)); const mesh = new Mesh("../assets/suzanne.obj", new THREE.Color(0xffffff)); - this.addShape(point2d, true, true); - this.addShape(point3d, true, true); - this.addShape(line1d, true, true); // gives a waring - this.addShape(line2d, true, false); - this.addShape(mesh, true, true); + this.addObject(point2d, true); + this.addObject(point3d, true); + this.addObject(line1d), true; // gives a waring + this.addObject(line2d); + this.addObject(mesh, true); const debugHelper = new DebugHelper(); this.observers.push(debugHelper); diff --git a/src/00-welcome-and-example/demo.ts b/src/00-welcome-and-example/demo.ts index 83f6d07d70d3fd53de3d8bffdbe654745b6ee021..2e0a8377c96b53d02043081f2fcd8ba509f9f986 100644 --- a/src/00-welcome-and-example/demo.ts +++ b/src/00-welcome-and-example/demo.ts @@ -6,8 +6,8 @@ class Demo extends PipelineData { data(): void { const spinningCube = new SpinningCube(); - this.scene.add(spinningCube.object3d); - this.addShape(spinningCube, true, false); + this.addObject(spinningCube, false); + this.addObserver(spinningCube); } } diff --git a/src/01-bezierCurves/CubicBezierCurve.ts b/src/01-bezierCurves/CubicBezierCurve.ts index 5b40aa69fea3f38b4598ac49476b7abe050eca6b..3e475c4fe41cce4f6c1eda070a9b8984c36a0401 100644 --- a/src/01-bezierCurves/CubicBezierCurve.ts +++ b/src/01-bezierCurves/CubicBezierCurve.ts @@ -1,5 +1,4 @@ import * as THREE from "three"; -import { Vector3 } from "three"; abstract class CubicBezierAlgorithm { /** @@ -38,11 +37,11 @@ class BernsteinAlgorithm extends CubicBezierAlgorithm { class DeCasteljauAlgorithm extends CubicBezierAlgorithm { evaluatePosition(positions: Array<THREE.Vector3>, t: number): THREE.Vector3 { while (positions.length > 1) - positions = DeCasteljauAlgorithm.Iteration(positions, t); + positions = DeCasteljauAlgorithm.iteration(positions, t); return positions[0]; } - public static Iteration(points: Array<THREE.Vector3>, t: number): Array<THREE.Vector3> + public static iteration(points: Array<THREE.Vector3>, t: number): Array<THREE.Vector3> { var result = new Array<THREE.Vector3>(); for (let i = 0; i < points.length - 1; i++) diff --git a/src/01-bezierCurves/Curve.ts b/src/01-bezierCurves/Curve.ts index d086d9926d954b9b34c5bf12345893b8270a2b4c..502dbb2a066db02f54dd71746d91cf256368bdcd 100644 --- a/src/01-bezierCurves/Curve.ts +++ b/src/01-bezierCurves/Curve.ts @@ -1,3 +1,4 @@ +import * as THREE from 'three'; import { Line2d, Point2d } from "../core/Shapes"; import { CubicBezierCurve, DeCasteljauAlgorithm } from "./CubicBezierCurve"; @@ -15,6 +16,8 @@ interface CurveParameter { class Curve extends Line2d { + public positions: Array<THREE.Vector3>; + private _generator: CubicBezierCurve; private _resolution: number; private _offset: number; @@ -25,13 +28,14 @@ class Curve extends Line2d { this._resolution = parameter.resolution; this._offset = parameter.offset; - const positions = new Array<THREE.Vector3>(); - positions.push(parameter.pointA.position); - positions.push(parameter.tangentA.position); - positions.push(parameter.tangentB.position); - positions.push(parameter.pointB.position); + this.positions = [ + parameter.pointA.position(), + parameter.tangentA.position(), + parameter.tangentB.position(), + parameter.pointB.position(), + ]; - this._generator = new CubicBezierCurve(positions, new DeCasteljauAlgorithm()); + this._generator = new CubicBezierCurve(this.positions, new DeCasteljauAlgorithm()); } public update(_deltaTime: number): void { diff --git a/src/01-bezierCurves/CurveHelper.ts b/src/01-bezierCurves/CurveHelper.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b08618caea9104672413a538bca4b78b2c7ac32 --- /dev/null +++ b/src/01-bezierCurves/CurveHelper.ts @@ -0,0 +1,87 @@ +import * as THREE from "three"; +import { Curve } from "./Curve"; +import { PipelineObserver, PipelineRenderable } from "../core/Pipeline"; +import { Line1d } from "../core/Shapes"; +import { DeCasteljauAlgorithm } from "./CubicBezierCurve"; +import dat from "dat.gui"; + +class CurveHelper implements PipelineObserver, PipelineRenderable { + + public group: THREE.Group; + private _curve: Curve; + private _lines: Array<Line1d>; + private _offset: number; + private _speed: number = 1; + + t: number = 0; + + constructor(curve: Curve, offset: number = 0) { + this._curve = curve; + this._offset = offset; + this._lines = new Array<Line1d>(); + this.group = new THREE.Group(); + + this._lines.push(new Line1d([new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()], new THREE.Color(0xffff00))); + this._lines.push(new Line1d([new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()], new THREE.Color(0x00ffff))); + this._lines.push(new Line1d([new THREE.Vector3(), new THREE.Vector3()], new THREE.Color(0xff00ff))); + + document.addEventListener('keydown', (event: KeyboardEvent) => { + if (event.key === 'a') { + this.t = Math.max(0, Math.min(1, this.t - 0.01 * this._speed)); + } + if (event.key === 'd') { + this.t = Math.max(0, Math.min(1, this.t + 0.01 * this._speed)); + } + if (event.key === 'w') { + this._speed = Math.max(1, Math.min(2, this._speed + 0.1)); + } + if (event.key === 's') { + this._speed = Math.max(1, Math.min(2, this._speed - 0.1)); + } + }); + + this._lines.forEach((line) => { + this.group.add(line.object()); + }); + + this.gui() + } + + object(): THREE.Object3D<THREE.Event> { + return this.group; + } + + position(): THREE.Vector3 { + return this.group.position; + } + + gui() : void { + const test = new dat.GUI(); + test.add(this, 't', 0, 1); + } + + public update(_deltaTime: number): void { + let points = new Array<THREE.Vector3>(); + this._curve.positions.forEach((position) => { + const p = position.clone(); + p.z = this._offset; + points.push(p); + + }); + this._lines[0].points = points; + points = DeCasteljauAlgorithm.iteration(points, this.t); + this._lines[1].points = points; + points = DeCasteljauAlgorithm.iteration(points, this.t); + this._lines[2].points = points; + } +} + +class CurveHelperExtension { + + constructor(helper: CurveHelper) { + helper.group.position.set(0, 0, 0); + } + +} + +export { CurveHelper } diff --git a/src/01-bezierCurves/DemoBezier.ts b/src/01-bezierCurves/DemoBezier.ts index 01ff12c93c394c2068368f7917bfb13d9b8e13fd..cc67a5063997334b960596171b6083fb2597a728 100644 --- a/src/01-bezierCurves/DemoBezier.ts +++ b/src/01-bezierCurves/DemoBezier.ts @@ -2,6 +2,7 @@ import * as THREE from 'three'; import { PipelineData } from "../core/Pipeline"; import { Point2d } from "../core/Shapes"; import { Curve } from './Curve'; +import { CurveHelper } from './CurveHelper'; class BezierDemo extends PipelineData { public constructor() { super(); } @@ -29,11 +30,21 @@ class BezierDemo extends PipelineData { color: new THREE.Color(0xffffff) }); - this.addShape(pointA, true, true); - this.addShape(tangentA, true, true); - this.addShape(pointB, true, true); - this.addShape(tangentB, true, true); - this.addShape(curve, true, false); + const curveHelper = new CurveHelper(curve, -.5); + + this.addObject(curve); + this.addObject(curveHelper); + this.addObject(pointA, true); + this.addObject(tangentA, true); + this.addObject(pointB, true); + this.addObject(tangentB, true); + + this.addObserver(curve); + this.addObserver(curveHelper); + this.addObserver(pointA); + this.addObserver(tangentA); + this.addObserver(pointB); + this.addObserver(tangentB); } } diff --git a/src/core/Pipeline.ts b/src/core/Pipeline.ts index 98ff748bc8897b883f617d36d5f45928e9c748c1..6383b92e7258a7b9f3815f68f52f733091268689 100644 --- a/src/core/Pipeline.ts +++ b/src/core/Pipeline.ts @@ -2,17 +2,30 @@ import * as THREE from "three"; import { DragControls } from "three/examples/jsm/controls/DragControls"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; -import { Shape } from "./Shapes"; interface PipelineObserver { /** - * @breif RendererObserver is an abstract class that is used to observe the renderer. + * PipelineObserver is an abstract class that is used to observe the renderer. * The renderer calls in the rendering loop update of this observer. * @param {number} _deltaTime gives the time in seconds between two frames */ update(_deltaTime: number): void; } +interface PipelineRenderable { + + /** + * PipelineRenderable is an abstract class that is used to have a unified interface + * to access 3d objectes of an object. + */ + object(): THREE.Object3D; + + /** + * Access the position of the 3d object directly. + */ + position(): THREE.Vector3; +} + abstract class PipelineData { observers: Array<PipelineObserver>; draggables: Array<THREE.Object3D>; @@ -25,16 +38,17 @@ abstract class PipelineData { this.data(); } - protected addShape(shape: Shape, observer: boolean = true, draggable: boolean = false) { - this.scene.add(shape.object3d); - if (observer) { - this.observers.push(shape); - } + protected addObject(renderable: PipelineRenderable, draggable: boolean = false) { + this.scene.add(renderable.object()); if (draggable) { - this.draggables.push(shape.object3d); + this.draggables.push(renderable.object()); } } + protected addObserver(observer: PipelineObserver) { + this.observers.push(observer); + } + /** * @brief Should define a scenario with observers, draggables and scene. * It initializes the scene and adds the draggables to the scene. @@ -162,4 +176,4 @@ class Pipeline { } export { Pipeline, PipelineData }; -export type { PipelineObserver }; +export type { PipelineObserver, PipelineRenderable }; diff --git a/src/core/Shapes.ts b/src/core/Shapes.ts index 8ef39e04ba9cb6dc7100ba33f86ba8adb2e45d33..3c3306ddc72ce217f9c2a08b488be253cf563e7c 100644 --- a/src/core/Shapes.ts +++ b/src/core/Shapes.ts @@ -1,11 +1,11 @@ import * as THREE from "three"; -import { PipelineObserver } from "./Pipeline"; +import { PipelineObserver, PipelineRenderable } from "./Pipeline"; import { LineMaterial } from "three/examples/jsm/lines/LineMaterial"; import { LineGeometry } from "three/examples/jsm/lines/LineGeometry"; import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader"; -abstract class Shape implements PipelineObserver { +abstract class Shape implements PipelineObserver, PipelineRenderable { /** * Any class that extends Shape must initialize this field. @@ -14,12 +14,12 @@ abstract class Shape implements PipelineObserver { */ protected _object3d!: THREE.Object3D; - public get object3d(): THREE.Object3D { + public object(): THREE.Object3D { return this._object3d!; } - public get position(): THREE.Vector3 { - return this.object3d.position; + public position(): THREE.Vector3 { + return this._object3d.position; } update(_deltaTime: number): void {/* INTENTIONAL */ } @@ -67,7 +67,7 @@ abstract class Point extends Shape { public set material(material: THREE.MeshBasicMaterial | THREE.MeshLambertMaterial) { this._material = material.clone(); this._material.color = this._color; - (this.object3d as THREE.Mesh).material = material; + (this._object3d as THREE.Mesh).material = material; } } @@ -99,8 +99,13 @@ abstract class Line extends Shape { protected abstract _createLine(): void; public set points(points: Array<THREE.Vector3>) { - this._points = points; - this._createLine(); + if (this._points.length !== points.length) { + this._points = points; + this._createLine(); + } else { + this._points = points; + this._geometry.setFromPoints(points); + } } public set color(color: THREE.Color) { @@ -128,7 +133,7 @@ class DebugHelper implements PipelineObserver { } public add(shape: Shape): void { - const box = new THREE.BoxHelper(shape.object3d); + const box = new THREE.BoxHelper(shape.object()); this._debug.set(shape, box); } @@ -169,7 +174,7 @@ class Point3d extends Point { } function pointsToBuffer(points: Array<THREE.Vector3>): LineGeometry { - if (points.length < 2) { + if (points.length < 1) { return new LineGeometry(); } return new LineGeometry().setPositions(points.reduce((acc, cur) => {