The code is rather straightforward and easy to follow, assuming there is a block reference with one or more curves (Line or Polyline) connecting to it (e.g. one of the end point of the Line/Polyline) at at the block's insertion point). The code is as following.
The class MyBlockJig:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; using System; using System.Collections.Generic; using CadDb = Autodesk.AutoCAD.DatabaseServices; namespace CurveConnectedBlockJig { public class MyBlockJig : IDisposable { private class ConnectedCurve { public ObjectId ConnectedCurveId { get; set; } public Drawable Ghost { get; set; } public bool AtStartPoint { get; set; } } private readonly Document _dwg; private readonly Database _db; private readonly Editor _ed; private readonly ObjectId _blkId; private readonly TransientManager _tsMng = TransientManager.CurrentTransientManager; private List<ConnectedCurve> _connectedCurves = new List<ConnectedCurve>(); private BlockReference _blkGhost = null; private Point3d _prevPoint; private Point3d _currPoint; public MyBlockJig(Document dwg, ObjectId blkId) { if (blkId.ObjectClass.DxfName.ToUpper()!="INSERT") { throw new InvalidOperationException( "Not a block reference!"); } _blkId = blkId; _dwg=dwg; _db = _dwg.Database; _ed= _dwg.Editor; } public void Move() { FindConnectedCurves(); _prevPoint = _blkGhost.Position; _currPoint = _blkGhost.Position; try { AddGhosts(); _ed.PointMonitor += Editor_PointMonitor; var res = _ed.GetPoint("\nMove block to: "); if (res.Status == PromptStatus.OK) { UpdateEntities(res.Value); } } finally { _ed.PointMonitor -= Editor_PointMonitor; } } public void Dispose() { ClearGhosts(); } #region private methods // Assume any Line/Polyline with one of its end point is at the block's insertion point // is considered connected to the block reference, thus the end segment of the // curve would move with the block reference during the drag; Also, if the connected curve is // Polyline, assume the connected segment is straight segment private void FindConnectedCurves() { using (var tran = _db.TransactionManager.StartTransaction()) { var model = (BlockTableRecord)tran.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(_db), OpenMode.ForRead); var blkRef = (BlockReference)tran.GetObject(_blkId, OpenMode.ForRead); _blkGhost = blkRef.Clone() as BlockReference; var position = blkRef.Position; foreach (ObjectId id in model) { var curve = tran.GetObject(id, OpenMode.ForRead) as Curve; if (curve == null) continue; if (!(curve is Line) && !(curve is CadDb.Polyline)) continue; if (IsConnected(position, curve.StartPoint, curve.EndPoint)) { var connectedCurve = GetConnectedCurve(curve, position); _connectedCurves.Add(connectedCurve); } } tran.Commit(); } } private bool IsConnected(Point3d position, Point3d point1, Point3d point2) { var dist = position.DistanceTo(point1); if (dist <= Tolerance.Global.EqualPoint) return true; dist = position.DistanceTo(point2); if (dist <= Tolerance.Global.EqualPoint) return true; return false; } private ConnectedCurve GetConnectedCurve(Curve curve, Point3d position) { var connected=new ConnectedCurve() { ConnectedCurveId = curve.Id }; Drawable drawable = null; Line line = null; Point3d pt; bool atStart; if (position.Equals(curve.StartPoint)) { if (curve is Line) { pt = ((Line)curve).EndPoint; } else { pt = ((CadDb.Polyline)curve).GetPoint3dAt(1); } atStart = true; } else { if (curve is Line) { pt = ((Line)curve).StartPoint; } else { var lastIndex = ((CadDb.Polyline)curve).NumberOfVertices - 1; pt = ((CadDb.Polyline)curve).GetPoint3dAt(lastIndex - 1); } atStart = false; } line = new Line(position, pt); line.ColorIndex = 2; drawable = line; connected.Ghost = drawable; connected.AtStartPoint = atStart; return connected; } private void Editor_PointMonitor(object sender, PointMonitorEventArgs e) { var point = e.Context.RawPoint; if (point.Equals(_currPoint)) return; UpdateGhosts(point); } private void AddGhosts() { _tsMng.AddTransient(_blkGhost, TransientDrawingMode.Highlight, 128, new IntegerCollection()); if (_connectedCurves.Count > 0 ) { foreach (var connected in _connectedCurves) { _tsMng.AddTransient(connected.Ghost, TransientDrawingMode.Highlight, 128, new IntegerCollection()); } } } private void UpdateGhosts(Point3d point) { _prevPoint = _currPoint; _currPoint = point; _blkGhost.TransformBy(Matrix3d.Displacement(_prevPoint.GetVectorTo(_currPoint))); _tsMng.UpdateTransient(_blkGhost, new IntegerCollection()); if ( _connectedCurves.Count > 0 ) { foreach (var connected in _connectedCurves) { ((Line)connected.Ghost).StartPoint=point; _tsMng.UpdateTransient(connected.Ghost, new IntegerCollection()); } } } private void ClearGhosts() { if (_blkGhost!=null) { _tsMng.EraseTransient(_blkGhost, new IntegerCollection()); _blkGhost.Dispose(); } if (_connectedCurves.Count > 0) { foreach (var connected in _connectedCurves) { if (connected.Ghost != null) { _tsMng.EraseTransient(connected.Ghost, new IntegerCollection()); connected.Ghost.Dispose(); } } } } private void UpdateEntities(Point3d point) { using (var tran = _db.TransactionManager.StartTransaction()) { var blk = (BlockReference)tran.GetObject(_blkId, OpenMode.ForWrite); blk.TransformBy(Matrix3d.Displacement(blk.Position.GetVectorTo(point))); foreach (var connected in _connectedCurves) { var curve = (Curve)tran.GetObject(connected.ConnectedCurveId, OpenMode.ForWrite); UpdateConnectedCurve(curve, point, connected.AtStartPoint); } tran.Commit(); } } private void UpdateConnectedCurve(Curve curve, Point3d point, bool changeStart) { if (curve is Line) { var line = (Line)curve; if (changeStart) { line.StartPoint = point; } else { line.EndPoint = point; } } else { var poly = (CadDb.Polyline)curve; if (changeStart) { poly.SetPointAt(0, new Point2d(point.X, point.Y)); } else { poly.SetPointAt(poly.NumberOfVertices-1, new Point2d(point.X,point.Y)); } } } #endregion } }
Then the CommandMethod to run the code:
using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using CadApp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(CurveConnectedBlockJig.MyCommands))] namespace CurveConnectedBlockJig { public class MyCommands { [CommandMethod("MoveBlk")] public static void RunCommand() { var dwg = CadApp.DocumentManager.MdiActiveDocument; var ed = dwg.Editor; var opt = new PromptEntityOptions( "\nSelect curve-connected block:"); opt.SetRejectMessage("Invalid: must be a block:"); opt.AddAllowedClass(typeof(BlockReference), true); var res = ed.GetEntity(opt); if (res.Status== PromptStatus.OK) { using (var jig = new MyBlockJig(dwg, res.ObjectId)) { jig.Move(); } } ed.WriteMessage("\n"); } } }
See the video clip below for the code in action:
As I mentioned, it would be equally easy to just derive a custom DrawJig for the same visual effect. If I can find extra time, I might put it up (hint: MIGHT, 😃).
Thank you for your tutorial. Could you please expand on the topic of splines? Can you provide a tutorial on moving blocks with spline objects? Thank you for sharing
ReplyDelete