def test_ray_intersection(self): data = CollisionDetection.get_intersection_data(self.first_line, self.second_line) self.assertIsNone(data, 'Data should be null' + str(data) + ' ' + str(self.second_line)) data = CollisionDetection.get_intersection_data(self.second_line, self.first_line) self.assertIsNone(data, 'Data should be null' + str(data) + ' ' + str(self.second_line)) data = CollisionDetection.get_intersection_data(self.seventh_ray, self.eighth_ray) self.assertIsNone(data, 'Data should be null' + str(data) + ' ' + str(self.second_line)) data = CollisionDetection.get_intersection_data(self.eighth_ray, self.seventh_ray) self.assertIsNone(data, 'Data should be null' + str(data) + ' ' + str(self.second_line)) data1 = CollisionDetection.get_intersection_data(self.sixth_ray, self.fifth_ray) self.assertIsNotNone(data1, 'data1 shouldn\'t be null') point = data1.intersection_points self.assertEqual(point, [Vector2d(-2.53, -0.4)], 'Intersection point for data1 does not equal to the expected') data2 = CollisionDetection.get_intersection_data(self.fifth_ray, self.sixth_ray) self.assertIsNotNone(data2, 'data2 shouldn\'t be null') point = data2.intersection_points self.assertEqual(point, [Vector2d(-2.53, -0.4)], 'Intersection point for data2 does not equal to ' 'the expected') data3 = CollisionDetection.get_intersection_data(self.third_ray, self.fourth_ray) self.assertIsNotNone(data3, 'Data3 should not be null') data4 = CollisionDetection.get_intersection_data(self.fourth_ray, self.third_ray) self.assertIsNotNone(data4, 'Data4 should not be null')
def test_ray_interaction(self): data = self.first_ray.get_intersection_data(self.second_ray) self.assertIsNone( data, 'Data should be null' + str(data) + ' ' + str(self.second_ray)) data = self.second_ray.get_intersection_data(self.first_ray) self.assertIsNone( data, 'Data should be null' + str(data) + ' ' + str(self.second_ray)) data1 = self.sixth_ray.get_intersection_data(self.fifth_ray) self.assertIsNotNone(data1, 'data1 shouldn\'t be null') point = data1[0] self.assertEqual(point, [Vector2d(2, 4)], 'Intersection point for data1 does not equal to ' \ 'the expected') data2 = self.fifth_ray.get_intersection_data(self.sixth_ray) self.assertIsNotNone(data2, 'data2 shouldn\'t be null') point = data2[0] self.assertEqual( point, [Vector2d(2, 4)], 'Intersection point for data2 does not equal to ' 'the expected') data3 = self.third_ray.get_intersection_data(self.fourth_ray) self.assertIsNotNone(data3, 'Data3 should not be null') data4 = self.fourth_ray.get_intersection_data(self.third_ray) self.assertIsNotNone(data4, 'Data4 should not be null')
def get_the_most_distant_points( self) -> Tuple[Vector2d, Vector2d, Vector2d, Vector2d]: """Return tuple of four the most distant points (x, y coordinates) starting from max x and by clockwise (max_x_point, max_y_point, min_x_point, min_y_point) """ max_x = -math.inf min_x = math.inf max_y = -math.inf min_y = math.inf max_x_point, max_y_point, min_x_point, min_y_point = Vector2d( 0, 0), Vector2d(0, 0), Vector2d(0, 0), Vector2d(0, 0) for point in self.points: if max_x < point.x: max_x = point.x max_x_point = point elif min_x > point.x: min_x = point.x min_x_point = point if max_y < point.y: max_y = point.y max_y_point = point elif min_y > point.y: min_y = point.y min_y_point = point return max_x_point, max_y_point, min_x_point, min_y_point
def test_project_on(self): self.assertEqual(self.second_vector.project_on(self.first_vector), Vector2d(1, 0)) self.assertEqual(self.third_vector.project_on(self.first_vector), Vector2d(-1, 0)) self.assertEqual(self.fourth_vector.project_on(self.first_vector), Vector2d(0, 0))
def __init__(self, name: str, attached_obj: Entity, linear_drag: float, mass: float = 1.0, angular_drag: float = 1.0, rb_type: str = "dynamic", velocity: Vector2d = Vector2d(0, 0), force: Vector2d = Vector2d(0, 0)): if not rb_type in RB_TYPES: raise AttributeError( "Rigid body time should be one of the following values {}, but not {}" .format(RB_TYPES, rb_type)) super().__init__(name, CT_RIGID_BODY, attached_obj) self.mass = 1 self.inverted_mass = 1 / mass self.result_force = force self.velocity = velocity self.linear_drag = linear_drag self.angular_drag = angular_drag self.simulate = True self.type = rb_type
class TestBaseForceGenerators(unittest.TestCase): shape = Circle(Vector2d(0, 0),1) _object = Object(shape) static_force_generator = StaticForceGenerator( "Test Static Force", Vector2d(0, -10), [_object]) calculated_force_generator = CalculatedForceGenerator( "Test Calculated Force", [_object], calculator) temporary_force_generator = TemporaryForceGenerator( "Test Temporary Force", Vector2d(10, 0), [_object], 2000) conditional_force_generator = ConditionalForceGenerator( "Test Conditional Force", Vector2d(-10, 0), [_object], condition) def test_static_force(self): self.static_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(0, -10), ) self._object.clear_force() def test_calculated_force(self): self.calculated_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(1, 1).scale(self._object.mass), "Calculated force error") self._object.clear_force() def test_conditional_force(self): self.conditional_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(0, 0), "Conditional force (condition == False) error") self._object.mass = 10 self.conditional_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(-10, 0), "Conditional force (condition == True) error") self._object.mass = 1 self._object.clear_force()
def get_dist_convex_polygons(first_polygon: "ConvexPolygon", second_polygon: "ConvexPolygon") -> float: """Uses the Gilbert-Johnson-Keerthi Algorithm to get penetration depth and intersection points""" new_polygon_points = polygons_difference(first_polygon, second_polygon) convex_hull = create_convex_hull(new_polygon_points) closest_point = get_closest_support_point(convex_hull, Vector2d(0, 0)) penetration = (closest_point - Vector2d(0, 0)).get_magnitude() if convex_hull.is_point_belongs(Vector2d(0, 0)): return -penetration return penetration
def get_centroid(self) -> Vector2d: # https://en.wikipedia.org/wiki/Polygon centroid = Vector2d(0, 0) signed_area = 0.0 for i in range(self.points_count - 1): x0 = self.points[i].x y0 = self.points[i].y x1 = self.points[i + 1].x y1 = self.points[i + 1].y temp_area = x0 * y1 - x1 * y0 signed_area += temp_area centroid.x += (x0 + x1) * temp_area centroid.y += (y0 + y1) * temp_area x0 = self.points[self.points_count - 1].x y0 = self.points[self.points_count - 1].y x1 = self.points[0].x y1 = self.points[0].y temp_area = x0 * y1 - x1 * y0 signed_area += temp_area centroid.x += (x0 + x1) * temp_area centroid.y += (y0 + y1) * temp_area signed_area *= 0.5 centroid.x /= (6.0 * signed_area) centroid.y /= (6.0 * signed_area) return centroid
def calculate_force(self, _object: BaseNonStaticObject) -> Vector2d: """Calculates gravity force for an object""" force_magnitude = self.free_fall_coefficient * _object.mass force = Vector2d(0, force_magnitude) return force
def test_static_force(self): self.static_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(0, -10), ) self._object.clear_force()
def test_calculated_force(self): self.calculated_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(1, 1).scale(self._object.mass), "Calculated force error") self._object.clear_force()
def test_conditional_force(self): self.conditional_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(0, 0), "Conditional force (condition == False) error") self._object.mass = 10 self.conditional_force_generator.apply_force(1) self.assertEqual(self._object.result_force, Vector2d(-10, 0), "Conditional force (condition == True) error") self._object.mass = 1 self._object.clear_force()
def test_add_vector(self): self.assertEqual( self.integer_case.add_vector(self.float_case), Vector2d(self.integer_case.x + self.float_case.x, self.integer_case.y + self.float_case.y), "Integer addition fails") self.assertEqual( self.integer_negative_case.add_vector(self.float_negative_case), Vector2d(self.integer_negative_case.x + self.float_negative_case.x, self.integer_negative_case.y + self.float_negative_case.y), "Integer addition fails") self.assertEqual( self.negative_case.add_vector(self.zero_case), Vector2d(self.negative_case.x + self.zero_case.x, self.negative_case.y + self.zero_case.y), "Integer addition fails")
def is_intersect_convexpolygons(first_poly: "ConvexPolygon", second_poly: "ConvexPolygon") -> bool: new_polygon_points = polygons_difference(first_poly, second_poly) convex_hull = create_convex_hull(new_polygon_points) if not convex_hull.is_point_belongs(Vector2d(0, 0)): return False return True
def get_shadow_polygon(poly: "BasePolygon", normal: Vector2d) -> Tuple[float, float]: min_shadow = float("inf") max_shadow = -float("inf") for point in poly.points: product = normal.dot_product(point) min_shadow = min(min_shadow, product) max_shadow = max(min_shadow, product) return min_shadow, max_shadow
def test_subtract_vector(self): self.assertEqual( self.integer_case.subtract_vector(self.float_case), Vector2d(self.integer_case.x - self.float_case.x, self.integer_case.y - self.float_case.y), "Integer subtraction fails") self.assertEqual( self.integer_negative_case.subtract_vector( self.float_negative_case), Vector2d(self.integer_negative_case.x - self.float_negative_case.x, self.integer_negative_case.y - self.float_negative_case.y), "Integer subtraction fails") self.assertEqual( self.negative_case.subtract_vector(self.zero_case), Vector2d(self.negative_case.x - self.zero_case.x, self.negative_case.y - self.zero_case.y), "Integer subtraction fails")
def get_support_point(self, direction: Vector2d) -> Vector2d: support_point = None max_product = -1 for point in self.points: product = direction.dot_product(point) if product > max_product: max_product = product support_point = point return support_point
def get_segments_intersection_points( segment: Union["Line", "Segment", "Ray"], penetration_coefficients: Tuple[float, float]) -> List[Vector2d]: t, u = penetration_coefficients intersection_point_x = segment.first_point.x + t * ( segment.second_point.x - segment.first_point.x) intersection_point_y = segment.first_point.y + t * ( segment.second_point.y - segment.first_point.y) intersection_point = Vector2d(intersection_point_x, intersection_point_y) return [intersection_point]
def test_segments_intersection(self): data = CollisionDetection.get_intersection_data(self.first_segment, self.second_segment) self.assertIsNone(data, 'Data should be null' + str(data) + ' ' + str(self.second_segment)) data = CollisionDetection.get_intersection_data(self.second_segment, self.first_segment) self.assertIsNone(data, 'Data should be null' + str(data) + ' ' + str(self.second_segment)) data1 = CollisionDetection.get_intersection_data(self.third_segment, self.fourth_segment) self.assertIsNotNone(data1, 'data1 shouldn\'t be null') point = data1.intersection_points self.assertEqual(point, [Vector2d(2, 4)], 'Intersection point for data1 does not equal to ' \ 'the expected') data2 = CollisionDetection.get_intersection_data(self.fourth_segment, self.third_segment) self.assertIsNotNone(data2, 'data2 shouldn\'t be null') point = data2.intersection_points self.assertEqual(point, [Vector2d(2, 4)], 'Intersection point for data2 does not equal to ' 'the expected') self.assertEqual(data1.intersection_points, data2.intersection_points, 'data1[0] and data2[0] does not equal')
def test_scale_integer(self): self.assertEqual( self.integer_case.scale(3), Vector2d(self.integer_case.x * 3, self.integer_case.y * 3), "Integer scale fails") self.assertEqual( self.float_case.scale(3), Vector2d(self.float_case.x * 3, self.float_case.y * 3), "Integer scale fails") self.assertEqual( self.integer_negative_case.scale(3), Vector2d(self.integer_negative_case.x * 3, self.integer_negative_case.y * 3), "Integer scale fails") self.assertEqual( self.float_negative_case.scale(3), Vector2d(self.float_negative_case.x * 3, self.float_negative_case.y * 3), "Integer scale fails") self.assertEqual( self.negative_case.scale(3), Vector2d(self.negative_case.x * 3, self.negative_case.y * 3), "Integer scale fails") self.assertEqual(self.zero_case.scale(3), Vector2d(self.zero_case.x * 3, self.zero_case.y * 3), "Integer scale fails")
def test_scale_negative_float(self): self.assertEqual( self.integer_case.scale(-3.5), Vector2d(self.integer_case.x * -3.5, self.integer_case.y * -3.5), "Integer scale fails") self.assertEqual( self.float_case.scale(-3.5), Vector2d(self.float_case.x * -3.5, self.float_case.y * -3.5), "Integer scale fails") self.assertEqual( self.integer_negative_case.scale(-3.5), Vector2d(self.integer_negative_case.x * -3.5, self.integer_negative_case.y * -3.5), "Integer scale fails") self.assertEqual( self.float_negative_case.scale(-3.5), Vector2d(self.float_negative_case.x * -3.5, self.float_negative_case.y * -3.5), "Integer scale fails") self.assertEqual( self.negative_case.scale(-3.5), Vector2d(self.negative_case.x * -3.5, self.negative_case.y * -3.5), "Integer scale fails") self.assertEqual( self.zero_case.scale(-3.5), Vector2d(self.zero_case.x * -3.5, self.zero_case.y * -3.5), "Integer scale fails")
def test_add_scaled_vector(self): self.assertEqual( self.integer_case.add_scaled_vector(self.float_case, -3.25), Vector2d(self.integer_case.x + self.float_case.x * -3.25, self.integer_case.y + self.float_case.y * -3.25), "Integer scaled addition fails") self.assertEqual( self.integer_negative_case.add_scaled_vector( self.float_negative_case, -3.25), Vector2d( self.integer_negative_case.x + self.float_negative_case.x * -3.25, self.integer_negative_case.y + self.float_negative_case.y * -3.25), "Integer scaled addition fails") self.assertEqual( self.negative_case.add_scaled_vector(self.zero_case, -3.25), Vector2d(self.negative_case.x + self.zero_case.x * -3.25, self.negative_case.y + self.zero_case.y * -3.25), "Integer scaled addition fails")
def get_two_inter_points_circle_circle(first_circle: "Circle", second_circle: "Circle") \ -> List[Vector2d]: # https://planetcalc.com/8098/ r1 = first_circle.radius r2 = second_circle.radius d = r1 + r2 center1 = first_circle.center center2 = second_circle.center a = (r1**2 - r2**2 + d**2) / (2 * d) h = math.sqrt(r1**2 - a**2) middle_point = center1.add_vector( center1.subtract_vector(center2).scale(a / d)) p1_x = middle_point.x + h / d * (center2.y - center1.y) p1_y = middle_point.x - h / d * (center2.x - center1.x) p2_x = middle_point.x - h / d * (center2.y - center1.y) p2_y = middle_point.x + h / d * (center2.x - center1.x) return [Vector2d(p1_x, p1_y), Vector2d(p2_x, p2_y)]
def calculate_force(self, attracting_object_pos: Vector2d, obj: BaseNonStaticObject) -> Vector2d: current_spring_length = obj.position - attracting_object_pos if current_spring_length.get_magnitude() <= self.rest_length: return Vector2d(0, 0) force_direction = current_spring_length.normalize() force_magnitude = current_spring_length.get_magnitude() force_magnitude = abs(force_magnitude - self.rest_length) force_magnitude *= self.spring_coefficient force = force_direction.scale(force_magnitude) return force
def get_ellipse_shape_line_intersection_points(line: Union["Segment", "Line"], ellipse: Union["Circle", "Ellipse"], get_x_coord_func) -> \ Optional[List[Vector2d]]: inter_points_x_coordinates = get_x_coord_func(line, ellipse) if not inter_points_x_coordinates: return None int_points_y_coordinates = get_y_coordinates_line( line, inter_points_x_coordinates) points: List[Vector2d] = [] for x, y in zip(inter_points_x_coordinates, int_points_y_coordinates): points.append(Vector2d(x, y)) for i in range(len(points)): if not line.is_point_belongs(points[i]): del points[i] return points
def __init__(self, name: str, force: Vector2d, trigger_object_density: float, trigger_object: BaseNonStaticObject, force_direction: Vector2d, targeted_objects: List[BaseNonStaticObject]) -> None: """ Parameters: name (str): name of the force force (Vector2d): force that will applied to the objects rest_length (float): length of a spring in rest state trigger_object (BaseNonStaticObject): object that will trigger force generator if targets collide with the object trigger_object_density (float): density of a pure water is 1000 kg per cubic meter targeted_objects (List[BaseNonStaticObject]): objects on which the force will be applied all static objects will be omitted duration (float): the time that a force will be active """ super().__init__(name, force, targeted_objects) self.trigger_object = trigger_object self.trigger_object_density = trigger_object_density self.force_direction = force_direction.normalize()
class TestObjectComponents(unittest.TestCase): circle_shape = Circle(Vector2d(0, 0), 1.0) polygon_shape = ConvexPolygon([Vector2d(0, 1), Vector2d(1, 1), Vector2d(1, 0), Vector2d(0, 0)]) _object = Object(circle_shape) def test_force_adding(self): self._object.add_force(Vector2d(10, 10)) self.assertEqual(self._object.result_force, Vector2d(10, 10)) self._object.clear_force() def test_force_subtraction(self): self._object.subtract_force(Vector2d(10, 10)) self.assertEqual(self._object.result_force, Vector2d(-10, -10)) self._object.clear_force()
def test_normalize(self): self.assertEqual( self.integer_case.normalize(), Vector2d(self.integer_case.x / self.integer_case.get_magnitude(), self.integer_case.y / self.integer_case.get_magnitude()), "Integer subtraction fails") self.assertEqual( self.float_case.normalize(), Vector2d(self.float_case.x / self.float_case.get_magnitude(), self.float_case.y / self.float_case.get_magnitude()), "Integer subtraction fails") self.assertEqual( self.integer_negative_case.normalize(), Vector2d( self.integer_negative_case.x / self.integer_negative_case.get_magnitude(), self.integer_negative_case.y / self.integer_negative_case.get_magnitude()), "Integer subtraction fails") self.assertEqual( self.float_negative_case.normalize(), Vector2d( self.float_negative_case.x / self.float_negative_case.get_magnitude(), self.float_negative_case.y / self.float_negative_case.get_magnitude()), "Integer subtraction fails") self.assertEqual( self.negative_case.normalize(), Vector2d(self.negative_case.x / self.negative_case.get_magnitude(), self.negative_case.y / self.negative_case.get_magnitude()), "Integer subtraction fails") self.assertEqual(self.zero_case.normalize(), Vector2d(0, 0), "Integer subtraction fails")
def calculator(obj: BaseNonStaticObject, time: float): mass = obj.mass return Vector2d(1, 1).scale(mass)
def cursor_pos_callback(self, window, x_pos, y_pos): event = events.mouse_events.MouseMovedEvent(Vector2d(x_pos, y_pos)) self.events_callback(event)