From b6dcf92e4ba37234ee9ecbe7a3c7242812fb34b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:57:54 +0000 Subject: [PATCH 01/15] build(deps): bump certifi from 2024.6.2 to 2024.7.4 Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.6.2 to 2024.7.4. - [Commits](https://github.com/certifi/python-certifi/compare/2024.06.02...2024.07.04) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index a3d0b1b..f745d0d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alabaster" @@ -111,13 +111,13 @@ zstandard = "*" [[package]] name = "certifi" -version = "2024.6.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, - {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -1548,6 +1548,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1555,8 +1556,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1573,6 +1582,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1580,6 +1590,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, From 3c429b98bfa87180bc90843dd4e423b8a2414b5a Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Fri, 18 Oct 2024 11:55:39 -0500 Subject: [PATCH 02/15] 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 03/15] 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 From 577aa9cf46c2cb0e21efa4869cfc187727e62fe9 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 19:37:06 -0500 Subject: [PATCH 04/15] feat: add ffmpeg command in camera_movement.py (in comment) --- .gitignore | 3 ++- examples/camera_movement.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5ae4121..3b9d99c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -# image files +# rendered files *.png +*.mov # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/examples/camera_movement.py b/examples/camera_movement.py index 7fec35a..777dbec 100644 --- a/examples/camera_movement.py +++ b/examples/camera_movement.py @@ -90,3 +90,4 @@ def main( if __name__ == "__main__": main() + # ffmpeg -threads 8 -r 60 -i render/camera_movement_%03d.png -b:v 90M -c:v prores -pix_fmt yuva444p10le camera_movement.mov From c380fb988a4c8e2648babd87fae121a5c646a206 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 19:38:27 -0500 Subject: [PATCH 05/15] refactor: rename example file: elastica_timoshenko.py --- examples/{elastica-timoshenko.py => elastica_timoshenko.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{elastica-timoshenko.py => elastica_timoshenko.py} (100%) diff --git a/examples/elastica-timoshenko.py b/examples/elastica_timoshenko.py similarity index 100% rename from examples/elastica-timoshenko.py rename to examples/elastica_timoshenko.py From b39aa961e517a921b2cb7fae585ba778c68d8283 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 19:50:15 -0500 Subject: [PATCH 06/15] refactor: rename argument --- src/bsr/frame.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bsr/frame.py b/src/bsr/frame.py index d519545..8a000f5 100644 --- a/src/bsr/frame.py +++ b/src/bsr/frame.py @@ -11,19 +11,19 @@ class FrameManager(metaclass=SingletonMeta): Only one instance exist, which you can access by: bsr.frame. """ - def update(self, forwardframe: int = 1) -> None: + def update(self, forward_frame: int = 1) -> None: """ Update the current frame number of the scene. Parameters ---------- - forwardframe : int, optional + forward_frame : int, optional The number of frames to move forward. The default is 1. """ assert ( - isinstance(forwardframe, int) and forwardframe > 0 - ), "forwardframe must be a positive integer" - bpy.context.scene.frame_current += forwardframe + isinstance(forward_frame, int) and forward_frame > 0 + ), "forward_frame must be a positive integer" + bpy.context.scene.frame_current += forward_frame @property def frame_current(self) -> int: From 165d02d9839417a30f6cdfe26884f1b3d9a7de1d Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 22:10:14 -0500 Subject: [PATCH 07/15] feat: add enumerate method --- examples/camera_movement.py | 53 ++++++++++++++++---------------- examples/pose_demo.py | 60 +++++++++++++++++-------------------- src/bsr/frame.py | 27 +++++++++++++++-- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/examples/camera_movement.py b/examples/camera_movement.py index 777dbec..fde6765 100644 --- a/examples/camera_movement.py +++ b/examples/camera_movement.py @@ -12,33 +12,40 @@ def main( camera_orbiting_radius: float = 1.0, ): - # Create a new scene + # Clear all mesh objects in the new scene bsr.clear_mesh_objects() # Set the camera film background to transparent bsr.camera.set_film_transparent() + # Set the render file path + bsr.camera.set_file_path(filename + "/frame") + + # Set resolution + bsr.camera.set_resolution(1920, 1080) + # Set the camera look at location bsr.camera.look_at = np.array([0.0, 0.0, 0.0]) - # Set a frame at the origin + # Set a pose at the origin _ = Pose( positions=np.zeros(3), directors=np.identity(3), unit_length=0.25, ) - # Set the current frame number - bsr.frame_manager.frame_current = 0 - - # Set the initial keyframe number - bsr.frame_manager.set_frame_start() - - # Set the camera orbiting keyframes + # Set the camera orbiting angles angles = np.linspace( 0.0, 360.0, int(frame_rate * total_time), endpoint=False ) - for k, angle in enumerate(angles): + + # Set the initial frame + frame_start = 0 + bsr.frame_manager.set_frame_start(frame=frame_start) + + for frame_current, angle in bsr.frame_manager.enumerate( + angles, frame_current=frame_start + ): # Set the camera location bsr.camera.location = np.array( @@ -49,15 +56,8 @@ def main( ] ) - # Update the keyframe - bsr.camera.set_keyframe(bsr.frame_manager.frame_current) - - if k != len(angles) - 1: - # Update the keyframe number - bsr.frame_manager.update() - else: - # Set the final keyframe number - bsr.frame_manager.set_frame_end() + # Set and update the camera in current frame + bsr.camera.set_keyframe(frame_current) # Set the frame rate bsr.frame_manager.set_frame_rate(fps=frame_rate) @@ -65,19 +65,13 @@ def main( # Set the view distance bsr.set_view_distance(distance=5) - # Deslect all objects + # Deselect all objects bsr.deselect_all() # Select the camera object bsr.camera.select() - # Set the render file path - bsr.camera.set_file_path("render/" + filename) - - # set resolution - bsr.camera.set_resolution(1920, 1080) - - # render the scene + # Render the scene bsr.camera.render( frames=np.arange( bsr.frame_manager.frame_start, bsr.frame_manager.frame_end + 1 @@ -90,4 +84,7 @@ def main( if __name__ == "__main__": main() - # ffmpeg -threads 8 -r 60 -i render/camera_movement_%03d.png -b:v 90M -c:v prores -pix_fmt yuva444p10le camera_movement.mov + print("\n\nTo convert the frames into a video, run the following command:") + print( + r"ffmpeg -threads 8 -r 60 -i camera_movement/frame_%03d.png -b:v 90M -c:v prores -pix_fmt yuva444p10le camera_movement.mov" + ) diff --git a/examples/pose_demo.py b/examples/pose_demo.py index ce320b4..370e886 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -48,64 +48,58 @@ 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_demo", + frame_rate: int = 60, + total_time: float = 5.0, +): - # 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 + # Clear all mesh objects in the new scene bsr.clear_mesh_objects() - # initializes pose instance - pose_object = Pose( + # Initialize pose instance + pose = 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) + # Create an array of angles from 0 to 360 degrees + angles = np.linspace( + 0.0, 360.0, int(frame_rate * total_time), endpoint=False + ) - # Set frame start - bsr.frame_manager.set_frame_start() + # Set the initial frame + frame_start = 0 + bsr.frame_manager.set_frame_start(frame=frame_start) - # iterates through each angle - for angle in angles: + for frame_current, angle in bsr.frame_manager.enumerate( + angles, frame_current=frame_start + ): - # defines path of of motion for positions of pose object + # Define path of of motion for positions of pose object positions = [np.cos(np.radians(angle)), np.sin(np.radians(angle)), 0.0] - # defines directors of pose object + # Define directors of pose object d2 = [-np.sin(np.radians(angle)), np.cos(np.radians(angle)), 0.0] d3 = [0, 0, 1] d1 = np.cross(d2, d3) directors = np.column_stack((d1, d2, d3)) - # updates positions and directors of pose object at each keyframe - pose_object.update_states( + # Update positions and directors of pose object at each keyframe + pose.update_states( positions=np.array(positions), directors=directors, ) - # converts angle to rgb color value at each frame + # Convert angle to rgb color value at each frame color = angle_to_color(angle) - # updates pose object's colors - pose_object.update_material(color=color) - - # sets and updates keyframes - pose_object.set_keyframe(bsr.frame_manager.frame_current) + # Update pose object's colors + pose.update_material(color=color) - 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 and update the pose in current frame + pose.set_keyframe(frame_current) # Set the frame rate bsr.frame_manager.set_frame_rate(fps=frame_rate) diff --git a/src/bsr/frame.py b/src/bsr/frame.py index 8a000f5..321e461 100644 --- a/src/bsr/frame.py +++ b/src/bsr/frame.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Iterable, Optional import bpy @@ -8,7 +8,7 @@ class FrameManager(metaclass=SingletonMeta): """ This class provides methods for manipulating the frame of the scene. - Only one instance exist, which you can access by: bsr.frame. + Only one instance exist, which you can access by: bsr.frame_manager. """ def update(self, forward_frame: int = 1) -> None: @@ -124,3 +124,26 @@ def get_frame_rate(self) -> float: bpy.context.scene.render.fps / bpy.context.scene.render.fps_base ) return fps + + def enumerate( + self, iterable: Iterable, frame_current: Optional[int] = None + ): + """ + Enumerate through the frames of the scene. + + Parameters + ---------- + iterable : Iterable + An iterable object to enumerate. + frame_current : int, optional + The current frame number of the scene. The default is None. + If None, the number self.frame_current is used. + """ + if frame_current is not None: + self.frame_current = frame_current + for k, item in enumerate(iterable): + yield self.frame_current, item + if k != len(iterable) - 1: + self.update() # Update the frame number + else: + self.set_frame_end() # Set the final frame number From cf7c3ec93fa6df668972dd64b1e8181eb2a8600e Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 22:46:18 -0500 Subject: [PATCH 08/15] refactor: update get set methods of properties in FrameManager --- examples/camera_movement.py | 4 +- examples/pose_demo.py | 4 +- src/bsr/frame.py | 112 +++++++++++++++++++----------------- tests/test_frame.py | 57 ++++++++++++------ 4 files changed, 104 insertions(+), 73 deletions(-) diff --git a/examples/camera_movement.py b/examples/camera_movement.py index fde6765..7953a85 100644 --- a/examples/camera_movement.py +++ b/examples/camera_movement.py @@ -41,7 +41,7 @@ def main( # Set the initial frame frame_start = 0 - bsr.frame_manager.set_frame_start(frame=frame_start) + bsr.frame_manager.frame_start = frame_start for frame_current, angle in bsr.frame_manager.enumerate( angles, frame_current=frame_start @@ -60,7 +60,7 @@ def main( bsr.camera.set_keyframe(frame_current) # Set the frame rate - bsr.frame_manager.set_frame_rate(fps=frame_rate) + bsr.frame_manager.frame_rate = frame_rate # Set the view distance bsr.set_view_distance(distance=5) diff --git a/examples/pose_demo.py b/examples/pose_demo.py index 370e886..1c34121 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -71,7 +71,7 @@ def main( # Set the initial frame frame_start = 0 - bsr.frame_manager.set_frame_start(frame=frame_start) + bsr.frame_manager.frame_start = frame_start for frame_current, angle in bsr.frame_manager.enumerate( angles, frame_current=frame_start @@ -102,7 +102,7 @@ def main( pose.set_keyframe(frame_current) # Set the frame rate - bsr.frame_manager.set_frame_rate(fps=frame_rate) + bsr.frame_manager.frame_rate = frame_rate # Set the view distance bsr.set_view_distance(distance=5) diff --git a/src/bsr/frame.py b/src/bsr/frame.py index 321e461..52c62c3 100644 --- a/src/bsr/frame.py +++ b/src/bsr/frame.py @@ -11,19 +11,19 @@ class FrameManager(metaclass=SingletonMeta): Only one instance exist, which you can access by: bsr.frame_manager. """ - def update(self, forward_frame: int = 1) -> None: + def update(self, frame_forward: int = 1) -> None: """ Update the current frame number of the scene. Parameters ---------- - forward_frame : int, optional + frame_forward : int, optional The number of frames to move forward. The default is 1. """ assert ( - isinstance(forward_frame, int) and forward_frame > 0 - ), "forward_frame must be a positive integer" - bpy.context.scene.frame_current += forward_frame + isinstance(frame_forward, int) and frame_forward > 0 + ), "frame_forward must be a positive integer" + bpy.context.scene.frame_current += frame_forward @property def frame_current(self) -> int: @@ -51,69 +51,62 @@ def frame_current(self, frame: int) -> None: def frame_start(self) -> int: """ Return the start frame number of the scene. - """ - return int(bpy.context.scene.frame_start) - @property - def frame_end(self) -> int: - """ - Return the end frame number of the scene. + Returns + ------- + int + The start frame number of the scene. """ - return int(bpy.context.scene.frame_end) + frame_start = int(bpy.context.scene.frame_start) + return frame_start - def set_frame_start(self, frame: Optional[int] = None) -> None: + @frame_start.setter + def frame_start(self, frame: int) -> None: """ Set the start frame number of the scene. Parameters ---------- - frame : int, optional - The start frame number of the scene. The default is None. - If None, the current frame number is used. - """ - if frame is None: - frame = bpy.context.scene.frame_current - else: - assert ( - isinstance(frame, int) and frame >= 0 - ), "frame must be a positive integer or 0" + frame : int + The start frame number of the scene. + """ + assert ( + isinstance(frame, int) and frame >= 0 + ), "frame must be a positive integer or 0" bpy.context.scene.frame_start = frame - def set_frame_end(self, frame: Optional[int] = None) -> None: + @property + def frame_end(self) -> int: """ - Set the end frame number of the scene. + Return the end frame number of the scene. - Parameters - ---------- - frame : int, optional - The end frame number of the scene. The default is None. - If None, the current frame number is used. - """ - if frame is None: - frame = bpy.context.scene.frame_current - else: - assert ( - isinstance(frame, int) and frame >= 0 - ), "frame must be a positive integer or 0" - bpy.context.scene.frame_end = frame + Returns + ------- + int + The end frame number of the scene. + """ + frame_end = int(bpy.context.scene.frame_end) + return frame_end - def set_frame_rate(self, fps: int | float) -> None: + @frame_end.setter + def frame_end(self, frame: int) -> None: """ - Set the frame rate of the scene. + Set the end frame number of the scene. Parameters ---------- - fps : float - The frame rate of the scene. (Frame per second) + frame : int + The end frame number of the scene. """ - assert isinstance(fps, (int, float)), "fps must be a number" - assert fps > 0, "fps must be a positive value" - bpy.context.scene.render.fps = int(fps) - bpy.context.scene.render.fps_base = int(fps) / fps + assert ( + isinstance(frame, int) and frame >= 0 + ), "frame must be a positive integer or 0" + bpy.context.scene.frame_end = frame - def get_frame_rate(self) -> float: + @property + def frame_rate(self) -> float: """ - Get the frame rate of the scene. + Return the frame rate of the scene. Returns ------- @@ -125,6 +118,21 @@ def get_frame_rate(self) -> float: ) return fps + @frame_rate.setter + def frame_rate(self, fps: int | float) -> None: + """ + Set the frame rate of the scene. + + Parameters + ---------- + fps : float + The frame rate of the scene. (Frame per second) + """ + assert isinstance(fps, (int, float)), "fps must be a number" + assert fps > 0, "fps must be a positive value" + bpy.context.scene.render.fps = int(fps) + bpy.context.scene.render.fps_base = int(fps) / fps + def enumerate( self, iterable: Iterable, frame_current: Optional[int] = None ): @@ -141,9 +149,7 @@ def enumerate( """ if frame_current is not None: self.frame_current = frame_current - for k, item in enumerate(iterable): + for item in iterable: yield self.frame_current, item - if k != len(iterable) - 1: - self.update() # Update the frame number - else: - self.set_frame_end() # Set the final frame number + self.update() # Update the frame number + self.frame_end = self.frame_current - 1 # Set the final frame number diff --git a/tests/test_frame.py b/tests/test_frame.py index cdde270..6f85432 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -27,33 +27,58 @@ def test_frame_manager_update(self): frame_manager.update(10) assert frame_manager.frame_current == 10 - def test_frame_manager_update_with_wrong_forwardframe(self): + def test_frame_manager_update_with_wrong_frame_forward(self): frame_manager = FrameManager() with pytest.raises(AssertionError): frame_manager.update(-1) - def test_frame_manager_set_frame_end(self): + def test_frame_manager_get_set_frame_start(self): frame_manager = FrameManager() - frame_manager.set_frame_end(100) - assert bpy.context.scene.frame_end == 100 + frame_manager.frame_start = 10 + assert bpy.context.scene.frame_start == 10 + assert frame_manager.frame_start == 10 - def test_frame_manager_set_frame_end_with_none(self): + def test_frame_manager_set_frame_start_with_wrong_frame(self): frame_manager = FrameManager() - frame_manager.frame_current = 0 - frame_manager.update(250) - frame_manager.set_frame_end() - assert bpy.context.scene.frame_end == 250 + frame_start = frame_manager.frame_start + with pytest.raises(AssertionError): + frame_manager.frame_start = -1 + assert frame_manager.frame_start == frame_start + + def test_frame_manager_get_set_frame_end(self): + frame_manager = FrameManager() + frame_manager.frame_end = 100 + assert bpy.context.scene.frame_end == 100 + assert frame_manager.frame_end == 100 def test_frame_manager_set_frame_end_with_wrong_frame(self): frame_manager = FrameManager() + frame_end = frame_manager.frame_end with pytest.raises(AssertionError): - frame_manager.set_frame_end(-1) + frame_manager.frame_end = -1 + assert frame_manager.frame_end == frame_end def test_frame_manager_get_set_frame_rate(self): frame_manager = FrameManager() - frame_manager.set_frame_rate(30) - assert frame_manager.get_frame_rate() == 30 - frame_manager.set_frame_rate(29.97) - assert math.isclose( - frame_manager.get_frame_rate(), 29.97, abs_tol=0.001 - ) + frame_manager.frame_rate = 30 + assert frame_manager.frame_rate == 30 + frame_manager.frame_rate = 29.97 + assert math.isclose(frame_manager.frame_rate, 29.97, abs_tol=0.001) + + def test_frame_manager_set_frame_rate_with_wrong_frame_rate(self): + frame_manager = FrameManager() + frame_rate = frame_manager.frame_rate + with pytest.raises(AssertionError): + frame_manager.frame_rate = 0 + assert frame_manager.frame_rate == frame_rate + + def test_frame_manager_enumerate(self): + frame_manager = FrameManager() + frame_start = 10 + for frame_current, frame in frame_manager.enumerate( + range(5), frame_current=frame_start + ): + assert frame_current == (frame + frame_start) + assert frame_manager.frame_current == frame_current + assert frame_manager.frame_end == 14 + assert frame_manager.frame_current == 15 From 167eec165fd253eed7b2d6be22b499f73e037fb1 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 22:52:16 -0500 Subject: [PATCH 09/15] fix: return type of enumerate method in FrameManager --- src/bsr/frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bsr/frame.py b/src/bsr/frame.py index 52c62c3..a4db549 100644 --- a/src/bsr/frame.py +++ b/src/bsr/frame.py @@ -135,7 +135,7 @@ def frame_rate(self, fps: int | float) -> None: def enumerate( self, iterable: Iterable, frame_current: Optional[int] = None - ): + ) -> Iterable: """ Enumerate through the frames of the scene. From 820c1cf13b980acdc0f6ecafe67589dd7614894c Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 23:04:41 -0500 Subject: [PATCH 10/15] wip: update argument of enumerate from frame_current to frame_current_init to avoid confussion of input argument and yield value --- examples/camera_movement.py | 2 +- examples/pose_demo.py | 2 +- src/bsr/frame.py | 17 ++++++++++------- tests/test_frame.py | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/examples/camera_movement.py b/examples/camera_movement.py index 7953a85..fc10eb1 100644 --- a/examples/camera_movement.py +++ b/examples/camera_movement.py @@ -44,7 +44,7 @@ def main( bsr.frame_manager.frame_start = frame_start for frame_current, angle in bsr.frame_manager.enumerate( - angles, frame_current=frame_start + angles, frame_current_init=frame_start ): # Set the camera location diff --git a/examples/pose_demo.py b/examples/pose_demo.py index 1c34121..a54fd7f 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -74,7 +74,7 @@ def main( bsr.frame_manager.frame_start = frame_start for frame_current, angle in bsr.frame_manager.enumerate( - angles, frame_current=frame_start + angles, frame_current_init=frame_start ): # Define path of of motion for positions of pose object diff --git a/src/bsr/frame.py b/src/bsr/frame.py index a4db549..54af1f1 100644 --- a/src/bsr/frame.py +++ b/src/bsr/frame.py @@ -72,7 +72,7 @@ def frame_start(self, frame: int) -> None: """ assert ( isinstance(frame, int) and frame >= 0 - ), "frame must be a positive integer or 0" + ), "frame must be a nonnegative integer" bpy.context.scene.frame_start = frame @property @@ -100,7 +100,7 @@ def frame_end(self, frame: int) -> None: """ assert ( isinstance(frame, int) and frame >= 0 - ), "frame must be a positive integer or 0" + ), "frame must be a nonnegative integer" bpy.context.scene.frame_end = frame @property @@ -134,7 +134,7 @@ def frame_rate(self, fps: int | float) -> None: bpy.context.scene.render.fps_base = int(fps) / fps def enumerate( - self, iterable: Iterable, frame_current: Optional[int] = None + self, iterable: Iterable, frame_current_init: Optional[int] = None ) -> Iterable: """ Enumerate through the frames of the scene. @@ -143,12 +143,15 @@ def enumerate( ---------- iterable : Iterable An iterable object to enumerate. - frame_current : int, optional - The current frame number of the scene. The default is None. + frame_current_init : int, optional + The initial current frame number of the scene. The default is None. If None, the number self.frame_current is used. """ - if frame_current is not None: - self.frame_current = frame_current + if frame_current_init is not None: + assert ( + isinstance(frame_current_init, int) and frame_current_init >= 0 + ), "frame_current_init must be a nonnegative integer" + self.frame_current = frame_current_init for item in iterable: yield self.frame_current, item self.update() # Update the frame number diff --git a/tests/test_frame.py b/tests/test_frame.py index 6f85432..ec4a7ab 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -76,7 +76,7 @@ def test_frame_manager_enumerate(self): frame_manager = FrameManager() frame_start = 10 for frame_current, frame in frame_manager.enumerate( - range(5), frame_current=frame_start + range(5), frame_current_init=frame_start ): assert frame_current == (frame + frame_start) assert frame_manager.frame_current == frame_current From 9a9278cf819089eb509c3bf42165a7ff21da619f Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sat, 2 Nov 2024 23:32:27 -0500 Subject: [PATCH 11/15] refactor: test_frame.py --- tests/test_frame.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_frame.py b/tests/test_frame.py index ec4a7ab..8e60fd2 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -74,11 +74,11 @@ def test_frame_manager_set_frame_rate_with_wrong_frame_rate(self): def test_frame_manager_enumerate(self): frame_manager = FrameManager() - frame_start = 10 - for frame_current, frame in frame_manager.enumerate( - range(5), frame_current_init=frame_start + frame_init = 10 + for frame_current, step in frame_manager.enumerate( + range(5), frame_current_init=frame_init ): - assert frame_current == (frame + frame_start) + assert frame_current == (step + frame_init) assert frame_manager.frame_current == frame_current assert frame_manager.frame_end == 14 assert frame_manager.frame_current == 15 From 009eb100999e01d16a14f3f966291274179a86af Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Sun, 3 Nov 2024 12:39:55 -0600 Subject: [PATCH 12/15] wip: update frame.py --- src/bsr/frame.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bsr/frame.py b/src/bsr/frame.py index 54af1f1..248e7b1 100644 --- a/src/bsr/frame.py +++ b/src/bsr/frame.py @@ -30,7 +30,8 @@ def frame_current(self) -> int: """ Return the current frame number of the scene. """ - return int(bpy.context.scene.frame_current) + frame_current = int(bpy.context.scene.frame_current) + return frame_current @frame_current.setter def frame_current(self, frame: int) -> None: From a2ee25524f7210b7d56ab31aba29405889b56692 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Tue, 5 Nov 2024 18:33:48 -0600 Subject: [PATCH 13/15] refactor: change set_keyframe to update_keyframe --- examples/br2/callbacks.py | 6 ++--- examples/camera_movement.py | 2 +- examples/pose_demo.py | 2 +- examples/single_rigid_rod_spring_action.py | 2 +- src/bsr/_camera.py | 2 +- src/bsr/_light.py | 2 +- src/bsr/geometry/composite/pose.py | 6 ++--- src/bsr/geometry/composite/rod.py | 6 ++--- src/bsr/geometry/composite/stack.py | 2 +- src/bsr/geometry/primitives/simple.py | 6 ++--- src/bsr/tools/keyframe_mixin.py | 4 +++- src/elastica_blender/converter/npz2blend.py | 4 ++-- src/elastica_blender/rod_callback.py | 2 +- tests/geometry/stack/test_stack_properties.py | 4 ++-- .../test_interface_keyframe_setting.py | 22 +++++++++---------- 15 files changed, 37 insertions(+), 35 deletions(-) diff --git a/examples/br2/callbacks.py b/examples/br2/callbacks.py index 3652e64..957dc58 100644 --- a/examples/br2/callbacks.py +++ b/examples/br2/callbacks.py @@ -142,9 +142,9 @@ def update_states( ) def set_keyframe(self, keyframe: int) -> None: - self.bending_actuation.set_keyframe(keyframe) - self.rotation_CW_actuation.set_keyframe(keyframe) - self.rotation_CCW_actuation.set_keyframe(keyframe) + self.bending_actuation.update_keyframe(keyframe) + self.rotation_CW_actuation.update_keyframe(keyframe) + self.rotation_CCW_actuation.update_keyframe(keyframe) class BlenderBR2CallBack(BasicCallBackBaseClass): diff --git a/examples/camera_movement.py b/examples/camera_movement.py index fc10eb1..ca5311a 100644 --- a/examples/camera_movement.py +++ b/examples/camera_movement.py @@ -57,7 +57,7 @@ def main( ) # Set and update the camera in current frame - bsr.camera.set_keyframe(frame_current) + bsr.camera.update_keyframe(frame_current) # Set the frame rate bsr.frame_manager.frame_rate = frame_rate diff --git a/examples/pose_demo.py b/examples/pose_demo.py index a54fd7f..5379cb1 100644 --- a/examples/pose_demo.py +++ b/examples/pose_demo.py @@ -99,7 +99,7 @@ def main( pose.update_material(color=color) # Set and update the pose in current frame - pose.set_keyframe(frame_current) + pose.update_keyframe(frame_current) # Set the frame rate bsr.frame_manager.frame_rate = frame_rate diff --git a/examples/single_rigid_rod_spring_action.py b/examples/single_rigid_rod_spring_action.py index 0e49a66..de97a70 100644 --- a/examples/single_rigid_rod_spring_action.py +++ b/examples/single_rigid_rod_spring_action.py @@ -61,7 +61,7 @@ def analytical_solution(t, y0: float, v0: float): # update the rod keyframe = int(time_index / simulation_ratio) + 1 rod.update_states(positions=positions, radii=radii) - rod.set_keyframe(keyframe) + rod.update_keyframe(keyframe) bsr.save("single_rigid_rod_spring_action.blend") diff --git a/src/bsr/_camera.py b/src/bsr/_camera.py index e05e941..3ff2cae 100644 --- a/src/bsr/_camera.py +++ b/src/bsr/_camera.py @@ -39,7 +39,7 @@ def select(self) -> None: """ bpy.context.view_layer.objects.active = self._camera - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Sets a keyframe at the given frame. diff --git a/src/bsr/_light.py b/src/bsr/_light.py index a3f9fcd..d7f6ec3 100644 --- a/src/bsr/_light.py +++ b/src/bsr/_light.py @@ -46,7 +46,7 @@ def location(self, location: np.ndarray) -> None: """ self._light.location = location - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Sets a keyframe at the given frame. diff --git a/src/bsr/geometry/composite/pose.py b/src/bsr/geometry/composite/pose.py index f8fd733..f1d9b16 100644 --- a/src/bsr/geometry/composite/pose.py +++ b/src/bsr/geometry/composite/pose.py @@ -135,15 +135,15 @@ def update_material(self, **kwargs: dict[str, Any]) -> None: for cylinder in self.cylinders: cylinder.update_material(**kwargs) - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Set the keyframe for the pose object """ for shperes in self.spheres: - shperes.set_keyframe(keyframe) + shperes.update_keyframe(keyframe) for cylinder in self.cylinders: - cylinder.set_keyframe(keyframe) + cylinder.update_keyframe(keyframe) if TYPE_CHECKING: diff --git a/src/bsr/geometry/composite/rod.py b/src/bsr/geometry/composite/rod.py index 7ff9ebf..1dfd760 100644 --- a/src/bsr/geometry/composite/rod.py +++ b/src/bsr/geometry/composite/rod.py @@ -133,15 +133,15 @@ def update_material(self, **kwargs: dict[str, Any]) -> None: for cylinder in self.cylinders: cylinder.update_material(**kwargs) - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Set keyframe for the rod object """ for idx, sphere in enumerate(self.spheres): - sphere.set_keyframe(keyframe) + sphere.update_keyframe(keyframe) for idx, cylinder in enumerate(self.cylinders): - cylinder.set_keyframe(keyframe) + cylinder.update_keyframe(keyframe) # Alias diff --git a/src/bsr/geometry/composite/stack.py b/src/bsr/geometry/composite/stack.py index 89213b9..c5bbbf7 100644 --- a/src/bsr/geometry/composite/stack.py +++ b/src/bsr/geometry/composite/stack.py @@ -64,7 +64,7 @@ def object(self) -> list[BlenderMeshInterfaceProtocol]: """ return self._objs - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Sets a keyframe at the given frame. """ diff --git a/src/bsr/geometry/primitives/simple.py b/src/bsr/geometry/primitives/simple.py index 9b0c6a1..8e8765e 100644 --- a/src/bsr/geometry/primitives/simple.py +++ b/src/bsr/geometry/primitives/simple.py @@ -206,7 +206,7 @@ def _create_sphere(self) -> bpy.types.Object: bpy.ops.mesh.primitive_uv_sphere_add() return bpy.context.active_object - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Sets a keyframe at the given frame. @@ -382,7 +382,7 @@ def _create_cylinder( cylinder = bpy.context.active_object return cylinder - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: """ Sets a keyframe at the given frame. @@ -475,7 +475,7 @@ def update_states( ) -> None: raise NotImplementedError - def set_keyframe(self, keyframe: int) -> None: + def update_keyframe(self, keyframe: int) -> None: raise NotImplementedError diff --git a/src/bsr/tools/keyframe_mixin.py b/src/bsr/tools/keyframe_mixin.py index 72824d3..630afdc 100644 --- a/src/bsr/tools/keyframe_mixin.py +++ b/src/bsr/tools/keyframe_mixin.py @@ -21,5 +21,7 @@ def clear_animation(self: BlenderMeshInterfaceProtocol) -> None: self.object.animation_data_create() @abstractmethod - def set_keyframe(self: BlenderMeshInterfaceProtocol, keyframe: int) -> None: + def update_keyframe( + self: BlenderMeshInterfaceProtocol, keyframe: int + ) -> None: raise NotImplementedError diff --git a/src/elastica_blender/converter/npz2blend.py b/src/elastica_blender/converter/npz2blend.py index 859dcbb..9f2cdac 100644 --- a/src/elastica_blender/converter/npz2blend.py +++ b/src/elastica_blender/converter/npz2blend.py @@ -63,7 +63,7 @@ def construct_blender_file( rods.update_states( position_history[:, tidx, ...], radius_history[:, tidx, ...] ) - rods.set_keyframe(tidx) + rods.update_keyframe(tidx) else: for tag in tags: position_history = data[tag + "_position_history"] @@ -77,7 +77,7 @@ def construct_blender_file( rods.update_states( position_history[:, tidx, ...], radius_history[:, tidx, ...] ) - rods.set_keyframe(tidx) + rods.update_keyframe(tidx) bsr.save(output) diff --git a/src/elastica_blender/rod_callback.py b/src/elastica_blender/rod_callback.py index 48ccff9..785cbb9 100644 --- a/src/elastica_blender/rod_callback.py +++ b/src/elastica_blender/rod_callback.py @@ -41,5 +41,5 @@ def make_callback( positions=system.position_collection, radii=system.radius, ) - self.bpy_objs.set_keyframe(self.keyframe) + self.bpy_objs.update_keyframe(self.keyframe) self.keyframe += 1 diff --git a/tests/geometry/stack/test_stack_properties.py b/tests/geometry/stack/test_stack_properties.py index 279df3f..20631df 100644 --- a/tests/geometry/stack/test_stack_properties.py +++ b/tests/geometry/stack/test_stack_properties.py @@ -9,13 +9,13 @@ def test_object_property(): assert stack.object == [1, 2, 3] -def test_set_keyframe(mocker): +def test_update_keyframe(mocker): stack = BaseStack() mock_rod = mocker.Mock() n_repeat = 3 val = 5 stack._objs = [mock_rod] * n_repeat - stack.set_keyframe(val) + stack.update_keyframe(val) mock_rod.set_keyframe.assert_called() assert mock_rod.set_keyframe.call_count == n_repeat diff --git a/tests/geometry/test_interface_keyframe_setting.py b/tests/geometry/test_interface_keyframe_setting.py index fbdaf26..ce5e91d 100644 --- a/tests/geometry/test_interface_keyframe_setting.py +++ b/tests/geometry/test_interface_keyframe_setting.py @@ -28,50 +28,50 @@ def count_number_of_keyframes_action(obj): return len(action.fcurves[0].keyframe_points) -def test_set_keyframe_count_for_primitive_sphere(): +def test_update_keyframe_count_for_primitive_sphere(): primitive = Sphere(position=np.array([0, 0, 0]), radius=1.0) - primitive.set_keyframe(1) + primitive.update_keyframe(1) assert count_number_of_keyframes_action(primitive.object) == 1 - primitive.set_keyframe(2) + primitive.update_keyframe(2) assert count_number_of_keyframes_action(primitive.object) == 2 - # Setting keyfrome at the same frame should not increase the number of keyframes: - primitive.set_keyframe(2) + # Setting keyframe at the same frame should not increase the number of keyframes: + primitive.update_keyframe(2) assert count_number_of_keyframes_action(primitive.object) == 2 primitive.clear_animation() assert count_number_of_keyframes_action(primitive.object) == 0 - primitive.set_keyframe(1) + primitive.update_keyframe(1) assert count_number_of_keyframes_action(primitive.object) == 1 # Clear the test primitive.clear_animation() -def test_set_keyframe_count_for_primitive_cylinder(): +def test_update_keyframe_count_for_primitive_cylinder(): primitive = Cylinder( position_1=np.array([0, 0, 0]), position_2=np.array([0, 0, 1]), radius=1.0, ) - primitive.set_keyframe(1) + primitive.update_keyframe(1) assert count_number_of_keyframes_action(primitive.object) == 1 - primitive.set_keyframe(2) + primitive.update_keyframe(2) assert count_number_of_keyframes_action(primitive.object) == 2 # Setting keyfrome at the same frame should not increase the number of keyframes: - primitive.set_keyframe(2) + primitive.update_keyframe(2) assert count_number_of_keyframes_action(primitive.object) == 2 primitive.clear_animation() assert count_number_of_keyframes_action(primitive.object) == 0 - primitive.set_keyframe(1) + primitive.update_keyframe(1) assert count_number_of_keyframes_action(primitive.object) == 1 # Clear the test From 4ad2b62e7f489bdeb69f2af1f052725997a37d90 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Tue, 5 Nov 2024 18:42:02 -0600 Subject: [PATCH 14/15] refactor: change set_keyframe to update_keyframe --- src/bsr/geometry/composite/stack.py | 2 +- src/bsr/geometry/primitives/simple.py | 2 +- src/bsr/tools/protocol.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bsr/geometry/composite/stack.py b/src/bsr/geometry/composite/stack.py index c5bbbf7..399bb11 100644 --- a/src/bsr/geometry/composite/stack.py +++ b/src/bsr/geometry/composite/stack.py @@ -69,7 +69,7 @@ def update_keyframe(self, keyframe: int) -> None: Sets a keyframe at the given frame. """ for obj in self._objs: - obj.set_keyframe(keyframe) + obj.update_keyframe(keyframe) @classmethod def create( diff --git a/src/bsr/geometry/primitives/simple.py b/src/bsr/geometry/primitives/simple.py index 8e8765e..bd5fd9e 100644 --- a/src/bsr/geometry/primitives/simple.py +++ b/src/bsr/geometry/primitives/simple.py @@ -51,7 +51,7 @@ def _validate_position(position: NDArray) -> None: """ Checks if inputted position values are valid - Paramters + Parameters --------- position: NDArray Position input (endpoint or centerpoint depending on Object type) diff --git a/src/bsr/tools/protocol.py b/src/bsr/tools/protocol.py index 914dc94..e9481ce 100644 --- a/src/bsr/tools/protocol.py +++ b/src/bsr/tools/protocol.py @@ -4,4 +4,4 @@ class BlenderKeyframeManipulateProtocol(Protocol): def clear_animation(self) -> None: ... - def set_keyframe(self, keyframe: int) -> None: ... + def update_keyframe(self, keyframe: int) -> None: ... From 8f906a554c2039d76eca49148176268a7a624aa2 Mon Sep 17 00:00:00 2001 From: Hanson-HSChang Date: Tue, 5 Nov 2024 18:45:17 -0600 Subject: [PATCH 15/15] refactor: change set_keyframe to update_keyframe --- tests/geometry/stack/test_stack_properties.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/geometry/stack/test_stack_properties.py b/tests/geometry/stack/test_stack_properties.py index 20631df..5caaf55 100644 --- a/tests/geometry/stack/test_stack_properties.py +++ b/tests/geometry/stack/test_stack_properties.py @@ -17,6 +17,6 @@ def test_update_keyframe(mocker): stack._objs = [mock_rod] * n_repeat stack.update_keyframe(val) - mock_rod.set_keyframe.assert_called() - assert mock_rod.set_keyframe.call_count == n_repeat - mock_rod.set_keyframe.assert_called_with(val) + mock_rod.update_keyframe.assert_called() + assert mock_rod.update_keyframe.call_count == n_repeat + mock_rod.update_keyframe.assert_called_with(val)