# 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 . 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)