Hi,
I've written a plug-in to create building (3D Solid) from 2D closed polylines with OD : ZMIN and HEIGHT
1. open transaction
2. Clone 2D closed polyline
3. Update elevation according ZMIN field
4. Extrude 2D polyline according value of field HEIGHT
5. dictionnary <objectid, objectid> of 2D polyline, 3D solid
6. commit transaction
7. for each objectid of 2D polyline => copy original OD to 3D Solid
With small quantity of polyline no problem, but if I try with lots of polyline, Map crash
10 000 polylines => 10 000 3DSolid WITHOUT copy OD => OK
300 polylines => 300 3D SOlid WITH copy of OD => OK
10 000 polylines => 10 000 3D solid with copy OD => Crash : no specific message, sometimes Error report, and sometimes directly to desktop without any error message.
In debug mode, if I put a break at evry 300 objects, wait 10 seconds and continue for a new cyle of 300 objects, new pause of 10s, sometimes I can treat all objects (and sometimes it crashes)
I've made a screen cast of my problem
using _AcDb = Autodesk.AutoCAD.DatabaseServices;
using _AcAp = Autodesk.AutoCAD.ApplicationServices;
using _AcRx = Autodesk.AutoCAD.Runtime;
using _AcGe = Autodesk.AutoCAD.Geometry;
using _AcEd = Autodesk.AutoCAD.EditorInput;
using _AcCo = Autodesk.AutoCAD.Colors;
using _AcMp = Autodesk.Gis.Map;
using _AcMpOdt = Autodesk.Gis.Map.ObjectData;
using _AcMpCst = Autodesk.Gis.Map.Constants;
using _AcMpUtl = Autodesk.Gis.Map.Utilities;
...
Dictionary<_AcDb.ObjectId, _AcDb.ObjectId> dct3DSolid = new Dictionary<_AcDb.ObjectId, _AcDb.ObjectId>();
// Boucle sur les entités
using (_AcAp.DocumentLock lk = _acCurDoc.LockDocument())
using (_AcDb.Transaction tr = _acCurDoc.TransactionManager.StartTransaction())
{
_AcDb.BlockTableRecord currentSpace = (_AcDb.BlockTableRecord)tr.GetObject(_acCurDb.CurrentSpaceId, _AcDb.OpenMode.ForWrite);
foreach (_AcDb.ObjectId idCurve in _lsPoly)
{
_AcDb.Curve oCurve = tr.GetObject(idCurve, _AcDb.OpenMode.ForRead) as _AcDb.Curve;
if (oCurve == null)
continue;
_AcDb.Curve oNewCurve = oCurve.Clone() as _AcDb.Curve;
if (oNewCurve == null)
continue;
//extrait l'élévation
if (ckbElevation.Checked)
{
sValue = RetrieveValue(idCurve, cbxElevTable.Text, cbxElevField.Text);
if (!double.TryParse(sValue, out dElev))
continue;
// Met la curve à la bonne hauteur
switch (oNewCurve.GetRXClass().DxfName)
{
case "CIRCLE":
_AcDb.Circle oCirle = oNewCurve as _AcDb.Circle;
oCirle.Center = new _AcGe.Point3d(oCirle.Center.X, oCirle.Center.Y, dElev);
break;
default:
_AcDb.Polyline oPoly = oNewCurve as _AcDb.Polyline;
if (oPoly != null)
oPoly.Elevation = dElev;
break;
}
}
// Extrait la hauteur
sValue = RetrieveValue(idCurve, cbxHautTable.Text, cbxHautField.Text);
if (!double.TryParse(sValue, out dHaut))
continue;
dHaut *= dFAct;
if (dHaut <= 0)
continue;
// Crée le solide
try
{
_AcDb.SweepOptions sweepOpts = new _AcDb.SweepOptions();
_AcDb.Solid3d sol = new _AcDb.Solid3d();
sol.CreateExtrudedSolid(oNewCurve, new _AcGe.Vector3d(0, 0, dHaut), sweepOpts);
sol.Layer = ckbEntityLayer.Checked ? oCurve.Layer : tbxSolidLayer.Text;
// Add 3D Solid to DB
_AcDb.ObjectId id3DSolid = currentSpace.AppendEntity(sol);
tr.AddNewlyCreatedDBObject(sol, true);
dct3DSolid.Add(idCurve, id3DSolid);
}
catch (System.Exception ex)
{
_acCurEd.WriteMessage(ex.Message);
}
}
tr.Commit();
}
if (ckbCopyOD.Checked)
{
foreach (_AcDb.ObjectId idCurve in dct3DSolid.Keys)
{
// Transfer OD fom base object to 3D Solid
// Get and Initialize Records
using (_AcMpOdt.Records records = _lsTables.GetObjectRecords(0, idCurve, _AcMpCst.OpenMode.OpenForWrite, false))
{
// SI pas de table associée
if (records.Count == 0) continue;
// Iterate through all records
foreach (_AcMpOdt.Record oRecord in records)
{
// Get the table
_AcMpOdt.Table table = _lsTables[oRecord.TableName];
using (_AcMpOdt.Record oNewRecord = _AcMpOdt.Record.Create())
{
table.InitRecord(oNewRecord);
// Get record info
for (int i = 0; i < oRecord.Count; i++)
{
_AcMpUtl.MapValue oVal = oRecord[i];
_AcMpUtl.MapValue oNewVal = oNewRecord[i];
oNewVal.Assign(oVal);
}
table.AddRecord(oNewRecord, dct3DSolid[idCurve]);
}
}
}
}
}
Thanks for any help
Olivier
Olivier Eckmann
Hi,
I've written a plug-in to create building (3D Solid) from 2D closed polylines with OD : ZMIN and HEIGHT
1. open transaction
2. Clone 2D closed polyline
3. Update elevation according ZMIN field
4. Extrude 2D polyline according value of field HEIGHT
5. dictionnary <objectid, objectid> of 2D polyline, 3D solid
6. commit transaction
7. for each objectid of 2D polyline => copy original OD to 3D Solid
With small quantity of polyline no problem, but if I try with lots of polyline, Map crash
10 000 polylines => 10 000 3DSolid WITHOUT copy OD => OK
300 polylines => 300 3D SOlid WITH copy of OD => OK
10 000 polylines => 10 000 3D solid with copy OD => Crash : no specific message, sometimes Error report, and sometimes directly to desktop without any error message.
In debug mode, if I put a break at evry 300 objects, wait 10 seconds and continue for a new cyle of 300 objects, new pause of 10s, sometimes I can treat all objects (and sometimes it crashes)
I've made a screen cast of my problem
using _AcDb = Autodesk.AutoCAD.DatabaseServices;
using _AcAp = Autodesk.AutoCAD.ApplicationServices;
using _AcRx = Autodesk.AutoCAD.Runtime;
using _AcGe = Autodesk.AutoCAD.Geometry;
using _AcEd = Autodesk.AutoCAD.EditorInput;
using _AcCo = Autodesk.AutoCAD.Colors;
using _AcMp = Autodesk.Gis.Map;
using _AcMpOdt = Autodesk.Gis.Map.ObjectData;
using _AcMpCst = Autodesk.Gis.Map.Constants;
using _AcMpUtl = Autodesk.Gis.Map.Utilities;
...
Dictionary<_AcDb.ObjectId, _AcDb.ObjectId> dct3DSolid = new Dictionary<_AcDb.ObjectId, _AcDb.ObjectId>();
// Boucle sur les entités
using (_AcAp.DocumentLock lk = _acCurDoc.LockDocument())
using (_AcDb.Transaction tr = _acCurDoc.TransactionManager.StartTransaction())
{
_AcDb.BlockTableRecord currentSpace = (_AcDb.BlockTableRecord)tr.GetObject(_acCurDb.CurrentSpaceId, _AcDb.OpenMode.ForWrite);
foreach (_AcDb.ObjectId idCurve in _lsPoly)
{
_AcDb.Curve oCurve = tr.GetObject(idCurve, _AcDb.OpenMode.ForRead) as _AcDb.Curve;
if (oCurve == null)
continue;
_AcDb.Curve oNewCurve = oCurve.Clone() as _AcDb.Curve;
if (oNewCurve == null)
continue;
//extrait l'élévation
if (ckbElevation.Checked)
{
sValue = RetrieveValue(idCurve, cbxElevTable.Text, cbxElevField.Text);
if (!double.TryParse(sValue, out dElev))
continue;
// Met la curve à la bonne hauteur
switch (oNewCurve.GetRXClass().DxfName)
{
case "CIRCLE":
_AcDb.Circle oCirle = oNewCurve as _AcDb.Circle;
oCirle.Center = new _AcGe.Point3d(oCirle.Center.X, oCirle.Center.Y, dElev);
break;
default:
_AcDb.Polyline oPoly = oNewCurve as _AcDb.Polyline;
if (oPoly != null)
oPoly.Elevation = dElev;
break;
}
}
// Extrait la hauteur
sValue = RetrieveValue(idCurve, cbxHautTable.Text, cbxHautField.Text);
if (!double.TryParse(sValue, out dHaut))
continue;
dHaut *= dFAct;
if (dHaut <= 0)
continue;
// Crée le solide
try
{
_AcDb.SweepOptions sweepOpts = new _AcDb.SweepOptions();
_AcDb.Solid3d sol = new _AcDb.Solid3d();
sol.CreateExtrudedSolid(oNewCurve, new _AcGe.Vector3d(0, 0, dHaut), sweepOpts);
sol.Layer = ckbEntityLayer.Checked ? oCurve.Layer : tbxSolidLayer.Text;
// Add 3D Solid to DB
_AcDb.ObjectId id3DSolid = currentSpace.AppendEntity(sol);
tr.AddNewlyCreatedDBObject(sol, true);
dct3DSolid.Add(idCurve, id3DSolid);
}
catch (System.Exception ex)
{
_acCurEd.WriteMessage(ex.Message);
}
}
tr.Commit();
}
if (ckbCopyOD.Checked)
{
foreach (_AcDb.ObjectId idCurve in dct3DSolid.Keys)
{
// Transfer OD fom base object to 3D Solid
// Get and Initialize Records
using (_AcMpOdt.Records records = _lsTables.GetObjectRecords(0, idCurve, _AcMpCst.OpenMode.OpenForWrite, false))
{
// SI pas de table associée
if (records.Count == 0) continue;
// Iterate through all records
foreach (_AcMpOdt.Record oRecord in records)
{
// Get the table
_AcMpOdt.Table table = _lsTables[oRecord.TableName];
using (_AcMpOdt.Record oNewRecord = _AcMpOdt.Record.Create())
{
table.InitRecord(oNewRecord);
// Get record info
for (int i = 0; i < oRecord.Count; i++)
{
_AcMpUtl.MapValue oVal = oRecord[i];
_AcMpUtl.MapValue oNewVal = oNewRecord[i];
oNewVal.Assign(oVal);
}
table.AddRecord(oNewRecord, dct3DSolid[idCurve]);
}
}
}
}
}
Thanks for any help
Olivier
Olivier Eckmann
It is interesting to see someone using Solid3d entities with AcadMap.
I guess you have already tried to isolate the issue from ObjectData side, or from Solid3d entity side (say, run code that only create Solid3d entities without reading/writing ObjectData), right? If so, then the problem would be on ObjectData side.
In my past experience with ObjectData, either with COM API.VBA, or .NET API, I did run into similar issue that AutoCAD crashes when reading/writing ObjectData from/to too many entities. I observed AutoCAD's memory usage went up if I ran code that access ObjectData of a lot entities, say, from my custom command. After the command finished, the memory were not released (or maybe only portion of it was released). If the command were executed a few times, AutoCAD would eventually ate all the memory available and crashed.
When in the process of AutoCAD memory fragmenting is inevitable, but it seems ObjectData's memory handling is especially bad, no matter how vigorously we try to wrap all the disposable map object with "using...." block. In my cases, I remember I ran into this when there are quite more than 10000 entities, while you ran into it with 10000 or less entities (but it is Solid3d, which I never used in Map).
In you case, you can try your code:
1. Watch memory use to see if the crash occurs with AutoCAD consuming exceptional high memory.
2. Run the Solid3d creation only process;
3. Add ObjectData reading (from the original Polyline) only to the process;
4. Add ObjectData writing (to the Solid3d entities) to the process.
With 2, 3, 4, watch the memory use. This way you might be able to tell where the high memory use happens. Not necessarily this would lead to a solution, but you may know where the problem lies in.
Norman Yuan
It is interesting to see someone using Solid3d entities with AcadMap.
I guess you have already tried to isolate the issue from ObjectData side, or from Solid3d entity side (say, run code that only create Solid3d entities without reading/writing ObjectData), right? If so, then the problem would be on ObjectData side.
In my past experience with ObjectData, either with COM API.VBA, or .NET API, I did run into similar issue that AutoCAD crashes when reading/writing ObjectData from/to too many entities. I observed AutoCAD's memory usage went up if I ran code that access ObjectData of a lot entities, say, from my custom command. After the command finished, the memory were not released (or maybe only portion of it was released). If the command were executed a few times, AutoCAD would eventually ate all the memory available and crashed.
When in the process of AutoCAD memory fragmenting is inevitable, but it seems ObjectData's memory handling is especially bad, no matter how vigorously we try to wrap all the disposable map object with "using...." block. In my cases, I remember I ran into this when there are quite more than 10000 entities, while you ran into it with 10000 or less entities (but it is Solid3d, which I never used in Map).
In you case, you can try your code:
1. Watch memory use to see if the crash occurs with AutoCAD consuming exceptional high memory.
2. Run the Solid3d creation only process;
3. Add ObjectData reading (from the original Polyline) only to the process;
4. Add ObjectData writing (to the Solid3d entities) to the process.
With 2, 3, 4, watch the memory use. This way you might be able to tell where the high memory use happens. Not necessarily this would lead to a solution, but you may know where the problem lies in.
Norman Yuan
no help from me - sorry. just some suggestions. you might get some information from the crash report (>>example<< ). i have not checked memory usage but also have never worked with 10000 entities. in the past i have used explicit declarations of tables, records, and record objects, and "Dispose()" method if it is available for the object, just to make sure the problem was not related to a "using" statement.
Check the "ObjectData.cs" sample in the Map ObjectARX SDK ????\Map Samples\DotNet\ObjectDataCS folder. the "AddODRecord" uses a Try/Catch structure.
again - just guessing. good luck! maybe you could easily create 100000 entities for testing?
no help from me - sorry. just some suggestions. you might get some information from the crash report (>>example<< ). i have not checked memory usage but also have never worked with 10000 entities. in the past i have used explicit declarations of tables, records, and record objects, and "Dispose()" method if it is available for the object, just to make sure the problem was not related to a "using" statement.
Check the "ObjectData.cs" sample in the Map ObjectARX SDK ????\Map Samples\DotNet\ObjectDataCS folder. the "AddODRecord" uses a Try/Catch structure.
again - just guessing. good luck! maybe you could easily create 100000 entities for testing?
Hi @norman.yuan ,
In my plug-in I've already 2 functions to export/import OD (similar to ATTIN and ATTOUT) to and from text tabbed file.
I've already export and import (update) more than 200 000 objects (DWG of 730Mo) without any problem.
I've no crash when reading/updating OD. Problem appears only when I create (lots of) record and add to objets (generally new created objects).
I try to explore your memory request to find possible solution. I'm not a specialist to follow memory usage but I'll check that.
I post info when I've tried.
I'll try solution of @fieldguy too with basic AddRecord function on existing objects and on newly created objects to see possible crash (or not).
Olivier
Olivier Eckmann
Hi @norman.yuan ,
In my plug-in I've already 2 functions to export/import OD (similar to ATTIN and ATTOUT) to and from text tabbed file.
I've already export and import (update) more than 200 000 objects (DWG of 730Mo) without any problem.
I've no crash when reading/updating OD. Problem appears only when I create (lots of) record and add to objets (generally new created objects).
I try to explore your memory request to find possible solution. I'm not a specialist to follow memory usage but I'll check that.
I post info when I've tried.
I'll try solution of @fieldguy too with basic AddRecord function on existing objects and on newly created objects to see possible crash (or not).
Olivier
Olivier Eckmann
Hi @fieldguy ,
I've tried with sample function CreateTable and AddODRecord found in ObjectSampleCS with this code :
[_AcRx.CommandMethodAttribute("TEST_OD1", _AcRx.CommandFlags.Modal)]
public void TEST_OD1()
{
_AcAp.Document doc = _AcAp.Application.DocumentManager.MdiActiveDocument;
_AcEd.Editor ed = doc.Editor;
ed.WriteMessage("\nTry to create table named TEST_OD...");
CreateTable(aMap.HostMapApplicationServices.Application.ActiveProject.ODTables, "TEST_OD");
ed.WriteMessage("\n ... table named TEST_OD created successfully");
}
[_AcRx.CommandMethodAttribute("TEST_OD2", _AcRx.CommandFlags.Modal)]
public void TEST_OD2()
{
_AcAp.Document doc = _AcAp.Application.DocumentManager.MdiActiveDocument;
_AcEd.Editor ed = doc.Editor;
ed.WriteMessage("\nTry to add record of table named TEST_OD to selected objects ...");
_AcEd.PromptSelectionOptions pso = new _AcEd.PromptSelectionOptions();
pso.MessageForAdding = "\nSelect objects to add record : ";
_AcEd.PromptSelectionResult psr = ed.GetSelection(pso);
foreach (_AcDb.ObjectId id in psr.Value.GetObjectIds())
{
AddODRecord(aMap.HostMapApplicationServices.Application.ActiveProject.ODTables, "TEST_OD", id);
}
ed.WriteMessage("\n ... all records added successfully");
}
1st function to create table is OK
2nd function to add Record on 10000 2D polylines crash. Here is screencast
Olivier Eckmann
Hi @fieldguy ,
I've tried with sample function CreateTable and AddODRecord found in ObjectSampleCS with this code :
[_AcRx.CommandMethodAttribute("TEST_OD1", _AcRx.CommandFlags.Modal)]
public void TEST_OD1()
{
_AcAp.Document doc = _AcAp.Application.DocumentManager.MdiActiveDocument;
_AcEd.Editor ed = doc.Editor;
ed.WriteMessage("\nTry to create table named TEST_OD...");
CreateTable(aMap.HostMapApplicationServices.Application.ActiveProject.ODTables, "TEST_OD");
ed.WriteMessage("\n ... table named TEST_OD created successfully");
}
[_AcRx.CommandMethodAttribute("TEST_OD2", _AcRx.CommandFlags.Modal)]
public void TEST_OD2()
{
_AcAp.Document doc = _AcAp.Application.DocumentManager.MdiActiveDocument;
_AcEd.Editor ed = doc.Editor;
ed.WriteMessage("\nTry to add record of table named TEST_OD to selected objects ...");
_AcEd.PromptSelectionOptions pso = new _AcEd.PromptSelectionOptions();
pso.MessageForAdding = "\nSelect objects to add record : ";
_AcEd.PromptSelectionResult psr = ed.GetSelection(pso);
foreach (_AcDb.ObjectId id in psr.Value.GetObjectIds())
{
AddODRecord(aMap.HostMapApplicationServices.Application.ActiveProject.ODTables, "TEST_OD", id);
}
ed.WriteMessage("\n ... all records added successfully");
}
1st function to create table is OK
2nd function to add Record on 10000 2D polylines crash. Here is screencast
Olivier Eckmann
Olivier Eckmann
Olivier Eckmann
Sorry for 3 messages, but I don't know how to add 2 screencast in 1 answer.
Olivier Eckmann
Sorry for 3 messages, but I don't know how to add 2 screencast in 1 answer.
Olivier Eckmann
Hi @norman.yuan , @fieldguy ,
I think I've found a solution. Sometimes in debug mode I obtain a crash with an error on DisposalUnwrapped on Table.
So when I use a Table, I try to include it in a "using" and it seems to work.
In my original code i just replace (after lots of other modifications 😀 )
// Get the table
_AmOd.Table table = _lsTables[oCurveRecord.TableName];
using (_AmOd.Record oNewRecord = _AmOd.Record.Create())
{
...
by
// Get the table
using (_AmOd.Table table = _lsTables[oCurveRecord.TableName])
using (_AmOd.Record oNewRecord = _AmOd.Record.Create())
{
...
and I can't reproduce the crash even on 10 000 3DSolid created.
Olivier
Olivier Eckmann
Hi @norman.yuan , @fieldguy ,
I think I've found a solution. Sometimes in debug mode I obtain a crash with an error on DisposalUnwrapped on Table.
So when I use a Table, I try to include it in a "using" and it seems to work.
In my original code i just replace (after lots of other modifications 😀 )
// Get the table
_AmOd.Table table = _lsTables[oCurveRecord.TableName];
using (_AmOd.Record oNewRecord = _AmOd.Record.Create())
{
...
by
// Get the table
using (_AmOd.Table table = _lsTables[oCurveRecord.TableName])
using (_AmOd.Record oNewRecord = _AmOd.Record.Create())
{
...
and I can't reproduce the crash even on 10 000 3DSolid created.
Olivier
Olivier Eckmann
good work! i can use that logic as well. i am curious what your application is doing - can you share a bit more info?
TIA
good work! i can use that logic as well. i am curious what your application is doing - can you share a bit more info?
TIA
Hi @fieldguy ,
My plug-in is available in this discussion (message 25, 43 and following for the evolutions)
Olivier
Olivier Eckmann
Hi @fieldguy ,
My plug-in is available in this discussion (message 25, 43 and following for the evolutions)
Olivier
Olivier Eckmann
Can't find what you're looking for? Ask the community or share your knowledge.