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>
31 #include <2geom/pathvector.h>
33 #include "document.h"
34 #include "sp-ellipse.h"
36 #include "preferences.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, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs);
78 static void sp_genericellipse_set_shape(SPShape *shape);
79 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
81 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, 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 SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass;
115 SPShapeClass *shape_class = (SPShapeClass *) klass;
117 ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
119 sp_object_class->update = sp_genericellipse_update;
120 sp_object_class->write = sp_genericellipse_write;
122 item_class->snappoints = sp_genericellipse_snappoints;
124 shape_class->set_shape = sp_genericellipse_set_shape;
125 lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect;
126 }
128 static void
129 sp_genericellipse_init(SPGenericEllipse *ellipse)
130 {
131 ellipse->cx.unset();
132 ellipse->cy.unset();
133 ellipse->rx.unset();
134 ellipse->ry.unset();
136 ellipse->start = 0.0;
137 ellipse->end = SP_2PI;
138 ellipse->closed = TRUE;
139 }
141 static void
142 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
143 {
144 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
145 SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
146 SPStyle const *style = object->style;
147 Geom::OptRect viewbox = ((SPItemCtx const *) ctx)->vp;
148 double const dx = viewbox->width();
149 double const dy = viewbox->height();
150 double const dr = sqrt(dx*dx + dy*dy)/sqrt(2);
151 double const em = style->font_size.computed;
152 double const ex = em * 0.5; // fixme: get from pango or libnrtype
153 ellipse->cx.update(em, ex, dx);
154 ellipse->cy.update(em, ex, dy);
155 ellipse->rx.update(em, ex, dr);
156 ellipse->ry.update(em, ex, dr);
157 sp_shape_set_shape((SPShape *) object);
158 }
160 if (((SPObjectClass *) ge_parent_class)->update)
161 ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
162 }
164 static void
165 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
166 {
167 SPShape *shape = (SPShape *) lpeitem;
168 sp_genericellipse_set_shape(shape);
170 if (write) {
171 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
172 if ( shape->curve != NULL ) {
173 gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
174 repr->setAttribute("d", str);
175 g_free(str);
176 } else {
177 repr->setAttribute("d", NULL);
178 }
179 }
181 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
182 }
184 /* fixme: Think (Lauris) */
185 /* Can't we use arcto in this method? */
186 static void sp_genericellipse_set_shape(SPShape *shape)
187 {
188 if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) {
189 g_warning ("The ellipse shape has unknown LPE on it! Convert to path to make it editable preserving the appearance; editing it as ellipse will remove the bad LPE");
190 if (SP_OBJECT_REPR(shape)->attribute("d")) {
191 // unconditionally read the curve from d, if any, to preserve appearance
192 Geom::PathVector pv = sp_svg_read_pathv(SP_OBJECT_REPR(shape)->attribute("d"));
193 SPCurve *cold = new SPCurve(pv);
194 sp_shape_set_curve_insync (shape, cold, TRUE);
195 cold->unref();
196 }
197 return;
198 }
200 double rx, ry, s, e;
201 double x0, y0, x1, y1, x2, y2, x3, y3;
202 double len;
203 gint slice = FALSE;
204 // gint i;
206 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
208 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
209 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
211 sp_genericellipse_normalize(ellipse);
213 rx = ellipse->rx.computed;
214 ry = ellipse->ry.computed;
216 // figure out if we have a slice, guarding against rounding errors
217 len = fmod(ellipse->end - ellipse->start, SP_2PI);
218 if (len < 0.0) len += SP_2PI;
219 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
220 slice = FALSE;
221 ellipse->end = ellipse->start + SP_2PI;
222 } else {
223 slice = TRUE;
224 }
226 SPCurve * curve = new SPCurve();
227 curve->moveto(cos(ellipse->start), sin(ellipse->start));
229 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
230 e = s + M_PI_2;
231 if (e > ellipse->end)
232 e = ellipse->end;
233 len = 4*tan((e - s)/4)/3;
234 x0 = cos(s);
235 y0 = sin(s);
236 x1 = x0 + len * cos(s + M_PI_2);
237 y1 = y0 + len * sin(s + M_PI_2);
238 x3 = cos(e);
239 y3 = sin(e);
240 x2 = x3 + len * cos(e - M_PI_2);
241 y2 = y3 + len * sin(e - M_PI_2);
242 #ifdef ELLIPSE_VERBOSE
243 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
244 i, s, e, x1, y1, x2, y2, x3, y3);
245 #endif
246 curve->curveto(x1,y1, x2,y2, x3,y3);
247 }
249 if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary?
250 curve->lineto(0., 0.);
251 }
252 if (ellipse->closed) {
253 curve->closepath();
254 }
256 Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
257 curve->transform(aff);
259 /* Reset the shape'scurve to the "original_curve"
260 * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
261 sp_shape_set_curve_insync (shape, curve, TRUE);
262 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
263 SPCurve *c_lpe = curve->copy();
264 bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
265 if (success) {
266 sp_shape_set_curve_insync (shape, c_lpe, TRUE);
267 }
268 c_lpe->unref();
269 }
270 curve->unref();
271 }
273 static void sp_genericellipse_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs)
274 {
275 g_assert(item != NULL);
276 g_assert(SP_IS_GENERICELLIPSE(item));
278 // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
279 if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
280 return;
281 }
283 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
284 sp_genericellipse_normalize(ellipse);
285 Geom::Matrix const i2d = sp_item_i2d_affine(item);
287 // figure out if we have a slice, while guarding against rounding errors
288 bool slice = false;
289 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
290 if (len < 0.0) len += SP_2PI;
291 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
292 slice = false;
293 ellipse->end = ellipse->start + SP_2PI;
294 } else {
295 slice = true;
296 }
298 double rx = ellipse->rx.computed;
299 double ry = ellipse->ry.computed;
300 double cx = ellipse->cx.computed;
301 double cy = ellipse->cy.computed;
303 Geom::Point pt;
305 // Snap to the 4 quadrant points of the ellipse, but only if the arc
306 // spans far enough to include them
307 if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this?
308 double angle = 0;
309 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
310 if (angle >= ellipse->start && angle <= ellipse->end) {
311 pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
312 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) : int(Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT)));
313 }
314 }
315 }
317 // Add the centre, if we have a closed slice or when explicitly asked for
318 if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) {
319 pt = Geom::Point(cx, cy) * i2d;
320 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_CENTER) : int(Inkscape::SNAPSOURCE_CENTER)));
321 }
323 // And if we have a slice, also snap to the endpoints
324 if (snapprefs->getSnapToItemNode() && slice) {
325 // Add the start point, if it's not coincident with a quadrant point
326 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
327 pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
328 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP)));
329 }
330 // Add the end point, if it's not coincident with a quadrant point
331 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
332 pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
333 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP)));
334 }
335 }
336 }
338 void
339 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
340 {
341 ellipse->start = fmod(ellipse->start, SP_2PI);
342 ellipse->end = fmod(ellipse->end, SP_2PI);
344 if (ellipse->start < 0.0)
345 ellipse->start += SP_2PI;
346 double diff = ellipse->start - ellipse->end;
347 if (diff >= 0.0)
348 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
350 /* Now we keep: 0 <= start < end <= 2*PI */
351 }
353 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
354 {
355 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
357 if (flags & SP_OBJECT_WRITE_EXT) {
358 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
359 repr = xml_doc->createElement("svg:path");
360 }
362 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
363 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
364 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
365 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
367 if (SP_IS_ARC(ellipse))
368 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
369 }
371 if (((SPObjectClass *) ge_parent_class)->write)
372 ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
374 return repr;
375 }
377 /* SVG <ellipse> element */
379 static void sp_ellipse_class_init(SPEllipseClass *klass);
380 static void sp_ellipse_init(SPEllipse *ellipse);
382 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
383 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
384 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
385 static gchar *sp_ellipse_description(SPItem *item);
387 static SPGenericEllipseClass *ellipse_parent_class;
389 GType
390 sp_ellipse_get_type(void)
391 {
392 static GType type = 0;
393 if (!type) {
394 GTypeInfo info = {
395 sizeof(SPEllipseClass),
396 NULL, /* base_init */
397 NULL, /* base_finalize */
398 (GClassInitFunc) sp_ellipse_class_init,
399 NULL, /* class_finalize */
400 NULL, /* class_data */
401 sizeof(SPEllipse),
402 16, /* n_preallocs */
403 (GInstanceInitFunc) sp_ellipse_init,
404 NULL, /* value_table */
405 };
406 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
407 }
408 return type;
409 }
411 static void sp_ellipse_class_init(SPEllipseClass *klass)
412 {
413 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
414 SPItemClass *item_class = (SPItemClass *) klass;
416 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
418 sp_object_class->build = sp_ellipse_build;
419 sp_object_class->write = sp_ellipse_write;
420 sp_object_class->set = sp_ellipse_set;
422 item_class->description = sp_ellipse_description;
423 }
425 static void
426 sp_ellipse_init(SPEllipse */*ellipse*/)
427 {
428 /* Nothing special */
429 }
431 static void
432 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
433 {
434 if (((SPObjectClass *) ellipse_parent_class)->build)
435 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
437 sp_object_read_attr(object, "cx");
438 sp_object_read_attr(object, "cy");
439 sp_object_read_attr(object, "rx");
440 sp_object_read_attr(object, "ry");
441 }
443 static Inkscape::XML::Node *
444 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
445 {
446 SPGenericEllipse *ellipse;
448 ellipse = SP_GENERICELLIPSE(object);
450 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
451 repr = xml_doc->createElement("svg:ellipse");
452 }
454 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
455 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
456 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
457 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
459 if (((SPObjectClass *) ellipse_parent_class)->write)
460 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
462 return repr;
463 }
465 static void
466 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
467 {
468 SPGenericEllipse *ellipse;
470 ellipse = SP_GENERICELLIPSE(object);
472 switch (key) {
473 case SP_ATTR_CX:
474 ellipse->cx.readOrUnset(value);
475 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
476 break;
477 case SP_ATTR_CY:
478 ellipse->cy.readOrUnset(value);
479 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
480 break;
481 case SP_ATTR_RX:
482 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
483 ellipse->rx.unset();
484 }
485 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
486 break;
487 case SP_ATTR_RY:
488 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
489 ellipse->ry.unset();
490 }
491 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
492 break;
493 default:
494 if (((SPObjectClass *) ellipse_parent_class)->set)
495 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
496 break;
497 }
498 }
500 static gchar *sp_ellipse_description(SPItem */*item*/)
501 {
502 return g_strdup(_("<b>Ellipse</b>"));
503 }
506 void
507 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
508 {
509 SPGenericEllipse *ge;
511 g_return_if_fail(ellipse != NULL);
512 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
514 ge = SP_GENERICELLIPSE(ellipse);
516 ge->cx.computed = x;
517 ge->cy.computed = y;
518 ge->rx.computed = rx;
519 ge->ry.computed = ry;
521 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
522 }
524 /* SVG <circle> element */
526 static void sp_circle_class_init(SPCircleClass *klass);
527 static void sp_circle_init(SPCircle *circle);
529 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
530 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
531 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
532 static gchar *sp_circle_description(SPItem *item);
534 static SPGenericEllipseClass *circle_parent_class;
536 GType
537 sp_circle_get_type(void)
538 {
539 static GType type = 0;
540 if (!type) {
541 GTypeInfo info = {
542 sizeof(SPCircleClass),
543 NULL, /* base_init */
544 NULL, /* base_finalize */
545 (GClassInitFunc) sp_circle_class_init,
546 NULL, /* class_finalize */
547 NULL, /* class_data */
548 sizeof(SPCircle),
549 16, /* n_preallocs */
550 (GInstanceInitFunc) sp_circle_init,
551 NULL, /* value_table */
552 };
553 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
554 }
555 return type;
556 }
558 static void
559 sp_circle_class_init(SPCircleClass *klass)
560 {
561 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
562 SPItemClass *item_class = (SPItemClass *) klass;
564 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
566 sp_object_class->build = sp_circle_build;
567 sp_object_class->write = sp_circle_write;
568 sp_object_class->set = sp_circle_set;
570 item_class->description = sp_circle_description;
571 }
573 static void
574 sp_circle_init(SPCircle */*circle*/)
575 {
576 /* Nothing special */
577 }
579 static void
580 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
581 {
582 if (((SPObjectClass *) circle_parent_class)->build)
583 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
585 sp_object_read_attr(object, "cx");
586 sp_object_read_attr(object, "cy");
587 sp_object_read_attr(object, "r");
588 }
590 static Inkscape::XML::Node *
591 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
592 {
593 SPGenericEllipse *ellipse;
595 ellipse = SP_GENERICELLIPSE(object);
597 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
598 repr = xml_doc->createElement("svg:circle");
599 }
601 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
602 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
603 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
605 if (((SPObjectClass *) circle_parent_class)->write)
606 ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
608 return repr;
609 }
611 static void
612 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
613 {
614 SPGenericEllipse *ge;
616 ge = SP_GENERICELLIPSE(object);
618 switch (key) {
619 case SP_ATTR_CX:
620 ge->cx.readOrUnset(value);
621 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
622 break;
623 case SP_ATTR_CY:
624 ge->cy.readOrUnset(value);
625 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
626 break;
627 case SP_ATTR_R:
628 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
629 ge->rx.unset();
630 }
631 ge->ry = ge->rx;
632 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
633 break;
634 default:
635 if (((SPObjectClass *) circle_parent_class)->set)
636 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
637 break;
638 }
639 }
641 static gchar *sp_circle_description(SPItem */*item*/)
642 {
643 return g_strdup(_("<b>Circle</b>"));
644 }
646 /* <path sodipodi:type="arc"> element */
648 static void sp_arc_class_init(SPArcClass *klass);
649 static void sp_arc_init(SPArc *arc);
651 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
652 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
653 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
654 static void sp_arc_modified(SPObject *object, guint flags);
656 static gchar *sp_arc_description(SPItem *item);
658 static SPGenericEllipseClass *arc_parent_class;
660 GType
661 sp_arc_get_type(void)
662 {
663 static GType type = 0;
664 if (!type) {
665 GTypeInfo info = {
666 sizeof(SPArcClass),
667 NULL, /* base_init */
668 NULL, /* base_finalize */
669 (GClassInitFunc) sp_arc_class_init,
670 NULL, /* class_finalize */
671 NULL, /* class_data */
672 sizeof(SPArc),
673 16, /* n_preallocs */
674 (GInstanceInitFunc) sp_arc_init,
675 NULL, /* value_table */
676 };
677 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
678 }
679 return type;
680 }
682 static void
683 sp_arc_class_init(SPArcClass *klass)
684 {
685 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
686 SPItemClass *item_class = (SPItemClass *) klass;
688 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
690 sp_object_class->build = sp_arc_build;
691 sp_object_class->write = sp_arc_write;
692 sp_object_class->set = sp_arc_set;
693 sp_object_class->modified = sp_arc_modified;
695 item_class->description = sp_arc_description;
696 }
698 static void
699 sp_arc_init(SPArc */*arc*/)
700 {
701 /* Nothing special */
702 }
704 static void
705 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
706 {
707 if (((SPObjectClass *) arc_parent_class)->build)
708 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
710 sp_object_read_attr(object, "sodipodi:cx");
711 sp_object_read_attr(object, "sodipodi:cy");
712 sp_object_read_attr(object, "sodipodi:rx");
713 sp_object_read_attr(object, "sodipodi:ry");
715 sp_object_read_attr(object, "sodipodi:start");
716 sp_object_read_attr(object, "sodipodi:end");
717 sp_object_read_attr(object, "sodipodi:open");
718 }
720 /*
721 * sp_arc_set_elliptical_path_attribute:
722 *
723 * Convert center to endpoint parameterization and set it to repr.
724 *
725 * See SVG 1.0 Specification W3C Recommendation
726 * ``F.6 Ellptical arc implementation notes'' for more detail.
727 */
728 static gboolean
729 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
730 {
731 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
733 Inkscape::SVG::PathString str;
735 Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
736 Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
737 double rx = ge->rx.computed;
738 double ry = ge->ry.computed;
740 str.moveTo(p1);
742 double dt = fmod(ge->end - ge->start, SP_2PI);
743 if (fabs(dt) < 1e-6) {
744 Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
745 str.arcTo(rx, ry, 0, true, true, ph)
746 .arcTo(rx, ry, 0, true, true, p2)
747 .closePath();
748 } else {
749 bool fa = (fabs(dt) > M_PI);
750 bool fs = (dt > 0);
751 str.arcTo(rx, ry, 0, fa, fs, p2);
752 if (ge->closed) {
753 Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
754 str.lineTo(center).closePath();
755 }
756 }
758 repr->setAttribute("d", str.c_str());
759 return true;
760 }
762 static Inkscape::XML::Node *
763 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
764 {
765 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
766 SPArc *arc = SP_ARC(object);
768 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
769 repr = xml_doc->createElement("svg:path");
770 }
772 if (flags & SP_OBJECT_WRITE_EXT) {
773 repr->setAttribute("sodipodi:type", "arc");
774 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
775 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
776 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
777 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
779 // write start and end only if they are non-trivial; otherwise remove
780 gdouble len = fmod(ge->end - ge->start, SP_2PI);
781 if (len < 0.0) len += SP_2PI;
782 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
783 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
784 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
785 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
786 } else {
787 repr->setAttribute("sodipodi:end", NULL);
788 repr->setAttribute("sodipodi:start", NULL);
789 repr->setAttribute("sodipodi:open", NULL);
790 }
791 }
793 // write d=
794 sp_arc_set_elliptical_path_attribute(arc, repr);
796 if (((SPObjectClass *) arc_parent_class)->write)
797 ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
799 return repr;
800 }
802 static void
803 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
804 {
805 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
807 switch (key) {
808 case SP_ATTR_SODIPODI_CX:
809 ge->cx.readOrUnset(value);
810 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
811 break;
812 case SP_ATTR_SODIPODI_CY:
813 ge->cy.readOrUnset(value);
814 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
815 break;
816 case SP_ATTR_SODIPODI_RX:
817 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
818 ge->rx.unset();
819 }
820 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
821 break;
822 case SP_ATTR_SODIPODI_RY:
823 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
824 ge->ry.unset();
825 }
826 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
827 break;
828 case SP_ATTR_SODIPODI_START:
829 if (value) {
830 sp_svg_number_read_d(value, &ge->start);
831 } else {
832 ge->start = 0;
833 }
834 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
835 break;
836 case SP_ATTR_SODIPODI_END:
837 if (value) {
838 sp_svg_number_read_d(value, &ge->end);
839 } else {
840 ge->end = 2 * M_PI;
841 }
842 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
843 break;
844 case SP_ATTR_SODIPODI_OPEN:
845 ge->closed = (!value);
846 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
847 break;
848 default:
849 if (((SPObjectClass *) arc_parent_class)->set)
850 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
851 break;
852 }
853 }
855 static void
856 sp_arc_modified(SPObject *object, guint flags)
857 {
858 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
859 sp_shape_set_shape((SPShape *) object);
860 }
862 if (((SPObjectClass *) arc_parent_class)->modified)
863 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
864 }
866 static gchar *sp_arc_description(SPItem *item)
867 {
868 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
870 gdouble len = fmod(ge->end - ge->start, SP_2PI);
871 if (len < 0.0) len += SP_2PI;
872 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
873 if (ge->closed) {
874 return g_strdup(_("<b>Segment</b>"));
875 } else {
876 return g_strdup(_("<b>Arc</b>"));
877 }
878 } else {
879 return g_strdup(_("<b>Ellipse</b>"));
880 }
881 }
883 void
884 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
885 {
886 g_return_if_fail(arc != NULL);
887 g_return_if_fail(SP_IS_ARC(arc));
889 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
891 ge->cx.computed = x;
892 ge->cy.computed = y;
893 ge->rx.computed = rx;
894 ge->ry.computed = ry;
895 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
896 if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
897 ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0);
898 if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
899 ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0);
900 if (!prefs->getBool("/tools/shapes/arc/open"))
901 ge->closed = 1;
902 else
903 ge->closed = 0;
905 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
906 }
908 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
909 {
910 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
912 return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
913 ge->ry.computed * sin(arg) + ge->cy.computed);
914 }
917 /*
918 Local Variables:
919 mode:c++
920 c-file-style:"stroustrup"
921 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
922 indent-tabs-mode:nil
923 fill-column:99
924 End:
925 */
926 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :