Hey all,
I am trying to get coordinates of objects on a sheet, as oppose to coordinates of the element in the model.
I am generating XPS, then PNG files of the viewsheet at 150 pixels per inch. So that is my scale. From what I understand according to this post the units are in feet, however that does not seem to add up for me. Because I can see there is both a translation and scale transformation for the elements locations to align with the coordiantes from get_BoundingBox.
This is the sum of what coordinates I am executing:
var bounding = element.get_BoundingBox(viewSheet);
File.AppendAllText(path, $"{element.Name}, {group.Key}, {bounding?.Max.X}, {bounding?.Max.Y}, {bounding?.Min.X}, {bounding?.Min.Y}\n");
Where viewSheet is a ViewSheet object.
I tried looking at viewport.get_BoundingBox(viewSheet);. But that seems to give me numbers that are too small in relation to the sift need for the elements to the right location on the sheet.
How would I get the exact location on a XPS/PDF sheet of an element?
Thanks,
Solved! Go to Solution.
Solved by RPTHOMAS108. Go to Solution.
Solved by RPTHOMAS108. Go to Solution.
Hey @jeremytammik ,
I went over a few of your blog posts, and now over those two in particular.
What I am doing is a little different, because I am eventually trying to figure out the coordinates on an XPS file (or PDF). So I don't only need the coordinates in relation between the sheet and the viewport, but also between the sheet and the actual XPS.
I've been trying different things in the past few house using the rac_basic_sample_project.rvt plan from the example projects.
For simplify I am using the sheet A1 - Floor Plan. Which has origin at 0 and scale 1 so I know that its not a problem as described in your second link (unless if there is a difference between a Plan class and a SheetView class that I am missing).
I am drawing all the doors, and it seems they make a shape that resembles the door order, so I know that this gives me the door elements in some coordinates system:
var bounding = element.get_BoundingBox(viewSheet);
However I can't figure out which one, and then I don't know how to plug that back to the sheet.
The door values move form -50 to +180, plotbelow.
I also draw a rectangle of (viewSheet.Outline.Min.U,viewSheet.Outline.Min.V), (viewSheet.Outline.Max.U,viewSheet.Outline.Max.V). Which is -100, 100. That does not add up because there are doors further out at +180.
I was generally looking for coordinates that added up will pass 180 so I know all doors are included.
But all of the following values yielded number below:
var viewsheet_bounding = viewSheet.get_BoundingBox(viewSheet);
var outline = viewport.GetBoxOutline();
double scale = Convert.ToDouble(viewSheet.Scale);
viewSheet.Origin.X / scale;
viewSheet.Origin.Y / scale;
var viewport_bounding = viewport.get_BoundingBox(viewSheet);
viewSheet.Outline.Min.U; // shown above in plot
viewSheet.Outline.Min.V; // shown above in plot
All these give far too smaller numbers.
I also tried rac_advanced_sample_project.rvt with similar results at the sheet E201 - Unnamed floor.
Can you point me what kind of data am I missing to do coordinate transformation, namely the translation?
(I found that the scale change is about ~3.8 for 150 DPI, I am not sure why because I don't know the units, but at least I can do that empirically there).
The first thing to note is that you are using rac_advanced_sample_project.rvt not the basic project since this is the only project with the named view sheet you mentioned.
I've included some code that plots bounding box position of doors to the sheet coords system. The transform is important even for plan views as although these usually align with the model coord system sometimes they are rotated:
The sheet coord system is from bottom left to top right. I don't know how the system in PDF/XPS is measured but a further translation may be required. It is important to note that the title block can be placed anywhere in the sheet space but when it is printed this space obviously gets truncated to the extents of the title block. Therefore the location of things relative to the title block is more relevant than the absolute sheet coords themselves.
This below is in VB, can easily be converted to C#.
Private Shared Function GetElements(Of T As Element)(Doc As Document, Optional View As View = Nothing) As List(Of T)
Dim FEC As FilteredElementCollector
If View Is Nothing Then
FEC = New FilteredElementCollector(Doc)
Else
FEC = New FilteredElementCollector(Doc, View.Id)
End If
Dim ECF As New ElementClassFilter(GetType(T))
Dim Out As List(Of T) = FEC.WherePasses(ECF).ToElements.Cast(Of T).ToList
Return Out
End Function
Private Shared Function GetElements(Doc As Document, BIC As BuiltInCategory, IsType As Boolean,
Optional View As View = Nothing) As List(Of Element)
Dim FEC As FilteredElementCollector
If View Is Nothing Then
FEC = New FilteredElementCollector(Doc)
Else
FEC = New FilteredElementCollector(Doc, View.Id)
End If
Dim ECF As New ElementCategoryFilter(BIC)
Dim Out As List(Of Element)
If IsType Then
Out = FEC.WherePasses(ECF).WhereElementIsElementType.ToElements
Else
Out = FEC.WherePasses(ECF).WhereElementIsNotElementType.ToElements
End If
Return Out
End Function
Private Function TObj116(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result
If commandData.Application.ActiveUIDocument Is Nothing Then Return Result.Cancelled Else
Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
Dim Doc As Document = UIDoc.Document
Dim AcView As ViewSheet = TryCast(Doc.ActiveView, ViewSheet)
If AcView Is Nothing Then Return Result.Cancelled Else
Dim FEC As New FilteredElementCollector(Doc, AcView.Id)
Dim VP_lst As List(Of Viewport) = GetElements(Of Viewport)(Doc, AcView)
Dim TB_Lst As List(Of Element) = GetElements(Doc, BuiltInCategory.OST_TitleBlocks, False, AcView)
If TB_Lst.Count <> 1 Then
'We need this to be the only one in the view
'We need to find the minimum bounding box point as that is most important for the printed sheet
Return Result.Cancelled
End If
'We use these points only to relate the sheet coord system to the printed lines (offset from corner of title block)
'i.e. title block can be located anywhere in sheet coord space but when it is printed
'such a coord space will be truncated to the extents of the title block
Dim TB_Pt_min = TB_Lst(0).BoundingBox(AcView).Min
Dim TB_Pt_max = TB_Lst(0).BoundingBox(AcView).Max
TB_Pt_min = New XYZ(TB_Pt_min.X, TB_Pt_min.Y, 0) 'Zero Z
TB_Pt_max = New XYZ(TB_Pt_max.X, TB_Pt_max.Y, 0) 'Zero Z
Dim TB_TopLeft = New XYZ(TB_Pt_min.X, TB_Pt_max.Y, 0)
Using Tx As New Transaction(Doc, "Door points on sheet")
If Tx.Start = TransactionStatus.Started Then
For VPi = 0 To VP_lst.Count - 1
Dim VPrt As Viewport = VP_lst(VPi)
Dim V As View = Doc.GetElement(VPrt.ViewId)
Dim Doors As List(Of Element) = GetElements(Doc, BuiltInCategory.OST_Doors, False, V)
Dim VPoln As Outline = VPrt.GetBoxOutline 'Viewport outline in Sheet coords
Dim Voln As BoundingBoxUV = V.Outline 'View outline
Dim Scale As Integer = V.Scale
'Transform for view coords (very important for rotated view plans set to true north etc.)
'completely not important when view plan is not rotated
Dim T As Transform = Transform.Identity
T.BasisX = V.RightDirection
T.BasisY = V.UpDirection
T.BasisZ = V.ViewDirection
T.Origin = V.Origin
'You can probably get this transform above from elsewhere such as cropbox.
Dim Voln_cen = (Voln.Min + Voln.Max) / 2 'View outline centre
Dim VPcen As XYZ = (VPoln.MaximumPoint + VPoln.MinimumPoint) / 2 'Viewport centre
VPcen = New XYZ(VPcen.X, VPcen.Y, 0) 'Zero z
'Correction offset from VCen to centre of Viewport in sheet coords
Dim Offset As XYZ = VPcen - New XYZ(Voln_cen.U, Voln_cen.V, 0)
For DRi = 0 To Doors.Count - 1
Dim Dr As Element = Doors(DRi)
Dim BB As BoundingBoxXYZ = Dr.BoundingBox(V)
'Location of door bounding box in sheet coords
Dim J1 As XYZ = T.Inverse.OfPoint(BB.Min).Multiply(1 / Scale) + Offset
Dim J2 As XYZ = T.Inverse.OfPoint(BB.Max).Multiply(1 / Scale) + Offset
'These points are plotted in the sheet coord space
'This may not relate to pdf space i.e. location of title block is important
J1.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
J2.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
'TODO Convert to PDF/XPS graphics space system.
'Use TB_pt_min & TB_pt_max & TB_TopLeft
'I am unsure of the coord system of XPS/PDF (graphics are sometimes plotted downwards from top left point
'not up from bottom right as considered above in the sheet coord system
'therefore need to transform into that space if applicable
'e.g. Y = ScreenHeight - OffsetFromBottom
Next
Next
Tx.Commit()
End If
End Using
End Function
public Result TObj116(Autodesk.Revit.UI.ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
{
if (commandData.Application.ActiveUIDocument == null)
return Result.Cancelled;
UIDocument UIDoc = commandData.Application.ActiveUIDocument;
Document Doc = UIDoc.Document;
ViewSheet AcView = Doc.ActiveView as ViewSheet;
if (AcView == null)
return Result.Cancelled;
List<Viewport> VP_lst = GetElements<Viewport>(Doc, AcView);
IList<Element> TB_Lst = GetElements(Doc, BuiltInCategory.OST_TitleBlocks, false, AcView);
if (TB_Lst.Count != 1)
{
//We need this to be the only one in the view
//We need to find the minimum bounding box point as that is most important for the printed sheet
return Result.Cancelled;
}
//We use these points only to relate the sheet coord system to the printed lines (offset from corner of title block)
//i.e. title block can be located anywhere in sheet coord space but when it is printed
//such a coord space will be truncated to the extents of the title block
XYZ TB_Pt_min = TB_Lst[0].get_BoundingBox(AcView).Min;
XYZ TB_Pt_max = TB_Lst[0].get_BoundingBox(AcView).Max;
TB_Pt_min = new XYZ(TB_Pt_min.X, TB_Pt_min.Y, 0);
//Zero Z
TB_Pt_max = new XYZ(TB_Pt_max.X, TB_Pt_max.Y, 0);
//Zero Z
dynamic TB_TopLeft = new XYZ(TB_Pt_min.X, TB_Pt_max.Y, 0);
using (Transaction Tx = new Transaction(Doc, "Door points on sheet"))
{
if (Tx.Start() == TransactionStatus.Started)
{
for (int VPi = 0; VPi <= VP_lst.Count - 1; VPi++)
{
Viewport VPrt = VP_lst[VPi];
View V = (View)Doc.GetElement(VPrt.ViewId);
IList<Element> Doors = GetElements(Doc, BuiltInCategory.OST_Doors, false, V);
Outline VPoln = VPrt.GetBoxOutline();
//Viewport outline in Sheet coords
BoundingBoxUV Voln = V.Outline;
//View outline
int Scale = V.Scale;
//Transform for view coords (very important for rotated view plans set to true north etc.)
//completely not important when view plan is not rotated
Transform T = Transform.Identity;
T.BasisX = V.RightDirection;
T.BasisY = V.UpDirection;
T.BasisZ = V.ViewDirection;
T.Origin = V.Origin;
//You can probably get this transform above from elsewhere such as cropbox.
dynamic Voln_cen = (Voln.Min + Voln.Max) / 2;
//View outline centre
XYZ VPcen = (VPoln.MaximumPoint + VPoln.MinimumPoint) / 2;
//Viewport centre
VPcen = new XYZ(VPcen.X, VPcen.Y, 0);
//Zero z
//Correction offset from VCen to centre of Viewport in sheet coords
XYZ Offset = VPcen - new XYZ(Voln_cen.U, Voln_cen.V, 0);
for (int DRi = 0; DRi <= Doors.Count - 1; DRi++)
{
Element Dr = Doors[DRi];
BoundingBoxXYZ BB = Dr.get_BoundingBox(V);
//Location of door bounding box in sheet coords
XYZ J1 = T.Inverse.OfPoint(BB.Min).Multiply((Double)1 / Scale) + Offset;
XYZ J2 = T.Inverse.OfPoint(BB.Max).Multiply((Double)1 / Scale) + Offset;
J1 = new XYZ(J1.X, J1.Y, 0);
J2 = new XYZ(J2.X, J2.Y, 0);
//These points are plotted in the sheet coord space
//This may not relate to pdf space i.e. location of title block is important
//J1.DrawPoint2D(Doc, 0.01);
//EXTENSION METHOD
//J2.DrawPoint2D(Doc, 0.01);
//EXTENSION METHOD
Line LN = Line.CreateBound(J1, J2);
Doc.Create.NewDetailCurve(AcView, LN);
//TODO Convert to PDF/XPS graphics space system.
//Use TB_pt_min & TB_pt_max & TB_TopLeft
//I am unsure of the coord system of XPS/PDF (graphics are sometimes plotted downwards from top left point
//not up from bottom right as considered above in the sheet coord system
//therefore need to transform into that space if applicable
//e.g. Y = ScreenHeight - OffsetFromBottom
}
}
Tx.Commit();
}
}
return Result.Succeeded;
}
Hey RPTHOMAS108,
Where I am currently stuck:
The following function is missing and I can't find it in the API so I expect you extended it:
J1.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
I could try and run your code if you provide it. That would give me something to try out and see how I get it to work with the rest of my C sharp code.
Here is more detail:
I know a little of VB script and C#, I tend to do most of my daily stuff with Python. None the less I want to get this working end to end in any language first. Only then I will be conferrable translating between languages. Its 130+ lines I will likely make a bug without feedback from running code.
I have have a "hello world" program IExternalCommand.Execute compiled and working both for VB and c sharp. So pick either as long as its consistent.
Thanks for pointing out the right file and trying it out.
In answer to your notes:
The sheet coord system for XPS is the same as PDF, which is also boom left to top right.
Truncation might be an issue so lets try getting the drawing printed out with Revit because that would let me compare what comes out the other side and how to manipulate it to be in the right place.
I have managed to compile and run without the drawing function by calling it like this:
Public Class Class1
Implements IExternalCommand
Public Function Execute(ByVal revit As ExternalCommandData, ByRef message As String,
ByVal elements As ElementSet) As Autodesk.Revit.UI.Result _
Implements IExternalCommand.Execute
TaskDialog.Show("Revit", "Hello World VB")
TObj116(revit, message, elements)
TaskDialog.Show("Revit", "VB Done")
Return Autodesk.Revit.UI.Result.Succeeded
End Function
I have also attached the full VB script for the class as a txt file.
Yes if you replace the lines in the code calling the extension method with the below then you'll get the same result as using the method.
I zero the Z values of the J1 and J2 points because a non-zero value is creeping in from one of the outline methods and this will not be permitted for the creation of detail lines. I then construct lines 0.02 Ft centred on the points in the X and Y directions (cross markers).
J1 = New XYZ(J1.X, J1.Y, 0) 'Zero Z
J2 = New XYZ(J2.X, J2.Y, 0) 'Zero Z
'Create line 0.01 ft either side of point in Basis.X and Basis.Y directions
Dim LN0 As Line = Line.CreateBound(J1 - (XYZ.BasisX * 0.01), J1 + (XYZ.BasisX * 0.01))
Dim LN1 As Line = Line.CreateBound(J1 - (XYZ.BasisY * 0.01), J1 + (XYZ.BasisY * 0.01))
Dim LN2 As Line = Line.CreateBound(J2 - (XYZ.BasisX * 0.01), J2 + (XYZ.BasisX * 0.01))
Dim LN3 As Line = Line.CreateBound(J2 - (XYZ.BasisY * 0.01), J2 + (XYZ.BasisY * 0.01))
Doc.Create.NewDetailCurve(AcView, LN0)
Doc.Create.NewDetailCurve(AcView, LN1)
Doc.Create.NewDetailCurve(AcView, LN2)
Doc.Create.NewDetailCurve(AcView, LN3)
Ok, great!
It now compile and I get these cross shapes next to doors:
I have an idea how to solve this now without making complex calculations to where the objects are in the PNG. And not knowing what is trimmed etc.
What I want to do is draw these crosses, but in a specific RGB color. That way I can create two PDFs, one with the RGB crosses, and extract the Rasta images locations. Ideally I want it to be a single pixel and not a cross, Or at least a small circle like you posted in the previous comment.
Is there any way to do that?
Attaching current code.
You can't draw a line that is less than the value set given by Application.ShortCurveTolerance
The red dots were actually the same cross markers but with thin lines turned off in the UI and they were 0.01 ft in length rather than 0.02 ft.
If you know the width of your sheet in pixels and you know the width of your sheet in feet then finding the location of a point can be done by proportions (e.g. similar triangles).
X_Pixels / SheetWidth_Pixels = X_Ft / SheetWidth_Ft
X_Pixels = (SheetWidth_Pixels * X_Ft)/SheetWidth_Ft
e.g.
512 = (1024*0.75) / 1.5
With a sheet width in pixels of 1024, a sheet width in Ft of 1.5 and an X ord in Ft as 0.75 (half the sheet width).
This is not overly complicated but I've lost track of what you original task is exactly? In most cases pdfs produced by Revit contain vector graphics so what is the PNG about?
Ok, though the most pressing thing is the color, because that would let me separate the location mark from the plan. As long as they don't overlap it should work.
PNG output is just converting the PDF to a raster image after the PDF is generated (preset DPI).
How could I change the cross markers to a color of choice set inside the code?
Hey RPTHOMAS108,
You wrote This below is in VB, can easily be converted to C#.""
Well, I got it translated, line by line, and it does not seem to work, any idea what I am missing?
@RPTHOMAS108 wrote:The first thing to note is that you are using rac_advanced_sample_project.rvt not the basic project since this is the only project with the named view sheet you mentioned.
I've included some code that plots bounding box position of doors to the sheet coords system. The transform is important even for plan views as although these usually align with the model coord system sometimes they are rotated:
The sheet coord system is from bottom left to top right. I don't know how the system in PDF/XPS is measured but a further translation may be required. It is important to note that the title block can be placed anywhere in the sheet space but when it is printed this space obviously gets truncated to the extents of the title block. Therefore the location of things relative to the title block is more relevant than the absolute sheet coords themselves.
This below is in VB, can easily be converted to C#.
Private Shared Function GetElements(Of T As Element)(Doc As Document, Optional View As View = Nothing) As List(Of T)
Dim FEC As FilteredElementCollector
If View Is Nothing Then
FEC = New FilteredElementCollector(Doc)
Else
FEC = New FilteredElementCollector(Doc, View.Id)
End If
Dim ECF As New ElementClassFilter(GetType(T))
Dim Out As List(Of T) = FEC.WherePasses(ECF).ToElements.Cast(Of T).ToList
Return Out
End Function
Private Shared Function GetElements(Doc As Document, BIC As BuiltInCategory, IsType As Boolean,
Optional View As View = Nothing) As List(Of Element)
Dim FEC As FilteredElementCollector
If View Is Nothing Then
FEC = New FilteredElementCollector(Doc)
Else
FEC = New FilteredElementCollector(Doc, View.Id)
End If
Dim ECF As New ElementCategoryFilter(BIC)
Dim Out As List(Of Element)
If IsType Then
Out = FEC.WherePasses(ECF).WhereElementIsElementType.ToElements
Else
Out = FEC.WherePasses(ECF).WhereElementIsNotElementType.ToElements
End If
Return Out
End Function
Private Function TObj116(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result
If commandData.Application.ActiveUIDocument Is Nothing Then Return Result.Cancelled Else
Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
Dim Doc As Document = UIDoc.Document
Dim AcView As ViewSheet = TryCast(Doc.ActiveView, ViewSheet)
If AcView Is Nothing Then Return Result.Cancelled Else
Dim FEC As New FilteredElementCollector(Doc, AcView.Id)
Dim VP_lst As List(Of Viewport) = GetElements(Of Viewport)(Doc, AcView)
Dim TB_Lst As List(Of Element) = GetElements(Doc, BuiltInCategory.OST_TitleBlocks, False, AcView)
If TB_Lst.Count <> 1 Then
'We need this to be the only one in the view
'We need to find the minimum bounding box point as that is most important for the printed sheet
Return Result.Cancelled
End If
'We use these points only to relate the sheet coord system to the printed lines (offset from corner of title block)
'i.e. title block can be located anywhere in sheet coord space but when it is printed
'such a coord space will be truncated to the extents of the title block
Dim TB_Pt_min = TB_Lst(0).BoundingBox(AcView).Min
Dim TB_Pt_max = TB_Lst(0).BoundingBox(AcView).Max
TB_Pt_min = New XYZ(TB_Pt_min.X, TB_Pt_min.Y, 0) 'Zero Z
TB_Pt_max = New XYZ(TB_Pt_max.X, TB_Pt_max.Y, 0) 'Zero Z
Dim TB_TopLeft = New XYZ(TB_Pt_min.X, TB_Pt_max.Y, 0)
Using Tx As New Transaction(Doc, "Door points on sheet")
If Tx.Start = TransactionStatus.Started Then
For VPi = 0 To VP_lst.Count - 1
Dim VPrt As Viewport = VP_lst(VPi)
Dim V As View = Doc.GetElement(VPrt.ViewId)
Dim Doors As List(Of Element) = GetElements(Doc, BuiltInCategory.OST_Doors, False, V)
Dim VPoln As Outline = VPrt.GetBoxOutline 'Viewport outline in Sheet coords
Dim Voln As BoundingBoxUV = V.Outline 'View outline
Dim Scale As Integer = V.Scale
'Transform for view coords (very important for rotated view plans set to true north etc.)
'completely not important when view plan is not rotated
Dim T As Transform = Transform.Identity
T.BasisX = V.RightDirection
T.BasisY = V.UpDirection
T.BasisZ = V.ViewDirection
T.Origin = V.Origin
'You can probably get this transform above from elsewhere such as cropbox.
Dim Voln_cen = (Voln.Min + Voln.Max) / 2 'View outline centre
Dim VPcen As XYZ = (VPoln.MaximumPoint + VPoln.MinimumPoint) / 2 'Viewport centre
VPcen = New XYZ(VPcen.X, VPcen.Y, 0) 'Zero z
'Correction offset from VCen to centre of Viewport in sheet coords
Dim Offset As XYZ = VPcen - New XYZ(Voln_cen.U, Voln_cen.V, 0)
For DRi = 0 To Doors.Count - 1
Dim Dr As Element = Doors(DRi)
Dim BB As BoundingBoxXYZ = Dr.BoundingBox(V)
'Location of door bounding box in sheet coords
Dim J1 As XYZ = T.Inverse.OfPoint(BB.Min).Multiply(1 / Scale) + Offset
Dim J2 As XYZ = T.Inverse.OfPoint(BB.Max).Multiply(1 / Scale) + Offset
'These points are plotted in the sheet coord space
'This may not relate to pdf space i.e. location of title block is important
J1.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
J2.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
'TODO Convert to PDF/XPS graphics space system.
'Use TB_pt_min & TB_pt_max & TB_TopLeft
'I am unsure of the coord system of XPS/PDF (graphics are sometimes plotted downwards from top left point
'not up from bottom right as considered above in the sheet coord system
'therefore need to transform into that space if applicable
'e.g. Y = ScreenHeight - OffsetFromBottom
Next
Next
Tx.Commit()
End If
End Using
End Function
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
namespace HelloRevit
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
class Class1 : IExternalCommand
{
public Result Execute(ExternalCommandData revit, ref string message, ElementSet elements)
{
TaskDialog.Show("Revit", "Hello World C#");
TObj116(revit, ref message, elements);
TaskDialog.Show("Revit", "C# Done script");
return Result.Succeeded;
}
private static List<T> GetElements<T>(Document doc, View view = null) where T : Element
{
FilteredElementCollector fec;
if (view == null)
fec = new FilteredElementCollector(doc);
else
fec = new FilteredElementCollector(doc, view.Id);
ElementClassFilter ecf = new ElementClassFilter(typeof(T));
List<T> outR = fec.WherePasses(ecf).ToElements().Cast<T>().ToList();
return outR;
}
private static List<Element> GetElements(Document doc, BuiltInCategory bic, bool isType, View view = null)
{
FilteredElementCollector fec;
if (view == null)
fec = new FilteredElementCollector(doc);
else
fec = new FilteredElementCollector(doc, view.Id);
ElementCategoryFilter ecf = new ElementCategoryFilter(bic);
List<Element> outR;
if (isType)
outR = fec.WherePasses(ecf).WhereElementIsElementType().ToElements().ToList();
else
outR = fec.WherePasses(ecf).WhereElementIsNotElementType().ToElements().ToList();
return outR;
}
private Result TObj116(ExternalCommandData commandData, ref string message, ElementSet elements)
{
if (commandData.Application.ActiveUIDocument == null)
return Result.Cancelled;
UIDocument uiDocument = commandData.Application.ActiveUIDocument;
Document doc = uiDocument.Document;
ViewSheet acView = doc.ActiveView as ViewSheet;
if (acView == null)
return Result.Cancelled;
FilteredElementCollector fec = new FilteredElementCollector(doc, acView.Id);
List<Viewport> vp_lst = GetElements<Viewport>(doc, acView);
List<Element> tb_lst = GetElements(doc, BuiltInCategory.OST_TitleBlocks, false, acView);
if (tb_lst.Count != 1)
// We need this to be the only one in the view
// We need to find the minimum bounding box point as that is most important for the printed sheet
return Result.Cancelled;
/* We use these points only to relate the sheet coord system to the printed lines (offset from corner of title block)
* i.e. title block can be located anywhere in sheet coord space but when it is printed
* such a coord space will be truncated to the extents of the title block */
var tb_pt_min = tb_lst[0].get_BoundingBox(acView).Min;
var tb_pt_max = tb_lst[0].get_BoundingBox(acView).Max;
tb_pt_min = new XYZ(tb_pt_min.X, tb_pt_min.Y, 0); // Zero Z
tb_pt_max = new XYZ(tb_pt_max.X, tb_pt_max.Y, 0); // Zero Z
var tb_TopLeft = new XYZ(tb_pt_min.X, tb_pt_max.Y, 0);
using (Transaction tx = new Transaction(doc, "Door points on sheet"))
{
if (tx.Start() == TransactionStatus.Started)
{
for (int vpi = 0; vpi <= vp_lst.Count - 1; vpi++)
{
Viewport vprt = vp_lst[vpi];
View v = (View)doc.GetElement(vprt.ViewId);
List<Element> doors = GetElements(doc, BuiltInCategory.OST_Doors, false, v);
Outline vpoln = vprt.GetBoxOutline(); // Viewport outline in Sheet coords.
BoundingBoxUV voln = v.Outline; // View outline.
int scale = v.Scale;
/*Transform for view coords (very important for rotated view plans set to true north etc.)
completely not important when view plan is not rotated*/
Transform t = Transform.Identity;
t.BasisX = v.RightDirection;
t.BasisY = v.UpDirection;
t.BasisZ = v.ViewDirection;
t.Origin = v.Origin;
// You can probably get this transform above from elsewhere such as cropbox.
var voln_cen = (voln.Min + voln.Max) / 2; // View outline centre
XYZ vpcen = (vpoln.MaximumPoint + vpoln.MinimumPoint) / 2; // Viewport centre
vpcen = new XYZ(vpcen.X, vpcen.Y, 0); // Zero z
// Correction offset from VCen to centre of Viewport in sheet coords
XYZ offset = vpcen - new XYZ(voln_cen.U, voln_cen.V, 0);
for (int dri = 0; dri <= doors.Count - 1; dri++)
{
Element dr = doors[dri];
BoundingBoxXYZ bb = dr.get_BoundingBox(v);
// Location of door bounding box in sheet coords
XYZ j1 = t.Inverse.OfPoint(bb.Min).Multiply(1 / scale) + offset;
XYZ j2 = t.Inverse.OfPoint(bb.Max).Multiply(1 / scale) + offset;
// These points are plotted in the sheet coord space
// This may not relate to pdf space i.e. location of title block is important
// J1.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
// J2.DrawPoint2D(Doc, 0.01) 'EXTENSION METHOD
j1 = new XYZ(j1.X, j1.Y, 0); // Zero Z
j2 = new XYZ(j2.X, j2.Y, 0); // Zero Z
// Create line 0.01 ft either side of point in Basis.X and Basis.Y directions
Line ln0 = Line.CreateBound(j1 - (XYZ.BasisX * 0.01), j1 + (XYZ.BasisX * 0.01));
Line ln1 = Line.CreateBound(j1 - (XYZ.BasisY * 0.01), j1 + (XYZ.BasisY * 0.01));
Line ln2 = Line.CreateBound(j2 - (XYZ.BasisX * 0.01), j2 + (XYZ.BasisX * 0.01));
Line ln3 = Line.CreateBound(j2 - (XYZ.BasisY * 0.01), j2 + (XYZ.BasisY * 0.01));
doc.Create.NewDetailCurve(acView, ln0);
doc.Create.NewDetailCurve(acView, ln1);
doc.Create.NewDetailCurve(acView, ln2);
doc.Create.NewDetailCurve(acView, ln3);
/*Dim gs As GraphicsStyle ' = Autodesk.Revit.DB.Arc.LineStyle As GraphicsStyle
gs.GraphicsStyleCategory.LineColor = New Color(250, 10, 10)
Autodesk.Revit.DB.CurveByPointsUtils.CreateRectangle(Doc, J1, J2)
TODO Convert to PDF/XPS graphics space system.
Use TB_pt_min & TB_pt_max & TB_TopLeft
I am unsure of the coord system of XPS/PDF (graphics are sometimes plotted downwards from top left point
not up from bottom right as considered above in the sheet coord system
therefore need to transform into that space if applicable
e.g. Y = ScreenHeight - OffsetFromBottom*/
}
}
tx.Commit();
}
}
return Result.Succeeded;
}
}
}
I already converted the bulk of it to C# and that version of it worked so compare with that. Automatic converters are not that good at context e.g.
In VB.NET the line:
Dim J1 As XYZ = T.Inverse.OfPoint(BB.Min).Multiply(1 / Scale) + Offset
Converts to:
XYZ J1 = T.Inverse.OfPoint(BB.Min).Multiply(1 / Scale) + Offset;
This becomes wrong due to the differences in how VB and C# divide an integer i.e. C# truncates and VB rounds it. Therefore you need to cast the Integer to a double as below:
XYZ J1 = T.Inverse.OfPoint(BB.Min).Multiply((Double)1 / Scale) + Offset;
There may be other issues but as stated start your comparisons with the C# version I converted. Other things that the converter gets wrong:
In VB there is no need to add brackets to a method with no arguments. So when this converts to C# the brackets are missing.
For indexers in C# the wrong type of brackets get passed through (round not square for some odd reason).
Lambdas and event handlers often need to be completely rewritten
Classic Linq expressions don't exist in C# so I try to use the '.' extension methods in VB as these then convert.
Xml string literals don't exist in C# so any of that will not usually convert.
To correct myself:
Classic LINQ expression do exist in C# but in my experience they don't convert well, by classic LINQ I mean:
Ok. Fixed. Works now, attached.
I have reduced though my issue to an even simpler challenge:
How do I add a mark on the sheet in a color I choose?
Ok solved that using this forum post:
https://forums.autodesk.com/t5/revit-api-forum/draw-different-colored-filled-circles/td-p/8914987
Hey,
So I have been playing with this, and there seems to be an issue when the viewport is with an angle, then items are not located correctly, is there a way to determine what direction the viewport is pointing at?
Thanks,
Hello @Mustafa.Salaheldin
Probably there is an additional transform required beyond what is done above. Depends for example how the applied scopebox changes or doesn't change the properties of the view such as view centre etc. I would probably look at where the points end up vs where they should be to try and understand the difference and what that is attributed to.
Apart from scopeboxes you can also have views rotated on sheets, so there are various things to consider in the ultimate solution.
Can't find what you're looking for? Ask the community or share your knowledge.