kernel/kernelVect.js

// on déplace ici dans un fichier séparé tout ce qui était dans kernel et dépendait de Vect ou autre chose
import { colineaires, distancePointPoint, intersectionDroitesSecantes, zero } from './kernel'
import PositionDroites from 'src/types/PositionDroites'
import Vect from 'src/types/Vect'
import PositionCercleCercle from 'src/types/PositionCercleCercle'
import PositionDroiteCercle from 'src/types/PositionDroiteCercle'

/**
 * Renvoie true si les poins de coordonnées (xa,ya), (xb,yb) et (xc,yc)
 * sont considérés comme pratiquement alignés.
 * @param {number} xa
 * @param {number} ya
 * @param {number} xb
 * @param {number} yb
 * @param {number} xc
 * @param {number} yc
 * @returns {boolean}
 */
export function aligne (xa, ya, xb, yb, xc, yc) {
  const u = new Vect()
  const v = new Vect()
  u.x = xb - xa
  u.y = yb - ya
  v.x = xc - xa
  v.y = yc - ya
  const nu = u.norme()
  const nv = v.norme()
  if (zero(nu) || zero(nv)) {
    return true
  }
  const k = (u.x * v.y - v.x * u.y) / nu / nv
  return zero(k)
}

/**
 * Renvoie true si le point de coordonnées (x,y) est considéré comme appartenant
 * au segment dont les extrémités ont pour coordonnées (p1x,p1y) et (p2x,p2y).
 * @param {number} x
 * @param {number} y
 * @param {number} p1x
 * @param {number} p1y
 * @param {number} p2x
 * @param {number} p2y
 * @returns {boolean}
 */
export function appartientSegment (x, y, p1x, p1y, p2x, p2y) {
  const difx = p2x - p1x
  const absx = Math.abs(difx)
  const dify = p2y - p1y
  const absy = Math.abs(dify)
  if (zero(difx) && zero(dify)) {
    return (zero(x - p1x) && zero(y - p1y))
  }
  if (!(aligne(p1x, p1y, p2x, p2y, x, y))) {
    return false
  }
  const k = (absy > absx)
    ? (y - p1y) / dify
    : (x - p1x) / difx
  // Modification apportée pour la version 1.9.5 pour compter avec les erreurs d'arrondi
  return (((k >= 0) && (k <= 1)) || zero(k) || zero(1 - k))
}

/**
 * Pour un point G intérieur à un triangle dont les sommets sont (x1,y1) (x2, y2) et (x3, y3), renvoie dans alpha
 * beta et gamma les coordonnées barycentriques normalisées du point de coordonnées (x,y) (la somme des coeff est 1)
 * G peut être éventuelleùent sur M1M2 ou sur M1M3
 * @param {number} x
 * @param {number} y
 * @param {number} x1
 * @param {number} y1
 * @param {number} x2
 * @param {number} y2
 * @param {number} x3
 * @param {number} y3
 * @param {Pointeur} alpha
 * @param {Pointeur} beta
 * @param {Pointeur} gamma
 * @returns {void}
 */
export function coefficientsPointInterieur (x, y, x1, y1, x2, y2, x3, y3, alpha, beta, gamma) {
  if (appartientSegment(x, y, x1, y1, x2, y2)) {
    coefficientsPointSurSegment(x, y, x1, y1, x2, y2, alpha, beta)
    gamma.setValue(0)
    return
  }
  if (appartientSegment(x, y, x1, y1, x3, y3)) {
    coefficientsPointSurSegment(x, y, x1, y1, x3, y3, alpha, gamma)
    beta.setValue(0)
    return
  }
  const u = new Vect(x, y, x1, y1)
  const v = new Vect(x2, y2, x3, y3)
  // On calcule l'intersection de (AM) avec (BC)
  const d = u.x * v.y - v.x * u.y // d ne doit pas être nul car u et v ne sont pas colinéaires
  // puisque le point est strictement intérieur au triangle
  const k1 = u.y * x1 - u.x * y1
  const k2 = v.y * x2 - v.x * y2
  const xs = (u.x * k2 - v.x * k1) / d
  const ys = (u.y * k2 - v.y * k1) / d
  // xs et ys sont les coordonnées du point d'intersection H qui est sur le segment [BC]
  const ga = u.norme()
  const v1 = new Vect(xs, ys, x2, y2)
  const hb = v1.norme()
  const v2 = new Vect(xs, ys, x3, y3)
  const hc = v2.norme()
  const v3 = new Vect(xs, ys, x, y)
  const gh = v3.norme()
  const den1 = ga + gh
  const den2 = den1 * (hb + hc)
  alpha.setValue(gh / den1)
  beta.setValue(ga * hc / den2)
  gamma.setValue(ga * hb / den2)
}

/**
 * Fonction calculant les coefficients barycentriques d'un point sur un segment
 * @param {number} x  abscisse du point qui est sur le segment
 * @param {number} y  ordonnée du point qui est sur le segment
 * @param {number} x1  Abscisse du point1 du segment
 * @param {number} y1  Ordonnée du point1 du segment
 * @param {number} x2  Abscisse du point2 du segment
 * @param {number} y2  Ordonnée du point2 du segment
 * @param {Pointeur} a  Au retour, getValue() renvoie le premier coefficient barycentrique.
 * @param {Pointeur} b  Au retour, getValue() renvoie le second coefficient barycentrique.
 * @returns {void}
 */
export function coefficientsPointSurSegment (x, y, x1, y1, x2, y2, a, b) {
  const u = new Vect(x, y, x1, y1)
  const v = new Vect(x, y, x2, y2)
  const nu = u.norme()
  const nv = v.norme()
  const d = nu + nv
  a.setValue(nv / d)
  b.setValue(nu / d)
}

/**
 * Fonction renvoyant true seulement si le point de coordonnées (x; y)intérieur
 * au triangle dont les sommets ont pour coordonnées (x1; y1), (x2, y2) et (x3, y3).
 * Il peut être sur le côté M1M2 ou sur le côté M2M3.
 * Sert pour déterminer la position d'un point lié à l'intérieur d'un polygone.
 * @param {number} x
 * @param {number} y
 * @param {number} x1
 * @param {number} y1
 * @param {number} x2
 * @param {number} y2
 * @param {number} x3
 * @param {number} y3
 * @returns {boolean}
 */
export function estInterieurATriangle (x, y, x1, y1, x2, y2, x3, y3) {
  if (appartientSegment(x, y, x1, y1, x2, y2) || appartientSegment(x, y, x2, y2, x3, y3)) return true
  const u = new Vect(x, y, x1, y1)
  const v = new Vect(x, y, x2, y2)
  const w = new Vect(x, y, x3, y3)
  const ang = u.mesureAngleVecteurs(v) + v.mesureAngleVecteurs(w) + w.mesureAngleVecteurs(u)
  const q = Math.abs(Math.round(ang / (2 * Math.PI)))
  return q === 1
}

/**
 * Calcule l'intersection de deux droites et renvoie un entier du type PositionDroites
 * @param {number} p1x  Abscisse d'un point de la première droite
 * @param {number} p1y  Ordonnée d'un point de la première droite
 * @param {Vect} u1  Vecteur directeur de la première droite.
 * @param {number} p2x  Abscisse d'un point de la deuxième droite
 * @param {number} p2y  Ordonnée d'un point de la deuxième droite
 * @param {Vect} u2  Vecteur directeur de la deuxième droite.
 * @param {Point} pointInt  Contient au retour les coordonnées du point d'intersection.
 * @returns {PositionDroites}
 */
export function intersection (p1x, p1y, u1, p2x, p2y, u2, pointInt) {
  const d = u1.x * u2.y - u2.x * u1.y
  // Modifié pour la version 1.9 : on teste la presque nullité du sinus et
  // non du déterminant
  if (zero(d / u1.norme() / u2.norme())) {
    const w = new Vect(p1x, p1y, p2x, p2y)
    // Il faut appeler colineaires() car w peut être nul
    return (colineaires(u1, w)) ? PositionDroites.Confondues : PositionDroites.Paralleles
  }
  const position = PositionDroites.Secantes
  const k1 = u1.y * p1x - u1.x * p1y
  const k2 = u2.y * p2x - u2.x * p2y
  pointInt.x = (u1.x * k2 - u2.x * k1) / d
  pointInt.y = (u1.y * k2 - u2.y * k1) / d
  return position
}

// Idem pour l'intersection d'une droite et d'un cercle
/**
 * Calcule l'intersection de deux cercles et renvoie un entier du type PositionCercleCercle
 * donnant la nature de cette intersection.
 * @param {number} c1x  Abscisse du centre du premier cercle.
 * @param {number} c1y  Ordonnée du centre du premier cercle.
 * @param {number} r1  Rayon du premier cercle.
 * @param {number} c2x  Abscisse du centre du second cercle.
 * @param {number} c2y  Ordonnée du centre du second cercle.
 * @param {number} r2  Rayon du second cercle.
 * @param {CPt} point1
 * @param {CPt} point2
 * @returns {PositionCercleCercle}
 */
export function intersectionCercleCercle (c1x, c1y, r1, c2x, c2y, r2, point1, point2) {
  let d, xi, yi, xh, yh, k
  const u = new Vect()
  const v = new Vect()
  if (zero(c1x - c2x) && zero(c1y - c2y)) {
    if (zero(r1 - r2)) return PositionCercleCercle.Confondus
    else return PositionCercleCercle.Vide
  } else {
    xi = (c1x + c2x) / 2
    yi = (c1y + c2y) / 2 // coordonnées du milieu de M1M2;
    const u0 = new Vect(c1x, c1y, c2x, c2y)
    d = distancePointPoint(c1x, c1y, c2x, c2y)
    u0.vecteurColineaire(1, u) // u colinéaire à M1M2 et même sens
    k = (r1 * r1 - r2 * r2) / 2 / d
    xh = xi + k * u.x
    yh = yi + k * u.y
    u.getOrthogonal(v)
    const positionDroites = intersectionDroiteCercle(xh, yh, v, c1x, c1y, r1, point1, point2)
    switch (positionDroites) {
      case PositionDroiteCercle.Tangents:
        return PositionCercleCercle.Tangents
      case PositionDroiteCercle.Secants:
        return PositionCercleCercle.Secants
      case PositionDroiteCercle.Vide:
        return PositionCercleCercle.Vide
      default : return PositionCercleCercle.Vide // Pour satisfaire le compilateur
    }
  }
}

/**
 * Calcule l'intersection d'une droite et un cercle et renvoie un entier du type PositionDroiteCercle
 * donnant la nature de cette intersection.
 * @param {number} px  Abscisse d'un point de la droite.
 * @param {number} py  Ordonnées d'un point de la droite.
 * @param {Vect} u  Un vecteur directeur de la droite.
 * @param {number} cx  Abscisse du cntre du cercle.
 * @param {number} cy  Ordonnée du cntre du cercle.
 * @param {number} R  Le rayon d cercle.
 * @param {Point} point1  Contient au retour les coordonnées du premier point d'intersection
 * (si il y en a).
 * @param {Point} point2  Contient au retour les coordonnées du second point d'intersection
 * (si il y en a).
 * @returns {PositionDroiteCercle}
 */
export function intersectionDroiteCercle (px, py, u, cx, cy, R, point1, point2) {
  const a = u.x
  const b = u.y
  const k1 = px - cx
  const k2 = py - cy
  const k3 = a * k1 + b * k2
  const k4 = a * k2 - b * k1
  const n2 = a * a + b * b
  const distcentredroite = Math.abs(a * k2 - b * k1) / Math.sqrt(n2)

  if (zero(distcentredroite - R)) {
    const t1 = -k3 / n2
    point1.x = px + a * t1
    point1.y = py + b * t1
    point2.x = point1.x
    point2.y = point1.y
    return PositionDroiteCercle.Tangents
  }

  if (distcentredroite > R) {
    return PositionDroiteCercle.Vide
  }

  const dis = n2 * R * R - k4 * k4
  const t1 = (Math.sqrt(dis) - k3) / n2// formules du second degré
  const t2 = (-Math.sqrt(dis) - k3) / n2
  point1.x = px + a * t1
  point1.y = py + b * t1
  point2.x = px + a * t2
  point2.y = py + b * t2
  return PositionDroiteCercle.Secants
}

/**
 * Renvoie dans res.x et res.y les coordonnées du projeté orthogonal du point
 * de coordonnées (x1,y1) sur la droite passant par le point de coordonnées
 * (p1x,p1y) et de vecteur directeur u.
 * @param {number} x1
 * @param {number} y1
 * @param {number} p1x
 * @param {number} p1y
 * @param {Vect} u
 * @param {Object} res
 * @returns {void}
 */
export function projetteOrtho (x1, y1, p1x, p1y, u, res) {
  const v = new Vect()
  u.getOrthogonal(v)
  intersectionDroitesSecantes(x1, y1, v, p1x, p1y, u, res)
}