»
Tiếng AnhTiếng PhápTiếng Việt

In - Đồ họa hoạt hóa 3D đơn giản với JavaScript - JavaScriptBank.com

Phiên bản đầy đủ: jsB@nk » Ứng dụng » Đồ họa hoạt hóa 3D đơn giản với JavaScript
URL: https://www.javascriptbank.com/simple-3d-graphics-animation-javascript.html

Đồ họa hoạt hóa 3D đơn giản với JavaScript © JavaScriptBank.comTrong đoạn mã nguồn JavaScript này, chúng ta có một mô hình hoạt hóa 3D đơn giản với các thành phần đồ họa cơ bản, hãy cùng nâng cao kĩ năng JavaScript của bạn thông qua ví dụ mẫu JavaScript cao cấp này.

Phiên bản đầy đủ: jsB@nk » Ứng dụng » Đồ họa hoạt hóa 3D đơn giản với 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>