83c3e288d624e96c8ab9c488829613c951d33b1c
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 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 rx = ellipse->rx.computed;
205 ry = ellipse->ry.computed;
207 // figure out if we have a slice, guarding against rounding errors
208 len = fmod(ellipse->end - ellipse->start, SP_2PI);
209 if (len < 0.0) len += SP_2PI;
210 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
211 slice = FALSE;
212 ellipse->end = ellipse->start + SP_2PI;
213 } else {
214 slice = TRUE;
215 }
217 NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
218 aff[4] = ellipse->cx.computed;
219 aff[5] = ellipse->cy.computed;
221 NArtBpath bpath[16];
222 i = 0;
223 if (ellipse->closed) {
224 bpath[i].code = NR_MOVETO;
225 } else {
226 bpath[i].code = NR_MOVETO_OPEN;
227 }
228 bpath[i].x3 = cos(ellipse->start);
229 bpath[i].y3 = sin(ellipse->start);
230 i++;
232 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
233 e = s + M_PI_2;
234 if (e > ellipse->end)
235 e = ellipse->end;
236 len = C1 * (e - s) / M_PI_2;
237 x0 = cos(s);
238 y0 = sin(s);
239 x1 = x0 + len * cos(s + M_PI_2);
240 y1 = y0 + len * sin(s + M_PI_2);
241 x3 = cos(e);
242 y3 = sin(e);
243 x2 = x3 + len * cos(e - M_PI_2);
244 y2 = y3 + len * sin(e - M_PI_2);
245 #ifdef ELLIPSE_VERBOSE
246 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
247 i, s, e, x1, y1, x2, y2, x3, y3);
248 #endif
249 bpath[i].code = NR_CURVETO;
250 bpath[i].x1 = x1;
251 bpath[i].y1 = y1;
252 bpath[i].x2 = x2;
253 bpath[i].y2 = y2;
254 bpath[i].x3 = x3;
255 bpath[i].y3 = y3;
256 i++;
257 }
259 if (slice && ellipse->closed) {
260 bpath[i].code = NR_LINETO;
261 bpath[i].x3 = 0.0;
262 bpath[i].y3 = 0.0;
263 i++;
264 bpath[i].code = NR_LINETO;
265 bpath[i].x3 = bpath[0].x3;
266 bpath[i].y3 = bpath[0].y3;
267 i++;
268 } else if (ellipse->closed) {
269 bpath[i-1].x3 = bpath[0].x3;
270 bpath[i-1].y3 = bpath[0].y3;
271 }
273 bpath[i].code = NR_END;
274 SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff));
275 g_assert(c != NULL);
277 sp_shape_perform_path_effect(c, SP_SHAPE (ellipse));
278 sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
279 sp_curve_unref(c);
280 }
282 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
283 {
284 g_assert(item != NULL);
285 g_assert(SP_IS_GENERICELLIPSE(item));
287 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
288 sp_genericellipse_normalize(ellipse);
289 NR::Matrix const i2d = sp_item_i2d_affine(item);
291 // figure out if we have a slice, whilst guarding against rounding errors
292 bool slice = false;
293 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
294 if (len < 0.0) len += SP_2PI;
295 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
296 slice = false;
297 ellipse->end = ellipse->start + SP_2PI;
298 } else {
299 slice = true;
300 }
302 double rx = ellipse->rx.computed;
303 double ry = ellipse->ry.computed;
304 double cx = ellipse->cx.computed;
305 double cy = ellipse->cy.computed;
307 // Snap to the 4 quadrant points of the ellipse, but only if the arc
308 // spans far enough to include them
309 double angle = 0;
310 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
311 if (angle >= ellipse->start && angle <= ellipse->end) {
312 *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
313 }
314 }
316 // And if we have a slice, also snap to the endpoints and the centre point
317 if (slice) {
318 // Add the centre, if we have a closed slice
319 if (ellipse->closed) {
320 *p = NR::Point(cx, cy) * i2d;
321 }
322 // Add the start point, if it's not coincident with a quadrant point
323 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
324 *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
325 }
326 // Add the end point, if it's not coincident with a quadrant point
327 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
328 *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
329 }
330 }
331 }
333 void
334 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
335 {
336 ellipse->start = fmod(ellipse->start, SP_2PI);
337 ellipse->end = fmod(ellipse->end, SP_2PI);
339 if (ellipse->start < 0.0)
340 ellipse->start += SP_2PI;
341 double diff = ellipse->start - ellipse->end;
342 if (diff >= 0.0)
343 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
345 /* Now we keep: 0 <= start < end <= 2*PI */
346 }
348 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
349 {
350 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
352 if (flags & SP_OBJECT_WRITE_EXT) {
353 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
354 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
355 repr = xml_doc->createElement("svg:path");
356 }
358 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
359 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
360 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
361 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
363 if (SP_IS_ARC(ellipse))
364 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
365 }
367 if (((SPObjectClass *) ge_parent_class)->write)
368 ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
370 return repr;
371 }
373 /* SVG <ellipse> element */
375 static void sp_ellipse_class_init(SPEllipseClass *klass);
376 static void sp_ellipse_init(SPEllipse *ellipse);
378 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
379 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
380 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
381 static gchar *sp_ellipse_description(SPItem *item);
383 static SPGenericEllipseClass *ellipse_parent_class;
385 GType
386 sp_ellipse_get_type(void)
387 {
388 static GType type = 0;
389 if (!type) {
390 GTypeInfo info = {
391 sizeof(SPEllipseClass),
392 NULL, /* base_init */
393 NULL, /* base_finalize */
394 (GClassInitFunc) sp_ellipse_class_init,
395 NULL, /* class_finalize */
396 NULL, /* class_data */
397 sizeof(SPEllipse),
398 16, /* n_preallocs */
399 (GInstanceInitFunc) sp_ellipse_init,
400 NULL, /* value_table */
401 };
402 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
403 }
404 return type;
405 }
407 static void sp_ellipse_class_init(SPEllipseClass *klass)
408 {
409 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
410 SPItemClass *item_class = (SPItemClass *) klass;
412 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
414 sp_object_class->build = sp_ellipse_build;
415 sp_object_class->write = sp_ellipse_write;
416 sp_object_class->set = sp_ellipse_set;
418 item_class->description = sp_ellipse_description;
419 }
421 static void
422 sp_ellipse_init(SPEllipse */*ellipse*/)
423 {
424 /* Nothing special */
425 }
427 static void
428 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
429 {
430 g_print ("sp_ellipse_build\n");
431 if (((SPObjectClass *) ellipse_parent_class)->build)
432 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
434 sp_object_read_attr(object, "cx");
435 sp_object_read_attr(object, "cy");
436 sp_object_read_attr(object, "rx");
437 sp_object_read_attr(object, "ry");
438 }
440 static Inkscape::XML::Node *
441 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
442 {
443 SPGenericEllipse *ellipse;
445 ellipse = SP_GENERICELLIPSE(object);
447 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
448 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
449 repr = xml_doc->createElement("svg:ellipse");
450 }
452 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
453 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
454 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
455 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
457 if (((SPObjectClass *) ellipse_parent_class)->write)
458 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
460 return repr;
461 }
463 static void
464 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
465 {
466 SPGenericEllipse *ellipse;
468 ellipse = SP_GENERICELLIPSE(object);
470 switch (key) {
471 case SP_ATTR_CX:
472 ellipse->cx.readOrUnset(value);
473 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
474 break;
475 case SP_ATTR_CY:
476 ellipse->cy.readOrUnset(value);
477 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
478 break;
479 case SP_ATTR_RX:
480 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
481 ellipse->rx.unset();
482 }
483 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
484 break;
485 case SP_ATTR_RY:
486 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
487 ellipse->ry.unset();
488 }
489 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
490 break;
491 default:
492 if (((SPObjectClass *) ellipse_parent_class)->set)
493 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
494 break;
495 }
496 }
498 static gchar *sp_ellipse_description(SPItem */*item*/)
499 {
500 return g_strdup(_("<b>Ellipse</b>"));
501 }
504 void
505 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
506 {
507 SPGenericEllipse *ge;
509 g_return_if_fail(ellipse != NULL);
510 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
512 ge = SP_GENERICELLIPSE(ellipse);
514 ge->cx.computed = x;
515 ge->cy.computed = y;
516 ge->rx.computed = rx;
517 ge->ry.computed = ry;
519 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
520 }
522 /* SVG <circle> element */
524 static void sp_circle_class_init(SPCircleClass *klass);
525 static void sp_circle_init(SPCircle *circle);
527 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
528 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
529 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
530 static gchar *sp_circle_description(SPItem *item);
532 static SPGenericEllipseClass *circle_parent_class;
534 GType
535 sp_circle_get_type(void)
536 {
537 static GType type = 0;
538 if (!type) {
539 GTypeInfo info = {
540 sizeof(SPCircleClass),
541 NULL, /* base_init */
542 NULL, /* base_finalize */
543 (GClassInitFunc) sp_circle_class_init,
544 NULL, /* class_finalize */
545 NULL, /* class_data */
546 sizeof(SPCircle),
547 16, /* n_preallocs */
548 (GInstanceInitFunc) sp_circle_init,
549 NULL, /* value_table */
550 };
551 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
552 }
553 return type;
554 }
556 static void
557 sp_circle_class_init(SPCircleClass *klass)
558 {
559 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
560 SPItemClass *item_class = (SPItemClass *) klass;
562 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
564 sp_object_class->build = sp_circle_build;
565 sp_object_class->write = sp_circle_write;
566 sp_object_class->set = sp_circle_set;
568 item_class->description = sp_circle_description;
569 }
571 static void
572 sp_circle_init(SPCircle */*circle*/)
573 {
574 /* Nothing special */
575 }
577 static void
578 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
579 {
580 if (((SPObjectClass *) circle_parent_class)->build)
581 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
583 sp_object_read_attr(object, "cx");
584 sp_object_read_attr(object, "cy");
585 sp_object_read_attr(object, "r");
586 }
588 static Inkscape::XML::Node *
589 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
590 {
591 SPGenericEllipse *ellipse;
593 ellipse = SP_GENERICELLIPSE(object);
595 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
596 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
597 repr = xml_doc->createElement("svg:circle");
598 }
600 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
601 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
602 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
604 if (((SPObjectClass *) circle_parent_class)->write)
605 ((SPObjectClass *) circle_parent_class)->write(object, repr, flags);
607 return repr;
608 }
610 static void
611 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
612 {
613 SPGenericEllipse *ge;
615 ge = SP_GENERICELLIPSE(object);
617 switch (key) {
618 case SP_ATTR_CX:
619 ge->cx.readOrUnset(value);
620 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
621 break;
622 case SP_ATTR_CY:
623 ge->cy.readOrUnset(value);
624 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
625 break;
626 case SP_ATTR_R:
627 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
628 ge->rx.unset();
629 }
630 ge->ry = ge->rx;
631 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
632 break;
633 default:
634 if (((SPObjectClass *) circle_parent_class)->set)
635 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
636 break;
637 }
638 }
640 static gchar *sp_circle_description(SPItem */*item*/)
641 {
642 return g_strdup(_("<b>Circle</b>"));
643 }
645 /* <path sodipodi:type="arc"> element */
647 static void sp_arc_class_init(SPArcClass *klass);
648 static void sp_arc_init(SPArc *arc);
650 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
651 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
652 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
653 static void sp_arc_modified(SPObject *object, guint flags);
655 static gchar *sp_arc_description(SPItem *item);
657 static SPGenericEllipseClass *arc_parent_class;
659 GType
660 sp_arc_get_type(void)
661 {
662 static GType type = 0;
663 if (!type) {
664 GTypeInfo info = {
665 sizeof(SPArcClass),
666 NULL, /* base_init */
667 NULL, /* base_finalize */
668 (GClassInitFunc) sp_arc_class_init,
669 NULL, /* class_finalize */
670 NULL, /* class_data */
671 sizeof(SPArc),
672 16, /* n_preallocs */
673 (GInstanceInitFunc) sp_arc_init,
674 NULL, /* value_table */
675 };
676 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
677 }
678 return type;
679 }
681 static void
682 sp_arc_class_init(SPArcClass *klass)
683 {
684 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
685 SPItemClass *item_class = (SPItemClass *) klass;
687 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
689 sp_object_class->build = sp_arc_build;
690 sp_object_class->write = sp_arc_write;
691 sp_object_class->set = sp_arc_set;
692 sp_object_class->modified = sp_arc_modified;
694 item_class->description = sp_arc_description;
695 }
697 static void
698 sp_arc_init(SPArc */*arc*/)
699 {
700 /* Nothing special */
701 }
703 static void
704 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
705 {
706 if (((SPObjectClass *) arc_parent_class)->build)
707 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
709 Inkscape::Version version = sp_object_get_sodipodi_version(object);
711 sp_object_read_attr(object, "sodipodi:cx");
712 sp_object_read_attr(object, "sodipodi:cy");
713 sp_object_read_attr(object, "sodipodi:rx");
714 sp_object_read_attr(object, "sodipodi:ry");
716 sp_object_read_attr(object, "sodipodi:start");
717 sp_object_read_attr(object, "sodipodi:end");
718 sp_object_read_attr(object, "sodipodi:open");
719 }
721 /*
722 * sp_arc_set_elliptical_path_attribute:
723 *
724 * Convert center to endpoint parameterization and set it to repr.
725 *
726 * See SVG 1.0 Specification W3C Recommendation
727 * ``F.6 Ellptical arc implementation notes'' for more detail.
728 */
729 static gboolean
730 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
731 {
732 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
734 Inkscape::SVG::PathString str;
736 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
737 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
738 double rx = ge->rx.computed;
739 double ry = ge->ry.computed;
741 str.moveTo(p1);
743 double dt = fmod(ge->end - ge->start, SP_2PI);
744 if (fabs(dt) < 1e-6) {
745 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
746 str.arcTo(rx, ry, 0, true, true, ph)
747 .arcTo(rx, ry, 0, true, true, p2)
748 .closePath();
749 } else {
750 bool fa = (fabs(dt) > M_PI);
751 bool fs = (dt > 0);
752 str.arcTo(rx, ry, 0, fa, fs, p2);
753 if (ge->closed) {
754 NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
755 str.lineTo(center).closePath();
756 }
757 }
759 repr->setAttribute("d", str.c_str());
760 return true;
761 }
763 static Inkscape::XML::Node *
764 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
765 {
766 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
767 SPArc *arc = SP_ARC(object);
769 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
770 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
771 repr = xml_doc->createElement("svg:path");
772 }
774 if (flags & SP_OBJECT_WRITE_EXT) {
775 repr->setAttribute("sodipodi:type", "arc");
776 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
777 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
778 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
779 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
781 // write start and end only if they are non-trivial; otherwise remove
782 gdouble len = fmod(ge->end - ge->start, SP_2PI);
783 if (len < 0.0) len += SP_2PI;
784 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
785 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
786 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
787 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
788 } else {
789 repr->setAttribute("sodipodi:end", NULL);
790 repr->setAttribute("sodipodi:start", NULL);
791 repr->setAttribute("sodipodi:open", NULL);
792 }
793 }
795 // write d=
796 sp_arc_set_elliptical_path_attribute(arc, repr);
798 if (((SPObjectClass *) arc_parent_class)->write)
799 ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
801 return repr;
802 }
804 static void
805 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
806 {
807 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
809 switch (key) {
810 case SP_ATTR_SODIPODI_CX:
811 ge->cx.readOrUnset(value);
812 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
813 break;
814 case SP_ATTR_SODIPODI_CY:
815 ge->cy.readOrUnset(value);
816 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
817 break;
818 case SP_ATTR_SODIPODI_RX:
819 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
820 ge->rx.unset();
821 }
822 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
823 break;
824 case SP_ATTR_SODIPODI_RY:
825 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
826 ge->ry.unset();
827 }
828 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
829 break;
830 case SP_ATTR_SODIPODI_START:
831 if (value) {
832 sp_svg_number_read_d(value, &ge->start);
833 } else {
834 ge->start = 0;
835 }
836 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
837 break;
838 case SP_ATTR_SODIPODI_END:
839 if (value) {
840 sp_svg_number_read_d(value, &ge->end);
841 } else {
842 ge->end = 2 * M_PI;
843 }
844 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
845 break;
846 case SP_ATTR_SODIPODI_OPEN:
847 ge->closed = (!value);
848 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
849 break;
850 default:
851 if (((SPObjectClass *) arc_parent_class)->set)
852 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
853 break;
854 }
855 }
857 static void
858 sp_arc_modified(SPObject *object, guint flags)
859 {
860 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
861 sp_shape_set_shape((SPShape *) object);
862 }
864 if (((SPObjectClass *) arc_parent_class)->modified)
865 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
866 }
868 static gchar *sp_arc_description(SPItem *item)
869 {
870 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
872 gdouble len = fmod(ge->end - ge->start, SP_2PI);
873 if (len < 0.0) len += SP_2PI;
874 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
875 if (ge->closed) {
876 return g_strdup(_("<b>Segment</b>"));
877 } else {
878 return g_strdup(_("<b>Arc</b>"));
879 }
880 } else {
881 return g_strdup(_("<b>Ellipse</b>"));
882 }
883 }
885 void
886 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
887 {
888 g_return_if_fail(arc != NULL);
889 g_return_if_fail(SP_IS_ARC(arc));
891 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
893 ge->cx.computed = x;
894 ge->cy.computed = y;
895 ge->rx.computed = rx;
896 ge->ry.computed = ry;
897 if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
898 ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
899 if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
900 ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
901 if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
902 ge->closed = 1;
903 else
904 ge->closed = 0;
906 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
907 }
909 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
910 {
911 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
913 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
914 ge->ry.computed * sin(arg) + ge->cy.computed);
915 }
918 /*
919 Local Variables:
920 mode:c++
921 c-file-style:"stroustrup"
922 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
923 indent-tabs-mode:nil
924 fill-column:99
925 End:
926 */
927 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :