How to use the Zposition of UIBezierPath() and SKNode? - swift

I have a UIView where i draw lines with UIBezierPath() and where i have SKSHapeNode child. Sometimes, the drawing is superimposed with the nodes. When it is the case, i want the node to be upside of the drawing. But even wehn the nodes have a bigger Zpos, the drawing is upside nodes.
I use this script when i want to draw a line :
private func addLine(fromPoint start: CGPoint, toPoint end:CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.zPosition = 80
line.path = linePath.cgPath
line.strokeColor = UIColor.black.cgColor
line.lineWidth = 5.0
line.lineJoin = CAShapeLayerLineJoin.round
self.view!.layer.addSublayer(line)
}
And i use this script for the SKSHapeNodes :
private func drawBase(map : Int) {
for i in 0...maps.xMaps[map].count - 1 {
let team = maps.mapTeam[map][i]
let weight = CGFloat(game!.baseWeight[i])
let base = SKShapeNode(rectOf: CGSize(width: weight, height: weight))
base.zPosition = 90
base.position = CGPoint(x: maps.xMaps[map][i], y: maps.yMaps[map][i])
if team == 0 {
base.fillColor = .blue
} else if team == 1 {
base.fillColor = .red
} else if team == 2 {
base.fillColor = .green
} else if team == 3 {
base.fillColor = .purple
}
mapEntity.append(base)
self.addChild(base)
}
}
I call the function here :
func drawMap(map : Int){
drawRoad(map: map)
drawBase(map: map)
}
DrawRoad is the function that draw lines :
private func drawRoad(map : Int) {
for i in 0...maps.xRoad[map].count - 1 {
let start = CGPoint(x: maps.xRoad[map][i][0], y: self.frame.height - maps.yRoad[map][i][0])
let end = CGPoint(x: maps.xRoad[map][i][1], y: self.frame.height - maps.yRoad[map][i][1])
addLine(fromPoint: start, toPoint: end)
}
}
As you can see i draw the line before adding the SKShapeNode but it do not work...

Related

In swift for spritekit, how would I change the textures of nodes when they are placed?

I’m currently working on a matching game and when the user touches the nodes (Fruit match cards) I want them to display different images when a user clicks the nodes (Fruit match cards).
This is my current code:
import Foundation
import SpriteKit
class EasyScreen: SKScene {
override func didMove(to view: SKView) {
var background = SKSpriteNode(imageNamed: "Easy Screen Background")
let timerText = SKLabelNode(fontNamed: "Arial")
timerText.fontSize = 40
timerText.fontColor = SKColor.white
timerText.position = CGPoint(x: 20, y: 400)
timerText.zPosition = 1
var counter:Int = 120
timerText.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.run {
counter -= 1
timerText.text = " Time: \(counter)"
print("\(counter)")
if counter <= 0 {
let newScene = TryAgainScreen(fileNamed: "Try Again Screen")
newScene?.scaleMode = .aspectFill
self.view?.presentScene(newScene)
}
},
SKAction.wait(forDuration: 1)
]
)
)
)
background.position = CGPoint(x: 0, y: 0)
background.size.width = self.size.width
background.size.height = self.size.height
background.anchorPoint = CGPoint(x: 0.5,y: 0.5)
let matchCardOne = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardTwo = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardThree = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardFour = SKSpriteNode(imageNamed: "Fruit Match Card")
matchCardOne.name = "FruitMatchCard1"
matchCardTwo.name = "FruitMatchCard2"
matchCardThree.name = "FruitMatchCard3"
matchCardFour.name = "FruitMatchCard4"
matchCardOne.size = CGSize(width: 150, height: 300)
matchCardTwo.size = CGSize(width: 150, height: 300)
matchCardThree.size = CGSize(width: 150, height: 300)
matchCardFour.size = CGSize(width: 150, height: 300)
matchCardOne.zPosition = 1
matchCardTwo.zPosition = 1
matchCardThree.zPosition = 1
matchCardFour.zPosition = 1
matchCardOne.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardTwo.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardThree.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardFour.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardOne.position = CGPoint(x: -125, y: 60)
matchCardTwo.position = CGPoint(x: -125, y: -260)
matchCardThree.position = CGPoint(x: 70, y: 60)
matchCardFour.position = CGPoint(x: 70 , y: -260)
addChild(background)
addChild(matchCardOne)
addChild(matchCardTwo)
addChild(matchCardThree)
addChild(matchCardFour)
addChild(timerText)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view?.isMultipleTouchEnabled = false
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedCardOneNode = self.atPoint(positionInScene)
if let name = touchedCardOneNode.name {
if name == "FruitMatchCard1" {
let newTexture = SKTexture(imageNamed: "Apple")
FruitMatchCard1.init(texture: newTexture)
}
}
let touchTwo = touches.first
let positionInSceneTwo = touch!.location(in: self)
let touchedCardTwoNode = self.atPoint(positionInScene)
if let name = touchedCardTwoNode.name {
if name == "FruitMatchCard2" {
FruitMatchCard2.init(imageNamed: "Banana")
}
}
let touchThree = touches.first
let positionInSceneThree = touch!.location(in: self)
let touchedCardThreeNode = self.atPoint(positionInScene)
if let name = touchedCardThreeNode.name {
if name == "FruitMatchCard3" {
FruitMatchCard3.init(imageNamed: "Apple")
}
}
let touchFour = touches.first
let positionInSceneFour = touch!.location(in: self)
let touchedCardFourNode = self.atPoint(positionInScene)
if let name = touchedCardFourNode.name {
if name == "FruitMatchCard4" {
FruitMatchCard4.init(imageNamed: "Banana")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
I’m trying to change the textures of the nodes in this part of the code. Inside the “if name == “FruitMatchCard” { } “ part of the code. However, when I launch the Xcode simulator the node’s textures aren't changing.
Any advice on how I can do this? Thanks!
Your fundamental problem is that when someone taps a card, you are creating a new card with the new texture and not doing anything with it. Take the following code you have:
FruitMatchCard2.init(imageNamed: "Banana")
This code creates a match card, but you don't do anything with the card.
To change the texture of the card, you must get the sprite node of the tapped card and change its texture. I see in your code you have the following variable:
let touchedCardTwoNode = self.atPoint(positionInScene)
This variable has the card that was touched. Set its texture to the texture you want to use.
touchedCardTwoNode.texture = SKTexture(imageNamed: "Banana")
My SpriteKit is a bit rusty so I can't guarantee that example will compile, but it gives you an idea of what you need to do to change the texture when tapping a card.

bezierPath setfill with diff colours Not Working ( to create a mountain climb color coding )

My goal is to create a chart much like below. Where the diff colours represent the different slope / gradient % for each segment.
My previous code, I have already managed to get the "mountain" outline, now further improved it such that I am now making a set of "boxes" which represents each colour segment. But am facing some issue.
Here is the code I'm using right now.
let myBezier = UIBezierPath()
let nextPt = fillSlopes(at: idx)
if idx > 0 {
let prevPt = fillSlopes(at: idx - 1)
myBezier.move(to: CGPoint(x: prevPt.x, y: height))
myBezier.addLine(to: CGPoint(x: prevPt.x, y: prevPt.y))
myBezier.addLine(to: CGPoint(x: nextPt.x, y: nextPt.y))
myBezier.addLine(to: CGPoint(x: nextPt.x, y: height))
myBezier.addLine(to: CGPoint(x: prevPt.x, y: height))
myBezier.close()
// This is test code, actual code will compare current slope %
// (in an array) and then based on the slope %, will change
// the colour accordingly.
if idx % 2 == 0 {
UIColor.systemRed.setFill()
myBezier.fill()
} else {
UIColor.systemBlue.setFill()
myBezier.fill()
}
This is the results. Unfortunately, the colours is not coming out properly. What Am I missing?
In addition to this, Drawing so many little boxes is really eating up a lot of CPU cycles, if there are other suggestions to get the desired output result is also appreciated. Thanks
Update:
Many Thanks for #seaspell, I realised what was happening. I was putting my let myBezier = UIBezierPath() outside of the for loop hence my [UIBezierPath] array was getting mangled up. eg:
This got things all wrong:
func xxx {
let myBezier = UIBezierPath() // <<<<<< this is wrong
for idx in slopeBars.indices {
// do stuff
}
This is Correct and fixed some performance issue as well:
func xxx {
var slopeBars = [UIBezierPath]()
var slopeBarColorArray = [UIColor]()
for idx in slopeBars.indices {
let myBezier = UIBezierPath() // <<<<< Moved here
let nextPt = fillSlopes(at: idx)
if idx > 0 {
let prevPt = fillSlopes(at: idx - 1)
myBezier.move(to: CGPoint(x: prevPt.x, y: height))
myBezier.addLine(to: CGPoint(x: prevPt.x, y: prevPt.y))
myBezier.addLine(to: CGPoint(x: nextPt.x, y: nextPt.y))
myBezier.addLine(to: CGPoint(x: nextPt.x, y: height))
myBezier.addLine(to: CGPoint(x: prevPt.x, y: height))
myBezier.close()
slopeBars.append(myBezier)
if gradient < 0.0 {
slopeBarColorArray.append(UIColor(cgColor: Consts.elevChart.slope0_0))
} else if gradient < 4.0 {
slopeBarColorArray.append(UIColor(cgColor: Consts.elevChart.slope0_4))
}
for i in 0..<slopeBars.count {
if !slopeBarColorArray.isEmpty {
slopeBarColorArray[i].setStroke()
slopeBarColorArray[i].setFill()
}
slopeBars[i].stroke()
slopeBars[i].fill()
}
}
There's nothing really wrong with using bezier paths for this. You need to use a new one for each bar though and also you don't want to build them in draw(rect:). I suspect that is the cause of the CPU issues.
Take a look at this example I put together.
class BarChartView: UIView {
lazy var dataPoints: [CGFloat] = {
var points = [CGFloat]()
for _ in 1...barCount {
let random = arc4random_uniform(UInt32(bounds.maxY / 2))
points.append(CGFloat(random))
}
return points
}()
var bars = [UIBezierPath]()
private let barCount = 20
lazy var colors: [UIColor] = {
let colors: [UIColor] = [.red, .blue, .cyan, .brown, .darkGray, .green, .yellow, .gray, .lightGray, .magenta, .orange, .purple, .systemPink, .white]
var random = [UIColor]()
for i in 0...barCount {
random.append(colors[Int(arc4random_uniform(UInt32(colors.count)))])
}
return random
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.bars = buildBars()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.bars = buildBars()
}
func buildBars() -> [UIBezierPath] {
let lineWidth = Int(bounds.maxX / CGFloat(barCount))
var bars = [UIBezierPath]()
for i in 0..<barCount {
let line = UIBezierPath()
let leftX = lineWidth * i
let rightX = leftX + lineWidth
let centerX = leftX + (lineWidth / 2)
let nextLeftPoint = (i == 0 ? dataPoints[i] : dataPoints[i - 1])
let nextRightPoint = (i == barCount - 1 ? dataPoints[i] : dataPoints[i + 1])
let currentPoint = dataPoints[i]
//bottom left
line.move(to: CGPoint(x: leftX, y: Int(bounds.maxY)) )
//bottom right
line.addLine(to: CGPoint(x: rightX, y: Int(bounds.maxY)) )
//top right
line.addLine(to: CGPoint(x: rightX, y: Int((currentPoint + nextRightPoint) / 2)) )
//top center
line.addLine(to: CGPoint(x: centerX, y: Int(currentPoint)) )
//top left
line.addLine(to: CGPoint(x: leftX, y: Int((currentPoint + nextLeftPoint) / 2)) )
//close the path
line.close()
bars.append(line)
}
return bars
}
override func draw(_ rect: CGRect) {
super.draw(rect)
for i in 0..<bars.count {
colors[i].setStroke()
colors[i].setFill()
bars[i].stroke()
bars[i].fill()
}
}
}

How to update UIBezierpath and CAShapeLayer path with pan gesture of UIView?

'''
import UIKit
class CanvasView: UIView {
var circleViewTag = 1000
var coordinatePoints: [String] = ["243,103","534,86","243,286","426,286"] {
didSet {
self.updateCoordinateArray()
self.drawPoints()
}
}
fileprivate var coordArray: [CGPoint] = []
var shape = CAShapeLayer()
var path = UIBezierPath()
/*// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}*/
private func drawPoints() -> Void {
CommonMethods.printLog("\(coordinatePoints)")
self.layer.addSublayer(shape)
shape.opacity = 0.5
shape.lineWidth = 2
shape.lineJoin = CAShapeLayerLineJoin.miter
shape.strokeColor = UIColor.white.cgColor
shape.fillColor = UIColor(red: 1.0, green: 0.2, blue: 0.2, alpha: 0.2).cgColor
if let firstCoord = self.coordArray.first {
path.move(to: firstCoord)
}
for (index, cgPoint) in self.coordArray.enumerated() {
self.drawCircularPoint(points: cgPoint)
if index == 0 {
continue
}
path.addLine(to: cgPoint)
}
path.close()
shape.path = path.cgPath
//self.drawLineBetweenPoints()
}
private func drawCircularPoint(points: CGPoint) -> Void {
let circleView = UIView.init(frame: .zero)
circleViewTag = circleViewTag + 1
circleView.tag = circleViewTag
circleView.frame.size = CGSize.init(width: 30.0, height: 30.0)
circleView.layer.cornerRadius = 15.0
circleView.center = points
circleView.backgroundColor = .random()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.draggedView(_:)))
panGesture.view?.tag = circleView.tag
circleView.isUserInteractionEnabled = true
circleView.addGestureRecognizer(panGesture)
self.addSubview(circleView)
}
#objc func draggedView(_ sender:UIPanGestureRecognizer){
guard let getTag = sender.view?.tag else { return }
if let viewToDrag = self.viewWithTag(getTag) as? UIView {
var currentPoint: CGPoint = .zero
if path.contains(viewToDrag.center) {
print("YES")
currentPoint = path.currentPoint
}
let translation = sender.translation(in: self)
viewToDrag.center = CGPoint(x: viewToDrag.center.x + translation.x, y: viewToDrag.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self)
if sender.state == .began && currentPoint != .zero {
let coordinateIndex = self.coordArray.firstIndex { (cgpoint) -> Bool in
if currentPoint == cgpoint {
return true
}
return false
}
if coordinateIndex != nil {
self.coordArray[coordinateIndex!] = viewToDrag.center
self.shape.removeFromSuperlayer()
self.path.removeAllPoints()
self.setNeedsDisplay()
self.layer.addSublayer(self.shape)
self.shape.opacity = 0.5
self.shape.lineWidth = 2
self.shape.lineJoin = CAShapeLayerLineJoin.miter
self.shape.strokeColor = UIColor.white.cgColor
self.shape.fillColor = UIColor(red: 1.0, green: 0.2, blue: 0.2, alpha: 0.2).cgColor
if let firstCoord = self.coordArray.first {
path.move(to: firstCoord)
}
for (index, cgPoint) in self.coordArray.enumerated() {
//self.drawCircularPoint(points: cgPoint)
if index == 0 {
continue
}
path.addLine(to: cgPoint)
}
path.close()
shape.path = path.cgPath
}
}
}
//self.bringSubviewToFront(viewDrag)
}
private func updateCoordinateArray() -> Void {
for singleCoordinate in self.coordinatePoints {
if singleCoordinate.contains(",") == true {
let splitCoordinate = singleCoordinate.split(separator: ",")
if splitCoordinate.count == 2 {
let xPos = CGFloat(Float(splitCoordinate[0]) ?? 0.0)
let yPos = CGFloat(Float(splitCoordinate[1]) ?? 0.0)
let cgPoint = CGPoint(x: xPos, y: yPos)
self.coordArray.append(cgPoint)
}
}
}
var penultimateIndex: Int?
if let penultimateCoordinate = self.coordArray.penultimate() {
penultimateIndex = self.coordArray.firstIndex { (cgpoint) -> Bool in
if penultimateCoordinate == cgpoint {
return true
}
return false
}
}
var lastIndex: Int?
if let lastCoordinate = self.coordArray.last {
lastIndex = self.coordArray.firstIndex { (cgpoint) -> Bool in
if lastCoordinate == cgpoint {
return true
}
return false
}
}
if penultimateIndex != nil && lastIndex != nil {
self.coordArray.swapAt(penultimateIndex!, lastIndex!)
}
}
'''
I am creating a polygon using UIBezierpath and CAShapelayer. Added pan gesture on all 4 points that is UIView. When I drag the point A,B,C,D the expected behaviour is that bezierpath and CAShapelayer gets updated with the updated points. And when user drag the inner part of the shape all the path gets updated. But I am unable to update the path and shape. Can anyone help me with this?
yon set name for layer
var shape = CAShapeLayer()
shape.name = "name1"
and then, instead of updating, you can delete it by first searching by name, and then add

Detecting Touch on Bezier Path is Incorrect

I draw a segmented circle using UIBezier Path with this code:
func createCircleWithStartAndgle(startAngle: CGFloat, endAngle: CGFloat, chartSection: Int) {
var subView = circleView
var radius: CGFloat = circleView.bounds.size.width / 2 - 20
var circleShapeLayer = CAShapeLayer()
var arcCenter = CGPoint(x: radius, y: radius)
var bezierPath = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
circleShapeLayer.path = bezierPath.cgPath
circleShapeLayer.position = CGPoint(x:(subView?.bounds.midX)! - radius , y: (subView?.bounds.midY)! - radius)
circleShapeLayer.fillColor = UIColor.clear.cgColor
var strokeColor = UIColor()
switch chartSection {
case 0:
strokeColor = .red
firstLayer = circleShapeLayer
fPath = bezierPath
case 1:
strokeColor = .yellow
secondLayer = circleShapeLayer
sPath = bezierPath
case 2:
strokeColor = .blue
thirdLayer = circleShapeLayer
thPath = bezierPath
default:
strokeColor = .gray
}
circleShapeLayer.strokeColor = strokeColor.cgColor
circleShapeLayer.lineWidth = 10
subView?.layer.insertSublayer(circleShapeLayer, at: UInt32(chartSection))
}
Which gave me a satisfied result:
Now I want to be able to touch on each segment and identify the segment, and make it bigger.
When I use this code to detect touches, it works fine for red and yellow segments (first and second path), but in order to identify blue segment, I need to tap on left upper corner or a little bit up of the blue segment. If I tap on the segment itself, it detects it as it is a first segment, not third.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
guard let point = touch?.location(in: circleView) else { return }
guard let sublayers = circleView.layer.sublayers as? [CAShapeLayer] else { return }
for layer in sublayers {
if let path = layer.path {
if path == fPath.cgPath && path.contains(point) {
print("first segment")
} else if path == sPath.cgPath && path.contains(point) {
print("second segment")
} else if path == thPath.cgPath && path.contains(point) {
print("third segment")
}
}
}
}
What is wrong? How can I correctly detect taps and modify the strokWidth of tapped bezierPath?

Connect points in grid with lines

I need to put line that will connect every 2 points in the grid when someone tap between those points , so they can be connected . I manage to create the point grid :
func drawPointGrid() {
let points: CGFloat = 5
let cellWidth = bounds.width / points
let cellHeight = bounds.height / points
for i in 0..<Int(points) {
for j in 0..<Int(points) {
let circleX: CGFloat = ((CGFloat(i) + 0.5) * cellWidth)
let circleY: CGFloat = ((CGFloat(j) + 0.5) * cellHeight)
let centerCirclePath = UIBezierPath(ovalIn: CGRect(x: circleX, y: circleY, width: diameter, height: diameter))
let customlayer = CAShapeLayer()
customlayer.path = centerCirclePath.cgPath
customlayer.fillColor = UIColor.black.cgColor
layer.addSublayer(customlayer)
}
}
}
This is my visual point grid :
I manage to make line on the view , but only when I click for start point and click again for end point, so the line to be created , but I need this line to be created between every 2 points on horizontal and vertical when user tap between them :
override func draw(_ rect: CGRect) {
drawPointGrid()
tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showMoreActions))
tapGestureRecognizer.numberOfTapsRequired = 1
addGestureRecognizer(tapGestureRecognizer)
}
// draw line from point to point that are clicked
var firstPoint: CGPoint?
var secondPoint: CGPoint?
func showMoreActions(touch: UITapGestureRecognizer) {
let touchPoint = touch.location(in: self)
guard let _ = firstPoint else {
firstPoint = touchPoint
return
}
guard let _ = secondPoint else {
secondPoint = touchPoint
addLine(start: firstPoint!,end: secondPoint!)
firstPoint = nil
secondPoint = nil
return
}
}
func addLine(start: CGPoint,end:CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.path = linePath.cgPath
line.strokeColor = UIColor.black.cgColor
line.lineWidth = 2
line.lineJoin = kCALineJoinRound
layer.addSublayer(line)
}
I've written a function called findEndPoints that finds the CGPoint coordinates of the proper line end points given a touchPoint in the view. I did most of the work as type Double to keep from having to worry about converting between that and CGFloat.
I also moved the setup of the touchGestureRecognizer to a setup routine that is called from the inits since you only want to do that once and draw could be called more than once.
class DotsView: UIView {
let diameter = CGFloat(5)
func setup() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showMoreActions))
tapGestureRecognizer.numberOfTapsRequired = 1
addGestureRecognizer(tapGestureRecognizer)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func draw(_ rect: CGRect) {
drawPointGrid()
}
// draw line between points
func showMoreActions(touch: UITapGestureRecognizer) {
let touchPoint = touch.location(in: self)
let (start, end) = findEndPoints(touchPt: touchPoint)
addLine(start: start, end: end)
}
func addLine(start: CGPoint,end:CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.path = linePath.cgPath
line.strokeColor = UIColor.black.cgColor
line.lineWidth = 2
line.lineJoin = kCALineJoinRound
layer.addSublayer(line)
}
func drawPointGrid() {
let points: CGFloat = 5
let diameter: CGFloat = 5
let cellWidth = bounds.width / points
let cellHeight = bounds.height / points
for i in 0..<Int(points) {
for j in 0..<Int(points) {
let circleX: CGFloat = ((CGFloat(i) + 0.5) * cellWidth)
let circleY: CGFloat = ((CGFloat(j) + 0.5) * cellHeight)
let centerCirclePath = UIBezierPath(ovalIn: CGRect(x: circleX, y: circleY, width: diameter, height: diameter))
let customlayer = CAShapeLayer()
customlayer.path = centerCirclePath.cgPath
customlayer.fillColor = UIColor.black.cgColor
layer.addSublayer(customlayer)
}
}
}
func findEndPoints(touchPt: CGPoint) -> (pt1: CGPoint, pt2: CGPoint) {
let points = 5.0
let cellWidth = Double(bounds.width) / points
let cellHeight = Double(bounds.height) / points
// convert touch point to grid coordinates
let gridX = Double(touchPt.x) / cellWidth - 0.5
let gridY = Double(touchPt.y) / cellHeight - 0.5
// snap to nearest point in the grid
let snapX = round(gridX)
let snapY = round(gridY)
// find distance from touch to snap point
let distX = abs(gridX - snapX)
let distY = abs(gridY - snapY)
// start second point on top of first
var secondX = snapX
var secondY = snapY
if distX < distY {
// this is a vertical line
if secondY > gridY {
secondY -= 1
} else {
secondY += 1
}
} else {
// this is a horizontal line
if secondX > gridX {
secondX -= 1
} else {
secondX += 1
}
}
let halfdot = Double(diameter) / 2
// convert line points to view coordinates
let pt1 = CGPoint(x: (snapX + 0.5) * cellWidth + halfdot, y: (snapY + 0.5) * cellHeight + halfdot)
let pt2 = CGPoint(x: (secondX + 0.5) * cellWidth + halfdot, y: (secondY + 0.5) * cellHeight + halfdot)
return (pt1, pt2)
}
}