def calculate_graph_layout(self, draw, font): text_size = draw.textsize(text=self.label_string, font=font) text_width = text_size[0] text_height = text_size[1] total_width = 0.0 margin = 2.0 for key in self.sub_node_map: sub_node = self.sub_node_map[key] sub_node.calculate_graph_layout(draw, font) sub_node.calculate_bounding_box() total_width += sub_node.bounding_box.Width() + 2.0 * margin location = Vector(-total_width / 2.0 + margin, -2.0 * text_height) for key in self.sub_node_map: sub_node = self.sub_node_map[key] transform = AffineTransform() upper_left_corner = Vector(sub_node.bounding_box.min_point.x, sub_node.bounding_box.max_point.y) transform.Translation(location - upper_left_corner) sub_node.transform_graph_layout(transform) location.x += sub_node.bounding_box.Width() + 2.0 * margin self.label_box.min_point = Vector(-text_width / 2.0 - 3.0, -text_height / 2.0 - 3.0) self.label_box.max_point = Vector(text_width / 2.0 + 3.0, text_height / 2.0 + 3.0)
def __init__(self): super().__init__() cut_region = CutRegion() cut_region.GenerateRegularPolygon(8, 1.0) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(8, 2.0) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(8, 3.0) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(8, 4.0) self.cut_region_list.append(cut_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(0.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(4.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(4.0 * math.cos(math.pi / 4.0), 4.0 * math.sin(math.pi / 4))) 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 / 2.0 - math.pi / 8.0) for cut_region in self.cut_region_list: cut_region.Transform(transform)
def Render(self, point, arrow_head_length=0.3): from OpenGL.GL import glBegin, glEnd, glVertex2f, GL_LINES from math2d_affine_transform import AffineTransform from math2d_line_segment import LineSegment transform = AffineTransform() transform.linear_transform.x_axis = self.Normalized() transform.linear_transform.y_axis = transform.linear_transform.x_axis.Rotated( math.pi / 2.0) transform.translation = point line_segment_list = [] head = Vector(self.Length(), 0.0) line_segment_list.append(LineSegment(Vector(0.0, 0.0), head)) if arrow_head_length > 0.0: line_segment_list.append( LineSegment( head, head + Vector(radius=arrow_head_length, angle=(7.0 / 8.0) * math.pi))) line_segment_list.append( LineSegment( head, head + Vector(radius=arrow_head_length, angle=-(7.0 / 8.0) * math.pi))) glBegin(GL_LINES) try: for line_segment in line_segment_list: line_segment = transform * line_segment glVertex2f(line_segment.point_a.x, line_segment.point_a.y) glVertex2f(line_segment.point_b.x, line_segment.point_b.y) finally: glEnd()
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__() 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 __init__(self): super().__init__() 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) cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, math.sqrt(2.0)) transform = AffineTransform() transform.RigidBodyMotion(math.pi / 4.0, Vector(-1.5, -1.5)) cut_region.Transform(transform) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, math.sqrt(2.0)) transform = AffineTransform() transform.RigidBodyMotion(math.pi / 4.0, Vector(3.0, 3.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region)
def __init__(self): super().__init__() cut_region = CutRegion() cut_region.GenerateRegularPolygon(3, 4.0) transform = AffineTransform() transform.Translation(Vector(-2.5, 0.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(3, 4.0) transform = AffineTransform() transform.RigidBodyMotion(math.pi / 3.0, Vector(2.5, 0.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region)
def __init__(self): super().__init__() cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, 3.0) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, 3.5) transform = AffineTransform() transform.RigidBodyMotion(0.0, Vector(-4.0, 0.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region) cut_region = CutRegion() cut_region.GenerateRegularPolygon(4, 3.0) transform = AffineTransform() transform.RigidBodyMotion(0.0, Vector(4.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(-1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.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(-1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 1.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) transform = AffineTransform() transform.Translation(Vector(2.0, 0.0)) cut_region.Transform(transform) 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, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 1.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) transform = AffineTransform() transform.Translation(Vector(-2.0, 0.0)) cut_region.Transform(transform) 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, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 1.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) transform = AffineTransform() transform.Translation(Vector(2.0, -2.0)) cut_region.Transform(transform) 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, -1.0)) sub_region.polygon.vertex_list.append(Vector(1.0, 1.0)) sub_region.polygon.vertex_list.append(Vector(-1.0, 1.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) transform = AffineTransform() transform.Translation(Vector(-2.0, 2.0)) cut_region.Transform(transform) self.cut_region_list.append(cut_region) sub_region = SubRegion() sub_region.polygon.vertex_list.append(Vector(-2.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(2.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(2.0, -4.0)) sub_region.polygon.vertex_list.append(Vector(4.0, -4.0)) sub_region.polygon.vertex_list.append(Vector(4.0, 2.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, 2.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(2.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, 0.0)) sub_region.polygon.vertex_list.append(Vector(-2.0, 4.0)) sub_region.polygon.vertex_list.append(Vector(-4.0, 4.0)) sub_region.polygon.vertex_list.append(Vector(-4.0, -2.0)) sub_region.polygon.vertex_list.append(Vector(2.0, -2.0)) cut_region = CutRegion() cut_region.region = Region() cut_region.region.sub_region_list.append(sub_region) 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 GenerateSymmetries(self): reflection_list = [] center_reflection_list = [] center = self.AveragePoint() for i in range(len(self.point_list)): for j in range(i + 1, len(self.point_list)): line_segment = LineSegment(self.point_list[i], self.point_list[j]) mid_point = line_segment.Lerp(0.5) normal = line_segment.Direction().Normalized().RotatedCCW90() reflection = AffineTransform() reflection.Reflection(mid_point, normal) center_reflected = reflection.Transform(center) if center_reflected.IsPoint(center): center_reflection_list.append({ 'reflection': reflection, 'normal': normal }) is_symmetry, total_error = self.IsSymmetry(reflection) if is_symmetry: new_entry = { 'reflection': reflection, 'total_error': total_error, 'center': mid_point, 'normal': normal } for k, entry in enumerate(reflection_list): if entry['reflection'].IsTransform(reflection): if entry['total_error'] > total_error: reflection_list[k] = new_entry break else: reflection_list.append(new_entry) # Rotations are just double-reflections. We return here a CCW rotational symmetry that generates # the sub-group of rotational symmetries of the overall group of symmetries of the cloud. We also # return its inverse for convenience. Of course, not all point clouds have any rotational symmetry. epsilon = 1e-7 def SortKey(entry): if entry['normal'].y <= -epsilon: entry['normal'] = -entry['normal'] angle = Vector(1.0, 0.0).SignedAngleBetween(entry['normal']) if angle < 0.0: angle += 2.0 * math.pi return angle ccw_rotation = None cw_rotation = None if len(reflection_list) >= 2: reflection_list.sort(key=SortKey) # Any 2 consecutive axes should be as close in angle between each other as possible. reflection_a = reflection_list[0]['reflection'] reflection_b = reflection_list[1]['reflection'] ccw_rotation = reflection_a * reflection_b cw_rotation = reflection_b * reflection_a # The following are just sanity checks. is_symmetry, total_error = self.IsSymmetry(ccw_rotation) if not is_symmetry: raise Exception('Failed to generate CCW rotational symmetry.') is_symmetry, total_error = self.IsSymmetry(cw_rotation) if not is_symmetry: raise Exception('Failed to generate CW rotational symmetry.') elif len(reflection_list) == 1: # If we found exactly one reflection, then I believe the cloud has no rotational symmetry. # Note that the identity transform is not considered a symmetry. pass else: # If we found no reflective symmetry, the cloud may still have rotational symmetry. # Furthermore, if it does, it must rotate about the average point. I think there is a way # to prove this using strong induction. Note that the statement holds for all point-clouds # made from vertices taken from regular polygons. Now for any given point-cloud with any # given rotational symmetry, consider 1 point of that cloud. Removing that point along # with the minimum number of other points necessary to keep the given rotational symmetry, # we must remove points making up a regular polygon's vertices. By inductive hypothesis, # the remaining cloud has its average point at the center of the rotational symmetry. Now # see that adding the removed points back does not change the average point of the cloud. # Is it true that every line of symmetry of the cloud contains the average point? I believe # the answer is yes. Take any point-cloud with a reflective symmetry and consider all but # 2 of its points that reflect into one another along that symmetry. If the cloud were just # these 2 points, then the average point is on the line of symmetry. Now notice that as you # add back points by pairs, each pair reflecting into one another, the average point of the # cloud remains on the line of symmetry. center_reflection_list.sort(key=SortKey) found = False for i in range(len(center_reflection_list)): for j in range(i + 1, len(center_reflection_list)): ccw_rotation = center_reflection_list[i][ 'reflection'] * center_reflection_list[j]['reflection'] is_symmetry, total_error = self.IsSymmetry(ccw_rotation) if is_symmetry: # By stopping at the first one we find, we should be minimizing the angle of rotation. found = True break if found: break if found: cw_rotation = ccw_rotation.Inverted() else: ccw_rotation = None return reflection_list, ccw_rotation, cw_rotation