~danieljabailey/inkscape/arc_node_editor

« back to all changes in this revision

Viewing changes to src/ui/tool/path-manipulator.cpp

  • Committer: tavmjong-free
  • Date: 2016-05-08 07:44:05 UTC
  • mfrom: (14873.1.1 gtk3)
  • Revision ID: tavmjong@free.fr-20160508074405-rm6tiapoq1ugamph
Start of GTK3 external style sheet support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
#include <2geom/bezier-curve.h>
22
22
#include <2geom/bezier-utils.h>
23
23
#include <2geom/path-sink.h>
24
 
#include <2geom/pathvector.h>
25
24
#include <glibmm/i18n.h>
26
25
#include "ui/tool/path-manipulator.h"
27
26
#include "desktop.h"
802
801
    }
803
802
}
804
803
 
805
 
// \todo The three functions setSegmentType, setArcSegmentLarge, setArcSegmentSweep could be
806
 
// refactored to share the code that iterates over all selected segments.
807
 
 
808
 
/** Make selected segments curves / lines / arcs. */
 
804
/** Make selected segments curves / lines. */
809
805
void PathManipulator::setSegmentType(SegmentType type)
810
806
{
811
807
    if (_num_selected == 0) return;
815
811
            if (!(k && j->selected() && k->selected())) continue;
816
812
            switch (type) {
817
813
            case SEGMENT_STRAIGHT:
818
 
                if ((j->front()->isDegenerate() && k->back()->isDegenerate())
819
 
                    && (j->arc_rx()->isDegenerate() && j->arc_ry()->isDegenerate()))
 
814
                if (j->front()->isDegenerate() && k->back()->isDegenerate())
820
815
                    break;
821
816
                j->front()->move(*j);
822
817
                k->back()->move(*k);
823
 
                j->retractArcHandles();
824
818
                break;
825
819
            case SEGMENT_CUBIC_BEZIER:
826
 
                if (!j->front()->isDegenerate() || !k->back()->isDegenerate()){
827
 
                    // Already a cubic bezier
828
 
                    break;
829
 
                }
830
 
                if (!j->arc_rx()->isDegenerate() || !j->arc_ry()->isDegenerate()){
831
 
                    // This is an elliptical arc that is being converted to a cubic bezier
832
 
                    // Generate the bezier path and use it to replace the current segment
833
 
                    Geom::Path cubicbezier_path = Geom::cubicbezierpath_from_sbasis(j->getEllipticalArc().toSBasis(), 0.1);
834
 
                    replaceSegmentWithPath(j, cubicbezier_path);
835
 
                    break;
836
 
                }
837
 
                
 
820
                if (!j->front()->isDegenerate() || !k->back()->isDegenerate())
 
821
                    break;
838
822
                // move both handles to 1/3 of the line
839
823
                j->front()->move(j->position() + (k->position() - j->position()) / 3);
840
824
                k->back()->move(k->position() + (j->position() - k->position()) / 3);
841
 
                j->arc_rx()->move(*j);
842
 
                j->arc_ry()->move(*j);
843
 
                j->retractArcHandles();
844
 
                break;
845
 
            case SEGMENT_ELIPTICAL_ARC:
846
 
                if (!(j->front()->isDegenerate() && k->back()->isDegenerate())){
847
 
                    j->front()->move(*j);
848
 
                    k->back()->move(*k);
849
 
                }
850
 
 
851
 
                if (j->arc_rx()->isDegenerate() && j->arc_ry()->isDegenerate()){
852
 
                    Geom::Point midPointOffset = ((k->position() - j->position()) / 2);
853
 
                    Geom::Point handleOrigin = j->position()+midPointOffset;
854
 
 
855
 
                    j->arc_rx()->setOffset(midPointOffset);
856
 
                    j->arc_ry()->setOffset(midPointOffset);
857
 
                    j->arc_rx()->move(j->position() + ((k->position() - handleOrigin) / 2));
858
 
                    j->updateArcHandleConstriants(j->arc_rx());
859
 
                }
860
 
                break;
861
 
            }
862
 
        }
863
 
    }
864
 
}
865
 
 
866
 
/** Set the large flag on selected arcs */
867
 
void PathManipulator::setArcSegmentLarge(bool large){
868
 
    if (_num_selected == 0) return;
869
 
    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
870
 
        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
871
 
            NodeList::iterator k = j.next();
872
 
            if (!(k && j->selected() && k->selected())) continue;
873
 
            // This code executes for every selected segment
874
 
            if (!j->arc_rx()->isDegenerate() || !j->arc_ry()->isDegenerate()){
875
 
                // This code executes for every selected ARC segment
876
 
                *(j->arc_large()) = large;
877
 
            }
878
 
        }
879
 
    }
880
 
}
881
 
 
882
 
/** Set the sweep flag on selected arcs */
883
 
void PathManipulator::toggleArcSegmentSweep(){
884
 
    if (_num_selected == 0) return;
885
 
    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
886
 
        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
887
 
            NodeList::iterator k = j.next();
888
 
            if (!(k && j->selected() && k->selected())) continue;
889
 
            // This code executes for every selected segment
890
 
            if (!j->arc_rx()->isDegenerate() || !j->arc_ry()->isDegenerate()){
891
 
                // This code executes for every selected ARC segment
892
 
                *(j->arc_sweep()) = !*j->arc_sweep();
 
825
                break;
893
826
            }
894
827
        }
895
828
    }
1161
1094
    return match;
1162
1095
}
1163
1096
 
1164
 
/** Replace a segment with a path.
1165
 
 * @param segment The segment to replace
1166
 
 * @param newPath The path to insert in place of 'segment'
1167
 
 *
1168
 
 * Currently, newPath must be composed only of cubic beziers, the caller must
1169
 
 * ensure that the path only contains cubic beziers (until other segments are
1170
 
 * implemented in this function)  */
1171
 
void PathManipulator::replaceSegmentWithPath(NodeList::iterator segment, Geom::Path newPath)
1172
 
{
1173
 
    if (!segment) throw std::invalid_argument("Invalid iterator for replacement");
1174
 
    NodeList &list = NodeList::get(segment);
1175
 
    NodeList::iterator second = segment.next();
1176
 
    if (!second) throw std::invalid_argument("Replace after last node in open path");
1177
 
 
1178
 
    // Retract all handles relating to this segment
1179
 
    segment->retractArcHandles();
1180
 
    segment->front()->retract();
1181
 
    
1182
 
    // get the insertion point
1183
 
    NodeList::iterator insert_at = segment;
1184
 
    ++insert_at;
1185
 
 
1186
 
    // Keep the previous node handy to update its handles when needed
1187
 
    Node *prevNode = &(*insert_at);
1188
 
 
1189
 
    // Path is to be inserted in reverse order
1190
 
    Geom::Path reversedPath = newPath.reversed();
1191
 
 
1192
 
    // Iterate over the path
1193
 
    for (Geom::Path::iterator i = reversedPath.begin(); i != reversedPath.end(); ++i){
1194
 
        const Geom::Curve & thisCurve = *i;
1195
 
 
1196
 
        // Try converting to a bezier
1197
 
        const Geom::BezierCurve * bezier = dynamic_cast<const Geom::BezierCurve*>(&thisCurve);
1198
 
        if (bezier) {
1199
 
            // Check order of bezier (currently only cubic beziers are supported)
1200
 
            if (bezier->order() == 3)
1201
 
            {
1202
 
                // Create one new node
1203
 
                Node *newNode = new Node(_multi_path_manipulator._path_data.node_data, bezier->finalPoint());
1204
 
                // Set the control points for this node and the previous node
1205
 
                newNode->front() ->setPosition((*bezier)[2]);
1206
 
                prevNode->back()->setPosition((*bezier)[1]);
1207
 
                // All new nodes are smooth
1208
 
                newNode->setType(NODE_SMOOTH, false);
1209
 
 
1210
 
                // Insert new node
1211
 
                list.insert(insert_at, newNode);
1212
 
                // Move along to next node
1213
 
                prevNode = newNode;
1214
 
                insert_at--;
1215
 
            }
1216
 
            else{
1217
 
                // TODO, Is there a better exception to raise here?
1218
 
                // TODO, implement this if needed in future
1219
 
                throw std::invalid_argument("Only cubic bezier curves are implemented in PathManipulator::replaceSegment."
1220
 
                    " newPath contains beziers with order!=3.");
1221
 
            }
1222
 
        }
1223
 
        else{
1224
 
            // Not a bezier
1225
 
            // TODO, Is there a better exception to raise here?
1226
 
            // TODO, implement this if needed in future
1227
 
            throw std::invalid_argument("Only cubic bezier curves are implemented in PathManipulator::replaceSegment."
1228
 
                " newPath contains non-bezier segments.");
1229
 
        }
1230
 
    }
1231
 
}
1232
 
 
1233
1097
/** Called by the XML observer when something else than us modifies the path. */
1234
1098
void PathManipulator::_externalChange(unsigned type)
1235
1099
{
1285
1149
 
1286
1150
    // sanitize pathvector and store it in SPCurve,
1287
1151
    // so that _updateDragPoint doesn't crash on paths with naked movetos
1288
 
    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers_and_arcs(_spcurve->get_pathvector());
1289
 
 
 
1152
    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(_spcurve->get_pathvector());
1290
1153
    for (Geom::PathVector::iterator i = pathv.begin(); i != pathv.end(); ) {
1291
1154
        // NOTE: this utilizes the fact that Geom::PathVector is an std::vector.
1292
1155
        // When we erase an element, the next one slides into position,
1336
1199
                previous_node->front()->setPosition((*bezier)[1]);
1337
1200
                current_node ->back() ->setPosition((*bezier)[2]);
1338
1201
            }
1339
 
            Geom::EllipticalArc const *arc = dynamic_cast<Geom::EllipticalArc const*>(&*cit);
1340
 
            if (arc)
1341
 
            {
1342
 
                Geom::Coord angleX = arc->rotationAngle();
1343
 
                Geom::Coord angleY = angleX + Geom::rad_from_deg(90);
1344
 
                previous_node->moveArcHandles(current_node->position() - previous_node->position(), arc->ray(Geom::X), arc->ray(Geom::Y), angleX, angleY);
1345
 
                *(previous_node->arc_large()) = arc->largeArc();
1346
 
                *(previous_node->arc_sweep()) = arc->sweep();
1347
 
            }
1348
1202
            previous_node = current_node;
1349
1203
        }
1350
1204
        // If the path is closed, make the list cyclic
1543
1397
 * @relates PathManipulator */
1544
1398
void build_segment(Geom::PathBuilder &builder, Node *prev_node, Node *cur_node)
1545
1399
{
1546
 
    if (prev_node->arc_rx()->isDegenerate() || prev_node->arc_ry()->isDegenerate() ){
1547
 
        // This is not an eliptical arc, check if it is a straight line or bezier
1548
 
        if (cur_node->back()->isDegenerate() && prev_node->front()->isDegenerate())
1549
 
        {
1550
 
            // NOTE: It seems like the renderer cannot correctly handle vline / hline segments,
1551
 
            // and trying to display a path using them results in funny artifacts.
1552
 
            builder.lineTo(cur_node->position());
1553
 
        } else {
1554
 
            // this is a bezier segment
1555
 
            builder.curveTo(
1556
 
                prev_node->front()->position(),
1557
 
                cur_node->back()->position(),
1558
 
                cur_node->position());
1559
 
        }
1560
 
    }
1561
 
    else{
1562
 
        // This is an eliptical arc, get the x and y set by the xy handle
1563
 
        Geom::Coord rx = prev_node->arc_rx()->length();
1564
 
        Geom::Coord ry = prev_node->arc_ry()->length();
1565
 
        Geom::Angle rot = prev_node->arc_rx()->angle();
1566
 
 
1567
 
        builder.arcTo(
1568
 
            rx,
1569
 
            ry,
1570
 
            rot,
1571
 
            *prev_node->arc_large(),
1572
 
            *prev_node->arc_sweep(),
 
1400
    if (cur_node->back()->isDegenerate() && prev_node->front()->isDegenerate())
 
1401
    {
 
1402
        // NOTE: It seems like the renderer cannot correctly handle vline / hline segments,
 
1403
        // and trying to display a path using them results in funny artifacts.
 
1404
        builder.lineTo(cur_node->position());
 
1405
    } else {
 
1406
        // this is a bezier segment
 
1407
        builder.curveTo(
 
1408
            prev_node->front()->position(),
 
1409
            cur_node->back()->position(),
1573
1410
            cur_node->position());
1574
1411
    }
1575
1412
}