From 2ea9d019988f82dbc07079905c4bbf632410c5b8 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Sat, 17 Apr 2021 10:04:28 +0200 Subject: [PATCH 1/4] Inset Special Mk2 --- docs/nodes/CAD/inset_special.rst | 48 -- docs/nodes/CAD/inset_special_mk2.rst | 79 +++ index.md | 2 +- json_examples/Advanced/Pineapple.json | 562 ++++++++-------- json_examples/Architecture/Coliseum.json | 310 ++++----- .../Architecture/Curved_Hexagonal_Truss.json | 119 ++-- json_examples/Fields/Biological.json | 236 +++---- nodes/CAD/inset_special_mk2.py | 205 ++++++ nodes/modifier_change/extrude_separate.py | 4 +- .../modifier_change/extrude_separate_lite.py | 4 +- nodes/modifier_change/inset_faces.py | 4 +- {nodes/CAD => old_nodes}/inset_special.py | 10 +- tests/references/monad_1.json | 31 +- utils/__init__.py | 2 +- utils/mesh/inset_faces.py | 599 ++++++++++++++++++ 15 files changed, 1566 insertions(+), 649 deletions(-) delete mode 100644 docs/nodes/CAD/inset_special.rst create mode 100644 docs/nodes/CAD/inset_special_mk2.rst create mode 100644 nodes/CAD/inset_special_mk2.py rename {nodes/CAD => old_nodes}/inset_special.py (96%) create mode 100644 utils/mesh/inset_faces.py diff --git a/docs/nodes/CAD/inset_special.rst b/docs/nodes/CAD/inset_special.rst deleted file mode 100644 index b708a202e0..0000000000 --- a/docs/nodes/CAD/inset_special.rst +++ /dev/null @@ -1,48 +0,0 @@ -Inset Special -============= - - - -Functionality -------------- - -Make inset in polygons. Output inner and outer polygons separately. - -Inputs and Parameters ---------------------- - -This node has the following inputs: - -- **Inset** - Proportional offset values meaning 0 in the center and 1 in the edges. Vectorized for every polygon as [[f,f,f,f,f]] - -- **Distance** - Offset distance along normal. Vectorized for every polygon as [[f,f,f,f,f]] - -- **vertices** - Vertices of objects - -- **polygons** - polygons of objects - -- **ignore** - mask of affected polygons - -- **Make Inner** - Determine if inner face should be created - - -Outputs -------- - -This node has the following outputs: - -- **vertices** -- **polygons** -- **Ignored** - get polygons that have not been affected. -- **Inset** - get inner polygons. - -Examples of usage ------------------ - -.. image:: https://raw.githubusercontent.com/vDicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example.png - :alt: procedural_Inset_example_blender_sverchok_1.png - -.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example2.png - :alt: procedural_Inset_example_blender_sverchok_2.png - - diff --git a/docs/nodes/CAD/inset_special_mk2.rst b/docs/nodes/CAD/inset_special_mk2.rst new file mode 100644 index 0000000000..2c1e613b1e --- /dev/null +++ b/docs/nodes/CAD/inset_special_mk2.rst @@ -0,0 +1,79 @@ +Inset Special +============= + + +Functionality +------------- + +Make inset in polygons. Output inner and outer polygons separately. + +Inputs +------ + +This node has the following inputs: + +- **Vertices** - Vertices of objects + +- **Polygons** - polygons of objects + +- **Inset** - Proportional offset values meaning 0 in the center and 1 in the edges. Vectorized for every polygon as [[f,f,f,f,f]] + +- **Distance** - Offset distance along normal. Vectorized for every polygon as [[f,f,f,f,f]] + +- **ignore** - mask of affected polygons + +- **Make Inner** - Determine if inner face should be created + +- **Custom Normals** - custom normals for inset displacement + +Options +------- + +- **Offset Mode** - How to interpret inset distance: + Center: Inset is measured as a proportion between the corners and the center of the polygon + Sides: Inset is measured as a constant distance to the sides of the polygon + +- **Proportional** - Multiply distance by polygon perimeter (only if 'Offset Mode' is set to 'Sides') + +- **Concave Support** - Try to fix distances in concave polygons. + Disclaimer: This node is will fail in polygons with the center of the polygon outside + of itself. For a better concave support use 'Inset Faces' node, which is over + 10 times slower than this node but more robust. + +- **Zero Mode** - How to handle faces with zero inset (only if 'Offset Mode' is set to 'Center'): + Skip: Ignore this faces + Fan: Merge the inset vertices into a single vertex + +- **Implementation** - How inset is calculated. + Numpy: Faster + Mathutils: Slower (Legacy. Face order may differ with new implementation) 'Custom normals' and the last 4 outputs wont work + + +Outputs +------- + +This node has the following outputs: + +- **vertices** +- **polygons** +- **Ignored** - get polygons that have not been affected. +- **Inset** - get inner polygons. +- **Original Vert Idx** - the index of the original vertex. Can be used to pass + Vertex Data to new vertices. In case of Zero inset in Fan mode the index of the + new vertex will be the first index of the polygon +- **Original Face Idx** - the index of the original face. Can be used to pass Face Data to new faces +- **Pols Group** - Outputs a list to mask polygons from the modified mesh, + 0 = Original Polygon + 1 = Side poligon + 2 = Inset Polygon. +- **New Verts Mask** - Mask of the new vertices + + +Examples of usage +----------------- + +.. image:: https://raw.githubusercontent.com/vDicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example.png + :alt: procedural_Inset_example_blender_sverchok_1.png + +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example2.png + :alt: procedural_Inset_example_blender_sverchok_2.png diff --git a/index.md b/index.md index 0f9d79474e..fd746400e8 100644 --- a/index.md +++ b/index.md @@ -465,7 +465,7 @@ SvBevelNode SvIntersectEdgesNodeMK2 SvOffsetNode - SvInsetSpecial + SvInsetSpecialMk2 SvInsetFaces SvLatheNode SvSmoothNode diff --git a/json_examples/Advanced/Pineapple.json b/json_examples/Advanced/Pineapple.json index 96e1b41e08..5a6a899204 100644 --- a/json_examples/Advanced/Pineapple.json +++ b/json_examples/Advanced/Pineapple.json @@ -20,7 +20,7 @@ "Frame.002": "Frame.010", "Frame.007": "Frame.010", "IcoSphere": "Frame.001", - "Inset Special": "Frame.001", + "Inset Special.001": "Frame.001", "Line": "Frame.003", "List Join": "Frame.003", "List Length": "Frame.008", @@ -64,7 +64,7 @@ "Vertex color mk3.001": "Frame.006" }, "groups": { - "Monad": "{\"export_version\": \"0.10\", \"framed_nodes\": {}, \"groups\": {}, \"nodes\": {\"Group Inputs Exp\": {\"bl_idname\": \"SvGroupInputsNodeExp\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1028.47900390625, -0.3426551818847656], \"params\": {\"node_kind\": \"outputs\"}, \"custom_socket_props\": {}, \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true, \"outputs\": [[\"Vertices\", \"SvVerticesSocket\"], [\"Movement Vectors\", \"SvVerticesSocket\"], [\"Polygons\", \"SvStringsSocket\"], [\"Center\", \"SvVerticesSocket\"], [\"Radius\", \"SvStringsSocket\"]]}, \"Group Outputs Exp\": {\"bl_idname\": \"SvGroupOutputsNodeExp\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1871.47509765625, -0.3426551818847656], \"params\": {\"node_kind\": \"inputs\"}, \"custom_socket_props\": {}, \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true, \"inputs\": [[\"Vertices\", \"SvVerticesSocket\"]]}, \"Move\": {\"bl_idname\": \"SvMoveNodeMk3\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1691.47509765625, -18.33763885498047], \"params\": {\"movement_vectors\": [0.0, 0.0, 0.20000000298023224]}, \"custom_socket_props\": {\"1\": {\"expanded\": true}}}, \"Select mesh elements by location\": {\"bl_idname\": \"SvMeshSelectNodeMk2\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1238.47900390625, 17.652328491210938], \"params\": {\"mode\": \"BySphere\", \"radius\": 0.6000000238418579, \"include_partial\": true}, \"custom_socket_props\": {\"3\": {\"prop\": [0.0, 0.0, 1.0]}, \"4\": {\"prop\": [0.0, 0.0, -1.0], \"expanded\": true}}}, \"Proportional Edit Falloff\": {\"bl_idname\": \"SvProportionalEditNode\", \"height\": 100.0, \"width\": 166.3173828125, \"label\": \"\", \"hide\": false, \"location\": [1466.7452392578125, -14.141777992248535], \"params\": {\"radius\": 0.699999988079071, \"falloff_type\": \"sharp\"}, \"custom_socket_props\": {}}}, \"update_lists\": [[\"Move\", 0, \"Group Outputs Exp\", 0], [\"Group Inputs Exp\", 0, \"Move\", 0], [\"Group Inputs Exp\", 1, \"Move\", 1], [\"Proportional Edit Falloff\", 0, \"Move\", 2], [\"Group Inputs Exp\", 0, \"Select mesh elements by location\", 0], [\"Group Inputs Exp\", 2, \"Select mesh elements by location\", 2], [\"Group Inputs Exp\", 3, \"Select mesh elements by location\", 4], [\"Group Inputs Exp\", 4, \"Select mesh elements by location\", 6], [\"Group Inputs Exp\", 0, \"Proportional Edit Falloff\", 0], [\"Select mesh elements by location\", 0, \"Proportional Edit Falloff\", 1]], \"bl_idname\": \"SverchGroupTreeType\", \"cls_bl_idname\": \"SvGroupNodeMonad_140486095450048\"}" + "Monad": "{\"export_version\": \"0.10\", \"framed_nodes\": {}, \"groups\": {}, \"nodes\": {\"Group Inputs Exp\": {\"bl_idname\": \"SvGroupInputsNodeExp\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1028.47900390625, -0.3426551818847656], \"params\": {\"node_kind\": \"outputs\"}, \"custom_socket_props\": {}, \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true, \"outputs\": [[\"Vertices\", \"SvVerticesSocket\"], [\"Movement Vectors\", \"SvVerticesSocket\"], [\"Polygons\", \"SvStringsSocket\"], [\"Center\", \"SvVerticesSocket\"], [\"Radius\", \"SvStringsSocket\"]]}, \"Group Outputs Exp\": {\"bl_idname\": \"SvGroupOutputsNodeExp\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1871.47509765625, -0.3426551818847656], \"params\": {\"node_kind\": \"inputs\"}, \"custom_socket_props\": {}, \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true, \"inputs\": [[\"Vertices\", \"SvVerticesSocket\"]]}, \"Move\": {\"bl_idname\": \"SvMoveNodeMk3\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1691.47509765625, -18.33763885498047], \"params\": {\"movement_vectors\": [0.0, 0.0, 0.20000000298023224]}, \"custom_socket_props\": {\"1\": {\"expanded\": true}}}, \"Select mesh elements by location\": {\"bl_idname\": \"SvMeshSelectNodeMk2\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [1238.47900390625, 17.652328491210938], \"params\": {\"mode\": \"BySphere\", \"radius\": 0.6000000238418579, \"include_partial\": true}, \"custom_socket_props\": {\"0\": {\"is_mandatory\": true}, \"1\": {\"nesting_level\": 3}, \"2\": {\"nesting_level\": 3}, \"3\": {\"prop\": [0.0, 0.0, 1.0]}, \"4\": {\"prop\": [0.0, 0.0, -1.0], \"expanded\": true}}}, \"Proportional Edit Falloff\": {\"bl_idname\": \"SvProportionalEditNode\", \"height\": 100.0, \"width\": 166.3173828125, \"label\": \"\", \"hide\": false, \"location\": [1466.7452392578125, -14.141777992248535], \"params\": {\"radius\": 0.699999988079071, \"falloff_type\": \"sharp\"}, \"custom_socket_props\": {}}}, \"update_lists\": [[\"Move\", 0, \"Group Outputs Exp\", 0], [\"Group Inputs Exp\", 0, \"Move\", 0], [\"Group Inputs Exp\", 1, \"Move\", 1], [\"Proportional Edit Falloff\", 0, \"Move\", 2], [\"Group Inputs Exp\", 0, \"Select mesh elements by location\", 0], [\"Group Inputs Exp\", 2, \"Select mesh elements by location\", 2], [\"Group Inputs Exp\", 3, \"Select mesh elements by location\", 4], [\"Group Inputs Exp\", 4, \"Select mesh elements by location\", 6], [\"Group Inputs Exp\", 0, \"Proportional Edit Falloff\", 0], [\"Select mesh elements by location\", 0, \"Proportional Edit Falloff\", 1]], \"bl_idname\": \"SverchGroupTreeType\", \"cls_bl_idname\": \"SvGroupNodeMonad_140486095450048\"}" }, "nodes": { "3pt Arc": { @@ -110,7 +110,7 @@ "label": "", "location": [ 826.978271484375, - -297.2085723876953 + -297.2085418701172 ], "params": { "selected_mode": "int" @@ -212,7 +212,7 @@ "label": "", "location": [ 3151.761474609375, - 1608.2613830566406 + 1608.2613525390625 ], "params": {}, "width": 140.0 @@ -237,8 +237,8 @@ "hide": false, "label": "", "location": [ - 2913.38623046875, - 558.7441329956055 + 2919.780397415161, + 609.8927307128906 ], "params": { "g_": 0.699999988079071 @@ -263,13 +263,24 @@ }, "Component Analyzer": { "bl_idname": "SvComponentAnalyzerNode", - "custom_socket_props": {}, + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "nesting_level": 3 + }, + "2": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", "location": [ - 2429.56982421875, - 1000.0673065185547 + 2435.963991165161, + 1051.2159118652344 ], "params": { "face_mode": "Perimeter" @@ -284,8 +295,8 @@ "hide": false, "label": "", "location": [ - 2604.19677734375, - 1000.0673027038574 + 2610.590944290161, + 1051.2159118652344 ], "params": {}, "width": 200.0 @@ -298,8 +309,8 @@ "hide": false, "label": "", "location": [ - 2496.1422805786133, - 561.019889831543 + 2502.5364475250244, + 612.1684875488281 ], "params": {}, "width": 200.0 @@ -348,7 +359,7 @@ "Frame.001": { "bl_idname": "NodeFrame", "custom_socket_props": {}, - "height": 311.9542236328125, + "height": 451.6605529785156, "hide": false, "label": "init", "location": [ @@ -356,7 +367,7 @@ 270.9542236328125 ], "params": {}, - "width": 720.0 + "width": 708.5906982421875 }, "Frame.002": { "bl_idname": "NodeFrame", @@ -366,12 +377,12 @@ 0.12654002010822296 ], "custom_socket_props": {}, - "height": 402.0673828125, + "height": 424.2159423828125, "hide": false, "label": "rad", "location": [ - 1948.903564453125, - 994.7980880737305 + 1955.2977313995361, + 1045.9466857910156 ], "params": {}, "use_custom_color": true, @@ -399,7 +410,7 @@ "Frame.004": { "bl_idname": "NodeFrame", "custom_socket_props": {}, - "height": 469.9317626953125, + "height": 513.9317626953125, "hide": false, "label": "shape", "location": [ @@ -412,7 +423,7 @@ "Frame.005": { "bl_idname": "NodeFrame", "custom_socket_props": {}, - "height": 475.2294921875, + "height": 519.2294921875, "hide": false, "label": "leafs", "location": [ @@ -443,12 +454,12 @@ 0.21782340109348297 ], "custom_socket_props": {}, - "height": 433.0198974609375, + "height": 433.1684875488281, "hide": false, "label": "green", "location": [ - 27.87104034423828, - -219.90485382080078 + 34.265207290649414, + -168.75625610351562 ], "params": {}, "use_custom_color": true, @@ -494,12 +505,12 @@ 0.6079999804496765 ], "custom_socket_props": {}, - "height": 932.0673828125, + "height": 932.2159423828125, "hide": false, "label": "color", "location": [ - -30.0, - 75.53160858154297 + -23.605833053588867, + 126.68020629882812 ], "params": {}, "use_custom_color": true, @@ -525,7 +536,16 @@ 0.9200000166893005, 0.9200000166893005 ], - "custom_socket_props": {}, + "custom_socket_props": { + "0": { + "nesting_level": 1, + "pre_processing": "ONE_ITEM" + }, + "1": { + "nesting_level": 1, + "pre_processing": "ONE_ITEM" + } + }, "height": 100.0, "hide": false, "label": "", @@ -539,15 +559,23 @@ "use_custom_color": true, "width": 140.0 }, - "Inset Special": { - "bl_idname": "SvInsetSpecial", - "custom_socket_props": {}, + "Inset Special.001": { + "bl_idname": "SvInsetSpecialMk2", + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", "location": [ - 1324.1441650390625, - 360.9542236328125 + 1312.73486328125, + 399.6605529785156 ], "params": { "distance": 0.019999999552965164, @@ -585,7 +613,7 @@ "hide": false, "label": "", "location": [ - 4178.937225341797, + 4178.936981201172, 164.03279781341553 ], "params": { @@ -627,8 +655,8 @@ "hide": false, "label": "", "location": [ - 2733.0851516723633, - 372.2207565307617 + 2739.4793186187744, + 423.3693542480469 ], "params": { "func_": "MAX" @@ -643,7 +671,7 @@ "label": "", "location": [ 3345.673828125, - 1073.1985473632812 + 1073.198486328125 ], "params": {}, "width": 140.0 @@ -655,7 +683,7 @@ "hide": false, "label": "", "location": [ - 982.6369934082031, + 982.636962890625, -70.78495788574219 ], "params": { @@ -675,8 +703,8 @@ "hide": false, "label": "", "location": [ - 2321.5238723754883, - 436.73082733154297 + 2327.9180393218994, + 487.8794250488281 ], "params": { "new_max": 1.0, @@ -692,7 +720,7 @@ "hide": false, "label": "", "location": [ - 1339.6885070800781, + 1339.6884765625, -70.78495788574219 ], "params": {}, @@ -730,7 +758,7 @@ "label": "", "location": [ 4346.8465576171875, - 1101.6138000488281 + 1101.6137390136719 ], "params": { "base_data_name": "Delta", @@ -958,7 +986,7 @@ "label": "", "location": [ 1633.909740447998, - 289.8028144836426 + 289.80282974243164 ], "params": { "nrotx": 10.0, @@ -1056,7 +1084,7 @@ "label": "", "location": [ 4346.6141357421875, - 755.6145324707031 + 755.614501953125 ], "params": { "count_": 8325, @@ -1082,7 +1110,7 @@ "label": "", "location": [ 3128.7529296875, - 1076.2294311523438 + 1076.2294921875 ], "params": { "count_": 20, @@ -1101,7 +1129,7 @@ "label": "", "location": [ 3985.8509521484375, - 922.4450378417969 + 922.445068359375 ], "params": { "modifiers": true @@ -1115,8 +1143,8 @@ "hide": false, "label": "", "location": [ - 1962.7212600708008, - 403.50341033935547 + 1969.115427017212, + 454.6520080566406 ], "params": { "mode": "Faces" @@ -1330,7 +1358,7 @@ "hide": false, "label": "", "location": [ - 1157.6025085449219, + 1157.6025390625, -70.78495788574219 ], "params": {}, @@ -1403,8 +1431,8 @@ "hide": false, "label": "", "location": [ - 2144.1171340942383, - 342.7903366088867 + 2150.5113010406494, + 393.9389343261719 ], "params": {}, "width": 100.0 @@ -1442,95 +1470,83 @@ }, "update_lists": [ [ - "Evaluate Curve", + "List Join", 0, - "Polyline Viewer.001", - 0 + "3pt Arc", + 1 ], [ - "Arc 3pt (Curve)", + "Inset Special.001", 0, - "Evaluate Curve", + "Area", 0 ], [ - "Polyline Viewer", - 0, - "Object ID Out MK2", - 0 + "Inset Special.001", + 1, + "Area", + 1 ], [ - "Object ID Out MK2", - 3, - "List Length.001", + "Number Range.002", + 0, + "Color in.001", 0 ], [ - "Object ID Out MK2", + "Scale", 0, - "Mesh viewer.001", + "Dual Mesh", 0 ], [ - "Object ID Out MK2", - 3, - "Mesh viewer.001", - 2 - ], - [ - "List Length.001", - 0, - "Number Range.002", + "IcoSphere", + 2, + "Dual Mesh", 2 ], [ - "Number Range.002", + "Arc 3pt (Curve)", 0, - "Color in.001", + "Evaluate Curve", 0 ], [ - "Mesh viewer.001", + "Scalar Math.002", 0, - "Vertex color mk3.001", + "Line", 0 ], [ - "Color in.001", + "Move.001", 0, - "Vertex color mk3.001", - 2 + "List Join", + 0 ], [ - "3pt Arc", + "Move", 0, - "Polyline Viewer", - 0 + "List Join", + 1 ], [ - "Scalar Math.001", + "Move.002", 0, - "Polyline Viewer", + "List Join", 2 ], [ - "Polyline Viewer.001", - 0, - "Polyline Viewer", - 4 + "Reroute.001", + "Output", + "List Length", + "Data" ], [ - "Scalar Math", - 0, - "Scalar Math.001", + "Object ID Out MK2", + 3, + "List Length.001", 0 ], - [ - "List Split", - 0, - "Scalar Math.001", - 1 - ], [ "Number Range.003", 0, @@ -1538,262 +1554,268 @@ 0 ], [ - "Number Range.001", + "Area", 0, - "Scalar Math", + "Logic functions", 0 ], [ - "Scale", + "Switch", 0, - "Dual Mesh", + "Mask to Index", 0 ], [ - "IcoSphere", - 2, - "Dual Mesh", - 2 - ], - [ - "Dual Mesh", - 0, - "Inset Special", - 2 + "Reroute.002", + "Output", + "Mesh viewer", + "vertices" ], [ - "Dual Mesh", - 1, - "Inset Special", - 3 + "Reroute.001", + "Output", + "Mesh viewer", + "faces" ], [ - "IcoSphere", + "Object ID Out MK2", 0, - "Scale", + "Mesh viewer.001", 0 ], [ - "Switch", - 0, - "Mask to Index", - 0 + "Object ID Out MK2", + 3, + "Mesh viewer.001", + 2 ], [ - "Logic functions", + "MultiExtrude Alt from addons", 0, - "Switch", + "Monad", 0 ], [ - "A Number", + "Vector in.001", 0, - "Switch", + "Monad", 1 ], [ - "A Number.001", - 0, - "Switch", + "MultiExtrude Alt from addons", + 1, + "Monad", 2 ], [ - "Area", + "Vector in", 0, - "Logic functions", - 0 + "Monad", + 3 ], [ - "Inset Special", + "A Number.003", 0, - "Area", - 0 - ], - [ - "Inset Special", - 1, - "Area", - 1 + "Monad", + 4 ], [ - "Inset Special", + "Monad", 0, - "MultiExtrude Alt from addons", + "Monad.001", 0 ], [ - "Inset Special", - 1, - "MultiExtrude Alt from addons", + "Vector in.003", + 0, + "Monad.001", 1 ], [ - "Mask to Index", - 0, "MultiExtrude Alt from addons", + 1, + "Monad.001", 2 ], [ - "A Number.002", + "Vector in.002", 0, - "Spiral", + "Monad.001", 3 ], [ - "A Number.005", + "A Number.004", 0, - "Spiral", + "Monad.001", 4 ], [ - "A Number.002", - 0, "Spiral.001", - 3 + 0, + "Move", + 0 ], [ - "A Number.005", + "Spiral", 0, - "Spiral.001", - 4 + "Move.001", + 0 ], [ - "Spiral.001", + "Line", 0, - "Move", + "Move.002", 0 ], [ - "A Number.002", + "Inset Special.001", 0, - "Scalar Math.002", + "MultiExtrude Alt from addons", 0 ], [ - "A Number.005", - 0, - "Scalar Math.002", + "Inset Special.001", + 1, + "MultiExtrude Alt from addons", 1 ], [ - "Scalar Math.002", + "Mask to Index", 0, - "Line", - 0 + "MultiExtrude Alt from addons", + 2 ], [ - "Line", + "List Length", 0, - "Move.002", - 0 + "Number Range", + 2 ], [ - "Spiral", + "List Length.001", 0, - "Move.001", - 0 + "Number Range.002", + 2 ], [ - "Move.001", + "Polyline Viewer", 0, - "List Join", + "Object ID Out MK2", 0 ], [ - "Move", + "3pt Arc", 0, - "List Join", - 1 + "Polyline Viewer", + 0 ], [ - "Move.002", + "Scalar Math.001", 0, - "List Join", + "Polyline Viewer", 2 ], [ - "List Join", + "Polyline Viewer.001", 0, - "3pt Arc", - 1 + "Polyline Viewer", + 4 ], [ - "Curve Mapper", + "Evaluate Curve", 0, - "Color in", + "Polyline Viewer.001", 0 ], [ - "Curve Mapper.001", + "MultiExtrude Alt from addons", + "faces", + "Reroute.001", + "Input" + ], + [ + "Monad.001", + "Vertices", + "Reroute.002", + "Input" + ], + [ + "Number Range.001", 0, - "Color in", - 1 + "Scalar Math", + 0 ], [ - "MultiExtrude Alt from addons", + "Scalar Math", 0, - "Component Analyzer", + "Scalar Math.001", 0 ], [ - "MultiExtrude Alt from addons", - 1, - "Component Analyzer", - 2 + "List Split", + 0, + "Scalar Math.001", + 1 ], [ - "Component Analyzer", + "A Number.002", 0, - "Curve Mapper", + "Scalar Math.002", 0 ], [ - "Map Range", + "A Number.005", 0, - "Curve Mapper.001", - 0 + "Scalar Math.002", + 1 ], [ - "Vector out", - 2, - "Map Range", + "IcoSphere", + 0, + "Scale", 0 ], [ - "Vector out", - 2, - "List Math", - 0 + "A Number.002", + 0, + "Spiral", + 3 ], [ - "Origins", + "A Number.005", 0, - "Vector out", - 0 + "Spiral", + 4 ], [ - "MultiExtrude Alt from addons", + "A Number.002", 0, - "Origins", - 0 + "Spiral.001", + 3 ], [ - "MultiExtrude Alt from addons", - 1, - "Origins", - 2 + "A Number.005", + 0, + "Spiral.001", + 4 ], [ - "Reroute.002", - "Output", - "Mesh viewer", - "vertices" + "Logic functions", + 0, + "Switch", + 0 ], [ - "Reroute.001", - "Output", - "Mesh viewer", - "faces" + "A Number", + 0, + "Switch", + 1 + ], + [ + "A Number.001", + 0, + "Switch", + 2 ], [ "Mesh viewer", @@ -1814,88 +1836,94 @@ 2 ], [ - "Reroute.001", - "Output", - "List Length", - "Data" + "Mesh viewer.001", + 0, + "Vertex color mk3.001", + 0 ], [ - "List Length", + "Color in.001", 0, - "Number Range", + "Vertex color mk3.001", 2 ], [ - "MultiExtrude Alt from addons", - "faces", - "Reroute.001", - "Input" + "Dual Mesh", + 0, + "Inset Special.001", + 0 ], [ - "Monad.001", - "Vertices", - "Reroute.002", - "Input" + "Dual Mesh", + 1, + "Inset Special.001", + 1 ], [ - "Monad", + "Curve Mapper", 0, - "Monad.001", + "Color in", 0 ], [ - "Vector in.003", + "Curve Mapper.001", 0, - "Monad.001", + "Color in", 1 ], + [ + "MultiExtrude Alt from addons", + 0, + "Component Analyzer", + 0 + ], [ "MultiExtrude Alt from addons", 1, - "Monad.001", + "Component Analyzer", 2 ], [ - "Vector in.002", + "Component Analyzer", 0, - "Monad.001", - 3 + "Curve Mapper", + 0 ], [ - "A Number.004", + "Map Range", 0, - "Monad.001", - 4 + "Curve Mapper.001", + 0 ], [ - "MultiExtrude Alt from addons", - 0, - "Monad", + "Vector out", + 2, + "List Math", 0 ], [ - "Vector in.001", + "Vector out", + 2, + "Map Range", + 0 + ], + [ + "MultiExtrude Alt from addons", 0, - "Monad", - 1 + "Origins", + 0 ], [ "MultiExtrude Alt from addons", 1, - "Monad", + "Origins", 2 ], [ - "Vector in", - 0, - "Monad", - 3 - ], - [ - "A Number.003", + "Origins", 0, - "Monad", - 4 + "Vector out", + 0 ] ] -} +} \ No newline at end of file diff --git a/json_examples/Architecture/Coliseum.json b/json_examples/Architecture/Coliseum.json index 2922b00049..f6cf31d42c 100644 --- a/json_examples/Architecture/Coliseum.json +++ b/json_examples/Architecture/Coliseum.json @@ -13,7 +13,7 @@ "Component Analyzer": "Frame", "Exec Node Mod.002": "Frame.005", "Frame.001": "Frame.002", - "Inset Special": "Frame", + "Inset Special.001": "Frame", "List Mask (out)": "Frame.005", "Logic functions": "Frame", "Logic functions.001": "Frame", @@ -194,7 +194,18 @@ }, "Component Analyzer": { "bl_idname": "SvComponentAnalyzerNode", - "custom_socket_props": {}, + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "nesting_level": 3 + }, + "2": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", @@ -235,7 +246,7 @@ "Frame": { "bl_idname": "NodeFrame", "custom_socket_props": {}, - "height": 533.35400390625, + "height": 592.7540283203125, "hide": false, "label": "", "location": [ @@ -243,7 +254,7 @@ 280.7985534667969 ], "params": {}, - "width": 1133.975341796875 + "width": 1137.3447265625 }, "Frame.001": { "bl_idname": "NodeFrame", @@ -310,15 +321,23 @@ "params": {}, "width": 1200.907958984375 }, - "Inset Special": { - "bl_idname": "SvInsetSpecial", - "custom_socket_props": {}, + "Inset Special.001": { + "bl_idname": "SvInsetSpecialMk2", + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", "location": [ - 2782.7352905273438, - 495.6429443359375 + 2789.3944702148438, + 510.554931640625 ], "params": { "inset": 0.800000011920929 @@ -685,6 +704,15 @@ "Select mesh elements by location": { "bl_idname": "SvMeshSelectNodeMk2", "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "nesting_level": 3 + }, + "2": { + "nesting_level": 3 + }, "3": { "prop": [ 0.0, @@ -785,139 +813,109 @@ 0 ], [ - "Inset Special", + "Inset Special.001", 3, "Calculate Mask", 0 ], [ - "Inset Special", + "Inset Special.001", 1, "Calculate Mask", 1 ], [ - "UV Connection", + "Scalar Math.004", 0, - "Inset Special", - 2 + "Circle", + 1 ], [ "UV Connection", - 1, - "Inset Special", - 3 - ], - [ - "Logic functions.003", 0, - "Inset Special", - 4 + "Component Analyzer", + 0 ], [ - "Select mesh elements by location", - 2, - "Logic functions", - 0 + "UV Connection", + 1, + "Component Analyzer", + 2 ], [ - "Profile Parametric Mk3", + "Mesh viewer", 0, - "Matrix Apply (verts)", + "Exec Node Mod.002", 0 ], [ - "Matrix Track To", - 0, - "Matrix Apply (verts)", - 1 + "Inset Special.001", + 1, + "List Mask (out)", + 0 ], [ - "Scalar Math", + "Calculate Mask", 0, - "Number Range", + "List Mask (out)", 1 ], [ - "A Number.002", - 0, - "Number Range", - 2 - ], - [ - "A Number.005", - 0, - "Scalar Math", + "Select mesh elements by location", + 2, + "Logic functions", 0 ], [ - "Number Range", + "Logic functions.002", 0, - "Scalar Math.001", - 0 - ], - [ - "Scalar Math.001", - 1, - "Scalar Math.002", + "Logic functions.001", 0 ], [ - "Scalar Math.001", + "Logic functions", 0, - "Scalar Math.003", - 0 + "Logic functions.001", + 1 ], [ - "Scalar Math.001", - 1, - "Scalar Math.005", + "Component Analyzer", + 0, + "Logic functions.002", 0 ], [ - "Scalar Math.001", + "Logic functions.001", 0, - "Scalar Math.006", + "Logic functions.003", 0 ], [ - "UV Connection", + "Profile Parametric Mk3", 0, - "Select mesh elements by location", + "Matrix Apply (verts)", 0 ], [ - "UV Connection", - 1, - "Select mesh elements by location", - 2 - ], - [ - "Matrix Apply (verts)", + "Matrix Track To", 0, - "UV Connection", - 0 + "Matrix Apply (verts)", + 1 ], [ - "Profile Parametric Mk3", - 2, - "Viewer Index+", + "Circle", + 0, + "Matrix Track To", 0 ], [ - "Profile Parametric Mk3", - 3, - "Viewer Index+", - 4 - ], - [ - "Mesh viewer", + "Circle", 0, - "Exec Node Mod.002", - 0 + "Matrix Track To", + 2 ], [ - "Inset Special", + "Inset Special.001", 0, "Mesh viewer", 0 @@ -934,30 +932,6 @@ "Mesh viewer", 4 ], - [ - "Inset Special", - 1, - "List Mask (out)", - 0 - ], - [ - "Calculate Mask", - 0, - "List Mask (out)", - 1 - ], - [ - "Inset Special", - 0, - "Solidify.001", - 0 - ], - [ - "Inset Special", - 3, - "Solidify.001", - 2 - ], [ "Solidify.001", 0, @@ -977,34 +951,16 @@ 4 ], [ - "UV Connection", - 0, - "Component Analyzer", - 0 - ], - [ - "UV Connection", - 1, - "Component Analyzer", - 2 - ], - [ - "Component Analyzer", - 0, - "Logic functions.002", - 0 - ], - [ - "Logic functions.002", + "Scalar Math", 0, - "Logic functions.001", - 0 + "Number Range", + 1 ], [ - "Logic functions", + "A Number.002", 0, - "Logic functions.001", - 1 + "Number Range", + 2 ], [ "A Number", @@ -1037,16 +993,28 @@ 4 ], [ - "Logic functions.001", + "A Number.005", 0, - "Logic functions.003", + "Scalar Math", 0 ], [ - "Scalar Math.004", + "Number Range", 0, - "Circle", - 1 + "Scalar Math.001", + 0 + ], + [ + "Scalar Math.001", + 1, + "Scalar Math.002", + 0 + ], + [ + "Scalar Math.001", + 0, + "Scalar Math.003", + 0 ], [ "A Number.002", @@ -1055,16 +1023,76 @@ 0 ], [ - "Circle", + "Scalar Math.001", + 1, + "Scalar Math.005", + 0 + ], + [ + "Scalar Math.001", 0, - "Matrix Track To", + "Scalar Math.006", 0 ], [ - "Circle", + "UV Connection", 0, - "Matrix Track To", + "Select mesh elements by location", + 0 + ], + [ + "UV Connection", + 1, + "Select mesh elements by location", + 2 + ], + [ + "Inset Special.001", + 0, + "Solidify.001", + 0 + ], + [ + "Inset Special.001", + 3, + "Solidify.001", 2 + ], + [ + "Matrix Apply (verts)", + 0, + "UV Connection", + 0 + ], + [ + "Profile Parametric Mk3", + 2, + "Viewer Index+", + 0 + ], + [ + "Profile Parametric Mk3", + 3, + "Viewer Index+", + 4 + ], + [ + "UV Connection", + 0, + "Inset Special.001", + 0 + ], + [ + "UV Connection", + 1, + "Inset Special.001", + 1 + ], + [ + "Logic functions.003", + 0, + "Inset Special.001", + 4 ] ] -} +} \ No newline at end of file diff --git a/json_examples/Architecture/Curved_Hexagonal_Truss.json b/json_examples/Architecture/Curved_Hexagonal_Truss.json index 02cac07bd4..4bce0fa8f7 100644 --- a/json_examples/Architecture/Curved_Hexagonal_Truss.json +++ b/json_examples/Architecture/Curved_Hexagonal_Truss.json @@ -7,7 +7,7 @@ "Curve Viewer": "Frame.002", "Curve Viewer.001": "Frame.002", "Dual Mesh": "Frame.002", - "Inset Special": "Frame.001", + "Inset Special.001": "Frame.001", "List Split": "Frame", "List Split.001": "Frame.002", "Mesh Join.001": "Frame.002", @@ -31,7 +31,7 @@ "hide": false, "label": "", "location": [ - -878.9776306152344, + -886.5869064331055, -800.5147476196289 ], "params": { @@ -47,7 +47,7 @@ "hide": false, "label": "", "location": [ - 173.6556396484375, + 166.0463638305664, -564.7157974243164 ], "params": { @@ -57,12 +57,23 @@ }, "Component Analyzer": { "bl_idname": "SvComponentAnalyzerNode", - "custom_socket_props": {}, + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "nesting_level": 3 + }, + "2": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", "location": [ - 594.536376953125, + 586.9271011352539, -1177.153793334961 ], "params": { @@ -82,7 +93,7 @@ "hide": false, "label": "", "location": [ - 1724.8819274902344, + 1717.2726516723633, -923.9109344482422 ], "params": { @@ -104,7 +115,7 @@ "hide": false, "label": "", "location": [ - 1722.6996765136719, + 1715.0904006958008, -1273.923080444336 ], "params": { @@ -121,7 +132,7 @@ "hide": false, "label": "", "location": [ - 374.117919921875, + 366.5086441040039, -1410.1214447021484 ], "params": {}, @@ -134,7 +145,7 @@ "hide": false, "label": "Control Surface", "location": [ - -88.71127319335938, + -96.32054901123047, 107.1540756225586 ], "params": {}, @@ -147,11 +158,11 @@ "hide": false, "label": "Cover", "location": [ - 68.99597930908203, + 61.3867073059082, 56.54072189331055 ], "params": {}, - "width": 806.707763671875 + "width": 792.0017700195312 }, "Frame.002": { "bl_idname": "NodeFrame", @@ -160,21 +171,29 @@ "hide": false, "label": "Structure", "location": [ - 85.90573120117188, + 78.29645538330078, -95.03233337402344 ], "params": {}, "width": 1550.7640380859375 }, - "Inset Special": { - "bl_idname": "SvInsetSpecial", - "custom_socket_props": {}, + "Inset Special.001": { + "bl_idname": "SvInsetSpecialMk2", + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", "location": [ - 762.4587478637695, - -431.1054573059082 + 769.5554695129395, + -379.6045112609863 ], "params": { "inset": 0.550000011920929, @@ -189,7 +208,7 @@ "hide": false, "label": "", "location": [ - -50.175506591796875, + -57.78478240966797, -557.3967056274414 ], "params": { @@ -204,7 +223,7 @@ "hide": false, "label": "", "location": [ - 1040.7282409667969, + 1033.1189651489258, -1097.4737396240234 ], "params": {}, @@ -228,7 +247,7 @@ "hide": false, "label": "", "location": [ - 1460.3860778808594, + 1452.7768020629883, -1012.3106536865234 ], "params": {}, @@ -246,7 +265,7 @@ "hide": false, "label": "", "location": [ - 1369.1665115356445, + 1361.5572395324707, -456.61589431762695 ], "params": { @@ -266,7 +285,7 @@ "hide": false, "label": "", "location": [ - -270.1755065917969, + -277.78478240966797, -568.8144302368164 ], "params": { @@ -310,7 +329,7 @@ "hide": false, "label": "", "location": [ - -677.8556823730469, + -685.464958190918, -482.10892486572266 ], "params": { @@ -329,7 +348,7 @@ "hide": false, "label": "", "location": [ - 1042.3290710449219, + 1034.7197952270508, -949.3807220458984 ], "params": {}, @@ -342,7 +361,7 @@ "hide": false, "label": "", "location": [ - -435.03851318359375, + -442.6477966308594, -1049.250732421875 ], "params": { @@ -362,7 +381,7 @@ "hide": false, "label": "", "location": [ - 591.927001953125, + 584.3177261352539, -1403.2000579833984 ], "params": { @@ -377,7 +396,7 @@ "hide": false, "label": "", "location": [ - -490.1755065917969, + -497.78478240966797, -488.76749420166016 ], "params": { @@ -398,7 +417,7 @@ "hide": false, "label": "", "location": [ - 1237.0633239746094, + 1229.4540481567383, -976.4100799560547 ], "params": { @@ -414,7 +433,7 @@ "hide": false, "label": "", "location": [ - 968.4492263793945, + 960.8399543762207, -417.33220291137695 ], "params": { @@ -433,7 +452,7 @@ "hide": false, "label": "", "location": [ - 829.8252868652344, + 822.2160110473633, -1280.355209350586 ], "params": { @@ -458,7 +477,7 @@ "hide": false, "label": "", "location": [ - 1204.044563293457, + 1196.4352912902832, -212.13191604614258 ], "params": { @@ -535,18 +554,6 @@ "Dual Mesh", 2 ], - [ - "Bend object along surface", - 0, - "Inset Special", - 2 - ], - [ - "Polygon Grid", - 3, - "Inset Special", - 3 - ], [ "Move", 0, @@ -565,6 +572,18 @@ "List Split.001", 0 ], + [ + "Segment", + 0, + "Mesh Join.001", + 0 + ], + [ + "Segment", + 1, + "Mesh Join.001", + 1 + ], [ "Solidify.001", 0, @@ -626,13 +645,13 @@ 1 ], [ - "Inset Special", + "Inset Special.001", 0, "Solidify.001", 0 ], [ - "Inset Special", + "Inset Special.001", 1, "Solidify.001", 2 @@ -656,15 +675,15 @@ 2 ], [ - "Segment", + "Bend object along surface", 0, - "Mesh Join.001", + "Inset Special.001", 0 ], [ - "Segment", - 1, - "Mesh Join.001", + "Polygon Grid", + 3, + "Inset Special.001", 1 ] ] diff --git a/json_examples/Fields/Biological.json b/json_examples/Fields/Biological.json index c9e007ce42..a6a1b36068 100644 --- a/json_examples/Fields/Biological.json +++ b/json_examples/Fields/Biological.json @@ -8,7 +8,7 @@ "Delaunay 2D": "Frame.004", "Exec Node Mod.002": "Frame.007", "Exec Node Mod.003": "Frame.006", - "Inset Special": "Frame.006", + "Inset Special.001": "Frame.006", "Line": "Frame", "List Mask (out)": "Frame.004", "List Split": "Frame.005", @@ -245,7 +245,7 @@ "Frame.005": { "bl_idname": "NodeFrame", "custom_socket_props": {}, - "height": 464.1585998535156, + "height": 507.1585998535156, "hide": false, "label": "", "location": [ @@ -258,7 +258,7 @@ "Frame.006": { "bl_idname": "NodeFrame", "custom_socket_props": {}, - "height": 384.34100341796875, + "height": 445.52374267578125, "hide": false, "label": "", "location": [ @@ -266,7 +266,7 @@ -133.2216796875 ], "params": {}, - "width": 837.9462890625 + "width": 832.9267578125 }, "Frame.007": { "bl_idname": "NodeFrame", @@ -281,15 +281,23 @@ "params": {}, "width": 666.05029296875 }, - "Inset Special": { - "bl_idname": "SvInsetSpecial", - "custom_socket_props": {}, + "Inset Special.001": { + "bl_idname": "SvInsetSpecialMk2", + "custom_socket_props": { + "0": { + "is_mandatory": true + }, + "1": { + "is_mandatory": true, + "nesting_level": 3 + } + }, "height": 100.0, "hide": false, "label": "", "location": [ - 1974.7886352539062, - 163.99664306640625 + 1979.8081665039062, + 203.52374267578125 ], "params": { "distance": -0.5, @@ -343,7 +351,7 @@ "label": "", "location": [ 2246.443603515625, - -242.3125228881836 + -242.31253814697266 ], "params": { "split": 90 @@ -386,8 +394,8 @@ 610.067024230957 ], "params": { - "is_smooth_mesh": true, - "material": "sv_material.001" + "base_data_name": "Iota", + "is_smooth_mesh": true }, "use_custom_color": true, "width": 140.0 @@ -409,8 +417,7 @@ ], "params": { "base_data_name": "Gamma", - "is_smooth_mesh": true, - "material": "sv_material.002" + "is_smooth_mesh": true }, "use_custom_color": true, "width": 140.0 @@ -498,7 +505,7 @@ "label": "", "location": [ 2420.7529296875, - -230.38065338134766 + -230.38064575195312 ], "params": { "count_": 40, @@ -529,12 +536,11 @@ "label": "", "location": [ 2608.053955078125, - -217.84137725830078 + -217.84136962890625 ], "params": { "base_data_name": "Beta", "is_merge": true, - "material": "sv_material", "preview_resolution_u": 5 }, "use_custom_color": true, @@ -687,226 +693,226 @@ }, "update_lists": [ [ - "Move.001", + "Vector Field Lines", 0, - "Attractor Field", + "Area", 0 ], [ - "Vector out", - 2, - "Scalar Math.001", - 0 + "Delaunay 2D", + 0, + "Area", + 1 ], [ - "Vector in", - 0, "Move.001", + 0, + "Attractor Field", 0 ], [ - "Vector out", + "Vector Field Lines", 0, - "Vector in", + "Delaunay 2D", 0 ], [ - "Vector out", - 1, - "Vector in", - 1 + "Mesh viewer", + 0, + "Exec Node Mod.002", + 0 ], [ - "Scalar Math.001", + "Mesh viewer.001", 0, - "Vector in", - 2 + "Exec Node Mod.003", + 0 ], [ - "Random Vector", + "Delaunay 2D", 0, - "Vector out", + "List Mask (out)", 0 ], [ - "A Number.001", + "Logic functions", 0, - "Scalar Math", + "List Mask (out)", + 1 + ], + [ + "Vector Field Lines", + 0, + "List Split", 0 ], [ "A Number", 0, - "Scalar Math", + "List Split", 1 ], [ - "Move", + "Area", 0, - "Randomize", + "Logic functions", 0 ], [ - "Line", + "Vector Field Lines", 0, - "Move", + "Mesh viewer", 0 ], [ - "Line", + "List Mask (out)", + 3, + "Mesh viewer", + 2 + ], + [ + "Inset Special.001", 0, - "RBF Vector Field", + "Mesh viewer.001", 0 ], [ - "Randomize", - 0, - "RBF Vector Field", - 1 + "Inset Special.001", + 1, + "Mesh viewer.001", + 2 ], [ - "Vector Field Math", + "Line", 0, - "Vector Field Lines", + "Move", 0 ], [ - "Line", + "Vector in", 0, - "Vector Field Lines", - 1 + "Move.001", + 0 ], [ - "Scalar Math", + "List Split", 0, - "Vector Field Lines", - 2 + "Polyline Viewer", + 0 ], [ - "A Number", + "Number Range", 0, - "Vector Field Lines", - 3 + "Polyline Viewer", + 2 ], [ - "Attractor Field", + "Line", 0, - "Vector Field Math", + "RBF Vector Field", 0 ], [ - "RBF Vector Field", + "Randomize", 0, - "Vector Field Math", + "RBF Vector Field", 1 ], [ - "Vector Field Lines", + "Move", 0, - "Delaunay 2D", + "Randomize", 0 ], [ - "Delaunay 2D", + "A Number.001", 0, - "List Mask (out)", + "Scalar Math", 0 ], [ - "Logic functions", + "A Number", 0, - "List Mask (out)", + "Scalar Math", 1 ], [ - "Area", - 0, - "Logic functions", + "Vector out", + 2, + "Scalar Math.001", 0 ], [ - "Vector Field Lines", + "Vector Field Math", 0, - "Area", + "Vector Field Lines", 0 ], [ - "Delaunay 2D", + "Line", 0, - "Area", + "Vector Field Lines", 1 ], [ - "Vector Field Lines", + "Scalar Math", 0, - "Inset Special", + "Vector Field Lines", 2 ], [ - "List Mask (out)", - 4, - "Inset Special", + "A Number", + 0, + "Vector Field Lines", 3 ], [ - "Inset Special", + "Attractor Field", 0, - "Mesh viewer.001", + "Vector Field Math", 0 ], [ - "Inset Special", - 1, - "Mesh viewer.001", - 2 + "RBF Vector Field", + 0, + "Vector Field Math", + 1 ], [ - "Mesh viewer.001", + "Vector out", 0, - "Exec Node Mod.003", + "Vector in", 0 ], [ - "Vector Field Lines", - 0, - "Mesh viewer", - 0 + "Vector out", + 1, + "Vector in", + 1 ], [ - "List Mask (out)", - 3, - "Mesh viewer", + "Scalar Math.001", + 0, + "Vector in", 2 ], [ - "Mesh viewer", + "Random Vector", 0, - "Exec Node Mod.002", + "Vector out", 0 ], [ "Vector Field Lines", 0, - "List Split", + "Inset Special.001", 0 ], [ - "A Number", - 0, - "List Split", + "List Mask (out)", + 4, + "Inset Special.001", 1 - ], - [ - "List Split", - 0, - "Polyline Viewer", - 0 - ], - [ - "Number Range", - 0, - "Polyline Viewer", - 2 ] ] } \ No newline at end of file diff --git a/nodes/CAD/inset_special_mk2.py b/nodes/CAD/inset_special_mk2.py new file mode 100644 index 0000000000..0e2911a30e --- /dev/null +++ b/nodes/CAD/inset_special_mk2.py @@ -0,0 +1,205 @@ +# ##### 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 random +from mathutils import Vector +import bpy + + +from mathutils import Vector +from bpy.props import BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, EnumProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import ( + updateNode, Vector_generate, zip_long_repeat, make_repeaters,repeat_last_for_length, + repeat_last) +from sverchok.utils.mesh.inset_faces import inset_special_np, inset_special_mathutils +from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode + + +class SvInsetSpecialMk2(bpy.types.Node, SverchCustomTreeNode, SvRecursiveNode): + """ + Triggers: or Extrude (Fast) + Tooltip: Fast Inset or extrude geometry + + """ + + bl_idname = 'SvInsetSpecialMk2' + bl_label = 'Inset Special' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_INSET' + + implentation_items = [ + ('mathutils', 'Mathutils', 'Slower (Legacy. Face order may differ with new implementation)', 0), + ('numpy', 'Numpy', 'Faster', 1)] + implementation: bpy.props.EnumProperty( + name='Implementation', + items=implentation_items, + default='numpy', + update=updateNode + ) + inset: FloatProperty( + name='Inset', + description='inset amount', + min = 0.0, + default=0.1, update=updateNode) + distance: FloatProperty( + name='Distance', + description='Distance', + default=0.0, update=updateNode) + + ignore: IntProperty(name='Ignore', description='skip polygons', default=0, update=updateNode) + make_inner: IntProperty(name='Make Inner', description='Make inner polygon', default=1, update=updateNode) + + zero_modes = [ + ("SKIP", "Skip", "Do not process such faces", 0), + ("FAN", "Fan", "Make a fan-like structure from such faces", 1) + ] + + zero_mode: EnumProperty( + name="Zero inset faces", + description="What to do with faces when inset is equal to zero", + default="SKIP", + items=zero_modes, + update=updateNode) + offset_modes = [ + ("CENTER", "Center", "Inset is measured as a proportion between the corners and the center of the polygon", 0), + ("SIDES", "Sides", "Inset is measured as a constant distance to the sides of the polygon", 1) + ] + offset_mode: EnumProperty( + name="Offset Mode", + description="How to interpret inset distance", + default="CENTER", + items=offset_modes, + update=updateNode) + proportional: BoolProperty( + name='Proportional', + description='Multiply Inset by face perimeter', + default=False, + update=updateNode + ) + concave_support: BoolProperty( + name='Concave Support', + description='Support concave polygons', + default=False, + update=updateNode + ) + + replacement_nodes = [ + ('SvExtrudeSeparateNode', + dict(Vertices='Vertices', Polygons='Polygons'), + dict(vertices='Vertices', polygons='Polygons')), + ('SvExtrudeSeparateLiteNode', + dict(Vertices='Vertices', Polygons='Polygons'), + dict(vertices='Vertices', polygons='Polygons')), + ('SvInsetFaces', + dict(Vertices='Verts', Polygons='Faces'), + dict(vertices='Verts', polygons='Faces')) + ] + + def sv_init(self, context): + i = self.inputs + self.sv_new_input('SvVerticesSocket', "Vertices", is_mandatory=True, nesting_level=3) + self.sv_new_input('SvStringsSocket', "Polygons", is_mandatory=True, nesting_level=3) + i.new('SvStringsSocket', 'inset').prop_name = 'inset' + i.new('SvStringsSocket', 'distance').prop_name = 'distance' + i.new('SvStringsSocket', 'ignore').prop_name = 'ignore' + i.new('SvStringsSocket', 'make_inner').prop_name = 'make_inner' + i.new('SvVerticesSocket', 'Custom normal') + + o = self.outputs + o.new('SvVerticesSocket', 'vertices') + o.new('SvStringsSocket', 'polygons') + o.new('SvStringsSocket', 'ignored') + o.new('SvStringsSocket', 'inset') + o.new('SvStringsSocket', 'original verts idx') + o.new('SvStringsSocket', 'original face idx') + o.new('SvStringsSocket', 'pols group') + o.new('SvStringsSocket', 'new verts mask') + + def draw_buttons(self, context, layout): + layout.prop(self, 'offset_mode') + if self.offset_mode == 'SIDES': + layout.prop(self, 'proportional') + def draw_buttons_ext(self, context, layout): + + layout.prop(self, "list_match") + layout.prop(self, "zero_mode") + layout.prop(self, "implementation") + layout.prop(self, "concave_support") + + def process_data(self, params): + i = self.inputs + o = self.outputs + + output = [[] for s in self.outputs] + output_old_face_id = o['original face idx'].is_linked + output_old_vert_id = o['original verts idx'].is_linked + output_pols_groups = o['pols group'].is_linked + output_new_verts_mask = o['new verts mask'].is_linked + for v, p, inset_rates_s, distance_vals_s, ignores_s, make_inners_s, custom_normals in zip_long_repeat(*params): + if self.implementation == 'mathutils': + + inset_rates, distance_vals, ignores, make_inners = make_repeaters([inset_rates_s, distance_vals_s, ignores_s, make_inners_s]) + + func_args = { + 'vertices': [Vector(vec) for vec in v], + 'faces': p, + 'inset_rates': inset_rates, + 'distances': distance_vals, + 'ignores': ignores, + 'make_inners': make_inners, + 'zero_mode': self.zero_mode + } + res = inset_special_mathutils(**func_args) + else: + func_args = { + 'vertices': v, + 'faces': p, + 'inset_rates': inset_rates_s, + 'distances': distance_vals_s, + 'ignores': ignores_s, + 'make_inners': make_inners_s, + 'custom_normals': custom_normals, + 'zero_mode': self.zero_mode, + 'offset_mode': self.offset_mode, + 'proportional':self.proportional, + 'concave_support':self.concave_support, + 'output_old_face_id':output_old_face_id, + 'output_old_v_id':output_old_vert_id, + 'output_pols_groups':output_pols_groups, + 'output_new_verts_mask':output_new_verts_mask + } + res = inset_special_np(**func_args) + if not res: + res = v, p, [], [], [], [], [], [] + + for r, so in zip(res, output): + so.append(r) + + return output + + + + +def register(): + bpy.utils.register_class(SvInsetSpecialMk2) + + +def unregister(): + bpy.utils.unregister_class(SvInsetSpecialMk2) diff --git a/nodes/modifier_change/extrude_separate.py b/nodes/modifier_change/extrude_separate.py index ed7ae3c8aa..655429d049 100644 --- a/nodes/modifier_change/extrude_separate.py +++ b/nodes/modifier_change/extrude_separate.py @@ -91,8 +91,8 @@ def update_mode(self, context): replacement_nodes = [ ('SvExtrudeSeparateLiteNode', None, None), - ('SvInsetSpecial', - dict(Vertices='vertices', Polygons='polygons'), + ('SvInsetSpecialMk2', + dict(Vertices='Vertices', Polygons='Polygons'), dict(Vertices='vertices', Polygons='polygons')), ('SvInsetFaces', dict(Vertices='Verts', Polygons='Faces'), diff --git a/nodes/modifier_change/extrude_separate_lite.py b/nodes/modifier_change/extrude_separate_lite.py index 07a7f1aa95..04d9d818c7 100644 --- a/nodes/modifier_change/extrude_separate_lite.py +++ b/nodes/modifier_change/extrude_separate_lite.py @@ -36,8 +36,8 @@ class SvExtrudeSeparateLiteNode(bpy.types.Node, SverchCustomTreeNode): replacement_nodes = [ ('SvExtrudeSeparateNode', None, None), - ('SvInsetSpecial', - dict(Vertices='vertices', Polygons='polygons'), + ('SvInsetSpecialMk2', + dict(Vertices='Vertices', Polygons='Polygons'), dict(Vertices='vertices', Polygons='polygons')), ('SvInsetFaces', dict(Vertices='Verts', Polygons='Faces'), diff --git a/nodes/modifier_change/inset_faces.py b/nodes/modifier_change/inset_faces.py index 507e60dc11..79de154a72 100644 --- a/nodes/modifier_change/inset_faces.py +++ b/nodes/modifier_change/inset_faces.py @@ -343,8 +343,8 @@ class SvInsetFaces(bpy.types.Node, SverchCustomTreeNode): ('SvExtrudeSeparateLiteNode', dict(Verts='Vertices', Faces='Polygons'), dict(Verts='Vertices', Faces='Polygons')), - ('SvInsetSpecial', - dict(Verts='vertices', Faces='polygons'), + ('SvInsetSpecialMk2', + dict(Verts='Vertices', Faces='Polygons'), dict(Verts='vertices', Faces='polygons')), ] diff --git a/nodes/CAD/inset_special.py b/old_nodes/inset_special.py similarity index 96% rename from nodes/CAD/inset_special.py rename to old_nodes/inset_special.py index ec43f8bad8..75c4b80f6e 100644 --- a/nodes/CAD/inset_special.py +++ b/old_nodes/inset_special.py @@ -203,15 +203,9 @@ class SvInsetSpecial(bpy.types.Node, SverchCustomTreeNode): # default=(0,0,1), update=updateNode) replacement_nodes = [ - ('SvExtrudeSeparateNode', + ('SvInsetSpecialMk2', dict(vertices='Vertices', polygons='Polygons'), - dict(vertices='Vertices', polygons='Polygons')), - ('SvExtrudeSeparateLiteNode', - dict(vertices='Vertices', polygons='Polygons'), - dict(vertices='Vertices', polygons='Polygons')), - ('SvInsetFaces', - dict(vertices='Verts', polygons='Faces'), - dict(vertices='Verts', polygons='Faces')) + dict(vertices='vertices', polygons='polygons')), ] def sv_init(self, context): diff --git a/tests/references/monad_1.json b/tests/references/monad_1.json index e724354da5..0c91f5c655 100644 --- a/tests/references/monad_1.json +++ b/tests/references/monad_1.json @@ -1,21 +1,21 @@ { - "export_version": "0.079", + "export_version": "0.10", "framed_nodes": {}, "groups": { - "Monad": "{\"nodes\": {\"Group Inputs Exp\": {\"params\": {\"node_kind\": \"outputs\"}, \"bl_idname\": \"SvGroupInputsNodeExp\", \"outputs\": [[\"Num X\", \"SvStringsSocket\"]], \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [-19.0225830078125, 252.48233032226562], \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true}, \"Group Outputs Exp\": {\"params\": {\"node_kind\": \"inputs\"}, \"bl_idname\": \"SvGroupOutputsNodeExp\", \"inputs\": [[\"vertices\", \"SvVerticesSocket\"], [\"polygons\", \"SvStringsSocket\"]], \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [590.9774169921875, 252.48233032226562], \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true}, \"Plane MK2\": {\"params\": {\"center\": 1, \"numx\": 3, \"numy\": 3}, \"bl_idname\": \"SvPlaneNodeMK2\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [190.9774169921875, 234.9845428466797], \"color\": [0.0, 0.5, 0.5], \"use_custom_color\": true}, \"Inset Special\": {\"params\": {\"distance\": 1.46999990940094, \"inset\": 0.6700000166893005, \"make_inner\": 0}, \"bl_idname\": \"SvInsetSpecial\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [410.9774169921875, 269.9801025390625]}}, \"groups\": {}, \"framed_nodes\": {}, \"update_lists\": [[\"Group Inputs Exp\", 0, \"Plane MK2\", 0], [\"Group Inputs Exp\", 0, \"Plane MK2\", 1], [\"Plane MK2\", 0, \"Inset Special\", 2], [\"Plane MK2\", 2, \"Inset Special\", 3], [\"Inset Special\", 0, \"Group Outputs Exp\", 0], [\"Inset Special\", 1, \"Group Outputs Exp\", 1]], \"export_version\": \"0.079\", \"bl_idname\": \"SverchGroupTreeType\", \"cls_bl_idname\": \"SvGroupNodeMonad_140482759202207\"}" + "Monad": "{\"export_version\": \"0.10\", \"framed_nodes\": {}, \"groups\": {}, \"nodes\": {\"Group Inputs Exp\": {\"bl_idname\": \"SvGroupInputsNodeExp\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [-25.839576721191406, 252.48233032226562], \"params\": {\"node_kind\": \"outputs\"}, \"custom_socket_props\": {}, \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true, \"outputs\": [[\"Num X\", \"SvStringsSocket\"]]}, \"Group Outputs Exp\": {\"bl_idname\": \"SvGroupOutputsNodeExp\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [584.160400390625, 252.48233032226562], \"params\": {\"node_kind\": \"inputs\"}, \"custom_socket_props\": {}, \"color\": [0.8308190107345581, 0.911391019821167, 0.7545620203018188], \"use_custom_color\": true, \"inputs\": [[\"vertices\", \"SvVerticesSocket\"], [\"polygons\", \"SvStringsSocket\"]]}, \"Plane MK2\": {\"bl_idname\": \"SvPlaneNodeMK2\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [184.16041564941406, 234.9845428466797], \"params\": {\"center\": true, \"numx\": 3, \"numy\": 3}, \"custom_socket_props\": {}, \"color\": [0.0, 0.5, 0.5], \"use_custom_color\": true}, \"Inset Special.001\": {\"bl_idname\": \"SvInsetSpecialMk2\", \"height\": 100.0, \"width\": 140.0, \"label\": \"\", \"hide\": false, \"location\": [404.1604309082031, 269.9801025390625], \"params\": {\"distance\": 1.46999990940094, \"inset\": 0.6700000166893005, \"make_inner\": 0}, \"custom_socket_props\": {\"0\": {\"is_mandatory\": true}, \"1\": {\"is_mandatory\": true, \"nesting_level\": 3}}}}, \"update_lists\": [[\"Inset Special.001\", 0, \"Group Outputs Exp\", 0], [\"Inset Special.001\", 1, \"Group Outputs Exp\", 1], [\"Group Inputs Exp\", 0, \"Plane MK2\", 0], [\"Group Inputs Exp\", 0, \"Plane MK2\", 1], [\"Plane MK2\", 0, \"Inset Special.001\", 0], [\"Plane MK2\", 2, \"Inset Special.001\", 1]], \"bl_idname\": \"SverchGroupTreeType\", \"cls_bl_idname\": \"SvGroupNodeMonad_140482759202207\"}" }, "nodes": { "A Number": { "bl_idname": "SvNumberNode", + "custom_socket_props": {}, "height": 100.0, "hide": false, "label": "", "location": [ - -17.99714469909668, - 125.99751281738281 + 55.772071838378906, + 134.84036254882812 ], "params": { - "float_": 0.0, "int_": 4, "selected_mode": "int" }, @@ -28,18 +28,25 @@ 0.911391019821167, 0.7545620203018188 ], + "custom_socket_props": {}, "height": 100.0, "hide": false, "label": "", "location": [ - 300.9774169921875, - 252.48233032226562 + 374.74664306640625, + 261.3251953125 ], "params": { "all_props": { "cls_bl_idname": "SvGroupNodeMonad_140482759202207", "float_props": {}, "int_props": { + "ints_1_numx": { + "default": 3, + "description": "Number of vertices along X", + "min": 2, + "name": "N Verts X" + }, "numx": { "default": 3, "description": "Number of vertices along X", @@ -56,7 +63,7 @@ "Num X", "SvStringsSocket", { - "prop_name": "numx" + "prop_name": "ints_1_numx" } ] ], @@ -71,7 +78,6 @@ ] ] }, - "loops": 0, "monad": "Monad" }, "use_custom_color": true, @@ -84,12 +90,13 @@ 0.30000001192092896, 0.0 ], + "custom_socket_props": {}, "height": 100.0, "hide": false, "label": "", "location": [ - 630.9774169921875, - 281.08734130859375 + 704.7466430664062, + 289.9302062988281 ], "params": { "selected_draw_mode": "facet" @@ -118,4 +125,4 @@ 2 ] ] -} +} \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py index e78439bdf3..3a3848dfe1 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -133,7 +133,7 @@ def app_handler_ops(append=None, remove=None): "avl_tree", "sv_nodeview_draw_helper", "sv_font_xml_parser", "exception_drawing_with_bgl", "wfc_algorithm", "handling_nodes", "handle_blender_data", "nodes_mixins.generating_objects", "nodes_mixins.show_3d_properties", "modules_inspection", "sv_json_export", "sv_json_import", - "meshes", "tree_walk", "mesh_functions", + "meshes", "tree_walk", "mesh_functions", 'mesh.inset_faces', 'mesh.extrude_edges', # UI text editor ui "text_editor_submenu", "text_editor_plugins", # UI operators and tools diff --git a/utils/mesh/inset_faces.py b/utils/mesh/inset_faces.py new file mode 100644 index 0000000000..f11b4f20e3 --- /dev/null +++ b/utils/mesh/inset_faces.py @@ -0,0 +1,599 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE + +import mathutils +from mathutils import Vector +import numpy as np +from numpy import( + arange as np_arange, + array as np_array, + concatenate as np_concatenate, + newaxis as np_newaxis, + repeat as np_repeat, + roll as np_roll, + vectorize as np_vectorize, +) +from sverchok.utils.math import np_dot + +from sverchok.data_structure import numpy_full_list, has_element + +def vector_length(arr): + return np.sqrt(arr[:, 0]**2 + arr[:, 1]**2 + arr[:, 2]**2) + +def normalize_v3(arr): + ''' Normalize a numpy array of 3 component vectors shape=(n,3) ''' + lens = vector_length(arr) + mask = lens!= 0 + arr[mask, 0] /= lens + arr[mask, 1] /= lens + arr[mask, 2] /= lens + return arr + +def face_normals(v_pols, non_planar=True): + sides = v_pols.shape[1] + if v_pols.shape[1] != 3 and non_planar: + normals = np.zeros((len(v_pols), 3), dtype=float) + for i in range(sides-2): + normals += normalize_v3(np.cross(v_pols[::, 1] - v_pols[::, 0], v_pols[::, (2 + i) % sides] - v_pols[::, 0] )) + else: + normals = np.cross(v_pols[::, 1] - v_pols[::, 0], v_pols[::, 2] - v_pols[::, 0] ) + normalize_v3(normals) + return normals + +def face_perimeter(v_pols): + sides = v_pols.shape[1] + perimeter = np.zeros((len(v_pols)), dtype=float) + for i in range(sides-2): + perimeter += vector_length(v_pols[:, i,:] - v_pols[:, i-1,:]) + return perimeter + +def prepare_data(faces, distances, ignores, make_inners, zero_mode='SKIP', output_old_face_id=False): + invert_face_mask = numpy_full_list(np_array(ignores, dtype=bool), len(faces)) + np_faces_mask = np.invert(invert_face_mask) + # np_faces_mask = numpy_full_list(np.invert(np_array(ignores, dtype=bool)), len(faces)) + + if not any(np_faces_mask): + return + + np_faces = np_array(faces) + if output_old_face_id: + np_faces_id = np.arange(len(faces)) + + np_inset_rate = numpy_full_list(inset_rates, len(faces)) + zero_inset = np_inset_rate == 0 + inset_faces_id, fan_faces_id = [],[] + if zero_mode == 'SKIP': + np_faces_mask[zero_inset] = False + invert_face_mask[zero_inset] = True + new_ignores = np_faces[invert_face_mask].tolist() + if output_old_face_id: + ignores_id = np_faces_id[invert_face_mask].tolist() + new_ignores = np_faces[invert_face_mask].tolist() + else: + new_ignores = np_faces[invert_face_mask].tolist() + + np_distances = numpy_full_list(distances, len(faces))[np_faces_mask] + if output_old_face_id: + inset_faces_id = np_faces_id[np_faces_mask] + else: # FAN + if output_old_face_id: + ignores_maks = np.invert(np_faces_mask) + ignores_id = np_faces_id[ignores_maks].tolist() + new_ignores = np_faces[ignores_maks].tolist() + else: + new_ignores = np_faces[np.invert(np_faces_mask)].tolist() + np_faces_mask[zero_inset] = False + + np_all_distances = numpy_full_list(distances, len(faces)) + np_distances = np_all_distances[np_faces_mask] + + fan_faces = np_faces[zero_inset] + fan_distances = np_all_distances[zero_inset] + if output_old_face_id: + inset_faces_id = np_faces_id[np_faces_mask] + fan_faces_id = np_faces_id[zero_inset] + + + + np_inset_rate = np_inset_rate[np_faces_mask] + # np_inset_rate = numpy_full_list(inset_rates, len(faces))[np_faces_mask] + np_make_inners = numpy_full_list(make_inners, len(faces)).astype(bool)[np_faces_mask] + np_pols = np_faces[np_faces_mask] + + return np_pols, np_make_inners, np_inset_rate, np_distances, fan_faces, fan_distances + +def sides_mode_prepare_data(np_faces, np_faces_mask, invert_face_mask, distances, np_inset_rate, make_inners, np_faces_id): + inset_pols = np_faces[np_faces_mask] + np_distances = numpy_full_list(distances, len(np_faces))[np_faces_mask] + np_inset_rate = np_inset_rate[np_faces_mask] + np_make_inners = numpy_full_list(make_inners, len(np_faces)).astype(bool)[np_faces_mask] + new_ignores = np_faces[invert_face_mask].tolist() + if output_old_face_id: + ignores_id = np_faces_id[invert_face_mask].tolist() + inset_faces_id = np_faces_id[np_faces_mask] + else: + ignores_id = [] + return inset_pols, np_inset_rate, np_distances, np_make_inners, new_ignores, ignores_id, inset_faces_id + +def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inners, custom_normals, + zero_mode="SKIP", offset_mode='CENTER', proportional=False, concave_support=True, + output_old_face_id=False, output_old_v_id=False, + output_pols_groups=False, output_new_verts_mask=False): + + if len(faces) == 0: + return + new_faces, new_ignores, new_insets = [], [], [] + original_face_ids, original_vertex_id, new_pols_groups, new_verts_mask = [], [], [], [] + inset_faces_id, fan_faces_id, ignores_id, fan_faces = [], [], [], [] + + np_verts = vertices if isinstance(vertices, np.ndarray) else np.array(vertices) + + invert_face_mask = numpy_full_list(np_array(ignores, dtype=bool), len(faces)) + np_faces_mask = np.invert(invert_face_mask) + + if not any(np_faces_mask): + return + + np_faces = np_array(faces) + np_faces_id = np.arange(len(faces)) if output_old_face_id else [] + np_inset_rate = numpy_full_list(inset_rates, len(faces)) + if has_element(custom_normals): + np_custom_normals = numpy_full_list(custom_normals, len(faces)) + use_custom_normals = True + else: + np_custom_normals = [] + use_custom_normals = False + + if offset_mode == 'CENTER': + zero_inset = np_inset_rate == 0 + if zero_mode == 'SKIP': + np_faces_mask[zero_inset] = False + invert_face_mask[zero_inset] = True + + inset_pols = np_faces[np_faces_mask] + np_distances = numpy_full_list(distances, len(faces))[np_faces_mask] + np_inset_rate = np_inset_rate[np_faces_mask] + np_make_inners = numpy_full_list(make_inners, len(faces)).astype(bool)[np_faces_mask] + new_ignores = np_faces[invert_face_mask].tolist() + + if output_old_face_id: + ignores_id = np_faces_id[invert_face_mask].tolist() + inset_faces_id = np_faces_id[np_faces_mask] + + else: # FAN + if output_old_face_id: + ignores_maks = np.invert(np_faces_mask) + ignores_id = np_faces_id[ignores_maks].tolist() + new_ignores = np_faces[ignores_maks].tolist() + else: + new_ignores = np_faces[np.invert(np_faces_mask)].tolist() + + np_faces_mask[zero_inset] = False + inset_pols = np_faces[np_faces_mask] + np_make_inners = numpy_full_list(make_inners, len(faces)).astype(bool)[np_faces_mask] + + np_all_distances = numpy_full_list(distances, len(faces)) + np_distances = np_all_distances[np_faces_mask] + np_inset_rate = np_inset_rate[np_faces_mask] + fan_faces = np_faces[zero_inset] + fan_distances = np_all_distances[zero_inset] + if output_old_face_id: + inset_faces_id = np_faces_id[np_faces_mask] + fan_faces_id = np_faces_id[zero_inset] + else: #SIDES mode + inset_pols = np_faces[np_faces_mask] + np_distances = numpy_full_list(distances, len(faces))[np_faces_mask] + np_inset_rate = np_inset_rate[np_faces_mask] + np_make_inners = numpy_full_list(make_inners, len(faces)).astype(bool)[np_faces_mask] + new_ignores = np_faces[invert_face_mask].tolist() + fan_faces = [] + if output_old_face_id: + ignores_id = np_faces_id[invert_face_mask].tolist() + inset_faces_id = np_faces_id[np_faces_mask] + + + common_args = { + 'use_custom_normals': use_custom_normals, + 'output_old_v_id': output_old_v_id, + 'output_old_face_id': output_old_face_id, + 'output_pols_groups': output_pols_groups + } + new_verts = np_verts.tolist() + if output_old_v_id: + original_vertex_id = list(range(len(vertices))) + if output_new_verts_mask: + new_verts_mask.extend([0]*len(new_verts)) + + variable_pols = inset_pols.dtype == 'object' + np_len = np_vectorize(len) + index_offset = 0 + + if len(inset_pols) > 0: + if variable_pols: + lens = np_len(inset_pols) + pol_types = np.unique(lens) + + else: + pol_types = [inset_pols.shape[1]] + + + for pol_sides in pol_types: + if variable_pols: + mask = lens == pol_sides + pols_group = np_array(inset_pols[mask].tolist(), dtype=int) + res = inset_regular_pols(np_verts, pols_group, + np_distances[mask], + np_inset_rate[mask], + np_make_inners[mask], + inset_faces_id[mask] if output_old_face_id else [], + np_custom_normals[mask] if use_custom_normals else [], + offset_mode=offset_mode, proportional=proportional, + concave_support=concave_support, + index_offset=index_offset, + **common_args) + + else: + res = inset_regular_pols(np_verts, inset_pols, + np_distances, + np_inset_rate, + np_make_inners, + inset_faces_id if output_old_face_id else [], + np_custom_normals if use_custom_normals else [], + offset_mode=offset_mode, proportional=proportional, + concave_support=concave_support, + index_offset=index_offset, + **common_args) + index_offset += len(res[0]) + new_verts.extend(res[0]) + new_faces.extend(res[1]) + new_insets.extend(res[2]) + original_vertex_id.extend(res[3]) + original_face_ids.extend(res[4]) + new_pols_groups.extend(res[5]) + if output_new_verts_mask: + new_verts_mask.extend([1]*len(res[0])) + + if zero_mode == 'FAN' and len(fan_faces) > 0: + if variable_pols: + lens = np_len(fan_faces) + pol_types = np.unique(lens) + else: + pol_types = [inset_pols.shape[1]] + for pol_sides in pol_types: + if variable_pols: + mask = lens == pol_sides + pols_group = np_array(fan_faces[mask].tolist(), dtype=int) + res = fan_regular_pols( + np_verts, pols_group, fan_distances[mask], + fan_faces_id[mask] if output_old_face_id else [], + np_custom_normals[mask] if use_custom_normals else [], + index_offset=index_offset, + **common_args) + else: + res = fan_regular_pols( + np_verts, fan_faces, fan_distances, + fan_faces_id if output_old_face_id else [], + np_custom_normals if use_custom_normals else [], + index_offset=index_offset, + **common_args) + + + index_offset += len(res[0]) + new_verts.extend(res[0]) + new_faces.extend(res[1]) + original_vertex_id.extend(res[2]) + original_face_ids.extend(res[3]) + new_pols_groups.extend(res[4]) + if output_new_verts_mask: + new_verts_mask.extend([1]*len(res[0])) + + return (new_verts, + new_faces + new_ignores, + new_ignores, + new_insets, + original_vertex_id, + original_face_ids + ignores_id, + new_pols_groups + [0]*len(new_ignores), + new_verts_mask) + +def normalize_or_calc(v1, v2, normals): + arr = v1 + v2 + lens = vector_length(arr) + mask = lens != 0 + arr[mask, 0] /= lens[mask] + arr[mask, 1] /= lens[mask] + arr[mask, 2] /= lens[mask] + zero_length_mask = np.invert(mask) + arr[zero_length_mask, :] = normalize_v3(np.cross(normals[zero_length_mask, :], v1[zero_length_mask, :])) + arr[zero_length_mask, :] = (np.cross(normals[zero_length_mask, :], v1[zero_length_mask, :])) + return arr + +def sides_mode_inset(v_pols, np_inset_rate, np_distances, + concave_support, proportional, + use_custom_normals, custom_normals): + pol_sides = v_pols.shape[1] + dirs = np.zeros(v_pols.shape, dtype=float) + + if concave_support: + normals = custom_normals if use_custom_normals else face_normals(v_pols) + for i in range(pol_sides): + side1 = normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) + side2 = normalize_v3(v_pols[:, i-1]- v_pols[:, i]) + dirs[:, i] = normalize_or_calc(side1, side2, normals) + dirs[:, i] *= (np_inset_rate/(np.sqrt(1-np.clip(np_dot(side1, dirs[:, i]), -1.0, 1.0)**2)))[:, np_newaxis] + + average = np.sum(v_pols, axis=1)/pol_sides + concave_mask = np_dot(average[:, np_newaxis, :] - v_pols, dirs, axis=2) < 0 + + dirs[concave_mask] *= -1 + else: + for i in range(pol_sides): + side1 = normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) + side2 = normalize_v3(v_pols[:, i-1]- v_pols[:, i]) + dirs[:, i] = normalize_v3( + normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) + + normalize_v3(v_pols[:, i-1]- v_pols[:, i]) + ) + + dirs[:, i] *= (np_inset_rate/(np.sqrt(1-np.clip(np_dot(side1, dirs[:, i]), -1.0, 1.0)**2)))[:, np_newaxis] + + if proportional: + dirs *= face_perimeter(v_pols)[:, np_newaxis, np_newaxis] + if any(np_distances != 0): + if not concave_support: + normals = custom_normals if use_custom_normals else face_normals(v_pols) + z_offset = normals * np_distances[:, np_newaxis] + inner_points = dirs + v_pols + z_offset[:, np_newaxis, :] + else: + inner_points = dirs + v_pols + return inner_points + + +def inset_regular_pols(np_verts, np_pols, + np_distances, np_inset_rate, np_make_inners, + np_faces_id, custom_normals, + offset_mode='CENTER', + proportional=False, + concave_support=True, + index_offset=0, + use_custom_normals=False, + output_old_face_id=True, + output_old_v_id=True, + output_pols_groups=True): + + pols_number = np_pols.shape[0] + pol_sides = np_pols.shape[1] + v_pols = np_verts[np_pols] #shape [num_pols, num_corners, 3] + if offset_mode == 'SIDES': + inner_points = sides_mode_inset(v_pols, np_inset_rate, np_distances, + concave_support, proportional, + use_custom_normals, custom_normals) + else: + if any(np_distances != 0): + if use_custom_normals: + normals = custom_normals + else: + normals = face_normals(v_pols) + average = np.sum(v_pols, axis=1)/pol_sides #+ normals*np_distances[:, np_newaxis] #shape [num_pols, 3] + inner_points = average[:, np_newaxis, :] + (v_pols - average[:, np_newaxis, :]) * np_inset_rate[:, np_newaxis, np_newaxis] + normals[:, np_newaxis, :]*np_distances[:, np_newaxis, np_newaxis] + else: + average = np.sum(v_pols, axis=1)/pol_sides #shape [num_pols, 3] + inner_points = average[:, np_newaxis, :] + (v_pols - average[:, np_newaxis, :]) * np_inset_rate[:, np_newaxis, np_newaxis] + + + idx_offset = len(np_verts) + index_offset + + new_v_idx = np_arange(idx_offset, pols_number * pol_sides + idx_offset).reshape(pols_number, pol_sides) + + side_pols = np.zeros([pols_number, pol_sides, 4], dtype=int) + side_pols[:, :, 0] = np_pols + side_pols[:, :, 1] = np_roll(np_pols, -1, axis=1) + side_pols[:, :, 2] = np_roll(new_v_idx, -1, axis=1) + side_pols[:, :, 3] = new_v_idx + + side_faces = side_pols.reshape(-1, 4) + + new_insets = new_v_idx[np_make_inners] + + if pol_sides == 4: + new_faces = np_concatenate([side_faces, new_insets]).tolist() + else: + new_faces = side_faces.tolist() + new_insets.tolist() + + old_v_id = np_pols.flatten().tolist() if output_old_v_id else [] + if output_old_face_id: + side_ids = np.repeat(np_faces_id[:, np_newaxis], pol_sides, axis=1) + inset_ids = np_faces_id[np_make_inners] + old_face_id = np.concatenate((side_ids.flatten(), inset_ids)).tolist() + else: + old_face_id = [] + + if output_pols_groups: + pols_groups = np_repeat([1, 2], [len(side_faces), len(new_insets)]).tolist() + else: + pols_groups = [] + + return (inner_points.reshape(-1, 3).tolist(), + new_faces, + new_insets.tolist(), + old_v_id, + old_face_id, + pols_groups + ) + +def fan_regular_pols(np_verts, np_pols, + np_distances, np_faces_id, + custom_normals, + index_offset=0, + use_custom_normals=False, + output_old_v_id=True, + output_old_face_id=True, + output_pols_groups=True): + + pols_number = np_pols.shape[0] + pol_sides = np_pols.shape[1] + v_pols = np_verts[np_pols] #shape [num_pols, num_corners, 3] + + if (len(np_distances)>1 and np.any(np_distances != 0)) or np_distances != 0: + if use_custom_normals: + normals = custom_normals + else: + normals = face_normals(v_pols) + average = np.sum(v_pols, axis=1)/pol_sides + normals*np_distances[:, np_newaxis] #shape [num_pols, 3] + else: + average = np.sum(v_pols, axis=1)/pol_sides + + + idx_offset = len(np_verts) + index_offset + new_idx = np_arange(idx_offset, pols_number + idx_offset) + new_pols = np.zeros([pols_number, pol_sides, 3], dtype=int) + new_pols[:, :, 0] = np_pols + new_pols[:, :, 1] = np_roll(np_pols, -1, axis=1) + new_pols[:, :, 2] = new_idx[:, np_newaxis] + + + + old_vert_id = np_pols[:, 0].tolist() if output_old_v_id else [] + + if output_old_face_id: + old_face_id = np_repeat(np_faces_id[:, np_newaxis], pol_sides, axis=1).tolist() + else: + old_face_id = [] + + if output_pols_groups: + pols_groups = np_repeat(1, len(new_pols)*pol_sides).tolist() + else: + pols_groups = [] + + return (average.tolist(), + new_pols.reshape(-1, 3).tolist(), + old_vert_id, + old_face_id, + pols_groups, + ) + + +''' +Old implementation, slower, left here because polygon order may differ with the new implementation +''' + +def inset_special_mathutils(vertices, faces, inset_rates, distances, ignores, make_inners, zero_mode="SKIP"): + + new_faces = [] + new_ignores = [] + new_insets = [] + + def get_average_vector(verts, n): + dummy_vec = Vector() + for v in verts: + dummy_vec = dummy_vec + v + return dummy_vec * 1/n + + def do_tri(face, lv_idx, make_inner): + a, b, c = face + d, e, f = lv_idx-2, lv_idx-1, lv_idx + out_faces = [] + out_faces.append([a, b, e, d]) + out_faces.append([b, c, f, e]) + out_faces.append([c, a, d, f]) + if make_inner: + out_faces.append([d, e, f]) + new_insets.append([d, e, f]) + return out_faces + + def do_quad(face, lv_idx, make_inner): + a, b, c, d = face + e, f, g, h = lv_idx-3, lv_idx-2, lv_idx-1, lv_idx + out_faces = [] + out_faces.append([a, b, f, e]) + out_faces.append([b, c, g, f]) + out_faces.append([c, d, h, g]) + out_faces.append([d, a, e, h]) + if make_inner: + out_faces.append([e, f, g, h]) + new_insets.append([e, f, g, h]) + return out_faces + + def do_ngon(face, lv_idx, make_inner): + ''' + setting up the forloop only makes sense for ngons + ''' + num_elements = len(face) + face_elements = list(face) + inner_elements = [lv_idx-n for n in range(num_elements-1, -1, -1)] + # padding, wrap-around + face_elements.append(face_elements[0]) + inner_elements.append(inner_elements[0]) + + out_faces = [] + add_face = out_faces.append + for j in range(num_elements): + add_face([face_elements[j], face_elements[j+1], inner_elements[j+1], inner_elements[j]]) + + if make_inner: + temp_face = [idx[-1] for idx in out_faces] + add_face(temp_face) + new_insets.append(temp_face) + + return out_faces + + def new_inner_from(face, inset_by, distance, make_inner): + ''' + face: (idx list) face to work on + inset_by: (scalar) amount to open the face + axis: (vector) axis relative to face normal + distance: (scalar) push new verts on axis by this amount + make_inner: create extra internal face + + # dumb implementation first. should only loop over the verts of face 1 time + to get + - new faces + - avg vertex location + - but can't lerp until avg is known. so each input face is looped at least twice. + ''' + current_verts_idx = len(vertices) + n = len(face) + verts = [vertices[i] for i in face] + avg_vec = get_average_vector(verts, n) + + if abs(inset_by) < 1e-6: + normal = mathutils.geometry.normal(*verts) + new_vertex = avg_vec.lerp(avg_vec + normal, distance) + vertices.append(new_vertex) + new_vertex_idx = current_verts_idx + new_faces + for i, j in zip(face, face[1:]): + new_faces.append([i, j, new_vertex_idx]) + new_faces.append([face[-1], face[0], new_vertex_idx]) + return + + # lerp and add to vertices immediately + new_verts_prime = [avg_vec.lerp(v, inset_by) for v in verts] + + if distance: + local_normal = mathutils.geometry.normal(*new_verts_prime) + new_verts_prime = [v.lerp(v+local_normal, distance) for v in new_verts_prime] + + vertices.extend(new_verts_prime) + + tail_idx = (current_verts_idx + n) - 1 + + get_faces_prime = {3: do_tri, 4: do_quad}.get(n, do_ngon) + new_faces_prime = get_faces_prime(face, tail_idx, make_inner) + new_faces.extend(new_faces_prime) + + for face, inset_by, ignore, dist, inner in zip(faces, inset_rates, ignores, distances, make_inners): + + good_inset = (inset_by > 0) or (zero_mode == 'FAN') + if good_inset and (not ignore): + new_inner_from(face, inset_by, dist, inner) + else: + new_faces.append(face) + new_ignores.append(face) + + new_verts = [v[:] for v in vertices] + return new_verts, new_faces, new_ignores, new_insets, [], [] From a64f2424baf41a0a8dfec111777447dd56b016e9 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Tue, 20 Apr 2021 17:42:42 +0200 Subject: [PATCH 2/4] Matrix Offset cleaning and docs --- docs/nodes/CAD/inset_special_mk2.rst | 27 ++- nodes/CAD/inset_special_mk2.py | 90 ++++---- nodes/modifier_change/extrude_separate.py | 6 +- .../modifier_change/extrude_separate_lite.py | 4 +- nodes/modifier_change/inset_faces.py | 6 +- utils/mesh/inset_faces.py | 193 +++++++----------- 6 files changed, 159 insertions(+), 167 deletions(-) diff --git a/docs/nodes/CAD/inset_special_mk2.rst b/docs/nodes/CAD/inset_special_mk2.rst index 2c1e613b1e..796d8dc9dd 100644 --- a/docs/nodes/CAD/inset_special_mk2.rst +++ b/docs/nodes/CAD/inset_special_mk2.rst @@ -12,13 +12,17 @@ Inputs This node has the following inputs: +Base Meshes: + - **Vertices** - Vertices of objects - **Polygons** - polygons of objects -- **Inset** - Proportional offset values meaning 0 in the center and 1 in the edges. Vectorized for every polygon as [[f,f,f,f,f]] +Transformation definition. Vectorized for every polygon: + +- **Inset** - Proportional offset values meaning 0 in the center and 1 in the edges. -- **Distance** - Offset distance along normal. Vectorized for every polygon as [[f,f,f,f,f]] +- **Distance** - Offset distance along normal. - **ignore** - mask of affected polygons @@ -26,12 +30,15 @@ This node has the following inputs: - **Custom Normals** - custom normals for inset displacement +- **Offset Matrix** - Matrix transformation for inset displacement (only if 'Offset Mode' is set to 'Matrix') + Options ------- - **Offset Mode** - How to interpret inset distance: Center: Inset is measured as a proportion between the corners and the center of the polygon Sides: Inset is measured as a constant distance to the sides of the polygon + Matrix: Inset is computed based on a offset Matrix - **Proportional** - Multiply distance by polygon perimeter (only if 'Offset Mode' is set to 'Sides') @@ -64,7 +71,7 @@ This node has the following outputs: - **Original Face Idx** - the index of the original face. Can be used to pass Face Data to new faces - **Pols Group** - Outputs a list to mask polygons from the modified mesh, 0 = Original Polygon - 1 = Side poligon + 1 = Side polygon 2 = Inset Polygon. - **New Verts Mask** - Mask of the new vertices @@ -72,8 +79,20 @@ This node has the following outputs: Examples of usage ----------------- -.. image:: https://raw.githubusercontent.com/vDicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example.png +.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example.png :alt: procedural_Inset_example_blender_sverchok_1.png .. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/CAD/Inset_special/inset_special_example2.png :alt: procedural_Inset_example_blender_sverchok_2.png + +Original Face idx and Original Vert Idx +.. image:: https://user-images.githubusercontent.com/10011941/115357139-9b846200-a1bc-11eb-9c32-529c820a1a36.png + +Custom Normal Example +.. image:: https://user-images.githubusercontent.com/10011941/115118493-af08b080-9fa3-11eb-88a2-3252eea02a33.png + +New verts mask used to bevel only inset vertices +.. image:: https://user-images.githubusercontent.com/10011941/115415530-46b20d00-a1f7-11eb-8d6f-48bcc941bcfd.png + +Pol groups output to filter the output polygons +.. image:: https://user-images.githubusercontent.com/10011941/115419634-c7263d00-a1fa-11eb-8730-d6ca1dc5511b.png diff --git a/nodes/CAD/inset_special_mk2.py b/nodes/CAD/inset_special_mk2.py index 0e2911a30e..6759e36b1c 100644 --- a/nodes/CAD/inset_special_mk2.py +++ b/nodes/CAD/inset_special_mk2.py @@ -16,18 +16,14 @@ # # ##### END GPL LICENSE BLOCK ##### -import random -from mathutils import Vector -import bpy - +import bpy +from bpy.props import BoolProperty, FloatProperty, IntProperty, EnumProperty from mathutils import Vector -from bpy.props import BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, EnumProperty from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import ( - updateNode, Vector_generate, zip_long_repeat, make_repeaters,repeat_last_for_length, - repeat_last) + updateNode, zip_long_repeat, make_repeaters) from sverchok.utils.mesh.inset_faces import inset_special_np, inset_special_mathutils from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode @@ -56,7 +52,7 @@ class SvInsetSpecialMk2(bpy.types.Node, SverchCustomTreeNode, SvRecursiveNode): inset: FloatProperty( name='Inset', description='inset amount', - min = 0.0, + min=0.0, default=0.1, update=updateNode) distance: FloatProperty( name='Distance', @@ -79,14 +75,21 @@ class SvInsetSpecialMk2(bpy.types.Node, SverchCustomTreeNode, SvRecursiveNode): update=updateNode) offset_modes = [ ("CENTER", "Center", "Inset is measured as a proportion between the corners and the center of the polygon", 0), - ("SIDES", "Sides", "Inset is measured as a constant distance to the sides of the polygon", 1) + ("SIDES", "Sides", "Inset is measured as a constant distance to the sides of the polygon", 1), + ("MATRIX", "Matrix", "Inset controled by offset matrix", 2) ] + def update_sockets(self, context): + self.inputs['Inset'].hide_safe = self.offset_mode == 'MATRIX' + self.inputs['Distance'].hide_safe = self.offset_mode == 'MATRIX' + self.inputs['Offset Matrix'].hide_safe = self.offset_mode != 'MATRIX' + updateNode(self, context) + offset_mode: EnumProperty( - name="Offset Mode", + name="Mode", description="How to interpret inset distance", default="CENTER", items=offset_modes, - update=updateNode) + update=update_sockets) proportional: BoolProperty( name='Proportional', description='Multiply Inset by face perimeter', @@ -102,35 +105,39 @@ class SvInsetSpecialMk2(bpy.types.Node, SverchCustomTreeNode, SvRecursiveNode): replacement_nodes = [ ('SvExtrudeSeparateNode', - dict(Vertices='Vertices', Polygons='Polygons'), - dict(vertices='Vertices', polygons='Polygons')), + dict(Vertices='Vertices', Polygons='Polygons', Distance='Height'), + dict(Vertices='Vertices', Polygons='Polygons')), ('SvExtrudeSeparateLiteNode', dict(Vertices='Vertices', Polygons='Polygons'), - dict(vertices='Vertices', polygons='Polygons')), + dict(vertices='Vertices', Polygons='Polygons')), ('SvInsetFaces', - dict(Vertices='Verts', Polygons='Faces'), - dict(vertices='Verts', polygons='Faces')) + dict(Vertices='Verts', Polygons='Faces', Distance='Depth'), + dict(Vertices='Verts', Polygons='Faces')) ] def sv_init(self, context): i = self.inputs self.sv_new_input('SvVerticesSocket', "Vertices", is_mandatory=True, nesting_level=3) self.sv_new_input('SvStringsSocket', "Polygons", is_mandatory=True, nesting_level=3) - i.new('SvStringsSocket', 'inset').prop_name = 'inset' - i.new('SvStringsSocket', 'distance').prop_name = 'distance' - i.new('SvStringsSocket', 'ignore').prop_name = 'ignore' - i.new('SvStringsSocket', 'make_inner').prop_name = 'make_inner' + i.new('SvStringsSocket', 'Inset').prop_name = 'inset' + i.new('SvStringsSocket', 'Distance').prop_name = 'distance' + i.new('SvStringsSocket', 'Ignore').prop_name = 'ignore' + i.new('SvStringsSocket', 'Make Inner').prop_name = 'make_inner' i.new('SvVerticesSocket', 'Custom normal') + self.sv_new_input('SvMatrixSocket', 'Offset Matrix', + nesting_level=2, + default_mode='MATRIX', + hide_safe=True) o = self.outputs - o.new('SvVerticesSocket', 'vertices') - o.new('SvStringsSocket', 'polygons') - o.new('SvStringsSocket', 'ignored') - o.new('SvStringsSocket', 'inset') - o.new('SvStringsSocket', 'original verts idx') - o.new('SvStringsSocket', 'original face idx') - o.new('SvStringsSocket', 'pols group') - o.new('SvStringsSocket', 'new verts mask') + o.new('SvVerticesSocket', 'Vertices') + o.new('SvStringsSocket', 'Polygons') + o.new('SvStringsSocket', 'Ignored') + o.new('SvStringsSocket', 'Inset') + o.new('SvStringsSocket', 'Original verts idx') + o.new('SvStringsSocket', 'Original face idx') + o.new('SvStringsSocket', 'Pols group') + o.new('SvStringsSocket', 'New verts mask') def draw_buttons(self, context, layout): layout.prop(self, 'offset_mode') @@ -144,22 +151,22 @@ def draw_buttons_ext(self, context, layout): layout.prop(self, "concave_support") def process_data(self, params): - i = self.inputs + o = self.outputs output = [[] for s in self.outputs] - output_old_face_id = o['original face idx'].is_linked - output_old_vert_id = o['original verts idx'].is_linked - output_pols_groups = o['pols group'].is_linked - output_new_verts_mask = o['new verts mask'].is_linked - for v, p, inset_rates_s, distance_vals_s, ignores_s, make_inners_s, custom_normals in zip_long_repeat(*params): + output_old_face_id = o['Original face idx'].is_linked + output_old_vert_id = o['Original verts idx'].is_linked + output_pols_groups = o['Pols group'].is_linked + output_new_verts_mask = o['New verts mask'].is_linked + for verts, pols, inset_rates_s, distance_vals_s, ignores_s, make_inners_s, custom_normals, matrices in zip_long_repeat(*params): if self.implementation == 'mathutils': inset_rates, distance_vals, ignores, make_inners = make_repeaters([inset_rates_s, distance_vals_s, ignores_s, make_inners_s]) func_args = { - 'vertices': [Vector(vec) for vec in v], - 'faces': p, + 'vertices': [Vector(vec) for vec in verts], + 'faces': pols, 'inset_rates': inset_rates, 'distances': distance_vals, 'ignores': ignores, @@ -169,13 +176,14 @@ def process_data(self, params): res = inset_special_mathutils(**func_args) else: func_args = { - 'vertices': v, - 'faces': p, + 'vertices': verts, + 'faces': pols, 'inset_rates': inset_rates_s, 'distances': distance_vals_s, 'ignores': ignores_s, 'make_inners': make_inners_s, 'custom_normals': custom_normals, + 'matrices': matrices, 'zero_mode': self.zero_mode, 'offset_mode': self.offset_mode, 'proportional':self.proportional, @@ -187,10 +195,10 @@ def process_data(self, params): } res = inset_special_np(**func_args) if not res: - res = v, p, [], [], [], [], [], [] + res = verts, pols, [], [], [], [], [], [] - for r, so in zip(res, output): - so.append(r) + for sub_res, socket in zip(res, output): + socket.append(sub_res) return output diff --git a/nodes/modifier_change/extrude_separate.py b/nodes/modifier_change/extrude_separate.py index 655429d049..09192c9340 100644 --- a/nodes/modifier_change/extrude_separate.py +++ b/nodes/modifier_change/extrude_separate.py @@ -90,10 +90,10 @@ def update_mode(self, context): description="Switch between untouched, inner and outer faces generated by insertion") replacement_nodes = [ - ('SvExtrudeSeparateLiteNode', None, None), ('SvInsetSpecialMk2', - dict(Vertices='Vertices', Polygons='Polygons'), - dict(Vertices='vertices', Polygons='polygons')), + dict(Vertices='Vertices', Polygons='Polygons', Height='Distance'), + dict(Vertices='Vertices', Polygons='Polygons')), + ('SvExtrudeSeparateLiteNode', None, None), ('SvInsetFaces', dict(Vertices='Verts', Polygons='Faces'), dict(Vertices='Verts', Polygons='Faces')) diff --git a/nodes/modifier_change/extrude_separate_lite.py b/nodes/modifier_change/extrude_separate_lite.py index 04d9d818c7..f9bdcb6fe8 100644 --- a/nodes/modifier_change/extrude_separate_lite.py +++ b/nodes/modifier_change/extrude_separate_lite.py @@ -35,10 +35,10 @@ class SvExtrudeSeparateLiteNode(bpy.types.Node, SverchCustomTreeNode): sv_icon = 'SV_EXTRUDE_FACE' replacement_nodes = [ - ('SvExtrudeSeparateNode', None, None), ('SvInsetSpecialMk2', dict(Vertices='Vertices', Polygons='Polygons'), - dict(Vertices='vertices', Polygons='polygons')), + dict(Vertices='Vertices', Polygons='Polygons')), + ('SvExtrudeSeparateNode', None, None), ('SvInsetFaces', dict(Vertices='Verts', Polygons='Faces'), dict(Vertices='Verts', Polygons='Faces')) diff --git a/nodes/modifier_change/inset_faces.py b/nodes/modifier_change/inset_faces.py index 79de154a72..7732215221 100644 --- a/nodes/modifier_change/inset_faces.py +++ b/nodes/modifier_change/inset_faces.py @@ -337,15 +337,15 @@ class SvInsetFaces(bpy.types.Node, SverchCustomTreeNode): description="Switch between inserting type") replacement_nodes = [ + ('SvInsetSpecialMk2', + dict(Verts='Vertices', Faces='Polygons', Depth='Distance'), + dict(Verts='Vertices', Faces='Polygons')), ('SvExtrudeSeparateNode', dict(Verts='Vertices', Faces='Polygons'), dict(Verts='Vertices', Faces='Polygons')), ('SvExtrudeSeparateLiteNode', dict(Verts='Vertices', Faces='Polygons'), dict(Verts='Vertices', Faces='Polygons')), - ('SvInsetSpecialMk2', - dict(Verts='Vertices', Faces='Polygons'), - dict(Verts='vertices', Faces='polygons')), ] def draw_buttons(self, context, layout): diff --git a/utils/mesh/inset_faces.py b/utils/mesh/inset_faces.py index f11b4f20e3..f1116a07e0 100644 --- a/utils/mesh/inset_faces.py +++ b/utils/mesh/inset_faces.py @@ -7,6 +7,7 @@ import mathutils from mathutils import Vector + import numpy as np from numpy import( arange as np_arange, @@ -17,118 +18,28 @@ roll as np_roll, vectorize as np_vectorize, ) -from sverchok.utils.math import np_dot - +from sverchok.utils.math import np_dot, np_normalize_vectors as normalize_v3 +from sverchok.utils.modules.polygon_utils import np_faces_normals, np_faces_perimeters as face_perimeter from sverchok.data_structure import numpy_full_list, has_element - +IDENTITY_MATRIX = np.eye(4, dtype=float) +INVERSE_IDENTITY_MATRIX = np.array([[-1, 0, 0, 0], + [0, -1, 0, 0], + [0, 0, -1, 0], + [0, 0, 0, 1]], dtype=float) def vector_length(arr): return np.sqrt(arr[:, 0]**2 + arr[:, 1]**2 + arr[:, 2]**2) -def normalize_v3(arr): - ''' Normalize a numpy array of 3 component vectors shape=(n,3) ''' - lens = vector_length(arr) - mask = lens!= 0 - arr[mask, 0] /= lens - arr[mask, 1] /= lens - arr[mask, 2] /= lens - return arr - -def face_normals(v_pols, non_planar=True): - sides = v_pols.shape[1] - if v_pols.shape[1] != 3 and non_planar: - normals = np.zeros((len(v_pols), 3), dtype=float) - for i in range(sides-2): - normals += normalize_v3(np.cross(v_pols[::, 1] - v_pols[::, 0], v_pols[::, (2 + i) % sides] - v_pols[::, 0] )) - else: - normals = np.cross(v_pols[::, 1] - v_pols[::, 0], v_pols[::, 2] - v_pols[::, 0] ) - normalize_v3(normals) - return normals - -def face_perimeter(v_pols): - sides = v_pols.shape[1] - perimeter = np.zeros((len(v_pols)), dtype=float) - for i in range(sides-2): - perimeter += vector_length(v_pols[:, i,:] - v_pols[:, i-1,:]) - return perimeter - -def prepare_data(faces, distances, ignores, make_inners, zero_mode='SKIP', output_old_face_id=False): - invert_face_mask = numpy_full_list(np_array(ignores, dtype=bool), len(faces)) - np_faces_mask = np.invert(invert_face_mask) - # np_faces_mask = numpy_full_list(np.invert(np_array(ignores, dtype=bool)), len(faces)) - - if not any(np_faces_mask): - return - - np_faces = np_array(faces) - if output_old_face_id: - np_faces_id = np.arange(len(faces)) - - np_inset_rate = numpy_full_list(inset_rates, len(faces)) - zero_inset = np_inset_rate == 0 - inset_faces_id, fan_faces_id = [],[] - if zero_mode == 'SKIP': - np_faces_mask[zero_inset] = False - invert_face_mask[zero_inset] = True - new_ignores = np_faces[invert_face_mask].tolist() - if output_old_face_id: - ignores_id = np_faces_id[invert_face_mask].tolist() - new_ignores = np_faces[invert_face_mask].tolist() - else: - new_ignores = np_faces[invert_face_mask].tolist() - - np_distances = numpy_full_list(distances, len(faces))[np_faces_mask] - if output_old_face_id: - inset_faces_id = np_faces_id[np_faces_mask] - else: # FAN - if output_old_face_id: - ignores_maks = np.invert(np_faces_mask) - ignores_id = np_faces_id[ignores_maks].tolist() - new_ignores = np_faces[ignores_maks].tolist() - else: - new_ignores = np_faces[np.invert(np_faces_mask)].tolist() - np_faces_mask[zero_inset] = False - - np_all_distances = numpy_full_list(distances, len(faces)) - np_distances = np_all_distances[np_faces_mask] - - fan_faces = np_faces[zero_inset] - fan_distances = np_all_distances[zero_inset] - if output_old_face_id: - inset_faces_id = np_faces_id[np_faces_mask] - fan_faces_id = np_faces_id[zero_inset] - - - - np_inset_rate = np_inset_rate[np_faces_mask] - # np_inset_rate = numpy_full_list(inset_rates, len(faces))[np_faces_mask] - np_make_inners = numpy_full_list(make_inners, len(faces)).astype(bool)[np_faces_mask] - np_pols = np_faces[np_faces_mask] - - return np_pols, np_make_inners, np_inset_rate, np_distances, fan_faces, fan_distances - -def sides_mode_prepare_data(np_faces, np_faces_mask, invert_face_mask, distances, np_inset_rate, make_inners, np_faces_id): - inset_pols = np_faces[np_faces_mask] - np_distances = numpy_full_list(distances, len(np_faces))[np_faces_mask] - np_inset_rate = np_inset_rate[np_faces_mask] - np_make_inners = numpy_full_list(make_inners, len(np_faces)).astype(bool)[np_faces_mask] - new_ignores = np_faces[invert_face_mask].tolist() - if output_old_face_id: - ignores_id = np_faces_id[invert_face_mask].tolist() - inset_faces_id = np_faces_id[np_faces_mask] - else: - ignores_id = [] - return inset_pols, np_inset_rate, np_distances, np_make_inners, new_ignores, ignores_id, inset_faces_id - -def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inners, custom_normals, +def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inners, custom_normals, matrices, zero_mode="SKIP", offset_mode='CENTER', proportional=False, concave_support=True, output_old_face_id=False, output_old_v_id=False, output_pols_groups=False, output_new_verts_mask=False): - if len(faces) == 0: + if not has_element(faces): return new_faces, new_ignores, new_insets = [], [], [] original_face_ids, original_vertex_id, new_pols_groups, new_verts_mask = [], [], [], [] inset_faces_id, fan_faces_id, ignores_id, fan_faces = [], [], [], [] + np_distances, np_matrices = [], [] np_verts = vertices if isinstance(vertices, np.ndarray) else np.array(vertices) @@ -186,8 +97,15 @@ def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inne fan_faces_id = np_faces_id[zero_inset] else: #SIDES mode inset_pols = np_faces[np_faces_mask] - np_distances = numpy_full_list(distances, len(faces))[np_faces_mask] - np_inset_rate = np_inset_rate[np_faces_mask] + if offset_mode == 'SIDES': + np_distances = numpy_full_list(distances, len(faces))[np_faces_mask] + np_inset_rate = np_inset_rate[np_faces_mask] + else: #MATRIX + if len(matrices) == len(faces): + np_matrices = np.array(matrices)[np_faces_mask] + else: + np_matrices = numpy_full_list(matrices, len(inset_pols)) + np_make_inners = numpy_full_list(make_inners, len(faces)).astype(bool)[np_faces_mask] new_ignores = np_faces[invert_face_mask].tolist() fan_faces = [] @@ -196,6 +114,8 @@ def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inne inset_faces_id = np_faces_id[np_faces_mask] + + common_args = { 'use_custom_normals': use_custom_normals, 'output_old_v_id': output_old_v_id, @@ -226,11 +146,12 @@ def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inne mask = lens == pol_sides pols_group = np_array(inset_pols[mask].tolist(), dtype=int) res = inset_regular_pols(np_verts, pols_group, - np_distances[mask], - np_inset_rate[mask], + np_distances[mask] if offset_mode != 'MATRIX' else [], + np_inset_rate[mask] if offset_mode != 'MATRIX' else [], np_make_inners[mask], inset_faces_id[mask] if output_old_face_id else [], np_custom_normals[mask] if use_custom_normals else [], + np_matrices[mask] if offset_mode == 'MATRIX' else [], offset_mode=offset_mode, proportional=proportional, concave_support=concave_support, index_offset=index_offset, @@ -243,6 +164,7 @@ def inset_special_np(vertices, faces, inset_rates, distances, ignores, make_inne np_make_inners, inset_faces_id if output_old_face_id else [], np_custom_normals if use_custom_normals else [], + np_matrices, offset_mode=offset_mode, proportional=proportional, concave_support=concave_support, index_offset=index_offset, @@ -319,7 +241,7 @@ def sides_mode_inset(v_pols, np_inset_rate, np_distances, dirs = np.zeros(v_pols.shape, dtype=float) if concave_support: - normals = custom_normals if use_custom_normals else face_normals(v_pols) + normals = custom_normals if use_custom_normals else np_faces_normals(v_pols) for i in range(pol_sides): side1 = normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) side2 = normalize_v3(v_pols[:, i-1]- v_pols[:, i]) @@ -334,10 +256,9 @@ def sides_mode_inset(v_pols, np_inset_rate, np_distances, for i in range(pol_sides): side1 = normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) side2 = normalize_v3(v_pols[:, i-1]- v_pols[:, i]) - dirs[:, i] = normalize_v3( - normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) + - normalize_v3(v_pols[:, i-1]- v_pols[:, i]) - ) + dirs[:, i] = normalize_v3(normalize_v3(v_pols[:, (i+1)%pol_sides]- v_pols[:, i]) + + normalize_v3(v_pols[:, i-1]- v_pols[:, i]) + ) dirs[:, i] *= (np_inset_rate/(np.sqrt(1-np.clip(np_dot(side1, dirs[:, i]), -1.0, 1.0)**2)))[:, np_newaxis] @@ -345,17 +266,59 @@ def sides_mode_inset(v_pols, np_inset_rate, np_distances, dirs *= face_perimeter(v_pols)[:, np_newaxis, np_newaxis] if any(np_distances != 0): if not concave_support: - normals = custom_normals if use_custom_normals else face_normals(v_pols) + normals = custom_normals if use_custom_normals else np_faces_normals(v_pols) z_offset = normals * np_distances[:, np_newaxis] inner_points = dirs + v_pols + z_offset[:, np_newaxis, :] else: inner_points = dirs + v_pols return inner_points +def apply_matrices_to_v_pols(verts, matrices): + + verts_co_4d = np.ones(shape=(verts.shape[0], verts.shape[1], 4), dtype=np.float) + verts_co_4d[:, :, :-1] = verts # cos v (x,y,z,1) - point, v(x,y,z,0)- vector + return np.einsum('aij,akj->aki', matrices, verts_co_4d)[:, :, :-1] + + +def matrix_mode_inset(v_pols, matrices, use_custom_normals, custom_normals): + pol_sides = v_pols.shape[1] + average = np.sum(v_pols, axis=1)/pol_sides + if use_custom_normals: + normals = custom_normals + else: + normals = np_faces_normals(v_pols) + + pol_matrix = np.repeat(IDENTITY_MATRIX[np.newaxis, :, :], len(v_pols), axis=0) + + mask = np.all([normals[:, 0] == 0, normals[:, 1] == 0], axis=0) + mask2 = normals[:, 2] <= 0 + mask4 = mask*mask2 + r_mask = np.invert(mask) + + x_axis = np.zeros(normals.shape, dtype=float) + x_axis[:, 0] = normals[:, 1] * -1 + x_axis[:, 1] = normals[:, 0] + y_axis = np.cross(normals, x_axis, axis=1) + + pol_matrix[r_mask, :3, 2] = normals[r_mask, :] + pol_matrix[r_mask, :3, 1] = y_axis[r_mask, :] + pol_matrix[r_mask, :3, 0] = x_axis[r_mask, :] + pol_matrix[mask4, :, :] = INVERSE_IDENTITY_MATRIX[np.newaxis, :] + pol_matrix[:, :3, 3] = average + + + inverted_mat = np.linalg.inv(pol_matrix) + + matrices = np.matmul(matrices, inverted_mat) + matrices = np.matmul(pol_matrix, matrices) + + return apply_matrices_to_v_pols(v_pols, matrices) + # verts_out =[v_pols_transformed.reshape(-1,3)] def inset_regular_pols(np_verts, np_pols, np_distances, np_inset_rate, np_make_inners, np_faces_id, custom_normals, + matrices, offset_mode='CENTER', proportional=False, concave_support=True, @@ -372,12 +335,15 @@ def inset_regular_pols(np_verts, np_pols, inner_points = sides_mode_inset(v_pols, np_inset_rate, np_distances, concave_support, proportional, use_custom_normals, custom_normals) + elif offset_mode == 'MATRIX': + inner_points = matrix_mode_inset(v_pols, matrices, + use_custom_normals, custom_normals) else: if any(np_distances != 0): if use_custom_normals: normals = custom_normals else: - normals = face_normals(v_pols) + normals = np_faces_normals(v_pols) average = np.sum(v_pols, axis=1)/pol_sides #+ normals*np_distances[:, np_newaxis] #shape [num_pols, 3] inner_points = average[:, np_newaxis, :] + (v_pols - average[:, np_newaxis, :]) * np_inset_rate[:, np_newaxis, np_newaxis] + normals[:, np_newaxis, :]*np_distances[:, np_newaxis, np_newaxis] else: @@ -438,11 +404,11 @@ def fan_regular_pols(np_verts, np_pols, pol_sides = np_pols.shape[1] v_pols = np_verts[np_pols] #shape [num_pols, num_corners, 3] - if (len(np_distances)>1 and np.any(np_distances != 0)) or np_distances != 0: + if (len(np_distances) > 1 and np.any(np_distances != 0)) or np_distances != 0: if use_custom_normals: normals = custom_normals else: - normals = face_normals(v_pols) + normals = np_faces_normals(v_pols) average = np.sum(v_pols, axis=1)/pol_sides + normals*np_distances[:, np_newaxis] #shape [num_pols, 3] else: average = np.sum(v_pols, axis=1)/pol_sides @@ -565,7 +531,6 @@ def new_inner_from(face, inset_by, distance, make_inner): new_vertex = avg_vec.lerp(avg_vec + normal, distance) vertices.append(new_vertex) new_vertex_idx = current_verts_idx - new_faces for i, j in zip(face, face[1:]): new_faces.append([i, j, new_vertex_idx]) new_faces.append([face[-1], face[0], new_vertex_idx]) From 21112cd59586ab1776622dd058337b3eefbd51d0 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Wed, 21 Apr 2021 07:51:24 +0200 Subject: [PATCH 3/4] update docs index --- docs/nodes/CAD/CAD_index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/nodes/CAD/CAD_index.rst b/docs/nodes/CAD/CAD_index.rst index 2232bda7cf..18dca9f719 100644 --- a/docs/nodes/CAD/CAD_index.rst +++ b/docs/nodes/CAD/CAD_index.rst @@ -9,5 +9,4 @@ CAD edges_to_faces_2d merge_mesh_2d merge_mesh_2d_lite - inset_special - + inset_special_mk2 From 770fa53495a75cfdc4a61fb5133a51f9a73f5b86 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Wed, 21 Apr 2021 07:56:15 +0200 Subject: [PATCH 4/4] docs images fix --- docs/nodes/CAD/inset_special_mk2.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/nodes/CAD/inset_special_mk2.rst b/docs/nodes/CAD/inset_special_mk2.rst index 796d8dc9dd..8bfa1beb1a 100644 --- a/docs/nodes/CAD/inset_special_mk2.rst +++ b/docs/nodes/CAD/inset_special_mk2.rst @@ -86,13 +86,17 @@ Examples of usage :alt: procedural_Inset_example_blender_sverchok_2.png Original Face idx and Original Vert Idx + .. image:: https://user-images.githubusercontent.com/10011941/115357139-9b846200-a1bc-11eb-9c32-529c820a1a36.png Custom Normal Example + .. image:: https://user-images.githubusercontent.com/10011941/115118493-af08b080-9fa3-11eb-88a2-3252eea02a33.png New verts mask used to bevel only inset vertices + .. image:: https://user-images.githubusercontent.com/10011941/115415530-46b20d00-a1f7-11eb-8d6f-48bcc941bcfd.png Pol groups output to filter the output polygons + .. image:: https://user-images.githubusercontent.com/10011941/115419634-c7263d00-a1fa-11eb-8730-d6ca1dc5511b.png