Skip to content

Instantly share code, notes, and snippets.

@sketchytech
Created December 5, 2014 12:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sketchytech/5c6bdd9a5e7bc75133da to your computer and use it in GitHub Desktop.
Save sketchytech/5c6bdd9a5e7bc75133da to your computer and use it in GitHub Desktop.
Swift: Spinning Prism
import UIKit
class ViewController: UIViewController {
var topOfCube:CAShapeLayer!
var leftOfCube:CAShapeLayer!
var frontOfCube:CAShapeLayer!
var backOfCube:CAShapeLayer!
var rightOfCube:CAShapeLayer!
var baseOfCube:CAShapeLayer!
override func viewDidLoad() {
super.viewDidLoad()
backOfCube = CAShapeLayer()
self.view.layer.addSublayer(backOfCube)
baseOfCube = CAShapeLayer()
self.view.layer.addSublayer(baseOfCube)
rightOfCube = CAShapeLayer()
self.view.layer.addSublayer(rightOfCube)
leftOfCube = CAShapeLayer()
leftOfCube.fillColor = UIColor(hue: 117/360, saturation: 29/100, brightness: 56/100, alpha: 1.0).CGColor
self.view.layer.addSublayer(leftOfCube)
frontOfCube = CAShapeLayer()
self.view.layer.addSublayer(frontOfCube)
topOfCube = CAShapeLayer()
topOfCube.fillColor = UIColor(hue: 117/360, saturation: 29/100, brightness: 46/100, alpha: 1.0).CGColor
// self.view.layer.addSublayer(topOfCube)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func animate(sender:AnyObject)
{ let positionX:CGFloat = 200
let positionY:CGFloat = 200
let radius:CGFloat = 125
let repeat = Float.infinity
let duration = 2.5
// roll animation
var newPos = positionNextCubeRotated(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, spaces: 5, position: CubeRelativePosition.RightFront, 30)
// new angle = 60 diagonally down, 120 straight down, 30 almost straight across, 15 flying up through air slightly, 0 flying up more, 270 lift up into the air as if weightless, 180 falling backwards
var caPos = CABasicAnimation(keyPath: "position")
caPos.toValue = NSValue(CGPoint: newPos)
caPos.duration = 12.0
caPos.repeatCount = Float.infinity
caPos.autoreverses = true
// colorChange
var colorChangeRight = CABasicAnimation(keyPath: "fillColor")
colorChangeRight.delegate = self
colorChangeRight.fromValue = UIColor(hue: 117/360, saturation: 29/100, brightness: 76/100, alpha: 1.0).CGColor
colorChangeRight.toValue = UIColor(hue: 117/360, saturation: 29/100, brightness: 96/100, alpha: 1.0).CGColor
colorChangeRight.duration = duration
colorChangeRight.repeatCount = repeat
// colorChange front
var colorChangeFront = CABasicAnimation(keyPath: "fillColor")
colorChangeFront.delegate = self
colorChangeFront.fromValue = UIColor(hue: 117/360, saturation: 29/100, brightness: 96/100, alpha: 1.0).CGColor
colorChangeFront.toValue = UIColor(hue: 117/360, saturation: 29/100, brightness: 56/100, alpha: 1.0).CGColor
colorChangeFront.duration = duration
colorChangeFront.repeatCount = repeat
// cube base
let animationb = CAKeyframeAnimation(keyPath:"path")
animationb.duration = duration
// animationb.calculationMode = kCAAnimationCubic
animationb.values = []
// animation for all angles from 0 to 60
for n in 0...60 {
let path = cubeBaseRotated(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, newangle: CGFloat(n))
animationb.values.append(path)
}
animationb.repeatCount = repeat
animationb.autoreverses = false
animationb.calculationMode = kCAAnimationLinear
// cube top
let animation = CAKeyframeAnimation(keyPath:"path")
animation.duration = duration
animation.values = []
// animation for all angles from 0 to 60
for n in 0...60 {
let path = cubeTopRotatedOntoRightFrontFace(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, newangle: CGFloat(n))
animation.values.append(path)
}
animation.repeatCount = repeat
animation.autoreverses = false
animation.calculationMode = kCAAnimationLinear
// cube right
let rightanimation1 = CAKeyframeAnimation(keyPath:"path")
rightanimation1.values = []
for n in 0...60 {
let path = cubeRightRotated(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, newangle: CGFloat(n))
rightanimation1.values.append(path)
}
rightanimation1.duration = duration
rightanimation1.fillMode = kCAFillModeRemoved
rightanimation1.repeatCount = repeat
rightanimation1.autoreverses = false
rightanimation1.calculationMode = kCAAnimationLinear
// cube left
let animation1 = CAKeyframeAnimation(keyPath:"path")
animation1.values = []
for n in 0...60 {
let path = cubeLeftRotated(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, newangle: CGFloat(n))
animation1.values.append(path)
}
animation1.duration = duration
animation1.fillMode = kCAFillModeRemoved
animation1.repeatCount = repeat
animation1.autoreverses = false
animation1.calculationMode = kCAAnimationLinear
// front of cube
let animation11 = CAKeyframeAnimation(keyPath:"path")
animation11.values = []
for n in 0...60 {
var path = cubeFrontRotated(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, newangle: CGFloat(n))
animation11.values.append(path)}
animation11.duration = duration
animation11.calculationMode = kCAAnimationLinear
animation11.autoreverses = false
animation11.repeatCount = repeat
// back of cube
let animation111 = CAKeyframeAnimation(keyPath:"path")
animation111.values = []
for n in 0...60 {
let path = cubeBackRotated(x: positionX, y: positionY, radius: radius, sides: 6, adjustment: 90, newangle: CGFloat(n))
animation111.values.append(path)}
animation111.duration = duration
animation111.calculationMode = kCAAnimationLinear
animation111.repeatCount = repeat
animation111.autoreverses = false
backOfCube.addAnimation(animation111, forKey: nil)
backOfCube.addAnimation(colorChangeFront, forKey: nil)
backOfCube.addAnimation(caPos, forKey: nil)
frontOfCube.addAnimation(animation11, forKey:nil)
frontOfCube.addAnimation(colorChangeFront, forKey: nil)
frontOfCube.addAnimation(caPos, forKey: nil)
leftOfCube.addAnimation(animation1, forKey:nil)
leftOfCube.addAnimation(caPos, forKey: nil)
rightOfCube.addAnimation(rightanimation1, forKey: nil)
topOfCube.addAnimation(animation, forKey:nil)
rightOfCube.addAnimation(colorChangeRight, forKey: nil)
topOfCube.addAnimation(caPos, forKey: nil)
rightOfCube.addAnimation(caPos, forKey: nil)
}
}
func cubeTopRotatedOntoRightFrontFace(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #newangle:CGFloat) -> CGPathRef {
let path = CGPathCreateMutable()
// HexagonRotatedPoints class takes details and returns new points at rotation angle
let points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius,adjustment: adjustment, direction:.RightFront,newangle:newangle)
CGPathMoveToPoint(path, nil, points.origin.x, points.origin.y)
CGPathAddLineToPoint(path, nil, points.index1.x, points.index1.y)
CGPathAddLineToPoint(path, nil, points.index0.x, points.index0.y)
CGPathAddLineToPoint(path, nil, points.index5.x, points.index5.y)
CGPathCloseSubpath(path)
return path
}
func cubeLeftRotated(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #newangle:CGFloat) -> CGPathRef {
let path = CGPathCreateMutable()
var points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius,adjustment: adjustment,direction: .RightFront,newangle:newangle)
CGPathMoveToPoint(path, nil, x, y)
CGPathAddLineToPoint(path, nil, points.index3.x, points.index3.y)
CGPathAddLineToPoint(path, nil, points.index2.x, points.index2.y)
// CGPathAddLineToPoint(path, nil, points[3].x, points[3].y)
CGPathCloseSubpath(path)
return path
}
func cubeRightRotated(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #newangle:CGFloat) -> CGPathRef {
let path = CGPathCreateMutable()
var points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius,adjustment: adjustment,direction: .RightFront,newangle:newangle)
CGPathMoveToPoint(path, nil, x,y)
CGPathAddLineToPoint(path, nil, points.index4.x, points.index4.y)
CGPathAddLineToPoint(path, nil, points.originrear.x, points.originrear.y)
// CGPathAddLineToPoint(path, nil, points[3].x, points[3].y)
CGPathCloseSubpath(path)
return path
}
func cubeBaseRotated(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #newangle:CGFloat) -> CGPathRef {
let path = CGPathCreateMutable()
var points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius,adjustment: adjustment,direction: .RightFront,newangle:newangle)
CGPathMoveToPoint(path, nil, points.index4.x, points.index1.y)
CGPathAddLineToPoint(path, nil, points.originrear.x, points.originrear.y)
CGPathAddLineToPoint(path, nil, points.index2.x, points.index2.y)
CGPathAddLineToPoint(path, nil, points.index3.x, points.index3.y)
CGPathCloseSubpath(path)
return path
}
func cubeBackRotated(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #newangle:CGFloat) -> CGPathRef {
let path = CGPathCreateMutable()
var points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius,adjustment: adjustment,direction: .RightFront,newangle:newangle)
CGPathMoveToPoint(path, nil, x,y)
CGPathAddLineToPoint(path, nil, points.index2.x, points.index2.y)
CGPathAddLineToPoint(path, nil, points.originrear.x, points.originrear.y)
CGPathCloseSubpath(path)
return path
}
func cubeFrontRotated(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #newangle:CGFloat) -> CGPathRef {
let path = CGPathCreateMutable()
let points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius,adjustment: adjustment, direction:.RightFront,newangle:newangle)
CGPathMoveToPoint(path, nil, x,y)
CGPathAddLineToPoint(path, nil, points.index3.x, points.index3.y)
CGPathAddLineToPoint(path, nil, points.index4.x, points.index4.y)
// CGPathAddLineToPoint(path, nil, points[3].x, points[3].y)
CGPathCloseSubpath(path)
return path
}
enum HexagonIndex:Int {
case Zero, One, Two, Three, Four, Five
}
func degree2radian(a:CGFloat)->CGFloat {
let b = CGFloat(M_PI) * a/180
return b
}
enum HexagonRollOntoDirection {
case RightFront, LeftFront, RightRear, LeftRear, ClockwiseSpin, AntiClockwiseSpin
}
func polygonPointArray(sides:Int,x:CGFloat,y:CGFloat,radius:CGFloat,adjustment:CGFloat=0)->[CGPoint] {
let angle = degree2radian(360/CGFloat(sides))
let cx = x // x origin
let cy = y // y origin
let r = radius // radius of circle
var i = sides
var points = [CGPoint]()
while points.count <= sides {
let xpo = cx - r * cos(angle * CGFloat(i)+degree2radian(adjustment))
let ypo = cy - r * sin(angle * CGFloat(i)+degree2radian(adjustment))
points.append(CGPoint(x: xpo, y: ypo))
i--;
}
return points
}
class HexagonRotatedPoints {
let origin:CGPoint, index0:CGPoint, index1:CGPoint, index2:CGPoint, index3:CGPoint, index4:CGPoint, index5:CGPoint, originrear:CGPoint
init (sides:Int,x:CGFloat,y:CGFloat,radius:CGFloat,adjustment:CGFloat, direction:HexagonRollOntoDirection, newangle:CGFloat) {
// Currently these points supply the points for a HexagonRollOntoDirection.RightFront
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment)
// cube is pivoting on index 3, so the x position never changes
let originX = points[0].x - radius * cos(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(180))
// origin is at a point 60 degrees from the 0 line of index 2, it pivots on the arc of the circle from index 2 (to index 3)
let originY = points[0].y - radius * sin(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(180))
origin = CGPoint(x: originX, y: originY)
// index 0 on the hexagon
let index0X = x - radius * cos(degree2radian(newangle) + degree2radian(adjustment))
let index0Y = y - radius * sin(degree2radian(newangle) + degree2radian(adjustment))
index0 = CGPoint(x: index0X, y: index0Y)
// index 1 is at the position 90 degrees (the adjustment) to index 2 and follows the arc of the index 2 circle (to the origin)
let index1X = x - radius * cos(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(300))
let index1Y = y - radius * sin(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(300))
index1 = CGPoint(x: index1X, y: index1Y)
// index 2 at 240 degrees to the 12 o'clock position of a circle about index 3
let index2x = points[3].x - radius * cos(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(300))
let index2y = points[3].y - radius * sin(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(300))
index2 = CGPoint(x: index2x, y: index2y)
// index 3 on hexagon (moves to index 2) [AJL: not entirely confident index 3 is correct)
let index3X = x - radius * cos(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(180))
let index3Y = y - radius * sin(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(180))
index3 = CGPoint(x: index3X, y: index3Y)
// index 4 on hexagon (moves to index 3)
let index4X = x - radius * cos(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(120))
let index4Y = y - radius * sin(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(120))
index4 = CGPoint(x: index4X, y: index4Y)
// index 5 at 60 degrees to the top position
let index5X = points[0].x - radius * cos(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(120))
let index5Y = points[0].y - radius * sin(degree2radian(newangle) + degree2radian(adjustment) + degree2radian(120))
index5 = CGPoint(x: index5X, y: index5Y)
// rear origin
let originrearX = points[3].x - radius * cos(degree2radian(newangle) + degree2radian(adjustment))
let originrearY = points[3].y - radius * sin(degree2radian(newangle) + degree2radian(adjustment))
originrear = CGPoint(x: originrearX, y: originrearY)
}
}
enum CubeRelativePosition:Int {
case Above = 0, LeftBehind, LeftFront, Underneath, RightFront, RightBehind
}
func positionNextCubeRotated(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #spaces:Int, #position:CubeRelativePosition, newangle:CGFloat) -> CGPoint {
// block rolls along slope, if HexagonRotatedPoints.origin taken would look like steps at 60 degrees
var points = HexagonRotatedPoints(sides: sides,x: x,y: y,radius: radius*CGFloat(spaces),adjustment: adjustment,direction:HexagonRollOntoDirection.RightFront,newangle:newangle).index5
// var newpoints = polygonPointArray(sides, points.x, points.y, radius, adjustment: adjustment)
return points
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment