Commit ffdae21a authored by Riegel's avatar Riegel
Browse files

Open Source release of CityDoctor GUI

parent a5a82382
Pipeline #10029 failed with stage
in 8 seconds
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<ns prefix="gml" uri="http://www.opengis.net/gml"/>
<ns prefix="bldg" uri="http://www.opengis.net/citygml/building/2.0"/>
<pattern>
<rule context="//*:Building">
<assert test="count(descendant::*:lod1Solid) &gt; 0 or count(descendant::*:lod2Solid) &gt; 0 or count(descendant::*:lod3Solid) &gt; 0 or count(descendant::*:lod4Solid) &gt; 0"><value-of select="@gml:id | @id"/>||||SE_ATTRIBUTE_MISSING||any solid</assert>
</rule>
<rule context="//*:BuildingPart">
<assert test="count(*:lod1Solid) = 1 or count(*:lod2Solid) = 1 or count(*:lod3Solid) = 1 or count(*:lod4Solid) = 1"><value-of select="ancestor::*:Building/@*:id"/>||<value-of select="@gml:id | @id"/>||SE_ATTRIBUTE_MISSING||any solid</assert>
</rule>
</pattern>
</schema>
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.14.1</version>
</parent>
<artifactId>CityDoctorHealerGenetic</artifactId>
<dependencies>
<dependency>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorHealerGUI</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
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;
}
}
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