1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
# Copyright (c) 2022 Ultimaker B.V.
# Uranium is released under the terms of the LGPLv3 or higher.
from UM.Math.Polygon import Polygon
from UM.Math.Float import Float
import numpy
import math
import pytest
#pytestmark = pytest.mark.skip(reason = "Incomplete tests")
class TestPolygon:
def setup_method(self, method):
# Called before the first testfunction is executed
pass
def teardown_method(self, method):
# Called after the last testfunction was executed
pass
def test_equalitySamePolygon(self):
polygon = Polygon.approximatedCircle(2)
assert polygon == polygon
def test_equalityString(self):
polygon = Polygon.approximatedCircle(42)
assert polygon != "42" # Not the answer after all!
def test_equality(self):
polygon_1 = Polygon.approximatedCircle(2)
polygon_2 = Polygon.approximatedCircle(2)
assert polygon_1 == polygon_2
def test_inequality(self):
# This case should short cirquit because the length of points are not the same
polygon_1 = Polygon.approximatedCircle(2)
polygon_2 = Polygon()
assert polygon_1 != polygon_2
def test_translate(self):
polygon = Polygon(numpy.array([[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], numpy.float32))
translated_poly = polygon.translate(2, 3)
result = Polygon(numpy.array([[2.0, 3.0], [4.0, 3.0], [3.0, 5.0]], numpy.float32))
assert result == translated_poly
def test_translateInvalidPoly(self):
polygon = Polygon()
assert polygon == polygon.translate(2, 3)
## The individual test cases for mirroring polygons.
test_mirror_data = [
({ "points": [[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], "axis_point": [0, 0], "axis_direction": [0, 1], "answer": [[-1.0, 2.0], [-2.0, 0.0], [0.0, 0.0]], "label": "Mirror Horizontal", "description": "Test mirroring a polygon horizontally." }),
({ "points": [[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], "axis_point": [0, 0], "axis_direction": [1, 0], "answer": [[1.0, -2.0], [2.0, 0.0], [0.0, 0.0]], "label": "Mirror Vertical", "description": "Test mirroring a polygon vertically." }),
({ "points": [[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], "axis_point": [10, 0], "axis_direction": [0, 1], "answer": [[19.0, 2.0], [18.0, 0.0], [20.0, 0.0]], "label": "Mirror Horizontal Far", "description": "Test mirrorring a polygon horizontally on an axis that is not through the origin." }),
({ "points": [[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], "axis_point": [0, 4], "axis_direction": [1, 1], "answer": [[-2.0, 5.0], [-4.0, 6.0], [-4.0, 4.0]], "label": "Mirror Diagonal", "description": "Test mirroring a polygon diagonally." }),
({ "points": [], "axis_point": [0, 0], "axis_direction": [1, 0], "answer": [], "label": "Mirror Empty", "description": "Test mirroring an empty polygon." }),
({ "points": [[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], "axis_point": [0, 4], "axis_direction": [0, 0], "answer": [[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]], "label": "Mirror Diagonal", "description": "Test mirroring with wrong axis"}),
]
## Tests the mirror function.
#
# \param data The data of the test. Must include a list of points of the
# polygon to mirror, a point on the axis, a direction of the axis and an
# answer that is the result of the mirroring.
@pytest.mark.parametrize("data", test_mirror_data)
def test_mirror(self, data):
polygon = Polygon(numpy.array(data["points"], numpy.float32)) #Create a polygon with the specified points.
mirrored_poly = polygon.mirror(data["axis_point"], data["axis_direction"]) #Mirror over the specified axis.
points = mirrored_poly.getPoints()
assert len(points) == len(data["points"]) #Must have the same amount of vertices.
for point_index in range(len(points)):
assert len(points[point_index]) == len(data["answer"][point_index]) #Same dimensionality (2).
for dimension in range(len(points[point_index])):
assert Float.fuzzyCompare(points[point_index][dimension], data["answer"][point_index][dimension]) #All points must be equal.
## The individual test cases for the projection tests.
test_project_data = [
({ "normal": [0.0, 1.0], "answer": [1.0, 2.0], "label": "Project Vertical", "description": "Project the polygon onto a vertical line." }),
({ "normal": [1.0, 0.0], "answer": [0.0, 1.0], "label": "Project Horizontal", "description": "Project the polygon onto a horizontal line." }),
({ "normal": [math.sqrt(0.5), math.sqrt(0.5)], "answer": [math.sqrt(0.5), math.sqrt(4.5)], "label": "Project Diagonal", "description": "Project the polygon onto a diagonal line." })
]
## Tests the project function.
#
# \param data The data of the test. Must include a normal vector to
# project on and a pair of coordinates that is the answer.
@pytest.mark.parametrize("data", test_project_data)
def test_project(self, data):
p = Polygon(numpy.array([
[0.0, 1.0],
[1.0, 1.0],
[1.0, 2.0],
[0.0, 2.0]
], numpy.float32))
result = p.project(data["normal"]) # Project the polygon onto the specified normal vector.
assert len(result) == len(data["answer"]) # Same dimensionality (2).
for dimension in range(len(result)):
assert Float.fuzzyCompare(result[dimension], data["answer"][dimension])
## The individual test cases for the intersection tests.
test_intersect_data = [
({ "polygon": [[ 5.0, 0.0], [15.0, 0.0], [15.0, 10.0], [ 5.0, 10.0]], "answer": [ 5.0, 10], "label": "Intersect Simple", "description": "Intersect with a polygon that fully intersects." }),
({ "polygon": [[-5.0, 0.0], [ 5.0, 0.0], [ 5.0, 10.0], [-5.0, 10.0]], "answer": [ 5.0, 10.0], "label": "Intersect Left", "description": "Intersect with a polygon on the negative x-axis side that fully intersects." }),
({ "polygon": [[ 0.0, 5.0], [10.0, 5.0], [10.0, 15.0], [ 0.0, 15.0]], "answer": [ 10.0, 5.0], "label": "Intersect Straight Above", "description": "Intersect with a polygon that is exactly above the base polygon (edge case)." }),
({ "polygon": [[ 0.0, -5.0], [10.0, -5.0], [10.0, 5.0], [ 0.0, 5.0]], "answer": [ 10.0, 5.0], "label": "Intersect Straight Left", "description": "Intersect with a polygon that is exactly left of the base polygon (edge case)." }),
({ "polygon": [[15.0, 0.0], [25.0, 0.0], [25.0, 10.0], [15.0, 10.0]], "answer": None, "label": "Intersect Miss", "description": "Intersect with a polygon that doesn't intersect at all." }),
({"polygon": [[15.0, 0.0]], "answer": None, "label": "Intersect invalid", "description": "Intersect with a polygon that is a single point"})
]
## Tests the polygon intersect function.
#
# Every test case intersects a parametrised polygon with a base square of
# 10 by 10 units at the origin.
#
# \param data The data of the test. Must include a polygon to intersect
# with and a required answer.
@pytest.mark.parametrize("data", test_intersect_data)
def test_intersectsPolygon(self, data):
p1 = Polygon(numpy.array([ # The base polygon to intersect with.
[ 0, 0],
[10, 0],
[10, 10],
[ 0, 10]
], numpy.float32))
p2 = Polygon(numpy.array(data["polygon"])) # The parametrised polygon to intersect with.
# Shift the order of vertices in both polygons around. The outcome should be independent of what the first vertex is.
for n in range(0, len(p1.getPoints())):
for m in range(0, len(data["polygon"])):
result = p1.intersectsPolygon(p2)
if not data["answer"]: # Result should be None.
assert result is None
else:
assert result is not None
for i in range(0, len(data["answer"])):
assert Float.fuzzyCompare(result[i], data["answer"][i])
p2 = Polygon(numpy.roll(p2.getPoints(), 1, axis = 0)) #Shift p2.
p1 = Polygon(numpy.roll(p1.getPoints(), 1, axis = 0)) #Shift p1.
## The individual test cases for convex hull intersection tests.
test_intersectConvex_data = [
({ "p1": [[-42, -32], [-42, 12], [62, 12], [62, -32]], "p2": [[-62, -12], [-62, 32], [42, 32], [42, -12]], "answer": [[42, -12], [42, 12], [-42, 12], [-42, -12]], "label": "UM2 Fans", "description": "A simple intersection without edge cases of UM2 fans collision area." })
]
## Tests the convex hull intersect function.
#
# \param data The data of the test case. Must include two polygons and a
# required result polygon.
@pytest.mark.parametrize("data", test_intersectConvex_data)
def test_intersectConvexHull(self, data) -> None:
p1 = Polygon(numpy.array(data["p1"]))
p2 = Polygon(numpy.array(data["p2"]))
result = p1.intersectionConvexHulls(p2)
assert len(result.getPoints()) == len(data["answer"]) # Same amount of vertices.
isCorrect = False
for rotation in range(0, len(result.getPoints())): # The order of vertices doesn't matter, so rotate the result around and if any check succeeds, the answer is correct.
thisCorrect = True # Is this rotation correct?
for vertex in range(0, len(result.getPoints())):
for dimension in range(0, len(result.getPoints()[vertex])):
if not Float.fuzzyCompare(result.getPoints()[vertex][dimension], data["answer"][vertex][dimension]):
thisCorrect = False
break # Break out of two loops.
if not thisCorrect:
break
if thisCorrect: # All vertices checked and it's still correct.
isCorrect = True
break
result = Polygon(numpy.roll(result.getPoints(), 1, axis = 0)) #Perform the rotation for the next check.
assert isCorrect
## The individual test cases for convex hull union tests.
test_unionConvex_data = [
({"p1": [[1, 1], [1.5, 1], [2, 2], [2, 0]], "p2": [[3, 2], [3.5, 1], [4, 1], [3, 0]], "answer": [[1, 1], [2, 2], [3, 2], [4, 1], [3, 0], [2, 0]], "label": "ConvexHull Union Separate", "description": "Two disparate shapes."}),
({"p1": [[1, 1], [3, 3], [3, -1]], "p2": [[2, 1], [4, 2], [4, 0]], "answer": [[1, 1], [3, 3], [4, 2], [4, 0], [3, -1]], "label": "ConvexHull Union Inside", "description": "One triangle partially inside the other."}),
({"p1": [[1, 1], [5, 5], [5, -4]], "p2": [[2, 1], [3, 2], [3, 1]], "answer": [[1, 1], [5, 5], [5, -4]], "label": "ConvexHull Union Enclosed", "description": "One triangle completely inside the other."}),
({"p1": [[1, 1], [2, 2], [2, 0]], "p2": [], "answer": [[1, 1], [2, 2], [2, 0]], "label": "ConvexHull Union Single", "description": "One triangle."})
]
## Tests the convex hull of convex hulls
#
# \param data The data of the test case.
@pytest.mark.parametrize("data", test_unionConvex_data)
def test_unionConvexHull(self, data) -> None:
p1 = Polygon(numpy.array(data["p1"]))
p2 = Polygon(numpy.array(data["p2"]))
result = p1.unionConvexHulls(p2)
assert len(result.getPoints()) == len(data["answer"]) # Same amount of vertices.
isCorrect = False
for rotation in range(0, len(result.getPoints())): # The order of vertices doesn't matter, so rotate the result around and if any check succeeds, the answer is correct.
thisCorrect = True # Is this rotation correct?
for vertex in range(0, len(result.getPoints())):
for dimension in range(0, len(result.getPoints()[vertex])):
if not Float.fuzzyCompare(result.getPoints()[vertex][dimension], data["answer"][vertex][dimension]):
thisCorrect = False
break # Break out of two loops.
if not thisCorrect:
break
if thisCorrect: # All vertices checked and it's still correct.
isCorrect = True
break
result = Polygon(numpy.roll(result.getPoints(), 1, axis = 0)) #Perform the rotation for the next check.
assert isCorrect
def test_isInside(self):
polygon = Polygon.approximatedCircle(5)
assert polygon.isInside((0, 0))
assert not polygon.isInside((9001, 9001))
|