// (c) Tuomas J. Lukka #include #include namespace Vob { namespace Geom { PREDBGVAR(dbg_fillets); /** One side of a circularly filleted connection. */ struct CircleFillet { float r; Vec ctr; Vec filletcenter; float filletrad; /** A unit vector pointing to the direction of the * connection side. */ Vec dirconn; /** A vector pointing to the direction of the tangent * side. */ Vec dirtang; CircleFillet() { } CircleFillet( Vec ctr, float r, float angle, float sign, float dist, float thick) { this->r = r; this->ctr = ctr; Vec dir = dirVec(angle); Vec p = ctr + dist * dir + .5 * thick * sign * dir.cw90(); this->filletcenter = circle__point_norm_circle(p, sign * dir.cw90(), ctr, r); this->filletrad = (filletcenter - p).length(); this->dirconn = dir; this->dirtang = (filletcenter - ctr).normalized(); } bool infillet(Vec dir) { return dirtang.cross(dirconn) * dirtang.cross(dir) >= 0 && dirconn.cross(dirtang) * dirconn.cross(dir) >= 0; } /** Get the radius of the filleted curve * at the given direction. */ float rad(Vec dir) { if(infillet(dir)) { // Same direction - try intersecting circle. bool succ; Vec in = project2circle(ctr + dir, ctr, filletcenter, filletrad, -1, &succ); if(succ) { return (in-ctr).length(); } else { return r; } } else { return r; } } }; struct FilletSpan { CircleFillet a, b; float aa, ab; Vec ctr; float r; /** The three states of the two fillet edges * involved here. Separate = no interaction, * Blend = overlap somewhat, have to blend, * cleave = overlap much, have to use * visual effect of separation. */ enum { SEPARATE, BLEND, CLEAVE }; int type; // For cleaved float f; float fanglea, fangleb, fangle; float aang, bang; /** * @param ab Angle of fillet b. Always > aa */ FilletSpan( Vec ctr, float r, float aa, float da, float ta, float ab, float db, float tb ) { this->aa = aa; this->ab = ab; this->ctr = ctr; this->r = r; this->f = -1; a = CircleFillet(ctr, r, aa, 1, da, ta); b = CircleFillet(ctr, r, ab, -1, db, tb); DBG(dbg_fillets) << "F CF A: "<? fangleb; this-> f = - ( mangle - fangle ) / fangle; this->aang = lerp(aa, aa + fangle, .5 + .5*f); this->bang = lerp(ab, ab - fangle, .5 + .5*f); DBG(dbg_fillets) << "F: Cleave: "<? rb; } template float rad_blended(Vec dir, const Blender &b) { float ra = this->a.rad(dir); float rb = this->b.rad(dir); return b(ra-r, rb-r) + r; } // If calls to point should be split bool split() { return type == CLEAVE; } template Vec point(float fract, const Blender &b) { if(type == CLEAVE) { if(fract < .5) { // Angle of real current sample float ang = lerp(aa, aang, 2*fract); Vec d = dirVec(ang); float r0 = this->a.rad(d); // Angles to use for the blended curve when aa-ab==fangle: Vec da = dirVec(aa + fangle/2 * (2*fract)); Vec db = dirVec(ab - fangle + fangle/2 * (2*fract)); float ra = this->a.rad(da); float rb = this->b.rad(db); float curvfra = b(ra-r, rb-r) + r; Vec p = ctr + lerp( da * curvfra, d * r0, f); //DBG(dbg_fillets) << "f<.5: "<b.rad(d); // Angles to use for the blended curve when aa-ab==fangle: Vec db = dirVec(ab - fangle/2 * (2*fract)); Vec da = dirVec(aa + fangle - fangle/2 * (2*fract)); float rb = this->b.rad(db); float ra = this->a.rad(da); float curvfra = b(rb-r, ra-r) + r; Vec p = ctr + lerp( db * curvfra, d * r0, f); return p; } } else { // Use angle Vec d = dirVec(lerp(aa, ab, fract)); if(type == SEPARATE) { return ctr + d * rad_separate(d); } else { return ctr + d * rad_blended(d, b); } } } }; struct BlendSimply { float operator()(float a, float b) const { return a + b; } }; } }