diff options
Diffstat (limited to 'src/sp-ellipse.cpp')
| -rw-r--r-- | src/sp-ellipse.cpp | 1225 |
1 files changed, 510 insertions, 715 deletions
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index d74eaa6fb..25e4c07d7 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -9,6 +9,7 @@ * * Copyright (C) 1999-2002 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2013 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -24,16 +25,45 @@ #include "style.h" #include "display/curve.h" #include <glibmm/i18n.h> +#include <2geom/angle.h> +#include <2geom/ellipse.h> #include <2geom/transforms.h> #include <2geom/pathvector.h> +#include <2geom/path-sink.h> #include "document.h" #include "sp-ellipse.h" #include "preferences.h" #include "snap-candidate.h" -/* Common parent class */ +#include "sp-factory.h" + +namespace { +SPObject *create_ellipse() +{ + SPGenericEllipse *ellipse = new SPGenericEllipse(); + ellipse->type = SP_GENERIC_ELLIPSE_ELLIPSE; + return (SPObject*)ellipse; +} + +SPObject *create_circle() +{ + SPGenericEllipse *circle = new SPGenericEllipse(); + circle->type = SP_GENERIC_ELLIPSE_CIRCLE; + return (SPObject*)circle; +} + +SPObject *create_arc() +{ + SPGenericEllipse *arc = new SPGenericEllipse(); + arc->type = SP_GENERIC_ELLIPSE_ARC; + return (SPObject*)arc; +} + +bool ellipse_registered = SPFactory::instance().registerObject("svg:ellipse", create_ellipse); +bool circle_registered = SPFactory::instance().registerObject("svg:circle", create_circle); +bool arc_registered = SPFactory::instance().registerObject("arc", create_arc); +} -#define noELLIPSE_VERBOSE #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -41,883 +71,648 @@ #define SP_2PI (2 * M_PI) -#if 1 -/* Hmmm... shouldn't this also qualify */ -/* Whether it is faster or not, well, nobody knows */ -#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m))) -#else -/* we do not use C99 round(3) function yet */ -static double sp_round(double x, double y) +SPGenericEllipse::SPGenericEllipse() + : SPShape() + , start(0) + , end(SP_2PI) + , type(SP_GENERIC_ELLIPSE_UNDEFINED) + , _closed(true) { - double remain; - - g_assert(y > 0.0); - - /* return round(x/y) * y; */ - - remain = fmod(x, y); - - if (remain >= 0.5*y) - return x - remain + y; - else - return x - remain; } -#endif -static void sp_genericellipse_class_init(SPGenericEllipseClass *klass); -static void sp_genericellipse_init(SPGenericEllipse *ellipse); +SPGenericEllipse::~SPGenericEllipse() +{ +} -static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags); +void SPGenericEllipse::setClosed(bool value) { + _closed = value; +} -static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); +bool SPGenericEllipse::closed() { + return _closed; +} -static void sp_genericellipse_set_shape(SPShape *shape); -static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write); +void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr) +{ + // std::cout << "SPGenericEllipse::build: Entrance: " << this->type + // << " (" << g_quark_to_string(repr->code()) << ")" << std::endl; -static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, - guint flags); + switch ( type ) { + case SP_GENERIC_ELLIPSE_ARC: + this->readAttr("sodipodi:cx"); + this->readAttr("sodipodi:cy"); + this->readAttr("sodipodi:rx"); + this->readAttr("sodipodi:ry"); + this->readAttr("sodipodi:start"); + this->readAttr("sodipodi:end"); + this->readAttr("sodipodi:open"); + break; -static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr); + case SP_GENERIC_ELLIPSE_CIRCLE: + this->readAttr("cx"); + this->readAttr("cy"); + this->readAttr("r"); + break; -static SPShapeClass *ge_parent_class; + case SP_GENERIC_ELLIPSE_ELLIPSE: + this->readAttr("cx"); + this->readAttr("cy"); + this->readAttr("rx"); + this->readAttr("ry"); + break; -GType -sp_genericellipse_get_type(void) -{ - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof(SPGenericEllipseClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_genericellipse_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(SPGenericEllipse), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_genericellipse_init, - NULL, /* value_table */ - }; - type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0); + default: + std::cerr << "SPGenericEllipse::build() unknown defined type." << std::endl; } - return type; + + SPShape::build(document, repr); } -static void sp_genericellipse_class_init(SPGenericEllipseClass *klass) +void SPGenericEllipse::set(unsigned int key, gchar const *value) { - SPObjectClass *sp_object_class = (SPObjectClass *) klass; - SPItemClass *item_class = (SPItemClass *) klass; - SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass; - SPShapeClass *shape_class = (SPShapeClass *) klass; - - ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE); + // There are multiple ways to set internal cx, cy, rx, and ry (via SVG attributes or Sodipodi + // attributes) thus we don't want to unset them if a read fails (e.g., when we explicitly clear + // an attribute by setting it to NULL). + SVGLength t; + switch (key) { + case SP_ATTR_CX: + case SP_ATTR_SODIPODI_CX: + if( t.read(value) ) cx = t; + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_CY: + case SP_ATTR_SODIPODI_CY: + if( t.read(value) ) cy = t; + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_RX: + case SP_ATTR_SODIPODI_RX: + if( t.read(value) && t.value > 0.0 ) this->rx = t; + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_RY: + case SP_ATTR_SODIPODI_RY: + if( t.read(value) && t.value > 0.0 ) this->ry = t; + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_R: + if( t.read(value) && t.value > 0.0 ) { + this->ry = this->rx = t; + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; - sp_object_class->update = sp_genericellipse_update; - sp_object_class->write = sp_genericellipse_write; + case SP_ATTR_SODIPODI_START: + if (value) { + sp_svg_number_read_d(value, &this->start); + } else { + this->start = 0; + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; - item_class->snappoints = sp_genericellipse_snappoints; + case SP_ATTR_SODIPODI_END: + if (value) { + sp_svg_number_read_d(value, &this->end); + } else { + this->end = 2 * M_PI; + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; - shape_class->set_shape = sp_genericellipse_set_shape; - lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect; -} + case SP_ATTR_SODIPODI_OPEN: + this->_closed = (!value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; -static void -sp_genericellipse_init(SPGenericEllipse *ellipse) -{ - ellipse->cx.unset(); - ellipse->cy.unset(); - ellipse->rx.unset(); - ellipse->ry.unset(); - - ellipse->start = 0.0; - ellipse->end = SP_2PI; - ellipse->closed = TRUE; + default: + SPShape::set(key, value); + break; + } } -static void -sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags) +void SPGenericEllipse::update(SPCtx *ctx, guint flags) { + // std::cout << "\nSPGenericEllipse::update: Entrance" << std::endl; if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { - SPGenericEllipse *ellipse = (SPGenericEllipse *) object; - SPStyle const *style = object->style; Geom::Rect const &viewbox = ((SPItemCtx const *) ctx)->viewport; double const dx = viewbox.width(); double const dy = viewbox.height(); - double const dr = sqrt(dx*dx + dy*dy)/sqrt(2); - double const em = style->font_size.computed; + double const dr = hypot(dx, dy) / sqrt(2); + double const em = this->style->font_size.computed; double const ex = em * 0.5; // fixme: get from pango or libnrtype - ellipse->cx.update(em, ex, dx); - ellipse->cy.update(em, ex, dy); - ellipse->rx.update(em, ex, dr); - ellipse->ry.update(em, ex, dr); - static_cast<SPShape *>(object)->setShape(); - } - if (((SPObjectClass *) ge_parent_class)->update) - ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags); -} + this->cx.update(em, ex, dx); + this->cy.update(em, ex, dy); + this->rx.update(em, ex, dr); + this->ry.update(em, ex, dr); -static void -sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write) -{ - SPShape *shape = (SPShape *) lpeitem; - sp_genericellipse_set_shape(shape); - - if (write) { - Inkscape::XML::Node *repr = shape->getRepr(); - if ( shape->_curve != NULL ) { - gchar *str = sp_svg_write_path(shape->_curve->get_pathvector()); - repr->setAttribute("d", str); - g_free(str); - } else { - repr->setAttribute("d", NULL); - } + this->set_shape(); } - ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + SPShape::update(ctx, flags); + // std::cout << "SPGenericEllipse::update: Exit\n" << std::endl; } -/* fixme: Think (Lauris) */ -/* Can't we use arcto in this method? */ -static void sp_genericellipse_set_shape(SPShape *shape) +Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { - if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) { - 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"); - if (shape->getRepr()->attribute("d")) { - // unconditionally read the curve from d, if any, to preserve appearance - Geom::PathVector pv = sp_svg_read_pathv(shape->getRepr()->attribute("d")); - SPCurve *cold = new SPCurve(pv); - shape->setCurveInsync( cold, TRUE); - cold->unref(); - } - return; - } - - double rx, ry, s, e; - double x0, y0, x1, y1, x2, y2, x3, y3; - double len; - gint slice = FALSE; - // gint i; - - SPGenericEllipse *ellipse = (SPGenericEllipse *) shape; - - if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return; - if (fabs(ellipse->end - ellipse->start) < 1e-9) return; - - sp_genericellipse_normalize(ellipse); + // std::cout << "\nSPGenericEllipse::write: Entrance (" + // << (repr == NULL ? " NULL" : g_quark_to_string(repr->code())) + // << ")" << std::endl; - rx = ellipse->rx.computed; - ry = ellipse->ry.computed; - - // figure out if we have a slice, guarding against rounding errors - len = fmod(ellipse->end - ellipse->start, SP_2PI); - if (len < 0.0) len += SP_2PI; - if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) { - slice = FALSE; - ellipse->end = ellipse->start + SP_2PI; + GenericEllipseType new_type = SP_GENERIC_ELLIPSE_UNDEFINED; + if (this->_isSlice() || hasPathEffect() ) { + new_type = SP_GENERIC_ELLIPSE_ARC; + } else if ( rx.computed == ry.computed ) { + new_type = SP_GENERIC_ELLIPSE_CIRCLE; } else { - slice = TRUE; + new_type = SP_GENERIC_ELLIPSE_ELLIPSE; } + // std::cout << " new_type: " << new_type << std::endl; - SPCurve * curve = new SPCurve(); - curve->moveto(cos(ellipse->start), sin(ellipse->start)); - - for (s = ellipse->start; s < ellipse->end; s += M_PI_2) { - e = s + M_PI_2; - if (e > ellipse->end) - e = ellipse->end; - len = 4*tan((e - s)/4)/3; - x0 = cos(s); - y0 = sin(s); - x1 = x0 + len * cos(s + M_PI_2); - y1 = y0 + len * sin(s + M_PI_2); - x3 = cos(e); - y3 = sin(e); - x2 = x3 + len * cos(e - M_PI_2); - y2 = y3 + len * sin(e - M_PI_2); -#ifdef ELLIPSE_VERBOSE - g_print("step %d s %f e %f coords %f %f %f %f %f %f\n", - i, s, e, x1, y1, x2, y2, x3, y3); -#endif - curve->curveto(x1,y1, x2,y2, x3,y3); - } + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary? - curve->lineto(0., 0.); - } - if (ellipse->closed) { - curve->closepath(); + switch ( new_type ) { + + case SP_GENERIC_ELLIPSE_ARC: + repr = xml_doc->createElement("svg:path"); + break; + case SP_GENERIC_ELLIPSE_CIRCLE: + repr = xml_doc->createElement("svg:circle"); + break; + case SP_GENERIC_ELLIPSE_ELLIPSE: + repr = xml_doc->createElement("svg:ellipse"); + break; + case SP_GENERIC_ELLIPSE_UNDEFINED: + default: + std::cerr << "SPGenericEllipse::write(): unknown type." << std::endl; + } } - Geom::Affine aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed); - curve->transform(aff); - - /* Reset the shape's curve to the "original_curve" - * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/ - shape->setCurveInsync( curve, TRUE); - shape->setCurveBeforeLPE(curve); - - if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) { - SPCurve *c_lpe = curve->copy(); - bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe); - if (success) { - shape->setCurveInsync( c_lpe, TRUE); + if( type != new_type ) { + switch( new_type ) { + case SP_GENERIC_ELLIPSE_ARC: + repr->setCodeUnsafe(g_quark_from_string("svg:path")); + break; + case SP_GENERIC_ELLIPSE_CIRCLE: + repr->setCodeUnsafe(g_quark_from_string("svg:circle")); + break; + case SP_GENERIC_ELLIPSE_ELLIPSE: + repr->setCodeUnsafe(g_quark_from_string("svg:ellipse")); + break; + default: + std::cerr << "SPGenericEllipse::write(): unknown type." << std::endl; } - c_lpe->unref(); - } - curve->unref(); -} + type = new_type; -static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) -{ - g_assert(item != NULL); - g_assert(SP_IS_GENERICELLIPSE(item)); - - SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item); - sp_genericellipse_normalize(ellipse); - Geom::Affine const i2dt = item->i2dt_affine(); - - // figure out if we have a slice, while guarding against rounding errors - bool slice = false; - double len = fmod(ellipse->end - ellipse->start, SP_2PI); - if (len < 0.0) len += SP_2PI; - if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) { - slice = false; - ellipse->end = ellipse->start + SP_2PI; - } else { - slice = true; + // FIXME: The XML dialog won't update the element name. We need + // a notifyElementNameChanged callback added to the XML observers + // to trigger a refresh. } - double rx = ellipse->rx.computed; - double ry = ellipse->ry.computed; - double cx = ellipse->cx.computed; - double cy = ellipse->cy.computed; + // std::cout << " type: " << g_quark_to_string( repr->code() ) << std::endl; + // std::cout << " cx: " << cx.computed + // << " cy: " << cy.computed + // << " rx: " << rx.computed + // << " ry: " << ry.computed << std::endl; + + switch ( type ) { + case SP_GENERIC_ELLIPSE_UNDEFINED: + case SP_GENERIC_ELLIPSE_ARC: + + repr->setAttribute("cx", NULL ); + repr->setAttribute("cy", NULL ); + repr->setAttribute("rx", NULL ); + repr->setAttribute("ry", NULL ); + repr->setAttribute("r", NULL ); + + if (flags & SP_OBJECT_WRITE_EXT) { + + repr->setAttribute("sodipodi:type", "arc"); + sp_repr_set_svg_double(repr, "sodipodi:cx", this->cx.computed); + sp_repr_set_svg_double(repr, "sodipodi:cy", this->cy.computed); + sp_repr_set_svg_double(repr, "sodipodi:rx", this->rx.computed); + sp_repr_set_svg_double(repr, "sodipodi:ry", this->ry.computed); + + // write start and end only if they are non-trivial; otherwise remove + if (this->_isSlice()) { + sp_repr_set_svg_double(repr, "sodipodi:start", this->start); + sp_repr_set_svg_double(repr, "sodipodi:end", this->end); + + repr->setAttribute("sodipodi:open", (!this->_closed) ? "true" : NULL); + } else { + repr->setAttribute("sodipodi:end", NULL); + repr->setAttribute("sodipodi:start", NULL); + repr->setAttribute("sodipodi:open", NULL); + } + } - Geom::Point pt; + // write d= + this->set_elliptical_path_attribute(repr); + break; - // Snap to the 4 quadrant points of the ellipse, but only if the arc - // spans far enough to include them - if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)) { - double angle = 0; - for (angle = 0; angle < SP_2PI; angle += M_PI_2) { - if (angle >= ellipse->start && angle <= ellipse->end) { - pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2dt; - p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)); - } - } - } + case SP_GENERIC_ELLIPSE_CIRCLE: + sp_repr_set_svg_double(repr, "cx", this->cx.computed); + sp_repr_set_svg_double(repr, "cy", this->cy.computed); + sp_repr_set_svg_double(repr, "r", this->rx.computed); + repr->setAttribute("rx", NULL ); + repr->setAttribute("ry", NULL ); + repr->setAttribute("sodipodi:cx", NULL ); + repr->setAttribute("sodipodi:cy", NULL ); + repr->setAttribute("sodipodi:rx", NULL ); + repr->setAttribute("sodipodi:ry", NULL ); + repr->setAttribute("sodipodi:end", NULL ); + repr->setAttribute("sodipodi:start", NULL ); + repr->setAttribute("sodipodi:open", NULL ); + repr->setAttribute("sodipodi:type", NULL ); + repr->setAttribute("d", NULL ); + break; - // Add the centre, if we have a closed slice or when explicitly asked for - bool c1 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && ellipse->closed; - bool c2 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT); - if (c1 || c2) { - pt = Geom::Point(cx, cy) * i2dt; - if (c1) { - p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); - } - if (c2) { - p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT)); - } - } + case SP_GENERIC_ELLIPSE_ELLIPSE: + sp_repr_set_svg_double(repr, "cx", this->cx.computed); + sp_repr_set_svg_double(repr, "cy", this->cy.computed); + sp_repr_set_svg_double(repr, "rx", this->rx.computed); + sp_repr_set_svg_double(repr, "ry", this->ry.computed); + repr->setAttribute("r", NULL ); + repr->setAttribute("sodipodi:cx", NULL ); + repr->setAttribute("sodipodi:cy", NULL ); + repr->setAttribute("sodipodi:rx", NULL ); + repr->setAttribute("sodipodi:ry", NULL ); + repr->setAttribute("sodipodi:end", NULL ); + repr->setAttribute("sodipodi:start", NULL ); + repr->setAttribute("sodipodi:open", NULL ); + repr->setAttribute("sodipodi:type", NULL ); + repr->setAttribute("d", NULL ); + break; - // And if we have a slice, also snap to the endpoints - if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice) { - // Add the start point, if it's not coincident with a quadrant point - if (fmod(ellipse->start, M_PI_2) != 0.0 ) { - pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2dt; - p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); - } - // Add the end point, if it's not coincident with a quadrant point - if (fmod(ellipse->end, M_PI_2) != 0.0 ) { - pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2dt; - p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); - } + default: + std::cerr << "SPGenericEllipse::write: unknown type." << std::endl; } -} -void -sp_genericellipse_normalize(SPGenericEllipse *ellipse) -{ - ellipse->start = fmod(ellipse->start, SP_2PI); - ellipse->end = fmod(ellipse->end, SP_2PI); + this->set_shape(); // evaluate SPCurve - if (ellipse->start < 0.0) - ellipse->start += SP_2PI; - double diff = ellipse->start - ellipse->end; - if (diff >= 0.0) - ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI; + SPShape::write(xml_doc, repr, flags); - /* Now we keep: 0 <= start < end <= 2*PI */ + // std::cout << "SPGenericEllipse::write: Exit: " << g_quark_to_string(repr->code()) << "\n" << std::endl; + return repr; } -static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +const char *SPGenericEllipse::displayName() const { - SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object); - if (flags & SP_OBJECT_WRITE_EXT) { - if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - repr = xml_doc->createElement("svg:path"); - } + switch ( type ) { + case SP_GENERIC_ELLIPSE_UNDEFINED: + case SP_GENERIC_ELLIPSE_ARC: - sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed); - sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed); - sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed); - sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed); + if (this->_isSlice()) { + if (this->_closed) { + return _("Segment"); + } else { + return _("Arc"); + } + } else { + return _("Ellipse"); + } - if (SP_IS_ARC(ellipse)) { - sp_arc_set_elliptical_path_attribute(SP_ARC(object), object->getRepr()); - } - } + case SP_GENERIC_ELLIPSE_CIRCLE: + return _("Circle"); - if (((SPObjectClass *) ge_parent_class)->write) { - ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags); - } + case SP_GENERIC_ELLIPSE_ELLIPSE: + return _("Ellipse"); - return repr; + default: + return "Unknown ellipse: ERROR"; + } + return ("Shouldn't be here"); } -/* SVG <ellipse> element */ - -static void sp_ellipse_class_init(SPEllipseClass *klass); -static void sp_ellipse_init(SPEllipse *ellipse); - -static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); -static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value); -static gchar *sp_ellipse_description(SPItem *item); +// Create path for rendering shape on screen +void SPGenericEllipse::set_shape() +{ + // std::cout << "SPGenericEllipse::set_shape: Entrance" << std::endl; + if (hasBrokenPathEffect()) { + 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"); -static SPGenericEllipseClass *ellipse_parent_class; + if (this->getRepr()->attribute("d")) { + // unconditionally read the curve from d, if any, to preserve appearance + Geom::PathVector pv = sp_svg_read_pathv(this->getRepr()->attribute("d")); + SPCurve *cold = new SPCurve(pv); + this->setCurveInsync(cold, TRUE); + cold->unref(); + } -GType -sp_ellipse_get_type(void) -{ - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof(SPEllipseClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_ellipse_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(SPEllipse), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_ellipse_init, - NULL, /* value_table */ - }; - type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0); + return; + } + if (Geom::are_near(this->rx.computed, 0) || Geom::are_near(this->ry.computed, 0)) { + return; } - return type; -} -static void sp_ellipse_class_init(SPEllipseClass *klass) -{ - SPObjectClass *sp_object_class = (SPObjectClass *) klass; - SPItemClass *item_class = (SPItemClass *) klass; + this->normalize(); - ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE); + SPCurve *curve = NULL; - sp_object_class->build = sp_ellipse_build; - sp_object_class->write = sp_ellipse_write; - sp_object_class->set = sp_ellipse_set; + // For simplicity, we use a circle with center (0, 0) and radius 1 for our calculations. + Geom::Circle circle(0, 0, 1); - item_class->description = sp_ellipse_description; -} + if (this->_isSlice()) { + Geom::Point center(0, 0); + Geom::Point start_point = Geom::Point::polar(start); + Geom::Point end_point = Geom::Point::polar(end); + Geom::Point middle_point = make_angle_bisector_ray(Geom::Ray(center, start), Geom::Ray(center, end)).versor(); -static void -sp_ellipse_init(SPEllipse */*ellipse*/) -{ - /* Nothing special */ -} + Geom::EllipticalArc *arc = circle.arc(start_point, middle_point, end_point); -static void -sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) -{ - if (((SPObjectClass *) ellipse_parent_class)->build) - (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr); + Geom::Path path(start_point); + path.append(*arc); - object->readAttr( "cx" ); - object->readAttr( "cy" ); - object->readAttr( "rx" ); - object->readAttr( "ry" ); -} + delete arc; -static Inkscape::XML::Node * -sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) -{ - SPGenericEllipse *ellipse; + Geom::PathBuilder pb; + pb.append(path); - ellipse = SP_GENERICELLIPSE(object); + if (this->_closed) { + // "pizza slice" + pb.lineTo(center); + pb.closePath(); + } else { + // arc only + pb.flush(); + } - if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - repr = xml_doc->createElement("svg:ellipse"); + curve = new SPCurve(pb.peek()); + } else { + // Full ellipse + // This code converts the circle to four elliptical arcs explicitly. + // Circle::getPath currently creates cubic bezier curves, these are not suitable here + // as a circle should have four mid markers at 0, 90, 180, 270 degrees. + Geom::Path path(Geom::Point::polar(0)); + Geom::EllipticalArc* arc; + + arc = circle.arc(Geom::Point::polar(0), Geom::Point::polar(M_PI / 4.0), Geom::Point::polar(M_PI / 2.0)); + path.append(*arc); + delete arc; + + arc = circle.arc(Geom::Point::polar(M_PI / 2.0), Geom::Point::polar(3.0 * M_PI / 4.0), Geom::Point::polar(M_PI)); + path.append(*arc); + delete arc; + + arc = circle.arc(Geom::Point::polar(M_PI), Geom::Point::polar(5.0 * M_PI / 4.0), Geom::Point::polar(3.0 * M_PI / 2.0)); + path.append(*arc); + delete arc; + + arc = circle.arc(Geom::Point::polar(3.0 * M_PI / 2.0), Geom::Point::polar(7.0 * M_PI / 4.0), Geom::Point::polar(2.0 * M_PI)); + path.append(*arc); + delete arc; + + Geom::PathBuilder pb; + pb.append(path); + pb.closePath(); + + curve = new SPCurve(pb.peek()); } - sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed); - sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed); - sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed); - sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed); + // gchar *str = sp_svg_write_path(curve->get_pathvector()); + // std::cout << " path: " << str << std::endl; + // g_free(str); - if (((SPObjectClass *) ellipse_parent_class)->write) - (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags); + // Stretching / moving the calculated shape to fit the actual dimensions. + Geom::Affine aff = Geom::Scale(rx.computed, ry.computed) * Geom::Translate(cx.computed, cy.computed); + curve->transform(aff); - return repr; -} + /* Reset the shape's curve to the "original_curve" + * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/ + this->setCurveInsync(curve, TRUE); + this->setCurveBeforeLPE(curve); -static void -sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value) -{ - SPGenericEllipse *ellipse; + if (hasPathEffect() && pathEffectsEnabled()) { + SPCurve *c_lpe = curve->copy(); + bool success = this->performPathEffect(c_lpe); - ellipse = SP_GENERICELLIPSE(object); + if (success) { + this->setCurveInsync(c_lpe, TRUE); + } - switch (key) { - case SP_ATTR_CX: - ellipse->cx.readOrUnset(value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_CY: - ellipse->cy.readOrUnset(value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_RX: - if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) { - ellipse->rx.unset(); - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_RY: - if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) { - ellipse->ry.unset(); - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - default: - if (((SPObjectClass *) ellipse_parent_class)->set) - ((SPObjectClass *) ellipse_parent_class)->set(object, key, value); - break; + c_lpe->unref(); } -} -static gchar *sp_ellipse_description(SPItem */*item*/) -{ - return g_strdup(_("<b>Ellipse</b>")); + curve->unref(); + // std::cout << "SPGenericEllipse::set_shape: Exit" << std::endl; } - -void -sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry) +Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform) { - SPGenericEllipse *ge; - - g_return_if_fail(ellipse != NULL); - g_return_if_fail(SP_IS_ELLIPSE(ellipse)); - - ge = SP_GENERICELLIPSE(ellipse); - - ge->cx.computed = x; - ge->cy.computed = y; - ge->rx.computed = rx; - ge->ry.computed = ry; + // Allow live effects + if (hasPathEffect() && pathEffectsEnabled()) { + return xform; + } - ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); -} + /* Calculate ellipse start in parent coords. */ + Geom::Point pos(Geom::Point(this->cx.computed, this->cy.computed) * xform); -/* SVG <circle> element */ + /* This function takes care of translation and scaling, we return whatever parts we can't + handle. */ + Geom::Affine ret(Geom::Affine(xform).withoutTranslation()); + gdouble const sw = hypot(ret[0], ret[1]); + gdouble const sh = hypot(ret[2], ret[3]); -static void sp_circle_class_init(SPCircleClass *klass); -static void sp_circle_init(SPCircle *circle); + if (sw > 1e-9) { + ret[0] /= sw; + ret[1] /= sw; + } else { + ret[0] = 1.0; + ret[1] = 0.0; + } -static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); -static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value); -static gchar *sp_circle_description(SPItem *item); + if (sh > 1e-9) { + ret[2] /= sh; + ret[3] /= sh; + } else { + ret[2] = 0.0; + ret[3] = 1.0; + } -static SPGenericEllipseClass *circle_parent_class; + if (this->rx._set) { + this->rx = this->rx.computed * sw; + } -GType -sp_circle_get_type(void) -{ - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof(SPCircleClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_circle_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(SPCircle), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_circle_init, - NULL, /* value_table */ - }; - type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0); + if (this->ry._set) { + this->ry = this->ry.computed * sh; } - return type; -} -static void -sp_circle_class_init(SPCircleClass *klass) -{ - SPObjectClass *sp_object_class = (SPObjectClass *) klass; - SPItemClass *item_class = (SPItemClass *) klass; + /* Find start in item coords */ + pos = pos * ret.inverse(); + this->cx = pos[Geom::X]; + this->cy = pos[Geom::Y]; - circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE); + this->set_shape(); - sp_object_class->build = sp_circle_build; - sp_object_class->write = sp_circle_write; - sp_object_class->set = sp_circle_set; + // Adjust stroke width + this->adjust_stroke(sqrt(fabs(sw * sh))); - item_class->description = sp_circle_description; -} + // Adjust pattern fill + this->adjust_pattern(xform * ret.inverse()); -static void -sp_circle_init(SPCircle */*circle*/) -{ - /* Nothing special */ -} + // Adjust gradient fill + this->adjust_gradient(xform * ret.inverse()); -static void -sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) -{ - if (((SPObjectClass *) circle_parent_class)->build) - (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); - object->readAttr( "cx" ); - object->readAttr( "cy" ); - object->readAttr( "r" ); + return ret; } -static Inkscape::XML::Node * -sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +void SPGenericEllipse::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const { - SPGenericEllipse *ellipse; + // CPPIFY: is this call necessary? + const_cast<SPGenericEllipse*>(this)->normalize(); - ellipse = SP_GENERICELLIPSE(object); + Geom::Affine const i2dt = this->i2dt_affine(); - if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - repr = xml_doc->createElement("svg:circle"); + // Snap to the 4 quadrant points of the ellipse, but only if the arc + // spans far enough to include them + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)) { + for (double angle = 0; angle < SP_2PI; angle += M_PI_2) { + if (Geom::AngleInterval(this->start, this->end, true).contains(angle)) { + Geom::Point pt = this->getPointAtAngle(angle) * i2dt; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)); + } + } } - sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed); - sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed); - sp_repr_set_svg_double(repr, "r", ellipse->rx.computed); + double cx = this->cx.computed; + double cy = this->cy.computed; + - if (((SPObjectClass *) circle_parent_class)->write) - ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags); + bool slice = this->_isSlice(); - return repr; -} + // Add the centre, if we have a closed slice or when explicitly asked for + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && this->_closed) { + Geom::Point pt = Geom::Point(cx, cy) * i2dt; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); + } -static void -sp_circle_set(SPObject *object, unsigned int key, gchar const *value) -{ - SPGenericEllipse *ge; + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT)) { + Geom::Point pt = Geom::Point(cx, cy) * i2dt; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT)); + } - ge = SP_GENERICELLIPSE(object); + // And if we have a slice, also snap to the endpoints + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice) { + // Add the start point, if it's not coincident with a quadrant point + if (!Geom::are_near(std::fmod(this->start, M_PI_2), 0)) { + Geom::Point pt = this->getPointAtAngle(this->start) * i2dt; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); + } - switch (key) { - case SP_ATTR_CX: - ge->cx.readOrUnset(value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_CY: - ge->cy.readOrUnset(value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_R: - if (!ge->rx.read(value) || ge->rx.value <= 0.0) { - ge->rx.unset(); - } - ge->ry = ge->rx; - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - default: - if (((SPObjectClass *) circle_parent_class)->set) - ((SPObjectClass *) circle_parent_class)->set(object, key, value); - break; + // Add the end point, if it's not coincident with a quadrant point + if (!Geom::are_near(std::fmod(this->end, M_PI_2), 0)) { + Geom::Point pt = this->getPointAtAngle(this->end) * i2dt; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); + } } } -static gchar *sp_circle_description(SPItem */*item*/) +void SPGenericEllipse::modified(guint flags) { - return g_strdup(_("<b>Circle</b>")); -} - -/* <path sodipodi:type="arc"> element */ - -static void sp_arc_class_init(SPArcClass *klass); -static void sp_arc_init(SPArc *arc); - -static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); -static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value); -static void sp_arc_modified(SPObject *object, guint flags); - -static gchar *sp_arc_description(SPItem *item); - -static SPGenericEllipseClass *arc_parent_class; - -GType -sp_arc_get_type(void) -{ - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof(SPArcClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_arc_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(SPArc), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_arc_init, - NULL, /* value_table */ - }; - type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0); + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + this->set_shape(); } - return type; + + SPShape::modified(flags); } -static void -sp_arc_class_init(SPArcClass *klass) +void SPGenericEllipse::update_patheffect(bool write) { - SPObjectClass *sp_object_class = (SPObjectClass *) klass; - SPItemClass *item_class = (SPItemClass *) klass; + this->set_shape(); - arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE); + if (write) { + Inkscape::XML::Node *repr = this->getRepr(); - sp_object_class->build = sp_arc_build; - sp_object_class->write = sp_arc_write; - sp_object_class->set = sp_arc_set; - sp_object_class->modified = sp_arc_modified; + if (this->_curve != NULL) { + gchar *str = sp_svg_write_path(this->_curve->get_pathvector()); + repr->setAttribute("d", str); + g_free(str); + } else { + repr->setAttribute("d", NULL); + } + } - item_class->description = sp_arc_description; + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static void -sp_arc_init(SPArc */*arc*/) +void SPGenericEllipse::normalize() { - /* Nothing special */ + Geom::AngleInterval a(this->start, this->end, true); + + this->start = a.initialAngle().radians0(); + this->end = a.finalAngle().radians0(); } -static void -sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +Geom::Point SPGenericEllipse::getPointAtAngle(double arg) const { - if (((SPObjectClass *) arc_parent_class)->build) - (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr); - - object->readAttr( "sodipodi:cx" ); - object->readAttr( "sodipodi:cy" ); - object->readAttr( "sodipodi:rx" ); - object->readAttr( "sodipodi:ry" ); - - object->readAttr( "sodipodi:start" ); - object->readAttr( "sodipodi:end" ); - object->readAttr( "sodipodi:open" ); + return Geom::Point::polar(arg) * Geom::Scale(rx.computed, ry.computed) * Geom::Translate(cx.computed, cy.computed); } /* - * sp_arc_set_elliptical_path_attribute: + * set_elliptical_path_attribute: * * Convert center to endpoint parameterization and set it to repr. * * See SVG 1.0 Specification W3C Recommendation * ``F.6 Ellptical arc implementation notes'' for more detail. */ -static gboolean -sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr) +bool SPGenericEllipse::set_elliptical_path_attribute(Inkscape::XML::Node *repr) { - SPGenericEllipse *ge = SP_GENERICELLIPSE(arc); + // Make sure our pathvector is up to date. + this->set_shape(); - Inkscape::SVG::PathString str; + if (this->getCurve() != NULL) { + gchar* d = sp_svg_write_path(this->getCurve()->get_pathvector()); - Geom::Point p1 = sp_arc_get_xy(arc, ge->start); - Geom::Point p2 = sp_arc_get_xy(arc, ge->end); - double rx = ge->rx.computed; - double ry = ge->ry.computed; + repr->setAttribute("d", d); - str.moveTo(p1); - - double dt = fmod(ge->end - ge->start, SP_2PI); - if (fabs(dt) < 1e-6) { - Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0); - str.arcTo(rx, ry, 0, true, true, ph) - .arcTo(rx, ry, 0, true, true, p2) - .closePath(); + g_free(d); } else { - bool fa = (fabs(dt) > M_PI); - bool fs = (dt > 0); - str.arcTo(rx, ry, 0, fa, fs, p2); - if (ge->closed) { - Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed); - str.lineTo(center).closePath(); - } + repr->setAttribute("d", NULL); } - repr->setAttribute("d", str.c_str()); return true; } -static Inkscape::XML::Node * -sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +void SPGenericEllipse::position_set(gdouble x, gdouble y, gdouble rx, gdouble ry) { - SPGenericEllipse *ge = SP_GENERICELLIPSE(object); - SPArc *arc = SP_ARC(object); - - if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - repr = xml_doc->createElement("svg:path"); - } - - if (flags & SP_OBJECT_WRITE_EXT) { - repr->setAttribute("sodipodi:type", "arc"); - sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed); - sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed); - sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed); - sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed); - - // write start and end only if they are non-trivial; otherwise remove - gdouble len = fmod(ge->end - ge->start, SP_2PI); - if (len < 0.0) len += SP_2PI; - if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) { - sp_repr_set_svg_double(repr, "sodipodi:start", ge->start); - sp_repr_set_svg_double(repr, "sodipodi:end", ge->end); - repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL); - } else { - repr->setAttribute("sodipodi:end", NULL); - repr->setAttribute("sodipodi:start", NULL); - repr->setAttribute("sodipodi:open", NULL); - } - } + this->cx.computed = x; + this->cy.computed = y; + this->rx.computed = rx; + this->ry.computed = ry; - // write d= - sp_arc_set_elliptical_path_attribute(arc, repr); - - if (((SPObjectClass *) arc_parent_class)->write) - ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags); - - return repr; -} - -static void -sp_arc_set(SPObject *object, unsigned int key, gchar const *value) -{ - SPGenericEllipse *ge = SP_GENERICELLIPSE(object); - - switch (key) { - case SP_ATTR_SODIPODI_CX: - ge->cx.readOrUnset(value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_CY: - ge->cy.readOrUnset(value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_RX: - if (!ge->rx.read(value) || ge->rx.computed <= 0.0) { - ge->rx.unset(); - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_RY: - if (!ge->ry.read(value) || ge->ry.computed <= 0.0) { - ge->ry.unset(); - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_START: - if (value) { - sp_svg_number_read_d(value, &ge->start); - } else { - ge->start = 0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_END: - if (value) { - sp_svg_number_read_d(value, &ge->end); - } else { - ge->end = 2 * M_PI; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_OPEN: - ge->closed = (!value); - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - default: - if (((SPObjectClass *) arc_parent_class)->set) - ((SPObjectClass *) arc_parent_class)->set(object, key, value); - break; - } -} + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -static void -sp_arc_modified(SPObject *object, guint flags) -{ - if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { - ((SPShape *) object)->setShape(); + // those pref values are in degrees, while we want radians + if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0) { + this->start = Geom::Angle::from_degrees(prefs->getDouble("/tools/shapes/arc/start", 0.0)).radians0(); } - if (((SPObjectClass *) arc_parent_class)->modified) - ((SPObjectClass *) arc_parent_class)->modified(object, flags); -} - -static gchar *sp_arc_description(SPItem *item) -{ - SPGenericEllipse *ge = SP_GENERICELLIPSE(item); - - gdouble len = fmod(ge->end - ge->start, SP_2PI); - if (len < 0.0) len += SP_2PI; - if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) { - if (ge->closed) { - return g_strdup(_("<b>Segment</b>")); - } else { - return g_strdup(_("<b>Arc</b>")); - } - } else { - return g_strdup(_("<b>Ellipse</b>")); + if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0) { + this->end = Geom::Angle::from_degrees(prefs->getDouble("/tools/shapes/arc/end", 0.0)).radians0(); } -} - -void -sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry) -{ - g_return_if_fail(arc != NULL); - g_return_if_fail(SP_IS_ARC(arc)); - SPGenericEllipse *ge = SP_GENERICELLIPSE(arc); + this->_closed = !prefs->getBool("/tools/shapes/arc/open"); - ge->cx.computed = x; - ge->cy.computed = y; - ge->rx.computed = rx; - ge->ry.computed = ry; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - // those pref values are in degrees, while we want radians - if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0) - ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0) * M_PI / 180; - if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0) - ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0) * M_PI / 180; - if (!prefs->getBool("/tools/shapes/arc/open")) - ge->closed = 1; - else - ge->closed = 0; - - ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg) +bool SPGenericEllipse::_isSlice() const { - SPGenericEllipse *ge = SP_GENERICELLIPSE(arc); + Geom::AngleInterval a(this->start, this->end, true); - return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed, - ge->ry.computed * sin(arg) + ge->cy.computed); + return !(Geom::are_near(a.extent(), 0) || Geom::are_near(a.extent(), SP_2PI)); } - /* Local Variables: mode:c++ |
