184 lines
6.4 KiB
Python
184 lines
6.4 KiB
Python
|
# SlideTools: Tools for generating slides
|
||
|
# Copyright (C) 2022 Spencer Killen
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful, but
|
||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
# General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
from functools import partial
|
||
|
from typing import Collection
|
||
|
|
||
|
import bpy
|
||
|
from bpy.types import ID, Curve, Operator, Context, Event, VIEW3D_MT_object, Object
|
||
|
from bpy.props import StringProperty, FloatProperty, FloatVectorProperty
|
||
|
|
||
|
from .playground import DEFAULT_SLIDE_BLEND_PATH, ObjectData, Slide, PARTS, ObjectData
|
||
|
|
||
|
|
||
|
class CreateSlideHelper:
|
||
|
bl_idname = "object.createslide"
|
||
|
bl_label = "Create Slide"
|
||
|
bl_options = {"UNDO", "REGISTER"}
|
||
|
|
||
|
blend_file: StringProperty(
|
||
|
name="Blend File",
|
||
|
description=".blend file to use as a slide template",
|
||
|
default=DEFAULT_SLIDE_BLEND_PATH,
|
||
|
)
|
||
|
|
||
|
radius: FloatProperty(name="Radius", description="Big the slide bigger")
|
||
|
thickness: FloatProperty(name="Thickness", description="Make the slide thicker")
|
||
|
body_color: FloatVectorProperty(
|
||
|
name="Body Color", description="Change the main color of the slide"
|
||
|
)
|
||
|
secondary_color: FloatVectorProperty(
|
||
|
name="Secondary Color", description="Change the secondary color of the slide"
|
||
|
)
|
||
|
|
||
|
@classmethod
|
||
|
def poll(cls, context: Context) -> bool:
|
||
|
return True
|
||
|
|
||
|
def _invoke_helper(self, context: Context, event: Event, slide=None):
|
||
|
if slide is None:
|
||
|
slide = Slide(self.blend_file)
|
||
|
self.radius = slide.radius
|
||
|
self.thickness = slide.thickness
|
||
|
self.body_color = slide.body_color
|
||
|
self.secondary_color = slide.secondary_color
|
||
|
return slide
|
||
|
|
||
|
def _execute_helper(self, context: Context, slide=None):
|
||
|
if slide is None:
|
||
|
slide = Slide(self.blend_file)
|
||
|
slide.radius = self.radius
|
||
|
slide.thickness = self.thickness
|
||
|
slide.body_color = self.body_color
|
||
|
slide.secondary_color = self.secondary_color
|
||
|
return slide
|
||
|
|
||
|
|
||
|
class CreateSlideOperator(Operator, CreateSlideHelper):
|
||
|
def invoke(self, context: Context, event: Event):
|
||
|
self._invoke_helper(context, event, slide=None)
|
||
|
return {"FINISHED"}
|
||
|
|
||
|
def execute(self, context: Context):
|
||
|
self._execute_helper(context, slide=None)
|
||
|
return {"FINISHED"}
|
||
|
|
||
|
|
||
|
def slide_collection_from_part(selected: ID):
|
||
|
if type(selected) is Collection:
|
||
|
return selected
|
||
|
if any(part in selected.name.lower() for part in PARTS):
|
||
|
for collection in bpy.data.collections:
|
||
|
if any(o == selected for o in collection.objects):
|
||
|
return collection
|
||
|
return None
|
||
|
|
||
|
|
||
|
class EditSlideOperator(Operator, CreateSlideHelper):
|
||
|
bl_idname = "object.editslide"
|
||
|
bl_label = "Edit Slide"
|
||
|
bl_options = {"UNDO", "REGISTER"}
|
||
|
|
||
|
POLL_CANT_MESSAGE = "Operator requires a slide collection or part to be selected"
|
||
|
|
||
|
blend_file: None = None
|
||
|
|
||
|
@staticmethod
|
||
|
def get_selection(context: Context):
|
||
|
if not hasattr(context, "selected_ids"):
|
||
|
return context.selected_ids[0] if len(context.selected_ids) == 1 else None
|
||
|
elif hasattr(context, "active_object"):
|
||
|
return context.active_object
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
@classmethod
|
||
|
def poll(cls, context: Context) -> bool:
|
||
|
return cls.get_selection(context) is not None
|
||
|
|
||
|
def invoke(self, context: Context, event: Event):
|
||
|
if not EditSlideOperator.poll(context):
|
||
|
self.report(EditSlideOperator.POLL_CANT_MESSAGE)
|
||
|
sel = EditSlideOperator.get_selection(context)
|
||
|
collection = slide_collection_from_part(sel)
|
||
|
slide = Slide.rehydrate(collection)
|
||
|
super()._invoke_helper(context, event, slide)
|
||
|
return {"FINISHED"}
|
||
|
|
||
|
def execute(self, context: Context):
|
||
|
if not EditSlideOperator.poll(context):
|
||
|
self.report(EditSlideOperator.POLL_CANT_MESSAGE)
|
||
|
sel = EditSlideOperator.get_selection(context)
|
||
|
collection = slide_collection_from_part(sel)
|
||
|
slide = Slide.rehydrate(collection)
|
||
|
super()._execute_helper(context, slide)
|
||
|
return {"FINISHED"}
|
||
|
|
||
|
|
||
|
class CreateSlideFromCurveOperator(Operator, CreateSlideHelper):
|
||
|
bl_idname = "object.createslidefromcurve"
|
||
|
bl_label = "Create Slide From Curve"
|
||
|
bl_options = {"UNDO", "REGISTER"}
|
||
|
|
||
|
POLL_CANT_MESSAGE = "Operator requires a single curve to be selected"
|
||
|
|
||
|
@staticmethod
|
||
|
def get_selection(context: Context) -> ObjectData[Curve]:
|
||
|
if hasattr(context, "selected_objects"):
|
||
|
if len(context.selected_objects) == 1:
|
||
|
sel: Object = context.selected_objects[0]
|
||
|
if type(sel.data) is Curve:
|
||
|
sel: ObjectData[Curve]
|
||
|
return sel
|
||
|
return None
|
||
|
|
||
|
@classmethod
|
||
|
def poll(cls, context: Context) -> bool:
|
||
|
return cls.get_selection(context) is not None
|
||
|
|
||
|
def invoke(self, context: Context, event: Event):
|
||
|
if not CreateSlideFromCurveOperator.poll(context):
|
||
|
self.report(CreateSlideFromCurveOperator.POLL_CANT_MESSAGE)
|
||
|
curve = CreateSlideFromCurveOperator.get_selection(context)
|
||
|
slide = Slide.from_curve(curve, self.blend_file)
|
||
|
super()._invoke_helper(context, event, slide)
|
||
|
return {"FINISHED"}
|
||
|
|
||
|
def execute(self, context: Context):
|
||
|
if not CreateSlideFromCurveOperator.poll(context):
|
||
|
self.report(CreateSlideFromCurveOperator.POLL_CANT_MESSAGE)
|
||
|
curve = CreateSlideFromCurveOperator.get_selection(context)
|
||
|
slide = Slide.from_curve(curve, self.blend_file)
|
||
|
super()._execute_helper(context, slide)
|
||
|
return {"FINISHED"}
|
||
|
|
||
|
|
||
|
def menu_func(bl_idname: str, self, context: Context):
|
||
|
self.layout.operator(bl_idname)
|
||
|
|
||
|
|
||
|
ops = (CreateSlideOperator, EditSlideOperator, CreateSlideFromCurveOperator)
|
||
|
menu_funcs = tuple(partial(menu_func, op.bl_idname) for op in ops)
|
||
|
|
||
|
|
||
|
def register():
|
||
|
for f in menu_funcs:
|
||
|
VIEW3D_MT_object.append(f)
|
||
|
|
||
|
|
||
|
def unregister():
|
||
|
for f in menu_funcs:
|
||
|
VIEW3D_MT_object.remove(f)
|