/*==============================================================================
cheungmine - All rights reserved
April 5, 2009
==============================================================================*/
#include <stdio.h>
#include <float.h>
#include <assert.h>
#include <math.h>
#ifndef CG_BIPI
#define CG_BIPI 6.28318530717958647692
#endif
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif
#ifndef INOUT
#define INOUT
#endif
/* Vertex structure */
typedef struct
{
double x, y;
} cg_vertex_t;
/**
* CG_vertex_get_dist_to_line
* 计算点到直线的距离和垂足ft
*/
double CG_vertex_get_dist_to_line (
IN const cg_vertex_t *pt,
IN const cg_vertex_t *start,
IN const cg_vertex_t *end,
OUT cg_vertex_t *ft)
{
double a1 = pt->x - start->x;
double a2 = pt->y - start->y;
double s = sqrt(a1*a1 + a2*a2);
a1 = atan2(a2, a1);
if(a1 < 0) a1 += CG_BIPI;
a2 = atan2((end->y-start->y), (end->x-start->x));
if(a2 < 0) a2 += CG_BIPI;
a1 = fabs(sin(a1-a2)) * s;
if (ft){
s *= cos(a1-a2);
ft->x = start->x + s*cos(a2);
ft->y = start->y + s*sin(a2);
}
return a1;
}
/*===========================================================================
Spline3 Functions
===========================================================================*/
/* 返回pl的点到直线[start,end]的最大距离点的索引imax和最大距离smax */
static int max_dist_at(
const cg_vertex_t* start,
const cg_vertex_t* end,
const cg_vertex_t* pl, int np,
double *smax)
{
int i, imax = 0;
double s;
*smax = 0;
// 计算全部点到直线的距离, 找出最大的点
for(i=0; i<np; i++){
s = CG_vertex_get_dist_to_line(&pl[i], start, end, 0);
if (s > *smax){
*smax = s;
imax = i;
}
}
return imax;
}
/* 递归重采样方法*/
static void resample_recursive(
const cg_vertex_t* start,
const cg_vertex_t* end,
const cg_vertex_t* pl,
int np,
cg_vertex_t *pout,
int *nout,
const double *dist)
{
double smax;
int imax;
if (np==0) return;
imax = max_dist_at(start, end, pl, np, &smax);
if (imax==0||imax==np-1) return;
if (smax<*dist) return;
resample_recursive( start, &pl[imax], pl, imax, pout, nout, dist);
pout[(*nout)++] = pl[imax];
resample_recursive( &pl[imax], end, &pl[imax+1], np-imax-1, pout, nout, dist);
}
/**
* CG_vertices_fit_resample
* 曲线重采样拟合, 返回拟合之后的pout数组的点数
*/
int CGAL_CALL CG_vertices_fit_resample (
IN const cg_vertex_t* start, /* 折线或多边形的起点*/
IN const cg_vertex_t* end, /* 折线或多边形的终点*/
IN const cg_vertex_t* mids, /* 折线或多边形的中间点数组*/
IN int nmid, /* 折线或多边形的中间点数组的点数*/
OUT cg_vertex_t* pout, /* 返回拟合后的中间点数组, 点数组由客户分配,
至少与输入的中间点数组的点数相同*/
IN double dist /* 拟合的最小距离, 必须指定>0的有效值*/
)
{
int nout = 0;
resample_recursive(start, end, mids, nmid, pout, &nout, &dist);
return nout;
}
/**
* CG_vertex_insert_pt_spline
* 计算张力样条插值点: p1, p2之间的点, 返回插值点数目
* p0-----p1........p2------p3
* 本程序为减少数据复制, 采用points数组返回插值点或拟合后的值,
* 因此调用者必须分配足够的空间以存储中间计算结果和返回的值
* 必须满足:
* size > (|p1,p2|/step)*2
* 如果设置的step太小, 系统则自动根据size/2来调整step, 确保
* 计算能正确进行
*/
int CGAL_CALL CG_vertex_insert_pt_spline (
IN double step, /* 插值的步长, 必须指定有效的步长*/
IN cg_vertex_t p0, /* 起点*/
IN cg_vertex_t p1, /* 插值段的起点*/
IN cg_vertex_t p2, /* 插值段的终点*/
IN cg_vertex_t p3, /* 终点*/
IN OUT cg_vertex_t *points, /* 返回的插值点数组, 数组由调用者分配*/
IN int size, /* 插值点数组的最大尺寸, 此尺寸必须足够容纳2倍的插值点数*/
IN double ratio, /* 张力系数[0,1], 最佳0.1666667. -1 为取默认.1666667 */
IN double dist /* 拟合的最小距离, 必须>0. <=0 不拟合*/
)
{
int i, count, at;
double x, y, ca, sa, s12, h1, h2, h3, u1, u2, v1, v3, d1, d2, D, M1, M2;
cg_vertex_t end = {p2.x, p2.y};
// 必须计算p1,p2距离, 确保不会溢出
d1 = p2.x-p1.x;
d2 = p2.y-p1.y;
s12 = sqrt(d1*d1 + d2*d2);
// p1,p2距离太小, 返回
if (dist>0 && s12 < dist)
return 0;
// 步距离太长, 计算p1与p2中点即可
if (step > s12/2 ){
points[0].x = (p2.x+p1.x)/2;
points[0].y = (p2.y+p1.y)/2;
return 1;
}
// 平移原点至p1
p0.x -= p1.x;
p0.y -= p1.y;
p2.x = d1;
p2.y = d2;
p3.x -= p1.x;
p3.y -= p1.y;
// 旋转至p1->p2为x轴方向
ca = p2.x / s12; // cos(a)
sa = p2.y / s12; // sin(a)
x = p0.x * ca + p0.y * sa;
p0.y = -p0.x * sa + p0.y * ca;
p0.x = x;
p2.x = s12;
p2.y = 0;
x = p3.x * ca + p3.y * sa;
p3.y = -p3.x * sa + p3.y * ca;
p3.x = x;
// 判断是否是单值函数
if ( p0.x * p2.x >= 0 || p2.x * (p2.x-p3.x) >= 0 )
return 0;
// 计算系数, 这里为清晰起见, 没有优化代码
h1 = -p0.x;
h2 = p2.x;
h3 = p3.x - p2.x;
u1 = h2/(h1+h2); // u1=p2.x/(p2.x-p0.x);
u2 = h2/(h2+h3); // u2=p2.x/(p3.x-p1.x);
v1 = -p0.y;
v3 = p3.y;
d1 = -6.0*p0.y/((h1+h2)*p0.x);
d2 = 6.0*p3.y/(h3*p3.x);
D = 4.0-u1*u2;
if(D < 0.001)
return 0;
M1 = (2*d1-u1*d2)/D;
M2 = (2*d2-u2*d1)/D;
// 计算插值点数, 如果点数超过size/2, 则自动增大step
count = (int) floor((s12+step/2)/step);
if (count > size/2){
count = size/2;
step=s12/(count+1);
}
// 张力系数, 最佳=0.16666667
if(ratio<0) ratio = 0.16666667;
x = 0.0;
y = 0.0;
at = (dist>0 && count<=size/2)? count:0;
for(i=0; i < count; i++){
x += step;
y = ratio*x*(M1*(h2-x)*(x-2*h2)+M2*(x-h2)*(x+h2))/h2;
points[at+i].x = x*ca - y*sa + p1.x;
points[at+i].y = x*sa + y*ca + p1.y;
}
// 需要重采样: p1 as start, p2 as end
if (dist>0)
count = CG_vertices_fit_resample(&p1, &end, points+at, count, points, dist);
return count;
}