001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view.dialog;
016
017import java.lang.reflect.Array;
018
019import hdf.view.Tools;
020import hdf.view.ViewProperties;
021
022import org.eclipse.swt.SWT;
023import org.eclipse.swt.events.DisposeEvent;
024import org.eclipse.swt.events.DisposeListener;
025import org.eclipse.swt.events.SelectionAdapter;
026import org.eclipse.swt.events.SelectionEvent;
027import org.eclipse.swt.graphics.Font;
028import org.eclipse.swt.graphics.Point;
029import org.eclipse.swt.graphics.Rectangle;
030import org.eclipse.swt.layout.GridData;
031import org.eclipse.swt.layout.GridLayout;
032import org.eclipse.swt.widgets.Button;
033import org.eclipse.swt.widgets.Composite;
034import org.eclipse.swt.widgets.Dialog;
035import org.eclipse.swt.widgets.Display;
036import org.eclipse.swt.widgets.Label;
037import org.eclipse.swt.widgets.List;
038import org.eclipse.swt.widgets.Shell;
039import org.eclipse.swt.widgets.Text;
040
041/**
042 * MathConversionDialog shows a message dialog requesting user input for math
043 * conversion.
044 *
045 * @author Jordan T. Henderson
046 * @version 2.4 1/28/2016
047 */
048public class MathConversionDialog extends Dialog {
049    private Shell shell;
050
051    private Font curFont;
052
053    private Text aField;
054
055    private Text bField;
056
057    private Text infoArea;
058
059    private List functionList;
060
061    private Object dataValue;
062
063    private char NT;
064
065    private String[] functionDescription;
066
067    private boolean isConverted;
068
069    /**
070     * Constructs MathConversionDialog.
071     *
072     * @param parent
073     *            the owner of the input
074     * @param data
075     *            the data array to convert.
076     */
077    public MathConversionDialog(Shell parent, Object data)
078    {
079        super(parent, SWT.APPLICATION_MODAL);
080
081        try {
082            curFont = new Font(Display.getCurrent(), ViewProperties.getFontType(),
083                               ViewProperties.getFontSize(), SWT.NORMAL);
084        }
085        catch (Exception ex) {
086            curFont = null;
087        }
088
089        isConverted = false;
090        dataValue   = data;
091        NT          = ' ';
092
093        String cName = data.getClass().getName();
094        int cIndex   = cName.lastIndexOf('[');
095        if (cIndex >= 0) {
096            NT = cName.charAt(cIndex + 1);
097        }
098
099        String[] tmpStrs = {"The filter by lower and upper bounds. x=a if x<a; x=b if x>b."
100                                + "\ne.g.\n x=5, [0, 127]=5\n x=-5, [0, 127]=0\n x=255, [0, 127]=127.",
101                            "The absolute value of a number, the number without its sign."
102                                + "\ne.g.\n abs(5)=5\n abs(-5)=5.",
103                            "Linear function."
104                                + "\ne.g.\n a=5, b=2, x=2.5, a+b*x=10.",
105                            "The result of a number raised to power of a."
106                                + "\ne.g.\n x=2.5, a=10, pow(x, a)=9536.743\n x=25, a=0.5, pow(x, a)=5.",
107                            "The exponential number e (i.e., 2.718...) raised to the power of x."
108                                + "\ne.g.\n exp(5.0)=148.41316\n exp(5.5)=244.69193",
109                            "The natural logarithm (base e) of x."
110                                + "\ne.g.\n ln(20.085541)=3\n ln(10)=2.302585",
111                            "The logarithm of x to the base of a, \"a\" must be an integer > 0."
112                                + "\ne.g.\n log(10, 2)=3.321928\n log(2, 10)=0.30103",
113                            "The trigonometric sine of angle x in radians."
114                                + "\ne.g.\n sin(0.523599)=0.5\n sin(1.047198)=0.866025",
115                            "The trigonometric cosine of angle x in radians."
116                                + "\ne.g.\n cos(0.523599)=0.866025\n cos(1.047198)=0.5",
117                            "The trigonometric tangent of angle x in radians."
118                                + "\ne.g.\n tan(0.785398)=1\n tan(1.047198)=1.732051"};
119
120        functionDescription = tmpStrs;
121    }
122
123    /**
124     * Open the MathConversionDialog for converting data.
125     */
126    public void open()
127    {
128        Shell parent = getParent();
129        shell        = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
130        shell.setFont(curFont);
131        shell.setText("Convert Data...");
132        shell.setImages(ViewProperties.getHdfIcons());
133        shell.setLayout(new GridLayout(1, true));
134
135        // Create content region
136        org.eclipse.swt.widgets.Group contentGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
137        contentGroup.setFont(curFont);
138        contentGroup.setText("Converting Data With A Mathematic Function");
139        contentGroup.setLayout(new GridLayout(2, false));
140        contentGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
141
142        String[] functionNames = {"[a, b]", "abs (x)",    "a + b * x", "pow (x, a)", "exp (x)",
143                                  "ln (x)", "log (a, x)", "sin (x)",   "cos (x)",    "tan (x)"};
144
145        functionList = new List(contentGroup, SWT.SINGLE | SWT.BORDER);
146        functionList.setFont(curFont);
147        functionList.setItems(functionNames);
148        GridData functionListData     = new GridData(SWT.FILL, SWT.FILL, true, false);
149        functionListData.minimumWidth = 350;
150        functionList.setLayoutData(functionListData);
151        functionList.addSelectionListener(new SelectionAdapter() {
152            @Override
153            public void widgetSelected(SelectionEvent e)
154            {
155                int index = functionList.getSelectionIndex();
156                infoArea.setText(functionDescription[index]);
157
158                if ((index == 0) || (index == 2)) {
159                    aField.setEnabled(true);
160                    bField.setEnabled(true);
161                }
162                else if ((index == 3) || (index == 6)) {
163                    aField.setEnabled(true);
164                    bField.setEnabled(false);
165                }
166                else {
167                    aField.setEnabled(false);
168                    bField.setEnabled(false);
169                }
170            }
171        });
172
173        Composite fieldComposite = new Composite(contentGroup, SWT.NONE);
174        fieldComposite.setLayout(new GridLayout(2, false));
175        fieldComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
176
177        Label label = new Label(fieldComposite, SWT.RIGHT);
178        label.setFont(curFont);
179        label.setText("a = ");
180
181        aField                  = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
182        GridData aFieldData     = new GridData(SWT.FILL, SWT.FILL, true, false);
183        aFieldData.minimumWidth = 100;
184        aField.setLayoutData(aFieldData);
185        aField.setFont(curFont);
186        aField.setText("0");
187        aField.setEnabled(false);
188
189        label = new Label(fieldComposite, SWT.RIGHT);
190        label.setFont(curFont);
191        label.setText("b = ");
192
193        bField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
194        bField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
195        bField.setFont(curFont);
196        bField.setText("1");
197        bField.setEnabled(false);
198
199        infoArea = new Text(contentGroup, SWT.MULTI | SWT.BORDER | SWT.WRAP);
200        infoArea.setEditable(false);
201        infoArea.setFont(curFont);
202        infoArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
203        GridData infoAreaData      = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
204        infoAreaData.minimumHeight = 150;
205        infoArea.setLayoutData(infoAreaData);
206
207        // Create Ok/Cancel button region
208        Composite buttonComposite = new Composite(shell, SWT.NONE);
209        buttonComposite.setLayout(new GridLayout(2, true));
210        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
211
212        Button okButton = new Button(buttonComposite, SWT.PUSH);
213        okButton.setFont(curFont);
214        okButton.setText("   &OK   ");
215        okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
216        okButton.addSelectionListener(new SelectionAdapter() {
217            @Override
218            public void widgetSelected(SelectionEvent e)
219            {
220                isConverted = convertData();
221
222                shell.dispose();
223            }
224        });
225
226        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
227        cancelButton.setFont(curFont);
228        cancelButton.setText(" &Cancel ");
229        cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
230        cancelButton.addSelectionListener(new SelectionAdapter() {
231            @Override
232            public void widgetSelected(SelectionEvent e)
233            {
234                isConverted = false;
235
236                shell.dispose();
237            }
238        });
239
240        shell.pack();
241
242        shell.addDisposeListener(new DisposeListener() {
243            @Override
244            public void widgetDisposed(DisposeEvent e)
245            {
246                if (curFont != null)
247                    curFont.dispose();
248            }
249        });
250
251        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
252
253        Rectangle parentBounds = parent.getBounds();
254        Point shellSize        = shell.getSize();
255        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
256                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
257
258        shell.open();
259
260        Display display = parent.getDisplay();
261        while (!shell.isDisposed()) {
262            if (!display.readAndDispatch())
263                display.sleep();
264        }
265    }
266
267    private boolean convertData()
268    {
269        double a = 0, b = 1;
270
271        int index = functionList.getSelectionIndex();
272
273        try {
274            if ((index == 0) || (index == 2)) {
275                a = Double.parseDouble(aField.getText().trim());
276                b = Double.parseDouble(bField.getText().trim());
277            }
278            else if (index == 3) {
279                a = Double.parseDouble(aField.getText().trim());
280            }
281            else if (index == 6) {
282                a = Integer.parseInt(aField.getText().trim());
283                if (a <= 0) {
284                    shell.getDisplay().beep();
285                    Tools.showError(shell, "Convert", "a must be an integer greater than zero.");
286                    return false;
287                }
288            }
289        }
290        catch (Exception ex) {
291            shell.getDisplay().beep();
292            Tools.showError(shell, "Convert", ex.getMessage());
293            return false;
294        }
295
296        int n        = Array.getLength(dataValue);
297        double value = 0, x = 0;
298
299        switch (NT) {
300        case 'B':
301            byte[] bdata = (byte[])dataValue;
302            for (int i = 0; i < n; i++) {
303                x     = bdata[i];
304                value = y(index, x, a, b);
305                if ((value > Byte.MAX_VALUE) || (value < Byte.MIN_VALUE)) {
306                    Tools.showError(shell, "Convert", "Invalid byte value: " + (long)value);
307                    return false;
308                }
309
310                bdata[i] = (byte)value;
311            }
312            break;
313        case 'S':
314            short[] sdata = (short[])dataValue;
315            for (int i = 0; i < n; i++) {
316                x     = sdata[i];
317                value = y(index, x, a, b);
318                if ((value > Short.MAX_VALUE) || (value < Short.MIN_VALUE)) {
319                    Tools.showError(shell, "Convert", "Invalid short value: " + (long)value);
320                    return false;
321                }
322
323                sdata[i] = (short)value;
324            }
325            break;
326        case 'I':
327            int[] idata = (int[])dataValue;
328            for (int i = 0; i < n; i++) {
329                x     = idata[i];
330                value = y(index, x, a, b);
331                if ((value > Integer.MAX_VALUE) || (value < Integer.MIN_VALUE)) {
332                    Tools.showError(shell, "Convert", "Invalid int value: " + (long)value);
333                    return false;
334                }
335
336                idata[i] = (int)value;
337            }
338            break;
339        case 'J':
340            long[] ldata = (long[])dataValue;
341            for (int i = 0; i < n; i++) {
342                x     = ldata[i];
343                value = y(index, x, a, b);
344                if ((value > Long.MAX_VALUE) || (value < Long.MIN_VALUE)) {
345                    Tools.showError(shell, "Convert", "Invalid long value: " + (long)value);
346                    return false;
347                }
348
349                ldata[i] = (long)value;
350            }
351            break;
352        case 'F':
353            float[] fdata = (float[])dataValue;
354            for (int i = 0; i < n; i++) {
355                x     = fdata[i];
356                value = y(index, x, a, b);
357                if ((value > Float.MAX_VALUE) || (value < -Float.MAX_VALUE) || (value == Float.NaN)) {
358                    Tools.showError(shell, "Convert", "Invalid float value: " + value);
359                    return false;
360                }
361
362                fdata[i] = (float)value;
363            }
364            break;
365        case 'D':
366            double[] ddata = (double[])dataValue;
367            for (int i = 0; i < n; i++) {
368                x     = ddata[i];
369                value = y(index, x, a, b);
370                if ((value > Double.MAX_VALUE) || (value < -Double.MAX_VALUE) || (value == Double.NaN)) {
371                    Tools.showError(shell, "Convert", "Invalid double value: " + value);
372                    return false;
373                }
374
375                ddata[i] = value;
376            }
377            break;
378        default:
379            break;
380        }
381
382        return true;
383    }
384
385    private double y(int index, double x, double a, double b)
386    {
387        double y = x;
388
389        switch (index) {
390        case 0:
391            if (x < a) {
392                y = a;
393            }
394            else if (x > b) {
395                y = b;
396            }
397            break;
398        case 1:
399            y = Math.abs(x);
400            break;
401        case 2:
402            y = (a + b * x);
403            break;
404        case 3:
405            y = Math.pow(x, a);
406            break;
407        case 4:
408            y = Math.exp(x);
409            break;
410        case 5:
411            y = Math.log(x);
412            break;
413        case 6:
414            y = (Math.log(x) / Math.log(a));
415            break;
416        case 7:
417            y = Math.sin(x);
418            break;
419        case 8:
420            y = Math.cos(x);
421            break;
422        case 9:
423            y = Math.tan(x);
424            break;
425        default:
426            break;
427        }
428
429        return y;
430    }
431
432    /**
433     * Check if the data is successfully converted.
434     *
435     * @return true if the data is successfully converted.
436     */
437    public boolean isConverted() { return isConverted; }
438}