Created
November 19, 2014 15:48
-
-
Save sketchytech/b707c17707085a767923 to your computer and use it in GitHub Desktop.
Swift: 3D drawing, digging a hole (with and without water)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import QuartzCore | |
class ViewController: UIViewController { | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view, typically from a nib. | |
// select cube rotation | |
let adjustment:CGFloat = 90 | |
let radius:CGFloat = 50 | |
let cubePoint = CGPoint(x: 100, y: 250) | |
// draw cube | |
let cube = drawCube(cubePoint.x, cubePoint.y, radius, adjustment, UIColor(hue: 60/360, saturation: 80/100, brightness: 100/100, alpha: 1.0)) | |
for c in cube { | |
self.view.layer.addSublayer(c) | |
} | |
// draw pyramid | |
let pyramid = drawPyramid(cubePoint.x, cubePoint.y-radius, radius, adjustment, UIColor(hue: 197/360, saturation: 65/100, brightness: 100/100, alpha: 1.0)) | |
for p in pyramid { | |
self.view.layer.addSublayer(p) | |
} | |
// tiles | |
var tilePos = positionNextCube(x: cubePoint.x, y: cubePoint.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightBehind) | |
for a in 1...2 { | |
let tile = drawCubeBase(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: 86/100, alpha: 1.0)) | |
tile.zPosition = -1 | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightBehind) | |
self.view.layer.addSublayer(tile) | |
} | |
// second cube | |
let secondCubePosition = positionNextCube(x: cubePoint.x, y: cubePoint.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 3, position:.RightBehind) | |
let secondCube = drawCube(secondCubePosition.x, secondCubePosition.y, radius, adjustment, UIColor(hue: 60/360, saturation: 80/100, brightness: 100/100, alpha: 1.0)) | |
for c in secondCube { | |
self.view.layer.addSublayer(c) | |
} | |
// second pyramid | |
let secondPyramid = drawPyramid(secondCubePosition.x, secondCubePosition.y-radius, radius, adjustment, UIColor(hue: 197/360, saturation: 65/100, brightness: 100/100, alpha: 1.0)) | |
for p in secondPyramid { | |
self.view.layer.addSublayer(p) | |
} | |
// second tiles | |
tilePos = positionNextCube(x: secondCubePosition.x, y: secondCubePosition.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightFront) | |
for a in 1...2 { | |
let tile = drawCubeBase(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: 86/100, alpha: 1.0)) | |
tile.zPosition = -1 | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightFront) | |
self.view.layer.addSublayer(tile) | |
} | |
// third cube | |
let thirdCubePosition = positionNextCube(x: secondCubePosition.x, y: secondCubePosition.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 3, position:.RightFront) | |
let thirdCube = drawCube(thirdCubePosition.x, thirdCubePosition.y, radius, adjustment, UIColor(hue: 60/360, saturation: 80/100, brightness: 100/100, alpha: 1.0)) | |
for c in thirdCube { | |
self.view.layer.addSublayer(c) | |
} | |
// third pyramid | |
let thirdPyramid = drawPyramid(thirdCubePosition.x, thirdCubePosition.y-radius, radius, adjustment, UIColor(hue: 197/360, saturation: 65/100, brightness: 100/100, alpha: 1.0)) | |
for p in thirdPyramid { | |
self.view.layer.addSublayer(p) | |
} | |
// third tiles | |
tilePos = positionNextCube(x: thirdCubePosition.x, y: thirdCubePosition.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.LeftFront) | |
for a in 1...3 { | |
let tile = drawCubeBase(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: 86/100, alpha: 1.0)) | |
tile.zPosition = -1 | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.LeftFront) | |
self.view.layer.addSublayer(tile) | |
} | |
// fourth cube | |
let fourthCubePosition = positionNextCube(x: thirdCubePosition.x, y: thirdCubePosition.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 3, position:.LeftFront) | |
let fourthCube = drawCube(fourthCubePosition.x, fourthCubePosition.y, radius, adjustment, UIColor(hue: 60/360, saturation: 80/100, brightness: 100/100, alpha: 1.0)) | |
for c in fourthCube { | |
// self.view.layer.addSublayer(c) | |
} | |
// fourth pyramid | |
let fourthPyramid = drawPyramid(fourthCubePosition.x, fourthCubePosition.y-radius, radius, adjustment, UIColor(hue: 197/360, saturation: 65/100, brightness: 100/100, alpha: 1.0)) | |
for p in fourthPyramid { | |
// self.view.layer.addSublayer(p) | |
} | |
// fourth tiles | |
tilePos = positionNextCube(x: fourthCubePosition.x, y: fourthCubePosition.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.LeftBehind) | |
for a in 1...2 { | |
let tile = drawCubeBase(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: 86/100, alpha: 1.0)) | |
tile.zPosition = -1 | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.LeftBehind) | |
self.view.layer.addSublayer(tile) | |
} | |
// light hole tiles | |
tilePos = positionNextCube(x: cubePoint.x, y: cubePoint.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightFront) | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightBehind) | |
let tile = drawCubeBase(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: 65/100, alpha: 1.0)) | |
tile.zPosition = -1 | |
self.view.layer.addSublayer(tile) | |
// corner hole tiles | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightBehind) | |
var tileCorner = cornerHoleTile(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: 65/100, alpha: 1.0)) | |
tileCorner.zPosition = -1 | |
self.view.layer.addSublayer(tileCorner) | |
// dark hole tile | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.RightFront) | |
let tileDark = drawCubeBase(tilePos.x, tilePos.y, radius, adjustment, UIColor(hue: 117/360, saturation: 29/100, brightness: (65/100)*0.8, alpha: 1.0)) | |
tileDark.zPosition = -1 | |
self.view.layer.addSublayer(tileDark) | |
// corner hole tiles | |
tilePos = positionNextCube(x: tilePos.x, y: tilePos.y, radius: radius, sides: 6, adjustment:adjustment, spaces: 1, position:.LeftFront) | |
let holeColor = UIColor(hue: 117/360, saturation: 29/100, brightness: 65/100, alpha: 1.0) | |
let waterColor = UIColor(hue: 192/360, saturation: 67/100, brightness: 79/100, alpha: 1.0) | |
tileCorner = cornerHoleTile(tilePos.x, tilePos.y, radius, adjustment, holeColor) | |
tileCorner.zPosition = -1 | |
self.view.layer.addSublayer(tileCorner) | |
} | |
} | |
func degree2radian(a:CGFloat)->CGFloat { | |
let b = CGFloat(M_PI) * a/180 | |
return b | |
} | |
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 | |
} | |
func drawCube (x:CGFloat,y:CGFloat,radius:CGFloat, adjustment:CGFloat, color:UIColor)->[CAShapeLayer] { | |
// --- Color is shaded with light coming from above and to the right --- // | |
var h:CGFloat = 0 | |
var s:CGFloat = 0 | |
var b:CGFloat = 0 | |
color.getHue(&h, saturation: &s, brightness: &b, alpha: nil) | |
// --- Left side of cube (visible) --- // | |
let leftSide = CAShapeLayer() | |
let leftSidePath = cubeLeft(x: x, y: y, radius: radius, sides: 6, adjustment:adjustment) | |
leftSide.path = leftSidePath | |
leftSide.fillColor = UIColor(hue: h, saturation: s, brightness: b*0.7, alpha: 1.0).CGColor | |
// --- Front side of cube (visible) --- // | |
let frontSide = CAShapeLayer() | |
let frontSidePath = cubeFront(x: x, y: y, radius: radius, sides: 6, adjustment:adjustment) | |
frontSide.path = frontSidePath | |
frontSide.fillColor = UIColor(hue: h, saturation: s, brightness: b*0.85, alpha: 1.0).CGColor | |
let topSide = CAShapeLayer() | |
let topSidePath = cubeTop(x: x, y: y, radius: radius, sides: 6, adjustment:adjustment) | |
topSide.path = topSidePath | |
topSide.fillColor = UIColor(hue: h, saturation: s, brightness: b*0.95, alpha: 1.0).CGColor | |
return [leftSide,frontSide,topSide] | |
} | |
func cubeTop(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[0] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[1].x, points[1].y) | |
CGPathAddLineToPoint(path, nil, x, y) | |
CGPathAddLineToPoint(path, nil, points[5].x, points[5].y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func cubeFront(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[5] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[4].x, points[4].y) | |
CGPathAddLineToPoint(path, nil, points[3].x, points[3].y) | |
CGPathAddLineToPoint(path, nil, x, y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func cubeLeft(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[5] | |
CGPathMoveToPoint(path, nil, x, y) | |
CGPathAddLineToPoint(path, nil, points[1].x, points[1].y) | |
CGPathAddLineToPoint(path, nil, points[2].x, points[2].y) | |
CGPathAddLineToPoint(path, nil, points[3].x, points[3].y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func cubeBase(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[2] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[3].x, points[3].y) | |
CGPathAddLineToPoint(path, nil, points[4].x, points[4].y) | |
CGPathAddLineToPoint(path, nil, x, y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func cornerHoleTileLeft(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[2] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[3].x, points[3].y) | |
CGPathAddLineToPoint(path, nil, x, y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func cornerHoleTileRight(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[3] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[4].x, points[4].y) | |
CGPathAddLineToPoint(path, nil, x, y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func drawCubeBase (x:CGFloat,y:CGFloat,radius:CGFloat, adjustment:CGFloat, color:UIColor)->CAShapeLayer { | |
// --- Left side of cube (visible) --- // | |
let cubeBaseObject = CAShapeLayer() | |
let cubeBasePath = cubeBase(x: x, y: y, radius: radius, sides: 6, adjustment:adjustment) | |
cubeBaseObject.path = cubeBasePath | |
cubeBaseObject.fillColor = color.CGColor | |
return cubeBaseObject | |
} | |
func cornerHoleTile (x:CGFloat,y:CGFloat,radius:CGFloat, adjustment:CGFloat, color:UIColor)->CAShapeLayer { | |
// --- Color is shaded with light coming from above and to the right --- // | |
var h:CGFloat = 0 | |
var s:CGFloat = 0 | |
var b:CGFloat = 0 | |
color.getHue(&h, saturation: &s, brightness: &b, alpha: nil) | |
let corner = CAShapeLayer() | |
let left = CAShapeLayer() | |
let leftPath = cornerHoleTileLeft(x: x, y: y, radius: radius, sides: 6, adjustment:adjustment) | |
left.path = leftPath | |
left.fillColor = color.CGColor | |
let right = CAShapeLayer() | |
let rightPath = cornerHoleTileRight(x: x, y: y, radius: radius, sides: 6, adjustment:adjustment) | |
right.path = rightPath | |
right.fillColor = UIColor(hue: h, saturation: s, brightness: b*0.8, alpha: 1.0).CGColor | |
corner.addSublayer(left) | |
corner.addSublayer(right) | |
return corner | |
} | |
func pyramidFront(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> CGPathRef { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[4] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[5].x/2+points[1].x/2, points[5].y/2+points[1].y/2) | |
CGPathAddLineToPoint(path, nil, points[3].x, points[3].y) | |
CGPathCloseSubpath(path) | |
return path | |
} | |
func pyramidLeft(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat) -> (path:CGPathRef,point:CGPoint) { | |
let path = CGPathCreateMutable() | |
let points = polygonPointArray(sides,x,y,radius,adjustment: adjustment) | |
var cpg = points[3] | |
CGPathMoveToPoint(path, nil, cpg.x, cpg.y) | |
CGPathAddLineToPoint(path, nil, points[5].x/2+points[1].x/2, points[5].y/2+points[1].y/2) | |
CGPathAddLineToPoint(path, nil, points[2].x, points[2].y) | |
CGPathCloseSubpath(path) | |
let topOfPyramid = CGPoint(x:points[5].x/2+points[1].x/2, y:points[5].y/2+points[1].y/2) | |
return (path,topOfPyramid) | |
} | |
func drawPyramid(x:CGFloat,y:CGFloat,radius:CGFloat,adjustment:CGFloat, color:UIColor)->[CAShapeLayer] { | |
// --- Color is shaded with light coming from above and to the right --- // | |
var h:CGFloat = 0 | |
var s:CGFloat = 0 | |
var b:CGFloat = 0 | |
color.getHue(&h, saturation: &s, brightness: &b, alpha: nil) | |
let pyramidFrontLayer = CAShapeLayer() | |
let pyramidFrontPath = pyramidFront(x: x, y: y, radius: radius, sides: 6, adjustment: adjustment) | |
pyramidFrontLayer.path = pyramidFrontPath | |
pyramidFrontLayer.fillColor = UIColor(hue: h, saturation: s, brightness: b*0.85, alpha: 1.0).CGColor | |
let pyramidLeftLayer = CAShapeLayer() | |
let pyramidLeftPath = pyramidLeft(x: x, y: y, radius: radius, sides: 6, adjustment: adjustment) | |
pyramidLeftLayer.path = pyramidLeftPath.path | |
pyramidLeftLayer.fillColor = UIColor(hue: h, saturation: s, brightness: b*0.7, alpha: 1.0).CGColor | |
return [pyramidFrontLayer,pyramidLeftLayer] | |
} | |
func positionNextCube(#x:CGFloat, #y:CGFloat, #radius:CGFloat, #sides:Int, #adjustment:CGFloat, #spaces:Int, #position:CubeRelativePosition) -> CGPoint { | |
// to get the origin of a hexagon so many spaces away, we simply multiply the radius by the number of spaces since each origin is space exactly the length of the radius | |
var points = polygonPointArray(sides,x,y,radius*CGFloat(spaces),adjustment: adjustment) | |
return points[position.rawValue] | |
} | |
enum CubeRelativePosition:Int { | |
case Above = 0, LeftBehind, LeftFront, Underneath, RightFront, RightBehind | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment