I'm attempting to scan a DGN model using the DgnPlatformNet. I'm using the ScanCriteria Class. When I scan with no filters, everything works and I see all the elements in the DGN model.
When I set an element type filter using SetElementTypeTest I see problems. Here's how I'm setting the type mask...
public static ScanCriteria AddElementTypes(this ScanCriteria criteria, params MSElementType[] types) { var bitMask = new BitMask(false); foreach (var type in types) bitMask.SetBit((uint)type - 1, true); StringBuilder s = new StringBuilder(); s.AppendFormat("AddElementTypes MSElementType {0} bit mask {1}...", types[0], bitMask.ToString (0)); ElementScanner.ShowMessage(s.ToString()); criteria.SetElementTypeTest(bitMask); return criteria; }
The message tells you what one would expect: that is, the appropriate bit of the mask is set. We scan like this...
public UInt32 Scan(MSElementType[] types) { nElements_ = 0; ScanCriteria criteria = new ScanCriteria(); criteria.SetDrawnElements(); criteria.SetModelRef(model_); criteria.AddElementTypes (types); criteria.Scan(ElementProcessor); return nElements_; }
public StatusInt ElementProcessor(Element el, DgnModelRef modelRef){ ++nElements_; StringBuilder s = new StringBuilder(); s.AppendFormat("Element [{0}] ID {1} type {2}", nElements_, el.ElementId, el.ElementType); ElementScanner.ShowMessage(s.ToString()); return StatusInt.Success; }
This works for elements such as line and shape. It doesn't work for elements of higher value MSElementType such as Arc, Ellipse or Tag. What am I doing wrong?
Please give the following code a try to see if it helps:
class ElementProcessor { public StatusInt ProcessElement(Element el, DgnModelRef model) { // Process elements here StringBuilder s = new StringBuilder(); s.AppendFormat("Element: {0}[{1}], ElementID: {2}", el.ToString(), el.ElementType, el.ElementId); System.Diagnostics.Trace.WriteLine("Element Details - " + s); return StatusInt.Success; } } private static void Test() { // Set element types to process MSElementType[] elTypes = new MSElementType[] { MSElementType.Line, MSElementType.Shape }; foreach (DGN.MSElementType elType in elTypes) { // Create bitmask for each element type to be scanned/enumerated DGN.BitMask bmType = new DGN.BitMask(false); bmType.Set((uint)elType - 1); uint bmSize = 1 + (uint)elType; bmSize = (bmSize + 7) / 8; bmSize = (bmSize * 16) - 15; bmType.EnsureCapacity(bmSize + 1); // Set bitmask size // Set model and scanner criteria DgnModelRef model = Session.Instance.GetActiveDgnModel(); DGN.ScanCriteria sc = new DGN.ScanCriteria(); sc.SetElementTypeTest(bmType); sc.SetModelRef(model); sc.SetModelSections(DGN.DgnModelSections.GraphicElements); // Create a ScanDelegate to allow each element to be processed ElementProcessor ep = new ElementProcessor(); ScanDelegate scanDelegate = new ScanDelegate(ep.ProcessElement); sc.Scan(scanDelegate); } }HTH,
Bob
Answer Verified By: Jon Summers
Hi Bob,
Would this be OK rather than calling Scan for each MSElementType?
using (BitMask bitMask = new BitMask(false)) { uint bmSize = 1; foreach (MSElementType type in types) { bitMask.SetBit((uint)type - 1, true); bmSize += (uint)type; } bmSize = (bmSize + 7) / 8; bmSize = (bmSize * 16) - 15; bitMask.EnsureCapacity(bmSize + 1); criteria.SetElementTypeTest(bitMask); }
Unknown said:Would this be OK rather than calling Scan for each MSElementType? uint bmSize = 1; foreach (MSElementType type in types) { bitMask.SetBit((uint)type - 1, true); bmSize += (uint)type; } bmSize = (bmSize + 7) / 8; bmSize = (bmSize * 16) - 15; bitMask.EnsureCapacity(bmSize + 1);
uint bmSize = 1; foreach (MSElementType type in types) { bitMask.SetBit((uint)type - 1, true); bmSize += (uint)type; } bmSize = (bmSize + 7) / 8; bmSize = (bmSize * 16) - 15; bitMask.EnsureCapacity(bmSize + 1);
The problem here is that, although you're calculating the new size of the Bitmask in your foreach loop, you're still doing SetBit on the original Bitmask. You don't resize it until the loop is done.
You need to resize the Bitmask before you assign any new value. I've posted that as a DgnPlatformNet scanning example.
Frankly, I don't see why we should have to do this. What do those magic numbers mean? The DgnPlatformNet Bitmask is probably a wrapper around the mdlBitMask_api. With procedural MDL, we expect to have to perform this sort of sizing calculation, but with object-oriented C# it's absurd to have to do that manually. Why can't the class figure out its own capacity?
For comparison, consider List<Element>. Should we have to calculate how big that List is and resize it before we call List<Element>.Add()? Of course not!
Regards, Jon Summers LA Solutions
It is not nessessary to call EnsureCapacity before setting the bit. Using SetBit will adjust the capacity of the BitMask to the amount required by the bit being set.
EnsureCapacity will just make sure it is at least X size so there should be no need to call it on every iteration.
The example I posted above did have a mistake, the max type value should be used rather than the sum of types.
uint maxType = 0; foreach (MSElementType type in types) { uint currentTypeValue = (uint) type; bitMask.SetBit(currentTypeValue - 1, true); if (currentTypeValue > maxType) maxType = currentTypeValue; } uint bmSize = 1 + maxType; bmSize = (bmSize + 7) / 8; bmSize = (bmSize * 16) - 15; bitMask.EnsureCapacity(bmSize + 1);
Both yours and mine should work, as they are doing the same thing.I agree with you though, hopefully they push out a cleaner solution in the upcoming sdk updates.
Unknown said:It is not nessessary to call EnsureCapacity before setting the bit
Incorrect! A Bitmask seems to have capacity for small bit indices, such as for a line element (type 3) or shape element (type 6). For larger indices, such as a text element (type 17), the default size fails. That's how I discovered this problem. The solution is to calculate the maximum required capacity before assigning the Bitmask.
The example ElementScanner shows working code.
Unknown said: I just tried with Text type (17) and with mesh type (105) and both are setting the capacity automatically when SetBit is called
If that's always true, then ...
Unknown said: I am not sure why you are seeing differently
Me neither – consequently, I'm concerned.
Well the problem was originally occurring due to the capacity not being the "required" size for SetElementTypeTest.. SetBit is setting the capacity to the minimum required bits.. Ex: if you set bit 17, it makes the capacity 17, not the magic number defined by bmSize. EnsureCapacity is still required, it just doesn't need to be done before setting the bit is all I am trying to say. Or am I misunderstanding and your SetBit is crashing unless you ensure capacity first?