Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 08/02/2026 in all areas

  1. Hello. I'm happy to share this routine I created with the help of Copilot, which I believe works acceptably for distributing elements (sprinklers, detectors, etc.) based on user input. I would be very grateful if any member of this forum could improve the code or offer any advice. I'm a big fan of @Lee Mac's programs, whom I consider a master. The routine will ask the user to define the measurements that define minimum and maximum values and then, by selecting a block, from which it acquires the layer and scale properties, by selecting an area, with or without "islands", it will generate a mesh pattern, which for a preliminary design is acceptable or approximate in many cases. I am Spanish and the routine is coded in Castilian Spanish (Spain). PCI_PRO (PFP).lsp
    5 points
  2. In another post there was questions about changing Mline styles for existing Mlines. So answered that and thought what about adding fillets to a mline. Wel as we know you can not do that, so did it this way. Converting the individual mlines to plines then filleting the plines produced. The fillet radius changing matching the offsets. ; Convert mline to plines and add a rdius. ; By AlanH Aug 2026 (defun LWPoly (lst cls) (entmakex (append (list (cons 0 "LWPOLYLINE") (cons 100 "AcDbEntity") (cons 100 "AcDbPolyline") (cons 90 (length lst)) (cons 70 cls)) (mapcar (function (lambda (p) (cons 10 p))) lst))) ) (defun c:ml2plrad ( / oldsnap pt1 pt2 ent obj obj2 co-ords dict col lst x) (setq oldsnap (getvar 'osmode)) (setvar 'osmode 0) (setq pt1 (getpoint "\nPick 1st point for drag ")) (setq pt2 (getpoint pt1 "\nPick 2nd point ")) (setq pts (list pt1 pt2)) (setq ent (ssname (ssget "F" pts (list (cons 0 "Mline"))) 0)) (setq obj (vlax-ename->vla-object ent)) (setq styleName (vla-get-StyleName obj)) (setq lay (vlax-get obj 'layer)) (setvar 'clayer lay) (setq co-ords (vlax-get obj 'coordinates)) (vla-delete obj) (setq pts2 '() x 0) (repeat (/ (length co-ords) 3) (setq pts2 (cons (list (nth x co-ords)(nth (1+ x) co-ords)) pts2)) (setq x (+ x 3)) ) (setq dict (dictsearch (cdr (assoc -1 (dictsearch (namedobjdict) "ACAD_MLINESTYLE"))) styleName)) (setq offsets (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 49)) dict))) (setq col (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 62)) dict))) (setq col (cdr col)) ; ignore 1st color (setq lst '() x 0) (repeat (length col) (setq lst (cons (list (nth x offsets)(nth x col)) lst)) (setq x (1+ x)) ) (LWPoly pts2 0) (setvar 'filletrad (getreal "\nEnter Fillet radius for most outside offset ")) (command "fillet" "P" (entlast)) (setq x 1) (repeat (- (length col) 1) (setq obj2 (vlax-ename->vla-object (entlast))) (vlax-put obj2 'color (nth (1- x) col)) (vla-offset (vlax-ename->vla-object (entlast)) (- (nth x offsets)(nth (1- x) offsets))) (setq x (1+ x)) ) (setq obj2 (vlax-ename->vla-object (entlast))) (vlax-put obj2 'color (nth (1- x) col)) (setvar 'osmode oldsnap) (princ) ) (c:ml2plrad)
    3 points
  3. @troggarf yep took the plunge and installed Python, the install is easy With @Danielm103 help also installed a couple of extra modules that where needed for his code. Then it's simple like lisp using Pyload instead of Appload. Or you can load python scripts using a lisp call. ; lisp start python (setq python "C:/Users/xxxxxx/AppData/Local/Python/Python-3.14-64/python.exe" pyscript "D:\\alan\\lisp\\ctb table.py" ) (startapp python pyscript)
    3 points
  4. Been playing quite a bit with Gilles Chanteau's kdtree lisp app published at theSwamp kd-tree (AutoLISP). So decided to turn it into a dll to use from autolisp. I am attaching the C# code below and a bat file to compile it for Autocad 2017. The dll will create the following function for autolisp: KDtreeLisp.dll Reference & Command SyntaxIn AutoLISP documentation standards, optional parameters are enclosed in square brackets [optional]. (KD-BUILD pointList [dimensions] [customHandle]) Builds a KD-Tree spatial index from a list of points. Arguments: pointList: LIST — List of 2D or 3D point lists ((x y z) ...). dimensions (optional): INT — 2 for 2D $(X,Y)$ or 3 for 3D $(X,Y,Z)$. Default is 2. customHandle (optional): STR — Custom string handle identifier. Returns: STR handle (e.g., "<KD-TREE-1>" or "<KD-TREE-SURVEY>"), or nil on failure. (KD-NEAREST handle targetPoint [count] [maxRadius]) Finds the nearest n points to a target point. Arguments: handle: STR — KD-Tree handle string. targetPoint: LIST — Target point (x y z). count (optional): INT — Number of nearest points to retrieve. Default is 1. maxRadius (optional): REAL or INT — Maximum search radius limit. Points farther than this distance are ignored. Returns:If count = 1 (or omitted): A single point (x y z). If count > 1: A list of points ((x1 y1 z1) (x2 y2 z2) ...). (KD-NEAREST-INFO handle targetPoint [count] [maxRadius]) Finds nearest points with detailed metadata. Arguments: handle: STR — KD-Tree handle string. targetPoint: LIST — Target point (x y z). count (optional): INT — Number of points to retrieve. Default is 1. maxRadius (optional): REAL or INT — Maximum search radius limit. Returns: A list of detailed entries (((x y z) index distance) ...). (KD-RANGE handle centerPoint radius) Finds all points inside a fixed search radius without a count limit. Arguments: handle: STR — KD-Tree handle string. centerPoint: LIST — Center search coordinate (x y z). radius: REAL or INT — Search radius. Returns: A list of all enclosed points ((x1 y1 z1) (x2 y2 z2) ...) sorted by distance, or nil if none are found. (KD-FREE [handle]) Frees tree memory. Arguments: handle (optional): STR — Tree handle to free. If omitted, clears all active trees. Returns: INT — Number of trees removed. Once you've compiled the C#, kdtreelisp.dll will be created and ready to use in Autocad. using System; using System.Collections.Generic; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; namespace KdTreeLisp { // ========================================== // 1. DATA STRUCTURES & TREE ENGINE // ========================================== public class KdItem { public Point3d Point { get; set; } public int Id { get; set; } public KdItem(Point3d pt, int id) { Point = pt; Id = id; } } public class KdNode { public Point3d Point { get; set; } public int Id { get; set; } public KdNode Left { get; set; } public KdNode Right { get; set; } public KdNode(Point3d point, int id) { Point = point; Id = id; } } public class NeighborResult : IComparable<NeighborResult> { public KdNode Node { get; set; } public double DistanceSq { get; set; } public NeighborResult(KdNode node, double distSq) { Node = node; DistanceSq = distSq; } public int CompareTo(NeighborResult other) { return other.DistanceSq.CompareTo(this.DistanceSq); } } public class KdTree { public KdNode Root { get; private set; } public int ActiveDimensions { get; private set; } public void Build(List<KdItem> items, int activeDimensions) { ActiveDimensions = Math.Max(1, Math.Min(3, activeDimensions)); Root = BuildRecursive(items, 0); } private KdNode BuildRecursive(List<KdItem> items, int depth) { if (items == null || items.Count == 0) return null; int axis = depth % ActiveDimensions; items.Sort(delegate(KdItem a, KdItem b) { return GetAxisValue(a.Point, axis).CompareTo(GetAxisValue(b.Point, axis)); }); int medianIndex = items.Count / 2; KdNode node = new KdNode(items[medianIndex].Point, items[medianIndex].Id); node.Left = BuildRecursive(items.GetRange(0, medianIndex), depth + 1); node.Right = BuildRecursive(items.GetRange(medianIndex + 1, items.Count - (medianIndex + 1)), depth + 1); return node; } public double CalculateDistanceSq(Point3d p1, Point3d p2) { double dx = p1.X - p2.X; double dy = p1.Y - p2.Y; if (ActiveDimensions == 2) { return dx * dx + dy * dy; } double dz = p1.Z - p2.Z; return dx * dx + dy * dy + dz * dz; } // K-Nearest Neighbor Search with Optional Maximum Radius Limit public List<NeighborResult> FindNearest(Point3d target, int count, double maxRadius = double.MaxValue) { List<NeighborResult> heap = new List<NeighborResult>(); if (Root == null || count <= 0) return heap; double maxDistSq = (maxRadius == double.MaxValue) ? double.MaxValue : maxRadius * maxRadius; SearchNearest(Root, target, count, maxDistSq, heap, 0); // Sort ascending by distance before returning heap.Sort(delegate(NeighborResult a, NeighborResult b) { return a.DistanceSq.CompareTo(b.DistanceSq); }); return heap; } private void SearchNearest(KdNode current, Point3d target, int count, double maxDistSq, List<NeighborResult> heap, int depth) { if (current == null) return; double distSq = CalculateDistanceSq(current.Point, target); // Accept point only if inside maximum distance threshold if (distSq <= maxDistSq) { if (heap.Count < count) { heap.Add(new NeighborResult(current, distSq)); heap.Sort(); } else if (distSq < heap[0].DistanceSq) { heap[0] = new NeighborResult(current, distSq); heap.Sort(); } } int axis = depth % ActiveDimensions; double diff = GetAxisValue(target, axis) - GetAxisValue(current.Point, axis); KdNode primary = diff < 0 ? current.Left : current.Right; KdNode secondary = diff < 0 ? current.Right : current.Left; SearchNearest(primary, target, count, maxDistSq, heap, depth + 1); // Pruning condition: check if secondary subtree could contain points closer than current worst candidate double currentSearchRadiusSq = (heap.Count < count) ? maxDistSq : Math.Min(heap[0].DistanceSq, maxDistSq); if (diff * diff < currentSearchRadiusSq) { SearchNearest(secondary, target, count, maxDistSq, heap, depth + 1); } } // Range Search: Retrieves ALL points within radius public List<NeighborResult> RangeSearch(Point3d center, double radius) { List<NeighborResult> results = new List<NeighborResult>(); if (Root == null || radius < 0) return results; double radiusSq = radius * radius; SearchRangeRecursive(Root, center, radiusSq, results, 0); results.Sort(delegate(NeighborResult a, NeighborResult b) { return a.DistanceSq.CompareTo(b.DistanceSq); }); return results; } private void SearchRangeRecursive(KdNode current, Point3d center, double radiusSq, List<NeighborResult> results, int depth) { if (current == null) return; double distSq = CalculateDistanceSq(current.Point, center); if (distSq <= radiusSq) { results.Add(new NeighborResult(current, distSq)); } int axis = depth % ActiveDimensions; double diff = GetAxisValue(center, axis) - GetAxisValue(current.Point, axis); if (diff < 0) { SearchRangeRecursive(current.Left, center, radiusSq, results, depth + 1); if (diff * diff <= radiusSq) { SearchRangeRecursive(current.Right, center, radiusSq, results, depth + 1); } } else { SearchRangeRecursive(current.Right, center, radiusSq, results, depth + 1); if (diff * diff <= radiusSq) { SearchRangeRecursive(current.Left, center, radiusSq, results, depth + 1); } } } private double GetAxisValue(Point3d pt, int axis) { switch (axis) { case 0: return pt.X; case 1: return pt.Y; default: return pt.Z; } } } // ========================================== // 2. AUTOLISP INTERFACE BRIDGE // ========================================== public class LispBridge { private static readonly Dictionary<string, KdTree> _trees = new Dictionary<string, KdTree>(); private static int _treeCounter = 1; // Signature: (KD-BUILD pointList [dimensions] [customHandle]) [LispFunction("KD-BUILD")] public static TypedValue BuildTree(ResultBuffer args) { if (args == null) return new TypedValue((int)LispDataType.Nil); TypedValue[] arr = args.AsArray(); List<KdItem> items = new List<KdItem>(); int idCounter = 0; int requestedDimensions = 2; string customHandle = null; foreach (TypedValue tv in arr) { if (tv.TypeCode == (int)LispDataType.Point3d) { items.Add(new KdItem((Point3d)tv.Value, idCounter++)); } else if (tv.TypeCode == (int)LispDataType.Point2d) { Point2d pt2 = (Point2d)tv.Value; items.Add(new KdItem(new Point3d(pt2.X, pt2.Y, 0.0), idCounter++)); } else if (tv.TypeCode == (int)LispDataType.Int16 || tv.TypeCode == (int)LispDataType.Int32) { requestedDimensions = Convert.ToInt32(tv.Value); } else if (tv.TypeCode == (int)LispDataType.Text) { customHandle = Convert.ToString(tv.Value); } } if (items.Count == 0) return new TypedValue((int)LispDataType.Nil); KdTree tree = new KdTree(); tree.Build(items, requestedDimensions); string handle = string.IsNullOrEmpty(customHandle) ? string.Format("<KD-TREE-{0}>", _treeCounter++) : string.Format("<KD-TREE-{0}>", customHandle.ToUpper()); _trees[handle] = tree; return new TypedValue((int)LispDataType.Text, handle); } // Signature: (KD-NEAREST handle targetPoint [count] [maxRadius]) [LispFunction("KD-NEAREST")] public static ResultBuffer FindNearest(ResultBuffer args) { if (args == null) return null; List<TypedValue> values = ExtractValues(args); if (values.Count < 2) return null; if (values[0].TypeCode != (int)LispDataType.Text) return null; string handle = Convert.ToString(values[0].Value); if (!_trees.ContainsKey(handle)) return null; KdTree tree = _trees[handle]; if (tree.Root == null) return null; Point3d target; if (values[1].TypeCode == (int)LispDataType.Point3d) { target = (Point3d)values[1].Value; } else if (values[1].TypeCode == (int)LispDataType.Point2d) { Point2d p2 = (Point2d)values[1].Value; target = new Point3d(p2.X, p2.Y, 0.0); } else { return null; } int count = 1; if (values.Count > 2 && (values[2].TypeCode == (int)LispDataType.Int16 || values[2].TypeCode == (int)LispDataType.Int32)) { count = Convert.ToInt32(values[2].Value); } double maxRadius = double.MaxValue; if (values.Count > 3) { TypedValue v = values[3]; if (v.TypeCode == (int)LispDataType.Double || v.TypeCode == (int)LispDataType.Int16 || v.TypeCode == (int)LispDataType.Int32) { maxRadius = Convert.ToDouble(v.Value); } } List<NeighborResult> results = tree.FindNearest(target, count, maxRadius); if (results.Count == 0) return null; ResultBuffer res = new ResultBuffer(); if (count == 1) { res.Add(new TypedValue((int)LispDataType.Point3d, results[0].Node.Point)); } else { res.Add(new TypedValue((int)LispDataType.ListBegin)); foreach (NeighborResult item in results) { res.Add(new TypedValue((int)LispDataType.Point3d, item.Node.Point)); } res.Add(new TypedValue((int)LispDataType.ListEnd)); } return res; } // Signature: (KD-NEAREST-INFO handle targetPoint [count] [maxRadius]) [LispFunction("KD-NEAREST-INFO")] public static ResultBuffer FindNearestInfo(ResultBuffer args) { if (args == null) return null; List<TypedValue> values = ExtractValues(args); if (values.Count < 2) return null; if (values[0].TypeCode != (int)LispDataType.Text) return null; string handle = Convert.ToString(values[0].Value); if (!_trees.ContainsKey(handle)) return null; KdTree tree = _trees[handle]; Point3d target; if (values[1].TypeCode == (int)LispDataType.Point3d) target = (Point3d)values[1].Value; else if (values[1].TypeCode == (int)LispDataType.Point2d) target = new Point3d(((Point2d)values[1].Value).X, ((Point2d)values[1].Value).Y, 0.0); else return null; int count = 1; if (values.Count > 2 && (values[2].TypeCode == (int)LispDataType.Int16 || values[2].TypeCode == (int)LispDataType.Int32)) count = Convert.ToInt32(values[2].Value); double maxRadius = double.MaxValue; if (values.Count > 3) { TypedValue v = values[3]; if (v.TypeCode == (int)LispDataType.Double || v.TypeCode == (int)LispDataType.Int16 || v.TypeCode == (int)LispDataType.Int32) maxRadius = Convert.ToDouble(v.Value); } List<NeighborResult> results = tree.FindNearest(target, count, maxRadius); if (results.Count == 0) return null; ResultBuffer res = new ResultBuffer(); res.Add(new TypedValue((int)LispDataType.ListBegin)); foreach (NeighborResult item in results) { res.Add(new TypedValue((int)LispDataType.ListBegin)); res.Add(new TypedValue((int)LispDataType.Point3d, item.Node.Point)); res.Add(new TypedValue((int)LispDataType.Int32, item.Node.Id)); res.Add(new TypedValue((int)LispDataType.Double, Math.Sqrt(item.DistanceSq))); res.Add(new TypedValue((int)LispDataType.ListEnd)); } res.Add(new TypedValue((int)LispDataType.ListEnd)); return res; } // Signature: (KD-RANGE handle centerPoint radius) [LispFunction("KD-RANGE")] public static ResultBuffer RangeSearch(ResultBuffer args) { if (args == null) return null; List<TypedValue> values = ExtractValues(args); if (values.Count < 3) return null; if (values[0].TypeCode != (int)LispDataType.Text) return null; string handle = Convert.ToString(values[0].Value); if (!_trees.ContainsKey(handle)) return null; KdTree tree = _trees[handle]; Point3d center; if (values[1].TypeCode == (int)LispDataType.Point3d) center = (Point3d)values[1].Value; else if (values[1].TypeCode == (int)LispDataType.Point2d) center = new Point3d(((Point2d)values[1].Value).X, ((Point2d)values[1].Value).Y, 0.0); else return null; double radius = 0.0; TypedValue rVal = values[2]; if (rVal.TypeCode == (int)LispDataType.Double || rVal.TypeCode == (int)LispDataType.Int16 || rVal.TypeCode == (int)LispDataType.Int32) { radius = Convert.ToDouble(rVal.Value); } else { return null; } List<NeighborResult> results = tree.RangeSearch(center, radius); if (results.Count == 0) return null; ResultBuffer res = new ResultBuffer(); res.Add(new TypedValue((int)LispDataType.ListBegin)); foreach (NeighborResult item in results) { res.Add(new TypedValue((int)LispDataType.Point3d, item.Node.Point)); } res.Add(new TypedValue((int)LispDataType.ListEnd)); return res; } // Signature: (KD-FREE [handle]) [LispFunction("KD-FREE")] public static TypedValue FreeTree(ResultBuffer args) { if (args == null) { int count = _trees.Count; _trees.Clear(); return new TypedValue((int)LispDataType.Int32, count); } List<TypedValue> values = ExtractValues(args); if (values.Count == 0 || values[0].TypeCode != (int)LispDataType.Text) { int count = _trees.Count; _trees.Clear(); return new TypedValue((int)LispDataType.Int32, count); } string handle = Convert.ToString(values[0].Value); bool removed = _trees.Remove(handle); return new TypedValue((int)LispDataType.Int32, removed ? 1 : 0); } private static List<TypedValue> ExtractValues(ResultBuffer resbuf) { List<TypedValue> list = new List<TypedValue>(); foreach (TypedValue tv in resbuf.AsArray()) { if (tv.TypeCode != (int)LispDataType.ListBegin && tv.TypeCode != (int)LispDataType.ListEnd) { list.Add(tv); } } return list; } } } ymg KDtreeLisp.cs Build.bat
    2 points
  5. all you would need to do is edit the bat file to point at the target platform. AI says you can jut create a project file next to the .cs, then use “dotnet build -c Release” <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <!-- Use net48 for AutoCAD 2021-2024, or net8.0-windows for AutoCAD 2025+ --> <TargetFramework>net8.0-windows</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <Platforms>x64</Platforms> </PropertyGroup> <!-- Reference your AutoCAD libraries --> <ItemGroup> <Reference Include="AcCoreMgd"> <HintPath>C:\Program Files\Autodesk\AutoCAD 2017\AcCoreMgd.dll</HintPath> <Private>False</Private> <!-- Prevents copying AutoCAD DLLs to your output folder --> </Reference> <Reference Include="AcDbMgd"> <HintPath>C:\Program Files\Autodesk\AutoCAD 2017\AcDbMgd.dll</HintPath> <Private>False</Private> </Reference> <Reference Include="AcMgd"> <HintPath>C:\Program Files\Autodesk\AutoCAD 2017\AcMgd.dll</HintPath> <Private>False</Private> </Reference> </ItemGroup> </Project>
    2 points
  6. I thought I might write some samples to help guide lisp users that are interested in trying out Python. Long time lisp users will feel right at home using result buffers (DXF Lists). In PyRx, it’s wrappers around the old school C ADS functions, I.e. ads_entget, yay, like driving an F-150 with a toilet bowl plunger as the gear shift. In Python, most of these old functions reside in Db.Core The resbuf* linked lists are wrapped into a list of tuples.. does this output look familiar? from pyrx import Ap, Db, Ed, Ge @Ap.Command() def doit0() -> None: #entsel with a filter ps, id, _ = Ed.Editor.entSel("\nPick it: ", Db.Line.desc()) if ps != Ed.PromptStatus.eOk: raise RuntimeError("oof {}:".format(ps)) #get the result buffer rb = Db.Core.entGet(id) print(rb) # [ # (-1, PyDb.ObjectId(1de8bf954d0)), # (0, 'LINE'), # (330, PyDb.ObjectId(1de8bf9a1f0)), # (5, '265'), # (100, 'AcDbEntity'), # (67, 0), # (410, 'Model'), # (8, '0'), # (100, 'AcDbLine'), # (10, PyGe.Point3d(0.00000000000000,0.00000000000000,0.00000000000000)), # (11, PyGe.Point3d(100.00000000000000,100.00000000000000,0.00000000000000)), # (210, PyGe.Point3d(0.00000000000000,0.00000000000000,1.00000000000000)) # ]
    2 points
  7. A google should have pointed you to the great Website by Lee-mac, Bounding Box.
    2 points
  8. As previously stated, used an install lisp, only a few minutes.
    2 points
  9. Nice work. I built the same kind of wheel for Revit a while back, so it was good to see someone doing it on the AutoCAD side. I could not see it running from the thread either, so a short screen recording would probably get you more replies here than anything else. Everything below is from my side, on Revit, so translate it however it fits. These are the ones that cost me the most time. Aim by the pointer angle, not by which shape is under the pointer. Point in a direction and that wedge lights up, whether you are near the middle or way out past the rim. That one change is what made mine feel like a game wheel instead of a menu. Number keys 1 to 9 pick a slot directly. Once you know your own layout you stop looking at the wheel at all, and that is where the real speed comes from. Grey out a slot that cannot run right now, and work all of them out in one pass before the wheel opens. Mine has to check the current selection to answer that. Doing it slot by slot meant a 50,000 element selection got walked once for every slot on the wheel. Once for the whole wheel fixed it. Check the command will really be accepted before you fire it. When mine was refused, nothing ran but the wheel still closed as if it had worked. That is the worst kind of failure because it looks like an answer. Smaller things I would not have guessed: Test the wedge maths outside CAD. I rebuilt mine as a standalone renderer and produced every slot count at every wheel size as images. Far quicker than restarting the host for each attempt, and it found a label collision at my highest slot count that I would never have caught by eye. Store the command name in the slot, not the button text. Rename a button later and the layout survives. Slot 1 at the top, the rest clockwise, always. The muscle memory is the whole point, so that order can never move. Drop any glow or fade. Mine had a soft shadow on the hovered wedge and a fade across the whole ring, and both were being recomputed every time the pointer crossed a boundary. A brighter fill and a thicker outline read exactly the same and cost nothing. Only redraw the wedge that changed, never the whole ring. Let the config file be missing or corrupt without complaining. Mine falls back to the defaults instead of throwing a message at me, and I have never once wanted the message. Do not let dead keys close it. Enter, space and a number higher than my slot count all used to dismiss the wheel doing nothing, which just felt broken. Open the settings from the wheel itself, and let people set it up by dragging onto a real wheel instead of filling in a list. Drag a tool onto a slot to fill it, drag one slot onto another to swap them. Mine draws the same ring at a smaller size for that, so what you arrange is what opens. Two things I am curious about, since you draw with real entities in model space rather than a window over the top. Does the wheel stay the same size on screen at any zoom, or does it scale with the drawing? Mine sits in a window above everything so I never had to deal with that, but it looks like the harder half of your approach. And the double click. In AutoCAD a double click on an object opens its editor, so what happens when the pointer is over an entity instead of empty space? I went with a keyboard shortcut and dodged the question, but yours is the nicer gesture if it works.
    2 points
  10. If you use Etransmit it will add extra files to what is exported, in particular say custom fonts and xrefs.
    2 points
  11. @HypnoS if your using title blocks then it is possible to detect the name of the title block and use the correct plot settings, for example a A4 in landscape or portrait mode. Just using say layout extents can return wrong answers as even where I worked there was occasional junk in a layout outside of the title block.
    2 points
  12. Based on my practice of networking LISP, I am going to make an online game platform that executes in the CAD model space. However, the game category is different from the products I developed before. It requires a certain timeliness, which is a challenge to the network infrastructure. In short, I have developed a local battle version of Gomoku. You can copy the following code or download the lsp file to run it locally. Gomoku V0.3.lsp After the problem of the network infrastructure part is solved, the plug-in will be iterated quickly to achieve online instant challenges. I have provided another updateable installation method to avoid duplicate copying or downloading. If I update it, you will know: [Gomoku CadCade] VCID: 1G8VY1 @ VedaCAD I hope to hear your opinions on UI and interaction. ;;; AutoCAD Gomoku v0.3 ;;; Author: Vico (CadCade) ;;; Description: Standalone 15x15 Gomoku in ModelSpace (vl-load-com) ;;; --- Globals & States --- (setq *gmk-size* 15 *gmk-cell* 10.0 *gmk-radius* (* *gmk-cell* 0.333) *gmk-layer* "CADCADE_CANVAS" *gmk-p1-color* 10 ; Soft Pastel Red *gmk-p2-color* 140 ; Bright Light Blue *gmk-win-color* 3 ; Bright Green *gmk-idle-color* 8) ; Dark Gray (setq *gmk-board* nil *gmk-hover-ent* nil *gmk-active* nil *gmk-ui-x* 0.0 *gmk-ui-y* 0.0 *gmk-p1-total* 0.0 *gmk-p2-total* 0.0 *gmk-history* nil) (setq *gmk-ui-p1-time-ent* nil *gmk-ui-p1-tot-ent* nil *gmk-ui-p2-time-ent* nil *gmk-ui-p2-tot-ent* nil *gmk-ui-p1-icon-ent* nil *gmk-ui-p1-name-ent* nil *gmk-ui-p2-icon-ent* nil *gmk-ui-p2-name-ent* nil *gmk-ui-hist-1* nil *gmk-ui-hist-2* nil *gmk-ui-hist-3* nil *gmk-ui-hist-4* nil *gmk-ui-hist-5* nil *gmk-ui-hist-6* nil) ;;; --- Data Structure --- (defun gmk:get-index (x y) (+ (* y *gmk-size*) x)) (defun gmk:get-cell (x y) (if (and (>= x 0) (< x *gmk-size*) (>= y 0) (< y *gmk-size*)) (nth (gmk:get-index x y) *gmk-board*) -1 ) ) (defun gmk:set-cell (x y val / idx i nb) (setq idx (gmk:get-index x y) i 0 nb nil) (foreach item *gmk-board* (setq nb (cons (if (= i idx) val item) nb) i (1+ i)) ) (setq *gmk-board* (reverse nb)) ) ;;; --- Graphics & Layer --- (defun gmk:ensure-layer () (if (not (tblsearch "LAYER" *gmk-layer*)) (entmakex (list '(0 . "LAYER") '(100 . "AcDbSymbolTableRecord") '(100 . "AcDbLayerTableRecord") (cons 2 *gmk-layer*) '(70 . 0))) ) ) (defun gmk:purge-board (/ ss i e) (if (setq ss (ssget "_X" (list (cons 8 *gmk-layer*)))) (progn (setq i -1) (while (setq e (ssname ss (setq i (1+ i)))) (vl-catch-all-apply 'entdel (list e)) ) ) ) ) (defun gmk:draw-line (pt1 pt2 col) (entmakex (list '(0 . "LINE") (cons 8 *gmk-layer*) (cons 10 pt1) (cons 11 pt2) (cons 62 col))) ) (defun gmk:draw-thick-line (pt1 pt2 col wid) (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") (cons 8 *gmk-layer*) '(100 . "AcDbPolyline") '(90 . 2) '(70 . 0) (cons 43 wid) (cons 62 col) (cons 10 (list (car pt1) (cadr pt1))) (cons 10 (list (car pt2) (cadr pt2))))) ) (defun gmk:draw-text (pt str hgt col) (entmakex (list '(0 . "TEXT") (cons 8 *gmk-layer*) (cons 10 pt) (cons 40 hgt) (cons 1 str) (cons 62 col))) ) (defun gmk:draw-centered-text (pt str hgt col) (entmakex (list '(0 . "TEXT") (cons 8 *gmk-layer*) (cons 10 pt) (cons 11 pt) (cons 40 hgt) (cons 1 str) (cons 62 col) '(72 . 1) '(73 . 2))) ) (defun gmk:update-dynamic-text (sym pt str hgt col / el) (if (and (eval sym) (setq el (entget (eval sym)))) (entmod (subst (cons 62 col) (assoc 62 el) (subst (cons 1 str) (assoc 1 el) el))) (set sym (gmk:draw-text pt str hgt col)) ) ) (defun gmk:update-color (ent col / el) (if (and ent (setq el (entget ent))) (entmod (subst (cons 62 col) (assoc 62 el) el)) ) ) (defun gmk:set-turn (p / c1 c2) (if (= p 1) (setq c1 *gmk-p1-color* c2 *gmk-idle-color*) (setq c1 *gmk-idle-color* c2 *gmk-p2-color*) ) (gmk:update-color *gmk-ui-p1-icon-ent* c1) (gmk:update-color *gmk-ui-p1-name-ent* c1) (gmk:update-color *gmk-ui-p2-icon-ent* c2) (gmk:update-color *gmk-ui-p2-name-ent* c2) (redraw) ) (defun gmk:draw-solid-circle (pt rad col / r2 p1 p2) (setq r2 (/ rad 2.0) p1 (list (- (car pt) r2) (cadr pt)) p2 (list (+ (car pt) r2) (cadr pt))) (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") (cons 8 *gmk-layer*) '(100 . "AcDbPolyline") '(90 . 2) '(70 . 1) (cons 62 col) (cons 43 rad) (cons 10 p1) '(42 . 1.0) (cons 10 p2) '(42 . 1.0))) ) (defun gmk:draw-rounded-rect (xmin ymin xmax ymax r col wid / b) (setq b 0.41421356) ; tan(pi/8) (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") (cons 8 *gmk-layer*) '(100 . "AcDbPolyline") '(90 . 8) '(70 . 1) (cons 43 wid) (cons 62 col) (cons 10 (list (+ xmin r) ymin)) (cons 42 0.0) (cons 10 (list (- xmax r) ymin)) (cons 42 b) (cons 10 (list xmax (+ ymin r))) (cons 42 0.0) (cons 10 (list xmax (- ymax r))) (cons 42 b) (cons 10 (list (- xmax r) ymax)) (cons 42 0.0) (cons 10 (list (+ xmin r) ymax)) (cons 42 b) (cons 10 (list xmin (- ymax r))) (cons 42 0.0) (cons 10 (list xmin (+ ymin r))) (cons 42 b))) ) (defun gmk:draw-board (/ i mdim cx ux-l ux-r) (setq mdim (* (1- *gmk-size*) *gmk-cell*)) (setq i 1) (while (< i (1- *gmk-size*)) (gmk:draw-line (list (* i *gmk-cell*) 0.0 0.0) (list (* i *gmk-cell*) mdim 0.0) 8) (gmk:draw-line (list 0.0 (* i *gmk-cell*) 0.0) (list mdim (* i *gmk-cell*) 0.0) 8) (setq i (1+ i)) ) (setq i 0) (while (< i *gmk-size*) (gmk:draw-text (list (- (* i *gmk-cell*) (* *gmk-cell* 0.2)) (* -0.8 *gmk-cell*) 0.0) (chr (+ 65 i)) (* *gmk-cell* 0.4) 8) (gmk:draw-text (list (* -1.4 *gmk-cell*) (- (* i *gmk-cell*) (* *gmk-cell* 0.2)) 0.0) (itoa (1+ i)) (* *gmk-cell* 0.4) 8) (setq i (1+ i)) ) (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(100 . "AcDbPolyline") (cons 8 *gmk-layer*) '(90 . 4) '(70 . 1) (cons 43 (* *gmk-cell* 0.08)) '(62 . 8) (cons 10 '(0.0 0.0)) (cons 10 (list mdim 0.0)) (cons 10 (list mdim mdim)) (cons 10 (list 0.0 mdim)))) (gmk:draw-solid-circle (list (* 7 *gmk-cell*) (* 7 *gmk-cell*) 0.0) (* *gmk-cell* 0.15) 8) (setq ux-l (+ mdim (* *gmk-cell* 2.5)) ux-r (+ ux-l (* *gmk-cell* 7.5)) cx (+ ux-l (* *gmk-cell* 3.75))) (gmk:draw-rounded-rect ux-l 0.0 ux-r mdim (* *gmk-cell* 0.5) 8 (* *gmk-cell* 0.05)) (gmk:draw-centered-text (list cx (- mdim (* *gmk-cell* 0.7)) 0.0) "GOMOKU" (* *gmk-cell* 0.55) 8) (gmk:draw-centered-text (list cx (- mdim (* *gmk-cell* 1.4)) 0.0) "CadCade.com" (* *gmk-cell* 0.25) 8) (gmk:draw-line (list (+ ux-l (* *gmk-cell* 0.4)) (- mdim (* *gmk-cell* 1.9)) 0.0) (list (- ux-r (* *gmk-cell* 0.4)) (- mdim (* *gmk-cell* 1.9)) 0.0) 8) (setq *gmk-ui-p1-icon-ent* (gmk:draw-solid-circle (list (+ ux-l (* *gmk-cell* 1.0)) (- mdim (* *gmk-cell* 2.7)) 0.0) *gmk-radius* *gmk-p1-color*)) (setq *gmk-ui-p1-name-ent* (gmk:draw-text (list (+ ux-l (* *gmk-cell* 1.8)) (- mdim (* *gmk-cell* 2.85)) 0.0) "Player 1" (* *gmk-cell* 0.4) *gmk-p1-color*)) (gmk:draw-text (list (+ ux-l (* *gmk-cell* 1.0)) (- mdim (* *gmk-cell* 3.6)) 0.0) "Think Time:" (* *gmk-cell* 0.3) 8) (gmk:update-dynamic-text '*gmk-ui-p1-time-ent* (list (+ ux-l (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 3.6)) 0.0) "0.00s" (* *gmk-cell* 0.3) 8) (gmk:draw-text (list (+ ux-l (* *gmk-cell* 1.0)) (- mdim (* *gmk-cell* 4.1)) 0.0) "Total Time:" (* *gmk-cell* 0.3) 8) (gmk:update-dynamic-text '*gmk-ui-p1-tot-ent* (list (+ ux-l (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 4.1)) 0.0) "0.00s" (* *gmk-cell* 0.3) 8) (gmk:draw-line (list (+ ux-l (* *gmk-cell* 0.4)) (- mdim (* *gmk-cell* 4.7)) 0.0) (list (- ux-r (* *gmk-cell* 0.4)) (- mdim (* *gmk-cell* 4.7)) 0.0) 8) (setq *gmk-ui-p2-icon-ent* (gmk:draw-solid-circle (list (+ ux-l (* *gmk-cell* 1.0)) (- mdim (* *gmk-cell* 5.5)) 0.0) *gmk-radius* *gmk-p2-color*)) (setq *gmk-ui-p2-name-ent* (gmk:draw-text (list (+ ux-l (* *gmk-cell* 1.8)) (- mdim (* *gmk-cell* 5.65)) 0.0) "Local AI" (* *gmk-cell* 0.4) *gmk-p2-color*)) (gmk:draw-text (list (+ ux-l (* *gmk-cell* 1.0)) (- mdim (* *gmk-cell* 6.4)) 0.0) "Think Time:" (* *gmk-cell* 0.3) 8) (gmk:update-dynamic-text '*gmk-ui-p2-time-ent* (list (+ ux-l (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 6.4)) 0.0) "0.00s" (* *gmk-cell* 0.3) 8) (gmk:draw-text (list (+ ux-l (* *gmk-cell* 1.0)) (- mdim (* *gmk-cell* 6.9)) 0.0) "Total Time:" (* *gmk-cell* 0.3) 8) (gmk:update-dynamic-text '*gmk-ui-p2-tot-ent* (list (+ ux-l (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 6.9)) 0.0) "0.00s" (* *gmk-cell* 0.3) 8) (gmk:draw-line (list (+ ux-l (* *gmk-cell* 0.4)) (- mdim (* *gmk-cell* 7.5)) 0.0) (list (- ux-r (* *gmk-cell* 0.4)) (- mdim (* *gmk-cell* 7.5)) 0.0) 8) (gmk:draw-text (list (+ ux-l (* *gmk-cell* 0.8)) (- mdim (* *gmk-cell* 8.2)) 0.0) "RECENT MOVES:" (* *gmk-cell* 0.35) 8) (setq *gmk-history* nil) (gmk:draw-line (list (+ ux-l (* *gmk-cell* 0.4)) (* *gmk-cell* 1.4) 0.0) (list (- ux-r (* *gmk-cell* 0.4)) (* *gmk-cell* 1.4) 0.0) 8) (gmk:draw-centered-text (list cx (* *gmk-cell* 0.8) 0.0) "Powered by CadCade.com" (* *gmk-cell* 0.2) 8) (gmk:draw-centered-text (list cx (* *gmk-cell* 0.3) 0.0) "Code by Vico" (* *gmk-cell* 0.2) 8) ) (defun gmk:format-move (x y) (strcat (chr (+ 65 x)) (itoa (1+ y)))) (defun gmk:add-history (p mstr / c pt i mdim ux-l) (setq *gmk-history* (cons (list p mstr) *gmk-history*)) (if (> (length *gmk-history*) 6) (setq *gmk-history* (reverse (cdr (reverse *gmk-history*)))) ) (setq mdim (* (1- *gmk-size*) *gmk-cell*) ux-l (+ mdim (* *gmk-cell* 2.5)) i 0) (foreach item *gmk-history* (setq c (if (= (car item) 1) *gmk-p1-color* *gmk-p2-color*) pt (list (+ ux-l (* *gmk-cell* 1.2)) (- mdim (* *gmk-cell* (+ 8.8 (* i 0.55)))) 0.0)) (gmk:update-dynamic-text (read (strcat "*gmk-ui-hist-" (itoa (1+ i)) "*")) pt (cadr item) (* *gmk-cell* 0.3) c) (setq i (1+ i)) ) ) (defun gmk:update-hover (x y / pt el) (if (and (>= x 0) (< x *gmk-size*) (>= y 0) (< y *gmk-size*) (zerop (gmk:get-cell x y))) (progn (setq pt (list (* x *gmk-cell*) (* y *gmk-cell*) 0.0)) (if (or (not *gmk-hover-ent*) (not (entget *gmk-hover-ent*))) (setq *gmk-hover-ent* (entmakex (list '(0 . "CIRCLE") (cons 8 *gmk-layer*) (cons 10 pt) (cons 40 *gmk-radius*) (cons 62 *gmk-p1-color*)))) (progn (setq el (entget *gmk-hover-ent*)) (entmod (subst (cons 10 pt) (assoc 10 el) el)) ) ) ) (gmk:clear-hover) ) ) (defun gmk:clear-hover () (if *gmk-hover-ent* (progn (vl-catch-all-apply 'entdel (list *gmk-hover-ent*)) (setq *gmk-hover-ent* nil) ) ) ) (defun gmk:cleanup () (gmk:clear-hover) (setq *gmk-entities* nil *gmk-hover-ent* nil *gmk-board* nil *gmk-ui-p1-time-ent* nil *gmk-ui-p1-tot-ent* nil *gmk-ui-p2-time-ent* nil *gmk-ui-p2-tot-ent* nil *gmk-ui-p1-icon-ent* nil *gmk-ui-p1-name-ent* nil *gmk-ui-p2-icon-ent* nil *gmk-ui-p2-name-ent* nil *gmk-ui-hist-1* nil *gmk-ui-hist-2* nil *gmk-ui-hist-3* nil *gmk-ui-hist-4* nil *gmk-ui-hist-5* nil *gmk-ui-hist-6* nil) ) ;;; --- DCL UI --- (defun gmk:show-lobby-dialog (/ fn f id res) (setq fn (vl-filename-mktemp "gmk_lobby.dcl") f (open fn "w")) (foreach str '( "gmk_lobby : dialog { label=\"CadCade.com Lobby\"; width=40;" " : spacer { height=0.5; }" " : text { label=\"Select Game Mode:\"; alignment=centered; font=\"bold\"; }" " : spacer { height=1; }" " : column { alignment=centered; fixed_width=true;" " : button { key=\"btn_match\"; label=\"Online Matchmaking\"; width=28; fixed_width=true; is_enabled=false; }" " : spacer { height=0.2; }" " : button { key=\"btn_watch\"; label=\"Spectate Mode\"; width=28; fixed_width=true; is_enabled=false; }" " : spacer { height=0.2; }" " : button { key=\"btn_local\"; label=\"Local PvE (vs AI)\"; width=28; fixed_width=true; is_default=true; }" " }" " : spacer { height=1; }" " : button { key=\"btn_quit\"; label=\"Exit Game\"; is_cancel=true; width=12; alignment=centered; }" "}") (write-line str f) ) (close f) (setq id (load_dialog fn)) (if (new_dialog "gmk_lobby" id) (progn (action_tile "btn_local" "(done_dialog 1)") (action_tile "btn_match" "(done_dialog 2)") (action_tile "btn_watch" "(done_dialog 3)") (action_tile "btn_quit" "(done_dialog 0)") (setq res (start_dialog)) ) (setq res 0) ) (unload_dialog id) (vl-file-delete fn) res ) (defun gmk:show-gameover-dialog (msg / fn f id res) (gmk:clear-hover) (setq fn (vl-filename-mktemp "gmk_go.dcl") f (open fn "w")) (foreach str (list "gmk_go : dialog { label=\"Match Complete\";" " : spacer { height=0.5; }" (strcat " : text { label=\"" msg "\"; alignment=centered; font=\"bold\"; }") " : spacer { height=1; }" " : row { alignment=centered; fixed_width=true;" " : button { key=\"btn_next\"; label=\"Play Again\"; is_default=true; width=16; fixed_width=true; }" " : button { key=\"btn_quit\"; label=\"End Match\"; is_cancel=true; width=16; fixed_width=true; }" " }" "}") (write-line str f) ) (close f) (setq id (load_dialog fn)) (if (new_dialog "gmk_go" id) (progn (action_tile "btn_next" "(done_dialog 1)") (action_tile "btn_quit" "(done_dialog 0)") (setq res (start_dialog)) ) ) (unload_dialog id) (vl-file-delete fn) res ) (defun gmk:show-cleanup-dialog (/ fn f id res) (gmk:clear-hover) (setq fn (vl-filename-mktemp "gmk_cl.dcl") f (open fn "w")) (foreach str '( "gmk_cl : dialog { label=\"Exit Game\";" " : spacer { height=0.5; }" " : text { label=\"Do you want to keep the board on the screen?\"; alignment=centered; }" " : text { label=\"(Kept entities will become standard CAD objects)\"; alignment=centered; color=8; }" " : spacer { height=1; }" " : row { alignment=centered; fixed_width=true;" " : button { key=\"btn_keep\"; label=\"Keep Board\"; is_default=true; width=14; fixed_width=true; }" " : button { key=\"btn_clean\"; label=\"Clean Up\"; is_cancel=true; width=14; fixed_width=true; }" " }" "}") (write-line str f) ) (close f) (setq id (load_dialog fn)) (if (new_dialog "gmk_cl" id) (progn (action_tile "btn_keep" "(done_dialog 1)") (action_tile "btn_clean" "(done_dialog 0)") (setq res (start_dialog)) ) ) (unload_dialog id) (vl-file-delete fn) res ) ;;; --- Core Math & AI --- (defun gmk:get-time-seconds () (* 86400.0 (getvar "DATE"))) (defun gmk:count-continuous (x y dx dy p / cnt blk cx cy px py) (setq cnt 0 blk 0 cx (+ x dx) cy (+ y dy) px x py y) (while (and (>= cx 0) (< cx *gmk-size*) (>= cy 0) (< cy *gmk-size*) (= (gmk:get-cell cx cy) p)) (setq px cx py cy cnt (1+ cnt) cx (+ cx dx) cy (+ cy dy)) ) (if (or (< cx 0) (>= cx *gmk-size*) (< cy 0) (>= cy *gmk-size*) (/= (gmk:get-cell cx cy) 0)) (setq blk 1) ) (list cnt blk px py) ) (defun gmk:check-win (x y p / dirs d r1 r2 tot win) (setq dirs '((1 0) (0 1) (1 1) (1 -1)) win nil) (foreach d dirs (setq r1 (gmk:count-continuous x y (car d) (cadr d) p) r2 (gmk:count-continuous x y (- (car d)) (- (cadr d)) p) tot (+ 1 (car r1) (car r2))) (if (>= tot 5) (setq win (list (nth 2 r2) (nth 3 r2) (nth 2 r1) (nth 3 r1))) ) ) win ) (defun gmk:eval-dir (x y dx dy p / r1 r2 cnt blk) (setq r1 (gmk:count-continuous x y dx dy p) r2 (gmk:count-continuous x y (- dx) (- dy) p) cnt (+ 1 (car r1) (car r2)) blk (+ (cadr r1) (cadr r2))) (cond ((>= cnt 5) 1000000) ; Win ((and (= cnt 4) (= blk 0)) 100000) ; Open 4 ((and (= cnt 4) (= blk 1)) 10000) ; Closed 4 ((and (= cnt 3) (= blk 0)) 5000) ; Open 3 ((and (= cnt 3) (= blk 1)) 50) ; Closed 3 ((and (= cnt 2) (= blk 0)) 100) ; Open 2 ((and (= cnt 2) (= blk 1)) 5) ; Closed 2 (t 1) ) ) (defun gmk:eval-point (x y p / dirs tot d) (setq dirs '((1 0) (0 1) (1 1) (1 -1)) tot 0) (foreach d dirs (setq tot (+ tot (gmk:eval-dir x y (car d) (cadr d) p))) ) tot ) (defun gmk:ai-move (/ bx by max-s x y s1 s2 cbias tot) (setq max-s -1 bx 7 by 7 x 0) (while (< x *gmk-size*) (setq y 0) (while (< y *gmk-size*) (if (zerop (gmk:get-cell x y)) (progn (setq s1 (gmk:eval-point x y 1) s2 (gmk:eval-point x y 2) ;; Center bias using inverse Manhattan distance to (7,7). Max value ~14, Min 0. cbias (- 14.0 (+ (abs (- x 7)) (abs (- y 7)))) tot (+ s2 (* s1 1.2) (* cbias 0.5))) (if (> tot max-s) (setq max-s tot bx x by y)) ) ) (setq y (1+ y)) ) (setq x (1+ x)) ) (list bx by) ) ;;; --- Main App Loop --- (defun c:GOMOKU (/ *error* o-cmd o-osm run md pa gr pt spt cx cy am win r st dt uxl mdim ccode) (defun *error* (msg) (gmk:purge-board) (gmk:cleanup) (if o-cmd (setvar "CMDECHO" o-cmd)) (if o-osm (setvar "OSMODE" o-osm)) (if (not (wcmatch (strcase msg) "*QUIT*,*CANCEL*,*BREAK*")) (princ (strcat "\n[Gomoku] Error: " msg)) (princ "\n[Gomoku] Match aborted by user.") ) (princ) ) (setq o-cmd (getvar "CMDECHO") o-osm (getvar "OSMODE")) (setvar "CMDECHO" 0) (setvar "OSMODE" 32) (setq mdim (* (1- *gmk-size*) *gmk-cell*) uxl (+ mdim (* *gmk-cell* 2.5)) run T) (while run (setq md (gmk:show-lobby-dialog)) (cond ((= md 0) (setq run nil)) ((= md 1) (setq pa T) (while pa (princ "\n[Gomoku] Initializing Sandbox Board...") (gmk:ensure-layer) (gmk:purge-board) (gmk:cleanup) (setq *gmk-board* '()) (repeat (* *gmk-size* *gmk-size*) (setq *gmk-board* (cons 0 *gmk-board*))) (setq *gmk-p1-total* 0.0 *gmk-p2-total* 0.0) (gmk:draw-board) (command "_.ZOOM" "_W" (list (* -2 *gmk-cell*) (* -2 *gmk-cell*)) (list (+ uxl (* *gmk-cell* 9.5)) (+ mdim (* *gmk-cell* 2.0)))) (princ "\n[Gomoku] Game Start! You are Red. Click to place your piece. Press ESC or Right-Click to exit.") (setq *gmk-active* T st (gmk:get-time-seconds)) (gmk:set-turn 1) (while *gmk-active* (setq gr (grread T 15 0) ccode (car gr) pt (cadr gr)) (cond ;; Hover ((= ccode 5) (if (setq spt (osnap pt "_int")) (setq pt spt)) (setq cx (fix (+ (/ (car pt) *gmk-cell*) 0.5)) cy (fix (+ (/ (cadr pt) *gmk-cell*) 0.5))) (gmk:update-hover cx cy) (setq dt (- (gmk:get-time-seconds) st)) (gmk:update-dynamic-text '*gmk-ui-p1-time-ent* (list (+ uxl (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 3.6)) 0.0) (strcat (rtos dt 2 2) "s") (* *gmk-cell* 0.3) 8) ) ;; Click ((= ccode 3) (if (setq spt (osnap pt "_int")) (setq pt spt)) (setq cx (fix (+ (/ (car pt) *gmk-cell*) 0.5)) cy (fix (+ (/ (cadr pt) *gmk-cell*) 0.5))) (if (and (>= cx 0) (< cx *gmk-size*) (>= cy 0) (< cy *gmk-size*) (zerop (gmk:get-cell cx cy))) (progn (setq dt (- (gmk:get-time-seconds) st) *gmk-p1-total* (+ *gmk-p1-total* dt)) (gmk:update-dynamic-text '*gmk-ui-p1-time-ent* (list (+ uxl (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 3.6)) 0.0) (strcat (rtos dt 2 2) "s") (* *gmk-cell* 0.3) 8) (gmk:update-dynamic-text '*gmk-ui-p1-tot-ent* (list (+ uxl (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 4.1)) 0.0) (strcat (rtos *gmk-p1-total* 2 2) "s") (* *gmk-cell* 0.3) 8) (gmk:set-cell cx cy 1) (gmk:draw-solid-circle (list (* cx *gmk-cell*) (* cy *gmk-cell*)) *gmk-radius* *gmk-p1-color*) (gmk:clear-hover) (gmk:add-history 1 (strcat "Red: " (gmk:format-move cx cy))) (if (setq win (gmk:check-win cx cy 1)) (progn (gmk:add-history 1 "Red: MATCH WIN!") (gmk:draw-thick-line (list (* (car win) *gmk-cell*) (* (cadr win) *gmk-cell*)) (list (* (nth 2 win) *gmk-cell*) (* (nth 3 win) *gmk-cell*)) *gmk-win-color* (* *gmk-cell* 0.4)) (redraw) (setq r (gmk:show-gameover-dialog "Victory! You defeated the Local AI.") *gmk-active* nil pa (= r 1)) ) (if (not (member 0 *gmk-board*)) (progn (gmk:add-history 1 "SYS: DRAW MATCH") (redraw) (setq r (gmk:show-gameover-dialog "Stalemate! The board is full.") *gmk-active* nil pa (= r 1)) ) (progn (gmk:set-turn 2) (princ "\n[Gomoku] AI is thinking...") (setq st (gmk:get-time-seconds) am (gmk:ai-move) dt (- (gmk:get-time-seconds) st) *gmk-p2-total* (+ *gmk-p2-total* dt)) (gmk:update-dynamic-text '*gmk-ui-p2-time-ent* (list (+ uxl (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 6.4)) 0.0) (strcat (rtos dt 2 3) "s") (* *gmk-cell* 0.3) 8) (gmk:update-dynamic-text '*gmk-ui-p2-tot-ent* (list (+ uxl (* *gmk-cell* 4.1)) (- mdim (* *gmk-cell* 6.9)) 0.0) (strcat (rtos *gmk-p2-total* 2 2) "s") (* *gmk-cell* 0.3) 8) (gmk:set-cell (car am) (cadr am) 2) (gmk:draw-solid-circle (list (* (car am) *gmk-cell*) (* (cadr am) *gmk-cell*)) *gmk-radius* *gmk-p2-color*) (gmk:add-history 2 (strcat "Blue: " (gmk:format-move (car am) (cadr am)))) (if (setq win (gmk:check-win (car am) (cadr am) 2)) (progn (gmk:add-history 2 "Blue: MATCH WIN!") (gmk:draw-thick-line (list (* (car win) *gmk-cell*) (* (cadr win) *gmk-cell*)) (list (* (nth 2 win) *gmk-cell*) (* (nth 3 win) *gmk-cell*)) *gmk-win-color* (* *gmk-cell* 0.4)) (redraw) (setq r (gmk:show-gameover-dialog "Defeat! The Local AI claims victory.") *gmk-active* nil pa (= r 1)) ) (if (not (member 0 *gmk-board*)) (progn (gmk:add-history 2 "SYS: DRAW MATCH") (redraw) (setq r (gmk:show-gameover-dialog "Stalemate! The board is full.") *gmk-active* nil pa (= r 1)) ) (progn (gmk:set-turn 1) (setq st (gmk:get-time-seconds)) (princ "\n[Gomoku] Your turn.") ) ) ) ) ) ) ) ) ) ;; Exit (Right Click or Enter/Space) ((member ccode '(11 2 25)) (gmk:clear-hover) (setq *gmk-active* nil pa nil) ) ;; Catch-all for robust input handling (e.g. middle mouse pan) (t nil) ) ) ) ) ) ) (princ "\n[Gomoku] Game session ended.") (setq r (gmk:show-cleanup-dialog)) (if (= r 1) (progn (princ " Board entities kept as standard CAD objects.") (gmk:cleanup)) (progn (princ " Cleaning up board...") (gmk:purge-board) (gmk:cleanup)) ) (setvar "CMDECHO" o-cmd) (setvar "OSMODE" o-osm) (princ "\n[Gomoku] Type GOMOKU to enter Lobby.") (princ) ) (princ "\n[CadCade] Gomoku Engine (Local PvE Edition) v0.3 loaded. Type GOMOKU to play.") (princ)
    2 points
  13. I think your overlooking the AUTOLOAD function, yes in my Autoload.lsp loads on startup there are 37 defuns, most are very small like DTR, RTD, then there are 38 Autoload's the way Autoload works is you type a command and a lisp program is loaded, so you limit how many lisps are loaded on startup, In the pop there is (load "myprogram") "nowrunit" so again only loaded when you need the program. (autoload "COPY0" '("COPY0")) (autoload "COPYCOMMAND" '("ZZZ")) (autoload "COVER" '("COVER"))
    2 points
  14. Whatever store there is on the internet will suffer similarly from Al generated code, a store where code is uploaded quickly will just become very large, lose its value when there are too many resources to choose all doing something very similar. I think I prefer the forum format (probably always will do), that in creating an answer to the problems asked there is some peer review going on, explanation as what does what and the ability to ask for explanations or how to modify a code supplied to a more unique solution - which is all in itself a great learnign tool
    2 points
  15. @indiancad @Javier Longa there may be a way of using Array if a new UCS OB is used to orient the grid. In the changed rotation need to rotate the block as well. A question @Javier Longa " since I have no programming knowledge whatsoever" then how did you come by the code ? It is considered good manners to acknowledge who wrote the code, if known, or mention Author unknown.
    2 points
  16. There is also PyInstaller to create an executable. PyInstaller Manual — PyInstaller 6.21.0 documentation pip install -U pyinstaller pyinstaller your_program.py
    2 points
  17. The Ax Space. Ax is the wrapper for ActiveX as described here https://help.autodesk.com/view/OARX/2025/ENU/?guid=GUID-A809CD71-4655-44E2-B674-1FE200B9FE30 It’s going to look familiar if you’ve done any VBA or Visual Lisp. The big difference is that all Points, Vectors, Matrices, use AcGe classes instead of variants. from pyrx import Ap, Ax, Db, Ed, Ge, Rx @Ap.Command() def doitx1(): # get the application, document, and modelspace using ActiveX automation axApp = Ap.Application.acadApplication() axDoc = axApp.activeDocument() axModel = axDoc.modelSpace() # add a lines to modelspace, then access it properties # use Ge.Point3d class instead of a variant axLine1 = axModel.addLine(Ge.Point3d(0, 0, 0), Ge.Point3d(100, 100, 0)) axLine2 = axModel.addLine(Ge.Point3d(0, 100, 0), Ge.Point3d(100, 0, 0)) interdata = axLine1.intersectWith(axLine2, Ax.AcExtendOption.acExtendNone) if len(interdata) == 0: print("\nDoes not intersect:") return axCircle = axModel.addCircle(interdata[0], 10) axCircle.setColor(Ax.AcColor.acCyan) #scale and rotate xform = Ge.Matrix3d.scaling(2,axCircle.center()) xform *= Ge.Matrix3d.rotation(0.7854, Ge.Vector3d.kZAxis, axCircle.center()) axLine1.transformBy(xform) axLine2.transformBy(xform)
    2 points
  18. Casting objects. Every database object has a static cast method, it is your responsibility to check the type from pyrx import Ap, Ax, Db, Ed, Ge, Rx import traceback @Ap.Command() def doitx3(): try: # Get ActiveX application instance axApp = Ap.Application.acadApplication() axDoc = axApp.activeDocument() axUtil = axDoc.utility() # Prompt user to pick an entity axEnt, pnt = axUtil.getEntity("\nPick a line") # Verify that the picked entity is a LINE if axEnt.objectName() != "AcDbLine": raise RuntimeError("oops!: ") # Cast to AcadLine axLine = Ax.AcadLine.cast(axEnt) # change color to green (RGB: 0, 255, 0) axLine.setTrueColor(Ax.AcadAcCmColor(0, 255, 0)) # Print information about the selected entity print(axEnt.objectName(), pnt) except Exception as err: traceback.print_exception(err)
    2 points
  19. here's an example of creating a table and selecting a sub region from pyrx import Ap, Ax, Db, Ed, Ge, Rx import traceback @Ap.Command() def doitx2(): try: axApp = Ap.Application.acadApplication() axDoc = axApp.activeDocument() axModel = axDoc.modelSpace() ps , point = Ed.Editor.getPoint("\nPick table location: ") if ps != Ed.PromptStatus.eOk: raise RuntimeError("oof {}:".format(ps)) # Creates a table with 7 rows and 5 columns axTable = axModel.addTable(point, 7, 5, 1, 5) #fill up the table for col in range(axTable.columns()): for row in range(2, axTable.rows()): axTable.setText(row, col, "{},{}".format(row, col)) #get the cell extents, is a list of 3d points cex11 = axTable.cellExtents(1, 1, False) cex43 = axTable.cellExtents(4, 3, False) # Create a selection region using ActiveX selection methods sssub = axTable.selectSubRegion( cex11[0], cex43[3], Ge.Vector3d.kZAxis, Ge.Vector3d.kXAxis, Ax.AcSelectType.acTableSelectCrossing, False, ) # Apply the selection to the table using ActiveX selection methods axTable.setSubSelection(*sssub) # Perform hit testing and geometric calculations using ActiveX services # Ge space makes it easier to do math pnt = cex11[0] + (cex43[3] - cex11[0]) * 0.5 bhit, row, col = axTable.hitTest(pnt, Ge.Vector3d.kZAxis) if(bhit): # Modify cell properties using ActiveX methods axTable.setCellTextHeight(row, col, 0.8) axTable.setText(row, col, "Bingo") # since were open source we can add stuff like suppost html colors axTable.setCellBackgroundColor(row, col, Ax.AcadAcCmColor("#228B22")) except Exception: print(traceback.format_exc())
    2 points
  20. Start here https://github.com/CEXT-Dan/PyRx#python-for-autocad 1, watch the short YouTube video and download Python 3.14 from the link provided 2, download in install VS Code, install python extension pack 3, try out some of the samples, https://github.com/CEXT-Dan/PyRx/tree/main/PySamples
    2 points
  21. There are some items in the database, where the only option is to use entGet, I.e. ACAD_FIELDLIST is not exposed to ARX See: https://www.cadtutor.net/forum/topic/99220-python-change-precision-of-all-fields-in-a-drawing/
    2 points
  22. And top it off with entMake from pyrx import Ap, Db, Ed, Ge @Ap.Command() def doit5() -> None: Db.Core.entMake([(0 , "LINE"),(10, Ge.Point3d(0, 0, 0)),(11, Ge.Point3d(100, 100, 0))])
    2 points
  23. This might be better example, maybe similar to the COND expression? from pyrx import Ap, Db, Ed, Ge @Ap.Command() def doit4() -> None: ps, id, _ = Ed.Editor.entSel("\nPick it: ", Db.Line.desc()) if ps != Ed.PromptStatus.eOk: raise RuntimeError("oof {}:".format(ps)) #get the result buffer rb = Db.Core.entGet(id) # Process the result buffer using structural pattern matching updated_rb = [] for item in rb: match item: case (10, _): updated_rb.append((10, Ge.Point3d(0, 0, 0))) case (11, _): updated_rb.append((11, Ge.Point3d(100, 100, 0))) case _: updated_rb.append(item) # Apply changes back to the AutoCAD database Db.Core.entMod(updated_rb)
    2 points
  24. In Python, tuples are immutable, so if we want to modify the list, you have to replace it. In this sample we modify the start and end of a line from pyrx import Ap, Db, Ed, Ge @Ap.Command() def doit3() -> None: #entsel with a filter ps, id, _ = Ed.Editor.entSel("\nPick it: ", Db.Line.desc()) if ps != Ed.PromptStatus.eOk: raise RuntimeError("oof {}:".format(ps)) #get the result buffer rb = Db.Core.entGet(id) # Loop through the result buffer list and modify matching DXF codes for i, item in enumerate(rb): dxf_code = item[0] if dxf_code == 10: rb[i] = (10, Ge.Point3d(0, 0, 0)) elif dxf_code == 11: rb[i] = (11, Ge.Point3d(100, 100, 0)) # Apply changes back to the AutoCAD database Db.Core.entMod(rb)
    2 points
  25. In Python, there isn’t an assoc function, we can iterate through and search for a DXF code from pyrx import Ap, Db, Ed, Ge @Ap.Command() def doit1() -> None: # entsel with a filter ps, id, _ = Ed.Editor.entSel("\nPick it: ", Db.Line.desc()) if ps != Ed.PromptStatus.eOk: raise RuntimeError("oof {}:".format(ps)) # get the result buffer rb = Db.Core.entGet(id) # 1. Analog to Lisp: (assoc 8 rb) -> Returns the full pair (8, "LayerName") dxf_code, value = next((item for item in rb if item[0] == 8), None) # 2. Extract just the value safely if dxf_code: print(f"The layer is: {value}")
    2 points
  26. those are attribute references of a block. Since vla-fieldcode only works on Text and mtext i used Lee Macs fieldcode instead https://www.lee-mac.com/fieldcode.html It should work on Blocks Attributereferences as well now. (defun c:change_prec (/ *error* acdoc undo-mark ss target index ename obj att field-string revised-string) (defun *error* (msg) (if undo-mark (progn (vl-catch-all-apply 'vla-EndUndoMark (list acdoc)) (setq undo-mark nil) ) ) (if (and msg (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) ) (princ (strcat "\nError: " msg)) ) (princ) ) (setq acdoc (vla-get-ActiveDocument (vlax-get-acad-object))) (prompt "\nSelect text, MText, or attributed blocks: ") (while (null (setq ss (ssget '((0 . "TEXT,MTEXT,INSERT"))))) (prompt "\nNo valid text, MText, or blocks selected.") ) (initget 1 "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Current") (setq target (getkword "\nTarget precision [0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/Current]: ")) (vla-StartUndoMark acdoc) (setq undo-mark T) (repeat (setq index (sslength ss)) (setq index (1- index) ename (ssname ss index) obj (vlax-ename->vla-object ename) ) (cond ;; Text or MText ((wcmatch (vla-get-ObjectName obj) "AcDbText,AcDbMText") (setq field-string (vla-FieldCode obj)) (if (and field-string (setq revised-string (replacePrecision field-string target)) ) (vla-put-TextString obj revised-string) ) ) ;; Block reference with editable attributes ((vlax-method-applicable-p obj 'GetAttributes) (foreach att (vlax-invoke obj 'GetAttributes) (setq field-string (LM:fieldcode (vlax-vla-object->ename att))) (if (and field-string (setq revised-string (replacePrecision field-string target)) ) (vla-put-TextString att revised-string) ) ) ) ) ) (vla-EndUndoMark acdoc) (setq undo-mark nil) (vla-Regen acdoc acActiveViewport) (princ) ) (defun replacePrecision (fieldStr target / nbs prec_source return) (setq nbs 0) (cond ((vl-string-search "%<\\" fieldStr nbs) (while nbs (if (setq nbs (vl-string-search "%pr" fieldStr (setq tmp_nbs nbs))) (setq prec_source (itoa (atoi (substr fieldStr (+ nbs 4) 2))) fieldStr (vl-string-subst (if (eq target "Current") (strcat "%pr" (itoa (getvar "LUPREC"))) (strcat "%pr" target) ) (strcat "%pr" prec_source) fieldStr tmp_nbs ) nbs (1+ nbs) ) ) ) (setq return fieldStr) ) ) return ) ;; Field Code - Lee Mac ;; Returns the field expression associated with an entity (defun LM:fieldcode ( ent / replacefield replaceobject fieldstring enx ) (defun replacefield ( str enx / ent fld pos ) (if (setq pos (vl-string-search "\\_FldIdx" (setq str (replaceobject str enx)))) (progn (setq ent (assoc 360 enx) fld (entget (cdr ent)) ) (strcat (substr str 1 pos) (replacefield (fieldstring fld) fld) (replacefield (substr str (1+ (vl-string-search ">%" str pos))) (cdr (member ent enx))) ) ) str ) ) (defun replaceobject ( str enx / ent pos ) (if (setq pos (vl-string-search "ObjIdx" str)) (strcat (substr str 1 (+ pos 5)) " " (LM:ObjectID (vlax-ename->vla-object (cdr (setq ent (assoc 331 enx))))) (replaceobject (substr str (1+ (vl-string-search ">%" str pos))) (cdr (member ent enx))) ) str ) ) (defun fieldstring ( enx / itm ) (if (setq itm (assoc 3 enx)) (strcat (cdr itm) (fieldstring (cdr (member itm enx)))) (cond ((cdr (assoc 2 enx))) ("")) ) ) (if (and (wcmatch (cdr (assoc 0 (setq enx (entget ent)))) "TEXT,MTEXT,ATTRIB,MULTILEADER,*DIMENSION") (setq enx (cdr (assoc 360 enx))) (setq enx (dictsearch enx "ACAD_FIELD")) (setq enx (dictsearch (cdr (assoc -1 enx)) "TEXT")) ) (replacefield (fieldstring enx) enx) ) ) ;; ObjectID - Lee Mac ;; Returns a string containing the ObjectID of a supplied VLA-Object ;; Compatible with 32-bit & 64-bit systems (defun LM:ObjectID ( obj ) (eval (list 'defun 'LM:ObjectID '( obj ) (if (and (vl-string-search "64" (getenv "PROCESSOR_ARCHITECTURE")) (vlax-method-applicable-p (vla-get-utility (LM:acdoc)) 'getobjectidstring) ) (list 'vla-getobjectidstring (vla-get-utility (LM:acdoc)) 'obj ':vlax-false) '(itoa (vla-get-objectid obj)) ) ) ) (LM:ObjectID obj) ) ;; Active Document - Lee Mac ;; Returns the VLA Active Document Object (defun LM:acdoc nil (eval (list 'defun 'LM:acdoc 'nil (vla-get-activedocument (vlax-get-acad-object)))) (LM:acdoc) )
    2 points
  27. Here is another for allowing explode... Solved: Re: Allow exploding outside the block editor - Autodesk Community Are these drawings from AutoCAD Civil 3D or some other CAD? (defun c:AllBlkYHtchN (/ ss i ent) (vl-load-com) ;;-------------------------------------------------- ;; Set Allow Exploding = Yes for all block definitions ;;-------------------------------------------------- (vlax-map-collection (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object))) '(lambda (blk) (if (vlax-property-available-p blk 'Explodable) (vlax-put-property blk 'Explodable :vlax-true) ) ) ) ;;-------------------------------------------------- ;; Set all hatches Annotative = No ;;-------------------------------------------------- (if (setq ss (ssget "_X" '((0 . "HATCH")))) (progn (setq i 0) (repeat (sslength ss) (setq ent (ssname ss i)) (vl-catch-all-apply '(lambda () (setpropertyvalue ent "Annotative" 0) ) ) (setq i (1+ i)) ) ) ) (princ (strcat "\nUpdated " (itoa (if ss (sslength ss) 0)) " hatch(es). All explodable block definitions enabled." ) ) (princ) ) Do you need nested hatches and blocks, dynamic and/or anonymous blocks?
    2 points
  28. Just a comment in Bricscad V25 the setpropertyvalue does not work, the get does work. One of those odd bugs
    2 points
  29. Hello everyone, I've just finished developing MPL (Multi Plot Layout/Model), a free AutoLISP tool that helps you batch print drawings in AutoCAD. Main Features MPL includes all the essential functions found in other batch plotting LISP tools, plus several improvements: Smart and intuitive DCL interface Merge all plots into a single PDF (no additional software or virtual PDF printer required) Automatically open the output file or folder after plotting, with customizable file name and save location Zone-based plotting – organize messy drawings and print them in the correct order based on defined zones Batch plot multiple layouts – rearrange the printing order of layout tabs without dragging the tabs at the bottom of AutoCAD Print Preview mode Add Selection feature – easily add more drawings to the current print list without starting over Export/Import plot configuration for quick reuse Download and user guide: https://drive.google.com/drive/folders/1WwmkXVgHFWTy8zIhARJYCH_G2OC2A_3D?usp=sharing Feel free to download it, give it a try, and let me know your feedback. Your suggestions will help improve MPL even further! Update for the BatchPlot.exe installation version. Free download: https://drive.google.com/drive/folders/16RZS69S2tzZZzFnYW0H6QNQ3klwxB2C6?usp=drive_link
    1 point
  30. Using AutoCAD's default Find & Replace command often causes the program to freeze or lag for a while when opening or closing the search dialog—especially with large drawings. I'm sharing a lightweight and fast Find & Replace LISP that helps reduce lag and waiting time compared to AutoCAD's built-in Find & Replace command. 2026/07/18 – Version V2 Added a new feature: run the FD command again to toggle the Find panel on/off. Command Name: FDP (FD) +Automatic Installation (Load Once Only) Step 1: Extract FD.zip. Step 2: Run setup.bat to install the files to drive C:. Step 3: Open APPLOAD (AP) and browse to: C:\FD Step 4: Load the file Load_FD.lsp. (Optional) Add it to the Startup Suite so it loads automatically whenever AutoCAD starts. +Manual Installation (Must Be Loaded Every Session) Load FD.vlx using APPLOAD (AP). Load the DLL using the NETLOAD command. Be sure to read the notes below to avoid loading the wrong DLL. !!_!! Important AutoCAD 2018 – Early 2024: Use FD_Palette_v15.dll. Late AutoCAD 2024 – 2025 / 2026 / 2027: Use FD_Palette_v16.dll. If Windows Blocks the DLL Windows may block downloaded DLL files, which can cause loading errors. To fix this: Right-click the .dll file. Open Properties. Check Unblock (if available). Click OK, then load the DLL again. Link: https://www.cadviet.com/forum/index.php?app=forums&module=forums&controller=topic&id=213381&app=forums&module=forums&id=213381
    1 point
  31. Very professional requirements, especially when dealing with large multi-image sets, where it is necessary to quickly locate a known text or object, sometimes having to go through all the drawings. In fact, you can try VC distribution. It can mount DLL and make two bundles for different versions(like VLX+DLLV1=Budl1, VLX+DLLV2=Budl2). The user can pull the collection to achieve configuration-free.
    1 point
  32. yes, same in acad. "Fehler: ActiveX-Server gab folgenden Fehler zurück: unbekannter Name: Annotative" There is no such property wich is why i would use setpropertyvalue for acad. As for Bricscad idk how one would do that
    1 point
  33. https://www.cadviet.com/forum/index.php?app=forums&module=forums&controller=topic&id=213313
    1 point
  34. Following on from EnM4st3r Code. I have added a function to create the "%pr" if none exists. I had this happen when the Field is created using "Current Precision" as the string does not contain the required %pr by default. (defun c:ChangePrec (/ *error* acdoc undo-mark ss target index ename obj att field-string revised-string) (defun *error* (msg) (if undo-mark (progn (vl-catch-all-apply 'vla-EndUndoMark (list acdoc)) (setq undo-mark nil) ) ) (if (and msg (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) ) (princ (strcat "\nError: " msg)) ) (princ) ) (setq acdoc (vla-get-ActiveDocument (vlax-get-acad-object))) (prompt "\nSelect text, MText, or Attributed Blocks: ") (while (null (setq ss (ssget '((0 . "TEXT,MTEXT,INSERT"))))) (prompt "\nNo valid text, MText, or blocks selected.") ) (initget 1 "0 1 2 3 4 5 6 7 Current") (setq target (getkword "\nTarget precision [0/1/2/3/4/5/6/7/Current]: ")) (vla-StartUndoMark acdoc) (setq undo-mark T) (repeat (setq index (sslength ss)) (setq index (1- index) ename (ssname ss index) obj (vlax-ename->vla-object ename) ) (cond ;; Text or MText ((wcmatch (vla-get-ObjectName obj) "AcDbText,AcDbMText") (setq field-string (vla-FieldCode obj)) (if (and field-string (setq revised-string (replacePrecision field-string target)) ) (vla-put-TextString obj revised-string) ) ) ;; Block reference with editable attributes ((vlax-method-applicable-p obj 'GetAttributes) (foreach att (vlax-invoke obj 'GetAttributes) (setq field-string (LM:fieldcode (vlax-vla-object->ename att))) (if (and field-string (setq revised-string (replacePrecision field-string target)) ) (vla-put-TextString att revised-string) ) ) ) ) ) (vla-EndUndoMark acdoc) (setq undo-mark nil) (vla-Regen acdoc acActiveViewport) (princ) ) (defun replacePrecision (fieldStr target / nbs tmp_nbs prec return fmtStart fmtEnd fmtString newPrec) (setq newPrec (strcat "%pr" (if (= target "Current") (itoa (getvar "LUPREC")) target ) ) ) ;; Existing precision -> replace it (if (setq nbs (vl-string-search "%pr" fieldStr)) (progn (while nbs (setq tmp_nbs nbs prec (itoa (atoi (substr fieldStr (+ nbs 4) 2))) fieldStr (vl-string-subst newPrec (strcat "%pr" prec) fieldStr tmp_nbs ) nbs (vl-string-search "%pr" fieldStr (+ tmp_nbs (strlen newPrec))) ) ) (setq return fieldStr) ) ;; No %pr found (progn ;; Existing format string? (if (setq fmtStart (vl-string-search "\\f \"" fieldStr)) ;; Append %pr to existing format string (progn (setq fmtStart (+ fmtStart 4) fmtEnd (vl-string-search "\"" fieldStr fmtStart) fmtString (substr fieldStr (1+ fmtStart) (- fmtEnd fmtStart)) ) (setq return (strcat (substr fieldStr 1 fmtStart) fmtString newPrec (substr fieldStr (1+ fmtEnd)) ) ) ) ;; No format string at all -> create one (if (setq fmtEnd (vl-string-search ">%" fieldStr)) (setq return (strcat (substr fieldStr 1 (1- fmtEnd)) " \\f \"" newPrec "\">%" ) ) ) ) ) ) return ) ;; Field Code - Lee Mac ;; Returns the field expression associated with an entity (defun LM:fieldcode ( ent / replacefield replaceobject fieldstring enx ) (defun replacefield ( str enx / ent fld pos ) (if (setq pos (vl-string-search "\\_FldIdx" (setq str (replaceobject str enx)))) (progn (setq ent (assoc 360 enx) fld (entget (cdr ent)) ) (strcat (substr str 1 pos) (replacefield (fieldstring fld) fld) (replacefield (substr str (1+ (vl-string-search ">%" str pos))) (cdr (member ent enx))) ) ) str ) ) (defun replaceobject ( str enx / ent pos ) (if (setq pos (vl-string-search "ObjIdx" str)) (strcat (substr str 1 (+ pos 5)) " " (LM:ObjectID (vlax-ename->vla-object (cdr (setq ent (assoc 331 enx))))) (replaceobject (substr str (1+ (vl-string-search ">%" str pos))) (cdr (member ent enx))) ) str ) ) (defun fieldstring ( enx / itm ) (if (setq itm (assoc 3 enx)) (strcat (cdr itm) (fieldstring (cdr (member itm enx)))) (cond ((cdr (assoc 2 enx))) ("")) ) ) (if (and (wcmatch (cdr (assoc 0 (setq enx (entget ent)))) "TEXT,MTEXT,ATTRIB,MULTILEADER,*DIMENSION") (setq enx (cdr (assoc 360 enx))) (setq enx (dictsearch enx "ACAD_FIELD")) (setq enx (dictsearch (cdr (assoc -1 enx)) "TEXT")) ) (replacefield (fieldstring enx) enx) ) ) ;; ObjectID - Lee Mac ;; Returns a string containing the ObjectID of a supplied VLA-Object ;; Compatible with 32-bit & 64-bit systems (defun LM:ObjectID ( obj ) (eval (list 'defun 'LM:ObjectID '( obj ) (if (and (vl-string-search "64" (getenv "PROCESSOR_ARCHITECTURE")) (vlax-method-applicable-p (vla-get-utility (LM:acdoc)) 'getobjectidstring) ) (list 'vla-getobjectidstring (vla-get-utility (LM:acdoc)) 'obj ':vlax-false) '(itoa (vla-get-objectid obj)) ) ) ) (LM:ObjectID obj) ) ;; Active Document - Lee Mac ;; Returns the VLA Active Document Object (defun LM:acdoc nil (eval (list 'defun 'LM:acdoc 'nil (vla-get-activedocument (vlax-get-acad-object)))) (LM:acdoc) )
    1 point
  35. I dont know if its my Bricscad V25 but this is what you get using; (vla-put-Annotative hobj :vlax-false) ; error : Automation Error. Property [ANNOTATIVE] not available Would appreciate to know if same in Acad etc.
    1 point
  36. What we have already just cleans up an incoming CAD file like unlocking all layers, color to ByLayer, Audit, PU, -PU(regapps), and changing the units. We use AutoCAD LT and I don't think we need to change nested hatches because the hatches I want to code are usually on the surface and include pavement, sidewalk, etc., and not in another block. I would want to include dynamic blocks to be able to be exploded as well since alot of utility symbols are dynamic blocks. I've never heard of anonymous blocks... (defun C:CLEANUP ( / allobjects hatchss i hobj hcount) (command "-layer" "unlock" "*" "") ; Unlocks all layers to make them editable (setq allobjects (ssget "_X" )) (command "_.CHPROP" allobjects "" "_color" "ByLAyer" "") ; Sets the color of all objects to ByLayer (command "_AUDIT" "Yes") (command "_PURGE" "Regapps" "*" "No") (command "_PURGE" "All" "*" "No") (setvar "lunits" 2) ; Set linear units to decimal (setvar "aunits" 0) ; Set angular units to decimal (command "_INSUNITS" "0") ; Specifies the drawing units as unitless ;; Turn off Annotative property on all hatch objects (vl-load-com) (setq hcount 0) (if (setq hatchss (ssget "_X" '((0 . "HATCH")))) (progn (setq i 0) (while (< i (sslength hatchss)) (setq hobj (vlax-ename->vla-object (ssname hatchss i))) (if (vlax-property-available-p hobj "Annotative") (progn (vla-put-Annotative hobj :vlax-false) (setq hcount (1+ hcount)) ) ) (setq i (1+ i)) ) ) ) (princ (strcat "\n" (itoa hcount) " hatch(es) set to non-annotative.")) (princ "\n\nFile has been cleaned.") ) (princ)
    1 point
  37. I have not played in python and I will give it a whirl.
    1 point
  38. Ahhh, I didn't see the link, I'll check it out later. Thanks, I'm sure it will help.
    1 point
  39. I have been testing the python code by @Danielm103 and it works really well, the only hiccup is that you need to install Python on your pc plus load a couple of extra python modules. It is very straight forward to install. A more advanced version could produce what you have as a result. It's a simple task to remove columns from the outputted table.
    1 point
  40. in autocad you could change Annotative to "No" using setpropertyvalue. For example: (setpropertyvalue ent "Annotative" 0)
    1 point
  41. @Danielm103 has updated the code and its running under Bricscad and is very impressive, I am sure the updated code will be posted here soon.
    1 point
  42. Have a look at this
    1 point
  43. seems ezdxf has this feature, so we can just use it. import wx from ezdxf.addons import acadctb from pyrx import Ap, Db, Ed def browse_for_ctb(): """Opens a native wxPython file dialog to browse for a CTB file.""" parent = wx.GetApp().GetTopWindow() with wx.FileDialog( parent, "Select AutoCAD CTB File", wildcard="CTB files (*.ctb)|*.ctb", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST, ) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return None return fileDialog.GetPath() @Ap.Command() def doit() -> None: ctb_path = browse_for_ctb() if not ctb_path: print("\nCommand cancelled.") return try: # 2. Parse the CTB using ezdxf ctb_data = acadctb.load(ctb_path) except Exception as e: print(f"\nFailed to parse CTB file: {str(e)}") return active_colors = [] for idx in range(1, 256): style = ctb_data[idx] # Keep the entry if it overrides lineweight or screening if style.lineweight >= 0 or style.screen < 100: # Store the index along with the full style object to access all 10 properties active_colors.append((idx, style)) if not active_colors: print("\nNo explicit pen styles overrides found in this CTB. Table skipped.") return db = Db.curDb() ps, insert_pt = Ed.Editor.getPoint("\nSpecify insertion point for the CTB table: ") if ps != Ed.PromptStatus.kNormal: return table = Db.Table() table.setDatabaseDefaults(db) # 10 headers corresponding to all the available style properties headers = [ "ACI Index", "Lineweight", "Screening", "Dither", "Phys Pen", "Virt Pen", "Linetype", "Adaptive LT", "Fill Style", "Style Index" ] total_rows = len(active_colors) + 2 # Title row + Header row + Data rows table.setSize(total_rows, len(headers)) table.setPosition(insert_pt) table.generateLayout() # Title Row Configurations table.setTextString(0, 0, f"Complete CTB Profile: {ctb_path.split('\\')[-1]}") # Header Row Configurations for col_idx, header_text in enumerate(headers): table.setTextString(1, col_idx, header_text) # Populate Data rows for row_idx, (idx, style) in enumerate(active_colors, start=2): lweight_str = f"{style.lineweight:.2f} mm" if style.lineweight >= 0 else "Use Object Value" table.setBackgroundColor(row_idx, 0, Db.Color(style.aci)) table.setContentColor(row_idx, 0, Db.Color(7)) table.setTextString(row_idx, 0, str(style.aci)) table.setTextString(row_idx, 1, lweight_str) table.setTextString(row_idx, 2, f"{style.screen}%") table.setTextString(row_idx, 3, "On" if style.dithering == 1 else "Off") table.setTextString(row_idx, 4, str(style.physical_pen_number)) table.setTextString(row_idx, 5, str(style.virtual_pen_number)) table.setTextString(row_idx, 6, str(style.linetype)) table.setTextString(row_idx, 7, "Yes" if style.adaptive_linetype == 1 else "No") table.setTextString(row_idx, 8, str(style.fill_style)) table.setTextString(row_idx, 9, str(style.index)) db.addToCurrentspace(table) print(f"\nSuccessfully generated a 10-column table with {len(active_colors)} CTB overrides.")
    1 point
  44. UPDATE: There has been a delay in the forum upgrade. I will make a further announcement in due course and let you know when it will be happening.
    1 point
  45. Here is a quick find from Google for setting all blocks as explodable: ;; Set every block as explodable ;; http://forums.augi.com/showthread.php?33008-Allow-block-exploding&highlight=explodable&pp=10 ;; posted by whdjr (defun c:eb () (vl-load-com) (vlax-map-collection (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)) ) '(lambda (x) (and (vlax-property-available-p x 'explodable) (eq (vlax-get-property x 'explodable) :vlax-false) (not (vlax-put-property x 'explodable :vlax-true)) ) ) ) )
    1 point
  46. Never have I needed Autosave. CTRL+S is just a habit now, even in non-AutoCAD programs. I also frequently Right-Click one of the Drawing Tabs and select Save All. I also know the difference between Qsave and Save and use both appropriately. (Note: they are the same in AutoCAD LT or used to be, I am not sure about newer LT versions) Saving your work is your job, not AutoCAD's. I wonder if a LISP with a reactor would be less troublesome for those that do not trust going without an Autosave? Or maybe this? A Better Autosave | AfraLISP Or... (command ".save") to Visual LISP - AutoLISP, Visual LISP & DCL - AutoCAD Forums Or... make a copy of current open drawing & place it into a predetermine folder - AutoLISP, Visual LISP & DCL - AutoCAD Forums And... LISP - Automatic Save that runs at set intervals
    1 point
  47. Welcome to CadTutor CamDuy and thank you for sharing your program, looks like you put a lot of effort in this. The world (and this site) needs more people like you
    1 point
  48. here's a dump from the BRep sample I posted here brep.txt
    1 point
  49. 3D Solid.dwg I’ve attached a sample DWG. When I run: (setq SOLID (ssget '((0 . "3DSOLID")))) (setq data (entget (ssname SOLID 0))) I get this result: When I run: (setq data (vlax-ename->vla-object (ssname SOLID 0))) I get this result instead:
    1 point
×
×
  • Create New...