ngspice/src/frontend/plotting/clip.c

216 lines
5.8 KiB
C
Raw Normal View History

2000-04-27 22:03:57 +02:00
/**********
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1986 Wayne A. Christopyher, U. C. Berkeley CAD Group
Author: 1982 Giles Billingsley
**********/
/*
* Some routines to do clipping of polygons, etc to boxes. Most of this code
* was rescued from MFB:
* sccsid "@(#)mfbclip.c 1.2 12/21/83"
*/
#include <ngspice/ngspice.h>
#include <ngspice/cpdefs.h>
#include <ngspice/ftedefs.h>
2000-04-27 22:03:57 +02:00
#include "clip.h"
#define POLYGONBUFSIZE 512
/* XXX */
#define CODEMINX 1
#define CODEMINY 2
#define CODEMAXX 4
#define CODEMAXY 8
#define CODE(x,y,c) c = 0;\
if (x < l)\
c = CODEMINX;\
else if (x > r)\
c = CODEMAXX;\
if (y < b)\
c |= CODEMINY;\
else if (y > t)\
c |= CODEMAXY;
#define SWAPINT(a, b) { int xxxx = (a); (a) = (b); (b) = xxxx; }
/* clip_line will clip a line to a rectangular area. The returned
* value is 'TRUE' if the line is out of the AOI (therefore does not
* need to be displayed) and 'FALSE' if the line is in the AOI.
*/
bool
clip_line(int *pX1, int *pY1, int *pX2, int *pY2, int l, int b, int r, int t)
{
int x1 = *pX1;
int y1 = *pY1;
int x2 = *pX2;
int y2 = *pY2;
2001-02-09 09:18:35 +01:00
int x = 0, y = 0;
int c,c1,c2;
2000-04-27 22:03:57 +02:00
CODE(x1,y1,c1)
CODE(x2,y2,c2)
while (c1 || c2) {
if (c1 & c2)
return (TRUE); /* Line is invisible. */
if ((c = c1) == 0)
2000-04-27 22:03:57 +02:00
c = c2;
if (c & CODEMINX) {
y = y1+(y2-y1)*(l-x1)/(x2-x1);
x = l;
} else if (c & CODEMAXX) {
y = y1+(y2-y1)*(r-x1)/(x2-x1);
x = r;
} else if (c & CODEMINY) {
x = x1+(x2-x1)*(b-y1)/(y2-y1);
y = b;
} else if (c & CODEMAXY) {
x = x1+(x2-x1)*(t-y1)/(y2-y1);
y = t;
}
if (c == c1) {
x1 = x;
y1 = y;
CODE(x,y,c1)
} else {
x2 = x;
y2 = y;
CODE(x,y,c2)
}
}
*pX1 = x1;
*pY1 = y1;
*pX2 = x2;
*pY2 = y2;
return (FALSE); /* Line is at least partially visible.*/
}
/* This routine will clip a line to a circle, returning TRUE if the line
* is entirely outside the circle. Note that we have to be careful not
* to switch the points around, since in grid.c we need to know which is
* the outer point for putting the label on.
*/
bool
clip_to_circle(int *x1, int *y1, int *x2, int *y2, int cx, int cy, int rad)
{
double perplen, a, b, c;
double tx, ty, dt;
double dtheta;
double theta1, theta2, tt, alpha, beta, gamma;
bool flip = FALSE;
int i;
/* Get the angles between the origin and the endpoints. */
if ((*x1-cx) || (*y1-cy))
theta1 = atan2((double) *y1 - cy, (double) *x1 - cx);
else
theta1 = M_PI;
if ((*x2-cx) || (*y2-cy))
theta2 = atan2((double) *y2 - cy, (double) *x2 - cx);
else
theta2 = M_PI;
if (theta1 < 0.0)
theta1 = 2 * M_PI + theta1;
if (theta2 < 0.0)
theta2 = 2 * M_PI + theta2;
dtheta = theta2 - theta1;
if (dtheta > M_PI)
dtheta = dtheta - 2 * M_PI;
else if (dtheta < - M_PI)
dtheta = 2 * M_PI - dtheta;
/* Make sure that p1 is the first point */
if (dtheta < 0) {
tt = theta1;
theta1 = theta2;
theta2 = tt;
i = *x1;
*x1 = *x2;
*x2 = i;
i = *y1;
*y1 = *y2;
*y2 = i;
flip = TRUE;
dtheta = -dtheta;
}
/* Figure out the distances between the points */
a = sqrt((double) ((*x1 - cx) * (*x1 - cx) + (*y1 - cy) * (*y1 - cy)));
b = sqrt((double) ((*x2 - cx) * (*x2 - cx) + (*y2 - cy) * (*y2 - cy)));
c = sqrt((double) ((*x1 - *x2) * (*x1 - *x2) +
(*y1 - *y2) * (*y1 - *y2)));
/* We have three cases now -- either the midpoint of the line is
* closest to the origon, or point 1 or point 2 is. Actually the
* midpoint won't in general be the closest, but if a point besides
* one of the endpoints is closest, the midpoint will be closer than
* both endpoints.
*/
tx = (*x1 + *x2) / 2;
ty = (*y1 + *y2) / 2;
dt = sqrt((double) ((tx - cx) * (tx - cx) + (ty - cy) * (ty - cy)));
if ((dt < a) && (dt < b)) {
/* This is wierd -- round-off errors I guess. */
tt = (a * a + c * c - b * b) / (2 * a * c);
if (tt > 1.0)
tt = 1.0;
else if (tt < -1.0)
tt = -1.0;
alpha = acos(tt);
perplen = a * sin(alpha);
} else if (a < b) {
perplen = a;
} else {
perplen = b;
}
/* Now we should see if the line is outside of the circle */
if (perplen >= rad)
return (TRUE);
/* It's at least partially inside */
if (a > rad) {
tt = (a * a + c * c - b * b) / (2 * a * c);
if (tt > 1.0)
tt = 1.0;
else if (tt < -1.0)
tt = -1.0;
alpha = acos(tt);
gamma = asin(sin(alpha) * a / rad);
if (gamma < M_PI / 2)
gamma = M_PI - gamma;
beta = M_PI - alpha - gamma;
2011-06-11 19:07:38 +02:00
*x1 = (int)(cx + rad * cos(theta1 + beta));
*y1 = (int)(cy + rad * sin(theta1 + beta));
2000-04-27 22:03:57 +02:00
}
if (b > rad) {
tt = (c * c + b * b - a * a) / (2 * b * c);
if (tt > 1.0)
tt = 1.0;
else if (tt < -1.0)
tt = -1.0;
alpha = acos(tt);
gamma = asin(sin(alpha) * b / rad);
if (gamma < M_PI / 2)
gamma = M_PI - gamma;
beta = M_PI - alpha - gamma;
2011-06-11 19:07:38 +02:00
*x2 = (int)(cx + rad * cos(theta2 - beta));
*y2 = (int)(cy + rad * sin(theta2 - beta));
2000-04-27 22:03:57 +02:00
}
if (flip) {
i = *x1;
*x1 = *x2;
*x2 = i;
i = *y1;
*y1 = *y2;
*y2 = i;
}
return (FALSE);
}