I have a shape drawn using Shape.graphics.drawRoundRect() with a lineStyle applied. I'm trying to capture that shape as a Bitmap using BitmapData.draw() but I am running into an issue with the stroke. See below:
As you can see, the stroke gets clipped when using draw() (and drawWithQuality()). The line is drawn centered on the object, so a thickness of 4 (as I used in the example) has 2 pixels outside the shape's area and 2 pixels inside it. draw() captures everything from (0,0) to (BitmapData.width,BitmapData.height), it seems, so everything to the left and top of (0,0) is lost. I tried to compensate using the clipRect option, but that just evened out the clipped border, ironically.
Any idea how to capture that remaining data?
As a more general solution, you can get the bounds of the object in its own coordinate space, and use that to set the size of the BitmapData and offset the draw():
import flash.geom.Matrix;
import flash.geom.Rectangle;
const thickness:int = 4;
const radius:int = 10;
const size:int = 100;
var shape:Shape = new Shape();
shape.graphics.lineStyle( thickness, 0xaaaaaa );
shape.graphics.beginFill( 0xdddddd );
shape.graphics.drawRoundRect( 0, 0, size, size, radius, radius );
shape.graphics.endFill();
addChild( shape );
var bounds:Rectangle = shape.getBounds(shape);
var m:Matrix = new Matrix();
m.translate(-bounds.left, -bounds.top);
var bmp1:Bitmap = new Bitmap();
bmp1.bitmapData = new BitmapData( bounds.width, bounds.height, true, 0 );
bmp1.x = 310;
bmp1.y = 100;
addChild( bmp1 );
bmp1.bitmapData.draw( shape, m );
And of course, the second I post the question, I figure out the way to do it. You have to offset your shape to match the line outside of the bounds and compensate for the additional size the line adds to the shape when using draw
const thickness:int = 4;
const radius:int = 10;
const size:int = 100;
var shape:Shape = new Shape();
shape.graphics.lineStyle( thickness, 0xaaaaaa );
shape.graphics.beginFill( 0xdddddd );
shape.graphics.drawRoundRect( thickness / 2, thickness / 2, size, size, radius, radius );
shape.graphics.endFill();
addChild( shape );
var bmp1:Bitmap = new Bitmap();
bmp1.bitmapData = new BitmapData( size + thickness, size + thickness, true, 0 );
bmp1.x = 310;
bmp1.y = 100;
addChild( bmp1 );
bmp1.bitmapData.draw( shape );
See the outcome here (you can ignore the clip rect one):
Related
I create mirror plane using mirror.js in three.js.
now i want to apply gravity over that plane. i have try this code but get error
var planeGeo = new THREE.PlaneBufferGeometry( 300, 300 );
var groundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT, color: 0x777777 } );
var Material = new Physijs.createMaterial(groundMirror);
var cube = new Physijs.BoxMesh(planeGeo,Material,0 );
cube.add( groundMirror );
scene.add(cube);
CubeCamera is a bit of a waste for a single plane, you could always use a single perspective camera and align it with your plane. The basic idea is to take plane normal, position your camera behind the plane in aligned on the normal going through the center of the plane, and orient it (rotate) to be aligned with the normal. Make sure you set near value of your camera to be the same as distance between the camera and the plane, so that reflections can be captured at to the point of contact. Question on scale is resolved by ensuring your viewport is the same size at the plane.
For reference, have a look at he CubeCamera source code:
https://github.com/mrdoob/three.js/blob/master/src/cameras/CubeCamera.js
EDIT:
After looking through examples again, i found that Slayvin has implemented just that:
http://threejs.org/examples/webgl_mirror.html
here's a relevant snippet:
var planeGeo = new THREE.PlaneBufferGeometry( 100.1, 100.1 );
// MIRORR planes
groundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT, color: 0x777777 } );
var mirrorMesh = new THREE.Mesh( planeGeo, groundMirror.material );
mirrorMesh.add( groundMirror );
mirrorMesh.rotateX( - Math.PI / 2 );
scene.add( mirrorMesh );
correct it by changing
var Material = new Physijs.createMaterial(groundMirror.material);
var planeGeo = new THREE.PlaneGeometry( 300, 300 );
var groundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT, color: 0x777777 } );
var Material = new Physijs.createMaterial(groundMirror.material);
var cube = new Physijs.BoxMesh(planeGeo,Material,0 );
cube.rotateX( - Math.PI / 2 );
cube.add( groundMirror );
I know how to draw a square:
graphics.moveTo(0, 0);
graphics.beginFill(0x00ff00, 1);
graphics.lineTo(point, 0);
graphics.lineTo(point, point);
graphics.lineTo(0, point);
graphics.lineTo(0, 0);
graphics.endFill();
now how would i draw another square inside the first one with an alpha of 0.5?
The following snippet will draw a green square then substract a square to its center (live demo).
// Size of the main square
var point:uint = 100;
// Create two sprites
var s1:Sprite = new Sprite();
var s2:Sprite = new Sprite();
// Shortcuts to graphics objects
var g1:Graphics = s1.graphics;
var g2:Graphics = s2.graphics;
// Draw the main square
g1.beginFill(0x00ff00, 1.0);
g1.drawRect(0, 0, point, point);
g1.endFill();
// Draw the eraser square
g2.beginFill(0x000000, 0.5);
g2.drawRect(0, 0, point/2, point/2);
g2.endFill();
// Configure blend modes to erase the center of the first square
s1.blendMode = BlendMode.LAYER;
s2.blendMode = BlendMode.ERASE;
// Add the eraser square to the first one and adjust its position
s1.addChild(s2);
s2.x = s2.y = point/4;
// Add the first square to the stage
addChild(s1);
Without having to use any blend modes, you can exclude portions of a drawn shape by drawing over (erasing) the desired portion before ending the fill.
Here's an example:
import flash.display.Shape;
var squares:Shape = new Shape();
squares.graphics.beginFill(0xFF0000, 1.0);
squares.graphics.drawRect(0, 0, 200, 200);
squares.graphics.drawRect(50, 50, 100, 100); //exclude
squares.graphics.endFill();
squares.graphics.beginFill(0x0000FF, 0.5);
squares.graphics.drawRect(75, 75, 100, 100);
squares.graphics.endFill();
addChild(squares);
var squ1:Sprite = new Sprite();
with(squ1.graphics){
beginFill(0xFF0000);
drawRect(0,0,100,100);
endFill();
}
addChild(squ1);
var inSq:Sprite = new Sprite();
with(inSq.graphics){
beginFill(0x00FF00,.5);
drawRect(0,0,25,25);
endFill();
}
squ1.addChild(inSq);
inSq.x = squ1.width/2 - inSq.width/2;
inSq.y = squ1.height/2 - inSq.height/2;
maintaining the center registration point of a circle shape, or any other display object with center registration, while being converted to a bitmap object is proving to be difficult.
the following code converts a circle shape into a bitmap object and positions it in the center of the stage and subsequently removes its center registration poin.
the x and y origin of a new bitmapData object (top left) is the same as the x and y origin of the circle (center), but it's not possible to translate the x and y position of the bitmapData.draw() - its parameters only accept width, height, transparency and fill color.
var myCircle:Shape = new Shape();
myCircle.graphics.beginFill(0xFF0000, 1.0);
myCircle.graphics.drawCircie(0, 0, 100);
myCircle.graphics.endFill();
var matrix:Matrix = new Matrix();
matrix.tx = myCircle.width / 2;
matrix.ty = myCircle.height / 2;
var myCircleBitmapData:BitmapData = new BitmapData(myCircle.width, myCircle.height, true, 0x00FFFFFF);
myCircleBitmapData.draw(myCircle, matrix);
var result:Bitmap = new Bitmap(myCircleBitmapData, PixelSnapping.AUTO, true);
result.x = stage.stageWidth / 2 - matrix.tx;
result.y = stage.stageHeight / 2 - matrix.ty;
addChild(result);
with the help of a matrix translation, the new bitmap object will appear centered in the stage, but applying a regular or 3D rotation, etc., will clearly demonstrate that the registration point is now the top left corner instead of the center.
how can i convert a center registered display object into a bitmap while maintaining its center registration?
it appears the most common approach is to simply add the bitmap as a child of a sprite container and rotate the sprite container rather than the bitmap itself.
var myCircle:Shape = new Shape();
myCircle.graphics.beginFill(0xFF0000, 1.0);
myCircle.graphics.drawCircie(0, 0, 100);
myCircle.graphics.endFill();
var matrix:Matrix = new Matrix();
matrix.tx = myCircle.width / 2;
matrix.ty = myCircle.height / 2;
var myCircleBitmapData:BitmapData = new BitmapData(myCircle.width, myCircle.height, true, 0x00FFFFFF);
myCircleBitmapData.draw(myCircle, matrix);
var myCircleBitmap:Bitmap = new Bitmap(myCircleBitmapData, PixelSnapping.AUTO, true);
myCircleBitmap.x -= matrix.tx;
myCircleBitmap.y -= matrix.ty;
var circleContainer:Sprite = new Sprite();
circleContainer.addChild(myCircleBitmap);
alternatively, for those using Flash Professional IDE, there is the option to employ fl.motion.MatrixTransformer.rotateAroundInternalPoint instead of using a container sprite.
The following tutorial looks like what you're trying to do.
http://www.8bitrocket.com/2007/10/30/Actionscript-3-Tutorial-BitmapData-rotation-with-a-matrix/
i'm unsuccessfully trying to rotate a rectangle around an external point while tweening. i'm trying to lock the top of the red rectangle to the line while it tweens from left to right and rotates from 0º to 90º.
alt text http://www.freeimagehosting.net/uploads/0b937c92e6.png
the image above shows 3 states of the tween. state 1 shows the red rectangle at the start of the line with no angle. state 2 shows the red rectangle has tweened half way along the line with an angle of 45º that is also half the total angle of 90º. state 3 shows the final position of the tween where the red rectangle has an angle of 90º and is placed at the edge of the line.
it seems the problem i'm experiencing is that while tweening, the rotation causes the top of the red rectangle to lose sync with the black line.
here is my code that doesn't work, but hopefully will give you a clearer picture of what i'm attempting.
var angle:Number = 90;
var previousAngle:Number = 0;
var distanceObject:Object = new Object();
distanceObject.distance = line.width;
distanceTween = new Tween(distanceObject, "distance", None.easeNone, 0, distanceObject.distance, 5, true);
distanceTween.addEventListener(TweenEvent.MOTION_CHANGE, tweenHandler);
function tweenHandler(evt:TweenEvent):void
{
var angleShift:Number = (angle / distance) * distanceObject.distance;
//1:tween RedBox position
redBox.x = line.x + line.width * distanceObject.distance;
//2:tween RedBox angle
var externalPointMatrix:Matrix = redBox.transform.matrix;
MatrixTransformer.rotateAroundExternalPoint(externalPointMatrix, 0 + redBox.width * distanceObject.distance, 0, angleShift - previousAngle);
redBox.transform.matrix = externalPointMatrix;
previousAngle = angleShift;
}
I don't think you have specified the problem well enough for a generic solution. There are 3 things changing here: x, y and rotation. Each of these is calculated as a result of a point on the rectangle (the blue "x" in your diagram) that changes over time. That means the thing you need to focus on first is the point on the rectangle that changes over time. Next you need to know that the x and y can be calculated using that point along with the rotation.
So break it down into steps.
find the location of the "x" point on the line
rotate the object
find the location of the "x" point wrt to the rectangle
based on the angle of rotation and the known location of the "x" point calculate the x and y position of the rectangle (SOHCAHTOA)
Here is some code to illustrate:
package
{
import com.greensock.TweenNano;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width='500', height='300', backgroundColor='#ffffff', frameRate='30')]
public class BoxAnim extends Sprite
{
private static const LINE_WIDTH:int = 350;
private static const RECT_WIDTH:int = 150;
private static const RECT_HEIGHT:int = 100;
private static const FINAL_ROTATION:Number = Math.PI/2;
public var point:Number;
private var line:Sprite;
private var rect:Sprite;
private var cross:Sprite;
public function BoxAnim()
{
addEventListener(Event.ADDED_TO_STAGE, addedToStage);
}
private function addedToStage(event:Event):void
{
line = new Sprite();
addChild(line);
line.graphics.lineStyle(10, 0x0);
line.graphics.lineTo(LINE_WIDTH, 0);
line.x = 50;
line.y = 175;
rect = new Sprite();
addChild(rect);
rect.graphics.lineStyle(4, 0xFF0000);
rect.graphics.beginFill(0xFF0000, 0.5);
rect.graphics.drawRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
rect.x = 50;
rect.y = 175;
cross = new Sprite();
addChild(cross);
cross.graphics.lineStyle(5, 0x41a9f4);
cross.graphics.moveTo(-5, -5);
cross.graphics.lineTo(5, 5);
cross.graphics.moveTo(5, -5);
cross.graphics.lineTo(-5, 5);
cross.x = 50;
cross.y = 175;
point = 0;
TweenNano.to(this, 3, {point: 1, onUpdate: tick});
}
private function tick():void
{
// first calculate where the point should be on the line
cross.x = (point * LINE_WIDTH) + line.x;
// calculate the angle of rotation
var rotationRadians:Number = (point * FINAL_ROTATION);
rect.rotation = rotationRadians*180/Math.PI;
// calculate where on the rectangle the point would be
var rectCrossX:Number = (point * RECT_WIDTH);
// use trig to find the x & y points
rect.x = cross.x - Math.cos(rotationRadians)*rectCrossX;
rect.y = cross.y - Math.sin(rotationRadians)*rectCrossX;
}
}
}
I'm just using the variable point as a percentage that goes from 0 to 1. I then scale it to find the position of the "x" point on the line. Scale it again to figure out the rotation. Scale it again to find where it lies along the top of the rectangle. Then trig solves the location of the corner of the rectangle wrt the point.
Let say I have a BitmapData with different pixels representing an object, and some black pixels around it that I want to remove.
I would like to obtain a new BitmapData, with width and height of the object represented by non-black pixels.
For example, let say I have a BitmapData 400x400px, but the object represented by non-black pixels occupies the rect: x=100, y=100, width=200, height=200. I want to get new BitmapData representing that rect, all black pixels should be removed. Of course, i have no coordinates for that rectangle, i need somehow to make difference between a full black bitmapdata and the original one, and construct a new bitmapdata (different width and height).
Any idea on how to do this please ?
You can use getColorBoundsRect to find the dimensions of the differently-colored-pixels inside your BitmapData:
//some fake data
var yourBigBmd:BitmapData = new BitmapData( 300, 300, false, 0 );
yourBigBmd.fillRect( new Rectangle( 10, 10, 30, 60 ), 0xFF0000 );
//a little notch
yourBigBmd.fillRect( new Rectangle( 10, 10, 10, 10), 0x00000 );
var blackColor:uint = 0x000000;
var littleBmdBounds:Rectangle = yourBigBmd.getColorBoundsRect( 0xFFFFFF, blackColor, false );
trace( "littleBmdBounds: " + littleBmdBounds );
This will trace littleBmdBounds: (x=10, y=10, w=30, h=60)
Next, we need to copy what's in those bounds into a new BitmapData:
var littleBmd:BitmapData = new BitmapData( littleBmdBounds.width, littleBmdBounds.height, true, 0 );
var mx:Matrix = new Matrix();
mx.translate( -littleBmdBounds.x, -littleBmdBounds.y );
littleBmd.draw( yourBigBmd, mx );
Finally, use threshold to remove any remaining black and make it transparent:
var blackAlphaColor:uint = 0xFF000000;
var transparentColor:uint = 0x00000000;
littleBmd.threshold( littleBmd, littleBmd.rect, littleBmd.rect.topLeft, "==", blackAlphaColor, transparentColor )