*/
vkvg_public
void vkvg_set_line_width (VkvgContext ctx, float width);
+/**
+ * @brief set line join miter size limit.
+ *
+ * If the current line join style is set to VKVG_LINE_JOIN_MITER (see vkvg_set_line_join()), the miter limit is used to determine whether the lines should be
+ * joined with a bevel instead of a miter. Vkvg divides the length of the miter by the line width. If the result is greater than the miter limit, the style is converted to a bevel.
+ *
+ * The default miter limit value is 10.0, which will convert joins with interior angles less than 11 degrees to bevels instead of miters.
+ * For reference, a miter limit of 2.0 makes the miter cutoff at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90 degrees.
+ *
+ * A miter limit for a desired angle can be computed as: miter limit = 1/sin(angle/2)
+ *
+ * @param ctx a valid vkvg @ref context
+ * @param limit new current miter limit value for the context.
+ */
+vkvg_public
+void vkvg_set_miter_limit (VkvgContext ctx, float limit);
+/**
+ * @brief Gets the current miter limit, as set by @ref vkvg_set_miter_limit().
+ * @param ctx a valid vkvg @ref context
+ * @return the current miter limit for the context.
+ */
+vkvg_public
+float vkvg_get_miter_limit (VkvgContext ctx);
/**
* @brief set line terminations for the next draw command.
*
* @param ctx a valid vkvg @ref context
* @param cap new line termination, may be one of the value of #vkvg_line_cap_t.
*/
-
vkvg_public
void vkvg_set_line_cap (VkvgContext ctx, vkvg_line_cap_t cap);
/**
#include "cross_os.h"
#include <sys/types.h>
#include <sys/stat.h>
-//#include <unistd.h>
#define _CRT_SECURE_NO_WARNINGS
if ((cmd & VKVG_CMD_PATHPROPS_COMMANDS) == VKVG_CMD_PATHPROPS_COMMANDS) {
switch (r->cmd) {
case VKVG_CMD_SET_LINE_WIDTH:
+ case VKVG_CMD_SET_MITER_LIMIT:
STORE_FLOATS(1);
break;
case VKVG_CMD_SET_LINE_JOIN:
case VKVG_CMD_SET_LINE_WIDTH:
vkvg_set_line_width (ctx, floats[0]);
return;
+ case VKVG_CMD_SET_MITER_LIMIT:
+ vkvg_set_miter_limit (ctx, floats[0]);
+ return;
case VKVG_CMD_SET_LINE_JOIN:
vkvg_set_line_join (ctx, (vkvg_line_join_t)uints[0]);
return;
#define VKVG_CMD_ELLIPTICAL_ARC_TO (0x000C|VKVG_CMD_PATH_COMMANDS)
#define VKVG_CMD_SET_LINE_WIDTH (0x0001|VKVG_CMD_PATHPROPS_COMMANDS)
-#define VKVG_CMD_SET_LINE_JOIN (0x0002|VKVG_CMD_PATHPROPS_COMMANDS)
-#define VKVG_CMD_SET_LINE_CAP (0x0003|VKVG_CMD_PATHPROPS_COMMANDS)
-#define VKVG_CMD_SET_OPERATOR (0x0004|VKVG_CMD_PATHPROPS_COMMANDS)
-#define VKVG_CMD_SET_FILL_RULE (0x0005|VKVG_CMD_PATHPROPS_COMMANDS)
-#define VKVG_CMD_SET_DASH (0x0006|VKVG_CMD_PATHPROPS_COMMANDS)
+#define VKVG_CMD_SET_MITER_LIMIT (0x0002|VKVG_CMD_PATHPROPS_COMMANDS)
+#define VKVG_CMD_SET_LINE_JOIN (0x0003|VKVG_CMD_PATHPROPS_COMMANDS)
+#define VKVG_CMD_SET_LINE_CAP (0x0004|VKVG_CMD_PATHPROPS_COMMANDS)
+#define VKVG_CMD_SET_OPERATOR (0x0005|VKVG_CMD_PATHPROPS_COMMANDS)
+#define VKVG_CMD_SET_FILL_RULE (0x0006|VKVG_CMD_PATHPROPS_COMMANDS)
+#define VKVG_CMD_SET_DASH (0x0007|VKVG_CMD_PATHPROPS_COMMANDS)
#define VKVG_CMD_TRANSLATE (0x0001|VKVG_CMD_TRANSFORM_COMMANDS)
#define VKVG_CMD_ROTATE (0x0002|VKVG_CMD_TRANSFORM_COMMANDS)
void _init_ctx (VkvgContext ctx) {
ctx->lineWidth = 1;
+ ctx->miterLimit = 10;
ctx->curOperator = VKVG_OPERATOR_OVER;
ctx->curFillRule = VKVG_FILL_RULE_NON_ZERO;
ctx->bounds = (VkRect2D) {{0,0},{ctx->pSurf->width,ctx->pSurf->height}};
LOG(VKVG_LOG_INFO, "STROKE: ctx = %p; path ptr = %d;\n", ctx, ctx->pathPtr);
stroke_context_t str = {0};
+ str.lhMax = ctx->miterLimit * ctx->lineWidth;
uint32_t ptrPath = 0;
- float hw = ctx->lineWidth / 2.0f;
+ float hw = ctx->lineWidth / 2.0f;//may be put in stroke ctx
while (ptrPath < ctx->pathPtr){
uint32_t ptrSegment = 0, lastSegmentPointIdx = 0;
RECORD(ctx, VKVG_CMD_SET_LINE_WIDTH, width);
ctx->lineWidth = width;
}
+void vkvg_set_miter_limit (VkvgContext ctx, float limit){
+ RECORD(ctx, VKVG_CMD_SET_LINE_WIDTH, limit);
+ ctx->miterLimit = limit;
+}
void vkvg_set_line_cap (VkvgContext ctx, vkvg_line_cap_t cap){
RECORD(ctx, VKVG_CMD_SET_LINE_CAP, cap);
ctx->lineCap = cap;
memcpy (sav->dashes, ctx->dashes, sizeof(float) * ctx->dashCount);
}
sav->lineWidth = ctx->lineWidth;
+ sav->miterLimit = ctx->miterLimit;
sav->curOperator= ctx->curOperator;
sav->lineCap = ctx->lineCap;
sav->lineWidth = ctx->lineWidth;
}
ctx->lineWidth = sav->lineWidth;
+ ctx->miterLimit = sav->miterLimit;
ctx->curOperator= sav->curOperator;
ctx->lineCap = sav->lineCap;
ctx->lineJoin = sav->lineJoint;
vec2 v1n = vec2_div_s (v1, length_v1);
float dot = vec2_dot (v0n, v1n);
float det = v0n.x * v1n.y - v0n.y * v1n.x;
- if (EQUF(dot,1.0f))
+ if (EQUF(dot,1.0f))//colinear
return false;
- if (EQUF(dot,-1.0f)) {
+ if (EQUF(dot,-1.0f)) {//cusp (could draw line butt?)
vec2 vPerp = vec2_mult_s(vec2_perp (v0n), hw);
VKVG_IBO_INDEX_TYPE idx = (VKVG_IBO_INDEX_TYPE)(ctx->vertCount - ctx->curVertOffset);
}
- vec2 bisec_n = vec2_norm(vec2_add(v0n,v1n));
+ vec2 bisec_n = vec2_norm(vec2_add(v0n,v1n));//bisec/bisec_perp are inverted names
float alpha = acosf(dot);
if (ctx->lineJoin == VKVG_LINE_JOIN_MITER || isCurve){
- if (dot < -0.95f && rlh < lh) {
- double x = (lh - rlh) * cosf (halfAlpha);
+ if (lh > str->lhMax) {//miter limit
+ double x = (lh - str->lhMax) * cosf (halfAlpha);
vec2 bisecPerp = vec2_mult_s (bisec_n, x);
+ bisec = vec2_mult_s (bisec_n_perp, str->lhMax);
if (det < 0) {
v.pos = rlh_inside_pos;
_add_vertex(ctx, v);
return false;
}
- } else {
+ } else {//normal miter
if (det < 0) {
v.pos = rlh_inside_pos;
_add_vertex(ctx, v);
struct _vkvg_context_save_t* pNext;
float lineWidth;
+ float miterLimit;
uint32_t dashCount; //value count in dash array, 0 if dash not set.
float dashOffset; //an offset for dash
float* dashes; //an array of alternate lengths of on and off stroke.
bool simpleConvex; //true if path is single rect or concave closed curve.
float lineWidth;
+ float miterLimit;
uint32_t dashCount; //value count in dash array, 0 if dash not set.
float dashOffset; //an offset for dash
float* dashes; //an array of alternate lengths of on and off stroke.
uint32_t cp;//current point
VKVG_IBO_INDEX_TYPE firstIdx;//save first point idx for closed path
+ float lhMax//miter limit * line width
}stroke_context_t;
void _check_vertex_cache_size (VkvgContext ctx);
float dash[] = {0, 60};
uint32_t dashCountInit = 2;
uint32_t dashCount = 0;
+float miterLimit = 10.0f;
vkvg_clear(ctx);
if (dashCount > 0)
vkvg_set_dash(ctx, dash, dashCount,0);
- vkvg_set_source_rgba(ctx,1,0,0,1);
- vkvg_set_line_width(ctx,lineWidth);
- vkvg_set_line_join (ctx, lineJoin);
- vkvg_set_line_cap (ctx, lineCap);
+ vkvg_set_source_rgba (ctx,1,0,0,1);
+ vkvg_set_line_width (ctx,lineWidth);
+ vkvg_set_line_join (ctx, lineJoin);
+ vkvg_set_line_cap (ctx, lineCap);
+ vkvg_set_miter_limit (ctx, miterLimit);
if (startWithArc)
vkvg_arc_negative(ctx,pts[0].x,pts[0].y,200, M_PIF*1.5f, M_PIF);
else
dash[0] = 80;
break;
+ case GLFW_KEY_L :
+ if (mods & GLFW_MOD_SHIFT)
+ miterLimit /= 2.0f;
+ else
+ miterLimit *= 2.0f;
+ break;
case GLFW_KEY_KP_ADD :
if (ptsCount < initPtsCount)
ptsCount++;