Using iOS - Swift Xcode 14 I have drawn an image using some points... and I got an image as output. But the image is an inverted or a flipped image. I used several code for flipping/Inverting the image but none of the code was working. by using of some codes the result image going out of the screen. The code provided here is 90% correct code but the image is flipped/inverted.
import SceneKit
class StairTwoDSceneViewController: UIViewController {
var plotPointsArray: [[Double]] = [[0.0,0.0,0.0], [0.229,0.0,0.005], [0.229,-0.0249,0.1926], [0.4566,-0.0249,0.2073], [0.4341,-0.0315,0.3989],[0.6754,-0.0315,0.4039],[0.6513,-0.0315,0.5892], [0.8782,-0.0315,0.5971], [0.8541,-0.0315,0.7834],[1.0765,-0.0315,0.78781]]
let lineWidth: CGFloat = 0.1 // Adjust this value to increase or decrease line thickness
let cornerRadius: CGFloat = 0.02 // Adjust this value to control the size of the corner circles
override func viewDidLoad() {
super.viewDidLoad()
let sceneView = SCNView(frame: view.bounds)
sceneView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(sceneView)
let scene = SCNScene()
if plotPointsArray.count > 0 {
let pointsNode = SCNNode()
for (index, point) in plotPointsArray.enumerated() {
guard point.count == 2 || point.count == 3 else {
continue // Skip invalid points
}
// Adjust the y-coordinate to place the green sphere at the left bottom
let spherePosition = SCNVector3(
x: Float(point[0]),
y: Float(point.count == 2 ? -0.0315 : -point[1]), // Flip and adjust y-coordinate
z: Float(point[point.count - 1])
)
let sphereNode = drawSphere(at: spherePosition, radius: cornerRadius, color: index == 0 ? UIColor.green : UIColor.red)
pointsNode.addChildNode(sphereNode)
}
scene.rootNode.addChildNode(pointsNode)
for i in 0..<plotPointsArray.count - 1 {
let currentPoint = plotPointsArray[i]
let nextPoint = plotPointsArray[i + 1]
guard currentPoint.count == nextPoint.count else {
continue // Skip invalid points
}
let start = SCNVector3(currentPoint[0], currentPoint.count == 2 ? -0.0315 : -currentPoint[1], currentPoint[currentPoint.count - 1])
let end = SCNVector3(nextPoint[0], nextPoint.count == 2 ? -0.0315 : -nextPoint[1], nextPoint[nextPoint.count - 1])
let lineGeometry = SCNGeometry.lineGeometry(from: start, to: end)
let lineNode = SCNNode(geometry: lineGeometry)
scene.rootNode.addChildNode(lineNode)
}
}
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.eulerAngles.x = -Float.pi / 2
cameraNode.eulerAngles.y = 0
cameraNode.position = SCNVector3(0.5, 2.5, 0.5)
scene.rootNode.addChildNode(cameraNode)
sceneView.scene = scene
sceneView.backgroundColor = UIColor.black
sceneView.allowsCameraControl = true
}
func drawLine(from start: SCNVector3, to end: SCNVector3, thickness: CGFloat, color: UIColor) -> SCNNode {
let lineGeometry = SCNGeometry.lineGeometry(from: start, to: end)
lineGeometry.firstMaterial?.diffuse.contents = color
let lineNode = SCNNode(geometry: lineGeometry)
lineNode.position = SCNVector3((start.x + end.x) / 2, (start.y + end.y) / 2, (start.z + end.z) / 2)
lineNode.eulerAngles.z = -atan2(end.x - start.x, end.y - start.y)
return lineNode
}
func drawSphere(at position: SCNVector3, radius: CGFloat, color: UIColor) -> SCNNode {
let sphereGeometry = SCNSphere(radius: radius)
let sphereNode = SCNNode(geometry: sphereGeometry)
sphereNode.position = position
sphereNode.geometry?.firstMaterial?.diffuse.contents = color
return sphereNode
}
}
extension SCNGeometry {
static func lineGeometry(from start: SCNVector3, to end: SCNVector3) -> SCNGeometry {
let indices: [Int32] = [0, 1]
let source = SCNGeometrySource(vertices: [start, end])
let element = SCNGeometryElement(indices: indices, primitiveType: .line)
return SCNGeometry(sources: [source], elements: [element])
}
}
extension UIImage {
func rotate(degrees: CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.rotate(by: degrees * CGFloat.pi / 180)
self.draw(in: CGRect(origin: .zero, size: self.size))
let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return rotatedImage
}
}
Actually I want the Inverted-image like this below
Note : Dont change the points, the points getting from backend. I have used sample points here
I need to flip/Invert the image.
With the "stair steps" you have generated, the camera is looking from the Y-axis ... the "stairs" are descending on the Z-axis.
Depending on what you want to do, you can either flip the camera:
cameraNode.eulerAngles.x = -Float.pi / 2
//cameraNode.eulerAngles.y = 0
cameraNode.eulerAngles.y = -Float.pi / 2
Or, translate the Z coordinates to invert the stairs:
if plotPointsArray.count > 0 {
let pointsNode = SCNNode()
// only use elements that have x,y,z values
plotPointsArray.removeAll(where: {$0.count != 3})
// will never fail (because we're only here if .count > 0)
// but safely unwrap anyway
guard let lastPoint = plotPointsArray.last
else { return }
// assuming for now the z-values are from 0.0 to a positive value
let maxZ = lastPoint[2]
for (index, point) in plotPointsArray.enumerated() {
// Adjust the Z-coordinate to place the green sphere at the left bottom
let spherePosition = SCNVector3(
x: Float(point[0]),
y: Float(point[1]),
z: Float(maxZ - point[2])
)
let sphereNode = drawSphere(at: spherePosition, radius: cornerRadius, color: index == 0 ? UIColor.green : UIColor.red)
pointsNode.addChildNode(sphereNode)
}
scene.rootNode.addChildNode(pointsNode)
// let's use those sphere nodes to generate the
// "connecting lines" instead of re-converting the plotPointsArray
var startVect: SCNVector3 = pointsNode.childNodes[0].position
for (i, n) in pointsNode.childNodes.enumerated() {
// skip the first time through
// since we have only the starting point
guard i != 0 else {
continue
}
let nextVect = n.position
let lineGeometry = SCNGeometry.lineGeometry(from: startVect, to: nextVect)
let lineNode = SCNNode(geometry: lineGeometry)
scene.rootNode.addChildNode(lineNode)
startVect = nextVect
}
}
Alternatively, if we add the "spheres" and the "connector lines" to pointsNode
, we could "invert" the entire node by scaling it and then moving it down (so the last sphere will be at Zero on the Z-axis):
if plotPointsArray.count > 0 {
let pointsNode = SCNNode()
// only use elements that have x,y,z values
plotPointsArray.removeAll(where: {$0.count != 3})
// will never fail (because we're only here if .count > 0)
// but safely unwrap anyway
guard let lastPoint = plotPointsArray.last
else { return }
// assuming for now the z-values are from 0.0 to a positive value
let maxZ = lastPoint[2]
var startVect: SCNVector3!
for (index, point) in plotPointsArray.enumerated() {
// Adjust the Z-coordinate to place the green sphere at the left bottom
let spherePosition = SCNVector3(
x: Float(point[0]),
y: Float(point[1]),
z: Float(point[2])
)
let sphereNode = drawSphere(at: spherePosition, radius: cornerRadius, color: index == 0 ? UIColor.green : UIColor.red)
pointsNode.addChildNode(sphereNode)
// add the "sphere connector lines" so they are
// part of the points node and will rotate/move along with it
let nextVect = spherePosition
if let sv = startVect {
let lineGeometry = SCNGeometry.lineGeometry(from: startVect, to: nextVect)
let lineNode = SCNNode(geometry: lineGeometry)
pointsNode.addChildNode(lineNode)
}
startVect = nextVect
}
let (minVec, maxVec) = pointsNode.boundingBox
// scale on the z-axis
// and move by the height of pointsNode minus the sphere radius
// so the LAST point (the center of the LAST sphere) will be at z: 0.0
var m = SCNMatrix4MakeScale(1.0, 1.0, -1.0)
m = SCNMatrix4Translate(m, 0.0, 0.0, maxVec.z - Float(cornerRadius))
pointsNode.pivot = m
scene.rootNode.addChildNode(pointsNode)
}
with either approach, this is the result (I added axis lines for clarity - x-axis: red, y-axis: green, z-axis: blue):