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 ... */
27 #if GLIB_CHECK_VERSION(2,7,0)
28 # define G_IMPLEMENT_INLINES
31 # define G_INLINE_FUNC extern
33 #define G_CAN_INLINE 1
38 rectangle_union(Rectangle
*r1
, const Rectangle
*r2
)
40 r1
->top
= MIN( r1
->top
, r2
->top
);
41 r1
->bottom
= MAX( r1
->bottom
, r2
->bottom
);
42 r1
->left
= MIN( r1
->left
, r2
->left
);
43 r1
->right
= MAX( r1
->right
, r2
->right
);
47 int_rectangle_union(IntRectangle
*r1
, const IntRectangle
*r2
)
49 r1
->top
= MIN( r1
->top
, r2
->top
);
50 r1
->bottom
= MAX( r1
->bottom
, r2
->bottom
);
51 r1
->left
= MIN( r1
->left
, r2
->left
);
52 r1
->right
= MAX( r1
->right
, r2
->right
);
56 rectangle_intersection(Rectangle
*r1
, const Rectangle
*r2
)
58 r1
->top
= MAX( r1
->top
, r2
->top
);
59 r1
->bottom
= MIN( r1
->bottom
, r2
->bottom
);
60 r1
->left
= MAX( r1
->left
, r2
->left
);
61 r1
->right
= MIN( r1
->right
, r2
->right
);
63 /* Is rectangle empty? */
64 if ( (r1
->top
>= r1
->bottom
) ||
65 (r1
->left
>= r1
->right
) ) {
66 r1
->top
= r1
->bottom
= r1
->left
= r1
->right
= 0.0;
71 rectangle_intersects(const Rectangle
*r1
, const Rectangle
*r2
)
73 if ( (r1
->right
< r2
->left
) ||
74 (r1
->left
> r2
->right
) ||
75 (r1
->top
> r2
->bottom
) ||
76 (r1
->bottom
< r2
->top
) )
83 point_in_rectangle(const Rectangle
* r
, const Point
*p
)
85 if ( (p
->x
< r
->left
) ||
95 rectangle_in_rectangle(const Rectangle
* outer
, const Rectangle
*inner
)
97 if ( (inner
->left
< outer
->left
) ||
98 (inner
->right
> outer
->right
) ||
99 (inner
->top
< outer
->top
) ||
100 (inner
->bottom
> outer
->bottom
))
107 rectangle_add_point(Rectangle
*r
, const Point
*p
)
111 else if (p
->x
> r
->right
)
116 else if (p
->y
> r
->bottom
)
122 * This function estimates the distance from a point to a rectangle.
123 * If the point is in the rectangle, 0.0 is returned. Otherwise the
124 * distance in a manhattan metric from the point to the nearest point
125 * on the rectangle is returned.
128 distance_rectangle_point(const Rectangle
*rect
, const Point
*point
)
133 if (point
->x
< rect
->left
) {
134 dx
= rect
->left
- point
->x
;
135 } else if (point
->x
> rect
->right
) {
136 dx
= point
->x
- rect
->right
;
139 if (point
->y
< rect
->top
) {
140 dy
= rect
->top
- point
->y
;
141 } else if (point
->y
> rect
->bottom
) {
142 dy
= point
->y
- rect
->bottom
;
149 * This function estimates the distance from a point to a line segment
150 * specified by two endpoints.
151 * If the point is on the line segment, 0.0 is returned. Otherwise the
152 * distance in the R^2 metric from the point to the nearest point
153 * on the line segment is returned. Does one sqrt per call.
154 * Philosophical bug: line_width is ignored iff point is beyond
155 * end of line segment.
158 distance_line_point(const Point
*line_start
, const Point
*line_end
,
159 real line_width
, const Point
*point
)
167 point_sub(&v1
,line_start
);
170 point_sub(&v2
, line_start
);
172 v1_lensq
= point_dot(&v1
,&v1
);
174 if (v1_lensq
<0.000001) {
175 return sqrt(point_dot(&v2
,&v2
));
178 projlen
= point_dot(&v1
,&v2
) / v1_lensq
;
181 return sqrt(point_dot(&v2
,&v2
));
186 point_sub(&v3
,line_end
);
187 return sqrt(point_dot(&v3
,&v3
));
190 point_scale(&v1
, projlen
);
193 perp_dist
= sqrt(point_dot(&v1
,&v1
));
195 perp_dist
-= line_width
/ 2.0;
196 if (perp_dist
< 0.0) {
203 /* returns 1 iff the line crosses the ray from (-Inf, rayend.y) to rayend */
205 line_crosses_ray(const Point
*line_start
,
206 const Point
*line_end
, const Point
*rayend
)
210 /* swap end points if necessary */
211 if (line_start
->y
> line_end
->y
) {
215 line_start
= line_end
;
218 /* if y coords of line do not include rayend.y */
219 if (line_start
->y
> rayend
->y
|| line_end
->y
< rayend
->y
)
221 /* Avoid division by zero for horizontal case */
222 if (line_end
->y
- line_start
->y
< 0.00000000001) {
223 return (line_end
->y
- rayend
->y
< 0.00000000001);
225 xpos
= line_start
->x
+ (rayend
->y
- line_start
->y
) *
226 (line_end
->x
- line_start
->x
) / (line_end
->y
- line_start
->y
);
227 return xpos
<= rayend
->x
;
231 distance_polygon_point(const Point
*poly
, guint npoints
, real line_width
,
234 guint i
, last
= npoints
- 1;
235 real line_dist
= G_MAXFLOAT
;
238 /* calculate ray crossings and line distances */
239 for (i
= 0; i
< npoints
; i
++) {
242 crossings
+= line_crosses_ray(&poly
[last
], &poly
[i
], point
);
243 dist
= distance_line_point(&poly
[last
], &poly
[i
], line_width
, point
);
244 line_dist
= MIN(line_dist
, dist
);
247 /* If there is an odd number of ray crossings, we are inside the polygon.
248 * Otherwise, return the minium distance from a line segment */
249 if (crossings
% 2 == 1)
255 /* number of segments to use in bezier curve approximation */
258 /* if cross is not NULL, it will be incremented for each ray crossing */
260 bez_point_distance_and_ray_crosses(const Point
*b1
,
261 const Point
*b2
, const Point
*b3
,
263 real line_width
, const Point
*point
,
266 static gboolean calculated_coeff
= FALSE
;
267 static real coeff
[NBEZ_SEGS
+1][4];
269 real line_dist
= G_MAXFLOAT
;
272 if (!calculated_coeff
) {
273 for (i
= 0; i
<= NBEZ_SEGS
; i
++) {
274 real t1
= ((real
)i
)/NBEZ_SEGS
, t2
= t1
*t1
, t3
= t1
*t2
;
275 real it1
= 1-t1
, it2
= it1
*it1
, it3
= it1
*it2
;
278 coeff
[i
][1] = 3 * t1
* it2
;
279 coeff
[i
][2] = 3 * t2
* it1
;
283 calculated_coeff
= TRUE
;
285 prev
.x
= coeff
[0][0] * b1
->x
+ coeff
[0][1] * b2
->x
+
286 coeff
[0][2] * b3
->x
+ coeff
[0][3] * b4
->x
;
287 prev
.y
= coeff
[0][0] * b1
->y
+ coeff
[0][1] * b2
->y
+
288 coeff
[0][2] * b3
->y
+ coeff
[0][3] * b4
->y
;
289 for (i
= 1; i
<= NBEZ_SEGS
; i
++) {
292 pt
.x
= coeff
[i
][0] * b1
->x
+ coeff
[i
][1] * b2
->x
+
293 coeff
[i
][2] * b3
->x
+ coeff
[i
][3] * b4
->x
;
294 pt
.y
= coeff
[i
][0] * b1
->y
+ coeff
[i
][1] * b2
->y
+
295 coeff
[i
][2] * b3
->y
+ coeff
[i
][3] * b4
->y
;
297 dist
= distance_line_point(&prev
, &pt
, line_width
, point
);
298 line_dist
= MIN(line_dist
, dist
);
300 *cross
+= line_crosses_ray(&prev
, &pt
, point
);
308 distance_bez_seg_point(const Point
*b1
, const Point
*b2
,
309 const Point
*b3
, const Point
*b4
,
310 real line_width
, const Point
*point
)
312 return bez_point_distance_and_ray_crosses(b1
, b2
, b3
, b4
,
313 line_width
, point
, NULL
);
317 distance_bez_line_point(const BezPoint
*b
, guint npoints
,
318 real line_width
, const Point
*point
)
322 real line_dist
= G_MAXFLOAT
;
324 g_return_val_if_fail(b
[0].type
== BEZ_MOVE_TO
, -1);
328 for (i
= 1; i
< npoints
; i
++) {
333 /*g_assert_not_reached();*/
334 g_warning("BEZ_MOVE_TO found half way through a bezier line");
337 dist
= distance_line_point(&last
, &b
[i
].p1
, line_width
, point
);
338 line_dist
= MIN(line_dist
, dist
);
342 dist
= bez_point_distance_and_ray_crosses(&last
, &b
[i
].p1
, &b
[i
].p2
,
343 &b
[i
].p3
, line_width
, point
,
345 line_dist
= MIN(line_dist
, dist
);
354 distance_bez_shape_point(const BezPoint
*b
, guint npoints
,
355 real line_width
, const Point
*point
)
359 real line_dist
= G_MAXFLOAT
;
362 g_return_val_if_fail(b
[0].type
== BEZ_MOVE_TO
, -1);
366 for (i
= 1; i
< npoints
; i
++) {
371 /*g_assert_not_reached();*/
372 g_warning("BEZ_MOVE_TO found half way through a bezier shape");
375 dist
= distance_line_point(&last
, &b
[i
].p1
, line_width
, point
);
376 crossings
+= line_crosses_ray(&last
, &b
[i
].p1
, point
);
377 line_dist
= MIN(line_dist
, dist
);
381 dist
= bez_point_distance_and_ray_crosses(&last
, &b
[i
].p1
, &b
[i
].p2
,
382 &b
[i
].p3
, line_width
, point
,
384 line_dist
= MIN(line_dist
, dist
);
389 /* If there is an odd number of ray crossings, we are inside the polygon.
390 * Otherwise, return the minium distance from a line segment */
391 if (crossings
% 2 == 1)
398 distance_ellipse_point(const Point
*centre
, real width
, real height
,
399 real line_width
, const Point
*point
)
401 /* A faster intersection method would be to scaled the ellipse and the
402 * point to where the ellipse is a circle, intersect with the circle,
405 real w2
= width
*width
, h2
= height
*height
;
406 real scale
, rad
, dist
;
409 /* find the radius of the ellipse in the appropriate direction */
411 /* find the point of intersection between line (x=cx+(px-cx)t; y=cy+(py-cy)t)
412 * and ellipse ((x-cx)^2)/(w/2)^2 + ((y-cy)^2)/(h/2)^2 = 1 */
413 /* radius along ray is sqrt((px-cx)^2 * t^2 + (py-cy)^2 * t^2) */
416 point_sub(&pt
, centre
);
418 pt
.y
*= pt
.y
; /* pt = (point - centre).^2 */
420 scale
= w2
* h2
/ (4*h2
*pt
.x
+ 4*w2
*pt
.y
);
421 rad
= sqrt((pt
.x
+ pt
.y
)*scale
) + line_width
/2;
423 dist
= sqrt(pt
.x
+ pt
.y
);
433 transform_point (Matrix m
, Point
*src
, Point
*dest
)
437 xx
= m
[0][0] * src
->x
+ m
[0][1] * src
->y
+ m
[0][2];
438 yy
= m
[1][0] * src
->x
+ m
[1][1] * src
->y
+ m
[1][2];
439 ww
= m
[2][0] * src
->x
+ m
[2][1] * src
->y
+ m
[2][2];
449 mult_matrix (Matrix m1
, Matrix m2
)
454 for (i
= 0; i
< 3; i
++)
455 for (j
= 0; j
< 3; j
++)
458 for (k
= 0; k
< 3; k
++)
459 result
[i
][j
] += m1
[i
][k
] * m2
[k
][j
];
462 /* copy the result into matrix 2 */
463 for (i
= 0; i
< 3; i
++)
464 for (j
= 0; j
< 3; j
++)
465 m2
[i
][j
] = result
[i
][j
];
469 identity_matrix (Matrix m
)
473 for (i
= 0; i
< 3; i
++)
474 for (j
= 0; j
< 3; j
++)
475 m
[i
][j
] = (i
== j
) ? 1 : 0;
480 translate_matrix (Matrix m
, real x
, real y
)
484 identity_matrix (trans
);
487 mult_matrix (trans
, m
);
491 scale_matrix (Matrix m
, real x
, real y
)
495 identity_matrix (scale
);
498 mult_matrix (scale
, m
);
502 rotate_matrix (Matrix m
, real theta
)
505 real cos_theta
, sin_theta
;
507 cos_theta
= cos (theta
);
508 sin_theta
= sin (theta
);
510 identity_matrix (rotate
);
511 rotate
[0][0] = cos_theta
;
512 rotate
[0][1] = -sin_theta
;
513 rotate
[1][0] = sin_theta
;
514 rotate
[1][1] = cos_theta
;
515 mult_matrix (rotate
, m
);
519 xshear_matrix (Matrix m
, real shear
)
523 identity_matrix (shear_m
);
524 shear_m
[0][1] = shear
;
525 mult_matrix (shear_m
, m
);
529 yshear_matrix (Matrix m
, real shear
)
533 identity_matrix (shear_m
);
534 shear_m
[1][0] = shear
;
535 mult_matrix (shear_m
, m
);
538 /* find the determinate for a 3x3 matrix */
540 determinate (Matrix m
)
545 for (i
= 0; i
< 3; i
++)
547 det
+= m
[0][i
] * m
[1][(i
+1)%3] * m
[2][(i
+2)%3];
548 det
-= m
[2][i
] * m
[1][(i
+1)%3] * m
[0][(i
+2)%3];
557 point_convex(Point
*dst
, const Point
*src1
, const Point
*src2
, real alpha
)
559 /* Make convex combination of src1 and src2:
560 dst = alpha * src1 + (1-alpha) * src2;
562 point_copy(dst
,src1
);
563 point_scale(dst
,alpha
);
564 point_add_scaled(dst
,src2
,1.0 - alpha
);
569 /* The following functions were originally taken from Graphics Gems III,
570 pg 193 and corresponding code pgs 496-499
572 They were modified to work within the dia coordinate system
574 /* return angle subtended by two vectors */
575 real
dot2(Point
*p1
, Point
*p2
)
578 d
= sqrt(((p1
->x
*p1
->x
)+(p1
->y
*p1
->y
)) *
579 ((p2
->x
*p2
->x
)+(p2
->y
*p2
->y
)));
582 t
= (p1
->x
*p2
->x
+p1
->y
*p2
->y
)/d
;
591 /* Find a,b,c in ax + by + c = 0 for line p1, p2 */
592 void line_coef(real
*a
, real
*b
, real
*c
, Point
*p1
, Point
*p2
)
594 *c
= ( p2
->x
* p1
->y
) - ( p1
->x
* p2
->y
);
599 /* Return signed distance from line
600 ax + by + c = 0 to point p. */
601 real
line_to_point(real a
, real b
, real c
, Point
*p
) {
603 d
= sqrt((a
*a
)+(b
*b
));
607 lp
= (a
*p
->x
+b
*p
->y
+c
)/d
;
612 /* Given line l = ax + by + c = 0 and point p,
613 compute x,y so point perp is perpendicular to line l. */
614 void point_perp(Point
*p
, real a
, real b
, real c
, Point
*perp
) {
621 perp
->x
= (-a
*c
-b
*cp
)/d
;
622 perp
->y
= (a
*cp
-b
*c
)/d
;
627 /* Compute a circular arc fillet between lines L1 (p1 to p2)
628 and L2 (p3 to p4) with radius r.
629 The circle center is c.
630 The start angle is pa
632 The points p1-p4 will be modified as necessary */
633 void fillet(Point
*p1
, Point
*p2
, Point
*p3
, Point
*p4
,
634 real r
, Point
*c
, real
*pa
, real
*aa
) {
635 real a1
, b1
, c1
; /* Coefficients for L1 */
636 real a2
, b2
, c2
; /* Coefficients for L2 */
640 real start_angle
, stop_angle
;
642 gboolean righthand
= FALSE
;
644 line_coef(&a1
,&b1
,&c1
,p1
,p2
);
645 line_coef(&a2
,&b2
,&c2
,p3
,p4
);
647 if ( (a1
*b2
) == (a2
*b1
) ) /* Parallel or coincident lines */
652 mp
.x
= (p3
->x
+ p4
->x
) / 2.0; /* Find midpoint of p3 p4 */
653 mp
.y
= (p3
->y
+ p4
->y
) / 2.0;
654 d1
= line_to_point(a1
, b1
, c1
, &mp
); /* Find distance p1 p2 to
656 if ( d1
== 0.0 ) return; /* p1p2 to p3 */
658 mp
.x
= (p1
->x
+ p2
->x
) / 2.0; /* Find midpoint of p1 p2 */
659 mp
.y
= (p1
->y
+ p2
->y
) / 2.0;
660 d2
= line_to_point(a2
, b2
, c2
, &mp
); /* Find distance p3 p4 to
662 if ( d2
== 0.0 ) return;
665 if ( d1
<= 0.0 ) rr
= -rr
;
667 c1p
= c1
-rr
*sqrt((a1
*a1
)+(b1
*b1
)); /* Line parallell l1 at d */
670 if ( d2
<= 0.0 ) rr
= -rr
;
671 c2p
= c2
-rr
*sqrt((a2
*a2
)+(b2
*b2
)); /* Line parallell l2 at d */
674 c
->x
= ( c2p
*b1
-c1p
*b2
) / d
; /* Intersect constructed lines */
675 c
->y
= ( c1p
*a2
-c2p
*a1
) / d
; /* to find center of arc */
677 point_perp(c
,a1
,b1
,c1
,p2
); /* Clip or extend lines as required */
678 point_perp(c
,a2
,b2
,c2
,p3
);
680 /* need to negate the y coords to calculate angles correctly */
681 gv1
.x
= p2
->x
-c
->x
; gv1
.y
= -(p2
->y
-c
->y
);
682 gv2
.x
= p3
->x
-c
->x
; gv2
.y
= -(p3
->y
-c
->y
);
684 start_angle
= atan2(gv1
.y
,gv1
.x
); /* Beginning angle for arc */
685 stop_angle
= dot2(&gv1
,&gv2
);
686 if ( point_cross(&gv1
,&gv2
) < 0.0 ) {
687 stop_angle
= -stop_angle
; /* Angle subtended by arc */
688 /* also this means we need to swap our angles later on */
691 /* now calculate the actual angles in a form that the draw arc function
692 of the renderer can use */
693 start_angle
= start_angle
*180.0/G_PI
;
694 stop_angle
= start_angle
+ stop_angle
*180.0/G_PI
;
695 while (start_angle
< 0.0) start_angle
+= 360.0;
696 while (stop_angle
< 0.0) stop_angle
+= 360.0;
697 /* swap the start and stop if we had to negate the cross product */
699 real tmp
= start_angle
;
700 start_angle
= stop_angle
;
709 /* moved this here since it is being reused by rounded polyline code*/
711 point_cross(Point
*p1
, Point
*p2
)
713 return p1
->x
* p2
->y
- p2
->x
* p1
->y
;
716 /* moved this here since it is used by line and bezier */
717 /* Computes one point between end and objmid which
718 * touches the edge of the object */
720 calculate_object_edge(Point
*objmid
, Point
*end
, DiaObject
*obj
)
724 Point trace
[MAXITER
];
725 real disttrace
[MAXITER
];
727 Point mid1
, mid2
, mid3
;
732 mid2
.x
= (objmid
->x
+end
->x
)/2;
733 mid2
.y
= (objmid
->y
+end
->y
)/2;
736 /* If the other end is inside the object */
737 dist
= obj
->ops
->distance_from(obj
, &mid3
);
738 if (dist
< 0.001) return mid1
;
742 dist
= obj
->ops
->distance_from(obj
, &mid2
);
743 if (dist
< 0.0000001) {
748 mid2
.x
= (mid1
.x
+ mid3
.x
)/2;
749 mid2
.y
= (mid1
.y
+ mid3
.y
)/2;
755 } while (i
< MAXITER
&& (dist
< 0.0000001 || dist
> 0.001));
759 for (i
= 0; i
< MAXITER
; i
++) {
760 printf("%d: %f, %f: %f\n", i
, trace
[i
].x
, trace
[i
].y
, disttrace
[i
]);
762 printf("i = %d, dist = %f\n", i
, dist
);