Scala: Orthogonal projection of a point onto a line












2















I'm trying to make a function that takes 3 points as arguments. The first two of which represent two points on a line. The third one represents another point, outside of that line. Suppose a perpendicular through the third point on the line defined by the first two points. Now what I want to do, is calculate that intersection. I've come up with this procedure so far, but somehow it works only like 50% of the time. Could somebody figure out what I'm doing wrong here?



def calculateIntersection(p1: (Double, Double), p2: (Double, Double), c: (Double, Double)): (Double, Double) = {
var intersection: (Double, Double) = null
// CASE 1: line is vertical
if(p1._1 == p2._1) {
intersection = (p1._1, c._2)
}
// CASE 2: line is horizontal
else if(p1._2 == p2._2) {
intersection = (c._1, p1._2)
}
// CASE 3: line is neither vertical, nor horizontal
else {
val slope1: Double = (p2._2 - p1._2) / (p2._1 - p1._1) // slope of the line
val slope2: Double = pow(slope1, -1) * -1 // slope of the perpendicular
val intercept1: Double = p1._2 - (slope1 * p1._1) // y-intercept of the line
val intercept2: Double = c._2 - (slope2 * c._1) // y-intercept of the perpendicular
intersection = ((intercept2 - intercept1) / (slope1 - slope2),
slope1 * ((intercept2 - intercept1) / (slope1 - slope2)) + intercept1)
}
intersection
}









share|improve this question

























  • There are at least three grave mistakes in this code: 1) You are treating horizontal lines separately. 2) You are treating vertical lines separately. 3) You are computing something with "slopes". No wonder it doesn't work. But that's hardly your fault: I've tried googling or finding a suitable duplicate, and I couldn't find anything. The amount of outrageous nonsense written on this topic is just staggering... Can anyone find a sane duplicate, please?

    – Andrey Tyukin
    Jan 2 at 17:05











  • What do you mean exactly? I thought working with the slope was the exact way to do it. My strategy was to calculate de equation of the line (given the two points), then extracting the slope out of that equation, because we know the slope of the perpendicular will be the opposite reciprocal of the slope of the line. Given the third point, and that slope, we can also find the equation of the perpendicular. So now I converted the two equations in order to find the intersection.

    – Frederik Vanclooster
    Jan 2 at 17:29











  • Possible duplicate of Perpendicular on a line from a given point

    – Ripi2
    Jan 2 at 17:51






  • 1





    I mean that if anyone tries to sell you "angles", "slopes" or "square roots" when dealing with objects that are completely covered by basic linear algebra, you should run as fast as possible. Remember, Carmack wrote Quake 3 without even using square roots. If your sketch on the paper contains only points, lines, and planes, but the formula contains angles, slopes, and square roots, you should treat it with healthy suspicion.

    – Andrey Tyukin
    Jan 2 at 18:04


















2















I'm trying to make a function that takes 3 points as arguments. The first two of which represent two points on a line. The third one represents another point, outside of that line. Suppose a perpendicular through the third point on the line defined by the first two points. Now what I want to do, is calculate that intersection. I've come up with this procedure so far, but somehow it works only like 50% of the time. Could somebody figure out what I'm doing wrong here?



def calculateIntersection(p1: (Double, Double), p2: (Double, Double), c: (Double, Double)): (Double, Double) = {
var intersection: (Double, Double) = null
// CASE 1: line is vertical
if(p1._1 == p2._1) {
intersection = (p1._1, c._2)
}
// CASE 2: line is horizontal
else if(p1._2 == p2._2) {
intersection = (c._1, p1._2)
}
// CASE 3: line is neither vertical, nor horizontal
else {
val slope1: Double = (p2._2 - p1._2) / (p2._1 - p1._1) // slope of the line
val slope2: Double = pow(slope1, -1) * -1 // slope of the perpendicular
val intercept1: Double = p1._2 - (slope1 * p1._1) // y-intercept of the line
val intercept2: Double = c._2 - (slope2 * c._1) // y-intercept of the perpendicular
intersection = ((intercept2 - intercept1) / (slope1 - slope2),
slope1 * ((intercept2 - intercept1) / (slope1 - slope2)) + intercept1)
}
intersection
}









share|improve this question

























  • There are at least three grave mistakes in this code: 1) You are treating horizontal lines separately. 2) You are treating vertical lines separately. 3) You are computing something with "slopes". No wonder it doesn't work. But that's hardly your fault: I've tried googling or finding a suitable duplicate, and I couldn't find anything. The amount of outrageous nonsense written on this topic is just staggering... Can anyone find a sane duplicate, please?

    – Andrey Tyukin
    Jan 2 at 17:05











  • What do you mean exactly? I thought working with the slope was the exact way to do it. My strategy was to calculate de equation of the line (given the two points), then extracting the slope out of that equation, because we know the slope of the perpendicular will be the opposite reciprocal of the slope of the line. Given the third point, and that slope, we can also find the equation of the perpendicular. So now I converted the two equations in order to find the intersection.

    – Frederik Vanclooster
    Jan 2 at 17:29











  • Possible duplicate of Perpendicular on a line from a given point

    – Ripi2
    Jan 2 at 17:51






  • 1





    I mean that if anyone tries to sell you "angles", "slopes" or "square roots" when dealing with objects that are completely covered by basic linear algebra, you should run as fast as possible. Remember, Carmack wrote Quake 3 without even using square roots. If your sketch on the paper contains only points, lines, and planes, but the formula contains angles, slopes, and square roots, you should treat it with healthy suspicion.

    – Andrey Tyukin
    Jan 2 at 18:04
















2












2








2


1






I'm trying to make a function that takes 3 points as arguments. The first two of which represent two points on a line. The third one represents another point, outside of that line. Suppose a perpendicular through the third point on the line defined by the first two points. Now what I want to do, is calculate that intersection. I've come up with this procedure so far, but somehow it works only like 50% of the time. Could somebody figure out what I'm doing wrong here?



def calculateIntersection(p1: (Double, Double), p2: (Double, Double), c: (Double, Double)): (Double, Double) = {
var intersection: (Double, Double) = null
// CASE 1: line is vertical
if(p1._1 == p2._1) {
intersection = (p1._1, c._2)
}
// CASE 2: line is horizontal
else if(p1._2 == p2._2) {
intersection = (c._1, p1._2)
}
// CASE 3: line is neither vertical, nor horizontal
else {
val slope1: Double = (p2._2 - p1._2) / (p2._1 - p1._1) // slope of the line
val slope2: Double = pow(slope1, -1) * -1 // slope of the perpendicular
val intercept1: Double = p1._2 - (slope1 * p1._1) // y-intercept of the line
val intercept2: Double = c._2 - (slope2 * c._1) // y-intercept of the perpendicular
intersection = ((intercept2 - intercept1) / (slope1 - slope2),
slope1 * ((intercept2 - intercept1) / (slope1 - slope2)) + intercept1)
}
intersection
}









share|improve this question
















I'm trying to make a function that takes 3 points as arguments. The first two of which represent two points on a line. The third one represents another point, outside of that line. Suppose a perpendicular through the third point on the line defined by the first two points. Now what I want to do, is calculate that intersection. I've come up with this procedure so far, but somehow it works only like 50% of the time. Could somebody figure out what I'm doing wrong here?



def calculateIntersection(p1: (Double, Double), p2: (Double, Double), c: (Double, Double)): (Double, Double) = {
var intersection: (Double, Double) = null
// CASE 1: line is vertical
if(p1._1 == p2._1) {
intersection = (p1._1, c._2)
}
// CASE 2: line is horizontal
else if(p1._2 == p2._2) {
intersection = (c._1, p1._2)
}
// CASE 3: line is neither vertical, nor horizontal
else {
val slope1: Double = (p2._2 - p1._2) / (p2._1 - p1._1) // slope of the line
val slope2: Double = pow(slope1, -1) * -1 // slope of the perpendicular
val intercept1: Double = p1._2 - (slope1 * p1._1) // y-intercept of the line
val intercept2: Double = c._2 - (slope2 * c._1) // y-intercept of the perpendicular
intersection = ((intercept2 - intercept1) / (slope1 - slope2),
slope1 * ((intercept2 - intercept1) / (slope1 - slope2)) + intercept1)
}
intersection
}






scala math geometry






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 2 at 19:04









Andrey Tyukin

30.1k42351




30.1k42351










asked Jan 2 at 16:28









Frederik VancloosterFrederik Vanclooster

474




474













  • There are at least three grave mistakes in this code: 1) You are treating horizontal lines separately. 2) You are treating vertical lines separately. 3) You are computing something with "slopes". No wonder it doesn't work. But that's hardly your fault: I've tried googling or finding a suitable duplicate, and I couldn't find anything. The amount of outrageous nonsense written on this topic is just staggering... Can anyone find a sane duplicate, please?

    – Andrey Tyukin
    Jan 2 at 17:05











  • What do you mean exactly? I thought working with the slope was the exact way to do it. My strategy was to calculate de equation of the line (given the two points), then extracting the slope out of that equation, because we know the slope of the perpendicular will be the opposite reciprocal of the slope of the line. Given the third point, and that slope, we can also find the equation of the perpendicular. So now I converted the two equations in order to find the intersection.

    – Frederik Vanclooster
    Jan 2 at 17:29











  • Possible duplicate of Perpendicular on a line from a given point

    – Ripi2
    Jan 2 at 17:51






  • 1





    I mean that if anyone tries to sell you "angles", "slopes" or "square roots" when dealing with objects that are completely covered by basic linear algebra, you should run as fast as possible. Remember, Carmack wrote Quake 3 without even using square roots. If your sketch on the paper contains only points, lines, and planes, but the formula contains angles, slopes, and square roots, you should treat it with healthy suspicion.

    – Andrey Tyukin
    Jan 2 at 18:04





















  • There are at least three grave mistakes in this code: 1) You are treating horizontal lines separately. 2) You are treating vertical lines separately. 3) You are computing something with "slopes". No wonder it doesn't work. But that's hardly your fault: I've tried googling or finding a suitable duplicate, and I couldn't find anything. The amount of outrageous nonsense written on this topic is just staggering... Can anyone find a sane duplicate, please?

    – Andrey Tyukin
    Jan 2 at 17:05











  • What do you mean exactly? I thought working with the slope was the exact way to do it. My strategy was to calculate de equation of the line (given the two points), then extracting the slope out of that equation, because we know the slope of the perpendicular will be the opposite reciprocal of the slope of the line. Given the third point, and that slope, we can also find the equation of the perpendicular. So now I converted the two equations in order to find the intersection.

    – Frederik Vanclooster
    Jan 2 at 17:29











  • Possible duplicate of Perpendicular on a line from a given point

    – Ripi2
    Jan 2 at 17:51






  • 1





    I mean that if anyone tries to sell you "angles", "slopes" or "square roots" when dealing with objects that are completely covered by basic linear algebra, you should run as fast as possible. Remember, Carmack wrote Quake 3 without even using square roots. If your sketch on the paper contains only points, lines, and planes, but the formula contains angles, slopes, and square roots, you should treat it with healthy suspicion.

    – Andrey Tyukin
    Jan 2 at 18:04



















There are at least three grave mistakes in this code: 1) You are treating horizontal lines separately. 2) You are treating vertical lines separately. 3) You are computing something with "slopes". No wonder it doesn't work. But that's hardly your fault: I've tried googling or finding a suitable duplicate, and I couldn't find anything. The amount of outrageous nonsense written on this topic is just staggering... Can anyone find a sane duplicate, please?

– Andrey Tyukin
Jan 2 at 17:05





There are at least three grave mistakes in this code: 1) You are treating horizontal lines separately. 2) You are treating vertical lines separately. 3) You are computing something with "slopes". No wonder it doesn't work. But that's hardly your fault: I've tried googling or finding a suitable duplicate, and I couldn't find anything. The amount of outrageous nonsense written on this topic is just staggering... Can anyone find a sane duplicate, please?

– Andrey Tyukin
Jan 2 at 17:05













What do you mean exactly? I thought working with the slope was the exact way to do it. My strategy was to calculate de equation of the line (given the two points), then extracting the slope out of that equation, because we know the slope of the perpendicular will be the opposite reciprocal of the slope of the line. Given the third point, and that slope, we can also find the equation of the perpendicular. So now I converted the two equations in order to find the intersection.

– Frederik Vanclooster
Jan 2 at 17:29





What do you mean exactly? I thought working with the slope was the exact way to do it. My strategy was to calculate de equation of the line (given the two points), then extracting the slope out of that equation, because we know the slope of the perpendicular will be the opposite reciprocal of the slope of the line. Given the third point, and that slope, we can also find the equation of the perpendicular. So now I converted the two equations in order to find the intersection.

– Frederik Vanclooster
Jan 2 at 17:29













Possible duplicate of Perpendicular on a line from a given point

– Ripi2
Jan 2 at 17:51





Possible duplicate of Perpendicular on a line from a given point

– Ripi2
Jan 2 at 17:51




1




1





I mean that if anyone tries to sell you "angles", "slopes" or "square roots" when dealing with objects that are completely covered by basic linear algebra, you should run as fast as possible. Remember, Carmack wrote Quake 3 without even using square roots. If your sketch on the paper contains only points, lines, and planes, but the formula contains angles, slopes, and square roots, you should treat it with healthy suspicion.

– Andrey Tyukin
Jan 2 at 18:04







I mean that if anyone tries to sell you "angles", "slopes" or "square roots" when dealing with objects that are completely covered by basic linear algebra, you should run as fast as possible. Remember, Carmack wrote Quake 3 without even using square roots. If your sketch on the paper contains only points, lines, and planes, but the formula contains angles, slopes, and square roots, you should treat it with healthy suspicion.

– Andrey Tyukin
Jan 2 at 18:04














1 Answer
1






active

oldest

votes


















3














Given the following definitions:



type Point = (Double, Double)

implicit class PointOps(p: Point) {
def +(other: Point) = (p._1 + other._1, p._2 + other._2)
def -(other: Point) = (p._1 - other._1, p._2 - other._2)
def dot(other: Point) = p._1 * other._1 + p._2 * other._2
def *(scalar: Double) = (p._1 * scalar, p._2 * scalar)
def normSquare: Double = p._1 * p._1 + p._2 * p._2
}


meaning that



a + b        // is vector addition
a - b // is vector subtraction
a dot b // is the dot product (scalar product)
a * f // is multiplication of a vector `a` with a scalar factor `f`
a.normSquare // is the squared length of a vector


you obtain the projection of a point p on line going through points line1 and line2 as follows:



/** Projects point `p` on line going through two points `line1` and `line2`. */
def projectPointOnLine(line1: Point, line2: Point, p: Point): Point = {
val v = p - line1
val d = line2 - line1
line1 + d * ((v dot d) / d.normSquare)
}


Example:



println(projectPointOnLine((-1.0, 10.0), (7.0, 4.0), (6.0, 11.0)))


gives



(3.0, 7.0)


This works in 3D (or n-D) in exactly the same way.





Some math behind that (How to derive it from scratch)



(notation as above)



We have three points: l1 and l2 for the line and p for the target point.
We want to project the point p orthogonally onto the line that goes through l1 and l2 (assuming l1 != l2).



Let d = l2 - l1 be the direction from l1 to l2. Then every point on the line can be represented as



l1 + d * t


with some scalar factor t. Now we want to find a t such that the vector connecting p and l1 + d * t is orthogonal to d, that is:



(p - (l1 + d * t)) dot d == 0


Recall that



(v1 + v2) dot v3 = (v1 dot v3) + (v2 dot v3)


for all vectors v1, v2, v3, and that



(v1 * s) dot v2 = (v1 dot v2) * s


for scalar factors s. Using this and the definition v.normSquared = v dot v, we obtain:



(p - l1 - d * t) dot d 
= (p - l1) dot d - (d dot d) * t
= (p - l1) dot d - d.normSquare * t


and this should become 0. Resolving for t gives:



t = ((p - l1) dot d) / d.normSquare


and this is exactly the formula that is used in the code.



(Thanks at SergGr for adding an initial sketch of the derivation)






share|improve this answer


























  • Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

    – SergGr
    Jan 2 at 18:18













  • @SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

    – Andrey Tyukin
    Jan 2 at 18:36













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54009832%2fscala-orthogonal-projection-of-a-point-onto-a-line%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









3














Given the following definitions:



type Point = (Double, Double)

implicit class PointOps(p: Point) {
def +(other: Point) = (p._1 + other._1, p._2 + other._2)
def -(other: Point) = (p._1 - other._1, p._2 - other._2)
def dot(other: Point) = p._1 * other._1 + p._2 * other._2
def *(scalar: Double) = (p._1 * scalar, p._2 * scalar)
def normSquare: Double = p._1 * p._1 + p._2 * p._2
}


meaning that



a + b        // is vector addition
a - b // is vector subtraction
a dot b // is the dot product (scalar product)
a * f // is multiplication of a vector `a` with a scalar factor `f`
a.normSquare // is the squared length of a vector


you obtain the projection of a point p on line going through points line1 and line2 as follows:



/** Projects point `p` on line going through two points `line1` and `line2`. */
def projectPointOnLine(line1: Point, line2: Point, p: Point): Point = {
val v = p - line1
val d = line2 - line1
line1 + d * ((v dot d) / d.normSquare)
}


Example:



println(projectPointOnLine((-1.0, 10.0), (7.0, 4.0), (6.0, 11.0)))


gives



(3.0, 7.0)


This works in 3D (or n-D) in exactly the same way.





Some math behind that (How to derive it from scratch)



(notation as above)



We have three points: l1 and l2 for the line and p for the target point.
We want to project the point p orthogonally onto the line that goes through l1 and l2 (assuming l1 != l2).



Let d = l2 - l1 be the direction from l1 to l2. Then every point on the line can be represented as



l1 + d * t


with some scalar factor t. Now we want to find a t such that the vector connecting p and l1 + d * t is orthogonal to d, that is:



(p - (l1 + d * t)) dot d == 0


Recall that



(v1 + v2) dot v3 = (v1 dot v3) + (v2 dot v3)


for all vectors v1, v2, v3, and that



(v1 * s) dot v2 = (v1 dot v2) * s


for scalar factors s. Using this and the definition v.normSquared = v dot v, we obtain:



(p - l1 - d * t) dot d 
= (p - l1) dot d - (d dot d) * t
= (p - l1) dot d - d.normSquare * t


and this should become 0. Resolving for t gives:



t = ((p - l1) dot d) / d.normSquare


and this is exactly the formula that is used in the code.



(Thanks at SergGr for adding an initial sketch of the derivation)






share|improve this answer


























  • Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

    – SergGr
    Jan 2 at 18:18













  • @SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

    – Andrey Tyukin
    Jan 2 at 18:36


















3














Given the following definitions:



type Point = (Double, Double)

implicit class PointOps(p: Point) {
def +(other: Point) = (p._1 + other._1, p._2 + other._2)
def -(other: Point) = (p._1 - other._1, p._2 - other._2)
def dot(other: Point) = p._1 * other._1 + p._2 * other._2
def *(scalar: Double) = (p._1 * scalar, p._2 * scalar)
def normSquare: Double = p._1 * p._1 + p._2 * p._2
}


meaning that



a + b        // is vector addition
a - b // is vector subtraction
a dot b // is the dot product (scalar product)
a * f // is multiplication of a vector `a` with a scalar factor `f`
a.normSquare // is the squared length of a vector


you obtain the projection of a point p on line going through points line1 and line2 as follows:



/** Projects point `p` on line going through two points `line1` and `line2`. */
def projectPointOnLine(line1: Point, line2: Point, p: Point): Point = {
val v = p - line1
val d = line2 - line1
line1 + d * ((v dot d) / d.normSquare)
}


Example:



println(projectPointOnLine((-1.0, 10.0), (7.0, 4.0), (6.0, 11.0)))


gives



(3.0, 7.0)


This works in 3D (or n-D) in exactly the same way.





Some math behind that (How to derive it from scratch)



(notation as above)



We have three points: l1 and l2 for the line and p for the target point.
We want to project the point p orthogonally onto the line that goes through l1 and l2 (assuming l1 != l2).



Let d = l2 - l1 be the direction from l1 to l2. Then every point on the line can be represented as



l1 + d * t


with some scalar factor t. Now we want to find a t such that the vector connecting p and l1 + d * t is orthogonal to d, that is:



(p - (l1 + d * t)) dot d == 0


Recall that



(v1 + v2) dot v3 = (v1 dot v3) + (v2 dot v3)


for all vectors v1, v2, v3, and that



(v1 * s) dot v2 = (v1 dot v2) * s


for scalar factors s. Using this and the definition v.normSquared = v dot v, we obtain:



(p - l1 - d * t) dot d 
= (p - l1) dot d - (d dot d) * t
= (p - l1) dot d - d.normSquare * t


and this should become 0. Resolving for t gives:



t = ((p - l1) dot d) / d.normSquare


and this is exactly the formula that is used in the code.



(Thanks at SergGr for adding an initial sketch of the derivation)






share|improve this answer


























  • Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

    – SergGr
    Jan 2 at 18:18













  • @SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

    – Andrey Tyukin
    Jan 2 at 18:36
















3












3








3







Given the following definitions:



type Point = (Double, Double)

implicit class PointOps(p: Point) {
def +(other: Point) = (p._1 + other._1, p._2 + other._2)
def -(other: Point) = (p._1 - other._1, p._2 - other._2)
def dot(other: Point) = p._1 * other._1 + p._2 * other._2
def *(scalar: Double) = (p._1 * scalar, p._2 * scalar)
def normSquare: Double = p._1 * p._1 + p._2 * p._2
}


meaning that



a + b        // is vector addition
a - b // is vector subtraction
a dot b // is the dot product (scalar product)
a * f // is multiplication of a vector `a` with a scalar factor `f`
a.normSquare // is the squared length of a vector


you obtain the projection of a point p on line going through points line1 and line2 as follows:



/** Projects point `p` on line going through two points `line1` and `line2`. */
def projectPointOnLine(line1: Point, line2: Point, p: Point): Point = {
val v = p - line1
val d = line2 - line1
line1 + d * ((v dot d) / d.normSquare)
}


Example:



println(projectPointOnLine((-1.0, 10.0), (7.0, 4.0), (6.0, 11.0)))


gives



(3.0, 7.0)


This works in 3D (or n-D) in exactly the same way.





Some math behind that (How to derive it from scratch)



(notation as above)



We have three points: l1 and l2 for the line and p for the target point.
We want to project the point p orthogonally onto the line that goes through l1 and l2 (assuming l1 != l2).



Let d = l2 - l1 be the direction from l1 to l2. Then every point on the line can be represented as



l1 + d * t


with some scalar factor t. Now we want to find a t such that the vector connecting p and l1 + d * t is orthogonal to d, that is:



(p - (l1 + d * t)) dot d == 0


Recall that



(v1 + v2) dot v3 = (v1 dot v3) + (v2 dot v3)


for all vectors v1, v2, v3, and that



(v1 * s) dot v2 = (v1 dot v2) * s


for scalar factors s. Using this and the definition v.normSquared = v dot v, we obtain:



(p - l1 - d * t) dot d 
= (p - l1) dot d - (d dot d) * t
= (p - l1) dot d - d.normSquare * t


and this should become 0. Resolving for t gives:



t = ((p - l1) dot d) / d.normSquare


and this is exactly the formula that is used in the code.



(Thanks at SergGr for adding an initial sketch of the derivation)






share|improve this answer















Given the following definitions:



type Point = (Double, Double)

implicit class PointOps(p: Point) {
def +(other: Point) = (p._1 + other._1, p._2 + other._2)
def -(other: Point) = (p._1 - other._1, p._2 - other._2)
def dot(other: Point) = p._1 * other._1 + p._2 * other._2
def *(scalar: Double) = (p._1 * scalar, p._2 * scalar)
def normSquare: Double = p._1 * p._1 + p._2 * p._2
}


meaning that



a + b        // is vector addition
a - b // is vector subtraction
a dot b // is the dot product (scalar product)
a * f // is multiplication of a vector `a` with a scalar factor `f`
a.normSquare // is the squared length of a vector


you obtain the projection of a point p on line going through points line1 and line2 as follows:



/** Projects point `p` on line going through two points `line1` and `line2`. */
def projectPointOnLine(line1: Point, line2: Point, p: Point): Point = {
val v = p - line1
val d = line2 - line1
line1 + d * ((v dot d) / d.normSquare)
}


Example:



println(projectPointOnLine((-1.0, 10.0), (7.0, 4.0), (6.0, 11.0)))


gives



(3.0, 7.0)


This works in 3D (or n-D) in exactly the same way.





Some math behind that (How to derive it from scratch)



(notation as above)



We have three points: l1 and l2 for the line and p for the target point.
We want to project the point p orthogonally onto the line that goes through l1 and l2 (assuming l1 != l2).



Let d = l2 - l1 be the direction from l1 to l2. Then every point on the line can be represented as



l1 + d * t


with some scalar factor t. Now we want to find a t such that the vector connecting p and l1 + d * t is orthogonal to d, that is:



(p - (l1 + d * t)) dot d == 0


Recall that



(v1 + v2) dot v3 = (v1 dot v3) + (v2 dot v3)


for all vectors v1, v2, v3, and that



(v1 * s) dot v2 = (v1 dot v2) * s


for scalar factors s. Using this and the definition v.normSquared = v dot v, we obtain:



(p - l1 - d * t) dot d 
= (p - l1) dot d - (d dot d) * t
= (p - l1) dot d - d.normSquare * t


and this should become 0. Resolving for t gives:



t = ((p - l1) dot d) / d.normSquare


and this is exactly the formula that is used in the code.



(Thanks at SergGr for adding an initial sketch of the derivation)







share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 2 at 19:03

























answered Jan 2 at 17:42









Andrey TyukinAndrey Tyukin

30.1k42351




30.1k42351













  • Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

    – SergGr
    Jan 2 at 18:18













  • @SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

    – Andrey Tyukin
    Jan 2 at 18:36





















  • Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

    – SergGr
    Jan 2 at 18:18













  • @SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

    – Andrey Tyukin
    Jan 2 at 18:36



















Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

– SergGr
Jan 2 at 18:18







Andrey I took the liberty of adding some description of the logic in words. Please take a look if you are OK with that.

– SergGr
Jan 2 at 18:18















@SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

– Andrey Tyukin
Jan 2 at 18:36







@SergGr Thanks for trying. Adding mathematical / geometric explanations seems a bit difficult here, because there is no TeX support, and because I don't know where to get a halfway sane sketch to illustrate what's going on. I'll leave it as-is for now, and maybe add a sketch later.

– Andrey Tyukin
Jan 2 at 18:36






















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54009832%2fscala-orthogonal-projection-of-a-point-onto-a-line%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

MongoDB - Not Authorized To Execute Command

Npm cannot find a required file even through it is in the searched directory

in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith