summaryrefslogtreecommitdiffstats
path: root/src/ui/tools/marker-tool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/tools/marker-tool.cpp')
-rw-r--r--src/ui/tools/marker-tool.cpp302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/ui/tools/marker-tool.cpp b/src/ui/tools/marker-tool.cpp
new file mode 100644
index 0000000..e2e2631
--- /dev/null
+++ b/src/ui/tools/marker-tool.cpp
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Marker edit mode - onCanvas marker editing of marker orientation, position, scale
+ *//*
+ * Authors:
+ * see git history
+ * Rachana Podaralla <rpodaralla3@gatech.edu>
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "display/curve.h"
+
+#include "desktop.h"
+#include "document.h"
+#include "style.h"
+#include "message-context.h"
+#include "selection.h"
+
+#include "object/sp-path.h"
+#include "object/sp-shape.h"
+#include "object/sp-marker.h"
+
+#include "ui/shape-editor.h"
+#include "ui/tool/event-utils.h"
+#include "ui/tool/multi-path-manipulator.h"
+#include "ui/tool/path-manipulator.h"
+#include "ui/tools/marker-tool.h"
+
+
+namespace Inkscape {
+namespace UI {
+namespace Tools {
+
+MarkerTool::MarkerTool(SPDesktop *desktop)
+ : ToolBase(desktop, "/tools/marker", "select.svg")
+{
+ Inkscape::Selection *selection = desktop->getSelection();
+
+ this->sel_changed_connection.disconnect();
+ this->sel_changed_connection = selection->connectChanged(
+ sigc::mem_fun(this, &MarkerTool::selection_changed)
+ );
+ this->selection_changed(selection);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/tools/marker/selcue")) this->enableSelectionCue();
+ if (prefs->getBool("/tools/marker/gradientdrag")) this->enableGrDrag();
+}
+
+MarkerTool::~MarkerTool()
+{
+ ungrabCanvasEvents();
+
+ this->message_context->clear();
+ this->_shape_editors.clear();
+
+ this->enableGrDrag(false);
+ this->sel_changed_connection.disconnect();
+}
+
+/*
+- cycles through all the selected items to see if any have a marker in the right location (based on enterMarkerMode)
+- if a matching item is found, loads the corresponding marker on the shape into the shape-editor and exits the loop
+- forces user to only edit one marker at a time
+*/
+void MarkerTool::selection_changed(Inkscape::Selection *selection) {
+ using namespace Inkscape::UI;
+
+ g_assert(_desktop != nullptr);
+
+ SPDocument *doc = _desktop->getDocument();
+ g_assert(doc != nullptr);
+
+ auto selected_items = selection->items();
+ this->_shape_editors.clear();
+
+ for(auto i = selected_items.begin(); i != selected_items.end(); ++i){
+ SPItem *item = *i;
+
+ if(item) {
+ SPShape* shape = dynamic_cast<SPShape*>(item);
+
+ if(shape && shape->hasMarkers() && (editMarkerMode != -1)) {
+ SPObject *obj = shape->_marker[editMarkerMode];
+
+ if(obj) {
+
+ SPMarker *sp_marker = dynamic_cast<SPMarker *>(obj);
+ g_assert(sp_marker != nullptr);
+
+ sp_validate_marker(sp_marker, doc);
+
+ ShapeRecord sr;
+ switch(editMarkerMode) {
+ case SP_MARKER_LOC_START:
+ sr = get_marker_transform(shape, item, sp_marker, SP_MARKER_LOC_START);
+ break;
+
+ case SP_MARKER_LOC_MID:
+ sr = get_marker_transform(shape, item, sp_marker, SP_MARKER_LOC_MID);
+ break;
+
+ case SP_MARKER_LOC_END:
+ sr = get_marker_transform(shape, item, sp_marker, SP_MARKER_LOC_END);
+ break;
+
+ default:
+ break;
+ }
+
+ auto si = std::make_unique<ShapeEditor>(_desktop, sr.edit_transform, sr.edit_rotation, editMarkerMode);
+ si->set_item(dynamic_cast<SPItem *>(sr.object));
+
+ this->_shape_editors.insert({item, std::move(si)});
+ break;
+ }
+ }
+ }
+ }
+}
+
+// handles selection of new items
+bool MarkerTool::root_handler(GdkEvent* event) {
+ g_assert(_desktop != nullptr);
+
+ Inkscape::Selection *selection = _desktop->getSelection();
+ gint ret = false;
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 1) {
+
+ Geom::Point const button_w(event->button.x, event->button.y);
+ this->item_to_select = sp_event_context_find_item (_desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE);
+
+ grabCanvasEvents();
+ ret = true;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 1) {
+
+ if (this->item_to_select) {
+ // unselect all items, except for newly selected item
+ selection->set(this->item_to_select);
+ } else {
+ // clicked into empty space, deselect any selected items
+ selection->clear();
+ }
+
+ this->item_to_select = nullptr;
+ ungrabCanvasEvents();
+ ret = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (!ret? ToolBase::root_handler(event): ret);
+}
+
+/*
+- this function uses similar logic that exists in sp_shape_update_marker_view
+- however, the tangent angle needs to be saved here and parent_item->i2dt_affine() needs to also be accounted for in the right places
+- calculate where the shape-editor knotholders need to go based on the reference shape
+*/
+ShapeRecord MarkerTool::get_marker_transform(SPShape* shape, SPItem *parent_item, SPMarker *sp_marker, SPMarkerLoc marker_type)
+{
+
+ // scale marker transform with parent stroke width
+ SPStyle *style = shape->style;
+ SPDocument *doc = _desktop->getDocument();
+ Geom::Scale scale = doc->getDocumentScale();
+
+ if(sp_marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
+ scale *= Geom::Scale(style->stroke_width.computed);
+ }
+
+ Geom::PathVector const &pathv = shape->curve()->get_pathvector();
+ Geom::Affine ret = Geom::identity(); //edit_transform
+ double angle = 0.0; // edit_rotation - tangent angle used for auto orientation
+ Geom::Point p;
+
+ if(marker_type == SP_MARKER_LOC_START) {
+
+ Geom::Curve const &c = pathv.begin()->front();
+ p = c.pointAt(0);
+ ret = Geom::Translate(p * parent_item->i2doc_affine());
+
+ if (!c.isDegenerate()) {
+ Geom::Point tang = c.unitTangentAt(0);
+ angle = Geom::atan2(tang);
+ ret = Geom::Rotate(angle) * ret;
+ }
+
+ } else if(marker_type == SP_MARKER_LOC_MID) {
+ /*
+ - a shape can have multiple mid markers - only one is needed
+ - once a valid mid marker is found, save edit_transfom and edit_rotation and break out of loop
+ */
+ for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
+
+ // mid marker start position
+ if (path_it != pathv.begin() && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)))
+ {
+ Geom::Curve const &c = path_it->front();
+ p = c.pointAt(0);
+ ret = Geom::Translate(p * parent_item->i2doc_affine());
+
+ if (!c.isDegenerate()) {
+ Geom::Point tang = c.unitTangentAt(0);
+ angle = Geom::atan2(tang);
+ ret = Geom::Rotate(angle) * ret;
+ break;
+ }
+ }
+
+ // mid marker mid positions
+ if ( path_it->size_default() > 1) {
+ Geom::Path::const_iterator curve_it1 = path_it->begin();
+ Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
+ while (curve_it2 != path_it->end_default())
+ {
+ Geom::Curve const & c1 = *curve_it1;
+ Geom::Curve const & c2 = *curve_it2;
+
+ p = c1.pointAt(1);
+ Geom::Curve * c1_reverse = c1.reverse();
+ Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
+ delete c1_reverse;
+ Geom::Point tang2 = c2.unitTangentAt(0);
+
+ double const angle1 = Geom::atan2(tang1);
+ double const angle2 = Geom::atan2(tang2);
+
+ angle = .5 * (angle1 + angle2);
+
+ if ( fabs( angle2 - angle1 ) > M_PI ) {
+ angle += M_PI;
+ }
+
+ ret = Geom::Rotate(angle) * Geom::Translate(p * parent_item->i2doc_affine());
+
+ ++curve_it1;
+ ++curve_it2;
+ break;
+ }
+ }
+
+ // mid marker end position
+ if ( path_it != (pathv.end()-1) && !path_it->empty()) {
+ Geom::Curve const &c = path_it->back_default();
+ p = c.pointAt(1);
+ ret = Geom::Translate(p * parent_item->i2doc_affine());
+
+ if ( !c.isDegenerate() ) {
+ Geom::Curve * c_reverse = c.reverse();
+ Geom::Point tang = - c_reverse->unitTangentAt(0);
+ delete c_reverse;
+ angle = Geom::atan2(tang);
+ ret = Geom::Rotate(angle) * ret;
+ break;
+ }
+ }
+ }
+
+ } else if (marker_type == SP_MARKER_LOC_END) {
+
+ Geom::Path const &path_last = pathv.back();
+ unsigned int index = path_last.size_default();
+ if (index > 0) index--;
+
+ Geom::Curve const &c = path_last[index];
+ p = c.pointAt(1);
+ ret = Geom::Translate(p * parent_item->i2doc_affine());
+
+ if ( !c.isDegenerate() ) {
+ Geom::Curve * c_reverse = c.reverse();
+ Geom::Point tang = - c_reverse->unitTangentAt(0);
+ delete c_reverse;
+ angle = Geom::atan2(tang);
+ ret = Geom::Rotate(angle) * ret;
+ }
+ }
+
+ /* scale by stroke width */
+ ret = scale * ret;
+ /* account for parent transform */
+ ret = parent_item->transform.withoutTranslation() * ret;
+
+ ShapeRecord sr;
+ sr.object = sp_marker;
+ sr.edit_transform = ret;
+ sr.edit_rotation = angle * 180.0/M_PI;
+ sr.role = SHAPE_ROLE_NORMAL;
+ return sr;
+}
+
+}}}