Skip to content

Commit ff4847d

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 5433792 commit ff4847d

File tree

7 files changed

+400
-7
lines changed

7 files changed

+400
-7
lines changed

apps/multi.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,57 @@ 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,
224+
twin_fixed_t radius,
225+
int number_of_petals)
226+
{
227+
const twin_angle_t angle_shift = TWIN_ANGLE_360 / number_of_petals;
228+
const twin_angle_t angle_start = angle_shift / 2;
229+
twin_fixed_t s, c;
230+
twin_sincos(-angle_start, &s, &c);
231+
twin_fixed_t p_x = twin_fixed_mul(radius, c);
232+
twin_fixed_t p_y = twin_fixed_mul(radius, s);
233+
twin_path_move(path, p_x, p_y);
234+
235+
for (twin_angle_t a = angle_start; a <= TWIN_ANGLE_360; a += angle_shift) {
236+
twin_sincos(a, &s, &c);
237+
twin_fixed_t c_x = twin_fixed_mul(radius, c);
238+
twin_fixed_t c_y = twin_fixed_mul(radius, s);
239+
twin_fixed_t rx = radius;
240+
twin_fixed_t ry = radius * 3;
241+
twin_path_arc_ellipse(path, 1, 1, rx, ry, p_x, p_y, c_x, c_y,
242+
a - angle_start);
243+
p_x = c_x;
244+
p_y = c_y;
245+
}
246+
247+
twin_path_close(path);
248+
}
249+
250+
static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
251+
{
252+
twin_window_t *window = twin_window_create(
253+
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
254+
twin_pixmap_t *pixmap = window->pixmap;
255+
twin_path_t *stroke = twin_path_create();
256+
twin_path_t *path = twin_path_create();
257+
twin_path_translate(path, D(200), D(200));
258+
twin_path_scale(path, D(10), D(10));
259+
twin_path_translate(stroke, D(200), D(200));
260+
twin_fill(pixmap, 0xffffffff, TWIN_SOURCE, 0, 0, w, h);
261+
twin_window_set_name(window, "Flower");
262+
twin_path_move(stroke, D(-200), D(0));
263+
twin_path_draw(stroke, D(200), D(0));
264+
twin_path_move(stroke, D(0), D(200));
265+
twin_path_draw(stroke, D(0), D(-200));
266+
twin_path_set_cap_style(stroke, TwinCapProjecting);
267+
twin_paint_stroke(pixmap, 0xffcc9999, stroke, D(10));
268+
draw_flower(path, D(3), 5);
269+
twin_paint_path(pixmap, 0xffe2d2d2, path);
270+
twin_path_destroy(stroke);
271+
twin_window_show(window);
272+
}
273+
223274
void apps_multi_start(twin_screen_t *screen,
224275
const char *name,
225276
int x,
@@ -233,4 +284,5 @@ void apps_multi_start(twin_screen_t *screen,
233284
apps_quickbrown_start(screen, x += 20, y += 20, w, h);
234285
apps_ascii_start(screen, x += 20, y += 20, w, h);
235286
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
287+
apps_flower_start(screen, x += 20, y += 20, w, h);
236288
}

apps/spline.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ static void _apps_spline_button_signal(maybe_unused twin_button_t *button,
116116
_twin_widget_queue_paint(&spline->widget);
117117
}
118118

119-
#define D(x) twin_double_to_fixed(x)
120119
static twin_dispatch_result_t _apps_spline_update_pos(apps_spline_t *spline,
121120
twin_event_t *event)
122121
{

include/twin.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ void twin_clear_file(twin_file_t *file);
670670
*/
671671

672672
#define twin_fixed_mul(a, b) ((twin_fixed_t) (((int64_t) (a) * (b)) >> 16))
673-
#define twin_fixed_div(a, b) ((twin_fixed_t) ((((int64_t) (a)) << 16) / b))
673+
#define twin_fixed_div(a, b) ((twin_fixed_t) ((((int64_t) (a)) << 16) / (b)))
674674

675675
twin_fixed_t twin_fixed_sqrt(twin_fixed_t a);
676676

@@ -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
*/

include/twin_private.h

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,21 @@
7676
* Max 0x7f 0111 1111 127 1.984375
7777
* Min 0x80 1000 0000 -128 -2
7878
*
79+
* twin_xfixed_t - A fixed-point type in the Q31.32 format.
80+
*
81+
* Hex Binary
82+
* Max 0x7fffffff 0111 1111 1111 1111 1111 1111 1111 1111
83+
* Min 0x80000000 1000 0000 0000 0000 0000 0000 0000 0000
84+
* Decimal Actual
85+
* Max 9,223,372,036,854,775,807 1,073,741,823.9999999997
86+
* Min -9,223,372,036,854,775,808 -1,073,741,824
87+
*
7988
* All of the above tables are based on two's complement representation.
8089
*/
8190
typedef int16_t twin_sfixed_t;
8291
typedef int32_t twin_dfixed_t;
8392
typedef int8_t twin_gfixed_t;
93+
typedef int64_t twin_xfixed_t;
8494

8595
#define twin_sfixed_floor(f) ((f) & ~0xf)
8696
#define twin_sfixed_trunc(f) ((f) >> 4)
@@ -95,6 +105,9 @@ typedef int8_t twin_gfixed_t;
95105
#define twin_sfixed_to_dfixed(s) (((twin_dfixed_t) (s)) << 4)
96106
#define twin_dfixed_to_sfixed(d) ((twin_sfixed_t) ((d) >> 4))
97107

108+
#define twin_xfixed_to_fixed(x) ((twin_fixed_t) ((x) >> 16))
109+
#define twin_fixed_to_xfixed(f) (((twin_xfixed_t) (f)) << 16)
110+
98111
/*
99112
* twin_sfixed_t a = b'10100;
100113
* twin_sfixed_t b = b'10000;
@@ -116,14 +129,19 @@ typedef int8_t twin_gfixed_t;
116129
*/
117130

118131
#define twin_sfixed_mul(a, b) \
119-
((((twin_sfixed_t) (a)) * ((twin_sfixed_t) (b))) >> 4)
132+
(twin_sfixed_t)((((int32_t) (a)) * ((int32_t) (b))) >> 4)
120133
#define twin_sfixed_div(a, b) \
121-
((((twin_sfixed_t) (a)) << 4) / ((twin_sfixed_t) (b)))
134+
(twin_sfixed_t)((((int32_t) (a)) << 4) / ((int32_t) (b)))
122135

123136
#define twin_dfixed_mul(a, b) \
124-
((((twin_dfixed_t) (a)) * ((twin_dfixed_t) (b))) >> 8)
137+
(twin_dfixed_t)((((int64_t) (a)) * ((int64_t) (b))) >> 8)
125138
#define twin_dfixed_div(a, b) \
126-
((((twin_dfixed_t) (a)) << 8) / ((twin_dfixed_t) (b)))
139+
(twin_dfixed_t)((((int64_t) (a)) << 8) / ((int64_t) (b)))
140+
141+
#define twin_xfixed_mul(a, b) \
142+
(twin_xfixed_t)((((__int128_t) (a)) * ((__int128_t) (b))) >> 32)
143+
#define twin_xfixed_div(a, b) \
144+
(twin_xfixed_t)((((__int128_t) (a)) << 32) / ((__int128_t) (b)))
127145

128146
/*
129147
* 'double' is a no-no in any shipping code, but useful during
@@ -141,6 +159,7 @@ typedef int8_t twin_gfixed_t;
141159

142160
#define TWIN_GFIXED_ONE (0x40)
143161

162+
#define TWIN_XFIXED_ONE (0x100000000)
144163
/*
145164
* Compositing stuff
146165
*/
@@ -375,7 +394,7 @@ twin_dfixed_t _twin_distance_to_line_squared(twin_spoint_t *p,
375394
* Fixed point helper functions
376395
*/
377396
twin_sfixed_t _twin_sfixed_sqrt(twin_sfixed_t as);
378-
397+
twin_xfixed_t _twin_xfixed_sqrt(twin_xfixed_t a);
379398
/*
380399
* Matrix stuff
381400
*/
@@ -595,11 +614,22 @@ static inline int twin_clz(uint32_t v)
595614
return 31 - leading_zero;
596615
return 32; /* undefined behavior */
597616
}
617+
static inline int twin_clzll(uint64_t v)
618+
{
619+
uint32_t leading_zero = 0;
620+
if (_BitScanReverse64(&leading_zero, v))
621+
return 63 - leading_zero;
622+
return 64; /* undefined behavior */
623+
}
598624
#elif defined(__GNUC__) || defined(__clang__)
599625
static inline int twin_clz(uint32_t v)
600626
{
601627
return __builtin_clz(v);
602628
}
629+
static inline int twin_clzll(uint64_t v)
630+
{
631+
return __builtin_clzll(v);
632+
}
603633
#else /* generic implementation */
604634
static inline int twin_clz(uint32_t v)
605635
{
@@ -617,6 +647,26 @@ static inline int twin_clz(uint32_t v)
617647

618648
return mul_debruijn[(uint32_t) (v * 0x07C4ACDDU) >> 27];
619649
}
650+
static inline int twin_clzll(uint64_t v)
651+
{
652+
/* https://stackoverflow.com/questions/21888140/de-bruijn-algorithm-binary-digit-count-64bits-c-sharp
653+
*/
654+
static const uint8_t mul_debruijn[] = {
655+
0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
656+
62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
657+
63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
658+
51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12,
659+
};
660+
661+
v |= v >> 1;
662+
v |= v >> 2;
663+
v |= v >> 4;
664+
v |= v >> 8;
665+
v |= v >> 16;
666+
v |= v >> 32;
667+
668+
return mul_debruijn[(uint64_t) (v * 0x022fdd63cc95386dUL) >> 58];
669+
}
620670
#endif
621671

622672
extern const uint8_t _twin_cursor_default[];

src/fixed.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
#define CHECK_INTERVAL(x, minx, epsilon) \
2020
((int32_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)
21+
#define CHECK_INTERVAL_64(x, minx, epsilon) \
22+
((int64_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)
2123

2224
twin_fixed_t twin_fixed_sqrt(twin_fixed_t a)
2325
{
@@ -79,3 +81,42 @@ twin_sfixed_t _twin_sfixed_sqrt(twin_sfixed_t as)
7981

8082
return (offset >= 0) ? z >> offset : z << (-offset);
8183
}
84+
85+
twin_xfixed_t _twin_xfixed_sqrt(twin_xfixed_t a)
86+
{
87+
/* Early return for non-positive inputs */
88+
if (a <= 0)
89+
return 0;
90+
91+
/* Special case for values very close to 1 */
92+
if (CHECK_INTERVAL_64(a, TWIN_XFIXED_ONE, (1ULL << (16 - 1))))
93+
return TWIN_XFIXED_ONE;
94+
95+
/* Count leading zero bits to normalize the input */
96+
int64_t offset = 0;
97+
for (twin_xfixed_t i = a; !(UINT64_C(0x4000000000000000) & i); i <<= 1)
98+
++offset;
99+
100+
/* Ensure even offset for precision */
101+
offset &= ~1;
102+
103+
/* Shift left to expand precision */
104+
a <<= offset;
105+
106+
/* Calculate scaling offset */
107+
offset >>= 1;
108+
offset -= (32 >> 1);
109+
110+
/* Digit-by-digit square root calculation */
111+
twin_xfixed_t z = 0;
112+
for (twin_xfixed_t m = 1ULL << ((63 - __builtin_clzll(a)) & ~1ULL); m;
113+
m >>= 2) {
114+
twin_xfixed_t b = z + m;
115+
z >>= 1;
116+
if (a >= b)
117+
a -= b, z += m;
118+
}
119+
120+
/* Shift back the expanded digits */
121+
return (offset >= 0) ? z >> offset : z << (-offset);
122+
}

0 commit comments

Comments
 (0)