Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
97a65114
Commit
97a65114
authored
Sep 10, 2024
by
Riegel
Browse files
Merge branch 'dev' into 'master'
Version 3.15.0 See merge request
!8
parents
99c8f6a8
5950ea5f
Pipeline
#10106
passed with stage
in 3 minutes and 15 seconds
Changes
589
Pipelines
1
Show whitespace changes
Inline
Side-by-side
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/common/checkForSolid.xml
0 → 100644
View file @
97a65114
<?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) > 0 or count(descendant::*:lod2Solid) > 0 or count(descendant::*:lod3Solid) > 0 or count(descendant::*:lod4Solid) > 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>
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/lin/assembly.xml
0 → 100644
View file @
97a65114
<assembly
xmlns=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"
>
<id>
zip
</id>
<formats>
<format>
zip
</format>
</formats>
<includeBaseDirectory>
false
</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>
app
</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>
${project.basedir}/src/assembly/lin
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
start.sh
</include>
</includes>
<filtered>
true
</filtered>
</fileSet>
<fileSet>
<directory>
${project.basedir}/src/assembly/common
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
checkForSolid.xml
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
<fileSet>
<directory>
${project.build.directory}/jre/jre-lin/runtime/${lin-jre}/
</directory>
<outputDirectory>
/runtime
</outputDirectory>
<includes>
<include>
**/*
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
</fileSets>
</assembly>
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/lin/start.sh
0 → 100644
View file @
97a65114
#!/bin/sh
./runtime/bin/java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.healer.gui.HealerGUI
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/mac/assembly.xml
0 → 100644
View file @
97a65114
<assembly
xmlns=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"
>
<id>
zip
</id>
<formats>
<format>
zip
</format>
</formats>
<includeBaseDirectory>
false
</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>
app
</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>
${project.basedir}/src/assembly/lin
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
start.sh
</include>
</includes>
<filtered>
true
</filtered>
</fileSet>
<fileSet>
<directory>
${project.basedir}/src/assembly/common
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
checkForSolid.xml
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
<fileSet>
<directory>
${project.build.directory}/jre/jre-mac/runtime/${mac-jre}/
</directory>
<outputDirectory>
/runtime
</outputDirectory>
<includes>
<include>
**/*
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
</fileSets>
</assembly>
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/no_runtime/assembly.xml
0 → 100644
View file @
97a65114
<assembly
xmlns=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"
>
<id>
zip
</id>
<formats>
<format>
zip
</format>
</formats>
<includeBaseDirectory>
false
</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>
app
</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>
${project.basedir}/src/assembly/no_runtime
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
start.bat
</include>
<include>
start.sh
</include>
</includes>
<filtered>
true
</filtered>
</fileSet>
<fileSet>
<directory>
${project.basedir}/src/assembly/common
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
checkForSolid.xml
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
</fileSets>
</assembly>
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/no_runtime/start.bat
0 → 100644
View file @
97a65114
java
-classpath
app
/
*;
plugins
/
*
de
.hft.stuttgart.citydoctor2.healer.gui.HealerGUI
pause
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/no_runtime/start.sh
0 → 100644
View file @
97a65114
#!/bin/sh
java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.healer.gui.HealerGUI
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/win/assembly.xml
0 → 100644
View file @
97a65114
<assembly
xmlns=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"
>
<id>
zip
</id>
<formats>
<format>
zip
</format>
</formats>
<includeBaseDirectory>
false
</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>
app
</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>
${project.basedir}/src/assembly/win
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
start.bat
</include>
</includes>
<filtered>
true
</filtered>
</fileSet>
<fileSet>
<directory>
${project.basedir}/src/assembly/common
</directory>
<outputDirectory>
/
</outputDirectory>
<includes>
<include>
checkForSolid.xml
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
<fileSet>
<directory>
${project.build.directory}/jre/jre-win/runtime/${win-jre}/
</directory>
<outputDirectory>
/runtime
</outputDirectory>
<includes>
<include>
**/*
</include>
</includes>
<filtered>
false
</filtered>
</fileSet>
</fileSets>
</assembly>
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/assembly/win/start.bat
0 → 100644
View file @
97a65114
"runtime/bin/java.exe"
-classpath
app
/
*;
plugins
/
*
de
.hft.stuttgart.citydoctor2.healer.gui.HealerGUI
pause
\ No newline at end of file
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/ChooseDialogTreeCell.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
javafx.scene.control.TreeCell
;
import
javafx.scene.paint.Color
;
public
class
ChooseDialogTreeCell
extends
TreeCell
<
SelectableFeature
>
{
@Override
protected
void
updateItem
(
SelectableFeature
item
,
boolean
empty
)
{
super
.
updateItem
(
item
,
empty
);
if
(!
empty
&&
item
!=
null
)
{
setTextFill
(
Color
.
RED
);
setText
(
item
.
getText
());
}
else
{
setText
(
null
);
setGraphic
(
null
);
}
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/ChooseFeatureDialog.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
java.io.IOException
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
de.hft.stuttgart.citydoctor2.gui.MainWindow
;
import
javafx.application.Platform
;
import
javafx.fxml.FXML
;
import
javafx.fxml.FXMLLoader
;
import
javafx.scene.Node
;
import
javafx.scene.Parent
;
import
javafx.scene.Scene
;
import
javafx.scene.control.Button
;
import
javafx.scene.control.SelectionMode
;
import
javafx.scene.control.TabPane
;
import
javafx.scene.control.TreeItem
;
import
javafx.scene.control.TreeView
;
import
javafx.scene.image.Image
;
import
javafx.stage.Modality
;
import
javafx.stage.Stage
;
import
javafx.stage.Window
;
public
class
ChooseFeatureDialog
{
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
ChooseFeatureDialog
.
class
);
private
Stage
stage
;
private
HealerController
controller
;
@FXML
private
TreeView
<
SelectableFeature
>
buildingsView
;
@FXML
private
Button
okButton
;
@FXML
private
Button
cancelButton
;
@FXML
private
TabPane
tabPane
;
public
ChooseFeatureDialog
(
Window
parent
,
HealerController
controller
)
{
FXMLLoader
loader
=
new
FXMLLoader
(
getClass
().
getResource
(
"ChooseErrorFeatureDialog.fxml"
));
loader
.
setController
(
this
);
this
.
controller
=
controller
;
try
{
Parent
dialogParent
=
loader
.
load
();
stage
=
new
Stage
();
stage
.
getIcons
()
.
add
(
new
Image
(
MainWindow
.
class
.
getResourceAsStream
(
"icons/CityDoctor-Logo-rot_klein.jpg"
)));
stage
.
setScene
(
new
Scene
(
dialogParent
));
stage
.
initOwner
(
parent
);
stage
.
initModality
(
Modality
.
APPLICATION_MODAL
);
stage
.
setTitle
(
"Select Feature Geometry"
);
}
catch
(
IOException
e
)
{
logger
.
catching
(
e
);
}
}
public
void
initialize
()
{
tabPane
.
getSelectionModel
().
selectedItemProperty
().
addListener
((
obs
,
oldV
,
newV
)
->
{
Node
content
=
oldV
.
getContent
();
if
(
content
instanceof
TreeView
<?>)
{
TreeView
<?>
view
=
(
TreeView
<?>)
content
;
view
.
getSelectionModel
().
clearSelection
();
okButton
.
setDisable
(
true
);
}
else
{
throw
new
IllegalStateException
(
"Programming error, tab does not contain TreeView as content"
);
}
});
okButton
.
setDisable
(
true
);
okButton
.
setOnAction
(
ae
->
{
Node
content
=
tabPane
.
getSelectionModel
().
getSelectedItem
().
getContent
();
if
(
content
instanceof
TreeView
<?>)
{
TreeView
<?>
view
=
(
TreeView
<?>)
content
;
Object
selectedItem
=
view
.
getSelectionModel
().
getSelectedItem
().
getValue
();
if
(
selectedItem
instanceof
SelectableFeature
)
{
controller
.
featureSelectionConfirmed
((
SelectableFeature
)
selectedItem
);
stage
.
close
();
}
else
{
throw
new
IllegalStateException
(
"Programming error, TreeView does not contain SelectableFeature"
);
}
}
else
{
throw
new
IllegalStateException
(
"Programming error, tab does not contain TreeView as content"
);
}
});
cancelButton
.
setOnAction
(
ae
->
stage
.
close
());
buildingsView
.
setRoot
(
new
TreeItem
<>(
null
));
buildingsView
.
setShowRoot
(
false
);
buildingsView
.
getSelectionModel
().
setSelectionMode
(
SelectionMode
.
SINGLE
);
buildingsView
.
getSelectionModel
().
selectedItemProperty
()
.
addListener
((
obs
,
oldV
,
newV
)
->
controller
.
itemSelected
(
newV
,
this
));
buildingsView
.
setCellFactory
(
param
->
new
ChooseDialogTreeCell
());
}
public
void
show
()
{
Platform
.
runLater
(()
->
stage
.
showAndWait
());
}
public
TreeView
<
SelectableFeature
>
getBuildingsTree
()
{
return
buildingsView
;
}
public
void
setOkButtonEnabled
(
boolean
enabled
)
{
okButton
.
setDisable
(!
enabled
);
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
java.util.ArrayList
;
import
java.util.EnumMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Optional
;
import
java.util.Set
;
import
de.hft.stuttgart.citydoctor2.check.Check
;
import
de.hft.stuttgart.citydoctor2.check.CheckError
;
import
de.hft.stuttgart.citydoctor2.check.CheckId
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.Checker
;
import
de.hft.stuttgart.citydoctor2.check.Requirement
;
import
de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding
;
import
de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface
;
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.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.GeometryType
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinkedPolygon
;
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.gui.AutoProWindow
;
import
de.hft.stuttgart.citydoctor2.gui.HighlightController
;
import
de.hft.stuttgart.citydoctor2.gui.ListErrorVisitor
;
import
de.hft.stuttgart.citydoctor2.gui.TriangulatedGeometry
;
import
de.hft.stuttgart.citydoctor2.healer.Healer
;
import
de.hft.stuttgart.citydoctor2.optimization.MeshGenerator
;
import
de.hft.stuttgart.citydoctor2.utils.Copy
;
import
javafx.collections.ObservableList
;
import
javafx.scene.control.TextInputDialog
;
import
javafx.scene.control.TreeItem
;
import
javafx.scene.shape.CullFace
;
import
javafx.scene.shape.DrawMode
;
public
class
HealerController
{
private
CityDoctorModel
model
;
private
HealerView
healerView
;
private
CityObject
currentFeature
;
private
Geometry
currentGeometry
;
private
CityObject
nextFeature
;
private
Geometry
nextGeometry
;
private
Checker
checker
;
private
CullFace
culling
=
CullFace
.
BACK
;
private
DrawMode
drawMode
=
DrawMode
.
FILL
;
private
TriangulatedGeometry
nextTriangulatedGeometry
;
private
TriangulatedGeometry
currentTriangulatedGeometry
;
private
ListErrorVisitor
currentErrorVisitor
;
private
ListErrorVisitor
nextErrorVisitor
;
private
HighlightController
currentHighlights
;
private
HighlightController
nextHighlights
;
private
SolidInjectorDialog
injectorDialog
;
public
HealerController
(
HealerView
healerView
)
{
this
.
healerView
=
healerView
;
this
.
currentHighlights
=
new
HighlightController
(
healerView
.
getCurrentWorld
());
this
.
nextHighlights
=
new
HighlightController
(
healerView
.
getNextWorld
());
currentErrorVisitor
=
new
ListErrorVisitor
(
currentHighlights
);
nextErrorVisitor
=
new
ListErrorVisitor
(
nextHighlights
);
healerView
.
translateZProperty
().
addListener
((
obs
,
oldV
,
newV
)
->
{
currentHighlights
.
changeScaling
(-
newV
.
doubleValue
());
nextHighlights
.
changeScaling
(-
newV
.
doubleValue
());
});
}
public
void
showSimplyfication
()
{
if
(
model
==
null
)
{
return
;
}
model
.
createFeatureStream
().
forEach
(
CityObject:
:
prepareForChecking
);
AutoProWindow
window
=
new
AutoProWindow
(
model
);
window
.
setLocationRelativeTo
(
null
);
window
.
setVisible
(
true
);
}
public
void
setCurrentModel
(
CityDoctorModel
model
)
{
if
(
this
.
model
!=
model
)
{
healerView
.
getCurrentMeshGroup
().
getChildren
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
getCurrentErrorList
().
getItems
().
clear
();
}
this
.
model
=
model
;
}
public
void
selectFeatureClicked
()
{
if
(
model
==
null
)
{
return
;
}
ChooseFeatureDialog
dialog
=
healerView
.
getChooseFeatureDialog
();
dialog
.
getBuildingsTree
().
getRoot
().
getChildren
().
clear
();
model
.
getBuildings
().
forEach
(
b
->
{
if
(
hasBuildingGeometryErrors
(
b
))
{
addBuildingToTree
(
b
,
dialog
);
}
});
dialog
.
show
();
}
private
boolean
hasBuildingGeometryErrors
(
Building
b
)
{
boolean
hasError
=
hasAbstractBuildingGeometryErrors
(
b
);
if
(
hasError
)
{
return
true
;
}
for
(
BuildingPart
bp
:
b
.
getBuildingParts
())
{
if
(
hasAbstractBuildingGeometryErrors
(
bp
))
{
return
true
;
}
}
return
false
;
}
private
boolean
hasAbstractBuildingGeometryErrors
(
AbstractBuilding
b
)
{
for
(
Geometry
geom
:
b
.
getGeometries
())
{
if
(
geom
.
containsAnyError
())
{
return
true
;
}
}
if
(
doBoundarySurfaceHaveGeometryErrors
(
b
.
getBoundarySurfaces
()))
{
return
true
;
}
return
doBuildingInstallationsContainGeometryErrors
(
b
.
getBuildingInstallations
());
}
private
void
addBuildingToTree
(
Building
b
,
ChooseFeatureDialog
dialog
)
{
TreeItem
<
SelectableFeature
>
buildingItem
=
new
TreeItem
<>(
new
SelectableFeature
(
b
.
getGmlId
().
getGmlString
()));
dialog
.
getBuildingsTree
().
getRoot
().
getChildren
().
add
(
buildingItem
);
buildingItem
.
setExpanded
(
true
);
buildGeometryTreeItems
(
b
,
buildingItem
);
buildBoundarySurfaceItems
(
b
.
getBoundarySurfaces
(),
buildingItem
);
buildBuildingInstallationItems
(
b
.
getBuildingInstallations
(),
buildingItem
);
}
private
void
buildBuildingInstallationItems
(
List
<
Installation
>
buildingInstallations
,
TreeItem
<
SelectableFeature
>
buildingItem
)
{
boolean
hasBiError
=
doBuildingInstallationsContainGeometryErrors
(
buildingInstallations
);
if
(
hasBiError
)
{
TreeItem
<
SelectableFeature
>
allInstItem
=
new
TreeItem
<>(
new
SelectableFeature
(
"BuildingInstallations"
));
buildingItem
.
getChildren
().
add
(
allInstItem
);
for
(
Installation
bi
:
buildingInstallations
)
{
if
(!
hasBuildingInstallationGeometryError
(
bi
))
{
continue
;
}
TreeItem
<
SelectableFeature
>
instItem
=
new
TreeItem
<>(
new
SelectableFeature
(
bi
.
getGmlId
().
getGmlString
()));
allInstItem
.
getChildren
().
add
(
instItem
);
buildBoundarySurfaceItems
(
bi
.
getBoundarySurfaces
(),
instItem
);
}
}
}
private
boolean
doBuildingInstallationsContainGeometryErrors
(
List
<
Installation
>
buildingInstallations
)
{
for
(
Installation
bi
:
buildingInstallations
)
{
boolean
hasError
=
hasBuildingInstallationGeometryError
(
bi
);
if
(
hasError
)
{
return
true
;
}
}
return
false
;
}
private
boolean
hasBuildingInstallationGeometryError
(
Installation
bi
)
{
for
(
Geometry
geom
:
bi
.
getGeometries
())
{
if
(
geom
.
containsAnyError
())
{
return
true
;
}
}
return
doBoundarySurfaceHaveGeometryErrors
(
bi
.
getBoundarySurfaces
());
}
private
void
buildBoundarySurfaceItems
(
List
<
BoundarySurface
>
boundarySurfaces
,
TreeItem
<
SelectableFeature
>
root
)
{
boolean
hasBsError
=
doBoundarySurfaceHaveGeometryErrors
(
boundarySurfaces
);
if
(
hasBsError
)
{
TreeItem
<
SelectableFeature
>
surfacesItem
=
new
TreeItem
<>(
new
SelectableFeature
(
"BoundarySurfaces"
));
root
.
getChildren
().
add
(
surfacesItem
);
// dummy
TreeItem
<
SelectableFeature
>
dummyItem
=
new
TreeItem
<>(
null
);
surfacesItem
.
getChildren
().
add
(
dummyItem
);
// expand listener to actually load the boundary surfaces
// this is for saving memory
surfacesItem
.
expandedProperty
().
addListener
((
obs
,
oldV
,
newV
)
->
{
if
(
Boolean
.
TRUE
.
equals
(
newV
)
&&
surfacesItem
.
getChildren
().
size
()
==
1
)
{
surfacesItem
.
getChildren
().
clear
();
for
(
BoundarySurface
bs
:
boundarySurfaces
)
{
if
(
hasBoundarySurfaceGeometryError
(
bs
))
{
TreeItem
<
SelectableFeature
>
surfaceItem
=
new
TreeItem
<>(
new
SelectableFeature
(
bs
.
getGmlId
().
getGmlString
()
+
" ["
+
bs
.
getType
()
+
"]"
));
surfacesItem
.
getChildren
().
add
(
surfaceItem
);
buildGeometryTreeItems
(
bs
,
surfaceItem
);
}
}
}
});
}
}
private
boolean
hasBoundarySurfaceGeometryError
(
BoundarySurface
bs
)
{
for
(
Geometry
geom
:
bs
.
getGeometries
())
{
if
(
geom
.
containsAnyError
())
{
return
true
;
}
}
return
false
;
}
private
void
buildGeometryTreeItems
(
CityObject
co
,
TreeItem
<
SelectableFeature
>
root
)
{
for
(
Geometry
geom
:
co
.
getGeometries
())
{
if
(!
geom
.
containsAnyError
())
{
continue
;
}
TreeItem
<
SelectableFeature
>
geomItem
=
new
TreeItem
<>(
new
SelectableGeometry
(
createGeometryText
(
geom
),
geom
,
co
));
root
.
getChildren
().
add
(
geomItem
);
}
}
private
boolean
doBoundarySurfaceHaveGeometryErrors
(
List
<
BoundarySurface
>
boundarySurfaces
)
{
for
(
BoundarySurface
bs
:
boundarySurfaces
)
{
boolean
hasError
=
hasBoundarySurfaceGeometryError
(
bs
);
if
(
hasError
)
{
return
true
;
}
}
return
false
;
}
private
String
createGeometryText
(
Geometry
geom
)
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"Geometry ["
);
sb
.
append
(
geom
.
getType
());
sb
.
append
(
", "
);
sb
.
append
(
geom
.
getLod
());
if
(
geom
.
getGmlId
().
isGenerated
())
{
sb
.
append
(
"]"
);
}
else
{
sb
.
append
(
"] "
);
sb
.
append
(
geom
.
getGmlId
());
}
return
sb
.
toString
();
}
public
void
itemSelected
(
TreeItem
<
SelectableFeature
>
item
,
ChooseFeatureDialog
dialog
)
{
if
(
item
==
null
)
{
return
;
}
SelectableFeature
value
=
item
.
getValue
();
dialog
.
setOkButtonEnabled
(
value
.
isGeometry
());
}
public
void
setCurrentChecker
(
Checker
checker
)
{
this
.
checker
=
checker
;
}
public
void
featureSelectionConfirmed
(
SelectableFeature
selectedItem
)
{
if
(!(
selectedItem
instanceof
SelectableGeometry
))
{
return
;
}
SelectableGeometry
selectedGeometry
=
(
SelectableGeometry
)
selectedItem
;
currentGeometry
=
selectedGeometry
.
getGeometry
();
currentFeature
=
selectedGeometry
.
getFeature
();
currentTriangulatedGeometry
=
TriangulatedGeometry
.
of
(
currentGeometry
);
currentErrorVisitor
.
setGeometry
(
currentTriangulatedGeometry
);
currentTriangulatedGeometry
.
setCullFace
(
culling
);
currentTriangulatedGeometry
.
setDrawMode
(
drawMode
);
healerView
.
setSelectedFeatureText
(
currentFeature
.
getGmlId
().
getGmlString
());
healerView
.
setFeatureTypeText
(
currentFeature
.
getFeatureType
().
toString
());
healerView
.
setGeometryText
(
currentGeometry
.
getLod
().
toString
()
+
", "
+
currentGeometry
.
getType
().
toString
());
updateCurrentErrors
();
healerView
.
zoomOutForBoundingBox
(
currentGeometry
.
calculateBoundingBox
());
healerView
.
getCurrentMeshGroup
().
getChildren
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
getCurrentMeshGroup
().
getChildren
().
addAll
(
currentTriangulatedGeometry
.
getMeshes
());
healerView
.
getNextStepBtn
().
setDisable
(
false
);
healerView
.
getHealCompleteBtn
().
setDisable
(
false
);
healerView
.
getAcceptBtn
().
setDisable
(
true
);
healerView
.
getCancelBtn
().
setDisable
(
true
);
}
private
void
updateCurrentErrors
()
{
List
<
CheckError
>
errors
=
new
ArrayList
<>();
currentGeometry
.
collectContainedErrors
(
errors
);
healerView
.
getCurrentErrorList
().
getItems
().
clear
();
for
(
CheckError
err
:
errors
)
{
healerView
.
getCurrentErrorList
().
getItems
().
add
(
err
);
}
}
public
void
nextStepClicked
()
{
if
(
currentGeometry
==
null
||
checker
==
null
||
currentFeature
==
null
)
{
return
;
}
healerView
.
getHealCompleteBtn
().
setDisable
(
true
);
currentFeature
.
prepareForChecking
();
if
(
nextGeometry
==
null
)
{
GmlId
id
=
currentGeometry
.
getGmlId
();
nextFeature
=
Copy
.
copy
(
currentFeature
);
nextGeometry
=
findNextGeometry
(
id
,
nextFeature
);
}
nextFeature
.
prepareForChecking
();
Healer
healer
=
new
Healer
(
checker
);
healer
.
heal
(
nextGeometry
,
nextFeature
,
1
);
nextFeature
.
prepareForChecking
();
checker
.
executeChecksForCheckable
(
nextFeature
);
updateNextErrors
();
updateNextGeometryView
();
}
private
void
updateNextGeometryView
()
{
nextTriangulatedGeometry
=
TriangulatedGeometry
.
of
(
nextGeometry
);
nextErrorVisitor
.
setGeometry
(
nextTriangulatedGeometry
);
nextTriangulatedGeometry
.
setCullFace
(
culling
);
nextTriangulatedGeometry
.
setDrawMode
(
drawMode
);
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
addAll
(
nextTriangulatedGeometry
.
getMeshes
());
healerView
.
getAcceptBtn
().
setDisable
(
false
);
healerView
.
getCancelBtn
().
setDisable
(
false
);
healerView
.
getSolidInjectorBtn
().
setDisable
(
true
);
}
public
void
healCompleteClicked
()
{
if
(
currentGeometry
==
null
)
{
return
;
}
// generate a gml id if there is none, so the selected geometry can be found
// later
// needs to be done befoe copy, otherwise copy will copy null
GmlId
gmlId
=
currentGeometry
.
getGmlId
();
nextFeature
=
Copy
.
copy
(
currentFeature
);
nextGeometry
=
findNextGeometry
(
gmlId
,
nextFeature
);
Healer
healer
=
new
Healer
(
checker
);
healer
.
heal
(
nextGeometry
,
nextFeature
);
updateNextErrors
();
nextTriangulatedGeometry
=
TriangulatedGeometry
.
of
(
nextGeometry
);
nextErrorVisitor
.
setGeometry
(
nextTriangulatedGeometry
);
nextTriangulatedGeometry
.
setCullFace
(
culling
);
nextTriangulatedGeometry
.
setDrawMode
(
drawMode
);
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
addAll
(
nextTriangulatedGeometry
.
getMeshes
());
healerView
.
getAcceptBtn
().
setDisable
(
false
);
healerView
.
getNextStepBtn
().
setDisable
(
true
);
healerView
.
getHealCompleteBtn
().
setDisable
(
true
);
healerView
.
getCancelBtn
().
setDisable
(
false
);
}
private
void
updateNextErrors
()
{
List
<
CheckError
>
errors
=
new
ArrayList
<>();
nextGeometry
.
collectContainedErrors
(
errors
);
ObservableList
<
CheckError
>
items
=
healerView
.
getNextErrorList
().
getItems
();
items
.
clear
();
for
(
CheckError
err
:
errors
)
{
items
.
add
(
err
);
}
}
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
];
}
public
void
acceptHealingClicked
()
{
if
(
model
==
null
||
checker
==
null
||
nextFeature
==
null
||
currentFeature
==
null
)
{
return
;
}
nextFeature
.
prepareForChecking
();
checker
.
executeChecksForCheckable
(
nextFeature
);
model
.
replaceFeature
(
currentFeature
,
nextFeature
);
currentFeature
=
nextFeature
;
currentGeometry
=
nextGeometry
;
updateCurrentErrors
();
healerView
.
getNextErrorList
().
getItems
().
clear
();
healerView
.
getCurrentMeshGroup
().
getChildren
().
clear
();
healerView
.
getCurrentMeshGroup
().
getChildren
().
addAll
(
nextTriangulatedGeometry
.
getMeshes
());
currentTriangulatedGeometry
=
nextTriangulatedGeometry
;
currentErrorVisitor
.
setGeometry
(
currentTriangulatedGeometry
);
nextTriangulatedGeometry
=
null
;
// nextHighlights = new HighlightController(healerView.getNextWorld());
// nextErrorVisitor = new ListErrorVisitor(nextHighlights);
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
getHealCompleteBtn
().
setDisable
(
false
);
healerView
.
getNextStepBtn
().
setDisable
(
false
);
healerView
.
getAcceptBtn
().
setDisable
(
true
);
healerView
.
getCancelBtn
().
setDisable
(
true
);
}
public
void
setWireframe
(
boolean
b
)
{
if
(
b
)
{
drawMode
=
DrawMode
.
LINE
;
}
else
{
drawMode
=
DrawMode
.
FILL
;
}
if
(
currentTriangulatedGeometry
!=
null
)
{
currentTriangulatedGeometry
.
setDrawMode
(
drawMode
);
}
if
(
nextTriangulatedGeometry
!=
null
)
{
nextTriangulatedGeometry
.
setDrawMode
(
drawMode
);
}
}
public
void
setCulling
(
CullFace
culling
)
{
this
.
culling
=
culling
;
if
(
currentTriangulatedGeometry
!=
null
)
{
currentTriangulatedGeometry
.
setCullFace
(
culling
);
}
if
(
nextTriangulatedGeometry
!=
null
)
{
nextTriangulatedGeometry
.
setCullFace
(
culling
);
}
}
public
void
cancelClicked
()
{
if
(
model
==
null
||
nextFeature
==
null
)
{
return
;
}
healerView
.
getNextErrorList
().
getItems
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
getAcceptBtn
().
setDisable
(
true
);
healerView
.
getCancelBtn
().
setDisable
(
true
);
healerView
.
getNextStepBtn
().
setDisable
(
false
);
healerView
.
getHealCompleteBtn
().
setDisable
(
false
);
nextFeature
=
null
;
nextGeometry
=
null
;
nextTriangulatedGeometry
=
null
;
}
public
void
currentHighlight
(
CheckError
err
)
{
if
(
err
==
null
)
{
currentHighlights
.
clearHighlights
();
return
;
}
err
.
accept
(
currentErrorVisitor
);
}
public
void
nextHighlight
(
CheckError
err
)
{
if
(
err
==
null
)
{
nextHighlights
.
clearHighlights
();
return
;
}
err
.
accept
(
nextErrorVisitor
);
}
public
void
onHide
()
{
currentHighlights
.
clearHighlights
();
nextHighlights
.
clearHighlights
();
healerView
.
getCurrentErrorList
().
getSelectionModel
().
clearSelection
();
healerView
.
getNextErrorList
().
getSelectionModel
().
clearSelection
();
healerView
.
getSelectFeatureBtn
().
setDisable
(
true
);
}
public
void
onShow
(
CityDoctorModel
model
,
Checker
checker
)
{
if
(
model
!=
null
)
{
// solid injector and meshing works without validation first
this
.
model
=
model
;
healerView
.
getSolidInjectorBtn
().
setDisable
(
false
);
healerView
.
getMeshBtn
().
setDisable
(
false
);
}
else
{
healerView
.
getSolidInjectorBtn
().
setDisable
(
true
);
healerView
.
getMeshBtn
().
setDisable
(
true
);
}
// TODO: refactor this better
if
(
model
==
null
||
checker
==
null
)
{
resetGUI
();
healerView
.
getSelectFeatureBtn
().
setDisable
(
true
);
healerView
.
getFixBtn
().
setDisable
(
true
);
healerView
.
getHealerToolBar
().
getSaveBtn
().
setDisable
(
true
);
return
;
}
if
(
model
!=
this
.
model
||
checker
!=
this
.
checker
)
{
this
.
model
=
model
;
this
.
checker
=
checker
;
resetGUI
();
healerView
.
getSelectFeatureBtn
().
setDisable
(
false
);
healerView
.
getFixBtn
().
setDisable
(
false
);
healerView
.
getHealerToolBar
().
getSaveBtn
().
setDisable
(
false
);
}
}
private
void
resetGUI
()
{
healerView
.
getAcceptBtn
().
setDisable
(
true
);
healerView
.
getCancelBtn
().
setDisable
(
true
);
healerView
.
getNextStepBtn
().
setDisable
(
true
);
healerView
.
getHealCompleteBtn
().
setDisable
(
true
);
healerView
.
getCurrentErrorList
().
getItems
().
clear
();
healerView
.
getNextErrorList
().
getItems
().
clear
();
healerView
.
getCurrentMeshGroup
().
getChildren
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
healerView
.
setSelectedFeatureText
(
""
);
healerView
.
setFeatureTypeText
(
""
);
healerView
.
setGeometryText
(
""
);
}
public
void
injectSolid
(
boolean
useBs
,
boolean
useBi
,
boolean
useLod2
,
boolean
useLod3
,
boolean
useLod4
)
{
if
(
model
==
null
||
currentFeature
==
null
)
{
return
;
}
CityObject
oldFeature
=
currentFeature
;
currentFeature
.
prepareForChecking
();
if
(
currentFeature
instanceof
BoundarySurface
)
{
BoundarySurface
surface
=
(
BoundarySurface
)
currentFeature
;
currentFeature
=
surface
.
getParent
();
}
if
(!(
currentFeature
instanceof
Building
))
{
return
;
}
// collect polygons
Map
<
Lod
,
List
<
Polygon
>>
availablePolygons
=
new
EnumMap
<>(
Lod
.
class
);
Map
<
Lod
,
Set
<
Polygon
>>
existingPolygons
=
new
EnumMap
<>(
Lod
.
class
);
Building
b
=
(
Building
)
currentFeature
;
collectPolygons
(
useBs
,
useBi
,
availablePolygons
,
existingPolygons
,
b
);
if
(
useLod2
)
{
insertSolidGeometry
(
availablePolygons
,
b
,
existingPolygons
,
Lod
.
LOD2
);
}
if
(
useLod3
)
{
insertSolidGeometry
(
availablePolygons
,
b
,
existingPolygons
,
Lod
.
LOD3
);
}
if
(
useLod4
)
{
insertSolidGeometry
(
availablePolygons
,
b
,
existingPolygons
,
Lod
.
LOD4
);
}
nextFeature
=
null
;
nextGeometry
=
null
;
Lod
lod
=
currentGeometry
.
getLod
();
GeometryType
type
=
GeometryType
.
SOLID
;
currentGeometry
=
Objects
.
requireNonNull
(
currentFeature
.
getGeometry
(
type
,
lod
));
checker
.
executeChecksForCheckable
(
currentFeature
);
healerView
.
getNextErrorList
().
getItems
().
clear
();
healerView
.
getNextMeshGroup
().
getChildren
().
clear
();
updateCurrentErrors
();
currentTriangulatedGeometry
=
TriangulatedGeometry
.
of
(
currentGeometry
);
currentErrorVisitor
.
setGeometry
(
currentTriangulatedGeometry
);
currentTriangulatedGeometry
.
setCullFace
(
culling
);
currentTriangulatedGeometry
.
setDrawMode
(
drawMode
);
healerView
.
zoomOutForBoundingBox
(
currentGeometry
.
calculateBoundingBox
());
healerView
.
getCurrentMeshGroup
().
getChildren
().
clear
();
healerView
.
getCurrentMeshGroup
().
getChildren
().
addAll
(
currentTriangulatedGeometry
.
getMeshes
());
healerView
.
getNextStepBtn
().
setDisable
(
false
);
healerView
.
getHealCompleteBtn
().
setDisable
(
false
);
healerView
.
getAcceptBtn
().
setDisable
(
true
);
healerView
.
getCancelBtn
().
setDisable
(
true
);
healerView
.
getSolidInjectorBtn
().
setDisable
(
true
);
currentFeature
=
oldFeature
;
}
private
void
collectPolygons
(
boolean
useBs
,
boolean
useBi
,
Map
<
Lod
,
List
<
Polygon
>>
availablePolygons
,
Map
<
Lod
,
Set
<
Polygon
>>
existingPolygons
,
Building
b
)
{
if
(
useBs
)
{
collectPolygons
(
availablePolygons
,
b
.
getBoundarySurfaces
());
}
if
(
useBi
)
{
for
(
Installation
bi
:
b
.
getBuildingInstallations
())
{
for
(
Geometry
geom
:
bi
.
getGeometries
())
{
List
<
Polygon
>
polygons
=
availablePolygons
.
computeIfAbsent
(
geom
.
getLod
(),
l
->
new
ArrayList
<>());
polygons
.
addAll
(
geom
.
getPolygons
());
}
collectPolygons
(
availablePolygons
,
bi
.
getBoundarySurfaces
());
}
}
for
(
Geometry
geom
:
b
.
getGeometries
())
{
Set
<
Polygon
>
polygons
=
existingPolygons
.
computeIfAbsent
(
geom
.
getLod
(),
l
->
new
HashSet
<>());
polygons
.
addAll
(
geom
.
getPolygons
());
}
}
private
void
insertSolidGeometry
(
Map
<
Lod
,
List
<
Polygon
>>
availablePolygons
,
Building
b
,
Map
<
Lod
,
Set
<
Polygon
>>
existingPolygons
,
Lod
lod
)
{
List
<
Polygon
>
newPolygons
=
availablePolygons
.
get
(
lod
);
if
(
newPolygons
!=
null
)
{
// found polygons to be added
Geometry
newGeometry
=
new
Geometry
(
GeometryType
.
SOLID
,
lod
);
Set
<
Polygon
>
polygons
=
existingPolygons
.
get
(
lod
);
if
(
polygons
!=
null
)
{
// add existing polygons
for
(
Polygon
p
:
polygons
)
{
newGeometry
.
addPolygon
(
p
);
}
}
for
(
Polygon
p
:
newPolygons
)
{
if
(
p
instanceof
ConcretePolygon
&&
!
newGeometry
.
containsPolygon
(
p
))
{
LinkedPolygon
newPoly
=
new
LinkedPolygon
((
ConcretePolygon
)
p
,
newGeometry
);
newGeometry
.
addPolygon
(
newPoly
);
}
}
// remove existing geometry, if possible
b
.
removeGeometry
(
lod
);
// add new geometry
b
.
addGeometry
(
newGeometry
);
newGeometry
.
updateEdgesAndVertices
();
if
(
currentGeometry
==
null
)
{
currentGeometry
=
newGeometry
;
}
}
}
private
void
collectPolygons
(
Map
<
Lod
,
List
<
Polygon
>>
availablePolygons
,
List
<
BoundarySurface
>
surfaces
)
{
for
(
BoundarySurface
bs
:
surfaces
)
{
for
(
Geometry
geom
:
bs
.
getGeometries
())
{
List
<
Polygon
>
polygons
=
availablePolygons
.
computeIfAbsent
(
geom
.
getLod
(),
l
->
new
ArrayList
<>());
polygons
.
addAll
(
geom
.
getPolygons
());
}
for
(
Opening
o
:
bs
.
getOpenings
())
{
for
(
Geometry
geom
:
o
.
getGeometries
())
{
List
<
Polygon
>
polygons
=
availablePolygons
.
computeIfAbsent
(
geom
.
getLod
(),
l
->
new
ArrayList
<>());
polygons
.
addAll
(
geom
.
getPolygons
());
}
}
}
}
public
void
solidInjectorClicked
()
{
if
(
injectorDialog
==
null
)
{
injectorDialog
=
new
SolidInjectorDialog
(
this
);
}
injectorDialog
.
show
();
}
public
void
fixModel
()
{
if
(
model
==
null
||
checker
==
null
)
{
return
;
}
Healer
healer
=
new
Healer
(
checker
);
healer
.
healModel
(
model
);
}
public
void
meshBtnClicked
()
{
if
(
model
==
null
)
{
return
;
}
TextInputDialog
dialog
=
new
TextInputDialog
(
"1"
);
dialog
.
setTitle
(
"Mesh Parameter Input"
);
dialog
.
setHeaderText
(
"Input a number > 0.01"
);
dialog
.
setContentText
(
"Maximum Area of meshed triangles in m²:"
);
Optional
<
String
>
result
=
dialog
.
showAndWait
();
if
(
result
.
isPresent
()){
String
areaString
=
result
.
get
();
try
{
double
area
=
Double
.
parseDouble
(
areaString
);
if
(
area
<
0.01
)
{
// triangle area is too small, ignore
return
;
}
for
(
Building
b
:
model
.
getBuildings
())
{
MeshGenerator
.
replaceGeometryWithMeshedGeometry
(
b
,
area
);
}
}
catch
(
NumberFormatException
e
)
{
// no real number was inserted, do nothing
}
}
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerGUI.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
de.hft.stuttgart.citydoctor2.gui.MainWindow
;
import
de.hft.stuttgart.citydoctor2.gui.ViewRegistration
;
public
class
HealerGUI
{
public
static
void
main
(
String
[]
args
)
{
ViewRegistration
.
registerView
(
new
HealerView
());
MainWindow
.
main
(
args
);
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerToolbar.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
java.io.IOException
;
import
de.hft.stuttgart.citydoctor2.gui.CityDoctorController
;
import
de.hft.stuttgart.citydoctor2.gui.MainToolBar
;
import
de.hft.stuttgart.citydoctor2.gui.MainWindow
;
import
javafx.fxml.FXML
;
import
javafx.fxml.FXMLLoader
;
import
javafx.scene.control.Button
;
import
javafx.scene.control.ToggleButton
;
import
javafx.scene.image.Image
;
import
javafx.scene.image.ImageView
;
import
javafx.scene.layout.HBox
;
import
javafx.scene.layout.Priority
;
import
javafx.scene.shape.CullFace
;
public
class
HealerToolbar
{
private
HealerController
controller
;
private
HBox
toolBar
;
@FXML
private
ToggleButton
wireframeBtn
;
@FXML
private
ToggleButton
cullingBtn
;
@FXML
private
ImageView
cullingImage
;
@FXML
private
ImageView
wireframeImage
;
@FXML
private
HBox
spacer
;
@FXML
private
Button
saveBtn
;
@FXML
private
ImageView
saveImage
;
@FXML
private
Button
simplyBtn
;
@FXML
private
ImageView
simplyImageView
;
private
CityDoctorController
cdController
;
public
HealerToolbar
(
HealerController
controller
,
MainWindow
mainWindow
)
throws
IOException
{
this
.
controller
=
controller
;
cdController
=
mainWindow
.
getController
();
FXMLLoader
loader
=
new
FXMLLoader
(
getClass
().
getResource
(
"healerToolbar.fxml"
));
loader
.
setController
(
this
);
toolBar
=
loader
.
load
();
}
public
void
initialize
()
{
HBox
.
setHgrow
(
spacer
,
Priority
.
ALWAYS
);
cullingImage
.
setImage
(
new
Image
(
MainToolBar
.
class
.
getResourceAsStream
(
"icons/Culling.png"
)));
wireframeImage
.
setImage
(
new
Image
(
MainToolBar
.
class
.
getResourceAsStream
(
"icons/wireframe32x32.png"
)));
simplyImageView
.
setImage
(
new
Image
(
HealerToolbar
.
class
.
getResourceAsStream
(
"autopro32x32.png"
)));
wireframeBtn
.
selectedProperty
().
addListener
((
obs
,
oldV
,
newV
)
->
controller
.
setWireframe
(
newV
));
cullingBtn
.
selectedProperty
().
addListener
((
obs
,
oldV
,
newV
)
->
{
if
(
Boolean
.
TRUE
.
equals
(
newV
))
{
controller
.
setCulling
(
CullFace
.
BACK
);
}
else
{
controller
.
setCulling
(
CullFace
.
NONE
);
}
});
saveBtn
.
setOnAction
(
ae
->
cdController
.
askAndSave
());
saveBtn
.
setDisable
(
true
);
saveImage
.
setImage
(
new
Image
(
MainWindow
.
class
.
getResourceAsStream
(
"icons/save.png"
)));
simplyBtn
.
setOnAction
(
ae
->
controller
.
showSimplyfication
());
}
public
HBox
getToolBar
()
{
return
toolBar
;
}
public
Button
getSaveBtn
()
{
return
saveBtn
;
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerView.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
java.io.IOException
;
import
java.util.Optional
;
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.Checker
;
import
de.hft.stuttgart.citydoctor2.datastructure.BoundingBox
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel
;
import
de.hft.stuttgart.citydoctor2.gui.FilterPane
;
import
de.hft.stuttgart.citydoctor2.gui.MainWindow
;
import
de.hft.stuttgart.citydoctor2.gui.View
;
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.PerspectiveCamera
;
import
javafx.scene.SceneAntialiasing
;
import
javafx.scene.SubScene
;
import
javafx.scene.control.Button
;
import
javafx.scene.control.Label
;
import
javafx.scene.control.ListView
;
import
javafx.scene.control.Tooltip
;
import
javafx.scene.control.cell.TextFieldListCell
;
import
javafx.scene.image.Image
;
import
javafx.scene.image.ImageView
;
import
javafx.scene.input.MouseButton
;
import
javafx.scene.layout.HBox
;
import
javafx.scene.layout.Pane
;
import
javafx.scene.paint.Color
;
import
javafx.scene.transform.Rotate
;
import
javafx.stage.Window
;
import
javafx.util.StringConverter
;
public
class
HealerView
extends
View
{
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
HealerView
.
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
;
private
Image
image
;
private
Node
healerWindow
;
private
Window
parent
;
private
HealerController
controller
;
private
ChooseFeatureDialog
chooseFeatureDialog
;
private
HealerToolbar
toolBar
;
private
DoubleProperty
translateZ
;
private
DoubleProperty
cameraXRot
;
private
DoubleProperty
cameraYRot
;
private
double
dragX
;
private
double
dragY
;
@FXML
private
Button
fixBtn
;
@FXML
private
Button
selectFeatureBtn
;
@FXML
private
Pane
currentCanvas
;
@FXML
private
Pane
nextCanvas
;
@FXML
private
Label
selectedFeatureLabel
;
@FXML
private
Label
featureTypeLabel
;
@FXML
private
Label
geometryLabel
;
@FXML
private
ListView
<
CheckError
>
currentErrorList
;
@FXML
private
Button
nextStep
;
@FXML
private
Button
healCompleteBtn
;
@FXML
private
Button
acceptBtn
;
@FXML
private
ListView
<
CheckError
>
nextErrorList
;
@FXML
private
Button
cancelButton
;
@FXML
private
ImageView
selectImageView
;
@FXML
private
ImageView
nextStepImage
;
@FXML
private
ImageView
healCompleteImage
;
@FXML
private
ImageView
acceptBtnImage
;
@FXML
private
ImageView
cancelImage
;
@FXML
private
Button
solidInjectorBtn
;
@FXML
private
Button
meshBtn
;
private
Group
currentWorld
;
private
Group
nextWorld
;
private
Group
currentMeshGroup
;
private
Group
nextMeshGroup
;
public
HealerView
()
{
image
=
new
Image
(
getClass
().
getResourceAsStream
(
"healing.png"
));
}
@Override
public
void
initializeView
(
MainWindow
mainWindow
)
{
this
.
parent
=
mainWindow
.
getMainStage
();
FXMLLoader
loader
=
new
FXMLLoader
(
getClass
().
getResource
(
"healerGUI.fxml"
));
loader
.
setController
(
this
);
try
{
healerWindow
=
loader
.
load
();
controller
=
new
HealerController
(
this
);
toolBar
=
new
HealerToolbar
(
controller
,
mainWindow
);
}
catch
(
IOException
e
)
{
logger
.
catching
(
e
);
}
}
public
void
initialize
()
{
setup3dViews
();
currentErrorList
.
getSelectionModel
().
selectedItemProperty
()
.
addListener
((
obs
,
oldV
,
newV
)
->
controller
.
currentHighlight
(
newV
));
currentErrorList
.
setCellFactory
(
TextFieldListCell
.
forListView
(
new
StringConverter
<
CheckError
>()
{
@Override
public
String
toString
(
CheckError
err
)
{
return
err
.
getErrorId
().
toString
();
}
@Override
public
CheckError
fromString
(
String
string
)
{
return
null
;
}
}));
nextErrorList
.
getSelectionModel
().
selectedItemProperty
()
.
addListener
((
obs
,
oldV
,
newV
)
->
controller
.
nextHighlight
(
newV
));
nextErrorList
.
setCellFactory
(
TextFieldListCell
.
forListView
(
new
StringConverter
<
CheckError
>()
{
@Override
public
String
toString
(
CheckError
err
)
{
return
err
.
getErrorId
().
toString
();
}
@Override
public
CheckError
fromString
(
String
string
)
{
return
null
;
}
}));
nextStep
.
setOnAction
(
ae
->
controller
.
nextStepClicked
());
nextStep
.
setTooltip
(
new
Tooltip
(
"Heal one Error"
));
selectFeatureBtn
.
setOnAction
(
ae
->
controller
.
selectFeatureClicked
());
selectFeatureBtn
.
setTooltip
(
new
Tooltip
(
"Select Feature for Healing"
));
healCompleteBtn
.
setOnAction
(
ae
->
controller
.
healCompleteClicked
());
healCompleteBtn
.
setTooltip
(
new
Tooltip
(
"Heal Complete"
));
acceptBtn
.
setOnAction
(
ae
->
controller
.
acceptHealingClicked
());
acceptBtn
.
setTooltip
(
new
Tooltip
(
"Accept Healing"
));
selectImageView
.
setImage
(
new
Image
(
getClass
().
getResourceAsStream
(
"list.png"
)));
nextStepImage
.
setImage
(
new
Image
(
getClass
().
getResourceAsStream
(
"step.png"
)));
healCompleteImage
.
setImage
(
new
Image
(
getClass
().
getResourceAsStream
(
"fastforward.png"
)));
acceptBtnImage
.
setImage
(
new
Image
(
getClass
().
getResourceAsStream
(
"tick.png"
)));
cancelButton
.
setOnAction
(
ae
->
controller
.
cancelClicked
());
cancelImage
.
setImage
(
new
Image
(
FilterPane
.
class
.
getResourceAsStream
(
"icons/Delete.png"
)));
solidInjectorBtn
.
setOnAction
(
ae
->
controller
.
solidInjectorClicked
());
meshBtn
.
setOnAction
(
ae
->
controller
.
meshBtnClicked
());
fixBtn
.
setOnAction
(
ae
->
controller
.
fixModel
());
}
private
void
setup3dViews
()
{
translateZ
=
new
SimpleDoubleProperty
(
CAMERA_TRANSLATE_Z
);
cameraXRot
=
new
SimpleDoubleProperty
(
CAMERA_INITIAL_X_ANGLE
);
cameraYRot
=
new
SimpleDoubleProperty
(
CAMERA_INITIAL_Y_ANGLE
);
buildCurrent3dView
();
buildNext3dView
();
}
private
void
buildCurrent3dView
()
{
Group
currentRoot
=
new
Group
();
SubScene
currentScene
=
new
SubScene
(
currentRoot
,
500
,
300
,
true
,
SceneAntialiasing
.
BALANCED
);
currentScene
.
heightProperty
().
bind
(
currentCanvas
.
heightProperty
());
currentScene
.
widthProperty
().
bind
(
currentCanvas
.
widthProperty
());
currentScene
.
setFill
(
Color
.
AZURE
);
currentCanvas
.
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
(
10000
d
);
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
(
currentCanvas
);
}
private
void
buildNext3dView
()
{
Group
nextRoot
=
new
Group
();
SubScene
nextScene
=
new
SubScene
(
nextRoot
,
500
,
300
,
true
,
SceneAntialiasing
.
BALANCED
);
nextScene
.
heightProperty
().
bind
(
nextCanvas
.
heightProperty
());
nextScene
.
widthProperty
().
bind
(
nextCanvas
.
widthProperty
());
nextScene
.
setFill
(
Color
.
AZURE
);
nextCanvas
.
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
(
10000
d
);
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
);
setupMeshViewControls
(
nextCanvas
);
}
public
void
zoomOutForBoundingBox
(
BoundingBox
b
)
{
double
longestSide
=
b
.
getDiagonalLength
()
*
0.4
;
double
d
=
longestSide
/
Math
.
tan
(
Math
.
toRadians
(
30
)
/
2
);
translateZ
.
set
(-
d
);
}
private
void
setupMeshViewControls
(
Pane
canvas
)
{
canvas
.
setOnMousePressed
(
me
->
{
if
(
me
.
getButton
()
==
MouseButton
.
PRIMARY
)
{
dragX
=
me
.
getScreenX
();
dragY
=
me
.
getScreenY
();
}
});
canvas
.
setOnScroll
(
se
->
translateZ
.
set
(
translateZ
.
get
()
+
se
.
getDeltaY
()
/
10
d
));
canvas
.
setOnMouseDragged
(
me
->
{
if
(
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
/
3
d
)
%
360
));
cameraYRot
.
set
(
cameraYRot
.
get
()
+
((
deltaY
/
3
d
)
%
360
));
}
});
}
@Override
public
Optional
<
HBox
>
getToolbar
()
{
return
Optional
.
of
(
toolBar
.
getToolBar
());
}
@Override
public
Node
getMainScreen
()
{
return
healerWindow
;
}
@Override
public
Image
getViewLogo
()
{
return
image
;
}
@Override
public
void
onHide
()
{
controller
.
onHide
();
}
@Override
public
void
onShow
(
CityDoctorModel
model
,
Checker
checker
)
{
controller
.
onShow
(
model
,
checker
);
}
public
ChooseFeatureDialog
getChooseFeatureDialog
()
{
if
(
chooseFeatureDialog
==
null
)
{
chooseFeatureDialog
=
new
ChooseFeatureDialog
(
parent
,
controller
);
}
return
chooseFeatureDialog
;
}
public
Group
getCurrentWorld
()
{
return
currentWorld
;
}
public
Group
getNextWorld
()
{
return
nextWorld
;
}
public
void
setSelectedFeatureText
(
String
text
)
{
selectedFeatureLabel
.
setText
(
text
);
}
public
void
setFeatureTypeText
(
String
text
)
{
featureTypeLabel
.
setText
(
text
);
}
public
void
setGeometryText
(
String
text
)
{
geometryLabel
.
setText
(
text
);
}
public
ListView
<
CheckError
>
getCurrentErrorList
()
{
return
currentErrorList
;
}
public
ListView
<
CheckError
>
getNextErrorList
()
{
return
nextErrorList
;
}
public
Button
getSelectFeatureBtn
()
{
return
selectFeatureBtn
;
}
public
Button
getAcceptBtn
()
{
return
acceptBtn
;
}
public
Button
getHealCompleteBtn
()
{
return
healCompleteBtn
;
}
public
Button
getNextStepBtn
()
{
return
nextStep
;
}
public
Button
getCancelBtn
()
{
return
cancelButton
;
}
public
DoubleProperty
translateZProperty
()
{
return
translateZ
;
}
public
Group
getNextMeshGroup
()
{
return
nextMeshGroup
;
}
public
Group
getCurrentMeshGroup
()
{
return
currentMeshGroup
;
}
public
Button
getSolidInjectorBtn
()
{
return
solidInjectorBtn
;
}
public
Button
getMeshBtn
()
{
return
meshBtn
;
}
public
Button
getFixBtn
()
{
return
fixBtn
;
}
public
HealerToolbar
getHealerToolBar
()
{
return
toolBar
;
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/SelectableFeature.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
public
class
SelectableFeature
{
private
String
text
;
public
SelectableFeature
(
String
text
)
{
this
.
text
=
text
;
}
public
boolean
isGeometry
()
{
return
false
;
}
public
Geometry
getGeometry
()
{
return
null
;
}
public
CityObject
getFeature
()
{
return
null
;
}
@Override
public
String
toString
()
{
return
"SelectableFeature [text="
+
text
+
"]"
;
}
public
String
getText
()
{
return
text
;
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/SelectableGeometry.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
public
class
SelectableGeometry
extends
SelectableFeature
{
private
Geometry
geometry
;
private
CityObject
feature
;
public
SelectableGeometry
(
String
text
,
Geometry
geometry
,
CityObject
feature
)
{
super
(
text
);
this
.
geometry
=
geometry
;
this
.
feature
=
feature
;
}
@Override
public
boolean
isGeometry
()
{
return
true
;
}
@Override
public
CityObject
getFeature
()
{
return
feature
;
}
@Override
public
Geometry
getGeometry
()
{
return
geometry
;
}
@Override
public
String
toString
()
{
return
"SelectableGeometry [geometry="
+
geometry
+
", feature="
+
feature
+
"]"
;
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/SolidInjectorDialog.java
0 → 100644
View file @
97a65114
package
de.hft.stuttgart.citydoctor2.healer.gui
;
import
java.io.IOException
;
import
java.io.UncheckedIOException
;
import
de.hft.stuttgart.citydoctor2.gui.MainWindow
;
import
javafx.fxml.FXML
;
import
javafx.fxml.FXMLLoader
;
import
javafx.scene.Parent
;
import
javafx.scene.Scene
;
import
javafx.scene.control.Button
;
import
javafx.scene.control.CheckBox
;
import
javafx.scene.image.Image
;
import
javafx.stage.Modality
;
import
javafx.stage.Stage
;
public
class
SolidInjectorDialog
{
@FXML
private
CheckBox
useBiCheckBox
;
@FXML
private
CheckBox
useBsCheckBox
;
@FXML
private
Button
okBtn
;
@FXML
private
Button
cancelBtn
;
@FXML
private
CheckBox
lod2Box
;
@FXML
private
CheckBox
lod3Box
;
@FXML
private
CheckBox
lod4Box
;
private
HealerController
controller
;
private
Stage
stage
;
public
SolidInjectorDialog
(
HealerController
controller
)
{
this
.
controller
=
controller
;
FXMLLoader
loader
=
new
FXMLLoader
(
getClass
().
getResource
(
"solidInjectorDialog.fxml"
));
loader
.
setController
(
this
);
try
{
Parent
n
=
loader
.
load
();
stage
=
new
Stage
();
stage
.
getIcons
().
add
(
new
Image
(
MainWindow
.
class
.
getResourceAsStream
(
"icons/CityDoctor-Logo-rot_klein.jpg"
)));
Scene
scene
=
new
Scene
(
n
);
stage
.
setScene
(
scene
);
stage
.
initModality
(
Modality
.
APPLICATION_MODAL
);
stage
.
setTitle
(
"Solid Injector"
);
}
catch
(
IOException
e
)
{
throw
new
UncheckedIOException
(
e
);
}
}
public
void
initialize
()
{
okBtn
.
setOnAction
(
ae
->
{
controller
.
injectSolid
(
useBsCheckBox
.
isSelected
(),
useBiCheckBox
.
isSelected
(),
lod2Box
.
isSelected
(),
lod3Box
.
isSelected
(),
lod4Box
.
isSelected
());
stage
.
close
();
});
cancelBtn
.
setOnAction
(
ae
->
stage
.
close
());
}
public
void
show
()
{
stage
.
showAndWait
();
}
}
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/resources/de/hft/stuttgart/citydoctor2/healer/gui/ChooseErrorFeatureDialog.fxml
0 → 100644
View file @
97a65114
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox
xmlns=
"http://javafx.com/javafx/8.0.221"
xmlns:fx=
"http://javafx.com/fxml/1"
>
<children>
<TabPane
fx:id=
"tabPane"
maxHeight=
"1.7976931348623157E308"
maxWidth=
"1.7976931348623157E308"
prefHeight=
"500.0"
tabClosingPolicy=
"UNAVAILABLE"
VBox.vgrow=
"ALWAYS"
>
<tabs>
<Tab
text=
"Buildings"
>
<content>
<TreeView
fx:id=
"buildingsView"
prefHeight=
"200.0"
prefWidth=
"200.0"
/>
</content>
</Tab>
<Tab
text=
"Vegetation"
>
<content>
<TreeView
fx:id=
"vegetationView"
prefHeight=
"200.0"
prefWidth=
"200.0"
/>
</content>
</Tab>
<Tab
text=
"Transportation"
>
<content>
<TreeView
fx:id=
"transportationView"
prefHeight=
"200.0"
prefWidth=
"200.0"
/>
</content>
</Tab>
<Tab
text=
"Bridges"
>
<content>
<TreeView
fx:id=
"bridgesView"
prefHeight=
"200.0"
prefWidth=
"200.0"
/>
</content>
</Tab>
<Tab
text=
"Water"
>
<content>
<TreeView
fx:id=
"waterView"
prefHeight=
"200.0"
prefWidth=
"200.0"
/>
</content>
</Tab>
<Tab
text=
"Terrain"
>
<content>
<TreeView
fx:id=
"terrainView"
prefHeight=
"200.0"
prefWidth=
"200.0"
/>
</content>
</Tab>
</tabs>
</TabPane>
<HBox
alignment=
"CENTER_RIGHT"
nodeOrientation=
"LEFT_TO_RIGHT"
spacing=
"5.0"
VBox.vgrow=
"NEVER"
>
<children>
<Button
fx:id=
"okButton"
mnemonicParsing=
"false"
text=
"OK"
/>
<Button
fx:id=
"cancelButton"
mnemonicParsing=
"false"
text=
"Cancel"
/>
</children>
<padding>
<Insets
bottom=
"5.0"
left=
"5.0"
right=
"5.0"
top=
"5.0"
/>
</padding>
</HBox>
</children>
</VBox>
CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/resources/de/hft/stuttgart/citydoctor2/healer/gui/autopro32x32.png
0 → 100644
View file @
97a65114
1.95 KB
Prev
1
…
19
20
21
22
23
24
25
26
27
…
30
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment