Re: Coordinate from device to user space without a context

Donn <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
On 07/11/2013 15:48, ed44 wrote:
>> So, x = [18,53,17,51] See the jumps?
>
> Yes , i see.
> But if you use the above formula and the matrix values are constant
> there should not be any jumps.

I'm not seeing the bug.
1. The matrix does change because it has to. It comes from a 
translate(x,y) [line 120], then gets saved into mymatrix [line 126],
2. then the move() method causes x,y to shift to mousex,y [called from 
line 39. The method at line 58]
3. Then a queue_draw() happens [line 420]
4. In draw(), a call to set_matrix(cr) happens [e.g. line 305],
5. which is back to 1 were the latest x,y is used.

Look, both you and Bryce tell me that formula should work as expected, 
so I'm happy to think there's a bug in my code. I just gotta find it!

Thanks for your time,
\d
(code attached this time.)

-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
matrix_maillist_debug.vala (text/x-vala, 10.9 KB)
//valac --pkg gee-1.0 --pkg gtk+-3.0 -X -lm -X -w "$1" -o test
using Gtk;
using Cairo;

public void grid(Cairo.Context cr, bool white=false) {
    cr.move_to(0,0);
    double m=0f;
    double w=0f;
    double x=0f;
    //for (x=10f; x <= 210f; x+=10f) {
    for (x=0f; x <= 500f; x+=10f) {
      m++;
      w = 10 + ( Math.fmod(m,2) * 10); //fluctuating height
      cr.rectangle (x,-w/2,1,w); //right arm
      cr.rectangle (-x,-w/2,1,w); //left arm
      cr.rectangle (-w/2,-x,w,1); //down arm
      cr.rectangle (-w/2,x,w,1); //up arm
    }
    if (white) {
      cr.set_source_rgb(1,1,1);
    } else {
      cr.set_source_rgba(0,0,0,0.2);
    }
    cr.fill();
}

public double c(int col) { return (double)col / 256.0; }

public class Dot: Object {
  public double x = 0;
  public double y = 0;
  public double sx = 1;
  public double sy = 1;
  public double rot = 0;
  public double radius = 8;
  public double r=0;
  public double g=0;
  public double b=0;
  public string name;
  public double device_x=0; 
  public double device_y=0; 
  
  //Device-space bounding box coords
  public double dbbx1;
  public double dbbx2;
  public double dbby1;
  public double dbby2;

  public bool is_hit = false;

  private Cairo.Matrix? mymatrix;


  public Dot(string n) {
    name = n;
  }
  
  public void move(double mx, double my) {

    //double xnew = mymatrix.xx * mx + mymatrix.xy * my + mymatrix.x0;
    //double ynew = mymatrix.yx * my + mymatrix.yy * my + mymatrix.y0;
    
    //double xnew = mymatrix.xx * mx + mymatrix.x0;
    //double ynew = mymatrix.yy * my + mymatrix.y0;
    
    // x = (x_new - x0)/xx
    // y = (y_new - y0)/yy
    // where - i think - x_new is mouse x
    stdout.printf("mymatrix:  %g|%g|%g|%g|%g|%g\n",mymatrix.xx ,mymatrix.yx ,mymatrix.xy ,mymatrix.yy ,mymatrix.x0 ,mymatrix.y0 );
    x = (mx - mymatrix.x0)/mymatrix.xx;
    y = (my - mymatrix.y0)/mymatrix.yy;

    stdout.printf("mx my: %G|%G becomes x y: %G|%G\n",mx,my, x, y);
   
    //x=xnew; y=ynew;
    

    //
    /*
    double px = mx; double py=my;
    stdout.printf("BEFORE mx my: %G | %G\n", px, py);
    mymatrix.transform_point(ref px, ref py);
    stdout.printf("AFTER mx my: %G | %G\n", px, py);
    
    //Set my obj vars for a later draw()
    x=px; y=py;
    */
    /*
    stdout.printf("mymatrix before: %g|%g|%g|%g|%g|%g\n",mymatrix.xx ,mymatrix.yx ,mymatrix.xy ,mymatrix.yy ,mymatrix.x0 ,mymatrix.y0 );
    var tm = mymatrix; //copies on assignment because tm is a struct.
    tm.invert();
    stdout.printf("tm after invert: %g|%g|%g|%g|%g|%g\n",tm.xx ,tm.yx ,tm.xy ,tm.yy ,tm.x0 ,tm.y0 );
    double px = mx; double py=my;
    stdout.printf("BEFORE mx my: %G | %G\n", px, py);
    tm.transform_point(ref px, ref py);
    stdout.printf("AFTER mx my: %G | %G\n", px, py);
    x=px;y=py;
    stdout.printf("mymatrix after: %g|%g|%g|%g|%g|%g\n",mymatrix.xx ,mymatrix.yx ,mymatrix.xy ,mymatrix.yy ,mymatrix.x0 ,mymatrix.y0 );
    */
  }

  public bool bbox_hit( double mx, double my) {
    is_hit = (
      (mx > dbbx1) && 
      (mx < dbbx2) && 
      (my > dbby1) && 
      (my < dbby2)
    );
    return is_hit;
  }

  public void set_matrix(Cairo.Context cr) {

    //Super NB that we store the u2d coords BEFORE the matrix stuff
    device_x = x; 
    device_y = y; //prep in-vars
    cr.user_to_device(ref device_x, ref device_y); //stores into refs

    //NOTE - goes after the above code.
    cr.translate(x,y);
    cr.scale(sx,sy);
    cr.rotate((Math.PI/180.0)*rot);

        cr.save();
          //cr.identity_matrix();
          mymatrix = cr.get_matrix();
        cr.restore();

  }

  private void _record_bbox(Cairo.Context cr) {
    cr.save();
    cr.identity_matrix();
    cr.path_extents (out dbbx1, out dbby1, out dbbx2, out dbby2);
    cr.restore();
  }

  public void draw_geom(Cairo.Context cr) {
      cr.arc (20 ,20, radius, 0, 2 * Math.PI);
      if (is_hit) {
        cr.set_source_rgb (1, 0, 0);
      } else {
        cr.set_source_rgb (r, g, b);
      }
      cr.rectangle(-radius+20,-radius+20,radius,radius);

      //Get myy bbox:
      _record_bbox(cr);
  }

  public void draw(Cairo.Context cr) {

      //random grid
      _grid(cr);

      draw_geom(cr);
      cr.fill ();

      //random label.
      cr.move_to(radius+5,0);
      cr.save(); //isolate the text from the current rotation and scale?
        cr.identity_matrix();
        cr.select_font_face ("Adventure", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
        cr.show_text(@"d$(this.name) u($x,$y) d($device_x,$device_y)");
      cr.restore();

  }

  public void draw_dbb(Cairo.Context cr) {
    cr.save();
    cr.identity_matrix();

    cr.rectangle(dbbx1,dbby1,(dbbx2-dbbx1),(dbby2-dbby1));
    cr.set_line_width(2);
    cr.set_source_rgb(1,1,1);
    cr.stroke();
    cr.restore();
  }


  private void _grid(Cairo.Context cr) {
    grid(cr);
  }

}

/*-------------------------------------------------*/
public class SceneGraphWidget : Gtk.DrawingArea {

  public Dot d1;
  public Dot d2;
  public Dot d3;
  public List<Dot> dlist;


  public const uint flag_button_press = 1<<0;
  public const uint flag_button_release = 1<<1;
  public const uint flag_motion_notify = 1<<2;
  public const uint flag_in_drag = 1<<3;
  
  public const uint pattern_click = flag_button_press | flag_button_release;
  public const uint pattern_drag_start = flag_button_press | flag_motion_notify;
  public const uint pattern_in_drag = pattern_drag_start | flag_in_drag;
  public const uint pattern_drop = flag_button_press | flag_in_drag | flag_button_release;


  private uint bitstate;

  private Dot? dotBeingDragged;
  private Dot? overDot;

  construct {


    d1 = new Dot("1");
    d2 = new Dot("2");
    d3 = new Dot("3");

    d1.r=0.02; d1.g=0.5; d1.b=0.02; d1.x=55; d1.y=55;

    d2.r=0; d2.g=0; d2.b=0; d2.x=17; d2.y=-112; 
    d2.rot=0; 
    d2.radius = 40; 
    d2.sx=0.5; d2.sy=0.5;

    d3.r=0; d3.g=0.7; d3.b=0.7; d3.radius=20;
    d3.x=-50; d3.y=0;
    
    dlist = new List<Dot>();
    dlist.append(d1);
    dlist.append(d2);
    dlist.append(d3);

    this.set_double_buffered (true);

    this.set_size_request (500,500);
    this.can_focus = true; //In order to receive keybd signals

    this.add_events (
    Gdk.EventMask.BUTTON_PRESS_MASK |
    Gdk.EventMask.BUTTON_RELEASE_MASK |
    Gdk.EventMask.POINTER_MOTION_MASK
    );

    event.connect(handle_events); //!!! Check it out!
  }

  private void initmatrix ( Cairo.Context cr ) {
	/*
     Scale to window's size and shift the origin of the page to the center.
		  -y | -y
		  -x | +x
		 ----0------
		  -x | +x
		  +y | +y
		*/

    int w,h;
    double pw, ph;

    h = get_allocated_height ();
    w = get_allocated_width ();

    pw = (float) w / 500f; 
    ph = (float) h / 500f;

		var matrix = Cairo.Matrix (1, 0, 0, 1, w/2,h/2 ); //translate to middle.
		
    matrix.scale (pw, ph); //Scale to the window's size.
		cr.transform(matrix);
  }

  /* Override of the draw() callback, GTK3 requires this. */
  public override bool draw ( Cairo.Context cr ) {
    initmatrix(cr);
     
     //Just draw some guides for sanity
    cr.set_source_rgb (1,1,1);
    cr.paint ();

    cr.rectangle(0,0,-250,-250);
    cr.set_source_rgb(c(255), c(204), c(102));
    cr.fill();

    cr.rectangle(0,0,250,-250);
    cr.set_source_rgb(c(153), c(204), c(255));
    cr.fill();
    
    cr.rectangle(0,0,250,250);
    cr.set_source_rgb(c(204), c(244), c(255));
    cr.fill();
    
    cr.rectangle(0,0,-250,250);
    cr.set_source_rgb(c(51), c(255), c(153));
    cr.fill();
    

    grid(cr, true);


      //Begin the pain
      cr.save();

      cr.save();
        d1.set_matrix(cr);
        d1.draw(cr);
        d1.draw_dbb(cr);


            cr.save();
              d2.set_matrix(cr);
              d2.draw(cr);
              d2.draw_dbb(cr);

            cr.restore();
      
      cr.restore();  

      d3.set_matrix(cr);
      d3.draw(cr);
      d3.draw_dbb(cr);


  
    cr.restore(); 
    //back in device space

    return true;
  }

  private bool handle_events(Gdk.Event event) {
    //stdout.printf("%s\n", event.type.to_string());

    if (event.type == Gdk.EventType.MOTION_NOTIFY) {

      bitstate |= flag_motion_notify;

          double mx = event.motion.x;
          double my = event.motion.y;
          //stdout.printf("Mouse at:(%G | %G)\n",mx,my);

          var some_hit = false;

          foreach(Dot d in dlist) {
            if ( (bitstate == pattern_in_drag) && (dotBeingDragged == d) ) continue;
              if (d.bbox_hit(mx,my)) {
                some_hit = true;
                overDot = d;
              }
            }

          //d2.rot += 1;d2.sx+=0.001;d2.sy+=0.001; //just animate d2 a bit
          if (!some_hit) overDot = null;
    }


    if (event.type == Gdk.EventType.BUTTON_PRESS) {
      //Set our flag:
      bitstate = bitstate | flag_button_press;


      Gdk.ModifierType modifiers;
      modifiers = Gtk.accelerator_get_default_mod_mask ();

      if ( (event.button.state & modifiers) == Gdk.ModifierType.CONTROL_MASK ) {
        stdout.printf (" Control Key\n");
        }
    }

    if (event.type == Gdk.EventType.BUTTON_RELEASE) {
      //Set our flag:
      bitstate = bitstate | flag_button_release;
    }

    //Use our flags!

    if (bitstate == pattern_click) {
      stdout.printf("CLICK!\n");
    }

    if (bitstate == pattern_drag_start) {
      //stdout.printf("DRAG START\n");
      bitstate |= flag_in_drag; //signal we are dragging flag
      if (overDot != null ) dotBeingDragged = overDot;
      if (dotBeingDragged != null) stdout.printf("DRAGSTART %s\n", dotBeingDragged.name);
    }
    if (bitstate == pattern_in_drag) {
      //stdout.printf("IN DRAG\n");
      if (dotBeingDragged != null) { 
        //stdout.printf("DRAGGING %s\n", dotBeingDragged.name);
        dotBeingDragged.move(event.motion.x, event.motion.y);
      //d2.x = mx; d2.y = my;
      }
    }

    if (bitstate == pattern_drop) {
      //stdout.printf("DROP!!!!!!!!!!!\n");
      if (dotBeingDragged != null) {
        stdout.printf("DROPPED %s\n", dotBeingDragged.name);
        dotBeingDragged = null;
      }
    }


    //switch off flags after we've used them.
    if (event.type == Gdk.EventType.MOTION_NOTIFY) {
      bitstate &= ~flag_motion_notify;
    }

    if (event.type == Gdk.EventType.BUTTON_PRESS) {
      //self.ui_state = self.ui_state & ~Mouserizer.__FLAG_RELEASING # clear bit
      bitstate = bitstate & ~flag_button_release; //clear release flag
    }
    if (event.type == Gdk.EventType.BUTTON_RELEASE) {
      bitstate &= ~flag_button_press; //clear press state
      bitstate &= ~flag_in_drag; //no drag either.
    }


    queue_draw ();

    return true; //True - stops the event here. False propogates it up.
  }


}

int main(string[] args) {

  Gtk.init ( ref args ); //ok = Gtk.init ... 

  var sgw = new SceneGraphWidget ();

  var w = new Gtk.Window();


  w.add( sgw );
  w.show_all ();
  w.destroy.connect (Gtk.main_quit);
  w.key_press_event.connect( (e) => { 
    if (e.keyval == Gdk.Key.Escape) {
      Gtk.main_quit(); 
      return true; 
    }
    return false;
  } );

  Gtk.main ();

  return 0;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.