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