How to convert all rectangles to dynamic block in a layer using lisp or C# ?

How to convert all rectangles to dynamic block in a layer using lisp or C# ?

Anonymous
Not applicable
2,753 Views
11 Replies
Message 1 of 12

How to convert all rectangles to dynamic block in a layer using lisp or C# ?

Anonymous
Not applicable
What can I do in order to accomplish this ?your help is much appreciated.
0 Likes
Accepted solutions (1)
2,754 Views
11 Replies
Replies (11)
Message 2 of 12

devitg
Advisor
Advisor

At last but not least , Please upload a sample dwg , with a before and an  after . 

 

See may attached file  

 

 

0 Likes
Message 3 of 12

_gile
Consultant
Consultant

Hi,

 

As there's neither AutoLISP, nor COM, nor .NET API to create a dynamic bloc by code, I assume the "rectangle" dynamic block already exists in the drawing (see attached rectangle.dwg).

 

Here's a C# example, which checks for rectangular polyline validity whatever the rectangle rotation and plane.

This can be converted to LISP, but I'm more cumfortable with .NET than with LISP these days (more accurately with Visual Studio than with the Visual LISP IDE)

 

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Linq;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(RectangleSample.Commands))]

namespace RectangleSample
{
    public class Commands
    {
        const string blockName = "rectangle";
        const string distX = "Length";
        const string distY = "Width";

        [CommandMethod("TEST")]
        public void Test()
        {
            var doc = AcAp.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            var opts = new PromptStringOptions("\nEnter the layer name: ");
            opts.AllowSpaces = true;
            var rslt = ed.GetString(opts);
            if (rslt.Status != PromptStatus.OK)
                return;
            var layerName = rslt.StringResult;

            using (var tr = db.TransactionManager.StartTransaction())
            {
                var lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
                if (!lt.Has(layerName))
                {
                    ed.WriteMessage($"\nLayer '{layerName}' not found.");
                    return;
                }

                var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (!bt.Has(blockName))
                {
                    ed.WriteMessage($"\nBlock '{blockName}' not found.");
                    return;
                }

                var model = (BlockTableRecord)tr.GetObject(
                    SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
                var plines = model
                    .Cast<ObjectId>()
                    .Where(id => id.ObjectClass.DxfName == "LWPOLYLINE")
                    .Select(id => (Polyline)tr.GetObject(id, OpenMode.ForRead))
                    .Where(pl => pl.Layer == layerName);

                foreach (var pline in plines)
                {
                    Point2d origin;
                    double length, width, rotation;
                    if (TryGetRectangle(pline, out origin, out length, out width, out rotation))
                    {
                        var insPt = new Point3d(origin.X, origin.Y, 0.0);
                        var br = new BlockReference(insPt, bt[blockName]);
                        br.Layer = layerName;
                        br.TransformBy(
                            Matrix3d.PlaneToWorld(new Plane(Point3d.Origin, pline.Normal)) *
                            Matrix3d.Displacement(new Vector3d(0.0, 0.0, pline.Elevation)) *
                            Matrix3d.Rotation(rotation, Vector3d.ZAxis, insPt));

                        model.AppendEntity(br);
                        tr.AddNewlyCreatedDBObject(br, true);
                        foreach (DynamicBlockReferenceProperty prop in br.DynamicBlockReferencePropertyCollection)
                        {
                            switch (prop.PropertyName)
                            {
                                case distX: prop.Value = length; break;
                                case distY: prop.Value = width; break;
                                default: break;
                            }
                        }
                        pline.UpgradeOpen();
                        pline.Erase();
                    }
                }

                tr.Commit();
            }
        }

        private bool IsRectangular(Polyline pline)
        {
            if (pline == null)
                return false;
            if (!pline.Closed)
                return false;
            if (pline.NumberOfVertices != 4)
                return false;
            if (Enumerable.Range(0, 4)
                .Select(i => pline.GetBulgeAt(i))
                .Any(b => b != 0.0))
                return false;
            var s1 = pline.GetLineSegment2dAt(0);
            var s2 = pline.GetLineSegment2dAt(1);
            var s3 = pline.GetLineSegment2dAt(2);
            var s4 = pline.GetLineSegment2dAt(3);
            return
                s1.IsParallelTo(s3) &&
                s2.IsParallelTo(s4) &&
                s1.IsPerpendicularTo(s2);
        }

        private bool TryGetRectangle(Polyline pline, out Point2d origin, out double length, out double width, out double rotation)
        {
            if (IsRectangular(pline))
            {
                var pts = Enumerable.Range(0, 4)
                    .Select(n => pline.GetPoint2dAt(n))
                    .ToList();
                if (!pts[0].GetVectorTo(pts[1]).GetPerpendicularVector().IsCodirectionalTo(pts[1].GetVectorTo(pts[2])))
                    pts.Reverse();
                origin = pts.OrderBy(p => p.Y).ThenBy(p => p.X).First();
                int i = pts.IndexOf(origin);
                length = origin.GetDistanceTo(i == 3 ? pts[0] : pts[i + 1]);
                width = origin.GetDistanceTo(i == 0 ? pts[3] : pts[i - 1]);
                rotation = origin.GetVectorTo(i == 3 ? pts[0] : pts[i + 1]).Angle;
                return true;
            }
            else
            {
                origin = Point2d.Origin;
                length = 0.0;
                width = 0.0;
                rotation = 0.0;
                return false;
            }
        }
    }
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 12

Anonymous
Not applicable

Thanks for your reply, I understand what you mean.

0 Likes
Message 5 of 12

_gile
Consultant
Consultant

Here's a LISP conversion.

 

(defun c:rec2blk (/	      *error*	  2dVar2PtList
		  midPt	      tryGetRectangle	      replaceByBlock
		  blkName     distX	  distY	      layName
		  ss	      rect	  rectangs    org
		  len	      wid	  rot
		 )
  (vl-load-com)
  (or *acad* (setq *acad* (vlax-get-acad-object)))
  (or *acdoc* (setq *acdoc* (vla-get-ActiveDocument *acad*)))
  (or *blocks* (setq *blocks* (vla-get-Blocks *acdoc*)))
  (or *layers* (setq *layers* (vla-get-Layers *acdoc*)))

  (defun *error* (msg)
    (and msg
	 (/= msg "Function cancelled")
	 (prompt (strcat "\nError: " msg))
    )
    (vla-EndUndoMark *acdoc*)
    (princ)
  )

  (defun 2dVar2PtList (lst)
    (if	lst
      (cons (list (car lst) (cadr lst)) (2dVar2PtList (cddr lst)))
    )
  )

  (defun midPt (p1 p2)
    (mapcar (function (lambda (x1 x2) (/ (+ x1 x2) 2.))) p1 p2)
  )

  (defun clockwise-p (p1 p2 p3)
    (< (sin (- (angle p1 p3) (angle p1 p2))) -1e-14)
  )

  (defun tryGetRectangle (pline / pts org len wid rot)
    (if
      (and
	(= (vla-get-closed pline) :vlax-true)
	(setq pts (2dVar2PtList (vlax-get pline 'Coordinates)))
	(= (length pts) 4)
	(equal (distance (car pts) (caddr pts))
	       (distance (cadr pts) (cadddr pts))
	       1e-9
	)
	(equal (midPt (car pts) (caddr pts))
	       (midPt (cadr pts) (cadddr pts))
	       1e-9
	)
      )
       (progn
	 (if (clockwise-p (car pts) (cadr pts) (caddr pts))
	   (setq pts (reverse pts))
	 )
	 (setq org (car
		     (vl-sort pts
			      (function
				(lambda	(p1 p2)
				  (if (= (cadr p1) (cadr p2))
				    (< (car p1) (car p2))
				    (< (cadr p1) (cadr p2))
				  )
				)
			      )
		     )
		   )
	 )
	 (cond
	   ((equal org (car pts))
	    (setq len (distance org (cadr pts))
		  wid (distance org (cadddr pts))
		  rot (angle org (cadr pts))
	    )
	   )
	   ((equal org (cadr pts))
	    (setq len (distance org (caddr pts))
		  wid (distance org (car pts))
		  rot (angle org (caddr pts))
	    )
	   )
	   ((equal org (caddr pts))
	    (setq len (distance org (cadddr pts))
		  wid (distance org (cadr pts))
		  rot (angle org (cadddr pts))
	    )
	   )
	   (T
	    (setq len (distance org (car pts))
		  wid (distance org (caddr pts))
		  rot (angle org (car pts))
	    )
	   )
	 )
	 (list org len wid rot)
       )
    )
  )

  (defun replaceByBlock
	 (pline rect blkName layName / org len wid rot ins blk)
    (mapcar 'set '(org len wid rot) rect)
    (setq
      ins (list (car org) (cadr org) (vla-get-Elevation pline))
      blk (vla-InsertBlock
	    (vla-get-ModelSpace *acdoc*)
	    (vlax-3d-point ins)
	    blkName
	    1.0
	    1.0
	    1.0
	    rot
	  )
    )
    (vla-put-Normal blk (vla-get-Normal pline))
    (vla-put-Layer blk layName)
    (foreach prop (vlax-invoke blk 'GetDynamicBlockProperties)
      (cond
	((= (vla-get-PropertyName prop) distX)
	 (vla-put-Value prop len)
	)
	((= (vla-get-PropertyName prop) distY)
	 (vla-put-Value prop wid)
	)
      )
    )
  )

  (setq	blkName	"rectangle"
	distX	"Length"
	distY	"Width"
  )
  (vla-StartUndoMark *acdoc*)
  (if (tblsearch "block" blkName)
    (if
      (and
	(setq layName (getstring T "\nEnter the layer name: "))
	(tblsearch "layer" layName)
      )
       (if (ssget "_X"
		  (list	(cons 0 "LWPOLYLINE")
			(cons 8 layName)
			(cons 410 "Model")
		  )
	   )
	 (progn
	   (vlax-for pline
		     (setq ss (vla-get-ActiveSelectionSet *acdoc*))
	     (if (setq rect (tryGetRectangle pline))
	       (progn
		 (replaceByBlock pline rect blkName layName)
		 (vla-Delete pline)
	       )
	     )
	   )
	   (vla-Delete ss)
	 )
       )
       (prompt (strcat "\nLayer '" layName "' not found."))
    )
    (prompt (strcat "\nBlock '" blkName "' not found."))
  )
  (*error* nil)
)


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 12

Anonymous
Not applicable

Thanks, but it doesn't work for dynamic block.

0 Likes
Message 7 of 12

_gile
Consultant
Consultant

Jyh2003 a écrit :

Thanks, but it doesn't work for dynamic block.


It does work with the "rectangle" block I provided in a previous message which is a dynamic block.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 12

Anonymous
Not applicable

I get the below error message when I input rec2blk command.

 

Block 'rectangle' not found.

 

Would you please help on this, thanks.

0 Likes
Message 9 of 12

_gile
Consultant
Consultant
Accepted solution

The block "rectangle" (attached) have to be in the current drawing (in the block table).

 

You can also use another dynamic block while it has an XY dynamic parameter with strtch actions. Just replace the block  and parameters names in this expression (near the end of the LISP code)

 

  (setq	blkName	"rectangle"
	distX	"Length"
	distY	"Width"
  )


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 10 of 12

Anonymous
Not applicable

Would you please help me to convert these rectangles (see attached) to dynamic block ?

 

Many thanks.

0 Likes
Message 11 of 12

_gile
Consultant
Consultant

You didn't provide any dynamic bloc.

If the "rectangle" dynamic block attached in upper message suit your need, just use it with the upper LISP.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 12 of 12

Anonymous
Not applicable

I see, thanks for your help.

0 Likes