summaryrefslogtreecommitdiffstats
path: root/src/sp-ellipse.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/sp-ellipse.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/sp-ellipse.cpp')
-rw-r--r--src/sp-ellipse.cpp863
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 :