001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data.hydra;
030
031import java.rmi.RemoteException;
032
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import ucar.visad.display.Displayable;
037import ucar.visad.display.LineDrawing;
038import visad.CartesianProductCoordinateSystem;
039import visad.CoordinateSystem;
040import visad.DataRenderer;
041import visad.DisplayRealType;
042import visad.DisplayTupleType;
043import visad.FlatField;
044import visad.FunctionType;
045import visad.GridCoordinateSystem;
046import visad.Gridded2DSet;
047import visad.Gridded3DSet;
048import visad.GriddedSet;
049import visad.IdentityCoordinateSystem;
050import visad.RealTupleType;
051import visad.RealType;
052import visad.ScalarMap;
053import visad.VisADException;
054
055public class SubsetRubberBandBox extends LineDrawing {
056
057    private static final Logger logger = LoggerFactory.getLogger(SubsetRubberBandBox.class);
058
059    /** x type for the box */
060    private RealType xType;
061
062    /** y type for the box */
063    private RealType yType;
064
065    /** renderer */
066    private MyRubberBandBoxRendererJ3D rubberBandBox;
067
068    /** bounds defined by the rubber band box */
069    private Gridded2DSet bounds;
070
071    /** mouse event mask */
072    private int mask;
073
074    private boolean lastBoxOn;
075    
076    private int elemMax = -1;
077    private int lineMax = -1;
078
079    private CoordinateSystem dataCS;
080
081    private static int count = 0;
082
083    /**
084     * Construct a RubberBandBox using xType as the X coordinate and
085     * yType as the Y coordinate of the box.
086     *
087     * @param data
088     * @param displayCS
089     *
090     * @throws VisADException VisAD error
091     * @throws RemoteException Remote error
092     */
093    
094    public SubsetRubberBandBox(FlatField data, CoordinateSystem displayCS)
095            throws VisADException, RemoteException {
096        this(false, data, displayCS, 0);
097    }
098
099    public SubsetRubberBandBox(FlatField data, CoordinateSystem displayCS, int mask)
100            throws VisADException, RemoteException {
101        this(false, data, displayCS, mask);
102    }
103
104    public SubsetRubberBandBox(boolean isLL, FlatField data, CoordinateSystem displayCS, int mask)
105            throws VisADException, RemoteException {
106        this(isLL, data, displayCS, mask, true);
107    }
108
109    public SubsetRubberBandBox(FlatField data, CoordinateSystem displayCS, int mask, boolean lastBoxOn)
110            throws VisADException, RemoteException {
111        this(false, data, displayCS, mask, lastBoxOn);
112    }
113
114
115
116    /**
117     * Construct a RubberBandBox using xType as the X coordinate and
118     * yType as the Y coordinate of the box.
119     *
120     * @param isLL
121     * @param data
122     * @param displayCS
123     * @param mask Key mask to use for rubberbanding
124     * @param lastBoxOn
125     *
126     * @throws VisADException   VisAD error
127     * @throws RemoteException   Remote error
128     */
129    
130    public SubsetRubberBandBox(boolean isLL, FlatField data, CoordinateSystem displayCS, int mask, boolean lastBoxOn)
131            throws VisADException, RemoteException {
132        super("Subset Rubber Band Box");
133
134        this.lastBoxOn =  lastBoxOn;
135
136        RealTupleType rtype = ((FunctionType)data.getType()).getDomain();
137        dataCS = rtype.getCoordinateSystem();
138        if (dataCS == null) {
139          dataCS = new GridCoordinateSystem((GriddedSet)data.getDomainSet());
140        }
141
142        IdentityCoordinateSystem iCS =
143             new IdentityCoordinateSystem(
144                   new RealTupleType(new RealType[] {RealType.getRealType("ZZtop")}));
145
146        CoordinateSystem cs =
147             new CartesianProductCoordinateSystem(new CoordinateSystem[] {dataCS, iCS});
148
149        CoordinateSystem new_cs = new DataToDisplayCoordinateSystem(isLL, cs, displayCS);
150        
151
152        DisplayRealType displayLineType =
153           new DisplayRealType("displayLine_"+count, true, 0.0, 10000.0, 0.0, null);
154        DisplayRealType displayElemType =
155           new DisplayRealType("displayElem_"+count, true, 0.0, 10000.0, 0.0, null);
156        DisplayRealType displayAltType =
157           new DisplayRealType("displayAlt_"+count, true, -1.0, 1.0, 0.0, null);
158        DisplayTupleType dtt =
159           new DisplayTupleType(new DisplayRealType[] {displayLineType, displayElemType, displayAltType}, new_cs);
160
161
162        RealType elemType = RealType.getRealType("elem_"+count);
163        RealType lineType = RealType.getRealType("line_"+count);
164        this.xType = lineType;
165        this.yType = elemType;
166        this.mask  = mask;
167        bounds = new Gridded2DSet(new RealTupleType(xType, yType), null, 1);
168
169        ScalarMap elemMap = new ScalarMap(elemType, displayElemType);
170        ScalarMap lineMap = new ScalarMap(lineType, displayLineType);
171
172        GriddedSet domainSet = (GriddedSet) data.getDomainSet();
173        float[] low = domainSet.getLow();
174        float[] hi  = domainSet.getHi();
175
176        logger.trace("{}: element range: {} to {}", hashCode(), low[1], hi[1]);
177        logger.trace("{}: line range: {} to {}", hashCode(), low[0], hi[0]);
178
179        elemMax = (int) hi[1];
180        lineMax = (int) hi[0];
181        
182        elemMap.setRange(low[1], hi[1]);
183        lineMap.setRange(low[0], hi[0]);
184
185        addScalarMap(elemMap);
186        addScalarMap(lineMap);
187
188        setData(bounds);
189        count += 1;
190    }
191
192    /**
193     * Constructor for creating a RubberBandBox from another instance
194     *
195     * @param that  other instance
196     *
197     * @throws VisADException   VisAD error
198     * @throws RemoteException   Remote error
199     */
200    protected SubsetRubberBandBox(SubsetRubberBandBox that)
201            throws VisADException, RemoteException {
202
203        super(that);
204
205        this.xType  = that.xType;
206        this.yType  = that.yType;
207        this.bounds = that.bounds;
208    }
209
210    /**
211     * Invoked when box mouse is released. Subclasses should invoke
212     * super.dataChange() to ensure the the bounds are set.
213     *
214     * @throws RemoteException
215     * @throws VisADException
216     */
217    
218    protected void dataChange() throws VisADException, RemoteException {
219        bounds = (Gridded2DSet) getData();
220        float[] highs = bounds.getHi();
221        float[] lows = bounds.getLow();
222        if (highs != null && lows != null) super.dataChange();
223    }
224
225    /**
226     * Return the bounds of the RubberBandBox.  The Gridded2DSet that
227     * is returned contains the opposite (starting and ending) corners
228     * of the box.
229     *
230     * @return  set containing the opposite corners of the box.
231     */
232    
233    public Gridded2DSet getBounds() {
234        return bounds;
235    }
236
237    /**
238         * @return the elemMax
239         */
240        public int getElemMax() {
241                return elemMax;
242        }
243
244        /**
245         * @return the lineMax
246         */
247        public int getLineMax() {
248                return lineMax;
249        }
250
251        /**
252     * Get the DataRenderer used for this displayable.
253     *
254     * @return  RubberBandBoxRendererJ3D associated with this displayable
255     */
256        
257    protected DataRenderer getDataRenderer() {
258        rubberBandBox = new MyRubberBandBoxRendererJ3D(xType, yType, mask,
259                mask);
260        rubberBandBox.setKeepLastBoxOn(lastBoxOn);
261
262        return rubberBandBox;
263    }
264
265    /**
266     * Returns a clone of this instance suitable for another VisAD display.
267     * Underlying data objects are not cloned.
268     *
269     * @return                  A semi-deep clone of this instance.
270     *
271     * @throws VisADException   VisAD failure.
272     * @throws RemoteException  Java RMI failure.
273     */
274    public Displayable cloneForDisplay()
275            throws RemoteException, VisADException {
276        return new SubsetRubberBandBox(this);
277    }
278
279    public void setBox(SubsetRubberBandBox rbb) {
280       rubberBandBox.setLastBox((MyRubberBandBoxRendererJ3D)rbb.getDataRenderer());
281    }
282
283    public Gridded3DSet getLastBox() {
284      return rubberBandBox.last_box;
285    }
286}
287
288
289class DataToDisplayCoordinateSystem extends CoordinateSystem {
290
291  private static final long serialVersionUID = 1L;
292  private CoordinateSystem dataCS;
293  private CoordinateSystem displayCS;
294  private boolean isLL;
295
296  DataToDisplayCoordinateSystem(boolean isLL, CoordinateSystem dataCS, CoordinateSystem displayCS) throws VisADException {
297    super(displayCS.getReference(), null);
298    try {
299        this.dataCS = dataCS;
300        this.displayCS = displayCS;
301        this.isLL = isLL;
302    } catch (Exception e) {
303        System.out.println("e=" + e);
304    }
305  }
306
307  public float[][] toReference(float[][] values) throws VisADException {
308    //- if (isLL) values = reverseArrayOrder(values);
309    float[][] new_values = dataCS.toReference(values);
310    if (isLL) new_values = reverseArrayOrder(new_values);
311    new_values = displayCS.toReference(new float[][] {new_values[1], new_values[0], new_values[2]});
312    return new_values;
313  }
314
315  public float[][] fromReference(float[][] values) throws VisADException {
316    //- if (isLL) values = reverseArrayOrder(values);
317    float[][] new_values = displayCS.fromReference(values);
318    if (isLL) new_values = reverseArrayOrder(new_values);
319    new_values = dataCS.fromReference(new float[][] {new_values[1], new_values[0], new_values[2]});
320
321    return new_values;
322  }
323
324  public double[][] toReference(double[][] values) throws VisADException {
325    //- if (isLL) values = reverseArrayOrder(values);
326    double[][] new_values = dataCS.toReference(values);
327    if (isLL) new_values = reverseArrayOrder(new_values);
328    new_values = displayCS.toReference(new double[][] {new_values[1], new_values[0], new_values[2]});
329
330    return new_values;
331  }
332                                                                                                                                  
333  public double[][] fromReference(double[][] values) throws VisADException {
334    //- if (isLL) values = reverseArrayOrder(values);
335    double[][] new_values = displayCS.fromReference(values);
336    if (isLL) new_values = reverseArrayOrder(new_values);
337    new_values = dataCS.fromReference(new double[][] {new_values[1], new_values[0], new_values[2]});
338    return new_values;
339  }
340
341  public boolean equals(Object obj) {
342    return true;
343  }
344
345    private double[][] reverseArrayOrder(double[][] in) {
346        if (in.length < 2) return in;
347        int len1 = 2;
348        int len2 = in[0].length;
349        double[][] out = new double[in.length][len2];;
350        for (int i=0; i<len1; i++) {
351            for (int j=0; j<len2; j++) {
352                out[len1-i-1][j] = in[i][j];
353            }
354        }
355        if (in.length > 2) {
356            for (int i=2; i<in.length; i++) {
357                for (int j=0; j<len2; j++) {
358                    out[i][j] = in[i][j];
359                }
360            }
361        }
362        return out;
363    }
364
365
366    private float[][] reverseArrayOrder(float[][] in) {
367        if (in.length < 2) return in;
368        int len1 = 2;
369        int len2 = in[0].length;
370        float[][] out = new float[in.length][len2];;
371        for (int i=0; i<len1; i++) {
372            for (int j=0; j<len2; j++) {
373                out[len1-i-1][j] = in[i][j];
374            }
375        }
376        if (in.length > 2) {
377            for (int i=2; i<in.length; i++) {
378                for (int j=0; j<len2; j++) {
379                    out[i][j] = in[i][j];
380                }
381            }
382        }
383        return out;
384    }
385}