Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[Bug] Correct the transformation calculation of views (#11933)
Browse files Browse the repository at this point in the history
* Added the test case
* Do not scale the translation
* Fix the ordering of the scale/rotate/translate matrices

Co-authored-by: Kangho Hur <rookiejava@gmail.com>
  • Loading branch information
mattleibow and rookiejava authored Nov 18, 2020
1 parent bef121f commit 36741b9
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<controls:TestContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue11931"
Title="Issue 11931">

<controls:TestContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="HorizontalOptions" Value="Start" />
<Setter Property="WidthRequest" Value="100" />
<Setter Property="BackgroundColor" Value="#EE888888" />
<Setter Property="FontSize" Value="12" />
<Setter Property="HeightRequest" Value="32" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Rotation" Value="{Binding Value, Source={x:Reference rotation}}" />
</Style>
<Style TargetType="BoxView">
<Setter Property="VerticalOptions" Value="Fill" />
<Setter Property="HorizontalOptions" Value="Start" />
<Setter Property="WidthRequest" Value="1" />
<Setter Property="Color" Value="LightGray" />
</Style>
<Style TargetType="Label">
<Setter Property="HorizontalOptions" Value="Start" />
</Style>
</ResourceDictionary>
</controls:TestContentPage.Resources>

<StackLayout>
<Label Padding="12" BackgroundColor="Black" TextColor="White"
HorizontalOptions="Fill"
Text="The buttons should completely cover the bright red boxes" />

<Slider x:Name="rotation" Minimum="-180" Maximum="180" />

<ScrollView VerticalOptions="FillAndExpand" Orientation="Both">
<Grid Margin="0" Padding="55,5,5,5">
<BoxView Margin="0,0,0,0" />
<BoxView Margin="50,0,0,0" />
<BoxView Margin="100,0,0,0" Color="Black" />
<BoxView Margin="150,0,0,0" />
<BoxView Margin="200,0,0,0" Color="Black" WidthRequest="3" />
<BoxView Margin="250,0,0,0" />
<BoxView Margin="300,0,0,0" Color="Black" />
<BoxView Margin="350,0,0,0" />

<Grid>
<Label Text="0" />
<Label Text="50" Margin="50,0,0,0" />
<Label Text="100" Margin="100,0,0,0" />
<Label Text="150" Margin="150,0,0,0" />
<Label Text="200" Margin="200,0,0,0" />
<Label Text="250" Margin="250,0,0,0" />
<Label Text="300" Margin="300,0,0,0" />
<Label Text="350" Margin="350,0,0,0" />
</Grid>

<Grid Margin="0,30,0,0">
<StackLayout Spacing="6">
<Label Text=" " />

<BoxView Color="Red" Margin="0,0,0,0" WidthRequest="100" HeightRequest="32" />
<BoxView Color="Red" Margin="-50,0,0,0" WidthRequest="200" HeightRequest="32" />
<BoxView Color="Red" Margin="0,0,0,0" WidthRequest="200" HeightRequest="32" />

<Label Text=" " />

<BoxView Color="Red" Margin="200,0,0,0" WidthRequest="100" HeightRequest="32" />
<BoxView Color="Red" Margin="150,0,0,0" WidthRequest="200" HeightRequest="32" />
<BoxView Color="Red" Margin="200,0,0,0" WidthRequest="200" HeightRequest="32" />

<Label Text=" " />

<StackLayout Spacing="16" Margin="0,8,0,0">
<BoxView Color="Red" Margin="50,0,0,0" WidthRequest="400" HeightRequest="64" />
<BoxView Color="Red" Margin="200,0,0,0" WidthRequest="400" HeightRequest="64" />
</StackLayout>
</StackLayout>

<StackLayout Spacing="6">
<Label Text="simple scale X" BackgroundColor="#AAFFFFFF" />

<Button Text="default" ScaleX="1" TranslationX="0" />
<Button Text="scale 2x" ScaleX="2" TranslationX="0" />
<Button Text="left scale 2x" ScaleX="2" TranslationX="0" AnchorX="0" />

<Label Text="Scale X then translate X to 200" BackgroundColor="#AAFFFFFF" />

<Button Text="default" ScaleX="1" TranslationX="200" />
<Button Text="scale 2x" ScaleX="2" TranslationX="200" />
<Button Text="left scale 2x" ScaleX="2" TranslationX="200" AnchorX="0" />

<Label Text="Scale 2x then scale X then translate to 200" BackgroundColor="#AAFFFFFF" />

<StackLayout Spacing="48" Margin="0,24,0,0">
<Button Text="scale 2x" Scale="2" ScaleX="2" TranslationX="200" />
<Button Text="left scale 2x" Scale="2" ScaleX="2" TranslationX="200" AnchorX="0" />
</StackLayout>
</StackLayout>
</Grid>

</Grid>
</ScrollView>
</StackLayout>

</controls:TestContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Xamarin.Forms.Internals;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Shapes;
using System.Collections.Generic;

#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.ViewBaseTests)]
#endif
[Preserve(AllMembers = true)]
[Issue(
IssueTracker.Github, 11931,
"[Bug] View translation is incorrectly calculated",
PlatformAffected.All)]
public partial class Issue11931: TestContentPage
{
public Issue11931()
{
#if APP
InitializeComponent();
#endif
}

protected override void Init()
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue11962.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10744.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10909.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11931.xaml.cs">
<DependentUpon>Issue11931.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue11709.xaml.cs">
<DependentUpon>Issue11709.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -1644,6 +1647,9 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue10672.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue11931.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue1497.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
Expand Down
41 changes: 41 additions & 0 deletions Xamarin.Forms.Platform.Android.UnitTests/TransformationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Collections;
using System.Threading.Tasks;
using NUnit.Framework;
using NUnit.Framework.Internal;

namespace Xamarin.Forms.Platform.Android.UnitTests
{
[TestFixture]
public class TransformationTests : PlatformTestFixture
{
static IEnumerable TransformationCases
{
get
{
foreach (var element in BasicElements)
{
element.TranslationX = 10.0;
element.TranslationY = 30.0;
element.Rotation = 248.0;
element.Scale = 2.0;
element.ScaleX = 2.0;
yield return CreateTestCase(element);
}
}
}

[Test, Category("Transformation"), TestCaseSource(nameof(TransformationCases))]
[Description("View transformation should match renderer transformation")]
public async Task TransformationConsistent(View element)
{
var expected = (Context.ToPixels(10d), Context.ToPixels(30d), 248d, 4d, 2d);
var actual = await GetRendererProperty(element, ver =>
{
var v = ver.View;
return (v.TranslationX, v.TranslationY, v.Rotation, v.ScaleX, v.ScaleY);
}, requiresParent: true);

Assert.That(actual, Is.EqualTo(expected));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<Compile Include="ButtonTests.cs" />
<Compile Include="BackgroundTests.cs" />
<Compile Include="TestActivity.cs" />
<Compile Include="TransformationTests.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NUnit">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1111,8 +1111,8 @@ protected virtual void ApplyTransformation()
map.PopulatePoints(geometry, 0);

bool changed = false;
ApplyRotation(map, geometry, ref changed);
ApplyScale(map, geometry, ref changed);
ApplyRotation(map, geometry, ref changed);
ApplyTranslation(map, geometry, ref changed);

NativeView.IsMapEnabled = changed;
Expand Down
50 changes: 50 additions & 0 deletions Xamarin.Forms.Platform.UAP.UnitTests/TransformationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections;
using System.Threading.Tasks;
using NUnit.Framework;
using Windows.UI.Xaml;
using WCompositeTransform = Windows.UI.Xaml.Media.CompositeTransform;

namespace Xamarin.Forms.Platform.UAP.UnitTests
{
[TestFixture]
public class TransformationTests : PlatformTestFixture
{
static IEnumerable TransformationCases
{
get
{
foreach (var element in BasicViews)
{
element.TranslationX = 10.0;
element.TranslationY = 30.0;
element.Rotation = 248.0;
element.Scale = 2.0;
element.ScaleX = 2.0;
yield return CreateTestCase(element);
}
}
}

[Test, Category("Transformation"), TestCaseSource(nameof(TransformationCases))]
[Description("View transformation should match renderer transformation")]
public async Task TransformationConsistent(View view)
{
var expected = (10d, 30d, 248d, 4d, 2d);
var actual = await GetRendererProperty(view, ver =>
{
var t = GetTransform(ver.ContainerElement);
return (t.TranslateX, t.TranslateY, t.Rotation, t.ScaleX, t.ScaleY);
});
Assert.That(actual, Is.EqualTo(expected));
}

WCompositeTransform GetTransform(FrameworkElement fe)
{
if (fe.RenderTransform is WCompositeTransform transform)
return transform;

throw new Exception("No rotation found");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
<Compile Include="ScaleTests.cs" />
<Compile Include="ScrollBarVisibilityTests.cs" />
<Compile Include="ShellTests.cs" />
<Compile Include="TransformationTests.cs" />
<EmbeddedResource Include="Properties\Xamarin.Forms.Platform.UAP.UnitTests.rd.xml" />
</ItemGroup>
<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions Xamarin.Forms.Platform.UAP/VisualElementTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,8 @@ static void UpdateRotation(VisualElement view, FrameworkElement frameworkElement
{
CenterOfRotationX = anchorX,
CenterOfRotationY = anchorY,
GlobalOffsetX = scaleX == 0 ? 0 : translationX / scaleX,
GlobalOffsetY = scaleY == 0 ? 0 : translationY / scaleY,
GlobalOffsetX = translationX,
GlobalOffsetY = translationY,
RotationX = -rotationX,
RotationY = -rotationY,
RotationZ = -rotation
Expand All @@ -760,8 +760,8 @@ static void UpdateRotation(VisualElement view, FrameworkElement frameworkElement
Rotation = rotation,
ScaleX = scaleX,
ScaleY = scaleY,
TranslateX = scaleX == 0 ? 0 : translationX / scaleX,
TranslateY = scaleY == 0 ? 0 : translationY / scaleY
TranslateX = translationX,
TranslateY = translationY
};
}
}
Expand Down
21 changes: 10 additions & 11 deletions Xamarin.Forms.Platform.WPF/VisualElementTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,31 +279,30 @@ void UpdateScaleAndTranslateAndRotation()
double translationX = Element.TranslationX;
double translationY = Element.TranslationY;
double scale = Element.Scale;

double offsetX = scale == 0 ? 0 : translationX / scale;
double offsetY = scale == 0 ? 0 : translationY / scale;
double scaleX = Element.ScaleX * scale;
double scaleY = Element.ScaleY * scale;

Control.RenderTransformOrigin = new System.Windows.Point(anchorX, anchorY);
Control.RenderTransform = new WTransformGroup()
{
Children = new WTransformCollection()
{
new WScaleTransform
{
ScaleX = scaleX,
ScaleY = scaleY
},
new WRotateTransform()
{
CenterX = anchorX,
CenterY = anchorY,
Angle = Element.Rotation
Angle = rotation
},
new WTranslateTransform()
{
X = offsetX,
Y = offsetY
X = translationX,
Y = translationY
},
new WScaleTransform
{
ScaleX = scale,
ScaleY = scale
}
}
};
}
Expand Down
Loading

0 comments on commit 36741b9

Please sign in to comment.