SCNShape没有为NSBezierPath绘制形状

时间:2016-12-07 13:06:11

标签: ios swift macos scenekit

我觉得有些NSBezierPath SCNShape line(to:)似乎无法画出形状。

仅使用//...set up scene... //Create path (working) let path = NSBezierPath() path.move(to: CGPoint.zero) path.line(to: NSMakePoint(0.000000, 0.000000)) path.line(to: NSMakePoint(0.011681, 0.029526)) // more points ... path.close() // Make a 3D shape (not working) let shape = SCNShape(path: path, extrusionDepth: 10) shape.firstMaterial?.diffuse.contents = NSColor.green let node = SCNNode(geometry: shape) root.addChildNode(node) 创建路径。

SCNShape

为了验证创建INSERT INTO (columns) VALUES (values) 的一般过程是否正确,我还画了一个蓝色的形状,只有不同点才有所不同。蓝色的形状被绘制出来,绿色的形状没有。

您可以找到包含完整示例here的游乐场。在示例中,您应该能够在助理编辑器中看到绿色和蓝色形状。但只画出蓝色的形状。

你知道为什么没有显示绿色形状吗?

2 个答案:

答案 0 :(得分:6)

简短的故事:你的路径有更多的分数,导致你意想不到的,很难找到几何问题。

请注意documentation中的这一位:

  

挤出自相交路径的结果未定义。

事实证明,在前8个左右的某个点,你的“曲线”使得转弯的方式与关闭路径的线(在路径0,0中的第一个点之间)和最后一点32.366829, 29.713470)与路径的其余部分相交。这是尝试通过从游乐场渲染中排除除了前几个点和最后一个点之外的所有点来看到它(见左下角那个小小的锯齿形):

move zig for great justice

至少在一些SceneKit版本/渲染器上,当它试图从自相交路径中制作一个网格时,它只是放弃并且什么都不做。

然而,你真的不需要那么多点来让你的路径看起来很好。如果您使用1x,1 / 5x和1 / 10x的点数,那就是这样:

original path path w/ every 5th point from the original path w/ every 10th point from the original

如果你总体上排除了足够的点数,和/或在开头跳过几个使你的曲线zag应该zig的地方,SceneKit渲染形状就好了:

first few points excluded, 1/5 as many points total

诊断问题的一些提示:

当处理大量像这样的坐标数据时,我喜欢使用ExpressibleByArrayLiteral,所以我可以轻松地构建一个包含很多点/向量/等的数组:

extension CGPoint: ExpressibleByArrayLiteral {
    public init(arrayLiteral elements: CGFloat...) {
        precondition(elements.count == 2)
        self.init(x: elements.first!, y: elements.last!)
    }
}

var points: [CGPoint] = [
    [0.000000, 0.000000],
    [0.011681, 0.029526],
    // ...
]

这让我得到了一个数组(而不是一遍又一遍地输入像NSPointMake这样的东西),所以我可以对数据进行切片和切块以找出它的错误。 (例如,我早期的一个理论是可能存在一些关于负坐标的东西,所以我做了一些mapmin()以找到最负的X和Y值,然后更多map 1}}创建一个数组,其中所有点都以恒定量偏移。)

现在,为了使用点阵列创建路径,我在NSBezierPath上进行了扩展:

extension NSBezierPath {
    convenience init(linesBetween points: [CGPoint], stride: Int = 1)  {
        precondition(points.count > 1)
        self.init()
        move(to: points.first! )
        for i in Swift.stride(from: 1, to: points.count, by: stride) {
            line(to: points[i])
        }
    }
}

有了这个,我可以轻松地从不仅仅是整个点阵列创建路径,而且......

  • 跳过部分原始数组的路径(使用stride参数)

    let path5 = NSBezierPath(linesBetween: points, stride: 5)
    let path10 = NSBezierPath(linesBetween: points, stride: 10)
    

    (这对于更快地生成游乐场预览也很方便。)

  • 使用原始数组的一些块或切片的路径

    let zigzag = NSBezierPath(linesBetween: Array(points.prefix(to:10)) + [points.last!])
    let lopOffBothEnds = NSBezierPath(linesBetween: Array(points[1 ..< points.count < 1]))
    

或者两者......获胜的条目(在上面的屏幕截图中)是:

let path = NSBezierPath(linesBetween: Array(points.suffix(from: 10)), stride: 5)

你可以通过路径中的更多点获得(略微)更好的渲染,但更好的方法是从曲线而不是线条中创建路径。要获得额外的功劳,请尝试扩展上面的NSBezierPath(linesBetween:)初始化程序,通过将每个n点作为路径的一部分来添加曲线,同时使用一些中间点作为控制句柄。 (这不是通用的自动跟踪算法,但对于这样的情况可能已经足够了。)

答案 1 :(得分:2)

与Rikster的答案相比,这绝不是比较,但还有另一种方法可以防止出现这种问题。这是一种商业方式,并且可能有相似的免费应用程序,但这是我以前习惯使用的,这样做很好。

这是什么&#39;那我在说什么?

通过名为PaintCode的应用程序将图纸转换为代码。这将允许您查看路径并确保它们没有Rickster指出的冲突是您的问题。

请在此处查看:https://www.paintcodeapp.com/

答案中列出了其他选项:How to import/parse SVG into UIBezierpaths, NSBezierpaths, CGPaths?