PDFlib - rotating objects around center by arbitrary degree - pdflib

Task: I need to to rotate a square, rectangle or triangle by arbitrary angle around its center as pivot.
PDFlib documentation states that rotation always occurs around object's bottom-left corner.
This is my attempt to rotate a square around its center:
// Gray square positioned before rotation
$p->setcolor('stroke', '#999999', null, null, null, null);
$p->rect(200, 200, 100, 100);
$p->stroke();
// Red square rotated by 45 degrees, around its center as pivot
$p->setcolor('stroke', '#ff0000', null, null, null, null);
$p->save();
$p->translate(200 - 20.71, 200 - 50); // 20.71 is the calculation product
$p->rotate(360 - 45);
$p->rect(0, 0, 100, 100);
$p->stroke();
$p->restore();
Result:
That was simple enough.
However, I need to be able to rotate rectangle by arbitrary degree around its center. Things get complicated quickly with geometry :-)
Question: Does PDFlib support rotation of any object by arbitrary degree around its center?

Well, PDFlib won't do geometry for you. Here's the code to calculate offsets for the rotated rectangle:
$x = 10;
$y = 10;
$width = 80;
$height = 40;
$angle = -33;
$centerX = $width / 2;
$centerY = $height / 2;
// Diagonal angle
$diagRadians = atan($width / $height);
$diagAngle = rad2deg($diagRadians);
// Half Length diagonal
$diagHalfLength = sqrt(pow($height, 2) + pow($width, 2)) / 2;
// Center coordinates of rotated rectangle.
$rotatedCenterX = sin($diagRadians + deg2rad($angle)) * $diagHalfLength;
$rotatedCenterY = cos($diagRadians + deg2rad($angle)) * $diagHalfLength;
$offsetX = $centerX - $rotatedCenterX;
$offsetY = $centerY - $rotatedCenterY;
$p->setcolor('stroke', '#ff00ff', null, null, null, null);
$p->save();
$x += $offsetX;
$y -= $offsetY;
$p->translate($x, $y);
// Place a dot at rotated rectangle center
$p->circle($rotatedCenterX, $rotatedCenterY * -1, 5);
$p->stroke();
$p->rotate(360 - $angle);
$p->rect(0, 0, $width, $height);
$p->stroke();
$p->restore();

Related

How can I get polar coordinates represented properly?

I have the following hexagonal grid and trying to calculate the degrees to each edge hexagon from the center (light blue):
The blue highlighted hex is correct at 0 degrees and that quadrant (lower right) is correct. Here is my angle calculation method:
private static function calculateAngle(hex1:Hexagon, hex2:Hexagon):Number {
// hex1 is always passed in as the grid center or start
var diffY:Number = Math.abs(hex2.center.y) - Math.abs(hex1.center.y);
var diffX:Number = Math.abs(hex2.center.x) - Math.abs(hex1.center.x);
var radians:Number = Math.atan(diffY / diffX);
return radians * 180 / Math.PI;
}
Why are the remaining angles (text in each hexagon) incorrect?
You're really close to correct; you just need to compensate for the periodicity of atan. The standard way to do this is to use atan2, which returns a signed angle in (-pi, pi] instead of an unsigned angle in [0, pi). You can do so like this:
var radians:Number = Math.atan2(
hex2.center.y - hex1.center.y, hex2.center.x - hex1.center.x);
Note that I didn't include the call to abs in there: the signedness of those values is needed for atan2 to know which quadrant its in!
Edit: if you're looking for an angle in [0, pi], which represents the minimum angle between the center hex and the blue-highlighted hex, you can just take the absolute value of the result of atan2: return Math.abs(radians) * 180 / Math.PI; the question leaves it a little unclear as to which one you're asking for.

Libgdx Tiled map object to box2d body location

A Tiled map object hat a position x, y in pixels and a rotation in degrees.
I am loading the coordinates and the rotation from the map and trying to assign them to a box2d Body. There are a couple of differences between the location models, for example Tiled object rotation is in degrees and box2d body angle is in radians.
How do I convert the location to the BodyDef coordinates x, y and angle so that the body will be created at the correct position?
Background:
Using the code:
float angle = -rotation * MathUtils.degreesToRadians;
bodyDef.angle = angle;
bodyDef.position.set(x, y);
Works when the rotation is 0, but the body is not positioned correctly when rotation is different than 0.
I found a couple of hints here:
http://www.tutorialsface.com/2015/12/qu ... dx-solved/
and here:
https://github.com/libgdx/libgdx/issues/2742
That seem to tackle this exact problem, however neither solution worked for me, the body objects are still positioned wrong after applying those transformations. By positioned wrong I mean that the body is positioned in the area of the map where it should be but slightly off depending on its rotation.
I feel that it should be pretty simple but I do not know how to mediate the differences between Tiled and box2d locations.
For reference these are the two solutions I tried from the links above (after transforming the values x, y, width, height from pixels to world units):
float angle = rotation * MathUtils.degreesToRadians;
bodyDef.angle = -angle;
Vector2 correctionPosition = new Vector2(
height * MathUtils.cosDeg(-rotation - 90),
height + height * MathUtils.sinDeg(-rotation - 90));
bodyDef.position.set(x, y).add(correctionPosition);
and
float angle = rotation * MathUtils.degreesToRadians;
bodyDef.angle = -angle;
// Top left corner of object
Vector2 correctedPosition = new Vector2(x, y + height);
// half of diagonal for rectangular object
float radius = (float)Math.sqrt(width * width + height * height) / 2.0f;
// Angle at diagonal of rectangular object
float theta = (float)Math.tanh(height / width) * MathUtils.degreesToRadians;
// Finding new position if rotation was with respect to top-left corner of object.
// X=x+radius*cos(theta-angle)+(h/2)cos(90+angle)
// Y=y+radius*sin(theta-angle)-(h/2)sin(90+angle)
correctedPosition = correctedPosition
.add(
radius * MathUtils.cos(theta - angle),
radius * MathUtils.sin(theta - angle))
.add(
((height / 2) * MathUtils.cos(MathUtils.PI2 + angle)),
(-(height / 2) * MathUtils.sin(MathUtils.PI2 + angle)));
bodyDef.position.set(correctedPosition);
Any hint would be highly welcomed.
Found the correct solution, lost about 1 day of my life :)
The information from above links is incorrect and/or outdated. Currently Tiled saves the object position depending on it's type. For an image is relative to bottom-left position.
Box2d doesn't really have an "origin" point, but you can consider is its center and the shapes of the fixtures attached to the body should be positioned relative to (0,0).
Step 1: Read tiled properties
float rotation = textureMapObject.getRotation();
float x = textureMapObject.getX();
float y = textureMapObject.getY();
float width = textureMapObject.getProperties()
.get("width", Float.class).floatValue();
float height = textureMapObject.getProperties()
.get("height", Float.class).floatValue();
Step 2: Scale these acording to your box2d world size, for example x = x * 1/25; etc.
Step 3: Create a body without any position or angle.
Step 4: Transform body position and angle with:
private void applyTiledLocationToBody(Body body,
float x, float y,
float width, float height,
float rotation) {
// set body position taking into consideration the center position
body.setTransform(x + width / 2, y + height / 2, 0);
// bottom left position in local coordinates
Vector2 localPosition = new Vector2(-width / 2, -height / 2);
// save world position before rotation
Vector2 positionBefore = body.getWorldPoint(localPosition).cpy();
// calculate angle in radians
float angle = -rotation * MathUtils.degreesToRadians;
// set new angle
body.setTransform(body.getPosition(), angle);
// save world position after rotation
Vector2 positionAfter = body.getWorldPoint(localPosition).cpy();
// adjust position with the difference (before - after)
// so that the bottom left position remains unchanged
Vector2 newPosition = body.getPosition()
.add(positionBefore)
.sub(positionAfter);
body.setTransform(newPosition, angle);
}
Hope it helps.

AS3 - How to Draw and Align a Circle?

I want to draw a circle and align it center. My code doesn't do that:
var circle:Shape = new Shape(); // The instance name circle is created
circle.graphics.beginFill(0x990000, 1); // Fill the circle with the color 990000
circle.graphics.lineStyle(2, 0x000000); // Give the ellipse a black, 2 pixels thick line
circle.graphics.drawCircle((stage.stageWidth - 100) / 2, (stage.stageHeight - 100) / 2, 100); // Draw the circle, assigning it a x position, y position, raidius.
circle.graphics.endFill(); // End the filling of the circle
addChild(circle); // Add a child
drawCircle((stage.stageWidth - 100) / 2, (stage.stageHeight - 100) / 2, 100);
The first two parameters of drawCircle are the X and Y position of the center of the circle, not the top left position of the circle.
If you want your circle in the center of the stage, you just need to put the circle’s center at the same position, so you’d call drawCircle like this:
drawCircle(stage.stageWidth / 2, stage.stageHeight / 2, 100);
I think your approach, although it would work, will just make working with your shape a bit tougher.
consider this approach :
var circle:Shape = new Shape();
circle.graphics.clear();
circle.graphics.lineStyle(2,0x000000);
circle.graphics.beginFill(0x990000);
circle.graphics.drawCircle(0,0,100);
circle.graphics.endFill();
addChild(circle);
circle.x = stage.stageWidth / 2;
circle.y = stage.stageHeight/ 2;
By drawing the circle centered on the 0,0 location in your shape and then placing it via the x and y properties is a far better approach. Suppose you want to move that circle ? Trying to figure out the offsets would be a nightmare.

How do I calculate the location of points in a rotated shape AS3

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.

Hit testing pseudo 3d space

So I am writing a little mini pseudo 3d engine for the canvas element in html5. In the code below I am drawing a bunch of squares with varying positions and rotations (rotation around the z axis, so no deformation)
Now I want to be able to tell which square the user clicks on. In the objects array the items are supported by the z position starting with the squares the furthest away from the camera (so that they draw properly). So given a 3d point relative to the top left of the corner of the canvas how can I tell which square was clicked?
//Draw objects
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
var cz = object.position.z - camera.position.z;
if (cz > 0) {
cz = 1 / ((cz - 1) * 0.75 + 1);
context.save();
context.translate(halfWidth, halfHeight); //viewport transform
context.scale(1, -1); //invert y axis
context.scale(cz, cz); //perspective
context.translate(-camera.position.x, -camera.position.y); //camera transform
context.translate(object.position.x, object.position.y); //world transform
context.rotate(object.rotation);
context.fillStyle = object.color;
context.fillRect(-40, -40, 80, 80);
context.restore();
}
}
P.S. If I am doing anything weird or backwards and you know of a way to improve, I would love to hear suggestions
I would suggest that you draw the objects with the same transformations to a hidden canvas of the same size, but give each square a unique color (maybe derived from the index i).
You would do that like this:
var col = index.toString(16); // convert to hex
while (col.length < 6) col = "0"+col; // pad leading 0s
ctx.fillStyle = "#"+col;
ctx.fillRect(-40,-40,80,80);
Then when you get a mouseclick event on the visible canvas, look at that location in your hidden one to get the color (index) of the selected object:
var colData = ctx.getImageData(clickX, clickY, 1, 1).data;
var index = (colData[2]<<16) | (colData[1]<<8) | colData[0];
This will work for up to 16M objects and is fairly simple.