• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

作図ソフト dia の改良版


Commit MetaInfo

Revisionca1bbf458aec08657c8e687db0c5b33ea8a26343 (tree)
Time2014-10-10 03:01:12
AuthorHans Breuer <hans@breu...>
CommiterHans Breuer

Log Message

[transform] text rotation for "Standard - Text"

Rotations is done around the text handle position. This position is
only known to the object implementation. There is new render API
draw_rotated_text() taking the rotation center an angle, which is
much less invasive than making the underlying Text object aware of
angle and vertical alignment (and than not using it almost anywhere).
It is assumed that free text rotation will only be directly supported
by very few objects, so the burden should not be propagated to all
objects with text.
The initial version is should be working for almost all renderers
due to the default implementation of DiaRenderer::draw_rotated_text()
converting the text to path and rotating that.

Change Summary

Incremental Difference

--- a/app/object_ops.c
+++ b/app/object_ops.c
@@ -37,7 +37,7 @@ object_add_updates(DiaObject *obj, Diagram *dia)
3737
3838 /* Bounding box */
3939 if (data_object_get_highlight(dia->data,obj) != DIA_HIGHLIGHT_NONE) {
40- diagram_add_update_with_border(dia, &obj->bounding_box, 5);
40+ diagram_add_update_with_border(dia, dia_object_get_enclosing_box (obj), 5);
4141 } else {
4242 diagram_add_update(dia, dia_object_get_enclosing_box (obj));
4343 }
--- a/lib/diarenderer.c
+++ b/lib/diarenderer.c
@@ -19,14 +19,15 @@
1919 * along with this program; if not, write to the Free Software
2020 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2121 */
22-
22+#include <config.h>
2323
2424 #include "diarenderer.h"
2525 #include "object.h"
2626 #include "text.h"
2727 #include "textline.h"
2828 #include "diatransformrenderer.h"
29-
29+#include "standard-path.h" /* text_to_path */
30+#include "boundingbox.h" /* PolyBBextra */
3031 /*
3132 * redefinition of isnan, for portability, as explained in :
3233 * http://www.gnu.org/software/autoconf/manual/html_node/Function-Portability.html
@@ -105,6 +106,8 @@ static void draw_text (DiaRenderer *renderer,
105106 Text *text);
106107 static void draw_text_line (DiaRenderer *renderer,
107108 TextLine *text_line, Point *pos, Alignment alignment, Color *color);
109+static void draw_rotated_text (DiaRenderer *renderer, Text *text,
110+ Point *center, real angle);
108111
109112 static void draw_polyline (DiaRenderer *renderer,
110113 Point *points, int num_points,
@@ -335,6 +338,7 @@ dia_renderer_class_init (DiaRendererClass *klass)
335338 renderer_class->draw_polyline = draw_polyline;
336339 renderer_class->draw_text = draw_text;
337340 renderer_class->draw_text_line = draw_text_line;
341+ renderer_class->draw_rotated_text = draw_rotated_text;
338342
339343 /* highest level functions */
340344 renderer_class->draw_rounded_rect = draw_rounded_rect;
@@ -602,6 +606,108 @@ draw_text (DiaRenderer *renderer,
602606 }
603607
604608 /*!
609+ * \brief Default implementation to draw rotated text
610+ *
611+ * The default implementation converts the given _Text object to a
612+ * path and passes it to draw_beziergon() if the renderer support
613+ * rendering wih holes. If not a fallback implementation is used.
614+ *
615+ * A Renderer with a good own concept of rotated text should
616+ * overwrite it.
617+ *
618+ * \memberof _DiaRenderer
619+ */
620+static void
621+draw_rotated_text (DiaRenderer *renderer, Text *text,
622+ Point *center, real angle)
623+{
624+ if (angle == 0.0) {
625+ /* maybe the fallback should also consider center? */
626+ draw_text (renderer, text);
627+ } else {
628+ GArray *path = g_array_new (FALSE, FALSE, sizeof(BezPoint));
629+ if (!text_is_empty (text) && text_to_path (text, path)) {
630+ /* Scaling and transformation here */
631+ Rectangle bz_bb, tx_bb;
632+ PolyBBExtras extra = { 0, };
633+ real sx, sy;
634+ guint i;
635+ real dx = center ? (text->position.x - center->x) : 0;
636+ real dy = center ? (text->position.y - center->y) : 0;
637+ DiaMatrix m = { 1, 0, 0, 1, 0, 0 };
638+ DiaMatrix t = { 1, 0, 0, 1, 0, 0 };
639+
640+ polybezier_bbox (&g_array_index (path, BezPoint, 0), path->len, &extra, TRUE, &bz_bb);
641+ text_calc_boundingbox (text, &tx_bb);
642+ sx = (tx_bb.right - tx_bb.left) / (bz_bb.right - bz_bb.left);
643+ sy = (tx_bb.bottom - tx_bb.top) / (bz_bb.bottom - bz_bb.top);
644+
645+ /* move center to origin */
646+ if (ALIGN_LEFT == text->alignment)
647+ t.x0 = -bz_bb.left;
648+ else if (ALIGN_RIGHT == text->alignment)
649+ t.x0 = - bz_bb.right;
650+ else
651+ t.x0 = -(bz_bb.left + bz_bb.right) / 2.0;
652+ t.x0 -= dx / sx;
653+ t.y0 = - bz_bb.top - (text_get_ascent (text) - dy) / sy;
654+ dia_matrix_set_angle_and_scales (&m, G_PI * angle / 180.0, sx, sx);
655+ dia_matrix_multiply (&m, &t, &m);
656+ /* move back center from origin */
657+ if (ALIGN_LEFT == text->alignment)
658+ t.x0 = tx_bb.left;
659+ else if (ALIGN_RIGHT == text->alignment)
660+ t.x0 = tx_bb.right;
661+ else
662+ t.x0 = (tx_bb.left + tx_bb.right) / 2.0;
663+ t.x0 += dx;
664+ t.y0 = tx_bb.top + (text_get_ascent (text) - dy);
665+ dia_matrix_multiply (&m, &m, &t);
666+
667+ for (i = 0; i < path->len; ++i) {
668+ BezPoint *bp = &g_array_index (path, BezPoint, i);
669+ transform_bezpoint (bp, &m);
670+ }
671+
672+ if (DIA_RENDERER_GET_CLASS (renderer)->is_capable_to(renderer, RENDER_HOLES))
673+ DIA_RENDERER_GET_CLASS (renderer)->draw_beziergon(renderer,
674+ &g_array_index (path, BezPoint, 0),
675+ path->len,
676+ &text->color, NULL);
677+ else
678+ bezier_render_fill (renderer,
679+ &g_array_index (path, BezPoint, 0), path->len,
680+ &text->color);
681+ } else {
682+ Color magenta = { 1.0, 0.0, 1.0, 1.0 };
683+ Point pt = center ? *center : text->position;
684+ DiaMatrix m = { 1, 0, 0, 1, pt.x, pt.y };
685+ DiaMatrix t = { 1, 0, 0, 1, -pt.x, -pt.y };
686+ Rectangle tb;
687+ Point poly[4];
688+ int i;
689+
690+ text_calc_boundingbox (text, &tb);
691+ poly[0].x = tb.left; poly[0].y = tb.top;
692+ poly[1].x = tb.right; poly[1].y = tb.top;
693+ poly[2].x = tb.right; poly[2].y = tb.bottom;
694+ poly[3].x = tb.left; poly[3].y = tb.bottom;
695+
696+ dia_matrix_set_angle_and_scales (&m, G_PI * angle / 180.0, 1.0, 1.0);
697+ dia_matrix_multiply (&m, &t, &m);
698+
699+ for (i = 0; i < 4; ++i)
700+ transform_point (&poly[i], &m);
701+
702+ DIA_RENDERER_GET_CLASS (renderer)->set_linewidth (renderer, 0.0);
703+ DIA_RENDERER_GET_CLASS (renderer)->draw_polygon (renderer, poly, 4,
704+ NULL, &magenta);
705+ }
706+ g_array_free (path, TRUE);
707+ }
708+}
709+
710+/*!
605711 * \brief Default implementation of draw_text_line
606712 *
607713 * The default implementation of draw_text_line() just calls set_font() and
--- a/lib/diarenderer.h
+++ b/lib/diarenderer.h
@@ -252,6 +252,9 @@ struct _DiaRendererClass
252252 RenderCapability cap);
253253 /*! fill with a pattern, currently only gradient */
254254 void (*set_pattern) (DiaRenderer *renderer, DiaPattern *pat);
255+ /*! draw text rotated around center with given angle in degrees */
256+ void (*draw_rotated_text) (DiaRenderer *renderer, Text *text,
257+ Point *center, real angle);
255258 };
256259
257260 /*
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -744,6 +744,7 @@ EXPORTS
744744 text_get_ascent
745745 text_get_attributes
746746 text_get_descent
747+ text_get_height
747748 text_get_line
748749 text_get_line_strlen
749750 text_get_line_width
--- a/lib/text.c
+++ b/lib/text.c
@@ -392,6 +392,12 @@ text_set_height(Text *text, real height)
392392 calc_ascent_descent(text);
393393 }
394394
395+real
396+text_get_height(const Text *text)
397+{
398+ return text->height;
399+}
400+
395401 void
396402 text_set_font(Text *text, DiaFont *font)
397403 {
--- a/lib/text.h
+++ b/lib/text.h
@@ -77,6 +77,7 @@ gchar *text_get_line(const Text *text, int line);
7777 char *text_get_string_copy(const Text *text);
7878 void text_set_string(Text *text, const char *string);
7979 void text_set_height(Text *text, real height);
80+real text_get_height(const Text *text);
8081 void text_set_font(Text *text, DiaFont *font);
8182 void text_set_position(Text *text, Point *pos);
8283 void text_set_color(Text *text, Color *col);
--- a/objects/standard/textobj.c
+++ b/objects/standard/textobj.c
@@ -33,6 +33,7 @@
3333 #include "properties.h"
3434 #include "diamenu.h"
3535 #include "create.h"
36+#include "message.h" /* just dia_log_message */
3637
3738 #include "tool-icons.h"
3839
@@ -66,6 +67,8 @@ struct _Textobj {
6667 gboolean show_background;
6768 /*! margin used for background drawing and connection point placement */
6869 real margin;
70+ /*! rotation around text handle position */
71+ real text_angle;
6972 };
7073
7174 static struct _TextobjProperties {
@@ -86,6 +89,7 @@ static DiaObject *textobj_create(Point *startpoint,
8689 void *user_data,
8790 Handle **handle1,
8891 Handle **handle2);
92+static DiaObject *textobj_copy(Textobj *textobj);
8993 static void textobj_destroy(Textobj *textobj);
9094
9195 static void textobj_get_props(Textobj *textobj, GPtrArray *props);
@@ -95,6 +99,7 @@ static void textobj_save(Textobj *textobj, ObjectNode obj_node,
9599 DiaContext *ctx);
96100 static DiaObject *textobj_load(ObjectNode obj_node, int version, DiaContext *ctx);
97101 static DiaMenu *textobj_get_object_menu(Textobj *textobj, Point *clickedpoint);
102+static gboolean textobj_transform(Textobj *textobj, const DiaMatrix *m);
98103
99104 static void textobj_valign_point(Textobj *textobj, Point* p);
100105
@@ -115,6 +120,7 @@ PropEnumData prop_text_vert_align_data[] = {
115120 { NULL, 0 }
116121 };
117122 static PropNumData text_margin_range = { 0.0, 10.0, 0.1 };
123+static PropNumData text_angle_range = { -180.0, 180.0, 5.0 };
118124 static PropDescription textobj_props[] = {
119125 OBJECT_COMMON_PROPERTIES,
120126 PROP_STD_TEXT_ALIGNMENT,
@@ -123,6 +129,8 @@ static PropDescription textobj_props[] = {
123129 PROP_STD_TEXT_FONT,
124130 PROP_STD_TEXT_HEIGHT,
125131 PROP_STD_TEXT_COLOUR,
132+ { "text_angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
133+ N_("Text angle"), NULL, &text_angle_range },
126134 PROP_STD_SAVED_TEXT,
127135 PROP_STD_FILL_COLOUR_OPTIONAL,
128136 PROP_STD_SHOW_BACKGROUND_OPTIONAL,
@@ -138,6 +146,7 @@ static PropOffset textobj_offsets[] = {
138146 {PROP_STDNAME_TEXT_HEIGHT,PROP_STDTYPE_TEXT_HEIGHT,offsetof(Textobj,text),offsetof(Text,height)},
139147 {"text_colour",PROP_TYPE_COLOUR,offsetof(Textobj,text),offsetof(Text,color)},
140148 {"text_alignment",PROP_TYPE_ENUM,offsetof(Textobj,text),offsetof(Text,alignment)},
149+ {"text_angle",PROP_TYPE_REAL,offsetof(Textobj,text_angle)},
141150 {"text_vert_alignment",PROP_TYPE_ENUM,offsetof(Textobj,vert_align)},
142151 { "fill_colour", PROP_TYPE_COLOUR, offsetof(Textobj, fill_color) },
143152 { "show_background", PROP_TYPE_BOOL, offsetof(Textobj, show_background) },
@@ -168,7 +177,7 @@ static ObjectOps textobj_ops = {
168177 (DrawFunc) textobj_draw,
169178 (DistanceFunc) textobj_distance_from,
170179 (SelectFunc) textobj_select,
171- (CopyFunc) object_copy_using_properties,
180+ (CopyFunc) textobj_copy,
172181 (MoveFunc) textobj_move,
173182 (MoveHandleFunc) textobj_move_handle,
174183 (GetPropertiesFunc) object_create_props_dialog,
@@ -179,6 +188,7 @@ static ObjectOps textobj_ops = {
179188 (SetPropsFunc) textobj_set_props,
180189 (TextEditFunc) 0,
181190 (ApplyPropertiesListFunc) object_apply_props,
191+ (TransformFunc) textobj_transform,
182192 };
183193
184194 static void
@@ -194,9 +204,47 @@ textobj_set_props(Textobj *textobj, GPtrArray *props)
194204 textobj_update_data(textobj);
195205 }
196206
207+static void
208+_textobj_get_poly (const Textobj *textobj, Point poly[4])
209+{
210+ Point ul, lr;
211+ Point pt = textobj->text_handle.pos;
212+ Rectangle box;
213+ DiaMatrix m = { 1, 0, 0, 1, pt.x, pt.y };
214+ DiaMatrix t = { 1, 0, 0, 1, -pt.x, -pt.y };
215+ int i;
216+
217+ dia_matrix_set_angle_and_scales (&m, G_PI * textobj->text_angle / 180.0, 1.0, 1.0);
218+ dia_matrix_multiply (&m, &t, &m);
219+
220+ text_calc_boundingbox (textobj->text, &box);
221+ ul.x = box.left - textobj->margin;
222+ ul.y = box.top - textobj->margin;
223+ lr.x = box.right + textobj->margin;
224+ lr.y = box.bottom + textobj->margin;
225+
226+ poly[0].x = ul.x;
227+ poly[0].y = ul.y;
228+ poly[1].x = lr.x;
229+ poly[1].y = ul.y;
230+ poly[2].x = lr.x;
231+ poly[2].y = lr.y;
232+ poly[3].x = ul.x;
233+ poly[3].y = lr.y;
234+
235+ for (i = 0; i < 4; ++i)
236+ transform_point (&poly[i], &m);
237+}
238+
197239 static real
198240 textobj_distance_from(Textobj *textobj, Point *point)
199241 {
242+ if (textobj->text_angle != 0) {
243+ Point poly[4];
244+
245+ _textobj_get_poly (textobj, poly);
246+ return distance_polygon_point(poly, 4, 0.0, point);
247+ }
200248 if (textobj->show_background)
201249 return distance_rectangle_point(&textobj->object.bounding_box, point);
202250 return text_distance_from(textobj->text, point);
@@ -250,9 +298,29 @@ textobj_draw(Textobj *textobj, DiaRenderer *renderer)
250298 ul.y = box.top - textobj->margin;
251299 lr.x = box.right + textobj->margin;
252300 lr.y = box.bottom + textobj->margin;
253- DIA_RENDERER_GET_CLASS (renderer)->draw_rect (renderer, &ul, &lr, &textobj->fill_color, NULL);
301+ if (textobj->text_angle == 0) {
302+ DIA_RENDERER_GET_CLASS (renderer)->draw_rect (renderer, &ul, &lr, &textobj->fill_color, NULL);
303+ } else {
304+ Point poly[4];
305+
306+ _textobj_get_poly (textobj, poly);
307+ DIA_RENDERER_GET_CLASS (renderer)->draw_polygon (renderer, poly, 4, &textobj->fill_color, NULL);
308+ }
309+ }
310+ if (textobj->text_angle == 0) {
311+ text_draw(textobj->text, renderer);
312+ } else {
313+ DIA_RENDERER_GET_CLASS (renderer)->draw_rotated_text (renderer, textobj->text,
314+ &textobj->text_handle.pos, textobj->text_angle);
315+ /* XXX: interactive case not working correctly */
316+ if (renderer->is_interactive &&
317+ dia_object_is_selected(&textobj->object) &&
318+ textobj->text->focus.has_focus) {
319+ /* editing is not rotated */
320+ text_draw(textobj->text, renderer);
321+ }
322+
254323 }
255- text_draw(textobj->text, renderer);
256324 }
257325
258326 static void
@@ -282,6 +350,7 @@ textobj_update_data(Textobj *textobj)
282350 {
283351 Point to2;
284352 DiaObject *obj = &textobj->object;
353+ Rectangle tx_bb;
285354
286355 text_set_position(textobj->text, &obj->position);
287356 text_calc_boundingbox(textobj->text, &obj->bounding_box);
@@ -298,7 +367,9 @@ textobj_update_data(Textobj *textobj)
298367 else if (ALIGN_RIGHT == textobj->text->alignment)
299368 to2.x -= textobj->margin; /* left */
300369 text_set_position(textobj->text, &to2);
301- text_calc_boundingbox(textobj->text, &obj->bounding_box);
370+
371+ /* always use the unrotated box ... */
372+ text_calc_boundingbox(textobj->text, &tx_bb);
302373 /* grow the bounding box by 2x margin */
303374 obj->bounding_box.top -= textobj->margin;
304375 obj->bounding_box.left -= textobj->margin;
@@ -306,6 +377,28 @@ textobj_update_data(Textobj *textobj)
306377 obj->bounding_box.right += textobj->margin;
307378
308379 textobj->text_handle.pos = obj->position;
380+ if (textobj->text_angle == 0) {
381+ obj->bounding_box = tx_bb;
382+ g_return_if_fail (obj->enclosing_box != NULL);
383+ *obj->enclosing_box = tx_bb;
384+ } else {
385+ /* ... and grow it when necessary */
386+ Point poly[4];
387+ int i;
388+
389+ _textobj_get_poly (textobj, poly);
390+ /* we don't want the joined box for boundingbox because
391+ * selection would become too greedy than.
392+ */
393+ obj->bounding_box.left = obj->bounding_box.right = poly[0].x;
394+ obj->bounding_box.top = obj->bounding_box.bottom = poly[0].y;
395+ for (i = 1; i < 4; ++i)
396+ rectangle_add_point (&obj->bounding_box, &poly[i]);
397+ g_return_if_fail (obj->enclosing_box != NULL);
398+ *obj->enclosing_box = obj->bounding_box;
399+ /* join for editing/selection bbox */
400+ rectangle_union (obj->enclosing_box, &tx_bb);
401+ }
309402 }
310403
311404 static DiaObject *
@@ -322,7 +415,8 @@ textobj_create(Point *startpoint,
322415
323416 textobj = g_malloc0(sizeof(Textobj));
324417 obj = &textobj->object;
325-
418+ obj->enclosing_box = g_new0 (Rectangle, 1);
419+
326420 obj->type = &textobj_type;
327421
328422 obj->ops = &textobj_ops;
@@ -358,10 +452,21 @@ textobj_create(Point *startpoint,
358452 return &textobj->object;
359453 }
360454
455+static DiaObject *
456+textobj_copy(Textobj *textobj)
457+{
458+ Textobj *copied = (Textobj *)object_copy_using_properties(&textobj->object);
459+ copied->object.enclosing_box = g_new (Rectangle, 1);
460+ *copied->object.enclosing_box = *textobj->object.enclosing_box;
461+ return &copied->object;
462+}
463+
361464 static void
362465 textobj_destroy(Textobj *textobj)
363466 {
364467 text_destroy(textobj->text);
468+ g_free (textobj->object.enclosing_box);
469+ textobj->object.enclosing_box = NULL;
365470 object_destroy(&textobj->object);
366471 }
367472
@@ -381,6 +486,8 @@ textobj_save(Textobj *textobj, ObjectNode obj_node, DiaContext *ctx)
381486 }
382487 if (textobj->margin > 0.0)
383488 data_add_real(new_attribute(obj_node, "margin"), textobj->margin, ctx);
489+ if (textobj->text_angle != 0.0)
490+ data_add_real(new_attribute(obj_node, "text_angle"), textobj->text_angle, ctx);
384491 }
385492
386493 static DiaObject *
@@ -393,6 +500,7 @@ textobj_load(ObjectNode obj_node, int version, DiaContext *ctx)
393500
394501 textobj = g_malloc0(sizeof(Textobj));
395502 obj = &textobj->object;
503+ obj->enclosing_box = g_new0(Rectangle,1);
396504
397505 obj->type = &textobj_type;
398506 obj->ops = &textobj_ops;
@@ -415,6 +523,9 @@ textobj_load(ObjectNode obj_node, int version, DiaContext *ctx)
415523 else if (version == 0) {
416524 textobj->vert_align = VALIGN_FIRST_LINE;
417525 }
526+ attr = object_find_attribute(obj_node, "text_angle");
527+ if (attr != NULL)
528+ textobj->text_angle = data_real(attribute_first_data(attr), ctx);
418529
419530 /* default visibility must be off to keep compatibility */
420531 textobj->fill_color = attributes_get_background();
@@ -489,3 +600,29 @@ textobj_get_object_menu(Textobj *textobj, Point *clickedpoint)
489600
490601 return &textobj_menu;
491602 }
603+static gboolean
604+textobj_transform(Textobj *textobj, const DiaMatrix *m)
605+{
606+ real a, sx, sy;
607+
608+ g_return_val_if_fail(m != NULL, FALSE);
609+
610+ if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
611+ dia_log_message ("textobj_transform() can't convert given matrix");
612+ return FALSE;
613+ } else {
614+ /* XXX: what to do if width!=height */
615+ real height = text_get_height (textobj->text) * MIN(sx,sy);
616+ real angle = a*180/G_PI;
617+ Point p = textobj->object.position;
618+
619+ /* rotation is invariant to the handle position */
620+ transform_point (&p, m);
621+ text_set_height (textobj->text, height);
622+ textobj->text_angle = angle;
623+ textobj->object.position = p;
624+ }
625+
626+ textobj_update_data(textobj);
627+ return TRUE;
628+}
Binary files /dev/null and b/samples/text-rotate.dia differ