123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /// @file
- /// API geomprocs.h
- /// @ingroup common_utils
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- /* geometric functions (e.g. on points and boxes) with application to, but
- * no specific dependence on graphs */
- #include "config.h"
- #include <assert.h>
- #include <common/geom.h>
- #include <common/geomprocs.h>
- #include <math.h>
- #include <stdbool.h>
- #include <util/unreachable.h>
- /*
- *--------------------------------------------------------------
- *
- * lineToBox --
- *
- * Determine whether a line lies entirely inside, entirely
- * outside, or overlapping a given rectangular area.
- *
- * Results:
- * -1 is returned if the line given by p and q
- * is entirely outside the rectangle given by b.
- * 0 is returned if the polygon overlaps the rectangle, and
- * 1 is returned if the polygon is entirely inside the rectangle.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- /* This code steals liberally from algorithms in tk/generic/tkTrig.c -- jce */
- int lineToBox(pointf p, pointf q, boxf b)
- {
- /*
- * First check the two points individually to see whether they
- * are inside the rectangle or not.
- */
- bool inside1 = INSIDE(p, b);
- bool inside2 = INSIDE(q, b);
- if (inside1 != inside2) {
- return 0;
- }
- if (inside1 && inside2) {
- return 1;
- }
- /*
- * Both points are outside the rectangle, but still need to check
- * for intersections between the line and the rectangle. Horizontal
- * and vertical lines are particularly easy, so handle them
- * separately.
- */
- if (p.x == q.x) {
- /*
- * Vertical line.
- */
- if (((p.y >= b.LL.y) ^ (q.y >= b.LL.y)) && BETWEEN(b.LL.x, p.x, b.UR.x)) {
- return 0;
- }
- } else if (p.y == q.y) {
- /*
- * Horizontal line.
- */
- if (((p.x >= b.LL.x) ^ (q.x >= b.LL.x)) && BETWEEN(b.LL.y, p.y, b.UR.y)) {
- return 0;
- }
- } else {
- double m, x, y;
- /*
- * Diagonal line. Compute slope of line and use
- * for intersection checks against each of the
- * sides of the rectangle: left, right, bottom, top.
- */
- m = (q.y - p.y)/(q.x - p.x);
- double low = fmin(p.x, q.x);
- double high = fmax(p.x, q.x);
- /*
- * Left edge.
- */
- y = p.y + (b.LL.x - p.x)*m;
- if (BETWEEN(low, b.LL.x, high) && BETWEEN(b.LL.y, y, b.UR.y)) {
- return 0;
- }
- /*
- * Right edge.
- */
- y += (b.UR.x - b.LL.x)*m;
- if (BETWEEN(b.LL.y, y, b.UR.y) && BETWEEN(low, b.UR.x, high)) {
- return 0;
- }
- /*
- * Bottom edge.
- */
- low = fmin(p.y, q.y);
- high = fmax(p.y, q.y);
- x = p.x + (b.LL.y - p.y)/m;
- if (BETWEEN(b.LL.x, x, b.UR.x) && BETWEEN(low, b.LL.y, high)) {
- return 0;
- }
- /*
- * Top edge.
- */
- x += (b.UR.y - b.LL.y)/m;
- if (BETWEEN(b.LL.x, x, b.UR.x) && BETWEEN(low, b.UR.y, high)) {
- return 0;
- }
- }
- return -1;
- }
- void rect2poly(pointf *p)
- {
- p[3].x = p[2].x = p[1].x;
- p[2].y = p[1].y;
- p[3].y = p[0].y;
- p[1].x = p[0].x;
- }
- pointf cwrotatepf(pointf p, int cwrot)
- {
- assert(cwrot == 0 || cwrot == 90 || cwrot == 180 || cwrot == 270);
- double x = p.x, y = p.y;
- switch (cwrot) {
- case 0:
- break;
- case 90:
- p.x = y;
- p.y = -x;
- break;
- case 180:
- p.x = x;
- p.y = -y;
- break;
- case 270:
- p.x = y;
- p.y = x;
- break;
- default:
- UNREACHABLE();
- }
- return p;
- }
- pointf ccwrotatepf(pointf p, int ccwrot)
- {
- assert(ccwrot == 0 || ccwrot == 90 || ccwrot == 180 || ccwrot == 270);
- double x = p.x, y = p.y;
- switch (ccwrot) {
- case 0:
- break;
- case 90:
- p.x = -y;
- p.y = x;
- break;
- case 180:
- p.x = x;
- p.y = -y;
- break;
- case 270:
- p.x = y;
- p.y = x;
- break;
- default:
- UNREACHABLE();
- }
- return p;
- }
- boxf flip_rec_boxf(boxf b, pointf p)
- {
- boxf r;
- /* flip box */
- r.UR.x = b.UR.y;
- r.UR.y = b.UR.x;
- r.LL.x = b.LL.y;
- r.LL.y = b.LL.x;
- /* move box */
- r.LL.x += p.x;
- r.LL.y += p.y;
- r.UR.x += p.x;
- r.UR.y += p.y;
- return r;
- }
- #define SMALL 0.0000000001
- /* ptToLine2:
- * Return distance from point p to line a-b squared.
- */
- double ptToLine2 (pointf a, pointf b, pointf p)
- {
- double dx = b.x-a.x;
- double dy = b.y-a.y;
- double a2 = (p.y-a.y)*dx - (p.x-a.x)*dy;
- a2 *= a2; /* square - ensures that it is positive */
- if (a2 < SMALL) return 0.; /* avoid 0/0 problems */
- return a2 / (dx*dx + dy*dy);
- }
- #define dot(v,w) (v.x*w.x+v.y*w.y)
- /* line_intersect:
- * Computes intersection of lines a-b and c-d, returning intersection
- * point in *p.
- * Returns 0 if no intersection (lines parallel), 1 otherwise.
- */
- int line_intersect (pointf a, pointf b, pointf c, pointf d, pointf* p)
- {
- pointf mv = sub_pointf(b,a);
- pointf lv = sub_pointf(d,c);
- pointf ln = perp (lv);
- double lc = -dot(ln,c);
- double dt = dot(ln,mv);
- if (fabs(dt) < SMALL) return 0;
- *p = sub_pointf(a,scale((dot(ln,a)+lc)/dt,mv));
- return 1;
- }
|