-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6576 from AvaloniaUI/feature/1279-combining-geome…
…tries Add GeometryGroup and CombinedGeometry
- Loading branch information
Showing
35 changed files
with
750 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using Avalonia.Platform; | ||
|
||
#nullable enable | ||
|
||
namespace Avalonia.Media | ||
{ | ||
public enum GeometryCombineMode | ||
{ | ||
/// <summary> | ||
/// The two regions are combined by taking the union of both. The resulting geometry is | ||
/// geometry A + geometry B. | ||
/// </summary> | ||
Union, | ||
|
||
/// <summary> | ||
/// The two regions are combined by taking their intersection. The new area consists of the | ||
/// overlapping region between the two geometries. | ||
/// </summary> | ||
Intersect, | ||
|
||
/// <summary> | ||
/// The two regions are combined by taking the area that exists in the first region but not | ||
/// the second and the area that exists in the second region but not the first. The new | ||
/// region consists of (A-B) + (B-A), where A and B are geometries. | ||
/// </summary> | ||
Xor, | ||
|
||
/// <summary> | ||
/// The second region is excluded from the first. Given two geometries, A and B, the area of | ||
/// geometry B is removed from the area of geometry A, producing a region that is A-B. | ||
/// </summary> | ||
Exclude, | ||
} | ||
|
||
/// <summary> | ||
/// Represents a 2-D geometric shape defined by the combination of two Geometry objects. | ||
/// </summary> | ||
public class CombinedGeometry : Geometry | ||
{ | ||
/// <summary> | ||
/// Defines the <see cref="Geometry1"/> property. | ||
/// </summary> | ||
public static readonly StyledProperty<Geometry?> Geometry1Property = | ||
AvaloniaProperty.Register<CombinedGeometry, Geometry?>(nameof(Geometry1)); | ||
|
||
/// <summary> | ||
/// Defines the <see cref="Geometry2"/> property. | ||
/// </summary> | ||
public static readonly StyledProperty<Geometry?> Geometry2Property = | ||
AvaloniaProperty.Register<CombinedGeometry, Geometry?>(nameof(Geometry2)); | ||
/// <summary> | ||
/// Defines the <see cref="GeometryCombineMode"/> property. | ||
/// </summary> | ||
public static readonly StyledProperty<GeometryCombineMode> GeometryCombineModeProperty = | ||
AvaloniaProperty.Register<CombinedGeometry, GeometryCombineMode>(nameof(GeometryCombineMode)); | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CombinedGeometry"/> class. | ||
/// </summary> | ||
public CombinedGeometry() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CombinedGeometry"/> class with the | ||
/// specified <see cref="Geometry"/> objects. | ||
/// </summary> | ||
/// <param name="geometry1">The first geometry to combine.</param> | ||
/// <param name="geometry2">The second geometry to combine.</param> | ||
public CombinedGeometry(Geometry geometry1, Geometry geometry2) | ||
{ | ||
Geometry1 = geometry1; | ||
Geometry2 = geometry2; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CombinedGeometry"/> class with the | ||
/// specified <see cref="Geometry"/> objects and <see cref="GeometryCombineMode"/>. | ||
/// </summary> | ||
/// <param name="combineMode">The method by which geometry1 and geometry2 are combined.</param> | ||
/// <param name="geometry1">The first geometry to combine.</param> | ||
/// <param name="geometry2">The second geometry to combine.</param> | ||
public CombinedGeometry(GeometryCombineMode combineMode, Geometry? geometry1, Geometry? geometry2) | ||
{ | ||
Geometry1 = geometry1; | ||
Geometry2 = geometry2; | ||
GeometryCombineMode = combineMode; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CombinedGeometry"/> class with the | ||
/// specified <see cref="Geometry"/> objects, <see cref="GeometryCombineMode"/> and | ||
/// <see cref="Transform"/>. | ||
/// </summary> | ||
/// <param name="combineMode">The method by which geometry1 and geometry2 are combined.</param> | ||
/// <param name="geometry1">The first geometry to combine.</param> | ||
/// <param name="geometry2">The second geometry to combine.</param> | ||
/// <param name="transform">The transform applied to the geometry.</param> | ||
public CombinedGeometry( | ||
GeometryCombineMode combineMode, | ||
Geometry? geometry1, | ||
Geometry? geometry2, | ||
Transform? transform) | ||
{ | ||
Geometry1 = geometry1; | ||
Geometry2 = geometry2; | ||
GeometryCombineMode = combineMode; | ||
Transform = transform; | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the first <see cref="Geometry"/> object of this | ||
/// <see cref="CombinedGeometry"/> object. | ||
/// </summary> | ||
public Geometry? Geometry1 | ||
{ | ||
get => GetValue(Geometry1Property); | ||
set => SetValue(Geometry1Property, value); | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the second <see cref="Geometry"/> object of this | ||
/// <see cref="CombinedGeometry"/> object. | ||
/// </summary> | ||
public Geometry? Geometry2 | ||
{ | ||
get => GetValue(Geometry2Property); | ||
set => SetValue(Geometry2Property, value); | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the method by which the two geometries (specified by the | ||
/// <see cref="Geometry1"/> and <see cref="Geometry2"/> properties) are combined. The | ||
/// default value is <see cref="GeometryCombineMode.Union"/>. | ||
/// </summary> | ||
public GeometryCombineMode GeometryCombineMode | ||
{ | ||
get => GetValue(GeometryCombineModeProperty); | ||
set => SetValue(GeometryCombineModeProperty, value); | ||
} | ||
|
||
public override Geometry Clone() | ||
{ | ||
return new CombinedGeometry(GeometryCombineMode, Geometry1, Geometry2, Transform); | ||
} | ||
|
||
protected override IGeometryImpl? CreateDefiningGeometry() | ||
{ | ||
var g1 = Geometry1; | ||
var g2 = Geometry2; | ||
|
||
if (g1 is object && g2 is object) | ||
{ | ||
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); | ||
return factory.CreateCombinedGeometry(GeometryCombineMode, g1, g2); | ||
} | ||
else if (GeometryCombineMode == GeometryCombineMode.Intersect) | ||
return null; | ||
else if (g1 is object) | ||
return g1.PlatformImpl; | ||
else if (g2 is object) | ||
return g2.PlatformImpl; | ||
else | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using Avalonia.Animation; | ||
|
||
#nullable enable | ||
|
||
namespace Avalonia.Media | ||
{ | ||
public class GeometryCollection : Animatable, IList<Geometry>, IReadOnlyList<Geometry> | ||
{ | ||
private List<Geometry> _inner; | ||
|
||
public GeometryCollection() => _inner = new List<Geometry>(); | ||
public GeometryCollection(IEnumerable<Geometry> collection) => _inner = new List<Geometry>(collection); | ||
public GeometryCollection(int capacity) => _inner = new List<Geometry>(capacity); | ||
|
||
public Geometry this[int index] | ||
{ | ||
get => _inner[index]; | ||
set => _inner[index] = value; | ||
} | ||
|
||
public int Count => _inner.Count; | ||
public bool IsReadOnly => false; | ||
|
||
public void Add(Geometry item) => _inner.Add(item); | ||
public void Clear() => _inner.Clear(); | ||
public bool Contains(Geometry item) => _inner.Contains(item); | ||
public void CopyTo(Geometry[] array, int arrayIndex) => _inner.CopyTo(array, arrayIndex); | ||
public IEnumerator<Geometry> GetEnumerator() => _inner.GetEnumerator(); | ||
public int IndexOf(Geometry item) => _inner.IndexOf(item); | ||
public void Insert(int index, Geometry item) => _inner.Insert(index, item); | ||
public bool Remove(Geometry item) => _inner.Remove(item); | ||
public void RemoveAt(int index) => _inner.RemoveAt(index); | ||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Avalonia.Metadata; | ||
using Avalonia.Platform; | ||
|
||
#nullable enable | ||
|
||
namespace Avalonia.Media | ||
{ | ||
/// <summary> | ||
/// Represents a composite geometry, composed of other <see cref="Geometry"/> objects. | ||
/// </summary> | ||
public class GeometryGroup : Geometry | ||
{ | ||
public static readonly DirectProperty<GeometryGroup, GeometryCollection?> ChildrenProperty = | ||
AvaloniaProperty.RegisterDirect<GeometryGroup, GeometryCollection?> ( | ||
nameof(Children), | ||
o => o.Children, | ||
(o, v) => o.Children = v); | ||
|
||
public static readonly StyledProperty<FillRule> FillRuleProperty = | ||
AvaloniaProperty.Register<GeometryGroup, FillRule>(nameof(FillRule)); | ||
|
||
private GeometryCollection? _children; | ||
private bool _childrenSet; | ||
|
||
/// <summary> | ||
/// Gets or sets the collection that contains the child geometries. | ||
/// </summary> | ||
[Content] | ||
public GeometryCollection? Children | ||
{ | ||
get => _children ??= (!_childrenSet ? new GeometryCollection() : null); | ||
set | ||
{ | ||
SetAndRaise(ChildrenProperty, ref _children, value); | ||
_childrenSet = true; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets how the intersecting areas of the objects contained in this | ||
/// <see cref="GeometryGroup"/> are combined. The default is <see cref="FillRule.EvenOdd"/>. | ||
/// </summary> | ||
public FillRule FillRule | ||
{ | ||
get => GetValue(FillRuleProperty); | ||
set => SetValue(FillRuleProperty, value); | ||
} | ||
|
||
public override Geometry Clone() | ||
{ | ||
var result = new GeometryGroup { FillRule = FillRule, Transform = Transform }; | ||
if (_children?.Count > 0) | ||
result.Children = new GeometryCollection(_children); | ||
return result; | ||
} | ||
|
||
protected override IGeometryImpl? CreateDefiningGeometry() | ||
{ | ||
if (_children?.Count > 0) | ||
{ | ||
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); | ||
return factory.CreateGeometryGroup(FillRule, _children); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change) | ||
{ | ||
base.OnPropertyChanged(change); | ||
|
||
if (change.Property == ChildrenProperty || change.Property == FillRuleProperty) | ||
{ | ||
InvalidateGeometry(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.