Étant donné un point en dehors d'un arc, comment trouver le point sur l'arc qui s'étend jusqu'à ce point?

Par exemple, le rayon du cercle ( R ) est de 10 cm, son point central est [0,0].
L'origine ( o ) de la ligne ( 8 ) est à [-3, 10]
Comment trouver le point ( p ) (p8) où la tangente à ce point continue à l'origine de la ligne?

Une solution de force brute ne serait pas acceptable.

Extensions of arc

0
Trevor 10 févr. 2020 à 21:31

3 réponses

Meilleure réponse

Soit les coordonnées du point sont px, py, et le centre du cercle est à (0,0) (sinon - soustrayez le centre cx, cy de toutes les coordonnées pour simplifier les équations, ajoutez-les à la fin).

Vous pouvez écrire deux équations pour x,y inconnu. Équation de cercle et perpendicularité un - la tangente est perpendiculaire au rayon-vecteur, leur produit scalaire est nul.

(x - px) * x + (y - py) * y = 0
x^2 + y^2 = r^2

x^2 - px * x + y^2 - py * y = 0
r^2 - px * x = py * y
y = (r^2 - px * x) / py
y^2  = r^4 / py ^2 - x * 2 * r^2 * px / py^2 + x^2 * px^2 / py^2    

x^2 * (1 + px^2 / py^2) - x * 2 * r^2 * px / py^2 +  (r^4 / py^2 - r^2) = 0
x^2 * (py^2 + px^2) - x * 2 * r^2 * px  +  (r^4 - r^2 * py^2) = 0

Résolvez la dernière équation quadratique pour x, puis calculez y.

enter image description here

Fonction Delphi pour référence (remarque: le cas py = 0 est traité séparément)

function GetTangentPts(px, py, r: Double): TArray<Double>;
var
  px2, py2, pxpy, r2, Dis, x, y: Double;
begin
  px2 := px * px;
  py2 := py * py;
  r2 := r * r;
  pxpy := px2 + py2;
  Dis := pxpy - r2;
  if Dis < 0 then    //point is inside
    Exit(nil)
  else if Dis = 0 then begin    //point is at circumference
    SetLength(Result, 2);
    if py = 0 then begin
      x := px;
      y := 0;
    end else begin
      x := px * r2 / pxpy;
      y := (r2 - px * x) / py;
    end;
    Result[0] := x;
    Result[1] := y;
  end else begin       //general case, two tangents
    SetLength(Result, 4);
    if py = 0 then begin
       y := - r * Sqrt(Dis) / px;
       x := px / Abs(px) * r * Sqrt(1 - Dis/px2);
       Result[0] := x;
       Result[1] := y;
       y := r * Sqrt(Dis) / px;
       Result[2] := x;
       Result[3] := y;
    end else begin
      x := (px * r2 - r * Sqrt(py2 * Dis)) / pxpy;
      y := (r2 - px * x) / py;
      Result[0] := x;
      Result[1] := y;
      x := (px * r2 + r * Sqrt(py2 * Dis)) / pxpy;
      y := (r2 - px * x) / py;
      Result[2] := x;
      Result[3] := y;
    end;
  end;
end;

Quelques résultats:

10.00 10.00 10.00 //two perpendicular tangents
 0.00
10.00
10.00
 0.00

-10.00 10.00 10.00
-10.00
 0.00
 0.00
10.00

 1.00  1.00 10.00
 //inside

 0.00 10.00 10.00 //horizontal tangent
 0.00
10.00

10.00  0.00 10.00 //vertical tangent
10.00
 0.00

-14.14  0.00 10.00  //two tangents from OX-axis
-7.07
 7.07
-7.07
-7.07
1
MBo 13 févr. 2020 à 07:43

WLOG le cercle est centré à l'origine. Nous exprimons qu'un point sur le cercle, soit (u, v), forme un angle droit avec les lignes vers le centre et vers le point cible (x, y):

u (x - u) + v (y - v) = 0

Ou

u x + v y = r².

Nous réécrivons et cadrons pour obtenir

(r² - u²) y² = (r² - u x)²,

Une équation quadratique dans u. De là, v = √(r² - u²) suit et vous avez le point de tangence.

1
Yves Daoust 11 févr. 2020 à 14:25

Éditer: Cette première méthode préférée est une version js très basée sur la méthode de @MBo Corrige quelques bugs là-bas.

function tangentLines(centerX, centerY, radius, pX, pY) {
  var horizontalAdjustment, verticalAdjustment, pX_Squared, pY_Squared,
    pXY_Squared, radiusSquared, delta, x, y, result, onSameY, temp

  // Center the circle at [0,0] to simplify things
  onSameY = pY === centerY
  horizontalAdjustment = centerX
  verticalAdjustment = centerY
  pX -= horizontalAdjustment
  pY -= verticalAdjustment
  centerX = centerY = 0
  // If pY is on the same vertical as centerY then temporarily swap pX and pY
  // to avoid bug caused by division of 0
  if(onSameY){
    temp = pY
    pY = pX
    pX = temp
  }

  pX_Squared = pX * pX
  pY_Squared = pY * pY
  radiusSquared = radius * radius
  pXY_Squared = pX_Squared + pY_Squared
  delta = pY_Squared * (pXY_Squared - radiusSquared)

  // Point is inside i.e. no tangent
  if (delta < 0 || pXY_Squared < radiusSquared) {
    return false
  }

  // Point is on circumference only one tangent point
  if (delta === 0) {
    x = (pX * radiusSquared)
    if (pXY_Squared) { x /= pXY_Squared }

    y = (radiusSquared - pX * x)
    if (pY) { y /= pY }
    x += horizontalAdjustment
    y += verticalAdjustment
    return onSameY ? [y,x] : [x, y]
  }

  // Regular case point is outside of tangent there are 2 tangent points
  x = (pX * radiusSquared - radius * Math.sqrt(delta))
  if (pXY_Squared) { x /= pXY_Squared }
  y = (radiusSquared - pX * x)
  if (pY) { y /= pY }
  x += horizontalAdjustment
  y += verticalAdjustment
  result = [
    onSameY ? [y, x] : [x, y],
    []
  ]
  x = (pX * radiusSquared + radius * Math.sqrt(delta))
  if (pXY_Squared) { x /= pXY_Squared }
  y = (radiusSquared - pX * x)
  if (pY) { y /= pY }
  x += horizontalAdjustment
  y += verticalAdjustment
  result[1] = onSameY ? [y, x] : [x, y]
  return result
}

new Vue({
  el: '#app',
  template: `
        <div>
          <div>
            centerX: <input v-model="centerX" @input="intersectionPoints">
            centerY:  <input v-model="centerY" @input="intersectionPoints">
            <div>radius: <input v-model="radius" @input="intersectionPoints"></div>
          </div>
          <div>
            pointX: <input v-model="pointX" @input="intersectionPoints">
            pointY: <input v-model="pointY" @input="intersectionPoints">
          </div>
          <div v-if=result>
          <div>Insect points: {{result}}</div>
          </div>
          <div v-if=!result>No intersections :-(</div>
        </div>
            `,
  data: function() {
    return {
      centerX: 200,
      centerY: 200,
      radius: 100,
      pointX: 160,
      pointY: 100,
      result: null
    };
  },
  methods: {
    intersectionPoints() {
      this.result = tangentLines(
        +this.centerX,
        +this.centerY,
        +this.radius,
        +this.pointX,
        +this.pointY
      );
    }
  },
  mounted: function() {
    this.intersectionPoints();
  }
});

// Without Vue just use something like
// tangentLines(200, 200, 100, 160, 100);
div {
  margin: .5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Voici le code original que j'ai trouvé.

Basé sur la réponse @Aretino https://math.stackexchange.com/questions/3541795/given-a-point-outside-of-an-arc-how-can-one-find -le-point-sur-l'arc-qui-ext / 3541928 # 3541928

  1. Faites un cercle avec un rayon de la moitié de la distance entre o et p, créez-le au milieu de la ligne op.
  2. Calculez le point d'intersection du cercle d'origine et du cercle nouvellement créé.

Intersection point

// Function to find tangent intersection points to a point outside of the circle
// Credits
// https://math.stackexchange.com/questions/3541795/given-a-point-outside-of-an-arc-how-can-one-find-the-point-on-the-arc-which-ext/3541928#3541928
// https://stackoverflow.com/questions/60156373/given-a-point-outside-of-an-arc-how-can-one-find-the-point-on-the-arc-which-ext
/**
 *
 *
 * @param {number} centerX1 Center of circle 1 X
 * @param {number} centerY1 Center of circle 1 Y
 * @param {number} radius1 Radius of circle 1
 * @param {number} centerX2 Center of circle 2 X
 * @param {number} centerY2 Center of circle 2 Y
 * @param {number} radius2 Radius of circle 2
 * @returns {object | boolean} The to intersect points { point1: [x1, y1], point2: [x2, y2] } or false
 * @credit Math based on https://www.analyzemath.com/CircleEq/circle_intersection_calcu.html
 */
var circleIntersections = function(
  centerX1,
  centerY1,
  radius1,
  centerX2,
  centerY2,
  radius2
) {
  var a, b, c, A, B, C, delta, x1, x2, y1, y2;

  a = -(centerY1 - centerY2) / (centerX1 - centerX2);
  b = 2 * (centerX1 - centerX2);
  c =
    (-radius1 * radius1 +
      radius2 * radius2 +
      centerX1 * centerX1 -
      centerX2 * centerX2 +
      centerY1 * centerY1 -
      centerY2 * centerY2) /
    b;
  A = a * a + 1;
  B = 2 * a * c - 2 * centerX1 * a - 2 * centerY1;
  C =
    c * c +
    centerX1 * centerX1 -
    2 * centerX1 * c +
    centerY1 * centerY1 -
    radius1 * radius1;
  delta = B * B - 4 * A * C;
  if (delta < 0) {
    return false;
  }
  y1 = (-B + Math.sqrt(delta)) / (2 * A);
  x1 = a * y1 + c;
  y2 = (-B - Math.sqrt(delta)) / (2 * A);
  x2 = a * y2 + c;

  return {
    point1: [x1, y1],
    point2: [x2, y2]
  };
};

/**
 *
 *
 * @param {number} centerX Center of circle X
 * @param {number} centerY Center of circle Y
 * @param {number} radius Radius of circle
 * @param {number} pointX Point to tangent to X
 * @param {number} pointY Point to tangent to Y
 * @returns {object | boolean} The to intersect points { point1: [x1, y1], point2: [x2, y2] } or false
 */
var tangentLines = function(centerX, centerY, radius, pointX, pointY) {
  var centerX2, centerY2, radius2, dX, dY;
  centerX2 = centerX - (centerX - pointX) / 2;
  centerY2 = centerY - (centerY - pointY) / 2;
  dX = centerX2 - centerX;
  dY = centerY2 - centerY;
  radius2 = Math.sqrt(dX * dX + dY * dY);

  return circleIntersections(
    centerX,
    centerY,
    radius,
    centerX2,
    centerY2,
    radius2
  );
};

new Vue({
  el: '#app',
  template: `
        <div>
          <div>
            centerX: <input v-model="centerX" @input="intersectionPoints">
            centerY:  <input v-model="centerY" @input="intersectionPoints">
            <div>radius: <input v-model="radius" @input="intersectionPoints"></div>
          </div>
          <div>
            pointX: <input v-model="pointX" @input="intersectionPoints">
            pointY: <input v-model="pointY" @input="intersectionPoints">
          </div>
          <div v-if=result>
          <div>point1: {{result.point1}}</div>
          <div>point2: {{result.point2}}</div>
          </div>
          <div v-if=!result>No intersections :-(</div>
        </div>
            `,
  data: function() {
    return {
      centerX: 200,
      centerY: 200,
      radius: 100,
      pointX: 160,
      pointY: 100,
      result: null
    };
  },
  methods: {
    intersectionPoints() {
      this.result = tangentLines(
        this.centerX,
        this.centerY,
        this.radius,
        this.pointX,
        this.pointY
      );
    }
  },
  mounted: function() {
    this.intersectionPoints();
  }
});

// Without Vue just use something like
// tangentLines(200, 200, 100, 160, 100);
div {
margin:5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id=app></div>
0
Trevor 13 févr. 2020 à 07:33