Skip to content
Snippets Groups Projects
Unverified Commit e7d527bd authored by Jamie Temple's avatar Jamie Temple
Browse files

feat: bezierCurven

parent f31b7176
No related branches found
No related tags found
1 merge request!1feat: base project
......@@ -19,7 +19,7 @@ class Example extends PipelineData {
const point2d = new Point2d(.1, 32, new THREE.Color(0xffffff));
const point3d = new Point3d(.1, 32, new THREE.Color(0xffffff));
const line1d = new Line1d(pointsA, new THREE.Color(0xffffff));
const line2d = new Line2d(pointsB, .001, new THREE.Color(0xffffff));
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);
......
import * as THREE from "three";
import { Vector3 } from "three";
abstract class CubicBezierAlgorithm {
/**
* @param controlPointsPosition an array with vectors that give the positons of the control points. The
* first vector is the start point, the second vector is the first control point, the third vector is the
* second control point and the fourth vector is the end point.
* @param t the time at which the curve should be evaluated (0.0 - 1.0)
*/
abstract evaluatePosition(controlPointsPosition: Array<THREE.Vector3>, t: number): THREE.Vector3;
}
class BernsteinAlgorithm extends CubicBezierAlgorithm {
evaluatePosition(positions: Array<THREE.Vector3>, t: number): THREE.Vector3 {
const c = BernsteinAlgorithm.calculateCoefficients(t);
const x = c[0] * positions[0].x + c[1] * positions[1].x + c[2] * positions[2].x + c[3] * positions[3].x;
const y = c[0] * positions[0].y + c[1] * positions[1].y + c[2] * positions[2].y + c[3] * positions[3].y;
const z = c[0] * positions[0].z + c[1] * positions[1].z + c[2] * positions[2].z + c[3] * positions[3].z;
return new THREE.Vector3(x, y, z);
}
/**
* Weight the contribution of controlpoints by calculating the values of the different terms of the
* Bernstein polynome. The terms yield a coefficient of the different points.
* p(t) = (1-t)^3 * p0 + 3 * (1-t)^2 * t * p1 +
* 3 * (1-t) * t^2 * p2 + t^3 * p3
*/
public static calculateCoefficients(t: number): number[]{
const u0 = 1.0 * Math.pow(1.0 - t, 3.0) * Math.pow(t, 0.0);
const u1 = 3.0 * Math.pow(1.0 - t, 2.0) * Math.pow(t, 1.0);
const u2 = 3.0 * Math.pow(1.0 - t, 1.0) * Math.pow(t, 2.0);
const u3 = 1.0 * Math.pow(1.0 - t, 0.0) * Math.pow(t, 3.0);
return [u0, u1, u2, u3];
}
}
class DeCasteljauAlgorithm extends CubicBezierAlgorithm {
evaluatePosition(positions: Array<THREE.Vector3>, t: number): THREE.Vector3 {
while (positions.length > 1)
positions = DeCasteljauAlgorithm.Iteration(positions, t);
return positions[0];
}
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++)
result.push(points[i + 1].clone().sub(points[i]).multiplyScalar(t).add(points[i]));
return result;
}
}
class CubicBezierCurve {
private _algorithm: CubicBezierAlgorithm;
private _controlPoints: Array<THREE.Vector3>;
constructor(controlPoints: Array<THREE.Vector3>, algorithm: CubicBezierAlgorithm) {
this._controlPoints = controlPoints;
this._algorithm = algorithm;
}
public evaluatePosition(t: number): THREE.Vector3 {
return this._algorithm.evaluatePosition(this._controlPoints, t);
}
public evaluatePositions(resolution: number): Array<THREE.Vector3> {
const positions = new Array<THREE.Vector3>();
for (let i = 0; i < resolution + 1; i++)
positions.push(this.evaluatePosition(i / resolution));
return positions;
}
public get controlPoints(): Array<THREE.Vector3> {
return this._controlPoints;
}
public set controlPoints(value: Array<THREE.Vector3>) {
this._controlPoints = value;
}
}
export {CubicBezierAlgorithm, BernsteinAlgorithm, DeCasteljauAlgorithm, CubicBezierCurve};
\ No newline at end of file
import { Line2d, Point2d } from "../core/Shapes";
import { CubicBezierCurve, DeCasteljauAlgorithm } from "./CubicBezierCurve";
interface CurveParameter {
pointA: Point2d;
tangentA: Point2d;
pointB: Point2d;
tangentB: Point2d;
offset: number;
resolution: number;
linewidth: number;
color: THREE.Color;
}
class Curve extends Line2d {
private _generator: CubicBezierCurve;
private _resolution: number;
private _offset: number;
constructor(parameter: CurveParameter) {
super([], parameter.linewidth, parameter.color);
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._generator = new CubicBezierCurve(positions, new DeCasteljauAlgorithm());
}
public update(_deltaTime: number): void {
this._points = this._generator.evaluatePositions(this._resolution);
this._points.forEach((point) => {
point.z = this._offset;
});
this._createLine();
}
}
export { Curve };
export type { CurveParameter };
import * as THREE from 'three';
import { PipelineData } from "../core/Pipeline";
import { Point2d } from "../core/Shapes";
import { Curve } from './Curve';
class BezierDemo extends PipelineData {
public constructor() { super(); }
data(): void {
const point = new Point2d(.1, 36, new THREE.Color(0x00ff00));
this.addShape(point, true, true);
const pointA = new Point2d(.05, 36, new THREE.Color(0x00ff00));
const tangentA = new Point2d(.025, 36, new THREE.Color(0x00ff00));
const pointB = new Point2d(.05, 36, new THREE.Color(0xff0000));
const tangentB = new Point2d(.025, 36, new THREE.Color(0xff0000));
pointA.material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: false });;
tangentA.material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: false });;
pointB.material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: false });;
tangentB.material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: false });;
const curve = new Curve({
pointA: pointA,
tangentA: tangentA,
pointB: pointB,
tangentB: tangentB,
resolution: 128,
linewidth: .02,
offset: -1,
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);
}
}
......
......@@ -7,21 +7,28 @@ import { QuaternionDemo } from "./02-quaternion/demoQuaternion";
import { SimulationDemo } from "./03-simulation/demoSimulation";
window.onload = () => {
const pipeline = new Pipeline();
const ref = window.location.href
let scenario: PipelineData;
let pipeline: Pipeline;
if (ref.includes("bezier")) {
pipeline = new Pipeline(false);
pipeline.orbitControls.enableRotate = false;
scenario = new BezierDemo();
} else if (ref.includes("quaternion")) {
pipeline = new Pipeline();
scenario = new QuaternionDemo();
} else if (ref.includes("simulation")) {
pipeline = new Pipeline();
scenario = new SimulationDemo();
} else if (ref.includes("example")) {
pipeline = new Pipeline();
scenario = new Example();
} else {
pipeline = new Pipeline();
scenario = new Demo();
}
pipeline.init(scenario);
pipeline.loop();
}
......@@ -18,8 +18,6 @@ abstract class PipelineData {
draggables: Array<THREE.Object3D>;
scene: THREE.Scene;
allowOrbit: boolean = true;
constructor() {
this.observers = new Array<PipelineObserver>();
this.draggables = new Array<THREE.Object3D>();
......@@ -48,25 +46,39 @@ abstract class PipelineData {
class Pipeline {
public readonly renderer: THREE.WebGLRenderer;
public readonly camera: THREE.PerspectiveCamera;
public readonly camera: THREE.Camera;
public readonly orbitControls: OrbitControls;
public readonly dragControls: DragControls;
public orbitControls: OrbitControls;
public dragControls: DragControls;
private scene?: THREE.Scene;
private observer: Array<PipelineObserver>;
private startTime: number;
constructor() {
constructor(perspective: boolean = true) {
// CREATE CAMERA
if (perspective) {
this.camera = new THREE.PerspectiveCamera(75,
window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.z = 5;
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()
(this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
(this.camera as THREE.PerspectiveCamera).updateProjectionMatrix()
}, false);
} else {
const aspect = window.innerWidth / window.innerHeight;
this.camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0.1, 1000);
this.camera.position.z = 1;
window.addEventListener('resize', () => {
const aspect = window.innerWidth / window.innerHeight;
(this.camera as THREE.OrthographicCamera).left = -aspect;
(this.camera as THREE.OrthographicCamera).right = aspect;
(this.camera as THREE.OrthographicCamera).updateProjectionMatrix()
}, false);
}
// CREATE RENDERER
this.renderer = new THREE.WebGLRenderer({
......
......@@ -25,34 +25,6 @@ abstract class Shape implements PipelineObserver {
update(_deltaTime: number): void {/* INTENTIONAL */ }
}
class DebugHelper implements PipelineObserver {
private _debug: Map<Shape, THREE.BoxHelper>;
constructor() {
this._debug = new Map<Shape, THREE.BoxHelper>();
}
public get debug(): Array<THREE.BoxHelper> {
return Array.from(this._debug.values());
}
public add(shape: Shape): void {
const box = new THREE.BoxHelper(shape.object3d);
this._debug.set(shape, box);
}
public remove(shape: Shape): void {
this._debug.delete(shape);
}
update(_deltaTime: number): void {
for (let [_, box] of this._debug) {
box.update();
}
}
}
abstract class Point extends Shape {
private _geometry: THREE.BufferGeometry;
private _material: THREE.MeshBasicMaterial | THREE.MeshLambertMaterial;
......@@ -93,38 +65,13 @@ abstract class Point extends Shape {
}
public set material(material: THREE.MeshBasicMaterial | THREE.MeshLambertMaterial) {
this._material = material;
this._material = material.clone();
this._material.color = this._color;
(this.object3d as THREE.Mesh).material = material;
}
}
class Point2d extends Point {
constructor(radius: number, resolution: number, color: THREE.Color) {
super(radius, resolution, color, new THREE.CircleBufferGeometry(radius, resolution),
new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
}
protected _createMesh(): void {
(this._object3d as THREE.Mesh).geometry.dispose();
(this._object3d as THREE.Mesh).geometry = new THREE.CircleBufferGeometry(this._radius, this._resolution);
}
}
class Point3d extends Point {
constructor(radius: number, resolution: number, color: THREE.Color) {
super(radius, resolution, color, new THREE.SphereBufferGeometry(radius, resolution, resolution / 2),
new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
}
protected _createMesh(): void {
(this._object3d as THREE.Mesh).geometry.dispose();
(this._object3d as THREE.Mesh).geometry = new THREE.SphereBufferGeometry(this._radius, this._resolution,
this._resolution / 2);
}
}
abstract class Line extends Shape {
protected _geometry: THREE.BufferGeometry;
protected _material: LineMaterial | THREE.LineBasicMaterial;
......@@ -142,8 +89,12 @@ abstract class Line extends Shape {
this._geometry = geometry;
this._material = material;
if (this._material instanceof LineMaterial) {
this._object3d = new THREE.Mesh(this._geometry, this._material);
} else {
this._object3d = new THREE.Line(this._geometry, this._material);
}
}
protected abstract _createLine(): void;
......@@ -158,13 +109,69 @@ abstract class Line extends Shape {
}
public set material(material: LineMaterial | THREE.LineBasicMaterial) {
this._material = material;
this._material = material.clone();
this._material.color = this._color;
(this._object3d as THREE.Line).material = material;
}
}
class DebugHelper implements PipelineObserver {
private _debug: Map<Shape, THREE.BoxHelper>;
constructor() {
this._debug = new Map<Shape, THREE.BoxHelper>();
}
public get debug(): Array<THREE.BoxHelper> {
return Array.from(this._debug.values());
}
public add(shape: Shape): void {
const box = new THREE.BoxHelper(shape.object3d);
this._debug.set(shape, box);
}
public remove(shape: Shape): void {
this._debug.delete(shape);
}
update(_deltaTime: number): void {
for (let [_, box] of this._debug) {
box.update();
}
}
}
class Point2d extends Point {
constructor(radius: number, resolution: number, color: THREE.Color) {
super(radius, resolution, color, new THREE.CircleBufferGeometry(radius, resolution),
new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
}
protected _createMesh(): void {
(this._object3d as THREE.Mesh).geometry.dispose();
(this._object3d as THREE.Mesh).geometry = new THREE.CircleBufferGeometry(this._radius, this._resolution);
}
}
class Point3d extends Point {
constructor(radius: number, resolution: number, color: THREE.Color) {
super(radius, resolution, color, new THREE.SphereBufferGeometry(radius, resolution, resolution / 2),
new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
}
protected _createMesh(): void {
(this._object3d as THREE.Mesh).geometry.dispose();
(this._object3d as THREE.Mesh).geometry = new THREE.SphereBufferGeometry(this._radius, this._resolution,
this._resolution / 2);
}
}
function pointsToBuffer(points: Array<THREE.Vector3>): LineGeometry {
if (points.length < 2) {
return new LineGeometry();
}
return new LineGeometry().setPositions(points.reduce((acc, cur) => {
acc.push(cur.x, cur.y, cur.z);
return acc;}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment