食用说明:1、c语言,软件VS2022。2、小型作业题分享,没有经过严谨测试。3、程序采用文件输入输出格式,输入文件格式为:点数\n缓冲区半径\n点坐标xi yi。4、分享经验+吐槽并发进行,攻击性较强,谁都攻击
0.编译预处理命令
(本来觉得不会有人这个都不会吧,但是感觉我自己都会忘)
#include<stdio.h>
#include<math.h>
#include<stdbool.h>
#define theta 30 //中间点的缓冲区角每次变化的角度
两个准备函数
方向角函数azimuth,通过求斜率和tan的反三角函数得到两点确定的直线的方向角。
判断凹凸性的布尔函数aotu,由于本程序全程只考虑逆时针求解,所以向量积为正时直接判断为凸角,不讨论所得角度矢量的方向问题。
double azimuth(double x1, double y1, double x2, double y2)//求方向角的函数
{
double k; double pi = atan(1) * 4;
k = (y2 - y1) / (x2 - x1);
return(atan(k)*180/pi);//弧度转为角度
}
bool aotu(double x1, double y1, double x2, double y2, double x3, double y3)
{//判断凹凸性,凹用平行线,凸用凸角圆弧法
double s = 0;//矢量的叉积
s = (x2 - x1)*(y3 - y2) - (x3 - x2)*(y2 - y1);
//统一逆时针开始走(线段的右侧)
if (s > 0)
return true;//若右侧为凸角,凸角丁真
else if (s < 0)
return false;
else return false;
}
2.Buffer函数
该函数用于求解缓冲区点,共分为三个部分:起点缓冲区,中间点缓冲区(分凹角和凸角讨论),终点缓冲区。
起点缓冲区与终点缓冲区方法一致,即根据缓冲半径和方向角联合求解;中间点缓冲区需使用aotu函数分凹角和凸角进行不同情况讨论:凹角仅求缓冲区平行线交点,根据方程式求解即可;凸角需在在凸起处形成缓冲区圆弧,具体为先根据方程解出圆弧起点和终点,再在变化角度小于起点终点所成角度的条件下,根据缓冲区半径和变化角度求出圆弧上的点。在求解过程中,原始点(输入点)和缓冲区点(输出点)应属于两套循环体系,程序里使用i控制缓冲区点,j控制原始点。
需要注意的是,由于本程序仅讨论逆时针,导致在一次从原始起点到原始终点的遍历过程中只能得到单边(向量右边)的缓冲区,所以需要再从原始终点到原始起点循环一次,才能得到双边结果。
在程序中对缓冲区点数进行记录,最后该函数返回缓冲区点数。
(虽然秃了但是毕竟写出来了,所以很得意地写了一大串感想。另外因为没有安全感所以经常写一步测试一步,注释掉的就是测试中间变量的,嫌冗长直接删了就好)
(beg是起点begin的缩写,不是乞讨的beg,我才没有第一次接触这个缩写时以为它是乞讨)
int buffer(double r, int n ,double *x,double* y, double *A,double* B)
{//r:缓冲区半径bufradus,n:点数,x1和Y1:数组点坐标,AB为缓冲区的点集合
// 缓冲区点用的i 原线段用的j
double azimuthangle[100];//每个原始点的方位角(最后一个点木有)
int j = 0; int i = 0;
for (j = 0; j < n - 1; j++)
azimuthangle[j]= azimuth(x[j], y[j], x[j+1], y[j+1]);//第j个点的方位角
for ( i = 0; i < 7; i++)
{
double ang = azimuthangle[0] + 90 + i * theta;
double setx = r * cos(ang);
double sety = r * sin(ang);
A[i] = x[0]+setx; B[i] = y[0]+sety;
}
int i1 = i;//记录一下
第1轮逆时针
for ( j = 1; j <= n-2; j++)//非首尾点进j循环,i留给缓冲区点
{
bool istu = true;
istu = aotu(x[j - 1], y[j - 1], x[j], x[j], x[j + 1], y[j + 1]);
if (istu == true)//(凸角处)
{//统一逆时针方向弥合(轴线右侧)
double xbeg, ybeg, xend, yend; double anglebeg, angleend;
if (x[j] != x[j - 1])//分两种情况讨论圆弧起点位置
{
ybeg = y[j] - r * (x[j] - x[j - 1]) / sqrt(pow(x[j] - x[j - 1], 2) + pow(y[j] - y[j - 1], 2));
xbeg = x[j] - (ybeg - y[j]) * (y[j] - y[j - 1]) / (x[j] - x[j - 1]);
yend = y[j] - r * (x[j + 1] - x[j]) / sqrt(pow(x[j] - x[j + 1], 2) + pow(y[j] - y[j + 1], 2));
xend = x[j] - (yend - y[j]) * (y[j] - y[j + 1]) / (x[j] - x[j + 1]);
}
else if(x[j] == x[j - 1])
{
ybeg = y[j]; xbeg = x[j] + r;
yend = y[j] - r * (x[j + 1] - x[j]) / sqrt(pow(x[j] - x[j + 1], 2) + pow(y[j] - y[j + 1], 2));
xend = x[j] - (yend - y[j]) * (y[j] - y[j + 1]) / (x[j] - x[j + 1]);
}
/*printf("\n圆弧的起点:%lf,%lf 终点:%lf,%lf\n", xbeg, ybeg, xend, yend);
printf("\nx[j]:%lf, y[j]:%lf\n", x[j],y[j]);*/
anglebeg = azimuth( x[j],y[j], xbeg, ybeg);
angleend = azimuth(x[j], y[j], xend, yend);
//printf("\n起点方向角:%lf, 终点方向角:%lf\n", anglebeg,angleend);
double angle=anglebeg,angledelta = angleend - anglebeg; //angle:角度变量,angledelta:起点终点角度差
int k = 0;
while ((angle + theta) < angleend)
{
angle = angle + theta;//theta为每次加的角度常量
double setx = r * cos(angle);
double sety = r * sin(angle);
A[i] = setx + x[j]; B[i] = sety + y[j];
i++;
//printf("\n%lf,%lf,%lf\n", setx, sety,angledelta);
}
}
if (istu == false)//凹角处的点(平行线)
{
double xp = r * (cos(azimuthangle[j - 1]) + cos(azimuthangle[j])) / sin(azimuthangle[j] - azimuthangle[j - 1]);
double yp = r * (sin(azimuthangle[j - 1]) + sin(azimuthangle[j]))/ sin(azimuthangle[j] - azimuthangle[j - 1]);
A[i] = x[j] - xp;
B[i] = y[j] - yp;
i++;
//printf("看看I:%d\n", i);
//printf("看看方向角:%lf,%lf\n", azimuthangle[j-1],azimuthangle[j]);
}
}
int i2 = i;
//第2轮逆时针,即从终点到起点
for (j = n - 2; j >= 1; j--)//非首尾点进j循环,i留给缓冲区点
{
bool istu = true;
istu = aotu(x[j - 1], y[j - 1], x[j], x[j], x[j + 1], y[j + 1]);
if (istu == true)//(凸角处)
{//统一逆时针方向弥合(轴线右侧)
double xbeg, ybeg, xend, yend; double anglebeg, angleend;
if (x[j] != x[j - 1])//分两种情况讨论圆弧起点位置
{
ybeg = y[j] - r * (x[j] - x[j - 1]) / sqrt(pow(x[j] - x[j - 1], 2) + pow(y[j] - y[j - 1], 2));
xbeg = x[j] - (ybeg - y[j]) * (y[j] - y[j - 1]) / (x[j] - x[j - 1]);
yend = y[j] - r * (x[j + 1] - x[j]) / sqrt(pow(x[j] - x[j + 1], 2) + pow(y[j] - y[j + 1], 2));
xend = x[j] - (yend - y[j]) * (y[j] - y[j + 1]) / (x[j] - x[j + 1]);
}
else if (x[j] == x[j - 1])
{
ybeg = y[j]; xbeg = x[j] + r;
yend = y[j] - r * (x[j + 1] - x[j]) / sqrt(pow(x[j] - x[j + 1], 2) + pow(y[j] - y[j + 1], 2));
xend = x[j] - (yend - y[j]) * (y[j] - y[j + 1]) / (x[j] - x[j + 1]);
}
//printf("\n圆弧的起点:%lf,%lf 终点:%lf,%lf\n", xbeg, ybeg, xend, yend);
//printf("\nx[j]:%lf, y[j]:%lf\n", x[j], y[j]);
anglebeg = azimuth(x[j], y[j], xbeg, ybeg);
angleend = azimuth(x[j], y[j], xend, yend);
//printf("\n起点方向角:%lf, 终点方向角:%lf\n", anglebeg, angleend);
double angle = anglebeg, angledelta = angleend - anglebeg; //angle:角度变量,angledelta:起点终点角度差
int k = 0;
while ((angle + theta) < angleend)
{
angle = angle + theta;
double setx = r * cos(angle);
double sety = r * sin(angle);
A[i] = setx + x[j]; B[i] = sety + y[j];
i++;
//printf("\n%lf,%lf,%lf\n", setx, sety, angledelta);
}
}
if (istu == false)//凹角处的点(平行线)
{
double xp = r * (cos(azimuthangle[j - 1]) + cos(azimuthangle[j])) / sin(azimuthangle[j] - azimuthangle[j - 1]);
double yp = r * (sin(azimuthangle[j - 1]) + sin(azimuthangle[j])) / sin(azimuthangle[j] - azimuthangle[j - 1]);
A[i] = x[j] - xp;
B[i] = y[j] - yp;
i++;
/*printf("看看I:%d\n", i);*/
/* printf("看看方向角:%lf,%lf\n", azimuthangle[j - 1], azimuthangle[j]);*/
}
}
int i3 = i;
for (i; i < i3+7; i++)
{
double ang = azimuthangle[n-2] + 90 + i * theta;
double setx = r * cos(ang);
double sety = r * sin(ang);
A[i] = x[n-1]+setx; B[i] = y[n-1]+sety;
}
//printf("第1、2、3个断点时有多少个缓冲区点:%d,%d,%d\n", i1,i2,i3);
return i;
}
3.主函数
使用fp1和fp2两种参数对”polyline.in”和“polyline.out”两个文件分别进行输入输出,从fp1中读入点数、缓冲区半径、点坐标的信息,使用Buffer函数得到缓冲区点数和坐标,从fp2中输出。
这个文件输入输出记下来以后涉及文件基本可以通用了(也许)。
(最简单的往往是主程序)
int main()
{
int n = 0, i = 0; FILE* fp1, * fp2; double bufradus = 0.0; int sum=0;
errno_t err1, err2; double x1[100], y1[100];
double bufferpointx[1000], bufferpointy[1000];
double pi = atan(1) * 4;//pi
double azimuthtangle;
//读入
printf("文件输入格式:\n点数\n缓冲区半径\n点坐标xi yi,\n");
err1 = fopen_s(&fp1, "polyline.in", "r");
if (err1)
printf("输入文件打开失败!\n");
else
printf("输入文件打开成功!\n");
err2 = fopen_s(&fp2, "polyline.out", "w");
if (err2)
printf("输出文件打开失败!");
else
printf("输出文件打开成功!\n");
printf("\n");
fscanf_s(fp1, "%d", &n);
fscanf_s(fp1, "%lf", &bufradus);
for (i = 0; i < n; i++)
{
fscanf_s(fp1, "%lf %lf,", &x1[i], &y1[i]);
sum++;
}
//
int he = 0;
he = buffer(bufradus, n, x1, y1, bufferpointx, bufferpointy);
//测试第一个方向角
/*azimuthtangle = azimuth(x1[0], y1[0], x1[1], y1[1]);*/
printf("输入点数为:%d\n", n);
printf("共有%d个缓冲区点,详情见out文件\n", he);
//输出测试
//输出所有原始点坐标
//for (i = 0; i < n; i++)
// fprintf(fp2, "原始点坐标是:%lf,%lf\n", x1[i], y1[i]);
fprintf(fp2,"共有%d个缓冲区点\n", he);
fprintf(fp2, "\n");
// 输出缓冲区集合的点(首尾点版)
for (i = 0; i < he; i++)
fprintf(fp2,"第%d个点为:%lf,%lf\n",i+1, bufferpointx[i], bufferpointy[i]);
return 0;
}
4.输入输出
共测试两组数据,一组为自设,使用特殊角度值,方便进行人工验证;另一组使用提供的nanjing-polygon前60组数据,数据精度相对较高(所以我也检查不出来这个到底对不对)。
自设测试数据:
(提供的数据找不到附件上传入口……CSDN也太难用了,图片居中也没有,编辑也不习惯,看个文章还总是收积分)