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"
37 ObjectChange obj_change
;
39 enum change_type type
;
45 Handle
*handle
; /* owning ref when not applied for ADD_POINT
46 owning ref when applied for REMOVE_POINT */
47 ConnectionPoint
*cp1
, *cp2
;
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
)
67 for (i
=0;i
<poly
->numpoints
;i
++) {
68 if (poly
->object
.handles
[i
] == handle
)
75 polyshape_move_handle(PolyShape
*poly
, Handle
*handle
,
76 Point
*to
, HandleMoveReason reason
)
80 handle_nr
= get_handle_nr(poly
, handle
);
81 poly
->points
[handle_nr
] = *to
;
85 polyshape_move(PolyShape
*poly
, Point
*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
);
100 polyshape_closest_segment(PolyShape
*poly
, Point
*point
, real line_width
)
106 dist
= distance_line_point( &poly
->points
[poly
->numpoints
-1], &poly
->points
[0],
108 closest
= poly
->numpoints
-1;
109 for (i
=0;i
<poly
->numpoints
-1;i
++) {
111 distance_line_point( &poly
->points
[i
], &poly
->points
[i
+1],
113 if (new_dist
< dist
) {
122 polyshape_closest_handle(PolyShape
*poly
, Point
*point
)
128 closest
= poly
->object
.handles
[0];
129 dist
= distance_point_point( point
, &closest
->pos
);
130 for (i
=1;i
<poly
->numpoints
;i
++) {
132 new_dist
= distance_point_point( point
, &poly
->points
[i
]);
133 if (new_dist
< dist
) {
135 closest
= poly
->object
.handles
[i
];
142 polyshape_distance_from(PolyShape
*poly
, Point
*point
, real line_width
)
144 return distance_polygon_point(poly
->points
, poly
->numpoints
,
149 add_handle(PolyShape
*poly
, int pos
, Point
*point
, Handle
*handle
,
150 ConnectionPoint
*cp1
, ConnectionPoint
*cp2
)
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
;
170 remove_handle(PolyShape
*poly
, int pos
)
175 ConnectionPoint
*old_cp1
, *old_cp2
;
177 obj
= (Object
*)poly
;
179 /* delete the points */
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 */
198 polyshape_add_point(PolyShape
*poly
, int segment
, Point
*point
)
202 ConnectionPoint
*new_cp1
, *new_cp2
;
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;
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
,
224 polyshape_remove_point(PolyShape
*poly
, int pos
)
227 ConnectionPoint
*old_cp1
, *old_cp2
;
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
,
247 polyshape_update_data(PolyShape
*poly
)
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];
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
;
268 polyshape_update_boundingbox(PolyShape
*poly
)
270 ElementBBExtras
*extra
;
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],
283 &poly
->object
.bounding_box
);
287 polyshape_simple_draw(PolyShape
*poly
, DiaRenderer
*renderer
, real width
)
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
,
307 polyshape_init(PolyShape
*poly
, int num_points
)
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);*/
340 polyshape_set_points(PolyShape
*poly
, int num_points
, Point
*points
)
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
];
355 polyshape_copy(PolyShape
*from
, PolyShape
*to
)
358 Object
*toobj
, *fromobj
;
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
);
381 polyshape_destroy(PolyShape
*poly
)
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
++)
406 g_free(poly
->points
);
411 polyshape_save(PolyShape
*poly
, ObjectNode obj_node
)
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
]);
426 polyshape_load(PolyShape
*poly
, ObjectNode obj_node
) /* NOTE: Does object_init() */
432 Object
*obj
= &poly
->object
;
434 object_load(obj
, obj_node
);
436 attr
= object_find_attribute(obj_node
, "poly_points");
439 poly
->numpoints
= attribute_num_data(attr
);
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
);
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
);
472 change
->handle
= NULL
;
479 polyshape_change_apply(struct PointChange
*change
, Object
*obj
)
482 switch (change
->type
) {
484 add_handle((PolyShape
*)obj
, change
->pos
, &change
->point
,
485 change
->handle
, change
->cp1
, change
->cp2
);
487 case TYPE_REMOVE_POINT
:
488 object_unconnect(obj
, change
->handle
);
489 remove_handle((PolyShape
*)obj
, change
->pos
);
495 polyshape_change_revert(struct PointChange
*change
, Object
*obj
)
497 switch (change
->type
) {
499 remove_handle((PolyShape
*)obj
, change
->pos
);
501 case TYPE_REMOVE_POINT
:
502 add_handle((PolyShape
*)obj
, change
->pos
, &change
->point
,
503 change
->handle
, change
->cp1
, change
->cp2
);
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
;
525 change
->point
= *point
;
527 change
->handle
= handle
;
531 return (ObjectChange
*)change
;