[Pitch] Angle Type

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon

2 Likes

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

···

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

2 Likes

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

···

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

I do a lot of geometry and spherical-related work and i have never found an Angle type to be worth having. Always use radians. It’s what sin() and cos() take, it’s what graphics APIs like Cairo expect, it’s what graphics formats like SVG use. plus, do you *really* want to be case-branching on every angle value? that really adds up when you’re converting 100,000s of lat-long pairs to cartesian.

···

On Jan 14, 2018, at 12:04 AM, BJ Homer via swift-evolution <swift-evolution@swift.org> wrote:

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

Maybe it would be good to pitch such improvements to the Foundation API? “Angle” is certainly a lot more discoverable and convenient to type than “Measurement<UnitAngle>”. That said, I tend not to use Foundation’s Measurement API. It’s pretty heavyweight.

test.swift:

import Foundation

@available(macOS 10.12, *)
func a() {
let a = Measurement(value: 20, unit: UnitDuration.minutes).converted(to: .seconds)
}

Generated IR:

define hidden swiftcc void @_T04test1ayyF() #0 {
entry:
%0 = call %swift.type* @_T0So12UnitDurationCMa() #3
%1 = getelementptr inbounds %swift.type, %swift.type* %0, i32 0, i32 0
%.kind = load i64, i64* %1, align 8
%isObjCClassWrapper = icmp eq i64 %.kind, 14
br i1 %isObjCClassWrapper, label %isWrapper, label %metadataForClass.cont

isWrapper: ; preds = %entry
%2 = bitcast %swift.type* %0 to %swift.type**
%3 = getelementptr inbounds %swift.type*, %swift.type** %2, i64 1

metadataForClass.cont: ; preds = %isWrapper, %entry
%.class = phi %swift.type* [ %0, %entry ], [ %4, %isWrapper ]
%5 = bitcast %swift.type* %.class to %objc_class*
%6 = load i8*, i8** @"\01L_selector(minutes)", align 8
%7 = bitcast %objc_class* %5 to i8*
%8 = call %0* bitcast (void ()* @objc_msgSend to %0* (i8*, i8*)*)(i8* %7, i8* %6)
%9 = bitcast %0* %8 to i8*
%10 = call i8* @objc_retainAutoreleasedReturnValue(i8* %9) #4
%11 = bitcast i8* %10 to %0*
%12 = bitcast %0* %11 to %TSo12UnitDurationC*
%.asUnsubstituted = bitcast %TSo12UnitDurationC* %12 to %TSo4UnitC*
%13 = call swiftcc { %TSo4UnitC*, double } @_T010Foundation11MeasurementVACyxGSd5value_x4unittcfC(double 2.000000e+01, %TSo4UnitC* %.asUnsubstituted, %swift.type* %0)
%14 = extractvalue { %TSo4UnitC*, double } %13, 0
%15 = extractvalue { %TSo4UnitC*, double } %13, 1
%.asSubstituted = bitcast %TSo4UnitC* %14 to %TSo12UnitDurationC*
%16 = getelementptr inbounds %swift.type, %swift.type* %0, i32 0, i32 0
%.kind1 = load i64, i64* %16, align 8
%isObjCClassWrapper2 = icmp eq i64 %.kind1, 14
br i1 %isObjCClassWrapper2, label %isWrapper3, label %metadataForClass.cont4

%17 = bitcast %swift.type* %0 to %swift.type**
%18 = getelementptr inbounds %swift.type*, %swift.type** %17, i64 1

%.class5 = phi %swift.type* [ %0, %metadataForClass.cont ], [ %19, %isWrapper3 ]
%20 = bitcast %swift.type* %.class5 to %objc_class*
%21 = load i8*, i8** @"\01L_selector(seconds)", align 8
%22 = bitcast %objc_class* %20 to i8*
%23 = call %0* bitcast (void ()* @objc_msgSend to %0* (i8*, i8*)*)(i8* %22, i8* %21)
%24 = bitcast %0* %23 to i8*
%25 = call i8* @objc_retainAutoreleasedReturnValue(i8* %24) #4
%26 = bitcast i8* %25 to %0*
%27 = bitcast %0* %26 to %TSo12UnitDurationC*
%.asUnsubstituted6 = bitcast %TSo12UnitDurationC* %27 to %TSo9DimensionC*
%.asSubstituted.asUnsubstituted = bitcast %TSo12UnitDurationC* %.asSubstituted to %TSo9DimensionC*
%28 = call swiftcc { %TSo9DimensionC*, double } @_T010Foundation11MeasurementVAASo9DimensionCRbzlE9convertedACyxGx2to_tF(%TSo9DimensionC* %.asUnsubstituted6, %TSo9DimensionC* %.asSubstituted.asUnsubstituted, double %15, %swift.type* %0)
%29 = extractvalue { %TSo9DimensionC*, double } %28, 0
%30 = extractvalue { %TSo9DimensionC*, double } %28, 1
%.asSubstituted7 = bitcast %TSo9DimensionC* %29 to %TSo12UnitDurationC*
call void bitcast (void (%objc_object*)* @objc_release to void (%TSo12UnitDurationC*)*)(%TSo12UnitDurationC* %.asSubstituted) #4
call void bitcast (void (%objc_object*)* @objc_release to void (%TSo12UnitDurationC*)*)(%TSo12UnitDurationC* %.asSubstituted7) #4
ret void
}

declare swiftcc { %TSo9DimensionC*, double } @_T010Foundation11MeasurementVAASo9DimensionCRbzlE9convertedACyxGx2to_tF(%TSo9DimensionC*, %TSo9DimensionC*, double, %swift.type*) #0

declare swiftcc { %TSo4UnitC*, double } @_T010Foundation11MeasurementVACyxGSd5value_x4unittcfC(double, %TSo4UnitC*, %swift.type*) #0

define linkonce_odr hidden %swift.type* @_T0So12UnitDurationCMa() #1 {
entry:
%0 = load %swift.type*, %swift.type** @_T0So12UnitDurationCML, align 8
%1 = icmp eq %swift.type* %0, null
br i1 %1, label %cacheIsNull, label %cont

cacheIsNull: ; preds = %entry
%2 = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_\$_NSUnitDuration", align 8
%3 = call %objc_class* @swift_rt_swift_getInitializedObjCClass(%objc_class* %2)
%4 = call %swift.type* @swift_getObjCClassMetadata(%objc_class* %3) #3
store atomic %swift.type* %4, %swift.type** @_T0So12UnitDurationCML release, align 8
br label %cont

cont: ; preds = %cacheIsNull, %entry
%5 = phi %swift.type* [ %0, %entry ], [ %4, %cacheIsNull ]
ret %swift.type* %5
}

; Function Attrs: noinline nounwind
define linkonce_odr hidden %objc_class* @swift_rt_swift_getInitializedObjCClass(%objc_class*) #2 {
entry:
%1 = tail call %objc_class* %load(%objc_class* %0)
ret %objc_class* %1
}

declare void @objc_msgSend()

declare i8* @objc_retainAutoreleasedReturnValue(i8*)

declare void @objc_release(%objc_object*)

… which, personally, I think is just excessive. Maybe for complex conversions it’s worth it, but I’m not sure it is for simple conversions.

- Karl

···

On 14. Jan 2018, at 06:04, BJ Homer via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I do a lot of geometry and spherical-related work and i have never found an Angle type to be worth having. Always use radians. It’s what sin() and cos() take, it’s what graphics APIs like Cairo expect, it’s what graphics formats like SVG use. plus, do you *really* want to be case-branching on every angle value? that really adds up when you’re converting 100,000s of lat-long pairs to cartesian.

You can do it without case-branching. I too have an Angle type; this is what I use:

public struct Angle<T: FloatingPoint> {
public var degrees: T {
return (radians / .pi) * 180
}

}
public static func degrees(_ degs: T) -> Angle {
return Angle(radians: (degs / 180) * .pi)
}
}

If you ask for “radians” (like most low-level trig code will), you just get the stored property. The conversion “overhead” is only done at construction time, so it makes a convenient parameter/return value.

- Karl

···

On 14. Jan 2018, at 09:51, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 14, 2018, at 12:04 AM, BJ Homer via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This could work, but you’re also giving up all the nice Numeric and
FloatingPoint conformances when you use this, all of a sudden adding two
angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians +
β.radians). just no. at the risk of blowing up the scope of this idea,
dedicated Angle types also begs for generic trigonometric functions like
Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even
tried implementing it but fast trig evaluation doesn’t genericize well
since it relies a lot on rsqrt-style magic constants

also, why are radians(_ and degrees(_ static functions? i really only
use static constructors for initializers that have side effects

···

On Sun, Jan 14, 2018 at 6:36 AM, Karl Wagner <razielim@gmail.com> wrote:

On 14. Jan 2018, at 09:51, Taylor Swift via swift-evolution < > swift-evolution@swift.org> wrote:

I do a lot of geometry and spherical-related work and i have never found
an Angle type to be worth having. Always use radians. It’s what sin() and
cos() take, it’s what graphics APIs like Cairo expect, it’s what graphics
formats like SVG use. plus, do you *really* want to be case-branching on
every angle value? that really adds up when you’re converting 100,000s of
lat-long pairs to cartesian.

You can do it without case-branching. I too have an Angle type; this is
what I use:

public struct Angle<T: FloatingPoint> {
public var degrees: T {
return (radians / .pi) * 180
}

}
public static func degrees(_ degs: T) -> Angle {
return Angle(radians: (degs / 180) * .pi)
}
}

If you ask for “radians” (like most low-level trig code will), you just
get the stored property. The conversion “overhead” is only done at
construction time, so it makes a convenient parameter/return value.

- Karl

On Jan 14, 2018, at 12:04 AM, BJ Homer via swift-evolution < > swift-evolution@swift.org> wrote:

An Angle type already exists in Foundation; see Measurement<UnitAngle>.
You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

I would like to see a full Geometry implementation but I don't think it
should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Evolution,

I would *really* like to see Swift gain an Angle type in the standard
library. Every time I have to deal with an angle in an api, I have to go
figure out the conventions for that call. Is it in degrees? Is it in

I ended up writing an Angle type for my own code a few years back, and I
have to say it is really wonderful. It has greatly simplified my graphics
work. It takes a lot of mental load off of my brain when dealing with
Angles.

I can of course initialize it either as degrees or radians (or
revolutions), but I can also just say things like ‘.threeQuarterTurn’, and
then I can get the value back out in whatever format I like. There are
also useful additions that let me normalize the angle to different ranges
and which let me snap to the nearest multiple of an angle. Both of these
are enormously useful for user facing features. I can also do math on
angles in a way that makes geometric sense for angles. It is also really
useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just
semantically wrong IMHO, and it stops us from adding all of these
angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This could work, but you’re also giving up all the nice Numeric and FloatingPoint conformances when you use this, all of a sudden adding two angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians + β.radians). just no. at the risk of blowing up the scope of this idea, dedicated Angle types also begs for generic trigonometric functions like Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even tried implementing it but fast trig evaluation doesn’t genericize well since it relies a lot on rsqrt-style magic constants

You could add those conformances back, if you wanted. Most low-level trig code will quickly escape the wrapper once it starts using the values. Mostly I use it as a currency type and for converting untyped angles. I think it’s a great example of Swift’s zero-cost abstractions - we added semantic meaning (this value isn’t just a number with a specific bit representation, it’s a number which represents a specific kind of quantity), and encapsulated some useful functionality, and we don't lose any performance.

I have a couple of these, such as Distance<T> and Time (wraps a TimeInterval). This allows me to write algorithms like:

public func point(_ distance: Distance<Double>, along bearing: Angle<Double>) -> Geo.Point

And use it like this:

let nextPlace = thisPlace.point(.kilometers(42), along: .degrees(45))

Or this:

let nextPlace = thisPlace.point(.miles(500), along: .radians(.pi/2))

And so my algorithm actually becomes quite difficult to use incorrectly. Also, that’s why I use static constructors; similar to how lots of OptionSet types are implemented, doing it this way lets you use an enum-like syntax to create values, which fits Angle’s intended use as a parameter/return type.

I’m not saying this should be part of the standard library (this isn’t my pitch), I’m just saying these kind of wrappers are useful when creating good APIs. I think somebody was dismissing the idea of an Angle<T> type in general before.

- Karl

···

On 14. Jan 2018, at 21:12, Kelvin Ma via swift-evolution <swift-evolution@swift.org> wrote:

also, why are radians(_ and degrees(_ static functions? i really only use static constructors for initializers that have side effects

On Sun, Jan 14, 2018 at 6:36 AM, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 14. Jan 2018, at 09:51, Taylor Swift via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I do a lot of geometry and spherical-related work and i have never found an Angle type to be worth having. Always use radians. It’s what sin() and cos() take, it’s what graphics APIs like Cairo expect, it’s what graphics formats like SVG use. plus, do you *really* want to be case-branching on every angle value? that really adds up when you’re converting 100,000s of lat-long pairs to cartesian.

You can do it without case-branching. I too have an Angle type; this is what I use:

public struct Angle<T: FloatingPoint> {
public var degrees: T {
return (radians / .pi) * 180
}

}
public static func degrees(_ degs: T) -> Angle {
return Angle(radians: (degs / 180) * .pi)
}
}

If you ask for “radians” (like most low-level trig code will), you just get the stored property. The conversion “overhead” is only done at construction time, so it makes a convenient parameter/return value.

- Karl

On Jan 14, 2018, at 12:04 AM, BJ Homer via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

And in fact you probably don’t want that conformance. It makes sense dimensionally to add two angles. It doesn’t (generally) make as much sense to multiply them, or to take the square root of an angle. I would expect an angle type to provide + and - but probably not * and / and most of the other floating-point operations.

– Steve

···

On Jan 15, 2018, at 4:31 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 14. Jan 2018, at 21:12, Kelvin Ma via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This could work, but you’re also giving up all the nice Numeric and FloatingPoint conformances when you use this, all of a sudden adding two angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians + β.radians). just no. at the risk of blowing up the scope of this idea, dedicated Angle types also begs for generic trigonometric functions like Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even tried implementing it but fast trig evaluation doesn’t genericize well since it relies a lot on rsqrt-style magic constants

You could add those conformances back, if you wanted. Most low-level trig code will quickly escape the wrapper once it starts using the values. Mostly I use it as a currency type and for converting untyped angles. I think it’s a great example of Swift’s zero-cost abstractions - we added semantic meaning (this value isn’t just a number with a specific bit representation, it’s a number which represents a specific kind of quantity), and encapsulated some useful functionality, and we don't lose any performance.

This could work, but you’re also giving up all the nice Numeric and FloatingPoint conformances when you use this, all of a sudden adding two angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians + β.radians). just no. at the risk of blowing up the scope of this idea, dedicated Angle types also begs for generic trigonometric functions like Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even tried implementing it but fast trig evaluation doesn’t genericize well since it relies a lot on rsqrt-style magic constants

You could add those conformances back, if you wanted. Most low-level trig code will quickly escape the wrapper once it starts using the values. Mostly I use it as a currency type and for converting untyped angles. I think it’s a great example of Swift’s zero-cost abstractions - we added semantic meaning (this value isn’t just a number with a specific bit representation, it’s a number which represents a specific kind of quantity), and encapsulated some useful functionality, and we don't lose any performance.

I have a couple of these, such as Distance<T> and Time (wraps a TimeInterval). This allows me to write algorithms like:

public func point(_ distance: Distance<Double>, along bearing: Angle<Double>) -> Geo.Point

And use it like this:

let nextPlace = thisPlace.point(.kilometers(42), along: .degrees(45))

Or this:

let nextPlace = thisPlace.point(.miles(500), along: .radians(.pi/2))

And so my algorithm actually becomes quite difficult to use incorrectly. Also, that’s why I use static constructors; similar to how lots of OptionSet types are implemented, doing it this way lets you use an enum-like syntax to create values, which fits Angle’s intended use as a parameter/return type.

I’m not saying this should be part of the standard library (this isn’t my pitch), I’m just saying these kind of wrappers are useful when creating good APIs. I think somebody was dismissing the idea of an Angle<T> type in general before.

- Karl

i thought the whole thing with resiliency barriers is that these kinds of abstractions are *not* zero cost anymore, accessing a member of an imported struct is always going to be through an indirect getter and setter. && this doesn’t get fixed by slava’s inlineable thing because you can’t “inline” a struct layout

···

On Jan 15, 2018, at 3:30 PM, Karl Wagner <razielim@gmail.com> wrote:

On 14. Jan 2018, at 21:12, Kelvin Ma via swift-evolution <swift-evolution@swift.org> wrote:

also, why are radians(_ and degrees(_ static functions? i really only use static constructors for initializers that have side effects

On Sun, Jan 14, 2018 at 6:36 AM, Karl Wagner <razielim@gmail.com> wrote:

On 14. Jan 2018, at 09:51, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

I do a lot of geometry and spherical-related work and i have never found an Angle type to be worth having. Always use radians. It’s what sin() and cos() take, it’s what graphics APIs like Cairo expect, it’s what graphics formats like SVG use. plus, do you *really* want to be case-branching on every angle value? that really adds up when you’re converting 100,000s of lat-long pairs to cartesian.

You can do it without case-branching. I too have an Angle type; this is what I use:

public struct Angle<T: FloatingPoint> {
public var degrees: T {
return (radians / .pi) * 180
}

}
public static func degrees(_ degs: T) -> Angle {
return Angle(radians: (degs / 180) * .pi)
}
}

If you ask for “radians” (like most low-level trig code will), you just get the stored property. The conversion “overhead” is only done at construction time, so it makes a convenient parameter/return value.

- Karl

On Jan 14, 2018, at 12:04 AM, BJ Homer via swift-evolution <swift-evolution@swift.org> wrote:

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

···

On Jan 15, 2018, at 3:40 PM, Stephen Canon <scanon@apple.com> wrote:

On Jan 15, 2018, at 4:31 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 14. Jan 2018, at 21:12, Kelvin Ma via swift-evolution <swift-evolution@swift.org> wrote:

This could work, but you’re also giving up all the nice Numeric and FloatingPoint conformances when you use this, all of a sudden adding two angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians + β.radians). just no. at the risk of blowing up the scope of this idea, dedicated Angle types also begs for generic trigonometric functions like Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even tried implementing it but fast trig evaluation doesn’t genericize well since it relies a lot on rsqrt-style magic constants

You could add those conformances back, if you wanted. Most low-level trig code will quickly escape the wrapper once it starts using the values. Mostly I use it as a currency type and for converting untyped angles. I think it’s a great example of Swift’s zero-cost abstractions - we added semantic meaning (this value isn’t just a number with a specific bit representation, it’s a number which represents a specific kind of quantity), and encapsulated some useful functionality, and we don't lose any performance.

And in fact you probably don’t want that conformance. It makes sense dimensionally to add two angles. It doesn’t (generally) make as much sense to multiply them, or to take the square root of an angle. I would expect an angle type to provide + and - but probably not * and / and most of the other floating-point operations.

– Steve

1 Like

This is getting way off topic for Evolution but I'd like to point out that `angle * factor` and `angle / factor ` both make sense. Maybe we should take this conversation over to some kind of ad-hoc Swift Geometry forum?

-- E

···

On Jan 15, 2018, at 2:40 PM, Stephen Canon via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 15, 2018, at 4:31 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 14. Jan 2018, at 21:12, Kelvin Ma via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This could work, but you’re also giving up all the nice Numeric and FloatingPoint conformances when you use this, all of a sudden adding two angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians + β.radians). just no. at the risk of blowing up the scope of this idea, dedicated Angle types also begs for generic trigonometric functions like Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even tried implementing it but fast trig evaluation doesn’t genericize well since it relies a lot on rsqrt-style magic constants

You could add those conformances back, if you wanted. Most low-level trig code will quickly escape the wrapper once it starts using the values. Mostly I use it as a currency type and for converting untyped angles. I think it’s a great example of Swift’s zero-cost abstractions - we added semantic meaning (this value isn’t just a number with a specific bit representation, it’s a number which represents a specific kind of quantity), and encapsulated some useful functionality, and we don't lose any performance.

And in fact you probably don’t want that conformance. It makes sense dimensionally to add two angles. It doesn’t (generally) make as much sense to multiply them, or to take the square root of an angle. I would expect an angle type to provide + and - but probably not * and / and most of the other floating-point operations.

– Steve

This could work, but you’re also giving up all the nice Numeric and FloatingPoint conformances when you use this, all of a sudden adding two angles together isn’t let γ = α + β, it’s γ = Angle.radians(α.radians + β.radians). just no. at the risk of blowing up the scope of this idea, dedicated Angle types also begs for generic trigonometric functions like Angle.sin(_ and Angle.cos(_:). i proposed that a while back and even tried implementing it but fast trig evaluation doesn’t genericize well since it relies a lot on rsqrt-style magic constants

You could add those conformances back, if you wanted. Most low-level trig code will quickly escape the wrapper once it starts using the values. Mostly I use it as a currency type and for converting untyped angles. I think it’s a great example of Swift’s zero-cost abstractions - we added semantic meaning (this value isn’t just a number with a specific bit representation, it’s a number which represents a specific kind of quantity), and encapsulated some useful functionality, and we don't lose any performance.

I have a couple of these, such as Distance<T> and Time (wraps a TimeInterval). This allows me to write algorithms like:

public func point(_ distance: Distance<Double>, along bearing: Angle<Double>) -> Geo.Point

And use it like this:

let nextPlace = thisPlace.point(.kilometers(42), along: .degrees(45))

Or this:

let nextPlace = thisPlace.point(.miles(500), along: .radians(.pi/2))

And so my algorithm actually becomes quite difficult to use incorrectly. Also, that’s why I use static constructors; similar to how lots of OptionSet types are implemented, doing it this way lets you use an enum-like syntax to create values, which fits Angle’s intended use as a parameter/return type.

I’m not saying this should be part of the standard library (this isn’t my pitch), I’m just saying these kind of wrappers are useful when creating good APIs. I think somebody was dismissing the idea of an Angle<T> type in general before.

- Karl

i thought the whole thing with resiliency barriers is that these kinds of abstractions are *not* zero cost anymore, accessing a member of an imported struct is always going to be through an indirect getter and setter. && this doesn’t get fixed by slava’s inlineable thing because you can’t “inline” a struct layout

This is what the @fixed_layout (or @frozen, or whatever it ends up being called) attribute is for. Resilience means that we have built-in indirection to allow for library evolution, but the design is that you can opt-out of that indirection and expose cross-module inlining or other optimisation opportunities.

https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#fixed-contents-structs

- Karl

···

On 15. Jan 2018, at 23:55, Taylor Swift <kelvin13ma@gmail.com> wrote:
On Jan 15, 2018, at 3:30 PM, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 14. Jan 2018, at 21:12, Kelvin Ma via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

also, why are radians(_ and degrees(_ static functions? i really only use static constructors for initializers that have side effects

On Sun, Jan 14, 2018 at 6:36 AM, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 14. Jan 2018, at 09:51, Taylor Swift via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I do a lot of geometry and spherical-related work and i have never found an Angle type to be worth having. Always use radians. It’s what sin() and cos() take, it’s what graphics APIs like Cairo expect, it’s what graphics formats like SVG use. plus, do you *really* want to be case-branching on every angle value? that really adds up when you’re converting 100,000s of lat-long pairs to cartesian.

You can do it without case-branching. I too have an Angle type; this is what I use:

public struct Angle<T: FloatingPoint> {
public var degrees: T {
return (radians / .pi) * 180
}

}
public static func degrees(_ degs: T) -> Angle {
return Angle(radians: (degs / 180) * .pi)
}
}

If you ask for “radians” (like most low-level trig code will), you just get the stored property. The conversion “overhead” is only done at construction time, so it makes a convenient parameter/return value.

- Karl

On Jan 14, 2018, at 12:04 AM, BJ Homer via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

An Angle type already exists in Foundation; see Measurement<UnitAngle>. You could add some convenience methods in an extension pretty easily.

import Foundation

typealias Angle = Measurement<UnitAngle>

extension Measurement where UnitType == UnitAngle {
var sine: Double {
}

static var threeQuarterTurn: Angle {
return Angle(value: 0.75, unit: .revolutions)
}
}

let x = Angle.threeQuarterTurn
x.sine // -1

-BJ

On Jan 13, 2018, at 9:31 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I would like to see a full Geometry implementation but I don't think it should be part of the standard library.

I've kicked around some ideas here:

and

On Jan 13, 2018, at 7:49 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Evolution,

I would really like to see Swift gain an Angle type in the standard library. Every time I have to deal with an angle in an api, I have to go figure out the conventions for that call. Is it in degrees? Is it in radians? What if it is in radians, but I want to think about it in degrees?

I ended up writing an Angle type for my own code a few years back, and I have to say it is really wonderful. It has greatly simplified my graphics work. It takes a lot of mental load off of my brain when dealing with Angles.

I can of course initialize it either as degrees or radians (or revolutions), but I can also just say things like ‘.threeQuarterTurn’, and then I can get the value back out in whatever format I like. There are also useful additions that let me normalize the angle to different ranges and which let me snap to the nearest multiple of an angle. Both of these are enormously useful for user facing features. I can also do math on angles in a way that makes geometric sense for angles. It is also really useful for interacting with CGVectors in intelligent ways.

Using Doubles or CGFloats to represent angles everywhere is just semantically wrong IMHO, and it stops us from adding all of these angle-specific niceties.

Happy to provide code if there is interest…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Slightly off topic, Could somebody please explain to me why the trig functions are part of `Darwin` and `Glibc` and not part of the standard library?

They’re such essential functions, why aren’t they standardized?!

Because there is no math library for Swift yet. Those functions are coming from the C math library, which on Linux is part of glibc and on Apple platforms is part of libSystem, which is imported into Swift as the Darwin module.

Why is that an issue? Object and existential container allocation uses `malloc`, and that’s a C function too. We could wrap `malloc`, why not `sin`?

swift doesn’t actually wrap malloc Builtin calls it internally but a swift-specific allocator is planned

Kelvin was working on pure-Swift versions. I don't know what he plans to do with it (if anything); maybe he'd welcome you expanding on that code.

As it says in the thread, the core team would prefer to keep the standard library minimal, and for such functions to evolve in 3rd-party open-source libraries. Once the community settles on a defacto "standard" library for something like trig, we can talk about absorbing that functionality and design in to the language-wide standard library.

1 Like

i’m an advocate for multiple standard libraries which is pretty much the same idea.

evaluating trig functions with conventional approximations is way too slow to get the kind of accuracy Glibc.sin(_ has and if you look at the source for that function or the Darwin implementation it uses a bunch of magic constants and you can’t really generalize magic constants to a generic type. if you read the thread Steve Canon thinks it can be done though, i’d tag him but idk his username on here though.

@Nicole_Jacque can you please turn off the emoji detection thing every function name is turning into an emoji because of the _ syntax

1 Like