I am working in AS3.
I have a generic rectangle. This rectangle can have any length, any width and any rotation. I am trying to solve for the x and y coordinates of the four corners of the rectangle. I know the coordinates of the centre of the rectangle, I know its width, its height, the y distance between the highest and lowest point and the x distance between the farthest left and farthest right point as well as knowing the rotation.
My code currently looks like this (Object, of course, being the rectangle in question, keep in mind that when I apply this it can have any dimensions - This is just one possibility. Initial width and height are the actual length and width, while width and height referenced later are the x and y distances between the highest and lowest points and the farthest left and right points, rotation is of course rotation, and x and y are the object's centre coordinates).
import flash.events.Event;
addEventListener(Event.ENTER_FRAME, Rotate, false, 0, true);
var Radius:Number = Math.sqrt(((Object.height / 2) * (Object.height / 2)) + ((Object.width / 2) * (Object.width / 2)));
function Rotate(event:Event)
{
Object.rotation += 1;
Marker1.x = Math.sqrt((Radius * Radius) - ((Object.height / 2) * (Object.height / 2))) + Object.x;
Marker2.x = - Math.sqrt((Radius * Radius) - ((Object.height / 2) * (Object.height / 2))) + Object.x;
Marker3.y = Math.sqrt((Radius * Radius) - ((Object.width / 2) * (Object.width / 2))) + Object.y;
Marker4.y = - Math.sqrt((Radius * Radius) - ((Object.width / 2) * (Object.width / 2))) + Object.y;
Marker1.y = Object.y + (Object.height / 2);
Marker2.y = Object.y - (Object.height / 2);
Marker3.x = Object.x + (Object.width / 2);
Marker4.x = Object.x - (Object.width / 2);
}
As you can see I am attempting to use circle geometry to place four small circles (Markers 1-4) at the corners of the rectangle, just for testing purposes to confirm that I have gathered the correct coordinates. Problem is, the coordinates will always be placed in either +x and +y or -x and -y, but never the other two quadrants of the graph. I can't figure out a simple way of dynamically simulating the +- of the quadratic equation in the program. Does anyone know of a way to find these four points with and length, width and rotation of the rectangle?
If you represent the coordinates of the corners as offsets from the midpoint of the rectangle you can easily rotate them anti-clockwise by an angle θ with
dx' = dx × cos θ - dy × sin θ
dy' = dx × sin θ + dy × cos θ
You can then add the rotated offsets to the midpoint to recover the new coordinates of the corners.
Given a bounding square and the total number of smaller squares. The smaller squares need to be drawn within the bigger square evenly spaced out in a circular fashion, just touching but not overlapping. How do you compute the width of the inner square ?
(updated fiddle link)
http://jsfiddle.net/mdluffy/6bUVz/3/
///////// INPUTS ///////////////////////////////////////////
var BoundingBoxSide = 100;
var NofInnerBoxes = 14;
////////////////////////////////////////////////////////////
drawBoxes();
function drawBoxes()
{
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(0,0,BoundingBoxSide,BoundingBoxSide);
ctx.stroke();
for(var i=0; i < NofInnerBoxes; i++)
{
// ************************************************************************************ //
// This needs to be computed so that the boxes touch each other, but not overlap
var innerBoxSide = 20;
// ************************************************************************************ //
var angle = degToRad(i * 360/NofInnerBoxes);
var innerX = ((BoundingBoxSide - innerBoxSide)/2) * Math.cos(angle);
var innerY = ((BoundingBoxSide - innerBoxSide)/2) * Math.sin(angle);
ctx.rect(BoundingBoxSide/2 + innerX - innerBoxSide/2, BoundingBoxSide/2 - innerY - innerBoxSide/2, innerBoxSide, innerBoxSide);
ctx.stroke();
}
}
function degToRad(d)
{
return d * Math.PI / 180;
}
Update:
I'm working on a 3D visualization using Three.js. This is a tree structure with nodes represented as cubes. The child node cubes are layed out circularly on top of the parent node cubes. Applied recursively.
The inner squares need not touch the outer squares, but it should touch the largest circle that can be fit inside the outer square.
Here's my attempt:
var alpha = Math.PI * (NofInnerBoxes - 2) / (2 * NofInnerBoxes)
var t = Math.tan(alpha)
var innerBoxSide = BoundingBoxSide / Math.sqrt(t*t + 4*t + 5)
(Credit: to WolframAlpha for solving for innerBoxSide; and to an envelope I doodled on the back of.)
Update: The above was under the assumption that two sides of each inner square are parallel to a line that passes through the centers of that inner square and the outer square. I now see that this assumption was not what you had in mind.
Here's another approach. In this one, the circles that circumscribe the inner squares will all touch each other, though the inner squares themselves will not quite touch each other:
var alpha = Math.PI / NofInnerBoxes
var t = Math.sin(alpha)
var innerBoxSide = BoundingBoxSide * t / ((t + 1) * Math.sqrt(2))
Result:
Derivation
In response to the request for how the formula was derived...
Let r_s (r sub s) be the "small radius", i.e. the radius of the circles that circumscribe the inner squares. These inner circles are all tangent to the big circle that inscribes the outer square. Call the radius of the big circle r_b (b for "big").
Note that the side of the outer square, a.k.a. BoundingBoxSide, is = 2 * r_b. Note also that the diameter of each inner circle, 2 * r_s, is also the diagonal of each inner square. Therefore the side of each inner square, a.k.a. innerBoxSide, is = (2 * r_s) / sqrt(2).
Now let angle alpha = half the angle that each inner circle subtends (if that's the right verb) around the center of the big circle. I.e. alpha = (2 * pi / NofInnerBoxes) / 2 = pi / NofInnerBoxes.
The key is to draw a right triangle, where one of the angles is alpha; this will tell us the ratios between all the sides of that triangle, using trig functions like sin(). And we can draw such a triangle whose vertices are:
A. The center of the big circle
B. The center of a small circle
C. The point where that small circle touches its neighbor
The right angle is at vertex C, and the angle alpha is at vertex A. The length of side BC = r_s, and the length of the hypotenuse (AB) = r_b - r_s.
So by the definition of sin(), we can say that sin(alpha) = opposite side / hypotenuse = BC / AB = r_s / (r_b - r_s). Solve this equation for r_s, and we get r_s = r_b * sin(alpha) / (1 + sin(alpha)).
Finally, plug in the fact (above) that BoundingBoxSide = 2 * r_b, and innerBoxSide = (2 * r_s) / sqrt(2); and you get the formula shown above, innerBoxSide = BoundingBoxSide * t / ((t + 1) * Math.sqrt(2)).
Sorry this is a longish wall of text, but I hope it's sufficiently clear if read carefully. I may post a diagram as well, if time allows. Let me know if you have questions. (There's probably an easier way to derive this, but that's how I did it.)
I want to draw a oval in html5 canvas,and i found a good method for it in stackoverflow.but I have another quesition.
function drawEllipse(ctx, x, y, w, h) {
var kappa = 0.5522848;
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
ctx.closePath();
ctx.stroke();
}
the method in the above link has use bezierCurveTo to draw a ellipse,but it has draw bezierCurveTo 4 times. but I think just 2 bezierCurveTo can draw a ellipse.like this:
but i'm weak in Mathematics,could someone tell me the relationship of "the control point" and "the oval point" please? or we must draw four bezier Curve to draw a oval?
thanks everybody
My background isn't in mathematics so if I'm wrong I'm sure someone will correct me, but from my understanding we can draw a pretty good approximation of an ellipse with just two cubic bezier curves but the coordinates will be a little tricky.
To answer your question about the relation between the oval point and the control points I think it best be answered by watching this video from the point I've selected if you're familiar with interpolation or from the beginning if you are not. Don't worry it is short.
One problem we're probably going to run into is that when we start from the top and do a bezierCurveTo the bottom of the ellipse with the corners of the rectangle (of the same width and height) as the control points, the ellipses width is going to be smaller than the rectangle. .75 times the size we want. So we can just scale the control points accordingly.
Our control point's x would be adjusted like so (assuming width is the width of the ellipse and we're dividing by two to get its offset from the origin)
var cpx = (width / .75) / 2;
Put together a visualization where you can play with the width and height and see the drawn ellipse.
The red ellipse is how we wanted it to be drawn, with the inner one how it would be drawn if we didnt reposition the control points. The lines illustrate De Casteljau's algorithm that was shown in the video.
Here's a screenshot of the visualization
You only need two cubic bezier curves to draw an ellipse. Here's a simplified version of DerekR's code that uses the original function arguments that you provided--assuming you want x and y to be the center of the ellipse:
jsFiddle
function drawEllipse(ctx, x, y, w, h) {
var width_over_2 = w / 2;
var width_two_thirds = w * 2 / 3;
var height_over_2 = h / 2;
ctx.beginPath();
ctx.moveTo(x, y - height_over_2);
ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2);
ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y -height_over_2);
ctx.closePath();
ctx.stroke();
}
Big thanks to BKH.
I used his code with two bezier curves to complete my ellipse drawing with any rotation angle. Also, I created an comparison demo between ellipses drawn by bezier curves and native ellipse() function (for now implemented only in Chrome).
function drawEllipseByBezierCurves(ctx, x, y, radiusX, radiusY, rotationAngle) {
var width_two_thirds = radiusX * 4 / 3;
var dx1 = Math.sin(rotationAngle) * radiusY;
var dy1 = Math.cos(rotationAngle) * radiusY;
var dx2 = Math.cos(rotationAngle) * width_two_thirds;
var dy2 = Math.sin(rotationAngle) * width_two_thirds;
var topCenterX = x - dx1;
var topCenterY = y + dy1;
var topRightX = topCenterX + dx2;
var topRightY = topCenterY + dy2;
var topLeftX = topCenterX - dx2;
var topLeftY = topCenterY - dy2;
var bottomCenterX = x + dx1;
var bottomCenterY = y - dy1;
var bottomRightX = bottomCenterX + dx2;
var bottomRightY = bottomCenterY + dy2;
var bottomLeftX = bottomCenterX - dx2;
var bottomLeftY = bottomCenterY - dy2;
ctx.beginPath();
ctx.moveTo(bottomCenterX, bottomCenterY);
ctx.bezierCurveTo(bottomRightX, bottomRightY, topRightX, topRightY, topCenterX, topCenterY);
ctx.bezierCurveTo(topLeftX, topLeftY, bottomLeftX, bottomLeftY, bottomCenterX, bottomCenterY);
ctx.closePath();
ctx.stroke();
}
You will find this explained slightly more math-based in http://pomax.github.io/bezierinfo/#circles_cubic, but the gist is that using a cubic bezier curve for more than a quarter turn is usually not a good idea. Thankfully, using four curves makes finding the required control points rather easy. Start off with a circle, in which case each quarter circle is (1,0)--(1,0.55228)--(0.55228,1)--(0,1) with scaled coordinates for your ellipse. Draw that four times with +/- signs swapped to effect a full circle, scale the dimensions to get your ellipse, and done.
If you use two curves, the coordinates become (1,0)--(1,4/3)--(-1,4/3)--(-1,0), scaled for your ellipse. It may still look decent enough in your application, it depends a bit on how big your drawing ends up being.
It can be mathematically proven, that circle can not be made with Bézier curve of any degree. You can make "almost circle" by approximating it.
Say you want to draw a quarter of circle around [0,0]. Cubic bézier coordinates are:
[0 , 1 ]
[0.55, 1 ]
[1 , 0.55]
[1 , 0 ]
It is a very good approximation. Transform it linearly to get an ellpise.
I am creating a game in which I draw a series of polygons by creating points around a radius of a cricle.
Later on I rotate the shapes and I need to calculate the new location (X,Y) of the points based on the rotation. I have the Old XY of each point, the XY of the center of the shape, radius of shape and the rotation.
Have a look at my diagram of the problem.
It should be possible to use matrix transformations for this, but you can also do it manually:
http://www.siggraph.org/education/materials/HyperGraph/modeling/mod_tran/2drota.htm
So essentially;
newX = initialX * Math.cos(angle) - initialY * Math.sin(angle);
newY = initialY * Math.cos(angle) + initialX * Math.sin(angle);
//Angle is in radians btw
This assumes that the initialX/Y is relative to the center of rotation, so you would have to subtract the center point before starting, and then add it again after the calculation to place it correctly.
Hope this helps!
For each point do:
alpha = arctan2(x, y)
len = sqrt(x^2 + y^2)
newX = len * cos(alpha + rotation)
newy = len * sin(alpha + rotation)
Original [x,y] and new [newX,newY] coordinates are both relative to the center of your rotation. If your original [x,y] is absolut, you have to calculate relative first:
x = xAbs - xCenter
y = yAbs - yCenter
Make sure your arctan2 function provides a result of PI/2 or -PI/2 if x=0. Primitive arctan functions do not allow x=0.
I have a dial which I drag around a circle to give me a reading between 0 and 1.
Something like this:
dx = mouseX-centerX;
dy = mouseY-centerY;
rad = Math.atan2(dy,dx);
rad += offset;
Tweener.addTween(knob,{y:centerY - Math.cos(rad)*radius, time:.1, transition:"easeOutSine"});
Tweener.addTween(knob,{x:centerX + Math.sin(rad)*radius, time:.1, transition:"easeOutSine"});
knob.rotation = rad * 180 / Math.PI;
This work's great, except when the slider goes from 359 degrees to 1 degree, my value between 0 and 1 returns to zero. (Which makes sense, as the value is based on the angle of my slider)
I'm trying to find a way for the dial to move from 359 degrees to 361 and onwards basically.
In my head: I need to check if the next value of my mouse drag goes past the 360 degree point and add 360 to the total, to stop it returning to zero and continue to 361 degrees.
I just cant work out how to put this into code...
On each frame when you are rotating the knob, check the change in angular distance instead of direct angle.
Save the previous frames angle and see if the difference is positive or negative.
var rad = Math.atan2(dy, dx);
var diff = rad - oldRad;
oldRad = rad;
if( diff > Math.PI )
diff -= Math.PI * 2;
if( shortestAngle < -Math.PI )
diff += Math.PI * 2;
diff should contain a value that if it's been rotated to the right, is positive (or negative if rotated left). Simply add that to the total angle.
There might be some errors in the code (took it from an old project), but that's the gist of it :)
Hope that helps!