2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / poly_conn.c
blobccd1167d968844a3d5c3eca967c682e45f124f6c
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 "poly_conn.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 *connected_to; /* NULL if not connected */
50 static ObjectChange *
51 polyconn_create_change(PolyConn *poly, enum change_type type,
52 Point *point, int segment, Handle *handle,
53 ConnectionPoint *connected_to);
55 typedef enum
57 PC_HANDLE_START,
58 PC_HANDLE_CORNER,
59 PC_HANDLE_END
60 } PolyConnHandleType;
62 static void
63 setup_handle(Handle *handle, PolyConnHandleType t)
65 handle->id = (PC_HANDLE_CORNER == t ? HANDLE_CORNER
66 : (PC_HANDLE_END == t ? HANDLE_MOVE_ENDPOINT
67 : HANDLE_MOVE_STARTPOINT));
68 handle->type = (PC_HANDLE_CORNER == t ? HANDLE_MINOR_CONTROL : HANDLE_MAJOR_CONTROL);
69 handle->connect_type = HANDLE_CONNECTABLE;
70 handle->connected_to = NULL;
74 static int get_handle_nr(PolyConn *poly, Handle *handle)
76 int i = 0;
77 for (i=0;i<poly->numpoints;i++) {
78 if (poly->object.handles[i] == handle)
79 return i;
81 return -1;
84 ObjectChange *
85 polyconn_move_handle(PolyConn *poly, Handle *handle,
86 Point *to, ConnectionPoint *cp,
87 HandleMoveReason reason, ModifierKeys modifiers)
89 int handle_nr;
91 handle_nr = get_handle_nr(poly, handle);
92 switch(handle->id) {
93 case HANDLE_MOVE_STARTPOINT:
94 poly->points[0] = *to;
95 break;
96 case HANDLE_MOVE_ENDPOINT:
97 poly->points[poly->numpoints-1] = *to;
98 break;
99 case HANDLE_CORNER:
100 poly->points[handle_nr] = *to;
101 break;
102 default:
103 message_error("Internal error in polyconn_move_handle.\n");
104 break;
107 return NULL;
110 ObjectChange*
111 polyconn_move(PolyConn *poly, Point *to)
113 Point p;
114 int i;
116 p = *to;
117 point_sub(&p, &poly->points[0]);
119 poly->points[0] = *to;
120 for (i=1;i<poly->numpoints;i++) {
121 point_add(&poly->points[i], &p);
124 return NULL;
128 polyconn_closest_segment(PolyConn *poly, Point *point, real line_width)
130 int i;
131 real dist;
132 int closest;
134 dist = distance_line_point( &poly->points[0], &poly->points[1],
135 line_width, point);
136 closest = 0;
137 for (i=1;i<poly->numpoints-1;i++) {
138 real new_dist =
139 distance_line_point( &poly->points[i], &poly->points[i+1],
140 line_width, point);
141 if (new_dist < dist) {
142 dist = new_dist;
143 closest = i;
146 return closest;
149 Handle *
150 polyconn_closest_handle(PolyConn *poly, Point *point)
152 int i;
153 real dist;
154 Handle *closest;
156 closest = poly->object.handles[0];
157 dist = distance_point_point( point, &closest->pos);
158 for (i=1;i<poly->numpoints;i++) {
159 real new_dist;
160 new_dist = distance_point_point( point, &poly->points[i]);
161 if (new_dist < dist) {
162 dist = new_dist;
163 closest = poly->object.handles[i];
166 return closest;
169 real
170 polyconn_distance_from(PolyConn *poly, Point *point, real line_width)
172 int i;
173 real dist;
175 dist = distance_line_point( &poly->points[0], &poly->points[1],
176 line_width, point);
177 for (i=1;i<poly->numpoints-1;i++) {
178 dist = MIN(dist,
179 distance_line_point( &poly->points[i], &poly->points[i+1],
180 line_width, point));
182 return dist;
185 static void
186 add_handle(PolyConn *poly, int pos, Point *point, Handle *handle)
188 int i;
189 DiaObject *obj;
191 poly->numpoints++;
192 poly->points = g_realloc(poly->points, poly->numpoints*sizeof(Point));
194 for (i=poly->numpoints-1; i > pos; i--) {
195 poly->points[i] = poly->points[i-1];
197 poly->points[pos] = *point;
198 object_add_handle_at((DiaObject*)poly, handle, pos);
200 obj = (DiaObject *)poly;
201 if (pos==0) {
202 obj->handles[1]->type = HANDLE_MINOR_CONTROL;
203 obj->handles[1]->id = HANDLE_CORNER;
205 if (pos==obj->num_handles-1) {
206 obj->handles[obj->num_handles-2]->type = HANDLE_MINOR_CONTROL;
207 obj->handles[obj->num_handles-2]->id = HANDLE_CORNER;
211 static void
212 remove_handle(PolyConn *poly, int pos)
214 int i;
215 DiaObject *obj;
216 Handle *old_handle;
218 obj = (DiaObject *)poly;
220 if (pos==0) {
221 obj->handles[1]->type = HANDLE_MAJOR_CONTROL;
222 obj->handles[1]->id = HANDLE_MOVE_STARTPOINT;
224 if (pos==obj->num_handles-1) {
225 obj->handles[obj->num_handles-2]->type = HANDLE_MAJOR_CONTROL;
226 obj->handles[obj->num_handles-2]->id = HANDLE_MOVE_ENDPOINT;
229 /* delete the points */
230 poly->numpoints--;
231 for (i=pos; i < poly->numpoints; i++) {
232 poly->points[i] = poly->points[i+1];
234 poly->points = g_realloc(poly->points, poly->numpoints*sizeof(Point));
236 old_handle = obj->handles[pos];
237 object_remove_handle(&poly->object, old_handle);
241 /* Add a point by splitting segment into two, putting the new point at
242 'point' or, if NULL, in the middle */
243 ObjectChange *
244 polyconn_add_point(PolyConn *poly, int segment, Point *point)
246 Point realpoint;
247 Handle *new_handle;
249 if (point == NULL) {
250 realpoint.x = (poly->points[segment].x+poly->points[segment+1].x)/2;
251 realpoint.y = (poly->points[segment].y+poly->points[segment+1].y)/2;
252 } else {
253 realpoint = *point;
256 new_handle = g_malloc(sizeof(Handle));
257 setup_handle(new_handle, PC_HANDLE_CORNER);
258 add_handle(poly, segment+1, &realpoint, new_handle);
259 return polyconn_create_change(poly, TYPE_ADD_POINT,
260 &realpoint, segment+1, new_handle,
261 NULL);
264 ObjectChange *
265 polyconn_remove_point(PolyConn *poly, int pos)
267 Handle *old_handle;
268 ConnectionPoint *connectionpoint;
269 Point old_point;
271 old_handle = poly->object.handles[pos];
272 old_point = poly->points[pos];
273 connectionpoint = old_handle->connected_to;
275 object_unconnect((DiaObject *)poly, old_handle);
277 remove_handle(poly, pos);
279 polyconn_update_data(poly);
281 return polyconn_create_change(poly, TYPE_REMOVE_POINT,
282 &old_point, pos, old_handle,
283 connectionpoint);
286 void
287 polyconn_update_data(PolyConn *poly)
289 int i;
290 DiaObject *obj = &poly->object;
292 /* handle the case of whole points array update (via set_prop) */
293 if (poly->numpoints != obj->num_handles) {
294 g_assert(0 == obj->num_connections);
296 obj->handles = g_realloc(obj->handles,
297 poly->numpoints*sizeof(Handle *));
298 obj->num_handles = poly->numpoints;
299 for (i=0;i<poly->numpoints;i++) {
300 obj->handles[i] = g_malloc(sizeof(Handle));
301 if (0 == i)
302 setup_handle(obj->handles[i], PC_HANDLE_START);
303 else if (i == poly->numpoints-1)
304 setup_handle(obj->handles[i], PC_HANDLE_END);
305 else
306 setup_handle(obj->handles[i], PC_HANDLE_CORNER);
310 /* Update handles: */
311 for (i=0;i<poly->numpoints;i++) {
312 obj->handles[i]->pos = poly->points[i];
316 void
317 polyconn_update_boundingbox(PolyConn *poly)
319 assert(poly != NULL);
321 polyline_bbox(&poly->points[0],
322 poly->numpoints,
323 &poly->extra_spacing, FALSE,
324 &poly->object.bounding_box);
327 void
328 polyconn_simple_draw(PolyConn *poly, DiaRenderer *renderer, real width)
330 Point *points;
332 assert(poly != NULL);
333 assert(renderer != NULL);
335 points = &poly->points[0];
337 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
338 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
339 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
340 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
342 DIA_RENDERER_GET_CLASS(renderer)->draw_polyline(renderer, points, poly->numpoints,
343 &color_black);
347 void
348 polyconn_init(PolyConn *poly, int num_points)
350 DiaObject *obj;
351 int i;
353 obj = &poly->object;
355 object_init(obj, num_points, 0);
357 poly->numpoints = num_points;
359 poly->points = g_malloc(num_points*sizeof(Point));
361 for (i=0;i<num_points;i++) {
362 obj->handles[i] = g_malloc(sizeof(Handle));
363 if (0 == i)
364 setup_handle(obj->handles[i], PC_HANDLE_START);
365 else if (i == num_points-1)
366 setup_handle(obj->handles[i], PC_HANDLE_END);
367 else
368 setup_handle(obj->handles[i], PC_HANDLE_CORNER);
371 polyconn_update_data(poly);
374 /** This function does *not* set up handles */
375 void
376 polyconn_set_points(PolyConn *poly, int num_points, Point *points)
378 int i;
380 poly->numpoints = num_points;
382 if (poly->points)
383 g_free(poly->points);
385 poly->points = g_malloc((poly->numpoints)*sizeof(Point));
387 for (i=0;i<poly->numpoints;i++) {
388 poly->points[i] = points[i];
392 void
393 polyconn_copy(PolyConn *from, PolyConn *to)
395 int i;
396 DiaObject *toobj, *fromobj;
398 toobj = &to->object;
399 fromobj = &from->object;
401 object_copy(fromobj, toobj);
403 to->object.handles[0] = g_new(Handle,1);
404 *to->object.handles[0] = *from->object.handles[0];
406 for (i=1;i<toobj->num_handles-1;i++) {
407 to->object.handles[i] = g_malloc(sizeof(Handle));
408 setup_handle(to->object.handles[i], PC_HANDLE_CORNER);
411 to->object.handles[toobj->num_handles-1] = g_new(Handle,1);
412 *to->object.handles[toobj->num_handles-1] =
413 *from->object.handles[toobj->num_handles-1];
414 polyconn_set_points(to, from->numpoints, from->points);
416 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
417 polyconn_update_data(to);
420 void
421 polyconn_destroy(PolyConn *poly)
423 int i;
424 Handle **temp_handles;
426 /* Need to store these temporary since object.handles is
427 freed by object_destroy() */
428 temp_handles = g_new(Handle *, poly->numpoints);
429 for (i=0;i<poly->numpoints;i++)
430 temp_handles[i] = poly->object.handles[i];
432 object_destroy(&poly->object);
434 for (i=0;i<poly->numpoints;i++)
435 g_free(temp_handles[i]);
436 g_free(temp_handles);
438 g_free(poly->points);
442 void
443 polyconn_save(PolyConn *poly, ObjectNode obj_node)
445 int i;
446 AttributeNode attr;
448 object_save(&poly->object, obj_node);
450 attr = new_attribute(obj_node, "poly_points");
452 for (i=0;i<poly->numpoints;i++) {
453 data_add_point(attr, &poly->points[i]);
457 void
458 polyconn_load(PolyConn *poly, ObjectNode obj_node) /* NOTE: Does object_init() */
460 int i;
461 AttributeNode attr;
462 DataNode data;
464 DiaObject *obj = &poly->object;
466 object_load(obj, obj_node);
468 attr = object_find_attribute(obj_node, "poly_points");
470 if (attr != NULL)
471 poly->numpoints = attribute_num_data(attr);
472 else
473 poly->numpoints = 0;
475 object_init(obj, poly->numpoints, 0);
477 data = attribute_first_data(attr);
478 poly->points = g_malloc(poly->numpoints*sizeof(Point));
479 for (i=0;i<poly->numpoints;i++) {
480 data_point(data, &poly->points[i]);
481 data = data_next(data);
484 obj->handles[0] = g_malloc(sizeof(Handle));
485 obj->handles[0]->connect_type = HANDLE_CONNECTABLE;
486 obj->handles[0]->connected_to = NULL;
487 obj->handles[0]->type = HANDLE_MAJOR_CONTROL;
488 obj->handles[0]->id = HANDLE_MOVE_STARTPOINT;
490 obj->handles[poly->numpoints-1] = g_malloc(sizeof(Handle));
491 obj->handles[poly->numpoints-1]->connect_type = HANDLE_CONNECTABLE;
492 obj->handles[poly->numpoints-1]->connected_to = NULL;
493 obj->handles[poly->numpoints-1]->type = HANDLE_MAJOR_CONTROL;
494 obj->handles[poly->numpoints-1]->id = HANDLE_MOVE_ENDPOINT;
496 for (i=1;i<poly->numpoints-1;i++) {
497 obj->handles[i] = g_malloc(sizeof(Handle));
498 setup_handle(obj->handles[i], PC_HANDLE_CORNER);
501 polyconn_update_data(poly);
504 static void
505 polyconn_change_free(struct PointChange *change)
507 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
508 (change->type==TYPE_REMOVE_POINT && change->applied) ){
509 if (change->handle)
510 g_free(change->handle);
511 change->handle = NULL;
515 static void
516 polyconn_change_apply(struct PointChange *change, DiaObject *obj)
518 change->applied = 1;
519 switch (change->type) {
520 case TYPE_ADD_POINT:
521 add_handle((PolyConn *)obj, change->pos, &change->point,
522 change->handle);
523 break;
524 case TYPE_REMOVE_POINT:
525 object_unconnect(obj, change->handle);
526 remove_handle((PolyConn *)obj, change->pos);
527 break;
531 static void
532 polyconn_change_revert(struct PointChange *change, DiaObject *obj)
534 switch (change->type) {
535 case TYPE_ADD_POINT:
536 remove_handle((PolyConn *)obj, change->pos);
537 break;
538 case TYPE_REMOVE_POINT:
539 add_handle((PolyConn *)obj, change->pos, &change->point,
540 change->handle);
541 if (change->connected_to) {
542 object_connect(obj, change->handle, change->connected_to);
545 break;
547 change->applied = 0;
550 static ObjectChange *
551 polyconn_create_change(PolyConn *poly, enum change_type type,
552 Point *point, int pos, Handle *handle,
553 ConnectionPoint *connected_to)
555 struct PointChange *change;
557 change = g_new(struct PointChange, 1);
559 change->obj_change.apply = (ObjectChangeApplyFunc) polyconn_change_apply;
560 change->obj_change.revert = (ObjectChangeRevertFunc) polyconn_change_revert;
561 change->obj_change.free = (ObjectChangeFreeFunc) polyconn_change_free;
563 change->type = type;
564 change->applied = 1;
565 change->point = *point;
566 change->pos = pos;
567 change->handle = handle;
568 change->connected_to = connected_to;
570 return (ObjectChange *)change;