Commit 297334ab authored by Matthias Betz's avatar Matthias Betz
Browse files

Add design spec for OpenGL 3D view



Replaces the JavaFX SubScene/MeshView rendering with an OpenGL viewport
(openglfx-jogl, GL 3.3 core), single global VBO, GPU color-picking, and
baked fake shading. Windows/Linux targets; JavaFX 3D path removed.

Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parents 5c2847ef bd40b396
package de.hft.stuttgart.citydoctor2.gui.tree;
package de.hft.stuttgart.citydoctor2.gui.tree.node;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
public class VertexNode extends Renderable {
......
......@@ -2,6 +2,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.Tab?>
......@@ -16,7 +17,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox prefHeight="800.0" spacing="15.0" xmlns="http://javafx.com/javafx/8.0.221" xmlns:fx="http://javafx.com/fxml/1">
<VBox prefHeight="800.0" spacing="15.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TabPane fx:id="tabPane" VBox.vgrow="ALWAYS">
<tabs>
......@@ -60,6 +61,15 @@
<Font size="16.0" />
</font>
</Label>
<Label fx:id="checkDescriptionsLabel" layoutX="10.0" layoutY="10.0" text="Check Descriptions Available At:">
<font>
<Font size="16.0" />
</font>
<HBox.margin>
<Insets left="100.0" />
</HBox.margin>
</Label>
<Hyperlink fx:id="homepageHyperlink" alignment="CENTER" text="Hyperlink" />
</children>
</HBox>
<HBox spacing="5.0" VBox.vgrow="ALWAYS">
......@@ -71,6 +81,16 @@
<Font size="16.0" />
</font>
</Label>
<HBox>
<children>
<Button fx:id="selectAllGCBtn" mnemonicParsing="false" text="Select All" />
<Button fx:id="selectNoneGCBtn" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" text="Select None">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<TreeTableView fx:id="geometricTable" editable="true" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="400.0" minWidth="400.0" prefWidth="600.0" showRoot="false" VBox.vgrow="ALWAYS" />
</children>
</VBox>
......@@ -81,6 +101,16 @@
<Font size="16.0" />
</font>
</Label>
<HBox>
<children>
<Button fx:id="selectAllSCBtn" mnemonicParsing="false" text="Select All" />
<Button fx:id="selectNoneSCBtn" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" text="Select None">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<TreeTableView fx:id="semanticTable" editable="true" maxHeight="1.7976931348623157E308" minWidth="400.0" prefWidth="600.0" showRoot="false" VBox.vgrow="ALWAYS" />
</children>
</VBox>
......
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minWidth="300.0" spacing="10.0" style="-fx-border-image-width: 2; -fx-border-color: BLACK;" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label fx:id="messageLbl" text="Message">
<font>
<Font size="14.0" />
</font>
</Label>
<ProgressIndicator />
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</VBox>
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1">
<children>
<HBox spacing="5.0" HBox.hgrow="NEVER">
<children>
......@@ -13,6 +16,7 @@
<ImageView fx:id="openImageView" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true" />
</graphic>
</Button>
<Separator layoutX="381.0" layoutY="10.0" orientation="VERTICAL" />
<Button fx:id="checkBtn" mnemonicParsing="false">
<graphic>
<ImageView fx:id="checkImageView" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true" />
......@@ -23,6 +27,7 @@
<ImageView fx:id="reportImageView" disable="true" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true" />
</graphic>
</Button>
<Separator layoutX="116.0" layoutY="10.0" orientation="VERTICAL" />
<ToggleButton fx:id="gridButton" disable="true" mnemonicParsing="false">
<graphic>
<ImageView fx:id="gridImageView" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true" />
......@@ -75,6 +80,7 @@
</HBox>
<HBox fx:id="spacer" alignment="TOP_RIGHT" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0" HBox.hgrow="ALWAYS">
<children>
<Separator orientation="VERTICAL" />
<Button id="loadBtn" fx:id="saveBtn" disable="true" mnemonicParsing="false">
<graphic>
<ImageView fx:id="saveView" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true" />
......
......@@ -19,5 +19,8 @@
<Root level="debug" additivity="false">
<AppenderRef ref="console" />
</Root>
<Logger name="com.zaxxer.hikari" level="INFO" additivity="false">
<AppenderRef ref="console"/>
</Logger>
</Loggers>
</Configuration>
\ No newline at end of file
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.16.0</version>
<version>3.18.3</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>CityDoctorHealer</artifactId>
......
package de.hft.stuttgart.citydoctor2.connect.edge;
/**
* Marker interface for excluding testcases that use the deprecated connection to the C++ library.
* The tests are kept for the Java replacement library that is currently being developed.
*/
public interface CppTest {
}
......@@ -12,6 +12,9 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
import de.hft.stuttgart.citydoctor2.exceptions.CityObjectCacheException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.ade.ADEException;
......@@ -26,7 +29,7 @@ import org.citygml4j.xml.writer.CityGMLWriteException;
import org.citygml4j.xml.writer.CityGMLWriter;
import de.hft.stuttgart.citydoctor2.CityDoctorValidation;
import de.hft.stuttgart.citydoctor2.check.AbstractCheck;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
......@@ -64,7 +67,7 @@ public class Healer {
private Checker checker;
private ModificationListener l;
private HealingPlan plan;
private CityObjectCache cache;
static {
// CppInitializer.initCpp();
}
......@@ -156,7 +159,7 @@ public class Healer {
if (configFile == null) {
config = ValidationConfiguration.loadStandardValidationConfig();
} else {
config = ValidationConfiguration.loadValidationConfig(configFile.getAbsolutePath());
config = ValidationConfiguration.loadValidationConfigFile(configFile.getAbsolutePath());
}
CityDoctorModel model = CityGmlParser.parseCityGmlFile(in.getAbsolutePath(), config.getParserConfiguration());
Checker c = new Checker(config, model);
......@@ -187,6 +190,12 @@ public class Healer {
}
};
}
if(checker.getModel() != null){
cache = checker.getModel().getCache();
} else {
//Stream healing is being used, use an unconnected cache
cache = new UnconnectedCache();
}
this.l = l;
}
......@@ -208,7 +217,12 @@ public class Healer {
}
public void healModel(CityDoctorModel model) {
try (AutoCloseable checkingContext = model.getCache().startCheckingMode()){
model.createFeatureStream().forEach(this::healCityObject);
} catch (Exception e) {
throw new CityObjectCacheException("Healer critically failed during attempt to heal the model",e);
}
}
public void healCityObject(CityObject co) {
......@@ -263,7 +277,7 @@ public class Healer {
co.reCreateGeometries(factory, checker.getConfig().getParserConfiguration());
byte[] gml = writeCityGml(co.getGmlObject());
// remove them again to save memory
co.unsetGmlGeometries();
co.clearGmlGeometries();
SvrlContentHandler handler = Checker.executeSchematronValidationIfAvailable(config,
new ByteArrayInputStream(gml));
if (handler != null) {
......@@ -282,14 +296,12 @@ public class Healer {
*
* @param aco the feature to write
* @return the byte array containing the feature
* @throws ADEException
* @throws CityGMLBuilderException
* @throws CityGMLWriteException
* @throws CityGMLWriteException if writing of the CityGML file fails
*/
private byte[] writeCityGml(AbstractCityObject aco) throws CityGMLWriteException {
CityModel model = new CityModel();
model.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
CityGMLContext context = CityGmlParser.getContext();
CityGMLContext context = CityGmlParser.setupContext();
CityGMLOutputFactory outputFactory = context.createCityGMLOutputFactory(CityGMLVersion.v2_0);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (CityGMLWriter writer = outputFactory.createCityGMLWriter(out)) {
......@@ -348,12 +360,14 @@ public class Healer {
if (checker.getConfig().getParserConfiguration().useLowMemoryConsumption()) {
co.clearMetaInformation();
}
//Reinsert object into cache and release locks
cache.put(co);
}
}
private void filterOutDuplicateVertices(CityObject co) {
Map<Vertex, Vertex> map = new HashMap<>();
co.accept(new AbstractCheck() {
co.accept(new CheckableUtilsVisitor() {
@Override
public void check(LinearRing ring) {
for (int i = 0; i < ring.getVertices().size(); i++) {
......
......@@ -24,7 +24,7 @@ import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.AbstractCheck;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.check.HealingID;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.check.ModificationListener;
......@@ -42,6 +42,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.GmlElement;
import de.hft.stuttgart.citydoctor2.datastructure.LinkedPolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation;
public class HealMissingSolid implements HealingMethod {
......@@ -103,7 +104,7 @@ public class HealMissingSolid implements HealingMethod {
private List<Polygon> collectPolygons(CityObject co) {
List<Polygon> polygons = new ArrayList<>();
AbstractCheck polygonCheck = new AbstractCheck() {
CheckableUtilsVisitor polygonCheck = new CheckableUtilsVisitor() {
@Override
public void check(Polygon p) {
if (!p.isLink()) {
......@@ -116,7 +117,7 @@ public class HealMissingSolid implements HealingMethod {
}
private boolean createSolidInFeature(Lod lod, AbstractBuilding building) {
Geometry geom = new Geometry(GeometryType.SOLID, lod);
Geometry geom = new Geometry(GeometryType.SOLID, lod, Orientation.OUTWARD);
List<Polygon> polygons = collectPolygons(building, lod, geom);
for (Polygon p : polygons) {
geom.addPolygon(p);
......
......@@ -19,6 +19,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.LinkedPolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class HealPolygonWithoutSurface implements HealingMethod {
......@@ -60,7 +61,7 @@ public class HealPolygonWithoutSurface implements HealingMethod {
Geometry parentGeometry = cp.getParent();
parentGeometry.getPolygons().remove(cp);
ab.addBoundarySurface(bs);
Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, parentGeometry.getLod());
Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, parentGeometry.getLod(), Orientation.OUTWARD);
bs.addGeometry(geom);
geom.addPolygon(cp);
parentGeometry.addPolygon(new LinkedPolygon(cp, parentGeometry));
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configuration>
<Configuration status="INFO">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout
pattern="[%-5level] %d{HH:mm:ss.SSS} %c{1} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug" additivity="false">
<AppenderRef ref="console" />
</Root>
<Logger name="com.zaxxer.hikari" level="INFO" additivity="false">
<AppenderRef ref="console"/>
</Logger>
</Loggers>
</Configuration>
\ No newline at end of file
......@@ -24,11 +24,11 @@ public class HealAllPolygonsWrongOrientationErrorTest {
CityDoctorModel model = TestUtil.loadCityModel(path, config);
Checker c = new Checker(config, model);
c.runChecks();
Geometry geom = model.getBuildings().get(0).getGeometries().get(0);
Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0);
Assert.assertTrue(geom.containsError(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION));
Healer healer = new Healer(c);
healer.healCityObject(model.getBuildings().get(0));
healer.healCityObject(model.getBuildings().toList().get(0));
Assert.assertFalse(geom.containsError(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION));
}
......
......@@ -26,11 +26,11 @@ public class HealConsecutivePointsSameErrorTest {
Checker c = new Checker(config, model);
c.runChecks();
Geometry geom = model.getBuildings().get(0).getGeometries().get(0);
Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0);
Assert.assertTrue(geom.containsError(CheckId.C_GE_R_DUPLICATE_POINT));
Healer healer = new Healer(c);
healer.healCityObject(model.getBuildings().get(0));
healer.healCityObject(model.getBuildings().toList().get(0));
Assert.assertFalse(geom.containsError(CheckId.C_GE_R_DUPLICATE_POINT));
}
......
......@@ -8,8 +8,8 @@ import org.junit.Test;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.healer.Healer;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
......@@ -25,13 +25,13 @@ public class HealHoleOutsideErrorTest {
Checker c = new Checker(config, model);
c.runChecks();
Geometry geom = model.getBuildings().get(0).getGeometries().get(0);
Assert.assertTrue(geom.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
Building building = model.getBuildings().toList().get(0);
Assert.assertTrue(building.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
Healer healer = new Healer(c);
healer.healCityObject(model.getBuildings().get(0));
healer.healCityObject(model.getBuildings().toList().get(0));
Assert.assertFalse(geom.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
Assert.assertFalse(building.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
}
}
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