In the last part we left off having obtained all the bolt distance and placing them in a domain object. In this instalment we will try to export all that data into an Excel Spreadsheet. Please note that the following code is untested – unfortunately there was a lightening storm in Melbourne which short circuited my flux capacitor which means I cannot connect to the TeklaServer – so rather than wait, I thought to get this code out to you.
Which library to write to Excel?
There are many libraries out there: XLS compatible and not:
OpenXML libraries
ClosedXML libraries
NPOI
EPPlus
The consensus is that the worst of the above is still better than using Microsoft’s office interop dll. If you use that approach, you will need to ensure that MS Office is installed in your deployment machine, and secondly, be sure to dispose of all relevant objects. If you forget, then you’ll be leaking memory. This is a very important point.
How to use ClosedXML in your code:
Firstly download closedXML using NuGet Package Manager. That should add the relevant references.
Secondly add the `using ClosedXML.Excel` directives.
Then add the code snippets I’ve provided for you below:
Notes on the code:
* A significant change has been made – we are now filtering the SinglePartDrawings based on: (i) whether they are beams or not and (ii) whether they have the relevant profile – a reader wrote an email asking for this version of the code. I have left the previous version out there as well.
* I’m not an expert with ClosedXML – I just wanted to get the code out there. So it’s a very hackish and non-elegant solution, but I hope it serves to illustrate the point.
Have you ever fallen down a flight of stairs? I hope not! But from experience I can tell you that it’s not a very pleasant one. I slipped as I was walking down – I fell supine, hard, like a hammer on a nail, bang into the corner of the steps. The pain was absolutely numbing – I could walk for about three days, nor could I even roll over in bed for about that same period. Falling down stairs is a dangerous business – and if you’re in the business of designing or fabricating stairs – especially public access stairs, then you absolutely have to get it right. Because if you don’t, then it’s only a matter of time till someone falls. Luckily I was a young man, so I recovered pretty quickly. But if I was an invalid, a fall like that could be potentially life threatening!
Here is an example of a badly designed stair:
An example of a staircase which was not made according to AS specifications or perhaps any sort of specifications apart from the builder’s convenience I suppose. This type of shoddy workmanship will be the cause of many injuries and accidents. Designing structures according to the specifications mandated is absolutely essential.
The steps are not uniform – they vary in height and length. This is not safe if you are traversing it. It’s easy to misjudge. That’s why when we do the shop drawings for a flight of stairs we check that it’s uniform, that you don’t have too many stairs in a flight, that there’s adequate room, that a child cannot squeeze his/her head in between the treads, etc. I have ascended and descended these steps – and were it not for the handrails, it would be very dangerous. Don’t do steps like this. Here were the measurement from the bottom riser going up:
29 cm
20 cm
20 cm
18 cm
17.5 cm
15.5 cm
The risers vary too much!
Our staff are trained to ensure that their stairs comply with Australian Standards. We’re definitely not the cheapest, but we’ll know if we see a bad design – and knowing that information could save you a bundle.
A pictorial representation of how Entity Framework, in the world of code, would look if it was a corporeal object.
The problem with using a database, when you have another primary source of information, is that the database needs to be updated. Constantly. If someone forgets to update the database, then you will be relying on information that is old/erroneous and not updated. That’s a huge risk. It’s the type of thing that you want to do only if your staff are disciplined, and the gravity of failure is low, should they forget. But if the reverse is true, then you’re sure to eat humble pie, and cause a lot of needless trouble and expense for yourself and all you deal with.
There was a political war over the implementation: I was for using the original database, and the boss was for creating a new one. Accuracy vs speed. Speed won the victory. And I must oblige by constantly updating a database with panel information.
What are these guys doing, you ask? I suspect that they are jigging a line. They are probably doing it this way because they didn’t read the ObjectARX documentation. Well actually, you don’t need to. Just use the poor man’s jig.
I wanted to implement a jig for drawing a Line – but strictly speaking I didn’t want the line itself – I wanted its two points, yet I wanted all the features that come with jigging: snaps, polar tracking, and a nice line leading from the base point to the cursor, which shows you where you were, and where you are going. I was originally going to jig it all myself – and all of this to obtain two coordinates in a manner that would allow the user to see what was actually going on. Jiggig takes a lot of effort. It was only then that I realised I could get the same result, but with a massive short cut:
Here is a poor man’s Line Jig – at the end of it, you’ll have the two points you are after, but without the effort. If required, you can encapsulate all the logic in it’s own class, so that the client doesn’t have to bother too much with the implementation details.
These are steps. When you get to the top, you’ll be an AutoCAD master programmer.
I’ve compiled a list. There’s actually quite a bit involved. I don’t think you can get away with simply not knowing anything about unamanged ObjectARX world. Here is the list below – which I will update. If you see any notable topics which I have missed, please feel free to add a note and I will update the list.
Documentation of the GetClosestPointTo method of the curve object – the overloads are extremely limited. So we have to be somewhat creative in obtaining a solution.
The Genesis of this problem
This is a tricky little problem and I could not find a solution on the forums. So I resolved, upon discovering the solution, to oblige posterity and the public, to publish my findings.
Specific Notes about this problem
Now the following code has been generalised to the specific case of Lines and a non-descript curve (which of course is an abstract base class), but the general principles can be applied to any type of curve.
Notes on implementation
Unfortunately, the curve object exposes a method: GetClosestPointTo, which is only overloaded to accept points and not other curves. In order to deal with this rigmarole we’ll have to first, convert the curve to a `Curve3d` object which is a member of the Autodesk.AutoCAD.Geometry namespace as distinct from the Autodesk.AutoCAD.DatabaseServices namespace.
We have someone who is pointing to the monitor. Makes perfect sense right?
This is largely a replication of what is contained in the documentation without the extraneous (and extremely confusing) intertwining of WPF/WinForms functionality. Personally I believe when engaging in hello world functions that they should be as simple as possible.
I have done as much here.
In time, I will add further details to provide you with some more information on the classes that are used. The main thing to pay attention to here is the FullSubentityPath class as well as the PointMonitorEventArgs.AppendToolTipText(string) method. More details will follow later on. For the moment, parse this code and try to understand what you can:
A physical representation of a Result Buffer outside the world of ObjectARX. hahha.
Unfortunately, and unsurprisingly, the documentation is a little sparse on this point. But somewhere deep within the annals of the AutoCAD documentation I found this little beauty:
Here is the documentation. But for you folks who may find that the link does not work, I will also copy and paste verbatim, what is said there:
The ResultBuffer type is a class that mirrors the resbuf struct defined in the ObjectARX SDK. The resbuf struct provides a flexible container for AutoCAD-specific data.
An Autodesk.AutoCAD.DatabaseServices.ResultBuffer class object is used in much the same way as a resbuf chain. You define a ResultBuffer and populate it with a sequence of data pairs. Each pair contains a data type description and a value. In the managed API, these data pairs are instances of theAutodesk.AutoCAD.DatabaseServices.TypedValue class. This utility class serves the same purpose as the restype and resval members of the resbuf struct.
The TypedValue.TypeCode property is a 16-bit integer value that indicates the TypedValue.Value property’s data type. Acceptable TypeCode values depend on the specific use of aResultBuffer instance. For example, TypeCode values that are suitable for an xrecord definition are not necessarily appropriate for xdata. TheAutodesk.AutoCAD.DatabaseServices.DxfCode enum defines codes that accurately describe the full range of possible ResultBuffer data types.
The TypedValue.Value property maps to an instance of System.Object, and theoretically may contain any type of data. However, the Value data must conform to the type indicated by TypeCode to guarantee usable results.
You can prepopulate a ResultBuffer by passing an array of TypedValue objects to its constructor, or you can construct an empty ResultBuffer and later call theResultBuffer::Add() method to append new TypedValue objects. The following example shows a typical ResultBuffer constructor usage:
using (Xrecord rec = new Xrecord())
{
rec.Data = new ResultBuffer(
new TypedValue(Convert.ToInt32(DxfCode.Text), “This is a test”),
new TypedValue(Convert.ToInt32(DxfCode.Int8), 0),
new TypedValue(Convert.ToInt32(DxfCode.Int16), 1),
new TypedValue(Convert.ToInt32(DxfCode.Int32), 2),
new TypedValue(Convert.ToInt32(DxfCode.HardPointerId), db.BlockTableId),
new TypedValue(Convert.ToInt32(DxfCode.BinaryChunk), new byte[] {0, 1, 2, 3, 4}),
new TypedValue(Convert.ToInt32(DxfCode.ArbitraryHandle), db.BlockTableId.Handle),
new TypedValue(Convert.ToInt32(DxfCode.UcsOrg),
new Point3d(0, 0, 0)));
}
What does all this mean?
I can’t justify an entire post without adding some degree of value: let’s try simplify its meaning a little more. Basically, result buffers are like CLR dictionaries: they are made up of a series of TypedValue objects. These TypedValue objects are like Key-Value pairs which you can fill with different types of data (i.e. AutoCAD data), or even non-AutoCAD data. The values you can use for the Key (in a TypedValue object) are basically that which are specified as DxfCode values.
Result Buffers are:
Made of TypedValue objects.
Typed value objects are like Key Value pairs
Where the key of the TypedValue is a DxfCode and
The value of the TypedValue is any AutoCAD type which corresponds to the DxfCode set as the key.
I hope that’s making some degree of sense? It will be a sad day if such an explanation obfuscates what is written in the documentation (where it exists) to a greater degree than the documentation itself. Anyways, I do hope it helps.