1- import re
21import bpy
32
43from bpy .types import Operator
54from bpy_extras .io_utils import ExportHelper
65from .base_export import BaseExport
7- from ..utils .servo_settings import get_pose_bone_by_servo_id
86
97
108class ArduinoExport (Operator , BaseExport , ExportHelper ):
11- bl_idname = "export_anim.servo_positions_arduino "
12- bl_label = "Animation Servo Positions (.h)"
9+ bl_idname = "export_anim.servo_animation_arduino "
10+ bl_label = "Servo Animation (.h)"
1311 bl_description = "Save an Arduino header file with servo position values of the active armature"
1412
1513 filename_ext = ".h"
16- position_chunk_size = 50
14+ chunk_size = 12
1715
1816 filter_glob : bpy .props .StringProperty (
1917 default = "*.h" ,
2018 options = {'HIDDEN' },
2119 maxlen = 255
2220 )
2321
24- progmem : bpy .props .BoolProperty (
25- name = "Add PROGMEM modifier" ,
26- description = (
27- "Add the PROGMEM modifier to each position array which enables "
28- "an Arduino micro controller to handle large arrays"
29- ),
30- default = True
31- )
32-
33- animation_variables : bpy .props .BoolProperty (
34- name = "Add animation variables" ,
35- description = "Add the fps and frames count as constant variables" ,
36- default = True
37- )
38-
3922 namespace : bpy .props .BoolProperty (
4023 name = "Add scene namespace" ,
4124 description = (
@@ -44,53 +27,60 @@ class ArduinoExport(Operator, BaseExport, ExportHelper):
4427 )
4528 )
4629
47- def export (self , positions , context ):
48- variable_type = 'int' if self .precision == 0 else 'float'
30+ def export (self , positions , filepath , context ):
4931 fps , frames , seconds = self .get_time_meta (context .scene )
5032 filename = self .get_blend_filename ()
5133
5234 content = (
5335 "/*\n Blender Servo Animation Positions\n \n "
5436 f"FPS: { fps } \n Frames: { frames } \n Seconds: { seconds } \n "
55- f"Bones: { len (positions )} \n Armature: { context .object .name } \n "
56- f"Scene: { context .scene .name } \n File: { filename } \n */\n "
37+ f"Bones: { len (positions [0 ])} \n Armature: { context .object .name } \n "
38+ f"Scene: { context .scene .name } \n File: { filename } \n */\n \n "
39+ "#include <Arduino.h>\n "
5740 )
5841
59- if self .progmem or self .animation_variables :
60- content += "\n #include <Arduino.h>\n "
42+ commands = self .get_commands (positions )
43+ length = len (commands )
44+ lines = self .join_by_chunk_size (commands , self .chunk_size )
6145
6246 if self .namespace :
63- content += f"\n namespace { context .scene .name } {{\n "
47+ scene_name = self .format_scene_name ()
48+ content += f"\n namespace { scene_name } {{\n "
6449
65- if self . animation_variables :
66- content += (
67- f"\n const byte FPS = { fps } ;"
68- f"\n const int FRAMES = { frames } ; \n "
69- )
50+ content += (
51+ f" \n const byte FPS = { fps } ;"
52+ f"\n const int FRAMES = { frames } ;"
53+ f"\n const int LENGTH = { length } ; \n \n "
54+ )
7055
71- for servo_id in positions :
72- pose_bone = get_pose_bone_by_servo_id (servo_id , context .scene )
73- bone_positions = list (map (str , positions [servo_id ]))
74- variable_name = re .sub ('[^a-zA-Z0-9_]' , '' , pose_bone .bone .name )
75- array_size = "FRAMES" if self .animation_variables else frames
76- content += (
77- f"\n // Servo ID: { servo_id } \n "
78- f"const { variable_type } { variable_name } [{ array_size } ] "
79- )
56+ content += f'const byte PROGMEM ANIMATION_DATA[LENGTH] = {{\n { lines } }};\n '
8057
81- if self .progmem :
82- content += 'PROGMEM '
58+ if self .namespace :
59+ content += f" \n }} // namespace { scene_name } \n "
8360
84- content += '= {\n '
61+ with open (filepath , 'w' , encoding = 'utf-8' ) as file :
62+ file .write (content )
8563
86- for i in range ( 0 , len ( bone_positions ), self . position_chunk_size ):
87- content += ' ' + \
88- ', ' . join (
89- bone_positions [ i : i + self . position_chunk_size ]) + ', \n '
64+ @ classmethod
65+ def join_by_chunk_size ( cls , iterable , chunk_size ):
66+ output = ''
67+ str_iterable = list ( map ( cls . format_hex , iterable ))
9068
91- content += '};\n '
69+ for i in range (0 , len (str_iterable ), chunk_size ):
70+ output += ' ' + ', ' .join (str_iterable [i :i + chunk_size ]) + ',\n '
9271
93- if self .namespace :
94- content += f"\n }} // namespace { context .scene .name } \n "
72+ return output
73+
74+ @classmethod
75+ def format_hex (cls , byte ):
76+ return f'{ byte :#04x} '
77+
78+ @classmethod
79+ def format_scene_name (cls ):
80+ valid_chars = set ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' )
81+ scene_name = '' .join (c if c in valid_chars else '_' for c in bpy .context .scene .name )
82+
83+ if scene_name [0 ].isdigit ():
84+ scene_name = '_' + scene_name
9585
96- return content
86+ return scene_name
0 commit comments