1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2018-02-22 12:04:47 -0600 (Thu, 22 Feb 2018) $
4  * $Revision: 21841 $
5  *
6  * Copyright (C) 2003-2005  Miguel, Jmol Development, www.jmol.org
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.g3d;
25 
26 /**
27  *<p>
28  * Implements 3D line drawing routines.
29  *</p>
30  *<p>
31  * A number of line drawing routines, most of which are used to
32  * implement higher-level shapes. Triangles and cylinders are drawn
33  * as a series of lines
34  *</p>
35  *
36  * @author Miguel, miguel@jmol.org and Bob Hanson, hansonr@stolaf.edu
37  */
38 
39 /* rewritten by Bob 7/2006 to fully implement the capabilities of the
40  * Cohen-Sutherland algorithm.
41  *
42  * added line bitset option for rockets. Rendering times for bonds done this way are a bit slower.
43  */
44 
45 import java.util.Hashtable;
46 import java.util.Map;
47 
48 import javajs.util.P3;
49 import javajs.util.P3i;
50 
51 import javajs.util.BS;
52 import org.jmol.util.GData;
53 import org.jmol.util.Shader;
54 
55 final class LineRenderer extends PrecisionRenderer {
56 
57   private final Graphics3D g3d;
58   private final Shader shader;
59 
LineRenderer(Graphics3D g3d)60   LineRenderer(Graphics3D g3d) {
61     this.g3d = g3d;
62     shader = g3d.shader;
63   }
64 
65   private BS lineBits;
66   private float slope;
67   private boolean lineTypeX;
68   private int nBits;
69   //  private int nCached = 0;
70   //  private int nFound = 0;
71   //int test = 5;
72   private Map<Float, BS> lineCache = new Hashtable<Float, BS>();
73   private Float slopeKey;
74 
setLineBits(float dx, float dy)75   void setLineBits(float dx, float dy) {
76     // from cylinder
77     slope = (dx != 0 ? dy / dx : dy >= 0 ? Float.MAX_VALUE : -Float.MAX_VALUE);
78     lineTypeX = (slope <= 1 && slope >= -1);
79     nBits = (lineTypeX ? g3d.width : g3d.height);
80 
81     // get cached line bits or create new ones
82 
83     slopeKey = Float.valueOf(slope);
84     if (lineCache.containsKey(slopeKey)) {
85       lineBits = lineCache.get(slopeKey);
86       //    if (Logger.debugging) {
87       //      nFound++;
88       //      if (nFound == 1000000)
89       //        Logger.debug("nCached/nFound lines: " + nCached + " " + nFound);
90       //    }
91       return;
92     }
93     lineBits = BS.newN(nBits);
94     dy = Math.abs(dy);
95     dx = Math.abs(dx);
96     if (dy > dx) {
97       float t = dx;
98       dx = dy;
99       dy = t;
100     }
101     int twoDError = 0;
102     float twoDx = dx + dx, twoDy = dy + dy;
103     for (int i = 0; i < nBits; i++) {
104       twoDError += twoDy;
105       if (twoDError > dx) {
106         lineBits.set(i);
107         twoDError -= twoDx;
108       }
109     }
110     lineCache.put(slopeKey, lineBits);
111   }
112 
clearLineCache()113   void clearLineCache() {
114     lineCache.clear();
115     //nCached = 0;
116   }
117 
plotLineOld(int argbA, int argbB, int xA, int yA, int zA, int xB, int yB, int zB)118   void plotLineOld(int argbA, int argbB, int xA, int yA, int zA, int xB,
119                    int yB, int zB) {
120     // primary method for mesh triangle, quadrilateral, hermite, backbone,
121     // sticks, and stars
122     x1t = xA;
123     x2t = xB;
124     y1t = yA;
125     y2t = yB;
126     z1t = zA;
127     z2t = zB;
128     boolean clipped = true;
129     switch (getTrimmedLineImpl()) {
130     case VISIBILITY_UNCLIPPED:
131       clipped = false;
132       break;
133     case VISIBILITY_OFFSCREEN:
134       return;
135     }
136     plotLineClippedOld(argbA, argbB, xA, yA, zA, xB - xA, yB - yA, zB - zA,
137         clipped, 0, 0);
138   }
139 
140   /**
141    * low-resolution linear z
142    *
143    * @param argbA
144    * @param argbB
145    * @param xA
146    * @param yA
147    * @param zA
148    * @param dxBA
149    * @param dyBA
150    * @param dzBA
151    * @param clipped
152    */
plotLineDeltaOld(int argbA, int argbB, int xA, int yA, int zA, int dxBA, int dyBA, int dzBA, boolean clipped)153   void plotLineDeltaOld(int argbA, int argbB, int xA, int yA, int zA, int dxBA,
154                         int dyBA, int dzBA, boolean clipped) {
155     // from cylinder -- endcaps open or flat, diameter 1, cone
156     // cartoon rockets, low-precision z-buffer
157     x1t = xA;
158     x2t = xA + dxBA;
159     y1t = yA;
160     y2t = yA + dyBA;
161     z1t = zA;
162     z2t = zA + dzBA;
163     if (clipped)
164       switch (getTrimmedLineImpl()) {
165       case VISIBILITY_OFFSCREEN:
166         return;
167       case VISIBILITY_UNCLIPPED:
168         clipped = false;
169         break;
170       }
171     plotLineClippedOld(argbA, argbB, xA, yA, zA, dxBA, dyBA, dzBA, clipped, 0,
172         0);
173   }
174 
175   /**
176    * low-precision old-style linear z, even in perspective mode
177    *
178    * @param shades1
179    * @param shades2
180    * @param screenMask
181    * @param shadeIndex
182    * @param x
183    * @param y
184    * @param z
185    * @param dx
186    * @param dy
187    * @param dz
188    * @param clipped
189    */
plotLineDeltaAOld(int[] shades1, int[] shades2, int screenMask, int shadeIndex, int x, int y, int z, int dx, int dy, int dz, boolean clipped)190   void plotLineDeltaAOld(int[] shades1, int[] shades2, int screenMask,
191                          int shadeIndex, int x, int y, int z, int dx, int dy,
192                          int dz, boolean clipped) {
193     // from cylinder -- standard bond with two colors or cone with one color
194     x1t = x;
195     x2t = x + dx;
196     y1t = y;
197     y2t = y + dy;
198     z1t = z;
199     z2t = z + dz;
200     if (clipped)
201       switch (getTrimmedLineImpl()) {
202       case VISIBILITY_OFFSCREEN:
203         return;
204       case VISIBILITY_UNCLIPPED:
205         clipped = false;
206       }
207     // special shading for bonds
208     int[] zbuf = g3d.zbuf;
209     int width = g3d.width;
210     int runIndex = 0;
211     int rise = Integer.MAX_VALUE;
212     int run = 1;
213     int offset = y * width + x;
214     int offsetMax = g3d.bufferSize;
215     int shadeIndexUp = (shadeIndex < Shader.SHADE_INDEX_LAST ? shadeIndex + 1
216         : shadeIndex);
217     int shadeIndexDn = (shadeIndex > 0 ? shadeIndex - 1 : shadeIndex);
218     int argb1 = shades1[shadeIndex];
219     int argb1Up = shades1[shadeIndexUp];
220     int argb1Dn = shades1[shadeIndexDn];
221     int argb2 = shades2[shadeIndex];
222     int argb2Up = shades2[shadeIndexUp];
223     int argb2Dn = shades2[shadeIndexDn];
224     int argb = argb1;
225     Pixelator p = g3d.pixel;
226     if (screenMask != 0) {
227       p = g3d.setScreened((screenMask & 1) == 1);
228       g3d.currentShadeIndex = 0;
229     }
230     if (argb != 0 && !clipped && offset >= 0 && offset < offsetMax
231         && z < zbuf[offset])
232       p.addPixel(offset, z, argb);
233     if (dx == 0 && dy == 0)
234       return;
235     int xIncrement = 1;
236     int yOffsetIncrement = width;
237     int x2 = x + dx;
238     int y2 = y + dy;
239 
240     if (dx < 0) {
241       dx = -dx;
242       xIncrement = -1;
243     }
244     if (dy < 0) {
245       dy = -dy;
246       yOffsetIncrement = -width;
247     }
248     int twoDx = dx + dx, twoDy = dy + dy;
249 
250     // the z dimension and the z increment are stored with a fractional
251     // component in the bottom 10 bits.
252     int zCurrentScaled = z << 10;
253     int argbUp = argb1Up;
254     int argbDn = argb1Dn;
255     if (dy <= dx) {
256       int roundingFactor = dx - 1;
257       if (dz < 0)
258         roundingFactor = -roundingFactor;
259       int zIncrementScaled = ((dz << 10) + roundingFactor) / dx;
260       int twoDxAccumulatedYError = 0;
261       int n1 = Math.abs(x2 - x2t) - 1;
262       int n2 = Math.abs(x2 - x1t) - 1;
263       for (int n = dx - 1, nMid = n / 2; --n >= n1;) {
264         if (n == nMid) {
265           argb = argb2;
266           if (argb == 0)
267             return;
268           argbUp = argb2Up;
269           argbDn = argb2Dn;
270           if (screenMask % 3 != 0) {
271             p = g3d.setScreened((screenMask & 2) == 2);
272             g3d.currentShadeIndex = 0;
273           }
274         }
275         offset += xIncrement;
276         zCurrentScaled += zIncrementScaled;
277         twoDxAccumulatedYError += twoDy;
278         if (twoDxAccumulatedYError > dx) {
279           offset += yOffsetIncrement;
280           twoDxAccumulatedYError -= twoDx;
281         }
282         if (argb != 0 && n < n2 && offset >= 0 && offset < offsetMax
283             && runIndex < rise) {
284           int zCurrent = zCurrentScaled >> 10;
285           if (zCurrent < zbuf[offset]) {
286             int rand8 = shader.nextRandom8Bit();
287             p.addPixel(offset, zCurrent, rand8 < 85 ? argbDn
288                 : (rand8 > 170 ? argbUp : argb));
289           }
290         }
291         runIndex = (runIndex + 1) % run;
292       }
293     } else {
294       int roundingFactor = dy - 1;
295       if (dz < 0)
296         roundingFactor = -roundingFactor;
297       int zIncrementScaled = ((dz << 10) + roundingFactor) / dy;
298       int twoDyAccumulatedXError = 0;
299       int n1 = Math.abs(y2 - y2t) - 1;// + 1;
300       int n2 = Math.abs(y2 - y1t) - 1;// - 1;
301       for (int n = dy - 1, nMid = n / 2; --n >= n1;) {
302         if (n == nMid) {
303           argb = argb2;
304           if (argb == 0)
305             return;
306           argbUp = argb2Up;
307           argbDn = argb2Dn;
308           if (screenMask % 3 != 0) {
309             p = g3d.setScreened((screenMask & 2) == 2);
310             g3d.currentShadeIndex = 0;
311           }
312         }
313         offset += yOffsetIncrement;
314         zCurrentScaled += zIncrementScaled;
315         twoDyAccumulatedXError += twoDx;
316         if (twoDyAccumulatedXError > dy) {
317           offset += xIncrement;
318           twoDyAccumulatedXError -= twoDy;
319         }
320         if (argb != 0 && n < n2 && offset >= 0 && offset < offsetMax
321             && runIndex < rise) {
322           int zCurrent = zCurrentScaled >> 10;
323           if (zCurrent < zbuf[offset]) {
324             int rand8 = g3d.shader.nextRandom8Bit();
325             p.addPixel(offset, zCurrent, rand8 < 85 ? argbDn
326                 : (rand8 > 170 ? argbUp : argb));
327           }
328         }
329         runIndex = (runIndex + 1) % run;
330       }
331     }
332   }
333 
plotLineDeltaABitsFloat(int[] shades1, int[] shades2, int shadeIndex, P3 ptA, P3 ptB, int screenMask, boolean clipped)334   void plotLineDeltaABitsFloat(int[] shades1, int[] shades2, int shadeIndex, P3 ptA,
335                           P3 ptB, int screenMask, boolean clipped) {
336     // from cylinder -- cartoonRockets - somewhat higher precision, because
337     // particularly for draw, we can have very short distances.
338     int x = Math.round(ptA.x);
339     int y = Math.round(ptA.y);
340     int z = Math.round(ptA.z);
341     int bx = Math.round(ptB.x);
342     int by = Math.round(ptB.y);
343     int bz = Math.round(ptB.z);
344     int dx = bx - x;
345     int dy = by - y;
346 
347     x1t = x;
348     x2t = bx;
349     y1t = y;
350     y2t = by;
351     z1t = z;
352     z2t = bz;
353     if (clipped && getTrimmedLineImpl() == VISIBILITY_OFFSCREEN)
354       return;
355     // special shading for rockets; somewhat slower than above;
356     int[] zbuf = g3d.zbuf;
357     int width = g3d.width;
358     int runIndex = 0;
359     int rise = Integer.MAX_VALUE;
360     int run = 1;
361     int shadeIndexUp = (shadeIndex < Shader.SHADE_INDEX_LAST ? shadeIndex + 1
362         : shadeIndex);
363     int shadeIndexDn = (shadeIndex > 0 ? shadeIndex - 1 : shadeIndex);
364     int argb1 = shades1[shadeIndex];
365     int argb1Up = shades1[shadeIndexUp];
366     int argb1Dn = shades1[shadeIndexDn];
367     int argb2 = shades2[shadeIndex];
368     int argb2Up = shades2[shadeIndexUp];
369     int argb2Dn = shades2[shadeIndexDn];
370     int offset = y * width + x;
371     int offsetMax = g3d.bufferSize;
372     int i0, iMid, i1, i2, iIncrement, xIncrement, yOffsetIncrement;
373     if (lineTypeX) {
374       i0 = x;
375       i1 = x1t;
376       i2 = x2t;
377       iMid = x + dx / 2;
378       iIncrement = (dx >= 0 ? 1 : -1);
379       xIncrement = iIncrement;
380       yOffsetIncrement = (dy >= 0 ? width : -width);
381       setRastABFloat(ptA.x, ptA.z, ptB.x, ptB.z);
382     } else {
383       i0 = y;
384       i1 = y1t;
385       i2 = y2t;
386       iMid = y + dy / 2;
387       iIncrement = (dy >= 0 ? 1 : -1);
388       xIncrement = (dy >= 0 ? width : -width);
389       yOffsetIncrement = (dx >= 0 ? 1 : -1);
390       setRastABFloat(ptA.y, ptA.z, ptB.y, ptB.z);
391     }
392     float zCurrent = z;
393     int argb = argb1;
394     int argbUp = argb1Up;
395     int argbDn = argb1Dn;
396     boolean isInWindow = false;
397     Pixelator p = g3d.pixel;
398     if (screenMask != 0) {
399       p = g3d.setScreened((screenMask & 1) == 1);
400       g3d.currentShadeIndex = 0;
401     }
402     // "x" is not necessarily the x-axis.
403 
404     //  x----x1t-----------x2t---x2
405     //  ------------xMid-----------
406     //0-|------------------>-----------w
407 
408     // or
409 
410     //  x2---x2t-----------x1t----x
411     //  ------------xMid-----------
412     //0-------<-------------------|----w
413 
414     for (int i = i0, iBits = i0;; i += iIncrement, iBits += iIncrement) {
415       if (i == i1)
416         isInWindow = true;
417       if (i == iMid) {
418         argb = argb2;
419         if (argb == 0)
420           return;
421         argbUp = argb2Up;
422         argbDn = argb2Dn;
423         if (screenMask % 3 != 0) {
424           p = g3d.setScreened((screenMask & 2) == 2);
425           g3d.currentShadeIndex = 0;
426         }
427       }
428       if (argb != 0 && isInWindow && offset >= 0 && offset < offsetMax
429           && runIndex < rise) {
430         zCurrent = getZCurrent(a, b, i);
431         if (zCurrent < zbuf[offset]) {
432           int rand8 = shader.nextRandom8Bit();
433           p.addPixel(offset, (int) zCurrent, rand8 < 85 ? argbDn
434               : (rand8 > 170 ? argbUp : argb));
435         }
436       }
437       if (i == i2)
438         break;
439       runIndex = (runIndex + 1) % run;
440       offset += xIncrement;
441       while (iBits < 0)
442         iBits += nBits;
443       if (lineBits.get(iBits % nBits)) {
444         offset += yOffsetIncrement;
445       }
446     }
447   }
448 
plotLineDeltaABitsInt(int[] shades1, int[] shades2, int shadeIndex, P3i ptA, P3i ptB, int screenMask, boolean clipped)449   void plotLineDeltaABitsInt(int[] shades1, int[] shades2, int shadeIndex, P3i ptA,
450                           P3i ptB, int screenMask, boolean clipped) {
451     // from cylinder -- cartoonRockets
452     int x = ptA.x;
453     int y = ptA.y;
454     int z = ptA.z;
455     int bx = ptB.x;
456     int by = ptB.y;
457     int bz = ptB.z;
458     int dx = bx - x;
459     int dy = by - y;
460 
461     x1t = x;
462     x2t = bx;
463     y1t = y;
464     y2t = by;
465     z1t = z;
466     z2t = bz;
467     if (clipped && getTrimmedLineImpl() == VISIBILITY_OFFSCREEN)
468       return;
469     // special shading for rockets; somewhat slower than above;
470     int[] zbuf = g3d.zbuf;
471     int width = g3d.width;
472     int runIndex = 0;
473     int rise = Integer.MAX_VALUE;
474     int run = 1;
475     int shadeIndexUp = (shadeIndex < Shader.SHADE_INDEX_LAST ? shadeIndex + 1
476         : shadeIndex);
477     int shadeIndexDn = (shadeIndex > 0 ? shadeIndex - 1 : shadeIndex);
478     int argb1 = shades1[shadeIndex];
479     int argb1Up = shades1[shadeIndexUp];
480     int argb1Dn = shades1[shadeIndexDn];
481     int argb2 = shades2[shadeIndex];
482     int argb2Up = shades2[shadeIndexUp];
483     int argb2Dn = shades2[shadeIndexDn];
484     int offset = y * width + x;
485     int offsetMax = g3d.bufferSize;
486     int i0, iMid, i1, i2, iIncrement, xIncrement, yOffsetIncrement;
487     if (lineTypeX) {
488       i0 = x;
489       i1 = x1t;
490       i2 = x2t;
491       iMid = x + dx / 2;
492       iIncrement = (dx >= 0 ? 1 : -1);
493       xIncrement = iIncrement;
494       yOffsetIncrement = (dy >= 0 ? width : -width);
495       setRastAB(ptA.x, ptA.z, ptB.x, ptB.z);
496     } else {
497       i0 = y;
498       i1 = y1t;
499       i2 = y2t;
500       iMid = y + dy / 2;
501       iIncrement = (dy >= 0 ? 1 : -1);
502       xIncrement = (dy >= 0 ? width : -width);
503       yOffsetIncrement = (dx >= 0 ? 1 : -1);
504       setRastAB(ptA.y, ptA.z, ptB.y, ptB.z);
505     }
506     float zCurrent = z;
507     int argb = argb1;
508     int argbUp = argb1Up;
509     int argbDn = argb1Dn;
510     boolean isInWindow = false;
511     Pixelator p = g3d.pixel;
512     if (screenMask != 0) {
513       p = g3d.setScreened((screenMask & 1) == 1);
514       g3d.currentShadeIndex = 0;
515     }
516     // "x" is not necessarily the x-axis.
517 
518     //  x----x1t-----------x2t---x2
519     //  ------------xMid-----------
520     //0-|------------------>-----------w
521 
522     // or
523 
524     //  x2---x2t-----------x1t----x
525     //  ------------xMid-----------
526     //0-------<-------------------|----w
527 
528     for (int i = i0, iBits = i0;; i += iIncrement, iBits += iIncrement) {
529       if (i == i1)
530         isInWindow = true;
531       if (i == iMid) {
532         argb = argb2;
533         if (argb == 0)
534           return;
535         argbUp = argb2Up;
536         argbDn = argb2Dn;
537         if (screenMask % 3 != 0) {
538           p = g3d.setScreened((screenMask & 2) == 2);
539           g3d.currentShadeIndex = 0;
540         }
541       }
542       if (argb != 0 && isInWindow && offset >= 0 && offset < offsetMax
543           && runIndex < rise) {
544         zCurrent = getZCurrent(a, b, i);
545         if (zCurrent < zbuf[offset]) {
546           int rand8 = shader.nextRandom8Bit();
547           p.addPixel(offset, (int) zCurrent, rand8 < 85 ? argbDn
548               : (rand8 > 170 ? argbUp : argb));
549         }
550       }
551       if (i == i2)
552         break;
553       runIndex = (runIndex + 1) % run;
554       offset += xIncrement;
555       while (iBits < 0)
556         iBits += nBits;
557       if (lineBits.get(iBits % nBits)) {
558         offset += yOffsetIncrement;
559       }
560     }
561   }
562 
plotLineBits(int argbA, int argbB, P3i ptA, P3i ptB, int run, int rise, boolean andClip)563   void plotLineBits(int argbA, int argbB, P3i ptA, P3i ptB, int run, int rise, boolean andClip) {
564     // standard, dashed or not dashed
565     // measures, axes, bbcage, mesh, cylinders
566     if (ptA.z <= 1 || ptB.z <= 1)
567       return;
568     boolean clipped = true;
569     x1t = ptA.x;
570     y1t = ptA.y;
571     z1t = ptA.z;
572     x2t = ptB.x;
573     y2t = ptB.y;
574     z2t = ptB.z;
575     switch (getTrimmedLineImpl()) {
576     case VISIBILITY_OFFSCREEN:
577       return;
578     case VISIBILITY_UNCLIPPED:
579       clipped = false;
580       break;
581     default:
582       if (andClip) {
583         ptA.set(x1t, y1t, z1t);
584         ptB.set(x2t, y2t, z2t);
585       }
586     }
587     int[] zbuf = g3d.zbuf;
588     int width = g3d.width;
589     int runIndex = 0;
590     if (run == 0) {
591       rise = Integer.MAX_VALUE;
592       run = 1;
593     }
594     int x = ptA.x;
595     int y = ptA.y;
596     int z = ptA.z;
597     int dx = ptB.x - x;
598     int x2 = x + dx;
599     int dy = ptB.y - y;
600     int y2 = y + dy;
601     int offset = y * width + x;
602     int offsetMax = g3d.bufferSize;
603     //boolean flipflop = (((x ^ y) & 1) != 0);
604     //boolean tScreened = tScreened1;
605     int argb = argbA;
606     Pixelator p = g3d.pixel;
607     if (argb != 0 && !clipped && offset >= 0 && offset < offsetMax
608         && z < zbuf[offset])
609       p.addPixel(offset, z, argb);
610 
611     if (dx == 0 && dy == 0)
612       return;
613     int xIncrement = 1, yIncrement = 1;
614     int yOffsetIncrement = width;
615 
616     if (dx < 0) {
617       dx = -dx;
618       xIncrement = -1;
619     }
620     if (dy < 0) {
621       dy = -dy;
622       yOffsetIncrement = -width;
623       yIncrement = -1;
624     }
625     int twoDx = dx + dx, twoDy = dy + dy;
626 
627     // the z dimension and the z increment are stored with a fractional
628     // component in the bottom 10 bits.
629     if (dy <= dx) {
630       // using x
631       setRastAB(ptA.x, ptA.z, ptB.x, ptB.z);
632       int twoDxAccumulatedYError = 0;
633       int n1 = Math.abs(x2 - x2t) - 1;
634       int n2 = Math.abs(x2 - x1t) - 1;
635       for (int n = dx - 1, nMid = n / 2; --n >= n1;) {
636         if (n == nMid) {
637           argb = argbB;
638           if (argb == 0)
639             return;
640         }
641         offset += xIncrement;
642         x += xIncrement;
643         twoDxAccumulatedYError += twoDy;
644         if (twoDxAccumulatedYError > dx) {
645           offset += yOffsetIncrement;
646           twoDxAccumulatedYError -= twoDx;
647         }
648         if (argb != 0 && n < n2 && offset >= 0 && offset < offsetMax
649             && runIndex < rise) {
650           int zCurrent = getZCurrent(a, b, x);
651           if (zCurrent < zbuf[offset])
652             p.addPixel(offset, zCurrent, argb);
653         }
654         runIndex = (runIndex + 1) % run;
655       }
656     } else {
657       // using y
658       setRastAB(ptA.y, ptA.z, ptB.y, ptB.z);
659       int twoDyAccumulatedXError = 0;
660       int n1 = Math.abs(y2 - y2t) - 1;
661       int n2 = Math.abs(y2 - y1t) - 1;
662       for (int n = dy - 1, nMid = n / 2; --n >= n1;) {
663         if (n == nMid) {
664           argb = argbB;
665           if (argb == 0)
666             return;
667         }
668         offset += yOffsetIncrement;
669         y += yIncrement;
670         twoDyAccumulatedXError += twoDx;
671         if (twoDyAccumulatedXError > dy) {
672           offset += xIncrement;
673           twoDyAccumulatedXError -= twoDy;
674         }
675         if (argb != 0 && n < n2 && offset >= 0 && offset < offsetMax
676             && runIndex < rise) {
677           int zCurrent = getZCurrent(a, b, y);
678           if (zCurrent < zbuf[offset])
679             p.addPixel(offset, zCurrent, argb);
680         }
681         runIndex = (runIndex + 1) % run;
682       }
683     }
684   }
685 
686   private final static int VISIBILITY_UNCLIPPED = 0;
687   private final static int VISIBILITY_CLIPPED = 1;
688   private final static int VISIBILITY_OFFSCREEN = 2;
689 
690   private int x1t, y1t, z1t, x2t, y2t, z2t; // trimmed
691 
692   /**
693    * <p>
694    * Cohen-Sutherland line clipping used to check visibility.
695    * </p>
696    * <p>
697    * Note that this routine is only used for visibility checking. To avoid
698    * integer rounding errors which cause cracking to occur in 'solid' surfaces,
699    * the lines are actually drawn from their original end-points.
700    *
701    * The nuance is that this algorithm doesn't just deliver a boolean. It
702    * delivers the trimmed line. Although we need to start the raster loop at the
703    * origin for good surfaces, we can save lots of time by saving the known
704    * endpoints as globals variables. -- Bob Hanson 7/06
705    * </p>
706    *
707    * @return Visibility (see VISIBILITY_... constants);
708    */
709 
getTrimmedLineImpl()710   private int getTrimmedLineImpl() { // formerly "visibilityCheck()"
711 
712     int cc1 = g3d.clipCode3(x1t, y1t, z1t);
713     int cc2 = g3d.clipCode3(x2t, y2t, z2t);
714     int c = (cc1 | cc2);
715     if ((cc1 | cc2) == 0)
716       return VISIBILITY_UNCLIPPED;
717     if (c == -1)
718       return VISIBILITY_OFFSCREEN;
719     int xLast = g3d.xLast;
720     int yLast = g3d.yLast;
721     int slab = g3d.slab;
722     int depth = g3d.depth;
723     do {
724       if ((cc1 & cc2) != 0)
725         return VISIBILITY_OFFSCREEN;
726 
727       float dx = x2t - x1t;
728       float dy = y2t - y1t;
729       float dz = z2t - z1t;
730       if (cc1 != 0) { //cohen-sutherland line clipping
731         if ((cc1 & GData.xLT) != 0) {
732           y1t += (int) ((-x1t * dy) / dx);
733           z1t += (int) ((-x1t * dz) / dx);
734           x1t = 0;
735         } else if ((cc1 & GData.xGT) != 0) {
736           y1t += (int) (((xLast - x1t) * dy) / dx);
737           z1t += (int) (((xLast - x1t) * dz) / dx);
738           x1t = xLast;
739         } else if ((cc1 & GData.yLT) != 0) {
740           x1t += (int) ((-y1t * dx) / dy);
741           z1t += (int) ((-y1t * dz) / dy);
742           y1t = 0;
743         } else if ((cc1 & GData.yGT) != 0) {
744           x1t += (int) (((yLast - y1t) * dx) / dy);
745           z1t += (int) (((yLast - y1t) * dz) / dy);
746           y1t = yLast;
747         } else if ((cc1 & GData.zLT) != 0) {
748           x1t += (int) (((slab - z1t) * dx) / dz);
749           y1t += (int) (((slab - z1t) * dy) / dz);
750           z1t = slab;
751         } else // must be zGT
752         {
753           x1t += (int) (((depth - z1t) * dx) / dz);
754           y1t += (int) (((depth - z1t) * dy) / dz);
755           z1t = depth;
756         }
757 
758         cc1 = g3d.clipCode3(x1t, y1t, z1t);
759       } else {
760         if ((cc2 & GData.xLT) != 0) {
761           y2t += (int) ((-x2t * dy) / dx);
762           z2t += (int) ((-x2t * dz) / dx);
763           x2t = 0;
764         } else if ((cc2 & GData.xGT) != 0) {
765           y2t += (int) (((xLast - x2t) * dy) / dx);
766           z2t += (int) (((xLast - x2t) * dz) / dx);
767           x2t = xLast;
768         } else if ((cc2 & GData.yLT) != 0) {
769           x2t += (int) ((-y2t * dx) / dy);
770           z2t += (int) ((-y2t * dz) / dy);
771           y2t = 0;
772         } else if ((cc2 & GData.yGT) != 0) {
773           x2t += (int) (((yLast - y2t) * dx) / dy);
774           z2t += (int) (((yLast - y2t) * dz) / dy);
775           y2t = yLast;
776         } else if ((cc2 & GData.zLT) != 0) {
777           x2t += (int) (((slab - z2t) * dx) / dz);
778           y2t += (int) (((slab - z2t) * dy) / dz);
779           z2t = slab;
780         } else // must be zGT
781         {
782           x2t += (int) (((depth - z2t) * dx) / dz);
783           y2t += (int) (((depth - z2t) * dy) / dz);
784           z2t = depth;
785         }
786         cc2 = g3d.clipCode3(x2t, y2t, z2t);
787       }
788     } while ((cc1 | cc2) != 0);
789     return VISIBILITY_CLIPPED;
790   }
791 
792   /**
793    * low-resolution linear z-buffer (old style)
794    *
795    * @param argb1
796    * @param argb2
797    * @param x
798    * @param y
799    * @param z
800    * @param dx
801    * @param dy
802    * @param dz
803    * @param clipped
804    * @param run
805    * @param rise
806    */
plotLineClippedOld(int argb1, int argb2, int x, int y, int z, int dx, int dy, int dz, boolean clipped, int run, int rise)807   private void plotLineClippedOld(int argb1, int argb2, int x, int y, int z,
808                                   int dx, int dy, int dz, boolean clipped,
809                                   int run, int rise) {
810     // standard, dashed or not dashed -- isosurface mesh
811     int[] zbuf = g3d.zbuf;
812     int width = g3d.width;
813     int runIndex = 0;
814     if (run == 0) {
815       rise = Integer.MAX_VALUE;
816       run = 1;
817     }
818     int offset = y * width + x;
819     int offsetMax = g3d.bufferSize;
820     //boolean flipflop = (((x ^ y) & 1) != 0);
821     //boolean tScreened = tScreened1;
822     int argb = argb1;
823     Pixelator p = g3d.pixel;
824     if (argb != 0 && !clipped && offset >= 0 && offset < offsetMax
825         && z < zbuf[offset])
826       p.addPixel(offset, z, argb);
827     if (dx == 0 && dy == 0)
828       return;
829     int xIncrement = 1;
830     int yOffsetIncrement = width;
831 
832     int x2 = x + dx;
833     int y2 = y + dy;
834 
835     if (dx < 0) {
836       dx = -dx;
837       xIncrement = -1;
838     }
839     if (dy < 0) {
840       dy = -dy;
841       yOffsetIncrement = -width;
842     }
843     int twoDx = dx + dx, twoDy = dy + dy;
844 
845     // the z dimension and the z increment are stored with a fractional
846     // component in the bottom 10 bits.
847     int zCurrentScaled = z << 10;
848     if (dy <= dx) {
849       int roundingFactor = dx - 1;
850       if (dz < 0)
851         roundingFactor = -roundingFactor;
852       int zIncrementScaled = ((dz << 10) + roundingFactor) / dx;
853       int twoDxAccumulatedYError = 0;
854       int n1 = Math.abs(x2 - x2t) - 1;
855       int n2 = Math.abs(x2 - x1t) - 1;
856       for (int n = dx - 1, nMid = n / 2; --n >= n1;) {
857         if (n == nMid) {
858 
859           //          tScreened = tScreened2;
860 
861           argb = argb2;
862           if (argb == 0)
863             return;
864         }
865         offset += xIncrement;
866         zCurrentScaled += zIncrementScaled;
867         twoDxAccumulatedYError += twoDy;
868         if (twoDxAccumulatedYError > dx) {
869           offset += yOffsetIncrement;
870           twoDxAccumulatedYError -= twoDx;
871           //          flipflop = !flipflop;
872         }
873         if (argb != 0 && n < n2 && offset >= 0 && offset < offsetMax
874             && runIndex < rise) {
875           int zCurrent = zCurrentScaled >> 10;
876           if (zCurrent < zbuf[offset])
877             p.addPixel(offset, zCurrent, argb);
878         }
879         runIndex = (runIndex + 1) % run;
880       }
881     } else {
882       int roundingFactor = dy - 1;
883       if (dz < 0)
884         roundingFactor = -roundingFactor;
885       int zIncrementScaled = ((dz << 10) + roundingFactor) / dy;
886       int twoDyAccumulatedXError = 0;
887       int n1 = Math.abs(y2 - y2t) - 1;
888       int n2 = Math.abs(y2 - y1t) - 1;
889       for (int n = dy - 1, nMid = n / 2; --n >= n1;) {
890         if (n == nMid) {
891           //          tScreened = tScreened2;
892           argb = argb2;
893           if (argb == 0)
894             return;
895         }
896         offset += yOffsetIncrement;
897         zCurrentScaled += zIncrementScaled;
898         twoDyAccumulatedXError += twoDx;
899         if (twoDyAccumulatedXError > dy) {
900           offset += xIncrement;
901           twoDyAccumulatedXError -= twoDy;
902         }
903         if (argb != 0 && n < n2 && offset >= 0 && offset < offsetMax
904             && runIndex < rise) {
905           int zCurrent = zCurrentScaled >> 10;
906           if (zCurrent < zbuf[offset])
907             p.addPixel(offset, zCurrent, argb);
908         }
909         runIndex = (runIndex + 1) % run;
910       }
911     }
912   }
913 
914 }
915