I'm trying to figure out how works famo.us perspective transformation.
I assume that famo.us use one-point perspective which means that new image is just intersections of plane (some plane with different z coordinate) with lines coming from the focal point to the original image.
I also assume that declaration context.setPerspective(100) sets distance from focal point to the original image (screen).
this means that Transform.translate(0, 0, -50) of the image should make new image 2 times smaller But it doesn't!!
I didn't find any information about this in famo.us docs.
Any ideas?
P.S: I need to calculate translate distance to fill screen width with the image (and I don't want to use scale).
Perspective is related to 3D space and is relative to the origin in pixels. First you should work with a larger value for your perspective (say 1000). Second, use a modifier to set the translation of your surface with respect to your context. Positive values position the element closer to the viewer, negative values further away.
Note: Moving your translateZ value by half does not change your surface size by that factor! It only moves it from your perspective that value closer or further away along the origin axis. Run the example below and change the translate to -1000 and you will see.
Example jsBin
var mainContext = Engine.createContext();
var contextPerspective = 1000;
mainContext.setPerspective(contextPerspective);
var surface = new Surface({
content: 'Background',
transform: Transform.translate(0, 0, 0.002),
properties: {
backgroundColor: 'rgba(0,0,0,0.1)'
}
});
var surfacemod = new Modifier({
origin: [0.5, 0.5],
align: [0.5, 0.5]
});
mainContext.add(surfacemod).add(surface);
surfacemod.setTransform(Transform.translate(0, 0, -500));
Edit: To find z transform value based on width of context
Example jsBin (width)
Example jsBin (Image width or heigh based on context size)
var surfacemod = new Modifier({
size: [200, 100],
origin: [0.5, 0.5],
align: [0.5, 0.5]
});
var contextSize = mainContext.getSize();
var surfaceSize = surface.getSize();
var percentage = (contextSize[0]- surfaceSize[0]) / contextSize[0];
var transZ = contextPerspective * percentage;
surfacemod.setTransform(Transform.translate(0, 0, transZ));
Related
I have created a ball node, and applied the texture images from my 3d model. I have captured totally 6 images, 3 images (with having 120deg) for rolling around x axis, and other 3 images for rolling around y axis. I want sprite kit to simulate it with following code below.When i apply impulse, it starts sliding instead rolling and when it collides to sides, then it starts turning but again not rolling. Normally, depending on the impulse on the ball, it should turn and roll together sometimes. The effect on "8 ball pool game" balls can be an example which i want to get a result.
var ball = SKSpriteNode()
var textureAtlas = SKTextureAtlas()
var textureArray = [SKTexture]()
override func didMove(to view: SKView) {
textureAtlas = SKTextureAtlas(named: "white")
for i in 0... textureAtlas.textureNames.count {
let name = "ball_\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
ball = SKSpriteNode(imageNamed: textureAtlas.textureNames[0])
ball.size = CGSize(width: ballRadius*2, height: ballRadius*2)
ball.position = CGPoint(x: -ballRadius/2-20, y: -ballRadius-20)
ball.zPosition = 0
ball.physicsBody = SKPhysicsBody(circleOfRadius: ballRadius)
ball.physicsBody?.isDynamic = true
ball.physicsBody?.restitution = 0.3
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.allowsRotation = true
addChild(ball)}
You need to apply angular impulse to get it to rotate
node.physicsBody!.applyAngularImpulse(1.0)
I have a UIImageView that you can tap on and it draws a circle. I store the location of the circles in an Array of Dictionaries. This allows me to "replay" the drawing of the circles. However, when the UIImageView is a different size from the original, the circles don't scale to the new UIImageView.
How can I get the circles to scale? For demonstration purposes, the top picture is the size of the UIImageView used for input and the second one is the size for replay.
Inputing the circles:
Replay the circles (the circles should be in the blue UIImageView
import Foundation
import UIKit
class DrawPuck {
func drawPuck(circle: CGPoint, circleColour: CGColor, circleSize: CGFloat, imageView: UIImageView) {
let circleBezierPath = UIBezierPath(arcCenter: CGPoint(x: circle.x,y: circle.y), radius: CGFloat(circleSize), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circleBezierPath.cgPath
//change the fill color
shapeLayer.fillColor = circleColour
//you can change the stroke color
shapeLayer.strokeColor = UIColor.white.cgColor
//you can change the line width
shapeLayer.lineWidth = 0.5
imageView.layer.addSublayer(shapeLayer)
}
}
I was able to resolve this with CATransform3DMakeScale As long as I keep the original aspect ratio of the original image it works great.
let width = yellowImageView.frame.width / blueImageView.frame.width
let height = yellowImageView.frame.height / blueImageView.frame.height
shapeLayer.transform = CATransform3DMakeScale(height, width, 1.0)
view.layer.addSublayer(shapeLayer)
I'm trying to understand how sets work with transforms. Basically, I would like to have a 'container' set with all children in it, that I can move around the canvas.
I created a fiddle to show what I mean, this is a simplification of a larger drawing. http://jsfiddle.net/thibs/Hsvpf/
I've created 3 squares, red, black, blue. Each are added to a set and then they are added to a main container (set). I've added outlines to show canvas and set.
Red and black sets do not have transforms on them, but blue does. Blue remains in the 'container' set... until the container gets a transform.
Why is that? I thought that transforms were applied to all the chidlren of the set...?
Thanks in advance
Here is the fiddle code:
var paper = Raphael('holder');
var container = paper.set();
paper.rect(0, 0, '100%', '100%').attr({
stroke : 'red'
});
var rectRedSet = paper.set();
var rectRed = paper.rect(100, 10, 20, 20).attr({
'fill' : 'red',
'stroke-opacity' : 0
});
rectRedSet.push(rectRed);
container.push(rectRedSet);
var rectBlackSet = paper.set();
var rectBlack = paper.rect(150, 10, 20, 20).attr({
'fill' : 'black',
'stroke-opacity' : 0
});
rectBlackSet.push(rectBlack);
container.push(rectBlackSet);
var rectBlueSet = paper.set();
rectBlue = paper.rect(0, 0, 20, 20).attr({
'fill' : 'blue',
'stroke-opacity' : 0
});
rectBlueSet.push(rectBlue);
rectBlueSet.transform('t50,150');
container.push(rectBlueSet);
var containerBBox = container.getBBox();
paper.rect(containerBBox.x, containerBBox.y, containerBBox.width, containerBBox.height).attr({
stroke : 'black'
});
//trying to get the entire container and its children to move to 0,0
//commenting out the transform below will keep rectBlue in the container...?
container.transform('t0,0');
A "set" in Raphael is not like a "group" in SVG. A set in Raphael is just a collection of elements that you can manipulate at the same time. So when you set the transform on the container set, it is really just setting the transform on every element inside the set, overwriting any previous transform settings.
You can append or prepend to existing transformations in Raphael using "..." notation.You need to change your last line to:
container.transform("...t0,0")
But "t0,0" doesn't actually move anything anywhere. If you want to move the container so the top left corner is at 0,0, then you need to write:
container.transform('...t-' + containerBBox.x + ',-' + containerBBox.y);
I started to play a little bit with raphaeljs, however I'm having a small problem when dragging and applying a transformation to a Paper.set()
Here is my example: http://jsfiddle.net/PQZmp/2/
1) Why is the drag event added only to the marker and not the slider?
2) The transformation is supposed to be relative(i.e. translate by and not translate to), however if I drag the marker twice, the second dragging starts from the beginning and not from the end of the first.
EDIT:
After the response of Zero, I created a new JSFiddle example: http://jsfiddle.net/9b9W3/1/
1) It would be cool if this referenced the set instead of the first element of the set. Can't this be done with dragger.apply(slider)? I tried it, but only works on the first execution of the method (perhaps inside Raphael it is already being done but to the first element inside the set instead of the set)
2) According to Raphael docs the transformation should be relative to the object position (i.e. translate by and not translate to). But it is not what is happening according to the jsfiddle above (check both markers drag events).
3) So 2) above creates a third question. If a transform("t30,0") is a translation by 30px horizontally, how is the origin calculated? Based on attr("x") or getBBox().x?
The drag event is actually being added to both the marker and the slider -- but your slider has a stroke-width of 1 and no fill, so unless you catch the exact border, the click "falls through" to the canvas.
Behind that is another issue: the drag is being applied to both elements, but this in your drag handler references a specific element, not the set -- so both elements will drag independently from each other.
Lastly: the reason that each drag is starting from the initial position is because the dx, dy parameters in dragger are relative to the coordinates of the initial drag event, and your transform does not take previous transforms into account. Consider an alternative like this:
var r = new Raphael(0, 0, 400, 200);
var marker = r.path("M10,0L10,100").attr({"stroke-width": 5});
var button = r.rect(0, 0, 20, 20, 1).attr( { 'stroke-width': 2, fill: 'white' } );
var slider = r.set( marker, button );
var startx, starty;
var startDrag = function(){
var bbox = slider.getBBox();
startx = bbox.x;
starty = bbox.y;
console.log(this);
}, dragger = function(dx, dy){
slider.transform("t" + ( startx + dx ) + "," + starty );
}, endDrag = function(){
};
slider.drag(dragger, startDrag, endDrag);
To address your updates:
I believe you can specify the context in which the drag function will be executed as optional fourth, fifth, and six parameters to element.drag. I haven't tried this myself, but it looks like this should work great:
slider.drag( dragger, startDrag, endDrag, slider, slider, slider );
The transformation is relative to the object position. This works great for the first slider because its starting position is 0, but not so great for the second slider because...
...the transformation for min/max sliders should actually be relative to the scale, not the individual markers. Thus you will notice that your max slider (the red one) returns to its initial position just as you drag the mouse cursor back over the zero position. Make sense?
var position;
var rect = paper.rect(20, 20, 40, 40).attr({
cursor: "move",
fill: "#f00",
stroke: "#000"
});
t = paper.text(70,70, 'test').attr({
"font-size":16,
"font-family":
"Arial, Helvetica, sans-serif"
});
var st = paper.set();
st.push(rect, t);
rect.mySet = st;
rect.drag(onMove, onStart, onEnd);
onStart = function () {
positions = new Array();
this.mySet.forEach(function(e) {
var ox = e.attr("x");
var oy = e.attr("y");
positions.push([e, ox, oy]);
});
}
onMove = function (dx, dy) {
for (var i = 0; i < positions.length; i++) {//you can use foreach but I want to
// show that is a simple array
positions[i][0].attr({x: positions[i][1] + dx, y: positions[i][2] + dy});
}
}
onEnd = function() {}
I am hoping you might be able to help me determine if the following animation is allowed in raphael.js. I am trying to have an element fly off the page, the idea is to have it appear to fall/fly off in 3D. I am able to tell the element to rotate X degrees and slide off but its lacking the look of the element being independent of hte background. What I would like to do is be able to tell raphael to rotate the top corner "out" as it falls giving the illusion of it falling out of view as picture falling off of a wall. Is this even possible or does Raphael only operate in two dimensional space?
Raphael only deals with 2D space. To implement a 3D flip you have to fake it. Thankfully Raphael implements Scale(sx,sy,x,y) as a transform op, so you can scale about an origin to fake a 3D 'flip' rotation.
For example:
Raphael.el.flipXTransform = function (parentBbox) {
var x = this.getBBox().x;
var width = this.getBBox().width;
parentBbox = parentBbox || { width:width, x: x};
var parentWidth = parentBbox.width;
var parentX = parentBbox.x;
var originX = parentX - x + parentWidth / 2;
return 's-1,1,' + originX + ',0';
};
Raphael.el.flipX = function (duration, easing, parentBbox) {
duration = duration || 500;
easing = easing || 'easeInOut';
var scale = this.flipXTransform(parentBbox);
this.animate({ transform: '...' + scale }, duration, easing);
};
Here's a fiddle example for you to play with. The downside is this doesn't convey a perspective like a true 3D rotate does.