/*
 * Renderer 3. The MIT License.
 * Copyright (c) 2022 rlkraft@pnw.edu
 * See LICENSE for details.
*/

package renderer.pipeline;

import renderer.scene.*;

import java.util.List;
import java.util.ArrayList;

/**
   Project each {@link Vertex} of a {@link Model} from camera
   coordinates to the {@link Camera}'s image plane {@code z = -1}.
<p>
   This stage does either a perspective projection of an
   orthographic projection, depending on the setting in
   the {@link Camera} object.
*/
public final class Projection
{
   /**
      Project each {@link Vertex} from a {@link Model} to
      the {@link Camera}'s image plane {@code z = -1}.
      <p>
      This pipeline stage assumes that the model's vertices
      have all been transformed to the camera coordinate system.

      @param model  {@link Model} whose {@link Vertex} objects are to be projected onto the image plane
      @param camera  a reference to the {@link Scene}'s {@link Camera} object
      @return a new {@link Model} object holding the projected {@link Vertex} objects
   */
   public static Model project(final Model model, final Camera camera)
   {
      // A new vertex list to hold the projected vertices.
      final List<Vertex> newVertexList =
                            new ArrayList<>(model.vertexList.size());

      // Replace each Vertex object with one that contains
      // the original Vertex's projected (x,y) coordinates.
      for (final Vertex v : model.vertexList)
      {
         if ( camera.perspective )
         {
            // Calculate the perspective projection.
            newVertexList.add(
              new Vertex(
                -v.x / v.z,  // xp = -xc / zc
                -v.y / v.z,  // yp = -yc / zc
                -1));        // zp = -1
         }
         else
         {
            // Calculate the parallel projection.
            newVertexList.add(
              new Vertex(
                v.x,  // xp = xc
                v.y,  // yp = yc
                0));  // zp = 0
         }
      }

      return new Model(newVertexList,
                       model.primitiveList,
                       model.colorList,
                       model.name,
                       model.visible);
   }



   // Private default constructor to enforce noninstantiable class.
   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
   private Projection() {
      throw new AssertionError();
   }
}
