4 * This module provides procedures to draw borders in
5 * the three-dimensional Motif style.
7 * Copyright 1990 Regents of the University of California.
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
18 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tk3d.c,v 1.30 92/06/15 14:28:18 ouster Exp $ SPRITE (Berkeley)";
25 * One of the following data structures is allocated for
26 * each 3-D border currently in use. Structures of this
27 * type are indexed by borderTable, so that a single
28 * structure can be shared for several uses.
32 Display
*display
; /* Display for which the resources
33 * below are allocated. */
34 int refCount
; /* Number of different users of
36 XColor
*bgColorPtr
; /* Background color (intensity
37 * between lightColorPtr and
39 XColor
*lightColorPtr
; /* Color used for lighter areas of
40 * border (must free this when
41 * deleting structure). */
42 XColor
*darkColorPtr
; /* Color for darker areas (must
43 * free when deleting structure). */
44 Pixmap shadow
; /* Stipple pattern to use for drawing
45 * lighter-shadow-ed areas. Only used on
46 * monochrome displays; on color displays
48 GC lightGC
; /* Used to draw lighter parts of
50 GC darkGC
; /* Used to draw darker parts of the
52 GC bgGC
; /* Used (if necessary) to draw areas in
53 * the background color. */
54 Tcl_HashEntry
*hashPtr
; /* Entry in borderTable (needed in
55 * order to delete structure). */
59 * Hash table to map from a border's values (color, etc.) to a
60 * Border structure for those values.
63 static Tcl_HashTable borderTable
;
65 Tk_Uid colorName
; /* Color for border. */
66 Colormap colormap
; /* Colormap used for allocating border
68 Screen
*screen
; /* Screen on which border will be drawn. */
72 * Maximum intensity for a color:
75 #define MAX_INTENSITY 65535
78 static int initialized
= 0; /* 0 means static structures haven't
79 * been initialized yet. */
82 * Forward declarations for procedures defined in this file:
85 static void BorderInit
_ANSI_ARGS_((void));
86 static int Intersect
_ANSI_ARGS_((XPoint
*a1Ptr
, XPoint
*a2Ptr
,
87 XPoint
*b1Ptr
, XPoint
*b2Ptr
, XPoint
*iPtr
));
88 static void ShiftLine
_ANSI_ARGS_((XPoint
*p1Ptr
, XPoint
*p2Ptr
,
89 int distance
, XPoint
*p3Ptr
));
92 *--------------------------------------------------------------
96 * Create a data structure for displaying a 3-D border.
99 * The return value is a token for a data structure
100 * describing a 3-D border. This token may be passed
101 * to Tk_Draw3DRectangle and Tk_Free3DBorder. If an
102 * error prevented the border from being created then
103 * NULL is returned and an error message will be left
107 * Data structures, graphics contexts, etc. are allocated.
108 * It is the caller's responsibility to eventually call
109 * Tk_Free3DBorder to release the resources.
111 *--------------------------------------------------------------
115 Tk_Get3DBorder(interp
, tkwin
, colormap
, colorName
)
116 Tcl_Interp
*interp
; /* Place to store an error message. */
117 Tk_Window tkwin
; /* Token for window in which
118 * border will be drawn. */
119 Colormap colormap
; /* Colormap to use for allocating border
120 * colors. None means use default colormap
122 Tk_Uid colorName
; /* String giving name of color
123 * for window background. */
126 Tcl_HashEntry
*hashPtr
;
127 register Border
*borderPtr
;
129 unsigned long light
, dark
;
138 * First, check to see if there's already a border that will work
142 key
.colorName
= colorName
;
143 if (colormap
== None
) {
144 colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
146 key
.colormap
= colormap
;
147 key
.screen
= Tk_Screen(tkwin
);
149 hashPtr
= Tcl_CreateHashEntry(&borderTable
, (char *) &key
, &new);
151 borderPtr
= (Border
*) Tcl_GetHashValue(hashPtr
);
152 borderPtr
->refCount
++;
156 * No satisfactory border exists yet. Initialize a new one.
159 borderPtr
= (Border
*) ckalloc(sizeof(Border
));
160 borderPtr
->display
= Tk_Display(tkwin
);
161 borderPtr
->refCount
= 1;
162 borderPtr
->bgColorPtr
= NULL
;
163 borderPtr
->lightColorPtr
= NULL
;
164 borderPtr
->darkColorPtr
= NULL
;
165 borderPtr
->shadow
= None
;
166 borderPtr
->lightGC
= None
;
167 borderPtr
->darkGC
= None
;
168 borderPtr
->bgGC
= None
;
169 borderPtr
->hashPtr
= hashPtr
;
170 Tcl_SetHashValue(hashPtr
, borderPtr
);
173 * Figure out what colors and GC's to use for the light
174 * and dark areas and set up the graphics contexts.
175 * Monochrome displays get handled differently than
179 borderPtr
->bgColorPtr
= Tk_GetColor(interp
, tkwin
,
180 key
.colormap
, colorName
);
181 if (borderPtr
->bgColorPtr
== NULL
) {
184 if (Tk_DefaultDepth(Tk_Screen(tkwin
)) == 1) {
186 * Monochrome display.
189 light
= borderPtr
->bgColorPtr
->pixel
;
190 if (light
== WhitePixelOfScreen(Tk_Screen(tkwin
))) {
191 dark
= BlackPixelOfScreen(Tk_Screen(tkwin
));
193 dark
= WhitePixelOfScreen(Tk_Screen(tkwin
));
195 borderPtr
->shadow
= Tk_GetBitmap(interp
, tkwin
,
196 Tk_GetUid("gray50"));
197 if (borderPtr
->shadow
== None
) {
201 XColor lightColor
, darkColor
;
205 * Color display. Compute the colors for the illuminated
206 * and shaded portions of the border.
209 tmp
= (14*(int)borderPtr
->bgColorPtr
->red
)/10;
210 if (tmp
> MAX_INTENSITY
) {
213 lightColor
.red
= tmp
;
214 tmp
= (14*(int)borderPtr
->bgColorPtr
->green
)/10;
215 if (tmp
> MAX_INTENSITY
) {
218 lightColor
.green
= tmp
;
219 tmp
= (14*(int)borderPtr
->bgColorPtr
->blue
)/10;
220 if (tmp
> MAX_INTENSITY
) {
223 lightColor
.blue
= tmp
;
224 darkColor
.red
= (60*(int)borderPtr
->bgColorPtr
->red
)/100;
225 darkColor
.green
= (60*(int)borderPtr
->bgColorPtr
->green
)/100;
226 darkColor
.blue
= (60*(int)borderPtr
->bgColorPtr
->blue
)/100;
227 borderPtr
->lightColorPtr
= Tk_GetColorByValue(interp
, tkwin
,
228 key
.colormap
, &lightColor
);
229 if (borderPtr
->lightColorPtr
== NULL
) {
232 borderPtr
->darkColorPtr
= Tk_GetColorByValue(interp
, tkwin
,
233 key
.colormap
, &darkColor
);
234 if (borderPtr
->darkColorPtr
== NULL
) {
237 light
= borderPtr
->lightColorPtr
->pixel
;
238 dark
= borderPtr
->darkColorPtr
->pixel
;
240 gcValues
.foreground
= light
;
241 gcValues
.background
= dark
;
242 mask
= GCForeground
|GCBackground
;
243 if (borderPtr
->shadow
!= None
) {
244 gcValues
.stipple
= borderPtr
->shadow
;
245 gcValues
.fill_style
= FillOpaqueStippled
;
246 mask
|= GCStipple
|GCFillStyle
;
248 borderPtr
->lightGC
= Tk_GetGC(tkwin
, mask
, &gcValues
);
249 gcValues
.foreground
= dark
;
250 gcValues
.background
= light
;
251 borderPtr
->darkGC
= Tk_GetGC(tkwin
, GCForeground
|GCBackground
,
253 gcValues
.foreground
= borderPtr
->bgColorPtr
->pixel
;
254 borderPtr
->bgGC
= Tk_GetGC(tkwin
, GCForeground
, &gcValues
);
256 return (Tk_3DBorder
) borderPtr
;
259 Tk_Free3DBorder((Tk_3DBorder
) borderPtr
);
264 *--------------------------------------------------------------
266 * Tk_Draw3DRectangle --
268 * Draw a 3-D border at a given place in a given window.
274 * A 3-D border will be drawn in the indicated drawable.
275 * The outside edges of the border will be determined by x,
276 * y, width, and height. The inside edges of the border
277 * will be determined by the borderWidth argument.
279 *--------------------------------------------------------------
283 Tk_Draw3DRectangle(display
, drawable
, border
, x
, y
, width
, height
,
285 Display
*display
; /* X display in which to draw. */
286 Drawable drawable
; /* X window or pixmap in which to draw. */
287 Tk_3DBorder border
; /* Token for border to draw. */
288 int x
, y
, width
, height
; /* Outside area of region in
289 * which border will be drawn. */
290 int borderWidth
; /* Desired width for border, in
292 int relief
; /* Should be either TK_RELIEF_RAISED
293 * or TK_RELIEF_SUNKEN; indicates
294 * position of interior of window relative
297 register Border
*borderPtr
= (Border
*) border
;
301 if ((width
< 2*borderWidth
) || (height
< 2*borderWidth
)) {
305 if (relief
== TK_RELIEF_RAISED
) {
306 top
= borderPtr
->lightGC
;
307 bottom
= borderPtr
->darkGC
;
308 } else if (relief
== TK_RELIEF_SUNKEN
) {
309 top
= borderPtr
->darkGC
;
310 bottom
= borderPtr
->lightGC
;
312 top
= bottom
= borderPtr
->bgGC
;
314 XFillRectangle(display
, drawable
, bottom
, x
, y
+height
-borderWidth
,
316 (unsigned int) width
, (unsigned int) borderWidth
);
317 XFillRectangle(display
, drawable
, bottom
, x
+width
-borderWidth
, y
,
318 (unsigned int) borderWidth
, (unsigned int) height
);
319 points
[0].x
= points
[1].x
= points
[6].x
= x
;
320 points
[0].y
= points
[6].y
= y
+ height
;
321 points
[1].y
= points
[2].y
= y
;
322 points
[2].x
= x
+ width
;
323 points
[3].x
= x
+ width
- borderWidth
;
324 points
[3].y
= points
[4].y
= y
+ borderWidth
;
325 points
[4].x
= points
[5].x
= x
+ borderWidth
;
326 points
[5].y
= y
+ height
- borderWidth
;
327 XFillPolygon(display
, drawable
, top
, points
, 7, Nonconvex
,
332 *--------------------------------------------------------------
334 * Tk_NameOf3DBorder --
336 * Given a border, return a textual string identifying the
340 * The return value is the string that was used to create
346 *--------------------------------------------------------------
350 Tk_NameOf3DBorder(border
)
351 Tk_3DBorder border
; /* Token for border. */
353 Border
*borderPtr
= (Border
*) border
;
355 return ((BorderKey
*) borderPtr
->hashPtr
->key
.words
)->colorName
;
359 *--------------------------------------------------------------------
361 * Tk_3DBorderColor --
363 * Given a 3D border, return the X color used for the "flat"
367 * Returns the color used drawing flat surfaces with the border.
372 *--------------------------------------------------------------------
375 Tk_3DBorderColor(border
)
378 return(((Border
*) border
)->bgColorPtr
);
382 *--------------------------------------------------------------
386 * This procedure is called when a 3D border is no longer
387 * needed. It frees the resources associated with the
388 * border. After this call, the caller should never again
389 * use the "border" token.
395 * Resources are freed.
397 *--------------------------------------------------------------
401 Tk_Free3DBorder(border
)
402 Tk_3DBorder border
; /* Token for border to be released. */
404 register Border
*borderPtr
= (Border
*) border
;
406 borderPtr
->refCount
--;
407 if (borderPtr
->refCount
== 0) {
408 if (borderPtr
->bgColorPtr
!= NULL
) {
409 Tk_FreeColor(borderPtr
->bgColorPtr
);
411 if (borderPtr
->lightColorPtr
!= NULL
) {
412 Tk_FreeColor(borderPtr
->lightColorPtr
);
414 if (borderPtr
->darkColorPtr
!= NULL
) {
415 Tk_FreeColor(borderPtr
->darkColorPtr
);
417 if (borderPtr
->shadow
!= None
) {
418 Tk_FreeBitmap(borderPtr
->shadow
);
420 if (borderPtr
->lightGC
!= None
) {
421 Tk_FreeGC(borderPtr
->lightGC
);
423 if (borderPtr
->darkGC
!= None
) {
424 Tk_FreeGC(borderPtr
->darkGC
);
426 if (borderPtr
->bgGC
!= None
) {
427 Tk_FreeGC(borderPtr
->bgGC
);
429 Tcl_DeleteHashEntry(borderPtr
->hashPtr
);
430 ckfree((char *) borderPtr
);
435 *----------------------------------------------------------------------
437 * Tk_SetBackgroundFromBorder --
439 * Change the background of a window to one appropriate for a given
446 * Tkwin's background gets modified.
448 *----------------------------------------------------------------------
452 Tk_SetBackgroundFromBorder(tkwin
, border
)
453 Tk_Window tkwin
; /* Window whose background is to be set. */
454 Tk_3DBorder border
; /* Token for border. */
456 register Border
*borderPtr
= (Border
*) border
;
458 Tk_SetWindowBackground(tkwin
, borderPtr
->bgColorPtr
->pixel
);
462 *----------------------------------------------------------------------
466 * Parse a relief description and return the corresponding
467 * relief value, or an error.
470 * A standard Tcl return value. If all goes well then
471 * *reliefPtr is filled in with one of the values
472 * TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN.
477 *----------------------------------------------------------------------
481 Tk_GetRelief(interp
, name
, reliefPtr
)
482 Tcl_Interp
*interp
; /* For error messages. */
483 char *name
; /* Name of a relief type. */
484 int *reliefPtr
; /* Where to store converted relief. */
490 length
= strlen(name
);
491 if ((c
== 'f') && (strncmp(name
, "flat", length
) == 0)) {
492 *reliefPtr
= TK_RELIEF_FLAT
;
493 } else if ((c
== 'r') && (strncmp(name
, "raised", length
) == 0)) {
494 *reliefPtr
= TK_RELIEF_RAISED
;
495 } else if ((c
== 's') && (strncmp(name
, "sunken", length
) == 0)) {
496 *reliefPtr
= TK_RELIEF_SUNKEN
;
498 sprintf(interp
->result
, "bad relief type \"%.50s\": must be %s",
499 name
, "flat, raised, or sunken");
506 *--------------------------------------------------------------
510 * Given a relief value, produce a string describing that
514 * The return value is a static string that is equivalent
520 *--------------------------------------------------------------
524 Tk_NameOfRelief(relief
)
525 int relief
; /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
526 * or TK_RELIEF_SUNKEN. */
528 if (relief
== TK_RELIEF_FLAT
) {
530 } else if (relief
== TK_RELIEF_SUNKEN
) {
532 } else if (relief
== TK_RELIEF_RAISED
) {
535 return "unknown relief";
540 *--------------------------------------------------------------
542 * Tk_Draw3DPolygon --
544 * Draw a border with 3-D appearance around the edge of a
551 * Information is drawn in "drawable" in the form of a
552 * 3-D border borderWidth units width wide on the left
553 * of the trajectory given by pointPtr and numPoints (or
554 * -borderWidth units wide on the right side, if borderWidth
557 *--------------------------------------------------------------
561 Tk_Draw3DPolygon(display
, drawable
, border
, pointPtr
, numPoints
,
562 borderWidth
, leftRelief
)
563 Display
*display
; /* X display in which to draw polygon. */
564 Drawable drawable
; /* X window or pixmap in which to draw. */
565 Tk_3DBorder border
; /* Token for border to draw. */
566 XPoint
*pointPtr
; /* Array of points describing
567 * polygon. All points must be
568 * absolute (CoordModeOrigin). */
569 int numPoints
; /* Number of points at *pointPtr. */
570 int borderWidth
; /* Width of border, measured in
571 * pixels to the left of the polygon's
572 * trajectory. May be negative. */
573 int leftRelief
; /* TK_RELIEF_RAISED or
574 * TK_RELIEF_SUNKEN: indicates how
575 * stuff to left of trajectory looks
576 * relative to stuff on right. */
578 XPoint poly
[4], b1
, b2
, newB1
, newB2
;
579 XPoint perp
, c
, shift1
, shift2
; /* Used for handling parallel lines. */
580 register XPoint
*p1Ptr
, *p2Ptr
;
581 Border
*borderPtr
= (Border
*) border
;
583 int i
, lightOnLeft
, dx
, dy
, parallel
, pointsSeen
;
586 * If the polygon is already closed, drop the last point from it
587 * (we'll close it automatically).
590 p1Ptr
= &pointPtr
[numPoints
-1];
591 p2Ptr
= &pointPtr
[0];
592 if ((p1Ptr
->x
== p2Ptr
->x
) && (p1Ptr
->y
== p2Ptr
->y
)) {
597 * The loop below is executed once for each vertex in the polgon.
598 * At the beginning of each iteration things look like this:
603 * b1 * poly[0] (pointPtr[i-1])
610 * b2 *--------------------*
613 * x-------------------------
615 * The job of this iteration is to do the following:
616 * (a) Compute x (the border corner corresponding to
617 * pointPtr[i]) and put it in poly[2]. As part of
618 * this, compute a new b1 and b2 value for the next
619 * side of the polygon.
620 * (b) Put pointPtr[i] into poly[3].
621 * (c) Draw the polygon given by poly[0..3].
622 * (d) Advance poly[0], poly[1], b1, and b2 for the
623 * next side of the polygon.
627 * The above situation doesn't first come into existence until
628 * two points have been processed; the first two points are
629 * used to "prime the pump", so some parts of the processing
630 * are ommitted for these points. The variable "pointsSeen"
631 * keeps track of the priming process; it has to be separate
632 * from i in order to be able to ignore duplicate points in the
637 for (i
= -2, p1Ptr
= &pointPtr
[numPoints
-2], p2Ptr
= p1Ptr
+1;
638 i
< numPoints
; i
++, p1Ptr
= p2Ptr
, p2Ptr
++) {
639 if ((i
== -1) || (i
== numPoints
-1)) {
642 if ((p2Ptr
->x
== p1Ptr
->x
) && (p2Ptr
->y
== p1Ptr
->y
)) {
644 * Ignore duplicate points (they'd cause core dumps in
645 * ShiftLine calls below).
649 ShiftLine(p1Ptr
, p2Ptr
, borderWidth
, &newB1
);
650 newB2
.x
= newB1
.x
+ (p2Ptr
->x
- p1Ptr
->x
);
651 newB2
.y
= newB1
.y
+ (p2Ptr
->y
- p1Ptr
->y
);
654 if (pointsSeen
>= 1) {
655 parallel
= Intersect(&newB1
, &newB2
, &b1
, &b2
, &poly
[2]);
658 * If two consecutive segments of the polygon are parallel,
659 * then things get more complex. Consider the following
663 * *----b1-----------b2------a
666 * *---------*----------* b
667 * poly[0] *p2Ptr *p1Ptr /
672 * Instead of using x and *p1Ptr for poly[2] and poly[3], as
673 * in the original diagram, use a and b as above. Then instead
674 * of using x and *p1Ptr for the new poly[0] and poly[1], use
677 * Do the computation in three stages:
678 * 1. Compute a point "perp" such that the line p1Ptr-perp
679 * is perpendicular to p1Ptr-p2Ptr.
680 * 2. Compute the points a and c by intersecting the lines
681 * b1-b2 and newB1-newB2 with p1Ptr-perp.
682 * 3. Compute b by shifting p1Ptr-perp to the right and
683 * intersecting it with p1Ptr-p2Ptr.
687 perp
.x
= p1Ptr
->x
+ (p2Ptr
->y
- p1Ptr
->y
);
688 perp
.y
= p1Ptr
->y
- (p2Ptr
->x
- p1Ptr
->x
);
689 (void) Intersect(p1Ptr
, &perp
, &b1
, &b2
, &poly
[2]);
690 (void) Intersect(p1Ptr
, &perp
, &newB1
, &newB2
, &c
);
691 ShiftLine(p1Ptr
, &perp
, borderWidth
, &shift1
);
692 shift2
.x
= shift1
.x
+ (perp
.x
- p1Ptr
->x
);
693 shift2
.y
= shift1
.y
+ (perp
.y
- p1Ptr
->y
);
694 (void) Intersect(p1Ptr
, p2Ptr
, &shift1
, &shift2
, &poly
[3]);
697 if (pointsSeen
>= 2) {
698 dx
= poly
[3].x
- poly
[0].x
;
699 dy
= poly
[3].y
- poly
[0].y
;
701 lightOnLeft
= (dy
<= dx
);
703 lightOnLeft
= (dy
< dx
);
705 if (lightOnLeft
^ (leftRelief
== TK_RELIEF_RAISED
)) {
706 gc
= borderPtr
->lightGC
;
708 gc
= borderPtr
->darkGC
;
710 XFillPolygon(display
, drawable
, gc
, poly
, 4, Convex
,
717 poly
[0].x
= poly
[3].x
;
718 poly
[0].y
= poly
[3].y
;
722 } else if (pointsSeen
>= 1) {
723 poly
[1].x
= poly
[2].x
;
724 poly
[1].y
= poly
[2].y
;
731 *----------------------------------------------------------------------
733 * Tk_Fill3DRectangle --
735 * Fill a rectangular area, supplying a 3D border if desired.
741 * Information gets drawn on the screen.
743 *----------------------------------------------------------------------
747 Tk_Fill3DRectangle(display
, drawable
, border
, x
, y
, width
,
748 height
, borderWidth
, relief
)
749 Display
*display
; /* X display in which to draw rectangle. */
750 Drawable drawable
; /* X window or pixmap in which to draw. */
751 Tk_3DBorder border
; /* Token for border to draw. */
752 int x
, y
, width
, height
; /* Outside area of rectangular region. */
753 int borderWidth
; /* Desired width for border, in
754 * pixels. Border will be *inside* region. */
755 int relief
; /* Indicates 3D effect: TK_RELIEF_FLAT,
756 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
758 register Border
*borderPtr
= (Border
*) border
;
760 XFillRectangle(display
, drawable
, borderPtr
->bgGC
,
761 x
, y
, (unsigned int) width
, (unsigned int) height
);
762 if (relief
!= TK_RELIEF_FLAT
) {
763 Tk_Draw3DRectangle(display
, drawable
, border
, x
, y
, width
,
764 height
, borderWidth
, relief
);
769 *----------------------------------------------------------------------
771 * Tk_Fill3DPolygon --
773 * Fill a polygonal area, supplying a 3D border if desired.
779 * Information gets drawn on the screen.
781 *----------------------------------------------------------------------
785 Tk_Fill3DPolygon(display
, drawable
, border
, pointPtr
, numPoints
,
786 borderWidth
, leftRelief
)
787 Display
*display
; /* X display in which to draw polygon. */
788 Drawable drawable
; /* X window or pixmap in which to draw. */
789 Tk_3DBorder border
; /* Token for border to draw. */
790 XPoint
*pointPtr
; /* Array of points describing
791 * polygon. All points must be
792 * absolute (CoordModeOrigin). */
793 int numPoints
; /* Number of points at *pointPtr. */
794 int borderWidth
; /* Width of border, measured in
795 * pixels to the left of the polygon's
796 * trajectory. May be negative. */
797 int leftRelief
; /* Indicates 3D effect of left side of
798 * trajectory relative to right:
799 * TK_RELIEF_FLAT, TK_RELIEF_RAISED,
800 * or TK_RELIEF_SUNKEN. */
802 register Border
*borderPtr
= (Border
*) border
;
804 XFillPolygon(display
, drawable
, borderPtr
->bgGC
,
805 pointPtr
, numPoints
, Complex
, CoordModeOrigin
);
806 if (leftRelief
!= TK_RELIEF_FLAT
) {
807 Tk_Draw3DPolygon(display
, drawable
, border
, pointPtr
, numPoints
,
808 borderWidth
, leftRelief
);
813 *--------------------------------------------------------------
817 * Initialize the structures used for border management.
825 *-------------------------------------------------------------
832 Tcl_InitHashTable(&borderTable
, sizeof(BorderKey
)/sizeof(int));
836 *--------------------------------------------------------------
840 * Given two points on a line, compute a point on a
841 * new line that is parallel to the given line and
842 * a given distance away from it.
850 *--------------------------------------------------------------
854 ShiftLine(p1Ptr
, p2Ptr
, distance
, p3Ptr
)
855 XPoint
*p1Ptr
; /* First point on line. */
856 XPoint
*p2Ptr
; /* Second point on line. */
857 int distance
; /* New line is to be this many
858 * units to the left of original
859 * line, when looking from p1 to
860 * p2. May be negative. */
861 XPoint
*p3Ptr
; /* Store coords of point on new
864 int dx
, dy
, dxNeg
, dyNeg
;
867 * The table below is used for a quick approximation in
868 * computing the new point. An index into the table
869 * is 128 times the slope of the original line (the slope
870 * must always be between 0 and 1). The value of the table
871 * entry is 128 times the amount to displace the new line
872 * in y for each unit of perpendicular distance. In other
873 * words, the table maps from the tangent of an angle to
874 * the inverse of its cosine. If the slope of the original
875 * line is greater than 1, then the displacement is done in
876 * x rather than in y.
879 static int shiftTable
[129];
882 * Initialize the table if this is the first time it is
886 if (shiftTable
[0] == 0) {
888 double tangent
, cosine
;
890 for (i
= 0; i
<= 128; i
++) {
892 cosine
= 128/cos(atan(tangent
)) + .5;
893 shiftTable
[i
] = cosine
;
898 dx
= p2Ptr
->x
- p1Ptr
->x
;
899 dy
= p2Ptr
->y
- p1Ptr
->y
;
913 dy
= ((distance
* shiftTable
[(dy
<<7)/dx
]) + 64) >> 7;
919 dx
= ((distance
* shiftTable
[(dx
<<7)/dy
]) + 64) >> 7;
928 *--------------------------------------------------------------
932 * Find the intersection point between two lines.
935 * Under normal conditions 0 is returned and the point
936 * at *iPtr is filled in with the intersection between
937 * the two lines. If the two lines are parallel, then
938 * -1 is returned and *iPtr isn't modified.
943 *--------------------------------------------------------------
947 Intersect(a1Ptr
, a2Ptr
, b1Ptr
, b2Ptr
, iPtr
)
948 XPoint
*a1Ptr
; /* First point of first line. */
949 XPoint
*a2Ptr
; /* Second point of first line. */
950 XPoint
*b1Ptr
; /* First point of second line. */
951 XPoint
*b2Ptr
; /* Second point of second line. */
952 XPoint
*iPtr
; /* Filled in with intersection point. */
954 int dxadyb
, dxbdya
, dxadxb
, dyadyb
, p
, q
;
957 * The code below is just a straightforward manipulation of two
958 * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve
959 * for the x-coordinate of intersection, then the y-coordinate.
962 dxadyb
= (a2Ptr
->x
- a1Ptr
->x
)*(b2Ptr
->y
- b1Ptr
->y
);
963 dxbdya
= (b2Ptr
->x
- b1Ptr
->x
)*(a2Ptr
->y
- a1Ptr
->y
);
964 dxadxb
= (a2Ptr
->x
- a1Ptr
->x
)*(b2Ptr
->x
- b1Ptr
->x
);
965 dyadyb
= (a2Ptr
->y
- a1Ptr
->y
)*(b2Ptr
->y
- b1Ptr
->y
);
967 if (dxadyb
== dxbdya
) {
970 p
= (a1Ptr
->x
*dxbdya
- b1Ptr
->x
*dxadyb
+ (b1Ptr
->y
- a1Ptr
->y
)*dxadxb
);
977 iPtr
->x
= - ((-p
+ q
/2)/q
);
979 iPtr
->x
= (p
+ q
/2)/q
;
981 p
= (a1Ptr
->y
*dxadyb
- b1Ptr
->y
*dxbdya
+ (b1Ptr
->x
- a1Ptr
->x
)*dyadyb
);
988 iPtr
->y
= - ((-p
+ q
/2)/q
);
990 iPtr
->y
= (p
+ q
/2)/q
;