Hello everyone,
I’m working on a Python script in Dynamo for Civil 3D to automate a surface grading task, and I’ve run into a persistent and unusual error. I would be very grateful for any insights the community might have.
The Goal: The script’s purpose is to generate a new, regularized TIN surface within a closed polyline boundary. The new surface must adhere to maximum slope constraints in both the North-South (Y-axis) and East-West (X-axis) directions. The logic involves creating a grid of points within the boundary and calculating the Z-elevation for each point based on its neighbors and the specified slope limits.
The Problem: The script consistently fails with the error: AttributeError: 'Polygon' object has no attribute 'Contains'
.
This error occurs on the line where the script checks if a newly generated grid point lies inside the boundary polygon. This is very strange, as standard Autodesk.DesignScript.Geometry.Polygon
objects should always have the .Contains()
method.
What We Have Already Tried (Extensive Debugging): We have gone through a long troubleshooting process and have already ruled out the most common issues:
- Input Verification: We confirmed via a separate diagnostic script (
type(IN[1]).__name__
) that the object being passed into the Python node is indeed a'Polygon'
. - Graph Logic: We have tried creating the Polygon input using two different methods:
- A custom node (
PolyCurveToPolygon
from the Arkance Systems package). - A full chain of native Dynamo nodes (
PolyCurve.Curves
,Curve.StartPoint
,List.AddItemToEnd
,Polygon.ByPoints
, etc.).
- Both methods produce a valid Polygon in the Dynamo environment, but both result in the same error inside the Python node.
- A custom node (
- Environment Configuration: We have explicitly set the Python engine to CPython3 in Dynamo’s preferences and have restarted Civil 3D multiple times.
- Package Conflicts: We have tried running the script after activating the “Disable Loading Custom Packages” option and restarting. The error still persists even in this “safe mode” using only native nodes.
- Script Robustness: The script itself was modified to internally check the input type and handle conversions, but the error remains.
Our conclusion so far is that this points to a corrupted Dynamo installation or a problem with the CPython3 engine’s interface with the Geometry library (ProtoGeometry
). Before proceeding with a full software repair, I wanted to ask for your help.
The Script: Here is the final, most robust version of the script we are using:
# Load Dynamo's geometry libraries
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# 1. GET INPUTS FROM DYNAMO
# =======================================
original_surface = IN[0]
boundary_input = IN[1] # Geometry from Dynamo graph
max_slope_ns = IN[2]
max_slope_ew = IN[3]
grid_step = IN[4]
# 2. MAIN LOGIC
# =======================================
# Safety check to ensure the boundary is a Polygon for the .Contains() method
if type(boundary_input).__name__ == 'PolyCurve':
boundary_polygon = Polygon.ByVertices(boundary_input.Vertices)
else:
boundary_polygon = boundary_input
final_points = []
point_dictionary = {}
# Get the BoundingBox from the original input geometry for reliability
bbox = boundary_input.BoundingBox
x_min, y_min = bbox.MinPoint.X, bbox.MinPoint.Y
x_max, y_max = bbox.MaxPoint.X, bbox.MaxPoint.Y
# Generate the grid coordinates
x_range = range(int(x_min // grid_step), int(x_max // grid_step) + 1)
y_range = range(int(y_min // grid_step), int(y_max // grid_step) + 1)
# Iterate over every point in the grid
for j in y_range:
y = j * grid_step
for i in x_range:
x = i * grid_step
current_point_2d = Point.ByCoordinates(x, y, 0)
# Check if the point is inside the boundary
if boundary_polygon.Contains(current_point_2d):
# If this is the first point, get Z from the original surface
if not point_dictionary:
initial_z = 0
try:
initial_z = original_surface.PointAtParameter(x,y).Z
except:
pass
final_point = Point.ByCoordinates(x, y, initial_z)
# For all other points, calculate Z based on neighbors and slopes
else:
neighbor_south = point_dictionary.get((x, y - grid_step))
neighbor_west = point_dictionary.get((x - grid_step, y))
potential_z_ns = float('inf')
potential_z_ew = float('inf')
if neighbor_south:
potential_z_ns = neighbor_south.Z + (grid_step * max_slope_ns)
if neighbor_west:
potential_z_ew = neighbor_west.Z + (grid_step * max_slope_ew)
# Choose the most restrictive (lowest) elevation to meet both slope criteria
new_z = min(potential_z_ns, potential_z_ew)
if new_z == float('inf'):
if neighbor_south: new_z = potential_z_ns
elif neighbor_west: new_z = potential_z_ew
else: new_z = 0
final_point = Point.ByCoordinates(x, y, new_z)
# Add the new point to our output list and our dictionary for the next iteration
final_points.append(final_point)
point_dictionary[(x, y)] = final_point
# 3. SEND OUTPUT TO DYNAMO
# =======================================
OUT = final_points
Environment Details:
- Software: Autodesk Civil 3D 2024
- Dynamo Version: Dynamo Core 2.19
- Python Engine: CPython3
My Question: Has anyone ever encountered a situation where a standard Dynamo geometry object (Polygon
) appears to lose its methods (.Contains
) when passed into a Python Script node? Is there any known issue with this Dynamo/Civil 3D version that could cause this?
Any ideas would be greatly appreciated. Thank you!
teste - simplificando superficie por declividade.dyn (27.7 KB)
teste de dynamo surface.dwg (3.9 MB)