Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotating leaf effect using span buffer rasterization #6

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
85 changes: 85 additions & 0 deletions prototypes/leaf/heap.pde
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
class BinaryHeap<E> extends ArrayList<E> {
Comparator<E> cmp;

BinaryHeap(Comparator<E> 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;
}
}
};
22 changes: 13 additions & 9 deletions prototypes/leaf/leaf.pde
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -42,7 +45,6 @@ void setup() {

void draw() {
background(0);
noStroke();

float step = sin(radians(frameCount / PI)) * PI;

Expand All @@ -54,5 +56,7 @@ void draw() {
drawTriangle(WIDTH / 2, HEIGHT / 2, radius, -angle);
}

sbuf.rasterize();

ocs.update();
}
125 changes: 125 additions & 0 deletions prototypes/leaf/sbuf.pde
Original file line number Diff line number Diff line change
@@ -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<Span> {
@Override public int compare(Span s1, Span s2) {
return s1.xs - s2.xs;
}
}

class SpanDepth implements Comparator<Span> {
@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<Segment> {
@Override public boolean test(Segment s) {
return s.ys >= s.ye;
}
}

class SegmentBuffer {
ArrayList<Segment> segments[]; // as many as lines on the screen

SegmentBuffer() {
segments = new ArrayList[HEIGHT];

for (int i = 0; i < HEIGHT; i++) {
segments[i] = new ArrayList<Segment>();
}
}

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<Segment> active = new ArrayList<Segment>();
BinaryHeap<Span> spans = new BinaryHeap<Span>(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();
54 changes: 54 additions & 0 deletions prototypes/leaf/triangle.pde
Original file line number Diff line number Diff line change
@@ -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);
}
}