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}