#include "sbasis-geometric.h" #include "sbasis.h" #include "sbasis-math.h" //#include "solver.h" #include "sbasis-geometric.h" /** Geometric operators on D2 (1D->2D). * Copyright 2007 JF Barraud * Copyright 2007 N Hurst * * The functions defined in this header related to 2d geometric operations such as arc length, * unit_vector, curvature, and centroid. Most are built on top of unit_vector, which takes an * arbitrary D2 and returns a D2 with unit length with the same direction. * * Todo/think about: * arclength D2 -> sbasis (giving arclength function) * does uniform_speed return natural parameterisation? * integrate sb2d code from normal-bundle * angle(md<2>) -> sbasis (gives angle from vector - discontinuous?) * osculating circle center? * **/ //namespace Geom{ using namespace Geom; using namespace std; //Some utils first. //TODO: remove this!! static vector vect_intersect(vector const &a, vector const &b, double tol=0.){ vector inter; unsigned i=0,j=0; while ( ib[j]){ j+=1; } } return inter; } static SBasis divide_by_sk(SBasis const &a, int k) { assert( k<(int)a.size()); if(k < 0) return shift(a,-k); SBasis c; c.insert(c.begin(), a.begin()+k, a.end()); return c; } static SBasis divide_by_t0k(SBasis const &a, int k) { if(k < 0) { SBasis c = Linear(0,1); for (int i=2; i<=-k; i++){ c*=c; } c*=a; return(c); }else{ SBasis c = Linear(1,0); for (int i=2; i<=k; i++){ c*=c; } c*=a; return(divide_by_sk(c,k)); } } static SBasis divide_by_t1k(SBasis const &a, int k) { if(k < 0) { SBasis c = Linear(1,0); for (int i=2; i<=-k; i++){ c*=c; } c*=a; return(c); }else{ SBasis c = Linear(0,1); for (int i=2; i<=k; i++){ c*=c; } c*=a; return(divide_by_sk(c,k)); } } static D2 RescaleForNonVanishingEnds(D2 const &MM, double ZERO=1.e-4){ D2 M = MM; //TODO: divide by all the s at once!!! while (fabs(M[0].at0()) > Geom::cutAtRoots(Piecewise > const &M, double ZERO){ vector rts; for (unsigned i=0; i seg_rts = roots((M.segs[i])[0]); seg_rts = vect_intersect(seg_rts, roots((M.segs[i])[1]), ZERO); Linear mapToDom = Linear(M.cuts[i],M.cuts[i+1]); for (unsigned r=0; r Geom::atan2(Piecewise > const &vect, double tol, unsigned order){ Piecewise result; Piecewise > v = cutAtRoots(vect); result.cuts.push_back(v.cuts.front()); for (unsigned i=0; i vi = RescaleForNonVanishingEnds(v.segs[i]); SBasis x=vi[0], y=vi[1]; Piecewise angle; angle = divide (x*derivative(y)-y*derivative(x), x*x+y*y, tol, order); //TODO: I don't understand this - sign. angle = integral(-angle); Point vi0 = vi.at0(); angle += -std::atan2(vi0[1],vi0[0]) - angle[0].at0(); //TODO: deal with 2*pi jumps form one seg to the other... //TODO: not exact at t=1 because of the integral. //TODO: force continuity? angle.setDomain(Interval(v.cuts[i],v.cuts[i+1])); result.concat(angle); } return result; } Piecewise Geom::atan2(D2 const &vect, double tol, unsigned order){ return atan2(Piecewise >(vect),tol,order); } //unitVector(x,y) is computed as (b,-a) where a and b are solutions of: // ax+by=0 (eqn1) and a^2+b^2=1 (eqn2) Piecewise > Geom::unitVector(D2 const &V_in, double tol, unsigned order){ D2 V = RescaleForNonVanishingEnds(V_in); if (V[0].empty() && V[1].empty()) return Piecewise >(D2(Linear(1),SBasis())); SBasis x = V[0], y = V[1], a, b; SBasis r_eqn1, r_eqn2; Point v0 = unit_vector(V.at0()); Point v1 = unit_vector(V.at1()); a.push_back(Linear(-v0[1],-v1[1])); b.push_back(Linear( v0[0], v1[0])); r_eqn1 = -(a*x+b*y); r_eqn2 = Linear(1.)-(a*a+b*b); for (unsigned k=1; k<=order; k++){ double r0 = (k unitV; unitV[0] = b; unitV[1] = -a; //is it good? double rel_tol = std::max(1.,std::max(V_in[0].tailError(0),V_in[1].tailError(0)))*tol; if (r_eqn1.tailError(order)>rel_tol || r_eqn2.tailError(order)>tol){ //if not: subdivide and concat results. Piecewise > unitV0, unitV1; unitV0 = unitVector(compose(V,Linear(0,.5)),tol,order); unitV1 = unitVector(compose(V,Linear(.5,1)),tol,order); unitV0.setDomain(Interval(0.,.5)); unitV1.setDomain(Interval(.5,1.)); unitV0.concat(unitV1); return(unitV0); }else{ //if yes: return it as pw. Piecewise > result; result=(Piecewise >)unitV; return result; } } Piecewise > Geom::unitVector(Piecewise > const &V, double tol, unsigned order){ Piecewise > result; Piecewise > VV = cutAtRoots(V); result.cuts.push_back(VV.cuts.front()); for (unsigned i=0; i > unit_seg; unit_seg = unitVector(VV.segs[i],tol, order); unit_seg.setDomain(Interval(VV.cuts[i],VV.cuts[i+1])); result.concat(unit_seg); } return result; } Piecewise Geom::arcLengthSb(Piecewise > const &M, double tol){ Piecewise > dM = derivative(M); Piecewise dMlength = sqrt(dot(dM,dM),tol,3); Piecewise length = integral(dMlength); length-=length.segs.front().at0(); return length; } Piecewise Geom::arcLengthSb(D2 const &M, double tol){ return arcLengthSb(Piecewise >(M), tol); } double Geom::length(D2 const &M, double tol){ Piecewise length = arcLengthSb(M, tol); return length.segs.back().at1(); } double Geom::length(Piecewise > const &M, double tol){ Piecewise length = arcLengthSb(M, tol); return length.segs.back().at1(); } // incomplete. Piecewise Geom::curvature(D2 const &M, double tol) { D2 dM=derivative(M); Piecewise result; Piecewise > unitv = unitVector(dM,tol); Piecewise dMlength = dot(Piecewise >(dM),unitv); Piecewise k = cross(derivative(unitv),unitv); k = divide(k,dMlength,tol,3); return(k); } Piecewise Geom::curvature(Piecewise > const &V, double tol){ Piecewise result; Piecewise > VV = cutAtRoots(V); result.cuts.push_back(VV.cuts.front()); for (unsigned i=0; i curv_seg; curv_seg = curvature(VV.segs[i],tol); curv_seg.setDomain(Interval(VV.cuts[i],VV.cuts[i+1])); result.concat(curv_seg); } return result; } //================================================================= Piecewise > Geom::arc_length_parametrization(D2 const &M, unsigned order, double tol){ Piecewise > u; u.push_cut(0); Piecewise s = arcLengthSb(Piecewise >(M),tol); for (unsigned i=0; i < s.size();i++){ double t0=s.cuts[i],t1=s.cuts[i+1]; D2 sub_M = compose(M,Linear(t0,t1)); D2 sub_u; for (unsigned dim=0;dim<2;dim++){ SBasis sub_s = s.segs[i]; sub_s-=sub_s.at0(); sub_s/=sub_s.at1(); sub_u[dim]=compose_inverse(sub_M[dim],sub_s, order, tol); } u.push(sub_u,s(t1)); } return u; } Piecewise > Geom::arc_length_parametrization(Piecewise > const &M, unsigned order, double tol){ Piecewise > result; for (unsigned i=0; i > uniform_seg=arc_length_parametrization(M[i],order,tol); result.concat(uniform_seg); } return(result); } /** centroid using sbasis integration. * This approach uses green's theorem to compute the area and centroid using integrals. For curved * shapes this is much faster than converting to polyline. * Returned values: 0 for normal execution; 2 if area is zero, meaning centroid is meaningless. * Copyright Nathan Hurst 2006 */ unsigned Geom::centroid(Piecewise > const &p, Point& centroid, double &area) { Point centroid_tmp(0,0); double atmp = 0; for(unsigned i = 0; i < p.size(); i++) { SBasis curl = dot(p[i], rot90(derivative(p[i]))); SBasis A = integral(curl); D2 C = integral(multiply(curl, p[i])); atmp += A.at1() - A.at0(); centroid_tmp += C.at1()- C.at0(); // first moment. } // join ends centroid_tmp *= 2; Point final = p[p.size()-1].at1(), initial = p[0].at0(); const double ai = cross(final, initial); atmp += ai; centroid_tmp += (final + initial)*ai; // first moment. area = atmp / 2; if (atmp != 0) { centroid = centroid_tmp / (3 * atmp); return 0; } return 2; } //}; // namespace /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :