001/*
002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003 * Copyright (C) 2007 - JScience (http://jscience.org/)
004 * All rights reserved.
005 * 
006 * Permission to use, copy, modify, and distribute this software is
007 * freely granted, provided that this notice is preserved.
008 */
009package javax.measure;
010
011import javax.measure.converter.UnitConverter;
012import javax.measure.quantity.Quantity;
013import javax.measure.unit.CompoundUnit;
014import javax.measure.unit.Unit;
015
016/**
017 * <p> This class represents a measurement vector of two or more dimensions.
018 *     For example:[code]
019 *         VectorMeasure<Length> dimension = VectorMeasure.valueOf(12.0, 30.0, 40.0, MILLIMETER);
020 *         VectorMeasure<Velocity> v2d = VectorMeasure.valueOf(-2.2, -3.0, KNOTS);
021 *         VectorMeasure<ElectricCurrent> c2d = VectorMeasure.valueOf(-7.3, 3.5, NANOAMPERE);
022 *     [/code]
023 * </p>
024 *     
025 * <p> Subclasses may provide fixed dimensions specializations:[code]
026 *         class Velocity2D extends VectorMeasure<Velocity> {
027 *              public Velocity2D(double x, double y, Unit<Velocity> unit) {
028 *                  ...
029 *              }
030 *         }
031 *     [/code]</p>
032 *     
033 * <p> Measurement vectors may use {@link CompoundUnit compound units}:[code]
034 *     VectorMeasure<Angle> latLong = VectorMeasure.valueOf(12.345, 22.23, DEGREE_ANGLE);
035 *     Unit<Angle> HOUR_MINUTE_SECOND_ANGLE = DEGREE_ANGLE.compound(MINUTE_ANGLE).compound(SECOND_ANGLE);
036 *     System.out.println(latLong.to(HOUR_MINUTE_SECOND_ANGLE));
037 *     
038 *     > [12°19'42", 22°12'48"] [/code]</p>
039 *     
040 * <p> Instances of this class (and sub-classes) are immutable.</p>    
041 *     
042 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
043 * @version 4.3, October 3, 2007
044 */
045public abstract class VectorMeasure<Q extends Quantity> extends Measure<double[], Q> {
046    
047    /**
048     * Default constructor (for sub-classes). 
049     */
050    protected VectorMeasure() {
051    }
052
053    /**
054     * Returns a 2-dimensional measurement vector.
055     * 
056     * @param x the first vector component value.
057     * @param y the second vector component value.
058     * @param unit the measurement unit.
059     */
060    public static <Q extends Quantity> VectorMeasure<Q> valueOf(
061            double x, double y, Unit<Q> unit) {
062        return new TwoDimensional<Q>(x, y, unit);
063    }
064
065    /**
066     * Returns a 3-dimensional measurement vector.
067     * 
068     * @param x the first vector component value.
069     * @param y the second vector component value.
070     * @param z the third vector component value.
071     * @param unit the measurement unit.
072     */
073    public static <Q extends Quantity> VectorMeasure<Q> valueOf(
074            double x, double y, double z, Unit<Q> unit) {
075        return new ThreeDimensional<Q>(x, y, z, unit);
076    }
077
078    /**
079     * Returns a multi-dimensional measurement vector.
080     * 
081     * @param components the vector component values.
082     * @param unit the measurement unit.
083     */
084    public static <Q extends Quantity> VectorMeasure<Q> valueOf(double[] components, 
085            Unit<Q> unit) {
086        return new MultiDimensional<Q>(components, unit);
087    }
088    
089    /**
090     * Returns the measurement vector equivalent to this one but stated in the 
091     * specified unit.
092     * 
093     * @param unit the new measurement unit.
094     * @return the vector measure stated in the specified unit.
095     */
096    public abstract VectorMeasure<Q> to(Unit<Q> unit);
097
098    /**
099     * Returns the norm of this measurement vector stated in the specified
100     * unit.
101     * 
102     * @param unit the unit in which the norm is stated.
103     * @return <code>|this|</code>
104     */
105    public abstract double doubleValue(Unit<Q> unit);
106    
107    /**
108     * Returns the <code>String</code> representation of this measurement
109     * vector (for example <code>[2.3 m/s, 5.6 m/s]</code>).
110     * 
111     * @return the textual representation of the measurement vector.
112     */
113    public String toString() {
114        double[] values = getValue();
115        Unit<Q> unit = getUnit();
116        StringBuffer tmp = new StringBuffer();
117        tmp.append('[');
118        for (double v : values) {
119            if (tmp.length() > 1) {
120                tmp.append(", ");
121            }
122            if (unit instanceof CompoundUnit) {
123                MeasureFormat.DEFAULT.formatCompound(v, unit, tmp, null);
124            } else {
125                tmp.append(v).append(" ").append(unit);
126            }
127        }
128        tmp.append("] ");
129        return tmp.toString();
130    }
131
132    // Holds 2-dimensional implementation.
133    private static class TwoDimensional<Q extends Quantity> extends VectorMeasure<Q> {
134        
135        private final double _x;
136        
137        private final double _y;
138        
139        private final Unit<Q> _unit;
140        
141        private TwoDimensional(double x, double y, Unit<Q> unit) {
142            _x = x;
143            _y = y;
144            _unit = unit;
145            
146        }
147        @Override
148        public double doubleValue(Unit<Q> unit) {
149            double norm = Math.sqrt(_x * _x + _y * _y); 
150            if ((unit == _unit) || (unit.equals(_unit)))
151                return norm;
152            return _unit.getConverterTo(unit).convert(norm);            
153        }
154
155        @Override
156        public Unit<Q> getUnit() {
157            return _unit;
158        }
159
160        @Override
161        public double[] getValue() {
162            return new double[] { _x, _y };
163        }
164
165        @Override
166        public TwoDimensional<Q> to(Unit<Q> unit) {
167            if ((unit == _unit) || (unit.equals(_unit)))
168                return this;
169            UnitConverter cvtr = _unit.getConverterTo(unit);
170            return new TwoDimensional<Q>(cvtr.convert(_x), cvtr.convert(_y), unit); 
171        } 
172
173        private static final long serialVersionUID = 1L;
174
175    }
176    
177    // Holds 3-dimensional implementation.
178    private static class ThreeDimensional<Q extends Quantity> extends VectorMeasure<Q> {
179        
180        private final double _x;
181        
182        private final double _y;
183        
184        private final double _z;
185        
186        private final Unit<Q> _unit;
187        
188        private ThreeDimensional(double x, double y, double z, Unit<Q> unit) {
189            _x = x;
190            _y = y;
191            _z = z;
192            _unit = unit;
193            
194        }
195        @Override
196        public double doubleValue(Unit<Q> unit) {
197            double norm = Math.sqrt(_x * _x + _y * _y + _z * _z); 
198            if ((unit == _unit) || (unit.equals(_unit)))
199                return norm;
200            return _unit.getConverterTo(unit).convert(norm);            
201        }
202
203        @Override
204        public Unit<Q> getUnit() {
205            return _unit;
206        }
207
208        @Override
209        public double[] getValue() {
210            return new double[] { _x, _y, _z };
211        }
212
213        @Override
214        public ThreeDimensional<Q> to(Unit<Q> unit) {
215            if ((unit == _unit) || (unit.equals(_unit)))
216                return this;
217            UnitConverter cvtr = _unit.getConverterTo(unit);
218            return new ThreeDimensional<Q>(cvtr.convert(_x), cvtr.convert(_y), cvtr.convert(_z), unit); 
219        } 
220
221        private static final long serialVersionUID = 1L;
222
223    }
224    // Holds multi-dimensional implementation.
225    private static class MultiDimensional<Q extends Quantity> extends VectorMeasure<Q> {
226        
227        private final double[] _components;
228        
229        private final Unit<Q> _unit;
230        
231        private MultiDimensional(double[] components, Unit<Q> unit) {
232            _components = components.clone();
233            _unit = unit;            
234        }
235        
236        @Override
237        public double doubleValue(Unit<Q> unit) {
238            double normSquare = _components[0] * _components[0];
239            for (int i=1, n=_components.length; i < n;) {
240                double d = _components[i++];
241                normSquare += d * d;
242            }
243            if ((unit == _unit) || (unit.equals(_unit)))
244                return Math.sqrt(normSquare);
245            return _unit.getConverterTo(unit).convert(Math.sqrt(normSquare));            
246        }
247
248        @Override
249        public Unit<Q> getUnit() {
250            return _unit;
251        }
252
253        @Override
254        public double[] getValue() {
255            return _components.clone();
256        }
257
258        @Override
259        public MultiDimensional<Q> to(Unit<Q> unit) {
260            if ((unit == _unit) || (unit.equals(_unit)))
261                return this;
262            UnitConverter cvtr = _unit.getConverterTo(unit);
263            double[] newValues = new double[_components.length];
264            for (int i=0; i < _components.length; i++) {
265                newValues[i] = cvtr.convert(_components[i]);
266            }
267            return new MultiDimensional<Q>(newValues, unit); 
268        } 
269
270        private static final long serialVersionUID = 1L;
271
272    }
273}