Skip to main content
Code Review

Return to Answer

in Python 2, need floating-point division
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210

Let's set this up in Python. I'm assuming that all the points are pygame.math.Vector2 objects, so that we can add them and take dot products and so on. I'm also assuming that we're using Python 3, so that division returns a float. If you're using Python 2, then you'll need:

from __future__ import division

I'm going to use capital letters for vectors and lower case for scalars:

Let's set this up in Python. I'm assuming that all the points are pygame.math.Vector2 objects, so that we can add them and take dot products and so on. I'm going to use capital letters for vectors and lower case for scalars:

Let's set this up in Python. I'm assuming that all the points are pygame.math.Vector2 objects, so that we can add them and take dot products and so on. I'm also assuming that we're using Python 3, so that division returns a float. If you're using Python 2, then you'll need:

from __future__ import division

I'm going to use capital letters for vectors and lower case for scalars:

added 96 characters in body
Source Link
200_success
  • 145.5k
  • 22
  • 190
  • 478

Circle with centre q with line segment p1-p2 crossing it

Now, a point \$ x \$ is on the circle if its distance from the centre of the circle is equal to the circle's radius, that is, if $$ \lvert x - q \rvert = r. $$ So the line intersects the circle when $$ \lvert p_1 + tv - q \rvert = r. $$ Squaring both sides gives $$ \lvert p_1 + tv - q \rvert^2 = r^2, $$ and now we can use a property of the dot product (namely \$ \lvert A \rvert^2 = A·A \$ for any vector \$ A \$) to get $$ (p_1 + tv - q)·(p_1 + tv - q) = r^2. $$ Expanding the dot product and collecting powers of \$ t \$ gives $$ t^2(v·v)+2t(v·(p_1 − q)) + (p_1·p_1 + q·q − 2p_1·q − r^2) = 0 $$ which is a quadratic equation in \$ t \$ with coefficients$$ a = v·v, b = 2(v·(p_1 − q)), c = p_1·p_1 + q·q − 2p_1·q − r^2 $$

$$\begin{array}{rl} a &= v·v \\ b &= 2(v·(p_1 − q)) \\ c &= p_1·p_1 + q·q − 2p_1·q − r^2\end{array}$$

and solutions $$ t = { −b ± \sqrt{b^2 − 4ac} \over 2a }. $$ Let's compute the coefficients in Python:

Now, a point \$ x \$ is on the circle if its distance from the centre of the circle is equal to the circle's radius, that is, if $$ \lvert x - q \rvert = r. $$ So the line intersects the circle when $$ \lvert p_1 + tv - q \rvert = r. $$ Squaring both sides gives $$ \lvert p_1 + tv - q \rvert^2 = r^2, $$ and now we can use a property of the dot product (namely \$ \lvert A \rvert^2 = A·A \$ for any vector \$ A \$) to get $$ (p_1 + tv - q)·(p_1 + tv - q) = r^2. $$ Expanding the dot product and collecting powers of \$ t \$ gives $$ t^2(v·v)+2t(v·(p_1 − q)) + (p_1·p_1 + q·q − 2p_1·q − r^2) = 0 $$ which is a quadratic equation in \$ t \$ with coefficients$$ a = v·v, b = 2(v·(p_1 − q)), c = p_1·p_1 + q·q − 2p_1·q − r^2 $$ and solutions $$ t = { −b ± \sqrt{b^2 − 4ac} \over 2a }. $$ Let's compute the coefficients in Python:

Circle with centre q with line segment p1-p2 crossing it

Now, a point \$ x \$ is on the circle if its distance from the centre of the circle is equal to the circle's radius, that is, if $$ \lvert x - q \rvert = r. $$ So the line intersects the circle when $$ \lvert p_1 + tv - q \rvert = r. $$ Squaring both sides gives $$ \lvert p_1 + tv - q \rvert^2 = r^2, $$ and now we can use a property of the dot product (namely \$ \lvert A \rvert^2 = A·A \$ for any vector \$ A \$) to get $$ (p_1 + tv - q)·(p_1 + tv - q) = r^2. $$ Expanding the dot product and collecting powers of \$ t \$ gives $$ t^2(v·v)+2t(v·(p_1 − q)) + (p_1·p_1 + q·q − 2p_1·q − r^2) = 0 $$ which is a quadratic equation in \$ t \$ with coefficients

$$\begin{array}{rl} a &= v·v \\ b &= 2(v·(p_1 − q)) \\ c &= p_1·p_1 + q·q − 2p_1·q − r^2\end{array}$$

and solutions $$ t = { −b ± \sqrt{b^2 − 4ac} \over 2a }. $$ Let's compute the coefficients in Python:

fix sign error
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210

Now, a point \$ x \$ is on the circle if its distance from the centre of the circle is equal to the circle's radius, that is, if $$ \lvert x - q \rvert = r. $$ So the line intersects the circle when $$ \lvert p_1 + tv - q \rvert = r. $$ Squaring both sides gives $$ \lvert p_1 + tv - q \rvert^2 = r^2, $$ and now we can use a property of the dot product (namely \$ \lvert A \rvert^2 = A·A \$ for any vector \$ A \$) to get $$ (p_1 + tv - q)·(p_1 + tv - q) = r^2. $$ Expanding the dot product and collecting powers of \$ t \$ gives $$ t^2(v·v)+2t(v·(p_1 − q)) + (p·p + q·q − 2p·q − r^2) = 0 $$$$ t^2(v·v)+2t(v·(p_1 − q)) + (p_1·p_1 + q·q − 2p_1·q − r^2) = 0 $$ which is a quadratic equation in \$ t \$ with coefficients $$ a = v·v, b = 2(v·(p_1 − q)), c = p·p + q·q − 2p·q − r^2 $$$$ a = v·v, b = 2(v·(p_1 − q)), c = p_1·p_1 + q·q − 2p_1·q − r^2 $$ and solutions $$ t = { −b ± \sqrt{b^2 − 4ac} \over 2a }. $$ Let's compute the coefficients in Python:

Now, the closest point on the extended line to the centre of the circle is \$ p_1 + tv \$ where $$ t = { (p_1 - q)·v \over \lvert v \rvert^2 } = { b \over 2a }. $$$$ t = { (q - p_1)·v \over \lvert v \rvert^2 } = { -b \over 2a }. $$ See Wikipedia for an explanation. But we want to ensure that the point is on the line segment, so we must clamp \$ t \$ to lie between 0 and 1.

t = max(0, min(1, - b / (2 * a)))
return True, P1 + t * V

Now, a point \$ x \$ is on the circle if its distance from the centre of the circle is equal to the circle's radius, that is, if $$ \lvert x - q \rvert = r. $$ So the line intersects the circle when $$ \lvert p_1 + tv - q \rvert = r. $$ Squaring both sides gives $$ \lvert p_1 + tv - q \rvert^2 = r^2, $$ and now we can use a property of the dot product (namely \$ \lvert A \rvert^2 = A·A \$ for any vector \$ A \$) to get $$ (p_1 + tv - q)·(p_1 + tv - q) = r^2. $$ Expanding the dot product and collecting powers of \$ t \$ gives $$ t^2(v·v)+2t(v·(p_1 − q)) + (p·p + q·q − 2p·q − r^2) = 0 $$ which is a quadratic equation in \$ t \$ with coefficients $$ a = v·v, b = 2(v·(p_1 − q)), c = p·p + q·q − 2p·q − r^2 $$ and solutions $$ t = { −b ± \sqrt{b^2 − 4ac} \over 2a }. $$ Let's compute the coefficients in Python:

Now, the closest point on the extended line to the centre of the circle is \$ p_1 + tv \$ where $$ t = { (p_1 - q)·v \over \lvert v \rvert^2 } = { b \over 2a }. $$ See Wikipedia for an explanation. But we want to ensure that the point is on the line segment, so we must clamp \$ t \$ to lie between 0 and 1.

t = max(0, min(1, b / (2 * a)))
return True, P1 + t * V

Now, a point \$ x \$ is on the circle if its distance from the centre of the circle is equal to the circle's radius, that is, if $$ \lvert x - q \rvert = r. $$ So the line intersects the circle when $$ \lvert p_1 + tv - q \rvert = r. $$ Squaring both sides gives $$ \lvert p_1 + tv - q \rvert^2 = r^2, $$ and now we can use a property of the dot product (namely \$ \lvert A \rvert^2 = A·A \$ for any vector \$ A \$) to get $$ (p_1 + tv - q)·(p_1 + tv - q) = r^2. $$ Expanding the dot product and collecting powers of \$ t \$ gives $$ t^2(v·v)+2t(v·(p_1 − q)) + (p_1·p_1 + q·q − 2p_1·q − r^2) = 0 $$ which is a quadratic equation in \$ t \$ with coefficients $$ a = v·v, b = 2(v·(p_1 − q)), c = p_1·p_1 + q·q − 2p_1·q − r^2 $$ and solutions $$ t = { −b ± \sqrt{b^2 − 4ac} \over 2a }. $$ Let's compute the coefficients in Python:

Now, the closest point on the extended line to the centre of the circle is \$ p_1 + tv \$ where $$ t = { (q - p_1)·v \over \lvert v \rvert^2 } = { -b \over 2a }. $$ See Wikipedia for an explanation. But we want to ensure that the point is on the line segment, so we must clamp \$ t \$ to lie between 0 and 1.

t = max(0, min(1, - b / (2 * a)))
return True, P1 + t * V
reuse a and b in the computation of t
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210
Loading
Fix closest point calculation
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210
Loading
link to demo
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210
Loading
return the result that seems to be needed by the OP — closest point to centre of circle
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210
Loading
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 210
Loading
lang-py

AltStyle によって変換されたページ (->オリジナル) /