I am using the Python language and the win32com.client library to add hatches to a drawing.
Here is the code that implements the creation of hatchings:
import win32com.client
import pythoncom
acad = win32com.client.Dispatch("AutoCAD.Application")
acadModel = acad.ActiveDocument.ModelSpace
def APoint(x, y, z = 0):
return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, (x, y, z))
def ADouble(xyz):
return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, (xyz))
def variants(object):
return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_DISPATCH, (object))
out_loop = [acadModel.AddPolyline(ADouble([0,0,0,1000,0,0,1000,1000,0,0,1000,0,0,0,0]))]
hatch = acadModel.AddHatch(0, "HEX", True)
hatch.PatternScale = 20
hatch.AppendOuterLoop(variants(out_loop))
hatch.Evaluate()
hatch = acadModel.AddHatch(0, "ANSI31", True)
hatch.PatternScale = 10
hatch.AppendOuterLoop(variants(out_loop))
hatch.Evaluate()
The task is to find a solution to overlapping hatches on each other, which can be implemented using python. The desired result is shown on the right side of the picture.
I can do this task manually in autocad using the Explode command to the first hatch and then add a second hatch. But I need to implement this using Python.
I was also advised a method where WipeOut and SuperHatch are used, but I still haven't figured out how to implement this in code. (https://stackoverflow.com/a/77905739/23389658)
Maybe it need to modify the autocad scripts, with lisp or Autolisp. I don't know.
Unfortunately I haven't found a solution using ActiveX (COM), mainly because Hatch doesn't have an explode method. I suggest using PyRx - Python wrapper over Object ARX.
In a nutshell:
import traceback
from itertools import pairwise
from typing import Iterable, Tuple
import numpy as np
import shapely
from pyrx_imp import Db, Ge
from shapely.ops import linemerge
def PyRxCmd_doit():
try:
# HEX hatch
hex_hatch = Db.Hatch()
hex_hatch.setDatabaseDefaults()
hex_hatch.setPattern(Db.HatchPatternType.kPreDefined, "HEX")
outer_edges = (
((0.0, 0.0), (100.0, 0.0)),
((100.0, 0.0), (100.0, 100.0)),
((100.0, 100.0), (0.0, 100.0)),
((0.0, 100.0), (0.0, 0.0)),
)
outer_segments = [
Ge.LineSeg2d(Ge.Point2d(p1), Ge.Point2d(p2)) for p1, p2 in outer_edges
]
hex_hatch.appendLoopEdges(
Db.HatchLoopType.kExternal,
outer_segments,
[Db.HatchEdgeType.kLine for _ in outer_segments],
)
hex_hatch.evaluateHatch()
# explode hatch and get single lines
hex_lines_ents = hex_hatch.explode()
hex_lines = [Db.Line.cast(ent) for ent in hex_lines_ents]
# connect individual lines in a "polyline" so that you can
# create loops for cutting the target hatch; round the coordinates
# so that the lines connect properly
hex_lines_points = [
(
np.round(i.startPoint().toTuple()[:2], 6),
np.round(i.endPoint().toTuple()[:2], 6),
)
for i in hex_lines
]
plines = get_plines(hex_lines_points) # get_plines below
# Target hatch
hatch = Db.Hatch()
hatch.setPattern(Db.HatchPatternType.kPreDefined, "ANSI31")
hatch.appendLoopEdges(
Db.HatchLoopType.kExternal,
outer_segments,
[Db.HatchEdgeType.kLine for _ in outer_edges],
)
for pline in plines: # hex plines
# create a list of segments that make up the polylines of one hexagon
line_segments = [
Ge.LineSeg2d(Ge.Point2d(p1), Ge.Point2d(p2))
for p1, p2 in pairwise(pline)
]
hatch.appendLoopEdges(
Db.HatchLoopType.kDefault,
line_segments,
[Db.HatchEdgeType.kLine for _ in line_segments],
)
hatch.evaluateHatch()
db = Db.HostApplicationServices().workingDatabase()
model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForWrite)
model.appendAcDbEntity(hatch) # add hatch to the model
model.appendAcDbEntities(hex_lines) # add hexagon lines if needed too
model.close()
except Exception:
traceback.print_exc()
Point_T = Tuple[float, float]
def get_plines(
line_points: Iterable[Tuple[Point_T, Point_T]]
) -> Iterable[Iterable[Point_T]]:
line_strings = linemerge(line_points)
if isinstance(line_strings, shapely.LineString):
line_strings = (line_strings,)
elif isinstance(line_strings, shapely.MultiLineString):
pass
else:
raise TypeError
for line_string in line_strings.geoms:
ch = line_string.convex_hull
if isinstance(ch, shapely.Polygon):
yield ch.exterior.coords
The algorithm requires refinement at the hatch outer edge; the beginning and end of a polyline that is part of a hexagon are connected straight ahead instead of along the outer hatch outline; I suggest playing with shapely
- create a HEX hatch whose outline is larger than the target hatch, and cut the polylines (get_plines()
above) of this hatch from the polygon (shapely.Polygon
) of the target hatch.
PYLOAD
command and select the python module with your code,DOIT
command (PyRxCmd_doit).PyRx allows user interaction, for the user to specify the external hatch outline, use the Ed
module (from pyrx_imp import Ed
)
You can also use ActiveX (Autocad Object Model) with PyRx, check it out:
from pyrx_impx import Ax
doc = Ax.getDbx() # same as acad.ActiveDocument