- 积分
- 24557
- 明经币
- 个
- 注册时间
- 2004-3-17
- 在线时间
- 小时
- 威望
-
- 金钱
- 个
- 贡献
-
- 激情
-
|
楼主 |
发表于 2009-5-15 15:17:00
|
显示全部楼层
May 12, 2008
An automatic numbering system for AutoCAD blocks using .NET - Part 3
In the last post we introduced some additional features to the original post in this series. In this post we take advantage of - and further extend - those features, by allowing deletion, movement and compaction of the numbered objects.
Here's the modified C# code:- using Autodesk.AutoCAD.ApplicationServices;
- using Autodesk.AutoCAD.Runtime;
- using Autodesk.AutoCAD.DatabaseServices;
- using Autodesk.AutoCAD.EditorInput;
- using Autodesk.AutoCAD.Geometry;
- using System.Collections.Generic;
- namespace AutoNumberedBubbles
- {
- public class Commands : IExtensionApplication
- {
- // Strings identifying the block
- // and the attribute name to use
- const string blockName = "BUBBLE";
- const string attbName = "NUMBER";
- // We will use a separate object to
- // manage our numbering, and maintain a
- // "base" index (the start of the list)
- private NumberedObjectManager m_nom;
- private int m_baseNumber = 0;
- // Constructor
- public Commands()
- {
- m_nom = new NumberedObjectManager();
- }
- // Functions called on initialization & termination
- public void Initialize()
- {
- try
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- ed.WriteMessage(
- "\nLNS Load numbering settings by analyzing the current drawing" +
- "\nDMP Print internal numbering information" +
- "\nBAP Create bubbles at points" +
- "\nBIC Create bubbles at the center of circles" +
- "\nMB Move a bubble in the list" +
- "\nDB Delete a bubble" +
- "\nRBS Reorder the bubbles, to close gaps caused by deletion" +
- "\nHLB Highlight a particular bubble"
- );
- }
- catch
- { }
- }
- public void Terminate()
- {
- }
- // Command to extract and display information
- // about the internal numbering
- [CommandMethod("DMP")]
- public void DumpNumberingInformation()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- m_nom.DumpInfo(ed);
- }
- // Command to analyze the current document and
- // understand which indeces have been used and
- // which are currently free
- [CommandMethod("LNS")]
- public void LoadNumberingSettings()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- // We need to clear any internal state
- // already collected
- m_nom.Clear();
- m_baseNumber = 0;
- // Select all the blocks in the current drawing
- TypedValue[] tvs =
- new TypedValue[1] {
- new TypedValue(
- (int)DxfCode.Start,
- "INSERT"
- )
- };
- SelectionFilter sf =
- new SelectionFilter(tvs);
- PromptSelectionResult psr =
- ed.SelectAll(sf);
- // If it succeeded and we have some blocks...
- if (psr.Status == PromptStatus.OK &&
- psr.Value.Count > 0)
- {
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
- // First get the modelspace and the ID
- // of the block for which we're searching
- BlockTableRecord ms;
- ObjectId blockId;
- if (GetBlock(
- db, tr, out ms, out blockId
- ))
- {
- // For each block reference in the drawing...
- foreach (SelectedObject o in psr.Value)
- {
- DBObject obj =
- tr.GetObject(o.ObjectId, OpenMode.ForRead);
- BlockReference br = obj as BlockReference;
- if (br != null)
- {
- // If it's the one we care about...
- if (br.BlockTableRecord == blockId)
- {
- // Check its attribute references...
- int pos = -1;
- AttributeCollection ac =
- br.AttributeCollection;
- foreach (ObjectId id in ac)
- {
- DBObject obj2 =
- tr.GetObject(id, OpenMode.ForRead);
- AttributeReference ar =
- obj2 as AttributeReference;
- // When we find the attribute
- // we care about...
- if (ar.Tag == attbName)
- {
- try
- {
- // Attempt to extract the number from
- // the text string property... use a
- // try-catch block just in case it is
- // non-numeric
- pos =
- int.Parse(ar.TextString);
- // Add the object at the appropriate
- // index
- m_nom.NumberObject(
- o.ObjectId, pos, false
- );
- }
- catch { }
- }
- }
- }
- }
- }
- }
- tr.Commit();
- }
- // Once we have analyzed all the block references...
- int start = m_nom.GetLowerBound(true);
- // If the first index is non-zero, ask the user if
- // they want to rebase the list to begin at the
- // current start position
- if (start > 0)
- {
- ed.WriteMessage(
- "\nLowest index is {0}. ",
- start
- );
- PromptKeywordOptions pko =
- new PromptKeywordOptions(
- "Make this the start of the list?"
- );
- pko.AllowNone = true;
- pko.Keywords.Add("Yes");
- pko.Keywords.Add("No");
- pko.Keywords.Default = "Yes";
- PromptResult pkr =
- ed.GetKeywords(pko);
- if (pkr.Status == PromptStatus.OK)
- {
- if (pkr.StringResult == "Yes")
- {
- // We store our own base number
- // (the object used to manage objects
- // always uses zero-based indeces)
- m_baseNumber = start;
- m_nom.RebaseList(m_baseNumber);
- }
- }
- }
- }
- }
-
- // Command to create bubbles at points selected
- // by the user - loops until cancelled
- [CommandMethod("BAP")]
- public void BubblesAtPoints()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- Autodesk.AutoCAD.ApplicationServices.
- TransactionManager tm =
- doc.TransactionManager;
- Transaction tr =
- tm.StartTransaction();
- using (tr)
- {
- // Get the information about the block
- // and attribute definitions we care about
- BlockTableRecord ms;
- ObjectId blockId;
- AttributeDefinition ad;
- List<AttributeDefinition> other;
- if (GetBlock(
- db, tr, out ms, out blockId
- ))
- {
- GetBlockAttributes(
- tr, blockId, out ad, out other
- );
- // By default the modelspace is returned to
- // us in read-only state
- ms.UpgradeOpen();
- // Loop until cancelled
- bool finished = false;
- while (!finished)
- {
- PromptPointOptions ppo =
- new PromptPointOptions("\nSelect point: ");
- ppo.AllowNone = true;
- PromptPointResult ppr =
- ed.GetPoint(ppo);
- if (ppr.Status != PromptStatus.OK)
- finished = true;
- else
- // Call a function to create our bubble
- CreateNumberedBubbleAtPoint(
- db, ms, tr, ppr.Value,
- blockId, ad, other
- );
- tm.QueueForGraphicsFlush();
- tm.FlushGraphics();
- }
- }
- tr.Commit();
- }
- }
- // Command to create a bubble at the center of
- // each of the selected circles
- [CommandMethod("BIC")]
- public void BubblesInCircles()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- // Allow the user to select circles
- TypedValue[] tvs =
- new TypedValue[1] {
- new TypedValue(
- (int)DxfCode.Start,
- "CIRCLE"
- )
- };
- SelectionFilter sf =
- new SelectionFilter(tvs);
- PromptSelectionResult psr =
- ed.GetSelection(sf);
- if (psr.Status == PromptStatus.OK &&
- psr.Value.Count > 0)
- {
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
- // Get the information about the block
- // and attribute definitions we care about
- BlockTableRecord ms;
- ObjectId blockId;
- AttributeDefinition ad;
- List<AttributeDefinition> other;
- if (GetBlock(
- db, tr, out ms, out blockId
- ))
- {
- GetBlockAttributes(
- tr, blockId, out ad, out other
- );
- // By default the modelspace is returned to
- // us in read-only state
- ms.UpgradeOpen();
- foreach (SelectedObject o in psr.Value)
- {
- // For each circle in the selected list...
- DBObject obj =
- tr.GetObject(o.ObjectId, OpenMode.ForRead);
- Circle c = obj as Circle;
- if (c == null)
- ed.WriteMessage(
- "\nObject selected is not a circle."
- );
- else
- // Call our numbering function, passing the
- // center of the circle
- CreateNumberedBubbleAtPoint(
- db, ms, tr, c.Center,
- blockId, ad, other
- );
- }
- }
- tr.Commit();
- }
- }
- }
- // Command to delete a particular bubble
- // selected by its index
- [CommandMethod("MB")]
- public void MoveBubble()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- // Use a helper function to select a valid bubble index
- int pos =
- GetBubbleNumber(
- ed,
- "\nEnter number of bubble to move: "
- );
- if (pos >= m_baseNumber)
- {
- int from = pos - m_baseNumber;
- pos =
- GetBubbleNumber(
- ed,
- "\nEnter destination position: "
- );
- if (pos >= m_baseNumber)
- {
- int to = pos - m_baseNumber;
- ObjectIdCollection ids =
- m_nom.MoveObject(from, to);
- RenumberBubbles(doc.Database, ids);
- }
- }
- }
- // Command to delete a particular bubbler,
- // selected by its index
- [CommandMethod("DB")]
- public void DeleteBubble()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- // Use a helper function to select a valid bubble index
- int pos =
- GetBubbleNumber(
- ed,
- "\nEnter number of bubble to erase: "
- );
- if (pos >= m_baseNumber)
- {
- // Remove the object from the internal list
- // (this returns the ObjectId stored for it,
- // which we can then use to erase the entity)
- ObjectId id =
- m_nom.RemoveObject(pos - m_baseNumber);
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
- DBObject obj =
- tr.GetObject(id, OpenMode.ForWrite);
- obj.Erase();
- tr.Commit();
- }
- }
- }
- // Command to reorder all the bubbles in the drawing,
- // closing all the gaps between numbers but maintaining
- // the current numbering order
- [CommandMethod("RBS")]
- public void ReorderBubbles()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- // Re-order the bubbles - the IDs returned are
- // of the objects that need to be renumbered
- ObjectIdCollection ids =
- m_nom.ReorderObjects();
- RenumberBubbles(doc.Database, ids);
- }
- // Command to highlight a particular bubble
- [CommandMethod("HLB")]
- public void HighlightBubble()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- // Use our function to select a valid bubble index
- int pos =
- GetBubbleNumber(
- ed,
- "\nEnter number of bubble to highlight: "
- );
- if (pos >= m_baseNumber)
- {
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
- // Get the ObjectId from the index...
- ObjectId id =
- m_nom.GetObjectId(pos - m_baseNumber);
- if (id == ObjectId.Null)
- {
- ed.WriteMessage(
- "\nNumber is not currently used -" +
- " nothing to highlight."
- );
- return;
- }
- // And then open & highlight the entity
- Entity ent =
- (Entity)tr.GetObject(
- id,
- OpenMode.ForRead
- );
- ent.Highlight();
- tr.Commit();
- }
- }
- }
- // Internal helper function to open and retrieve
- // the model-space and the block def we care about
- private bool
- GetBlock(
- Database db,
- Transaction tr,
- out BlockTableRecord ms,
- out ObjectId blockId
- )
- {
- BlockTable bt =
- (BlockTable)tr.GetObject(
- db.BlockTableId,
- OpenMode.ForRead
- );
- if (!bt.Has(blockName))
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- ed.WriteMessage(
- "\nCannot find block definition "" +
- blockName +
- "" in the current drawing."
- );
- blockId = ObjectId.Null;
- ms = null;
- return false;
- }
- ms =
- (BlockTableRecord)tr.GetObject(
- bt[BlockTableRecord.ModelSpace],
- OpenMode.ForRead
- );
- blockId = bt[blockName];
- return true;
- }
- // Internal helper function to retrieve
- // attribute info from our block
- // (we return the main attribute def
- // and then all the "others")
- private void
- GetBlockAttributes(
- Transaction tr,
- ObjectId blockId,
- out AttributeDefinition ad,
- out List<AttributeDefinition> other
- )
- {
- BlockTableRecord blk =
- (BlockTableRecord)tr.GetObject(
- blockId,
- OpenMode.ForRead
- );
- ad = null;
- other =
- new List<AttributeDefinition>();
- foreach (ObjectId attId in blk)
- {
- DBObject obj =
- (DBObject)tr.GetObject(
- attId,
- OpenMode.ForRead
- );
- AttributeDefinition ad2 =
- obj as AttributeDefinition;
- if (ad2 != null)
- {
- if (ad2.Tag == attbName)
- {
- if (ad2.Constant)
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- ed.WriteMessage(
- "\nAttribute to change is constant!"
- );
- }
- else
- ad = ad2;
- }
- else
- if (!ad2.Constant)
- other.Add(ad2);
- }
- }
- }
- // Internal helper function to create a bubble
- // at a particular point
- private Entity
- CreateNumberedBubbleAtPoint(
- Database db,
- BlockTableRecord btr,
- Transaction tr,
- Point3d pt,
- ObjectId blockId,
- AttributeDefinition ad,
- List<AttributeDefinition> other
- )
- {
- // Create a new block reference
- BlockReference br =
- new BlockReference(pt, blockId);
- // Add it to the database
- br.SetDatabaseDefaults();
- ObjectId blockRefId = btr.AppendEntity(br);
- tr.AddNewlyCreatedDBObject(br, true);
- // Create an attribute reference for our main
- // attribute definition (where we'll put the
- // bubble's number)
- AttributeReference ar =
- new AttributeReference();
- // Add it to the database, and set its position, etc.
- ar.SetDatabaseDefaults();
- ar.SetAttributeFromBlock(ad, br.BlockTransform);
- ar.Position =
- ad.Position.TransformBy(br.BlockTransform);
- ar.Tag = ad.Tag;
- // Set the bubble's number
- int bubbleNumber =
- m_baseNumber +
- m_nom.NextObjectNumber(blockRefId);
- ar.TextString = bubbleNumber.ToString();
- ar.AdjustAlignment(db);
- // Add the attribute to the block reference
- br.AttributeCollection.AppendAttribute(ar);
- tr.AddNewlyCreatedDBObject(ar, true);
- // Now we add attribute references for the
- // other attribute definitions
- foreach (AttributeDefinition ad2 in other)
- {
- AttributeReference ar2 =
- new AttributeReference();
- ar2.SetAttributeFromBlock(ad2, br.BlockTransform);
- ar2.Position =
- ad2.Position.TransformBy(br.BlockTransform);
- ar2.Tag = ad2.Tag;
- ar2.TextString = ad2.TextString;
- ar2.AdjustAlignment(db);
- br.AttributeCollection.AppendAttribute(ar2);
- tr.AddNewlyCreatedDBObject(ar2, true);
- }
- return br;
- }
- // Internal helper function to have the user
- // select a valid bubble index
- private int
- GetBubbleNumber(
- Editor ed,
- string prompt
- )
- {
- int upper = m_nom.GetUpperBound();
- if (upper <= 0)
- {
- ed.WriteMessage(
- "\nNo bubbles are currently being managed."
- );
- return upper;
- }
- PromptIntegerOptions pio =
- new PromptIntegerOptions(prompt);
- pio.AllowNone = false;
- // Get the limits from our manager object
- pio.LowerLimit =
- m_baseNumber +
- m_nom.GetLowerBound(false);
- pio.UpperLimit =
- m_baseNumber +
- upper;
- PromptIntegerResult pir =
- ed.GetInteger(pio);
- if (pir.Status == PromptStatus.OK)
- return pir.Value;
- else
- return -1;
- }
- // Internal helper function to open up a list
- // of bubbles and reset their number to the
- // position in the list
- private void RenumberBubbles(
- Database db, ObjectIdCollection ids)
- {
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
- // Get the block information
- BlockTableRecord ms;
- ObjectId blockId;
- if (GetBlock(
- db, tr, out ms, out blockId
- ))
- {
- // Open each bubble to be renumbered
- foreach (ObjectId bid in ids)
- {
- if (bid != ObjectId.Null)
- {
- DBObject obj =
- tr.GetObject(bid, OpenMode.ForRead);
- BlockReference br = obj as BlockReference;
- if (br != null)
- {
- if (br.BlockTableRecord == blockId)
- {
- AttributeCollection ac =
- br.AttributeCollection;
- // Go through its attributes
- foreach (ObjectId aid in ac)
- {
- DBObject obj2 =
- tr.GetObject(aid, OpenMode.ForRead);
- AttributeReference ar =
- obj2 as AttributeReference;
- if (ar.Tag == attbName)
- {
- // Change the one we care about
- ar.UpgradeOpen();
- int bubbleNumber =
- m_baseNumber + m_nom.GetNumber(bid);
- ar.TextString =
- bubbleNumber.ToString();
- break;
- }
- }
- }
- }
- }
- }
- }
- tr.Commit();
- }
- }
- }
- // A generic class for managing groups of
- // numbered (and ordered) objects
- public class NumberedObjectManager
- {
- // We need to store a list of object IDs, but
- // also a list of free positions in the list
- // (this allows numbering gaps)
- private List<ObjectId> m_ids;
- private List<int> m_free;
- // Constructor
- public NumberedObjectManager()
- {
- m_ids =
- new List<ObjectId>();
- m_free =
- new List<int>();
- }
- // Clear the internal lists
- public void Clear()
- {
- m_ids.Clear();
- m_free.Clear();
- }
- // Return the first entry in the ObjectId list
- // (specify "true" if you want to skip
- // any null object IDs)
- public int GetLowerBound(bool ignoreNull)
- {
- if (ignoreNull)
- // Define an in-line predicate to check
- // whether an ObjectId is null
- return
- m_ids.FindIndex(
- delegate(ObjectId id)
- {
- return id != ObjectId.Null;
- }
- );
- else
- return 0;
- }
- // Return the last entry in the ObjectId list
- public int GetUpperBound()
- {
- return m_ids.Count - 1;
- }
- // Store the specified ObjectId in the next
- // available location in the list, and return
- // what that is
- public int NextObjectNumber(ObjectId id)
- {
- int pos;
- if (m_free.Count > 0)
- {
- // Get the first free position, then remove
- // it from the "free" list
- pos = m_free[0];
- m_free.RemoveAt(0);
- m_ids[pos] = id;
- }
- else
- {
- // There are no free slots (gaps in the numbering)
- // so we append it to the list
- pos = m_ids.Count;
- m_ids.Add(id);
- }
- return pos;
- }
- // Go through the list of objects and close any gaps
- // by shuffling the list down (easy, as we're using a
- // List<> rather than an array)
- public ObjectIdCollection ReorderObjects()
- {
- // Create a collection of ObjectIds we'll return
- // for the caller to go and update
- // (so the renumbering will gets reflected
- // in the objects themselves)
- ObjectIdCollection ids =
- new ObjectIdCollection();
- // We'll go through the "free" list backwards,
- // to allow any changes made to the list of
- // objects to not affect what we're doing
- List<int> rev =
- new List<int>(m_free);
- rev.Reverse();
- foreach (int pos in rev)
- {
- // First we remove the object at the "free"
- // position (in theory this should be set to
- // ObjectId.Null, as the slot has been marked
- // as blank)
- m_ids.RemoveAt(pos);
- // Now we go through and add the IDs of any
- // affected objects to the list to return
- for (int i = pos; i < m_ids.Count; i++)
- {
- ObjectId id = m_ids[pos];
- // Only add non-null objects
- // not already in the list
-
- if (!ids.Contains(id) &&
- id != ObjectId.Null)
- ids.Add(id);
- }
- }
-
- // Our free slots have been filled, so clear
- // the list
-
- m_free.Clear();
- return ids;
- }
- // Get the ID of an object at a particular position
- public ObjectId GetObjectId(int pos)
- {
- if (pos < m_ids.Count)
- return m_ids[pos];
- else
- return ObjectId.Null;
- }
- // Get the position of an ObjectId in the list
- public int GetNumber(ObjectId id)
- {
- if (m_ids.Contains(id))
- return m_ids.IndexOf(id);
- else
- return -1;
- }
- // Store an ObjectId in a particular position
- // (shuffle == true will "insert" it, shuffling
- // the remaining objects down,
- // shuffle == false will replace the item in
- // that slot)
- public void NumberObject(
- ObjectId id, int index, bool shuffle)
- {
- // If we're inserting into the list
- if (index < m_ids.Count)
- {
- if (shuffle)
- // Insert takes care of the shuffling
- m_ids.Insert(index, id);
- else
- {
- // If we're replacing the existing item, do
- // so and then make sure the slot is removed
- // from the "free" list, if applicable
- m_ids[index] = id;
- if (m_free.Contains(index))
- m_free.Remove(index);
- }
- }
- else
- {
- // If we're appending, shuffling is irrelevant,
- // but we may need to add additional "free" slots
- // if the position comes after the end
- while (m_ids.Count < index)
- {
- m_ids.Add(ObjectId.Null);
- m_free.Add(m_ids.LastIndexOf(ObjectId.Null));
- m_free.Sort();
- }
- m_ids.Add(id);
- }
- }
- // Move an ObjectId already in the list to a
- // particular position
- // (ObjectIds between the two positions will
- // get shuffled down automatically)
- public ObjectIdCollection MoveObject(
- int from, int to)
- {
- ObjectIdCollection ids =
- new ObjectIdCollection();
- if (from < m_ids.Count &&
- to < m_ids.Count)
- {
- if (from != to)
- {
- ObjectId id = m_ids[from];
- m_ids.RemoveAt(from);
- m_ids.Insert(to, id);
- int start = (from < to ? from : to);
- int end = (from < to ? to : from);
- for (int i = start; i <= end; i++)
- {
- ids.Add(m_ids[i]);
- }
- }
- // Now need to adjust/recreate "free" list
- m_free.Clear();
- for (int j = 0; j < m_ids.Count; j++)
- {
- if (m_ids[j] == ObjectId.Null)
- m_free.Add(j);
- }
- }
- return ids;
- }
- // Remove an ObjectId from the list
- public int RemoveObject(ObjectId id)
- {
- // Check it's non-null and in the list
- if (id != ObjectId.Null &&
- m_ids.Contains(id))
- {
- int pos = m_ids.IndexOf(id);
- RemoveObject(pos);
- return pos;
- }
- return -1;
- }
- // Remove the ObjectId at a particular position
- public ObjectId RemoveObject(int pos)
- {
- // Get the ObjectId in the specified position,
- // making sure it's non-null
- ObjectId id = m_ids[pos];
- if (id != ObjectId.Null)
- {
- // Null out the position and add it to the
- // "free" list
- m_ids[pos] = ObjectId.Null;
- m_free.Add(pos);
- m_free.Sort();
- }
- return id;
- }
- // Dump out the object list information
- // as well as the "free" slots
- public void DumpInfo(Editor ed)
- {
- if (m_ids.Count > 0)
- {
- ed.WriteMessage("\nIdx ObjectId");
- int index = 0;
- foreach (ObjectId id in m_ids)
- ed.WriteMessage("\n{0} {1}", index++, id);
- }
- if (m_free.Count > 0)
- {
- ed.WriteMessage("\n\nFree list: ");
- foreach (int pos in m_free)
- ed.WriteMessage("{0} ", pos);
- }
- }
- // Remove the initial n items from the list
- public void RebaseList(int start)
- {
- // First we remove the ObjectIds
- for (int i=0; i < start; i++)
- m_ids.RemoveAt(0);
- // Then we go through the "free" list...
- int idx = 0;
- while (idx < m_free.Count)
- {
- if (m_free[idx] < start)
- // Remove any that refer to the slots
- // we've removed
- m_free.RemoveAt(idx);
- else
- {
- // Subtracting the number of slots
- // we've removed from the other items
- m_free[idx] -= start;
- idx++;
- }
- }
- }
- }
- }
The above code defines four new commands which move, delete and highlight a bubble, and reorder the bubble list. I could probably have used better terminology for some of the command-names - the MB (Move Bubble) command does not move the physical position of the block in the drawing, it moves the bubble inside the list (i.e. it changes the bubble's number while maintaining the consistency of the list). Similarly, RBS (Reorder BubbleS) actually just compacts the list, removing unnecessary gaps in the list created by deletion. Anyway, the user is notified of the additional commands by lines 46-50, and the commands themselves are implemented by lines 370-517. MB, DB (Delete Bubble) and HLB (HighLight Bubble) all use a new helper function, GetBubbleNumber(), defined by lines 695-733, which asks the user to select a valid bubble from the list, which will then get moved, deleted or highlighted, as appropriate.
The other new helper function which is defined outside the NumberedObjectManager class (as the function depends on the specific implementation of our object numbering, i.e. with the value stored in an attribute in a block), is RenumberBubbles(), defined by lines 734-800. This function opens up a list of bubbles and sets their visible number to the one stored in the NamedObjectManager object. It is used by both MB and RBS.
To support these new commands, the NamedObjectManager class has also been extended in two new sections of the above code. The first new chunk of code (lines 888-961) implements new methods ReorderObjects() which again, is really a list compaction function and then GetObjectId() and GetNumber(), which - as you'd expect - return an ObjectId at a particular position and a position for a particular ObjectId. The next chunk (lines 1006-1079) implements MoveObject(), which moves an object from one place to another - shuffling the intermediate bubbles around, as needed - and two versions of RemoveObject(), depending on whether you wish to select the object by its ID or its position.
Something important to note about this implementation: so far we haven't dealt with what happens should the user choose to undo these commands: as the objects we're creating are not managed by AutoCAD (they are not stored in the drawing, for instance), their state is not captured in the undo filer, and so will not be affected by undo. But the geometry they refer to will, of course, so there is substantial potential for our list getting out of sync with reality. The easy (and arguably the best) way to get around this is to check for undo-related commands to be executed, and invalidate our list at that point (providing a suitable notification to the user, requesting that they run LNS again once done with their undoing & redoing). The current implementation does not do this.
Let's now take our new commands for a quick spin...
We're going to take our previously-created drawing, as a starting point, and use our new commands on it.
Let's start with DB:- Command: LNS
- Lowest index is 1. Make this the start of the list? [Yes/No] <Yes>: Yes
- Command: DB
- Enter number of bubble to erase: 3
- Command: DB
- Enter number of bubble to erase: 5
- Command: DB
- Enter number of bubble to erase: 15
- Command: DB
- Enter number of bubble to erase: 16
- Command: DMP
- Idx ObjectId
- 0 (2129683752)
- 1 (2129683776)
- 2 (0)
- 3 (2129683824)
- 4 (0)
- 5 (2129683872)
- 6 (2129683896)
- 7 (2129683920)
- 8 (2129683944)
- 9 (2129683968)
- 10 (2129683992)
- 11 (2129684016)
- 12 (2129684040)
- 13 (2129684064)
- 14 (0)
- 15 (0)
- 16 (2129684136)
- 17 (2129684160)
- 18 (2129684184)
- 19 (2129684208)
- Free list: 2 4 14 15
复制代码 As you can see, we've ended up with a few free slots in our list (and you'll note you need to add our "base number" (1) to get to the visible number). Here's the state of the drawing at this point:
Now let's try MB:- Command: MB
- Enter number of bubble to move: 7
- Enter destination position: 2
- Command: DMP
- Idx ObjectId
- 0 (2129683752)
- 1 (2129683896)
- 2 (2129683776)
- 3 (0)
- 4 (2129683824)
- 5 (0)
- 6 (2129683872)
- 7 (2129683920)
- 8 (2129683944)
- 9 (2129683968)
- 10 (2129683992)
- 11 (2129684016)
- 12 (2129684040)
- 13 (2129684064)
- 14 (0)
- 15 (0)
- 16 (2129684136)
- 17 (2129684160)
- 18 (2129684184)
- 19 (2129684208)
- Free list: 3 5 14 15
复制代码 This results in the item in internal slot 6 being moved to internal slot 1 (remember that base number :-) and the objects between being shuffled along. Here's what's on the screen at this point:
And finally we'll compact the list - removing those four free slots - with our RBS command:- Command: RBS
- Command: DMP
- Idx ObjectId
- 0 (2129683752)
- 1 (2129683896)
- 2 (2129683776)
- 3 (2129683824)
- 4 (2129683872)
- 5 (2129683920)
- 6 (2129683944)
- 7 (2129683968)
- 8 (2129683992)
- 9 (2129684016)
- 10 (2129684040)
- 11 (2129684064)
- 12 (2129684136)
- 13 (2129684160)
- 14 (2129684184)
- 15 (2129684208)
复制代码 And here's how that looks:
I don't currently have any further enhancements planned for this application. Feel free to post a comment or send me an email if there's a particular direction in which you'd like to see it go. For instance, is it interesting to see support for prefixes/suffixes...?
|
|