2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / lib / polyshape.c
blob22209c03bc5c71f2c82737d425c85f61da35d4a7
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 #define NUM_CONNECTIONS(poly) ((poly)->numpoints * 2 + 1)
33 enum change_type {
34 TYPE_ADD_POINT,
35 TYPE_REMOVE_POINT
38 struct PointChange {
39 ObjectChange obj_change;
41 enum change_type type;
42 int applied;
44 Point point;
45 int pos;
47 Handle *handle; /* owning ref when not applied for ADD_POINT
48 owning ref when applied for REMOVE_POINT */
49 ConnectionPoint *cp1, *cp2;
52 static ObjectChange *
53 polyshape_create_change(PolyShape *poly, enum change_type type,
54 Point *point, int segment, Handle *handle,
55 ConnectionPoint *cp1, ConnectionPoint *cp2);
57 static void setup_handle(Handle *handle)
59 handle->id = HANDLE_CORNER;
60 handle->type = HANDLE_MAJOR_CONTROL;
61 handle->connect_type = HANDLE_NONCONNECTABLE;
62 handle->connected_to = NULL;
66 static int get_handle_nr(PolyShape *poly, Handle *handle)
68 int i = 0;
69 for (i=0;i<poly->numpoints;i++) {
70 if (poly->object.handles[i] == handle)
71 return i;
73 return -1;
76 ObjectChange*
77 polyshape_move_handle(PolyShape *poly, Handle *handle,
78 Point *to, ConnectionPoint *cp,
79 HandleMoveReason reason, ModifierKeys modifiers)
81 int handle_nr;
83 handle_nr = get_handle_nr(poly, handle);
84 poly->points[handle_nr] = *to;
86 return NULL;
89 ObjectChange*
90 polyshape_move(PolyShape *poly, Point *to)
92 Point p;
93 int i;
95 p = *to;
96 point_sub(&p, &poly->points[0]);
98 poly->points[0] = *to;
99 for (i=1;i<poly->numpoints;i++) {
100 point_add(&poly->points[i], &p);
103 return NULL;
107 polyshape_closest_segment(PolyShape *poly, Point *point, real line_width)
109 int i;
110 real dist;
111 int closest;
113 dist = distance_line_point( &poly->points[poly->numpoints-1], &poly->points[0],
114 line_width, point);
115 closest = poly->numpoints-1;
116 for (i=0;i<poly->numpoints-1;i++) {
117 real new_dist =
118 distance_line_point( &poly->points[i], &poly->points[i+1],
119 line_width, point);
120 if (new_dist < dist) {
121 dist = new_dist;
122 closest = i;
125 return closest;
128 Handle *
129 polyshape_closest_handle(PolyShape *poly, Point *point)
131 int i;
132 real dist;
133 Handle *closest;
135 closest = poly->object.handles[0];
136 dist = distance_point_point( point, &closest->pos);
137 for (i=1;i<poly->numpoints;i++) {
138 real new_dist;
139 new_dist = distance_point_point( point, &poly->points[i]);
140 if (new_dist < dist) {
141 dist = new_dist;
142 closest = poly->object.handles[i];
145 return closest;
148 real
149 polyshape_distance_from(PolyShape *poly, Point *point, real line_width)
151 return distance_polygon_point(poly->points, poly->numpoints,
152 line_width, point);
155 static void
156 add_handle(PolyShape *poly, int pos, Point *point, Handle *handle,
157 ConnectionPoint *cp1, ConnectionPoint *cp2)
159 int i;
160 DiaObject *obj;
162 poly->numpoints++;
163 poly->points = g_realloc(poly->points, poly->numpoints*sizeof(Point));
165 for (i=poly->numpoints-1; i > pos; i--) {
166 poly->points[i] = poly->points[i-1];
168 poly->points[pos] = *point;
169 object_add_handle_at((DiaObject*)poly, handle, pos);
170 object_add_connectionpoint_at((DiaObject*)poly, cp1, 2*pos);
171 object_add_connectionpoint_at((DiaObject*)poly, cp2, 2*pos+1);
173 obj = (DiaObject *)poly;
176 static void
177 remove_handle(PolyShape *poly, int pos)
179 int i;
180 DiaObject *obj;
181 Handle *old_handle;
182 ConnectionPoint *old_cp1, *old_cp2;
184 obj = (DiaObject *)poly;
186 /* delete the points */
187 poly->numpoints--;
188 for (i=pos; i < poly->numpoints; i++) {
189 poly->points[i] = poly->points[i+1];
191 poly->points = g_realloc(poly->points, poly->numpoints*sizeof(Point));
193 old_handle = obj->handles[pos];
194 old_cp1 = obj->connections[2*pos];
195 old_cp2 = obj->connections[2*pos+1];
196 object_remove_handle(&poly->object, old_handle);
197 object_remove_connectionpoint(obj, old_cp1);
198 object_remove_connectionpoint(obj, old_cp2);
202 /* Add a point by splitting segment into two, putting the new point at
203 'point' or, if NULL, in the middle */
204 ObjectChange *
205 polyshape_add_point(PolyShape *poly, int segment, Point *point)
207 Point realpoint;
208 Handle *new_handle;
209 ConnectionPoint *new_cp1, *new_cp2;
211 if (point == NULL) {
212 realpoint.x = (poly->points[segment].x+poly->points[segment+1].x)/2;
213 realpoint.y = (poly->points[segment].y+poly->points[segment+1].y)/2;
214 } else {
215 realpoint = *point;
218 new_handle = g_new(Handle, 1);
219 new_cp1 = g_new0(ConnectionPoint, 1);
220 new_cp1->object = &poly->object;
221 new_cp2 = g_new0(ConnectionPoint, 1);
222 new_cp2->object = &poly->object;
223 setup_handle(new_handle);
224 add_handle(poly, segment+1, &realpoint, new_handle, new_cp1, new_cp2);
225 return polyshape_create_change(poly, TYPE_ADD_POINT,
226 &realpoint, segment+1, new_handle,
227 new_cp1, new_cp2);
230 ObjectChange *
231 polyshape_remove_point(PolyShape *poly, int pos)
233 Handle *old_handle;
234 ConnectionPoint *old_cp1, *old_cp2;
235 Point old_point;
237 old_handle = poly->object.handles[pos];
238 old_point = poly->points[pos];
239 old_cp1 = poly->object.connections[2*pos];
240 old_cp2 = poly->object.connections[2*pos+1];
242 object_unconnect((DiaObject *)poly, old_handle);
244 remove_handle(poly, pos);
246 polyshape_update_data(poly);
248 return polyshape_create_change(poly, TYPE_REMOVE_POINT,
249 &old_point, pos, old_handle,
250 old_cp1, old_cp2);
253 /** Returns the first clockwise direction in dirs
254 * (as returned from find_slope_directions) */
255 static gint
256 first_direction(gint dirs) {
257 switch (dirs) {
258 case DIR_NORTHEAST: return DIR_NORTH;
259 case DIR_SOUTHEAST: return DIR_EAST;
260 case DIR_NORTHWEST: return DIR_WEST;
261 case DIR_SOUTHWEST: return DIR_SOUTH;
262 default: return dirs;
266 /** Returns the last clockwise direction in dirs
267 * (as returned from find_slope_directions) */
268 static gint
269 last_direction(gint dirs) {
270 switch (dirs) {
271 case DIR_NORTHEAST: return DIR_EAST;
272 case DIR_SOUTHEAST: return DIR_SOUTH;
273 case DIR_NORTHWEST: return DIR_NORTH;
274 case DIR_SOUTHWEST: return DIR_WEST;
275 default: return dirs;
279 /** Returns the available directions for a corner */
280 static gint
281 find_tip_directions(Point prev, Point this, Point next)
283 gint startdirs = find_slope_directions(prev, this);
284 gint enddirs = find_slope_directions(this, next);
285 gint firstdir = first_direction(startdirs);
286 gint lastdir = last_direction(enddirs);
287 gint dir, dirs = 0;
289 dir = firstdir;
290 while (dir != lastdir) {
291 dirs |= dir;
292 dir = dir * 2;
293 if (dir == 16) dir = 1;
295 dirs |= dir;
297 return dirs;
300 void
301 polyshape_update_data(PolyShape *poly)
303 Point next;
304 int i;
305 DiaObject *obj = &poly->object;
306 Point minp, maxp;
308 /* handle the case of whole points array update (via set_prop) */
309 if (poly->numpoints != obj->num_handles ||
310 NUM_CONNECTIONS(poly) != obj->num_connections) {
311 object_unconnect_all(obj); /* too drastic ? */
313 obj->handles = g_realloc(obj->handles,
314 poly->numpoints*sizeof(Handle *));
315 obj->num_handles = poly->numpoints;
316 for (i=0;i<poly->numpoints;i++) {
317 obj->handles[i] = g_new(Handle, 1);
318 setup_handle(obj->handles[i]);
321 obj->connections = g_realloc(obj->connections,
322 NUM_CONNECTIONS(poly) * sizeof(ConnectionPoint *));
323 for (i = 0; i < NUM_CONNECTIONS(poly); i++) {
324 obj->connections[i] = g_new0(ConnectionPoint, 1);
325 obj->connections[i]->object = obj;
327 obj->num_connections = NUM_CONNECTIONS(poly);
330 /* Update handles: */
331 minp = maxp = poly->points[0];
332 for (i = 0; i < poly->numpoints; i++) {
333 gint thisdir, nextdir;
334 Point prev;
335 obj->handles[i]->pos = poly->points[i];
337 if (i == 0)
338 prev = poly->points[poly->numpoints-1];
339 else
340 prev = poly->points[i-1];
341 if (i == poly->numpoints - 1)
342 next = poly->points[0];
343 else
344 next = poly->points[i+1];
345 point_add(&next, &poly->points[i]);
346 point_scale(&next, 0.5);
348 thisdir = find_tip_directions(prev, poly->points[i], next);
349 nextdir = find_slope_directions(poly->points[i], next);
351 obj->connections[2*i]->pos = poly->points[i];
352 obj->connections[2*i]->directions = thisdir;
353 obj->connections[2*i+1]->pos = next;
354 obj->connections[2*i+1]->directions = nextdir;
356 if (poly->points[i].x < minp.x) minp.x = poly->points[i].x;
357 if (poly->points[i].x > maxp.x) maxp.x = poly->points[i].x;
358 if (poly->points[i].y < minp.y) minp.y = poly->points[i].y;
359 if (poly->points[i].y > maxp.y) maxp.y = poly->points[i].y;
362 obj->connections[obj->num_connections-1]->pos.x = (minp.x + maxp.x) / 2;
363 obj->connections[obj->num_connections-1]->pos.y = (minp.y + maxp.y) / 2;
364 obj->connections[obj->num_connections-1]->directions = DIR_ALL;
367 void
368 polyshape_update_boundingbox(PolyShape *poly)
370 ElementBBExtras *extra;
371 PolyBBExtras pextra;
373 assert(poly != NULL);
375 extra = &poly->extra_spacing;
376 pextra.start_trans = pextra.end_trans =
377 pextra.start_long = pextra.end_long = 0;
378 pextra.middle_trans = extra->border_trans;
380 polyline_bbox(&poly->points[0],
381 poly->numpoints,
382 &pextra, TRUE,
383 &poly->object.bounding_box);
386 void
387 polyshape_simple_draw(PolyShape *poly, DiaRenderer *renderer, real width)
389 Point *points;
391 assert(poly != NULL);
392 assert(renderer != NULL);
394 points = &poly->points[0];
396 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
397 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
398 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
399 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
401 DIA_RENDERER_GET_CLASS(renderer)->draw_polygon(renderer, points, poly->numpoints,
402 &color_black);
406 void
407 polyshape_init(PolyShape *poly, int num_points)
409 DiaObject *obj;
410 int i;
412 obj = &poly->object;
414 object_init(obj, num_points, 2 * num_points + 1);
416 poly->numpoints = num_points;
418 poly->points = g_malloc(num_points*sizeof(Point));
420 for (i = 0; i < num_points; i++) {
421 obj->handles[i] = g_new(Handle, 1);
423 obj->handles[i]->connect_type = HANDLE_NONCONNECTABLE;
424 obj->handles[i]->connected_to = NULL;
425 obj->handles[i]->type = HANDLE_MAJOR_CONTROL;
426 obj->handles[i]->id = HANDLE_CORNER;
429 for (i = 0; i < NUM_CONNECTIONS(poly); i++) {
430 obj->connections[i] = g_new0(ConnectionPoint, 1);
431 obj->connections[i]->object = &poly->object;
432 obj->connections[i]->flags = 0;
434 obj->connections[obj->num_connections - 1]->flags = CP_FLAGS_MAIN;
437 /* Since the points aren't set yet, and update_data only deals with
438 the points, don't call it (Thanks, valgrind!) */
439 /* polyshape_update_data(poly);*/
442 void
443 polyshape_set_points(PolyShape *poly, int num_points, Point *points)
445 int i;
447 poly->numpoints = num_points;
449 if (poly->points) g_free(poly->points);
450 poly->points = g_new(Point, num_points);
452 for (i = 0; i < num_points; i++) {
453 poly->points[i] = points[i];
457 void
458 polyshape_copy(PolyShape *from, PolyShape *to)
460 int i;
461 DiaObject *toobj, *fromobj;
463 toobj = &to->object;
464 fromobj = &from->object;
466 object_copy(fromobj, toobj);
468 polyshape_set_points(to, from->numpoints, from->points);
470 for (i=0;i<to->numpoints;i++) {
471 toobj->handles[i] = g_new(Handle, 1);
472 setup_handle(toobj->handles[i]);
473 toobj->connections[2*i] = g_new0(ConnectionPoint, 1);
474 toobj->connections[2*i]->object = &to->object;
475 toobj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
476 toobj->connections[2*i+1]->object = &to->object;
478 toobj->connections[toobj->num_connections - 1] = g_new0(ConnectionPoint, 1);
479 toobj->connections[toobj->num_connections - 1]->object = &to->object;
481 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
482 polyshape_update_data(to);
485 void
486 polyshape_destroy(PolyShape *poly)
488 int i;
489 Handle **temp_handles;
490 ConnectionPoint **temp_cps;
492 /* Need to store these temporary since object.handles is
493 freed by object_destroy() */
494 temp_handles = g_new(Handle *, poly->numpoints);
495 for (i=0;i<poly->numpoints;i++)
496 temp_handles[i] = poly->object.handles[i];
498 temp_cps = g_new(ConnectionPoint *, NUM_CONNECTIONS(poly));
499 for (i = 0; i < NUM_CONNECTIONS(poly); i++)
500 temp_cps[i] = poly->object.connections[i];
502 object_destroy(&poly->object);
504 for (i=0;i<poly->numpoints;i++)
505 g_free(temp_handles[i]);
506 g_free(temp_handles);
507 for (i = 0; i < NUM_CONNECTIONS(poly); i++)
508 g_free(temp_cps[i]);
509 g_free(temp_cps);
511 g_free(poly->points);
515 void
516 polyshape_save(PolyShape *poly, ObjectNode obj_node)
518 int i;
519 AttributeNode attr;
521 object_save(&poly->object, obj_node);
523 attr = new_attribute(obj_node, "poly_points");
525 for (i=0;i<poly->numpoints;i++) {
526 data_add_point(attr, &poly->points[i]);
530 void
531 polyshape_load(PolyShape *poly, ObjectNode obj_node) /* NOTE: Does object_init() */
533 int i;
534 AttributeNode attr;
535 DataNode data;
537 DiaObject *obj = &poly->object;
539 object_load(obj, obj_node);
541 attr = object_find_attribute(obj_node, "poly_points");
543 if (attr != NULL)
544 poly->numpoints = attribute_num_data(attr);
545 else
546 poly->numpoints = 0;
548 object_init(obj, poly->numpoints, NUM_CONNECTIONS(poly));
550 data = attribute_first_data(attr);
551 poly->points = g_new(Point, poly->numpoints);
552 for (i=0;i<poly->numpoints;i++) {
553 data_point(data, &poly->points[i]);
554 data = data_next(data);
557 for (i=0;i<poly->numpoints;i++) {
558 obj->handles[i] = g_new(Handle, 1);
559 setup_handle(obj->handles[i]);
561 for (i = 0; i < NUM_CONNECTIONS(poly); i++) {
562 obj->connections[i] = g_new0(ConnectionPoint, 1);
563 obj->connections[i]->object = obj;
565 obj->connections[obj->num_connections - 1]->flags = CP_FLAGS_MAIN;
567 polyshape_update_data(poly);
570 static void
571 polyshape_change_free(struct PointChange *change)
573 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
574 (change->type==TYPE_REMOVE_POINT && change->applied) ){
575 g_free(change->handle);
576 g_free(change->cp1);
577 g_free(change->cp2);
578 change->handle = NULL;
579 change->cp1 = NULL;
580 change->cp2 = NULL;
584 static void
585 polyshape_change_apply(struct PointChange *change, DiaObject *obj)
587 change->applied = 1;
588 switch (change->type) {
589 case TYPE_ADD_POINT:
590 add_handle((PolyShape *)obj, change->pos, &change->point,
591 change->handle, change->cp1, change->cp2);
592 break;
593 case TYPE_REMOVE_POINT:
594 object_unconnect(obj, change->handle);
595 remove_handle((PolyShape *)obj, change->pos);
596 break;
600 static void
601 polyshape_change_revert(struct PointChange *change, DiaObject *obj)
603 switch (change->type) {
604 case TYPE_ADD_POINT:
605 remove_handle((PolyShape *)obj, change->pos);
606 break;
607 case TYPE_REMOVE_POINT:
608 add_handle((PolyShape *)obj, change->pos, &change->point,
609 change->handle, change->cp1, change->cp2);
611 break;
613 change->applied = 0;
616 static ObjectChange *
617 polyshape_create_change(PolyShape *poly, enum change_type type,
618 Point *point, int pos, Handle *handle,
619 ConnectionPoint *cp1, ConnectionPoint *cp2)
621 struct PointChange *change;
623 change = g_new(struct PointChange, 1);
625 change->obj_change.apply = (ObjectChangeApplyFunc) polyshape_change_apply;
626 change->obj_change.revert = (ObjectChangeRevertFunc) polyshape_change_revert;
627 change->obj_change.free = (ObjectChangeFreeFunc) polyshape_change_free;
629 change->type = type;
630 change->applied = 1;
631 change->point = *point;
632 change->pos = pos;
633 change->handle = handle;
634 change->cp1 = cp1;
635 change->cp2 = cp2;
637 return (ObjectChange *)change;