i am new bie in IOS Image editing work.
I want to make functionality of Image Warp in ios or swift (any one).
I search lots of googling but not getting exact what i want
below link i am searf
How can you apply distortions to a UIImage using OpenGL ES?
https://github.com/BradLarson/GPUImage
https://github.com/Ciechan/BCMeshTransformView
Here is image what i want (When i touch the grid point image should we wrap and if i place the grid point at original place its should be original like wise)
I've written an extension for BCMeshTransformView that allows you to apply warp transform in response to user's touch input.
extension BCMutableMeshTransform {
static func warpTransform(from startPoint:CGPoint,
to endPoint:CGPoint, in size:CGSize) -> BCMutableMeshTransform {
let resolution:UInt = 30
let mesh = BCMutableMeshTransform.identityMeshTransform(withNumberOfRows: resolution,
numberOfColumns: resolution)!
let _startPoint = CGPoint(x: startPoint.x/size.width, y: startPoint.y/size.height)
let _endPoint = CGPoint(x: endPoint.x/size.width, y: endPoint.y/size.height)
let dragDistance = _startPoint.distance(to: _endPoint)
for i in 0..<mesh.vertexCount {
var vertex = mesh.vertex(at: i)
let myDistance = _startPoint.distance(to: vertex.from)
let hEdgeDistance = min(vertex.from.x, 1 - vertex.from.x)
let vEdgeDistance = min(vertex.from.y, 1 - vertex.from.y)
let hProtection = min(100, pow(hEdgeDistance * 100, 1.5))/100
let vProtection = min(100, pow(vEdgeDistance * 100, 1.5))/100
if (myDistance < dragDistance) {
let maxDistort = CGPoint(x:(_endPoint.x - _startPoint.x) / 2,
y:(_endPoint.y - _startPoint.y) / 2)
let normalizedDistance = myDistance/dragDistance
let normalizedImpact = (cos(normalizedDistance * .pi) + 1) / 2
vertex.to.x += maxDistort.x * normalizedImpact * hProtection
vertex.to.y += maxDistort.y * normalizedImpact * vProtection
mesh.replaceVertex(at: i, with: vertex)
}
}
return mesh
}
}
Then just set the property to your transformView.
fileprivate var startPoint:CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
startPoint = touch.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint_ = startPoint else { return }
guard let touch = touches.first else { return }
let position = touch.location(in: self)
transformView.meshTransform = BCMutableMeshTransform.warpTransform(from: startPoint_,
to: position, in: bounds.size)
}
The warp code is not perfect, but it gets the job done in my case. You can play with it, but the general idea stays the same.
Take a look at my answer to this question:
Warp \ bend effect on a UIView?
You might also look at this git library:
https://github.com/Ciechan/BCMeshTransformView
That might be a good starting point for what you want to do, but you'll need to learn about OpenGL, transformation matrices, at lots of other things.
What you are asking about is fairly straightforward OpenGL. You just need to set up a triangle strip that describes the modified grid points. You'd load your image as a texture, and then render the texture using the triangle strips.
However, "straightforward OpenGL" is sort of like straightforward rocket science. The high-level concepts may be straightforward, but there end up being lots of very fussy details you have to get right in order to make it work.
Take a look at this short video I created with my app Face Dancer
Face Dancer video with grid lines
Related
I am working with SceneKit and ARKit. I have made a collectionView with an array of emoji's. Now I want the user to be able to select the emoji from collectionView and when he/she touches the screen that selected emoji will be placed in 3D.
How can I do that? I think I have to create a function for the Node, but still my idea is blurry in the mind and I am not very much clear.
As far as any emoji is a 2D element, it's better to use a SpriteKit framework to upload them, not a SceneKit. But, of course, you might choose a SceneKit as well. So, there are two ways you can work with emojis in ARKit:
Using SpriteKit. In that case all the 2D sprites you spawn in ARSKView are always face the camera. So, if the camera moves around a definite point of real scene, all the sprites are rotates about their pivot point facing a camera.
Using SceneKit. In ARSCNView you can use all your sprites as a texture for 3D geometry. This texture could be for a plane, cube, sphere, or any custom model, it's up to you. To make, for example, a plane (with emojis texture on it) to face a camera use SCNBillboardConstraint constraint.
Here's how you code in ViewController might look like:
// Element's index coming from `collectionView`
var i: Int = 0
func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
let emojiArray = ["🐶","🦊","🐸","🐼","🐹"]
let emojiNode = SKLabelNode(text: emojiArray[i])
emojiNode.horizontalAlignmentMode = .center
emojiNode.verticalAlignmentMode = .center
return emojiNode
}
...and in Scene.swift file:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let sceneView = self.view as? ARSKView else { return }
if let currentFrame = sceneView.session.currentFrame {
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.75 // 75 cm from camera
let transform = simd_mul(currentFrame.camera.transform, translation)
let anchor = ARAnchor(transform: transform)
sceneView.session.add(anchor: anchor)
}
}
Or, if you use hit-testing, your code might look like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let sceneView = self.view as? ARSKView else { return }
if let touchLocation = touches.first?.location(in: sceneView) {
if let hit = sceneView.hitTest(touchLocation, types: .featurePoint).first {
sceneView.session.add(anchor: ARAnchor(transform: hit.worldTransform))
}
}
}
If you'd like to create an UICollectionView overlay containing emojis to choose from, read the following post.
If you'd like to create an SKView overlay, containing emojis to choose from, read the following post.
I am new to Swift and SpriteKit and am learning to understand the control in the game "Fish & Trip". The sprite node is always at the center of the view and it will rotate according to moving your touch, no matter where you touch and move (hold) it will rotate correspondingly.
The difficulty here is that it is different from the Pan Gesture and simple touch location as I noted in the picture 1 and 2.
For the 1st pic, the touch location is processed by atan2f and then sent to SKAction.rotate and it is done, I can make this working.
For the 2nd pic, I can get this by setup a UIPanGestureRecognizer and it works, but you can only rotate the node when you move your finger around the initial point (touchesBegan).
My question is for the 3rd pic, which is the same as the Fish & Trip game, you can touch anywhere on the screen and then move (hold) to anywhere and the node still rotate as you move, you don't have to move your finger around the initial point to let the node rotate and the rotation is smooth and accurate.
My code is as follow, it doesn't work very well and it is with some jittering, my question is how can I implement this in a better way? and How can I make the rotation smooth?
Is there a way to filter the previousLocation in the touchesMoved function? I always encountered jittering when I use this property, I think it reports too fast. I haven't had any issue when I used UIPanGestureRecoginzer and it is very smooth, so I guess I must did something wrong with the previousLocation.
func mtoRad(x: CGFloat, y: CGFloat) -> CGFloat {
let Radian3 = atan2f(Float(y), Float(x))
return CGFloat(Radian3)
}
func moveplayer(radian: CGFloat){
let rotateaction = SKAction.rotate(toAngle: radian, duration: 0.1, shortestUnitArc: true)
thePlayer.run(rotateaction)
}
var touchpoint = CGPoint.zero
var R2 : CGFloat? = 0.0
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches{
let previousPointOfTouch = t.previousLocation(in: self)
touchpoint = t.location(in: self)
if touchpoint.x != previousPointOfTouch.x && touchpoint.y != previousPointOfTouch.y {
let delta_y = touchpoint.y - previousPointOfTouch.y
let delta_x = touchpoint.x - previousPointOfTouch.x
let R1 = mtoRad(x: delta_x, y: delta_y)
if R2! != R1 {
moveplayer(radiant: R1)
}
R2 = R1
}
}
}
This is not an answer (yet - hoping to post one/edit this into one later), but you can make your code a bit more 'Swifty' by changing the definition for movePlayer() from:
func moveplayer(radian: CGFloat)
to
rotatePlayerTo(angle targetAngle: CGFloat) {
let rotateaction = SKAction.rotate(toAngle: targetAngle, duration: 0.1, shortestUnitArc: true)
thePlayer.run(rotateaction)
}
then, to call it, instead of:
moveplayer(radiant: R1)
use
rotatePlayerTo(angle: R1)
which is more readable as it describes what you are doing better.
Also, your rotation to the new angle is constant at 0.1s - so if the player has to rotate further, it will rotate faster. it would be better to keep the rotational speed constant (in terms of radians per second). we can do this as follows:
Add the following property:
let playerRotationSpeed = CGFloat((2 *Double.pi) / 2.0) //Radian per second; 2 second for full rotation
change your moveShip to:
func rotatePlayerTo(angle targetAngle: CGFloat) {
let angleToRotateBy = abs(targetAngle - thePlayer.zRotation)
let rotationTime = TimeInterval(angleToRotateBy / shipRotationSpeed)
let rotateAction = SKAction.rotate(toAngle: targetAngle, duration: rotationTime , shortestUnitArc: true)
thePlayer.run(rotateAction)
}
this may help smooth the rotation too.
I need to keep track of animation with texture. I am animating power bar and when user clicks the screen it should stop and save the power. I can not figure out how to save power. So far I have this: on first touch power bar animates, but on the second touch it only stops but does not save power.
This is how I create animation:
textureAtlas = SKTextureAtlas(named:"images")
for i in 1...textureAtlas.textureNames.count{
let name = "\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
let animateForward = SKAction.animate(with: textureArray, timePerFrame: 0.1)
let animateBackward = SKAction.reversed(animateForward)
let sequence = SKAction.sequence([animateForward,animateBackward()])
This is how I detect touches:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouchStorage = touches.first{ // after first touch
let animateForward = SKAction.animate(with: textureArray, timePerFrame: 0.1)
let animateBackward = SKAction.reversed(animateForward)
let sequence = SKAction.sequence([animateForward,animateBackward()])
arrow.removeAllActions()
arrow.run(SKAction.repeatForever(sequence))
firstTouch = firstTouchStorage
}
for touch in touches{
if touch == firstTouch{
touchesArray.append(touch)
let angle = arrow.zRotation
if touchesArray.count == 2{
arrow.removeAllActions()
arrow.removeFromParent()
}
}
}
I am trying to solve this problem for too long, but I can not figure it out. I hope you will help me.
As #KnightOfDragon suggested : animateWithTextures has a restore: parameter, if you set that to false, when you stop animating the last texture should stay on the sprite. If you go ahead and read the textures description, then you will know what texture it stopped on, and could plan accordingly
I can't really find a simple solution for this, every example I see only shows very complex solutions, but all I want is 2-3 images that cycle so it appears as if it is animated. Same effect as an animated Gif. For now I have this to create an image
MonsterNode = SKSpriteNode(imageNamed: "MonsterNode_GameScene")
but how would I set MonsterNode variable to an animation of this sort? I am really looking for the very least amount of code needed to achieve this.
The main idea is to use animateWithTextures for this task. You need to set all the frames that the sprite needs to animated and the displayed time of each frame. Then use repeatActionForever to run the animation loop.
// Add 3 frames
let f0 = SKTexture.init(imageNamed: "MonsterNode_GameScene_0")
let f1 = SKTexture.init(imageNamed: "MonsterNode_GameScene_1")
let f2 = SKTexture.init(imageNamed: "MonsterNode_GameScene_2")
let frames: [SKTexture] = [f0, f1, f2]
// Load the first frame as initialization
monsterNode = SKSpriteNode(imageNamed: "MonsterNode_GameScene_0")
monsterNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
// Change the frame per 0.2 sec
let animation = SKAction.animateWithTextures(frames, timePerFrame: 0.2)
monsterNode.runAction(SKAction.repeatActionForever(animation))
self.addChild(monsterNode)
I was getting the same issue of big red X.
So instead of defining the monsterNode in code. I created the monstor on sks screen by dragging first image of animation from atlas folder. Then assign it a name: monsterNode from properties section. Here's the code
var runAnimation = [SKTexture]()
override func didMove(to view: SKView) {
let runAtlas = SKTextureAtlas(named: "run")
for index in 1...runAtlas.textureNames.count{
let textureName = String.init(format: "run%1d", index)
runAnimation.append(SKTexture(imageNamed: textureName))
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let monsterNode = self.childNode(withName: "monsterNode")
if(monsterNode != nil){
let animation = SKAction.animate(with: runAnimation, timePerFrame: 0.1)
monsterNode?.run(SKAction.repeatForever(animation))
}
}
I am using SceneKit to import a 3d image model of a human body. When i select a particular location point in the image, i want the app to recognize the body part and perform a different function for each part. How do i go about implementing this? What is the best way to do this?
P.s. when the image is rotated it shows a different view. I need the app to be able to recognize the body part even when it is rotated by the user. Any guidance as to how to proceed would be appreciated..
Here's a simple SceneKit picking example.
The scene is setup in the viewDidLoad, for your use case I'd expect a scene would be loaded from file (best done in another method). This file will hopefully have the different components you wish to pick as separate components in a tree-like hierarchy. The author of this 3D body model will have hopefully labelled these components appropriately so that your code can identify what to do when your left-femur is selected (and not comp2345).
For a complex model expect several 'hits' for any xy coordinate as you will be returned all nodes intersected by the hit ray. You may wish to only use the first hit.
import UIKit
import SceneKit
class ViewController: UIViewController {
#IBOutlet var scenekitView: SCNView!
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()
let boxNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
boxNode.name = "box"
scene.rootNode.addChildNode(boxNode)
let sphereNode = SCNNode(geometry: SCNSphere(radius: 1))
sphereNode.name = "sphere"
sphereNode.position = SCNVector3Make(2, 0, 0)
boxNode.addChildNode(sphereNode)
let torusNode = SCNNode(geometry: SCNTorus(ringRadius: 1, pipeRadius: 0.3))
torusNode.name = "torus"
torusNode.position = SCNVector3Make(2, 0, 0)
sphereNode.addChildNode(torusNode)
scenekitView.scene = scene
scenekitView.autoenablesDefaultLighting = true
scenekitView.allowsCameraControl = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//get the first touch location in screen coordinates
guard let touch = touches.first else {
return
}
//convert the screen coordinates to view coordinates as the SCNView make not take
//up the entire screen.
let pt = touch.locationInView(self.scenekitView)
//pass a ray from the points 2d coordinates into the scene, returning a list
//of objects it hits
let hits = self.scenekitView.hitTest(pt, options: nil)
for hit in hits {
//do something with each hit
print("touched ", hit.node.name!)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}