Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Definition of work plane for picking point of point cloud in orthographic 3D View

16 REPLIES 16
Reply
Message 1 of 17
reitererMGQBZ
1554 Views, 16 Replies

Definition of work plane for picking point of point cloud in orthographic 3D View

Hello,

 

I would like to pick a single point of a point cloud in a orthographic 3D View.

 

I follow the method described in:

https://thebuildingcoder.typepad.com/blog/2011/07/point-cloud-snap-and-freeze.html 

 

I use PickPoint, so I have to define a SketchPlane in the active view.

 

And there is the problem...

 

I want to position the sketch plane at the position of the camera eye position and later, filter the extracted points of the pointcloud (via a selection filter) to give me the nearest point to the sketch plane, which would be the nearest point to the camera.

 

However when generating the sketch plane via:

Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(uiDoc.Document.ActiveView.ViewDirection, uiDoc.Document.ActiveView.Origin)

the position of the work/sketch plane is wrong. It is not in front of everything.

 

wrong1.PNGwrong2.PNG

 

 

But when I switch from Orthographic to Perspective

switch.PNG

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

and then move/zoom/pan the camera in Perspective Projection, and then switch back to Orthographic projection, and then again start the picking of the point cloud point the placed work/sketch plane is placed correctly infront of the pointcloud:

 

right1.PNGright2.PNG

 

 

Why is "ActiveView.Origin" in the beginning wrong and just this change from Orthographic --> Perspective (move/pan/...) --> Orthographic can fix this problem?

 

Thanks

 

 

 

 

16 REPLIES 16
Message 2 of 17
RPTHOMAS108
in reply to: reitererMGQBZ

For 3D selection it is often better to use :

 

Selection.PickObject(ObjectType.PointOnElement)

 

Not sure how effective this is for point clouds however although I suspect it is just as useful as pick point.

 

I believe what you are experiencing is the difference between how perspective and parallel projection 3D views are laid out (in terms of origin position). 

 

View3D | Revit | Autodesk Knowledge Network

 

Message 3 of 17
reitererMGQBZ
in reply to: RPTHOMAS108

ad 1) better use Selection.PickObject(ObjectType.PointOnElement):

 

OK, I can use this method with a selection filter the just allow picking on pointcloud instances.

Then I can get the point, on which the reference (pointcloud instance) is hit, that is GlobalPoint.

But how do I get the "real" point cloud coordinates, resp. actual points?

Previously I used this approach with PointCloudInstance.GetPoints:

 

"In either case, you should be able to obtain a 3D point representing the user's selection. The point will have no real relationship to the view and the front of the point cloud. Then you could use the View.ViewDirection property to compute a small rectangle centered on the picked point, and build planes for the PointCloudFilter through the boundary lines of the rectangle and extending parallel to the view direction. That will give you a filter capable of intersecting the point cloud and returning actual points. If too many points are returned, you may need a back clipping plane added to the filter to focus on the points nearest the user's eye position. " from: https://thebuildingcoder.typepad.com/blog/2011/07/point-cloud-snap-and-freeze.html 

 

ad 2) Difference perspective and parallel projection 3D views

What is the difference? As I thought ActiveView.Origin or GetOrientation().EyePosition would also give me the right camera location for parallel projection.

How can I overcome these behavior?

Message 4 of 17
RPTHOMAS108
in reply to: reitererMGQBZ

I got the below working for orthographic views using the pick point approach.

 

The following steps are carried out

1. Get the minimum point of the UIView from UIView.GetZoomCorners

2. Set up a work plane using view direction and min point from (1)*

3. Pick the point on the plane

4. Filter the cloud by creating a box aligned with the view direction around the picked point

5. Transform the filtered cloud points to model space and project them onto plane and find nearest to original picked point.

 

*Since the user can rotate the view during selection of points this invalidates the work plane position for point selection. So I'm locking the view orientation after the projection plane is set.

 

 Public Function Obj_220821c(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result
        Dim IntUIApp As UIApplication = commandData.Application
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document
        Dim IntAView As View3D = TryCast(IntUIDoc.ActiveGraphicalView, View3D)
        If IntAView Is Nothing Then
            Throw New NotSupportedException("View must be View3D.")
        End If

        Dim UIV As UIView = IntUIDoc.GetOpenUIViews.FirstOrDefault(Function(q) q.ViewId = IntAView.Id)
        Dim Max As XYZ = UIV.GetZoomCorners()(1)
        Dim Min As XYZ = UIV.GetZoomCorners()(0)

        Dim V3D As View3D = IntAView
        If V3D.IsPerspective Then
            Throw New NotSupportedException("View must be orthographic.")
        End If

        Dim R As Reference = Nothing
        Try
            R = IntUIDoc.Selection.PickObject(Selection.ObjectType.Element)
        Catch ex As Exception
            Return Result.Cancelled
        End Try
        Dim El As PointCloudInstance = TryCast(IntDoc.GetElement(R), PointCloudInstance)
        If El Is Nothing Then
            Return Result.Cancelled
        End If

        Dim Relock As Boolean = False
        If V3D.IsLocked = False Then
            If V3D.CanBeLocked = False Then
                TaskDialog.Show("Rename", "Rename view so it can be temporarily locked during point selection.")
                Return Result.Cancelled
            Else
                Using Tx As New Transaction(IntDoc, "Lock view")
                    If Tx.Start = TransactionStatus.Started Then
                        V3D.SaveOrientationAndLock()
                        Tx.Commit()
                    End If
                End Using
            End If
        Else
            Relock = True
        End If

        Dim PL As Plane = Plane.CreateByNormalAndOrigin(IntAView.ViewDirection, Min)
        Using tx As New Transaction(IntDoc, "Set plane")
            If tx.Start = TransactionStatus.Started Then
                IntAView.SketchPlane = SketchPlane.Create(IntDoc, PL)
                tx.Commit()
            End If
        End Using

        Dim GP As XYZ
        Try
            GP = IntUIDoc.Selection.PickPoint()
        Catch ex As Exception
            GoTo ResCancel
        End Try

        Dim Bx As XYZ = IntAView.RightDirection
        Dim By As XYZ = IntAView.UpDirection
        Dim Bz As XYZ = IntAView.ViewDirection

        Dim SZ As Double = 0.5 / 304.8
        'Create filter box around global point
        'Dim BtmPlane As Plane = Plane.CreateByNormalAndOrigin(Bz, Max - (Bz * SZ)) 'NOT USED
        Dim TopPlane As Plane = Plane.CreateByNormalAndOrigin(-Bz, Min + (Bz * SZ))
        Dim LeftPlane As Plane = Plane.CreateByNormalAndOrigin(Bx, GP - (Bx * SZ))
        Dim RightPlane As Plane = Plane.CreateByNormalAndOrigin(-Bx, GP + (Bx * SZ))
        Dim FrontPlane As Plane = Plane.CreateByNormalAndOrigin(By, GP - (By * SZ))
        Dim BackPlane As Plane = Plane.CreateByNormalAndOrigin(-By, GP + (By * SZ))
        Dim PList As List(Of Plane) = {TopPlane, LeftPlane, RightPlane, FrontPlane, BackPlane}.ToList

        Dim PCF As PointClouds.PointCloudFilter = PointClouds.PointCloudFilterFactory.CreateMultiPlaneFilter(PList, 5)
        Dim PC As PointClouds.PointCollection = El.GetPoints(PCF, 0.5 / 304.8, 1000) '1000000 Max
        If PC.Count = 0 Then
            GoTo ResCancel
        End If

        'Dim PL As Plane = V3D.SketchPlane.GetPlane
        Dim CP_Lst As List(Of Tuple(Of PointClouds.CloudPoint, UV, Double)) = PC.Select(Function(x) ProjectedPoint(x, El, PL)).ToList
        Dim TestPt As Tuple(Of PointClouds.CloudPoint, UV, Double) = ProjectedPoint(GP, PL)

        Dim Dist As Double = CP_Lst.Min(Function(k) k.Item2.DistanceTo(TestPt.Item2))
        Dim CP = CP_Lst.FindAll(Function(k) k.Item2.DistanceTo(TestPt.Item2) = Dist)

        If CP.Count = 0 Then
            GoTo ResCancel
        End If

        Using tx As New Transaction(IntDoc, "XX")
            If tx.Start = TransactionStatus.Started Then
                Dim CPxyz As XYZ = CloudPointToXYZ(CP(0).Item1)
                Dim CPa As XYZ = El.GetTransform.OfPoint(CPxyz)
                CPa.DrawPoint(IntDoc) 'Extension method that plots the point for checking

                If Relock = False Then
                    V3D.Unlock()
                End If


                tx.Commit()
            End If
        End Using

        GoTo ResOK
ResCancel:
        Return Result.Cancelled
ResOK:
        Return Result.Succeeded
    End Function
    Public Function ProjectedPoint(CP As PointClouds.CloudPoint, CPInst As PointCloudInstance, ProjectionPlane As Plane) As Tuple(Of PointClouds.CloudPoint, UV, Double)
        Dim Pt As New XYZ(CP.X, CP.Y, CP.Z)
        Pt = CPInst.GetTransform.OfPoint(Pt)
        Dim Dist As Double = Double.PositiveInfinity
        Dim UV As UV = Nothing
        ProjectionPlane.Project(Pt, UV, Dist)
        Dim Out As New Tuple(Of PointClouds.CloudPoint, UV, Double)(CP, UV, Dist)
        Return Out
    End Function
    Public Function ProjectedPoint(PT As XYZ, ProjectionPlane As Plane) As Tuple(Of PointClouds.CloudPoint, UV, Double)
        Dim Dist As Double = Double.PositiveInfinity
        Dim UV As UV = Nothing
        ProjectionPlane.Project(Pt, UV, Dist)
        Dim Out As New Tuple(Of PointClouds.CloudPoint, UV, Double)(Nothing, UV, Dist)
        Return Out
    End Function
    Private Function CloudPointToXYZ(CP As PointClouds.CloudPoint) As XYZ
        Return New XYZ(CP.X, CP.Y, CP.Z)
    End Function

 

 

 

220822a.PNG

 

 

 

Message 5 of 17
RPTHOMAS108
in reply to: RPTHOMAS108

Here is an example that works with pick object. It isn't as intuitive as the other option due to no snap icon but it will pick the points out when the point cloud instance lights up as highlighted. Also has the advantage of letting the user orientate the view while picking.

 

It is a similar process but uses ISelection filter to find the nearest point.

 

I tried to add a snap icon InCanvasControl but it seems to work better without due to highlighting of instance overriding the canvas control. If you do use InCanvasControl then the centre of the image should be transparent so that clicking of the control doesn't prevent selection.

 

 

Public Function Obj_220821b(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result
        Dim IntUIApp As UIApplication = commandData.Application
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document

        Dim R As Reference = Nothing
        Try
            R = IntUIDoc.Selection.PickObject(Selection.ObjectType.Element)
        Catch ex As Exception
            Return Result.Cancelled
        End Try

        Dim El As PointCloudInstance = IntDoc.GetElement(R)

        Dim UIV As UIView = IntUIDoc.GetOpenUIViews.FirstOrDefault(Function(k) k.ViewId = IntUIDoc.ActiveGraphicalView.Id)
        If UIV Is Nothing Then
            Return Result.Cancelled
        End If

        'Dim PCSF As New PointCloudSelectionFilter(IntDoc, UIV, El, "c:\temp\SnapIco.bmp")
        'Works better without snap icon due to highlighting of point cloud when selected.
        Dim PCSF As New PointCloudSelectionFilter(IntDoc, UIV, El)

        Dim R1 As Reference = Nothing
        Try
            R1 = IntUIDoc.Selection.PickObject(Selection.ObjectType.Element, PCSF)
        Catch ex As Exception
            GoTo Failure
        End Try

        If PCSF.FoundPoint.HasValue Then
            Using Tx As New Transaction(IntDoc, "PT")
                If Tx.Start = TransactionStatus.Started Then

                    Dim PTxyz As XYZ = CloudPointToXYZ(PCSF.FoundPoint)
                    Dim PTxyzMdl As XYZ = El.GetTransform.OfPoint(PTxyz)
                    PTxyzMdl.DrawPoint(IntDoc)

                    Tx.Commit()
                End If
            End Using
        End If


        GoTo Success
Failure:
        PCSF.HideLast()
        Return Result.Cancelled
Success:
        PCSF.HideLast()
        Return Result.Succeeded
    End Function

 

 

The main part is the implementation of ISelection filter below:

 

 

Public Class PointCloudSelectionFilter
    Implements Selection.ISelectionFilter

    Private IntUIV As UIView = Nothing
    Private IntV As View = Nothing
    Private IntPC As PointCloudInstance = Nothing
    Private IntFoundPoint As PointClouds.CloudPoint?

    Private IntSPImg_Idx As Integer = -1
    Private IntSPImg As InCanvasControlData = Nothing
    Private IntTGM As TemporaryGraphicsManager = Nothing
    Private IntSnapVisible As Boolean = False
    Public ReadOnly Property FoundPoint As PointClouds.CloudPoint?
        Get
            Return IntFoundPoint
        End Get
    End Property

    Public Sub New(D As Document, UIV As UIView, PC As PointCloudInstance, Optional SnapImagePath As String = "")
        If D Is Nothing OrElse UIV Is Nothing OrElse PC Is Nothing Then
            Throw New ArgumentNullException
        End If
        IntV = D.GetElement(UIV.ViewId)
        If IntV.ViewType <> ViewType.ThreeD Then
            Throw New ArgumentException("View of UIV to be View3D")
        End If
        IntUIV = UIV
        IntPC = PC

        If String.IsNullOrEmpty(SnapImagePath) = False Then
            If IO.File.Exists(SnapImagePath) Then
                IntSPImg = New InCanvasControlData(SnapImagePath)
                IntTGM = TemporaryGraphicsManager.GetTemporaryGraphicsManager(D)

                IntSPImg_Idx = IntTGM.AddControl(IntSPImg, IntV.Id)
                IntTGM.SetVisibility(IntSPImg_Idx, False)
            End If
        End If

    End Sub
    Private Sub SetSnapIcon(Pos As XYZ)
        If IntTGM Is Nothing Then
            Exit Sub
        End If

        If IntSnapVisible = False Then
            IntSnapVisible = True
            IntTGM.SetVisibility(IntSPImg_Idx, True)
        End If
        IntSPImg.Position = Pos
        IntTGM.UpdateControl(IntSPImg_Idx, IntSPImg)

    End Sub

    Private Sub HideSnapIcon()
        If IntTGM Is Nothing Then
            Exit Sub
        End If
        If IntSnapVisible = True Then
            IntSnapVisible = False
            IntTGM.SetVisibility(IntSPImg_Idx, False)
        End If

    End Sub

    Public Sub HideLast()
        If IntTGM Is Nothing Then
            Exit Sub
        End If
        IntTGM.Clear()
    End Sub

    Private Function GetProjectionPlane() As Plane
        Dim Origin As XYZ = IntUIV.GetZoomCorners(0)
        Dim Dir As XYZ = IntV.ViewDirection
        Return Plane.CreateByNormalAndOrigin(Dir, Origin)
    End Function

    Private Function ProjectedCP(PL As Plane, CP As PointClouds.CloudPoint, T As Transform) As Tuple(Of PointClouds.CloudPoint, UV, Double)
        Dim Dist As Double = Double.PositiveInfinity
        Dim UV As UV = Nothing
        Dim CP0 As XYZ = CloudPointToXYZ(CP)
        CP0 = T.OfPoint(CP0)

        PL.Project(CP0, UV, Dist)
        Return New Tuple(Of PointClouds.CloudPoint, UV, Double)(CP, UV, Dist)
    End Function
    Private Function ProjectedXYZ(PL As Plane, P As XYZ) As Tuple(Of PointClouds.CloudPoint, UV, Double)
        Dim Dist As Double = Double.PositiveInfinity
        Dim UV As UV = Nothing
        PL.Project(P, UV, Dist)
        Return New Tuple(Of PointClouds.CloudPoint, UV, Double)(Nothing, UV, Dist)
    End Function

    Public Function AllowElement(elem As Element) As Boolean Implements Selection.ISelectionFilter.AllowElement
        ' IntFoundPoint = Nothing

        If elem.Document.ActiveView.Id <> IntV.Id Then
            Return False
        End If
        If elem.GetType IsNot GetType(PointCloudInstance) Then Return False Else
        Dim Inst As Instance = elem
        Dim T As Transform = Inst.GetTransform

        Dim GP As XYZ = elem.Document.ConvertScreenPositionToModel(IntUIV, Windows.Forms.Cursor.Position)

        Dim ViewExtentModel As List(Of XYZ) = IntUIV.GetZoomCorners
        Dim Min As XYZ = ViewExtentModel(0)

        Dim Bx As XYZ = IntV.RightDirection
        Dim By As XYZ = IntV.UpDirection
        Dim Bz As XYZ = IntV.ViewDirection

        Dim SZ As Double = 1 / 304.8
        'Create filter box 2ft around global point
        'Dim BtmPlane As Plane = Plane.CreateByNormalAndOrigin(Bz, GP - Bz)  far clip not usd
        Dim TopPlane As Plane = Plane.CreateByNormalAndOrigin(-Bz, Min + (Bz * SZ))
        Dim LeftPlane As Plane = Plane.CreateByNormalAndOrigin(Bx, GP - (Bx * SZ))
        Dim RightPlane As Plane = Plane.CreateByNormalAndOrigin(-Bx, GP + (Bx * SZ))
        Dim FrontPlane As Plane = Plane.CreateByNormalAndOrigin(By, GP - (By * SZ))
        Dim BackPlane As Plane = Plane.CreateByNormalAndOrigin(-By, GP + (By * SZ))
        Dim PList As List(Of Plane) = {TopPlane, LeftPlane, RightPlane, FrontPlane, BackPlane}.ToList

        Dim PCF As PointClouds.PointCloudFilter = PointClouds.PointCloudFilterFactory.CreateMultiPlaneFilter(PList, 5)
        Dim PC As PointClouds.PointCollection = IntPC.GetPoints(PCF, 0.5 / 304.8, 100000) '1000000 max

        If PC.Count = 0 Then
            Return False
        End If
        Dim PL As Plane = GetProjectionPlane()

        Dim CPs As List(Of Tuple(Of PointClouds.CloudPoint, UV, Double)) =
            PC.Select(Function(q) ProjectedCP(PL, q, T)).ToList

        Dim Check As Tuple(Of PointClouds.CloudPoint, UV, Double) = ProjectedXYZ(PL, GP)

        Dim Dist As Double = CPs.Min(Function(q) q.Item2.DistanceTo(Check.Item2))
        Dim CP_lst As List(Of Tuple(Of PointClouds.CloudPoint, UV, Double)) _
            = CPs.FindAll(Function(q) q.Item2.DistanceTo(Check.Item2) = Dist)

        If CP_lst.Count > 0 Then
            IntFoundPoint = CP_lst(0).Item1

            If IntTGM IsNot Nothing Then
                Dim CPx As XYZ = CloudPointToXYZ(IntFoundPoint.Value)
                CPx = T.OfPoint(CPx)

                'shoft to view plane in front
                Dim ProjDist As Double = Double.PositiveInfinity
                PL.Project(CPx, Nothing, ProjDist)
                CPx += (Bz * 2)

                SetSnapIcon(CPx)
            End If

            Return True
        Else
            If IntTGM IsNot Nothing Then
                HideSnapIcon()
            End If

            Return False
        End If

    End Function

    Public Function AllowReference(reference As Reference, position As XYZ) As Boolean Implements Selection.ISelectionFilter.AllowReference
        Return True
    End Function

    Private Function CloudPointToXYZ(CP As PointClouds.CloudPoint) As XYZ
        Return New XYZ(CP.X, CP.Y, CP.Z)
    End Function
End Class

 

Lastly this is key extension required:

<Extension>
    Public Function ConvertScreenPositionToModel(D As Document, UIV As UIView, PT As System.Drawing.Point) As XYZ

        Dim V As View = D.GetElement(UIV.ViewId)
        Dim ViewExtentModel As List(Of XYZ) = UIV.GetZoomCorners

        Dim T As Transform = Transform.Identity
        T.BasisX = V.RightDirection
        T.BasisY = V.UpDirection
        T.BasisZ = V.ViewDirection

        For i = 0 To 1
            ViewExtentModel(i) = T.Inverse.OfPoint(ViewExtentModel(i))
        Next

        Dim ViewExtMin As XYZ = ViewExtentModel(0)
        Dim ViewExtMax As XYZ = ViewExtentModel(1)

        Dim SizeMdl As XYZ = ViewExtMax - ViewExtMin

        Dim WindowExt As Rectangle = UIV.GetWindowRectangle
        Dim WinW As Double = WindowExt.Right - WindowExt.Left
        Dim WinH As Double = WindowExt.Bottom - WindowExt.Top

        Dim CursorRelPosX As Double = PT.X - WindowExt.Left
        Dim CursorRelPosY As Double = PT.Y - WindowExt.Top

        Dim RatioX As Double = CursorRelPosX / WinW
        Dim RatioY As Double = CursorRelPosY / WinH

        Dim OffsetX As Double = SizeMdl.X * RatioX
        Dim OffsetY As Double = SizeMdl.Y * RatioY

        Dim TL As New XYZ(ViewExtMin.X, ViewExtMax.Y, ViewExtMin.Z)
        Dim Pos As XYZ = TL + (OffsetX * XYZ.BasisX) - (OffsetY * XYZ.BasisY)

        Dim Res As XYZ = T.OfPoint(Pos)

        Return Res
    End Function

Extension for converting mouse cursor position to model position, there are probably other ways of doing this floating about:

 

Message 6 of 17

Hi, thanks @RPTHOMAS108 for your examples, they really helped me.

 

As I need to visualize my picked point cloud points, could you possible give me a the code for your extension DrawPoint?

 

Thanks

Message 7 of 17
RPTHOMAS108
in reply to: reitererMGQBZ

Yes I just draw a line in the three principle directions and then colour them:

You can do similar for the origin and basis of transforms which is quite useful also.

 

 <Extension()>
    Public Sub DrawPoint(Point As XYZ, Doc As Document, Optional PtSz As Double = 1)
        Dim Ln As Double = PtSz / 2
        Dim LNxx As Line = Line.CreateBound(Point - (XYZ.BasisX * Ln), Point + (XYZ.BasisX * Ln))
        Dim LNyy As Line = Line.CreateBound(Point - (XYZ.BasisY * Ln), Point + (XYZ.BasisY * Ln))
        Dim LNzz As Line = Line.CreateBound(Point - (XYZ.BasisZ * Ln), Point + (XYZ.BasisZ * Ln))

        Dim PlaneXY As SketchPlane = SketchPlane.Create(Doc, Plane.CreateByOriginAndBasis(Point, XYZ.BasisX, XYZ.BasisY))
        Dim PlaneXZ As SketchPlane = SketchPlane.Create(Doc, Plane.CreateByOriginAndBasis(Point, XYZ.BasisX, XYZ.BasisZ))

        Dim MCxx As ModelCurve = Doc.Create.NewModelCurve(LNxx, PlaneXY)
        Dim MCyy As ModelCurve = Doc.Create.NewModelCurve(LNyy, PlaneXY)
        Dim MCzz As ModelCurve = Doc.Create.NewModelCurve(LNzz, PlaneXZ)

        Const ax_xx As String = "Axis_XX_Red"
        Const ax_yy As String = "Axis_YY_Green"
        Const ax_zz As String = "Axis_ZZ_Blue"

        MCxx.LineStyle = Doc.AddOrChangeLineStyle(ax_xx, New Color(255, 0, 0), 1, "Dash")
        MCyy.LineStyle = Doc.AddOrChangeLineStyle(ax_yy, New Color(0, 255, 0), 1, "Dash")
        MCzz.LineStyle = Doc.AddOrChangeLineStyle(ax_zz, New Color(0, 0, 255), 1, "Dash")
    End Sub
 <Extension()>
    Public Function AddOrChangeLineStyle(Doc As Document, Name As String,
                                       Optional Colour As Color = Nothing,
                                       Optional Weight As Integer = -1,
                                         Optional PatternName As String = Nothing) As GraphicsStyle

        Dim Out As GraphicsStyle = Nothing
        Dim CC As Category = Category.GetCategory(Doc, BuiltInCategory.OST_Lines)
        Dim SC As Category = Nothing
        If CC.SubCategories.Contains(Name) = False Then
            SC = Doc.Settings.Categories.NewSubcategory(CC, Name)
        Else
            SC = CC.SubCategories.Item(Name)
        End If
        Out = SC.GetGraphicsStyle(GraphicsStyleType.Projection)
        If Colour Is Nothing = False Then
            Out.GraphicsStyleCategory.LineColor = Colour
        End If
        If Weight > 0 Then
            Out.GraphicsStyleCategory.SetLineWeight(Weight, GraphicsStyleType.Projection)
        End If
        If String.IsNullOrEmpty(PatternName) = False Then
            Dim FEC As New FilteredElementCollector(Doc)
            Dim ECF As New ElementClassFilter(GetType(LinePatternElement))
            Dim Els As List(Of Element) = FEC.WherePasses(ECF).ToElements
            Dim Itm As Element = Els.Find(Function(x) x.Name = PatternName)
            If Itm Is Nothing = False Then
                Out.GraphicsStyleCategory.SetLinePatternId(Itm.Id, GraphicsStyleType.Projection)
            End If
        End If

        Return Out
    End Function

 

 

Message 8 of 17
adorjanKZX7D
in reply to: RPTHOMAS108

Hi,

 

thank you @RPTHOMAS108 for your code snippets. I tried to derive a 3D pick-ray using your ConvertScreenPositionToModel function for the ray-origin and ActiveGraphicalView.ViewDirection for its direction.

 

var activeView = uiDoc.ActiveGraphicalView;
var uiView = uiDoc.GetOpenUIViews().FirstOrDefault(k => k.ViewId == activeView.Id);

var viewDir = activeView.ViewDirection;
var origin = ConvertScreenPositionToModel(uiDoc.Document, uiView, Cursor.Position);

//visualize ray-origin
using (Transaction tx = new Transaction(uiDoc.Document, "rayorigin"))
{
    if (tx.Start() == TransactionStatus.Started)
    {
        DrawPoint(origin, uiDoc.Document, 10);
        tx.Commit();
    }
}

//... custom components using Ray(viewDir,origin)

 

The direction seems to be correct, however, the origin is only correct if computed with active perspective projection. In ortho-projection the Z-part of ViewExtentModel (from GetZoomCorners) is not correct. Strangely, it is correct if I toggle perspective projection, zoom extents and return to orthographic projection or if I activate a "default" view via the "3D view cube" in orthographic projection. Can this be a bug? Some camera initialization missing in orthographic projection? Something thats only initialized when doing "zoom extents"?

Message 9 of 17
RPTHOMAS108
in reply to: adorjanKZX7D

Wasn't this the reason for this thread, I don't think I used view origin anywhere above?

 

What you experience is probably the target updating the origin when it is switches between view projection types. If you are expecting the View.Origin to be aligning with the centre of the screen, then this is probably wrong. In perspective the camera is fixed and the centre of the screen has to align with a target. In orthographic the camera is not fixed. You can pan sideways but the view origin will remain where it is since it probably doesn't need to update to carry out that projection type. We have to remember that these things are exposed to the API from the inner workings which I'm guessing would only update them if there were a reason to do so for their purposes.

 

Why do you need View.Origin for your task? You have the model position you are viewing from the window extents. I believe they are in model coords so you just need to average them to get an arbitrary centre.

 

Message 10 of 17
adorjanKZX7D
in reply to: RPTHOMAS108

I don't use View.Origin. I only use GetZoomCorners() and your ConvertScreenPositionToModel to get a 3D starting point for a ray-cast along view-direction through the scene. This starting point, however, is only correct in perspective projection, because GetZoomCorners().Min.Z and GetZoomCorners().Max.Z deliver strange values in orthographic projection.

The ray is afterwards used to do spatial queries in our datasets, which are linked to the revit model. Therefore, the ray origin with respect to the visible revit model is essential.
Message 11 of 17
RPTHOMAS108
in reply to: adorjanKZX7D

I don't know how you can get Z from that method anyway since you are picking on a screen.

 

There is probably a logic to the Z values used in GetZoomCorners but you would probably have to plot the box parallel to the view to understand the logic. I think it'll fall short of the model because it is based on arbitrary target.

Message 12 of 17

I think Z must be on the camera's near plane and therefore slightly in front of the camera location in model coordinates. Maybe, we can get the information from the CameraInfo class...

Message 13 of 17
RPTHOMAS108
in reply to: adorjanKZX7D

If you transform the two points you get from GetZoomCorners in terms of the Max/Mins into the view coordinate system you'll likely find the Z values are the same for the two points indicating they sit on a plane aligned with that system. You'll also find they share the same plane as the view origin. I don't believe this changes regardless of the projection mode.

 

Subject viewSubject viewView plane of subject view with origin and view directionView plane of subject view with origin and view direction

 

 

 

Message 14 of 17
adorjanKZX7D
in reply to: RPTHOMAS108

Yes, I could also observe this behavior. The problem is, that if I don't toggle perspective mode or a default view from the "view cube" before, the viewplane cuts through the scene (and raycast from viewplane-center misses objects behind the view plane). The Z-position of the camera in model-space isn't "behind" the scene, as one would suspect, but within the scene, such that objects are geometrically behind the camera (but visible in 3D-view...).  Thats the same behavior as in the starting post of this thread...

 

adorjanKZX7D_0-1664267810838.png

Scene: Wall starting at 0/0/0 along X-axis with length 30cm

 

adorjanKZX7D_1-1664267837127.png

Red view extents before perspective or "view cube"-toggle + zoom-extents

Green view extents after perspective or "view cube"-toggle + zoom-extents

 

Conclusio: I cannot get the correct starting point of a ray in orthographic projection to do a query on the visible spatial bounds of the 3D view...

Message 15 of 17
RPTHOMAS108
in reply to: adorjanKZX7D

In my examples I was switching between perspective and orthographic but I didn't do the zoom extents part.

 

I don't think the CameraInfo class is applicable, there is the GetViewOrientation3D that reports the eye position, up direction and forward direction. Apart from those you also have the parameters Target Elevation and Eye Elevation.

 

So in theory would have thought you can derive the target point from the intersection of the target elevation XY plane and the forward direction from the eye origin. Alternatively work from eye elevation down/up to target elevation then you can solve the triangle that has two known angles and a side (the side you know is distance from eye elevation to target elevation). The forward direction will allow you to form one known angle to vertical and the other known angle is a right angle i.e. you know all three angles and one side. The only issue with that is when the target and eye share the same elevation.

 

However I don't know if any of that helps in terms of what you are ultimately trying to achieve. As above in this original thread there may be a workaround.

 

Message 16 of 17
adorjanKZX7D
in reply to: RPTHOMAS108

Ok, thanks for the hints. For now, I applied an offset to move the eye away from the scene to ensure that all scene objects are in front of the viewplane/eye-position. Since all objects are visible in orthographic projection anyway, there cannot be anything behind the camera/eye.

var orthoOffset = view is View3D v3d && !v3d.IsPerspective ? 10000.0 * view.ViewDirection : XYZ.Zero;
var posModelSpace = invViewTrafo.OfPoint(posViewSpace) + orthoOffset;

Let's see how far I get with it ...
Message 17 of 17
amitcivil876
in reply to: RPTHOMAS108

Hi @RPTHOMAS108 

 

I just wanted to say a big thank you for sharing your knowledge and code snippet. It's been really helpful to me. I was wondering if you could spare a moment to help me out. I'm trying to figure out how to select a point from a point cloud in  Revit project and find its coordinates accuratly. Would you mind taking a look

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Drawing;
using Autodesk.Revit.DB.PointClouds;
using System.Windows.Forms;


public static class ExtensionMethods
{
    public static XYZ ConvertScreenPositionToModel(this Document D, UIView UIV, System.Drawing.Point PT)
    {
        Autodesk.Revit.DB.View V = (Autodesk.Revit.DB.View)D.GetElement(UIV.ViewId);
        IList<XYZ> ViewExtentModel = UIV.GetZoomCorners();

        Transform T = Transform.Identity;
        T.BasisX = V.RightDirection;
        T.BasisY = V.UpDirection;
        T.BasisZ = V.ViewDirection;

        XYZ ViewExtMin = ViewExtentModel[0];
        XYZ ViewExtMax = ViewExtentModel[1];

        XYZ SizeMdl = ViewExtMax - ViewExtMin;

        System.Drawing.Rectangle WindowExt = System.Windows.Forms.Screen.PrimaryScreen.Bounds; // Assuming full screen. Adjust as necessary.
        double WinW = WindowExt.Width;
        double WinH = WindowExt.Height;

        double CursorRelPosX = PT.X - WindowExt.Left;
        double CursorRelPosY = PT.Y - WindowExt.Top;

        double RatioX = CursorRelPosX / WinW;
        double RatioY = CursorRelPosY / WinH;

        double OffsetX = SizeMdl.X * RatioX;
        double OffsetY = SizeMdl.Y * RatioY;

        XYZ TL = new XYZ(ViewExtMin.X, ViewExtMax.Y, ViewExtMin.Z);
        XYZ Pos = TL + (OffsetX * XYZ.BasisX) - (OffsetY * XYZ.BasisY);

        XYZ Res = T.OfPoint(Pos);

        return Res;
    }
}

namespace RevitNewTest
{
    [Transaction(TransactionMode.Manual)]
    public class PointCloudpickpoint : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIApplication uiApp = commandData.Application;
            UIDocument uiDoc = uiApp.ActiveUIDocument;
            Document doc = uiDoc.Document;

            Reference reference = null;
            try
            {
                reference = uiDoc.Selection.PickObject(ObjectType.Element);
            }
            catch (Exception)
            {
                return Result.Cancelled;
            }

            // Yahaan par hum type check aur cast ka use kar rahe hain instead of 'is not' pattern ka.
            Element elem = doc.GetElement(reference);
            if (!(elem is PointCloudInstance element))
            {
                return Result.Failed;
            }

            UIView uiView = uiDoc.GetOpenUIViews().FirstOrDefault(v => v.ViewId == uiDoc.ActiveGraphicalView.Id);
            if (uiView == null)
            {
                return Result.Cancelled;
            }

            PointCloudSelectionFilter filter = new PointCloudSelectionFilter(doc, uiView, element);

            Reference pickedRef = null;
            try
            {
                pickedRef = uiDoc.Selection.PickObject(ObjectType.Element, filter);
            }
            catch (Exception)
            {
                filter.HideLast();
                return Result.Cancelled;
            }

            if (filter.FoundPoint.HasValue)
            {
                using (Transaction tx = new Transaction(doc, "PointCloud Transaction"))
                {
                    if (tx.Start() == TransactionStatus.Started)
                    {
                        XYZ pointXYZ = CloudPointToXYZ(filter.FoundPoint.Value);
                        XYZ modelPoint = element.GetTransform().OfPoint(pointXYZ);
                        // DrawPoint(doc, modelPoint); // Implement this method based on your requirements
                        TaskDialog.Show("Coordinate Info", "Selected Point Coordinates (X, Y, Z): " + modelPoint.X.ToString() + ", " + modelPoint.Y.ToString() + ", " + modelPoint.Z.ToString());
                        tx.Commit();
                    }
                }
            }

            filter.HideLast();
            return Result.Succeeded;
        }

        private static XYZ CloudPointToXYZ(CloudPoint cloudPoint)
        {
            return new XYZ(cloudPoint.X, cloudPoint.Y, cloudPoint.Z);
        }
    }

    public class PointCloudSelectionFilter : ISelectionFilter
    {
        private UIView IntUIV = null;
        private View3D IntV = null;
        private PointCloudInstance IntPC = null;
        private CloudPoint? IntFoundPoint = null;

        private int IntSPImg_Idx = -1;
        private InCanvasControlData IntSPImg = null;
        private TemporaryGraphicsManager IntTGM = null;
        private bool IntSnapVisible = false;

        public CloudPoint? FoundPoint
        {
            get { return IntFoundPoint; }
        }

        public PointCloudSelectionFilter(Document D, UIView UIV, PointCloudInstance PC, string SnapImagePath = "")
        {
            if (D == null || UIV == null || PC == null)
                throw new ArgumentNullException();

            IntV = D.GetElement(UIV.ViewId) as View3D;
            if (IntV.ViewType != ViewType.ThreeD)
                throw new ArgumentException("View of UIV to be View3D");

            IntUIV = UIV;
            IntPC = PC;

            if (!string.IsNullOrEmpty(SnapImagePath) && File.Exists(SnapImagePath))
            {
                IntSPImg = new InCanvasControlData(SnapImagePath);
                IntTGM = TemporaryGraphicsManager.GetTemporaryGraphicsManager(D);

                IntSPImg_Idx = IntTGM.AddControl(IntSPImg, IntV.Id);
                IntTGM.SetVisibility(IntSPImg_Idx, false);
            }
        }

        private void SetSnapIcon(XYZ Pos)
        {
            if (IntTGM == null)
                return;

            if (!IntSnapVisible)
            {
                IntSnapVisible = true;
                IntTGM.SetVisibility(IntSPImg_Idx, true);
            }

            IntSPImg.Position = Pos;
            IntTGM.UpdateControl(IntSPImg_Idx, IntSPImg);
        }

        private void HideSnapIcon()
        {
            if (IntTGM == null)
                return;

            if (IntSnapVisible)
            {
                IntSnapVisible = false;
                IntTGM.SetVisibility(IntSPImg_Idx, false);
            }
        }

        public void HideLast()
        {
            if (IntTGM == null)
                return;

            IntTGM.Clear();
        }

        private Plane GetProjectionPlane()
        {
            XYZ origin = IntUIV.GetZoomCorners().First();
            XYZ direction = IntV.ViewDirection;
            return Plane.CreateByNormalAndOrigin(direction, origin);
        }


        private Tuple<CloudPoint, UV, double> ProjectedCP(Plane PL, CloudPoint CP, Transform T)
        {
            XYZ pointXYZ = CloudPointToXYZ(CP);
            pointXYZ = T.OfPoint(pointXYZ);
            UV uv; double distance;
            PL.Project(pointXYZ, out uv, out distance);
            return new Tuple<CloudPoint, UV, double>(CP, uv, distance);
        }

        private XYZ CloudPointToXYZ(CloudPoint cloudPoint)
        {
            return new XYZ(cloudPoint.X, cloudPoint.Y, cloudPoint.Z);
        }


        /*
                private Tuple<CloudPoint, UV, double> ProjectedXYZ(Plane PL, XYZ P)
                {
                    double Dist = double.PositiveInfinity;
                    UV UV = null;
                    PL.Project(P, out UV, out Dist);
                    return new Tuple<CloudPoint, UV, double>(null, UV, Dist);
                }*/


        // Adjusted to accept an XYZ point and a Transform
        // Adjusted to accept an XYZ point and a Transform
        private Tuple<XYZ, UV, double> ProjectedXYZ(Plane PL, XYZ point, Transform T)
        {
            double distance = double.PositiveInfinity;
            UV uv = new UV(); // Initialize a new UV variable
            XYZ transformedPoint = T.OfPoint(point);

            PL.Project(transformedPoint, out uv, out distance); // Pass the initialized UV variable with out
            return new Tuple<XYZ, UV, double>(transformedPoint, uv, distance);
        }


        public bool AllowReference(Reference reference, XYZ position)
        {
            return true;
        }     


        /*private Tuple<CloudPoint?, UV, double> ProjectedXYZ(Plane PL, XYZ P)
        {
            UV uv; double distance;
            PL.Project(P, out uv, out distance);
            // Now explicitly allowing CloudPoint to be null in the tuple.
            return new Tuple<CloudPoint?, UV, double>(null, uv, distance);
        }*/



        public bool AllowElement(Element elem)
        {
            if (elem.Document.ActiveView.Id != IntV.Id)
                return false;

            if (!(elem is PointCloudInstance))
                return false;

            Instance Inst = elem as Instance;
            Transform T = Inst.GetTransform();

            XYZ GP = elem.Document.ConvertScreenPositionToModel(IntUIV, System.Windows.Forms.Cursor.Position);

            IList<XYZ> ViewExtentModel = IntUIV.GetZoomCorners();
            XYZ Min = ViewExtentModel[0];

            XYZ Bx = IntV.RightDirection;
            XYZ By = IntV.UpDirection;
            XYZ Bz = IntV.ViewDirection;

            double SZ = 1 / 304.8;
            Plane TopPlane = Plane.CreateByNormalAndOrigin(-Bz, Min + (Bz * SZ));
            Plane LeftPlane = Plane.CreateByNormalAndOrigin(Bx, GP - (Bx * SZ));
            Plane RightPlane = Plane.CreateByNormalAndOrigin(-Bx, GP + (Bx * SZ));
            Plane FrontPlane = Plane.CreateByNormalAndOrigin(By, GP - (By * SZ));
            Plane BackPlane = Plane.CreateByNormalAndOrigin(-By, GP + (By * SZ));
            List<Plane> PList = new List<Plane> { TopPlane, LeftPlane, RightPlane, FrontPlane, BackPlane };

            PointCloudFilter PCF = PointCloudFilterFactory.CreateMultiPlaneFilter(PList, 5); 

            PointCollection PC = IntPC.GetPoints(PCF, 0.5 / 304.8, 100000);

            if (PC.Count == 0)
                return false;

            Plane PL = GetProjectionPlane();

            List<Tuple<CloudPoint, UV, double>> CPs =
                PC.Select(q => ProjectedCP(PL, q, T)).ToList();

            // Assuming GP is an XYZ point and T is the transform you want to apply
            Tuple<XYZ, UV, double> Check = ProjectedXYZ(PL, GP, T); // Ensure 'T' is defined and valid in this context

            double Dist = CPs.Min(q => q.Item2.DistanceTo(Check.Item2));
            List<Tuple<CloudPoint, UV, double>> CP_lst =
                CPs.FindAll(q => q.Item2.DistanceTo(Check.Item2) == Dist);

            if (CP_lst.Count > 0)
            {
                IntFoundPoint = CP_lst[0].Item1;

                if (IntTGM != null)
                {
                    XYZ CPx = CloudPointToXYZ(IntFoundPoint.Value);
                    CPx = T.OfPoint(CPx);

                    UV dummyUV = new UV(); // Initialize a dummy UV variable
                    double ProjDist = double.PositiveInfinity;
                    PL.Project(CPx, out dummyUV, out ProjDist); // Use the dummy UV variable

                    CPx += (Bz * 2);

                    SetSnapIcon(CPx);
                }

                return true;
            }
            else
            {
                if (IntTGM != null)
                    HideSnapIcon();

                return false;
            }
        }       

    }
}

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Rail Community


Autodesk Design & Make Report