Resize 3d cube base on screen size - swift

let's say I have a 3d cube of this size.
Height: 1002.0
Width: 564.0
The cube size is based on image size, here's how I'm creating the cube:
let image = UIImage(named: "img")
artRoomScene.setup(height: image!.size.height / 600, width: image!.size.width / 600, position: SCNVector3(0, 0.4, -1.5), rotation: SCNVector4(0,30,0,-56))
Obviously, the cube will be way too big for any iPhone screen if I don't divide it by 600.
But instead of dividing by 600. How can I resize the cube Proportionally to the iPhone screen size?

you can use the % you want, let's say you want a 20% of the screen width:
let desiredWidth = view.frame.width * 0.2
and setup the artRoomScene
image!.size.width / desiredWidth

Related

Display bounding boxes in Apple AR SceneKit

I am using YOLOv3 with Apple Vision to classify objects during my AR SceneKit session. I want to render the bounding boxes of the detected objects in my screen view. Unfortunately, the bounding boxes are are placed too far down and have a wrong aspect ratio. Does someone know what the issue might be?
This is how I am currently transforming the bounding boxes.
Assumptions:
The app is in portrait mode
Vision request is performed with centerCrop and orientation .right.
Fix the coordinate origin of vision:
let newY = 1 - boundingBox.origin.y
let newBox = CGRect(x: boundingBox.origin.x, y: newY, width: boundingBox.width, height: boundingBox.height)
Undo center cropping of Vision:
let imageResolution: CGSize = currentFrame.camera.imageResolution
// Switching height and width because the original image is rotated
let imageWidth = imageResolution.height
let imageHeight = imageResolution.width
// Square inside of normalized coordinates.
let roi = CGRect(x: 0, y: 1 - (imageWidth/imageHeight + ((imageHeight-imageWidth) / (imageHeight*2))), width: 1, height: imageWidth / imageHeight)
let newBox = VNImageRectForNormalizedRectUsingRegionOfInterest(boundingBox, Int(imageWidth), Int(imageHeight), roi)
Bring coordinates back to normalized form:
let imageWidth = imageResolution.height
let imageHeight = imageResolution.width
let transformNormalize = CGAffineTransform(scaleX: 1.0 / imageWidth, y: 1.0 / imageHeight)
let newBox = boundingBox.applying(transformNormalize)
Transform to scene view: (I assume the error is here. I found out while debugging that the aspect ratio of the bounding box changes here.)
let viewPort = sceneView.frame.size
let transformFormat = currentFrame.displayTransform(for: .landscapeRight, viewportSize: viewPort)
let newBox = boundingBox.applying(transformFormat)
Scale up to viewport size:
let viewPort = sceneView.frame.size
let transformScale = CGAffineTransform(scaleX: viewPort.width, y: viewPort.height)
let newBox = boundingBox.applying(transformScale)
Thanks in advance for any help!
Picture of incorrect bounding box on a bottle of water

Why does PKDrawing to pngData return a scaled image

I am trying to export a PKDrawing as image like so
drawing.image(from: CGRect(x: 0, y: 0, width: 1042, height: 21000), scale: 2).pngData()
but the returned image has a width of 401 for some reason (while it should be 2048 because of the 2x scale).
Why is this happening and how to I export the image without automatic scaling?

Vision framework barcode detection region of interest not working

I am trying to decode barcodes that appear on a region of interest, that is 80% of the screen width and 20% of the screen height and centered on both directions (blue rectangle).
The camera pixel buffer is rotated right.
This is what Apple has to say about this orientation:
The (x,y) pixel coordinates of the origin point (0,0) represent the
top row and rightmost column, respectively. Pixel (x,y) positions
increase top-to-bottom, right-to-left. If an image is encoded with
this orientation, then displayed by software unaware of orientation
metadata, the image appears to be rotated 90° counter-clockwise. (That
is, to present the image in its intended orientation, you must rotate
it 90° clockwise.)
So, when I define the region of interest of my VNDetectBarcodesRequest I do like this:
lazy var barcodeRequest: VNDetectBarcodesRequest = {
let barcodeRequest = VNDetectBarcodesRequest {[weak self] request, error in
guard error == nil else {
print ("ERROR")
return
}
self?.classification(request)
}
barcodeRequest.regionOfInterest = CGRect(x: 0.1,
y: 0.4,
width: 0.9,
height: 0.6)
If the bar code is inside the blue area and at any point above that, including anywhere on the area at the top of the blue area, it will detect. If the barcode is down the blue area, it will not detect anything.
Just making sure, if you look at regionOfInterest, the documentation says:
The rectangle is normalized to the dimensions of the processed image. Its origin is specified relative to the image's lower-left corner.
So the origin (0,0) is at the bottom left. With your current CGRect,
CGRect(x: 0.1,
y: 0.4,
width: 0.9,
height: 0.6)
you are getting the expected result - "If the bar code is inside the blue area and at any point above that, including anywhere on the area at the top of the blue area, it will detect."
All you need to do is change the height from 0.6 to 0.2. You will want:
barcodeRequest.regionOfInterest = CGRect(x: 0.1,
y: 0.4,
width: 0.9,
height: 0.2) /// your height is wrong
Just to chime in here for added clarity, cuz this was also tripping me up.
The documentation for regionOfInterest says:
The default value is { { 0, 0 }, { 1, 1 } }
Which I was also confusing for 2 points (a bottom left corner and a top right corner). But that last pair is supposed to be the normalized width and height; not a normalized coordinate.
// ❌
request.regionOfInterest = CGRect(x: 0.1, y: 0.4, width: 0.9, height: 0.6)
// ✔️
request.regionOfInterest = CGRect(x: 0.1, y: 0.4, width: 0.8, height: 0.2)

Is it possible to create an instance of a particular location on the screen (using auto layout)?

I have 4 playing cards on the screen. At the press of a button, I want one of the cards at random to move to the middle of the top half of the screen. Is it possible to create an instance of a constraint (eg: centerXAnchor with constant 0, and centerYAnchor with constant -200) so that I can use CGAffineTransform and move the random image to this point?
Ive tried creating an instance of a CGRect Frame:
let destination = CGPoint(x: 10, y: 10)
but this does not move evenly across devices.
An affine transformation matrix is used to rotate, scale, translate, or skew the objects you draw in a graphics context.
I don't think CGAffineTransform is the ideal thing to use for this task. You aren't doing any the above things (rotate, scale, translate, or skew).
I think you would likely be best using UIView.animateWithDuration
let cardSize = CGSize(width: 100, height: 100)
let card = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: cardSize))
UIView.animate(withDuration: 1.0) {
card.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: cardSize)
}

Adding SKShapeNode as Child to SKSpriteNode

Using SpriteKit, I'm trying to create a rectangle as a background, with a second rectangle 25% of the size of the background and position it at the bottom of the background.
(so the lower 25% is blue, and the upper 75% is dark grey)
This is my set up:
screenWidth = size.width //312
screenHeight = size.height //390
func createSky(){
let sky = SKSpriteNode(color: SKColor.darkGray, size: CGSize(width: screenWidth, height: screenHeight))
print("Sky: ", sky.frame)
addChild(sky)
}
func createGround(){
let ground = SKShapeNode.init(rectOf: CGSize(width: screenWidth, height: screenHeight / 4))
ground.fillColor = SKColor.blue
print("Ground: ", ground.frame )
sky.addChild(ground)
}
For some reason, ground.frame is printing Ground: (-157.35, -50.1, 314.7, 100.2). Why is this? Shouldn't the last 2 figures be 312, 97.5?
I've tried various .positions and .anchorPositions but I still can't get the SKShapeNode to sit correctly within the SKSpriteNode.
Eventually the Shape will be more complex which is why I'm not using Sprite for the second one. What am I missing?
So what it looks like from your code is that you are trying to create a shapeNode and then position it inside your SKSpriteNode. Most of it you got right, you just did the positioning code incorrectly.
When you add a child to a node, that child uses the parent node as its origin point for reference. So if you tell the child node to be -50 X and 150 Y, that child node will be -50 X and 150 Y from the center point of its parent node.
When I do child nodes (especially ones that have a ton of similarities to the parent node) I like to reference the parent node for positioning, width, and height. I don't know if it might be bad practice, but its what I've done for two years now.
This is how I would have done it:
var sky = SKSpriteNode()
var ground = SKShapeNode()
func createSky() {
sky = SKSpriteNode(color: SKColor.darkGray, size: CGSize(width: self.frame.size.wdith, height: self.frame.size.height))
// self.frame.size.width/self.frame.size.height calls its info from the ViewController, which is tied directly to your screen size (unless otherwise changed)
sky.position = CGPoint(x: 0,y: 0)
self.addChild(sky)
}
func createGround() {
ground = SKShapeNode((rectOf: CGSize(width: sky.width, height: sky.height * 0.25))
ground.position = CGPoint(x: 0, y: sky.position.y - (ground.size.height / 1.5))
}