A game framework written with osu! in mind.
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement apple's "continuous curves"

+96 -12
+22 -5
osu.Framework.Tests/Visual/Containers/TestSceneMasking.cs
··· 16 16 public class TestSceneMasking : FrameworkTestScene 17 17 { 18 18 protected Container TestContainer; 19 + protected int CurrentTest; 20 + protected float TestCornerExponent = 2f; 19 21 20 22 public TestSceneMasking() 21 23 { ··· 42 44 AddStep(testNames[i], delegate { loadTest(test); }); 43 45 } 44 46 47 + AddSliderStep<float>("Corner exponent", 2, 5, 2, exponent => 48 + { 49 + TestCornerExponent = exponent; 50 + loadTest(CurrentTest); 51 + }); 52 + 45 53 loadTest(0); 46 54 addCrosshair(); 47 55 } ··· 84 92 private void loadTest(int testType) 85 93 { 86 94 TestContainer.Clear(); 95 + CurrentTest = testType; 87 96 88 97 switch (testType) 89 98 { ··· 96 105 Origin = Anchor.Centre, 97 106 Masking = true, 98 107 CornerRadius = 100, 108 + CornerExponent = TestCornerExponent, 99 109 BorderColour = Color4.Aquamarine, 100 110 BorderThickness = 3, 101 111 EdgeEffect = new EdgeEffectParameters ··· 132 142 { 133 143 Masking = true, 134 144 CornerRadius = 100, 145 + CornerExponent = TestCornerExponent, 135 146 Size = new Vector2(400, 400), 136 147 Alpha = 0.5f, 137 148 Origin = Anchor.Centre, ··· 162 173 { 163 174 Masking = true, 164 175 CornerRadius = 25, 176 + CornerExponent = TestCornerExponent, 165 177 Shear = new Vector2(0.5f, 0), 166 178 Size = new Vector2(150, 150), 167 179 Scale = new Vector2(2.5f, 1.5f), ··· 204 216 { 205 217 Masking = true, 206 218 CornerRadius = 25, 219 + CornerExponent = TestCornerExponent, 207 220 Shear = new Vector2(0.5f, 0), 208 221 Alpha = 0.5f, 209 222 Origin = Anchor.Centre, ··· 215 228 { 216 229 Masking = true, 217 230 CornerRadius = 25, 231 + CornerExponent = TestCornerExponent, 218 232 Shear = new Vector2(0.25f, 0.25f), 219 233 Size = new Vector2(100, 200), 220 234 Alpha = 0.5f, ··· 234 248 235 249 case 4: 236 250 { 237 - static Drawable createMaskingBox(float scale) 251 + static Drawable createMaskingBox(float scale, float testCornerExponent) 238 252 { 239 253 float size = 200 / scale; 240 254 return new Container 241 255 { 242 256 Masking = true, 243 257 CornerRadius = 25 / scale, 258 + CornerExponent = testCornerExponent, 244 259 BorderThickness = 12.5f / scale, 245 260 BorderColour = Color4.Red, 246 261 Size = new Vector2(size), ··· 278 293 RelativeSizeAxes = Axes.Both, 279 294 Size = new Vector2(0.5f), 280 295 Masking = true, 281 - Children = new[] { createMaskingBox(100) } 296 + Children = new[] { createMaskingBox(100, TestCornerExponent) } 282 297 }, 283 298 new Container 284 299 { 285 300 RelativeSizeAxes = Axes.Both, 286 301 Size = new Vector2(0.5f), 287 302 Masking = true, 288 - Children = new[] { createMaskingBox(10) } 303 + Children = new[] { createMaskingBox(10, TestCornerExponent) } 289 304 }, 290 305 new Container 291 306 { 292 307 RelativeSizeAxes = Axes.Both, 293 308 Size = new Vector2(0.5f), 294 309 Masking = true, 295 - Children = new[] { createMaskingBox(1) } 310 + Children = new[] { createMaskingBox(1, TestCornerExponent) } 296 311 }, 297 312 new Container 298 313 { 299 314 RelativeSizeAxes = Axes.Both, 300 315 Size = new Vector2(0.5f), 301 316 Masking = true, 302 - Children = new[] { createMaskingBox(0.1f) } 317 + Children = new[] { createMaskingBox(0.1f, TestCornerExponent) } 303 318 }, 304 319 } 305 320 }); ··· 320 335 { 321 336 Masking = true, 322 337 CornerRadius = 100f, 338 + CornerExponent = TestCornerExponent, 323 339 BorderThickness = 50f, 324 340 BorderColour = Color4.Red, 325 341 RelativeSizeAxes = Axes.Both, ··· 444 460 Origin = Anchor.Centre, 445 461 Masking = true, 446 462 CornerRadius = 100, 463 + CornerExponent = TestCornerExponent, 447 464 Alpha = 0.8f, 448 465 EdgeEffect = new EdgeEffectParameters 449 466 {
+4 -1
osu.Framework.Tests/Visual/Input/TestSceneNestedHover.cs
··· 19 19 { 20 20 Anchor = Anchor.Centre, 21 21 Origin = Anchor.Centre, 22 - Size = new Vector2(300, 300) 22 + Size = new Vector2(300, 300), 23 + CornerRadius = 100, 24 + CornerExponent = 5, 25 + Masking = true, 23 26 }); 24 27 25 28 HoverBox box2;
+2 -1
osu.Framework/Graphics/Containers/CircularContainer.cs
··· 6 6 namespace osu.Framework.Graphics.Containers 7 7 { 8 8 /// <summary> 9 - /// A container which is rounded (via automatic corner-radius) on the shortest edge. 9 + /// A container which is rounded (via automatic corner-radius and corner-exponent=2) on the shortest edge. 10 10 /// </summary> 11 11 public class CircularContainer : Container 12 12 { ··· 15 15 // this shouldn't have to be done here, but it's the only place it works correctly. 16 16 // see https://github.com/ppy/osu-framework/pull/1666 17 17 CornerRadius = Math.Min(DrawSize.X, DrawSize.Y) / 2f; 18 + CornerExponent = 2; 18 19 19 20 return base.GenerateDrawNodeSubtree(frame, treeIndex, forceNewDrawNode); 20 21 }
+32 -3
osu.Framework/Graphics/Containers/CompositeDrawable.cs
··· 1217 1217 1218 1218 public override bool Contains(Vector2 screenSpacePos) 1219 1219 { 1220 - float cRadius = CornerRadius; 1220 + float cRadius = effectiveCornerRadius; 1221 + float cExponent = CornerExponent; 1221 1222 1222 1223 // Select a cheaper contains method when we don't need rounded edges. 1223 1224 if (cRadius == 0.0f) 1224 1225 return base.Contains(screenSpacePos); 1225 1226 1226 - return DrawRectangle.Shrink(cRadius).DistanceSquared(ToLocalSpace(screenSpacePos)) <= cRadius * cRadius; 1227 + return DrawRectangle.Shrink(cRadius).DistanceExponentiated(ToLocalSpace(screenSpacePos), cExponent) <= Math.Pow(cRadius, cExponent); 1227 1228 } 1228 1229 1229 1230 /// <summary> ··· 1339 1340 } 1340 1341 } 1341 1342 1343 + private float cornerExponent = 2.5f; 1344 + 1345 + /// <summary> 1346 + /// Determines how gentle the curve of the corner straightens. A value of 2 results in 1347 + /// circular arcs, a value of 2.5 (default) results in something closer to apple's "continuous curve". 1348 + /// Recommended range: 2--5. Values outside of that range may result in unpredictable behavior. 1349 + /// Only has an effect when <see cref="Masking"/> is true and <see cref="CornerRadius"/> is non-zero. 1350 + /// </summary> 1351 + public float CornerExponent 1352 + { 1353 + get => cornerExponent; 1354 + protected set 1355 + { 1356 + if (cornerExponent == value) 1357 + return; 1358 + 1359 + cornerExponent = value; 1360 + Invalidate(Invalidation.DrawNode); 1361 + } 1362 + } 1363 + 1364 + // This _hacky_ modification of the corner radius (obtained from playing around) ensures that the corner remains at roughly 1365 + // equal size (perceptually) compared to the circular arc as the CornerExponent is adjusted within the range ~2-5. 1366 + private float effectiveCornerRadius => CornerRadius * 0.8f * CornerExponent / 2 + 0.2f * CornerRadius; 1367 + 1342 1368 private float borderThickness; 1343 1369 1344 1370 /// <summary> ··· 1422 1448 Vector2 offset = ToParentSpace(Vector2.Zero); 1423 1449 Vector2 u = ToParentSpace(new Vector2(cRadius, 0)) - offset; 1424 1450 Vector2 v = ToParentSpace(new Vector2(0, cRadius)) - offset; 1425 - Vector2 inflation = new Vector2((float)Math.Sqrt(u.X * u.X + v.X * v.X), (float)Math.Sqrt(u.Y * u.Y + v.Y * v.Y)); 1451 + Vector2 inflation = new Vector2( 1452 + (float)Math.Sqrt(u.X * u.X + v.X * v.X), 1453 + (float)Math.Sqrt(u.Y * u.Y + v.Y * v.Y) 1454 + ); 1426 1455 1427 1456 RectangleF result = ToParentSpace(drawRect).AABBFloat.Inflate(inflation); 1428 1457 // The above algorithm will return incorrect results if the rounded corners are not fully visible.
+2 -1
osu.Framework/Graphics/Containers/CompositeDrawable_DrawNode.cs
··· 93 93 MaskingRect = Source.DrawRectangle, 94 94 ConservativeScreenSpaceQuad = Quad.FromRectangle(shrunkDrawRectangle) * DrawInfo.Matrix, 95 95 ToMaskingSpace = DrawInfo.MatrixInverse, 96 - CornerRadius = Source.CornerRadius, 96 + CornerRadius = Source.effectiveCornerRadius, 97 + CornerExponent = Source.CornerExponent, 97 98 BorderThickness = Source.BorderThickness, 98 99 BorderColour = Source.BorderColour, 99 100 // We are setting the linear blend range to the approximate size of a _pixel_ here.
+12
osu.Framework/Graphics/Containers/Container.cs
··· 310 310 } 311 311 312 312 /// <summary> 313 + /// Determines how gentle the curve of the corner straightens. A value of 2 results in 314 + /// circular arcs, a value of 2.5 (default) results in something closer to apple's "continuous curve". 315 + /// Recommended range: 2--5. Values outside of that range may result in unpredictable behavior. 316 + /// Only has an effect when <see cref="Masking"/> is true and <see cref="CornerRadius"/> is non-zero. 317 + /// </summary> 318 + public new float CornerExponent 319 + { 320 + get => base.CornerExponent; 321 + set => base.CornerExponent = value; 322 + } 323 + 324 + /// <summary> 313 325 /// Determines how thick of a border to draw around the inside of the masked region. 314 326 /// Only has an effect when <see cref="Masking"/> is true. 315 327 /// The border only is drawn on top of children using a sprite shader.
+5
osu.Framework/Graphics/OpenGL/GLWrapper.cs
··· 147 147 ToMaskingSpace = Matrix3.Identity, 148 148 BlendRange = 1, 149 149 AlphaExponent = 1, 150 + CornerExponent = 2.5f, 150 151 }, true); 151 152 152 153 PushDepthInfo(DepthInfo.Default); ··· 488 489 maskingInfo.MaskingRect.Bottom)); 489 490 490 491 GlobalPropertyManager.Set(GlobalProperty.ToMaskingSpace, maskingInfo.ToMaskingSpace); 492 + 491 493 GlobalPropertyManager.Set(GlobalProperty.CornerRadius, maskingInfo.CornerRadius); 494 + GlobalPropertyManager.Set(GlobalProperty.CornerExponent, maskingInfo.CornerExponent); 492 495 493 496 GlobalPropertyManager.Set(GlobalProperty.BorderThickness, maskingInfo.BorderThickness / maskingInfo.BlendRange); 494 497 ··· 784 787 public Matrix3 ToMaskingSpace; 785 788 786 789 public float CornerRadius; 790 + public float CornerExponent; 787 791 788 792 public float BorderThickness; 789 793 public SRGBColour BorderColour; ··· 801 805 MaskingRect == other.MaskingRect && 802 806 ToMaskingSpace == other.ToMaskingSpace && 803 807 CornerRadius == other.CornerRadius && 808 + CornerExponent == other.CornerExponent && 804 809 BorderThickness == other.BorderThickness && 805 810 BorderColour.Equals(other.BorderColour) && 806 811 BlendRange == other.BlendRange &&
+10
osu.Framework/Graphics/Primitives/RectangleF.cs
··· 338 338 return dist.LengthSquared; 339 339 } 340 340 341 + internal float DistanceExponentiated(Vector2 localSpacePos, float exponent) 342 + { 343 + Vector2 dist = new Vector2( 344 + Math.Max(0.0f, Math.Max(localSpacePos.X - Right, Left - localSpacePos.X)), 345 + Math.Max(0.0f, Math.Max(localSpacePos.Y - Bottom, Top - localSpacePos.Y)) 346 + ); 347 + 348 + return (float)Math.Pow(dist.X, exponent) + (float)Math.Pow(dist.Y, exponent); 349 + } 350 + 341 351 // This could be optimized further in the future, but made for a simple implementation right now. 342 352 public RectangleI AABB => ((Quad)this).AABB; 343 353
+1
osu.Framework/Graphics/Shaders/GlobalProperty.cs
··· 12 12 MaskingRect, 13 13 ToMaskingSpace, 14 14 CornerRadius, 15 + CornerExponent, 15 16 BorderThickness, 16 17 BorderColour, 17 18 MaskingBlendRange,
+1
osu.Framework/Graphics/Shaders/GlobalPropertyManager.cs
··· 23 23 global_properties[(int)GlobalProperty.MaskingRect] = new UniformMapping<Vector4>("g_MaskingRect"); 24 24 global_properties[(int)GlobalProperty.ToMaskingSpace] = new UniformMapping<Matrix3>("g_ToMaskingSpace"); 25 25 global_properties[(int)GlobalProperty.CornerRadius] = new UniformMapping<float>("g_CornerRadius"); 26 + global_properties[(int)GlobalProperty.CornerExponent] = new UniformMapping<float>("g_CornerExponent"); 26 27 global_properties[(int)GlobalProperty.BorderThickness] = new UniformMapping<float>("g_BorderThickness"); 27 28 global_properties[(int)GlobalProperty.BorderColour] = new UniformMapping<Vector4>("g_BorderColour"); 28 29 global_properties[(int)GlobalProperty.MaskingBlendRange] = new UniformMapping<float>("g_MaskingBlendRange");
+5 -1
osu.Framework/Resources/Shaders/sh_TextureRounded.fs
··· 8 8 9 9 uniform lowp sampler2D m_Sampler; 10 10 uniform highp float g_CornerRadius; 11 + uniform highp float g_CornerExponent; 11 12 uniform highp vec4 g_MaskingRect; 12 13 uniform highp float g_BorderThickness; 13 14 uniform lowp vec4 g_BorderColour; ··· 40 41 return maxDist; 41 42 // Outside of the shrunk rectangle 42 43 else 43 - return length(max(vec2(0.0), distanceFromShrunkRect)); 44 + { 45 + distanceFromShrunkRect = max(vec2(0.0), distanceFromShrunkRect); 46 + return pow(pow(distanceFromShrunkRect.x, g_CornerExponent) + pow(distanceFromShrunkRect.y, g_CornerExponent), 1.0 / g_CornerExponent); 47 + } 44 48 } 45 49 46 50 highp float distanceFromDrawingRect()