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;
import java.util.List;
import java.util.function.Supplier;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
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.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.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 {
private double scale;
private Group points;
private Group edges;
private Group highlights;
public HighlightController(Group world) {
highlights = new Group();
points = new Group();
edges = new Group();
highlights.getChildren().add(edges);
highlights.getChildren().add(points);
Platform.runLater(() -> world.getChildren().add(highlights));
}
public void clearHighlights() {
points.getChildren().clear();
edges.getChildren().clear();
}
public void changeScaling(double translateZ) {
scale = Math.abs(translateZ);
scale = Math.min(150, scale);
scale = Math.max(10, scale);
scale = scale * 0.01;
for (Node n : points.getChildren()) {
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);
}
}
}
public void highlight(Polygon p, TriangulatedGeometry currentTriGeom) {
clearHighlights();
addHighlight(p, currentTriGeom, Color.RED, Color.BLUE);
}
private void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom, Color extColor, Color intColor) {
if (currentTriGeom == null || p == null) {
return;
}
addHighlight(p.getExteriorRing(), currentTriGeom, extColor);
for (LinearRing intRing : p.getInnerRings()) {
addHighlight(intRing, currentTriGeom, intColor);
}
}
public void highlight(LinearRing ring, TriangulatedGeometry currentTriGeom) {
clearHighlights();
if (ring.getType() == LinearRingType.EXTERIOR) {
addHighlight(ring, currentTriGeom, Color.RED);
} else {
addHighlight(ring, currentTriGeom, Color.BLUE);
}
}
public void highlight(List<LinearRing> rings, TriangulatedGeometry currentTriGeom) {
clearHighlights();
for (LinearRing lr : rings) {
if (lr.getType() == LinearRingType.EXTERIOR) {
addHighlight(lr, currentTriGeom, Color.RED);
} else {
addHighlight(lr, currentTriGeom, Color.BLUE);
}
}
}
private void addHighlight(LinearRing ring, TriangulatedGeometry currentTriGeom, Color pointColor) {
if (currentTriGeom == null || ring == null) {
return;
}
Vector3d movedBy = currentTriGeom.getMovedBy();
for (Vertex v : ring.getVertices()) {
highlightPoint(movedBy, v, pointColor);
}
for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vertex v1 = ring.getVertices().get(i);
Vertex v2 = ring.getVertices().get(i + 1);
highlightEdge(v1, v2, movedBy);
}
}
public void highlight(Edge e, TriangulatedGeometry currentTriGeom) {
clearHighlights();
addHighlight(e, currentTriGeom);
}
private void addHighlight(Edge e, TriangulatedGeometry currentTriGeom) {
if (currentTriGeom == null || e == null) {
return;
}
Vector3d movedBy = currentTriGeom.getMovedBy();
highlightEdge(e.getFrom(), e.getTo(), movedBy);
highlightPoint(movedBy, e.getFrom(), Color.RED);
highlightPoint(movedBy, e.getTo(), Color.RED);
}
public void highlight(Vertex v, TriangulatedGeometry currentTriGeom) {
clearHighlights();
addHighlight(v, currentTriGeom, Color.RED);
}
public void addHighlight(Vertex v, TriangulatedGeometry currentTriGeom, Color c) {
if (currentTriGeom == null || v == null) {
return;
}
Vector3d movedBy = currentTriGeom.getMovedBy();
highlightPoint(movedBy, v, c);
}
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) {
clearHighlights();
for (Edge e : errorEdges) {
addHighlight(e, currentTriGeom);
}
}
public void highlightPolygons(List<List<Polygon>> components, TriangulatedGeometry currentTriGeom) {
clearHighlights();
for (int i = 0; i < components.size(); i++) {
Color extColor;
Color intColor = switch (i % 3) {
private final GLViewport viewport;
private Supplier<double[]> centerSupplier;
private final FloatList points = new FloatList();
private final FloatList pointColors = new FloatList();
private final FloatList lines = new FloatList();
private final FloatList lineColors = new FloatList();
public HighlightController(GLViewport viewport) {
this.viewport = viewport;
}
public void setCenterSupplier(Supplier<double[]> centerSupplier) {
this.centerSupplier = centerSupplier;
}
private double[] center() {
if (centerSupplier == null) {
return new double[3];
}
double[] c = centerSupplier.get();
return c == null ? new double[3] : c;
}
public void clearHighlights() {
points.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) {
double scale = Math.abs(translateZ);
scale = Math.min(150, scale);
scale = Math.max(10, scale);
scale = scale * 0.01;
viewport.setPointScale((float) (scale * 8));
}
// ---- 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 highlight API (no TriangulatedGeometry) ----
public void highlight(Polygon p) {
clearHighlights();
addHighlight(p, Color.RED, Color.BLUE);
push();
}
private void addHighlight(Polygon p, Color extColor, Color intColor) {
if (p == null) {
return;
}
addHighlight(p.getExteriorRing(), extColor);
for (LinearRing intRing : p.getInnerRings()) {
addHighlight(intRing, intColor);
}
}
public void highlight(LinearRing ring) {
clearHighlights();
if (ring.getType() == LinearRingType.EXTERIOR) {
addHighlight(ring, Color.RED);
} else {
addHighlight(ring, Color.BLUE);
}
push();
}
public void highlight(List<LinearRing> rings) {
clearHighlights();
for (LinearRing lr : rings) {
if (lr.getType() == LinearRingType.EXTERIOR) {
addHighlight(lr, Color.RED);
} else {
addHighlight(lr, Color.BLUE);
}
}
push();
}
private void addHighlight(LinearRing ring, Color pointColor) {
if (ring == null) {
return;
}
for (Vertex v : ring.getVertices()) {
addPoint(v, pointColor);
}
for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vertex v1 = ring.getVertices().get(i);
Vertex v2 = ring.getVertices().get(i + 1);
addLine(v1, v2, Color.ORANGE);
}
}
public void highlight(Edge e) {
clearHighlights();
addHighlight(e);
push();
}
private void addHighlight(Edge e) {
if (e == null) {
return;
}
addLine(e.getFrom(), e.getTo(), Color.ORANGE);
addPoint(e.getFrom(), Color.RED);
addPoint(e.getTo(), Color.RED);
}
public void highlight(Vertex v) {
clearHighlights();
addPoint(v, Color.RED);
push();
}
public void addHighlight(Vertex v, Color c) {
if (v == null) {
return;
}
addPoint(v, c);
push();
}
public void highlightEdges(List<Edge> errorEdges) {
clearHighlights();
for (Edge e : errorEdges) {
addHighlight(e);
}
push();
}
public void highlightPolygons(List<List<Polygon>> components) {
clearHighlights();
for (int i = 0; i < components.size(); i++) {
Color extColor;
Color intColor = switch (i % 3) {
case 1 -> {
extColor = Color.GREEN;
yield Color.YELLOW;
......@@ -202,21 +192,24 @@ public class HighlightController {
yield Color.BLUE;
}
};
// select some color pairs for exterior and inner rings
// select some color pairs for exterior and inner rings
List<Polygon> component = components.get(i);
for (Polygon p : component) {
addHighlight(p, currentTriGeom, extColor, intColor);
}
}
}
public void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom) {
addHighlight(p, currentTriGeom, Color.RED, Color.BLUE);
}
public void highlight(Triangle3d t, TriangulatedGeometry currentTriGeom) {
highlightEdge(t.getP1(), t.getP2(), currentTriGeom.getMovedBy());
highlightEdge(t.getP2(), t.getP3(), currentTriGeom.getMovedBy());
highlightEdge(t.getP3(), t.getP1(), currentTriGeom.getMovedBy());
}
for (Polygon p : component) {
addHighlight(p, extColor, intColor);
}
}
push();
}
public void addHighlight(Polygon p) {
addHighlight(p, Color.RED, Color.BLUE);
push();
}
public void highlight(Triangle3d t) {
addLine(t.getP1(), t.getP2(), Color.ORANGE);
addLine(t.getP2(), t.getP3(), Color.ORANGE);
addLine(t.getP3(), t.getP1(), Color.ORANGE);
push();
}
}
......@@ -55,32 +55,27 @@ import javafx.scene.paint.Color;
public class ListErrorVisitor implements ErrorVisitor {
private HighlightController controller;
private TriangulatedGeometry geom;
public ListErrorVisitor(HighlightController controller) {
this.controller = controller;
}
public void setGeometry(TriangulatedGeometry geom) {
this.geom = geom;
}
@Override
public void visit(PolygonHoleOutsideError err) {
List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.addAll(err.getHolesOutside());
controller.highlight(highlightedRings, geom);
controller.highlight(highlightedRings);
}
@Override
public void visit(NonManifoldEdgeError err) {
controller.highlightEdges(err.getEdges(), geom);
controller.highlightEdges(err.getEdges());
}
@Override
public void visit(MultipleConnectedComponentsError err) {
controller.highlightPolygons(err.getComponents(), geom);
controller.highlightPolygons(err.getComponents());
}
@Override
......@@ -88,18 +83,18 @@ public class ListErrorVisitor implements ErrorVisitor {
List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.add(err.getInnerRing());
controller.highlight(highlightedRings, geom);
controller.highlight(highlightedRings);
}
@Override
public void visit(NonManifoldVertexError err) {
controller.highlightPolygons(err.getComponents(), geom);
controller.addHighlight(err.getVertex(), geom, Color.BLUEVIOLET);
controller.highlightPolygons(err.getComponents());
controller.addHighlight(err.getVertex(), Color.BLUEVIOLET);
}
@Override
public void visit(PolygonWrongOrientationError err) {
controller.highlightEdges(err.getEdges(), geom);
controller.highlightEdges(err.getEdges());
}
@Override
......@@ -107,12 +102,12 @@ public class ListErrorVisitor implements ErrorVisitor {
List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.add(err.getInnerRing());
controller.highlight(highlightedRings, geom);
controller.highlight(highlightedRings);
}
@Override
public void visit(SolidNotClosedError err) {
controller.highlightEdges(err.getErrorEdges(), geom);
controller.highlightEdges(err.getErrorEdges());
}
@Override
......@@ -127,14 +122,14 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(RingNotClosedError err) {
controller.highlight(err.getRing(), geom);
controller.highlight(err.getRing());
}
@Override
public void visit(ConsecutivePointSameError err) {
controller.highlight(err.getRing(), geom);
controller.addHighlight(err.getVertex1(), geom, Color.BLACK);
controller.addHighlight(err.getVertex2(), geom, Color.BLACK);
controller.highlight(err.getRing());
controller.addHighlight(err.getVertex1(), Color.BLACK);
controller.addHighlight(err.getVertex2(), Color.BLACK);
}
@Override
......@@ -144,7 +139,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(PolygonInteriorDisconnectedError err) {
controller.highlight(err.getConnectedRings(), geom);
controller.highlight(err.getConnectedRings());
}
@Override
......@@ -154,18 +149,18 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(RingTooFewPointsError err) {
controller.highlight(err.getRing(), geom);
controller.highlight(err.getRing());
}
@Override
public void visit(NonPlanarPolygonNormalsDeviation err) {
controller.highlight(err.getPolygon(), geom);
controller.highlight(err.getPolygon());
}
@Override
public void visit(NonPlanarPolygonDistancePlaneError err) {
controller.highlight(err.getPolygon(), geom);
controller.addHighlight(err.getVertex(), geom, Color.BLACK);
controller.highlight(err.getPolygon());
controller.addHighlight(err.getVertex(), Color.BLACK);
}
@Override
......@@ -173,15 +168,15 @@ public class ListErrorVisitor implements ErrorVisitor {
List<LinearRing> rings = new ArrayList<>();
rings.add(err.getIntersectingRings().getValue0());
rings.add(err.getIntersectingRings().getValue1());
controller.highlight(rings, geom);
controller.highlight(rings);
}
@Override
public void visit(SolidSelfIntError err) {
controller.clearHighlights();
for (PolygonIntersection intersection : err.getIntersections()) {
controller.addHighlight(intersection.getP1(), geom);
controller.addHighlight(intersection.getP2(), geom);
controller.addHighlight(intersection.getP1());
controller.addHighlight(intersection.getP2());
}
}
......@@ -192,9 +187,9 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(RingDuplicatePointError err) {
controller.highlight(err.getRing(), geom);
controller.addHighlight(err.getVertex1(), geom, Color.BLACK);
controller.addHighlight(err.getVertex2(), geom, Color.BLACK);
controller.highlight(err.getRing());
controller.addHighlight(err.getVertex1(), Color.BLACK);
controller.addHighlight(err.getVertex2(), Color.BLACK);
}
@Override
......@@ -202,14 +197,14 @@ public class ListErrorVisitor implements ErrorVisitor {
List<Edge> list = new ArrayList<>();
list.add(err.getEdge1());
list.add(err.getEdge2());
controller.highlightEdges(list, geom);
controller.addHighlight(new Vertex(err.getIntersection()), geom, Color.BLACK);
controller.highlightEdges(list);
controller.addHighlight(new Vertex(err.getIntersection()), Color.BLACK);
}
@Override
public void visit(PointTouchesEdgeError err) {
controller.highlight(err.getEdge(), geom);
controller.addHighlight(err.getVertex(), geom, Color.BLACK);
controller.highlight(err.getEdge());
controller.addHighlight(err.getVertex(), Color.BLACK);
}
@Override
......@@ -219,22 +214,22 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(NotCeilingError err) {
controller.highlight(err.getPolygon(), geom);
controller.highlight(err.getPolygon());
}
@Override
public void visit(NotFloorError err) {
controller.highlight(err.getPolygon(), geom);
controller.highlight(err.getPolygon());
}
@Override
public void visit(NotWallError err) {
controller.highlight(err.getPolygon(), geom);
controller.highlight(err.getPolygon());
}
@Override
public void visit(NotGroundError err) {
controller.highlight(err.getPolygon(), geom);
controller.highlight(err.getPolygon());
}
@Override
......@@ -249,7 +244,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(DegeneratedRingError err) {
controller.highlight(err.getRing(), geom);
controller.highlight(err.getRing());
}
@Override
......@@ -269,7 +264,7 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public void visit(PolygonWithoutSurfaceError err) {
controller.highlight(err.getPolygon(), geom);
controller.highlight(err.getPolygon());
}
@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