001/*
002 * Renderer 7. The MIT License.
003 * Copyright (c) 2022 rlkraft@pnw.edu
004 * See LICENSE for details.
005*/
006
007package renderer.pipeline;
008
009import renderer.scene.*;
010import renderer.scene.primitives.*;
011import static renderer.pipeline.PipelineLogger.*;
012
013import java.util.List;
014import java.util.ArrayList;
015import java.util.Optional;
016
017/**
018   Clip a (projected) geometric {@link Primitive} that sticks out
019   of the camera's view rectangle in the image plane. Interpolate
020   {@link Vertex} color from any clipped off {@link Vertex} to the
021   new {@link Vertex}.
022*/
023public class Clip
024{
025   public static boolean debug = false;
026
027   /**
028      Start with a {@link Model} that contains {@link Primitive}s
029      that have been projected onto the camera's view plane,
030      {@code z = -1}.
031   <p>
032      If a projected {@link Primitive} sticks out of the camera's
033      view rectangle, then replace that {@link Primitive}, in the
034      {@link Model}'s list of primitives, with one that has been
035      clipped so that it is contained in the view rectangle.
036   <p>
037      If a projected {@link Primitive} is completely outside of
038      the view rectangle, then drop that {@link Primitive} from
039      the {@link Model}'s list of primitives.
040   <p>
041      Return a {@link Model} for which every {@link Primitive} is
042      completely contained in the camera's view rectangle.
043
044      @param model  {@link Model} containing projected {@link Primitive}s
045      @return a {@link Model} containing {@link Primitive}s clipped to the view rectangle
046   */
047   public static Model clip(final Model model)
048   {
049      // Replace the model's list of colors with a shallow copy.
050      final Model model2 =  new Model(model.vertexList,
051                                      model.primitiveList,
052                                      new ArrayList<>(model.colorList),
053                                      model.name,
054                                      model.visible);
055
056      final List<Primitive> newPrimitiveList = new ArrayList<>();
057
058      for (final Primitive p : model2.primitiveList)
059      {
060         logPrimitive("5. Clipping", model2, p);
061
062         final Optional<Primitive> p_clipped =
063                    (p instanceof LineSegment)
064                      ? Clip_Line.clip(model2, (LineSegment)p)
065                      : Clip_Point.clip(model2, (Point)p);
066
067         if ( p_clipped.isPresent() )
068         {
069            // Keep the primitives that are visible.
070            newPrimitiveList.add( p_clipped.get() );
071            logPrimitive("5. Clipped (accept)", model2, p_clipped.get());
072         }
073         else
074         {
075            // Discard the primitives that are not visible.
076            logPrimitive("5. Clipped (reject)", model2, p);
077         }
078      }
079
080      // Replace the model's original list of line segments
081      // with the list of clipped line segments.
082      return new Model(model2.vertexList,  // has been updated with clipped vertices
083                       newPrimitiveList,   // clipped primitives
084                       model2.colorList,   // has been updated with interpolated colors
085                       model2.name,
086                       model2.visible);
087   }
088
089
090
091   // Private default constructor to enforce noninstantiable class.
092   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
093   private Clip() {
094      throw new AssertionError();
095   }
096}