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/path-string.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 void sp_genericellipse_update_patheffect (SPShape *shape, bool write);
81 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr,
82 guint flags);
84 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
86 static SPShapeClass *ge_parent_class;
88 GType
89 sp_genericellipse_get_type(void)
90 {
91 static GType type = 0;
92 if (!type) {
93 GTypeInfo info = {
94 sizeof(SPGenericEllipseClass),
95 NULL, /* base_init */
96 NULL, /* base_finalize */
97 (GClassInitFunc) sp_genericellipse_class_init,
98 NULL, /* class_finalize */
99 NULL, /* class_data */
100 sizeof(SPGenericEllipse),
101 16, /* n_preallocs */
102 (GInstanceInitFunc) sp_genericellipse_init,
103 NULL, /* value_table */
104 };
105 type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
106 }
107 return type;
108 }
110 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
111 {
112 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
113 SPItemClass *item_class = (SPItemClass *) klass;
114 SPShapeClass *shape_class = (SPShapeClass *) klass;
116 ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
118 sp_object_class->update = sp_genericellipse_update;
119 sp_object_class->write = sp_genericellipse_write;
121 item_class->snappoints = sp_genericellipse_snappoints;
123 shape_class->set_shape = sp_genericellipse_set_shape;
124 shape_class->update_patheffect = sp_genericellipse_update_patheffect;
125 }
127 static void
128 sp_genericellipse_init(SPGenericEllipse *ellipse)
129 {
130 ellipse->cx.unset();
131 ellipse->cy.unset();
132 ellipse->rx.unset();
133 ellipse->ry.unset();
135 ellipse->start = 0.0;
136 ellipse->end = SP_2PI;
137 ellipse->closed = TRUE;
138 }
140 static void
141 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
142 {
143 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
144 SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
145 SPStyle const *style = object->style;
146 double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
147 double const em = style->font_size.computed;
148 double const ex = em * 0.5; // fixme: get from pango or libnrtype
149 ellipse->cx.update(em, ex, d);
150 ellipse->cy.update(em, ex, d);
151 ellipse->rx.update(em, ex, d);
152 ellipse->ry.update(em, ex, d);
153 sp_shape_set_shape((SPShape *) object);
154 }
156 if (((SPObjectClass *) ge_parent_class)->update)
157 ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
158 }
160 static void
161 sp_genericellipse_update_patheffect(SPShape *shape, bool write)
162 {
163 sp_genericellipse_set_shape(shape);
165 if (write) {
166 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
167 if ( shape->curve != NULL ) {
168 NArtBpath *abp = sp_curve_first_bpath(shape->curve);
169 if (abp) {
170 gchar *str = sp_svg_write_path(abp);
171 repr->setAttribute("d", str);
172 g_free(str);
173 } else {
174 repr->setAttribute("d", "");
175 }
176 } else {
177 repr->setAttribute("d", NULL);
178 }
179 }
181 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
182 }
185 #define C1 0.552
187 /* fixme: Think (Lauris) */
189 static void sp_genericellipse_set_shape(SPShape *shape)
190 {
191 double cx, cy, rx, ry, s, e;
192 double x0, y0, x1, y1, x2, y2, x3, y3;
193 double len;
194 gint slice = FALSE;
195 gint i;
197 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
199 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
200 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
202 sp_genericellipse_normalize(ellipse);
204 cx = 0.0;
205 cy = 0.0;
206 rx = ellipse->rx.computed;
207 ry = ellipse->ry.computed;
209 // figure out if we have a slice, guarding against rounding errors
210 len = fmod(ellipse->end - ellipse->start, SP_2PI);
211 if (len < 0.0) len += SP_2PI;
212 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
213 slice = FALSE;
214 ellipse->end = ellipse->start + SP_2PI;
215 } else {
216 slice = TRUE;
217 }
219 NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
220 aff[4] = ellipse->cx.computed;
221 aff[5] = ellipse->cy.computed;
223 NArtBpath bpath[16];
224 i = 0;
225 if (ellipse->closed) {
226 bpath[i].code = NR_MOVETO;
227 } else {
228 bpath[i].code = NR_MOVETO_OPEN;
229 }
230 bpath[i].x3 = cos(ellipse->start);
231 bpath[i].y3 = sin(ellipse->start);
232 i++;
234 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
235 e = s + M_PI_2;
236 if (e > ellipse->end)
237 e = ellipse->end;
238 len = C1 * (e - s) / M_PI_2;
239 x0 = cos(s);
240 y0 = sin(s);
241 x1 = x0 + len * cos(s + M_PI_2);
242 y1 = y0 + len * sin(s + M_PI_2);
243 x3 = cos(e);
244 y3 = sin(e);
245 x2 = x3 + len * cos(e - M_PI_2);
246 y2 = y3 + len * sin(e - M_PI_2);
247 #ifdef ELLIPSE_VERBOSE
248 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
249 i, s, e, x1, y1, x2, y2, x3, y3);
250 #endif
251 bpath[i].code = NR_CURVETO;
252 bpath[i].x1 = x1;
253 bpath[i].y1 = y1;
254 bpath[i].x2 = x2;
255 bpath[i].y2 = y2;
256 bpath[i].x3 = x3;
257 bpath[i].y3 = y3;
258 i++;
259 }
261 if (slice && ellipse->closed) {
262 bpath[i].code = NR_LINETO;
263 bpath[i].x3 = 0.0;
264 bpath[i].y3 = 0.0;
265 i++;
266 bpath[i].code = NR_LINETO;
267 bpath[i].x3 = bpath[0].x3;
268 bpath[i].y3 = bpath[0].y3;
269 i++;
270 } else if (ellipse->closed) {
271 bpath[i-1].x3 = bpath[0].x3;
272 bpath[i-1].y3 = bpath[0].y3;
273 }
275 bpath[i].code = NR_END;
276 SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff));
277 g_assert(c != NULL);
279 sp_shape_perform_path_effect(c, SP_SHAPE (ellipse));
280 sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
281 sp_curve_unref(c);
282 }
284 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
285 {
286 SPGenericEllipse const *ge = SP_GENERICELLIPSE(item);
288 NR::Matrix const i2d = sp_item_i2d_affine(item);
290 /* Add the centre */
291 *p = NR::Point(ge->cx.computed, ge->cy.computed) * i2d;
293 // TODO: add the ends of radii
294 }
296 void
297 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
298 {
299 ellipse->start = fmod(ellipse->start, SP_2PI);
300 ellipse->end = fmod(ellipse->end, SP_2PI);
302 if (ellipse->start < 0.0)
303 ellipse->start += SP_2PI;
304 double diff = ellipse->start - ellipse->end;
305 if (diff >= 0.0)
306 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
308 /* Now we keep: 0 <= start < end <= 2*PI */
309 }
311 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
312 {
313 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
315 if (flags & SP_OBJECT_WRITE_EXT) {
316 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
317 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
318 repr = xml_doc->createElement("svg:path");
319 }
321 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
322 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
323 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
324 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
326 if (SP_IS_ARC(ellipse))
327 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
328 }
330 if (((SPObjectClass *) ge_parent_class)->write)
331 ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
333 return repr;
334 }
336 /* SVG <ellipse> element */
338 static void sp_ellipse_class_init(SPEllipseClass *klass);
339 static void sp_ellipse_init(SPEllipse *ellipse);
341 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
342 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
343 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
344 static gchar *sp_ellipse_description(SPItem *item);
346 static SPGenericEllipseClass *ellipse_parent_class;
348 GType
349 sp_ellipse_get_type(void)
350 {
351 static GType type = 0;
352 if (!type) {
353 GTypeInfo info = {
354 sizeof(SPEllipseClass),
355 NULL, /* base_init */
356 NULL, /* base_finalize */
357 (GClassInitFunc) sp_ellipse_class_init,
358 NULL, /* class_finalize */
359 NULL, /* class_data */
360 sizeof(SPEllipse),
361 16, /* n_preallocs */
362 (GInstanceInitFunc) sp_ellipse_init,
363 NULL, /* value_table */
364 };
365 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
366 }
367 return type;
368 }
370 static void sp_ellipse_class_init(SPEllipseClass *klass)
371 {
372 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
373 SPItemClass *item_class = (SPItemClass *) klass;
375 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
377 sp_object_class->build = sp_ellipse_build;
378 sp_object_class->write = sp_ellipse_write;
379 sp_object_class->set = sp_ellipse_set;
381 item_class->description = sp_ellipse_description;
382 }
384 static void
385 sp_ellipse_init(SPEllipse */*ellipse*/)
386 {
387 /* Nothing special */
388 }
390 static void
391 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
392 {
393 g_print ("sp_ellipse_build\n");
394 if (((SPObjectClass *) ellipse_parent_class)->build)
395 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
397 sp_object_read_attr(object, "cx");
398 sp_object_read_attr(object, "cy");
399 sp_object_read_attr(object, "rx");
400 sp_object_read_attr(object, "ry");
401 }
403 static Inkscape::XML::Node *
404 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
405 {
406 SPGenericEllipse *ellipse;
408 ellipse = SP_GENERICELLIPSE(object);
410 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
411 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
412 repr = xml_doc->createElement("svg:ellipse");
413 }
415 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
416 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
417 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
418 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
420 if (((SPObjectClass *) ellipse_parent_class)->write)
421 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
423 return repr;
424 }
426 static void
427 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
428 {
429 SPGenericEllipse *ellipse;
431 ellipse = SP_GENERICELLIPSE(object);
433 switch (key) {
434 case SP_ATTR_CX:
435 ellipse->cx.readOrUnset(value);
436 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
437 break;
438 case SP_ATTR_CY:
439 ellipse->cy.readOrUnset(value);
440 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
441 break;
442 case SP_ATTR_RX:
443 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
444 ellipse->rx.unset();
445 }
446 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
447 break;
448 case SP_ATTR_RY:
449 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
450 ellipse->ry.unset();
451 }
452 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
453 break;
454 default:
455 if (((SPObjectClass *) ellipse_parent_class)->set)
456 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
457 break;
458 }
459 }
461 static gchar *sp_ellipse_description(SPItem */*item*/)
462 {
463 return g_strdup(_("<b>Ellipse</b>"));
464 }
467 void
468 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
469 {
470 SPGenericEllipse *ge;
472 g_return_if_fail(ellipse != NULL);
473 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
475 ge = SP_GENERICELLIPSE(ellipse);
477 ge->cx.computed = x;
478 ge->cy.computed = y;
479 ge->rx.computed = rx;
480 ge->ry.computed = ry;
482 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
483 }
485 /* SVG <circle> element */
487 static void sp_circle_class_init(SPCircleClass *klass);
488 static void sp_circle_init(SPCircle *circle);
490 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
491 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
492 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
493 static gchar *sp_circle_description(SPItem *item);
495 static SPGenericEllipseClass *circle_parent_class;
497 GType
498 sp_circle_get_type(void)
499 {
500 static GType type = 0;
501 if (!type) {
502 GTypeInfo info = {
503 sizeof(SPCircleClass),
504 NULL, /* base_init */
505 NULL, /* base_finalize */
506 (GClassInitFunc) sp_circle_class_init,
507 NULL, /* class_finalize */
508 NULL, /* class_data */
509 sizeof(SPCircle),
510 16, /* n_preallocs */
511 (GInstanceInitFunc) sp_circle_init,
512 NULL, /* value_table */
513 };
514 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
515 }
516 return type;
517 }
519 static void
520 sp_circle_class_init(SPCircleClass *klass)
521 {
522 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
523 SPItemClass *item_class = (SPItemClass *) klass;
525 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
527 sp_object_class->build = sp_circle_build;
528 sp_object_class->write = sp_circle_write;
529 sp_object_class->set = sp_circle_set;
531 item_class->description = sp_circle_description;
532 }
534 static void
535 sp_circle_init(SPCircle */*circle*/)
536 {
537 /* Nothing special */
538 }
540 static void
541 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
542 {
543 if (((SPObjectClass *) circle_parent_class)->build)
544 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
546 sp_object_read_attr(object, "cx");
547 sp_object_read_attr(object, "cy");
548 sp_object_read_attr(object, "r");
549 }
551 static Inkscape::XML::Node *
552 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
553 {
554 SPGenericEllipse *ellipse;
556 ellipse = SP_GENERICELLIPSE(object);
558 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
559 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
560 repr = xml_doc->createElement("svg:circle");
561 }
563 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
564 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
565 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
567 if (((SPObjectClass *) circle_parent_class)->write)
568 ((SPObjectClass *) circle_parent_class)->write(object, repr, flags);
570 return repr;
571 }
573 static void
574 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
575 {
576 SPGenericEllipse *ge;
578 ge = SP_GENERICELLIPSE(object);
580 switch (key) {
581 case SP_ATTR_CX:
582 ge->cx.readOrUnset(value);
583 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
584 break;
585 case SP_ATTR_CY:
586 ge->cy.readOrUnset(value);
587 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
588 break;
589 case SP_ATTR_R:
590 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
591 ge->rx.unset();
592 }
593 ge->ry = ge->rx;
594 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
595 break;
596 default:
597 if (((SPObjectClass *) circle_parent_class)->set)
598 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
599 break;
600 }
601 }
603 static gchar *sp_circle_description(SPItem */*item*/)
604 {
605 return g_strdup(_("<b>Circle</b>"));
606 }
608 /* <path sodipodi:type="arc"> element */
610 static void sp_arc_class_init(SPArcClass *klass);
611 static void sp_arc_init(SPArc *arc);
613 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
614 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
615 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
616 static void sp_arc_modified(SPObject *object, guint flags);
618 static gchar *sp_arc_description(SPItem *item);
620 static SPGenericEllipseClass *arc_parent_class;
622 GType
623 sp_arc_get_type(void)
624 {
625 static GType type = 0;
626 if (!type) {
627 GTypeInfo info = {
628 sizeof(SPArcClass),
629 NULL, /* base_init */
630 NULL, /* base_finalize */
631 (GClassInitFunc) sp_arc_class_init,
632 NULL, /* class_finalize */
633 NULL, /* class_data */
634 sizeof(SPArc),
635 16, /* n_preallocs */
636 (GInstanceInitFunc) sp_arc_init,
637 NULL, /* value_table */
638 };
639 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
640 }
641 return type;
642 }
644 static void
645 sp_arc_class_init(SPArcClass *klass)
646 {
647 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
648 SPItemClass *item_class = (SPItemClass *) klass;
650 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
652 sp_object_class->build = sp_arc_build;
653 sp_object_class->write = sp_arc_write;
654 sp_object_class->set = sp_arc_set;
655 sp_object_class->modified = sp_arc_modified;
657 item_class->description = sp_arc_description;
658 }
660 static void
661 sp_arc_init(SPArc */*arc*/)
662 {
663 /* Nothing special */
664 }
666 static void
667 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
668 {
669 if (((SPObjectClass *) arc_parent_class)->build)
670 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
672 Inkscape::Version version = sp_object_get_sodipodi_version(object);
674 sp_object_read_attr(object, "sodipodi:cx");
675 sp_object_read_attr(object, "sodipodi:cy");
676 sp_object_read_attr(object, "sodipodi:rx");
677 sp_object_read_attr(object, "sodipodi:ry");
679 sp_object_read_attr(object, "sodipodi:start");
680 sp_object_read_attr(object, "sodipodi:end");
681 sp_object_read_attr(object, "sodipodi:open");
682 }
684 /*
685 * sp_arc_set_elliptical_path_attribute:
686 *
687 * Convert center to endpoint parameterization and set it to repr.
688 *
689 * See SVG 1.0 Specification W3C Recommendation
690 * ``F.6 Ellptical arc implementation notes'' for more detail.
691 */
692 static gboolean
693 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
694 {
695 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
697 Inkscape::SVG::PathString str;
699 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
700 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
701 double rx = ge->rx.computed;
702 double ry = ge->ry.computed;
704 str.moveTo(p1);
706 double dt = fmod(ge->end - ge->start, SP_2PI);
707 if (fabs(dt) < 1e-6) {
708 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
709 str.arcTo(rx, ry, 0, true, true, ph)
710 .arcTo(rx, ry, 0, true, true, p2)
711 .closePath();
712 } else {
713 bool fa = (fabs(dt) > M_PI);
714 bool fs = (dt > 0);
715 str.arcTo(rx, ry, 0, fa, fs, p2);
716 if (ge->closed) {
717 NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
718 str.lineTo(center).closePath();
719 }
720 }
722 repr->setAttribute("d", str.c_str());
723 return true;
724 }
726 static Inkscape::XML::Node *
727 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
728 {
729 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
730 SPArc *arc = SP_ARC(object);
732 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
733 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
734 repr = xml_doc->createElement("svg:path");
735 }
737 if (flags & SP_OBJECT_WRITE_EXT) {
738 repr->setAttribute("sodipodi:type", "arc");
739 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
740 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
741 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
742 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
744 // write start and end only if they are non-trivial; otherwise remove
745 gdouble len = fmod(ge->end - ge->start, SP_2PI);
746 if (len < 0.0) len += SP_2PI;
747 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
748 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
749 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
750 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
751 } else {
752 repr->setAttribute("sodipodi:end", NULL);
753 repr->setAttribute("sodipodi:start", NULL);
754 repr->setAttribute("sodipodi:open", NULL);
755 }
756 }
758 // write d=
759 sp_arc_set_elliptical_path_attribute(arc, repr);
761 if (((SPObjectClass *) arc_parent_class)->write)
762 ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
764 return repr;
765 }
767 static void
768 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
769 {
770 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
772 switch (key) {
773 case SP_ATTR_SODIPODI_CX:
774 ge->cx.readOrUnset(value);
775 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
776 break;
777 case SP_ATTR_SODIPODI_CY:
778 ge->cy.readOrUnset(value);
779 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
780 break;
781 case SP_ATTR_SODIPODI_RX:
782 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
783 ge->rx.unset();
784 }
785 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
786 break;
787 case SP_ATTR_SODIPODI_RY:
788 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
789 ge->ry.unset();
790 }
791 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
792 break;
793 case SP_ATTR_SODIPODI_START:
794 if (value) {
795 sp_svg_number_read_d(value, &ge->start);
796 } else {
797 ge->start = 0;
798 }
799 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
800 break;
801 case SP_ATTR_SODIPODI_END:
802 if (value) {
803 sp_svg_number_read_d(value, &ge->end);
804 } else {
805 ge->end = 2 * M_PI;
806 }
807 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
808 break;
809 case SP_ATTR_SODIPODI_OPEN:
810 ge->closed = (!value);
811 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
812 break;
813 default:
814 if (((SPObjectClass *) arc_parent_class)->set)
815 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
816 break;
817 }
818 }
820 static void
821 sp_arc_modified(SPObject *object, guint flags)
822 {
823 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
824 sp_shape_set_shape((SPShape *) object);
825 }
827 if (((SPObjectClass *) arc_parent_class)->modified)
828 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
829 }
831 static gchar *sp_arc_description(SPItem *item)
832 {
833 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
835 gdouble len = fmod(ge->end - ge->start, SP_2PI);
836 if (len < 0.0) len += SP_2PI;
837 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
838 if (ge->closed) {
839 return g_strdup(_("<b>Segment</b>"));
840 } else {
841 return g_strdup(_("<b>Arc</b>"));
842 }
843 } else {
844 return g_strdup(_("<b>Ellipse</b>"));
845 }
846 }
848 void
849 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
850 {
851 g_return_if_fail(arc != NULL);
852 g_return_if_fail(SP_IS_ARC(arc));
854 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
856 ge->cx.computed = x;
857 ge->cy.computed = y;
858 ge->rx.computed = rx;
859 ge->ry.computed = ry;
860 if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
861 ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
862 if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
863 ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
864 if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
865 ge->closed = 1;
866 else
867 ge->closed = 0;
869 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
870 }
872 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
873 {
874 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
876 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
877 ge->ry.computed * sin(arg) + ge->cy.computed);
878 }
881 /*
882 Local Variables:
883 mode:c++
884 c-file-style:"stroustrup"
885 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
886 indent-tabs-mode:nil
887 fill-column:99
888 End:
889 */
890 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :