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