summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2024-10-05 08:28:22 +0200
committerMitya Selivanov <automainint@guattari.tech>2024-10-05 08:28:22 +0200
commitd4f820e37bbb6571af4587adb2a3b3519d76849a (patch)
tree09f34b65f91c8f678c659d758ca967d5415c8d85
parentcb518b00efc8fe29df713e652450e90448340c29 (diff)
downloadreduced_system_layer-d4f820e37bbb6571af4587adb2a3b3519d76849a.zip
Draw triangles and lines
-rwxr-xr-xexamples/graph.c66
-rwxr-xr-xexamples/gravity.c11
-rwxr-xr-xexamples/ui.c18
-rw-r--r--graphics.c163
4 files changed, 223 insertions, 35 deletions
diff --git a/examples/graph.c b/examples/graph.c
new file mode 100755
index 0000000..c7b2877
--- /dev/null
+++ b/examples/graph.c
@@ -0,0 +1,66 @@
+#if 0 /*
+#/ ================================================================
+#/
+#/ graph.c
+#/
+#/ ================================================================
+#/
+#/ Self-compilation shell script
+#/
+SRC=${0##*./}
+BIN=${SRC%.*}
+gcc \
+ -Wall -Wextra -Werror -pedantic \
+ -Wno-old-style-declaration \
+ -Wno-missing-braces \
+ -Wno-unused-variable \
+ -Wno-unused-but-set-variable \
+ -Wno-unused-parameter \
+ -Wno-overlength-strings \
+ -O3 \
+ -fsanitize=undefined,address,leak -mshstk \
+ -lX11 -lm \
+ -o $BIN $SRC && \
+ ./$BIN $@ && rm $BIN
+exit $? # */
+#endif
+
+#include "../graphics.c"
+
+i32 main(i32 argc, c8 **argv) {
+ (void) argc;
+ (void) argv;
+
+ platform = (Platform) {
+ .title = "Graph",
+ .frame_width = 960,
+ .frame_height = 720,
+ };
+
+ u32 WHITE = u32_from_rgb(1.f, 1.f, 1.f);
+ u32 BLACK = u32_from_rgb(0.f, 0.f, 0.f);
+ u32 RED = u32_from_rgb(1.f, 0.f, 0.f);
+ u32 BLUE = u32_from_rgb(0.f, 0.f, 1.f);
+
+ p_init();
+
+ while (!platform.done) {
+ p_wait_events();
+
+ i64 x = platform.frame_width / 2;
+ i64 y = platform.frame_height / 2;
+
+ fill_rectangle(OP_SET, WHITE, 0, 0, platform.frame_width, platform.frame_height);
+
+ fill_triangle(OP_SET, RED, 100, 100, 200, 100, 150, 200);
+
+ fill_line(OP_SET, BLUE, 100, 300, 300, 500, 30);
+
+ fill_ellipse(OP_SET, BLACK, x - 140, y - 100, 280, 200);
+
+ p_render_frame();
+ }
+
+ p_cleanup();
+ return 0;
+}
diff --git a/examples/gravity.c b/examples/gravity.c
index 8bc45e0..246aab1 100755
--- a/examples/gravity.c
+++ b/examples/gravity.c
@@ -40,13 +40,6 @@ typedef struct {
Entity entities[10 * 1024 * 1024];
} World;
-i64 time_milliseconds() {
- struct timespec t;
- timespec_get(&t, TIME_UTC);
-
- return (i64) t.tv_sec * 1000ll + (i64) t.tv_nsec / 1000000ll;
-}
-
i32 main(i32 argc, c8 **argv) {
(void) argc;
(void) argv;
@@ -64,12 +57,12 @@ i32 main(i32 argc, c8 **argv) {
srand(time(0));
static World world = {0};
- i64 time_0 = time_milliseconds();
+ i64 time_0 = p_time();
while (!platform.done) {
p_handle_events();
- i64 time_elapsed = time_milliseconds() - time_0;
+ i64 time_elapsed = p_time() - time_0;
time_0 += time_elapsed;
if (platform.key_pressed[BUTTON_LEFT]) {
diff --git a/examples/ui.c b/examples/ui.c
index 0847af3..4730a9d 100755
--- a/examples/ui.c
+++ b/examples/ui.c
@@ -59,12 +59,12 @@ i32 main(i32 argc, c8 **argv) {
if (platform.cursor_x >= 40 && platform.cursor_x < 100 && platform.cursor_y >= 40 && platform.cursor_y < 100) {
button_0_down = platform.key_down[BUTTON_LEFT];
if (button_0_down)
- draw_panel(OP_SET, 0xffffff, 40, 40, 60, 60);
+ fill_rectangle(OP_SET, 0xffffff, 40, 40, 60, 60);
else
- draw_panel(OP_SET, 0x00ff00, 40, 40, 60, 60);
+ fill_rectangle(OP_SET, 0x00ff00, 40, 40, 60, 60);
} else {
button_0_down = 0;
- draw_panel(OP_SET, 0x208020, 40, 40, 60, 60);
+ fill_rectangle(OP_SET, 0x208020, 40, 40, 60, 60);
}
if (platform.cursor_x >= 40 && platform.cursor_x < 100 && platform.cursor_y >= 120 && platform.cursor_y < 180) {
@@ -72,17 +72,17 @@ i32 main(i32 argc, c8 **argv) {
if (platform.key_pressed[BUTTON_LEFT])
button_1_checked = !button_1_checked;
if (button_1_down)
- draw_panel(OP_SET, 0xffffff, 40, 120, 60, 60);
+ fill_rectangle(OP_SET, 0xffffff, 40, 120, 60, 60);
else if (button_1_checked)
- draw_panel(OP_SET, 0xff8080, 40, 120, 60, 60);
+ fill_rectangle(OP_SET, 0xff8080, 40, 120, 60, 60);
else
- draw_panel(OP_SET, 0x80ff80, 40, 120, 60, 60);
+ fill_rectangle(OP_SET, 0x80ff80, 40, 120, 60, 60);
} else {
button_1_down = 0;
if (button_1_checked)
- draw_panel(OP_SET, 0xff0000, 40, 120, 60, 60);
+ fill_rectangle(OP_SET, 0xff0000, 40, 120, 60, 60);
else
- draw_panel(OP_SET, 0x00ff00, 40, 120, 60, 60);
+ fill_rectangle(OP_SET, 0x00ff00, 40, 120, 60, 60);
}
i64 w = platform.frame_width / 2;
@@ -246,7 +246,7 @@ i32 main(i32 argc, c8 **argv) {
draw_text_area(0, x0 + 8, y0 - 8, w, h, 10., 10., text_len, text);
draw_text_area(color, x0, y0, w, h, 10., 10., text_len, text);
- draw_text_cursor(0xffffff, x0, y0, w, h, 10., 10., cursor, selection, text_len, text);
+ draw_selection_cursor(0xffffff, x0, y0, w, h, 10., 10., cursor, selection, text_len, text);
p_render_frame();
}
diff --git a/graphics.c b/graphics.c
index b054db7..50f3b68 100644
--- a/graphics.c
+++ b/graphics.c
@@ -2,6 +2,12 @@
//
// graphics.c
//
+// ----------------------------------------------------------------
+//
+// TODO:
+// - Blending.
+// - Anti-aliasing.
+//
// ================================================================
#ifndef GRAPHICS_HEADER_GUARD_
@@ -13,24 +19,53 @@
#include "reduced_system_layer.c"
+#define EPSILON 1e-9
+
enum {
OP_SET,
OP_XOR,
};
-u32 u32_from_rgb(f32 red, f32 green, f32 blue);
-void draw_panel(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height);
-void draw_text_area(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text);
-void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text);
+u32 u32_from_rgb (f32 red, f32 green, f32 blue);
+void fill_rectangle (u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height);
+void fill_triangle (u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2);
+void fill_ellipse (u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height);
+void fill_line (u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width);
+void draw_text_area (u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text);
+void draw_selection_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text);
#endif // GRAPHICS_HEADER_GUARD_
+// ================================================================
+
#ifndef GRAPHICS_HEADER
#ifndef GRAPHICS_IMPL_GUARD_
#define GRAPHICS_IMPL_GUARD_
#include <math.h>
+f64 min3(f64 a, f64 b, f64 c) {
+ if (a < b && a < c)
+ return a;
+ if (b < c)
+ return b;
+ return c;
+}
+
+f64 max3(f64 a, f64 b, f64 c) {
+ if (a > b && a > c)
+ return a;
+ if (b > c)
+ return b;
+ return c;
+}
+
+b8 same_sign(f64 a, f64 b) {
+ if (a >= EPSILON && b <= -EPSILON) return 0;
+ if (a <= -EPSILON && b >= EPSILON) return 0;
+ return 1;
+}
+
u32 u32_from_rgb(f32 red, f32 green, f32 blue) {
i32 r = (i32) floor(red * 255.f);
i32 g = (i32) floor(green * 255.f);
@@ -202,7 +237,7 @@ i64 enum_text_rows(i64 num_chars, c32 *text) {
return rows;
}
-void print_text(u32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
+void draw_text(u32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
assert(text != NULL);
f64 x = x0;
@@ -256,7 +291,16 @@ void print_text(u32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_cha
}
}
-void draw_panel(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) {
+void put_pixel(i64 i, i64 j, u32 op, u32 color) {
+ if (i < 0 || i >= platform.frame_width || j < 0 || j >= platform.frame_height)
+ return;
+ if (op == OP_XOR)
+ platform.pixels[j * platform.frame_width + i] ^= color;
+ else
+ platform.pixels[j * platform.frame_width + i] = color;
+}
+
+void fill_rectangle(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) {
i64 i0 = (i64) floor(x0 + .5);
i64 j0 = (i64) floor(y0 + .5);
i64 i1 = (i64) floor(x0 + width + .5);
@@ -269,10 +313,95 @@ void draw_panel(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) {
for (i64 j = j0; j < j1; ++j)
for (i64 i = i0; i < i1; ++i)
- if (op == OP_XOR)
- platform.pixels[j * platform.frame_width + i] ^= color;
- else
- platform.pixels[j * platform.frame_width + i] = color;
+ put_pixel(i, j, op, color);
+}
+
+void fill_triangle(u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
+ i64 min_x = (i64) floor(min3(x0, x1, x2));
+ i64 min_y = (i64) floor(min3(y0, y1, y2));
+ i64 max_x = (i64) ceil (max3(x0, x1, x2));
+ i64 max_y = (i64) ceil (max3(y0, y1, y2));
+
+ // Z-components of cross-products
+ //
+ f64 z0 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
+ f64 z1 = (x2 - x1) * (y0 - y1) - (x0 - x1) * (y2 - y1);
+ f64 z2 = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2);
+
+ for (i64 j = min_y; j <= max_y; ++j)
+ for (i64 i = min_x; i <= max_x; ++i) {
+ f64 x = (f64) i;
+ f64 y = (f64) j;
+
+ // Z-components of cross-products
+ //
+ f64 pz0 = (x - x0) * (y2 - y0) - (x2 - x0) * (y - y0);
+ f64 pz1 = (x - x1) * (y0 - y1) - (x0 - x1) * (y - y1);
+ f64 pz2 = (x - x2) * (y1 - y2) - (x1 - x2) * (y - y2);
+
+ // Check signs
+ //
+ if (!same_sign(z0, pz0)) continue;
+ if (!same_sign(z1, pz1)) continue;
+ if (!same_sign(z2, pz2)) continue;
+
+ put_pixel(i, j, op, color);
+ }
+}
+
+void fill_ellipse(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) {
+ // FIXME PERF:
+ // Implement better algorithm.
+
+ i64 i0 = (i64) floor(x0 + .5);
+ i64 j0 = (i64) floor(y0 + .5);
+ i64 i1 = (i64) floor(x0 + width + .5);
+ i64 j1 = (i64) floor(y0 + height + .5);
+
+ f64 dw = width / 2;
+ f64 dh = height / 2;
+
+ if (dw < EPSILON || dh < EPSILON)
+ return;
+
+ f64 cx = x0 + dw;
+ f64 cy = y0 + dh;
+ f64 kx = 1. / dw;
+ f64 ky = 1. / dh;
+
+ for (i64 j = j0; j < j1; ++j) {
+ if (j < 0 || j >= platform.frame_height)
+ continue;
+ f64 dy = (((f64) j) - cy) * ky;
+ f64 dydy = dy * dy;
+ for (i64 i = i0; i < i1; ++i) {
+ if (i < 0 || i >= platform.frame_width)
+ continue;
+ f64 dx = (((f64) i) - cx) * kx;
+ if (dx * dx + dydy <= 1.0)
+ put_pixel(i, j, op, color);
+ }
+ }
+}
+
+void fill_line(u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) {
+ f64 dx = x1 - x0;
+ f64 dy = y1 - y0;
+
+ // Tangent
+ //
+ f64 tx = -dy;
+ f64 ty = dx;
+ f64 tl = sqrt(tx * tx + ty * ty);
+ if (tl >= EPSILON) {
+ tx /= tl;
+ ty /= tl;
+ }
+ tx *= width * .5;
+ ty *= width * .5;
+
+ fill_triangle(op, color, x0 - tx, y0 - ty, x0 + tx, y0 + ty, x1 + tx, y1 + ty);
+ fill_triangle(op, color, x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty);
}
void draw_text_area(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text) {
@@ -293,10 +422,10 @@ void draw_text_area(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_sc
kx = k * max_scale_x;
ky = k * max_scale_y;
- print_text(color, x0, y0, kx, ky, num_chars, text);
+ draw_text(color, x0, y0, kx, ky, num_chars, text);
}
-void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) {
+void draw_selection_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) {
assert(max_scale_x > 1e-6);
assert(max_scale_y > 1e-6);
@@ -329,27 +458,27 @@ void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_
}
if (cursor_y == selection_y)
- draw_panel(OP_XOR, color,
+ fill_rectangle(OP_XOR, color,
x0 + kx * cursor_x,
y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1),
kx * (selection_x - cursor_x),
ky * (CHAR_NUM_BITS_Y + 1)
);
else {
- draw_panel(OP_XOR, color,
+ fill_rectangle(OP_XOR, color,
x0 + kx * cursor_x,
y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1),
kx * (num_columns - cursor_x),
ky * (CHAR_NUM_BITS_Y + 1)
);
for (i64 j = cursor_y + CHAR_NUM_BITS_Y + 1; j < selection_y; j += CHAR_NUM_BITS_Y + 1)
- draw_panel(OP_XOR, color,
+ fill_rectangle(OP_XOR, color,
x0,
y0 + ky * j - ky * (CHAR_NUM_BITS_Y + 1),
kx * num_columns,
ky * (CHAR_NUM_BITS_Y + 1)
);
- draw_panel(OP_XOR, color,
+ fill_rectangle(OP_XOR, color,
x0,
y0 + ky * selection_y - ky * (CHAR_NUM_BITS_Y + 1),
kx * selection_x,
@@ -357,7 +486,7 @@ void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_
);
}
} else
- draw_panel(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * CHAR_NUM_BITS_Y, kx * .5, ky * (CHAR_NUM_BITS_Y - 1));
+ fill_rectangle(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * CHAR_NUM_BITS_Y, kx * .5, ky * (CHAR_NUM_BITS_Y - 1));
}
#endif // GRAPHICS_IMPL_GUARD_