Commit a5a82382 authored by Riegel's avatar Riegel
Browse files

Open Source release of CityDoctor-GUI

parent 7a22ca9c
Pipeline #10028 failed with stage
in 7 seconds
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.ExcludeFilterConfiguration;
import de.hft.stuttgart.citydoctor2.check.FilterConfiguration;
import de.hft.stuttgart.citydoctor2.check.IncludeFilterConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
import de.hft.stuttgart.citydoctor2.gui.filter.TypeFilterSelection;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
public class FilterPane {
@FXML
private VBox inclFeatureBox;
@FXML
private ImageView addInclFeatureView;
@FXML
private VBox inclMatchBox;
@FXML
private ImageView addInclMatchView;
@FXML
private Button addInclFeatureBtn;
@FXML
private Button addInclMatchBtn;
@FXML
private VBox exclFeatureBox;
@FXML
private ImageView addExclFeatureView;
@FXML
private VBox exclMatchBox;
@FXML
private ImageView addExclMatchView;
@FXML
private Button addExclFeatureBtn;
@FXML
private Button addExclMatchBtn;
private ScrollPane pane;
private Image deleteImg;
private List<ComboBox<TypeFilterSelection>> includeTypeBoxes = new ArrayList<>();
private List<TextField> includeMatcherFields = new ArrayList<>();
private List<TextField> excludeMatcherFields = new ArrayList<>();
private List<ComboBox<TypeFilterSelection>> excludeTypeBoxes = new ArrayList<>();
private List<TypeFilterSelection> availableFilters;
public FilterPane() throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(FilterPane.class.getResource("FilterPane.fxml"));
fxmlLoader.setController(this);
pane = fxmlLoader.load();
}
public void initialize() {
loadImages();
availableFilters = new ArrayList<>();
availableFilters.add(new TypeFilterSelection(FeatureType.BUILDING, Localization.getText("FilterPane.buildings")));
availableFilters.add(new TypeFilterSelection(FeatureType.BRIDGE, Localization.getText("FilterPane.bridges")));
availableFilters.add(new TypeFilterSelection(FeatureType.LAND, Localization.getText("FilterPane.landUse")));
availableFilters.add(new TypeFilterSelection(FeatureType.TRANSPORTATION, Localization.getText("FilterPane.transportation")));
availableFilters.add(new TypeFilterSelection(FeatureType.VEGETATION, Localization.getText("FilterPane.vegetation")));
availableFilters.add(new TypeFilterSelection(FeatureType.WATER, Localization.getText("FilterPane.water")));
setupIncludeFeatureButton();
setupIncludeMatchButton();
setupExcludeFeatureButton();
setupExcludeMatchButton();
}
private void setupExcludeMatchButton() {
addExclMatchBtn.setOnAction(ae -> addExcludeMatchField());
}
private TextField addExcludeMatchField() {
TextField tf = new TextField();
tf.setPrefWidth(400);
excludeMatcherFields.add(tf);
HBox box = new HBox(10);
box.getChildren().add(tf);
ImageView imgView = new ImageView(deleteImg);
imgView.setFitHeight(25);
imgView.setFitWidth(25);
box.getChildren().add(imgView);
imgView.setOnMouseClicked(me2 -> {
excludeMatcherFields.remove(tf);
exclMatchBox.getChildren().remove(box);
});
exclMatchBox.getChildren().add(box);
return tf;
}
private void setupExcludeFeatureButton() {
addExclFeatureBtn.setOnAction(ae -> addExcludeTypeFilterBox());
}
private ComboBox<TypeFilterSelection> addExcludeTypeFilterBox() {
ComboBox<TypeFilterSelection> cBox = new ComboBox<>();
cBox.setPrefWidth(400);
cBox.getItems().addAll(availableFilters);
cBox.getSelectionModel().select(0);
excludeTypeBoxes.add(cBox);
HBox box = new HBox(10);
box.getChildren().add(cBox);
ImageView imgView = new ImageView(deleteImg);
imgView.setFitHeight(25);
imgView.setFitWidth(25);
box.getChildren().add(imgView);
imgView.setOnMouseClicked(me2 -> {
excludeTypeBoxes.remove(cBox);
exclFeatureBox.getChildren().remove(box);
});
exclFeatureBox.getChildren().add(box);
return cBox;
}
private void setupIncludeMatchButton() {
addInclMatchBtn.setOnAction(ae -> addIncludeMatchField());
}
private TextField addIncludeMatchField() {
TextField tf = new TextField();
tf.setPrefWidth(400);
includeMatcherFields.add(tf);
HBox box = new HBox(10);
box.getChildren().add(tf);
ImageView imgView = new ImageView(deleteImg);
imgView.setFitHeight(25);
imgView.setFitWidth(25);
box.getChildren().add(imgView);
imgView.setOnMouseClicked(me2 -> {
includeMatcherFields.remove(tf);
inclMatchBox.getChildren().remove(box);
});
inclMatchBox.getChildren().add(box);
return tf;
}
public void applyFilterConfig(FilterConfiguration filter) {
includeMatcherFields.clear();
inclMatchBox.getChildren().clear();
includeTypeBoxes.clear();
inclFeatureBox.getChildren().clear();
excludeMatcherFields.clear();
exclMatchBox.getChildren().clear();
excludeTypeBoxes.clear();
exclFeatureBox.getChildren().clear();
applyExcludeFilters(filter);
applyIncludeFilters(filter);
}
private void applyIncludeFilters(FilterConfiguration filter) {
IncludeFilterConfiguration include = filter.getInclude();
if (include != null) {
for (String s : include.getIds()) {
addIncludeMatchField().setText(s);
}
for (FeatureType type : include.getTypes()) {
ComboBox<TypeFilterSelection> box = addIncludeTypeFilterBox();
for (TypeFilterSelection tfs : box.getItems()) {
if (tfs.getType() == type) {
box.getSelectionModel().select(tfs);
}
}
}
}
}
private void applyExcludeFilters(FilterConfiguration filter) {
ExcludeFilterConfiguration exclude = filter.getExclude();
if (exclude != null) {
for (String s : exclude.getIds()) {
addExcludeMatchField().setText(s);
}
for (FeatureType type : exclude.getTypes()) {
ComboBox<TypeFilterSelection> box = addExcludeTypeFilterBox();
for (TypeFilterSelection tfs : box.getItems()) {
if (tfs.getType() == type) {
box.getSelectionModel().select(tfs);
}
}
}
}
}
private void setupIncludeFeatureButton() {
addInclFeatureBtn.setOnAction(ae -> addIncludeTypeFilterBox());
}
private ComboBox<TypeFilterSelection> addIncludeTypeFilterBox() {
ComboBox<TypeFilterSelection> cBox = new ComboBox<>();
cBox.setPrefWidth(400);
cBox.getItems().addAll(availableFilters);
cBox.getSelectionModel().select(0);
includeTypeBoxes.add(cBox);
HBox box = new HBox(10);
box.getChildren().add(cBox);
ImageView imgView = new ImageView(deleteImg);
imgView.setFitHeight(25);
imgView.setFitWidth(25);
box.getChildren().add(imgView);
imgView.setOnMouseClicked(me2 -> {
includeTypeBoxes.remove(cBox);
inclFeatureBox.getChildren().remove(box);
});
inclFeatureBox.getChildren().add(box);
return cBox;
}
public FilterConfiguration getConfiguration() {
IncludeFilterConfiguration includeFilters = new IncludeFilterConfiguration();
List<FeatureType> featureTypes = new ArrayList<>();
for (ComboBox<TypeFilterSelection> box : includeTypeBoxes) {
featureTypes.add(box.getSelectionModel().getSelectedItem().getType());
}
includeFilters.setTypes(featureTypes);
List<String> ids = new ArrayList<>();
for (TextField tf : includeMatcherFields) {
ids.add(tf.getText());
}
includeFilters.setIds(ids);
ExcludeFilterConfiguration excludeFilters = new ExcludeFilterConfiguration();
List<FeatureType> excludeFeatureTypes = new ArrayList<>();
for (ComboBox<TypeFilterSelection> box : excludeTypeBoxes) {
excludeFeatureTypes.add(box.getSelectionModel().getSelectedItem().getType());
}
excludeFilters.setTypes(excludeFeatureTypes);
List<String> excludeIds = new ArrayList<>();
for (TextField tf : excludeMatcherFields) {
excludeIds.add(tf.getText());
}
excludeFilters.setIds(excludeIds);
FilterConfiguration fConfig = new FilterConfiguration();
fConfig.setInclude(includeFilters);
fConfig.setExclude(excludeFilters);
return fConfig;
}
private void loadImages() {
try {
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Add.png")) {
Image img = new Image(inStream);
addInclFeatureView.setImage(img);
addInclMatchView.setImage(img);
addExclFeatureView.setImage(img);
addExclMatchView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Delete.png")) {
deleteImg = new Image(inStream);
}
} catch (IOException e) {
// ignore close exception
}
}
public ScrollPane getPane() {
return pane;
}
}
/*-
* Copyright 2020 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;
import java.io.Serializable;
import de.hft.stuttgart.citydoctor2.check.Unit;
public class GlobalParameter implements Serializable {
private static final long serialVersionUID = 1423983452743345752L;
private String name;
private String value;
private Unit unit;
public GlobalParameter(String name, String value, Unit unit) {
super();
this.name = name;
this.value = value;
this.unit = unit;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((unit == null) ? 0 : unit.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GlobalParameter other = (GlobalParameter) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (unit != other.unit)
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
@Override
public String toString() {
return "GlobalParameter [name=" + name + ", value=" + value + ", unit=" + unit + "]";
}
public Unit getUnit() {
return unit;
}
public void setUnit(Unit unit) {
this.unit = unit;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import javafx.application.Platform;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
public class HighlightController {
private double scale;
private Group points;
private Group edges;
private Group highlights;
public HighlightController(Group world) {
highlights = new Group();
points = new Group();
edges = new Group();
highlights.getChildren().add(edges);
highlights.getChildren().add(points);
Platform.runLater(() -> world.getChildren().add(highlights));
}
public void clearHighlights() {
points.getChildren().clear();
edges.getChildren().clear();
}
public void changeScaling(double translateZ) {
scale = Math.abs(translateZ);
scale = Math.min(150, scale);
scale = Math.max(10, scale);
scale = scale * 0.01;
for (Node n : points.getChildren()) {
n.setScaleX(scale);
n.setScaleY(scale);
n.setScaleZ(scale);
}
double edgeSize = scale / 10;
for (Node n : edges.getChildren()) {
if (n instanceof Cylinder) {
Cylinder cy = (Cylinder) n;
cy.setRadius(edgeSize);
}
}
}
public void highlight(Polygon p, TriangulatedGeometry currentTriGeom) {
clearHighlights();
addHighlight(p, currentTriGeom, Color.RED, Color.BLUE);
}
private void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom, Color extColor, Color intColor) {
if (currentTriGeom == null || p == null) {
return;
}
addHighlight(p.getExteriorRing(), currentTriGeom, extColor);
for (LinearRing intRing : p.getInnerRings()) {
addHighlight(intRing, currentTriGeom, intColor);
}
}
public void highlight(LinearRing ring, TriangulatedGeometry currentTriGeom) {
clearHighlights();
if (ring.getType() == LinearRingType.EXTERIOR) {
addHighlight(ring, currentTriGeom, Color.RED);
} else {
addHighlight(ring, currentTriGeom, Color.BLUE);
}
}
public void highlight(List<LinearRing> rings, TriangulatedGeometry currentTriGeom) {
clearHighlights();
for (LinearRing lr : rings) {
if (lr.getType() == LinearRingType.EXTERIOR) {
addHighlight(lr, currentTriGeom, Color.RED);
} else {
addHighlight(lr, currentTriGeom, Color.BLUE);
}
}
}
private void addHighlight(LinearRing ring, TriangulatedGeometry currentTriGeom, Color pointColor) {
if (currentTriGeom == null || ring == null) {
return;
}
Vector3d movedBy = currentTriGeom.getMovedBy();
for (Vertex v : ring.getVertices()) {
highlightPoint(movedBy, v, pointColor);
}
for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vertex v1 = ring.getVertices().get(i + 0);
Vertex v2 = ring.getVertices().get(i + 1);
highlightEdge(v1, v2, movedBy);
}
}
public void highlight(Edge e, TriangulatedGeometry currentTriGeom) {
clearHighlights();
addHighlight(e, currentTriGeom);
}
private void addHighlight(Edge e, TriangulatedGeometry currentTriGeom) {
if (currentTriGeom == null || e == null) {
return;
}
Vector3d movedBy = currentTriGeom.getMovedBy();
highlightEdge(e.getFrom(), e.getTo(), movedBy);
highlightPoint(movedBy, e.getFrom(), Color.RED);
highlightPoint(movedBy, e.getTo(), Color.RED);
}
public void highlight(Vertex v, TriangulatedGeometry currentTriGeom) {
clearHighlights();
addHighlight(v, currentTriGeom, Color.RED);
}
public void addHighlight(Vertex v, TriangulatedGeometry currentTriGeom, Color c) {
if (currentTriGeom == null || v == null) {
return;
}
Vector3d movedBy = currentTriGeom.getMovedBy();
highlightPoint(movedBy, v, c);
}
private void highlightPoint(Vector3d movedBy, Vertex v, Color color) {
Sphere sp = new Sphere(0.5);
sp.setMaterial(new PhongMaterial(color));
sp.setTranslateX(v.getX() - movedBy.getX());
sp.setTranslateY(v.getY() - movedBy.getY());
sp.setTranslateZ(v.getZ() - movedBy.getZ());
sp.setScaleX(scale);
sp.setScaleY(scale);
sp.setScaleZ(scale);
sp.setUserData(new VertexClickDispatcher(v));
points.getChildren().add(sp);
}
private void highlightEdge(Vector3d v1, Vector3d v2, Vector3d movedBy) {
Point3D origin = new Point3D(v1.getX() - movedBy.getX(), v1.getY() - movedBy.getY(),
v1.getZ() - movedBy.getZ());
Point3D target = new Point3D(v2.getX() - movedBy.getX(), v2.getY() - movedBy.getY(),
v2.getZ() - movedBy.getZ());
Point3D yAxis = new Point3D(0, 1, 0);
Point3D diff = target.subtract(origin);
double height = diff.magnitude();
Point3D mid = target.midpoint(origin);
Translate moveToMidpoint = new Translate(mid.getX(), mid.getY(), mid.getZ());
Point3D axisOfRotation = diff.crossProduct(yAxis);
double angle = Math.acos(diff.normalize().dotProduct(yAxis));
Rotate rotateAroundCenter = new Rotate(-Math.toDegrees(angle), axisOfRotation);
Cylinder cy = new Cylinder(scale / 10, height);
cy.setMaterial(new PhongMaterial(Color.ORANGE));
cy.getTransforms().addAll(moveToMidpoint, rotateAroundCenter);
edges.getChildren().add(cy);
}
public void highlightEdges(List<Edge> errorEdges, TriangulatedGeometry currentTriGeom) {
clearHighlights();
for (Edge e : errorEdges) {
addHighlight(e, currentTriGeom);
}
}
public void highlightPolygons(List<List<Polygon>> components, TriangulatedGeometry currentTriGeom) {
clearHighlights();
for (int i = 0; i < components.size(); i++) {
Color extColor;
Color intColor;
// select some color pairs for exterior and inner rings
switch (i % 3) {
case 1:
extColor = Color.GREEN;
intColor = Color.YELLOW;
break;
case 2:
extColor = Color.BROWN;
intColor = Color.VIOLET;
break;
default:
extColor = Color.RED;
intColor = Color.BLUE;
}
List<Polygon> component = components.get(i);
for (Polygon p : component) {
addHighlight(p, currentTriGeom, extColor, intColor);
}
}
}
public void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom) {
addHighlight(p, currentTriGeom, Color.RED, Color.BLUE);
}
public void highlight(Triangle3d t, TriangulatedGeometry currentTriGeom) {
highlightEdge(t.getP1(), t.getP2(), currentTriGeom.getMovedBy());
highlightEdge(t.getP2(), t.getP3(), currentTriGeom.getMovedBy());
highlightEdge(t.getP3(), t.getP1(), currentTriGeom.getMovedBy());
}
}
/*-
* Copyright 2020 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;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javafx.scene.control.ListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class LanguageSelectorCell extends ListCell<Locale> {
private static Map<String, Image> imageCache = new HashMap<>();
@Override
protected void updateItem(Locale item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
String country = item.getLanguage().toLowerCase(Locale.US);
Image iconRef = imageCache.computeIfAbsent(country, key -> {
String iconPath = "icons/icon_" + country + ".png";
return new Image(MainWindow.class.getResourceAsStream(iconPath));
});
ImageView iconImageView = new ImageView(iconRef);
setGraphic(iconImageView);
}
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.ErrorVisitor;
import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError;
import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError;
import de.hft.stuttgart.citydoctor2.check.error.NestedRingError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldVertexError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonNormalsDeviation;
import de.hft.stuttgart.citydoctor2.check.error.NotCeilingError;
import de.hft.stuttgart.citydoctor2.check.error.NotFloorError;
import de.hft.stuttgart.citydoctor2.check.error.NotGroundError;
import de.hft.stuttgart.citydoctor2.check.error.NotWallError;
import de.hft.stuttgart.citydoctor2.check.error.NullAreaError;
import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonSameOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import javafx.scene.paint.Color;
public class ListErrorVisitor implements ErrorVisitor {
private HighlightController controller;
private TriangulatedGeometry geom;
public ListErrorVisitor(HighlightController controller) {
this.controller = controller;
}
public void setGeometry(TriangulatedGeometry geom) {
this.geom = geom;
}
@Override
public void visit(PolygonHoleOutsideError err) {
List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.addAll(err.getHolesOutside());
controller.highlight(highlightedRings, geom);
}
@Override
public void visit(NonManifoldEdgeError err) {
controller.highlightEdges(err.getEdges(), geom);
}
@Override
public void visit(MultipleConnectedComponentsError err) {
controller.highlightPolygons(err.getComponents(), geom);
}
@Override
public void visit(NestedRingError err) {
List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.add(err.getInnerRing());
controller.highlight(highlightedRings, geom);
}
@Override
public void visit(NonManifoldVertexError err) {
controller.highlightPolygons(err.getComponents(), geom);
controller.addHighlight(err.getVertex(), geom, Color.BLUEVIOLET);
}
@Override
public void visit(PolygonWrongOrientationError err) {
controller.highlightEdges(err.getEdges(), geom);
}
@Override
public void visit(PolygonSameOrientationError err) {
List<LinearRing> highlightedRings = new ArrayList<>();
highlightedRings.add(err.getPolygon().getExteriorRing());
highlightedRings.add(err.getInnerRing());
controller.highlight(highlightedRings, geom);
}
@Override
public void visit(SolidNotClosedError err) {
controller.highlightEdges(err.getErrorEdges(), geom);
}
@Override
public void visit(DependenciesNotMetError err) {
// don't display
}
@Override
public void visit(UnknownCheckError err) {
// don't display
}
@Override
public void visit(RingNotClosedError err) {
controller.highlight(err.getRing(), geom);
}
@Override
public void visit(ConsecutivePointSameError err) {
controller.highlight(err.getRing(), geom);
controller.addHighlight(err.getVertex1(), geom, Color.BLACK);
controller.addHighlight(err.getVertex2(), geom, Color.BLACK);
}
@Override
public void visit(AllPolygonsWrongOrientationError err) {
// don't display
}
@Override
public void visit(PolygonInteriorDisconnectedError err) {
controller.highlight(err.getConnectedRings(), geom);
}
@Override
public void visit(NullAreaError err) {
// don't display
}
@Override
public void visit(RingTooFewPointsError err) {
controller.highlight(err.getRing(), geom);
}
@Override
public void visit(NonPlanarPolygonNormalsDeviation err) {
controller.highlight(err.getPolygon(), geom);
}
@Override
public void visit(NonPlanarPolygonDistancePlaneError err) {
controller.highlight(err.getPolygon(), geom);
controller.addHighlight(err.getVertex(), geom, Color.BLACK);
}
@Override
public void visit(PolygonIntersectingRingsError err) {
List<LinearRing> rings = new ArrayList<>();
rings.add(err.getIntersectingRings().getValue0());
rings.add(err.getIntersectingRings().getValue1());
controller.highlight(rings, geom);
}
@Override
public void visit(SolidSelfIntError err) {
// nothing to display for now
}
@Override
public void visit(TooFewPolygonsError err) {
// don't display
}
@Override
public void visit(RingDuplicatePointError err) {
controller.highlight(err.getRing(), geom);
controller.addHighlight(err.getVertex1(), geom, Color.BLACK);
controller.addHighlight(err.getVertex2(), geom, Color.BLACK);
}
@Override
public void visit(RingEdgeIntersectionError err) {
List<Edge> list = new ArrayList<>();
list.add(err.getEdge1());
list.add(err.getEdge2());
controller.highlightEdges(list, geom);
controller.addHighlight(new Vertex(err.getIntersection()), geom, Color.BLACK);
}
@Override
public void visit(PointTouchesEdgeError err) {
controller.highlight(err.getEdge(), geom);
controller.addHighlight(err.getVertex(), geom, Color.BLACK);
}
@Override
public void visit(CheckError err) {
// nothing to display
}
@Override
public void visit(NotCeilingError err) {
// nothing to display
}
@Override
public void visit(NotFloorError err) {
// nothing to display
}
@Override
public void visit(NotWallError err) {
// nothing to display
}
@Override
public void visit(NotGroundError err) {
// nothing to display
}
@Override
public void visit(SchematronError err) {
// nothing to display
}
@Override
public void visit(SurfaceUnfragmentedError err) {
// nothing to display
}
@Override
public void visit(DegeneratedRingError err) {
controller.highlight(err.getRing(), geom);
}
@Override
public void visit(AttributeMissingError err) {
// nothing to display
}
@Override
public void visit(AttributeValueWrongError err) {
// nothing to display
}
@Override
public void visit(AttributeInvalidError err) {
// nothing to display
}
@Override
public void visit(PolygonWithoutSurfaceError err) {
controller.highlight(err.getPolygon(), geom);
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
public class LoadingInfoDialog {
private Stage stage;
public LoadingInfoDialog(Window parent) throws IOException {
FXMLLoader loader = new FXMLLoader(LoadingInfoDialog.class.getResource("CreateRenderDataDialog.fxml"));
loader.setController(this);
VBox box = loader.load();
stage = new Stage(StageStyle.UNDECORATED);
Scene scene = new Scene(box);
scene.setFill(null);
stage.setScene(scene);
stage.initOwner(parent);
stage.initModality(Modality.APPLICATION_MODAL);
}
public void show() {
stage.show();
}
public void hide() {
stage.hide();
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import java.io.InputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MainToolBar {
private static final Logger logger = LogManager.getLogger(MainToolBar.class);
@FXML
private Button saveBtn;
@FXML
private ImageView saveView;
@FXML
private Button openBtn;
@FXML
private ImageView openImageView;
@FXML
private Button checkBtn;
@FXML
private ImageView checkImageView;
@FXML
private Button showWorldBtn;
@FXML
private ImageView showWorldImageView;
@FXML
private Button schematronBtn;
@FXML
private ImageView schematronImgView;
@FXML
private ToggleButton lod1Btn;
@FXML
private ImageView lod1View;
@FXML
private ToggleButton lod2Btn;
@FXML
private ImageView lod2View;
@FXML
private ToggleButton lod3Btn;
@FXML
private ImageView lod3View;
@FXML
private ToggleButton lod4Btn;
@FXML
private ImageView lod4View;
@FXML
private Button aboutBtn;
@FXML
private ImageView aboutImgView;
@FXML
private ToggleButton gridButton;
@FXML
private ToggleButton cullingButton;
@FXML
private ImageView cullingImageView;
@FXML
private ImageView gridImageView;
@FXML
private Button reportBtn;
@FXML
private ImageView reportImageView;
private OpenFileDialog fileDialog;
private CheckDialog checkDialog;
private WriteReportDialog writeDialog;
private AboutDialog aboutDialog;
private CityDoctorController controller;
private TabPane featurePane;
private Stage stage;
private Renderer renderer;
private MainWindow mainWindow;
private HBox toolBar;
public MainToolBar(Stage stage, CityDoctorController controller, TabPane featurePane, Renderer renderer,
MainWindow mainWindow) throws IOException {
this.controller = controller;
this.featurePane = featurePane;
this.renderer = renderer;
this.stage = stage;
this.mainWindow = mainWindow;
FXMLLoader loader = new FXMLLoader(MainToolBar.class.getResource("MainToolBar.fxml"));
loader.setController(this);
toolBar = loader.load();
fileDialog = new OpenFileDialog(stage, controller);
}
public void initialize() {
openBtn.setOnAction(ae -> fileDialog.show());
setupSaveBtn();
setupCheckButton();
setupLodButtons();
setupAboutButton();
setupReportButton();
loadImages();
gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected()));
gridButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.wireframe")));
cullingButton.setOnAction(ae -> renderer.enableCulling(cullingButton.isSelected()));
cullingButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.culling")));
showWorldBtn.setOnAction(ar -> {
Tab selectedItem = featurePane.getSelectionModel().getSelectedItem();
if (selectedItem.getContent() instanceof TreeView) {
@SuppressWarnings("unchecked")
TreeView<Renderable> content = (TreeView<Renderable>) selectedItem.getContent();
content.getSelectionModel().clearSelection();
}
controller.showWorld();
});
}
private void loadImages() {
try {
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/openFolderIcon.png")) {
Image img = new Image(inStream);
openImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/check32x32.png")) {
Image img = new Image(inStream);
checkImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/wireframe32x32.png")) {
Image img = new Image(inStream);
gridImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Culling.png")) {
Image img = new Image(inStream);
cullingImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) {
Image img = new Image(inStream);
reportImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/scene.png")) {
Image img = new Image(inStream);
showWorldImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod1_32x32.png")) {
Image img = new Image(inStream);
lod1View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod2_32x32.png")) {
Image img = new Image(inStream);
lod2View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod3_32x32.png")) {
Image img = new Image(inStream);
lod3View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod4_32x32.png")) {
Image img = new Image(inStream);
lod4View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/about.png")) {
Image img = new Image(inStream);
aboutImgView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/save.png")) {
Image img = new Image(inStream);
saveView.setImage(img);
}
} catch (IOException e) {
// ignore close exception
}
}
private void setupReportButton() {
reportBtn.setDisable(true);
reportBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.writeReports")));
reportBtn.setOnAction(ae -> {
if (writeDialog == null) {
try {
writeDialog = new WriteReportDialog(stage, controller, mainWindow);
} catch (IOException e) {
logger.catching(e);
mainWindow.showExceptionDialog(e);
}
}
if (writeDialog != null) {
// writeDialog can be null if creation of said dialog fails
writeDialog.show();
}
});
}
private void setupAboutButton() {
aboutBtn.setOnAction(ae -> {
if (aboutDialog == null) {
try {
aboutDialog = new AboutDialog(stage);
aboutDialog.show();
} catch (IOException e) {
logger.error("Could not load about dialog.", e);
}
} else {
aboutDialog.show();
}
});
}
private void setupLodButtons() {
lod1Btn.setOnAction(ae -> {
if (lod1Btn.isSelected()) {
renderer.enableLod1();
} else {
renderer.disableLod1();
}
});
lod2Btn.setOnAction(ae -> {
if (lod2Btn.isSelected()) {
renderer.enableLod2();
} else {
renderer.disableLod2();
}
});
lod3Btn.setOnAction(ae -> {
if (lod3Btn.isSelected()) {
renderer.enableLod3();
} else {
renderer.disableLod3();
}
});
lod4Btn.setOnAction(ae -> {
if (lod4Btn.isSelected()) {
renderer.enableLod4();
} else {
renderer.disableLod4();
}
});
}
private void setupSaveBtn() {
saveBtn.setOnAction(ae -> controller.askAndSave());
}
private void setupCheckButton() {
checkBtn.setDisable(true);
checkBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.executeChecks")));
checkBtn.setOnAction(ae -> {
if (checkDialog == null) {
try {
checkDialog = new CheckDialog(mainWindow, stage, controller);
} catch (IOException e) {
mainWindow.showExceptionDialog(e);
logger.catching(e);
}
}
checkDialog.show();
});
}
public Button getCheckButton() {
return checkBtn;
}
public ToggleButton getGridButton() {
return gridButton;
}
public ToggleButton getCullingButton() {
return cullingButton;
}
public Button getWriteReportButton() {
return reportBtn;
}
public ToggleButton getLod1Btn() {
return lod1Btn;
}
public ToggleButton getLod2Btn() {
return lod2Btn;
}
public ToggleButton getLod3Btn() {
return lod3Btn;
}
public ToggleButton getLod4Btn() {
return lod4Btn;
}
public Button getWorldBtn() {
return showWorldBtn;
}
public Button getSaveBtn() {
return saveBtn;
}
public HBox getToolBar() {
return toolBar;
}
public Button getOpenBtn() {
return openBtn;
}
}
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 de.hft.stuttgart.citydoctor2.CityDoctorValidation;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
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.RenderableTreeCell;
import de.hft.stuttgart.citydoctor2.parameter.ArgumentParser;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ProgressListener;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.AmbientLight;
import javafx.scene.Group;
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.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.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class MainWindow extends Application {
private static final Logger logger = LogManager.getLogger(MainWindow.class);
private static final double CAMERA_TRANSLATE_Z = -100.0;
private static final double CAMERA_INITIAL_X_ANGLE = 20.0;
private static final double CAMERA_INITIAL_Y_ANGLE = 120.0;
@FXML
private TreeView<Renderable> buildingsView;
@FXML
private TreeView<Renderable> vegetationView;
@FXML
private TreeView<Renderable> transView;
@FXML
private TreeView<Renderable> bridgeView;
@FXML
private TreeView<Renderable> waterView;
@FXML
private TreeView<Renderable> terrainView;
@FXML
private TreeView<Renderable> polygonView;
@FXML
private TreeView<Renderable> edgeView;
@FXML
private TreeView<Renderable> vertexView;
@FXML
private Pane meshView;
@FXML
private SplitPane mainContainer;
@FXML
private TabPane detailsTabPane;
@FXML
private TabPane featurePane;
@FXML
private TextField searchField;
@FXML
private TreeView<Renderable> errorView;
@FXML
private Button clearBtn;
@FXML
private Button searchBtn;
@FXML
private ToggleButton allButton;
@FXML
private ToggleButton errorButton;
@FXML
private ComboBox<String> showCityObjectsCombo;
@FXML
private HBox viewPane;
@FXML
private ListView<String> globalErrorsView;
@FXML
private BorderPane mainPane;
@FXML
private HBox viewButtonBox;
@FXML
private ComboBox<Locale> languageSelector;
@FXML
private Tab buildingsTab;
@FXML
private Tab vegetationTab;
@FXML
private Tab transportationTab;
@FXML
private Tab bridgeTab;
@FXML
private Tab waterTab;
@FXML
private Tab terrainTab;
@FXML
private Tab errorsTab;
@FXML
private Tab polygonsTab;
@FXML
private Tab edgesTab;
@FXML
private Tab verticesTab;
@FXML
private Tab logTab;
@FXML
private Tab globalErrorsTab;
@FXML
private Label viewLabel;
@FXML
private Label showLabel;
@FXML
private Label searchLabel;
@FXML
private Label memoryLabel;
@FXML
private ProgressBar memoryBar;
@FXML
private Label memoryConsumptionLabel;
@FXML
private Label availableLabel;
private Group meshGroup;
private Group world;
private PerspectiveCamera camera;
private Rotate cameraXRotation = new Rotate();
private Rotate cameraZRotation = new Rotate();
private double dragX;
private double dragY;
private double cameraXRot = CAMERA_INITIAL_X_ANGLE;
private double cameraYRot = CAMERA_INITIAL_Y_ANGLE;
private double translateZ = CAMERA_TRANSLATE_Z;
private double[] clickStart = new double[2];
@FXML
private TextArea logArea;
@FXML
private ToolBar viewBar;
private Stage stage;
private ExceptionDialog exceptionDialog;
private Renderer renderer;
private CityDoctorController controller;
private HighlightController highlightController;
private FeatureType selectedTab = FeatureType.BUILDING;
private MainToolBar mainToolBar;
private SubScene geomScene;
private static boolean loadFileAtStartup = false;
private static String inputFile;
private static String xmlOutput;
private static String pdfOutput;
private static ValidationConfiguration config;
private VertexClickHandler clickHandler;
private ChangeListener<Number> filterChangeListener;
public static void main(String[] args) {
setLocaleFromSettings();
ArgumentParser argParser = new ArgumentParser(args);
inputFile = CityDoctorValidation.getInputFile(argParser, true);
if (inputFile != null) {
loadFileAtStartup = true;
xmlOutput = CityDoctorValidation.getXmlOutput(argParser);
pdfOutput = CityDoctorValidation.getPdfOutput(argParser);
try {
config = CityDoctorValidation.getValidationConfig(argParser, true);
} catch (FileNotFoundException e) {
Platform.runLater(() -> {
List<String> configFiles = argParser.getValues("config");
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText(Localization.getText("MainWindow.missingConfig") + configFiles);
alert.showAndWait();
});
System.exit(4);
}
}
Application.launch(args);
}
private static void setLocaleFromSettings() {
String localeString = Settings.get(Settings.LANGUAGE);
if (localeString != null) {
try {
Locale loc = new Locale(localeString);
Locale.setDefault(loc);
} catch (Exception e) {
logger.warn("Could not set language to {}, using system language", localeString);
}
}
}
@Override
public void start(Stage stage) throws IOException {
this.stage = stage;
stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/citydoctor_logo.png")));
FXMLLoader loader = new FXMLLoader(MainWindow.class.getResource("MainWindow.fxml"));
loader.setController(this);
BorderPane bp = loader.load();
highlightController = new HighlightController(world);
renderer = new Renderer(this, highlightController);
clickHandler = new VertexClickHandler(errorView, renderer, stage);
controller = new CityDoctorController(this, highlightController, renderer);
mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this);
viewPane.getChildren().add(mainToolBar.getToolBar());
HBox.setHgrow(mainToolBar.getToolBar(), Priority.ALWAYS);
ValidationView valView = new ValidationView(this, controller);
ViewRegistration.registerView(valView);
setupViews(valView);
createLanguageSelector();
setLabelsInCorrectLanguage();
Scene scene = new Scene(bp, 1280, 800);
createDropTarget(scene, valView);
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version);
stage.setScene(scene);
stage.show();
checkForStartupLoading();
memoryBar.setOnMouseClicked(me -> System.gc());
Timer timer = new Timer(true);
// check memory every second
TimerTask task = new TimerTask() {
Runtime runtime = Runtime.getRuntime();
@Override
public void run() {
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
double percentage = usedMemory / (double) totalMemory;
if (totalMemory / 1024 / 1024 >= 1024) {
// gb
double totalMemoryGb = totalMemory / (1024d * 1024d * 1024d);
double usedMemoryGb = usedMemory / (1024d * 1024d * 1024d);
String memoryString = String.format("%.1f GB / %.1f GB", usedMemoryGb, totalMemoryGb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
} else if (totalMemory / 1024 >= 1024) {
// mb
double totalMemoryMb = totalMemory / (1024d * 1024d);
double usedMemoryMb = usedMemory / (1024d * 1024d);
String memoryString = String.format("%.1f MB / %.1f MB", usedMemoryMb, totalMemoryMb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
}
}
};
timer.schedule(task, 0, 1000);
}
private void createDropTarget(Scene scene, ValidationView valView) {
setDragOverInteraction(scene, valView);
scene.setOnDragDropped(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles() && db.getFiles().size() == 1) {
File f = db.getFiles().get(0);
Thread t = new Thread(() -> {
try {
controller.loadCityGml(f.getAbsolutePath(), 8, (ProgressListener) null, false);
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(Localization.getText("OpenFileDialog.loadFailed"), e);
}
Platform.runLater(() -> {
ExceptionDialog exDialog = new ExceptionDialog();
exDialog.show(e);
});
}
});
t.start();
success = true;
}
// let the source know whether the string was successfully transferred and used
event.setDropCompleted(success);
event.consume();
});
}
private void setDragOverInteraction(Scene scene, ValidationView valView) {
scene.setOnDragOver(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
if (event.getGestureSource() != scene && event.getDragboard().hasFiles()
&& event.getDragboard().getFiles().size() == 1) {
// allow for both copying and moving, whatever user chooses
event.acceptTransferModes(TransferMode.LINK);
}
event.consume();
});
}
private void setLabelsInCorrectLanguage() {
buildingsTab.setText(Localization.getText("MainWindow.buildingsTab"));
vegetationTab.setText(Localization.getText("MainWindow.vegetationTab"));
transportationTab.setText(Localization.getText("MainWindow.transportationTab"));
bridgeTab.setText(Localization.getText("MainWindow.bridgeTab"));
waterTab.setText(Localization.getText("MainWindow.waterTab"));
terrainTab.setText(Localization.getText("MainWindow.terrainTab"));
viewLabel.setText(Localization.getText("MainWindow.viewLabel"));
showLabel.setText(Localization.getText("MainWindow.showLabel"));
searchLabel.setText(Localization.getText("MainWindow.searchLabel"));
searchBtn.setText(Localization.getText("MainWindow.searchBtn"));
clearBtn.setText(Localization.getText("MainWindow.clearBtn"));
errorsTab.setText(Localization.getText("MainWindow.errorsTab"));
polygonsTab.setText(Localization.getText("MainWindow.polygonsTab"));
edgesTab.setText(Localization.getText("MainWindow.edgesTab"));
verticesTab.setText(Localization.getText("MainWindow.verticesTab"));
logTab.setText(Localization.getText("MainWindow.logTab"));
globalErrorsTab.setText(Localization.getText("MainWindow.globalErrorsTab"));
memoryLabel.setText(Localization.getText("MainWindow.memoryLabel"));
availableLabel.setText(String.format("%s %.1f GB", Localization.getText("MainWindow.availableLabel"),
Runtime.getRuntime().maxMemory() / 1024d / 1024d / 1024d));
}
private void createLanguageSelector() {
languageSelector.setButtonCell(new LanguageSelectorCell());
languageSelector.setCellFactory(view -> new LanguageSelectorCell());
languageSelector.getItems().add(Locale.GERMAN);
languageSelector.getItems().add(Locale.ENGLISH);
if (Locale.getDefault().getLanguage().equals(Locale.GERMAN.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.GERMAN);
} else if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
} else {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
Settings.set(Settings.LANGUAGE, Locale.ENGLISH.getLanguage());
}
languageSelector.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> {
Alert alert = new Alert(AlertType.CONFIRMATION, Localization.getText("MainWindow.languageChange"),
ButtonType.OK);
alert.showAndWait();
Settings.set(Settings.LANGUAGE, newV.getLanguage());
});
}
private void checkForStartupLoading() {
if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile"));
Thread t = new Thread(() -> {
try {
// wait a bit for the gui to show
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
controller.loadCityGml(inputFile, config.getNumberOfRoundingPlaces(), null,
config.isXmlValidation());
logger.info(Localization.getText("MainWindow.finishedLoading"));
logger.info(Localization.getText("MainWindow.checking"));
controller.startChecks(config, null);
if (xmlOutput != null) {
logger.info(Localization.getText("MainWindow.writeXml"));
controller.writeXmlReport(new File(xmlOutput));
logger.info(Localization.getText("MainWindow.finishedXml"));
}
if (pdfOutput != null) {
logger.info(Localization.getText("MainWindow.writePdf"));
controller.writePdfReport(new File(pdfOutput));
logger.info(Localization.getText("MainWindow.finishedPdf"));
}
} catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(Localization.getText("MainWindow.loadFailed"), e.getMessage());
}
});
t.start();
}
}
private void setupViews(ValidationView valView) {
ToggleGroup group = new ToggleGroup();
for (View v : ViewRegistration.getRegisteredViews()) {
RadioButton radioButton = new RadioButton();
radioButton.getStyleClass().remove("radio-button");
radioButton.getStyleClass().add("toggle-button");
ImageView view = new ImageView(v.getViewLogo());
view.setFitHeight(32);
view.setFitWidth(32);
radioButton.setGraphic(view);
group.getToggles().add(radioButton);
if (v == valView) {
ViewRegistration.setCurrentActiveView(valView);
group.selectToggle(radioButton);
}
viewButtonBox.getChildren().add(radioButton);
radioButton.selectedProperty().addListener((obs, oldV, newV) -> {
if (Boolean.TRUE.equals(newV)) {
showView(v);
ViewRegistration.setCurrentActiveView(v);
} else {
v.onHide();
}
});
}
}
private void showView(View v) {
viewPane.getChildren().clear();
controller.showView(v);
Optional<HBox> toolbar = v.getToolbar();
if (toolbar.isPresent()) {
viewPane.getChildren().add(toolbar.get());
HBox.setHgrow(toolbar.get(), Priority.ALWAYS);
} else {
HBox placeHolderToolbar = new HBox();
placeHolderToolbar.setMaxHeight(Double.MAX_VALUE);
viewPane.getChildren().add(placeHolderToolbar);
HBox.setHgrow(placeHolderToolbar, Priority.ALWAYS);
}
mainPane.setCenter(v.getMainScreen());
}
public void initialize() {
GuiLogger.setTextArea(logArea);
loadFrameConfig();
setup3dView();
setupTrees();
setupSearchField();
setupSearchButtons();
setupShowCityComboBox();
detailsTabPane.getSelectionModel().selectedIndexProperty()
.addListener((ov, oldI, newI) -> Platform.runLater(() -> {
if (newI.intValue() == 0 && errorView.getSelectionModel().getSelectedItem() != null) {
// the first tab is selected, meaning the error tab
// redisplay the selected error
errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
}
}));
}
private void setupShowCityComboBox() {
showCityObjectsCombo.getItems().addAll(Localization.getText("MainWindow.all"),
Localization.getText("MainWindow.withErrors"));
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.setCellFactory(param -> new ListCell<String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contentEquals(Localization.getText("MainWindow.withErrors"))) {
setTextFill(Color.RED);
}
} else {
setText(null);
}
}
});
filterChangeListener = setupFilterSelectionListener();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
public void resetFilterComboBox() {
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().removeListener(filterChangeListener);
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
private ChangeListener<Number> setupFilterSelectionListener() {
return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV);
}
private void setupSearchButtons() {
searchBtn.setOnAction(ae -> controller.searchFeature(searchField.getText(), selectedTab));
clearBtn.setOnAction(ae -> {
if (searchField.getText().isEmpty()) {
// do not reset search if nothing has bee searched
return;
}
controller.resetSearch(selectedTab);
searchField.setText("");
});
}
private void setupSearchField() {
searchField.textProperty().addListener((obs, oldV, newV) -> {
if (newV.isEmpty()) {
controller.resetSearch(selectedTab);
}
});
featurePane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> {
if (!searchField.getText().isEmpty()) {
resetSearchBar();
controller.resetSearch(selectedTab);
}
int index = newV.intValue();
switch (index) {
case 0:
selectedTab = FeatureType.BUILDING;
break;
case 1:
selectedTab = FeatureType.VEGETATION;
break;
case 2:
selectedTab = FeatureType.TRANSPORTATION;
break;
case 3:
selectedTab = FeatureType.BRIDGE;
break;
case 4:
selectedTab = FeatureType.WATER;
break;
case 5:
selectedTab = FeatureType.LAND;
break;
default:
throw new IllegalStateException("Unknown tab index: " + index);
}
});
}
public void resetSearchBar() {
searchField.setText("");
}
private void loadFrameConfig() {
stage.setMaximized(Boolean.valueOf(Settings.get(Settings.MAXIMIZED, Boolean.FALSE.toString())));
stage.maximizedProperty()
.addListener((obs, oldV, newV) -> Settings.set(Settings.MAXIMIZED, Boolean.toString(newV)));
String widthString = Settings.get(Settings.FRAME_WIDTH);
if (widthString != null) {
stage.setWidth(Double.parseDouble(widthString));
}
stage.widthProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_WIDTH, Double.toString(newV.doubleValue())));
String heightString = Settings.get(Settings.FRAME_HEIGHT);
if (heightString != null) {
stage.setHeight(Double.parseDouble(heightString));
}
stage.heightProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_HEIGHT, Double.toString(newV.doubleValue())));
}
public void showExceptionDialog(Throwable e) {
if (exceptionDialog == null) {
exceptionDialog = new ExceptionDialog();
}
exceptionDialog.show(e);
}
private void setupTrees() {
setupSelectListener(errorView);
errorView.setRoot(new TreeItem<>());
errorView.setCellFactory(param -> new RenderableTreeCell());
buildingsView.setShowRoot(true);
setupSelectListener(buildingsView);
buildingsView.setCellFactory(param -> new RenderableTreeCell());
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) {
BuildingNode node = (BuildingNode) render;
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);
vegetationView.setShowRoot(true);
setupSelectListener(vegetationView);
vegetationView.setCellFactory(param -> new RenderableTreeCell());
transView.setShowRoot(true);
setupSelectListener(transView);
transView.setCellFactory(param -> new RenderableTreeCell());
bridgeView.setShowRoot(true);
setupSelectListener(bridgeView);
bridgeView.setCellFactory(param -> new RenderableTreeCell());
waterView.setShowRoot(true);
setupSelectListener(waterView);
waterView.setCellFactory(param -> new RenderableTreeCell());
terrainView.setShowRoot(true);
setupSelectListener(terrainView);
terrainView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(vertexView);
vertexView.setRoot(new TreeItem<>());
vertexView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(polygonView);
polygonView.setRoot(new TreeItem<>());
polygonView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(edgeView);
edgeView.setRoot(new TreeItem<>());
edgeView.setCellFactory(param -> new RenderableTreeCell());
}
private void setupSelectListener(TreeView<Renderable> view) {
view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> {
if (newI != null) {
newI.getValue().visit(renderer);
}
});
}
private void setup3dView() {
Group root = new Group();
geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED);
geomScene.heightProperty().bind(meshView.heightProperty());
geomScene.widthProperty().bind(meshView.widthProperty());
geomScene.setFill(Color.AZURE);
meshView.getChildren().add(geomScene);
geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> {
clickStart[0] = me.getScreenX();
clickStart[1] = me.getScreenY();
});
geomScene.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> {
if (Math.abs(clickStart[0] - me.getScreenX()) > 3 || Math.abs(clickStart[1] - me.getScreenY()) > 3) {
// skip when mouse moved too much
return;
}
Node node = me.getPickResult().getIntersectedNode();
if (node != null) {
Object o = node.getUserData();
if (o instanceof ClickDispatcher) {
ClickDispatcher cd = (ClickDispatcher) o;
cd.click(me, clickHandler);
}
}
});
world = new Group();
root.getChildren().add(world);
meshGroup = new Group();
world.getChildren().add(meshGroup);
AmbientLight al = new AmbientLight(Color.WHITE);
root.getChildren().add(al);
buildCamera();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
world.getTransforms().add(cameraXRotation);
world.getTransforms().add(cameraZRotation);
root.getChildren().add(camera);
geomScene.setCamera(camera);
setupMeshViewControls();
}
private void setupMeshViewControls() {
meshView.setOnMousePressed(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
});
meshView.setOnScroll(se -> {
if (se.getDeltaY() < 0) {
translateZ += translateZ * 0.05;
} else {
translateZ -= translateZ * 0.05;
}
camera.setTranslateZ(translateZ);
highlightController.changeScaling(translateZ);
});
meshView.setOnMouseDragged(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
cameraXRot += (deltaX / 3d) % 360;
cameraYRot += (deltaY / 3d) % 360;
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
});
}
private void buildCamera() {
camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000d);
camera.setTranslateZ(translateZ);
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
public void addFileNameToTitle(String fileName) {
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version + " - " + fileName);
}
public TreeView<Renderable> getBuildingsView() {
return buildingsView;
}
public TreeView<Renderable> getVegetationView() {
return vegetationView;
}
public TreeView<Renderable> getTransportationView() {
return transView;
}
public TreeView<Renderable> getBridgeView() {
return bridgeView;
}
public TreeView<Renderable> getWaterView() {
return waterView;
}
public TreeView<Renderable> getTerrainView() {
return terrainView;
}
public TreeView<Renderable> getPolygonsView() {
return polygonView;
}
public TreeView<Renderable> getEdgeView() {
return edgeView;
}
public TreeView<Renderable> getVertexView() {
return vertexView;
}
public Button getCheckButton() {
return mainToolBar.getCheckButton();
}
public ToggleButton getGridButton() {
return mainToolBar.getGridButton();
}
public Group getMeshGroup() {
return meshGroup;
}
public ToggleButton getCullingButton() {
return mainToolBar.getCullingButton();
}
public Button getWriteReportButton() {
return mainToolBar.getWriteReportButton();
}
public TreeView<Renderable> getErrorTree() {
return errorView;
}
public Stage getMainStage() {
return stage;
}
public void unselectEverything() {
buildingsView.getSelectionModel().clearSelection();
vegetationView.getSelectionModel().clearSelection();
transView.getSelectionModel().clearSelection();
waterView.getSelectionModel().clearSelection();
terrainView.getSelectionModel().clearSelection();
bridgeView.getSelectionModel().clearSelection();
polygonView.getSelectionModel().clearSelection();
edgeView.getSelectionModel().clearSelection();
vertexView.getSelectionModel().clearSelection();
errorView.getSelectionModel().clearSelection();
}
public ToggleButton getLod1Btn() {
return mainToolBar.getLod1Btn();
}
public ToggleButton getLod2Btn() {
return mainToolBar.getLod2Btn();
}
public ToggleButton getLod3Btn() {
return mainToolBar.getLod3Btn();
}
public ToggleButton getLod4Btn() {
return mainToolBar.getLod4Btn();
}
public Button getWorldBtn() {
return mainToolBar.getWorldBtn();
}
public Button getSaveBtn() {
return mainToolBar.getSaveBtn();
}
public SplitPane getMainContainer() {
return mainContainer;
}
public ListView<String> getGlobalErrorsView() {
return globalErrorsView;
}
public void takeViewScreenshot() throws IOException {
WritableImage snapshot = geomScene.snapshot(null, null);
File outputFile = new File("img.png");
BufferedImage bImage = SwingFXUtils.fromFXImage(snapshot, null);
ImageIO.write(bImage, "png", outputFile);
}
public void zoomOutForBoundingBox(BoundingBox b) {
double longestSide = b.getDiagonalLength() * 0.4;
double d = longestSide / Math.tan(Math.toRadians(30) / 2);
translateZ = -d;
camera.setTranslateZ(translateZ);
highlightController.changeScaling(-translateZ);
}
public MainToolBar getMainToolbar() {
return mainToolBar;
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public CityDoctorController getController() {
return controller;
}
public Button getOpenBtn() {
return mainToolBar.getOpenBtn();
}
public VertexClickHandler getClickHandler() {
return clickHandler;
}
public FeatureType getSelectedTab() {
return selectedTab;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
public class ModelProvider {
private CityDoctorController controller;
public ModelProvider(CityDoctorController controller) {
this.controller = controller;
}
public CityDoctorModel getModel() {
return controller.getModel();
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.File;
import java.io.IOException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TitledPane;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
public class OpenFileDialog {
private static final Logger logger = LogManager.getLogger(OpenFileDialog.class);
private Stage stage;
@FXML
private Button loadBtn;
@FXML
private Button cancelBtn;
@FXML
private Button selectBtn;
@FXML
private TextField precisionField;
@FXML
private TextField pathField;
@FXML
private ProgressBar progress;
@FXML
private CheckBox useValidationBox;
@FXML
private Label fileLabel;
@FXML
private TitledPane settingsPane;
@FXML
private Label roundingPlacesLabel;
@FXML
private Label xmlValidationLabel;
@FXML
private Label lowMemoryLabel;
@FXML
private CheckBox lowMemoryBox;
private CityDoctorController controller;
private ExceptionDialog exDialog;
private FileChooser fc;
public OpenFileDialog(Window parent, CityDoctorController controller) throws IOException {
FXMLLoader loader = new FXMLLoader(OpenFileDialog.class.getResource("OpenFileDialog.fxml"));
loader.setController(this);
VBox box = loader.load();
this.controller = controller;
stage = new Stage();
stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/CityDoctor-Logo-rot_klein.jpg")));
stage.setScene(new Scene(box));
stage.initOwner(parent);
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle("Open File");
stage.getScene().addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) -> {
if (event.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
}
public void initialize() {
cancelBtn.setOnAction(ae -> stage.close());
setupPrecisionField();
setupLoadButton();
setupSelectButton();
applyLanguageToControls();
}
private void applyLanguageToControls() {
fileLabel.setText(Localization.getText("OpenFileDialog.fileLabel"));
selectBtn.setText(Localization.getText("OpenFileDialog.selectBtn"));
loadBtn.setText(Localization.getText("OpenFileDialog.loadBtn"));
settingsPane.setText(Localization.getText("OpenFileDialog.settingsPane"));
roundingPlacesLabel.setText(Localization.getText("OpenFileDialog.roundingPlacesLabel"));
xmlValidationLabel.setText(Localization.getText("OpenFileDialog.xmlValidationLabel"));
cancelBtn.setText(Localization.getText("OpenFileDialog.cancelBtn"));
lowMemoryLabel.setText(Localization.getText("OpenFileDialog.lowMemoryLabel"));
}
private void setupSelectButton() {
selectBtn.setOnAction(ae -> {
if (fc == null) {
fc = new FileChooser();
fc.setTitle(Localization.getText("OpenFileDialog.select"));
fc.getExtensionFilters().add(new ExtensionFilter("GML/XML", "*.gml", "*.xml"));
fc.getExtensionFilters().add(new ExtensionFilter(Localization.getText("MainWindow.all"), "*.*"));
}
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
String userDir = System.getProperty("user.dir");
Settings.set(Settings.LAST_OPEN_FOLDER, userDir);
fc.setInitialDirectory(new File(userDir));
}
File f = fc.showOpenDialog(stage);
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
pathField.setText(f.getAbsolutePath());
}
});
}
private void setupLoadButton() {
loadBtn.setOnAction(ae -> {
int numberOfRoundingPlaces = Integer.parseInt(precisionField.getText());
boolean useValidation = useValidationBox.isSelected();
boolean lowMemory = lowMemoryBox.isSelected();
String path = pathField.getText();
cancelBtn.setDisable(true);
loadBtn.setDisable(true);
pathField.setDisable(true);
selectBtn.setDisable(true);
stage.setOnCloseRequest(Event::consume);
Thread t = new Thread(() -> {
try {
controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation,
lowMemory);
Platform.runLater(() -> stage.close());
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(Localization.getText("OpenFileDialog.loadFailed"), e);
}
Platform.runLater(() -> {
if (exDialog == null) {
exDialog = new ExceptionDialog();
}
exDialog.show(e);
});
} finally {
selectBtn.setDisable(false);
pathField.setDisable(false);
cancelBtn.setDisable(false);
loadBtn.setDisable(false);
stage.setOnCloseRequest(null);
}
});
t.start();
});
}
private void setupPrecisionField() {
TextFormatter<String> formatter = new TextFormatter<>(change -> {
if (!change.isContentChange()) {
return change;
}
String text = change.getControlNewText();
try {
Integer.parseInt(text);
return change;
} catch (NumberFormatException e) {
return null;
}
});
precisionField.setTextFormatter(formatter);
}
public void show() {
Platform.runLater(() -> {
progress.setProgress(0d);
stage.showAndWait();
});
}
}
package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import javafx.scene.input.MouseEvent;
public class PolygonClickDispatcher implements ClickDispatcher {
private Polygon p;
public PolygonClickDispatcher(Polygon p) {
this.p = p;
}
@Override
public void click(MouseEvent me, ClickHandler handler) {
handler.onPolygonClick(p, me);
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject;
import de.hft.stuttgart.citydoctor2.datastructure.TinObject;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter;
import de.hft.stuttgart.citydoctor2.gui.tree.EdgeNode;
import de.hft.stuttgart.citydoctor2.gui.tree.ErrorItemVisitor;
import de.hft.stuttgart.citydoctor2.gui.tree.ErrorNode;
import de.hft.stuttgart.citydoctor2.gui.tree.LinearRingNode;
import de.hft.stuttgart.citydoctor2.gui.tree.PolygonNode;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.gui.tree.VertexNode;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import javafx.application.Platform;
import javafx.scene.control.TreeItem;
import javafx.scene.paint.Color;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
public class Renderer {
private static final Logger logger = LogManager.getLogger(Renderer.class);
private TriangulatedGeometry currentTriGeom;
private Geometry currentGeometry;
private CullFace currentCulling = CullFace.BACK;
private DrawMode currentDrawMode = DrawMode.FILL;
private MainWindow mainWindow;
private HighlightController highlightController;
private ListErrorVisitor errVisitor;
private LoadingInfoDialog loadingDialog;
private List<ViewFilter> lodFilters = new ArrayList<>();
private Runnable refresher;
private Runnable errorUpdater;
public Renderer(MainWindow mainWindow, HighlightController highlightController) throws IOException {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
loadingDialog = new LoadingInfoDialog(mainWindow.getMainStage());
errVisitor = new ListErrorVisitor(highlightController);
setupLodFilters();
}
private void setupLodFilters() {
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD1;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD2;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD3;
}
});
lodFilters.add(new ViewFilter() {
@Override
protected boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD0;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD4;
}
});
}
public void enableLod1() {
lodFilters.get(0).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod1() {
lodFilters.get(0).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod2() {
lodFilters.get(1).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod2() {
lodFilters.get(1).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod3() {
lodFilters.get(2).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod3() {
lodFilters.get(2).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod4() {
lodFilters.get(3).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod4() {
lodFilters.get(3).disable();
if (refresher != null) {
refresher.run();
}
}
public void render(Building building) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingPolygons = setupBuildingPolygons(building);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPolygons));
render(setupBuildingPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(building);
errorUpdater.run();
});
};
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 (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();
});
};
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(BridgeObject bridge) {
refresher = () -> {
Set<ConcretePolygon> setupBridgePolygons = setupBridgePolygons(bridge);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBridgePolygons));
render(setupBridgePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bridge);
errorUpdater.run();
});
};
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(() -> {
errorUpdater = () -> displayErrors(co);
errorUpdater.run();
});
};
refresher.run();
}
public void render(ReliefObject relief) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupReliefPolygons(relief);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(relief);
errorUpdater.run();
});
};
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();
});
};
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(BoundarySurface bs) {
refresher = () -> {
Set<ConcretePolygon> setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBoundarySurfacePolygons));
render(setupBoundarySurfacePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bs);
errorUpdater.run();
});
};
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) {
for (Geometry geom : co.getGeometries()) {
boolean used = false;
for (ViewFilter filter : lodFilters) {
if (filter.allowedToUse(co, geom)) {
used = true;
break;
}
}
if (used) {
addConcretePolygons(polygons, geom);
}
}
}
private void displayErrors(Checkable c) {
if (!c.isValidated()) {
return;
}
List<CheckError> errors = new ArrayList<>();
c.collectContainedErrors(errors);
// filter out duplicate errors (polygon can be contained in multiple geometries)
Set<CheckError> errorSet = new HashSet<>(errors);
for (CheckError err : errorSet) {
ErrorNode node = new ErrorNode(err);
TreeItem<Renderable> errItem = new TreeItem<>(node);
ErrorItemVisitor visitor = new ErrorItemVisitor(errItem);
err.accept(visitor);
mainWindow.getErrorTree().getRoot().getChildren().add(errItem);
}
}
public void render(Geometry geom) {
refresher = () -> {
Platform.runLater(this::clearGeometryTrees);
currentTriGeom = TriangulatedGeometry.of(geom);
if (geom.getEdges() == null && currentGeometry != null) {
// if there are no edges available low memory mode is enabled
// clear the old geometry of all meta information
currentGeometry.clearMetaInformation();
}
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
if (geom.getEdges() == null) {
// create edges and vertices so they can be listed in the gui
geom.prepareForChecking();
// remember the geometry, so it can be cleared if another is displayed
currentGeometry = geom;
}
addGeometryDataToView(geom);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(geom.getPolygons()));
errorUpdater = () -> displayErrors(geom);
errorUpdater.run();
});
};
refresher.run();
}
private void addGeometryDataToView(Geometry geom) {
for (Polygon p : geom.getPolygons()) {
addPolygonToView(p);
}
for (Edge e : geom.getEdges()) {
addEdgeToView(e);
}
for (Vertex v : geom.getVertices()) {
addVertexToView(v);
}
}
private void addVertexToView(Vertex v) {
Renderable cf = new VertexNode(v);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getVertexView().getRoot().getChildren().add(ti);
}
private void addEdgeToView(Edge e) {
EdgeNode cf = new EdgeNode(e);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getEdgeView().getRoot().getChildren().add(ti);
}
private void addPolygonToView(Polygon p) {
CheckStatus cs = determineCheckStatus(p);
PolygonNode cf = new PolygonNode(p, cs);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getPolygonsView().getRoot().getChildren().add(ti);
// add linear rings
CheckStatus csRing = determineCheckStatus(p.getExteriorRing());
Renderable ccRing = new LinearRingNode(p.getExteriorRing(), csRing);
TreeItem<Renderable> tiRing = new TreeItem<>(ccRing);
ti.getChildren().add(tiRing);
for (Vertex v : p.getExteriorRing().getVertices()) {
VertexNode vn = new VertexNode(v);
TreeItem<Renderable> tiV = new TreeItem<>(vn);
tiRing.getChildren().add(tiV);
}
for (LinearRing lr : p.getInnerRings()) {
CheckStatus csInteriorRing = determineCheckStatus(lr);
Renderable ccInteriorRing = new LinearRingNode(lr, csInteriorRing);
TreeItem<Renderable> tiInteriorRing = new TreeItem<>(ccInteriorRing);
ti.getChildren().add(tiInteriorRing);
for (Vertex v : lr.getVertices()) {
VertexNode vn = new VertexNode(v);
TreeItem<Renderable> tiV = new TreeItem<>(vn);
tiRing.getChildren().add(tiV);
}
}
}
private CheckStatus determineCheckStatus(Checkable c) {
if (!c.isValidated()) {
return CheckStatus.NOT_CHECKED;
}
if (c.containsAnyError()) {
return CheckStatus.ERROR;
}
return CheckStatus.OK;
}
private void render(Collection<? extends Polygon> polygons) {
Platform.runLater(this::clearGeometryTrees);
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
setupRenderState();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
public void renderBuildings(List<Building> objects) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (Building b : objects) {
collectPolygons(polygons, b);
for (BuildingPart bp : b.getBuildingParts()) {
collectPolygons(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) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
currentTriGeom = TriangulatedGeometry.of(model, lodFilters);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(model));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void renderVegetation(List<Vegetation> vegetation) {
renderCityObjects(vegetation, Color.LIGHTGREEN);
}
public void renderTransportation(List<TransportationObject> transportation) {
renderCityObjects(transportation, Color.YELLOW);
}
public void renderBridges(List<BridgeObject> bridges) {
renderCityObjects(bridges, Color.CORAL);
}
public void renderWater(List<WaterObject> water) {
renderCityObjects(water, Color.LIGHTSKYBLUE);
}
public void renderTerrain(List<CityObject> land) {
renderLandObjects(land, Color.BROWN);
}
private void renderLandObjects(List<CityObject> cos, Color baseColor) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
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) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) {
addPolygons(co, 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();
}
private void collectPolygons(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 (BoundarySurface bs : ab.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
private void addConcretePolygons(Set<ConcretePolygon> polygons, Geometry geom) {
for (Polygon p : geom.getPolygons()) {
polygons.add(p.getOriginal());
}
}
private void setupRenderState() {
currentTriGeom.setCullFace(currentCulling);
currentTriGeom.setDrawMode(currentDrawMode);
Platform.runLater(() -> {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.getMeshGroup().getChildren().addAll(currentTriGeom.getMeshes());
});
mainWindow.getGridButton().setDisable(false);
mainWindow.getCullingButton().setDisable(false);
}
public void showWireFrame(boolean show) {
if (currentTriGeom != null) {
if (show) {
currentDrawMode = DrawMode.LINE;
} else {
currentDrawMode = DrawMode.FILL;
}
currentTriGeom.setDrawMode(currentDrawMode);
}
}
public void enableCulling(boolean enable) {
if (currentTriGeom != null) {
if (enable) {
currentCulling = CullFace.BACK;
} else {
currentCulling = CullFace.NONE;
}
currentTriGeom.setCullFace(currentCulling);
}
}
public void clearCurrentRender() {
// don't render anything
Platform.runLater(() -> {
mainWindow.getMeshGroup().getChildren().clear();
clearGeometryTrees();
});
}
public void highlight(Polygon p) {
highlightController.highlight(p, currentTriGeom);
}
public void highlight(LinearRing lr) {
highlightController.highlight(lr, currentTriGeom);
}
public void highlight(Edge e) {
highlightController.highlight(e, currentTriGeom);
}
public void highlight(Vertex v) {
highlightController.highlight(v, currentTriGeom);
}
public void highlight(List<LinearRing> highlightedRings) {
highlightController.highlight(highlightedRings, currentTriGeom);
}
public void highlightEdges(List<Edge> edges) {
highlightController.highlightEdges(edges, currentTriGeom);
}
public void highlightPolygons(List<List<Polygon>> components) {
highlightController.highlightPolygons(components, currentTriGeom);
}
public void addHighlight(Vertex vertex, Color c) {
highlightController.addHighlight(vertex, currentTriGeom, c);
}
public void highlight(CheckError err) {
err.accept(errVisitor);
}
public void refresh() {
if (refresher != null) {
refresher.run();
}
}
public void updateErrors() {
if (errorUpdater != null) {
errorUpdater.run();
}
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public void addHighlight(Polygon p) {
highlightController.addHighlight(p, currentTriGeom);
}
public void addHighlight(Triangle3d t) {
highlightController.highlight(t, currentTriGeom);
}
public void reset() {
errorUpdater = null;
refresher = null;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Settings {
private static Logger logger = LogManager.getLogger(Settings.class);
public static final String LAST_OPEN_FOLDER = "lastOpenFolder";
public static final String MAXIMIZED = "maximized";
public static final String FRAME_HEIGHT = "frameHeight";
public static final String FRAME_WIDTH = "frameWidth";
public static final String FRAME_X = "frameX";
public static final String FRAME_Y = "frameY";
public static final String LANGUAGE = "language";
private static Properties props;
static {
props = new Properties();
File propFile = new File("GUISettings.properties");
if (propFile.exists()) {
try (BufferedReader bis = new BufferedReader(new FileReader(propFile))) {
props.load(bis);
} catch (IOException e) {
logger.error("Failed to load settings", e);
}
}
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(propFile))) {
props.store(bw, "GUI configuration");
} catch (IOException e) {
logger.error("Failed to save settings", e);
}
}));
}
private Settings() {
}
public static String get(String name) {
return props.getProperty(name);
}
public static void set(String name, String value) {
props.setProperty(name, value);
}
public static String get(String name, String defaultV) {
return props.getProperty(name, defaultV);
}
}
/*-
* Copyright 2020 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;
import javafx.event.Event;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;
public class TableEditCell<S, T> extends TableCell<S, T> {
// Text field for editing
private final TextField textField = new TextField();
// Converter for converting the text in the text field to the user type, and
// vice-versa:
private final StringConverter<T> converter;
public TableEditCell(StringConverter<T> converter) {
this.converter = converter;
itemProperty().addListener((obx, oldItem, newItem) -> {
if (newItem == null) {
setText(null);
} else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
textField.setOnAction(evt -> commitEdit(this.converter.fromString(textField.getText())));
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (Boolean.FALSE.equals(isNowFocused)) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
} else if (event.getCode() == KeyCode.RIGHT) {
getTableView().getSelectionModel().selectRightCell();
event.consume();
} else if (event.getCode() == KeyCode.LEFT) {
getTableView().getSelectionModel().selectLeftCell();
event.consume();
} else if (event.getCode() == KeyCode.UP) {
getTableView().getSelectionModel().selectAboveCell();
event.consume();
} else if (event.getCode() == KeyCode.DOWN) {
getTableView().getSelectionModel().selectBelowCell();
event.consume();
}
});
}
/**
* Convenience converter that does nothing (converts Strings to themselves and
* vice-versa...).
*/
public static final StringConverter<String> IDENTITY_CONVERTER = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
/**
* Convenience method for creating an EditCell for a String value.
*
* @return
*/
public static <S> TableEditCell<S, String> createStringEditCell() {
return new TableEditCell<>(IDENTITY_CONVERTER);
}
// set the text of the text field and display the graphic
@Override
public void startEdit() {
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
// revert to text display
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
// commits the edit. Update property if possible and revert to text display
@Override
public void commitEdit(T item) {
// This block is necessary to support commit on losing focus, because the
// baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TableView<S> table = getTableView();
if (table != null) {
TableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(table,
new TablePosition<>(table, getIndex(), column), TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
\ No newline at end of file
/*-
* Copyright 2020 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;
import javafx.event.Event;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellEditEvent;
import javafx.scene.control.TreeTablePosition;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;
public class TreeEditCell<S, T> extends TreeTableCell<S, T> {
// Text field for editing
private final TextField textField = new TextField();
// Converter for converting the text in the text field to the user type, and
// vice-versa:
private final StringConverter<T> converter;
public TreeEditCell(StringConverter<T> converter) {
this.converter = converter;
itemProperty().addListener((obx, oldItem, newItem) -> {
if (newItem == null) {
setText(null);
} else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
textField.setOnAction(evt -> commitEdit(this.converter.fromString(textField.getText())));
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (Boolean.FALSE.equals(isNowFocused)) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
} else if (event.getCode() == KeyCode.RIGHT) {
getTreeTableView().getSelectionModel().selectRightCell();
event.consume();
} else if (event.getCode() == KeyCode.LEFT) {
getTreeTableView().getSelectionModel().selectLeftCell();
event.consume();
} else if (event.getCode() == KeyCode.UP) {
getTreeTableView().getSelectionModel().selectAboveCell();
event.consume();
} else if (event.getCode() == KeyCode.DOWN) {
getTreeTableView().getSelectionModel().selectBelowCell();
event.consume();
}
});
}
// set the text of the text field and display the graphic
@Override
public void startEdit() {
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
// revert to text display
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
// commits the edit. Update property if possible and revert to text display
@Override
public void commitEdit(T item) {
// This block is necessary to support commit on losing focus, because the
// baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TreeTableView<S> table = getTreeTableView();
if (table != null) {
TreeTableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(table,
new TreeTablePosition<>(table, getIndex(), column), TreeTableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.OpeningType;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.UnitVector3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.shape.VertexFormat;
public class TriangulatedGeometry {
private static final PhongMaterial GRID_MAT = new PhongMaterial(Color.BLACK);
// random vector for calculating normal angles, for color determination
private static final UnitVector3d AXIS = new Vector3d(19, 0.8, 1.5).normalize();
private Vector3d movedBy;
private List<MeshView> meshes;
private List<PhongMaterial> materials;
public static TriangulatedGeometry of(Geometry geom) {
return of(geom.getPolygons());
}
public static TriangulatedGeometry of(Collection<? extends Polygon> polygons, Color basePolygonColor) {
TriangulatedGeometry triGeom = new TriangulatedGeometry();
triGeom.materials = new ArrayList<>();
triGeom.meshes = new ArrayList<>();
List<Vector3d> points = new ArrayList<>();
for (Polygon p : polygons) {
for (Vertex v : p.getExteriorRing().getVertices()) {
points.add(v);
}
}
triGeom.movedBy = triGeom.findCenter(points);
addPolygonDataToTriGeom(polygons, basePolygonColor, triGeom);
return triGeom;
}
private static void addPolygonDataToTriGeom(Collection<? extends Polygon> polygons, Color basePolygonColor,
TriangulatedGeometry triGeom) {
for (Polygon p : polygons) {
TesselatedPolygon tp = p.tesselate();
TriangleMesh triMesh = new TriangleMesh(VertexFormat.POINT_TEXCOORD);
Map<Vector3d, Integer> indexMap = new HashMap<>();
List<Vector3d> vertices = new ArrayList<>();
int index = 0;
for (Triangle3d t : tp.getTriangles()) {
index = triGeom.filterDuplicates(triMesh, indexMap, vertices, index, t.getP1());
index = triGeom.filterDuplicates(triMesh, indexMap, vertices, index, t.getP2());
index = triGeom.filterDuplicates(triMesh, indexMap, vertices, index, t.getP3());
}
for (Vector3d point : vertices) {
float x = (float) (point.getX() - triGeom.movedBy.getX());
float y = (float) (point.getY() - triGeom.movedBy.getY());
float z = (float) (point.getZ() - triGeom.movedBy.getZ());
triMesh.getPoints().addAll(x, y, z);
}
triMesh.getTexCoords().addAll(0, 0);
MeshView view = new MeshView(triMesh);
view.setUserData(new PolygonClickDispatcher(p));
PhongMaterial mat = triGeom.calculateMaterial(p, basePolygonColor);
triGeom.materials.add(mat);
triGeom.meshes.add(view);
}
}
public static TriangulatedGeometry of(CityDoctorModel model, List<ViewFilter> filters) {
List<Vector3d> points = new ArrayList<>();
addPointsFromBuildings(model.getBuildings(), 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();
triGeom.materials = new ArrayList<>();
triGeom.meshes = new ArrayList<>();
triGeom.movedBy = triGeom.findCenter(points);
addPolygonDataFromBuildings(model.getBuildings(), triGeom, filters);
addPolygonDataFromCityObjects(model.getBridges(), triGeom, Color.CORAL, filters);
addPolygonDataFromCityObjects(model.getLand(), triGeom, Color.BROWN, filters);
addPolygonDataFromCityObjects(model.getTransportation(), triGeom, Color.YELLOW, filters);
addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters);
addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters);
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 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,
TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
for (CityObject co : cos) {
addPolygonData(co, triGeom, color, filters);
}
}
private static void addPolygonData(CityObject co, TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
for (Geometry geom : co.getGeometries()) {
if (isGeometryFiltered(co, geom, filters)) {
continue;
}
List<Polygon> polygons = new ArrayList<>();
for (Polygon p : geom.getPolygons()) {
if (p.isLink()) {
continue;
}
polygons.add(p);
}
addPolygonDataToTriGeom(polygons, color, triGeom);
}
}
private static boolean isGeometryFiltered(CityObject co, Geometry geom, List<ViewFilter> filters) {
for (ViewFilter filter : filters) {
if (filter.allowedToUse(co, geom)) {
return false;
}
}
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) {
for (CityObject co : cos) {
addPoints(co, points);
}
}
private static void addPoints(CityObject co, List<Vector3d> points) {
for (Geometry geom : co.getGeometries()) {
for (Polygon p : geom.getPolygons()) {
if (p.isLink()) {
continue;
}
for (Vertex v : p.getExteriorRing().getVertices()) {
points.add(v);
}
}
}
}
public static TriangulatedGeometry of(Collection<? extends Polygon> polygons) {
return of(polygons, Color.WHITE);
}
private int filterDuplicates(TriangleMesh triMesh, Map<Vector3d, Integer> indexMap, List<Vector3d> vertices,
int index, Vector3d v) {
Integer vertexIndex = indexMap.get(v);
if (vertexIndex == null) {
indexMap.put(v, index);
vertices.add(v);
vertexIndex = index;
index++;
}
triMesh.getFaces().addAll(vertexIndex, 0);
return index;
}
private PhongMaterial calculateMaterial(Polygon p, Color baseColor) {
Vector3d normal = p.calculateNormalNormalized();
BoundarySurface bs = p.getPartOfSurface();
if (bs != null) {
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 acos = Math.acos(cos);
// normalize to range [0.3, 0.9]
acos = acos / Math.PI;
acos = acos * 0.6 + 0.3;
Color derivedColor = baseColor.deriveColor(0, 1.0, acos, 1.0);
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) {
Opening op = (Opening) parent;
if (op.getType() == OpeningType.DOOR) {
baseColor = Color.ORANGE;
} else {
baseColor = Color.TEAL;
}
}
return baseColor;
}
private Vector3d findCenter(List<Vector3d> points) {
double xMin = Double.MAX_VALUE;
double yMin = Double.MAX_VALUE;
double zMin = Double.MAX_VALUE;
double xMax = Double.NEGATIVE_INFINITY;
double yMax = Double.NEGATIVE_INFINITY;
double zMax = Double.NEGATIVE_INFINITY;
for (Vector3d point : points) {
if (point.getX() < xMin) {
xMin = point.getX();
}
if (point.getX() > xMax) {
xMax = point.getX();
}
if (point.getY() < yMin) {
yMin = point.getY();
}
if (point.getY() > yMax) {
yMax = point.getY();
}
if (point.getZ() < zMin) {
zMin = point.getZ();
}
if (point.getZ() > zMax) {
zMax = point.getZ();
}
}
// center
double x = (xMax - xMin) / 2 + xMin;
double y = (yMax - yMin) / 2 + yMin;
double z = (zMax - zMin) / 2 + zMin;
return new Vector3d(x, y, z);
}
public Vector3d getMovedBy() {
return movedBy;
}
public void setCullFace(CullFace currentCulling) {
for (MeshView mesh : meshes) {
mesh.setCullFace(currentCulling);
}
}
public void setDrawMode(DrawMode currentDrawMode) {
if (currentDrawMode == DrawMode.LINE) {
for (MeshView mesh : meshes) {
mesh.setDrawMode(currentDrawMode);
mesh.setMaterial(GRID_MAT);
}
} else if (currentDrawMode == DrawMode.FILL) {
for (int i = 0; i < meshes.size(); i++) {
MeshView mesh = meshes.get(i);
mesh.setDrawMode(currentDrawMode);
mesh.setMaterial(materials.get(i));
}
}
}
public List<MeshView> getMeshes() {
return meshes;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
public class ValidationView extends View {
private static final Logger logger = LogManager.getLogger(ValidationView.class);
private Image viewLogo;
private MainWindow mainWindow;
private CityDoctorController controller;
public ValidationView(MainWindow mainWindow, CityDoctorController controller) {
this.mainWindow = mainWindow;
this.controller = controller;
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) {
viewLogo = new Image(inStream);
} catch (IOException e) {
logger.catching(e);
}
}
@Override
public Node getMainScreen() {
return mainWindow.getMainContainer();
}
@Override
public Optional<HBox> getToolbar() {
return Optional.of(mainWindow.getMainToolbar().getToolBar());
}
@Override
public void onHide() {
Platform.runLater(() -> {
mainWindow.unselectEverything();
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.clearHighlights();
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
});
}
@Override
public void onShow(CityDoctorModel model, Checker checker) {
if (model == null) {
return;
}
controller.buildTrees();
controller.updateFeatureTrees();
}
@Override
public Image getViewLogo() {
return viewLogo;
}
@Override
public void initializeView(MainWindow mainWindow) {
// already initialized
}
}
package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import javafx.scene.input.MouseEvent;
public class VertexClickDispatcher implements ClickDispatcher {
private Vertex v;
public VertexClickDispatcher(Vertex v) {
this.v = v;
}
@Override
public void click(MouseEvent me, ClickHandler handler) {
handler.onVertexClick(v, me);
}
}
package de.hft.stuttgart.citydoctor2.gui;
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.parser.ParserConfiguration;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class VertexClickHandler implements ClickHandler {
private TreeView<Renderable> errorView;
private Renderer renderer;
private Stage stage;
private ParserConfiguration config;
public VertexClickHandler(TreeView<Renderable> errorView, Renderer renderer, Stage stage) {
this.errorView = errorView;
this.renderer = renderer;
this.stage = stage;
}
public void setConfig(ParserConfiguration config) {
this.config = config;
}
@Override
public void onPolygonClick(Polygon p, MouseEvent me) {
if (me.getButton() == MouseButton.PRIMARY) {
errorView.getSelectionModel().clearSelection();
renderer.highlight(p);
} else if (me.getButton() == MouseButton.SECONDARY) {
MenuItem mi = new MenuItem(p.getGmlId().getGmlString());
ContextMenu cMenu = new ContextMenu(mi);
cMenu.show(stage, me.getScreenX(), me.getScreenY());
}
}
@Override
public void onVertexClick(Vertex v, MouseEvent me) {
if (me.getButton() == MouseButton.SECONDARY) {
MenuItem mi1 = new CustomMenuItem(new Label("Vertex"));
double x = v.getX();
double y = v.getY();
if (config.getOriginalTransform() != null) {
ProjCoordinate p1 = new ProjCoordinate();
ProjCoordinate p2 = new ProjCoordinate();
p1.x = v.getX();
p1.y = v.getY();
config.getOriginalTransform().transform(p1, p2);
x = p2.x;
y = p2.y;
}
MenuItem mi2 = new MenuItem("x = " + x);
MenuItem mi3 = new MenuItem("y = " + y);
MenuItem mi4 = new MenuItem("z = " + v.getZ());
ContextMenu cMenu = new ContextMenu(mi1, mi2, mi3, mi4);
cMenu.show(stage, me.getScreenX(), me.getScreenY());
}
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.util.Optional;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
public abstract class View {
private boolean firstTime = true;
public abstract Optional<HBox> getToolbar();
public abstract Node getMainScreen();
public abstract Image getViewLogo();
public abstract void initializeView(MainWindow mainWindow);
public void fireOnShowEvent(CityDoctorModel model, Checker checker, MainWindow mainWindow) {
if (firstTime) {
firstTime = false;
initializeView(mainWindow);
}
onShow(model, checker);
}
public abstract void onHide();
public abstract void onShow(CityDoctorModel model, Checker checker);
}
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