From c21e538ceb2ff247a0b12be6c8291ed618a38357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Wed, 5 Oct 2022 15:56:54 +0200 Subject: [PATCH 01/11] Add incomplete line edge algorithm. --- prototypes/easy_ocs.pde | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/prototypes/easy_ocs.pde b/prototypes/easy_ocs.pde index 23cba8ca..f86b2b93 100644 --- a/prototypes/easy_ocs.pde +++ b/prototypes/easy_ocs.pde @@ -92,6 +92,39 @@ class EasyOCS { throw new IllegalStateException("invalid color number"); } + void blitLine(int x1, int y1, int x2, int y2) { + if (y1 > y2) { + int xt = x1; x1 = x2; x2 = xt; + int yt = y1; y1 = y2; y2 = yt; + } + + int dx = x2 - x1; + int dy = y2 - y1; + + if (dy == 0) + return; + + int di = dx / dy; + int df = abs(dx) % dy; + int xi = x1; + int xf = 0; + int s = (dx >= 0) ? 1 : -1; + + loadPixels(); + + while (y1 < y2) { + // bxor(xi, y1++); + xi += di; + xf += df; + if (xf > dy) { + xf -= dy; + xi += s; + } + } + + updatePixels(); + } + void blitFill() { loadPixels(); for (int j = 0; j < HEIGHT; j++) { From 1116f913d27ae87398ed252efe383169bea8162c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Tue, 18 Oct 2022 15:44:11 +0200 Subject: [PATCH 02/11] Triangle rasterization with subpixeling. --- prototypes/leaf/leaf.pde | 86 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/prototypes/leaf/leaf.pde b/prototypes/leaf/leaf.pde index d1f754e8..6d546287 100644 --- a/prototypes/leaf/leaf.pde +++ b/prototypes/leaf/leaf.pde @@ -19,17 +19,86 @@ final color[] palette = new color[] { EasyOCS ocs; +void rasterizeSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { + assert(ys <= ye); + assert(xs <= xe); + + int ysi = ceil(ys); + int yei = ceil(ye); + float prestep = ysi - ys; + + xs += prestep * dxs; + xe += prestep * dxe; + + for (int y = ysi; y < yei; y++) { + if (y >= 0 && y < height) { + int xsi = floor(xs + 0.5); + int xei = floor(xe + 0.5); + + for (int x = xsi; x < xei; x++) { + if (x >= 0 && x < width) { + pixels[y * width + x] = g.fillColor; + } + } + } + + xs += dxs; + xe += dxe; + } +} + +void rasterizeTriangle(PVector p1, PVector p2, PVector p3) { + PVector pt; + + if (p1.y > p2.y) { + pt = p1; + p1 = p2; + p2 = pt; + } + + if (p1.y > p3.y) { + pt = p1; + p1 = p3; + p3 = pt; + } + + if (p2.y > p3.y) { + pt = p2; + p2 = p3; + p3 = pt; + } + + float dy21 = p2.y - p1.y; + float dy31 = p3.y - p1.y; + float dy32 = p3.y - p2.y; + + float dx21 = (dy21 > 0.0) ? (p2.x - p1.x) / dy21 : 0.0; + float dx31 = (dy31 > 0.0) ? (p3.x - p1.x) / dy31 : 0.0; + float dx32 = (dy32 > 0.0) ? (p3.x - p2.x) / dy32 : 0.0; + + float x_mid = p1.x + dx31 * dy21; + + // Split the triangle into two parts by p2.y + if (p2.x <= x_mid) { + rasterizeSegment(p1.x, p1.x, dx21, dx31, p1.y, p2.y); + rasterizeSegment(p2.x, x_mid, dx32, dx31, p2.y, p3.y); + } else { + rasterizeSegment(p1.x, p1.x, dx31, dx21, p1.y, p2.y); + rasterizeSegment(x_mid, p2.x, dx31, dx32, p2.y, p3.y); + } +} + void drawTriangle(float xc, float yc, float r, float angle) { angle -= radians(90); - float x1 = r * cos(angle + TWO_PI * 0 / 3) + xc; - float y1 = r * sin(angle + TWO_PI * 0 / 3) + yc; - float x2 = r * cos(angle + TWO_PI * 1 / 3) + xc; - float y2 = r * sin(angle + TWO_PI * 1 / 3) + yc; - float x3 = r * cos(angle + TWO_PI * 2 / 3) + xc; - float y3 = r * sin(angle + TWO_PI * 2 / 3) + yc; + PVector p1 = new PVector(r * cos(angle + TWO_PI * 0 / 3) + xc, + r * sin(angle + TWO_PI * 0 / 3) + yc); + PVector p2 = new PVector(r * cos(angle + TWO_PI * 1 / 3) + xc, + r * sin(angle + TWO_PI * 1 / 3) + yc); + PVector p3 = new PVector(r * cos(angle + TWO_PI * 2 / 3) + xc, + r * sin(angle + TWO_PI * 2 / 3) + yc); - triangle(x1, y1, x2, y2, x3, y3); + rasterizeTriangle(p1, p2, p3); } void setup() { @@ -43,6 +112,7 @@ void setup() { void draw() { background(0); noStroke(); + loadPixels(); float step = sin(radians(frameCount / PI)) * PI; @@ -54,5 +124,7 @@ void draw() { drawTriangle(WIDTH / 2, HEIGHT / 2, radius, -angle); } + updatePixels(); + ocs.update(); } From f06f55408fd049fa88bf20515a701da6d88546c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Tue, 18 Oct 2022 15:51:48 +0200 Subject: [PATCH 03/11] Move triangle rasterizer to separate file. --- prototypes/leaf/leaf.pde | 78 +++--------------------------------- prototypes/leaf/triangle.pde | 68 +++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 72 deletions(-) create mode 100644 prototypes/leaf/triangle.pde diff --git a/prototypes/leaf/leaf.pde b/prototypes/leaf/leaf.pde index 6d546287..e86c331e 100644 --- a/prototypes/leaf/leaf.pde +++ b/prototypes/leaf/leaf.pde @@ -19,83 +19,17 @@ final color[] palette = new color[] { EasyOCS ocs; -void rasterizeSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { - assert(ys <= ye); - assert(xs <= xe); - - int ysi = ceil(ys); - int yei = ceil(ye); - float prestep = ysi - ys; - - xs += prestep * dxs; - xe += prestep * dxe; - - for (int y = ysi; y < yei; y++) { - if (y >= 0 && y < height) { - int xsi = floor(xs + 0.5); - int xei = floor(xe + 0.5); - - for (int x = xsi; x < xei; x++) { - if (x >= 0 && x < width) { - pixels[y * width + x] = g.fillColor; - } - } - } - - xs += dxs; - xe += dxe; - } -} - -void rasterizeTriangle(PVector p1, PVector p2, PVector p3) { - PVector pt; - - if (p1.y > p2.y) { - pt = p1; - p1 = p2; - p2 = pt; - } - - if (p1.y > p3.y) { - pt = p1; - p1 = p3; - p3 = pt; - } - - if (p2.y > p3.y) { - pt = p2; - p2 = p3; - p3 = pt; - } - - float dy21 = p2.y - p1.y; - float dy31 = p3.y - p1.y; - float dy32 = p3.y - p2.y; - - float dx21 = (dy21 > 0.0) ? (p2.x - p1.x) / dy21 : 0.0; - float dx31 = (dy31 > 0.0) ? (p3.x - p1.x) / dy31 : 0.0; - float dx32 = (dy32 > 0.0) ? (p3.x - p2.x) / dy32 : 0.0; - - float x_mid = p1.x + dx31 * dy21; - - // Split the triangle into two parts by p2.y - if (p2.x <= x_mid) { - rasterizeSegment(p1.x, p1.x, dx21, dx31, p1.y, p2.y); - rasterizeSegment(p2.x, x_mid, dx32, dx31, p2.y, p3.y); - } else { - rasterizeSegment(p1.x, p1.x, dx31, dx21, p1.y, p2.y); - rasterizeSegment(x_mid, p2.x, dx31, dx32, p2.y, p3.y); - } -} - void drawTriangle(float xc, float yc, float r, float angle) { angle -= radians(90); - PVector p1 = new PVector(r * cos(angle + TWO_PI * 0 / 3) + xc, + PVector p1 = new PVector( + r * cos(angle + TWO_PI * 0 / 3) + xc, r * sin(angle + TWO_PI * 0 / 3) + yc); - PVector p2 = new PVector(r * cos(angle + TWO_PI * 1 / 3) + xc, + PVector p2 = new PVector( + r * cos(angle + TWO_PI * 1 / 3) + xc, r * sin(angle + TWO_PI * 1 / 3) + yc); - PVector p3 = new PVector(r * cos(angle + TWO_PI * 2 / 3) + xc, + PVector p3 = new PVector( + r * cos(angle + TWO_PI * 2 / 3) + xc, r * sin(angle + TWO_PI * 2 / 3) + yc); rasterizeTriangle(p1, p2, p3); diff --git a/prototypes/leaf/triangle.pde b/prototypes/leaf/triangle.pde new file mode 100644 index 00000000..cbe4cb88 --- /dev/null +++ b/prototypes/leaf/triangle.pde @@ -0,0 +1,68 @@ +void rasterizeSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { + assert(ys <= ye); + assert(xs <= xe); + + int ysi = ceil(ys); + int yei = ceil(ye); + float prestep = ysi - ys; + + xs += prestep * dxs; + xe += prestep * dxe; + + for (int y = ysi; y < yei; y++) { + if (y >= 0 && y < height) { + int xsi = floor(xs + 0.5); + int xei = floor(xe + 0.5); + + for (int x = xsi; x < xei; x++) { + if (x >= 0 && x < width) { + pixels[y * width + x] = g.fillColor; + } + } + } + + xs += dxs; + xe += dxe; + } +} + +void rasterizeTriangle(PVector p1, PVector p2, PVector p3) { + PVector pt; + + if (p1.y > p2.y) { + pt = p1; + p1 = p2; + p2 = pt; + } + + if (p1.y > p3.y) { + pt = p1; + p1 = p3; + p3 = pt; + } + + if (p2.y > p3.y) { + pt = p2; + p2 = p3; + p3 = pt; + } + + float dy21 = p2.y - p1.y; + float dy31 = p3.y - p1.y; + float dy32 = p3.y - p2.y; + + float dx21 = (dy21 > 0.0) ? (p2.x - p1.x) / dy21 : 0.0; + float dx31 = (dy31 > 0.0) ? (p3.x - p1.x) / dy31 : 0.0; + float dx32 = (dy32 > 0.0) ? (p3.x - p2.x) / dy32 : 0.0; + + float x_mid = p1.x + dx31 * dy21; + + // Split the triangle into two segments by p2.y + if (p2.x <= x_mid) { + rasterizeSegment(p1.x, p1.x, dx21, dx31, p1.y, p2.y); + rasterizeSegment(p2.x, x_mid, dx32, dx31, p2.y, p3.y); + } else { + rasterizeSegment(p1.x, p1.x, dx31, dx21, p1.y, p2.y); + rasterizeSegment(x_mid, p2.x, dx31, dx32, p2.y, p3.y); + } +} From 9dea491045afa9e1b9f2b01bcc7ca65b229f9d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Tue, 18 Oct 2022 17:10:05 +0200 Subject: [PATCH 04/11] Buggy implementation of naive span buffer. --- prototypes/leaf/leaf.pde | 6 +-- prototypes/leaf/triangle.pde | 100 +++++++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 26 deletions(-) diff --git a/prototypes/leaf/leaf.pde b/prototypes/leaf/leaf.pde index e86c331e..2a6350e1 100644 --- a/prototypes/leaf/leaf.pde +++ b/prototypes/leaf/leaf.pde @@ -32,7 +32,7 @@ void drawTriangle(float xc, float yc, float r, float angle) { r * cos(angle + TWO_PI * 2 / 3) + xc, r * sin(angle + TWO_PI * 2 / 3) + yc); - rasterizeTriangle(p1, p2, p3); + addTriangle(p1, p2, p3); } void setup() { @@ -45,8 +45,6 @@ void setup() { void draw() { background(0); - noStroke(); - loadPixels(); float step = sin(radians(frameCount / PI)) * PI; @@ -58,7 +56,7 @@ void draw() { drawTriangle(WIDTH / 2, HEIGHT / 2, radius, -angle); } - updatePixels(); + rasterize(); ocs.update(); } diff --git a/prototypes/leaf/triangle.pde b/prototypes/leaf/triangle.pde index cbe4cb88..788b43d4 100644 --- a/prototypes/leaf/triangle.pde +++ b/prototypes/leaf/triangle.pde @@ -1,4 +1,73 @@ -void rasterizeSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { +import java.util.*; + +class Span implements Comparable { + int ys, ye; + float xs, xe; + float dxs, dxe; + color c; + + Span(float xs, float xe, float dxs, float dxe, int ys, int ye, color c) { + this.xs = xs; + this.xe = xe; + this.dxs = dxs; + this.dxe = dxe; + this.ys = ys; + this.ye = ye; + this.c = c; + } + + @Override public int compareTo(Span other) { + return this.ys - other.ys; + } +}; + +ArrayList inactive = new ArrayList(); +ArrayList active = new ArrayList(); + +void rasterize() { + Collections.sort(inactive); + + loadPixels(); + + while (active.size() > 0 || inactive.size() > 0) { + if (active.size() == 0) { + active.add(inactive.remove(0)); + } + + while (inactive.size() > 0 && inactive.get(0).ys == active.get(0).ys) { + active.add(inactive.remove(0)); + } + + // println(active.get(0).ys, active.size(), inactive.size()); + + for (Span s : active) { + if (s.ys >= 0 && s.ys < height) { + int xsi = floor(s.xs + 0.5); + int xei = floor(s.xe + 0.5); + + // println(xsi, xei); + + for (int x = xsi; x < xei; x++) { + if (x >= 0 && x < width) { + pixels[s.ys * width + x] = s.c; + } + } + } + + s.xs += s.dxs; + s.xe += s.dxe; + s.ys++; + } + + while (active.size() > 0 && active.get(0).ys >= active.get(0).ye) { + active.remove(0); + } + } + + updatePixels(); +} + +void addSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { assert(ys <= ye); assert(xs <= xe); @@ -8,25 +77,12 @@ void rasterizeSegment(float xs, float xe, float dxs, float dxe, float ys, float xs += prestep * dxs; xe += prestep * dxe; - - for (int y = ysi; y < yei; y++) { - if (y >= 0 && y < height) { - int xsi = floor(xs + 0.5); - int xei = floor(xe + 0.5); - - for (int x = xsi; x < xei; x++) { - if (x >= 0 && x < width) { - pixels[y * width + x] = g.fillColor; - } - } - } - - xs += dxs; - xe += dxe; - } + + inactive.add(new Span(xs, ys, dxs, dxe, ysi, yei, g.fillColor)); } -void rasterizeTriangle(PVector p1, PVector p2, PVector p3) { + +void addTriangle(PVector p1, PVector p2, PVector p3) { PVector pt; if (p1.y > p2.y) { @@ -59,10 +115,10 @@ void rasterizeTriangle(PVector p1, PVector p2, PVector p3) { // Split the triangle into two segments by p2.y if (p2.x <= x_mid) { - rasterizeSegment(p1.x, p1.x, dx21, dx31, p1.y, p2.y); - rasterizeSegment(p2.x, x_mid, dx32, dx31, p2.y, p3.y); + addSegment(p1.x, p1.x, dx21, dx31, p1.y, p2.y); + addSegment(p2.x, x_mid, dx32, dx31, p2.y, p3.y); } else { - rasterizeSegment(p1.x, p1.x, dx31, dx21, p1.y, p2.y); - rasterizeSegment(x_mid, p2.x, dx31, dx32, p2.y, p3.y); + addSegment(p1.x, p1.x, dx31, dx21, p1.y, p2.y); + addSegment(x_mid, p2.x, dx31, dx32, p2.y, p3.y); } } From f74ba52f66802b93bd6898eca94d7ce3fb7665c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Wed, 19 Oct 2022 15:26:09 +0200 Subject: [PATCH 05/11] Less buggy implementation of naive span buffer. --- prototypes/leaf/sbuf.pde | 73 ++++++++++++++++++++++++++++++++++++ prototypes/leaf/triangle.pde | 73 +----------------------------------- 2 files changed, 75 insertions(+), 71 deletions(-) create mode 100644 prototypes/leaf/sbuf.pde diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde new file mode 100644 index 00000000..ea181498 --- /dev/null +++ b/prototypes/leaf/sbuf.pde @@ -0,0 +1,73 @@ +import java.util.*; + +class Span implements Comparable { + int ys, ye; + float xs, xe; + float dxs, dxe; + color c; + + Span(float xs, float xe, float dxs, float dxe, int ys, int ye, color c) { + this.xs = xs; + this.xe = xe; + this.dxs = dxs; + this.dxe = dxe; + this.ys = ys; + this.ye = ye; + this.c = c; + } + + @Override public int compareTo(Span other) { + return this.ys - other.ys; + } +}; + +ArrayList spans = new ArrayList(); + +void rasterize() { + ArrayList opened = new ArrayList(); + + int next = 0; + + Collections.sort(spans); + + loadPixels(); + + while (opened.size() > 0 || next < spans.size()) { + while (next < spans.size() && (opened.size() == 0 || spans.get(next).ys == opened.get(0).ys)) { + opened.add(spans.get(next++)); + } + + ListIterator iter = opened.listIterator(); + + // println("***"); + + while (iter.hasNext()) { + Span s = iter.next(); + + if (s.ys >= 0 && s.ys < height) { + int xsi = floor(s.xs + 0.5); + int xei = floor(s.xe + 0.5); + + // println(xsi, xei); + + for (int x = xsi; x < xei; x++) { + if (x >= 0 && x < width) { + pixels[s.ys * width + x] = s.c; + } + } + } + + s.xs += s.dxs; + s.xe += s.dxe; + s.ys += 1; + + if (s.ys >= s.ye) { + iter.remove(); + } + } + } + + updatePixels(); + + spans.clear(); +} diff --git a/prototypes/leaf/triangle.pde b/prototypes/leaf/triangle.pde index 788b43d4..88d77caf 100644 --- a/prototypes/leaf/triangle.pde +++ b/prototypes/leaf/triangle.pde @@ -1,72 +1,3 @@ -import java.util.*; - -class Span implements Comparable { - int ys, ye; - float xs, xe; - float dxs, dxe; - color c; - - Span(float xs, float xe, float dxs, float dxe, int ys, int ye, color c) { - this.xs = xs; - this.xe = xe; - this.dxs = dxs; - this.dxe = dxe; - this.ys = ys; - this.ye = ye; - this.c = c; - } - - @Override public int compareTo(Span other) { - return this.ys - other.ys; - } -}; - -ArrayList inactive = new ArrayList(); -ArrayList active = new ArrayList(); - -void rasterize() { - Collections.sort(inactive); - - loadPixels(); - - while (active.size() > 0 || inactive.size() > 0) { - if (active.size() == 0) { - active.add(inactive.remove(0)); - } - - while (inactive.size() > 0 && inactive.get(0).ys == active.get(0).ys) { - active.add(inactive.remove(0)); - } - - // println(active.get(0).ys, active.size(), inactive.size()); - - for (Span s : active) { - if (s.ys >= 0 && s.ys < height) { - int xsi = floor(s.xs + 0.5); - int xei = floor(s.xe + 0.5); - - // println(xsi, xei); - - for (int x = xsi; x < xei; x++) { - if (x >= 0 && x < width) { - pixels[s.ys * width + x] = s.c; - } - } - } - - s.xs += s.dxs; - s.xe += s.dxe; - s.ys++; - } - - while (active.size() > 0 && active.get(0).ys >= active.get(0).ye) { - active.remove(0); - } - } - - updatePixels(); -} - void addSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { assert(ys <= ye); assert(xs <= xe); @@ -77,8 +8,8 @@ void addSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { xs += prestep * dxs; xe += prestep * dxe; - - inactive.add(new Span(xs, ys, dxs, dxe, ysi, yei, g.fillColor)); + + spans.add(new Span(xs, xe, dxs, dxe, ysi, yei, g.fillColor)); } From bd8a0e8a3942e8c769f6b76ae496b3ae7e2b8875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Wed, 19 Oct 2022 17:12:16 +0200 Subject: [PATCH 06/11] Working simplified span buffer for leaf effect. --- prototypes/leaf/leaf.pde | 2 +- prototypes/leaf/sbuf.pde | 92 +++++++++++++++++++++++------------- prototypes/leaf/triangle.pde | 2 +- 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/prototypes/leaf/leaf.pde b/prototypes/leaf/leaf.pde index 2a6350e1..6296c909 100644 --- a/prototypes/leaf/leaf.pde +++ b/prototypes/leaf/leaf.pde @@ -56,7 +56,7 @@ void draw() { drawTriangle(WIDTH / 2, HEIGHT / 2, radius, -angle); } - rasterize(); + sbuf.rasterize(); ocs.update(); } diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde index ea181498..a4d26c7b 100644 --- a/prototypes/leaf/sbuf.pde +++ b/prototypes/leaf/sbuf.pde @@ -1,6 +1,8 @@ -import java.util.*; +import java.util.Comparator; +import java.util.Collections; +import java.util.function.Predicate; -class Span implements Comparable { +class Span { int ys, ye; float xs, xe; float dxs, dxe; @@ -15,59 +17,83 @@ class Span implements Comparable { this.ye = ye; this.c = c; } +}; - @Override public int compareTo(Span other) { - return this.ys - other.ys; +class SpanFinished implements Predicate { + @Override + public boolean test(Span s) { + return s.ys >= s.ye; } -}; +} + +class DepthComparator implements Comparator{ + @Override public int compare(Span s1, Span s2) { + return s1.c - s2.c; + } +} -ArrayList spans = new ArrayList(); +class SpanBuffer { + ArrayList spans[]; // as many as lines on the screen -void rasterize() { - ArrayList opened = new ArrayList(); + SpanBuffer(int n) { + spans = new ArrayList[n]; - int next = 0; + for (int i = 0; i < n; i++) { + spans[i] = new ArrayList(); + } + } - Collections.sort(spans); + void add(Span s) { + if (s.ye < 0 || s.ys >= spans.length) { + return; + } - loadPixels(); + // clip against top of the screen + if (s.ys < 0) { + s.xs -= s.ys * s.dxs; + s.xe -= s.ys * s.dxe; + s.ys = 0; + } - while (opened.size() > 0 || next < spans.size()) { - while (next < spans.size() && (opened.size() == 0 || spans.get(next).ys == opened.get(0).ys)) { - opened.add(spans.get(next++)); + // clip against bottom of the screen + if (s.ye > spans.length) { + s.ye = spans.length; } - ListIterator iter = opened.listIterator(); + spans[s.ys].add(s); + } + + void rasterize() { + ArrayList opened = new ArrayList(); - // println("***"); + loadPixels(); - while (iter.hasNext()) { - Span s = iter.next(); + for (int y = 0; y < spans.length; y++) { + opened.addAll(spans[y]); + opened.sort(new DepthComparator()); - if (s.ys >= 0 && s.ys < height) { + for (Span s : opened) { int xsi = floor(s.xs + 0.5); int xei = floor(s.xe + 0.5); - - // println(xsi, xei); - + for (int x = xsi; x < xei; x++) { if (x >= 0 && x < width) { pixels[s.ys * width + x] = s.c; } } - } - s.xs += s.dxs; - s.xe += s.dxe; - s.ys += 1; - - if (s.ys >= s.ye) { - iter.remove(); + s.xs += s.dxs; + s.xe += s.dxe; + s.ys += 1; } - } - } - updatePixels(); + opened.removeIf(new SpanFinished()); + + spans[y].clear(); + } - spans.clear(); + updatePixels(); + } } + +SpanBuffer sbuf = new SpanBuffer(HEIGHT); diff --git a/prototypes/leaf/triangle.pde b/prototypes/leaf/triangle.pde index 88d77caf..b6b4d430 100644 --- a/prototypes/leaf/triangle.pde +++ b/prototypes/leaf/triangle.pde @@ -9,7 +9,7 @@ void addSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { xs += prestep * dxs; xe += prestep * dxe; - spans.add(new Span(xs, xe, dxs, dxe, ysi, yei, g.fillColor)); + sbuf.add(new Span(xs, xe, dxs, dxe, ysi, yei, g.fillColor)); } From d9e4caf58b7bbeab6f75dbb107f6145aedc41714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Wed, 19 Oct 2022 17:15:33 +0200 Subject: [PATCH 07/11] Restore easy_ocs.pde to its original state. --- prototypes/easy_ocs.pde | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/prototypes/easy_ocs.pde b/prototypes/easy_ocs.pde index f86b2b93..23cba8ca 100644 --- a/prototypes/easy_ocs.pde +++ b/prototypes/easy_ocs.pde @@ -92,39 +92,6 @@ class EasyOCS { throw new IllegalStateException("invalid color number"); } - void blitLine(int x1, int y1, int x2, int y2) { - if (y1 > y2) { - int xt = x1; x1 = x2; x2 = xt; - int yt = y1; y1 = y2; y2 = yt; - } - - int dx = x2 - x1; - int dy = y2 - y1; - - if (dy == 0) - return; - - int di = dx / dy; - int df = abs(dx) % dy; - int xi = x1; - int xf = 0; - int s = (dx >= 0) ? 1 : -1; - - loadPixels(); - - while (y1 < y2) { - // bxor(xi, y1++); - xi += di; - xf += df; - if (xf > dy) { - xf -= dy; - xi += s; - } - } - - updatePixels(); - } - void blitFill() { loadPixels(); for (int j = 0; j < HEIGHT; j++) { From e54a925e230722468a16e1071331bca21919bb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Wed, 19 Oct 2022 17:22:23 +0200 Subject: [PATCH 08/11] X-clipping. --- prototypes/leaf/sbuf.pde | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde index a4d26c7b..179db40c 100644 --- a/prototypes/leaf/sbuf.pde +++ b/prototypes/leaf/sbuf.pde @@ -35,16 +35,16 @@ class DepthComparator implements Comparator{ class SpanBuffer { ArrayList spans[]; // as many as lines on the screen - SpanBuffer(int n) { - spans = new ArrayList[n]; + SpanBuffer() { + spans = new ArrayList[HEIGHT]; - for (int i = 0; i < n; i++) { + for (int i = 0; i < HEIGHT; i++) { spans[i] = new ArrayList(); } } void add(Span s) { - if (s.ye < 0 || s.ys >= spans.length) { + if (s.ye < 0 || s.ys >= HEIGHT) { return; } @@ -56,8 +56,8 @@ class SpanBuffer { } // clip against bottom of the screen - if (s.ye > spans.length) { - s.ye = spans.length; + if (s.ye > HEIGHT) { + s.ye = HEIGHT; } spans[s.ys].add(s); @@ -73,11 +73,14 @@ class SpanBuffer { opened.sort(new DepthComparator()); for (Span s : opened) { - int xsi = floor(s.xs + 0.5); - int xei = floor(s.xe + 0.5); - - for (int x = xsi; x < xei; x++) { - if (x >= 0 && x < width) { + int xs = floor(s.xs + 0.5); + int xe = floor(s.xe + 0.5); + + if (xe > 0 && xs < WIDTH) { + xs = max(xs, 0); + xe = min(xe, WIDTH); + + for (int x = xs; x < xe; x++) { pixels[s.ys * width + x] = s.c; } } @@ -96,4 +99,4 @@ class SpanBuffer { } } -SpanBuffer sbuf = new SpanBuffer(HEIGHT); +SpanBuffer sbuf = new SpanBuffer(); From af8e276e1e4a5068216a1806ff965df5867f0d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Wed, 19 Oct 2022 17:49:44 +0200 Subject: [PATCH 09/11] Segment lives over several lines, while Span only within single line. --- prototypes/leaf/sbuf.pde | 42 ++++++++++++++++++------------------ prototypes/leaf/triangle.pde | 3 +-- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde index 179db40c..2d682521 100644 --- a/prototypes/leaf/sbuf.pde +++ b/prototypes/leaf/sbuf.pde @@ -2,13 +2,13 @@ import java.util.Comparator; import java.util.Collections; import java.util.function.Predicate; -class Span { +class Segment { int ys, ye; float xs, xe; float dxs, dxe; color c; - Span(float xs, float xe, float dxs, float dxe, int ys, int ye, color c) { + Segment(float xs, float xe, float dxs, float dxe, int ys, int ye, color c) { this.xs = xs; this.xe = xe; this.dxs = dxs; @@ -19,31 +19,31 @@ class Span { } }; -class SpanFinished implements Predicate { +class SegmentFinished implements Predicate { @Override - public boolean test(Span s) { + public boolean test(Segment s) { return s.ys >= s.ye; } } -class DepthComparator implements Comparator{ - @Override public int compare(Span s1, Span s2) { +class SegmentDepth implements Comparator{ + @Override public int compare(Segment s1, Segment s2) { return s1.c - s2.c; } } -class SpanBuffer { - ArrayList spans[]; // as many as lines on the screen +class SegmentBuffer { + ArrayList segments[]; // as many as lines on the screen - SpanBuffer() { - spans = new ArrayList[HEIGHT]; + SegmentBuffer() { + segments = new ArrayList[HEIGHT]; for (int i = 0; i < HEIGHT; i++) { - spans[i] = new ArrayList(); + segments[i] = new ArrayList(); } } - void add(Span s) { + void add(Segment s) { if (s.ye < 0 || s.ys >= HEIGHT) { return; } @@ -60,19 +60,19 @@ class SpanBuffer { s.ye = HEIGHT; } - spans[s.ys].add(s); + segments[s.ys].add(s); } void rasterize() { - ArrayList opened = new ArrayList(); + ArrayList active = new ArrayList(); loadPixels(); - for (int y = 0; y < spans.length; y++) { - opened.addAll(spans[y]); - opened.sort(new DepthComparator()); + for (int y = 0; y < HEIGHT; y++) { + active.addAll(segments[y]); + active.sort(new SegmentDepth()); - for (Span s : opened) { + for (Segment s : active) { int xs = floor(s.xs + 0.5); int xe = floor(s.xe + 0.5); @@ -90,13 +90,13 @@ class SpanBuffer { s.ys += 1; } - opened.removeIf(new SpanFinished()); + active.removeIf(new SegmentFinished()); - spans[y].clear(); + segments[y].clear(); } updatePixels(); } } -SpanBuffer sbuf = new SpanBuffer(); +SegmentBuffer sbuf = new SegmentBuffer(); diff --git a/prototypes/leaf/triangle.pde b/prototypes/leaf/triangle.pde index b6b4d430..eb538750 100644 --- a/prototypes/leaf/triangle.pde +++ b/prototypes/leaf/triangle.pde @@ -9,10 +9,9 @@ void addSegment(float xs, float xe, float dxs, float dxe, float ys, float ye) { xs += prestep * dxs; xe += prestep * dxe; - sbuf.add(new Span(xs, xe, dxs, dxe, ysi, yei, g.fillColor)); + sbuf.add(new Segment(xs, xe, dxs, dxe, ysi, yei, g.fillColor)); } - void addTriangle(PVector p1, PVector p2, PVector p3) { PVector pt; From 73de2322442a106a125bb386635ac3c71f319b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Thu, 20 Oct 2022 17:06:49 +0200 Subject: [PATCH 10/11] Use binary heap to extract span with smallest color. --- prototypes/leaf/heap.pde | 69 ++++++++++++++++++++++++++++++++++++++++ prototypes/leaf/sbuf.pde | 45 ++++++++++++++++++-------- 2 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 prototypes/leaf/heap.pde diff --git a/prototypes/leaf/heap.pde b/prototypes/leaf/heap.pde new file mode 100644 index 00000000..38fef9fa --- /dev/null +++ b/prototypes/leaf/heap.pde @@ -0,0 +1,69 @@ +class BinaryHeap extends ArrayList { + Comparator cmp; + + BinaryHeap(Comparator cmp) { + super(); + this.cmp = cmp; + } + + int parent(int i) { + return (i - 1) / 2; + } + + int left(int i) { + return 2 * i + 1; + } + + int right(int i) { + return 2 * i + 2; + } + + void swap(int i, int j) { + E tmp = get(i); + set(i, get(j)); + set(j, tmp); + } + + boolean less(int i, int j) { + return cmp.compare(get(i), get(j)) < 0; + } + + E pop() { + int last = size() - 1; + swap(0, last); + E e = remove(last); + siftDown(0); + return e; + } + + void heapify() { + for (int i = size() / 2 - 1; i >= 0; i--) { + siftDown(i); + } + } + + void siftDown(int i) { + int n = size(); + + while (i < n) { + int l = left(i); + int r = right(i); + int j = i; + + if (l < n && less(l, j)) { + j = l; + } + + if (r < n && less(r, j)) { + j = r; + } + + if (i == j) { + break; + } + + swap(i, j); + i = j; + } + } +}; diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde index 2d682521..16ade1eb 100644 --- a/prototypes/leaf/sbuf.pde +++ b/prototypes/leaf/sbuf.pde @@ -2,6 +2,23 @@ import java.util.Comparator; import java.util.Collections; import java.util.function.Predicate; +class Span { + int xs, xe; + color c; + + Span(int xs, int xe, color c) { + this.xs = xs; + this.xe = xe; + this.c = c; + } +}; + +class SpanDepth implements Comparator { + @Override public int compare(Span s1, Span s2) { + return s1.c - s2.c; + } +} + class Segment { int ys, ye; float xs, xe; @@ -26,12 +43,6 @@ class SegmentFinished implements Predicate { } } -class SegmentDepth implements Comparator{ - @Override public int compare(Segment s1, Segment s2) { - return s1.c - s2.c; - } -} - class SegmentBuffer { ArrayList segments[]; // as many as lines on the screen @@ -65,24 +76,25 @@ class SegmentBuffer { void rasterize() { ArrayList active = new ArrayList(); + BinaryHeap spans = new BinaryHeap(new SpanDepth()); loadPixels(); for (int y = 0; y < HEIGHT; y++) { active.addAll(segments[y]); - active.sort(new SegmentDepth()); + segments[y].clear(); + + spans.clear(); for (Segment s : active) { int xs = floor(s.xs + 0.5); int xe = floor(s.xe + 0.5); - + if (xe > 0 && xs < WIDTH) { xs = max(xs, 0); xe = min(xe, WIDTH); - - for (int x = xs; x < xe; x++) { - pixels[s.ys * width + x] = s.c; - } + + spans.add(new Span(xs, xe, s.c)); } s.xs += s.dxs; @@ -92,7 +104,14 @@ class SegmentBuffer { active.removeIf(new SegmentFinished()); - segments[y].clear(); + spans.heapify(); + + while (spans.size() > 0) { + Span s = spans.pop(); + for (int x = s.xs; x < s.xe; x++) { + pixels[y * width + x] = s.c; + } + } } updatePixels(); From ac3d5e9981c5aae026913e445eff67cafb610a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krystian=20Bac=C5=82awski?= Date: Thu, 20 Oct 2022 17:51:31 +0200 Subject: [PATCH 11/11] Add peek & push operations on binary heap. --- prototypes/leaf/heap.pde | 16 ++++++++++++++++ prototypes/leaf/sbuf.pde | 12 ++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/prototypes/leaf/heap.pde b/prototypes/leaf/heap.pde index 38fef9fa..15879b26 100644 --- a/prototypes/leaf/heap.pde +++ b/prototypes/leaf/heap.pde @@ -35,6 +35,15 @@ class BinaryHeap extends ArrayList { siftDown(0); return e; } + + E peek() { + return get(0); + } + + void push(E e) { + add(e); + siftUp(size() - 1); + } void heapify() { for (int i = size() / 2 - 1; i >= 0; i--) { @@ -42,6 +51,13 @@ class BinaryHeap extends ArrayList { } } + void siftUp(int i) { + while (i > 0 && less(i, parent(i))) { + swap(i, parent(i)); + i = parent(i); + } + } + void siftDown(int i) { int n = size(); diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde index 16ade1eb..324bac98 100644 --- a/prototypes/leaf/sbuf.pde +++ b/prototypes/leaf/sbuf.pde @@ -13,6 +13,12 @@ class Span { } }; +class SpanFirst implements Comparator { + @Override public int compare(Span s1, Span s2) { + return s1.xs - s2.xs; + } +} + class SpanDepth implements Comparator { @Override public int compare(Span s1, Span s2) { return s1.c - s2.c; @@ -37,8 +43,7 @@ class Segment { }; class SegmentFinished implements Predicate { - @Override - public boolean test(Segment s) { + @Override public boolean test(Segment s) { return s.ys >= s.ye; } } @@ -84,8 +89,6 @@ class SegmentBuffer { active.addAll(segments[y]); segments[y].clear(); - spans.clear(); - for (Segment s : active) { int xs = floor(s.xs + 0.5); int xe = floor(s.xe + 0.5); @@ -108,6 +111,7 @@ class SegmentBuffer { while (spans.size() > 0) { Span s = spans.pop(); + for (int x = s.xs; x < s.xe; x++) { pixels[y * width + x] = s.c; }