* added GDK_PIXBUF_LIBS in order to create pixbuf.dll
[dia.git] / lib / polyshape.c
bloba0b5e0f7bed56696199180f5f5f74aa502bae79f
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <assert.h>
24 #include <math.h>
25 #include <string.h> /* memcpy() */
27 #include "polyshape.h"
28 #include "message.h"
29 #include "diarenderer.h"
31 enum change_type {
32 TYPE_ADD_POINT,
33 TYPE_REMOVE_POINT
36 struct PointChange {
37 ObjectChange obj_change;
39 enum change_type type;
40 int applied;
42 Point point;
43 int pos;
45 Handle *handle; /* owning ref when not applied for ADD_POINT
46 owning ref when applied for REMOVE_POINT */
47 ConnectionPoint *cp1, *cp2;
50 static ObjectChange *
51 polyshape_create_change(PolyShape *poly, enum change_type type,
52 Point *point, int segment, Handle *handle,
53 ConnectionPoint *cp1, ConnectionPoint *cp2);
55 static void setup_handle(Handle *handle)
57 handle->id = HANDLE_CORNER;
58 handle->type = HANDLE_MAJOR_CONTROL;
59 handle->connect_type = HANDLE_NONCONNECTABLE;
60 handle->connected_to = NULL;
64 static int get_handle_nr(PolyShape *poly, Handle *handle)
66 int i = 0;
67 for (i=0;i<poly->numpoints;i++) {
68 if (poly->object.handles[i] == handle)
69 return i;
71 return -1;
74 void
75 polyshape_move_handle(PolyShape *poly, Handle *handle,
76 Point *to, HandleMoveReason reason)
78 int handle_nr;
80 handle_nr = get_handle_nr(poly, handle);
81 poly->points[handle_nr] = *to;
84 void
85 polyshape_move(PolyShape *poly, Point *to)
87 Point p;
88 int i;
90 p = *to;
91 point_sub(&p, &poly->points[0]);
93 poly->points[0] = *to;
94 for (i=1;i<poly->numpoints;i++) {
95 point_add(&poly->points[i], &p);
99 int
100 polyshape_closest_segment(PolyShape *poly, Point *point, real line_width)
102 int i;
103 real dist;
104 int closest;
106 dist = distance_line_point( &poly->points[poly->numpoints-1], &poly->points[0],
107 line_width, point);
108 closest = poly->numpoints-1;
109 for (i=0;i<poly->numpoints-1;i++) {
110 real new_dist =
111 distance_line_point( &poly->points[i], &poly->points[i+1],
112 line_width, point);
113 if (new_dist < dist) {
114 dist = new_dist;
115 closest = i;
118 return closest;
121 Handle *
122 polyshape_closest_handle(PolyShape *poly, Point *point)
124 int i;
125 real dist;
126 Handle *closest;
128 closest = poly->object.handles[0];
129 dist = distance_point_point( point, &closest->pos);
130 for (i=1;i<poly->numpoints;i++) {
131 real new_dist;
132 new_dist = distance_point_point( point, &poly->points[i]);
133 if (new_dist < dist) {
134 dist = new_dist;
135 closest = poly->object.handles[i];
138 return closest;
141 real
142 polyshape_distance_from(PolyShape *poly, Point *point, real line_width)
144 return distance_polygon_point(poly->points, poly->numpoints,
145 line_width, point);
148 static void
149 add_handle(PolyShape *poly, int pos, Point *point, Handle *handle,
150 ConnectionPoint *cp1, ConnectionPoint *cp2)
152 int i;
153 Object *obj;
155 poly->numpoints++;
156 poly->points = g_realloc(poly->points, poly->numpoints*sizeof(Point));
158 for (i=poly->numpoints-1; i > pos; i--) {
159 poly->points[i] = poly->points[i-1];
161 poly->points[pos] = *point;
162 object_add_handle_at((Object*)poly, handle, pos);
163 object_add_connectionpoint_at((Object*)poly, cp1, 2*pos);
164 object_add_connectionpoint_at((Object*)poly, cp2, 2*pos+1);
166 obj = (Object *)poly;
169 static void
170 remove_handle(PolyShape *poly, int pos)
172 int i;
173 Object *obj;
174 Handle *old_handle;
175 ConnectionPoint *old_cp1, *old_cp2;
177 obj = (Object *)poly;
179 /* delete the points */
180 poly->numpoints--;
181 for (i=pos; i < poly->numpoints; i++) {
182 poly->points[i] = poly->points[i+1];
184 poly->points = g_realloc(poly->points, poly->numpoints*sizeof(Point));
186 old_handle = obj->handles[pos];
187 old_cp1 = obj->connections[2*pos];
188 old_cp2 = obj->connections[2*pos+1];
189 object_remove_handle(&poly->object, old_handle);
190 object_remove_connectionpoint(obj, old_cp1);
191 object_remove_connectionpoint(obj, old_cp2);
195 /* Add a point by splitting segment into two, putting the new point at
196 'point' or, if NULL, in the middle */
197 ObjectChange *
198 polyshape_add_point(PolyShape *poly, int segment, Point *point)
200 Point realpoint;
201 Handle *new_handle;
202 ConnectionPoint *new_cp1, *new_cp2;
204 if (point == NULL) {
205 realpoint.x = (poly->points[segment].x+poly->points[segment+1].x)/2;
206 realpoint.y = (poly->points[segment].y+poly->points[segment+1].y)/2;
207 } else {
208 realpoint = *point;
211 new_handle = g_new(Handle, 1);
212 new_cp1 = g_new0(ConnectionPoint, 1);
213 new_cp1->object = &poly->object;
214 new_cp2 = g_new0(ConnectionPoint, 1);
215 new_cp2->object = &poly->object;
216 setup_handle(new_handle);
217 add_handle(poly, segment+1, &realpoint, new_handle, new_cp1, new_cp2);
218 return polyshape_create_change(poly, TYPE_ADD_POINT,
219 &realpoint, segment+1, new_handle,
220 new_cp1, new_cp2);
223 ObjectChange *
224 polyshape_remove_point(PolyShape *poly, int pos)
226 Handle *old_handle;
227 ConnectionPoint *old_cp1, *old_cp2;
228 Point old_point;
230 old_handle = poly->object.handles[pos];
231 old_point = poly->points[pos];
232 old_cp1 = poly->object.connections[2*pos];
233 old_cp2 = poly->object.connections[2*pos+1];
235 object_unconnect((Object *)poly, old_handle);
237 remove_handle(poly, pos);
239 polyshape_update_data(poly);
241 return polyshape_create_change(poly, TYPE_REMOVE_POINT,
242 &old_point, pos, old_handle,
243 old_cp1, old_cp2);
246 void
247 polyshape_update_data(PolyShape *poly)
249 Point next;
250 int i;
252 /* Update handles: */
253 for (i = 0; i < poly->numpoints; i++) {
254 poly->object.handles[i]->pos = poly->points[i];
256 poly->object.connections[2*i]->pos = poly->points[i];
257 if (i == poly->numpoints - 1)
258 next = poly->points[0];
259 else
260 next = poly->points[i+1];
261 point_add(&next, &poly->points[i]);
262 point_scale(&next, 0.5);
263 poly->object.connections[2*i+1]->pos = next;
267 void
268 polyshape_update_boundingbox(PolyShape *poly)
270 ElementBBExtras *extra;
271 PolyBBExtras pextra;
273 assert(poly != NULL);
275 extra = &poly->extra_spacing;
276 pextra.start_trans = pextra.end_trans =
277 pextra.start_long = pextra.end_long = 0;
278 pextra.middle_trans = extra->border_trans;
280 polyline_bbox(&poly->points[0],
281 poly->numpoints,
282 &pextra, TRUE,
283 &poly->object.bounding_box);
286 void
287 polyshape_simple_draw(PolyShape *poly, DiaRenderer *renderer, real width)
289 Point *points;
291 assert(poly != NULL);
292 assert(renderer != NULL);
294 points = &poly->points[0];
296 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
297 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
298 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
299 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
301 DIA_RENDERER_GET_CLASS(renderer)->draw_polygon(renderer, points, poly->numpoints,
302 &color_black);
306 void
307 polyshape_init(PolyShape *poly, int num_points)
309 Object *obj;
310 int i;
312 obj = &poly->object;
314 object_init(obj, num_points, 2*num_points);
316 poly->numpoints = num_points;
318 poly->points = g_malloc(num_points*sizeof(Point));
320 for (i = 0; i < num_points; i++) {
321 poly->object.handles[i] = g_new(Handle, 1);
323 obj->handles[i]->connect_type = HANDLE_NONCONNECTABLE;
324 obj->handles[i]->connected_to = NULL;
325 obj->handles[i]->type = HANDLE_MAJOR_CONTROL;
326 obj->handles[i]->id = HANDLE_CORNER;
329 for (i = 0; i < 2*num_points; i++) {
330 poly->object.connections[i] = g_new0(ConnectionPoint, 1);
331 poly->object.connections[i]->object = &poly->object;
334 /* Since the points aren't set yet, and update_data only deals with
335 the points, don't call it (Thanks, valgrind!) */
336 /* polyshape_update_data(poly);*/
339 void
340 polyshape_set_points(PolyShape *poly, int num_points, Point *points)
342 int i;
344 poly->numpoints = num_points;
346 if (poly->points) g_free(poly->points);
347 poly->points = g_new(Point, num_points);
349 for (i = 0; i < num_points; i++) {
350 poly->points[i] = points[i];
354 void
355 polyshape_copy(PolyShape *from, PolyShape *to)
357 int i;
358 Object *toobj, *fromobj;
360 toobj = &to->object;
361 fromobj = &from->object;
363 object_copy(fromobj, toobj);
365 polyshape_set_points(to, from->numpoints, from->points);
367 for (i=0;i<to->numpoints;i++) {
368 to->object.handles[i] = g_new(Handle, 1);
369 setup_handle(to->object.handles[i]);
370 to->object.connections[2*i] = g_new0(ConnectionPoint, 1);
371 to->object.connections[2*i]->object = &to->object;
372 to->object.connections[2*i+1] = g_new0(ConnectionPoint, 1);
373 to->object.connections[2*i+1]->object = &to->object;
376 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
377 polyshape_update_data(to);
380 void
381 polyshape_destroy(PolyShape *poly)
383 int i;
384 Handle **temp_handles;
385 ConnectionPoint **temp_cps;
387 /* Need to store these temporary since object.handles is
388 freed by object_destroy() */
389 temp_handles = g_new(Handle *, poly->numpoints);
390 for (i=0;i<poly->numpoints;i++)
391 temp_handles[i] = poly->object.handles[i];
393 temp_cps = g_new(ConnectionPoint *, poly->numpoints*2);
394 for (i = 0; i < poly->numpoints * 2; i++)
395 temp_cps[i] = poly->object.connections[i];
397 object_destroy(&poly->object);
399 for (i=0;i<poly->numpoints;i++)
400 g_free(temp_handles[i]);
401 g_free(temp_handles);
402 for (i = 0; i < poly->numpoints*2; i++)
403 g_free(temp_cps[i]);
404 g_free(temp_cps);
406 g_free(poly->points);
410 void
411 polyshape_save(PolyShape *poly, ObjectNode obj_node)
413 int i;
414 AttributeNode attr;
416 object_save(&poly->object, obj_node);
418 attr = new_attribute(obj_node, "poly_points");
420 for (i=0;i<poly->numpoints;i++) {
421 data_add_point(attr, &poly->points[i]);
425 void
426 polyshape_load(PolyShape *poly, ObjectNode obj_node) /* NOTE: Does object_init() */
428 int i;
429 AttributeNode attr;
430 DataNode data;
432 Object *obj = &poly->object;
434 object_load(obj, obj_node);
436 attr = object_find_attribute(obj_node, "poly_points");
438 if (attr != NULL)
439 poly->numpoints = attribute_num_data(attr);
440 else
441 poly->numpoints = 0;
443 object_init(obj, poly->numpoints, 2*poly->numpoints);
445 data = attribute_first_data(attr);
446 poly->points = g_new(Point, poly->numpoints);
447 for (i=0;i<poly->numpoints;i++) {
448 data_point(data, &poly->points[i]);
449 data = data_next(data);
452 for (i=0;i<poly->numpoints;i++) {
453 obj->handles[i] = g_new(Handle, 1);
454 setup_handle(obj->handles[i]);
456 for (i = 0; i < poly->numpoints * 2; i++) {
457 obj->connections[i] = g_new0(ConnectionPoint, 1);
458 obj->connections[i]->object = obj;
461 polyshape_update_data(poly);
464 static void
465 polyshape_change_free(struct PointChange *change)
467 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
468 (change->type==TYPE_REMOVE_POINT && change->applied) ){
469 g_free(change->handle);
470 g_free(change->cp1);
471 g_free(change->cp2);
472 change->handle = NULL;
473 change->cp1 = NULL;
474 change->cp2 = NULL;
478 static void
479 polyshape_change_apply(struct PointChange *change, Object *obj)
481 change->applied = 1;
482 switch (change->type) {
483 case TYPE_ADD_POINT:
484 add_handle((PolyShape *)obj, change->pos, &change->point,
485 change->handle, change->cp1, change->cp2);
486 break;
487 case TYPE_REMOVE_POINT:
488 object_unconnect(obj, change->handle);
489 remove_handle((PolyShape *)obj, change->pos);
490 break;
494 static void
495 polyshape_change_revert(struct PointChange *change, Object *obj)
497 switch (change->type) {
498 case TYPE_ADD_POINT:
499 remove_handle((PolyShape *)obj, change->pos);
500 break;
501 case TYPE_REMOVE_POINT:
502 add_handle((PolyShape *)obj, change->pos, &change->point,
503 change->handle, change->cp1, change->cp2);
505 break;
507 change->applied = 0;
510 static ObjectChange *
511 polyshape_create_change(PolyShape *poly, enum change_type type,
512 Point *point, int pos, Handle *handle,
513 ConnectionPoint *cp1, ConnectionPoint *cp2)
515 struct PointChange *change;
517 change = g_new(struct PointChange, 1);
519 change->obj_change.apply = (ObjectChangeApplyFunc) polyshape_change_apply;
520 change->obj_change.revert = (ObjectChangeRevertFunc) polyshape_change_revert;
521 change->obj_change.free = (ObjectChangeFreeFunc) polyshape_change_free;
523 change->type = type;
524 change->applied = 1;
525 change->point = *point;
526 change->pos = pos;
527 change->handle = handle;
528 change->cp1 = cp1;
529 change->cp2 = cp2;
531 return (ObjectChange *)change;