e1284f61ec0d1838dfe98f04e617b6d39c191716
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/nr-matrix-fns.h"
23 #include "svg/svg.h"
24 #include "svg/path-string.h"
25 #include "xml/repr.h"
26 #include "attributes.h"
27 #include "style.h"
28 #include "display/curve.h"
29 #include <glibmm/i18n.h>
30 #include <2geom/transforms.h>
32 #include "document.h"
33 #include "sp-ellipse.h"
35 #include "preferences.h"
37 /* Common parent class */
39 #define noELLIPSE_VERBOSE
41 #ifndef M_PI
42 #define M_PI 3.14159265358979323846
43 #endif
45 #define SP_2PI (2 * M_PI)
47 #if 1
48 /* Hmmm... shouldn't this also qualify */
49 /* Whether it is faster or not, well, nobody knows */
50 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
51 #else
52 /* we do not use C99 round(3) function yet */
53 static double sp_round(double x, double y)
54 {
55 double remain;
57 g_assert(y > 0.0);
59 /* return round(x/y) * y; */
61 remain = fmod(x, y);
63 if (remain >= 0.5*y)
64 return x - remain + y;
65 else
66 return x - remain;
67 }
68 #endif
70 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
71 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
73 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
75 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs);
77 static void sp_genericellipse_set_shape(SPShape *shape);
78 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
80 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
81 guint flags);
83 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
85 static SPShapeClass *ge_parent_class;
87 GType
88 sp_genericellipse_get_type(void)
89 {
90 static GType type = 0;
91 if (!type) {
92 GTypeInfo info = {
93 sizeof(SPGenericEllipseClass),
94 NULL, /* base_init */
95 NULL, /* base_finalize */
96 (GClassInitFunc) sp_genericellipse_class_init,
97 NULL, /* class_finalize */
98 NULL, /* class_data */
99 sizeof(SPGenericEllipse),
100 16, /* n_preallocs */
101 (GInstanceInitFunc) sp_genericellipse_init,
102 NULL, /* value_table */
103 };
104 type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
105 }
106 return type;
107 }
109 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
110 {
111 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
112 SPItemClass *item_class = (SPItemClass *) klass;
113 SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) 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 lpe_item_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(SPLPEItem *lpeitem, bool write)
162 {
163 SPShape *shape = (SPShape *) lpeitem;
164 sp_genericellipse_set_shape(shape);
166 if (write) {
167 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
168 if ( shape->curve != NULL ) {
169 gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
170 repr->setAttribute("d", str);
171 g_free(str);
172 } else {
173 repr->setAttribute("d", NULL);
174 }
175 }
177 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
178 }
181 #define C1 0.552
183 /* fixme: Think (Lauris) */
184 /* Can't we use arcto in this method? */
185 static void sp_genericellipse_set_shape(SPShape *shape)
186 {
187 double rx, ry, s, e;
188 double x0, y0, x1, y1, x2, y2, x3, y3;
189 double len;
190 gint slice = FALSE;
191 // gint i;
193 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
195 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
196 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
198 sp_genericellipse_normalize(ellipse);
200 rx = ellipse->rx.computed;
201 ry = ellipse->ry.computed;
203 // figure out if we have a slice, guarding against rounding errors
204 len = fmod(ellipse->end - ellipse->start, SP_2PI);
205 if (len < 0.0) len += SP_2PI;
206 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
207 slice = FALSE;
208 ellipse->end = ellipse->start + SP_2PI;
209 } else {
210 slice = TRUE;
211 }
213 SPCurve * curve = new SPCurve();
214 curve->moveto(cos(ellipse->start), sin(ellipse->start));
216 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
217 e = s + M_PI_2;
218 if (e > ellipse->end)
219 e = ellipse->end;
220 len = C1 * (e - s) / M_PI_2;
221 x0 = cos(s);
222 y0 = sin(s);
223 x1 = x0 + len * cos(s + M_PI_2);
224 y1 = y0 + len * sin(s + M_PI_2);
225 x3 = cos(e);
226 y3 = sin(e);
227 x2 = x3 + len * cos(e - M_PI_2);
228 y2 = y3 + len * sin(e - M_PI_2);
229 #ifdef ELLIPSE_VERBOSE
230 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
231 i, s, e, x1, y1, x2, y2, x3, y3);
232 #endif
233 curve->curveto(x1,y1, x2,y2, x3,y3);
234 }
236 if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary?
237 curve->lineto(0., 0.);
238 }
239 if (ellipse->closed) {
240 curve->closepath();
241 }
243 Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
244 curve->transform(aff);
246 sp_lpe_item_perform_path_effect(SP_LPE_ITEM (ellipse), curve);
247 sp_shape_set_curve_insync((SPShape *) ellipse, curve, TRUE);
248 curve->unref();
249 }
251 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
252 {
253 g_assert(item != NULL);
254 g_assert(SP_IS_GENERICELLIPSE(item));
256 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
257 sp_genericellipse_normalize(ellipse);
258 NR::Matrix const i2d = sp_item_i2d_affine(item);
260 // figure out if we have a slice, whilst guarding against rounding errors
261 bool slice = false;
262 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
263 if (len < 0.0) len += SP_2PI;
264 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
265 slice = false;
266 ellipse->end = ellipse->start + SP_2PI;
267 } else {
268 slice = true;
269 }
271 double rx = ellipse->rx.computed;
272 double ry = ellipse->ry.computed;
273 double cx = ellipse->cx.computed;
274 double cy = ellipse->cy.computed;
276 // Snap to the 4 quadrant points of the ellipse, but only if the arc
277 // spans far enough to include them
278 double angle = 0;
279 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
280 if (angle >= ellipse->start && angle <= ellipse->end) {
281 *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
282 }
283 }
285 // And if we have a slice, also snap to the endpoints and the centre point
286 if (slice) {
287 // Add the centre, if we have a closed slice
288 if (ellipse->closed) {
289 *p = NR::Point(cx, cy) * i2d;
290 }
291 // Add the start point, if it's not coincident with a quadrant point
292 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
293 *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
294 }
295 // Add the end point, if it's not coincident with a quadrant point
296 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
297 *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
298 }
299 }
300 }
302 void
303 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
304 {
305 ellipse->start = fmod(ellipse->start, SP_2PI);
306 ellipse->end = fmod(ellipse->end, SP_2PI);
308 if (ellipse->start < 0.0)
309 ellipse->start += SP_2PI;
310 double diff = ellipse->start - ellipse->end;
311 if (diff >= 0.0)
312 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
314 /* Now we keep: 0 <= start < end <= 2*PI */
315 }
317 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
318 {
319 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
321 if (flags & SP_OBJECT_WRITE_EXT) {
322 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
323 repr = xml_doc->createElement("svg:path");
324 }
326 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
327 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
328 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
329 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
331 if (SP_IS_ARC(ellipse))
332 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
333 }
335 if (((SPObjectClass *) ge_parent_class)->write)
336 ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
338 return repr;
339 }
341 /* SVG <ellipse> element */
343 static void sp_ellipse_class_init(SPEllipseClass *klass);
344 static void sp_ellipse_init(SPEllipse *ellipse);
346 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
347 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
348 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
349 static gchar *sp_ellipse_description(SPItem *item);
351 static SPGenericEllipseClass *ellipse_parent_class;
353 GType
354 sp_ellipse_get_type(void)
355 {
356 static GType type = 0;
357 if (!type) {
358 GTypeInfo info = {
359 sizeof(SPEllipseClass),
360 NULL, /* base_init */
361 NULL, /* base_finalize */
362 (GClassInitFunc) sp_ellipse_class_init,
363 NULL, /* class_finalize */
364 NULL, /* class_data */
365 sizeof(SPEllipse),
366 16, /* n_preallocs */
367 (GInstanceInitFunc) sp_ellipse_init,
368 NULL, /* value_table */
369 };
370 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
371 }
372 return type;
373 }
375 static void sp_ellipse_class_init(SPEllipseClass *klass)
376 {
377 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
378 SPItemClass *item_class = (SPItemClass *) klass;
380 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
382 sp_object_class->build = sp_ellipse_build;
383 sp_object_class->write = sp_ellipse_write;
384 sp_object_class->set = sp_ellipse_set;
386 item_class->description = sp_ellipse_description;
387 }
389 static void
390 sp_ellipse_init(SPEllipse */*ellipse*/)
391 {
392 /* Nothing special */
393 }
395 static void
396 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
397 {
398 if (((SPObjectClass *) ellipse_parent_class)->build)
399 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
401 sp_object_read_attr(object, "cx");
402 sp_object_read_attr(object, "cy");
403 sp_object_read_attr(object, "rx");
404 sp_object_read_attr(object, "ry");
405 }
407 static Inkscape::XML::Node *
408 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
409 {
410 SPGenericEllipse *ellipse;
412 ellipse = SP_GENERICELLIPSE(object);
414 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
415 repr = xml_doc->createElement("svg:ellipse");
416 }
418 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
419 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
420 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
421 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
423 if (((SPObjectClass *) ellipse_parent_class)->write)
424 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
426 return repr;
427 }
429 static void
430 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
431 {
432 SPGenericEllipse *ellipse;
434 ellipse = SP_GENERICELLIPSE(object);
436 switch (key) {
437 case SP_ATTR_CX:
438 ellipse->cx.readOrUnset(value);
439 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
440 break;
441 case SP_ATTR_CY:
442 ellipse->cy.readOrUnset(value);
443 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
444 break;
445 case SP_ATTR_RX:
446 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
447 ellipse->rx.unset();
448 }
449 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
450 break;
451 case SP_ATTR_RY:
452 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
453 ellipse->ry.unset();
454 }
455 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
456 break;
457 default:
458 if (((SPObjectClass *) ellipse_parent_class)->set)
459 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
460 break;
461 }
462 }
464 static gchar *sp_ellipse_description(SPItem */*item*/)
465 {
466 return g_strdup(_("<b>Ellipse</b>"));
467 }
470 void
471 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
472 {
473 SPGenericEllipse *ge;
475 g_return_if_fail(ellipse != NULL);
476 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
478 ge = SP_GENERICELLIPSE(ellipse);
480 ge->cx.computed = x;
481 ge->cy.computed = y;
482 ge->rx.computed = rx;
483 ge->ry.computed = ry;
485 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
486 }
488 /* SVG <circle> element */
490 static void sp_circle_class_init(SPCircleClass *klass);
491 static void sp_circle_init(SPCircle *circle);
493 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
494 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
495 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
496 static gchar *sp_circle_description(SPItem *item);
498 static SPGenericEllipseClass *circle_parent_class;
500 GType
501 sp_circle_get_type(void)
502 {
503 static GType type = 0;
504 if (!type) {
505 GTypeInfo info = {
506 sizeof(SPCircleClass),
507 NULL, /* base_init */
508 NULL, /* base_finalize */
509 (GClassInitFunc) sp_circle_class_init,
510 NULL, /* class_finalize */
511 NULL, /* class_data */
512 sizeof(SPCircle),
513 16, /* n_preallocs */
514 (GInstanceInitFunc) sp_circle_init,
515 NULL, /* value_table */
516 };
517 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
518 }
519 return type;
520 }
522 static void
523 sp_circle_class_init(SPCircleClass *klass)
524 {
525 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
526 SPItemClass *item_class = (SPItemClass *) klass;
528 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
530 sp_object_class->build = sp_circle_build;
531 sp_object_class->write = sp_circle_write;
532 sp_object_class->set = sp_circle_set;
534 item_class->description = sp_circle_description;
535 }
537 static void
538 sp_circle_init(SPCircle */*circle*/)
539 {
540 /* Nothing special */
541 }
543 static void
544 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
545 {
546 if (((SPObjectClass *) circle_parent_class)->build)
547 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
549 sp_object_read_attr(object, "cx");
550 sp_object_read_attr(object, "cy");
551 sp_object_read_attr(object, "r");
552 }
554 static Inkscape::XML::Node *
555 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
556 {
557 SPGenericEllipse *ellipse;
559 ellipse = SP_GENERICELLIPSE(object);
561 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
562 repr = xml_doc->createElement("svg:circle");
563 }
565 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
566 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
567 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
569 if (((SPObjectClass *) circle_parent_class)->write)
570 ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
572 return repr;
573 }
575 static void
576 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
577 {
578 SPGenericEllipse *ge;
580 ge = SP_GENERICELLIPSE(object);
582 switch (key) {
583 case SP_ATTR_CX:
584 ge->cx.readOrUnset(value);
585 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
586 break;
587 case SP_ATTR_CY:
588 ge->cy.readOrUnset(value);
589 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
590 break;
591 case SP_ATTR_R:
592 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
593 ge->rx.unset();
594 }
595 ge->ry = ge->rx;
596 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
597 break;
598 default:
599 if (((SPObjectClass *) circle_parent_class)->set)
600 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
601 break;
602 }
603 }
605 static gchar *sp_circle_description(SPItem */*item*/)
606 {
607 return g_strdup(_("<b>Circle</b>"));
608 }
610 /* <path sodipodi:type="arc"> element */
612 static void sp_arc_class_init(SPArcClass *klass);
613 static void sp_arc_init(SPArc *arc);
615 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
616 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
617 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
618 static void sp_arc_modified(SPObject *object, guint flags);
620 static gchar *sp_arc_description(SPItem *item);
622 static SPGenericEllipseClass *arc_parent_class;
624 GType
625 sp_arc_get_type(void)
626 {
627 static GType type = 0;
628 if (!type) {
629 GTypeInfo info = {
630 sizeof(SPArcClass),
631 NULL, /* base_init */
632 NULL, /* base_finalize */
633 (GClassInitFunc) sp_arc_class_init,
634 NULL, /* class_finalize */
635 NULL, /* class_data */
636 sizeof(SPArc),
637 16, /* n_preallocs */
638 (GInstanceInitFunc) sp_arc_init,
639 NULL, /* value_table */
640 };
641 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
642 }
643 return type;
644 }
646 static void
647 sp_arc_class_init(SPArcClass *klass)
648 {
649 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
650 SPItemClass *item_class = (SPItemClass *) klass;
652 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
654 sp_object_class->build = sp_arc_build;
655 sp_object_class->write = sp_arc_write;
656 sp_object_class->set = sp_arc_set;
657 sp_object_class->modified = sp_arc_modified;
659 item_class->description = sp_arc_description;
660 }
662 static void
663 sp_arc_init(SPArc */*arc*/)
664 {
665 /* Nothing special */
666 }
668 static void
669 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
670 {
671 if (((SPObjectClass *) arc_parent_class)->build)
672 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
674 Inkscape::Version version = sp_object_get_sodipodi_version(object);
676 sp_object_read_attr(object, "sodipodi:cx");
677 sp_object_read_attr(object, "sodipodi:cy");
678 sp_object_read_attr(object, "sodipodi:rx");
679 sp_object_read_attr(object, "sodipodi:ry");
681 sp_object_read_attr(object, "sodipodi:start");
682 sp_object_read_attr(object, "sodipodi:end");
683 sp_object_read_attr(object, "sodipodi:open");
684 }
686 /*
687 * sp_arc_set_elliptical_path_attribute:
688 *
689 * Convert center to endpoint parameterization and set it to repr.
690 *
691 * See SVG 1.0 Specification W3C Recommendation
692 * ``F.6 Ellptical arc implementation notes'' for more detail.
693 */
694 static gboolean
695 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
696 {
697 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
699 Inkscape::SVG::PathString str;
701 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
702 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
703 double rx = ge->rx.computed;
704 double ry = ge->ry.computed;
706 str.moveTo(p1);
708 double dt = fmod(ge->end - ge->start, SP_2PI);
709 if (fabs(dt) < 1e-6) {
710 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
711 str.arcTo(rx, ry, 0, true, true, ph)
712 .arcTo(rx, ry, 0, true, true, p2)
713 .closePath();
714 } else {
715 bool fa = (fabs(dt) > M_PI);
716 bool fs = (dt > 0);
717 str.arcTo(rx, ry, 0, fa, fs, p2);
718 if (ge->closed) {
719 NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
720 str.lineTo(center).closePath();
721 }
722 }
724 repr->setAttribute("d", str.c_str());
725 return true;
726 }
728 static Inkscape::XML::Node *
729 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
730 {
731 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
732 SPArc *arc = SP_ARC(object);
734 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
735 repr = xml_doc->createElement("svg:path");
736 }
738 if (flags & SP_OBJECT_WRITE_EXT) {
739 repr->setAttribute("sodipodi:type", "arc");
740 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
741 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
742 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
743 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
745 // write start and end only if they are non-trivial; otherwise remove
746 gdouble len = fmod(ge->end - ge->start, SP_2PI);
747 if (len < 0.0) len += SP_2PI;
748 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
749 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
750 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
751 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
752 } else {
753 repr->setAttribute("sodipodi:end", NULL);
754 repr->setAttribute("sodipodi:start", NULL);
755 repr->setAttribute("sodipodi:open", NULL);
756 }
757 }
759 // write d=
760 sp_arc_set_elliptical_path_attribute(arc, repr);
762 if (((SPObjectClass *) arc_parent_class)->write)
763 ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
765 return repr;
766 }
768 static void
769 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
770 {
771 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
773 switch (key) {
774 case SP_ATTR_SODIPODI_CX:
775 ge->cx.readOrUnset(value);
776 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
777 break;
778 case SP_ATTR_SODIPODI_CY:
779 ge->cy.readOrUnset(value);
780 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
781 break;
782 case SP_ATTR_SODIPODI_RX:
783 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
784 ge->rx.unset();
785 }
786 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
787 break;
788 case SP_ATTR_SODIPODI_RY:
789 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
790 ge->ry.unset();
791 }
792 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
793 break;
794 case SP_ATTR_SODIPODI_START:
795 if (value) {
796 sp_svg_number_read_d(value, &ge->start);
797 } else {
798 ge->start = 0;
799 }
800 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
801 break;
802 case SP_ATTR_SODIPODI_END:
803 if (value) {
804 sp_svg_number_read_d(value, &ge->end);
805 } else {
806 ge->end = 2 * M_PI;
807 }
808 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
809 break;
810 case SP_ATTR_SODIPODI_OPEN:
811 ge->closed = (!value);
812 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
813 break;
814 default:
815 if (((SPObjectClass *) arc_parent_class)->set)
816 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
817 break;
818 }
819 }
821 static void
822 sp_arc_modified(SPObject *object, guint flags)
823 {
824 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
825 sp_shape_set_shape((SPShape *) object);
826 }
828 if (((SPObjectClass *) arc_parent_class)->modified)
829 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
830 }
832 static gchar *sp_arc_description(SPItem *item)
833 {
834 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
836 gdouble len = fmod(ge->end - ge->start, SP_2PI);
837 if (len < 0.0) len += SP_2PI;
838 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
839 if (ge->closed) {
840 return g_strdup(_("<b>Segment</b>"));
841 } else {
842 return g_strdup(_("<b>Arc</b>"));
843 }
844 } else {
845 return g_strdup(_("<b>Ellipse</b>"));
846 }
847 }
849 void
850 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
851 {
852 g_return_if_fail(arc != NULL);
853 g_return_if_fail(SP_IS_ARC(arc));
855 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
857 ge->cx.computed = x;
858 ge->cy.computed = y;
859 ge->rx.computed = rx;
860 ge->ry.computed = ry;
861 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
862 if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
863 ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0);
864 if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
865 ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0);
866 if (!prefs->getBool("/tools/shapes/arc/open"))
867 ge->closed = 1;
868 else
869 ge->closed = 0;
871 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
872 }
874 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
875 {
876 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
878 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
879 ge->ry.computed * sin(arg) + ge->cy.computed);
880 }
883 /*
884 Local Variables:
885 mode:c++
886 c-file-style:"stroustrup"
887 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
888 indent-tabs-mode:nil
889 fill-column:99
890 End:
891 */
892 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :