I’ve refactored @villares’ Java Mode version and now it should be at least as fast (or faster ) as his Python Mode original version.
“Closest_Point_on_SVG_Path.pde”:
/**
* Closest Point on SVG Path (v1.0.6)
* by SixFeet & Villares
* mod GoToLoop (2020/Nov/25)
* https://Discourse.Processing.org/t/closest-point-on-svg-path/25592/17
*/
import geomerative.RG;
import geomerative.RShape;
import geomerative.RPoint;
static final String FILENAME = "lines.svg", INFO = "chosenPathIndex = ";
static final color BG = #64C800, FILL = #C80000, STROKE = 0;
static final int BOLD = 3, DIAM = 10, INFO_OFFSET = 30, MOUSE_OFFSET = 10;
final RPoint mouse = new RPoint();
RPoint[][] paths;
boolean dragging = true, displayPoints = true;
int farOff, chosenPath = -1, nearestPoint = -1;
void settings() {
RG.init(this);
final RShape svg = RG.loadShape(FILENAME);
paths = svg.getPointsInPaths();
println("\npaths:", paths.length);
println("x, y, w, h:", svg.getX(), svg.getY(), svg.width, svg.height);
final RPoint center = svg.getCenter();
size((int) center.x << 1, (int) center.y << 1);
center.print();
println("width, height:", width, height);
}
void setup() {
fill(FILL);
stroke(STROKE);
strokeWeight(BOLD);
}
void draw() {
mouse.x = mouseX;
mouse.y = mouseY;
background(BG);
if (displayPoints) displayPoints();
if (nearestPoint >= 0) drawMouseLine();
if (chosenPath >= 0) text(INFO + chosenPath, INFO_OFFSET, INFO_OFFSET);
}
void keyPressed() {
dragging = false;
chosenPath = (chosenPath + 1) % paths.length;
findNearestPointIndex();
}
void mousePressed() {
dragging = true;
displayPoints ^= mouseButton == RIGHT;
findChosenPathIndex();
}
void mouseMoved() {
if (!dragging) mouseDragged();
}
void mouseDragged() {
if (chosenPath >= 0) findNearestPointIndex();
else findChosenPathIndex();
}
void mouseReleased() {
chosenPath = nearestPoint = -1;
}
void displayPoints() {
for (final RPoint[] pts : paths) for (final RPoint p : pts) point(p.x, p.y);
}
void findChosenPathIndex() {
final int len = paths.length;
int nearest = MAX_INT, lastChosenPath = -1;
for (int i = 0; (chosenPath = i) < len; ++i) {
findNearestPointIndex();
if (farOff < nearest) {
nearest = farOff;
lastChosenPath = i;
}
}
if ((chosenPath = lastChosenPath) >= 0) findNearestPointIndex();
}
void findNearestPointIndex() {
final RPoint[] pts = paths[chosenPath];
final int len = pts.length;
farOff = MAX_INT;
nearestPoint = -1;
for (int i = 0; i < len; ++i) {
final int d = round(pts[i].dist(mouse));
if (d < farOff) {
farOff = d;
nearestPoint = i;
}
}
}
void drawMouseLine() {
final RPoint p = paths[chosenPath][nearestPoint];
strokeWeight(1);
line(p.x, p.y, mouse.x, mouse.y);
circle(p.x, p.y, DIAM);
text(farOff, mouse.x + MOUSE_OFFSET, mouse.y - MOUSE_OFFSET);
strokeWeight(BOLD);
}
“lines.svg”:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:10;stroke-miterlimit:10;}
</style>
<line class="st0" x1="142.5" y1="111.7" x2="358" y2="111.7"/>
<line class="st0" x1="142.5" y1="167.9" x2="358" y2="167.9"/>
<line class="st0" x1="142.5" y1="224.1" x2="358" y2="224.1"/>
<line class="st0" x1="142.5" y1="280.3" x2="358" y2="280.3"/>
<path class="st0" d="M352.9,398.8H147.6c-6.6,0-12-5.4-12-12v-38.3c0-6.6,5.4-12,12-12h205.3c6.6,0,12,5.4,12,12v38.3
C364.9,393.4,359.5,398.8,352.9,398.8z"/>
</svg>