Re: Coordinate from device to user space without a context
Donn <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <[email protected]> |
On 06/11/2013 21:25, Bryce W. Harrington wrote:
> But let's say your two coordinates aren't rotated or skewed, they're
> just straight multiples with an offset. The formula simplifies to
> this:
>
> x_new = xx * x + x0; y_new = yy * y + y0;
>
(slimmer file attached.)
I gave this one a go in my move(), where mx,my are mouse coords:
public void move(double mx, double my) {
double xnew = mymatrix.xx * mx + mymatrix.x0;
double ynew = mymatrix.yy * my + mymatrix.y0;
stdout.printf("mx my: %G|%G maps to xnew ynew: %G|%G\n",mx,my, xnew,
ynew);
x=xnew; y=ynew;
In my set_matrix method, after setting a new trans/scale/rot, I do:
[line 105] cr.identity_matrix();
[line 106] mymatrix = cr.get_matrix();
The printout shows if:
I remark line 105 out (no identity call) the numbers grow rapidly,
showing no relation to user space or mouse space.
If I put 106 back in, the numbers are out by about half my window
width. They also do not adhere to the initial matrix that was set in my
initmatrix call (line 255) which sets up a zero,zero in the middle.
So, I'm close. But ...
Thanks for your math insight - I'm monkey-see-monkey-type most of the
time.I have read about matrix math (linear?) and even had my bro in law
'splain it, but I'm like a stone in these waters!
> Once you understand the above, then you'll understand exactly what
> the cairo_matrix_transform_point() and cairo_matrix_invert()
> functions do.
Well.. I'll do my best, but that's a tall order. ;)
Thanks!
\d
--
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
matrix_maillist_debug.vala
(text/x-vala, 9.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;
stdout.printf("mx my: %G|%G maps to xnew ynew: %G|%G\n",mx,my, xnew, ynew);
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;
*/
}
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;
}