Skip to content

Commit

Permalink
feat(PinchGestureRecognizer): Report Pinch angle in degrees (#13244)
Browse files Browse the repository at this point in the history
* feat(PinchGestureRecognizer): Report Pinch angle in degrees

* fix: Address review

* fix: Angle calculation

* fix: Prevent Gesture Recognition from other Recognition

* feat: Add Sample

* fix: make GetDistance static

* feat(PinchGestureRecognizer): AngleDelta
  • Loading branch information
workgroupengineering authored Nov 23, 2023
1 parent 8734d9d commit 2852c14
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 115 deletions.
13 changes: 12 additions & 1 deletion samples/ControlCatalog/Pages/GesturePage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,18 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
image.InvalidateMeasure();
}
};




if (this.Find<Slider>("AngleSlider") is { } slider &&
this.Find<Panel>("RotationGesture") is { } rotationGesture
)
{
rotationGesture.AddHandler(Gestures.PinchEvent, (s, e) =>
{
slider.Value = e.Angle;
});
}
}

private void SetPinchHandlers(Control? control)
Expand Down
263 changes: 158 additions & 105 deletions samples/ControlCatalog/Pages/GesturePage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,114 +4,167 @@
d:DesignHeight="800"
d:DesignWidth="400"
x:Class="ControlCatalog.Pages.GesturePage">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock FontWeight="Bold"
FontSize="18"
Margin="5">Pull Gexture (Touch / Pen)</TextBlock>
<TextBlock Margin="5">Pull from colored rectangles</TextBlock>
<Border>
<DockPanel HorizontalAlignment="Stretch"
ClipToBounds="True"
Margin="5"
Height="200">
<Border DockPanel.Dock="Top"
Margin="2"
Name="TopPullZone"
Background="Transparent"
BorderBrush="Red"
HorizontalAlignment="Stretch"
Height="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="TopToBottom"/>
</Border.GestureRecognizers>
<Border Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="5"
Name="TopBall"
Background="Green"/>
</Border>
<Border DockPanel.Dock="Bottom"
BorderBrush="Green"
Margin="2"
Background="Transparent"
Name="BottomPullZone"
HorizontalAlignment="Stretch"
Height="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="BottomToTop"/>
</Border.GestureRecognizers>
<Border Width="10"
Name="BottomBall"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="10"
CornerRadius="5"
Background="Green"/>
</Border>
<Border DockPanel.Dock="Right"
Margin="2"
Background="Transparent"
Name="RightPullZone"
BorderBrush="Blue"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Width="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="RightToLeft"/>
</Border.GestureRecognizers>
<Border Width="10"
Height="10"
Name="RightBall"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="5"
Background="Green"/>
<TabControl>
<TabItem>
<TabItem.Header>
<TextBlock FontWeight="Bold"
FontSize="18"
Margin="5">Pull Gexture (Touch / Pen)</TextBlock>

</TabItem.Header>
<StackPanel>
<TextBlock Margin="5">Pull from colored rectangles</TextBlock>
<Border>
<DockPanel HorizontalAlignment="Stretch"
ClipToBounds="True"
Margin="5"
Height="200">
<Border DockPanel.Dock="Top"
Margin="2"
Name="TopPullZone"
Background="Transparent"
BorderBrush="Red"
HorizontalAlignment="Stretch"
Height="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="TopToBottom"/>
</Border.GestureRecognizers>
<Border Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="5"
Name="TopBall"
Background="Green"/>
</Border>
<Border DockPanel.Dock="Bottom"
BorderBrush="Green"
Margin="2"
Background="Transparent"
Name="BottomPullZone"
HorizontalAlignment="Stretch"
Height="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="BottomToTop"/>
</Border.GestureRecognizers>
<Border Width="10"
Name="BottomBall"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="10"
CornerRadius="5"
Background="Green"/>
</Border>
<Border DockPanel.Dock="Right"
Margin="2"
Background="Transparent"
Name="RightPullZone"
BorderBrush="Blue"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Width="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="RightToLeft"/>
</Border.GestureRecognizers>
<Border Width="10"
Height="10"
Name="RightBall"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="5"
Background="Green"/>

</Border>
<Border DockPanel.Dock="Left"
Margin="2"
Background="Transparent"
Name="LeftPullZone"
BorderBrush="Orange"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Width="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="LeftToRight"/>
</Border.GestureRecognizers>
<Border Width="10"
Height="10"
Name="LeftBall"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="5"
Background="Green"/>

</Border>
</DockPanel>
</Border>
<Border DockPanel.Dock="Left"
Margin="2"
Background="Transparent"
Name="LeftPullZone"
BorderBrush="Orange"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Width="50"
BorderThickness="1">
<Border.GestureRecognizers>
<PullGestureRecognizer PullDirection="LeftToRight"/>
</Border.GestureRecognizers>
<Border Width="10"
Height="10"
Name="LeftBall"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="5"
Background="Green"/>
</StackPanel>
</TabItem>

<TabItem>
<TabItem.Header>
<TextBlock FontWeight="Bold"
FontSize="18"
Margin="5">Pinch/Zoom Gexture (Multi Touch)</TextBlock>
</TabItem.Header>
<StackPanel>
<Border ClipToBounds="True">
<Image Stretch="UniformToFill"
Margin="5"
Name="PinchImage"
Source="/Assets/delicate-arch-896885_640.jpg">
<Image.GestureRecognizers>
<PinchGestureRecognizer/>
<ScrollGestureRecognizer CanHorizontallyScroll="True" CanVerticallyScroll="True"/>
</Image.GestureRecognizers>
</Image>
</Border>
</DockPanel>
</Border>
<Button HorizontalAlignment="Center" Name="ResetButton">Reset</Button>
</StackPanel>
</TabItem>

<TextBlock FontWeight="Bold"
FontSize="18"
Margin="5">Pinch/Zoom Gexture (Multi Touch)</TextBlock>
<Border ClipToBounds="True">
<Image Stretch="UniformToFill"
Margin="5"
Name="PinchImage"
Source="/Assets/delicate-arch-896885_640.jpg">
<Image.GestureRecognizers>
<PinchGestureRecognizer/>
<ScrollGestureRecognizer CanHorizontallyScroll="True" CanVerticallyScroll="True"/>
</Image.GestureRecognizers>
</Image>
</Border>
<Button HorizontalAlignment="Center" Name="ResetButton">Reset</Button>
</StackPanel>
<TabItem>
<TabItem.Header>
<TextBlock FontWeight="Bold"
FontSize="18"
Margin="5">Pinch/Rotation Gexture (Multi Touch)</TextBlock>
</TabItem.Header>
<DockPanel>
<Slider Minimum="0"
Maximum="360"
DockPanel.Dock="Bottom"
x:Name="AngleSlider"
/>
<Panel x:Name="RotationGesture">
<Panel.GestureRecognizers>
<PinchGestureRecognizer/>
</Panel.GestureRecognizers>
<Border BorderThickness="1.5" BorderBrush="LawnGreen"/>
<Panel HorizontalAlignment="Center"
Width="100"
Height="100">
<Panel.RenderTransform>
<RotateTransform Angle="{Binding #AngleSlider.Value}"/>
</Panel.RenderTransform>
<Rectangle Fill="SkyBlue"/>
<Rectangle HorizontalAlignment="Center"
VerticalAlignment="Top"
Fill="Yellow"
Width="5"
Height="35"/>
</Panel>

<TextBlock Text="{Binding #AngleSlider.Value, StringFormat=0°}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="DemiBold"
FontSize="20"
/>
</Panel>
</DockPanel>
</TabItem>
</TabControl>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class PinchGestureRecognizer : GestureRecognizer
private IPointer? _secondContact;
private Point _secondPoint;
private Point _origin;
private double _previousAngle;

protected override void PointerCaptureLost(IPointer pointer)
{
Expand All @@ -20,7 +21,7 @@ protected override void PointerMoved(PointerEventArgs e)
{
if (Target is Visual visual)
{
if(_firstContact == e.Pointer)
if (_firstContact == e.Pointer)
{
_firstPoint = e.GetPosition(visual);
}
Expand All @@ -39,10 +40,13 @@ protected override void PointerMoved(PointerEventArgs e)

var scale = distance / _initialDistance;

var pinchEventArgs = new PinchEventArgs(scale, _origin);
Target?.RaiseEvent(pinchEventArgs);
var degree = GetAngleDegreeFromPoints(_firstPoint, _secondPoint);

var pinchEventArgs = new PinchEventArgs(scale, _origin, degree, _previousAngle - degree);
_previousAngle = degree;
Target?.RaiseEvent(pinchEventArgs);
e.Handled = pinchEventArgs.Handled;
e.PreventGestureRecognition();
}
}
}
Expand Down Expand Up @@ -74,18 +78,24 @@ protected override void PointerPressed(PointerPressedEventArgs e)

_origin = new Point((_firstPoint.X + _secondPoint.X) / 2.0f, (_firstPoint.Y + _secondPoint.Y) / 2.0f);

_previousAngle = GetAngleDegreeFromPoints(_firstPoint, _secondPoint);

Capture(_firstContact);
Capture(_secondContact);
e.PreventGestureRecognition();
}
}
}

protected override void PointerReleased(PointerReleasedEventArgs e)
{
RemoveContact(e.Pointer);
if(RemoveContact(e.Pointer))
{
e.PreventGestureRecognition();
}
}

private void RemoveContact(IPointer pointer)
private bool RemoveContact(IPointer pointer)
{
if (_firstContact == pointer || _secondContact == pointer)
{
Expand All @@ -102,13 +112,28 @@ private void RemoveContact(IPointer pointer)
}

Target?.RaiseEvent(new PinchEndedEventArgs());
return true;
}
return false;
}

private float GetDistance(Point a, Point b)
private static float GetDistance(Point a, Point b)
{
var length = _secondPoint - _firstPoint;
var length = b - a;
return (float)new Vector(length.X, length.Y).Length;
}

private static double GetAngleDegreeFromPoints(Point a, Point b)
{
// https://stackoverflow.com/a/15994225/20894223

var deltaX = a.X - b.X;
var deltaY = -(a.Y - b.Y); // I reverse the sign, because on the screen the Y axes
// are reversed with respect to the Cartesian plane.
var rad = System.Math.Atan2(deltaX, deltaY); // radians from -π to +π
var degree = ((rad * (180 / System.Math.PI))) + 180; // Atan2 returns a radian value between -π to +π, in degrees -180 to +180.
// To get the angle between 0 and 360 degrees you need to add 180 degrees.
return degree;
}
}
}
Loading

0 comments on commit 2852c14

Please sign in to comment.