作図ソフト dia の改良版
Revisão | d5d5b662517bbf7c67cc8832b545ba8d47b80833 (tree) |
---|---|
Hora | 2014-10-02 04:39:29 |
Autor | Hans Breuer <hans@breu...> |
Commiter | Hans Breuer |
[transform] svg: more transformation support for Standard objects
New optional method DiaObject::transform() to apply an affine transformation
to the object. Immediately visible effect - without SVG - is rotation support
for 'Standard - Box' and 'Standard - Ellipse'.
'Standard - Text' and 'Standard - Image' are not fully supported yet, because
they require significant work on the renderer level.
Standard - Arc, Outline and ZigZagLine are not included because they wont be
used from SVG and the direct support is considered pointless.
Currently the intended main use-case is improved transformation from SVG
especially on the level of single elements. The included test file
transform-variations.svg shows the issues solved and open.
@@ -68,6 +68,7 @@ static DiaObject *group_copy(Group *group); | ||
68 | 68 | static const PropDescription *group_describe_props(Group *group); |
69 | 69 | static void group_get_props(Group *group, GPtrArray *props); |
70 | 70 | static void group_set_props(Group *group, GPtrArray *props); |
71 | +static void group_transform (Group *group, const DiaMatrix *m); | |
71 | 72 | |
72 | 73 | static ObjectOps group_ops = { |
73 | 74 | (DestroyFunc) group_destroy, |
@@ -85,6 +86,7 @@ static ObjectOps group_ops = { | ||
85 | 86 | (SetPropsFunc) group_set_props, |
86 | 87 | (TextEditFunc) 0, |
87 | 88 | (ApplyPropertiesListFunc) group_apply_properties_list, |
89 | + (TransformFunc) group_transform | |
88 | 90 | }; |
89 | 91 | |
90 | 92 | DiaObjectType group_type = { |
@@ -888,7 +890,7 @@ group_prop_change_free(GroupPropChange *change) | ||
888 | 890 | g_list_free(change->changes_per_object); |
889 | 891 | } |
890 | 892 | |
891 | -void | |
893 | +static void | |
892 | 894 | group_transform (Group *group, const DiaMatrix *m) |
893 | 895 | { |
894 | 896 | g_return_if_fail (m != NULL); |
@@ -33,7 +33,6 @@ DiaObject *group_create_with_matrix(GList *objects, DiaMatrix *matrix); | ||
33 | 33 | GList *group_objects(DiaObject *group); |
34 | 34 | |
35 | 35 | void group_destroy_shallow(DiaObject *group); |
36 | -void group_transform (Group *group, const DiaMatrix *mat); | |
37 | 36 | const DiaMatrix *group_get_transform (Group *group); |
38 | 37 | |
39 | 38 | #define IS_GROUP(obj) ((obj)->type == &group_type) |
@@ -300,6 +300,7 @@ EXPORTS | ||
300 | 300 | pixbuf_decode_base64 |
301 | 301 | |
302 | 302 | dia_path_renderer_get_type |
303 | + path_build_ellipse | |
303 | 304 | |
304 | 305 | dia_pattern_new |
305 | 306 | dia_pattern_add_color |
@@ -491,7 +492,6 @@ EXPORTS | ||
491 | 492 | group_destroy_shallow |
492 | 493 | group_objects |
493 | 494 | group_type |
494 | - group_transform | |
495 | 495 | |
496 | 496 | get_active_focus |
497 | 497 | give_focus |
@@ -403,6 +403,20 @@ typedef DiaMenu *(*ObjectMenuFunc) (DiaObject* obj, Point *position); | ||
403 | 403 | */ |
404 | 404 | typedef gboolean (*TextEditFunc) (DiaObject *obj, Text *text, TextEditState state, gchar *textchange); |
405 | 405 | |
406 | +/*! | |
407 | + * \brief Transform the object with the given matrix | |
408 | + * | |
409 | + * This function - if not null - will apply the transformation matrix to the | |
410 | + * object. It should be implemented for every standard object, because it's | |
411 | + * main use-case is the support of transformations from SVG. | |
412 | + * | |
413 | + * @param obj Explicit this pointer | |
414 | + * @param m The transformation matrix | |
415 | + * @returns TRUE if the matrix can be applied to the object, FALSE otherwise. | |
416 | + * \public \memberof _DiaObject | |
417 | + */ | |
418 | +typedef gboolean (*TransformFunc) (DiaObject *obj, const DiaMatrix *m); | |
419 | + | |
406 | 420 | /************************************* |
407 | 421 | ** The functions provided in object.c |
408 | 422 | *************************************/ |
@@ -479,14 +493,15 @@ struct _ObjectOps { | ||
479 | 493 | TextEditFunc edit_text; |
480 | 494 | |
481 | 495 | ApplyPropertiesListFunc apply_properties_list; |
482 | - | |
496 | + /*! check for NULL before calling */ | |
497 | + TransformFunc transform; | |
483 | 498 | /*! |
484 | 499 | Unused places (for extension). |
485 | 500 | These should be NULL for now. In the future they might be used. |
486 | 501 | Then an older object will be binary compatible, because all new code |
487 | 502 | checks if new ops are supported (!= NULL) |
488 | 503 | */ |
489 | - void (*(unused[4]))(DiaObject *obj,...); | |
504 | + void (*(unused[3]))(DiaObject *obj,...); | |
490 | 505 | }; |
491 | 506 | |
492 | 507 | /*! |
@@ -498,7 +513,7 @@ struct _ObjectOps { | ||
498 | 513 | when connection objects. (Then handles and |
499 | 514 | connections are changed). |
500 | 515 | |
501 | - position is not necessarly the corner of the object, but rather | |
516 | + position is not necessarily the corner of the object, but rather | |
502 | 517 | some 'good' spot on it which will be natural to snap to. |
503 | 518 | */ |
504 | 519 | struct _DiaObject { |
@@ -540,13 +555,13 @@ struct _DiaObject { | ||
540 | 555 | * should not be accessed directly, but through dia_object_get_bounding_box(). |
541 | 556 | * Note that handles and connection points are not included by this, but |
542 | 557 | * added by that which needs it. |
543 | - * Internal: If this is set to a 0x0 box, returns bounding_box. That is for | |
558 | + * Internal: If this is set to a NULL, returns bounding_box. That is for | |
544 | 559 | * those objects that don't actually calculate it, but can just use the BB. |
545 | 560 | * Since handles and CPs are not in the BB, that will be the case for most |
546 | 561 | * objects. |
547 | 562 | */ |
548 | 563 | Rectangle *enclosing_box; |
549 | - /*! Metainfo of the object, should not be manipulated directy. Use dia_object_set_meta() */ | |
564 | + /*! Metainfo of the object, should not be manipulated directly. Use dia_object_set_meta() */ | |
550 | 565 | GHashTable *meta; |
551 | 566 | }; |
552 | 567 |
@@ -186,6 +186,7 @@ static DiaMenu *stdpath_get_object_menu(StdPath *stdpath, | ||
186 | 186 | Point *clickedpoint); |
187 | 187 | static void stdpath_get_props(StdPath *stdpath, GPtrArray *props); |
188 | 188 | static void stdpath_set_props(StdPath *stdpath, GPtrArray *props); |
189 | +static gboolean stdpath_transform(StdPath *stdpath, const DiaMatrix *m); | |
189 | 190 | |
190 | 191 | static ObjectOps stdpath_ops = { |
191 | 192 | (DestroyFunc) stdpath_destroy, |
@@ -203,6 +204,7 @@ static ObjectOps stdpath_ops = { | ||
203 | 204 | (SetPropsFunc) stdpath_set_props, |
204 | 205 | (TextEditFunc) 0, |
205 | 206 | (ApplyPropertiesListFunc) object_apply_props, |
207 | + (TransformFunc) stdpath_transform, | |
206 | 208 | }; |
207 | 209 | |
208 | 210 | /*! |
@@ -1030,6 +1032,23 @@ stdpath_select (StdPath *stdpath, Point *clicked_point, | ||
1030 | 1032 | { |
1031 | 1033 | stdpath_update_handles (stdpath); |
1032 | 1034 | } |
1035 | +/*! | |
1036 | + * \brief Change the object state regarding selection | |
1037 | + * \memberof _StdPath | |
1038 | + */ | |
1039 | +static gboolean | |
1040 | +stdpath_transform(StdPath *stdpath, const DiaMatrix *m) | |
1041 | +{ | |
1042 | + int i; | |
1043 | + | |
1044 | + g_return_val_if_fail (m != NULL, FALSE); | |
1045 | + | |
1046 | + for (i = 0; i < stdpath->num_points; i++) | |
1047 | + transform_bezpoint (&stdpath->points[i], m); | |
1048 | + | |
1049 | + stdpath_update_data(stdpath); | |
1050 | + return TRUE; | |
1051 | +} | |
1033 | 1052 | |
1034 | 1053 | gboolean |
1035 | 1054 | text_to_path (const Text *text, GArray *points) |
@@ -85,6 +85,7 @@ static void bezierline_save(Bezierline *bezierline, ObjectNode obj_node, | ||
85 | 85 | DiaContext *ctx); |
86 | 86 | static DiaObject *bezierline_load(ObjectNode obj_node, int version, DiaContext *ctx); |
87 | 87 | static DiaMenu *bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint); |
88 | +static gboolean bezierline_transform(Bezierline *bezierline, const DiaMatrix *m); | |
88 | 89 | |
89 | 90 | static void compute_gap_points(Bezierline *bezierline, Point *gap_points); |
90 | 91 | static real approx_bez_length(BezierConn *bez); |
@@ -129,6 +130,7 @@ static ObjectOps bezierline_ops = { | ||
129 | 130 | (SetPropsFunc) bezierline_set_props, |
130 | 131 | (TextEditFunc) 0, |
131 | 132 | (ApplyPropertiesListFunc) object_apply_props, |
133 | + (TransformFunc) bezierline_transform, | |
132 | 134 | }; |
133 | 135 | |
134 | 136 | static PropNumData gap_range = { -G_MAXFLOAT, G_MAXFLOAT, 0.1}; |
@@ -812,3 +814,17 @@ bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint) | ||
812 | 814 | (ctype != BEZ_CORNER_CUSP); |
813 | 815 | return &bezierline_menu; |
814 | 816 | } |
817 | + | |
818 | +static gboolean | |
819 | +bezierline_transform(Bezierline *bezierline, const DiaMatrix *m) | |
820 | +{ | |
821 | + int i; | |
822 | + | |
823 | + g_return_val_if_fail (m != NULL, FALSE); | |
824 | + | |
825 | + for (i = 0; i < bezierline->bez.bezier.num_points; i++) | |
826 | + transform_bezpoint (&bezierline->bez.bezier.points[i], m); | |
827 | + | |
828 | + bezierline_update_data(bezierline); | |
829 | + return TRUE; | |
830 | +} |
@@ -88,6 +88,7 @@ static void beziergon_save(Beziergon *beziergon, ObjectNode obj_node, | ||
88 | 88 | static DiaObject *beziergon_load(ObjectNode obj_node, int version, DiaContext *ctx); |
89 | 89 | static DiaMenu *beziergon_get_object_menu(Beziergon *beziergon, |
90 | 90 | Point *clickedpoint); |
91 | +static gboolean beziergon_transform(Beziergon *beziergon, const DiaMatrix *m); | |
91 | 92 | |
92 | 93 | static ObjectTypeOps beziergon_type_ops = |
93 | 94 | { |
@@ -155,6 +156,7 @@ static ObjectOps beziergon_ops = { | ||
155 | 156 | (SetPropsFunc) beziergon_set_props, |
156 | 157 | (TextEditFunc) 0, |
157 | 158 | (ApplyPropertiesListFunc) object_apply_props, |
159 | + (TransformFunc) beziergon_transform, | |
158 | 160 | }; |
159 | 161 | |
160 | 162 | static void |
@@ -545,3 +547,17 @@ beziergon_get_object_menu(Beziergon *beziergon, Point *clickedpoint) | ||
545 | 547 | beziergon_menu_items[1].active = beziergon->bezier.bezier.num_points > 3; |
546 | 548 | return &beziergon_menu; |
547 | 549 | } |
550 | + | |
551 | +static gboolean | |
552 | +beziergon_transform(Beziergon *beziergon, const DiaMatrix *m) | |
553 | +{ | |
554 | + int i; | |
555 | + | |
556 | + g_return_val_if_fail (m != NULL, FALSE); | |
557 | + | |
558 | + for (i = 0; i < beziergon->bezier.bezier.num_points; i++) | |
559 | + transform_bezpoint (&beziergon->bezier.bezier.points[i], m); | |
560 | + | |
561 | + beziergon_update_data(beziergon); | |
562 | + return TRUE; | |
563 | +} |
@@ -31,6 +31,7 @@ | ||
31 | 31 | #include "attributes.h" |
32 | 32 | #include "properties.h" |
33 | 33 | #include "create.h" |
34 | +#include "message.h" | |
34 | 35 | #include "pattern.h" |
35 | 36 | |
36 | 37 | #include "tool-icons.h" |
@@ -69,6 +70,7 @@ struct _Box { | ||
69 | 70 | real corner_radius; |
70 | 71 | AspectType aspect; |
71 | 72 | DiaPattern *pattern; |
73 | + real angle; /*!< between [-45°-45°] to simplify connection point handling */ | |
72 | 74 | }; |
73 | 75 | |
74 | 76 | static struct _BoxProperties { |
@@ -99,6 +101,7 @@ static void box_set_props(Box *box, GPtrArray *props); | ||
99 | 101 | static void box_save(Box *box, ObjectNode obj_node, DiaContext *ctx); |
100 | 102 | static DiaObject *box_load(ObjectNode obj_node, int version, DiaContext *ctx); |
101 | 103 | static DiaMenu *box_get_object_menu(Box *box, Point *clickedpoint); |
104 | +static gboolean box_transform(Box *box, const DiaMatrix *m); | |
102 | 105 | |
103 | 106 | static ObjectTypeOps box_type_ops = |
104 | 107 | { |
@@ -121,10 +124,12 @@ static PropOffset box_offsets[] = { | ||
121 | 124 | { "line_join", PROP_TYPE_ENUM, offsetof(Box, line_join) }, |
122 | 125 | { "corner_radius", PROP_TYPE_REAL, offsetof(Box, corner_radius) }, |
123 | 126 | { "pattern", PROP_TYPE_PATTERN, offsetof(Box, pattern) }, |
127 | + { "angle", PROP_TYPE_REAL, offsetof(Box, angle) }, | |
124 | 128 | { NULL, 0, 0 } |
125 | 129 | }; |
126 | 130 | |
127 | 131 | static PropNumData corner_radius_data = { 0.0, 10.0, 0.1 }; |
132 | +static PropNumData angle_data = { -45, 45, 1 }; | |
128 | 133 | |
129 | 134 | static PropEnumData prop_aspect_data[] = { |
130 | 135 | { N_("Free"), FREE_ASPECT }, |
@@ -145,6 +150,8 @@ static PropDescription box_props[] = { | ||
145 | 150 | { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE, |
146 | 151 | N_("Aspect ratio"), NULL, prop_aspect_data }, |
147 | 152 | PROP_STD_PATTERN, |
153 | + { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL, | |
154 | + N_("Rotation"), N_("Rotation angle"), &angle_data }, | |
148 | 155 | PROP_DESC_END |
149 | 156 | }; |
150 | 157 |
@@ -179,6 +186,7 @@ static ObjectOps box_ops = { | ||
179 | 186 | (SetPropsFunc) box_set_props, |
180 | 187 | (TextEditFunc) 0, |
181 | 188 | (ApplyPropertiesListFunc) object_apply_props, |
189 | + (TransformFunc) box_transform, | |
182 | 190 | }; |
183 | 191 | |
184 | 192 | static void |
@@ -189,17 +197,50 @@ box_set_props(Box *box, GPtrArray *props) | ||
189 | 197 | box_update_data(box); |
190 | 198 | } |
191 | 199 | |
200 | +static void | |
201 | +_box_get_poly (const Box *box, Point corners[4]) | |
202 | +{ | |
203 | + const Element *elem = &box->element; | |
204 | + | |
205 | + corners[0] = elem->corner; | |
206 | + corners[1] = corners[0]; | |
207 | + corners[1].x += elem->width; | |
208 | + corners[2] = corners[1]; | |
209 | + corners[2].y += elem->height; | |
210 | + corners[3] = corners[2]; | |
211 | + corners[3].x -= elem->width; | |
212 | + | |
213 | + if (box->angle != 0) { | |
214 | + real cx = elem->corner.x + elem->width / 2.0; | |
215 | + real cy = elem->corner.y + elem->height / 2.0; | |
216 | + DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy }; | |
217 | + DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy }; | |
218 | + int i; | |
219 | + | |
220 | + dia_matrix_set_angle_and_scales (&m, G_PI*box->angle/180, 1.0, 1.0); | |
221 | + dia_matrix_multiply (&m, &t, &m); | |
222 | + for (i = 0; i < 4; ++i) | |
223 | + transform_point (&corners[i], &m); | |
224 | + } | |
225 | +} | |
226 | + | |
192 | 227 | static real |
193 | 228 | box_distance_from(Box *box, Point *point) |
194 | 229 | { |
195 | 230 | Element *elem = &box->element; |
196 | - Rectangle rect; | |
197 | 231 | |
198 | - rect.left = elem->corner.x - box->border_width/2; | |
199 | - rect.right = elem->corner.x + elem->width + box->border_width/2; | |
200 | - rect.top = elem->corner.y - box->border_width/2; | |
201 | - rect.bottom = elem->corner.y + elem->height + box->border_width/2; | |
202 | - return distance_rectangle_point(&rect, point); | |
232 | + if (box->angle == 0) { | |
233 | + Rectangle rect; | |
234 | + rect.left = elem->corner.x - box->border_width/2; | |
235 | + rect.right = elem->corner.x + elem->width + box->border_width/2; | |
236 | + rect.top = elem->corner.y - box->border_width/2; | |
237 | + rect.bottom = elem->corner.y + elem->height + box->border_width/2; | |
238 | + return distance_rectangle_point(&rect, point); | |
239 | + } else { | |
240 | + Point corners[4]; | |
241 | + _box_get_poly (box, corners); | |
242 | + return distance_polygon_point (corners, 4, box->border_width, point); | |
243 | + } | |
203 | 244 | } |
204 | 245 | |
205 | 246 | static void |
@@ -326,31 +367,28 @@ box_draw(Box *box, DiaRenderer *renderer) | ||
326 | 367 | if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN)) |
327 | 368 | renderer_ops->set_pattern (renderer, box->pattern); |
328 | 369 | } |
329 | - /* we only want separate calls for potential pattern fill */ | |
330 | - if (box->corner_radius > 0) { | |
370 | + if (box->angle == 0) { | |
331 | 371 | renderer_ops->draw_rounded_rect (renderer, |
332 | 372 | &elem->corner, &lr_corner, |
333 | 373 | &fill, &box->border_color, |
334 | 374 | box->corner_radius); |
335 | 375 | } else { |
336 | - renderer_ops->draw_rect(renderer, | |
337 | - &elem->corner, | |
338 | - &lr_corner, | |
339 | - &fill, &box->border_color); | |
376 | + Point poly[4]; | |
377 | + _box_get_poly (box, poly); | |
378 | + renderer_ops->draw_polygon (renderer, poly, 4, &fill, &box->border_color); | |
340 | 379 | } |
341 | 380 | if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN)) |
342 | 381 | renderer_ops->set_pattern (renderer, NULL); |
343 | 382 | } else { |
344 | - if (box->corner_radius > 0) { | |
383 | + if (box->angle == 0) { | |
345 | 384 | renderer_ops->draw_rounded_rect (renderer, |
346 | 385 | &elem->corner, &lr_corner, |
347 | 386 | NULL, &box->border_color, |
348 | 387 | box->corner_radius); |
349 | 388 | } else { |
350 | - renderer_ops->draw_rect (renderer, | |
351 | - &elem->corner, | |
352 | - &lr_corner, | |
353 | - NULL, &box->border_color); | |
389 | + Point poly[4]; | |
390 | + _box_get_poly (box, poly); | |
391 | + renderer_ops->draw_polygon (renderer, poly, 4, &box->inner_color, &box->border_color); | |
354 | 392 | } |
355 | 393 | } |
356 | 394 | } |
@@ -393,6 +431,18 @@ box_update_data(Box *box) | ||
393 | 431 | box->connections[8].pos.x = elem->corner.x + elem->width / 2.0; |
394 | 432 | box->connections[8].pos.y = elem->corner.y + elem->height / 2.0; |
395 | 433 | |
434 | + if (box->angle != 0) { | |
435 | + real cx = elem->corner.x + elem->width / 2.0; | |
436 | + real cy = elem->corner.y + elem->height / 2.0; | |
437 | + DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy }; | |
438 | + DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy }; | |
439 | + int i; | |
440 | + | |
441 | + dia_matrix_set_angle_and_scales (&m, G_PI*box->angle/180, 1.0, 1.0); | |
442 | + dia_matrix_multiply (&m, &t, &m); | |
443 | + for (i = 0; i < 8; ++i) | |
444 | + transform_point (&box->connections[i].pos, &m); | |
445 | + } | |
396 | 446 | box->connections[0].directions = DIR_NORTH|DIR_WEST; |
397 | 447 | box->connections[1].directions = DIR_NORTH; |
398 | 448 | box->connections[2].directions = DIR_NORTH|DIR_EAST; |
@@ -455,6 +505,7 @@ box_create(Point *startpoint, | ||
455 | 505 | box->show_background = default_properties.show_background; |
456 | 506 | box->corner_radius = default_properties.corner_radius; |
457 | 507 | box->aspect = default_properties.aspect; |
508 | + box->angle = 0.0; | |
458 | 509 | |
459 | 510 | element_init(elem, 8, NUM_CONNECTIONS); |
460 | 511 |
@@ -505,6 +556,7 @@ box_copy(Box *box) | ||
505 | 556 | newbox->dashlength = box->dashlength; |
506 | 557 | newbox->corner_radius = box->corner_radius; |
507 | 558 | newbox->aspect = box->aspect; |
559 | + newbox->angle = box->angle; | |
508 | 560 | if (box->pattern) |
509 | 561 | newbox->pattern = g_object_ref (box->pattern); |
510 | 562 |
@@ -563,6 +615,11 @@ box_save(Box *box, ObjectNode obj_node, DiaContext *ctx) | ||
563 | 615 | if (box->pattern) |
564 | 616 | data_add_pattern(new_attribute(obj_node, "pattern"), |
565 | 617 | box->pattern, ctx); |
618 | + | |
619 | + if (box->angle != 0.0) | |
620 | + data_add_real(new_attribute(obj_node, "angle"), | |
621 | + box->angle, ctx); | |
622 | + | |
566 | 623 | } |
567 | 624 | |
568 | 625 | static DiaObject * |
@@ -632,6 +689,11 @@ box_load(ObjectNode obj_node, int version, DiaContext *ctx) | ||
632 | 689 | if (attr != NULL) |
633 | 690 | box->pattern = data_pattern(attribute_first_data(attr), ctx); |
634 | 691 | |
692 | + box->angle = 0.0; | |
693 | + attr = object_find_attribute(obj_node, "angle"); | |
694 | + if (attr != NULL) | |
695 | + box->angle = data_real(attribute_first_data(attr), ctx); | |
696 | + | |
635 | 697 | element_init(elem, 8, NUM_CONNECTIONS); |
636 | 698 | |
637 | 699 | for (i=0;i<NUM_CONNECTIONS;i++) { |
@@ -744,3 +806,33 @@ box_get_object_menu(Box *box, Point *clickedpoint) | ||
744 | 806 | |
745 | 807 | return &box_menu; |
746 | 808 | } |
809 | + | |
810 | +static gboolean | |
811 | +box_transform(Box *box, const DiaMatrix *m) | |
812 | +{ | |
813 | + real a, sx, sy; | |
814 | + | |
815 | + g_return_val_if_fail(m != NULL, FALSE); | |
816 | + | |
817 | + if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) { | |
818 | + dia_log_message ("box_transform() can't convert given matrix"); | |
819 | + return FALSE; | |
820 | + } else { | |
821 | + real width = box->element.width * sx; | |
822 | + real height = box->element.height * sy; | |
823 | + real angle = a*180/G_PI; | |
824 | + Point c = { box->element.corner.x + width/2.0, box->element.corner.y + height/2.0 }; | |
825 | + | |
826 | + /* rotation is invariant to the center */ | |
827 | + transform_point (&c, m); | |
828 | + /* XXX: we have to bring angle in range [-45..45] which may swap width and height */ | |
829 | + box->angle = angle; | |
830 | + box->element.width = width; | |
831 | + box->element.height = height; | |
832 | + box->element.corner.x = c.x - width / 2.0; | |
833 | + box->element.corner.y = c.y - height / 2.0; | |
834 | + } | |
835 | + | |
836 | + box_update_data(box); | |
837 | + return TRUE; | |
838 | +} |
@@ -31,6 +31,8 @@ | ||
31 | 31 | #include "attributes.h" |
32 | 32 | #include "properties.h" |
33 | 33 | #include "pattern.h" |
34 | +#include "diapathrenderer.h" | |
35 | +#include "message.h" | |
34 | 36 | |
35 | 37 | #include "tool-icons.h" |
36 | 38 |
@@ -65,6 +67,7 @@ struct _Ellipse { | ||
65 | 67 | LineStyle line_style; |
66 | 68 | real dashlength; |
67 | 69 | DiaPattern *pattern; |
70 | + real angle; /*!< between [-45ー-45ー] to simplify connection point handling */ | |
68 | 71 | }; |
69 | 72 | |
70 | 73 | static struct _EllipseProperties { |
@@ -93,6 +96,7 @@ static void ellipse_set_props(Ellipse *ellipse, GPtrArray *props); | ||
93 | 96 | static void ellipse_save(Ellipse *ellipse, ObjectNode obj_node, DiaContext *ctx); |
94 | 97 | static DiaObject *ellipse_load(ObjectNode obj_node, int version, DiaContext *ctx); |
95 | 98 | static DiaMenu *ellipse_get_object_menu(Ellipse *ellipse, Point *clickedpoint); |
99 | +static gboolean ellipse_transform(Ellipse *ellipse, const DiaMatrix *m); | |
96 | 100 | |
97 | 101 | static ObjectTypeOps ellipse_type_ops = |
98 | 102 | { |
@@ -113,9 +117,12 @@ static PropOffset ellipse_offsets[] = { | ||
113 | 117 | { "line_style", PROP_TYPE_LINESTYLE, |
114 | 118 | offsetof(Ellipse, line_style), offsetof(Ellipse, dashlength) }, |
115 | 119 | { "pattern", PROP_TYPE_PATTERN, offsetof(Ellipse, pattern) }, |
120 | + { "angle", PROP_TYPE_REAL, offsetof(Ellipse, angle) }, | |
116 | 121 | { NULL, 0, 0 } |
117 | 122 | }; |
118 | 123 | |
124 | +static PropNumData angle_data = { -45, 45, 1 }; | |
125 | + | |
119 | 126 | static PropEnumData prop_aspect_data[] = { |
120 | 127 | { N_("Free"), FREE_ASPECT }, |
121 | 128 | { N_("Fixed"), FIXED_ASPECT }, |
@@ -132,6 +139,8 @@ static PropDescription ellipse_props[] = { | ||
132 | 139 | { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE, |
133 | 140 | N_("Aspect ratio"), NULL, prop_aspect_data }, |
134 | 141 | PROP_STD_PATTERN, |
142 | + { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL, | |
143 | + N_("Rotation"), N_("Rotation angle"), &angle_data }, | |
135 | 144 | PROP_DESC_END |
136 | 145 | }; |
137 | 146 |
@@ -166,6 +175,7 @@ static ObjectOps ellipse_ops = { | ||
166 | 175 | (SetPropsFunc) ellipse_set_props, |
167 | 176 | (TextEditFunc) 0, |
168 | 177 | (ApplyPropertiesListFunc) object_apply_props, |
178 | + (TransformFunc) ellipse_transform, | |
169 | 179 | }; |
170 | 180 | |
171 | 181 | static void |
@@ -178,6 +188,24 @@ ellipse_set_props(Ellipse *ellipse, GPtrArray *props) | ||
178 | 188 | ellipse_update_data(ellipse); |
179 | 189 | } |
180 | 190 | |
191 | +static GArray * | |
192 | +_ellipse_to_path (Ellipse *ellipse, Point *center) | |
193 | +{ | |
194 | + Element *elem = &ellipse->element; | |
195 | + DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, center->x, center->y }; | |
196 | + DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -center->x, -center->y }; | |
197 | + GArray *path; | |
198 | + int i; | |
199 | + | |
200 | + dia_matrix_set_angle_and_scales (&m, G_PI*ellipse->angle/180, 1.0, 1.0); | |
201 | + dia_matrix_multiply (&m, &t, &m); | |
202 | + path = g_array_new (FALSE, FALSE, sizeof(BezPoint)); | |
203 | + path_build_ellipse (path, center, elem->width, elem->height); | |
204 | + for (i = 0; i < path->len; ++i) | |
205 | + transform_bezpoint (&g_array_index (path, BezPoint, i), &m); | |
206 | + return path; | |
207 | +} | |
208 | + | |
181 | 209 | static real |
182 | 210 | ellipse_distance_from(Ellipse *ellipse, Point *point) |
183 | 211 | { |
@@ -187,6 +215,15 @@ ellipse_distance_from(Ellipse *ellipse, Point *point) | ||
187 | 215 | center.x = elem->corner.x+elem->width/2; |
188 | 216 | center.y = elem->corner.y+elem->height/2; |
189 | 217 | |
218 | + if (ellipse->angle != 0) { | |
219 | + real dist; | |
220 | + GArray *path = _ellipse_to_path (ellipse, ¢er); | |
221 | + | |
222 | + dist = distance_bez_shape_point (&g_array_index (path, BezPoint, 0), path->len, | |
223 | + ellipse->border_width, point); | |
224 | + g_array_free (path, TRUE); | |
225 | + return dist; | |
226 | + } | |
190 | 227 | return distance_ellipse_point(¢er, elem->width, elem->height, |
191 | 228 | ellipse->border_width, point); |
192 | 229 | } |
@@ -298,7 +335,8 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer) | ||
298 | 335 | DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer); |
299 | 336 | Point center; |
300 | 337 | Element *elem; |
301 | - | |
338 | + GArray *path = NULL; | |
339 | + | |
302 | 340 | assert(ellipse != NULL); |
303 | 341 | assert(renderer != NULL); |
304 | 342 |
@@ -307,6 +345,9 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer) | ||
307 | 345 | center.x = elem->corner.x + elem->width/2; |
308 | 346 | center.y = elem->corner.y + elem->height/2; |
309 | 347 | |
348 | + if (ellipse->angle != 0) | |
349 | + path = _ellipse_to_path (ellipse, ¢er); | |
350 | + | |
310 | 351 | renderer_ops->set_linewidth(renderer, ellipse->border_width); |
311 | 352 | renderer_ops->set_linestyle(renderer, ellipse->line_style, ellipse->dashlength); |
312 | 353 | if (ellipse->show_background) { |
@@ -317,18 +358,30 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer) | ||
317 | 358 | if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN)) |
318 | 359 | renderer_ops->set_pattern (renderer, ellipse->pattern); |
319 | 360 | } |
320 | - renderer_ops->draw_ellipse (renderer, | |
321 | - ¢er, | |
322 | - elem->width, elem->height, | |
323 | - &fill, &ellipse->border_color); | |
361 | + if (!path) | |
362 | + renderer_ops->draw_ellipse (renderer, | |
363 | + ¢er, | |
364 | + elem->width, elem->height, | |
365 | + &fill, &ellipse->border_color); | |
366 | + else | |
367 | + renderer_ops->draw_beziergon (renderer, | |
368 | + &g_array_index (path, BezPoint, 0), path->len, | |
369 | + &fill, &ellipse->border_color); | |
324 | 370 | if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN)) |
325 | 371 | renderer_ops->set_pattern (renderer, NULL); |
326 | 372 | } else { |
327 | - renderer_ops->draw_ellipse (renderer, | |
328 | - ¢er, | |
329 | - elem->width, elem->height, | |
330 | - NULL, &ellipse->border_color); | |
373 | + if (!path) | |
374 | + renderer_ops->draw_ellipse (renderer, | |
375 | + ¢er, | |
376 | + elem->width, elem->height, | |
377 | + NULL, &ellipse->border_color); | |
378 | + else | |
379 | + renderer_ops->draw_beziergon (renderer, | |
380 | + &g_array_index (path, BezPoint, 0), path->len, | |
381 | + NULL, &ellipse->border_color); | |
331 | 382 | } |
383 | + if (path) | |
384 | + g_array_free (path, TRUE); | |
332 | 385 | } |
333 | 386 | |
334 | 387 | static void |
@@ -372,6 +425,16 @@ ellipse_update_data(Ellipse *ellipse) | ||
372 | 425 | ellipse->connections[8].pos.x = center.x; |
373 | 426 | ellipse->connections[8].pos.y = center.y; |
374 | 427 | |
428 | + if (ellipse->angle != 0) { | |
429 | + DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, center.x, center.y }; | |
430 | + DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -center.x, -center.y }; | |
431 | + int i; | |
432 | + | |
433 | + dia_matrix_set_angle_and_scales (&m, G_PI*ellipse->angle/180, 1.0, 1.0); | |
434 | + dia_matrix_multiply (&m, &t, &m); | |
435 | + for (i = 0; i < 8; ++i) | |
436 | + transform_point (&ellipse->connections[i].pos, &m); | |
437 | + } | |
375 | 438 | /* Update directions -- if the ellipse is very thin, these may not be good */ |
376 | 439 | ellipse->connections[0].directions = DIR_NORTH|DIR_WEST; |
377 | 440 | ellipse->connections[1].directions = DIR_NORTH; |
@@ -476,6 +539,7 @@ ellipse_copy(Ellipse *ellipse) | ||
476 | 539 | newellipse->dashlength = ellipse->dashlength; |
477 | 540 | newellipse->show_background = ellipse->show_background; |
478 | 541 | newellipse->aspect = ellipse->aspect; |
542 | + newellipse->angle = ellipse->angle; | |
479 | 543 | newellipse->line_style = ellipse->line_style; |
480 | 544 | if (ellipse->pattern) |
481 | 545 | newellipse->pattern = g_object_ref (ellipse->pattern); |
@@ -520,6 +584,9 @@ ellipse_save(Ellipse *ellipse, ObjectNode obj_node, DiaContext *ctx) | ||
520 | 584 | if (ellipse->aspect != FREE_ASPECT) |
521 | 585 | data_add_enum(new_attribute(obj_node, "aspect"), |
522 | 586 | ellipse->aspect, ctx); |
587 | + if (ellipse->angle != 0.0) | |
588 | + data_add_real(new_attribute(obj_node, "angle"), | |
589 | + ellipse->angle, ctx); | |
523 | 590 | |
524 | 591 | if (ellipse->line_style != LINESTYLE_SOLID) { |
525 | 592 | data_add_enum(new_attribute(obj_node, "line_style"), |
@@ -577,6 +644,11 @@ static DiaObject *ellipse_load(ObjectNode obj_node, int version, DiaContext *ctx | ||
577 | 644 | if (attr != NULL) |
578 | 645 | ellipse->aspect = data_enum(attribute_first_data(attr), ctx); |
579 | 646 | |
647 | + ellipse->angle = 0.0; | |
648 | + attr = object_find_attribute(obj_node, "angle"); | |
649 | + if (attr != NULL) | |
650 | + ellipse->angle = data_real(attribute_first_data(attr), ctx); | |
651 | + | |
580 | 652 | ellipse->line_style = LINESTYLE_SOLID; |
581 | 653 | attr = object_find_attribute(obj_node, "line_style"); |
582 | 654 | if (attr != NULL) |
@@ -708,3 +780,33 @@ ellipse_get_object_menu(Ellipse *ellipse, Point *clickedpoint) | ||
708 | 780 | |
709 | 781 | return &ellipse_menu; |
710 | 782 | } |
783 | + | |
784 | +static gboolean | |
785 | +ellipse_transform(Ellipse *ellipse, const DiaMatrix *m) | |
786 | +{ | |
787 | + real a, sx, sy; | |
788 | + | |
789 | + g_return_val_if_fail(m != NULL, FALSE); | |
790 | + | |
791 | + if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) { | |
792 | + dia_log_message ("ellipse_transform() can't convert given matrix"); | |
793 | + return FALSE; | |
794 | + } else { | |
795 | + real width = ellipse->element.width * sx; | |
796 | + real height = ellipse->element.height * sy; | |
797 | + real angle = a*180/G_PI; | |
798 | + Point c = { ellipse->element.corner.x + width/2.0, ellipse->element.corner.y + height/2.0 }; | |
799 | + | |
800 | + /* rotation is invariant to the center */ | |
801 | + transform_point (&c, m); | |
802 | + /* XXX: we have to bring angle in range [-45..45] which may swap width and height */ | |
803 | + ellipse->angle = angle; | |
804 | + ellipse->element.width = width; | |
805 | + ellipse->element.height = height; | |
806 | + ellipse->element.corner.x = c.x - width / 2.0; | |
807 | + ellipse->element.corner.y = c.y - height / 2.0; | |
808 | + } | |
809 | + | |
810 | + ellipse_update_data(ellipse); | |
811 | + return TRUE; | |
812 | +} |
@@ -89,6 +89,7 @@ static ObjectChange* image_move_handle(Image *image, Handle *handle, | ||
89 | 89 | HandleMoveReason reason, ModifierKeys modifiers); |
90 | 90 | static ObjectChange* image_move(Image *image, Point *to); |
91 | 91 | static void image_draw(Image *image, DiaRenderer *renderer); |
92 | +static gboolean image_transform(Image *image, const DiaMatrix *m); | |
92 | 93 | static void image_update_data(Image *image); |
93 | 94 | static DiaObject *image_create(Point *startpoint, |
94 | 95 | void *user_data, |
@@ -160,6 +161,7 @@ static ObjectOps image_ops = { | ||
160 | 161 | (SetPropsFunc) image_set_props, |
161 | 162 | (TextEditFunc) 0, |
162 | 163 | (ApplyPropertiesListFunc) object_apply_props, |
164 | + (TransformFunc) image_transform, | |
163 | 165 | }; |
164 | 166 | |
165 | 167 | static PropOffset image_offsets[] = { |
@@ -463,6 +465,34 @@ image_draw(Image *image, DiaRenderer *renderer) | ||
463 | 465 | } |
464 | 466 | } |
465 | 467 | |
468 | +static gboolean | |
469 | +image_transform(Image *image, const DiaMatrix *m) | |
470 | +{ | |
471 | + Element *elem = &image->element; | |
472 | + real a, sx, sy; | |
473 | + | |
474 | + g_return_val_if_fail(m != NULL, FALSE); | |
475 | + | |
476 | + if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) { | |
477 | + dia_log_message ("image_transform() can't convert given matrix"); | |
478 | + return FALSE; | |
479 | + } else { | |
480 | + real width = elem->width * sx; | |
481 | + real height = elem->height * sy; | |
482 | + real angle = a*180/G_PI; | |
483 | + Point c = { elem->corner.x + width/2.0, elem->corner.y + height/2.0 }; | |
484 | + | |
485 | + /* rotation is invariant to the center */ | |
486 | + transform_point (&c, m); | |
487 | + /* XXX: implement image rotate! */ | |
488 | + elem->width = width; | |
489 | + elem->height = height; | |
490 | + elem->corner.x = c.x - width / 2.0; | |
491 | + elem->corner.y = c.y - height / 2.0; | |
492 | + } | |
493 | + return TRUE; | |
494 | +} | |
495 | + | |
466 | 496 | static void |
467 | 497 | image_update_data(Image *image) |
468 | 498 | { |
@@ -94,6 +94,7 @@ static void line_set_props(Line *line, GPtrArray *props); | ||
94 | 94 | static void line_save(Line *line, ObjectNode obj_node, DiaContext *ctx); |
95 | 95 | static DiaObject *line_load(ObjectNode obj_node, int version, DiaContext *ctx); |
96 | 96 | static DiaMenu *line_get_object_menu(Line *line, Point *clickedpoint); |
97 | +static gboolean line_transform(Line *line, const DiaMatrix *m); | |
97 | 98 | |
98 | 99 | void Line_adjust_for_absolute_gap(Line *line, Point *gap_endpoints); |
99 | 100 |
@@ -181,6 +182,7 @@ static ObjectOps line_ops = { | ||
181 | 182 | (SetPropsFunc) line_set_props, |
182 | 183 | (TextEditFunc) 0, |
183 | 184 | (ApplyPropertiesListFunc) object_apply_props, |
185 | + (TransformFunc) line_transform, | |
184 | 186 | }; |
185 | 187 | |
186 | 188 | static void |
@@ -342,7 +344,19 @@ line_get_object_menu(Line *line, Point *clickedpoint) | ||
342 | 344 | return &object_menu; |
343 | 345 | } |
344 | 346 | |
347 | +static gboolean | |
348 | +line_transform(Line *line, const DiaMatrix *m) | |
349 | +{ | |
350 | + int i; | |
351 | + | |
352 | + g_return_val_if_fail (m != NULL, FALSE); | |
353 | + | |
354 | + for (i = 0; i < 2; i++) | |
355 | + transform_point (&line->connection.endpoints[i], m); | |
345 | 356 | |
357 | + line_update_data(line); | |
358 | + return TRUE; | |
359 | +} | |
346 | 360 | |
347 | 361 | /*! |
348 | 362 | * \brief Gap calculation for _Line |
@@ -90,6 +90,7 @@ static void polygon_save(Polygon *polygon, ObjectNode obj_node, | ||
90 | 90 | DiaContext *ctx); |
91 | 91 | static DiaObject *polygon_load(ObjectNode obj_node, int version, DiaContext *ctx); |
92 | 92 | static DiaMenu *polygon_get_object_menu(Polygon *polygon, Point *clickedpoint); |
93 | +static gboolean polygon_transform(Polygon *polygon, const DiaMatrix *m); | |
93 | 94 | |
94 | 95 | static ObjectTypeOps polygon_type_ops = |
95 | 96 | { |
@@ -157,6 +158,7 @@ static ObjectOps polygon_ops = { | ||
157 | 158 | (SetPropsFunc) polygon_set_props, |
158 | 159 | (TextEditFunc) 0, |
159 | 160 | (ApplyPropertiesListFunc) object_apply_props, |
161 | + (TransformFunc) polygon_transform, | |
160 | 162 | }; |
161 | 163 | |
162 | 164 | static void |
@@ -500,3 +502,18 @@ polygon_get_object_menu(Polygon *polygon, Point *clickedpoint) | ||
500 | 502 | polygon_menu_items[1].active = polygon->poly.numpoints > 3; |
501 | 503 | return &polygon_menu; |
502 | 504 | } |
505 | + | |
506 | +static gboolean | |
507 | +polygon_transform(Polygon *polygon, const DiaMatrix *m) | |
508 | +{ | |
509 | + PolyShape *poly = &polygon->poly; | |
510 | + int i; | |
511 | + | |
512 | + g_return_val_if_fail (m != NULL, FALSE); | |
513 | + | |
514 | + for (i = 0; i < poly->numpoints; i++) | |
515 | + transform_point (&poly->points[i], m); | |
516 | + | |
517 | + polygon_update_data(polygon); | |
518 | + return TRUE; | |
519 | +} |
@@ -81,6 +81,7 @@ static DiaObject *polyline_load(ObjectNode obj_node, int version, DiaContext *ct | ||
81 | 81 | static DiaMenu *polyline_get_object_menu(Polyline *polyline, Point *clickedpoint); |
82 | 82 | void polyline_calculate_gap_endpoints(Polyline *polyline, Point *gap_endpoints); |
83 | 83 | static void polyline_exchange_gap_points(Polyline *polyline, Point *gap_points); |
84 | +static gboolean polyline_transform(Polyline *polyline, const DiaMatrix *m); | |
84 | 85 | |
85 | 86 | static ObjectTypeOps polyline_type_ops = |
86 | 87 | { |
@@ -164,6 +165,7 @@ static ObjectOps polyline_ops = { | ||
164 | 165 | (SetPropsFunc) polyline_set_props, |
165 | 166 | (TextEditFunc) 0, |
166 | 167 | (ApplyPropertiesListFunc) object_apply_props, |
168 | + (TransformFunc) polyline_transform, | |
167 | 169 | }; |
168 | 170 | |
169 | 171 | static void |
@@ -638,3 +640,18 @@ polyline_get_object_menu(Polyline *polyline, Point *clickedpoint) | ||
638 | 640 | polyline_menu_items[1].active = polyline->poly.numpoints > 2; |
639 | 641 | return &polyline_menu; |
640 | 642 | } |
643 | + | |
644 | +static gboolean | |
645 | +polyline_transform(Polyline *polyline, const DiaMatrix *m) | |
646 | +{ | |
647 | + PolyConn *poly = &polyline->poly; | |
648 | + int i; | |
649 | + | |
650 | + g_return_val_if_fail (m != NULL, FALSE); | |
651 | + | |
652 | + for (i = 0; i < poly->numpoints; i++) | |
653 | + transform_point (&poly->points[i], m); | |
654 | + | |
655 | + polyline_update_data(polyline); | |
656 | + return TRUE; | |
657 | +} |
@@ -4,7 +4,7 @@ | ||
4 | 4 | * |
5 | 5 | * svg-import.c: SVG import filter for dia |
6 | 6 | * Copyright (C) 2002 Steffen Macke |
7 | - * Copyright (C) 2005 Hans Breuer | |
7 | + * Copyright (C) 2005-2014 Hans Breuer | |
8 | 8 | * |
9 | 9 | * This program is free software; you can redistribute it and/or modify |
10 | 10 | * it under the terms of the GNU General Public License as published by |
@@ -216,7 +216,7 @@ _node_get_real (xmlNodePtr node, const char *name, real defval) | ||
216 | 216 | * \ingroup SvgImport |
217 | 217 | */ |
218 | 218 | static void |
219 | -use_position (DiaObject *obj, xmlNodePtr node) | |
219 | +use_position (DiaObject *obj, xmlNodePtr node, DiaContext *ctx) | |
220 | 220 | { |
221 | 221 | Point pos = {0, 0}; |
222 | 222 | xmlChar *str; |
@@ -234,41 +234,29 @@ use_position (DiaObject *obj, xmlNodePtr node) | ||
234 | 234 | DiaMatrix *m = dia_svg_parse_transform ((char *)str, user_scale); |
235 | 235 | |
236 | 236 | if (m) { |
237 | - if (IS_GROUP (obj)) { | |
237 | + if (obj->ops->transform) { | |
238 | 238 | /* it is the only one transformation aware yet */ |
239 | - Group *grp = (Group *)obj; | |
240 | - | |
241 | - group_transform (grp, m); | |
239 | + if (!obj->ops->transform (obj, m)) | |
240 | + dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"), | |
241 | + obj->type->name); | |
242 | 242 | } else { |
243 | 243 | GPtrArray *props = g_ptr_array_new (); |
244 | 244 | |
245 | 245 | PointProperty *pp; |
246 | 246 | RealProperty *pr; |
247 | - PointarrayProperty *pap = NULL; | |
248 | - BezPointarrayProperty *bpap = NULL; | |
249 | - Property *prop = NULL; | |
250 | 247 | |
251 | 248 | /* setting obj_pos is pointless, it is read-only in all objects */ |
252 | 249 | prop_list_add_point (props, "obj_pos", &pos); |
253 | 250 | prop_list_add_point (props, "elem_corner", &pos); |
254 | 251 | prop_list_add_real (props, "elem_width", 1.0); |
255 | 252 | prop_list_add_real (props, "elem_height", 1.0); |
256 | - prop_list_add_real (props, PROP_STDNAME_LINE_WIDTH, 0.1); | |
257 | - | |
258 | - if ((prop = object_prop_by_name_type (obj, "bez_points", PROP_TYPE_BEZPOINTARRAY)) != NULL) { | |
259 | - prop_list_add_list (props, prop_list_from_single (prop)); | |
260 | - bpap = g_ptr_array_index (props, 5); | |
261 | - } else if ((prop = object_prop_by_name_type (obj, "poly_points", PROP_TYPE_POINTARRAY)) != NULL) { | |
262 | - prop_list_add_list (props, prop_list_from_single (prop)); | |
263 | - pap = g_ptr_array_index (props, 5); | |
264 | - } | |
265 | 253 | |
266 | 254 | obj->ops->get_props (obj, props); |
267 | - /* try to transform the object without the full matrix */ | |
255 | + /* XXX: try to transform the object without the full matrix */ | |
268 | 256 | pp = g_ptr_array_index (props, 0); |
269 | 257 | pp->point_data.x += m->x0; |
270 | 258 | pp->point_data.y += m->y0; |
271 | - /* set position a second time, now for non-elements */ | |
259 | + /* XXX: set position a second time, now for non-elements */ | |
272 | 260 | pp = g_ptr_array_index (props, 1); |
273 | 261 | pp->point_data.x += m->x0; |
274 | 262 | pp->point_data.y += m->y0; |
@@ -277,24 +265,8 @@ use_position (DiaObject *obj, xmlNodePtr node) | ||
277 | 265 | pr->real_data *= m->xx; |
278 | 266 | pr = g_ptr_array_index (props, 3); |
279 | 267 | pr->real_data *= m->yy; |
280 | - pr = g_ptr_array_index (props, 4); | |
281 | - pr->real_data *= m->yy; | |
282 | - | |
283 | - if (bpap) { | |
284 | - GArray *data = bpap->bezpointarray_data; | |
285 | - int i; | |
286 | - for (i = 0; i < data->len; ++i) | |
287 | - transform_bezpoint (&g_array_index(data, BezPoint, i), m); | |
288 | - } else if (pap) { | |
289 | - GArray *data = pap->pointarray_data; | |
290 | - int i; | |
291 | - for (i = 0; i < data->len; ++i) | |
292 | - transform_point (&g_array_index(data, Point, i), m); | |
293 | - } | |
294 | 268 | |
295 | 269 | obj->ops->set_props (obj, props); |
296 | - if (prop) | |
297 | - prop->ops->free (prop); | |
298 | 270 | prop_list_free (props); |
299 | 271 | } |
300 | 272 | g_free (m); |
@@ -967,7 +939,7 @@ read_poly_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
967 | 939 | static GList * |
968 | 940 | read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style, |
969 | 941 | GHashTable *style_ht, GHashTable *pattern_ht, |
970 | - GList *list) | |
942 | + GList *list, DiaContext *ctx) | |
971 | 943 | { |
972 | 944 | xmlChar *str; |
973 | 945 | real width, height; |
@@ -995,15 +967,6 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
995 | 967 | width = height = get_value_as_cm((char *) str, NULL)*2; |
996 | 968 | xmlFree(str); |
997 | 969 | } |
998 | - if (matrix) { | |
999 | - /* TODO: transform angle - when it is supported */ | |
1000 | - Point wh = {width, height}; | |
1001 | - transform_point (&wh, matrix); | |
1002 | - width = wh.x; | |
1003 | - height = wh.y; | |
1004 | - transform_point (&start, matrix); | |
1005 | - g_free (matrix); | |
1006 | - } | |
1007 | 970 | /* A negative value is an error [...]. A value of zero disables rendering of the element. */ |
1008 | 971 | if (width <= 0.0 || height <= 0.0) |
1009 | 972 | return list; |
@@ -1014,6 +977,13 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1014 | 977 | props = make_element_props(start.x-(width/2), start.y-(height/2), |
1015 | 978 | width, height); |
1016 | 979 | new_obj->ops->set_props(new_obj, props); |
980 | + if (matrix) { | |
981 | + g_return_val_if_fail (new_obj->ops->transform, list); | |
982 | + if (!new_obj->ops->transform (new_obj, matrix)) | |
983 | + dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"), | |
984 | + new_obj->type->name); | |
985 | + g_free (matrix); | |
986 | + } | |
1017 | 987 | prop_list_free(props); |
1018 | 988 | return g_list_append (list, new_obj); |
1019 | 989 | } |
@@ -1025,7 +995,7 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1025 | 995 | static GList * |
1026 | 996 | read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style, |
1027 | 997 | GHashTable *style_ht, GHashTable *pattern_ht, |
1028 | - GList *list) | |
998 | + GList *list, DiaContext *ctx) | |
1029 | 999 | { |
1030 | 1000 | xmlChar *str; |
1031 | 1001 | DiaObjectType *otype = object_get_type("Standard - Line"); |
@@ -1047,12 +1017,6 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1047 | 1017 | end.x = _node_get_real (node, "x2", start.x); |
1048 | 1018 | end.y = _node_get_real (node, "y2", start.y); |
1049 | 1019 | |
1050 | - if (matrix) { | |
1051 | - transform_point (&start, matrix); | |
1052 | - transform_point (&end, matrix); | |
1053 | - g_free (matrix); | |
1054 | - } | |
1055 | - | |
1056 | 1020 | new_obj = otype->ops->create(&start, otype->default_user_data, |
1057 | 1021 | &h1, &h2); |
1058 | 1022 |
@@ -1072,6 +1036,14 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1072 | 1036 | |
1073 | 1037 | apply_style(new_obj, node, parent_style, style_ht, pattern_ht, TRUE); |
1074 | 1038 | |
1039 | + if (matrix) { | |
1040 | + g_return_val_if_fail (new_obj->ops->transform, list); | |
1041 | + if (!new_obj->ops->transform (new_obj, matrix)) | |
1042 | + dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"), | |
1043 | + new_obj->type->name); | |
1044 | + g_free (matrix); | |
1045 | + } | |
1046 | + | |
1075 | 1047 | return g_list_append (list, new_obj); |
1076 | 1048 | } |
1077 | 1049 |
@@ -1082,7 +1054,7 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1082 | 1054 | static GList * |
1083 | 1055 | read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style, |
1084 | 1056 | GHashTable *style_ht, GHashTable *pattern_ht, |
1085 | - GList *list) | |
1057 | + GList *list, DiaContext *ctx) | |
1086 | 1058 | { |
1087 | 1059 | xmlChar *str; |
1088 | 1060 | real width, height; |
@@ -1126,19 +1098,12 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1126 | 1098 | end.x = start.x + width; |
1127 | 1099 | end.y = start.y + height; |
1128 | 1100 | |
1129 | - if (matrix) { | |
1130 | - /* TODO: for rotated rects we would need to create a polygon */ | |
1131 | - transform_point (&start, matrix); | |
1132 | - transform_point (&end, matrix); | |
1133 | - g_free (matrix); | |
1134 | - width = end.x - start.x; | |
1135 | - height = end.y - start.y; | |
1136 | - } | |
1137 | 1101 | /* A negative value is an error [...]. A value of zero disables rendering of the element. */ |
1138 | 1102 | if (width <= 0.0 || height <= 0.0) |
1139 | 1103 | return list; /* just ignore it w/o much complaints */ |
1140 | 1104 | new_obj = otype->ops->create(&start, otype->default_user_data, |
1141 | 1105 | &h1, &h2); |
1106 | + | |
1142 | 1107 | list = g_list_append (list, new_obj); |
1143 | 1108 | props = prop_list_from_descs(svg_rect_prop_descs, pdtpp_true); |
1144 | 1109 | g_assert(props->len == 3); |
@@ -1160,6 +1125,13 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1160 | 1125 | apply_style(new_obj, node, parent_style, style_ht, pattern_ht, TRUE); |
1161 | 1126 | prop_list_free(props); |
1162 | 1127 | |
1128 | + if (matrix) { | |
1129 | + g_return_val_if_fail (new_obj->ops->transform != NULL, list); | |
1130 | + if (!new_obj->ops->transform (new_obj, matrix)) | |
1131 | + dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"), | |
1132 | + new_obj->type->name); | |
1133 | + g_free (matrix); | |
1134 | + } | |
1163 | 1135 | return list; |
1164 | 1136 | } |
1165 | 1137 |
@@ -1171,7 +1143,7 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1171 | 1143 | static GList * |
1172 | 1144 | read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style, |
1173 | 1145 | GHashTable *style_ht, GHashTable *pattern_ht, |
1174 | - GList *list, const gchar *filename_svg) | |
1146 | + GList *list, const gchar *filename_svg, DiaContext *ctx) | |
1175 | 1147 | { |
1176 | 1148 | xmlChar *str; |
1177 | 1149 | real x, y, width, height; |
@@ -1191,21 +1163,6 @@ read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1191 | 1163 | |
1192 | 1164 | /* TODO: aspect ratio? */ |
1193 | 1165 | |
1194 | - if (matrix) { | |
1195 | - /* TODO: transform angle - when it is supported */ | |
1196 | - Point xy = {x, y}; | |
1197 | - Point wh = {width, height}; | |
1198 | - | |
1199 | - transform_point (&xy, matrix); | |
1200 | - transform_point (&wh, matrix); | |
1201 | - width = wh.x; | |
1202 | - height = wh.y; | |
1203 | - x = xy.x; | |
1204 | - y = xy.y; | |
1205 | - | |
1206 | - g_free (matrix); | |
1207 | - } | |
1208 | - | |
1209 | 1166 | str = xmlGetNsProp (node, (const xmlChar *)"href", (const xmlChar *)"http://www.w3.org/1999/xlink"); |
1210 | 1167 | if (str) { |
1211 | 1168 | if (strncmp ((char *)str, "data:image/", 11) == 0) { |
@@ -1265,6 +1222,13 @@ read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style, | ||
1265 | 1222 | xmlFree(str); |
1266 | 1223 | } |
1267 | 1224 | |
1225 | + if (matrix) { | |
1226 | + g_return_val_if_fail (new_obj->ops->transform, list); | |
1227 | + if (!new_obj->ops->transform (new_obj, matrix)) | |
1228 | + dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"), | |
1229 | + new_obj->type->name); | |
1230 | + g_free (matrix); | |
1231 | + } | |
1268 | 1232 | if (new_obj) |
1269 | 1233 | return g_list_append (list, new_obj); |
1270 | 1234 |
@@ -1629,15 +1593,15 @@ read_items (xmlNodePtr startnode, | ||
1629 | 1593 | else if (moreitems) |
1630 | 1594 | obj = g_list_last(moreitems)->data; |
1631 | 1595 | } else if (!xmlStrcmp(node->name, (const xmlChar *)"rect")) { |
1632 | - items = read_rect_svg(node, parent_gs, style_ht, pattern_ht, items); | |
1596 | + items = read_rect_svg(node, parent_gs, style_ht, pattern_ht, items, ctx); | |
1633 | 1597 | if (items) |
1634 | 1598 | obj = g_list_last(items)->data; |
1635 | 1599 | } else if (!xmlStrcmp(node->name, (const xmlChar *)"line")) { |
1636 | - items = read_line_svg(node, parent_gs, style_ht, pattern_ht, items); | |
1600 | + items = read_line_svg(node, parent_gs, style_ht, pattern_ht, items, ctx); | |
1637 | 1601 | if (items) |
1638 | 1602 | obj = g_list_last(items)->data; |
1639 | 1603 | } else if (!xmlStrcmp(node->name, (const xmlChar *)"ellipse") || !xmlStrcmp(node->name, (const xmlChar *)"circle")) { |
1640 | - items = read_ellipse_svg(node, parent_gs, style_ht, pattern_ht, items); | |
1604 | + items = read_ellipse_svg(node, parent_gs, style_ht, pattern_ht, items, ctx); | |
1641 | 1605 | if (items) |
1642 | 1606 | obj = g_list_last(items)->data; |
1643 | 1607 | } else if (!xmlStrcmp(node->name, (const xmlChar *)"polyline")) { |
@@ -1659,7 +1623,7 @@ read_items (xmlNodePtr startnode, | ||
1659 | 1623 | if (items && g_list_nth(items, first)) |
1660 | 1624 | obj = g_list_nth(items, first)->data; |
1661 | 1625 | } else if(!xmlStrcmp(node->name, (const xmlChar *)"image")) { |
1662 | - items = read_image_svg(node, parent_gs, style_ht, pattern_ht, items, filename_svg); | |
1626 | + items = read_image_svg(node, parent_gs, style_ht, pattern_ht, items, filename_svg, ctx); | |
1663 | 1627 | if (items) |
1664 | 1628 | obj = g_list_last(items)->data; |
1665 | 1629 | } else if(!xmlStrcmp(node->name, (const xmlChar *)"linearGradient") || |
@@ -1680,7 +1644,7 @@ read_items (xmlNodePtr startnode, | ||
1680 | 1644 | * be target for meta info, comment or something. */ |
1681 | 1645 | obj = otemp->ops->copy (otemp); |
1682 | 1646 | |
1683 | - use_position (obj, node); | |
1647 | + use_position (obj, node, ctx); | |
1684 | 1648 | /* this should only be styled from the containing group, |
1685 | 1649 | * if it has no style on it's own. Sorry Dia can't create |
1686 | 1650 | * objects w/o style so we have two options beside complete |
@@ -0,0 +1,98 @@ | ||
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | |
3 | +<svg version="1.1" | |
4 | + xmlns="http://www.w3.org/2000/svg" | |
5 | + xmlns:xlink="http://www.w3.org/1999/xlink" | |
6 | + x="0" y="0" width="28cm" height="28cm" viewBox="0, -5, 280, 275" | |
7 | + stroke-width="2" | |
8 | + text-anchor="middle" font-size="6" font-family="sans-serif"> | |
9 | + <text x="210" y="30" text-anchor="start">Standard - Box</text> | |
10 | + <text x="210" y="40" text-anchor="start">Standard - Line</text> | |
11 | + <text x="210" y="80" text-anchor="start">Standard - Ellipse</text> | |
12 | + <text x="210" y="90" text-anchor="start">Standard - Text</text> | |
13 | + <text x="210" y="130" text-anchor="start">Standard - Polygon</text> | |
14 | + <text x="210" y="140" text-anchor="start">Standard - Polyline</text> | |
15 | + <text x="210" y="180" text-anchor="start">Standard - Beziergon</text> | |
16 | + <text x="210" y="190" text-anchor="start">Standard - Bezierline</text> | |
17 | + <text x="210" y="230" text-anchor="start">Standard - Image</text> | |
18 | + | |
19 | + <text x="30" y="0" >Reference</text> | |
20 | + <!-- to be used below and directly --> | |
21 | + <rect id="box" x="18" y="10" width="24" height="40" fill="yellow" stroke="red"/> | |
22 | + <line id="line" x1="10" y1="30" x2="50" y2="30" stroke="red"/> | |
23 | + <ellipse id="ell" cx="30" cy="80" rx="20" ry="15" fill="lime" stroke="blue"/> | |
24 | + <text id="txt" x="30" y="80" text-anchor="middle" fill="blue">Rotate!</text> | |
25 | + <polygon id="pg1" points="30,110,50,145,10,145" fill="lightblue"/> | |
26 | + <polyline id="pl1" points="30,110,50,145,10,145" stroke="green" fill="none"/> | |
27 | + <path id="pc1" d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160z" fill="cyan"/> | |
28 | + <path id="po1" d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160" fill="none" stroke="gold"/> | |
29 | + <image id="img" x="10" y="210" width="40" height="40" | |
30 | + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAACbCAYAAABWOrhZAAAABHNCSVQICAgIfAhkiAAABDBJREFU eJzt3V2SEkEQReFsw0d34C5diLt0B763L4PBMMBt6Kyqm1XnezNCsYc+ZP9QAxGAsCU8xp7wGM9k bCNO+DZ6A+CPSCARCaTv6Y949gyFMxA7TBJIRAKJSCARCSQigUQkkIgEEpFAIhJIRAKJSCARCSQi gUQkkIgEUv56EtaDTIdJAolIIBEJpJ5nELerXzl7KYJJAolIIBEJJCKBRCSQiAQSkUAiEkhEAolI IN3eGm/90VZ4zvKtCiKpYWg8RFJP92CIpLYuwbBUYJzMF2TT55JIPGQF0+Q55RLYwxbGH7xMJF6y QkmNhUj82E0VIvFlM1WIxFvWieipUIjE3/DDD5Hc+Pvrp+sNxWGhEMmVSyCE8hmR1NM9FCL5cDs9 jKdJlsM/H5HU1PUtDSKJx1PDfJp0O+wQSW1dQlk+EjUtzKdJRIdQlo/kiAKhNLV0JBPt/KbTZOlI XjFRUC9bNpIJd3qzabJsJO+YMKxDloxk4p3dZJosF8nZQCYO7KHlIllA+jRZKpIjU+DH7z/ySV5t miwVCd6zTCSvTJEJpknqIWeZSMw4B/bFEpG8cy7SYZqk/xJVK0tE0lLCYcc+lukjOXNFc2SaJMqO JW3bp4/krAEnsU6TZY+YPJKs+yKD2MQybSSZr+7Bl8TDY5k2kiNcpsi+77HvsoNhsUwZSYvDTI9p 4hrLyI/DwodHYWzbod3z7C9lPOfblJNkFi6ThUgg5X/DONIkHG5SjIwk/YfrdV/k6Anqk//r6b93 ieNimsNNzxtnrS6dt207EkjWJx8dNk0kvWVeErvGcTHFOUnx2+/K8O1mkpzQ+AbbsMlxq/wkmXCK 2G1r6UnisM40cZrYTI4rW8QEk0QpMEVabR8fG+50mHlzmjhOjrvKRlJciTguSn4pksO5yLs6Hv7O Pkf/t5NJMqe1v++m8hSJqLn95SKZQeNQUhYaXf+hVCQVX4UzKBXJTIyD/3JiXSYS4yfVybrf5jlr IIY/193L85L3SXBX+gnrRYlJAqlZIBFEggOIpL6mUySCSKprHkgEkeAAIqmryxSJIJKqugUSQSQV dQ0kgkiq6R5IBJFUMiSQCCKpYlggEURSwdBAIhb4vZvCst4hPv1GKpF4Gj49rhGJF5vpcY1IPFjG cUEkY1nH8ejB3ZbT4bkuq/uYJDV1XfpJJHUMWxNMJL5sFoqzWh4St+UhEQkkIoFEJJCIBBKRQCIS SEQCiUggEQmkrFvjrZYYcOveQOZOyA6FQExwuIGU/WotsRwPr2m+nkR9Q/bBrzfFQNmHm4w9TjVm mp+TPJsUTJEaWkRyZs9TjaEuVzf3JgZTpI5WkbxTANWY6naf5HpyMEVqaRnJKyVQjbHWO+fozTUi MdZj56hQCMQc791AGv0dtUyRApgkkPhdYEhMEtjZg09TKodJAgAAHPwDyEsX4l8AdBUAAAAASUVO RK5CYII="/> | |
31 | + | |
32 | + <text x="80" y="0">Element rotate</text> | |
33 | + <!-- transform object directly - must not have copied id="...", otherwise Dia would | |
34 | + translate the wrong object (not the first of id, but the last) --> | |
35 | + <rect x="18" y="10" width="24" height="40" fill="yellow" stroke="red" | |
36 | + transform="translate(80,30) rotate(30) translate(-30,-30)"/> | |
37 | + <line x1="10" y1="30" x2="50" y2="30" stroke="red" | |
38 | + transform="translate(80,30) rotate(30) translate(-30,-30)"/> | |
39 | + <ellipse cx="30" cy="80" rx="20" ry="15" fill="lime" stroke="blue" | |
40 | + transform="translate(80,80) rotate(30) translate(-30,-80)"/> | |
41 | + <text x="30" y="80" text-anchor="middle" fill="blue" | |
42 | + transform="translate(80,80) rotate(30) translate(-30,-80)">Rotate!</text> | |
43 | + <polygon points="30,110,50,145,10,145" fill="lightblue" | |
44 | + transform="translate(80,130)rotate(30)translate(-30,-130)"/> | |
45 | + <polyline points="30,110,50,145,10,145" fill="none" stroke="green" | |
46 | + transform="translate(80,130) rotate(30) translate(-30,-130)"/> | |
47 | + <path d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160z" fill="cyan" | |
48 | + transform="translate(80,180) rotate(30) translate(-30,-180)"/> | |
49 | + <path d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160" fill="none" stroke="gold" | |
50 | + transform="translate(80,180) rotate(30) translate(-30,-180)"/> | |
51 | + <image x="10" y="210" width="40" height="40" | |
52 | + transform="translate(80,230) rotate(30) translate(-30,-230)" | |
53 | + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAACbCAYAAABWOrhZAAAABHNCSVQICAgIfAhkiAAABDBJREFU eJzt3V2SEkEQReFsw0d34C5diLt0B763L4PBMMBt6Kyqm1XnezNCsYc+ZP9QAxGAsCU8xp7wGM9k bCNO+DZ6A+CPSCARCaTv6Y949gyFMxA7TBJIRAKJSCARCSQigUQkkIgEEpFAIhJIRAKJSCARCSQi gUQkkIgEUv56EtaDTIdJAolIIBEJpJ5nELerXzl7KYJJAolIIBEJJCKBRCSQiAQSkUAiEkhEAolI IN3eGm/90VZ4zvKtCiKpYWg8RFJP92CIpLYuwbBUYJzMF2TT55JIPGQF0+Q55RLYwxbGH7xMJF6y QkmNhUj82E0VIvFlM1WIxFvWieipUIjE3/DDD5Hc+Pvrp+sNxWGhEMmVSyCE8hmR1NM9FCL5cDs9 jKdJlsM/H5HU1PUtDSKJx1PDfJp0O+wQSW1dQlk+EjUtzKdJRIdQlo/kiAKhNLV0JBPt/KbTZOlI XjFRUC9bNpIJd3qzabJsJO+YMKxDloxk4p3dZJosF8nZQCYO7KHlIllA+jRZKpIjU+DH7z/ySV5t miwVCd6zTCSvTJEJpknqIWeZSMw4B/bFEpG8cy7SYZqk/xJVK0tE0lLCYcc+lukjOXNFc2SaJMqO JW3bp4/krAEnsU6TZY+YPJKs+yKD2MQybSSZr+7Bl8TDY5k2kiNcpsi+77HvsoNhsUwZSYvDTI9p 4hrLyI/DwodHYWzbod3z7C9lPOfblJNkFi6ThUgg5X/DONIkHG5SjIwk/YfrdV/k6Anqk//r6b93 ieNimsNNzxtnrS6dt207EkjWJx8dNk0kvWVeErvGcTHFOUnx2+/K8O1mkpzQ+AbbsMlxq/wkmXCK 2G1r6UnisM40cZrYTI4rW8QEk0QpMEVabR8fG+50mHlzmjhOjrvKRlJciTguSn4pksO5yLs6Hv7O Pkf/t5NJMqe1v++m8hSJqLn95SKZQeNQUhYaXf+hVCQVX4UzKBXJTIyD/3JiXSYS4yfVybrf5jlr IIY/193L85L3SXBX+gnrRYlJAqlZIBFEggOIpL6mUySCSKprHkgEkeAAIqmryxSJIJKqugUSQSQV dQ0kgkiq6R5IBJFUMiSQCCKpYlggEURSwdBAIhb4vZvCst4hPv1GKpF4Gj49rhGJF5vpcY1IPFjG cUEkY1nH8ejB3ZbT4bkuq/uYJDV1XfpJJHUMWxNMJL5sFoqzWh4St+UhEQkkIoFEJJCIBBKRQCIS SEQCiUggEQmkrFvjrZYYcOveQOZOyA6FQExwuIGU/WotsRwPr2m+nkR9Q/bBrzfFQNmHm4w9TjVm mp+TPJsUTJEaWkRyZs9TjaEuVzf3JgZTpI5WkbxTANWY6naf5HpyMEVqaRnJKyVQjbHWO+fozTUi MdZj56hQCMQc791AGv0dtUyRApgkkPhdYEhMEtjZg09TKodJAgAAHPwDyEsX4l8AdBUAAAAASUVO RK5CYII="/> | |
54 | + | |
55 | + <text x="130" y="0">Group rotate</text> | |
56 | + <!-- just shift with use, transform by group --> | |
57 | + <g transform="translate(130,30)rotate(45)translate(-130,-30)"> | |
58 | + <use x="100" y="0" xlink:href="#box"/> | |
59 | + <use x="100" y="0" xlink:href="#line"/> | |
60 | + </g> | |
61 | + <g transform="translate(130,80)rotate(45)translate(-130,-80)"> | |
62 | + <use x="100" y="0" xlink:href="#ell"/> | |
63 | + <use x="100" y="0" xlink:href="#txt"/> | |
64 | + </g> | |
65 | + <g transform="translate(130,130)rotate(45)translate(-130,-130)"> | |
66 | + <use x="100" y="0" xlink:href="#pg1"/> | |
67 | + <use x="100" y="0" xlink:href="#pl1"/> | |
68 | + </g> | |
69 | + <g transform="translate(130,180)rotate(45)translate(-130,-180)"> | |
70 | + <use x="100" y="0" xlink:href="#pc1"/> | |
71 | + <use x="100" y="0" xlink:href="#po1"/> | |
72 | + </g> | |
73 | + <g transform="translate(130,230)rotate(45)translate(-130,-230)"> | |
74 | + <use x="100" y="0" xlink:href="#img"/> | |
75 | + </g> | |
76 | + | |
77 | + <text x="180" y="0">Use rotate</text> | |
78 | + <!-- transformed use, shifted by group --> | |
79 | + <g transform="translate(180,30)"> | |
80 | + <use x="-30" y="-30" xlink:href="#box" transform="rotate(60)"/> | |
81 | + <use x="-30" y="-30" xlink:href="#line" transform="rotate(60)"/> | |
82 | + </g> | |
83 | + <g transform="translate(180,80)"> | |
84 | + <use x="-30" y="-80" xlink:href="#ell" transform="rotate(60)"/> | |
85 | + <use x="-30" y="-80" xlink:href="#txt" transform="rotate(60)"/> | |
86 | + </g> | |
87 | + <g transform="translate(180,130)"> | |
88 | + <use x="-30" y="-130" xlink:href="#pg1" transform="rotate(60)"/> | |
89 | + <use x="-30" y="-130" xlink:href="#pl1" transform="rotate(60)"/> | |
90 | + </g> | |
91 | + <g transform="translate(180,180)"> | |
92 | + <use x="-30" y="-180" xlink:href="#pc1" transform="rotate(60)"/> | |
93 | + <use x="-30" y="-180" xlink:href="#po1" transform="rotate(60)"/> | |
94 | + </g> | |
95 | + <g transform="translate(180,230)"> | |
96 | + <use x="-30" y="-230" xlink:href="#img" transform="rotate(60)"/> | |
97 | + </g> | |
98 | +</svg> |