Source code for ggmolvis.sceneobjects.base

"""
This module contains the base classes for the scene objects.

Classes
=======
.. autoclass:: SceneObject
    :members:
.. autoclass:: SceneObjectCollection
    :members:
"""
import bpy
from abc import abstractmethod

import numpy as np
from typing import Tuple, List, Union

from ..base import GGMolvisArtist
from ..world import World
from ..camera import Camera
from ..properties import Color, Material, Style
from ..utils import look_at


[docs] class SceneObject(GGMolvisArtist): """Class for the scene object. This class is the parent class for all the objects in the scene. Access the blender object using the object property, `self.object`. The name might be different from the initial name, as Blender might append a number to the name if the name already exists. """ def __init__( self, name=None, location=None, rotation=None, scale=None, color="black", material="backdrop", style="default", ): self.world = World(location=location, rotation=rotation, scale=scale) super().__init__() self.name = name self._subframes = 0 # Create the object obj = self._create_object() # set the name that Blender assigned to the object self.name = obj.name self._init_color(color) self._init_material(material) self._init_style(style) self.world._apply_to(self.object) self._init_camera() self._move_to_collection() self.draw() self._update_frame(bpy.context.scene.frame_current) def _init_color(self, color="black"): self._color = Color(self, color) def _init_material(self, material="backdrop"): self._material = Material(self, material) def _init_style(self, style="default"): self._style = Style(self, style) def _init_camera(self): self.camera = Camera(name=f"{self.name}_camera") size_obj_xyz = np.array(self.object.dimensions) # center of the object center_xyz = np.zeros(3) bbox = self.object.bound_box for v in bbox: center_xyz += np.array(v) center_xyz /= 8 # shift by the location of the object center_xyz += np.array(self.object.location) camera_center = center_xyz.copy() camera_center[1] = camera_center[1] - size_obj_xyz[1] * 3 camera_center[2] = camera_center[2] + size_obj_xyz[2] * 1.3 rot = look_at( camera_position=camera_center, target_position=center_xyz ) self.camera.world.location._set_coordinates(camera_center) self.camera.world.rotation._set_coordinates(np.rad2deg(list(rot.to_euler()))) def _move_to_collection(self): """Move the object to the collection with the same name""" mn_coll = bpy.data.collections.get('MolecularNodes') coll = mn_coll.children.get(self.name) if coll is None: coll = bpy.data.collections.new(self.name) mn_coll.children.link(coll) coll.objects.link(self.object) mn_coll.objects.unlink(self.object) self.camera._move_to_collection(self.name) def _update_frame(self, frame): object = self.object self.material._apply_to(object, frame) self.color._apply_to(object, frame) self.world._apply_to(object, frame) camera = self.camera camera._update_frame(frame) @abstractmethod def _create_object(self): """Create the object Returns ------- bpy.types.Object The created object """ raise NotImplementedError( "This method is only available in the subclass" )
[docs] def draw(self): """Draw the object""" self.style._apply_to(self.object) self.color._apply_to(self.object) self.material._apply_to(self.object)
@property def object(self): return bpy.data.objects[self.name] @property def name(self): return self._name @name.setter def name(self, value): self._name = value def set_style(self, style): self.style.set_style(style) self.style._apply_to(self.object) def set_material(self, material): self.material.set_material(material) self.material._apply_to(self.object) def set_color(self, color): self.color.set_color(color) self.color._apply_to(self.object) def render(self, **kwargs): self.camera.render(**kwargs) @property def color(self): return self._color @color.setter def color(self, value): raise AttributeError("Use `set_color` instead") @property def style(self): return self._style @style.setter def style(self, value): raise AttributeError("Use `set_style` instead") @property def material(self): return self._material @material.setter def material(self, value): raise AttributeError("Use `set_material` instead") def __getstate__(self): state = self.__dict__.copy() try: del state["object"] except KeyError: pass return state
[docs] class SceneObjectCollection: """Class for the collection of scene objects"""
#TODO: Implement the class