001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.math;
020
021import icy.common.CollapsibleEvent;
022import icy.common.UpdateEventHandler;
023import icy.common.listener.ChangeListener;
024import icy.file.xml.XMLPersistent;
025import icy.type.TypeUtil;
026import icy.type.collection.array.ArrayUtil;
027import icy.util.XMLUtil;
028
029import javax.swing.event.EventListenerList;
030
031import org.w3c.dom.Node;
032
033/**
034 * @author stephane
035 */
036public class Scaler implements ChangeListener, XMLPersistent
037{
038    private enum ScalerRange
039    {
040        SR_ABSIN, SR_IN, SR_OUT
041    };
042
043    private static final String ID_ABSLEFTIN = "absleftin";
044    private static final String ID_ABSRIGHTIN = "absrightin";
045    private static final String ID_LEFTIN = "leftin";
046    private static final String ID_RIGHTIN = "rightin";
047    private static final String ID_LEFTOUT = "leftout";
048    private static final String ID_RIGHTOUT = "rightout";
049    private static final String ID_INTEGERDATA = "integerdata";
050    private static final String ID_CANCROSS = "cancross";
051
052    private double absLeftIn;
053    private double absRightIn;
054
055    private double leftIn;
056    private double rightIn;
057
058    private double leftOut;
059    private double rightOut;
060
061    private double scaler;
062    private double unscaler;
063
064    private boolean integerData;
065    private boolean canCross;
066    private boolean crossed;
067
068    public double scaleLK[];
069
070    private final EventListenerList listeners;
071
072    /**
073     * internal updater
074     */
075    private final UpdateEventHandler updater;
076
077    public static int indexOf(Scaler[] scalers, Scaler scaler)
078    {
079        for (int i = 0; i < scalers.length; i++)
080            if (scalers[i].equals(scaler))
081                return i;
082
083        return -1;
084    }
085
086    public static boolean contains(Scaler[] scalers, Scaler scaler)
087    {
088        return (indexOf(scalers, scaler) != -1);
089    }
090
091    /**
092     * 
093     */
094    public Scaler(double leftIn, double rightIn, double leftOut, double rightOut, boolean integerData)
095    {
096        this(leftIn, rightIn, leftIn, rightIn, leftOut, rightOut, integerData, false);
097    }
098
099    /**
100     * 
101     */
102    public Scaler(double leftIn, double rightIn, double leftOut, double rightOut, boolean integerData, boolean canCross)
103    {
104        this(leftIn, rightIn, leftIn, rightIn, leftOut, rightOut, integerData, canCross);
105    }
106
107    /**
108     * 
109     */
110    public Scaler(double absLeftIn, double absRightIn, double leftIn, double rightIn, double leftOut, double rightOut,
111            boolean integerData, boolean canCross)
112    {
113        super();
114
115        this.absLeftIn = absLeftIn;
116        this.absRightIn = absRightIn;
117        this.leftIn = leftIn;
118        this.rightIn = rightIn;
119        this.leftOut = leftOut;
120        this.rightOut = rightOut;
121        this.integerData = integerData;
122        this.canCross = canCross;
123
124        crossed = absLeftIn > absRightIn;
125
126        if (crossed && !canCross)
127            throw new IllegalArgumentException("Can't create scaler : left > right and canCross = false");
128
129        listeners = new EventListenerList();
130        updater = new UpdateEventHandler(this, false);
131
132        // update scaler
133        updateScaler(false);
134    }
135
136    /**
137     * Refresh the scale lookup table
138     */
139    private void updateLookup()
140    {
141        scaleLK = null;
142
143        if (integerData)
144        {
145            final boolean rangeOk;
146
147            if (crossed)
148                rangeOk = (absLeftIn <= 65535) && (absRightIn >= 0);
149            else
150                rangeOk = (absLeftIn >= 0) && (absRightIn <= 65535);
151
152            // use lookup table only for integer scalar value in [0..65535] range
153            if (rangeOk)
154            {
155                final int len;
156
157                if (crossed)
158                    len = (int) absRightIn;
159                else
160                    len = (int) absLeftIn;
161
162                scaleLK = new double[len];
163
164                // refresh lookup table data
165                for (int i = 0; i < len; i++)
166                    scaleLK[i] = scale(i);
167            }
168        }
169    }
170
171    /**
172     * Refresh the scaler value
173     */
174    private void updateScaler(boolean notify)
175    {
176        final double deltaIn = rightIn - leftIn;
177        final double deltaOut = rightOut - leftOut;
178
179        // delta null
180        if ((deltaIn == 0) || (deltaOut == 0))
181            scaler = 1;
182        else
183        {
184            scaler = deltaOut / deltaIn;
185            unscaler = deltaIn / deltaOut;
186        }
187
188        // refresh lookup table
189        updateLookup();
190
191        // notify scaler changed
192        if (notify)
193            changed();
194    }
195
196    private void checkBounds()
197    {
198        double l = leftIn;
199        double r = rightIn;
200
201        if (crossed)
202        {
203            // check absolute range first
204            if (l > absLeftIn)
205            {
206                l = absLeftIn;
207                if (r > l)
208                    r = l - Float.MIN_VALUE;
209            }
210            if (r < absRightIn)
211            {
212                r = absRightIn;
213                if (l < r)
214                    l = r + Float.MIN_VALUE;
215            }
216        }
217        else
218        {
219            // check absolute range first
220            if (l < absLeftIn)
221            {
222                l = absLeftIn;
223                if (r < l)
224                    r = l + Float.MIN_VALUE;
225            }
226            if (r > absRightIn)
227            {
228                r = absRightIn;
229                if (l > r)
230                    l = r - Float.MIN_VALUE;
231            }
232        }
233
234        // set left and right for input value
235        leftIn = l;
236        rightIn = r;
237    }
238
239    /**
240     * Sets the left and right value of specified range.
241     * 
242     * @param left
243     *        the new left value to set
244     * @param right
245     *        the new right value to set
246     * @param range
247     *        range to modify
248     * @param leftPrio
249     *        priority to left border (to resolve conflict)
250     */
251    private void setLeftRight(double left, double right, ScalerRange range, boolean leftPrio)
252    {
253        double l = left;
254        double r = right;
255
256        if ((!canCross) && (l > r))
257        {
258            if (leftPrio)
259                r = l + Float.MIN_VALUE;
260            else
261                l = r - Float.MIN_VALUE;
262        }
263
264        switch (range)
265        {
266            case SR_ABSIN:
267                // nothing to do
268                if ((absLeftIn == l) && (absRightIn == r))
269                    return;
270
271                // update crossed information
272                crossed = l > r;
273                // set absolute left and right for input value
274                absLeftIn = l;
275                absRightIn = r;
276                // adjust current left and right for input value if they are out bounds
277                checkBounds();
278                break;
279
280            case SR_IN:
281                // nothing to do
282                if ((leftIn == l) && (rightIn == r))
283                    return;
284
285                // update crossed information updated only on absolute
286                // crossed = l > r;
287                // set left and right for input value
288                leftIn = l;
289                rightIn = r;
290                // adjust current left and right for input value if they are out bounds
291                checkBounds();
292                break;
293
294            case SR_OUT:
295                // nothing to do
296                if ((leftOut == l) && (rightOut == r))
297                    return;
298
299                // set left and right for output value
300                leftOut = l;
301                rightOut = r;
302                break;
303        }
304
305        // update scaler
306        updateScaler(true);
307    }
308
309    /**
310     * Scale the value
311     * 
312     * @param value
313     *        value to scale
314     * @return scaled output value
315     */
316    public double scale(double value)
317    {
318        if (crossed)
319        {
320            if (value >= leftIn)
321                return leftOut;
322            else if (value <= rightIn)
323                return rightOut;
324            else
325                return ((value - leftIn) * scaler) + leftOut;
326        }
327
328        if (value <= leftIn)
329            return leftOut;
330        else if (value >= rightIn)
331            return rightOut;
332        else
333            return ((value - leftIn) * scaler) + leftOut;
334    }
335
336    /**
337     * Scale the value
338     * 
339     * @param value
340     *        value to scale
341     * @return scaled output value
342     */
343    public double unscale(double value)
344    {
345        if (crossed)
346        {
347            if (value >= leftOut)
348                return leftIn;
349            else if (value <= rightOut)
350                return rightIn;
351            else
352                return ((value - leftOut) * unscaler) + leftIn;
353        }
354
355        if (value <= leftOut)
356            return leftIn;
357        else if (value >= rightOut)
358            return rightIn;
359        else
360            return ((value - leftOut) * unscaler) + leftIn;
361    }
362
363    /**
364     * Scale each value in the "src" array and return result in "dest" array
365     * 
366     * @param src
367     *        array of byte (unscaled values)
368     * @param srcOffset
369     *        offset for src buffer
370     * @param dest
371     *        result as array of int (scaled values)
372     * @param destOffset
373     *        offset for dest buffer
374     * @param len
375     *        length to compute
376     * @param signed
377     *        signed/unsigned src data flag
378     */
379    public void scale(Object src, int srcOffset, int[] dest, int destOffset, int len, boolean signed)
380    {
381        if ((src == null) || (dest == null))
382            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
383
384        switch (ArrayUtil.getDataType(src))
385        {
386            case BYTE:
387                scale((byte[]) src, srcOffset, dest, destOffset, len, signed);
388                break;
389
390            case SHORT:
391                scale((short[]) src, srcOffset, dest, destOffset, len, signed);
392                break;
393
394            case INT:
395                scale((int[]) src, srcOffset, dest, destOffset, len, signed);
396                break;
397
398            case LONG:
399                scale((long[]) src, srcOffset, dest, destOffset, len, signed);
400                break;
401
402            case FLOAT:
403                scale((float[]) src, srcOffset, dest, destOffset, len);
404                break;
405
406            case DOUBLE:
407                scale((double[]) src, srcOffset, dest, destOffset, len);
408                break;
409        }
410    }
411
412    /**
413     * Scale each value in the "src" array and return result in "dest" array
414     * 
415     * @param src
416     *        array of byte (unscaled values)
417     * @param srcOffset
418     *        offset for src buffer
419     * @param dest
420     *        result as array of int (scaled values)
421     * @param destOffset
422     *        offset for dest buffer
423     * @param len
424     *        length to compute
425     * @param signed
426     *        signed/unsigned src data flag
427     */
428    public void scale(byte[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed)
429    {
430        if ((src == null) || (dest == null))
431            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
432
433        if (signed)
434        {
435            // signed
436            for (int i = 0; i < len; i++)
437                dest[destOffset + i] = (int) scale(src[srcOffset + i]);
438        }
439        else
440        {
441            // unsigned
442            for (int i = 0; i < len; i++)
443                dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i]));
444        }
445    }
446
447    /**
448     * Scale each value in the "src" array and return result in "dest" array
449     * 
450     * @param src
451     *        array of short (unscaled values)
452     * @param srcOffset
453     *        offset for src buffer
454     * @param dest
455     *        result as array of int (scaled values)
456     * @param destOffset
457     *        offset for dest buffer
458     * @param len
459     *        length to compute
460     * @param signed
461     *        signed/unsigned src data flag
462     */
463    public void scale(short[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed)
464    {
465        if ((src == null) || (dest == null))
466            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
467
468        if (signed)
469        {
470            // signed
471            for (int i = 0; i < len; i++)
472                dest[destOffset + i] = (int) scale(src[srcOffset + i]);
473        }
474        else
475        {
476            // unsigned
477            for (int i = 0; i < len; i++)
478                dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i]));
479        }
480    }
481
482    /**
483     * Scale each value in the "src" array and return result in "dest" array
484     * 
485     * @param src
486     *        array of int (unscaled values)
487     * @param srcOffset
488     *        offset for src buffer
489     * @param dest
490     *        result as array of int (scaled values)
491     * @param destOffset
492     *        offset for dest buffer
493     * @param len
494     *        length to compute
495     * @param signed
496     *        signed/unsigned src data flag
497     */
498    public void scale(int[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed)
499    {
500        if ((src == null) || (dest == null))
501            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
502
503        if (signed)
504        {
505            // signed
506            for (int i = 0; i < len; i++)
507                dest[destOffset + i] = (int) scale(src[srcOffset + i]);
508        }
509        else
510        {
511            // unsigned
512            for (int i = 0; i < len; i++)
513                dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i]));
514        }
515    }
516
517    /**
518     * Scale each value in the "src" array and return result in "dest" array
519     * 
520     * @param src
521     *        array of long (unscaled values)
522     * @param srcOffset
523     *        offset for src buffer
524     * @param dest
525     *        result as array of int (scaled values)
526     * @param destOffset
527     *        offset for dest buffer
528     * @param len
529     *        length to compute
530     * @param signed
531     *        signed/unsigned src data flag
532     */
533    public void scale(long[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed)
534    {
535        if ((src == null) || (dest == null))
536            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
537
538        if (signed)
539        {
540            // signed
541            for (int i = 0; i < len; i++)
542                dest[destOffset + i] = (int) scale(src[srcOffset + i]);
543        }
544        else
545        {
546            // unsigned
547            for (int i = 0; i < len; i++)
548                dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i]));
549        }
550    }
551
552    /**
553     * Scale array
554     * 
555     * @param src
556     *        array of float (unscaled values)
557     * @param srcOffset
558     *        offset for src buffer
559     * @param dest
560     *        result as array of int (scaled values)
561     * @param destOffset
562     *        offset for dest buffer
563     * @param len
564     *        length to compute
565     */
566    public void scale(float[] src, int srcOffset, int[] dest, int destOffset, int len)
567    {
568        if ((src == null) || (dest == null))
569            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
570
571        for (int i = 0; i < len; i++)
572            dest[destOffset + i] = (int) scale(src[srcOffset + i]);
573    }
574
575    /**
576     * Scale array
577     * 
578     * @param src
579     *        array of double (unscaled values)
580     * @param srcOffset
581     *        offset for src buffer
582     * @param dest
583     *        result as array of int (scaled values)
584     * @param destOffset
585     *        offset for dest buffer
586     * @param len
587     *        length to compute
588     */
589    public void scale(double[] src, int srcOffset, int[] dest, int destOffset, int len)
590    {
591        if ((src == null) || (dest == null))
592            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
593
594        for (int i = 0; i < len; i++)
595            dest[destOffset + i] = (int) scale(src[srcOffset + i]);
596    }
597
598    /**
599     * Scale each value in the "src" array and return result in "dest" array
600     * 
601     * @param src
602     *        array of byte (unscaled values)
603     * @param srcOffset
604     *        offset for src buffer
605     * @param dest
606     *        result as array of double (scaled values)
607     * @param destOffset
608     *        offset for dest buffer
609     * @param len
610     *        length to compute
611     * @param signed
612     *        signed/unsigned src data flag
613     */
614    public void scale(byte[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed)
615    {
616        if ((src == null) || (dest == null))
617            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
618
619        if (signed)
620        {
621            // signed
622            for (int i = 0; i < len; i++)
623                dest[destOffset + i] = scale(src[srcOffset + i]);
624        }
625        else
626        {
627            // unsigned
628            for (int i = 0; i < len; i++)
629                dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i]));
630        }
631    }
632
633    /**
634     * Scale each value in the "src" array and return result in "dest" array
635     * 
636     * @param src
637     *        array of short (unscaled values)
638     * @param srcOffset
639     *        offset for src buffer
640     * @param dest
641     *        result as array of double (scaled values)
642     * @param destOffset
643     *        offset for dest buffer
644     * @param len
645     *        length to compute
646     * @param signed
647     *        signed/unsigned src data flag
648     */
649    public void scale(short[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed)
650    {
651        if ((src == null) || (dest == null))
652            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
653
654        if (signed)
655        {
656            // signed
657            for (int i = 0; i < len; i++)
658                dest[destOffset + i] = scale(src[srcOffset + i]);
659        }
660        else
661        {
662            // unsigned
663            for (int i = 0; i < len; i++)
664                dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i]));
665        }
666    }
667
668    /**
669     * Scale each value in the "src" array and return result in "dest" array
670     * 
671     * @param src
672     *        array of int (unscaled values)
673     * @param srcOffset
674     *        offset for src buffer
675     * @param dest
676     *        result as array of double (scaled values)
677     * @param destOffset
678     *        offset for dest buffer
679     * @param len
680     *        length to compute
681     * @param signed
682     *        signed/unsigned src data flag
683     */
684    public void scale(int[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed)
685    {
686        if ((src == null) || (dest == null))
687            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
688
689        if (signed)
690        {
691            // signed
692            for (int i = 0; i < len; i++)
693                dest[destOffset + i] = scale(src[srcOffset + i]);
694        }
695        else
696        {
697            // unsigned
698            for (int i = 0; i < len; i++)
699                dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i]));
700        }
701    }
702
703    /**
704     * Scale each value in the "src" array and return result in "dest" array
705     * 
706     * @param src
707     *        array of long (unscaled values)
708     * @param srcOffset
709     *        offset for src buffer
710     * @param dest
711     *        result as array of double (scaled values)
712     * @param destOffset
713     *        offset for dest buffer
714     * @param len
715     *        length to compute
716     * @param signed
717     *        signed/unsigned src data flag
718     */
719    public void scale(long[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed)
720    {
721        if ((src == null) || (dest == null))
722            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
723
724        if (signed)
725        {
726            // signed
727            for (int i = 0; i < len; i++)
728                dest[destOffset + i] = scale(src[srcOffset + i]);
729        }
730        else
731        {
732            // unsigned
733            for (int i = 0; i < len; i++)
734                dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i]));
735        }
736    }
737
738    /**
739     * Scale array
740     * 
741     * @param src
742     *        array of float (unscaled values)
743     * @param srcOffset
744     *        offset for src buffer
745     * @param dest
746     *        result as array of double (scaled values)
747     * @param destOffset
748     *        offset for dest buffer
749     * @param len
750     *        length to compute
751     */
752    public void scale(float[] src, int srcOffset, double[] dest, int destOffset, int len)
753    {
754        if ((src == null) || (dest == null))
755            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
756
757        for (int i = 0; i < len; i++)
758            dest[destOffset + i] = scale(src[srcOffset + i]);
759    }
760
761    /**
762     * Scale array
763     * 
764     * @param src
765     *        array of double (unscaled values)
766     * @param srcOffset
767     *        offset for src buffer
768     * @param dest
769     *        result as array of double (scaled values)
770     * @param destOffset
771     *        offset for dest buffer
772     * @param len
773     *        length to compute
774     */
775    public void scale(double[] src, int srcOffset, double[] dest, int destOffset, int len)
776    {
777        if ((src == null) || (dest == null))
778            throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !");
779
780        for (int i = 0; i < len; i++)
781            dest[destOffset + i] = scale(src[srcOffset + i]);
782    }
783
784    /**
785     * Scale array
786     * 
787     * @param data
788     *        array of float value to scale
789     * @param offset
790     *        offset for buffer
791     * @param len
792     *        length to compute
793     */
794    public void scale(float[] data, int offset, int len)
795    {
796        if (data == null)
797            throw new IllegalArgumentException("Parameters 'data' should not be null !");
798
799        for (int i = 0; i < len; i++)
800            data[offset + i] = (float) scale(data[i]);
801    }
802
803    /**
804     * Scale array
805     * 
806     * @param data
807     *        array of double value to scale
808     * @param offset
809     *        offset for buffer
810     * @param len
811     *        length to compute
812     */
813    public void scale(double[] data, int offset, int len)
814    {
815        if (data == null)
816            throw new IllegalArgumentException("Parameters 'data' should not be null !");
817
818        for (int i = 0; i < len; i++)
819            data[offset + i] = scale(data[i]);
820    }
821
822    /**
823     * Scale each value in the "src" array and return result in "dest" array
824     * 
825     * @param src
826     *        array of byte (unscaled values)
827     * @param dest
828     *        result as array of int (scaled values)
829     * @param signed
830     *        signed/unsigned src data flag
831     */
832    public void scale(Object src, int[] dest, boolean signed)
833    {
834        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
835    }
836
837    /**
838     * Scale each value in the "src" array and return result in "dest" array
839     * 
840     * @param src
841     *        array of byte (unscaled values)
842     * @param dest
843     *        result as array of int (scaled values)
844     * @param signed
845     *        signed/unsigned src data flag
846     */
847    public void scale(byte[] src, int[] dest, boolean signed)
848    {
849        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
850    }
851
852    /**
853     * Scale each value in the "src" array and return result in "dest" array
854     * 
855     * @param src
856     *        array of short (unscaled values)
857     * @param dest
858     *        result as array of int (scaled values)
859     * @param signed
860     *        signed/unsigned src data flag
861     */
862    public void scale(short[] src, int[] dest, boolean signed)
863    {
864        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
865    }
866
867    /**
868     * Scale each value in the "src" array and return result in "dest" array
869     * 
870     * @param src
871     *        array of int (unscaled values)
872     * @param dest
873     *        result as array of int (scaled values)
874     * @param signed
875     *        signed/unsigned src data flag
876     */
877    public void scale(int[] src, int[] dest, boolean signed)
878    {
879        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
880    }
881
882    /**
883     * Scale array
884     * 
885     * @param src
886     *        array of float (unscaled values)
887     * @param dest
888     *        result as array of int (scaled values)
889     */
890    public void scale(float[] src, int[] dest)
891    {
892        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src));
893    }
894
895    /**
896     * Scale array
897     * 
898     * @param src
899     *        array of double (unscaled values)
900     * @param dest
901     *        result as array of int (scaled values)
902     */
903    public void scale(double[] src, int[] dest)
904    {
905        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src));
906    }
907
908    /**
909     * Scale each value in the "src" array and return result in "dest" array
910     * 
911     * @param src
912     *        array of byte (unscaled values)
913     * @param dest
914     *        result as array of double (scaled values)
915     * @param signed
916     *        signed/unsigned src data flag
917     */
918    public void scale(byte[] src, double[] dest, boolean signed)
919    {
920        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
921    }
922
923    /**
924     * Scale each value in the "src" array and return result in "dest" array
925     * 
926     * @param src
927     *        array of short (unscaled values)
928     * @param dest
929     *        result as array of double (scaled values)
930     * @param signed
931     *        signed/unsigned src data flag
932     */
933    public void scale(short[] src, double[] dest, boolean signed)
934    {
935        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
936    }
937
938    /**
939     * Scale each value in the "src" array and return result in "dest" array
940     * 
941     * @param src
942     *        array of int (unscaled values)
943     * @param dest
944     *        result as array of double (scaled values)
945     * @param signed
946     *        signed/unsigned src data flag
947     */
948    public void scale(int[] src, double[] dest, boolean signed)
949    {
950        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed);
951    }
952
953    /**
954     * Scale array
955     * 
956     * @param src
957     *        array of float (unscaled values)
958     * @param dest
959     *        result as array of double (scaled values)
960     */
961    public void scale(float[] src, double[] dest)
962    {
963        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src));
964    }
965
966    /**
967     * Scale array
968     * 
969     * @param src
970     *        array of double (unscaled values)
971     * @param dest
972     *        result as array of double (scaled values)
973     */
974    public void scale(double[] src, double[] dest)
975    {
976        scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src));
977    }
978
979    /**
980     * Scale array
981     * 
982     * @param data
983     *        array of float value to scale
984     */
985    public void scale(float[] data)
986    {
987        scale(data, 0, data.length);
988    }
989
990    /**
991     * Scale array
992     * 
993     * @param data
994     *        array of double value to scale
995     */
996    public void scale(double[] data)
997    {
998        scale(data, 0, data.length);
999    }
1000
1001    /**
1002     * Return the scaler value
1003     * 
1004     * @return the scaler value
1005     */
1006    public double getScaler()
1007    {
1008        return scaler;
1009    }
1010
1011    /**
1012     * @return the integerData
1013     */
1014    public boolean isIntegerData()
1015    {
1016        return integerData;
1017    }
1018
1019    /**
1020     * @return the crossed flag
1021     */
1022    public boolean isCrossed()
1023    {
1024        return crossed;
1025    }
1026
1027    /**
1028     * Return true if scaler doesn't change value (input = output)
1029     */
1030    public boolean isNull()
1031    {
1032        return (leftIn == leftOut) && (rightIn == rightOut);
1033    }
1034
1035    /**
1036     * @return the canCross
1037     */
1038    public boolean getCanCross()
1039    {
1040        return canCross;
1041    }
1042
1043    /**
1044     * @param canCross
1045     *        the canCross to set
1046     */
1047    public void setCanCross(boolean canCross)
1048    {
1049        if (this.canCross != canCross)
1050        {
1051            // crossed and we can't anymore...
1052            if (!canCross && crossed)
1053            {
1054                final double ali = absLeftIn;
1055                final double ari = absRightIn;
1056                final double li = leftIn;
1057                final double ri = rightIn;
1058
1059                // uncross
1060                setLeftRight(ari, ali, ScalerRange.SR_ABSIN, true);
1061                setLeftRight(ri, li, ScalerRange.SR_IN, true);
1062            }
1063
1064            this.canCross = canCross;
1065        }
1066    }
1067
1068    /**
1069     * @return the absLeftIn
1070     */
1071    public double getAbsLeftIn()
1072    {
1073        return absLeftIn;
1074    }
1075
1076    /**
1077     * @param absLeftIn
1078     *        the absLeftIn to set
1079     */
1080    public void setAbsLeftIn(double absLeftIn)
1081    {
1082        setLeftRight(absLeftIn, absRightIn, ScalerRange.SR_ABSIN, true);
1083    }
1084
1085    /**
1086     * @return the absRightIn
1087     */
1088    public double getAbsRightIn()
1089    {
1090        return absRightIn;
1091    }
1092
1093    /**
1094     * @param absRightIn
1095     *        the absRightIn to set
1096     */
1097    public void setAbsRightIn(double absRightIn)
1098    {
1099        setLeftRight(absLeftIn, absRightIn, ScalerRange.SR_ABSIN, false);
1100    }
1101
1102    /**
1103     * @return the leftIn
1104     */
1105    public double getLeftIn()
1106    {
1107        return leftIn;
1108    }
1109
1110    /**
1111     * @param leftIn
1112     *        the leftIn to set
1113     */
1114    public void setLeftIn(double leftIn)
1115    {
1116        setLeftRight(leftIn, rightIn, ScalerRange.SR_IN, true);
1117    }
1118
1119    /**
1120     * @return the rightIn
1121     */
1122    public double getRightIn()
1123    {
1124        return rightIn;
1125    }
1126
1127    /**
1128     * @param rightIn
1129     *        the rightIn to set
1130     */
1131    public void setRightIn(double rightIn)
1132    {
1133        setLeftRight(leftIn, rightIn, ScalerRange.SR_IN, false);
1134    }
1135
1136    /**
1137     * @return the leftOut
1138     */
1139    public double getLeftOut()
1140    {
1141        return leftOut;
1142    }
1143
1144    /**
1145     * @param leftOut
1146     *        the leftOut to set
1147     */
1148    public void setLeftOut(double leftOut)
1149    {
1150        setLeftRight(leftOut, rightOut, ScalerRange.SR_OUT, true);
1151    }
1152
1153    /**
1154     * @return the rightOut
1155     */
1156    public double getRightOut()
1157    {
1158        return rightOut;
1159    }
1160
1161    /**
1162     * @param rightOut
1163     *        the rightOut to set
1164     */
1165    public void setRightOut(double rightOut)
1166    {
1167        setLeftRight(leftOut, rightOut, ScalerRange.SR_OUT, false);
1168    }
1169
1170    /**
1171     * @param left
1172     *        the leftAbsIn to set
1173     * @param right
1174     *        the rightAbsIn to set
1175     */
1176    public void setAbsLeftRightIn(double left, double right)
1177    {
1178        setLeftRight(left, right, ScalerRange.SR_ABSIN, false);
1179    }
1180
1181    /**
1182     * @param left
1183     *        the leftIn to set
1184     * @param right
1185     *        the rightIn to set
1186     */
1187    public void setLeftRightIn(double left, double right)
1188    {
1189        setLeftRight(left, right, ScalerRange.SR_IN, false);
1190    }
1191
1192    /**
1193     * @param left
1194     *        the leftOut to set
1195     * @param right
1196     *        the rightOut to set
1197     */
1198    public void setLeftRightOut(double left, double right)
1199    {
1200        setLeftRight(left, right, ScalerRange.SR_OUT, false);
1201    }
1202
1203    /**
1204     * fire event
1205     */
1206    public void fireEvent(ScalerEvent e)
1207    {
1208        for (ScalerListener listener : listeners.getListeners(ScalerListener.class))
1209            listener.scalerChanged(e);
1210    }
1211
1212    /**
1213     * Add a listener
1214     * 
1215     * @param listener
1216     */
1217    public void addListener(ScalerListener listener)
1218    {
1219        listeners.add(ScalerListener.class, listener);
1220    }
1221
1222    /**
1223     * Remove a listener
1224     * 
1225     * @param listener
1226     */
1227    public void removeListener(ScalerListener listener)
1228    {
1229        listeners.remove(ScalerListener.class, listener);
1230    }
1231
1232    @Override
1233    public void onChanged(CollapsibleEvent compare)
1234    {
1235        final ScalerEvent event = (ScalerEvent) compare;
1236
1237        // notify listener we have changed
1238        fireEvent(event);
1239    }
1240
1241    /**
1242     * process on change
1243     */
1244    private void changed()
1245    {
1246        // handle changed via updater object
1247        updater.changed(new ScalerEvent(this));
1248    }
1249
1250    public void beginUpdate()
1251    {
1252        updater.beginUpdate();
1253    }
1254
1255    public void endUpdate()
1256    {
1257        updater.endUpdate();
1258    }
1259
1260    public boolean isUpdating()
1261    {
1262        return updater.isUpdating();
1263    }
1264
1265    @Override
1266    public boolean loadFromXML(Node node)
1267    {
1268        if (node == null)
1269            return false;
1270
1271        beginUpdate();
1272        try
1273        {
1274            double l, r;
1275
1276            setCanCross(XMLUtil.getElementBooleanValue(node, ID_CANCROSS, false));
1277            integerData = XMLUtil.getElementBooleanValue(node, ID_INTEGERDATA, false);
1278
1279            l = XMLUtil.getElementDoubleValue(node, ID_ABSLEFTIN, 0d);
1280            r = XMLUtil.getElementDoubleValue(node, ID_ABSRIGHTIN, 0d);
1281            setLeftRight(l, r, ScalerRange.SR_ABSIN, true);
1282            l = XMLUtil.getElementDoubleValue(node, ID_LEFTIN, 0d);
1283            r = XMLUtil.getElementDoubleValue(node, ID_RIGHTIN, 0d);
1284            setLeftRight(l, r, ScalerRange.SR_IN, true);
1285            l = XMLUtil.getElementDoubleValue(node, ID_LEFTOUT, 0d);
1286            r = XMLUtil.getElementDoubleValue(node, ID_RIGHTOUT, 0d);
1287            setLeftRight(l, r, ScalerRange.SR_OUT, true);
1288        }
1289        finally
1290        {
1291            endUpdate();
1292        }
1293
1294        return true;
1295    }
1296
1297    @Override
1298    public boolean saveToXML(Node node)
1299    {
1300        if (node == null)
1301            return false;
1302
1303        XMLUtil.setElementBooleanValue(node, ID_CANCROSS, getCanCross());
1304        XMLUtil.setElementBooleanValue(node, ID_INTEGERDATA, isIntegerData());
1305
1306        XMLUtil.setElementDoubleValue(node, ID_ABSLEFTIN, getAbsLeftIn());
1307        XMLUtil.setElementDoubleValue(node, ID_ABSRIGHTIN, getAbsRightIn());
1308        XMLUtil.setElementDoubleValue(node, ID_LEFTIN, getLeftIn());
1309        XMLUtil.setElementDoubleValue(node, ID_RIGHTIN, getRightIn());
1310        XMLUtil.setElementDoubleValue(node, ID_LEFTOUT, getLeftOut());
1311        XMLUtil.setElementDoubleValue(node, ID_RIGHTOUT, getRightOut());
1312
1313        return true;
1314    }
1315
1316}