Skip to content

Commit 7029371

Browse files
committed
Implement endpoint-based elliptical arc drawing
Allow drawing elliptical arcs by specifying end points instead of center point, following some common vector graphic endpoint parameterization format. Ref: https://www.w3.org/TR/SVG/implnote.html
1 parent c5dd720 commit 7029371

File tree

4 files changed

+314
-0
lines changed

4 files changed

+314
-0
lines changed

apps/multi.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,55 @@ static void apps_jelly_start(twin_screen_t *screen, int x, int y, int w, int h)
220220
twin_window_show(window);
221221
}
222222

223+
static void draw_flower(twin_path_t *path, twin_fixed_t radius)
224+
{
225+
const twin_angle_t angle_72 = 819;
226+
const twin_angle_t angle_36 = 409;
227+
twin_fixed_t p_x = twin_fixed_mul(radius, twin_cos(-angle_36));
228+
twin_fixed_t p_y = twin_fixed_mul(radius, twin_sin(-angle_36));
229+
twin_path_move(path, p_x, p_y);
230+
231+
for (twin_angle_t a = angle_36; a <= TWIN_ANGLE_360; a += angle_72) {
232+
twin_fixed_t c_x = twin_fixed_mul(radius, twin_cos(a));
233+
twin_fixed_t c_y = twin_fixed_mul(radius, twin_sin(a));
234+
twin_fixed_t rx = radius;
235+
twin_fixed_t ry = radius * 3;
236+
twin_path_arc_ellipse(path, 1, 1, rx, ry, p_x, p_y, c_x, c_y,
237+
a - angle_36);
238+
p_x = c_x;
239+
p_y = c_y;
240+
}
241+
242+
twin_path_close(path);
243+
}
244+
245+
static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
246+
{
247+
twin_window_t *window = twin_window_create(
248+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
249+
twin_pixmap_t *pixmap = window->pixmap;
250+
twin_path_t *stroke = twin_path_create();
251+
twin_path_t *path = twin_path_create();
252+
twin_path_translate(path, D(200), D(200));
253+
twin_path_scale(path, D(10), D(10));
254+
twin_path_translate(stroke, D(200), D(200));
255+
twin_fill(pixmap, 0xffffffff, TWIN_SOURCE, 0, 0, w, h);
256+
twin_window_set_name(window, "Flower");
257+
twin_path_move(stroke, D(-200), D(0));
258+
twin_path_draw(stroke, D(200), D(0));
259+
twin_path_move(stroke, D(0), D(200));
260+
twin_path_draw(stroke, D(0), D(-200));
261+
twin_path_set_cap_style(stroke, TwinCapProjecting);
262+
twin_paint_stroke(pixmap, 0xffcc9999, stroke, D(10));
263+
draw_flower(path, D(3));
264+
// twin_path_arc_circle(path, 0, 1, D(3), D(3), D(0), D(0), D(3));
265+
// twin_path_draw(path, 0, 0);
266+
// twin_path_close(path);
267+
twin_paint_path(pixmap, 0xffe2d2d2, path);
268+
twin_path_destroy(stroke);
269+
twin_window_show(window);
270+
}
271+
223272
void apps_multi_start(twin_screen_t *screen,
224273
const char *name,
225274
int x,
@@ -233,4 +282,5 @@ void apps_multi_start(twin_screen_t *screen,
233282
apps_quickbrown_start(screen, x += 20, y += 20, w, h);
234283
apps_ascii_start(screen, x += 20, y += 20, w, h);
235284
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
285+
apps_flower_start(screen, x += 20, y += 20, w, h);
236286
}

include/twin.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,7 @@ void twin_path_ellipse(twin_path_t *path,
800800
twin_fixed_t y,
801801
twin_fixed_t x_radius,
802802
twin_fixed_t y_radius);
803+
803804
void twin_path_arc(twin_path_t *path,
804805
twin_fixed_t x,
805806
twin_fixed_t y,
@@ -808,6 +809,26 @@ void twin_path_arc(twin_path_t *path,
808809
twin_angle_t start,
809810
twin_angle_t extent);
810811

812+
void twin_path_arc_ellipse(twin_path_t *path,
813+
bool large_arc,
814+
bool sweep,
815+
twin_fixed_t radius_x,
816+
twin_fixed_t radius_y,
817+
twin_fixed_t cur_x,
818+
twin_fixed_t cur_y,
819+
twin_fixed_t target_x,
820+
twin_fixed_t target_y,
821+
twin_angle_t rotation);
822+
823+
void twin_path_arc_circle(twin_path_t *path,
824+
bool large_arc,
825+
bool sweep,
826+
twin_fixed_t radius,
827+
twin_fixed_t cur_x,
828+
twin_fixed_t cur_y,
829+
twin_fixed_t target_x,
830+
twin_fixed_t target_y);
831+
811832
void twin_path_rectangle(twin_path_t *path,
812833
twin_fixed_t x,
813834
twin_fixed_t y,
@@ -1104,6 +1125,10 @@ twin_fixed_t twin_tan(twin_angle_t a);
11041125

11051126
void twin_sincos(twin_angle_t a, twin_fixed_t *sin, twin_fixed_t *cos);
11061127

1128+
twin_angle_t twin_atan2(twin_fixed_t y, twin_fixed_t x);
1129+
1130+
twin_angle_t twin_acos(twin_fixed_t x);
1131+
11071132
/*
11081133
* widget.c
11091134
*/

src/path.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,146 @@ void twin_path_arc(twin_path_t *path,
236236
twin_path_set_matrix(path, save);
237237
}
238238

239+
static twin_angle_t vector_angle(twin_fixed_t ux,
240+
twin_fixed_t uy,
241+
twin_fixed_t vx,
242+
twin_fixed_t vy)
243+
{
244+
twin_fixed_t dot = twin_fixed_mul(ux, vx) + twin_fixed_mul(uy, vy);
245+
246+
twin_fixed_t ua =
247+
twin_fixed_sqrt(twin_fixed_mul(ux, ux) + twin_fixed_mul(uy, uy));
248+
twin_fixed_t va =
249+
twin_fixed_sqrt(twin_fixed_mul(vx, vx) + twin_fixed_mul(vy, vy));
250+
251+
/* cos(theta) = dot / (|u| * |v|) */
252+
twin_fixed_t cos_theta = twin_fixed_div(dot, twin_fixed_mul(ua, va));
253+
twin_fixed_t cross = twin_fixed_mul(ux, vy) - twin_fixed_mul(uy, vx);
254+
twin_angle_t angle = twin_acos(cos_theta);
255+
return (cross < 0) ? -angle : angle;
256+
}
257+
258+
typedef struct {
259+
twin_fixed_t cx, cy;
260+
twin_angle_t start, extent;
261+
} twin_ellipse_para_t;
262+
263+
twin_ellipse_para_t get_center_parameters(twin_fixed_t x1,
264+
twin_fixed_t y1,
265+
twin_fixed_t x2,
266+
twin_fixed_t y2,
267+
bool fa,
268+
bool fs,
269+
twin_fixed_t rx,
270+
twin_fixed_t ry,
271+
twin_fixed_t phi)
272+
{
273+
twin_fixed_t sin_phi = twin_sin(phi);
274+
twin_fixed_t cos_phi = twin_cos(phi);
275+
276+
/* Simplify through translation/rotation */
277+
twin_fixed_t x =
278+
twin_fixed_mul(cos_phi, twin_fixed_mul(x1 - x2, TWIN_FIXED_HALF)) +
279+
twin_fixed_mul(sin_phi, twin_fixed_mul(y1 - y2, TWIN_FIXED_HALF));
280+
281+
twin_fixed_t y =
282+
twin_fixed_mul(-sin_phi, twin_fixed_mul(x1 - x2, TWIN_FIXED_HALF)) +
283+
twin_fixed_mul(cos_phi, twin_fixed_mul(y1 - y2, TWIN_FIXED_HALF));
284+
285+
twin_fixed_t px = twin_fixed_mul(x, x);
286+
twin_fixed_t py = twin_fixed_mul(y, y);
287+
twin_fixed_t prx = twin_fixed_mul(rx, rx);
288+
twin_fixed_t pry = twin_fixed_mul(ry, ry);
289+
290+
/* Correct out-of-range radii */
291+
twin_fixed_t L = twin_fixed_div(px, prx) + twin_fixed_div(py, pry);
292+
293+
if (L > TWIN_FIXED_ONE) {
294+
twin_fixed_t sqrt_L = twin_fixed_sqrt(L);
295+
rx = twin_fixed_mul(sqrt_L, twin_fixed_abs(rx));
296+
ry = twin_fixed_mul(sqrt_L, twin_fixed_abs(ry));
297+
} else {
298+
rx = twin_fixed_abs(rx);
299+
ry = twin_fixed_abs(ry);
300+
}
301+
302+
/* Compute center */
303+
twin_fixed_t sign = (fa != fs) ? -1 : 1;
304+
twin_fixed_t numerator = twin_fixed_mul(prx, pry) -
305+
twin_fixed_mul(prx, py) - twin_fixed_mul(pry, px);
306+
307+
twin_fixed_t denominator =
308+
twin_fixed_mul(prx, py) + twin_fixed_mul(pry, px);
309+
twin_fixed_t M =
310+
sign * twin_fixed_sqrt(twin_fixed_div(numerator, denominator));
311+
twin_fixed_t _cx =
312+
twin_fixed_mul(M, twin_fixed_div(twin_fixed_mul(rx, y), ry));
313+
twin_fixed_t _cy =
314+
twin_fixed_mul(M, twin_fixed_div(twin_fixed_mul(-ry, x), rx));
315+
316+
twin_ellipse_para_t ret;
317+
ret.cx = twin_fixed_mul(cos_phi, _cx) - twin_fixed_mul(sin_phi, _cy) +
318+
twin_fixed_mul(x1 + x2, TWIN_FIXED_HALF);
319+
320+
ret.cy = twin_fixed_mul(sin_phi, _cx) + twin_fixed_mul(cos_phi, _cy) +
321+
twin_fixed_mul(y1 + y2, TWIN_FIXED_HALF);
322+
323+
/* Compute θ and dθ */
324+
ret.start = vector_angle(TWIN_FIXED_ONE, 0, twin_fixed_div(x - _cx, rx),
325+
twin_fixed_div(y - _cy, ry));
326+
twin_angle_t extent = vector_angle(
327+
twin_fixed_div(x - _cx, rx), twin_fixed_div(y - _cy, ry),
328+
twin_fixed_div(-x - _cx, rx), twin_fixed_div(-y - _cy, ry));
329+
330+
if (fs && extent > TWIN_ANGLE_0)
331+
extent -= TWIN_ANGLE_360;
332+
if (!fs && extent < TWIN_ANGLE_0)
333+
extent += TWIN_ANGLE_360;
334+
ret.start %= TWIN_ANGLE_360;
335+
extent %= TWIN_ANGLE_360;
336+
337+
ret.extent = extent;
338+
return ret;
339+
}
340+
341+
void twin_path_arc_ellipse(twin_path_t *path,
342+
bool large_arc,
343+
bool sweep,
344+
twin_fixed_t radius_x,
345+
twin_fixed_t radius_y,
346+
twin_fixed_t cur_x,
347+
twin_fixed_t cur_y,
348+
twin_fixed_t target_x,
349+
twin_fixed_t target_y,
350+
twin_angle_t rotation)
351+
{
352+
twin_ellipse_para_t para;
353+
para = get_center_parameters(cur_x, cur_y, target_x, target_y, large_arc,
354+
sweep, radius_x, radius_y, rotation);
355+
twin_matrix_t save = twin_path_current_matrix(path);
356+
357+
twin_path_translate(path, para.cx, para.cy);
358+
twin_path_rotate(path, rotation);
359+
twin_path_translate(path, -para.cx, -para.cy);
360+
twin_path_arc(path, para.cx, para.cy, radius_x, radius_y, para.start,
361+
para.extent);
362+
363+
twin_path_set_matrix(path, save);
364+
}
365+
366+
void twin_path_arc_circle(twin_path_t *path,
367+
bool large_arc,
368+
bool sweep,
369+
twin_fixed_t radius,
370+
twin_fixed_t cur_x,
371+
twin_fixed_t cur_y,
372+
twin_fixed_t target_x,
373+
twin_fixed_t target_y)
374+
{
375+
twin_path_arc_ellipse(path, large_arc, sweep, radius, radius, cur_x, cur_y,
376+
target_x, target_y, TWIN_ANGLE_0);
377+
}
378+
239379
void twin_path_rectangle(twin_path_t *path,
240380
twin_fixed_t x,
241381
twin_fixed_t y,

src/trig.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,102 @@ void twin_sincos(twin_angle_t a, twin_fixed_t *sin, twin_fixed_t *cos)
9797
*cos = cos_val;
9898
}
9999
}
100+
101+
const twin_angle_t atan_table[] = {
102+
0x0200, /* arctan(2^0) = 45° -> 512 */
103+
0x0130, /* arctan(2^-1) = 26.565° -> 303 */
104+
0x009B, /* arctan(2^-2) = 14.036° -> 155 */
105+
0x004F, /* arctan(2^-3) = 7.125° -> 79 */
106+
0x0027, /* arctan(2^-4) = 3.576° -> 39 */
107+
0x0014, /* arctan(2^-5) = 1.790° -> 20 */
108+
0x000A, /* arctan(2^-6) = 0.895° -> 10 */
109+
0x0005, /* arctan(2^-7) = 0.448° -> 5 */
110+
0x0002, /* arctan(2^-8) = 0.224° -> 2 */
111+
0x0001, /* arctan(2^-9) = 0.112° -> 1 */
112+
0x0001, /* arctan(2^-10) = 0.056° -> 1 */
113+
0x0000, /* arctan(2^-11) = 0.028° -> 0 */
114+
};
115+
116+
twin_angle_t twin_atan2_first_quadrant(twin_fixed_t y, twin_fixed_t x)
117+
{
118+
if (x == 0 && y == 0)
119+
return 0;
120+
if (x == 0)
121+
return TWIN_ANGLE_90;
122+
if (y == 0)
123+
return TWIN_ANGLE_0;
124+
twin_fixed_t current_x = x;
125+
twin_fixed_t current_y = y;
126+
twin_angle_t angle = 0;
127+
/* CORDIC iteration */
128+
for (int i = 0; i < 12; i++) {
129+
twin_fixed_t temp_x, temp_y;
130+
if (current_y > 0) {
131+
temp_x = current_x + (current_y >> i);
132+
temp_y = current_y - (current_x >> i);
133+
angle += atan_table[i];
134+
} else {
135+
temp_x = current_x - (current_y >> i);
136+
temp_y = current_y + (current_x >> i);
137+
angle -= atan_table[i];
138+
}
139+
current_x = temp_x;
140+
current_y = temp_y;
141+
}
142+
return angle;
143+
}
144+
145+
twin_angle_t twin_atan2(twin_fixed_t y, twin_fixed_t x)
146+
{
147+
if (x == 0 && y == 0)
148+
return TWIN_ANGLE_0;
149+
if (x == 0)
150+
return (y > 0) ? TWIN_ANGLE_90 : TWIN_ANGLE_270;
151+
if (y == 0)
152+
return (x > 0) ? TWIN_ANGLE_0 : TWIN_ANGLE_180;
153+
int quadrant;
154+
twin_fixed_t abs_x = x;
155+
twin_fixed_t abs_y = y;
156+
if (x >= 0 && y >= 0) {
157+
quadrant = 1;
158+
} else if (x < 0 && y >= 0) {
159+
quadrant = 2;
160+
abs_x = -x;
161+
} else if (x < 0 && y < 0) {
162+
quadrant = 3;
163+
abs_x = -x;
164+
abs_y = -y;
165+
} else {
166+
quadrant = 4;
167+
abs_y = -y;
168+
}
169+
twin_angle_t angle = twin_atan2_first_quadrant(abs_y, abs_x);
170+
switch (quadrant) {
171+
case 1:
172+
return angle;
173+
case 2:
174+
return 2048 - angle;
175+
case 3:
176+
return 2048 + angle;
177+
case 4:
178+
return 4096 - angle;
179+
default:
180+
return 0;
181+
}
182+
}
183+
184+
twin_angle_t twin_acos(twin_fixed_t x)
185+
{
186+
if (x <= -TWIN_FIXED_ONE)
187+
return TWIN_ANGLE_180;
188+
if (x >= TWIN_FIXED_ONE)
189+
return TWIN_ANGLE_0;
190+
twin_fixed_t y = twin_fixed_sqrt(TWIN_FIXED_ONE - twin_fixed_mul(x, x));
191+
twin_angle_t angle;
192+
if (x >= 0) {
193+
angle = twin_atan2_first_quadrant(y, x);
194+
} else {
195+
angle = TWIN_ANGLE_180 - twin_atan2_first_quadrant(y, -x);
196+
}
197+
return angle;
198+
}

0 commit comments

Comments
 (0)