3

How do I flip the y value of an NSPoint to a flipped coordinate space.

I have two sources of points, both in global screen space, but one is flipped:

  1. one from a screen coordinate space with 0,0 = top left.

  2. one from a screen coordinate space with 0,0 = bottom left.

I need to flip the second one (bottom left) to be in the same space as the first one, top left. How can I do that on an NSPoint or CGPoint?

gngrwzrd
  • 5,902
  • 4
  • 43
  • 56

4 Answers4

4

For Mac OSX Cocoa, you can flip the y Coordinate for a view, just override isFlipped to return YES for each view:

- (BOOL)isFlipped {
    return YES;
}
zaph
  • 111,848
  • 21
  • 189
  • 228
3

If you have multiple monitors, the overall height of the global coordinate space may be larger than the vertical height on any individual display. I found it necessary to use the following code (given originalPoint) to find that:

let globalHeight = screens.map {$0.frame.origin.y + $0.frame.height}.max()!
let flippedY = globalHeight - originalPoint.y
let convertedPoint = NSPoint(x: originalPoint.x, y: flippedY)
Ian
  • 11,280
  • 3
  • 36
  • 58
  • 1
    I think this is wrong. You only need to use the zeroScreen (`NSScreen.screens[0]`) for flipping as far as I know. I think this code will be wrong in some situations. – Noah Nuebling Nov 19 '22 at 15:52
1

Let's say you have a point in a bottom left coordinate space, called 'originalPoint'. To flip this point to a top left coordinate space, you can use the following code:

let screenFrame = (NSScreen.main()?.frame)!
let flippedY = screenFrame.size.height - originalPoint.y
let convertedPoint = NSPoint(x: originalPoint.x, y: flippedY)

We can also make an extension:

extension NSPoint {

    var flipped: NSPoint {

        let screenFrame = (NSScreen.main()?.frame)!
        let screenY = screenFrame.size.height - self.y
        return NSPoint(x: self.x, y: screenY)  
    }
}

Use:

let flippedPoint = originalPoint.flipped
Tuslareb
  • 1,200
  • 14
  • 21
1

I think the other answers are not quite correct and will fail in some circumstances.

See this post for what I think is the correct answer.

Here's my implementation for copy-pasting:

#pragma mark - Coordinate conversion

/// The *cocoa*  global "display" coordinate system starts at the bottom left of the zero screen, while the *quartz* coordinate system starts at the top left
/// We sometimes need to convert between them

/// Convenience wrappers

+ (CGPoint)flipPoint:(CGPoint)point {
    return [self cocoaToQuartzScreenSpace:NSMakeRect(point.x, point.y, 0, 0)].origin;
}

+ (CGRect)flipRect:(CGRect)rect {
    return [self cocoaToQuartzScreenSpaceConversionWithOriginFrame:rect];
}

/// Base function

+ (CGRect)cocoaToQuartzScreenSpaceConversionWithOriginFrame:(CGRect)originFrame {
    
    /// Get zero screen height
    NSScreen *zeroScreen = NSScreen.screens[0];
    CGFloat screenHeight = zeroScreen.frame.size.height;
    
    /// Extract values
    CGFloat originY = originFrame.origin.y;
    CGFloat frameHeight = originFrame.size.height;
    
    /// Get new y
    CGFloat destinationY = screenHeight - (originY + frameHeight);
    
    /// Get new frame
    CGRect destinationFrame = originFrame;
    destinationFrame.origin.y = destinationY;
    
    /// return
    return destinationFrame;
}
Noah Nuebling
  • 219
  • 2
  • 11
  • 1
    I was scratching my head about this for a while. I don't know why Apple is making such a mystery of this.. It works, thank you! – soundflix Mar 04 '23 at 11:55