aboutsummaryrefslogtreecommitdiff
path: root/drawing/drawing.ha
blob: 729b4412406424f07a4ccdba93508d4d926a86dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use fmt;
use io;
use endian;
use math;

export def CHUNKSIZE = 512;

export type pos = (i32, i32);

export type picture = struct {
	// the surface data as u32s
	d: *[*]u32,
	w: size,
	h: size,

	// position of topleft corner in the world
	world_pos: pos,

};

// Returns array index of the pixel at position pos.
// Bounds check happens in here instead of using a slice type, so
// that it's easier to remove later.
fn pidx(pic: *picture, pos: pos) size = {
	const (x,y) = pos;
	const (xs,ys) = (x:size, y:size);
	assert(0 <= x, "x position must not be less than 0");
	assert(0 <= y, "y position must not be less than 0");
	assert(xs < pic.w, "x position must be less than picture width");
	assert(ys < pic.h, "y position must be less than picture height");

	return xs + pic.w*ys;
};

export fn pic_set(pic: *picture, pos: pos, val: u32) void =
	pic.d[pidx(pic,pos)] = val;

export fn clear_picture(pic: *picture, color: u32) void = {
	for (let i = 0z; i < pic.w*pic.h; i+=1) pic.d[i] = color;
};

export fn outline_picture(pic: *picture) void = {
	for (let x = 0; x:size < pic.w; x+=1) {
		pic_set(pic, (x, 0), 0);
		pic_set(pic, (x, pic.h:int-1), 0);
	};
	for (let y = 0; y:size < pic.h; y+=1) {
		pic_set(pic, (0, y), 0);
		pic_set(pic, (pic.w:int-1, y), 0);
	};
};

fn min(a: i32, b: i32) i32 = if (a<b) a else b;
fn max(a: i32, b: i32) i32 = if (a<b) b else a;
fn sign(x: i32) i32 = if (x < 0) -1 else if (x > 0) 1 else 0;
fn abs(x: i32) i32 = if (x<0) -x else x;

// Draws a circle onto the picture, at given position radius color
// Clips at the boundaries of the picture to avoid overflow.
export fn circle(picture: *picture, c: pos, r: i32, color: u32) void = {
	// fmt::printfln("C {} {} {} {:x}",c.0,c.1,r,color)!;
	const (cx,cy) = c;
	const ymin = max(0, cy-r);
	const ymax = min(picture.h:i32-1, cy+r);
	const xmin = max(0, cx-r);
	const xmax = min(picture.w:i32-1, cx+r);

	const r2 = r*r + r;

	for (let y = ymin; y<=ymax; y+=1) {
		const yd = y-cy;
		for (let x = xmin; x<=xmax; x+=1) {
			const xd = x-cx;
			if (yd*yd + xd*xd <= r2) {
				pic_set(picture, (x,y), color);
			};
		};
	};
};

export fn circle_hollow(picture: *picture, c: pos, r: i32, color: u32) void = {
	const (cx,cy) = c;
	const ymin = max(0, cy-r);
	const ymax = min(picture.h:i32-1, cy+r);
	const xmin = max(0, cx-r);
	const xmax = min(picture.w:i32-1, cx+r);

	const r2l = r*r - r;
	const r2u = r*r + r;

	for (let y = ymin; y<=ymax; y+=1) {
		const yd = y-cy;
		for (let x = xmin; x<=xmax; x+=1) {
			const xd = x-cx;
			const v = yd*yd + xd*xd;
			if (r2l <= v && v <= r2u) {
				pic_set(picture, (x,y), color);
			};
		};
	};
};

export fn stroke(picture: *picture, c0: pos, c1: pos, r: i32, color: u32) void = {
	const dx = c1.0 - c0.0;
	const dy = c1.1 - c0.1;
	const d = math::sqrtf64((dx*dx+dy*dy):f64):i32;
	const count = d/r  + 1;
	if (count == 0) {
		circle(picture, c1, r, color);
		return;
	} else if (count == 1) {
		circle(picture, c0, r, color);
		circle(picture, c1, r, color);
		return;
	};
	const sx = dx: f64 / (count): f64;
	const sy = dy: f64 / (count): f64;

	for (let i = 0i32; i < count; i += 1) {
		const cx = c0.0 + math::roundf64(i:f64*sx):i32;
		const cy = c0.1 + math::roundf64(i:f64*sy):i32;

		circle(picture, (cx, cy), r, color);
	};
	circle(picture, c1, r, color);
};

// ehh should check bounding box instead of doing all pictures maybe
export fn perform(pic: *picture, op: op) void = {
	match (op) {
	case let o: op_circle =>
		const x = o.pos.0, y=o.pos.1;
		const r = o.radius;
		const c = o.color & 0xffffff;
		const pos_within_pic: pos =
			(x - pic.world_pos.0, y - pic.world_pos.1);
		circle(pic, pos_within_pic, r: i32, c);
	case let s: op_stroke =>
		const col = s.color & 0xffffff;
		const c0 = (s.pos0.0 - pic.world_pos.0, s.pos0.1 - pic.world_pos.1);
		const c1 = (s.pos1.0 - pic.world_pos.0, s.pos1.1 - pic.world_pos.1);
		stroke(pic, c0, c1, s.radius: i32, col);
	};
};

export fn perform_multi(pictures: []picture, op: op) void = {
	for (const pic &.. pictures)
		perform(pic, op);
};