ModelToViewCoordinates.init(modelBounds:,viewBounds:)

The ModelToViewCoordinates struct contains the following initializer:

/**

 Create a transformation that maps the points inside the
 modelBounds rectangle to the greatest possible area inside
 of the viewBounds rectangle.
 ...

*/
public init(modelBounds: CGRect, viewBounds: CGRect)

You will use this for the “zoom to max” feature at the end of Part 2.

This initializer is the most subtle part of ModelToViewCoordinates, so you may want to implement it after the other methods.

And there are a number of ways to compute the zoomScale and viewOffset properties described in the documentation. My testBounds() unit test for this initializer assumes that you set zoomScale and viewOffset so that the center point of the modelBounds rectangle will be translated to be at the center point of the viewBounds rectangle. While not strictly necessary, adopting the same strategy will enable you to pass the testBounds() unit test without changing it. Below is more detail about each of the four cases tested in testBounds() to illustrate how this initializer behaves:

  1. Let t1 = ModelToViewCoordinates(modelBounds: b1, viewBounds: b2) where

    b1 = CGRect(x: 0, y: 0, width: 400, height: 200)
    b2 = CGRect(x: 0, y: 0, width: 200, height: 100)

    In this case, t1.zoomScale = 0.5. With that zoom scale, the center of b1 = (200, 100) is scaled to be at the center of b2 = (100, 50), so no offset is needed, and t1.viewOffset = (0,0).

    The following shows how the center, top-left, and bottom-right points of b1 are translated into b2’s coordinates. Note that we cover all of b2 with b1’s translation.

  2. Let t2 = ModelToViewCoordinates(modelBounds: b1, viewBounds: b3) where

    b1 = CGRect(x: 0, y: 0, width: 400, height: 200)
    b3 = CGRect(x: 0, y: 0, width: 200, height: 50)

    In this case, t2.zoomScale = 0.25. With that zoom scale, the center of b1 = (200, 100) is scaled to be at (50, 25). However, the center of b3 = (100, 25), so we must offset the scaled center of b1 by t2.viewOffset = (50, 0) to get the centers to align properly. Given this offset, the origin (0,0) in b1’s coordinate system appears at (50,0) in b1’s coordinate system.

    The following shows how the center, top-left, and bottom-right points of b1 are translated into b3’s coordinates. In this case, some of b3 is not covered by b1’s translation.

  3. Let t3 = ModelToViewCoordinates(modelBounds: b4, viewBounds: b1) where

    b4 = CGRect(x: -1, y: -1, width: 2, height: 2)
    b1 = CGRect(x: 0, y: 0, width: 400, height: 200)

    In this case, t3.zoomScale = 100. With that zoom scale, the center of b4 = (0, 0) is scaled to be at (0, 0). However, the center of b1 = (200, 100), so we must offset the scaled center of b4 by t3.viewOffset = (200, 100) to get the centers to align properly. Given this offset, the origin (0,0) in b4’s coordinate system appears at (200, 100) in b1’s coordinate system.

    The following shows how the center, top-left, and bottom-right points of b4 are translated into b1’s coordinates:

  4. Let t4 = ModelToViewCoordinates(modelBounds: b4, viewBounds: b3) where

    b4 = CGRect(x: -1, y: -1, width: 2, height: 2)
    b3 = CGRect(x: 0, y: 0, width: 200, height: 50)

    In this case, t4.zoomScale = 25. With that zoom scale, the center of b4 = (0, 0) is scaled to be at (0, 0). However, the center of b3 = (100, 25), so we must offset the scaled center of b4 by t4.viewOffset = (100, 25) to get the centers to align properly. Given this offset, the origin (0,0) in b4’s coordinate system appears at (100, 25) in b3’s coordinate system.

    The following shows how the center, top-left, and bottom-right points of b4 are translated into b3’s coordinates: