source: trunk/third/libgnomecanvas/libgnomecanvas/gnome-canvas-util.c @ 18584

Revision 18584, 17.5 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18583, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
3 * All rights reserved.
4 *
5 * This file is part of the Gnome Library.
6 *
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * The Gnome Library 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 GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22/*
23  @NOTATION@
24 */
25/* Miscellaneous utility functions for the GnomeCanvas widget
26 *
27 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
28 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
29 *
30 *
31 * Author: Federico Mena <federico@nuclecu.unam.mx>
32 */
33
34#include <config.h>
35
36/* needed for M_PI_2 under 'gcc -ansi -predantic' on GNU/Linux */
37#ifndef _BSD_SOURCE
38#  define _BSD_SOURCE 1
39#endif
40#include <sys/types.h>
41
42#include <glib.h>
43#include <math.h>
44#include "gnome-canvas.h"
45#include "gnome-canvas-util.h"
46#include <libart_lgpl/art_uta.h>
47#include <libart_lgpl/art_svp.h>
48#include <libart_lgpl/art_svp_ops.h>
49#include <libart_lgpl/art_rgb.h>
50#include <libart_lgpl/art_rgb_svp.h>
51#include <libart_lgpl/art_uta_svp.h>
52#include <libart_lgpl/art_rect_svp.h>
53
54/**
55 * gnome_canvas_points_new:
56 * @num_points: The number of points to allocate space for in the array.
57 *
58 * Creates a structure that should be used to pass an array of points to
59 * items.
60 *
61 * Return value: A newly-created array of points.  It should be filled in
62 * by the user.
63 **/
64GnomeCanvasPoints *
65gnome_canvas_points_new (int num_points)
66{
67        GnomeCanvasPoints *points;
68
69        g_return_val_if_fail (num_points > 1, NULL);
70
71        points = g_new (GnomeCanvasPoints, 1);
72        points->num_points = num_points;
73        points->coords = g_new (double, 2 * num_points);
74        points->ref_count = 1;
75
76        return points;
77}
78
79/**
80 * gnome_canvas_points_ref:
81 * @points: A canvas points structure.
82 *
83 * Increases the reference count of the specified points structure.
84 *
85 * Return value: The canvas points structure itself.
86 **/
87GnomeCanvasPoints *
88gnome_canvas_points_ref (GnomeCanvasPoints *points)
89{
90        g_return_val_if_fail (points != NULL, NULL);
91
92        points->ref_count += 1;
93        return points;
94}
95
96/**
97 * gnome_canvas_points_free:
98 * @points: A canvas points structure.
99 *
100 * Decreases the reference count of the specified points structure.  If it
101 * reaches zero, then the structure is freed.
102 **/
103void
104gnome_canvas_points_free (GnomeCanvasPoints *points)
105{
106        g_return_if_fail (points != NULL);
107
108        points->ref_count -= 1;
109        if (points->ref_count == 0) {
110                g_free (points->coords);
111                g_free (points);
112        }
113}
114
115/**
116 * gnome_canvas_get_miter_points:
117 * @x1: X coordinate of the first point
118 * @y1: Y coordinate of the first point
119 * @x2: X coordinate of the second (angle) point
120 * @y2: Y coordinate of the second (angle) point
121 * @x3: X coordinate of the third point
122 * @y3: Y coordinate of the third point
123 * @width: Width of the line
124 * @mx1: The X coordinate of the first miter point is returned here.
125 * @my1: The Y coordinate of the first miter point is returned here.
126 * @mx2: The X coordinate of the second miter point is returned here.
127 * @my2: The Y coordinate of the second miter point is returned here.
128 *
129 * Given three points forming an angle, computes the coordinates of the inside
130 * and outside points of the mitered corner formed by a line of a given width at
131 * that angle.
132 *
133 * Return value: FALSE if the angle is less than 11 degrees (this is the same
134 * threshold as X uses.  If this occurs, the return points are not modified.
135 * Otherwise, returns TRUE.
136 **/
137int
138gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
139                               double width,
140                               double *mx1, double *my1, double *mx2, double *my2)
141{
142        double theta1;          /* angle of segment p2-p1 */
143        double theta2;          /* angle of segment p2-p3 */
144        double theta;           /* angle between line segments */
145        double theta3;          /* angle that bisects theta1 and theta2 and points to p1 */
146        double dist;            /* distance of miter points from p2 */
147        double dx, dy;          /* x and y offsets corresponding to dist */
148
149#define ELEVEN_DEGREES (11.0 * G_PI / 180.0)
150
151        if (y2 == y1)
152                theta1 = (x2 < x1) ? 0.0 : G_PI;
153        else if (x2 == x1)
154                theta1 = (y2 < y1) ? G_PI_2 : -G_PI_2;
155        else
156                theta1 = atan2 (y1 - y2, x1 - x2);
157
158        if (y3 == y2)
159                theta2 = (x3 > x2) ? 0 : G_PI;
160        else if (x3 == x2)
161                theta2 = (y3 > y2) ? G_PI_2 : -G_PI_2;
162        else
163                theta2 = atan2 (y3 - y2, x3 - x2);
164
165        theta = theta1 - theta2;
166
167        if (theta > G_PI)
168                theta -= 2.0 * G_PI;
169        else if (theta < -G_PI)
170                theta += 2.0 * G_PI;
171
172        if ((theta < ELEVEN_DEGREES) && (theta > -ELEVEN_DEGREES))
173                return FALSE;
174
175        dist = 0.5 * width / sin (0.5 * theta);
176        if (dist < 0.0)
177                dist = -dist;
178
179        theta3 = (theta1 + theta2) / 2.0;
180        if (sin (theta3 - (theta1 + G_PI)) < 0.0)
181                theta3 += G_PI;
182
183        dx = dist * cos (theta3);
184        dy = dist * sin (theta3);
185
186        *mx1 = x2 + dx;
187        *mx2 = x2 - dx;
188        *my1 = y2 + dy;
189        *my2 = y2 - dy;
190
191        return TRUE;
192}
193
194/**
195 * gnome_canvas_get_butt_points:
196 * @x1: X coordinate of first point in the line
197 * @y1: Y cooordinate of first point in the line
198 * @x2: X coordinate of second point (endpoint) of the line
199 * @y2: Y coordinate of second point (endpoint) of the line
200 * @width: Width of the line
201 * @project: Whether the butt points should project out by width/2 distance
202 * @bx1: X coordinate of first butt point is returned here
203 * @by1: Y coordinate of first butt point is returned here
204 * @bx2: X coordinate of second butt point is returned here
205 * @by2: Y coordinate of second butt point is returned here
206 *
207 * Computes the butt points of a line segment.
208 **/
209void
210gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
211                              double width, int project,
212                              double *bx1, double *by1, double *bx2, double *by2)
213{
214        double length;
215        double dx, dy;
216
217        width *= 0.5;
218        dx = x2 - x1;
219        dy = y2 - y1;
220        length = sqrt (dx * dx + dy * dy);
221
222        if (length < GNOME_CANVAS_EPSILON) {
223                *bx1 = *bx2 = x2;
224                *by1 = *by2 = y2;
225        } else {
226                dx = -width * (y2 - y1) / length;
227                dy = width * (x2 - x1) / length;
228
229                *bx1 = x2 + dx;
230                *bx2 = x2 - dx;
231                *by1 = y2 + dy;
232                *by2 = y2 - dy;
233
234                if (project) {
235                        *bx1 += dy;
236                        *bx2 += dy;
237                        *by1 -= dx;
238                        *by2 -= dx;
239                }
240        }
241}
242
243/**
244 * gnome_canvas_polygon_to_point:
245 * @poly: Vertices of the polygon.  X coordinates are in the even indices, and Y
246 * coordinates are in the odd indices
247 * @num_points: Number of points in the polygon
248 * @x: X coordinate of the point
249 * @y: Y coordinate of the point
250 *
251 * Computes the distance between a point and a polygon.
252 *
253 * Return value: The distance from the point to the polygon, or zero if the
254 * point is inside the polygon.
255 **/
256double
257gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
258{
259        double best;
260        int intersections;
261        int i;
262        double *p;
263        double dx, dy;
264
265        /* Iterate through all the edges in the polygon, updating best and intersections.
266         *
267         * When computing intersections, include left X coordinate of line within its range, but not
268         * Y coordinate.  Otherwise if the point lies exactly below a vertex we'll count it as two
269         * intersections.
270         */
271
272        best = 1.0e36;
273        intersections = 0;
274
275        for (i = num_points, p = poly; i > 1; i--, p += 2) {
276                double px, py, dist;
277
278                /* Compute the point on the current edge closest to the point and update the
279                 * intersection count.  This must be done separately for vertical edges, horizontal
280                 * edges, and others.
281                 */
282
283                if (p[2] == p[0]) {
284                        /* Vertical edge */
285
286                        px = p[0];
287
288                        if (p[1] >= p[3]) {
289                                py = MIN (p[1], y);
290                                py = MAX (py, p[3]);
291                        } else {
292                                py = MIN (p[3], y);
293                                py = MAX (py, p[1]);
294                        }
295                } else if (p[3] == p[1]) {
296                        /* Horizontal edge */
297
298                        py = p[1];
299
300                        if (p[0] >= p[2]) {
301                                px = MIN (p[0], x);
302                                px = MAX (px, p[2]);
303
304                                if ((y < py) && (x < p[0]) && (x >= p[2]))
305                                        intersections++;
306                        } else {
307                                px = MIN (p[2], x);
308                                px = MAX (px, p[0]);
309
310                                if ((y < py) && (x < p[2]) && (x >= p[0]))
311                                        intersections++;
312                        }
313                } else {
314                        double m1, b1, m2, b2;
315                        int lower;
316
317                        /* Diagonal edge.  Convert the edge to a line equation (y = m1*x + b1), then
318                         * compute a line perpendicular to this edge but passing through the point,
319                         * (y = m2*x + b2).
320                         */
321
322                        m1 = (p[3] - p[1]) / (p[2] - p[0]);
323                        b1 = p[1] - m1 * p[0];
324
325                        m2 = -1.0 / m1;
326                        b2 = y - m2 * x;
327
328                        px = (b2 - b1) / (m1 - m2);
329                        py = m1 * px + b1;
330
331                        if (p[0] > p[2]) {
332                                if (px > p[0]) {
333                                        px = p[0];
334                                        py = p[1];
335                                } else if (px < p[2]) {
336                                        px = p[2];
337                                        py = p[3];
338                                }
339                        } else {
340                                if (px > p[2]) {
341                                        px = p[2];
342                                        py = p[3];
343                                } else if (px < p[0]) {
344                                        px = p[0];
345                                        py = p[1];
346                                }
347                        }
348
349                        lower = (m1 * x + b1) > y;
350
351                        if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
352                                intersections++;
353                }
354
355                /* Compute the distance to the closest point, and see if that is the best so far */
356
357                dx = x - px;
358                dy = y - py;
359                dist = sqrt (dx * dx + dy * dy);
360                if (dist < best)
361                        best = dist;
362        }
363
364        /* We've processed all the points.  If the number of intersections is odd, the point is
365         * inside the polygon.
366         */
367
368        if (intersections & 0x1)
369                return 0.0;
370        else
371                return best;
372}
373
374/* Here are some helper functions for aa rendering: */
375
376/**
377 * gnome_canvas_render_svp:
378 * @buf: the canvas buffer to render over
379 * @svp: the vector path to render
380 * @rgba: the rgba color to render
381 *
382 * Render the svp over the buf.
383 **/
384void
385gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
386{
387        guint32 fg_color, bg_color;
388        int alpha;
389
390        if (buf->is_bg) {
391                bg_color = buf->bg_color;
392                alpha = rgba & 0xff;
393                if (alpha == 0xff)
394                        fg_color = rgba >> 8;
395                else {
396                        /* composite over background color */
397                        int bg_r, bg_g, bg_b;
398                        int fg_r, fg_g, fg_b;
399                        int tmp;
400
401                        bg_r = (bg_color >> 16) & 0xff;
402                        fg_r = (rgba >> 24) & 0xff;
403                        tmp = (fg_r - bg_r) * alpha;
404                        fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
405
406                        bg_g = (bg_color >> 8) & 0xff;
407                        fg_g = (rgba >> 16) & 0xff;
408                        tmp = (fg_g - bg_g) * alpha;
409                        fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
410
411                        bg_b = bg_color & 0xff;
412                        fg_b = (rgba >> 8) & 0xff;
413                        tmp = (fg_b - bg_b) * alpha;
414                        fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
415
416                        fg_color = (fg_r << 16) | (fg_g << 8) | fg_b;
417                }
418                art_rgb_svp_aa (svp,
419                                buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
420                                fg_color, bg_color,
421                                buf->buf, buf->buf_rowstride,
422                                NULL);
423                buf->is_bg = 0;
424                buf->is_buf = 1;
425        } else {
426                art_rgb_svp_alpha (svp,
427                                   buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
428                                   rgba,
429                                   buf->buf, buf->buf_rowstride,
430                                   NULL);
431        }
432}
433
434/**
435 * gnome_canvas_update_svp:
436 * @canvas: the canvas containing the svp that needs updating.
437 * @p_svp: a pointer to the existing svp
438 * @new_svp: the new svp
439 *
440 * Sets the svp to the new value, requesting repaint on what's changed. This
441 * function takes responsibility for freeing new_svp.
442 **/
443void
444gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp)
445{
446        ArtSVP *old_svp;
447        ArtSVP *diff G_GNUC_UNUSED;
448        ArtUta *repaint_uta;
449
450        old_svp = *p_svp;
451
452        if (old_svp != NULL) {
453                ArtDRect bb;
454                art_drect_svp (&bb, old_svp);
455                if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
456                        repaint_uta = art_uta_from_svp (old_svp);
457                        gnome_canvas_request_redraw_uta (canvas, repaint_uta);
458                } else {
459                        ArtIRect ib;
460                        art_drect_to_irect (&ib, &bb);
461                        gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
462                }
463                art_svp_free (old_svp);
464        }
465
466        if (new_svp != NULL) {
467                ArtDRect bb;
468                art_drect_svp (&bb, new_svp);
469                if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
470                        repaint_uta = art_uta_from_svp (new_svp);
471                        gnome_canvas_request_redraw_uta (canvas, repaint_uta);
472                } else {
473                        ArtIRect ib;
474                        art_drect_to_irect (&ib, &bb);
475                        gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
476                }
477        }
478
479        *p_svp = new_svp;
480}
481
482/**
483 * gnome_canvas_update_svp_clip:
484 * @canvas: the canvas containing the svp that needs updating.
485 * @p_svp: a pointer to the existing svp
486 * @new_svp: the new svp
487 * @clip_svp: a clip path, if non-null
488 *
489 * Sets the svp to the new value, clipping if necessary, and requesting repaint
490 * on what's changed. This function takes responsibility for freeing new_svp.
491 **/
492void
493gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp, ArtSVP *clip_svp)
494{
495        ArtSVP *clipped_svp;
496
497        if (clip_svp != NULL) {
498                clipped_svp = art_svp_intersect (new_svp, clip_svp);
499                art_svp_free (new_svp);
500        } else {
501                clipped_svp = new_svp;
502        }
503        gnome_canvas_update_svp (canvas, p_svp, clipped_svp);
504}
505
506/**
507 * gnome_canvas_item_reset_bounds:
508 * @item: A canvas item
509 *
510 * Resets the bounding box of a canvas item to an empty rectangle.
511 **/
512void
513gnome_canvas_item_reset_bounds (GnomeCanvasItem *item)
514{
515        item->x1 = 0.0;
516        item->y1 = 0.0;
517        item->x2 = 0.0;
518        item->y2 = 0.0;
519}
520
521/**
522 * gnome_canvas_item_update_svp:
523 * @item: the canvas item containing the svp that needs updating.
524 * @p_svp: a pointer to the existing svp
525 * @new_svp: the new svp
526 *
527 * Sets the svp to the new value, requesting repaint on what's changed. This
528 * function takes responsibility for freeing new_svp. This routine also adds the
529 * svp's bbox to the item's.
530 **/
531void
532gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp)
533{
534        ArtDRect bbox;
535
536        gnome_canvas_update_svp (item->canvas, p_svp, new_svp);
537        if (new_svp) {
538                bbox.x0 = item->x1;
539                bbox.y0 = item->y1;
540                bbox.x1 = item->x2;
541                bbox.y1 = item->y2;
542                art_drect_svp_union (&bbox, new_svp);
543                item->x1 = bbox.x0;
544                item->y1 = bbox.y0;
545                item->x2 = bbox.x1;
546                item->y2 = bbox.y1;
547        }
548}
549
550/**
551 * gnome_canvas_item_update_svp_clip:
552 * @item: the canvas item containing the svp that needs updating.
553 * @p_svp: a pointer to the existing svp
554 * @new_svp: the new svp
555 * @clip_svp: a clip path, if non-null
556 *
557 * Sets the svp to the new value, clipping if necessary, and requesting repaint
558 * on what's changed. This function takes responsibility for freeing new_svp.
559 **/
560void
561gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
562                                   ArtSVP *clip_svp)
563{
564        ArtSVP *clipped_svp;
565
566        if (clip_svp != NULL) {
567                clipped_svp = art_svp_intersect (new_svp, clip_svp);
568                art_svp_free (new_svp);
569        } else {
570                clipped_svp = new_svp;
571        }
572
573        gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
574}
575
576/**
577 * gnome_canvas_item_request_redraw_svp
578 * @item: the item containing the svp
579 * @svp: the svp that needs to be redrawn
580 *
581 * Request redraw of the svp if in aa mode, or the entire item in in xlib mode.
582 **/
583void
584gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp)
585{
586        GnomeCanvas *canvas;
587        ArtUta *uta;
588
589        canvas = item->canvas;
590        if (canvas->aa) {
591                if (svp != NULL) {
592                        uta = art_uta_from_svp (svp);
593                        gnome_canvas_request_redraw_uta (canvas, uta);
594                }
595        } else {
596                gnome_canvas_request_redraw (canvas, item->x1, item->y1, item->x2, item->y2);           
597        }
598}
599
600/**
601 * gnome_canvas_update_bbox:
602 * @item: the canvas item needing update
603 * @x1: Left coordinate of the new bounding box
604 * @y1: Top coordinate of the new bounding box
605 * @x2: Right coordinate of the new bounding box
606 * @y2: Bottom coordinate of the new bounding box
607 *
608 * Sets the bbox to the new value, requesting full repaint.
609 **/
610void
611gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2)
612{
613        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
614        item->x1 = x1;
615        item->y1 = y1;
616        item->x2 = x2;
617        item->y2 = y2;
618        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
619}
620
621/**
622 * gnome_canvas_buf_ensure_buf:
623 * @buf: the buf that needs to be represened in RGB format
624 *
625 * Ensure that the buffer is in RGB format, suitable for compositing.
626 **/
627void
628gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
629{
630        guchar *bufptr;
631        int y;
632
633        if (!buf->is_buf) {
634                bufptr = buf->buf;
635                for (y = buf->rect.y0; y < buf->rect.y1; y++) {
636                        art_rgb_fill_run (bufptr,
637                                          buf->bg_color >> 16,
638                                          (buf->bg_color >> 8) & 0xff,
639                                          buf->bg_color & 0xff,
640                                          buf->rect.x1 - buf->rect.x0);
641                        bufptr += buf->buf_rowstride;
642                }
643                buf->is_buf = 1;
644        }
645}
646
647/**
648 * gnome_canvas_join_gdk_to_art
649 * @gdk_join: a join type, represented in GDK format
650 *
651 * Convert from GDK line join specifier to libart.
652 *
653 * Return value: The line join specifier in libart format.
654 **/
655ArtPathStrokeJoinType
656gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join)
657{
658        switch (gdk_join) {
659        case GDK_JOIN_MITER:
660                return ART_PATH_STROKE_JOIN_MITER;
661
662        case GDK_JOIN_ROUND:
663                return ART_PATH_STROKE_JOIN_ROUND;
664
665        case GDK_JOIN_BEVEL:
666                return ART_PATH_STROKE_JOIN_BEVEL;
667
668        default:
669                g_assert_not_reached ();
670                return ART_PATH_STROKE_JOIN_MITER; /* shut up the compiler */
671        }
672}
673
674/**
675 * gnome_canvas_cap_gdk_to_art
676 * @gdk_cap: a cap type, represented in GDK format
677 *
678 * Convert from GDK line cap specifier to libart.
679 *
680 * Return value: The line cap specifier in libart format.
681 **/
682ArtPathStrokeCapType
683gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap)
684{
685        switch (gdk_cap) {
686        case GDK_CAP_BUTT:
687        case GDK_CAP_NOT_LAST:
688                return ART_PATH_STROKE_CAP_BUTT;
689
690        case GDK_CAP_ROUND:
691                return ART_PATH_STROKE_CAP_ROUND;
692
693        case GDK_CAP_PROJECTING:
694                return ART_PATH_STROKE_CAP_SQUARE;
695
696        default:
697                g_assert_not_reached ();
698                return ART_PATH_STROKE_CAP_BUTT; /* shut up the compiler */
699        }
700}
Note: See TracBrowser for help on using the repository browser.