1 /*
2  * GooCanvas Demo. Copyright (C) 2007 Damon Chaplin.
3  * Released under the GNU LGPL license. See COPYING for details.
4  *
5  * demo-large-line.c - a demo item that exceeds the cairo 16-bit size limit.
6  *                     Note that it doesn't support miters.
7  */
8 #include <config.h>
9 #include "goocanvas.h"
10 #include "demo-large-line.h"
11 
12 
13 /* Use the GLib convenience macro to define the type. GooDemoLargeLine is the
14    class struct, goo_demo_large_line is the function prefix, and our class is a
15    subclass of GOO_TYPE_CANVAS_ITEM_SIMPLE. */
G_DEFINE_TYPE(GooDemoLargeLine,goo_demo_large_line,GOO_TYPE_CANVAS_ITEM_SIMPLE)16 G_DEFINE_TYPE (GooDemoLargeLine, goo_demo_large_line,
17 	       GOO_TYPE_CANVAS_ITEM_SIMPLE)
18 
19 
20 /* The standard object initialization function. */
21 static void
22 goo_demo_large_line_init (GooDemoLargeLine *demo_large_line)
23 {
24   demo_large_line->x1 = 0.0;
25   demo_large_line->y1 = 0.0;
26   demo_large_line->x2 = 0.0;
27   demo_large_line->y2 = 0.0;
28 }
29 
30 
31 /* The convenience function to create new items. This should start with a
32    parent argument and end with a variable list of object properties to fit
33    in with the standard canvas items. */
34 GooCanvasItem*
goo_demo_large_line_new(GooCanvasItem * parent,gdouble x1,gdouble y1,gdouble x2,gdouble y2,...)35 goo_demo_large_line_new (GooCanvasItem      *parent,
36 			 gdouble             x1,
37 			 gdouble             y1,
38 			 gdouble             x2,
39 			 gdouble             y2,
40 			 ...)
41 {
42   GooCanvasItem *item;
43   GooDemoLargeLine *demo_large_line;
44   const char *first_property;
45   va_list var_args;
46 
47   item = g_object_new (GOO_TYPE_DEMO_LARGE_LINE, NULL);
48 
49   demo_large_line = (GooDemoLargeLine*) item;
50   demo_large_line->x1 = x1;
51   demo_large_line->y1 = y1;
52   demo_large_line->x2 = x2;
53   demo_large_line->y2 = y2;
54 
55   va_start (var_args, y2);
56   first_property = va_arg (var_args, char*);
57   if (first_property)
58     g_object_set_valist ((GObject*) item, first_property, var_args);
59   va_end (var_args);
60 
61   if (parent)
62     {
63       goo_canvas_item_add_child (parent, item, -1);
64       g_object_unref (item);
65     }
66 
67   return item;
68 }
69 
70 
71 /* The update method. This is called when the canvas is initially shown and
72    also whenever the object is updated and needs to change its size and/or
73    shape. It should calculate its new bounds in its own coordinate space,
74    storing them in simple->bounds. */
75 static void
goo_demo_large_line_update(GooCanvasItemSimple * simple,cairo_t * cr)76 goo_demo_large_line_update  (GooCanvasItemSimple *simple,
77 			     cairo_t             *cr)
78 {
79   GooDemoLargeLine *item = (GooDemoLargeLine*) simple;
80   gdouble half_line_width;
81 
82   half_line_width = goo_canvas_item_simple_get_line_width (simple) / 2;
83 
84   /* Compute the new bounds. */
85   simple->bounds.x1 = MIN (item->x1, item->x2) - half_line_width;
86   simple->bounds.y1 = MIN (item->y1, item->y2) - half_line_width;
87   simple->bounds.x2 = MAX (item->x1, item->x2) + half_line_width;
88   simple->bounds.y2 = MAX (item->y1, item->y2) + half_line_width;
89 }
90 
91 
92 static void
clamp_x(gdouble point1[2],gdouble point2[2],gdouble clamp)93 clamp_x (gdouble point1[2],
94 	 gdouble point2[2],
95 	 gdouble clamp)
96 
97 {
98   gdouble fraction = (clamp - point1[0]) / (point2[0] - point1[0]);
99   point1[0] = clamp;
100   point1[1] += fraction * (point2[1] - point1[1]);
101 }
102 
103 
104 static void
clamp_y(gdouble point1[2],gdouble point2[2],gdouble clamp)105 clamp_y (gdouble point1[2],
106 	 gdouble point2[2],
107 	 gdouble clamp)
108 
109 {
110   gdouble fraction = (clamp - point1[1]) / (point2[1] - point1[1]);
111   point1[1] = clamp;
112   point1[0] += fraction * (point2[0] - point1[0]);
113 }
114 
115 
116 static void
paint_large_line(GooDemoLargeLine * line,cairo_t * cr,const GooCanvasBounds * bounds,gdouble line_width,gdouble x1,gdouble y1,gdouble x2,gdouble y2)117 paint_large_line (GooDemoLargeLine      *line,
118 		  cairo_t               *cr,
119 		  const GooCanvasBounds *bounds,
120 		  gdouble                line_width,
121 		  gdouble                x1,
122 		  gdouble                y1,
123 		  gdouble                x2,
124 		  gdouble                y2)
125 {
126   GooCanvasItem *item = (GooCanvasItem*) line;
127   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) line;
128   GooCanvas *canvas = simple->canvas;
129   GooCanvasBounds tmp_bounds = *bounds;
130   gdouble point1[2], point2[2], *p1, *p2;
131 
132   point1[0] = x1;
133   point1[1] = y1;
134   point2[0] = x2;
135   point2[1] = y2;
136 
137   /* Transform the coordinates to the canvas device space, so we can clamp
138      the line to the bounds to be painted. */
139   goo_canvas_convert_from_item_space (canvas, item, &point1[0], &point1[1]);
140   goo_canvas_convert_from_item_space (canvas, item, &point2[0], &point2[1]);
141 
142   /* Extend the bounds a bit to account for the line width. */
143   tmp_bounds.x1 -= line_width;
144   tmp_bounds.y1 -= line_width;
145   tmp_bounds.x2 += line_width;
146   tmp_bounds.y2 += line_width;
147 
148   /* Make p1 the left-most point. */
149   if (point1[0] < point2[0])
150     {
151       p1 = point1;
152       p2 = point2;
153     }
154   else
155     {
156       p1 = point2;
157       p2 = point1;
158     }
159 
160   /* Just return if the line is completely outside the bounds horizontally. */
161   if (p2[0] < tmp_bounds.x1 || p1[0] > tmp_bounds.x2)
162     return;
163 
164   /* Clamp each x coordinate to the bounds. */
165   if (p1[0] < tmp_bounds.x1)
166     clamp_x (p1, p2, tmp_bounds.x1);
167   if (p2[0] > tmp_bounds.x2)
168     clamp_x (p2, p1, tmp_bounds.x2);
169 
170   /* Now make p1 the top-most point. */
171   if (point1[1] < point2[1])
172     {
173       p1 = point1;
174       p2 = point2;
175     }
176   else
177     {
178       p1 = point2;
179       p2 = point1;
180     }
181 
182   /* Just return if the line is completely outside the bounds vertically. */
183   if (p2[1] < tmp_bounds.y1 || p1[1] > tmp_bounds.y2)
184     return;
185 
186   /* Clamp each y coordinate to the bounds. */
187   if (p1[1] < tmp_bounds.y1)
188     clamp_y (p1, p2, tmp_bounds.y1);
189   if (p2[1] > tmp_bounds.y2)
190     clamp_y (p2, p1, tmp_bounds.y2);
191 
192   /* Convert back to item space. */
193   goo_canvas_convert_to_item_space (canvas, item, &point1[0], &point1[1]);
194   goo_canvas_convert_to_item_space (canvas, item, &point2[0], &point2[1]);
195 
196   /* Draw the line. */
197   cairo_move_to (cr, point1[0], point1[1]);
198   cairo_line_to (cr, point2[0], point2[1]);
199   cairo_stroke (cr);
200 }
201 
202 
203 /* The paint method. This should draw the item on the given cairo_t, using
204    the item's own coordinate space. */
205 static void
goo_demo_large_line_paint(GooCanvasItemSimple * simple,cairo_t * cr,const GooCanvasBounds * bounds)206 goo_demo_large_line_paint (GooCanvasItemSimple   *simple,
207 			   cairo_t               *cr,
208 			   const GooCanvasBounds *bounds)
209 {
210   GooDemoLargeLine *item = (GooDemoLargeLine*) simple;
211   gdouble line_width;
212 
213   goo_canvas_style_set_stroke_options (simple->simple_data->style, cr);
214   line_width = goo_canvas_item_simple_get_line_width (simple);
215   paint_large_line (item, cr, bounds, line_width,
216 		    item->x1, item->y1, item->x2, item->y2);
217 }
218 
219 
220 /* Hit detection. This should check if the given coordinate (in the item's
221    coordinate space) is within the item. If it is it should return TRUE,
222    otherwise it should return FALSE. */
223 static gboolean
goo_demo_large_line_is_item_at(GooCanvasItemSimple * simple,gdouble x,gdouble y,cairo_t * cr,gboolean is_pointer_event)224 goo_demo_large_line_is_item_at (GooCanvasItemSimple *simple,
225 				gdouble              x,
226 				gdouble              y,
227 				cairo_t             *cr,
228 				gboolean             is_pointer_event)
229 {
230   /*GooDemoLargeLine *item = (GooDemoLargeLine*) simple;*/
231 
232   /* FIXME: Implement this. */
233 
234   return FALSE;
235 }
236 
237 
238 /* The class initialization function. Here we set the class' update(), paint()
239    and is_item_at() methods. */
240 static void
goo_demo_large_line_class_init(GooDemoLargeLineClass * klass)241 goo_demo_large_line_class_init (GooDemoLargeLineClass *klass)
242 {
243   GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
244 
245   simple_class->simple_update        = goo_demo_large_line_update;
246   simple_class->simple_paint         = goo_demo_large_line_paint;
247   simple_class->simple_is_item_at    = goo_demo_large_line_is_item_at;
248 }
249 
250 
251