Skip to content

Instantly share code, notes, and snippets.

@takoikatakotako
Last active September 19, 2020 02:56
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 takoikatakotako/1d0234fecb2a0934b1955a4ded308287 to your computer and use it in GitHub Desktop.
Save takoikatakotako/1d0234fecb2a0934b1955a4ded308287 to your computer and use it in GitHub Desktop.
iOSDC-2020 ベジェ曲線
import PlaygroundSupport
import XCPlayground
import UIKit
struct Point {
let x: Double
let y: Double
var cgPoint: CGPoint {
CGPoint(x: Double(self.x), y: Double(self.y))
}
func scaled(_ scaled: Double) -> Point {
return Point(x: x * scaled, y: y * scaled)
}
}
struct Stroke {
var points: [Point] = []
var cgPoints: [CGPoint] {
return points.map {$0.cgPoint}
}
}
var strokes: [Stroke] = []
// か
let scale = 3.0
var stroke0 = Stroke()
stroke0.points.append(Point(x: 21.5 , y: 106.5))
stroke0.points.append(Point(x: 63.5, y: 85.0))
stroke0.points.append(Point(x: 93.0, y: 81.0))
stroke0.points.append(Point(x: 148.5, y: 98.0))
stroke0.points.append(Point(x: 159.5, y: 117.5))
stroke0.points.append(Point(x: 160.0, y: 135.5))
stroke0.points.append(Point(x: 148.5, y: 167.5))
stroke0.points.append(Point(x: 120.0, y: 196.5))
stroke0.points.append(Point(x: 110.0, y: 189.5))
stroke0.points.append(Point(x: 102.5, y: 174.0))
var stroke1 = Stroke()
stroke1.points.append(Point(x: 107.0, y: 45.5))
stroke1.points.append(Point(x: 104.5, y: 80.5))
stroke1.points.append(Point(x: 92.0, y: 118.5))
stroke1.points.append(Point(x: 58.0, y: 166.0))
var stroke2 = Stroke()
stroke2.points.append(Point(x: 196.0, y: 77.0))
stroke2.points.append(Point(x: 202.5, y: 88.5))
stroke2.points.append(Point(x: 208.5, y: 98.0))
strokes.append(stroke0)
strokes.append(stroke1)
strokes.append(stroke2)
class CurveAlgorithm {
struct Segment: CustomStringConvertible {
var p0: CGPoint = .zero
var p1: CGPoint = .zero
var p2: CGPoint = .zero
var p3: CGPoint = .zero
var description: String {
"p0: \(p0), p1: \(p1), P2: \(p2), P3: \(p3)"
}
}
static func getSegments(points: [CGPoint]) -> [Segment] {
// points の要素数 - 1 がセグメントの個数
let count = points.count - 1
var segments: [Segment] = [Segment](repeating: Segment(), count: count)
// Thomasのアルゴリズムを使ってP1を計算する
// a, b, c, d, e, f を求める
var a: [CGFloat] = []
var b: [CGFloat] = []
var c: [CGFloat] = []
var dX: [CGFloat] = []
var dY: [CGFloat] = []
var e: [CGFloat] = []
var fX: [CGFloat] = []
var fY: [CGFloat] = []
for i in 0..<count {
if i == 0 {
segments[0].p0 = points[0]
segments[0].p3 = points[1]
a.append(0)
b.append(2)
c.append(1)
dX.append(points[0].x + 2 * points[1].x)
dY.append(points[0].y + 2 * points[1].y)
e.append(c[0] / b[0])
fX.append(dX[0] / b[0])
fY.append(dY[0] / b[0])
} else if i == count - 1 {
segments[count - 1].p0 = points[count - 1]
segments[count - 1].p3 = points[count]
a.append(2)
b.append(7)
c.append(0)
dX.append(8 * points[count - 1].x + points[count].x)
dY.append(8 * points[count - 1].y + points[count].y)
} else {
segments[i].p0 = points[i]
segments[i].p3 = points[i + 1]
a.append(1)
b.append(4)
c.append(1)
dX.append(4 * points[i].x + 2 * points[i + 1].x)
dY.append(4 * points[i].y + 2 * points[i + 1].y)
e.append(c[i] / (b[i] - a[i] * e[i - 1]))
fX.append((dX[i] - a[i] * fX[i - 1]) / (b[i] - a[i] * e[i - 1]))
fY.append((dY[i] - a[i] * fY[i - 1]) / (b[i] - a[i] * e[i - 1]))
}
}
// 最後のP1を求める
let x = (dX[count - 1] - a[count - 1] * fX[count - 2]) / (b[count - 1] - a[count - 1] * e[count - 2])
let y = (dY[count - 1] - a[count - 1] * fY[count - 2]) / (b[count - 1] - a[count - 1] * e[count - 2])
segments[count - 1].p1 = CGPoint(x: x, y: y)
// 残りのP1を全て計算する
for i in (1..<count).reversed() {
let x = fX[i - 1] - e[i - 1] * segments[i].p1.x
let y = fY[i - 1] - e[i - 1] * segments[i].p1.y
segments[i - 1].p1 = CGPoint(x: x, y: y)
}
// P2を求める
for i in (0..<count) {
if i == count - 1 {
let x = 0.5 * (points[count].x + segments[i].p1.x)
let y = 0.5 * (points[count].y + segments[i].p1.y)
segments[i].p2 = CGPoint(x: x, y: y)
} else {
let x = 2 * points[i + 1].x - segments[i + 1].p1.x
let y = 2 * points[i + 1].y - segments[i + 1].p1.y
segments[i].p2 = CGPoint(x: x, y: y)
}
}
return segments
}
}
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 600, height: 600)
view.backgroundColor = .black
for stroke in strokes {
let linePath = UIBezierPath()
let segments = CurveAlgorithm.getSegments(points: stroke.points.map{$0.scaled(2.5).cgPoint})
for segment in segments {
linePath.move(to: segment.p0)
print("current: \(linePath.currentPoint), to: \(segment.p3), control1: \(segment.p1), control2: \(segment.p2), ")
linePath.addCurve(to: segment.p3, controlPoint1: segment.p1, controlPoint2: segment.p2)
}
let shapeLayer = CAShapeLayer()
shapeLayer.path = linePath.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 16
shapeLayer.strokeColor = UIColor.white.cgColor
view.layer.addSublayer(shapeLayer)
}
PlaygroundPage.current.liveView = view
@takoikatakotako
Copy link
Author

スクリーンショット 2020-09-19 11 17 23

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment