Home Reference Source Repository

src/app/components/circumference-component/models/circumference-model.js

/*
 * iGeom by LInE
 * Geometric Object: Circumference
 * Model to Circumference
 * www.matematica.br/igeom
 * ./app/components/line-component/models/circumference-model.js
 * @version 2020/11/02: Implemented Line instersection
 */

import { ELEMENTS_CLASS } from "../../../core/enums/elements-class-enum";
import { GeometricObject } from "../../../core/models/objects/geometric-object";
import { IntersectionModel } from "../../intersection-component/models/intersection-model";

export class CircumferenceModel extends GeometricObject {

  constructor(center, radius, id) {
    super(id);
    this.center = center;
    this.radius = radius;
    this._coordinates = [];
    this._coordinates[0] = this.center.posX;
    this._coordinates[1] = this.center.posY;
    this._coordinates[2] = this.radius.posX;
    this._coordinates[3] = this.radius.posY;
    this.color = -16776961;
    super.setClass(ELEMENTS_CLASS.CIRCUMFERENCE);
    this.definitions.push(this.center);
    this.definitions.push(this.radius);
  }

  getCenter() {
    return this.center;
  }

  getPoint() {
    return this.radius;
  }

  getRadius() {
    /* Deixar mais intuitiva...
    this._coordinates[0] = this.center.posX;
    this._coordinates[1] = this.center.posY;
    this._coordinates[2] = this.radius.posX;
    this._coordinates[3] = this.radius.posY;
    const legA = this._coordinates[2] - this._coordinates[0];
    const legB = this._coordinates[3] - this._coordinates[1];
    const radius = Math.sqrt(Math.pow(legA, 2) + Math.pow(legB, 2)); // Desnecessario!
    return radius;
    */
    var Cx, Cy, Ax, Ay;
    Cx = this._coordinates[0] = this.center.posX;
    Cy = this._coordinates[1] = this.center.posY;
    Ax = this._coordinates[2] = this.radius.posX;
    Ay = this._coordinates[3] = this.radius.posY;
    //D console.log("circumference-model.js: circ((" + Cx +","+ Cy +"), ("+Ax+","+Ay+")");
    const dx = Ax - Cx;
    const dy = Ay - Cy;
    const radius = Math.sqrt(dx * dx + dy * dy);
    return radius;
  }

  getStraight() {
    const dx = this.radius.posX - this.center.posX;
    const dy = this.radius.posY - this.center.posY;
    const c = Math.sqrt(dy * dy + dx * dx);
    return [dx, dy, c];
  }

  getDirection() {
    const aX = this.center.posX;
    const aY = this.center.posY;
    const bX = this.radius.posX;
    const bY = this.radius.posY;
    const a = bX - aX;
    const b = bY - aY;
    return [a, b];
  }

  getIntersection(geometricObject) {
    switch (geometricObject.elementClass) {
      case ELEMENTS_CLASS.LINE: // StraightLine
        return geometricObject.getIntersectionWithCircumference(this); // Delegate to StraightLine    
      case ELEMENTS_CLASS.LINE_SEGMENT:
        return this.getIntersectionByLine(geometricObject); //TODO melhor 'with' que 'by'
      case ELEMENTS_CLASS.CIRCUMFERENCE:
        return this.getIntersectionsByCircumference(geometricObject); //TODO melhor 'with' que 'by'
      default:
        break;
    }
  }

  distance(center) {
    const dx = center.posX - this.center.posX;
    const dy = center.posY - this.center.posY;
    const dist = Math.sqrt(dy * dy + dx * dx);
    return dist;
  }

  getIntersectionsByCircumference(circumference) {
    //TODO if (this.cente().igual(cd.C())) return null; // duas circ. com mesmo raio => devolva "null" (ambiguidade!)
    const r1 = this.getRadius(); // raio circ. atual
    const r2 = circumference.getRadius(); // raio circ. "cd"
    const d = this.distance(circumference); // distancia entre os raios das circ.
    if (r1 + r2 < d || Math.abs(r1 - r2) > d) return null;
    const A = this.center; // pega o centro da circunferencia atual
    const B = circumference.center; // pega o centro da circunferencia "cd"
    const x = B.posX - A.posX;
    const y = B.posY - A.posY;
    let cos = x / Math.sqrt(x * x + y * y); //(B.posX-A.posX)/Math.sqrt(x*x + y*y); // cos a = <z,w>/(|z||w|)
    let sen = Math.sqrt(1 - cos * cos);
    if (A.posY > B.posY) sen = -sen;
    let u1 = B.posX - A.posX;
    let v1 = B.posY - A.posY;
    let u = u1 * cos + v1 * sen;
    let v = -u1 * sen + v1 * cos;
    const rA = this.getRadius();
    const rB = circumference.getRadius();
    u = (u * u - rB * rB + rA * rA) / (2 * u);
    v = Math.sqrt(rA * rA - u * u);
    v1 = v;
    let v2 = -v;
    let x1 = u * cos - v1 * sen;
    let y1 = u * sen + v1 * cos;
    let x2 = u * cos - v2 * sen;
    let y2 = u * sen + v2 * cos;
    x1 = A.posX + x1;
    y1 = A.posY + y1;
    x2 = A.posX + x2;
    y2 = A.posY + y2;
    return [
      new IntersectionModel(x1, y1, undefined, this, circumference, true, 0),
      new IntersectionModel(x2, y2, undefined, this, circumference, true, 1)
    ];
  }


  /// Get Intersection Poin By Circumference and Line Segment
  /// @param {LineSegmentModel  } lineSegment 
  getIntersectionByLine(lineSegment) {
    const pointA = lineSegment.pointA, pointB = lineSegment.pointB; // sl = segment(A,B)
    const center = this.center, radius = this.getRadius();          // c0 = circunference(C,A) = circ("center,radius")

    const dx = pointB.posX - pointA.posX;
    const dy = pointB.posY - pointA.posY;

    const cx = center.posX - pointA.posX;
    const cy = center.posY - pointA.posY;

    const a = dx * dx + dy * dy;
    const b = dx * cx + dy * cy;
    const c = cx * cx + cy * cy - radius * radius;

    const D = b / a;
    const q = c / a;

    const delta = D * D - q;
    const PA = new IntersectionModel(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, undefined, lineSegment, this, false, 0);
    const PB = new IntersectionModel(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, undefined, lineSegment, this, false, 1);

    if (delta < 0) {
      return [PA, PB];
    }

    const deltaSqrt = Math.sqrt(delta);
    const root1 = -D + deltaSqrt;
    const root2 = -D - deltaSqrt;
    const x1 = pointA.posX - dx * root1
    const y1 = pointA.posY - dy * root1;

    if (delta == 0) {
      PA.bind(x1, y1, undefined, lineSegment, this, true, 0);
      return [PA, PB];
    }

    const x2 = pointA.posX - dx * root2;
    const y2 = pointA.posY - dy * root2;

    PA.bind(x1, y1, undefined, lineSegment, this, true, 0);
    PB.bind(x2, y2, undefined, lineSegment, this, true, 1);

    if (this.visible) {
      if (!this.insideSegment(PA.posX, PA.posY))
        PA.bind(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, undefined, lineSegment, this, false, 0)

      if (!this.insideSegment(PB.posX, PB.posY))
        PB.bind(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, undefined, lineSegment, this, false, 1)
    }

    return [PA, PB];
  }

  // Verify intersection using "level set"
  insideSegment(intersecX, intersecY) {
    const valuesR = this.getDirection();
    const dirX = valuesR[0];
    const dirY = valuesR[1];
    const cInterA = dirX * intersecX + dirY * intersecY;

    // comparaca cv do ponto A > cv da intersec =>  vazio
    const cRA = dirX * this.center.posX + dirY * this.center.posY;

    if (cInterA < cRA) {
      return false;
    }

    // comparaca cv do ponto B < cv da intersec =>  vazio
    const cRB = dirX * this.radius.posX + dirY * this.radius.posY;

    if (cInterA > cRB) {
      this.posX = Number.MAX_SAFE_INTEGER;
      this.posY = Number.MAX_SAFE_INTEGER;
      return false;
    }

    return true;
  }

  static do(map, list) {
    const id = map.get("id");
    const centerId = map.get("param")[0];
    const radiusId = map.get("param")[1];
    const center = list.find(x => x.id === centerId);
    const radius = list.find(x => x.id === radiusId);
    const label = map.get("label")[0];
    const circumference = new CircumferenceModel(center, radius, id);
    circumference.setLabel(label);
    return circumference;
  }

}