diff options
Diffstat (limited to 'src/sp-ellipse.cpp')
| -rw-r--r-- | src/sp-ellipse.cpp | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp new file mode 100644 index 000000000..b2b0f7b05 --- /dev/null +++ b/src/sp-ellipse.cpp @@ -0,0 +1,863 @@ +#define __SP_ELLIPSE_C__ + +/* + * SVG <ellipse> and related implementations + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Mitsuru Oka + * bulia byak <buliabyak@users.sf.net> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include "libnr/n-art-bpath.h" +#include "libnr/nr-path.h" +#include "libnr/nr-matrix-fns.h" +#include "svg/svg.h" +#include "svg/stringstream.h" +#include "xml/repr.h" +#include "attributes.h" +#include "style.h" +#include "display/curve.h" +#include <glibmm/i18n.h> + +#include "sp-ellipse.h" + +#include "prefs-utils.h" + +/* Common parent class */ + +#define noELLIPSE_VERBOSE + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define SP_2PI (2 * M_PI) + +#if 1 +/* Hmmm... shouldn't this also qualify */ +/* Whether it is faster or not, well, nobody knows */ +#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m))) +#else +/* we do not use C99 round(3) function yet */ +static double sp_round(double x, double y) +{ + double remain; + + g_assert(y > 0.0); + + /* return round(x/y) * y; */ + + remain = fmod(x, y); + + if (remain >= 0.5*y) + return x - remain + y; + else + return x - remain; +} +#endif + +static void sp_genericellipse_class_init(SPGenericEllipseClass *klass); +static void sp_genericellipse_init(SPGenericEllipse *ellipse); + +static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags); + +static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p); + +static void sp_genericellipse_set_shape(SPShape *shape); +static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, + guint flags); + +static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr); + +static SPShapeClass *ge_parent_class; + +GType +sp_genericellipse_get_type(void) +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPGenericEllipseClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_genericellipse_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(SPGenericEllipse), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_genericellipse_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0); + } + return type; +} + +static void sp_genericellipse_class_init(SPGenericEllipseClass *klass) +{ + SPObjectClass *sp_object_class = (SPObjectClass *) klass; + SPItemClass *item_class = (SPItemClass *) klass; + SPShapeClass *shape_class = (SPShapeClass *) klass; + + ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE); + + sp_object_class->update = sp_genericellipse_update; + sp_object_class->write = sp_genericellipse_write; + + item_class->snappoints = sp_genericellipse_snappoints; + + shape_class->set_shape = sp_genericellipse_set_shape; +} + +static void +sp_genericellipse_init(SPGenericEllipse *ellipse) +{ + ellipse->cx.unset(); + ellipse->cy.unset(); + ellipse->rx.unset(); + ellipse->ry.unset(); + + ellipse->start = 0.0; + ellipse->end = SP_2PI; + ellipse->closed = TRUE; +} + +static void +sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags) +{ + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + SPGenericEllipse *ellipse = (SPGenericEllipse *) object; + SPStyle const *style = object->style; + double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp); + double const em = style->font_size.computed; + double const ex = em * 0.5; // fixme: get from pango or libnrtype + ellipse->cx.update(em, ex, d); + ellipse->cy.update(em, ex, d); + ellipse->rx.update(em, ex, d); + ellipse->ry.update(em, ex, d); + sp_shape_set_shape((SPShape *) object); + } + + if (((SPObjectClass *) ge_parent_class)->update) + ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags); +} + +#define C1 0.552 + +/* fixme: Think (Lauris) */ + +static void sp_genericellipse_set_shape(SPShape *shape) +{ + double cx, cy, rx, ry, s, e; + double x0, y0, x1, y1, x2, y2, x3, y3; + double len; + gint slice = FALSE; + gint i; + + SPGenericEllipse *ellipse = (SPGenericEllipse *) shape; + + if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return; + if (fabs(ellipse->end - ellipse->start) < 1e-9) return; + + sp_genericellipse_normalize(ellipse); + + cx = 0.0; + cy = 0.0; + rx = ellipse->rx.computed; + ry = ellipse->ry.computed; + + // figure out if we have a slice, guarding against rounding errors + len = fmod(ellipse->end - ellipse->start, SP_2PI); + if (len < 0.0) len += SP_2PI; + if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) { + slice = FALSE; + ellipse->end = ellipse->start + SP_2PI; + } else { + slice = TRUE; + } + + NR::Matrix aff = NR::Matrix(NR::scale(rx, ry)); + aff[4] = ellipse->cx.computed; + aff[5] = ellipse->cy.computed; + + NArtBpath bpath[16]; + i = 0; + if (ellipse->closed) { + bpath[i].code = NR_MOVETO; + } else { + bpath[i].code = NR_MOVETO_OPEN; + } + bpath[i].x3 = cos(ellipse->start); + bpath[i].y3 = sin(ellipse->start); + i++; + + for (s = ellipse->start; s < ellipse->end; s += M_PI_2) { + e = s + M_PI_2; + if (e > ellipse->end) + e = ellipse->end; + len = C1 * (e - s) / M_PI_2; + x0 = cos(s); + y0 = sin(s); + x1 = x0 + len * cos(s + M_PI_2); + y1 = y0 + len * sin(s + M_PI_2); + x3 = cos(e); + y3 = sin(e); + x2 = x3 + len * cos(e - M_PI_2); + y2 = y3 + len * sin(e - M_PI_2); +#ifdef ELLIPSE_VERBOSE + g_print("step %d s %f e %f coords %f %f %f %f %f %f\n", + i, s, e, x1, y1, x2, y2, x3, y3); +#endif + bpath[i].code = NR_CURVETO; + bpath[i].x1 = x1; + bpath[i].y1 = y1; + bpath[i].x2 = x2; + bpath[i].y2 = y2; + bpath[i].x3 = x3; + bpath[i].y3 = y3; + i++; + } + + if (slice && ellipse->closed) { + bpath[i].code = NR_LINETO; + bpath[i].x3 = 0.0; + bpath[i].y3 = 0.0; + i++; + bpath[i].code = NR_LINETO; + bpath[i].x3 = bpath[0].x3; + bpath[i].y3 = bpath[0].y3; + i++; + } else if (ellipse->closed) { + bpath[i-1].x3 = bpath[0].x3; + bpath[i-1].y3 = bpath[0].y3; + } + + bpath[i].code = NR_END; + SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff)); + g_assert(c != NULL); + + sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE); + sp_curve_unref(c); +} + +static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p) +{ + SPGenericEllipse const *ge = SP_GENERICELLIPSE(item); + + NR::Matrix const i2d = sp_item_i2d_affine(item); + + /* Add the centre */ + *p = NR::Point(ge->cx.computed, ge->cy.computed) * i2d; + + // TODO: add the ends of radii +} + +void +sp_genericellipse_normalize(SPGenericEllipse *ellipse) +{ + ellipse->start = fmod(ellipse->start, SP_2PI); + ellipse->end = fmod(ellipse->end, SP_2PI); + + if (ellipse->start < 0.0) + ellipse->start += SP_2PI; + double diff = ellipse->start - ellipse->end; + if (diff >= 0.0) + ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI; + + /* Now we keep: 0 <= start < end <= 2*PI */ +} + +static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) +{ + SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object); + + if (flags & SP_OBJECT_WRITE_EXT) { + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { + repr = sp_repr_new("svg:path"); + } + + sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed); + sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed); + sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed); + sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed); + + if (SP_IS_ARC(ellipse)) + sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object)); + } + + if (((SPObjectClass *) ge_parent_class)->write) + ((SPObjectClass *) ge_parent_class)->write(object, repr, flags); + + return repr; +} + +/* SVG <ellipse> element */ + +static void sp_ellipse_class_init(SPEllipseClass *klass); +static void sp_ellipse_init(SPEllipse *ellipse); + +static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); +static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags); +static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value); +static gchar *sp_ellipse_description(SPItem *item); + +static SPGenericEllipseClass *ellipse_parent_class; + +GType +sp_ellipse_get_type(void) +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPEllipseClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_ellipse_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(SPEllipse), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_ellipse_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0); + } + return type; +} + +static void sp_ellipse_class_init(SPEllipseClass *klass) +{ + SPObjectClass *sp_object_class = (SPObjectClass *) klass; + SPItemClass *item_class = (SPItemClass *) klass; + + ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE); + + sp_object_class->build = sp_ellipse_build; + sp_object_class->write = sp_ellipse_write; + sp_object_class->set = sp_ellipse_set; + + item_class->description = sp_ellipse_description; +} + +static void +sp_ellipse_init(SPEllipse *ellipse) +{ + /* Nothing special */ +} + +static void +sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +{ + if (((SPObjectClass *) ellipse_parent_class)->build) + (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr); + + sp_object_read_attr(object, "cx"); + sp_object_read_attr(object, "cy"); + sp_object_read_attr(object, "rx"); + sp_object_read_attr(object, "ry"); +} + +static Inkscape::XML::Node * +sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) +{ + SPGenericEllipse *ellipse; + + ellipse = SP_GENERICELLIPSE(object); + + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { + repr = sp_repr_new("svg:ellipse"); + } + + sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed); + sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed); + sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed); + sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed); + + if (((SPObjectClass *) ellipse_parent_class)->write) + (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags); + + return repr; +} + +static void +sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value) +{ + SPGenericEllipse *ellipse; + + ellipse = SP_GENERICELLIPSE(object); + + switch (key) { + case SP_ATTR_CX: + ellipse->cx.readOrUnset(value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_CY: + ellipse->cy.readOrUnset(value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_RX: + if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) { + ellipse->rx.unset(); + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_RY: + if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) { + ellipse->ry.unset(); + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + default: + if (((SPObjectClass *) ellipse_parent_class)->set) + ((SPObjectClass *) ellipse_parent_class)->set(object, key, value); + break; + } +} + +static gchar *sp_ellipse_description(SPItem *item) +{ + return g_strdup(_("<b>Ellipse</b>")); +} + + +void +sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry) +{ + SPGenericEllipse *ge; + + g_return_if_fail(ellipse != NULL); + g_return_if_fail(SP_IS_ELLIPSE(ellipse)); + + ge = SP_GENERICELLIPSE(ellipse); + + ge->cx.computed = x; + ge->cy.computed = y; + ge->rx.computed = rx; + ge->ry.computed = ry; + + ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +/* SVG <circle> element */ + +static void sp_circle_class_init(SPCircleClass *klass); +static void sp_circle_init(SPCircle *circle); + +static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); +static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags); +static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value); +static gchar *sp_circle_description(SPItem *item); + +static SPGenericEllipseClass *circle_parent_class; + +GType +sp_circle_get_type(void) +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPCircleClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_circle_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(SPCircle), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_circle_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0); + } + return type; +} + +static void +sp_circle_class_init(SPCircleClass *klass) +{ + SPObjectClass *sp_object_class = (SPObjectClass *) klass; + SPItemClass *item_class = (SPItemClass *) klass; + + circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE); + + sp_object_class->build = sp_circle_build; + sp_object_class->write = sp_circle_write; + sp_object_class->set = sp_circle_set; + + item_class->description = sp_circle_description; +} + +static void +sp_circle_init(SPCircle *circle) +{ + /* Nothing special */ +} + +static void +sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +{ + if (((SPObjectClass *) circle_parent_class)->build) + (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr); + + sp_object_read_attr(object, "cx"); + sp_object_read_attr(object, "cy"); + sp_object_read_attr(object, "r"); +} + +static Inkscape::XML::Node * +sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) +{ + SPGenericEllipse *ellipse; + + ellipse = SP_GENERICELLIPSE(object); + + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { + repr = sp_repr_new("svg:circle"); + } + + sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed); + sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed); + sp_repr_set_svg_double(repr, "r", ellipse->rx.computed); + + if (((SPObjectClass *) circle_parent_class)->write) + ((SPObjectClass *) circle_parent_class)->write(object, repr, flags); + + return repr; +} + +static void +sp_circle_set(SPObject *object, unsigned int key, gchar const *value) +{ + SPGenericEllipse *ge; + + ge = SP_GENERICELLIPSE(object); + + switch (key) { + case SP_ATTR_CX: + ge->cx.readOrUnset(value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_CY: + ge->cy.readOrUnset(value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_R: + if (!ge->rx.read(value) || ge->rx.value <= 0.0) { + ge->rx.unset(); + } + ge->ry = ge->rx; + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + default: + if (((SPObjectClass *) circle_parent_class)->set) + ((SPObjectClass *) circle_parent_class)->set(object, key, value); + break; + } +} + +static gchar *sp_circle_description(SPItem *item) +{ + return g_strdup(_("<b>Circle</b>")); +} + +/* <path sodipodi:type="arc"> element */ + +static void sp_arc_class_init(SPArcClass *klass); +static void sp_arc_init(SPArc *arc); + +static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); +static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags); +static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value); +static void sp_arc_modified(SPObject *object, guint flags); + +static gchar *sp_arc_description(SPItem *item); + +static SPGenericEllipseClass *arc_parent_class; + +GType +sp_arc_get_type(void) +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPArcClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_arc_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(SPArc), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_arc_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0); + } + return type; +} + +static void +sp_arc_class_init(SPArcClass *klass) +{ + SPObjectClass *sp_object_class = (SPObjectClass *) klass; + SPItemClass *item_class = (SPItemClass *) klass; + + arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE); + + sp_object_class->build = sp_arc_build; + sp_object_class->write = sp_arc_write; + sp_object_class->set = sp_arc_set; + sp_object_class->modified = sp_arc_modified; + + item_class->description = sp_arc_description; +} + +static void +sp_arc_init(SPArc *arc) +{ + /* Nothing special */ +} + +static void +sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +{ + if (((SPObjectClass *) arc_parent_class)->build) + (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr); + + Inkscape::Version version = sp_object_get_sodipodi_version(object); + + sp_object_read_attr(object, "sodipodi:cx"); + sp_object_read_attr(object, "sodipodi:cy"); + sp_object_read_attr(object, "sodipodi:rx"); + sp_object_read_attr(object, "sodipodi:ry"); + + sp_object_read_attr(object, "sodipodi:start"); + sp_object_read_attr(object, "sodipodi:end"); + sp_object_read_attr(object, "sodipodi:open"); +} + +/* + * sp_arc_set_elliptical_path_attribute: + * + * Convert center to endpoint parameterization and set it to repr. + * + * See SVG 1.0 Specification W3C Recommendation + * ``F.6 Ellptical arc implementation notes'' for more detail. + */ +static gboolean +sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr) +{ + gint fa, fs; + gdouble dt; + Inkscape::SVGOStringStream os; + + SPGenericEllipse *ge = SP_GENERICELLIPSE(arc); + + NR::Point p1 = sp_arc_get_xy(arc, ge->start); + NR::Point p2 = sp_arc_get_xy(arc, ge->end); + + dt = fmod(ge->end - ge->start, SP_2PI); + if (fabs(dt) < 1e-6) { + NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0); + os << "M " << p1[NR::X] << " " << p1[NR::Y] + << " A " << ge->rx.computed << " " << ge->ry.computed + << " 0 1 1 " << " " << ph[NR::X] << "," << ph[NR::Y] + << " A " << ge->rx.computed << " " << ge->ry.computed + << " 0 1 1 " << " " << p2[NR::X] << " " << p2[NR::Y] << " z"; + } else { + fa = (fabs(dt) > M_PI) ? 1 : 0; + fs = (dt > 0) ? 1 : 0; +#ifdef ARC_VERBOSE + g_print("start:%g end:%g fa=%d fs=%d\n", ge->start, ge->end, fa, fs); +#endif + if (ge->closed) { + os << "M " << p1[NR::X] << "," << p1[NR::Y] + << " A " << ge->rx.computed << "," << ge->ry.computed + << " 0 " << fa << " " << fs << " " << p2[NR::X] << "," << p2[NR::Y] + << " L " << ge->cx.computed << "," << ge->cy.computed << " z"; + } else { + os << "M " << p1[NR::X] << "," << p1[NR::Y] + << " A " << ge->rx.computed << "," << ge->ry.computed + << " 0 " << fa << " " << fs << " " << p2[NR::X] << "," << p2[NR::Y]; + + } + } + repr->setAttribute("d", os.str().c_str()); + return true; +} + +static Inkscape::XML::Node * +sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) +{ + SPGenericEllipse *ge = SP_GENERICELLIPSE(object); + SPArc *arc = SP_ARC(object); + + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { + repr = sp_repr_new("svg:path"); + } + + if (flags & SP_OBJECT_WRITE_EXT) { + repr->setAttribute("sodipodi:type", "arc"); + sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed); + sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed); + sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed); + sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed); + + // write start and end only if they are non-trivial; otherwise remove + gdouble len = fmod(ge->end - ge->start, SP_2PI); + if (len < 0.0) len += SP_2PI; + if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) { + sp_repr_set_svg_double(repr, "sodipodi:start", ge->start); + sp_repr_set_svg_double(repr, "sodipodi:end", ge->end); + repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL); + } else { + repr->setAttribute("sodipodi:end", NULL); + repr->setAttribute("sodipodi:start", NULL); + repr->setAttribute("sodipodi:open", NULL); + } + } + + // write d= + sp_arc_set_elliptical_path_attribute(arc, repr); + + if (((SPObjectClass *) arc_parent_class)->write) + ((SPObjectClass *) arc_parent_class)->write(object, repr, flags); + + return repr; +} + +static void +sp_arc_set(SPObject *object, unsigned int key, gchar const *value) +{ + SPGenericEllipse *ge = SP_GENERICELLIPSE(object); + + switch (key) { + case SP_ATTR_SODIPODI_CX: + ge->cx.readOrUnset(value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_CY: + ge->cy.readOrUnset(value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_RX: + if (!ge->rx.read(value) || ge->rx.computed <= 0.0) { + ge->rx.unset(); + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_RY: + if (!ge->ry.read(value) || ge->ry.computed <= 0.0) { + ge->ry.unset(); + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_START: + if (value) { + sp_svg_number_read_d(value, &ge->start); + } else { + ge->start = 0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_END: + if (value) { + sp_svg_number_read_d(value, &ge->end); + } else { + ge->end = 2 * M_PI; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_OPEN: + ge->closed = (!value); + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + default: + if (((SPObjectClass *) arc_parent_class)->set) + ((SPObjectClass *) arc_parent_class)->set(object, key, value); + break; + } +} + +static void +sp_arc_modified(SPObject *object, guint flags) +{ + if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) { + sp_shape_set_shape((SPShape *) object); + } + + if (((SPObjectClass *) arc_parent_class)->modified) + ((SPObjectClass *) arc_parent_class)->modified(object, flags); +} + +static gchar *sp_arc_description(SPItem *item) +{ + SPGenericEllipse *ge = SP_GENERICELLIPSE(item); + + gdouble len = fmod(ge->end - ge->start, SP_2PI); + if (len < 0.0) len += SP_2PI; + if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) { + if (ge->closed) { + return g_strdup(_("<b>Segment</b>")); + } else { + return g_strdup(_("<b>Arc</b>")); + } + } else { + return g_strdup(_("<b>Ellipse</b>")); + } +} + +void +sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry) +{ + g_return_if_fail(arc != NULL); + g_return_if_fail(SP_IS_ARC(arc)); + + SPGenericEllipse *ge = SP_GENERICELLIPSE(arc); + + ge->cx.computed = x; + ge->cy.computed = y; + ge->rx.computed = rx; + ge->ry.computed = ry; + if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0) + ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0); + if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0) + ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0); + if (!prefs_get_string_attribute("tools.shapes.arc", "open")) + ge->closed = 1; + else + ge->closed = 0; + + ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg) +{ + SPGenericEllipse *ge = SP_GENERICELLIPSE(arc); + + return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed, + ge->ry.computed * sin(arg) + ge->cy.computed); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
