From: Jean-Philippe Bruyère Date: Thu, 6 Jan 2022 10:15:54 +0000 (+0100) Subject: elliptical_arc, ellipse, rounded_rectangle2 X-Git-Tag: v0.3.0-beta~44 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=6ea125a2ce97e336b729de70828324e535d6c97b;p=jp%2Fvkvg.git elliptical_arc, ellipse, rounded_rectangle2 --- diff --git a/include/vkvg.h b/include/vkvg.h index 0d60d34..931036d 100644 --- a/include/vkvg.h +++ b/include/vkvg.h @@ -1070,6 +1070,25 @@ vkvg_status_t vkvg_rectangle(VkvgContext ctx, float x, float y, float w, float h */ vkvg_public vkvg_status_t vkvg_rounded_rectangle (VkvgContext ctx, float x, float y, float w, float h, float radius); +/** +* @brief Add an axis aligned rectangle with rounded corners defined in both axis to the current path. +* +* Adds a closed sub-path rectangle of the given size to the current path at position (x, y). +* @param ctx The vkvg context pointer. +* @param x The x coordinate of the top left corner of the rectangle to emit. +* @param y The y coordinate of the top left corner of the rectangle to emit. +* @param w The width in pixel of the rectangle to draw. +* @param h The height in pixel of the rectangle to draw. +* @param rx The horizontal radius of the corners. +* @param ry The vertical radius of the corners. +* @return VKVG_STATUS_SUCCESS or VKVG_STATUS_INVALID_RECT if width or height is equal to 0. +*/ +vkvg_public +void vkvg_rounded_rectangle2 (VkvgContext ctx, float x, float y, float w, float h, float rx, float ry); +vkvg_public +void vkvg_ellipse (VkvgContext ctx, float radiusX, float radiusY, float x, float y, float rotationAngle); +vkvg_public +void vkvg_elliptic_arc (VkvgContext ctx, float x2, float y2, bool largeArc, bool counterClockWise, float rx, float ry, float phi); /** * @brief Stroke command * diff --git a/screenshot3.png b/screenshot3.png new file mode 100644 index 0000000..aeba2ba Binary files /dev/null and b/screenshot3.png differ diff --git a/src/vkvg_context.c b/src/vkvg_context.c index d590694..5f51734 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -1299,3 +1299,56 @@ void vkvg_set_matrix (VkvgContext ctx, const vkvg_matrix_t* matrix){ void vkvg_get_matrix (VkvgContext ctx, const vkvg_matrix_t* matrix){ memcpy ((void*)matrix, &ctx->pushConsts.mat, sizeof(vkvg_matrix_t)); } + +void vkvg_elliptic_arc (VkvgContext ctx, float x2, float y2, bool largeArc, bool counterClockWise, float rx, float ry, float phi) { + float x1, y1; + vkvg_get_current_point(ctx, &x1, &y1); + _elliptic_arc(ctx, x1, y1, x2, y2, largeArc, counterClockWise, rx, ry, phi); +} + +void vkvg_ellipse (VkvgContext ctx, float radiusX, float radiusY, float x, float y, float rotationAngle) { + if (ctx->status) + return; + + float width_two_thirds = radiusX * 4 / 3; + + float dx1 = sinf(rotationAngle) * radiusY; + float dy1 = cosf(rotationAngle) * radiusY; + float dx2 = cosf(rotationAngle) * width_two_thirds; + float dy2 = sinf(rotationAngle) * width_two_thirds; + + float topCenterX = x - dx1; + float topCenterY = y + dy1; + float topRightX = topCenterX + dx2; + float topRightY = topCenterY + dy2; + float topLeftX = topCenterX - dx2; + float topLeftY = topCenterY - dy2; + + float bottomCenterX = x + dx1; + float bottomCenterY = y - dy1; + float bottomRightX = bottomCenterX + dx2; + float bottomRightY = bottomCenterY + dy2; + float bottomLeftX = bottomCenterX - dx2; + float bottomLeftY = bottomCenterY - dy2; + + vkvg_move_to (ctx, bottomCenterX, bottomCenterY); + vkvg_curve_to (ctx, bottomRightX, bottomRightY, topRightX, topRightY, topCenterX, topCenterY); + vkvg_curve_to (ctx, topLeftX, topLeftY, bottomLeftX, bottomLeftY, bottomCenterX, bottomCenterY); + vkvg_close_path (ctx); +} +void vkvg_rounded_rectangle2 (VkvgContext ctx, float x, float y, float w, float h, float rx, float ry){ + vkvg_move_to (ctx, x+rx, y); + vkvg_line_to (ctx, x+w-rx, y); + vkvg_elliptic_arc(ctx, x+w, y+ry, false, true, rx, ry, 0); + + vkvg_line_to (ctx, x+w, y+h-ry); + vkvg_elliptic_arc(ctx, x+w-rx, y+h, false, true, rx, ry, 0); + + vkvg_line_to (ctx, x+rx, y+h); + vkvg_elliptic_arc(ctx, x, y+h-ry , false, true, rx, ry, 0); + + vkvg_line_to (ctx, x, y+ry); + vkvg_elliptic_arc(ctx, x+rx, y , false, true, rx, ry, 0); + + vkvg_close_path(ctx); +} diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c index a3edd2f..5af91aa 100644 --- a/src/vkvg_context_internal.c +++ b/src/vkvg_context_internal.c @@ -1314,6 +1314,132 @@ void _recursive_bezier (VkvgContext ctx, float distanceTolerance, } #pragma warning(default:4127) +void _elliptic_arc (VkvgContext ctx, float x1, float y1, float x2, float y2, bool largeArc, bool counterClockWise, float _rx, float _ry, float phi) { + if (ctx->status) + return; + + if (_rx==0||_ry==0) { + if (_current_path_is_empty(ctx)) + vkvg_move_to(ctx, x1, y1); + vkvg_line_to(ctx, x2, y2); + return; + } + float rx = fabsf(_rx); + float ry = fabsf(_ry); + + mat2 m = { + { cosf (phi), sinf (phi)}, + {-sinf (phi), cosf (phi)} + }; + vec2 p = {(x1 - x2)/2, (y1 - y2)/2}; + vec2 p1 = mat2_mult_vec2 (m, p); + + //radii corrections + double lambda = powf (p1.x, 2) / powf (rx, 2) + powf (p1.y, 2) / powf (ry, 2); + if (lambda > 1) { + lambda = sqrtf (lambda); + rx *= lambda; + ry *= lambda; + } + + p = (vec2){rx * p1.y / ry, -ry * p1.x / rx}; + + vec2 cp = vec2_mult_s (p, sqrtf (fabsf ( + (powf (rx,2) * powf (ry,2) - powf (rx,2) * powf (p1.y, 2) - powf (ry,2) * powf (p1.x, 2)) / + (powf (rx,2) * powf (p1.y, 2) + powf (ry,2) * powf (p1.x, 2)) + ))); + + if (largeArc == counterClockWise) + vec2_inv(&cp); + + m = (mat2) { + {cosf (phi),-sinf (phi)}, + {sinf (phi), cosf (phi)} + }; + p = (vec2){(x1 + x2)/2, (y1 + y2)/2}; + vec2 c = vec2_add (mat2_mult_vec2(m, cp) , p); + + vec2 u = vec2_unit_x; + vec2 v = {(p1.x-cp.x)/rx, (p1.y-cp.y)/ry}; + double sa = acosf (vec2_dot (u, v) / (fabsf(vec2_length(v)) * fabsf(vec2_length(u)))); + if (isnanf(sa)) + sa=M_PIF; + if (u.x*v.y-u.y*v.x < 0) + sa = -sa; + + u = v; + v = (vec2) {(-p1.x-cp.x)/rx, (-p1.y-cp.y)/ry}; + double delta_theta = acosf (vec2_dot (u, v) / (fabsf(vec2_length (v)) * fabsf(vec2_length (u)))); + if (isnanf(delta_theta)) + delta_theta=M_PIF; + if (u.x*v.y-u.y*v.x < 0) + delta_theta = -delta_theta; + + if (counterClockWise) { + if (delta_theta < 0) + delta_theta += M_PIF * 2.0; + } else if (delta_theta > 0) + delta_theta -= M_PIF * 2.0; + + m = (mat2) { + {cosf (phi),-sinf (phi)}, + {sinf (phi), cosf (phi)} + }; + + double theta = sa; + double ea = sa + delta_theta; + + float step = _get_arc_step(ctx, fminf (rx, ry))*0.1f; + + p = (vec2) { + rx * cosf (theta), + ry * sinf (theta) + }; + vec2 xy = vec2_add (mat2_mult_vec2 (m, p), c); + + if (_current_path_is_empty(ctx)){ + _set_curve_start (ctx); + _add_point (ctx, xy.x, xy.y); + }else{ + vkvg_line_to(ctx, xy.x, xy.y); + _set_curve_start (ctx); + } + + _set_curve_start (ctx); + + if (sa < ea) { + theta += step; + while (theta < ea) { + p = (vec2) { + rx * cosf (theta), + ry * sinf (theta) + }; + xy = vec2_add (mat2_mult_vec2 (m, p), c); + _add_point (ctx, xy.x, xy.y); + theta += step; + } + } else { + theta -= step; + while (theta > ea) { + p = (vec2) { + rx * cosf (theta), + ry * sinf (theta) + }; + xy = vec2_add (mat2_mult_vec2 (m, p), c); + _add_point (ctx, xy.x, xy.y); + theta -= step; + } + } + p = (vec2) { + rx * cosf (ea), + ry * sinf (ea) + }; + xy = vec2_add (mat2_mult_vec2 (m, p), c); + _add_point (ctx, xy.x, xy.y); + _set_curve_end(ctx); +} + + //Even-Odd inside test with stencil buffer implementation. void _poly_fill (VkvgContext ctx){ //we anticipate the check for vbo buffer size, ibo is not used in poly_fill diff --git a/src/vkvg_context_internal.h b/src/vkvg_context_internal.h index 5bd51f6..699fce7 100644 --- a/src/vkvg_context_internal.h +++ b/src/vkvg_context_internal.h @@ -283,4 +283,6 @@ void _recursive_bezier(VkvgContext ctx, float distanceTolerance, void _bezier (VkvgContext ctx, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + +void _elliptic_arc (VkvgContext ctx, float x1, float y1, float x2, float y2, bool largeArc, bool counterClockWise, float _rx, float _ry, float phi); #endif