1
1
ļ»æusing System ;
2
+ using System . Collections . Generic ;
3
+ using System . Collections . Immutable ;
4
+ using System . Linq ;
2
5
3
6
namespace GalaxyCheck . Internal . Sizing
4
7
{
@@ -8,61 +11,111 @@ namespace GalaxyCheck.Internal.Sizing
8
11
9
12
public static class BoundsScalingFactoryFuncs
10
13
{
11
- public static BoundsScalingFactoryFunc Unscaled => ( min , max , origin ) => ( size ) =>
14
+ public static BoundsScalingFactoryFunc ScaledExponentiallyWithDiscreteIntervals => ( min , max , origin ) =>
12
15
{
13
- return ( min , max ) ;
14
- } ;
16
+ static Dictionary < Size , int > GetBoundsBySize ( int from , int to )
17
+ {
18
+ var discreteBounds = GetIntervals ( from , to ) . Select ( interval => IntervalToBound ( from , to , interval ) ) ;
19
+ return InterpolateBoundOverSizes ( discreteBounds . ToImmutableList ( ) ) ;
20
+ }
15
21
16
- public static BoundsScalingFactoryFunc ScaledLinearly => ( min , max , origin ) => ( size ) =>
17
- {
18
- if ( size . Value == Size . MinValue . Value ) return ( origin , origin ) ;
19
- if ( size . Value == Size . MaxValue . Value ) return ( min , max ) ;
22
+ var leftIntervals = GetBoundsBySize ( origin , min ) ;
23
+ var rightIntervals = GetBoundsBySize ( origin , max ) ;
20
24
21
- return ( ScaleLinear ( min , origin , size ) , ScaleLinear ( origin , max , size ) ) ;
25
+ return ( size ) => ( leftIntervals [ size ] , rightIntervals [ size ] ) ;
22
26
} ;
23
27
24
- public static BoundsScalingFactoryFunc ScaledExponentially => ( min , max , origin ) => ( size ) =>
28
+ private static Func < int , int > Interpolate ( int x1 , int y1 , int x2 , int y2 ) => ( x ) =>
25
29
{
26
- if ( size . Value == Size . MinValue . Value ) return ( origin , origin ) ;
27
- if ( size . Value == Size . MaxValue . Value ) return ( min , max ) ;
28
-
29
- return ( ScaleExponential ( min , origin , size ) , ScaleExponential ( origin , max , size ) ) ;
30
+ return ( int ) ( y1 + ( x - x1 ) * ( ( double ) ( y2 - y1 ) / ( x2 - x1 ) ) ) ;
30
31
} ;
31
32
32
- private static int ScaleLinear ( int from , int to , Size size )
33
+ private static int IntervalToBound ( int from , int to , uint interval )
33
34
{
34
- var width = CalculateWidthSafe ( from , to ) ;
35
- var multiplier = ( double ) size . Value / Size . MaxValue . Value ;
36
- var scaledWidth = ( int ) Math . Round ( width * multiplier ) ;
37
-
38
- return from < 0
39
- ? to > from ? to - scaledWidth : to + scaledWidth
40
- : to > from ? from + scaledWidth : from - scaledWidth ;
35
+ if ( to > from )
36
+ {
37
+ return ( int ) ( from + interval ) ;
38
+ }
39
+ else
40
+ {
41
+ return ( int ) ( from - interval ) ;
42
+ }
41
43
}
42
44
43
- private static int ScaleExponential ( int from , int to , Size size )
45
+ private static uint CalculateWidthSafe ( int from , int to )
44
46
{
45
- var width = CalculateWidthSafe ( from , to ) ;
46
- var exponent = ( ( double ) size . Value / Size . MaxValue . Value ) - 1 ;
47
- var multiplier = Math . Pow ( Size . MaxValue . Value , exponent ) ;
48
- var scaledWidth = ( int ) Math . Round ( width * multiplier ) ;
47
+ var fromSign = Math . Sign ( from ) ;
48
+ var toSign = Math . Sign ( to ) ;
49
+
50
+ if ( fromSign >= 0 && toSign >= 0 )
51
+ {
52
+ return SafeNegate ( to - from ) ;
53
+ }
54
+ else if ( toSign <= 0 && fromSign <= 0 )
55
+ {
56
+ return SafeNegate ( to - from ) ;
57
+ }
58
+ else
59
+ {
60
+ var fromToZero = SafeNegate ( from ) ;
61
+ var toToZero = SafeNegate ( to ) ;
62
+ return fromToZero + toToZero ;
63
+ }
64
+ }
49
65
50
- return from < 0
51
- ? to > from ? to - scaledWidth : to + scaledWidth
52
- : to > from ? from + scaledWidth : from - scaledWidth ;
66
+ private static uint SafeNegate ( int x )
67
+ {
68
+ return x == int . MinValue ? ( uint ) ( int . MaxValue ) + 1 : ( uint ) Math . Abs ( x ) ;
53
69
}
54
70
55
- private static int CalculateWidthSafe ( int from , int to )
71
+ private static IEnumerable < uint > GetIntervals ( int from , int to )
56
72
{
57
- var unsignedWidth = to - from ;
73
+ const int MinimumIntervalCount = 6 ;
58
74
59
- if ( unsignedWidth == int . MinValue )
75
+ var width = CalculateWidthSafe ( from , to ) ;
76
+ var subMaxIntervals = WidthIntervals . Where ( w => w < width ) . ToList ( ) ;
77
+
78
+ foreach ( var interval in subMaxIntervals )
60
79
{
61
- // int.MinValue is -2147483648, which when negated, exceeds int.MaxValue (2147483647).
62
- return int . MaxValue ;
80
+ yield return interval ;
63
81
}
64
82
65
- return Math . Abs ( unsignedWidth ) ;
83
+ yield return width ;
84
+
85
+ for ( var i = subMaxIntervals . Count + 1 ; i < MinimumIntervalCount ; i ++ )
86
+ {
87
+ yield return width ;
88
+ }
89
+ }
90
+
91
+ private static readonly ImmutableList < uint > WidthIntervals = BaseWidthIntervals
92
+ . Concat ( ExponentialWidthIntervals )
93
+ . Concat ( AlmostMaxIntervals )
94
+ . Concat ( new [ ] { uint . MaxValue } )
95
+ . ToImmutableList ( ) ;
96
+
97
+ private static IEnumerable < uint > BaseWidthIntervals => new uint [ ] { 0 , 1 , 5 } ;
98
+
99
+ private static IEnumerable < uint > ExponentialWidthIntervals => new [ ] { 1 , 2 , 4 , 8 } . Select ( exponent => ( uint ) Math . Pow ( 10 , exponent ) ) ;
100
+
101
+ // These make the range from int.MinValue => int.MaxValue more interesting. The bound won't jump from < 0 to int.MaxValue.
102
+ private static IEnumerable < uint > AlmostMaxIntervals => ImmutableList . Create (
103
+ uint . MaxValue / 2 ,
104
+ ( uint . MaxValue / 4 ) * 3 ,
105
+ ( uint . MaxValue / 8 ) * 7 ) ;
106
+
107
+ private static Dictionary < Size , int > InterpolateBoundOverSizes ( ImmutableList < int > bounds )
108
+ {
109
+ var interpolate = Interpolate ( 0 , 0 , 100 , bounds . Count - 1 ) ;
110
+
111
+ return Enumerable . Range ( 0 , 101 ) . ToDictionary (
112
+ size => new Size ( size ) ,
113
+ size =>
114
+ {
115
+ var interpolatedIndex = interpolate ( size ) ;
116
+ var boundIndex = size == 0 ? 0 : Math . Min ( interpolatedIndex + 1 , bounds . Count - 1 ) ;
117
+ return bounds [ boundIndex ] ;
118
+ } ) ;
66
119
}
67
120
}
68
121
}
0 commit comments