ymg3 Posted 1 hour ago Posted 1 hour ago Here's another Net app to facilitate the simplification of polyline with lots of segments. I am including the code and a bat file to compile it for Autocad 2017. Once you've compiled VWSimplifyInteractive.cs into VWSimplifyInteractive.dll, Netload it into autocad. New comand VWSimplifyInteractive will now be available to trhe command line or Autolisp. If you run the command you will be asked to select a polyline. The selected poly will be copied in Magenta,then a dialog box with 3 sliders will appear to let you vary the parameter of the simplification. The polyline is simplified in real-time and you can adjust the parameters until you Accept or Cancel. Note that your original polyline remains untouched and that the simplified one is place on layer "Simplified Poly" with color Green. once you press Accept. Here's the C# code: // VWSimplifyInteractive.cs // Interactive Visvalingam + Zhou-Jones for AutoCAD 2017 using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using AcApp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(VWSimplify.InteractiveCommands))] namespace VWSimplify { public class InteractiveCommands { [CommandMethod("VWSimplifyInteractive", CommandFlags.Modal | CommandFlags.Session)] public void Run() { Document doc = AcApp.DocumentManager.MdiActiveDocument; if (doc == null) return; Editor ed = doc.Editor; PromptEntityOptions peo = new PromptEntityOptions("\nSelect polyline to simplify: "); peo.SetRejectMessage("\nOnly LWPOLYLINE allowed."); peo.AddAllowedClass(typeof(Polyline), true); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; ObjectId srcId = per.ObjectId; // Create and show the modeless form var form = new SimplifyForm(srcId); AcApp.ShowModelessDialog(form); } } public class SimplifyForm : Form { private ObjectId _srcId; private ObjectId _previewId = ObjectId.Null; private TrackBar tbPercent; private CheckBox chkFlat, chkSkew, chkConv; private TrackBar tbFlat, tbSkew, tbConv; private Label lblPercent, lblVertices, lblFlat, lblSkew, lblConv; private Button btnAccept, btnCancel; // Current parameters private double _percent = 15.0; private bool _useFlat = true; private bool _useSkew = false; private bool _useConv = true; private double _flatStrength = 1.5; // reasonable start private double _skewStrength = 1.0; private double _convStrength = 2.5; // reasonable start public SimplifyForm(ObjectId srcId) { _srcId = srcId; InitializeComponent(); this.Shown += SimplifyForm_Shown; } private void SimplifyForm_Shown(object sender, EventArgs e) { UpdatePreview(); // First preview once form is displayed } private void InitializeComponent() { this.Text = "VW Simplify – Interactive"; this.Size = new Size(380, 445); this.FormBorderStyle = FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.StartPosition = FormStartPosition.CenterScreen; int y = 15; // Percentage lblPercent = new Label() { Left = 15, Top = y, Width = 330, Text = "Target Ratio: 15%" }; tbPercent = new TrackBar() { Left = 15, Top = y + 20, Width = 330, Minimum = 1, Maximum = 100, Value = 15, TickFrequency = 5 }; tbPercent.ValueChanged += (s, e) => { _percent = tbPercent.Value; lblPercent.Text = "Target Ratio: " + _percent + "%"; UpdatePreview(); }; y += 65; // Vertex Counter Label lblVertices = new Label() { Left = 15, Top = y, Width = 330, Text = "Vertices: Original: - | Current: - (-%)", ForeColor = Color.DarkBlue }; y += 25; // Flatness chkFlat = new CheckBox() { Left = 15, Top = y, Text = "Use Flatness weight", Checked = true, Width = 200 }; chkFlat.CheckedChanged += (s, e) => { _useFlat = chkFlat.Checked; tbFlat.Enabled = _useFlat; UpdatePreview(); }; y += 25; lblFlat = new Label() { Left = 30, Top = y, Width = 300, Text = "Flatness strength: 1.5" }; tbFlat = new TrackBar() { Left = 30, Top = y + 20, Width = 300, Minimum = 10, Maximum = 40, Value = 15, // 1.0 – 4.0 TickFrequency = 5 }; tbFlat.ValueChanged += (s, e) => { _flatStrength = tbFlat.Value / 10.0; lblFlat.Text = "Flatness strength: " + _flatStrength.ToString("0.0"); UpdatePreview(); }; y += 65; // Skewness chkSkew = new CheckBox() { Left = 15, Top = y, Text = "Use Skewness weight", Checked = false, Width = 200 }; chkSkew.CheckedChanged += (s, e) => { _useSkew = chkSkew.Checked; tbSkew.Enabled = _useSkew; UpdatePreview(); }; y += 25; lblSkew = new Label() { Left = 30, Top = y, Width = 300, Text = "Skewness strength: 1.0" }; tbSkew = new TrackBar() { Left = 30, Top = y + 20, Width = 300, Minimum = 10, Maximum = 30, Value = 10, Enabled = false }; tbSkew.ValueChanged += (s, e) => { _skewStrength = tbSkew.Value / 10.0; lblSkew.Text = "Skewness strength: " + _skewStrength.ToString("0.0"); UpdatePreview(); }; y += 65; // Convexity chkConv = new CheckBox() { Left = 15, Top = y, Text = "Use Convexity weight", Checked = true, Width = 200 }; chkConv.CheckedChanged += (s, e) => { _useConv = chkConv.Checked; tbConv.Enabled = _useConv; UpdatePreview(); }; y += 25; lblConv = new Label() { Left = 30, Top = y, Width = 300, Text = "Convexity strength: 2.5" }; tbConv = new TrackBar() { Left = 30, Top = y + 20, Width = 300, Minimum = 10, Maximum = 50, Value = 25, // 1.0 – 5.0 TickFrequency = 5 }; tbConv.ValueChanged += (s, e) => { _convStrength = tbConv.Value / 10.0; lblConv.Text = "Convexity strength: " + _convStrength.ToString("0.0"); UpdatePreview(); }; y += 70; // Buttons btnAccept = new Button() { Text = "Accept", Left = 80, Top = y, Width = 90 }; btnCancel = new Button() { Text = "Cancel", Left = 190, Top = y, Width = 90 }; btnAccept.Click += BtnAccept_Click; btnCancel.Click += BtnCancel_Click; this.Controls.AddRange(new Control[] { lblPercent, tbPercent, lblVertices, chkFlat, lblFlat, tbFlat, chkSkew, lblSkew, tbSkew, chkConv, lblConv, tbConv, btnAccept, btnCancel }); this.FormClosing += (s, e) => CleanupPreview(false); } private void UpdatePreview() { Document doc = AcApp.DocumentManager.MdiActiveDocument; if (doc == null) return; int originalCount = 0; int simplifiedCount = 0; using (DocumentLock dl = doc.LockDocument()) using (Transaction tr = doc.TransactionManager.StartTransaction()) { Polyline src = tr.GetObject(_srcId, OpenMode.ForRead) as Polyline; if (src == null) { tr.Commit(); return; } originalCount = src.NumberOfVertices; List<Point2d> simplified = Simplify(src, _percent, _useFlat, _useSkew, _useConv, _flatStrength, _skewStrength, _convStrength); if (simplified == null || simplified.Count < 2) { tr.Commit(); return; } simplifiedCount = simplified.Count; BlockTableRecord btr = (BlockTableRecord)tr.GetObject(doc.Database.CurrentSpaceId, OpenMode.ForWrite); // Erase old preview polyline if it exists if (!_previewId.IsNull && !_previewId.IsErased) { DBObject oldObj = tr.GetObject(_previewId, OpenMode.ForWrite, false); if (oldObj != null) { oldObj.Erase(); } _previewId = ObjectId.Null; } // Construct new polyline Polyline preview = new Polyline(); for (int i = 0; i < simplified.Count; i++) { preview.AddVertexAt(i, simplified[i], 0, 0, 0); } preview.Closed = src.Closed && simplified.Count >= 3; preview.ColorIndex = 6; // Magenta preview.ConstantWidth = 0; btr.AppendEntity(preview); tr.AddNewlyCreatedDBObject(preview, true); _previewId = preview.ObjectId; tr.Commit(); } // Update UI vertex stats if (originalCount > 0) { double reduction = 100.0 * (1.0 - ((double)simplifiedCount / originalCount)); lblVertices.Text = string.Format("Vertices: Original: {0} | Current: {1} ({2:0.0}% reduced)", originalCount, simplifiedCount, reduction); } AcApp.DocumentManager.MdiActiveDocument.Editor.Regen(); } private void BtnAccept_Click(object sender, EventArgs e) { Document doc = AcApp.DocumentManager.MdiActiveDocument; if (doc == null) return; using (DocumentLock dl = doc.LockDocument()) using (Transaction tr = doc.TransactionManager.StartTransaction()) { if (!_previewId.IsNull && !_previewId.IsErased) { Polyline preview = tr.GetObject(_previewId, OpenMode.ForWrite) as Polyline; if (preview != null) { // Ensure layer exists LayerTable lt = (LayerTable)tr.GetObject(doc.Database.LayerTableId, OpenMode.ForRead); ObjectId layerId; if (!lt.Has("Simplified Poly")) { lt.UpgradeOpen(); LayerTableRecord ltr = new LayerTableRecord(); ltr.Name = "Simplified Poly"; ltr.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex( Autodesk.AutoCAD.Colors.ColorMethod.ByAci, 3); // green layerId = lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); } else layerId = lt["Simplified Poly"]; preview.LayerId = layerId; preview.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex( Autodesk.AutoCAD.Colors.ColorMethod.ByLayer, 256); // Add XData with parameters AddXData(preview, tr, doc.Database); _previewId = ObjectId.Null; // prevent cleanup from erasing it } } tr.Commit(); } this.Close(); } private void BtnCancel_Click(object sender, EventArgs e) { CleanupPreview(true); this.Close(); } private void CleanupPreview(bool erase) { if (_previewId.IsNull || _previewId.IsErased) return; Document doc = AcApp.DocumentManager.MdiActiveDocument; if (doc == null) return; using (DocumentLock dl = doc.LockDocument()) using (Transaction tr = doc.TransactionManager.StartTransaction()) { if (erase) { DBObject obj = tr.GetObject(_previewId, OpenMode.ForWrite, false); if (obj != null) obj.Erase(); } tr.Commit(); } _previewId = ObjectId.Null; } private void AddXData(Polyline pl, Transaction tr, Database db) { RegAppTable rat = (RegAppTable)tr.GetObject(db.RegAppTableId, OpenMode.ForRead); if (!rat.Has("VW_SIMPLIFY")) { rat.UpgradeOpen(); RegAppTableRecord reg = new RegAppTableRecord(); reg.Name = "VW_SIMPLIFY"; rat.Add(reg); tr.AddNewlyCreatedDBObject(reg, true); } ResultBuffer rb = new ResultBuffer( new TypedValue(1001, "VW_SIMPLIFY"), new TypedValue(1000, "Visvalingam-ZhouJones"), new TypedValue(1040, _percent), new TypedValue(1071, _useFlat ? 1 : 0), new TypedValue(1040, _flatStrength), new TypedValue(1071, _useSkew ? 1 : 0), new TypedValue(1040, _skewStrength), new TypedValue(1071, _useConv ? 1 : 0), new TypedValue(1040, _convStrength) ); pl.XData = rb; } //-------------------------------------------------------------------- // Simplification core with Zhou-Jones weights //-------------------------------------------------------------------- private List<Point2d> Simplify(Polyline src, double percent, bool useFlat, bool useSkew, bool useConv, double flatStr, double skewStr, double convStr) { int n = src.NumberOfVertices; if (n < 3) return null; List<Point2d> pts = new List<Point2d>(n); for (int i = 0; i < n; i++) pts.Add(src.GetPoint2dAt(i)); bool closed = src.Closed; if (closed && !pts[0].IsEqualTo(pts[pts.Count - 1], new Tolerance(1e-10, 1e-10))) pts.Add(pts[0]); int target = (int)Math.Max(2, Math.Round(n * percent / 100.0)); if (closed) target = Math.Max(target, 3); return VisvalingamZhouJones(pts, target, useFlat, useSkew, useConv, flatStr, skewStr, convStr); } private List<Point2d> VisvalingamZhouJones(List<Point2d> pts, int target, bool useFlat, bool useSkew, bool useConv, double flatStr, double skewStr, double convStr) { int n = pts.Count; if (n <= target) return new List<Point2d>(pts); Point2d[] points = pts.ToArray(); double[] areas = new double[n]; bool[] alive = new bool[n]; int[] prev = new int[n]; int[] next = new int[n]; for (int i = 0; i < n; i++) { alive[i] = true; prev[i] = i - 1; next[i] = i + 1; areas[i] = double.MaxValue; } prev[0] = -1; next[n - 1] = -1; var heap = new SortedSet<HeapItem>(); for (int i = 1; i < n - 1; i++) { double a = WeightedArea(points, i, prev, next, useFlat, useSkew, useConv, flatStr, skewStr, convStr); areas[i] = a; heap.Add(new HeapItem(a, i)); } double lastArea = 0.0; int live = n; while (heap.Count > 0 && live > target) { HeapItem item = heap.Min; heap.Remove(item); int idx = item.Index; if (!alive[idx] || Math.Abs(item.Area - areas[idx]) > 1e-12) continue; double a = Math.Max(item.Area, lastArea); lastArea = a; areas[idx] = a; alive[idx] = false; live--; int p0 = prev[idx]; int p2 = next[idx]; if (p0 >= 0) next[p0] = p2; if (p2 >= 0) prev[p2] = p0; if (p0 >= 0 && alive[p0] && prev[p0] >= 0) { double na = WeightedArea(points, p0, prev, next, useFlat, useSkew, useConv, flatStr, skewStr, convStr); if (na < lastArea) na = lastArea; areas[p0] = na; heap.Add(new HeapItem(na, p0)); } if (p2 >= 0 && alive[p2] && next[p2] >= 0) { double na = WeightedArea(points, p2, prev, next, useFlat, useSkew, useConv, flatStr, skewStr, convStr); if (na < lastArea) na = lastArea; areas[p2] = na; heap.Add(new HeapItem(na, p2)); } } List<Point2d> res = new List<Point2d>(); for (int i = 0; i < n; i++) if (alive[i]) res.Add(points[i]); return res; } private double WeightedArea(Point2d[] pts, int i, int[] prev, int[] next, bool useFlat, bool useSkew, bool useConv, double flatStr, double skewStr, double convStr) { Point2d a = pts[prev[i]]; Point2d b = pts[i]; Point2d c = pts[next[i]]; double ea = TriangleArea(a, b, c); if (ea < 1e-20) return 0.0; double wFlat = 1.0, wSkew = 1.0, wConv = 1.0; // Base length and height double baseLen = a.GetDistanceTo(c); if (baseLen < 1e-12) return ea; // Height double area2 = Math.Abs((b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X)); double height = area2 / baseLen; // ---- Flatness ---- if (useFlat) { double ratio = height / baseLen; wFlat = Math.Pow(Math.Max(ratio, 1e-6), flatStr); } // ---- Skewness (prefer isosceles) ---- if (useSkew) { double d1 = a.GetDistanceTo(b); double d2 = b.GetDistanceTo(c); double maxd = Math.Max(d1, d2); double mind = Math.Min(d1, d2); double skewRatio = (maxd > 1e-12) ? mind / maxd : 1.0; wSkew = Math.Pow(skewRatio, skewStr); } // ---- Convexity ---- if (useConv) { // Cross product sign to determine turn direction double cross = (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X); if (cross > 0) wConv = convStr; // boost convex else wConv = 1.0; // concave stays normal } return ea * wFlat * wSkew * wConv; } private double TriangleArea(Point2d a, Point2d b, Point2d c) { return 0.5 * Math.Abs(a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y)); } private class HeapItem : IComparable<HeapItem> { public double Area; public int Index; public HeapItem(double area, int index) { Area = area; Index = index; } public int CompareTo(HeapItem other) { int cmp = Area.CompareTo(other.Area); return cmp != 0 ? cmp : Index.CompareTo(other.Index); } } } } Bat file to compile for Acad 2017, From a powershell prompt issue: .\Build VWSimplifyInteractive.cs @echo off if "%~1"=="" ( echo Error: No source file specified. echo Usage: build.bat SourceFileName exit /b 1 ) set CSC="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" set ACAD=C:\Program Files\Autodesk\AutoCAD 2017 %CSC% /target:library /platform:x64 /out:"%~1.dll" "/r:%ACAD%\AcCoreMgd.dll" "/r:%ACAD%\AcDbMgd.dll" "/r:%ACAD%\AcMgd.dll" "%~1.cs" if %ERRORLEVEL% equ 0 ( echo. echo Successfully built %~1.dll ) else ( echo. echo Compilation failed. ) VWSimplifyInteractive.cs Build.bat Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.