2016-11-23 00:29:38 -08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <float.h>
|
|
|
|
#include <ctype.h>
|
2018-01-16 21:20:15 -08:00
|
|
|
#include <locale.h>
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
#include <Efl.h>
|
|
|
|
|
|
|
|
typedef struct _Efl_Gfx_Path_Data Efl_Gfx_Path_Data;
|
|
|
|
struct _Efl_Gfx_Path_Data
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
double x;
|
|
|
|
double y;
|
|
|
|
} current, current_ctrl;
|
|
|
|
|
|
|
|
Efl_Gfx_Path_Command *commands;
|
|
|
|
double *points;
|
|
|
|
|
|
|
|
unsigned int commands_count;
|
|
|
|
unsigned int points_count;
|
2018-12-23 20:52:13 -08:00
|
|
|
|
|
|
|
unsigned int reserved_pts_cnt; //Reserved Points Count
|
|
|
|
unsigned int reserved_cmd_cnt; //Reserved Commands Count
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
char *path_data;
|
2016-11-23 00:29:38 -08:00
|
|
|
Eina_Bool convex;
|
|
|
|
};
|
|
|
|
|
2018-12-23 20:52:13 -08:00
|
|
|
static void _path_interpolation(Eo *obj, Efl_Gfx_Path_Data *pd, char *from, char *to, double pos);
|
|
|
|
static void _efl_gfx_path_reset(Eo *obj, Efl_Gfx_Path_Data *pd);
|
|
|
|
|
2016-11-23 00:29:38 -08:00
|
|
|
static inline unsigned int
|
|
|
|
_efl_gfx_path_command_length(Efl_Gfx_Path_Command command)
|
|
|
|
{
|
|
|
|
switch (command)
|
|
|
|
{
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_END: return 0;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO: return 2;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO: return 2;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO: return 6;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_CLOSE: return 0;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_LAST: return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
_efl_gfx_path_length(const Efl_Gfx_Path_Command *commands,
|
|
|
|
unsigned int *cmd_length,
|
|
|
|
unsigned int *pts_length)
|
|
|
|
{
|
|
|
|
if (commands)
|
|
|
|
{
|
|
|
|
while (commands[*cmd_length] != EFL_GFX_PATH_COMMAND_TYPE_END)
|
|
|
|
{
|
|
|
|
*pts_length += _efl_gfx_path_command_length(commands[*cmd_length]);
|
|
|
|
(*cmd_length)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Accounting for END command and handle gracefully the NULL case
|
|
|
|
at the same time */
|
|
|
|
(*cmd_length)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Eina_Bool
|
|
|
|
efl_gfx_path_grow(Efl_Gfx_Path_Command command,
|
|
|
|
Efl_Gfx_Path_Data *pd,
|
|
|
|
double **offset_point)
|
|
|
|
{
|
|
|
|
unsigned int cmd_length = 0, pts_length = 0;
|
|
|
|
|
|
|
|
cmd_length = pd->commands_count ? pd->commands_count : 1;
|
|
|
|
pts_length = pd->points_count;
|
|
|
|
|
|
|
|
if (_efl_gfx_path_command_length(command))
|
|
|
|
{
|
|
|
|
pts_length += _efl_gfx_path_command_length(command);
|
|
|
|
|
2018-12-23 20:52:13 -08:00
|
|
|
//grow up twice
|
|
|
|
if (pts_length > pd->reserved_pts_cnt)
|
|
|
|
{
|
|
|
|
double *pts_tmp = realloc(pd->points, sizeof(double) * (pts_length * 2));
|
|
|
|
if (!pts_tmp) return EINA_FALSE;
|
|
|
|
pd->reserved_pts_cnt = pts_length * 2;
|
|
|
|
pd->points = pts_tmp;
|
|
|
|
}
|
|
|
|
|
2016-11-23 00:29:38 -08:00
|
|
|
*offset_point =
|
|
|
|
pd->points + pts_length - _efl_gfx_path_command_length(command);
|
|
|
|
}
|
|
|
|
|
2018-12-23 20:52:13 -08:00
|
|
|
//grow up twice
|
|
|
|
if ((cmd_length + 1) > pd->reserved_cmd_cnt)
|
|
|
|
{
|
|
|
|
Efl_Gfx_Path_Command *cmd_tmp =
|
|
|
|
realloc(pd->commands, (cmd_length * 2) * sizeof (Efl_Gfx_Path_Command));
|
|
|
|
if (!cmd_tmp) return EINA_FALSE;
|
|
|
|
pd->reserved_cmd_cnt = (cmd_length * 2);
|
|
|
|
pd->commands = cmd_tmp;
|
|
|
|
}
|
|
|
|
|
2016-11-23 00:29:38 -08:00
|
|
|
pd->commands_count = cmd_length + 1;
|
|
|
|
pd->points_count = pts_length;
|
|
|
|
|
|
|
|
// Append the command
|
2018-12-23 20:52:13 -08:00
|
|
|
pd->commands[cmd_length - 1] = command;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
// NULL terminate the stream
|
2018-12-23 20:52:13 -08:00
|
|
|
pd->commands[cmd_length] = EFL_GFX_PATH_COMMAND_TYPE_END;
|
2016-11-23 00:29:38 -08:00
|
|
|
pd->convex = EINA_FALSE;
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_efl_gfx_path_current_search(const Efl_Gfx_Path_Command *cmd,
|
|
|
|
const double *points,
|
|
|
|
double *current_x, double *current_y,
|
|
|
|
double *current_ctrl_x, double *current_ctrl_y)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (current_x) *current_x = 0;
|
|
|
|
if (current_y) *current_y = 0;
|
|
|
|
if (current_ctrl_x) *current_ctrl_x = 0;
|
|
|
|
if (current_ctrl_y) *current_ctrl_y = 0;
|
|
|
|
|
|
|
|
if (!cmd || !points) return EINA_FALSE;
|
|
|
|
|
2020-02-17 23:23:11 -08:00
|
|
|
for (i = 0; cmd[i] != EFL_GFX_PATH_COMMAND_TYPE_END; i++)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
switch (cmd[i])
|
|
|
|
{
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO:
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_LINE_TO:
|
|
|
|
if (current_x) *current_x = points[0];
|
|
|
|
if (current_y) *current_y = points[1];
|
|
|
|
points += 2;
|
|
|
|
break;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO:
|
|
|
|
if (current_x) *current_x = points[0];
|
|
|
|
if (current_y) *current_y = points[1];
|
|
|
|
if (current_ctrl_x) *current_ctrl_x = points[4];
|
|
|
|
if (current_ctrl_y) *current_ctrl_y = points[5];
|
|
|
|
points += 6;
|
|
|
|
break;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_CLOSE:
|
|
|
|
break;
|
|
|
|
case EFL_GFX_PATH_COMMAND_TYPE_LAST:
|
|
|
|
default:
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_path_set(Eo *obj, Efl_Gfx_Path_Data *pd,
|
2018-12-23 20:52:13 -08:00
|
|
|
const Efl_Gfx_Path_Command *commands,
|
|
|
|
const double *points)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-12-23 20:52:13 -08:00
|
|
|
if (!commands)
|
|
|
|
{
|
|
|
|
_efl_gfx_path_reset(obj, pd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-23 00:29:38 -08:00
|
|
|
Efl_Gfx_Path_Command *cmds;
|
|
|
|
double *pts;
|
|
|
|
unsigned int cmds_length = 0, pts_length = 0;
|
|
|
|
|
|
|
|
_efl_gfx_path_length(commands, &cmds_length, &pts_length);
|
|
|
|
|
|
|
|
cmds = realloc(pd->commands, sizeof (Efl_Gfx_Path_Command) * cmds_length);
|
|
|
|
if (!cmds) return;
|
|
|
|
|
|
|
|
pd->commands = cmds;
|
|
|
|
|
|
|
|
pts = realloc(pd->points, sizeof (double) * pts_length);
|
|
|
|
if (!pts) return;
|
|
|
|
|
|
|
|
pd->points = pts;
|
|
|
|
pd->commands_count = cmds_length;
|
|
|
|
pd->points_count = pts_length;
|
|
|
|
|
2018-12-23 20:52:13 -08:00
|
|
|
//full reserved memory
|
|
|
|
pd->reserved_cmd_cnt = cmds_length;
|
|
|
|
pd->reserved_pts_cnt = pts_length;
|
|
|
|
|
|
|
|
memcpy(pd->commands, commands, sizeof(Efl_Gfx_Path_Command) * cmds_length);
|
|
|
|
memcpy(pd->points, points, sizeof(double) * pts_length);
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
_efl_gfx_path_current_search(pd->commands, pd->points,
|
|
|
|
&pd->current.x, &pd->current.y,
|
|
|
|
&pd->current_ctrl.x, &pd->current_ctrl.y);
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_gfx_path_path_get(const Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd,
|
2016-11-23 00:29:38 -08:00
|
|
|
const Efl_Gfx_Path_Command **commands,
|
|
|
|
const double **points)
|
|
|
|
{
|
|
|
|
if (commands) *commands = pd->commands;
|
|
|
|
if (points) *points = pd->points;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_gfx_path_length_get(const Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd,
|
2016-11-23 00:29:38 -08:00
|
|
|
unsigned int *commands, unsigned int *points)
|
|
|
|
{
|
|
|
|
if (commands) *commands = pd->commands_count;
|
|
|
|
if (points) *points = pd->points_count;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_gfx_path_bounds_get(const Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd, Eina_Rect *r)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
double minx, miny, maxx, maxy;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
EINA_RECTANGLE_SET(r, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
if (pd->points_count <= 0) return;
|
|
|
|
|
|
|
|
minx = pd->points[0];
|
|
|
|
miny = pd->points[1];
|
|
|
|
maxx = pd->points[0];
|
|
|
|
maxy = pd->points[1];
|
|
|
|
|
2017-02-13 06:27:55 -08:00
|
|
|
for (i = 2; i < pd->points_count; i += 2)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
minx = minx < pd->points[i] ? minx : pd->points[i];
|
|
|
|
miny = miny < pd->points[i + 1] ? miny : pd->points[i + 1];
|
|
|
|
maxx = maxx > pd->points[i] ? maxx : pd->points[i];
|
|
|
|
maxy = maxy > pd->points[i + 1] ? maxy : pd->points[i + 1];
|
|
|
|
}
|
|
|
|
|
2020-06-03 17:22:39 -07:00
|
|
|
EINA_RECTANGLE_SET(r, floor(minx), floor(miny), (ceil(maxx) - floor(minx)), (ceil(maxy) - floor(miny)));
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_gfx_path_current_get(const Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd,
|
2016-11-23 00:29:38 -08:00
|
|
|
double *x, double *y)
|
|
|
|
{
|
|
|
|
if (x) *x = pd->current.x;
|
|
|
|
if (y) *y = pd->current.y;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_gfx_path_current_ctrl_get(const Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd,
|
2016-11-23 00:29:38 -08:00
|
|
|
double *x, double *y)
|
|
|
|
{
|
|
|
|
if (x) *x = pd->current_ctrl.x;
|
|
|
|
if (y) *y = pd->current_ctrl.y;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static Eina_Bool
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_equal_commands_internal(Efl_Gfx_Path_Data *a,
|
|
|
|
Efl_Gfx_Path_Data *b)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (a->commands_count != b->commands_count) return EINA_FALSE;
|
|
|
|
if (a->commands_count <= 0) return EINA_TRUE;
|
|
|
|
|
|
|
|
for (i = 0; a->commands[i] == b->commands[i] &&
|
|
|
|
a->commands[i] != EFL_GFX_PATH_COMMAND_TYPE_END; i++)
|
|
|
|
{
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (a->commands[i] == b->commands[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline double
|
|
|
|
interpolate(double from, double to, double pos_map)
|
|
|
|
{
|
|
|
|
return (from * (1.0 - pos_map)) + (to * pos_map);
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static Eina_Bool
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_interpolate(Eo *obj, Efl_Gfx_Path_Data *pd,
|
2018-06-14 03:51:13 -07:00
|
|
|
const Eo *from, const Eo *to, double pos_map)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
Efl_Gfx_Path_Data *from_pd, *to_pd;
|
|
|
|
Efl_Gfx_Path_Command *cmds;
|
2018-06-14 03:51:13 -07:00
|
|
|
double interv; //interpolated value
|
2016-11-23 00:29:38 -08:00
|
|
|
double *pts;
|
|
|
|
|
|
|
|
if (!efl_isa(from, EFL_GFX_PATH_MIXIN) || !efl_isa(to, EFL_GFX_PATH_MIXIN))
|
|
|
|
return EINA_FALSE;
|
|
|
|
|
2018-06-14 03:51:13 -07:00
|
|
|
from_pd = efl_data_scope_get(from, EFL_GFX_PATH_MIXIN);
|
|
|
|
to_pd = efl_data_scope_get(to, EFL_GFX_PATH_MIXIN);
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-06-14 03:51:13 -07:00
|
|
|
//just in case
|
|
|
|
if (pd == from_pd || pd == to_pd) return EINA_FALSE;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
if (from_pd->path_data && to_pd->path_data)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
_efl_gfx_path_reset(obj, pd);
|
2018-06-14 03:51:13 -07:00
|
|
|
_path_interpolation(obj, pd,
|
|
|
|
from_pd->path_data, to_pd->path_data, pos_map);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
else
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
if (!_efl_gfx_path_equal_commands_internal(from_pd, to_pd))
|
|
|
|
return EINA_FALSE;
|
2018-06-14 03:51:13 -07:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
cmds = realloc(pd->commands,
|
2018-06-14 03:51:13 -07:00
|
|
|
sizeof(Efl_Gfx_Path_Command) * from_pd->commands_count);
|
|
|
|
if (!cmds && (from_pd->commands_count > 0)) return EINA_FALSE;
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
pd->commands = cmds;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
pts = realloc(pd->points,
|
2018-06-14 03:51:13 -07:00
|
|
|
sizeof(double) * from_pd->points_count);
|
|
|
|
if (!pts && (from_pd->points_count > 0)) return EINA_FALSE;
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
pd->points = pts;
|
|
|
|
|
|
|
|
if (cmds)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
memcpy(cmds, from_pd->commands,
|
|
|
|
sizeof (Efl_Gfx_Path_Command) * from_pd->commands_count);
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
if (pts)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
double *to_pts = to_pd->points;
|
|
|
|
double *from_pts = from_pd->points;
|
2018-06-24 22:25:52 -07:00
|
|
|
unsigned int i, j;
|
2018-01-16 21:20:15 -08:00
|
|
|
|
|
|
|
for (i = 0; cmds[i] != EFL_GFX_PATH_COMMAND_TYPE_END; i++)
|
|
|
|
for (j = 0; j < _efl_gfx_path_command_length(cmds[i]); j++)
|
|
|
|
{
|
|
|
|
*pts = interpolate(*from_pts, *to_pts, pos_map);
|
|
|
|
pts++;
|
|
|
|
from_pts++;
|
|
|
|
to_pts++;
|
|
|
|
}
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
|
|
|
|
pd->points_count = from_pd->points_count;
|
|
|
|
pd->commands_count = from_pd->commands_count;
|
2018-12-23 20:52:13 -08:00
|
|
|
pd->reserved_cmd_cnt = from_pd->commands_count;
|
|
|
|
pd->reserved_pts_cnt = from_pd->points_count;
|
2018-01-16 21:20:15 -08:00
|
|
|
|
2018-06-14 03:51:13 -07:00
|
|
|
interv = interpolate(from_pd->current.x, to_pd->current.x, pos_map);
|
|
|
|
pd->current.x = interv;
|
2018-01-16 21:20:15 -08:00
|
|
|
|
2018-06-14 03:51:13 -07:00
|
|
|
interv = interpolate(from_pd->current.y, to_pd->current.y, pos_map);
|
|
|
|
pd->current.y = interv;
|
2018-01-16 21:20:15 -08:00
|
|
|
|
2018-06-14 03:51:13 -07:00
|
|
|
interv = interpolate(from_pd->current_ctrl.x, to_pd->current_ctrl.x,
|
|
|
|
pos_map);
|
|
|
|
pd->current_ctrl.x = interv;
|
|
|
|
|
|
|
|
interv = interpolate(from_pd->current_ctrl.y, to_pd->current_ctrl.y,
|
|
|
|
pos_map);
|
|
|
|
pd->current_ctrl.y = interv;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-06-14 03:51:13 -07:00
|
|
|
}
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static Eina_Bool
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_equal_commands(Eo *obj EINA_UNUSED,
|
|
|
|
Efl_Gfx_Path_Data *pd,
|
|
|
|
const Eo *with)
|
|
|
|
{
|
|
|
|
Efl_Gfx_Path_Data *with_pd;
|
|
|
|
|
|
|
|
with_pd = efl_data_scope_get(with, EFL_GFX_PATH_MIXIN);
|
|
|
|
if (!with_pd) return EINA_FALSE;
|
|
|
|
|
|
|
|
return _efl_gfx_path_equal_commands_internal(with_pd, pd);
|
|
|
|
}
|
|
|
|
|
2018-12-23 20:52:13 -08:00
|
|
|
EOLIAN static void
|
|
|
|
_efl_gfx_path_reserve(Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd,
|
|
|
|
unsigned int cmd_count, unsigned int pts_count)
|
|
|
|
{
|
|
|
|
if (pd->reserved_cmd_cnt < cmd_count)
|
|
|
|
{
|
|
|
|
//+1 for path close.
|
|
|
|
pd->reserved_cmd_cnt = cmd_count + 1;
|
|
|
|
pd->commands = realloc(pd->commands, sizeof(Efl_Gfx_Path_Command) * pd->reserved_cmd_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pd->reserved_pts_cnt < pts_count)
|
|
|
|
{
|
|
|
|
pd->reserved_pts_cnt = pts_count;
|
|
|
|
pd->points = realloc(pd->points, sizeof(double) * pts_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
efl gfx_path: remove EFL_GFX_PATH_EVENT_CHANGED
Summary:
Here is an additional optmization patch for removing
unnecessary updation of path,
For instance, let's assume this scenario:
If one vg object has 20 path nodes(shapes),
and every single nodes would have 50 path changes.
(like, append_cubic, append_rect, append_xxx ...);
There would 1000 events triggering.
Furthermore, if there are 20 vector objects in one view,
hese events would be triggered 20000 in one frame.
It's insane, actually I could figured out that happens.
and it took a lot of cpu consumption in some vector usages.
efl_gfx_path_commit() is my idea to avoid this.
When path is ready, the path need to get this api call in the last
to make object changed properly.
@feature
Reviewers: #committers, cedric
Reviewed By: #committers, cedric
Subscribers: segfaultxavi, cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D7494
2018-12-27 19:20:15 -08:00
|
|
|
_efl_gfx_path_reset(Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
free(pd->commands);
|
2018-12-23 20:52:13 -08:00
|
|
|
pd->reserved_cmd_cnt = 0;
|
2016-11-23 00:29:38 -08:00
|
|
|
pd->commands = NULL;
|
|
|
|
pd->commands_count = 0;
|
|
|
|
|
|
|
|
free(pd->points);
|
2018-12-23 20:52:13 -08:00
|
|
|
pd->reserved_pts_cnt = 0;
|
2016-11-23 00:29:38 -08:00
|
|
|
pd->points = NULL;
|
|
|
|
pd->points_count = 0;
|
2018-12-23 20:52:13 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
free(pd->path_data);
|
|
|
|
pd->path_data = NULL;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
pd->current.x = 0;
|
|
|
|
pd->current.y = 0;
|
|
|
|
pd->current_ctrl.x = 0;
|
|
|
|
pd->current_ctrl.y = 0;
|
|
|
|
pd->convex = EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
efl gfx_path: remove EFL_GFX_PATH_EVENT_CHANGED
Summary:
Here is an additional optmization patch for removing
unnecessary updation of path,
For instance, let's assume this scenario:
If one vg object has 20 path nodes(shapes),
and every single nodes would have 50 path changes.
(like, append_cubic, append_rect, append_xxx ...);
There would 1000 events triggering.
Furthermore, if there are 20 vector objects in one view,
hese events would be triggered 20000 in one frame.
It's insane, actually I could figured out that happens.
and it took a lot of cpu consumption in some vector usages.
efl_gfx_path_commit() is my idea to avoid this.
When path is ready, the path need to get this api call in the last
to make object changed properly.
@feature
Reviewers: #committers, cedric
Reviewed By: #committers, cedric
Subscribers: segfaultxavi, cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D7494
2018-12-27 19:20:15 -08:00
|
|
|
_efl_gfx_path_append_move_to(Eo *obj EINA_UNUSED,
|
|
|
|
Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
double *offset_point;
|
|
|
|
|
|
|
|
if (!efl_gfx_path_grow(EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO, pd, &offset_point))
|
|
|
|
return;
|
|
|
|
|
|
|
|
offset_point[0] = x;
|
|
|
|
offset_point[1] = y;
|
|
|
|
|
|
|
|
pd->current.x = x;
|
|
|
|
pd->current.y = y;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
efl gfx_path: remove EFL_GFX_PATH_EVENT_CHANGED
Summary:
Here is an additional optmization patch for removing
unnecessary updation of path,
For instance, let's assume this scenario:
If one vg object has 20 path nodes(shapes),
and every single nodes would have 50 path changes.
(like, append_cubic, append_rect, append_xxx ...);
There would 1000 events triggering.
Furthermore, if there are 20 vector objects in one view,
hese events would be triggered 20000 in one frame.
It's insane, actually I could figured out that happens.
and it took a lot of cpu consumption in some vector usages.
efl_gfx_path_commit() is my idea to avoid this.
When path is ready, the path need to get this api call in the last
to make object changed properly.
@feature
Reviewers: #committers, cedric
Reviewed By: #committers, cedric
Subscribers: segfaultxavi, cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D7494
2018-12-27 19:20:15 -08:00
|
|
|
_efl_gfx_path_append_line_to(Eo *obj EINA_UNUSED,
|
|
|
|
Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
double *offset_point;
|
|
|
|
|
|
|
|
if (!efl_gfx_path_grow(EFL_GFX_PATH_COMMAND_TYPE_LINE_TO, pd, &offset_point))
|
|
|
|
return;
|
|
|
|
|
|
|
|
offset_point[0] = x;
|
|
|
|
offset_point[1] = y;
|
|
|
|
|
|
|
|
pd->current.x = x;
|
|
|
|
pd->current.y = y;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
efl gfx_path: remove EFL_GFX_PATH_EVENT_CHANGED
Summary:
Here is an additional optmization patch for removing
unnecessary updation of path,
For instance, let's assume this scenario:
If one vg object has 20 path nodes(shapes),
and every single nodes would have 50 path changes.
(like, append_cubic, append_rect, append_xxx ...);
There would 1000 events triggering.
Furthermore, if there are 20 vector objects in one view,
hese events would be triggered 20000 in one frame.
It's insane, actually I could figured out that happens.
and it took a lot of cpu consumption in some vector usages.
efl_gfx_path_commit() is my idea to avoid this.
When path is ready, the path need to get this api call in the last
to make object changed properly.
@feature
Reviewers: #committers, cedric
Reviewed By: #committers, cedric
Subscribers: segfaultxavi, cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D7494
2018-12-27 19:20:15 -08:00
|
|
|
_efl_gfx_path_append_cubic_to(Eo *obj EINA_UNUSED,
|
|
|
|
Efl_Gfx_Path_Data *pd,
|
|
|
|
double ctrl_x0, double ctrl_y0,
|
|
|
|
double ctrl_x1, double ctrl_y1,
|
|
|
|
double x, double y)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
double *offset_point;
|
|
|
|
|
|
|
|
if (!efl_gfx_path_grow(EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO,
|
|
|
|
pd, &offset_point))
|
|
|
|
return;
|
|
|
|
|
|
|
|
offset_point[0] = ctrl_x0;
|
|
|
|
offset_point[1] = ctrl_y0;
|
|
|
|
offset_point[2] = ctrl_x1;
|
|
|
|
offset_point[3] = ctrl_y1;
|
|
|
|
offset_point[4] = x;
|
|
|
|
offset_point[5] = y;
|
|
|
|
|
|
|
|
pd->current.x = x;
|
|
|
|
pd->current.y = y;
|
|
|
|
pd->current_ctrl.x = ctrl_x1;
|
|
|
|
pd->current_ctrl.y = ctrl_y1;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_scubic_to(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y,
|
|
|
|
double ctrl_x, double ctrl_y)
|
|
|
|
{
|
|
|
|
double ctrl_x0, ctrl_y0;
|
|
|
|
double current_x = 0, current_y = 0;
|
|
|
|
double current_ctrl_x = 0, current_ctrl_y = 0;
|
|
|
|
|
|
|
|
current_x = pd->current.x;
|
|
|
|
current_y = pd->current.y;
|
|
|
|
current_ctrl_x = pd->current_ctrl.x;
|
|
|
|
current_ctrl_y = pd->current_ctrl.y;
|
|
|
|
|
|
|
|
/* if previous command is cubic then use reflection point of current control
|
|
|
|
point as the first control point */
|
|
|
|
if ((pd->commands_count > 1) && (pd->commands[pd->commands_count-2] ==
|
|
|
|
EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO))
|
|
|
|
{
|
|
|
|
ctrl_x0 = 2 * current_x - current_ctrl_x;
|
|
|
|
ctrl_y0 = 2 * current_y - current_ctrl_y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// use currnt point as the 1st control point
|
|
|
|
ctrl_x0 = current_x;
|
|
|
|
ctrl_y0 = current_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
_efl_gfx_path_append_cubic_to(obj, pd, ctrl_x0, ctrl_y0, ctrl_x, ctrl_y,
|
|
|
|
x, y);
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_quadratic_to(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y,
|
|
|
|
double ctrl_x, double ctrl_y)
|
|
|
|
{
|
|
|
|
double current_x = 0, current_y = 0;
|
|
|
|
double ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1;
|
|
|
|
|
|
|
|
current_x = pd->current.x;
|
|
|
|
current_y = pd->current.y;
|
|
|
|
|
|
|
|
// Convert quadratic bezier to cubic
|
|
|
|
ctrl_x0 = (current_x + 2 * ctrl_x) * (1.0 / 3.0);
|
|
|
|
ctrl_y0 = (current_y + 2 * ctrl_y) * (1.0 / 3.0);
|
|
|
|
ctrl_x1 = (x + 2 * ctrl_x) * (1.0 / 3.0);
|
|
|
|
ctrl_y1 = (y + 2 * ctrl_y) * (1.0 / 3.0);
|
|
|
|
|
|
|
|
_efl_gfx_path_append_cubic_to(obj, pd, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1,
|
|
|
|
x, y);
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_squadratic_to(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y)
|
|
|
|
{
|
|
|
|
double xc, yc; /* quadratic control point */
|
|
|
|
double ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1;
|
|
|
|
double current_x = 0, current_y = 0;
|
|
|
|
double current_ctrl_x = 0, current_ctrl_y = 0;
|
|
|
|
|
|
|
|
current_x = pd->current.x;
|
|
|
|
current_y = pd->current.y;
|
|
|
|
current_ctrl_x = pd->current_ctrl.x;
|
|
|
|
current_ctrl_y = pd->current_ctrl.y;
|
|
|
|
|
|
|
|
xc = 2 * current_x - current_ctrl_x;
|
|
|
|
yc = 2 * current_y - current_ctrl_y;
|
|
|
|
|
|
|
|
/* generate a quadratic bezier with control point = xc, yc */
|
|
|
|
ctrl_x0 = (current_x + 2 * xc) * (1.0 / 3.0);
|
|
|
|
ctrl_y0 = (current_y + 2 * yc) * (1.0 / 3.0);
|
|
|
|
ctrl_x1 = (x + 2 * xc) * (1.0 / 3.0);
|
|
|
|
ctrl_y1 = (y + 2 * yc) * (1.0 / 3.0);
|
|
|
|
|
|
|
|
_efl_gfx_path_append_cubic_to(obj, pd, ctrl_x0, ctrl_y0,
|
|
|
|
ctrl_x1, ctrl_y1,
|
|
|
|
x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* code adapted from enesim which was adapted from moonlight sources
|
|
|
|
*/
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_arc_to(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y,
|
|
|
|
double rx, double ry,
|
|
|
|
double angle,
|
|
|
|
Eina_Bool large_arc, Eina_Bool sweep)
|
|
|
|
{
|
|
|
|
double cxp, cyp, cx, cy;
|
|
|
|
double sx, sy;
|
|
|
|
double cos_phi, sin_phi;
|
|
|
|
double dx2, dy2;
|
|
|
|
double x1p, y1p;
|
|
|
|
double x1p2, y1p2;
|
|
|
|
double rx2, ry2;
|
|
|
|
double lambda;
|
|
|
|
double c;
|
|
|
|
double at;
|
|
|
|
double theta1, delta_theta;
|
|
|
|
double nat;
|
|
|
|
double delta, bcp;
|
|
|
|
double cos_phi_rx, cos_phi_ry;
|
|
|
|
double sin_phi_rx, sin_phi_ry;
|
|
|
|
double cos_theta1, sin_theta1;
|
|
|
|
int segments, i;
|
|
|
|
|
|
|
|
// some helpful stuff is available here:
|
|
|
|
// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
|
|
|
sx = pd->current.x;
|
|
|
|
sy = pd->current.y;
|
|
|
|
|
|
|
|
// if start and end points are identical, then no arc is drawn
|
|
|
|
if ((fabs(x - sx) < (1 / 256.0)) && (fabs(y - sy) < (1 / 256.0)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Correction of out-of-range radii, see F6.6.1 (step 2)
|
|
|
|
rx = fabs(rx);
|
|
|
|
ry = fabs(ry);
|
|
|
|
if ((rx < 0.5) || (ry < 0.5))
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, x, y);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
angle = angle * M_PI / 180.0;
|
|
|
|
cos_phi = cos(angle);
|
|
|
|
sin_phi = sin(angle);
|
|
|
|
dx2 = (sx - x) / 2.0;
|
|
|
|
dy2 = (sy - y) / 2.0;
|
|
|
|
x1p = cos_phi * dx2 + sin_phi * dy2;
|
|
|
|
y1p = cos_phi * dy2 - sin_phi * dx2;
|
|
|
|
x1p2 = x1p * x1p;
|
|
|
|
y1p2 = y1p * y1p;
|
|
|
|
rx2 = rx * rx;
|
|
|
|
ry2 = ry * ry;
|
|
|
|
lambda = (x1p2 / rx2) + (y1p2 / ry2);
|
|
|
|
|
|
|
|
// Correction of out-of-range radii, see F6.6.2 (step 4)
|
|
|
|
if (lambda > 1.0)
|
|
|
|
{
|
|
|
|
// see F6.6.3
|
|
|
|
double lambda_root = sqrt(lambda);
|
|
|
|
|
|
|
|
rx *= lambda_root;
|
|
|
|
ry *= lambda_root;
|
|
|
|
// update rx2 and ry2
|
|
|
|
rx2 = rx * rx;
|
|
|
|
ry2 = ry * ry;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
|
|
|
|
|
|
|
|
// check if there is no possible solution
|
|
|
|
// (i.e. we can't do a square root of a negative value)
|
|
|
|
if (c < 0.0)
|
|
|
|
{
|
|
|
|
// scale uniformly until we have a single solution
|
|
|
|
// (see F6.2) i.e. when c == 0.0
|
|
|
|
double scale = sqrt(1.0 - c / (rx2 * ry2));
|
|
|
|
rx *= scale;
|
|
|
|
ry *= scale;
|
|
|
|
// update rx2 and ry2
|
|
|
|
rx2 = rx * rx;
|
|
|
|
ry2 = ry * ry;
|
|
|
|
|
|
|
|
// step 2 (F6.5.2) - simplified since c == 0.0
|
|
|
|
cxp = 0.0;
|
|
|
|
cyp = 0.0;
|
|
|
|
// step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
|
|
|
|
cx = 0.0;
|
|
|
|
cy = 0.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// complete c calculation
|
|
|
|
c = sqrt(c / ((rx2 * y1p2) + (ry2 * x1p2)));
|
|
|
|
// inverse sign if Fa == Fs
|
|
|
|
if (large_arc == sweep)
|
|
|
|
c = -c;
|
|
|
|
|
|
|
|
// step 2 (F6.5.2)
|
|
|
|
cxp = c * ( rx * y1p / ry);
|
|
|
|
cyp = c * (-ry * x1p / rx);
|
|
|
|
|
|
|
|
// step 3 (F6.5.3 first part)
|
|
|
|
cx = cos_phi * cxp - sin_phi * cyp;
|
|
|
|
cy = sin_phi * cxp + cos_phi * cyp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// step 3 (F6.5.3 second part) we now have the center point of the ellipse
|
|
|
|
cx += (sx + x) / 2.0;
|
|
|
|
cy += (sy + y) / 2.0;
|
|
|
|
|
|
|
|
// step 4 (F6.5.4)
|
2020-06-22 11:24:38 -07:00
|
|
|
// we don't use arccos (as per w3c doc), see
|
2016-11-23 00:29:38 -08:00
|
|
|
// http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
|
|
|
|
// note: atan2 (0.0, 1.0) == 0.0
|
|
|
|
at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
|
|
|
|
theta1 = (at < 0.0) ? 2.0 * M_PI + at : at;
|
|
|
|
|
|
|
|
nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
|
|
|
|
delta_theta = (nat < at) ? 2.0 * M_PI - at + nat : nat - at;
|
|
|
|
|
|
|
|
if (sweep)
|
|
|
|
{
|
|
|
|
// ensure delta theta < 0 or else add 360 degrees
|
|
|
|
if (delta_theta < 0.0)
|
|
|
|
delta_theta += 2.0 * M_PI;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ensure delta theta > 0 or else substract 360 degrees
|
|
|
|
if (delta_theta > 0.0)
|
|
|
|
delta_theta -= 2.0 * M_PI;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add several cubic bezier to approximate the arc
|
|
|
|
// (smaller than 90 degrees)
|
|
|
|
// we add one extra segment because we want something
|
|
|
|
// smaller than 90deg (i.e. not 90 itself)
|
|
|
|
segments = (int) (fabs(delta_theta / M_PI_2)) + 1;
|
|
|
|
delta = delta_theta / segments;
|
|
|
|
|
|
|
|
// http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
|
|
|
|
bcp = 4.0 / 3 * (1 - cos(delta / 2)) / sin(delta / 2);
|
|
|
|
|
|
|
|
cos_phi_rx = cos_phi * rx;
|
|
|
|
cos_phi_ry = cos_phi * ry;
|
|
|
|
sin_phi_rx = sin_phi * rx;
|
|
|
|
sin_phi_ry = sin_phi * ry;
|
|
|
|
|
|
|
|
cos_theta1 = cos(theta1);
|
|
|
|
sin_theta1 = sin(theta1);
|
|
|
|
|
|
|
|
for (i = 0; i < segments; ++i)
|
|
|
|
{
|
|
|
|
// end angle (for this segment) = current + delta
|
|
|
|
double c1x, c1y, ex, ey, c2x, c2y;
|
|
|
|
double theta2 = theta1 + delta;
|
|
|
|
double cos_theta2 = cos(theta2);
|
|
|
|
double sin_theta2 = sin(theta2);
|
|
|
|
|
|
|
|
// first control point (based on start point sx,sy)
|
|
|
|
c1x = sx - bcp * (cos_phi_rx * sin_theta1 + sin_phi_ry * cos_theta1);
|
|
|
|
c1y = sy + bcp * (cos_phi_ry * cos_theta1 - sin_phi_rx * sin_theta1);
|
|
|
|
|
|
|
|
// end point (for this segment)
|
|
|
|
ex = cx + (cos_phi_rx * cos_theta2 - sin_phi_ry * sin_theta2);
|
|
|
|
ey = cy + (sin_phi_rx * cos_theta2 + cos_phi_ry * sin_theta2);
|
|
|
|
|
|
|
|
// second control point (based on end point ex,ey)
|
|
|
|
c2x = ex + bcp * (cos_phi_rx * sin_theta2 + sin_phi_ry * cos_theta2);
|
|
|
|
c2y = ey + bcp * (sin_phi_rx * sin_theta2 - cos_phi_ry * cos_theta2);
|
|
|
|
|
|
|
|
_efl_gfx_path_append_cubic_to(obj, pd, c1x, c1y, c2x, c2y, ex, ey);
|
|
|
|
|
|
|
|
// next start point is the current end point (same for angle)
|
|
|
|
sx = ex;
|
|
|
|
sy = ey;
|
|
|
|
theta1 = theta2;
|
|
|
|
// avoid recomputations
|
|
|
|
cos_theta1 = cos_theta2;
|
|
|
|
sin_theta1 = sin_theta2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// append arc implementation
|
|
|
|
typedef struct _Point
|
|
|
|
{
|
|
|
|
double x;
|
|
|
|
double y;
|
|
|
|
} Point;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PATH_KAPPA 0.5522847498
|
|
|
|
|
|
|
|
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;
|
2017-01-06 09:57:46 -08:00
|
|
|
if (EINA_FLT_EQ(angle, 90.0)) return 1;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2016-11-23 22:56:52 -08:00
|
|
|
radians = (angle/180) * M_PI;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
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];
|
|
|
|
|
2017-04-13 18:16:48 -07:00
|
|
|
if (EINA_FLT_EQ(w, 0.0) || EINA_FLT_EQ(h, 0.0))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
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
|
2017-01-06 09:57:46 -08:00
|
|
|
if (EINA_FLT_EQ(start_angle, 0))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2017-01-06 09:57:46 -08:00
|
|
|
if (EINA_FLT_EQ(sweep_length, 360))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
for (i = 11; i >= 0; --i)
|
|
|
|
curves[(*point_count)++] = points[i];
|
|
|
|
return points[12];
|
|
|
|
}
|
2017-01-06 09:57:46 -08:00
|
|
|
else if (EINA_FLT_EQ(sweep_length, -360))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
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
|
2017-01-06 09:57:46 -08:00
|
|
|
if (EINA_FLT_EQ(start_t, 1.0))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
start_t = 0;
|
|
|
|
start_segment += delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
// avoid empty end segment
|
2017-01-06 09:57:46 -08:00
|
|
|
if (EINA_FLT_EQ(end_t, 0.0))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
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?
|
2017-01-06 09:57:46 -08:00
|
|
|
if (start_segment == end_segment && (EINA_FLT_EQ(start_t, end_t)))
|
2016-11-23 00:29:38 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_arc(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y, double w, double h,
|
|
|
|
double start_angle, double sweep_length)
|
|
|
|
{
|
|
|
|
int i, point_count;
|
|
|
|
Point pts[15];
|
|
|
|
Point curve_start =
|
|
|
|
_curves_for_arc(x, y, w, h, start_angle, sweep_length, pts, &point_count);
|
|
|
|
|
|
|
|
if (pd->commands_count &&
|
|
|
|
(pd->commands[pd->commands_count-2] != EFL_GFX_PATH_COMMAND_TYPE_CLOSE))
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, curve_start.x, curve_start.y);
|
|
|
|
else
|
|
|
|
_efl_gfx_path_append_move_to(obj, pd, curve_start.x, curve_start.y);
|
|
|
|
|
|
|
|
for (i = 0; i < point_count; i += 3)
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_cubic_to(obj, pd, pts[i].x, pts[i].y,
|
|
|
|
pts[i+1].x, pts[i+1].y,
|
|
|
|
pts[i+2].x, pts[i+2].y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
efl gfx_path: remove EFL_GFX_PATH_EVENT_CHANGED
Summary:
Here is an additional optmization patch for removing
unnecessary updation of path,
For instance, let's assume this scenario:
If one vg object has 20 path nodes(shapes),
and every single nodes would have 50 path changes.
(like, append_cubic, append_rect, append_xxx ...);
There would 1000 events triggering.
Furthermore, if there are 20 vector objects in one view,
hese events would be triggered 20000 in one frame.
It's insane, actually I could figured out that happens.
and it took a lot of cpu consumption in some vector usages.
efl_gfx_path_commit() is my idea to avoid this.
When path is ready, the path need to get this api call in the last
to make object changed properly.
@feature
Reviewers: #committers, cedric
Reviewed By: #committers, cedric
Subscribers: segfaultxavi, cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D7494
2018-12-27 19:20:15 -08:00
|
|
|
_efl_gfx_path_append_close(Eo *obj EINA_UNUSED, Efl_Gfx_Path_Data *pd)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
double *offset_point;
|
|
|
|
efl_gfx_path_grow(EFL_GFX_PATH_COMMAND_TYPE_CLOSE, pd, &offset_point);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_gfx_path_append_circle(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double xc, double yc, double radius)
|
|
|
|
{
|
|
|
|
Eina_Bool first = (pd->commands_count <= 0);
|
|
|
|
|
|
|
|
_efl_gfx_path_append_arc(obj, pd, (xc - radius), (yc - radius),
|
|
|
|
(2 * radius), (2 * radius), 0, 360);
|
|
|
|
_efl_gfx_path_append_close(obj, pd);
|
|
|
|
|
|
|
|
//update convex flag
|
|
|
|
pd->convex = first;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_rect(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
double x, double y, double w, double h,
|
|
|
|
double rx, double ry)
|
|
|
|
{
|
|
|
|
Eina_Bool first = (pd->commands_count <= 0);
|
|
|
|
|
|
|
|
// check for invalid rectangle
|
|
|
|
if (w <=0 || h<= 0) return;
|
|
|
|
|
|
|
|
if (rx <=0 || ry<=0)
|
|
|
|
{
|
|
|
|
// add a normal rect.
|
|
|
|
_efl_gfx_path_append_move_to(obj, pd, x, y);
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, x, y + h);
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, x + w, y + h);
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, x + w, y);
|
|
|
|
_efl_gfx_path_append_close(obj, pd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clamp the rx and ry radius value.
|
2020-06-24 21:13:31 -07:00
|
|
|
rx = 2*rx;
|
|
|
|
ry = 2*ry;
|
2016-11-23 00:29:38 -08:00
|
|
|
if (rx > w) rx = w;
|
|
|
|
if (ry > h) ry = h;
|
|
|
|
|
2020-06-24 21:13:31 -07:00
|
|
|
_efl_gfx_path_append_move_to(obj, pd, x, y + h/2);
|
|
|
|
_efl_gfx_path_append_arc(obj, pd, x, y + h - ry, rx, ry, 180, 90);
|
|
|
|
_efl_gfx_path_append_arc(obj, pd, x + w - rx, y + h - ry, rx, ry, 270, 90);
|
|
|
|
_efl_gfx_path_append_arc(obj, pd, x + w - rx, y, rx, ry, 0, 90);
|
|
|
|
_efl_gfx_path_append_arc(obj, pd, x, y, rx, ry, 90, 90);
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_close(obj, pd);
|
|
|
|
|
|
|
|
//update convex flag
|
|
|
|
pd->convex = first;
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_horizontal_to(Eo *obj, Efl_Gfx_Path_Data *pd, double d,
|
|
|
|
double current_x EINA_UNUSED,
|
|
|
|
double current_y)
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, d, current_y);
|
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_vertical_to(Eo *obj, Efl_Gfx_Path_Data *pd, double d,
|
|
|
|
double current_x,
|
|
|
|
double current_y EINA_UNUSED)
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, current_x, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
_skipcomma(const char *content)
|
|
|
|
{
|
|
|
|
while (*content && isspace(*content)) content++;
|
|
|
|
if (*content == ',') return (char*) content + 1;
|
|
|
|
return (char*) content;
|
|
|
|
}
|
|
|
|
|
2018-01-18 00:24:22 -08:00
|
|
|
#if 0
|
2016-11-23 00:29:38 -08:00
|
|
|
static inline Eina_Bool
|
|
|
|
_next_isnumber(const char *content)
|
|
|
|
{
|
|
|
|
char *tmp = NULL;
|
|
|
|
|
|
|
|
(void) strtod(content, &tmp);
|
|
|
|
return content != tmp;
|
|
|
|
}
|
2018-01-18 00:24:22 -08:00
|
|
|
#endif
|
2016-11-23 00:29:38 -08:00
|
|
|
|
|
|
|
static inline Eina_Bool
|
|
|
|
_parse_number(char **content, double *number)
|
|
|
|
{
|
|
|
|
char *end = NULL;
|
|
|
|
*number = strtod(*content, &end);
|
2018-01-16 21:20:15 -08:00
|
|
|
// if the start of string is not number
|
2016-11-23 00:29:38 -08:00
|
|
|
if ((*content) == end) return EINA_FALSE;
|
|
|
|
//skip comma if any
|
|
|
|
*content = _skipcomma(end);
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
static inline Eina_Bool
|
|
|
|
_parse_long(char **content, int *number)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
char *end = NULL;
|
|
|
|
*number = strtol(*content, &end, 10) ? 1 : 0;
|
|
|
|
// if the start of string is not number
|
|
|
|
if ((*content) == end) return EINA_FALSE;
|
|
|
|
*content = _skipcomma(end);
|
|
|
|
return EINA_TRUE;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
static int
|
|
|
|
_number_count(char cmd)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
int count = 0;
|
|
|
|
switch (cmd)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
case 'M':
|
|
|
|
case 'm':
|
|
|
|
case 'L':
|
|
|
|
case 'l':
|
|
|
|
{
|
|
|
|
count = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'C':
|
|
|
|
case 'c':
|
|
|
|
case 'E':
|
|
|
|
case 'e':
|
|
|
|
{
|
|
|
|
count = 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'H':
|
|
|
|
case 'h':
|
|
|
|
case 'V':
|
|
|
|
case 'v':
|
|
|
|
{
|
|
|
|
count = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'S':
|
|
|
|
case 's':
|
|
|
|
case 'Q':
|
|
|
|
case 'q':
|
|
|
|
case 'T':
|
|
|
|
case 't':
|
|
|
|
{
|
|
|
|
count = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'A':
|
|
|
|
case 'a':
|
|
|
|
{
|
|
|
|
count = 7;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return count;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
static void
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
process_command(Eo *obj, Efl_Gfx_Path_Data *pd, char cmd, double *arr, int count, double *cur_x, double *cur_y, double *start_x, double *start_y)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
int i;
|
|
|
|
switch (cmd)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
case 'm':
|
|
|
|
case 'l':
|
|
|
|
case 'c':
|
|
|
|
case 's':
|
|
|
|
case 'q':
|
|
|
|
case 't':
|
|
|
|
{
|
2018-09-17 19:48:13 -07:00
|
|
|
for(i = 0; i < count - 1; i += 2)
|
2018-01-16 21:20:15 -08:00
|
|
|
{
|
|
|
|
arr[i] = arr[i] + *cur_x;
|
|
|
|
arr[i+1] = arr[i+1] + *cur_y;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'h':
|
|
|
|
{
|
|
|
|
arr[0] = arr[0] + *cur_x;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'v':
|
|
|
|
{
|
|
|
|
arr[0] = arr[0] + *cur_y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'a':
|
|
|
|
{
|
|
|
|
arr[5] = arr[5] + *cur_x;
|
|
|
|
arr[6] = arr[6] + *cur_y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case 'm':
|
|
|
|
case 'M':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_move_to(obj, pd, arr[0], arr[1]);
|
|
|
|
*cur_x = arr[0];
|
|
|
|
*cur_y = arr[1];
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
*start_x = arr[0];
|
|
|
|
*start_y = arr[1];
|
2018-01-16 21:20:15 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'l':
|
|
|
|
case 'L':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_line_to(obj, pd, arr[0], arr[1]);
|
|
|
|
*cur_x = arr[0];
|
|
|
|
*cur_y = arr[1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'c':
|
|
|
|
case 'C':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_cubic_to(obj, pd, arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);
|
|
|
|
*cur_x = arr[4];
|
|
|
|
*cur_y = arr[5];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 's':
|
|
|
|
case 'S':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_scubic_to(obj, pd, arr[2], arr[3], arr[0], arr[1]);
|
|
|
|
*cur_x = arr[2];
|
|
|
|
*cur_y = arr[3];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'q':
|
|
|
|
case 'Q':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_quadratic_to(obj, pd, arr[2], arr[3], arr[0], arr[1]);
|
|
|
|
*cur_x = arr[2];
|
|
|
|
*cur_y = arr[3];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 't':
|
|
|
|
case 'T':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_move_to(obj, pd, arr[0], arr[1]);
|
|
|
|
*cur_x = arr[0];
|
|
|
|
*cur_y = arr[1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'h':
|
|
|
|
case 'H':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_horizontal_to(obj, pd, arr[0], *cur_x, *cur_y);
|
|
|
|
*cur_x = arr[0];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'v':
|
|
|
|
case 'V':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_vertical_to(obj, pd, arr[0], *cur_x, *cur_y);
|
|
|
|
*cur_y = arr[0];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'z':
|
|
|
|
case 'Z':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_close(obj, pd);
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
*cur_x = *start_x;
|
|
|
|
*cur_y = *start_y;
|
2018-01-16 21:20:15 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'a':
|
|
|
|
case 'A':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_arc_to(obj, pd, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]);
|
|
|
|
*cur_x = arr[5];
|
|
|
|
*cur_y = arr[6];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'E':
|
|
|
|
case 'e':
|
|
|
|
{
|
|
|
|
_efl_gfx_path_append_arc(obj, pd, arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
static char *
|
|
|
|
_next_command(char *path, char *cmd, double *arr, int *count)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
int i=0, large, sweep;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
path = _skipcomma(path);
|
|
|
|
if (isalpha(*path))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
*cmd = *path;
|
|
|
|
path++;
|
|
|
|
*count = _number_count(*cmd);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2020-06-20 02:47:56 -07:00
|
|
|
else
|
2019-09-23 18:20:43 -07:00
|
|
|
{
|
|
|
|
if (*cmd == 'm')
|
2019-09-23 19:52:30 -07:00
|
|
|
*cmd = 'l';
|
|
|
|
else if (*cmd == 'M')
|
|
|
|
*cmd = 'L';
|
2019-09-23 18:20:43 -07:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
if ( *count == 7)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
// special case for arc command
|
|
|
|
if(_parse_number(&path, &arr[0]))
|
|
|
|
if(_parse_number(&path, &arr[1]))
|
|
|
|
if(_parse_number(&path, &arr[2]))
|
|
|
|
if(_parse_long(&path, &large))
|
|
|
|
if(_parse_long(&path, &sweep))
|
|
|
|
if(_parse_number(&path, &arr[5]))
|
|
|
|
if(_parse_number(&path, &arr[6]))
|
|
|
|
{
|
|
|
|
arr[3] = large;
|
|
|
|
arr[4] = sweep;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
*count = 0;
|
|
|
|
return NULL;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
for (i = 0; i < *count; i++)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
if (!_parse_number(&path, &arr[i]))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
*count = 0;
|
|
|
|
return NULL;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
path = _skipcomma(path);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
return path;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
static void
|
|
|
|
_path_interpolation(Eo *obj, Efl_Gfx_Path_Data *pd,
|
|
|
|
char *from, char *to, double pos)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
int i;
|
|
|
|
double from_arr[7], to_arr[7];
|
|
|
|
int from_count=0, to_count=0;
|
|
|
|
double cur_x=0, cur_y=0;
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
double start_x=0, start_y=0;
|
2018-01-16 21:20:15 -08:00
|
|
|
char from_cmd= 0, to_cmd = 0;
|
|
|
|
char *cur_locale;
|
|
|
|
|
|
|
|
if (!from || !to)
|
|
|
|
return;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
cur_locale = setlocale(LC_NUMERIC, NULL);
|
|
|
|
if (cur_locale)
|
|
|
|
cur_locale = strdup(cur_locale);
|
|
|
|
setlocale(LC_NUMERIC, "POSIX");
|
|
|
|
|
|
|
|
while ((from[0] != '\0') && (to[0] != '\0'))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
from = _next_command(from, &from_cmd, from_arr, &from_count);
|
|
|
|
to = _next_command(to, &to_cmd, to_arr, &to_count);
|
|
|
|
if (from_cmd == to_cmd)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
if (from_count == 7)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
//special case for arc command
|
|
|
|
i=0;
|
|
|
|
from_arr[i] = interpolate(from_arr[i], to_arr[i], pos);
|
|
|
|
i=1;
|
|
|
|
from_arr[i] = interpolate(from_arr[i], to_arr[i], pos);
|
|
|
|
i=2;
|
|
|
|
from_arr[i] = interpolate(from_arr[i], to_arr[i], pos);
|
|
|
|
i=5;
|
|
|
|
from_arr[i] = interpolate(from_arr[i], to_arr[i], pos);
|
|
|
|
i=6;
|
|
|
|
from_arr[i] = interpolate(from_arr[i], to_arr[i], pos);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
for(i=0; i < from_count; i++)
|
|
|
|
{
|
|
|
|
from_arr[i] = interpolate(from_arr[i], to_arr[i], pos);
|
|
|
|
}
|
|
|
|
}
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
process_command(obj, pd, from_cmd, from_arr, from_count, &cur_x, &cur_y, &start_x, &start_y);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
else
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
goto error;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
error:
|
|
|
|
setlocale(LC_NUMERIC, cur_locale);
|
|
|
|
if (cur_locale)
|
|
|
|
free(cur_locale);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2017-11-29 22:58:04 -08:00
|
|
|
EOLIAN static void
|
2016-11-23 00:29:38 -08:00
|
|
|
_efl_gfx_path_append_svg_path(Eo *obj, Efl_Gfx_Path_Data *pd,
|
2018-01-16 21:20:15 -08:00
|
|
|
const char *svg_path_data)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
double number_array[7];
|
|
|
|
int number_count = 0;
|
|
|
|
double cur_x=0, cur_y=0;
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
double start_x=0, start_y=0;
|
2018-01-16 21:20:15 -08:00
|
|
|
char cmd= 0;
|
|
|
|
char *path = (char *) svg_path_data;
|
|
|
|
Eina_Bool arc = EINA_FALSE;
|
|
|
|
char *cur_locale;
|
|
|
|
|
|
|
|
if (!path)
|
|
|
|
return;
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
cur_locale = setlocale(LC_NUMERIC, NULL);
|
|
|
|
if (cur_locale)
|
|
|
|
cur_locale = strdup(cur_locale);
|
|
|
|
setlocale(LC_NUMERIC, "POSIX");
|
2016-11-23 00:29:38 -08:00
|
|
|
|
2018-01-16 21:20:15 -08:00
|
|
|
while ((path[0] != '\0'))
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
path = _next_command(path, &cmd, number_array, &number_count);
|
|
|
|
if (!path)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
2018-01-16 21:20:15 -08:00
|
|
|
//printf("Error parsing command\n");
|
|
|
|
goto error;
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
efl_gfx_path: When path end(Z,z), Current point are returned to starting point(M,m)
Summary:
When path ends with 'z' or 'Z' command, if 'm' comes as next command,
the current point is incorrectly referenced.
Since 'Z', 'z' means to close the path,
so, Save point and reuse it when 'M' or 'm' is called.
Test Plan:
[Error Case]
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path d="M 52.17,20
H 11.92
V 43
H 52.17
Z
m -1.5,21.5
H 13.42
v -20
H 50.67
Z
" fill="#a8b7c1" stroke="#F00" stroke-width="0.5" />
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: cedric, herb, kimcinoo, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12158
2020-09-21 22:52:42 -07:00
|
|
|
process_command(obj, pd, cmd, number_array, number_count, &cur_x, &cur_y, &start_x, &start_y);
|
2018-01-16 21:20:15 -08:00
|
|
|
if ((!arc) && ((cmd == 'a') || (cmd == 'A') ||
|
|
|
|
(cmd == 'e') || (cmd == 'E')))
|
|
|
|
arc = EINA_TRUE;
|
|
|
|
}
|
|
|
|
if (arc)
|
|
|
|
{
|
|
|
|
// need to keep the path for interpolation
|
|
|
|
if (pd->path_data)
|
|
|
|
free(pd->path_data);
|
|
|
|
pd->path_data = malloc(strlen(svg_path_data) + 1);
|
2020-02-13 20:15:11 -08:00
|
|
|
if (!pd->path_data) goto error;
|
2018-01-16 21:20:15 -08:00
|
|
|
strcpy(pd->path_data, svg_path_data);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
2018-01-16 21:20:15 -08:00
|
|
|
|
|
|
|
error:
|
|
|
|
setlocale(LC_NUMERIC, cur_locale);
|
|
|
|
if (cur_locale)
|
|
|
|
free(cur_locale);
|
2016-11-23 00:29:38 -08:00
|
|
|
}
|
|
|
|
|
2017-11-29 03:03:16 -08:00
|
|
|
EOLIAN static void
|
|
|
|
_efl_gfx_path_copy_from(Eo *obj, Efl_Gfx_Path_Data *pd, const Eo *dup_from)
|
2016-11-23 00:29:38 -08:00
|
|
|
{
|
|
|
|
Efl_Gfx_Path_Data *from;
|
|
|
|
|
|
|
|
if (obj == dup_from) return;
|
|
|
|
from = efl_data_scope_get(dup_from, EFL_GFX_PATH_MIXIN);
|
|
|
|
if (!from) return;
|
|
|
|
|
|
|
|
pd->convex = from->convex;
|
|
|
|
|
|
|
|
_efl_gfx_path_path_set(obj, pd, from->commands, from->points);
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "interfaces/efl_gfx_path.eo.c"
|