I would like to get the coordinates of a mouse event in Raphael's paper coordinates. I would like those to be accurate even when I have used setViewBox.
Please see http://jsfiddle.net/CEnBN/
The following creates a 10x10 green box and then zooms way in - with the center of that box at the view's origin.
var paper = Raphael(10, 50, 320, 200);
var rect = paper.rect(0, 0, 10, 10);
rect.attr('fill', 'green');
rect.mousedown(function (event, a, b) {
$('#here').text([a, b]);
console.log(event);
});
paper.setViewBox(5, 5, 10, 10);
I would like to receive click coordinates that reflect their position in the box. ie. they should range from ([5-10], [5-10]).
Note: much later, and I have migrated to D3.js - which has generally made me a lot happier.
Edited: simplified by using clientX/Y of the mouse event - remove need to get element offset
Here is what I came up with. Basically, correct the mouse position to be relative to the paper by using the client rect of the paper and clientX/Y of the mouse event. Then compare the corrected positions to the client rect's width/height, then factor the results by original paper dimensions:
var paper = Raphael(10, 50, 320, 200);
var rect = paper.rect(0, 0, 10, 10);
rect.attr('fill', 'green');
rect.mousedown(function (event, a, b) {
// get bounding rect of the paper
var bnds = event.target.getBoundingClientRect();
// adjust mouse x/y
var mx = event.clientX - bnds.left;
var my = event.clientY - bnds.top;
// divide x/y by the bounding w/h to get location %s and apply factor by actual paper w/h
var fx = mx/bnds.width * rect.attrs.width
var fy = my/bnds.height * rect.attrs.height
// cleanup output
fx = Number(fx).toPrecision(3);
fy = Number(fy).toPrecision(3);
$('#here').text('x: ' + fx + ', y: ' + fy);
});
paper.setViewBox(5, 5, 10, 10);
An updated fiddle link is here:
http://jsfiddle.net/CEnBN/3/
more compact version of mouse down func:
rect.mousedown(function (event, a, b) {
var bnds = event.target.getBoundingClientRect();
var fx = (event.clientX - bnds.left)/bnds.width * rect.attrs.width
var fy = (event.clientY - bnds.top)/bnds.height * rect.attrs.height
$('#here').text('x: ' + fx + ', y: ' + fy);
});
You need to offset the result, something like this:
var posx, posy;
var paper = Raphael("canvas", 320, 200);
var rect = paper.rect(0, 0, 10, 10);
rect.attr('fill', 'green');
rect.mousedown(function (e, a, b) {
posx = e.pageX - $(document).scrollLeft() - $('#canvas').offset().left;
posy = e.pageY - $(document).scrollTop() - $('#canvas').offset().top;
$('#here').text([posx, posy]);
console.log(e);
});
paper.setViewBox(5, 5, 10, 10);
I added an element for Raphaeljs to target, have a look at this update to your jsfiddle
The answer by gthmb is very good, but missing a detail - the position of the rectangle on the paper. This version is only working, if the rectangle is at position (0,0). To support also the situation where it is translated, add the position of the rectangle to the result:
function mouseEvent_handler(e) {
var bnds = event.target.getBoundingClientRect();
var bbox = this.getBBox();
var fx = (event.clientX - bnds.left)/bnds.width * rect.attrs.width + bbox.x;
var fy = (event.clientY - bnds.top)/bnds.height * rect.attrs.height + bbox.y;
$('#here').text('x: ' + fx + ', y: ' + fy);
}
Here the modified version of the fiddle: http://jsfiddle.net/zJu8b/1/
Related
I'm currently developing an application that takes images and detect a specific angle in that image.
The images always look something like this: original image.
I want to detect the angle of the bottom cone.
In order to do that i crop that image in image and use two Houghline algorithms. One for the cone and one for the table at the bottom. This works failry well and i get the correct result in 90% of the images.
result of the two algorithms
Doesnt work
Doesnt work either
My approach works for now because i can guarantee that the cone will alwys be in an angle range of 5 to 90°. So i can filter the houghlines based on their angle.
However i wonder if their is a better approach to this. This is my first time working with OpenCV, so maybe this community has some tips to improve the whole thing. Any help is appreciated!
My code for the cone so far:
public (Bitmap bmp , double angle) Calculate(Mat imgOriginal, Mat imgCropped, int Y)
{
Logging.Log("Functioncall: Calculate");
var finalAngle = 0.0;
Mat imgWithLines = imgOriginal.Clone();
how croppedImage look's
var grey = new Mat();
CvInvoke.CvtColor(imgCropped, grey, ColorConversion.Bgr2Gray);
var bilateral = new Mat();
CvInvoke.BilateralFilter(grey, bilateral, 15, 85, 15);
var blur = new Mat();
CvInvoke.GaussianBlur(bilateral, blur, new Size(5, 5), 0); // Kernel reduced from 31 to 5
var edged = new Mat();
CvInvoke.Canny(blur, edged, 0, 50);
var iterator = true;
var counter = 0;
var hlThreshhold = 28;
while (iterator &&counter<40)
{
counter++;
var threshold = hlThreshhold;
var rho = 1;
var theta = Math.PI / 180;
var lines = new VectorOfPointF();
CvInvoke.HoughLines(edged, lines, rho, theta, threshold);
var angles = CalculateAngles(lines);
if (angles.Length > 1)
{
hlThreshhold += 1;
}
if (angles.Length < 1)
{
hlThreshhold -= 1;
}
if (angles.Length == 1)
{
try
{
//Calc the more detailed position of glassLine and use it for Calc with ConeLine instead of perfect horizontal line
var glassLines = new VectorOfPointF();
var glassTheta = Math.PI / 720; // accuracy: PI / 180 => 1 degree | PI / 720 => 0.25 degree |
CvInvoke.HoughLines(edged, glassLines, rho, glassTheta, threshold);
var glassEdge = CalculateGlassEdge(glassLines);
iterator = false;
// finalAngle = angles.FoundAngle; // Anzeige der Winkel auf 2 Nachkommastellen
CvInvoke.Line(imgWithLines, new Point((int)angles.LineCoordinates[0].P1.X, (int)angles.LineCoordinates[0].P1.Y + Y), new Point((int)angles.LineCoordinates[0].P2.X, (int)angles.LineCoordinates[0].P2.Y + Y), new MCvScalar(0, 0, 255), 5);
CvInvoke.Line(imgWithLines, new Point((int)glassEdge.LineCoordinates[0].P1.X, (int)glassEdge.LineCoordinates[0].P1.Y + Y), new Point((int)glassEdge.LineCoordinates[0].P2.X, (int)glassEdge.LineCoordinates[0].P2.Y + Y), new MCvScalar(255, 255, 0), 5);
// calc Angle ConeLine and GlassLine
finalAngle = 90 + angles.LineCoordinates[0].GetExteriorAngleDegree(glassEdge.LineCoordinates[0]);
finalAngle = Math.Round(finalAngle, 1);
//Calc CrossPoint
PointF crossPoint = getCrossPoint(angles.LineCoordinates[0], glassEdge.LineCoordinates[0]);
//Draw dashed Line through crossPoint
drawDrashedLineInCrossPoint(imgWithLines, crossPoint, 30);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
finalAngle = 0.0;
imgWithLines = imgOriginal.Clone();
}
}
}
Image cropping (the table is always on the same position, so i use this position and a height parameter to only get the bottom of the cone )
public Mat ReturnCropped(Bitmap imgOriginal, int GlassDiscLine, int HeightOffset)
{
var rect = new Rectangle(0, 2500-GlassDiscLine-HeightOffset, imgOriginal.Width, 400);
return new Mat(imgOriginal.ToMat(), rect);
}
I have a page with a rectangular image.
The user can rotate the image to any angle and move it to any position on the paper.
Without the rotation I can just use the getBBox() method to find the corners but with rotation it doesn't work.
How can I find the corners of a rotated image in Raphael?
You can use the x and y functions on the Matrix object to find the image corner coordinates after transformation. http://raphaeljs.com/reference.html#Matrix.x
All 4 corners of image:
var paper = Raphael(0, 0, 500, 500);
var el= paper.image("http://www.abcgallery.com/R/raphael/raphael57a.jpg", 100, 100, 210, 258);
el.transform('t20r45');
var x = el.matrix.x(el.attr("x"), el.attr("y"));
var y = el.matrix.y(el.attr("x"), el.attr("y"));
var x2 = el.matrix.x(el.attr("x") + el.attr("width"), el.attr("y"));
var y2 = el.matrix.y(el.attr("x") + el.attr("width"), el.attr("y"));
var x3 = el.matrix.x(el.attr("x"), el.attr("y") + el.attr("height"));
var y3 = el.matrix.y(el.attr("x"), el.attr("y") + el.attr("height"));
var x4 = el.matrix.x(el.attr("x") + el.attr("width"), el.attr("y") + el.attr("height"));
var y4 = el.matrix.y(el.attr("x") + el.attr("width"), el.attr("y") + el.attr("height"));
paper.circle(x, y,5);
paper.circle(x2, y2, 5);
paper.circle(x3, y3, 5);
paper.circle(x4, y4, 5);
http://jsfiddle.net/eWdE8/5/
Old answer, only for finding the coordinates of the bounding box:
You should use getBBox() with isWithoutTransform set to false (which is the default), and it will work.
var paper = Raphael(0, 0, 500, 500);
var el = paper.image("http://www.abcgallery.com/R/raphael/raphael57a.jpg", 100, 100, 210, 258);
el.transform('r45')
console.log(el.getBBox(false)) // After transform
console.log(el.getBBox(true)) // Before transform
http://jsfiddle.net/eWdE8/
I am using Raphael library to draw and work with objects in my extjs app. Basically I was able to find a way to DRAG the SET the following way:
var set = paper.set();
set.push(
paper.circle(200,200,65).attr({fill: "orange", stroke: "black"}),
paper.text(200, 200, buttonText).attr({"font-size": 24, "font-weight": "bold", fill: "black"})
);
set.data("myset", set);
set.data("position", [0,0]);
var current_position = [0,0];
set.drag(
function(dx, dy) {
this.data('myset').transform("T" + (this.data("position")[0] + dx) + "," + (this.data("position")[1] + dy));
current_position = [dx,dy];
},
function() {
this.data('myset').data("position",
[this.data("position")[0] += current_position[0],
this.data("position")[1] += current_position[1]
]);
}
);
I found this little code in jsFiddle and modified it a little to do what I need. You can check out the demo here. When you are dragging the item by clicking on it once, then everything seams to work. But the problem is that when you doublclick on it couple of times and play with it/drag, it starts to jump around. The coordinates get messed up.
Could you help me to find where my coordinates get messed up. Thanks
I fixed it by looking at this example: here
var paper = Raphael(0, 0, 500, 500);
var set = paper.set();
set.push(paper.circle(100,100,35), paper.circle(150,150,15));
set.attr("fill", "orange");
set.data("myset", set);
set.drag(
function(dx, dy, x, y, e) {
this.data('myset').transform("T" + dx + "," + dy);
},
function(x, y, e) {},
function(e) {}
);
For some reason i can get this working with rectangle variables but not with circles.
At the moment, this code allows both circles to be dragged independently but not together
Anybody know how to fix this or an alternative method?
addIntermediateSymbol = function()
{
var Intermediate = raphaelReference.set();
Intermediate.push(
raphaelReference.circle(74, 79, 20).attr({fill: "#ff7f00",stroke: "#000000",'stroke-width': 3}),
raphaelReference.circle(74, 79, 10).attr({fill: "#ff7f00",stroke: "#000000",'stroke-width': 4})
);
var start = function () {
// storing original coordinates
this.ox = this.attr("cx");
this.oy = this.attr("cy");
},
move = function (dx, dy) {
// move will be called with dx and dy
this.attr({cx: this.ox + dx, cy: this.oy + dy});
},
up = function () {
;
};
Intermediate.drag(move, start, up);
}
You have to use Intermediate again in the drag functions (start, move, up), but with translate function (which make everybody in the set move in the same way):
var start = function () {
Intermediate.oBB = Intermediate.getBBox();
},
move = function (dx, dy) {
var bb = Intermediate.getBBox();
Intermediate.translate(Intermediate.oBB.x - bb.x + dx, Intermediate.oBB.y - bb.y + dy);
},
See http://irunmywebsite.com/raphael/additionalhelp.php?v=1&q=anglebannersoncurves#PAGETOP (click on "Draggable Set" down of the right hand side list of examples)
It seems Intermediate.func() is just mapping the property func() to the elements inside of the set (applies to drag() and translate()), like:
for (var shape in Intermediate) {shape.func();}
About monkee answer:
As monkee points it out, in the dragging methods this references the clicked SVG object
Raphael sets don't have "cx" as such, so Intermediate.attr({cx:this.ox ... is working only if all the elements of the set are circles and have the same geometrical center ...
In the move function, "this" references to the clicked Raphäel object.
Instead of:
move = function (dx, dy) {
this.attr({cx: this.ox + dx, cy: this.oy + dy});
}
Do this:
move = function (dx, dy) {
Intermediate.attr({cx: this.ox + dx, cy: this.oy + dy});
}
Bad formatted working example here: http://jsbin.com/uxege4/7/edit
Here is a helpful js Fiddle solution that does exactly what you want, adapted from http://www.irunmywebsite.com/raphael/additionalhelp.php?v=2#pagetop
http://jsfiddle.net/q4vUx/102/
var paper = Raphael('stage', 300, 300);
var r = paper.rect(50,100,30,50).attr({fill:"#FFF"}),
c = paper.circle(90,150,10).attr({fill:"#FFF"}),
t = paper.text(50, 140, "Hello");
var rr = paper.rect(200,100,30,50).attr({fill:"#FFF"}),
cc = paper.circle(240,150,10).attr({fill:"#FFF"}),
tt = paper.text(200, 140, "Hello");
var pp = paper.set(rr, cc, tt);
var p = paper.set(r, c, t);
r.set = p, c.set = p, t.set = p;
rr.set = pp, cc.set = pp, tt.set = pp;
p.newTX=0,p.newTY=0,p.fDx=0,p.fDy=0,p.tAddX,p.tAddY,p.reInitialize=false,
pp.newTX=0,pp.newTY=0,pp.fDx=0,pp.fDy=0,pp.tAddX,pp.tAddY,pp.reInitialize=false,
start = function () {
},
move = function (dx, dy) {
var a = this.set;
a.tAddX=dx-a.fDx,a.tAddY=dy-a.fDy,a.fDx=dx,a.fDy=dy;
if(a.reInitialize)
{
a.tAddX=0,a.fDx=0,a.tAddY=0;a.fDy=0,a.reInitialize=false;
}
else
{
a.newTX+=a.tAddX,a.newTY+=a.tAddY;
a.attr({transform: "t"+a.newTX+","+a.newTY});
}
},
up = function () {
this.set.reInitialize=true;
};
p.drag(move, start, up);
pp.drag(move, start, up);
I was running into all sort of problems too regards dragging sets around.
It does:
- extends Raphael to make dragging sets possible
- creates new sets with a mouse click
- keeps the dragged set within the canvas boundaries.
The code in short:
CANVAS_WIDTH = 250;
CANVAS_HEIGHT = 250;
var newSet = document.getElementById("newSet");
paper = Raphael('canvas', CANVAS_WIDTH, CANVAS_HEIGHT);
var backGround = paper.rect(0,0,CANVAS_HEIGHT, CANVAS_WIDTH);
backGround.attr({fill: "lightgrey", "fill-opacity": 0.5, "stroke-width": 0});
newSet.onclick = function() {
createNewSet();
}
createNewSet = function() {
var mySet = paper.set();
var rect = paper.rect(0, 0, 50, 50);
rect.attr({fill: "red", "fill-opacity": 0.5, "stroke-width": 0});
var bBox = rect.getBBox();
var text = paper.text(10, 10, "Hello");
text.attr({fill: 'black', 'text-anchor': 'start'});
mySet.push(rect, text);
mySet.draggable();
//mySet = reposText(mySet);
mySet.max_x = CANVAS_WIDTH - bBox.width;
mySet.min_x = 0;
mySet.max_y = CANVAS_HEIGHT - bBox.height;
mySet.min_y = 0;
};
Raphael.st.draggable = function() {
var me = this,
lx = 0,
ly = 0,
ox = 0,
oy = 0,
moveFnc = function(dx, dy) {
lx = dx + ox;
ly = dy + oy;
if (lx < me.min_x ) {
lx = me.min_x;
}
else if ( lx > me.max_x) {
lx = me.max_x;
}
if ( ly < me.min_y ) {
ly = me.min_y;
}
else if ( ly > me.max_y) {
ly = me.max_y;
}
me.transform('t' + lx + ',' + ly);
},
startFnc = function() {},
endFnc = function() {
ox = lx;
oy = ly;
};
this.drag(moveFnc, startFnc, endFnc);
};
See this code in action here:
http://jsfiddle.net/Alexis2000/mG2EL/
Good Luck!
So I'm just doing a basic orbit simulator using Raphael JS, where I draw one circle as the "star" and another circle as the "planet". It seems to be working just fine, with the one snag that as the simulation continues, its framerate progressively slows down until the orbital motion no longer appears fluid. Here's the code (note: uses jQuery only to initialize the page):
$(function() {
var paper = Raphael(document.getElementById('canvas'), 640, 480);
var star = paper.circle(320, 240, 10);
var planet = paper.circle(320, 150, 5);
var starVelocity = [0,0];
var planetVelocity = [20.42,0];
var starMass = 3.08e22;
var planetMass = 3.303e26;
var gravConstant = 1.034e-18;
function calculateOrbit() {
var accx = 0;
var accy = 0;
accx = (gravConstant * starMass * ((star.attr('cx') - planet.attr('cx')))) / (Math.pow(circleDistance(), 3));
accy = (gravConstant * starMass * ((star.attr('cy') - planet.attr('cy')))) / (Math.pow(circleDistance(), 3));
planetVelocity[0] += accx;
planetVelocity[1] += accy;
planet.animate({cx: planet.attr('cx') + planetVelocity[0], cy: planet.attr('cy') + planetVelocity[1]}, 150, calculateOrbit);
paper.circle(planet.attr('cx'), planet.attr('cy'), 1); // added to 'trace' orbit
}
function circleDistance() {
return (Math.sqrt(Math.pow(star.attr('cx') - planet.attr('cx'), 2) + Math.pow(star.attr('cy') - planet.attr('cy'), 2)));
}
calculateOrbit();
});
It doesn't appear, to me anyway, that any part of that code would cause the animation to gradually slow down to a crawl, so any help solving the problem will be appreciated!
The problem is with the call back to calculateOrbit in your planet.animate() call. Its not being handled by raphael correctly and is causing a memory leak or an execution slow down. if you remove it and replace the line
calculateOrbit()
with
setInterval(calculateOrbit, 150);
it should run smoothly.
full code:
$(function() {
var paper = Raphael(document.getElementById('canvas'), 640, 480);
var star = paper.circle(320, 240, 10);
var planet = paper.circle(320, 150, 5);
var starVelocity = [0,0];
var planetVelocity = [20.42,0];
var starMass = 3.08e22;
var planetMass = 3.303e26;
var gravConstant = 1.034e-18;
function calculateOrbit() {
var accx = 0;
var accy = 0;
accx = (gravConstant * starMass * ((star.attr('cx') - planet.attr('cx')))) / (Math.pow(circleDistance(), 3));
accy = (gravConstant * starMass * ((star.attr('cy') - planet.attr('cy')))) / (Math.pow(circleDistance(), 3));
planetVelocity[0] += accx;
planetVelocity[1] += accy;
planet.animate({cx: planet.attr('cx') + planetVelocity[0], cy: planet.attr('cy') + planetVelocity[1]}, 150);
paper.circle(planet.attr('cx'), planet.attr('cy'), 1); // added to 'trace' orbit
}
function circleDistance() {
return (Math.sqrt(Math.pow(star.attr('cx') - planet.attr('cx'), 2) + Math.pow(star.attr('cy') - planet.attr('cy'), 2)));
}
setInterval(calculateOrbit, 150);
});