Commit 81be0b1d authored by Riegel's avatar Riegel
Browse files

Merge branch 'dev_GUI' into 'dev'

Open source release of CityDoctorGUI and other extensions.

See merge request !6
parents 12d96d95 5a4d0a74
Pipeline #10056 passed with stage
in 1 minute and 6 seconds
package de.hft.stuttgart.citydoctor2.healer.genetic;
public interface Crossover<T> {
public T cross(T parent1, T parent2);
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
public interface FitnessEvaluation<T> {
public double evaluate(T t);
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class GeneticAlgorithm<T> implements Runnable {
private int populationSize = 5;
private int maxGenerations = 2;
private Crossover<T> cross;
private Mutation<T> mutate;
private Heuristic<T> heuristic;
private FitnessEvaluation<T> evaluation;
private Individual<T> result;
private GeneticAlgorithm(Crossover<T> cross, Mutation<T> mutate, Heuristic<T> heuristic,
FitnessEvaluation<T> evaluation) {
this.cross = Objects.requireNonNull(cross);
this.mutate = Objects.requireNonNull(mutate);
this.heuristic = Objects.requireNonNull(heuristic);
this.evaluation = Objects.requireNonNull(evaluation);
}
public static <T> GeneticAlgorithm<T> of(Crossover<T> cross, Mutation<T> mutate, Heuristic<T> heuristic,
FitnessEvaluation<T> evaluation) {
return new GeneticAlgorithm<>(cross, mutate, heuristic, evaluation);
}
@Override
public void run() {
// initial population
List<Individual<T>> population = new ArrayList<>();
for (int i = 0; i < populationSize; i++) {
population.add(new Individual<>(heuristic.generate()));
}
for (Individual<T> genome : population) {
genome.evaluateFitness(evaluation);
}
Collections.sort(population);
System.out.println("Best Fitness: " + population.get(0).getFitness());
// do the generations thing
for (int i = 0; i < maxGenerations; i++) {
// generate children
for (int j = 0; j < populationSize - 1; j++) {
// maybe random children generation
Individual<T> parent1 = population.get(j);
Individual<T> parent2 = population.get(j + 1);
Individual<T> child1 = new Individual<>(cross.cross(parent1.getObject(), parent2.getObject()));
Individual<T> child2 = new Individual<>(cross.cross(parent2.getObject(), parent1.getObject()));
// mutate
child1 = new Individual<>(mutate.mutate(child1.getObject()));
child2 = new Individual<>(mutate.mutate(child2.getObject()));
child1.evaluateFitness(evaluation);
child2.evaluateFitness(evaluation);
population.add(child1);
population.add(child2);
}
// sort
Collections.sort(population);
// eliminate
List<Individual<T>> newGeneration = new ArrayList<>(populationSize);
for (int j = 0; j < populationSize; j++) {
newGeneration.add(population.get(j));
}
population = newGeneration;
System.out.println("Best Fitness: " + population.get(0).getFitness());
}
result = population.get(0);
System.out.println("End. Best fitness: " + population.get(0).getFitness() + " with plan: " + population.get(0).getObject());
}
public Individual<T> getResult() {
return result;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
public interface Heuristic<T> {
public T generate();
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
public class Individual<T> implements Comparable<Individual<T>> {
private T object;
private double fitness;
public Individual(T object) {
this.object = object;
}
public void evaluateFitness(FitnessEvaluation<T> evaluation) {
fitness = evaluation.evaluate(object);
}
public double getFitness() {
return fitness;
}
@Override
public int compareTo(Individual<T> o) {
return Double.compare(o.getFitness(), fitness);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(fitness);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((object == null) ? 0 : object.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;
}
Individual<?> other = (Individual<?>) obj;
if (Double.doubleToLongBits(fitness) != Double.doubleToLongBits(other.fitness)) {
return false;
}
if (object == null) {
if (other.object != null) {
return false;
}
} else if (!object.equals(other.object)) {
return false;
}
return true;
}
public T getObject() {
return object;
}
@Override
public String toString() {
return "Genotype [object=" + object + ", fitness=" + fitness + "]";
}
public void setFitness(double fitness) {
this.fitness = fitness;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
public interface Mutation<T> {
public T mutate(T genotype);
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
public class NoMutation implements Mutation<HealingPlan> {
@Override
public HealingPlan mutate(HealingPlan plan) {
return plan;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
public class OrderCrossover implements Crossover<HealingPlan> {
private Random r;
public OrderCrossover() {
r = new Random();
}
@Override
public HealingPlan cross(HealingPlan parent1, HealingPlan parent2) {
int size1 = parent1.getHealingMethods().size();
int firstPoint = Math.max(0, r.nextInt(size1 - 2));
int secondPoint = Math.min(size1, firstPoint + r.nextInt(size1 - firstPoint) + 1);
List<HealingMethod> childMethods = new ArrayList<>(size1);
// fill with empty values, so set(i, x) works
for (int i = 0; i < size1; i++) {
childMethods.add(null);
}
for (int i = firstPoint; i < secondPoint; i++) {
childMethods.set(i, parent1.getHealingMethods().get(i));
}
int writeIndex = 0;
for (HealingMethod m : parent2.getHealingMethods()) {
if (!contains(childMethods, m)) {
while (childMethods.get(writeIndex) != null) {
writeIndex++;
}
childMethods.set(writeIndex, m);
}
}
for (HealingMethod m : parent1.getHealingMethods()) {
if (!contains(childMethods, m)) {
while (childMethods.get(writeIndex) != null) {
writeIndex++;
}
childMethods.set(writeIndex, m);
}
}
HealingPlan child = new HealingPlan();
child.getHealingMethods().addAll(childMethods);
return child;
}
private boolean contains(List<HealingMethod> childMethods, HealingMethod m) {
for (HealingMethod method : childMethods) {
if (method != null && method.getClass() == m.getClass()) {
return true;
}
}
return false;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
public class RandomFitness implements FitnessEvaluation<HealingPlan> {
@Override
public double evaluate(HealingPlan plan) {
return Math.random() * 50000;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.healer.HealingMethodPrototype;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
public class RandomHeuristic implements Heuristic<HealingPlan> {
private List<HealingMethodPrototype> availableHealingMethods;
public RandomHeuristic(List<HealingMethodPrototype> availableHealingMethods) {
this.availableHealingMethods = availableHealingMethods;
}
@Override
public HealingPlan generate() {
List<HealingMethod> methods = new ArrayList<>();
for (HealingMethodPrototype proto : availableHealingMethods) {
methods.add(proto.createMethod());
}
Collections.shuffle(methods);
HealingPlan plan = new HealingPlan();
plan.getHealingMethods().addAll(methods);
System.out.println("Generated: " + plan);
return plan;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic;
import java.util.List;
import java.util.Random;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
public class RandomSwitchMutation implements Mutation<HealingPlan> {
private Random r = new Random();
@Override
public HealingPlan mutate(HealingPlan individual) {
double percentage = r.nextDouble();
if (percentage > 0.7) {
// mutate 30% of the time
List<HealingMethod> healingMethods = individual.getHealingMethods();
int index = r.nextInt(healingMethods.size() - 1);
HealingMethod temp = healingMethods.get(index + 1);
healingMethods.set(index + 1, healingMethods.get(index));
healingMethods.set(index, temp);
}
return individual;
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic.gui;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
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.GmlId;
import de.hft.stuttgart.citydoctor2.gui.MainWindow;
import de.hft.stuttgart.citydoctor2.gui.TriangulatedGeometry;
import de.hft.stuttgart.citydoctor2.gui.View;
import de.hft.stuttgart.citydoctor2.gui.ViewRegistration;
import de.hft.stuttgart.citydoctor2.healer.HealingMethods;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
import de.hft.stuttgart.citydoctor2.healer.genetic.GeneticAlgorithm;
import de.hft.stuttgart.citydoctor2.healer.genetic.NoMutation;
import de.hft.stuttgart.citydoctor2.healer.genetic.OrderCrossover;
import de.hft.stuttgart.citydoctor2.healer.genetic.RandomHeuristic;
import de.hft.stuttgart.citydoctor2.healer.gui.HealerGUI;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Copy;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.AmbientLight;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.PerspectiveCamera;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SnapshotParameters;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.transform.Rotate;
public class GeneticsGUI extends View {
private static final double CAMERA_INITIAL_X_ANGLE = 20.0;
private static final double CAMERA_INITIAL_Y_ANGLE = 120.0;
private Parent pane;
private Image image;
private DoubleProperty translateZ;
private DoubleProperty cameraXRot;
private DoubleProperty cameraYRot;
private boolean enableGuiControls = true;
private Group currentWorld;
private Group nextWorld;
private Group currentMeshGroup;
private Group nextMeshGroup;
@FXML
private Pane view1;
@FXML
private Pane view2;
@FXML
private Button test;
private Geometry currentGeometry;
private CityObject currentFeature;
private Checker currentChecker;
private TriangulatedGeometry currentTriangulatedGeometry;
private TriangulatedGeometry nextTriangulatedGeometry;
private Geometry nextGeometry;
private double dragX;
private double dragY;
public GeneticsGUI() {
image = new Image(getClass().getResourceAsStream("genetic.png"));
}
public void initialize() {
view2.prefWidthProperty().bind(view1.widthProperty());
view2.prefHeightProperty().bind(view1.heightProperty());
translateZ = new SimpleDoubleProperty(0);
cameraXRot = new SimpleDoubleProperty(CAMERA_INITIAL_X_ANGLE);
cameraYRot = new SimpleDoubleProperty(CAMERA_INITIAL_Y_ANGLE);
test.setOnAction(ae -> {
test.setDisable(true);
enableGuiControls = false;
GmlId gmlId = currentGeometry.getGmlId();
OrderCrossover cross = new OrderCrossover();
NoMutation mutation = new NoMutation();
RandomHeuristic heuristic = new RandomHeuristic(HealingMethods.getAvailableHealingMethods());
VisualFitness fitness = new VisualFitness(this, currentGeometry, currentChecker);
GeneticAlgorithm<HealingPlan> ga = GeneticAlgorithm.of(cross, mutation, heuristic, fitness);
ga.run();
CityObject nextFeature = Copy.copy(currentFeature);
nextGeometry = findNextGeometry(gmlId, nextFeature);
System.out.println(gmlId);
System.out.println(nextGeometry);
System.out.println(nextFeature.getGeometries().get(0));
showCurrentGeometry();
showNextGeometry();
test.setDisable(false);
enableGuiControls = true;
});
buildCurrent3dView();
buildNext3dView();
}
private Geometry findNextGeometry(GmlId gmlId, CityObject nextFeature) {
Geometry[] foundGeom = new Geometry[1];
Check geomFinder = new Check() {
@Override
public void check(Geometry geom) {
if (geom.getGmlId().equals(gmlId)) {
foundGeom[0] = geom;
}
}
@Override
public RequirementType getType() {
return null;
}
@Override
public Check createNewInstance() {
return null;
}
@Override
public CheckId getCheckId() {
return null;
}
@Override
public Set<Requirement> appliesToRequirements() {
return null;
}
};
nextFeature.accept(geomFinder);
return foundGeom[0];
}
private void showCurrentGeometry() {
currentTriangulatedGeometry = TriangulatedGeometry.of(currentGeometry);
currentTriangulatedGeometry.setCullFace(CullFace.BACK);
currentTriangulatedGeometry.setDrawMode(DrawMode.FILL);
zoomOutForBoundingBox(currentGeometry.calculateBoundingBox());
getCurrentMeshGroup().getChildren().clear();
getCurrentMeshGroup().getChildren().addAll(currentTriangulatedGeometry.getMeshes());
}
private void showNextGeometry() {
nextTriangulatedGeometry = TriangulatedGeometry.of(nextGeometry);
nextTriangulatedGeometry.setCullFace(CullFace.BACK);
nextTriangulatedGeometry.setDrawMode(DrawMode.FILL);
zoomOutForBoundingBox(nextGeometry.calculateBoundingBox());
getNextMeshGroup().getChildren().clear();
getNextMeshGroup().getChildren().addAll(nextTriangulatedGeometry.getMeshes());
}
private Group getNextMeshGroup() {
return nextMeshGroup;
}
private List<Image> makeScreenshots(int xRotSteps, int yRotSteps) {
List<Image> images = new ArrayList<>();
SnapshotParameters params = new SnapshotParameters();
for (int j = 0; j < xRotSteps; j++) {
cameraXRot.set(j * (180d / xRotSteps));
for (int i = 0; i < yRotSteps; i++) {
cameraYRot.set(i * (360d / yRotSteps));
Image screen = view1.snapshot(params, null);
images.add(screen);
}
}
cameraXRot.set(0);
cameraYRot.set(0);
return images;
}
private void buildCurrent3dView() {
Group currentRoot = new Group();
SubScene currentScene = new SubScene(currentRoot, 500, 300, true, SceneAntialiasing.BALANCED);
currentScene.heightProperty().bind(view1.heightProperty());
currentScene.widthProperty().bind(view1.widthProperty());
currentScene.setFill(Color.AZURE);
view1.getChildren().add(currentScene);
currentWorld = new Group();
currentRoot.getChildren().add(currentWorld);
currentMeshGroup = new Group();
currentWorld.getChildren().add(currentMeshGroup);
AmbientLight al = new AmbientLight(Color.WHITE);
currentRoot.getChildren().add(al);
PerspectiveCamera currentCamera = new PerspectiveCamera(true);
currentCamera.setNearClip(0.1);
currentCamera.setFarClip(10000d);
currentCamera.translateZProperty().bind(translateZ);
Rotate cameraZRotation = new Rotate();
Rotate cameraXRotation = new Rotate();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
cameraZRotation.angleProperty().bind(cameraXRot);
cameraXRotation.angleProperty().bind(cameraYRot);
currentWorld.getTransforms().add(cameraXRotation);
currentWorld.getTransforms().add(cameraZRotation);
currentRoot.getChildren().add(currentCamera);
currentScene.setCamera(currentCamera);
setupMeshViewControls(view1);
}
private void setupMeshViewControls(Pane canvas) {
canvas.setOnMousePressed(me -> {
if (enableGuiControls && me.getButton() == MouseButton.PRIMARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
});
canvas.setOnScroll(se -> translateZ.set(translateZ.get() + se.getDeltaY() / 10d));
canvas.setOnMouseDragged(me -> {
if (enableGuiControls && me.getButton() == MouseButton.PRIMARY) {
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
cameraXRot.set(cameraXRot.get() + ((deltaX / 3d) % 360));
cameraYRot.set(cameraYRot.get() + ((deltaY / 3d) % 360));
}
});
}
private void buildNext3dView() {
Group nextRoot = new Group();
SubScene nextScene = new SubScene(nextRoot, 500, 300, true, SceneAntialiasing.BALANCED);
nextScene.heightProperty().bind(view2.heightProperty());
nextScene.widthProperty().bind(view2.widthProperty());
nextScene.setFill(Color.AZURE);
view2.getChildren().add(nextScene);
nextWorld = new Group();
nextRoot.getChildren().add(nextWorld);
nextMeshGroup = new Group();
nextWorld.getChildren().add(nextMeshGroup);
AmbientLight al = new AmbientLight(Color.WHITE);
nextRoot.getChildren().add(al);
PerspectiveCamera nextCamera = new PerspectiveCamera(true);
nextCamera.setNearClip(0.1);
nextCamera.setFarClip(10000d);
nextCamera.translateZProperty().bind(translateZ);
Rotate cameraZRotation = new Rotate();
Rotate cameraXRotation = new Rotate();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
cameraZRotation.angleProperty().bind(cameraXRot);
cameraXRotation.angleProperty().bind(cameraYRot);
nextWorld.getTransforms().add(cameraXRotation);
nextWorld.getTransforms().add(cameraZRotation);
nextRoot.getChildren().add(nextCamera);
nextScene.setCamera(nextCamera);
}
@Override
public Optional<HBox> getToolbar() {
return Optional.empty();
}
@Override
public Node getMainScreen() {
return pane;
}
@Override
public Image getViewLogo() {
return image;
}
@Override
public void initializeView(MainWindow mainWindow) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("geneticGUI.fxml"));
loader.setController(this);
try {
pane = loader.load();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
try {
setupViews();
} catch (CityGmlParseException | InvalidGmlFileException e) {
e.printStackTrace();
}
}
private void setupViews() throws CityGmlParseException, InvalidGmlFileException {
ParserConfiguration parserConfig = new ParserConfiguration(8, false);
// CityDoctorModel model1 = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml",
// parserConfig);
CityDoctorModel model1 = CityGmlParser.parseCityGmlFile("src/test/resources/TestBuilding3.gml",
parserConfig);
currentChecker = new Checker(model1);
currentChecker.runChecks();
Building building = model1.getBuildings().get(0);
currentGeometry = building.getGeometries().get(0);
currentFeature = building;
showCurrentGeometry();
}
public void zoomOutForBoundingBox(BoundingBox b) {
double cameraDistance = 2.0d;
Vector3d objectSizes = b.getDiagonal();
double objectSize = max(objectSizes.getX(), objectSizes.getY(), objectSizes.getZ());
double cameraView = 2.0d * Math.tan(0.5d * Math.toRadians(30)); // Visible height 1 meter in front
double distance = cameraDistance * objectSize / cameraView; // Combined wanted distance from the object
distance += 0.5d * objectSize; // Estimated offset from the center to the outside of the object
translateZ.set(-distance);
}
private double max(double a, double b, double c) {
double temp = Math.max(a, b);
return Math.max(temp, c);
}
public Group getCurrentMeshGroup() {
return currentMeshGroup;
}
@Override
public void onHide() {
// TODO Auto-generated method stub
}
@Override
public void onShow(CityDoctorModel model, Checker checker) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
ViewRegistration.registerView(new GeneticsGUI());
HealerGUI.main(args);
}
public List<Image> createScreenshots(Geometry geom, double zoomLevel, int xRotSteps, int yRotSteps) {
currentGeometry = geom;
currentTriangulatedGeometry = TriangulatedGeometry.of(currentGeometry);
currentTriangulatedGeometry.setCullFace(CullFace.BACK);
currentTriangulatedGeometry.setDrawMode(DrawMode.FILL);
setZoomLevel(zoomLevel);
getCurrentMeshGroup().getChildren().clear();
getCurrentMeshGroup().getChildren().addAll(currentTriangulatedGeometry.getMeshes());
return makeScreenshots(xRotSteps, yRotSteps);
}
private void setZoomLevel(double zoomLevel) {
translateZ.set(zoomLevel);
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic.gui;
import java.util.Objects;
import javafx.scene.image.Image;
import javafx.scene.image.PixelReader;
import javafx.scene.paint.Color;
public class ImageComparison {
private ImageComparison() {
// only static access
}
public static double compare(Image image1, Image image2) {
assertPredefinitions(image1, image2);
PixelReader pr1 = image1.getPixelReader();
Objects.requireNonNull(pr1);
PixelReader pr2 = image2.getPixelReader();
Objects.requireNonNull(pr2);
double pictureDif = 0;
for (int i = 0; i < image1.getWidth(); i++) {
for (int j = 0; j < image2.getHeight(); j++) {
double pixelComp = 1.0;
Color c1 = pr1.getColor(i, j);
Color c2 = pr2.getColor(i, j);
double redDif = Math.abs(c1.getRed() - c2.getRed());
double blueDif = Math.abs(c1.getBlue() - c2.getBlue());
double greenDif = Math.abs(c1.getGreen() - c2.getGreen());
if (redDif > 0.05) {
pixelComp -= 1/3d;
}
if (blueDif > 0.05) {
pixelComp -= 1/3d;
}
if (greenDif > 0.05) {
pixelComp -= 1/3d;
}
pictureDif += pixelComp;
}
}
double dimension = image1.getWidth() * image1.getHeight();
pictureDif = Math.max(0, dimension - 50 * (dimension - pictureDif));
return pictureDif / dimension;
}
private static void assertPredefinitions(Image image1, Image image2) {
if (image1.getProgress() != 1.0 || image2.getProgress() != 1.0) {
throw new IllegalArgumentException("Pictures are not fully loaded");
}
if (image1.getWidth() != image2.getWidth() || image1.getHeight() != image2.getHeight()) {
throw new IllegalArgumentException("Pictures have to be the same size");
}
}
}
package de.hft.stuttgart.citydoctor2.healer.genetic.gui;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.healer.Healer;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
import de.hft.stuttgart.citydoctor2.healer.genetic.FitnessEvaluation;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.Copy;
import javafx.scene.image.Image;
public class VisualFitness implements FitnessEvaluation<HealingPlan> {
private int xRotSteps = 6;
private int yRotSteps = 12;
private GeneticsGUI gui;
private Geometry geom;
private List<Image> originalImages;
private Checker c;
private double distance;
public VisualFitness(GeneticsGUI gui, Geometry geom, Checker c) {
this.gui = gui;
this.geom = geom;
this.c = c;
BoundingBox b = geom.calculateBoundingBox();
double cameraDistance = 2.0d;
Vector3d objectSizes = b.getDiagonal();
double objectSize = max(objectSizes.getX(), objectSizes.getY(), objectSizes.getZ());
double cameraView = 2.0d * Math.tan(0.5d * Math.toRadians(30)); // Visible height 1 meter in front
distance = cameraDistance * objectSize / cameraView;
distance += 0.5d * objectSize; // Estimated offset from the center to the outside of the object
distance = -distance;
originalImages = gui.createScreenshots(geom, distance, xRotSteps, yRotSteps);
}
private double max(double a, double b, double c) {
double temp = Math.max(a, b);
return Math.max(temp, c);
}
@Override
public double evaluate(HealingPlan plan) {
Healer healer = new Healer(c, plan);
Geometry copy = Copy.copy(geom);
healer.heal(copy, copy.getParent());
if (copy.containsAnyError()) {
System.out.println(plan);
System.out.println("Fitness: 0");
return 0;
}
List<Image> compareImages = gui.createScreenshots(copy, distance, xRotSteps, yRotSteps);
if (originalImages.size() != compareImages.size()) {
throw new IllegalStateException("Number of images differs");
}
double value = 0;
for (int i = 0; i < originalImages.size(); i++) {
Image orig = originalImages.get(i);
Image compare = compareImages.get(i);
value += ImageComparison.compare(orig, compare);
}
double fitness = value / (xRotSteps * yRotSteps);
System.out.println("Fitness: " + fitness);
return fitness;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.221" xmlns:fx="http://javafx.com/fxml/1">
<children>
<SplitPane dividerPositions="0.7" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" orientation="VERTICAL" VBox.vgrow="ALWAYS">
<items>
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
<children>
<Pane fx:id="view1" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" HBox.hgrow="ALWAYS" />
<Pane fx:id="view2" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" HBox.hgrow="ALWAYS" />
</children>
</HBox>
<HBox>
<children>
<Button fx:id="test" mnemonicParsing="false" text="Button" />
</children>
</HBox>
</items>
</SplitPane>
</children>
</VBox>
package de.hft.stuttgart.citydoctor2.healer.genetic;
import static org.junit.Assert.*;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.healer.HealingMethods;
import de.hft.stuttgart.citydoctor2.healer.HealingPlan;
public class GeneticAlgorithmTest {
@Test
public void test() {
Crossover<HealingPlan> cross = new OrderCrossover();
Mutation<HealingPlan> mutation = new NoMutation();
FitnessEvaluation<HealingPlan> fitness = new RandomFitness();
Heuristic<HealingPlan> heuristic = new RandomHeuristic(HealingMethods.getAvailableHealingMethods());
GeneticAlgorithm<HealingPlan> ga = GeneticAlgorithm.of(cross, mutation, heuristic, fitness);
ga.run();
assertNotNull(ga.getResult());
}
}
/*-
* 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.healer.genetic;
public class MetaFitnessValues {
int numberOfPolygonsWithPlanarityIssues = 0;
int numberOfPolygonsWithoutPlanarityIssues = 0;
int numberOfPolygonsWithOtherErrors = 0;
int numberOfPolygonsWithNoErrors = 0;
public void reset() {
numberOfPolygonsWithPlanarityIssues = 0;
numberOfPolygonsWithoutPlanarityIssues = 0;
numberOfPolygonsWithOtherErrors = 0;
numberOfPolygonsWithNoErrors = 0;
}
public MetaFitnessValues copy() {
MetaFitnessValues copy = new MetaFitnessValues();
copy.numberOfPolygonsWithNoErrors = numberOfPolygonsWithNoErrors;
copy.numberOfPolygonsWithOtherErrors = numberOfPolygonsWithOtherErrors;
copy.numberOfPolygonsWithoutPlanarityIssues = numberOfPolygonsWithoutPlanarityIssues;
copy.numberOfPolygonsWithPlanarityIssues = numberOfPolygonsWithPlanarityIssues;
return copy;
}
}
/*-
* 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.healer.genetic;
import static org.junit.Assert.assertFalse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import org.junit.Test;
import Jama.EigenvalueDecomposition;
import de.hft.stuttgart.citydoctor2.check.AbstractCheck;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
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.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.CovarianceMatrix;
import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.utils.Copy;
public class NonPlanarGenetic {
private static final int ERROR_PENALTY = 100000;
private Random r = new Random();
private Map<Integer, List<Long>> durationMap = new HashMap<>();
@Test
public void findVerticesForNonPlanarTest() throws CityGmlParseException, InvalidGmlFileException, IOException {
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
config.setNumberOfRoundingPlacesInGlobalParameters(8);
config.setSchematronFilePathInGlobalParameters(null);
config.getParserConfiguration().setUseLowMemoryConsumption(false);
List<Path> gmlFiles = getPathsForGmlFiles();
for (Path gmlFile : gmlFiles) {
System.out.println("Parsing File: " + gmlFile);
CityDoctorModel model = CityGmlParser.parseCityGmlFile(gmlFile.toString(),
config.getParserConfiguration());
Checker c = new Checker(config, model);
c.runChecks();
List<CheckError> errors = new ArrayList<>();
model.createFeatureStream().forEach(co -> co.collectContainedErrors(errors));
assertFalse(errors.isEmpty());
NonPlanarPolygonDistancePlaneError planarError = null;
for (CheckError err : errors) {
if (err instanceof NonPlanarPolygonDistancePlaneError) {
planarError = (NonPlanarPolygonDistancePlaneError) err;
break;
}
}
if (planarError == null) {
throw new IllegalStateException("File " + gmlFile + " does not contain a planarity error");
}
CityObject feature = getFeature(planarError.getPolygon());
Polygon polygon = planarError.getPolygon();
// ensure a gml id has been generated
polygon.getGmlId();
int vertexCount = polygon.getExteriorRing().getVertices().size();
List<Vertex> subList = polygon.getExteriorRing().getVertices().subList(0, vertexCount - 1);
System.out.println("Computing polygon: " + polygon.getGmlId() + " with " + subList.size() + " Vertices");
FitnessEvaluation<PolygonVertexList> fitnessFunction = createFitnessEvaluationFunction(c, feature);
// complete calculation of all possibilities
if (subList.size() == 7) {
System.out.println();
}
Individual<PolygonVertexList> bestOfBoth = null;
if (subList.size() > 12) {
System.out.println("Skipping complete calculation, too many vertices");
bestOfBoth = doGeneticAlgorithmCalculation(polygon, subList, fitnessFunction);
} else {
bestOfBoth = doCompleteCalculation(polygon, fitnessFunction, vertexCount, subList);
}
System.out.println();
// abstands diff, ebene ohne punkt und normale ausgleichsebene
// if (bestOfBoth.getFitness() > -9000) {
// write only those that do not have errors in neighbor polygons
// errors in neighbors mean -10000 so check for higher than 9000 to be sure
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("result.txt"), StandardOpenOption.APPEND,
StandardOpenOption.CREATE)) {
for (int i = 0; i < bestOfBoth.getObject().getUseVertex().length; i++) {
boolean used = bestOfBoth.getObject().getUseVertex()[i];
Vertex v = bestOfBoth.getObject().getVertices().get(i);
int numberOfAdjacentPolygons = 0;
int numberOfPolygonsWithPlanarityIssues = 0;
int numberOfPolygonsWithOtherErrors = 0;
double distanceToPlane = planarError.getPlane().getDistance(v);
Set<Polygon> adjacentPolygons = v.getAdjacentPolygons(polygon.getParent());
numberOfAdjacentPolygons = adjacentPolygons.size();
for (Polygon adjacentPolygon : adjacentPolygons) {
List<CheckError> adjacentPolygonErrors = new ArrayList<>();
adjacentPolygon.collectContainedErrors(adjacentPolygonErrors);
// impossible to have duplicate errors so no Set filter on top
for (CheckError err : adjacentPolygonErrors) {
if (err instanceof NonPlanarPolygonDistancePlaneError) {
numberOfPolygonsWithPlanarityIssues++;
} else {
numberOfPolygonsWithOtherErrors++;
}
}
}
if (used) {
writer.write("1");
} else {
writer.write("0");
}
List<Vertex> vertices = new ArrayList<>();
for (Vertex vertexForPlan : subList) {
if (vertexForPlan == v) {
continue;
}
vertices.add(vertexForPlan);
}
Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Vector3d eigenvalues = OrthogonalRegressionPlane.getEigenvalues(ed);
Plane plane = OrthogonalRegressionPlane.calculatePlane(centroid, ed, eigenvalues);
double distanceDifferenceWithoutPoint = distanceToPlane - plane.getDistance(v);
writer.write(" 1:");
writer.write(Integer.toString(numberOfAdjacentPolygons));
writer.write(" 2:");
writer.write(Integer.toString(numberOfPolygonsWithPlanarityIssues));
writer.write(" 3:");
writer.write(Integer.toString(numberOfPolygonsWithOtherErrors));
writer.write(" 4:");
writer.write(Double.toString(distanceToPlane));
writer.write(" 5:");
writer.write(Double.toString(distanceDifferenceWithoutPoint));
writer.write('\n');
}
}
// }
}
for (Entry<Integer, List<Long>> e : durationMap.entrySet()) {
long sum = 0;
for (Long l : e.getValue()) {
sum += l;
}
System.out.println(e.getKey() + " Vertices took on average: " + (sum / (float) e.getValue().size()) + "ms");
}
}
private Individual<PolygonVertexList> doGeneticAlgorithmCalculation(Polygon polygon, List<Vertex> subList, FitnessEvaluation<PolygonVertexList> fitnessFunction)
throws IOException {
Mutation<PolygonVertexList> mutation = createMutationFunction();
Crossover<PolygonVertexList> crossOver = createCrossoverFunction();
Heuristic<PolygonVertexList> randomHeuristic = createHeursiticFunction(polygon, subList);
GeneticAlgorithm<PolygonVertexList> algo = GeneticAlgorithm.of(crossOver, mutation, randomHeuristic, fitnessFunction);
long startGen = System.currentTimeMillis();
algo.run();
long deltaGen = System.currentTimeMillis() - startGen;
System.out.println("Genetic Algo took: " + deltaGen + " ms");
durationMap.compute(subList.size(), (key, value) -> {
if (value == null) {
value = new ArrayList<>();
}
value.add(deltaGen);
return value;
});
PolygonVertexList result = algo.getResult().getObject();
printUsage(result.getUseVertex());
return algo.getResult();
}
private Heuristic<PolygonVertexList> createHeursiticFunction(Polygon polygon, List<Vertex> subList) {
Heuristic<PolygonVertexList> randomHeuristic = () -> {
PolygonVertexList vertexList = new PolygonVertexList(polygon, subList);
boolean[] useVertex = vertexList.getUseVertex();
for (int i = 0; i < useVertex.length; i++) {
useVertex[i] = r.nextBoolean();
}
validateIndividual(vertexList);
return vertexList;
};
return randomHeuristic;
}
private Crossover<PolygonVertexList> createCrossoverFunction() {
Crossover<PolygonVertexList> crossOver = (p1, p2) -> {
PolygonVertexList result = new PolygonVertexList(p1.getPolygon(), p1.getVertices());
int splitIndex = r.nextInt(p1.getUseVertex().length);
boolean[] array = result.getUseVertex();
for (int i = 0; i < splitIndex; i++) {
array[i] = p1.getUseVertex()[i];
}
for (int i = splitIndex; i < array.length; i++) {
array[i] = p2.getUseVertex()[i];
}
validateIndividual(result);
return result;
};
return crossOver;
}
private Mutation<PolygonVertexList> createMutationFunction() {
Mutation<PolygonVertexList> mutation = p -> {
int doSomethingValue = r.nextInt(100);
if (doSomethingValue > 50) {
// don't do anything
return p;
}
int index = r.nextInt(p.getUseVertex().length);
p.getUseVertex()[index] = !p.getUseVertex()[index];
validateIndividual(p);
return p;
};
return mutation;
}
private Individual<PolygonVertexList> doCompleteCalculation(Polygon polygon, FitnessEvaluation<PolygonVertexList> fitnessFunction, int vertexCount,
List<Vertex> subList) {
System.out.println("Doing complete calculation");
long startComplete = System.currentTimeMillis();
double highestFitness = Double.NEGATIVE_INFINITY;
int computeCount = 0;
PolygonVertexList bestIndividual = null;
for (int i = 7; i < Math.pow(2, vertexCount - 1) - 1; i++) {
PolygonVertexList individual = new PolygonVertexList(polygon, subList);
int usedVertexCount = 0;
boolean[] useVertices = individual.getUseVertex();
for (int j = 0; j < vertexCount - 1; j++) {
long l = (i >> j) & 1;
if (l == 1) {
usedVertexCount++;
useVertices[j] = true;
} else {
useVertices[j] = false;
}
}
if (usedVertexCount >= 3) {
double fitnessValue = fitnessFunction.evaluate(individual);
computeCount++;
if (fitnessValue > highestFitness) {
highestFitness = fitnessValue;
System.out.println("Best Fitness: " + highestFitness);
bestIndividual = individual;
}
}
if (usedVertexCount == vertexCount - 1) {
throw new IllegalStateException("All values should not be computed");
}
}
long deltaComplete = System.currentTimeMillis() - startComplete;
System.out.println("End. Best Fitness: " + highestFitness + " Individual: " + bestIndividual);
System.out.println("Computed " + computeCount + " individuals");
if (bestIndividual != null) {
printUsage(bestIndividual.getUseVertex());
}
System.out.println("Complete calculation took: " + deltaComplete + "ms");
durationMap.compute(subList.size(), (key, value) -> {
if (value == null) {
value = new ArrayList<>();
}
value.add(deltaComplete);
return value;
});
Individual<PolygonVertexList> bestResult = new Individual<PolygonVertexList>(bestIndividual);
bestResult.setFitness(highestFitness);
return bestResult;
}
private FitnessEvaluation<PolygonVertexList> createFitnessEvaluationFunction(Checker c, CityObject feature) {
FitnessEvaluation<PolygonVertexList> fitness = p -> {
List<Vertex> vertices = new ArrayList<>();
for (int i = 0; i < p.getVertices().size(); i++) {
if (p.getUseVertex()[i]) {
vertices.add(p.getVertices().get(i));
}
}
Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Vector3d eigenvalues = OrthogonalRegressionPlane.getEigenvalues(ed);
Plane plane = OrthogonalRegressionPlane.calculatePlane(centroid, ed, eigenvalues);
// check if it produces errors somewhere else
// first repair
// only repair clone
CityObject copy = Copy.copy(feature);
Polygon copyP = findPolygonInFeature(copy, p.getPolygon());
Set<Vertex> allVertices = new HashSet<>();
// collect all vertices, eliminating duplicates
collectVertices(copyP, allVertices);
// project each vertex to the plane and change the coordinates of existing
// vertices
for (Vertex v : allVertices) {
Vector3d newPoint = plane.projectPointToPlane(v);
v.setX(newPoint.getX());
v.setY(newPoint.getY());
v.setZ(newPoint.getZ());
}
// update edges
copy.prepareForChecking();
// recheck
c.executeChecksForCheckable(copy);
List<CheckError> remainingErrors = new ArrayList<>();
for (Polygon poly : collectNeighborPolygons(copyP)) {
poly.collectContainedErrors(remainingErrors);
}
double distanceSum = 0;
distanceSum = remainingErrors.size() * ERROR_PENALTY;
// check the distance of the original vertices to calculate distance
allVertices = new HashSet<>();
// collect all vertices, eliminating duplicates
collectVertices(p.getPolygon(), allVertices);
for (Vertex v : allVertices) {
double distance = plane.getDistance(v);
distanceSum += distance;
}
// negative, as higher is better but more distance is worse
return -distanceSum;
};
return fitness;
}
public long factorial(int n) {
long fact = 1;
for (int i = 2; i <= n; i++) {
fact = fact * i;
}
return fact;
}
private List<Path> getPathsForGmlFiles() throws IOException {
Path entry = Paths.get("C:/Job/CityGML Files/GE_P_NON_PLANAR_POLYGON_DISTANCE_PLANE");
List<Path> paths = new ArrayList<>();
Files.list(entry).forEach(path -> collectGmlFiles(path, paths));
return paths;
}
private void collectGmlFiles(Path path, List<Path> paths) {
if (Files.isRegularFile(path) && path.getFileName().toString().endsWith("gml")) {
paths.add(path);
} else if (Files.isDirectory(path)) {
try {
Files.list(path).forEach(p -> collectGmlFiles(p, paths));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
private void printUsage(boolean[] useVertex) {
System.out.println(createUseString(useVertex));
}
private String createUseString(boolean[] useVertex) {
StringBuilder builder = new StringBuilder();
for (boolean b : useVertex) {
if (b) {
builder.append('1');
} else {
builder.append('0');
}
}
return builder.toString();
}
private void validateIndividual(PolygonVertexList p) {
int index;
// count the number of used vertices
int useCount = 0;
for (boolean b : p.getUseVertex()) {
if (b) {
useCount++;
}
}
if (useCount < 3) {
// at least 3 points should be used
for (int i = 0; i < 3 - useCount; i++) {
index = r.nextInt(p.getUseVertex().length);
// search for a false index all the time
while (p.getUseVertex()[index]) {
index = r.nextInt(p.getUseVertex().length);
}
p.getUseVertex()[index] = true;
}
} else if (useCount == p.getVertices().size()) {
// not all vertices should be used
// flip one randomly back
index = r.nextInt(p.getUseVertex().length);
p.getUseVertex()[index] = false;
}
}
private Polygon findPolygonInFeature(CityObject copy, Polygon polygon) {
Polygon[] result = new Polygon[1];
copy.accept(new AbstractCheck() {
@Override
public void check(Polygon p) {
if (p.getGmlId().equals(polygon.getGmlId())) {
result[0] = p;
}
}
});
return result[0];
}
private CityObject getFeature(Polygon polygon) {
CityObject parent = polygon.getParent().getParent();
if (parent instanceof BoundarySurface) {
BoundarySurface bs = (BoundarySurface) parent;
parent = bs.getParent();
if (parent instanceof Installation) {
Installation bi = (Installation) parent;
parent = bi.getParent();
}
} else if (parent instanceof Installation) {
Installation bi = (Installation) parent;
parent = bi.getParent();
}
return parent;
}
private Set<Polygon> collectNeighborPolygons(Polygon polygon) {
Geometry geometry;
if (polygon.isLinkedTo()) {
geometry = polygon.getLinkedFromPolygon().getParent();
} else {
geometry = polygon.getParent();
}
Set<Polygon> polygons = new HashSet<>();
for (Vertex v : polygon.getExteriorRing().getVertices()) {
polygons.addAll(v.getAdjacentPolygons(geometry));
}
return polygons;
}
private void collectVertices(Polygon p, Set<Vertex> vertices) {
collectVertices(p.getExteriorRing(), vertices);
for (LinearRing lr : p.getInnerRings()) {
collectVertices(lr, vertices);
}
}
private void collectVertices(LinearRing ring, Set<Vertex> vertices) {
for (Vertex v : ring.getVertices()) {
vertices.add(v);
}
}
}
/*-
* 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.healer.genetic;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
public class PolygonVertexList {
private Polygon polygon;
private List<Vertex> vertices;
private boolean[] useVertex;
public PolygonVertexList(Polygon polygon, List<Vertex> vertices) {
this.polygon = polygon;
this.vertices = vertices;
useVertex = new boolean[vertices.size()];
}
public Polygon getPolygon() {
return polygon;
}
public List<Vertex> getVertices() {
return vertices;
}
public boolean[] getUseVertex() {
return useVertex;
}
}
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