File: ScaleBorder.java

package info (click to toggle)
freehep-swing 2.0.3-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,272 kB
  • sloc: java: 11,442; xml: 107; sh: 9; makefile: 3
file content (704 lines) | stat: -rw-r--r-- 23,468 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
// Charles A. Loomis, Jr., and University of California, Santa Cruz,
// Copyright (c) 2000
package org.freehep.swing.graphics;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Stroke;

import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;

import org.freehep.graphics2d.VectorGraphics;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.Border;

/**
 * This class implements a Border in which the left and bottom sides
 * contain numerical scales.
 *
 * @author Charles Loomis
 * @version $Id: ScaleBorder.java 8584 2006-08-10 23:06:37Z duns $ */
public class ScaleBorder
    implements Border{

    /**
     * Constant giving the unicode value of a capital Greek delta. */
    final static private String DELTA = "\u0394";

    /**
     * Constant giving the unicode value of a prime symbol. */
    final static private String PRIME = "\u00b4";

    /**
     * Constant giving the unicode value of a middle dot. */
    final static private String DOT = "\u00b7";

    /**
     * A constant giving the horizontal index of the data arrays. */
    final static private int HORIZONTAL = 0;

    /**
     * A constant giving the vertical index of the data arrays. */
    final static private int VERTICAL = 1;

    /**
     * Constant describing a transform in which the transformed x and
     * y axes are parallel (or antiparallel) to the original axes. */
    final static public int TYPE_PARALLEL_TRANSFORM = 0;

    /**
     * Constant describing a transform in which the transformed x and
     * y axes are parallel (or antiparallel) to the original y and x
     * axes, respectively. */
    final static public int TYPE_SWITCHED_TRANSFORM = 1;

    /**
     * Constant describing a transform in which the transformed x-axis
     * is parallel (or antiparallel) to the original x-axis and the
     * transformed y-axis forms a non-zero angle with the original
     * one. */
    final static public int TYPE_X_SKEW_TRANSFORM = 2;

    /**
     * Constant describing a transform in which the transformed y-axis
     * is parallel (or antiparallel) to the original y-axis and the
     * transformed x-axis forms a non-zero angle with the original
     * one. */
    final static public int TYPE_Y_SKEW_TRANSFORM = 3;

    /**
     * Constant describing a transform in which the transformed x-axis
     * is parallel (or antiparallel) to the original y-axis and the
     * transformed y-axis forms a non-zero angle with the original
     * x-axis. */
    final static public int TYPE_SWITCHED_X_SKEW_TRANSFORM = 4;

    /**
     * Constant describing a transform in which the transformed y-axis
     * is parallel (or antiparallel) to the original x-axis and the
     * transformed x-axis forms a non-zero angle with the original
     * y-axis. */
    final static public int TYPE_SWITCHED_Y_SKEW_TRANSFORM = 5;

    /**
     * Constant describing a transform which does not fall into one of
     * the other categories. */
    final static public int TYPE_GENERAL_TRANSFORM = 6;

    /**
     * Background color. */
    private Color bkgColor;

    /**
     * Foreground color. */
    private Color fgColor;

    /**
     * Paths describing the primary tick marks for the two axes. */
    private GeneralPath[] pTicks = new GeneralPath[2];

    /**
     * Paths describing the secondary tick marks for the two axes. */
    private GeneralPath[] sTicks = new GeneralPath[2];

    /**
     * String giving the axis labels. */
    private String[] axisLabels = new String[2];

    /**
     * String giving the axis units. */
    private String[] axisUnits = new String[2];

    /**
     * The labels for the horizontal and vertical axes. */
    private String[][] labels;

    /**
     * The positions of each of the axis labels. */
    private double[][] positions;

    /**
     * The initial font size for labeling. */
    private int fontSize = 12;

    /**
     * The initial font to use. */
    private Font labelFont = new Font("SansSerif", Font.BOLD, fontSize);

    /**
     * The width of the line used for the secondary tick marks. */
    final static private Stroke thinStroke = new BasicStroke(1.f);

    /**
     * The width of the line used for the primary tick marks. */
    final static private Stroke thickStroke = new BasicStroke(2.f);

    /**
     * An array to hold temporary point values for transformation. */
    private double[] axisPts = new double[6];

    /**
     * Flag to indicate that the scale has changed and that it needs
     * to be redrawn. */
    private boolean scaleChanged;

    /**
     * The current width of the horizontal scale. */
    private int currentWidth;

    /**
     * The current width of the vertical scale. */
    private int currentHeight;

    /**
     * Minimum value on the horizontal axis. */
    private double minHoriz;

    /**
     * Maximum value of the horizontal axis. */
    private double maxHoriz;

    /**
     * Minimum value of the vertical axis. */
    private double minVert;

    /**
     * Maximum value on the vertical axis. */
    private double maxVert;

    /**
     * The calculated Insets for this border. */
    private Insets insets;

    /**
     * Constructs a ScaleBorder with the default foreground (black) and
     * background (orange) colors. */
    public ScaleBorder() {
        this(Color.orange,Color.black);
    }

    /**
     * Constructs a ScaleBorder with the given background and
     * foreground colors. */
    public ScaleBorder(Color bkgColor, Color fgColor) {

        // Give default values for the size of the axes.
        minHoriz = 0.;
        maxHoriz = 0.;
        minVert = 0.;
        maxVert = 0.;

        // Make the new insets.
        resetInsets();

        // Make the paths which will hold the axes.
        pTicks[HORIZONTAL] = new GeneralPath();
        pTicks[VERTICAL] = new GeneralPath();
        sTicks[HORIZONTAL] = new GeneralPath();
        sTicks[VERTICAL] = new GeneralPath();

        // Make the labels and positions.
        labels = new String[2][3];
        positions = new double[2][3];

        // Set the current width and height.
        currentWidth = 0;
        currentHeight = 0;
        scaleChanged = false;

        // Set the colors.
        setBackgroundColor(bkgColor);
        setForegroundColor(fgColor);
    }

    /**
     * Set the background color. */
    public void setBackgroundColor(Color bkgColor) {
        this.bkgColor = bkgColor;
    }

    /**
     * Get the current background color. */
    public Color getBackgroundColor() {
        return bkgColor;
    }

    /**
     * Set the foreground color. */
    public void setForegroundColor(Color bkgColor) {
        this.fgColor = bkgColor;
    }

    /**
     * Get the current foreground color. */
    public Color getForegroundColor() {
        return fgColor;
    }

    /**
     * Set the font for the labels. */
    public void setLabelFont(Font labelFont) {
        this.labelFont = labelFont;
        fontSize = labelFont.getSize();
        resetInsets();
    }

    /**
     * Get the current font for the labels. */
    public Font getLabelFont() {
        return labelFont;
    }

    /**
     * Set the horizontal and vertical limits for the scales. */
    public void setLimits(double minHoriz, double maxHoriz,
                          double minVert, double maxVert) {
        this.minHoriz = minHoriz;
        this.maxHoriz = maxHoriz;
        this.minVert = minVert;
        this.maxVert = maxVert;
        scaleChanged = true;
    }

    /**
     * Set the axis labels. */
    public void setAxisLabels(String horizontalLabel,
                              String verticalLabel) {
        axisLabels[HORIZONTAL] = horizontalLabel;
        axisLabels[VERTICAL] = verticalLabel;
    }

    /**
     * Set the axis units. */
    public void setAxisUnits(String horizontalUnits,
                             String verticalUnits) {
        if (horizontalUnits!=null) {
            axisUnits[HORIZONTAL] = "("+horizontalUnits+")";
        } else {
            axisUnits[HORIZONTAL] = "";
        }

        if (verticalUnits!=null) {
            axisUnits[VERTICAL] = "("+verticalUnits+")";
        } else {
            axisUnits[VERTICAL] = "";
        }
    }

    /**
     * Returns the insets of this border. */
    public Insets getBorderInsets(Component c) {
        return (Insets) insets.clone();
    }

    /**
     * Returns whether or not the border is opaque.  This always
     * returns true. */
    public boolean isBorderOpaque() {
        return true;
    }

    /**
     * Paints the border for the specified component with the
     * specified graphics context, position, and size. */
    public void paintBorder(Component c, Graphics g,
                            int x, int y,
                            int width, int height) {

        // Set the flag indicating that the scales must be remade.
        scaleChanged =
            (scaleChanged || width!=currentWidth || height!=currentHeight);

        VectorGraphics vg =
            VectorGraphics.create(g);

        if (vg!=null) {

            // First fill in the background.
            vg.setColor(bkgColor);
            vg.fillRect(0,0,width,insets.top);
            vg.fillRect(0,0,insets.left,height);
            vg.fillRect(width-insets.right,0,insets.right,height);
            vg.fillRect(0,height-insets.bottom,width,insets.bottom);

            // Get the scale.
            if (scaleChanged) {
                Scale.drawLinearScale(minHoriz,maxHoriz,
                                      width-(insets.left+insets.right),3,7,
                                      Scale.RIGHT_TICKS,
                                      pTicks[HORIZONTAL],
                                      sTicks[HORIZONTAL],
                                      labels[HORIZONTAL],
                                      positions[HORIZONTAL]);
            }

            // Create a sub-graphics context to isolate the coordinate
            // transformations.
            Graphics sg = vg.create();
            VectorGraphics svg =
                VectorGraphics.create(sg);
            svg.translate(insets.left,height-insets.bottom+2);

            // Set the color and font.
            svg.setColor(fgColor);
            svg.setFont(labelFont);

            // Draw the primary and secondary tick marks.
            svg.setStroke(thickStroke);
            svg.draw(pTicks[HORIZONTAL]);
            svg.setStroke(thinStroke);
            svg.draw(sTicks[HORIZONTAL]);

            // Paint the tick labels.
            for (int i=0; i<3; i++) {
                if (labels[HORIZONTAL][i]!=null) {
                    svg.drawString(labels[HORIZONTAL][i],
                                   (float) positions[HORIZONTAL][i],
                                   Scale.getPrimaryTickSize()+0.2f*fontSize,
                                   VectorGraphics.TEXT_CENTER,
                                   VectorGraphics.TEXT_TOP);
                }
            }

            // Paint the axis label.  The third position is always set
            // so that the axis label should be between this position
            // and one of the other two.
            float axisPosition;
            if (Math.abs(positions[HORIZONTAL][2]-positions[HORIZONTAL][1])>
                Math.abs(positions[HORIZONTAL][2]-positions[HORIZONTAL][0])) {
                axisPosition = (float) (0.5*(positions[HORIZONTAL][2]+
                                             positions[HORIZONTAL][1]));
            } else {
                axisPosition = (float) (0.5*(positions[HORIZONTAL][2]+
                                             positions[HORIZONTAL][0]));
            }
            svg.drawString(axisLabels[HORIZONTAL]+" "+
                           axisUnits[HORIZONTAL],
                           axisPosition,
                           Scale.getPrimaryTickSize()+0.2f*fontSize,
                           VectorGraphics.TEXT_CENTER,
                           VectorGraphics.TEXT_TOP);

            // End this context.
            svg.dispose();

            // Create the vertical scale.
            if (scaleChanged) {
                Scale.drawLinearScale(minVert,maxVert,
                                      height-(insets.top+insets.bottom),3,7,
                                      Scale.LEFT_TICKS,
                                      pTicks[VERTICAL],
                                      sTicks[VERTICAL],
                                      labels[VERTICAL],
                                      positions[VERTICAL]);
            }

            // Now create a new graphics context for the vertical axis.
            sg = vg.create();
            svg =
                VectorGraphics.create(sg);
            svg.translate(insets.left-2,height-insets.bottom);
            svg.rotate(-Math.PI/2.);

            // Set the color and font.
            svg.setColor(fgColor);
            svg.setFont(labelFont);

            // Draw the primary and secondary tick marks.
            svg.setStroke(thickStroke);
            svg.draw(pTicks[VERTICAL]);
            svg.setStroke(thinStroke);
            svg.draw(sTicks[VERTICAL]);

            // Paint the tick labels.
            for (int i=0; i<3; i++) {
                if (labels[VERTICAL][i]!=null) {
                    svg.drawString(labels[VERTICAL][i],
                                   (float) positions[VERTICAL][i],
                                   -(Scale.getPrimaryTickSize()+0.2f*fontSize),
                                   VectorGraphics.TEXT_CENTER,
                                   VectorGraphics.TEXT_BOTTOM);
                }
            }

            // Paint the axis label.  The third position is always set
            // so that the axis label should be between this position
            // and one of the other two.
            if (Math.abs(positions[VERTICAL][2]-positions[VERTICAL][1])>
                Math.abs(positions[VERTICAL][2]-positions[VERTICAL][0])) {
                axisPosition = (float) (0.5*(positions[VERTICAL][2]+
                                             positions[VERTICAL][1]));
            } else {
                axisPosition = (float) (0.5*(positions[VERTICAL][2]+
                                             positions[VERTICAL][0]));
            }
            svg.drawString(axisLabels[VERTICAL]+" "+
                           axisUnits[VERTICAL],
                           axisPosition,
                           -(Scale.getPrimaryTickSize()+0.2f*fontSize),
                           VectorGraphics.TEXT_CENTER,
                           VectorGraphics.TEXT_BOTTOM);

            // End this context.
            svg.dispose();

            // Reset the current width and height.
            currentWidth = width;
            currentHeight = height;
            scaleChanged = false;

        }
    }

    /**
     * Recalculate the insets based on the current font size. */
    private void resetInsets() {
        int lb = (int) (2+2+Scale.getPrimaryTickSize()+1.5*fontSize);
        int tr = (int) (2+2+Scale.getPrimaryTickSize());
        insets = new Insets(tr,lb,lb,tr);
    }

    /**
     * Set the scale labels taking into account the given linear
     * transformation. */
    public void setScales(String horizLabel,
                          String vertLabel,
                          String horizUnits,
                          String vertUnits,
                          AffineTransform transform,
                          int panelWidth,
                          int panelHeight) {

        // Determine the type of the tranform.
        int type = classifyTransform(transform);

        switch (type) {

        case (TYPE_PARALLEL_TRANSFORM):
            setAxisLabels(horizLabel,vertLabel);
            setAxisUnits(horizUnits,vertUnits);
            break;
        case (TYPE_SWITCHED_TRANSFORM):
            setAxisLabels(vertLabel,horizLabel);
            setAxisUnits(vertUnits,horizUnits);
            break;
        case (TYPE_Y_SKEW_TRANSFORM):
            setAxisLabels(horizLabel+PRIME,DELTA+vertLabel);
            if (horizUnits.equals(vertUnits)) {
                setAxisUnits(horizUnits,vertUnits);
            } else {
                setAxisUnits(horizUnits+DOT+vertUnits,vertUnits);
            }
            break;
        case (TYPE_X_SKEW_TRANSFORM):
            setAxisLabels(DELTA+horizLabel,vertLabel+PRIME);
            if (vertUnits.equals(horizUnits)) {
                setAxisUnits(horizUnits,vertUnits);
            } else {
                setAxisUnits(horizUnits,vertUnits+DOT+horizUnits);
            }
            break;
        case (TYPE_SWITCHED_Y_SKEW_TRANSFORM):
            setAxisLabels(vertLabel+PRIME,DELTA+horizLabel);
            if (vertUnits.equals(horizUnits)) {
                setAxisUnits(vertUnits,horizUnits);
            } else {
                setAxisUnits(vertUnits+DOT+horizUnits,horizUnits);
            }
            break;
        case (TYPE_SWITCHED_X_SKEW_TRANSFORM):
            setAxisLabels(DELTA+vertLabel,horizLabel+PRIME);
            if (horizUnits.equals(vertUnits)) {
                setAxisUnits(vertUnits,horizUnits);
            } else {
                setAxisUnits(vertUnits,horizUnits+DOT+vertUnits);
            }
            break;
        default:
            setAxisLabels(horizLabel+PRIME,vertLabel+PRIME);
            if (horizUnits.equals(vertUnits)) {
                setAxisUnits(horizUnits,vertUnits);
            } else {
                setAxisUnits(horizUnits+DOT+vertUnits,
                             vertUnits+DOT+horizUnits);
            }
            break;
        }

        // Get the size of the scales.
        axisPts[0] = 0.;
        axisPts[1] = panelHeight;
        axisPts[2] = 0.;
        axisPts[3] = panelHeight;
        axisPts[4] = panelWidth;
        axisPts[5] = 0.;
        try {

            // Avoid using the following call because of a bug in
            // AffineTransform.  Instead create the inverse matrix
            // explicitly as done below.
            //transform.inverseTransform(physicsPt,0,physicsPt,0,3);
            AffineTransform ixform = transform.createInverse();
            ixform.transform(axisPts,0,axisPts,0,1);
            ixform.deltaTransform(axisPts,2,axisPts,2,2);

            // Calculate the values for the vertical axis and the
            // distance.
            double vdy = axisPts[3];
            double vdx = axisPts[2];
            double vdist = Math.sqrt(vdx*vdx+vdy*vdy);

            // Calculate the values for the horizontal axis and the
            // distance.
            double hdy = axisPts[5];
            double hdx = axisPts[4];
            double hdist = Math.sqrt(hdx*hdx+hdy*hdy);

            // Initialize the endpoints of the axes.
            double vmin = 0.;
            double vmax = 0.;
            double hmin = 0.;
            double hmax = 0.;

            // Do what is necessary for the different types of
            // transformations.
            switch (type) {
            case (TYPE_PARALLEL_TRANSFORM): {
                double vsign = (vdy<0.) ? 1. : -1.;
                vmin = axisPts[1];
                vmax = axisPts[1]+vsign*vdist;

                double hsign = (hdx>0.) ? 1. : -1.;
                hmin = axisPts[0];
                hmax = axisPts[0]+hsign*hdist;
                break;
            }

            case (TYPE_SWITCHED_TRANSFORM): {
                double hsign = (hdy>0.) ? 1. : -1.;
                hmin = axisPts[1];
                hmax = axisPts[1]+hsign*hdist;

                double vsign = (vdx<0.) ? 1. : -1.;
                vmin = axisPts[0];
                vmax = axisPts[0]+vsign*vdist;
                break;
            }

            case (TYPE_Y_SKEW_TRANSFORM): {
                double vsign = (vdy>0.) ? 1. : -1.;
                vmax = -vsign*vdist/2.;
                vmin = -vmax;

                hmin = 0.;
                hmax = hdist;
                break;
            }

            case (TYPE_X_SKEW_TRANSFORM): {
                double hsign = (hdx>0.) ? 1. : -1.;
                hmax = hsign*hdist/2.;
                hmin = -hmax;

                vmin = 0.;
                vmax = vdist;
                break;
            }

            case (TYPE_SWITCHED_Y_SKEW_TRANSFORM): {
                double vsign = (vdx>0.) ? 1. : -1.;
                vmax = -vsign*vdist/2.;
                vmin = -vmax;

                hmin = 0.;
                hmax = hdist;
                break;
            }

            case (TYPE_SWITCHED_X_SKEW_TRANSFORM): {
                double hsign = (hdy>0.) ? 1. : -1.;
                hmax = hsign*hdist/2.;
                hmin = -hmax;

                vmin = 0.;
                vmax = vdist;
                break;
            }

            default: {
                vmin = 0.;
                vmax = vdist;
                hmin = 0.;
                hmax = hdist;
                break;
            }
            }

            // Actually set the limits.
            setLimits(hmin,hmax,vmin,vmax);

        } catch (Exception e) {
            setLimits(0.,0.,0.,0.);
        }
    }

    /**
     * This is a protected utility method which classifies the given
     * transform into seven categories: parallel, switched, x-skew,
     * y-skew, switched x-skew, switched y-skew, and general.  The
     * parallel category describes transformations in which the
     * transformed x and y axes are parallel or antiparallel to the
     * original x and y axes, respectively.  The switched category
     * describes transformations in which the transformed x and y axes
     * are parallel or antiparallel to the original y and x axes,
     * respectively.  That is, the x and y axes have been switched.
     * The x-skew describes transformations in which the transformed
     * x-axis is parallel (or antiparallel) to the original one while
     * the transformed y-axis forms some non-zero angle to the
     * original one.  The y-skew is similar; the switch skews are just
     * rotated (counter)clockwise by 90 degrees. The general category
     * encompasses all transforms not falling into one of the other
     * categories.  */
    static protected int classifyTransform(AffineTransform xform) {

        // Set the default return type to a general matrix.
        int category = TYPE_GENERAL_TRANSFORM;

        // Get the four non-translation quantities from the
        // transformation.
        double sx = xform.getScaleX();
        double sy = xform.getScaleY();
        double kx = xform.getShearX();
        double ky = xform.getShearY();

        // Check the type.
        if (kx==0. && ky==0.) {
            category = TYPE_PARALLEL_TRANSFORM;
        } else if (sx==0. && sy==0.) {
            category = TYPE_SWITCHED_TRANSFORM;
        } else if (kx==0.) {
            category = TYPE_Y_SKEW_TRANSFORM;
        } else if (ky==0.) {
            category = TYPE_X_SKEW_TRANSFORM;
        } else if (sx==0.) {
            category = TYPE_SWITCHED_Y_SKEW_TRANSFORM;
        } else if (sy==0.) {
            category = TYPE_SWITCHED_X_SKEW_TRANSFORM;
        }

        // Return the transformtion type.
        return category;
    }

}