/* $Id$
*
* Copyright (c) 2010 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 "ballcutter.hpp"
namespace ocl
{
BallCutter::BallCutter() {
std::cout << " usage: BallCutter( double diameter, double length )\n";
assert(0);
}
BallCutter::BallCutter(double d, double l) {
diameter = d; assert( diameter>0.0 );
radius = d/2.0;
length = l; assert( length>0.0 );
normal_length = radius;
xy_normal_length = 0.0;
center_height = radius;
}
// drop-cutter methods: vertex and facet are handled in base-class
// drop-cutter edgeDrop
CC_CLZ_Pair BallCutter::singleEdgeDropCanonical(const Point& u1, const Point& u2) const {
// the plane of the line will slice the spherical cutter at
// a distance d==u1.y==u2.y from the center of the cutter
// here the radius of the circular section is s:
double s = sqrt( square(radius) - square( u1.y ) );
Point normal(u2.z - u1.z, -(u2.x - u1.x), 0); // (dz, -du) is a normal to the line
normal.xyNormalize();
if (normal.y < 0) // flip normal so it points upward
normal = -1*normal;
Point cc( -s*normal.x, u1.y, 0); // from (0,u1.y,0) we go a distance -s in the normal direction
cc.z_projectOntoEdge(u1,u2);
double cl_z = cc.z + s*normal.y - radius;
return CC_CLZ_Pair( cc.x , cl_z);
}
// push-cutter: vertex and facet handled in base-class
bool BallCutter::generalEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const {
bool result = false;
if ( isZero_tol( (p2-p1).xyNorm() ) ) { // this would be a vertical edge
return result;
}
const Point ufp1 = f.p1 + Point(0,0,radius); // take a fiber which is raised up by radius
const Point ufp2 = f.p2 + Point(0,0,radius); // and intersect it with a cylinder around the edge p1-p2
// Ray : P(t) = O + t*V from point O, in direction V
// Cylinder [A, B, r] from point A to point B, radius r
// Point P on infinite cylinder if ((P - A) x (B - A))^2 = r^2 * (B - A)^2
// expand : ((O - A) x (B - A) + t * (V x (B - A)))^2 = r^2 * (B - A)^2
// equation in the form (X + t * Y)^2 = d , where:
// X = (O - A) x (B - A)
// Y = V x (B - A)
// d = r^2 * (B - A)^2
// expand the equation :
// t^2 * (Y . Y) + t * (2 * (X . Y)) + (X . X) - d = 0
// => second order equation in the form : a*t^2 + b*t + c = 0 where
// a = (Y . Y)
// b = 2 * (X . Y)
// c = (X . X) - d
Point ab = p2-p1; // axis of the cylinder
Point ao = (ufp1 - p1); // cyl start to ray start
Point ao_x_ab = ao.cross(ab); // cross product
Point v_x_ab = (ufp2-ufp1).cross(ab); // cross product
double ab2 = ab.dot(ab); // dot product
double a = v_x_ab.dot(v_x_ab); // dot product
double b = 2 * ( v_x_ab.dot(ao_x_ab) ); // dot product
double c = ao_x_ab.dot(ao_x_ab) - (radius*radius * ab2);
// solve second order equation : a*t^2 + b*t + c = 0
// t = (-b +/- sqrt( b^2 - 4ac ) ) / 2a
double discr = b*b-4*a*c;
double t1, t2;
if ( isZero_tol( discr ) ) { // tangent case, only one root
t1= -b/(2*a);
if ( calcCCandUpdateInterval( t1, p1, p2, f, i ) )
result = true;
} else if ( discr > 0.0 ) { // two roots
t1 = (-b + sqrt( discr))/(2*a);
t2 = (-b - sqrt( discr))/(2*a);
if ( calcCCandUpdateInterval( t1, p1, p2, f, i ) )
result = true;
if ( calcCCandUpdateInterval( t2, p1, p2, f, i ) )
result = true;
}
return result;
}
// t is a position along the fiber
// p1-p2 is the edge
// Interval& i is updated
bool BallCutter::calcCCandUpdateInterval( double t, const Point& p1, const Point& p2, const Fiber& f, Interval& i) const {
Point cl_center = f.point(t) + Point(0,0,radius); // center of ball is here
CCPoint cc_tmp = cl_center.closestPoint(p1,p2); // cc-point on the edge, point on edge closest to center
cc_tmp.type = EDGE_BALL;
// require contact with lower hemishphere
return i.update_ifCCinEdgeAndTrue( t, cc_tmp, p1, p2, (cl_center.z-cc_tmp.z >=0) );
}
std::string BallCutter::str() const {
std::ostringstream o;
o << *this;
return o.str();
}
std::ostream& operator<<(std::ostream &stream, BallCutter c) {
stream << "BallCutter(d=" << c.diameter << ", r=" << c.radius << ", L=" << c.length << ")";
return stream;
}
} // end namespace
// end file ballcutter.cpp