bool SPLPEItem::hasPathEffectRecursive() const { if (parent && SP_IS_LPE_ITEM(parent)) { return hasPathEffect() || SP_LPE_ITEM(parent)->hasPathEffectRecursive(); } else { return hasPathEffect(); } }
Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform) { // Allow live effects if (hasPathEffect() && pathEffectsEnabled()) { return xform; } /* Calculate ellipse start in parent coords. */ Geom::Point pos(Geom::Point(this->cx.computed, this->cy.computed) * 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 sw = hypot(ret[0], ret[1]); gdouble const sh = hypot(ret[2], ret[3]); if (sw > 1e-9) { ret[0] /= sw; ret[1] /= sw; } else { ret[0] = 1.0; ret[1] = 0.0; } if (sh > 1e-9) { ret[2] /= sh; ret[3] /= sh; } else { ret[2] = 0.0; ret[3] = 1.0; } if (this->rx._set) { this->rx = this->rx.computed * sw; } if (this->ry._set) { this->ry = this->ry.computed * sh; } /* Find start in item coords */ pos = pos * ret.inverse(); this->cx = pos[Geom::X]; this->cy = pos[Geom::Y]; this->set_shape(); // Adjust stroke width this->adjust_stroke(sqrt(fabs(sw * sh))); // 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 SPStar::set_transform(Geom::Affine const &xform) { bool opt_trans = (randomized == 0); // 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(opt_trans ? xform.withoutTranslation() : xform); 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; }
Inkscape::XML::Node* SPLPEItem::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { if (flags & SP_OBJECT_WRITE_EXT) { if ( hasPathEffect() ) { std::string href = patheffectlist_write_svg(*this->path_effect_list); repr->setAttribute("inkscape:path-effect", href.c_str()); } else { repr->setAttribute("inkscape:path-effect", NULL); } } SPItem::write(xml_doc, repr, flags); return repr; }
/** * Return duplicate of curve *before* LPE (if any exists) or NULL if there is no curve */ SPCurve * SPShape::getCurveBeforeLPE() const { if (hasPathEffect()) { if (_curve_before_lpe) { return this->_curve_before_lpe->copy(); } } else { if (_curve) { return _curve->copy(); } } return NULL; }
void SPStar::set_shape() { // perhaps we should convert all our shapes into LPEs without source path // and with knotholders for parameters, then this situation will be handled automatically // by disabling the entire stack (including the shape LPE) if (hasBrokenPathEffect()) { g_warning ("The star shape has unknown LPE on it! Convert to path to make it editable preserving the appearance; editing it as star will remove the bad LPE"); if (this->getRepr()->attribute("d")) { // unconditionally read the curve from d, if any, to preserve appearance Geom::PathVector pv = sp_svg_read_pathv(this->getRepr()->attribute("d")); SPCurve *cold = new SPCurve(pv); this->setCurveInsync( cold, TRUE); this->setCurveBeforeLPE(cold); cold->unref(); } return; } SPCurve *c = new SPCurve (); bool not_rounded = (fabs (this->rounded) < 1e-4); // note that we pass randomized=true to sp_star_get_xy, because the curve must be randomized; // other places that call that function (e.g. the knotholder) need the exact point // draw 1st segment c->moveto(sp_star_get_xy (this, SP_STAR_POINT_KNOT1, 0, true)); if (this->flatsided == false) { if (not_rounded) { c->lineto(sp_star_get_xy (this, SP_STAR_POINT_KNOT2, 0, true)); } else { c->curveto(sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, 0, NEXT), sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT2, 0, PREV), sp_star_get_xy (this, SP_STAR_POINT_KNOT2, 0, true)); } } // draw all middle segments for (gint i = 1; i < sides; i++) { if (not_rounded) { c->lineto(sp_star_get_xy (this, SP_STAR_POINT_KNOT1, i, true)); } else { if (this->flatsided == false) { c->curveto(sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT2, i - 1, NEXT), sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, i, PREV), sp_star_get_xy (this, SP_STAR_POINT_KNOT1, i, true)); } else { c->curveto(sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, i - 1, NEXT), sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, i, PREV), sp_star_get_xy (this, SP_STAR_POINT_KNOT1, i, true)); } } if (this->flatsided == false) { if (not_rounded) { c->lineto(sp_star_get_xy (this, SP_STAR_POINT_KNOT2, i, true)); } else { c->curveto(sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, i, NEXT), sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT2, i, PREV), sp_star_get_xy (this, SP_STAR_POINT_KNOT2, i, true)); } } } // draw last segment if (!not_rounded) { if (this->flatsided == false) { c->curveto(sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT2, sides - 1, NEXT), sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, 0, PREV), sp_star_get_xy (this, SP_STAR_POINT_KNOT1, 0, true)); } else { c->curveto(sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, sides - 1, NEXT), sp_star_get_curvepoint (this, SP_STAR_POINT_KNOT1, 0, PREV), sp_star_get_xy (this, SP_STAR_POINT_KNOT1, 0, true)); } } c->closepath(); /* Reset the shape'scurve to the "original_curve" * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/ this->setCurveInsync( c, TRUE); this->setCurveBeforeLPE( c ); if (hasPathEffect() && pathEffectsEnabled()) { SPCurve *c_lpe = c->copy(); bool success = this->performPathEffect(c_lpe); if (success) { this->setCurveInsync( c_lpe, TRUE); } c_lpe->unref(); } c->unref(); }
// Create path for rendering shape on screen void SPGenericEllipse::set_shape() { // std::cout << "SPGenericEllipse::set_shape: Entrance" << std::endl; if (hasBrokenPathEffect()) { g_warning("The ellipse shape has unknown LPE on it! Convert to path to make it editable preserving the appearance; editing it as ellipse will remove the bad LPE"); if (this->getRepr()->attribute("d")) { // unconditionally read the curve from d, if any, to preserve appearance Geom::PathVector pv = sp_svg_read_pathv(this->getRepr()->attribute("d")); SPCurve *cold = new SPCurve(pv); this->setCurveInsync(cold, TRUE); cold->unref(); } return; } if (Geom::are_near(this->rx.computed, 0) || Geom::are_near(this->ry.computed, 0)) { return; } this->normalize(); SPCurve *curve = NULL; // For simplicity, we use a circle with center (0, 0) and radius 1 for our calculations. Geom::Circle circle(0, 0, 1); if (!this->_isSlice()) { start = 0.0; end = 2.0*M_PI; } double incr = end - start; // arc angle if (incr < 0.0) incr += 2.0*M_PI; int numsegs = 1 + int(incr*2.0/M_PI); // number of arc segments if (numsegs > 4) numsegs = 4; incr = incr/numsegs; // limit arc angle to less than 90 degrees Geom::Path path(Geom::Point::polar(start)); Geom::EllipticalArc* arc; for (int seg = 0; seg < numsegs; seg++) { arc = circle.arc(Geom::Point::polar(start + seg*incr), Geom::Point::polar(start + (seg + 0.5)*incr), Geom::Point::polar(start + (seg + 1.0)*incr)); path.append(*arc); delete arc; } Geom::PathBuilder pb; pb.append(path); if (this->_isSlice() && this->_closed) { pb.lineTo(Geom::Point(0, 0)); } if (this->_closed) { pb.closePath(); } else { pb.flush(); } curve = new SPCurve(pb.peek()); // gchar *str = sp_svg_write_path(curve->get_pathvector()); // std::cout << " path: " << str << std::endl; // g_free(str); // Stretching / moving the calculated shape to fit the actual dimensions. Geom::Affine aff = Geom::Scale(rx.computed, ry.computed) * Geom::Translate(cx.computed, cy.computed); curve->transform(aff); /* Reset the shape's curve to the "original_curve" * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/ this->setCurveInsync(curve, TRUE); this->setCurveBeforeLPE(curve); if (hasPathEffect() && pathEffectsEnabled()) { SPCurve *c_lpe = curve->copy(); bool success = this->performPathEffect(c_lpe); if (success) { this->setCurveInsync(c_lpe, TRUE); } c_lpe->unref(); } curve->unref(); // std::cout << "SPGenericEllipse::set_shape: Exit" << std::endl; }
Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { // std::cout << "\nSPGenericEllipse::write: Entrance (" // << (repr == NULL ? " NULL" : g_quark_to_string(repr->code())) // << ")" << std::endl; GenericEllipseType new_type = SP_GENERIC_ELLIPSE_UNDEFINED; if (this->_isSlice() || hasPathEffect() ) { new_type = SP_GENERIC_ELLIPSE_ARC; } else if ( rx.computed == ry.computed ) { new_type = SP_GENERIC_ELLIPSE_CIRCLE; } else { new_type = SP_GENERIC_ELLIPSE_ELLIPSE; } // std::cout << " new_type: " << new_type << std::endl; if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { switch ( new_type ) { case SP_GENERIC_ELLIPSE_ARC: repr = xml_doc->createElement("svg:path"); break; case SP_GENERIC_ELLIPSE_CIRCLE: repr = xml_doc->createElement("svg:circle"); break; case SP_GENERIC_ELLIPSE_ELLIPSE: repr = xml_doc->createElement("svg:ellipse"); break; case SP_GENERIC_ELLIPSE_UNDEFINED: default: std::cerr << "SPGenericEllipse::write(): unknown type." << std::endl; } } if( type != new_type ) { switch( new_type ) { case SP_GENERIC_ELLIPSE_ARC: repr->setCodeUnsafe(g_quark_from_string("svg:path")); break; case SP_GENERIC_ELLIPSE_CIRCLE: repr->setCodeUnsafe(g_quark_from_string("svg:circle")); break; case SP_GENERIC_ELLIPSE_ELLIPSE: repr->setCodeUnsafe(g_quark_from_string("svg:ellipse")); break; default: std::cerr << "SPGenericEllipse::write(): unknown type." << std::endl; } type = new_type; // FIXME: The XML dialog won't update the element name. We need // a notifyElementNameChanged callback added to the XML observers // to trigger a refresh. } // std::cout << " type: " << g_quark_to_string( repr->code() ) << std::endl; // std::cout << " cx: " << cx.computed // << " cy: " << cy.computed // << " rx: " << rx.computed // << " ry: " << ry.computed << std::endl; switch ( type ) { case SP_GENERIC_ELLIPSE_UNDEFINED: case SP_GENERIC_ELLIPSE_ARC: repr->setAttribute("cx", NULL ); repr->setAttribute("cy", NULL ); repr->setAttribute("rx", NULL ); repr->setAttribute("ry", NULL ); repr->setAttribute("r", NULL ); if (flags & SP_OBJECT_WRITE_EXT) { repr->setAttribute("sodipodi:type", "arc"); sp_repr_set_svg_double(repr, "sodipodi:cx", this->cx.computed); sp_repr_set_svg_double(repr, "sodipodi:cy", this->cy.computed); sp_repr_set_svg_double(repr, "sodipodi:rx", this->rx.computed); sp_repr_set_svg_double(repr, "sodipodi:ry", this->ry.computed); // write start and end only if they are non-trivial; otherwise remove if (this->_isSlice()) { sp_repr_set_svg_double(repr, "sodipodi:start", this->start); sp_repr_set_svg_double(repr, "sodipodi:end", this->end); repr->setAttribute("sodipodi:open", (!this->_closed) ? "true" : NULL); } else { repr->setAttribute("sodipodi:end", NULL); repr->setAttribute("sodipodi:start", NULL); repr->setAttribute("sodipodi:open", NULL); } } // write d= this->set_elliptical_path_attribute(repr); break; case SP_GENERIC_ELLIPSE_CIRCLE: sp_repr_set_svg_double(repr, "cx", this->cx.computed); sp_repr_set_svg_double(repr, "cy", this->cy.computed); sp_repr_set_svg_double(repr, "r", this->rx.computed); repr->setAttribute("rx", NULL ); repr->setAttribute("ry", NULL ); repr->setAttribute("sodipodi:cx", NULL ); repr->setAttribute("sodipodi:cy", NULL ); repr->setAttribute("sodipodi:rx", NULL ); repr->setAttribute("sodipodi:ry", NULL ); repr->setAttribute("sodipodi:end", NULL ); repr->setAttribute("sodipodi:start", NULL ); repr->setAttribute("sodipodi:open", NULL ); repr->setAttribute("sodipodi:type", NULL ); repr->setAttribute("d", NULL ); break; case SP_GENERIC_ELLIPSE_ELLIPSE: sp_repr_set_svg_double(repr, "cx", this->cx.computed); sp_repr_set_svg_double(repr, "cy", this->cy.computed); sp_repr_set_svg_double(repr, "rx", this->rx.computed); sp_repr_set_svg_double(repr, "ry", this->ry.computed); repr->setAttribute("r", NULL ); repr->setAttribute("sodipodi:cx", NULL ); repr->setAttribute("sodipodi:cy", NULL ); repr->setAttribute("sodipodi:rx", NULL ); repr->setAttribute("sodipodi:ry", NULL ); repr->setAttribute("sodipodi:end", NULL ); repr->setAttribute("sodipodi:start", NULL ); repr->setAttribute("sodipodi:open", NULL ); repr->setAttribute("sodipodi:type", NULL ); repr->setAttribute("d", NULL ); break; default: std::cerr << "SPGenericEllipse::write: unknown type." << std::endl; } this->set_shape(); // evaluate SPCurve SPShape::write(xml_doc, repr, flags); // std::cout << "SPGenericEllipse::write: Exit: " << g_quark_to_string(repr->code()) << "\n" << std::endl; return repr; }
void Box3DSide::set_shape() { if (!this->document->getRoot()) { // avoid a warning caused by sp_document_height() (which is called from sp_item_i2d_affine() below) // when reading a file containing 3D boxes return; } SPObject *parent = this->parent; if (!SP_IS_BOX3D(parent)) { g_warning("Parent of 3D box side is not a 3D box.\n"); return; } SPBox3D *box = SP_BOX3D(parent); Persp3D *persp = box3d_side_perspective(this); if (!persp) { return; } // TODO: Draw the correct quadrangle here // To do this, determine the perspective of the box, the orientation of the side (e.g., XY-FRONT) // compute the coordinates of the corners in P^3, project them onto the canvas, and draw the // resulting path. unsigned int corners[4]; box3d_side_compute_corner_ids(this, corners); SPCurve *c = new SPCurve(); if (!box3d_get_corner_screen(box, corners[0]).isFinite() || !box3d_get_corner_screen(box, corners[1]).isFinite() || !box3d_get_corner_screen(box, corners[2]).isFinite() || !box3d_get_corner_screen(box, corners[3]).isFinite() ) { g_warning ("Trying to draw a 3D box side with invalid coordinates.\n"); return; } c->moveto(box3d_get_corner_screen(box, corners[0])); c->lineto(box3d_get_corner_screen(box, corners[1])); c->lineto(box3d_get_corner_screen(box, corners[2])); c->lineto(box3d_get_corner_screen(box, corners[3])); c->closepath(); /* Reset the this'scurve to the "original_curve" * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/ this->setCurveInsync( c, TRUE); if (hasPathEffect() && pathEffectsEnabled()) { SPCurve *c_lpe = c->copy(); bool success = this->performPathEffect(c_lpe); if (success) { this->setCurveInsync(c_lpe, TRUE); } c_lpe->unref(); } c->unref(); }