How to change color of pixels of an SKSpriteNode that go over a specific color? - xcode7

I'm not sure if my title question was clear but what I would like to do is take an SKSpriteNode (or maybe an SKShapeNode) and make it so that whenever it moves over a specific region of the background that has a specific color, it changes the color of ONLY the pixels that are on top to another specified color.
So for example, if I do:
let blackBox = SKSpriteNode(texture: nil, color: UIColor.blackColor(), size: CGSizeMake(100, 100))
blackBox.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
blackBox.zPosition = 5
self.addChild(blackBox)
let redBox = SKSpriteNode(texture: nil, color: UIColor.redColor(), size: CGSizeMake(100, 100))
redBox.position = CGPointMake(CGRectGetMidX(self.frame) + 50, CGRectGetMidY(self.frame) + 50)
redBox.zPosition = 4
self.addChild(redBox)
As you can see the blackBox is on top of the redBox since it has a higher zPosition but it's not completely covering it since the position of the redBox is slightly to the northeast. So in this case the blackBox would be full and the redBox would be covered on its bottom left quadrant.
What I would like to do is make ONLY the portion of the blackBox that is on top of any UIColor.redColor() pixels (ie the top right quadrant of the blackBox) change to another color...say blue for example? How could I program the blackBox node to constantly update the color of its pixels depending on what the color is behind it?
Thanks in advance! :)

Related

Centering on a specific point when zooming using SVG viewBox with arbitrary initial state

I have a SVG HTML element and I have implemented panning and zooming into it using the mouse. The current implementation of the zooming functionality just multiplies the original width and height of the element by a number that changes when the user scrolls the mouse.
This implementation preserves the origin (0,0) and all other points appear to move closer/further away from it depending on the direction of the zoom.
Intuitively and based o this question. I know, that If I want to zoom in/out on the point the mouse is currently pointing at, I have to pan the viewBox.
I have already looked at the linked questio, as well as two otheres, but I was unable to successfully apply the suggested solutions to my problem. I have also tried to derive the correct formula multiple times, but all my attempts so far have failed.
I am most likely missunderstanding something about the problem and I seem to be unable to generalise the existing answers to my problem.
The following values represent the current state of my viewBox:
offsetX
offsetY
scroll
width
height
I compute the zoomFactor as a function of the scroll variable (Math.exp(scroll/1000)) and set the viewBox property of my SVG as follows: `${offsetX} ${offsetY} ${width * zoomFactor} ${height * zoomFactor}`.
What I am struggling with, is computing the new offsetX and offsetY values based on the previous state and the current position of the mouse inside of the SVG.
processMouseScroll(event: WheelEvent) {
const oldZoomFactor = zoomFactor(this.scroll);
const newZoomFactor = zoomFactor(this.scroll + event.deltaY);
this.scroll = this.scroll + event.deltaY;
this.offsetX = ???;
this.offsetY = ???;
}
How do I compute the new offsets, based on the previous state, so that the when scrolling the mouse, the point bellow it will appear to be stationary?
Thank you for your answers.
I have finally managed to get it working. Turns out, the answer from the first question I found was correct, but my understanding of SVG viewBox was incorrect and I used bad mouse coordinates.
the offset (min-x and min-y; drawn green) of a viewBox is abbsolute and does not depend on the width and height of the viewBox. The mouse coordinates relative to the SVG element (coordinates drawn in black, SVG element drawn in red) are relative to the size of the viewBox. If I enlarge the viewBox, then the part of the picture I can see inside of it shrinks and 100px line drawn by the mouse will cover more of the image.
If we set the size of the dimensions of the viewBox to be the same as the size of the SVG element (initial state), we have a 1:1 scale between the image and the viewBox (the red rectangle would cover the entire image, bordered black). When we make the viewBox smaller we will not fit the entire image into it and therefore the image will appear to be larger.
If we want to compute the absolute position of our mouse in relation to the entire image we can do it like this (same for Y):
position = offsetX + zoomFactor * mouseX (mouseX relative to the SVG element).
When we zoom, we change the factor, but don't change the position of the mouse. If we want the absolute position under the mouse to remain the same, we have to solve the following set of equations:
oldPosition = oldOffsetX + oldZoomFactor * mouseX
newPosition = newOffsetX + newZoomFactor * mouseX
oldPosition = newPosition
we know the mouse position, both zoom factors and the old offset, therefore we solve for the new offset and get:
newOffsetX = oldOffsetX + mouseX * (oldZoomFactor - newZoomFactor)
which is the final formula and very similar to this answer.
Put together we get the final working solution:
processMouseScroll(event: WheelEvent) {
const oldZoomFactor = zoomFactor(this.scroll);
const newZoomFactor = zoomFactor(this.scroll + event.deltaY);
// mouse position relative to the SVG element
const mouseX = event.pageX - (event.target as SVGElement).getBoundingClientRect().x;
const mouseY = event.pageY - (event.target as SVGElement).getBoundingClientRect().y;
this.scroll = this.scroll + event.deltaY;
this.offsetX = this.offsetX + mouseX * (oldZoomFactor - newZoomFactor);
this.offsetY = this.offsetY + mouseY * (oldZoomFactor - newZoomFactor);
}

Jittering lines when scrolling a TMX map in cocos2d-x

I feel like I'm missing some fundamental concept as to why I am getting flickering when moving a tile map around.
I create a layer. In it, I add a TMXTiledMap.
_tileMap = TMXTiledMap::create("TMX/32Map.tmx");
_tileMap->setScale(1.f);
_floorLayer = _tileMap->getLayer("Floor");
this->addChild(_tileMap);
for(const auto& l : _tileMap->getChildren()) {
static_cast<SpriteBatchNode*>(l)->getTexture()->setAliasTexParameters();
}
this->scheduleUpdate();
In the update I move the layer.
Vec2 newPos = this->getPosition();
newPos.x = (newPos.x - 1);
newPos.y = (newPos.y - 1);
this->setPosition(newPos);
I realize I'm not moving it by dt. If I move it by dt I get an overall jumpiness to the whole layer. I understand this is due to how it renders partial pixels. But if I move it by one pixel like above, I get this # looking set of lines on the screen about 64 pixels or so on top and bottom and about 224 pixels from the left and right
That is when the window is 1024x768. If I make a 320x240 window, I don't see the lines and if I make it 640x480 I only see them on the left and right sides right near the edge of the screen.
Ultimately I'd just like to smoothly scroll a tile map around. Any help would be super appreciated, because I just can't seem to get started on this project.
For me working solution was to change CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL in ccConfig.h from 1 to 0. Find ccConfig.h in cocos/base/.

Scale, Position & Rotate Parent object to make child object take up entire stage

Using the first photo below, let's say:
The red outline is the stage bounds
The gray box is a Sprite on the stage.
The green box is a child of the gray box and has a rotation set.
both display object are anchored at the top-left corner (0,0).
I'd like to rotate, scale, and position the gray box, so the green box fills the stage bounds (the green box and stage have the same aspect ratio).
I can negate the rotation easily enough
parent.rotation = -child.rotation
But the scale and position are proving tricky (because of the rotation). I could use some assistance with the Math involved to calculate the scale and position.
This is what I had tried but didn't produce the results I expected:
gray.scaleX = stage.stageWidth / green.width;
gray.scaleY = gray.scaleX;
gray.x = -green.x;
gray.y = -green.y;
gray.rotation = -green.rotation;
I'm not terribly experienced with Transformation matrices but assume I will need to go that route.
Here is an .fla sample what I'm working with:
SampleFile
You can use this answer: https://stackoverflow.com/a/15789937/1627055 to get some basics. First, you are in need to rotate around the top left corner of the green rectangle, so you use green.x and green.y as center point coordinates. But in between you also need to scale the gray rectangle so that the green rectangle's dimensions get equal to stage. With uniform scaling you don't have to worry about distortion, because if a gray rectangle is scaled uniformly, then a green rectangle will remain a rectangle. If the green rectangle's aspect ratio will be different than what you want it to be, you'd better scale the green rectangle prior to performing this trick. So, you need to first transpose the matrix to offset the center point, then you need to add rotation and scale, then you need to transpose it away. Try this set of code:
var green:Sprite; // your green rect. The code is executed within gray rect
var gr:Number=green.rotation*Math.PI/180; // radians
var gs:Number=stage.stageWidth/green.width; // get scale ratio
var alreadyTurned:Boolean; // if we have already applied the rotation+scale
function turn():void {
if (alreadyTurned) return;
var mat:flash.geom.Matrix=this.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs*green.x,-gs*green.y);
mat.rotate(-1*gr);
this.transform.matrix=mat;
alreadyTurned=true;
}
Sorry, didn't have time to test, so errors might exist. If yes, try swapping scale, translate and rotate, you pretty much need this set of operations to make it work.
For posterity, here is what I ended up using. I create a sprite/movieClip inside the child (green) box and gave it an instance name of "innerObj" (making it the actually content).
var tmpRectangle:Rectangle = new Rectangle(greenChild.x, greenChild.y, greenChild.innerObj.width * greenChild.scaleX, greenChild.innerObj.height * greenChild.scaleY);
//temporary reset
grayParent.transform.matrix = new Matrix();
var gs:Number=stage.stageHeight/(tmpRectangle.height); // get scale ratio
var mat:Matrix=grayParent.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs * tmpRectangle.x, -gs * tmpRectangle.y);
mat.rotate( -greenChild.rotation * Math.PI / 180);
grayParent.transform.matrix = mat;
If the registration point of the green box is at one of it's corners (let's say top left), and in order to be displayed this way it has a rotation increased, then the solution is very simple: apply this rotation with negative sign to the parent (if it's 56, add -56 to parent's). This way the child will be with rotation 0 and parent -> -56;
But if there is no rotation applied to the green box, there is almost no solution to your problem, because of wrong registration point. There is no true way to actually determine if the box is somehow rotated or not. And this is why - imagine you have rotated the green box at 90 degrees, but changed it's registration point and thus it has no property for rotation. How could the script understand that this is not it's normal position, but it's flipped? Even if you get the bounds, you will see that it's a regular rectangle, but nobody know which side is it's regular positioned one.
So the short answer is - make the registration point properly, and use rotation in order to display it like in the first image. Then add negative rotation to the parent, and its all good :)
Edit:
I'm uploading an image so I can explain my idea better:
 
As you can see, I've created a green object inside the grey one, and the graphics INSIDE are rotated. The green object itself, has rotation of 0, and origin point - top left.
#Vesper - I don't think that the matrix will fix anything in this situation (remember that the green object has rotation of 0).
Otherwise I agree, that the matrix will do a pretty job, but there are many ways to do it :)

how can I set the size of a directional light in Three.JS?

I've added a directional light to my scene, but it doesn't show all the shadow. The shadow gets cut off, just like when the FOV of a spotlight is too small.
When I enable the shadowCameraVisible, i see that my light is like a big box which shows the shadow (which makes sense). The question is: how can I make this 'box' bigger?
Found it! Turns out the shadowcamera has left, right, top and bottom properties:
light.shadowCameraLeft = -3000;
light.shadowCameraRight = 3000;
light.shadowCameraTop = 3500;
light.shadowCameraBottom = -3000;
If you get pixelized shadows, it means your shadow map (read: memory) is not big enough. Experiment which is the lowest map you need, because this seems to be expensive. Example:
light.shadow.mapSize.x = 2048
light.shadow.mapSize.y = 2048

How do I create and distribute diagonal stripes on a rectangle?

I would like to be able to create bar charts with JFreeChart that looks similar to the following picture.
It is a very basic mono-colored bar chart, but with one "fancy" detail: the diagonal stripes. I was thinking that this could be made possible by overlaying another picture on top of the normal bar. This picture would have the same dimensions as the bar, have diagonal white stripes and a transparent background. I am not quite sure how to do this though, as I have very little GUI experience, but I found a very useful article that deals with overlaying images on top of graphics from JFreeChart, so I am quite certain I should be able to pull that of.
But how should I create the diagonal stripes?
I see how I could distribute the lines from the lower left corner to the upper right corner, but not the capped lines in the upper left and lower right corner. Can I somehow paint outside the rectangle (and not have it included in the picture)?
edit: After some searching I cannot see that my suggestion of overlaying an image with a transparent background would work, as I cannot find any examples on how to do this. On the other hand, merely painting the lines on the rectangle is probably easier.
Using a gradient fill to draw lines
On trashgod's tip I tried filling a shape with a gradient that had sharp edges to simulate line drawing. This would prevent a lot of calculations and could potentially be a lot simpler. It worked quite ok for thick lines, but not for thinner lines. Using the following code produces the fill in the first picture:
rect.setSpace(spaceBetweenLines);
Color bg = Color.YELLOW;
Color fg = Color.BLUE;
rect.setPaint(new LinearGradientPaint(
(float) startX, (float) startY, (float) (startX + spaceBetweenLines), (float) (startY + spaceBetweenLines),
new float[] {0,.1f,.1001f}, new Color[] {fg,fg,bg}, MultipleGradientPaint.CycleMethod.REPEAT)
);
Drawing lines using graphic primitives
Although simpler it did not work in my case. The more elaborate, but to me, more natural way of doing it, is simply drawing lines on top of the shape (rectangle, cirle, ...). The following code was used in producing the second image. Observe the use of the clip(Shape s) to restrict the line drawing to the shape underneath. The reason for not simply drawing a rectangle and using clip() to limit the shape is that the clip() operation is not aliased, thus producing jaggies. Therefore I have to draw the shape first to get smooth edges, then set the clip to prevent overflow in the forthcoming line drawing, and finally draw the lines.
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(getBackground());
g2.fill(getShape());
g2.setClip(getShape());
// draw diagonal lines
g2.setPaint(getLineColor());
for (int x = (int) this.x, y = (int) (this.y); y - height < (this.y + height + getSpace()); ) {
g2.drawLine(x, y , x + (int) width , y - (int) width);
y += getSpace();
}
The source code for BarChartDemo1 shows how to apply a GradientPaint, but you may want to experiment with LinearGradientPaint to get the diagonal effect.
I want to paint the bars, not the background.
If you already have a suitable image, TexturePaint may be an alternative.