how to display entity on autocad drawing area without add to database? - c#

I want to display entities on a drawing area as a preview for the user, then if the user accepts the program, add the the entities to the database or make some modification.
I'm used to use transaction and commit the transaction the entities appear if i can make the entities appear before commit the transaction
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
int i = poly2TxtSetting.currentFormat.IndexFormat.startWith;
List<ObjectId> ListTextId = new List<ObjectId>();
List<ObjectId> ListPointId = new List<ObjectId>();
foreach (var po in Points)
{
i += poly2TxtSetting.currentFormat.IndexFormat.step;
DBText dtext = new DBText();
dtext.TextString = i.tostring();
dtext.Position = po;
dtext.SetDatabaseDefaults();
DBPoint point = new DBPoint(po);
btr.AppendEntity(dtext);
tr.AddNewlyCreatedDBObject(dtext, true);
btr.AppendEntity(point);
tr.AddNewlyCreatedDBObject(point, true);
}
tr.Commit();
}

If you want to display your model in AutoCAD model space, you have two options.
1) Insert it into database.
2) Add it into Transient Manager.
I think you need is 2nd option.
Search for Transient Graphics.
Check below Code that will help you.
Solid3d solid=new Solid(0);
solid.CreateSphere(10);
TransientManager.CurrentTransientManager.AddTransient(solid, TransientDrawingMode.Main, 128, new IntegerCollection());
This will display sphere on origin with radius=10;

You can wait for the graphics flush:
tr.TransactionManager.QueueForGraphicsFlush();
then prompt for input so the user has time to see the update:
PromptKeywordOptions pko = new PromptKeywordOptions("\nKeep Changes?");
pko.AllowNone = true;
pko.Keywords.Add("Y");
pko.Keywords.Add("N");
pko.Keywords.Default = "Y";
PromptResult pkr = ed.GetKeywords(pko);
if (pkr.StringResult == "Y") {
tr.Commit();
} else {
tr.Abort();
}
This link provides an example application using this technique.

Related

How to vertically format embedded fields

Current Formatting For Embed Fields
Here is an embed I currently use for my semi-public Ark Servers.
First field is the Map name,
Second field is the direct connect IP Address,
Third field is if/where there is a community base on that map.
As you can see it works as intended but if there's to much info on a single line in the field the formatting is screwed up. Is there a way to fix this?
I'm using 3 separate stream builders to build the different fields and then adding them to the embed. If code is needed I can post a "dumbed down version" so it doesn't take up the whole page.
var linkHeading = "steam://connect/";
var sb = new StringBuilder();
var sb2 = new StringBuilder();
var sb3 = new StringBuilder();
var embed = new EmbedBuilder();
embed.WithColor(new Color(0, 255, 0));
embed.Title = "List of Server Ips";
JObject o1;
using (StreamReader file = File.OpenText("serverips.json"))
using (JsonTextReader reader = new JsonTextReader(file))
{
o1 = (JObject)JToken.ReadFrom(reader);
}
var ipsObject = JsonConvert.DeserializeObject<Rootobject>(o1.ToString());
sb.AppendLine("The Island: ");
sb2.AppendLine($"{linkHeading}{ipsObject.TheIsland.ip}:{ipsObject.TheIsland.port}/");
if(ipsObject.TheIsland.comm != "")
{
sb3.AppendLine($"Comm: {ipsObject.TheIsland.comm}");
} else { sb3.AppendLine($"No Comm Info Available"); };
sb.AppendLine("Aberration: ");
sb2.AppendLine($"{linkHeading}{ipsObject.Aberration.ip}:{ipsObject.Aberration.port}/");
if (ipsObject.Aberration.comm != "")
{
sb3.AppendLine($"Comm: {ipsObject.Aberration.comm}");
} else { sb3.AppendLine($"No Comm Info Available"); };
embed.WithDescription($"Cluster Ip and Comm Information");
embed.AddField(x =>
{
x.Name = "Map";
x.Value = sb.ToString();
x.IsInline = true;
});
embed.AddField(x =>
{
x.Name = "IP";
x.Value = sb2.ToString();
x.IsInline = true;
});
embed.AddField(x =>
{
x.Name = "Comm?";
x.Value = sb3.ToString();
x.IsInline = true;
});
await Context.User.SendMessageAsync(null, false, embed.Build());
await ReplyAsync("Server Ip List was sent directly to your inbox! :)");
You don't have that much control over how embed fields are displayed. The only thing you control in regards to fields are if they inline or not. The rendering is completely up to Discord and the end users screen size. For example, your current output on mobile will ignore the inline setting and list the fields one on top of the other instead of side by side.
Unless your fields consistently contain a small amount of text each you can't guarantee how the end use will see the output. If you need to guarantee some sort of consistent structured display across all devices, your best bet is to use an image.

AutoCAD C# Release .dwl file

Hello AutoCAD C# Masters,
I have this code down here that opens a drawing, change some layers and close, but after it's done and the drawing closes, there is a file named DocName.dwl in the same folder as the dwg that I can't delete without closing autocad.
Is there a place where I can release the lock and I'm not doing?
[CommandMethod("Test", CommandFlags.UsePickSet | CommandFlags.Redraw | CommandFlags.Session | CommandFlags.Modal)]
public void Test()
{
var DocList = AskUserToSelectDocs();
foreach (string FileName in DocList.Files)
{
Application.DocumentManager.Open(FileName, false);
Document zcDoc = Application.DocumentManager.MdiActiveDocument;
Database zcDB = zcDoc.Database;
using (DocumentLock acLckDoc = zcDoc.LockDocument())
{
using (Transaction ZcTran = zcDoc.TransactionManager.StartTransaction())
{
BlockTable zcBLT = (BlockTable)ZcTran.GetObject(zcDB.BlockTableId, OpenMode.ForWrite);
BlockTableRecord zcBLTR = (BlockTableRecord)ZcTran.GetObject(zcBLT[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
zcBLTR.UpgradeOpen();
var editor = zcDoc.Editor;
var SelectionSet = editor.SelectAll().Value;
foreach (ObjectId Objeto in SelectionSet.GetObjectIds())
{
Entity ent = ZcTran.GetObject(Objeto, OpenMode.ForWrite) as Entity;
if (ent is Viewport)
{
var VP = ent as Viewport;
VP.Layer = "Defpoints";
}
}
editor.Regen();
ZcTran.Commit();
}
}
zcDB.SaveAs(zcDB.Filename, zcDB.OriginalFileVersion);
zcDoc.CloseAndSave(zcDoc.Name);
zcDoc.Dispose();
}
}
EDIT: Now the full code
Thanks!
For something like this, there is no need to open the document. I would suggest just loading the database into memory, manipulating it, then saving. All of this will be outside the 'DocumentManager'. Great for performance, especially as the number of files to process increases.
It does not matter which document is currently open, as long as it is not one of the ones getting processed (you can check for that).
Try this:
public static void Test()
{
var DocList = AskUserToSelectDocs();
//save working db reference
Database originalDB = HostApplicationServices.WorkingDatabase;
foreach (string FileName in DocList.Files)
{
//a little trick here:
//construct the database in memory, and read in the target file.
//now your database is your working database, not the active doc!!
using (Database database = new Database(false, true))
{
database.ReadDwgFile(FileName, System.IO.FileShare.ReadWrite, true, string.Empty);
HostApplicationServices.WorkingDatabase = database;//important!
using (Transaction transaction = database.TransactionManager.StartTransaction())
{
//do your stuff
}
//reset WorkingDB to original db
HostApplicationServices.WorkingDatabase = originalDB;
database.SaveAs(FileName, DwgVersion.Current);
}
}
}

Copy dimensions from a View to another Revit API

I'm trying to create a new plug-in on Revit 2016/2017 with the API.
The idea is to copy a set of element from small revit file to a central one to compile them.
Here is the code I'm using :
FilterableValueProvider provider = new ParameterValueProvider(new ElementId(BuiltInParameter.ALL_MODEL_TYPE_NAME));
FilterRule rule = new FilterStringRule(provider, new FilterStringContains(), "BY_GO", false);
ElementParameterFilter epf = new ElementParameterFilter(rule, true);
ICollection<ElementId> npText = new FilteredElementCollector(secDoc, secView.Id).WherePasses(epf).ToElementIds();
using (TransactionGroup tx = new TransactionGroup(mainDoc, "Insert " + Main._roomFile.Typology))
{
ICollection<ElementId> pastedElements;
tx.Start();
using (Transaction tr = new Transaction(mainDoc, "Copy elements"))
{
tr.Start();
pastedElements = ElementTransformUtils.CopyElements(secView, npText, mainView, null, new CopyPasteOptions());
tr.Commit();
}
using (Transaction tr = new Transaction(mainDoc, "Move elements"))
{
tr.Start();
pastedElements = new FilteredElementCollector(mainDoc, pastedElements).WherePasses(epf).ToElementIds();
XYZ originePoint = new FilteredElementCollector(mainDoc, pastedElements).OfClass(typeof(Floor)).First().get_BoundingBox(null).Min;
XYZ translation = extremitePoint - originePoint;
translation = new XYZ(translation.X, translation.Y, 0);
ElementTransformUtils.MoveElements(mainDoc, pastedElements, translation);
tr.Commit();
}
tx.Assimilate();
}
When I use it, everything is good except the dimensions. They are inside the new document (I can get them with there id and RevitLookup) but they are hidden. If I select on of them and add a witness line the dimension is now visible again.
I tried to close and reopen Revit and place the vien on a sheet bt nothing.
Any idea ?
Thank you !
You need to regenerate the view I believe.
Try adding:
Document.Regenerate();
Here is the answer to the probleme.
It's Autodesk which have to solve it but the workaround is to create Dimensions with the reference of invisible one and then delete them.

How do I get all the entities on a specific layer in a .DWG file without opening the file (C#.NET/AutoCAD)?

I am writing a C#.NET program that interacts with AutoCAD through the AutoCAD .NET API. The program loops through DWG files in a directory and checks every text entity on the "testLayer" layer to see if it matches "testText". I got this to work by opening up every file and making a Selectionfilter to get all of the entities on the "testLayer" layer.
Application.DocumentManager.Open(curfile.FullName, false);
....
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction acTrans = doc.TransactionManager.StartTransaction())
{
ObjectIdCollection ents = new ObjectIdCollection();
// Set up filter and filter on layer name
TypedValue[] tvs = new TypedValue[1] { new TypedValue((int)DxfCode.LayerName, "testLayer")};
SelectionFilter sf = new SelectionFilter(tvs);
PromptSelectionResult psr = ed.SelectAll(sf);
if (psr.Status == PromptStatus.OK)
{
// Get the object ids for all of the entities for the filtered layer
ents = new ObjectIdCollection(psr.Value.GetObjectIds());
foreach (ObjectId objid in ents)
{
DBText dbText = acTrans.GetObject(objid, OpenMode.ForRead) as DBText;
if (dbText.TextString.Contains("testText")
{
return dbText.TextString;
}
}
return "";
}
else
{
return "";
}
}
}
But now I am converting my program to side-load the underlying databases because it was taking too long for the program to open and close every .DWG file. The problem is that now I am using
db.ReadDwgFile(currentDWG, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
to read files without actually opening them so I can't use
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor
and
ed.SelectAll(sf) for the selection filter strategy I was using earlier because the document isn't actually open. So how can I can get all of the text entities on each layer named "testLayer" without actually opening the DWG file?
In a 'side database', to mimic SelectAll, you have to iterate through all entities in all the layouts and check the entity layer.
EDIT: In a 'side database', to mimic SelectAll, you have to iterate through all entities in all the layouts and check the entity type and layer.
private IEnumerable<ObjectId> GetTextEntitiesOnLayer(Database db, string layerName)
{
using (var tr = db.TransactionManager.StartOpenCloseTransaction())
{
var blockTable = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
foreach (ObjectId btrId in blockTable)
{
var btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
var textClass = RXObject.GetClass(typeof(DBText));
if (btr.IsLayout)
{
foreach (ObjectId id in btr)
{
if (id.ObjectClass == textClass)
{
var text = (DBText)tr.GetObject(id, OpenMode.ForRead);
if (text.Layer.Equals(layerName, System.StringComparison.CurrentCultureIgnoreCase))
{
yield return id;
}
}
}
}
}
}
}

Autocad API detect shapes inside layer

I am using Autocad 2012 with the API provided. I am developing in c#.
What I am trying to do is select a certain layer, and "detect" all rectangles / squares in that layer. Ultimateley, I would like to be able to draw inside of all of those rectangles that I have "detected" (using their coordinates).
So far, I am using the LayerTable class along with GetObjects to associate layers with objects, like so:
LayerTable layers;
layers = acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForRead) as LayerTable;
String layerNames = "";
foreach (ObjectId layer in layers)
{
LayerTableRecord layerTableRec;
layerTableRec = acTrans.GetObject(layer, OpenMode.ForRead) as LayerTableRecord;
layerNames += layerTableRec.Name+"\n";
}
I can't seem to figure out where to go from here though. How to select just one layer, and then detect shapes inside of it. Can someone point me in the correct direction, in terms of what classes / methods to look into? Thanks.
Ultimately, you need to take another look at the AutoCAD object model. The BlockTableRecord "ModelSpace" is what will contain all* of the AutoCAD entities that have layer assignments. Once you have the BlockTableRecord open for read, you can filter down to entities matching whatever layers you're interested in. LINQ can come in handy here.
You don't actually care about the layer's objectID in this instance, just the name. You only really open up the LayerTableRecord when you want to change a layer. If you'll be changing entity properties, you really need to familiarize yourself with the Transaction class. There's also a faster alternative to using 'As' in AutoCAD by leveraging RXObject.GetClass().
*Entities can also live in other BlockTableRecords (any additional layouts for example) but for now you'll likely be fine with just modelspace.
Here's a little snippet to get you started:
var acDoc = Application.DocumentManager.MdiActiveDocument;
var acDb = acDoc.Database;
using (var tr = database.TransactionManager.StartTransaction())
{
try
{
var entClass = RXObject.GetClass(typeof(Entity));
var modelSpaceId = SymbolUtilityServices.GetBlockModelSpaceId(acDb);
var modelSpace = (BlockTableRecord)tr.GetObject(modelSpaceId, OpenMode.ForRead);
foreach (ObjectId id in modelSpace)
{
if (!id.ObjectClass.IsDerivedFrom(entClass)) // For entity this is a little redundant, but it works well with derived classes
continue;
var ent = (Entity)tr.GetObject(id, OpenMode.ForRead)
// Check for the entity's layer
// You'll need to upgrade the entity to OpenMode.ForWrite if you want to change anything
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
acDoc.Editor.WriteMessage(ex.Message);
}
}
var acDoc = Application.DocumentManager.MdiActiveDocument;
var acDb = acDoc.Database;
using (var tr = database.TransactionManager.StartTransaction())
{
try
{
var entClass = RXObject.GetClass(typeof(Entity));
var modelSpaceId = SymbolUtilityServices.GetBlockModelSpaceId(acDb);
var modelSpace = (BlockTableRecord)tr.GetObject(modelSpaceId, OpenMode.ForRead);
foreach (ObjectId id in modelSpace)
{
Entity acEnt = (Entity)tr.GetObject(id, OpenMode.ForRead);
string layerName = acEnt.Name;
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
acDoc.Editor.WriteMessage(ex.Message);
}
}

Categories

Resources