[cairo-commit] cairo-demo/sproing COPYING, NONE, 1.1 Makefile, NONE, 1.1 sproing.c, NONE, 1.1

Kristian Hogsberg commit at pdx.freedesktop.org
Thu Mar 10 22:52:18 PST 2005


Committed by: krh

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

Added Files:
	COPYING Makefile sproing.c 
Log Message:
2005-03-11  Kristian Høgsberg  <krh at redhat.com>

        * Makefile, sproing.c: New cairo demo.  Drag a wobbly patch around
        for fun.



--- NEW FILE: COPYING ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: Makefile ---
CFLAGS = -Wall -g
CPPFLAGS = $(shell pkg-config --cflags gtk+-2.0 cairo)
LDLIBS = $(shell pkg-config --libs gtk+-2.0 cairo)
LDFLAGS = -g

target = sproing
objs = sproing.o

$(target) : $(objs)

clean :
	rm $(target) $(objs)

--- NEW FILE: sproing.c ---
/* -*- mode: c; c-basic-offset: 2 -*- */

#include <gtk/gtk.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <gdk/gdkx.h>
#include <math.h>

typedef struct _Point Point;
struct _Point {
  double x, y;
};

typedef struct _Vector Vector;
struct _Vector {
  double x, y;
};

typedef struct _Model Model;
typedef struct _Object Object;
typedef struct _Attractor Attractor;

#define MAX_ATTRACTORS 5

struct _Attractor {
  Object *object;
  Vector offset;
};

struct _Object {
  int num_attractors;
  Attractor attractors[MAX_ATTRACTORS];

  Vector attractor_offset;

  Point position;
  Point next_position;
  Vector velocity;
  Vector prev_velocity;
  Vector acceleration;
  Vector prev_acceleration;
  double mass;
  double theta;
};

struct _Model {
  int num_objects;
  Object *objects;
  Object attractor;

  Object *current_attractor;
  double k;		/* Spring constant */
  double friction;	/* Friction 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->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;
}

static void
model_add_object_attractor (Object *object, Object *attractor_object,
			    double offset_x, double offset_y)
{
  Attractor *attractor;

  attractor = &object->attractors[object->num_attractors];
  object->num_attractors++;

  attractor->object = attractor_object;
  attractor->offset.x = offset_x;
  attractor->offset.y = offset_y;
}

static void
model_init_grid (Model *model, int width, int height)
{
  int x, y, i;
  const int hpad = 50, vpad = 50;

  model->objects = g_new (Object, width * height);
  model->num_objects = width * height;

  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);
      
#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);
      if (x > CX)
	model_add_object_attractor (&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);
      if (y > CY)
	model_add_object_attractor (&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);

      if (y > 0)
	model_add_object_attractor (&model->objects[i],
				    &model->objects[i - width],
				    0, vpad);
#endif
#if 0
      if (x < width - 1)
	model_add_object_attractor (&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);
#endif

      i++;
    }
}

static void
model_init (Model *model)
{
  model->current_attractor = &model->attractor;
  model_move_attractor (model, 200, 140);
  model->k = 2.0;
  model->friction = 4.2;

  model_init_grid (model, 4, 4);
}

static void
model_step_object (Model *model, Object *object)
{
  Vector target;
  Vector force;
  int i;

  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;

    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;
  }

  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;

  object->prev_velocity.x = object->velocity.x;
  object->prev_velocity.y = object->velocity.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;

  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->theta += 0.05;
}

static void
model_step (Model *model)
{
  int i;

  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 *
begin_paint (GdkDrawable *window)
{
    Display *dpy;
    Drawable xid;
    GdkDrawable *drawable;
    gint x_offset, y_offset;
    cairo_t *cr;
  
    if (GDK_IS_WINDOW (window))
	gdk_window_get_internal_paint_info (window, &drawable,
					    &x_offset, &y_offset);
    else
	drawable = window;

    dpy = gdk_x11_drawable_get_xdisplay (drawable);
    xid = gdk_x11_drawable_get_xid (drawable);

    cr = cairo_create ();
    cairo_set_target_drawable (cr, dpy, xid);

    if (GDK_IS_WINDOW (window))
	cairo_translate (cr, -x_offset, -y_offset);

    return cr;
}

static void
end_paint (cairo_t *cr)
{
    cairo_destroy (cr);
}

typedef struct _Color Color;
struct _Color {
  double red, green, blue;
};

static Color red = { 1, 0, 0 };
static Color blue = { 0, 0, 1 };

static void
draw_ball (GtkWidget	*widget,
	   gdouble	x,
	   gdouble	y,
	   Color	*color)
{
  cairo_t *cr;

  cr = begin_paint (widget->window);

  cairo_set_rgb_color (cr, color->red, color->green, color->blue);
  cairo_set_alpha (cr, 0.5);
  cairo_new_path (cr);
  cairo_arc (cr, x, y, 3, 0, 2 * G_PI);
  cairo_fill (cr);

  cairo_new_path (cr);
  cairo_arc (cr, x, y, 10, 0, 2 * G_PI);
  cairo_stroke (cr);

  end_paint (cr);
}

static void
draw_star (GtkWidget	*widget,
	   gdouble	cx,
	   gdouble	cy,
	   double	theta,
	   Color	*color)
{
  const int spike_count = 5;
  const int inner_radius = 2;
  const int outer_radius = 4;
  cairo_t *cr;
  double x, y;
  int i;

  cr = begin_paint (widget->window);

  cairo_set_rgb_color (cr, color->red, color->green, color->blue);
  cairo_set_alpha (cr, 0.5);
  cairo_new_path (cr);
  for (i = 0; i < spike_count; i++) {
    x = cx + cos ((i * 2) * M_PI / spike_count + theta) * inner_radius;
    y = cy + sin ((i * 2) * M_PI / spike_count + theta) * inner_radius;

    if (i == 0)
      cairo_move_to (cr, x, y);
    else
      cairo_line_to (cr, x, y);
		 
    x = cx + cos ((i * 2 + 1) * M_PI / spike_count + theta) * outer_radius;
    y = cy + sin ((i * 2 + 1) * M_PI / spike_count + theta) * outer_radius;

    cairo_line_to (cr, x, y);
  }
  cairo_fill (cr);

  end_paint (cr);
}

void
evaluate_bezier_point (Object *objects,
		       double u, double v,
		       double *patch_x, double *patch_y)
{
  double coeffs_u[4], coeffs_v[4];
  double x, y;
  int i, j;
  
  coeffs_u[0] = (1 - u) * (1 - u) * (1 - u);
  coeffs_u[1] = 3 * u * (1 - u) * (1 - u);
  coeffs_u[2] = 3 * u * u * (1 - u);
  coeffs_u[3] = u * u * u;

  coeffs_v[0] = (1 - v) * (1 - v) * (1 - v);
  coeffs_v[1] = 3 * v * (1 - v) * (1 - v);
  coeffs_v[2] = 3 * v * v * (1 - v);
  coeffs_v[3] = v * v * v;

  x = 0;
  y = 0;
  for (i = 0; i < 4; i++)
    for (j = 0; j < 4; j++)
      {
	x += coeffs_u[i] * coeffs_v[j] * objects[j * 4 + i].position.x;
	y += coeffs_u[i] * coeffs_v[j] * objects[j * 4 + i].position.y;
      }

  *patch_x = x;
  *patch_y = y;
}

static void
draw_spline_grid (GtkWidget	*widget,
		  Model      *model)
{
  cairo_t *cr;
  double u, v;
  double x, y;

  cr = begin_paint (widget->window);

  cairo_set_rgb_color (cr, 0, 0, 0);
  cairo_set_line_width (cr, 0.8);

  cairo_new_path (cr);
  for (u = 0; u <= 1.01; u += 0.2)
    {
      for (v = 0; v <= 1.01; v += 0.05)
	{
	  evaluate_bezier_point (model->objects, u, v, &x, &y);
	  if (v == 0)
	    cairo_move_to (cr, x, y);
	  else
	    cairo_line_to (cr, x, y);
	}
    }

  for (v = 0; v <= 1.01; v += 0.2)
    {
      for (u = 0; u <= 1.01; u += 0.05)
	{
	  evaluate_bezier_point (model->objects, u, v, &x, &y);
	  if (u == 0)
	    cairo_move_to (cr, x, y);
	  else
	    cairo_line_to (cr, x, y);
	}
    }

  cairo_stroke (cr);

  end_paint (cr);
}

static void
draw_spline_spiral (GtkWidget	*widget,
		    Model      *model)
{
  cairo_t *cr;
  double u, v, r, phi, offset;
  double x, y;

  cr = begin_paint (widget->window);

  cairo_set_rgb_color (cr, 0, 0, 0);
  cairo_set_line_width (cr, 0.8);

  cairo_new_path (cr);

  offset = 0.01;
  for (phi = 0, r = 0; r < 0.5; phi += 0.1, r += 0.002)
    {
      u = cos (phi) * (r + offset) + 0.5;
      v = sin (phi) * (r + offset)+ 0.5;
      offset = -offset;
      evaluate_bezier_point (model->objects, u, v, &x, &y);
      if (phi < 0.05)
	cairo_move_to (cr, x, y);
      else
	cairo_line_to (cr, x, y);
    }

  cairo_stroke (cr);

  end_paint (cr);
}

static gboolean
scribble_expose_event (GtkWidget      *widget,
		       GdkEventExpose *event,
		       gpointer	       data)
{
  Model *model = data;
  int i;

#if 0
  draw_spline_spiral (widget, model);
#else
  draw_spline_grid (widget, model);
#endif

  draw_ball (widget, model->current_attractor->position.x,
	     model->current_attractor->position.y, &red);

#if 1
  for (i = 0; i < model->num_objects; i++) {
    draw_star (widget, model->objects[i].position.x,
	       model->objects[i].position.y, model->objects[i].theta, &blue);
  }
#endif
  
  return TRUE;
}

static gboolean
scribble_button_press_event (GtkWidget	    *widget,
			     GdkEventButton *event,
			     gpointer	     data)
{
  Model *model = data;
  
  if (event->button == 1)
    model_move_attractor (model, event->x, event->y);

  return TRUE;
}

static gboolean
scribble_motion_notify_event (GtkWidget	     *widget,
			      GdkEventMotion *event,
			      gpointer	      data)
{
  Model *model = data;
  int x, y;
  GdkModifierType state;

  gdk_window_get_pointer (event->window, &x, &y, &state);
    
  if (state & GDK_BUTTON1_MASK)
    model_move_attractor (model, x, y);

  return TRUE;
}

static void
spring_constant_changed (GtkSpinButton *spinbutton, gpointer user_data)
{
  Model *model = user_data;

  model->k = gtk_spin_button_get_value (spinbutton);
}

static void
friction_changed (GtkSpinButton *spinbutton, gpointer user_data)
{
  Model *model = user_data;

  model->friction = gtk_spin_button_get_value (spinbutton);
}

GtkWidget *
create_spinners (Model *model)
{
  GtkWidget *hbox;
  GtkWidget *spinner, *label;

  hbox = gtk_hbox_new (FALSE, 8);

  label = gtk_label_new_with_mnemonic ("_Spring constant:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  spinner = gtk_spin_button_new_with_range  (0.05, 15.00, 0.05);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
  gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), model->k);
  g_signal_connect (spinner, "value-changed",
		    G_CALLBACK (spring_constant_changed), model);

  label = gtk_label_new_with_mnemonic ("_Friction:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  spinner = gtk_spin_button_new_with_range  (0.05, 15.00, 0.05);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
  gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), model->friction);
  g_signal_connect (spinner, "value-changed",
		    G_CALLBACK (friction_changed), model);

  return hbox;
}

GtkWidget *
create_window (Model *model)
{
  GtkWidget *window;
  GtkWidget *frame;
  GtkWidget *vbox;
  GtkWidget *da;
  GtkWidget *spinners;

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");

  g_signal_connect (window, "destroy",
		    G_CALLBACK (gtk_main_quit), &window);

  gtk_container_set_border_width (GTK_CONTAINER (window), 8);

  vbox = gtk_vbox_new (FALSE, 8);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  /*
   * Create the scribble area
   */
      
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
      
  da = gtk_drawing_area_new ();
  /* set a minimum size */
  gtk_widget_set_size_request (da, 400, 300);

  gtk_container_add (GTK_CONTAINER (frame), da);

  /* Signals used to handle backing pixmap */
      
  g_signal_connect (da, "expose_event",
		    G_CALLBACK (scribble_expose_event), model);
      
  /* Event signals */
      
  g_signal_connect (da, "motion_notify_event",
		    G_CALLBACK (scribble_motion_notify_event), model);
  g_signal_connect (da, "button_press_event",
		    G_CALLBACK (scribble_button_press_event), model);


  /* Ask to receive events the drawing area doesn't normally
   * subscribe to
   */
  gtk_widget_set_events (da, gtk_widget_get_events (da)
			 | GDK_LEAVE_NOTIFY_MASK
			 | GDK_BUTTON_PRESS_MASK
			 | GDK_POINTER_MOTION_MASK
			 | GDK_POINTER_MOTION_HINT_MASK);

  spinners = create_spinners (model);
  gtk_box_pack_start (GTK_BOX (vbox), spinners, FALSE, FALSE, 0);

  return da;
}

typedef struct _Closure Closure;
struct _Closure {
  GtkWidget *drawing_area;
  Model *model;
  int i;
};

static gint
timeout_callback (gpointer data)
{
  Closure *closure = data;

  model_step (closure->model);
  closure->i++;
  if (closure->i == 1) {
    gtk_widget_queue_draw (closure->drawing_area);
    closure->i = 0;
  }

  return TRUE;
}

int
main (int argc, char *argv[])
{
  Closure closure;
  Model model;

  gtk_init (&argc, &argv);
  model_init (&model);
  closure.drawing_area = create_window (&model);
  closure.i = 0;
  gtk_widget_show_all (gtk_widget_get_toplevel (closure.drawing_area));
  closure.model = &model;
  g_timeout_add (10, timeout_callback, &closure);
  gtk_main ();

  return 0;
}




More information about the cairo-commit mailing list