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
39ffb121
Commit
39ffb121
authored
Jan 24, 2025
by
Matthias Betz
Browse files
Solid self intersection with better triangulation to eliminate false positives
parent
8afe47c2
Pipeline
#10954
passed with stage
in 1 minute and 12 seconds
Changes
16
Pipelines
1
Show whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorModel/pom.xml
View file @
39ffb121
...
...
@@ -58,6 +58,14 @@
<groupId>
org.locationtech.proj4j
</groupId>
<artifactId>
proj4j
</artifactId>
</dependency>
<dependency>
<groupId>
org.locationtech.jts
</groupId>
<artifactId>
jts-core
</artifactId>
</dependency>
<dependency>
<groupId>
io.github.earcut4j
</groupId>
<artifactId>
earcut4j
</artifactId>
</dependency>
</dependencies>
<build>
<resources>
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/MovedPolygon.java
View file @
39ffb121
...
...
@@ -39,6 +39,25 @@ public class MovedPolygon {
private
MovedRing
exteriorRing
;
private
List
<
MovedRing
>
innerRings
;
/**
* Moves the polygon by the first exterior vertex. The center of the polygon
* will not me the origin, the polygon will just be near the origin. This can
* eliviate numeric issues in large calculations.
*
* @param p the polygon that is moved to the center.
* @return the moved polygon.
*/
public
static
MovedPolygon
ofPolygon
(
Polygon
p
)
{
Vector3d
movedBy
=
p
.
getExteriorRing
().
getVertices
().
get
(
0
);
MovedPolygon
indPoly
=
new
MovedPolygon
();
indPoly
.
original
=
p
;
indPoly
.
exteriorRing
=
MovedRing
.
ofRing
(
p
.
getExteriorRing
(),
movedBy
);
for
(
LinearRing
inner
:
p
.
getInnerRings
())
{
indPoly
.
addInnerRing
(
MovedRing
.
ofRing
(
inner
,
movedBy
));
}
return
indPoly
;
}
public
static
MovedPolygon
ofPolygon
(
Polygon
p
,
Vector3d
movedBy
)
{
MovedPolygon
indPoly
=
new
MovedPolygon
();
indPoly
.
original
=
p
;
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Polygon2d.java
View file @
39ffb121
...
...
@@ -41,6 +41,11 @@ public class Polygon2d {
return
projectTo2D
(
poly
,
axis
);
}
public
static
Polygon2d
withProjection
(
MovedPolygon
poly
)
{
ProjectionAxis
axis
=
ProjectionAxis
.
of
(
poly
.
getOriginal
());
return
projectTo2D
(
poly
,
axis
);
}
public
static
Polygon2d
withProjection
(
MovedPolygon
poly
,
ProjectionAxis
projectionAxis
)
{
return
projectTo2D
(
poly
,
projectionAxis
);
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Vector3d.java
View file @
39ffb121
...
...
@@ -22,6 +22,8 @@ import java.io.Serial;
import
java.io.Serializable
;
import
java.util.Arrays
;
import
org.locationtech.jts.geom.Coordinate
;
/**
* A three dimensional vector
*
...
...
@@ -45,6 +47,17 @@ public class Vector3d implements Serializable {
this
(
0
d
,
0
d
,
0
d
);
}
/**
* Convert JTS Coordinate class to Vector3d.
* @param coord JTS Coordinate
*/
public
Vector3d
(
Coordinate
coord
)
{
coords
=
new
double
[
3
];
coords
[
0
]
=
coord
.
getX
();
coords
[
1
]
=
coord
.
getY
();
coords
[
2
]
=
coord
.
getZ
();
}
public
Vector3d
(
double
x
,
double
y
,
double
z
)
{
coords
=
new
double
[
3
];
coords
[
0
]
=
x
;
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java
0 → 100644
View file @
39ffb121
package
de.hft.stuttgart.citydoctor2.tesselation
;
import
java.util.ArrayList
;
import
java.util.List
;
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.Polygon2d
;
import
de.hft.stuttgart.citydoctor2.math.Ring2d
;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
import
de.hft.stuttgart.citydoctor2.math.Vector2d
;
import
earcut4j.Earcut
;
/**
* Tesselator to create triangles out of polygons. Uses earcut4j implementation.
*
* @author Matthias Betz
*
*/
public
class
EarcutTesselator
{
public
static
TesselatedPolygon
tesselatePolygon
(
Polygon
p
)
{
// sum number of vertices, don't use duplicate last point
// assume polygons are closed
int
nrOfVertices
=
p
.
getExteriorRing
().
getVertices
().
size
()
-
1
;
int
[]
holeStart
=
null
;
if
(!
p
.
getInnerRings
().
isEmpty
())
{
holeStart
=
new
int
[
p
.
getInnerRings
().
size
()];
List
<
LinearRing
>
innerRings
=
p
.
getInnerRings
();
for
(
int
i
=
0
;
i
<
innerRings
.
size
();
i
++)
{
LinearRing
r
=
innerRings
.
get
(
i
);
holeStart
[
i
]
=
nrOfVertices
;
nrOfVertices
+=
r
.
getVertices
().
size
()
-
1
;
}
}
// collect vertices
// project polygon to 2D space as library is buggy for more than 2 dimensions
Polygon2d
projectedPolygon
=
Polygon2d
.
withProjection
(
p
);
double
[]
vertices
=
new
double
[
nrOfVertices
*
2
];
List
<
Vertex
>
vertexObjects
=
new
ArrayList
<>();
addVerticesToList
(
p
.
getExteriorRing
(),
vertexObjects
);
for
(
LinearRing
innerRing
:
p
.
getInnerRings
())
{
addVerticesToList
(
innerRing
,
vertexObjects
);
}
int
start
=
addRingToArray
(
projectedPolygon
.
getExterior
(),
vertices
,
0
);
for
(
Ring2d
innerRing
:
projectedPolygon
.
getInteriorRings
())
{
start
=
addRingToArray
(
innerRing
,
vertices
,
start
);
}
// triangulation
List
<
Integer
>
indices
=
Earcut
.
earcut
(
vertices
,
holeStart
,
2
);
List
<
Triangle3d
>
triangles
=
new
ArrayList
<>();
if
(
indices
.
size
()
%
3
!=
0
)
{
throw
new
IllegalStateException
();
}
for
(
int
i
=
0
;
i
<
indices
.
size
();
i
=
i
+
3
)
{
Triangle3d
t
=
new
Triangle3d
(
vertexObjects
.
get
(
indices
.
get
(
i
+
0
)),
vertexObjects
.
get
(
indices
.
get
(
i
+
1
)),
vertexObjects
.
get
(
indices
.
get
(
i
+
2
)));
triangles
.
add
(
t
);
}
return
new
TesselatedPolygon
(
triangles
,
p
);
}
private
static
void
addVerticesToList
(
LinearRing
ring
,
List
<
Vertex
>
vertexObjects
)
{
List
<
Vertex
>
vertices
=
ring
.
getVertices
();
for
(
int
i
=
0
;
i
<
vertices
.
size
()
-
1
;
i
++)
{
Vertex
v
=
vertices
.
get
(
i
);
vertexObjects
.
add
(
v
);
}
}
private
static
int
addRingToArray
(
Ring2d
ring
,
double
[]
vertices
,
int
start
)
{
List
<
Vector2d
>
ringVertices
=
ring
.
getVertices
();
for
(
int
i
=
0
;
i
<
ringVertices
.
size
()
-
1
;
i
++)
{
Vector2d
v
=
ringVertices
.
get
(
i
);
vertices
[
start
++]
=
v
.
getX
();
vertices
[
start
++]
=
v
.
getY
();
}
return
start
;
}
private
EarcutTesselator
()
{
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/JoglTesselator.java
View file @
39ffb121
...
...
@@ -33,7 +33,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import
de.hft.stuttgart.citydoctor2.math.Vector3d
;
/**
* Tesselator to create triangles out of polygons.
* Tesselator to create triangles out of polygons.
Uses JOGL implementation.
*
* @author Matthias Betz
*
...
...
@@ -102,6 +102,10 @@ public class JoglTesselator {
}
public
static
TesselatedPolygon
tesselatePolygon
(
Polygon
p
)
{
return
tesselatePolygon
(
p
,
true
);
}
public
static
TesselatedPolygon
tesselatePolygon
(
Polygon
p
,
boolean
discardDegeneratedTriangles
)
{
ArrayList
<
Vector3d
>
vertices
=
new
ArrayList
<>();
Vector3d
normal
=
p
.
calculateNormalNormalized
();
synchronized
(
tess
)
{
...
...
@@ -121,7 +125,7 @@ public class JoglTesselator {
GLU
.
gluTessEndPolygon
(
tess
);
ArrayList
<
Integer
>
indices
=
new
ArrayList
<>();
for
(
Primitive
primitive
:
primitives
)
{
primitive
.
fillListWithIndices
(
indices
);
primitive
.
fillListWithIndices
(
indices
,
discardDegeneratedTriangles
);
}
primitives
.
clear
();
return
new
TesselatedPolygon
(
vertices
,
indices
,
p
);
...
...
@@ -137,7 +141,7 @@ public class JoglTesselator {
GLU
.
gluTessEndContour
(
tess
);
}
public
static
TesselatedRing
tesselateRing
(
LinearRing
r
)
{
public
static
TesselatedRing
tesselateRing
(
LinearRing
r
,
boolean
discardDegeneratedTriangles
)
{
ArrayList
<
Vector3d
>
vertices
=
new
ArrayList
<>();
Vector3d
normal
=
r
.
calculateNormal
();
synchronized
(
tess
)
{
...
...
@@ -147,13 +151,17 @@ public class JoglTesselator {
GLU
.
gluTessEndPolygon
(
tess
);
ArrayList
<
Integer
>
indices
=
new
ArrayList
<>();
for
(
Primitive
primitive
:
primitives
)
{
primitive
.
fillListWithIndices
(
indices
);
primitive
.
fillListWithIndices
(
indices
,
discardDegeneratedTriangles
);
}
primitives
.
clear
();
return
new
TesselatedRing
(
vertices
,
indices
,
r
);
}
}
public
static
TesselatedRing
tesselateRing
(
LinearRing
r
)
{
return
tesselateRing
(
r
,
true
);
}
private
JoglTesselator
()
{
// only static useage
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/Primitive.java
View file @
39ffb121
...
...
@@ -49,30 +49,34 @@ public class Primitive {
this
.
type
=
type
;
}
public
void
fillListWithIndices
(
List
<
Integer
>
indices
)
{
public
void
fillListWithIndices
(
List
<
Integer
>
indices
,
boolean
discardDegeneratedTriangles
)
{
switch
(
type
)
{
case
GL
.
GL_TRIANGLES
:
for
(
int
i
=
0
;
i
<
pIndices
.
size
();
i
+=
3
)
{
addToListIfAreaNonNull
(
pIndices
.
get
(
i
),
pIndices
.
get
(
i
+
1
),
pIndices
.
get
(
i
+
2
),
indices
);
addToListIfAreaNonNull
(
pIndices
.
get
(
i
),
pIndices
.
get
(
i
+
1
),
pIndices
.
get
(
i
+
2
),
indices
,
discardDegeneratedTriangles
);
}
break
;
case
GL
.
GL_TRIANGLE_STRIP
:
for
(
int
i
=
0
;
i
<
pIndices
.
size
()
-
2
;
i
++)
{
if
(
i
%
2
==
0
)
{
addToListIfAreaNonNull
(
pIndices
.
get
(
i
),
pIndices
.
get
(
i
+
1
),
pIndices
.
get
(
i
+
2
),
indices
);
addToListIfAreaNonNull
(
pIndices
.
get
(
i
),
pIndices
.
get
(
i
+
1
),
pIndices
.
get
(
i
+
2
),
indices
,
discardDegeneratedTriangles
);
}
else
{
addToListIfAreaNonNull
(
pIndices
.
get
(
i
),
pIndices
.
get
(
i
+
2
),
pIndices
.
get
(
i
+
1
),
indices
);
addToListIfAreaNonNull
(
pIndices
.
get
(
i
),
pIndices
.
get
(
i
+
2
),
pIndices
.
get
(
i
+
1
),
indices
,
discardDegeneratedTriangles
);
}
}
break
;
case
GL
.
GL_TRIANGLE_FAN
:
Integer
first
=
pIndices
.
get
(
0
);
for
(
int
i
=
0
;
i
<
pIndices
.
size
()
-
2
;
i
++)
{
addToListIfAreaNonNull
(
first
,
pIndices
.
get
(
i
+
1
),
pIndices
.
get
(
i
+
2
),
indices
);
addToListIfAreaNonNull
(
first
,
pIndices
.
get
(
i
+
1
),
pIndices
.
get
(
i
+
2
),
indices
,
discardDegeneratedTriangles
);
}
break
;
default
:
throw
new
IllegalStateException
(
"unknown type found: "
+
type
);
throw
new
IllegalStateException
(
"unknown
triangle
type found: "
+
type
);
}
}
...
...
@@ -80,17 +84,18 @@ public class Primitive {
pIndices
.
add
(
i
);
}
private
void
addToListIfAreaNonNull
(
Integer
i1
,
Integer
i2
,
Integer
i3
,
List
<
Integer
>
indices
)
{
private
void
addToListIfAreaNonNull
(
Integer
i1
,
Integer
i2
,
Integer
i3
,
List
<
Integer
>
indices
,
boolean
discardDegeneratedTriangles
)
{
Vector3d
v1
=
vertices
.
get
(
i1
);
Vector3d
v2
=
vertices
.
get
(
i2
);
Vector3d
v3
=
vertices
.
get
(
i3
);
double
area
=
Math
.
abs
(
calculateAreaOfTriangle
(
v1
,
v2
,
v3
));
if
(
area
>
AREA_EPSILON
)
{
if
(
discardDegeneratedTriangles
&&
Math
.
abs
(
calculateAreaOfTriangle
(
v1
,
v2
,
v3
))
<=
AREA_EPSILON
)
{
// switch to discard degenerated triangles is enabled and triangle has nearly no area
logger
.
trace
(
"Tesselator created degenerated triangle, ignoring"
);
}
else
{
indices
.
add
(
i1
);
indices
.
add
(
i2
);
indices
.
add
(
i3
);
}
else
{
logger
.
trace
(
"Tesselator created degenerated triangle, ignoring"
);
}
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/PolygonIntersection.java
View file @
39ffb121
...
...
@@ -25,6 +25,7 @@ import java.util.List;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.math.Segment3d
;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
/**
* Result of a polygon polygon intersection.
...
...
@@ -38,7 +39,7 @@ public class PolygonIntersection implements Serializable {
private
static
final
long
serialVersionUID
=
-
6301963226688351725L
;
public
enum
IntersectionType
{
NONE
,
LINE
,
POLYGON
NONE
,
LINE
,
POLYGON
,
TRIANGLES
}
private
final
IntersectionType
type
;
...
...
@@ -48,10 +49,30 @@ public class PolygonIntersection implements Serializable {
private
Polygon
p1
;
private
Polygon
p2
;
private
Triangle3d
t1
;
private
Triangle3d
t2
;
public
static
PolygonIntersection
none
()
{
return
new
PolygonIntersection
(
IntersectionType
.
NONE
);
}
public
static
PolygonIntersection
triangles
(
Triangle3d
t1
,
Triangle3d
t2
)
{
PolygonIntersection
poly
=
new
PolygonIntersection
(
IntersectionType
.
TRIANGLES
);
poly
.
p1
=
t1
.
getPartOf
().
getOriginal
();
poly
.
p2
=
t2
.
getPartOf
().
getOriginal
();
poly
.
t1
=
t1
;
poly
.
t2
=
t2
;
return
poly
;
}
public
static
PolygonIntersection
any
(
Polygon
p1
,
Polygon
p2
)
{
PolygonIntersection
poly
=
new
PolygonIntersection
(
IntersectionType
.
POLYGON
);
poly
.
p1
=
p1
;
poly
.
p2
=
p2
;
return
poly
;
}
public
static
PolygonIntersection
lines
(
List
<
Segment3d
>
lines
,
Polygon
p1
,
Polygon
p2
)
{
PolygonIntersection
poly
=
new
PolygonIntersection
(
IntersectionType
.
LINE
);
poly
.
lines
=
lines
;
...
...
@@ -76,6 +97,14 @@ public class PolygonIntersection implements Serializable {
return
p2
;
}
public
Triangle3d
getT1
()
{
return
t1
;
}
public
Triangle3d
getT2
()
{
return
t2
;
}
private
PolygonIntersection
(
IntersectionType
type
)
{
this
.
type
=
type
;
}
...
...
CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties
View file @
39ffb121
...
...
@@ -120,3 +120,12 @@ MainWindow.memoryLabel=Memory:
CheckDialog.checksReenabledAlert
=
Some checks have been reenabled so that other wanted checks can be executed
\n
See the log for more information.
MainWindow.availableLabel
=
Available:
OpenFileDialog.lowMemoryLabel
=
Low Memory Consumption Mode
ErrorItemVisitor.intersection
=
Intersection
ErrorItemVisitor.component
=
Component
ErrorItemVisitor.nameOfAttribute
=
Name of attribute
ErrorItemVisitor.childId
=
Child id
ErrorItemVisitor.distance
=
Distance
ErrorItemVisitor.pointTouchesEdge
=
Point touches edge
ErrorItemVisitor.degeneratedRing
=
Degenerated ring
ErrorItemVisitor.deviation
=
Deviation
ErrorItemVisitor.triangles
=
Triangles
\ No newline at end of file
CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties
View file @
39ffb121
...
...
@@ -118,3 +118,12 @@ MainWindow.memoryLabel=Speicher:
CheckDialog.checksReenabledAlert
=
Manche Pr
\u
00fcfungen wurden reaktiviert damit andere gewollte Pr
\u
00fcfungen durchgef
\u
00fchrt werden k
\u
00f6nnen
\n
Mehr Details sind im Log geschrieben
MainWindow.availableLabel
=
Verf
\u
00fcgbar:
OpenFileDialog.lowMemoryLabel
=
Reduzierter Speicherverbrauchsmodus
ErrorItemVisitor.intersection
=
Verschneidung
ErrorItemVisitor.component
=
Komponente
ErrorItemVisitor.nameOfAttribute
=
Name des Attributs
ErrorItemVisitor.childId
=
Kind ID
ErrorItemVisitor.distance
=
Distanz
ErrorItemVisitor.pointTouchesEdge
=
Punkt ber
\u
00fchrt Kante
ErrorItemVisitor.degeneratedRing
=
Degenerierter Ring
ErrorItemVisitor.deviation
=
Abweichung
ErrorItemVisitor.triangles
=
Dreiecke
\ No newline at end of file
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java
View file @
39ffb121
...
...
@@ -27,19 +27,16 @@ 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.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection
;
import
de.hft.stuttgart.citydoctor2.check.Requirement
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
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.math.Segment3d
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection.IntersectionType
;
/**
* Check for self intersecting solids
...
...
@@ -78,7 +75,8 @@ public class SolidSelfIntCheck extends Check {
return
;
}
CheckResult
cr
;
List
<
PolygonIntersection
>
intersections
=
SelfIntersectionUtil
.
doesSolidSelfIntersect2
(
g
);
List
<
PolygonIntersection
>
intersections
=
SelfIntersectionUtil
.
calculateSolidSelfIntersection
(
g
);
// List<PolygonIntersection> intersections = SelfIntersectionUtil.doesSolidSelfIntersect2(g);
if
(
intersections
.
isEmpty
())
{
cr
=
new
CheckResult
(
this
,
ResultStatus
.
OK
,
null
);
}
else
{
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java
View file @
39ffb121
...
...
@@ -62,6 +62,7 @@ import de.hft.stuttgart.citydoctor2.math.Segment3d;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
import
de.hft.stuttgart.citydoctor2.math.Vector2d
;
import
de.hft.stuttgart.citydoctor2.math.Vector3d
;
import
de.hft.stuttgart.citydoctor2.tesselation.EarcutTesselator
;
import
de.hft.stuttgart.citydoctor2.tesselation.JoglTesselator
;
import
de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
...
...
@@ -75,7 +76,6 @@ import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection.IntersectionType;
*/
public
class
SelfIntersectionUtil
{
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
SelfIntersectionUtil
.
class
);
private
static
final
GeometryFactory
factory
=
new
GeometryFactory
(
new
PrecisionModel
(
PrecisionModel
.
FLOATING
));
...
...
@@ -84,6 +84,25 @@ public class SelfIntersectionUtil {
}
public
static
List
<
PolygonIntersection
>
calculateSolidSelfIntersection
(
Geometry
g
)
{
List
<
TesselatedPolygon
>
tesselatedPolygons
=
new
ArrayList
<>();
for
(
Polygon
p
:
g
.
getPolygons
())
{
tesselatedPolygons
.
add
(
EarcutTesselator
.
tesselatePolygon
(
p
));
}
List
<
PolygonIntersection
>
intersections
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
()
-
1
;
i
++)
{
TesselatedPolygon
p1
=
tesselatedPolygons
.
get
(
i
);
for
(
int
j
=
i
+
1
;
j
<
tesselatedPolygons
.
size
();
j
++)
{
TesselatedPolygon
p2
=
tesselatedPolygons
.
get
(
j
);
GeometrySelfIntersection
intersection
=
doPolygonsIntersect
(
p1
,
p2
);
if
(
intersection
!=
null
)
{
intersections
.
add
(
PolygonIntersection
.
triangles
(
intersection
.
t1
(),
intersection
.
t2
()));
}
}
}
return
intersections
;
}
public
static
GeometrySelfIntersection
doesSolidSelfIntersect
(
Geometry
g
)
{
return
selfIntersectionJava
(
g
);
}
...
...
@@ -114,14 +133,15 @@ public class SelfIntersectionUtil {
return
intersections
;
}
private
static
void
performEdgeSelfIntersection
(
List
<
PolygonIntersection
>
intersections
,
Map
<
Polygon
,
EdgePolygon
>
edgePolyMap
,
Polygon
p1
,
Polygon
p2
)
{
private
static
void
performEdgeSelfIntersection
(
List
<
PolygonIntersection
>
intersections
,
Map
<
Polygon
,
EdgePolygon
>
edgePolyMap
,
Polygon
p1
,
Polygon
p2
)
{
EdgePolygon
edgeP1
=
edgePolyMap
.
get
(
p1
);
EdgePolygon
edgeP2
=
edgePolyMap
.
get
(
p2
);
List
<
PolygonPolygonIntersection
>
results
=
IntersectPlanarPolygons
.
intersectPolygons
(
edgeP1
,
edgeP2
,
0.000001
,
0.001
);
List
<
PolygonPolygonIntersection
>
results
=
IntersectPlanarPolygons
.
intersectPolygons
(
edgeP1
,
edgeP2
,
0.000001
,
0.001
);
if
(!
results
.
isEmpty
())
{
// at least one intersection happened
for
(
PolygonPolygonIntersection
result:
results
)
{
for
(
PolygonPolygonIntersection
result
:
results
)
{
// Ensure that Intersection-Edge is not a Null-Line.
// If it is a Null-Line, detected Intersection is a False-Positive.
if
(!
result
.
getPolyLine
().
isNullLine
())
{
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/GeometryTestUtils.java
View file @
39ffb121
...
...
@@ -21,6 +21,7 @@ package de.hft.stuttgart.citydoctor2.checks.util;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.StringJoiner
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ListErrorVisitor.java
View file @
39ffb121
...
...
@@ -43,6 +43,7 @@ import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import
de.hft.stuttgart.citydoctor2.datastructure.Edge
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
import
javafx.scene.paint.Color
;
public
class
ListErrorVisitor
implements
ErrorVisitor
{
...
...
@@ -171,7 +172,11 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
SolidSelfIntError
err
)
{
// nothing to display for now
controller
.
clearHighlights
();
for
(
PolygonIntersection
intersection
:
err
.
getIntersections
())
{
controller
.
addHighlight
(
intersection
.
getP1
(),
geom
);
controller
.
addHighlight
(
intersection
.
getP2
(),
geom
);
}
}
@Override
...
...
@@ -208,22 +213,22 @@ public class ListErrorVisitor implements ErrorVisitor {
@Override
public
void
visit
(
NotCeilingError
err
)
{
// nothing to display
controller
.
highlight
(
err
.
getPolygon
(),
geom
);
}
@Override
public
void
visit
(
NotFloorError
err
)
{
// nothing to display
controller
.
highlight
(
err
.
getPolygon
(),
geom
);
}
@Override
public
void
visit
(
NotWallError
err
)
{
// nothing to display
controller
.
highlight
(
err
.
getPolygon
(),
geom
);
}
@Override
public
void
visit
(
NotGroundError
err
)
{
// nothing to display
controller
.
highlight
(
err
.
getPolygon
(),
geom
);
}
@Override
...
...
CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorItemVisitor.java
View file @
39ffb121
...
...
@@ -39,16 +39,23 @@ import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import
de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Edge
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.gui.CheckStatus
;
import
de.hft.stuttgart.citydoctor2.math.Triangle3d
;
import
de.hft.stuttgart.citydoctor2.utils.Localization
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection.IntersectionType
;
import
javafx.scene.control.TreeItem
;
public
class
ErrorItemVisitor
implements
ErrorVisitor
{
private
static
final
String
NAME_OF_ATTRIBUTE
=
"Name of
Attribute: "
;
private
static
final
String
CHILD_ID
=
"ChildId
: "
;
private
static
String
nameOfAttribute
=
Localization
.
getText
(
"ErrorItemVisitor.nameOf
Attribute
"
)
+
"
: "
;
private
static
String
childId
=
Localization
.
getText
(
"ErrorItemVisitor.childId"
)
+
"
: "
;
private
final
TreeItem
<
Renderable
>
root
;
public
ErrorItemVisitor
(
TreeItem
<
Renderable
>
root
)
{
...
...
@@ -74,7 +81,7 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
MultipleConnectedComponentsError
err
)
{
for
(
int
i
=
0
;
i
<
err
.
getComponents
().
size
();
i
++)
{
TextNode
textNode
=
new
TextNode
(
"C
omponent "
+
i
);
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.c
omponent
"
)
+
"
"
+
i
);
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
List
<
Polygon
>
component
=
err
.
getComponents
().
get
(
i
);
...
...
@@ -99,7 +106,7 @@ public class ErrorItemVisitor implements ErrorVisitor {
TreeItem
<
Renderable
>
vertexItem
=
new
TreeItem
<>(
vertexNode
);
root
.
getChildren
().
add
(
vertexItem
);
for
(
int
i
=
0
;
i
<
err
.
getComponents
().
size
();
i
++)
{
TextNode
textNode
=
new
TextNode
(
"C
omponent "
+
i
);
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.c
omponent
"
)
+
"
"
+
i
);
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
List
<
Polygon
>
component
=
err
.
getComponents
().
get
(
i
);
...
...
@@ -207,7 +214,8 @@ public class ErrorItemVisitor implements ErrorVisitor {
TreeItem
<
Renderable
>
polyItem
=
new
TreeItem
<>(
polyNode
);
root
.
getChildren
().
add
(
polyItem
);
TextNode
textNode
=
new
TextNode
(
"Deviation: "
+
err
.
getDeviation
());
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.deviation"
)
+
": "
+
err
.
getDeviation
());
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
}
...
...
@@ -222,7 +230,8 @@ public class ErrorItemVisitor implements ErrorVisitor {
TreeItem
<
Renderable
>
vertexItem
=
new
TreeItem
<>(
vertexNode
);
root
.
getChildren
().
add
(
vertexItem
);
TextNode
textNode
=
new
TextNode
(
"Distance: "
+
err
.
getDistance
()
+
"m"
);
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.distance"
)
+
": "
+
err
.
getDistance
()
+
"m"
);
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
...
...
@@ -237,12 +246,48 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
SolidSelfIntError
err
)
{
PolygonNode
polyNode1
=
new
PolygonNode
(
err
.
getIntersections
().
get
(
0
).
getP1
(),
CheckStatus
.
NOT_CHECKED
);
List
<
PolygonIntersection
>
intersections
=
err
.
getIntersections
();
for
(
int
i
=
0
;
i
<
intersections
.
size
();
i
++)
{
PolygonIntersection
pi
=
intersections
.
get
(
i
);
TextNode
intersectionNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.intersection"
)
+
" "
+
i
);
TreeItem
<
Renderable
>
intersectionItem
=
new
TreeItem
<>(
intersectionNode
);
root
.
getChildren
().
add
(
intersectionItem
);
PolygonNode
polyNode1
=
new
PolygonNode
(
pi
.
getP1
(),
CheckStatus
.
NOT_CHECKED
);
TreeItem
<
Renderable
>
polyItem1
=
new
TreeItem
<>(
polyNode1
);
root
.
getChildren
().
add
(
polyItem1
);
PolygonNode
polyNode2
=
new
PolygonNode
(
err
.
getIntersections
().
get
(
0
)
.
getP2
(),
CheckStatus
.
NOT_CHECKED
);
intersectionItem
.
getChildren
().
add
(
polyItem1
);
PolygonNode
polyNode2
=
new
PolygonNode
(
pi
.
getP2
(),
CheckStatus
.
NOT_CHECKED
);
TreeItem
<
Renderable
>
polyItem2
=
new
TreeItem
<>(
polyNode2
);
root
.
getChildren
().
add
(
polyItem2
);
intersectionItem
.
getChildren
().
add
(
polyItem2
);
PolygonIntersection
intersection
=
err
.
getIntersections
().
get
(
0
);
if
(
intersection
.
getType
()
==
IntersectionType
.
TRIANGLES
)
{
TextNode
trianglesNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.triangles"
));
TreeItem
<
Renderable
>
trianglesItem
=
new
TreeItem
<>(
trianglesNode
);
intersectionItem
.
getChildren
().
add
(
trianglesItem
);
ConcretePolygon
c
=
createPolygonForTriangle
(
intersection
.
getT1
());
PolygonNode
pN1
=
new
PolygonNode
(
c
,
CheckStatus
.
NOT_CHECKED
);
TreeItem
<
Renderable
>
pI1
=
new
TreeItem
<>(
pN1
);
trianglesItem
.
getChildren
().
add
(
pI1
);
ConcretePolygon
c2
=
createPolygonForTriangle
(
intersection
.
getT2
());
PolygonNode
pN2
=
new
PolygonNode
(
c2
,
CheckStatus
.
NOT_CHECKED
);
TreeItem
<
Renderable
>
pI2
=
new
TreeItem
<>(
pN2
);
trianglesItem
.
getChildren
().
add
(
pI2
);
}
}
}
private
ConcretePolygon
createPolygonForTriangle
(
Triangle3d
t
)
{
ConcretePolygon
c
=
new
ConcretePolygon
();
LinearRing
extRing
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
Vertex
start
=
new
Vertex
(
t
.
getP1
());
extRing
.
getVertices
().
add
(
start
);
extRing
.
getVertices
().
add
(
new
Vertex
(
t
.
getP2
()));
extRing
.
getVertices
().
add
(
new
Vertex
(
t
.
getP3
()));
extRing
.
getVertices
().
add
(
start
);
c
.
setExteriorRing
(
extRing
);
return
c
;
}
@Override
...
...
@@ -288,7 +333,7 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
PointTouchesEdgeError
err
)
{
TextNode
textNode
=
new
TextNode
(
"P
oint
t
ouches
e
dge"
);
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.p
oint
T
ouches
E
dge"
)
)
;
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
...
...
@@ -347,14 +392,15 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
SurfaceUnfragmentedError
err
)
{
TextNode
textNode
=
new
TextNode
(
"Deviation: "
+
err
.
getAngleDeviation
());
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.deviation"
)
+
": "
+
err
.
getAngleDeviation
());
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
}
@Override
public
void
visit
(
DegeneratedRingError
err
)
{
TextNode
textNode
=
new
TextNode
(
"Type:
degenerated
r
ing"
);
TextNode
textNode
=
new
TextNode
(
Localization
.
getText
(
"ErrorItemVisitor.
degenerated
R
ing"
)
)
;
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
...
...
@@ -366,11 +412,11 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
AttributeMissingError
err
)
{
if
(!
err
.
getChildId
().
isEmpty
())
{
TextNode
textNode
=
new
TextNode
(
CHILD_ID
+
err
.
getChildId
());
TextNode
textNode
=
new
TextNode
(
childId
+
err
.
getChildId
());
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
}
TextNode
nameNode
=
new
TextNode
(
NAME_OF_ATTRIBUTE
+
err
.
getNameOfAttribute
());
TextNode
nameNode
=
new
TextNode
(
nameOfAttribute
+
err
.
getNameOfAttribute
());
TreeItem
<
Renderable
>
nameItem
=
new
TreeItem
<>(
nameNode
);
root
.
getChildren
().
add
(
nameItem
);
...
...
@@ -379,11 +425,11 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
AttributeValueWrongError
err
)
{
if
(!
err
.
getChildId
().
isEmpty
())
{
TextNode
textNode
=
new
TextNode
(
CHILD_ID
+
err
.
getChildId
());
TextNode
textNode
=
new
TextNode
(
childId
+
err
.
getChildId
());
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
}
TextNode
nameNode
=
new
TextNode
(
NAME_OF_ATTRIBUTE
+
err
.
getNameOfAttribute
());
TextNode
nameNode
=
new
TextNode
(
nameOfAttribute
+
err
.
getNameOfAttribute
());
TreeItem
<
Renderable
>
nameItem
=
new
TreeItem
<>(
nameNode
);
root
.
getChildren
().
add
(
nameItem
);
}
...
...
@@ -391,11 +437,11 @@ public class ErrorItemVisitor implements ErrorVisitor {
@Override
public
void
visit
(
AttributeInvalidError
err
)
{
if
(!
err
.
getChildId
().
isEmpty
())
{
TextNode
textNode
=
new
TextNode
(
CHILD_ID
+
err
.
getChildId
());
TextNode
textNode
=
new
TextNode
(
childId
+
err
.
getChildId
());
TreeItem
<
Renderable
>
textItem
=
new
TreeItem
<>(
textNode
);
root
.
getChildren
().
add
(
textItem
);
}
TextNode
nameNode
=
new
TextNode
(
NAME_OF_ATTRIBUTE
+
err
.
getNameOfAttribute
());
TextNode
nameNode
=
new
TextNode
(
nameOfAttribute
+
err
.
getNameOfAttribute
());
TreeItem
<
Renderable
>
nameItem
=
new
TreeItem
<>(
nameNode
);
root
.
getChildren
().
add
(
nameItem
);
}
...
...
CityDoctorParent/pom.xml
View file @
39ffb121
<?xml version="1.0" encoding="utf-8"?>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
>
<modelVersion>
4.0.0
</modelVersion>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
...
...
@@ -221,7 +220,7 @@
<dependency>
<groupId>
org.locationtech.jts
</groupId>
<artifactId>
jts-core
</artifactId>
<version>
1.
19
.0
</version>
<version>
1.
20
.0
</version>
</dependency>
<dependency>
<groupId>
com.sun.xml.bind
</groupId>
...
...
@@ -233,6 +232,11 @@
<artifactId>
jakarta.xml.bind-api
</artifactId>
<version>
2.3.3
</version>
</dependency>
<dependency>
<groupId>
io.github.earcut4j
</groupId>
<artifactId>
earcut4j
</artifactId>
<version>
2.2.2
</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
...
...
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