[gtk/layout-manager-transform-demo: 2/2] Add another layout manager demo




commit 9209cf4d77ce5a74439a0162aa00748a59e3a839
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Aug 28 07:45:57 2020 -0400

    Add another layout manager demo
    
    This demo uses transforms to place icons on a sphere.
    
    Still to do: backface culling, keynav instead of fixed
    speed animation.

 demos/gtk-demo/demo.gresource.xml             |   7 +
 demos/gtk-demo/demo2layout.c                  |  98 ++++---
 demos/gtk-demo/demo2widget.c                  |   2 +-
 demos/gtk-demo/four_point_transform.c         |  90 ++++++
 demos/gtk-demo/four_point_transform.h         |  13 +
 demos/gtk-demo/layoutmanager2.c               | 168 ++++++++++-
 demos/gtk-demo/meson.build                    |   2 +
 demos/gtk-demo/singular_value_decomposition.c | 396 ++++++++++++++++++++++++++
 demos/gtk-demo/singular_value_decomposition.h |  17 ++
 tests/simple.c                                | 144 +++++++++-
 10 files changed, 883 insertions(+), 54 deletions(-)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index d9084a96ae..a374cd0308 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -136,6 +136,12 @@
     <file>demochild.h</file>
     <file>demochild.c</file>
   </gresource>
+  <gresource prefix="/layoutmanager2">
+    <file>demo2layout.h</file>
+    <file>demo2layout.c</file>
+    <file>demo2widget.h</file>
+    <file>demo2widget.c</file>
+  </gresource>
   <gresource prefix="/listview_filebrowser">
     <file>listview_filebrowser.ui</file>
     <file>listview_filebrowser.css</file>
@@ -219,6 +225,7 @@
     <file>images.c</file>
     <file>infobar.c</file>
     <file>layoutmanager.c</file>
+    <file>layoutmanager2.c</file>
     <file>links.c</file>
     <file>listbox.c</file>
     <file>listbox2.c</file>
diff --git a/demos/gtk-demo/demo2layout.c b/demos/gtk-demo/demo2layout.c
index 9eaae52a60..7cf92ba720 100644
--- a/demos/gtk-demo/demo2layout.c
+++ b/demos/gtk-demo/demo2layout.c
@@ -1,5 +1,5 @@
 #include "demo2layout.h"
-
+#include "four_point_transform.h"
 
 struct _Demo2Layout
 {
@@ -49,6 +49,14 @@ demo2_layout_measure (GtkLayoutManager *layout_manager,
   *natural = 3 * natural_size;
 }
 
+
+#define RADIANS(angle) ((angle)*M_PI/180.0);
+
+/* Spherical coordinates */
+#define SX(r,t,p) ((r) * sin (t) * cos (p))
+#define SZ(r,t,p) ((r) * sin (t) * sin (p))
+#define SY(r,t,p) ((r) * cos (t))
+
 static void
 demo2_layout_allocate (GtkLayoutManager *layout_manager,
                        GtkWidget        *widget,
@@ -56,49 +64,61 @@ demo2_layout_allocate (GtkLayoutManager *layout_manager,
                        int               height,
                        int               baseline)
 {
-  Demo2Layout *self = DEMO2_LAYOUT (layout_manager);
   GtkWidget *child;
-  float t = fmodf (self->position, 4.0);
-  int x, y;
-
-  for (child = gtk_widget_get_first_child (widget);
+  GtkRequisition child_req;
+  int i, j, k;
+  float x0, y0;
+  float w, h;
+  graphene_point3d_t p1, p2, p3, p4;
+  graphene_point3d_t q1, q2, q3, q4;
+  double t_1, t_2, p_1, p_2;
+  double r;
+  graphene_matrix_t m;
+  GskTransform *transform;
+  double offset = DEMO2_LAYOUT (layout_manager)->position;
+
+  gtk_widget_get_preferred_size (gtk_widget_get_first_child (widget), &child_req, NULL);
+  w = child_req.width;
+  h = child_req.height;
+
+  r = 300;
+  x0 = y0 = 300;
+
+  for (child = gtk_widget_get_first_child (widget), i = 0;
        child != NULL;
-       child = gtk_widget_get_next_sibling (child))
+       child = gtk_widget_get_next_sibling (child), i++)
     {
-      GtkRequisition child_req;
+      j = i / 36;
+      k = i % 36;
 
-      if (!gtk_widget_should_layout (child))
-        continue;
+      graphene_point3d_init (&p1, 0., 0., 1);
+      graphene_point3d_init (&p2, w, 0., 1);
+      graphene_point3d_init (&p3, 0., h, 1);
+      graphene_point3d_init (&p4, w, h, 1);
+
+      t_1 = RADIANS (10 * j);
+      t_2 = RADIANS (10 * (j + 1));
+      p_1 = RADIANS (offset + 10 * k);
+      p_2 = RADIANS (offset + 10 * (k + 1));
+
+      graphene_point3d_init (&q1, x0 + SX (r, t_1, p_1), y0 + SY (r, t_1, p_1), SZ (r, t_1, p_1));
+      graphene_point3d_init (&q2, x0 + SX (r, t_2, p_1), y0 + SY (r, t_2, p_1), SZ (r, t_2, p_1));
+      graphene_point3d_init (&q3, x0 + SX (r, t_1, p_2), y0 + SY (r, t_1, p_2), SZ (r, t_1, p_2));
+      graphene_point3d_init (&q4, x0 + SX (r, t_2, p_2), y0 + SY (r, t_2, p_2), SZ (r, t_2, p_2));
+
+      perspective_3d (&p1, &p2, &p3, &p4,
+                      &q1, &q2, &q3, &q4,
+                      &m);
+
+      transform = gsk_transform_matrix (NULL, &m);
+
+      /* Since our matrix was built for transforming points with z = 1,
+       * prepend a translation to the z = 1 plane.
+       */
+      transform = gsk_transform_translate_3d (transform,
+                                              &GRAPHENE_POINT3D_INIT (0, 0, 1));
 
-      gtk_widget_get_preferred_size (child, &child_req, NULL);
-
-      if (t < 1.0)
-        {
-          x = t * (width - child_req.width);
-          y = 0;
-        }
-      else if (t < 2.0)
-        {
-          t -= 1.0;
-          x = (1.0 - t) * (width - child_req.width);
-          y = t * (height - child_req.height);
-        }
-      else if (t < 3.0)
-        {
-          t -= 2.0;
-          x = t * (width - child_req.width);
-          y = height - child_req.height;
-        }
-      else
-        {
-          t -= 3.0;
-          x = (1.0 - t) * (width - child_req.width);
-          y = (1.0 - t) * (height - child_req.height);
-        }
-
-      gtk_widget_size_allocate (child,
-                                &(const GtkAllocation){ x, y, child_req.width, child_req.height},
-                                -1);
+      gtk_widget_allocate (child, w, h, -1, transform);
     }
 }
 
diff --git a/demos/gtk-demo/demo2widget.c b/demos/gtk-demo/demo2widget.c
index e8e200dd1c..661b701a66 100644
--- a/demos/gtk-demo/demo2widget.c
+++ b/demos/gtk-demo/demo2widget.c
@@ -32,7 +32,7 @@ transition (GtkWidget     *widget,
   gint64 now = g_get_monotonic_time ();
 
   gtk_widget_queue_allocate (widget);
-  demo2_layout_set_position (demo2_layout, (now - self->start_time) / (double)G_TIME_SPAN_SECOND);
+  demo2_layout_set_position (demo2_layout, (now - self->start_time) * 30.0 / ((double)G_TIME_SPAN_SECOND));
 
   return G_SOURCE_CONTINUE;
 }
diff --git a/demos/gtk-demo/four_point_transform.c b/demos/gtk-demo/four_point_transform.c
new file mode 100644
index 0000000000..9df10ef163
--- /dev/null
+++ b/demos/gtk-demo/four_point_transform.c
@@ -0,0 +1,90 @@
+#include "four_point_transform.h"
+#include "singular_value_decomposition.h"
+
+/* Make a 4x4 matrix that maps
+ * e1        -> p1
+ * e2        -> p3
+ * e3        -> p3
+ * (1,1,1,0) -> p4
+ */
+static void
+unit_to (graphene_point3d_t *p1,
+         graphene_point3d_t *p2,
+         graphene_point3d_t *p3,
+         graphene_point3d_t *p4,
+         graphene_matrix_t  *m)
+{
+  graphene_vec3_t v1, v2, v3, v4;
+  graphene_vec4_t vv1, vv2, vv3, vv4, p;
+  graphene_matrix_t u, s;
+  float v[16] = { 0., };
+  double A[16];
+  double U[16];
+  double S[4];
+  double V[16];
+  double B[4];
+  double x[4];
+  int i, j;
+
+  graphene_point3d_to_vec3 (p1, &v1);
+  graphene_point3d_to_vec3 (p2, &v2);
+  graphene_point3d_to_vec3 (p3, &v3);
+  graphene_point3d_to_vec3 (p4, &v4);
+
+  graphene_vec4_init_from_vec3 (&vv1, &v1, 1.);
+  graphene_vec4_init_from_vec3 (&vv2, &v2, 1.);
+  graphene_vec4_init_from_vec3 (&vv3, &v3, 1.);
+  graphene_vec4_init_from_vec3 (&vv4, &v4, 1.);
+
+  graphene_vec4_init (&p, 0., 0., 0., 1.);
+
+  graphene_matrix_init_from_vec4 (&u, &vv1, &vv2, &vv3, &p);
+
+  /* solve x * u = vv4 */
+
+  for (i = 0; i < 4; i++)
+    for (j = 0; j < 4; j++)
+      A[j * 4 + i] = graphene_matrix_get_value (&u, i, j);
+
+  B[0] = graphene_vec4_get_x (&vv4);
+  B[1] = graphene_vec4_get_y (&vv4);
+  B[2] = graphene_vec4_get_z (&vv4);
+  B[3] = graphene_vec4_get_w (&vv4);
+
+  singular_value_decomposition (A, 4, 4, U, S, V);
+  singular_value_decomposition_solve (U, S, V, 4, 4, B, x);
+
+  v[ 0] = x[0];
+  v[ 5] = x[1];
+  v[10] = x[2];
+  v[15] = 1;
+
+  graphene_matrix_init_from_float (&s, (const float *)&v);
+  graphene_matrix_multiply (&s, &u, m);
+}
+
+/* Make a 4x4 matrix that maps
+ * p1 -> q1
+ * p2 -> q2
+ * p3 -> q3
+ * p4 -> q4
+ */
+void
+perspective_3d (graphene_point3d_t *p1,
+                graphene_point3d_t *p2,
+                graphene_point3d_t *p3,
+                graphene_point3d_t *p4,
+                graphene_point3d_t *q1,
+                graphene_point3d_t *q2,
+                graphene_point3d_t *q3,
+                graphene_point3d_t *q4,
+                graphene_matrix_t  *m)
+{
+  graphene_matrix_t a, a_inv, b;
+
+  unit_to (p1, p2, p3, p4, &a);
+  unit_to (q1, q2, q3, q4, &b);
+
+  graphene_matrix_inverse (&a, &a_inv);
+  graphene_matrix_multiply (&a_inv, &b, m);
+}
diff --git a/demos/gtk-demo/four_point_transform.h b/demos/gtk-demo/four_point_transform.h
new file mode 100644
index 0000000000..44b73c21b5
--- /dev/null
+++ b/demos/gtk-demo/four_point_transform.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <graphene.h>
+
+void perspective_3d (graphene_point3d_t *p1,
+                     graphene_point3d_t *p2,
+                     graphene_point3d_t *p3,
+                     graphene_point3d_t *p4,
+                     graphene_point3d_t *q1,
+                     graphene_point3d_t *q2,
+                     graphene_point3d_t *q3,
+                     graphene_point3d_t *q4,
+                     graphene_matrix_t  *m);
diff --git a/demos/gtk-demo/layoutmanager2.c b/demos/gtk-demo/layoutmanager2.c
index 97ed89fcee..ba48a4f1fd 100644
--- a/demos/gtk-demo/layoutmanager2.c
+++ b/demos/gtk-demo/layoutmanager2.c
@@ -11,6 +11,8 @@
 #include <gtk/gtk.h>
 
 #include "demo2widget.h"
+#include "demo2layout.h"
+#include "demochild.h"
 
 GtkWidget *
 do_layoutmanager2 (GtkWidget *parent)
@@ -21,6 +23,159 @@ do_layoutmanager2 (GtkWidget *parent)
     {
       GtkWidget *widget;
       GtkWidget *child;
+      const char *color[] = {
+        "red", "orange", "yellow", "green",
+        "blue", "grey", "magenta", "lime",
+        "yellow", "firebrick", "aqua", "purple",
+        "tomato", "pink", "thistle", "maroon"
+      };
+      const char *name[] = {
+        "action-unavailable-symbolic",
+        "address-book-new-symbolic",
+        "application-exit-symbolic",
+        "appointment-new-symbolic",
+        "bookmark-new-symbolic",
+        "call-start-symbolic",
+        "call-stop-symbolic",
+        "camera-switch-symbolic",
+        "chat-message-new-symbolic",
+        "color-select-symbolic",
+        "contact-new-symbolic",
+        "document-edit-symbolic",
+        "document-new-symbolic",
+        "document-open-recent-symbolic",
+        "document-open-symbolic",
+        "document-page-setup-symbolic",
+        "document-print-preview-symbolic",
+        "document-print-symbolic",
+        "document-properties-symbolic",
+        "document-revert-symbolic-rtl",
+        "document-revert-symbolic",
+        "document-save-as-symbolic",
+        "document-save-symbolic",
+        "document-send-symbolic",
+        "edit-clear-all-symbolic",
+        "edit-clear-symbolic-rtl",
+        "edit-clear-symbolic",
+        "edit-copy-symbolic",
+        "edit-cut-symbolic",
+        "edit-delete-symbolic",
+        "edit-find-replace-symbolic",
+        "edit-find-symbolic",
+        "edit-paste-symbolic",
+        "edit-redo-symbolic-rtl",
+        "edit-redo-symbolic",
+        "edit-select-all-symbolic",
+        "edit-select-symbolic",
+        "edit-undo-symbolic-rtl",
+        "edit-undo-symbolic",
+        "error-correct-symbolic",
+        "find-location-symbolic",
+        "folder-new-symbolic",
+        "font-select-symbolic",
+        "format-indent-less-symbolic-rtl",
+        "format-indent-less-symbolic",
+        "format-indent-more-symbolic-rtl",
+        "format-indent-more-symbolic",
+        "format-justify-center-symbolic",
+        "format-justify-fill-symbolic",
+        "format-justify-left-symbolic",
+        "format-justify-right-symbolic",
+        "format-text-bold-symbolic",
+        "format-text-direction-symbolic-rtl",
+        "format-text-direction-symbolic",
+        "format-text-italic-symbolic",
+        "format-text-strikethrough-symbolic",
+        "format-text-underline-symbolic",
+        "go-bottom-symbolic",
+        "go-down-symbolic",
+        "go-first-symbolic-rtl",
+        "go-first-symbolic",
+        "go-home-symbolic",
+        "go-jump-symbolic-rtl",
+        "go-jump-symbolic",
+        "go-last-symbolic-rtl",
+        "go-last-symbolic",
+        "go-next-symbolic-rtl",
+        "go-next-symbolic",
+        "go-previous-symbolic-rtl",
+        "go-previous-symbolic",
+        "go-top-symbolic",
+        "go-up-symbolic",
+        "help-about-symbolic",
+        "insert-image-symbolic",
+        "insert-link-symbolic",
+        "insert-object-symbolic",
+        "insert-text-symbolic",
+        "list-add-symbolic",
+        "list-remove-all-symbolic",
+        "list-remove-symbolic",
+        "mail-forward-symbolic",
+        "mail-mark-important-symbolic",
+        "mail-mark-junk-symbolic",
+        "mail-mark-notjunk-symbolic",
+        "mail-message-new-symbolic",
+        "mail-reply-all-symbolic",
+        "mail-reply-sender-symbolic",
+        "mail-send-receive-symbolic",
+        "mail-send-symbolic",
+        "mark-location-symbolic",
+        "media-eject-symbolic",
+        "media-playback-pause-symbolic",
+        "media-playback-start-symbolic",
+        "media-playback-stop-symbolic",
+        "media-record-symbolic"
+        "media-seek-backward-symbolic",
+        "media-seek-forward-symbolic",
+        "media-skip-backward-symbolic",
+        "media-skip-forward-symbolic",
+        "media-view-subtitles-symbolic",
+        "object-flip-horizontal-symbolic",
+        "object-flip-vertical-symbolic",
+        "object-rotate-left-symbolic",
+        "object-rotate-right-symbolic",
+        "object-select-symbolic",
+        "open-menu-symbolic",
+        "process-stop-symbolic",
+        "send-to-symbolic",
+        "sidebar-hide-symbolic",
+        "sidebar-show-symbolic",
+        "star-new-symbolic",
+        "system-log-out-symbolic",
+        "system-reboot-symbolic",
+        "system-run-symbolic",
+        "system-search-symbolic",
+        "system-shutdown-symbolic",
+        "system-switch-user-symbolic",
+        "tab-new-symbolic",
+        "tools-check-spelling-symbolic",
+        "value-decrease-symbolic",
+        "value-increase-symbolic",
+        "view-app-grid-symbolic",
+        "view-conceal-symbolic",
+        "view-continuous-symbolic",
+        "view-dual-symbolic",
+        "view-fullscreen-symbolic",
+        "view-grid-symbolic",
+        "view-list-bullet-symbolic",
+        "view-list-ordered-symbolic",
+        "view-list-symbolic",
+        "view-mirror-symbolic",
+        "view-more-horizontal-symbolic",
+        "view-more-symbolic",
+        "view-paged-symbolic",
+        "view-pin-symbolic",
+        "view-refresh-symbolic",
+        "view-restore-symbolic",
+        "view-reveal-symbolic",
+        "view-sort-ascending-symbolic",
+        "view-sort-descending-symbolic",
+        "zoom-fit-best-symbolic",
+        "zoom-in-symbolic",
+        "zoom-original-symbolic",
+        "zoom-out-symbolic",
+      };
+      int i;
 
       window = gtk_window_new ();
       gtk_window_set_title (GTK_WINDOW (window), "Transformation");
@@ -29,9 +184,16 @@ do_layoutmanager2 (GtkWidget *parent)
 
       widget = demo2_widget_new ();
 
-      child =  gtk_button_new_with_label ("Hello");
-      gtk_widget_set_cursor_from_name (child, "crosshair");
-      demo2_widget_add_child (DEMO2_WIDGET (widget), child);
+      for (i = 0; i < 18 * 36; i++)
+        {
+          child = gtk_image_new_from_icon_name (name[i % G_N_ELEMENTS (name)]);
+          //child = demo_child_new (color[i % G_N_ELEMENTS(color)]);
+          gtk_widget_set_margin_start (child, 4);
+          gtk_widget_set_margin_end (child, 4);
+          gtk_widget_set_margin_top (child, 4);
+          gtk_widget_set_margin_bottom (child, 4);
+          demo2_widget_add_child (DEMO2_WIDGET (widget), child);
+        }
 
       gtk_window_set_child (GTK_WINDOW (window), widget);
     }
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 358868d54e..05b32c6f46 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -106,6 +106,8 @@ extra_demo_sources = files(['main.c',
                             'demolayout.c',
                             'demowidget.c',
                             'demo2layout.c',
+                            'singular_value_decomposition.c',
+                            'four_point_transform.c',
                             'demo2widget.c'])
 
 if harfbuzz_dep.found() and pangoft_dep.found()
diff --git a/demos/gtk-demo/singular_value_decomposition.c b/demos/gtk-demo/singular_value_decomposition.c
new file mode 100644
index 0000000000..d50bb80ace
--- /dev/null
+++ b/demos/gtk-demo/singular_value_decomposition.c
@@ -0,0 +1,396 @@
+#include <string.h>
+#include <float.h>
+#include <math.h>
+#include <alloca.h>
+
+/* See Golub and Reinsch,
+ * "Handbook for Automatic Computation vol II - Linear Algebra",
+ * Springer, 1971
+ */
+
+
+#define MAX_ITERATION_COUNT 30
+
+static void
+householder_reduction (double *A,
+                       int     nrows,
+                       int     ncols,
+                       double *U,
+                       double *V,
+                       double *diagonal,
+                       double *superdiagonal)
+{
+  int i, j, k, ip1;
+  double s, s2, si, scale;
+  double *pu, *pui, *pv, *pvi;
+  double half_norm_squared;
+
+  memcpy (U, A, sizeof (double) * nrows * ncols);
+
+  diagonal[0] = 0.0;
+  s = 0.0;
+  scale = 0.0;
+  for (i = 0, pui = U, ip1 = 1;
+       i < ncols;
+       pui += ncols, i++, ip1++)
+    {
+      superdiagonal[i] = scale * s;
+
+      for (j = i, pu = pui, scale = 0.0;
+           j < nrows;
+           j++, pu += ncols)
+        scale += fabs( *(pu + i) );
+
+      if (scale > 0.0)
+        {
+          for (j = i, pu = pui, s2 = 0.0; j < nrows; j++, pu += ncols)
+            {
+              *(pu + i) /= scale;
+              s2 += *(pu + i) * *(pu + i);
+            }
+          s = *(pui + i) < 0.0 ? sqrt (s2) : -sqrt (s2);
+          half_norm_squared = *(pui + i) * s - s2;
+          *(pui + i) -= s;
+
+          for (j = ip1; j < ncols; j++)
+            {
+              for (k = i, si = 0.0, pu = pui; k < nrows; k++, pu += ncols)
+                si += *(pu + i) * *(pu + j);
+              si /= half_norm_squared;
+              for (k = i, pu = pui; k < nrows; k++, pu += ncols)
+                *(pu + j) += si * *(pu + i);
+            }
+        }
+      for (j = i, pu = pui; j < nrows; j++, pu += ncols)
+        *(pu + i) *= scale;
+      diagonal[i] = s * scale;
+      s = 0.0;
+      scale = 0.0;
+      if (i >= nrows || i == ncols - 1)
+        continue;
+      for (j = ip1; j < ncols; j++)
+        scale += fabs (*(pui + j));
+      if (scale > 0.0)
+        {
+          for (j = ip1, s2 = 0.0; j < ncols; j++)
+            {
+              *(pui + j) /= scale;
+              s2 += *(pui + j) * *(pui + j);
+            }
+          s = *(pui + ip1) < 0.0 ? sqrt (s2) : -sqrt (s2);
+          half_norm_squared = *(pui + ip1) * s - s2;
+          *(pui + ip1) -= s;
+          for (k = ip1; k < ncols; k++)
+            superdiagonal[k] = *(pui + k) / half_norm_squared;
+          if (i < (nrows - 1))
+            {
+              for (j = ip1, pu = pui + ncols; j < nrows; j++, pu += ncols)
+                {
+                  for (k = ip1, si = 0.0; k < ncols; k++)
+                    si += *(pui + k) * *(pu + k);
+                  for (k = ip1; k < ncols; k++)
+                    *(pu + k) += si * superdiagonal[k];
+                }
+            }
+          for (k = ip1; k < ncols; k++)
+            *(pui + k) *= scale;
+        }
+    }
+
+  pui = U + ncols * (ncols - 2);
+  pvi = V + ncols * (ncols - 1);
+  *(pvi + ncols - 1) = 1.0;
+  s = superdiagonal[ncols - 1];
+  pvi -= ncols;
+  for (i = ncols - 2, ip1 = ncols - 1;
+       i >= 0;
+       i--, pui -= ncols, pvi -= ncols, ip1--)
+    {
+      if (s != 0.0)
+        {
+          pv = pvi + ncols;
+          for (j = ip1; j < ncols; j++, pv += ncols)
+            *(pv + i) = ( *(pui + j) / *(pui + ip1) ) / s;
+          for (j = ip1; j < ncols; j++)
+            {
+              si = 0.0;
+              for (k = ip1, pv = pvi + ncols; k < ncols; k++, pv += ncols)
+                si += *(pui + k) * *(pv + j);
+              for (k = ip1, pv = pvi + ncols; k < ncols; k++, pv += ncols)
+                *(pv + j) += si * *(pv + i);
+            }
+        }
+      pv = pvi + ncols;
+      for (j = ip1; j < ncols; j++, pv += ncols)
+        {
+          *(pvi + j) = 0.0;
+          *(pv + i) = 0.0;
+        }
+      *(pvi + i) = 1.0;
+      s = superdiagonal[i];
+    }
+
+  pui = U + ncols * (ncols - 1);
+  for (i = ncols - 1, ip1 = ncols;
+       i >= 0;
+       ip1 = i, i--, pui -= ncols)
+    {
+      s = diagonal[i];
+      for (j = ip1; j < ncols; j++)
+        *(pui + j) = 0.0;
+      if (s != 0.0)
+        {
+          for (j = ip1; j < ncols; j++)
+            {
+              si = 0.0;
+              pu = pui + ncols;
+              for (k = ip1; k < nrows; k++, pu += ncols)
+                si += *(pu + i) * *(pu + j);
+              si = (si / *(pui + i)) / s;
+              for (k = i, pu = pui; k < nrows; k++, pu += ncols)
+                *(pu + j) += si * *(pu + i);
+            }
+          for (j = i, pu = pui; j < nrows; j++, pu += ncols)
+            *(pu + i) /= s;
+        }
+      else
+        for (j = i, pu = pui; j < nrows; j++, pu += ncols)
+          *(pu + i) = 0.0;
+      *(pui + i) += 1.0;
+    }
+}
+
+static int
+givens_reduction (int nrows,
+                  int ncols,
+                  double *U,
+                  double *V,
+                  double *diagonal,
+                  double *superdiagonal)
+{
+  double epsilon;
+  double c, s;
+  double f,g,h;
+  double x,y,z;
+  double *pu, *pv;
+  int i,j,k,m;
+  int rotation_test;
+  int iteration_count;
+
+  for (i = 0, x = 0.0; i < ncols; i++)
+    {
+      y = fabs (diagonal[i]) + fabs (superdiagonal[i]);
+      if (x < y)
+        x = y;
+    }
+  epsilon = x * DBL_EPSILON;
+  for (k = ncols - 1; k >= 0; k--)
+    {
+      iteration_count = 0;
+      while (1)
+        {
+          rotation_test = 1;
+          for (m = k; m >= 0; m--)
+            {
+              if (fabs (superdiagonal[m]) <= epsilon)
+                {
+                  rotation_test = 0;
+                  break;
+                }
+              if (fabs (diagonal[m-1]) <= epsilon)
+                break;
+            }
+          if (rotation_test)
+            {
+              c = 0.0;
+              s = 1.0;
+              for (i = m; i <= k; i++)
+                {
+                  f = s * superdiagonal[i];
+                  superdiagonal[i] *= c;
+                  if (fabs (f) <= epsilon)
+                    break;
+                  g = diagonal[i];
+                  h = sqrt (f*f + g*g);
+                  diagonal[i] = h;
+                  c = g / h;
+                  s = -f / h;
+                  for (j = 0, pu = U; j < nrows; j++, pu += ncols)
+                    {
+                      y = *(pu + m - 1);
+                      z = *(pu + i);
+                      *(pu + m - 1 ) = y * c + z * s;
+                      *(pu + i) = -y * s + z * c;
+                    }
+                }
+            }
+          z = diagonal[k];
+          if (m == k)
+            {
+              if (z < 0.0)
+                {
+                  diagonal[k] = -z;
+                  for (j = 0, pv = V; j < ncols; j++, pv += ncols)
+                    *(pv + k) = - *(pv + k);
+                }
+              break;
+            }
+          else
+            {
+              if (iteration_count >= MAX_ITERATION_COUNT)
+                return -1;
+              iteration_count++;
+              x = diagonal[m];
+              y = diagonal[k-1];
+              g = superdiagonal[k-1];
+              h = superdiagonal[k];
+              f = ((y - z) * ( y + z ) + (g - h) * (g + h))/(2.0 * h * y);
+              g = sqrt (f * f + 1.0);
+              if (f < 0.0)
+                g = -g;
+              f = ((x - z) * (x + z) + h * (y / (f + g) - h)) / x;
+              c = 1.0;
+              s = 1.0;
+              for (i = m + 1; i <= k; i++)
+                {
+                  g = superdiagonal[i];
+                  y = diagonal[i];
+                  h = s * g;
+                  g *= c;
+                  z = sqrt (f * f + h * h);
+                  superdiagonal[i-1] = z;
+                  c = f / z;
+                  s = h / z;
+                  f =  x * c + g * s;
+                  g = -x * s + g * c;
+                  h = y * s;
+                  y *= c;
+                  for (j = 0, pv = V; j < ncols; j++, pv += ncols)
+                    {
+                      x = *(pv + i - 1);
+                      z = *(pv + i);
+                      *(pv + i - 1) = x * c + z * s;
+                      *(pv + i) = -x * s + z * c;
+                    }
+                  z = sqrt (f * f + h * h);
+                  diagonal[i - 1] = z;
+                  if (z != 0.0)
+                    {
+                      c = f / z;
+                      s = h / z;
+                    }
+                  f = c * g + s * y;
+                  x = -s * g + c * y;
+                  for (j = 0, pu = U; j < nrows; j++, pu += ncols)
+                    {
+                      y = *(pu + i - 1);
+                      z = *(pu + i);
+                      *(pu + i - 1) = c * y + s * z;
+                      *(pu + i) = -s * y + c * z;
+                    }
+                }
+              superdiagonal[m] = 0.0;
+              superdiagonal[k] = f;
+              diagonal[k] = x;
+            }
+        }
+    }
+  return 0;
+}
+
+static void
+sort_singular_values (int     nrows,
+                      int     ncols,
+                      double *S,
+                      double *U,
+                      double *V)
+{
+  int i, j, max_index;
+  double temp;
+  double *p1, *p2;
+
+  for (i = 0; i < ncols - 1; i++)
+    {
+      max_index = i;
+      for (j = i + 1; j < ncols; j++)
+        if (S[j] > S[max_index])
+          max_index = j;
+      if (max_index == i)
+        continue;
+      temp = S[i];
+      S[i] = S[max_index];
+      S[max_index] = temp;
+      p1 = U + max_index;
+      p2 = U + i;
+      for (j = 0; j < nrows; j++, p1 += ncols, p2 += ncols)
+        {
+          temp = *p1;
+          *p1 = *p2;
+          *p2 = temp;
+        }
+      p1 = V + max_index;
+      p2 = V + i;
+      for (j = 0; j < ncols; j++, p1 += ncols, p2 += ncols)
+        {
+          temp = *p1;
+          *p1 = *p2;
+          *p2 = temp;
+        }
+    }
+}
+
+int
+singular_value_decomposition (double *A,
+                              int     nrows,
+                              int     ncols,
+                              double *U,
+                              double *S,
+                              double *V)
+{
+  double *superdiagonal;
+
+  superdiagonal = alloca (sizeof (double) * ncols);
+
+  if (nrows < ncols)
+    return -1;
+
+  householder_reduction (A, nrows, ncols, U, V, S, superdiagonal);
+
+  if (givens_reduction (nrows, ncols, U, V, S, superdiagonal) < 0)
+    return -1;
+
+  sort_singular_values (nrows, ncols, S, U, V);
+
+  return 0;
+}
+
+void
+singular_value_decomposition_solve (double *U,
+                                    double *S,
+                                    double *V,
+                                    int     nrows,
+                                    int     ncols,
+                                    double *B,
+                                    double *x)
+{
+  int i, j, k;
+  double *pu, *pv;
+  double d;
+  double tolerance;
+
+  tolerance = DBL_EPSILON * S[0] * (double) ncols;
+
+  for ( i = 0, pv = V; i < ncols; i++, pv += ncols)
+    {
+      x[i] = 0.0;
+      for (j = 0; j < ncols; j++)
+        {
+          if (S[j] > tolerance)
+            {
+              for (k = 0, d = 0.0, pu = U; k < nrows; k++, pu += ncols)
+                d += *(pu + j) * B[k];
+              x[i] += d * *(pv + j) / S[j];
+            }
+        }
+    }
+}
diff --git a/demos/gtk-demo/singular_value_decomposition.h b/demos/gtk-demo/singular_value_decomposition.h
new file mode 100644
index 0000000000..9b4c65a2a6
--- /dev/null
+++ b/demos/gtk-demo/singular_value_decomposition.h
@@ -0,0 +1,17 @@
+#pragma once
+
+int singular_value_decomposition (double *A,
+                                  int     nrows,
+                                  int     ncols,
+                                  double *U,
+                                  double *S,
+                                  double *V);
+
+void singular_value_decomposition_solve (double *U,
+                                         double *S,
+                                         double *V,
+                                         int     nrows,
+                                         int     ncols,
+                                         double *B,
+                                         double *x);
+
diff --git a/tests/simple.c b/tests/simple.c
index 062afdc9fa..b861631090 100644
--- a/tests/simple.c
+++ b/tests/simple.c
@@ -36,28 +36,150 @@ quit_cb (GtkWidget *widget,
   g_main_context_wakeup (NULL);
 }
 
+static void
+invert3 (double m[9], double n[9])
+{
+  double det;
+  double a = m[0];
+  double b = m[1];
+  double c = m[2];
+  double d = m[3];
+  double e = m[4];
+  double f = m[5];
+  double g = m[6];
+  double h = m[7];
+  double k = m[8];
+
+  det = a*(e*k - f*h) - b*(d*k - f*g) + c*(d*h - e*g);
+
+  n[0] =  (e*k - f*h) / det;
+  n[1] = - (b*k - c*h) / det;
+  n[2] = (b*f - c*e) / det;
+  n[3] = - (d*k - f*g) / det;
+  n[4] = (a*k - c*g) / det;
+  n[5] = - (a*g - c*d) / det;
+  n[6] = (d*h - e*g) / det;
+  n[7] = - (a*h - b*g) / det;
+  n[8] = (a*e - b*d) / det;
+}
+
+static void
+perspective2d (double x0, double y0,
+               double x1, double y1,
+               double x2, double y2,
+               double x3, double y3,
+               double o[9])
+{
+  double mat[9], inv[9];
+  double l, m, t;
+
+  mat[0] = x0;
+  mat[1] = x1;
+  mat[2] = x2;
+  mat[3] = y0;
+  mat[4] = y1;
+  mat[5] = y2;
+  mat[6] = 1;
+  mat[7] = 1;
+  mat[8] = 1;
+
+  invert3 (mat, inv);
+
+  l = inv[0] * x3 + inv[1] * y3 + inv[2];
+  m = inv[3] * x3 + inv[4] * y3 + inv[5];
+  t = inv[6] * x3 + inv[7] * y3 + inv[8];
+
+  o[0] = l*x0; o[1] = m*x1; o[2] = t*x2;
+  o[3] = l*y0; o[4] = m*y1; o[5] = t*y2;
+  o[6] = l;    o[7] = m;    o[8] = t;
+}
+
+#define RADIANS(angle) ((angle)*M_PI/180.0);
+#define SX(r,t,p) ((r) * sin (t) * cos (p))
+#define SZ(r,t,p) ((r) * sin (t) * sin (p))
+#define SY(r,t,p) ((r) * cos (t))
+
+static double offset;
+
+static gboolean
+tick_cb (GtkWidget *widget,
+         GdkFrameClock *clock,
+         gpointer data)
+{
+  offset += 1;
+  gtk_widget_queue_draw (widget);
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+draw_func (GtkDrawingArea *da,
+           cairo_t *cr,
+           int width,
+           int height,
+           gpointer data)
+{
+  double r = 500;
+  double t1, t2, p1, p2;
+  double x0, y0, x1, y1, x2, y2, x3, y3;
+  int i, j;
+
+  for (i = 0; i < 18; i++)
+    for (j = 0; j < 36; j++)
+      {
+        if ((i + j) % 2)
+          cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+        else
+          cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+
+        t1 = RADIANS (10 * i);
+        t2 = RADIANS (10 * (i + 1));
+        p1 = RADIANS (offset + 10 * j);
+        p2 = RADIANS (offset + 10 * (j + 1));
+
+        x0 = SX (r, t1, p1);
+        y0 = SY (r, t1, p1);
+        x1 = SX (r, t1, p2);
+        y1 = SY (r, t1, p2);
+        x2 = SX (r, t2, p2);
+        y2 = SY (r, t2, p2);
+        x3 = SX (r, t2, p1);
+        y3 = SY (r, t2, p1);
+
+        if (SZ (r, t1, p1) > 0)
+          continue;
+
+        cairo_move_to (cr, 150, 150);
+        cairo_rel_move_to (cr, x0, y0);
+        cairo_rel_line_to (cr, x1 - x0, y1 - y0);
+        cairo_rel_line_to (cr, x2 - x1, y2 - y1);
+        cairo_rel_line_to (cr, x3 - x2, y3 - y2);
+        cairo_rel_line_to (cr, x0 - x3, y0 - y3);
+        cairo_close_path (cr);
+
+        cairo_fill (cr);
+      }
+}
+
 int
 main (int argc, char *argv[])
 {
-  GtkWidget *window, *button;
+  GtkWidget *window, *box, *da;
   gboolean done = FALSE;
 
   gtk_init ();
 
   window = gtk_window_new ();
   gtk_window_set_title (GTK_WINDOW (window), "hello world");
-  gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
   g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
 
-  button = gtk_button_new ();
-  gtk_button_set_label (GTK_BUTTON (button), "hello world");
-  gtk_widget_set_margin_top (button, 10);
-  gtk_widget_set_margin_bottom (button, 10);
-  gtk_widget_set_margin_start (button, 10);
-  gtk_widget_set_margin_end (button, 10);
-  g_signal_connect (button, "clicked", G_CALLBACK (hello), NULL);
-
-  gtk_window_set_child (GTK_WINDOW (window), button);
+  box = gtk_box_new (1, 10);
+  gtk_window_set_child (GTK_WINDOW (window), box);
+  da = gtk_drawing_area_new ();
+  gtk_widget_set_hexpand (da, TRUE);
+  gtk_widget_set_vexpand (da, TRUE);
+  gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
+  gtk_box_append (GTK_BOX (box), da);
+  gtk_widget_add_tick_callback (da, tick_cb, NULL, NULL);
 
   gtk_widget_show (window);
 


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