I want to add inner loops (holes) to a hatch

I’m working on a Dynamo Python script for Civil3D, where I want to add inner loops (holes) to an existing Hatch object using closed polylines or circles.


import clr
clr.AddReference('AcMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AcCoreMgd')

from Autodesk.AutoCAD.ApplicationServices import Application
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *
from Autodesk.AutoCAD.DatabaseServices import HatchLoopTypes
from System.Collections.Generic import List

# IN[0] = Existing Hatch
# IN[1] = List of closed polylines (includes outer boundary and inner holes)

hatch_input = IN[0]
poly_inputs = IN[1]

print("Python Script: Input received")

def flatten(x):
    if isinstance(x, list):
        return sum([flatten(i) for i in x], [])
    else:
        return [x]

def get_object_id(obj):
    try:
        return obj.InternalObjectId
    except:
        try:
            return obj.ObjectId
        except:
            return None

flat_polys = flatten(poly_inputs)
poly_ids = [get_object_id(p) for p in flat_polys if get_object_id(p) is not None]

doc = Application.DocumentManager.MdiActiveDocument
db = doc.Database
ed = doc.Editor

result = None
tr = db.TransactionManager.StartTransaction()
try:
    print("Python Script: Transaction started")

    hatch_id = get_object_id(hatch_input)
    hatch = tr.GetObject(hatch_id, OpenMode.ForWrite)
    print("Hatch object acquired")

    # Remove all existing loops (from last to first)
    for i in reversed(range(hatch.NumberOfLoops)):
        hatch.RemoveLoopAt(i)
    print("Existing loops removed")

    db_objs = [tr.GetObject(pid, OpenMode.ForRead) for pid in poly_ids]

    # Calculate areas for each object
    areas = []
    for obj in db_objs:
        if hasattr(obj, "Area"):
            areas.append(obj.Area)
        else:
            areas.append(0)

    if not areas or max(areas) == 0:
        raise Exception("No valid closed shapes with measurable area found.")

    # Find index of the largest area object (assumed to be outer loop)
    max_idx = areas.index(max(areas))
    outer_id = poly_ids[max_idx]

    # Add outer loop
    loop_ext = ObjectIdCollection()
    loop_ext.Add(outer_id)
    hatch.AppendLoop(HatchLoopTypes.External, loop_ext)

    # Add inner loops (all except the outer one)
    for i, pid in enumerate(poly_ids):
        if i == max_idx:
            continue
        loop = ObjectIdCollection()
        loop.Add(pid)
        hatch.AppendLoop(HatchLoopTypes.Default, loop)

    # Evaluate hatch to apply changes
    hatch.EvaluateHatch(True)
    print("Hatch evaluation complete")

    result = hatch
    tr.Commit()

except Exception as e:
    print("Exception occurred:", e)
    ed.WriteMessage("\nError: {}\n".format(str(e)))
    result = "Error: " + str(e)
    tr.Abort()

finally:
    tr.Dispose()
    print("Transaction ended")

OUT = result


:magnifying_glass_tilted_left: Result

I ran the following code to update an existing Hatch entity by removing its loops and re-adding a new outer boundary and inner boundaries (holes). The script executes without any errors, but the result is not what I expected.

Instead of showing a hatched area with holes, the hatch only fills the inner shapes, and the outer boundary seems to disappear.
Could you help me understand what I’m doing wrong?

How should I modify this script to correctly add inner loops (holes) to an existing Hatch?
Any insight or working example would be greatly appreciated.
Uploading: TEST_OBJHatch.dwg…
Uploading: hatch2.dyn…

I use HatchLoopTypes.Outermost for the exterior, maybe that will help.

1 Like


Thank you so much! :folded_hands:
Your suggestion to use HatchLoopTypes.Outermost instead of Default was exactly what I needed.

I was finally able to add inner loops (holes) to an existing Hatch without removing the original one — and it worked perfectly.

Here is the final code I used (in case it helps someone else):

python

コピーする編集する

import clr
clr.AddReference('AcMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AcCoreMgd')

from Autodesk.AutoCAD.ApplicationServices import Application
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *
from Autodesk.AutoCAD.DatabaseServices import HatchLoopTypes
from System.Collections.Generic import List

# IN[0] = Existing Hatch
# IN[1] = List of closed polylines to be used as holes (inner loops)

hatch_input = IN[0]
poly_inputs = IN[1]

def flatten(x):
    if isinstance(x, list):
        return sum([flatten(i) for i in x], [])
    else:
        return [x]

def get_object_id(obj):
    try:
        return obj.InternalObjectId
    except:
        try:
            return obj.ObjectId
        except:
            return None

flat_polys = flatten(poly_inputs)
poly_ids = [get_object_id(p) for p in flat_polys if get_object_id(p) is not None]

doc = Application.DocumentManager.MdiActiveDocument
db = doc.Database

tr = db.TransactionManager.StartTransaction()
try:
    hatch_id = get_object_id(hatch_input)
    hatch = tr.GetObject(hatch_id, OpenMode.ForWrite)

    for pid in poly_ids:
        loop = ObjectIdCollection()
        loop.Add(pid)
        hatch.AppendLoop(HatchLoopTypes.Outermost, loop)

    hatch.EvaluateHatch(True)
    tr.Commit()

except Exception as e:
    print("Error:", e)
    tr.Abort()
finally:
    tr.Dispose()

OUT = hatch_input

Thanks again — really appreciate the help!

3 Likes