Calculate the maximum available area after TranslateTransform - c#

Let's say I want to draw a rectangle with an angle inside a windows form.
I can do this with
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.RotateTransform(20);
e.Graphics.DrawRectangle(Pens.Black, 0, 0, e.ClipRectangle.Width, e.ClipRectangle.Height);
}
but this will just rotate the rectangle and thus the left and bottom part is missing.
But what I really want to achive is that I want to draw the biggest possible rectangle with a certain angle inside my form like this
What's the best way to do this?

We can find the rectangle at angle θ which touches all four sides. If we let W, H be the width and height of our window, th the angle in radians which are all known beforehand. Now let a, b be the width and height of the rectangle we are trying to fit in the box, these are the two values we wish to find. Let x0,y0 be the coordinates of the point in the center of the window x0=W/2, y0=H/2. One of the corners of the rectangle will be
x1 = x0 - 0.5 * a * sin(th) + 0.5 * b * cos(th)
y1 = y0 + 0.5 * a * cos(th) + 0.5 * b * sin(th)
the other point are similar with different signs. A bit of trigonometry will show this.
For the rectangle to touch the sides we want
a * sin(th) + b cos(th) = W
a * cos(th) + b sin(th) = H
this gives us a pair of simultaneous equations which we can solve. Multiply the first by sin(th) and the second by cos(th)
a * sin(th) sin(th) + b cos(th) sin(th) = W sin(th)
a * cos(th) cos(th) + b sin(th) cos(th) = H cos(th)
subtract
a ( sin(th) sin(th) - cos(th) cos(th) ) = W sin(th) - H cos(th)
divide by ( sin(th) sin(th) - cos(th) cos(th) ) gives
a = (W sin(th) - H cos(th)) / ( sin(th) sin(th) - cos(th) cos(th) )
A similar process gives
b = (H sin(th) - W cos(th)) / ( sin(th) sin(th) - cos(th) cos(th) )
Once we have the a and b we can calculate the corners of the rectangles and draw it.
I've put the code in a jsfiddle http://jsfiddle.net/SalixAlba/5jcT7/ the code is
// canvas and mousedown related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// save canvas size to vars b/ they're used often
var W = canvas.width;
var H = canvas.height;
var spinner = $( "#startAng" ).spinner({
spin: function( event, ui ) {
if ( ui.value > 180 ) {
$( this ).spinner( "value", ui.value - 360 );
draw();
return false;
} else if ( ui.value < -180 ) {
$( this ).spinner( "value", ui.value + 360 );
draw();
return false;
}
draw();
}
});
var angle = 10.0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
angle = $("#startAng").spinner("value");
var th = Math.PI*angle/180.0;
var S = Math.sin(th);
var C = Math.cos(th);
var S2 = S*S - C*C;
var a = (W*S-H*C)/S2;
var b = (H*S-W*C)/S2;
var x0 = W/2;
var y0 = H/2;
//alert("angle "+angle+"S "+S+" "+C+" "+a+" "+b);
var x1 = x0 - 0.5 * a * Math.sin(th) + 0.5 * b * Math.cos(th);
var y1 = y0 + 0.5 * a * Math.cos(th) + 0.5 * b * Math.sin(th);
var x2 = x0 - 0.5 * a * Math.sin(th) - 0.5 * b * Math.cos(th);
var y2 = y0 + 0.5 * a * Math.cos(th) - 0.5 * b * Math.sin(th);
var x3 = x0 + 0.5 * a * Math.sin(th) + 0.5 * b * Math.cos(th);
var y3 = y0 - 0.5 * a * Math.cos(th) + 0.5 * b * Math.sin(th);
var x4 = x0 + 0.5 * a * Math.sin(th) - 0.5 * b * Math.cos(th);
var y4 = y0 - 0.5 * a * Math.cos(th) - 0.5 * b * Math.sin(th);
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x4,y4);
ctx.lineTo(x3,y3);
ctx.lineTo(x1,y1);
ctx.closePath();
ctx.stroke();
}
$( ".ui-spinner-input" ).on( "spinchange", draw );
$( ".ui-spinner-input" ).on( "spinstop", draw );
$( "#baseRad" ).on( "spin", draw );
draw();
Note this does not necessarily give the largest rectangle as there may be larger one which touch two sides, also there is some limit on the possible angle.

I did some sketching to solve this problem. The problem is more about math than programming, but here's my idea. I did in paint but try to follow:
When you rotate the rectangle, you need to calculate the newWidth and newHeight for it. Make a new rectangle with those dimensions, and place it to the upper left corner, like your current rectangle. The alpha α stands for the degree you want to rotate. After rotating, you need to move this new rectangle to right, the amounth of X. Then you have the biggest possible rectangle positioned perfectly at your drawing area.
Here's some idea for the code. I haven't tested it:
private void Form1_Paint(object sender, PaintEventArgs e)
{
double angle = 20;
double width = Convert.ToDouble(e.ClipRectangle.Width),
height = Convert.ToDouble(e.ClipRectangle.Height),
h = Math.Sqrt((width*width) + (height*height)),
y = (width/h);
int newHeight = Convert.ToInt32(height*y),
newWidth = Convert.ToInt32(width*y),
x = Convert.ToInt32((Math.Sin(angle) * height);
e.Graphics.RotateTransform(angle);
e.Graphics.DrawRectangle(Pens.Black, x, 0, newWidth, newHeight);
}

Related

c# - Rotating Points About Axis

I currently have code that will generate segment points and at each segment point will generate some points within a short cylinder around the point (in 3D, the segments positions all have a z value of 0.0F (however I'd like to have varying z-values that are in a line - i.e. it's still in a line, but in the line z = 3x for example), but the x, and y are randomised). However currently all the points generated are in a cylinder that is facing upwards, I want to be able to rotate the points such that the cylinder they are 'generated' in is facing in the direction between the two segments. Here's an image of what it should look like vs. what it currently looks like
I found this similar question about rotating points around an axis; I took the answer and used that code for my RotatePoints() function but it doesn't seem to work correctly and I'm not sure why. Below is my psuedo code, what would I need to do get this function working correctly? Is there a better way to do this? The points just need to be generated within a rotated cylinder so would a completely different method be more efficient and easier?
All I have is the location of each segment and each point stored as a Vector3 {x,y,z} in local space.
Psuedo-Code
double radius;
// Generates the positions where the points will be generated around
// These are just the x,y,z positions of the object in world space
Vector3[] segmentLocations = GenerateSegmentPositions(numSegments);
for (int i = 0; i < numSegments; i++) {
// Generates points in a cylinder facing up the +ve y-axis
// This works fine
Vector3[][] pointsAroundSegment = GeneratePoints(segmentLocations[i], radius);
if (i != numSegments - 1 && i > 0) {
// Generate a normalise direction vector for the new direction
Vector3 newDir = Vector3.Normalise(segmentLocations[i + 1] - segmentLocations[i]);
double theta = Vector3.AngleBetween(newDir - Vector3.Normalise(segmentLocations[i] - segmentLocations[i - 1]));
// Rotates points (this currently rotates the points so they 'should' be facing the new direction, I haven't yet modified this to face the halfway point)
// This doesn't work
pointsAroundSegment = RotatePoints(pointsAroundSegment, newDir, theta/2);
} else if (i == numSegments - 1) {
// Generate final point
// This works fine
pointsAboutSegment = GenerateFinalPoint(segmentLocations[i]);
}
}
// This is the actual rotation function
// RotatePoints() effectively just calls this for each point in the array
public static double[] Rotate(double x, double y, double z, double u, double v, double w, double theta) {
double[] c = new double[3];
c [0] = u * (u * x + v * y + w * z) * (1 - Math.Cos (theta)) + x * Math.Cos (theta) + (-w * y + v * z) * Math.Sin (theta);
c [1] = v * (u * x + v * y + w * z) * (1 - Math.Cos (theta)) + y * Math.Cos (theta) + (w * x - u * z) * Math.Sin (theta);
c [2] = w * (u * x + v * y + w * z) * (1 - Math.Cos (theta)) + z * Math.Cos (theta) + (-v * x + u * y) * Math.Sin (theta);
return c;
}
Answer courtesy of Poosh;
To rotate the point (x,y,z) about the line through (a,b,c) with the normalised (u^2 + v^2 + w^2 = 1) direction vector by the angle theta use the following function:
public static double[] Rotate(double x, double y, double z, double a, double b, double c, double nu, double nv, double nw, double theta) {
double[] rP = new double[3];
rP [0] = (a * (nv * nv + nw * nw) - nu * (b * nv + c * nw - nu * x - nv * y - nw * z)) * (1 - Math.Cos (theta)) + x * Math.Cos (theta) + (-c * nv + b * nw - nw * y + nv * z) * Math.Sin (theta);
rP [1] = (b * (nu * nu + nw * nw) - nv * (a * nu + c * nw - nu * x - nv * y - nw * z)) * (1 - Math.Cos (theta)) + y * Math.Cos (theta) + (c * nu - a * nw + nw * x - nu * z) * Math.Sin (theta);
rP [2] = (c * (nu * nu + nv * nv) - nw * (a * nu + b * nv - nu * x - nv * y - nw * z)) * (1 - Math.Cos (theta)) + z * Math.Cos (theta) + (-b * nu + a * nv - nv * x + nu * y) * Math.Sin (theta);
return rP;
}

draw an equilateral triangle C#

I have tried to adapt some code I came across to draw an equilateral triangle in c#
public void drawTriangle(PaintEventArgs e, int x, int y, int distance)
{
float angle = 0;
SolidBrush brs = new SolidBrush(Color.Green);
PointF[] p = new PointF[3];
p[0].X = x;
p[0].Y = y;
p[1].Y = (float)( x + distance * Math.Cos(angle + Math.PI / 3));
p[1].X = (float)( y + distance * Math.Sin(angle + Math.PI / 3));
p[2].Y = (float)( x + distance * Math.Cos(angle - Math.PI / 3));
p[2].X = (float)( y + distance * Math.Sin(angle - Math.PI / 3));
e.Graphics.FillPolygon(brs, p);
}
Unfortunately, this doesn't even come close. I have drawn equilateral triangles, but the points were always based on the centers of congruent circles. I am trying to find a simpler way. I am sure there must be an obvious problem with this code, but I am trying to learn the math needed as I go, so I don't know what it is. Thanks for your time.
Try this approach. I assume that for zero angle p[0] is left bottom vertex, p[1] is right bottom (the same horizontal).
(BTW, you have got strange mangling of Y/X)
p[0].X = x;
p[0].Y = y;
p[1].X = (float)( x + distance * Math.Cos(angle));
p[1].Y = (float)( y + distance * Math.Sin(angle));
p[2].X = (float)( x + distance * Math.Cos(angle + Math.PI / 3));
p[2].Y = (float)( y + distance * Math.Sin(angle + Math.PI / 3));

How to bound a circle inside an ellipse?

The title for this post was quite hard to think of, so if you can think of a more descriptive title please tell me. Anyway, my problem is quite specific and requires some simple maths knowledge. I am writing a C# WinForms application which is a bit like the old 'xeyes' Linux application. It basically is a set of eyes which follow around your mouse cursor. This may sound easy at first, however can get rather complicated if you're a perfectionist like me :P. This is my code so far (only the paint method, that is called on an interval of 16).
int lx = 35;
int ly = 50;
int rx;
int ry;
int wx = Location.X + Width / 2;
int wy = Location.Y + Height / 2;
Rectangle bounds = Screen.FromControl(this).Bounds;
// Calculate X
float tempX = (mx - wx) / (float)(bounds.Width / 2);
// Calculate Y
float tempY = (my - wy) / (float)(bounds.Height / 2);
// Draw eyes
e.Graphics.FillEllipse(Brushes.LightGray, 10, 10, 70, 100);
e.Graphics.FillEllipse(Brushes.LightGray, 90, 10, 70, 100);
// Draw pupils (this only draws the left one)
e.Graphics.FillEllipse(Brushes.Black, lx += (int)(25 * tempX), ly += (int)(40 * tempY), 20, 20);
Now this does work at a basic level, however sometimes this can happen if the user puts the cursor at 0,0.
Now my question is how to fix this? What would the IF statement be to check where the mouse pointer is, and then reduce the pupil X depending on that?
Thanks.
Edit: This is where I get the mouse positions (my and mx):
private void timer_Tick(object sender, EventArgs e)
{
mx = Cursor.Position.X;
my = Cursor.Position.Y;
Invalidate();
}
The timer is started in the eyes_Load event and the interval is 16.
Edit 2: Final solution: http://pastebin.com/fT5HfiQR
Modelling the eyeball as the following ellipse:
Its equation is:
And that of the line joining its center and the cursor:
(don't worry about the singularity)
We can then solve to get the intersection point:
Where
Now you can calculate the distance to the eyeball's edge, by dividing the distance from the center to the cursor by sigma. What remains is just interpolating to cap the position of the pupil:
The if statement you want is then
(N.B. for math-mo's out there the above was a slight simplification, which assumes your ellipse is not too narrow; the exact solution is non-analytical)
EDIT: my tests in VB.NET:
EDIT 2: C# port
PointF Bound(double xc, double yc, double w, double h, double xm, double ym, double r)
{
double dx = xm - xc, dy = ym - yc;
if (Math.Abs(dx) > 0.001 && Math.Abs(dy) > 0.001)
{
double dx2 = dx * dx, dy2 = dy * dy;
double sig = 1.0 / Math.Sqrt(dx2 / (w * w * 0.25) + dy2 / (h * h * 0.25));
double d = Math.Sqrt(dx2 + dy2), e = d * sig;
if (d > e - r)
{
double ratio = (e - r) / d;
return new PointF((float)(xc + dx * ratio),
(float)(yc + dy * ratio));
}
}
return new PointF((float)xm, (float)ym);
}
xc, yc: Center coordinates of the ellipse
w, h: Width and height of the ellipse
xm, ym: Mouse coordinates
r: Radius of the circle you wanna constrain (the pupil)
Returns: The point where you wanna place the center of the circle
EDIT 3: Many thanks to Quinchilion for the following optimization (gawd damn this smacked me hard in the face)
PointF Bound(double xc, double yc, double w, double h, double xm, double ym, double r)
{
double x = (xm - xc) / (w - r);
double y = (ym - yc) / (h - r);
double dot = x*x + y*y;
if (dot > 1) {
double mag = 1.0 / Math.Sqrt(dot);
x *= mag; y *= mag;
}
return new PointF((float)(x * (w - r) + xc), (float)(y * (h - r) + yc));
}

Slope of a line using houghline transformation c# Aforge.Net

I used the code from aforge.net library
HoughLineTransformation lineTransform = new HoughLineTransformation( );
// apply Hough line transofrm
lineTransform.ProcessImage( sourceImage );
Bitmap houghLineImage = lineTransform.ToBitmap( );
// get lines using relative intensity
HoughLine[] lines = lineTransform.GetLinesByRelativeIntensity( 0.5 );
foreach ( HoughLine line in lines )
{
// get line's radius and theta values
int r = line.Radius;
double t = line.Theta;
// check if line is in lower part of the image
if ( r < 0 )
{
t += 180;
r = -r;
}
// convert degrees to radians
t = ( t / 180 ) * Math.PI;
Console.WriteLine("{0},{1}",t,r)
// get image centers (all coordinate are measured relative
// to center)
int w2 = image.Width /2;
int h2 = image.Height / 2;
double x0 = 0, x1 = 0, y0 = 0, y1 = 0;
if ( line.Theta != 0 )
{
// none-vertical line
x0 = -w2; // most left point
x1 = w2; // most right point
// calculate corresponding y values
y0 = ( -Math.Cos( t ) * x0 + r ) / Math.Sin( t );
y1 = ( -Math.Cos( t ) * x1 + r ) / Math.Sin( t );
}
else
{
// vertical line
x0 = line.Radius;
x1 = line.Radius;
y0 = h2;
y1 = -h2;
}
// draw line on the image
Drawing.Line( sourceData,
new IntPoint( (int) x0 + w2, h2 - (int) y0 ),
new IntPoint( (int) x1 + w2, h2 - (int) y1 ),
Color.Red );
}
I added Console.WriteLine("{0},{1}",t,r) for the code to display slope and radius of lines.
In the output, the value of slope is either 45 or 135 degrees for all the lines in the image, though the image has different line slopes.
What could be the reason? Please help...
There are no compilation errors in the code.
I think that you forget to apply a edge detection Filter, like Sobel. The Hough Line of Aforge uses every not black pixel to calculate a Line. If you have a large colored area, you will have many Lines with 45 and 135 slope degree.
Hope I understood what your question was.

Draw a circle on a map by radius and angle

Im using Microsoft visual studio 2010, with the refernce dynamic data display.
I would like to draw a circle on a map, i have 2 points, one of them is the center of the circle and the other is the point on the circle, the distance between them is the radius of the circle.
the result should look like this :
http://sizmedia.com/my.php?i=mjmynzim2nhy.png
my result when I draw a circle with one point and const distance is like this (distance = radius = 15):
http://sizmedia.com/my.php?i=hm2zuv5yyenj.png
***** I don't care if the circle will look like my result(the ellipse)
because as I understood the earth is circle and its type of reasonable. ****
but when I draw circle with distance between 2 point (distance = 3400 +) I can't see the circle that I draw.
I would love to get some help, there is my code to find distance between 2 points.
// Calculating the distance between the two points
double dLat = (ps.X - centerPoint.X) / 180 * Math.PI;
double dLong = (
double.Parse(this.plotter.Viewport.Transform.DataTransform.ViewportToData(ps).Y.ToString()) -
double.Parse(this.plotter.Viewport.Transform.DataTransform.ViewportToData(centerPoint).Y.ToString())) / 180 * Math.PI;
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2)
+ Math.Cos(ps.X / 180 * Math.PI) * Math.Cos(pointLine1.X / 180 * Math.PI)
* Math.Sin(dLong / 2) * Math.Sin(dLong / 2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
//Calculate radius of earth
double radiusE = 6378135; // Equatorial radius, in metres
double radiusP = 6356750; // Polar Radius
//Numerator part of function
double nr = Math.Pow(radiusE * radiusP * Math.Cos(ps.X / 180 * Math.PI), 2);
//Denominator part of the function
double dr = Math.Pow(radiusE * Math.Cos(ps.X / 180 * Math.PI), 2)
+ Math.Pow(radiusP * Math.Sin(ps.X / 180 * Math.PI), 2);
double radius = Math.Sqrt(nr / dr);
//Calculate distance in meters.
distance = (radius * c); // resualt in meters
distance /= 1000; // resualt in KM
And there is my code to add the circle :
while (a < 360) // Doing one round around the point (The angels)
{
// Get the X position of the pointClicked
cx = (double)prePs.X;
// Get the Y position of the pointClicked
cy = double.Parse(this.plotter.Viewport.Transform.DataTransform.ViewportToData(prePs).Y.ToString());
// Get the new X position of the pointClicked by the angel with math calculation
xEndP = (float)(distance * Math.Cos(a * Math.PI / 180F)) + cx;
// Get the new Y position of the pointClicked by the angel with math calculation
yEndP = (float)(distance * Math.Sin(a * Math.PI / 180F)) + cy;
// Creating the new point
globalPoint = new DraggablePoint(new Point(xEndP, yEndP));
globalPoint.Position = new Point(xEndP, yEndP);
globalPoint.Visibility = Visibility.Visible;
// Increas the angel
a++;
//Creat new point on the circle with new angel
xEndPNext = (float)(distance * Math.Cos(a * Math.PI / 180F)) + cx;
yEndPNext = (float)(distance * Math.Sin(a * Math.PI / 180F)) + cy;
// Creat line between the two new points that we creat now
segmentHelper = new Segment(new Point(xEndP, yEndP), new Point(xEndPNext, yEndPNext));
// Brush between the points by line
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(255, 47, 79, 49);
segmentHelper.Stroke = mySolidColorBrush;
// Add the line to the chartplotter
plotter.Children.Add(segmentHelper);
// Add the angel
a++;
}
My algorithm is take one point, and the next point and to draw line between them ( when the points are visiblty false) and then i get a nice circle.
Thank you very much :)

Categories