diff --git a/prototypes/leaf/heap.pde b/prototypes/leaf/heap.pde new file mode 100644 index 00000000..15879b26 --- /dev/null +++ b/prototypes/leaf/heap.pde @@ -0,0 +1,85 @@ +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; + } + + 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--) { + siftDown(i); + } + } + + 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(); + + 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/leaf.pde b/prototypes/leaf/leaf.pde index d1f754e8..6296c909 100644 --- a/prototypes/leaf/leaf.pde +++ b/prototypes/leaf/leaf.pde @@ -22,14 +22,17 @@ EasyOCS ocs; 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; - - triangle(x1, y1, x2, y2, x3, y3); + 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); + + addTriangle(p1, p2, p3); } void setup() { @@ -42,7 +45,6 @@ void setup() { void draw() { background(0); - noStroke(); float step = sin(radians(frameCount / PI)) * PI; @@ -54,5 +56,7 @@ void draw() { drawTriangle(WIDTH / 2, HEIGHT / 2, radius, -angle); } + sbuf.rasterize(); + ocs.update(); } diff --git a/prototypes/leaf/sbuf.pde b/prototypes/leaf/sbuf.pde new file mode 100644 index 00000000..324bac98 --- /dev/null +++ b/prototypes/leaf/sbuf.pde @@ -0,0 +1,125 @@ +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 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; + } +} + +class Segment { + int ys, ye; + float xs, xe; + float dxs, dxe; + 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; + this.dxe = dxe; + this.ys = ys; + this.ye = ye; + this.c = c; + } +}; + +class SegmentFinished implements Predicate { + @Override public boolean test(Segment s) { + return s.ys >= s.ye; + } +} + +class SegmentBuffer { + ArrayList segments[]; // as many as lines on the screen + + SegmentBuffer() { + segments = new ArrayList[HEIGHT]; + + for (int i = 0; i < HEIGHT; i++) { + segments[i] = new ArrayList(); + } + } + + void add(Segment s) { + if (s.ye < 0 || s.ys >= HEIGHT) { + return; + } + + // 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; + } + + // clip against bottom of the screen + if (s.ye > HEIGHT) { + s.ye = HEIGHT; + } + + segments[s.ys].add(s); + } + + void rasterize() { + ArrayList active = new ArrayList(); + BinaryHeap spans = new BinaryHeap(new SpanDepth()); + + loadPixels(); + + for (int y = 0; y < HEIGHT; y++) { + active.addAll(segments[y]); + segments[y].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); + + spans.add(new Span(xs, xe, s.c)); + } + + s.xs += s.dxs; + s.xe += s.dxe; + s.ys += 1; + } + + active.removeIf(new SegmentFinished()); + + 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(); + } +} + +SegmentBuffer sbuf = new SegmentBuffer(); diff --git a/prototypes/leaf/triangle.pde b/prototypes/leaf/triangle.pde new file mode 100644 index 00000000..eb538750 --- /dev/null +++ b/prototypes/leaf/triangle.pde @@ -0,0 +1,54 @@ +void addSegment(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; + + sbuf.add(new Segment(xs, xe, dxs, dxe, ysi, yei, g.fillColor)); +} + +void addTriangle(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) { + addSegment(p1.x, p1.x, dx21, dx31, p1.y, p2.y); + addSegment(p2.x, x_mid, dx32, dx31, p2.y, p3.y); + } else { + addSegment(p1.x, p1.x, dx31, dx21, p1.y, p2.y); + addSegment(x_mid, p2.x, dx31, dx32, p2.y, p3.y); + } +}