[cairo-commit] cairo-demo/sproing ChangeLog, 1.5, 1.6 sproing.c, 1.4, 1.5

Carl Worth commit at pdx.freedesktop.org
Mon Mar 28 12:02:45 PST 2005


Committed by: cworth

Update of /cvs/cairo/cairo-demo/sproing
In directory gabe:/tmp/cvs-serv30866

Modified Files:
	ChangeLog sproing.c 
Log Message:

        * sproing.c: Reworked to have 2-way springs in the model, rather
        than 1-way attractors in the objects. The 2-way behavior lets us
        now pull any point around, (which is what's in place here). But
        the springs add a bit more bouncing than we'll actually want I
        think.


Index: ChangeLog
===================================================================
RCS file: /cvs/cairo/cairo-demo/sproing/ChangeLog,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- ChangeLog	28 Mar 2005 14:28:38 -0000	1.5
+++ ChangeLog	28 Mar 2005 20:02:43 -0000	1.6
@@ -1,5 +1,13 @@
 2005-03-28  Carl Worth  <cworth at cworth.org>
 
+	* sproing.c: Reworked to have 2-way springs in the model, rather
+	than 1-way attractors in the objects. The 2-way behavior lets us
+	now pull any point around, (which is what's in place here). But
+	the springs add a bit more bouncing than we'll actually want I
+	think.
+
+2005-03-28  Carl Worth  <cworth at cworth.org>
+
 	* sproing.c: (sproing_expose_event), (sproing_button_press_event),
 	(sproing_motion_notify_event), (create_window): Remove old
 	scribble names left over from some copy-and-paste.

Index: sproing.c
===================================================================
RCS file: /cvs/cairo/cairo-demo/sproing/sproing.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- sproing.c	28 Mar 2005 14:28:38 -0000	1.4
+++ sproing.c	28 Mar 2005 20:02:43 -0000	1.5
@@ -6,21 +6,31 @@
 #include <gdk/gdkx.h>
 #include <math.h>
 
-typedef struct _Point Point;
-struct _Point {
-  double x, y;
-};
-
-typedef struct _Vector Vector;
-struct _Vector {
+typedef struct _xy_pair Point;
+typedef struct _xy_pair Vector;
+struct _xy_pair {
   double x, y;
 };
 
 typedef struct _Model Model;
 typedef struct _Object Object;
 typedef struct _Attractor Attractor;
+typedef struct _Spring Spring;
 
-#define MAX_ATTRACTORS 5
+#define MODEL_MAX_SPRINGS 50
+#define MASS_INFINITE -1.0
+
+#define DEFAULT_SPRING_K 2.0
+#define DEFAULT_FRICTION 4.2
+
+struct _Spring {
+  Object *a;
+  Object *b;
+  /* Spring position at rest, from a to b:
+	offset = b.position - a.position
+  */
+  Vector offset;
+};
 
 struct _Attractor {
   Object *object;
@@ -28,74 +38,73 @@
 };
 
 struct _Object {
-  int num_attractors;
-  Attractor attractors[MAX_ATTRACTORS];
-
-  Vector attractor_offset;
+  Vector force;
 
   Point position;
-  Point next_position;
   Vector velocity;
-  Vector prev_velocity;
-  Vector acceleration;
-  Vector prev_acceleration;
+
   double mass;
   double theta;
+
+  int immobile;
 };
 
 struct _Model {
   int num_objects;
   Object *objects;
-  Object attractor;
 
-  Object *current_attractor;
-  double k;		/* Spring constant */
+  int num_springs;
+  Spring springs[MODEL_MAX_SPRINGS];
+  
+  Object *anchor_object;
+
   double friction;	/* Friction constant */
+  double k;		/* Spring constant */
 };
 
-
-static void
-model_move_attractor (Model *model, double x, double y)
-{
-  model->current_attractor->position.x = x;
-  model->current_attractor->position.y = y;
-}
-
 static void
-model_init_object (Object *object,
-		   double position_x, double position_y,
-		   double velocity_x, double velocity_y, double mass)
+object_init (Object *object,
+	     double position_x, double position_y,
+	     double velocity_x, double velocity_y, double mass)
 {
-  object->num_attractors = 0;
-
   object->position.x = position_x;
   object->position.y = position_y;
 
   object->velocity.x = velocity_x;
   object->velocity.y = velocity_y;
-  object->prev_velocity.x = velocity_x;
-  object->prev_velocity.y = velocity_y;
-
-  object->acceleration.x = 0;
-  object->acceleration.y = 0;
-  object->prev_acceleration.x = 0;
-  object->prev_acceleration.y = 0;
 
   object->mass = mass;
+
+  object->force.x = 0;
+  object->force.y = 0;
+
+  object->immobile = 0;
 }
 
 static void
-model_add_object_attractor (Object *object, Object *attractor_object,
-			    double offset_x, double offset_y)
+spring_init (Spring *spring,
+	     Object *object_a, Object *object_b,
+	     double offset_x, double offset_y)
 {
-  Attractor *attractor;
+  spring->a = object_a;
+  spring->b = object_b;
+  spring->offset.x = offset_x;
+  spring->offset.y = offset_y;
+}
 
-  attractor = &object->attractors[object->num_attractors];
-  object->num_attractors++;
+static void
+model_add_spring (Model *model,
+		  Object *object_a, Object *object_b,
+		  double offset_x, double offset_y)
+{
+  Spring *spring;
 
-  attractor->object = attractor_object;
-  attractor->offset.x = offset_x;
-  attractor->offset.y = offset_y;
+  g_assert (model->num_springs < MODEL_MAX_SPRINGS);
+
+  spring = &model->springs[model->num_springs];
+  model->num_springs++;
+
+  spring_init (spring, object_a, object_b, offset_x, offset_y);
 }
 
 static void
@@ -107,62 +116,65 @@
   model->objects = g_new (Object, width * height);
   model->num_objects = width * height;
 
+  model->num_springs = 0;
+
   i = 0;
   for (y = 0; y < height; y++)
     for (x = 0; x < width; x++) {
-      model_init_object (&model->objects[i], 200, 150, 0, 0, 20);
+      object_init (&model->objects[i], 200, 150, 0, 0, 20);
       
-#if 1
-      if (i == 0)
-	model_add_object_attractor (&model->objects[i],
-				    model->current_attractor,
-				    0, 0);
-#endif
-
 #define CX 0
 #define CY 0
 
 #if 0
       if (x < CX)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i + 1],
-				    -hpad, 0);
+	model_add_spring (model,
+			  &model->objects[i],
+			  &model->objects[i + 1],
+			  -hpad, 0);
       if (x > CX)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i - 1],
-				    hpad, 0);
+	model_add_spring (model,
+			  &model->objects[i],
+			  &model->objects[i - 1],
+			  hpad, 0);
 
       if (y < CY)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i + width],
-				    0, -vpad);
+	model_add_spring (model,
+			  &model->objects[i],
+			  &model->objects[i + width],
+			  0, -vpad);
       if (y > CY)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i - width],
-				    0, vpad);
+	model_add_spring (model,
+			  &model->objects[i],
+			  &model->objects[i - width],
+			  0, vpad);
 #endif
 
 #if 1
       if (x > 0)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i - 1],
-				    hpad, 0);
+	model_add_spring (model,
+			  &model->objects[i - 1],
+			  &model->objects[i],
+			  hpad, 0);
 
       if (y > 0)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i - width],
-				    0, vpad);
+	model_add_spring (model,
+			  &model->objects[i - width],
+			  &model->objects[i],
+			  0, vpad);
 #endif
 #if 0
       if (x < width - 1)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i + 1],
-				    -hpad, 0);
+	model_add_spring (model,
+			  &model->objects[i],
+			  &model->objects[i + 1],
+			  -hpad, 0);
 
       if (y < height - 1)
-	model_add_object_attractor (&model->objects[i],
-				    &model->objects[i + width],
-				    0, -vpad);
+	model_add_spring (model,
+			  &model->objects[i],
+			  &model->objects[i + width],
+			  0, -vpad);
 #endif
 
       i++;
@@ -172,58 +184,92 @@
 static void
 model_init (Model *model)
 {
-  model->current_attractor = &model->attractor;
-  model_move_attractor (model, 200.5, 140.5);
-  model->k = 2.0;
-  model->friction = 4.2;
+  model->anchor_object = NULL;
+
+  model->k        = DEFAULT_SPRING_K;
+  model->friction = DEFAULT_FRICTION;
 
   model_init_grid (model, 4, 4);
 }
 
 static void
-model_step_object (Model *model, Object *object)
+object_apply_force (Object *object, double fx, double fy)
 {
-  Vector target;
-  Vector force;
-  int i;
+  object->force.x += fx;
+  object->force.y += fy;
+}
 
-  force.x = 0;
-  force.y = 0;
-  for (i = 0; i < object->num_attractors; i++) {
-    target.x = object->attractors[i].object->position.x +
-      object->attractors[i].offset.x;
-    target.y = object->attractors[i].object->position.y +
-      object->attractors[i].offset.y;
+/* The model here can be understood as a rigid body of the spring's
+ * rest shape, centered on the vector between the two object
+ * positions. This rigid body is then connected by linear-force
+ * springs to each object. This model does degnerate into a simple
+ * spring for linear displacements, and does something reasonable for
+ * rotation.
+ *
+ * There are other possibilities for handling the rotation of the
+ * spring, and it might be interesting to explore something which has
+ * better length-preserving properties. For example, with the current
+ * model, an initial 180 degree rotation of the spring results in the
+ * spring collapsing down to 0 size before expanding back to it's
+ * natural size again.
+ */
+static void
+spring_exert_forces (Spring *spring, double k)
+{
+  Vector da, db;
+  Vector a, b;
 
-    force.x += model->k * (target.x - object->position.x) -
-      model->friction * object->velocity.x;
-    force.y += model->k * (target.y - object->position.y) - 
-      model->friction * object->velocity.y;
-  }
+  a = spring->a->position;
+  b = spring->b->position;
 
-  object->prev_acceleration.x = object->acceleration.x;
-  object->prev_acceleration.y = object->acceleration.y;
-  object->acceleration.x = force.x / object->mass;
-  object->acceleration.y = force.y / object->mass;
+  /* A nice vector diagram would likely help here, but my ASCII-art
+   * skills aren't up to the task. Here's how to make your own
+   * diagram:
+   *
+   * Draw a and b, and the vector AB from a to b
+   * Find the center of AB
+   * Draw spring->offset so that its center point is on the center of AB
+   * Draw da from a to the initial point of spring->offset
+   * Draw db from b to the final point of spring->offset
+   *
+   * The math below should be easy to verify from the diagram.
+   */
 
-  object->prev_velocity.x = object->velocity.x;
-  object->prev_velocity.y = object->velocity.y;
+  da.x = 0.5 * (b.x - a.x - spring->offset.x);
+  da.y = 0.5 * (b.y - a.y - spring->offset.y);
 
-  object->velocity.x +=
-    (object->acceleration.x + object->prev_acceleration.x) / 2.0 +
-    object->acceleration.x - object->prev_acceleration.x;
-  object->velocity.y +=
-    (object->acceleration.y + object->prev_acceleration.y) / 2.0 +
-    object->acceleration.y - object->prev_acceleration.y;
+  db.x = 0.5 * (a.x - b.x + spring->offset.x);
+  db.y = 0.5 * (a.y - b.y + spring->offset.y);
 
-  object->next_position.x = object->position.x +
-    (object->velocity.x + object->prev_velocity.x) / 2.0 +
-    object->velocity.x - object->prev_velocity.x;
-  object->next_position.y = object->position.y +
-    (object->velocity.y + object->prev_velocity.y) / 2.0 +
-    object->velocity.y - object->prev_velocity.y;
+  object_apply_force (spring->a, k *da.x, k * da.y);
+
+  object_apply_force (spring->b, k * db.x, k * db.y);
+}
+
+static void
+model_step_object (Model *model, Object *object)
+{
+  Vector acceleration;
 
   object->theta += 0.05;
+
+  /* Slow down due to friction. */
+  object->force.x -= model->friction * object->velocity.x;
+  object->force.y -= model->friction * object->velocity.y;
+
+  acceleration.x = object->force.x / object->mass;
+  acceleration.y = object->force.y / object->mass;
+
+  object->velocity.x += acceleration.x;
+  object->velocity.y += acceleration.y;
+
+  if (! object->immobile) {
+    object->position.x += object->velocity.x;
+    object->position.y += object->velocity.y;
+  }
+
+  object->force.x = 0.0;
+  object->force.y = 0.0;
 }
 
 static void
@@ -231,14 +277,11 @@
 {
   int i;
 
+  for (i = 0; i < model->num_springs; i++)
+    spring_exert_forces (&model->springs[i], model->k);
+
   for (i = 0; i < model->num_objects; i++)
     model_step_object (model, &model->objects[i]);
-
-  for (i = 0; i < model->num_objects; i++) {
-    if (&model->objects[i] == model->current_attractor)
-      continue;
-    model->objects[i].position = model->objects[i].next_position;
-  }
 }
 
 static cairo_t *
@@ -283,10 +326,10 @@
 static Color blue = { 0, 0, 1 };
 
 static void
-draw_ball (GtkWidget	*widget,
-	   gdouble	x,
-	   gdouble	y,
-	   Color	*color)
+draw_ball (GtkWidget   *widget,
+	   gdouble      x,
+	   gdouble      y,
+	   Color        *color)
 {
   cairo_t *cr;
 
@@ -343,7 +386,7 @@
   end_paint (cr);
 }
 
-void
+static void
 evaluate_bezier_point (Object *objects,
 		       double u, double v,
 		       double *patch_x, double *patch_y)
@@ -418,6 +461,7 @@
   end_paint (cr);
 }
 
+#if 0
 static void
 draw_spline_spiral (GtkWidget	*widget,
 		    Model      *model)
@@ -450,6 +494,7 @@
 
   end_paint (cr);
 }
+#endif
 
 static gboolean
 sproing_expose_event (GtkWidget      *widget,
@@ -465,8 +510,10 @@
   draw_spline_grid (widget, model);
 #endif
 
-  draw_ball (widget, model->current_attractor->position.x,
-	     model->current_attractor->position.y, &red);
+  if (model->anchor_object)
+    draw_ball (widget,
+	       model->anchor_object->position.x,
+	       model->anchor_object->position.y, &red);
 
 #if 1
   for (i = 0; i < model->num_objects; i++) {
@@ -478,15 +525,59 @@
   return TRUE;
 }
 
+static double
+object_distance (Object *object, double x, double y)
+{
+  double dx, dy;
+
+  dx = object->position.x - x;
+  dy = object->position.y - y;
+
+  return sqrt (dx*dx + dy*dy);
+}
+
+static Object *
+model_find_nearest (Model *model, double x, double y)
+{
+  Object *object;
+  double distance, min_distance;
+  int i;
+
+  for (i = 0; i < model->num_objects; i++) {
+    distance = object_distance (&model->objects[i], x, y);
+    if (i == 0 || distance < min_distance) {
+      min_distance = distance;
+      object = &model->objects[i];
+    }
+  }
+
+  return object;
+}
+
 static gboolean
 sproing_button_press_event (GtkWidget	   *widget,
 			    GdkEventButton *event,
 			    gpointer	    data)
 {
   Model *model = data;
-  
-  if (event->button == 1)
-    model_move_attractor (model, event->x + 0.5, event->y + 0.5);
+  double x, y;
+
+  if (event->button != 1)
+    return TRUE;
+
+  /* Shift by one-half pixel for better stroke alignment */
+  x = event->x + 0.5;
+  y = event->y + 0.5;
+
+  if (model->anchor_object)
+    model->anchor_object->immobile = 0;
+
+  model->anchor_object = model_find_nearest (model, x, y);
+
+  model->anchor_object->immobile = 1;
+
+  model->anchor_object->position.x = x;
+  model->anchor_object->position.y = y;
 
   return TRUE;
 }
@@ -502,8 +593,10 @@
 
   gdk_window_get_pointer (event->window, &x, &y, &state);
     
-  if (state & GDK_BUTTON1_MASK)
-    model_move_attractor (model, x + 0.5, y + 0.5);
+  if (state & GDK_BUTTON1_MASK) {
+    model->anchor_object->position.x = x + 0.5;
+    model->anchor_object->position.y = y + 0.5;
+  }
 
   return TRUE;
 }
@@ -524,7 +617,7 @@
   model->friction = gtk_spin_button_get_value (spinbutton);
 }
 
-GtkWidget *
+static GtkWidget *
 create_spinners (Model *model)
 {
   GtkWidget *hbox;
@@ -553,7 +646,7 @@
   return hbox;
 }
 
-GtkWidget *
+static GtkWidget *
 create_window (Model *model)
 {
   GtkWidget *window;
@@ -650,7 +743,7 @@
   closure.i = 0;
   gtk_widget_show_all (gtk_widget_get_toplevel (closure.drawing_area));
   closure.model = &model;
-  g_timeout_add (10, timeout_callback, &closure);
+  g_timeout_add (100, timeout_callback, &closure);
   gtk_main ();
 
   return 0;




More information about the cairo-commit mailing list