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轴的正方向旋转构成的角,这个角度可正可负