Swift:如何在封闭的CGPath中添加点? [英] Swift: How to add points to a closed CGPath?
问题描述
我想使SKSpriteNodes沿字母轮廓移动.我有很多字母,但这是一个例子:
我想让精灵跟随红线.我找到的答案基本上涵盖了我的问题:
我试图像这样向 path
添加一些点:
CGPathAddLineToPoint(path,nil,0,0)
,但结果可能不起作用,因为添加的点位于< Close>
语句之后:
< UIBezierPath:0x7889ff70;< MoveTo {25.950001,55.800003}> ;,< LineTo {25.950001,95.100006}> ;,< LineTo {53.850002,95.100006}> ;,< QuadCurveTo {71.625,90.075005}-{66,95.100006}> ;,< QuadCurveTo {77.25,75.450005}-{77.25,85.050003}> ;,< QuadCurveTo {71.625,60.750004}-{77.25,65.850006}> ;,< QuadCurveTo {53.850002,55.800003}-{66,55.650002}> ;,<关闭> ;,< MoveTo {11.700001,107.10001}> ;,< LineTo {11.700001,0}> ;,< LineTo {25.950001,0}> ;,< LineTo {25.950001,43.800003}> ;,< LineTo {58.650002,43.800003}> ;,< QuadCurveTo {83.175003,52.050003}-{74.850006,43.650002}> ;,< QuadCurveTo {91.5,75.450005}-{91.5,60.450001}> ;,< QuadCurveTo {83.175003,98.775002}-{91.5,90.450005}> ;,< QuadCurveTo {58.650002,107.10001}-{74.850006,107.10001}> ;,<关闭> ;,< LineTo {0,0}>
首先,您需要一种方法来从 CGPath
中检索所有元素.
我已将此方法转换为 Swift (您还可以找到一个示例,了解如何使用它.)
正如我在您的代码中看到的那样,您还需要知道哪种类型的元素构成了您的路径,但是 CGPathElement
与 UnsafeMutablePointer< CGPoint>
一起使用(它可以不太舒服),因此您可以通过创建具有两个输出数组的函数来修改我的翻译,如下所示:
//MARK:-CGPath扩展扩展CGPath {func getPathElementsPointsAndTypes()->([CGPoint],[CGPathElementType]){var arrayPoints:[CGPoint]!= [CGPoint]()var arrayTypes:[CGPathElementType]!= [CGPathElementType]()self.forEach {开关(element.type){案例CGPathElementType.MoveToPoint:arrayPoints.append(element.points [0])arrayTypes.append(element.type)案例.AddLineToPoint:arrayPoints.append(element.points [0])arrayTypes.append(element.type)案例.AddQuadCurveToPoint:arrayPoints.append(element.points [0])arrayPoints.append(element.points [1])arrayTypes.append(element.type)arrayTypes.append(element.type)案例.AddCurveToPoint:arrayPoints.append(element.points [0])arrayPoints.append(element.points [1])arrayPoints.append(element.points [2])arrayTypes.append(element.type)arrayTypes.append(element.type)arrayTypes.append(element.type)默认值:break}}返回(arrayPoints,arrayTypes)}}
之后,您将获得一个点数组和一个镜面反射类型数组,这些数组将解释列表中每个点的类型.这次所有的 closePath
都被删除了.
现在该为您重新创建路径临时HOC了:
func createNewPath(path:CGPath)->UIBezierPath {让(点,类型)= path.getPathElementsPointsAndTypes()如果points.count< = 1 {return UIBezierPath()//退出}让pathRef = UIBezierPath()变量i = 0而我<points.count {开关([i]型){案例CGPathElementType.MoveToPoint:pathRef.moveToPoint(CGPointMake(points [i] .x,points [i] .y))案例.AddLineToPoint:pathRef.addLineToPoint(CGPointMake(points [i] .x,points [i] .y))案例.AddQuadCurveToPoint:pathRef.addQuadCurveToPoint(CGPointMake(points [i] .x,points [i] .y),controlPoint:CGPointMake(points [i + 1] .x,points [i + 1] .y))我+ = 1案例.AddCurveToPoint:pathRef.addCurveToPoint(CGPointMake(points [i] .x,points [i] .y),controlPoint1:CGPointMake(points [i + 1] .x,points [i + 1] .y),controlPoint2:CGPointMake(points [pointsi + 2] .x,点[i + 2] .y))我+ = 2默认值:break}我+ = 1}//pathRef.closePath()如果要添加新元素,请不要取消注释返回pathRef}
启动此方法后,将使用所有 closePath
清除路径,并准备根据需要添加新行.
I'd like to make SKSpriteNodes to move along letter outlines. I have many letters but here's one example:
I would like the sprite to follow the red line. I found this answer that mostly covers my problem: Get path to trace out a character in an iOS UIFont
The answer comes with this good and working example code:
let font = UIFont(name: "HelveticaNeue", size: 64)!
var unichars = [UniChar]("P".utf16)
var glyphs = [CGGlyph](count: unichars.count, repeatedValue: 0)
let gotGlyphs = CTFontGetGlyphsForCharacters(font, &unichars, &glyphs, unichars.count)
if gotGlyphs {
let cgpath = CTFontCreatePathForGlyph(font, glyphs[0], nil)!
let path = UIBezierPath(CGPath: cgpath)
print(path)
XCPlaygroundPage.currentPage.captureValue(path, withIdentifier: "glyph \(glyphs[0])")
}
However I still run into a problem as my sprite doesn't complete full path around the letter for all the letters but instead e.g. with "P" stops here (and started from the bottom):
I tried adding some points to the path
like so:
CGPathAddLineToPoint(path, nil, 0, 0)
but the result doesn't work probably because the added point is after the <Close>
statement:
<UIBezierPath: 0x7889ff70; <MoveTo {25.950001, 55.800003}>,
<LineTo {25.950001, 95.100006}>,
<LineTo {53.850002, 95.100006}>,
<QuadCurveTo {71.625, 90.075005} - {66, 95.100006}>,
<QuadCurveTo {77.25, 75.450005} - {77.25, 85.050003}>,
<QuadCurveTo {71.625, 60.750004} - {77.25, 65.850006}>,
<QuadCurveTo {53.850002, 55.800003} - {66, 55.650002}>,
<Close>,
<MoveTo {11.700001, 107.10001}>,
<LineTo {11.700001, 0}>,
<LineTo {25.950001, 0}>,
<LineTo {25.950001, 43.800003}>,
<LineTo {58.650002, 43.800003}>,
<QuadCurveTo {83.175003, 52.050003} - {74.850006, 43.650002}>,
<QuadCurveTo {91.5, 75.450005} - {91.5, 60.450001}>,
<QuadCurveTo {83.175003, 98.775002} - {91.5, 90.450005}>,
<QuadCurveTo {58.650002, 107.10001} - {74.850006, 107.10001}>,
<Close>,
<LineTo {0, 0}>
First of all you need a method to retrieve all elements from CGPath
.
I've translated to Swift this method (you find also an example to how to use it).
As I can see here in your code, you need also to know what kinds of element compose your path, but CGPathElement
work with UnsafeMutablePointer<CGPoint>
(it can be not confortable), so you can modify my translation by creating a function with two output arrays like this:
//MARK: - CGPath extensions
extension CGPath {
func getPathElementsPointsAndTypes() -> ([CGPoint],[CGPathElementType]) {
var arrayPoints : [CGPoint]! = [CGPoint]()
var arrayTypes : [CGPathElementType]! = [CGPathElementType]()
self.forEach { element in
switch (element.type) {
case CGPathElementType.MoveToPoint:
arrayPoints.append(element.points[0])
arrayTypes.append(element.type)
case .AddLineToPoint:
arrayPoints.append(element.points[0])
arrayTypes.append(element.type)
case .AddQuadCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
arrayTypes.append(element.type)
arrayTypes.append(element.type)
case .AddCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
arrayPoints.append(element.points[2])
arrayTypes.append(element.type)
arrayTypes.append(element.type)
arrayTypes.append(element.type)
default: break
}
}
return (arrayPoints,arrayTypes)
}
}
After that you have a array of points and a specular array of type that explain what type is each point in our list. This time all closePath
are removed.
Now it's time to re-create path ad HOC for you situation:
func createNewPath(path:CGPath) -> UIBezierPath {
let (points,types) = path.getPathElementsPointsAndTypes()
if points.count <= 1 {
return UIBezierPath() // exit
}
let pathRef = UIBezierPath()
var i = 0
while i < points.count {
switch (types[i]) {
case CGPathElementType.MoveToPoint:
pathRef.moveToPoint(CGPointMake(points[i].x,points[i].y))
case .AddLineToPoint:
pathRef.addLineToPoint(CGPointMake(points[i].x,points[i].y))
case .AddQuadCurveToPoint:
pathRef.addQuadCurveToPoint(CGPointMake(points[i].x,points[i].y), controlPoint: CGPointMake(points[i+1].x,points[i+1].y))
i += 1
case .AddCurveToPoint:
pathRef.addCurveToPoint(CGPointMake(points[i].x,points[i].y), controlPoint1: CGPointMake(points[i+1].x,points[i+1].y), controlPoint2: CGPointMake(points[i+2].x,points[i+2].y))
i += 2
default: break
}
i += 1
}
//pathRef.closePath() if you want to add new elements dont uncomment this
return pathRef
}
After launching this method, you will have your path cleaned by all closePath
and ready to add new lines as you wish.
这篇关于Swift:如何在封闭的CGPath中添加点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!