Warning
This documents how the resulting code should work, not necessarily how it may work right now. I’m currently thinking about writing it entirely in MTscript to check whether the performance will be acceptable. I expect that as long as there aren’t too many nested rotations, it shouldn’t be too bad. But a ship or other vehicle that contains dozens or hundreds of passengers may be a problem.

Overview of the approach

The format for images to be rotated supports a container image and zero or more children images. When a container image is rotated, an option allows the children to be rotated by the same amount. This allows passengers in a vehicle to rotate their facing when the vehicle rotates. Children are always rotated around the center point of the container using a radius of the distance between the container’s and the child’s origin.

JSON input structure

To rotate a (possibly nestred) group of images, create a JSON structure like the following:

    {
        "apply_to_children": true,  # <1>
        "angle": -45,               # <2>
        "centerpoint": [x, y],      # <3>
        "origin": [x, y],           # <4>
        "children": [
            { ... },                # <5>
            { ... }                 # <6>
        ]
    }
  1. When true, rotation of this container should apply to its children. Default: false

  2. Amount of rotation, measured in degrees. Default: 0.

  3. Center point of the rotation for this image. Default: no rotation. Must be given if non-zero angle is specified.

  4. When apply_to_children is true, origin position of the current image.

  5. When apply_to_children is true, first child.

  6. When apply_to_children is true, second and remaining children.

Any children specified are in the same format as above, allowing nesting to any depth.

The following examples may take some liberties with the formatting of JSON data, such as allowing trailing commas on the last item in an object or array, or not including quotes around property names. Think of the examples as "pseudo-code" and not literal code.

Example cases and their JSON data

Example 1 — a ship with no passengers

For this example, we have a single ship whose starting position is facing south.

ship-ex1.png
    {
        "angle": -90,
        "origin": [225, 88],        # Image is 550x176
        "centerpoint": [225, 88]
    }

The effect of rotating the ship 90º counterclockwise (the negative number) is to put the prow of the ship on the right. Because the center point of the rotation is also the exact center of the ship’s image, the ship is rotating around its center mast.

ship-ex1-rotated.png

Example 2 — a ship with one passenger (portrait image)

This example uses the same ship (which is still facing south) and adds a passenger. The ship is rotated 90º counterclockwise, but notice that the passenger’s image is not. This is important because the passenger is a portrait image and not a top-down image. (A top-down image would’ve been rotated by the same amount as its container so that it keeps the same facing.)

The origin of the passenger is based on an image size of approximately 40px, putting the center point roughly 50px from the bow of the ship.

ship-ex2.png
    {
        "angle": -90,
        "origin": [225, 88],
        "centerpoint": [225, 88],
        "apply_to_children": true,
        "children": [
            {
                "angle: 0,
                "origin": [500, 88]
            }
        ]
    }
ship-ex2-rotated.png

Notice how the apply_to_children is set to true, indicating that children should be rotated around their container’s center point, but because the child itself doesn’t have a centerpoint defined, it cannot be rotated. The next example shows how a top-down passenger image would be handled.

Example 3 — a ship with one passenger (top-down image)

This example uses the same ship (which is still facing south) with a different passenger. The ship is rotated 90º counterclockwise, along with the passenger’s image. Top-down images should be rotated by the same amount as its container so that it keeps the same facing.

ship-ex3.png
    {
        "angle": -90,
        "origin": [225, 88],
        "centerpoint": [225, 88],
        "apply_to_children": true,
        "children": [
            {
                "angle: 0,
                "origin": [500, 88],
                "centerpoint": [500, 88]
            }
        ]
    }
ship-ex3-rotated.png

This time the passenger image is rotated around its center point because that property was defined.

Example 4 — a ship with a horse on it and a fighter riding the horse

Warning
This example is incomplete. Images should be ready shortly. (Or so he said…)

This example follows the pattern of the previous ones, but adds a container within a container.

This demonstrates how rotating the ship also rotates the horse around the ship’s centerpoint, but because the horse is a top-down image, we define its centerpoint as well so that it’s also rotated. We also set its apply_to_children flag so the fighter is rotated. The centerpoint of the horse is the front half of the horse, meaning the rear of the horse will rotate around the front. This is unfortunate but necessary, since the horse image is not a perfect square (if it were, we could use its geometric center point as its centerpoint). Any non-square shape will rotate in an odd way if it is to conform to an existing grid.

(Perhaps an option that says the map is gridless, a.k.a. not snap-to-grid? Then the geometric center point of every image could be passed into this function. That could be done with the existing code, but the results would not align to MapTool’s grid.)

ship-ex4.png
    {
        "angle": -90,
        "origin": [225, 88],
        "centerpoint": [225, 88],
        "apply_to_children": true,
        "children": [
            // This is the two-weapon fighter at the bow...
            {
                // "angle" defaults to zero, so it's been removed
                "origin": [500, 88],
                "centerpoint": [500, 88]
            },
            // This is the horse and rider.
            {
                "origin": [100, 88],
                "centerpoint": [140, 88]
                "children": [
                    {
                        "origin": [140, 88],
                        "centerpoint": [140, 88]
                    }
                ]
            }
        ]
    }
ship-ex4-rotated.png