UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

238 lines (221 loc) 6.83 kB
import {Vector3} from 'three/src/math/Vector3'; import {BufferGeometry} from 'three/src/core/BufferGeometry'; import {BufferAttribute} from 'three/src/core/BufferAttribute'; import {TypeAssert} from '../../../engine/poly/Assert'; export enum PointsCountMode { SEGMENTS_COUNT = 'segments count', SEGMENTS_LENGTH = 'segments length', } export const POINTS_COUNT_MODE: PointsCountMode[] = [PointsCountMode.SEGMENTS_COUNT, PointsCountMode.SEGMENTS_LENGTH]; export enum JoinMode { ABC = 'abc', ACB = 'acb', AB = 'ab', BC = 'bc', AC = 'ac', } export const JOIN_MODES: JoinMode[] = [JoinMode.ABC, JoinMode.ACB, JoinMode.AB, JoinMode.AC, JoinMode.BC]; interface Circle3PointsParameters { arc: boolean; center: boolean; pointsCountMode: PointsCountMode; segmentsLength: number; segmentsCount: number; full: boolean; joinMode: JoinMode; addIdAttribute: boolean; addIdnAttribute: boolean; } interface CreatedGeometries { arc?: BufferGeometry; center?: BufferGeometry; } export class Circle3Points { private a: Vector3 = new Vector3(); private b: Vector3 = new Vector3(); private c: Vector3 = new Vector3(); private an: Vector3 = new Vector3(); private bn: Vector3 = new Vector3(); private cn: Vector3 = new Vector3(); private ac: Vector3 = new Vector3(); private ab: Vector3 = new Vector3(); private ab_x_ac: Vector3 = new Vector3(); private part0: Vector3 = new Vector3(); private part1: Vector3 = new Vector3(); private divider: number = 1; private a_center: Vector3 = new Vector3(); private center: Vector3 = new Vector3(); private normal: Vector3 = new Vector3(); private radius: number = 1; private x: Vector3 = new Vector3(); private y: Vector3 = new Vector3(); private z: Vector3 = new Vector3(); private angle_ab: number = 1; private angle_ac: number = 1; private angle_bc: number = 1; private angle: number = 2 * Math.PI; private x_rotated: Vector3 = new Vector3(); private _created_geometries: CreatedGeometries = {}; constructor(private params: Circle3PointsParameters) {} created_geometries() { return this._created_geometries; } create(a: Vector3, b: Vector3, c: Vector3) { this.a.copy(a); this.b.copy(b); this.c.copy(c); this._compute_axis(); this._create_arc(); this._create_center(); } private _create_arc() { this._compute_angle(); const points_count = this._points_count(); const positions: number[] = new Array(points_count * 3); const indices: number[] = new Array(points_count); const angle_increment = this.angle / (points_count - 1); this.x_rotated.copy(this.x).multiplyScalar(this.radius); let i = 0; for (i = 0; i < points_count; i++) { this.x_rotated .copy(this.x) .applyAxisAngle(this.normal, angle_increment * i) .multiplyScalar(this.radius) .add(this.center); this.x_rotated.toArray(positions, i * 3); if (i > 0) { indices[(i - 1) * 2] = i - 1; indices[(i - 1) * 2 + 1] = i; } } if (this.params.full) { // also add the last segment indices.push(i - 1); indices.push(0); } const geometry = new BufferGeometry(); geometry.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3)); geometry.setIndex(indices); if (this.params.addIdAttribute || this.params.addIdnAttribute) { const ids: number[] = new Array(points_count); for (let i = 0; i < ids.length; i++) { ids[i] = i; } if (this.params.addIdAttribute) { geometry.setAttribute('id', new BufferAttribute(new Float32Array(ids), 1)); } const idns = ids.map((id) => id / (points_count - 1)); if (this.params.addIdnAttribute) { geometry.setAttribute('idn', new BufferAttribute(new Float32Array(idns), 1)); } } this._created_geometries.arc = geometry; } private _create_center() { if (!this.params.center) { return; } const geometry = new BufferGeometry(); const positions = [this.center.x, this.center.y, this.center.z]; geometry.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3)); this._created_geometries.center = geometry; } private _compute_axis() { this.ac.copy(this.c).sub(this.a); this.ab.copy(this.b).sub(this.a); this.ab_x_ac.copy(this.ab).cross(this.ac); this.divider = 2.0 * this.ab_x_ac.lengthSq(); this.part0.copy(this.ab_x_ac).cross(this.ab).multiplyScalar(this.ac.lengthSq()); this.part1.copy(this.ac).cross(this.ab_x_ac).multiplyScalar(this.ab.lengthSq()); this.a_center.copy(this.part0).add(this.part1).divideScalar(this.divider); this.radius = this.a_center.length(); this.normal.copy(this.ab_x_ac).normalize(); this.center.copy(this.a).add(this.a_center); } private _compute_angle() { if (!this.params.arc) { return; } if (this.params.full) { this.x.copy(this.a).sub(this.center).normalize(); this.angle = 2 * Math.PI; } else { this.an.copy(this.a).sub(this.center).normalize(); this.bn.copy(this.b).sub(this.center).normalize(); this.cn.copy(this.c).sub(this.center).normalize(); this._set_x_from_joinMode(); this.y.copy(this.normal); this.z.copy(this.x).cross(this.y).normalize(); this.angle_ab = this.an.angleTo(this.bn); this.angle_ac = this.an.angleTo(this.cn); this.angle_bc = this.bn.angleTo(this.cn); this._set_angle_from_joinMode(); } } private _points_count() { const mode = this.params.pointsCountMode; switch (mode) { case PointsCountMode.SEGMENTS_COUNT: { return this.params.segmentsCount + 1; } case PointsCountMode.SEGMENTS_LENGTH: { let perimeter = Math.PI * this.radius * this.radius; if (!this.params.full) { perimeter *= Math.abs(this.angle) / (Math.PI * 2); } return Math.ceil(perimeter / this.params.segmentsLength); } } TypeAssert.unreachable(mode); } private _set_x_from_joinMode() { const joinMode = this.params.joinMode; this.x.copy(this.a).sub(this.center).normalize(); switch (joinMode) { case JoinMode.ABC: { return this.x.copy(this.an); } case JoinMode.ACB: { return this.x.copy(this.an); } case JoinMode.AB: { return this.x.copy(this.an); } case JoinMode.AC: { return this.x.copy(this.an); } case JoinMode.BC: { return this.x.copy(this.bn); } } TypeAssert.unreachable(joinMode); } private _set_angle_from_joinMode(): void { const joinMode = this.params.joinMode; switch (joinMode) { case JoinMode.ABC: { this.angle = this.angle_ab + this.angle_bc; return; } case JoinMode.ACB: { this.angle = this.angle_ac + this.angle_bc; this.angle *= -1; return; } case JoinMode.AB: { this.angle = this.angle_ab; return; } case JoinMode.AC: { this.angle = this.angle_ac; this.angle *= -1; return; } case JoinMode.BC: { this.angle = this.angle_bc; return; } } TypeAssert.unreachable(joinMode); } }