幾何庫從零開始

jianlun3009發表於2020-09-30

Point

import Segment from './Segment';
import MathUtils from '../math/MathUtils';
import Vector2f from './Vector2f';

export default class Point {
  public readonly zero = new Point(0, 0);
  public x: number;
  public y: number;

  public constructor(x: number = 0, y: number) {
    this.x = x;
    this.y = y;
  }

  /**
   * @author lianbo
   * @date 2020-09-30 18:48:03
   * @Description: 兩個點比較
  */
  public equalTo(p: Point) {
    if (MathUtils.equal(this.x, p.x) && MathUtils.equal(this.y, p.y)) {
      return true;
    }
    return false;
  }

  /**
   * @author lianbo
   * @date 2020-09-30 15:33:03
   * @Description: 點線上段上,包括端點
   */
  public on(seg: Segment) {

  }

  /**
   * @author lianbo
   * @date 2020-09-30 15:58:12
   * @Description: 點到點的距離
   */
  public distanceToPoint(p: Point): number {
    let dx = p.x - this.x;
    let dy = p.y - this.y;
    return Math.sqrt(dx * dx + dy * dy);
  }

  /**
   * @author lianbo
   * @date 2020-09-30 18:47:24
   * @Description: 點按向量平移
  */
  public translate(v: Vector2f): Point {
    return new Point(this.x + v.x, this.y + v.y);
  }


  /**
   * @author lianbo
   * @date 2020-09-30 17:10:09
   * @Description: 線段是否線上段範圍內,包括端點
   */
  public inSegmentScope(seg: Segment): boolean {
    const segV = new Vector2f(seg.start, seg.end);
    const startV = new Vector2f(seg.start, this);
    const endV = new Vector2f(seg.end, this);
    const startDot = startV.dot(segV);
    const endDot = -endV.dot(segV);
    if (MathUtils.greaterEqual(startDot, 0) && MathUtils.greaterEqual(endDot, 0)) {
      return true;
    }
    return false;
  }

  /**
   * @author lianbo
   * @date 2020-09-30 18:35:05
   * @Description: 點到線段的距離
   */
  public distanceToSegment(seg: Segment): number {
    if (seg.start.equalTo(seg.end)) { //線段縮成一個點?
      return this.distanceToPoint(seg.start);
    }

    const segV = new Vector2f(seg.start, seg.end);
    const startV = new Vector2f(seg.start, this);
    const endV = new Vector2f(seg.end, this);
    const startDot = startV.dot(segV);
    const endDot = -endV.dot(segV);

    if (MathUtils.greaterEqual(startDot, 0) && MathUtils.greaterEqual(endDot, 0)) {
      let v_unit = segV.normalize;
      const dist = Math.abs(v_unit.cross(startV));
      return dist;
    } else if (startDot < 0) {                             /* point is out of scope closer to ps */
      return this.distanceToPoint(seg.start);
    } else {                                               /* point is out of scope closer to pe */
      return this.distanceToPoint(seg.end);
    }
  }

  /**
   * @author lianbo
   * @date 2020-09-30 18:43:21
   * @Description: 點到線的垂足
   */
  public closestPoint(seg: Segment): Point {
    const segV = new Vector2f(seg.start, seg.end);
    const n = segV.normalize;
    const startV = new Vector2f(seg.start, this);
    const translateLength = segV.dot(startV);
    return seg.start.translate(n.multiply(translateLength));
  }
}

Segment

import Point from './Point';

export default class Segment {

  public start: Point;
  public end: Point;
}

Polygon

export default class Polygon {}

Vector2

import Point from './Point';
import MathUtils from '../math/MathUtils';

export default class Vector2f {

  public pS: Point;
  public pE: Point;

  public x: number;
  public y: number;

  public constructor(pS: Point|number, pE: Point|number) {
    if(pS instanceof Point && pE instanceof Point){
      this.pS = pS;
      this.pE = pE;
      this.x = pE.x - pS.x;
      this.y = pE.y - pS.y;
    }
    if(typeof pS ===  'number' && typeof pE === 'number'){
      this.x = pS;
      this.y = pE;
    }
  }

  public dot(vec: Vector2f): number {
    return this.x * vec.x + this.y * vec.y;
  }

  public cross(vec: Vector2f): number {
    return this.x * vec.y - this.y * vec.x;
  }

  public get length(): number {
    const length = Math.sqrt(this.x & this.x + this.y & this.y);
    return length;
  }

  public get normalize(): Vector2f {
    if (this.isZero) {
      throw new Error('0 向量,無單位向量');
    }
    const length = this.length;
    return new Vector2f(this.x / length, this.y / length);
  }

  public get isZero(): boolean {
    if (MathUtils.equal(this.length, 0)) {
      return true;
    }
    return false;
  }

  public multiply(scalar: number) {
    return new Vector2f(this.x * scalar,this.y * scalar);
  }
}

MathUtils

export default class MathUtils {
  public static readonly Deg2Rad: number = (Math.PI / 180.0);
  public static readonly Rad2Deg: number = (180.0 / Math.PI);
  public static readonly Epsilon: number = 0.000001;

  public static equal(a: number, b: number, epsilon: number = MathUtils.Epsilon) {
    return Math.abs(a - b) <= epsilon;
  }

  public static greater(a:number,b:number,epsilon: number = MathUtils.Epsilon) {
    return a - b > epsilon;
  }

  public static greaterEqual(a:number,b:number,epsilon: number = MathUtils.Epsilon) {
    return !MathUtils.less(a,b);
  }

  public static less(a:number,b:number,epsilon: number = MathUtils.Epsilon) {
    return this.greater(b,a);
  }

  public static lessEqual(a:number,b:number,epsilon: number = MathUtils.Epsilon) {
    return !MathUtils.greater(a,b);
  }
}

 

相關文章