Change variable during runtime - forms

I have an enum:
type Tool =
| Rectangle = 0
| Circle = 1
And a variable to hold a enum value:
let mutable selectedTool : Tool = Tool.Rectangle
I have two buttons:
let btn (location:Point) (size:Size) =
let bt = new Button()
do bt.Location <- location
do bt.Size <- size
bt
// These badboys right here:
let rectangleButton = btn (Point(0, 0)) (Size(50,50))
let ellipseButton = btn (Point(50, 0)) (Size(50,50))
They each have an event connected to them:
let btnRectEvent = rectangleButton.Click.Add(fun(x) -> selectedTool <-Tool.Rectangle)
let btnElliEvent = ellipseButton.Click.Add(fun(x) -> selectedTool <- Tool.Circle)
I want my events to change the value of the selectedTool variable.
Currently, it doesen't work.
I then record my MouseDown event on my drawing area to draw a shape using this event:
let mouseEvent =
let x = selectedTool
form.MouseDown
|> Event.add ( fun (evArgs1) ->
if x = Tool.Circle then
graphics.Graphics.DrawEllipse(whitePen, evArgs1.X, evArgs1.Y, 15, 15)
else
graphics.Graphics.DrawRectangle(whitePen, evArgs1.X, evArgs1.Y, 15, 15)
form.Refresh())
form is running as follows:
form.Controls.Add(rectangleButton)
form.Controls.Add(ellipseButton)
form.Show()
while form.Created do
Application.DoEvents()
Bonus Question but not in need of answering:
Is it possible to catch a click event on the button, wait for a mousedown event on the drawing area, place the first corner of my shape, have a preview of the shape when I'm moving my mouse around, and set the second corner on mouseUp, in an not-too-overkill way?
TL;DR bonus question: Can I easily make the rectangledrawing work like in MSPaint?

So I spotted the problem:
when you do
let x = selectedTool
form.MouseDown
|> Event.add ( fun (evArgs1) ->
Something goes here
Inside the lambda x won't change. You probably want to either get rid of the let x = ... before the lambda or use a ref cell like this:
let selectedTool = ref Rectangle
then you assign with
selectedTool := Circle
and get the value with
if (!selectedTool) = Circle
Although a match would be more common - like so
form.MouseDown
|> Event.add ( fun (evArgs1) ->
match (!selectedTool) with
|Circle -> graphics.Graphics.DrawEllipse(whitePen, evArgs1.X, evArgs1.Y, 15, 15)
|Rectangle -> graphics.Graphics.DrawRectangle(whitePen, evArgs1.X, evArgs1.Y, 15,15)
form.Refresh())

Related

SceneKit: How to arrange buttons in ascending order using for in loop?

The task is to add 10 buttons (0...9) with labels using for in loop.
I created buttons based on class ButtonPrototype. I assigned label to each button via counter inside for in loop.
It works, but there is incorrect labels order:
I need another order:
How can I implement correct order?
Code:
func createButtons() {
for y in 0...1 {
for x in 0...4 {
counterForLoop += 1
self.button = ButtonPrototype(pos: .init( CGFloat(x)/7, CGFloat(y)/7, 0 ), imageName: "\(counterForLoop)")
parentNode.addChildNode(button)
parentNode.position = SCNVector3(x: 100,
y: 100,
z: 100)
}
}
}
The following approach perfectly makes the trick:
for y in 0...1 {
for x in 0...4 {
let textNode = SCNNode()
let ascendingOrder: String = "\(((x+1)+(y*5)) % 10)"
let geo = SCNText(string: ascendingOrder, extrusionDepth: 0.5)
geo.flatness = 0.04
geo.firstMaterial?.diffuse.contents = UIImage(named: ascendingOrder)
textNode.geometry = geo
textNode.position = SCNVector3(x*10, -y*10, 0)
sceneView.scene?.rootNode.addChildNode(textNode)
print(ascendingOrder)
}
}
You have at least two problems with your code. Your smallest button label is in the lower left and you want it to be in the lower right, and your labels go 0-9, and you want them to go from 1 to 10 (but display 10 as “0”).
To reverse the x ordering, change X to 10-x in your creation of a position, and change your imageName to “((counterForLoop+1)%10)”:
self.button = ButtonPrototype(
pos: .init(
CGFloat(10-x)/7,
CGFloat(y)/7,
0),
imageName: "\((counterForLoop+1)%10)")
By the way, you should add a SceneKit tag to your question. That seems more important than either the label tag or the loops tag.

SKSpriteNode isn't responding when I try to hide it (Spoiler: SKAction timing issue)

OK, so I've been working on doing an RPG-style dialog box for a project, and while most of it is going smoothly, the one thing that's tripping me up right now is the little icon in the corner of the box to let you know there's more.
I tried to figure out how to get draw the shape, but not having any luck getting Core Graphics to draw triangles I decided to just use a PNG image of one instead. The code below shows everything relevant to how it's been set up and managed.
That being figured out, I'm now trying to get it to hide the marker when updating the box and show it again afterward. Here's what I've tried so far:
Method 1: Use .alpha = 0 to hide it from view during updates, restore with .alpha = 1
Method 2: Remove it from the node tree
Method 3: Place it behind the box background (located at .zPosition = -1)
The result has been consistent across all 3 methods: The triangle just stays in place, unresponsive when invoked.
class DialogBox: SKNode {
private var continueMarker = SKSpriteNode(imageNamed: "continueTriangle") // The triangle that shows in the lower-right to show there's more to read
init() {
/// Setup and placement. It appears in the proper position if I draw it and don't try to hide anything
continueMarker.size.width = 50
continueMarker.size.height = 25
continueMarker.position = CGPoint(x: ((width / 2) - (continueMarker.size.width * 0.9)), y: ((continueMarker.size.height * 0.9) - (height - margin)))
addChild(continueMarker)
}
func updateContent(forceAnimation: Bool = false) {
/// Determine what content to put into the box
hideContinueMarker()
/// Perform the content update in the box (which works as it should)
showContinueMarker()
}
func showContinueMarker() {
// continueMarker.alpha = 1 /// Method 1: Use .alpha to hide it from view during updates
// if (continueMarker.parent == nil) { // Method 2: Remove it from the tree
// addChild(continueMarker)
// }
continueMarker.zPosition = -2 /// Method 3: place it behind the box background (zPosition -1)
}
func hideContinueMarker() {
// continueMarker.alpha = 0 /// Method 1
// if (continueMarker.parent != nil) { /// Method 2
// continueMarker.removeFromParent()
// }
continueMarker.zPosition = 2 /// Method 3
}
}
OK, so while typing this one up I had some more ideas and ended up solving my own problem, so I figured I'd share the solution here, rather than pull a DenverCoder9 on everyone.
On the plus side, you get a look at a simple way to animate text in SpriteKit! Hooray!
In a final check to make sure I wasn't losing my mind, I added some print statements to showContinueMarker() and hideContinueMarker() and noticed that they always appeared simultaneously.
What's that mean? SKAction is likely at fault. Here's a look at the code for animating updates to the box:
private func animatedContentUpdate(contentBody: String, speaker: String? = nil) {
if let speaker = speaker {
// Update speaker box, if provided
speakerLabel.text = speaker
}
var updatedText = "" // Text shown so far
var actionSequence: [SKAction] = []
for char in contentBody {
updatedText += "\(char)"
dialogTextLabel.text = updatedText
// Set up a custom action to update the label with the new text
let updateLabelAction = SKAction.customAction(withDuration: animateUpdateSpeed.rawValue, actionBlock: { [weak self, updatedText] (node, elapsed) in
self?.dialogTextLabel.text = updatedText
})
// Queue up the action so we can run the batch afterward
actionSequence.append(updateLabelAction)
}
/// HERE'S THE FIX
// We needed to add another action to the end of the sequence so that showing the marker again didn't occur concurrent with the update sequence.
let showMarker = SKAction.customAction(withDuration: animateUpdateSpeed.rawValue, actionBlock: { [weak self] (node, elapsed) in
self?.showContinueMarker()
})
// Run the sequence
actionSequence.append(showMarker)
removeAction(forKey: "animatedUpdate") // Cancel any animated updates already in progress
run(SKAction.sequence(actionSequence), withKey: "animatedUpdate") // Start the update
}
In case you missed it in the big block there, here's the specific bit in isolation
let showMarker = SKAction.customAction(withDuration: animateUpdateSpeed.rawValue, actionBlock: { [weak self] (node, elapsed) in
self?.showContinueMarker()
})
Basically, we needed to add showing the triangle as an action at the end of the update sequence instead of just assuming it would occur after the update since the function was invoked at a later time.
And since all 3 methods work equally well now that the timing has been fixed, I've gone back to the .alpha = 0 method to keep it simple.

ARkit SCNMorpher isn't working for me. No errors, just no shape changes

I'm trying a simple example using SCNMorpher to blend between to poly spheres. They are identical in topology except for the position of the points
Each is stored in a .scn file and I get the shapes like:
sphereNode = SCNReferenceNode(named: "sphere")
sphereNode2 = SCNReferenceNode(named: "sphere2")
sphereNode?.morpher = SCNMorpher()
sphereNode!.morpher?.targets = [(sphereNode2?.childNodes.first!.geometry)!]
sphereNode!.name = "EFFECT"
I'm using the faceAnchor blend shapes to drive it
if let effectNode = sceneView?.scene.rootNode.childNode(withName: "EFFECT", recursively: true) {
let v = faceAnchor?.blendShapes[ARFaceAnchor.BlendShapeLocation.jawOpen]
effectNode.morpher?.setWeight(v as! CGFloat, forTargetNamed: "sphere2")
}
I've also tried:
...
effectNode.morpher?.setWeight(v as! CGFloat, forTargetAt: 0)
...
The code runs.. I can print values for v.. they change as I open/close my jaw and that value is passed to the morpher. I see the base sphere shape but it never deforms toward the sphere2 shape. Am I suppose to do anything else to force it to redraw or calc the deformation?
Hmm. looks like I was attaching the morpher to the parent of the shape, not the actual sphere.. funny how asking a question here sometimes creates that "Ah Ha" moment. Reading in my spheres like this fixed it:
sphereNode = SCNReferenceNode(named: "sphere").childNodes.first
sphereNode2 = SCNReferenceNode(named: "sphere2").childNodes.first
sphereNode?.morpher = SCNMorpher()
sphereNode!.morpher?.targets = [(sphereNode2.geometry)!]
sphereNode!.name = "EFFECT"

How to show all entry y value in the same x line in Swift?

I'm making a line chart by using iOS-charts framework. I have 3 line or set. Sometimes it will overlay each other so I made a balloon marker. It will show the value when hover the point but it only show 1 y value. How to show all the y value in the same x line?
similar question but no solve yet : https://github.com/danielgindi/Charts/issues/2173
This code is on BalloonMarker.swift, it is for edit the marker value.
public override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
setLabel(String(entry.y))
}
My solution
public override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
let set = chartView?.data?.dataSets[0]
let set1 = chartView?.data?.dataSets[1]
let setEntry = set?.entryForIndex(Int(entry.x))
let setEntry2 = set1?.entryForIndex(Int(entry.x))
let month = Int(entry.x + 1.0)
setLabel(String("\(month)月\n 收入:\(setEntry?.y ?? 0.0)\n 支出:\(setEntry2?.y ?? 0.0)"))
}

Closure CallBack Only Retain Last Object Created in Swift

i'm not sure if the title fit my question but my question is as below.
First of all, i will talk how i encounter this problem. Basically, I'm creating a game(you can just imagine) which has A MainPlayer and Many Of Enemies(AI). So, the MainPlayer will move around and enemies will chase after him. As you can imagine, the MainPlayer's position will update in every frame(maybe) and i need also to update enemies' chasing position(which is main character's position). So i'm using Closure to do it. The reason why i'm not using array to store all the enemies and update it every frame is because the enemies can be killed and will random spawn new enemies in the amount of time i set. If i use array it is kind of tricky and unsafe.
So, back to my question, i created this beginning of GameScene class:
typealias CallBacks = () -> Void
var playerDidMoveCallBacks: CallBacks?
This is my create enemy class: (The callback is at most bottom)
// Create Enemies
func createEnemyAtPosition(position: CGPoint) {
let enemyNode = EnemyNode()
enemyNode.name = "ENEMY_NODE"
enemyNode.position = position
enemyNode.setScale(1.5)
addChild(enemyNode)
let sprite = SKSpriteNode(imageNamed: "enemyWisp")
enemyNode.addChild(sprite)
enemyNode.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
enemyNode.physicsBody?.dynamic = true
enemyNode.physicsBody?.categoryBitMask = CollisionCategoryBitMask.Enemy
enemyNode.physicsBody?.collisionBitMask = 0
enemyNode.physicsBody?.contactTestBitMask = CollisionCategoryBitMask.ThrowingKnife | CollisionCategoryBitMask.Player
enemyNode.physicsBody?.usesPreciseCollisionDetection = true
let distanceWillCollideWithPlayer = sqrt(pow(enemyNode.position.x - self.playerNode.position.x, 2) + pow(enemyNode.position.y - self.playerNode.position.y, 2))
let durationWillColldeWithPlayer = NSTimeInterval(distanceWillCollideWithPlayer / self.enemyMovingSpeed)
let enemyMoveAction = SKAction.moveTo(self.playerNode.position, duration: durationWillColldeWithPlayer)
enemyNode.runAction(enemyMoveAction)
println("\((unsafeAddressOf(enemyNode)))")
// Update Player Position
playerDidMoveCallBacks = {() -> Void in
let distanceWillCollideWithPlayer = sqrt(pow(enemyNode.position.x - self.playerNode.position.x, 2) + pow(enemyNode.position.y - self.playerNode.position.y, 2))
let durationWillColldeWithPlayer = NSTimeInterval(distanceWillCollideWithPlayer / self.enemyMovingSpeed)
println("\((unsafeAddressOf(enemyNode)))")
let enemyMoveAction = SKAction.moveTo(self.playerNode.position, duration: durationWillColldeWithPlayer)
enemyNode.runAction(enemyMoveAction)
}
}
When i know my hero changed position i call the callback like this:
if self.playerDidMoveCallBacks != nil {
self.playerDidMoveCallBacks!()
}
But this can only work for the last object created and i think this make sense. Anyone can give me a solution?
What you want is an array of callbacks.
var playerDidMoveCallBacks: [CallBack]?
As every enemy is captured in the closure you could just iterate the array and call each callback.
for callback in playerDidMoveCallBacks {
callback()
}
But the solution of 0x141E is better.
The problem is that the enemy could not exist anymore when you call the closure, but the closure captures the object which can lead to strange behaviour. (ghost enemies)
With help from 0x141E i create this line of code in func update and solve my problem
// Update Enemies Position
for childNode in children {
let childSprite = childNode as? SKNode
if (childSprite?.name == "ENEMY_NODE") {
let distanceWillCollideWithPlayer = sqrt(pow(CGFloat(childSprite!.position.x) - self.playerNode.position.x, 2) + pow(CGFloat(childSprite!.position.y) - self.playerNode.position.y, 2))
let durationWillColldeWithPlayer = NSTimeInterval(distanceWillCollideWithPlayer / self.enemyMovingSpeed)
let enemyMoveAction = SKAction.moveTo(self.playerNode.position, duration: durationWillColldeWithPlayer)
childSprite!.runAction(enemyMoveAction)
}
}