def GeneratePolygonCycles(self, epsilon=1e-7): # Find and return, as a list of polygons, the cycles of the # graph that do not encompass any other part of the graph. # Here we ignore the direction of the edges of the graph. # If there is more than one connected component of the graph, # then this may not produce a desirable result. graph = self.Copy() for edge in self.edge_list: reverse_edge = (edge[1], edge[0], edge[2]) i = graph.FindEdge(reverse_edge, ignore_direction=False) if i is None: graph.edge_list.append(reverse_edge) from math2d_polygon import Polygon polygon_list = [] while len(graph.edge_list) > 0: edge = graph.edge_list[0] cycle_list, cycle_found = graph.FindCycleContainingEdge( edge, wind_ccw=True) if cycle_found: polygon = Polygon() polygon.vertex_list = [ graph.vertex_list[edge[0]] for edge in cycle_list ] if polygon.IsWoundCCW(epsilon): polygon_list.append(polygon) for edge in cycle_list: i = graph.FindEdge(edge, False, False) del graph.edge_list[i] return polygon_list
def GeneratePolygon(self): from math2d_polygon import Polygon polygon = Polygon() polygon.vertex_list.append(Vector(self.min_point.x, self.min_point.y)) polygon.vertex_list.append(Vector(self.max_point.x, self.min_point.y)) polygon.vertex_list.append(Vector(self.max_point.x, self.max_point.y)) polygon.vertex_list.append(Vector(self.min_point.x, self.max_point.y)) return polygon
def __init__(self): super().__init__() cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, 7.0 * math.sqrt(2.0)) hole = Polygon() hole.MakeRegularPolygon(4, 5.0 * math.sqrt(2.0)) cut_region.region.sub_region_list[0].hole_list.append(hole) transform = AffineTransform() transform.RigidBodyMotion(math.pi / 4.0, Vector(3.0, 3.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, 7.0 * math.sqrt(2.0)) hole = Polygon() hole.MakeRegularPolygon(4, 5.0 * math.sqrt(2.0)) cut_region.region.sub_region_list[0].hole_list.append(hole) transform = AffineTransform() transform.RigidBodyMotion(math.pi / 4.0, Vector(-3.0, -3.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, 3.0 * math.sqrt(2.0)) transform = AffineTransform() transform.RigidBodyMotion(math.pi / 4.0, Vector(0.0, 0.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region)
def __init__(self): super().__init__() sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(0.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(0.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(3.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(3.0, 0.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.region = Region() sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(0.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(0.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 0.0)) cut_region.region.sub_region_list.append(sub_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(2.0, -2.0)) sub_region.polygon.vertex_list.append(Vector(2.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(3.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(3.0, -2.0)) cut_region.region.sub_region_list.append(sub_region) self.cut_region_list.append(cut_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, -1.0)) hole = Polygon() hole.vertex_list.append(Vector(0.0, 0.0)) hole.vertex_list.append(Vector(0.0, 1.0)) hole.vertex_list.append(Vector(-1.0, 1.0)) hole.vertex_list.append(Vector(-1.0, 0.0)) sub_region.hole_list.append(hole) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) self.cut_region_list.append(cut_region) transform = AffineTransform() transform.Rotation(Vector(0.0, 0.0), math.pi / 4.0) for cut_region in self.cut_region_list: cut_region.Transform(transform)
def __init__(self): super().__init__() # This shape is almost a swashtica (I'm trying to avoid that for obvious reason) and # therefore only has rotational symmetry. sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 4.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, 4.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-4.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-4.0, -2.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, -2.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, -4.0)) sub_region.polygon.vertex_list.append(Vector(2.0, -4.0)) sub_region.polygon.vertex_list.append(Vector(2.0, -2.0)) sub_region.polygon.vertex_list.append(Vector(1.0, -2.0)) sub_region.polygon.vertex_list.append(Vector(1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(4.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(4.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(2.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(2.0, 1.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) self.cut_region_list.append(cut_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(-3.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(-3.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(3.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(3.0, 0.0)) hole = Polygon() hole.vertex_list.append(Vector(-1.0, -1.0)) hole.vertex_list.append(Vector(-1.0, -2.0)) hole.vertex_list.append(Vector(1.0, -2.0)) hole.vertex_list.append(Vector(1.0, -1.0)) sub_region.hole_list.append(hole) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) self.cut_region_list.append(cut_region)
def Transform(self, object): from math2d_polygon import Polygon from math2d_region import Region, SubRegion from math2d_aa_rect import AxisAlignedRectangle if isinstance(object, Vector): return self.linear_transform.Transform(object) + self.translation elif isinstance(object, AffineTransform): transform = AffineTransform() transform.linear_transform.x_axis = self.linear_transform.Transform( object.linear_transform.x_axis) transform.linear_transform.y_axis = self.linear_transform.Transform( object.linear_transform.y_axis) transform.translation = self.Transform(object.translation) return transform elif isinstance(object, LineSegment): return LineSegment(self.Transform(object.point_a), self.Transform(object.point_b)) elif isinstance(object, Polygon): polygon = Polygon() for vertex in object.vertex_list: polygon.vertex_list.append(self.Transform(vertex)) return polygon elif isinstance(object, Region): region = Region() for sub_region in object.sub_region_list: region.sub_region_list.append(self.Transform(sub_region)) return region elif isinstance(object, SubRegion): sub_region = SubRegion() sub_region.polygon = self.Transform(object.polygon) for hole in object.hole_list: sub_region.hole_list.append(self.Transform(hole)) return sub_region elif isinstance(object, AxisAlignedRectangle): return AxisAlignedRectangle( min_point=self.Transform(object.min_point), max_point=self.Transform(object.max_point))
def __init__(self): super().__init__() cut_region = CutRegion() cut_region.GenerateRegularPolygon(5, 5.0) hole = Polygon() hole.MakeRegularPolygon(5, 4.0) cut_region.region.sub_region_list[0].hole_list.append(hole) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(5, 3.0) hole = Polygon() hole.MakeRegularPolygon(5, 2.0) cut_region.region.sub_region_list[0].hole_list.append(hole) self.cut_region_list.append(cut_region) # This is almost like puzzle 4, but with a subtle difference. cut_region = CutRegion() cut_region.GenerateRectangle(5.0, 1.0) transform = AffineTransform() transform.translation = Vector(-3.0, 0.0) cut_region.Transform(transform) self.cut_region_list.append(cut_region)
def __init__(self): super().__init__() cut_region = CutRegion() cut_region.GenerateRegularPolygon(5, 5.0) hole = Polygon() hole.MakeRegularPolygon(5, 4.0) cut_region.region.sub_region_list[0].hole_list.append(hole) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(5, 3.0) hole = Polygon() hole.MakeRegularPolygon(5, 2.0) cut_region.region.sub_region_list[0].hole_list.append(hole) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRectangle(5.0, 1.0) transform = AffineTransform() transform.translation = Vector(3.5 * math.cos(math.pi * 4.0 / 5.0), 0.0) cut_region.Transform(transform) self.cut_region_list.append(cut_region)
def GenerateLineMesh(self, thickness=0.5, epsilon=1e-7): half_thickness = thickness / 2.0 from math2d_tri_mesh import TriangleMesh, Triangle from math2d_polygon import Polygon from math2d_affine_transform import AffineTransform mesh = TriangleMesh() def SortKey(vector): angle = Vector(1.0, 0.0).SignedAngleBetween(vector) if angle < 0.0: angle += 2.0 * math.pi return angle polygon_list = [] for i in range(len(self.vertex_list)): center = self.vertex_list[i] vector_list = [] adj_list = self.FindAllAdjacencies(i, ignore_direction=True, vertices=True) for j in adj_list: vector_list.append(self.vertex_list[j] - center) vector_list.sort(key=SortKey) polygon = Polygon() for j in range(len(vector_list)): vector_a = vector_list[j] vector_b = vector_list[(j + 1) % len(vector_list)] angle = vector_a.SignedAngleBetween(vector_b) if angle < 0.0: angle += 2.0 * math.pi if math.fabs(angle - math.pi) < epsilon or angle < math.pi: affine_transform = AffineTransform() affine_transform.translation = center affine_transform.linear_transform.x_axis = vector_a.Normalized( ) affine_transform.linear_transform.y_axis = affine_transform.linear_transform.x_axis.RotatedCCW90( ) if math.fabs(angle - math.pi) < epsilon: length = 0.0 else: length = half_thickness / math.tan(angle / 2.0) point = affine_transform * Vector(length, half_thickness) polygon.vertex_list.append(point) else: # Here we might create a rounded joint or something fancy, but this is good for now. polygon.vertex_list.append(vector_a.Normalized( ).RotatedCCW90().Scaled(half_thickness) + center) polygon.vertex_list.append(vector_b.Normalized( ).RotatedCW90().Scaled(half_thickness) + center) polygon.Tessellate() mesh.AddMesh(polygon.mesh) polygon_list.append(polygon) for edge in self.edge_list: center_a = self.vertex_list[edge[0]] center_b = self.vertex_list[edge[1]] line_segment = LineSegment(center_a, center_b) def FindEdgeSegment(polygon): for edge_segment in polygon.GenerateLineSegments(): point = edge_segment.IntersectWith(line_segment) if point is not None: return edge_segment else: raise Exception('Failed to find line quad end.') polygon_a = polygon_list[edge[0]] polygon_b = polygon_list[edge[1]] edge_segment_a = FindEdgeSegment(polygon_a) edge_segment_b = FindEdgeSegment(polygon_b) triangle_a = Triangle(edge_segment_a.point_a, edge_segment_b.point_b, edge_segment_b.point_a) triangle_b = Triangle(edge_segment_a.point_a, edge_segment_b.point_a, edge_segment_a.point_b) area_a = triangle_a.Area() area_b = triangle_b.Area() if area_a < 0.0 or area_b < 0.0: raise Exception('Miscalculated line quad triangles.') mesh.AddTriangle(triangle_a) mesh.AddTriangle(triangle_b) return mesh
def ApplyCuts(self, region): # Turn all the cuts into bi-directional borders. Discard cuts that don't cut anything. region.Tessellate() while True: for i, edge in enumerate(self.edge_list): if edge[2] == PlanarGraphEdgeLabel.CUT: break else: break del self.edge_list[i] edge_segment = self.EdgeSegment(edge) if region.ContainsPoint(edge_segment.Lerp(0.5)): self.edge_list.append( (edge[0], edge[1], PlanarGraphEdgeLabel.REGION_BORDER)) self.edge_list.append( (edge[1], edge[0], PlanarGraphEdgeLabel.REGION_BORDER)) # Now go read-off all the perimeter and hole polygons. from math2d_polygon import Polygon perimeter_list = [] hole_list = [] while True: for edge in self.edge_list: if edge[2] == PlanarGraphEdgeLabel.REGION_BORDER: break else: break polygon = Polygon() cycle_list, cycle_found = self.FindCycleContainingEdge(edge, True) polygon.vertex_list = [ self.vertex_list[edge[0]] for edge in cycle_list ] if polygon.IsWoundCCW(): polygon.Tessellate() perimeter_list.append(polygon) else: cycle_list, cycle_found = self.FindCycleContainingEdge( edge, False) polygon.vertex_list = [ self.vertex_list[edge[0]] for edge in cycle_list ] if polygon.IsWoundCW(): polygon.ReverseWinding() hole_list.append(polygon) else: raise Exception('Failed to process cycle containing edge.') for edge in cycle_list: i = self.FindEdge(edge, False, False) del self.edge_list[i] # Finally, merry all the holes to the appropriate perimeters. # TODO: This is a bit tricky. A hole may lie inside a perimeter, but that doesn't mean it belongs to that perimeter, # because it may lie inside a yet smaller perimeter that is contained in a hole of the other perimeter. Make sure # we assign holes to containing perimeters of the smallest area. I think this may fix the problem. from math2d_region import Region, SubRegion region = Region() for perimeter in perimeter_list: sub_region = SubRegion(perimeter) new_hole_list = [] for hole in hole_list: # I believe we need only test a single point on the hole. if perimeter.ContainsPoint( hole.vertex_list[0] ) and not perimeter.ContainsPointOnBorder(hole.vertex_list[0]): sub_region.hole_list.append(hole) else: new_hole_list.append(hole) hole_list = new_hole_list region.sub_region_list.append(sub_region) if len(hole_list) > 0: raise Exception('Failed to marry %d holes to perimeters.' % len(hole_list)) return region
def __init__(self): super().__init__() sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(-5.0, -4.0)) sub_region.polygon.vertex_list.append(Vector(4.0, -4.0)) sub_region.polygon.vertex_list.append(Vector(4.0, 3.0)) sub_region.polygon.vertex_list.append(Vector(-5.0, 3.0)) hole = Polygon() hole.vertex_list.append(Vector(-4.0, -3.0)) hole.vertex_list.append(Vector(3.0, -3.0)) hole.vertex_list.append(Vector(3.0, -2.0)) hole.vertex_list.append(Vector(-3.0, -2.0)) hole.vertex_list.append(Vector(-3.0, 1.0)) hole.vertex_list.append(Vector(3.0, 1.0)) hole.vertex_list.append(Vector(3.0, 2.0)) hole.vertex_list.append(Vector(-4.0, 2.0)) sub_region.hole_list.append(hole) hole = Polygon() hole.vertex_list.append(Vector(-2.0, -1.0)) hole.vertex_list.append(Vector(0.0, -1.0)) hole.vertex_list.append(Vector(0.0, 0.0)) hole.vertex_list.append(Vector(-2.0, 0.0)) sub_region.hole_list.append(hole) hole = Polygon() hole.vertex_list.append(Vector(1.0, -1.0)) hole.vertex_list.append(Vector(3.0, -1.0)) hole.vertex_list.append(Vector(3.0, 0.0)) hole.vertex_list.append(Vector(1.0, 0.0)) sub_region.hole_list.append(hole) self.region = Region() self.region.sub_region_list.append(sub_region) self.cut_region = Region() sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(-1.0, -5.0)) sub_region.polygon.vertex_list.append(Vector(5.0, -5.0)) sub_region.polygon.vertex_list.append(Vector(5.0, 4.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 4.0)) self.cut_region.sub_region_list.append(sub_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(-6.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-3.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-3.0, 4.0)) sub_region.polygon.vertex_list.append(Vector(-6.0, 4.0)) self.cut_region.sub_region_list.append(sub_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(-6.0, -5.0)) sub_region.polygon.vertex_list.append(Vector(-3.0, -5.0)) sub_region.polygon.vertex_list.append(Vector(-3.0, -3.0)) sub_region.polygon.vertex_list.append(Vector(-6.0, -3.0)) self.cut_region.sub_region_list.append(sub_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(0.5, 0.5)) sub_region.polygon.vertex_list.append(Vector(0.5, -1.5)) sub_region.polygon.vertex_list.append(Vector(3.5, -1.5)) sub_region.polygon.vertex_list.append(Vector(3.5, 0.5)) self.cut_region.sub_region_list.append(sub_region) self.result_region = None self.polygon_list = []
def GenerateConvexHull(self): from math2d_line import Line from math2d_polygon import Polygon from math2d_triangle import Triangle found = False for i in range(len(self.point_list)): for j in range(len(self.point_list)): if j == i: continue for k in range(len(self.point_list)): if k == i or k == j: continue triangle = Triangle(self.point_list[i], self.point_list[j], self.point_list[k]) if triangle.Area() > 0.0: found = True break if found: break if found: break if not found: raise Exception('Failed to find initial triangle.') polygon = Polygon() polygon.vertex_list = [ triangle.vertex_a, triangle.vertex_b, triangle.vertex_c ] point_list = [point for point in self.point_list] random.seed(0) while True: new_point_list = [] for point in point_list: if not polygon.ContainsPoint(point, assume_convex=True): new_point_list.append(point) point_list = new_point_list if len(point_list) == 0: break # Perhaps randomly choosing a point will help us terminate quicker on average? point = random.choice(point_list) point_cloud = PointCloud() point_cloud.Add(polygon) j = None k = None for i in range(len(polygon.vertex_list)): vertex = polygon.vertex_list[i] line = Line() line.MakeForPoints(point, vertex) point_cloud_back, point_cloud_front, point_cloud_neither = point_cloud.Split( line) if point_cloud_front.Size() == 0: j = i if point_cloud_back.Size() == 0: k = i if j is not None and k is not None and j != k: break else: raise Exception( 'Failed to determine how to add point to convex hull.') new_vertex_list = [point] i = k while True: new_vertex_list.append(polygon.vertex_list[i]) if i == j: break i = (i + 1) % len(polygon.vertex_list) polygon.vertex_list = new_vertex_list return polygon