Home > database >  Set lineWidth of UIBezierPath for use in SceneKit
Set lineWidth of UIBezierPath for use in SceneKit

Time:01-18

I can't get UIBezierPath's lineWidth property to work when using it in SceneKit. The end product has the minimum lineWidth (it's very thin), whereas I need a thick line.

The path is used to construct an SCNShape, which is then used to construct an SCNNode.

Consider the following code:

let hugePath = UIBezierPath()
        
hugePath.lineWidth = 40.0 //Has no effect

hugePath.move(to: CGPoint(x: previousPathPosition.x, y: previousPathPosition.y))

hugePath.addLine(to: CGPoint(x: block.position.x, y: block.position.y))

let hugeShape = SCNShape(path: hugePath, extrusionDepth: 150.0)
let hugeMaterial = SCNMaterial()
hugeMaterial.diffuse.contents = UIColor.red
hugeShape.materials = [hugeMaterial, hugeMaterial, hugeMaterial, hugeMaterial, hugeMaterial, hugeMaterial]

let hugeNode = SCNNode(geometry: hugeShape)
hugeNode.position.x = 0.0 
hugeNode.position.z = 5.0
hugeNode.position.y = 0.0

scnView.scene?.rootNode.addChildNode(hugeNode)

There are numerous SO questions on how this problem pertains to UIBezierPath and CAShapeLayer, but none that I see on how it pertains to SceneKit. With the CAShapeLayer problem, the enter image description here

CodePudding user response:

From Apple's docs: "SceneKit uses a right-handed coordinate system where (by default) the direction of view is along the negative z-axis..."

enter image description here

Path geometry starts on the XY plane, and is extruded on the Z-axis.

So, if we start with a (vertical) "line" path and extrude it:

    let path = UIBezierPath()
    path.move(to: .zero)
    path.addLine(to: .init(x: 0.0, y: 1.0))
    
    // extrude it to create the shape
    let shape = SCNShape(path: path, extrusionDepth: 10.0)

We get this:

enter image description here

It has Y and Z dimensions, but no X (width).

So, instead of a line, let's start with a rectangle - 0.1 width and 1.0 height:

    // rectangle bezier path
    let path = UIBezierPath(rect: CGRect(x: 0.0, y: 0.0, width: 0.10, height: 1.0))

enter image description here

We see that the path is on the XY plane... if we extrude it:

    // rectangle bezier path
    let path = UIBezierPath(rect: CGRect(x: 0.0, y: 0.0, width: 0.10, height: 1.0))

    // extrude it to create the shape
    let shape = SCNShape(path: path, extrusionDepth: 10.0)

We get this:

enter image description here

Quick example code:

class WallViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let sceneView = SCNView(frame: self.view.frame)
        self.view.addSubview(sceneView)
        
        sceneView.allowsCameraControl = true
        sceneView.autoenablesDefaultLighting = true
        sceneView.backgroundColor = .black
        
        let scene = SCNScene()
        sceneView.scene = scene
        
        // rectangle bezier path
        let path = UIBezierPath(rect: CGRect(x: 0.0, y: 0.0, width: 0.10, height: 1.0))
        
        // extrude it to create the shape
        let shape = SCNShape(path: path, extrusionDepth: 10.0)
        
        let mat = SCNMaterial()
        mat.diffuse.contents = UIColor(white: 0.75, alpha: 1.0)
        mat.lightingModel = .physicallyBased
        shape.materials = [mat]
        
        // set shape node
        let shapeNode = SCNNode(geometry: shape)
        
        // add it to the scene
        scene.rootNode.addChildNode(shapeNode)
        
        // let's add a camera for the "starting view"
        let camera = SCNCamera()
        let cameraNode = SCNNode()
        cameraNode.camera = camera
        cameraNode.position = SCNVector3(x: 0.5, y: 2.0, z: 7.0)
        scene.rootNode.addChildNode(cameraNode)
        
        let constraint = SCNLookAtConstraint(target: shapeNode)
        constraint.isGimbalLockEnabled = true
        cameraNode.constraints = [constraint]
        
    }
    
}
  • Related