Source code for pohlke.properties

# SPDX-FileCopyrightText: 2021-2026 Julien Rippinger, Ian Bertin <alicelab.be>
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""
Defines the PropertyGroup storing projection settings and update callbacks for the Parallel Cameras add-on.
"""

import bpy
from math import pi, radians
from .utils.data import CAMERA_SETTINGS
from .utils.helpers import camera_attributes_updated, camera_type_updated

# -------------------------------------------------------------------
# UPDATE CALLBACKS
# -------------------------------------------------------------------


[docs] def camera_type_update( self: bpy.types.PropertyGroup, context: bpy.types.Context ) -> None: """ Update callback triggered when projection type or preset changes, it calls :py:func:`pohlke.utils.helpers.camera_type_updated`. Parameters --------- self : bpy.types.PropertyGroup Property group instance context : bpy.types.Context Blender context """ if self.is_updating: return self.is_updating = True try: camera_type_updated(self) finally: self.is_updating = False
[docs] def camera_alpha_updated( self: bpy.types.PropertyGroup, context: bpy.types.Context ) -> None: """ Update callback triggered when alpha value changes, it calls :py:func:`pohlke.utils.helpers.camera_attributes_updated` Parameters --------- self : bpy.types.PropertyGroup Property group instance context : bpy.types.Context Blender context """ if self.is_updating: return self.is_updating = True try: camera_attributes_updated(self, "alpha") finally: self.is_updating = False
[docs] def camera_beta_updated( self: bpy.types.PropertyGroup, context: bpy.types.Context ) -> None: """ Update callback triggered when beta value changes, it calls :py:func:`pohlke.utils.helpers.camera_attributes_updated` Parameters --------- self : bpy.types.PropertyGroup Property group instance context : bpy.types.Context Blender context """ if self.is_updating: return self.is_updating = True try: camera_attributes_updated(self, "beta") finally: self.is_updating = False
[docs] def camera_z_value_updated( self: bpy.types.PropertyGroup, context: bpy.types.Context ) -> None: """ Update callback triggered when z value changes. Change the camera type if the z-axis reduction factor does not match the selected preset. Parameters --------- self : bpy.types.PropertyGroup Property group instance context : bpy.types.Context Blender context """ if self.is_updating: return self.is_updating = True try: if self.projection_type == "OBLIQUE": camera_type = CAMERA_SETTINGS.get_preset_name( self.projection_type, self.alpha, self.beta, self.z_value ) self.oblique_type = camera_type finally: self.is_updating = False
# ------------------------------------------------------------------- # PROPERTY GROUP # -------------------------------------------------------------------
[docs] class Pohlke_PT_CameraProperties(bpy.types.PropertyGroup): """ Property group storing projection parameters for the parallel cameras. This group is attached to bpy.types.Scene and used by the UI panel to configure camera creation. Attributes ---------- projection_type : enum Main projection category: * 'AXONOMETRIC' : Axonometric projections. * 'OBLIQUE' : Oblique projections. Triggers `camera_type_update` on change. axonometric_type : enum Sub-preset for axonometric cameras. Available values are dynamically generated from the projection definitions declared in `presets.toml` and loaded via `data.py`. They are no longer hardcoded. Each preset follows the naming pattern: 'AXONOMETRIC_#<n>' where <n> is the index of the preset as defined in `presets.toml`. A 'CUSTOM' entry is automatically added when the current projection parameters do not match any predefined preset. Triggers `camera_type_update` on change. oblique_type : enum Sub-preset for oblique cameras. Available values are dynamically generated from the projection definitions declared in `presets.toml` and loaded via `data.py`. They are no longer hardcoded. Each preset follows the naming pattern: 'OBLIQUE_#<n>' where <n> is the index of the preset as defined in `presets.toml`. A 'CUSTOM' entry is automatically added when the current projection parameters do not match any predefined preset. Triggers `camera_type_update` on change. alpha : float The alpha angle of the plan (in radians). Triggers `camera_alpha_updated` on change. beta : float The beta angle of the plan (in radians). Triggers `camera_beta_updated` on change. rotation : float Computed rotation around the vertical axis, derived from beta. altitude : float Computed altitude angle, derived from alpha. altitude_oblique : float Complementary angle used specifically for oblique projections. is_updating : bool Internal flag to prevent infinite update loops during property sync. x_value : float Scaling factor for the X axis (0.0 to 1.0). normalized_x_value : float Nomalized scaling factor for the X axis (0.0 to 1.0). y_value : float Scaling factor for the Y axis (0.0 to 1.0). normalized_y_value : float Nomalized scaling factor for the Y axis (0.0 to 1.0). z_value : float Scaling factor for the Z axis (0.0 to 1.0). normalized_z_value : float Nomalized scaling factor for the Z axis (0.0 to 1.0). """ projection_type: bpy.props.EnumProperty( name="Type de Projection", items=[ ("AXONOMETRIC", "Axonometric", "Axonometrics projections", "NONE", 0), ("OBLIQUE", "Oblique", "Obliques projections", "NONE", 1), ], default="AXONOMETRIC", update=camera_type_update, ) # pyright: ignore[reportInvalidTypeForm] axonometric_type: bpy.props.EnumProperty( name="Presets", description="Type of projection for the new axonometric camera", items=[ (key, preset["name"], "Presetting an {} camera".format(preset["name"])) for (key, preset) in CAMERA_SETTINGS.get_preset_menu_items( "AXONOMETRIC" ).items() ] + [("CUSTOM", "Custom", "No camera preselection")], default="AXONOMETRIC_#1", update=camera_type_update, ) # pyright: ignore[reportInvalidTypeForm] oblique_type: bpy.props.EnumProperty( name="Presets", description="Type of projection for the new oblique camera", items=[ (key, preset["name"], "Presetting an {} camera".format(preset["name"])) for (key, preset) in CAMERA_SETTINGS.get_preset_menu_items( "OBLIQUE" ).items() ] + [("CUSTOM", "Custom", "No camera preselection")], default="OBLIQUE_#1", update=camera_type_update, ) # pyright: ignore[reportInvalidTypeForm] alpha: bpy.props.FloatProperty( name="Alpha", description="Rotation of the camera around the vertical axis", default=radians(30), min=0.0, step=1, precision=4, max=pi / 2.0, subtype="ANGLE", update=camera_alpha_updated, ) # pyright: ignore[reportInvalidTypeForm] beta: bpy.props.FloatProperty( name="Beta", description="Altitude from the side. 0° is top view, 90° is side view", default=radians(30), precision=4, min=0, step=1, max=pi / 2.0, subtype="ANGLE", update=camera_beta_updated, ) # pyright: ignore[reportInvalidTypeForm] rotation: bpy.props.FloatProperty( name="Rotation", description="Rotation of the camera around the vertical axis", default=radians(45), min=0, precision=4, max=pi / 2.0, subtype="ANGLE", ) # pyright: ignore[reportInvalidTypeForm] altitude: bpy.props.FloatProperty( name="Altitude", description="Altitude from the side. 0° is top view, 90° is side view", default=radians(35.26), min=0, max=pi / 2.0, precision=4, subtype="ANGLE", ) # pyright: ignore[reportInvalidTypeForm] altitude_oblique: bpy.props.FloatProperty( name="Altitude", precision=4, subtype="ANGLE" ) # pyright: ignore[reportInvalidTypeForm] is_updating: bpy.props.BoolProperty(default=False) # pyright: ignore[reportInvalidTypeForm] x_value: bpy.props.FloatProperty( name="X axis", description="Size of x axis", default=0.82, min=0.01, max=1 ) # pyright: ignore[reportInvalidTypeForm] normalized_x_value: bpy.props.FloatProperty( name="X axis", description="Size of x axis", default=1, min=0.01, max=1, ) # pyright: ignore[reportInvalidTypeForm] y_value: bpy.props.FloatProperty( name="Y axis", description="Size of y axis", default=0.82, min=0.01, max=1 ) # pyright: ignore[reportInvalidTypeForm] normalized_y_value: bpy.props.FloatProperty( name="Y axis", description="Size of y axis", default=1, min=0.01, max=1, ) # pyright: ignore[reportInvalidTypeForm] z_value: bpy.props.FloatProperty( name="Z axis", description="Size of z axis", default=0.82, step=1, precision=2, soft_min=0.01, soft_max=1, update=camera_z_value_updated, ) # pyright: ignore[reportInvalidTypeForm] normalized_z_value: bpy.props.FloatProperty( name="Z axis", description="Size of z axis", default=1, soft_min=0.01, soft_max=1, ) # pyright: ignore[reportInvalidTypeForm]
# ------------------------------------------------------------------- # REGISTRATION # ------------------------------------------------------------------- _classes = (Pohlke_PT_CameraProperties,) _register, _unregister = bpy.utils.register_classes_factory(_classes)
[docs] def register() -> None: """Register the property group and attach it to the Scene type.""" _register() bpy.types.Scene.pohlke_camera_props = bpy.props.PointerProperty( type=Pohlke_PT_CameraProperties )
[docs] def unregister() -> None: """Unregister the property group and remove it from the Scene type.""" _unregister() del bpy.types.Scene.pohlke_camera_props