diff --git a/hidden.js b/hidden.js
index 2ac57bd..3058a8b 100644
--- a/hidden.js
+++ b/hidden.js
@@ -264,22 +264,19 @@ function intercept_lines(p0,p1,p2,p3)
// returns a list of segments that are visible
// TODO: best if triangles is sorted by z depth
// TODO: figure out a better representation for the screen map
-function hidden_wire(s, screen_map, work_queue)
+function hidden_wire(s, interval, work_queue)
{
let segments = [];
- let min_key_x = Math.trunc(Math.min(s.p0.x, s.p1.x) / stl_key2d_scale);
- let min_key_y = Math.trunc(Math.min(s.p0.y, s.p1.y) / stl_key2d_scale);
- let max_key_x = Math.trunc(Math.max(s.p0.x, s.p1.x) / stl_key2d_scale);
- let max_key_y = Math.trunc(Math.max(s.p0.y, s.p1.y) / stl_key2d_scale);
+ if (!p_max) p_max = createVector();
+ if (!p_min) p_min = createVector();
- for(let x = min_key_x ; x <= max_key_x ; x++)
- {
- for(let y = min_key_y ; y <= max_key_y ; y++)
- {
- let triangles = screen_map[x + "," + y];
- if (!triangles)
- continue;
+ v3max(p_max, s.p0, s.p1);
+ v3min(p_min, s.p0, s.p1);
+
+ let triangles = interval.query(p_min.x,p_min.y,p_max.x,p_max.y);
+ //console.log("occlude ", triangles.length);
+ //triangles.sort((a,b) => a.min.z - b.min.z);
for(let t of triangles)
{
@@ -300,7 +297,10 @@ function hidden_wire(s, screen_map, work_queue)
// this triangle, which means that no other
// triangles on the sorted list can occlude
// the segment, so we're done.
- break;
+ //break;
+
+ // but the interval tree isn't sorted (yet)
+ continue;
}
if (rc == tri_clipped
@@ -317,8 +317,6 @@ function hidden_wire(s, screen_map, work_queue)
// huh?
console.log("occlude() returned? ", rc)
}
- }
- }
// if we have made it all the way here, the remaining part
// of this segment is visible and should be added to the draw list
diff --git a/index.html b/index.html
index 3e3dd4e..a6fb02b 100644
--- a/index.html
+++ b/index.html
@@ -7,6 +7,7 @@
+
diff --git a/interval.js b/interval.js
new file mode 100644
index 0000000..c0c12a8
--- /dev/null
+++ b/interval.js
@@ -0,0 +1,92 @@
+/*
+ * 2D interval tree for sorting triangles.
+ */
+
+function Interval(x,y,scale)
+{
+ this.x = x;
+ this.y = y;
+ this.scale = scale;
+
+ console.log("Interval("+x+","+y+","+scale+")");
+
+ this.quads = [
+ null,
+ null,
+ null,
+ null,
+ ];
+
+ this.span = [];
+
+ this.quad_mask = function (min_x,min_y,max_x,max_y)
+ {
+ let q0 = (this.x < max_x && this.y < max_y) ? 0x01: 0x00;
+ let q1 = (min_x < this.x && this.y < max_y) ? 0x02: 0x00;
+ let q2 = (min_x < this.x && min_y < this.y) ? 0x04: 0x00;
+ let q3 = (this.x < max_x && min_y < this.y) ? 0x08: 0x00;
+ let span = ((min_x < this.x && this.x < max_x)
+ || (min_y < this.y && this.y < max_y)) ? 0x10 : 0x00;;
+
+ return span | q0 | q1 | q2 | q3;
+ };
+
+ this.insert = function (t,min_x,min_y,max_x,max_y) {
+ if (min_x > max_x || min_y > max_y)
+ console.log(t);
+
+ if (this.scale > 8)
+ {
+ let quads = this.quad_mask(min_x,min_y,max_x,max_y);
+ //console.log(t, quads);
+
+ for (let quad = 0 ; quad < 4 ; quad++)
+ {
+ if (quads != (1 << quad))
+ continue;
+ return this.quad(quad).insert(t,min_x,min_y,max_x,max_y);
+ }
+ }
+
+ // multiple quadrants, put this in the span list
+ this.span.push(t);
+ return this;
+ };
+
+ this.quad = function (q) {
+ if (this.quads[q])
+ return this.quads[q];
+
+ let ns = this.scale / 2;
+ let nx = this.x + (q == 0 || q == 3 ? ns : -ns);
+ let ny = this.y + (q == 0 || q == 1 ? ns : -ns);
+ this.quads[q] = new Interval(nx,ny,ns);
+ return this.quads[q];
+ };
+
+ this.query = function(min_x,min_y,max_x,max_y) {
+ if (min_x > max_x || min_y > max_y)
+ console.log("error:", min_x,min_y,max_x,max_y);
+
+ let quads = this.quad_mask(min_x,min_y,max_x,max_y);
+ let quad_list = [];
+
+ for (let quad = 0 ; quad < 4 ; quad++)
+ {
+ if ((quads & (1 << quad)) == 0)
+ continue;
+ if (!this.quads[quad])
+ continue;
+
+ let new_list = this.quads[quad].query(min_x,min_y,max_x,max_y);
+ quad_list = quad_list.concat(new_list);
+ }
+
+ // multiple quadrants? put this in the span list
+ //if ((quads & 0x10) != 0)
+ quad_list = quad_list.concat(this.span);
+
+ //console.log("query:", this.x, this.y, min_x,min_y,max_x,max_y, quads, quad_list);
+ return quad_list;
+ };
+};
diff --git a/sketch.js b/sketch.js
index b333912..6d88126 100644
--- a/sketch.js
+++ b/sketch.js
@@ -367,6 +367,6 @@ function draw()
pop();
// they are dragging; do not try to do any additional work
- if (!mouseIsPressed && !vx && !vy)
+ if (!mouseIsPressed)
redraw = stl.do_work(camera, 200);
}
diff --git a/stl.js b/stl.js
index aebb82c..9d475f0 100644
--- a/stl.js
+++ b/stl.js
@@ -91,16 +91,19 @@ function STL(rawbytes_arraybuffer)
this.visible_segments = [];
this.segments = [];
this.coplanar = [];
+ this.interval = null;
// project the triangles into the screen mapping
console.log("projecting triangles");
for(let t of this.triangles)
this.project_triangle(t, camera);
+/*
// sort the screen mapped triangles by Z
console.log("sorting triangles");
for(let key in this.screen_map)
this.screen_map[key].sort((a,b) => a.min.z - b.min.z);
+*/
}
this.project_triangle = function(t,camera)
@@ -142,7 +145,7 @@ function STL(rawbytes_arraybuffer)
this.segments.push({ p0: t2, p1: t0 });
}
-
+/*
// build the screen map for all of the sectors
// that might contain this triangle's projection
let min_key_x = Math.trunc(t.min.x/stl_key2d_scale);
@@ -161,6 +164,7 @@ function STL(rawbytes_arraybuffer)
this.screen_map[key] = [t];
}
}
+*/
}
@@ -186,6 +190,7 @@ function STL(rawbytes_arraybuffer)
}
this.project(camera);
+
return true;
}
@@ -194,6 +199,24 @@ function STL(rawbytes_arraybuffer)
// only processes some of the segments per call.
this.do_hidden = function(camera,ms)
{
+ if (!this.interval)
+ {
+ // ofload the interval work until we are
+ // starting to compute hidden lines
+ this.interval = new Interval(0,0,width/2);
+
+ // insert the triangle into the interval tree for
+ // faster queries of segment intersections
+ for(t of this.triangles)
+ {
+ if (t.invisible)
+ continue;
+ this.interval.insert(t, t.min.x, t.min.y, t.max.x, t.max.y);
+ }
+
+ return true;
+ }
+
let num_segments = this.segments.length;
if (num_segments == 0)
return false;
@@ -207,7 +230,7 @@ function STL(rawbytes_arraybuffer)
while(this.segments.length != 0)
{
let s = this.segments.shift();
- let visible_segment = hidden_wire(s, this.screen_map, this.segments);
+ let visible_segment = hidden_wire(s, this.interval, this.segments);
if (visible_segment)
this.visible_segments.push(visible_segment);
count++;
diff --git a/triangle.js b/triangle.js
index 0c355e3..7c5dca5 100644
--- a/triangle.js
+++ b/triangle.js
@@ -75,6 +75,10 @@ function Triangle(p0, p1, p2)
// either off-screen or backface culling
this.invisible = false;
+ // discarded triangles are completely eliminated, so they will
+ // not be processed ever again.
+ this.discarded = false;
+
// compute the coordinates in screen space and decide
// if it is onscreen or backfaced culled
@@ -83,6 +87,9 @@ function Triangle(p0, p1, p2)
this.generation = generation;
this.invisible = true; // assume it will be discarded
+ if (this.discarded)
+ return false;
+
let s0 = camera.project(this.model[0], this.screen[0]);
let s1 = camera.project(this.model[1], this.screen[1]);
let s2 = camera.project(this.model[2], this.screen[2]);
@@ -161,7 +168,10 @@ function Triangle(p0, p1, p2)
// all three points match; this must be a duplicate
// triangle of some sort.
if (matches == 0b111)
- console.log("three points match? " + this + " " + t);
+ {
+ //console.log("three points match? " + this + " " + t);
+ return -1;
+ }
return 0;
}
@@ -176,6 +186,12 @@ function Triangle(p0, p1, p2)
for(let t of triangle_map[stl_key3d(p)])
{
let edges = this.coplanar_check(t);
+ if (edges === -1)
+ {
+ t.discarded = true;
+ continue;
+ }
+
if (edges == 0)
continue;