»
EnglishFrenchVietnamese

Print - Simple 3D Graphics Animation in JavaScript - JavaScriptBank.com

Full version: jsB@nk » Utility » Simple 3D Graphics Animation in JavaScript
URL: https://www.javascriptbank.com/simple-3d-graphics-animation-javascript.html

Simple 3D Graphics Animation in JavaScript © JavaScriptBank.comHere are some 3D graphics made with just JavaScript, let teach yourself by this advance JavaScript code & tutorial. We have a TIE fighter rotating, and moving left and right; we may change axises, drawing modes, viewing styles and scenes.

Full version: jsB@nk » Utility » Simple 3D Graphics Animation in JavaScript
URL: https://www.javascriptbank.com/simple-3d-graphics-animation-javascript.html



JavaScript
<script type="text/javascript" from="JavaScriptBank.com">/*     This script downloaded from www.JavaScriptBank.com     Come to view and download over 2000+ free javascript at www.JavaScriptBank.com*//************************************* * Copyright 2006 Christopher Thomas * * You are free to redistribute this * *  code, in modified or unmodified  * * form, so long as you retain this  * *          copyright notice.        * *************************************//****************************************************************************** * Due to the performance issues of painting pixels, I had to create a custom * * z-buffering algorithm that works on pixel ranges rather than individual    * * pixels to reduce the number of canvas draw calls.  It might be             * * patentable... though I doubt it is worth anything nowadays due to the      * * relative costs of operations of modern hardware (and even most software    * * framebuffers).                                                             * ******************************************************************************/var canvas;var ctx;var FULL_WIDTH = 800;var FULL_HEIGHT = 600;var WIDTH = 800;var HEIGHT = 600;var avgRenderTime = 0;// x, y, zvar vert1 = [[-1,-1,-1,1], [-1,1,-1,1], [1,1,-1,1], [1, -1, -1,1],    [-1,-1,1,1], [-1,1,1,1], [1,1,1,1], [1, -1, 1,1]];var tri1 = [[0, 1, 2, "rgb(255,0,0)"], [2, 3, 0, "rgb(255,0,0)"],   [4, 6, 5, "rgb(0,255,0)"], [6, 4, 7, "rgb(0,255,0)"],   [0, 5, 1, "rgb(0,0,255)"], [0, 4, 5, "rgb(0,0,255)"],   [3, 2, 7, "rgb(0,255,255)"], [2, 6, 7, "rgb(0,255,255)"],   [0, 3, 4, "rgb(255,255,0)"], [3, 7, 4, "rgb(255,255,0)"],   [1, 6, 2, "rgb(255,0,255)"], [1, 5, 6, "rgb(255,0,255)"]];var vert2 = [[-1, -1.5, .51, 1], [-1, 1.5, -.51, 1], [0, -1.5, .51, 1],    [-1, .5, .51, 1], [2, 1.5, -.2, 1], [-2, 1.5, .51, 1],    [1, 1.5, .51, 1], [2, 1.5, .51, 1], [-1, -1.5, -.51, 1]];var tri2 = [[0, 1, 2, "rgb(255,0,0)"],    [3, 5, 4, "rgb(0,255,0)"],    [6, 7, 8, "rgb(0,0,255)"]];var vert3 = [[.866, 0, .5, 1], [.5, .866, .5, 1], [0, 0, .5, 1],     [.5, .866, .5, 1], [-.5, .866, .5, 1], [0, 0, .5, 1],     [-.5, .866, .5, 1], [-.866, 0, .5, 1], [0, 0, .5, 1],     [-.866, .5, .5, 1], [-.866, -.5, .5, 1], [0, 0, .5, 1],     [-.866, 0, .5, 1], [-.5, -.866, .5, 1], [0, 0, .5, 1],     [-.5, -.866, .5, 1], [.5, -.866, .5, 1], [0, 0, .5, 1],     [.5, -.866, .5, 1], [.866, 0, .5, 1], [0, 0, .5, 1],     [.866, -.5, .5, 1], [.866, .5, .5, 1], [0, 0, .5, 1],     [.866, 0, -.5, 1], [.5, .866, -.5, 1], [0, 0, -.5, 1],     [.5, .866, -.5, 1], [-.5, .866, -.5, 1], [0, 0, -.5, 1],     [-.5, .866, -.5, 1], [-.866, 0, -.5, 1], [0, 0, -.5, 1],     [-.866, .5, -.5, 1], [-.866, -.5, -.5, 1], [0, 0, -.5, 1],     [-.866, 0, -.5, 1], [-.5, -.866, -.5, 1], [0, 0, -.5, 1],     [-.5, -.866, -.5, 1], [.5, -.866, -.5, 1], [0, 0, -.5, 1],     [.5, -.866, -.5, 1], [.866, 0, -.5, 1], [0, 0, -.5, 1],     [.866, -.5, -.5, 1], [.866, .5, -.5, 1], [0, 0, -.5, 1],     [.25, 0, 0, 1], [0, .25, 0, 1], [.0, 0, -.5, 1],     [.25, 0, 0, 1], [0, -.25, 0, 1], [.0, 0, -.5, 1],     [-.25, 0, 0, 1], [0, .25, 0, 1], [.0, 0, -.5, 1],     [-.25, 0, 0, 1], [0, -.25, 0, 1], [.0, 0, -.5, 1],     [.25, 0, 0, 1], [0, .25, 0, 1], [.0, 0, .5, 1],     [.25, 0, 0, 1], [0, -.25, 0, 1], [.0, 0, .5, 1],     [-.25, 0, 0, 1], [0, .25, 0, 1], [.0, 0, .5, 1],     [-.25, 0, 0, 1], [0, -.25, 0, 1], [.0, 0, .5, 1],];// I did not properly order some of these triangles, so they're facing the wrong wayvar tri3 = [[0, 1, 2, "rgb(64, 64, 64)"],    [3, 4, 5, "rgb(64, 64, 64)"],    [6, 7, 8, "rgb(64, 64, 64)"],    //    [9, 10, 11, "rgb(64, 64, 64)"],    [12, 13, 14, "rgb(64, 64, 64)"],    [15, 16, 17, "rgb(64, 64, 64)"],    [18, 19, 20, "rgb(64, 64, 64)"],    //    [21, 22, 23, "rgb(64, 64, 64)"],    [24, 25, 26, "rgb(64, 64, 64)"],    [27, 28, 29, "rgb(64, 64, 64)"],    [30, 31, 32, "rgb(64, 64, 64)"],    //    [33, 34, 35, "rgb(64, 64, 64)"],    [36, 37, 38, "rgb(64, 64, 64)"],    [39, 40, 41, "rgb(64, 64, 64)"],    [42, 43, 44, "rgb(64, 64, 64)"],    //    [45, 46, 47, "rgb(64, 64, 64)"],    [48, 49, 50, "rgb(128, 128, 128)"],    [51, 52, 53, "rgb(128, 128, 128)"],    [54, 55, 56, "rgb(128, 128, 128)"],    [57, 58, 59, "rgb(128, 128, 128)"],    [60, 61, 62, "rgb(128, 128, 128)"],    [63, 64, 65, "rgb(128, 128, 128)"],    [66, 67, 68, "rgb(128, 128, 128)"],    [69, 70, 71, "rgb(128, 128, 128)"]];var vert = vert3;var tri = tri3;var twosided = true;var angle = 47.4;var axis = "X";var mode = "lines";var lastPoint = null;var fastLines = true;var fill = false;var scanLineInfo;var zbuff;var drawInterval;var dumpZ = false;function changeQuality(direction) {  if (direction == "better" && WIDTH < FULL_WIDTH) {    WIDTH *= 2; HEIGHT *= 2;  }  else if (direction == "worse" && FULL_WIDTH/WIDTH < 16){    WIDTH /= 2; HEIGHT /= 2;  }  if (WIDTH < FULL_WIDTH) {    document.getElementById("qUpButton").disabled = false;  }  else    document.getElementById("qUpButton").disabled = true;  if (FULL_WIDTH / WIDTH >= 16) {    document.getElementById("qDownButton").disabled = true;  }  else    document.getElementById("qDownButton").disabled = false;  document.getElementById("qualitySpan").innerHTML = (WIDTH / FULL_WIDTH)*100 + "%";}function init() {  canvas = document.getElementById("theCanvas");  var error = false;  try {    ctx = canvas.getContext("2d");  } catch(e) {    document.getElementById("errorMsg").style.display="block"; error = true;  };    if (!error)    drawInterval = setInterval(plot, 10);}function dist(a, b) {  var da; var db; var dc;  da = a[0] - b[0];  db = a[1] - b[1];  dc = a[2] - b[2];  return Math.sqrt(da*da + db*db + dc*dc)}function min(a, b) {  return (a < b) ? a : b;}function max(a, b) {  return (a > b) ? a : b;}function bresenham(x2, y2, z2) {  if (!lastPoint) {    lastPoint = [x2, y2, z2];    return; // first point, just set up start point  }  var x1 = lastPoint[0];  var y1 = lastPoint[1];  var z1 = lastPoint[2];  lastPoint = [x2,y2,z2];  var steep;  var temp;  steep = Math.abs(y2-y1) > Math.abs(x2-x1);  if (steep) {    temp = x1;    x1 = y1;    y1 = temp;    temp = x2;    x2 = y2;    y2 = temp;  }  if (x1 > x2) {    temp = x1;    x1 = x2;    x2 = temp;    temp = y1;    y1 = y2;    y2 = temp;    temp = z1;    z1 = z2;    z2 = temp;  }  var dx = x2 - x1;  var dy = Math.abs(y2 - y1);  var error = 0;  var derror = dy / dx;  var y = y1;  var ystep;  if (y1 < y2)    ystep = 1;  else    ystep = -1;  var x;  var minx, maxx;  var zA;  var zB;  for (x=x1; x<=x2; x++) {    var dx = max(x2 - x1, .01);    if (steep) {      plotPixel(y, x);      if (fill && x >= 0 && x < HEIGHT) {if (scanLineInfo[x]) {  minx = scanLineInfo[x][0];  maxx = scanLineInfo[x][1];  zA = scanLineInfo[x][2];  zB = scanLineInfo[x][3];}else {  minx = y;  maxx = y;  zA = (x-x1)/dx * (z2-z1) + z1;  zB = (x-x1)/dx * (z2-z1) + z1;}if (y < minx) {  minx = y;  zA = (x-x1)/dx * (z2-z1) + z1;}if (y > maxx) {  maxx = y;  zB = (x-x1)/dx * (z2-z1) + z1;}if (isNaN(zA) || isNaN(zB)) {  alert(x + " " + x1 + " " + dx + " " + z2 + " " + z1);  null[5] = 0;}scanLineInfo[x] = [minx, maxx, zA, zB];      }    }    else {      plotPixel(x, y);      if (fill && y >= 0 && y < HEIGHT) {if (scanLineInfo[y]) {  minx = scanLineInfo[y][0];  maxx = scanLineInfo[y][1];  zA = scanLineInfo[y][2];  zB = scanLineInfo[y][3];}else {  minx = x;  maxx = x;  zA = (x-x1)/dx * (z2-z1) + z1;  zB = (x-x1)/dx * (z2-z1) + z1;}if (x < minx) {  minx = x;  zA = (x-x1)/dx * (z2-z1) + z1;}if (x > maxx) {  maxx = x;  zB = (x-x1)/dx * (z2-z1) + z1;}scanLineInfo[y] = [minx, maxx, zA, zB];      }    }    error += derror;    if (error > .5) {      y += ystep;      error--;    }  }}function plotPixel(x, y) {  if (fill)    return;  ctx.fillRect(x, y, 1, 1);}function doVert(v, d, first) {  var y1 = (v[1]*d) / (d + v[2]);  var x1 = (v[0]*d) / (d + v[2]);  var z1 = v[2];  // center 0,0 on the screen  y1 *= HEIGHT;  x1 *= HEIGHT;  y1 += HEIGHT/2;  x1 += WIDTH/2;  if (mode == "lines") {    if (fastLines) {      if (first)ctx.moveTo(x1, y1);      elsectx.lineTo(x1, y1);    }    else {      //bresenham(parseInt(x1), parseInt(y1), parseInt(z1));      bresenham(parseInt(x1), parseInt(y1), z1);    }  }  else  if (mode == "points") {    ctx.fillRect(x1-1, y1-1, 2, 2);  }}function transform(v, m) {  var x2 = v[0] * m[0][0] + v[1]*m[0][1] + v[2]*m[0][2] + v[3]*m[0][3];  var y2 = v[0] * m[1][0] + v[1]*m[1][1] + v[2]*m[1][2] + v[3]*m[1][3];  var z2 = v[0] * m[2][0] + v[1]*m[2][1] + v[2]*m[2][2] + v[3]*m[2][3];  var w2 = v[0] * m[3][0] + v[1]*m[3][1] + v[2]*m[3][2] + v[3]*m[3][3];  var rv = [x2, y2, z2, w2];  return rv;}function plot() {  var start; var stop;  try { // safari compatibility    start = Date.now();  } catch (e) {}  angle += .05;  //angle = 94.148; axis = "Y"; fastLines = false; fill = true; dumpZ = true;clearInterval(drawInterval);  //angle = 94.149; axis = "Y"; fastLines = false; fill = true; dumpZ = true;clearInterval(drawInterval);  //angle = 56.7; axis = "Y"; fastLines = false; fill = true; dumpZ = true;clearInterval(drawInterval);  var xposition = 1.5 * Math.cos(1.05*angle);  var yposition = 0;//Math.sin(2*angle);  var aboutZ = (axis == "Z");  var aboutX = (axis == "X");  var i; var j;  var matrix;  ctx.save();  ctx.scale(FULL_WIDTH/WIDTH, FULL_HEIGHT/HEIGHT);  zbuff = []; // clear z buffer  ctx.clearRect(0, 0, WIDTH, HEIGHT); // clear screen  if (aboutZ) {    matrix = [[Math.cos(angle), -Math.sin(angle), 0, xposition],      [Math.sin(angle),  Math.cos(angle), 0, yposition],      [0,                0,               1, +5],      [0,                0,               0, 1]];  }  else if (aboutX) {    matrix = [[1,                0,                0,               xposition],      [0,                Math.cos(angle),  Math.sin(angle), yposition],      [0,                -Math.sin(angle), Math.cos(angle), +5],      [0,                0,                0,               1]];  }  else {    matrix = [[Math.cos(angle), 0,                 -Math.sin(angle), xposition],      [0,               1,                 0,                yposition],      [Math.sin(angle), 0,                 Math.cos(angle), +5],      [0,               0,                 0,               1]];  }  for (i=0; i<tri.length; i++) {    doTri(tri[i], matrix);  }  if (!fastLines && mode == "lines" && fill)    drawFromZBuff();  try { // safari compatibility    stop = Date.now();    avgRenderTime = (.9 * avgRenderTime) + (.1 * (stop - start));    window.status = "Frame render took " + (stop-start) + " ms (frame rate: " + parseInt(100000/avgRenderTime)/100 + " fps)";  } catch (e) { }  ctx.restore();}function zForPoint(x, range, reason) {  var x1; var x2;  var z1; var z2;  x1 = range[0];  x2 = range[1];  z1 = range[2];  z2 = range[3];  var dx = x2-x1;  dx = max(dx, .001);  var fraction = (x-x1) / dx;  var rv = fraction * (z2-z1) + z1;  if (isNaN(rv)) {    clearInterval(drawInterval);    alert("x: " + x + " from range " + range + "\n" +  "dx was " + dx + " with fraction " + fraction + "\n" +  "z1: " + z1 + " z2: " + z2 + "\n" +   "requested by " + reason);    null[5] = 0;  }  return rv;}function horizOverlap(z, scan) {  var zx1; var zx2;  var sx1; var sx2;  zx1 = z[0]; zx2 = z[1];  sx1 = scan[0]; sx2 = scan[1];  if (zx1 >= sx2 || zx2 <= sx1)    return false; // no overlap  var minx, maxx, overlap;  if (zx1 <= sx1 && zx2 >= sx2) {    // we're smaller than zbuff    overlap = "newSmaller";  }  else if (sx1 <= zx1 && sx2 >= zx2) {    // we cover entire range    overlap = "newBigger";  }  else    overlap = "partial";  minx = max(zx1, sx1);  maxx = min(zx2, sx2);  if (minx == maxx)    return false; // they touch, or it's 0 width or something, so reject this  return [minx, maxx, overlap];}function cross(n1, n2) {  var x, y, z;  x = n1[1]*n2[2] - n1[2]*n2[1];  y = n1[2]*n2[0] - n1[0]*n2[2];  z = n1[0]*n2[1] - n1[1]*n2[0];  var w = 0;  return [x, y, z, w];}function dot(v1, v2) {  return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];}function normalize(v) {  var l = Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]+v[3]*v[3]);  return [v[0]/l, v[1]/l, v[2]/l, v[3]/l];}function doTri(poly, matrix) {  scanLineInfo = [];  lastPoint = null;  if (mode == "lines" && fastLines) {    ctx.beginPath();    ctx.lineJoin = "bevel";    ctx.strokeStyle = "rgb(0,0,0)";  }  var str;  if (dumpZ)    str = document.getElementById("statusText2").innerHTML;  for (j=0; j<3; j++) {    var d = 1;    var v = transform(vert[poly[j]], matrix);    //clearInterval(2);    //alert(v);    doVert(v, d, j == 0);    if (dumpZ)      str += v.join(", ") + "<br>";  }  str += "&nbsp;<br>";  if (dumpZ)    document.getElementById("statusText2").innerHTML = str;  doVert(transform(vert[poly[0]], matrix), d, false);  if (mode == "lines" && fastLines)    ctx.stroke();  if (mode == "lines" && fill) {    var v1 = vert[poly[0]];    var v2 = vert[poly[1]];    var v3 = vert[poly[2]];    var n1 = [v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2], 0];    var n2 = [v3[0]-v1[0], v3[1]-v1[1], v3[2]-v1[2], 0];    n1 = normalize(n1);    n2 = normalize(n2);    var norm = cross(n1, n2);    norm = normalize(norm);    norm = transform(norm, matrix);    // don't show backfacing    if (!twosided && dot(norm, [0, 0, -1]) < -.1)      return;    // lighting    var lightVec = normalize([.25, -.2, -1, 0]);    var scaleFactor = Math.abs(dot(norm, lightVec));    var newColor = poly[3];        newColor = newColor.replace(/.*\(/, "");    newColor = newColor.replace(/\)/, "");    newColor = newColor.split(/,/);    scaleFactor = max(scaleFactor, .3);    newColor = "rgb(" + parseInt(newColor[0]*scaleFactor) + "," +                        parseInt(newColor[1]*scaleFactor) + "," +                        parseInt(newColor[2]*scaleFactor) + ")";        for (var i=0; i<HEIGHT; i++) {      if (i == 563563)alert("Scanline " + i + "\n" +       "before: " + zbuff[i]);      // was there anything drawn to this row?      if (scanLineInfo[i]) {// pull out the zbuffer array for this rowvar zbrow = zbuff[i];if (!zbrow) {  zbrow = [[0, WIDTH, 9999, 9999, "rgb(255,255,255)"]];}for (var j=0; j<zbrow.length; j++) {  if (i == 563563) {    alert("for poly " + poly + "\n" +   "zbrow item " + j + "\n" +  "before: " + zbuff[i])  }    // is there horizontal overlap?  var range = horizOverlap(zbrow[j], scanLineInfo[i]);  if (range) {    var zbz1; var zbz2;    var mz1; var mz2;    zbz1 = zForPoint(range[0], zbrow[j], "z1");    zbz2 = zForPoint(range[1], zbrow[j], "z2");    mz1 = zForPoint(range[0], scanLineInfo[i], "s1");    mz2 = zForPoint(range[1], scanLineInfo[i], "s2");    if (zbz1 >= mz1 && zbz2 >= mz2)      {// over the entire overlap range, we're closer// set our material for this overlap rangeif (range[2] == "newSmaller") {  // split the range up into 3 pieces {z, us, z}  var newx1, newx2, newx3, newx4;  newx1 = zbrow[j][0];  newx2 = scanLineInfo[i][0];  newx3 = scanLineInfo[i][1];  newx4 = zbrow[j][1];  var oldMat = zbrow[j][4];  var oldz1 = zbrow[j][2];  var oldz2 = zbrow[j][3];  var addedCount = 0;  if (newx1 != newx2) {    zbrow.splice(j+1+addedCount, 0, [newx1, newx2, oldz1, zbz1, oldMat]);    addedCount++;  }  if (newx2 != newx3) {    zbrow.splice(j+1+addedCount, 0, [newx2, newx3, mz1, mz2, newColor]);    addedCount++;  }  if (newx3 != newx4) {    zbrow.splice(j+1+addedCount, 0, [newx3, newx4, zbz2, oldz2, oldMat]);    addedCount++;  }  zbrow.splice(j, 1);  j += 2; // skip the 2 we added}else if (range[2] == "newBigger") {  // leave the full range, but set it to our material and update the z values  zbrow[j][2] = mz1;  zbrow[j][3] = mz2;  zbrow[j][4] = newColor;}else {  // split the range into pieces, since we're not completely  // contained within the range or larger than it  if (zbrow[j][0] >= scanLineInfo[i][0]) {    // this poly is in the left half of the interval    // insert it, and move the start of original region right    if (i == 563563) {      var blahstr = "";      for (var blah = 0; blah < zbuff[i].length; blah++)blahstr += zbuff[i][blah] + "\n";      alert("left before\n" +    "j: " + j + "\n" +     "zbrow[j]: " + zbrow[j] + "\n" +     "scanLineInfo[i]: " + scanLineInfo[i] + "\n" +    "poly: " + poly + "\n" +     "range: " + range + "\n" +     "zbrow: " + blahstr);    }    zbrow[j][2] = zForPoint(scanLineInfo[i][1], zbrow[j], "lateCut");    zbrow[j][0] = range[1];//scanLineInfo[i][1];    if (i == 563563) alert("left mid: " + zbrow[j]);    var cutCount = (zbrow[j][0] == zbrow[j][1]) ? 1 : 0;    if (i == 563563 && cutCount == 1) alert("CUT!");    zbrow.splice(j, cutCount, [range[0], scanLineInfo[i][1], mz1, mz2, newColor]);    //if (confirm(zbrow))    //  null[5] = 0;    j++;    if (i == 563563) {      var blahstr = "";      for (var blah = 0; blah < zbuff[i].length; blah++)blahstr += zbuff[i][blah] + "\n";      alert("left after\n" +    "j: " + j + "\n" +     "zbrow[j]: " + zbrow[j] + "\n" +     "scanLineInfo[i]: " + scanLineInfo[i] + "\n" +    "range: " + range + "\n" +    "poly: " + poly + "\n" +     "zbrow: " + blahstr);    }  }  // i don' understand why this test is <= instead of >=  else if (zbrow[j][1] <= scanLineInfo[i][1]) {    // this poly is in the right half of the interval    // make original region end early and add the poly after it    if (i == 563563) {      var blahstr = "";      for (var blah = 0; blah < zbuff[i].length; blah++)blahstr += zbuff[i][blah] + "\n";      alert("right before\n" +    "j: " + j + "\n" +     "zbrow[j]: " + zbrow[j] + "\n" +     "scanLineInfo[i]: " + scanLineInfo[i] + "\n" +    "poly: " + poly + "\n" +     "range: " + range + "\n" +    "zbrow: " + blahstr);    }    // TODO can this be zbz1?    zbrow[j][3] = zForPoint(scanLineInfo[i][0], zbrow[j], "earlyCut");    zbrow[j][1] = range[0];    var cutCount = (zbrow[j][0] == zbrow[j][1]) ? 1 : 0;    zbrow.splice(j+1, cutCount, [range[0], range[1], mz1, mz2, newColor]);    //if (confirm(zbrow))    //  null[5] = 0;    j++; // skip over new range    if (i == 563563) {      var blahstr = "";      for (var blah = 0; blah < zbuff[i].length; blah++)blahstr += zbuff[i][blah] + "\n";      alert("right after\n" +    "j: " + j + "\n" +     "cutCount: " + cutCount + "\n" +    "zbrow[j]: " + zbrow[j] + "\n" +     "scanLineInfo[i]: " + scanLineInfo[i] + "\n" +    "range: " + range + "\n" +    "zbrow: " + blahstr);    }  }  else if (range[0] == range[1]) {    // HACK TODO FIXME!!!    // this range is 0px wide, so ignore it?    window.status = "ignored!";    if (i == 563563) alert("ignored");  }  else {    // wtf?        clearInterval(drawInterval);    alert("wtf?");    alert("need to split :-(\n" +   "range of overlap: " + range + "\n" +   "z: " + zbrow[j] + "\n" +   "poly: " + scanLineInfo[i] + "\n" +  "zbrow: " + zbrow);    null[5] = 0;  }}      }    else if (zbz1 <= mz1 && zbz2 <= mz2)      {// already something closer... do nothingif (i == 563563) alert("already something closer");      }    else      {// partially in front, partially behindvar isect = findIntersection(range, mz1, mz2, zbz1, zbz2);if (i == 563563) {  alert("partially in front, partially behind - doing intersect!\n" +"range: " + range + "\n" + "poly: " + scanLineInfo[i] + "\n" +"zb: " + zbrow[j] + "\n" + "isect: " + isect + "\n" + "j: " + j);}isect = parseInt(isect);var die = true;if (mz1 <= zbz1) {  // we're on the left of the range, so end result should be  // [existing] {[us] [existing]}  // where { } indicates range of overlap  if (i == 563563) {    var blahstr = "";    for (var blah = 0; blah < zbuff[i].length; blah++)      blahstr += zbuff[i][blah] + "\n";    alert("Partial Left Before: " + blahstr);  }  var newx1; var newx2; var newx3; var newx4;  newx1 = zbrow[j][0];  newx2 = range[0];  newx3 = isect;  newx4 = zbrow[j][1];  var oldz1 = zbrow[j][2];  var oldz2 = zbrow[j][3];  var oldMat = zbrow[j][4];  var addedCount = 0;  if (newx1 != newx2) {    zbrow.splice(j+1+addedCount, 0, [newx1, newx2, oldz1, zbz1, oldMat]);    addedCount++;  }  if (newx2 != newx3) {    zbrow.splice(j+1+addedCount, 0, [newx2, newx3, mz1, zForPoint(newx3, [range[0], range[1], mz1, mz2], "splitRegion"), newColor]);    addedCount++;  }  if (newx3 != newx4) {    zbrow.splice(j+1+addedCount, 0, [newx3, newx4, zForPoint(newx3, [range[0], range[1], zbz1, zbz2], "splitRegion"), oldz2, oldMat]);    addedCount++;  }  zbrow.splice(j, 1); // cut the existing one  die = false;}else if (mz2 <= zbz2) {  if (i == 563563) {    var blahstr = "";    for (var blah = 0; blah < zbrow.length; blah++)      blahstr += zbrow[blah] + "\n";    alert("Partial Right Before: " + blahstr + "\n" +   "scanLine: " + scanLineInfo[i] + "\n" +  "zb: " + zbrow[j] + "\n" +   "range: " + range + "\n");  }  var newx1, newx2, newx3, newx4;  newx1 = zbrow[j][0];  newx2 = isect;  newx3 = range[1];  newx4 = zbrow[j][1];  if (i == 563563) alert(newx1 + " " + newx2 + " " + newx3 + " " + newx4);  var oldz1 = zbrow[j][2];  var oldz2 = zbrow[j][3];  var oldMat = zbrow[j][4];  var addedCount = 0;  if (newx1 != newx2) {    zbrow.splice(j+1+addedCount, 0, [newx1, newx2, oldz1, zForPoint(isect, [range[0], range[1], zbz1, zbz2], "splitRegion"), oldMat]);    addedCount++;  }  if (newx2 != newx3) {    zbrow.splice(j+1+addedCount, 0, [newx2, newx3, zForPoint(isect, [range[0], range[1], mz1, mz2], "splitRegion"), mz2, newColor]);    addedCount++;  }  if (newx3 != newx4) {    zbrow.splice(j+1+addedCount, 0, [newx3, newx4, zbz2, oldz1, oldMat]);    addedCount++;  }  zbrow.splice(j, 1); // cut the existing one  if (i == 563563) {    var blahstr = "";    for (var blah = 0; blah < zbrow.length; blah++)      blahstr += zbrow[blah] + "\n";    alert("Partial Right After: " + blahstr);  }  die = false;}else {  alert("partial, strange!");}if (die) {  alert("partial :-(");  alert("range: " + range + "\n" +"zbrow: " + zbrow[j] + "\n" + "poly: " + scanLineInfo[i] + "\n");  //zbz1 + "->" + zbz2 + " vs " + mz1 + "->" + mz2);  clearInterval(drawInterval);  null[5] = 0;}      }  }  if (i == 563563 && zbuff[i]) {    var blahstr = "";    for (var blah = 0; blah < zbuff[i].length; blah++)      blahstr += zbuff[i][blah] + "\n";    alert("for poly " + poly + "\n" +   "after item " + j + ": " + blahstr);  }}zbuff[i] = zbrow;if (i == 563563) {  var blahstr = "";  for (var blah = 0; blah < zbuff[i].length; blah++)    blahstr += zbuff[i][blah] + "\n";  alert("after: " + blahstr);}      }    }  }}// return x coordinate of intersection pointfunction findIntersection(range, za1, za2, zb1, zb2) {  var dx = range[1] - range[0];  var dza = za2 - za1;  var dzb = zb2 - zb1;  var slopea = dza / dx;  var slopeb = dzb / dx;  var offseta = zForPoint(0, [range[0], range[1], za1, za2], "findIntersection");  var offsetb = zForPoint(0, [range[0], range[1], zb1, zb2], "findIntersection");  var rv = (offseta - offsetb) / (slopeb - slopea);  return rv;}function drawFromZBuff() {  var str;  if (dumpZ)    str = "<table>";  var j;  for (j=0; j<HEIGHT; j++) {    var zbrow = zbuff[j];    if (zbrow) {      if (dumpZ)str += "<tr><td>"+j+"</td>";      var i;      for (i=0; i<zbrow.length; i++) {if (!dumpZ)  if (zbrow[i][4] == "rgb(255,255,255)") {    continue; // don't bother drawing white  }ctx.beginPath();ctx.strokeStyle = zbrow[i][4];ctx.moveTo(zbrow[i][0], j+.5);ctx.lineTo(zbrow[i][1], j+.5);ctx.stroke();if (dumpZ)  str += "<td>" + zbrow[i] + "</td>";      }      if (dumpZ)str += "</tr>";    }  }  if (dumpZ) {    str += "</table>";    document.getElementById("statusText").innerHTML = str;  }  }window.onload = init;</script>


HTML
<p>Rotation axis: <input name="axis" value="Z" onclick="axis='Z'" type="radio"> Z      <input name="axis" value="Y" onclick="axis='Y';" type="radio"> Y      <input name="axis" value="X" onclick="axis='X';" checked="checked" type="radio"> X</p>    <p>Draw mode: <input name="drawStyle" value="lines" onclick="mode='lines'" checked="checked" type="radio"> Lines / Polygons      <input name="drawStyle" value="points" onclick="mode='points'" type="radio"> Points</p>    <p>View style: <input name="fastLines" value="fast" onclick="fastLines=true;fill=false;" checked="checked" type="radio"> Built in lines (fast) <input name="fastLines" value="slow" onclick="fastLines=false;fill=false;" type="radio"> Manual Bresenham <input name="fastLines" value="true2" onclick="fill=true; fastLines=false;" type="radio"> <b>Filled polygons</b></p>    <p>Scene: <input name="scene" value="square" onclick="vert=vert1;tri=tri1;twosided=false;" type="radio"> Cube <input name="scene" value="tris" onclick="vert=vert2;tri=tri2;twosided=true;" type="radio"> Mutually-overlapping triangles <input name="scene" value="tie" onclick="vert=vert3;tri=tri3;twosided=true;" checked="checked" type="radio"> <b>TIE fighter</b></p>    <p>Quality: <input id="qDownButton" value="Worse (faster)" onclick="changeQuality('worse')" type="button"> <input id="qUpButton" disabled="DISABLED" value="Better (slower)" onclick="changeQuality('better')" type="button"> <span id="qualitySpan">100%</span>    </p><div id="errorMsg" style="border: 5px solid red; display: none; font-size: 14pt;">Your browser doesn't support a required feature (the <a href="http://www.google.com/search?q=html+canvas+tag&amp;start=0&amp;start=0&amp;ie=utf-8&amp;oe=utf-8&amp;client=mozilla&amp;rls=org.mozilla:en-US:unofficial">HTML Canvas tag</a>).  You could get a <a href="http://www.mozilla.org/projects/seamonkey">better browser</a> (or <a href="http://www.mozilla.com/">this one</a>, or, if you're on a Mac, Safari).</div>    <canvas id="theCanvas" width="800" height="600" onmousemove="return;var y = event.clientY - document.getBoxObjectFor(document.getElementById('theCanvas')).y + document.body.scrollTop; var x = event.clientX - document.getBoxObjectFor(document.getElementById('theCanvas')).x + document.body.scrollLeft; window.status=x+', '+y;"></canvas>    <div id="statusText2"></div>    <div id="statusText"></div>