Logo Search packages:      
Sourcecode: inkscape version File versions

sp-ellipse.cpp

#define __SP_ELLIPSE_C__

/*
 * SVG <ellipse> and related implementations
 *
 * Authors:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *   Mitsuru Oka
 *
 * Copyright (C) 1999-2002 Lauris Kaplinski
 * Copyright (C) 2000-2001 Ximian, Inc.
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#include <config.h>

#include <math.h>
#include <string.h>

#include "libnr/n-art-bpath.h"
#include "libnr/nr-matrix.h"
#include "libnr/nr-matrix-ops.h"
#include "svg/svg.h"
#include "svg/stringstream.h"
#include "xml/repr.h"
#include "attributes.h"
#include "style.h"
#include "version.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 SPRepr *sp_genericellipse_write (SPObject *object, SPRepr *repr, guint flags);

static gboolean sp_arc_set_elliptical_path_attribute (SPArc *arc, SPRepr *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)
{
      sp_svg_length_unset (&ellipse->cx, SP_SVG_UNIT_NONE, 0.0, 0.0);
      sp_svg_length_unset (&ellipse->cy, SP_SVG_UNIT_NONE, 0.0, 0.0);
      sp_svg_length_unset (&ellipse->rx, SP_SVG_UNIT_NONE, 0.0, 0.0);
      sp_svg_length_unset (&ellipse->ry, SP_SVG_UNIT_NONE, 0.0, 0.0);

      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_MATRIX_DF_EXPANSION(&((SPItemCtx const *) ctx)->i2vp);
            double const em = style->font_size.computed;
            double const ex = em * 0.5; // fixme: get from pango or libnrtype
            sp_svg_length_update(&ellipse->cx, em, ex, d);
            sp_svg_length_update(&ellipse->cy, em, ex, d);
            sp_svg_length_update(&ellipse->rx, em, ex, d);
            sp_svg_length_update(&ellipse->ry, 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);

      /* Use the bounding box corners */
      NRRect bbox;
      sp_item_invoke_bbox(item, &bbox, i2d, TRUE);
      *p = NR::Point(bbox.x0, bbox.y0);
      *p = NR::Point(bbox.x1, bbox.y0);
      *p = NR::Point(bbox.x1, bbox.y1);
      *p = NR::Point(bbox.x0, bbox.y1);

      /* And the centre */
      *p = NR::Point(ge->cx.computed, ge->cy.computed) * i2d;
}

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 SPRepr *sp_genericellipse_write (SPObject *object, SPRepr *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_double (repr, "sodipodi:cx", ellipse->cx.computed);
            sp_repr_set_double (repr, "sodipodi:cy", ellipse->cy.computed);
            sp_repr_set_double (repr, "sodipodi:rx", ellipse->rx.computed);
            sp_repr_set_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, SPRepr *repr);
static SPRepr *sp_ellipse_write(SPObject *object, SPRepr *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, SPRepr *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 SPRepr *
sp_ellipse_write (SPObject *object, SPRepr *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_double (repr, "cx", ellipse->cx.computed);
      sp_repr_set_double (repr, "cy", ellipse->cy.computed);
      sp_repr_set_double (repr, "rx", ellipse->rx.computed);
      sp_repr_set_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, const gchar *value)
{
      SPGenericEllipse *ellipse;

      ellipse = SP_GENERICELLIPSE (object);

      switch (key) {
      case SP_ATTR_CX:
            if (!sp_svg_length_read (value, &ellipse->cx)) {
                  sp_svg_length_unset (&ellipse->cx, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_CY:
            if (!sp_svg_length_read (value, &ellipse->cy)) {
                  sp_svg_length_unset (&ellipse->cy, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_RX:
            if (!sp_svg_length_read (value, &ellipse->rx) || (ellipse->rx.value <= 0.0)) {
                  sp_svg_length_unset (&ellipse->rx, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_RY:
            if (!sp_svg_length_read (value, &ellipse->ry) || (ellipse->ry.value <= 0.0)) {
                  sp_svg_length_unset (&ellipse->ry, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            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, SPRepr *repr);
static SPRepr *sp_circle_write(SPObject *object, SPRepr *repr, guint flags);
static void sp_circle_set(SPObject *object, unsigned int key, const gchar *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, SPRepr *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 SPRepr *
sp_circle_write (SPObject *object, SPRepr *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_double (repr, "cx", ellipse->cx.computed);
      sp_repr_set_double (repr, "cy", ellipse->cy.computed);
      sp_repr_set_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, const gchar *value)
{
      SPGenericEllipse *ge;

      ge = SP_GENERICELLIPSE (object);

      switch (key) {
      case SP_ATTR_CX:
            if (!sp_svg_length_read (value, &ge->cx)) {
                  sp_svg_length_unset (&ge->cx, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_CY:
            if (!sp_svg_length_read (value, &ge->cy)) {
                  sp_svg_length_unset (&ge->cy, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_R:
            if (!sp_svg_length_read (value, &ge->rx) || (ge->rx.value <= 0.0)) {
                  sp_svg_length_unset (&ge->rx, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            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, SPRepr *repr);
static SPRepr *sp_arc_write(SPObject *object, SPRepr *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, SPRepr *repr)
{
      if (((SPObjectClass *) arc_parent_class)->build)
            (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);

      SPVersion version = sp_object_get_sodipodi_version (object);

      if (sp_version_inside_range (version, 0, 0, 0, 25)) {
            /* Old spec violating arc attributes */
            sp_object_read_attr (object, "cx");
            sp_object_read_attr (object, "cy");
            sp_object_read_attr (object, "rx");
            sp_object_read_attr (object, "ry");
      } else {
            /* New attributes */
            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");

      if (sp_version_inside_range (version, 0, 0, 0, 25)) {
            /* fixme: I am 99.9% sure we can do this here safely, but check nevertheless (Lauris) */
            sp_arc_write (object, repr, SP_OBJECT_WRITE_EXT);
            sp_repr_set_attr (repr, "cx", NULL);
            sp_repr_set_attr (repr, "cy", NULL);
            sp_repr_set_attr (repr, "rx", NULL);
            sp_repr_set_attr (repr, "ry", NULL);
      }
}

/*
 * 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, SPRepr *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];

            }
      }
      return sp_repr_set_attr (repr, "d", os.str().c_str());
}

static SPRepr *
sp_arc_write (SPObject *object, SPRepr *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) {
            sp_repr_set_attr (repr, "sodipodi:type", "arc");
            sp_repr_set_double (repr, "sodipodi:cx", ge->cx.computed);
            sp_repr_set_double (repr, "sodipodi:cy", ge->cy.computed);
            sp_repr_set_double (repr, "sodipodi:rx", ge->rx.computed);
            sp_repr_set_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_double (repr, "sodipodi:start", ge->start);
                  sp_repr_set_double (repr, "sodipodi:end", ge->end);
                  sp_repr_set_attr (repr, "sodipodi:open", (!ge->closed) ? "true" : NULL);
            } else {
                  sp_repr_set_attr (repr, "sodipodi:end", NULL);
                  sp_repr_set_attr (repr, "sodipodi:start", NULL);
                  sp_repr_set_attr (repr, "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, const gchar *value)
{
      SPGenericEllipse *ge = SP_GENERICELLIPSE (object);

      SPVersion version = sp_object_get_sodipodi_version (object);

      if (sp_version_inside_range (version, 0, 0, 0, 25)) {
            switch (key) {
            case SP_ATTR_CX:
                  key = SP_ATTR_SODIPODI_CX;
                  break;
            case SP_ATTR_CY:
                  key = SP_ATTR_SODIPODI_CY;
                  break;
            case SP_ATTR_RX:
                  key = SP_ATTR_SODIPODI_RX;
                  break;
            case SP_ATTR_RY:
                  key = SP_ATTR_SODIPODI_RY;
                  break;
            }
      }

      switch (key) {
      case SP_ATTR_SODIPODI_CX:
            if (!sp_svg_length_read (value, &ge->cx)) {
                  sp_svg_length_unset (&ge->cx, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_SODIPODI_CY:
            if (!sp_svg_length_read (value, &ge->cy)) {
                  sp_svg_length_unset (&ge->cy, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_SODIPODI_RX:
            if (!sp_svg_length_read (value, &ge->rx) || (ge->rx.computed <= 0.0)) {
                  sp_svg_length_unset (&ge->rx, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
            break;
      case SP_ATTR_SODIPODI_RY:
            if (!sp_svg_length_read (value, &ge->ry) || (ge->ry.computed <= 0.0)) {
                  sp_svg_length_unset (&ge->ry, SP_SVG_UNIT_NONE, 0.0, 0.0);
            }
            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)
{
      return g_strdup(_("<b>Arc</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);
}


Generated by  Doxygen 1.6.0   Back to index