[.NET API] Convert screen coordinates to WCS like the status bar (works only in top view)

[.NET API] Convert screen coordinates to WCS like the status bar (works only in top view)

s3b79
Participant Participant
684 Views
8 Replies
Message 1 of 9

[.NET API] Convert screen coordinates to WCS like the status bar (works only in top view)

s3b79
Participant
Participant

Hi everyone ,

I'm developing a .NET plugin for AutoCAD (C#), and I'm trying to convert screen coordinates (from a drag & drop or mouse position) into WCS coordinates, exactly like the coordinates shown in the AutoCAD status bar when you move the mouse.

 

When I use

Point3d world = doc.Editor.PointToWorld(screenPoint);

this works perfectly in the top view, but gives unexpected results in 3D views.

 

I have read several threads and tried many of the suggested solutions but non of then have worked. 

 

My questions:

  • Is there a way to get the actual world coordinates under the cursor, like AutoCAD does internally?
  • How does the status bar compute the WCS position in real time?
  • What’s the correct way to convert a System.Drawing.point to a Point3D in WCS ?
  • Any help or pointers would be much appreciated.

 

Thanks in advance!

0 Likes
Accepted solutions (1)
685 Views
8 Replies
Replies (8)
Message 2 of 9

daniel_cadext
Advisor
Advisor

In arx you could use acedTrans, I don’t know in .NET though. You can p/invoke it

https://help.autodesk.com/view/OARX/2024/ENU/?guid=GUID-01A45BA0-CC4F-4DCA-840E-DCA8802A060A

 

 

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
Message 3 of 9

ActivistInvestor
Mentor
Mentor

Have a look at this article 

Message 4 of 9

s3b79
Participant
Participant

Thank you both for your answers.


I had already looked at this article and given up because it did not work as I wanted..
I tested it again and I can say that yes, the coordinates returned are the ones I'm looking for.
But when Windows captures the mouse for drag and drop, the PointMonitor event is no longer raised... So...

 

I'll try acedTrans and I'll come back here if my tries are successful.

 

Thanks for your help.

0 Likes
Message 5 of 9

ActivistInvestor
Mentor
Mentor

That was a bad suggestion (I didn't review it beforehand, and didn't notice the involvement of a PointMonitor, which obviously doesn't work very well).

 

Instead of that, have a look at this article.

 

Also note that when you're dealing with the active viewport, the problem isn't nearly as complicated as dragging over a paper space layout with multiple model space viewports. In that case you have to first use the screen point to get the model space viewport(s) that contain the mouse cursor (viewports can overlap which can make that much more complicated). Then you can use the viewport number to transform the screen coordinate to WCS).

0 Likes
Message 6 of 9

s3b79
Participant
Participant
Accepted solution

Hi,

I finally found a solution in this blog post.


I'm pasting my code here for those who might be interested:

public static class AutoCadCoordinateHelper
{
    // https://adndevblog.typepad.com/autocad/2013/02/how-to-get-the-mouse-cursor-coordinates-in-ucs.html
    public static Point2d GetMousePositionInUcs(int screenX, int screenY)
    {
        var doc = Application.DocumentManager.MdiActiveDocument;
        var ed = doc.Editor;

        var pointInScreen = new Point(screenX, screenY);

        // Get point in DCS
        var pointInDcs = ed.PointToWorld(pointInScreen);

        // Convert to UCS
        var pointInUcs = AutoCadCoordinateTransformer.Transform(
            pointInDcs,
            AutoCadCoordinateTransformer.CoordinateSystem.Display,
            AutoCadCoordinateTransformer.CoordinateSystem.User);

        // Project to UCS XY plane
        var view = ed.GetCurrentView();
        var projectedPoint = view.ProjectToUcsXYPlane(pointInUcs);

        return new Point2d(projectedPoint.X, projectedPoint.Y);
    }
}

public static class AutoCadCoordinateTransformer
{
    private const int RTSHORT = 5003;
    private const int RTNORM = 5100;

    public enum CoordinateSystem
    {
        World = 0,
        User = 1,
        Display = 2,
        PaperSpace = 3
    }

    [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int acedTrans(
        double[] point,
        IntPtr fromRb,
        IntPtr toRb,
        int disp,
        double[] result
    );

    public static Point3d Transform(Point3d point, CoordinateSystem from, CoordinateSystem to)
    {
        var result = new double[3];

        using var rbFrom = new ResultBuffer(new TypedValue(RTSHORT, (int)from));
        using var rbTo = new ResultBuffer(new TypedValue(RTSHORT, (int)to));

        var status = acedTrans(point.ToArray(), rbFrom.UnmanagedObject, rbTo.UnmanagedObject, 0, result);
        if (status != RTNORM)
            throw new InvalidOperationException("Coordinate transformation failed.");

        return new Point3d(result);
    }
}

public static class ViewExtensions
{
    public static Point3d ProjectToUcsXYPlane(this ViewTableRecord view, Point3d pointInUcs)
    {
        if (view == null)
            throw new ArgumentNullException(nameof(view));

        var ucsPlane = new Plane(Point3d.Origin, Vector3d.ZAxis);
        var direction = view.ViewDirection.GetNormal();

        return pointInUcs.Project(ucsPlane, direction);
    }
}

 

Note that PointToWorld adds a step to take into account the screen scale before calling acedCoordFromPixelToWorld.

Rather than calling acedtrans I saw this post but didn't test it.

Finally, I don't know if I could come up with anything simpler.

0 Likes
Message 7 of 9

ActivistInvestor
Mentor
Mentor

@s3b79 wrote:

Hi,

I finally found a solution in this blog post.

 

Note that PointToWorld adds a step to take into account the screen scale before calling acedCoordFromPixelToWorld.


This looks like a re-invention of View.WorldToDeviceMatrix

 

 

 

 

0 Likes
Message 8 of 9

s3b79
Participant
Participant

Yes, that's right.

 

But in the current implementation of PointToWorld there is this extra step

pOINT = Autodesk.AutoCAD.ApplicationServices.Core.Application.ConvertToPixelCoords(pt);

to take into account the scale factor of a monitor.

 

This is not done in the original article, which could explain differences if some people try the two versions. 

 

But that's a detail. Thanks again for your help.

0 Likes
Message 9 of 9

daniel_cadext
Advisor
Advisor

I noticed this in the python wrapper, not sure if would help, I use ScreenToClient with the doc window, maybe a hint

 

void PyEdUIContext::calcHitPoint()
{
    CPoint cursorPos;
    ::GetCursorPos(&cursorPos);
    ScreenToClient(adsw_acadDocWnd(), &cursorPos);

    acedDwgPoint cpt;
    acedCoordFromPixelToWorld(cursorPos, cpt);

    resbuf fromrb;
    fromrb.restype = RTSHORT;
    fromrb.resval.rint = 2; // DCS

    resbuf torb;
    torb.restype = RTSHORT;
    torb.resval.rint = 0; // WCS 

    acedTrans(cpt, &fromrb, &torb, FALSE, asDblArray(m_hitPoint));
}

 

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes