How to represent magnitude for mass in Swift?
If you want to be able to specify the unit of the mass with a String
perhaps you could use String
to represent the shorthand name and variables to give you more information about the unit, such as magnitude
. Here's an example:
1. MassUnit
enum MassUnit: String {
case Milligrams = "mg"
case Grams = "g"
case Kilos = "kg"
case Tons = "t"
var magnitude: Int {
let mag: Int
switch self {
case .Milligrams: mag = -3
case .Grams : mag = 0
case .Kilos : mag = 3
case .Tons : mag = 6
}
return mag
}
static func ordersOfMagnitudeFrom(unit1: MassUnit, to unit2: MassUnit) -> Int {
return unit1.magnitude - unit2.magnitude
}
}
extension MassUnit: Printable {
var description: String {
return self.rawValue
}
}
2. Then, for storing actual masses you could use a Struct
, which could also handle the conversions. For example:
struct Mass {
var value : Double
var unit : MassUnit
static func convertMass(mass: Mass, toUnit unit: MassUnit) -> Mass {
let ordersOfMagnitude = MassUnit.ordersOfMagnitudeFrom(mass.unit, to: unit)
let multipler = pow(10.0, Double(ordersOfMagnitude))
return Mass(value: mass.value * multipler, unit: unit)
}
// Returns an optional Mass because we can't know for sure
// unitString will represent a MassUnit.
static func convertMass(mass: Mass, toUnit unitString: String) -> Mass? {
if let unit = MassUnit(rawValue: unitString) {
return convertMass(mass, toUnit: unit)
}
return nil
}
}
extension Mass {
init?(value: Double, _ unitString: String) {
if let unit = MassUnit(rawValue: unitString) {
self = Mass(value: value, unit: unit)
} else {
return nil
}
}
}
extension Mass : Printable {
var description: String {
return "\(value) \(unit)"
}
}
3. Then you can use the masses and units:
if let mass = Mass(value: 1, "kg"),
let convertedMass = Mass.convertMass(mass, toUnit: "g") {
println("\(mass) converted to \(MassUnit.Grams) equals \(convertedMass)")
// Prints: 1.0 kg converted to g equals 1000.0 g
}
However if you use a unitString
that isn't convertible to a MassUnit
(either when creating or converting) nil
will be returned. For example:
let mass = Mass(value: 1, "NotAUnit") // nil
Swift: maximum readability and extendability in example unit converter project
I already did something very similar, although it doesn't have a strict notion of SI yet.
See https://github.com/fluidsonic/JetPack/tree/master/sources/measurement
How to adhere to protocol method with a Raw type in method argument?
When dealing with your unit conversions, I advise not trying to use String
s to represent the units when converting the way you've got setup above. That's going to make your code complicated with checking the String
can be converted to its respective enum every time you want to make a conversion. Also, what if you want to use the a MassUnit
/VolumeUnit
instead of a String
?
I would recommend using the a setup similar to what I've outlined below. It references my previous answer - How to represent magnitude for mass in Swift?
(Note - I've excluded anything to do with the volume because it's basically the same as the implementation for the mass)
I'd make the units like so:
protocol UnitProtocol {
var magnitude: Int { get }
init?(rawValue: String)
}
// Taken from my previous answer.
enum MassUnit: String, UnitProtocol, Printable {
case Milligram = "mg"
case Gram = "g"
var magnitude: Int {
let mag: Int
switch self {
case .Milligram: mag = -3
case .Gram : mag = 0
}
return mag
}
var description: String {
return rawValue
}
}
// Not making this a method requirement of `UnitProtocol` means you've only got to
// write the code once, here, instead of in every enum that conforms to `UnitProtocol`.
func ordersOfMagnitudeFrom<T: UnitProtocol>(unit1: T, to unit2: T) -> Int {
return unit1.magnitude - unit2.magnitude
}
Then I'd make the masses/volumes like so:
protocol UnitConstruct {
typealias UnitType: UnitProtocol
var amount: Double { get }
var unit : UnitType { get }
init(amount: Double, unit: UnitType)
}
struct Mass : UnitConstruct {
let amount: Double
let unit : MassUnit
}
Now for the converting function! Using a global function means you don't need to rewrite the code for every type than conforms to UnitConstruct
.
func convert<T: UnitConstruct>(lhs: T, toUnits unit: T.UnitType) -> T {
let x = Double(ordersOfMagnitudeFrom(lhs.unit, to: unit))
return T(amount: lhs.amount * pow(10, x), unit: unit)
}
// This function is for converting to different units using a `String`,
// as asked in the OP.
func convert<T: UnitConstruct>(lhs: T, toUnits unit: String) -> T? {
if let unit = T.UnitType(rawValue: unit) {
return convert(lhs, toUnits: unit)
}
return nil
}
You can then use the previous code like so:
let mass1 = Mass(amount: 1.0, unit: .Gram)
let mass2 = convert(mass1, toUnits: .Milligram) // 1000.0 mg
// Or, converting using Strings:
let right = convert(mass1, toUnits: "mg") // Optional(1000.0 mg)
let wrong = convert(mass1, toUnits: "NotAUnit") // nil
SKPhysicsBody Change Center of Mass
The center of mass can't be way off, it is exact. The problem is you want your physics body to have a non-uniform density. The density of a rigid physics body is uniform throughout. I don't see a way of doing this other than modifying the physics body itself (i.e. Changing the size/shape of the physics body).
Even if you try creating a physics body from the union of multiple physics bodies using SKPhysicsBody(bodies: [body1,body2])
the density will still be uniform because Sprite Kit will still simulate this as just one body.
The only solution I can think of is to fuse 2 bodes together using a joint as shown below.
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let node1 = SKShapeNode(circleOfRadius: 20)
node1.physicsBody = SKPhysicsBody(circleOfRadius: 20)
node1.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
let node2 = SKShapeNode(circleOfRadius: 2)
node2.physicsBody = SKPhysicsBody(circleOfRadius: 1)
node2.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0+19)
node2.physicsBody!.mass = node1.physicsBody!.mass * 0.2 //Set body2 mass as a ratio of body1 mass.
self.addChild(node1)
self.addChild(node2)
let joint = SKPhysicsJointFixed.jointWithBodyA(node1.physicsBody!, bodyB: node2.physicsBody!, anchor: node1.position)
self.physicsWorld.addJoint(joint)
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
node1.physicsBody!.applyImpulse(CGVector(dx: -10, dy: 0))
}
}
However this won't fix the special case where the circle lands perfectly centered. You see I apply an impulse to temporarily solve the issue in the code above. A better solution is to check for this special case in the update method.
Note the gif got cut off a little at the bottom.
Calculating Angle of Reach in Scenekit
You are not supposed to "apply a force", you are supposed to set the initial velocity to the V you used in the formula.
If the vector you are returning is a unit vector (has a magnitude of 1), then multiply each component by V and set the velocity of the object directly.
Also, these formula's don't take damping into account (air friction) -- so to hit the target exactly, set your ball's linearDamping
and angularDamping
to 0.0 (default is 0.1). You will notice more damping on longer trajectories.
NOTE: if you do this, don't use "mass" in the angle calculation
Why Int8.max &+ Int8.max equals to -2?
Swift (and every other programming language I know) uses 2's complement to represent signed integers, rather than sign-and-magnitude as you seem to assume.
In the 2's complement representation, the leftmost 1
does not represent "a negative sign". You can think of it as representing -128, so the Int8
value of -2 would be represented as 1111 1110 (-128 + 64 + 32 + 16 + 8 + 4 + 2).
OTOH, -126 would be represented as 1000 0010 (-128 + 2).
Related Topics
Why Does Swift Provide Both a Cgrect Initializer and a Cgrectmake Function
Why Do I Still Need to Unwrap Swift Dictionary Value
Difference Between Println and Print in Swift
Transparent Nscollectionview Background
Dispatchqueue.Main.Asyncafter Is Inaccurate
Nsbundle.Mainbundle().Urlforresource("Bach1", Withextension: "Jpg") Returning Null
How to Have a Swift Protocol Without Functions
How to Save and Load Arworldmap in Swiftui App
Swift - Class Method Which Must Be Overridden by Subclass
How to Benchmark Swift Code Execution
Can a Swift Property Wrapper Reference the Owner of the Property Its Wrapping
How to Get Date and Time to Show a Clock in Uilabel
Open a Viewcontroller from Remote Notification
How to Get the Realy Fixed Device-Id in Swift
Error: Bool Is Not Convertible to Void:
Swift: Loop Over Array Elements and Access Previous and Next Elements