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