1
- import re
2
1
import bpy
3
2
4
3
from bpy .types import Operator
5
4
from bpy_extras .io_utils import ExportHelper
6
5
from .base_export import BaseExport
7
- from ..utils .servo_settings import get_pose_bone_by_servo_id
8
6
9
7
10
8
class 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)"
13
11
bl_description = "Save an Arduino header file with servo position values of the active armature"
14
12
15
13
filename_ext = ".h"
16
- position_chunk_size = 50
14
+ chunk_size = 12
17
15
18
16
filter_glob : bpy .props .StringProperty (
19
17
default = "*.h" ,
20
18
options = {'HIDDEN' },
21
19
maxlen = 255
22
20
)
23
21
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
-
39
22
namespace : bpy .props .BoolProperty (
40
23
name = "Add scene namespace" ,
41
24
description = (
@@ -44,53 +27,60 @@ class ArduinoExport(Operator, BaseExport, ExportHelper):
44
27
)
45
28
)
46
29
47
- def export (self , positions , context ):
48
- variable_type = 'int' if self .precision == 0 else 'float'
30
+ def export (self , positions , filepath , context ):
49
31
fps , frames , seconds = self .get_time_meta (context .scene )
50
32
filename = self .get_blend_filename ()
51
33
52
34
content = (
53
35
"/*\n Blender Servo Animation Positions\n \n "
54
36
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 "
57
40
)
58
41
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 )
61
45
62
46
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 "
64
49
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
+ )
70
55
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 '
80
57
81
- if self .progmem :
82
- content += 'PROGMEM '
58
+ if self .namespace :
59
+ content += f" \n }} // namespace { scene_name } \n "
83
60
84
- content += '= {\n '
61
+ with open (filepath , 'w' , encoding = 'utf-8' ) as file :
62
+ file .write (content )
85
63
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 ))
90
68
91
- content += '};\n '
69
+ for i in range (0 , len (str_iterable ), chunk_size ):
70
+ output += ' ' + ', ' .join (str_iterable [i :i + chunk_size ]) + ',\n '
92
71
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
95
85
96
- return content
86
+ return scene_name
0 commit comments