/* * Copyright (c) 2008 Hypertriton, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Routines related to circles. */ #include #include "m.h" M_Circle2 M_CircleRead2(AG_DataSource *ds) { M_Circle2 C; C.p = M_ReadVector2(ds); C.r = M_ReadReal(ds); return (C); } M_Circle3 M_CircleRead3(AG_DataSource *ds) { M_Circle3 C; C.p = M_ReadVector3(ds); C.r = M_ReadReal(ds); return (C); } void M_CircleWrite2(AG_DataSource *ds, M_Circle2 *C) { M_WriteVector2(ds, &C->p); M_WriteReal(ds, C->r); } void M_CircleWrite3(AG_DataSource *ds, M_Circle3 *C) { M_WriteVector3(ds, &C->p); M_WriteReal(ds, C->r); } /* Create a line from a point, direction vector and length. */ M_Circle2 M_CircleFromPt(M_Vector2 p, M_Real r) { M_Circle2 C; C.p = p; C.r = r; return (C); } /* Compute minimal distance from a circle to a point p. */ M_Real M_CirclePointDistance2(M_Circle2 C, M_Vector2 p) { M_Vector2 vR = M_VecSub2(p, C.p); M_Real theta = Atan2(vR.y, vR.x); return M_VecDistance2(p, M_VECTOR2(C.p.x + C.r*Cos(theta), C.p.y + C.r*Sin(theta))); } /* Compute the intersection of two circles. */ M_GeomSet2 M_IntersectCircleCircle2(M_Circle2 C1, M_Circle2 C2) { M_GeomSet2 Sint = M_GEOM_SET_EMPTY; M_Real d12 = M_VecDistance2(C1.p, C2.p); M_Real a, h, b; M_Vector2 p; M_Geom2 G1, G2; if (Fabs(C1.p.x - C2.p.x) <= M_MACHEP && Fabs(C1.p.x - C2.p.x) <= M_MACHEP && Fabs(C1.r - C2.r) <= M_MACHEP) { G1.type = M_CIRCLE; G1.g_circle = C1; M_GeomSetAdd2(&Sint, &G1); return (Sint); } if (d12 > (C1.r + C2.r) || d12 < Fabs(C1.r - C2.r)) { return (Sint); } a = (C1.r*C1.r - C2.r*C2.r + d12*d12) / (2.0*d12); h = Sqrt(C1.r*C1.r - a*a); p = M_VecLERP2(C1.p, C2.p, a/d12); b = h/d12; G1.type = M_POINT; G1.g_point.x = p.x - b*(C2.p.y - C1.p.y); G1.g_point.y = p.y + b*(C2.p.x - C1.p.x); G2.type = M_POINT; G2.g_point.x = p.x + b*(C2.p.y - C1.p.y); G2.g_point.y = p.y - b*(C2.p.x - C1.p.x); M_GeomSetAdd2(&Sint, &G1); if (M_VecDistance2(G1.g_point, G2.g_point) > M_MACHEP) { M_GeomSetAdd2(&Sint, &G2); } return (Sint); } /* Compute the intersection of a circle and a line. */ M_GeomSet2 M_IntersectCircleLine2(M_Circle2 C, M_Line2 L) { M_GeomSet2 Sint = M_GEOM_SET_EMPTY; M_Vector2 p1 = M_LineFirstPt2(L); M_Vector2 p2 = M_LineSecondPt2(L); M_Vector2 p3 = C.p; M_Real a, b, c, det; M_Geom2 G; a = (p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y)*(p2.y - p1.y); b = 2.0*( (p2.x - p1.x)*(p1.x - p3.x) + (p2.y - p1.y)*(p1.y - p3.y) ); c = p3.x*p3.x + p3.y*p3.y + p1.x*p1.x + p1.y*p1.y - 2.0*(p3.x*p1.x + p3.y*p1.y) - C.r*C.r; det = b*b - 4.0*a*c; if (det < 0.0) { return (Sint); } else if (det == 0.0) { /* TODO Tangent! */ return (Sint); } else { M_Real e = Sqrt(det); M_Real u1 = (-b + e) / (2.0*a); M_Real u2 = (-b - e) / (2.0*a); if ((u1 < 0.0 || u1 > 1.0) && (u2 < 0.0 || u2 > 1.0)) { if ((u1 < 0.0 && u2 < 0.0) || (u1 > 1.0 && u2 > 1.0)) { return (Sint); } else { if (u1 >= 0.0 && u1 <= 1.0) { G.type = M_POINT; G.g_point = M_VecLERP2(p1,p2,u1); M_GeomSetAdd2(&Sint, &G); } if (u2 >= 0.0 && u2 <= 1.0) { G.type = M_POINT; G.g_point = M_VecLERP2(p1,p2,u2); M_GeomSetAdd2(&Sint, &G); } } } else { if (u1 >= 0.0 && u1 <= 1.0) { G.type = M_POINT; G.g_point = M_VecLERP2(p1,p2,u1); M_GeomSetAdd2(&Sint, &G); } if (u2 >= 0.0 && u2 <= 1.0) { G.type = M_POINT; G.g_point = M_VecLERP2(p1,p2,u2); M_GeomSetAdd2(&Sint, &G); } } } return (Sint); }