001/*
002
003*/
004
005package renderer.pipeline;
006import  renderer.scene.Vertex;
007import  renderer.scene.LineSegment;
008
009/**
010   Clip a (projected) {@link LineSegment} that sticks out of the
011   view rectangle in the image plane. Interpolate {@link Vertex}
012   color from any clipped off {@code Vertex} to the new {@code Vertex}.
013<p>
014   This clipping algorithm is a simplification of the Liang-Barsky Parametric
015   Line Clipping algorithm.
016<p>
017   This algorithm assumes that all {@code LineSegment} objects have been
018   projected onto the {@link Camera}'s image plane, {@code z = -1}. This
019   algorithm also assumes that the camera's view rectangle in the image
020   plane is
021   <pre>{@code
022      -1 <= x <= +1  and
023      -1 <= y <= +1.
024   }</pre>
025<p>
026   If a line segment's projected vertex has an {@code x} or {@code y}
027   coordinate with absolute value greater than 1, then that vertex
028   "sticks out" of the view rectangle. This algorithm will clip the
029   line segment so that both of the line segment's vertices are within
030   the view rectangle with {@code -1 <= x <= +1} and {@code -1 <= y <= +1}.
031<p>
032   Here is an outline of the clipping algorithm.
033<p>
034   Recursively process each line segment, using the following steps.
035<p>
036     1) Test if the line segment no longer needs to be clipped, i.e., both
037        of its vertices are within the clipping rectangle. If this is the
038        case, then return true.
039<p>
040     2) Test if the line segment should be "trivially rejected". A line
041        segment is "trivially rejected" if it is on the wrong side of any
042        of the four lines that bound the view rectangle (i.e., the four
043        lines {@code x = 1}, {@code x = -1}, {@code y = 1}, {@code y = -1}).
044        If so, then return {@code false} (so the line segment will not be
045        rasterized into the framebuffer).
046<p>
047        Notice that a line like the following one is trivially rejected
048        because it is on the "wrong" side of the line {@code x = 1}.
049        <pre>{@code
050                           x=1
051                            |                      v1
052                            |                    /
053                 +----------+                  /
054                 |          |                /
055                 |          |              /
056                 |          |            /
057                 |          |          /
058                 |          |        /
059                 +----------+      /
060                            |    /
061                            |  v0
062        }</pre>
063        But the following line is NOT trivially rejected because, even
064        though it is completely outside of the view rectangle, this line
065        is not entirely on the wrong side of any one of the four lines
066        {@code x = 1}, {@code x = -1}, {@code y = 1}, or {@code y = -1}.
067        The line below will get clipped at least one time (either on the
068        line {@code x = 1} or the line {@code y = -1}) before it is
069        (recursively) a candidate for "trivial rejection". Notice that
070        the line below could even be clipped twice, first on {@code y = 1},
071        then on {@code x = 1}, before it can be trivially rejected (by
072        being on the wrong side of {@code y = -1}).
073        <pre>{@code
074                           x=1
075                            |                      v1
076                            |                    /
077                 +----------+                  /
078                 |          |                /
079                 |          |              /
080                 |          |            /
081                 |          |          /
082                 |          |        /
083                 +----------+      /
084                            |    /
085                            |  /
086                             /
087                           /
088                        v0  |
089                            |
090        }</pre>
091<p>
092     3) If the line segment has been neither accepted nor rejected, then
093        it needs to be clipped. So we test the line segment against each
094        of the four clipping lines, {@code x = 1}, {@code x = -1},
095        {@code y = 1}, and {@code y = -1}, to determine if the line segment
096        crosses one of those lines. We clip the line segment against the
097        first line which we find that it crosses. Then we recursively clip
098        the resulting clipped line segment. Notice that we only clip against
099        the first clipping line which the segment is found to cross. We do
100        not continue to test against the other clipping lines. This is
101        because it may be the case, after just one clip, that the line
102        segment is now a candidate for trivial accept or reject. So rather
103        than test the line segment against several more clipping lines
104        (which may be useless tests) it is more efficient to recursively
105        clip the line segment, which will then start with the trivial accept
106        or reject tests.
107<p>
108        When we clip a line segment against a clipping line, it is always
109        the case that one endpoint of the line segment is on the "right"
110        side of the clipping line and the other endpoint of the line segment
111        is on the "wrong" side of the clipping line. In the following picture,
112        assume that {@code v0} is on the "wrong" side of the clipping line
113        ({@code x = 1}) and {@code v1} is on the "right" side. So {@code v0}
114        needs to be clipped off the line segment and replaced by a new
115        endpoint.
116        <pre>{@code
117                             x=1
118                              |
119                        v1    |
120                          \   |
121                            \ |
122                              \
123                              | \
124                              |   \
125                              |     v0
126        }</pre>
127        Represent points {@code p(t)} on the line segment between {@code v0}
128        and {@code v1} with the following parametric equation.
129        <pre>{@code
130                  p(t) = (1-t) * v0 + t * v1  with  0 <= t <= 1
131        }</pre>
132        Notice that this equation parameterizes the line segment starting
133        with {@code v0} at {@code t=0} (on the "wrong side") and ending
134        with {@code v1} at {@code t=1} (on the "right side"). We need to
135        find the value of {@code t} when the line segment crosses the
136        clipping line {@code x = 1}. Let {@code v0 = (x0, y0)} and let
137        {@code v1 = (x1, y1)}. Then the above parametric equation becomes
138        the two component equations
139        <pre>{@code
140                 x(t) = (1-t) * x0 + t * x1,
141                 y(t) = (1-t) * y0 + t * y1,  with  0 <= t <= 1.
142        }</pre>
143        Since the clipping line in this example is {@code x = 1}, we need
144        to solve the equation {@code x(t) = 1} for {@code t}. So we need
145        to solve
146        <pre>{@code
147                  (1-t) * x0 + t * x1 = 1
148        }</pre>
149        for {@code t}. Here are a few algebra steps.
150        <pre>{@code
151                  x0 - t * x0 + t * x1 = 1
152                  x0 + t * (x1 - x0) = 1
153                       t * (x1 - x0) = 1 - x0
154                       t = (1 - x0)/(x1 - x0)
155        }</pre>
156        We get similar equations for {@code t} if we clip against the other
157        clipping lines ({@code x = -1}, {@code y = 1}, or {@code y = -1}) and
158        we assume that {@code v0} is on the "wrong side" and {@code v1} is on
159        the "right side". With the above value for {@code t}, the new vertex
160        {@code p(t)} that replaces {@code v0} can easily be computed.
161        <pre>{@code
162                             x=1
163                              |
164                        v1    |
165                          \   |
166                            \ |
167                              v0=p( (1 - x0)/(x1 - x0) )
168                              |
169                              |
170                              |
171         }</pre>
172         Finally, the new line segment between {@code v1} and the new
173         {@code v0} is recursively clipped so that it can be checked
174         once again to see if it can be trivially accepted, trivially
175         rejected, or clipped again.
176*/
177public class Clip
178{
179   public static boolean debug = false;
180
181   /**
182      If the {@link LineSegment} sticks out of the view rectangle,
183      then clip it so that it is contained in the view rectangle.
184
185      @param ls  {@link LineSegment} to be clipped
186      @return a boolean that indicates if this line segment is within the view rectangle
187   */
188   public static boolean clip(LineSegment ls)
189   {
190      // Make local copies of several values.
191      Vertex v0 = ls.v[0];
192      Vertex v1 = ls.v[1];
193
194      double x0 = v0.x,  y0 = v0.y;
195      double x1 = v1.x,  y1 = v1.y;
196
197      // 1. Check for trivial accept.
198      if ( ! ( Math.abs( x0 ) > 1
199            || Math.abs( y0 ) > 1
200            || Math.abs( x1 ) > 1
201            || Math.abs( y1 ) > 1 ) )
202      {
203         if (debug) System.out.println(" Trivial accept.");
204         return true;
205      }
206      // 2. Check for trivial delete.
207      else if ( (x0 >  1 && x1 >  1)   // to the right of the line x = 1
208             || (x0 < -1 && x1 < -1)   // to the left of the line x = -1
209             || (y0 >  1 && y1 >  1)   // above the line y = 1
210             || (y0 < -1 && y1 < -1) ) // below the line y = -1
211      {
212         if (debug) System.out.println(" Trivial delete.");
213         return false;
214      }
215      // 3. Need to clip this line segment.
216      else if (x0 > 1 || x1 > 1)  // ls crosses the line x = 1
217      {
218         if (x1 > 1)  // clip off v1
219         {
220            if (debug) System.out.println(" Clip off v1 at x = 1.");
221            // Create a new Vertex between v0 and v1.
222            Vertex v_new = interpolateNewVertex(v0, v1, 1);
223            // Modify the LineSegment to contain v0 and the new Vertex.
224            ls.v[1] = v_new;
225         }
226         else // (x0 > 1)  // clip off v0
227         {
228            if (debug) System.out.println(" Clip off v0 at x = 1.");
229            // Create a new Vertex between v1 and v0.
230            Vertex v_new = interpolateNewVertex(v1, v0, 1);
231            // Modify the LineSegment to contain v1 and the new Vertex.
232            ls.v[0] = v_new;
233         }
234      }
235      else if (x0 < -1 || x1 < -1)  // ls crosses the line x = -1
236      {
237         if (x1 < -1)  // clip off v1
238         {
239            if (debug) System.out.println(" Clip off v1 at x = -1.");
240            // Create a new Vertex between v0 and v1.
241            Vertex v_new = interpolateNewVertex(v0, v1, 2);
242            // Modify the LineSegment to contain v0 and the new Vertex.
243            ls.v[1] = v_new;
244         }
245         else // (x0 < -1)  // clip off v0
246         {
247            if (debug) System.out.println(" Clip off v0 at x = -1.");
248            // Create a new Vertex between v1 and v0.
249            Vertex v_new = interpolateNewVertex(v1, v0, 2);
250            // Modify the LineSegment to contain v1 and the new Vertex.
251            ls.v[0] = v_new;
252         }
253      }
254      else if (y0 > 1 || y1 > 1)  // ls crosses the line y = 1
255      {
256         if (y1 > 1)  // clip off v1
257         {
258            if (debug) System.out.println(" Clip off v1 at y = 1.");
259            // Create a new Vertex between v0 and v1.
260            Vertex v_new = interpolateNewVertex(v0, v1, 3);
261            // Modify the LineSegment to contain v0 and the new Vertex.
262            ls.v[1] = v_new;
263         }
264         else // (y0 > 1)  // clip off v0
265         {
266            if (debug) System.out.println(" Clip off v0 at y = 1.");
267            // Create a new Vertex between v1 and v0.
268            Vertex v_new = interpolateNewVertex(v1, v0, 3);
269            // Modify the LineSegment to contain v1 and the new Vertex.
270            ls.v[0] = v_new;
271         }
272      }
273      else if (y0 < -1 || y1 < -1)  // ls crosses the line y = -1
274      {
275         if (y1 < -1)  // clip off v1
276         {
277            if (debug) System.out.println(" Clip off v1 at y = -1.");
278            // Create a new Vertex between v0 and v1.
279            Vertex v_new = interpolateNewVertex(v0, v1, 4);
280            // Modify the LineSegment to contain v0 and the new Vertex.
281            ls.v[1] = v_new;
282         }
283         else // (y0 < -1)  // clip off v0
284         {
285            if (debug) System.out.println(" Clip off v0 at y = -1.");
286            // Create a new Vertex between v1 and v0.
287            Vertex v_new = interpolateNewVertex(v1, v0, 4);
288            // Modify the LineSegment to contain v1 and the new Vertex.
289            ls.v[0] = v_new;
290         }
291      }
292      else // We should never get here.
293      {
294         System.err.println("Clipping Error!");
295         Thread.dumpStack();
296       //System.err.println(Arrays.toString(Thread.currentThread().getStackTrace()));
297         System.exit(-1);
298      }
299      return clip(ls);  // recursively clip this line segment again
300   }
301
302
303   /**
304      This method takes in two vertices, one that is on the "right" side
305      of a clipping line and the other that is on the "wrong" side of the
306      clipping line, and an integer which specifies which clipping line
307      to use, where
308      <pre>{@code
309         eqn_number == 1 means clipping line x =  1
310         eqn_number == 2 means clipping line x = -1
311         eqn_number == 3 means clipping line y =  1
312         eqn_number == 4 means clipping line y = -1
313      }</pre>
314      This method returns the vertex that is the intersection point
315      between the given line segment and the given clipping line.
316      <p>
317      This method solves for the value of {@code t} for which the
318      parametric equation
319      <pre>{@code
320                  p(t) = (1-t) * v_outside + t * v_inside
321      }</pre>
322      intersects the given clipping line. (Notice that the equation
323      is parameterized so that we move from the outside vertex towards
324      the inside vertex as {@code t} increases from 0 to 1.) The solved
325      for value of {@code t} is then plugged into the parametric formula
326      to get the coordinates of the intersection point.
327
328      @param v_inside    the Vertex that is inside the view rectangle
329      @param v_outside   the Vertex that is outside the view rectangle
330      @param eqn_number  the identifier of the view rectangle edge crossed by the line segment
331      @return a Vertex object that replaces the clipped off vertex
332   */
333   private static Vertex interpolateNewVertex(Vertex v_inside,
334                                              Vertex v_outside,
335                                              int eqn_number)
336   {
337      //if (debug) System.out.println(" Create new vertex.");
338
339      // Make local copies of several values.
340      double vix =  v_inside.x; // "i" for "inside"
341      double viy =  v_inside.y;
342      double vox = v_outside.x; // "o" for "outside"
343      double voy = v_outside.y;
344      // Interpolate between v_outside and v_inside.
345      double t = 0.0;
346      if (1 == eqn_number)            // clip to x = 1
347         t = (1 - vox) / (vix - vox);
348      else if (2 == eqn_number)       // clip to x = -1
349         t = (-1 - vox) / (vix - vox);
350      else if (3 == eqn_number)       // clip to y = 1
351         t = (1 - voy) / (viy - voy);
352      else if (4 == eqn_number)       // clip to y = -1
353         t = (-1 - voy) / (viy - voy);
354
355      // Use the value of t to interpolate the coordinates of the new vertex.
356      double x = (1-t) * vox + t * vix;
357      double y = (1-t) * voy + t * viy;
358
359      Vertex v_new = new Vertex(x, y, 0);
360
361      // Use the value of t to interpolate the color of the new vertex.
362      double r = (1-t) * v_outside.r + t * v_inside.r;
363      double g = (1-t) * v_outside.g + t * v_inside.g;
364      double b = (1-t) * v_outside.b + t * v_inside.b;
365
366      if (debug)
367      {
368         System.out.printf("  t = % .25f\n", t);
369         System.out.printf("  <x_o,y_o> = <% .24f % .24f\n",  vox, voy);
370         System.out.printf("  <x_i,y_i> = <% .24f % .24f\n",  vix, viy);
371         System.out.printf("  <x,  y>   = <% .24f % .24f\n",    x,   y);
372         System.out.printf("  <r_o,g_o,b_o> = <% .15f  % .15f  % .15f>\n",
373                              v_outside.r, v_outside.g, v_outside.b);
374         System.out.printf("  <r_i,g_i,b_i> = <% .15f  % .15f  % .15f>\n",
375                              v_inside.r, v_inside.g, v_inside.b);
376         System.out.printf("  <r,  g,  b>   = <% .15f  % .15f  % .15f>\n",
377                               r,  g,  b);
378      }
379
380      v_new.setColor((float)r, (float)g, (float)b);
381
382      return v_new;
383   }
384}