1 /*
2  * goc-circle.c :
3  *
4  * Copyright (C) 2009 Jean Brefort (jean.brefort@normalesup.org)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24 #include <gsf/gsf-impl-utils.h>
25 
26 #include <glib/gi18n-lib.h>
27 #include <gsf/gsf-impl-utils.h>
28 
29 /**
30  * SECTION:goc-circle
31  * @short_description: Circle.
32  *
33  * #GocCircle implements circle drawing in the canvas.
34 **/
35 
36 static GocItemClass *parent_class;
37 
38 enum {
39 	CIRCLE_PROP_0,
40 	CIRCLE_PROP_X,
41 	CIRCLE_PROP_Y,
42 	CIRCLE_PROP_R
43 };
44 
45 static void
goc_circle_set_property(GObject * gobject,guint param_id,GValue const * value,GParamSpec * pspec)46 goc_circle_set_property (GObject *gobject, guint param_id,
47 				    GValue const *value, GParamSpec *pspec)
48 {
49 	GocCircle *circle = GOC_CIRCLE (gobject);
50 
51 	switch (param_id) {
52 	case CIRCLE_PROP_X:
53 		circle->x = g_value_get_double (value);
54 		break;
55 
56 	case CIRCLE_PROP_Y:
57 		circle->y = g_value_get_double (value);
58 		break;
59 
60 	case CIRCLE_PROP_R:
61 		circle->radius = g_value_get_double (value);
62 		break;
63 
64 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
65 		return; /* NOTE : RETURN */
66 	}
67 	goc_item_bounds_changed (GOC_ITEM (gobject));
68 }
69 
70 static void
goc_circle_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)71 goc_circle_get_property (GObject *gobject, guint param_id,
72 				    GValue *value, GParamSpec *pspec)
73 {
74 	GocCircle *circle = GOC_CIRCLE (gobject);
75 
76 	switch (param_id) {
77 	case CIRCLE_PROP_X:
78 		g_value_set_double (value, circle->x);
79 		break;
80 
81 	case CIRCLE_PROP_Y:
82 		g_value_set_double (value, circle->y);
83 		break;
84 
85 	case CIRCLE_PROP_R:
86 		g_value_set_double (value, circle->radius);
87 		break;
88 
89 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
90 		return; /* NOTE : RETURN */
91 	}
92 }
93 
94 static double
goc_circle_outline_extra_radius(GocItem * item)95 goc_circle_outline_extra_radius (GocItem *item)
96 {
97 	GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (item));
98 
99 	if (style->line.dash_type == GO_LINE_NONE)
100 		return 0;
101 
102 	/* FIXME take transform into account */
103 	return style->line.width > 0 ? style->line.width / 2. : .5;
104 }
105 
106 static double
goc_circle_distance(GocItem * item,double x,double y,GocItem ** near_item)107 goc_circle_distance (GocItem *item, double x, double y, GocItem **near_item)
108 {
109 	GocCircle *circle = GOC_CIRCLE (item);
110 	double d, extra_dist = goc_circle_outline_extra_radius (item);
111 	*near_item = item;
112 	x -= circle->x;
113 	y -= circle->y;
114 	d = sqrt (x * x + y * y);
115 	return MAX (d - circle->radius - extra_dist, 0);
116 }
117 
118 static void
goc_circle_draw(GocItem const * item,cairo_t * cr)119 goc_circle_draw (GocItem const *item, cairo_t *cr)
120 {
121 	GocCircle *circle = GOC_CIRCLE (item);
122 	double scale = (circle->radius > 0.)? circle->radius: 1.e-10;
123 
124 	cairo_save (cr);
125 	_goc_item_transform (item, cr, TRUE);
126 	goc_group_cairo_transform (item->parent, cr, circle->x, circle->y);
127 	cairo_scale (cr, scale, scale);
128 	cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI);
129 	cairo_restore (cr);
130 	/* Fill the shape */
131 	go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
132 	/* Draw the line */
133 	if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr))
134 		cairo_stroke (cr);
135 	else
136 		cairo_new_path (cr);
137 }
138 
139 static void
goc_circle_update_bounds(GocItem * item)140 goc_circle_update_bounds (GocItem *item)
141 {
142 	GocCircle *circle = GOC_CIRCLE (item);
143 	double r = circle->radius + goc_circle_outline_extra_radius (item);
144 	item->x0 = circle->x - r;
145 	item->y0 = circle->y - r;
146 	item->x1 = circle->x + r;
147 	item->y1 = circle->y + r;
148 }
149 
150 static void
goc_circle_copy(GocItem * dest,GocItem * source)151 goc_circle_copy (GocItem *dest, GocItem *source)
152 {
153 	GocCircle *src = GOC_CIRCLE (source), *dst = GOC_CIRCLE (dest);
154 
155 	dst->x = src->x;
156 	dst->y = src->y;
157 	dst->radius = src->radius;
158 	parent_class->copy (dest, source);
159 }
160 
161 static void
goc_circle_init_style(G_GNUC_UNUSED GocStyledItem * item,GOStyle * style)162 goc_circle_init_style (G_GNUC_UNUSED GocStyledItem *item, GOStyle *style)
163 {
164 	style->interesting_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
165 	if (style->line.auto_dash)
166 		style->line.dash_type = GO_LINE_SOLID;
167 	if (style->line.auto_color)
168 		style->line.color = GO_COLOR_BLACK;
169 	if (style->fill.auto_type)
170 		style->fill.type  = GO_STYLE_FILL_PATTERN;
171 	if (style->fill.auto_fore)
172 		style->fill.pattern.fore = GO_COLOR_BLACK;
173 	if (style->fill.auto_back)
174 		style->fill.pattern.back = GO_COLOR_WHITE;
175 }
176 
177 static void
goc_circle_class_init(GocItemClass * item_klass)178 goc_circle_class_init (GocItemClass *item_klass)
179 {
180 	GObjectClass *obj_klass = (GObjectClass *) item_klass;
181 	GocStyledItemClass *gsi_klass = (GocStyledItemClass *) item_klass;
182 	parent_class = g_type_class_peek_parent (item_klass);
183 
184 	obj_klass->get_property = goc_circle_get_property;
185 	obj_klass->set_property = goc_circle_set_property;
186 	g_object_class_install_property (obj_klass, CIRCLE_PROP_X,
187 		g_param_spec_double ("x",
188 			_("x"),
189 			_("The circle center horizontal position"),
190 			-G_MAXDOUBLE, G_MAXDOUBLE, 0.,
191 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
192 	g_object_class_install_property (obj_klass, CIRCLE_PROP_Y,
193 		g_param_spec_double ("y",
194 			_("y"),
195 			_("The circle center vertical position"),
196 			-G_MAXDOUBLE, G_MAXDOUBLE, 0.,
197 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
198 	g_object_class_install_property (obj_klass, CIRCLE_PROP_R,
199 		g_param_spec_double ("radius",
200 			_("Radius"),
201 			_("The circle radius"),
202 			0., G_MAXDOUBLE, 0.,
203 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
204 
205 	gsi_klass->init_style = goc_circle_init_style;
206 
207 	item_klass->distance = goc_circle_distance;
208 	item_klass->draw = goc_circle_draw;
209 	item_klass->copy = goc_circle_copy;
210 	item_klass->update_bounds = goc_circle_update_bounds;
211 }
212 
213 GSF_CLASS (GocCircle, goc_circle,
214 	   goc_circle_class_init, NULL,
215 	   GOC_TYPE_STYLED_ITEM)
216