Cleanup: simplify file name incrementing logic
[blender-addons.git] / space_view3d_stored_views / core.py
blob5360b4bb8b6a5013c40d8b5b78940a145d6fcd91
1 # gpl authors: nfloyd, Francesco Siddi
4 import logging
5 module_logger = logging.getLogger(__name__)
7 import hashlib
8 import bpy
11 #Utility function get preferences setting for exporters
12 def get_preferences():
13 # replace the key if the add-on name changes
14 addon = bpy.context.preferences.addons[__package__]
15 show_warn = (addon.preferences.show_exporters if addon else False)
17 return show_warn
20 class StoredView():
21 def __init__(self, mode, index=None):
22 self.logger = logging.getLogger('%s.StoredView' % __name__)
23 self.scene = bpy.context.scene
24 self.view3d = bpy.context.space_data
25 self.index = index
26 self.data_store = DataStore(mode=mode)
28 def save(self):
29 if self.index == -1:
30 stored_view, self.index = self.data_store.create()
31 else:
32 stored_view = self.data_store.get(self.index)
33 self.from_v3d(stored_view)
34 self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name))
36 def set(self):
37 stored_view = self.data_store.get(self.index)
38 self.update_v3d(stored_view)
39 self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name))
41 def from_v3d(self, stored_view):
42 raise NotImplementedError("Subclass must implement abstract method")
44 def update_v3d(self, stored_view):
45 raise NotImplementedError("Subclass must implement abstract method")
47 @staticmethod
48 def is_modified(context, stored_view):
49 raise NotImplementedError("Subclass must implement abstract method")
52 class POV(StoredView):
53 def __init__(self, index=None):
54 super().__init__(mode='POV', index=index)
55 self.logger = logging.getLogger('%s.POV' % __name__)
57 def from_v3d(self, stored_view):
58 view3d = self.view3d
59 region3d = view3d.region_3d
61 stored_view.distance = region3d.view_distance
62 stored_view.location = region3d.view_location
63 stored_view.rotation = region3d.view_rotation
64 stored_view.perspective_matrix_md5 = POV._get_perspective_matrix_md5(region3d)
65 stored_view.perspective = region3d.view_perspective
66 stored_view.lens = view3d.lens
67 stored_view.clip_start = view3d.clip_start
68 stored_view.clip_end = view3d.clip_end
70 if region3d.view_perspective == 'CAMERA':
71 stored_view.camera_type = view3d.camera.type # type : 'CAMERA' or 'MESH'
72 stored_view.camera_name = view3d.camera.name # store string instead of object
73 if view3d.lock_object is not None:
74 stored_view.lock_object_name = view3d.lock_object.name # idem
75 else:
76 stored_view.lock_object_name = ""
77 stored_view.lock_cursor = view3d.lock_cursor
78 stored_view.cursor_location = bpy.context.scene.cursor.location
80 def update_v3d(self, stored_view):
81 view3d = self.view3d
82 region3d = view3d.region_3d
83 region3d.view_distance = stored_view.distance
84 region3d.view_location = stored_view.location
85 region3d.view_rotation = stored_view.rotation
86 region3d.view_perspective = stored_view.perspective
87 view3d.lens = stored_view.lens
88 view3d.clip_start = stored_view.clip_start
89 view3d.clip_end = stored_view.clip_end
90 view3d.lock_cursor = stored_view.lock_cursor
91 if stored_view.lock_cursor is True:
92 # update cursor only if view is locked to cursor
93 view3d.cursor_location = stored_view.cursor_location
95 if stored_view.perspective == "CAMERA":
97 lock_obj = self._get_object(stored_view.lock_object_name)
98 if lock_obj:
99 view3d.lock_object = lock_obj
100 else:
101 cam = self._get_object(stored_view.camera_name)
102 if cam:
103 view3d.camera = cam
105 @staticmethod
106 def _get_object(name, pointer=None):
107 return bpy.data.objects.get(name)
109 @staticmethod
110 def is_modified(context, stored_view):
111 # TODO: check for others param, currently only perspective
112 # and perspective_matrix are checked
113 POV.logger = logging.getLogger('%s.POV' % __name__)
114 view3d = context.space_data
115 region3d = view3d.region_3d
116 if region3d.view_perspective != stored_view.perspective:
117 POV.logger.debug('view_perspective')
118 return True
120 md5 = POV._get_perspective_matrix_md5(region3d)
121 if (md5 != stored_view.perspective_matrix_md5 and
122 region3d.view_perspective != "CAMERA"):
123 POV.logger.debug('perspective_matrix')
124 return True
126 return False
128 @staticmethod
129 def _get_perspective_matrix_md5(region3d):
130 md5 = hashlib.md5(str(region3d.perspective_matrix).encode('utf-8')).hexdigest()
131 return md5
134 # class Layers(StoredView):
135 # def __init__(self, index=None):
136 # super().__init__(mode='COLLECTIONS', index=index)
137 # self.logger = logging.getLogger('%s.Layers' % __name__)
139 # def from_v3d(self, stored_view):
140 # view3d = self.view3d
141 # stored_view.view_layers = bpy.types.layer.collection
142 # stored_view.scene_layers = self.scene.layers
143 # stored_view.lock_camera = view3d.lock_camera
145 # def update_v3d(self, stored_view):
146 # view3d = self.view3d
147 # view3d.camera = stored_view.camera
148 # if stored_view.camera is True:
149 # self.scene.layers = stored_view.scene_layers
150 # else:
151 # view3d.layers = stored_view.view_layers
153 # @staticmethod
154 # def is_modified(context, stored_view):
155 # Layers.logger = logging.getLogger('%s.Layers' % __name__)
156 # if stored_view.camera != context.space_data.camera:
157 # Layers.logger.debug('lock_camera')
158 # return True
159 # if stored_view.camera is True:
160 # for i in range(20):
161 # if stored_view.scene_layers[i] != context.scene.layers[i]:
162 # Layers.logger.debug('scene_layers[%s]' % (i, ))
163 # return True
164 # else:
165 # for i in range(20):
166 # if stored_view.view_layers[i] != context.space_data.view3d.layers[i]:
167 # return True
168 # return False
171 # class Display(StoredView):
172 # def __init__(self, index=None):
173 # super().__init__(mode='DISPLAY', index=index)
174 # self.logger = logging.getLogger('%s.Display' % __name__)
176 # def from_v3d(self, stored_view):
177 # view3d = self.view3d
178 # stored_view.viewport_shade = view3d.viewport_shade
179 # stored_view.show_only_render = view3d.show_only_render
180 # stored_view.show_outline_selected = view3d.show_outline_selected
181 # stored_view.show_all_objects_origin = view3d.show_all_objects_origin
182 # stored_view.show_relationship_lines = view3d.show_relationship_lines
183 # stored_view.show_floor = view3d.show_floor
184 # stored_view.show_axis_x = view3d.show_axis_x
185 # stored_view.show_axis_y = view3d.show_axis_y
186 # stored_view.show_axis_z = view3d.show_axis_z
187 # stored_view.grid_lines = view3d.grid_lines
188 # stored_view.grid_scale = view3d.grid_scale
189 # stored_view.grid_subdivisions = view3d.grid_subdivisions
190 # stored_view.material_mode = self.scene.game_settings.material_mode
191 # stored_view.show_textured_solid = view3d.show_textured_solid
193 # def update_v3d(self, stored_view):
194 # view3d = self.view3d
195 # view3d.viewport_shade = stored_view.viewport_shade
196 # view3d.show_only_render = stored_view.show_only_render
197 # view3d.show_outline_selected = stored_view.show_outline_selected
198 # view3d.show_all_objects_origin = stored_view.show_all_objects_origin
199 # view3d.show_relationship_lines = stored_view.show_relationship_lines
200 # view3d.show_floor = stored_view.show_floor
201 # view3d.show_axis_x = stored_view.show_axis_x
202 # view3d.show_axis_y = stored_view.show_axis_y
203 # view3d.show_axis_z = stored_view.show_axis_z
204 # view3d.grid_lines = stored_view.grid_lines
205 # view3d.grid_scale = stored_view.grid_scale
206 # view3d.grid_subdivisions = stored_view.grid_subdivisions
207 # self.scene.game_settings.material_mode = stored_view.material_mode
208 # view3d.show_textured_solid = stored_view.show_textured_solid
210 # @staticmethod
211 # def is_modified(context, stored_view):
212 # Display.logger = logging.getLogger('%s.Display' % __name__)
213 # view3d = context.space_data
214 # excludes = ["material_mode", "quad_view", "lock_rotation", "show_sync_view", "use_box_clip", "name"]
215 # for k, v in stored_view.items():
216 # if k not in excludes:
217 # if getattr(view3d, k) != getattr(stored_view, k):
218 # return True
220 # if stored_view.material_mode != context.scene.game_settings.material_mode:
221 # Display.logger.debug('material_mode')
222 # return True
225 class View(StoredView):
226 def __init__(self, index=None):
227 super().__init__(mode='VIEW', index=index)
228 self.logger = logging.getLogger('%s.View' % __name__)
229 self.pov = POV()
230 # self.layers = Layers()
231 # self.display = Display()
233 def from_v3d(self, stored_view):
234 self.pov.from_v3d(stored_view.pov)
235 # self.layers.from_v3d(stored_view.layers)
236 # self.display.from_v3d(stored_view.display)
238 def update_v3d(self, stored_view):
239 self.pov.update_v3d(stored_view.pov)
240 # self.layers.update_v3d(stored_view.layers)
241 # self.display.update_v3d(stored_view.display)
243 @staticmethod
244 def is_modified(context, stored_view):
245 POV.is_modified(context, stored_view.pov) #or \
246 # Layers.is_modified(context, stored_view.layers) or \
247 # Display.is_modified(context, stored_view.display):
248 return True
249 # return False
252 class DataStore():
253 def __init__(self, scene=None, mode=None):
254 if scene is None:
255 scene = bpy.context.scene
256 stored_views = scene.stored_views
257 self.mode = mode
259 if mode is None:
260 self.mode = stored_views.mode
262 if self.mode == 'VIEW':
263 self.list = stored_views.view_list
264 self.current_index = stored_views.current_indices[0]
265 elif self.mode == 'POV':
266 self.list = stored_views.pov_list
267 self.current_index = stored_views.current_indices[1]
268 elif self.mode == 'LAYERS':
269 self.list = stored_views.layers_list
270 self.current_index = stored_views.current_indices[2]
271 elif self.mode == 'DISPLAY':
272 self.list = stored_views.display_list
273 self.current_index = stored_views.current_indices[3]
275 def create(self):
276 item = self.list.add()
277 item.name = self._generate_name()
278 index = len(self.list) - 1
279 self._set_current_index(index)
280 return item, index
282 def get(self, index):
283 self._set_current_index(index)
284 return self.list[index]
286 def delete(self, index):
287 if self.current_index > index:
288 self._set_current_index(self.current_index - 1)
289 elif self.current_index == index:
290 self._set_current_index(-1)
292 self.list.remove(index)
294 def _set_current_index(self, index):
295 self.current_index = index
296 mode = self.mode
297 stored_views = bpy.context.scene.stored_views
298 if mode == 'VIEW':
299 stored_views.current_indices[0] = index
300 elif mode == 'POV':
301 stored_views.current_indices[1] = index
302 elif mode == 'LAYERS':
303 stored_views.current_indices[2] = index
304 elif mode == 'DISPLAY':
305 stored_views.current_indices[3] = index
307 def _generate_name(self):
308 default_name = str(self.mode)
309 names = []
310 for i in self.list:
311 i_name = i.name
312 if i_name.startswith(default_name):
313 names.append(i_name)
314 names.sort()
315 try:
316 l_name = names[-1]
317 post_fix = l_name.rpartition('.')[2]
318 if post_fix.isnumeric():
319 post_fix = str(int(post_fix) + 1).zfill(3)
320 else:
321 if post_fix == default_name:
322 post_fix = "001"
323 return default_name + "." + post_fix
324 except:
325 return default_name
327 @staticmethod
328 def sanitize_data(scene):
330 def check_objects_references(mode, list):
331 to_remove = []
332 for i, list_item in enumerate(list.items()):
333 key, item = list_item
334 if mode == 'POV' or mode == 'VIEWS':
335 if mode == 'VIEWS':
336 item = item.pov
338 if item.perspective == "CAMERA":
340 camera = bpy.data.objects.get(item.camera_name)
341 if camera is None:
342 try: # pick a default camera TODO: ask to pick?
343 camera = bpy.data.cameras[0]
344 item.camera_name = camera.name
345 except: # couldn't find a camera in the scene
346 pass
348 obj = bpy.data.objects.get(item.lock_object_name)
349 if obj is None and camera is None:
350 to_remove.append(i)
352 for i in reversed(to_remove):
353 list.remove(i)
355 modes = ['POV', 'VIEW', 'DISPLAY', 'LAYERS']
356 for mode in modes:
357 data = DataStore(scene=scene, mode=mode)
358 check_objects_references(mode, data.list)
361 def stored_view_factory(mode, *args, **kwargs):
362 if mode == 'POV':
363 return POV(*args, **kwargs)
364 elif mode == 'LAYERS':
365 return Layers(*args, **kwargs)
366 elif mode == 'DISPLAY':
367 return Display(*args, **kwargs)
368 elif mode == 'VIEW':
369 return View(*args, **kwargs)