Commit 419243a7 authored by Matthias Betz's avatar Matthias Betz
Browse files

Render highlights through GL overlay instead of JavaFX shapes



Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 2877e8f0
package de.hft.stuttgart.citydoctor2.gui; package de.hft.stuttgart.citydoctor2.gui;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import de.hft.stuttgart.citydoctor2.datastructure.Edge; import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.gui.gl.FloatList;
import de.hft.stuttgart.citydoctor2.gui.gl.GLViewport;
import de.hft.stuttgart.citydoctor2.math.Triangle3d; import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
import javafx.application.Platform;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
/**
* Accumulates highlight marker geometry (points and lines) and pushes it to the {@link GLViewport}
* overlay. Coordinates are recentred by the current scene center supplied by the {@link Renderer}.
*/
public class HighlightController { public class HighlightController {
private final GLViewport viewport;
private Supplier<double[]> centerSupplier;
private double scale; private final FloatList points = new FloatList();
private Group points; private final FloatList pointColors = new FloatList();
private Group edges; private final FloatList lines = new FloatList();
private final FloatList lineColors = new FloatList();
private Group highlights; public HighlightController(GLViewport viewport) {
this.viewport = viewport;
}
public void setCenterSupplier(Supplier<double[]> centerSupplier) {
this.centerSupplier = centerSupplier;
}
public HighlightController(Group world) { private double[] center() {
highlights = new Group(); if (centerSupplier == null) {
points = new Group(); return new double[3];
edges = new Group(); }
highlights.getChildren().add(edges); double[] c = centerSupplier.get();
highlights.getChildren().add(points); return c == null ? new double[3] : c;
Platform.runLater(() -> world.getChildren().add(highlights));
} }
public void clearHighlights() { public void clearHighlights() {
points.getChildren().clear(); points.clear();
edges.getChildren().clear(); pointColors.clear();
lines.clear();
lineColors.clear();
push();
}
private void push() {
viewport.setHighlights(points.toArray(), pointColors.toArray(), lines.toArray(), lineColors.toArray());
} }
public void changeScaling(double translateZ) { public void changeScaling(double translateZ) {
scale = Math.abs(translateZ); double scale = Math.abs(translateZ);
scale = Math.min(150, scale); scale = Math.min(150, scale);
scale = Math.max(10, scale); scale = Math.max(10, scale);
scale = scale * 0.01; scale = scale * 0.01;
for (Node n : points.getChildren()) { viewport.setPointScale((float) (scale * 8));
n.setScaleX(scale);
n.setScaleY(scale);
n.setScaleZ(scale);
}
double edgeSize = scale / 10;
for (Node n : edges.getChildren()) {
if (n instanceof Cylinder cy) {
cy.setRadius(edgeSize);
} }
// ---- point/line accumulation ----
private void addPoint(Vertex v, Color color) {
double[] c = center();
points.add((float) (v.getX() - c[0]), (float) (v.getY() - c[1]), (float) (v.getZ() - c[2]));
pointColors.add((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue());
} }
private void addLine(Vector3d v1, Vector3d v2, Color color) {
double[] c = center();
lines.add((float) (v1.getX() - c[0]), (float) (v1.getY() - c[1]), (float) (v1.getZ() - c[2]));
lines.add((float) (v2.getX() - c[0]), (float) (v2.getY() - c[1]), (float) (v2.getZ() - c[2]));
float r = (float) color.getRed();
float g = (float) color.getGreen();
float b = (float) color.getBlue();
lineColors.add(r, g, b);
lineColors.add(r, g, b);
} }
public void highlight(Polygon p, TriangulatedGeometry currentTriGeom) { // ---- public highlight API (no TriangulatedGeometry) ----
public void highlight(Polygon p) {
clearHighlights(); clearHighlights();
addHighlight(p, currentTriGeom, Color.RED, Color.BLUE); addHighlight(p, Color.RED, Color.BLUE);
push();
} }
private void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom, Color extColor, Color intColor) { private void addHighlight(Polygon p, Color extColor, Color intColor) {
if (currentTriGeom == null || p == null) { if (p == null) {
return; return;
} }
addHighlight(p.getExteriorRing(), currentTriGeom, extColor); addHighlight(p.getExteriorRing(), extColor);
for (LinearRing intRing : p.getInnerRings()) { for (LinearRing intRing : p.getInnerRings()) {
addHighlight(intRing, currentTriGeom, intColor); addHighlight(intRing, intColor);
} }
} }
public void highlight(LinearRing ring, TriangulatedGeometry currentTriGeom) { public void highlight(LinearRing ring) {
clearHighlights(); clearHighlights();
if (ring.getType() == LinearRingType.EXTERIOR) { if (ring.getType() == LinearRingType.EXTERIOR) {
addHighlight(ring, currentTriGeom, Color.RED); addHighlight(ring, Color.RED);
} else { } else {
addHighlight(ring, currentTriGeom, Color.BLUE); addHighlight(ring, Color.BLUE);
} }
push();
} }
public void highlight(List<LinearRing> rings, TriangulatedGeometry currentTriGeom) { public void highlight(List<LinearRing> rings) {
clearHighlights(); clearHighlights();
for (LinearRing lr : rings) { for (LinearRing lr : rings) {
if (lr.getType() == LinearRingType.EXTERIOR) { if (lr.getType() == LinearRingType.EXTERIOR) {
addHighlight(lr, currentTriGeom, Color.RED); addHighlight(lr, Color.RED);
} else { } else {
addHighlight(lr, currentTriGeom, Color.BLUE); addHighlight(lr, Color.BLUE);
} }
} }
push();
} }
private void addHighlight(LinearRing ring, TriangulatedGeometry currentTriGeom, Color pointColor) { private void addHighlight(LinearRing ring, Color pointColor) {
if (currentTriGeom == null || ring == null) { if (ring == null) {
return; return;
} }
Vector3d movedBy = currentTriGeom.getMovedBy();
for (Vertex v : ring.getVertices()) { for (Vertex v : ring.getVertices()) {
highlightPoint(movedBy, v, pointColor); addPoint(v, pointColor);
} }
for (int i = 0; i < ring.getVertices().size() - 1; i++) { for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vertex v1 = ring.getVertices().get(i); Vertex v1 = ring.getVertices().get(i);
Vertex v2 = ring.getVertices().get(i + 1); Vertex v2 = ring.getVertices().get(i + 1);
highlightEdge(v1, v2, movedBy); addLine(v1, v2, Color.ORANGE);
} }
} }
public void highlight(Edge e, TriangulatedGeometry currentTriGeom) { public void highlight(Edge e) {
clearHighlights(); clearHighlights();
addHighlight(e, currentTriGeom); addHighlight(e);
push();
} }
private void addHighlight(Edge e, TriangulatedGeometry currentTriGeom) { private void addHighlight(Edge e) {
if (currentTriGeom == null || e == null) { if (e == null) {
return; return;
} }
Vector3d movedBy = currentTriGeom.getMovedBy(); addLine(e.getFrom(), e.getTo(), Color.ORANGE);
highlightEdge(e.getFrom(), e.getTo(), movedBy); addPoint(e.getFrom(), Color.RED);
highlightPoint(movedBy, e.getFrom(), Color.RED); addPoint(e.getTo(), Color.RED);
highlightPoint(movedBy, e.getTo(), Color.RED);
} }
public void highlight(Vertex v, TriangulatedGeometry currentTriGeom) { public void highlight(Vertex v) {
clearHighlights(); clearHighlights();
addHighlight(v, currentTriGeom, Color.RED); addPoint(v, Color.RED);
push();
} }
public void addHighlight(Vertex v, TriangulatedGeometry currentTriGeom, Color c) { public void addHighlight(Vertex v, Color c) {
if (currentTriGeom == null || v == null) { if (v == null) {
return; return;
} }
Vector3d movedBy = currentTriGeom.getMovedBy(); addPoint(v, c);
highlightPoint(movedBy, v, c); push();
}
private void highlightPoint(Vector3d movedBy, Vertex v, Color color) {
Sphere sp = new Sphere(0.5);
sp.setMaterial(new PhongMaterial(color));
sp.setTranslateX(v.getX() - movedBy.getX());
sp.setTranslateY(v.getY() - movedBy.getY());
sp.setTranslateZ(v.getZ() - movedBy.getZ());
sp.setScaleX(scale);
sp.setScaleY(scale);
sp.setScaleZ(scale);
sp.setUserData(new VertexClickDispatcher(v));
points.getChildren().add(sp);
}
private void highlightEdge(Vector3d v1, Vector3d v2, Vector3d movedBy) {
Point3D origin = new Point3D(v1.getX() - movedBy.getX(), v1.getY() - movedBy.getY(),
v1.getZ() - movedBy.getZ());
Point3D target = new Point3D(v2.getX() - movedBy.getX(), v2.getY() - movedBy.getY(),
v2.getZ() - movedBy.getZ());
Point3D yAxis = new Point3D(0, 1, 0);
Point3D diff = target.subtract(origin);
double height = diff.magnitude();
Point3D mid = target.midpoint(origin);
Translate moveToMidpoint = new Translate(mid.getX(), mid.getY(), mid.getZ());
Point3D axisOfRotation = diff.crossProduct(yAxis);
double angle = Math.acos(diff.normalize().dotProduct(yAxis));
Rotate rotateAroundCenter = new Rotate(-Math.toDegrees(angle), axisOfRotation);
Cylinder cy = new Cylinder(scale / 10, height);
cy.setMaterial(new PhongMaterial(Color.ORANGE));
cy.getTransforms().addAll(moveToMidpoint, rotateAroundCenter);
edges.getChildren().add(cy);
} }
public void highlightEdges(List<Edge> errorEdges, TriangulatedGeometry currentTriGeom) { public void highlightEdges(List<Edge> errorEdges) {
clearHighlights(); clearHighlights();
for (Edge e : errorEdges) { for (Edge e : errorEdges) {
addHighlight(e, currentTriGeom); addHighlight(e);
} }
push();
} }
public void highlightPolygons(List<List<Polygon>> components, TriangulatedGeometry currentTriGeom) { public void highlightPolygons(List<List<Polygon>> components) {
clearHighlights(); clearHighlights();
for (int i = 0; i < components.size(); i++) { for (int i = 0; i < components.size(); i++) {
Color extColor; Color extColor;
...@@ -205,18 +195,21 @@ public class HighlightController { ...@@ -205,18 +195,21 @@ public class HighlightController {
// select some color pairs for exterior and inner rings // select some color pairs for exterior and inner rings
List<Polygon> component = components.get(i); List<Polygon> component = components.get(i);
for (Polygon p : component) { for (Polygon p : component) {
addHighlight(p, currentTriGeom, extColor, intColor); addHighlight(p, extColor, intColor);
} }
} }
push();
} }
public void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom) { public void addHighlight(Polygon p) {
addHighlight(p, currentTriGeom, Color.RED, Color.BLUE); addHighlight(p, Color.RED, Color.BLUE);
push();
} }
public void highlight(Triangle3d t, TriangulatedGeometry currentTriGeom) { public void highlight(Triangle3d t) {
highlightEdge(t.getP1(), t.getP2(), currentTriGeom.getMovedBy()); addLine(t.getP1(), t.getP2(), Color.ORANGE);
highlightEdge(t.getP2(), t.getP3(), currentTriGeom.getMovedBy()); addLine(t.getP2(), t.getP3(), Color.ORANGE);
highlightEdge(t.getP3(), t.getP1(), currentTriGeom.getMovedBy()); addLine(t.getP3(), t.getP1(), Color.ORANGE);
push();
} }
} }
...@@ -55,32 +55,27 @@ import javafx.scene.paint.Color; ...@@ -55,32 +55,27 @@ import javafx.scene.paint.Color;
public class ListErrorVisitor implements ErrorVisitor { public class ListErrorVisitor implements ErrorVisitor {
private HighlightController controller; private HighlightController controller;
private TriangulatedGeometry geom;
public ListErrorVisitor(HighlightController controller) { public ListErrorVisitor(HighlightController controller) {
this.controller = controller; this.controller = controller;
} }
public void setGeometry(TriangulatedGeometry geom) {
this.geom = geom;
}
@Override @Override
public void visit(PolygonHoleOutsideError err) { public void visit(PolygonHoleOutsideError err) {
List<LinearRing> highlightedRings = new ArrayList<>(); List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing()); highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.addAll(err.getHolesOutside()); highlightedRings.addAll(err.getHolesOutside());
controller.highlight(highlightedRings, geom); controller.highlight(highlightedRings);
} }
@Override @Override
public void visit(NonManifoldEdgeError err) { public void visit(NonManifoldEdgeError err) {
controller.highlightEdges(err.getEdges(), geom); controller.highlightEdges(err.getEdges());
} }
@Override @Override
public void visit(MultipleConnectedComponentsError err) { public void visit(MultipleConnectedComponentsError err) {
controller.highlightPolygons(err.getComponents(), geom); controller.highlightPolygons(err.getComponents());
} }
@Override @Override
...@@ -88,18 +83,18 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -88,18 +83,18 @@ public class ListErrorVisitor implements ErrorVisitor {
List<LinearRing> highlightedRings = new ArrayList<>(); List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing()); highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.add(err.getInnerRing()); highlightedRings.add(err.getInnerRing());
controller.highlight(highlightedRings, geom); controller.highlight(highlightedRings);
} }
@Override @Override
public void visit(NonManifoldVertexError err) { public void visit(NonManifoldVertexError err) {
controller.highlightPolygons(err.getComponents(), geom); controller.highlightPolygons(err.getComponents());
controller.addHighlight(err.getVertex(), geom, Color.BLUEVIOLET); controller.addHighlight(err.getVertex(), Color.BLUEVIOLET);
} }
@Override @Override
public void visit(PolygonWrongOrientationError err) { public void visit(PolygonWrongOrientationError err) {
controller.highlightEdges(err.getEdges(), geom); controller.highlightEdges(err.getEdges());
} }
@Override @Override
...@@ -107,12 +102,12 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -107,12 +102,12 @@ public class ListErrorVisitor implements ErrorVisitor {
List<LinearRing> highlightedRings = new ArrayList<>(); List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing()); highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.add(err.getInnerRing()); highlightedRings.add(err.getInnerRing());
controller.highlight(highlightedRings, geom); controller.highlight(highlightedRings);
} }
@Override @Override
public void visit(SolidNotClosedError err) { public void visit(SolidNotClosedError err) {
controller.highlightEdges(err.getErrorEdges(), geom); controller.highlightEdges(err.getErrorEdges());
} }
@Override @Override
...@@ -127,14 +122,14 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -127,14 +122,14 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(RingNotClosedError err) { public void visit(RingNotClosedError err) {
controller.highlight(err.getRing(), geom); controller.highlight(err.getRing());
} }
@Override @Override
public void visit(ConsecutivePointSameError err) { public void visit(ConsecutivePointSameError err) {
controller.highlight(err.getRing(), geom); controller.highlight(err.getRing());
controller.addHighlight(err.getVertex1(), geom, Color.BLACK); controller.addHighlight(err.getVertex1(), Color.BLACK);
controller.addHighlight(err.getVertex2(), geom, Color.BLACK); controller.addHighlight(err.getVertex2(), Color.BLACK);
} }
@Override @Override
...@@ -144,7 +139,7 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -144,7 +139,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(PolygonInteriorDisconnectedError err) { public void visit(PolygonInteriorDisconnectedError err) {
controller.highlight(err.getConnectedRings(), geom); controller.highlight(err.getConnectedRings());
} }
@Override @Override
...@@ -154,18 +149,18 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -154,18 +149,18 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(RingTooFewPointsError err) { public void visit(RingTooFewPointsError err) {
controller.highlight(err.getRing(), geom); controller.highlight(err.getRing());
} }
@Override @Override
public void visit(NonPlanarPolygonNormalsDeviation err) { public void visit(NonPlanarPolygonNormalsDeviation err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
} }
@Override @Override
public void visit(NonPlanarPolygonDistancePlaneError err) { public void visit(NonPlanarPolygonDistancePlaneError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
controller.addHighlight(err.getVertex(), geom, Color.BLACK); controller.addHighlight(err.getVertex(), Color.BLACK);
} }
@Override @Override
...@@ -173,15 +168,15 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -173,15 +168,15 @@ public class ListErrorVisitor implements ErrorVisitor {
List<LinearRing> rings = new ArrayList<>(); List<LinearRing> rings = new ArrayList<>();
rings.add(err.getIntersectingRings().getValue0()); rings.add(err.getIntersectingRings().getValue0());
rings.add(err.getIntersectingRings().getValue1()); rings.add(err.getIntersectingRings().getValue1());
controller.highlight(rings, geom); controller.highlight(rings);
} }
@Override @Override
public void visit(SolidSelfIntError err) { public void visit(SolidSelfIntError err) {
controller.clearHighlights(); controller.clearHighlights();
for (PolygonIntersection intersection : err.getIntersections()) { for (PolygonIntersection intersection : err.getIntersections()) {
controller.addHighlight(intersection.getP1(), geom); controller.addHighlight(intersection.getP1());
controller.addHighlight(intersection.getP2(), geom); controller.addHighlight(intersection.getP2());
} }
} }
...@@ -192,9 +187,9 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -192,9 +187,9 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(RingDuplicatePointError err) { public void visit(RingDuplicatePointError err) {
controller.highlight(err.getRing(), geom); controller.highlight(err.getRing());
controller.addHighlight(err.getVertex1(), geom, Color.BLACK); controller.addHighlight(err.getVertex1(), Color.BLACK);
controller.addHighlight(err.getVertex2(), geom, Color.BLACK); controller.addHighlight(err.getVertex2(), Color.BLACK);
} }
@Override @Override
...@@ -202,14 +197,14 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -202,14 +197,14 @@ public class ListErrorVisitor implements ErrorVisitor {
List<Edge> list = new ArrayList<>(); List<Edge> list = new ArrayList<>();
list.add(err.getEdge1()); list.add(err.getEdge1());
list.add(err.getEdge2()); list.add(err.getEdge2());
controller.highlightEdges(list, geom); controller.highlightEdges(list);
controller.addHighlight(new Vertex(err.getIntersection()), geom, Color.BLACK); controller.addHighlight(new Vertex(err.getIntersection()), Color.BLACK);
} }
@Override @Override
public void visit(PointTouchesEdgeError err) { public void visit(PointTouchesEdgeError err) {
controller.highlight(err.getEdge(), geom); controller.highlight(err.getEdge());
controller.addHighlight(err.getVertex(), geom, Color.BLACK); controller.addHighlight(err.getVertex(), Color.BLACK);
} }
@Override @Override
...@@ -219,22 +214,22 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -219,22 +214,22 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(NotCeilingError err) { public void visit(NotCeilingError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
} }
@Override @Override
public void visit(NotFloorError err) { public void visit(NotFloorError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
} }
@Override @Override
public void visit(NotWallError err) { public void visit(NotWallError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
} }
@Override @Override
public void visit(NotGroundError err) { public void visit(NotGroundError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
} }
@Override @Override
...@@ -249,7 +244,7 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -249,7 +244,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(DegeneratedRingError err) { public void visit(DegeneratedRingError err) {
controller.highlight(err.getRing(), geom); controller.highlight(err.getRing());
} }
@Override @Override
...@@ -269,7 +264,7 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -269,7 +264,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override @Override
public void visit(PolygonWithoutSurfaceError err) { public void visit(PolygonWithoutSurfaceError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon());
} }
@Override @Override
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment