Reputation: 109
I have a line segment and a circle in my processing sketch. I want the center of the circle, point q, to find the closest point, p, on the line segment and the circle will move towards it. I'm not quite sure how to code this (in processing), so any suggestions would be great! Thanks! This is my code so far:
int xPos1 = 200;
int yPos1 = 200;
int xp1 = 50;
int yp1 = 50;
int xp2 = 350;
int yp2 = 50;
void setup() {
size(400, 400);
strokeWeight(2);
line(xp1, yp1, xp2, yp2);
strokeWeight(1);
}
void draw() {
drawCircle();
}
void drawCircle() {
fill(50, 120, 120);
//circle
ellipse(xPos1, yPos1, 75, 75);
//circle center
ellipse(xPos1, yPos1, 7, 7);
fill(255);
text("Q", xPos1 + 15, yPos1 + 5);
fill(50, 120, 120);
}
Upvotes: 3
Views: 1735
Reputation: 1349
If you need implementation in Javafx
AB is the line.
P is a point close to line.
M is the point on the line that is closest to P.
public static Point findPointMOnLineClosestToGivenPointP(Point vector_A, Point vector_B, Point vector_P) {
Point vector_AB = vector_B.subtract(vector_A);
Point vector_AP = vector_P.subtract(vector_A);
double distance_AM = vector_AB.dotProduct(vector_AP) / vector_AB.magnitude();
Point vector_AM = vector_AB.normalize().multiply(distance_AM);
Point vector_M = vector_A.add(vector_AM);
return vector_M;
}
the Point
class is just the same as javafx.geometry.Point2D
class, it just I renamed it...
(Following are not important)
(This produce same result as the other answer's getNearestPointOnLine()
, similar Math formula.)
package com.redfrog.trafficsm;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Random;
import org.junit.jupiter.api.Test;
import com.redfrog.trafficsm.model.Point;
import com.redfrog.trafficsm.util.MathUtil;
import processing.core.PVector;
class SimpleTest {
Random random = new Random(111);
@Test
public void test_getNearestPointOnLine() {
float bound = 100;
for (int i = 0; i < 100; i++) {
float f_x;
float f_y;
f_x = random.nextFloat(-bound, bound);
f_y = random.nextFloat(-bound, bound);
Point point_A = new Point(f_x, f_y);
PVector pvector_A = new PVector(f_x, f_y);
f_x = random.nextFloat(-bound, bound);
f_y = random.nextFloat(-bound, bound);
Point point_B = new Point(f_x, f_y);
PVector pvector_B = new PVector(f_x, f_y);
PVector pvector_N = pvector_B.sub(pvector_A).normalize();
f_x = random.nextFloat(-bound, bound);
f_y = random.nextFloat(-bound, bound);
Point point_P = new Point(f_x, f_y);
PVector pvector_P = new PVector(f_x, f_y);
Point point_M = MathUtil.findPointMOnLineClosestToGivenPointP(point_A, point_B, point_P);
PVector pvector_M = MathUtil.getNearestPointOnLine(pvector_P, pvector_A, pvector_N);
System.out.println();
System.out.println(point_M);
System.out.println(pvector_M);
int scale = 2;
System.out.println(MathUtil.round(point_M.getX(), scale));
System.out.println(MathUtil.round(pvector_M.x, scale));
System.out.println(MathUtil.round(point_M.getY(), scale));
System.out.println(MathUtil.round(pvector_M.y, scale));
assertEquals(MathUtil.round(point_M.getX(), scale), MathUtil.round(pvector_M.x, scale));
assertEquals(MathUtil.round(point_M.getY(), scale), MathUtil.round(pvector_M.y, scale));
}
}
}
package com.redfrog.trafficsm.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
import com.redfrog.trafficsm.model.Point;
import processing.core.PVector;
public class MathUtil {
public static double round(double value, int scale) {
return round(value, scale, RoundingMode.HALF_DOWN); //
}
public static double round(double value, int scale, RoundingMode roundingMode) {
return new BigDecimal(value).setScale(scale, roundingMode).doubleValue(); //
}
// ############
public static Point findPointMOnLineClosestToGivenPointP(Point vector_A, Point vector_B, Point vector_P) {
Point vector_AB = vector_B.subtract(vector_A);
Point vector_AP = vector_P.subtract(vector_A);
double distance_AM = vector_AB.dotProduct(vector_AP) / vector_AB.magnitude();
Point vector_AM = vector_AB.normalize().multiply(distance_AM);
Point vector_M = vector_A.add(vector_AM);
return vector_M;
}
// https://stackoverflow.com/questions/36238017/orthogonal-projection-of-a-point-onto-a-line-in-processing
public static PVector getNearestPointOnLine(PVector p, PVector a, PVector n) {
// the notation turns the computation inside out,
// but this is equivalent to the above equation
PVector q = PVector.mult(n, -PVector.sub(a, p).dot(n));
q.add(a);
return q;
}
}
package com.redfrog.note;
import java.util.Random;
import com.google.common.util.concurrent.AtomicDouble;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import processing.core.PVector;
public class TestAppJavafxBoot extends Application {
@Override
public void start(Stage primaryStage) {
AnchorPane pane_JfxRoot = new AnchorPane();
AnchorPane panel_SemanticRoot = new AnchorPane();
pane_JfxRoot.getChildren().add(panel_SemanticRoot);
final double opacity = 0.9;
panel_SemanticRoot.setLayoutX(350);
panel_SemanticRoot.setLayoutY(350);
Random random = new Random(111);
float bound = 100;
float f_x;
float f_y;
f_x = random.nextFloat(-bound, bound);
f_y = random.nextFloat(-bound, bound);
Point2D point_A = new Point2D(f_x, f_y);
PVector pvector_A = new PVector(f_x, f_y);
final Circle circle = new Circle();
panel_SemanticRoot.getChildren().add(circle);
circle.setRadius(3);
circle.setFill(Color.rgb(255, 0, 0, opacity));
circle.setCenterX(f_x);
circle.setCenterY(f_y);
f_x = random.nextFloat(-bound, bound);
f_y = random.nextFloat(-bound, bound);
Point2D point_B = new Point2D(f_x, f_y);
PVector pvector_B = new PVector(f_x, f_y);
PVector pvector_N = pvector_B.sub(pvector_A).normalize();
final Circle circle_02 = new Circle();
panel_SemanticRoot.getChildren().add(circle_02);
circle_02.setRadius(3);
circle_02.setFill(Color.rgb(0, 255, 0, opacity));
circle_02.setCenterX(f_x);
circle_02.setCenterY(f_y);
f_x = random.nextFloat(-bound, bound);
f_y = random.nextFloat(-bound, bound);
Point2D point_P = new Point2D(f_x, f_y);
PVector pvector_P = new PVector(f_x, f_y);
final Circle circle_03 = new Circle();
panel_SemanticRoot.getChildren().add(circle_03);
circle_03.setRadius(3);
circle_03.setFill(Color.rgb(0, 0, 255, opacity));
circle_03.setCenterX(f_x);
circle_03.setCenterY(f_y);
Point2D point_M = MathUtil.findPointMOnLineClosestToGivenPointP(point_A, point_B, point_P);
PVector pvector_M = MathUtil.getNearestPointOnLine(pvector_P, pvector_A, pvector_N);
System.out.println();
System.out.println(point_M);
final Circle circle_04 = new Circle();
panel_SemanticRoot.getChildren().add(circle_04);
circle_04.setRadius(3);
circle_04.setFill(Color.rgb(0, 255, 255, opacity));
circle_04.setCenterX(point_M.getX());
circle_04.setCenterY(point_M.getY());
final Circle circle_05 = new Circle();
panel_SemanticRoot.getChildren().add(circle_05);
circle_05.setRadius(3);
circle_05.setFill(Color.rgb(255, 255, 0, opacity));
circle_05.setCenterX(pvector_M.x);
circle_05.setCenterY(pvector_M.y);
Scene scene = new Scene(pane_JfxRoot, 950, 750);
primaryStage.setTitle("DDD");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Upvotes: 0
Reputation: 886
The projection of a point onto a line is as follows:
Start with a line of the form x = a + t * n and a point p.
The vector compontent representing the nearest point on the line from the point p is:
(a - p) - ((a - p) dot n)n
so we have: p + (a - p) - ((a - p) dot n)n
after some simplification we have: a - ((a - p) dot n)n
Note that ((a - p) dot n) n is the vector component that represents the position along the line from the nearest point to the beginning (i.e. from nearest point to p back to a)
Let's use PVector
s to make life a bit easier.
PVector p = new PVector(200, 200);
PVector a = new PVector(50, 50);
PVector b = new PVector(350, 50);
PVector n = new PVector(350, 50); // |p2 - p1|
void setup() {
size(400, 400);
strokeWeight(2);
strokeWeight(1);
// initialize our normalized (unit length) line direction
n.sub(a);
n.normalize();
}
void draw() {
drawCircle();
}
PVector getNearestPointOnLine(PVector p, PVector a, PVector n){
// the notation turns the computation inside out,
// but this is equivalent to the above equation
PVector q = PVector.mult(n, -PVector.sub(a, p).dot(n));
q.add(a);
return q;
}
void drawCircle() {
// lets draw everything here where we can see it
background(255, 255, 255);
line(a.x, a.y, b.x, b.y);
fill(50, 120, 120);
//circle
// NOTE: this may require hooking up a mouse move event handler
p.x = mouseX;
p.y = mouseY;
PVector q = getNearestPointOnLine(p, a, n);
ellipse(q.x, q.y, 75, 75);
//circle center
ellipse(q.x, q.y, 7, 7);
fill(0); // make text visible on white background
text("Q", q.x + 15, q.y + 5);
//fill(50, 120, 120);
}
Reference: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation
Upvotes: 4