Esempio n. 1
0
static void set_and_post_mask(struct MTouch *mt, DeviceIntPtr dev,
                              mstime_t delta_t){
	struct Gestures* gs;
	ValuatorMask* mask;
	float speed_factor;

	gs = &mt->gs;
	mask = mt->vm;

	valuator_mask_zero(mask);

	if (gs->move_dx)
		valuator_mask_set_double(mask, 0, gs->move_dx);
	if (gs->move_dy)
		valuator_mask_set_double(mask, 1, gs->move_dy);

	switch (gs->move_type){
	case GS_SCROLL:
	case GS_SWIPE3:
	case GS_SWIPE4:
	//case GS_MOVE:
	case GS_NONE:
		/* Only scroll, or swipe or move can trigger coasting right now */
		/* Continue coasting if enabled */
		speed_factor = (mt->cfg.scroll_coast.num_of_ticks - gs->scroll_coast_tick_no) / (float)(mt->cfg.scroll_coast.num_of_ticks);
		if (ABSVAL(gs->scroll_speed_x) > mt->cfg.scroll_coast.min_speed)
			valuator_mask_set_double(mask, 2, gs->scroll_speed_x * delta_t * speed_factor);
		if (ABSVAL(gs->scroll_speed_y) > mt->cfg.scroll_coast.min_speed)
			valuator_mask_set_double(mask, 3, gs->scroll_speed_y * delta_t * speed_factor);

		break;

	default:
		/* Any other movement/gesture type will break coasting. */
		mt->gs.scroll_speed_y = mt->gs.scroll_speed_x = 0.0f;
		break;
	}

	xf86PostMotionEventM(dev, Relative, mask);

	/* Once posted, we can clear the move variables */
	gs->move_dx = gs->move_dy = 0;
}
Esempio n. 2
0
/*
 * This must be FAST!
 *
 * new_line   is a boolean that tells us if the last time this was
 *            called the line terminated, and we need to start a
 *            new line. This must be initialized to 1 on the first call.
 *
 *  Quickplot must cull when drawing lines, after zooming,
 *  so that we do not try to draw lines "out of bounds".
 *  Like for example, after zooming we may have points that
 *  are way out side the drawing area.  Drawing lines with
 *  end points outside the drawing area will/may give undefined
 *  results.  We have seen examples without culling where
 *  the lines look like they are drawn from random end points,
 *  when that was clearly not what we wanted to see.  Even
 *  though cario uses doubles it does not map end points of
 *  lines that are outside the drawing area to edges where
 *  we would expect to see them, at least not all the time.
 *
 *  Besides that, it turns out that culling out lines
 *  is faster than trying to draw them.  Which is what
 *  you would expect.
 *
 *  In this function, we find the points on the drawing area
 *  edges where the line would intersect and draw lines "in
 *  bounds" to represent lines that would otherwise be drawn
 *  off the drawing area.
 */
static
inline
void CullDrawLine(struct qp_graph *gr,
    
    int *new_line, /* if (*new_line) then we start a
                    * new line, else we continue the last
                    * line */ 

    /* We treat the drawing area (x0,y0) to (x1,y1) like it is
     * (minusLWidthP1,minusLWidthP1) to (widthPlus,heightPlus)
     * though the real drawing area is smaller we use a larger
     * area in order to catch edges of thick lines that may
     * pass through the edge of the real drawing area.
     */
    double minusLWidthP1, double widthPlus, double heightPlus,

    /* fromX, fromY and toX, toY are in pixels that define
     * a line that we wish to draw. But if they are
     * not in the drawing region we must find the line that
     * they would make though the drawing region by finding
     * side intersection points.  You can not draw lines from
     * points like (x,y) 10, -1e10 to 10, 1e10 but we can find
     * edge intersections of a line that we can draw from. 
     * The number 1e10 is to large to be used to draw lines
     * with. */
    double fromX, double fromY,
    double toX, double toY)
{
  if(!is_good_double(fromX) || !is_good_double(fromY) ||
      !is_good_double(toX) || !is_good_double(toY))
  {
    /* We need to do this uncommon case first because bad
     * values can not be used well in the code that follows. */
    *new_line = 1;
    return;
  }

  
  // The common cases come first.
  
  // If both points are in the drawing area
  if(fromX > minusLWidthP1 && fromX < widthPlus &&
     toX > minusLWidthP1 && toX < widthPlus &&
     fromY > minusLWidthP1 && fromY < heightPlus &&
     toY > minusLWidthP1 && toY < heightPlus)
    {
      gr->DrawLine(gr, new_line, fromX, fromY, toX, toY);
      return;
    }

  
  // Do some quick Culls for both points way off to one side: right,
  // left, up, or down.  This will Cull MOST points out!  Speed is
  // the result.

  //points all on right side,
  if((fromX >= widthPlus && toX >= widthPlus) ||
     //  on the left side,
     (fromX <= minusLWidthP1 && toX <= minusLWidthP1) ||
     //  on top (up),
     (fromY <= minusLWidthP1 && toY <= minusLWidthP1) ||
     // or on bottom (down)
     (fromY >= heightPlus && toY >= heightPlus))
  {
    /* we start over the next line draw */
    *new_line = 1;
    return; // Culled
  }
  

  // now both points are not in the drawing area (maybe one point is).
  // Below we refer to the line formed by the two points fromX, fromY and
  // toX, toY as "line" or "the line" ...
  

  /* close to horizontal lines are more common than close
   * to vertical lines */


  if(ABSVAL(toX - fromX) > 0.01)
  {
    double m; /* slope of the line */
    m = (toY - fromY)/(toX - fromX);

    if(ABSVAL(m) < 1.0e+5)
    {
      /* The slope is not large */
      double a;
      a = fromY - m * fromX;
      
      /* the equation for the line is:  Y = m * X + a  */

      if(fromX < minusLWidthP1)
      {
        fromX = minusLWidthP1;
        fromY = m * fromX + a;
      }
      else if(fromX > widthPlus)
      {
        fromX = widthPlus;
        fromY = m * fromX + a;
      }

      if(toX < minusLWidthP1)
      {
        toX = minusLWidthP1;
        toY = m * toX + a;
      }
      else if(toX > widthPlus)
      {
        toX = widthPlus;
        toY = m * toX + a;
      }

      if((fromY < minusLWidthP1 && toY < minusLWidthP1)
          ||
        (fromY > heightPlus && toY > heightPlus))
      {
        /* The line is above or below the drawing area */
        *new_line = 1;
        return; // Culled
      }
  
      if(toY < minusLWidthP1)
      {
        toY = minusLWidthP1;
        toX = (toY - a)/m;
      }
      else if(toY > heightPlus)
      {
        toY = heightPlus;
        toX = (toY - a)/m;
      }

      if(fromY < minusLWidthP1)
      {
        fromY = minusLWidthP1;
        fromX = (fromY - a)/m;
      }
      else if(fromY > heightPlus)
      {
        fromY = heightPlus;
        fromX = (fromY - a)/m;
      }

      gr->DrawLine(gr, new_line, fromX, fromY, toX, toY);
      return;
    }
  }


  /* Now we do the more vertical line case */

  if(ABSVAL(toY - fromY) > 0.01)
  {
    double u; /* inverse slope of the line */
    u = (toX - fromX)/(toY - fromY);

    if(ABSVAL(u) < 1.0e5)
    {
      /* The inverse slope is not too large */
      double b;
      b = fromX - u * fromY;
      
      /* the equation for the line is:  X = u * Y + b  */

      if(fromY < minusLWidthP1)
      {
        fromY = minusLWidthP1;
        fromX = u * fromY + b;
      }
      else if(fromY > heightPlus)
      {
        fromY = heightPlus;
        fromX = u * fromY + b;
      }
      
      if(toY < minusLWidthP1)
      {
        toY = minusLWidthP1;
        toX = u * toY + b;
      }
      else if(toY > heightPlus)
      {  
        toY = heightPlus;
        toX = u * toY + b;
      }

      if((fromX < minusLWidthP1 && toX < minusLWidthP1)
          ||
        (fromX > widthPlus && toX > widthPlus))
      {
        /* The line is to the left or right of the drawing area */
        *new_line = 1;
        return; // Culled
      }
       
      /* The line goes through the drawing area */
  
      if(toX < minusLWidthP1)
      {
        toX = minusLWidthP1;
        toY = (toX - b)/u;
      }
      else if(toX > widthPlus)
      {
        toX = widthPlus;
        toY = (toX - b)/u;
      }

      if(fromX < minusLWidthP1)
      {
        fromX = minusLWidthP1;
        fromY = (fromX - b)/u;
      }
      else if(fromX > widthPlus)
      {
        fromX = widthPlus;
        fromY = (fromX - b)/u;
      }

      gr->DrawLine(gr, new_line, fromX, fromY, toX, toY);
      return;
    }
  }

  /* The points are close to each other and not in the
   * drawing area */

  *new_line = 1;
  // Culled
}
Esempio n. 3
0
static inline
void DrawYGrid(cairo_t *cr, PangoLayout *pangolayout,
        struct qp_graph *gr, struct qp_plot *z,
	int64_t xmin_mat, int64_t xmax_mat,
	int64_t xinc, double xpow_part,
	int64_t ymin_mat, int64_t ymax_mat,
	int64_t yinc, double ypow_part,
        int width, int height, int view_width)
{
  /******************************************************/
  /* find where to put the value text for y lines       */
  /******************************************************/

  int64_t xLabel_start, xLabel_inc, xLabel_max;
  gint x_ylinetext;
  int64_t i, max;
  char FMT[FORMAT_LEN];


  x_ylinetext = qp_plot_get_xpixel(z, (xmin_mat + xinc*0.2)*xpow_part);
  if(x_ylinetext < 3)
    x_ylinetext = qp_plot_get_xpixel(z, (xmin_mat + xinc*1.2)*xpow_part);


  if(gr->grid_numbers)
    {
      /* We can get better X position precision if we
       * increase the range of the numbers in X. */

      xmin_mat *= 100;
      xmax_mat *= 100;
      xinc *= 100;
      xpow_part /= 100;

      if(gr->same_x_scale)
	{
	  xLabel_inc = (qp_plot_get_xval(z, view_width) -
			qp_plot_get_xval(z, 0))/
	    xpow_part;

	  // make xLabel_inc a multiple of xinc
          if(xLabel_inc < 14*xinc/10)
            xLabel_inc = xinc;
          else if(xLabel_inc < 24*xinc/10)
	    xLabel_inc = 2*xinc;
          else if(xLabel_inc < 34*xinc/10)
            xLabel_inc = 3*xinc;
          else
            xLabel_inc -= xLabel_inc%xinc;

	  xLabel_start = qp_plot_get_xval(z, gr->pixbuf_x
		       - 14 /* padding from grid line */)/
	    xpow_part;

	  xLabel_start -=
	    ((int64_t)(xLabel_start - xmin_mat))%xinc;

	  if(qp_plot_get_xpixel(z, xLabel_start*xpow_part) <
	     gr->pixbuf_x - 14 /* padding */)
	    xLabel_start += xinc;

	  xLabel_start += - ((int64_t)(xLabel_start - xmin_mat)) +
	    ((int64_t)(xLabel_start - xmin_mat))%xLabel_inc;

	  xLabel_max = xmax_mat;
	  xLabel_max -= ((xLabel_max - xLabel_start)%xLabel_inc);
	}
      else
	{
	  xLabel_inc = (
              qp_plot_get_xval(z, view_width) -
              qp_plot_get_xval(z, 0)
              )/xpow_part;
	  
	  xLabel_start = qp_plot_get_xval(z, gr->pixbuf_x)/xpow_part;
	  
	  xLabel_start += - ((int64_t)(xLabel_start - xmin_mat)) +
	    ((int64_t)(xLabel_start - xmin_mat))%xLabel_inc;
	  
	  xLabel_max = xmax_mat;
	  xLabel_max -= ((xLabel_max - xLabel_start)%xLabel_inc);
	}
    }


  max = ABSVAL(ymin_mat);
  if(ABSVAL(ymax_mat) > max)
    max = ABSVAL(ymax_mat);


  if(max/yinc > 10000) {
    strcpy(FMT, H_GRID_PRINT_FORMAT);
    //printf("y high res\n");
  }  else
    strcpy(FMT, L_GRID_PRINT_FORMAT);


  /****************************************************
   *     draw y labels 
   ****************************************************/
  if(gr->grid_numbers)
  {
    cairo_set_source_rgba(cr, gr->grid_text_color.r,
              gr->grid_text_color.g, gr->grid_text_color.b,
              gr->grid_text_color.a);

    for(i=ymin_mat; i <= ymax_mat; i += yinc)
    {
      char str[64];
      int y;
      int64_t j;

      snprintf(str,64, FMT, i*ypow_part);

      //snprintf(str,64, "%*.*g", digit_count,digit_count, i*ypow_part);
      //printf(GRID_PRINT_FORMAT" ", str);

      y = qp_plot_get_ypixel(z, i*ypow_part);

      for(j = xLabel_start; j <= xLabel_max; j += xLabel_inc)
      {
        cairo_translate(cr,
            qp_plot_get_xpixel(z, j*xpow_part) + gr->grid_line_width/2 + 10,
            y+ gr->grid_line_width/2);
        pango_layout_set_text(pangolayout, str, -1);
        pango_cairo_update_layout(cr, pangolayout);
        pango_cairo_show_layout(cr, pangolayout);
        /* clear the translation */
        cairo_identity_matrix(cr);
      }
    }
  }
  /****************************************************
   *     draw horizontal lines at y values 
   ****************************************************/
  cairo_set_source_rgba(cr, gr->grid_line_color.r,
              gr->grid_line_color.g, gr->grid_line_color.b,
              gr->grid_line_color.a);
  cairo_set_line_width(cr, gr->grid_line_width);

  for(i=ymin_mat; i <= ymax_mat; i += yinc)
  {
    int y;
    y = qp_plot_get_ypixel(z, i*ypow_part); // - gr->grid_line_width;
    cairo_move_to(cr, 0, y);
    cairo_line_to(cr, width, y);
  }
  /* draw the lines */
  cairo_stroke(cr);
}
Esempio n. 4
0
/* This is not too trival.  Number round-off must be avoided.  We
 * require the grid lines be drawn with the same method that the plot
 * points plot lines, so that they are not off by one pixel.  We must
 * not have plots that miss-lead the user in any way. */
static
void PreDrawGrid(struct qp_graph *gr, struct qp_plot *z, 

			int64_t *xmin_mat, int64_t *xmax_mat,
			int64_t *xinc, double *xpow_part,

			int64_t *ymin_mat, int64_t *ymax_mat,
			int64_t *yinc, double *ypow_part,
                        int width, int height)
{
  double xmin = qp_plot_get_xval(z, 0);
  double xmax = qp_plot_get_xval(z, width);
  double ymax = qp_plot_get_yval(z, 0);
  double ymin = qp_plot_get_yval(z, height);

  /***************************************************/
  /*        get the x value lines calculated         */
  /***************************************************/

  //DEBUG("pix width=%d  values xmin=%g xmax=%g\n", width, xmin, xmax);
  //DEBUG("pix height=%d values ymin=%g ymax=%g\n", height, ymin, ymax);


  double delta;
  /* change in x values between vertical grid lines */
  delta = (xmax - xmin)/(2 + ((double) width/gr->grid_x_space));

  //printf("xdelta=%g  gr->grid_x_space=%d\n", delta,
  //gr->grid_x_space);

  gint delta_p = (gint) log10(ABSVAL(delta) > SMALL_NUM ?
			      ABSVAL(delta) : SMALL_NUM);

  if(delta_p <= 0) delta_p--;

 
  *xpow_part = POW(TEN, delta_p);

  //printf("xdelta_p=%d  xpow_part=%g\n", delta_p, *xpow_part);

  {
    // strip off the round-off error off of xpow_part.
    char s[16];
    //for example: xpow_part=0.20005465 --> 
    //               s=0.20 --> xpow_part=0.2000000
    sprintf(s, TWO_DIGIT_FORMAT, *xpow_part);
    sscanf(s, SCAN_FORMAT, xpow_part);
    //printf("s=%s  xpow_part=%.15g\n",s, xpow_part);
  }

  *xmin_mat = (int64_t) (xmin/(*xpow_part));
  *xmax_mat = (int64_t) (xmax/(*xpow_part));

  //printf(" xmin = %ldx10%+d xmax = %ldx10%+d\n",
  //	 *xmin_mat, delta_p, *xmax_mat, delta_p);

  if(width > gr->grid_x_space)
    *xinc = (int64_t) ((*xmax_mat - *xmin_mat)/
		      ((double) width/
		       gr->grid_x_space));
  else
    *xinc = *xmax_mat - *xmin_mat;

  //printf("               xinc=%ld\n", *xinc);

  // round to 1, 2, 5, times 10^N
  if     (*xinc < (int64_t) 2)   *xinc = 1;
  else if(*xinc < (int64_t) 5)   *xinc = 2;
  else if(*xinc < (int64_t) 10)  *xinc = 5;
  else if(*xinc < (int64_t) 20)  *xinc = 10;
  else if(*xinc < (int64_t) 50)  *xinc = 20;
  else if(*xinc < (int64_t) 100) *xinc = 50;
  else if(*xinc < (int64_t) 200) *xinc = 100;
  else                           *xinc = 200;


  //DEBUG("xinc=%ld xmin_mat=%ld\n", *xinc, *xmin_mat);

  if(*xmin_mat > (int64_t) 0 && (*xmin_mat)%(*xinc))
    *xmin_mat -= (*xmin_mat)%(*xinc);
  else if(*xmin_mat < (int64_t) 0 && (*xmin_mat)%(*xinc))
    *xmin_mat -= *xinc + (*xmin_mat)%(*xinc);


  //printf("               xinc=%ld\n", *xinc);
  
  /***************************************************/
  /*        get the y value lines calculated         */
  /***************************************************/

  delta = (ymax - ymin)/(2 + ((double) height/
			      gr->grid_y_space));
 
  delta_p = (gint) log10(ABSVAL(delta) > SMALL_NUM ?
			 ABSVAL(delta) : SMALL_NUM);

  if(delta_p <= 0) delta_p--;

  //printf("ydelta_p=%d\n", delta_p);

  *ypow_part = POW(TEN, delta_p);
  {
    // strip off the round-off error off of ypow_part.
    char s[16];
    //for example: ypow_part=0.20005465 -->
    //            s=0.20 --> ypow_part=0.2000000
    sprintf(s, TWO_DIGIT_FORMAT, *ypow_part);
    sscanf(s, SCAN_FORMAT, ypow_part);
  }

  *ymin_mat = (int64_t) (ymin/(*ypow_part));
  *ymax_mat = (int64_t) (ymax/(*ypow_part));

  //printf(" ymin = %ldx10%+d ymax = %ldx10%+d\n",
  //  *ymin_mat, delta_p, *ymax_mat, delta_p);

  if(height > gr->grid_y_space)
    *yinc = (int64_t) ((*ymax_mat - *ymin_mat)/
		      ((double) height/
		       gr->grid_y_space));
  else
    *yinc = *ymax_mat - *ymin_mat;

  if     (*yinc < (int64_t) 2)   *yinc = 1;
  else if(*yinc < (int64_t) 5)   *yinc = 2;
  else if(*yinc < (int64_t) 10)  *yinc = 5;
  else if(*yinc < (int64_t) 20)  *yinc = 10;
  else if(*yinc < (int64_t) 50)  *yinc = 20;
  else if(*yinc < (int64_t) 100) *yinc = 50;
  else if(*yinc < (int64_t) 200) *yinc = 100;
  else                           *yinc = 200;


  //printf("yinc=%ld\n", *yinc);

  if(*ymin_mat > (int64_t) 0 && (*ymin_mat)%(*yinc))
    *ymin_mat -= (*ymin_mat)%(*yinc);
  else if(*ymin_mat < (int64_t) 0 && (*ymin_mat)%(*yinc))
    *ymin_mat -= *yinc + (*ymin_mat)%(*yinc);


  
}
Esempio n. 5
0
static inline
void DrawXGrid(cairo_t *cr, PangoLayout *pangolayout,
        struct qp_graph *gr, struct qp_plot *z,
	int64_t xmin_mat, int64_t xmax_mat,
	int64_t xinc, double xpow_part,
	int64_t ymin_mat, int64_t ymax_mat,
	int64_t yinc, double ypow_part,
        int width, int height, int view_height)
{
  /******************************************************/
  /* find where to put the value text for x lines       */
  /******************************************************/

  int64_t yLabel_start, yLabel_inc, yLabel_max;
  int64_t i, max;
  char FMT[FORMAT_LEN];
  
  if(gr->grid_numbers)
    {
      /* We can get better Y position precision if we
       * increase the range of the numbers in Y. */ 
      yinc *= 100;
      ymin_mat *= 100;
      ymax_mat *= 100;
      ypow_part /= 100;

      if(gr->same_y_scale)
	{
	  yLabel_inc = (qp_plot_get_yval(z, 0) -
			qp_plot_get_yval(z, view_height))/
	    ypow_part;

	  // make yLabel_inc a multiple of yinc
          if(yLabel_inc < 14*yinc/10)
            yLabel_inc = yinc;
          else if(yLabel_inc < 24*yinc/10)
	    yLabel_inc = 2*yinc;
          else if(yLabel_inc < 34*yinc/10)
            yLabel_inc = 3*yinc;
          else
            yLabel_inc -= yLabel_inc%yinc;
     

	  yLabel_start = qp_plot_get_yval(z, view_height - 15 +
				      gr->pixbuf_y)/
	    ypow_part;


	  yLabel_start -=
	    ((int64_t)(yLabel_start - ymin_mat + yinc*0.5))%yinc;

	  if(qp_plot_get_ypixel(z, yLabel_start*ypow_part) >
	     view_height + gr->pixbuf_y
	     - 10/* font height */
	     - 3 /* padding */)
	    yLabel_start += yinc;

	  yLabel_start += - ((int64_t)(yLabel_start -
				       ymin_mat + yinc*0.5)) +
	    ((int64_t)(yLabel_start - ymin_mat + yinc*0.5))%yLabel_inc;

	  yLabel_max = ymax_mat - yinc*0.5;
	  yLabel_max -= ((yLabel_max - yLabel_start)% yLabel_inc);
	}
      else
	{
	  yLabel_inc = (qp_plot_get_yval(z,0) -
			qp_plot_get_yval(z,view_height))/
	    ypow_part;

	   yLabel_start = qp_plot_get_yval(z,view_height - 15 +
				    gr->pixbuf_y)/
	     ypow_part;

	   yLabel_start += - ((int64_t)(yLabel_start - ymin_mat)) +
	     ((int64_t)(yLabel_start - ymin_mat))%yLabel_inc;

	   yLabel_max = ymax_mat;
	   yLabel_max -= ((yLabel_max - yLabel_start)% yLabel_inc);
	}
    }


  max = ABSVAL(xmin_mat);
  if(ABSVAL(xmax_mat) > max)
    max = ABSVAL(xmax_mat);


  if(max/xinc > 10000) {
    strcpy(FMT, H_GRID_PRINT_FORMAT);
    //printf("x high res\n");
  }
  else
    strcpy(FMT, L_GRID_PRINT_FORMAT);


  /****************************************************
   *     draw x labels 
   ****************************************************/
  if(gr->grid_numbers)
  {
    cairo_set_source_rgba(cr, gr->grid_text_color.r,
              gr->grid_text_color.g, gr->grid_text_color.b,
              gr->grid_text_color.a);

    for(i=xmin_mat; i <= xmax_mat; i += xinc)
    {
      char str[64];
      int x;
      int64_t j;
      
      snprintf(str,64, FMT, i*xpow_part);

      //printf(" xpow_part=%.15g\n",xpow_part);
      //printf(GRID_PRINT_FORMAT" ", i*xpow_part);

      x = qp_plot_get_xpixel(z, i*xpow_part);
        
      for(j = yLabel_start; j <= yLabel_max; j += yLabel_inc)
      {
        cairo_translate(cr, x+3+ gr->grid_line_width/2,
	    qp_plot_get_ypixel(z, j*ypow_part)
	    - 5 /* 1/2 font height */);
        pango_layout_set_text(pangolayout, str, -1);

        pango_cairo_update_layout(cr, pangolayout);
        pango_cairo_show_layout(cr, pangolayout);

        /* clear the translation */
        cairo_identity_matrix(cr);
            
	//printf("%g\n", j*ypow_part);
      }
    }
  }

  /****************************************************
   *     draw vertical lines at x values 
   ****************************************************/
  cairo_set_source_rgba(cr, gr->grid_line_color.r,
              gr->grid_line_color.g, gr->grid_line_color.b,
              gr->grid_line_color.a);
  cairo_set_line_width(cr, gr->grid_line_width);

  for(i=xmin_mat; i <= xmax_mat; i += xinc)
  {
    int x;
    x = qp_plot_get_xpixel(z, i*xpow_part); // gr->grid_line_width;
    cairo_move_to(cr, x, 0);
    cairo_line_to(cr, x, height);
  }
  /* draw the lines */
  cairo_stroke(cr);
}