Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/nortikin/sverchok
Browse files Browse the repository at this point in the history
  • Loading branch information
nortikin committed Sep 17, 2024
2 parents 86e8314 + a48c58d commit 3451093
Show file tree
Hide file tree
Showing 16 changed files with 464 additions and 31 deletions.
14 changes: 7 additions & 7 deletions core/socket_conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from enum import Enum

from sverchok.core.sv_custom_exceptions import ImplicitConversionProhibited
from sverchok.data_structure import get_data_nesting_level, is_ultimately
from sverchok.data_structure import get_data_nesting_level, is_ultimately, NUMERIC_DATA_TYPES
from sverchok.utils.field.vector import SvVectorField, SvMatrixVectorField, SvConstantVectorField
from sverchok.utils.field.scalar import SvScalarField, SvConstantScalarField
from sverchok.utils.curve import SvCurve
Expand All @@ -42,7 +42,7 @@ def matrices_to_vfield(data):


def vertices_to_vfield(data):
if isinstance(data, (tuple, list)) and len(data) == 3 and isinstance(data[0], (float, int)):
if isinstance(data, (tuple, list)) and len(data) == 3 and isinstance(data[0], NUMERIC_DATA_TYPES):
data = deepcopy(data)
return SvConstantVectorField(data)
elif isinstance(data, (list, tuple)):
Expand All @@ -52,7 +52,7 @@ def vertices_to_vfield(data):


def numbers_to_sfield(data):
if isinstance(data, (int, float)):
if isinstance(data, NUMERIC_DATA_TYPES):
return SvConstantScalarField(data)
elif isinstance(data, (list, tuple)):
return [numbers_to_sfield(item) for item in data]
Expand All @@ -68,7 +68,7 @@ def vectors_to_matrices(source_data):

def get_all(data):
for item in data:
if isinstance(item, (tuple, list, ndarray)) and len(item) == 3 and isinstance(item[0], (float, int)):
if isinstance(item, (tuple, list, ndarray)) and len(item) == 3 and isinstance(item[0], NUMERIC_DATA_TYPES):
# generate location matrix from location
x, y, z = item
collect_matrix(Matrix([(1., .0, .0, x), (.0, 1., .0, y), (.0, .0, 1., z), (.0, .0, .0, 1.)]))
Expand Down Expand Up @@ -97,7 +97,7 @@ def quaternions_to_matrices(source_data):

def get_all(data):
for item in data:
if isinstance(item, (tuple, list)) and len(item) == 4 and isinstance(item[0], (float, int)):
if isinstance(item, (tuple, list)) and len(item) == 4 and isinstance(item[0], NUMERIC_DATA_TYPES):
mat = Quaternion(item).to_matrix().to_4x4()
collect_matrix(mat)
else:
Expand All @@ -122,14 +122,14 @@ def get_all(data):

def string_to_vector(source_data):
# it can be so that socket is string but data their are already vectors, performance-wise we check only first item
if isinstance(source_data[0][0], (float, int)):
if isinstance(source_data[0][0], NUMERIC_DATA_TYPES):
return [[(v, v, v) for v in obj] for obj in source_data]
return source_data


def string_to_color(source_data):
# it can be so that socket is string but data their are already colors, performance-wise we check only first item
if isinstance(source_data[0][0], (float, int)):
if isinstance(source_data[0][0], NUMERIC_DATA_TYPES):
return [[(v, v, v, 1) for v in obj] for obj in source_data]
if len(source_data[0][0]) == 3:
return vector_to_color(source_data)
Expand Down
1 change: 1 addition & 0 deletions data_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ def levels_of_list_or_np(lst):
return level
return 0

NUMERIC_DATA_TYPES = (float, int, float64, int32, int64)
SIMPLE_DATA_TYPES = (float, int, float64, int32, int64, str, Matrix)


Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions docs/nodes/analyzer/poly_inscribed_circle.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Polygon Inscribed Circle
========================

Dependencies
------------

This node requires SciPy library to work.

Functionality
-------------

This node calculates the center and the radius of inscribed circle for each
convex face of the input mesh. Obviously, it is not always possible to inscribe
a circle into a polygon, if polygon is not a triangle. For non-tiangular
polygons, this node calculates the biggest circle which can be inscribed into
the polygon, i.e. the circle which touches as many polygon edges as possible.

Inputs
------

This node has the following inputs:

- **Vertices**. The vertices of the input mesh. This input is mandatory.
- **Faces**. The faces of the input mesh. This input is mandatory.

Parameters
----------

This node has the following parameters:

- **Flat Matrix output**. If checked, the node will generate a single flat list
of matrices in the **Matrix** output, for all input meshes. Checked by default.
- **On concave faces**. This parameter is available in the N panel only.
Defines what the node should do if it encounters a concave face. There are
the following options available:

- **Skip**. Just skip such faces - do not generate inscribed circles for them.
- **Error**. Stop processing and give an error (turn the node red).
- **As Is**. Try to generate an inscribed circle for such face anyway. In
many cases, the generated circle will be incorrect (will be too small or
even outside the polygon), but in some simple cases it can be valid.

The default option is **Skip**.

Outputs
-------

This node has the following outputs:

- **Center**. For each inscribed circle, this contains a matrix, Z axis of
which points along face normal, and the translation component equals to the
center of the inscribed circle. This output can be used to actually place
circles at their places.
- **Radius**. Radiuses of the inscribed circles.

Examples of Usage
-----------------

In many cases inscribed circle can touch only two or three polygon edges:

.. image:: ../../../docs/assets/nodes/analyzer/inscribed_circle_1.png
:target: ../../../docs/assets/nodes/analyzer/inscribed_circle_1.png

If the polygon is almost regular, the circle will touch more edges:

.. image:: ../../../docs/assets/nodes/analyzer/inscribed_circle_2.png
:target: ../../../docs/assets/nodes/analyzer/inscribed_circle_2.png

1 change: 1 addition & 0 deletions index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@
- SvCircleApproxNode
- SvSphereApproxNode
- SvInscribedCircleNode
- SvSemiInscribedCircleNode
- SvSteinerEllipseNode
- ---
- SvMeshSelectNodeMk2
Expand Down
1 change: 1 addition & 0 deletions menus/full_by_data_type.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@
- SvCircleApproxNode
- SvSphereApproxNode
- SvInscribedCircleNode
- SvSemiInscribedCircleNode
- SvSteinerEllipseNode
- ---
- SvMeshSelectNodeMk2
Expand Down
1 change: 1 addition & 0 deletions menus/full_nortikin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@
- SvCircleApproxNode
- SvSphereApproxNode
- SvInscribedCircleNode
- SvSemiInscribedCircleNode
- SvSteinerEllipseNode
- ---
- SvMeshSelectNodeMk2
Expand Down
96 changes: 96 additions & 0 deletions nodes/analyzer/poly_inscribed_circle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 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 numpy as np
import bpy
from bpy.props import BoolProperty, EnumProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.utils.inscribed_circle import calc_inscribed_circle, ERROR, RETURN_NONE, ASIS
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level

class SvSemiInscribedCircleNode(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Polygon Inscribed Circle
Tooltip: Inscribed circle for an arbitrary convex polygon
"""
bl_idname = 'SvSemiInscribedCircleNode'
bl_label = 'Polygon Inscribed Circle'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_POLY_INSCRIBED_CIRCLE'
sv_dependencies = {'scipy'}

def sv_init(self, context):
self.inputs.new('SvVerticesSocket', "Vertices")
self.inputs.new('SvStringsSocket', "Faces")
self.outputs.new('SvMatrixSocket', "Center")
self.outputs.new('SvStringsSocket', "Radius")

flat_output : BoolProperty(
name = "Flat Matrix output",
description = "Output single flat list of matrices",
default = True,
update = updateNode)

concave_modes = [
(RETURN_NONE, "Skip", "Skip concave faces - do not generate output for them", 0),
(ERROR, "Error", "Generate an error if encounter a concave face", 1),
(ASIS, "As Is", "Try to calculate inscribed circle anyway (it probably will be incorrect)", 2)
]

on_concave : EnumProperty(
name = "On concave face",
description = "What to do if encounter a concave face",
default = RETURN_NONE,
items = concave_modes,
update = updateNode)

def draw_buttons(self, context, layout):
layout.prop(self, 'flat_output')

def draw_buttons_ext(self, context, layout):
self.draw_buttons(context, layout)
layout.label(text = "On concave faces:")
layout.prop(self, 'on_concave', text='')

def process(self):
if not any(socket.is_linked for socket in self.outputs):
return

vertices_s = self.inputs['Vertices'].sv_get()
vertices_s = ensure_nesting_level(vertices_s, 4)
faces_s = self.inputs['Faces'].sv_get()
faces_s = ensure_nesting_level(faces_s, 4)

matrix_out = []
radius_out = []
for params in zip_long_repeat(vertices_s, faces_s):
new_matrix = []
new_radius = []
for vertices, faces in zip_long_repeat(*params):
vertices = np.array(vertices)
for face in faces:
face = np.array(face)
circle = calc_inscribed_circle(vertices[face],
on_concave = self.on_concave)
if circle is not None:
new_matrix.append(circle.get_matrix())
new_radius.append(circle.radius)
if self.flat_output:
matrix_out.extend(new_matrix)
else:
matrix_out.append(new_matrix)
radius_out.append(new_radius)

self.outputs['Center'].sv_set(matrix_out)
self.outputs['Radius'].sv_set(radius_out)

def register():
bpy.utils.register_class(SvSemiInscribedCircleNode)

def unregister():
bpy.utils.unregister_class(SvSemiInscribedCircleNode)

6 changes: 3 additions & 3 deletions nodes/modifier_change/edge_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class SvSplitEdgesMk3Node(ModifierNode, SverchCustomTreeNode, bpy.types.Node):
def update_mode(self, context):
self.inputs['Factor'].hide = False # This can be True in old nodes
self.inputs['Cuts'].hide = False # This can be True in old nodes
self.inputs['Factor'].enabled = self.mode != 'MULTI'
self.inputs['Cuts'].enabled = self.mode == 'MULTI'
self.inputs['Factor'].hide_safe = self.mode != 'MULTI'
self.inputs['Cuts'].hide_safe = self.mode == 'MULTI'
self.process_node(context)

factor: bpy.props.FloatProperty(
Expand All @@ -60,7 +60,7 @@ def sv_init(self, context):
self.inputs.new('SvStringsSocket', 'Factor').prop_name = 'factor'
s = self.inputs.new('SvStringsSocket', 'Cuts')
s.prop_name = 'count'
s.enabled = False
#s.enabled = False
self.outputs.new('SvVerticesSocket', 'Vertices')
self.outputs.new('SvStringsSocket', 'Edges')
self.outputs.new('SvStringsSocket', 'Faces')
Expand Down
17 changes: 11 additions & 6 deletions nodes/surface/intersect_curve_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def sv_init(self, context):
self.inputs.new('SvSurfaceSocket', "Surface")
self.outputs.new('SvVerticesSocket', "Point")
self.outputs.new('SvStringsSocket', "T")
self.outputs.new('SvVerticesSocket', "UVPoint")

def process(self):
if not any(socket.is_linked for socket in self.outputs):
Expand All @@ -89,7 +90,8 @@ def process(self):
tolerance = 10**(-self.accuracy)

points_out = []
u_out = []
t_out = []
uv_out = []
for surfaces, curves in zip_long_repeat(surfaces_s, curves_s):
for surface, curve in zip_long_repeat(surfaces, curves):
result = intersect_curve_surface(curve, surface,
Expand All @@ -99,14 +101,17 @@ def process(self):
raycast_method = self.raycast_method,
support_nurbs = self.use_nurbs
)
new_points = [p[1] for p in result]
new_u = [p[0] for p in result]
new_points = result.points
new_t = result.ts
new_uv = [(u, v, 0) for u, v in zip(result.us, result.vs)]
points_out.append(new_points)
u_out.append(new_u)
t_out.append(new_t)
uv_out.append(new_uv)

self.outputs['Point'].sv_set(points_out)
self.outputs['T'].sv_set(u_out)

self.outputs['T'].sv_set(t_out)
if 'UVPoint' in self.outputs:
self.outputs['UVPoint'].sv_set(uv_out)

def register():
bpy.utils.register_class(SvExCrossCurveSurfaceNode)
Expand Down
Binary file added ui/icons/sv_poly_inscribed_circle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 3451093

Please sign in to comment.