From c35eeadff685fb93343ee334ec6da857c95c8288 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Thu, 2 Mar 2017 14:08:17 +0100 Subject: Implement arc "chord" type. (Matches Krita "chord" arc type.) (bzr r15561) --- share/icons/icons.svg | 10 +++-- src/attributes.cpp | 3 +- src/attributes.h | 1 + src/sp-ellipse.cpp | 75 +++++++++++++++++++++++++++---------- src/sp-ellipse.h | 17 +++++---- src/ui/object-edit.cpp | 8 +++- src/widgets/arc-toolbar.cpp | 91 ++++++++++++++++++++++++++++----------------- 7 files changed, 139 insertions(+), 66 deletions(-) diff --git a/share/icons/icons.svg b/share/icons/icons.svg index 3fd4e8bf6..4556054bf 100644 --- a/share/icons/icons.svg +++ b/share/icons/icons.svg @@ -3055,11 +3055,15 @@ http://www.inkscape.org/ - + - - + + + + + + diff --git a/src/attributes.cpp b/src/attributes.cpp index 8154bfbbe..5b3d35590 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -174,7 +174,8 @@ static SPStyleProp const props[] = { {SP_ATTR_SODIPODI_RY, "sodipodi:ry"}, {SP_ATTR_SODIPODI_START, "sodipodi:start"}, {SP_ATTR_SODIPODI_END, "sodipodi:end"}, - {SP_ATTR_SODIPODI_OPEN, "sodipodi:open"}, + {SP_ATTR_SODIPODI_OPEN, "sodipodi:open"}, // Deprecated + {SP_ATTR_SODIPODI_ARC_TYPE, "sodipodi:arc-type"}, /* SPStar */ {SP_ATTR_SODIPODI_SIDES, "sodipodi:sides"}, {SP_ATTR_SODIPODI_R1, "sodipodi:r1"}, diff --git a/src/attributes.h b/src/attributes.h index e21851bcf..6d4ac7543 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -182,6 +182,7 @@ enum SPAttributeEnum { SP_ATTR_SODIPODI_START, SP_ATTR_SODIPODI_END, SP_ATTR_SODIPODI_OPEN, + SP_ATTR_SODIPODI_ARC_TYPE, /* SPStar */ SP_ATTR_SODIPODI_SIDES, SP_ATTR_SODIPODI_R1, diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index ed1e2b504..5dfc60f1a 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -39,7 +39,7 @@ SPGenericEllipse::SPGenericEllipse() , start(0) , end(SP_2PI) , type(SP_GENERIC_ELLIPSE_UNDEFINED) - , _closed(true) + , arc_type(SP_GENERIC_ELLIPSE_ARC_TYPE_ARC) { } @@ -47,14 +47,6 @@ SPGenericEllipse::~SPGenericEllipse() { } -void SPGenericEllipse::setClosed(bool value) { - _closed = value; -} - -bool SPGenericEllipse::closed() { - return _closed; -} - void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr) { // std::cout << "SPGenericEllipse::build: Entrance: " << this->type @@ -69,6 +61,7 @@ void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr) this->readAttr("sodipodi:start"); this->readAttr("sodipodi:end"); this->readAttr("sodipodi:open"); + this->readAttr("sodipodi:arc-type"); break; case SP_GENERIC_ELLIPSE_CIRCLE: @@ -166,7 +159,25 @@ void SPGenericEllipse::set(unsigned int key, gchar const *value) break; case SP_ATTR_SODIPODI_OPEN: - this->_closed = (!value); + // This is only for reading in old files so rely on constructor to set default. + if (!value) { // Only set if not "true" + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE; + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_SODIPODI_ARC_TYPE: + // To read in old files that use 'open', we need to not set if value is null. + // We could also check inkscape version. + if (value) { + if (!strcmp(value,"arc")) { + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_ARC; + } else if (!strcmp(value,"chord")) { + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD; + } else { + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE; + } + } this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -285,11 +296,28 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I sp_repr_set_svg_double(repr, "sodipodi:start", start); sp_repr_set_svg_double(repr, "sodipodi:end", end); - repr->setAttribute("sodipodi:open", (!_closed) ? "true" : NULL); + switch ( arc_type ) { + case SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE: + repr->setAttribute("sodipodi:open", NULL); // For backwards compat. + repr->setAttribute("sodipodi:arc-type", "slice"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD: + // A chord's path isn't "open" but its fill most closely resembles an arc. + repr->setAttribute("sodipodi:open", "true"); // For backwards compat. + repr->setAttribute("sodipodi:arc-type", "chord"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_ARC: + repr->setAttribute("sodipodi:open", "true"); // For backwards compat. + repr->setAttribute("sodipodi:arc-type", "arc"); + break; + default: + std::cerr << "SPGenericEllipse::write: unknown arc-type." << std::endl; + } } else { repr->setAttribute("sodipodi:end", NULL); repr->setAttribute("sodipodi:start", NULL); repr->setAttribute("sodipodi:open", NULL); + repr->setAttribute("sodipodi:arc-type", NULL); } } @@ -310,6 +338,7 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I repr->setAttribute("sodipodi:end", NULL ); repr->setAttribute("sodipodi:start", NULL ); repr->setAttribute("sodipodi:open", NULL ); + repr->setAttribute("sodipodi:arc-type", NULL); repr->setAttribute("sodipodi:type", NULL ); repr->setAttribute("d", NULL ); break; @@ -327,6 +356,7 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I repr->setAttribute("sodipodi:end", NULL ); repr->setAttribute("sodipodi:start", NULL ); repr->setAttribute("sodipodi:open", NULL ); + repr->setAttribute("sodipodi:arc-type", NULL); repr->setAttribute("sodipodi:type", NULL ); repr->setAttribute("d", NULL ); break; @@ -350,10 +380,16 @@ const char *SPGenericEllipse::displayName() const case SP_GENERIC_ELLIPSE_ARC: if (_isSlice()) { - if (_closed) { - return _("Segment"); - } else { - return _("Arc"); + switch ( arc_type ) { + case SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE: + return _("Slice"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD: + return _("Chord"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_ARC: + return _("Arc"); + break; } } else { return _("Ellipse"); @@ -419,10 +455,10 @@ void SPGenericEllipse::set_shape() } Geom::PathBuilder pb; pb.append(path); - if (this->_isSlice() && this->_closed) { + if (this->_isSlice() && this->arc_type == SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE) { pb.lineTo(Geom::Point(0, 0)); } - if (this->_closed) { + if ( !(this->arc_type == SP_GENERIC_ELLIPSE_ARC_TYPE_ARC) ) { pb.closePath(); } else { pb.flush(); @@ -543,7 +579,8 @@ void SPGenericEllipse::snappoints(std::vector &p, bool slice = this->_isSlice(); // Add the centre, if we have a closed slice or when explicitly asked for - if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && this->_closed) { + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && + this->arc_type == SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE) { Geom::Point pt = Geom::Point(cx, cy) * i2dt; p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } @@ -654,7 +691,7 @@ void SPGenericEllipse::position_set(gdouble x, gdouble y, gdouble rx, gdouble ry this->end = Geom::Angle::from_degrees(prefs->getDouble("/tools/shapes/arc/end", 0.0)).radians0(); } - this->_closed = !prefs->getBool("/tools/shapes/arc/open"); + this->arc_type = (GenericEllipseArcType)prefs->getInt("/tools/shapes/arc/arc_type", 0); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/sp-ellipse.h b/src/sp-ellipse.h index 09768fd6d..dafece625 100644 --- a/src/sp-ellipse.h +++ b/src/sp-ellipse.h @@ -30,6 +30,12 @@ enum GenericEllipseType { SP_GENERIC_ELLIPSE_ELLIPSE }; +enum GenericEllipseArcType { + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE, // Default + SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD, + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC +}; + class SPGenericEllipse : public SPShape { public: SPGenericEllipse(); @@ -42,14 +48,13 @@ public: SVGLength rx; SVGLength ry; - /** - * If we have a slice, returns whether the shape is closed ("pizza slice") or not (arc only). - */ - bool closed(); - void setClosed(bool value); + // Return slice, chord, or arc. + GenericEllipseArcType arcType() { return arc_type; }; + void setArcType(GenericEllipseArcType type) { arc_type = type; }; double start, end; GenericEllipseType type; + GenericEllipseArcType arc_type; virtual void build(SPDocument *document, Inkscape::XML::Node *repr); @@ -83,8 +88,6 @@ protected: * @brief Determines whether the shape is a part of an ellipse. */ bool _isSlice() const; - - bool _closed; }; #endif diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp index cf2c03396..b11a61710 100644 --- a/src/ui/object-edit.cpp +++ b/src/ui/object-edit.cpp @@ -812,7 +812,9 @@ ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*or g_assert(arc != NULL); gint side = sp_genericellipse_side(arc, p); - if(side != 0) { arc->setClosed(side == -1); } + if(side != 0) { arc->setArcType( (side == -1) ? + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); Geom::Scale sc(arc->rx.computed, arc->ry.computed); @@ -861,7 +863,9 @@ ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*orig g_assert(arc != NULL); gint side = sp_genericellipse_side(arc, p); - if(side != 0) { arc->setClosed(side == -1); } + if(side != 0) { arc->setArcType( (side == -1) ? + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); Geom::Scale sc(arc->rx.computed, arc->ry.computed); diff --git a/src/widgets/arc-toolbar.cpp b/src/widgets/arc-toolbar.cpp index 9b408a7b2..56eeb8922 100644 --- a/src/widgets/arc-toolbar.cpp +++ b/src/widgets/arc-toolbar.cpp @@ -148,7 +148,7 @@ static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl ) SPDesktop *desktop = static_cast(g_object_get_data( tbl, "desktop" )); if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0); + prefs->setInt("/tools/shapes/arc/arc_type", ege_select_one_action_get_active(act)); } // quit if run by the attr_changed listener @@ -159,35 +159,43 @@ static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl ) // in turn, prevent listener from responding g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); - bool modmade = false; - if ( ege_select_one_action_get_active(act) != 0 ) { - auto itemlist= desktop->getSelection()->items(); - for(auto i=itemlist.begin();i!=itemlist.end();++i){ - SPItem *item = *i; - if (SP_IS_GENERICELLIPSE(item)) { - Inkscape::XML::Node *repr = item->getRepr(); - repr->setAttribute("sodipodi:open", "true"); - item->updateRepr(); - modmade = true; - } - } - } else { - auto itemlist= desktop->getSelection()->items(); - for(auto i=itemlist.begin();i!=itemlist.end();++i){ - SPItem *item = *i; - if (SP_IS_GENERICELLIPSE(item)) { - Inkscape::XML::Node *repr = item->getRepr(); - repr->setAttribute("sodipodi:open", NULL); - item->updateRepr(); - modmade = true; - } + int mode = ege_select_one_action_get_active(act); + Glib::ustring arc_type = "slice"; + bool open = false; + switch (mode) { + case 0: + arc_type = "slice"; + open = false; + break; + case 1: + arc_type = "arc"; + open = true; + break; + case 2: + arc_type = "chord"; + open = true; // For backward compat, not truly open but chord most like arc. + break; + default: + std::cerr << "sp_arctb_open_state_changed: bad arc type: " << mode << std::endl; + } + + bool modmade = false; + auto itemlist= desktop->getSelection()->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + SPItem *item = *i; + if (SP_IS_GENERICELLIPSE(item)) { + Inkscape::XML::Node *repr = item->getRepr(); + repr->setAttribute("sodipodi:open", (open?"true":NULL) ); + repr->setAttribute("sodipodi:arc-type", arc_type.c_str()); + item->updateRepr(); + modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_ARC, - _("Arc: Change open/closed")); + _("Arc: Changed arc type")); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -234,14 +242,22 @@ static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const * / sp_arctb_sensitivize( tbl, gtk_adjustment_get_value(adj1), gtk_adjustment_get_value(adj2) ); - char const *openstr = NULL; - openstr = repr->attribute("sodipodi:open"); + char const *arctypestr = NULL; + arctypestr = repr->attribute("sodipodi:arc-type"); + if (!arctypestr) { // For old files. + char const *openstr = NULL; + openstr = repr->attribute("sodipodi:open"); + arctypestr = (openstr ? "arc" : "slice"); + } + EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) ); - if (openstr) { + if (!strcmp(arctypestr,"slice")) { + ege_select_one_action_set_active( ocb, 0 ); + } else if (!strcmp(arctypestr,"arc")) { ege_select_one_action_set_active( ocb, 1 ); } else { - ege_select_one_action_set_active( ocb, 0 ); + ege_select_one_action_set_active( ocb, 2 ); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -338,25 +354,32 @@ void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObjec gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); } - /* Segments / Pie checkbox */ + /* Arc: Slice, Arc, Chord */ { GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); GtkTreeIter iter; gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, - 0, _("Closed arc"), - 1, _("Switch to segment (closed shape with two radii)"), + 0, _("Slice"), + 1, _("Switch to slice (closed shape with two radii)"), 2, INKSCAPE_ICON("draw-ellipse-segment"), -1 ); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, - 0, _("Open Arc"), + 0, _("Arc (Open)"), 1, _("Switch to arc (unclosed shape)"), 2, INKSCAPE_ICON("draw-ellipse-arc"), -1 ); + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Chord"), + 1, _("Switch to chord (closed shape)"), + 2, INKSCAPE_ICON("draw-ellipse-chord"), + -1 ); + EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); g_object_set_data( holder, "open_action", act ); @@ -368,8 +391,8 @@ void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObjec ege_select_one_action_set_icon_size( act, secondarySize ); ege_select_one_action_set_tooltip_column( act, 1 ); - bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false); - ege_select_one_action_set_active( act, isClosed ? 0 : 1 ); + int arc_type = prefs->getInt("/tools/shapes/arc/arc_type", 0); + ege_select_one_action_set_active( act, arc_type ); g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder ); } -- cgit v1.2.3