I'm fairly new to Swift, only having used Python and Pascal before. I was wondering if anyone could help with generating a floating point number in range. I know that cannot be done straight up. So this is what I've created. However, it doesn't seem to work.
func location() {
// let DivisionConstant = UInt32(1000)
let randomIntHeight = arc4random_uniform(1000000) + 12340000
let randomIntWidth = arc4random_uniform(1000000) + 7500000
XRandomFloat = Float(randomIntHeight / UInt32(10000))
YRandomFloat = Float(randomIntWidth / UInt32(10000))
randomXFloat = CGFloat(XRandomFloat)
randomYFloat = CGFloat(YRandomFloat)
self.Item.center = CGPointMake(randomXFloat, randomYFloat)
}
By the looks of it, when I run it, it is not dividing by the value of the DivisionConstant, so I commented this and replaced it with a raw value. However, self.Item still appears off screen. Any advice would be greatly appreciated.
This division probably isn't what you intended:
XRandomFloat = Float(randomIntHeight / UInt32(10000))
This performs integer division (discarding any remainder) and then converts the result to Float. What you probably meant was:
XRandomFloat = Float(randomIntHeight) / Float(10000)
This is a floating point number with a granularity of approximately 1/10000.
Your initial code:
let randomIntHeight = arc4random_uniform(1000000) + 12340000
generates a random number between 12340000 and (12340000+1000000-1). Given your final scaling, that means a range of 1234 and 1333. This seems odd for your final goals. I assume you really meant just arc4random_uniform(12340000), but I may misunderstand your goal.
Given your comments, I think you've over-complicated this. The following should give you a random point on the screen, assuming you want an integral (i.e. non-fractional) point, which is almost always what you'd want:
let bounds = UIScreen.mainScreen().bounds
let x = arc4random_uniform(UInt32(bounds.width))
let y = arc4random_uniform(UInt32(bounds.height))
let randomPoint = CGPoint(x: CGFloat(x), y: CGFloat(y))
Your problem is that you're adding the the maximum value to your random value, so of course it's always going to be offscreen.
I'm not sure what numbers you're hoping to generate, but what you're getting are results like:
1317.0, 764.0
1237.0, 795.0
1320.0, 814.0
1275.0, 794.0
1314.0, 758.0
1300.0, 758.0
1260.0, 809.0
1279.0, 768.0
1315.0, 838.0
1284.0, 763.0
1273.0, 828.0
1263.0, 770.0
1252.0, 776.0
1255.0, 848.0
1277.0, 847.0
1236.0, 847.0
1320.0, 772.0
1268.0, 759.0
You're then using this as the center of a UI element. Unless it's very large, it's likely to be off-screen.
Related
I'm building a small application that takes the user's device's lat/long position as well as the heading (true north) and tells you what sort of points of interests are in front of the user. I am getting all my points of interests from google maps api.
I thought the easiest way to do this would be a dot product calculation between my forward vector and the AB vector however I have some false positives.
Here is my code:
func isFront(_ p1 : Point, _ p2 : Point, _ p1Heading : Double) -> Bool {
let forward = Point(cos(p1Heading), sin(p1Heading))
let AB = Point(p2.x - p1.x, p2.y - p1.y)
let lenAB = (AB.x * AB.x + AB.y * AB.y).squareRoot()
let normalAB = Point(AB.x / lenAB, AB.y / lenAB)
let dot = (normalAB.x * forward.x + normalAB.y * forward.y)
return (dot > 0)
}
So something that is coming back as true but I think shouldnt is:
My location(lat/lng): 42.359291, -71.059638 heading: 173.89306640625
Point of interest (lat/lng): 42.359980, -71.060303
Is this a good approach in figuring out if things are in front or should I look into doing something different?
Yes dot product is a good approach for this but I do not see any dot product in your equations/code I see only messed up gibberish there... If I get it right:
p1 is device position
forward is forward direction
p2 is tested point
then it should be:
let forward = Point(cos(p1Heading), sin(p1Heading))
let dot = ((p2.x-p1.x)*forward.x)+((p2.y-p1.y)*forward.y)
return (dot>0.0)
As you can see no lengths are required. Also no normal (even if your normal is not a normal but some gibberish). I think you should read some book/tutorial on vector math basic and check how things are computed here the 2D stuff you mess up:
dot(a,b) = a.x*b.x + a.y*b.y
normal(a) = Point(a.y,-a.x)
According to this question, using == and != should let you check for equality between two CGPoint objects.
However, the code below fails to consider two CGPoint objects as equal even though they output the same value.
What is the right way to check equality among CGPoint objects?
Code:
let boardTilePos = boardLayer.convert(boardTile.position, from: boardTile.parent!)
let shapeTilePos = boardLayer.convert(tile.position, from: tile.parent!)
print("board tile pos: \(boardTilePos). active tile pos: \(shapeTilePos). true/false: \(shapeTilePos == boardTilePos)")
Output:
board tile pos: (175.0, 70.0). active tile pos: (175.0, 70.0). true/false: false
Unfortunately, what you see in the console is not what your real value is.
import UIKit
var x = CGPoint(x:175.0,y:70.0)
var y = CGPoint(x:175.0,y:70.00000000000001)
print("\(x.equalTo(y)), \(x == y),\(x),\(y)")
The problem is, the console only allows for 10-16 but in reality your CGFloat can go even lower than that because on 64bit architecture, CGFloat is Double.
This means you have to cast your CGPoint values to a Float if you want to get equality that will appear on the console, so you need to do something like:
if Float(boxA.x) == Float(boxB.x) && Float(boxA.y) == Float(boxB.y)
{
//We have equality
}
Now I like to take it one step further.
In most cases, we are using CGPoint to determine points on the scene. Rarely do we ever want to be dealing with 1/2 points, they make our lives just confusing.
So instead of Float, I like to cast to Int. This will guarantee if two points are lying on the same CGPoint in scene space
if Int(boxA.x) == Int(boxB.x) && Int(boxA.y) == Int(boxB.y)
{
//We have equality
}
I'm providing an alternate answer since I don't agree with Knight0fDragon's implementation. This is only if you want to deal with factions of a point. If you only care about points in whole numbers, see Knight0fDragon's answer.
You don't always have the luxury of logging points to the console, or seeing if you're trying to compare points that are the victim of floating point math, like comparing (175.0, 70.0) to (175.0, 70.00001) (which both log as (175.0, 70.0) in the console). Yes, truncating to Int is a great way of understanding why two points that appear to print to the console as equal aren't. But it's not a catch all solution one should use for comparing every point. Depending on what level of precision you need, you want to take the absolute value of the difference of both x and y for each point, and see if it is in an acceptable range of a delta you specify.
var boxA = CGPoint(x:175.0, y:70.0)
var boxB = CGPoint(x:175.0, y:70.00000000000001)
let delta: CGFloat = 0.01
if (fabs(boxA.x - boxB.x) < delta) &&
(fabs(boxA.y - boxB.y) < delta) {
// equal enough for our needs
}
The answer to the question "What is the right way to check equality among CGPoint objects?" really depends on the way you compare floating point numbers.
CGPoint provides its own comparison method: equalTo(_ point2: CGPoint)
Try this:
shapeTilePos.equalTo(boardTilePos)
I have a calculation that calculate square meters from millimeters and for this to show corect I would like to remove all numbers after the 2 first digits.
This is what I have tried so far.
let spha = sizeh.wshight()
let spwa = sizew.wswidth()
let areas1 = areaModel(pwa:spwa, pha:spha)
let formatter5 = NSNumberFormatter ()
formatter5.maximum = 2
let scarea = formatter5.stringFromNumber(areas1.sarea())!
screenAreaLabel?.text = "\(scarea)sqm"
I am a little lost at this point, I am using Float in calculation of this area.
Hope someone could help me in the right direction.
More detail info added.
I would like the Screen Area to only show 24sqm not 24772610sqm
You are multiplicating a value in millimeter by another value in millimeters, therefore the result is in square millimeters.
To convert square millimeters to square meters, just divide the result by 1_000_000.
You could also set formatter.multiplier to 1.0 / 1_000_000.
I'm building an app that features some graphical manipulation. I'm storing shapes as UIBezierPaths, and I want to allow users to touch points along the line to create saved locations. Using the wonderful answer to this question, and more specifically, this project, I'm able to place a point on a line knowing the percentage of its length the point rests on. This is half of my problem.
I want a way to take a point on a path, and derive the percent of its length.
My math-fu is extremely weak. I've studied bezier curves but I simply don't have the math to understand it.
I would humbly submit that "go back and learn geometry and trigonometry" is a correct answer, but sadly one I don't have time for at present. What I need is a way to fill in this method:
- (CGFloat)percentOfLengthAtPoint:(CGPoint)point onPath:(UIBezierPath*)path
Any help appreciated!
I have working code that solves my problem. I'm not particularly proud of it; the overall technique is essentially a brute-force attack on a UIBezierPath, which is kind of funny if you think about it. (Please don't think about it).
As I mentioned, I have access to a method that allows me to get a point from a given percentage of a line. I have taken advantage of that power to find the closest percentage to the given point by running through 1000 percentage values. To wit:
Start with a CGPoint that represents where on the line the user touched.
let pointA = // the incoming CGPoint
Run through the 0-1 range in the thousands. This is the set of percentages we're going to brute-force and see if we have a match. For each, we run pointAtPercentOfLength, from the linked project above.
var pointArray:[[String:Any]] = []
for (var i:Int = 0; i <= 1000; i++) {
let value = CGFloat(round((CGFloat(i) / CGFloat(1000)) * 1000) / 1000)
let testPoint = path.pointAtPercentOfLength(value)
let pointB = CGPoint(x: floor(testPoint.x), y: floor(testPoint.y))
pointArray.append(["point" : pointB, "percent" : value])
}
That was the hard part. Now we take the returning values and calculate the distance between each point and the touched point. Closest one is our winner.
// sort the damned array by distance so we find the closest
var distanceArray:[[String:Any]] = []
for point in pointArray {
distanceArray.append([
"distance" : self.distanceFrom(point["point"] as! CGPoint, point2: pointA),
"point" : point["point"],
"percent" : point["percent"] as! CGFloat
])
}
Here's the sorting function if you're interested:
func distanceFrom(point1:CGPoint, point2:CGPoint) -> CGFloat {
let xDist = (point2.x - point1.x);
let yDist = (point2.y - point1.y);
return sqrt((xDist * xDist) + (yDist * yDist));
}
Finally, I sort the array by the distance of the values, and pick out the winner as our closest percent.
let ordered = distanceArray.sort { return CGFloat($0["distance"] as! CGFloat) < CGFloat($1["distance"] as! CGFloat) }
ordered is a little dictionary that includes percent, the correct value for a percentage of a line's length.
This is not pretty code, I know. I know. But it gets the job done and doesn't appear to be computationally expensive.
As a postscript, I should point to what appears to be a proper resource for doing this. During my research I read this beautiful article by David Rönnqvist, which included an equation for calculating the percentage distance along a path:
start⋅(1-t)3 + 3⋅c1⋅t(1-t)2 + 3⋅c2⋅t2(1-t) + end⋅t3
I was just about to try implementing that before my final solution occurred to me. Math, man. I can't even brain it. But if you're more ambitious than I, and wish to override my 30 lines of code with a five-line alternative, everyone would appreciate it!
I think your approach is sound, but you could do this far more efficiently.
Instead of creating an two arrays of dicts (with a thousand elements each) and then sorting the array - just use a while loop to move from 0.0 to 1.0, calculate the distance to the touch point and keep track of the minimum distance.
For example:
var t:CGFloat = 0.0
let step:CGFloat = 0.001
var minDistance:CGFloat = -1.0
var minPoint:CGPoint = CGPointZero
var minT:CGFloat = -1;
while (t<1.0) {
let point = pointAtPercentOfLength(t)
let distance:CGFloat = self.distanceFrom(point, point2: pointA)
if (minDistance == -1.0 || distance < minDistance) {
minDistance = distance
minPoint = point
minT = t
}
t += step
}
print("minDistance: \(minDistance) minPoint: \(minPoint.x) \(minPoint.y) t\(minT)\n")
When I use the hard coded data for diameter and height Swift runs simulator fine, but when I try and use text values in place using TextField.text.toInt() then I keep getting very annoying error message of:-
cannot invoke \ with an argument list of type $st15
Just where am I going wrong. I am new to Swift and have only used AppInventor before to create an app with a few thousand downloads. I am a enthusiastic but probably slow learner, but I will get there if someone would be so kind to help me out a little. Note: Formula is simply PIr2 x h to give cylinder volume. I want to use diameter which explains why I am halving each time.
let PI = 3.142
var bodyDiameter = bodyDiameterTextField.text.toInt() // 3.0
var bodyHeight = bodyHeightTextField.text.toInt() // 10.0
var cylinderVolume: Double
var cylinderVolume = (PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume")
cylinderVolumeLabel.text = "(cylinderVolume)"
Here follows same code with hard coded values for Bodydiameter and Bodyheight. It all works great in the playground and the simulator. I guess its got something to do with Integers and Floats, but I'm probably way out.
let PI = 3.142
var bodyDiameter = 3.0
var bodyHeight = 10.0
var cylinderVolume = (PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume")
cylinderVolumeLabel.text = "(cylinderVolume)"
Your code has several errors.
First, and not really an error, there's already a built-in constant for π, named M_PI. Use that instead of defining your own PI constant.
Next, String.toInt() returns an Int?. The question mark means the return type is really Optional<Int>. This is a container that is either empty (nil), or contains an Int. If you want to use the Int value, you need to unwrap it. You might want to check that the Optional isn't nil first, though.
Next, assuming you unwrap the Int, you can't perform arithmetic on mixed Int and Double values in Swift. You have to convert to all Int or all Double. You probably want to use all Double. In fact, you probably don't want to convert from Int to Double at all. You probably want to get a Double from the text field in the first place. There's no toDouble on String in Swift, but there are some other ways to do it.
Finally, you need to say \(cylinderVolume) to interpolate the value into the string. Your code omits the \.
Try this:
var bodyDiameter = (bodyDiameterTextField.text as NSString).doubleValue
var bodyHeight = (bodyHeightTextField.text as NSString).doubleValue
var cylinderVolume = (M_PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume: \(cylinderVolume)")
cylinderVolumeLabel.text = "(cylinderVolume)"
If you want to convert the strings to Doubles in a localization-friendly way, or detect when the strings aren't valid Doubles, look up NSNumberFormatter.