| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904 |
- import bpy
- from bpy.types import Operator
- from mathutils import Vector
- from .utilities import (prRed, prGreen, prPurple, prWhite,
- prOrange,
- wrapRed, wrapGreen, wrapPurple, wrapWhite,
- wrapOrange,)
- def TellClasses():
- return [
- MantisGroupNodes,
- MantisEditGroup,
- ExecuteNodeTree,
- # CreateMetaGroup,
- QueryNodeSockets,
- ForceDisplayUpdate,
- CleanUpNodeGraph,
- MantisMuteNode,
- MantisVisualizeOutput,
- TestOperator,
- # xForm
- AddCustomProperty,
- EditCustomProperty,
- RemoveCustomProperty,
- # Fcurve
- # EditFCurveNode,
- FcurveAddKeyframeInput,
- FcurveRemoveKeyframeInput,
- # Driver
- DriverAddDriverVariableInput,
- DriverRemoveDriverVariableInput,
- # Armature Link Node
- LinkArmatureAddTargetInput,
- LinkArmatureRemoveTargetInput,]
- # ExportNodeTreeToJSON,]
- def mantis_tree_poll_op(context):
- # return True
- space = context.space_data
- if hasattr(space, "node_tree"):
- if (space.node_tree):
- return (space.tree_type == "MantisTree")
- return False
- def get_override(active=None, edit=False, selected=[], type='VIEW_3D'):
- #no clue what this does... taken from sorcar
- override = bpy.context.copy()
- if (type == 'VIEW_3D'):
- override["active_object"] = active
- if (edit):
- override["edit_object"] = active
- if (active not in selected):
- selected.append(active)
- override["selected_object"] = selected
- flag = False
- for window in bpy.data.window_managers[0].windows:
- for area in window.screen.areas:
- if area.type == type:
- override["area"] = area
- override["region"] = [i for i in area.regions if i.type == 'WINDOW'][0]
- flag = True
- break
- if (flag):
- break
- return override
- def ChooseNodeGroupNode(treetype):
- #I don't need this anymore... but I'm leaving it here
- # because this is a useful thing to separate
- # in case I add multiple tree types in the future
- if (treetype == "MantisTree"):
- return "MantisNodeGroup"
- # if (treetype == "LinkTree"):
- # return "linkGroupNode"
- #########################################################################3
- class MantisGroupNodes(Operator):
- """Create node-group from selected nodes"""
- bl_idname = "mantis.group_nodes"
- bl_label = "Group Nodes"
- @classmethod
- def poll(cls, context):
- return mantis_tree_poll_op(context)
- def execute(self, context):
- base_tree=context.space_data.path[-1].node_tree
- base_tree.is_exporting = True
- from .i_o import export_to_json, do_import
- from random import random
- grp_name = "".join([chr(int(random()*30)+35) for i in range(20)])
- trees=[base_tree]
- selected_nodes=export_to_json(trees, write_file=False, only_selected=True)
- selected_nodes[base_tree.name][0]["name"]=grp_name
- # this is for debugging the result of the export
- # for k,v in selected_nodes[base_tree.name][2].items():
- # prPurple(k)
- # for k1, v1 in v["sockets"].items():
- # prRed(" ", k1, v1["name"])
- do_import(selected_nodes, context)
- affected_links_in = []
- affected_links_out = []
- for l in base_tree.links:
- if l.from_node.select and not l.to_node.select: affected_links_out.append(l)
- if not l.from_node.select and l.to_node.select: affected_links_in.append(l)
- delete_me = []
- all_nodes_bounding_box=[Vector((float("inf"),float("inf"))), Vector((-float("inf"),-float("inf")))]
- for n in base_tree.nodes:
- if n.select:
- if n.location.x < all_nodes_bounding_box[0].x:
- all_nodes_bounding_box[0].x = n.location.x
- if n.location.y < all_nodes_bounding_box[0].y:
- all_nodes_bounding_box[0].y = n.location.y
- #
- if n.location.x > all_nodes_bounding_box[1].x:
- all_nodes_bounding_box[1].x = n.location.x
- if n.location.y > all_nodes_bounding_box[1].y:
- all_nodes_bounding_box[1].y = n.location.y
- delete_me.append(n)
- grp_node = base_tree.nodes.new('MantisNodeGroup')
- grp_node.node_tree = bpy.data.node_groups[grp_name]
- bb_center = all_nodes_bounding_box[0].lerp(all_nodes_bounding_box[1],0.5)
- for n in grp_node.node_tree.nodes:
- n.location -= bb_center
- grp_node.location = Vector((all_nodes_bounding_box[0].x+200, all_nodes_bounding_box[0].lerp(all_nodes_bounding_box[1], 0.5).y))
- # for l in selected_nodes[base_tree.name][3]:
- # if source := l.get("source"):
- # n_from = base_tree.nodes.get(source[0])
- # # s_from = n_from.
- for n in selected_nodes[base_tree.name][2].values():
- for s in n["sockets"].values():
- if source := s.get("source"):
- prGreen (s["name"], source[0], source[1])
- base_tree_node=base_tree.nodes.get(source[0])
- if s["is_output"]:
- for output in base_tree_node.outputs:
- if output.identifier == source[1]:
- break
- else:
- raise RuntimeError(wrapRed("Socket not found when grouping"))
- base_tree.links.new(input=output, output=grp_node.inputs[s["name"]])
- else:
- for s_input in base_tree_node.inputs:
- if s_input.identifier == source[1]:
- break
- else:
- raise RuntimeError(wrapRed("Socket not found when grouping"))
- base_tree.links.new(input=grp_node.outputs[s["name"]], output=s_input)
- for n in delete_me: base_tree.nodes.remove(n)
- base_tree.nodes.active = grp_node
- base_tree.is_exporting = False
- grp_node.node_tree.name = "Group_Node.000"
- return {'FINISHED'}
- class MantisEditGroup(Operator):
- """Edit the group referenced by the active node (or exit the current node-group)"""
- bl_idname = "mantis.edit_group"
- bl_label = "Edit Group"
- @classmethod
- def poll(cls, context):
- return (
- mantis_tree_poll_op(context)
- )
- def execute(self, context):
- space = context.space_data
- path = space.path
- node = path[len(path)-1].node_tree.nodes.active
- if hasattr(node, "node_tree"):
- if (node.node_tree):
- path.append(node.node_tree, node=node)
- path[0].node_tree.display_update(context)
- return {"FINISHED"}
- elif len(path) > 1:
- path.pop()
- path[0].node_tree.display_update(context)
- # get the active node in the current path
- path[len(path)-1].node_tree.nodes.active.update() # call update to force the node group to check if its tree has changed
- return {"CANCELLED"}
- class ExecuteNodeTree(Operator):
- """Execute this node tree"""
- bl_idname = "mantis.execute_node_tree"
- bl_label = "Execute Node Tree"
- @classmethod
- def poll(cls, context):
- return (mantis_tree_poll_op(context))
- def execute(self, context):
- from time import time
- from .utilities import wrapGreen
-
- tree=context.space_data.path[0].node_tree
-
- import cProfile
- from os import environ
- start_time = time()
- do_profile=False
- print (environ.get("DOPROFILE"))
- if environ.get("DOPROFILE"):
- do_profile=True
- if do_profile:
- # cProfile.runctx("tree.update_tree(context)", None, locals())
- # cProfile.runctx("tree.execute_tree(context)", None, locals())
- # import hunter
- # hunter.trace(stdlib=False, action=hunter.CallPrinter(force_colors=False))
- # tree.update_tree(context)
- # tree.execute_tree(context)
- # return {"FINISHED"}
- import pstats, io
- from pstats import SortKey
- with cProfile.Profile() as pr:
- tree.update_tree(context)
- tree.execute_tree(context)
- # from the Python docs at https://docs.python.org/3/library/profile.html#module-cProfile
- s = io.StringIO()
- sortby = SortKey.TIME
- # sortby = SortKey.CUMULATIVE
- ps = pstats.Stats(pr, stream=s).strip_dirs().sort_stats(sortby)
- ps.print_stats(20) # print the top 20
- print(s.getvalue())
- else:
- tree.update_tree(context)
- tree.execute_tree(context)
- prGreen("Finished executing tree in %f seconds" % (time() - start_time))
- return {"FINISHED"}
- # class CreateMetaGroup(Operator):
- # """Create Meta Rig group node"""
- # bl_idname = "mantis.create_meta_group"
- # bl_label = "Create Meta Rig group node"
- # @classmethod
- # def poll(cls, context):
- # return (mantis_tree_poll_op(context))
- # def execute(self, context):
- # space = context.space_data
- # path = space.path
- # node_tree = space.path[len(path)-1].node_tree
- # # selected_nodes = [i for i in node_tree.nodes if i.select]
- # ob = bpy.context.active_object
- # matrices_build = []
- # if (ob):
- # if (ob.type == 'ARMATURE'):
- # for pb in ob.pose.bones:
- # matrices_build.append((pb.name, pb.matrix, pb.length))
- # xloc = -400
- # yloc = 400
- # loops = 0
- # node_group = bpy.data.node_groups.new(ob.name, space.tree_type)
- # group_node = node_tree.nodes.new("MantisNodeGroup")
- # group_output = node_group.nodes.new("NodeGroupOutput")
- # path.append(node_group, node=group_node)
- # group_node.node_tree = node_group
- # gTree = group_node.node_tree
- # for name, m, length in matrices_build:
- # n = gTree.nodes.new("MetaRigMatrixNode")
- # n.first_row = m[0]
- # n.second_row = m[1]
- # n.third_row = m[2]
- # n.fourth_row = [m[3][0], m[3][1], m[3][2], length]
- # print (n.fourth_row[3])
- # n.name = name
- # n.label = name
- # n.location = (xloc + loops*250, yloc)
- # if (yloc > -800):
- # yloc-=55
- # else:
- # loops+=1
- # yloc = 400
- # node_group.links.new(n.outputs["Matrix"], group_output.inputs[''])
- # node_group.outputs["Matrix"].name = name
- # return {"FINISHED"}
- class QueryNodeSockets(Operator):
- """Utility Operator for querying the data in a socket"""
- bl_idname = "mantis.query_sockets"
- bl_label = "Query Node Sockets"
- @classmethod
- def poll(cls, context):
- return (mantis_tree_poll_op(context))
- def execute(self, context):
- node = context.active_node
- print ("Node type: ", node.bl_idname)
-
- # This is useful. Todo: reimplement this eventually.
-
- return {"FINISHED"}
- class ForceDisplayUpdate(Operator):
- """Utility Operator for querying the data in a socket"""
- bl_idname = "mantis.force_display_update"
- bl_label = "Force Mantis Display Update"
- @classmethod
- def poll(cls, context):
- return (mantis_tree_poll_op(context))
- def execute(self, context):
- base_tree = bpy.context.space_data.path[0].node_tree
- base_tree.display_update(context)
- return {"FINISHED"}
- class CleanUpNodeGraph(bpy.types.Operator):
- """Clean Up Node Graph"""
- bl_idname = "mantis.nodes_cleanup"
- bl_label = "Clean Up Node Graph"
- bl_options = {'REGISTER', 'UNDO'}
- # num_iterations=bpy.props.IntProperty(default=8)
- @classmethod
- def poll(cls, context):
- return hasattr(context, 'active_node')
- def execute(self, context):
- base_tree=context.space_data.path[-1].node_tree
- from .utilities import SugiyamaGraph
- SugiyamaGraph(base_tree, 12)
- return {'FINISHED'}
- class MantisMuteNode(Operator):
- """Mantis Test Operator"""
- bl_idname = "mantis.mute_node"
- bl_label = "Mute Node"
- @classmethod
- def poll(cls, context):
- return (mantis_tree_poll_op(context))
- def execute(self, context):
- path = context.space_data.path
- node = path[len(path)-1].node_tree.nodes.active
- node.mute = not node.mute
- # There should only be one of these
- if (enable := node.inputs.get("Enable")):
- # annoyingly, 'mute' and 'enable' are opposites
- enable.default_value = not node.mute
- if (hide := node.inputs.get("Hide")):
- hide.default_value = node.mute
- return {"FINISHED"}
- class MantisVisualizeOutput(Operator):
- """Mantis Visualize Output Operator"""
- bl_idname = "mantis.visualize_output"
- bl_label = "Visualize Output"
- @classmethod
- def poll(cls, context):
- return (mantis_tree_poll_op(context))
- def execute(self, context):
- from time import time
- from .utilities import wrapGreen, prGreen
-
- tree=context.space_data.path[0].node_tree
- tree.update_tree(context)
- # tree.execute_tree(context)
- prGreen(f"Visualize Tree: {tree.name}")
- nodes = tree.parsed_tree
- from .readtree import visualize_tree
- visualize_tree(nodes, tree, context)
- return {"FINISHED"}
- class TestOperator(Operator):
- """Mantis Test Operator"""
- bl_idname = "mantis.test_operator"
- bl_label = "Mantis Test Operator"
- @classmethod
- def poll(cls, context):
- return (mantis_tree_poll_op(context))
- def execute(self, context):
- path = context.space_data.path
- base_tree = path[0].node_tree
- tree = path[len(path)-1].node_tree
- node = tree.nodes.active
- node.display_update(base_tree.parsed_tree, context)
- # from .base_definitions import get_signature_from_edited_tree
- # if nc := base_tree.parsed_tree.get(get_signature_from_edited_tree(node, context)):
- # from .utilities import get_all_dependencies
- # deps = get_all_dependencies(nc)
- # self.report({'INFO'}, f"Number of Node Dependencies: {len(deps)}")
- # # for n in deps:
- # # prGreen(n)
- # else:
- # # prRed("No NC found in parsed tree.")
- # self.report({'ERROR_INVALID_CONTEXT'}, "No data for node.")
- return {"FINISHED"}
- ePropertyType =(
- ('BOOL' , "Boolean", "Boolean", 0),
- ('INT' , "Integer", "Integer", 1),
- ('FLOAT' , "Float" , "Float" , 2),
- ('VECTOR', "Vector" , "Vector" , 3),
- ('STRING', "String" , "String" , 4),
- #('ENUM' , "Enum" , "Enum" , 5),
- )
-
- from .base_definitions import xFormNode
- class AddCustomProperty(bpy.types.Operator):
- """Add Custom Property to xForm Node"""
- bl_idname = "mantis.add_custom_property"
- bl_label = "Add Custom Property"
- prop_type : bpy.props.EnumProperty(
- items=ePropertyType,
- name="New Property Type",
- description="Type of data for new Property",
- default = 'BOOL',)
- prop_name : bpy.props.StringProperty(default='Prop')
-
- min:bpy.props.FloatProperty(default = 0)
- max:bpy.props.FloatProperty(default = 1)
- soft_min:bpy.props.FloatProperty(default = 0)
- soft_max:bpy.props.FloatProperty(default = 1)
- description:bpy.props.StringProperty(default = "")
-
- node_invoked : bpy.props.PointerProperty(type=bpy.types.Node,
- options ={'HIDDEN'}) # note this seems to affect all
- # subsequent properties
- @classmethod
- def poll(cls, context):
- return True #( hasattr(context, 'node') )
- def invoke(self, context, event):
- self.node_invoked = context.node
- wm = context.window_manager
- return wm.invoke_props_dialog(self)
-
- def execute(self, context):
- n = self.node_invoked
- # For whatever reason, context.node doesn't exist anymore
- # (probably because I use a window to execute)
- # so as a sort of dumb workaround I am saving it to a hidden
- # property of the operator... it works.
- socktype = ''
- if not (self.prop_name):
- self.report({'ERROR_INVALID_INPUT'}, "Must name the property.")
- return {'CANCELLED'}
- if self.prop_type == 'BOOL':
- socktype = 'ParameterBoolSocket'
- if self.prop_type == 'INT':
- socktype = 'ParameterIntSocket'
- if self.prop_type == 'FLOAT':
- socktype = 'ParameterFloatSocket'
- if self.prop_type == 'VECTOR':
- socktype = 'ParameterVectorSocket'
- if self.prop_type == 'STRING':
- socktype = 'ParameterStringSocket'
- #if self.prop_type == 'ENUM':
- # sock_type = 'ParameterStringSocket'
- if (s := n.inputs.get(self.prop_name)):
- try:
- number = int(self.prop_name[-3:])
- # see if it has a number
- number+=1
- self.prop_name = self.prop_name[:-3] + str(number).zfill(3)
- except ValueError:
- self.prop_name+='.001'
- # WRONG # HACK # TODO # BUG #
- new_prop = n.inputs.new( socktype, self.prop_name)
- if self.prop_type in ['INT','FLOAT']:
- new_prop.min = self.min
- new_prop.max = self.max
- new_prop.soft_min = self.soft_min
- new_prop.soft_max = self.soft_max
- new_prop.description = self.description
- # now do the output
- n.outputs.new( socktype, self.prop_name)
-
- return {'FINISHED'}
- #DOESN'T WORK YET
- class EditCustomProperty(bpy.types.Operator):
- """Edit Custom Property"""
- bl_idname = "mantis.edit_custom_property"
- bl_label = "Edit Custom Property"
- prop_type : bpy.props.EnumProperty(
- items=ePropertyType,
- name="New Property Type",
- description="Type of data for new Property",
- default = 'BOOL',)
- prop_name : bpy.props.StringProperty(default='Prop')
-
- min:bpy.props.FloatProperty(default = 0)
- max:bpy.props.FloatProperty(default = 1)
- soft_min:bpy.props.FloatProperty(default = 0)
- soft_max:bpy.props.FloatProperty(default = 1)
- description:bpy.props.StringProperty(default = "") # TODO: use getters to fill these automatically
-
- node_invoked : bpy.props.PointerProperty(type=bpy.types.Node,
- options ={'HIDDEN'}) # note this seems to affect all
- # subsequent properties
- @classmethod
- def poll(cls, context):
- return True #( hasattr(context, 'node') )
- def invoke(self, context, event):
- self.node_invoked = context.node
- wm = context.window_manager
- return wm.invoke_props_dialog(self)
-
- def execute(self, context):
- n = self.node_invoked
- prop = n.inputs.get( self.prop_name )
- if (s := n.inputs.get(self.prop_name)):
- if self.prop_type in ['INT','FLOAT']:
- new_prop.min = self.min
- new_prop.max = self.max
- new_prop.soft_min = self.soft_min
- new_prop.soft_max = self.soft_max
- new_prop.description = self.description
-
- return {'FINISHED'}
- class RemoveCustomProperty(bpy.types.Operator):
- """Remove a Custom Property from an xForm Node"""
- bl_idname = "mantis.remove_custom_property"
- bl_label = "Remove Custom Property"
- def get_existing_custom_properties(self, context):
- ret = []; i = -1
- n = context.active_node
- for inp in n.inputs:
- if 'Parameter' in inp.bl_idname:
- ret.append( (inp.identifier, inp.name, "Parameter to remove", i := i + 1), )
- if ret:
- return ret
- return None
-
- prop_remove : bpy.props.EnumProperty(
- items=get_existing_custom_properties,
- name="Property to remove?",
- description="Select which property to remove",)
- node_invoked : bpy.props.PointerProperty(type=bpy.types.Node,
- options ={'HIDDEN'}) # note this seems to affect all
- # subsequent properties
- @classmethod
- def poll(cls, context):
- return True #(hasattr(context, 'active_node') )
- def invoke(self, context, event):
- print (context.node)
- self.node_invoked = context.node
- t = context.node.id_data
- # HACK the props dialog makes this necesary
- # because context.node only exists during the event that
- # was created by clicking on the node.
- t.nodes.active = context.node # HACK
- context.node.select = True # HACK
- # I need this bc of the callback for the enum property.
- # for whatever reason I can't use node_invoked there
- wm = context.window_manager
- return wm.invoke_props_dialog(self)
-
- def execute(self, context):
- n = self.node_invoked
- # For whatever reason, context.node doesn't exist anymore
- # (probably because I use a window to execute)
- # so as a sort of dumb workaround I am saving it to a hidden
- # property of the operator... it works.
- for i, inp in enumerate(n.inputs):
- if inp.identifier == self.prop_remove:
- break
- else:
- self.report({'ERROR'}, "Input not found")
- raise RuntimeError("This should not happen!")
- # it's possible that the output property's identifier isn't the
- # exact same... but I don' care. Shouldn't ever happen. TODO
- for j, out in enumerate(n.outputs):
- if out.identifier == self.prop_remove:
- break
- else:
- self.report({'ERROR'}, "Output not found")
- raise RuntimeError("This should not happen!")
- n.inputs.remove ( n.inputs [i] )
- n.outputs.remove( n.outputs[j] )
- return {'FINISHED'}
- # TODO: not a priority
- # This one will remove the old socket and add a new one
- # and it'll put it back in place and reconnect the links
- # It's OK to just ask the user to do this manually for now
- #
- # class EditCustomProperty(bpy.types.Operator):
- # """Edit Custom Property in xForm Node"""
- # bl_idname = "mantis.edit_custom_property"
- # bl_label = "Edit Custom Property"
- # prop_type : bpy.props.EnumProperty(
- # items=ePropertyType,
- # name="New Property Type",
- # description="Type of data for new Property",
- # default = 'BOOL',)
- # prop_name : bpy.props.StringProperty(default='Prop')
-
- # min:bpy.props.FloatProperty(default = 0)
- # max:bpy.props.FloatProperty(default = 1)
- # soft_min:bpy.props.FloatProperty(default = 0)
- # soft_max:bpy.props.FloatProperty(default = 1)
- # description:bpy.props.StringProperty(default = "")
-
- # node_invoked : bpy.props.PointerProperty(type=bpy.types.Node,
- # options ={'HIDDEN'}) # note this seems to affect all
- # # subsequent properties
- # @classmethod
- # def poll(cls, context):
- # return True #( hasattr(context, 'node') )
- # def invoke(self, context, event):
- # print (context.node)
- # self.node_invoked = context.node
- # print(dir(self))
- # wm = context.window_manager
- # return wm.invoke_props_dialog(self)
-
- # def execute(self, context):
- # n = self.node_invoked
- # # For whatever reason, context.node doesn't exist anymore
- # # (probably because I use a window to execute)
- # # so as a sort of dumb workaround I am saving it to a hidden
- # # property of the operator... it works.
- # socktype = ''
- # if not (self.prop_name):
- # self.report({'ERROR_INVALID_INPUT'}, "Must name the property.")
- # return {'CANCELLED'}
- # if self.prop_type == 'BOOL':
- # socktype = 'ParameterBoolSocket'
- # if self.prop_type == 'INT':
- # socktype = 'ParameterIntSocket'
- # if self.prop_type == 'FLOAT':
- # socktype = 'ParameterFloatSocket'
- # if self.prop_type == 'VECTOR':
- # socktype = 'ParameterVectorSocket'
- # if self.prop_type == 'STRING':
- # socktype = 'ParameterStringSocket'
- # #if self.prop_type == 'ENUM':
- # # sock_type = 'ParameterStringSocket'
- # if (s := n.inputs.get(self.prop_name)):
- # try:
- # number = int(self.prop_name[-3:])
- # # see if it has a number
- # number+=1
- # self.prop_name = self.prop_name[:-3] + str(number).zfill(3)
- # except ValueError:
- # self.prop_name+='.001'
- # new_prop = n.inputs.new( socktype, self.prop_name)
- # if self.prop_type in ['INT','FLOAT']:
- # new_prop.min = self.min
- # new_prop.max = self.max
- # new_prop.soft_min = self.soft_min
- # new_prop.soft_max = self.soft_max
- # new_prop.description = self.description
-
-
- # return {'FINISHED'}
- class EditFCurveNode(bpy.types.Operator):
- """Edit the fCurve owned by fCurve node"""
- bl_idname = "mantis.edit_fcurve_node"
- bl_label = "Edit fCurve"
- bl_options = {'INTERNAL'}
-
- my_window : bpy.props.StringProperty(default = "-1")
- node_invoked : bpy.props.PointerProperty(type=bpy.types.Node,
- options ={'HIDDEN'}) # note this seems to affect all
- # subsequent properties
- fake_fcurve_ob: bpy.props.PointerProperty(
- type=bpy.types.Object,
- options ={'HIDDEN'},)
- prev_active: bpy.props.PointerProperty(
- type=bpy.types.Object,
- options ={'HIDDEN'},)
-
- @classmethod
- def poll(cls, context):
- return True #(hasattr(context, 'active_node') )
- def modal(self, context, event):
- for w in context.window_manager.windows:
- if str(w.as_pointer()) == self.my_window:
- break
- else:
- context.scene.collection.objects.unlink( self.fake_fcurve_ob )
- context.view_layer.objects.active = self.prev_active
- self.prev_active.select_set(True)
- # at this point I will push the fcurve to nodes
- # or some kind of internal data
- return {'FINISHED'}
- # I can't currently think of anything I need to do with w
- return {'PASS_THROUGH'}
-
-
- def invoke(self, context, event):
- self.node_invoked = context.node
- self.fake_fcurve_ob = self.node_invoked.fake_fcurve_ob
- context.scene.collection.objects.link( self.fake_fcurve_ob )
- self.prev_active = context.view_layer.objects.active
- context.view_layer.objects.active = self.fake_fcurve_ob
- self.fake_fcurve_ob.select_set(True)
- context.window_manager.modal_handler_add(self)
- # this is added to the active window.
- if (self.my_window == "-1"):
- prev_windows = set()
- for w in context.window_manager.windows:
- prev_windows.add(w.as_pointer())
- bpy.ops.wm.window_new()
- for w in context.window_manager.windows:
- w_int = w.as_pointer()
- if (w_int not in prev_windows):
- self.my_window = str(w_int)
- break
- else:
- print ("cancelled")
- return {'CANCELLED'}
- # set up properties for w
- # w.height = 256 # READ
- # w.width = 400 # ONLY
- w.screen.areas[0].type = 'GRAPH_EDITOR'
- w.screen.areas[0].spaces[0].auto_snap = 'NONE'
-
- return {'RUNNING_MODAL'}
- # SIMPLE node operators...
- # May rewrite these in a more generic way later
- class FcurveAddKeyframeInput(bpy.types.Operator):
- """Add a keyframe input to the fCurve node"""
- bl_idname = "mantis.fcurve_node_add_kf"
- bl_label = "Add Keyframe"
- bl_options = {'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return (hasattr(context, 'active_node') )
- def execute(self, context):
- num_keys = len( context.node.inputs)
- context.node.inputs.new("KeyframeSocket", "Keyframe."+str(num_keys).zfill(3))
- return {'FINISHED'}
- class FcurveRemoveKeyframeInput(bpy.types.Operator):
- """Remove a keyframe input from the fCurve node"""
- bl_idname = "mantis.fcurve_node_remove_kf"
- bl_label = "Remove Keyframe"
- bl_options = {'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return (hasattr(context, 'active_node') )
- def execute(self, context):
- n = context.node
- n.inputs.remove(n.inputs[-1])
- return {'FINISHED'}
- class DriverAddDriverVariableInput(bpy.types.Operator):
- """Add a Driver Variable input to the Driver node"""
- bl_idname = "mantis.driver_node_add_variable"
- bl_label = "Add Driver Variable"
- bl_options = {'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return (hasattr(context, 'active_node') )
- def execute(self, context): # unicode for 'a'
- i = len (context.node.inputs) - 2 + 96
- context.node.inputs.new("DriverVariableSocket", chr(i))
- return {'FINISHED'}
- class DriverRemoveDriverVariableInput(bpy.types.Operator):
- """Remove a DriverVariable input from the active Driver node"""
- bl_idname = "mantis.driver_node_remove_variable"
- bl_label = "Remove Driver Variable"
- bl_options = {'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return (hasattr(context, 'active_node') )
- def execute(self, context):
- n = context.node
- n.inputs.remove(n.inputs[-1])
- return {'FINISHED'}
-
-
-
- class LinkArmatureAddTargetInput(bpy.types.Operator):
- """Add a Driver Variable input to the Driver node"""
- bl_idname = "mantis.link_armature_node_add_target"
- bl_label = "Add Target"
- bl_options = {'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return hasattr(context, 'node')
- def execute(self, context): # unicode for 'a'
- num_targets = len( list(context.node.inputs)[6:])//2
- context.node.inputs.new("xFormSocket", "Target."+str(num_targets).zfill(3))
- context.node.inputs.new("FloatFactorSocket", "Weight."+str(num_targets).zfill(3))
- return {'FINISHED'}
- class LinkArmatureRemoveTargetInput(bpy.types.Operator):
- """Remove a DriverVariable input from the active Driver node"""
- bl_idname = "mantis.link_armature_node_remove_target"
- bl_label = "Remove Target"
- bl_options = {'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return hasattr(context, 'node')
- def execute(self, context):
- n = context.node
- n.inputs.remove(n.inputs[-1]); n.inputs.remove(n.inputs[-1])
- return {'FINISHED'}
- # class ExportNodeTreeToJSON(Operator):
- # """Export this node tree as a JSON file"""
- # bl_idname = "mantis.export_node_tree_json"
- # bl_label = "Export Mantis Tree to JSON"
- # @classmethod
- # def poll(cls, context):
- # return (mantis_tree_poll_op(context))
- # def execute(self, context):
- # from .i_o import export_to_json
- # import bpy
- # tree = context.space_data.path[0].node_tree
- # # tree.update_tree(context)
- # trees = {tree}
- # check_trees=[tree]
- # while check_trees:
- # check = check_trees.pop()
- # for n in check.nodes:
- # if hasattr(n, "node_tree"):
- # if n.node_tree not in trees:
- # check_trees.append(n.node_tree)
- # trees.add(n.node_tree)
-
- # def remove_special_characters(stritree):
- # # https://stackoverflow.com/questions/295135/turn-a-stritree-into-a-valid-filename
- # # thank you user "Sophie Gage"
- # import re # regular expressions
- # return re.sub('[^\w_.)( -]', '', stritree)
- # path = bpy.path.abspath('//')+remove_special_characters(tree.name)+".json"
- # export_to_json(trees, path)
- # return {"FINISHED"}
|