2

This math is not platform specific and I'll take any language as an answer. This is difficult to explain why I'm doing this, but I'll try to include images.

I have a view (View A) that overlays a map as a container. The purpose is to contain our content while remaining fixed to the map as the user drags the map. That view has a coordinate system where it's origin is in the top left of the screen. It will be our absolute coordinate system, where we are trying to convert the positions to and from.

UIView coordinate system

Next, we have a Rectangle that is formed in the intersection between View A and what is visible on the screen. I achieved that with the following property in my UIView:

    var visibleRect: CGRect {
        guard let superview = self.superview?.superview else {
            return CGRect(x: 0, y: 0, width: 0, height: 0)
        }
        // Convert the superview's coordinate system to the current view's coordinate system and find the intersecting rectangle. This is so we don't have to render excessive particles.
        let val = self.bounds.intersection(
            self.convert(superview.frame, from: superview)
        )
        return val
    }

This returns said Rectangle with an origin relative to the origin of View A. The purpose of this Rectangle is that Metal View cannot be too large, so I limited Metal View to drawing within what is visible exclusively while maintaining the exact coordinates of where Metal View will be drawn in relation to View A.

I've been able to verify that the relative origin of the Rectangle within View A is accurate, and I've verified that Metal View fits within the Rectangle.

Here's what I keep banging my head on: Metal vertex functions have a center coordinate system that spans from -1 to 1 for the entire width and height as shown in the following figure (except I'm only using it in 2D space so ignore z position):

center coordinate system

Now my question is:

If I have a point found in the top left quadrant of the Metal View (-0.002, 0.5), how would I take the given information to find it's position in View A? How would I return the point's position in View A to Metal View accounting for the relatively positioned Rectangle?


Visual

Here's a visual for hopeful clarification:

example coordinate system

Edit

I was able to convert from View A to Metal View if Metal View takes up the entire span of View A with the following text code:

def normalizedToViewA(x, y):
    x *=  1.0
    y *= -1.0
    x +=  1.0
    y +=  1.0
    return ((x / 2.0) * width, (y / 2.0) * height)

def viewAToNormalized(x, y):
    normalizedX = x / (width / 2.0)
    normalizedY = y / (height / 2.0)
    normalizedX -=  1.0
    normalizedY -=  1.0
    normalizedX *=  1.0
    normalizedY *= -1.0
    return (normalizedX, normalizedY)

But now I need to calculate as if Metal View fills the Rectangle which is only a portion of View A.

Dev4Hire
  • 119
  • 12
  • Does this answer your question? [Convert screen coordinates to Metal's Normalized Device Coordinates](https://stackoverflow.com/questions/58293221/convert-screen-coordinates-to-metals-normalized-device-coordinates) – Jeshua Lacock Mar 16 '22 at 05:19
  • Partially, but I do appreciate the link as it is very helpful. The next part is only doing that math within the visible portion and adjusting relative to View A, and then doing the reverse. I've been stuck on this project for so long that my brain has turned to mush. – Dev4Hire Mar 16 '22 at 05:24
  • You're welcome. Agreed; anyone that needs to should be able to figure the rest out, otherwise, might want to ask a more specific question. – Jeshua Lacock Mar 16 '22 at 05:28

1 Answers1

0

I found the answer and have used Python for legibility.

View A is 1270*680.

# View A (Top Left Origin)
width = 1270
height = 680

# Visible Rectangle (Top Left Origin)
#   with an origin as a position in View A
subOriginX = 1000
subOriginY = 400
subWidth = 20
subHeight = 20

# Centered origin converted to top left origin
#   where origin is (0,0)
def normalizedToSubview(x, y):
    x *=  1.0
    y *= -1.0
    x +=  1.0
    y +=  1.0
    return ((x / 2.0) * subWidth, (y / 2.0) * subHeight)

# Top Left origin to centered origin
def subviewToNormalized(x, y):
    normalizedX = x / (subWidth / 2.0)
    normalizedY = y / (subHeight / 2.0)
    normalizedX -=  1.0
    normalizedY -=  1.0
    normalizedX *=  1.0
    normalizedY *= -1.0
    return (normalizedX, normalizedY)

# Relative position of a point within subview
#   but on View A's plane
def subviewToViewA(x, y):
    return (x + subOriginX, y + subOriginY)


# Relative position of a point within View A
#   but on the subview's plane
def viewAToSubView(x, y):
    return (x - subOriginX, y - subOriginY)


# Position within Metal View to a position within View A
normalizedCoord = (0.0, 0.0)
toSubview = normalizedToSubview(*normalizedCoord)
viewACoord = subviewToViewA(*toSubview)

print(f"Converted {normalizedCoord} to {toSubview}")
print(f"Converted {toSubview} to {viewACoord}")
# Converted (0.0, 0.0) to (10.0, 10.0)
# Converted (10.0, 10.0) to (1010.0, 410.0)

# Position within View A to Metal View
backToSubview = viewAToSubView(*viewACoord)
backToNormalized = subviewToNormalized(*backToSubview)
# Converted (1010.0, 410.0) to (10.0, 10.0)
# Converted (10.0, 10.0) to (0.0, -0.0)

print(f"Converted {viewACoord} to {backToSubview}")
print(f"Converted {backToSubview} to {backToNormalized}")

This is an extremely niche problem, but please comment if you are facing something similar and I will try to expand the best that I can.

Dev4Hire
  • 119
  • 12