double degPi = degrees * Math.PI / 180;
double a = Math.cos(degPi)*tImgCover.getScaledHeight();
double b = Math.sin(degPi)*tImgCover.getScaledWidth();
double c = -Math.sin(degPi) * tImgCover.getScaledHeight();
double d = Math.cos(degPi)* tImgCover.getScaledWidth();
double e = absX;
double f = absY;
contentByte.addImage(imgae, a, b, c, d, e, f);/*add image*/
How to rotate around the image center by itext?
If we have an Image image and coordinates x, y, we can draw the image without rotation with its lower left corner at the given coordinates like this
contentByte.addImage(image, image.getWidth(), 0, 0, image.getHeight(), x, y);
A bitmap image from the resources has a size of 1x1 with the coordinate origin at its lower left. Thus, this operation stretches the image to its correct size and moves it so its lower left is at the given coordinates.
If we want to draw the same image as if the one drawn above was rotated around its center by an angle rotate, therefore, we can do this by moving the 1x1 image so that the origin is in its center, stretch it to its correct size, rotate it, and then move the origin (which still is at the center of the rotated image) to the center of the unrotated image. These operations are easier to express using AffineTransform instances (from package com.itextpdf.awt.geom) instead number tupels. Thus:
// Draw image as if the previous image was rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Stretch it to its dimensions
AffineTransform B = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform C = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform D = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedImage)
For example drawing both images using
int x = 200;
int y = 300;
float rotate = (float) Math.PI / 3;
results in something like this:
With a Flip
The OP asked in a comment
how to add rotate and flip image?
For this you simply insert a mirroring affine transformation into the sequence of transformations above.
Unfortunately the OP did not mention which he meant a horizontal or a vertical flip. But as changing the rotation angle accordingly transforms one in the other, that isn't really necessary, either.
// Draw image as if the previous image was flipped and rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Flip it horizontally
AffineTransform B = new AffineTransform(-1, 0, 0, 1, 0, 0);
// Stretch it to its dimensions
AffineTransform C = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform D = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform E = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
M.preConcatenate(E);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedFlippedImage)
The result with the same image as above:
With Interpolation
The OP asked in a yet another comment
How anti aliasing ?
The iText Image class knows an Interpolation property. By setting it to true (before adding the image to the document, obviously),
image.setInterpolation(true);
low resolution images are subject to interpolation when drawn.
E.g. using a 2x2 image with differently colored pixels instead of the image of Willi, you get the following results, first without interpolation, then with interpolation:
Confer the AddRotatedImage.java test testAddRotatedInterpolatedImage which adds this image:
Beware: iText Image property Interpolation effectively sets the Interpolate entry in the PDF image dictionary. The PDF specification notes in this context:
NOTE A conforming Reader may choose to not implement this feature of PDF, or may use any specific implementation of interpolation that it wishes.
Thus, on some viewers interpolation may occur differently than in your viewer, maybe even not at all. If you need a specific kind of interpolation on every viewer, upscale the image with the desired amount of interpolation / anti-aliasing before loading it into an iText Image.
public static BufferedImage rotateClockwise90( BufferedImage inputImage ){
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage returnImage = new BufferedImage( height, width , inputImage.getType() );
for( int x = 0; x < width; x++ ) {
for( int y = 0; y < height; y++ ) {
returnImage.setRGB( height-y-1, x, inputImage.getRGB( x, y ) );
}
}
return returnImage;
}
Related
I'm playing a game and I want to draw a line on it. I can't use Graphics.DrawLine because it takes the window/screen coordinates, not the game coordinates.
I want to draw a line from game's pos A to games's pos B. If I put those coordinates in DrawLine, it will take X's and Y's coordinates of the window/screen, not from the game.
In the image below as a example, I want to draw the blue line, but using DrawLine it will draw the grey line.
I want to draw even if the points were not visible in the screen, as I showed in this example. If I move the screen through the game's scenario, the line keeps static since the coordinates of the points A and B keep the same.
Is there a way to do it?
The point here is to convert the world coordinates into screen coordinates. For example, suppose I want to draw a line from point x = 100, y = 600 to point x = 700, y 600 in the world map and the left of my screen is at x = 300 and it's bottom is at y = 300 in world coordinates, then the drawing should start from x = -200, y = 300 to x = 400, y = 300 in screen coordinates which would finish the drawed line at the center of the screen, assuming it's resolution is 800x600.
Since the screen moves in relation to the world scenario, the code for the world to screen method could be:
static int[] WorldToScreen(int worldX, int worldY, int worldX2, int worldY2, int screenLeft, int screenBottom)
{
int screenX = worldX - screenLeft;
int screenY = worldY - screenBottom;
int screenX2 = worldX2 - screenLeft;
int screenY2 = worldY2 - screenBottom;
return new int[] { screenX, screenY, screenX2, screenY2 };
}
Now we just take these converted coordinates and draw on the screen using GDI, DirectX or whatever you want.
PS: Screen (or camera) coordinates are usually related to the center of the screen. I used the edges here just for simplification.
Basically I want to take a fixed straight line across the devices point of view and determine if anything intercepts it but in my example I want to make the "laser line" configurable with regards to the distance from the top of the field of view.
Now it's easy enough to get the depth data at a given pixel point simply by doing this.
var depthInMM = DepthImagePixel.Depth;
and its also easy to simply say I want to focus on the 100th line of pixels from the top by doing something like this.
for (int i = 0; i < this._DepthPixels.Length; ++i) //_DepthPixels.Length is obviously 307200 for 640x480
{
if (i >= 64000 && i <= 64640) //Hundredth vertical pixel line
{
//Draw line or whatever
}
}
Which ends up with something like this.
BUT for example I might want to have the line intercept at 50 cm from the top of the field of view at 3 meters depth. Now obviously I understand that as the depth increases so does the area represented but I cannot find any reference or myself work out how to calculate this relationship.
So, how can one calculate the coordinate space represented at a given depth utilizing the Kinect sensor. Any help sincerely appreciated.
EDIT:
So if I understand correctly this can be implemented as such in C#
double d = 2; //2 meters depth
double y = 100; //100 pixels from top
double vres = 480; //480 pixels vertical resolution
double vfov = 43; //43 degrees vertical field of view of Kinect
double x = (2 * Math.Sin(Math.PI * vfov / 360) * d * y) / vres;
//x = 0.30541768893691434
//x = 100 pixels down is 30.5 cm from top field of view at 2 meters depth
2 sin(PI VFOV / 360) D Y
X = --------------------------
VRES
X: distance of your line from the top of the image in meters
D: distance - orthogonal to the image plane - of your line from the camera in meters
Y: distance of your line from the top of the image in pixels
VRES: vertical resolution of the image in pixels
VFOV: vertical field of view of the camera in degrees
I have a set of images of various objects of different shapes and sizes. They have transparent backgrounds set on them but the full dimension of the image is a square. I want to calculate a box of coordinates (upper left x/y, lower right x/y) that encompasses the object in the image while ignoring as much of the transparent background as possible. And I need to do this on the fly in code.
Is there an example, or a library, available for C# that would allow me to do this? I am using these in a website where several objects are dynamically overlaid into a single image and I want to calculate an image map with coordinates for each object in the merged image. Using the full size of the square image creates huge overlaps in the coordinate sets and often the last in coordinates hide the lower object from being clickable.
Well, using System.Drawing.Bitmap this is not too hard (the following certainly is not the most performant way):
// we will store actual bounds in here
int left, right, top, bottom;
using (Bitmap b = ...) // open image here
{
var pixelsX = Enumerable.Range(0, b.Width);
var pixelsY = Enumerable.Range(0, b.Height);
left = pixelsX.FirstOrDefault(
x => pixelsY.Any(y => b.GetPixel(x, y).A != 0));
right = pixelsX.Reverse().FirstOrDefault(
x => pixelsY.Any(y => b.GetPixel(x, y).A != 0));
top = pixelsY.FirstOrDefault(
y => pixelsX.Any(x => b.GetPixel(x, y).A != 0));
bottom = pixelsY.Reverse().FirstOrDefault(
y => pixelsX.Any(x => b.GetPixel(x, y).A != 0));
}
Notice that all these 4 coordinates are "inclusive" bounds (meaning: the row/column of pixels they represent does contain at least one non-transparent pixel), so if you should calculate width and height of your new bounds do it like this:
int width = right - left + 1;
int height = bottom - top + 1;
By the way, for an entirely transparent image, all 4 coordinates should be 0, as a result width and height will both be 1 - I guess this is not a problem for you.
Consider the two images below (original and transformed respectively). The three blue squares (markers) are used for orientation.
Original Image:
We know the width, height
We know the (x,y) coordinates of all three markers.
Transformed Image:
We can detect the (x,y) coordinates of all three markers.
As a result, we can calculate the angle of rotation, the amount of (x,y) translation and the (x,y) scaling factor.
I now want to use the System.Drawing.Graphics object to perform RotateTransform, TranslateTransform and ScaleTransform. The trouble is, the resulting image is NEVER like the original.
I've been told on stack overflow that the order of applying transformations does not matter but my observation is different. Below is some code that generates an original image and attempts to draw it on a new canvas after introducing some transformations. You can change the order of the transformations to see different results.
public static void GenerateImages ()
{
int width = 200;
int height = 200;
string filename = "";
System.Drawing.Bitmap original = null; // Original image.
System.Drawing.Bitmap transformed = null; // Transformed image.
System.Drawing.Graphics graphics = null; // Drawing context.
// Generate original image.
original = new System.Drawing.Bitmap(width, height);
graphics = System.Drawing.Graphics.FromImage(original);
graphics.Clear(System.Drawing.Color.MintCream);
graphics.DrawRectangle(System.Drawing.Pens.Red, 0, 0, original.Width - 1, original.Height - 1);
graphics.FillRectangle(System.Drawing.Brushes.Blue, 10, 10, 20, 20);
graphics.FillRectangle(System.Drawing.Brushes.Blue, original.Width - 31, 10, 20, 20);
graphics.FillRectangle(System.Drawing.Brushes.Blue, original.Width - 31, original.Height - 31, 20, 20);
filename = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Original.png");
original.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
graphics.Dispose();
// Generate transformed images.
transformed = new System.Drawing.Bitmap(width, height);
graphics = System.Drawing.Graphics.FromImage(transformed);
graphics.Clear(System.Drawing.Color.LightBlue);
graphics.ScaleTransform(0.5F, 0.7F); // Add arbitrary transformation.
graphics.RotateTransform(8); // Add arbitrary transformation.
graphics.TranslateTransform(100, 50); // Add arbitrary transformation.
graphics.DrawImage(original, 0, 0);
filename = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Transformed.png");
transformed.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
graphics.Dispose();
transformed.Dispose();
original.Dispose();
System.Diagnostics.Process.Start(filename);
}
I can see two potential issues here:
Since the transformations are being applies one after another, they render the originally calculated values useless.
The graphics object applies rotation at the (0, 0) coordinate where as I should be doing something different. Not sure what.
From what I understand from here, here, and here, the Graphics.Drawing transformations are performed by multiplying matrices together in the order in which you apply the transformations.
With integers, a*b*c = b*a*c
However, with matricies, ABC almost never equals BAC.
So, it appears the order of transformations does matter, since matrix multiplication is not commutative.
Put another way, it seems that if I do the following on your picture:
case 1:
translate (100,50)
scale (0.5,0.7)
picture ends up with top-left corner at: (100,50)
and bottom-right corner at: (200,190)
case 2:
scale (0.5,0.7)
translate (100,50)
picture ends up with top-left corner at: (50,35)
and bottom-right corner at: (150,174)
This means that by scaling first, and then translating, that the scaling will also scale the amount of translation, that is why in case two the the picture ended up at (50,35) for the top left corner, half of the translated X and .7 of the translated Y.
How to draw the spring like shape using c# drawing class
alt text http://img812.imageshack.us/img812/373/spring.jpg
First of all you'd need to think of a formula that would represent the spring. You could draw a circle and as you're going around it, let the X increase a bit. For instance:
for (double i = 0; i < 50; i += 0.01)
{
int x = (int)(Math.Sin(i) * 10 + i * 3);
int y =(int)(Math.Cos(i) * 10 + 50);
}
See the i variable there as time, and the result x and y the coordinates to draw; you'd traverse the path of the spring in small steps.
You could then create a new Bitmap and use the SetPixel method on those coordinates, and in the OnPaint method of your form, draw the bitmap on it.
If you're any good with math (I'm not :P) you might be able to only plot pixels inside the bitmap - the above example doesn't solve the problem of the minimum and maximum values for i.
This is more of a math problem than a C# one. What you want is to derive a Parametric equation for the curve you wish to draw.
With that go and fill an array of Point objects with values for the parametric equation on a certain interval with a certain step (the smaller the step the more the final drawing will look like the actual shape). Then you can use g.DrawLines (MSDN: DrawLines) to draw the actual curve on a surface.
You can edit the width, color and other properties of the line by modifying parameters of the Pen object.
Your actual code would look like this:
void DrawSpring (Graphics g)
{
List<Point> points = new List<Point>();
double step = 0.01;
for(double t = -2; t < 2; t += step)
{
Point p = new Point();
p.X = XPartOfTheEquation(t);
p.Y = YPartOfTheEquation(t);
points.Add(p);
}
g.DrawLines(new Pen(new SolidBrush(Color.Black), 2f), points.ToArray());
}