/**
 * 向量类
 * @prop {number} x 向量横坐标
 * @prop {number} y 向量纵坐标
 */
class Vec {

  /**
   * 构建向量对象
   * @param {number} x 向量横坐标
   * @param {number} y 向量纵坐标
   * @return {Vec} 向量对象
   */
  constructor(x, y){
    this.x = x;
    this.y = y;
  }

  /**
   * 方向角度
   * @type {number}
   * @readonly
   */
  get angle(){
    if(this.y > 0 || this.y == 0 && this.x > 0){
      return Math.acos(this.x / this.mag());
    }
    if(this.y < 0 || this.y == 0 && this.x < 0){
      return 2 * PI - Math.acos(this.x / this.mag());
    }
  }

  /**
   * 计算两个向量的和
   * @param {Vec} b 向量和运算对象
   * @return {Vec} 向量的和
   */
  add(b){
    let a = this;
    return new Vec(a.x + b.x, a.y + b.y);
  }

  /**
   * 计算两个向量的差
   * @param {Vec} b 向量差运算对象
   * @return {Vec} 向量的差
   */
  sub(b){
    let a = this;
    return new Vec(a.x - b.x, a.y - b.y);
  }

  /**
   * 获取向量的副本
   * @return {Vec} 向量副本
   */
  copy(){
    return new Vec(this.x, this.y);
  }

  /**
   * 计算数与向量的积
   * @param {number} s 数与向量的积运算对象
   * @return {Vec} 数与向量的积
   */
  multi(s){
    return new Vec(s * this.x, s * this.y);
  }

  /**
   * 获取向量的大小
   * @return {number} 向量大小
   */
  mag(){
    return sqrt(this.x ** 2 + this.y ** 2);
  }

  /**
   * 移动向量
   * @param {number} distance 移动距离
   * @param {number} angle 移动角度
   * @return {Vec} 移动以后的向量
   */
  move(distance, angle){
    return this.add(Vec.getUnitVectorFromAngle(angle).multi(distance));
  }

  /**
   * 获取某个角度的单位向量
   * @param {number} angle 角度(弧度制)
   * @return {Vec} 单位向量
   */
  static getUnitVectorFromAngle(angle){
    return new Vec(cos(angle), sin(angle));
  }

  /**
   * 计算单位向量
   * @return {Vec} 单位向量
   */
  unitize(){
    return Vec.getUnitVectorFromAngle(this.angle);
  }

  /**
   * 计算点积
   * @param {Vec} b 点积运算对象
   * @return {number} 点积结果
   */
  dotProduct(b){
    let a = this;
    return a.x * b.x + a.y * b.y;
  }
}

/**
 * 线段类
 * @prop {Vec} pos 线段起点的位置向量
 * @prop {Vec} way 线段的方向向量
 */
class Ray {

  /**
   * 构建线段对象
   * @param {Vec} pos 线段起点的位置向量
   * @param {Vec} way 线段的方向向量
   * @return {Ray} 线段对象
   */
   constructor(pos, way){
     this.pos = pos;
     this.way = way;
   }

   /**
    * 通过两个点的位置向量构建线段
    * @param {Vec} begin 线段起点的位置向量
    * @param {Vec} end 线段终点的位置向量
    * @return {Ray} 构建成的线段对象
    */
   static getRayFromPoints(begin, end){
     return new Ray(begin, end.sub(begin));
   }

   /**
    * 线段起点的位置向量
    * @type {Vec}
    * @readonly
    */
   get begin(){
     return this.pos;
   }

   /**
    * 线段终点的位置向量
    * @type {Vec}
    * @readonly
    */
   get end(){
     return this.pos.add(this.way);
   }

   /**
    * 获取两个线段焦点的位置向量
    * @param {Ray} r2 相交运算对象线段
    * @return {Vec | null} 两个线段焦点的位置向量,不相交的场合为null
    */
   intersection(r2){
     let r1 = this;

     // 近似化处理,以防除数过小导致t1、t2越界
     if(abs(r1.way.x) < 0.01) r1.way.x = 0.01;
     if(abs(r2.way.x) < 0.01) r2.way.x = 0.01;

     let t1 = r1.way.y / r1.way.x;
     let t2 = r2.way.y / r2.way.x;
     let x1 = r1.pos.x;
     let x2 = r2.pos.x;
     let y1 = r1.pos.y;
     let y2 = r2.pos.y;
     let sx = (t1 * x1 - t2 * x2 - y1 + y2) / (t1 - t2);
     let sy = t1 * (sx - x1) + y1;
     if(
       sx > min(r1.begin.x, r1.end.x)
       && sx < max(r1.begin.x, r1.end.x)
       && sx > min(r2.begin.x, r2.end.x)
       && sx < max(r2.begin.x, r2.end.x)
     ){
       return new Vec(sx, sy);
     } else{
       return null;
     }
   }

}

/**
 * 视点类
 * @prop {Vec} pos 视点位置向量
 * @prop {number} angle 视线方向角度(弧度制)
 */
class ViewPoint {

  /**
   * 构建视点对象
   * @param {Vec} pos 视点位置向量
   * @param {number} angle 视线方向角度(弧度制)
   * @return {ViewPoint} 视点对象
   */
  constructor(pos, angle){
    this.pos = pos;
    this.angle = angle;
  }

  /**
   * 视线方向的单位向量
   * @type {Vec}
   * @readonly
   */
  get viewLineUnitVector(){
    return Vec.getUnitVectorFromAngle(this.angle);
  }

}