1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * Matrix code from GIMP:app/transform_tool.c
5 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 /* include glib.h first, so we don't pick up its inline functions as well */
26 /* include normal versions of the inlined functions here ... */
28 #define G_INLINE_FUNC extern
29 #define G_CAN_INLINE 1
34 rectangle_union(Rectangle
*r1
, const Rectangle
*r2
)
36 r1
->top
= MIN( r1
->top
, r2
->top
);
37 r1
->bottom
= MAX( r1
->bottom
, r2
->bottom
);
38 r1
->left
= MIN( r1
->left
, r2
->left
);
39 r1
->right
= MAX( r1
->right
, r2
->right
);
43 int_rectangle_union(IntRectangle
*r1
, const IntRectangle
*r2
)
45 r1
->top
= MIN( r1
->top
, r2
->top
);
46 r1
->bottom
= MAX( r1
->bottom
, r2
->bottom
);
47 r1
->left
= MIN( r1
->left
, r2
->left
);
48 r1
->right
= MAX( r1
->right
, r2
->right
);
52 rectangle_intersection(Rectangle
*r1
, const Rectangle
*r2
)
54 r1
->top
= MAX( r1
->top
, r2
->top
);
55 r1
->bottom
= MIN( r1
->bottom
, r2
->bottom
);
56 r1
->left
= MAX( r1
->left
, r2
->left
);
57 r1
->right
= MIN( r1
->right
, r2
->right
);
59 /* Is rectangle empty? */
60 if ( (r1
->top
>= r1
->bottom
) ||
61 (r1
->left
>= r1
->right
) ) {
62 r1
->top
= r1
->bottom
= r1
->left
= r1
->right
= 0.0;
67 rectangle_intersects(const Rectangle
*r1
, const Rectangle
*r2
)
69 if ( (r1
->right
< r2
->left
) ||
70 (r1
->left
> r2
->right
) ||
71 (r1
->top
> r2
->bottom
) ||
72 (r1
->bottom
< r2
->top
) )
79 point_in_rectangle(const Rectangle
* r
, const Point
*p
)
81 if ( (p
->x
< r
->left
) ||
91 rectangle_in_rectangle(const Rectangle
* outer
, const Rectangle
*inner
)
93 if ( (inner
->left
< outer
->left
) ||
94 (inner
->right
> outer
->right
) ||
95 (inner
->top
< outer
->top
) ||
96 (inner
->bottom
> outer
->bottom
))
103 rectangle_add_point(Rectangle
*r
, const Point
*p
)
107 else if (p
->x
> r
->right
)
112 else if (p
->y
> r
->bottom
)
118 * This function estimates the distance from a point to a rectangle.
119 * If the point is in the rectangle, 0.0 is returned. Otherwise the
120 * distance in a manhattan metric from the point to the nearest point
121 * on the rectangle is returned.
124 distance_rectangle_point(const Rectangle
*rect
, const Point
*point
)
129 if (point
->x
< rect
->left
) {
130 dx
= rect
->left
- point
->x
;
131 } else if (point
->x
> rect
->right
) {
132 dx
= point
->x
- rect
->right
;
135 if (point
->y
< rect
->top
) {
136 dy
= rect
->top
- point
->y
;
137 } else if (point
->y
> rect
->bottom
) {
138 dy
= point
->y
- rect
->bottom
;
145 * This function estimates the distance from a point to a line segment
146 * specified by two endpoints.
147 * If the point is on the line segment, 0.0 is returned. Otherwise the
148 * distance in the R^2 metric from the point to the nearest point
149 * on the line segment is returned. Does one sqrt per call.
150 * Philosophical bug: line_width is ignored iff point is beyond
151 * end of line segment.
154 distance_line_point(const Point
*line_start
, const Point
*line_end
,
155 real line_width
, const Point
*point
)
163 point_sub(&v1
,line_start
);
166 point_sub(&v2
, line_start
);
168 v1_lensq
= point_dot(&v1
,&v1
);
170 if (v1_lensq
<0.000001) {
171 return sqrt(point_dot(&v2
,&v2
));
174 projlen
= point_dot(&v1
,&v2
) / v1_lensq
;
177 return sqrt(point_dot(&v2
,&v2
));
182 point_sub(&v3
,line_end
);
183 return sqrt(point_dot(&v3
,&v3
));
186 point_scale(&v1
, projlen
);
189 perp_dist
= sqrt(point_dot(&v1
,&v1
));
191 perp_dist
-= line_width
/ 2.0;
192 if (perp_dist
< 0.0) {
199 /* returns 1 iff the line crosses the ray from (-Inf, rayend.y) to rayend */
201 line_crosses_ray(const Point
*line_start
,
202 const Point
*line_end
, const Point
*rayend
)
206 /* swap end points if necessary */
207 if (line_start
->y
> line_end
->y
) {
211 line_start
= line_end
;
214 /* if y coords of line do not include rayend.y */
215 if (line_start
->y
> rayend
->y
|| line_end
->y
< rayend
->y
)
217 /* Avoid division by zero for horizontal case */
218 if (line_end
->y
- line_start
->y
< 0.00000000001) {
219 return (line_end
->y
- rayend
->y
< 0.00000000001);
221 xpos
= line_start
->x
+ (rayend
->y
- line_start
->y
) *
222 (line_end
->x
- line_start
->x
) / (line_end
->y
- line_start
->y
);
223 return xpos
<= rayend
->x
;
227 distance_polygon_point(const Point
*poly
, guint npoints
, real line_width
,
230 guint i
, last
= npoints
- 1;
231 real line_dist
= G_MAXFLOAT
;
234 /* calculate ray crossings and line distances */
235 for (i
= 0; i
< npoints
; i
++) {
238 crossings
+= line_crosses_ray(&poly
[last
], &poly
[i
], point
);
239 dist
= distance_line_point(&poly
[last
], &poly
[i
], line_width
, point
);
240 line_dist
= MIN(line_dist
, dist
);
243 /* If there is an odd number of ray crossings, we are inside the polygon.
244 * Otherwise, return the minium distance from a line segment */
245 if (crossings
% 2 == 1)
251 /* number of segments to use in bezier curve approximation */
254 /* if cross is not NULL, it will be incremented for each ray crossing */
256 bez_point_distance_and_ray_crosses(const Point
*b1
,
257 const Point
*b2
, const Point
*b3
,
259 real line_width
, const Point
*point
,
262 static gboolean calculated_coeff
= FALSE
;
263 static real coeff
[NBEZ_SEGS
+1][4];
265 real line_dist
= G_MAXFLOAT
;
268 if (!calculated_coeff
) {
269 for (i
= 0; i
<= NBEZ_SEGS
; i
++) {
270 real t1
= ((real
)i
)/NBEZ_SEGS
, t2
= t1
*t1
, t3
= t1
*t2
;
271 real it1
= 1-t1
, it2
= it1
*it1
, it3
= it1
*it2
;
274 coeff
[i
][1] = 3 * t1
* it2
;
275 coeff
[i
][2] = 3 * t2
* it1
;
279 calculated_coeff
= TRUE
;
281 prev
.x
= coeff
[0][0] * b1
->x
+ coeff
[0][1] * b2
->x
+
282 coeff
[0][2] * b3
->x
+ coeff
[0][3] * b4
->x
;
283 prev
.y
= coeff
[0][0] * b1
->y
+ coeff
[0][1] * b2
->y
+
284 coeff
[0][2] * b3
->y
+ coeff
[0][3] * b4
->y
;
285 for (i
= 1; i
<= NBEZ_SEGS
; i
++) {
288 pt
.x
= coeff
[i
][0] * b1
->x
+ coeff
[i
][1] * b2
->x
+
289 coeff
[i
][2] * b3
->x
+ coeff
[i
][3] * b4
->x
;
290 pt
.y
= coeff
[i
][0] * b1
->y
+ coeff
[i
][1] * b2
->y
+
291 coeff
[i
][2] * b3
->y
+ coeff
[i
][3] * b4
->y
;
293 dist
= distance_line_point(&prev
, &pt
, line_width
, point
);
294 line_dist
= MIN(line_dist
, dist
);
296 *cross
+= line_crosses_ray(&prev
, &pt
, point
);
304 distance_bez_seg_point(const Point
*b1
, const Point
*b2
,
305 const Point
*b3
, const Point
*b4
,
306 real line_width
, const Point
*point
)
308 return bez_point_distance_and_ray_crosses(b1
, b2
, b3
, b4
,
309 line_width
, point
, NULL
);
313 distance_bez_line_point(const BezPoint
*b
, guint npoints
,
314 real line_width
, const Point
*point
)
318 real line_dist
= G_MAXFLOAT
;
320 g_return_val_if_fail(b
[0].type
== BEZ_MOVE_TO
, -1);
324 for (i
= 1; i
< npoints
; i
++) {
329 /*g_assert_not_reached();*/
330 g_warning("BEZ_MOVE_TO found half way through a bezier line");
333 dist
= distance_line_point(&last
, &b
[i
].p1
, line_width
, point
);
334 line_dist
= MIN(line_dist
, dist
);
338 dist
= bez_point_distance_and_ray_crosses(&last
, &b
[i
].p1
, &b
[i
].p2
,
339 &b
[i
].p3
, line_width
, point
,
341 line_dist
= MIN(line_dist
, dist
);
350 distance_bez_shape_point(const BezPoint
*b
, guint npoints
,
351 real line_width
, const Point
*point
)
355 real line_dist
= G_MAXFLOAT
;
358 g_return_val_if_fail(b
[0].type
== BEZ_MOVE_TO
, -1);
362 for (i
= 1; i
< npoints
; i
++) {
367 /*g_assert_not_reached();*/
368 g_warning("BEZ_MOVE_TO found half way through a bezier shape");
371 dist
= distance_line_point(&last
, &b
[i
].p1
, line_width
, point
);
372 crossings
+= line_crosses_ray(&last
, &b
[i
].p1
, point
);
373 line_dist
= MIN(line_dist
, dist
);
377 dist
= bez_point_distance_and_ray_crosses(&last
, &b
[i
].p1
, &b
[i
].p2
,
378 &b
[i
].p3
, line_width
, point
,
380 line_dist
= MIN(line_dist
, dist
);
385 /* If there is an odd number of ray crossings, we are inside the polygon.
386 * Otherwise, return the minium distance from a line segment */
387 if (crossings
% 2 == 1)
394 distance_ellipse_point(const Point
*centre
, real width
, real height
,
395 real line_width
, const Point
*point
)
397 /* A faster intersection method would be to scaled the ellipse and the
398 * point to where the ellipse is a circle, intersect with the circle,
401 real w2
= width
*width
, h2
= height
*height
;
402 real scale
, rad
, dist
;
405 /* find the radius of the ellipse in the appropriate direction */
407 /* find the point of intersection between line (x=cx+(px-cx)t; y=cy+(py-cy)t)
408 * and ellipse ((x-cx)^2)/(w/2)^2 + ((y-cy)^2)/(h/2)^2 = 1 */
409 /* radius along ray is sqrt((px-cx)^2 * t^2 + (py-cy)^2 * t^2) */
412 point_sub(&pt
, centre
);
414 pt
.y
*= pt
.y
; /* pt = (point - centre).^2 */
416 scale
= w2
* h2
/ (4*h2
*pt
.x
+ 4*w2
*pt
.y
);
417 rad
= sqrt((pt
.x
+ pt
.y
)*scale
) + line_width
/2;
419 dist
= sqrt(pt
.x
+ pt
.y
);
429 transform_point (Matrix m
, Point
*src
, Point
*dest
)
433 xx
= m
[0][0] * src
->x
+ m
[0][1] * src
->y
+ m
[0][2];
434 yy
= m
[1][0] * src
->x
+ m
[1][1] * src
->y
+ m
[1][2];
435 ww
= m
[2][0] * src
->x
+ m
[2][1] * src
->y
+ m
[2][2];
445 mult_matrix (Matrix m1
, Matrix m2
)
450 for (i
= 0; i
< 3; i
++)
451 for (j
= 0; j
< 3; j
++)
454 for (k
= 0; k
< 3; k
++)
455 result
[i
][j
] += m1
[i
][k
] * m2
[k
][j
];
458 /* copy the result into matrix 2 */
459 for (i
= 0; i
< 3; i
++)
460 for (j
= 0; j
< 3; j
++)
461 m2
[i
][j
] = result
[i
][j
];
465 identity_matrix (Matrix m
)
469 for (i
= 0; i
< 3; i
++)
470 for (j
= 0; j
< 3; j
++)
471 m
[i
][j
] = (i
== j
) ? 1 : 0;
476 translate_matrix (Matrix m
, real x
, real y
)
480 identity_matrix (trans
);
483 mult_matrix (trans
, m
);
487 scale_matrix (Matrix m
, real x
, real y
)
491 identity_matrix (scale
);
494 mult_matrix (scale
, m
);
498 rotate_matrix (Matrix m
, real theta
)
501 real cos_theta
, sin_theta
;
503 cos_theta
= cos (theta
);
504 sin_theta
= sin (theta
);
506 identity_matrix (rotate
);
507 rotate
[0][0] = cos_theta
;
508 rotate
[0][1] = -sin_theta
;
509 rotate
[1][0] = sin_theta
;
510 rotate
[1][1] = cos_theta
;
511 mult_matrix (rotate
, m
);
515 xshear_matrix (Matrix m
, real shear
)
519 identity_matrix (shear_m
);
520 shear_m
[0][1] = shear
;
521 mult_matrix (shear_m
, m
);
525 yshear_matrix (Matrix m
, real shear
)
529 identity_matrix (shear_m
);
530 shear_m
[1][0] = shear
;
531 mult_matrix (shear_m
, m
);
534 /* find the determinate for a 3x3 matrix */
536 determinate (Matrix m
)
541 for (i
= 0; i
< 3; i
++)
543 det
+= m
[0][i
] * m
[1][(i
+1)%3] * m
[2][(i
+2)%3];
544 det
-= m
[2][i
] * m
[1][(i
+1)%3] * m
[0][(i
+2)%3];
553 point_convex(Point
*dst
, const Point
*src1
, const Point
*src2
, real alpha
)
555 /* Make convex combination of src1 and src2:
556 dst = alpha * src1 + (1-alpha) * src2;
558 point_copy(dst
,src1
);
559 point_scale(dst
,alpha
);
560 point_add_scaled(dst
,src2
,1.0 - alpha
);
565 /* The following functions were originally taken from Graphics Gems III,
566 pg 193 and corresponding code pgs 496-499
568 They were modified to work within the dia coordinate system
570 /* return angle subtended by two vectors */
571 real
dot2(Point
*p1
, Point
*p2
)
574 d
= sqrt(((p1
->x
*p1
->x
)+(p1
->y
*p1
->y
)) *
575 ((p2
->x
*p2
->x
)+(p2
->y
*p2
->y
)));
578 t
= (p1
->x
*p2
->x
+p1
->y
*p2
->y
)/d
;
587 /* Find a,b,c in ax + by + c = 0 for line p1, p2 */
588 void line_coef(real
*a
, real
*b
, real
*c
, Point
*p1
, Point
*p2
)
590 *c
= ( p2
->x
* p1
->y
) - ( p1
->x
* p2
->y
);
595 /* Return signed distance from line
596 ax + by + c = 0 to point p. */
597 real
line_to_point(real a
, real b
, real c
, Point
*p
) {
599 d
= sqrt((a
*a
)+(b
*b
));
603 lp
= (a
*p
->x
+b
*p
->y
+c
)/d
;
608 /* Given line l = ax + by + c = 0 and point p,
609 compute x,y so point perp is perpendicular to line l. */
610 void point_perp(Point
*p
, real a
, real b
, real c
, Point
*perp
) {
617 perp
->x
= (-a
*c
-b
*cp
)/d
;
618 perp
->y
= (a
*cp
-b
*c
)/d
;
623 /* Compute a circular arc fillet between lines L1 (p1 to p2)
624 and L2 (p3 to p4) with radius r.
625 The circle center is c.
626 The start angle is pa
628 The points p1-p4 will be modified as necessary */
629 void fillet(Point
*p1
, Point
*p2
, Point
*p3
, Point
*p4
,
630 real r
, Point
*c
, real
*pa
, real
*aa
) {
631 real a1
, b1
, c1
; /* Coefficients for L1 */
632 real a2
, b2
, c2
; /* Coefficients for L2 */
636 real start_angle
, stop_angle
;
638 gboolean righthand
= FALSE
;
640 line_coef(&a1
,&b1
,&c1
,p1
,p2
);
641 line_coef(&a2
,&b2
,&c2
,p3
,p4
);
643 if ( (a1
*b2
) == (a2
*b1
) ) /* Parallel or coincident lines */
648 mp
.x
= (p3
->x
+ p4
->x
) / 2.0; /* Find midpoint of p3 p4 */
649 mp
.y
= (p3
->y
+ p4
->y
) / 2.0;
650 d1
= line_to_point(a1
, b1
, c1
, &mp
); /* Find distance p1 p2 to
652 if ( d1
== 0.0 ) return; /* p1p2 to p3 */
654 mp
.x
= (p1
->x
+ p2
->x
) / 2.0; /* Find midpoint of p1 p2 */
655 mp
.y
= (p1
->y
+ p2
->y
) / 2.0;
656 d2
= line_to_point(a2
, b2
, c2
, &mp
); /* Find distance p3 p4 to
658 if ( d2
== 0.0 ) return;
661 if ( d1
<= 0.0 ) rr
= -rr
;
663 c1p
= c1
-rr
*sqrt((a1
*a1
)+(b1
*b1
)); /* Line parallell l1 at d */
666 if ( d2
<= 0.0 ) rr
= -rr
;
667 c2p
= c2
-rr
*sqrt((a2
*a2
)+(b2
*b2
)); /* Line parallell l2 at d */
670 c
->x
= ( c2p
*b1
-c1p
*b2
) / d
; /* Intersect constructed lines */
671 c
->y
= ( c1p
*a2
-c2p
*a1
) / d
; /* to find center of arc */
673 point_perp(c
,a1
,b1
,c1
,p2
); /* Clip or extend lines as required */
674 point_perp(c
,a2
,b2
,c2
,p3
);
676 /* need to negate the y coords to calculate angles correctly */
677 gv1
.x
= p2
->x
-c
->x
; gv1
.y
= -(p2
->y
-c
->y
);
678 gv2
.x
= p3
->x
-c
->x
; gv2
.y
= -(p3
->y
-c
->y
);
680 start_angle
= atan2(gv1
.y
,gv1
.x
); /* Beginning angle for arc */
681 stop_angle
= dot2(&gv1
,&gv2
);
682 if ( point_cross(&gv1
,&gv2
) < 0.0 ) {
683 stop_angle
= -stop_angle
; /* Angle subtended by arc */
684 /* also this means we need to swap our angles later on */
687 /* now calculate the actual angles in a form that the draw arc function
688 of the renderer can use */
689 start_angle
= start_angle
*180.0/G_PI
;
690 stop_angle
= start_angle
+ stop_angle
*180.0/G_PI
;
691 while (start_angle
< 0.0) start_angle
+= 360.0;
692 while (stop_angle
< 0.0) stop_angle
+= 360.0;
693 /* swap the start and stop if we had to negate the cross product */
695 real tmp
= start_angle
;
696 start_angle
= stop_angle
;
705 /* moved this here since it is being reused by rounded polyline code*/
707 point_cross(Point
*p1
, Point
*p2
)
709 return p1
->x
* p2
->y
- p2
->x
* p1
->y
;
712 /* moved this here since it is used by line and bezier */
713 /* Computes one point between end and objmid which
714 * touches the edge of the object */
716 calculate_object_edge(Point
*objmid
, Point
*end
, DiaObject
*obj
)
720 Point trace
[MAXITER
];
721 real disttrace
[MAXITER
];
723 Point mid1
, mid2
, mid3
;
728 mid2
.x
= (objmid
->x
+end
->x
)/2;
729 mid2
.y
= (objmid
->y
+end
->y
)/2;
732 /* If the other end is inside the object */
733 dist
= obj
->ops
->distance_from(obj
, &mid3
);
734 if (dist
< 0.001) return mid1
;
738 dist
= obj
->ops
->distance_from(obj
, &mid2
);
739 if (dist
< 0.0000001) {
744 mid2
.x
= (mid1
.x
+ mid3
.x
)/2;
745 mid2
.y
= (mid1
.y
+ mid3
.y
)/2;
751 } while (i
< MAXITER
&& (dist
< 0.0000001 || dist
> 0.001));
755 for (i
= 0; i
< MAXITER
; i
++) {
756 printf("%d: %f, %f: %f\n", i
, trace
[i
].x
, trace
[i
].y
, disttrace
[i
]);
758 printf("i = %d, dist = %f\n", i
, dist
);