path-manipulator.cpp revision ef28c9682b3526f09d7b197547730beeabbe076f
1858N/A * Path manipulator - implementation. 1858N/A * Krzysztof KosiĆski <tweenk.pl@gmail.com> 1858N/A * Copyright (C) 2009 Authors 1858N/A * Released under GNU GPL, read the file 'COPYING' for more information 1858N/A/// Types of path changes that we must react to. 1858N/A * Notifies the path manipulator when something changes the path being edited 1858N/A // only react to "d" (path data) and "transform" attribute changes 1858N/A/** Handle motion events to update the position of the curve drag point. */ 1858N/A/** Check whether the manipulator has any nodes. */ 1858N/A/** Update the display and the outline of the path. 1858N/A * \param alert_LPE if true, alerts an applied LPE to what the path is going to be changed to, so it can adjust its parameters for nicer user interfacing 1858N/A/** Store the changes to the path in XML. */ 1858N/A // this manipulator will have to be destroyed right after this call 1858N/A/** Remove all nodes from the path. */ 1858N/A // no longer necessary since nodes remove themselves from selection on destruction 1858N/A //_removeNodesFromSelection(); 1858N/A/** Select all nodes in subpaths that have something selected. */ 1858N/A // if at least one of the nodes from this subpath is selected, 1858N/A // select all nodes from this subpath 1858N/A/** Invert selection in the selected subpaths. */ 1858N/A // found selected node - invert selection in this subpath 1858N/A/** Insert a new node in the middle of each selected segment. */ 1858N/A // replace all extreme nodes with the new one 1858N/A // very rare but: extremum node at the same extreme value!!! so add it to the list 1858N/A/** Insert a new node at the extremum of the selected segments. */ 1858N/A // a line segment has is extrema at the start and end, no node should be added 1858N/A // build 1D cubic bezier curve 1858N/A // don't insert node at the start or end of a segment, i.e. round values for extr_t 1858N/A/** Insert new nodes exactly at the positions of selected nodes while preserving shape. 1858N/A * This is equivalent to breaking, except that it doesn't split into subpaths. */ 1858N/A // Move the new node to the bottom of the Z-order. This way you can drag all 1858N/A // nodes that were selected before this operation without deselecting 1858N/A // everything because there is a new node above. 1858N/A // We need to manually call the selection change callback to refresh 1858N/A // the handle display correctly. 1858N/A // This call changes num_selected, but we call this once for a selected node 1858N/A // and once for an unselected node, so in the end the number stays correct. 1858N/A // select the new end node instead of the node just before it 1858N/A break;
// this was the end node, nothing more to do 1858N/A/** Replace contiguous selections of nodes in each subpath with one node. */ 1858N/A // if all nodes in a subpath are selected, the operation doesn't make much sense 1858N/A // Start from unselected node in closed paths, so that we don't start in the middle 1858N/A "but there are still nodes to process!");
1858N/A // note: this is initialized to zero, because the loop below counts sel_beg as well 1858N/A // the loop conditions are simpler that way 1858N/A // do not move handles if they aren't degenerate 1858N/A/** Remove nodes in the middle of selected segments. */ 1858N/A // if all nodes in a closed subpath are selected, the operation doesn't make much sense 1858N/A // Start from unselected node in closed paths, so that we don't start in the middle 1858N/A "but there are still nodes to process!");
1858N/A // note: this is initialized to zero, because the loop below counts sel_beg as well 1858N/A // the loop conditions are simpler that way 1858N/A // find the end of selected segment 1858N/A // remove nodes in the middle 1858N/A/** Break the subpath at selected nodes. It also works for single node closed paths. */ 1858N/A // Each open path must have at least two nodes so no checks are required. 1858N/A // For 2-node open paths, cur == end 1858N/A // Move the node to break at to the beginning of path /** Delete selected nodes in the path, optionally substituting deleted segments with bezier curves * in a way that attempts to preserve the original shape of the curve. */ // If there are less than 2 unselected nodes in an open subpath or no unselected nodes // in a closed one, delete entire subpath. // In closed paths, start from an unselected node - otherwise we might start in the middle // of a selected stretch and the resulting bezier fit would be suboptimal * Delete nodes between the two iterators. * The given range can cross the beginning of the subpath in closed subpaths. * @param start Beginning of the range to delete * @param end End of the range * @param keep_shape Whether to fit the handles at surrounding nodes to approximate * the shape before deletion * @return Number of deleted nodes // set surrounding node types to cusp if: // 1. keep_shape is on, or // 2. we are deleting at the end or beginning of an open path // Compute replacement bezier curve // TODO the fitting algorithm sucks - rewrite it to be awesome // We can't use nl->erase(start, end), because it would break when the stretch // crosses the beginning of a closed subpath /** Removes selected segments */ // In closed paths, relocate the beginning of the path to the last selected // node and then unclose it. Remove the nodes from the first selected node // to the new end of path. // 1. At end or beginning, delete including the node on the end or beginning // 2. In the middle, delete only inner nodes /** Reverse subpaths of the path. * @param selected_only If true, only paths that have at least one selected node * will be reversed. Otherwise all subpaths will be reversed. */ break;
// continue with the next subpath /** Make selected segments curves / lines. */ // move both handles to 1/3 of the line gchar const *
key =
which < 0 ?
"handle:scale:left" :
"handle:scale:right";
gchar const *
key =
which < 0 ?
"handle:rotate:left" :
"handle:rotate:right";
// on an endnode, the remaining handle automatically wins // compare X coord ofline segments // we just swap the handles and pick the right handle below. /** Set the visibility of handles. */ /** Set the visibility of outline. */ /** Hide the curve drag point until the next motion event. * This should be called at the beginning of every method that can delete nodes. * Otherwise the invalidated iterator in the dragpoint can cause crashes. */ /** Insert a node in the segment beginning with the supplied iterator, * at the given time value */ // We need to insert the segment after 'first'. We can't simply use 'second' // as the point of insertion, because when 'first' is the last node of closed path, // the new node will be inserted as the first node instead. // for a line segment, insert a cusp node // build bezier curve and subdivide // set new handle positions * @param origin Point of reference * @param search_selected Consider selected nodes * @param search_unselected Consider unselected nodes * @param closest If true, return closest node, if false, return farthest * @return The matching node, or an empty iterator if none found /** Called by the XML observer when something else than us modifies the path. */ // ugly: stored offsets of selected nodes in a vector // vector<bool> should be specialized so that it takes only 1 bit per value /** Create nodes and handles based on the XML of the edited path. */ // sanitize pathvector and store it in SPCurve, // so that _updateDragPoint doesn't crash on paths with naked movetos // NOTE: this utilizes the fact that Geom::PathVector is an std::vector. // When we erase an element, the next one slides into position, // so we do not increment the iterator even though it is theoretically invalidated. // in this loop, we know that there are no zero-segment subpaths // if the closing segment is degenerate and the path is closed, we need to move // the handle of the first node instead of creating a new one /* regardless of segment type, create a new node at the end * of this segment (unless this is the last segment of a closed path * with a degenerate closing segment */ // if this is a bezier segment, move handles appropriately // TODO: I don't know why the dynamic cast below doesn't want to work // when I replace BezierCurve with CubicBezier. Might be a bug // somewhere in pathv_to_linear_and_cubic_beziers // If the path is closed, make the list cyclic // we need to set the nodetypes after all the handles are in place, // so that pickBestType works correctly // TODO maybe migrate to inkscape:node-types? // TODO move this into SPPath - do not manipulate directly //XML Tree being used here directly while it shouldn't be. /* Calculate the needed length of the nodetype string. * For closed paths, the entry is duplicated for the starting node, * so we can just use the count of segments including the closing one * to include the extra end node. */ if (i->
empty())
continue;
/* pad the string to required length with a bogus value. * 'b' and any other letter not recognized by the parser causes the best fit to be set // STUPIDITY ALERT: it seems we need to use the duplicate type symbol instead of // the first one to remain backward compatible. /** Construct the geometric representation of nodes and handles, update the outline * \param alert_LPE if true, first the LPE is warned what the new path is going to be before updating it // Here we link the last and first node if the path is closed. // If the last segment is Bezier, we add it. // if that segment is linear, we just call closePath(). /** Build one segment of the geometric representation. * @relates PathManipulator */ // NOTE: It seems like the renderer cannot correctly handle vline / hline segments, // and trying to display a path using them results in funny artifacts. // this is a bezier segment /** Construct a node type string to store in the sodipodi:nodetypes attribute. */ // precondition: no single-node subpaths // nodestring format peculiarity: first node is counted twice for closed paths /** Update the path outline. */ // This SPCurve thing has to be killed with extreme prejudice // To show the direction, we append additional subpaths which consist of a single // linear segment that starts at the time value of 0.5 and extends for 10 pixels // at an angle 150 degrees from the unit tangent. This creates the appearance // of little 'harpoons' that show the direction of the subpaths. /** Retrieve the geometry of the edited object from the object tree */ /** Set the geometry of the edited object in the object tree, but do not commit to XML */ // NOTE: if we are editing an LPE param, _path is not actually an SPPath, it is // a LivePathEffectObject. (mad laughter) //XML Tree being used here directly while it shouldn't be. /** Figure out in what attribute to store the nodetype string. */ /** Return the XML node we are editing. * This method is wrong but necessary at the moment. */ //XML Tree being used here directly while it shouldn't be. //XML Tree being used here directly while it shouldn't be. // Ctrl+Alt+click: delete nodes // Removing last node of closed path - delete it // In other cases, delete the node under cursor // We need to call MPM's method because it could have been our last node // Ctrl+click: cycle between node types // retracting by Ctrl+click // don't do anything if we do not show handles // only do something if a node changed selection state // selection - show handles on this node and adjacent ones /* Deselection is more complex. * The change might affect 3 nodes - this one and two adjacent. * If the node and both its neighbors are deselected, hide handles. * Otherwise, leave as is. */ for (
int i = 0; i <
5; ++i) {
for (
int i =
1; i <
4; ++i) {
/** Removes all nodes belonging to this manipulator from the control pont selection */ // remove this manipulator's nodes from selection /** Update the XML representation and put the specified annotation on the undo stack */ /** Update the position of the curve drag point such that it is over the nearest /// This is called on zoom change to update the direction arrows /** Compute the radius from the edge of the path where clicks chould initiate a curve drag * or segment selection, in window coordinates. */ /* Stroke event tolerance is equal to half the stroke's width plus the global * drag tolerance setting. */ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :