Add interactive zoom_rect.
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / subwinDrawing / TicksDrawerGL.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2007 - INRIA - Jean-Baptiste Silvy
4  * desc : Class drawing ticks for the one axis  
5  * 
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at    
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14
15 package org.scilab.modules.renderer.subwinDrawing;
16
17 import java.awt.Font;
18 import java.awt.geom.Rectangle2D;
19
20 import javax.media.opengl.GL;
21
22 import org.scilab.modules.renderer.textDrawing.SciTextRenderer;
23 import org.scilab.modules.renderer.utils.CoordinateTransformation;
24 import org.scilab.modules.renderer.utils.FontManager;
25 import org.scilab.modules.renderer.utils.geom3D.GeomAlgos;
26 import org.scilab.modules.renderer.utils.geom3D.Vector3D;
27 import org.scilab.modules.renderer.utils.glTools.GLTools;
28
29 /**
30  * Class drawing ticks for the one axis
31  * @author Jean-Baptiste Silvy
32  */
33 public abstract class TicksDrawerGL extends BoxTrimmingObjectGL {
34
35         /** Size in pixel of ticks */
36         public static final double TICKS_PIXEL_LENGTH = 0.02;
37         /** Size of subticks compared to ticks */
38         public static final double SUBTICKS_FACTOR = 0.6;
39         
40         /** Maximum accpetable value for dot product between axis direction and ticks direction */
41         private static final double MAX_COS = 0.99;
42         
43         /** Distance from labels to axis relative to ticks length */
44         private static final double LABEL_TO_AXIS_DISTANCE = 1.5;
45         
46         /** Exponent size compared to label size */
47         private static final float EXPONENT_SIZE = 0.5f;
48         
49         private double[] ticksPositions;
50         private String[] ticksLabels;
51         private String[] labelsExponents;
52         private double[] subticksPositions;
53         
54         private double[] curLabelBox = new double[GeomAlgos.RECTANGLE_NB_CORNERS]; // [xmin, xmax, ymin, ymax]
55         private double[] nextLabelBox = new double[GeomAlgos.RECTANGLE_NB_CORNERS]; // [xmin, xmax, ymin, ymax]
56         
57         private int lineStyle;
58         private float thickness;
59         private int lineColor;
60         
61         /** Specify wether the axis line (the long one) should be drawn */
62         private boolean drawAxisLine;
63         
64         private Font labelFont;
65         private int labelColor;
66         
67         /** keep it for speed */
68         private CoordinateTransformation transform;
69         
70         /** Sepecify where the ticks are drawn in the window
71          *  Cut the viewport in 4 distincts part 
72          */
73         private enum TicksPositionCase {
74                 TOP, LEFT, BOTTOM, RIGHT
75         };
76         
77         /**
78          * Default constructor.
79          */
80         public TicksDrawerGL() {
81                 super();
82                 lineStyle = 0;
83                 thickness = 0.0f;
84                 lineColor = 0;
85                 /* draw the line by default */
86                 drawAxisLine = true;
87                 labelColor = -1;
88                 labelFont = null;
89                 transform = null;
90         }
91         
92         /**
93          * Function called before beginning to use OpenGL methods.
94          * @param parentFigureIndex index of the parent figure.
95          *                          Needed to get the GL context to draw in.
96          */
97         public void initializeDrawing(int parentFigureIndex) {
98                 super.initializeDrawing(parentFigureIndex);
99                 transform = CoordinateTransformation.getTransformation(getGL());
100         }
101         
102         /**
103          * Function called at the end of the OpenGL use.
104          */
105         public void endDrawing() {
106                 super.endDrawing();
107         }
108         
109         /**
110          * @return Coordinate trasnformation
111          */
112         public CoordinateTransformation getTransform() {
113                 return transform;
114         }
115         
116         /**
117          * @return Nummber of ticks to draw
118          */
119         protected int getNbTicks() {
120                 return ticksPositions.length;
121         }
122         
123         /**
124          * @param tickIndex index of the tick
125          * @return positions of ticks on its axis.
126          */
127         protected double getTickPosition(int tickIndex) {
128                 return ticksPositions[tickIndex];
129         }
130         
131         /**
132          * @param tickIndex index of the tick
133          * @return label to draw in front of the tick
134          */
135         protected String getTickLabel(int tickIndex) {
136                 return ticksLabels[tickIndex];
137         }
138         
139         /**
140          * @param tickIndex index of the corresponding ticks
141          * @return string to draw in top of label
142          */
143         protected String getLabelExponent(int tickIndex) {
144                 if (labelsExponents != null) {
145                         return labelsExponents[tickIndex];
146                 } else {
147                         return "";
148                 }
149         }
150         
151         /**
152          * @return number of subticks to draw
153          */
154         protected int getNbSubticks() {
155                 return subticksPositions.length;
156         }
157         
158         /**
159          * @param subtickIndex index of the subtick
160          * @return position of the subtick to draw on its axis.
161          */
162         protected double getSubtickPosition(int subtickIndex) {
163                 return subticksPositions[subtickIndex];
164         }
165         
166         /**
167          * @return positions of the subticks on their axis.
168          */
169         protected double[] getSubTicksPositions() {
170                 return subticksPositions;
171         }
172         
173         /**
174          * @return positions of ticks on their axis.
175          */
176         protected double[] getTicksPositions() {
177                 return ticksPositions;
178         }
179
180         
181         /**
182          * Set a new line style for the line.
183          * @param lineStyle index of the line Style
184          */
185         public void setLineStyle(int lineStyle) {
186                 this.lineStyle = lineStyle;
187         }
188         
189         /**
190          * @return index of line style to use.
191          */
192         public int getLineStyle() {
193                 return lineStyle;
194         }
195         
196         /**
197          * Set the thickness
198          * @param thickness thickness of the line in pixels
199          */
200         public void setThickness(float thickness) {
201                 this.thickness = thickness;
202         }
203         
204         /**
205          * @return thickness of the lines to draw in pixels
206          */
207         public float getThickness() {
208                 return thickness;
209         }
210         
211         /**
212          * Set the color of font to use
213          * @param colorIndex index of the color in the colormap
214          */
215         public void setFontColor(int colorIndex) {
216                 labelColor = colorIndex;
217         }
218         
219         /**
220          * Specify a new font draw the text object.
221          * @param fontTypeIndex index of the font in the font array.
222          * @param fontSize font size to use.
223          */
224         public void setFont(int fontTypeIndex, double fontSize) {
225                 labelFont = FontManager.getSciFontManager().getFontFromIndex(fontTypeIndex, fontSize);
226         }
227         
228         /**
229          * Set a color for the axes to draw.
230          * @param colorIndex color index in the colormap
231          */
232         public void setLineColor(int colorIndex) {
233                 this.lineColor = colorIndex;
234         }
235         
236         /**
237          * @return array of size 3 containing the 3 color channels
238          */
239         public double[] getLineColor() {
240                 return getColorMap().getColor(lineColor);
241         }
242         
243         /**
244          * Specify all constant parameters in a single function
245          * @param lineStyle index of the line Style
246          * @param lineWidth thickness of the line in pixels
247          * @param lineColor color index in the colormap of the line color
248          * @param fontType ndex of the font in the font array.
249          * @param fontSize font size to use.
250          * @param fontColor index of the color in the colormap for the font
251          */
252         public void setAxisParameters(int lineStyle, float lineWidth, int lineColor,
253                                                                   int fontType, double fontSize, int fontColor) {
254                 setLineStyle(lineStyle);
255                 setThickness(lineWidth);
256                 setLineColor(lineColor);
257                 setFont(fontType, fontSize);
258                 setFontColor(fontColor);
259         }
260         
261         /**
262          * Specify whether the axis line should be drawn or not.
263          * By default it is drawn
264          * @param drawAxisLine boolean
265          */
266         public void setAxisLineDrawing(boolean drawAxisLine) {
267                 this.drawAxisLine = drawAxisLine;
268         }
269         
270         /**
271          * Draw a set of ticks
272          * @param ticksPositions positions of each ticks on their axis.
273          * @param ticksLabels labels to draw in front of ticks.
274          *                    must have the same size with ticks positions.
275          * @param subticksPositions positions of sub ticks on their axis.
276          * @return maximum distance between labels and axis.
277          */
278         public double drawTicks(double[] ticksPositions, String[] ticksLabels, double[] subticksPositions) {
279                 this.ticksPositions = ticksPositions;
280                 this.ticksLabels = ticksLabels;
281                 this.subticksPositions = subticksPositions;
282                 this.labelsExponents = null;
283                 return drawTicks();
284                 
285         }
286         
287         /**
288          * Temporary function to set labels exponents
289          * @param labelsExponents exponents to draw in top of labels
290          */
291         public void setLabelsExponents(String[] labelsExponents) {
292                 this.labelsExponents = labelsExponents;
293         }
294         
295         /**
296          * Draw a set of ticks and labels with an exponent
297          * @param ticksPositions positions of each ticks on their axis.
298          * @param ticksLabels labels to draw in front of ticks.
299          *                    must have the same size with ticks positions.
300          * @param labelsExponents exponents to draw in top of labels
301          * @param subticksPositions positions of sub ticks on their axis.
302          * @return maximum distance between labels and axis.
303          */
304         public double drawTicks(double[] ticksPositions, String[] ticksLabels,
305                                                     String[] labelsExponents, double[] subticksPositions) {
306                 this.ticksPositions = ticksPositions;
307                 this.ticksLabels = ticksLabels;
308                 this.labelsExponents = labelsExponents;
309                 this.subticksPositions = subticksPositions;
310                 
311                 return drawTicks();
312         }
313         
314         /**
315          * Draw ticks segment and the axis segment
316          * @param ticksPosPix starting edge of ticks lines.
317          * @param subticksPosPix positions of subticks
318          * @param ticksDirPix direction of ticks in pixels
319          * @param axisSegementStart one end of the axis segment
320          * @param axisSegmentEnd the other end
321          */
322         protected void drawTicksLines(Vector3D[] ticksPosPix, Vector3D[] subticksPosPix,
323                                                                   Vector3D ticksDirPix,
324                                                                   Vector3D axisSegementStart, Vector3D axisSegmentEnd) {
325                 GL gl = getGL();
326                 
327                 double[] color = getLineColor();
328                 gl.glColor3d(color[0], color[1], color[2]);
329                 GLTools.beginDashMode(gl, getLineStyle(), getThickness());
330                 
331                 
332                 gl.glBegin(GL.GL_LINES);
333                 
334                 
335                 // draw axis segmentif
336                 if (drawAxisLine) {
337                         gl.glVertex3d(axisSegementStart.getX(), axisSegementStart.getY(), axisSegementStart.getZ());
338                         gl.glVertex3d(axisSegmentEnd.getX(), axisSegmentEnd.getY(), axisSegmentEnd.getZ());
339                 }
340                 
341                 // draw ticks
342                 for (int i = 0; i < ticksPosPix.length; i++) {
343                         if (ticksPosPix[i] == null) { continue; }
344                         gl.glVertex3d(ticksPosPix[i].getX(), ticksPosPix[i].getY(), ticksPosPix[i].getZ());
345                         Vector3D lineEnd = ticksPosPix[i].add(ticksDirPix);
346                         gl.glVertex3d(lineEnd.getX(), lineEnd.getY(), lineEnd.getZ());
347                         
348                 }
349                 
350                 // draw subticks
351                 // same has ticks but with a new ticks length.
352                 Vector3D subTicksDirection = ticksDirPix.scalarMult(SUBTICKS_FACTOR);
353                 for (int i = 0; i < subticksPosPix.length; i++) {
354                         if (subticksPosPix[i] == null) { continue; }
355                         gl.glVertex3d(subticksPosPix[i].getX(), subticksPosPix[i].getY(), subticksPosPix[i].getZ());
356                         Vector3D lineEnd = subticksPosPix[i].add(subTicksDirection);
357                         gl.glVertex3d(lineEnd.getX(), lineEnd.getY(), lineEnd.getZ());
358                 }
359                 
360                 gl.glEnd();
361                 
362                 GLTools.endDashMode(gl);
363         }
364         
365         /**
366          * Compute label center from its ticks direction and location
367          * @param textWidth width of the text to place
368          * @param textHeight height of the text to draw
369          * @param tickPosPix base of the ticks on the axis segment in pixel
370          * @param ticksDirPix ticks direction in pixels
371          * @param exponentPosition out argument giving the position to use for exponent drawing.
372          * @param centerPosition out argument giving the postions to use for labels drawing.
373          * @param ticksSide define the direction of ticks
374          * @return distance from the label to the end of ticks in pixels
375          */
376         private double computeLabelCenter(double textWidth, double textHeight, Vector3D tickPosPix, Vector3D ticksDirPix,
377                                                                           Vector3D centerPosition, Vector3D exponentPosition,
378                                                                           TicksPositionCase ticksSide) {
379                                 
380                 Vector3D textCenter = tickPosPix.add(ticksDirPix);
381                 double res;
382                 
383                 // move text in order to put its box in front of ticks
384                 // the aim is to put ticks segment and text center aligned
385                 switch (ticksSide) {
386                 case RIGHT:
387                         res = textWidth;
388                         textCenter.setY(textCenter.getY() - textWidth / 2.0);
389                         break;
390                 case LEFT:
391                         res = textWidth;
392                         textCenter.setX(textCenter.getX() - textWidth);
393                         textCenter.setY(textCenter.getY() - textHeight / 2.0);
394                         break;
395                 case TOP:
396                         res = textHeight;
397                         textCenter.setX(textCenter.getX() - textWidth / 2.0);
398                         textCenter.setY(textCenter.getY() + textHeight / 2.0);
399                         break;
400                 case BOTTOM:
401                         res = textHeight;
402                         textCenter.setX(textCenter.getX() - textWidth / 2.0);
403                         textCenter.setY(textCenter.getY() - textHeight / 2.0);
404                         break;
405                 default:
406                         res = 0.0;
407                         break;
408                 }
409                 
410                 if (exponentPosition != null) {
411                         exponentPosition.setValues(textCenter.getX() + textWidth,
412                                                                            textCenter.getY() + textHeight,
413                                                                            textCenter.getZ());
414                 }
415                 
416                 centerPosition.setValues(textCenter);
417                 return res;
418         }
419         
420         /**
421          * Draw labels in front of ticks
422          * @param renderer textrenderer used to draw text
423          * @param ticksPosPix position of ticks along the axis in pixel
424          * @param ticksDirPix direction of ticks in pixels
425          * @param bboxWidth contains width of labels bounding box
426          * @param bboxHeight contains height of labels bounding box
427          * @return maximum distance of ticks from the axis in pixel
428          */
429         protected double drawLabels(SciTextRenderer renderer, Vector3D[] ticksPosPix, Vector3D ticksDirPix,
430                                                                 double[] bboxWidth, double[] bboxHeight) {
431                 int nbLabels = ticksPosPix.length;
432                 Vector3D exponentPosition = new Vector3D();
433                 Vector3D textCenter = new Vector3D();
434                 double maxDist = 0.0;
435                 
436                 
437                 GL gl = getGL();
438                 gl.glDisable(GL.GL_COLOR_LOGIC_OP); // does not work well with thext rendering
439                 ticksDirPix.scalarMultSelf(LABEL_TO_AXIS_DISTANCE);
440                 
441                 renderer.begin3DRendering();
442                 
443                 TicksPositionCase ticksOrientation;
444                 if (ticksDirPix.getX() > Math.abs(ticksDirPix.getY())) {
445                         ticksOrientation = TicksPositionCase.RIGHT;
446                 } else if (ticksDirPix.getX() < -Math.abs(ticksDirPix.getY())) {
447                         ticksOrientation = TicksPositionCase.LEFT;
448                 } else if (ticksDirPix.getY() > Math.abs(ticksDirPix.getX())) {
449                         ticksOrientation = TicksPositionCase.TOP;
450                 } else {
451                         ticksOrientation = TicksPositionCase.BOTTOM;
452                 }
453                 
454                 for (int i = 0; i < nbLabels; i++) {
455                         
456                         if (ticksPosPix[i] == null) { continue; }
457                         
458                         double curDist = computeLabelCenter(bboxWidth[i],
459                                                                                                 bboxHeight[i],
460                                                                                                 ticksPosPix[i],
461                                                                                                 ticksDirPix,
462                                                                                                 textCenter,
463                                                                                                 exponentPosition,
464                                                                                                 ticksOrientation);
465                         
466                         // find the maximum distance
467                         if (curDist > maxDist) {
468                                 maxDist = curDist;
469                         }
470                         
471                         renderer.draw3D(getTickLabel(i), textCenter.getX(), textCenter.getY(), textCenter.getZ());
472                         
473                         if (labelsExponents != null) {
474                                 renderer.draw3D(getLabelExponent(i),
475                                                             exponentPosition.getXf(),
476                                                             exponentPosition.getYf(),
477                                                             exponentPosition.getZf(),
478                                                             EXPONENT_SIZE);
479                         }
480                         
481                 }
482                 
483                 renderer.end3DRendering();
484
485                 gl.glEnable(GL.GL_COLOR_LOGIC_OP); // does not work well with thext rendering
486                 
487                 return maxDist + ticksDirPix.getNorm();
488         }
489         
490         /**
491          * Check if the labels which should be displayed do not concealed each other.
492          * Also compute with and height of ticks labels
493          * @param renderer textrenderer used to draw text
494          * @param ticksPosPix position of ticks along the axis in pixel
495          * @param ticksDirPix direction of ticks in pixel
496          * @param bboxWidth contains width of labels bounding box
497          * @param bboxHeight contains height of labels bounding box
498          * @return true if no labels concealed, false otherwise
499          */
500         protected boolean checkLabels(SciTextRenderer renderer, Vector3D[] ticksPosPix, Vector3D ticksDirPix,
501                                                                   double[] bboxWidth, double[] bboxHeight) {
502                 
503                 int nbLabels = ticksPosPix.length;
504
505                 
506                 int firstNonNullTicksIndex = 0;
507                 
508                 // find first non null ticks
509                 while (firstNonNullTicksIndex < nbLabels && ticksPosPix[firstNonNullTicksIndex] == null) {
510                         firstNonNullTicksIndex++;
511                 }
512                 
513                 if (firstNonNullTicksIndex == nbLabels) {
514                         // no ticks, no conceal possible
515                         return true;
516                 }
517                 
518                 // get bouding box of current label
519                 Rectangle2D rect = renderer.getBounds(getTickLabel(firstNonNullTicksIndex));
520                 
521                 bboxWidth[firstNonNullTicksIndex] = rect.getWidth();
522                 bboxHeight[firstNonNullTicksIndex] = rect.getHeight();
523                 curLabelBox[0] = ticksPosPix[firstNonNullTicksIndex].getX();
524                 curLabelBox[1] = curLabelBox[0] + bboxWidth[firstNonNullTicksIndex];
525                 curLabelBox[2] = ticksPosPix[firstNonNullTicksIndex].getY();
526                 curLabelBox[2 + 1] = curLabelBox[2] + bboxHeight[firstNonNullTicksIndex];
527                 
528                 for (int i = firstNonNullTicksIndex + 1; i < nbLabels; i++) {
529                         if (ticksPosPix[i] == null) { continue; }
530                         
531                         // set label text
532                         rect = renderer.getBounds(getTickLabel(i));
533                         bboxWidth[i] = rect.getWidth();
534                         bboxHeight[i] = rect.getHeight();
535                         nextLabelBox[0] = ticksPosPix[i].getX();
536                         nextLabelBox[1] = nextLabelBox[0] + bboxWidth[i];
537                         nextLabelBox[2] = ticksPosPix[i].getY();
538                         nextLabelBox[2 + 1] = nextLabelBox[2] + bboxHeight[i];
539
540                         
541                         if (GeomAlgos.areRectangleConcealing(nextLabelBox, curLabelBox)) {
542                                 return false;
543                         }
544                         
545                         curLabelBox[0] = nextLabelBox[0];
546                         curLabelBox[1] = nextLabelBox[1];
547                         curLabelBox[2] = nextLabelBox[2];
548                         curLabelBox[2 + 1] = nextLabelBox[2 + 1];
549                 }
550                 return true;
551         }
552         
553         /**
554          * Draw ticks knowing ticksPositions, subticks positions
555          * @param ticksPosPix position of ticks along the axis in pixel
556          * @param subticksPositions positions of subticks
557          * @param ticksDirPix direction of ticks in pixel
558          * @param axisSegementStart one end of the axis segment
559          * @param axisSegmentEnd the other end
560          * @return negative value if some texts are concealing, distance from the label to the axis in pixels
561          *        otherwise
562          */
563         public double drawTicks(Vector3D[] ticksPosPix, Vector3D[] subticksPositions,
564                                                         Vector3D ticksDirPix,
565                                                         Vector3D axisSegementStart, Vector3D axisSegmentEnd) {
566                 int nbTicks = ticksPosPix.length;
567                 double[] bboxWidth = new double[nbTicks];
568                 double[] bboxHeight = new double[nbTicks];
569                 
570                 GL gl = getGL();
571                 
572                 // get text renderer
573                 SciTextRenderer renderer = getParentFigureGL().getTextWriter(labelFont, getColorMap().getColor(labelColor));
574                 
575                 GLTools.usePixelCoordinates(gl);
576                 
577                 if (!checkLabels(renderer, ticksPosPix, ticksDirPix, bboxWidth, bboxHeight)) {
578                         GLTools.endPixelCoordinates(gl);
579                         return -1.0;
580                 }
581                 
582                 
583                 drawTicksLines(ticksPosPix, subticksPositions, ticksDirPix, axisSegementStart, axisSegmentEnd);
584                 
585                 double res = drawLabels(renderer, ticksPosPix, ticksDirPix, bboxWidth, bboxHeight);
586                 
587                 GLTools.endPixelCoordinates(gl);
588                 
589                 return res;
590                 
591         }
592         
593         
594         /**
595          * Chack if the ticks direction is not too close to the axis segment. That would lead to a bad displaying.
596          * @param ticksDirPix direction of ticks in pixels
597          * @param axisSegmentStart one edge of the axis segment in pixels
598          * @param axisSegmentEnd the other edge in pixels
599          * @return true if ticks direction is OK, false otherwise
600          */
601         public boolean checkTicksDirection(Vector3D ticksDirPix, Vector3D axisSegmentStart, Vector3D axisSegmentEnd) {
602                 
603                 // compute ticks direction in pixels
604                 Vector3D ticksN = ticksDirPix.getNormalized();
605                 
606                 // compute axis direction in pixels
607                 Vector3D axisDirPix = axisSegmentEnd.substract(axisSegmentStart);
608                 axisDirPix.normalize();
609                 
610                 if (Math.abs(axisDirPix.dotProduct(ticksN)) > MAX_COS) {
611                         return false;
612                 }
613                 
614                 return true;
615                 
616                 
617         }
618         
619         /**
620          * Compute a new vector with the same direction than ticksDirection but with the right length
621          * @param ticksDirection initial direction of ticks whose length will be modified 
622          * @return new direction with right length
623          */
624         public Vector3D setTicksDirectionLength(Vector3D ticksDirection) {
625                 
626                 Vector3D origin = new Vector3D(0.0, 0.0, 0.0);
627                 origin = transform.getCanvasCoordinates(getGL(), origin);
628                 
629                 Vector3D pixDir = transform.getCanvasCoordinates(getGL(), ticksDirection);
630                 // get length in pixels
631                 pixDir = pixDir.substract(origin);
632                 pixDir.normalize();
633                 //double pixelLength = pixDir.getNorm();
634                 
635                 double[] viewPort = transform.getViewPort();
636                 
637                 // compute sizes wich would apply if axes where along X or Y axis
638                 double xSize = viewPort[2] * TICKS_PIXEL_LENGTH;
639                 double ySize = viewPort[CoordinateTransformation.VIEW_PORT_SIZE - 1] * TICKS_PIXEL_LENGTH;
640                 
641                 // compute angle between ticks direction and x axis in pixel coordinates
642                 double angle = Math.acos(Math.abs(pixDir.dotProduct(new Vector3D(1.0, 0.0, 0.0))));
643                 
644                 // push it between 0 and 1.
645                 double fact = angle * 2.0 / Math.PI;
646                 
647                 // apply number of pixels 
648                 return pixDir.scalarMult(((1.0 - fact) * xSize + fact * ySize));
649                 
650                 // apply number of pixels 
651                 //return ticksDirection.scalarMult(((1.0 - fact) * xSize + fact * ySize));
652                 
653         }
654         
655         
656         /**
657          * Find the height of the background facet
658          * @return Z coordinate of the segment to draw
659          */
660         protected double findUpperZCoordinate() {
661                 // inverse of lower coordinate
662                 if (findLowerZCoordinate() == getZmin()) {
663                         return getZmax();
664                 } else {
665                         return getZmin();
666                 }
667         }
668         
669         /**
670          * Draw ticks from the recorded data.
671          * @return maximum distance from ticks to the axis.
672          */
673         public abstract double drawTicks();
674
675 }