.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Creating hatches for Polyline3D (or hatching surfaces)?

3 REPLIES 3
Reply
Message 1 of 4
CadWCCNL
213 Views, 3 Replies

Creating hatches for Polyline3D (or hatching surfaces)?

Having some issues / confusion about creating hatches in 3D. I just want the user to be able to click on surfaces and for them to be hatched or to have surfaces pre-selected and for them to be hatched. I have pre-processed all 3dpolylines in the drawing and they are guaranteed to be planar and left-handed. Each 3dpolyline was used to define the surface and a UCS.

 

This is the entry point for the function.

 

 

[CommandMethod("HatchSurfaces")]
public static void HatchSurfaces()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;

    // Prompt user to select surfaces
    PromptSelectionOptions selOpts = new PromptSelectionOptions();
    selOpts.MessageForAdding = "Select the surface or surfaces to continue: ";
    selOpts.AllowDuplicates = false;

    PromptSelectionResult selRes = ed.GetSelection(selOpts);
    if (selRes.Status != PromptStatus.OK)
    {
        ed.WriteMessage("No surfaces selected. Exiting.");
        return;
    }

    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        Utilities utils = new Utilities();
        // Iterate through the selected objects
        foreach (SelectedObject selObj in selRes.Value)
        {
            // Open the object for read
            Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
            if (ent != null && ent is Autodesk.AutoCAD.DatabaseServices.Surface)
            {
                Autodesk.AutoCAD.DatabaseServices.Surface surf = ent as Autodesk.AutoCAD.DatabaseServices.Surface;

                // Get the 3DPolyline with the same layer as the surface
                Polyline3d pline3d = utils.GetPolyline3dByLayer(surf.Layer, tr);
                if (pline3d != null)
                {
                    // Hatch the surface
                    utils.HatchSurface(surf, pline3d, tr);
                }
            }
        }

        tr.Commit();
    }
}

 

 

 
Where I am seriously confused is in generating the hatch with either the polyline or surface that was originally selected. I was assuming that I would need the UCS that defines the plane the polyline/surface sits on, just like in AutoCAD where you select 3point, make a new UCS and you can hatch your flat 3d object.

 

 

public static UcsTableRecord GetUCSByName(string name)
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // Open the UCS table for read
        UcsTable ucsTable = tr.GetObject(db.UcsTableId, OpenMode.ForRead) as UcsTable;

        // Check if the table contains a UCS with the specified name
        if (ucsTable.Has(name))
        {
            // Open the UCS for read and get its properties
            UcsTableRecord ucsRecord = tr.GetObject(ucsTable[name], OpenMode.ForRead) as UcsTableRecord;
            return ucsRecord;
        }
    }

    // If no UCS was found, return null
    return null;
}

 

 

 

Because I am not sure how I can create a hatch with a surface (I tried looking for a convenient method but hatch loop seems to like polyline2d and curve2d?) I get the polyline3d that also shares the same layer as the UCS name and surface layer.

 

 

public Polyline3d GetPolyline3dByLayer(string layerName, Transaction tr)
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    LayerTable layerTable = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
    if (!layerTable.Has(layerName))
    {
        return null;
    }

    LayerTableRecord layerRecord = tr.GetObject(layerTable[layerName], OpenMode.ForRead) as LayerTableRecord;

    // Get all 3DPolyline objects on the layer
    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
    BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
    foreach (ObjectId objId in ms)
    {
        if (objId.ObjectClass.Name == "AcDb3dPolyline")
        {
            Polyline3d pline3d = tr.GetObject(objId, OpenMode.ForRead) as Polyline3d;
            if (pline3d.LayerId == layerRecord.Id)
            {
                return pline3d;
            }
        }
    }

    return null;
}

 

 


This is where my errors / issues are. I'm not understanding the hatch object. I am currently passing in everything because I've been experimenting trying to get the hatch to work for the surfaces that were selected by themselves (this would be ideal). And desperately have tried using the polyline in hopes it was like creating a surface from the polyline3D, but it doesn't seem to be that way.

 

 

public static void HatchSurface(Autodesk.AutoCAD.DatabaseServices.Surface surf, Polyline3d pline3d, Transaction tr)
{
    Database db = surf.Database;

    // Create a new Hatch object
    using (Hatch hatch = new Hatch())
    {
        // Set the hatch properties
        hatch.SetDatabaseDefaults();
        hatch.SetHatchPattern(HatchPatternType.PreDefined, "ANSI31");
        hatch.Associative = true;
        hatch.PatternAngle = Math.PI / 4;
        hatch.PatternScale = 192;

        // Add the loop to the hatch object
        HatchLoop loop = new HatchLoop(pline3d);
        loop.SetPolyline(pline3d);
        hatch.AppendLoop(loop);

        // Add the hatch to the database
        ObjectId hatchId = db.AddToModelSpace(hatch);

        // Associate the hatch with the surface
        surf.UpgradeOpen();
        surf.AssociativeHatch = true;
        surf.AppendHatch(hatchId);
    }
}

 

 

 

HATCH PATTERN: ANSI31 / ANGLE 45 / SCALE 192 

I guess my question is, can I make the hatch with the surface alone or surface + ucs lookup? Or do I need to work with the polyline3d? 

I kind of gave up near the end, started to look through random methods and tried random nonsense with no clear sense of what direction I should be going in. Hatches are way worse than surfaces...

3 REPLIES 3
Message 2 of 4
CadWCCNL
in reply to: CadWCCNL

I am having trouble with my latest attempt at creating a hatch. Specifically, there are two new issues that I cannot seem to solve:

  1. Precision: Despite assuming that a coordinate system to coordinate system transformation should not affect the geometry, I am experiencing a fair amount of imprecision, including changes in size (not so much shape)

  2. Elevation and alignment of normal: I have tried to set the elevation and alignment of the normal to match the UCS, which is defined by the best fit plane of the polyline3d that the hatch is supposed to fit to. However, it appears that the hatch is based on the centroid of the hatch, which causes imprecision because of problem 1.

I have already tried defining the transformation matrix multiple times, even copying and pasting some of my old code, but to no avail. Could you please help me find a solution? I have already spent so many hours on this and I am at my wits' end.

 

 

 

public void CreateHatch(Polyline3d outerLoop, CoordinateSystem3d ucs, String hatchName, Double angle, Double scale, List<Polyline3d> holePolylines = null)
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        BlockTable acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;
        BlockTableRecord acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;

        // Project the outerLoop polyline onto the WCS
        Polyline outerLoop2d = Get2dPolyline(outerLoop, ucs);
        acBlkTblRec.AppendEntity(outerLoop2d);
        acTrans.AddNewlyCreatedDBObject(outerLoop2d, true);

        // Adds the outerLoop2d ObjectId to an object id array
        ObjectIdCollection acObjIdColl = new ObjectIdCollection();
        acObjIdColl.Add(outerLoop2d.ObjectId);

        // Create the hatch object and append it to the block table record
        Hatch acHatch = new Hatch();
        acBlkTblRec.AppendEntity(acHatch);
        acTrans.AddNewlyCreatedDBObject(acHatch, true);

        // Set the properties of the hatch object
        acHatch.SetDatabaseDefaults();
        acHatch.SetHatchPattern(HatchPatternType.PreDefined, hatchName);
        acHatch.PatternScale = scale;
        acHatch.PatternAngle = angle;
        acHatch.AppendLoop(HatchLoopTypes.Outermost, acObjIdColl);
        acHatch.Associative = true;

        // Handle holePolylines as inner loops
        if (holePolylines != null)
        {
            foreach (Polyline3d holePolyline in holePolylines)
            {
                // Project the holePolyline onto the WCS
                Polyline holePolyline2d = Get2dPolyline(holePolyline, ucs);
                acBlkTblRec.AppendEntity(holePolyline2d);
                acTrans.AddNewlyCreatedDBObject(holePolyline2d, true);

                // Adds the holePolyline2d ObjectId to an object id array
                ObjectIdCollection holeObjIdColl = new ObjectIdCollection();
                holeObjIdColl.Add(holePolyline2d.ObjectId);

                // Append hole polyline as inner loop
                acHatch.AppendLoop(HatchLoopTypes.Default, holeObjIdColl);
            }
        }

        acHatch.EvaluateHatch(true);
        var wcs = acDoc.Editor.CurrentUserCoordinateSystem;
        var wcsOrgin = wcs.CoordinateSystem3d.Origin;
        var wcsX = wcs.CoordinateSystem3d.Xaxis;
        var wcsY = wcs.CoordinateSystem3d.Yaxis;
        var wcsZ = wcs.CoordinateSystem3d.Zaxis;

        var ucsOrgin = ucs.Origin;
        var ucsX = ucs.Xaxis;
        var ucsY = ucs.Yaxis;
        var ucsZ = ucsX.CrossProduct(ucsY).GetNormal();
        Matrix3d ucs2wcs = Matrix3d.AlignCoordinateSystem(wcsOrgin, wcsX, wcsY, wcsZ, ucsOrgin, ucsX, ucsY, ucsZ);
        Matrix3d wcs2ucs = Matrix3d.AlignCoordinateSystem(ucsOrgin, ucsX, ucsY, ucsZ, wcsOrgin, wcsX, wcsY, wcsZ);
        // Create wcsToUcs transformation matrix

        // Transform elevation and normal by wcsToUcs
        acHatch.Elevation = (new Point3d(0, 0, ucs.Origin.Z)).TransformBy(wcs2ucs).Z;
        
        acHatch.Normal = ucs.Zaxis.TransformBy(wcs2ucs).GetNormal();

        // Save the new object to the database
        acTrans.Commit();
    }
}

private Polyline Get2dPolyline(Polyline3d polyline3d, CoordinateSystem3d ucs)
{
    Polyline polyline = new Polyline();
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    Editor ed = acDoc.Editor;
    using (Transaction tr = polyline3d.Database.TransactionManager.StartTransaction())
    {
        // Calculate the WCS to UCS matrix
        var wcs = acDoc.Editor.CurrentUserCoordinateSystem;
        var wcsOrgin = wcs.CoordinateSystem3d.Origin;
        var wcsX = wcs.CoordinateSystem3d.Xaxis;
        var wcsY = wcs.CoordinateSystem3d.Yaxis;
        var wcsZ = wcs.CoordinateSystem3d.Zaxis;

        var ucsOrgin = ucs.Origin;
        var ucsX = ucs.Xaxis;
        var ucsY = ucs.Yaxis;
        var ucsZ = ucsX.CrossProduct(ucsY).GetNormal();
        Matrix3d wcs2ucs = Matrix3d.AlignCoordinateSystem(wcsOrgin, wcsX, wcsY, wcsZ, ucsOrgin, ucsX, ucsY, ucsZ);
        Matrix3d ucs2wcs = Matrix3d.AlignCoordinateSystem(ucsOrgin, ucsX, ucsY, ucsZ, wcsOrgin, wcsX, wcsY, wcsZ);

        int index = 0;
        foreach (ObjectId vertexId in polyline3d)
        {
            PolylineVertex3d vertex = (PolylineVertex3d)tr.GetObject(vertexId, OpenMode.ForRead);
            Point3d pt3d = vertex.Position;
            Point3d ptInUcs = pt3d.TransformBy(wcs2ucs);
            polyline.AddVertexAt(index, new Point2d(ptInUcs.X, ptInUcs.Y), 0, 0, 0);
            index++;
        }
    }

 

 

 

 

Message 3 of 4
_gile
in reply to: CadWCCNL

Hi,

The following example creates a Hatch with any planar closed Curve entity.

        public static void HatchCurve(Curve curve, Transaction tr)
        {
            if (!curve.Closed)
            {
                Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(
                    "\nCurve is not closed.");
                return;
            }
            var curve3d = curve.GetGeCurve();
            if (!curve3d.IsPlanar(out Plane plane))
            {
                Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(
                    "\nCurve is not planar.");
                return;
            }
            var normal = plane.Normal;
            var ids = new ObjectIdCollection { curve.ObjectId };
            var owner = (BlockTableRecord)tr.GetObject(curve.OwnerId, OpenMode.ForWrite);
            var hatch = new Hatch();
            hatch.SetHatchPattern(HatchPatternType.PreDefined, "ANSI31");
            hatch.Normal = normal;
            hatch.Elevation = plane.PointOnPlane
                .TransformBy(Matrix3d.WorldToPlane(new Plane(Point3d.Origin, normal)))
                .Z;
            owner.AppendEntity(hatch);
            tr.AddNewlyCreatedDBObject(hatch, true);
            hatch.Associative = true;
            hatch.AppendLoop(HatchLoopTypes.Default, ids);
            hatch.EvaluateHatch(true);
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 4
CadWCCNL
in reply to: _gile

var _ = (Entity)acHatch;
_.TransformBy(acDoc.Editor.CurrentUserCoordinateSystem);

 

Also removed the lines

 

acHatch.Normal = normal;
acHatch.Elevation = elevation + (elevation - offset)


@_gile something inherently unsafe or unwise with this cast?


Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost