I created some Functions, these will draw Rectangles, Circles, Hexagons etc.
One of them looks like this:
rotation = 45;
function hex(hex_sides, hex_size, hex_color){
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
ctx.save();
ctx.rotate(rotation*Math.PI/180);
ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
ctx.moveTo(x + hex_size * Math.cos(0), y + hex_size * Math.sin(0));
ctx.restore();
for (i = 0; i < hex_sides+1; i++) {
ctx.lineTo(x + hex_size * Math.cos(i * 2 * Math.PI / hex_sides), y + hex_size * Math.sin(i * 2 * Math.PI / hex_sides));
}
ctx.strokeStyle = hex_color;
ctx.stroke();
}
Now i call the Functions to Draw the Shapes inside my animation loop.
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circle(200);
circle(220);
hex(6, 180, "#fff");
rotation += 0.4;
requestAnimationFrame(loop);
}
I'm incrementing the var rotation inside the loop but it does not rotate the whole shape but just one line of it instead. Other Shapes i cant get to rotate at all.
I think i got a wrong approach, maybe because of my confusion about .save() and .restore() or .beginPath() and .closePath().
In General the behaviour is very strange when i start to use .translate() and .rotate()
The entire Code is here.
UPDATE
It is definitely something about this line:
ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
Somehow it does not translate correctly. The rotation now happens around the right middle side of the normal shape position but i want the rotation around its own axis.
I changed the hex() function to:
ctx.save();
ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
ctx.rotate(rotation*Math.PI/180);
ctx.moveTo(x + hex_size * Math.cos(0), y + hex_size * Math.sin(0));
for (i = 0; i < hex_sides+1; i++) {
ctx.lineTo(x + hex_size * Math.cos(i * 2 * Math.PI / hex_sides), y + hex_size * Math.sin(i * 2 * Math.PI / hex_sides));
}
ctx.strokeStyle = hex_color;
ctx.stroke();
ctx.restore();
Again, entire Code is here.
Nevermind... figured it out, i have forgotten to substract half of the shapes width from the translation point and also the order seems to be more important then i tought.
Working Code
function hex(hex_sides, hex_size, hex_color){
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
ctx.save();
ctx.translate(x,y);
ctx.rotate(rotation*Math.PI/180);
//ctx.moveTo(x + hex_size * Math.cos(0), y + hex_size * Math.sin(0));
ctx.moveTo(0,0);
for (i = 0; i < hex_sides+1; i++) {
ctx.lineTo(x/hex_size + hex_size * Math.cos(i * 2 * Math.PI / hex_sides), y/hex_size + hex_size * Math.sin(i * 2 * Math.PI / hex_sides));
}
ctx.strokeStyle = hex_color;
ctx.stroke();
ctx.restore();
}
Related
My homework is to write a C program with openGL/Glut which, after getting groups of 4 points by mouse click (points with 3 coordinates), should draw a bezier curve with adaptive algorithm. At a theoretical level it's clear how the algorithm works but I don't know how to put that in C code. I mean that at lesson we saw that the 4 control points could have a shape similar to a "trapeze" and then the algorithm calculates the two "heights" and then checks if they satisfy a tollerance. The problem is that the user might click everywhere in the screen and the points might not have trapeze-like shape...so, where can I start from? This is all I have
This is the cole I have written, which is called each time a control point is added:
if (bezierMode == CASTELJAU_ADAPTIVE) {
glColor3f (0.0f, 0.8f, 0.4f); /* draw adaptive casteljau curve in green */
for(i=0; i+3<numCV; i += 3)
adaptiveDeCasteljau3(CV, i, 0.01);
}
void adaptiveDeCasteljau3(float CV[MAX_CV][3], int position, float tolerance) {
float x01 = (CV[position][0] + CV[position+1][0]) / 2;
float y01 = (CV[position][1] + CV[position+1][1]) / 2;
float x12 = (CV[position+1][0] + CV[position+2][0]) / 2;
float y12 = (CV[position+1][1] + CV[position+2][1]) / 2;
float x23 = (CV[position+2][0] + CV[position+3][0]) / 2;
float y23 = (CV[position+2][1] + CV[position+3][1]) / 2;
float x012 = (x01 + x12) / 2;
float y012 = (y01 + y12) / 2;
float x123 = (x12 + x23) / 2;
float y123 = (y12 + y23) / 2;
float x0123 = (x012 + x123) / 2;
float y0123 = (y012 + y123) / 2;
float dx = CV[3][0] - CV[0][0];
float dy = CV[3][1] - CV[0][1];
float d2 = fabs(((CV[1][0] - CV[3][0]) * dy - (CV[1][1] - CV[3][1]) * dx));
float d3 = fabs(((CV[2][0] - CV[3][0]) * dy - (CV[2][1] - CV[3][1]) * dx));
if((d2 + d3)*(d2 + d3) < tolerance * (dx*dx + dy*dy)) {
glBegin(GL_LINE_STRIP);
glVertex2f(x0123, y0123);
glEnd();
return;
}
float tmpLEFT[4][3];
float tmpRIGHT[4][3];
tmpLEFT[0][0] = CV[0][0];
tmpLEFT[0][1] = CV[0][1];
tmpLEFT[1][0] = x01;
tmpLEFT[1][1] = y01;
tmpLEFT[2][0] = x012;
tmpLEFT[2][1] = y012;
tmpLEFT[3][0] = x0123;
tmpLEFT[3][1] = y0123;
tmpRIGHT[0][0] = x0123;
tmpRIGHT[0][1] = y0123;
tmpRIGHT[1][0] = x123;
tmpRIGHT[1][1] = y123;
tmpRIGHT[2][0] = x23;
tmpRIGHT[2][1] = y23;
tmpRIGHT[3][0] = CV[3][0];
tmpRIGHT[3][1] = CV[3][1];
adaptiveDeCasteljau3(tmpLEFT, 0, tolerance);
adaptiveDeCasteljau3(tmpRIGHT, 0, tolerance);
}
and obviously nothing is drawn. Do you have any idea?
the Begin / End should engulf your whole loop, not being inside for each isolated vertex !
Given the following test code inside a function:
int orientation = 0; // increments up to 359, then loops to 0
int tempx, tempy;
float radians = orientation * (M_PI / 180);
tempx = point->x;
tempy = point->y;
tempx = (int) (tempx * cos(radians) - tempy * sin(radians));
tempy = (int) (tempx * sin(radians) + tempy * cos(radians));
tempx = tempx + origin->x;
tempy = tempy + origin->y;
With the following points (relative to origin): (-100, 0), (0, -100), (0, 100) I get this strange plot:
The blue and green lines are overlapping paths. The intersection at the middle (with the barely-visible yellow point) is the origin. All points are in the correct position when orientation is 0 or 180 but in a non-circular path at all other angles. I've done plenty of linear algebra in my time, but this has me stumped. I'm not sure if I'm overlooking something in C itself, or if I'm just not seeing the problem.
You are reusing tempx after rotating it. Try the following instead:
tempx = (int) (point->x* cos(radians) - point->y* sin(radians));
tempy = (int) (point->x* sin(radians) + point->y* cos(radians));
and see if that fixes things or not.
I am working on a countdown project that involves an arc. I've been struggling with the math for a few hours now and hope someone can help me. I have a 150px circle on which I want to draw arc that runs over starting at the top. The circle (ellipse really) is at 160,4
<Ellipse Height="150" HorizontalAlignment="Left" Margin="160,4,0,0"
Name="minutesClock" Stroke="Gainsboro" StrokeThickness="20"
VerticalAlignment="Top" Width="150" />
I have a method that is supposed to calculate the arc from the center top of the circle to whatever number of seconds is left
private void drawArc(int timevalue, int maxvalue)
{
Ellipse element = (Ellipse)LayoutRoot.FindName("minutesClock");
double top = element.Margin.Top;
double left = element.Margin.Left;
double width = element.Width;
double height = element.Height;
double strokeWidth = element.StrokeThickness;
double startX = left + (width / 2);
double startY = top + (strokeWidth / 2);
double radius = (width / 2) - (strokeWidth / 2);
double percent = (double)timevalue / (double)maxvalue;
Point center = new Point();
center.X = top + (height / 2);
center.Y = left + (width / 2);
Point newEnd = calculatePoint(radius, percent, center);
Path arc = (Path)LayoutRoot.FindName(what + "Arc");
PathFigure path = new PathFigure();
path.StartPoint = new Point(startX, startY);
ArcSegment arcSegment = new ArcSegment();
arcSegment.IsLargeArc = false;
Size arcSize = new Size(radius,radius);
arcSegment.Size = arcSize;
arcSegment.SweepDirection = SweepDirection.Clockwise;
arcSegment.Point = newEnd;
arcSegment.RotationAngle = 45;
path.Segments.Add(arcSegment);
PathGeometry pg = new PathGeometry();
pg.Figures = new PathFigureCollection();
pg.Figures.Add(path);
arc.Data = pg;
}
The arc starts at the right place but doesn't end up in the right place (the end point is all over the place). My calculatePoint code has to be where the error is. I figure it has something to do with
private Point calculatePoint(double radius, double percent, Point center)
{
double degrees = 90 - (360 * percent);
double radians = (Math.PI * degrees) / 180;
Point endPoint = new Point();
endPoint.X = center.X + (radius * Math.Sin(radians));
endPoint.Y = center.Y + (radius * Math.Cos(radians));
return endPoint;
}
Where am I going wrong?
You need to subtract the sinus (to go from the center "upwards" on the UI canvas):
endPoint.X = center.X - (radius * Math.Sin(radians));
Origin 0,0 is Top Left corner, not bottom left.
[Edit]
Oh and you are confusding x and y: think x is horizontal coordinated and y is vertical, so this is wrong:
center.X = top + (height / 2);
center.Y = left + (width / 2);
and this is wrong:
endPoint.X = center.X + (radius * Math.Sin(radians));
endPoint.Y = center.Y + (radius * Math.Cos(radians));
Corrected:
center.Y = top + (height / 2);
center.X = left + (width / 2);
and (with the subtraction fix I mentioned)
endPoint.Y = center.Y - (radius * Math.Sin(radians));
endPoint.X = center.X + (radius * Math.Cos(radians));
So I have a function here
void rotateLocal(GLfloat deg, GLfloat x, GLfloat y, GLfloat z)
where x, y, z are the coordinates of a local axis you want to rotate around for this object. I am using (0,1,0) for testing purposes, but yet it still ONLY rotates around world y instead of local y. Here is the logic for one vertex of an object in this function:
ax = x;
ay = y;
az = z;
//normalize
length = sqrt((ax * ax) + (ay * ay) + (az * az));
ux = ax / length;
uy = ay / length;
uz = az / length;
//square these
uxS = ux * ux;
uyS = uy * uy;
uzS = uz * uz;
getx = vertex[0];
gety = vertex[1];
getz = vertex[2];
//find new vertex points using rotation matrix for local axis
vertex[0] = (getx * (uxS + cos(deg) * (1 - uxS))) + (gety * (ux * uy * (1 - cos(deg)) - uz * sin(deg))) + (getz * (uz * ux * (1 - cos(deg)) + uy * sin(deg)));
vertex[1] = (getx * (ux * uy * (1-cos(deg)) + uz * sin(deg))) + (gety * (uyS + cos(deg) * (1 - uyS))) + (getz * (uy * uz * (1 - cos(deg)) - ux * sin(deg)));
vertex[2] = (getx * (uz * ux * (1-cos(deg)) - uy * sin(deg))) + (gety * (uy * uz * (1-cos(deg)) + ux * sin(deg))) + (getz * (uzS + cos(deg) * (1-uzS)));
is there something wrong with my rotation matrix? Am I using incorrect variable somewhere?
NOTE: I don't want to use RotateGL or anything like that, I want to do the matrix math myself.
The problem is that you are expressing the axis in world coordinates. What you have to do is:
Get the local to world transformation matrix of the object you want
to rotate.
Use the inverse of this matrix to convert the world axis (in your
example (0,1,0)) to local coordinates.
Use the converted axis to compute the rotation as you are doing in
the code.
I understand that the coordinates of the object are expressed in local space.
That's all.
I am a newbie hobbyist trying to program a box blur and I am having trouble with respect to edges. I am hoping that someone can spot the error.
The edges are black and I am assuming that it's because the borders are not being reflected properly. I am sure this has been discussed with a fix size kernel however I am using a variable sized kernel.
I am using the code found on another post --
Optimized float Blur variations
However I just do not understand the reflected borders portion.
I do not really care if optimized or not nor do I care about other kernel shapes, box shape will be just fine.
The code is
{// code from https://stackoverflow.com/questions/7860575/optimized-float-blur-variations
//--------------------------------------
int image_width ;
int image_height ;
int scale = 0;
int weight = (radius * 2) + 1;
int kernel_X = 0;
int kernel_Y = 0;
//--------------------------------------
float sum = 0.0;
int kernel_width = radius;//set both to the same to make the kernel square
int kernel_height = radius;//set both to the same to make the kernel square
// HORIZONTAL
for(iy = 0; iy < image_height ;iy++)
{
sum = 0.0;
// Process entire window for first pixel (including wrap-around edge)
for (kernel_X = 0; kernel_X <= kernel_width; kernel_X++)
{
if (kernel_X >= 0 && kernel_X < image_width)
//sum += src[iy * image_width ];
sum += src[iy * image_width + kernel_X];
}
//>-------------- border code does not reflect edges HELP!!
// Wrap watch for left side of image & resulting black bar
for (kernel_X = (image_width - kernel_width); kernel_X < image_width; kernel_X++)
{
// if (kernel_X >= 0 && kernel_X < image_width)// HORIZONTAL width = horizontal = X
// sum += src[iy * kernel_width + image_width ];//<-------------------enter tester formula here
// sum += src[iy + ix * image_width + kernel_X];//<-------------------FAIL
// sum += src[iy * kernel_width + image_width ];//<-------------------streaky
}
// Store first window
tmp[iy * image_width] = (sum / weight );
for(ix = 1; ix < image_width; ix++)
{
// Subtract pixel leaving window
if (ix - kernel_width - 1 >= 0)
sum -= src[iy * image_width + ix - kernel_width - 1];
// Add pixel entering window
if (ix + kernel_width < image_width)
sum += src[iy * image_width + ix + kernel_width];
else
sum += src[iy * image_width + ix + kernel_width - image_width];
tmp[iy * image_width + ix] = (sum / weight);//just for testing
}
}
// VERTICAL
for(ix = 0; ix < image_width; ix++)
{
sum = 0.0;
// Process entire window for first pixel
for (kernel_Y = 0; kernel_Y <= kernel_height; kernel_Y++)
{
if (kernel_Y >= 0 && kernel_Y < image_height)
sum += tmp[kernel_Y * image_width + ix];
}
//>-------------- border code does not reflect edges HELP!!
// Wrap watch for top side of image & resulting black bar
for (kernel_Y = image_height-kernel_height; kernel_Y < kernel_height; kernel_Y++)
{
//if (kernel_Y >= 0 && kernel_Y < image_height)
// sum += tmp[(iy + kernel_height - image_height) * image_width + ix];
}
for(iy=1;iy< image_height ;iy++)
{
// Subtract pixel leaving window
if (iy-kernel_height-1 >= 0)
sum -= tmp[(iy - kernel_height-1) * image_width + ix];
// Add pixel entering window
if (iy + kernel_height < image_height)
sum += tmp[(iy + kernel_height) * image_width + ix];
else
sum += tmp[(iy + kernel_height - image_height) * image_width + ix];
dst[ (scale * image_width * image_height) + (iy * image_width + ix) ] = (sum / weight);
}
}
}
I appreciate any help on this.
Thanks
John
edit here are some links of image examples of the edges.
image with proper box blur
http://img687.imageshack.us/img687/931/standardboxblur.jpg
Image with improper edges using the above code (notice dark bar on Top and Left edges, bottom and right are not quite right either)
http://img202.imageshack.us/img202/5137/boxblurbadedges.jpg
It might be easiest if you put your sampling into a separate routine which, given an x and y coordinate, returns the pixel value. You can then do some checks and clamp the x and y values to be between 0 and width and 0 and height, respectively. Then you can safely pass in negative values or values greater than width or height. It also allows you to more easily try other schemes like reflection, clamping to a color, extrapolation, etc. Simply swap out the sampling function that clamps with one that does some other behavior.