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 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;
106 }
108 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
109 {
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;
122 }
124 static void
125 sp_genericellipse_init(SPGenericEllipse *ellipse)
126 {
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;
135 }
137 static void
138 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
139 {
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);
155 }
157 #define C1 0.552
159 /* fixme: Think (Lauris) */
161 static void sp_genericellipse_set_shape(SPShape *shape)
162 {
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);
253 }
255 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
256 {
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
265 }
267 void
268 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
269 {
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 */
280 }
282 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
283 {
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;
305 }
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)
321 {
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;
339 }
341 static void sp_ellipse_class_init(SPEllipseClass *klass)
342 {
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;
353 }
355 static void
356 sp_ellipse_init(SPEllipse *ellipse)
357 {
358 /* Nothing special */
359 }
361 static void
362 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
363 {
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");
371 }
373 static Inkscape::XML::Node *
374 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
375 {
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;
394 }
396 static void
397 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
398 {
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 }
429 }
431 static gchar *sp_ellipse_description(SPItem *item)
432 {
433 return g_strdup(_("<b>Ellipse</b>"));
434 }
437 void
438 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
439 {
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);
453 }
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)
469 {
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;
487 }
489 static void
490 sp_circle_class_init(SPCircleClass *klass)
491 {
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;
502 }
504 static void
505 sp_circle_init(SPCircle *circle)
506 {
507 /* Nothing special */
508 }
510 static void
511 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
512 {
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");
519 }
521 static Inkscape::XML::Node *
522 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
523 {
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;
541 }
543 static void
544 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
545 {
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 }
571 }
573 static gchar *sp_circle_description(SPItem *item)
574 {
575 return g_strdup(_("<b>Circle</b>"));
576 }
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)
594 {
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;
612 }
614 static void
615 sp_arc_class_init(SPArcClass *klass)
616 {
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;
628 }
630 static void
631 sp_arc_init(SPArc *arc)
632 {
633 /* Nothing special */
634 }
636 static void
637 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
638 {
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");
652 }
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)
664 {
665 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
667 Inkscape::SVG::PathString str;
669 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
670 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
671 double rx = ge->rx.computed;
672 double ry = ge->ry.computed;
674 str.moveTo(p1);
676 double dt = fmod(ge->end - ge->start, SP_2PI);
677 if (fabs(dt) < 1e-6) {
678 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
679 str.arcTo(rx, ry, 0, true, true, ph)
680 .arcTo(rx, ry, 0, true, true, p2)
681 .closePath();
682 } else {
683 bool fa = (fabs(dt) > M_PI);
684 bool fs = (dt > 0);
685 str.arcTo(rx, ry, 0, fa, fs, p2);
686 if (ge->closed) {
687 NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
688 str.lineTo(center).closePath();
689 }
690 }
692 repr->setAttribute("d", str.c_str());
693 return true;
694 }
696 static Inkscape::XML::Node *
697 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
698 {
699 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
700 SPArc *arc = SP_ARC(object);
702 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
703 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
704 repr = xml_doc->createElement("svg:path");
705 }
707 if (flags & SP_OBJECT_WRITE_EXT) {
708 repr->setAttribute("sodipodi:type", "arc");
709 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
710 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
711 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
712 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
714 // write start and end only if they are non-trivial; otherwise remove
715 gdouble len = fmod(ge->end - ge->start, SP_2PI);
716 if (len < 0.0) len += SP_2PI;
717 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
718 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
719 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
720 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
721 } else {
722 repr->setAttribute("sodipodi:end", NULL);
723 repr->setAttribute("sodipodi:start", NULL);
724 repr->setAttribute("sodipodi:open", NULL);
725 }
726 }
728 // write d=
729 sp_arc_set_elliptical_path_attribute(arc, repr);
731 if (((SPObjectClass *) arc_parent_class)->write)
732 ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
734 return repr;
735 }
737 static void
738 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
739 {
740 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
742 switch (key) {
743 case SP_ATTR_SODIPODI_CX:
744 ge->cx.readOrUnset(value);
745 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
746 break;
747 case SP_ATTR_SODIPODI_CY:
748 ge->cy.readOrUnset(value);
749 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
750 break;
751 case SP_ATTR_SODIPODI_RX:
752 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
753 ge->rx.unset();
754 }
755 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
756 break;
757 case SP_ATTR_SODIPODI_RY:
758 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
759 ge->ry.unset();
760 }
761 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
762 break;
763 case SP_ATTR_SODIPODI_START:
764 if (value) {
765 sp_svg_number_read_d(value, &ge->start);
766 } else {
767 ge->start = 0;
768 }
769 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
770 break;
771 case SP_ATTR_SODIPODI_END:
772 if (value) {
773 sp_svg_number_read_d(value, &ge->end);
774 } else {
775 ge->end = 2 * M_PI;
776 }
777 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
778 break;
779 case SP_ATTR_SODIPODI_OPEN:
780 ge->closed = (!value);
781 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
782 break;
783 default:
784 if (((SPObjectClass *) arc_parent_class)->set)
785 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
786 break;
787 }
788 }
790 static void
791 sp_arc_modified(SPObject *object, guint flags)
792 {
793 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
794 sp_shape_set_shape((SPShape *) object);
795 }
797 if (((SPObjectClass *) arc_parent_class)->modified)
798 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
799 }
801 static gchar *sp_arc_description(SPItem *item)
802 {
803 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
805 gdouble len = fmod(ge->end - ge->start, SP_2PI);
806 if (len < 0.0) len += SP_2PI;
807 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
808 if (ge->closed) {
809 return g_strdup(_("<b>Segment</b>"));
810 } else {
811 return g_strdup(_("<b>Arc</b>"));
812 }
813 } else {
814 return g_strdup(_("<b>Ellipse</b>"));
815 }
816 }
818 void
819 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
820 {
821 g_return_if_fail(arc != NULL);
822 g_return_if_fail(SP_IS_ARC(arc));
824 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
826 ge->cx.computed = x;
827 ge->cy.computed = y;
828 ge->rx.computed = rx;
829 ge->ry.computed = ry;
830 if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
831 ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
832 if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
833 ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
834 if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
835 ge->closed = 1;
836 else
837 ge->closed = 0;
839 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
840 }
842 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
843 {
844 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
846 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
847 ge->ry.computed * sin(arg) + ge->cy.computed);
848 }
851 /*
852 Local Variables:
853 mode:c++
854 c-file-style:"stroustrup"
855 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
856 indent-tabs-mode:nil
857 fill-column:99
858 End:
859 */
860 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :