C语言常用二维解析几何函数集源代码
这个代码是我今天放学后抽时间写的,选取了用于做动画或者游戏里较常用的数学函数(就是说不可能很全面)包括简单的碰撞检测计算
函数包括:
向量加减法,向量点乘与叉乘,向量缩放,向量长度
三角形面积,点到直线(和线段)的距离,
判断直线平行,判断线段相交,求直线(和线段)的交点,
点到直线的垂足,点关于直线的对称点,线段关于直线的反射线
点绕给定点旋转
判断点在三角形(和椭圆)内
直线(和线段)与三角形碰撞,三角形与三角形碰撞
两平行矩形碰撞,平行矩形与椭圆碰撞
程序代码:#include <math.h>
//定义点结构
typedef struct point
{
double x;
double y;
}point;
//定义有向线段
typedef struct segment
{
point s;
point e;
}segment;
//定义三角形
typedef struct triangle
{
point p[3];
}triangle;
//定义精度误差
const double eps = 1e-6;
//两点距离
double distance(const point *p1, const point *p2)
{
double dx = p1->x - p2->x, dy = p1->y - p2->y;
return sqrt(dx * dx + dy * dy);
}
//线段长
double len(const segment* seg)
{
double dx = seg->s.x - seg->e.x, dy = seg->s.y - seg->e.y;
return sqrt(dx * dx + dy * dy);
}
//线段长的平方
double lensqr(const segment* seg)
{
double dx = seg->s.x - seg->e.x, dy = seg->s.y - seg->e.y;
return dx * dx + dy * dy;
}
//向量加法
point add(const point *p1, const point *p2)
{
point ret;
ret.x = p1->x + p2->x;
ret.y = p1->y + p2->y;
return ret;
}
//向量减法
point minus(const point *p1, const point *p2)
{
point ret;
ret.x = p1->x - p2->x;
ret.y = p1->y - p2->y;
return ret;
}
//向量缩放
point scale(const point *p, double s)
{
point ret;
ret.x = p->x * s;
ret.y = p->y * s;
return ret;
}
//向量缩放(以o点为基准点)
point scale2(const point *p, const point *o, double s)
{
point v;
v.x = (p->x - o->x) * s;
v.y = (p->y - o->y) * s;
return add(&v, o);
}
//向量点乘
double dotmul(const point *p1, const point *p2)
{
return p1->x * p2->x + p1->y * p2->y;
}
//返回两点与原点组成的三角形面积的两倍(叉乘z值)
double multi(const point *p1, const point *p2)
{
return p1->x * p2->y - p1->y * p2->x;
}
//返回三点组成的三角形面积的两倍(有次序,相当于叉乘)
double multi3(point p1, point p2, point p3)
{
p2 = minus(&p2, &p1);
p3 = minus(&p3, &p1);
return multi(&p2, &p3);
}
//三角形面积
double triangleArea(point a, point b, point c)
{
return multi3(a, b, c) / 2;
}
//计算点到直线的距离,a,b定义那个直线,p为要计算的点
double pointToLine(point a, point b, point p)
{
a = minus(&a, &p);
b = minus(&b, &p);
return fabs(multi(&a, &b)) / distance(&a, &b);
}
//计算点到线段的距离,a,b定义那个线段,p为要计算的点
double pointToSeg(point a, point b, point p)
{
point ab, o = {0};
ab = minus(&b, &a);
a = minus(&a, &p);
b = minus(&b, &p);
if (dotmul(&a, &ab) > -eps) return distance(&o, &a);
ab.x = -ab.x; ab.y = -ab.y;
if (dotmul(&b, &ab) > -eps) return distance(&o, &b);
return fabs(multi(&a, &b)) / distance(&a, &b);
}
//判断两直线平行(重载1)
int isParallelP(const point* as, const point* ae, const point* bs, const point* be)
{
double d1, d2;
d1 = multi3(*as, *bs, *ae); d2 = multi3(*as, *ae, *be);
return fabs(d1 + d2) < eps;
}
//判断两直线平行(重载2)
int isParallelS(const segment* a, const segment* b)
{
double d1, d2;
d1 = multi3(a->s, b->s, a->e); d2 = multi3(a->s, a->e, b->e);
return fabs(d1 + d2) < eps;
}
//直线求交点(重载1)
point linesIntersectP(const point* as, const point* ae, const point* bs, const point* be)
{
point ret;
double d1, d2;
d1 = multi3(*as, *bs, *ae); d2 = multi3(*as, *ae, *be);
ret.x = (bs->x * d2 + be->x * d1) / (d1 + d2);
ret.y = (bs->y * d2 + be->y * d1) / (d1 + d2);
return ret;
}
//直线求交点(重载2)
point linesIntersectS(const segment* a, const segment* b)
{
point ret;
double d1, d2;
d1 = multi3(a->s, b->s, a->e); d2 = multi3(a->s, a->e, b->e);
ret.x = (b->s.x * d2 + b->e.x * d1) / (d1 + d2);
ret.y = (b->s.y * d2 + b->e.y * d1) / (d1 + d2);
return ret;
}
//点到直线的垂足
point pointToLineFoot(point a, point b, point p)
{
point t;
t = p;
t.x += a.y - b.y;
t.y += b.x - a.x;
return linesIntersectP(&a, &b, &t, &p);
}
//点关于直线的对称点
point mirrorPoint(point a, point b, point p)
{
point f;
double dis;
dis = pointToSeg(a, b, p);
if (dis < eps) return p;
f = pointToLineFoot(a, b, p);
f.x += f.x - p.x;
f.y += f.y - p.y;
return f;
}
//求对称线段或者反射线(返回点放回p,q)
void mirrorSeg(const point* a, const point* b, point* p, point* q)
{
*p = mirrorPoint(*a, *b, *p);
*q = mirrorPoint(*a, *b, *q);
}
//对p点以o点为中心逆时针旋转r弧度,计算结果返回p中(参数返回方式的重载)
void rotate(point* p, point o, double r)
{
point q;
double sr, cr;
q = minus(p, &o);
sr = sin(r); cr = cos(r);
p->x = o.x + (cr * q.x - sr * q.y);
p->y = o.y + (sr * q.x + cr * q.y);
}
//对p点以o点为中心逆时针旋转r弧度(直接返回点的重载)
point rotateR(point p, point o, double r)
{
point q;
double sr, cr;
q = minus(&p, &o);
sr = sin(r); cr = cos(r);
p.x = o.x + (cr * q.x - sr * q.y);
p.y = o.y + (sr * q.x + cr * q.y);
return p;
}
//判断两线段相交(相交返回1,不相交返回0)
int segCrashSegP(const point* as, const point* ae, const point* bs, const point* be)
{
double s, t;
s = multi3(*as, *bs, *be);
t = multi3(*ae, *bs, *be);
if (s * t > eps) return 0;
s = multi3(*bs, *as, *ae);
t = multi3(*be, *as, *ae);
if (s * t > eps) return 0;
return 1;
}
//判断两线段相交(相交返回1,不相交返回0)
int segCrashSegS(const segment* a, const segment* b)
{
double s, t;
s = multi3(a->s, b->s, b->e);
t = multi3(a->e, b->s, b->e);
if (s * t > eps) return 0;
s = multi3(b->s, a->s, a->e);
t = multi3(b->e, a->s, a->e);
if (s * t > eps) return 0;
return 1;
}
//判断点在三角形内(返回1:内;返回0:边上;返回-1:外)
int pointInTriangle(const triangle* tri, const point* p)
{
double s, t;
s = multi3(*p, tri->p[0], tri->p[1]);
if (fabs(s) < eps) return 0;
t = multi3(*p, tri->p[1], tri->p[2]);
if (fabs(t) < eps) return 0;
if ((s < 0) ^ (t < 0)) return -1;
s = multi3(*p, tri->p[2], tri->p[0]);
if (fabs(s) < eps) return 0;
if ((s < 0) ^ (t < 0)) return -1;
return 1;
}
//三角形与直线碰撞检测(碰撞返回1,不碰撞返回0)
int triangleCrashLine(const triangle* tri, const segment* seg)
{
double s, t;
s = multi3(tri->p[0], seg->s, seg->e);
t = multi3(tri->p[1], seg->s, seg->e);
if ((s < 0) ^ (t < 0)) return 1;
s = multi3(tri->p[2], seg->s, seg->e);
if ((s < 0) ^ (t < 0)) return 1;
return 0;
}
//三角形与线段碰撞检测(碰撞返回1,不碰撞返回0)
int triangleCrashSeg(const triangle* tri, const segment* seg)
{
if (pointInTriangle(tri, &seg->s) > 0) return 1;
if (segCrashSegP(&tri->p[0], &tri->p[1], &seg->s, &seg->e)) return 1;
if (segCrashSegP(&tri->p[0], &tri->p[2], &seg->s, &seg->e)) return 1;
if (segCrashSegP(&tri->p[2], &tri->p[1], &seg->s, &seg->e)) return 1;
return 0;
}
//三角形与三角形碰撞检测(碰撞返回1,不碰撞返回0)
int triangleCrashTriangle(const triangle* tri1, const triangle* tri2)
{
segment seg;
seg.s = tri2->p[0]; seg.e = tri2->p[1];
if (triangleCrashSeg(tri1, &seg)) return 1;
seg.s = tri2->p[0]; seg.e = tri2->p[2];
if (triangleCrashSeg(tri1, &seg)) return 1;
seg.s = tri2->p[2]; seg.e = tri2->p[1];
if (triangleCrashSeg(tri1, &seg)) return 1;
return 0;
}
//两平行于坐标轴的矩形碰撞检测(碰撞返回1,不碰撞返回0)
int rectCrashRect(const point* rect1lt, const point* rect1rb, const point* rect2lt, const point* rect2rb)
{
if (rect1lt->x > rect2rb->x || rect1lt->y > rect2rb->y) return 0;
if (rect2lt->x > rect1rb->x || rect2lt->y > rect1rb->y) return 0;
return 1;
}
//判断点在椭圆内(返回1:内;返回0:边上;返回-1:外)
int pointInEllipse(const point* elplt, const point* elprb, const point* pt)
{
point center, p;
double a, b, s;
center.x = (elplt->x + elprb->x) / 2; center.y = (elplt->y + elprb->y) / 2;
a = center.x - elplt->x; b = center.y - elplt->y;
a *= a; b *= b;
p = minus(pt, ¢er);
s = p.x * p.x / a + p.y * p.y / b;
if (fabs(s) < eps) return 0;
if (s > 1) return -1;
return 1;
}
//判断平行矩形与椭圆碰撞(碰撞返回1,不碰撞返回0)
int rectCrashEllipse(const point* rectlt, const point* rectrb, const point* elplt, const point* elprb)
{
if (rectCrashRect(rectlt, rectrb, elplt, elprb))
{
point p;
int xs, ys;
p.x = (elplt->x + elprb->x) / 2; p.y = (elplt->y + elprb->y) / 2;
if (p.x < rectlt->x) xs = -1;
else if (p.x < rectrb->x) xs = 0;
else xs = 1;
if (p.y < rectlt->y) ys = -1;
else if (p.y < rectrb->y) ys = 0;
else ys = 1;
if (xs * ys == 0) return 1;
if (xs < 0)
{
if (ys < 0) p = *elplt;
else p.x = elplt->x, p.y = elprb->y;
}
else
{
if (ys > 0) p = *elprb;
else p.x = elprb->x, p.y = elplt->y;
}
return pointInEllipse(elplt, elprb, &p) > 0;
}
return 0;
}
因为代码是我匆忙编写的,所以代码很可能还存在问题,如果发现请告诉我
但有一些函数,要你自己注意调用场合,比如求两直线的交点,最好先判断是否平行
求两线段的交点,先判断是否相交等等
另外,代码是未经优化的,在效率要求较高的场合可能不适用,但多数场合下的应用不会有什么问题
要注意的是,里面的旋转函数的坐标,用的是数学坐标,逆时针是指从x轴的正方向,向y轴的正方向旋转构成的角,这个角度可正可负










搬个凳子 看切磋 期待!