void
RegisteredTransformedPoint::setTransform(Geom::Affine const & canvas_to_svg)
{
    // check if matrix is singular / has inverse
    if ( ! canvas_to_svg.isSingular() ) {
        to_svg = canvas_to_svg;
    } else {
        // set back to default
        to_svg = Geom::identity();
    }
}
Geom::Point
KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint, guint state)
{
    if (state & GDK_SHIFT_MASK) { // Don't snap when shift-key is held
        return p;
    }

    Geom::Affine const i2d (item->i2dt_affine());
    Geom::Point s = p * i2d;

    SnapManager &m = desktop->namedview->snap_manager;
    m.setup(desktop, true, item);

    // constrainedSnap() will first project the point p onto the constraint line and then try to snap along that line.
    // This way the constraint is already enforced, no need to worry about that later on
    Inkscape::Snapper::SnapConstraint transformed_constraint = Inkscape::Snapper::SnapConstraint(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d);
    m.constrainedSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE, transformed_constraint);
    m.unSetup();

    return s * i2d.inverse();
}
Geom::OptRect SPFlowtext::bbox(Geom::Affine const &transform, SPItem::BBoxType type) const {
    Geom::OptRect bbox = this->layout.bounds(transform);

    // Add stroke width
    // FIXME this code is incorrect
    if (bbox && type == SPItem::VISUAL_BBOX && !this->style->stroke.isNone()) {
        double scale = transform.descrim();
        bbox->expandBy(0.5 * this->style->stroke_width.computed * scale);
    }

    return bbox;
}
Geom::Affine SPStar::set_transform(Geom::Affine const &xform)
{
    // Only set transform with proportional scaling
    if (!xform.withoutTranslation().isUniformScale()) {
        return xform;
    }

    // Allow live effects
    if (hasPathEffect() && pathEffectsEnabled()) {
        return xform;
    }

    /* Calculate star start in parent coords. */
    Geom::Point pos( this->center * xform );

    /* This function takes care of translation and scaling, we return whatever parts we can't
       handle. */
    Geom::Affine ret(Geom::Affine(xform).withoutTranslation());
    gdouble const s = hypot(ret[0], ret[1]);
    if (s > 1e-9) {
        ret[0] /= s;
        ret[1] /= s;
        ret[2] /= s;
        ret[3] /= s;
    } else {
        ret[0] = 1.0;
        ret[1] = 0.0;
        ret[2] = 0.0;
        ret[3] = 1.0;
    }

    this->r[0] *= s;
    this->r[1] *= s;

    /* Find start in item coords */
    pos = pos * ret.inverse();
    this->center = pos;

    this->set_shape();

    // Adjust stroke width
    this->adjust_stroke(s);

    // Adjust pattern fill
    this->adjust_pattern(xform * ret.inverse());

    // Adjust gradient fill
    this->adjust_gradient(xform * ret.inverse());

    this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);

    return ret;
}
Geom::Affine SPFlowtext::set_transform (Geom::Affine const &xform)
{
    if ((this->_optimizeScaledText && !xform.withoutTranslation().isNonzeroUniformScale())
        || (!this->_optimizeScaledText && !xform.isNonzeroUniformScale())) {
        this->_optimizeScaledText = false;
        return xform;
    }
    this->_optimizeScaledText = false;
    
    SPText *text = reinterpret_cast<SPText *>(this);
    
    double const ex = xform.descrim();
    if (ex == 0) {
        return xform;
    }

    Geom::Affine ret(xform);
    ret[0] /= ex;
    ret[1] /= ex;
    ret[2] /= ex;
    ret[3] /= ex;

    // Adjust font size
    text->_adjustFontsizeRecursive (this, ex);

    // Adjust stroke width
    this->adjust_stroke_width_recursive (ex);

    // Adjust pattern fill
    this->adjust_pattern(xform * ret.inverse());

    // Adjust gradient fill
    this->adjust_gradient(xform * ret.inverse());

    this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG);

    return ret;
}
Exemple #6
0
void SPClipPath::update(SPCtx* ctx, unsigned int flags) {
    if (flags & SP_OBJECT_MODIFIED_FLAG) {
        flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
    }

    flags &= SP_OBJECT_MODIFIED_CASCADE;

    GSList *l = NULL;
    for ( SPObject *child = this->firstChild(); child; child = child->getNext()) {
        sp_object_ref(child);
        l = g_slist_prepend(l, child);
    }

    l = g_slist_reverse(l);

    while (l) {
        SPObject *child = SP_OBJECT(l->data);
        l = g_slist_remove(l, child);

        if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
            child->updateDisplay(ctx, flags);
        }

        sp_object_unref(child);
    }

    for (SPClipPathView *v = this->display; v != NULL; v = v->next) {
        Inkscape::DrawingGroup *g = dynamic_cast<Inkscape::DrawingGroup *>(v->arenaitem);

        if (this->clipPathUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX && v->bbox) {
            Geom::Affine t = Geom::Scale(v->bbox->dimensions());
            t.setTranslation(v->bbox->min());
            g->setChildTransform(t);
        } else {
            g->setChildTransform(Geom::identity());
        }
    }
}
Exemple #7
0
void
sp_gradient_set_gs2d_matrix(SPGradient *gr, Geom::Affine const &ctm,
                            Geom::Rect const &bbox, Geom::Affine const &gs2d)
{
    gr->gradientTransform = gs2d * ctm.inverse();
    if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) {
        gr->gradientTransform = ( gr->gradientTransform
                                  * Geom::Translate(-bbox.min())
                                  * Geom::Scale(bbox.dimensions()).inverse() );
    }
    gr->gradientTransform_set = TRUE;

    gr->requestModified(SP_OBJECT_MODIFIED_FLAG);
}
static void sp_offset_move_compensate(Geom::Affine const *mp, SPItem */*original*/, SPOffset *self)
{
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    guint mode = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_PARALLEL);

    Geom::Affine m(*mp);

    if (!(m.isTranslation()) || mode == SP_CLONE_COMPENSATION_NONE) {
        self->sourceDirty=true;
        self->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
        return;
    }

    // calculate the compensation matrix and the advertized movement matrix
    self->readAttr("transform");

    Geom::Affine t = self->transform;
    Geom::Affine offset_move = t.inverse() * m * t;

    Geom::Affine advertized_move;
    if (mode == SP_CLONE_COMPENSATION_PARALLEL) {
        offset_move = offset_move.inverse() * m;
        advertized_move = m;
    } else if (mode == SP_CLONE_COMPENSATION_UNMOVED) {
        offset_move = offset_move.inverse();
        advertized_move.setIdentity();
    } else {
        g_assert_not_reached();
    }

    self->sourceDirty=true;

    // commit the compensation
    self->transform *= offset_move;
    self->doWriteTransform(self->getRepr(), self->transform, &advertized_move);
    self->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
Exemple #9
0
/**
 * Set additional transform for the group.
 * This is applied after the normal transform and mainly useful for
 * markers, clipping paths, etc.
 */
void
DrawingGroup::setChildTransform(Geom::Affine const &new_trans)
{
    Geom::Affine current;
    if (_child_transform) {
        current = *_child_transform;
    }

    if (!Geom::are_near(current, new_trans, 1e-18)) {
        // mark the area where the object was for redraw.
        _markForRendering();
        if (new_trans.isIdentity()) {
            delete _child_transform; // delete NULL; is safe
            _child_transform = NULL;
        } else {
            _child_transform = new Geom::Affine(new_trans);
        }
        _markForUpdate(STATE_ALL, true);
    }
}
Exemple #10
0
Geom::Affine SPLine::set_transform(Geom::Affine const &transform) {
    Geom::Point points[2];

    points[0] = Geom::Point(this->x1.computed, this->y1.computed);
    points[1] = Geom::Point(this->x2.computed, this->y2.computed);

    points[0] *= transform;
    points[1] *= transform;

    this->x1.computed = points[0][Geom::X];
    this->y1.computed = points[0][Geom::Y];
    this->x2.computed = points[1][Geom::X];
    this->y2.computed = points[1][Geom::Y];

    this->adjust_stroke(transform.descrim());

    this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);

    return Geom::identity();
}
Exemple #11
0
/**
 * Writes the given transform into the repr for the given item.
 */
static Geom::Affine
sp_path_set_transform(SPItem *item, Geom::Affine const &xform)
{
    if (!SP_IS_PATH(item)) {
        return Geom::identity();
    }
    SPPath *path = SP_PATH(item);

    if (!path->_curve) { // 0 nodes, nothing to transform
        return Geom::identity();
    }

    // Transform the original-d path if this is a valid LPE item, other else the (ordinary) path
    if (path->_curve_before_lpe && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(item))) {
        if (sp_lpe_item_has_path_effect_of_type(SP_LPE_ITEM(item), Inkscape::LivePathEffect::CLONE_ORIGINAL)) {
            // if path has the CLONE_ORIGINAL LPE applied, don't write the transform to the pathdata, but write it 'unoptimized'
            return xform;
        } else {
            path->_curve_before_lpe->transform(xform);
        }
    } else {
        path->_curve->transform(xform);
    }

    // Adjust stroke
    item->adjust_stroke(xform.descrim());

    // Adjust pattern fill
    item->adjust_pattern(xform);

    // Adjust gradient fill
    item->adjust_gradient(xform);

    // Adjust LPE
    item->adjust_livepatheffect(xform);

    item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);

    // nothing remains - we've written all of the transform, so return identity
    return Geom::identity();
}
Exemple #12
0
Geom::Affine SPLine::setTransform(SPItem *item, Geom::Affine const &xform)
{
    SPLine *line = SP_LINE(item);
    Geom::Point points[2];

    points[0] = Geom::Point(line->x1.computed, line->y1.computed);
    points[1] = Geom::Point(line->x2.computed, line->y2.computed);

    points[0] *= xform;
    points[1] *= xform;

    line->x1.computed = points[0][Geom::X];
    line->y1.computed = points[0][Geom::Y];
    line->x2.computed = points[1][Geom::X];
    line->y2.computed = points[1][Geom::Y];

    item->adjust_stroke(xform.descrim());

    SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);

    return Geom::identity();
}
static gdouble sp_pattern_extract_theta(SPPattern *pat)
{
    Geom::Affine transf = pat->patternTransform;
    return Geom::atan2(transf.xAxis());
}
Exemple #14
0
/**
 * Sensing a movement of the original, this function attempts to compensate for it in such a way
 * that the clone stays unmoved or moves in parallel (depending on user setting) regardless of the
 * clone's transform.
 */
void SPUse::move_compensate(Geom::Affine const *mp) {
    // the clone is orphaned; or this is not a real use, but a clone of another use;
    // we skip it, otherwise duplicate compensation will occur
    if (this->cloned) {
        return;
    }

    // never compensate uses which are used in flowtext
    if (parent && dynamic_cast<SPFlowregion *>(parent)) {
        return;
    }

    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    guint mode = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_PARALLEL);
    // user wants no compensation
    if (mode == SP_CLONE_COMPENSATION_NONE)
        return;

    Geom::Affine m(*mp);
    Geom::Affine t = this->get_parent_transform();
    Geom::Affine clone_move = t.inverse() * m * t;

    // this is not a simple move, do not try to compensate
    if (!(m.isTranslation())){
    	//BUT move clippaths accordingly.
        //if clone has a clippath, move it accordingly
        if(clip_ref->getObject()){
            SPObject *clip = clip_ref->getObject()->firstChild() ;
            while(clip){
            	SPItem *item = (SPItem*) clip;
            	if(item){
                    item->transform *= m;
                    Geom::Affine identity;
                    item->doWriteTransform(clip->getRepr(),item->transform, &identity);
            	}
                clip = clip->getNext();
            }
        }
        if(mask_ref->getObject()){
            SPObject *mask = mask_ref->getObject()->firstChild() ;
            while(mask){
            	SPItem *item = (SPItem*) mask;
            	if(item){
                    item->transform *= m;
                    Geom::Affine identity;
                    item->doWriteTransform(mask->getRepr(),item->transform, &identity);
            	}
                mask = mask->getNext();
            }
        }
        return;
    }

    // restore item->transform field from the repr, in case it was changed by seltrans
    this->readAttr ("transform");


    // calculate the compensation matrix and the advertized movement matrix
    Geom::Affine advertized_move;
    if (mode == SP_CLONE_COMPENSATION_PARALLEL) {
        clone_move = clone_move.inverse() * m;
        advertized_move = m;
    } else if (mode == SP_CLONE_COMPENSATION_UNMOVED) {
        clone_move = clone_move.inverse();
        advertized_move.setIdentity();
    } else {
        g_assert_not_reached();
    }

    //if clone has a clippath, move it accordingly
    if(clip_ref->getObject()){
        SPObject *clip = clip_ref->getObject()->firstChild() ;
        while(clip){
        	SPItem *item = (SPItem*) clip;
        	if(item){
                item->transform *= clone_move.inverse();
                Geom::Affine identity;
                item->doWriteTransform(clip->getRepr(),item->transform, &identity);
        	}
            clip = clip->getNext();
        }
    }
    if(mask_ref->getObject()){
        SPObject *mask = mask_ref->getObject()->firstChild() ;
        while(mask){
        	SPItem *item = (SPItem*) mask;
        	if(item){
                item->transform *= clone_move.inverse();
                Geom::Affine identity;
                item->doWriteTransform(mask->getRepr(),item->transform, &identity);
        	}
            mask = mask->getNext();
        }
    }


    // commit the compensation
    this->transform *= clone_move;
    this->doWriteTransform(this->getRepr(), this->transform, &advertized_move);
    this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
Exemple #15
0
    void draw(cairo_t *cr,
              std::ostringstream *notify,
              int width, int height, bool save, std::ostringstream *timer_stream) {

        if (first_time)
        {
            first_time = false;
            sliders[0].geometry(Point(50, 50), 100);
        }
        size_t const num_points = static_cast<size_t>(sliders[0].value());

        D2<SBasis> B1 = b_handle.asBezier();
        Piecewise<D2<SBasis> >B;
        B.concat(Piecewise<D2<SBasis> >(B1));

        // testing fuse_nearby_ends
        std::vector< Piecewise<D2<SBasis> > > pieces;
        pieces = fuse_nearby_ends(split_at_discontinuities(B),9);
        Piecewise<D2<SBasis> > C;
        for (unsigned i=0; i<pieces.size(); i++){
            C.concat(pieces[i]);
        }
        // testing fuse_nearby_ends

        cairo_set_line_width (cr, 2.);
        cairo_set_source_rgba (cr, 0., 0.5, 0., 1);
        //cairo_d2_sb(cr, B1);
        //cairo_pw_d2_sb(cr, C);
        //cairo_pw_d2_sb(cr, B);
        cairo_stroke(cr);

        Timer tm;
        Timer::Time als_time = tm.lap();

        cairo_set_source_rgba (cr, 0., 0., 0.9, 1);
        //dot_plot(cr,uniform_B);
        cairo_stroke(cr);

        std::cout << B[0] << std::endl;

        Geom::Affine translation;

        Geom::Path original_path;
        //original_bezier.append(B[0]);
        //original_bezier.appendNew<CubicBezier> (B[0]);
        CubicBezier original_bezier(b_handle.pts);
        original_path.append(original_bezier);

        std::vector<double> initial_t;
        std::vector<Geom::Point> curve_points;
        if (randomize_times) {
            std::uniform_real_distribution<double> dist_t(0,1);
            for (size_t ii = 0; ii < num_points; ++ii) {
                double const t = dist_t(generator);
                initial_t.push_back(t);
            }
            std::sort(initial_t.begin(), initial_t.end());
            double const min = initial_t.front();
            double const max = initial_t.back();
            for (auto& t : initial_t) {
                t = (t-min)/(max-min);
            }
            for (auto const t : initial_t) {
                curve_points.push_back(original_bezier.pointAt(t));
            }
        }
        else {
            for (size_t ii = 0; ii < num_points; ++ii) {
                double const t = static_cast<double>(ii) / (num_points-1);
                Geom::Point const p = original_bezier.pointAt(t);
                initial_t.push_back(t);
                curve_points.push_back(p);
            }
        }

        cairo_set_source_rgba (cr, 0., 0., .9, 1);
        cairo_path(cr, original_path);
        draw_text(cr, original_path.initialPoint(), "original curve and old fit");


        Geom::CubicBezier fitted_new;
        Geom::CubicBezier fitted_new_a;
        Geom::Point very_old_version_raw[4];
        bezier_fit_cubic(very_old_version_raw, curve_points.data(), curve_points.size(), 0.);
        Geom::CubicBezier very_old_bezier(
                    very_old_version_raw[0],
                very_old_version_raw[1],
                very_old_version_raw[2],
                very_old_version_raw[3]
                );

        Geom::Path very_old_version_path;
        very_old_version_path.append(very_old_bezier);

        cairo_set_source_rgba (cr, .7, .7, 0., 1);
        cairo_stroke(cr);
        cairo_path(cr, very_old_version_path);

        cairo_set_source_rgba (cr, 0., 0., .9, 1);
        cairo_stroke(cr);
        cross_plot(cr, curve_points);

        if(1) {
            Geom::CubicBezier combination(very_old_bezier);
            tm.ask_for_timeslice();
            tm.start();
            auto new_result_ig_a = experiment::fit_bezier(combination, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit a, old algorithm as initial guess, time = " << als_time << std::endl
                    << "Worst residual: " << new_result_ig_a.first << " at t=" << new_result_ig_a.second << std::endl;

            Geom::Path combination_path;
            translation.setTranslation(Geom::Point(300,300));
            combination_path.append(combination.transformed(translation));

            cairo_set_source_rgba (cr, .0, .0, .9, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, combination_path);
            draw_text(cr, combination_path.initialPoint(), "old fit as i.g.");
        }
        {
            tm.ask_for_timeslice();
            tm.start();
            auto new_result = fit_bezier(fitted_new, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit, time = " << als_time << std::endl
                    << "Worst residual: " << new_result.first << " at t=" << new_result.second << std::endl;


            Geom::Path fitted_new_path;
            translation.setTranslation(Geom::Point(300,0));
            fitted_new_path.append(fitted_new.transformed(translation));

            cairo_set_source_rgba (cr, .0, .9, .0, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, fitted_new_path);
            draw_text(cr, fitted_new_path.initialPoint(), "new fit");
        }

        {
            tm.ask_for_timeslice();
            tm.start();
            auto new_result_a = experiment::fit_bezier(fitted_new_a, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit a, time = " << als_time << std::endl
                    << "Worst residual: " << new_result_a.first << " at t=" << new_result_a.second << std::endl;




            Geom::Path fitted_new_a_path;
            translation.setTranslation(Geom::Point(0,300));
            fitted_new_a_path.append(fitted_new_a.transformed(translation));


            cairo_set_source_rgba (cr, .9, .0, .0, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, fitted_new_a_path);
            draw_text(cr, fitted_new_a_path.initialPoint(), "new fit (a)");
        }

        Geom::CubicBezier fixed_times_bezier;
        {
            tm.ask_for_timeslice();
            tm.start();
            auto fixed_times_result = experiment::fit_bezier_fixed_times(fixed_times_bezier, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit a (fixed times), time = " << als_time << std::endl
                    << "Worst residual: " << fixed_times_result.first << " at t=" << fixed_times_result.second << std::endl;

            Geom::Path fixed_times_path;
            translation.setTranslation(Geom::Point(600,300));
            fixed_times_path.append(fixed_times_bezier.transformed(translation));

            cairo_set_source_rgba (cr, .9, .0, .0, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, fixed_times_path);
            draw_text(cr, fixed_times_path.initialPoint(), "fixed t fit (a)");
        }

        Geom::CubicBezier fixed_times_ig_bezier = fixed_times_bezier;
        {
            tm.ask_for_timeslice();
            tm.start();
            auto fixed_times_ig_result = experiment::fit_bezier(fixed_times_ig_bezier, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit a (with fixed times as i.g.), time = " << als_time << std::endl
                    << "Worst residual: " << fixed_times_ig_result.first << " at t=" << fixed_times_ig_result.second << std::endl;

            Geom::Path fixed_times_path;
            translation.setTranslation(Geom::Point(900,300));
            fixed_times_path.append(fixed_times_ig_bezier.transformed(translation));

            cairo_set_source_rgba (cr, .9, .0, .0, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, fixed_times_path);
            draw_text(cr, fixed_times_path.initialPoint(), "new (a) with fixed t as i.g.");
        }

        Geom::CubicBezier icp_bezier;
        {
            tm.ask_for_timeslice();
            tm.start();
            auto icp_result = experiment::fit_bezier_icp(icp_bezier, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit icp, time = " << als_time << std::endl
                    << "Worst residual: " << icp_result.first << " at t=" << icp_result.second << std::endl;

            Geom::Path icp_path;
            translation.setTranslation(Geom::Point(600,600));
            icp_path.append(icp_bezier.transformed(translation));

            cairo_set_source_rgba (cr, .9, .0, .0, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, icp_path);
            draw_text(cr, icp_path.initialPoint(), "icp fit");
        }

        Geom::CubicBezier icp_ig_bezier(icp_bezier);
        {
            tm.ask_for_timeslice();
            tm.start();
            auto icp_ig_result = experiment::fit_bezier(icp_ig_bezier, curve_points);
            als_time = tm.lap();
            *notify << "Bezier fit with icp i.g., time = " << als_time << std::endl
                    << "Worst residual: " << icp_ig_result.first << " at t=" << icp_ig_result.second << std::endl;

            Geom::Path icp_ig_path;
            translation.setTranslation(Geom::Point(900,600));
            icp_ig_path.append(icp_ig_bezier.transformed(translation));

            cairo_set_source_rgba (cr, .9, .0, .0, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, icp_ig_path);
            draw_text(cr, icp_ig_path.initialPoint(), "bezier fit with icp as i.g.");
        }

        std::cout << "original: " << write_svg_path(original_path) << std::endl;

        Geom::CubicBezier initial_guess(
                    curve_points.front(), curve_points.front(),
                    curve_points.back(), curve_points.back()
                    );
        {
            experiment::get_initial_guess(initial_guess, curve_points);

            Geom::Path initial_guess_path;
            translation.setTranslation(Geom::Point(600,0));
            initial_guess_path.append(initial_guess.transformed(translation));

            cairo_set_source_rgba (cr, .8, .0, .8, 1);
            cross_plot(cr, curve_points, translation.translation());
            cairo_path(cr, initial_guess_path);
            draw_text(cr, initial_guess_path.initialPoint(), "initial guess");
        }

        cairo_stroke(cr);

        Toy::draw(cr, notify, width, height, save,timer_stream);
    }
Exemple #16
0
Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bboxtype) const {
    Geom::OptRect bbox;

    if (!this->_curve) {
    	return bbox;
    }

    bbox = bounds_exact_transformed(this->_curve->get_pathvector(), transform);

    if (!bbox) {
    	return bbox;
    }

    if (bboxtype == SPItem::VISUAL_BBOX) {
        // convert the stroke to a path and calculate that path's geometric bbox

        if (!this->style->stroke.isNone()) {
            Geom::PathVector *pathv = item_outline(this, true);  // calculate bbox_only

            if (pathv) {
                bbox |= bounds_exact_transformed(*pathv, transform);
                delete pathv;
            }
        }

        // Union with bboxes of the markers, if any
        if ( this->hasMarkers()  && !this->_curve->get_pathvector().empty() ) {
            /** \todo make code prettier! */
            Geom::PathVector const & pathv = this->_curve->get_pathvector();
            // START marker
            for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
                if ( this->_marker[i] ) {
                    SPItem* marker_item = sp_item_first_item_child( _marker[i] );

                    if (marker_item) {
                        Geom::Affine tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));

                        if (_marker[i]->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) {
                            // Reverse start marker if necessary
                            tr = Geom::Rotate::from_degrees( 180.0 ) * tr;
                        } else if (_marker[i]->orient_mode == MARKER_ORIENT_ANGLE) {
                            Geom::Point transl = tr.translation();
                            tr = Geom::Rotate::from_degrees(_marker[i]->orient) * Geom::Translate(transl);
                        }

                        if (_marker[i]->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
                            tr = Geom::Scale(this->style->stroke_width.computed) * tr;
                        }

                        // total marker transform
                        tr = marker_item->transform * _marker[i]->c2p * tr * transform;

                        // get bbox of the marker with that transform
                        bbox |= marker_item->visualBounds(tr);
                    }
                }
            }

            // MID marker
            for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
                if ( !this->_marker[i] ) {
                	continue;
                }

                SPMarker* marker = _marker[i];
                SPItem* marker_item = sp_item_first_item_child( marker );

                if ( !marker_item ) {
                	continue;
                }

                for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
                    // START position
                    if ( path_it != pathv.begin() 
                         && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
                    {
                        Geom::Affine tr(sp_shape_marker_get_transform_at_start(path_it->front()));

                        if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
                            Geom::Point transl = tr.translation();
                            tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
                        }

                        if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
                            tr = Geom::Scale(this->style->stroke_width.computed) * tr;
                        }

                        tr = marker_item->transform * marker->c2p * tr * transform;
                        bbox |= marker_item->visualBounds(tr);
                    }

                    // MID position
                    if ( path_it->size_default() > 1) {
                        Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
                        Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve

                        while (curve_it2 != path_it->end_default())
                        {
                            /* Put marker between curve_it1 and curve_it2.
                             * Loop to end_default (so including closing segment), because when a path is closed,
                             * there should be a midpoint marker between last segment and closing straight line segment */

                            SPMarker* marker = _marker[i];
                            SPItem* marker_item = sp_item_first_item_child( marker );

                            if (marker_item) {
                                Geom::Affine tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));

                                if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
                                    Geom::Point transl = tr.translation();
                                    tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
                                }

                                if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
                                    tr = Geom::Scale(this->style->stroke_width.computed) * tr;
                                }

                                tr = marker_item->transform * marker->c2p * tr * transform;
                                bbox |= marker_item->visualBounds(tr);
                            }

                            ++curve_it1;
                            ++curve_it2;
                        }
                    }

                    // END position
                    if ( path_it != (pathv.end()-1) && !path_it->empty()) {
                        Geom::Curve const &lastcurve = path_it->back_default();
                        Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve);

                        if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
                            Geom::Point transl = tr.translation();
                            tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
                        }

                        if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
                            tr = Geom::Scale(this->style->stroke_width.computed) * tr;
                        }

                        tr = marker_item->transform * marker->c2p * tr * transform;
                        bbox |= marker_item->visualBounds(tr);
                    }
                }
            }

            // END marker
            for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
                if ( _marker[i] ) {
                    SPMarker* marker = _marker[i];
                    SPItem* marker_item = sp_item_first_item_child( marker );

                    if (marker_item) {
                        /* Get reference to last curve in the path.
                         * For moveto-only path, this returns the "closing line segment". */
                        Geom::Path const &path_last = pathv.back();
                        unsigned int index = path_last.size_default();

                        if (index > 0) {
                            index--;
                        }

                        Geom::Curve const &lastcurve = path_last[index];

                        Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve);

                        if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
                            Geom::Point transl = tr.translation();
                            tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
                        }

                        if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
                            tr = Geom::Scale(this->style->stroke_width.computed) * tr;
                        }

                        // total marker transform
                        tr = marker_item->transform * marker->c2p * tr * transform;

                        // get bbox of the marker with that transform
                        bbox |= marker_item->visualBounds(tr);
                    }
                }
            }
        }
    }

    return bbox;
}
Exemple #17
0
void
sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done)
{
    g_return_if_fail (group != NULL);
    g_return_if_fail (SP_IS_GROUP (group));

    SPDocument *doc = group->document;
    SPRoot *root = doc->getRoot();
    SPObject *defs = root->defs;

    SPItem *gitem = group;
    Inkscape::XML::Node *grepr = gitem->getRepr();

    g_return_if_fail (!strcmp (grepr->name(), "svg:g") || !strcmp (grepr->name(), "svg:a") || !strcmp (grepr->name(), "svg:switch"));

    // this converts the gradient/pattern fill/stroke on the group, if any, to userSpaceOnUse
    gitem->adjust_paint_recursive (Geom::identity(), Geom::identity(), false);

    SPItem *pitem = SP_ITEM(gitem->parent);
    Inkscape::XML::Node *prepr = pitem->getRepr();

	if (SP_IS_BOX3D(gitem)) {
		group = box3d_convert_to_group(SP_BOX3D(gitem));
		gitem = group;
	}

	sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(group), false);

    /* Step 1 - generate lists of children objects */
    GSList *items = NULL;
    GSList *objects = NULL;
    for (SPObject *child = group->firstChild() ; child; child = child->getNext() ) {

        if (SP_IS_ITEM (child)) {

            SPItem *citem = SP_ITEM (child);

            /* Merging of style */
            // this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do
            // it here _before_ the new transform is set, so as to use the pre-transform bbox
            citem->adjust_paint_recursive (Geom::identity(), Geom::identity(), false);

            sp_style_merge_from_dying_parent(child->style, gitem->style);
            /*
             * fixme: We currently make no allowance for the case where child is cloned
             * and the group has any style settings.
             *
             * (This should never occur with documents created solely with the current
             * version of inkscape without using the XML editor: we usually apply group
             * style changes to children rather than to the group itself.)
             *
             * If the group has no style settings, then
             * sp_style_merge_from_dying_parent should be a no-op.  Otherwise (i.e. if
             * we change the child's style to compensate for its parent going away)
             * then those changes will typically be reflected in any clones of child,
             * whereas we'd prefer for Ungroup not to affect the visual appearance.
             *
             * The only way of preserving styling appearance in general is for child to
             * be put into a new group -- a somewhat surprising response to an Ungroup
             * command.  We could add a new groupmode:transparent that would mostly
             * hide the existence of such groups from the user (i.e. editing behaves as
             * if the transparent group's children weren't in a group), though that's
             * extra complication & maintenance burden and this case is rare.
             */

            child->updateRepr();

            Inkscape::XML::Node *nrepr = child->getRepr()->duplicate(prepr->document());

            // Merging transform
            Geom::Affine ctrans;
            Geom::Affine const g(gitem->transform);
            if (SP_IS_USE(citem) && sp_use_get_original (SP_USE(citem)) &&
                sp_use_get_original( SP_USE(citem) )->parent == SP_OBJECT(group)) {
                // make sure a clone's effective transform is the same as was under group
                ctrans = g.inverse() * citem->transform * g;
            } else {
                // We should not apply the group's transformation to both a linked offset AND to its source
                if (SP_IS_OFFSET(citem)) { // Do we have an offset at hand (whether it's dynamic or linked)?
                    SPItem *source = sp_offset_get_source(SP_OFFSET(citem));
                    // When dealing with a chain of linked offsets, the transformation of an offset will be
                    // tied to the transformation of the top-most source, not to any of the intermediate
                    // offsets. So let's find the top-most source
                    while (source != NULL && SP_IS_OFFSET(source)) {
                        source = sp_offset_get_source(SP_OFFSET(source));
                    }
                    if (source != NULL && // If true then we must be dealing with a linked offset ...
                        group->isAncestorOf(source) == false) { // ... of which the source is not in the same group
                        ctrans = citem->transform * g; // then we should apply the transformation of the group to the offset
                    } else {
                        ctrans = citem->transform;
                    }
                } else {
                    ctrans = citem->transform * g;
                }
            }

            // FIXME: constructing a transform that would fully preserve the appearance of a
            // textpath if it is ungrouped with its path seems to be impossible in general
            // case. E.g. if the group was squeezed, to keep the ungrouped textpath squeezed
            // as well, we'll need to relink it to some "virtual" path which is inversely
            // stretched relative to the actual path, and then squeeze the textpath back so it
            // would both fit the actual path _and_ be squeezed as before. It's a bummer.

            // This is just a way to temporarily remember the transform in repr. When repr is
            // reattached outside of the group, the transform will be written more properly
            // (i.e. optimized into the object if the corresponding preference is set)
            gchar *affinestr=sp_svg_transform_write(ctrans);
            nrepr->setAttribute("transform", affinestr);
            g_free(affinestr);

            items = g_slist_prepend (items, nrepr);

        } else {
            Inkscape::XML::Node *nrepr = child->getRepr()->duplicate(prepr->document());
            objects = g_slist_prepend (objects, nrepr);
        }
    }

    /* Step 2 - clear group */
    // remember the position of the group
    gint pos = group->getRepr()->position();

    // the group is leaving forever, no heir, clones should take note; its children however are going to reemerge
    group->deleteObject(true, false);

    /* Step 3 - add nonitems */
    if (objects) {
        Inkscape::XML::Node *last_def = defs->getRepr()->lastChild();
        while (objects) {
            Inkscape::XML::Node *repr = (Inkscape::XML::Node *) objects->data;
            if (!sp_repr_is_meta_element(repr)) {
                defs->getRepr()->addChild(repr, last_def);
            }
            Inkscape::GC::release(repr);
            objects = g_slist_remove (objects, objects->data);
        }
    }

    /* Step 4 - add items */
    while (items) {
        Inkscape::XML::Node *repr = (Inkscape::XML::Node *) items->data;
        // add item
        prepr->appendChild(repr);
        // restore position; since the items list was prepended (i.e. reverse), we now add
        // all children at the same pos, which inverts the order once again
        repr->setPosition(pos > 0 ? pos : 0);

        // fill in the children list if non-null
        SPItem *item = static_cast<SPItem *>(doc->getObjectByRepr(repr));

        item->doWriteTransform(repr, item->transform, NULL, false);

        Inkscape::GC::release(repr);
        if (children && SP_IS_ITEM (item))
            *children = g_slist_prepend (*children, item);

        items = g_slist_remove (items, items->data);
    }

    if (do_done) {
        DocumentUndo::done(doc, SP_VERB_NONE, _("Ungroup"));
    }
}
static Geom::Point sp_pattern_extract_scale(SPPattern *pat)
{
    Geom::Affine transf = pat->patternTransform;
    return Geom::Point( transf.expansionX(), transf.expansionY() );
}