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
...@@ -22,7 +22,9 @@ import de.hft.stuttgart.citydoctor2.check.error.NotFloorError; ...@@ -22,7 +22,9 @@ import de.hft.stuttgart.citydoctor2.check.error.NotFloorError;
import de.hft.stuttgart.citydoctor2.check.error.NotGroundError; import de.hft.stuttgart.citydoctor2.check.error.NotGroundError;
import de.hft.stuttgart.citydoctor2.check.error.NotWallError; import de.hft.stuttgart.citydoctor2.check.error.NotWallError;
import de.hft.stuttgart.citydoctor2.check.error.NullAreaError; import de.hft.stuttgart.citydoctor2.check.error.NullAreaError;
import de.hft.stuttgart.citydoctor2.check.error.PlanarityError;
import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError; import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError; import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError; import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError; import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError;
...@@ -31,15 +33,19 @@ import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError; ...@@ -31,15 +33,19 @@ import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError; import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError; import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError; import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingError;
import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError; import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.RingSelfIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError; import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError; import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.check.error.SolidError;
import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError; import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError; import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError; import de.hft.stuttgart.citydoctor2.check.error.SurfaceFragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError; import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError; import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError; import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
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.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
...@@ -237,7 +243,7 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -237,7 +243,7 @@ public class ListErrorVisitor implements ErrorVisitor {
} }
@Override @Override
public void visit(SurfaceUnfragmentedError err) { public void visit(SurfaceFragmentedError err) {
// nothing to display // nothing to display
} }
...@@ -265,4 +271,34 @@ public class ListErrorVisitor implements ErrorVisitor { ...@@ -265,4 +271,34 @@ public class ListErrorVisitor implements ErrorVisitor {
public void visit(PolygonWithoutSurfaceError err) { public void visit(PolygonWithoutSurfaceError err) {
controller.highlight(err.getPolygon(), geom); controller.highlight(err.getPolygon(), geom);
} }
@Override
public void visit(RingError err) {
// nothing to display
}
@Override
public void visit(PolygonError err) {
// nothing to display
}
@Override
public void visit(RingSelfIntersectionError err) {
// nothing to display
}
@Override
public void visit(PlanarityError err) {
// nothing to display
}
@Override
public void visit(SolidError err) {
// nothing to display
}
@Override
public void visit(XMLValidationError err) {
// nothing to display
}
} }
...@@ -162,6 +162,7 @@ public class MainToolBar { ...@@ -162,6 +162,7 @@ public class MainToolBar {
content.getSelectionModel().clearSelection(); content.getSelectionModel().clearSelection();
} }
controller.showWorld(); controller.showWorld();
mainWindow.resetSearchBar();
}); });
showWorldBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.showWorld"))); showWorldBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.showWorld")));
} }
......
package de.hft.stuttgart.citydoctor2.gui; package de.hft.stuttgart.citydoctor2.gui;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.huskerdev.openglfx.GLExecutor;
import com.huskerdev.openglfx.canvas.GLCanvas;
import com.huskerdev.openglfx.jogl.JOGLExecutor;
import com.huskerdev.openglfx.jogl.events.JOGLEvent;
import com.huskerdev.openglfx.jogl.events.JOGLReshapeEvent;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GL3;
import de.hft.stuttgart.citydoctor2.CityDoctorValidation; import de.hft.stuttgart.citydoctor2.CityDoctorValidation;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
import de.hft.stuttgart.citydoctor2.gui.logger.GuiLogger; import de.hft.stuttgart.citydoctor2.gui.logger.GuiLogger;
import de.hft.stuttgart.citydoctor2.gui.tree.BuildingNode;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.gui.tree.RenderableTreeCell; import de.hft.stuttgart.citydoctor2.gui.tree.RenderableTreeCell;
import de.hft.stuttgart.citydoctor2.parameter.ArgumentParser; import de.hft.stuttgart.citydoctor2.parameter.ArgumentParser;
...@@ -36,50 +13,20 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; ...@@ -36,50 +13,20 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ProgressListener; import de.hft.stuttgart.citydoctor2.parser.ProgressListener;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.utils.Pair;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.embed.swing.SwingFXUtils; import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.AmbientLight; import javafx.scene.*;
import javafx.scene.Group; import javafx.scene.control.*;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.RadioButton;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javafx.scene.input.Dragboard; import javafx.scene.input.*;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
...@@ -87,6 +34,16 @@ import javafx.scene.layout.Priority; ...@@ -87,6 +34,16 @@ import javafx.scene.layout.Priority;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
public class MainWindow extends Application { public class MainWindow extends Application {
...@@ -124,7 +81,7 @@ public class MainWindow extends Application { ...@@ -124,7 +81,7 @@ public class MainWindow extends Application {
private TreeView<Renderable> attributeView; private TreeView<Renderable> attributeView;
@FXML @FXML
private TreeView<Renderable> otherObjectsView; TreeView<Renderable> otherObjectsView;
@FXML @FXML
private TreeView<Renderable> polygonView; private TreeView<Renderable> polygonView;
...@@ -251,6 +208,8 @@ public class MainWindow extends Application { ...@@ -251,6 +208,8 @@ public class MainWindow extends Application {
@FXML @FXML
private Label availableLabel; private Label availableLabel;
private Label globalErrorsLabel;
private Image north; private Image north;
private Image northFlip; private Image northFlip;
...@@ -319,6 +278,8 @@ public class MainWindow extends Application { ...@@ -319,6 +278,8 @@ public class MainWindow extends Application {
System.exit(4); System.exit(4);
} }
} }
CityDoctorValidation.lookForDbConfigFileParameter(argParser);
CityDoctorValidation.lookForDbLocationParameter(argParser);
Application.launch(args); Application.launch(args);
} }
...@@ -343,7 +304,7 @@ public class MainWindow extends Application { ...@@ -343,7 +304,7 @@ public class MainWindow extends Application {
BorderPane bp = loader.load(); BorderPane bp = loader.load();
highlightController = new HighlightController(world); highlightController = new HighlightController(world);
renderer = new Renderer(this, highlightController); renderer = new Renderer(this, highlightController);
clickHandler = new VertexClickHandler(errorView, renderer, stage); clickHandler = new VertexClickHandler(errorView, renderer, stage, this);
controller = new CityDoctorController(this, highlightController, renderer); controller = new CityDoctorController(this, highlightController, renderer);
mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this); mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this);
viewPane.getChildren().add(mainToolBar.getToolBar()); viewPane.getChildren().add(mainToolBar.getToolBar());
...@@ -353,9 +314,6 @@ public class MainWindow extends Application { ...@@ -353,9 +314,6 @@ public class MainWindow extends Application {
ViewRegistration.registerView(valView); ViewRegistration.registerView(valView);
setupViews(valView); setupViews(valView);
createLanguageSelector();
setLabelsInCorrectLanguage();
Scene scene = new Scene(bp, 1280, 800); Scene scene = new Scene(bp, 1280, 800);
createDropTarget(scene, valView); createDropTarget(scene, valView);
String version = Localization.getText(Localization.VERSION); String version = Localization.getText(Localization.VERSION);
...@@ -496,6 +454,7 @@ public class MainWindow extends Application { ...@@ -496,6 +454,7 @@ public class MainWindow extends Application {
}); });
} }
@SuppressWarnings("java:S2629")
private void checkForStartupLoading() { private void checkForStartupLoading() {
if (loadFileAtStartup) { if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile")); logger.info(Localization.getText("MainWindow.loadGivenFile"));
...@@ -586,6 +545,9 @@ public class MainWindow extends Application { ...@@ -586,6 +545,9 @@ public class MainWindow extends Application {
setupSearchButtons(); setupSearchButtons();
setupShowCityComboBox(); setupShowCityComboBox();
createLanguageSelector();
setLabelsInCorrectLanguage();
detailsTabPane.getSelectionModel().selectedIndexProperty() detailsTabPane.getSelectionModel().selectedIndexProperty()
.addListener((ov, oldI, newI) -> Platform.runLater(() -> { .addListener((ov, oldI, newI) -> Platform.runLater(() -> {
...@@ -595,6 +557,11 @@ public class MainWindow extends Application { ...@@ -595,6 +557,11 @@ public class MainWindow extends Application {
errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer); errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
} }
})); }));
// setup label of the global error tab for color changes
globalErrorsLabel = new Label(globalErrorsTab.getText());
globalErrorsTab.setGraphic(globalErrorsLabel);
globalErrorsTab.setText("");
} }
private void setupShowCityComboBox() { private void setupShowCityComboBox() {
...@@ -627,6 +594,10 @@ public class MainWindow extends Application { ...@@ -627,6 +594,10 @@ public class MainWindow extends Application {
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener); showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
} }
public void rebuildErrorFilterTrees(){
controller.errorFilterIndexChanged(showCityObjectsCombo.getSelectionModel().getSelectedIndex());
}
private ChangeListener<Number> setupFilterSelectionListener() { private ChangeListener<Number> setupFilterSelectionListener() {
return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV); return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV);
} }
...@@ -681,7 +652,7 @@ public class MainWindow extends Application { ...@@ -681,7 +652,7 @@ public class MainWindow extends Application {
selectedTab = FeatureType.CITY_FURNITURE; selectedTab = FeatureType.CITY_FURNITURE;
break; break;
case 8: case 8:
selectedTab = FeatureType.GENERIC_CITY_OBJECT; selectedTab = FeatureType.OTHER_CITY_OBJECT;
break; break;
default: default:
throw new IllegalStateException("Unknown tab index: " + index); throw new IllegalStateException("Unknown tab index: " + index);
...@@ -715,7 +686,7 @@ public class MainWindow extends Application { ...@@ -715,7 +686,7 @@ public class MainWindow extends Application {
case CITY_FURNITURE: case CITY_FURNITURE:
cityFurnitureTab.setDisable(value); cityFurnitureTab.setDisable(value);
break; break;
case GENERIC_CITY_OBJECT: case OTHER_CITY_OBJECT:
otherObjectsTab.setDisable(value); otherObjectsTab.setDisable(value);
break; break;
default: default:
...@@ -769,79 +740,102 @@ public class MainWindow extends Application { ...@@ -769,79 +740,102 @@ public class MainWindow extends Application {
} }
private void setupTrees() { private void setupTrees() {
setupTreeRoot(errorView);
setupSelectListener(errorView); setupSelectListener(errorView);
errorView.setRoot(new TreeItem<>());
errorView.setCellFactory(param -> new RenderableTreeCell());
buildingsView.setShowRoot(true); setupTreeRoot(buildingsView);
setupSelectListener(buildingsView); setupSelectListener(buildingsView);
buildingsView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(buildingsView);
ContextMenu cMenu = new ContextMenu();
MenuItem mi = new MenuItem(Localization.getText("MainWindow.export"));
mi.setOnAction(ea -> {
Renderable render = buildingsView.getSelectionModel().getSelectedItem().getValue();
if (render instanceof BuildingNode node) {
controller.export(node.getBuilding());
}
});
cMenu.getItems().add(mi);
MenuItem deleteMi = new MenuItem("Delete");
deleteMi.setOnAction(ae -> controller.delete(buildingsView.getSelectionModel().getSelectedItem()));
cMenu.getItems().add(deleteMi);
buildingsView.setContextMenu(cMenu); setupTreeRoot(vegetationView);
vegetationView.setShowRoot(true);
setupSelectListener(vegetationView); setupSelectListener(vegetationView);
vegetationView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(vegetationView);
vegetationView.setContextMenu(cMenu);
transView.setShowRoot(true); setupTreeRoot(transView);
setupSelectListener(transView); setupSelectListener(transView);
transView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(transView);
tunnelView.setShowRoot(true); setupTreeRoot(tunnelView);
setupSelectListener(tunnelView); setupSelectListener(tunnelView);
tunnelView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(tunnelView);
bridgeView.setShowRoot(true); setupTreeRoot(bridgeView);
setupSelectListener(bridgeView); setupSelectListener(bridgeView);
bridgeView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(bridgeView);
waterView.setShowRoot(true); setupTreeRoot(waterView);
setupSelectListener(waterView); setupSelectListener(waterView);
waterView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(waterView);
terrainView.setShowRoot(true); setupTreeRoot(terrainView);
setupSelectListener(terrainView); setupSelectListener(terrainView);
terrainView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(terrainView);
cityFurnitureView.setShowRoot(true); setupTreeRoot(cityFurnitureView);
setupSelectListener(cityFurnitureView); setupSelectListener(cityFurnitureView);
cityFurnitureView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(cityFurnitureView);
otherObjectsView.setShowRoot(true); setupTreeRoot(otherObjectsView);
setupSelectListener(otherObjectsView); setupSelectListener(otherObjectsView);
otherObjectsView.setCellFactory(param -> new RenderableTreeCell()); setupFeatureTabContextMenu(otherObjectsView);
setupTreeRoot(vertexView);
setupSelectListener(vertexView); setupSelectListener(vertexView);
vertexView.setRoot(new TreeItem<>());
vertexView.setCellFactory(param -> new RenderableTreeCell());
setupTreeRoot(polygonView);
setupSelectListener(polygonView); setupSelectListener(polygonView);
polygonView.setRoot(new TreeItem<>());
polygonView.setCellFactory(param -> new RenderableTreeCell());
setupTreeRoot(edgeView);
setupSelectListener(edgeView); setupSelectListener(edgeView);
edgeView.setRoot(new TreeItem<>());
edgeView.setCellFactory(param -> new RenderableTreeCell());
setupTreeRoot(attributeView);
setupSelectListener(attributeView); setupSelectListener(attributeView);
attributeView.setRoot(new TreeItem<>());
attributeView.setCellFactory(param -> new RenderableTreeCell());
} }
private void setupTreeRoot(TreeView<Renderable> tView){
tView.setRoot(new TreeItem<>());
tView.setCellFactory(param -> new RenderableTreeCell());
}
private void setupFeatureTabContextMenu(TreeView<Renderable> featureView) {
ContextMenu menu = new ContextMenu();
MenuItem exportOption = new MenuItem(Localization.getText("MainWindow.export"));
exportOption.setOnAction(ea -> {
Renderable node = featureView.getSelectionModel().getSelectedItem().getValue();
fetchExportAction(node);
});
menu.getItems().add(exportOption);
MenuItem copyToClipboardOption = new MenuItem(Localization.getText("MainWindow.copyId"));
copyToClipboardOption.setOnAction(ea -> {
Renderable node = featureView.getSelectionModel().getSelectedItem().getValue();
fetchCopyAction(node);
});
menu.getItems().add(copyToClipboardOption);
/* Was used for debugging the repair module, functionless since the RenderNode rework.
* Commented out for now, might be needed later on again
MenuItem deleteOption = new MenuItem(Localization.getText("MainWindow.delete"));
deleteOption.setOnAction(ae -> controller.delete(featureView.getSelectionModel().getSelectedItem()));
menu.getItems().add(deleteOption);
*/
featureView.setContextMenu(menu);
}
public void fetchCopyAction(Renderable node) {
node.getClipboardAction(this);
}
public void fetchExportAction(Renderable node) {
node.getFeatureExportAction(this);
}
private void setupSelectListener(TreeView<Renderable> view) { private void setupSelectListener(TreeView<Renderable> view) {
view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> { view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> {
if (newI != null) { if (newI != null) {
...@@ -851,48 +845,13 @@ public class MainWindow extends Application { ...@@ -851,48 +845,13 @@ public class MainWindow extends Application {
} }
private void setup3dView() { private void setup3dView() {
JOGLExecutor exec = new JOGLExecutor();
GLCanvas canvas = new GLCanvas(exec);
canvas.setMinHeight(300);
canvas.setMaxHeight(300);
canvas.setMinWidth(500);
canvas.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
canvas.setOnMouseClicked(me -> {
System.out.println("Height: " + canvas.getHeight());
});
// HBox box = new HBox();
// box.getChildren().add(canvas);
canvas.addOnInitEvent(event -> {
GL3 gl = ((JOGLEvent) event).getGl();
});
canvas.addOnReshapeEvent(event -> {
GL3 gl = ((JOGLReshapeEvent) event).getGl();
});
canvas.addOnRenderEvent(event -> {
GL3 gl = ((JOGLEvent) event).getGl();
});
canvas.addOnDisposeEvent(event -> {
GL3 gl = ((JOGLEvent) event).getGl();
});
Group root = new Group(); Group root = new Group();
geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED); geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED);
geomScene.heightProperty().bind(meshView.heightProperty()); geomScene.heightProperty().bind(meshView.heightProperty());
geomScene.widthProperty().bind(meshView.widthProperty()); geomScene.widthProperty().bind(meshView.widthProperty());
geomScene.setFill(Color.AZURE); geomScene.setFill(Color.AZURE);
meshView.getChildren().add(geomScene); meshView.getChildren().add(geomScene);
// meshView.getChildren().add(canvas);
// to avoid strange behaviour of the GUI resizeing itself
meshView.setBackground(new Background(new BackgroundFill(null, null, null)));
geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> { geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> {
clickStart[0] = me.getScreenX(); clickStart[0] = me.getScreenX();
clickStart[1] = me.getScreenY(); clickStart[1] = me.getScreenY();
...@@ -1072,7 +1031,7 @@ public class MainWindow extends Application { ...@@ -1072,7 +1031,7 @@ public class MainWindow extends Application {
return attributeView; return attributeView;
} }
public TreeView<Renderable> getOtherObjectsView() { public TreeView<Renderable> getOtherCityObjectView() {
return otherObjectsView; return otherObjectsView;
} }
...@@ -1195,6 +1154,34 @@ public class MainWindow extends Application { ...@@ -1195,6 +1154,34 @@ public class MainWindow extends Application {
controller.setOriginBB(b); controller.setOriginBB(b);
} }
public void showCityObjectDetail(CityObject co) {
Pair<Tab, TreeView<Renderable>> tabPair = getFeatureTabOf(co);
Tab featureTab = tabPair.getValue0();
TreeView<Renderable> featureView = tabPair.getValue1();
featurePane.getSelectionModel().select(featureTab);
searchField.setText(co.getGmlId().getGmlString());
searchBtn.fire();
featureView.getSelectionModel().select(0);
featureView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
}
private Pair<Tab,TreeView<Renderable>> getFeatureTabOf(CityObject co) {
FeatureType coType = co.getFeatureType();
return switch (coType) {
case BUILDING -> new Pair<>(buildingsTab, buildingsView);
case VEGETATION -> new Pair<>(vegetationTab, vegetationView);
case TRANSPORTATION -> new Pair<>(transportationTab, transView);
case TUNNEL -> new Pair<>(tunnelTab, tunnelView);
case BRIDGE -> new Pair<>(bridgeTab, bridgeView);
case WATER -> new Pair<>(waterTab, waterView);
case LAND -> new Pair<>(terrainTab, terrainView);
case CITY_FURNITURE -> new Pair<>(cityFurnitureTab, cityFurnitureView);
case OTHER_CITY_OBJECT -> new Pair<>(otherObjectsTab, otherObjectsView);
default -> throw new IllegalArgumentException("unknown feature type: " + coType);
};
}
public MainToolBar getMainToolbar() { public MainToolBar getMainToolbar() {
return mainToolBar; return mainToolBar;
} }
...@@ -1211,6 +1198,7 @@ public class MainWindow extends Application { ...@@ -1211,6 +1198,7 @@ public class MainWindow extends Application {
return mainToolBar.getOpenBtn(); return mainToolBar.getOpenBtn();
} }
public VertexClickHandler getClickHandler() { public VertexClickHandler getClickHandler() {
return clickHandler; return clickHandler;
} }
...@@ -1218,4 +1206,8 @@ public class MainWindow extends Application { ...@@ -1218,4 +1206,8 @@ public class MainWindow extends Application {
public FeatureType getSelectedTab() { public FeatureType getSelectedTab() {
return selectedTab; return selectedTab;
} }
public Label getGlobalErrorsLabel() {
return globalErrorsLabel;
}
} }
...@@ -150,6 +150,7 @@ public class OpenFileDialog { ...@@ -150,6 +150,7 @@ public class OpenFileDialog {
pathField.setDisable(true); pathField.setDisable(true);
selectBtn.setDisable(true); selectBtn.setDisable(true);
stage.setOnCloseRequest(Event::consume); stage.setOnCloseRequest(Event::consume);
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
try { try {
controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation, controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation,
......
...@@ -2,9 +2,16 @@ package de.hft.stuttgart.citydoctor2.gui; ...@@ -2,9 +2,16 @@ package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checkable; import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector;
import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter; import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter;
import de.hft.stuttgart.citydoctor2.gui.tree.*; import de.hft.stuttgart.citydoctor2.gui.tree.*;
import de.hft.stuttgart.citydoctor2.gui.tree.node.EdgeNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.ErrorNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.GenericAttributeNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.LinearRingNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.PolygonNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.VertexNode;
import de.hft.stuttgart.citydoctor2.math.Triangle3d; import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
...@@ -177,303 +184,14 @@ public class Renderer { ...@@ -177,303 +184,14 @@ public class Renderer {
} }
} }
public void render(Building building) { public void setupRefresher(CityObject co) {
refresher = () -> { refresher = () -> {
Set<ConcretePolygon> setupBuildingPolygons = setupBuildingPolygons(building); CityObjectCollector collector = new CityObjectCollector();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPolygons)); co.accept(collector);
render(setupBuildingPolygons); Set<ConcretePolygon> objectPolygons = new HashSet<>();
Platform.runLater(() -> { collector.getCityObjects().forEach(object -> addPolygons(object, objectPolygons));
errorUpdater = () -> displayErrors(building); mainWindow.zoomOutForBoundingBox(BoundingBox.of(objectPolygons));
errorUpdater.run(); render(objectPolygons);
addGenericAttributesToView(building);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingPolygons(Building b) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(b, polygons);
for (BoundarySurface bs : b.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : b.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
for (BuildingRoom br : b.getBuildingRooms()) {
addPolygons(br, polygons);
for (BoundarySurface bs : br.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BuildingRoomFurniture brf : b.getBuildingRoomFurnitureList()) {
addPolygons(brf, polygons);
for (BoundarySurface bs : brf.getBoundarySurfaceList()) {
addPolygons(bs, polygons);
}
}
for (BuildingPart bp : b.getBuildingParts()) {
polygons.addAll(setupBuildingPartPolygons(bp));
}
return polygons;
}
public void render(BuildingPart bp) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingPartPolygons = setupBuildingPartPolygons(bp);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPartPolygons));
render(setupBuildingPartPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bp);
errorUpdater.run();
addGenericAttributesToView(bp);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingPartPolygons(BuildingPart bp) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bp, polygons);
for (BoundarySurface bs : bp.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : bp.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
return polygons;
}
public void render(AbstractRoom room) {
refresher = () -> {
Set<ConcretePolygon> setupRoomPolygons = setupRoomPolygons(room);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupRoomPolygons));
render(setupRoomPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(room);
errorUpdater.run();
addGenericAttributesToView(room);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupRoomPolygons(AbstractRoom room) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(room, polygons);
for (BoundarySurface bs : room.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(AbstractFurniture furniture) {
refresher = () -> {
Set<ConcretePolygon> setupFurniturePolygons = setupFurniturePolygons(furniture);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupFurniturePolygons));
render(setupFurniturePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(furniture);
errorUpdater.run();
addGenericAttributesToView(furniture);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupFurniturePolygons(AbstractFurniture furniture) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(furniture, polygons);
for (BoundarySurface bs : furniture.getBoundarySurfaceList()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(Storey storey) {
refresher = () -> {
Set<ConcretePolygon> setupStoreyPolygons = setupStoreyPolygons(storey);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupStoreyPolygons));
render(setupStoreyPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(storey);
errorUpdater.run();
addGenericAttributesToView(storey);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupStoreyPolygons(Storey storey) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(storey, polygons);
for (BoundarySurface bs : storey.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(BuildingUnit buildingUnit) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingUnitPolgons = setupBuildingUnitPolygons(buildingUnit);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingUnitPolgons));
render(setupBuildingUnitPolgons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(buildingUnit);
errorUpdater.run();
addGenericAttributesToView(buildingUnit);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingUnitPolygons(BuildingUnit buildingUnit) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(buildingUnit, polygons);
for (BoundarySurface bs : buildingUnit.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(Tunnel tunnel) {
refresher = () -> {
Set<ConcretePolygon> setupTunnelPolygons = setupTunnelPolygons(tunnel);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupTunnelPolygons));
render(setupTunnelPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(tunnel);
errorUpdater.run();
addGenericAttributesToView(tunnel);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupTunnelPolygons(Tunnel tunnel) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(tunnel, polygons);
for (BoundarySurface bs : tunnel.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
for (Installation ti : tunnel.getTunnelInstallations()) {
addPolygons(ti, polygons);
for (BoundarySurface bs : ti.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
for (TunnelHollow th : tunnel.getTunnelHollows()) {
addPolygons(th, polygons);
for (BoundarySurface bs : th.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (TunnelFurniture tf : tunnel.getTunnelFurnitureList()) {
addPolygons(tf, polygons);
for (BoundarySurface bs : tf.getBoundarySurfaceList()) {
addPolygons(bs, polygons);
}
}
for (TunnelPart tp : tunnel.getTunnelParts()) {
polygons.addAll(setupTunnelPartPolygons(tp));
}
return polygons;
}
private Set<ConcretePolygon> setupTunnelPartPolygons(TunnelPart tp) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(tp, polygons);
for (BoundarySurface bs : tp.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : tp.getTunnelInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
return polygons;
}
public void render(BridgeObject bridge) {
refresher = () -> {
Set<ConcretePolygon> setupBridgePolygons = setupBridgePolygons(bridge);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBridgePolygons));
render(setupBridgePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bridge);
errorUpdater.run();
addGenericAttributesToView(bridge);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBridgePolygons(BridgeObject bridge) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bridge, polygons);
for (BoundarySurface bs : bridge.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
for (BridgeConstructiveElement consElement : bridge.getConstructiveElements()) {
addPolygons(consElement, polygons);
for (BoundarySurface bs : consElement.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (Installation inst : bridge.getBridgeInstallations()) {
addPolygons(inst, polygons);
for (BoundarySurface bs : inst.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
return polygons;
}
public void render(CityObject co) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupCityObjectPolygons(co);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> { Platform.runLater(() -> {
errorUpdater = () -> displayErrors(co); errorUpdater = () -> displayErrors(co);
errorUpdater.run(); errorUpdater.run();
...@@ -483,128 +201,8 @@ public class Renderer { ...@@ -483,128 +201,8 @@ public class Renderer {
refresher.run(); refresher.run();
} }
public void render(ReliefObject relief) { public void render(CityObject co) {
refresher = () -> { setupRefresher(co);
Set<ConcretePolygon> setupCityObjectPolygons = setupReliefPolygons(relief);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(relief);
errorUpdater.run();
addGenericAttributesToView(relief);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupReliefPolygons(ReliefObject relief) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(relief, polygons);
for (TinObject tin : relief.getComponents()) {
addPolygons(tin, polygons);
}
return polygons;
}
public void render(Installation bi) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupBuildingInstallationPolygons(bi);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bi);
errorUpdater.run();
addGenericAttributesToView(bi);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingInstallationPolygons(Installation bi) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(CityFurniture cf) {
refresher = () -> {
Set<ConcretePolygon> setupCityFurniturePolygons = setupCityFurniturePolygons(cf);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityFurniturePolygons));
render(setupCityFurniturePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(cf);
errorUpdater.run();
addGenericAttributesToView(cf);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupCityFurniturePolygons(CityFurniture cf) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(cf, polygons);
return polygons;
}
public void render(GenericCityObject gco) {
refresher = () -> {
Set<ConcretePolygon> setupGenericCityObjectPolygons = setupGenericCityObjectPolygons(gco);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupGenericCityObjectPolygons));
render(setupGenericCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(gco);
errorUpdater.run();
addGenericAttributesToView(gco);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupGenericCityObjectPolygons(GenericCityObject gco) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(gco, polygons);
for (BoundarySurface bs : gco.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(BoundarySurface bs) {
refresher = () -> {
Set<ConcretePolygon> setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBoundarySurfacePolygons));
render(setupBoundarySurfacePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bs);
errorUpdater.run();
addGenericAttributesToView(bs);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBoundarySurfacePolygons(BoundarySurface bs) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
return polygons;
}
private Set<ConcretePolygon> setupCityObjectPolygons(CityObject co) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(co, polygons);
return polygons;
} }
private void addPolygons(CityObject co, Set<ConcretePolygon> polygons) { private void addPolygons(CityObject co, Set<ConcretePolygon> polygons) {
...@@ -650,7 +248,7 @@ public class Renderer { ...@@ -650,7 +248,7 @@ public class Renderer {
if (geom.getEdges() == null && currentGeometry != null) { if (geom.getEdges() == null && currentGeometry != null) {
// if there are no edges available low memory mode is enabled // if there are no edges available low memory mode is enabled
// clear the old geometry of all meta information // clear the old geometry of all meta information
currentGeometry.clearMetaInformation(); currentGeometry.clearMetaData();
} }
errVisitor.setGeometry(currentTriGeom); errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> { Platform.runLater(() -> {
...@@ -765,36 +363,7 @@ public class Renderer { ...@@ -765,36 +363,7 @@ public class Renderer {
} }
public void renderBuildings(List<Building> objects) { public void renderBuildings(List<Building> objects) {
errorUpdater = null; renderCityObjects(objects, null);
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (Building b : objects) {
collectBuildingPolygons(polygons, b);
for (BuildingPart bp : b.getBuildingParts()) {
collectBuildingPolygons(polygons, bp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
} }
public void render(CityDoctorModel model) { public void render(CityDoctorModel model) {
...@@ -827,41 +396,12 @@ public class Renderer { ...@@ -827,41 +396,12 @@ public class Renderer {
renderCityObjects(vegetation, Color.LIGHTGREEN); renderCityObjects(vegetation, Color.LIGHTGREEN);
} }
public void renderTransportation(List<TransportationObject> transportation) { public void renderTransportation(List<? extends TransportationObject> transportation) {
renderCityObjects(transportation, Color.YELLOW); renderCityObjects(transportation, Color.YELLOW);
} }
public void renderBridges(List<BridgeObject> bridges) { public void renderBridges(List<BridgeObject> bridges) {
errorUpdater = null; renderCityObjects(bridges, Color.CORAL);
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (BridgeObject b : bridges) {
collectBridgePolygons(polygons, b);
for (BridgeObject bp : b.getParts()) {
collectBridgePolygons(polygons, bp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons, Color.CORAL);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
} }
public void renderWater(List<WaterObject> water) { public void renderWater(List<WaterObject> water) {
...@@ -869,72 +409,13 @@ public class Renderer { ...@@ -869,72 +409,13 @@ public class Renderer {
} }
public void renderCityFurniture(List<CityFurniture> cityFurniture) { public void renderCityFurniture(List<CityFurniture> cityFurniture) {
errorUpdater = null; renderCityObjects(cityFurniture, Color.BLUEVIOLET);
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityFurniture cf : cityFurniture) {
collectCityFurniture(polygons, cf);
}
currentTriGeom = TriangulatedGeometry.of(polygons, Color.BLUEVIOLET);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
} }
public void renderTerrain(List<CityObject> land) { public void renderTerrain(List<CityObject> land) {
renderLandObjects(land, Color.BROWN); renderCityObjects(land, Color.BROWN);
} }
private void renderLandObjects(List<CityObject> cos, Color baseColor) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) {
addPolygons(co, polygons);
if (co instanceof ReliefObject) {
ReliefObject relief = (ReliefObject) co;
for (TinObject tin : relief.getComponents()) {
addPolygons(tin, polygons);
}
}
}
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide()));
t.start();
};
refresher.run();
}
public void renderCityObjects(List<? extends CityObject> cos, Color baseColor) { public void renderCityObjects(List<? extends CityObject> cos, Color baseColor) {
errorUpdater = null; errorUpdater = null;
...@@ -946,10 +427,14 @@ public class Renderer { ...@@ -946,10 +427,14 @@ public class Renderer {
}); });
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>(); Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) { CityObjectCollector collector = new CityObjectCollector();
addPolygons(co, polygons); cos.forEach(cityObject -> cityObject.accept(collector));
collector.getCityObjects().forEach(cityObject -> addPolygons(cityObject, polygons));
if (baseColor != null) {
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
} else {
currentTriGeom = TriangulatedGeometry.of(polygons);
} }
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
errVisitor.setGeometry(currentTriGeom); errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> { Platform.runLater(() -> {
setupRenderState(); setupRenderState();
...@@ -964,129 +449,9 @@ public class Renderer { ...@@ -964,129 +449,9 @@ public class Renderer {
} }
public void renderTunnels(List<Tunnel> objects) { public void renderTunnels(List<Tunnel> objects) {
errorUpdater = null; renderCityObjects(objects, Color.DARKGRAY);
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (Tunnel tunnel : objects) {
collectTunnelPolygons(polygons, tunnel);
for (TunnelPart tp : tunnel.getTunnelParts()) {
collectTunnelPolygons(polygons, tp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
} }
private void collectBuildingPolygons(Set<ConcretePolygon> polygons, AbstractBuilding ab) {
addPolygons(ab, polygons);
for (Installation bi : ab.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (BuildingRoom br : ab.getBuildingRooms()) {
addPolygons(br, polygons);
for (BoundarySurface bs : br.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BoundarySurface bs : ab.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
private void collectBridgePolygons(Set<ConcretePolygon> polygons, BridgeObject br) {
addPolygons(br, polygons);
for (Installation bi : br.getBridgeInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (BridgeRoom r : br.getBridgeRooms()) {
addPolygons(r, polygons);
for (BoundarySurface bs : r.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BridgeConstructiveElement bce : br.getConstructiveElements()) {
addPolygons(bce, polygons);
for (BoundarySurface bs : bce.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BoundarySurface bs : br.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
public void collectTunnelPolygons(Set<ConcretePolygon> polygons, AbstractTunnel at) {
addPolygons(at, polygons);
for (Installation ti : at.getTunnelInstallations()) {
addPolygons(ti, polygons);
for (BoundarySurface bs : ti.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (TunnelHollow th : at.getTunnelHollows()) {
addPolygons(th, polygons);
for (BoundarySurface bs : th.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (TunnelConstructiveElement tce : at.getTunnelConstructiveElements()) {
addPolygons(tce, polygons);
for (BoundarySurface bs : tce.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BoundarySurface bs : at.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
private void collectCityFurniture(Set<ConcretePolygon> polygons, CityFurniture cf) {
addPolygons(cf, polygons);
}
private void addConcretePolygons(Set<ConcretePolygon> polygons, Geometry geom) { private void addConcretePolygons(Set<ConcretePolygon> polygons, Geometry geom) {
for (Polygon p : geom.getPolygons()) { for (Polygon p : geom.getPolygons()) {
...@@ -1206,5 +571,4 @@ public class Renderer { ...@@ -1206,5 +571,4 @@ public class Renderer {
errorUpdater = null; errorUpdater = null;
refresher = null; refresher = null;
} }
} }
...@@ -6,6 +6,7 @@ import java.io.File; ...@@ -6,6 +6,7 @@ import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Properties; import java.util.Properties;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
...@@ -22,6 +23,7 @@ public class Settings { ...@@ -22,6 +23,7 @@ public class Settings {
public static final String FRAME_X = "frameX"; public static final String FRAME_X = "frameX";
public static final String FRAME_Y = "frameY"; public static final String FRAME_Y = "frameY";
public static final String LANGUAGE = "language"; public static final String LANGUAGE = "language";
public static final String CONFIG = "config";
private static Properties props; private static Properties props;
...@@ -30,14 +32,14 @@ public class Settings { ...@@ -30,14 +32,14 @@ public class Settings {
File propFile = new File("GUISettings.properties"); File propFile = new File("GUISettings.properties");
if (propFile.exists()) { if (propFile.exists()) {
try (BufferedReader bis = new BufferedReader(new FileReader(propFile))) { try (BufferedReader bis = new BufferedReader(new FileReader(propFile, StandardCharsets.UTF_8))) {
props.load(bis); props.load(bis);
} catch (IOException e) { } catch (IOException e) {
logger.error("Failed to load settings", e); logger.error("Failed to load settings", e);
} }
} }
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(propFile))) { try (BufferedWriter bw = new BufferedWriter(new FileWriter(propFile, StandardCharsets.UTF_8))) {
props.store(bw, "GUI configuration"); props.store(bw, "GUI configuration");
} catch (IOException e) { } catch (IOException e) {
logger.error("Failed to save settings", e); logger.error("Failed to save settings", e);
......
package de.hft.stuttgart.citydoctor2.gui; package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter; import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter;
...@@ -12,6 +13,7 @@ import javafx.scene.paint.PhongMaterial; ...@@ -12,6 +13,7 @@ import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.*; import javafx.scene.shape.*;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
public class TriangulatedGeometry { public class TriangulatedGeometry {
...@@ -75,78 +77,33 @@ public class TriangulatedGeometry { ...@@ -75,78 +77,33 @@ public class TriangulatedGeometry {
public static TriangulatedGeometry of(CityDoctorModel model, List<ViewFilter> filters) { public static TriangulatedGeometry of(CityDoctorModel model, List<ViewFilter> filters) {
List<Vector3d> points = new ArrayList<>(); List<Vector3d> points = new ArrayList<>();
addPointsFromBuildings(model.getBuildings(), points); addPointsFromCityObject(model.createFeatureStream().toList(), points);
addPointsFromCityObject(model.getBridges(), points);
addPointsFromCityObject(model.getLand(), points);
addPointsFromCityObject(model.getTransportation(), points);
addPointsFromCityObject(model.getVegetation(), points);
addPointsFromCityObject(model.getWater(), points);
TriangulatedGeometry triGeom = new TriangulatedGeometry(); TriangulatedGeometry triGeom = new TriangulatedGeometry();
triGeom.materials = new ArrayList<>(); triGeom.materials = new ArrayList<>();
triGeom.meshes = new ArrayList<>(); triGeom.meshes = new ArrayList<>();
triGeom.movedBy = triGeom.findCenter(points); triGeom.movedBy = triGeom.findCenter(points);
addPolygonDataFromBuildings(model.getBuildings(), triGeom, filters); addPolygonDataFromCityObjects(model.getBuildings(), triGeom, Color.WHITE, filters);
addPolygonDataFromBridges(model.getBridges(), triGeom, filters); addPolygonDataFromCityObjects(model.getBridges(), triGeom, Color.LIGHTSTEELBLUE, filters);
addPolygonDataFromCityObjects(model.getLand(), triGeom, Color.BROWN, filters); addPolygonDataFromCityObjects(model.getTunnels(), triGeom, Color.SLATEGRAY, filters);
addPolygonDataFromCityObjects(model.getTransportation(), triGeom, Color.YELLOW, filters); addPolygonDataFromCityObjects(model.getLand(), triGeom, Color.TAN, filters);
addPolygonDataFromCityObjects(model.getTransportation(), triGeom, Color.DIMGRAY, filters);
addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters); addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters);
addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters); addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters);
addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters); addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters);
addPolygonDataFromCityObjects(model.getGenericCityObjects(), triGeom, Color.DARKGRAY, filters); addPolygonDataFromCityObjects(model.getOtherCityObjects(), triGeom, Color.BLACK, filters);
return triGeom; return triGeom;
} }
private static void addPolygonDataFromBuildings(List<Building> buildings, TriangulatedGeometry triGeom, List<ViewFilter> filters) {
for (Building b : buildings) {
addPolygonData(b, triGeom, Color.WHITE, filters);
addPolygonDataFromBoundarySurfaces(b.getBoundarySurfaces(), triGeom, filters);
addPolygonDataFromCityObjects(b.getBuildingInstallations(), triGeom, Color.WHITE, filters);
for (Installation bi : b.getBuildingInstallations()) {
addPolygonDataFromCityObjects(bi.getBoundarySurfaces(), triGeom, Color.WHITE, filters);
}
for (BuildingPart bp : b.getBuildingParts()) {
addPolygonData(bp, triGeom, Color.WHITE, filters);
addPolygonDataFromBoundarySurfaces(bp.getBoundarySurfaces(), triGeom, filters);
addPolygonDataFromCityObjects(bp.getBuildingInstallations(), triGeom, Color.WHITE, filters);
}
}
}
private static void addPolygonDataFromBridges(List<BridgeObject> bridges, TriangulatedGeometry triGeom, List<ViewFilter> filters) {
for (BridgeObject b : bridges) {
addPolygonData(b, triGeom, Color.CORAL, filters);
addPolygonDataFromBoundarySurfaces(b.getBoundarySurfaces(), triGeom, filters);
addPolygonDataFromCityObjects(b.getBridgeInstallations(), triGeom, Color.CORAL, filters);
for (Installation bi : b.getBridgeInstallations()) {
addPolygonData(bi, triGeom, Color.CORAL, filters);
addPolygonDataFromCityObjects(bi.getBoundarySurfaces(), triGeom, Color.CORAL, filters);
}
for (BridgeConstructiveElement bce : b.getConstructiveElements()) {
addPolygonData(bce, triGeom, Color.CORAL, filters);
addPolygonDataFromCityObjects(bce.getBoundarySurfaces(), triGeom, Color.CORAL, filters);
}
addPolygonDataFromBridges(b.getParts(), triGeom, filters);
}
}
private static void addPolygonDataFromBoundarySurfaces(List<BoundarySurface> boundarySurfaces,
TriangulatedGeometry triGeom, List<ViewFilter> filters) {
for (BoundarySurface bs : boundarySurfaces) {
addPolygonData(bs, triGeom, Color.WHITE, filters);
for (Opening o : bs.getOpenings()) {
addPolygonData(o, triGeom, Color.WHITE, filters);
}
}
}
private static void addPolygonDataFromCityObjects(List<? extends CityObject> cos, private static void addPolygonDataFromCityObjects(Stream<? extends CityObject> cos,
TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) { TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
for (CityObject co : cos) { CityObjectCollector collector = new CityObjectCollector();
addPolygonData(co, triGeom, color, filters); cos.forEach(cityObject -> cityObject.accept(collector));
} collector.getCityObjects().forEach(cityObject -> addPolygonData(cityObject, triGeom, color, filters));
} }
private static void addPolygonData(CityObject co, TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) { private static void addPolygonData(CityObject co, TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
...@@ -174,23 +131,10 @@ public class TriangulatedGeometry { ...@@ -174,23 +131,10 @@ public class TriangulatedGeometry {
return true; return true;
} }
private static void addPointsFromBuildings(List<Building> buildings, List<Vector3d> points) {
for (Building b : buildings) {
addPoints(b, points);
for (BuildingPart bp : b.getBuildingParts()) {
addPoints(bp, points);
addPointsFromCityObject(bp.getBoundarySurfaces(), points);
addPointsFromCityObject(bp.getBuildingInstallations(), points);
}
addPointsFromCityObject(b.getBoundarySurfaces(), points);
addPointsFromCityObject(b.getBuildingInstallations(), points);
}
}
private static void addPointsFromCityObject(List<? extends CityObject> cos, List<Vector3d> points) { private static void addPointsFromCityObject(List<? extends CityObject> cos, List<Vector3d> points) {
for (CityObject co : cos) { CityObjectCollector collector = new CityObjectCollector();
addPoints(co, points); cos.forEach(cityObject -> cityObject.accept(collector));
} collector.getCityObjects().forEach(cityObject -> addPoints(cityObject, points));
} }
private static void addPoints(CityObject co, List<Vector3d> points) { private static void addPoints(CityObject co, List<Vector3d> points) {
...@@ -224,16 +168,9 @@ public class TriangulatedGeometry { ...@@ -224,16 +168,9 @@ public class TriangulatedGeometry {
private PhongMaterial calculateMaterial(Polygon p, Color baseColor) { private PhongMaterial calculateMaterial(Polygon p, Color baseColor) {
Vector3d normal = p.calculateNormalNormalized(); Vector3d normal = p.calculateNormalNormalized();
BoundarySurface bs = p.getPartOfSurface(); if (p.getRenderColor() != null && p.getRenderColor() != Color.WHITE) {
if (bs != null) { baseColor = p.getRenderColor();
if (bs.getType() == BoundarySurfaceType.ROOF) {
baseColor = Color.RED;
} else if (bs.getType() == BoundarySurfaceType.GROUND) {
baseColor = Color.KHAKI;
}
} }
baseColor = determineColorDependingOnParentType(p, baseColor);
double cos = normal.dot(AXIS); double cos = normal.dot(AXIS);
double acos = Math.acos(cos); double acos = Math.acos(cos);
...@@ -245,28 +182,6 @@ public class TriangulatedGeometry { ...@@ -245,28 +182,6 @@ public class TriangulatedGeometry {
return new PhongMaterial(derivedColor); return new PhongMaterial(derivedColor);
} }
private Color determineColorDependingOnParentType(Polygon p, Color baseColor) {
p = p.getOriginal();
Polygon p1 = p.getLinkedFromPolygon();
baseColor = changeBaseColorIfPolygonHasOpeningParent(p1, baseColor);
baseColor = changeBaseColorIfPolygonHasOpeningParent(p, baseColor);
return baseColor;
}
private Color changeBaseColorIfPolygonHasOpeningParent(Polygon p, Color baseColor) {
if (p == null) {
return baseColor;
}
CityObject parent = p.getParent().getParent();
if (parent instanceof Opening op) {
if (op.getType() == OpeningType.DOOR) {
baseColor = Color.ORANGE;
} else {
baseColor = Color.TEAL;
}
}
return baseColor;
}
private Vector3d findCenter(List<Vector3d> points) { private Vector3d findCenter(List<Vector3d> points) {
double xMin = Double.MAX_VALUE; double xMin = Double.MAX_VALUE;
......
package de.hft.stuttgart.citydoctor2.gui; package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.locationtech.proj4j.ProjCoordinate; import org.locationtech.proj4j.ProjCoordinate;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import javafx.scene.control.ContextMenu; import javafx.scene.control.ContextMenu;
...@@ -15,17 +17,21 @@ import javafx.scene.input.MouseButton; ...@@ -15,17 +17,21 @@ import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.stage.Stage; import javafx.stage.Stage;
import static javafx.scene.input.Clipboard.getSystemClipboard;
public class VertexClickHandler implements ClickHandler { public class VertexClickHandler implements ClickHandler {
private TreeView<Renderable> errorView; private TreeView<Renderable> errorView;
private Renderer renderer; private Renderer renderer;
private Stage stage; private Stage stage;
private MainWindow mainWindow;
private ParserConfiguration config; private ParserConfiguration config;
public VertexClickHandler(TreeView<Renderable> errorView, Renderer renderer, Stage stage) { public VertexClickHandler(TreeView<Renderable> errorView, Renderer renderer, Stage stage, MainWindow mainWindow) {
this.errorView = errorView; this.errorView = errorView;
this.renderer = renderer; this.renderer = renderer;
this.stage = stage; this.stage = stage;
this.mainWindow = mainWindow;
} }
public void setConfig(ParserConfiguration config) { public void setConfig(ParserConfiguration config) {
...@@ -38,8 +44,26 @@ public class VertexClickHandler implements ClickHandler { ...@@ -38,8 +44,26 @@ public class VertexClickHandler implements ClickHandler {
errorView.getSelectionModel().clearSelection(); errorView.getSelectionModel().clearSelection();
renderer.highlight(p); renderer.highlight(p);
} else if (me.getButton() == MouseButton.SECONDARY) { } else if (me.getButton() == MouseButton.SECONDARY) {
MenuItem mi = new MenuItem(p.getGmlId().getGmlString()); ContextMenu cMenu = new ContextMenu();
ContextMenu cMenu = new ContextMenu(mi);
MenuItem clipMi = new MenuItem(Localization.getText("MainWindow.copyId"));
clipMi.setOnAction(ea -> {
CityObject topLevelCityObject = p.getParent().getParent().getTopLevelCityObject();
Clipboard clipboard = getSystemClipboard();
ClipboardContent content = new ClipboardContent();
content.putString(topLevelCityObject.getGmlId().toString());
clipboard.setContent(content);
});
cMenu.getItems().add(clipMi);
MenuItem detailMi = new MenuItem(Localization.getText("MainWindow.focusCityObject"));
detailMi.setOnAction(ea -> {
CityObject topLevelCityObject = p.getParent().getParent().getTopLevelCityObject();
mainWindow.showCityObjectDetail(topLevelCityObject);
});
cMenu.getItems().add(detailMi);
cMenu.show(stage, me.getScreenX(), me.getScreenY()); cMenu.show(stage, me.getScreenX(), me.getScreenY());
} }
} }
......
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.gui.CityDoctorController;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AddMoreButtonRunnable implements Runnable{
private int featureCount;
private final TreeView<Renderable> view;
private final TreeItem<Renderable> root;
private final AtomicInteger chunkCounter;
private static final int MAX_FEATURES_PER_CHUNK = CityDoctorController.getMaxFeaturesPerChunk();
protected AddMoreButtonRunnable(int featureCount, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter){
this.featureCount = featureCount;
this.view = view;
this.root = root;
this.chunkCounter = chunkCounter;
}
@Override
public void run() {
// 29 is the height of one item
int numberOfDisplayedItems = (int) (view.getHeight() / 29);
int selectedIndex = view.getSelectionModel().getSelectedIndex();
// remove button node
root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.getAndIncrement();
int visibleFeatures = countVisibleNodes(view);
buildNodeTreeCall();
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
/**
* Function hook for the buildTree call of the view
*/
public abstract void buildNodeTreeCall();
private static int countVisibleNodes(TreeView<Renderable> view) {
return countVisibleNodes(view.getRoot()) + 1;
}
private static int countVisibleNodes(TreeItem<Renderable> root) {
int visibleFeatures = root.getChildren().size();
for (TreeItem<Renderable> child : root.getChildren()) {
if (!child.getChildren().isEmpty() && child.isExpanded()) {
visibleFeatures += countVisibleNodes(child);
}
}
return visibleFeatures;
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllBoundarySurfacesNode extends Renderable {
private final List<BoundarySurface> boundarySurfaces;
public AllBoundarySurfacesNode(List<BoundarySurface> boundarySurfaces) {
this.boundarySurfaces = boundarySurfaces;
}
@Override
public String getText() {
return "Boundary Surfaces";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean isValidated = false;
for (BoundarySurface bs : boundarySurfaces) {
if (bs.isValidated()) {
isValidated = true;
}
if (bs.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
if (isValidated) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
/*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllBridgeConstructiveElementsNode extends Renderable {
private final List<BridgeConstructiveElement> constructiveElements;
public AllBridgeConstructiveElementsNode(List<BridgeConstructiveElement> constructiveElements) {
this.constructiveElements = constructiveElements;
}
@Override
public String getText() {
return "Constructive Elements";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean isValidated = false;
for (BridgeConstructiveElement bce : constructiveElements) {
if (bce.isValidated()) {
isValidated = true;
}
if (bce.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
if (isValidated) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
/*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllBridgePartsNode extends Renderable {
private final List<BridgeObject> bridgeParts;
public AllBridgePartsNode(List<BridgeObject> bridgeParts) {
this.bridgeParts = bridgeParts;
}
@Override
public String getText() {
return "Bridge Parts";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (BridgeObject bp : bridgeParts) {
if (bp.isValidated()) {
wasChecked = true;
if (bp.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllBridgesNode extends Renderable {
private final List<BridgeObject> bridges;
public AllBridgesNode(List<BridgeObject> bridges) {
this.bridges = bridges;
}
@Override
public void refreshTextColor() {
// no color changes
}
@Override
public String getText() {
return "Bridges";
}
@Override
public void visit(Renderer renderer) {
renderer.renderBridges(bridges);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllBuildingPartsNode extends Renderable {
private final List<BuildingPart> buildingParts;
public AllBuildingPartsNode(List<BuildingPart> buildingParts) {
this.buildingParts = buildingParts;
}
@Override
public String getText() {
return "Building Parts";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (BuildingPart bp : buildingParts) {
if (bp.isValidated()) {
wasChecked = true;
if (bp.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import java.util.List;
public class AllBuildingUnitsNode extends Renderable{
private final List<BuildingUnit> buildingUnits;
public AllBuildingUnitsNode(List<BuildingUnit> buildingUnits) {
this.buildingUnits = buildingUnits;
}
@Override
public String getText() {
return "Building Units";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (BuildingUnit buildingUnit : buildingUnits) {
if (buildingUnit.isValidated()) {
wasChecked = true;
if (buildingUnit.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllBuildingsNode extends Renderable {
private final List<Building> buildings;
public AllBuildingsNode(List<Building> buildings) {
this.buildings = buildings;
}
@Override
public void refreshTextColor() {
// no color changes
}
@Override
public String getText() {
return "Buildings";
}
@Override
public void visit(Renderer renderer) {
renderer.renderBuildings(buildings);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.CityFurniture;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import java.util.List;
public class AllCityFurnitureNode extends Renderable{
private final List<CityFurniture> cityFurnitures;
public AllCityFurnitureNode(List<CityFurniture> cityFurnitures) {
this.cityFurnitures = cityFurnitures;
}
@Override
public void refreshTextColor() {
// no color changes
}
@Override
public String getText() {
return "CityFurniture";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
renderer.renderCityFurniture(cityFurnitures);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractFurniture;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import java.util.List;
public class AllFurnitureNode extends Renderable{
private final List<? extends AbstractFurniture> furniture;
public AllFurnitureNode(List<? extends AbstractFurniture> furniture) {
this.furniture = furniture;
}
@Override
public String getText() {
return "Furniture";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (AbstractFurniture af : furniture) {
if (af.isValidated()) {
wasChecked = true;
if (af.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllInstallationsNode extends Renderable {
private final List<Installation> buildingInstallations;
public AllInstallationsNode(List<Installation> bis) {
this.buildingInstallations = bis;
}
@Override
public String getText() {
return "Building Installations";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (Installation bp : buildingInstallations) {
if (bp.isValidated()) {
wasChecked = true;
if (bp.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
\ No newline at end of file
package de.hft.stuttgart.citydoctor2.gui.tree;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
public class AllOpeningsNode extends Renderable {
private final List<Opening> openings;
public AllOpeningsNode(List<Opening> openings) {
this.openings = openings;
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (Opening op : openings) {
if (op.isValidated()) {
wasChecked = true;
if (op.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
@Override
public String getText() {
return "Openings";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
}
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