efl_gfx_path_append_arc(obj, x, y, w, h, start_angle, sweep_length);
#else
Efl_Canvas_Vg_Shape_Data *sd = NULL;
+ const Tvg_Path_Command *cmds = NULL;
+ uint32_t cmds_count = 0;
+
+ int i = 0, point_count = 0;
+ Point pts[15] = { 0 };
+ //FIX_TVG: we can revise this with tvg appendArc() way.
+ Point curve_start = _curves_for_arc(x, y, w, h, start_angle, sweep_length, pts, &point_count);
if (!obj) return;
sd = _get_shape_data(obj);
if (!sd || !sd->shape) return;
- double radius = fmin(w / 2.0f, h / 2.0f);
- double cx = x + (w / 2.0f);
- double cy = y + (h / 2.0f);
+ tvg_shape_get_path_commands(sd->shape, &cmds, &cmds_count);
+ if (cmds_count && cmds[cmds_count - 1] != TVG_PATH_COMMAND_CLOSE)
+ {
+ tvg_shape_line_to(sd->shape, curve_start.x, curve_start.y);
+ }
+ else
+ {
+ tvg_shape_move_to(sd->shape, curve_start.x, curve_start.y);
+ }
- tvg_shape_append_arc(sd->shape, cx, cy, radius, -start_angle, -sweep_length, 0);
- _assign_current_point(sd, NULL, cx + radius*cos((start_angle + sweep_length) * M_PI / 180),
- cy - radius*sin((start_angle + sweep_length) * M_PI / 180));
+ for (i = 0; i < point_count; i += 3)
+ {
+ tvg_shape_cubic_to(sd->shape, pts[i].x, pts[i].y,
+ pts[i+1].x, pts[i+1].y,
+ pts[i+2].x, pts[i+2].y);
+ }
+ _assign_current_point(sd, NULL, pts[i+2].x, pts[i+2].y);
#endif
efl_canvas_vg_node_change(obj);
}
* which will be deprecated at some point
*/
+#define PATH_KAPPA 0.5522847498
+
typedef struct _Efl_Tvg_Shape_Svg_Path Efl_Tvg_Shape_Svg_Path;
typedef struct _Arc_To_Init_Variables Arc_To_Init_Variables;
+typedef struct _Point Point;
struct _Efl_Tvg_Shape_Svg_Path {
char *svg_path_data;
int segments;
};
+struct _Point {
+ double x;
+ double y;
+};
+
static char *
_skipcomma(const char *content)
{
break;
}
}
+
+
+inline static void
+_bezier_coefficients(double t, double *ap, double *bp, double *cp, double *dp)
+{
+ double a,b,c,d;
+ double m_t = 1.0 - t;
+
+ b = m_t * m_t;
+ c = t * t;
+ d = c * t;
+ a = b * m_t;
+ b *= 3.0 * t;
+ c *= 3.0 * m_t;
+ *ap = a;
+ *bp = b;
+ *cp = c;
+ *dp = d;
+}
+
+static double
+_efl_gfx_t_for_arc_angle(double angle)
+{
+ double radians, cos_angle, sin_angle, tc, ts, t;
+
+ if (angle < 0.00001) return 0;
+ if (EINA_FLT_EQ(angle, 90.0)) return 1;
+
+ radians = (angle/180) * M_PI;
+
+ cos_angle = cos(radians);
+ sin_angle = sin(radians);
+
+ // initial guess
+ tc = angle / 90;
+
+ // do some iterations of newton's method to approximate cos_angle
+ // finds the zero of the function b.pointAt(tc).x() - cos_angle
+ tc -= ((((2-3*PATH_KAPPA) * tc + 3*(PATH_KAPPA-1)) * tc) * tc + 1 - cos_angle) // value
+ / (((6-9*PATH_KAPPA) * tc + 6*(PATH_KAPPA-1)) * tc); // derivative
+ tc -= ((((2-3*PATH_KAPPA) * tc + 3*(PATH_KAPPA-1)) * tc) * tc + 1 - cos_angle) // value
+ / (((6-9*PATH_KAPPA) * tc + 6*(PATH_KAPPA-1)) * tc); // derivative
+
+ // initial guess
+ ts = tc;
+ // do some iterations of newton's method to approximate sin_angle
+ // finds the zero of the function b.pointAt(tc).y() - sin_angle
+ ts -= ((((3*PATH_KAPPA-2) * ts - 6*PATH_KAPPA + 3) * ts + 3*PATH_KAPPA) * ts - sin_angle)
+ / (((9*PATH_KAPPA-6) * ts + 12*PATH_KAPPA - 6) * ts + 3*PATH_KAPPA);
+ ts -= ((((3*PATH_KAPPA-2) * ts - 6*PATH_KAPPA + 3) * ts + 3*PATH_KAPPA) * ts - sin_angle)
+ / (((9*PATH_KAPPA-6) * ts + 12*PATH_KAPPA - 6) * ts + 3*PATH_KAPPA);
+
+ // use the average of the t that best approximates cos_angle
+ // and the t that best approximates sin_angle
+ t = 0.5 * (tc + ts);
+ return t;
+}
+
+static void
+_find_ellipse_coords(double x, double y, double w, double h, double angle,
+ double length, Point* start_point, Point *end_point)
+{
+ int i, quadrant;
+ double theta, t, a, b, c, d, px, py, cx, cy;
+ double w2 = w / 2;
+ double h2 = h / 2;
+ double angles[2] = { angle, angle + length };
+ Point *points[2];
+
+ if (EINA_FLT_EQ(w, 0.0) || EINA_FLT_EQ(h, 0.0))
+ {
+ if (start_point)
+ {
+ start_point->x = 0;
+ start_point->y = 0;
+ }
+ if (end_point)
+ {
+ end_point->x = 0;
+ end_point->y = 0;
+ }
+ return;
+ }
+
+ points[0] = start_point;
+ points[1] = end_point;
+
+ for (i = 0; i < 2; ++i)
+ {
+ if (!points[i])
+ continue;
+
+ theta = angles[i] - 360 * floor(angles[i] / 360);
+ t = theta / 90;
+ // truncate
+ quadrant = (int)t;
+ t -= quadrant;
+
+ t = _efl_gfx_t_for_arc_angle(90 * t);
+
+ // swap x and y?
+ if (quadrant & 1)
+ t = 1 - t;
+
+ _bezier_coefficients(t, &a, &b, &c, &d);
+ px = a + b + c*PATH_KAPPA;
+ py = d + c + b*PATH_KAPPA;
+
+ // left quadrants
+ if (quadrant == 1 || quadrant == 2)
+ px = -px;
+
+ // top quadrants
+ if (quadrant == 0 || quadrant == 1)
+ py = -py;
+ cx = x+w/2;
+ cy = y+h/2;
+ points[i]->x = cx + w2 * px;
+ points[i]->y = cy + h2 * py;
+ }
+}
+
+
+// The return value is the starting point of the arc
+static Point
+_curves_for_arc(double x, double y, double w, double h,
+ double start_angle, double sweep_length,
+ Point *curves, int *point_count)
+{
+ int start_segment, end_segment, delta, i, j, end, quadrant;
+ double start_t, end_t;
+ Eina_Bool split_at_start, split_at_end;
+ Eina_Bezier b, res;
+ Point start_point, end_point;
+ double w2 = w / 2;
+ double w2k = w2 * PATH_KAPPA;
+ double h2 = h / 2;
+ double h2k = h2 * PATH_KAPPA;
+
+ Point points[16] =
+ {
+ // start point
+ { x + w, y + h2 },
+
+ // 0 -> 270 degrees
+ { x + w, y + h2 + h2k },
+ { x + w2 + w2k, y + h },
+ { x + w2, y + h },
+
+ // 270 -> 180 degrees
+ { x + w2 - w2k, y + h },
+ { x, y + h2 + h2k },
+ { x, y + h2 },
+
+ // 180 -> 90 degrees
+ { x, y + h2 - h2k },
+ { x + w2 - w2k, y },
+ { x + w2, y },
+
+ // 90 -> 0 degrees
+ { x + w2 + w2k, y },
+ { x + w, y + h2 - h2k },
+ { x + w, y + h2 }
+ };
+
+ *point_count = 0;
+
+ if (sweep_length > 360) sweep_length = 360;
+ else if (sweep_length < -360) sweep_length = -360;
+
+ // Special case fast paths
+ if (EINA_FLT_EQ(start_angle, 0))
+ {
+ if (EINA_FLT_EQ(sweep_length, 360))
+ {
+ for (i = 11; i >= 0; --i)
+ curves[(*point_count)++] = points[i];
+ return points[12];
+ }
+ else if (EINA_FLT_EQ(sweep_length, -360))
+ {
+ for (i = 1; i <= 12; ++i)
+ curves[(*point_count)++] = points[i];
+ return points[0];
+ }
+ }
+
+ start_segment = (int)(floor(start_angle / 90));
+ end_segment = (int)(floor((start_angle + sweep_length) / 90));
+
+ start_t = (start_angle - start_segment * 90) / 90;
+ end_t = (start_angle + sweep_length - end_segment * 90) / 90;
+
+ delta = sweep_length > 0 ? 1 : -1;
+ if (delta < 0)
+ {
+ start_t = 1 - start_t;
+ end_t = 1 - end_t;
+ }
+
+ // avoid empty start segment
+ if (EINA_FLT_EQ(start_t, 1.0))
+ {
+ start_t = 0;
+ start_segment += delta;
+ }
+
+ // avoid empty end segment
+ if (EINA_FLT_EQ(end_t, 0.0))
+ {
+ end_t = 1;
+ end_segment -= delta;
+ }
+
+ start_t = _efl_gfx_t_for_arc_angle(start_t * 90);
+ end_t = _efl_gfx_t_for_arc_angle(end_t * 90);
+
+ split_at_start = !(fabs(start_t) <= 0.00001f);
+ split_at_end = !(fabs(end_t - 1.0) <= 0.00001f);
+
+ end = end_segment + delta;
+
+ // empty arc?
+ if (start_segment == end)
+ {
+ quadrant = 3 - ((start_segment % 4) + 4) % 4;
+ j = 3 * quadrant;
+ return delta > 0 ? points[j + 3] : points[j];
+ }
+
+ _find_ellipse_coords(x, y, w, h, start_angle, sweep_length,
+ &start_point, &end_point);
+
+ for (i = start_segment; i != end; i += delta)
+ {
+ quadrant = 3 - ((i % 4) + 4) % 4;
+ j = 3 * quadrant;
+
+ if (delta > 0)
+ eina_bezier_values_set(&b, points[j + 3].x, points[j + 3].y,
+ points[j + 2].x, points[j + 2].y,
+ points[j + 1].x, points[j + 1].y,
+ points[j].x, points[j].y);
+ else
+ eina_bezier_values_set(&b, points[j].x, points[j].y,
+ points[j + 1].x, points[j + 1].y,
+ points[j + 2].x, points[j + 2].y,
+ points[j + 3].x, points[j + 3].y);
+
+ // empty arc?
+ if (start_segment == end_segment && (EINA_FLT_EQ(start_t, end_t)))
+ return start_point;
+
+ res = b;
+ if (i == start_segment)
+ {
+ if (i == end_segment && split_at_end)
+ eina_bezier_on_interval(&b, start_t, end_t, &res);
+ else if (split_at_start)
+ eina_bezier_on_interval(&b, start_t, 1, &res);
+ }
+ else if (i == end_segment && split_at_end)
+ {
+ eina_bezier_on_interval(&b, 0, end_t, &res);
+ }
+
+ // push control points
+ curves[(*point_count)].x = res.ctrl_start.x;
+ curves[(*point_count)++].y = res.ctrl_start.y;
+ curves[(*point_count)].x = res.ctrl_end.x;
+ curves[(*point_count)++].y = res.ctrl_end.y;
+ curves[(*point_count)].x = res.end.x;
+ curves[(*point_count)++].y = res.end.y;
+ }
+
+ curves[*(point_count)-1] = end_point;
+
+ return start_point;
+}
+
+
#endif