/* $Id$
*
* Copyright (c) 2010-2011 Anders Wallin (anders.e.e.wallin "at" gmail.com).
*
* This file is part of OpenCAMlib
* (see https://github.com/aewallin/opencamlib).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "conecutter.hpp"
#include "compositecutter.hpp" // for offsetCutter()
#include "numeric.hpp"
namespace ocl
{
ConeCutter::ConeCutter() {
assert(0);
}
ConeCutter::ConeCutter(double d, double a, double l) {
diameter = d;
radius = d/2.0;
angle = a;
assert( angle > 0.0 );
length = radius/tan(angle) + l;
center_height = radius/tan(angle);
assert( center_height > 0.0 );
xy_normal_length = radius;
normal_length = 0.0;
}
double ConeCutter::height(double r) const {
assert( tan(angle) > 0.0 ); // guard against division by zero
return r/tan(angle);
}
double ConeCutter::width(double h) const {
// grows from zero up to radius
// above that (cutter shaft) return radius
return (h abs(mu) there is contact with the circular edge at +/- xu
double ccu;
if ( hyperbola_case ) {
ccu = sign(m) * sqrt( square(radius)*square(m)*square(d) / (square(length) -square(radius)*square(m) ) );
} else {
ccu = sign(m)*xu;
}
Point cc_tmp( ccu, d, 0.0); // cc-point in the XY plane
cc_tmp.z_projectOntoEdge(u1,u2);
double cl_z;
if ( hyperbola_case ) { // 1) zc = zp - Lc + (R - sqrt(xp^2 + d^2)) / tan(beta2)
cl_z = cc_tmp.z - center_height + (radius-sqrt(square(ccu) + square(d)))/ tan(angle);
} else { // 2) zc = zp - Lc
cl_z = cc_tmp.z - center_height; // case where we hit the edge of the cone
}
return CC_CLZ_Pair( ccu , cl_z);
}
bool ConeCutter::facetPush(const Fiber& fib, Interval& i, const Triangle& t) const {
// push two objects: tip, and base-circle
bool result = false;
if ( generalFacetPush( 0, 0, 0, fib, i, t) ) // TIP
result = true;
// normal_length, center_height, xy_normal_length, fiber, interval, triangle
if ( generalFacetPush( 0, this->center_height, this->xy_normal_length , fib, i ,t) ) // BASE
result = true;
return result;
}
// cone is pushed along Fiber f into contact with edge p1-p2
bool ConeCutter::generalEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const {
bool result = false;
if ( isZero_tol(p2.z-p1.z) ) // guard against horizontal edge
return result;
assert( (p2.z-p1.z) != 0.0 );
// idea: as the ITO-cone slides along the edge it will pierce a z-plane at the height of the fiber
// the shaped of the pierced area is either a circle if the edge is steep
// or a 'half-circle' + cone shape if the edge is shallow (ice-cream cone...)
// we can now intersect this 2D shape with the fiber and get the CL-points.
// this is where the ITO cone pierces the z-plane of the fiber
// edge-line: p1+t*(p2-p1) = zheight
// => t = (zheight - p1)/ (p2-p1)
double t_tip = (f.p1.z - p1.z) / (p2.z-p1.z);
Point p_tip = p1 + t_tip*(p2-p1);
assert( isZero_tol( abs(p_tip.z-f.p1.z) ) ); // p_tip should be in plane of fiber
// this is where the ITO cone base exits the plane
double t_base = (f.p1.z+center_height - p1.z) / (p2.z-p1.z);
Point p_base = p1 + t_base*(p2-p1);
p_base.z = f.p1.z; // project to plane of fiber
double L = (p_base-p_tip).xyNorm();
//if ( L <= radius ){ // this is where the ITO-slice is a circle
// find intersection points, if any, between the fiber and the circle
// fiber is f.p1 - f.p2
// circle is centered at p_base
double d = p_base.xyDistanceToLine(f.p1, f.p2);
if ( d <= radius ) {
// we know there is an intersection point.
// http://mathworld.wolfram.com/Circle-LineIntersection.html
// subtract circle center, math is for circle centered at (0,0)
double dx = f.p2.x - f.p1.x;
double dy = f.p2.y - f.p1.y;
double dr = sqrt( square(dx) + square(dy) );
double det = (f.p1.x-p_base.x) * (f.p2.y-p_base.y) - (f.p2.x-p_base.x) * (f.p1.y-p_base.y);
// intersection given by:
// x = det*dy +/- sign(dy) * dx * sqrt( r^2 dr^2 - det^2 ) / dr^2
// y = -det*dx +/- abs(dy) * sqrt( r^2 dr^2 - det^2 ) / dr^2
double discr = square(radius) * square(dr) - square(det);
if (discr >= 0.0 ) {
if ( discr == 0.0 ) { // tangent case
double x_tang = ( det*dy )/ square(dr);
double y_tang = -( det*dx )/ square(dr);
Point p_tang(x_tang+p_base.x, y_tang+p_base.y); // translate back from (0,0) system!
double t_tang = f.tval( p_tang );
if ( circle_CC( t_tang, p1, p2, f, i) ) {
result = true;
}
} else {
// two intersection points with the base-circle
double x_pos = ( det*dy + sign(dy) * dx * sqrt( discr ) ) / square(dr);
double y_pos = ( -det*dx + fabs(dy) * sqrt( discr ) ) / square(dr);
Point cl_pos(x_pos+p_base.x, y_pos+p_base.y);
double t_pos = f.tval( cl_pos );
// the same with "-" sign:
double x_neg = ( det*dy - sign(dy) * dx * sqrt( discr ) ) / square(dr);
double y_neg = ( -det*dx - fabs(dy) * sqrt( discr ) ) / square(dr);
Point cl_neg(x_neg+p_base.x, y_neg+p_base.y);
double t_neg = f.tval( cl_neg );
if ( circle_CC( t_pos, p1, p2, f, i) )
result = true;
if ( circle_CC( t_neg, p1, p2, f, i) )
result = true;
}
}
}
//} // circle-case
if ( L > radius ) {
// ITO-slice is cone + "half-circle"
// lines from p_tip to tangent points of the base-circle
// this page has an analytic solution:
// http://mathworld.wolfram.com/CircleTangentLine.html
// this page has a geometric construction:
// http://www.mathopenref.com/consttangents.html
// circle p_base, radius
// top p_tip
// tangent point at intersection of base-circle with this circle:
Point p_mid = 0.5*( p_base + p_tip );
p_mid.z = f.p1.z;
double r_tang = L/2;
// circle-circle intersection to find tangent-points
// three cases: no intersection point
// one intersection point
// two intersection points
//d is the distance between the circle centers
Point pd = p_mid - p_base;
pd.z = 0;
double dist = pd.xyNorm(); //distance between the circles
//Check for special cases which do not lead to solutions we want
bool case1 = ( isZero_tol( dist ) && isZero_tol( fabs(radius - r_tang) ) );
bool case2 = (dist > (radius + r_tang) ); //no solution. circles do not intersect
bool case3 = ( dist < fabs(radius - r_tang)); //no solution. one circle is contained in the other
bool case4 = ( isZero_tol( dist - (radius+r_tang) ) ); // tangent case
if (case1 || case2 || case3 || case4 ) {
} else {
// here we know we have two solutions.
//Determine the distance from point 0 to point 2
// law of cosines (http://en.wikipedia.org/wiki/Law_of_cosines)
// rt^2 = d^2 + r^2 -2*d*r*cos(gamma)
// so cos(gamma) = (rt^2-d^2-r^2)/ -(2*d*r)
// and the sought distance is
// a = r*cos(gamma) = (-rt^2+d^2+r^2)/ (2*d)
double a = ( -square(r_tang) + square(radius) + square(dist) ) / (2.0 * dist);
assert( a >= 0.0 );
Point v2 = p_base + (a/dist)*pd; // v2 is the point where the line through the circle intersection points crosses the line between the circle centers.
//Determine the distance from v2 to either of the intersection points
double h = sqrt( square(radius) - square(a) );
//Now determine the offsets of the intersection points from point 2
Point ofs( -pd.y * (h / dist), pd.x * (h / dist) );
// now we know the tangent-points
Point tang1 = v2 + ofs;
Point tang2 = v2 - ofs;
if ( cone_CC( tang1, p_tip, p_base, p1, p2, f, i ) )
result = true;
if ( cone_CC( tang2, p_tip, p_base, p1, p2, f, i ) )
result = true;
}
} // end circle+cone case
return result;
}
// test for intersection with the fiber and a tip-tang line
// if there is an intersection in tip-tang, calculate the cc-point and update the interval
bool ConeCutter::cone_CC(const Point& tang,
const Point& tip,
const Point& base,
const Point& p1,
const Point& p2,
const Fiber& f,
Interval& i) const {
double u,t;
if ( xy_line_line_intersection( f.p1, f.p2, u, tang, tip, t) ) {
if ( (t>=0.0) && (t<=1.0) ) {
CCPoint cc_tmp = base + t*(tip-base);
cc_tmp.z_projectOntoEdge(p1,p2);
cc_tmp.type = EDGE_CONE;
return i.update_ifCCinEdgeAndTrue( u, cc_tmp, p1, p2, (true) ) ;
}
}
return false;
}
// t is a position along the fiber
// p1-p2 is the edge
// Interval& i is updated
bool ConeCutter::circle_CC( double t, const Point& p1, const Point& p2, const Fiber& f, Interval& i) const {
// cone base circle is center_height above fiber
double t_cc = (f.p1.z+center_height - p1.z) / (p2.z-p1.z); // t-parameter of the cc-point, center_height above fiber
CCPoint cc_tmp = p1 + t_cc*(p2-p1); // cc-point on the edge
cc_tmp.type = EDGE_CONE_BASE;
return i.update_ifCCinEdgeAndTrue( t, cc_tmp, p1, p2, (true) );
}
std::string ConeCutter::str() const {
std::ostringstream o;
o << *this;
return o.str();
}
std::ostream& operator<<(std::ostream &stream, ConeCutter c) {
stream << "ConeCutter (d=" << c.diameter << ", angle=" << c.angle << ", L=" << c.length << ")";
return stream;
}
} // end namespace
// end file conecutter.cpp