A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Linq;
6using osu.Framework.Allocation;
7using osu.Framework.Bindables;
8using osu.Framework.Graphics;
9using osu.Framework.Graphics.Containers;
10using osu.Framework.Graphics.Lines;
11using osu.Framework.Graphics.Shapes;
12using osu.Framework.Utils;
13using osuTK;
14using osuTK.Graphics;
15
16namespace osu.Framework.Tests.Visual.Drawables
17{
18 public class TestSceneCircularArcBoundingBox : FrameworkTestScene
19 {
20 private SmoothPath path;
21 private Box boundingBox;
22
23 private readonly BindableList<Vector2> controlPoints = new BindableList<Vector2>();
24
25 private float startAngle, endAngle, radius;
26
27 [BackgroundDependencyLoader]
28 private void load()
29 {
30 Child = new Container
31 {
32 AutoSizeAxes = Axes.Both,
33 Children = new Drawable[]
34 {
35 boundingBox = new Box
36 {
37 RelativeSizeAxes = Axes.None,
38 Colour = Color4.Red
39 },
40 path = new SmoothPath
41 {
42 Colour = Color4.White,
43 PathRadius = 2
44 }
45 }
46 };
47
48 AddSliderStep("starting angle", 0, 360, 90, angle =>
49 {
50 startAngle = angle;
51 generateControlPoints();
52 });
53
54 AddSliderStep("end angle", 0, 360, 270, angle =>
55 {
56 endAngle = angle;
57 generateControlPoints();
58 });
59
60 AddSliderStep("radius", 1, 300, 150, radius =>
61 {
62 this.radius = radius;
63 generateControlPoints();
64 });
65 }
66
67 protected override void LoadComplete()
68 {
69 controlPoints.BindCollectionChanged((_, __) =>
70 {
71 var copy = controlPoints.ToArray();
72 if (copy.Length != 3)
73 return;
74
75 path.Vertices = PathApproximator.ApproximateCircularArc(copy);
76
77 var bounds = PathApproximator.CircularArcBoundingBox(copy);
78 boundingBox.Size = bounds.Size;
79
80 // because SmoothPath's bounding box is not exact,
81 // adjust our box's anchoring so that it's always aligned correctly to encapsulate the arc.
82
83 Anchor anchor = 0;
84
85 if (path.Vertices.All(v => v.X < 0))
86 anchor |= Anchor.x0;
87 else if (path.Vertices.All(v => v.X > 0))
88 anchor |= Anchor.x2;
89 else
90 anchor |= Anchor.x1;
91
92 if (path.Vertices.All(v => v.Y < 0))
93 anchor |= Anchor.y0;
94 else if (path.Vertices.All(v => v.Y > 0))
95 anchor |= Anchor.y2;
96 else
97 anchor |= Anchor.y1;
98
99 boundingBox.Anchor = boundingBox.Origin = anchor;
100 });
101 }
102
103 private void generateControlPoints()
104 {
105 float midpoint = (startAngle + endAngle) / 2;
106
107 Vector2 polarToCartesian(float r, float theta) =>
108 new Vector2(
109 r * MathF.Cos(MathHelper.DegreesToRadians(theta)),
110 r * MathF.Sin(MathHelper.DegreesToRadians(theta)));
111
112 controlPoints.Clear();
113 controlPoints.AddRange(new[]
114 {
115 polarToCartesian(radius, startAngle),
116 polarToCartesian(radius, midpoint),
117 polarToCartesian(radius, endAngle)
118 });
119 }
120 }
121}