def set_angle_face_force(angle_vertex: Vertex, alfa0, face: Face, k_face): # TODO: For potential speedup - calculations for the whole face at one time alfa = face.angle_for_vertex(angle_vertex) p1 = face.prev_vertex(angle_vertex) p2 = angle_vertex p3 = face.next_vertex(angle_vertex) c = k_face * (alfa0 - alfa) p21 = vector_from_to(p2.pos, p1.pos) p21_len = p21.length p23 = vector_from_to(p2.pos, p3.pos) p23_len = p23.length normal = face.normal n_x_p21 = cross(normal, p21) n_x_p23 = cross(normal, p23) dp1 = n_x_p21 / p21_len dp2 = -n_x_p21 / p21_len + n_x_p23 / p23_len dp3 = -n_x_p23 / p23_len f1 = c * dp1 f2 = c * dp2 f3 = c * dp3 p1.set_force(ForceName.FACE, Vector3.from_vec(f1)) p2.set_force(ForceName.FACE, Vector3.from_vec(f2)) p3.set_force(ForceName.FACE, Vector3.from_vec(f3))
def test_zero_angle(self): v1 = Vector3(0, 0, 3) v2 = Vector3(0, 0, 12) n = Vector3(0, 1, 0) angle = signed_vector_angle(v1, v2, n) self.assertEqual(angle, 0)
def test_180_angle(self): v1 = Vector3(1, 1, 3) v2 = Vector3(-1, -1, -3) n = Vector3(1, 0, 0) angle = signed_vector_angle(v1, v2, n) self.assertEqual(angle, math.pi)
def test_is_zero_if_is_not_being_folded(self): set_all_crease_forces([self.edge]) self.assertEqual(self.v1.total_force(), Vector3(0, 0, 0)) self.assertEqual(self.v2.total_force(), Vector3(0, 0, 0)) self.assertEqual(self.v3.total_force(), Vector3(0, 0, 0)) self.assertEqual(self.v4.total_force(), Vector3(0, 0, 0))
def test_rotated_positive_to_negative(self): v1 = Vector3(0, 3, 0) v2 = Vector3(0, 0, 3) n = Vector3(-1, 0, 0) angle = signed_vector_angle(v1, v2, n) self.assertEqual(angle, -math.pi / 2)
def test_negative_angle(self): v1 = Vector3(0, -3, 0) v2 = Vector3(0, 0, 3) n = Vector3(1, 0, 0) angle = signed_vector_angle(v1, v2, n) self.assertEqual(angle, -math.pi / 2)
def test_takes_orientation_into_account(self): v1 = Vector3(0.0, 0.0, 0.0) v2 = Vector3(1.0, 0.0, 0.0) v3 = Vector3(0.0, 1.0, 0.0) normal_1 = plane_normal(v1, v2, v3) normal_2 = plane_normal(v3, v2, v1) self.assertEqual(normal_1, -normal_2)
def test_rotated_positive_to_negative_180_angle(self): v1 = Vector3(1, 1, 3) v2 = Vector3(-1, -1, -3) n = Vector3(-1, 0, 0) angle = signed_vector_angle(v1, v2, n) self.assertEqual(angle, math.pi)
def test_creates_expanding_force(self): self.v2.x = 1 set_all_beam_forces([self.edge]) self.assertTrue( same_direction_vec(self.v1.total_force(), Vector3(-1, 0, 0))) self.assertTrue( same_direction_vec(self.v2.total_force(), Vector3(1, 0, 0)))
def __init__(self, iden: int, x: float, y: float, z: float): self.id = iden self.pos = Vector3(x, y, z) self._total_force = Vector3(0.0, 0.0, 0.0) self.velocity = Vector3(0.0, 0.0, 0.0) if CONFIG['DEBUG'] and CONFIG['DEBUG_FORCES']: self.forces_by_type = {}
def test_returns_normalized_normal(self): v1 = Vector3(0.0, 0.0, 0.0) v2 = Vector3(1.0, 0.0, 0.0) v3 = Vector3(0.0, 1.0, 0.0) normal = plane_normal(v1, v2, v3) self.assertAlmostEqual(normal.length, 1.0) self.assertEqual(normal, Vector3(0.0, 0.0, 1.0))
def test_normalize_bounding(self): verts = [Vertex(0, 1.0, 1.0, 1.0), Vertex(1, -20.0, -20.0, -20.0)] scale_fac = verts[1].pos.length / 5.0 normalized = normalize_bounding_box(verts, 10) self.assertEqual( normalized[0].pos, Vector3(1.0 / scale_fac, 1.0 / scale_fac, 1.0 / scale_fac)) self.assertEqual( normalized[1].pos, Vector3(-20.0 / scale_fac, -20.0 / scale_fac, -20.0 / scale_fac))
def test_creates_equal_forces_when_velocity_changes(self): self.v1.velocity = Vector3(-1, 0, 0) self.v2.velocity = Vector3(1, 0, 0) set_all_damping_forces([self.edge]) self.assertEqual(self.v1.total_force(), -self.v2.total_force()) self.assertTrue( same_direction_vec(self.v1.total_force(), Vector3(1, 0, 0))) self.assertTrue( same_direction_vec(self.v2.total_force(), Vector3(-1, 0, 0)))
def test_creates_upward_force_for_valley_assignment(self): self.edge.assignment = EDGE_VALLEY self.edge.target_angle = angle_from_assignment(self.edge.assignment) set_all_crease_forces([self.edge]) self.assertTrue( same_direction_vec(self.v1.total_force(), Vector3(0, 0, 1))) self.assertTrue( same_direction_vec(self.v2.total_force(), Vector3(0, 0, -1))) self.assertTrue( same_direction_vec(self.v3.total_force(), Vector3(0, 0, 1))) self.assertTrue( same_direction_vec(self.v4.total_force(), Vector3(0, 0, -1)))
def test_creates_downwards_force_for_mountain_assignment(self): self.edge.assignment = EDGE_MOUNTAIN self.edge.target_angle = angle_from_assignment(self.edge.assignment) set_all_crease_forces([self.edge]) self.assertTrue( same_direction_vec(self.v1.total_force(), Vector3(0, 0, -1))) self.assertTrue( same_direction_vec(self.v2.total_force(), Vector3(0, 0, 1))) self.assertTrue( same_direction_vec(self.v3.total_force(), Vector3(0, 0, -1))) self.assertTrue( same_direction_vec(self.v4.total_force(), Vector3(0, 0, 1)))
def test_getting_by_index(self): v = Vector3(1, 2, 3) self.assertEqual(v[0], 1) self.assertEqual(v[1], 2) self.assertEqual(v[2], 3) self.assertRaises(IndexError, lambda: v[3]) self.assertRaises(IndexError, lambda: v[-1])
def cross(v1: Vector3, v2: Vector3) -> Vector3: vec1 = v1.vec vec2 = v2.vec x = (vec1[1] * vec2[2] - vec1[2] * vec2[1]) y = (vec1[2] * vec2[0] - vec1[0] * vec2[2]) z = (vec1[0] * vec2[1] - vec1[1] * vec2[0]) return Vector3(x, y, z)
def test_setting_by_index(self): v = Vector3(1, 2, 3) v[0] = 4 v[1] = 5 v[2] = 6 self.assertEqual(v[0], 4) self.assertEqual(v[1], 5) self.assertEqual(v[2], 6)
def solve(self, output): self._reset_forces() self._reset_velocities() self._set_forces() self._set_target_angles() cur_forces = self._total_forces_vecs() if CONFIG['DEBUG']: print('Starting solver') print('FORCES: ', cur_forces) plot_idx = 0 finished = False while not finished: if CONFIG['DEBUG']: print(cur_forces) for i, (v, total_force) in enumerate(zip(self.vertices, cur_forces)): node_mass = v.mass v_t = v.velocity p_t = v.pos a = Vector3.from_vec(total_force) / node_mass v_next = v_t + a * self.d_t v.pos = p_t + v_next * self.d_t v.velocity = v_next if CONFIG['DEBUG']: print(v) print('VELOCITY: ', v.velocity) print('TOTAL FORCE: ', v.total_force()) print('a = ', a) print() if CONFIG['DEBUG_PLOT']: if plot_idx >= CONFIG['DEBUG_PLOT_FROM'] and plot_idx % CONFIG[ 'DEBUG_PLOT_EVERY'] == 0: from origuide.tools import plot plot.plot3d(self.vertices, self.edges, self.faces, cur_forces) plot_idx += 1 if CONFIG['DEBUG']: print('---') self._reset_forces() self._set_forces() prev_forces = cur_forces.copy() cur_forces = self._total_forces_vecs() finished = self._should_end(prev_forces, cur_forces) output.accept(self.vertices, finished) return
def set_force(self, name: ForceName, force: Vector3): # if CONFIG['DEBUG'] and CONFIG['DEBUG_FORCES']: # logging.debug('Setting force (l={}) {}:{} on {}'.format( # force.length, # name, # force, # self.__str__() # )) self._total_force += force if CONFIG['DEBUG'] and CONFIG['DEBUG_FORCES']: if name not in self.forces_by_type: self.forces_by_type[name] = Vector3(0, 0, 0) self.forces_by_type[name] += force
def test_forces_manipulation(self): self.v.set_force(ForceName.BEAM, Vector3(1, 1, 0)) self.v.set_force(ForceName.DAMPING, Vector3(1, 1, 0)) self.v.set_force(ForceName.CREASE, Vector3(1, 1, 0)) self.v.set_force(ForceName.FACE, Vector3(1, 1, -1)) self.assertEqual(self.v.total_force(), Vector3(4, 4, -1)) self.v.reset_forces() self.assertEqual(self.v.total_force(), Vector3(0, 0, 0))
def test_length(self): v = Vector3(1, 2, 2) self.assertEqual(v.length, 3) v = Vector3(-1, 2, 2) self.assertEqual(v.length, 3) v = Vector3(1, -2, 2) self.assertEqual(v.length, 3) v = Vector3(1, 2, -2) self.assertEqual(v.length, 3) v = Vector3(0, 0, 3) self.assertEqual(v.length, 3) v = Vector3(3, 0, 0) self.assertEqual(v.length, 3) v = Vector3(0, 3, 0) self.assertEqual(v.length, 3)
def test_division_by_number(self): v = Vector3(1, 2, 3) self.assertEqual(v / 2, Vector3(0.5, 1, 1.5))
def test_does_nothing_when_all_correctly_bound(self): verts = [Vertex(0, 0.0, 0.0, 0.0), Vertex(1, 1.0, 1.0, 1.0)] normalized = normalize_bounding_box(verts, 2 * math.sqrt(3)) self.assertEqual(list(map(lambda vert: vert.pos, normalized)), [Vector3(0, 0, 0), Vector3(1, 1, 1)])
def reset_velocity(self): self.velocity = Vector3(0.0, 0.0, 0.0)
def test_is_zero_if_velocity_is_zero(self): set_all_damping_forces([self.edge]) self.assertEqual(self.v1.total_force(), Vector3(0, 0, 0)) self.assertEqual(self.v2.total_force(), Vector3(0, 0, 0))
def test_creates_pushing_force_if_face_is_expanded(self): self.v3.y = 3 set_all_face_forces([self.face]) self.assertTrue(same_direction_vec(self.v3.total_force(), Vector3(0, -1, 0)))
def test_creates_pulling_force_if_face_is_squished(self): self.v3.y = 1 set_all_face_forces([self.face]) self.assertTrue(same_direction_vec(self.v3.total_force(), Vector3(0, 1, 0)))
def test_is_zero_if_no_position_changed(self): set_all_face_forces([self.face]) self.assertEqual(self.v1.total_force(), Vector3(0, 0, 0)) self.assertEqual(self.v2.total_force(), Vector3(0, 0, 0)) self.assertEqual(self.v3.total_force(), Vector3(0, 0, 0))
def reset_forces(self): self._total_force = Vector3(0.0, 0.0, 0.0) if CONFIG['DEBUG'] and CONFIG['DEBUG_FORCES']: self.forces_by_type = {}