(Playground and Workbook attached)
let xform = CGAffineTransform(translationX: 1, y: 2).scaledBy(x: 3, y: 4)
debugPrint(xform)
The result is __C.CGAffineTransform(a: 3.0, b: 0.0, c: 0.0, d: 4.0, tx: 1.0, ty: 2.0)
Open Xamarin Workbooks
var xlatAndScale = CGAffineTransform.MakeTranslation(1, 2);
xlatAndScale.Scale(3, 4);
xlatAndScale
"Like Swift" behavior would produce xx:3.0 yx:0.0 xy:0.0 yy:4.0 x0:1.0 y0:2.0
"xx:3.0 yx:0.0 xy:0.0 yy:4.0 x0:3.0 y0:8.0"
It's clear what we're doing, but if this is what we want to do, I'd suggest another method that is `MakeTranslatedAndScaled(CGPoint translation, CGFloat scaleX, CGFloat scaleY) that just sets the values directly.
An added complexity is that the Swift code :
let xformR = CGAffineTransform(translationX: 1, y: 2).scaledBy(x: 3, y: 4).rotated(by: 3.1417 / 4)
produces C.CGAffineTransform(a: 2.121263413764814, b: 2.828503029102253, c: -2.12137727182669, d: 2.828351218353085, tx: 1.0, ty: 2.0)
Set the scale first and then set the translation:
var scaleAndXlat = CGAffineTransform.MakeScale(3, 4);
scaleAndXlat.Translate(1, 2);
scaleAndXlat
Results in: "xx:3.0 yx:0.0 xy:0.0 yy:4.0 x0:1.0 y0:2.0"
=== Visual Studio Community 2017 for Mac (Preview) ===
Version 7.6 Preview (7.6 build 1773)
Installation UUID: 6b94f136-026d-4a5a-bf6d-af2c0d8dc019
Runtime:
Mono 5.12.0.273 (2018-02/f59eac4c0f1) (64-bit)
GTK+ 2.24.23 (Raleigh theme)
Xamarin.Mac 4.4.1.178 (master / eeaeb7e6)
Package version: 512000273
=== NuGet ===
Version: 4.3.1.4445
=== .NET Core ===
Runtime: /usr/local/share/dotnet/dotnet
Runtime Versions:
2.1.1
2.0.5
2.0.3
2.0.0
SDK: /usr/local/share/dotnet/sdk/2.1.301/Sdks
SDK Versions:
2.1.301
2.1.4
2.0.3
2.0.0
MSBuild SDKs: /Library/Frameworks/Mono.framework/Versions/5.12.0/lib/mono/msbuild/15.0/bin/Sdks
=== Xamarin.Profiler ===
Version: 1.6.3
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler
=== Xamarin.Android ===
Version: 8.4.0.1 (Visual Studio Community)
Android SDK: /Users/larryobrien/Library/Android/sdk
Supported Android versions:
4.1 (API level 16)
5.0 (API level 21)
6.0 (API level 23)
7.0 (API level 24)
7.1 (API level 25)
8.0 (API level 26)
8.1 (API level 27)
SDK Tools Version: 26.1.1
SDK Platform Tools Version: 25.0.3
SDK Build Tools Version: 27.0.3
Java SDK: /usr
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL
=== Xamarin Inspector ===
Version: 1.4.0
Hash: b3f92f9
Branch: master
Build date: Fri, 19 Jan 2018 22:00:34 GMT
Client compatibility: 1
=== Apple Developer Tools ===
Xcode 10.0 (14313.2)
Build 10L213o
=== Xamarin.Mac ===
Version: 4.99.3.344 (Visual Studio Community)
Hash: 0b726519
Branch:
Build date: 2018-08-09 10:37:58-0400
=== Xamarin.iOS ===
Version: 11.99.3.224 (Visual Studio Community)
Hash: 0b726519
Branch: HEAD
Build date: 2018-08-09 10:37:57-0400
=== Build Information ===
Release ID: 706001773
Git revision: e5958ff9015312c81fc3b1b6b6413585575ca869
Build date: 2018-06-19 12:19:27+00
Build branch: release-7.6
Xamarin extensions: 6910f36b43e7d559ff40ae268e73ae26c30a2a6b
=== Operating System ===
Mac OS X 10.13.6
Darwin 17.7.0 Darwin Kernel Version 17.7.0
Thu Jun 21 22:53:14 PDT 2018
root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
=== Enabled user installed extensions ===
AddinMaker 1.4.2
Larry's F# Templates 2.0.8
MicroFramework 1.0.3
Internet of Things (IoT) development (Preview) 7.5.3
I could confirm the different results given by the C# and Swift test cases. Investigating this a bit further.
Ah ok our scale is incorrect (not doing the same as the native version at least).
The native scaleBy code has a comment that indicates what it's doing: t' = [ sx 0 0 sy 0 0 ] * t
We do t' = t * [ sx 0 0 sy 0 0 ]
See CGAffineTransform.Scale where t is a (here) and [ sx 0 0 sy 0 0 ] or MakeScale is b (passed as argument here).
Matrices are non-commutative so our code isn't equivalent to the native operation.
We need:
C#
public void Scale (nfloat sx, nfloat sy)
{
this = Multiply (MakeScale (sx, sy), this);
}
My suggestion is to add a new function called ScaleBy that mimics Swift's behavior.
public void ScaleBy (nfloat sx, nfloat sy)
{
this = Multiply (MakeScale (sx, sy), this);
}
And additional document it:
t' = t * [ sx 0 0 sy 0 0 ], which is not equivalent to Swift's scaleBy:. Use CGAffineTransform.ScaleBy in that case"t' = [ sx 0 0 sy 0 0 ] * t, which is equivalent to Swift's scaleBy:.I like that idea. Otherwise Sebastien suggested we'd copy System.Drawing. So if we look at https://docs.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.matrixorder?view=netframework-4.7.2.
We'd do something like:
```C#
public enum MultiplyOrder {
Prepend = 0,
Append = 1,
}
public void Scale (nfloat sx, nfloat sy) {
Scale (sx, sy, MultiplyOrder.Append);
}
public void Scale (nfloat sx, nfloat sy, MultiplyOrder order)
{
switch (order) {
case MultiplyOrder.Append: // The new operation is applied after the old operation.
this = Multiply (this, MakeScale (sx, sy)); // t' = t * [ sx 0 0 sy 0 0 ]
break;
case MultiplyOrder.Prepend: // The new operation is applied before the old operation.
default:
this = Multiply (MakeScale (sx, sy), this); // t' = [ sx 0 0 sy 0 0 ] * t – Swift equivalent
break;
}
}
```
I like the System.Drawing approach.
@VincentDondain yes, that's how we should expose both versions. However better use MatrixOrder (like .NET) since we're sharing the same API _pattern_
We could also add [Advice] on the existing API that mention the default order.
Using an enum (MatrixOrder) looks good to me too, although in the implementation I'd throw ArgumentOutOfRangeException in the default case (instead of treating it as .Prepend).
Most helpful comment
My suggestion is to add a new function called
ScaleBythat mimics Swift's behavior.And additional document it:
t' = t * [ sx 0 0 sy 0 0 ], which is not equivalent to Swift'sscaleBy:. Use CGAffineTransform.ScaleBy in that case"t' = [ sx 0 0 sy 0 0 ] * t, which is equivalent to Swift'sscaleBy:.