From dc9cc4b48867e7fb960cb516a8e85ed8020c8bd2 Mon Sep 17 00:00:00 2001 From: DolphinDream Date: Mon, 30 Oct 2017 22:20:06 -0400 Subject: [PATCH] Add new Matrix Math node A node for matrix math.. for now it supports: Multiply, Invert, Filter. Notes: - This operates on 4x4 matrices (for now) - This is added for now to beta nodes The Multiply and Invert needs no further introduction... the Filter is an operation allowing to filter out the translation, rotation and/or the scale components of the 4x4 homogeneous matrix. --- docs/nodes/matrix/matrix_math.rst | 81 +++++++++++++ index.md | 1 + nodes/matrix/matrix_math.py | 185 ++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 docs/nodes/matrix/matrix_math.rst create mode 100644 nodes/matrix/matrix_math.py diff --git a/docs/nodes/matrix/matrix_math.rst b/docs/nodes/matrix/matrix_math.rst new file mode 100644 index 000000000..71b0a5ac3 --- /dev/null +++ b/docs/nodes/matrix/matrix_math.rst @@ -0,0 +1,81 @@ +Matrix Math +=========== + +Functionality +------------- + +Matrix Math node allows for various matrix operations to be performed on the input matrices, such as: Multiply, Invert and Filter. + +Inputs +------ + +All inputs are vectorized and they will accept single or multiple values. + +- **A** +- **B** [1] + +Notes: +[1] : The second input is only available for the operations that require a second operand, like Multiply. + +Parameters +---------- + +The **Operation** parameter allows to select one of following operations: Multiply, Invert, Filter. + +All parameters except **Operation**, **PrePost** and **Filter T/R/S** can be given as an external input. + ++---------------+------------+----------+--------------------------------------------------+ +| Param | Type | Default | Description | ++===============+============+==========+==================================================+ +| **Operation** | Enum: | Multiply | Multiply: A,B => C = A*B [1] | +| | Multiply | | Invert: A => C = A^-1 | +| | Invert | | Filter: A => C = [AT]*[AR]*[AS] | +| | Filter | | | ++---------------+------------+----------+--------------------------------------------------+ +| **PrePost** | Enum: | Pre | Determines the order the operands [2] | +| | Pre | | | +| | Post | | | ++---------------+------------+----------+--------------------------------------------------+ +| **Filter T** | Bool | False | Filter out the Translation component [3] | ++---------------+------------+----------+--------------------------------------------------+ +| **Filter R** | Bool | False | Filter out the Rotation component [3] | ++---------------+------------+----------+--------------------------------------------------+ +| **Filter S** | Bool | False | Filter out the Scale component [3] | ++---------------+------------+----------+--------------------------------------------------+ +| **A** | Matrix | identity | First matrix input | ++---------------+------------+----------+--------------------------------------------------+ +| **B** | Matrix | identity | Second matrix input [4] | ++---------------+------------+----------+--------------------------------------------------+ + +Notes: + [1] : The order of multiplication is given by the PrePost setting. + [2] : The PrePost setting is only available for the Multiply operation. + [3] : The Filter T/R/S toggle settings are only available for the Filter operation. + [4] : Second input is only available for Multiply operation. + +Operations +---------- +**Multiply** +The multiplication of the 4x4 homogeneous matrices result in a composite 4x4 homogeneous matrix having a composed Translation, a composed Rotation and a composed Scale. The order of multiplication is given by the PrePost setting, which is set to PRE-multiplication by default (C = A*B). The POST multiplication reverses the order of multiplication (C = B*A). + +Note: When using a PRE multiply composition A*B to transform a mesh, essentially the overall operation is equivalent to applying the matrix B to the mesh first, then matrix A. + +**Filter** +A 4x4 homogeneous matrix is composed by a translation (T), rotation (R) and a scale (S) matrix and is defined as: T*R*S. The filter operation allows you to set the individual components T, R and/or S to the identity matrix so that they are filtered out of the output composite matrix. + +Note: filtering out all components will result in an identity matrix output. + +**Invert** +The inversion of a 4x4 homogeneous matrix A is a 4x4 homogenous matrix A' for which A * A' is the identity matrix. + + +Outputs +------- + +**Matrix** +Outputs will be generated when connected. + + +Example of usage +---------------- + diff --git a/index.md b/index.md index 263c9ff76..6652c54a9 100644 --- a/index.md +++ b/index.md @@ -273,6 +273,7 @@ --- SvMatrixNormalNode SvMatrixTrackToNode + SvMatrixMathNode --- SvSculptMaskNode SvGreasePencilStrokes diff --git a/nodes/matrix/matrix_math.py b/nodes/matrix/matrix_math.py new file mode 100644 index 000000000..196296818 --- /dev/null +++ b/nodes/matrix/matrix_math.py @@ -0,0 +1,185 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty + +from mathutils import Matrix +import re + +from sverchok.node_tree import SverchCustomTreeNode, MatrixSocket, StringsSocket +from sverchok.data_structure import (updateNode, fullList, match_long_repeat, + Matrix_listing, Matrix_generate) + +operationItems = [ + ("MULTIPLY", "Multiply", "Multiply two matrices", 0), + ("INVERT", "Invert", "Invert matrix", 1), + ("FILTER", "Filter", "Filter matrix components", 2) +] + +prePostItems = [ + ("PRE", "Pre", "Calculate A op B", 0), + ("POST", "Post", "Calculate B op A", 1) +] + + +class SvMatrixMathNode(bpy.types.Node, SverchCustomTreeNode): + ''' Math operation on matrices ''' + bl_idname = 'SvMatrixMathNode' + bl_label = 'Matrix Math' + bl_icon = 'OUTLINER_OB_EMPTY' + + def update_operation(self, context): + self.label = "Matrix " + self.operation.title() + self.update_sockets() + updateNode(self, context) + + prePost = EnumProperty( + name='Pre Post', + description='Order of operations PRE = A op B vs POST = B op A)', + items=prePostItems, + default="PRE", + update=updateNode) + + operation = EnumProperty( + name="Operation", + description="Operation to apply on the given matrices", + items=operationItems, + default="MULTIPLY", + update=update_operation) + + filter_t = BoolProperty( + name="Filter Translation", + description="Filter out the translation component of the matrix", + default=False, + update=updateNode) + + filter_r = BoolProperty( + name="Filter Rotation", + description="Filter out the rotation component of the matrix", + default=False, + update=updateNode) + + filter_s = BoolProperty( + name="Filter Scale", + description="Filter out the scale component of the matrix", + default=False, + update=updateNode) + + def sv_init(self, context): + self.inputs.new('MatrixSocket', "A", "A") + self.inputs.new('MatrixSocket', "B", "B") + + self.outputs.new('MatrixSocket', "C", "C") + + self.operation = "MULTIPLY" + + def update_sockets(self): + inputs = self.inputs + + if self.operation == "MULTIPLY": + # two matrix inputs available + if not "B" in inputs: + inputs.new("MatrixSocket", "B") + else: + # one matrix input available + if "B" in inputs: + inputs.remove(inputs["B"]) + + def draw_buttons(self, context, layout): + layout.prop(self, "operation", text = "") + if self.operation == "MULTIPLY": + layout.prop(self, "prePost", expand=True) + elif self.operation == "FILTER": + row = layout.row(align=True) + row.prop(self, "filter_t", toggle=True, text="T") + row.prop(self, "filter_r", toggle=True, text="R") + row.prop(self, "filter_s", toggle=True, text="S") + + def operation_filter(self, a): + T, R, S = a.decompose() + + if self.filter_t: + mat_t = Matrix().Identity(4) + else: + mat_t = Matrix().Translation(T) + + if self.filter_r: + mat_r = Matrix().Identity(4) + else: + mat_r = R.to_matrix().to_4x4() + + if self.filter_s: + mat_s = Matrix().Identity(4) + else: + mat_s = Matrix().Identity(4) + mat_s[0][0] = S[0] + mat_s[1][1] = S[1] + mat_s[2][2] = S[2] + + m = mat_t * mat_r * mat_s + + return m + + def get_operation(self): + if self.operation == "MULTIPLY": + return lambda a, b: a * b + elif self.operation == "FILTER": + return self.operation_filter + elif self.operation == "INVERT": + return lambda a: a.inverted() + + def process(self): + outputs = self.outputs + if not outputs['C'].is_linked: + return + + inputs = self.inputs + id_mat = Matrix_listing([Matrix.Identity(4)]) + A = Matrix_generate(inputs['A'].sv_get(default=id_mat)) + + if self.operation in { "MULTIPLY" }: + # two matrix inputs available + B = Matrix_generate(inputs['B'].sv_get(default=id_mat)) + + if self.prePost == "PRE": + parameters = match_long_repeat([A, B]) + else: + parameters = match_long_repeat([B, A]) + else: + # one matrix input available + parameters = [A] + + operation = self.get_operation() + + matrixList = [] + for params in zip(*parameters): + c = operation(*params) + matrixList.append(c) + + matrices = Matrix_listing(matrixList) + + outputs['C'].sv_set(matrices) + + +def register(): + bpy.utils.register_class(SvMatrixMathNode) + + +def unregister(): + bpy.utils.unregister_class(SvMatrixMathNode) -- GitLab