def advance_positions(self, lerp_value, eps=1e-2): line_segment = LineSegment(self.position, self.target_position) if line_segment.Length() < eps: self.position = self.target_position else: self.position = line_segment.Lerp(lerp_value) for child in self.child_list: child.advance_positions(lerp_value)
def Interpolate(self, value): from math2d_line_segment import LineSegment point_list = [point for point in self.point_list] while len(point_list) > 1: new_point_list = [] for i in range(len(point_list) - 1): line_segment = LineSegment(point_list[i], point_list[i + 1]) new_point_list.append(line_segment.Lerp(value)) point_list = new_point_list return point_list[0]
def Interpolate(self, value, length=None): from math2d_line_segment import LineSegment if length is None: length = self.Length() distance = length * value if distance < 0.0 or distance > length: raise Exception('Invalid parameter value.') i = 0 point = None while distance >= 0.0: point = self.point_list[i] line_segment = LineSegment(self.point_list[i], self.point_list[i + 1]) segment_length = line_segment.Lenght() if segment_length < distance: distance -= segment_length i += 1 else: lerp_value = segment_length / distance point = line_segment.Lerp(lerp_value) break return point
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