Source code for procfunc.nodes.geo

import logging
from dataclasses import dataclass
from typing import Generic, Literal, NamedTuple, TypeVar

from procfunc import types as pt
from procfunc.nodes import types as nt
from procfunc.nodes.util.bindings_util import RuntimeResolveDataType, raise_io_error
from procfunc.nodes.util.bpy_node_info import NodeDataType
from procfunc.util import pytree

logger = logging.getLogger(__name__)

TDomain = Literal["POINT", "EDGE", "FACE", "CORNER", "CURVE", "INSTANCE", "LAYER"]

TAttribute = TypeVar("TAttribute", int, float, bool)

# data_type values the generic attribute nodes (Store/Input Named Attribute,
# Sample Curve, ...) genuinely support, for runtime data-type resolution.
# FLOAT_VECTOR precedes RGBA so an ambiguous tuple resolves to a vector, not a color.
_ATTRIBUTE_NODE_DATA_TYPES = [
    NodeDataType.BOOLEAN,
    NodeDataType.INT,
    NodeDataType.FLOAT,
    NodeDataType.FLOAT_VECTOR,
    NodeDataType.RGBA,
    NodeDataType.FLOAT_VECTOR_2D,
    NodeDataType.ROTATION,
    NodeDataType.FLOAT_MATRIX,
    NodeDataType.STRING,
]

# data_type values the geometry sample/transfer nodes (Sample Index/Nearest
# Surface/UV Surface, Field at Index, Evaluate on Domain, Raycast) genuinely
# support. Unlike the named-attribute nodes these omit FLOAT2 and STRING.
# FLOAT_VECTOR precedes RGBA so an ambiguous tuple resolves to a vector.
_SAMPLE_NODE_DATA_TYPES = [
    NodeDataType.BOOLEAN,
    NodeDataType.INT,
    NodeDataType.FLOAT,
    NodeDataType.FLOAT_VECTOR,
    NodeDataType.RGBA,
    NodeDataType.ROTATION,
    NodeDataType.FLOAT_MATRIX,
]

TMeshOrCurve = TypeVar(
    "TMeshOrCurve",
    pt.MeshObject,
    pt.CurveObject,
)
TAnyGeometry = TypeVar(
    "TAnyGeometry",
    pt.MeshObject,
    pt.CurveObject,
    pt.VolumeObject,
    nt.Instances,
)


[docs] class AccumulateFieldResult(NamedTuple, Generic[TAttribute]): leading: nt.ProcNode[TAttribute] total: nt.ProcNode[TAttribute] trailing: nt.ProcNode[TAttribute]
[docs] def accumulate_field( value: nt.ProcNode[TAttribute] | None = None, group_id: nt.SocketOrVal[int] = 0, domain: TDomain = "POINT", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> AccumulateFieldResult[TAttribute]: """ Uses a AccumulateField Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/utilities/field/accumulate_field.html """ if data_type is None: data_type = RuntimeResolveDataType( [NodeDataType.INT, NodeDataType.FLOAT], ["Value"], ) res = nt.ProcNode.from_nodetype( node_type="GeometryNodeAccumulateField", inputs={"Group ID": group_id, "Value": value}, attrs={ "domain": domain, "data_type": data_type, }, ) return AccumulateFieldResult( leading=res._output_socket("leading"), total=res._output_socket("total"), trailing=res._output_socket("trailing"), )
[docs] class AttributeDomainSizeResult(NamedTuple): point_count: nt.ProcNode[int] edge_count: nt.ProcNode[int] face_count: nt.ProcNode[int] face_corner_count: nt.ProcNode[int] spline_count: nt.ProcNode[int] instance_count: nt.ProcNode[int]
[docs] def attribute_domain_size( geometry: nt.ProcNode[nt.Geometry] | None, component: Literal["MESH", "POINTCLOUD", "CURVE", "INSTANCES"] = "MESH", ) -> AttributeDomainSizeResult: """ Uses a AttributeDomainSize Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/attribute/domain_size.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeAttributeDomainSize", inputs={"Geometry": geometry}, attrs={"component": component}, ) return AttributeDomainSizeResult( point_count=res._output_socket("point_count"), edge_count=res._output_socket("edge_count"), face_count=res._output_socket("face_count"), face_corner_count=res._output_socket("face_corner_count"), spline_count=res._output_socket("spline_count"), instance_count=res._output_socket("instance_count"), )
[docs] class AttributeStatisticResult(NamedTuple, Generic[TAttribute]): max: nt.ProcNode[TAttribute] mean: nt.ProcNode[TAttribute] median: nt.ProcNode[TAttribute] min: nt.ProcNode[TAttribute] range: nt.ProcNode[TAttribute] standard_deviation: nt.ProcNode[TAttribute] sum: nt.ProcNode[TAttribute] variance: nt.ProcNode[TAttribute]
[docs] def attribute_statistic( geometry: nt.ProcNode[nt.Geometry] | None, attribute: nt.ProcNode[TAttribute] | None = None, selection: nt.SocketOrVal[bool] = True, domain: TDomain = "POINT", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> AttributeStatisticResult[TAttribute]: """ Uses a AttributeStatistic Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/attribute/attribute_statistic.html """ if data_type is None: data_type = RuntimeResolveDataType( [ NodeDataType.INT, NodeDataType.FLOAT, NodeDataType.FLOAT_VECTOR, NodeDataType.RGBA, ], ["Attribute"], ) res = nt.ProcNode.from_nodetype( node_type="GeometryNodeAttributeStatistic", inputs={"Attribute": attribute, "Geometry": geometry, "Selection": selection}, attrs={"domain": domain, "data_type": data_type}, ) return AttributeStatisticResult( max=res._output_socket("max"), mean=res._output_socket("mean"), median=res._output_socket("median"), min=res._output_socket("min"), range=res._output_socket("range"), standard_deviation=res._output_socket("standard_deviation"), sum=res._output_socket("sum"), variance=res._output_socket("variance"), )
[docs] def blur_attribute( value: nt.ProcNode[TAttribute] | None = None, iterations: nt.SocketOrVal[int] = 1, weight: nt.SocketOrVal[float] = 1.0, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> nt.ProcNode[TAttribute]: """ Uses a BlurAttribute Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/attribute/blur_attribute.html """ if data_type is None: data_type = RuntimeResolveDataType( [NodeDataType.INT, NodeDataType.FLOAT], ["Value"], ) return nt.ProcNode.from_nodetype( node_type="GeometryNodeBlurAttribute", inputs={"Iterations": iterations, "Value": value, "Weight": weight}, attrs={ "data_type": data_type, }, )
[docs] class BoundBoxResult(NamedTuple): bounding_box: nt.ProcNode[pt.MeshObject] min: nt.ProcNode[pt.Vector] max: nt.ProcNode[pt.Vector]
[docs] def bound_box(geometry: nt.ProcNode[nt.Geometry] | None) -> BoundBoxResult: """ Uses a BoundBox Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/bounding_box.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeBoundBox", inputs={"Geometry": geometry}, attrs={}, ) return BoundBoxResult( node._output_socket("bounding_box"), node._output_socket("min"), node._output_socket("max"), )
[docs] @dataclass class CaptureAttributeResult(Generic[TAnyGeometry]): geometry: nt.ProcNode[TAnyGeometry] attributes: dict[str, nt.ProcNode] def __getattr__(self, name: str) -> nt.ProcNode: if name in self.attributes: return self.attributes[name] else: return object.__getattribute__(self, name)
# CaptureAttribute has one fixed output (geometry) plus a dynamic set of captured # attribute outputs keyed by user-chosen names, so it can't be a NamedTuple like # the other multi-output bindings. Register it as a pytree container instead so # the computegraph builder flattens it into its geometry + attribute sockets. def _capture_result_flatten( obj: CaptureAttributeResult, ) -> tuple[list[nt.ProcNode], list[str]]: return [obj.geometry, *obj.attributes.values()], list(obj.attributes.keys()) def _capture_result_unflatten( children: list[nt.ProcNode], spec: pytree.PyTreeDef ) -> CaptureAttributeResult: geometry, *attrs = children return CaptureAttributeResult( geometry=geometry, attributes=dict(zip(spec.aux, attrs)) ) def _capture_result_names(obj: CaptureAttributeResult) -> list[str]: return ["geometry", *obj.attributes.keys()] pytree.register_pytree_container( CaptureAttributeResult, flatten_func=_capture_result_flatten, unflatten_func=_capture_result_unflatten, names_func=_capture_result_names, )
[docs] def capture_attribute( geometry: nt.ProcNode[TAnyGeometry] | None, # active_index: int = 0, # TODO unsure how active_* function # active_item: pt.NodeItem | None = None, domain: TDomain = "POINT", **attributes: nt.SocketOrVal[TAttribute], ) -> CaptureAttributeResult[TAnyGeometry]: """ Uses a CaptureAttribute Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/attribute/capture_attribute.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCaptureAttribute", inputs={"Geometry": geometry, **attributes}, attrs={ "domain": domain, }, ) return CaptureAttributeResult( geometry=res._output_socket("geometry"), attributes={k: res._output_socket(k) for k in attributes.keys()}, )
[docs] def collection_info( collection: nt.SocketOrVal[pt.Collection], separate_children: nt.SocketOrVal[bool] = False, reset_children: nt.SocketOrVal[bool] = False, transform_space: Literal["ORIGINAL", "RELATIVE"] = "ORIGINAL", ) -> nt.ProcNode[nt.Instances]: """ Uses a CollectionInfo Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/collection_info.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCollectionInfo", inputs={ "Collection": collection, "Separate Children": separate_children, "Reset Children": reset_children, }, attrs={"transform_space": transform_space}, )
[docs] def convex_hull( geometry: nt.ProcNode[nt.Geometry] | None, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a ConvexHull Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/convex_hull.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeConvexHull", inputs={"Geometry": geometry}, attrs={}, )
[docs] class CornerResult(NamedTuple): corner_index: nt.ProcNode[int] total: nt.ProcNode[int]
[docs] def corners_of_edge( edge_index: nt.SocketOrVal[int] = 0, weights: nt.SocketOrVal[float] = 0.0, sort_index: nt.SocketOrVal[int] = 0, ) -> CornerResult: """ Uses a CornersOfEdge Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/corners_of_edge.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCornersOfEdge", inputs={"Edge Index": edge_index, "Weights": weights, "Sort Index": sort_index}, attrs={}, ) return CornerResult( corner_index=res._output_socket("corner_index"), total=res._output_socket("total"), )
[docs] def corners_of_face( face_index: nt.SocketOrVal[int] = 0, weights: nt.SocketOrVal[float] = 0.0, sort_index: nt.SocketOrVal[int] = 0, ) -> CornerResult: """ Uses a CornersOfFace Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/corners_of_face.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCornersOfFace", inputs={"Face Index": face_index, "Weights": weights, "Sort Index": sort_index}, attrs={}, ) return CornerResult( corner_index=res._output_socket("corner_index"), total=res._output_socket("total"), )
[docs] def corners_of_vertex( vertex_index: nt.SocketOrVal[int] = 0, weights: nt.SocketOrVal[float] = 0.0, sort_index: nt.SocketOrVal[int] = 0, ) -> CornerResult: """ Uses a CornersOfVertex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/corners_of_vertex.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCornersOfVertex", inputs={ "Vertex Index": vertex_index, "Weights": weights, "Sort Index": sort_index, }, attrs={}, ) return CornerResult( corner_index=res._output_socket("corner_index"), total=res._output_socket("total"), )
[docs] def curve_arc( resolution: nt.SocketOrVal[int] = 16, radius: nt.SocketOrVal[float] = 1.0, start_angle: nt.SocketOrVal[float] = 0.0, sweep_angle: nt.SocketOrVal[float] = 5.497787, connect_center: nt.SocketOrVal[bool] = False, invert_arc: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurveArc Geometry Node in RADIUS mode (the arc is defined by a radius and a start/sweep angle, and produces a single curve output). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/arc.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveArc", inputs={ "Resolution": resolution, "Radius": radius, "Start Angle": start_angle, "Sweep Angle": sweep_angle, "Connect Center": connect_center, "Invert Arc": invert_arc, }, attrs={"mode": "RADIUS"}, )
[docs] class CurveArcFromPointsResult(NamedTuple): curve: nt.ProcNode[pt.CurveObject] center: nt.ProcNode[pt.Vector] normal: nt.ProcNode[pt.Vector] radius: nt.ProcNode[float]
[docs] def curve_arc_from_points( start: nt.SocketOrVal[pt.Vector], middle: nt.SocketOrVal[pt.Vector], end: nt.SocketOrVal[pt.Vector], resolution: nt.SocketOrVal[int] = 16, offset_angle: nt.SocketOrVal[float] = 0.0, connect_center: nt.SocketOrVal[bool] = False, invert_arc: nt.SocketOrVal[bool] = False, ) -> CurveArcFromPointsResult: """ Uses a CurveArc Geometry Node in POINTS mode (the arc passes through three points, additionally exposing the fitted center, normal and radius). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/arc.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveArc", inputs={ "Resolution": resolution, "Start": start, "Middle": middle, "End": end, "Offset Angle": offset_angle, "Connect Center": connect_center, "Invert Arc": invert_arc, }, attrs={"mode": "POINTS"}, ) return CurveArcFromPointsResult( node._output_socket("curve"), node._output_socket("center"), node._output_socket("normal"), node._output_socket("radius"), )
[docs] def curve_endpoint_selection( start_size: nt.SocketOrVal[int] = 1, end_size: nt.SocketOrVal[int] = 1 ) -> nt.ProcNode[bool]: """ Uses a CurveEndpointSelection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/endpoint_selection.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveEndpointSelection", inputs={"Start Size": start_size, "End Size": end_size}, attrs={}, )
# def curve_handle_type_selection( # handle_type: Literal["FREE", "AUTO", "VECTOR", "ALIGN"] = "AUTO", # mode: Literal["LEFT", "RIGHT"] = "RIGHT", # ) -> t.ProcNode: # """ # Uses a CurveHandleTypeSelection Geometry Node. # # See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/handle_type_selection.html # """ # return t.ProcNode.from_nodetype( # node_type="GeometryNodeCurveHandleTypeSelection", # inputs={}, # attrs={"handle_type": handle_type, "mode": mode}, # )
[docs] def curve_length(curve: nt.ProcNode[pt.CurveObject] | None) -> nt.ProcNode[float]: """ Uses a CurveLength Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/curve_length.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveLength", inputs={"Curve": curve}, attrs={}, )
[docs] class CurveOfPointResult(NamedTuple): curve_index: nt.ProcNode[int] index_in_curve: nt.ProcNode[int]
[docs] def curve_of_point(point_index: nt.SocketOrVal[int] = 0) -> CurveOfPointResult: """ Uses a CurveOfPoint Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/topology/curve_of_point.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveOfPoint", inputs={"Point Index": point_index}, attrs={}, ) return CurveOfPointResult( node._output_socket("curve_index"), node._output_socket("index_in_curve") )
[docs] def curve_bezier_segment( start: nt.SocketOrVal[nt.pt.Vector], start_handle: nt.SocketOrVal[nt.pt.Vector], end_handle: nt.SocketOrVal[nt.pt.Vector], end: nt.SocketOrVal[nt.pt.Vector], resolution: nt.SocketOrVal[int] = 16, mode: Literal["POSITION", "OFFSET"] = "POSITION", ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveBezierSegment Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/bezier_segment.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveBezierSegment", inputs={ "Resolution": resolution, "Start": start, "Start Handle": start_handle, "End Handle": end_handle, "End": end, }, attrs={"mode": mode}, )
[docs] def curve_circle( resolution: nt.SocketOrVal[int] = 32, radius: nt.SocketOrVal[float] = 1.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveCircle Geometry Node in RADIUS mode (the circle is defined by a radius and produces a single curve output). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/curve_circle.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveCircle", inputs={"Resolution": resolution, "Radius": radius}, attrs={"mode": "RADIUS"}, )
[docs] class CurveCircleFromPointsResult(NamedTuple): curve: nt.ProcNode[pt.CurveObject] center: nt.ProcNode[pt.Vector]
[docs] def curve_circle_from_points( point_1: nt.SocketOrVal[pt.Vector], point_2: nt.SocketOrVal[pt.Vector], point_3: nt.SocketOrVal[pt.Vector], resolution: nt.SocketOrVal[int] = 32, ) -> CurveCircleFromPointsResult: """ Uses a CurvePrimitiveCircle Geometry Node in POINTS mode (the circle passes through three points, additionally exposing the fitted center). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/curve_circle.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveCircle", inputs={ "Resolution": resolution, "Point 1": point_1, "Point 2": point_2, "Point 3": point_3, }, attrs={"mode": "POINTS"}, ) return CurveCircleFromPointsResult( node._output_socket("curve"), node._output_socket("center"), )
[docs] def curve_line( start: nt.SocketOrVal[nt.pt.Vector], end: nt.SocketOrVal[nt.pt.Vector], ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveLine Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/curve_line.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveLine", inputs={"Start": start, "End": end}, attrs={"mode": "POINTS"}, )
[docs] def curve_line_from_direction( start: nt.SocketOrVal[nt.pt.Vector], direction: nt.SocketOrVal[nt.pt.Vector], length: nt.SocketOrVal[float] = 1.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurveLineFromDirection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/curve_line.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveLine", inputs={"Start": start, "Direction": direction, "Length": length}, attrs={"mode": "DIRECTION"}, )
[docs] def curve_quadrilateral( width: nt.SocketOrVal[float] = 2.0, height: nt.SocketOrVal[float] = 2.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveQuadrilateral Geometry Node in RECTANGLE mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/quadrilateral.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveQuadrilateral", inputs={"Width": width, "Height": height}, attrs={"mode": "RECTANGLE"}, )
[docs] def curve_quadrilateral_parallelogram( width: nt.SocketOrVal[float] = 2.0, height: nt.SocketOrVal[float] = 2.0, offset: nt.SocketOrVal[float] = 1.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveQuadrilateral Geometry Node in PARALLELOGRAM mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/quadrilateral.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveQuadrilateral", inputs={"Width": width, "Height": height, "Offset": offset}, attrs={"mode": "PARALLELOGRAM"}, )
[docs] def curve_quadrilateral_trapezoid( bottom_width: nt.SocketOrVal[float] = 4.0, top_width: nt.SocketOrVal[float] = 2.0, height: nt.SocketOrVal[float] = 2.0, offset: nt.SocketOrVal[float] = 1.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveQuadrilateral Geometry Node in TRAPEZOID mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/quadrilateral.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveQuadrilateral", inputs={ "Bottom Width": bottom_width, "Top Width": top_width, "Height": height, "Offset": offset, }, attrs={"mode": "TRAPEZOID"}, )
[docs] def curve_quadrilateral_kite( width: nt.SocketOrVal[float] = 2.0, bottom_height: nt.SocketOrVal[float] = 3.0, top_height: nt.SocketOrVal[float] = 1.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveQuadrilateral Geometry Node in KITE mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/quadrilateral.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveQuadrilateral", inputs={ "Width": width, "Bottom Height": bottom_height, "Top Height": top_height, }, attrs={"mode": "KITE"}, )
[docs] def curve_quadrilateral_points( point_1: nt.SocketOrVal[pt.Vector], point_2: nt.SocketOrVal[pt.Vector], point_3: nt.SocketOrVal[pt.Vector], point_4: nt.SocketOrVal[pt.Vector], ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurvePrimitiveQuadrilateral Geometry Node in POINTS mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/quadrilateral.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurvePrimitiveQuadrilateral", inputs={ "Point 1": point_1, "Point 2": point_2, "Point 3": point_3, "Point 4": point_4, }, attrs={"mode": "POINTS"}, )
[docs] def curve_bezier( start: nt.SocketOrVal[nt.pt.Vector], middle: nt.SocketOrVal[nt.pt.Vector], end: nt.SocketOrVal[nt.pt.Vector], resolution: nt.SocketOrVal[int] = 16, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurveQuadraticBezier Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/quadratic_bezier.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveQuadraticBezier", inputs={"Resolution": resolution, "Start": start, "Middle": middle, "End": end}, attrs={}, )
[docs] def curve_set_handles( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, handle_type: Literal["FREE", "AUTO", "VECTOR", "ALIGN"] = "AUTO", mode: set[str] = frozenset({"LEFT", "RIGHT"}), ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurveSetHandles Geometry Node. `mode` is a flag set whose members are any of "LEFT", "RIGHT" (which curve handles to affect); defaults to both, matching Blender's default. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_handle_type.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveSetHandles", inputs={"Curve": curve, "Selection": selection}, attrs={"handle_type": handle_type, "mode": mode}, )
[docs] def curve_spiral( resolution: nt.SocketOrVal[int] = 32, rotations: nt.SocketOrVal[float] = 2.0, start_radius: nt.SocketOrVal[float] = 1.0, end_radius: nt.SocketOrVal[float] = 2.0, height: nt.SocketOrVal[float] = 2.0, reverse: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurveSpiral Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/curve_spiral.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveSpiral", inputs={ "Resolution": resolution, "Rotations": rotations, "Start Radius": start_radius, "End Radius": end_radius, "Height": height, "Reverse": reverse, }, attrs={}, )
[docs] def curve_spline_type( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, spline_type: Literal["CATMULL_ROM", "POLY", "BEZIER", "NURBS"] = "POLY", ) -> nt.ProcNode[pt.CurveObject]: """ Uses a CurveSplineType Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_spline_type.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveSplineType", inputs={"Curve": curve, "Selection": selection}, attrs={"spline_type": spline_type}, )
[docs] class CurveStarResult(NamedTuple): curve: nt.ProcNode[pt.CurveObject] outer_points: nt.ProcNode[bool]
[docs] def curve_star( points: nt.SocketOrVal[int] = 8, inner_radius: nt.SocketOrVal[float] = 1.0, outer_radius: nt.SocketOrVal[float] = 2.0, twist: nt.SocketOrVal[float] = 0.0, ) -> CurveStarResult: """ Uses a CurveStar Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/primitives/star.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveStar", inputs={ "Points": points, "Inner Radius": inner_radius, "Outer Radius": outer_radius, "Twist": twist, }, attrs={}, ) return CurveStarResult( curve=res._output_socket("curve"), outer_points=res._output_socket("outer_points"), )
[docs] def curve_to_mesh( curve: nt.ProcNode[pt.CurveObject] | None, profile_curve: nt.ProcNode[pt.CurveObject] | None = None, fill_caps: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a CurveToMesh Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/curve_to_mesh.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveToMesh", inputs={"Curve": curve, "Profile Curve": profile_curve, "Fill Caps": fill_caps}, attrs={}, )
[docs] class CurveToPointsResult(NamedTuple): points: nt.ProcNode[pt.MeshObject] tangent: nt.ProcNode[pt.Vector] normal: nt.ProcNode[pt.Vector] rotation: nt.ProcNode[pt.Vector]
[docs] def curve_to_points( curve: nt.ProcNode[pt.CurveObject] | None, count: nt.SocketOrVal[int] = 10, length: nt.SocketOrVal[float] = 1.0, mode: Literal["EVALUATED", "COUNT", "LENGTH"] = "COUNT", ) -> CurveToPointsResult: """ Uses a CurveToPoints Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/curve_to_points.html """ inputs = {"Curve": curve} if mode == "COUNT": inputs["Count"] = count elif mode == "LENGTH": inputs["Length"] = length res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveToPoints", inputs=inputs, attrs={"mode": mode}, ) return CurveToPointsResult( points=res._output_socket("points"), tangent=res._output_socket("tangent"), normal=res._output_socket("normal"), rotation=res._output_socket("rotation"), )
[docs] def curve_to_points_evaluated( curve: nt.ProcNode[pt.CurveObject] | None, ) -> CurveToPointsResult: """ Uses a CurveToPoints Geometry Node with mode="EVALUATED". From Blender docs: Creates points based on how the curve is evaluated. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/curve_to_points.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveToPoints", inputs={"Curve": curve}, attrs={"mode": "EVALUATED"}, ) return CurveToPointsResult( points=res._output_socket("points"), tangent=res._output_socket("tangent"), normal=res._output_socket("normal"), rotation=res._output_socket("rotation"), )
[docs] def curve_to_points_count( curve: nt.ProcNode[pt.CurveObject] | None, count: nt.SocketOrVal[int] = 10, ) -> CurveToPointsResult: """ Uses a CurveToPoints Geometry Node with mode="COUNT". See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/curve_to_points.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveToPoints", inputs={"Curve": curve, "Count": count}, attrs={"mode": "COUNT"}, ) return CurveToPointsResult( points=res._output_socket("points"), tangent=res._output_socket("tangent"), normal=res._output_socket("normal"), rotation=res._output_socket("rotation"), )
[docs] def curve_to_points_length( curve: nt.ProcNode[pt.CurveObject] | None, length: nt.SocketOrVal[float] = 0.1, ) -> CurveToPointsResult: """ Uses a CurveToPoints Geometry Node with mode="LENGTH". See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/curve_to_points.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeCurveToPoints", inputs={"Curve": curve, "Length": length}, attrs={"mode": "LENGTH"}, ) return CurveToPointsResult( points=res._output_socket("points"), tangent=res._output_socket("tangent"), normal=res._output_socket("normal"), rotation=res._output_socket("rotation"), )
[docs] def deform_curves_on_surface( curves: nt.ProcNode[pt.HairObject] | None, ) -> nt.ProcNode[pt.HairObject]: """ Uses a DeformCurvesOnSurface Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/deform_curves_on_surface.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeDeformCurvesOnSurface", inputs={"Curves": curves}, attrs={}, )
TDeleteGeometry = TypeVar( "TDeleteGeometry", nt.ProcNode[pt.MeshObject], nt.ProcNode[pt.CurveObject], )
[docs] def delete_geometry( geometry: nt.ProcNode[TDeleteGeometry] | None, selection: nt.SocketOrVal[bool] = True, domain: Literal["POINT", "EDGE", "FACE", "CURVE", "INSTANCE", "LAYER"] = "POINT", mode: Literal["ALL", "EDGE_FACE", "ONLY_FACE"] = "ALL", ) -> nt.ProcNode[TDeleteGeometry]: """ Uses a DeleteGeometry Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/delete_geometry.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeDeleteGeometry", inputs={"Geometry": geometry, "Selection": selection}, attrs={"domain": domain, "mode": mode}, )
[docs] def distribute_points_in_grid( grid: nt.SocketOrVal[float] = 0.0, density: nt.SocketOrVal[float] = 1.0, seed: nt.SocketOrVal[int] = 0, mode: Literal["DENSITY_RANDOM", "DENSITY_GRID"] = "DENSITY_RANDOM", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a DistributePointsInGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/distribute_points_in_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeDistributePointsInGrid", inputs={"Grid": grid, "Density": density, "Seed": seed}, attrs={"mode": mode}, )
[docs] def distribute_points_in_volume( volume: nt.ProcNode[pt.VolumeObject] | None, density: nt.SocketOrVal[float] = 1.0, seed: nt.SocketOrVal[int] = 0, mode: Literal["DENSITY_RANDOM", "DENSITY_GRID"] = "DENSITY_RANDOM", ) -> nt.ProcNode[pt.VolumeObject]: """ Uses a DistributePointsInVolume Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/distribute_points_in_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeDistributePointsInVolume", inputs={"Volume": volume, "Density": density, "Seed": seed}, attrs={"mode": mode}, )
[docs] class DistributePointsOnFacesResult(NamedTuple): points: nt.ProcNode[pt.MeshObject] normal: nt.ProcNode[pt.Vector] rotation: nt.ProcNode[pt.Vector]
[docs] def distribute_points_on_faces( mesh: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True, density: nt.SocketOrVal[float] | None = None, seed: nt.SocketOrVal[int] = 0, use_legacy_normal: bool = False, ) -> DistributePointsOnFacesResult: """ Uses a DistributePointsOnFaces Geometry Node with RANDOM distribution. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/distribute_points_on_faces.html """ inputs: dict = { "Mesh": mesh, "Selection": selection, "Seed": seed, } if density is not None: inputs["Density"] = density res = nt.ProcNode.from_nodetype( node_type="GeometryNodeDistributePointsOnFaces", inputs=inputs, attrs={ "distribute_method": "RANDOM", "use_legacy_normal": use_legacy_normal, }, ) return DistributePointsOnFacesResult( points=res._output_socket("points"), normal=res._output_socket("normal"), rotation=res._output_socket("rotation"), )
[docs] def distribute_points_on_faces_poisson( mesh: nt.ProcNode[pt.MeshObject] | None, density_factor: nt.SocketOrVal[float], selection: nt.SocketOrVal[bool] = True, distance_min: nt.SocketOrVal[float] = 0.0, density_max: nt.SocketOrVal[float] = 10.0, seed: nt.SocketOrVal[int] = 0, use_legacy_normal: bool = False, ) -> DistributePointsOnFacesResult: """ Uses a DistributePointsOnFaces Geometry Node with POISSON distribution. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/distribute_points_on_faces.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeDistributePointsOnFaces", inputs={ "Mesh": mesh, "Selection": selection, "Distance Min": distance_min, "Density Max": density_max, "Density Factor": density_factor, "Seed": seed, }, attrs={ "distribute_method": "POISSON", "use_legacy_normal": use_legacy_normal, }, ) return DistributePointsOnFacesResult( points=res._output_socket("points"), normal=res._output_socket("normal"), rotation=res._output_socket("rotation"), )
[docs] def dual_mesh( mesh: nt.ProcNode[pt.MeshObject] | None, keep_boundaries: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a DualMesh Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/dual_mesh.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeDualMesh", inputs={"Mesh": mesh, "Keep Boundaries": keep_boundaries}, attrs={}, )
[docs] class DuplicateElementsResult(NamedTuple, Generic[TAnyGeometry]): geometry: nt.ProcNode[TAnyGeometry] duplicate_index: nt.ProcNode[int]
[docs] def duplicate_elements( geometry: nt.ProcNode[TAnyGeometry] | None, selection: nt.SocketOrVal[bool] = True, amount: nt.SocketOrVal[int] = 1, domain: Literal["POINT", "EDGE", "FACE", "SPLINE", "INSTANCE"] = "POINT", ) -> DuplicateElementsResult[TAnyGeometry]: """ Uses a DuplicateElements Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/duplicate_elements.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeDuplicateElements", inputs={"Geometry": geometry, "Selection": selection, "Amount": amount}, attrs={"domain": domain}, ) return DuplicateElementsResult( geometry=res._output_socket("Geometry"), duplicate_index=res._output_socket("Duplicate Index"), )
[docs] def edge_paths_to_curves( mesh: nt.ProcNode[pt.MeshObject] | None, start_vertices: nt.SocketOrVal[bool] = True, next_vertex_index: nt.SocketOrVal[int] = -1, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a EdgePathsToCurves Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/edge_paths_to_curves.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeEdgePathsToCurves", inputs={ "Mesh": mesh, "Start Vertices": start_vertices, "Next Vertex Index": next_vertex_index, }, attrs={}, )
[docs] def edge_paths_to_selection( start_vertices: nt.SocketOrVal[bool] = True, next_vertex_index: nt.SocketOrVal[int] = -1, ) -> nt.ProcNode[bool]: """ Uses a EdgePathsToSelection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/edge_paths_to_selection.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeEdgePathsToSelection", inputs={ "Start Vertices": start_vertices, "Next Vertex Index": next_vertex_index, }, attrs={}, )
[docs] class EdgesOfCornerResult(NamedTuple): next_edge_index: nt.ProcNode[int] previous_edge_index: nt.ProcNode[int]
[docs] def edges_of_corner(corner_index: nt.SocketOrVal[int] = 0) -> EdgesOfCornerResult: """ Uses a EdgesOfCorner Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/edges_of_corner.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeEdgesOfCorner", inputs={"Corner Index": corner_index}, attrs={}, ) return EdgesOfCornerResult( next_edge_index=res._output_socket("next_edge_index"), previous_edge_index=res._output_socket("previous_edge_index"), )
[docs] class EdgesOfVertexResult(NamedTuple): edge_index: nt.ProcNode[int] total: nt.ProcNode[int]
[docs] def edges_of_vertex( vertex_index: nt.SocketOrVal[int] = 0, weights: nt.SocketOrVal[float] = 0.0, sort_index: nt.SocketOrVal[int] = 0, ) -> EdgesOfVertexResult: """ Uses a EdgesOfVertex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/edges_of_vertex.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeEdgesOfVertex", inputs={ "Vertex Index": vertex_index, "Weights": weights, "Sort Index": sort_index, }, attrs={}, ) return EdgesOfVertexResult( edge_index=res._output_socket("edge_index"), total=res._output_socket("total"), )
[docs] def edges_to_face_groups( boundary_edges: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[int]: """ Uses a EdgesToFaceGroups Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/edges_to_face_groups.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeEdgesToFaceGroups", inputs={"Boundary Edges": boundary_edges}, attrs={}, )
[docs] class ExtrudeMeshResult(NamedTuple): mesh: nt.ProcNode[pt.MeshObject] top: nt.ProcNode[bool] side: nt.ProcNode[bool]
[docs] def extrude_mesh( mesh: nt.ProcNode[pt.MeshObject] | None, offset: nt.SocketOrVal[nt.pt.Vector] | None = None, selection: nt.SocketOrVal[bool] = True, offset_scale: nt.SocketOrVal[float] = 1.0, individual: nt.SocketOrVal[bool] = True, mode: Literal["VERTICES", "EDGES", "FACES"] = "FACES", ) -> ExtrudeMeshResult: # The node's Individual socket only exists in FACES mode; in other modes a # passed value would be silently ignored, so reject it loudly. if mode != "FACES" and individual is not True: raise ValueError( f"individual is only meaningful with mode='FACES', got {mode=}" ) inputs = { "Mesh": mesh, "Selection": selection, "Offset Scale": offset_scale, } # A disconnected Offset extrudes along the normal (implicit field), so we # omit the input entirely when None rather than passing None to a value socket. if offset is not None: inputs["Offset"] = offset if mode == "FACES": inputs["Individual"] = individual node = nt.ProcNode.from_nodetype( node_type="GeometryNodeExtrudeMesh", inputs=inputs, attrs={"mode": mode}, ) return ExtrudeMeshResult( node._output_socket("mesh"), node._output_socket("top"), node._output_socket("side"), )
[docs] class FaceOfCornerResult(NamedTuple): face_index: nt.ProcNode[int] index_in_face: nt.ProcNode[int]
[docs] def face_of_corner(corner_index: nt.SocketOrVal[int] = 0) -> FaceOfCornerResult: """ Uses a FaceOfCorner Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/face_of_corner.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeFaceOfCorner", inputs={"Corner Index": corner_index}, attrs={}, ) return FaceOfCornerResult( node._output_socket("face_index"), node._output_socket("index_in_face") )
[docs] def field_at_index( value: TAttribute | None = None, index: nt.SocketOrVal[int] = 0, domain: TDomain = "POINT", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> nt.ProcNode[TAttribute]: """ Uses a FieldAtIndex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/utilities/field/evaluate_at_index.html """ if data_type is None: data_type = RuntimeResolveDataType(_SAMPLE_NODE_DATA_TYPES, ["Value"]) return nt.ProcNode.from_nodetype( node_type="GeometryNodeFieldAtIndex", inputs={"Index": index, "Value": value}, attrs={ "domain": domain, "data_type": data_type, }, )
TFieldOnDomain = TypeVar( "TFieldOnDomain", nt.SocketOrVal[bool], nt.SocketOrVal[int], nt.SocketOrVal[float] )
[docs] def field_on_domain( value: TFieldOnDomain = 0, domain: TDomain = "POINT", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> nt.ProcNode[TFieldOnDomain]: """ Uses a FieldOnDomain Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/utilities/field/evaluate_on_domain.html """ if data_type is None: data_type = RuntimeResolveDataType(_SAMPLE_NODE_DATA_TYPES, ["Value"]) return nt.ProcNode.from_nodetype( node_type="GeometryNodeFieldOnDomain", inputs={"Value": value}, attrs={ "domain": domain, "data_type": data_type, }, )
[docs] def fill_curve( curve: nt.ProcNode[pt.CurveObject] | None, group_id: nt.SocketOrVal[int] = 0, mode: Literal["TRIANGLES", "NGONS"] = "TRIANGLES", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a FillCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/fill_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeFillCurve", inputs={"Curve": curve, "Group ID": group_id}, attrs={"mode": mode}, )
[docs] def fillet_curve_bezier( curve: nt.ProcNode[pt.CurveObject] | None, radius: nt.SocketOrVal[float] = 0.25, limit_radius: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a FilletCurve Geometry Node in BEZIER mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/fillet_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeFilletCurve", inputs={ "Curve": curve, "Radius": radius, "Limit Radius": limit_radius, }, attrs={"mode": "BEZIER"}, )
[docs] def fillet_curve_poly( curve: nt.ProcNode[pt.CurveObject] | None, radius: nt.SocketOrVal[float] = 0.25, count: nt.SocketOrVal[int] = 1, limit_radius: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a FilletCurve Geometry Node in POLY mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/fillet_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeFilletCurve", inputs={ "Curve": curve, "Radius": radius, "Limit Radius": limit_radius, "Count": count, }, attrs={"mode": "POLY"}, )
[docs] def flip_faces( mesh: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True ) -> nt.ProcNode[pt.MeshObject]: """ Uses a FlipFaces Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/flip_faces.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeFlipFaces", inputs={"Mesh": mesh, "Selection": selection}, attrs={}, )
[docs] def geometry_to_instance( geometry: nt.ProcNode[TAnyGeometry] | None, ) -> nt.ProcNode[nt.Instances]: """ Uses a GeometryToInstance Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/geometry_to_instance.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeGeometryToInstance", inputs={"Geometry": geometry}, attrs={}, )
[docs] class GetNamedGridResult(NamedTuple): volume: nt.ProcNode[nt.Geometry] grid: nt.ProcNode[float]
[docs] def get_named_grid( volume: nt.ProcNode[nt.Geometry] | None, name: nt.SocketOrVal[str] = "", remove: nt.SocketOrVal[bool] = True, ) -> GetNamedGridResult: """ Uses a GetNamedGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/index.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeGetNamedGrid", inputs={"Name": name, "Remove": remove, "Volume": volume}, attrs={}, ) return GetNamedGridResult( node._output_socket("volume"), node._output_socket("grid") )
[docs] def grid_to_mesh( grid: nt.SocketOrVal[float] = 0.0, threshold: nt.SocketOrVal[float] = 0.1, adaptivity: nt.SocketOrVal[float] = 0.0, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a GridToMesh Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/operations/volume_to_mesh.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeGridToMesh", inputs={"Grid": grid, "Threshold": threshold, "Adaptivity": adaptivity}, attrs={}, )
''' def group(node_tree=None) -> t.ProcNode: """ Uses a Group Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/group.html """ return t.ProcNode.from_nodetype( node_type="GeometryNodeGroup", inputs={}, attrs={"node_tree": node_tree}, ) '''
[docs] class ImageInfoResult(NamedTuple): width: nt.ProcNode[int] height: nt.ProcNode[int] has_alpha: nt.ProcNode[bool] frame_count: nt.ProcNode[int] fps: nt.ProcNode[float]
[docs] def image_info( image: nt.SocketOrVal[pt.Image], frame: nt.SocketOrVal[int] = 0 ) -> ImageInfoResult: """ Uses a ImageInfo Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/image_info.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeImageInfo", inputs={"Image": image, "Frame": frame}, attrs={}, ) return ImageInfoResult( node._output_socket("width"), node._output_socket("height"), node._output_socket("has_alpha"), node._output_socket("frame_count"), node._output_socket("fps"), )
[docs] class ImageTextureResult(NamedTuple): color: nt.ProcNode[pt.Color] alpha: nt.ProcNode[float]
[docs] def image_texture( image: nt.SocketOrVal[pt.Image], vector: nt.SocketOrVal[nt.pt.Vector], frame: nt.SocketOrVal[int] = 0, extension: Literal["REPEAT", "EXTEND", "CLIP", "MIRROR"] = "REPEAT", interpolation: Literal["Linear", "Closest", "Cubic"] = "Linear", ) -> ImageTextureResult: """ Uses a ImageTexture Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/texture/image.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeImageTexture", inputs={"Image": image, "Vector": vector, "Frame": frame}, attrs={"extension": extension, "interpolation": interpolation}, ) return ImageTextureResult( node._output_socket("color"), node._output_socket("alpha") )
[docs] class IndexOfNearestResult(NamedTuple): index: nt.ProcNode[int] has_neighbor: nt.ProcNode[bool]
[docs] def index_of_nearest( position: nt.SocketOrVal[nt.pt.Vector], group_id: nt.SocketOrVal[int] = 0, ) -> IndexOfNearestResult: """ Uses a IndexOfNearest Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/sample/index_of_nearest.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeIndexOfNearest", inputs={"Position": position, "Group ID": group_id}, attrs={}, ) return IndexOfNearestResult( node._output_socket("index"), node._output_socket("has_neighbor") )
[docs] def input_active_camera() -> nt.ProcNode[pt.Object]: """ Uses a InputActiveCamera Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/active_camera.html """ raise_io_error("input_active_camera", logger=logger) return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputActiveCamera", inputs={}, attrs={}, )
[docs] class InputCurveHandlePositionsResult(NamedTuple): left: nt.ProcNode[pt.Vector] right: nt.ProcNode[pt.Vector]
[docs] def input_curve_handle_positions( relative: nt.SocketOrVal[bool] = False, ) -> InputCurveHandlePositionsResult: """ Uses a InputCurveHandlePositions Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/curve_handle_position.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputCurveHandlePositions", inputs={"Relative": relative}, attrs={}, ) return InputCurveHandlePositionsResult( node._output_socket("left"), node._output_socket("right") )
[docs] def input_curve_tilt() -> nt.ProcNode[float]: """ Uses a InputCurveTilt Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/curve_tilt.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputCurveTilt", inputs={}, attrs={}, )
[docs] def input_edge_smooth() -> nt.ProcNode[bool]: """ Uses a InputEdgeSmooth Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/is_edge_smooth.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputEdgeSmooth", inputs={}, attrs={}, )
[docs] def input_id() -> nt.ProcNode[int]: """ Uses a InputID Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/id.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputID", inputs={}, attrs={} )
[docs] def input_image(image: pt.Image | None = None) -> nt.ProcNode[pt.Image]: """ Uses a InputImage Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/constant/image.html """ raise_io_error("input_image", logger=logger) return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputImage", inputs={}, attrs={"image": image}, )
[docs] def input_index() -> nt.ProcNode[int]: """ Uses a InputIndex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/input_index.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputIndex", inputs={}, attrs={}, )
[docs] def input_instance_rotation() -> nt.ProcNode[pt.Vector]: """ Uses a InputInstanceRotation Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/constant/rotation.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputInstanceRotation", inputs={}, attrs={}, )
[docs] def input_instance_scale() -> nt.ProcNode[pt.Vector]: """ Uses a InputInstanceScale Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/instance_scale.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputInstanceScale", inputs={}, attrs={}, )
[docs] def input_material(material: pt.Material | None = None) -> nt.ProcNode[pt.Material]: """ Uses a InputMaterial Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/constant/material.html """ raise_io_error("input_material", logger=logger) return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMaterial", inputs={}, attrs={"material": material}, )
[docs] def input_material_index() -> nt.ProcNode[int]: """ Uses a InputMaterialIndex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/material/material_index.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMaterialIndex", inputs={}, attrs={}, )
[docs] class InputMeshEdgeAngleResult(NamedTuple): unsigned_angle: nt.ProcNode[float] signed_angle: nt.ProcNode[float]
[docs] def input_mesh_edge_angle() -> InputMeshEdgeAngleResult: """ Uses a InputMeshEdgeAngle Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/edge_angle.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshEdgeAngle", inputs={}, attrs={}, ) return InputMeshEdgeAngleResult( node._output_socket("unsigned_angle"), node._output_socket("signed_angle") )
[docs] def input_mesh_edge_neighbors() -> nt.ProcNode[int]: """ Uses a InputMeshEdgeNeighbors Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/edge_neighbors.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshEdgeNeighbors", inputs={}, attrs={}, )
[docs] class InputMeshEdgeVerticesResult(NamedTuple): vertex_index_1: nt.ProcNode[int] vertex_index_2: nt.ProcNode[int] position_1: nt.ProcNode[pt.Vector] position_2: nt.ProcNode[pt.Vector]
[docs] def input_mesh_edge_vertices() -> InputMeshEdgeVerticesResult: """ Uses a InputMeshEdgeVertices Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/edge_vertices.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshEdgeVertices", inputs={}, attrs={}, ) return InputMeshEdgeVerticesResult( node._output_socket("vertex_index_1"), node._output_socket("vertex_index_2"), node._output_socket("position_1"), node._output_socket("position_2"), )
[docs] def input_mesh_face_area() -> nt.ProcNode[float]: """ Uses a InputMeshFaceArea Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/face_area.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshFaceArea", inputs={}, attrs={}, )
[docs] def input_mesh_face_is_planar( threshold: nt.SocketOrVal[float] = 0.01, ) -> nt.ProcNode[bool]: """ Uses a InputMeshFaceIsPlanar Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/face_is_planar.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshFaceIsPlanar", inputs={"Threshold": threshold}, attrs={}, )
[docs] class InputMeshFaceNeighborsResult(NamedTuple): vertex_count: nt.ProcNode[int] face_count: nt.ProcNode[int]
[docs] def input_mesh_face_neighbors() -> InputMeshFaceNeighborsResult: """ Uses a InputMeshFaceNeighbors Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/face_neighbors.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshFaceNeighbors", inputs={}, attrs={}, ) return InputMeshFaceNeighborsResult( vertex_count=res._output_socket("vertex_count"), face_count=res._output_socket("face_count"), )
[docs] class InputMeshIslandResult(NamedTuple): island_index: nt.ProcNode[int] island_count: nt.ProcNode[int]
[docs] def input_mesh_island() -> InputMeshIslandResult: """ Uses a InputMeshIsland Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/mesh_island.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshIsland", inputs={}, attrs={}, ) return InputMeshIslandResult( node._output_socket("island_index"), node._output_socket("island_count") )
[docs] class InputMeshVertexNeighborsResult(NamedTuple): vertex_count: nt.ProcNode[int] face_count: nt.ProcNode[int]
[docs] def input_mesh_vertex_neighbors() -> InputMeshVertexNeighborsResult: """ Uses a InputMeshVertexNeighbors Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/vertex_neighbors.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputMeshVertexNeighbors", inputs={}, attrs={}, ) return InputMeshVertexNeighborsResult( node._output_socket("vertex_count"), node._output_socket("face_count") )
[docs] class InputNamedAttributeResult(NamedTuple): attribute: nt.ProcNode exists: nt.ProcNode[bool]
[docs] def input_named_attribute( name: nt.SocketOrVal[str] = "", data_type: NodeDataType | None = None, ) -> InputNamedAttributeResult: """ Uses a InputNamedAttribute Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/named_attribute.html """ assert data_type is not NodeDataType.FLOAT_VECTOR_2D, ( "GeometryNodeInputNamedAttribute does not support FLOAT_VECTOR_2D; use FLOAT_VECTOR instead" ) node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputNamedAttribute", inputs={"Name": name}, attrs={"data_type": data_type}, ) return InputNamedAttributeResult( node._output_socket("attribute"), node._output_socket("exists") )
[docs] def input_named_layer_selection(name: nt.SocketOrVal[str] = "") -> nt.ProcNode[bool]: """ Uses a InputNamedLayerSelection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/selection.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputNamedLayerSelection", inputs={"Name": name}, attrs={}, )
[docs] def input_normal() -> nt.ProcNode[pt.Vector]: """ Uses a InputNormal Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/normal.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputNormal", inputs={}, attrs={}, )
[docs] def input_position() -> nt.ProcNode[pt.Vector]: """ Uses a InputPosition Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/position.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputPosition", inputs={}, attrs={}, )
[docs] def input_radius() -> nt.ProcNode[float]: """ Uses a InputRadius Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/radius.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputRadius", inputs={}, attrs={}, )
[docs] class InputSceneTimeResult(NamedTuple): seconds: nt.ProcNode[float] frame: nt.ProcNode[int]
[docs] def input_scene_time() -> InputSceneTimeResult: """ Uses a InputSceneTime Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/scene_time.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputSceneTime", inputs={}, attrs={}, ) return InputSceneTimeResult( seconds=node._output_socket("seconds"), frame=node._output_socket("frame"), )
[docs] def input_shade_smooth() -> nt.ProcNode[bool]: """ Uses a InputShadeSmooth Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/write/set_shade_smooth.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputShadeSmooth", inputs={}, attrs={}, )
[docs] class InputShortestEdgePathsResult(NamedTuple): next_vertex_index: nt.ProcNode[int] total_cost: nt.ProcNode[float]
[docs] def input_shortest_edge_paths( end_vertex: nt.SocketOrVal[bool] = False, edge_cost: nt.SocketOrVal[float] = 1.0 ) -> InputShortestEdgePathsResult: """ Uses a InputShortestEdgePaths Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/shortest_edge_paths.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeInputShortestEdgePaths", inputs={"End Vertex": end_vertex, "Edge Cost": edge_cost}, attrs={}, ) return InputShortestEdgePathsResult( next_vertex_index=res._output_socket("next_vertex_index"), total_cost=res._output_socket("total_cost"), )
[docs] def input_spline_cyclic() -> nt.ProcNode[bool]: """ Uses a InputSplineCyclic Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/is_spline_cyclic.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputSplineCyclic", inputs={}, attrs={}, )
[docs] def input_spline_resolution() -> nt.ProcNode[int]: """ Uses a InputSplineResolution Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/spline_resolution.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputSplineResolution", inputs={}, attrs={}, )
[docs] def input_tangent() -> nt.ProcNode[pt.Vector]: """ Uses a InputTangent Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/curve_tangent.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInputTangent", inputs={}, attrs={}, )
[docs] def instance_on_points( points: nt.ProcNode[nt.Geometry] | None, instance: nt.ProcNode[nt.Geometry] | None, rotation: nt.SocketOrVal[pt.Euler] = (0, 0, 0), scale: nt.SocketOrVal[pt.Vector] = (1, 1, 1), selection: nt.SocketOrVal[bool] = True, pick_instance: nt.SocketOrVal[bool] = False, instance_index: nt.SocketOrVal[int] = 0, ) -> nt.ProcNode[nt.Instances]: """ Uses a InstanceOnPoints Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/instance_on_points.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInstanceOnPoints", inputs={ "Points": points, "Selection": selection, "Instance": instance, "Pick Instance": pick_instance, "Instance Index": instance_index, "Rotation": rotation, "Scale": scale, }, attrs={}, )
[docs] def instance_transform() -> nt.ProcNode: """ Uses a InstanceTransform Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/instance_transform.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInstanceTransform", inputs={}, attrs={}, )
[docs] def instances_to_points( position: nt.SocketOrVal[nt.pt.Vector], instances: nt.ProcNode[nt.Instances] | None, selection: nt.SocketOrVal[bool] = True, radius: nt.SocketOrVal[float] = 0.05, ) -> nt.ProcNode[nt.Points]: """ Uses a InstancesToPoints Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/instances_to_points.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeInstancesToPoints", inputs={ "Instances": instances, "Selection": selection, "Position": position, "Radius": radius, }, attrs={}, )
[docs] class InterpolateCurvesResult(NamedTuple): curves: nt.ProcNode[pt.HairObject] closest_index: nt.ProcNode[int] closest_weight: nt.ProcNode[float]
[docs] def interpolate_curves( guide_curves: nt.ProcNode[pt.HairObject] | None, points: nt.ProcNode[nt.Geometry] | None, guide_up: nt.SocketOrVal[nt.pt.Vector], point_up: nt.SocketOrVal[nt.pt.Vector], guide_group_id: nt.SocketOrVal[int] = 0, point_group_id: nt.SocketOrVal[int] = 0, max_neighbors: nt.SocketOrVal[int] = 4, ) -> InterpolateCurvesResult: """ Uses a InterpolateCurves Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/interpolate_curves.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeInterpolateCurves", inputs={ "Guide Curves": guide_curves, "Guide Up": guide_up, "Guide Group ID": guide_group_id, "Points": points, "Point Up": point_up, "Point Group ID": point_group_id, "Max Neighbors": max_neighbors, }, attrs={}, ) return InterpolateCurvesResult( curves=res._output_socket("curves"), closest_index=res._output_socket("closest_index"), closest_weight=res._output_socket("closest_weight"), )
[docs] def is_viewport() -> nt.ProcNode[bool]: """ Uses a IsViewport Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/is_viewport.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeIsViewport", inputs={}, attrs={}, )
[docs] def join_geometry( geometries: list[nt.ProcNode[TAnyGeometry]], ) -> nt.ProcNode[TAnyGeometry]: """ Uses a JoinGeometry Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/join_geometry.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeJoinGeometry", inputs={"Geometry": geometries}, attrs={}, )
[docs] def material_selection( material: nt.SocketOrVal[pt.Material], ) -> nt.ProcNode[bool]: """ Uses a MaterialSelection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/material/material_selection.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMaterialSelection", inputs={"Material": material}, attrs={}, )
""" TMenuSwitch = TypeVar( "TMenuSwitch", nt.SocketOrVal[bool], nt.SocketOrVal[int], nt.SocketOrVal[pt.Color], nt.SocketOrVal[str], nt.SocketOrVal[float], nt.SocketOrVal[nt.pt.Vector], ) def menu_switch( a: TMenuSwitch = 0, b: TMenuSwitch = 0, menu: nt.SocketOrVal[str] = "A", active_index: int = 1, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> nt.ProcNode[TMenuSwitch]: if data_type is None: data_type = RuntimeResolveDataType( [ NodeDataType.BOOLEAN, NodeDataType.INT, NodeDataType.RGBA, NodeDataType.STRING, NodeDataType.FLOAT, NodeDataType.FLOAT_VECTOR, ], ["A", "B"], ) return nt.ProcNode.from_nodetype( node_type="GeometryNodeMenuSwitch", inputs={"A": a, "B": b, "Menu": menu}, attrs={ "active_index": active_index, "data_type": data_type, }, ) """
[docs] def merge_by_distance( geometry: nt.ProcNode[nt.Geometry] | None, selection: nt.SocketOrVal[bool] = True, distance: nt.SocketOrVal[float] = 0.001, mode: Literal["ALL", "CONNECTED"] = "ALL", ) -> nt.ProcNode[nt.Geometry]: """ Uses a MergeByDistance Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/merge_by_distance.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMergeByDistance", inputs={"Geometry": geometry, "Selection": selection, "Distance": distance}, attrs={"mode": mode}, )
[docs] class MeshBooleanResult(NamedTuple): mesh: nt.ProcNode[pt.MeshObject] # edges where the inputs cut each other; only the EXACT solver outputs this, # so it is None when solver="FLOAT" intersecting_edges: nt.ProcNode[bool] | None
[docs] def mesh_boolean( a: nt.ProcNode[nt.Geometry] | None, b: nt.ProcNode[nt.Geometry] | None, self_intersection: nt.SocketOrVal[bool] = False, hole_tolerant: nt.SocketOrVal[bool] = False, solver: Literal["EXACT", "FLOAT"] = "FLOAT", ) -> MeshBooleanResult: """ Uses a MeshBoolean Geometry Node in DIFFERENCE mode (a minus b). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_boolean.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshBoolean", inputs={ "Mesh 1": a, "Mesh 2": b, "Self Intersection": self_intersection, "Hole Tolerant": hole_tolerant, }, attrs={"operation": "DIFFERENCE", "solver": solver}, ) return MeshBooleanResult( node._output_socket("mesh"), node._output_socket("intersecting_edges") if solver == "EXACT" else None, )
[docs] def mesh_boolean_union( mesh: nt.ProcNode[nt.Geometry] | list[nt.ProcNode[nt.Geometry]] | None, self_intersection: nt.SocketOrVal[bool] = False, hole_tolerant: nt.SocketOrVal[bool] = False, solver: Literal["EXACT", "FLOAT"] = "FLOAT", ) -> MeshBooleanResult: """ Uses a MeshBoolean Geometry Node in UNION mode (combines the input meshes). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_boolean.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshBoolean", inputs={ "Mesh 2": mesh, "Self Intersection": self_intersection, "Hole Tolerant": hole_tolerant, }, attrs={"operation": "UNION", "solver": solver}, ) return MeshBooleanResult( node._output_socket("mesh"), node._output_socket("intersecting_edges") if solver == "EXACT" else None, )
[docs] def mesh_boolean_intersect( mesh: nt.ProcNode[nt.Geometry] | list[nt.ProcNode[nt.Geometry]] | None, self_intersection: nt.SocketOrVal[bool] = False, hole_tolerant: nt.SocketOrVal[bool] = False, solver: Literal["EXACT", "FLOAT"] = "FLOAT", ) -> MeshBooleanResult: """ Uses a MeshBoolean Geometry Node in INTERSECT mode (keeps the volume shared by all input meshes). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_boolean.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshBoolean", inputs={ "Mesh 2": mesh, "Self Intersection": self_intersection, "Hole Tolerant": hole_tolerant, }, attrs={"operation": "INTERSECT", "solver": solver}, ) return MeshBooleanResult( node._output_socket("mesh"), node._output_socket("intersecting_edges") if solver == "EXACT" else None, )
[docs] def mesh_circle( vertices: nt.SocketOrVal[int] = 32, radius: nt.SocketOrVal[float] = 1.0, fill_type: Literal["NONE", "NGON", "TRIANGLE_FAN"] = "NONE", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a MeshCircle Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/mesh_circle.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshCircle", inputs={"Vertices": vertices, "Radius": radius}, attrs={"fill_type": fill_type}, )
[docs] class MeshResult(NamedTuple): mesh: nt.ProcNode[pt.MeshObject] uv_map: nt.ProcNode[pt.MeshObject]
[docs] class MeshConeResult(NamedTuple): mesh: nt.ProcNode[pt.MeshObject] uv_map: nt.ProcNode[pt.MeshObject] top: nt.ProcNode[pt.MeshObject] bottom: nt.ProcNode[pt.MeshObject] side: nt.ProcNode[pt.MeshObject]
[docs] def mesh_cone( vertices: nt.SocketOrVal[int] = 32, side_segments: nt.SocketOrVal[int] = 1, fill_segments: nt.SocketOrVal[int] = 1, radius_top: nt.SocketOrVal[float] = 0.0, radius_bottom: nt.SocketOrVal[float] = 1.0, depth: nt.SocketOrVal[float] = 2.0, fill_type: Literal["NONE", "NGON", "TRIANGLE_FAN"] = "NGON", ) -> MeshConeResult: """ Uses a MeshCone Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/cone.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshCone", inputs={ "Vertices": vertices, "Side Segments": side_segments, "Fill Segments": fill_segments, "Radius Top": radius_top, "Radius Bottom": radius_bottom, "Depth": depth, }, attrs={"fill_type": fill_type}, ) return MeshConeResult( mesh=res._output_socket("mesh"), uv_map=res._output_socket("uv_map"), top=res._output_socket("top"), bottom=res._output_socket("bottom"), side=res._output_socket("side"), )
[docs] def mesh_cube( size: nt.SocketOrVal[nt.pt.Vector] = (1, 1, 1), vertices_x: nt.SocketOrVal[int] = 2, vertices_y: nt.SocketOrVal[int] = 2, vertices_z: nt.SocketOrVal[int] = 2, ) -> MeshResult: """ Uses a MeshCube Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/cube.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshCube", inputs={ "Size": size, "Vertices X": vertices_x, "Vertices Y": vertices_y, "Vertices Z": vertices_z, }, attrs={}, ) return MeshResult( mesh=res._output_socket("mesh"), uv_map=res._output_socket("uv_map"), )
[docs] class MeshCylinderResult(NamedTuple): mesh: nt.ProcNode[pt.MeshObject] top: nt.ProcNode[pt.MeshObject] side: nt.ProcNode[pt.MeshObject] bottom: nt.ProcNode[pt.MeshObject] uv_map: nt.ProcNode[pt.MeshObject]
[docs] def mesh_cylinder( vertices: nt.SocketOrVal[int] = 32, side_segments: nt.SocketOrVal[int] = 1, fill_segments: nt.SocketOrVal[int] = 1, radius: nt.SocketOrVal[float] = 1.0, depth: nt.SocketOrVal[float] = 2.0, fill_type: Literal["NONE", "NGON", "TRIANGLE_FAN"] = "NGON", ) -> MeshCylinderResult: """ Uses a MeshCylinder Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/cylinder.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshCylinder", inputs={ "Vertices": vertices, "Side Segments": side_segments, "Fill Segments": fill_segments, "Radius": radius, "Depth": depth, }, attrs={"fill_type": fill_type}, ) return MeshCylinderResult( mesh=res._output_socket("mesh"), top=res._output_socket("top"), side=res._output_socket("side"), bottom=res._output_socket("bottom"), uv_map=res._output_socket("uv_map"), )
[docs] def mesh_face_set_boundaries( face_group_id: nt.SocketOrVal[int] = 0, ) -> nt.ProcNode[bool]: """ Uses a MeshFaceSetBoundaries Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/face_group_boundaries.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshFaceSetBoundaries", inputs={"Face Group ID": face_group_id}, attrs={}, )
[docs] def mesh_grid( size_x: nt.SocketOrVal[float] = 1.0, size_y: nt.SocketOrVal[float] = 1.0, vertices_x: nt.SocketOrVal[int] = 3, vertices_y: nt.SocketOrVal[int] = 3, ) -> MeshResult: """ Uses a MeshGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/grid.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshGrid", inputs={ "Size X": size_x, "Size Y": size_y, "Vertices X": vertices_x, "Vertices Y": vertices_y, }, attrs={}, ) return MeshResult( mesh=res._output_socket("mesh"), uv_map=res._output_socket("uv_map"), )
[docs] def mesh_icosphere( radius: nt.SocketOrVal[float] = 1.0, subdivisions: nt.SocketOrVal[int] = 1 ) -> MeshResult: """ Uses a MeshIcoSphere Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/icosphere.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshIcoSphere", inputs={"Radius": radius, "Subdivisions": subdivisions}, attrs={}, ) return MeshResult( mesh=res._output_socket("mesh"), uv_map=res._output_socket("uv_map"), )
[docs] def mesh_line( start_location: nt.SocketOrVal[nt.pt.Vector], offset: nt.SocketOrVal[nt.pt.Vector], count: nt.SocketOrVal[int] = 10, count_mode: Literal["TOTAL", "RESOLUTION"] = "TOTAL", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a MeshLine Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/mesh_line.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshLine", inputs={"Count": count, "Start Location": start_location, "Offset": offset}, attrs={"count_mode": count_mode, "mode": "OFFSET"}, )
[docs] def mesh_line_from_endpoints( start_location: nt.SocketOrVal[nt.pt.Vector], end_location: nt.SocketOrVal[nt.pt.Vector], count: nt.SocketOrVal[int] = 10, count_mode: Literal["TOTAL", "RESOLUTION"] = "TOTAL", ) -> nt.ProcNode[pt.MeshObject]: return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshLine", inputs={ "Count": count, "Start Location": start_location, "Offset": end_location, # 4.2 uses "Offset" key in both cases }, attrs={"count_mode": count_mode, "mode": "END_POINTS"}, )
[docs] def mesh_to_curve( mesh: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True ) -> nt.ProcNode[pt.CurveObject]: """ Uses a MeshToCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_to_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshToCurve", inputs={"Mesh": mesh, "Selection": selection}, attrs={}, )
[docs] def mesh_to_density_grid( mesh: nt.ProcNode[pt.MeshObject] | None, density: nt.SocketOrVal[float] = 1.0, voxel_size: nt.SocketOrVal[float] = 0.3, gradient_width: nt.SocketOrVal[float] = 0.2, ) -> nt.ProcNode: """ Uses a MeshToDensityGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_to_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshToDensityGrid", inputs={ "Mesh": mesh, "Density": density, "Voxel Size": voxel_size, "Gradient Width": gradient_width, }, attrs={}, )
[docs] def mesh_to_points( mesh: nt.ProcNode[pt.MeshObject] | None, position: nt.SocketOrVal[nt.pt.Vector], selection: nt.SocketOrVal[bool] = True, radius: nt.SocketOrVal[float] = 0.05, mode: Literal["VERTICES", "EDGES", "FACES", "CORNERS"] = "VERTICES", ) -> nt.ProcNode[nt.Points]: """ Uses a MeshToPoints Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_to_points.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshToPoints", inputs={ "Mesh": mesh, "Selection": selection, "Position": position, "Radius": radius, }, attrs={"mode": mode}, )
[docs] def mesh_to_sdf_grid( mesh: nt.ProcNode[pt.MeshObject] | None, voxel_size: nt.SocketOrVal[float] = 0.3, band_width: nt.SocketOrVal[int] = 3, ) -> nt.ProcNode: """ Uses a MeshToSDFGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_to_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshToSDFGrid", inputs={"Mesh": mesh, "Voxel Size": voxel_size, "Band Width": band_width}, attrs={}, )
[docs] def mesh_to_volume( mesh: nt.ProcNode[pt.MeshObject] | None, density: nt.SocketOrVal[float] = 1.0, voxel_amount: nt.SocketOrVal[float] = 64.0, interior_band_width: nt.SocketOrVal[float] = 0.2, resolution_mode: Literal["VOXEL_AMOUNT", "VOXEL_SIZE"] = "VOXEL_AMOUNT", ) -> nt.ProcNode[pt.VolumeObject]: """ Uses a MeshToVolume Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/mesh_to_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshToVolume", inputs={ "Mesh": mesh, "Density": density, "Voxel Amount": voxel_amount, "Interior Band Width": interior_band_width, }, attrs={"resolution_mode": resolution_mode}, )
[docs] def mesh_uv_sphere( segments: nt.SocketOrVal[int] = 32, rings: nt.SocketOrVal[int] = 16, radius: nt.SocketOrVal[float] = 1.0, ) -> MeshResult: """ Uses a MeshUVSphere Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/primitives/uv_sphere.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeMeshUVSphere", inputs={"Segments": segments, "Rings": rings, "Radius": radius}, attrs={}, ) return MeshResult( mesh=res._output_socket("mesh"), uv_map=res._output_socket("uv_map"), )
TObjectInfo = TypeVar("TObjectInfo", pt.MeshObject, pt.CurveObject, pt.VolumeObject)
[docs] class ObjectInfoResult(NamedTuple, Generic[TObjectInfo]): geometry: nt.ProcNode[TObjectInfo] transform: nt.ProcNode[pt.Vector] location: nt.ProcNode[pt.Vector] rotation: nt.ProcNode[pt.Vector] scale: nt.ProcNode[pt.Vector]
[docs] def object_info( object: nt.SocketOrVal[TObjectInfo], as_instance: nt.SocketOrVal[bool] = False, transform_space: Literal["ORIGINAL", "RELATIVE"] = "ORIGINAL", ) -> ObjectInfoResult[TObjectInfo]: """ Uses a ObjectInfo Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/object_info.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeObjectInfo", inputs={"Object": object, "As Instance": as_instance}, attrs={"transform_space": transform_space}, ) return ObjectInfoResult( geometry=res._output_socket("geometry"), transform=res._output_socket("transform"), location=res._output_socket("location"), rotation=res._output_socket("rotation"), scale=res._output_socket("scale"), )
[docs] def offset_corner_in_face( corner_index: nt.SocketOrVal[int] = 0, offset: nt.SocketOrVal[int] = 0 ) -> nt.ProcNode[int]: """ Uses a OffsetCornerInFace Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/offset_corner_in_face.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeOffsetCornerInFace", inputs={"Corner Index": corner_index, "Offset": offset}, attrs={}, )
[docs] class OffsetPointInCurveResult(NamedTuple): is_valid_offset: nt.ProcNode[bool] point_index: nt.ProcNode[int]
[docs] def offset_point_in_curve( point_index: nt.SocketOrVal[int] = 0, offset: nt.SocketOrVal[int] = 0 ) -> OffsetPointInCurveResult: """ Uses a OffsetPointInCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/topology/offset_point_in_curve.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeOffsetPointInCurve", inputs={"Point Index": point_index, "Offset": offset}, attrs={}, ) return OffsetPointInCurveResult( is_valid_offset=res._output_socket("is_valid_offset"), point_index=res._output_socket("point_index"), )
[docs] def points( position: nt.SocketOrVal[nt.pt.Vector], count: nt.SocketOrVal[int] = 1, radius: nt.SocketOrVal[float] = 0.1, ) -> nt.ProcNode[nt.Points]: """ Uses a Points Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/points.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodePoints", inputs={"Count": count, "Position": position, "Radius": radius}, attrs={}, )
[docs] class PointsOfCurveResult(NamedTuple): point_index: nt.ProcNode[int] total: nt.ProcNode[int]
[docs] def points_of_curve( curve_index: nt.SocketOrVal[int] = 0, weights: nt.SocketOrVal[float] = 0.0, sort_index: nt.SocketOrVal[int] = 0, ) -> PointsOfCurveResult: """ Uses a PointsOfCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/topology/points_of_curve.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodePointsOfCurve", inputs={ "Curve Index": curve_index, "Weights": weights, "Sort Index": sort_index, }, attrs={}, ) return PointsOfCurveResult( point_index=res._output_socket("point_index"), total=res._output_socket("total"), )
[docs] def points_to_curves( points: nt.ProcNode[nt.Geometry] | None, curve_group_id: nt.SocketOrVal[int] = 0, weight: nt.SocketOrVal[float] = 0.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a PointsToCurves Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/points_to_curves.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodePointsToCurves", inputs={"Points": points, "Curve Group ID": curve_group_id, "Weight": weight}, attrs={}, )
[docs] def points_to_sdf_grid( points: nt.ProcNode[nt.Points] | None, radius: nt.SocketOrVal[float] = 0.5, voxel_size: nt.SocketOrVal[float] = 0.3, ) -> nt.ProcNode: """ Uses a PointsToSDFGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/points_to_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodePointsToSDFGrid", inputs={"Points": points, "Radius": radius, "Voxel Size": voxel_size}, attrs={}, )
[docs] def points_to_vertices( points: nt.ProcNode[nt.Points] | None, selection: nt.SocketOrVal[bool] = True ) -> nt.ProcNode[pt.MeshObject]: """ Uses a PointsToVertices Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/points_to_vertices.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodePointsToVertices", inputs={"Points": points, "Selection": selection}, attrs={}, )
[docs] def points_to_volume( points: nt.ProcNode[nt.Points] | None, density: nt.SocketOrVal[float] = 1.0, voxel_amount: nt.SocketOrVal[float] = 64.0, radius: nt.SocketOrVal[float] = 0.5, resolution_mode: Literal["VOXEL_AMOUNT", "VOXEL_SIZE"] = "VOXEL_AMOUNT", ) -> nt.ProcNode[pt.VolumeObject]: """ Uses a PointsToVolume Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/points_to_volume.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodePointsToVolume", inputs={ "Points": points, "Density": density, "Voxel Amount": voxel_amount, "Radius": radius, }, attrs={"resolution_mode": resolution_mode}, )
[docs] class ProximityResult(NamedTuple): position: nt.ProcNode[nt.pt.Vector] distance: nt.ProcNode[float] is_valid: nt.ProcNode[bool]
[docs] def proximity( geometry: nt.ProcNode[pt.MeshObject] | None, sample_position: nt.SocketOrVal[nt.pt.Vector], group_id: nt.SocketOrVal[int] = 0, sample_group_id: nt.SocketOrVal[int] = 0, target_element: Literal["POINTS", "EDGES", "FACES"] = "FACES", ) -> ProximityResult: """ Uses a Proximity Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/sample/geometry_proximity.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeProximity", inputs={ "Geometry": geometry, "Group ID": group_id, "Sample Position": sample_position, "Sample Group ID": sample_group_id, }, attrs={"target_element": target_element}, ) return ProximityResult( position=res._output_socket("position"), distance=res._output_socket("distance"), is_valid=res._output_socket("is_valid"), )
TRaycast = TypeVar( "TRaycast", nt.SocketOrVal[bool], nt.SocketOrVal[int], nt.SocketOrVal[float] )
[docs] class RaycastResult(NamedTuple): attribute: nt.ProcNode[nt.pt.Vector] hit_distance: nt.ProcNode[float] hit_normal: nt.ProcNode[nt.pt.Vector] hit_position: nt.ProcNode[nt.pt.Vector] is_hit: nt.ProcNode[bool]
[docs] def raycast( geometry: nt.ProcNode[pt.MeshObject] | None, source_position: nt.SocketOrVal[nt.pt.Vector], ray_direction: nt.SocketOrVal[nt.pt.Vector], attribute: TRaycast = 0, ray_length: nt.SocketOrVal[float] = 100.0, mapping: Literal["INTERPOLATED", "NEAREST"] = "INTERPOLATED", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> RaycastResult: """ Uses a Raycast Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/sample/raycast.html """ if data_type is None: data_type = RuntimeResolveDataType(_SAMPLE_NODE_DATA_TYPES, ["Attribute"]) res = nt.ProcNode.from_nodetype( node_type="GeometryNodeRaycast", inputs={ "Attribute": attribute, "Ray Direction": ray_direction, "Ray Length": ray_length, "Source Position": source_position, "Target Geometry": geometry, }, attrs={ "mapping": mapping, "data_type": data_type, }, ) return RaycastResult( attribute=res._output_socket("attribute"), hit_distance=res._output_socket("hit_distance"), hit_normal=res._output_socket("hit_normal"), hit_position=res._output_socket("hit_position"), is_hit=res._output_socket("is_hit"), )
[docs] def realize_instances( geometry: nt.ProcNode[nt.Geometry] | None | nt.ProcNode[nt.Instances], selection: nt.SocketOrVal[bool] = True, realize_all: nt.SocketOrVal[bool] = True, depth: nt.SocketOrVal[int] = 0, ) -> nt.ProcNode[nt.Geometry]: """ Uses a RealizeInstances Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/realize_instances.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeRealizeInstances", inputs={ "Geometry": geometry, "Selection": selection, "Realize All": realize_all, "Depth": depth, }, attrs={}, )
[docs] def remove_attribute( geometry: nt.ProcNode[TAnyGeometry] | None, name: nt.SocketOrVal[str] = "", pattern_mode: Literal["EXACT", "WILDCARD"] = "EXACT", ) -> nt.ProcNode[TAnyGeometry]: """ Uses a RemoveAttribute Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/attribute/remove_named_attribute.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeRemoveAttribute", inputs={"Geometry": geometry, "Name": name}, attrs={"pattern_mode": pattern_mode}, )
[docs] def replace_material( geometry: nt.ProcNode[pt.MeshObject] | None, old: nt.SocketOrVal[pt.Material] | None = None, new: nt.SocketOrVal[pt.Material] | None = None, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a ReplaceMaterial Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/material/replace_material.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeReplaceMaterial", inputs={"Geometry": geometry, "Old": old, "New": new}, attrs={}, )
[docs] def resample_curve_evaluated( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a ResampleCurve Geometry Node with mode="EVALUATED". From Blender docs: Evaluate the spline’s points based on the resolution attribute for NURBS and Bézier splines. Changes nothing for poly splines. https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/curve/operations/resample_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeResampleCurve", inputs={"Curve": curve, "Selection": selection}, attrs={"mode": "EVALUATED"}, )
[docs] def resample_curve_count( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, count: nt.SocketOrVal[int] = 10, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a ResampleCurve Geometry Node with mode="COUNT". https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/curve/operations/resample_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeResampleCurve", inputs={"Curve": curve, "Selection": selection, "Count": count}, attrs={"mode": "COUNT"}, )
[docs] def resample_curve_length( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, length: nt.SocketOrVal[float] = 1.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a ResampleCurve Geometry Node with mode="LENGTH". https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/curve/operations/resample_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeResampleCurve", inputs={"Curve": curve, "Selection": selection, "Length": length}, attrs={"mode": "LENGTH"}, )
[docs] def reverse_curve( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True ) -> nt.ProcNode[pt.CurveObject]: """ Uses a ReverseCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/reverse_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeReverseCurve", inputs={"Curve": curve, "Selection": selection}, attrs={}, )
[docs] def rotate_instances( instances: nt.ProcNode[nt.Instances] | None, rotation: nt.SocketOrVal[pt.Euler] = (0, 0, 0), pivot_point: nt.SocketOrVal[pt.Vector] = (0, 0, 0), selection: nt.SocketOrVal[bool] = True, local_space: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[nt.Instances]: """ Uses a RotateInstances Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/rotate_instances.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeRotateInstances", inputs={ "Instances": instances, "Selection": selection, "Rotation": rotation, "Pivot Point": pivot_point, "Local Space": local_space, }, attrs={}, )
[docs] def sdf_grid_boolean( a: nt.SocketOrVal[float] = 0.0, b: nt.SocketOrVal[float] = 0.0 ) -> nt.ProcNode[nt.Geometry]: """ Uses a SDFGridBoolean Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/index.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSDFGridBoolean", inputs={"Grid 1": a, "Grid 2": b}, attrs={}, )
[docs] class SampleCurveResult(NamedTuple): normal: nt.ProcNode[nt.pt.Vector] position: nt.ProcNode[nt.pt.Vector] tangent: nt.ProcNode[nt.pt.Vector] value: nt.ProcNode[TAttribute]
[docs] def sample_curve( curves: nt.ProcNode[nt.Geometry] | None, factor: nt.SocketOrVal[float], curve_index: nt.SocketOrVal[int] = 0, value: nt.SocketOrVal[TAttribute] | None = None, mode: Literal["FACTOR", "LENGTH"] = "FACTOR", use_all_curves: bool = False, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> SampleCurveResult: """ Uses a SampleCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/sample/sample_curve.html """ if data_type is None: data_type = RuntimeResolveDataType(_ATTRIBUTE_NODE_DATA_TYPES, ["Value"]) inputs = { "Curves": curves, "Factor": factor, "Value": value, } if not use_all_curves: inputs["Curve Index"] = curve_index res = nt.ProcNode.from_nodetype( node_type="GeometryNodeSampleCurve", inputs=inputs, attrs={ "mode": mode, "use_all_curves": use_all_curves, "data_type": data_type, }, ) return SampleCurveResult( normal=res._output_socket("normal"), position=res._output_socket("position"), tangent=res._output_socket("tangent"), value=res._output_socket("value"), )
[docs] def sample_curve_length( curves: nt.ProcNode[nt.Geometry] | None, length: nt.SocketOrVal[float] = 0.0, curve_index: nt.SocketOrVal[int] = 0, value: nt.SocketOrVal[TAttribute] | None = None, use_all_curves: bool = False, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> SampleCurveResult: """ Uses a SampleCurve Geometry Node with LENGTH mode. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/sample/sample_curve.html """ if data_type is None: data_type = RuntimeResolveDataType( [NodeDataType.BOOLEAN, NodeDataType.INT, NodeDataType.FLOAT], ["Value"], ) inputs = { "Curves": curves, "Length": length, "Value": value, } if not use_all_curves: inputs["Curve Index"] = curve_index res = nt.ProcNode.from_nodetype( node_type="GeometryNodeSampleCurve", inputs=inputs, attrs={ "mode": "LENGTH", "use_all_curves": use_all_curves, "data_type": data_type, }, ) return SampleCurveResult( normal=res._output_socket("normal"), position=res._output_socket("position"), tangent=res._output_socket("tangent"), value=res._output_socket("value"), )
''' TSampleGrid = TypeVar( "TSampleGrid", nt.SocketOrVal[bool], nt.SocketOrVal[int], nt.SocketOrVal[float], nt.SocketOrVal[nt.pt.Vector], ) def sample_grid( grid: TSampleGrid = 0, position: t.SocketOrVal[t.pt.Vector] = (0, 0, 0), interpolation_mode: Literal["NEAREST", "TRILINEAR", "TRIQUADRATIC"] = "TRILINEAR", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> t.ProcNode: """ Uses a SampleGrid Geometry Node. TODO: link """ if data_type is None: data_type = RuntimeResolveDataType([NodeDataType.BOOLEAN, NodeDataType.INT, NodeDataType.FLOAT, NodeDataType.FLOAT_VECTOR], ["Grid"]) return t.ProcNode.from_nodetype( node_type="GeometryNodeSampleGrid", inputs={"Grid": grid, "Position": position}, attrs={ "interpolation_mode": interpolation_mode, "data_type": data_type, }, ) TSampleGridIndex = TypeVar( "TSampleGridIndex", t.SocketOrVal[bool], t.SocketOrVal[int], t.SocketOrVal[float], t.SocketOrVal[t.pt.Vector], ) def sample_grid_index( grid: TSampleGridIndex = 0, x: t.SocketOrVal[int] = 0, y: t.SocketOrVal[int] = 0, z: t.SocketOrVal[int] = 0, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> t.ProcNode: """ Uses a SampleGridIndex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/index.html """ if data_type is None: data_type = RuntimeResolveDataType( [NodeDataType.BOOLEAN, NodeDataType.INT, NodeDataType.FLOAT, NodeDataType.FLOAT_VECTOR], ["Grid"], ) return t.ProcNode.from_nodetype( node_type="GeometryNodeSampleGridIndex", inputs={"Grid": grid, "X": x, "Y": y, "Z": z}, attrs={ "data_type": data_type, }, ) '''
[docs] def sample_index( geometry: nt.ProcNode[TAnyGeometry] | None, index: nt.SocketOrVal[int] = 0, value: nt.ProcNode[TAttribute] | None = None, clamp: bool = False, domain: TDomain = "POINT", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> nt.ProcNode[TAttribute]: """ Uses a SampleIndex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/sample/sample_index.html """ if data_type is None: data_type = RuntimeResolveDataType(_SAMPLE_NODE_DATA_TYPES, ["Value"]) return nt.ProcNode.from_nodetype( node_type="GeometryNodeSampleIndex", inputs={"Geometry": geometry, "Index": index, "Value": value}, attrs={ "clamp": clamp, "domain": domain, "data_type": data_type, }, )
[docs] def sample_nearest( geometry: nt.ProcNode[nt.Points] | None, sample_position: nt.SocketOrVal[nt.pt.Vector], domain: Literal["POINT", "EDGE", "FACE", "CORNER"] = "POINT", ) -> nt.ProcNode[int]: """ Uses a SampleNearest Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/sample/sample_nearest.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSampleNearest", inputs={"Geometry": geometry, "Sample Position": sample_position}, attrs={"domain": domain}, )
[docs] class SampleResult(NamedTuple, Generic[TAttribute]): value: nt.ProcNode[TAttribute] is_valid: nt.ProcNode[bool]
[docs] def sample_nearest_surface( mesh: nt.ProcNode[pt.MeshObject] | None, sample_position: nt.SocketOrVal[nt.pt.Vector], value: nt.ProcNode[TAttribute] | None = None, group_id: nt.SocketOrVal[int] = 0, sample_group_id: nt.SocketOrVal[int] = 0, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> SampleResult[TAttribute]: """ Uses a SampleNearestSurface Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/sample/sample_nearest_surface.html """ if data_type is None: data_type = RuntimeResolveDataType(_SAMPLE_NODE_DATA_TYPES, ["Value"]) res = nt.ProcNode.from_nodetype( node_type="GeometryNodeSampleNearestSurface", inputs={ "Group ID": group_id, "Mesh": mesh, "Sample Group ID": sample_group_id, "Sample Position": sample_position, "Value": value, }, attrs={ "data_type": data_type, }, ) return SampleResult( is_valid=res._output_socket("is_valid"), value=res._output_socket("value"), )
[docs] def sample_uv_surface( mesh: nt.ProcNode[pt.MeshObject] | None, sample_uv: nt.SocketOrVal[nt.pt.Vector], uv_map: nt.SocketOrVal[nt.pt.Vector], value: nt.ProcNode[TAttribute] | None = None, data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> SampleResult[TAttribute]: """ Uses a SampleUVSurface Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/sample/sample_uv_surface.html """ if data_type is None: data_type = RuntimeResolveDataType(_SAMPLE_NODE_DATA_TYPES, ["Value"]) res = nt.ProcNode.from_nodetype( node_type="GeometryNodeSampleUVSurface", inputs={"Mesh": mesh, "Sample UV": sample_uv, "UV Map": uv_map, "Value": value}, attrs={ "data_type": data_type, }, ) return SampleResult( is_valid=res._output_socket("is_valid"), value=res._output_socket("value"), )
[docs] def scale_elements( geometry: nt.ProcNode[nt.Geometry] | None, scale: nt.SocketOrVal[float], center: nt.SocketOrVal[nt.pt.Vector], selection: nt.SocketOrVal[bool] = True, axis: nt.SocketOrVal[nt.pt.Vector] | None = None, domain: Literal["FACE", "EDGE"] = "FACE", scale_mode: Literal["UNIFORM", "SINGLE_AXIS"] = "UNIFORM", ) -> nt.ProcNode[nt.Geometry]: """ Uses a ScaleElements Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/scale_elements.html """ inputs = { "Geometry": geometry, "Selection": selection, "Scale": scale, "Center": center, } if scale_mode == "SINGLE_AXIS" and axis is not None: inputs["Axis"] = axis return nt.ProcNode.from_nodetype( node_type="GeometryNodeScaleElements", inputs=inputs, attrs={"domain": domain, "scale_mode": scale_mode}, )
[docs] def scale_instances( instances: nt.ProcNode[nt.Instances] | None, scale: nt.SocketOrVal[pt.Vector] = (1, 1, 1), center: nt.SocketOrVal[pt.Vector] = (0, 0, 0), selection: nt.SocketOrVal[bool] = True, local_space: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[nt.Instances]: """ Uses a ScaleInstances Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/scale_instances.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeScaleInstances", inputs={ "Instances": instances, "Selection": selection, "Scale": scale, "Center": center, "Local Space": local_space, }, attrs={}, )
[docs] def self_object() -> nt.ProcNode[pt.Object]: """ Uses a SelfObject Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/self_object.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSelfObject", inputs={}, attrs={}, )
[docs] class SeparateComponentsResult(NamedTuple): mesh: nt.ProcNode[pt.MeshObject] curve: nt.ProcNode[pt.CurveObject] point_cloud: nt.ProcNode[nt.Points] volume: nt.ProcNode[pt.VolumeObject] instances: nt.ProcNode[nt.Instances]
[docs] def separate_components( geometry: nt.ProcNode[nt.Geometry] | None, ) -> SeparateComponentsResult: """ Uses a SeparateComponents Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/separate_components.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeSeparateComponents", inputs={"Geometry": geometry}, attrs={}, ) return SeparateComponentsResult( node._output_socket("mesh"), node._output_socket("curve"), node._output_socket("point_cloud"), node._output_socket("volume"), node._output_socket("instances"), )
[docs] class SeparateGeometryResult(NamedTuple, Generic[TMeshOrCurve]): selection: nt.ProcNode[TMeshOrCurve] inverted: nt.ProcNode[TMeshOrCurve]
[docs] def separate_geometry( geometry: nt.ProcNode[TMeshOrCurve], selection: nt.SocketOrVal[bool] = True, domain: Literal["POINT", "EDGE", "FACE", "CURVE", "INSTANCE", "LAYER"] = "POINT", ) -> SeparateGeometryResult[TMeshOrCurve]: """ Uses a SeparateGeometry Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/separate_geometry.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeSeparateGeometry", inputs={"Geometry": geometry, "Selection": selection}, attrs={"domain": domain}, ) return SeparateGeometryResult( selection=res._output_socket("selection"), inverted=res._output_socket("inverted"), )
[docs] def set_curve_handle_positions( curve: nt.ProcNode[pt.CurveObject] | None, position: nt.SocketOrVal[nt.pt.Vector], offset: nt.SocketOrVal[nt.pt.Vector], selection: nt.SocketOrVal[bool] = True, mode: Literal["LEFT", "RIGHT"] = "LEFT", ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SetCurveHandlePositions Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_handle_positions.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetCurveHandlePositions", inputs={ "Curve": curve, "Selection": selection, "Position": position, "Offset": offset, }, attrs={"mode": mode}, )
[docs] def set_curve_normal( curve: nt.ProcNode[pt.CurveObject] | None, normal: nt.SocketOrVal[pt.Vector] | None = None, selection: nt.SocketOrVal[bool] = True, mode: Literal["MINIMUM_TWIST", "Z_UP", "FREE"] = "MINIMUM_TWIST", ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SetCurveNormal Geometry Node. The Normal socket only exists in FREE mode; MINIMUM_TWIST and Z_UP compute normals themselves, so `normal` is required for FREE and rejected otherwise. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_curve_normal.html """ if mode == "FREE" and normal is None: raise ValueError("normal is required when mode='FREE'") if mode != "FREE" and normal is not None: raise ValueError(f"normal is only meaningful when mode='FREE', got {mode=}") inputs = {"Curve": curve, "Selection": selection} if mode == "FREE": inputs["Normal"] = normal return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetCurveNormal", inputs=inputs, attrs={"mode": mode}, )
[docs] def set_curve_radius( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, radius: nt.SocketOrVal[float] = 0.005, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SetCurveRadius Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_curve_radius.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetCurveRadius", inputs={"Curve": curve, "Selection": selection, "Radius": radius}, attrs={}, )
[docs] def set_curve_tilt( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, tilt: nt.SocketOrVal[float] = 0.0, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SetCurveTilt Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_curve_tilt.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetCurveTilt", inputs={"Curve": curve, "Selection": selection, "Tilt": tilt}, attrs={}, )
[docs] def set_id( geometry: nt.ProcNode[TAnyGeometry] | None, selection: nt.SocketOrVal[bool] = True, id: nt.SocketOrVal[int] = 0, ) -> nt.ProcNode[TAnyGeometry]: """ Uses a SetID Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/write/set_id.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetID", inputs={"Geometry": geometry, "Selection": selection, "ID": id}, attrs={}, )
[docs] def set_instance_transform( instances: nt.ProcNode[nt.Instances] | None, transform: nt.SocketOrVal[pt.Matrix] | None = None, selection: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[nt.Instances]: """ Uses a SetInstanceTransform Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/set_instance_transform.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetInstanceTransform", inputs={"Instances": instances, "Selection": selection, "Transform": transform}, attrs={}, )
[docs] def set_material( geometry: nt.ProcNode[pt.MeshObject] | None, material: nt.SocketOrVal[pt.Material], selection: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a SetMaterial Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/material/set_material.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetMaterial", inputs={"Geometry": geometry, "Selection": selection, "Material": material}, attrs={}, )
[docs] def set_material_index( geometry: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True, material_index: nt.SocketOrVal[int] = 0, ) -> nt.ProcNode[pt.MeshObject]: """ Uses a SetMaterialIndex Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/material/set_material_index.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetMaterialIndex", inputs={ "Geometry": geometry, "Selection": selection, "Material Index": material_index, }, attrs={}, )
[docs] def set_point_radius( points: nt.ProcNode[pt.PointCloudObject] | None, selection: nt.SocketOrVal[bool] = True, radius: nt.SocketOrVal[float] = 0.05, ) -> nt.ProcNode[pt.PointCloudObject]: """ Uses a SetPointRadius Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/point/set_point_radius.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetPointRadius", inputs={"Points": points, "Selection": selection, "Radius": radius}, attrs={}, )
[docs] def set_position( geometry: nt.ProcNode[TAnyGeometry] | None, position: nt.SocketOrVal[pt.Vector] | None = None, offset: nt.SocketOrVal[pt.Vector] | None = None, selection: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[TAnyGeometry]: """ Uses a SetPosition Geometry Node. A disconnected Position keeps the geometry's existing position; a disconnected Offset applies no offset. We therefore omit these inputs entirely when None rather than passing None to a value socket (see strict-None policy). See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/write/set_position.html """ inputs = {"Geometry": geometry, "Selection": selection} if position is not None: inputs["Position"] = position if offset is not None: inputs["Offset"] = offset return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetPosition", inputs=inputs, attrs={}, )
[docs] def set_shade_smooth( geometry: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True, shade_smooth: nt.SocketOrVal[bool] = True, domain: Literal["EDGE", "FACE"] = "FACE", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a SetShadeSmooth Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/write/set_shade_smooth.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetShadeSmooth", inputs={ "Geometry": geometry, "Selection": selection, "Shade Smooth": shade_smooth, }, attrs={"domain": domain}, )
[docs] def set_spline_cyclic( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, cyclic: nt.SocketOrVal[bool] = False, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SetSplineCyclic Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_spline_cyclic.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetSplineCyclic", inputs={"Geometry": curve, "Selection": selection, "Cyclic": cyclic}, attrs={}, )
[docs] def set_spline_resolution( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, resolution: nt.SocketOrVal[int] = 12, ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SetSplineResolution Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/write/set_spline_resolution.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSetSplineResolution", inputs={"Geometry": curve, "Selection": selection, "Resolution": resolution}, attrs={}, )
[docs] def sort_elements( geometry: nt.ProcNode[TAnyGeometry] | None, selection: nt.SocketOrVal[bool] = True, group_id: nt.SocketOrVal[int] = 0, sort_weight: nt.SocketOrVal[float] = 0.0, domain: Literal["POINT", "EDGE", "FACE", "CURVE", "INSTANCE"] = "POINT", ) -> nt.ProcNode[TAnyGeometry]: """ Uses a SortElements Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/sort_elements.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSortElements", inputs={ "Geometry": geometry, "Selection": selection, "Group ID": group_id, "Sort Weight": sort_weight, }, attrs={"domain": domain}, )
[docs] class SplineLengthResult(NamedTuple): length: nt.ProcNode[float] point_count: nt.ProcNode[int]
[docs] def spline_length() -> SplineLengthResult: """ Uses a SplineLength Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/spline_length.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeSplineLength", inputs={}, attrs={}, ) return SplineLengthResult( node._output_socket("length"), node._output_socket("point_count") )
[docs] class SplineParameterResult(NamedTuple): factor: nt.ProcNode[float] length: nt.ProcNode[float] index: nt.ProcNode[int]
[docs] def spline_parameter() -> SplineParameterResult: """ Uses a SplineParameter Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/read/spline_parameter.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeSplineParameter", inputs={}, attrs={}, ) return SplineParameterResult( node._output_socket("factor"), node._output_socket("length"), node._output_socket("index"), )
[docs] def split_edges( mesh: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True ) -> nt.ProcNode[pt.MeshObject]: """ Uses a SplitEdges Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/split_edges.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSplitEdges", inputs={"Mesh": mesh, "Selection": selection}, attrs={}, )
[docs] class SplitToInstancesResult(NamedTuple): instances: nt.ProcNode[nt.Instances] group_id: nt.ProcNode[int]
[docs] def split_to_instances( geometry: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True, group_id: nt.SocketOrVal[int] = 0, domain: Literal["POINT", "EDGE", "FACE", "CURVE", "INSTANCE", "LAYER"] = "POINT", ) -> SplitToInstancesResult: """ Uses a SplitToInstances Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/split_to_instances.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeSplitToInstances", inputs={"Geometry": geometry, "Selection": selection, "Group ID": group_id}, attrs={"domain": domain}, ) return SplitToInstancesResult( instances=res._output_socket("instances"), group_id=res._output_socket("group_id"), )
[docs] def store_named_attribute( geometry: nt.ProcNode[TMeshOrCurve], name: nt.SocketOrVal[str] = "", selection: nt.SocketOrVal[bool] = True, value: nt.SocketOrVal[TAttribute] | None = None, domain: TDomain = "POINT", data_type: NodeDataType | RuntimeResolveDataType | None = None, ) -> nt.ProcNode[TMeshOrCurve]: """ Uses a StoreNamedAttribute Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/attribute/store_named_attribute.html """ if data_type is None: data_type = RuntimeResolveDataType(_ATTRIBUTE_NODE_DATA_TYPES, ["Value"]) return nt.ProcNode.from_nodetype( node_type="GeometryNodeStoreNamedAttribute", inputs={ "Geometry": geometry, "Name": name, "Selection": selection, "Value": value, }, attrs={ "domain": domain, "data_type": data_type, }, )
[docs] def store_named_grid( volume: nt.ProcNode[nt.Geometry] | None, grid: nt.SocketOrVal[float] = 0.0, name: nt.SocketOrVal[str] = "", ) -> nt.ProcNode: """ Uses a StoreNamedGrid Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/index.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeStoreNamedGrid", inputs={"Grid": grid, "Name": name, "Volume": volume}, attrs={}, )
[docs] def string_join( strings: list[nt.SocketOrVal[str]], delimiter: nt.SocketOrVal[str] = "", ) -> nt.ProcNode[str]: """ Uses a StringJoin Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/utilities/text/join_strings.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeStringJoin", inputs={"Delimiter": delimiter, "Strings": strings}, attrs={}, )
[docs] class StringToCurvesResult(NamedTuple): curve_instances: nt.ProcNode[nt.Instances] line: nt.ProcNode[pt.CurveObject] pivot_point: nt.ProcNode[nt.pt.Vector]
[docs] def string_to_curves( string: nt.SocketOrVal[str], size: nt.SocketOrVal[float] = 1.0, character_spacing: nt.SocketOrVal[float] = 1.0, word_spacing: nt.SocketOrVal[float] = 1.0, line_spacing: nt.SocketOrVal[float] = 1.0, text_box_width: nt.SocketOrVal[float] = 0.0, align_x: Literal["LEFT", "CENTER", "RIGHT", "JUSTIFY", "FLUSH"] = "LEFT", align_y: Literal[ "TOP", "TOP_BASELINE", "MIDDLE", "BOTTOM_BASELINE", "BOTTOM" ] = "TOP_BASELINE", overflow: Literal["OVERFLOW", "SCALE_TO_FIT", "TRUNCATE"] = "OVERFLOW", pivot_mode: Literal[ "MIDPOINT", "TOP_LEFT", "TOP_CENTER", "TOP_RIGHT", "BOTTOM_LEFT", "BOTTOM_CENTER", "BOTTOM_RIGHT", ] = "BOTTOM_LEFT", ) -> StringToCurvesResult: """ Uses a StringToCurves Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/utilities/text/string_to_curves.html """ res = nt.ProcNode.from_nodetype( node_type="GeometryNodeStringToCurves", inputs={ "String": string, "Size": size, "Character Spacing": character_spacing, "Word Spacing": word_spacing, "Line Spacing": line_spacing, "Text Box Width": text_box_width, }, attrs={ "align_x": align_x, "align_y": align_y, "overflow": overflow, "pivot_mode": pivot_mode, }, ) return StringToCurvesResult( curve_instances=res._output_socket("curve_instances"), line=res._output_socket("line"), pivot_point=res._output_socket("pivot_point"), )
[docs] def subdivide_curve( curve: nt.ProcNode[pt.CurveObject] | None, cuts: nt.SocketOrVal[int] = 1 ) -> nt.ProcNode[pt.CurveObject]: """ Uses a SubdivideCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/subdivide_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSubdivideCurve", inputs={"Curve": curve, "Cuts": cuts}, attrs={}, )
[docs] def subdivide_mesh( mesh: nt.ProcNode[pt.MeshObject] | None, level: nt.SocketOrVal[int] = 1 ) -> nt.ProcNode[pt.MeshObject]: """ Uses a SubdivideMesh Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/subdivide_mesh.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSubdivideMesh", inputs={"Mesh": mesh, "Level": level}, attrs={}, )
[docs] def subdivision_surface( mesh: nt.ProcNode[pt.MeshObject] | None, level: nt.SocketOrVal[int] = 1, edge_crease: nt.SocketOrVal[float] = 0.0, vertex_crease: nt.SocketOrVal[float] = 0.0, boundary_smooth: Literal["PRESERVE_CORNERS", "ALL"] = "ALL", uv_smooth: Literal[ "NONE", "PRESERVE_CORNERS", "PRESERVE_CORNERS_AND_JUNCTIONS", "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", "PRESERVE_BOUNDARIES", "SMOOTH_ALL", ] = "PRESERVE_BOUNDARIES", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a SubdivisionSurface Geometry Node. See: http://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/operations/mesh/subdivision_surface.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeSubdivisionSurface", inputs={ "Mesh": mesh, "Level": level, "Edge Crease": edge_crease, "Vertex Crease": vertex_crease, }, attrs={"boundary_smooth": boundary_smooth, "uv_smooth": uv_smooth}, )
_SWITCH_DATA_TYPES = [ NodeDataType.BOOLEAN, NodeDataType.INT, NodeDataType.FLOAT, NodeDataType.FLOAT_VECTOR, NodeDataType.ROTATION, NodeDataType.FLOAT_MATRIX, NodeDataType.STRING, NodeDataType.RGBA, NodeDataType.OBJECT, # NodeDataType.IMAGE, # TODO verify support NodeDataType.GEOMETRY, NodeDataType.COLLECTION, # NodeDataType.TEXTURE, # TODO verify support NodeDataType.MATERIAL, ] ''' class Tool3DCursorResult(NamedTuple): location: t.ProcNode[pt.Vector] rotation: t.ProcNode[pt.Vector] def tool3_d_cursor() -> Tool3DCursorResult: """ Uses a Tool3DCursor Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/3d_cursor.html """ node = t.ProcNode.from_nodetype( node_type="GeometryNodeTool3DCursor", inputs={}, attrs={}, ) return Tool3DCursorResult(node._output_socket("location"), node._output_socket("rotation")) class ToolActiveElementResult(NamedTuple): index: t.ProcNode[int] exists: t.ProcNode[bool] def tool_active_element(domain: TDomain = "POINT") -> ToolActiveElementResult: """ Uses a ToolActiveElement Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/active_element.html """ node = t.ProcNode.from_nodetype( node_type="GeometryNodeToolActiveElement", inputs={}, attrs={"domain": domain}, ) return ToolActiveElementResult(node._output_socket("index"), node._output_socket("exists")) class ToolFaceSetResult(NamedTuple): face_set: t.ProcNode[int] exists: t.ProcNode[bool] def tool_face_set() -> ToolFaceSetResult: """ Uses a ToolFaceSet Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/read/face_set.html """ node = t.ProcNode.from_nodetype( node_type="GeometryNodeToolFaceSet", inputs={}, attrs={}, ) return ToolFaceSetResult(node._output_socket("face_set"), node._output_socket("exists")) class ToolMousePositionResult(NamedTuple): mouse_x: t.ProcNode[float] mouse_y: t.ProcNode[float] region_width: t.ProcNode[int] region_height: t.ProcNode[int] def tool_mouse_position() -> ToolMousePositionResult: """ Uses a ToolMousePosition Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/mouse_position.html """ node = t.ProcNode.from_nodetype( node_type="GeometryNodeToolMousePosition", inputs={}, attrs={}, ) return ToolMousePositionResult( node._output_socket("mouse_x"), node._output_socket("mouse_y"), node._output_socket("region_width"), node._output_socket("region_height") ) def tool_selection() -> t.ProcNode: """ Uses a ToolSelection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/read/selection.html """ return t.ProcNode.from_nodetype( node_type="GeometryNodeToolSelection", inputs={}, attrs={}, ) def tool_set_face_set( mesh: t.ProcNode[pt.MeshObject], selection: t.SocketOrVal[bool] = True, face_set: t.SocketOrVal[int] = 0, ) -> t.ProcNode: """ Uses a ToolSetFaceSet Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/write/set_face_set.html """ return t.ProcNode.from_nodetype( node_type="GeometryNodeToolSetFaceSet", inputs={"Mesh": mesh, "Selection": selection, "Face Set": face_set}, attrs={}, ) def tool_set_selection( geometry: t.ProcNode[t.Geometry], selection: t.SocketOrVal[bool] = True, domain: TDomain = "POINT", ) -> t.ProcNode: """ Uses a ToolSetSelection Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/write/set_selection.html """ return t.ProcNode.from_nodetype( node_type="GeometryNodeToolSetSelection", inputs={"Geometry": geometry, "Selection": selection}, attrs={"domain": domain}, ) '''
[docs] def transform( geometry: nt.ProcNode[TMeshOrCurve], translation: nt.SocketOrVal[pt.Vector] = (0, 0, 0), rotation: nt.SocketOrVal[pt.Euler] = (0, 0, 0), scale: nt.SocketOrVal[pt.Vector] = (1, 1, 1), ) -> nt.ProcNode[TMeshOrCurve]: """ Uses a Transform Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/geometry/operations/transform_geometry.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeTransform", inputs={ "Geometry": geometry, "Translation": translation, "Rotation": rotation, "Scale": scale, }, attrs={"mode": "COMPONENTS"}, )
[docs] def transform_by_matrix( geometry: nt.ProcNode[TMeshOrCurve], matrix: nt.SocketOrVal[pt.Matrix], ): return nt.ProcNode.from_nodetype( node_type="GeometryNodeTransform", inputs={ "Geometry": geometry, "Transform": matrix, }, attrs={"mode": "MATRIX"}, )
[docs] def translate_instances( instances: nt.ProcNode[nt.Instances] | None, translation: nt.SocketOrVal[pt.Vector] = (0, 0, 0), selection: nt.SocketOrVal[bool] = True, local_space: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[nt.Instances]: """ Uses a TranslateInstances Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/instances/translate_instances.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeTranslateInstances", inputs={ "Instances": instances, "Selection": selection, "Translation": translation, "Local Space": local_space, }, attrs={}, )
[docs] def triangulate( mesh: nt.ProcNode[pt.MeshObject] | None, selection: nt.SocketOrVal[bool] = True, minimum_vertices: nt.SocketOrVal[int] = 4, ngon_method: Literal["BEAUTY", "CLIP"] = "BEAUTY", quad_method: Literal[ "BEAUTY", "FIXED", "FIXED_ALTERNATE", "SHORTEST_DIAGONAL", "LONGEST_DIAGONAL" ] = "SHORTEST_DIAGONAL", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a Triangulate Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/operations/triangulate.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeTriangulate", inputs={ "Mesh": mesh, "Selection": selection, "Minimum Vertices": minimum_vertices, }, attrs={"ngon_method": ngon_method, "quad_method": quad_method}, )
[docs] def trim_curve( curve: nt.ProcNode[pt.CurveObject] | None, selection: nt.SocketOrVal[bool] = True, start: nt.SocketOrVal[float] = 0.0, end: nt.SocketOrVal[float] = 1.0, mode: Literal["FACTOR", "LENGTH"] = "FACTOR", ) -> nt.ProcNode[pt.CurveObject]: """ Uses a TrimCurve Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/curve/operations/trim_curve.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeTrimCurve", inputs={"Curve": curve, "Selection": selection, "Start": start, "End": end}, attrs={"mode": mode}, )
[docs] def uv_pack_islands( uv: nt.SocketOrVal[pt.Vector], selection: nt.SocketOrVal[bool] = True, margin: nt.SocketOrVal[float] = 0.001, rotate: nt.SocketOrVal[bool] = True, ) -> nt.ProcNode[pt.Vector]: """ Uses a UVPackIslands Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/uv/pack_uv_islands.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeUVPackIslands", inputs={"UV": uv, "Selection": selection, "Margin": margin, "Rotate": rotate}, attrs={}, )
[docs] def uv_unwrap( selection: nt.SocketOrVal[bool] = True, seam: nt.SocketOrVal[bool] = False, margin: nt.SocketOrVal[float] = 0.001, fill_holes: nt.SocketOrVal[bool] = True, method: Literal["ANGLE_BASED", "CONFORMAL"] = "ANGLE_BASED", ) -> nt.ProcNode[pt.Vector]: """ Uses a UVUnwrap Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/uv/uv_unwrap.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeUVUnwrap", inputs={ "Selection": selection, "Seam": seam, "Margin": margin, "Fill Holes": fill_holes, }, attrs={"method": method}, )
[docs] def vertex_of_corner(corner_index: nt.SocketOrVal[int] = 0) -> nt.ProcNode[int]: """ Uses a VertexOfCorner Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/mesh/topology/vertex_of_corner.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeVertexOfCorner", inputs={"Corner Index": corner_index}, attrs={}, )
TViewer = TypeVar( "TViewer", nt.SocketOrVal[bool], nt.SocketOrVal[int], nt.SocketOrVal[float] ) # def viewer( # geometry: t.ProcNode[t.Geometry], # value: TViewer = 0, # domain: TDomain = "AUTO", # data_type: NodeDataType | RuntimeResolveDataType | None = None, # ) -> t.ProcNode: # """ # Uses a Viewer Geometry Node. # # See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/output/viewer.html # """ # if data_type is None: # data_type = RuntimeResolveDataType( # [NodeDataType.BOOLEAN, NodeDataType.INT, NodeDataType.FLOAT], # ["Value"], # ) # return t.ProcNode.from_nodetype( # node_type="GeometryNodeViewer", # inputs={"Geometry": geometry, "Value": value}, # attrs={ # "domain": domain, # "data_type": data_type, # }, # )
[docs] class ViewportTransformResult(NamedTuple): projection: nt.ProcNode view: nt.ProcNode is_orthographic: nt.ProcNode[bool]
[docs] def viewport_transform() -> ViewportTransformResult: """ Uses a ViewportTransform Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/input/scene/viewport_transform.html """ node = nt.ProcNode.from_nodetype( node_type="GeometryNodeViewportTransform", inputs={}, attrs={}, ) return ViewportTransformResult( node._output_socket("projection"), node._output_socket("view"), node._output_socket("is_orthographic"), )
[docs] def volume_cube( density: nt.SocketOrVal[float] = 1.0, background: nt.SocketOrVal[float] = 0.0, min: nt.SocketOrVal[nt.pt.Vector] = (-1, -1, -1), max: nt.SocketOrVal[nt.pt.Vector] = (1, 1, 1), resolution_x: nt.SocketOrVal[int] = 32, resolution_y: nt.SocketOrVal[int] = 32, resolution_z: nt.SocketOrVal[int] = 32, ) -> nt.ProcNode[pt.VolumeObject]: """ Uses a VolumeCube Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/primitives/volume_cube.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeVolumeCube", inputs={ "Density": density, "Background": background, "Min": min, "Max": max, "Resolution X": resolution_x, "Resolution Y": resolution_y, "Resolution Z": resolution_z, }, attrs={}, )
[docs] def volume_to_mesh( volume: nt.ProcNode[pt.VolumeObject] | None, threshold: nt.SocketOrVal[float] = 0.1, adaptivity: nt.SocketOrVal[float] = 0.0, resolution_mode: Literal["GRID", "VOXEL_AMOUNT", "VOXEL_SIZE"] = "GRID", ) -> nt.ProcNode[pt.MeshObject]: """ Uses a VolumeToMesh Geometry Node. See: https://docs.blender.org/manual/en/4.2/modeling/geometry_nodes/volume/operations/volume_to_mesh.html """ return nt.ProcNode.from_nodetype( node_type="GeometryNodeVolumeToMesh", inputs={"Volume": volume, "Threshold": threshold, "Adaptivity": adaptivity}, attrs={"resolution_mode": resolution_mode}, )