[dia] [transform] Affine transformation for every renderer



commit 507f0cbc725115d14933372272bd67569bee367f
Author: Hans Breuer <hans breuer org>
Date:   Sun Aug 25 10:50:25 2013 +0200

    [transform] Affine transformation for every renderer
    
    DiaTransformRenderer is a renderer facade which adds basic transformation
    support to every renderer. It implements the basic renderer interface to
    translate the object given coordinates through the currently active
    transformation matrix (given by DiaRenderer::draw_object()) and calls
    the output renderer (worker) given at construction time.
    Text and image transformation are still incomplete, but the rest should
    be working fine.
    Some transformation have to be done on the basic shape level, e.g. a
    rectangle is turned into polygon; transformed ellipse and arc are rendered
    as bezier path with some helper functions extracted from DiaPathRenderer.

 lib/Makefile.am            |    2 +
 lib/diapathrenderer.c      |   63 ++++--
 lib/diapathrenderer.h      |    8 +
 lib/diarenderer.c          |    8 +
 lib/diatransformrenderer.c |  530 ++++++++++++++++++++++++++++++++++++++++++++
 lib/diatransformrenderer.h |   25 ++
 lib/geometry.c             |   14 ++
 lib/geometry.h             |    1 +
 lib/makefile.msc           |    3 +-
 9 files changed, 633 insertions(+), 21 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 827610f..061c030 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -165,6 +165,8 @@ libdia_la_SOURCES =  \
                diagdkrenderer.c \
                diapathrenderer.h \
                diapathrenderer.c \
+               diatransformrenderer.h \
+               diatransformrenderer.c \
                diasvgrenderer.h \
                diasvgrenderer.c \
                dia_svg.h \
diff --git a/lib/diapathrenderer.c b/lib/diapathrenderer.c
index 5d8401c..7e2e93e 100644
--- a/lib/diapathrenderer.c
+++ b/lib/diapathrenderer.c
@@ -336,21 +336,17 @@ fill_rect (DiaRenderer *self,
 {
   _rect (self, ul_corner, lr_corner, NULL, color);
 }
+
 /*!
- * \brief Convert an arc to some bezier curve-to
- * \bug For arcs going through angle 0 the result is wrong, 
- * kind of the opposite of the desired.
- * \protected \memberof _DiaPathRenderer
+ * \brief Create a path from an arc
  */
-static void
-_arc (DiaRenderer *self, 
-      Point *center,
-      real width, real height,
-      real angle1, real angle2,
-      const Color *stroke, const Color *fill)
+void
+path_build_arc (GArray *path,
+               Point *center,
+               real width, real height,
+               real angle1, real angle2,
+               gboolean closed)
 {
-  DiaPathRenderer *renderer = DIA_PATH_RENDERER (self);
-  GArray *path = _get_current_path (renderer, stroke, fill);
   Point start;
   real radius = sqrt(width * height) / 2.0;
   real ar1;
@@ -381,7 +377,7 @@ _arc (DiaRenderer *self,
     ar2 += 2 * M_PI;
   }
 
-  if (stroke) {
+  if (!closed) {
     _path_append (path, &start);
     for (i = 0; i < segs; ++i, ar1 += ars)
       _path_arc_segment (path, center, radius, ar1, ar1 + ars);
@@ -392,6 +388,25 @@ _arc (DiaRenderer *self,
     _path_lineto (path, &start);
   }
 }
+
+/*!
+ * \brief Convert an arc to some bezier curve-to
+ * \bug For arcs going through angle 0 the result is wrong, 
+ * kind of the opposite of the desired.
+ * \protected \memberof _DiaPathRenderer
+ */
+static void
+_arc (DiaRenderer *self, 
+      Point *center,
+      real width, real height,
+      real angle1, real angle2,
+      const Color *stroke, const Color *fill)
+{
+  DiaPathRenderer *renderer = DIA_PATH_RENDERER (self);
+  GArray *path = _get_current_path (renderer, stroke, fill);
+
+  path_build_arc (path, center, width, height, angle1, angle2, stroke == NULL);
+}
 static void
 draw_arc (DiaRenderer *self, 
          Point *center,
@@ -410,14 +425,11 @@ fill_arc (DiaRenderer *self,
 {
   _arc (self, center, width, height, angle1, angle2, NULL, color);
 }
-static void
-_ellipse (DiaRenderer *self,
-         Point *center,
-         real width, real height,
-         const Color *stroke, const Color *fill)
+void
+path_build_ellipse (GArray *path,
+                   Point *center,
+                   real width, real height)
 {
-  DiaPathRenderer *renderer = DIA_PATH_RENDERER (self);
-  GArray *path = _get_current_path (renderer, stroke, fill);
   real w2 = width/2;
   real h2 = height/2;
   /* FIXME: just a rough estimation to get started */
@@ -469,6 +481,17 @@ _ellipse (DiaRenderer *self,
 
     g_array_append_val (path, bp);
   }
+}    
+static void
+_ellipse (DiaRenderer *self,
+         Point *center,
+         real width, real height,
+         const Color *stroke, const Color *fill)
+{
+  DiaPathRenderer *renderer = DIA_PATH_RENDERER (self);
+  GArray *path = _get_current_path (renderer, stroke, fill);
+
+  path_build_ellipse (path, center, width, height);
 }
 static void
 draw_ellipse (DiaRenderer *self, 
diff --git a/lib/diapathrenderer.h b/lib/diapathrenderer.h
index 16a9c89..a679f03 100644
--- a/lib/diapathrenderer.h
+++ b/lib/diapathrenderer.h
@@ -39,4 +39,12 @@ typedef struct _DiaPathRendererClass DiaPathRendererClass;
 
 GType dia_path_renderer_get_type (void) G_GNUC_CONST;
 
+void path_build_arc (GArray *path, Point *center,
+                    real width, real height,
+                    real angle1, real angle2,
+                    gboolean closed);
+void path_build_ellipse (GArray *path,
+                        Point *center,
+                        real width, real height);
+
 #endif
diff --git a/lib/diarenderer.c b/lib/diarenderer.c
index 642bb6a..20767bc 100644
--- a/lib/diarenderer.c
+++ b/lib/diarenderer.c
@@ -25,6 +25,7 @@
 #include "object.h"
 #include "text.h"
 #include "textline.h"
+#include "diatransformrenderer.h"
 
 /*
  * redefinition of isnan, for portability, as explained in :
@@ -244,6 +245,12 @@ draw_object (DiaRenderer *renderer,
             DiaMatrix   *matrix) 
 {
   if (matrix) {
+#if 1
+    DiaRenderer *tr = dia_transform_renderer_new (renderer);
+    DIA_RENDERER_GET_CLASS(tr)->draw_object (tr, object, matrix);
+    g_object_unref (tr);
+    return;
+#else
     /* visual complaints - not completely correct */
     Point pt[4];
     Rectangle *bb = &object->bounding_box;
@@ -263,6 +270,7 @@ draw_object (DiaRenderer *renderer,
     DIA_RENDERER_GET_CLASS(renderer)->draw_polygon(renderer, pt, 4, &red);
     DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &pt[0], &pt[2], &red);
     DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &pt[1], &pt[3], &red);
+#endif
   }
   object->ops->draw(object, renderer);
 }
diff --git a/lib/diatransformrenderer.c b/lib/diatransformrenderer.c
new file mode 100644
index 0000000..2895950
--- /dev/null
+++ b/lib/diatransformrenderer.c
@@ -0,0 +1,530 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diatransformrenderer.c -- facade renderer to support affine transformation
+ *
+ * Copyright (C) 2013 Hans Breuer <hans breuer org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include "config.h"
+
+#include "diatransformrenderer.h"
+#include "text.h"
+#include "object.h"
+#include "diapathrenderer.h"
+
+typedef struct _DiaTransformRenderer DiaTransformRenderer;
+typedef struct _DiaTransformRendererClass DiaTransformRendererClass;
+
+/*! GObject boiler plate, create runtime information */
+#define DIA_TYPE_TRANSFORM_RENDERER           (dia_transform_renderer_get_type ())
+/*! GObject boiler plate, a safe type cast */
+#define DIA_TRANSFORM_RENDERER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
DIA_TYPE_TRANSFORM_RENDERER, DiaTransformRenderer))
+/*! GObject boiler plate, in C++ this would be the vtable */
+#define DIA_TRANSFORM_RENDERER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), 
DIA_TYPE_TRANSFORM_RENDERER, DiaTransformRendererClass))
+/*! GObject boiler plate, type check */
+#define DIA_IS_TRANSFORM_RENDERER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
DIA_TYPE_TRANSFORM_RENDERER))
+/*! GObject boiler plate, get from object to class (vtable) */
+#define DIA_TRANSFORM_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), 
DIA_TYPE_TRANSFORM_RENDERER, DiaTransformRendererClass))
+
+GType dia_transform_renderer_get_type (void) G_GNUC_CONST;
+
+/*!
+ * \brief Renderer which does affine transform rendering
+ *
+ * The transform renderer does not produce any external output by itself. It
+ * directly delegates it's results to the worker renderer. 
+ *
+ * \extends _DiaRenderer
+ */
+struct _DiaTransformRenderer
+{
+  DiaRenderer parent_instance; /*!< inheritance in object oriented C */
+
+  DiaRenderer *worker; /*!< the renderer with real output */
+
+  GQueue *matrices; 
+};
+
+struct _DiaTransformRendererClass
+{
+  DiaRendererClass parent_class; /*!< the base class */
+};
+
+
+G_DEFINE_TYPE (DiaTransformRenderer, dia_transform_renderer, DIA_TYPE_RENDERER)
+
+/*!
+ * \brief Constructor
+ * Initialize everything which needs something else than 0.
+ * \memberof _DiaTransformRenderer
+ */
+static void
+dia_transform_renderer_init (DiaTransformRenderer *self)
+{
+  self->matrices = g_queue_new ();
+}
+/*!
+ * \brief Destructor
+ * If there are still matrices left, deallocate them
+ * \memberof _DiaTransformRenderer
+ */
+static void
+dia_path_renderer_finalize (GObject *object)
+{
+  DiaTransformRenderer *self = DIA_TRANSFORM_RENDERER (object);
+
+  g_queue_free (self->matrices);
+  /* drop our reference */
+  g_object_unref (self->worker);
+}
+
+/*!
+ * \brief Starting a new rendering run
+ * Should not be called because this renderer is intermediate.
+ * \memberof _DiaTransformRenderer
+ */
+static void
+begin_render (DiaRenderer *self, const Rectangle *update)
+{
+}
+/*!
+ * \brief End of current rendering run
+ * Should not be called because this renderer is intermediate.
+ * \memberof _DiaTransformRenderer
+ */
+static void
+end_render(DiaRenderer *self)
+{
+}
+/*!
+ * \brief Advertize the renderers capabilities
+ * The DiaTransformRenderer can extend every DiaRenderer with affine
+ * transormations. Other capabilities are just pass through of the
+ * wrapped renderer.
+ * \memberof _DiaTransformRenderer
+ */
+static gboolean
+is_capable_to (DiaRenderer *self, RenderCapability cap)
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+
+  if (RENDER_AFFINE == cap)
+    return TRUE; /* reason for existance */
+  g_return_val_if_fail (renderer->worker != NULL, FALSE);
+  return DIA_RENDERER_GET_CLASS (renderer->worker)->is_capable_to (renderer->worker, cap);
+}
+static void
+set_linewidth(DiaRenderer *self, real linewidth)
+{  /* 0 == hairline **/
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  real lw = linewidth;
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+  if (m)
+    transform_length (&lw, m);
+  DIA_RENDERER_GET_CLASS (renderer->worker)->set_linewidth (renderer->worker, lw);
+}
+static void
+set_linecaps(DiaRenderer *self, LineCaps mode)
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  g_return_if_fail (renderer->worker != NULL);
+  DIA_RENDERER_GET_CLASS (renderer->worker)->set_linecaps (renderer->worker, mode);
+}
+static void
+set_linejoin(DiaRenderer *self, LineJoin mode)
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  g_return_if_fail (renderer->worker != NULL);
+  DIA_RENDERER_GET_CLASS (renderer->worker)->set_linejoin (renderer->worker, mode);
+}
+static void
+set_linestyle(DiaRenderer *self, LineStyle mode)
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  g_return_if_fail (renderer->worker != NULL);
+  DIA_RENDERER_GET_CLASS (renderer->worker)->set_linestyle (renderer->worker, mode);
+}
+static void
+set_dashlength(DiaRenderer *self, real length)
+{  /* dot = 20% of len */
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  real dl= length;
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+  if (m)
+    transform_length (&dl, m);
+  DIA_RENDERER_GET_CLASS (renderer->worker)->set_dashlength (renderer->worker, dl);
+}
+static void
+set_fillstyle(DiaRenderer *self, FillStyle mode)
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  g_return_if_fail (renderer->worker != NULL);
+  DIA_RENDERER_GET_CLASS (renderer->worker)->set_fillstyle (renderer->worker, mode);
+}
+static void
+draw_line(DiaRenderer *self, 
+         Point *start, Point *end, 
+         Color *line_colour)
+{
+  Point p1 = *start;
+  Point p2 = *end;
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+  if (m) {
+    transform_point(&p1, m);
+    transform_point(&p2, m);
+  }
+  DIA_RENDERER_GET_CLASS (renderer->worker)->draw_line (renderer->worker, &p1, &p2, line_colour);
+}
+static void
+_polyline(DiaRenderer *self, 
+         Point *points, int num_points, 
+         Color *stroke, Color *fill,
+         gboolean closed)
+{
+  Point *a_pts = g_newa (Point, num_points);
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+  memcpy (a_pts, points, sizeof(Point)*num_points);
+  if (m) {
+    int i;
+    for (i = 0; i < num_points; ++i)
+      transform_point (&a_pts[i], m);
+  }
+  if (fill)
+    DIA_RENDERER_GET_CLASS (renderer->worker)->fill_polygon (renderer->worker, a_pts, num_points, fill);
+  else if (closed)
+    DIA_RENDERER_GET_CLASS (renderer->worker)->draw_polygon (renderer->worker, a_pts, num_points, stroke);
+  else
+    DIA_RENDERER_GET_CLASS (renderer->worker)->draw_polyline (renderer->worker, a_pts, num_points, stroke);
+}
+static void
+draw_polyline(DiaRenderer *self, 
+             Point *points, int num_points, 
+             Color *line_colour)
+{
+  _polyline (self, points, num_points, line_colour, NULL, FALSE);
+}
+static void
+draw_polygon(DiaRenderer *self, 
+             Point *points, int num_points, 
+             Color *line_colour)
+{
+  _polyline (self, points, num_points, line_colour, NULL, TRUE);
+}
+static void
+fill_polygon(DiaRenderer *self, 
+            Point *points, int num_points, 
+            Color *color)
+{
+  _polyline (self, points, num_points, NULL, color, TRUE);
+}
+static void
+_rect (DiaRenderer *self, 
+       Point *ul_corner, Point *lr_corner,
+       Color *stroke, Color *fill)
+{
+  Point corner[4];
+  /* translate to polygon */
+  corner[0] = *ul_corner;
+  corner[1].x = lr_corner->x;
+  corner[1].y = ul_corner->y;
+  corner[2] = *lr_corner;
+  corner[3].x = ul_corner->x;
+  corner[3].y = lr_corner->y;
+  /* delegate transformation and drawing */
+  _polyline (self, corner, 4, stroke, fill, TRUE);
+}
+static void
+draw_rect (DiaRenderer *self, 
+          Point *ul_corner, Point *lr_corner,
+          Color *color)
+{
+  _rect (self, ul_corner, lr_corner, color, NULL);
+}
+static void
+fill_rect (DiaRenderer *self, 
+          Point *ul_corner, Point *lr_corner,
+          Color *color)
+{
+  _rect (self, ul_corner, lr_corner, NULL, color);
+}
+/* ToDo: arc and ellipse to be emulated by bezier - in base class? */
+static void
+_bezier (DiaRenderer *self, 
+        BezPoint *points, int num_points,
+        Color *stroke, Color *fill)
+{
+  BezPoint *a_pts = g_newa (BezPoint, num_points);
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+  memcpy (a_pts, points, sizeof(BezPoint)*num_points);
+  if (m) {
+    int i;
+    for (i = 0; i < num_points; ++i)
+      transform_bezpoint (&a_pts[i], m);
+  }
+  if (fill)
+    DIA_RENDERER_GET_CLASS (renderer->worker)->fill_bezier (renderer->worker, a_pts, num_points, fill);
+  else
+    DIA_RENDERER_GET_CLASS (renderer->worker)->draw_bezier (renderer->worker, a_pts, num_points, stroke);
+}
+static void
+_arc (DiaRenderer *self, 
+      Point *center,
+      real width, real height,
+      real angle1, real angle2,
+      Color *stroke, Color *fill)
+{
+  GArray *path = g_array_new (FALSE, FALSE, sizeof(BezPoint));
+  path_build_arc (path, center, width, height, angle1, angle2, stroke == NULL);
+  _bezier (self, &g_array_index (path, BezPoint, 0), path->len, stroke, fill);
+  g_array_free (path, TRUE);
+}
+static void
+draw_arc (DiaRenderer *self, 
+         Point *center,
+         real width, real height,
+         real angle1, real angle2,
+         Color *color)
+{
+  _arc (self, center, width, height, angle1, angle2, color, NULL);
+}
+static void
+fill_arc (DiaRenderer *self, 
+         Point *center,
+         real width, real height,
+         real angle1, real angle2,
+         Color *color)
+{
+  _arc (self, center, width, height, angle1, angle2, NULL, color);
+}
+static void
+_ellipse (DiaRenderer *self,
+         Point *center,
+         real width, real height,
+         const Color *stroke, const Color *fill)
+{
+  GArray *path = g_array_new (FALSE, FALSE, sizeof(BezPoint));
+  path_build_ellipse (path, center, width, height);
+  _bezier (self, &g_array_index (path, BezPoint, 0), path->len, stroke, fill);
+  g_array_free (path, TRUE);
+}
+static void
+draw_ellipse (DiaRenderer *self, 
+             Point *center,
+             real width, real height,
+             Color *color)
+{
+  _ellipse (self, center, width, height, color, NULL);
+}
+static void
+fill_ellipse (DiaRenderer *self, 
+             Point *center,
+             real width, real height,
+             Color *color)
+{
+  _ellipse (self, center, width, height, NULL, color);
+}
+static void
+draw_bezier (DiaRenderer *self, 
+            BezPoint *points,
+            int numpoints,
+            Color *color)
+{
+  _bezier(self, points, numpoints, color, NULL);
+}
+static void
+fill_bezier(DiaRenderer *self, 
+           BezPoint *points, /* Last point must be same as first point */
+           int numpoints,
+           Color *color)
+{
+  _bezier(self, points, numpoints, NULL, color);
+}
+/*!
+ * \brief Tansform the text object while drawing
+ * \memberof _DiaTransformRenderer
+ */
+static void 
+draw_text (DiaRenderer *self,
+          Text        *text) 
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+
+  /* ToDo: see DiaPathRenderer? */
+  Point pos;
+  int i;
+
+  pos = text->position;
+
+  for (i=0;i<text->numlines;i++) {
+    TextLine *text_line = text->lines[i];
+    Point pt;
+
+    pt = pos;
+    if (m) {
+      transform_point (&pt, m);
+      /* ToDo: fon-size and angle */
+    }
+    DIA_RENDERER_GET_CLASS(renderer->worker)->draw_text_line(renderer->worker, text_line,
+                                                            &pt, text->alignment,
+                                                            &text->color);
+    pos.y += text->height;
+  }
+
+}
+/*!
+ * \brief Convert the string back to a _Text object and render that
+ * \memberof _DiaTransformRenderer
+ */
+static void
+draw_string(DiaRenderer *self,
+           const char *text,
+           Point *pos, Alignment alignment,
+           Color *color)
+{
+  if (text && strlen(text)) {
+    Text *text_obj;
+    /* it could have been so easy without the context switch */
+    text_obj = new_text (text,
+                        self->font, self->font_height,
+                        pos, color, alignment);
+    draw_text (self, text_obj);
+    text_destroy (text_obj);
+  }
+}
+/*!
+ * \brief Draw a potentially transformed image
+ * \memberof _DiaTransformRenderer
+ */
+static void
+draw_image(DiaRenderer *self,
+          Point *point,
+          real width, real height,
+          DiaImage *image)
+{
+  Point p1 = *point;
+  Point p2 = p1;
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+
+  /* ToDo: some support on the library level? */
+  p2.x += width;
+  p2.y += height;
+  if (m) {
+    transform_point (&p1, m);
+    transform_point (&p2, m);
+  }
+  /* FIXME: for now only the position is transformed */
+  DIA_RENDERER_GET_CLASS (renderer->worker)->draw_image (renderer->worker, &p1, p2.x - p1.x, p2.y - p1.y, 
image);
+}
+
+/*!
+ * \brief Main renderer function used by lib/app, matrix transport
+ * \memberof _DiaTransformRenderer
+ */
+static void
+draw_object (DiaRenderer *self,
+            DiaObject   *object,
+            DiaMatrix   *matrix) 
+{
+  DiaTransformRenderer *renderer = DIA_TRANSFORM_RENDERER (self);
+  DiaMatrix *m = g_queue_peek_tail (renderer->matrices);
+  g_return_if_fail (renderer->worker != NULL);
+  
+  if (matrix) {
+    DiaMatrix *m2 = g_new (DiaMatrix, 1);
+    if (m)
+      dia_matrix_multiply (m2, matrix, m);
+    else
+      *m2 = *matrix;
+    g_queue_push_tail (renderer->matrices, m2);
+  }
+  /* This will call us again */
+  object->ops->draw(object, DIA_RENDERER (renderer));
+  if (matrix)
+    g_queue_pop_tail (renderer->matrices);
+}
+
+/*!
+ * \brief _DiaPathRenderer class initialization
+ * Overwrite methods of the base classes here.
+ * \memberof _DiaTransformRenderer
+ */
+static void
+dia_transform_renderer_class_init (DiaTransformRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
+
+  object_class->finalize = dia_path_renderer_finalize;
+
+  renderer_class->draw_object = draw_object;
+  /* renderer members */
+  renderer_class->begin_render = begin_render;
+  renderer_class->end_render   = end_render;
+
+  renderer_class->set_linewidth  = set_linewidth;
+  renderer_class->set_linecaps   = set_linecaps;
+  renderer_class->set_linejoin   = set_linejoin;
+  renderer_class->set_linestyle  = set_linestyle;
+  renderer_class->set_dashlength = set_dashlength;
+  renderer_class->set_fillstyle  = set_fillstyle;
+
+  renderer_class->draw_line    = draw_line;
+  renderer_class->fill_polygon = fill_polygon;
+  renderer_class->draw_rect    = draw_rect;
+  renderer_class->fill_rect    = fill_rect;
+  renderer_class->draw_arc     = draw_arc;
+  renderer_class->fill_arc     = fill_arc;
+  renderer_class->draw_ellipse = draw_ellipse;
+  renderer_class->fill_ellipse = fill_ellipse;
+
+  renderer_class->draw_string  = draw_string;
+  renderer_class->draw_image   = draw_image;
+
+  /* medium level functions */
+  renderer_class->draw_rect = draw_rect;
+  renderer_class->draw_polyline  = draw_polyline;
+  renderer_class->draw_polygon   = draw_polygon;
+
+  renderer_class->draw_bezier   = draw_bezier;
+  renderer_class->fill_bezier   = fill_bezier;
+  renderer_class->draw_text     = draw_text;
+  /* other */
+  renderer_class->is_capable_to = is_capable_to;
+}
+
+/*!
+ * \brief Factory function to construct a transform renderer
+ */
+DiaRenderer *
+dia_transform_renderer_new (DiaRenderer *to_be_wrapped)
+{
+  DiaTransformRenderer *tr = g_object_new (DIA_TYPE_TRANSFORM_RENDERER, NULL);
+  tr->worker = g_object_ref (to_be_wrapped);
+
+  return DIA_RENDERER (tr);
+}
diff --git a/lib/diatransformrenderer.h b/lib/diatransformrenderer.h
new file mode 100644
index 0000000..3f9ae86
--- /dev/null
+++ b/lib/diatransformrenderer.h
@@ -0,0 +1,25 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diatransformrenderer.c -- facade renderer to support affine transformation
+ *
+ * Copyright (C) 2013 Hans Breuer <hans breuer org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include "diarenderer.h"
+
+/* intentionally _not_ declaring the class here */
+DiaRenderer *dia_transform_renderer_new (DiaRenderer *to_be_wrapped);
diff --git a/lib/geometry.c b/lib/geometry.c
index dede3e9..d42b785 100644
--- a/lib/geometry.c
+++ b/lib/geometry.c
@@ -430,6 +430,16 @@ transform_point (Point *pt, const DiaMatrix *m)
   pt->y = x * m->yx + y * m->yy + m->y0;
 }
 void
+transform_length (real *len, const DiaMatrix *m)
+{
+  Point pt = { *len, 0 };
+  transform_point (&pt, m);
+  /* not interested in the offset */
+  pt.x -= m->x0;
+  pt.y -= m->y0;
+  *len = point_len (&pt);
+}
+void
 transform_bezpoint (BezPoint *bpt, const DiaMatrix *m)
 {
   transform_point (&bpt->p1, m);
@@ -789,8 +799,12 @@ dia_matrix_set_angle_and_scales (DiaMatrix *m,
                                 real       sx,
                                 real       sy)
 {
+  real dx = m->x0;
+  real dy = m->y0;
   cairo_matrix_init_rotate ((cairo_matrix_t *)m, a);
   cairo_matrix_scale ((cairo_matrix_t *)m, sx, sy);
+  m->x0 = dx;
+  m->y0 = dy;
 }
 
 gboolean
diff --git a/lib/geometry.h b/lib/geometry.h
index 69a120b..920f1ac 100644
--- a/lib/geometry.h
+++ b/lib/geometry.h
@@ -366,6 +366,7 @@ real distance_bez_shape_point(const BezPoint *b, guint npoints,
 real distance_ellipse_point(const Point *centre, real width, real height,
                            real line_width, const Point *point);
 
+void transform_length (real *length, const DiaMatrix *m);
 void transform_point (Point *pt, const DiaMatrix *m);
 void transform_bezpoint (BezPoint *bpt, const DiaMatrix *m);
 
diff --git a/lib/makefile.msc b/lib/makefile.msc
index 81cb5e1..bd2451a 100644
--- a/lib/makefile.msc
+++ b/lib/makefile.msc
@@ -74,6 +74,7 @@ OBJECTS = \
        diainteractiverenderer.obj \
        diarenderer.obj \
        diapathrenderer.obj \
+       diatransformrenderer.obj \
        diasvgrenderer.obj \
        dynamic_obj.obj \
        element.obj \
@@ -188,4 +189,4 @@ splint :
 # Not using $(CFLAGS) for msvc either
 .c.obj :
        clang --analyze $(INCLUDES) $(DEFINES) $(PKG_CFLAGS) $<
-!ENDIF
\ No newline at end of file
+!ENDIF


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]