/** * Computes a point on the offset; used to set a "seed" position for * the control knot. * * \return the topmost point on the offset. */ void sp_offset_top_point (SPOffset const * offset, Geom::Point *px) { (*px) = Geom::Point(0, 0); if (offset == NULL) { return; } if (offset->knotSet) { (*px) = offset->knot; return; } SPCurve *curve = SP_SHAPE (offset)->getCurve(); if (curve == NULL) { // CPPIFY //offset->set_shape(); const_cast<SPOffset*>(offset)->set_shape(); curve = SP_SHAPE (offset)->getCurve(); if (curve == NULL) return; } if (curve->is_empty()) { curve->unref(); return; } Path *finalPath = new Path; finalPath->LoadPathVector(curve->get_pathvector()); Shape *theShape = new Shape; finalPath->Convert (1.0); finalPath->Fill (theShape, 0); if (theShape->hasPoints()) { theShape->SortPoints (); *px = theShape->getPoint(0).x; } delete theShape; delete finalPath; curve->unref(); }
void SPOffset::set_shape() { if ( this->originalPath == NULL ) { // oops : no path?! (the offset object should do harakiri) return; } #ifdef OFFSET_VERBOSE g_print ("rad=%g\n", offset->rad); #endif // au boulot if ( fabs(this->rad) < 0.01 ) { // grosso modo: 0 // just put the source this as the offseted one, no one will notice // it's also useless to compute the offset with a 0 radius //XML Tree being used directly here while it shouldn't be. const char *res_d = this->getRepr()->attribute("inkscape:original"); if ( res_d ) { Geom::PathVector pv = sp_svg_read_pathv(res_d); SPCurve *c = new SPCurve(pv); g_assert(c != NULL); this->setCurveInsync (c, TRUE); this->setCurveBeforeLPE(c); c->unref(); } return; } // extra paraniac careful check. the preceding if () should take care of this case if (fabs (this->rad) < 0.01) { this->rad = (this->rad < 0) ? -0.01 : 0.01; } Path *orig = new Path; orig->Copy ((Path *)this->originalPath); if ( use_slow_but_correct_offset_method == false ) { // version par outline Shape *theShape = new Shape; Shape *theRes = new Shape; Path *originaux[1]; Path *res = new Path; res->SetBackData (false); // and now: offset float o_width; if (this->rad >= 0) { o_width = this->rad; orig->OutsideOutline (res, o_width, join_round, butt_straight, 20.0); } else { o_width = -this->rad; orig->OutsideOutline (res, -o_width, join_round, butt_straight, 20.0); } if (o_width >= 1.0) { // res->ConvertForOffset (1.0, orig, offset->rad); res->ConvertWithBackData (1.0); } else { // res->ConvertForOffset (o_width, orig, offset->rad); res->ConvertWithBackData (o_width); } res->Fill (theShape, 0); theRes->ConvertToShape (theShape, fill_positive); originaux[0] = res; theRes->ConvertToForme (orig, 1, originaux); Geom::OptRect bbox = this->desktopVisualBounds(); if ( bbox ) { gdouble size = L2(bbox->dimensions()); gdouble const exp = this->transform.descrim(); if (exp != 0) { size /= exp; } orig->Coalesce (size * 0.001); //g_print ("coa %g exp %g item %p\n", size * 0.001, exp, item); } // if (o_width >= 1.0) // { // orig->Coalesce (0.1); // small treshhold, since we only want to get rid of small segments // the curve should already be computed by the Outline() function // orig->ConvertEvenLines (1.0); // orig->Simplify (0.5); // } // else // { // orig->Coalesce (0.1*o_width); // orig->ConvertEvenLines (o_width); // orig->Simplify (0.5 * o_width); // } delete theShape; delete theRes; delete res; } else { // version par makeoffset Shape *theShape = new Shape; Shape *theRes = new Shape; // and now: offset float o_width; if (this->rad >= 0) { o_width = this->rad; } else { o_width = -this->rad; } // one has to have a measure of the details if (o_width >= 1.0) { orig->ConvertWithBackData (0.5); } else { orig->ConvertWithBackData (0.5*o_width); } orig->Fill (theShape, 0); theRes->ConvertToShape (theShape, fill_positive); Path *originaux[1]; originaux[0]=orig; Path *res = new Path; theRes->ConvertToForme (res, 1, originaux); int nbPart=0; Path** parts=res->SubPaths(nbPart,true); char *holes=(char*)malloc(nbPart*sizeof(char)); // we offset contours separately, because we can. // this way, we avoid doing a unique big ConvertToShape when dealing with big shapes with lots of holes { Shape* onePart=new Shape; Shape* oneCleanPart=new Shape; theShape->Reset(); for (int i=0; i<nbPart; i++) { double partSurf=parts[i]->Surface(); parts[i]->Convert(1.0); { // raffiner si besoin double bL,bT,bR,bB; parts[i]->PolylineBoundingBox(bL,bT,bR,bB); double mesure=((bR-bL)+(bB-bT))*0.5; if ( mesure < 10.0 ) { parts[i]->Convert(0.02*mesure); } } if ( partSurf < 0 ) { // inverse par rapport a la realite // plein holes[i]=0; parts[i]->Fill(oneCleanPart,0); onePart->ConvertToShape(oneCleanPart,fill_positive); // there aren't intersections in that one, but maybe duplicate points and null edges oneCleanPart->MakeOffset(onePart,this->rad,join_round,20.0); onePart->ConvertToShape(oneCleanPart,fill_positive); onePart->CalcBBox(); double typicalSize=0.5*((onePart->rightX-onePart->leftX)+(onePart->bottomY-onePart->topY)); if ( typicalSize < 0.05 ) { typicalSize=0.05; } typicalSize*=0.01; if ( typicalSize > 1.0 ) { typicalSize=1.0; } onePart->ConvertToForme (parts[i]); parts[i]->ConvertEvenLines (typicalSize); parts[i]->Simplify (typicalSize); double nPartSurf=parts[i]->Surface(); if ( nPartSurf >= 0 ) { // inversion de la surface -> disparait delete parts[i]; parts[i]=NULL; } else { } /* int firstP=theShape->nbPt; for (int j=0;j<onePart->nbPt;j++) theShape->AddPoint(onePart->pts[j].x); for (int j=0;j<onePart->nbAr;j++) theShape->AddEdge(firstP+onePart->aretes[j].st,firstP+onePart->aretes[j].en);*/ } else { // trou holes[i]=1; parts[i]->Fill(oneCleanPart,0,false,true,true); onePart->ConvertToShape(oneCleanPart,fill_positive); oneCleanPart->MakeOffset(onePart,-this->rad,join_round,20.0); onePart->ConvertToShape(oneCleanPart,fill_positive); // for (int j=0;j<onePart->nbAr;j++) onePart->Inverse(j); // pas oublier de reinverser onePart->CalcBBox(); double typicalSize=0.5*((onePart->rightX-onePart->leftX)+(onePart->bottomY-onePart->topY)); if ( typicalSize < 0.05 ) { typicalSize=0.05; } typicalSize*=0.01; if ( typicalSize > 1.0 ) { typicalSize=1.0; } onePart->ConvertToForme (parts[i]); parts[i]->ConvertEvenLines (typicalSize); parts[i]->Simplify (typicalSize); double nPartSurf=parts[i]->Surface(); if ( nPartSurf >= 0 ) { // inversion de la surface -> disparait delete parts[i]; parts[i]=NULL; } else { } /* int firstP=theShape->nbPt; for (int j=0;j<onePart->nbPt;j++) theShape->AddPoint(onePart->pts[j].x); for (int j=0;j<onePart->nbAr;j++) theShape->AddEdge(firstP+onePart->aretes[j].en,firstP+onePart->aretes[j].st);*/ } // delete parts[i]; } // theShape->MakeOffset(theRes,offset->rad,join_round,20.0); delete onePart; delete oneCleanPart; } if ( nbPart > 1 ) { theShape->Reset(); for (int i=0; i<nbPart; i++) { if ( parts[i] ) { parts[i]->ConvertWithBackData(1.0); if ( holes[i] ) { parts[i]->Fill(theShape,i,true,true,true); } else { parts[i]->Fill(theShape,i,true,true,false); } } } theRes->ConvertToShape (theShape, fill_positive); theRes->ConvertToForme (orig,nbPart,parts); for (int i=0; i<nbPart; i++) { if ( parts[i] ) { delete parts[i]; } } } else if ( nbPart == 1 ) { orig->Copy(parts[0]); for (int i=0; i<nbPart; i++) { if ( parts[i] ) { delete parts[i]; } } } else { orig->Reset(); } // theRes->ConvertToShape (theShape, fill_positive); // theRes->ConvertToForme (orig); /* if (o_width >= 1.0) { orig->ConvertEvenLines (1.0); orig->Simplify (1.0); } else { orig->ConvertEvenLines (1.0*o_width); orig->Simplify (1.0 * o_width); }*/ if ( parts ) { free(parts); } if ( holes ) { free(holes); } delete res; delete theShape; delete theRes; } { char *res_d = NULL; if (orig->descr_cmd.size() <= 1) { // Aie.... nothing left. res_d = strdup ("M 0 0 L 0 0 z"); //printf("%s\n",res_d); } else { res_d = orig->svg_dump_path (); } delete orig; Geom::PathVector pv = sp_svg_read_pathv(res_d); SPCurve *c = new SPCurve(pv); g_assert(c != NULL); this->setCurveInsync (c, TRUE); this->setCurveBeforeLPE(c); c->unref(); free (res_d); } }
static void refresh_offset_source(SPOffset* offset) { if ( offset == NULL ) { return; } offset->sourceDirty=false; // le mauvais cas: pas d'attribut d => il faut verifier que c'est une SPShape puis prendre le contour // The bad case: no d attribute. Must check that it's an SPShape and then take the outline. SPObject *refobj=offset->sourceObject; if ( refobj == NULL ) { return; } SPItem *item = SP_ITEM (refobj); SPCurve *curve = NULL; if (SP_IS_SHAPE (item)) { curve = SP_SHAPE (item)->getCurve (); } else if (SP_IS_TEXT (item)) { curve = SP_TEXT (item)->getNormalizedBpath (); } else { return; } if (curve == NULL) { return; } Path *orig = new Path; orig->LoadPathVector(curve->get_pathvector()); curve->unref(); if (!item->transform.isIdentity()) { gchar const *t_attr = item->getRepr()->attribute("transform"); if (t_attr) { Geom::Affine t; if (sp_svg_transform_read(t_attr, &t)) { orig->Transform(t); } } } // Finish up. { SPCSSAttr *css; const gchar *val; Shape *theShape = new Shape; Shape *theRes = new Shape; orig->ConvertWithBackData (1.0); orig->Fill (theShape, 0); css = sp_repr_css_attr (offset->sourceRepr , "style"); val = sp_repr_css_property (css, "fill-rule", NULL); if (val && strcmp (val, "nonzero") == 0) { theRes->ConvertToShape (theShape, fill_nonZero); } else if (val && strcmp (val, "evenodd") == 0) { theRes->ConvertToShape (theShape, fill_oddEven); } else { theRes->ConvertToShape (theShape, fill_nonZero); } Path *originaux[1]; originaux[0] = orig; Path *res = new Path; theRes->ConvertToForme (res, 1, originaux); delete theShape; delete theRes; char *res_d = res->svg_dump_path (); delete res; delete orig; // TODO fix: //XML Tree being used diectly here while it shouldn't be. offset->getRepr()->setAttribute("inkscape:original", res_d); free (res_d); } }