Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@jedypod
Last active October 12, 2023 06:31
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jedypod/98dc18acd8008e7e5cbe to your computer and use it in GitHub Desktop.
Save jedypod/98dc18acd8008e7e5cbe to your computer and use it in GitHub Desktop.
PlanarProjection Planar Projection Generates 2D coordinates for points in 3D space. Works on 4 points at once, is instantaneous to calculate, and generates a 4x4 transform matrix for use in rotos.
set cut_paste_input [stack 0]
push $cut_paste_input
Group {
name PlanarProjection
help "<b>Planar Projection</b>\nGenerates 2D coordinates for points in 3D space. Type in 3D point coordinates, or use vertex selection in 3D viewer and click set to pick average of selected points, or set points to set all four points at once. \n\nYou can connect node output to scene together with your pointcloud or geometry and see where your points are located in 3d space. Double click any of them to move it in 3d space like any traditional nuke transform control. \n\nA matrix transform is also generated to be used with RotoPaint, SplineWarp and GridWarp nodes. If you are using matrix in GridWarp, points have to be in clockwise order, pick them one by one! \n\nCommand set points doesn't respect selection order! \n\nCheck out the demo video on my website! Kudos to Ivan Busquets for help with matrix math. \n\n-- developed by Vit Sedlacek 2012 www.vitsedlacek.com \n\n-- Modified by Jed Smith to make calculation time nearly instantaneous, fix some bugs and create a BG input so that the sampled format could be easily specified instead of using nuke.root() format. Also enabled animated 3d input points, and added some functionality in the set knobs to snarf data from axis nodes.\nhttp://gist.github.com/jedypod"
addUserKnob {20 PlanarProjection}
addUserKnob {26 ""}
addUserKnob {26 usage_label_header l " " T "<font size=4>\n<b>Input 3D Positions</b>"}
addUserKnob {20 usage_label_grp l " help" n 1}
usage_label_grp 0
addUserKnob {26 usage_label l " " T "<font size=3>\nSelect any number of points in the 3D view, in vertex selection mode.<br/>\n<b>Set from Selection</b>: 4 points will be set based on the selection bounds.<br/>\n<b>Set from Axes</b>: Gets position from 4 axis nodes with or without animation.<br/>\n<b>Set Animated</b>: Set the target point. For animated geometry.<br/>\n<b>Set Point</b>: Set point from vertex selection. If no vertex selection,<br/>\n set from selected Axis. If no axis, set from selected pixel value (for PWorld).<br/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"}
addUserKnob {20 endGroup n -1}
addUserKnob {22 set_from_selection l "Set from Selection" t "Set value of all points If Axis nodes are selected, it grabs translate values from them. If not, the first 4 selected vertexes in the 3D viewport is used instead. " T "## Set points to selected\nfrom __future__ import with_statement\nfrom nukescripts import snap3d as sn\n\n\nnode = nuke.thisNode()\nnode.begin()\nmatch_sel = nuke.toNode('_MATCH_SELECTION_')\n\nsn.translateRotateScaleToPoints(match_sel)\n\naxes = \[nuke.toNode('LL'), nuke.toNode('LR'), nuke.toNode('UR'), nuke.toNode('UL')]\nfor i, axis in enumerate(axes):\n matrix = axis\['world_matrix']\n translate = \[matrix.getValue(3), matrix.getValue(7), matrix.getValue(11)]\n node\['ip\{0\}'.format(i+1)].setValue(translate)" +STARTLINE}
addUserKnob {22 set_from_axes l "Set from Axes" t "Select up to 4 different axes, which can be animated. This button will set the points to each axis in selection order." -STARTLINE T "node = nuke.thisNode()\nnuke.root().begin()\n\naxis_nodes = \[axis for axis in nuke.selectedNodes() if axis.Class() == \"Axis2\"]\nif axis_nodes:\n if len(axis_nodes) > 4:\n nuke.message(\"More than 4 axis nodes selected: Will only use the first 4 selected ones.\")\n axis_nodes = list(reversed(axis_nodes)) # Reverse selected so it goes in selection order\n for i in range(4):\n point = node\[\"ip\{0\}\".format(i+1)]\n axis = axis_nodes\[i]\n if axis\['translate'].isAnimated():\n point.clearAnimated()\n point.copyAnimations(axis\['translate'].animations())\n else:\n point.clearAnimated()\n point.setValue(axis\['translate'].getValue())"}
addUserKnob {22 clear_input l Clear t "Remove all animated and non-default knob values." -STARTLINE T "n = nuke.thisNode()\n\nfor i in range(4):\n kip = n\['ip'+str(i+1)]\n kip.clearAnimated()\n kip.setValue(kip.defaultValue())"}
addUserKnob {22 set_single_animated l "Set Animated" t "Sets single point to selected vertex position. Set the target knob to put the data on." T "import nuke\nfrom nukescripts import snap3d as sn\n\ndef _get_framerange():\n first_frame = int(nuke.numvalue('root.first_frame'))\n last_frame = int(nuke.numvalue('root.last_frame'))\n step = 1\n default_frange = str(nuke.FrameRange(first_frame, last_frame, step))\n frange = nuke.getInput('Enter Frame Range:', default_frange)\n if not frange:\n return None\n else:\n try:\n return nuke.FrameRange(frange)\n except:\n nuke.message('Invalid frame range')\n return None\n\ndef get_vertex_position(frame):\n # return last selected vertex position on specified frame\n vertices = sn.getSelection()\n if not vertices:\n nuke.message(\"A vertex must be selected.\")\n return\n center = sn.calcAveragePosition(vertices)\n vertices.translate(-center)\n return center\n\ndef calc_vertex_positions():\n framerange = _get_framerange()\n if not framerange:\n return\n curvetool = nuke.nodes.CurveTool()\n positions_animcurve = \[\[], \[], \[]]\n for frame in framerange:\n nuke.execute(curvetool, frame, frame)\n curpos = get_vertex_position(frame)\n if curpos:\n for i in range(3):\n positions_animcurve\[i].append(nuke.AnimationKey(frame, curpos\[i]))\n if curvetool:\n nuke.delete(curvetool)\n return positions_animcurve\n\nnode = nuke.thisNode()\nnuke.root().begin()\niptarget = node\['iptarget'].getValue()\nipknob = node\['ip\{0\}'.format(int(iptarget)+1)]\nipknob.clearAnimated()\nipknob.setAnimated()\n\npositions = calc_vertex_positions()\nfor index, curve in enumerate(ipknob.animations()):\n curve.addKey(positions\[index])" +STARTLINE}
addUserKnob {22 set_point l "Set Point" -STARTLINE T "import nuke\nfrom nukescripts import snap3d as sn\n\ndef get_vertex_position():\n points = sn.getSelection()\n if not points:\n return None\n pos = sn.calcAveragePosition(points)\n return pos\n\nif __name__==\"__main__\":\n node = nuke.thisNode()\n nuke.root().begin()\n axes = \['Axis', 'Axis2', 'Axis3']\n axis_nodes = \[axis for axis in nuke.selectedNodes() if axis.Class() in axes]\n iptarget = node\['iptarget'].getValue()\n ipknob = node\['ip\{0\}'.format(int(iptarget)+1)]\n pos = get_vertex_position()\n if pos:\n ipknob.setValue(pos)\n elif axis_nodes:\n axis = axis_nodes\[-1]\n if axis\['translate'].isAnimated():\n ipknob.clearAnimated()\n ipknob.copyAnimations(axis\['translate'].animations())\n else:\n ipknob.clearAnimated()\n ipknob.setValue(axis\['translate'].getValue())\n else:\n viewer = nuke.activeViewer().node()\n if viewer.input(0):\n viewed_node = viewer.input(0)\n if not nuke.selectedNodes():\n viewed_node.setSelected(1)\n else:\n viewed_node = nuke.activeViewer().node()\n bboxinfo = nuke.activeViewer().node()\['colour_sample_bbox'].value()\n aspect = float(viewed_node.width() * viewed_node.pixelAspect()) / float(viewed_node.height())\n cornerA = \[(bboxinfo\[0]*0.5+0.5) * viewed_node.width(), (((bboxinfo\[1] * 0.5) + (0.5/aspect)) * aspect) * viewed_node.height()]\n cornerB = \[(bboxinfo\[2]*0.5+0.5) * viewed_node.width(), (((bboxinfo\[3] * 0.5) + (0.5/aspect)) * aspect) * viewed_node.height()]\n area = \[cornerB\[0] - cornerA\[0], cornerB\[1] - cornerA\[1]]\n center = \[cornerA\[0] + (area\[0]/2), cornerA\[1] + (area\[1] / 2)]\n color_sample = \[viewed_node.sample('rgba.red', center\[0], center\[1], area\[0], area\[1]), viewed_node.sample('rgba.green', center\[0], center\[1], area\[0], area\[1]), viewed_node.sample('rgba.blue', center\[0], center\[1], area\[0], area\[1])]\n ipknob.setValue(color_sample)"}
addUserKnob {4 iptarget l "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target" -STARTLINE M {"point 1" "point 2" "point 3" "point 4" "" ""}}
iptarget "point 2"
addUserKnob {41 ip1 l "point 1" T point_1.translate}
addUserKnob {22 set_ip1 l "Set 1" t "Set point 1 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(0)\nnode\['set_point'].execute()"}
addUserKnob {41 ip2 l "point 2" T point_2.translate}
addUserKnob {22 set_ip2 l "Set 2" t "Set point 2 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(1)\nnode\['set_point'].execute()"}
addUserKnob {41 ip3 l "point 3" T point_3.translate}
addUserKnob {22 set_ip3 l "Set 3" t "Set point 3 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(2)\nnode\['set_point'].execute()"}
addUserKnob {41 ip4 l "point 4" T point_4.translate}
addUserKnob {22 set_ip4 l "Set 4" t "Set point 4 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(3)\nnode\['set_point'].execute()"}
addUserKnob {26 ""}
addUserKnob {26 reconcile_explanation l " " T "<font size=4><b>Reconcile Points into Screen Space</b>"}
addUserKnob {20 reconcile_points_grp l " help" n 1}
addUserKnob {26 reconcile_points_help l " " T "<font size=3>Press Calculate and the 3D points will be reconciled into 2d tracking data. <br/>\nA perspective transform matrix is also calculated. <br/>This enables tracking in \ntranslation, rotation, scale and perspective.\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"}
addUserKnob {20 endGroup_1 l endGroup n -1}
addUserKnob {22 Calculate t "Calculate the reconciled 2D points for each 3D point.\n\nAlso calculate a matrix transform for the planar surface, if there are 4 total points. This can then be used for Rotos and CornerPins." T "import nuke\nfrom nukescripts import snap3d as sn\nimport math\n\n\n\ndef cameraProjectionMatrix(cameraNode, frame, imageformat):\n ## modified code from nukescripts/Snap3D\n\n\n # Matrix to transform points into camera-relative coords.\n wm = nuke.math.Matrix4()\n for i in range(16):\n wm\[i] = cameraNode\['matrix'].getValueAt(frame,i)\n wm.transpose()\n camTransform = wm.inverse()\n\n\n # Matrix to take the camera projection knobs into account\n roll = float(cameraNode\['winroll'].getValueAt(frame,0))\n scale_x = float(cameraNode\['win_scale'].getValueAt(frame,0))\n scale_y = float(cameraNode\['win_scale'].getValueAt(frame,1))\n translate_x = float(cameraNode\['win_translate'].getValueAt(frame,0))\n translate_y = float(cameraNode\['win_translate'].getValueAt(frame,1))\n m = nuke.math.Matrix4()\n m.makeIdentity()\n m.rotateZ(math.radians(roll))\n m.scale(1.0 / scale_x, 1.0 / scale_y, 1.0)\n m.translate(-translate_x, -translate_y, 0.0)\n\n\n # Projection matrix based on the focal length, aperture and clipping planes of the camera\n focal_length = float(cameraNode\['focal'].getValueAt(frame))\n h_aperture = float(cameraNode\['haperture'].getValueAt(frame))\n near = float(cameraNode\['near'].getValueAt(frame))\n far = float(cameraNode\['far'].getValueAt(frame))\n projection_mode = int(cameraNode\['projection_mode'].getValueAt(frame))\n p = nuke.math.Matrix4()\n p.projection(focal_length / h_aperture, near, far, projection_mode == 0)\n\n\n # Matrix to translate the projected points into normalised pixel coords\n imageAspect = float(imageformat.height()) / float(imageformat.width())\n\n\n t = nuke.math.Matrix4()\n t.makeIdentity()\n t.translate( 1.0, 1.0 - (1.0 - imageAspect / float(imageformat.pixelAspect())), 0.0 )\n\n\n # Matrix to scale normalised pixel coords into actual pixel coords.\n x_scale = float(imageformat.width()) / 2.0\n y_scale = x_scale * imageformat.pixelAspect()\n s = nuke.math.Matrix4()\n s.makeIdentity()\n s.scale(x_scale, y_scale, 1.0)\n\n\n # The projection matrix transforms points into camera coords, modifies based\n # on the camera knob values, projects points into clip coords, translates the\n # clip coords so that they lie in the range 0,0 - 2,2 instead of -1,-1 - 1,1,\n # then scales the clip coords to proper pixel coords.\n return s * t * p * m * camTransform\n\n\n\n\ndef pointsToMatrix(frame, node, reference_frame=None):\n cpToMtx = nuke.math.Matrix4()\n cpFromMtx = nuke.math.Matrix4()\n\n\n # Get a list of Vector2 objects for each of the 'to' knobs and each of the 'from' knobs.\n ToVectors = \[nuke.math.Vector2(node\[f].getValueAt(frame, 0), node\[f].getValueAt(frame, 1)) for f in sorted(node.knobs().keys()) if f.startswith('op')]\n\n\n # Feed all 4 coordinates into the mapUnitSquareToQuad() function\n cpToMtx.mapUnitSquareToQuad(ToVectors\[0].x, ToVectors\[0].y, ToVectors\[1].x, ToVectors\[1].y, ToVectors\[2].x, ToVectors\[2].y, ToVectors\[3].x, ToVectors\[3].y)\n\n\n # If we have a reference frame, use the to vectors on that frame as our from matrix, otherwise use the image width and height\n if reference_frame:\n FromVectors = \[nuke.math.Vector2(node\[f].getValueAt(reference_frame, 0), node\[f].getValueAt(reference_frame, 1)) for f in sorted(node.knobs().keys()) if f.startswith('op')]\n cpFromMtx.mapUnitSquareToQuad(FromVectors\[0].x, FromVectors\[0].y, FromVectors\[1].x, FromVectors\[1].y, FromVectors\[2].x, FromVectors\[2].y, FromVectors\[3].x, FromVectors\[3].y)\n else:\n cpFromMtx.mapUnitSquareToQuad(0, 0, node.width(), 0, node.width(), node.height(), 0, node.height())\n\n\n transform_matrix = cpToMtx * cpFromMtx.inverse()\n\n\n # This is not needed to apply the transformation. The matrix is already correct.\n # However, the matrix knob in rotopaint is indexed differently (row major), so I just transpose the matrix here to make it easier to copy its values to the knob\n transform_matrix.transpose()\n\n\n # Fill in the transform_matrix knob of the target node\n return transform_matrix\n\n\n\n\ndef projectPoints(frame, camera=None, point=None, imageformat=None):\n # Modify projectpoint function in nukescripts.snap3d to add frame argument\n if not imageformat:\n imageformat = nuke.root()\['format'].value()\n camMatrix = cameraProjectionMatrix(camera, frame, imageformat)\n if camMatrix == None:\n raise RuntimeError(\"snap3d.cameraProjectionMatrix() returned None for camera.\")\n\n\n if not ( isinstance(point, list) or isinstance(point, tuple) ):\n raise ValueError(\"Argument point must be a list or tuple.\")\n\n\n for point in point:\n # Would be nice to not do this for every item but since lists/tuples can\n # containg anything...\n if isinstance(point, nuke.math.Vector3):\n pt = point\n elif isinstance(point, list) or isinstance(point, tuple):\n pt = nuke.math.Vector3(point\[0], point\[1], point\[2])\n else:\n raise ValueError(\"All items in point must be nuke.math.Vector3 or list/tuple of 3 floats.\")\n\n\n tPos = camMatrix * nuke.math.Vector4(pt.x, pt.y, pt.z, 1.0)\n #print(tPos)\n try:\n yield nuke.math.Vector2(tPos.x / tPos.w, tPos.y / tPos.w)\n except ZeroDivisionError:\n print(\"Zero Division Error on frame \{0\} with point data \{1\}\".format(frame, point))\n yield nuke.math.Vector2()\n\n\n\ndef calculate(node):\n # Get the input Camera and verify it is right. (Assume camera is topnode of input to handle dots)\n cam_input = node.input(1)\n # Sanity check\n if not (cam_input and isinstance(cam_input, nuke.Node)):\n nuke.message(\"A Camera node must be connected.\")\n return\n if 'Camera' in cam_input.Class():\n cam = cam_input\n else:\n cam = nuke.toNode(nuke.tcl(\"full_name \[topnode %s]\" % cam_input.name()))\n bg = node.input(0)\n if not bg:\n nuke.message(\"BG not connected, so the root format will be used to reconcile the 3D points into screen space.\")\n\n\n # Get framerange to operate on from camera animation curves\n first = None\n last = None\n try:\n if cam\['translate'].isAnimated():\n for curve in cam\['translate'].animations():\n if first == None:\n first = int(curve.keys()\[0].x)\n else:\n first = min(first, int(curve.keys()\[0].x))\n for curve in cam\['translate'].animations():\n if last == None:\n last = int(curve.keys()\[-1].x)\n else:\n last = max(last, int(curve.keys()\[-1].x))\n elif cam\['rotate'].isAnimated():\n for curve in cam\['rotate'].animations():\n if first == None:\n first = int(curve.keys()\[0].x)\n else:\n first = min(first, int(curve.keys()\[0].x))\n for curve in cam\['rotate'].animations():\n if last == None:\n last = int(curve.keys()\[-1].x)\n else:\n last = max(last, int(curve.keys()\[-1].x))\n else:\n nuke.message(\"Input Camera is not animated.\")\n return\n except IndexError:\n print(\"Could not caclucate the framerange base on the keys (Are you using expressions?), using Root framerange...\")\n first = nuke.root().firstFrame()\n last = nuke.root().lastFrame()\n except Exception as e:\n print(e)\n nuke.message(\"Something went wrong getting the camera animation. Using Root framerange...\")\n first = nuke.root().firstFrame()\n last = nuke.root().lastFrame()\n\n\n\n framerange = nuke.FrameRange('\{0\}-\{1\}'.format(first, last))\n\n\n # Loop through the 3D points, reconcile the coordinates through the camera into screen space\n for opnum in range(4):\n # Only run if the ip knob is not default\n ipknob = node\[\"ip\{0\}\".format(opnum+1)]\n if not ipknob.notDefault():\n print(\"Skipping \{0\} because it is still default!\".format('ip'+str(opnum+1)))\n continue\n\n\n opknob = node\[\"op\{0\}\".format(opnum+1)]\n opknob.clearAnimated()\n opknob.setAnimated()\n\n\n # Building the data into a list of AnimationKey objects,\n # and then applying that list to the knob using addKey is significantly faster than other methods.\n # This makes the Calculate button instantaneous instead of taking forever.\n point_animcurve = \[\[], \[]]\n for frame in framerange:\n\n\n #print(\"values are \", ipknob.getValueAt(frame))\n # Sample input point knob on every frame if it's animated or expression-linked\n if ipknob.isAnimated() or ipknob.hasExpression():\n point = next(projectPoints(frame, cam, \[ipknob.getValueAt(frame)], node.format()))\n else:\n point = next(projectPoints(frame, cam, \[ipknob.value()], node.format()))\n for index in range(2):\n point_animcurve\[index].append(nuke.AnimationKey(frame, point\[index]))\n\n\n for index, curve in enumerate(opknob.animations()):\n curve.addKey(point_animcurve\[index])\n\n\n # Calculate the transformation Matrix, if we have 4 output points\n for i in range(4):\n ipknob = node\[\"op\{0\}\".format(i+1)]\n #print('op'+str(i+1)+' IS NOT DEFAULT')\n if not ipknob.notDefault():\n return\n mknob = node\['matrix']\n mknob.clearAnimated()\n mknob.setAnimated()\n\n\n use_reference_frame = True\n reference_frame = node\['identity_frame'].getValue()\n\n\n matrix_animcurve = \[\[] for i in range(16)]\n for frame in framerange:\n if use_reference_frame:\n matrix_data = pointsToMatrix(frame, node, reference_frame)\n else:\n matrix_data = pointsToMatrix(frame, node)\n for index in range(16):\n matrix_animcurve\[index].append(nuke.AnimationKey(frame, matrix_data\[index]))\n matrix_animcurve = \[\[] for i in range(16)]\n for frame in framerange:\n if use_reference_frame:\n matrix_data = pointsToMatrix(frame, node, reference_frame)\n else:\n matrix_data = pointsToMatrix(frame, node)\n for index in range(16):\n matrix_animcurve\[index].append(nuke.AnimationKey(frame, matrix_data\[index]))\n\n\n for index, item in enumerate(mknob.animations()):\n item.addKey(matrix_animcurve\[index])\n for index, item in enumerate(mknob.animations()):\n item.addKey(matrix_animcurve\[index])\n\n\nif __name__==\"__main__\":\n calculate(nuke.thisNode())" +STARTLINE}
addUserKnob {22 clear_out l Clear t "Clear output knobs." -STARTLINE T "n = nuke.thisNode()\n\nfor i in range(4):\n kop = n\['op'+str(i+1)]\n kop.clearAnimated()\n kop.setValue(kop.defaultValue())\n n\['matrix'].clearAnimated()\n n\['matrix'].setValue(\[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])"}
addUserKnob {12 op1 l "point 1"}
addUserKnob {12 op2 l "point 2"}
addUserKnob {12 op3 l "point 3"}
addUserKnob {12 op4 l "point 4"}
addUserKnob {41 matrix t "Calculated matrix." T Matrix_original.matrix}
addUserKnob {26 identity_frame_label l " " T "\nIdentity Frame"}
addUserKnob {3 identity_frame l " " t "If a frame is specified here, the matrix calculated will be relative to this frame. On the reference frame the matrix will be a null transformation."}
identity_frame 1001
addUserKnob {22 set_current l Current -STARTLINE T nuke.thisNode().knob('identity_frame').setValue(nuke.frame())}
addUserKnob {26 ""}
addUserKnob {26 link_tracking_label l " " T "<font size=4><b>Link Tracking Data to Nodes</b>"}
addUserKnob {20 link_tracking_help_grp l " help" n 1}
link_tracking_help_grp 0
addUserKnob {26 link_tracking_help l " " T "<font size=3>\n<b>Expression link</b> will keep a live expression to the data on this node, in case it is updated.\n<br/><b>Independent identity frame</b> will add extra knobs to the created node to allow you to \n<br/>specify an independent identity frame on the node. It's heavier!\n<br/>&nbsp;&nbsp;&nbsp;&nbsp;"}
addUserKnob {20 endGroup_2 l endGroup n -1}
addUserKnob {22 create_tracker3 l Tracker3 t "Create a Tracker3 class Tracker node, in case this is what you prefer to link roto shapes to." T "# Create tracker3 node\nfrom __future__ import with_statement\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\npproj = nuke.thisNode()\nwith nuke.root():\n track_node = nuke.nodes.Tracker3()\n track_node.setXYpos(pproj.xpos()-grid_x*0, pproj.ypos()+grid_y*2)\n _ = \[n.setSelected(False) for n in nuke.allNodes()]\n track_node.setSelected(True)\n enable_knobs = \['enable1', 'enable2', 'enable3', 'enable4']\n trs_knobs = \['use_for1', 'use_for2', 'use_for3', 'use_for4']\n track_knobs = \['track1', 'track2', 'track3', 'track4']\n\n for i, k in enumerate(enable_knobs):\n if pproj\['op\{0\}'.format(i+1)].isAnimated():\n track_node\[k].setValue(True)\n for i, k in enumerate(trs_knobs):\n if pproj\['op\{0\}'.format(i+1)].isAnimated():\n track_node\[k].setValue(7)\n for i, track in enumerate(track_knobs):\n if pproj\['op\{0\}'.format(i+1)].isAnimated():\n tknob = track_node\[track]\n tknob.clearAnimated()\n tknob.setAnimated()\n for x, c in enumerate(tknob.animations()):\n opknob = pproj\['op\{0\}'.format(track.split('track')\[-1])]\n if opknob.isAnimated():\n c.addKey(opknob.animation(x).keys())\n else:\n tknob.clearAnimated()\n # This hackiness is necessary so knob values work correctly\n track_node.setTab(2)\n track_node.showControlPanel()\n track_node\[trs_knobs\[0]].setValue(1)\n track_node\[trs_knobs\[0]].setValue(7)\n track_node.hideControlPanel()" +STARTLINE}
addUserKnob {22 create_tracker4 l Tracker4 t "Create Tracker4 class tracker node." -STARTLINE T "from __future__ import with_statement\n\n# http://community.foundry.com/discuss/topic/99665\n# https://www.mail-archive.com/nuke-python@support.thefoundry.co.uk/msg04697.html\n\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\nnode = nuke.thisNode()\nexpression_link = node\['expression_link'].getValue()\n# pros not worth the cons ... disabling expression linking Tracker4 nodes for now\nexpression_link = False\n\nif expression_link:\n nuke.message(\"Warning: If expression linking a Tracker4 node, it must be placed in the \\nimage stream in order to evaluate correctly.\")\n\nwith nuke.root():\n _ = \[n.setSelected(False) for n in nuke.allNodes()]\n track_node = nuke.createNode('Tracker4')\n track_node.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\n _ = \[n.setSelected(False) for n in nuke.allNodes()]\n track_node.setSelected(True)\n\n # First line 3rd number is length of tracks: explicitly setting it to 4 here.\n tracks_fromscript = \"\{ 1 31 4 \} \\n\{ \{ 5 1 20 enable e 1 \} \\n\{ 3 1 75 name name 1 \} \\n\{ 2 1 58 track_x track_x 1 \} \\n\{ 2 1 58 track_y track_y 1 \} \\n\{ 2 1 63 offset_x offset_x 1 \} \\n\{ 2 1 63 offset_y offset_y 1 \} \\n\{ 4 1 27 T T 1 \} \\n\{ 4 1 27 R R 1 \} \\n\{ 4 1 27 S S 1 \} \\n\{ 2 0 45 error error 1 \} \\n\{ 1 1 0 error_min error_min 1 \} \\n\{ 1 1 0 error_max error_max 1 \} \\n\{ 1 1 0 pattern_x pattern_x 1 \} \\n\{ 1 1 0 pattern_y pattern_y 1 \} \\n\{ 1 1 0 pattern_r pattern_r 1 \} \\n\{ 1 1 0 pattern_t pattern_t 1 \} \\n\{ 1 1 0 search_x search_x 1 \} \\n\{ 1 1 0 search_y search_y 1 \} \\n\{ 1 1 0 search_r search_r 1 \} \\n\{ 1 1 0 search_t search_t 1 \} \\n\{ 2 1 0 key_track key_track 1 \} \\n\{ 2 1 0 key_search_x key_search_x 1 \} \\n\{ 2 1 0 key_search_y key_search_y 1 \} \\n\{ 2 1 0 key_search_r key_search_r 1 \} \\n\{ 2 1 0 key_search_t key_search_t 1 \} \\n\{ 2 1 0 key_track_x key_track_x 1 \} \\n\{ 2 1 0 key_track_y key_track_y 1 \} \\n\{ 2 1 0 key_track_r key_track_r 1 \} \\n\{ 2 1 0 key_track_t key_track_t 1 \} \\n\{ 2 1 0 key_centre_offset_x key_centre_offset_x 1 \} \\n\{ 2 1 0 key_centre_offset_y key_centre_offset_y 1 \} \\n\} \"\n default_track_end_script = \" \{0\} \{0\} 1 1 1 \{0\} 1 0 -32 -32 32 32 -22 -22 22 22 \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \}\\n\"\n\n # Loop through each 2d track knob and add it's animation to the Tracker4 node.\n track_knobs = \['op1', 'op2', 'op3', 'op4']\n all_trackdata = \[]\n for i, knobname in enumerate(track_knobs):\n opknob = node\[knobname]\n if opknob.isAnimated():\n trackdata = \[]\n # Gather animation data from opknobs\n for i, curve in enumerate(opknob.animations()):\n if expression_link:\n if i == 0:\n dimension = 'x'\n elif i == 1:\n dimension = 'y'\n trackdata_string = \" \{\{\{0\}\}\}\".format(\"parent.\{0\}.\{1\}.\{2\}\".format(node.fullName(), knobname, dimension))\n trackdata.append(trackdata_string)\n # livelink_transform must be true in order for expression links to work correctly on a tracker node\n track_node\['livelink_transform'].setValue(True)\n else:\n trackdata_string = \" \{ curve\"\n for key in curve.keys():\n trackdata_string += \" x\{0\} \{1\}\".format(int(key.x), key.y)\n trackdata_string += \"\}\"\n trackdata.append(trackdata_string)\n all_trackdata.append(trackdata)\n\n else:\n i -= 1\n continue\n # Append data to fromscript\n tracks_fromscript += \"\\n\{\\n\"\n for i, curve in enumerate(all_trackdata):\n tracks_fromscript += \" \{ \{1\} \"\n tracks_fromscript += \"\\\"track_\{0\}\\\"\".format(i)\n for dim in curve:\n tracks_fromscript += \"\{0\}\".format(dim)\n tracks_fromscript += default_track_end_script\n tracks_fromscript += \"\\n\}\"\n tracks_knob = track_node\['tracks']\n tracks_knob.fromScript(tracks_fromscript)\n # Force update of Translate / Rotate / Scale\n track_node.showControlPanel()\n track_node.setTab(0)\n tracks_knob.setValue(0, 6)\n tracks_knob.setValue(1, 6)\n track_node.hideControlPanel()\n track_node\['keyframe_display'].setValue(3)"}
addUserKnob {6 expression_link l "expression link" t "If enabled, expression links will be created instead of copying the animation curves" +STARTLINE}
expression_link true
addUserKnob {6 independent_identity_frame l "independent identity frame" t "Add an independent identity frame to the output node. (More expressions! Use with caution.)" -STARTLINE}
addUserKnob {22 create_rotopaint l "Roto / Add Layer" t "Create a roto node with a layer that has the matrix transform in it to use for tracking. \n\nWorks with any number of Existing Roto / RotoPaint / SplineWarp nodes as well if they are selected." T "## Create Rotopaint\nimport nuke\nimport nuke.rotopaint as rp\nimport _curvelib as cl\n\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\nnode = nuke.thisNode()\nmatrix = node\['matrix']\nnuke.root().begin()\nidentity_frame = int(node\['identity_frame'].getValue())\nexpression_link = node\['expression_link'].getValue()\nindependent_identity_frame = node\['independent_identity_frame'].getValue()\n\nfirst = None\nfor every in matrix.animations():\n if first == None:\n first = int(every.keys()\[0].x)\n else:\n first = min(first, int(every.keys()\[0].x))\n\nlast = None\nfor every in matrix.animations():\n if last == None:\n last = int(every.keys()\[0].x)\n else:\n last = max(last, int(every.keys()\[-1].x))\n\n\nif first != None and last != None:\n if matrix.isAnimated():\n # Get all selected roto nodes\n selected_roto_nodes = \[n for n in nuke.selectedNodes() if n.Class() in \[\"Roto\", \"RotoPaint\", \"SplineWarp3\"]]\n if not selected_roto_nodes:\n _ = \[n.setSelected(False) for n in nuke.allNodes()]\n roto_node = nuke.createNode(\"Roto\", inpanel=False)\n roto_node.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\n roto_node.setSelected(True)\n selected_roto_nodes = \[roto_node]\n\n for roto_node in selected_roto_nodes:\n\n curve_knob = roto_node\['curves']\n roto_layer = rp.Layer(curve_knob)\n roto_layer.name = node.name()\n\n curve = \[cl.AnimCurve() for i in range(16)]\n\n # Add independent identity frame calculation onto the target node.\n if expression_link and independent_identity_frame:\n # Add identity frame knob\n roto_node.addKnob(nuke.Tab_Knob('Identity Frame'))\n roto_node.addKnob(nuke.Int_Knob('identity_frame', 'identity frame'))\n roto_node\['identity_frame'].setValue(int(node\['identity_frame'].getValue()))\n roto_node.addKnob(nuke.PyScript_Knob('identity_to_curframe', 'Current'))\n roto_node.knob('identity_to_curframe').setTooltip(\"Set identity frame to current frame.\")\n roto_node.knob('identity_to_curframe').setCommand(\"nuke.thisNode().knob('identity_frame').setValue(nuke.frame())\")\n roto_node.setTab(1)\n\n roto_node.addKnob(nuke.Double_Knob('determinant', 'determinant'))\n determinant = \"parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+ parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+ parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)- parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)- parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+ parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+ parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+ parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+ parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)- parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)- parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+ parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)\".format(node.fullName())\n roto_node\['determinant'].setExpression(determinant)\n for i in range(4):\n roto_node.addKnob(nuke.BBox_Knob(\"invmatrix\{0\}\".format(i)))\n # view-source:https://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 0)\n roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 1)\n roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 2)\n roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame))/determinant\".format(node.fullName()), 3)\n\n # view-source:https://www.euclideanspace.com/maths/algebra/matrix/arithmetic/fourD/index.htm\n curve\[0].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.0 + parent.\{0\}.matrix.1*invmatrix1.0 + parent.\{0\}.matrix.2*invmatrix2.0 + parent.\{0\}.matrix.3*invmatrix3.0\".format(node.fullName())\n curve\[1].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.1 + parent.\{0\}.matrix.1*invmatrix1.1 + parent.\{0\}.matrix.2*invmatrix2.1 + parent.\{0\}.matrix.3*invmatrix3.1\".format(node.fullName())\n curve\[2].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.2 + parent.\{0\}.matrix.1*invmatrix1.2 + parent.\{0\}.matrix.2*invmatrix2.2 + parent.\{0\}.matrix.3*invmatrix3.2\".format(node.fullName())\n curve\[3].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.3 + parent.\{0\}.matrix.1*invmatrix1.3 + parent.\{0\}.matrix.2*invmatrix2.3 + parent.\{0\}.matrix.3*invmatrix3.3\".format(node.fullName())\n\n curve\[4].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.0 + parent.\{0\}.matrix.5*invmatrix1.0 + parent.\{0\}.matrix.6*invmatrix2.0 + parent.\{0\}.matrix.7*invmatrix3.0\".format(node.fullName())\n curve\[5].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.1 + parent.\{0\}.matrix.5*invmatrix1.1 + parent.\{0\}.matrix.6*invmatrix2.1 + parent.\{0\}.matrix.7*invmatrix3.1\".format(node.fullName())\n curve\[6].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.2 + parent.\{0\}.matrix.5*invmatrix1.2 + parent.\{0\}.matrix.6*invmatrix2.2 + parent.\{0\}.matrix.7*invmatrix3.2\".format(node.fullName())\n curve\[7].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.3 + parent.\{0\}.matrix.5*invmatrix1.3 + parent.\{0\}.matrix.6*invmatrix2.3 + parent.\{0\}.matrix.7*invmatrix3.3\".format(node.fullName())\n\n curve\[8].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.0 + parent.\{0\}.matrix.9*invmatrix1.0 + parent.\{0\}.matrix.10*invmatrix2.0 + parent.\{0\}.matrix.11*invmatrix3.0\".format(node.fullName())\n curve\[9].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.1 + parent.\{0\}.matrix.9*invmatrix1.1 + parent.\{0\}.matrix.10*invmatrix2.1 + parent.\{0\}.matrix.11*invmatrix3.1\".format(node.fullName())\n curve\[10].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.2 + parent.\{0\}.matrix.9*invmatrix1.2 + parent.\{0\}.matrix.10*invmatrix2.2 + parent.\{0\}.matrix.11*invmatrix3.2\".format(node.fullName())\n curve\[11].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.3 + parent.\{0\}.matrix.9*invmatrix1.3 + parent.\{0\}.matrix.10*invmatrix2.3 + parent.\{0\}.matrix.11*invmatrix3.3\".format(node.fullName())\n\n curve\[12].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.0 + parent.\{0\}.matrix.13*invmatrix1.0 + parent.\{0\}.matrix.14*invmatrix2.0 + parent.\{0\}.matrix.15*invmatrix3.0\".format(node.fullName())\n curve\[13].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.1 + parent.\{0\}.matrix.13*invmatrix1.1 + parent.\{0\}.matrix.14*invmatrix2.1 + parent.\{0\}.matrix.15*invmatrix3.1\".format(node.fullName())\n curve\[14].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.2 + parent.\{0\}.matrix.13*invmatrix1.2 + parent.\{0\}.matrix.14*invmatrix2.2 + parent.\{0\}.matrix.15*invmatrix3.2\".format(node.fullName())\n curve\[15].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.3 + parent.\{0\}.matrix.13*invmatrix1.3 + parent.\{0\}.matrix.14*invmatrix2.3 + parent.\{0\}.matrix.15*invmatrix3.3\".format(node.fullName())\n\n for i in range(16):\n curve\[i].useExpression = True\n roto_node\['label'].setValue('\{0\}: x\[value identity_frame]'.format(node.name()))\n\n elif expression_link and not independent_identity_frame:\n # Just expression link\n for f in range(first,last+1):\n for i in range(16):\n if node\['expression_link'].getValue():\n curve\[i].expressionString = \"parent.\{0\}.matrix.\{1\}\".format(node.fullName(), i)\n curve\[i].useExpression = True\n else:\n curve\[i].addKey(int(f), matrix.valueAt(f)\[i])\n roto_node\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), identity_frame))\n else:\n # Copy the animation curves as is\n for f in range(first, last+1):\n for i in range(16):\n curve\[i].addKey(int(f), node\['matrix'].getValueAt(f, i))\n roto_node\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), identity_frame))\n\n for y in range(1,5):\n for x in range(1,5):\n cell = ((y-1)*4)+x-1\n roto_layer.getTransform().setExtraMatrixAnimCurve(y-1,x-1, curve\[cell])\n curve_knob.rootLayer.append(roto_layer)\n roto_node.setTab(1)" +STARTLINE}
addUserKnob {22 create_cornerpin l CornerPin t "Create a cornerpin node, using the specified identity frame, if enabled. \n\nIf you need a CornerPin stabilizing, click the invert checkbox." -STARTLINE T "## Create Cornerpin\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\n\nnode = nuke.thisNode()\nnuke.root().begin()\n\n_ = \[n.setSelected(False) for n in nuke.allNodes()]\ncornerpin = nuke.createNode(\"CornerPin2D\", inpanel=False)\ncornerpin.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\ncornerpin.setSelected(True)\ncornerpin.addKnob(nuke.Tab_Knob('Identity Frame'))\ncornerpin.addKnob(nuke.Int_Knob('identity_frame', 'identity frame'))\nreference_frame = int(node\['identity_frame'].getValue())\ncornerpin\['identity_frame'].setValue(int(node\['identity_frame'].getValue()))\ncornerpin\['label'].setValue(\"PlanarProjection : ref x\{0\}\".format('\[value identity_frame]'))\n\nfor i in range(1, 5):\n if node\['expression_link'].getValue():\n cornerpin\['to\{0\}'.format(i)].setExpression(\"parent.\{0\}.op\{1\}\".format(node.fullName(), i))\n else:\n cornerpin\['to\{0\}'.format(i)].copyAnimations(node\['op\{0\}'.format(i)].animations())\n cornerpin\['from\{0\}'.format(i)].setExpression('to\{0\}(identity_frame)'.format(i))\ncornerpin.setTab(1)"}
addUserKnob {22 create_cornerpin_matrix l "CornerPin Matrix" t "Create a Cornerpin with the animation in the extra matrix knob instead of the to/from knobs." -STARTLINE T "## Create Cornerpin with tracking data in matrix\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\n\nnode = nuke.thisNode()\nnuke.root().begin()\n\n_ = \[n.setSelected(False) for n in nuke.allNodes()]\ncornerpin = nuke.createNode(\"CornerPin2D\", inpanel=False)\ncornerpin.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\ncornerpin.setSelected(True)\n\n\nreference_frame = int(node\['identity_frame'].getValue())\nexpression_link = node\['expression_link'].getValue()\nindependent_identity_frame = node\['independent_identity_frame'].getValue()\ncpmatrix = cornerpin\['transform_matrix']\n\nif expression_link and independent_identity_frame:\n # Add identity frame knob\n cornerpin.addKnob(nuke.Tab_Knob('Identity Frame'))\n cornerpin.addKnob(nuke.Int_Knob('identity_frame', 'identity frame'))\n cornerpin\['identity_frame'].setValue(int(node\['identity_frame'].getValue()))\n cornerpin.addKnob(nuke.PyScript_Knob('identity_to_curframe', 'Current'))\n cornerpin.knob('identity_to_curframe').setTooltip(\"Set identity frame to current frame.\")\n cornerpin.knob('identity_to_curframe').setCommand(\"nuke.thisNode().knob('identity_frame').setValue(nuke.frame())\")\n cornerpin.setTab(1)\n\n cornerpin.addKnob(nuke.Double_Knob('determinant', 'determinant'))\n determinant = \"parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+ parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+ parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)- parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)- parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)- parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+ parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+ parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+ parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)- parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+ parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+ parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)- parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)- parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+ parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)\".format(node.fullName())\n cornerpin\['determinant'].setExpression(determinant)\n for i in range(4):\n cornerpin.addKnob(nuke.BBox_Knob(\"invmatrix\{0\}\".format(i)))\n # view-source:https://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 0)\n cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 1)\n cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 2)\n cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame))/determinant\".format(node.fullName()), 3)\n\n # view-source:https://www.euclideanspace.com/maths/algebra/matrix/arithmetic/fourD/index.htm\n cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.0 + parent.\{0\}.matrix.1*invmatrix1.0 + parent.\{0\}.matrix.2*invmatrix2.0 + parent.\{0\}.matrix.3*invmatrix3.0\".format(node.fullName()), 0)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.1 + parent.\{0\}.matrix.1*invmatrix1.1 + parent.\{0\}.matrix.2*invmatrix2.1 + parent.\{0\}.matrix.3*invmatrix3.1\".format(node.fullName()), 1)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.2 + parent.\{0\}.matrix.1*invmatrix1.2 + parent.\{0\}.matrix.2*invmatrix2.2 + parent.\{0\}.matrix.3*invmatrix3.2\".format(node.fullName()), 2)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.3 + parent.\{0\}.matrix.1*invmatrix1.3 + parent.\{0\}.matrix.2*invmatrix2.3 + parent.\{0\}.matrix.3*invmatrix3.3\".format(node.fullName()), 3)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.0 + parent.\{0\}.matrix.5*invmatrix1.0 + parent.\{0\}.matrix.6*invmatrix2.0 + parent.\{0\}.matrix.7*invmatrix3.0\".format(node.fullName()), 4)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.1 + parent.\{0\}.matrix.5*invmatrix1.1 + parent.\{0\}.matrix.6*invmatrix2.1 + parent.\{0\}.matrix.7*invmatrix3.1\".format(node.fullName()), 5)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.2 + parent.\{0\}.matrix.5*invmatrix1.2 + parent.\{0\}.matrix.6*invmatrix2.2 + parent.\{0\}.matrix.7*invmatrix3.2\".format(node.fullName()), 6)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.3 + parent.\{0\}.matrix.5*invmatrix1.3 + parent.\{0\}.matrix.6*invmatrix2.3 + parent.\{0\}.matrix.7*invmatrix3.3\".format(node.fullName()), 7)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.0 + parent.\{0\}.matrix.9*invmatrix1.0 + parent.\{0\}.matrix.10*invmatrix2.0 + parent.\{0\}.matrix.11*invmatrix3.0\".format(node.fullName()), 8)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.1 + parent.\{0\}.matrix.9*invmatrix1.1 + parent.\{0\}.matrix.10*invmatrix2.1 + parent.\{0\}.matrix.11*invmatrix3.1\".format(node.fullName()), 9)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.2 + parent.\{0\}.matrix.9*invmatrix1.2 + parent.\{0\}.matrix.10*invmatrix2.2 + parent.\{0\}.matrix.11*invmatrix3.2\".format(node.fullName()), 10)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.3 + parent.\{0\}.matrix.9*invmatrix1.3 + parent.\{0\}.matrix.10*invmatrix2.3 + parent.\{0\}.matrix.11*invmatrix3.3\".format(node.fullName()), 11)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.0 + parent.\{0\}.matrix.13*invmatrix1.0 + parent.\{0\}.matrix.14*invmatrix2.0 + parent.\{0\}.matrix.15*invmatrix3.0\".format(node.fullName()), 12)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.1 + parent.\{0\}.matrix.13*invmatrix1.1 + parent.\{0\}.matrix.14*invmatrix2.1 + parent.\{0\}.matrix.15*invmatrix3.1\".format(node.fullName()), 13)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.2 + parent.\{0\}.matrix.13*invmatrix1.2 + parent.\{0\}.matrix.14*invmatrix2.2 + parent.\{0\}.matrix.15*invmatrix3.2\".format(node.fullName()), 14)\n cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.3 + parent.\{0\}.matrix.13*invmatrix1.3 + parent.\{0\}.matrix.14*invmatrix2.3 + parent.\{0\}.matrix.15*invmatrix3.3\".format(node.fullName()), 15)\n\n cornerpin\['label'].setValue(\"PlanarProjection : ref x\{0\}\".format('\[value identity_frame]'))\nelif expression_link and not independent_identity_frame:\n cornerpin\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), reference_frame))\n for i in range(16):\n cpmatrix.setExpression(\"parent.\{0\}.matrix.\{1\}\".format(node.fullName(), i), i)\nelse:\n cornerpin\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), reference_frame))\n cpmatrix.copyAnimations(matrix.animations())"}
addUserKnob {22 create_gridwarp l GridWarp -STARTLINE T "node = nuke.thisNode()\nmatrix = node\['matrix']\nnuke.root().begin()\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\nexpression_link = node\['expression_link'].getValue()\n\nif matrix.isAnimated():\n _ = \[n.setSelected(False) for n in nuke.allNodes()]\n gridwarp = nuke.createNode(\"GridWarp3\")\n gridwarp.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\n gridwarp.setSelected(True)\n\n # Add Label\n reference_frame = int(node\['identity_frame'].getValue())\n if expression_link:\n gridwarp\['label'].setValue('\{0\}: x\[value identity_frame]'.format(node.name()))\n else:\n gridwarp\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), reference_frame))\n\n gridwarp_matrices = \[gridwarp\['source_grid_transform_matrix'], gridwarp\['destination_grid_transform_matrix']]\n\n if expression_link:\n for i in range(16):\n for gridwarp_matrix in gridwarp_matrices:\n gridwarp_matrix.setExpression(\"parent.\{0\}.matrix.\{1\}\".format(node.fullName(), i), i)\n else:\n gridwarp_matrices\[0].copyAnimations(matrix.animations())"}
addUserKnob {26 ""}
addUserKnob {26 desc l " " T "developed by Vit Sedlacek www.vitsedlacek.com\noptimized and improved by Jed Smith - http://gist.github.com/jedypod"}
}
Axis2 {
inputs 0
useMatrix true
name Matrix_original
xpos -276
ypos -260
disable true
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
Input {
inputs 0
name InputBG
label "\[value number]"
xpos 620
ypos -304
addUserKnob {20 User}
addUserKnob {18 ip1 R -100 100}
ip1 {0 0 0}
addUserKnob {6 ip1_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
addUserKnob {18 ip2 l "" +STARTLINE R -100 100}
ip2 {0 0 0}
addUserKnob {6 ip2_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
addUserKnob {18 ip3 l "" +STARTLINE R -100 100}
ip3 {0 0 0}
addUserKnob {6 ip3_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
addUserKnob {18 ip4 l "" +STARTLINE R -100 100}
ip4 {0 0 0}
addUserKnob {6 ip4_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
}
Output {
name Output1
xpos 600
ypos 531
}
Axis2 {
inputs 0
translate {0.5 0.5 0}
useMatrix true
matrix {
{{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
{{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
{{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
{{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
}
name CENTER
xpos -190
ypos 78
addUserKnob {20 User}
addUserKnob {7 plane_size l "" +STARTLINE R 0.1 100}
plane_size 1
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
set N70e4b400 [stack 0]
Axis2 {
translate {{parent.CENTER.plane_size/2} {parent.CENTER.plane_size/2} 0}
name UR
xpos -131
ypos 222
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
push $N70e4b400
Axis2 {
translate {{-parent.CENTER.plane_size/2} {parent.CENTER.plane_size/2} 0}
name UL
xpos -23
ypos 222
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
push $N70e4b400
Axis2 {
translate {{parent.CENTER.plane_size/2} {-parent.CENTER.plane_size/2} 0}
name LR
xpos -241
ypos 222
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
push $N70e4b400
Axis2 {
translate {{-parent.CENTER.plane_size/2} {-parent.CENTER.plane_size/2} 0}
name LL
xpos -367
ypos 222
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
TransformGeo {
inputs 0
translate {0.1070738435 0.007514983416 -0.8936647177}
rotate {-6.806648731 -55.99889755 179.3054504}
scaling {1.000000119 0.5625 1.043081284e-07}
name _MATCH_SELECTION_
xpos -40
ypos 99
}
Input {
inputs 0
name InputCam
label "\[value number]"
xpos 440
ypos -303
number 1
}
push 0
Axis2 {
inputs 0
name point_4
xpos 300
ypos -270
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
Axis2 {
inputs 0
name point_3
xpos 190
ypos -270
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
Axis2 {
inputs 0
name point_2
xpos 80
ypos -270
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
Axis2 {
inputs 0
name point_1
xpos -30
ypos -271
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
addUserKnob {20 look l Constrain}
addUserKnob {1 look_at l "Look at"}
addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
}
Scene {
inputs 6
name Scene1
xpos 450
ypos -95
}
end_group
@rafaelperez
Copy link

Just came to say this is one of my most loved scripts for Nuke. It saves me so much time, I'm truly grateful for it. Thanks

@melMass
Copy link

melMass commented Jun 5, 2021

Thanks a lot!!

Here is the python3 version:

Planar projection for Nuke 13+ (python3)
set cut_paste_input [stack 0]
version 13.0 v2
push $cut_paste_input
Group {
 name PlanarProjection
 help "<b>Planar Projection</b>\nGenerates 2D coordinates for points in 3D space. Type in 3D point coordinates, or use vertex selection in 3D viewer and click set to pick average of selected points, or set points to set all four points at once. \n\nYou can connect node output to scene together with your pointcloud or geometry and see where your points are located in 3d space. Double click any of them to move it in 3d space like any traditional nuke transform control. \n\nA matrix transform is also generated to be used with RotoPaint, SplineWarp and GridWarp nodes. If you are using matrix in GridWarp, points have to be in clockwise order, pick them one by one! \n\nCommand set points doesn't respect selection order! \n\nCheck out the demo video on my website! Kudos to Ivan Busquets for help with matrix math. \n\n-- developed by Vit Sedlacek 2012 www.vitsedlacek.com \n\n-- Modified by Jed Smith to make calculation time nearly instantaneous, fix some bugs and create a BG input so that the sampled format could be easily specified instead of using nuke.root() format. Also enabled animated 3d input points, and added some functionality in the set knobs to snarf data from axis nodes.\nhttp://gist.github.com/jedypod"
 selected true
 xpos 1499
 ypos 643
 addUserKnob {20 PlanarProjection}
 addUserKnob {26 ""}
 addUserKnob {26 usage_label_header l " " T "<font size=4>\n<b>Input 3D Positions</b>"}
 addUserKnob {20 usage_label_grp l "       help" n 1}
 usage_label_grp 0
 addUserKnob {26 usage_label l " " T "<font size=3>\nSelect any number of points in the 3D view, in vertex selection mode.<br/>\n<b>Set from Selection</b>: 4 points will be set based on the selection bounds.<br/>\n<b>Set from Axes</b>: Gets position from 4 axis nodes with or without animation.<br/>\n<b>Set Animated</b>: Set the target point. For animated geometry.<br/>\n<b>Set Point</b>: Set point from vertex selection. If no vertex selection,<br/>\n set from selected Axis. If no axis, set from selected pixel value (for PWorld).<br/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"}
 addUserKnob {20 endGroup n -1}
 addUserKnob {22 set_from_selection l "Set from Selection" t "Set value of all points If Axis nodes are selected, it grabs translate values from them. If not, the first 4 selected vertexes in the 3D viewport is used instead. " T "## Set points to selected\nfrom __future__ import with_statement\nfrom nukescripts import snap3d as sn\n\n\nnode = nuke.thisNode()\nnode.begin()\nmatch_sel = nuke.toNode('_MATCH_SELECTION_')\n\nsn.translateRotateScaleToPoints(match_sel)\n\naxes = \[nuke.toNode('LL'), nuke.toNode('LR'), nuke.toNode('UR'), nuke.toNode('UL')]\nfor i, axis in enumerate(axes):\n    matrix = axis\['world_matrix']\n    translate = \[matrix.getValue(3), matrix.getValue(7), matrix.getValue(11)]\n    node\['ip\{0\}'.format(i+1)].setValue(translate)" +STARTLINE}
 addUserKnob {22 set_from_axes l "Set from Axes" t "Select up to 4 different axes, which can be animated. This button will set the points to each axis in selection order." -STARTLINE T "node = nuke.thisNode()\nnuke.root().begin()\n\naxis_nodes = \[axis for axis in nuke.selectedNodes() if axis.Class() == \"Axis2\"]\nif axis_nodes:\n    if len(axis_nodes) > 4:\n        nuke.message(\"More than 4 axis nodes selected: Will only use the first 4 selected ones.\")\n    axis_nodes = list(reversed(axis_nodes)) # Reverse selected so it goes in selection order\n    for i in range(4):\n        point = node\[\"ip\{0\}\".format(i+1)]\n        axis = axis_nodes\[i]\n        if axis\['translate'].isAnimated():\n            point.clearAnimated()\n            point.copyAnimations(axis\['translate'].animations())\n        else:\n            point.clearAnimated()\n            point.setValue(axis\['translate'].getValue())"}
 addUserKnob {22 clear_input l Clear t "Remove all animated and non-default knob values." -STARTLINE T "n = nuke.thisNode()\n\nfor i in range(4):\n    kip = n\['ip'+str(i+1)]\n    kip.clearAnimated()\n    kip.setValue(kip.defaultValue())"}
 addUserKnob {22 set_single_animated l "Set Animated" t "Sets single point to selected vertex position. Set the target knob to put the data on." T "import nuke\nfrom nukescripts import snap3d as sn\n\ndef _get_framerange():\n    first_frame = int(nuke.numvalue('root.first_frame'))\n    last_frame = int(nuke.numvalue('root.last_frame'))\n    step = 1\n    default_frange = str(nuke.FrameRange(first_frame, last_frame, step))\n    frange = nuke.getInput('Enter Frame Range:', default_frange)\n    if not frange:\n        return None\n    else:\n        try:\n            return nuke.FrameRange(frange)\n        except:\n            nuke.message('Invalid frame range')\n            return None\n\ndef get_vertex_position(frame):\n    # return last selected vertex position on specified frame\n    vertices = sn.getSelection()\n    if not vertices:\n        nuke.message(\"A vertex must be selected.\")\n        return\n    center = sn.calcAveragePosition(vertices)\n    vertices.translate(-center)\n    return center\n\ndef calc_vertex_positions():\n    framerange = _get_framerange()\n    if not framerange:\n        return\n    curvetool = nuke.nodes.CurveTool()\n    positions_animcurve = \[\[], \[], \[]]\n    for frame in framerange:\n        nuke.execute(curvetool, frame, frame)\n        curpos = get_vertex_position(frame)\n        if curpos:\n            for i in range(3):\n                positions_animcurve\[i].append(nuke.AnimationKey(frame, curpos\[i]))\n    if curvetool:\n        nuke.delete(curvetool)\n    return positions_animcurve\n\nnode = nuke.thisNode()\nnuke.root().begin()\niptarget = node\['iptarget'].getValue()\nipknob = node\['ip\{0\}'.format(int(iptarget)+1)]\nipknob.clearAnimated()\nipknob.setAnimated()\n\npositions = calc_vertex_positions()\nfor index, curve in enumerate(ipknob.animations()):\n    curve.addKey(positions\[index])" +STARTLINE}
 addUserKnob {22 set_point l "Set Point" -STARTLINE T "import nuke\nfrom nukescripts import snap3d as sn\n\ndef get_vertex_position():\n    points = sn.getSelection()\n    if not points:\n        return None\n    pos = sn.calcAveragePosition(points)\n    return pos\n\nif __name__==\"__main__\":\n    node = nuke.thisNode()\n    nuke.root().begin()\n    axis_nodes = \[axis for axis in nuke.selectedNodes() if axis.Class() == \"Axis2\"]\n    iptarget = node\['iptarget'].getValue()\n    ipknob = node\['ip\{0\}'.format(int(iptarget)+1)]\n    pos = get_vertex_position()\n    if pos:\n        ipknob.setValue(pos)\n    elif axis_nodes:\n        axis = axis_nodes\[-1]\n        if axis\['translate'].isAnimated():\n            ipknob.clearAnimated()\n            ipknob.copyAnimations(axis\['translate'].animations())\n        else:\n            ipknob.clearAnimated()\n            ipknob.setValue(axis\['translate'].getValue())\n    else:\n        viewer = nuke.activeViewer().node()\n        if viewer.input(0):\n            viewed_node = viewer.input(0)\n            if not nuke.selectedNodes():\n                viewed_node.setSelected(1)\n        else:\n            viewed_node = nuke.activeViewer().node()\n        bboxinfo = nuke.activeViewer().node()\['colour_sample_bbox'].value()\n        aspect = float(viewed_node.width() * viewed_node.pixelAspect()) / float(viewed_node.height())\n        cornerA = \[(bboxinfo\[0]*0.5+0.5) * viewed_node.width(), (((bboxinfo\[1] * 0.5) + (0.5/aspect)) * aspect) * viewed_node.height()]\n        cornerB = \[(bboxinfo\[2]*0.5+0.5) * viewed_node.width(), (((bboxinfo\[3] * 0.5) + (0.5/aspect)) * aspect) * viewed_node.height()]\n        area = \[cornerB\[0] - cornerA\[0], cornerB\[1] - cornerA\[1]]\n        center = \[cornerA\[0] + (area\[0]/2), cornerA\[1] + (area\[1] / 2)]\n        color_sample = \[viewed_node.sample('rgba.red', center\[0], center\[1], area\[0], area\[1]), viewed_node.sample('rgba.green', center\[0], center\[1], area\[0], area\[1]), viewed_node.sample('rgba.blue', center\[0], center\[1], area\[0], area\[1])]\n        ipknob.setValue(color_sample)"}
 addUserKnob {4 iptarget l "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target" -STARTLINE M {"point 1" "point 2" "point 3" "point 4" "" ""}}
 iptarget "point 2"
 addUserKnob {41 ip1 l "point 1" T point_1.translate}
 addUserKnob {22 set_ip1 l "Set 1" t "Set point 1 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(0)\nnode\['set_point'].execute()"}
 addUserKnob {41 ip2 l "point 2" T point_2.translate}
 addUserKnob {22 set_ip2 l "Set 2" t "Set point 2 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(1)\nnode\['set_point'].execute()"}
 addUserKnob {41 ip3 l "point 3" T point_3.translate}
 addUserKnob {22 set_ip3 l "Set 3" t "Set point 3 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(2)\nnode\['set_point'].execute()"}
 addUserKnob {41 ip4 l "point 4" T point_4.translate}
 addUserKnob {22 set_ip4 l "Set 4" t "Set point 4 to current selection." -STARTLINE T "node = nuke.thisNode()\nnode\['iptarget'].setValue(3)\nnode\['set_point'].execute()"}
 addUserKnob {26 ""}
 addUserKnob {26 reconcile_explanation l " " T "<font size=4><b>Reconcile Points into Screen Space</b>"}
 addUserKnob {20 reconcile_points_grp l "       help" n 1}
 addUserKnob {26 reconcile_points_help l " " T "<font size=3>Press Calculate and the 3D points will be reconciled into 2d tracking data. <br/>\nA perspective transform matrix is also calculated. <br/>This enables tracking in \ntranslation, rotation, scale and perspective.\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"}
 addUserKnob {20 endGroup_1 l endGroup n -1}
 addUserKnob {22 Calculate t "Calculate the reconciled 2D points for each 3D point.\n\nAlso calculate a matrix transform for the planar surface, if there are 4 total points. This can then be used for Rotos and CornerPins." T "import nuke\nfrom nukescripts import snap3d as sn\nimport math\n\n\n\ndef cameraProjectionMatrix(cameraNode, frame, imageformat):\n    ## modified code from nukescripts/Snap3D\n\n\n    # Matrix to transform points into camera-relative coords.\n    wm = nuke.math.Matrix4()\n    for i in range(16):\n        wm\[i] = cameraNode\['matrix'].getValueAt(frame,i)\n    wm.transpose()\n    camTransform = wm.inverse()\n\n\n    # Matrix to take the camera projection knobs into account\n    roll = float(cameraNode\['winroll'].getValueAt(frame,0))\n    scale_x = float(cameraNode\['win_scale'].getValueAt(frame,0))\n    scale_y = float(cameraNode\['win_scale'].getValueAt(frame,1))\n    translate_x = float(cameraNode\['win_translate'].getValueAt(frame,0))\n    translate_y = float(cameraNode\['win_translate'].getValueAt(frame,1))\n    m = nuke.math.Matrix4()\n    m.makeIdentity()\n    m.rotateZ(math.radians(roll))\n    m.scale(1.0 / scale_x, 1.0 / scale_y, 1.0)\n    m.translate(-translate_x, -translate_y, 0.0)\n\n\n    # Projection matrix based on the focal length, aperture and clipping planes of the camera\n    focal_length = float(cameraNode\['focal'].getValueAt(frame))\n    h_aperture = float(cameraNode\['haperture'].getValueAt(frame))\n    near = float(cameraNode\['near'].getValueAt(frame))\n    far = float(cameraNode\['far'].getValueAt(frame))\n    projection_mode = int(cameraNode\['projection_mode'].getValueAt(frame))\n    p = nuke.math.Matrix4()\n    p.projection(focal_length / h_aperture, near, far, projection_mode == 0)\n\n\n    # Matrix to translate the projected points into normalised pixel coords\n    imageAspect = float(imageformat.height()) / float(imageformat.width())\n\n\n    t = nuke.math.Matrix4()\n    t.makeIdentity()\n    t.translate( 1.0, 1.0 - (1.0 - imageAspect / float(imageformat.pixelAspect())), 0.0 )\n\n\n    # Matrix to scale normalised pixel coords into actual pixel coords.\n    x_scale = float(imageformat.width()) / 2.0\n    y_scale = x_scale * imageformat.pixelAspect()\n    s = nuke.math.Matrix4()\n    s.makeIdentity()\n    s.scale(x_scale, y_scale, 1.0)\n\n\n    # The projection matrix transforms points into camera coords, modifies based\n    # on the camera knob values, projects points into clip coords, translates the\n    # clip coords so that they lie in the range 0,0 - 2,2 instead of -1,-1 - 1,1,\n    # then scales the clip coords to proper pixel coords.\n    return s * t * p * m * camTransform\n\n\n\n\ndef pointsToMatrix(frame, node, reference_frame=None):\n    cpToMtx = nuke.math.Matrix4()\n    cpFromMtx = nuke.math.Matrix4()\n\n\n    # Get a list of Vector2 objects for each of the 'to' knobs and each of the 'from' knobs.\n    ToVectors = \[nuke.math.Vector2(node\[f].getValueAt(frame, 0), node\[f].getValueAt(frame, 1)) for f in sorted(node.knobs().keys()) if f.startswith('op')]\n\n\n    # Feed all 4 coordinates into the mapUnitSquareToQuad() function\n    cpToMtx.mapUnitSquareToQuad(ToVectors\[0].x, ToVectors\[0].y, ToVectors\[1].x, ToVectors\[1].y, ToVectors\[2].x, ToVectors\[2].y, ToVectors\[3].x, ToVectors\[3].y)\n\n\n    # If we have a reference frame, use the to vectors on that frame as our from matrix, otherwise use the image width and height\n    if reference_frame:\n        FromVectors = \[nuke.math.Vector2(node\[f].getValueAt(reference_frame, 0), node\[f].getValueAt(reference_frame, 1)) for f in sorted(node.knobs().keys()) if f.startswith('op')]\n        cpFromMtx.mapUnitSquareToQuad(FromVectors\[0].x, FromVectors\[0].y, FromVectors\[1].x, FromVectors\[1].y, FromVectors\[2].x, FromVectors\[2].y, FromVectors\[3].x, FromVectors\[3].y)\n    else:\n        cpFromMtx.mapUnitSquareToQuad(0, 0, node.width(), 0, node.width(), node.height(), 0, node.height())\n\n\n    transform_matrix = cpToMtx * cpFromMtx.inverse()\n\n\n    # This is not needed to apply the transformation. The matrix is already correct.\n    # However, the matrix knob in rotopaint is indexed differently (row major), so I just transpose the matrix here to make it easier to copy its values to the knob\n    transform_matrix.transpose()\n\n\n    # Fill in the transform_matrix knob of the target node\n    return transform_matrix\n\n\n\n\ndef projectPoints(frame, camera=None, point=None, imageformat=None):\n    # Modify projectpoint function in nukescripts.snap3d to add frame argument\n    if not imageformat:\n        imageformat = nuke.root()\['format'].value()\n    camMatrix = cameraProjectionMatrix(camera, frame, imageformat)\n    if camMatrix == None:\n        raise RuntimeError(\"snap3d.cameraProjectionMatrix() returned None for camera.\")\n\n\n    if not ( isinstance(point, list) or isinstance(point, tuple) ):\n        raise ValueError(\"Argument point must be a list or tuple.\")\n\n\n    for point in point:\n        # Would be nice to not do this for every item but since lists/tuples can\n        # containg anything...\n        if isinstance(point, nuke.math.Vector3):\n            pt = point\n        elif isinstance(point, list) or isinstance(point, tuple):\n            pt = nuke.math.Vector3(point\[0], point\[1], point\[2])\n        else:\n            raise ValueError(\"All items in point must be nuke.math.Vector3 or list/tuple of 3 floats.\")\n\n\n        tPos = camMatrix * nuke.math.Vector4(pt.x, pt.y, pt.z, 1.0)\n        #print(tPos)\n        try:\n            yield nuke.math.Vector2(tPos.x / tPos.w, tPos.y / tPos.w)\n        except ZeroDivisionError:\n            print(\"Zero Division Error on frame \{0\} with point data \{1\}\".format(frame, point))\n            yield nuke.math.Vector2()\n\n\n\ndef calculate(node):\n    # Get the input Camera and verify it is right. (Assume camera is topnode of input to handle dots)\n    cam_input = node.input(1)\n    # Sanity check\n    if not (cam_input and isinstance(cam_input, nuke.Node)):\n        nuke.message(\"A Camera node must be connected.\")\n        return\n    if 'Camera' in cam_input.Class():\n        cam = cam_input\n    else:\n        cam = nuke.toNode(nuke.tcl(\"full_name \[topnode %s]\" % cam_input.name()))\n    bg = node.input(0)\n    if not bg:\n        nuke.message(\"BG not connected, so the root format will be used to reconcile the 3D points into screen space.\")\n\n\n    # Get framerange to operate on from camera animation curves\n    first = None\n    last = None\n    try:\n        if cam\['translate'].isAnimated():\n            for curve in cam\['translate'].animations():\n                if first == None:\n                    first = int(curve.keys()\[0].x)\n                else:\n                    first = min(first, int(curve.keys()\[0].x))\n            for curve in cam\['translate'].animations():\n                if last == None:\n                    last = int(curve.keys()\[-1].x)\n                else:\n                    last = max(last, int(curve.keys()\[-1].x))\n        elif cam\['rotate'].isAnimated():\n            for curve in cam\['rotate'].animations():\n                if first == None:\n                    first = int(curve.keys()\[0].x)\n                else:\n                    first = min(first, int(curve.keys()\[0].x))\n            for curve in cam\['rotate'].animations():\n                if last == None:\n                    last = int(curve.keys()\[-1].x)\n                else:\n                    last = max(last, int(curve.keys()\[-1].x))\n        else:\n            nuke.message(\"Input Camera is not animated.\")\n            return\n    except IndexError:\n        print(\"Could not caclucate the framerange base on the keys (Are you using expressions?), using Root framerange...\")\n        first = nuke.root().firstFrame()\n        last = nuke.root().lastFrame()\n    except Exception as e:\n        print(e)\n        nuke.message(\"Something went wrong getting the camera animation. Using Root framerange...\")\n        first = nuke.root().firstFrame()\n        last = nuke.root().lastFrame()\n\n\n\n    framerange = nuke.FrameRange('\{0\}-\{1\}'.format(first, last))\n\n\n    # Loop through the 3D points, reconcile the coordinates through the camera into screen space\n    for opnum in range(4):\n        # Only run if the ip knob is not default\n        ipknob = node\[\"ip\{0\}\".format(opnum+1)]\n        if not ipknob.notDefault():\n            print(\"Skipping \{0\} because it is still default!\".format('ip'+str(opnum+1)))\n            continue\n\n\n        opknob = node\[\"op\{0\}\".format(opnum+1)]\n        opknob.clearAnimated()\n        opknob.setAnimated()\n\n\n        # Building the data into a list of AnimationKey objects,\n        # and then applying that list to the knob using addKey is significantly faster than other methods.\n        # This makes the Calculate button instantaneous instead of taking forever.\n        point_animcurve = \[\[], \[]]\n        for frame in framerange:\n\n\n            #print(\"values are \", ipknob.getValueAt(frame))\n            # Sample input point knob on every frame if it's animated or expression-linked\n            if ipknob.isAnimated() or ipknob.hasExpression():\n                point = next(projectPoints(frame, cam, \[ipknob.getValueAt(frame)], node.format()))\n            else:\n                point = next(projectPoints(frame, cam, \[ipknob.value()], node.format()))\n            for index in range(2):\n                point_animcurve\[index].append(nuke.AnimationKey(frame, point\[index]))\n\n\n        for index, curve in enumerate(opknob.animations()):\n            curve.addKey(point_animcurve\[index])\n\n\n    # Calculate the transformation Matrix, if we have 4 output points\n    for i in range(4):\n        ipknob = node\[\"op\{0\}\".format(i+1)]\n        #print('op'+str(i+1)+' IS NOT DEFAULT')\n        if not ipknob.notDefault():\n            return\n    mknob = node\['matrix']\n    mknob.clearAnimated()\n    mknob.setAnimated()\n\n\n    use_reference_frame = True\n    reference_frame = node\['identity_frame'].getValue()\n\n\n    matrix_animcurve = \[\[] for i in range(16)]\n    for frame in framerange:\n        if use_reference_frame:\n            matrix_data = pointsToMatrix(frame, node, reference_frame)\n        else:\n            matrix_data = pointsToMatrix(frame, node)\n        for index in range(16):\n            matrix_animcurve\[index].append(nuke.AnimationKey(frame, matrix_data\[index]))\n    matrix_animcurve = \[\[] for i in range(16)]\n    for frame in framerange:\n        if use_reference_frame:\n            matrix_data = pointsToMatrix(frame, node, reference_frame)\n        else:\n            matrix_data = pointsToMatrix(frame, node)\n        for index in range(16):\n            matrix_animcurve\[index].append(nuke.AnimationKey(frame, matrix_data\[index]))\n\n\n    for index, item in enumerate(mknob.animations()):\n        item.addKey(matrix_animcurve\[index])\n    for index, item in enumerate(mknob.animations()):\n        item.addKey(matrix_animcurve\[index])\n\n\nif __name__==\"__main__\":\n    calculate(nuke.thisNode())" +STARTLINE}
 addUserKnob {22 clear_out l Clear t "Clear output knobs." -STARTLINE T "n = nuke.thisNode()\n\nfor i in range(4):\n    kop = n\['op'+str(i+1)]\n    kop.clearAnimated()\n    kop.setValue(kop.defaultValue())\n    n\['matrix'].clearAnimated()\n    n\['matrix'].setValue(\[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])"}
 addUserKnob {12 op1 l "point 1"}
 addUserKnob {12 op2 l "point 2"}
 addUserKnob {12 op3 l "point 3"}
 addUserKnob {12 op4 l "point 4"}
 addUserKnob {41 matrix t "Calculated matrix." T Matrix_original.matrix}
 addUserKnob {26 identity_frame_label l " " T "\nIdentity Frame"}
 addUserKnob {3 identity_frame l " " t "If a frame is specified here, the matrix calculated will be relative to this frame. On the reference frame the matrix will be a null transformation."}
 identity_frame 1001
 addUserKnob {22 set_current l Current -STARTLINE T nuke.thisNode().knob('identity_frame').setValue(nuke.frame())}
 addUserKnob {26 ""}
 addUserKnob {26 link_tracking_label l " " T "<font size=4><b>Link Tracking Data to Nodes</b>"}
 addUserKnob {20 link_tracking_help_grp l "       help" n 1}
 link_tracking_help_grp 0
 addUserKnob {26 link_tracking_help l " " T "<font size=3>\n<b>Expression link</b> will keep a live expression to the data on this node, in case it is updated.\n<br/><b>Independent identity frame</b> will add extra knobs to the created node to allow you to \n<br/>specify an independent identity frame on the node. It's heavier!\n<br/>&nbsp;&nbsp;&nbsp;&nbsp;"}
 addUserKnob {20 endGroup_2 l endGroup n -1}
 addUserKnob {22 create_tracker3 l Tracker3 t "Create a Tracker3 class Tracker node, in case this is what you prefer to link roto shapes to." T "# Create tracker3 node\nfrom __future__ import with_statement\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\npproj = nuke.thisNode()\nwith nuke.root():\n    track_node = nuke.nodes.Tracker3()\n    track_node.setXYpos(pproj.xpos()-grid_x*0, pproj.ypos()+grid_y*2)\n    _ = \[n.setSelected(False) for n in nuke.allNodes()]\n    track_node.setSelected(True)\n    enable_knobs = \['enable1', 'enable2', 'enable3', 'enable4']\n    trs_knobs = \['use_for1', 'use_for2', 'use_for3', 'use_for4']\n    track_knobs = \['track1', 'track2', 'track3', 'track4']\n\n    for i, k in enumerate(enable_knobs):\n        if pproj\['op\{0\}'.format(i+1)].isAnimated():\n            track_node\[k].setValue(True)\n    for i, k in enumerate(trs_knobs):\n        if pproj\['op\{0\}'.format(i+1)].isAnimated():\n            track_node\[k].setValue(7)\n    for i, track in enumerate(track_knobs):\n        if pproj\['op\{0\}'.format(i+1)].isAnimated():\n            tknob = track_node\[track]\n            tknob.clearAnimated()\n            tknob.setAnimated()\n            for x, c in enumerate(tknob.animations()):\n                opknob = pproj\['op\{0\}'.format(track.split('track')\[-1])]\n                if opknob.isAnimated():\n                    c.addKey(opknob.animation(x).keys())\n                else:\n                    tknob.clearAnimated()\n    # This hackiness is necessary so knob values work correctly\n    track_node.setTab(2)\n    track_node.showControlPanel()\n    track_node\[trs_knobs\[0]].setValue(1)\n    track_node\[trs_knobs\[0]].setValue(7)\n    track_node.hideControlPanel()" +STARTLINE}
 addUserKnob {22 create_tracker4 l Tracker4 t "Create Tracker4 class tracker node." -STARTLINE T "from __future__ import with_statement\n\n# http://community.foundry.com/discuss/topic/99665\n# https://www.mail-archive.com/nuke-python@support.thefoundry.co.uk/msg04697.html\n\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\nnode = nuke.thisNode()\nexpression_link = node\['expression_link'].getValue()\n# pros not worth the cons ... disabling expression linking Tracker4 nodes for now\nexpression_link = False\n\nif expression_link:\n    nuke.message(\"Warning: If expression linking a Tracker4 node, it must be placed in the \\nimage stream in order to evaluate correctly.\")\n\nwith nuke.root():\n    _ = \[n.setSelected(False) for n in nuke.allNodes()]\n    track_node = nuke.createNode('Tracker4')\n    track_node.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\n    _ = \[n.setSelected(False) for n in nuke.allNodes()]\n    track_node.setSelected(True)\n\n    # First line 3rd number is length of tracks: explicitly setting it to 4 here.\n    tracks_fromscript = \"\{ 1 31 4 \} \\n\{ \{ 5 1 20 enable e 1 \} \\n\{ 3 1 75 name name 1 \} \\n\{ 2 1 58 track_x track_x 1 \} \\n\{ 2 1 58 track_y track_y 1 \} \\n\{ 2 1 63 offset_x offset_x 1 \} \\n\{ 2 1 63 offset_y offset_y 1 \} \\n\{ 4 1 27 T T 1 \} \\n\{ 4 1 27 R R 1 \} \\n\{ 4 1 27 S S 1 \} \\n\{ 2 0 45 error error 1 \} \\n\{ 1 1 0 error_min error_min 1 \} \\n\{ 1 1 0 error_max error_max 1 \} \\n\{ 1 1 0 pattern_x pattern_x 1 \} \\n\{ 1 1 0 pattern_y pattern_y 1 \} \\n\{ 1 1 0 pattern_r pattern_r 1 \} \\n\{ 1 1 0 pattern_t pattern_t 1 \} \\n\{ 1 1 0 search_x search_x 1 \} \\n\{ 1 1 0 search_y search_y 1 \} \\n\{ 1 1 0 search_r search_r 1 \} \\n\{ 1 1 0 search_t search_t 1 \} \\n\{ 2 1 0 key_track key_track 1 \} \\n\{ 2 1 0 key_search_x key_search_x 1 \} \\n\{ 2 1 0 key_search_y key_search_y 1 \} \\n\{ 2 1 0 key_search_r key_search_r 1 \} \\n\{ 2 1 0 key_search_t key_search_t 1 \} \\n\{ 2 1 0 key_track_x key_track_x 1 \} \\n\{ 2 1 0 key_track_y key_track_y 1 \} \\n\{ 2 1 0 key_track_r key_track_r 1 \} \\n\{ 2 1 0 key_track_t key_track_t 1 \} \\n\{ 2 1 0 key_centre_offset_x key_centre_offset_x 1 \} \\n\{ 2 1 0 key_centre_offset_y key_centre_offset_y 1 \} \\n\} \"\n    default_track_end_script = \" \{0\} \{0\} 1 1 1 \{0\} 1 0 -32 -32 32 32 -22 -22 22 22 \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \{0\} \}\\n\"\n\n    # Loop through each 2d track knob and add it's animation to the Tracker4 node.\n    track_knobs = \['op1', 'op2', 'op3', 'op4']\n    all_trackdata = \[]\n    for i, knobname in enumerate(track_knobs):\n        opknob = node\[knobname]\n        if opknob.isAnimated():\n            trackdata = \[]\n            # Gather animation data from opknobs\n            for i, curve in enumerate(opknob.animations()):\n                if expression_link:\n                    if i == 0:\n                        dimension = 'x'\n                    elif i == 1:\n                        dimension = 'y'\n                    trackdata_string = \" \{\{\{0\}\}\}\".format(\"parent.\{0\}.\{1\}.\{2\}\".format(node.fullName(), knobname, dimension))\n                    trackdata.append(trackdata_string)\n                    # livelink_transform must be true in order for expression links to work correctly on a tracker node\n                    track_node\['livelink_transform'].setValue(True)\n                else:\n                    trackdata_string = \" \{ curve\"\n                    for key in curve.keys():\n                        trackdata_string += \" x\{0\} \{1\}\".format(int(key.x), key.y)\n                    trackdata_string += \"\}\"\n                    trackdata.append(trackdata_string)\n            all_trackdata.append(trackdata)\n\n        else:\n            i -= 1\n            continue\n    # Append data to fromscript\n    tracks_fromscript += \"\\n\{\\n\"\n    for i, curve in enumerate(all_trackdata):\n        tracks_fromscript += \"  \{ \{1\} \"\n        tracks_fromscript += \"\\\"track_\{0\}\\\"\".format(i)\n        for dim in curve:\n            tracks_fromscript += \"\{0\}\".format(dim)\n        tracks_fromscript += default_track_end_script\n    tracks_fromscript += \"\\n\}\"\n    tracks_knob = track_node\['tracks']\n    tracks_knob.fromScript(tracks_fromscript)\n    # Force update of Translate / Rotate / Scale\n    track_node.showControlPanel()\n    track_node.setTab(0)\n    tracks_knob.setValue(0, 6)\n    tracks_knob.setValue(1, 6)\n    track_node.hideControlPanel()\n    track_node\['keyframe_display'].setValue(3)"}
 addUserKnob {6 expression_link l "expression link" t "If enabled, expression links will be created instead of copying the animation curves" +STARTLINE}
 expression_link true
 addUserKnob {6 independent_identity_frame l "independent identity frame" t "Add an independent identity frame to the output node. (More expressions! Use with caution.)" -STARTLINE}
 addUserKnob {22 create_rotopaint l "Roto / Add Layer" t "Create a roto node with a layer that has the matrix transform in it to use for tracking. \n\nWorks with any number of Existing Roto / RotoPaint / SplineWarp nodes as well if they are selected." T "## Create Rotopaint\nimport nuke\nimport nuke.rotopaint as rp\nimport _curvelib as cl\n\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\nnode = nuke.thisNode()\nmatrix = node\['matrix']\nnuke.root().begin()\nidentity_frame = int(node\['identity_frame'].getValue())\nexpression_link = node\['expression_link'].getValue()\nindependent_identity_frame = node\['independent_identity_frame'].getValue()\n\nfirst = None\nfor every in matrix.animations():\n    if first == None:\n        first = int(every.keys()\[0].x)\n    else:\n        first = min(first, int(every.keys()\[0].x))\n\nlast = None\nfor every in matrix.animations():\n    if last == None:\n        last = int(every.keys()\[0].x)\n    else:\n        last = max(last, int(every.keys()\[-1].x))\n\n\nif first != None and last != None:\n    if matrix.isAnimated():\n        # Get all selected roto nodes\n        selected_roto_nodes = \[n for n in nuke.selectedNodes() if n.Class() in \[\"Roto\", \"RotoPaint\", \"SplineWarp3\"]]\n        if not selected_roto_nodes:\n            _ = \[n.setSelected(False) for n in nuke.allNodes()]\n            roto_node = nuke.createNode(\"Roto\", inpanel=False)\n            roto_node.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\n            roto_node.setSelected(True)\n            selected_roto_nodes = \[roto_node]\n\n        for roto_node in selected_roto_nodes:\n\n            curve_knob = roto_node\['curves']\n            roto_layer = rp.Layer(curve_knob)\n            roto_layer.name = node.name()\n\n            curve = \[cl.AnimCurve() for i in range(16)]\n\n            # Add independent identity frame calculation onto the target node.\n            if expression_link and independent_identity_frame:\n                # Add identity frame knob\n                roto_node.addKnob(nuke.Tab_Knob('Identity Frame'))\n                roto_node.addKnob(nuke.Int_Knob('identity_frame', 'identity frame'))\n                roto_node\['identity_frame'].setValue(int(node\['identity_frame'].getValue()))\n                roto_node.addKnob(nuke.PyScript_Knob('identity_to_curframe', 'Current'))\n                roto_node.knob('identity_to_curframe').setTooltip(\"Set identity frame to current frame.\")\n                roto_node.knob('identity_to_curframe').setCommand(\"nuke.thisNode().knob('identity_frame').setValue(nuke.frame())\")\n                roto_node.setTab(1)\n\n                roto_node.addKnob(nuke.Double_Knob('determinant', 'determinant'))\n                determinant = \"parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)-  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)-  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)-  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)-  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)\".format(node.fullName())\n                roto_node\['determinant'].setExpression(determinant)\n                for i in range(4):\n                    roto_node.addKnob(nuke.BBox_Knob(\"invmatrix\{0\}\".format(i)))\n                # view-source:https://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n                roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n                roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n                roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n                roto_node\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n                roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n                roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n                roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n                roto_node\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n                roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n                roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n                roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n                roto_node\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n                roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 0)\n                roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 1)\n                roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 2)\n                roto_node\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame))/determinant\".format(node.fullName()), 3)\n\n                # view-source:https://www.euclideanspace.com/maths/algebra/matrix/arithmetic/fourD/index.htm\n                curve\[0].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.0 + parent.\{0\}.matrix.1*invmatrix1.0 + parent.\{0\}.matrix.2*invmatrix2.0 + parent.\{0\}.matrix.3*invmatrix3.0\".format(node.fullName())\n                curve\[1].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.1 + parent.\{0\}.matrix.1*invmatrix1.1 + parent.\{0\}.matrix.2*invmatrix2.1 + parent.\{0\}.matrix.3*invmatrix3.1\".format(node.fullName())\n                curve\[2].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.2 + parent.\{0\}.matrix.1*invmatrix1.2 + parent.\{0\}.matrix.2*invmatrix2.2 + parent.\{0\}.matrix.3*invmatrix3.2\".format(node.fullName())\n                curve\[3].expressionString = \"parent.\{0\}.matrix.0*invmatrix0.3 + parent.\{0\}.matrix.1*invmatrix1.3 + parent.\{0\}.matrix.2*invmatrix2.3 + parent.\{0\}.matrix.3*invmatrix3.3\".format(node.fullName())\n\n                curve\[4].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.0 + parent.\{0\}.matrix.5*invmatrix1.0 + parent.\{0\}.matrix.6*invmatrix2.0 + parent.\{0\}.matrix.7*invmatrix3.0\".format(node.fullName())\n                curve\[5].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.1 + parent.\{0\}.matrix.5*invmatrix1.1 + parent.\{0\}.matrix.6*invmatrix2.1 + parent.\{0\}.matrix.7*invmatrix3.1\".format(node.fullName())\n                curve\[6].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.2 + parent.\{0\}.matrix.5*invmatrix1.2 + parent.\{0\}.matrix.6*invmatrix2.2 + parent.\{0\}.matrix.7*invmatrix3.2\".format(node.fullName())\n                curve\[7].expressionString = \"parent.\{0\}.matrix.4*invmatrix0.3 + parent.\{0\}.matrix.5*invmatrix1.3 + parent.\{0\}.matrix.6*invmatrix2.3 + parent.\{0\}.matrix.7*invmatrix3.3\".format(node.fullName())\n\n                curve\[8].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.0 + parent.\{0\}.matrix.9*invmatrix1.0 + parent.\{0\}.matrix.10*invmatrix2.0 + parent.\{0\}.matrix.11*invmatrix3.0\".format(node.fullName())\n                curve\[9].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.1 + parent.\{0\}.matrix.9*invmatrix1.1 + parent.\{0\}.matrix.10*invmatrix2.1 + parent.\{0\}.matrix.11*invmatrix3.1\".format(node.fullName())\n                curve\[10].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.2 + parent.\{0\}.matrix.9*invmatrix1.2 + parent.\{0\}.matrix.10*invmatrix2.2 + parent.\{0\}.matrix.11*invmatrix3.2\".format(node.fullName())\n                curve\[11].expressionString = \"parent.\{0\}.matrix.8*invmatrix0.3 + parent.\{0\}.matrix.9*invmatrix1.3 + parent.\{0\}.matrix.10*invmatrix2.3 + parent.\{0\}.matrix.11*invmatrix3.3\".format(node.fullName())\n\n                curve\[12].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.0 + parent.\{0\}.matrix.13*invmatrix1.0 + parent.\{0\}.matrix.14*invmatrix2.0 + parent.\{0\}.matrix.15*invmatrix3.0\".format(node.fullName())\n                curve\[13].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.1 + parent.\{0\}.matrix.13*invmatrix1.1 + parent.\{0\}.matrix.14*invmatrix2.1 + parent.\{0\}.matrix.15*invmatrix3.1\".format(node.fullName())\n                curve\[14].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.2 + parent.\{0\}.matrix.13*invmatrix1.2 + parent.\{0\}.matrix.14*invmatrix2.2 + parent.\{0\}.matrix.15*invmatrix3.2\".format(node.fullName())\n                curve\[15].expressionString = \"parent.\{0\}.matrix.12*invmatrix0.3 + parent.\{0\}.matrix.13*invmatrix1.3 + parent.\{0\}.matrix.14*invmatrix2.3 + parent.\{0\}.matrix.15*invmatrix3.3\".format(node.fullName())\n\n                for i in range(16):\n                    curve\[i].useExpression = True\n                roto_node\['label'].setValue('\{0\}: x\[value identity_frame]'.format(node.name()))\n\n            elif expression_link and not independent_identity_frame:\n                # Just expression link\n                for f in range(first,last+1):\n                    for i in range(16):\n                        if node\['expression_link'].getValue():\n                            curve\[i].expressionString = \"parent.\{0\}.matrix.\{1\}\".format(node.fullName(), i)\n                            curve\[i].useExpression = True\n                        else:\n                            curve\[i].addKey(int(f), matrix.valueAt(f)\[i])\n                roto_node\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), identity_frame))\n            else:\n                # Copy the animation curves as is\n                for f in range(first, last+1):\n                    for i in range(16):\n                        curve\[i].addKey(int(f), node\['matrix'].getValueAt(f, i))\n                roto_node\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), identity_frame))\n\n            for y in range(1,5):\n                for x in range(1,5):\n                    cell = ((y-1)*4)+x-1\n                    roto_layer.getTransform().setExtraMatrixAnimCurve(y-1,x-1, curve\[cell])\n            curve_knob.rootLayer.append(roto_layer)\n            roto_node.setTab(1)" +STARTLINE}
 addUserKnob {22 create_cornerpin l CornerPin t "Create a cornerpin node, using the specified identity frame, if enabled. \n\nIf you need a CornerPin stabilizing, click the invert checkbox." -STARTLINE T "## Create Cornerpin\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\n\nnode = nuke.thisNode()\nnuke.root().begin()\n\n_ = \[n.setSelected(False) for n in nuke.allNodes()]\ncornerpin = nuke.createNode(\"CornerPin2D\", inpanel=False)\ncornerpin.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\ncornerpin.setSelected(True)\ncornerpin.addKnob(nuke.Tab_Knob('Identity Frame'))\ncornerpin.addKnob(nuke.Int_Knob('identity_frame', 'identity frame'))\nreference_frame = int(node\['identity_frame'].getValue())\ncornerpin\['identity_frame'].setValue(int(node\['identity_frame'].getValue()))\ncornerpin\['label'].setValue(\"PlanarProjection : ref x\{0\}\".format('\[value identity_frame]'))\n\nfor i in range(1, 5):\n    if node\['expression_link'].getValue():\n        cornerpin\['to\{0\}'.format(i)].setExpression(\"parent.\{0\}.op\{1\}\".format(node.fullName(), i))\n    else:\n        cornerpin\['to\{0\}'.format(i)].copyAnimations(node\['op\{0\}'.format(i)].animations())\n    cornerpin\['from\{0\}'.format(i)].setExpression('to\{0\}(identity_frame)'.format(i))\ncornerpin.setTab(1)"}
 addUserKnob {22 create_cornerpin_matrix l "CornerPin Matrix" t "Create a Cornerpin with the animation in the extra matrix knob instead of the to/from knobs." -STARTLINE T "## Create Cornerpin with tracking data in matrix\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\n\nnode = nuke.thisNode()\nnuke.root().begin()\n\n_ = \[n.setSelected(False) for n in nuke.allNodes()]\ncornerpin = nuke.createNode(\"CornerPin2D\", inpanel=False)\ncornerpin.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\ncornerpin.setSelected(True)\n\n\nreference_frame = int(node\['identity_frame'].getValue())\nexpression_link = node\['expression_link'].getValue()\nindependent_identity_frame = node\['independent_identity_frame'].getValue()\ncpmatrix = cornerpin\['transform_matrix']\n\nif expression_link and independent_identity_frame:\n    # Add identity frame knob\n    cornerpin.addKnob(nuke.Tab_Knob('Identity Frame'))\n    cornerpin.addKnob(nuke.Int_Knob('identity_frame', 'identity frame'))\n    cornerpin\['identity_frame'].setValue(int(node\['identity_frame'].getValue()))\n    cornerpin.addKnob(nuke.PyScript_Knob('identity_to_curframe', 'Current'))\n    cornerpin.knob('identity_to_curframe').setTooltip(\"Set identity frame to current frame.\")\n    cornerpin.knob('identity_to_curframe').setCommand(\"nuke.thisNode().knob('identity_frame').setValue(nuke.frame())\")\n    cornerpin.setTab(1)\n\n    cornerpin.addKnob(nuke.Double_Knob('determinant', 'determinant'))\n    determinant = \"parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.12(identity_frame)+  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.12(identity_frame)-  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)-  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.13(identity_frame)-  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.13(identity_frame)+  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.3(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.7(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.14(identity_frame)+  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.11(identity_frame) * parent.\{0\}.matrix.14(identity_frame)-  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.8(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+  parent.\{0\}.matrix.2(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)-  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.6(identity_frame) * parent.\{0\}.matrix.9(identity_frame) * parent.\{0\}.matrix.15(identity_frame)-  parent.\{0\}.matrix.1(identity_frame) * parent.\{0\}.matrix.4(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)+  parent.\{0\}.matrix.0(identity_frame) * parent.\{0\}.matrix.5(identity_frame) * parent.\{0\}.matrix.10(identity_frame) * parent.\{0\}.matrix.15(identity_frame)\".format(node.fullName())\n    cornerpin\['determinant'].setExpression(determinant)\n    for i in range(4):\n        cornerpin.addKnob(nuke.BBox_Knob(\"invmatrix\{0\}\".format(i)))\n    # view-source:https://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n    cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n    cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n    cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n    cornerpin\['invmatrix0'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n    cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n    cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n    cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n    cornerpin\['invmatrix1'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.10(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n    cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 0)\n    cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.11(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.15(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 1)\n    cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.15(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.15(identity_frame))/determinant\".format(node.fullName()), 2)\n    cornerpin\['invmatrix2'].setExpression(\"(parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.3(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.7(identity_frame)*parent.\{0\}.matrix.9(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.11(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.11(identity_frame))/determinant\".format(node.fullName()), 3)\n    cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 0)\n    cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.12(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.10(identity_frame)*parent.\{0\}.matrix.13(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.8(identity_frame)*parent.\{0\}.matrix.14(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.9(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 1)\n    cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.12(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.13(identity_frame) + parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.14(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.14(identity_frame))/determinant\".format(node.fullName()), 2)\n    cornerpin\['invmatrix3'].setExpression(\"(parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.8(identity_frame) - parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.8(identity_frame) + parent.\{0\}.matrix.2(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.6(identity_frame)*parent.\{0\}.matrix.9(identity_frame) - parent.\{0\}.matrix.1(identity_frame)*parent.\{0\}.matrix.4(identity_frame)*parent.\{0\}.matrix.10(identity_frame) + parent.\{0\}.matrix.0(identity_frame)*parent.\{0\}.matrix.5(identity_frame)*parent.\{0\}.matrix.10(identity_frame))/determinant\".format(node.fullName()), 3)\n\n    # view-source:https://www.euclideanspace.com/maths/algebra/matrix/arithmetic/fourD/index.htm\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.0 + parent.\{0\}.matrix.1*invmatrix1.0 + parent.\{0\}.matrix.2*invmatrix2.0 + parent.\{0\}.matrix.3*invmatrix3.0\".format(node.fullName()), 0)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.1 + parent.\{0\}.matrix.1*invmatrix1.1 + parent.\{0\}.matrix.2*invmatrix2.1 + parent.\{0\}.matrix.3*invmatrix3.1\".format(node.fullName()), 1)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.2 + parent.\{0\}.matrix.1*invmatrix1.2 + parent.\{0\}.matrix.2*invmatrix2.2 + parent.\{0\}.matrix.3*invmatrix3.2\".format(node.fullName()), 2)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.0*invmatrix0.3 + parent.\{0\}.matrix.1*invmatrix1.3 + parent.\{0\}.matrix.2*invmatrix2.3 + parent.\{0\}.matrix.3*invmatrix3.3\".format(node.fullName()), 3)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.0 + parent.\{0\}.matrix.5*invmatrix1.0 + parent.\{0\}.matrix.6*invmatrix2.0 + parent.\{0\}.matrix.7*invmatrix3.0\".format(node.fullName()), 4)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.1 + parent.\{0\}.matrix.5*invmatrix1.1 + parent.\{0\}.matrix.6*invmatrix2.1 + parent.\{0\}.matrix.7*invmatrix3.1\".format(node.fullName()), 5)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.2 + parent.\{0\}.matrix.5*invmatrix1.2 + parent.\{0\}.matrix.6*invmatrix2.2 + parent.\{0\}.matrix.7*invmatrix3.2\".format(node.fullName()), 6)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.4*invmatrix0.3 + parent.\{0\}.matrix.5*invmatrix1.3 + parent.\{0\}.matrix.6*invmatrix2.3 + parent.\{0\}.matrix.7*invmatrix3.3\".format(node.fullName()), 7)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.0 + parent.\{0\}.matrix.9*invmatrix1.0 + parent.\{0\}.matrix.10*invmatrix2.0 + parent.\{0\}.matrix.11*invmatrix3.0\".format(node.fullName()), 8)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.1 + parent.\{0\}.matrix.9*invmatrix1.1 + parent.\{0\}.matrix.10*invmatrix2.1 + parent.\{0\}.matrix.11*invmatrix3.1\".format(node.fullName()), 9)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.2 + parent.\{0\}.matrix.9*invmatrix1.2 + parent.\{0\}.matrix.10*invmatrix2.2 + parent.\{0\}.matrix.11*invmatrix3.2\".format(node.fullName()), 10)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.8*invmatrix0.3 + parent.\{0\}.matrix.9*invmatrix1.3 + parent.\{0\}.matrix.10*invmatrix2.3 + parent.\{0\}.matrix.11*invmatrix3.3\".format(node.fullName()), 11)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.0 + parent.\{0\}.matrix.13*invmatrix1.0 + parent.\{0\}.matrix.14*invmatrix2.0 + parent.\{0\}.matrix.15*invmatrix3.0\".format(node.fullName()), 12)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.1 + parent.\{0\}.matrix.13*invmatrix1.1 + parent.\{0\}.matrix.14*invmatrix2.1 + parent.\{0\}.matrix.15*invmatrix3.1\".format(node.fullName()), 13)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.2 + parent.\{0\}.matrix.13*invmatrix1.2 + parent.\{0\}.matrix.14*invmatrix2.2 + parent.\{0\}.matrix.15*invmatrix3.2\".format(node.fullName()), 14)\n    cpmatrix.setExpression(\"parent.\{0\}.matrix.12*invmatrix0.3 + parent.\{0\}.matrix.13*invmatrix1.3 + parent.\{0\}.matrix.14*invmatrix2.3 + parent.\{0\}.matrix.15*invmatrix3.3\".format(node.fullName()), 15)\n\n    cornerpin\['label'].setValue(\"PlanarProjection : ref x\{0\}\".format('\[value identity_frame]'))\nelif expression_link and not independent_identity_frame:\n    cornerpin\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), reference_frame))\n    for i in range(16):\n        cpmatrix.setExpression(\"parent.\{0\}.matrix.\{1\}\".format(node.fullName(), i), i)\nelse:\n    cornerpin\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), reference_frame))\n    cpmatrix.copyAnimations(matrix.animations())"}
 addUserKnob {22 create_gridwarp l GridWarp -STARTLINE T "node = nuke.thisNode()\nmatrix = node\['matrix']\nnuke.root().begin()\ngrid_x = int(nuke.toNode('preferences').knob('GridWidth').value())\ngrid_y = int(nuke.toNode('preferences').knob('GridHeight').value())\nexpression_link = node\['expression_link'].getValue()\n\nif matrix.isAnimated():\n    _ = \[n.setSelected(False) for n in nuke.allNodes()]\n    gridwarp = nuke.createNode(\"GridWarp3\")\n    gridwarp.setXYpos(node.xpos()-grid_x*0, node.ypos()+grid_y*2)\n    gridwarp.setSelected(True)\n\n    # Add Label\n    reference_frame = int(node\['identity_frame'].getValue())\n    if expression_link:\n        gridwarp\['label'].setValue('\{0\}: x\[value identity_frame]'.format(node.name()))\n    else:\n        gridwarp\['label'].setValue('\{0\}: x\{1\}'.format(node.name(), reference_frame))\n\n    gridwarp_matrices = \[gridwarp\['source_grid_transform_matrix'], gridwarp\['destination_grid_transform_matrix']]\n\n    if expression_link:\n        for i in range(16):\n            for gridwarp_matrix in gridwarp_matrices:\n                gridwarp_matrix.setExpression(\"parent.\{0\}.matrix.\{1\}\".format(node.fullName(), i), i)\n    else:\n        gridwarp_matrices\[0].copyAnimations(matrix.animations())"}
 addUserKnob {26 ""}
 addUserKnob {26 desc l " " T "developed by Vit Sedlacek www.vitsedlacek.com\noptimized and improved by Jed Smith - http://gist.github.com/jedypod"}
}
 Axis2 {
  inputs 0
  useMatrix true
  name Matrix_original
  xpos -276
  ypos -260
  disable true
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
 Input {
  inputs 0
  name InputBG
  label "\[value number]"
  xpos 620
  ypos -304
  addUserKnob {20 User}
  addUserKnob {18 ip1 R -100 100}
  ip1 {0 0 0}
  addUserKnob {6 ip1_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip1_panelDropped_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip1_panelDropped_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip1_panelDropped_1_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {18 ip2 l "" +STARTLINE R -100 100}
  ip2 {0 0 0}
  addUserKnob {6 ip2_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip2_panelDropped_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip2_panelDropped_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip2_panelDropped_1_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {18 ip3 l "" +STARTLINE R -100 100}
  ip3 {0 0 0}
  addUserKnob {6 ip3_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip3_panelDropped_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip3_panelDropped_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip3_panelDropped_1_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {18 ip4 l "" +STARTLINE R -100 100}
  ip4 {0 0 0}
  addUserKnob {6 ip4_panelDropped l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip4_panelDropped_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip4_panelDropped_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
  addUserKnob {6 ip4_panelDropped_1_1_1 l "panel dropped state" -STARTLINE +HIDDEN}
 }
 Output {
  name Output1
  xpos 600
  ypos 531
 }
 Axis2 {
  inputs 0
  translate {0.5 0.5 0}
  useMatrix true
  matrix {
      {{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
      {{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
      {{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
      {{parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix} {parent._MATCH_SELECTION_.matrix}}
    }
  name CENTER
  xpos -190
  ypos 78
  addUserKnob {20 User}
  addUserKnob {7 plane_size l "" +STARTLINE R 0.1 100}
  plane_size 1
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
set N70e4b400 [stack 0]
 Axis2 {
  translate {{parent.CENTER.plane_size/2} {parent.CENTER.plane_size/2} 0}
  name UR
  xpos -131
  ypos 222
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
push $N70e4b400
 Axis2 {
  translate {{-parent.CENTER.plane_size/2} {parent.CENTER.plane_size/2} 0}
  name UL
  xpos -23
  ypos 222
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
push $N70e4b400
 Axis2 {
  translate {{parent.CENTER.plane_size/2} {-parent.CENTER.plane_size/2} 0}
  name LR
  xpos -241
  ypos 222
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
push $N70e4b400
 Axis2 {
  translate {{-parent.CENTER.plane_size/2} {-parent.CENTER.plane_size/2} 0}
  name LL
  xpos -367
  ypos 222
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
 TransformGeo {
  inputs 0
  translate {0.1070738435 0.007514983416 -0.8936647177}
  rotate {-6.806648731 -55.99889755 179.3054504}
  scaling {1.000000119 0.5625 1.043081284e-07}
  name _MATCH_SELECTION_
  xpos -40
  ypos 99
 }
 Input {
  inputs 0
  name InputCam
  label "\[value number]"
  xpos 440
  ypos -303
  number 1
 }
push 0
 Axis2 {
  inputs 0
  name point_4
  xpos 300
  ypos -270
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
 Axis2 {
  inputs 0
  name point_3
  xpos 190
  ypos -270
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
 Axis2 {
  inputs 0
  name point_2
  xpos 80
  ypos -270
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
 Axis2 {
  inputs 0
  name point_1
  xpos -30
  ypos -271
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
  addUserKnob {20 look l Constrain}
  addUserKnob {1 look_at l "Look at"}
  addUserKnob {22 set l Set -STARTLINE T "sel_nodes = nuke.selectedNodes()\n\nif len(sel_nodes) == 1:\n\tthisNode = sel_nodes\[-1]\n\tk = thisNode\['look_at']\n\t\n\tif k.value() == \"\":\n\t\tlookAt = nuke.getInput('Type the target node name')\n\t\tk.setValue(lookAt)\n\n\telse:\n\t\tlookAt = k.value()\n\t\tk.setValue(lookAt)\n\nelif len(sel_nodes) > 1:\n\tthisNode = sel_nodes\[-1]\n\tlookAt = sel_nodes\[-2]\n\tk = thisNode\['look_at']\n\tk.setValue(lookAt.name())\n\t\nelse:\n\tpass\n\nlookObject = k.value()\n\nxX = 'degrees(atan2(' + lookObject + '.translate.y-translate.y,sqrt(pow(' + lookObject + '.translate.x-translate.x,2)+pow(' + lookObject + '.translate.z-translate.z,2))))'\nyX = lookObject + '.translate.z-this.translate.z >= 0 ? 180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z)):180+degrees(atan2(' + lookObject + '.translate.x-translate.x,' + lookObject + '.translate.z-translate.z))'\n\nthisNode\['rotate'].setExpression(xX, 0)\nthisNode\['rotate'].setExpression(yX, 1)\n"}
 }
 Scene {
  inputs 6
  name Scene1
  xpos 450
  ypos -95
 }
end_group

@jedypod
Copy link
Author

jedypod commented Jun 12, 2021

@melMass awesome thanks! I'd been meaning to get around to this. I'm gonna test it out and update the gist when I get some time

@pelado74aldea
Copy link

Thanks a lot!!

Here is the python3 version:

Planar projection for Nuke 13+ (python3)

Hey, thanks for this, cant make it work on Nuke 13, syntax error.

@jedypod
Copy link
Author

jedypod commented Jul 12, 2021

I actually just tested this on 13.0v3 and it worked fine. @pelado74aldea what is the syntax error you are getting?

@pelado74aldea
Copy link

pelado74aldea commented Jul 13, 2021 via email

@jedypod
Copy link
Author

jedypod commented Jul 14, 2021

@pelado74aldea I see! Yes I think it will work better if you select the code that @melMass posted and paste it in the node graph. It's not a python script it's just a group node.

@pelado74aldea
Copy link

pelado74aldea commented Jul 14, 2021 via email

@dan-ger-eux
Copy link

dan-ger-eux commented Nov 12, 2021

Hi! This tool sounds awesome but it will not work for me and/ or I am using it the wrong way. Help is highly appreciated! So, I thought I can pick four points in 2D and press then "Calculate" and then I will get, for example, an animated corner pin which is something I will get from a planar tracker but in a very faster way. Am I wrong? I connected my matchmove cam and my undistorted plate directly to the PlanarProjection Node. But the created corner pin has no animation in it. I used the code from melMass from Jun 5 and I am working with Nuke 12.

@LewieFX
Copy link

LewieFX commented Nov 13, 2021

@dan-ger-eux you need to select 4 vertices in 3d space to calculate the information from - "Set from Selection". If the object you want to track is animated you need to select each point individually and select "Set Animated". Once that is done you can hit calculate and spit out your cornerpin

@dan-ger-eux
Copy link

@LewieFX Thanks for your quick reply! Ah, it is only a 3D space solution - I understand. But what about the four points that you have by default in “2D mode” at the bottom left, what do you do with them?

@LewieFX
Copy link

LewieFX commented Nov 13, 2021

Not sure, i have only used it as a 3D tracker. I think they are there because it is showing the preview of the calculated track, which would be whatever it is when brought in. @dan-ger-eux

@jedypod
Copy link
Author

jedypod commented Nov 14, 2021

@dan-ger-eux
This node reconciles 3d points through a camera into 2d tracking points. If you reconcile 4 points, a transformation matrix is also calculated which gives you perspective transformation.

This tool does not transform 2d points into 3d points. If you're looking for something that does this you could try the built-in node PointsTo3D.

Good luck

@Shrinks99
Copy link

Shrinks99 commented May 5, 2022

Tried @melMass' updated version on Nuke 13.1v3 today, works well except for that it seems to fully crash Nuke when I try to export a Tracker4. Tracker3 works fine, as does everything else?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment