-
-
Notifications
You must be signed in to change notification settings - Fork 190
Implemented the Line.project #3402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 17 commits
22ccf84
e2001dd
193071a
ea76d39
44ab4fc
cd9c648
896f7b2
ed8af87
bfe2698
a291329
317719e
e6c1f8c
383741f
f7178f6
e89deaa
d06b383
81dc704
70dc960
82c43c9
32ae369
1a8d9f6
2401414
4194dce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -740,3 +740,30 @@ | |
.. versionadded:: 2.5.3 | ||
|
||
.. ## Line.flip_ab_ip ## | ||
|
||
.. method:: project | ||
|
||
| :sl:`projects the line onto the given line` | ||
| :sg:`project(point: tuple[float, float], clamp=False) -> tuple[float, float]` | ||
|
||
This method takes in a point and one boolean keyword argument clamp. It outputs an orthogonally projected point onto the line. | ||
If clamp is `True` it makes sure that the outputted point will be on the line segment (which might not be orthogonal), and if it is `False` (the default) then any point on the infinitely extended line may be outputted. | ||
This method can be used to find the closest point on a line to the given point. The output is the unique point on the line or line segment that is the smallest distance away from the given point. | ||
|
||
|
||
.. figure:: code_examples/project.png | ||
:alt: project method image | ||
|
||
Example of how it projects the point onto the line. The red point is the point we want to project and the blue point is what you would get as a result. | ||
|
||
|
||
.. figure:: code_examples/project_clamp.png | ||
:alt: project clamp argument image | ||
|
||
Example of what the clamp argument changes. If it is `True`, the point is bounded between the line segment ends. | ||
|
||
WARNING: This method has to have some length or the clamp parameter must be true for it to work and not throw a `ValueError` | ||
|
||
.. versionadded:: 2.5.4 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this would need to be updated to 2.5.6 after we have consensus on this PR |
||
|
||
.. ## Line.project ## |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -219,6 +219,86 @@ pg_line_scale_ip(pgLineObject *self, PyObject *const *args, Py_ssize_t nargs) | |
Py_RETURN_NONE; | ||
} | ||
|
||
static PyObject * | ||
_line_project_helper(pgLineBase *line, double *point, int clamp) | ||
{ | ||
// this is a vector that goes from one point of the line to another | ||
double line_vector[2] = {line->bx - line->ax, line->by - line->ay}; | ||
double squred_line_length = | ||
XFajk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
line_vector[0] * line_vector[0] + line_vector[1] * line_vector[1]; | ||
|
||
if (squred_line_length == 0.0 && clamp) { | ||
double projected_point[2]; | ||
projected_point[0] = line->ax; | ||
projected_point[1] = line->ay; | ||
return pg_tuple_couple_from_values_double(projected_point[0], | ||
projected_point[1]); | ||
} | ||
else if (squred_line_length == 0.0) { | ||
return RAISE(PyExc_ValueError, | ||
"The Line has to have some length or this method has to " | ||
"be clamped to work"); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API discussion point: is the current implementation better, or would it be better if the clamp and no clamp behaviour was identical when Me personally I don't have much of an opinion either way but I'd like to get other reviewers say on this front, for now I am not asking you to change anything here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it should not error when the line has no length in any case. actually, it makes less sense to error when it is not clamped. there is one good available spot to project the point, which is the only one. The clamped-0-length behavior is the one that actually has an "exception" since the method doesn't know in what direction to project, resulting in the only valid spot. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No one would want their code to error because while the mouse was dragging or while the objects overlapped, the line got a length of zero. If you really do care about it working differently in that case, checking it before hand is straight forward. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that these edge case errors should be avoided when possible. For the 0-length non-clamped projection, the method could possibly either return the line point or the argument point. (Clamped returns the line point.) I think returning the line point is accurate though. |
||
|
||
// this is a vector that goes from the start of the line to the point we | ||
// are projecting onto the line | ||
double vector_from_line_start_to_point[2] = {point[0] - line->ax, | ||
point[1] - line->ay}; | ||
|
||
double dot_product = | ||
(vector_from_line_start_to_point[0] * line_vector[0] + | ||
vector_from_line_start_to_point[1] * line_vector[1]) / | ||
(line_vector[0] * line_vector[0] + line_vector[1] * line_vector[1]); | ||
XFajk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
double projection[2] = {dot_product * line_vector[0], | ||
dot_product * line_vector[1]}; | ||
|
||
if (clamp) { | ||
if (dot_product < 0) { | ||
projection[0] = 0; | ||
projection[1] = 0; | ||
} | ||
else if (projection[0] * projection[0] + | ||
projection[1] * projection[1] > | ||
line_vector[0] * line_vector[0] + | ||
line_vector[1] * line_vector[1]) { | ||
XFajk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
projection[0] = line_vector[0]; | ||
projection[1] = line_vector[1]; | ||
} | ||
} | ||
|
||
double projected_point[2] = {line->ax + projection[0], | ||
line->ay + projection[1]}; | ||
|
||
return pg_tuple_couple_from_values_double(projected_point[0], | ||
projected_point[1]); | ||
} | ||
|
||
static PyObject * | ||
pg_line_project(pgLineObject *self, PyObject *args, PyObject *kwnames) | ||
{ | ||
double point[2] = {0.f, 0.f}; | ||
int clamp = 0; | ||
|
||
PyObject *point_obj = NULL; | ||
|
||
static char *kwlist[] = {"point", "clamp", NULL}; | ||
|
||
if (!PyArg_ParseTupleAndKeywords(args, kwnames, "O|p:project", kwlist, | ||
&point_obj, &clamp)) { | ||
return RAISE( | ||
PyExc_TypeError, | ||
"project requires a sequence(point) and an optional clamp flag"); | ||
} | ||
|
||
if (!pg_TwoDoublesFromObj(point_obj, &point[0], &point[1])) { | ||
return RAISE(PyExc_TypeError, | ||
"project requires a sequence of two numbers"); | ||
} | ||
|
||
return _line_project_helper(&pgLine_AsLine(self), point, clamp); | ||
} | ||
|
||
static struct PyMethodDef pg_line_methods[] = { | ||
{"__copy__", (PyCFunction)pg_line_copy, METH_NOARGS, DOC_LINE_COPY}, | ||
{"copy", (PyCFunction)pg_line_copy, METH_NOARGS, DOC_LINE_COPY}, | ||
|
@@ -231,6 +311,8 @@ static struct PyMethodDef pg_line_methods[] = { | |
{"scale", (PyCFunction)pg_line_scale, METH_FASTCALL, DOC_LINE_SCALE}, | ||
{"scale_ip", (PyCFunction)pg_line_scale_ip, METH_FASTCALL, | ||
DOC_LINE_SCALEIP}, | ||
{"project", (PyCFunction)pg_line_project, METH_VARARGS | METH_KEYWORDS, | ||
DOC_LINE_PROJECT}, | ||
{NULL, NULL, 0, NULL}}; | ||
|
||
static PyObject * | ||
|
Uh oh!
There was an error while loading. Please reload this page.