In my Flash program I have a step where I want to rotate a displayObject around the center of its container.
As some of you may know, Flash has a default center point for rotation which is the top left corner, and doesn't fit for my case.
To achieve my specific rotation, I do 3 successive transformations using matrices, like this:
public function rotateAroundCenter(object:DisplayObject, container:DisplayObject, angleDegrees:Number):void {
var matrix:Matrix = object.transform.matrix;
var rect:Rectangle = object.getBounds(container);
matrix.translate(-(rect.left + (rect.width / 2)), -(rect.top + (rect.height / 2)));
matrix.rotate((angleDegrees / 180) * Math.PI);
matrix.translate(rect.left + (rect.width / 2), rect.top + (rect.height / 2));
object.transform.matrix = matrix;
}
This bit of code does the trick and I can rotate displayObjects around their container center like I want to.
Problem: For some of these objects (couldn't find a discriminating factor between those who work and those who don't), any time I try to apply a 180 degrees rotation to put them upside down using previous bit of code, Flash unloads the SWF, which seems very much like a crash to me. I only get this crash for some of these objects, but if they crash once they crash anytime I apply a 180 degrees rotation using my function.
I suspect a memory leak, but then, if 90 or 270 degrees rotations work, why would this specific case make my whole program crash?
Any clues about this issue will be very appreciated. Thanks!
Related
I have composited my sprites to build a monster truck with customizable bumpers, cabs, spoilers wheels etc. The class that holds these Sprites is MTruck and I can draw it perfectly provided I stay with scale 1.0.
mWheels.setPosition(posX + 17 * scale, posY);
mCab.setPosition(posX + 22 * scale, posY + 7 * scale);
mFender.setPosition(posX, posY + 75 * scale);
mWheels is positioned at the y origin of the Truck and mFender at the x origin.
I've tried all sorts of values for scale and extracting it separate from the scale I apply to mWheels, mCab etc but all that happens is the sprites scale but their positions become misaligned.
I'm going to have to render to a texture and scale that as I whole if I can't crack this.
perhaps, Set origin could help you:
void setOrigin(float originX, float originY)
Sets the origin in relation to the sprite's position for scaling and
rotation.
float getOriginX()
The origin influences setPosition(float, float),
setRotation(float) and the expansion direction
of scaling setScale(float, float)
float getOriginY()
The origin influences setPosition(float, float),
setRotation(float) and the expansion direction of
scaling setScale(float, float)
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/Sprite.html
NEW EDIT: maybe if you are customizing the vehicle on a menu for example, as is practicable after this the customized vehicle and create an image at runtime, and scale, an use this image in Sprite, for maybe it's easier, but it's just an idea
I'm writing a paint program that uses shape brushes to draw by using the matrix function.
Everything works well aside from the fact that it's not smooth at all. There will be gaps in the painting if the mouse is moved at a high speed.
I've looked everywhere but haven't been able to find any solution.
The code basically looks like this:
//Press mouse within container. Uses Matrix to draw instances of the brush.
private function handleMouseDown_drawContainer(e:MouseEvent):void
{
_matrix.identity();
_matrix.translate(mouseX - 10, mouseY - 30);
_layout.bitmapData.draw(_layout.brush, _matrix);
_layout.drawContainer.addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove_drawContainer);
_layout.drawContainer.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp_drawContainer)
}
//Move mouse within container. Uses Matrix to draw instances of the brush.
private function handleMouseMove_drawContainer(e:MouseEvent):void
{
_matrix.identity();
_matrix.translate(mouseX - 10, mouseY - 30);
_layout.bitmapData.draw(_layout.brush, _matrix);
}
If anyone could help me figure out how to smooth out the drawing, I'd be forever grateful! =p
Thanks in advance.
You probably need some kind of interpolation between the mouse positions... there are of course many ways, I'll describe one very easy to implement but a bit hard to fine tune. Basically instead of drawing in each mouse position, you use an easing equation that follows the mouse with some delay... this way the described line will be a bit smoother, and will draw a few times between each mouse position.
So instead of doing (pseudocode):
onMouseMove {
draw(mouseX, mouseY);
}
You do something like:
x = 0;
y = 0;
onEnterFrame {
x += (mouseX - x) * 0.2;
y += (mouseY - y) * 0.2;
draw(x, y);
}
Although maybe what you really need is a way to limit the maximum distance between points, so if the mouse moves more in one frame, you interpolate points between the two positions and draw as many times as it's needed.
Or if you're looking for smoother lines (avoid sharp corners) maybe you also need to use beziers to control the resulting line.
Anyway, it all depends on the kind of drawing you're looking for.
I'm using Greensock's TweenLite (AS3) for zooming in and out on a picture. It gets tricky when zooming out near the corners.
I am wondering if there's a way to clamp the width/height/x/y during tweening, so that it never goes out of bounds.
At the moment I have a 'fixer' method that I call when tweening is complete, but it's not ideal.
I've made a demo to show what I mean, try zooming out near a corner. (Mouse Scroll to zoom, click to pan...)
http://cloudchamber.knapnokgames.com/CCMedia/CCImageViewer/CCImageViewer.html
UPDATE: Here's the tweening code:
TweenLite.to(content, 0.3, {
width : _nativeWidth * newZoom ,
height : _nativeHeight * newZoom,
x : (((content.x - origin.x) / content.width) * (_nativeWidth * newZoom)) + origin.x,
y : (((content.y - origin.y) / content.height) * (_nativeHeight * newZoom)) + origin.y,
onComplete: ClampContentPosition});
The X and Y calculations are maybe not the best, but they work. They took me a while to figure out ;)
I would recommend running your logic BEFORE you even create the tween, thus you feed the tween the adjusted values in the first place. Or you could use an onUpdate as Neil suggested.
I'm looking to draw a 3D wire frame sphere in 2D Canvas. I'm not a math ninja by any means, so I'm wondering if anyone knows a simple way to draw one in Canvas using lineto arc connections and drawing it with :math:
I would appreciate any assistance.
Something like this: http://en.wikipedia.org/wiki/File:Sphere_wireframe_10deg_6r.svg
I'm hoping this is a simple equation, but if you know that it isn't (i.e. drawing that would be a lot of code), I would appreciate knowing that as well as I may need to reconsider what I wanna do.
The easiest for you would probably to view the source of the SVG file (here) and recreate those paths using canvas commands.
If you want an actual 3d sphere, projected onto 2d space, I'd suggest using a library like Three.js
You can also look at some of the math I've done here: swarms
The _3d and Matrix modules should be all that you need.
This time SO didn't help me, so I've helped myself and here it is: a pure HTML5 + JavaScript configurable rendering or a wireframe sphere.
I started from this excellent post and then went on. Basically I collected some vertex generation code from Qt3D and adapted to JS.
I'm not 100% sure the rotation functions are correct, but you are welcome to contribute back in case you find errors.
To be clearer, I've distinguished Z positions and draw white on the front and gray on the back.
Here's the result (16 rings x 32 slices) and related jsFiddle link
Enjoy
This is an old thread, but I had the same question and could not find any existing satisfying answer. That is, an answer other than "use WebGL" or "use Three.js". Lo and behold, I am the bearer of great news: it is actually possible to render such a sphere using exclusively Canvas2D's ellipse function, giving us:
a straightforward implementation (~130 lines everything included)
no need for computing vertices and edges
sexy smooth edges
You can find a demo on JSBin, for posterity, with a bunch of options.
The key is to notice that the "wireframe" we're trying to draw is solely composed of circles, and every circle rotated in 3d space will get projected to the camera as an ellipse. The question, then, is: how to find the ellipse corresponding to the projection of the rotated circle?
As we are only interested in circles that lay on the surface of the sphere, we can characterize each of them by the (inter)section of a plane and the (unit) sphere. Therefore, each circle can be described by a normal vector and an offset -1 < o < 1.
Then it's not too difficult to compute and draw the ellipse resulting from the projection of the circle:
function draw_section(n, o = 0) {
let {x, y, z} = project(_p, n) // project normal on camera
let a = atan2(y, x) // angle of projected normal -> angle of ellipse
let ry = sqrt(1 - o * o) // radius of section -> y-radius of ellipse
let rx = ry * abs(z) // x-radius of ellipse
let W = sqrt(x * x + y * y)
let sa = acos(clamp(-1, 1, o * (1 / W - W) / rx || 0)) // ellipse start angle
let sb = z > 0 ? 2 * PI - sa : - sa // ellipse end angle
ctx.beginPath()
ctx.ellipse(x * o * RADIUS, y * o * RADIUS, rx * RADIUS, ry * RADIUS, a, sa, sb, z <= 0)
ctx.stroke()
}
The disks from your example image can be obtained by:
rotating a plane around the z axis
shifting a plane along the z axis
function draw_arcs() {
for (let i = 10; i--;) {
let a = i / 10 * Math.PI
draw_section(vec.set(_n, cos(a), sin(a), 0))
}
for (let i = 9; i--;) {
let a = (i + 1) / 10 * Math.PI
draw_section(Z, cos(a))
}
}
A nice benefit of this method is that you can do this "shifting a plane along the Z axis" for all axes, resulting in a lovely wireframe that would be hard to reproduce if computing vertices and edges by hand:
The only change was the following:
function draw_arcs() {
for (let i = 9; i--;) {
let a = (i + 1) / 10 * Math.PI
draw_section(Z, cos(a))
draw_section(X, cos(a))
draw_section(Y, cos(a))
}
}
The function draw_section above was carefully crafted so that it only draws the camera-facing arc of a given section, which means we get occlusion-culling for free.
(and my dirty trick to render the back of the sphere with a different color is to run draw_arcs again after flipping the canvas)
It's also possible to use 2 radial gradients to have some fake depth shading like in your image:
Sadly browsers seem to struggle a lot when drawing paths with gradients.
There are but two drawbacks I see right now:
performance may vary between browsers. It's likely some optimization could be done, like merging successive calls to .stroke() into one. Frankly quite surprised by how slow using ellipse seems to be at times.
it's a parallel projection rather than a perspective one. If we were to add perspective, projected circles would still appear as ellipses, but the calculation of the ellipse would be a tad more involved. I haven't done it yet, I expect it to be possible, might update my answer if I succeed.
Look at this one: http://jsfiddle.net/aJMBp/
you should just draw a lot of these lines to create a complete sphere. This is a good starting point, give me 5 minutes and I'll see if I can improve it to draw a sphere.
Getting better:
http://jsfiddle.net/aJMBp/1/
Ok, thats def out of my capacity. However, another little improvement here: http://jsfiddle.net/aJMBp/2/
I have made a Car Game using Box2D [in Flash] and I have one remaining bug, which I cannot fix. I added graphics and put them on top of the Box2D body. Everything went as good as expected, but after X rotations the movie clips for the car-wheels, stop spinning. I do something like this wheelSprite.rotation = wheelBody.GetAngle() * 180 / Math.PI. I ran a separate program and I saw that, if you do X.rotation += variable and you increase the variable every frame, after ~30 000 (value of variable) the MovieClip stops rotating, so I reset it to 0 after ~28 000. What do I do? The wheelBody.GetAngle() keeps going up, and I need it to make it look real. How do I reset it?
I faced this problem some time ago. The solution was:
rotation = newRotation % 2*Math.PI;
Which means that rotation must be between 0 and 360 degrees (0 - 2*PI).
Remainder solve this issue:
yourMC.rotation = (yourMCbody.GetAngle() * 180 / Math.PI) % 360;
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/operators.html#modulo
Can't you use the SetAngle() function to set the angle to an equivalent angle?
ie: If the angle rotates over 360 degrees, set it back to 0?
The Box2D manual specifies that the rotation of bodies is unbounded and may get big after awhile and that you can you can reset it using SetAngle.
I use a while loop to calculate the normalized angle (you can apparently call modulo with a floating point operand but I don't know if that is bad for performance). This code normalizes the angle to 0 <= angle < 2pi but I've seen angles sometimes normalized to -pi <= angle < pi as well.
const 2PI:Number = Math.PI * 2;
var rotation:Number = wheelBody.GetAngle();
// normalize angle
while(rotation >= 2PI)
rotation -= 2PI;
while(rotation < 0)
rotation += 2PI;
// store the normalized angle back into Box2D body so it doesn't overflow (optional)
wheelBody.SetAngle(rotation);
// convert to degrees and set rotation of flash sprite
wheelSprite.rotation = rotation * 180 / Math.PI;
I haven't collected the code into a function but it would be easy to do so.