Пример #1
0
// FIXME: why is 'transform' argument not used?
void
PrintLatex::print_pathvector(SVGOStringStream &os, Geom::PathVector const &pathv_in, const Geom::Affine & /*transform*/)
{
    if (pathv_in.empty())
        return;

//    Geom::Affine tf=transform;   // why was this here?
    Geom::Affine tf_stack=m_tr_stack.top(); // and why is transform argument not used?
    Geom::PathVector pathv = pathv_in * tf_stack; // generates new path, which is a bit slow, but this doesn't have to be performance optimized

    os << "\\newpath\n";

    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {

        os << "\\moveto(" << it->initialPoint()[Geom::X] << "," << it->initialPoint()[Geom::Y] << ")\n";

        for(Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) {
            print_2geomcurve(os, *cit);
        }

        if (it->closed()) {
            os << "\\closepath\n";
        }

    }
}
Пример #2
0
/** Feeds path-creating calls to the cairo context translating them from the PathVector
 *  One must have done cairo_new_path(ct); before calling this function. */
void
feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv)
{
    if (pathv.empty())
        return;

    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
        feed_path_to_cairo(ct, *it);
    }
}
Пример #3
0
/*
 * Converts all segments in all paths to Geom::LineSegment or Geom::HLineSegment or
 * Geom::VLineSegment or Geom::CubicBezier.
 */
Geom::PathVector
pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv )
{
    Geom::PathVector output;

    for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
        output.push_back( Geom::Path() );
        output.back().start( pit->initialPoint() );
        output.back().close( pit->closed() );

        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
            if (is_straight_curve(*cit)) {
                Geom::LineSegment l(cit->initialPoint(), cit->finalPoint());
                output.back().append(l);
            } else {
                Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&*cit);
                if (curve && curve->order() == 3) {
                    Geom::CubicBezier b((*curve)[0], (*curve)[1], (*curve)[2], (*curve)[3]);
                    output.back().append(b);
                } else {
                    // convert all other curve types to cubicbeziers
                    Geom::Path cubicbezier_path = Geom::cubicbezierpath_from_sbasis(cit->toSBasis(), 0.1);
                    output.back().append(cubicbezier_path);
                }
            }
        }
    }
    
    return output;
}
Пример #4
0
/** Feeds path-creating calls to the cairo context translating them from the PathVector, with the given transform and shift
 *  One must have done cairo_new_path(ct); before calling this function. */
void
feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width)
{
    if (!area)
        return;
    if (pathv.empty())
        return;

    for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
        feed_path_to_cairo(ct, *it, trans, area, optimize_stroke, stroke_width);
    }
}
Пример #5
0
int main(int argc, char **argv) {
    if (argc > 1) {
        SVGPathTestPrinter sink;
        Geom::parse_svg_path(&*argv[1], sink);
        std::cout << "Try real pathsink:" << std::endl;
        Geom::PathVector testpath = Geom::parse_svg_path(&*argv[1]);
        std::cout << "Geom::PathVector length: " << testpath.size() << std::endl;
        if ( !testpath.empty() )
        	std::cout << "Path curves: " << testpath.front().size() << std::endl;
        std::cout << "success!" << std::endl;
    }
    return 0;
};
Пример #6
0
/**
 * Returns a list of new curves corresponding to the subpaths in \a curve.
 * 2geomified
 */
GSList *
SPCurve::split() const
{
    GSList *l = NULL;

    for (Geom::PathVector::const_iterator path_it = _pathv.begin(); path_it != _pathv.end(); ++path_it) {
        Geom::PathVector newpathv;
        newpathv.push_back(*path_it);
        SPCurve * newcurve = new SPCurve(newpathv);
        l = g_slist_prepend(l, newcurve);
    }

    return l;
}
Пример #7
0
Geom::OptRect
bounds_exact_transformed(Geom::PathVector const & pv, Geom::Affine const & t)
{
    if (pv.empty())
        return Geom::OptRect();

    Geom::Point initial = pv.front().initialPoint() * t;
    Geom::Rect bbox(initial, initial);        // obtain well defined bbox as starting point to unionWith

    for (Geom::PathVector::const_iterator it = pv.begin(); it != pv.end(); ++it) {
        bbox.expandTo(it->initialPoint() * t);

        // don't loop including closing segment, since that segment can never increase the bbox
        for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) {
            Geom::Curve const &c = *cit;

            unsigned order = 0;
            if (Geom::BezierCurve const* b = dynamic_cast<Geom::BezierCurve const*>(&c)) {
                order = b->order();
            }

            if (order == 1) { // line segment
                bbox.expandTo(c.finalPoint() * t);

            // TODO: we can make the case for quadratics faster by degree elevating them to
            // cubic and then taking the bbox of that.

            } else if (order == 3) { // cubic bezier
                Geom::CubicBezier const &cubic_bezier = static_cast<Geom::CubicBezier const&>(c);
                Geom::Point c0 = cubic_bezier[0] * t;
                Geom::Point c1 = cubic_bezier[1] * t;
                Geom::Point c2 = cubic_bezier[2] * t;
                Geom::Point c3 = cubic_bezier[3] * t;
                cubic_bbox(c0[0], c0[1], c1[0], c1[1], c2[0], c2[1], c3[0], c3[1], bbox);
            } else {
                // should handle all not-so-easy curves:
                Geom::Curve *ctemp = cit->transformed(t);
                bbox.unionWith( ctemp->boundsExact());
                delete ctemp;
            }
        }
    }
    //return Geom::bounds_exact(pv * t);
    return bbox;
}
Пример #8
0
/*
 * Converts all segments in all paths to Geom::LineSegment.  There is an intermediate
 * stage where some may be converted to beziers.  maxdisp is the maximum displacement from
 * the line segment to the bezier curve; ** maxdisp is not used at this moment **.
 *
 * This is NOT a terribly fast method, but it should give a solution close to the one with the
 * fewest points.
 */
Geom::PathVector
pathv_to_linear( Geom::PathVector const &pathv, double /*maxdisp*/)
{
    Geom::PathVector output;
    Geom::PathVector tmppath = pathv_to_linear_and_cubic_beziers(pathv);
    
    // Now all path segments are either already lines, or they are beziers.

    for (Geom::PathVector::const_iterator pit = tmppath.begin(); pit != tmppath.end(); ++pit) {
        output.push_back( Geom::Path() );
        output.back().start( pit->initialPoint() );
        output.back().close( pit->closed() );

        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
            if (is_straight_curve(*cit)) {
                Geom::LineSegment ls(cit->initialPoint(), cit->finalPoint());
                output.back().append(ls);
            } 
            else { /* all others must be Bezier curves */
                Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&*cit);
                Geom::CubicBezier b((*curve)[0], (*curve)[1], (*curve)[2], (*curve)[3]);
                std::vector<Geom::Point> bzrpoints = b.points();
                Geom::Point A = bzrpoints[0];
                Geom::Point B = bzrpoints[1];
                Geom::Point C = bzrpoints[2];
                Geom::Point D = bzrpoints[3];
                std::vector<Geom::Point> pointlist;
                pointlist.push_back(A);
                recursive_bezier4(
                   A[X], A[Y], 
                   B[X], B[Y], 
                   C[X], C[Y], 
                   D[X], D[Y],
                   pointlist, 
                   0);
                pointlist.push_back(D);
                Geom::Point r1 = pointlist[0];
                for (unsigned int i=1; i<pointlist.size();i++){
                   Geom::Point prev_r1 = r1;
                   r1 = pointlist[i];
                   Geom::LineSegment ls(prev_r1, r1);
                   output.back().append(ls);
                }
                pointlist.clear();
           }
        }
    }
    
    return output;
}
Пример #9
0
int main(int argc, char **argv) {
    if (argc > 1) {
        Geom::PathVector originald = Geom::parse_svg_path(&*argv[1]);
        Geom::Piecewise<Geom::D2<Geom::SBasis> > originaldpwd2;
        for (unsigned int i=0; i < originald.size(); i++) {
            originaldpwd2.concat( originald[i].toPwSb() );
        }

        Geom::PathVector pattern = Geom::parse_svg_path(&*argv[2]);
        Geom::Piecewise<Geom::D2<Geom::SBasis> > patternpwd2;
        for (unsigned int i=0; i < pattern.size(); i++) {
            patternpwd2.concat( pattern[i].toPwSb() );
        }
        
        doEffect_pwd2(originaldpwd2, patternpwd2);
    }
    return 0;
};
Пример #10
0
Geom::OptRect
bounds_exact_transformed(Geom::PathVector const & pv, Geom::Affine const & t)
{
    if (pv.empty())
        return Geom::OptRect();

    Geom::Point initial = pv.front().initialPoint() * t;
    Geom::Rect bbox(initial, initial);        // obtain well defined bbox as starting point to unionWith

    for (Geom::PathVector::const_iterator it = pv.begin(); it != pv.end(); ++it) {
        bbox.expandTo(it->initialPoint() * t);

        // don't loop including closing segment, since that segment can never increase the bbox
        for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) {
            Geom::Curve const &c = *cit;

            if( is_straight_curve(c) )
            {
                bbox.expandTo( c.finalPoint() * t );
            }
            else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const  *>(&c))
            {
                Geom::Point c0 = (*cubic_bezier)[0] * t;
                Geom::Point c1 = (*cubic_bezier)[1] * t;
                Geom::Point c2 = (*cubic_bezier)[2] * t;
                Geom::Point c3 = (*cubic_bezier)[3] * t;
                cubic_bbox( c0[0], c0[1],
                            c1[0], c1[1],
                            c2[0], c2[1],
                            c3[0], c3[1],
                            bbox );
            }
            else
            {
                // should handle all not-so-easy curves:
                Geom::Curve *ctemp = cit->transformed(t);
                bbox.unionWith( ctemp->boundsExact());
                delete ctemp;
            }
        }
    }
    //return Geom::bounds_exact(pv * t);
    return bbox;
}
Пример #11
0
/* Calculates...
   and returns ... in *wind and the distance to ... in *dist.
   Returns bounding box in *bbox if bbox!=NULL.
 */
void
pathv_matrix_point_bbox_wind_distance (Geom::PathVector const & pathv, Geom::Affine const &m, Geom::Point const &pt,
                         Geom::Rect *bbox, int *wind, Geom::Coord *dist,
                         Geom::Coord tolerance, Geom::Rect const *viewbox)
{
    if (pathv.empty()) {
        if (wind) *wind = 0;
        if (dist) *dist = Geom::infinity();
        return;
    }

    // remember last point of last curve
    Geom::Point p0(0,0);

    // remembering the start of subpath
    Geom::Point p_start(0,0);
    bool start_set = false;

    for (Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {

        if (start_set) { // this is a new subpath
            if (wind && (p0 != p_start)) // for correct fill picking, each subpath must be closed
                geom_line_wind_distance (p0[X], p0[Y], p_start[X], p_start[Y], pt, wind, dist);
        }
        p0 = it->initialPoint() * m;
        p_start = p0;
        start_set = true;
        if (bbox) {
            bbox->expandTo(p0);
        }

        // loop including closing segment if path is closed
        for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_default(); ++cit) {
            geom_curve_bbox_wind_distance(*cit, m, pt, bbox, wind, dist, tolerance, viewbox, p0);
        }
    }

    if (start_set) { 
        if (wind && (p0 != p_start)) // for correct picking, each subpath must be closed
            geom_line_wind_distance (p0[X], p0[Y], p_start[X], p_start[Y], pt, wind, dist);
    }
}
Пример #12
0
void
KnotHolderEntityAttachPt::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
{
    using namespace Geom;

    LPETangentToCurve* lpe = dynamic_cast<LPETangentToCurve *>(_effect);

    Geom::Point const s = snap_knot_position(p, state);

    // FIXME: There must be a better way of converting the path's SPCurve* to pwd2.
    SPCurve *curve = SP_PATH(item)->get_curve_for_edit();
    Geom::PathVector pathv = curve->get_pathvector();
    Piecewise<D2<SBasis> > pwd2;
    for (unsigned int i=0; i < pathv.size(); i++) {
        pwd2.concat(pathv[i].toPwSb());
    }

    double t0 = nearest_point(s, pwd2);
    lpe->t_attach.param_set_value(t0);

    // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
    sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
}
Пример #13
0
/*
 * interpolate path_in[0] to path_in[1]
 */
Geom::PathVector
LPEInterpolate::doEffect_path (Geom::PathVector const & path_in)
{
    if ( (path_in.size() < 2) || (number_of_steps < 2)) {
        return path_in;
    }
    // Don't allow empty path parameter:
    if ( trajectory_path.get_pathvector().empty() ) {
        return path_in;
    }

    Geom::PathVector path_out;

    Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_A = path_in[0].toPwSb();
    Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_B = path_in[1].toPwSb();

    // Transform both paths to (0,0) midpoint, so they can easily be positioned along interpolate_path
    if (Geom::OptRect bounds = Geom::bounds_exact(pwd2_A)) {
        pwd2_A -= bounds->midpoint();
    }
    if (Geom::OptRect bounds = Geom::bounds_exact(pwd2_B)) {
        pwd2_B -= bounds->midpoint();
    }

    // Make sure both paths have the same number of segments and cuts at the same locations
    pwd2_B.setDomain(pwd2_A.domain());
    Geom::Piecewise<Geom::D2<Geom::SBasis> > pA = Geom::partition(pwd2_A, pwd2_B.cuts);
    Geom::Piecewise<Geom::D2<Geom::SBasis> > pB = Geom::partition(pwd2_B, pwd2_A.cuts);

    Geom::Piecewise<Geom::D2<Geom::SBasis> > trajectory = trajectory_path.get_pathvector()[0].toPwSb();
    if (equidistant_spacing)
        trajectory = Geom::arc_length_parametrization(trajectory);

    Geom::Interval trajectory_domain = trajectory.domain();

    for (int i = 0; i < number_of_steps; ++i) {
        double fraction = i / (number_of_steps-1);

        Geom::Piecewise<Geom::D2<Geom::SBasis> > pResult = pA*(1-fraction)  +  pB*fraction;
        pResult += trajectory.valueAt(trajectory_domain.min() + fraction*trajectory_domain.extent());

        Geom::PathVector pathv = Geom::path_from_piecewise(pResult, LPE_CONVERSION_TOLERANCE);
        path_out.push_back( pathv[0] );
    }

    return path_out;
}
Пример #14
0
Geom::PathVector
LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
{
    // Don't allow empty path parameter:
    if ( reflection_line.get_pathvector().empty() ) {
        return path_in;
    }

    Geom::PathVector path_out;
    if (!discard_orig_path) {
        path_out = path_in;
    }

    Geom::PathVector mline(reflection_line.get_pathvector());
    Geom::Point A(mline.front().initialPoint());
    Geom::Point B(mline.back().finalPoint());

    Geom::Affine m1(1.0, 0.0, 0.0, 1.0, A[0], A[1]);
    double hyp = Geom::distance(A, B);
    double c = (B[0] - A[0]) / hyp; // cos(alpha)
    double s = (B[1] - A[1]) / hyp; // sin(alpha)

    Geom::Affine m2(c, -s, s, c, 0.0, 0.0);
    Geom::Affine sca(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);

    Geom::Affine m = m1.inverse() * m2;
    m = m * sca;
    m = m * m2.inverse();
    m = m * m1;

    for (int i = 0; i < static_cast<int>(path_in.size()); ++i) {
        path_out.push_back(path_in[i] * m);
    }

    return path_out;
}
Пример #15
0
void font_instance::LoadGlyph(int glyph_id)
{
    if ( pFont == NULL ) {
        return;
    }
    InitTheFace();
#ifndef USE_PANGO_WIN32
    if ( !FT_IS_SCALABLE(theFace) ) {
        return; // bitmap font
    }
#endif

    if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
        Geom::PathBuilder path_builder;

        if ( nbGlyph >= maxGlyph ) {
            maxGlyph=2*nbGlyph+1;
            glyphs=(font_glyph*)realloc(glyphs,maxGlyph*sizeof(font_glyph));
        }
        font_glyph  n_g;
        n_g.pathvector=NULL;
        n_g.bbox[0]=n_g.bbox[1]=n_g.bbox[2]=n_g.bbox[3]=0;
        n_g.h_advance = 0;
        n_g.v_advance = 0;
        n_g.h_width = 0;
        n_g.v_width = 0;
        bool   doAdd=false;

#ifdef USE_PANGO_WIN32

#ifndef GGO_UNHINTED         // For compatibility with old SDKs.
#define GGO_UNHINTED 0x0100
#endif

        MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
        OUTLINETEXTMETRIC otm;
        GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm);
        GLYPHMETRICS metrics;
        DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
        double scale=1.0/daddy->fontSize;
        n_g.h_advance=metrics.gmCellIncX*scale;
        n_g.v_advance=otm.otmTextMetrics.tmHeight*scale;
        n_g.h_width=metrics.gmBlackBoxX*scale;
        n_g.v_width=metrics.gmBlackBoxY*scale;
        if ( bufferSize == GDI_ERROR) {
            // shit happened
        } else if ( bufferSize == 0) {
            // character has no visual representation, but is valid (eg whitespace)
            doAdd=true;
        } else {
            char *buffer = new char[bufferSize];
            if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) {
                // shit happened
            } else {
                // Platform SDK is rubbish, read KB87115 instead
                DWORD polyOffset=0;
                while ( polyOffset < bufferSize ) {
                    TTPOLYGONHEADER const *polyHeader=(TTPOLYGONHEADER const *)(buffer+polyOffset);
                    if (polyOffset+polyHeader->cb > bufferSize) break;

                    if (polyHeader->dwType == TT_POLYGON_TYPE) {
                        path_builder.moveTo(pointfx_to_nrpoint(polyHeader->pfxStart, scale));
                        DWORD curveOffset=polyOffset+sizeof(TTPOLYGONHEADER);

                        while ( curveOffset < polyOffset+polyHeader->cb ) {
                            TTPOLYCURVE const *polyCurve=(TTPOLYCURVE const *)(buffer+curveOffset);
                            POINTFX const *p=polyCurve->apfx;
                            POINTFX const *endp=p+polyCurve->cpfx;

                            switch (polyCurve->wType) {
                            case TT_PRIM_LINE:
                                while ( p != endp )
                                    path_builder.lineTo(pointfx_to_nrpoint(*p++, scale));
                                break;

                            case TT_PRIM_QSPLINE:
                                {
                                    g_assert(polyCurve->cpfx >= 2);

                                    // The list of points specifies one or more control points and ends with the end point.
                                    // The intermediate points (on the curve) are the points between the control points.
                                    Geom::Point this_control = pointfx_to_nrpoint(*p++, scale);
                                    while ( p+1 != endp ) { // Process all "midpoints" (all points except the last)
                                        Geom::Point new_control = pointfx_to_nrpoint(*p++, scale);
                                        path_builder.quadTo(this_control, (new_control+this_control)/2);
                                        this_control = new_control;
                                    }
                                    Geom::Point end = pointfx_to_nrpoint(*p++, scale);
                                    path_builder.quadTo(this_control, end);
                                }
                                break;

                            case 3:  // TT_PRIM_CSPLINE
                                g_assert(polyCurve->cpfx % 3 == 0);
                                while ( p != endp ) {
                                    path_builder.curveTo(pointfx_to_nrpoint(p[0], scale),
                                                         pointfx_to_nrpoint(p[1], scale),
                                                         pointfx_to_nrpoint(p[2], scale));
                                    p += 3;
                                }
                                break;
                            }
                            curveOffset += sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(polyCurve->cpfx-1);
                        }
                    }
                    polyOffset += polyHeader->cb;
                }
                doAdd=true;
            }
            delete [] buffer;
        }
#else
        if (FT_Load_Glyph (theFace, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) {
            // shit happened
        } else {
            if ( FT_HAS_HORIZONTAL(theFace) ) {
                n_g.h_advance=((double)theFace->glyph->metrics.horiAdvance)/((double)theFace->units_per_EM);
                n_g.h_width=((double)theFace->glyph->metrics.width)/((double)theFace->units_per_EM);
            } else {
                n_g.h_width=n_g.h_advance=((double)(theFace->bbox.xMax-theFace->bbox.xMin))/((double)theFace->units_per_EM);
            }
            if ( FT_HAS_VERTICAL(theFace) ) {
                n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM);
                n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM);
            } else {
                n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM);
            }
            if ( theFace->glyph->format == ft_glyph_format_outline ) {
                FT_Outline_Funcs ft2_outline_funcs = {
                    ft2_move_to,
                    ft2_line_to,
                    ft2_conic_to,
                    ft2_cubic_to,
                    0, 0
                };
                FT2GeomData user(path_builder, 1.0/((double)theFace->units_per_EM));
                FT_Outline_Decompose (&theFace->glyph->outline, &ft2_outline_funcs, &user);
            }
            doAdd=true;
        }
#endif
        path_builder.finish();

        if ( doAdd ) {
            Geom::PathVector pv = path_builder.peek();
            // close all paths
            for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) {
                i->close();
            }
            if ( !pv.empty() ) {
                n_g.pathvector = new Geom::PathVector(pv);
                Geom::OptRect bounds = bounds_exact(*n_g.pathvector);
                if (bounds) {
                    n_g.bbox[0] = bounds->left();
                    n_g.bbox[1] = bounds->top();
                    n_g.bbox[2] = bounds->right();
                    n_g.bbox[3] = bounds->bottom();
                }
            }
            glyphs[nbGlyph]=n_g;
            id_to_no[glyph_id]=nbGlyph;
            nbGlyph++;
        }
    } else {
    }
}
Пример #16
0
void
LPESpiro::doEffect(SPCurve * curve)
{
    using Geom::X;
    using Geom::Y;

    // Make copy of old path as it is changed during processing
    Geom::PathVector const original_pathv = curve->get_pathvector();
    guint len = curve->get_segment_count() + 2;

    curve->reset();
    bezctx *bc = new_bezctx_ink(curve);
    spiro_cp *path = g_new (spiro_cp, len);
    int ip = 0;

    for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
        if (path_it->empty())
            continue;

        // start of path
        {
            Geom::Point p = path_it->front().pointAt(0);
            path[ip].x = p[X];
            path[ip].y = p[Y];
            path[ip].ty = '{' ;  // for closed paths, this is overwritten
            ip++;
        }

        // midpoints
        Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
        Geom::Path::const_iterator curve_it2 = ++(path_it->begin());         // outgoing curve

        Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
        if (path_it->closed()) {
            // if the path is closed, maybe we have to stop a bit earlier because the closing line segment has zerolength.
            const Geom::Curve &closingline = path_it->back_closed(); // the closing line segment is always of type Geom::LineSegment.
            if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
                // closingline.isDegenerate() did not work, because it only checks for *exact* zero length, which goes wrong for relative coordinates and rounding errors...
                // the closing line segment has zero-length. So stop before that one!
                curve_endit = path_it->end_open();
            }
        }

        while ( curve_it2 != curve_endit )
        {
            /* This deals with the node between curve_it1 and curve_it2.
             * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
             * and then curve_it1 points to end or closing segment */
            Geom::Point p = curve_it1->finalPoint();
            path[ip].x = p[X];
            path[ip].y = p[Y];

            // Determine type of spiro node this is, determined by the tangents (angles) of the curves
            // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
            bool this_is_line = is_straight_curve(*curve_it1);
            bool next_is_line = is_straight_curve(*curve_it2);

            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);

            if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
            {
                if (this_is_line && !next_is_line) {
                    path[ip].ty = ']';
                } else if (next_is_line && !this_is_line) {
                    path[ip].ty = '[';
                } else {
                    path[ip].ty = 'c';
                }
            } else {
                path[ip].ty = 'v';
            }

            ++curve_it1;
            ++curve_it2;
            ip++;
        }

        // add last point to the spiropath
        Geom::Point p = curve_it1->finalPoint();
        path[ip].x = p[X];
        path[ip].y = p[Y];
        if (path_it->closed()) {
            // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
            switch (nodetype) {
                case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
                    path[ip].ty = '}';
                    ip++;
                    break;
                case Geom::NODE_CUSP:
                    path[0].ty = path[ip].ty = 'v';
                    break;
                case Geom::NODE_SMOOTH:
                case Geom::NODE_SYMM:
                    path[0].ty = path[ip].ty = 'c';
                    break;
            }
        } else {
            // set type to path closer
            path[ip].ty = '}';
            ip++;
        }

        // run subpath through spiro
        int sp_len = ip;
        spiro_seg *s = run_spiro(path, sp_len);
        spiro_to_bpath(s, sp_len, bc);
        free(s);
        ip = 0;
    }

    g_free (path);
}
Пример #17
0
void
LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result)
{
    if(steps < 1) {
        return;
    }
    Geom::PathVector tmp_path;
    Geom::CubicBezier const *cubic = NULL;
    for (Geom::PathVector::iterator path_it = result.begin(); path_it != result.end(); ++path_it) {
        if (path_it->empty()) {
            continue;
        }

        Geom::Path::iterator curve_it1 = path_it->begin(); // incoming curve
        Geom::Path::iterator curve_it2 = ++(path_it->begin());// outgoing curve
        Geom::Path::iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
        SPCurve *nCurve = new SPCurve();
        if (path_it->closed()) {
            // if the path is closed, maybe we have to stop a bit earlier because the
            // closing line segment has zerolength.
            const Geom::Curve &closingline =
                path_it->back_closed(); // the closing line segment is always of type
            // Geom::LineSegment.
            if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
                // closingline.isDegenerate() did not work, because it only checks for
                // *exact* zero length, which goes wrong for relative coordinates and
                // rounding errors...
                // the closing line segment has zero-length. So stop before that one!
                curve_endit = path_it->end_open();
            }
        }
        if(helper_size > 0) {
            drawNode(curve_it1->initialPoint());
        }
        nCurve->moveto(curve_it1->initialPoint());
        Geom::Point start = Geom::Point(0,0);
        while (curve_it1 != curve_endit) {
            cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
            Geom::Point point_at1 = curve_it1->initialPoint();
            Geom::Point point_at2 = curve_it1->finalPoint();
            Geom::Point point_at3 = curve_it1->finalPoint();
            Geom::Point point_at4 = curve_it1->finalPoint();

            if(start == Geom::Point(0,0)) {
                start = point_at1;
            }

            if (cubic) {
                point_at1 = (*cubic)[1];
                point_at2 = (*cubic)[2];
            }

            if(path_it->closed() && curve_it2 == curve_endit) {
                point_at4 = start;
            }
            if(curve_it2 != curve_endit) {
                cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2);
                if (cubic) {
                    point_at4 = (*cubic)[1];
                }
            }
            Geom::Ray ray1(point_at2, point_at3);
            Geom::Ray ray2(point_at3, point_at4);
            double angle1 = Geom::deg_from_rad(ray1.angle());
            double angle2 = Geom::deg_from_rad(ray2.angle());
            if((smooth_angles  >= std::abs(angle2 - angle1)) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) {
                double dist = Geom::distance(point_at2,point_at3);
                Geom::Angle angleFixed = ray2.angle();
                angleFixed -= Geom::Angle::from_degrees(180.0);
                point_at2 =  Geom::Point::polar(angleFixed, dist) + point_at3;
            }
            nCurve->curveto(point_at1, point_at2, curve_it1->finalPoint());
            cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment());
            if (cubic) {
                point_at1 = (*cubic)[1];
                point_at2 = (*cubic)[2];
                if(helper_size > 0) {
                    if(!are_near((*cubic)[0],(*cubic)[1])) {
                        drawHandle((*cubic)[1]);
                        drawHandleLine((*cubic)[0],(*cubic)[1]);
                    }
                    if(!are_near((*cubic)[3],(*cubic)[2])) {
                        drawHandle((*cubic)[2]);
                        drawHandleLine((*cubic)[3],(*cubic)[2]);
                    }
                }
            }
            if(helper_size > 0) {
                drawNode(curve_it1->finalPoint());
            }
            ++curve_it1;
            ++curve_it2;
        }
        if (path_it->closed()) {
            nCurve->closepath_current();
        }
        tmp_path.push_back(nCurve->get_pathvector()[0]);
        nCurve->reset();
        delete nCurve;
    }
    result = tmp_path;
}
Пример #18
0
/// @todo investigate why Geom::Point p is passed in but ignored.
void Inkscape::ObjectSnapper::_collectPaths(Geom::Point /*p*/,
                                         SnapSourceType const source_type,
                                         bool const &first_point) const
{
    // Now, let's first collect all paths to snap to. If we have a whole bunch of points to snap,
    // e.g. when translating an item using the selector tool, then we will only do this for the
    // first point and store the collection for later use. This significantly improves the performance
    if (first_point) {
        _clear_paths();

        // Determine the type of bounding box we should snap to
        SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX;

        bool p_is_a_node = source_type & SNAPSOURCE_NODE_CATEGORY;
        bool p_is_a_bbox = source_type & SNAPSOURCE_BBOX_CATEGORY;
        bool p_is_other = (source_type & SNAPSOURCE_OTHERS_CATEGORY) || (source_type & SNAPSOURCE_DATUMS_CATEGORY);

        if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_EDGE)) {
            Preferences *prefs = Preferences::get();
            int prefs_bbox = prefs->getBool("/tools/bounding_box", 0);
            bbox_type = !prefs_bbox ?
                SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX;
        }

        // Consider the page border for snapping
        if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PAGE_BORDER) && _snapmanager->snapprefs.isAnyCategorySnappable()) {
            Geom::PathVector *border_path = _getBorderPathv();
            if (border_path != NULL) {
                _paths_to_snap_to->push_back(SnapCandidatePath(border_path, SNAPTARGET_PAGE_BORDER, Geom::OptRect()));
            }
        }

        for (std::vector<SnapCandidateItem>::const_iterator i = _candidates->begin(); i != _candidates->end(); ++i) {

            /* Transform the requested snap point to this item's coordinates */
            Geom::Affine i2doc(Geom::identity());
            SPItem *root_item = NULL;
            /* We might have a clone at hand, so make sure we get the root item */
            if (SP_IS_USE((*i).item)) {
                i2doc = SP_USE((*i).item)->get_root_transform();
                root_item = SP_USE((*i).item)->root();
                g_return_if_fail(root_item);
            } else {
                i2doc = (*i).item->i2doc_affine();
                root_item = (*i).item;
            }

            //Build a list of all paths considered for snapping to

            //Add the item's path to snap to
            if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH, SNAPTARGET_PATH_INTERSECTION, SNAPTARGET_TEXT_BASELINE)) {
                if (p_is_other || p_is_a_node || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_bbox)) {
                    if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) {
                        if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_TEXT_BASELINE)) {
                            // Snap to the text baseline
                            Text::Layout const *layout = te_get_layout(static_cast<SPItem *>(root_item));
                            if (layout != NULL && layout->outputExists()) {
                                Geom::PathVector *pv = new Geom::PathVector();
                                pv->push_back(layout->baseline() * root_item->i2dt_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt());
                                _paths_to_snap_to->push_back(SnapCandidatePath(pv, SNAPTARGET_TEXT_BASELINE, Geom::OptRect()));
                            }
                        }
                    } else {
                        // Snapping for example to a traced bitmap is very stressing for
                        // the CPU, so we'll only snap to paths having no more than 500 nodes
                        // This also leads to a lag of approx. 500 msec (in my lousy test set-up).
                        bool very_complex_path = false;
                        if (SP_IS_PATH(root_item)) {
                            very_complex_path = SP_PATH(root_item)->nodesInPath() > 500;
                        }

                        if (!very_complex_path && root_item && _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH, SNAPTARGET_PATH_INTERSECTION)) {
                            SPCurve *curve = NULL;
                            if (SP_IS_SHAPE(root_item)) {
                               curve = SP_SHAPE(root_item)->getCurve();
                            }/* else if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) {
                               curve = te_get_layout(root_item)->convertToCurves();
                            }*/
                            if (curve) {
                                // We will get our own copy of the pathvector, which must be freed at some point

                                // Geom::PathVector *pv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine);

                                Geom::PathVector *pv = new Geom::PathVector(curve->get_pathvector());
                                (*pv) *= root_item->i2dt_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt(); // (_edit_transform * _i2d_transform);

                                _paths_to_snap_to->push_back(SnapCandidatePath(pv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it.
                                curve->unref();
                            }
                        }
                    }
                }
            }

            //Add the item's bounding box to snap to
            if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_EDGE)) {
                if (p_is_other || p_is_a_bbox || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) {
                    // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox
                    // of the item AND the bbox of the clipping path at the same time
                    if (!(*i).clip_or_mask) {
                        Geom::OptRect rect = root_item->bounds(bbox_type, i2doc);
                        if (rect) {
                            Geom::PathVector *path = _getPathvFromRect(*rect);
                            rect = root_item->desktopBounds(bbox_type);
                            _paths_to_snap_to->push_back(SnapCandidatePath(path, SNAPTARGET_BBOX_EDGE, rect));
                        }
                    }
                }
            }
        }
    }
}
Пример #19
0
static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, SPCurve *curve)
{
    using namespace Inkscape::LivePathEffect;
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();

    if (item && SP_IS_LPE_ITEM(item)) {
        bool simplify = prefs->getInt(tool_name(dc) + "/simplify", 0);
        if(simplify){
            double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0);
            tol = tol/(100.0*(102.0-tol));
            std::ostringstream ss;
            ss << tol;
            spdc_apply_simplify(ss.str(), dc, item);
            sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
        }
        if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1) {
            Effect::createAndApply(SPIRO, dc->desktop->doc(), item);
        }

        if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2) {
            Effect::createAndApply(BSPLINE, dc->desktop->doc(), item);
        }
        SPShape *sp_shape = dynamic_cast<SPShape *>(item);
        if (sp_shape) {
            curve = sp_shape->getCurve();
        }

        //Store the clipboard path to apply in the future without the use of clipboard
        static Geom::PathVector previous_shape_pathv;

        shapeType shape = (shapeType)prefs->getInt(tool_name(dc) + "/shape", 0);
        bool shape_applied = false;
        SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS);
        const char *cstroke = sp_repr_css_property(css_item, "stroke", "none");
        static SPItem *bend_item;

#define SHAPE_LENGTH 10
#define SHAPE_HEIGHT 10


        if(shape == LAST_APPLIED){

            shape = previous_shape_type;
            if(shape == CLIPBOARD || shape == BEND_CLIPBOARD){
                shape = LAST_APPLIED;
            }
        }

        switch (shape) {
            case NONE:
                // don't apply any shape
                break;
            case TRIANGLE_IN:
            {
                // "triangle in"
                std::vector<Geom::Point> points(1);
                points[0] = Geom::Point(0., SHAPE_HEIGHT/2);
                spdc_apply_powerstroke_shape(points, dc, item);

                shape_applied = true;
                break;
            }
            case TRIANGLE_OUT:
            {
                // "triangle out"
                guint curve_length = curve->get_segment_count();
                std::vector<Geom::Point> points(1);
                points[0] = Geom::Point((double)curve_length, SHAPE_HEIGHT/2);
                spdc_apply_powerstroke_shape(points, dc, item);

                shape_applied = true;
                break;
            }
            case ELLIPSE:
            {
                // "ellipse"
                SPCurve *c = new SPCurve();
                const double C1 = 0.552;
                c->moveto(0, SHAPE_HEIGHT/2);
                c->curveto(0, (1 - C1) * SHAPE_HEIGHT/2, (1 - C1) * SHAPE_LENGTH/2, 0, SHAPE_LENGTH/2, 0);
                c->curveto((1 + C1) * SHAPE_LENGTH/2, 0, SHAPE_LENGTH, (1 - C1) * SHAPE_HEIGHT/2, SHAPE_LENGTH, SHAPE_HEIGHT/2);
                c->curveto(SHAPE_LENGTH, (1 + C1) * SHAPE_HEIGHT/2, (1 + C1) * SHAPE_LENGTH/2, SHAPE_HEIGHT, SHAPE_LENGTH/2, SHAPE_HEIGHT);
                c->curveto((1 - C1) * SHAPE_LENGTH/2, SHAPE_HEIGHT, 0, (1 + C1) * SHAPE_HEIGHT/2, 0, SHAPE_HEIGHT/2);
                c->closepath();
                spdc_paste_curve_as_freehand_shape(c->get_pathvector(), dc, item);
                c->unref();

                shape_applied = true;
                break;
            }
            case CLIPBOARD:
            {
                // take shape from clipboard;
                Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
                if(cm->paste(SP_ACTIVE_DESKTOP,true) == true){
                    SPItem * pasted_clipboard = dc->selection->singleItem();
                    if(pasted_clipboard){
                        Inkscape::XML::Node *pasted_clipboard_root = pasted_clipboard->getRepr();
                        Inkscape::XML::Node *path = sp_repr_lookup_name(pasted_clipboard_root, "svg:path", -1); // unlimited search depth
                        if ( path != NULL ) {
                            gchar const *svgd = path->attribute("d");
                            dc->selection->remove(SP_OBJECT(pasted_clipboard));
                            previous_shape_pathv =  sp_svg_read_pathv(svgd);
                            previous_shape_pathv *= pasted_clipboard->transform;
                            spdc_paste_curve_as_freehand_shape(previous_shape_pathv, dc, item);

                            shape = CLIPBOARD;
                            shape_applied = true;
                            pasted_clipboard->deleteObject();
                        } else {
                            shape = NONE;
                        }
                    } else {
                        shape = NONE;
                    }
                } else {
                    shape = NONE;
                }
                break;
            }
            case BEND_CLIPBOARD:
            {
                Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
                if(cm->paste(SP_ACTIVE_DESKTOP,true) == true){
                    gchar const *svgd = item->getRepr()->attribute("d");
                    bend_item = dc->selection->singleItem();
                    if(bend_item){
                        bend_item->moveTo(item,false);
                        bend_item->transform.setTranslation(Geom::Point());
                        spdc_apply_bend_shape(svgd, dc, bend_item);
                        dc->selection->add(SP_OBJECT(bend_item));

                        shape = BEND_CLIPBOARD;
                    } else {
                        shape = NONE;
                    }
                } else {
                    shape = NONE;
                }
                break;
            }
            case LAST_APPLIED:
            {
                if(previous_shape_type == CLIPBOARD){
                    if(previous_shape_pathv.size() != 0){
                        spdc_paste_curve_as_freehand_shape(previous_shape_pathv, dc, item);

                        shape_applied = true;
                        shape = CLIPBOARD;
                    } else{
                        shape = NONE;
                    }
                } else {
                    if(bend_item != NULL && bend_item->getRepr() != NULL){
                        gchar const *svgd = item->getRepr()->attribute("d");
                        dc->selection->add(SP_OBJECT(bend_item));
                        sp_selection_duplicate(dc->desktop);
                        dc->selection->remove(SP_OBJECT(bend_item));
                        bend_item = dc->selection->singleItem();
                        if(bend_item){
                            bend_item->moveTo(item,false);
                            Geom::Coord expansion_X = bend_item->transform.expansionX();
                            Geom::Coord expansion_Y = bend_item->transform.expansionY();
                            bend_item->transform = Geom::Affine(1,0,0,1,0,0);
                            bend_item->transform.setExpansionX(expansion_X);
                            bend_item->transform.setExpansionY(expansion_Y);
                            spdc_apply_bend_shape(svgd, dc, bend_item);
                            dc->selection->add(SP_OBJECT(bend_item));

                            shape = BEND_CLIPBOARD;
                        } else {
                            shape = NONE;
                        }
                    } else {
                        shape = NONE;
                    }
                }
                break;
            }
            default:
                break;
        }
        previous_shape_type = shape;

        if (shape_applied) {
            // apply original stroke color as fill and unset stroke; then return
            SPCSSAttr *css = sp_repr_css_attr_new();

            if (!strcmp(cstroke, "none")){
                sp_repr_css_set_property (css, "fill", "black");
            } else {
                sp_repr_css_set_property (css, "fill", cstroke);
            }
            sp_repr_css_set_property (css, "stroke", "none");
            sp_desktop_apply_css_recursive(item, css, true);
            sp_repr_css_attr_unref(css);
            return;
        }

        if (dc->waiting_LPE_type != INVALID_LPE) {
            Effect::createAndApply(dc->waiting_LPE_type, dc->desktop->doc(), item);
            dc->waiting_LPE_type = INVALID_LPE;

            if (SP_IS_LPETOOL_CONTEXT(dc)) {
                // since a geometric LPE was applied, we switch back to "inactive" mode
                lpetool_context_switch_mode(SP_LPETOOL_CONTEXT(dc), INVALID_LPE);
            }
        }
        if (SP_IS_PEN_CONTEXT(dc)) {
            SP_PEN_CONTEXT(dc)->setPolylineMode();
        }
    }
}
Пример #20
0
void sp_spiro_do_effect(SPCurve *curve){
    using Geom::X;
    using Geom::Y;

    // Make copy of old path as it is changed during processing
    Geom::PathVector const original_pathv = curve->get_pathvector();
    guint len = curve->get_segment_count() + 2;

    curve->reset();
    Spiro::spiro_cp *path = g_new (Spiro::spiro_cp, len);
    int ip = 0;

    for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
        if (path_it->empty())
            continue;

        // start of path
        {
            Geom::Point p = path_it->initialPoint();
            path[ip].x = p[X];
            path[ip].y = p[Y];
            path[ip].ty = '{' ;  // for closed paths, this is overwritten
            ip++;
        }

        // midpoints
        Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
        Geom::Path::const_iterator curve_it2 = ++(path_it->begin());         // outgoing curve
        Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop

        while ( curve_it2 != curve_endit )
        {
            /* This deals with the node between curve_it1 and curve_it2.
             * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
             * and then curve_it1 points to end or closing segment */
            Geom::Point p = curve_it1->finalPoint();
            path[ip].x = p[X];
            path[ip].y = p[Y];

            // Determine type of spiro node this is, determined by the tangents (angles) of the curves
            // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
            bool this_is_line = is_straight_curve(*curve_it1);
            bool next_is_line = is_straight_curve(*curve_it2);

            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);

            if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
            {
                if (this_is_line && !next_is_line) {
                    path[ip].ty = ']';
                } else if (next_is_line && !this_is_line) {
                    path[ip].ty = '[';
                } else {
                    path[ip].ty = 'c';
                }
            } else {
                path[ip].ty = 'v';
            }

            ++curve_it1;
            ++curve_it2;
            ip++;
        }

        // add last point to the spiropath
        Geom::Point p = curve_it1->finalPoint();
        path[ip].x = p[X];
        path[ip].y = p[Y];
        if (path_it->closed()) {
            // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
            switch (nodetype) {
                case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
                    path[ip].ty = '}';
                    ip++;
                    break;
                case Geom::NODE_CUSP:
                    path[0].ty = path[ip].ty = 'v';
                    break;
                case Geom::NODE_SMOOTH:
                case Geom::NODE_SYMM:
                    path[0].ty = path[ip].ty = 'c';
                    break;
            }
        } else {
            // set type to path closer
            path[ip].ty = '}';
            ip++;
        }

        // run subpath through spiro
        int sp_len = ip;
        Spiro::spiro_run(path, sp_len, *curve);
        ip = 0;
    }

    g_free (path);
}