source: 2014/30/MiskaK/The Reclaim/The Reclaim/Jypeli/Meter.cs @ 5602

Revision 5480, 20.4 KB checked in by anlakane, 6 years ago (diff)

Lisätty Reclaim versionhallintaan

Line 
1#region MIT License
2/*
3 * Copyright (c) 2009 University of Jyväskylä, Department of Mathematical
4 * Information Technology.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#endregion
25
26/*
27 * Authors: Tero Jäntti, Tomi Karppinen, Janne Nikkanen.
28 */
29
30using System;
31using System.Collections.Generic;
32
33namespace Jypeli
34{
35    /// <summary>
36    /// Suunta mittarin muutokselle.
37    /// </summary>
38    public enum TriggerDirection
39    {
40        /// <summary>
41        /// Mittarin arvo kasvaa.
42        /// </summary>
43        Up,
44
45        /// <summary>
46        /// Mittarin arvo vähenee.
47        /// </summary>
48        Down,
49       
50        /// <summary>
51        /// Ei väliä suunnalla (kasvaa tai vähenee).
52        /// </summary>
53        Irrelevant
54    };
55
56    /// <summary>
57    /// Mittari, joka mittaa erityyppisiä arvoja.
58    /// Sidottavissa näyttöihin, kuten <c>ValueDisplay</c> ja <c>BarGauge</c>.
59    /// </summary>
60    public abstract class Meter
61    {
62        /// <summary>
63        /// Mittarin suhteellinen arvo (minimi 0, maksimi 1)
64        /// </summary>
65        public abstract double RelativeValue { get; set; }
66
67        internal abstract double GetValue();
68        internal abstract double GetMinValue();
69        internal abstract double GetMaxValue();
70
71        /// <summary>
72        /// Palauttaa mittarin sen todellisessa muodossa, jotta sen kaikkiin
73        /// jäseniin pääsee käsiksi.
74        /// </summary>
75        /// <typeparam name="T">Tyyppi, jota mittari mittaa</typeparam>
76        /// <returns>Mittari, joka mittaa tyyppiä T</returns>
77        public Meter<T> OfType<T>() where T : struct, IComparable<T>, IEquatable<T>
78        {
79            Type meterType = this.GetType();
80            Type[] genargs = meterType.GetGenericArguments();
81
82            if ( genargs.Length < 1 ) throw new ArgumentException( "This meter is not typed" );
83            if ( genargs[0] != typeof( T ) ) throw new ArgumentException( String.Format( "This meter measures {0}, not {1}", genargs[0].Name, typeof( T ).Name ) );
84
85            return (Meter<T>)this;
86        }
87    }
88
89    /// <summary>
90    /// Mittari, joka mittaa erityyppisiä arvoja.
91    /// Sidottavissa näyttöihin, kuten <c>ValueDisplay</c> ja <c>BarGauge</c>.
92    /// </summary>
93    [Save]
94    public abstract class Meter<ValueType> : Meter where ValueType : struct, IComparable<ValueType>, IEquatable<ValueType>
95    {
96        private struct Trigger
97        {
98            public ValueType value;
99            public TriggerDirection direction;
100            public Action method;
101
102            public Trigger(ValueType value, TriggerDirection direction, Action method)
103            {
104                this.value = value;
105                this.direction = direction;
106                this.method = method;
107            }
108        }
109
110        private bool valueSet;
111
112        private ValueType val;
113        private ValueType minval;
114        private ValueType maxval;
115        private ValueType defval;
116
117        private List<Trigger> triggers;
118
119        /// <summary>
120        /// Mittarin arvo.
121        /// </summary>
122        [Save]
123        public ValueType Value
124        {
125            get { return val; }
126            set
127            {
128                if ( value.Equals( val ) )
129                    return;
130
131                ValueType oldval = val;
132                valueSet = true;
133
134                if (value.CompareTo(minval) < 0) value = minval;
135                if (value.CompareTo(maxval) > 0) value = maxval;
136
137                val = value;
138                OnChange(oldval, value);
139                CheckLimits( oldval, value );
140                CheckTriggers(oldval, value);
141            }
142        }
143
144        /// <summary>
145        /// Mittarin oletusarvo.
146        /// </summary>
147        public ValueType DefaultValue
148        {
149            get { return defval; }
150            set
151            {
152                defval = clampValue( value, minval, maxval );
153                if ( !valueSet ) Value = value;
154            }
155        }
156
157        /// <summary>
158        /// Mittarin pienin sallittu arvo.
159        /// Kun mittari saavuttaa tämän arvon, laukeaa tapahtuma <c>LowerLimit</c>.
160        /// </summary>
161        public ValueType MinValue
162        {
163            get { return minval; }
164            set { minval = value; updateBounds(); }
165        }
166
167        /// <summary>
168        /// Mittarin suurin sallittu arvo.
169        /// Kun mittari saavuttaa tämän arvon, laukeaa tapahtuma <c>UpperLimit</c>.
170        /// </summary>
171        public ValueType MaxValue
172        {
173            get { return maxval; }
174            set { maxval = value; updateBounds(); }
175        }
176
177        #region Events
178
179        /// <summary>
180        /// Mittarin muutostapahtumankäsittelijä.
181        /// </summary>
182        public delegate void ChangeHandler( ValueType oldValue, ValueType newValue );
183
184        /// <summary>
185        /// Tapahtuu, kun mittarin arvo muuttuu.
186        /// </summary>
187        public event ChangeHandler Changed;
188
189        /// <summary>
190            /// Tapahtuu, kun mittari saavuttaa pienimmän sallitun arvonsa.
191            /// </summary>
192        public event Action LowerLimit;
193
194            /// <summary>
195            /// Tapahtuu, kun mittari saavuttaa suurimman sallitun arvonsa.
196            /// </summary>
197            public event Action UpperLimit;
198
199
200        private void OnChange( ValueType oldValue, ValueType newValue )
201        {
202            if ( Changed != null )
203                Changed( oldValue, newValue );
204        }
205
206        private void CheckLimits( ValueType oldValue, ValueType newValue )
207        {
208            if ( LowerLimit != null && newValue.CompareTo( minval ) <= 0 )
209            {
210                LowerLimit();
211                return;
212            }
213
214            if ( UpperLimit != null && newValue.CompareTo( maxval ) >= 0 )
215            {
216                UpperLimit();
217                return;
218            }
219        }
220
221        private void CheckTriggers( ValueType oldValue, ValueType newValue )
222        {
223            if ( triggers == null ) return;
224
225            foreach ( Trigger t in triggers )
226            {
227                if ( t.direction != TriggerDirection.Down && oldValue.CompareTo( t.value ) < 0 && newValue.CompareTo( t.value ) >= 0 )
228                    t.method();
229               
230                else if ( t.direction != TriggerDirection.Up && oldValue.CompareTo( t.value ) > 0 && newValue.CompareTo( t.value ) <= 0 )
231                    t.method();
232            }
233        }
234
235        #endregion
236
237        /// <summary>
238        /// Luo uuden mittarin.
239        /// </summary>
240        /// <param name="defaultVal">Oletusarvo.</param>
241        /// <param name="minVal">Pienin sallittu arvo.</param>
242        /// <param name="maxVal">Suurin sallittu arvo.</param>
243        public Meter( ValueType defaultVal, ValueType minVal, ValueType maxVal )
244        {
245            minval = minVal;
246            maxval = maxVal;
247            defval = defaultVal;
248            val = defaultVal;
249            updateBounds();
250        }
251
252        /// <summary>
253        /// Luo uuden mittarin kopiona parametrina annetusta.
254        /// </summary>
255        /// <param name="src">Kopioitava mittari.</param>
256        public Meter( Meter<ValueType> src )
257        {
258            minval = src.minval;
259            maxval = src.maxval;
260            defval = src.defval;
261            val = src.val;
262            updateBounds();
263        }
264
265        /// <summary>
266        /// Palauttaa mittarin arvon oletusarvoonsa.
267        /// </summary>
268        public void Reset()
269        {
270            Value = DefaultValue;
271        }
272
273        /// <summary>
274        /// Asettaa mittarille arvon. Sama kuin Value-ominaisuuteen sijoitus,
275        /// mutta helpompi käyttää tapahtumakäsittelijöissä.
276        /// </summary>
277        /// <param name="value">Uusi arvo</param>
278        public void SetValue( ValueType value )
279        {
280            Value = value;
281        }
282
283        /// <summary>
284        /// Lisää mittarille rajan, jonka yli mentäessä laukaistaan aliohjelma.
285        /// </summary>
286        /// <param name="value">Mittarin arvo</param>
287        /// <param name="direction">Suunta (TriggerDirection.Irrelevant, TriggerDirection.Up tai TriggerDirection.Down)</param>
288        /// <param name="method">Aliohjelma, jota kutsutaan.</param>
289        public void AddTrigger( ValueType value, TriggerDirection direction, Action method )
290        {
291            if ( triggers == null ) triggers = new List<Trigger>();
292            triggers.Add( new Trigger( value, direction, method ) );
293        }
294
295        /// <summary>
296        /// Lisää mittarille rajan, jonka yli mentäessä laukaistaan aliohjelma.
297        /// </summary>
298        /// <param name="value">Mittarin arvo</param>
299        /// <param name="direction">Suunta (TriggerDirection.Irrelevant, TriggerDirection.Up tai TriggerDirection.Down)</param>
300        /// <param name="method">Aliohjelma, jota kutsutaan (parametrina mittarin arvo).</param>
301        public void AddTrigger( ValueType value, TriggerDirection direction, Action<ValueType> method )
302        {
303            AddTrigger( value, direction, delegate() { method( this.Value ); } );
304        }
305
306        /// <summary>
307        /// Poistaa kaikki tietylle arvolle asetetut raja-arvotapahtumat.
308        /// </summary>
309        /// <param name="value">Arvo</param>
310        public void RemoveTriggers( ValueType value )
311        {
312            if ( triggers == null ) return;
313            triggers.RemoveAll( t => t.value.Equals( value ) );
314        }
315
316        /// <summary>
317        /// Poistaa kaikki raja-arvotapahtumat, jotka kutsuvat tiettyä aliohjelmaa.
318        /// </summary>
319        /// <param name="method">Aliohjelma</param>
320        public void RemoveTriggers( Action method )
321        {
322            if ( triggers == null ) return;
323            triggers.RemoveAll( t => t.method == method );
324        }
325
326        /// <summary>
327        /// Poistaa kaikki raja-arvotapahtumat.
328        /// </summary>
329        public void ClearTriggers()
330        {
331            if ( triggers == null ) return;
332            triggers.Clear();
333        }
334
335        private static ValueType clampValue( ValueType v, ValueType min, ValueType max )
336        {
337            if ( v.CompareTo( min ) < 0 )
338                return min;
339
340            if ( v.CompareTo( max ) > 0 )
341                return max;
342
343            return v;
344        }
345
346        private static void clampValue( ref ValueType v, ValueType min, ValueType max )
347        {
348            if ( v.CompareTo( min ) < 0 )
349                v = min;
350
351            else if ( v.CompareTo( max ) > 0 )
352                v = max;
353        }
354
355        private void updateBounds()
356        {
357            clampValue( ref val, minval, maxval );
358            clampValue( ref defval, minval, maxval );
359        }
360
361        /// <summary>
362        /// Palauttaa mittarin arvon merkkijonona.
363        /// </summary>
364        public override String ToString()
365        {
366            return Value.ToString();
367        }
368    }
369
370
371    /// <summary>
372    /// Mittari, joka mittaa int-tyyppisiä arvoja.
373    /// Sidottavissa näyttöihin, kuten <c>ValueDisplay</c> ja <c>BarGauge</c>.
374    /// </summary>
375    public class IntMeter : Meter<int>
376    {
377        private List<Operation> operations = new List<Operation>();
378
379        public override double RelativeValue
380        {
381            get { return ( Value - MinValue ) / (double)( MaxValue - MinValue ); }
382            set { Value = (int)( MinValue + value * ( MaxValue - MinValue ) ); }
383        }
384
385        public IntMeter( int defaultValue )
386            : base( defaultValue, 0, int.MaxValue )
387        {
388        }
389
390        public IntMeter(int defaultValue,int minValue, int MaxValue)
391            : base(defaultValue, minValue,MaxValue)
392        {
393        }
394
395        /// <summary>
396        /// Antaa mittariolion <c>m</c> arvon, kun mittaria käytetään
397        /// sellaisessa yhteydessä, jossa vaaditaan tavallista <c>int</c>-
398        /// tyyppistä kokonaislukua.
399        /// </summary>
400        public static implicit operator int( IntMeter m )
401        {
402            return m.Value;
403        }
404
405        /// <summary>
406        /// Antaa mittariolion <c>m</c> arvon, kun mittaria käytetään
407        /// sellaisessa yhteydessä, jossa vaaditaan tavallista <c>double</c>-
408        /// tyyppistä liukulukua.
409        /// </summary>
410        public static implicit operator double( IntMeter m )
411        {
412            return (double)m.Value;
413        }
414
415        /// <summary>
416        /// Lisää jotain mittarin arvoon. Sama kuin Value-ominaisuuteen lisääminen,
417        /// mutta helpompi käyttää tapahtumakäsittelijöissä.
418        /// </summary>
419        /// <param name="change">Lisättävä luku</param>
420        public void AddValue( int change )
421        {
422            Value += change;
423        }
424
425        /// <summary>
426        /// Kertoo mittarin arvon jollakin. Sama kuin Value-ominaisuuden kertominen,
427        /// mutta helpompi käyttää tapahtumakäsittelijöissä.
428        /// </summary>
429        /// <param name="multiplier">Uusi arvo</param>
430        public void MultiplyValue( int multiplier )
431        {
432            Value *= multiplier;
433        }
434
435        /// <summary>
436        /// Kertoo mittarin arvon jollakin. Sama kuin Value-ominaisuuden kertominen,
437        /// mutta helpompi käyttää tapahtumakäsittelijöissä.
438        /// </summary>
439        /// <param name="multiplier">Uusi arvo</param>
440        public void MultiplyValue( double multiplier )
441        {
442            Value = (int)Math.Round( Value * multiplier );
443        }
444
445        /// <summary>
446        /// Lisää tietyn summan mittariin tasaisesti tietyn ajan sisällä.
447        /// </summary>
448        /// <param name="change">Kuinka paljon lisätään</param>
449        /// <param name="seconds">Aika joka lisämiseen kuluu</param>
450        /// <param name="onComplete">Aliohjelma, joka suoritetaan kun lisäys on valmis.</param>
451        /// <returns>Operation-tyyppinen muuttuja, jolla voi hallita tapahtuvaa muutosta</returns>
452        public Operation AddOverTime( int change, double seconds, Action onComplete )
453        {
454            Operation op = AddOverTime( change, seconds );
455            op.Finished += onComplete;
456            return op;
457        }
458
459        /// <summary>
460        /// Lisää tietyn summan mittariin tasaisesti tietyn ajan sisällä.
461        /// </summary>
462        /// <param name="change">Kuinka paljon lisätään</param>
463        /// <param name="seconds">Aika joka lisämiseen kuluu</param>
464        /// <returns>Operation-tyyppinen muuttuja, jolla voi hallita tapahtuvaa muutosta</returns>
465        public Operation AddOverTime( int change, double seconds )
466        {
467            Operation op = new IntMeterAddOperation( this, change, seconds );
468            op.Finished += delegate { operations.Remove( op ); };
469            op.Stopped += delegate { operations.Remove( op ); };
470            operations.Add( op );
471            return op;
472        }
473
474        /// <summary>
475        /// Pysäyttää AddOverTime-metodilla tehtävät lisäykset mittariin.
476        /// </summary>
477        public void Stop()
478        {
479            while ( operations.Count > 0 )
480                operations[0].Stop();
481        }
482
483        internal override double GetValue()
484        {
485            return Value;
486        }
487
488        internal override double GetMinValue()
489        {
490            return MinValue;
491        }
492
493        internal override double GetMaxValue()
494        {
495            return MaxValue;
496        }
497    }
498
499    /// <summary>
500    /// Mittari, joka mittaa double-tyyppisiä arvoja.
501    /// Sidottavissa näyttöihin, kuten <c>ValueDisplay</c> ja <c>BarGauge</c>.
502    /// </summary>
503    public class DoubleMeter : Meter<double>
504    {
505        private List<Operation> operations = new List<Operation>();
506
507        public override double RelativeValue
508        {
509            get { return ( Value - MinValue ) / ( MaxValue - MinValue ); }
510            set { Value = MinValue + value * ( MaxValue - MinValue ); }
511        }
512
513        public DoubleMeter( double defaultValue )
514            : base( defaultValue, 0.0, double.MaxValue )
515        {
516        }
517
518        public DoubleMeter(double defaultValue,double minValue, double maxValue)
519            : base(defaultValue,minValue, maxValue)
520        {
521        }
522
523        /// <summary>
524        /// Antaa mittariolion <c>m</c> arvon, kun mittaria käytetään
525        /// sellaisessa yhteydessä, jossa vaaditaan tavallista <c>double</c>-
526        /// tyyppistä liukulukua.
527        /// </summary>
528        public static implicit operator double( DoubleMeter m )
529        {
530            return m.Value;
531        }
532
533        /// <summary>
534        /// Lisää jotain mittarin arvoon. Sama kuin Value-ominaisuuteen lisääminen,
535        /// mutta helpompi käyttää tapahtumakäsittelijöissä.
536        /// </summary>
537        /// <param name="change">Lisättävä luku</param>
538        public void AddValue( double change )
539        {
540            Value += change;
541        }
542
543        /// <summary>
544        /// Kertoo mittarin arvon jollakin. Sama kuin Value-ominaisuuden kertominen,
545        /// mutta helpompi käyttää tapahtumakäsittelijöissä.
546        /// </summary>
547        /// <param name="multiplier">Uusi arvo</param>
548        public void MultiplyValue( double multiplier )
549        {
550            Value *= multiplier;
551        }
552
553        /// <summary>
554        /// Lisää tietyn summan mittariin tasaisesti tietyn ajan sisällä.
555        /// </summary>
556        /// <param name="change">Kuinka paljon lisätään</param>
557        /// <param name="seconds">Aika joka lisämiseen kuluu</param>
558        /// <param name="onComplete">Aliohjelma, joka suoritetaan kun lisäys on valmis.</param>
559        /// <returns>Operation-tyyppinen muuttuja, jolla voi hallita tapahtuvaa muutosta</returns>
560        public Operation AddOverTime( double change, double seconds, Action onComplete )
561        {
562            Operation op = AddOverTime( change, seconds );
563            op.Finished += onComplete;
564            return op;
565        }
566
567        /// <summary>
568        /// Lisää tietyn summan mittariin tasaisesti tietyn ajan sisällä.
569        /// </summary>
570        /// <param name="change">Kuinka paljon lisätään</param>
571        /// <param name="seconds">Aika joka lisämiseen kuluu</param>
572        /// <returns>Operation-tyyppinen muuttuja, jolla voi hallita tapahtuvaa muutosta</returns>
573        public Operation AddOverTime( double change, double seconds )
574        {
575            Operation op = new DoubleMeterAddOperation( this, change, seconds );
576            op.Finished += delegate { operations.Remove( op ); };
577            op.Stopped += delegate { operations.Remove( op ); };
578            operations.Add( op );
579            return op;
580        }
581
582        /// <summary>
583        /// Pysäyttää AddOverTime-metodilla tehtävät lisäykset mittariin.
584        /// </summary>
585        public void Stop()
586        {
587            while ( operations.Count > 0 )
588                operations[0].Stop();
589        }
590
591        internal override double GetValue()
592        {
593            return Value;
594        }
595
596        internal override double GetMinValue()
597        {
598            return MinValue;
599        }
600
601        internal override double GetMaxValue()
602        {
603            return MaxValue;
604        }
605    }
606}
Note: See TracBrowser for help on using the repository browser.