Getting element coordiantes on sheet

Anonymous

Getting element coordiantes on sheet

Anonymous
Not applicable

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,

0 Likes
Reply
Accepted solutions (3)
7,740 Views
27 Replies
Replies (27)

Anonymous
Not applicable

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.

myplot.png

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).

0 Likes

RPTHOMAS108
Mentor
Mentor

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:

 

201007a.PNG

 

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

 

 

RPTHOMAS108
Mentor
Mentor
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;
		}

Anonymous
Not applicable

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.

0 Likes

RPTHOMAS108
Mentor
Mentor

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)

 

 

Anonymous
Not applicable

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.

0 Likes

RPTHOMAS108
Mentor
Mentor

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?

 

 

0 Likes

Anonymous
Not applicable

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?

0 Likes

Anonymous
Not applicable

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:

 

201007a.PNG

 

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;

        }

    }
}

 

0 Likes

RPTHOMAS108
Mentor
Mentor

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.

 

RPTHOMAS108
Mentor
Mentor

To correct myself:

Classic LINQ expression do exist in C# but in my experience they don't convert well, by classic LINQ I mean:

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/query-expression-syntax-examples-join-...

 

0 Likes

Anonymous
Not applicable

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?

0 Likes

Anonymous
Not applicable
Accepted solution
0 Likes

Mustafa.Salaheldin
Collaborator
Collaborator

Thanks a lot your solution worked for me 🤗


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Anonymous
Not applicable

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,

0 Likes

Anonymous
Not applicable

It seems to be an issue when the viewport is with an angle in v.ViewDirection then the code does not work correctly.

0 Likes

Mustafa.Salaheldin
Collaborator
Collaborator

I noticed that when the view has Scope Box applied, the solution is not working properly, any ideas?


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes

RPTHOMAS108
Mentor
Mentor
Accepted solution

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.