How to change sprite width and cause more texture repetition? - cocos2d-x

I have sprite with repeated texture and I want to change width frequently of sprite but on that way that cause texture to be repeated more times instead of stay same number of scaled repetition.
This is what happened when I change width with
setScaleX(factor) ;
I also tried to getContentSize() and change width and setContentSize(newContentSize);
but it didn't help, just cause strange behaviour. How to change sprite width and cause more texture repetition ? Is this possible at all ?
( I can remove current sprite and recreate new but it looks like btute force solution and I am looking for something more elegant)

use TexParams,like
Texture2D* tex = Director::getInstance()->getTextureCache()->addImage("CloseNormal.png");
Texture2D::TexParams param = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
tex->setTexParameters(param);
auto size = tex->getContentSize();
auto s = Sprite::createWithTexture(tex, Rect(0,0,size.width * 2, size.height * 2));
s->setPosition(Point(300,300));
this->addChild(s);
or like
auto s = Sprite::create("CloseSelected1.png");
auto size = s->getContentSize();
Texture2D::TexParams param = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
s->getTexture()->setTexParameters(param);
s->setTextureRect(Rect(0, 0, size.width * 2, size.height * 2));
s->setPosition(Point(200,200));
this->addChild(s);

Related

`Autodesk.Viewing.ScreenShot.getScreenShotWithBounds` uses viewer's default camera dimensions instead of bounds

Following this advice I am trying to replace our usage of viewer::getScreenshot with Autodesk.Viewing.ScreenShot.getScreenShotWithBounds which calls Autodesk.Viewing.ScreenShot.getScreenShot to support larger screenshots. However, the bounds are ignored. The behaviour seems to be that it makes a screenshot of the viewer's default camera or corresponding to its dimensions. Then it stretches the image to fit the given width and height. I expected the function to return a screenshot of the elements inside the given bounding box.
Is the function getScreenShotWithBounds supposed to do something different from what I am assuming?
Example code (LMV 7.40.0):
const bbounds: THREE.Bbox3; // calculated for some elements I want to screenshot
Autodesk.Viewing.ScreenShot.getScreenShotWithBounds(NOP_VIEWER, Math.ceil(bbounds.size().x * 4), Math.ceil(bbounds.size().y* 4), (blob) => window.open(blob), {bounds: bbounds, margin: 0});
Manual screenshot of the viewer
Returned image of getScreenShotWithBounds
Update:
I misunderstood the function. Autodesk.Viewing.ScreenShot.getScreenShotWithBounds just fits the bounds into the camera view. The bounds are not used for any cropping. See my more detailed answer.
The given width and height of Autodesk.Viewing.ScreenShot.getScreenShotWithBounds must be of the same aspect ratio as the viewer (see Adam Nagy's answer), i.e.:
getScreenShotWithBounds(NOP_VIEWER, NOP_VIEWER.getDimensions().width * 4, NOP_VIEWER.getDimensions().height * 4, options);
getScreenShotWithBounds just fits the bounds into the camera view (internally it calls viewer.navigation.fitBounds(true, bounds, false);). The bounds are not used for any kind of cropping / pixel calculation or otherwise.
To get a specific aspect ratio by cropping, you have to provide getCropBounds in the options parameter.
For example to force a aspect ratio by cropping:
getCropBounds: function(viewer: Autodesk.Viewing.Viewer3D, camera: Autodesk.Viewing.UnifiedCamera, bounds: THREE.Box3): THREE.Box2 {
// calculate the crop bounds in pixels
// if the crop bounds are larger the screenshot's width / height is taken
const aspectRatio = new THREE.Vector2(4, 3);
const viewerBoundsWidthRatio = width / aspectRatio.x;
const viewerBoundsHeightRatio = height / aspectRatio.y;
const cropToHeight = viewerBoundsWidthRatio > viewerBoundsHeightRatio;
const smallerScaleRatio = cropToHeight ? viewerBoundsHeightRatio : viewerBoundsWidthRatio;
const cropBoundsSize = aspectRatio.clone().multiplyScalar(smallerScaleRatio);
const cropBounds = new THREE.Box2(new THREE.Vector2(0, 0), new THREE.Vector2(cropBoundsSize.x, cropBoundsSize.y));
const offset = cropToHeight ? new THREE.Vector2((width - cropBoundsSize.x) / 2, 0) : new THREE.Vector2(0, (height - cropBoundsSize.y) / 2);
cropBounds.min.add(offset);
cropBounds.max.add(offset);
return cropBounds;
}
The squashed image you are getting is suggesting it, but it's not clear from the comments in the source code, that you should keep the ratio of the Viewer for the width and height input parameters. So you should only scale them depending on your needs: getScreenShotWithBounds(NOP_VIEWER, viewer_width * x, viewer_height * x, etc
Then the bounds in the options should take care of the cropping.

maintaining relative position when changing scaleX and scaleY?

I am trying to implement a simple "zoom" function in a map presentation type app. The user interacts with a NumericStepper to dial in a scale value and I then use that value to set the scaleX and scaleY properties of my map sprite. The parent of the map sprite has a scrollRect defined so the map is cropped as it scales. That all seems to work fine.
Naturally when I change the scale, the visible content shifts as the sprite becomes larger or smaller. I would like to keep the content in relatively the same screen location. I've taken a first pass at it below but it's not quite right.
Question: Am I on the right track here thinking that I can determine how much to shift the x/y by comparing the change in the width/height of the sprite after scaling? (as I write this I am thinking I can determine the center of the sprite before scaling, then reposition it so it stays centered over that point. Hmm. . .).
protected function scaleStepper_changeHandler(event:Event):void
{
var cX:Number = wrapper.x + (wrapper.width /2);
var cY:Number = wrapper.y + (wrapper.height /2);
wrapper.scaleX = scaleStepper.value;
wrapper.scaleY = scaleStepper.value;
wrapper.x = cX - (wrapper.width /2);
wrapper.y = cY - (wrapper.height /2);
}
You are on the right track, but for a better solution you should use a matrix to transform your Sprite. Use the following code below to achieve what you need:
private var originalMatrix:Matrix;
private function scaleAroundPoint(target:Sprite, scalePoint:Point, scaleFactor:Number):void
{
if(originalMatrix == null)
originalMatrix = target.transform.matrix;
var matrix:Matrix = originalMatrix.clone();
matrix.translate(-scalePoint.x, -scalePoint.y);
matrix.scale(scaleFactor, scaleFactor);
matrix.translate(scalePoint.x, scalePoint.y);
target.transform.matrix = matrix;
}
You can call this method like this:
scaleAroundPoint(wrapper, new Point(yourWidth/2, yourHeight/2), scaleStepper.value);
Hope this helps and solves your problem.
Am I on the right track here thinking that I can determine how much to shift the x/y by comparing the change in the width/height of the sprite after scaling?
Yes. As all values are known, you don't really have to "test" after scaling. You basically want to distribute the movement of the bounding box borders evenly.
Here's an example in one dimension, scaling factor 2, X is the registration point, | a boundary:
before scaling |--X--|
after scaling |----X----|
No problem there. Now what if the registration point is not in the middle?
before scaling |-X---|
after scaling |--X------|
As a last example, the edge case with the registration point on the boundary:
before scaling |X----|
after scaling |X--------|
Note how the boundaries of all 3 examples are equal before scaling and within each example, the registration point remains constant.
The problem is clearly identified. Now how to solve this?
We do know how much the width changes
before scaling width
after scaling width * scaleFactor
and from the first example we can determine where the left boundary should be after scaling (assuming that the registration point is at 0, so the object is centered):
before scaling -width * 0.5
after scaling -width * 0.5 * scaleFactor
This value depends on where the registration point of course is within the display object relative to the left boarder. To circumvent this dependency, subtract the values from each other to know how much the left boundary is moved to the left after scaling while keeping the object centered:
boundary shift width * 0.5 * (scaleFactor - 1)
Comparing before and after scaling, the left boundary should be further to the left by that amount and the right boundary should be further to the right by that amount.
The problem is that you cannot really set the left or right boundary directly.
You have to set the registration point, which will influence where the boundaries are. To know how far you should move the registration point, imagine both edge cases:
before scaling |X----|
after scaling |X--------|
corrected, |X--------|
before scaling |----X|
after scaling |--------X|
corrected, |--------X|
In both cases, the registration point has to be moved by the amount which the boundary should move, because essentially, the registration point is on the boundary and thus behaves the same way.
Any value in between can be found by linearly interpolating between both cases:
-[width * 0.5 * (scaleFactor - 1)] <= value <= +[width * 0.5 * (scaleFactor - 1)]
-[width * 0.5 * (scaleFactor - 1)] * (1-t) + [width * 0.5 * (scaleFactor - 1)] * t
To find the interpolation value t, which is 0 if X is on the left and 1 when on the right:
t = (X - L) / width
Add -[width * 0.5 * (scaleFactor - 1)] * (1-t) + [width * 0.5 * (scaleFactor - 1)] * t to the x position of the registration point and the scale the object.
Do the same for y in a similar fashion.

How do I get the width of bitmap using createJS?

How do I get the width of a bitmap image in createJS? I need it to change only the width of the image to calculate the scaleX value. By the way, Is there any direct way to change the image width or do I need to use the scaleX property?
Every image is preloaded using preloadJS in the library.
piecesArray[0].val = Math.floor(Math.random() * 50)
piecesArray[0].sym = new lib["a"+ piecesArray[0].val]()
model = new createjs.Bitmap(piecesArray[0].sym);
(function to change width)
stage.addChild(model.image)
I've searched all the variables of model using the console and there is no getBounds(), nor width, nor height, nor nominalBounds...
P.S. This what happens when I test it using the console
model = new createjs.Bitmap(new lib["c"+ 1]());
a {_listeners: null, _captureListeners: null, alpha: 1, cacheCanvas: null, cacheID: 0…}
console.log(model.image)
VM281:2 lib.c1 {spriteSheet: a, paused: true, currentAnimationFrame: 0, _animation: null, currentAnimation: null…}
undefined
console.log(model.image.width)
VM282:2 undefined
undefined
You can get the size of the image via bitmap.image.width, so in your code it would be model.image.width. And no, there is no direct way to set the width of a bitmap - you have to set it via scaleX/scaleY.
Solved it by re-sizing before creating the Bitmap. I think the reason I couldn't get the width value was because the image comes from a spritesheet.
piecesArray[0].sym.scaleX = 232/piecesArray[0].sym.getBounds().width
piecesArray[0].sym.scaleY = 150/piecesArray[0].sym.getBounds().height
model = new createjs.Bitmap(piecesArray[0].sym);

AS3 sprite with rectangle is empty and returns 0 for width and other properties

I have an array of sprites:
for (i = 0; i < 3; i++) {
var newLine:Sprite = new Sprite();
newLine.graphics.beginFill(0xFFFFFF);
newLine.graphics.drawRect((uiSizeX * (.25 + .25 * i)) + (doorSizeX - lineLengths[0][i]) / 2, uiSizeY * .5, lineLengths[0][i], lineHeight);
newLine.name = "lines" + i;
lines.push(newLine);
}
I later add each to a background Sprite:
for (i = 0; i < 3; i++) {
uiContainer.addChild(doors[i]);
uiContainer.addChild(lines[i]);
}
And later wish to change the width of the rectangles contained in each. When I set the width property using the array reference:
lines[i].width = lineLengths[trialNumber][i];
The rectangle moves towards the center of the stage and uiContainer. Trying to set the x value results in even stranger behavior. I've gathered from answers to other questions that this is because the sprite is empty, which is confusing since it seems like it should have a rectangle in it. How do I access or change the properties of the rectangle or make it so the sprite is not empty?
Never try to directly change the width or height of a sprite, unless you really really have to, because it leads to all sorts of unexpected behavior.
If you want to resize the sprite, then redraw the rectangle:
var sprite = lines[i];
sprite.graphics.beginFill(0xFFFFFF);
sprite.graphics.drawRect(newWidth, newHeight);
Also don't forget to call endFill:
sprite.graphics.endFill();
You should probably wrap all this logic into a separate class so that you can redraw your sprites more easily. Even better, create a class that extends Sprite and that contains all the logic to redraw/resize.

Scaling and cropping an image with fixed dimensions

I want to display an image, and it should be transformed like this:
If for example, my original image is 200x300 pixels in size, I want it to have a width of 150, and then scale the height accordingly. Then I want to crop the image, so that the result has a dimension of 150x150 pixels.
I've tried several ways, but haven't figured out how to do it.
You can calculate the scale factor by dividing new width by old width:
var scale : Number = 150 / myImage.width;
myImage.scaleX = myImage.scaleY = scale;
To crop, either use a mask on the scaled clip, or draw to a new Bitmap:
var myBitmapData : BitmapData = new BitmapData ( 150, 150 );
// use concatenated matrix of the image to scale the new bitmap data
var matrix : Matrix = myImage.transform.concatenatedMatrix;
myBitmapData.draw ( myImage, matrix );
var myBitmap : Bitmap = new Bitmap ( myBitmapData );
addChild ( myBitmap );
Depending on how many images there are, and what you are going to do with them later, you should always test both possibilities for performance.