From 60aa6a1b822fbe887510ce1a6ddc4aaeb4e283aa Mon Sep 17 00:00:00 2001 From: Rohar10 Date: Sun, 29 Sep 2024 15:48:49 -0500 Subject: [PATCH 1/4] Created a pose object using pose class --- examples/pose_demo.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/examples/pose_demo.py b/examples/pose_demo.py index 1ffb13f..80c4381 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -29,13 +29,16 @@ def angle_to_color(angle: float) -> np.ndarray: return np.array([r, g, b, 1.0]) -def main(filename: str = "pose_demo"): +def main(filename: str = "pose_demo2"): frame_rate = 60 total_time = 5 + total_frames = frame_rate * total_time + # clear all mesh objects bsr.clear_mesh_objects() + bsr.frame_manager.set_frame_start() # Task 1 # create a pose, i.e. position and director, using Pose class @@ -44,12 +47,29 @@ def main(filename: str = "pose_demo"): # the z axis should be d3, and d1 = d2 cross d3 # the pose should be updated every frame, and will go around a circle with period 1 second + pose_object = Pose(positions=np.array([1, 0, 0]), directors=np.eye(3),thickness_ratio=0.1) + theta = 0 + for frame in range(total_frames): + theta = 2*np.pi*frame/total_frames + new_positions = np.array([np.cos(theta), np.sin(theta), 0]) + pose_object.positions = new_positions + d2 = np.array([-np.sin(theta), np.cos(theta), 0]) + d3 = np.array([0, 0, 1]) + d1 = np.cross(d2, d3) + new_directors = np.column_stack((d1, d2, d3)) + pose_object.directors = new_directors + pose_object.update_states(new_positions, new_directors) + pose_object.set_keyframe(frame) + bsr.frame_manager.update() + # Task 2 # the color of the pose should change based on the angle # use the angle_to_color function defined above to compute the color code # the angle is in degrees, and the function returns a numpy array of RGBA values # the color of the pose can be updated throught pose.update_material(color=...) + + # Set the final keyframe number bsr.frame_manager.set_frame_end() From ad87d2df17d607258001aa873d0c177fe2380b71 Mon Sep 17 00:00:00 2001 From: Rohar10 Date: Thu, 10 Oct 2024 01:17:18 -0500 Subject: [PATCH 2/4] Completed pose_demo.py file --- examples/pose_demo.py | 42 +++++++++++++++------------ src/bsr/geometry/primitives/simple.py | 9 ++++-- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/examples/pose_demo.py b/examples/pose_demo.py index 80c4381..85322ef 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -2,6 +2,7 @@ import bsr from bsr import Pose +import bpy def angle_to_color(angle: float) -> np.ndarray: @@ -29,47 +30,52 @@ def angle_to_color(angle: float) -> np.ndarray: return np.array([r, g, b, 1.0]) -def main(filename: str = "pose_demo2"): +def main(filename: str = "pose_demo5"): + # initial values for frame rate and period frame_rate = 60 total_time = 5 + + # calculates total number of frames in the visualization total_frames = frame_rate * total_time - # clear all mesh objects + # clears all mesh objects bsr.clear_mesh_objects() bsr.frame_manager.set_frame_start() - - # Task 1 - # create a pose, i.e. position and director, using Pose class - # start circling around (CCW) the origin on a unit circle trajectory - # the moving direction is the tangent of the circle, which should be d2 - # the z axis should be d3, and d1 = d2 cross d3 - # the pose should be updated every frame, and will go around a circle with period 1 second - + + # intializes pose instance and angle pose_object = Pose(positions=np.array([1, 0, 0]), directors=np.eye(3),thickness_ratio=0.1) theta = 0 + + # iterates through each frame in total time duration for frame in range(total_frames): theta = 2*np.pi*frame/total_frames + + # defines path of of motion for positions of pose object new_positions = np.array([np.cos(theta), np.sin(theta), 0]) pose_object.positions = new_positions + + # defines directors of pose object d2 = np.array([-np.sin(theta), np.cos(theta), 0]) d3 = np.array([0, 0, 1]) d1 = np.cross(d2, d3) new_directors = np.column_stack((d1, d2, d3)) pose_object.directors = new_directors + + # updates positions and directors of pose object at each keyframe pose_object.update_states(new_positions, new_directors) + + # converts angle to rgb color value at each frame + color = angle_to_color(np.degrees(theta)) + + # updates pose object's colors + pose_object.update_material(color = color) + + # sets and updates keyframes pose_object.set_keyframe(frame) bsr.frame_manager.update() - # Task 2 - # the color of the pose should change based on the angle - # use the angle_to_color function defined above to compute the color code - # the angle is in degrees, and the function returns a numpy array of RGBA values - # the color of the pose can be updated throught pose.update_material(color=...) - - - # Set the final keyframe number bsr.frame_manager.set_frame_end() diff --git a/src/bsr/geometry/primitives/simple.py b/src/bsr/geometry/primitives/simple.py index 6f3f484..adc0df8 100644 --- a/src/bsr/geometry/primitives/simple.py +++ b/src/bsr/geometry/primitives/simple.py @@ -197,7 +197,7 @@ def update_material(self, **kwargs: dict[str, Any]) -> None: assert np.all(color >= 0) and np.all( color <= 1 ), "Keyword argument color should be in the range of [0, 1]." - self._material.diffuse_color = tuple(color) + self.material.diffuse_color = tuple(color) def _create_sphere(self) -> bpy.types.Object: """ @@ -215,6 +215,8 @@ def set_keyframe(self, keyframe: int) -> None: keyframe : int """ self.object.keyframe_insert(data_path="location", frame=keyframe) + self.material.keyframe_insert(data_path="diffuse_color", frame=keyframe) + class Cylinder(KeyFrameControlMixin): @@ -366,7 +368,7 @@ def update_material(self, **kwargs: dict[str, Any]) -> None: assert np.all(color >= 0) and np.all( color <= 1 ), "Values of the keyword argument `color` should be in the range of [0, 1]." - self._material.diffuse_color = tuple(color) + self.material.diffuse_color = tuple(color) def _create_cylinder( self, @@ -392,7 +394,8 @@ def set_keyframe(self, keyframe: int) -> None: self.object.keyframe_insert(data_path="location", frame=keyframe) self.object.keyframe_insert(data_path="rotation_euler", frame=keyframe) self.object.keyframe_insert(data_path="scale", frame=keyframe) - + self.material.keyframe_insert(data_path="diffuse_color", frame=keyframe) + # TODO: Will be implemented in the future class Frustum(KeyFrameControlMixin): # pragma: no cover From 3c429b98bfa87180bc90843dd4e423b8a2414b5a Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Fri, 18 Oct 2024 11:55:39 -0500 Subject: [PATCH 3/4] wip: update pose_demo and fix code style --- examples/pose_demo.py | 100 +++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/examples/pose_demo.py b/examples/pose_demo.py index 85322ef..ce320b4 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -2,11 +2,24 @@ import bsr from bsr import Pose -import bpy def angle_to_color(angle: float) -> np.ndarray: - # Normalize angle to 0-360 range + """ + Convert angle to RGB color value + + Parameters + ---------- + angle : float + The angle in degrees + + Returns + ------- + np.ndarray + The RGBA color value + """ + + # Reset angle range to 0-360 degrees angle = angle % 360 # Convert angle to radians @@ -14,70 +27,85 @@ def angle_to_color(angle: float) -> np.ndarray: # Calculate RGB values if angle < 120: - r = np.cos(rad) / 2 + 0.5 - g = np.sin(rad) / 2 + 0.5 - b = 0 + rad = 3 * rad / 2 + r = 0.5 + np.sin(rad) / 2 + g = 0.5 - np.sin(rad) / 2 + b = 0.5 - np.sin(rad) / 2 elif angle < 240: - r = 0 - g = np.cos(rad - 2 * np.pi / 3) / 2 + 0.5 - b = np.sin(rad - 2 * np.pi / 3) / 2 + 0.5 + rad = rad - 2 * np.pi / 3 + rad = 3 * rad / 2 + r = 0.5 - np.sin(rad) / 2 + g = 0.5 + np.sin(rad) / 2 + b = 0.5 - np.sin(rad) / 2 else: - r = np.sin(rad - 4 * np.pi / 3) / 2 + 0.5 - g = 0 - b = np.cos(rad - 4 * np.pi / 3) / 2 + 0.5 + rad = rad - 2 * np.pi / 3 * 2 + rad = 3 * rad / 2 + r = 0.5 - np.sin(rad) / 2 + g = 0.5 - np.sin(rad) / 2 + b = 0.5 + np.sin(rad) / 2 # Return RGBA numpy array return np.array([r, g, b, 1.0]) -def main(filename: str = "pose_demo5"): +def main(filename: str = "pose_demo"): - # initial values for frame rate and period + # initial values for frame rate and total time frame_rate = 60 total_time = 5 # calculates total number of frames in the visualization total_frames = frame_rate * total_time - # clears all mesh objects bsr.clear_mesh_objects() + + # initializes pose instance + pose_object = Pose( + positions=np.array([1, 0, 0]), + directors=np.eye(3), + thickness_ratio=0.1, + ) + + # creates an array of angles from 0 to 360 degrees + angles = np.linspace(0, 360, total_frames) + + # Set frame start bsr.frame_manager.set_frame_start() - - # intializes pose instance and angle - pose_object = Pose(positions=np.array([1, 0, 0]), directors=np.eye(3),thickness_ratio=0.1) - theta = 0 - # iterates through each frame in total time duration - for frame in range(total_frames): - theta = 2*np.pi*frame/total_frames + # iterates through each angle + for angle in angles: # defines path of of motion for positions of pose object - new_positions = np.array([np.cos(theta), np.sin(theta), 0]) - pose_object.positions = new_positions + positions = [np.cos(np.radians(angle)), np.sin(np.radians(angle)), 0.0] # defines directors of pose object - d2 = np.array([-np.sin(theta), np.cos(theta), 0]) - d3 = np.array([0, 0, 1]) + d2 = [-np.sin(np.radians(angle)), np.cos(np.radians(angle)), 0.0] + d3 = [0, 0, 1] d1 = np.cross(d2, d3) - new_directors = np.column_stack((d1, d2, d3)) - pose_object.directors = new_directors + directors = np.column_stack((d1, d2, d3)) # updates positions and directors of pose object at each keyframe - pose_object.update_states(new_positions, new_directors) + pose_object.update_states( + positions=np.array(positions), + directors=directors, + ) # converts angle to rgb color value at each frame - color = angle_to_color(np.degrees(theta)) + color = angle_to_color(angle) # updates pose object's colors - pose_object.update_material(color = color) + pose_object.update_material(color=color) # sets and updates keyframes - pose_object.set_keyframe(frame) - bsr.frame_manager.update() - - # Set the final keyframe number - bsr.frame_manager.set_frame_end() + pose_object.set_keyframe(bsr.frame_manager.frame_current) + + if bsr.frame_manager.frame_current == total_frames - 1: + # Set the final keyframe + bsr.frame_manager.set_frame_end() + else: + # updates frame + bsr.frame_manager.update() # Set the frame rate bsr.frame_manager.set_frame_rate(fps=frame_rate) @@ -85,7 +113,7 @@ def main(filename: str = "pose_demo5"): # Set the view distance bsr.set_view_distance(distance=5) - # Deslect all objects + # Deselect all objects bsr.deselect_all() # Select the camera object From 509c73aea03c703433bbc9ffc8b79d28d98916d0 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Fri, 18 Oct 2024 11:59:15 -0500 Subject: [PATCH 4/4] wip: update pose_demo and fix code style --- src/bsr/geometry/primitives/simple.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bsr/geometry/primitives/simple.py b/src/bsr/geometry/primitives/simple.py index adc0df8..9b0c6a1 100644 --- a/src/bsr/geometry/primitives/simple.py +++ b/src/bsr/geometry/primitives/simple.py @@ -216,7 +216,6 @@ def set_keyframe(self, keyframe: int) -> None: """ self.object.keyframe_insert(data_path="location", frame=keyframe) self.material.keyframe_insert(data_path="diffuse_color", frame=keyframe) - class Cylinder(KeyFrameControlMixin): @@ -229,7 +228,7 @@ class Cylinder(KeyFrameControlMixin): position_1 : NDArray The first endpoint position of the cylinder object. (3D) position_2 : NDArray - The second enspoint position of the cylinder object. (3D) + The second endpoint position of the cylinder object. (3D) radius : float The radius of the cylinder object. """ @@ -395,7 +394,7 @@ def set_keyframe(self, keyframe: int) -> None: self.object.keyframe_insert(data_path="rotation_euler", frame=keyframe) self.object.keyframe_insert(data_path="scale", frame=keyframe) self.material.keyframe_insert(data_path="diffuse_color", frame=keyframe) - + # TODO: Will be implemented in the future class Frustum(KeyFrameControlMixin): # pragma: no cover