Skip to content

Commit

Permalink
Implement endpoint-based elliptical arc drawing
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ndsl7109256 committed Dec 2, 2024
1 parent 5433792 commit 9ae9814
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 2 deletions.
49 changes: 49 additions & 0 deletions apps/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,54 @@ static void apps_jelly_start(twin_screen_t *screen, int x, int y, int w, int h)
twin_window_show(window);
}

static void draw_flower(twin_path_t *path,
twin_fixed_t radius,
int number_of_petals)
{
const twin_angle_t angle_shift = TWIN_ANGLE_360 / number_of_petals;
const twin_angle_t angle_start = angle_shift / 2;
twin_fixed_t p_x = twin_fixed_mul(radius, twin_cos(-angle_start));
twin_fixed_t p_y = twin_fixed_mul(radius, twin_sin(-angle_start));
twin_path_move(path, p_x, p_y);

for (twin_angle_t a = angle_start; a <= TWIN_ANGLE_360; a += angle_shift) {
twin_fixed_t c_x = twin_fixed_mul(radius, twin_cos(a));
twin_fixed_t c_y = twin_fixed_mul(radius, twin_sin(a));
twin_fixed_t rx = radius;
twin_fixed_t ry = radius * 3;
twin_path_arc_ellipse(path, 1, 1, rx, ry, p_x, p_y, c_x, c_y,
a - angle_start);
p_x = c_x;
p_y = c_y;
}

twin_path_close(path);
}

static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
twin_pixmap_t *pixmap = window->pixmap;
twin_path_t *stroke = twin_path_create();
twin_path_t *path = twin_path_create();
twin_path_translate(path, D(200), D(200));
twin_path_scale(path, D(10), D(10));
twin_path_translate(stroke, D(200), D(200));
twin_fill(pixmap, 0xffffffff, TWIN_SOURCE, 0, 0, w, h);
twin_window_set_name(window, "Flower");
twin_path_move(stroke, D(-200), D(0));
twin_path_draw(stroke, D(200), D(0));
twin_path_move(stroke, D(0), D(200));
twin_path_draw(stroke, D(0), D(-200));
twin_path_set_cap_style(stroke, TwinCapProjecting);
twin_paint_stroke(pixmap, 0xffcc9999, stroke, D(10));
draw_flower(path, D(3), 5);
twin_paint_path(pixmap, 0xffe2d2d2, path);
twin_path_destroy(stroke);
twin_window_show(window);
}

void apps_multi_start(twin_screen_t *screen,
const char *name,
int x,
Expand All @@ -233,4 +281,5 @@ void apps_multi_start(twin_screen_t *screen,
apps_quickbrown_start(screen, x += 20, y += 20, w, h);
apps_ascii_start(screen, x += 20, y += 20, w, h);
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
apps_flower_start(screen, x += 20, y += 20, w, h);
}
27 changes: 26 additions & 1 deletion include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ void twin_clear_file(twin_file_t *file);
*/

#define twin_fixed_mul(a, b) ((twin_fixed_t) (((int64_t) (a) * (b)) >> 16))
#define twin_fixed_div(a, b) ((twin_fixed_t) ((((int64_t) (a)) << 16) / b))
#define twin_fixed_div(a, b) ((twin_fixed_t) ((((int64_t) (a)) << 16) / (b)))

twin_fixed_t twin_fixed_sqrt(twin_fixed_t a);

Expand Down Expand Up @@ -800,6 +800,7 @@ void twin_path_ellipse(twin_path_t *path,
twin_fixed_t y,
twin_fixed_t x_radius,
twin_fixed_t y_radius);

void twin_path_arc(twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
Expand All @@ -808,6 +809,26 @@ void twin_path_arc(twin_path_t *path,
twin_angle_t start,
twin_angle_t extent);

void twin_path_arc_ellipse(twin_path_t *path,
bool large_arc,
bool sweep,
twin_fixed_t radius_x,
twin_fixed_t radius_y,
twin_fixed_t cur_x,
twin_fixed_t cur_y,
twin_fixed_t target_x,
twin_fixed_t target_y,
twin_angle_t rotation);

void twin_path_arc_circle(twin_path_t *path,
bool large_arc,
bool sweep,
twin_fixed_t radius,
twin_fixed_t cur_x,
twin_fixed_t cur_y,
twin_fixed_t target_x,
twin_fixed_t target_y);

void twin_path_rectangle(twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
Expand Down Expand Up @@ -1104,6 +1125,10 @@ twin_fixed_t twin_tan(twin_angle_t a);

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

twin_angle_t twin_atan2(twin_fixed_t y, twin_fixed_t x);

twin_angle_t twin_acos(twin_fixed_t x);

/*
* widget.c
*/
Expand Down
51 changes: 50 additions & 1 deletion include/twin_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,20 @@
* Max 0x7f 0111 1111 127 1.984375
* Min 0x80 1000 0000 -128 -2
*
* twin_xfixed_t - A fixed-point type in the Q31.32 format.
*
* Hex Binary
* Max 0x7fffffff 0111 1111 1111 1111 1111 1111 1111 1111
* Min 0x80000000 1000 0000 0000 0000 0000 0000 0000 0000
* Decimal Actual
* Max 9,223,372,036,854,775,807 1,073,741,823.9999999997
* Min -9,223,372,036,854,775,808 -1,073,741,824
* All of the above tables are based on two's complement representation.
*/
typedef int16_t twin_sfixed_t;
typedef int32_t twin_dfixed_t;
typedef int8_t twin_gfixed_t;
typedef int64_t twin_xfixed_t;

#define twin_sfixed_floor(f) ((f) & ~0xf)
#define twin_sfixed_trunc(f) ((f) >> 4)
Expand All @@ -95,6 +104,9 @@ typedef int8_t twin_gfixed_t;
#define twin_sfixed_to_dfixed(s) (((twin_dfixed_t) (s)) << 4)
#define twin_dfixed_to_sfixed(d) ((twin_sfixed_t) ((d) >> 4))

#define twin_xfixed_to_fixed(x) (twin_fixed_t)((x) >> 16)
#define twin_fixed_to_xfixed(f) (((twin_xfixed_t) (f)) << 16)

/*
* twin_sfixed_t a = b'10100;
* twin_sfixed_t b = b'10000;
Expand Down Expand Up @@ -125,6 +137,11 @@ typedef int8_t twin_gfixed_t;
#define twin_dfixed_div(a, b) \
((((twin_dfixed_t) (a)) << 8) / ((twin_dfixed_t) (b)))

#define twin_xfixed_mul(a, b) \
(twin_xfixed_t)((((__int128_t) (a)) * ((__int128_t) (b))) >> 32)
#define twin_xfixed_div(a, b) \
(twin_xfixed_t)((((__int128_t) (a)) << 32) / ((__int128_t) (b)))

/*
* 'double' is a no-no in any shipping code, but useful during
* development
Expand All @@ -141,6 +158,7 @@ typedef int8_t twin_gfixed_t;

#define TWIN_GFIXED_ONE (0x40)

#define TWIN_XFIXED_ONE (0x100000000)
/*
* Compositing stuff
*/
Expand Down Expand Up @@ -375,7 +393,7 @@ twin_dfixed_t _twin_distance_to_line_squared(twin_spoint_t *p,
* Fixed point helper functions
*/
twin_sfixed_t _twin_sfixed_sqrt(twin_sfixed_t as);

twin_xfixed_t _twin_xfixed_sqrt(twin_xfixed_t a);
/*
* Matrix stuff
*/
Expand Down Expand Up @@ -595,11 +613,22 @@ static inline int twin_clz(uint32_t v)
return 31 - leading_zero;
return 32; /* undefined behavior */
}
static inline int twin_clzll(uint64_t v)
{
uint32_t leading_zero = 0;
if (_BitScanReverse64(&leading_zero, v))
return 63 - leading_zero;
return 64; /* undefined behavior */
}
#elif defined(__GNUC__) || defined(__clang__)
static inline int twin_clz(uint32_t v)
{
return __builtin_clz(v);
}
static inline int twin_clzll(uint64_t v)
{
return __builtin_clzll(v);
}
#else /* generic implementation */
static inline int twin_clz(uint32_t v)
{
Expand All @@ -617,6 +646,26 @@ static inline int twin_clz(uint32_t v)

return mul_debruijn[(uint32_t) (v * 0x07C4ACDDU) >> 27];
}
static inline int twin_clzll(uint64_t v)
{
/* https://stackoverflow.com/questions/21888140/de-bruijn-algorithm-binary-digit-count-64bits-c-sharp
*/
static const uint8_t mul_debruijn[] = {
0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12,
};

v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;

return mul_debruijn[(uint64_t) (v * 0x022fdd63cc95386dUL) >> 58];
}
#endif

extern const uint8_t _twin_cursor_default[];
Expand Down
41 changes: 41 additions & 0 deletions src/fixed.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
#define CHECK_INTERVAL(x, minx, epsilon) \
((int32_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)
#define CHECK_INTERVAL_64(x, minx, epsilon) \
((int64_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)

twin_fixed_t twin_fixed_sqrt(twin_fixed_t a)
{
Expand Down Expand Up @@ -79,3 +81,42 @@ twin_sfixed_t _twin_sfixed_sqrt(twin_sfixed_t as)

return (offset >= 0) ? z >> offset : z << (-offset);
}

twin_xfixed_t _twin_xfixed_sqrt(twin_xfixed_t a)
{
/* Early return for non-positive inputs */
if (a <= 0)
return 0;

/* Special case for values very close to 1 */
if (CHECK_INTERVAL_64(a, TWIN_XFIXED_ONE, (1ULL << (16 - 1))))
return TWIN_XFIXED_ONE;

/* Count leading zero bits to normalize the input */
int64_t offset = 0;
for (twin_xfixed_t i = a; !(UINT64_C(0x4000000000000000) & i); i <<= 1)
++offset;

/* Ensure even offset for precision */
offset &= ~1;

/* Shift left to expand precision */
a <<= offset;

/* Calculate scaling offset */
offset >>= 1;
offset -= (32 >> 1);

/* Digit-by-digit square root calculation */
twin_xfixed_t z = 0;
for (twin_xfixed_t m = 1ULL << ((63 - __builtin_clzll(a)) & ~1ULL); m;
m >>= 2) {
twin_xfixed_t b = z + m;
z >>= 1;
if (a >= b)
a -= b, z += m;
}

/* Shift back the expanded digits */
return (offset >= 0) ? z >> offset : z << (-offset);
}
Loading

0 comments on commit 9ae9814

Please sign in to comment.