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.
25 #include <string.h> /* memcpy() */
27 #include "polyshape.h"
29 #include "diarenderer.h"
31 #define NUM_CONNECTIONS(poly) ((poly)->numpoints * 2 + 1)
39 ObjectChange obj_change
;
41 enum change_type type
;
47 Handle
*handle
; /* owning ref when not applied for ADD_POINT
48 owning ref when applied for REMOVE_POINT */
49 ConnectionPoint
*cp1
, *cp2
;
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
)
69 for (i
=0;i
<poly
->numpoints
;i
++) {
70 if (poly
->object
.handles
[i
] == handle
)
77 polyshape_move_handle(PolyShape
*poly
, Handle
*handle
,
78 Point
*to
, ConnectionPoint
*cp
,
79 HandleMoveReason reason
, ModifierKeys modifiers
)
83 handle_nr
= get_handle_nr(poly
, handle
);
84 poly
->points
[handle_nr
] = *to
;
90 polyshape_move(PolyShape
*poly
, Point
*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
);
107 polyshape_closest_segment(PolyShape
*poly
, Point
*point
, real line_width
)
113 dist
= distance_line_point( &poly
->points
[poly
->numpoints
-1], &poly
->points
[0],
115 closest
= poly
->numpoints
-1;
116 for (i
=0;i
<poly
->numpoints
-1;i
++) {
118 distance_line_point( &poly
->points
[i
], &poly
->points
[i
+1],
120 if (new_dist
< dist
) {
129 polyshape_closest_handle(PolyShape
*poly
, Point
*point
)
135 closest
= poly
->object
.handles
[0];
136 dist
= distance_point_point( point
, &closest
->pos
);
137 for (i
=1;i
<poly
->numpoints
;i
++) {
139 new_dist
= distance_point_point( point
, &poly
->points
[i
]);
140 if (new_dist
< dist
) {
142 closest
= poly
->object
.handles
[i
];
149 polyshape_distance_from(PolyShape
*poly
, Point
*point
, real line_width
)
151 return distance_polygon_point(poly
->points
, poly
->numpoints
,
156 add_handle(PolyShape
*poly
, int pos
, Point
*point
, Handle
*handle
,
157 ConnectionPoint
*cp1
, ConnectionPoint
*cp2
)
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
;
177 remove_handle(PolyShape
*poly
, int pos
)
182 ConnectionPoint
*old_cp1
, *old_cp2
;
184 obj
= (DiaObject
*)poly
;
186 /* delete the points */
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 */
205 polyshape_add_point(PolyShape
*poly
, int segment
, Point
*point
)
209 ConnectionPoint
*new_cp1
, *new_cp2
;
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;
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
,
231 polyshape_remove_point(PolyShape
*poly
, int pos
)
234 ConnectionPoint
*old_cp1
, *old_cp2
;
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
,
253 /** Returns the first clockwise direction in dirs
254 * (as returned from find_slope_directions) */
256 first_direction(gint 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) */
269 last_direction(gint 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 */
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
);
290 while (dir
!= lastdir
) {
293 if (dir
== 16) dir
= 1;
301 polyshape_update_data(PolyShape
*poly
)
305 DiaObject
*obj
= &poly
->object
;
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
;
335 obj
->handles
[i
]->pos
= poly
->points
[i
];
338 prev
= poly
->points
[poly
->numpoints
-1];
340 prev
= poly
->points
[i
-1];
341 if (i
== poly
->numpoints
- 1)
342 next
= poly
->points
[0];
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
;
368 polyshape_update_boundingbox(PolyShape
*poly
)
370 ElementBBExtras
*extra
;
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],
383 &poly
->object
.bounding_box
);
387 polyshape_simple_draw(PolyShape
*poly
, DiaRenderer
*renderer
, real width
)
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
,
407 polyshape_init(PolyShape
*poly
, int num_points
)
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);*/
443 polyshape_set_points(PolyShape
*poly
, int num_points
, Point
*points
)
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
];
458 polyshape_copy(PolyShape
*from
, PolyShape
*to
)
461 DiaObject
*toobj
, *fromobj
;
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
);
486 polyshape_destroy(PolyShape
*poly
)
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
++)
511 g_free(poly
->points
);
516 polyshape_save(PolyShape
*poly
, ObjectNode obj_node
)
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
]);
531 polyshape_load(PolyShape
*poly
, ObjectNode obj_node
) /* NOTE: Does object_init() */
537 DiaObject
*obj
= &poly
->object
;
539 object_load(obj
, obj_node
);
541 attr
= object_find_attribute(obj_node
, "poly_points");
544 poly
->numpoints
= attribute_num_data(attr
);
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
);
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
);
578 change
->handle
= NULL
;
585 polyshape_change_apply(struct PointChange
*change
, DiaObject
*obj
)
588 switch (change
->type
) {
590 add_handle((PolyShape
*)obj
, change
->pos
, &change
->point
,
591 change
->handle
, change
->cp1
, change
->cp2
);
593 case TYPE_REMOVE_POINT
:
594 object_unconnect(obj
, change
->handle
);
595 remove_handle((PolyShape
*)obj
, change
->pos
);
601 polyshape_change_revert(struct PointChange
*change
, DiaObject
*obj
)
603 switch (change
->type
) {
605 remove_handle((PolyShape
*)obj
, change
->pos
);
607 case TYPE_REMOVE_POINT
:
608 add_handle((PolyShape
*)obj
, change
->pos
, &change
->point
,
609 change
->handle
, change
->cp1
, change
->cp2
);
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
;
631 change
->point
= *point
;
633 change
->handle
= handle
;
637 return (ObjectChange
*)change
;