Code

start switching sp_repr_new* over to XML::Document::create*, and rename create method...
[inkscape.git] / src / sp-ellipse.cpp
1 #define __SP_ELLIPSE_C__
3 /*
4  * SVG <ellipse> and related implementations
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Mitsuru Oka
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 1999-2002 Lauris Kaplinski
12  * Copyright (C) 2000-2001 Ximian, Inc.
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
22 #include "libnr/n-art-bpath.h"
23 #include "libnr/nr-path.h"
24 #include "libnr/nr-matrix-fns.h"
25 #include "svg/svg.h"
26 #include "svg/stringstream.h"
27 #include "xml/repr.h"
28 #include "attributes.h"
29 #include "style.h"
30 #include "display/curve.h"
31 #include <glibmm/i18n.h>
33 #include "document.h"
34 #include "sp-ellipse.h"
36 #include "prefs-utils.h"
38 /* Common parent class */
40 #define noELLIPSE_VERBOSE
42 #ifndef M_PI
43 #define M_PI 3.14159265358979323846
44 #endif
46 #define SP_2PI (2 * M_PI)
48 #if 1
49 /* Hmmm... shouldn't this also qualify */
50 /* Whether it is faster or not, well, nobody knows */
51 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
52 #else
53 /* we do not use C99 round(3) function yet */
54 static double sp_round(double x, double y)
55 {
56     double remain;
58     g_assert(y > 0.0);
60     /* return round(x/y) * y; */
62     remain = fmod(x, y);
64     if (remain >= 0.5*y)
65         return x - remain + y;
66     else
67         return x - remain;
68 }
69 #endif
71 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
72 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
74 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
76 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p);
78 static void sp_genericellipse_set_shape(SPShape *shape);
79 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr,
80                                                     guint flags);
82 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
84 static SPShapeClass *ge_parent_class;
86 GType
87 sp_genericellipse_get_type(void)
88 {
89     static GType type = 0;
90     if (!type) {
91         GTypeInfo info = {
92             sizeof(SPGenericEllipseClass),
93             NULL,   /* base_init */
94             NULL,   /* base_finalize */
95             (GClassInitFunc) sp_genericellipse_class_init,
96             NULL,   /* class_finalize */
97             NULL,   /* class_data */
98             sizeof(SPGenericEllipse),
99             16,   /* n_preallocs */
100             (GInstanceInitFunc) sp_genericellipse_init,
101             NULL,   /* value_table */
102         };
103         type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
104     }
105     return type;
108 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
110     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
111     SPItemClass *item_class = (SPItemClass *) klass;
112     SPShapeClass *shape_class = (SPShapeClass *) klass;
114     ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
116     sp_object_class->update = sp_genericellipse_update;
117     sp_object_class->write = sp_genericellipse_write;
119     item_class->snappoints = sp_genericellipse_snappoints;
121     shape_class->set_shape = sp_genericellipse_set_shape;
124 static void
125 sp_genericellipse_init(SPGenericEllipse *ellipse)
127     ellipse->cx.unset();
128     ellipse->cy.unset();
129     ellipse->rx.unset();
130     ellipse->ry.unset();
132     ellipse->start = 0.0;
133     ellipse->end = SP_2PI;
134     ellipse->closed = TRUE;
137 static void
138 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
140     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
141         SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
142         SPStyle const *style = object->style;
143         double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
144         double const em = style->font_size.computed;
145         double const ex = em * 0.5; // fixme: get from pango or libnrtype
146         ellipse->cx.update(em, ex, d);
147         ellipse->cy.update(em, ex, d);
148         ellipse->rx.update(em, ex, d);
149         ellipse->ry.update(em, ex, d);
150         sp_shape_set_shape((SPShape *) object);
151     }
153     if (((SPObjectClass *) ge_parent_class)->update)
154         ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
157 #define C1 0.552
159 /* fixme: Think (Lauris) */
161 static void sp_genericellipse_set_shape(SPShape *shape)
163     double cx, cy, rx, ry, s, e;
164     double x0, y0, x1, y1, x2, y2, x3, y3;
165     double len;
166     gint slice = FALSE;
167     gint i;
169     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
171     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
172     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
174     sp_genericellipse_normalize(ellipse);
176     cx = 0.0;
177     cy = 0.0;
178     rx = ellipse->rx.computed;
179     ry = ellipse->ry.computed;
181     // figure out if we have a slice, guarding against rounding errors
182     len = fmod(ellipse->end - ellipse->start, SP_2PI);
183     if (len < 0.0) len += SP_2PI;
184     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
185         slice = FALSE;
186         ellipse->end = ellipse->start + SP_2PI;
187     } else {
188         slice = TRUE;
189     }
191     NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
192     aff[4] = ellipse->cx.computed;
193     aff[5] = ellipse->cy.computed;
195     NArtBpath bpath[16];
196     i = 0;
197     if (ellipse->closed) {
198         bpath[i].code = NR_MOVETO;
199     } else {
200         bpath[i].code = NR_MOVETO_OPEN;
201     }
202     bpath[i].x3 = cos(ellipse->start);
203     bpath[i].y3 = sin(ellipse->start);
204     i++;
206     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
207         e = s + M_PI_2;
208         if (e > ellipse->end)
209             e = ellipse->end;
210         len = C1 * (e - s) / M_PI_2;
211         x0 = cos(s);
212         y0 = sin(s);
213         x1 = x0 + len * cos(s + M_PI_2);
214         y1 = y0 + len * sin(s + M_PI_2);
215         x3 = cos(e);
216         y3 = sin(e);
217         x2 = x3 + len * cos(e - M_PI_2);
218         y2 = y3 + len * sin(e - M_PI_2);
219 #ifdef ELLIPSE_VERBOSE
220         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
221                 i, s, e, x1, y1, x2, y2, x3, y3);
222 #endif
223         bpath[i].code = NR_CURVETO;
224         bpath[i].x1 = x1;
225         bpath[i].y1 = y1;
226         bpath[i].x2 = x2;
227         bpath[i].y2 = y2;
228         bpath[i].x3 = x3;
229         bpath[i].y3 = y3;
230         i++;
231     }
233     if (slice && ellipse->closed) {
234         bpath[i].code = NR_LINETO;
235         bpath[i].x3 = 0.0;
236         bpath[i].y3 = 0.0;
237         i++;
238         bpath[i].code = NR_LINETO;
239         bpath[i].x3 = bpath[0].x3;
240         bpath[i].y3 = bpath[0].y3;
241         i++;
242     } else if (ellipse->closed) {
243         bpath[i-1].x3 = bpath[0].x3;
244         bpath[i-1].y3 = bpath[0].y3;
245     }
247     bpath[i].code = NR_END;
248     SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff));
249     g_assert(c != NULL);
251     sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
252     sp_curve_unref(c);
255 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
257     SPGenericEllipse const *ge = SP_GENERICELLIPSE(item);
259     NR::Matrix const i2d = sp_item_i2d_affine(item);
261     /* Add the centre */
262     *p = NR::Point(ge->cx.computed, ge->cy.computed) * i2d;
264     // TODO: add the ends of radii
267 void
268 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
270     ellipse->start = fmod(ellipse->start, SP_2PI);
271     ellipse->end = fmod(ellipse->end, SP_2PI);
273     if (ellipse->start < 0.0)
274         ellipse->start += SP_2PI;
275     double diff = ellipse->start - ellipse->end;
276     if (diff >= 0.0)
277         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
279     /* Now we keep: 0 <= start < end <= 2*PI */
282 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
284     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
286     if (flags & SP_OBJECT_WRITE_EXT) {
287         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
288             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
289             repr = xml_doc->createElement("svg:path");
290         }
292         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
293         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
294         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
295         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
297         if (SP_IS_ARC(ellipse))
298             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
299     }
301     if (((SPObjectClass *) ge_parent_class)->write)
302         ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
304     return repr;
307 /* SVG <ellipse> element */
309 static void sp_ellipse_class_init(SPEllipseClass *klass);
310 static void sp_ellipse_init(SPEllipse *ellipse);
312 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
313 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
314 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
315 static gchar *sp_ellipse_description(SPItem *item);
317 static SPGenericEllipseClass *ellipse_parent_class;
319 GType
320 sp_ellipse_get_type(void)
322     static GType type = 0;
323     if (!type) {
324         GTypeInfo info = {
325             sizeof(SPEllipseClass),
326             NULL,   /* base_init */
327             NULL,   /* base_finalize */
328             (GClassInitFunc) sp_ellipse_class_init,
329             NULL,   /* class_finalize */
330             NULL,   /* class_data */
331             sizeof(SPEllipse),
332             16,   /* n_preallocs */
333             (GInstanceInitFunc) sp_ellipse_init,
334             NULL,   /* value_table */
335         };
336         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
337     }
338     return type;
341 static void sp_ellipse_class_init(SPEllipseClass *klass)
343     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
344     SPItemClass *item_class = (SPItemClass *) klass;
346     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
348     sp_object_class->build = sp_ellipse_build;
349     sp_object_class->write = sp_ellipse_write;
350     sp_object_class->set = sp_ellipse_set;
352     item_class->description = sp_ellipse_description;
355 static void
356 sp_ellipse_init(SPEllipse *ellipse)
358     /* Nothing special */
361 static void
362 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
364     if (((SPObjectClass *) ellipse_parent_class)->build)
365         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
367     sp_object_read_attr(object, "cx");
368     sp_object_read_attr(object, "cy");
369     sp_object_read_attr(object, "rx");
370     sp_object_read_attr(object, "ry");
373 static Inkscape::XML::Node *
374 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
376     SPGenericEllipse *ellipse;
378     ellipse = SP_GENERICELLIPSE(object);
380     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
381         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
382         repr = xml_doc->createElement("svg:ellipse");
383     }
385     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
386     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
387     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
388     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
390     if (((SPObjectClass *) ellipse_parent_class)->write)
391         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
393     return repr;
396 static void
397 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
399     SPGenericEllipse *ellipse;
401     ellipse = SP_GENERICELLIPSE(object);
403     switch (key) {
404         case SP_ATTR_CX:
405             ellipse->cx.readOrUnset(value);
406             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
407             break;
408         case SP_ATTR_CY:
409             ellipse->cy.readOrUnset(value);
410             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
411             break;
412         case SP_ATTR_RX:
413             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
414                 ellipse->rx.unset();
415             }
416             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
417             break;
418         case SP_ATTR_RY:
419             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
420                 ellipse->ry.unset();
421             }
422             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
423             break;
424         default:
425             if (((SPObjectClass *) ellipse_parent_class)->set)
426                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
427             break;
428     }
431 static gchar *sp_ellipse_description(SPItem *item)
433     return g_strdup(_("<b>Ellipse</b>"));
437 void
438 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
440     SPGenericEllipse *ge;
442     g_return_if_fail(ellipse != NULL);
443     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
445     ge = SP_GENERICELLIPSE(ellipse);
447     ge->cx.computed = x;
448     ge->cy.computed = y;
449     ge->rx.computed = rx;
450     ge->ry.computed = ry;
452     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
455 /* SVG <circle> element */
457 static void sp_circle_class_init(SPCircleClass *klass);
458 static void sp_circle_init(SPCircle *circle);
460 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
461 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
462 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
463 static gchar *sp_circle_description(SPItem *item);
465 static SPGenericEllipseClass *circle_parent_class;
467 GType
468 sp_circle_get_type(void)
470     static GType type = 0;
471     if (!type) {
472         GTypeInfo info = {
473             sizeof(SPCircleClass),
474             NULL,   /* base_init */
475             NULL,   /* base_finalize */
476             (GClassInitFunc) sp_circle_class_init,
477             NULL,   /* class_finalize */
478             NULL,   /* class_data */
479             sizeof(SPCircle),
480             16,   /* n_preallocs */
481             (GInstanceInitFunc) sp_circle_init,
482             NULL,   /* value_table */
483         };
484         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
485     }
486     return type;
489 static void
490 sp_circle_class_init(SPCircleClass *klass)
492     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
493     SPItemClass *item_class = (SPItemClass *) klass;
495     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
497     sp_object_class->build = sp_circle_build;
498     sp_object_class->write = sp_circle_write;
499     sp_object_class->set = sp_circle_set;
501     item_class->description = sp_circle_description;
504 static void
505 sp_circle_init(SPCircle *circle)
507     /* Nothing special */
510 static void
511 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
513     if (((SPObjectClass *) circle_parent_class)->build)
514         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
516     sp_object_read_attr(object, "cx");
517     sp_object_read_attr(object, "cy");
518     sp_object_read_attr(object, "r");
521 static Inkscape::XML::Node *
522 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
524     SPGenericEllipse *ellipse;
526     ellipse = SP_GENERICELLIPSE(object);
528     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
529         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
530         repr = xml_doc->createElement("svg:circle");
531     }
533     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
534     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
535     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
537     if (((SPObjectClass *) circle_parent_class)->write)
538         ((SPObjectClass *) circle_parent_class)->write(object, repr, flags);
540     return repr;
543 static void
544 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
546     SPGenericEllipse *ge;
548     ge = SP_GENERICELLIPSE(object);
550     switch (key) {
551         case SP_ATTR_CX:
552             ge->cx.readOrUnset(value);
553             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
554             break;
555         case SP_ATTR_CY:
556             ge->cy.readOrUnset(value);
557             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
558             break;
559         case SP_ATTR_R:
560             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
561                 ge->rx.unset();
562             }
563             ge->ry = ge->rx;
564             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
565             break;
566         default:
567             if (((SPObjectClass *) circle_parent_class)->set)
568                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
569             break;
570     }
573 static gchar *sp_circle_description(SPItem *item)
575     return g_strdup(_("<b>Circle</b>"));
578 /* <path sodipodi:type="arc"> element */
580 static void sp_arc_class_init(SPArcClass *klass);
581 static void sp_arc_init(SPArc *arc);
583 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
584 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
585 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
586 static void sp_arc_modified(SPObject *object, guint flags);
588 static gchar *sp_arc_description(SPItem *item);
590 static SPGenericEllipseClass *arc_parent_class;
592 GType
593 sp_arc_get_type(void)
595     static GType type = 0;
596     if (!type) {
597         GTypeInfo info = {
598             sizeof(SPArcClass),
599             NULL,   /* base_init */
600             NULL,   /* base_finalize */
601             (GClassInitFunc) sp_arc_class_init,
602             NULL,   /* class_finalize */
603             NULL,   /* class_data */
604             sizeof(SPArc),
605             16,   /* n_preallocs */
606             (GInstanceInitFunc) sp_arc_init,
607             NULL,   /* value_table */
608         };
609         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
610     }
611     return type;
614 static void
615 sp_arc_class_init(SPArcClass *klass)
617     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
618     SPItemClass *item_class = (SPItemClass *) klass;
620     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
622     sp_object_class->build = sp_arc_build;
623     sp_object_class->write = sp_arc_write;
624     sp_object_class->set = sp_arc_set;
625     sp_object_class->modified = sp_arc_modified;
627     item_class->description = sp_arc_description;
630 static void
631 sp_arc_init(SPArc *arc)
633     /* Nothing special */
636 static void
637 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
639     if (((SPObjectClass *) arc_parent_class)->build)
640         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
642     Inkscape::Version version = sp_object_get_sodipodi_version(object);
644     sp_object_read_attr(object, "sodipodi:cx");
645     sp_object_read_attr(object, "sodipodi:cy");
646     sp_object_read_attr(object, "sodipodi:rx");
647     sp_object_read_attr(object, "sodipodi:ry");
649     sp_object_read_attr(object, "sodipodi:start");
650     sp_object_read_attr(object, "sodipodi:end");
651     sp_object_read_attr(object, "sodipodi:open");
654 /*
655  * sp_arc_set_elliptical_path_attribute:
656  *
657  * Convert center to endpoint parameterization and set it to repr.
658  *
659  * See SVG 1.0 Specification W3C Recommendation
660  * ``F.6 Ellptical arc implementation notes'' for more detail.
661  */
662 static gboolean
663 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
665     gint fa, fs;
666     gdouble  dt;
667     Inkscape::SVGOStringStream os;
669     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
671     NR::Point p1 = sp_arc_get_xy(arc, ge->start);
672     NR::Point p2 = sp_arc_get_xy(arc, ge->end);
674     dt = fmod(ge->end - ge->start, SP_2PI);
675     if (fabs(dt) < 1e-6) {
676         NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
677         os << "M " << p1[NR::X] << " " <<  p1[NR::Y]
678            << " A " << ge->rx.computed << " " << ge->ry.computed
679            << " 0 1 1 " << " " << ph[NR::X] << "," << ph[NR::Y]
680            << " A " << ge->rx.computed << " " << ge->ry.computed
681            << " 0 1 1 " << " " << p2[NR::X] << " " << p2[NR::Y] << " z";
682     } else {
683         fa = (fabs(dt) > M_PI) ? 1 : 0;
684         fs = (dt > 0) ? 1 : 0;
685 #ifdef ARC_VERBOSE
686         g_print("start:%g end:%g fa=%d fs=%d\n", ge->start, ge->end, fa, fs);
687 #endif
688         if (ge->closed) {
689             os << "M " << p1[NR::X] << "," << p1[NR::Y]
690                << " A " << ge->rx.computed << "," << ge->ry.computed
691                << " 0 " << fa << " " << fs << " " << p2[NR::X] << "," << p2[NR::Y]
692                << " L " << ge->cx.computed << "," << ge->cy.computed << " z";
693         } else {
694             os << "M " << p1[NR::X] << "," << p1[NR::Y]
695                << " A " << ge->rx.computed << "," << ge->ry.computed
696                << " 0 " << fa << " " << fs << " " << p2[NR::X] << "," << p2[NR::Y];
698         }
699     }
700     repr->setAttribute("d", os.str().c_str());
701     return true;
704 static Inkscape::XML::Node *
705 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
707     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
708     SPArc *arc = SP_ARC(object);
710     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
711         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
712         repr = xml_doc->createElement("svg:path");
713     }
715     if (flags & SP_OBJECT_WRITE_EXT) {
716         repr->setAttribute("sodipodi:type", "arc");
717         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
718         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
719         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
720         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
722         // write start and end only if they are non-trivial; otherwise remove
723         gdouble len = fmod(ge->end - ge->start, SP_2PI);
724         if (len < 0.0) len += SP_2PI;
725         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
726             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
727             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
728             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
729         } else {
730             repr->setAttribute("sodipodi:end", NULL);
731             repr->setAttribute("sodipodi:start", NULL);
732             repr->setAttribute("sodipodi:open", NULL);
733         }
734     }
736     // write d=
737     sp_arc_set_elliptical_path_attribute(arc, repr);
739     if (((SPObjectClass *) arc_parent_class)->write)
740         ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
742     return repr;
745 static void
746 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
748     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
750     switch (key) {
751         case SP_ATTR_SODIPODI_CX:
752             ge->cx.readOrUnset(value);
753             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
754             break;
755         case SP_ATTR_SODIPODI_CY:
756             ge->cy.readOrUnset(value);
757             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
758             break;
759         case SP_ATTR_SODIPODI_RX:
760             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
761                 ge->rx.unset();
762             }
763             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
764             break;
765         case SP_ATTR_SODIPODI_RY:
766             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
767                 ge->ry.unset();
768             }
769             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
770             break;
771         case SP_ATTR_SODIPODI_START:
772             if (value) {
773                 sp_svg_number_read_d(value, &ge->start);
774             } else {
775                 ge->start = 0;
776             }
777             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
778             break;
779         case SP_ATTR_SODIPODI_END:
780             if (value) {
781                 sp_svg_number_read_d(value, &ge->end);
782             } else {
783                 ge->end = 2 * M_PI;
784             }
785             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
786             break;
787         case SP_ATTR_SODIPODI_OPEN:
788             ge->closed = (!value);
789             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
790             break;
791         default:
792             if (((SPObjectClass *) arc_parent_class)->set)
793                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
794             break;
795     }
798 static void
799 sp_arc_modified(SPObject *object, guint flags)
801     if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
802         sp_shape_set_shape((SPShape *) object);
803     }
805     if (((SPObjectClass *) arc_parent_class)->modified)
806         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
809 static gchar *sp_arc_description(SPItem *item)
811     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
813     gdouble len = fmod(ge->end - ge->start, SP_2PI);
814     if (len < 0.0) len += SP_2PI;
815     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
816         if (ge->closed) {
817             return g_strdup(_("<b>Segment</b>"));
818         } else {
819             return g_strdup(_("<b>Arc</b>"));
820         }
821     } else {
822         return g_strdup(_("<b>Ellipse</b>"));
823     }
826 void
827 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
829     g_return_if_fail(arc != NULL);
830     g_return_if_fail(SP_IS_ARC(arc));
832     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
834     ge->cx.computed = x;
835     ge->cy.computed = y;
836     ge->rx.computed = rx;
837     ge->ry.computed = ry;
838     if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
839         ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
840     if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
841         ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
842     if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
843         ge->closed = 1;
844     else
845         ge->closed = 0;
847     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
850 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
852     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
854     return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
855                      ge->ry.computed * sin(arg) + ge->cy.computed);
859 /*
860   Local Variables:
861   mode:c++
862   c-file-style:"stroustrup"
863   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
864   indent-tabs-mode:nil
865   fill-column:99
866   End:
867 */
868 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :