///////////////////////////////////////////////////////////////////////////// // Name: crvgeomtry.cpp // Purpose: // Author: Cesar Mauri Loba (cesar at crea-si dot com) // Modified by: // Created: 22/02/2008 // Copyright: (C) 2008 Cesar Mauri Loba - CREA Software Systems // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ///////////////////////////////////////////////////////////////////////////// #include "crvgeomtry.h" #include /* troba el punt mig d'un segment */ inline void segment_mean_point (tRPoint *p1, tRPoint *p2, tRPoint *mp) { mp->x= (p1->x + p2->x) / 2.0; mp->y= (p1->y + p2->y) / 2.0; } /* distancia entre dos punts */ inline Real points_distance (tRPoint *p1, tRPoint *p2) { Real xd= p2->x - p1->x, yd= p2->y - p1->y; return sqrt(xd*xd + yd*yd); } /* distància entre 2 punts 3D */ Real points3d_distance (t3DRPoint *p1, t3DRPoint * p2) { Real xd, yd, zd; xd= p1->x - p2->x; yd= p1->y - p2->y; zd= p1->z - p2->z; return sqrt (xd*xd + yd*yd + zd*zd); } /* equació de la recta que passa per dos punts y= mx + n */ void points2rect (tRPoint *p1, tRPoint *p2, tRect *r) { Real v1, v2; v1= p2->y - p1->y; v2= p2->x - p1->x; if (v2!= 0.0) { r->m= v1 / v2; r->n= p1->y - (r->m * p1->x); } else { r->m= INF_M; r->n= p1->x; } } /* distància d'una recta a un punt */ Real point_rect_distance (tRect *r, tRPoint *p) { tRect nr; tRPoint np; if (r->m>= INF_M) return fabs(r->n - p->x); /* pendent infinita */ if (r->m== 0.0) return fabs(p->y - r->n); /* pendent 0 */ /* perpendicular a r que passa per p */ nr.m = -1.0 / r->m; nr.n = p->y - (nr.m * p->x); /* punt de tall */ np.x = (r->n - nr.n) / (nr.m - r->m); np.y = r->m * np.x + r->n; return points_distance (p, &np); } /* calcula l'angle del vector que va de p1 a p2 */ Real segment_angle (tRPoint *p1, tRPoint *p2) { Real dx, dy; dx= p2->x - p1->x; dy= p2->y - p1->y; if (dx== 0.0) { if (dy>= 0.0) return (M_PI / 2.0); else return (3.0 * M_PI / 2.0); } if (dx> 0.0) { if (dy< 0.0) return (2.0 * M_PI) + atan(dy / dx); else return atan(dy / dx); } else return atan(dy / dx) + M_PI; // return 0.0; /* evitar warning */ } /* donada una recta diu si un punt es troba sobre la recta (0) o a la banda positiva (1) o negativa de la recta (-1) */ int point_rect_where (tRect *r, tRPoint *p, Real error) { Real result; /* si la recta es vertical només cal mirar la coordenada X */ if (r->m>= INF_M) result= p->x - r->n; /* sinó s'ha de substituir el punt a l'equació de la recta i avaluar-ne el resultat */ else result= r->m * p->x + r->n - p->y; if (fabs(result)<= error) return 0; else if (result> 0.0) return 1; else return -1; } /* donat un segment retorna si un determinat punt es troba a la dreta (1), sobre el segment (0) o a l'esquerra (-1). La posició es mira respecte la direcció del vector entre p1 i p2 */ int point_segment_where (tRPoint *p1, tRPoint *p2, tRPoint *p, Real error) { Real vx= p2->x - p1->x, vy= p2->y - p1->y; Real ang= atan2 (vx, vy); tRect r; points2rect (p1, p2, &r); int rectWhere= point_rect_where (&r, p, error); if (r.m>= INF_M) { if (vy<= 0) return rectWhere; else return -rectWhere; } if (rectWhere== 0) return 0; if (ang> 0) return -rectWhere; else return rectWhere; } /* calcula la recta perpendicular a una donada que passa per un punt */ void perpendicular_rect (tRect *r, tRPoint *p, tRect *perp) { if (r->m>= INF_M) { /* pendent infinita */ perp->m= 0.0; perp->n= p->y; } else if (r->m== 0.0) { /* pendent 0 */ perp->m= INF_M; perp->n= p->x; } else { /* perpendicular a r que passa per p */ perp->m = -1.0 / r->m; perp->n = p->y - (perp->m * p->x); } } /* troba el punt de tall entre dues rectes. retorna 0 si no hi ha punt de tall, 1 altrament */ int rects_cutting_point (tRect *r1, tRect *r2, tRPoint *p) { if (r1->m== r2->m) return 0; if (r1->m>= INF_M && r2->m>= INF_M) return 0; if (r1->m>= INF_M) { p->x= r1->n; p->y = r2->m * p->x + r2->n; return 1; } if (r2->m>= INF_M) { p->x= r2->n; p->y = r1->m * p->x + r1->n; return 1; } p->x = (r1->n - r2->n) / (r2->m - r1->m); p->y = r1->m * p->x + r1->n; return 1; } /* determina si un punt que pertany a la recta que passa per un segment es troba dins els límits del segment */ static inline int point_rect_on_segment (tRPoint *p1, tRPoint *p2, tRPoint *ptest) { tRPoint smin, smax; /* caixa englobant del segment */ if (p1->x< p2->x) { smin.x= p1->x; smax.x= p2->x; } else { smin.x= p2->x; smax.x= p1->x; } if (p1->y< p2->y) { smin.y= p1->y; smax.y= p2->y; } else { smin.y= p2->y; smax.y= p1->y; } if (ptest->x> smax.x || ptest->x< smin.x || ptest->y> smax.y || ptest->y< smin.y) return 0; return 1; } /* diu si dos segments es tallen en algun punt */ int segments_cutting (tRPoint *s1p1, tRPoint *s1p2, tRPoint *s2p1, tRPoint *s2p2) { tRect r1, r2; tRPoint pcut; /* obté equacions de les rectes dels segments */ points2rect (s1p1, s1p2, &r1); points2rect (s2p1, s2p2, &r2); /* punt de tall de les dues rectes */ if (rects_cutting_point (&r1, &r2, &pcut)== 0) return 0; /* comprova si el punt de tall pertany als dos segments */ return (point_rect_on_segment (s1p1, s1p2, &pcut) && point_rect_on_segment (s2p1, s2p2, &pcut) ); } /* calcula el centre d'una circumferència a partir de 3 punts del perímetre. retorna 0 si no es possible determinar el center, 1 si tot correcte */ int circle_center (tRPoint *p1, tRPoint *p2, tRPoint *p3, tRPoint *center) { tRect r1, r2, pr1, pr2; tRPoint mp1, mp2; points2rect (p1, p2, &r1); points2rect (p2, p3, &r2); /* punts mitjos de les rectes */ segment_mean_point (p1, p2, &mp1); segment_mean_point (p2, p3, &mp2); /* rectes perpendiculars que passen per aquestos punts */ perpendicular_rect (&r1, &mp1, &pr1); perpendicular_rect (&r2, &mp2, &pr2); /* punt de tall de les rectes anteriors */ return rects_cutting_point (&pr1, &pr2, center); } /* donat un punt el rota l'angle indicat respecte del centre */ void point_rotate (tRPoint *p, tRPoint *center, Real ang) { Real beta, dist; beta= segment_angle (center, p) + ang; dist= points_distance (center, p); p->x= center->x + dist * cos (beta); p->y= center->y + dist * sin (beta); } /* escalat d'un segment. es modifica el punt *target per tal que el segment que va de *p a *target tingui la nova distància indicada */ void scale_segment (tRPoint *target, tRPoint *p, Real real_dist) { Real modul; Real dx, dy; modul= points_distance (target, p); if (modul== 0.0) return; dx= (target->x - p->x) / modul; dy= (target->y - p->y) / modul; target->x= p->x + dx * real_dist; target->y= p->y + dy * real_dist; } /* reescala un segment modificant la posició dels 2 extrems per a que tingui la mida desitjada */ void rescale_segment (tRPoint *p1, tRPoint *p2, Real real_dist) { Real modul; Real dx, dy; modul= points_distance (p1, p2); if (modul== 0.0) return; dx= (p2->x - p1->x) / modul; dy= (p2->y - p1->y) / modul; dx*= (real_dist - modul) / 2.0; dy*= (real_dist - modul) / 2.0; p1->x-= dx; p1->y-= dy; p2->x+= dx; p2->y+= dy; } /* donada una llista de punts que formen un polígon tancat en calcula la seva caixa englobant */ void calculate_englobing_box (tRPoint *lvertexs, int nvertexs, tRPoint *min_box, tRPoint *max_box) { int i; min_box->x= max_box->x= lvertexs[0].x; min_box->y= max_box->y= lvertexs[0].y; for (i= 1; i< nvertexs; i++) { if (lvertexs[i].x> max_box->x) max_box->x= lvertexs[i].x; else if (lvertexs[i].x< min_box->x) min_box->x= lvertexs[i].x; if (lvertexs[i].y> max_box->y) max_box->y= lvertexs[i].y; else if (lvertexs[i].y< min_box->y) min_box->y= lvertexs[i].y; } } /* comprova si dos polígons tancats tenen alguna àrea solapada. NOTA: no es comprova si un polígon és interior a un altre */ int polys_intersect (tRPoint *v1, int lv1, tRPoint *v2, int lv2) { int i, j; for (i= 0; i< lv1; i++) for (j= 0; j< lv2; j++) { if (segments_cutting (&v1[i], &v1[(i + 1) % lv1], &v2[j], &v2[(j + 1) % lv2])) return 1; } return 0; }