#include "posemath.h"
#include "tc_types.h"
#include "tc.h"
#include "tp_types.h"
#include "rtapi_math.h"
#include "spherical_arc.h"
#include "blendmath.h"
#include "tp_debug.h"
double findMaxTangentAngle(double v_plan, double acc_limit, double cycle_time)
{
double acc_margin = BLEND_ACC_RATIO_NORMAL * BLEND_KINK_FACTOR * acc_limit;
double dx = v_plan / cycle_time;
if (dx > 0.0) {
return (acc_margin / dx);
} else {
tp_debug_print(" Velocity or period is negative!\n");
return TP_ANGLE_EPSILON;
}
}
double findKinkAccel(double kink_angle, double v_plan, double cycle_time)
{
double dx = v_plan / cycle_time;
if (dx > 0.0) {
return (dx * kink_angle);
} else {
rtapi_print_msg(RTAPI_MSG_ERR, "dx < 0 in KinkAccel\n");
return 0;
}
}
double fsign(double f)
{
if (f>0) {
return 1.0;
} else if (f < 0) {
return -1.0;
} else {
return 0;
}
}
static inline double negate(double f, int neg)
{
return (neg) ? -f : f;
}
int clip_min(double * const x, double min) {
if ( *x < min ) {
*x = min;
return 1;
}
return 0;
}
int clip_max(double * const x, double max) {
if ( *x > max ) {
*x = max;
return 1;
}
return 0;
}
double saturate(double x, double max) {
if ( x > max ) {
return max;
}
else if ( x < (-max) ) {
return -max;
}
else {
return x;
}
}
double bisaturate(double x, double max, double min) {
if ( x > max ) {
return max;
}
else if ( x < min ) {
return min;
}
else {
return x;
}
}
inline double bound(double x, double max, double min) {
if ( x > max ) {
return max;
}
else if ( x < (min) ) {
return min;
}
else {
return x;
}
}
int sat_inplace(double * const x, double max) {
if ( *x > max ) {
*x = max;
return 1;
}
else if ( *x < -max ) {
*x = -max;
return -1;
}
return 0;
}
#if 0#endif
static inline int findSpiralApproximation(PmCircle const * const circ,
PmCartesian const * const base_pt,
PmCartesian const * const u_tan,
PmCartesian * const center_out,
double * const radius_out)
{
double dr = circ->spiral / circ->angle;
pmCartScalMult(u_tan, dr, center_out);
pmCartCartAddEq(center_out, &circ->center);
PmCartesian r_adjust;
pmCartCartSub(base_pt, center_out, &r_adjust);
pmCartMag(&r_adjust, radius_out);
tp_debug_print(" adjusted center = %f %f %f\n",
center_out->x,
center_out->y,
center_out->z);
tp_debug_print(" adjusted radius = %f\n", *radius_out);
return TP_ERR_OK;
}
static inline double findTrimAngle(PmCartesian const * const P,
PmCartesian const * const arc_center,
PmCartesian const * const center)
{
PmCartesian u_P;
pmCartCartSub(P, center, &u_P);
pmCartUnitEq(&u_P);
PmCartesian u_arccenter;
pmCartCartSub(arc_center, center, &u_arccenter);
pmCartUnitEq(&u_arccenter);
double dot;
pmCartCartDot(&u_arccenter, &u_P, &dot);
double dphi = acos(saturate(dot,1.0));
tp_debug_print(" dphi = %g\n",dphi);
return dphi;
}
int checkTangentAngle(PmCircle const * const circ, SphericalArc const * const arc, BlendGeom3 const * const geom, BlendParameters const * const param, double cycle_time, int at_end)
{
PmCartesian u_circ, u_arc;
arcTangent(arc, &u_arc, at_end);
if (at_end) {
pmCircleTangentVector(circ, 0, &u_circ);
} else {
pmCircleTangentVector(circ, circ->angle, &u_circ);
}
pmCartUnitEq(&u_arc);
double dot;
pmCartCartDot(&u_circ, &u_arc, &dot);
double blend_angle = acos(saturate(dot,1.0));
double angle_max = findMaxTangentAngle(param->v_plan, param->a_max, cycle_time);
tp_debug_print("tangent angle = %f, max = %f\n",
blend_angle,
angle_max);
tp_debug_print("circ_tan = [%g %g %g]\n",
u_circ.x,
u_circ.y,
u_circ.z);
tp_debug_print("arc_tan = [%g %g %g]\n",
u_arc.x,
u_arc.y,
u_arc.z);
PmCartesian diff;
pmCartCartSub(&u_arc,&u_circ,&diff);
tp_debug_print("diff = [%g %g %g]\n",
diff.x,
diff.y,
diff.z);
if (blend_angle > angle_max) {
tp_debug_print("angle too large\n");
return TP_ERR_FAIL;
}
return TP_ERR_OK;
}
int pmCartCartParallel(PmCartesian const * const u1,
PmCartesian const * const u2,
double tol)
{
double d_diff;
{
PmCartesian u_diff;
pmCartCartSub(u1, u2, &u_diff);
pmCartMagSq(&u_diff, &d_diff);
}
tp_debug_json_start(pmCartCartParallel);
tp_debug_json_double(d_diff);
tp_debug_json_end();
return d_diff < tol;
}
int pmCartCartAntiParallel(PmCartesian const * const u1,
PmCartesian const * const u2,
double tol)
{
double d_sum;
{
PmCartesian u_sum;
pmCartCartAdd(u1, u2, &u_sum);
pmCartMagSq(&u_sum, &d_sum);
}
tp_debug_json_start(pmCartCartAntiParallel);
tp_debug_json_double(d_sum);
tp_debug_json_end();
return d_sum < tol;
}
int pmUnitCartsColinear(PmCartesian const * const u1,
PmCartesian const * const u2)
{
return pmCartCartParallel(u1, u2, TP_ANGLE_EPSILON_SQ) || pmCartCartAntiParallel(u1, u2, TP_ANGLE_EPSILON_SQ);
}
int findIntersectionAngle(PmCartesian const * const u1,
PmCartesian const * const u2, double * const theta)
{
double dot;
pmCartCartDot(u1, u2, &dot);
if (dot > 1.0 || dot < -1.0) {
tp_debug_print("dot product %.16g outside domain of acos! u1 = %.16g %.16g %.16g, u2 = %.16g %.16g %.16g\n",
dot,
u1->x,
u1->y,
u1->z,
u2->x,
u2->y,
u2->z);
sat_inplace(&dot,1.0);
}
*theta = acos(-dot)/2.0;
return TP_ERR_OK;
}
double pmCartMin(PmCartesian const * const in)
{
return fmin(fmin(in->x,in->y),in->z);
}
int calculateInscribedDiameter(PmCartesian const * const normal,
PmCartesian const * const bounds, double * const diameter)
{
if (!normal ) {
return TP_ERR_MISSING_INPUT;
}
double n_mag;
pmCartMagSq(normal, &n_mag);
double mag_err = fabs(1.0 - n_mag);
if (mag_err > pmSqrt(TP_POS_EPSILON)) {
return TP_ERR_FAIL;
}
PmCartesian planar_x,planar_y,planar_z;
pmCartScalMult(normal, -normal->x, &planar_x);
pmCartScalMult(normal, -normal->y, &planar_y);
pmCartScalMult(normal, -normal->z, &planar_z);
planar_x.x += 1.0;
planar_y.y += 1.0;
planar_z.z += 1.0;
pmCartAbs(&planar_x, &planar_x);
pmCartAbs(&planar_y, &planar_y);
pmCartAbs(&planar_z, &planar_z);
planar_x.x = fmax(planar_x.x,TP_POS_EPSILON);
planar_y.y = fmax(planar_y.y,TP_POS_EPSILON);
planar_z.z = fmax(planar_z.z,TP_POS_EPSILON);
double x_scale, y_scale, z_scale;
pmCartMag(&planar_x, &x_scale);
pmCartMag(&planar_y, &y_scale);
pmCartMag(&planar_z, &z_scale);
double x_extent=0, y_extent=0, z_extent=0;
if (bounds->x != 0) {
x_extent = bounds->x / x_scale;
}
if (bounds->y != 0) {
y_extent = bounds->y / y_scale;
}
if (bounds->z != 0) {
z_extent = bounds->z / z_scale;
}
*diameter = fmax(fmax(x_extent, y_extent),z_extent);
if (bounds->x != 0) {
*diameter = fmin(*diameter, x_extent);
}
if (bounds->y != 0) {
*diameter = fmin(*diameter, y_extent);
}
if (bounds->z != 0) {
*diameter = fmin(*diameter, z_extent);
}
return TP_ERR_OK;
}
int findAccelScale(PmCartesian const * const acc,
PmCartesian const * const bounds,
PmCartesian * const scale)
{
if (!acc || !bounds ) {
return TP_ERR_MISSING_INPUT;
}
if (!scale ) {
return TP_ERR_MISSING_OUTPUT;
}
if (bounds->x != 0) {
scale->x = fabs(acc->x / bounds->x);
} else {
scale->x = 0;
}
if (bounds->y != 0) {
scale->y = fabs(acc->y / bounds->y);
} else {
scale->y = 0;
}
if (bounds->z != 0) {
scale->z = fabs(acc->z / bounds->z);
} else {
scale->z = 0;
}
return TP_ERR_OK;
}
int quadraticFormula(double A, double B, double C, double * const root0,
double * const root1)
{
double disc = pmSq(B) - 4.0 * A * C;
if (disc < 0) {
tp_debug_print("discriminant %.12g < 0, A=%.12g, B=%.12g,C=%.12g\n", disc, A, B, C);
return TP_ERR_FAIL;
}
double t1 = pmSqrt(disc);
if (root0) {
*root0 = ( -B + t1) / (2.0 * A);
}
if (root1) {
*root1 = ( -B - t1) / (2.0 * A);
}
return TP_ERR_OK;
}
int blendGeom3Init(BlendGeom3 * const geom,
TC_STRUCT const * const prev_tc,
TC_STRUCT const * const tc)
{
geom->v_max1 = prev_tc->maxvel;
geom->v_max2 = tc->maxvel;
int res_u1 = tcGetEndTangentUnitVector(prev_tc, &geom->u_tan1);
int res_u2 = tcGetStartTangentUnitVector(tc, &geom->u_tan2);
geom->u1 = geom->u_tan1;
geom->u2 = geom->u_tan2;
int res_intersect = tcGetIntersectionPoint(prev_tc, tc, &geom->P);
tp_debug_print("Intersection point P = %f %f %f\n",
geom->P.x,
geom->P.y,
geom->P.z);
int res_angle = findIntersectionAngle(&geom->u_tan1,
&geom->u_tan2,
&geom->theta_tan);
if(PM_PI / 2.0 - geom->theta_tan < TP_ANGLE_EPSILON) {
tp_debug_print("Intersection angle too close to pi/2, can't compute normal\n");
return TP_ERR_TOLERANCE;
}
if(geom->theta_tan < TP_ANGLE_EPSILON) {
tp_debug_print("Intersection angle too small for arc fit\n");
return TP_ERR_TOLERANCE;
}
blendCalculateNormals3(geom);
return res_u1 |
res_u2 |
res_intersect |
res_angle;
}
int blendParamKinematics(BlendGeom3 * const geom,
BlendParameters * const param,
TC_STRUCT const * const prev_tc,
TC_STRUCT const * const tc,
PmCartesian const * const acc_bound,
PmCartesian const * const vel_bound,
double maxFeedScale)
{
param->phi = (PM_PI - param->theta * 2.0);
double nominal_tolerance;
tcFindBlendTolerance(prev_tc, tc, ¶m->tolerance, &nominal_tolerance);
int res_dia = calculateInscribedDiameter(&geom->binormal, acc_bound, ¶m->a_max);
param->a_n_max = param->a_max * BLEND_ACC_RATIO_NORMAL;
tp_debug_print("a_max = %f, a_n_max = %f\n", param->a_max,
param->a_n_max);
double v_req_prev = tcGetMaxTargetVel(prev_tc, 1.0);
double v_req_this = tcGetMaxTargetVel(tc, 1.0);
tp_debug_print("vr_prev = %f, vr_this = %f\n", v_req_prev, v_req_this);
param->v_req = fmax(v_req_prev, v_req_this);
param->v_goal = fmax(tcGetMaxTargetVel(prev_tc, maxFeedScale),
tcGetMaxTargetVel(tc, maxFeedScale));
double v_planar_max;
res_dia |= calculateInscribedDiameter(&geom->binormal, vel_bound, &v_planar_max);
tp_debug_print("v_planar_max = %f\n", v_planar_max);
double phi_effective = fmin(param->phi, PM_PI * 0.49);
double v_max1 = fmin(prev_tc->maxvel, tc->maxvel / cos(phi_effective));
double v_max2 = fmin(tc->maxvel, prev_tc->maxvel / cos(phi_effective));
tp_debug_print("v_max1 = %f, v_max2 = %f\n", v_max1, v_max2);
double v_area = v_max1 * v_max2 / 2.0 * sin(param->phi);
tp_debug_print("phi = %f\n", param->phi);
tp_debug_print("v_area = %f\n", v_area);
PmCartesian tmp1, tmp2, diff;
pmCartScalMult(&geom->u1, v_max1, &tmp1);
pmCartScalMult(&geom->u2, v_max2, &tmp2);
pmCartCartSub(&tmp2, &tmp1, &diff);
double base;
pmCartMag(&diff, &base);
tp_debug_print("v_base = %f\n", base);
double v_max_alt = 2.0 * v_area / base;
if (prev_tc->motion_type != TC_LINEAR || tc->motion_type != TC_LINEAR) {
v_max_alt = 0.0;
}
tp_debug_print("v_max_alt = %f\n", v_max_alt);
double v_max = fmax(v_max_alt, v_planar_max);
tp_debug_print("v_max = %f\n", v_max);
param->v_goal = fmin(param->v_goal, v_max);
tp_debug_print("v_goal = %f, max scale = %f\n", param->v_goal, maxFeedScale);
return res_dia;
}
int blendInit3FromLineArc(BlendGeom3 * const geom, BlendParameters * const param,
TC_STRUCT const * const prev_tc,
TC_STRUCT const * const tc,
PmCartesian const * const acc_bound,
PmCartesian const * const vel_bound,
double maxFeedScale)
{
if (tc->motion_type != TC_CIRCULAR || prev_tc->motion_type != TC_LINEAR) {
return TP_ERR_INPUT_TYPE;
}
int res_init = blendGeom3Init(geom, prev_tc, tc);
if (res_init != TP_ERR_OK) {
return res_init;
}
findSpiralApproximation(&tc->coords.circle.xyz,
&geom->P,
&geom->u_tan2,
&geom->center2,
&geom->radius2);
param->convex2 = arcConvexTest(&geom->center2, &geom->P, &geom->u_tan1, true);
tp_debug_print("circ2 convex: %d\n",
param->convex2);
double blend_angle_2 = param->convex2 ? geom->theta_tan : PM_PI / 2.0;
param->phi2_max = fmin(tc->coords.circle.xyz.angle / 3.0, blend_angle_2);
param->theta = geom->theta_tan;
if (param->convex2) {
PmCartesian blend_point;
pmCirclePoint(&tc->coords.circle.xyz,
param->phi2_max / 2.0,
&blend_point);
pmCartCartSub(&blend_point, &geom->P, &geom->u2);
pmCartUnitEq(&geom->u2);
param->theta = fmin(param->theta, geom->theta_tan - param->phi2_max / 4.0);
}
tp_debug_print("phi2_max = %f\n", param->phi2_max);
blendGeom3Print(geom);
const double theta_min = PM_PI / 6.0;
if (param->theta < theta_min) {
tp_debug_print("theta = %f < min %f, aborting arc...\n",
param->theta,
theta_min);
}
tp_debug_print("theta = %f\n", param->theta);
param->phi = (PM_PI - param->theta * 2.0);
param->L1 = fmin(prev_tc->target, prev_tc->nominal_length / 2.0);
if (param->convex2) {
param->L2 = sin(param->phi2_max/4.0) * geom->radius2;
} else {
param->L2 = param->phi2_max * geom->radius2;
}
tp_debug_print("L1 = %f, L2 = %f\n", param->L1, param->L2);
int res_kin = blendParamKinematics(geom,
param,
prev_tc,
tc,
acc_bound,
vel_bound,
maxFeedScale);
return res_kin;
}
int blendInit3FromArcLine(BlendGeom3 * const geom, BlendParameters * const param,
TC_STRUCT const * const prev_tc,
TC_STRUCT const * const tc,
PmCartesian const * const acc_bound,
PmCartesian const * const vel_bound,
double maxFeedScale)
{
if (tc->motion_type != TC_LINEAR || prev_tc->motion_type != TC_CIRCULAR) {
return TP_ERR_INPUT_TYPE;
}
int res_init = blendGeom3Init(geom, prev_tc, tc);
if (res_init != TP_ERR_OK) {
return res_init;
}
findSpiralApproximation(&prev_tc->coords.circle.xyz,
&geom->P,
&geom->u_tan1,
&geom->center1,
&geom->radius1);
param->convex1 = arcConvexTest(&geom->center1, &geom->P, &geom->u_tan2, false);
tp_debug_print("circ1 convex: %d\n",
param->convex1);
double blend_angle_1 = param->convex1 ? geom->theta_tan : PM_PI / 2.0;
param->phi1_max = fmin(prev_tc->coords.circle.xyz.angle * 2.0 / 3.0, blend_angle_1);
param->theta = geom->theta_tan;
if (param->convex1) {
PmCartesian blend_point;
pmCirclePoint(&prev_tc->coords.circle.xyz,
prev_tc->coords.circle.xyz.angle - param->phi1_max / 2.0 ,
&blend_point);
pmCartCartSub(&geom->P, &blend_point, &geom->u1);
pmCartUnitEq(&geom->u1);
param->theta = fmin(param->theta, geom->theta_tan - param->phi1_max / 4.0);
}
blendGeom3Print(geom);
tp_debug_print("phi1_max = %f\n", param->phi1_max);
const double theta_min = PM_PI / 6.0;
if (param->theta < theta_min) {
tp_debug_print("theta = %f < min %f, aborting arc...\n",
param->theta,
theta_min);
}
tp_debug_print("theta = %f\n", param->theta);
param->L1 = param->phi1_max * (geom->radius1);
param->L2 = tc->nominal_length / 2.0;
if (param->convex1) {
param->L1 = sin(param->phi1_max/4.0) * geom->radius1;
}
tp_debug_print("L1 = %f, L2 = %f\n", param->L1, param->L2);
int res_kin = blendParamKinematics(geom,
param,
prev_tc,
tc,
acc_bound,
vel_bound,
maxFeedScale);
return res_kin;
}
int blendInit3FromArcArc(BlendGeom3 * const geom, BlendParameters * const param,
TC_STRUCT const * const prev_tc,
TC_STRUCT const * const tc,
PmCartesian const * const acc_bound,
PmCartesian const * const vel_bound,
double maxFeedScale)
{
if (tc->motion_type != TC_CIRCULAR || prev_tc->motion_type != TC_CIRCULAR) {
return TP_ERR_FAIL;
}
int res_init = blendGeom3Init(geom, prev_tc, tc);
if (res_init != TP_ERR_OK) {
return res_init;
}
findSpiralApproximation(&prev_tc->coords.circle.xyz,
&geom->P,
&geom->u_tan1,
&geom->center1,
&geom->radius1);
findSpiralApproximation(&tc->coords.circle.xyz,
&geom->P,
&geom->u_tan2,
&geom->center2,
&geom->radius2);
blendCalculateNormals3(geom);
pmCirclePoint(&tc->coords.circle.xyz, 0.0, &geom->P);
tp_debug_print("Intersection point P = %f %f %f\n",
geom->P.x,
geom->P.y,
geom->P.z);
param->convex1 = arcConvexTest(&geom->center1, &geom->P, &geom->u_tan2, false);
param->convex2 = arcConvexTest(&geom->center2, &geom->P, &geom->u_tan1, true);
tp_debug_print("circ1 convex: %d, circ2 convex: %d\n",
param->convex1,
param->convex2);
double blend_angle_1 = param->convex1 ? geom->theta_tan : PM_PI / 2.0;
double blend_angle_2 = param->convex2 ? geom->theta_tan : PM_PI / 2.0;
param->phi1_max = fmin(prev_tc->coords.circle.xyz.angle * 2.0 / 3.0, blend_angle_1);
param->phi2_max = fmin(tc->coords.circle.xyz.angle / 3.0, blend_angle_2);
param->theta = geom->theta_tan;
if (param->convex1) {
PmCartesian blend_point;
pmCirclePoint(&prev_tc->coords.circle.xyz,
prev_tc->coords.circle.xyz.angle - param->phi1_max / 2.0,
&blend_point);
pmCartCartSub(&geom->P, &blend_point, &geom->u1);
pmCartUnitEq(&geom->u1);
param->theta = fmin(param->theta, geom->theta_tan - param->phi1_max / 4.0);
}
if (param->convex2) {
PmCartesian blend_point;
pmCirclePoint(&tc->coords.circle.xyz,
param->phi2_max / 2.0,
&blend_point);
pmCartCartSub(&blend_point, &geom->P, &geom->u2);
pmCartUnitEq(&geom->u2);
param->theta = fmin(param->theta, geom->theta_tan - param->phi2_max / 4.0);
}
blendGeom3Print(geom);
const double theta_min = PM_PI / 12.0;
if (param->theta < theta_min) {
tp_debug_print("theta = %f < min %f, aborting arc...\n",
param->theta,
theta_min);
return TP_ERR_FAIL;
}
tp_debug_print("theta = %f\n", param->theta);
param->phi = (PM_PI - param->theta * 2.0);
param->L1 = param->phi1_max * geom->radius1;
param->L2 = param->phi2_max * geom->radius2;
if (param->convex1) {
param->L1 = sin(param->phi1_max/4.0) * geom->radius1;
}
if (param->convex2) {
param->L2 = sin(param->phi2_max/4.0) * geom->radius2;
}
tp_debug_print("L1 = %f, L2 = %f\n", param->L1, param->L2);
tp_debug_print("phi1_max = %f\n",param->phi1_max);
tp_debug_print("phi2_max = %f\n",param->phi2_max);
int res_kin = blendParamKinematics(geom,
param,
prev_tc,
tc,
acc_bound,
vel_bound,
maxFeedScale);
return res_kin;
}
int blendInit3FromLineLine(BlendGeom3 * const geom, BlendParameters * const param,
TC_STRUCT const * const prev_tc,
TC_STRUCT const * const tc,
PmCartesian const * const acc_bound,
PmCartesian const * const vel_bound,
double maxFeedScale)
{
if (tc->motion_type != TC_LINEAR || prev_tc->motion_type != TC_LINEAR) {
return TP_ERR_FAIL;
}
int res_init = blendGeom3Init(geom, prev_tc, tc);
if (res_init != TP_ERR_OK) {
return res_init;
}
param->theta = geom->theta_tan;
tp_debug_print("theta = %f\n", param->theta);
param->phi = (PM_PI - param->theta * 2.0);
blendGeom3Print(geom);
param->L1 = fmin(prev_tc->target, prev_tc->nominal_length * BLEND_DIST_FRACTION);
param->L2 = tc->target * BLEND_DIST_FRACTION;
tp_debug_print("prev. nominal length = %f, next nominal_length = %f\n",
prev_tc->nominal_length, tc->nominal_length);
tp_debug_print("L1 = %f, L2 = %f\n", param->L1, param->L2);
int res_kin = blendParamKinematics(geom,
param,
prev_tc,
tc,
acc_bound,
vel_bound,
maxFeedScale);
return res_kin;
}
int blendCalculateNormals3(BlendGeom3 * const geom)
{
int err_cross = pmCartCartCross(&geom->u_tan1,
&geom->u_tan2,
&geom->binormal);
int err_unit_b = pmCartUnitEq(&geom->binormal);
tp_debug_print("binormal = [%f %f %f]\n", geom->binormal.x,
geom->binormal.y,
geom->binormal.z);
pmCartCartSub(&geom->u_tan2, &geom->u_tan1, &geom->normal);
int err_unit_n = pmCartUnitEq(&geom->normal);
tp_debug_print("normal = [%f %f %f]\n", geom->normal.x,
geom->normal.y,
geom->normal.z);
return (err_cross || err_unit_b || err_unit_n);
}
int blendComputeParameters(BlendParameters * const param)
{
double h_tol = param->tolerance / (1.0 - sin(param->theta));
double d_tol = cos(param->theta) * h_tol;
tp_debug_print(" d_tol = %f\n", d_tol);
double d_lengths = fmin(param->L1, param->L2);
double d_geom = fmin(d_lengths, d_tol);
double R_geom = tan(param->theta) * d_geom;
double v_normal = pmSqrt(param->a_n_max * R_geom);
tp_debug_print("v_normal = %f\n", v_normal);
param->v_plan = fmin(v_normal, param->v_goal);
double a_parabolic = param->a_max * 0.5;
double v_triangle = pmSqrt(2.0 * a_parabolic * d_geom);
double t_blend = fmin(v_triangle, param->v_plan) / (a_parabolic);
double s_blend = t_blend * param->v_plan;
double R_blend = fmin(s_blend / param->phi, R_geom);
param->R_plan = fmax(pmSq(param->v_plan) / param->a_n_max, R_blend);
param->d_plan = param->R_plan / tan(param->theta);
tp_debug_print("v_plan = %f\n", param->v_plan);
tp_debug_print("R_plan = %f\n", param->R_plan);
tp_debug_print("d_plan = %f\n", param->d_plan);
if (param->v_plan > param->v_req) {
param->v_actual = param->v_req;
} else {
param->v_actual = param->v_plan;
}
param->s_arc = param->R_plan * param->phi;
if (param->R_plan < TP_POS_EPSILON) {
tp_debug_print("#Blend radius too small, aborting arc\n");
return TP_ERR_FAIL;
}
if (param->s_arc < TP_MIN_ARC_LENGTH) {
tp_debug_print("#Blend arc length too small, aborting arc\n");
return TP_ERR_FAIL;
}
return TP_ERR_OK;
}
int blendCheckConsume(BlendParameters * const param,
BlendPoints3 const * const points,
TC_STRUCT const * const prev_tc, int gap_cycles)
{
param->consume = 0;
param->line_length = 0;
if (!prev_tc) {
return -1;
}
if (prev_tc->motion_type != TC_LINEAR) {
return 0;
}
double L_prev = prev_tc->target - points->trim1;
double prev_seg_time = L_prev / param->v_plan;
bool can_consume = tcCanConsume(prev_tc);
param->consume = (prev_seg_time < gap_cycles * prev_tc->cycle_time && can_consume);
if (param->consume) {
tp_debug_print("consuming prev line, L_prev = %g\n",
L_prev);
param->line_length = L_prev;
}
return 0;
}
int blendFindPoints3(BlendPoints3 * const points, BlendGeom3 const * const geom,
BlendParameters const * const param)
{
double center_dist = param->R_plan / sin(param->theta);
tp_debug_print("center_dist = %f\n", center_dist);
pmCartScalMult(&geom->normal, center_dist, &points->arc_center);
pmCartCartAddEq(&points->arc_center, &geom->P);
tp_debug_print("arc_center = %f %f %f\n",
points->arc_center.x,
points->arc_center.y,
points->arc_center.z);
pmCartScalMult(&geom->u1, -param->d_plan, &points->arc_start);
pmCartCartAddEq(&points->arc_start, &geom->P);
tp_debug_print("arc_start = %f %f %f\n",
points->arc_start.x,
points->arc_start.y,
points->arc_start.z);
pmCartScalMult(&geom->u2, param->d_plan, &points->arc_end);
pmCartCartAddEq(&points->arc_end, &geom->P);
tp_debug_print("arc_end = %f %f %f\n",
points->arc_end.x,
points->arc_end.y,
points->arc_end.z);
points->trim1 = param->d_plan;
points->trim2 = param->d_plan;
return TP_ERR_OK;
}
int blendLineArcPostProcess(BlendPoints3 * const points, BlendPoints3 const * const points_in,
BlendParameters * const param, BlendGeom3 const * const geom,
PmCartLine const * const line1, PmCircle const * const circ2)
{
double d2 = negate(param->R_plan, param->convex2) + geom->radius2;
tp_debug_print("d2 = %f\n", d2);
PmCartesian n1;
pmCartCartCross(&geom->binormal, &geom->u1, &n1);
pmCartUnitEq(&n1);
tp_debug_print("n1 = %f %f %f\n",
n1.x,
n1.y,
n1.z);
PmCartesian r_PC2;
pmCartCartSub(&geom->center2, &geom->P, &r_PC2);
double c2_u,c2_n; pmCartCartDot(&r_PC2, &geom->u1, &c2_u);
pmCartCartDot(&r_PC2, &n1, &c2_n);
tp_debug_print("c2_u = %f, c2_n = %f\n",
c2_u,
c2_n);
double d_L; double A = 1;
double B = 2.0 * c2_u;
double C = pmSq(c2_u) - pmSq(d2) + pmSq(param->R_plan - c2_n);
double root0,root1;
int res_dist = quadraticFormula(A, B, C, &root0, &root1);
if (res_dist) {
return TP_ERR_FAIL;
}
tp_debug_print("root0 = %f, root1 = %f\n", root0,
root1);
d_L = fmin(fabs(root0),fabs(root1));
if (d_L < 0) {
tp_debug_print("d_L can't be < 0, aborting...\n");
return TP_ERR_FAIL;
}
PmCartesian C_u, C_n;
pmCartScalMult(&geom->u1, -d_L, &C_u);
pmCartScalMult(&n1, param->R_plan, &C_n);
PmCartesian r_PC;
pmCartCartAdd(&C_u, &C_n, &r_PC);
pmCartCartAdd(&geom->P, &r_PC, &points->arc_center);
tp_debug_print("arc center = %f %f %f\n",
points->arc_center.x,
points->arc_center.y,
points->arc_center.z);
double h;
pmCartMag(&r_PC, &h);
tp_debug_print("center_dist = %f\n", h);
double T_final = h - param->R_plan;
tp_debug_print("T_final = %f\n",T_final);
if (T_final > param->tolerance) {
tp_debug_print("Projected circle T (%f) exceeds tolerance %f, aborting blend arc\n",
T_final,
param->tolerance);
return TP_ERR_FAIL;
}
points->trim1 = d_L;
points->trim2 = findTrimAngle(&geom->P,
&points->arc_center,
&geom->center2);
return TP_ERR_OK;
}
int blendArcLinePostProcess(BlendPoints3 * const points,
BlendPoints3 const * const points_in,
BlendParameters * const param,
BlendGeom3 const * const geom,
PmCircle const * const circ1,
PmCartLine const * const line2)
{
double d1 = negate(param->R_plan, param->convex1) + geom->radius1;
tp_debug_print("d1 = %f\n", d1);
PmCartesian n2;
pmCartCartCross(&geom->binormal, &geom->u2, &n2);
pmCartUnitEq(&n2);
tp_debug_print("n2 = %f %f %f\n",
n2.x,
n2.y,
n2.z);
PmCartesian r_PC1;
pmCartCartSub(&geom->center1, &geom->P, &r_PC1);
double c1_u, c1_n; pmCartCartDot(&r_PC1, &geom->u2, &c1_u);
pmCartCartDot(&r_PC1, &n2, &c1_n);
double d_L; double A = 1;
double B = 2.0 * c1_u;
double C = pmSq(c1_u) - pmSq(d1) + pmSq(param->R_plan - c1_n);
double root0,root1;
int res_dist = quadraticFormula(A, B, C, &root0, &root1);
if (res_dist) {
return TP_ERR_FAIL;
}
tp_debug_print("root0 = %f, root1 = %f\n", root0,
root1);
d_L = fmin(fabs(root0),fabs(root1));
if (d_L < 0) {
tp_debug_print("d_L can't be < 0, aborting...\n");
return TP_ERR_FAIL;
}
PmCartesian C_u, C_n;
pmCartScalMult(&geom->u2, d_L, &C_u);
pmCartScalMult(&n2, param->R_plan, &C_n);
PmCartesian r_PC;
pmCartCartAdd(&C_u, &C_n, &r_PC);
pmCartCartAdd(&geom->P, &r_PC, &points->arc_center);
tp_debug_print("arc center = %f %f %f\n",
points->arc_center.x,
points->arc_center.y,
points->arc_center.z);
double h;
pmCartMag(&r_PC, &h);
tp_debug_print("center_dist = %f\n", h);
double T_final = h - param->R_plan;
if (T_final > param->tolerance) {
tp_debug_print("Projected circle T (%f) exceeds tolerance %f, aborting blend arc\n",
T_final,
param->tolerance);
return TP_ERR_FAIL;
}
tp_debug_print("T_final = %f\n",T_final);
points->trim1 = findTrimAngle(&geom->P,
&points->arc_center,
&geom->center1);
points->trim2 = d_L;
return TP_ERR_OK;
}
int blendArcArcPostProcess(BlendPoints3 * const points, BlendPoints3 const * const points_in,
BlendParameters * const param, BlendGeom3 const * const geom,
PmCircle const * const circ1, PmCircle const * const circ2)
{
double d1 = negate(param->R_plan, param->convex1) + geom->radius1;
double d2 = negate(param->R_plan, param->convex2) + geom->radius2;
tp_debug_print("d1 = %f, d2 = %f\n", d1, d2);
PmCartesian r_C1C2;
pmCartCartSub(&geom->center2, &geom->center1, &r_C1C2);
double c2x;
pmCartMag(&r_C1C2, &c2x);
double Cx = (-pmSq(d1) + pmSq(d2)-pmSq(c2x)) / (-2.0 * c2x);
double Cy = pmSqrt(pmSq(d1) - pmSq(Cx));
tp_debug_print("Cx = %f, Cy = %f\n",Cx,Cy);
PmCartesian uc;
int norm_err = pmCartUnit(&r_C1C2, &uc);
if (norm_err) {
return TP_ERR_FAIL;
}
PmCartesian nc;
pmCartCartCross(&geom->binormal, &uc, &nc);
double dot1;
pmCartCartDot(&geom->normal, &nc, &dot1);
if (dot1 < 0) {
pmCartNegEq(&nc);
}
norm_err = pmCartUnitEq(&nc);
if (norm_err) {
return TP_ERR_FAIL;
}
PmCartesian c_x, c_y;
pmCartScalMult(&uc, Cx, &c_x);
pmCartScalMult(&nc, Cy, &c_y);
PmCartesian r_PC1;
pmCartCartSub(&geom->center1, &geom->P, &r_PC1);
PmCartesian test1, test2;
pmCartCartAdd(&r_PC1, &c_x, &test1);
test2=test1;
pmCartCartAddEq(&test1, &c_y);
pmCartCartSubEq(&test2, &c_y);
double mag1,mag2;
pmCartMag(&test1, &mag1);
pmCartMag(&test2, &mag2);
if (mag2 < mag1)
{
pmCartNegEq(&c_y);
}
PmCartesian r_C1C;
pmCartCartAdd(&c_x, &c_y, &r_C1C);
pmCartCartAdd(&geom->center1, &r_C1C, &points->arc_center);
tp_debug_print("arc center = %f %f %f\n",
points->arc_center.x,
points->arc_center.y,
points->arc_center.z);
PmCartesian r_C2C;
pmCartCartSub(&points->arc_center, &geom->center2, &r_C2C);
PmCartesian r_PC;
pmCartCartSub(&points->arc_center, &geom->P, &r_PC);
double h;
pmCartMag(&r_PC, &h);
tp_debug_print("center_dist = %f\n", h);
double T_final = h - param->R_plan;
if (T_final > param->tolerance) {
tp_debug_print("Projected circle T (%f) exceeds tolerance %f, aborting blend arc\n",
T_final,
param->tolerance);
return TP_ERR_FAIL;
}
tp_debug_print("T_final = %f\n",T_final);
points->trim1 = findTrimAngle(&geom->P,
&points->arc_center,
&geom->center1);
points->trim2 = findTrimAngle(&geom->P,
&points->arc_center,
&geom->center2);
tp_debug_print("trim1 = %f, trim2 = %f\n",
points->trim1,
points->trim2);
return TP_ERR_OK;
}
int arcFromBlendPoints3(SphericalArc * const arc, BlendPoints3 const * const points,
BlendGeom3 const * const geom, BlendParameters const * const param)
{
arc->uTan = geom->u_tan1;
arc->line_length = param->line_length;
arc->binormal = geom->binormal;
return arcInitFromPoints(arc, &points->arc_start,
&points->arc_end, &points->arc_center);
}
int blendGeom3Print(BlendGeom3 const * const geom)
{
tp_debug_print("u1 = %f %f %f\n",
geom->u1.x,
geom->u1.y,
geom->u1.z);
tp_debug_print("u2 = %f %f %f\n",
geom->u2.x,
geom->u2.y,
geom->u2.z);
return 0;
}
int blendPoints3Print(BlendPoints3 const * const points)
{
tp_debug_print("arc_start = %f %f %f\n",
points->arc_start.x,
points->arc_start.y,
points->arc_start.z);
tp_debug_print("arc_center = %f %f %f\n",
points->arc_center.x,
points->arc_center.y,
points->arc_center.z);
tp_debug_print("arc_end = %f %f %f\n",
points->arc_end.x,
points->arc_end.y,
points->arc_end.z);
return 0;
}
double pmCartAbsMax(PmCartesian const * const v)
{
return fmax(fmax(fabs(v->x),fabs(v->y)),fabs(v->z));
}
PmCircleLimits pmCircleActualMaxVel(PmCircle const * circle,
double v_max,
double a_max)
{
double a_n_max_cutoff = BLEND_ACC_RATIO_NORMAL * a_max;
double eff_radius = pmCircleEffectiveMinRadius(circle);
double a_n_vmax = pmSq(v_max) / fmax(eff_radius, DOUBLE_FUZZ);
double v_max_cutoff = pmSqrt(a_n_max_cutoff * eff_radius);
double v_max_actual = v_max;
double acc_ratio_tan = BLEND_ACC_RATIO_TANGENTIAL;
if (a_n_vmax > a_n_max_cutoff) {
v_max_actual = v_max_cutoff;
} else {
acc_ratio_tan = pmSqrt(1.0 - pmSq(a_n_vmax / a_max));
}
tp_debug_json_start(pmCircleActualMaxVel);
tp_debug_json_double(eff_radius);
tp_debug_json_double(v_max);
tp_debug_json_double(v_max_cutoff);
tp_debug_json_double(a_n_max_cutoff);
tp_debug_json_double(a_n_vmax);
tp_debug_json_double(acc_ratio_tan);
tp_debug_json_end();
PmCircleLimits limits = {
v_max_actual,
acc_ratio_tan
};
return limits;
}
static int pmCircleAngleFromParam(PmCircle const * const circle,
SpiralArcLengthFit const * const fit,
double t,
double * const angle)
{
if (fit->spiral_in) {
t = 1.0 - t;
}
double s_in = t * fit->total_planar_length;
double A = fit->b0;
double B = fit->b1;
double C = -s_in;
double disc = pmSq(B) - 4.0 * A * C ;
if (disc < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "discriminant %f is negative in angle calculation\n",disc);
return TP_ERR_FAIL;
}
double angle_out = (2.0 * C) / ( -B - pmSqrt(disc));
if (fit->spiral_in) {
angle_out = circle->angle - angle_out;
}
*angle = angle_out;
return TP_ERR_OK;
}
static void printSpiralArcLengthFit(SpiralArcLengthFit const * const fit)
{
tp_debug_print("Spiral fit: b0 = %.12f, b1 = %.12f, length = %.12f, spiral_in = %d\n",
fit->b0,
fit->b1,
fit->total_planar_length,
fit->spiral_in);
}
int findSpiralArcLengthFit(PmCircle const * const circle,
SpiralArcLengthFit * const fit)
{
double spiral_coef = circle->spiral / circle->angle;
double min_radius = circle->radius;
if (fsign(circle->spiral) < 0.0) {
spiral_coef*=-1.0;
min_radius+=circle->spiral;
fit->spiral_in = true;
} else {
fit->spiral_in = false;
}
tp_debug_print("radius = %.12f, angle = %.12f\n", min_radius, circle->angle);
tp_debug_print("spiral_coef = %.12f\n", spiral_coef);
double slope_start = pmSqrt(pmSq(min_radius) + pmSq(spiral_coef));
double slope_end = pmSqrt(pmSq(min_radius + spiral_coef * circle->angle) + pmSq(spiral_coef));
fit->b0 = (slope_end - slope_start) / (2.0 * circle->angle);
fit->b1 = slope_start;
fit->total_planar_length = fit->b0 * pmSq(circle->angle) + fit->b1 * circle->angle;
printSpiralArcLengthFit(fit);
double angle_end_chk = 0.0;
int res_angle = pmCircleAngleFromParam(circle, fit, 1.0, &angle_end_chk);
if (res_angle != TP_ERR_OK) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Spiral fit failed\n");
return TP_ERR_FAIL;
}
double fit_err = angle_end_chk - circle->angle;
if (fabs(fit_err) > TP_ANGLE_EPSILON) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Spiral fit angle difference is %e, maximum allowed is %e\n",
fit_err,
TP_ANGLE_EPSILON);
return TP_ERR_FAIL;
}
return TP_ERR_OK;
}
int pmCircleAngleFromProgress(PmCircle const * const circle,
SpiralArcLengthFit const * const fit,
double progress,
double * const angle)
{
double h2;
pmCartMagSq(&circle->rHelix, &h2);
double s_end = pmSqrt(pmSq(fit->total_planar_length) + h2);
double t = progress / s_end;
return pmCircleAngleFromParam(circle, fit, t, angle);
}
double pmCircleEffectiveMinRadius(PmCircle const * circle)
{
double dr = circle->spiral / circle->angle;
double h2;
pmCartMagSq(&circle->rHelix, &h2);
double n_inner = pmSq(dr) + pmSq(circle->radius);
double den = n_inner+pmSq(dr);
double num = pmSqrt(n_inner * n_inner * n_inner);
double r_spiral = num / den;
double effective_radius = h2 / r_spiral + r_spiral;
return effective_radius;
}