How to align 2 SCNNode's together horizontally? - swift

I'm trying to align two SCNNodes together but can't seem to figure out how. This is what I have right now:
cube.scale = SCNVector3(x: 0.1, y: 0.1, z: 0.1)
cube.position = SCNVector3(0, 3, -3)
cubeTwo.scale = SCNVector3(x: 0.15, y: 0.15, z: 0.15)
cubeTwo.position = SCNVector3(0.5, 3, -3)
cubeThree.scale = SCNVector3(x: 0.2, y: 0.2, z: 0.2)
cubeThree.position = SCNVector3(1, 3, -3)
How can I achieve this? Thank you!!

To align them horizontally, set the position Y values to be the same. X is left-right, Y is up-down, Z is near-far (until you start moving the camera around!).

parentNode.addChildNode(cube)
parentNode.addChildNode(cubeTwo)
parentNode.addChildNode(cubeThree)
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
parentNode.position = self.getPositionRelativeToCameraView(distance: 1.0).position
}
func getPositionRelativeToCameraView(distance: Float) -> (position: SCNVector3, rotation: SCNVector4) {
var x = Float()
var y = Float()
var z = Float()
let cameraLocation = self.sceneView.pointOfView!.position //else { return (SCNVector3Zero) }
let rotation = self.sceneView.pointOfView!.rotation //else { return (SCNVector3Zero) }
let direction = calculateCameraDirection(cameraNode: rotation)
x = cameraLocation.x + distance * direction.x
y = cameraLocation.y + distance * direction.y
z = cameraLocation.z + distance * direction.z
let position = SCNVector3Make(x, y, z)
return (position, rotation)
}
func calculateCameraDirection(cameraNode: SCNVector4) -> SCNVector3 {
let x = -cameraNode.x
let y = -cameraNode.y
let z = -cameraNode.z
let w = cameraNode.w
let cameraRotationMatrix = GLKMatrix3Make(cos(w) + pow(x, 2) * (1 - cos(w)),
x * y * (1 - cos(w)) - z * sin(w),
x * z * (1 - cos(w)) + y*sin(w),
y*x*(1-cos(w)) + z*sin(w),
cos(w) + pow(y, 2) * (1 - cos(w)),
y*z*(1-cos(w)) - x*sin(w),
z*x*(1 - cos(w)) - y*sin(w),
z*y*(1 - cos(w)) + x*sin(w),
cos(w) + pow(z, 2) * ( 1 - cos(w)))
let cameraDirection = GLKMatrix3MultiplyVector3(cameraRotationMatrix, GLKVector3Make(0.0, 0.0, -1.0))
return SCNVector3FromGLKVector3(cameraDirection)
}

Related

How can l set frames for 12 buttons with loop (programatically)?

I'm trying to make Concentration game (with matching cards) and my task is to set frames for the array of 12 UIButtons with for-loop (I tried to realize it with switches, but it wan't right as my mentor said).
So maybe you can help me with this. The code added below works correctly, but l have to escape of switch and make it works with the help of for-loop and universal formulas.
func setFrames() {
for i in 0...11{
var x = CGFloat()
var y = CGFloat()
switch i {
case 0...2:
y = 50
case 3...5:
y = (7/36 * view.bounds.height + 50);
case 6...8:
y = (7/18 * view.bounds.height + 50);
default:
y = (7/12 * view.bounds.height + 50);
}
switch i {
case 0, 3, 6, 9:
x = 1/18 * view.bounds.width; //20
case 1, 4, 7, 10:
x = 3/8 * view.bounds.width; //3/8
default:
x = 3/4 * view.bounds.width - 20;
}
buttonsArray[i].frame = CGRect(x: x, y: y, width: (1/4 * view.bounds.width), height: (1/6 * view.bounds.height))
}
}
You can use something like so:
func placeBtnsIn(frame: CGRect, padding: CGFloat) {
let numCols = 3
let numRows = 4
let buttonWidth = (frame.width - CGFloat(numCols - 1) * padding) / CGFloat(numCols)
let buttonHeight = (frame.height - CGFloat(numRows - 1) * padding) / CGFloat(numRows)
var x: CGFloat
var y: CGFloat
for row in 0...(numRows - 1) {
y = frame.origin.y + (buttonHeight + padding) * CGFloat(row)
for col in 0...(numCols - 1) {
x = frame.origin.x + (buttonWidth + padding) * CGFloat(col)
let button = UIButton()
button.frame = CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight)
self.addSubview(button)
}
}
}
Then in your code use it like so:
placeBtnsIn(frame: yourFrame, padding: 5)
Where yourFrame is a rectangle in which buttons should be drawn, and padding is the distance between button "cells"

white borders around physicsbody of tiles in tileMapNode

I have a tileMapNode in which I want to assign a physics body to some tiles.
I use the following function:
static func addPhysicsBody(to tileMap: SKTileMapNode, and tileInfo: String){
let tileSize = tileMap.tileSize
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2 * tileSize.width
let halfHeight = CGFloat(tileMap.numberOfRows) / 2 * tileSize.height
for column in 0..<tileMap.numberOfColumns{
for row in 0..<tileMap.numberOfRows{
let tileDefinition = tileMap.tileDefinition(atColumn: column, row: row)
let isCorrectTile = tileDefinition?.userData?[tileInfo] as? Bool
if isCorrectTile ?? false && tileInfo == "wall"{
let x = CGFloat(column) * tileSize.width - halfWidth
let y = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.position = CGPoint(x: x, y: y)
tileNode.physicsBody = SKPhysicsBody.init(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2, y: tileSize.height / 2))
tileNode.physicsBody!.isDynamic = false
tileNode.physicsBody!.restitution = 0.0
tileNode.physicsBody!.categoryBitMask = Constants.PhysicsCategories.wall
tileNode.physicsBody!.collisionBitMask = Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
tileMap.addChild(tileNode)
}
}
}
}
This all is working well, but if I run the app now, the tiles where I assigned a physics body to, have white borders. view.showphysics is set to false.
Anyone an idea why there are white borders around the tiles? It looks like this:

How do I compute the intersection of two circle's third set of coordinates under these conditions?

The small circle can move left and right by any amount
and I have to calculate the red dot’s coordinates wherever
its location is, if they intersect. I only calculate this under that condition. I must find the intersection and be sure that it is the intersection on the red dot, and not the intersection below it, so always the one with the higher Y value.
I have solved for all the distances of the triangles and blue dots.
How do I compute the red point?
If you want to look at my current code to help debug it, try this.
My Playground To Test:
//: Playground - noun: a place where people can play
import UIKit
infix operator **
let pretendWidth: CGFloat = 374
let pretendHeight: CGFloat = 7
// Testing scenario is pretendWidth..<(pretendWidth + (pretendHeight / 2))
let spacer: CGFloat = 0.5
extension CGFloat {
public static func **(base: CGFloat, exp: CGFloat) -> CGFloat {
return CGFloat(pow(Double(base), Double(exp)))
}
}
class BottomBarGradientNode: UIView {
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
context.saveGState()
context.clip(to: bounds)
// Gradient Creation
let locations: [CGFloat] = [0, 1]
let components: [CGFloat] = [0.2706, 0.6863, 0.8902, 1, 0, 0.8745, 0.7294, 1]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient: CGGradient = CGGradient(colorSpace: colorSpace, colorComponents: components, locations: locations, count: 2)!
let startPoint = CGPoint(x: bounds.maxX, y: bounds.maxY)
let endPoint = CGPoint(x: bounds.minX, y: bounds.minY)
let halfHeight = bounds.height / 2
let path = UIBezierPath()
let startPointForPath = CGPoint(x: bounds.width - halfHeight, y: 0)
path.move(to: startPointForPath)
let firstCenterPoint = CGPoint(x: bounds.width - halfHeight, y: halfHeight)
let secondCenterPoint = CGPoint(x: pretendWidth - bounds.height, y: 0)
Computation: if bounds.width > (pretendWidth + halfHeight) {
path.addArc(withCenter: secondCenterPoint, radius: bounds.height, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)
} else if bounds.width > pretendWidth {
//
// ------------------------------------------------------------------------------------------------------------------------------------
// Though this looks like a complicated geometry problem, this is really best done as an ugly algebra problem.
// We want the coordinates of the red dot: (x,y)
// We know the red dot is on the big circle and since that circle is not moving I'm going to call it's center (0,0) thus:
// x^2 + y^2 = 49
// We also know that the red dot is on the little circle, it has a moving center but we know that the y value for that
// center is always -3.5. so we'll let the x-value of that center be t:
// (x-t)^2 + (y-3.5)^2 = (3.5)^2
// which expands to:
// x^2 - 2xt + t^2 + y^2 -7y + (3.5)^2 = (3.5)^2
// which when we plug in our other equation simplifies to:
// y = (1/7)(-2tx + 49 + t^2)
// plugging that back into the first equation gives:
// x^2 + ((1/7)(-2tx + 49 + t^2))^2 = 49
// which is terrible to look out but turns out to be a quadratic equation in x, so from this point you'd just simplify
// and plug it into the quadratic formula. Pick the value of x that is smaller in magnitude (be careful about negatives
// here). Then plug that x back into the first equation to solve for y.
// ------------------------------------------------------------------------------------------------------------------------------------
//
let boundsHeightSquared = bounds.height ** 2
let distanceFromOtherCenter = firstCenterPoint.x - secondCenterPoint.x
// x^2 + ((1/7)(-2tx + 49 + t^2))^2 = 49 <<<< translates to VVVVVV
//
// ((4/49)t^2 + 1)(x^2) + (-4t - (4t^3/49))(x) + (2t^2 + (t^4)/49) = 0
// ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
// value1(a) value2(b) value3(c)
let value1 = ((4 * (distanceFromOtherCenter ** 2)) / boundsHeightSquared) + 1
let value2 = (-4 * distanceFromOtherCenter) - ((4 * (distanceFromOtherCenter ** 3)) / boundsHeightSquared)
let value3 = (2 * (distanceFromOtherCenter ** 2)) + ((distanceFromOtherCenter ** 4) / boundsHeightSquared)
let (first, second) = getQuadraticValues(a: value1, b: value2, c: value3)
// guarentee positive values
var areBothGreaterThanZero: Bool = false
var chosenX: CGFloat!
if first < 0 { chosenX = second }
else if second < 0 { chosenX = first }
else { chosenX = first < second ? first : second; areBothGreaterThanZero = true }
// y = (1/7)(-2tx + 49 + t^2)
var chosenY = (1 / bounds.height) * ((-2 * distanceFromOtherCenter * chosenX) + boundsHeightSquared - (distanceFromOtherCenter ** 2))
// last check on weird values
if chosenY < 0 && areBothGreaterThanZero {
chosenX = first < second ? first : second
chosenY = (1 / bounds.height) * ((-2 * distanceFromOtherCenter * chosenX) + boundsHeightSquared - (distanceFromOtherCenter ** 2))
}
// Computatation failed. Show full segment.
if chosenY < 0 {
print("Computation Failed")
path.addArc(withCenter: secondCenterPoint, radius: bounds.height, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)
break Computation
}
// true point
let intersectingPoint = CGPoint(x: chosenX + secondCenterPoint.x, y: chosenY)
// c^2 = a^2 + b^2 - 2abCOS(C)
// (a^2 + b^2 - c^2) / 2ab = COS(C)
let topPoint = CGPoint(x: firstCenterPoint.x, y: 0)
// compute c (distance)
let firstDistanceBetweenPoints = getDistanceBetweenTwoPoints(firstPoint: intersectingPoint, secondPoint: topPoint)
// where a and b are halfHeight
let firstCosC = getCosC(a: halfHeight, b: halfHeight, c: firstDistanceBetweenPoints)
let firstAngle = acos(firstCosC)
path.addArc(withCenter: firstCenterPoint, radius: halfHeight, startAngle: CGFloat.pi * 1.5, endAngle: CGFloat.pi * 1.5 + firstAngle, clockwise: true)
// c^2 = a^2 + b^2 - 2abCOS(C)
// (a^2 + b^2 - c^2) / 2ab = COS(C)
let lastPoint = CGPoint(x: pretendWidth, y: 0)
// compute c (distance)
let secondDistanceBetweenPoints = getDistanceBetweenTwoPoints(firstPoint: lastPoint, secondPoint: intersectingPoint)
// where a and b are bounds.height
let secondCosC = getCosC(a: bounds.height, b: bounds.height, c: secondDistanceBetweenPoints)
let secondAngle = acos(secondCosC)
path.addArc(withCenter: secondCenterPoint, radius: bounds.height, startAngle: secondAngle, endAngle: CGFloat.pi / 2, clockwise: true)
} else {
path.addArc(withCenter: firstCenterPoint, radius: halfHeight, startAngle: CGFloat.pi * 1.5, endAngle: CGFloat.pi / 2, clockwise: true)
}
path.addLine(to: CGPoint(x: bounds.height, y: bounds.height))
let finalCenterPoint = CGPoint(x: bounds.height, y: 0)
path.addArc(withCenter: finalCenterPoint, radius: bounds.height, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true)
path.addLine(to: startPointForPath)
path.close()
path.addClip()
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: .drawsAfterEndLocation)
context.restoreGState()
}
}
func getDistanceBetweenTwoPoints(firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
let diffX = (firstPoint.x - secondPoint.x) ** 2
let diffY = (firstPoint.y - secondPoint.y) ** 2
return sqrt(diffX + diffY)
}
func getSlopeBetweenTwoPoints(firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
let diffY = firstPoint.y - secondPoint.y
let diffX = firstPoint.x - secondPoint.x
return diffY / diffX
}
func getHypotenuse(firstDistance: CGFloat, secondDistance: CGFloat) -> CGFloat {
return sqrt((firstDistance ** 2) + (secondDistance ** 2))
}
func getQuadraticValues(a: CGFloat, b: CGFloat, c: CGFloat) -> (CGFloat, CGFloat) {
let first = (-b + sqrt((b ** 2) - (4 * a * c))) / (2 * a)
let second = (-b - sqrt((b ** 2) - (4 * a * c))) / (2 * a)
return (first, second)
}
func getCosC(a: CGFloat, b: CGFloat, c: CGFloat) -> CGFloat {
// (a^2 + b^2 - c^2) / 2ab = COS(C)
return ((a ** 2) + (b ** 2) - (c ** 2)) / (2 * a * b)
}
// Testing scenario is pretendWidth..<(pretendWidth + (height / 2))
let bounds = CGRect(x: 0, y: 0, width: pretendWidth + spacer, height: pretendHeight)
let bar = BottomBarGradientNode(frame: bounds)
Find both points of intersection then pick the appropriate one. Or formulate solution in terms of y coordinate then pick higher solution there to compute x coordinate for that.
The equation of a circle 1 is (x2+y2)+a1x+b1y+c1=0. Write both circles in this form, then subtract one equation from the other. The quadratic terms will cancel out, and the remaining equation describes the radical axis of the circles. ax+by+c=0 where a=a1−a2 and so on. Solve for x=−(by+c)/a. Plug this term into one of the original equations for the circle, and you have a quadratic equation in y.
Now a quadratic equation in y is of the form py2+qy+r=0 and has solutions −q±sqrt(q2−4pr)/2p. Look at the sign of p, then pick that same sign in front of the square root to get the solution with larger y value. Plug that back into the equation of the radical axis to compute the x coordinate.
If there is no intersection, q2−4pr < 0 and your solutions would become complex. If a=0 your radical axis is horizontal so you can't parametrize it by y value, and picking a solution by y value doesn't make any sense.

SceneKit - Rotate camera with mouse

I'm trying to achieve an fps control mechanism. The problem is that my camera starts moving in the wanted direction then jumps back and forth... also I can not figure out how to connect 2 or more axis. Here is my code:
func gameView(didReceiveMouseMovedEvent event: NSEvent) {
if let p = self.lastMouseP{
var x:CGFloat = 0
x = event.locationInWindow.x - p.x
var y:CGFloat = 0
y = event.locationInWindow.y - p.y
self.player.move(cameraByX: 0, y: x, z: y, w: 0)
}
self.lastMouseP = event.locationInWindow
}
And in player file:
func move(cameraByX x: CGFloat, y: CGFloat, z:CGFloat, w:CGFloat){
let rot = self.head.rotation
let xx = (x)*CGFloat(M_PI)/180.0 + rot.x
let yy = (y)*CGFloat(M_PI)/180.0 + rot.y
let zz = (z)*CGFloat(M_PI)/180.0 + rot.z
//let xr = SCNMatrix4MakeRotation(xx, 1, 0, 0)
let yr = SCNMatrix4MakeRotation(yy, 0, 1, 0)
let zr = SCNMatrix4MakeRotation(zz, 0, 0, 1)
self.head.transform = SCNMatrix4Mult(self.head.transform, zr)
}

Rotating a CGPoint around another CGPoint

Okay so I want to rotate CGPoint(A) 50 degrees around CGPoint(B) is there a good way to do that?
CGPoint(A) = CGPoint(x: 50, y: 100)
CGPoint(B) = CGPoint(x: 50, y: 0)
Here's what I want to do:
This is really a maths question. In Swift, you want something like:
func rotatePoint(target: CGPoint, aroundOrigin origin: CGPoint, byDegrees: CGFloat) -> CGPoint {
let dx = target.x - origin.x
let dy = target.y - origin.y
let radius = sqrt(dx * dx + dy * dy)
let azimuth = atan2(dy, dx) // in radians
let newAzimuth = azimuth + byDegrees * CGFloat(M_PI / 180.0) // convert it to radians
let x = origin.x + radius * cos(newAzimuth)
let y = origin.y + radius * sin(newAzimuth)
return CGPoint(x: x, y: y)
}
There are lots of ways to simplify this, and it's a perfect case for an extension to CGPoint, but I've left it verbose for clarity.
public extension CGFloat {
///Returns radians if given degrees
var radians: CGFloat{return self * .pi / 180}
}
public extension CGPoint {
///Rotates point by given degrees
func rotate(origin: CGPoint? = CGPoint(x: 0.5, y: 0.5), _ byDegrees: CGFloat) -> CGPoint {
guard let origin = origin else {return self}
let rotationSin = sin(byDegrees.radians)
let rotationCos = cos(byDegrees.radians)
let x = (self.x * rotationCos - self.y * rotationSin) + origin.x
let y = (self.x * rotationSin + self.y * rotationCos) + origin.y
return CGPoint(x: x, y: y)
}
}
Usage
var myPoint = CGPoint(x: 40, y: 50).rotate(45)
var myPoint = CGPoint(x: 40, y: 50).rotate(origin: CGPoint(x: 0, y: 0), 45)