UIBezierPath二次曲线是一条直线。

3

我正在尝试创建一条曲线箭头,在ARKit场景中显示,但是箭杆的弯曲在两侧都只呈现为一条直线。

func createTurnArrow(_ direction: Direction) -> SCNShape {
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0.2, y: 0)) // A
    path.addLine(to: CGPoint(x: 0, y: 0.2)) // B
    path.addLine(to: CGPoint(x: 0, y: 0.1)) // C
    path.addQuadCurve(to: CGPoint(x: -0.3, y: -0.3), controlPoint: CGPoint(x: -0.3, y: 0.1)) // Curve 1
    path.addLine(to: CGPoint(x: -0.1, y: -0.3)) // D
    path.addQuadCurve(to: CGPoint(x: 0, y: -0.1), controlPoint: CGPoint(x: -0.1, y: -0.1)) // Curve 2
    path.addLine(to: CGPoint(x: 0, y: -0.2)) // E
    path.close()

    return direction == .left ?
      SCNShape(path: path.reversing(), extrusionDepth: self.defaultDepth) :
      SCNShape(path: path, extrusionDepth: self.defaultDepth)
}

我的直觉告诉我,使用这个函数创建一个节点:
SCNNode(geometry: createTurnArrow(.right))

应该生成类似于这样的形状:

enter image description here

但是,它没有将箭头的尾部呈现为任何曲线:

enter image description here

我尝试了许多其他的数学方法来获取二次曲线的当前控制点,但都没有成功。有任何想法吗?
编辑:哪里有带有绘制点的原理图和我对如何使用曲线进行渲染的假设?

enter image description here


当我需要绘图时,如果它太复杂或者我无法看清问题,那么我会这样做。在你的图纸的每个点/角上放置一个字母,并标出它们的坐标。当有曲线时,尝试使用一个带有字母和其坐标的“控制点”。检查你的代码是否像这样。给我们这份示意图,如果你误解了坐标,错过了如何放置一个点(例如弧的中心点,控制点等),它可能会有所帮助。 - Larme
@Larme 我已经尝试画出来进行调试,最终得出了相同的结论。我编辑了帖子,包括了你要求的原理图。 - m_callens
点(0,0)在哪里,这很奇怪(因为它是一个SCNShape?) - Larme
@Larme 我最初将其向右偏移了0.2个偏移量,以使对象在ARKit视图中居中,当屏幕被点击时。 - m_callens
为什么控制点都是小于1的数量级的小数值?(我以前没有使用过ARKit。这是ARKit的特性吗?) - Duncan C
@DuncanC ARKit 中的所有内容都是以米为单位计量的,所以对象的大小在这里被考虑进去了。所有距离和尺寸都是相对的。 - m_callens
1个回答

6
阅读SCNShape path文档。它说:
路径的平坦度(请参阅NSBezierPath中的flatness)确定SceneKit在从路径构建三维形状时使用的详细级别 - 更大的平坦值会导致较少的多边形渲染,从而提高性能。
(由于您在iOS上,请将NSBezierPath替换为UIBezierPath。) UIBezierPath的默认flatness是多少?这里是文档所说:
平坦度值测量真实曲线上的点与渲染曲线上的点之间的最大允许距离(以像素为单位)。 较小的值会导致更平滑的曲线,但需要更多的计算时间。 较大的值会导致更加锯齿的曲线,但渲染速度更快。 默认平坦度值为0.6。
现在将默认平坦度(0.6)与您的形状的总体大小(0.5×0.5)进行比较。 注意,平坦度大于您的形状的大小! 因此,您的每个曲线都被压扁成一条直线。
将路径的平坦度更改为适合您形状的值,或将形状的比例更改为适合默认平坦度的值。
let path = UIBezierPath()
path.flatness = 0.05 // <----------------------- insert this statement
path.move(to: CGPoint(x: 0.2, y: 0)) // A
path.addLine(to: CGPoint(x: 0, y: 0.2)) // B
// etc.

哦,看起来 ARKit 和 Bezier 路径(NS 或 UI)存在“油水不相容”的问题,因为 ARKit 以米为单位解释单位,而 UI/NS bezier 路径则以点为单位。 - Duncan C
做一些简单的计算,如果你想让你的ARKit渲染器渲染到点大小,你应该使用大约0.0003的平坦度。当然,这会给你带来巨大的多边形数量... - Duncan C
核心图形实际上是以像素为单位考虑平坦度的。在我的测试中,似乎CG在使用图形上下文的CTM变换路径之后应用了“平坦度”。至于ARKit,由于相机到物体的距离不同,因此没有必要使用最佳平坦度。我认为你真正想做的是使用“SCNGeometry”的“levelsOfDetail”或“subdivisionLevel”根据距离变化几何形状。但我从未使用过SceneKit,所以我不确定。 - rob mayoff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接