summaryrefslogtreecommitdiffstats
path: root/ast/plot3d.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2019-05-10 18:53:10 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2019-05-10 18:53:10 (GMT)
commita213dadabce2b2e02eca8376765fa425f01792f5 (patch)
tree9a21116c977447511fed092e80415ae664a92945 /ast/plot3d.c
parent9ef925b353e71b6b8abd23b59ca1adef488e3a3b (diff)
downloadblt-a213dadabce2b2e02eca8376765fa425f01792f5.zip
blt-a213dadabce2b2e02eca8376765fa425f01792f5.tar.gz
blt-a213dadabce2b2e02eca8376765fa425f01792f5.tar.bz2
upgrade ast 8.7.1
Diffstat (limited to 'ast/plot3d.c')
-rw-r--r--ast/plot3d.c8587
1 files changed, 8587 insertions, 0 deletions
diff --git a/ast/plot3d.c b/ast/plot3d.c
new file mode 100644
index 0000000..1bed16c
--- /dev/null
+++ b/ast/plot3d.c
@@ -0,0 +1,8587 @@
+/*
+*class++
+* Name:
+* Plot3D
+
+* Purpose:
+* Provide facilities for 3D graphical output.
+
+* Constructor Function:
+c astPlot3D
+f AST_PLOT3D
+
+* Description:
+* A Plot3D is a specialised form of Plot that provides facilities
+* for producing 3D graphical output, including fully annotated 3D
+* coordinate grids. The base Frame in a Plot3D describes a 3-dimensional
+* "graphical" coordinate system. The axes of this coordinate system are
+* assumed to be right-handed (that is, if X appears horizontally to the
+* right and Y vertically upwards, then Z is out of the screen towards
+* the viewer), and are assumed to be equally scaled (that is, the same
+* units are used to measure positions on each of the 3 axes). The upper
+* and lower bounds of a volume within this graphical coordinate system
+* is specified when the Plot3D is created, and all subsequent graphics
+* are "drawn" in this volume.
+*
+* The Plot3D class does not itself include any ability to draw on a
+* graphics device. Instead it calls upon function in an externally
+* supplied module (the "grf3d" module) to do the required drawing.
+* A module should be written that implements the functions of the
+* grf3d interface using the facilities of a specific graphics system
+* This module should then be linked into the application so that the
+* Plot3D class can use its functions (see the description of the
+* ast_link commands for details of how to do this). The grf3d interface
+* defines a few simple functions for drawing primitives such as straight
+* lines, markers and character strings. These functions all accept
+* positions in the 3D graphics coordinate system (the base Frame of the
+* Plot3D), and so the grf3d module must also manage the projection of
+* these 3D coordinates onto the 2D viewing surface, including the choice
+* of "eye"/"camera" position, direction of viewing, etc. The AST
+* library includes a sample implementation of the grf3d interface
+* based on the PGPLOT graphics system (see file grf3d_pgplot.c). This
+* implementation also serves to document the grf3d interface itself and
+* should be consulted for details before writing a new implementation.
+*
+* The current Frame of a Plot3D describes a "physical" 3-dimensional
+* coordinate system, which is the coordinate system in which plotting
+* operations are specified when invoking the methods of the Plot3D
+* class. The results of each plotting operation are automatically
+* transformed into 3D graphical coordinates before being plotted
+* using the facilities of the grf3d module linked into the application.
+* Note, at least one of the three axes of the current Frame must be
+* independent of the other two current Frame axes.
+*
+* You may select different physical coordinate systems in which to
+* plot (including the native graphical coordinate system itself)
+* by selecting different Frames as the current Frame of a Plot3D,
+* using its Current attribute.
+*
+* Like any FrameSet, a Plot3D may also be used as a Frame. In this
+* case, it behaves like its current Frame, which describes the
+* physical coordinate system.
+*
+* When used as a Mapping, a Plot3D describes the inter-relation
+* between 3D graphical coordinates (its base Frame) and 3D physical
+* coordinates (its current Frame).
+*
+* Although the Plot3D class inherits from the Plot class, several of
+* the facilities of the Plot class are not available in the Plot3D
+* class, and an error will be reported if any attempt is made to use
+* them. Specifically, the Plot3D class does not support clipping
+* using the
+* astClip function.
+f AST_CLIP routine.
+* Nor does it support the specification of graphics primitive functions
+* at run-time using the
+c astGrfSet, astGrfPop, astGrfPush and astGetGrfContext functions.
+f AST_GRFSET, AST_GRFPOP, AST_GRFPUSH, and AST_GETGRFCONTEXT routines.
+
+* Inheritance:
+* The Plot3D class inherits from the Plot class.
+
+* Attributes:
+* In addition to those attributes common to all Plots, every
+* Plot3D also has the following attributes:
+*
+* - Norm: Normal vector defining the 2D plane used for text and markers
+* - RootCorner: Specifies which edges of the 3D box should be annotated.
+*
+* Some attributes of the Plot class refer to specific physical
+* coordinate axes (e.g. Gap, LabelUp, DrawAxes, etc). For a basic
+* Plot, the axis index must be 1 or 2, but for a Plot3D the axis index
+* can be 1, 2 or 3.
+*
+* Certain Plot attributes are ignored by the Plot3D class (e.g. Edge,
+* DrawTitle, TitleGap, etc). Consult the Plot attribute documentation
+* for details. All other Plot attributes can be set for a specific
+* plane of the 3-d plot by appending one of the strings "_XY", "_XZ"
+* or "_YZ" to the end of the Plot attribute name. For instance,
+* "Grid_YZ" refers to the "Grid" attribute for the plane spanning
+* the second (Y) and third (Z) axes of the 3-d plot.
+
+
+* Functions:
+c The Plot3D class does not define any new functions beyond those
+f The Plot3D class does not define any new routines beyond those
+* which are applicable to all Plots. Note, however, that the
+* following methods inherited from the Plot class cannot be used with
+* a Plot3D and will report an error if called:
+c - astBoundingBox, astClip, astCurve, astGenCurve,
+c astGetGrfContext, astGrfPop, astGrfPush, astGrfSet, astGridLine,
+c astPolyCurve.
+f - AST_BOUNDINGBOX, AST_CLIP, AST_CURVE, AST_GENCURVE,
+f AST_GETGRFCONTEXT, AST_GRFPOP, AST_GRFPUSH, AST_GRFSET,
+f AST_GRIDLINE, AST_POLYCURVE.
+
+* Copyright:
+* Copyright (C) 2007 Science & Technology Facilities Council.
+* All Rights Reserved.
+
+* Licence:
+* This program is free software: you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation, either
+* version 3 of the License, or (at your option) any later
+* version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General
+* License along with this program. If not, see
+* <http://www.gnu.org/licenses/>.
+
+* Authors:
+* DSB: David S. Berry (Starlink)
+
+* History:
+* 6-JUN-2007 (DSB):
+* Original version.
+* 6-SEP-2007 (DSB):
+* Re-code the astGrid function.
+* 12-NOV-2007 (DSB):
+* Clear up compiler warnings.
+* 4-MAY-2012 (DSB):
+* Avoid segvio in Grid if no ticks are drawn.
+* 21-MAY-2012 (DSB):
+* In astLoadPlot3D, do not call SetRootCorner as it requires an
+* active graphics system to be present which may not yet have been
+* established. Also establish the grf routines to be used.
+* 5-OCT-2015 (DSB):
+* Allow Plot attributes to be set for specific planes.
+*class--
+*/
+
+/* Module Macros. */
+/* ============== */
+/* Set the name of the class we are implementing. This indicates to
+ the header files that define class interfaces that they should make
+ "protected" symbols available. */
+#define astCLASS Plot3D
+
+/* Integers identifying the 3 plotting surfaces */
+#define XY 1
+#define XZ 2
+#define YZ 3
+
+/* Integers identifying the 8 corners of the graphics cube. */
+#define LLL 0
+#define ULL 1
+#define LUL 2
+#define UUL 3
+#define LLU 4
+#define ULU 5
+#define LUU 6
+#define UUU 7
+
+/* Identify the 4 edges of a Plot */
+#define LEFT 0
+#define TOP 1
+#define RIGHT 2
+#define BOTTOM 3
+
+/* A macros that returns a pointer to the Plot that spans a given plane. */
+#define GET_PLOT(plane) ( \
+ ( plane == XY ) ? this->plotxy : ( \
+ ( plane == XZ ) ? this->plotxz : ( \
+ ( plane == YZ ) ? this->plotyz : NULL ) ) )
+
+
+/*
+*
+* Name:
+* MAKE_CLEAR3
+
+* Purpose:
+* Implement a method to clear a single value in a multi-valued attribute.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_CLEAR3(attr,component,assign,nval)
+
+* Class Membership:
+* Defined by the Plot3D class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Clear<Attribute>( AstPlot *this, int axis )
+*
+* and an external interface function of the form:
+*
+* void astClear<Attribute>_( AstPlot *this, int axis )
+*
+* which implement a method for clearing a single value in a specified
+* multi-valued attribute for an axis of a Plot3D.
+
+* Parameters:
+* attr
+* The name of the attribute to be cleared, as it appears in the function
+* name (e.g. LabelAt in "astClearLabelAt").
+* component
+* The name of the class structure component that holds the attribute
+* value.
+* assign
+* An expression that evaluates to the value to assign to the component
+* to clear its value.
+* nval
+* Specifies the number of values in the multi-valued attribute. The
+* "axis" values supplied to the created function should be in the
+* range zero to (nval - 1).
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_CLEAR3(attr,component,assign,nval) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Clear##attr( AstPlot3D *this, int axis, int *status ) { \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Validate the axis index. */ \
+ if( axis < 0 || axis >= nval ){ \
+ astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
+ #attr " - it should be in the range 1 to %d.", status, \
+ "astClear" #attr, astGetClass( this ), \
+ axis + 1, nval ); \
+\
+/* Assign the "clear" value. */ \
+ } else { \
+ this->component[ axis ] = (assign); \
+ } \
+} \
+\
+/* External interface. */ \
+/* ------------------- */ \
+void astClear##attr##_( AstPlot3D *this, int axis, int *status ) { \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Invoke the required method via the virtual function table. */ \
+ (**astMEMBER(this,Plot3D,Clear##attr))( this, axis, status ); \
+}
+
+
+/*
+*
+* Name:
+* MAKE_GET3
+
+* Purpose:
+* Implement a method to get a single value in a multi-valued attribute.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_GET3(attr,type,bad_value,assign,nval)
+
+* Class Membership:
+* Defined by the Plot3D class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static <Type> Get<Attribute>( AstPlot3D *this, int axis )
+*
+* and an external interface function of the form:
+*
+* <Type> astGet<Attribute>_( AstPlot3D *this, int axis )
+*
+* which implement a method for getting a single value from a specified
+* multi-valued attribute for an axis of a Plot3D.
+
+* Parameters:
+* attr
+* The name of the attribute whose value is to be obtained, as it
+* appears in the function name (e.g. Label in "astGetLabel").
+* type
+* The C type of the attribute.
+* bad_value
+* A constant value to return if the global error status is set, or if
+* the function fails.
+* assign
+* An expression that evaluates to the value to be returned. This can
+* use the string "axis" to represent the zero-based value index.
+* nval
+* Specifies the number of values in the multi-valued attribute. The
+* "axis" values supplied to the created function should be in the
+* range zero to (nval - 1).
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_GET3(attr,type,bad_value,assign,nval) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static type Get##attr( AstPlot3D *this, int axis, int *status ) { \
+ type result; /* Result to be returned */ \
+\
+/* Initialise */ \
+ result = (bad_value); \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+/* Validate the axis index. */ \
+ if( axis < 0 || axis >= nval ){ \
+ astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
+ #attr " - it should be in the range 1 to %d.", status, \
+ "astGet" #attr, astGetClass( this ), \
+ axis + 1, nval ); \
+\
+/* Assign the result value. */ \
+ } else { \
+ result = (assign); \
+ } \
+\
+/* Check for errors and clear the result if necessary. */ \
+ if ( !astOK ) result = (bad_value); \
+\
+/* Return the result. */ \
+ return result; \
+} \
+/* External interface. */ \
+/* ------------------- */ \
+type astGet##attr##_( AstPlot3D *this, int axis, int *status ) { \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return (bad_value); \
+\
+/* Invoke the required method via the virtual function table. */ \
+ return (**astMEMBER(this,Plot3D,Get##attr))( this, axis, status ); \
+}
+
+/*
+*
+* Name:
+* MAKE_SET3
+
+* Purpose:
+* Implement a method to set a single value in a multi-valued attribute
+* for a Plot3D.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_SET3(attr,type,component,assign,nval)
+
+* Class Membership:
+* Defined by the Plot3D class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Set<Attribute>( AstPlot3D *this, int axis, <Type> value )
+*
+* and an external interface function of the form:
+*
+* void astSet<Attribute>_( AstPlot3D *this, int axis, <Type> value )
+*
+* which implement a method for setting a single value in a specified
+* multi-valued attribute for a Plot3D.
+
+* Parameters:
+* attr
+* The name of the attribute to be set, as it appears in the function
+* name (e.g. LabelAt in "astSetLabelAt").
+* type
+* The C type of the attribute.
+* component
+* The name of the class structure component that holds the attribute
+* value.
+* assign
+* An expression that evaluates to the value to be assigned to the
+* component.
+* nval
+* Specifies the number of values in the multi-valued attribute. The
+* "axis" values supplied to the created function should be in the
+* range zero to (nval - 1). If a value of 0 is supplied, the
+* value of the Plot3D's Nin attribute is used instead.
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*-
+*/
+
+/* Define the macro. */
+#define MAKE_SET3(attr,type,component,assign,nval) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Set##attr( AstPlot3D *this, int axis, type value, int *status ) { \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Validate the axis index. */ \
+ if( axis < 0 || axis >= nval ){ \
+ astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
+ #attr " - it should be in the range 1 to %d.", status, \
+ "astSet" #attr, astGetClass( this ), \
+ axis + 1, nval ); \
+\
+/* Store the new value in the structure component. */ \
+ } else { \
+ this->component[ axis ] = (assign); \
+ } \
+} \
+\
+/* External interface. */ \
+/* ------------------- */ \
+void astSet##attr##_( AstPlot3D *this, int axis, type value, int *status ) { \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Invoke the required method via the virtual function table. */ \
+ (**astMEMBER(this,Plot3D,Set##attr))( this, axis, value, status ); \
+}
+
+/*
+*
+* Name:
+* MAKE_TEST3
+
+* Purpose:
+* Implement a method to test if a single value has been set in a
+* multi-valued attribute for a class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_TEST3(attr,assign,nval)
+
+* Class Membership:
+* Defined by the Plot3D class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static int Test<Attribute>( AstPlot3D *this, int axis )
+*
+* and an external interface function of the form:
+*
+* int astTest<Attribute>_( AstPlot3D *this, int axis )
+*
+* which implement a method for testing if a single value in a specified
+* multi-valued attribute has been set for a class.
+
+* Parameters:
+* attr
+* The name of the attribute to be tested, as it appears in the function
+* name (e.g. LabelAt in "astTestLabelAt").
+* assign
+* An expression that evaluates to 0 or 1, to be used as the returned
+* value. This can use the string "axis" to represent the zero-based
+* index of the value within the attribute.
+* nval
+* Specifies the number of values in the multi-valued attribute. The
+* "axis" values supplied to the created function should be in the
+* range zero to (nval - 1).
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*-
+*/
+
+/* Define the macro. */
+#define MAKE_TEST3(attr,assign,nval) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static int Test##attr( AstPlot3D *this, int axis, int *status ) { \
+ int result; /* Value to return */ \
+\
+/* Initialise */ \
+ result = 0; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+/* Validate the axis index. */ \
+ if( axis < 0 || axis >= nval ){ \
+ astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
+ #attr " - it should be in the range 1 to %d.", status, \
+ "astTest" #attr, astGetClass( this ), \
+ axis + 1, nval ); \
+\
+/* Assign the result value. */ \
+ } else { \
+ result = (assign); \
+ } \
+\
+/* Check for errors and clear the result if necessary. */ \
+ if ( !astOK ) result = 0; \
+\
+/* Return the result. */ \
+ return result; \
+} \
+/* External interface. */ \
+/* ------------------- */ \
+int astTest##attr##_( AstPlot3D *this, int axis, int *status ) { \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return 0; \
+\
+/* Invoke the required method via the virtual function table. */ \
+ return (**astMEMBER(this,Plot3D,Test##attr))( this, axis, status ); \
+}
+
+/*
+*
+* Name:
+* MAKE_CLEAR2
+
+* Purpose:
+* Implement a method to clear an element-specific attribute inherited
+* from the Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_CLEAR2(attr)
+
+* Class Membership:
+* Defined by the Plot3d class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Clear<Attribute>( AstPlot *this, int element )
+*
+* which implements a method for clearing one of the element-specific
+* attributes (e.g. Size, Colour, Width, etc) inherited from the
+* parent Plot class.
+
+* Parameters:
+* attr
+* The name of the attribute to be cleared, as it appears in the function
+* name (e.g. LabelAt in "astClearSize").
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_CLEAR2(attr) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Clear##attr( AstPlot *this_plot, int element, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot3D *this; \
+ int axis3d; \
+ int elem2d1; \
+ int elem2d2; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Clear the attribute value in the parent Plot structure. */ \
+ (*parent_clear##attr)( this_plot, element, status ); \
+\
+/* If OK, clear the attribute in the encapsulated Plots. */ \
+ if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+\
+/* Get the zero-based index of the 3D axis to which the supplied element \
+ refers. Use an index of -1 to indicate that the element does not \
+ relate to a specific axis. Also get the corresponding elements to use \
+ with the two Plots that share the specified 3D axis. */ \
+ axis3d = Element2D( this, element, &elem2d1, &elem2d2, status ); \
+\
+/* If the element is not axis-specific, clear the attribute value in all \
+ three plots. */ \
+ if( axis3d == -1 ) { \
+ astClear##attr( this->plotxy, element ); \
+ astClear##attr( this->plotxz, element ); \
+ astClear##attr( this->plotyz, element ); \
+\
+/* Otherwise, clear the attribute in the two plots that share the \
+ specified 3D axis. */ \
+ } else { \
+ astClear##attr( GET_PLOT(this->axis_plot1[ axis3d ]), elem2d1 ); \
+ astClear##attr( GET_PLOT(this->axis_plot2[ axis3d ]), elem2d2 ); \
+ } \
+ } \
+}
+
+/*
+*
+* Name:
+* MAKE_SET2
+
+* Purpose:
+* Implement a method to set an element-specific attribute inherited
+* from the Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_SET2(attr,type)
+
+* Class Membership:
+* Defined by the Plot3d class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Set<Attribute>( AstPlot *this, int element, type value )
+*
+* which implements a method for setting one of the element-specific
+* attributes (e.g. Size, Colour, Width, etc) inherited from the
+* parent Plot class.
+
+* Parameters:
+* attr
+* The name of the attribute to be cleared, as it appears in the function
+* name (e.g. LabelAt in "astClearSize").
+* type
+* The attribute data type.
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_SET2(attr,type) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Set##attr( AstPlot *this_plot, int element, type value, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot3D *this; \
+ int axis3d; \
+ int elem2d1; \
+ int elem2d2; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Set the attribute value in the parent Plot structure. */ \
+ (*parent_set##attr)( this_plot, element, value, status ); \
+\
+/* If OK, set the attribute in the encapsulated Plots. */ \
+ if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+\
+/* Get the zero-based index of the 3D axis to which the supplied element \
+ refers. Use an index of -1 to indicate that the element does not \
+ relate to a specific axis. Also get the corresponding elements to use \
+ with the two Plots that share the specified 3D axis. */ \
+ axis3d = Element2D( this, element, &elem2d1, &elem2d2, status ); \
+\
+/* If the element is not axis-specific, clear the attribute value in all \
+ three plots. */ \
+ if( axis3d == -1 ) { \
+ astSet##attr( this->plotxy, element, value ); \
+ astSet##attr( this->plotxz, element, value ); \
+ astSet##attr( this->plotyz, element, value ); \
+\
+/* Otherwise, clear the attribute in the two plots that share the \
+ specified 3D axis. */ \
+ } else { \
+ astSet##attr( GET_PLOT(this->axis_plot1[ axis3d ]), elem2d1, value ); \
+ astSet##attr( GET_PLOT(this->axis_plot2[ axis3d ]), elem2d2, value ); \
+ } \
+ } \
+}
+
+
+/*
+*
+* Name:
+* MAKE_CLEAR1
+
+* Purpose:
+* Implement a method to clear an attribute inherited from the Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_CLEAR1(attr)
+
+* Class Membership:
+* Defined by the Plot3d class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Clear<Attribute>( AstPlot *this )
+*
+* which implements a method for clearing a Plot3D attribute inherited
+* from the parent Plot class. It clears the attribute in all three
+* plots encapsulated within the Plot3D.
+
+* Parameters:
+* attr
+* The name of the attribute to be cleared, as it appears in the function
+* name (e.g. LabelAt in "astClearLabelAt").
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_CLEAR1(attr) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Clear##attr( AstPlot *this_plot, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot3D *this; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Clear the attribute value in the parent Plot structure. */ \
+ (*parent_clear##attr)( this_plot, status ); \
+\
+/* If OK, clear the attribute in all three of the encapsulated Plots. */ \
+ if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+ astClear##attr( this->plotxy ); \
+ astClear##attr( this->plotxz ); \
+ astClear##attr( this->plotyz ); \
+ } \
+}
+
+/*
+*
+* Name:
+* MAKE_SET1
+
+* Purpose:
+* Implement a method to set an attribute inherited from the Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_SET1(attr,type)
+
+* Class Membership:
+* Defined by the Plot3d class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Set<Attribute>( AstPlot *this, type value )
+*
+* which implements a method for setting a Plot3D attribute inherited
+* from the parent Plot class. It sets the attribute in all three
+* plots encapsulated within the Plot3D.
+
+* Parameters:
+* attr
+* The name of the attribute to be set, as it appears in the function
+* name (e.g. LabelAt in "astSetLabelAt").
+* type
+* The C data type for the attribute value.
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_SET1(attr,type) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Set##attr( AstPlot *this_plot, type value, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot3D *this; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Set the attribute value in the parent Plot structure. */ \
+ (*parent_set##attr)( this_plot, value, status ); \
+\
+/* If OK, set the attribute in all three of the encapsulated Plots. */ \
+ if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+ astSet##attr( this->plotxy, value ); \
+ astSet##attr( this->plotxz, value ); \
+ astSet##attr( this->plotyz, value ); \
+ } \
+}
+
+/*
+*
+* Name:
+* MAKE_CLEAR
+
+* Purpose:
+* Implement a method to clear an attribute inherited from the Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_CLEAR(attr,whichplots)
+
+* Class Membership:
+* Defined by the Plot3d class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Clear<Attribute>( AstPlot *this, int axis )
+*
+* which implements a method for clearing an axis specific Plot3D
+* attribute inherited from the parent Plot class.
+
+* Parameters:
+* attr
+* The name of the attribute to be cleared, as it appears in the function
+* name (e.g. LabelAt in "astClearLabelAt").
+* whichplots
+* A value indicating which Plots should be affected. A negative value
+* means "all threee plots", a value of zero means "just the two plots
+* that touch at the specified axis in 3D space", a positive value
+* means "just the Plot that is used to label th 3D axis."
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_CLEAR(attr,whichplots) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Clear##attr( AstPlot *this_plot, int axis, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot *plot; \
+ AstPlot3D *this; \
+ int axis2d; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Clear the attribute value in the parent Plot structure. This will \
+ validate the axis index. */ \
+ (*parent_clear##attr)( this_plot, axis, status ); \
+\
+/* If OK, clear the attribute for the relevant axis, or axes, of the Plots \
+ encapsulated inside the Plot3D. First get a pointer to the Plot3D \
+ structure. */ \
+ if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+\
+/* If requested clear the attribute in all three Plots. */ \
+ if( whichplots < 0 ) { \
+ astClear##attr( this->plotxy, axis ); \
+ astClear##attr( this->plotxz, axis ); \
+ astClear##attr( this->plotyz, axis ); \
+\
+/* Each axis in 3D graphics space is described by two of the encapsulated \
+ Plots, but only one of these two Plots is used to generate labels for \
+ the axis. Now deal with cases where we are clearing the attribute \
+ value in both of the two Plots that describe the axis. */ \
+ } else if ( whichplots == 0 ) { \
+ if( axis == 0 ) { \
+ astClear##attr( this->plotxy, 0 ); \
+ astClear##attr( this->plotxz, 0 ); \
+\
+ } else if( axis == 1 ) { \
+ astClear##attr( this->plotxy, 1 ); \
+ astClear##attr( this->plotyz, 0 ); \
+\
+ } else { \
+ astClear##attr( this->plotxz, 1 ); \
+ astClear##attr( this->plotyz, 1 ); \
+ } \
+\
+/* Now deal with cases where we are clearing the attribute value only in \
+ the Plot that is used to label the axis. */ \
+ } else { \
+ plot = AxisPlot( this, axis, &axis2d, status ); \
+ astClear##attr( plot, axis2d ); \
+ } \
+ } \
+}
+
+
+/*
+*
+* Name:
+* MAKE_GET
+
+* Purpose:
+* Implement a method to get the value of an attribute inherited from the
+* Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot.h"
+* MAKE_GET(attr,type,bad_value)
+
+* Class Membership:
+* Defined by the Plot3D class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static <Type> Get<Attribute>( AstPlot *this, int axis )
+*
+* which implements a method for getting a value for an axis specific
+* attribute for a Plot3D.
+
+* Parameters:
+* attr
+* The name of the attribute whose value is to be obtained, as it
+* appears in the function name (e.g. Label in "astGetLabel").
+* type
+* The C type of the attribute.
+* bad_value
+* A constant value to return if the global error status is set, or if
+* the function fails.
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*
+*/
+
+/* Define the macro. */
+#define MAKE_GET(attr,type,bad_value) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static type Get##attr( AstPlot *this_plot, int axis, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot *plot; \
+ AstPlot3D *this; \
+ int axis2d; \
+ type result; \
+\
+/* Initialise */ \
+ result = (bad_value); \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+/* See if the attribute value is set in the parent Plot structure. If so, \
+ use the parent get method to get its value. */ \
+ if( astTest##attr( this_plot, axis ) ) { \
+ result = (*parent_get##attr)( this_plot, axis, status ); \
+\
+/* If the attribute value is not set in the parent Plot structure, get \
+ the default value from the Plot that is used to label the 3D axis. The \
+ parent test method called above will have reported an error if the axis \
+ index is invalid, so check astOK here. */ \
+ } else if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+ plot = AxisPlot( this, axis, &axis2d, status ); \
+ result = astGet##attr( plot, axis2d ); \
+ } \
+\
+/* Return the result. */ \
+ return result; \
+}
+
+/*
+*
+* Name:
+* MAKE_SET
+
+* Purpose:
+* Implement a method to set a value for an attribute inherited from the
+* Plot class.
+
+* Type:
+* Private macro.
+
+* Synopsis:
+* #include "plot3d.h"
+* MAKE_SET(attr,type,whichplots)
+
+* Class Membership:
+* Defined by the Plot3d class.
+
+* Description:
+* This macro expands to an implementation of a private member function of
+* the form:
+*
+* static void Set<Attribute>( AstPlot *this, int axis, <Type> value )
+*
+* which implements a method for setting a value for an axis specific
+* attribute inherited from the parent Plot class.
+
+* Parameters:
+* attr
+* The name of the attribute to be set, as it appears in the function
+* name (e.g. LabelAt in "astSetLabelAt").
+* type
+* The C type of the attribute.
+* whichplots
+* A value indicating which Plots should be affected. A negative value
+* means "all threee plots", a value of zero means "just the two plots
+* that touch at the specified axis in 3D space", a positive value
+* means "just the Plot that is used to label the 3D axis."
+
+* Notes:
+* - To avoid problems with some compilers, you should not leave any white
+* space around the macro arguments.
+*-
+*/
+
+/* Define the macro. */
+#define MAKE_SET(attr,type,whichplots) \
+\
+/* Private member function. */ \
+/* ------------------------ */ \
+static void Set##attr( AstPlot *this_plot, int axis, type value, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPlot3D *this; \
+ AstPlot *plot; \
+ int axis2d; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Set the supplied value in the parent Plot class. This will validate \
+ the axis index. */ \
+ (*parent_set##attr)( this_plot, axis, value, status ); \
+\
+/* If this went OK, also set the value for the appropriate axis of the \
+ appropriate encapsulated Plot(s). First get a pointer to the Plot3D \
+ structure. */ \
+ if( astOK ) { \
+ this = (AstPlot3D *) this_plot; \
+\
+/* If requested set the attribute in all three Plots. */ \
+ if( whichplots < 0 ) { \
+ astSet##attr( this->plotxy, axis, value ); \
+ astSet##attr( this->plotxz, axis, value ); \
+ astSet##attr( this->plotyz, axis, value ); \
+\
+/* Each axis in 3D graphics space is described by two of the encapsulated \
+ Plots, but only one of these two Plots is used to generate labels for \
+ the axis. First deal with cases where we are setting the attribute \
+ value in both of the two Plots that describe the axis. */ \
+ } else if( whichplots == 0 ) { \
+ if( axis == 0 ) { \
+ astSet##attr( this->plotxy, 0, value ); \
+ astSet##attr( this->plotxz, 0, value ); \
+\
+ } else if( axis == 1 ) { \
+ astSet##attr( this->plotxy, 1, value ); \
+ astSet##attr( this->plotyz, 0, value ); \
+\
+ } else { \
+ astSet##attr( this->plotxz, 1, value ); \
+ astSet##attr( this->plotyz, 1, value ); \
+ } \
+\
+/* Now deal with cases where we are setting the attribute value only in \
+ the Plot that is used to label the axis. */ \
+ } else { \
+ plot = AxisPlot( this, axis, &axis2d, status ); \
+ astSet##attr( plot, axis2d, value ); \
+ } \
+ } \
+}
+
+
+/* Header files. */
+/* ============= */
+/* Interface definitions. */
+/* ---------------------- */
+
+#include "globals.h" /* Thread-safe global data access */
+#include "error.h" /* Error reporting facilities */
+#include "memory.h" /* Memory allocation facilities */
+#include "object.h" /* Base Object class */
+#include "cmpframe.h" /* Compound Frames */
+#include "cmpmap.h" /* Compound Mappings */
+#include "unitmap.h" /* Unit mappings */
+#include "permmap.h" /* Axis permutations */
+#include "winmap.h" /* Scale and shift mappings */
+#include "frame.h" /* Coordinate systems */
+#include "frameset.h" /* Inter-related coordinate systems */
+#include "keymap.h" /* Hash array */
+#include "plot.h" /* Interface definition for parent class */
+#include "plot3d.h" /* Interface definition for this class */
+#include "grf3d.h" /* The grf3D interface */
+#include "pointset.h" /* Sets of points */
+#include "globals.h" /* Thread-safe global data access */
+
+/* Error code definitions. */
+/* ----------------------- */
+#include "ast_err.h" /* AST error codes */
+
+/* C header files. */
+/* --------------- */
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <math.h>
+#include <limits.h>
+
+/* Module Variables. */
+/* ================= */
+
+/* Address of this static variable is used as a unique identifier for
+ member of this class. */
+static int class_check;
+
+/* Pointers to parent class methods which are used or extended by this
+ class. */
+static AstObject *(* parent_cast)( AstObject *, AstObject *, int * );
+static void (* parent_removeframe)( AstFrameSet *, int, int * );
+static int (* parent_getobjsize)( AstObject *, int * );
+static int (* parent_equal)( AstObject *, AstObject *, int * );
+static void (* parent_vset)( AstObject *, const char *, char **, va_list, int * );
+static void (* parent_clear)( AstObject *, const char *, int * );
+static void (* parent_clearcurrent)( AstFrameSet *, int * );
+static void (* parent_setcurrent)( AstFrameSet *, int, int * );
+static const char *(* parent_getattrib)( AstObject *, const char *, int * );
+static int (* parent_testattrib)( AstObject *, const char *, int * );
+static void (* parent_clearattrib)( AstObject *, const char *, int * );
+static void (* parent_setattrib)( AstObject *, const char *, int * );
+
+/* A FrameSet pointer that is used when calling astCast. */
+static AstFrameSet *dummy_frameset = NULL;
+
+#if defined(THREAD_SAFE)
+static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
+#endif
+
+/* Define macros for accessing each item of thread specific global data. */
+#ifdef THREAD_SAFE
+
+/* Define how to initialise thread-specific globals. */
+#define GLOBAL_inits \
+ globals->Class_Init = 0; \
+ globals->GetAttrib_Buff[ 0 ] = 0;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(Plot3D)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define class_init astGLOBAL(Plot3D,Class_Init)
+#define class_vtab astGLOBAL(Plot3D,Class_Vtab)
+#define getattrib_buff astGLOBAL(Plot3D,GetAttrib_Buff)
+
+static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
+#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
+
+static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 );
+#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 );
+
+/* If thread safety is not needed, declare and initialise globals at static
+ variables. */
+#else
+
+static char getattrib_buff[ 101 ];
+
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstPlot3DVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+
+#define LOCK_MUTEX2
+#define UNLOCK_MUTEX2
+
+#define LOCK_MUTEX3
+#define UNLOCK_MUTEX3
+
+#endif
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+static AstFrameSet *Fset3D( AstFrameSet *, int, int * );
+static AstKeyMap *GetGrfContext( AstPlot *, int * );
+static AstObject *Cast( AstObject *, AstObject *, int * );
+static AstPlot *AxisPlot( AstPlot3D *, int, int *, int * );
+static AstPointSet *ExtendTicks( AstPlot *, AstPointSet *, int * );
+static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
+static const char *RootCornerString( int, int * );
+static int Attr3D( AstKeyMap *, int, double, double *, int, int * );
+static int Border( AstPlot *, int * );
+static int Element2D( AstPlot3D *, int, int *, int *, int * );
+static int Equal( AstObject *, AstObject *, int * );
+static int GetObjSize( AstObject *, int * );
+static int Plot3DAttr( AstKeyMap *, int, double, double *, int );
+static int Plot3DCap( AstKeyMap *, int, int );
+static int Plot3DFlush( AstKeyMap * );
+static int Plot3DLine( AstKeyMap *, int, const float *, const float * );
+static int Plot3DMark( AstKeyMap *, int, const float *, const float *, int );
+static int Plot3DQch( AstKeyMap *, float *, float * );
+static int Plot3DScales( AstKeyMap *, float *, float * );
+static int Plot3DText( AstKeyMap *, const char *, float, float, const char *, float, float );
+static int Plot3DTxExt( AstKeyMap *, const char *, float, float, const char *, float, float, float *, float * );
+static int RootCornerInt( const char *, int * );
+static void BoundingBox( AstPlot *, float[2], float[2], int * );
+static void ChangeRootCorner( AstPlot3D *, int, int, int * );
+static void Clear( AstObject *, const char *, int * );
+static void ClearCurrent( AstFrameSet *, int * );
+static void Clip( AstPlot *, int, const double [], const double [], int * );
+static void Copy( const AstObject *, AstObject *, int * );
+static void CreatePlots( AstPlot3D *, AstFrameSet *, const float *, const double *, int * );
+static void Curve( AstPlot *, const double [], const double [], int * );
+static void Delete( AstObject *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void GenCurve( AstPlot *, AstMapping *, int * );
+static void GrfPop( AstPlot *, int * );
+static void GrfPush( AstPlot *, int * );
+static void GrfSet( AstPlot *, const char *, AstGrfFun, int * );
+static void Grid( AstPlot *, int * );
+static void GridLine( AstPlot *, int, const double [], double, int * );
+static void Mark( AstPlot *, int, int, int, const double *, int, int * );
+static void PolyCurve( AstPlot *, int, int, int, const double *, int * );
+static void RemoveFrame( AstFrameSet *, int, int * );
+static void Set3DGrf( AstPlot3D *, AstPlot *, int, int * );
+static void SetCurrent( AstFrameSet *, int, int * );
+static void SetPlotAttr( AstPlot *, int, int[ 2 ], int * );
+static void SetTickValues( AstPlot *, int, int, double *, int, double *, int * );
+static void SplitFrameSet( AstFrameSet *, AstFrameSet **, int[2], int[2], AstFrameSet **, int[2], int[2], AstFrameSet **, int[2], int[2], int *, int * );
+static void StoreAxisInfo( AstPlot3D *, int[2], int[2], int[2], int[2], int[2], int[2], int * );
+static void Text( AstPlot *, const char *, const double [], const float [2], const char *, int * );
+static void UpdatePlots( AstPlot3D *, int * );
+static void VSet( AstObject *, const char *, char **, va_list, int * );
+
+static const char *GetAttrib( AstObject *, const char *, int * );
+static int TestAttrib( AstObject *, const char *, int * );
+static void ClearAttrib( AstObject *, const char *, int * );
+static void SetAttrib( AstObject *, const char *, int * );
+
+#if defined(THREAD_SAFE)
+static int ManageLock( AstObject *, int, int, AstObject **, int * );
+#endif
+
+/* Declare private member functions that access Plot3D attributes.
+ --------------------------------------------------------------*/
+
+/* Axis independent... */
+
+#define DECLARE_PLOT3D_ACCESSORS(attr,type) \
+ static type Get##attr(AstPlot3D *,int *); \
+ static void Set##attr(AstPlot3D *,type,int *); \
+ static void Clear##attr(AstPlot3D *,int *); \
+ static int Test##attr(AstPlot3D *,int *);
+
+DECLARE_PLOT3D_ACCESSORS(RootCorner,int)
+
+#undef DECLARE_PLOT3D_ACCESSORS
+
+
+/* Axis specific... */
+
+#define DECLARE_PLOT3D_ACCESSORS(attr,type) \
+ static type Get##attr(AstPlot3D *,int,int *); \
+ static void Set##attr(AstPlot3D *,int,type,int *); \
+ static void Clear##attr(AstPlot3D *,int,int *); \
+ static int Test##attr(AstPlot3D *,int,int *);
+
+DECLARE_PLOT3D_ACCESSORS(Norm,double)
+
+#undef DECLARE_PLOT3D_ACCESSORS
+
+
+/* Declare private member functions that access axis-specific attributes
+ inherited from the Plot class. Also declare pointers to hold the parent
+ function pointers.
+ ----------------------------------------------------------------------*/
+
+#define DECLARE_PLOT_ACCESSORS(attr,type) \
+ static type Get##attr(AstPlot *,int,int *); \
+ static void Set##attr(AstPlot *,int,type,int *); \
+ static void Clear##attr(AstPlot *,int,int *); \
+ static type (*parent_get##attr)(AstPlot *,int,int *); \
+ static void (*parent_set##attr)(AstPlot *,int,type,int *); \
+ static void (*parent_clear##attr)(AstPlot *,int,int *);
+
+DECLARE_PLOT_ACCESSORS(MinTick,int)
+DECLARE_PLOT_ACCESSORS(Abbrev,int)
+DECLARE_PLOT_ACCESSORS(Gap,double)
+DECLARE_PLOT_ACCESSORS(LogGap,double)
+DECLARE_PLOT_ACCESSORS(LogPlot,int)
+DECLARE_PLOT_ACCESSORS(LogTicks,int)
+DECLARE_PLOT_ACCESSORS(LogLabel,int)
+DECLARE_PLOT_ACCESSORS(LabelUp,int)
+DECLARE_PLOT_ACCESSORS(DrawAxes,int)
+DECLARE_PLOT_ACCESSORS(LabelUnits,int)
+DECLARE_PLOT_ACCESSORS(MinTickLen,double)
+DECLARE_PLOT_ACCESSORS(MajTickLen,double)
+DECLARE_PLOT_ACCESSORS(NumLab,int)
+DECLARE_PLOT_ACCESSORS(NumLabGap,double)
+DECLARE_PLOT_ACCESSORS(TextLab,int)
+DECLARE_PLOT_ACCESSORS(TextLabGap,double)
+
+#undef DECLARE_PLOT_ACCESSORS
+
+
+/* Declare private member functions that access element-specific attributes
+ inherited from the Plot class. Also declare pointers to hold the parent
+ function pointers.
+ ----------------------------------------------------------------------*/
+
+#define DECLARE_PLOT_ACCESSORS(attr,type) \
+ static void Set##attr(AstPlot *,int,type,int *); \
+ static void Clear##attr(AstPlot *,int,int *); \
+ static void (*parent_set##attr)(AstPlot *,int,type,int *); \
+ static void (*parent_clear##attr)(AstPlot *,int,int *);
+
+DECLARE_PLOT_ACCESSORS(Style,int)
+DECLARE_PLOT_ACCESSORS(Font,int)
+DECLARE_PLOT_ACCESSORS(Colour,int)
+DECLARE_PLOT_ACCESSORS(Width,double)
+DECLARE_PLOT_ACCESSORS(Size,double)
+
+#undef DECLARE_PLOT_ACCESSORS
+
+
+/* Declare private member functions that access attributes inherited from
+ the Plot class that do not need to override the Get method. This
+ includes attributes that are not axis-specific or that do not have
+ dynamic defaults. Also declare pointers to hold the parent function
+ pointers.
+ ----------------------------------------------------------------------*/
+#define DECLARE_PLOT_ACCESSORS(attr,type) \
+ static void Set##attr(AstPlot *,type,int *); \
+ static void Clear##attr(AstPlot *,int *); \
+ static void (*parent_set##attr)(AstPlot *,type,int *); \
+ static void (*parent_clear##attr)(AstPlot *,int *);
+
+DECLARE_PLOT_ACCESSORS(Ink,int)
+DECLARE_PLOT_ACCESSORS(Tol,double)
+DECLARE_PLOT_ACCESSORS(Invisible,int)
+DECLARE_PLOT_ACCESSORS(TickAll,int)
+DECLARE_PLOT_ACCESSORS(ForceExterior,int)
+DECLARE_PLOT_ACCESSORS(Border,int)
+DECLARE_PLOT_ACCESSORS(Clip,int)
+DECLARE_PLOT_ACCESSORS(ClipOp,int)
+DECLARE_PLOT_ACCESSORS(Escape,int)
+DECLARE_PLOT_ACCESSORS(Grid,int)
+DECLARE_PLOT_ACCESSORS(Labelling,int)
+
+#undef DECLARE_PLOT_ACCESSORS
+
+
+
+/* Member functions. */
+/* ================= */
+
+static int Attr3D( AstKeyMap *grfconID, int attr, double value,
+ double *old_value, int prim, int *status ){
+/*
+* Name:
+* Attr3D
+
+* Purpose:
+* Get or set the value of a 3D grf attribute.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* Attr3D( AstKeyMap *grfconID, int attr, double value, double *old_value,
+* int prim, int *status )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function gets or sets the current value of a specified graphics
+* attribute in the parent Plot structure of a Plot3D. It forwards the
+* call to the grf3D module being used by this Plot3D. It should be
+* registered with the parent Plot using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap.
+* attr
+* An integer value identifying the required attribute. This should
+* be one of the symbolic values defined in grf.h.
+* value
+* A new value to store for the attribute. If this is AST__BAD
+* no value is stored.
+* old_value
+* A pointer to a double in which to return the attribute value.
+* If this is NULL, no value is returned.
+* prim
+* The sort of graphics primitive to be drawn with the new attribute.
+* Identified by one of the values defined in grf.h.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ int result;
+
+/* Check the inherited status. */
+ if( !astOK ) return 0;
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Use the function in the external Grf3D module, selected at link-time
+ using ast_link options. */
+ result = astG3DAttr( attr, value, old_value, prim );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+
+/* Return the result. */
+ return result;
+}
+
+static AstPlot *AxisPlot( AstPlot3D *this, int axis3d, int *axis2d, int *status ){
+/*
+* Name:
+* AxisPlot
+
+* Purpose:
+* Find the Plot used to label a 3D axis.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstPlot *AxisPlot( AstPlot3D *this, int axis3d, int *axis2d, int *status )
+
+* Class Membership:
+* Plot method.
+
+* Description:
+* This function returns a pointer to the encapsulated 2D Plot that
+* is used to label the given 3D axis. It also returns the index
+* of the labelled axis within the 2D Plot.
+
+* Parameters:
+* this
+* Pointer to a Plot3D.
+* axis3d
+* A zero-based axis index within the Plot3D.
+* axis2d
+* Pointer to an int in which to put the index of the labelled axis
+* within the returned 2D Plot.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the Plot used to label the 3D axis. Do not annul this
+* pointer.
+
+*-
+*/
+
+/* Local Variables: */
+ AstPlot *plot;
+
+/* Check the global status. */
+ if( !astOK ) return NULL;
+
+/* Return the required information form the Plot3D structure. */
+ plot = GET_PLOT( this->axis_plot1[ axis3d ] );
+ if( ! plot ) {
+ astError( AST__INTER, "AxisPlot(Plot3D): Illegal value %d "
+ "for axis3d (internal AST programming error).", status,
+ this->axis_plot1[ axis3d ] );
+ }
+
+ *axis2d = this->axis_index1[ axis3d ];
+
+ return plot;
+}
+
+static int Border( AstPlot *this_plot, int *status ){
+/*
+* Name:
+* Border
+
+* Purpose:
+* Draw a border around valid regions of a Plot.
+
+* Type:
+* Private member function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Border( AstPlot *this, int *status )
+
+* Class Membership:
+* Plot method (overrides the astBorder method inherited from the
+* Plot class)
+
+* Description:
+* This function draws a (line) border around regions of the
+* plotting area of a Plot which correspond to valid, unclipped
+* physical coordinates. For example, when plotting using an
+* all-sky map projection, this function could be used to draw the
+* boundary of the celestial sphere when it is projected on to the
+* plotting surface.
+*
+* If the entire plotting area contains valid, unclipped physical
+* coordinates, then the boundary will just be a rectangular box
+* around the edges of the plotting area.
+
+* Parameters:
+* this
+* Pointer to the Plot.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Zero is returned if the plotting area is completely filled by
+* valid, unclipped physical coordinates (so that only a
+* rectangular box was drawn around the edge). Otherwise, one is
+* returned.
+
+* Notes:
+* - The Plot3D implementation of this method, invokes the astBorder
+* method on each of the three encapsulated Plots, and returns the
+* logical OR of the three returned flags.
+* - A value of zero will be returned if this function is invoked
+* with the AST error status set, or if it should fail for any
+* reason.
+* - An error results if either the current Frame or the base Frame
+* of the Plot is not 2-dimensional, or (for a Plot3D) 3-dimensional.
+* - An error also results if the transformation between the base
+* and current Frames of the Plot is not defined (i.e. the Plot's
+* TranForward attribute is zero).
+*/
+
+/* Local Variables: */
+ AstPlot3D *this;
+ const char *class;
+ const char *method;
+ float x1;
+ float y1;
+ float z1;
+ float x[ 2 ];
+ float y[ 2 ];
+ float z[ 2 ];
+ int flag1;
+ int flag2;
+ int flag3;
+ int naxes;
+ int ok;
+ int result;
+ int root_corner;
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* Get a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_plot;
+
+/* Store the current method, and the class of the supplied object for use
+ in error messages.*/
+ method = "astBorder";
+ class = astGetClass( this );
+
+/* Check the base Frame of the Plot is 3-D. */
+ naxes = astGetNin( this );
+ if( naxes != 3 && astOK ){
+ astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base "
+ "Frame of the supplied %s is invalid - this number should "
+ "be 3.", status, method, class, naxes, class );
+ }
+
+/* Check the current Frame of the Plot is 3-D. */
+ naxes = astGetNout( this );
+ if( naxes != 3 && astOK ){
+ astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the current "
+ "Frame of the supplied %s is invalid - this number should "
+ "be 3.", status, method, class, naxes, class );
+ }
+
+/* Invoke the astBorder method on each of the three encapsulated Plots. */
+ flag1 = astBorder( this->plotxy );
+ flag2 = astBorder( this->plotxz );
+ flag3 = astBorder( this->plotyz );
+
+/* If no bad values were encountered in any of the Plots, draw lines
+ along the remaining plot edges. */
+ result = ( flag1 || flag2 || flag3 );
+ if( !result ) {
+
+/* The three remaining edges to be drawn all meet at the corner
+ diagonally opposite the root corner. Get the root corner. */
+ root_corner = astGetRootCorner( this );
+
+/* The (x0,y0,z0) position is the graphics coords at the corner
+ diagonally opposite the root corner. The x1, y1 and z1 values
+ are the graphics x, y and z values at the ends of the three
+ lines that remain to be drawn. */
+ if( root_corner & 1 ) {
+ x[ 0 ] = this->gbox[ 0 ];
+ x1 = this->gbox[ 3 ];
+ } else {
+ x[ 0 ] = this->gbox[ 3 ];
+ x1 = this->gbox[ 0 ];
+ }
+
+ if( root_corner & 2 ) {
+ y[ 0 ] = this->gbox[ 1 ];
+ y1 = this->gbox[ 4 ];
+ } else {
+ y[ 0 ] = this->gbox[ 4 ];
+ y1 = this->gbox[ 1 ];
+ }
+
+ if( root_corner & 4 ) {
+ z[ 0 ] = this->gbox[ 2 ];
+ z1 = this->gbox[ 5 ];
+ } else {
+ z[ 0 ] = this->gbox[ 5 ];
+ z1 = this->gbox[ 2 ];
+ }
+
+/* Establish the correct graphical attributes as defined by attributes
+ with the supplied Plot. */
+ astGrfAttrs( this, AST__BORDER_ID, 1, GRF__LINE, method, class );
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Draw the remaining line parallel to the X axis. */
+ x[ 1 ] = x1;
+ y[ 1 ] = y[ 0 ];
+ z[ 1 ] = z[ 0 ];
+ ok = astG3DLine( 2, x, y, z );
+
+/* Draw the remaining line parallel to the Y axis. */
+ x[ 1 ] = x[ 0 ];
+ y[ 1 ] = y1;
+ z[ 1 ] = z[ 0 ];
+ ok = ok && astG3DLine( 2, x, y, z );
+
+/* Draw the remaining line parallel to the X axis. */
+ x[ 1 ] = x[ 0 ];
+ y[ 1 ] = y[ 0 ];
+ z[ 1 ] = z1;
+ ok = ok && astG3DLine( 2, x, y, z );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+
+/* Re-establish the original graphical attributes. */
+ astGrfAttrs( this, AST__BORDER_ID, 0, GRF__LINE, method, class );
+
+/* Report an error if anything went wrong in the grf3d module. */
+ if( !ok && astOK ) {
+ astError( AST__GRFER, "%s(%s): Graphics error in astG3DLine. ", status,
+ method, class );
+ }
+ }
+
+/* Return zero if an error has occurrred. */
+ if( !astOK ) result = 0;
+
+/* Return a flag indicating if any bad values were encountered in any of
+ the Plots. */
+ return result;
+}
+
+static AstObject *Cast( AstObject *this_object, AstObject *obj, int *status ) {
+/*
+* Name:
+* Cast
+
+* Purpose:
+* Cast an Object into an instance of a sub-class.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstObject *Cast( AstObject *this, AstObject *obj, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the protected astCast
+* method inherited from the Frame class).
+
+* Description:
+* This function returns a deep copy of an ancestral component of the
+* supplied object. The required class of the ancestral component is
+* specified by another object. Specifically, if "this" and "new" are
+* of the same class, a copy of "this" is returned. If "this" is an
+* instance of a subclass of "obj", then a copy of the component
+* of "this" that matches the class of "obj" is returned. Otherwise,
+* a NULL pointer is returned without error.
+
+* Parameters:
+* this
+* Pointer to the Object to be cast.
+* obj
+* Pointer to an Object that defines the class of the returned Object.
+* The returned Object will be of the same class as "obj".
+
+* Returned Value:
+* A pointer to the new Object. NULL if "this" is not a sub-class of
+* "obj", or if an error occurs.
+
+* Notes:
+* - A NULL pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*/
+
+/* Local Variables; */
+ AstObject *new;
+ astDECLARE_GLOBALS
+ int generation_gap;
+
+/* Initialise */
+ new = NULL;
+
+/* Check inherited status */
+ if( !astOK ) return new;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* See how many steps up the class inheritance ladder it is from "obj"
+ to this class (Plot3D). A positive value is returned if Plot3D
+ is a sub-class of "obj". A negative value is returned if "obj" is
+ a sub-class of Plot3D. Zero is returned if "obj" is a Plot3D.
+ AST__COUSIN is returned if "obj" is not on the same line of descent
+ as Plot3D. */
+ generation_gap = astClassCompare( (AstObjectVtab *) &class_vtab,
+ astVTAB( obj ) );
+
+/* If "obj" is a Plot3D or a sub-class of Plot3D, we can cast by
+ truncating the vtab for "this" so that it matches the vtab of "obJ",
+ and then taking a deep copy of "this". */
+ if( generation_gap <= 0 && generation_gap != AST__COUSIN ) {
+ new = astCastCopy( this_object, obj );
+
+/* If "obj" is a Plot (the parent class), we cast by returning a deep
+ copy of the Plot covering the XY face. */
+ } else if( generation_gap == 1 ) {
+ new = astCopy( ( (AstPlot3D *) this_object)->plotxy );
+
+/* If "obj" is a FrameSet or higher, we attempt to use the implementation
+ inherited from the parent class to cast the FrameSet component into the
+ class indicated by "obj". */
+ } else {
+ new = (*parent_cast)( this_object, obj, status );
+ }
+
+/* Return the new pointer. */
+ return new;
+}
+
+static void ChangeRootCorner( AstPlot3D *this, int old, int new, int *status ){
+/*
+* Name:
+* ChangeRootCorner
+
+* Purpose:
+* Use a new RootCorner value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void ChangeRootCorner( AstPlot3D *this, int old, int new, int *status )
+
+* Class Membership:
+* Plot method.
+
+* Description:
+* This function sets the attributes of the encapsulated Plots so that
+* labels appear on the edges of the 3D graphics cube that join at the
+* specified new root corner. It also reverses the graphics axes in
+* the encapsulated Plots as required in order to ensure that the Plots
+* look "normal" when viewed from the outside of the 3D graphics cube.
+* This happens if a the Plot used to label a specific axis moves from
+* one face the the 3D graphics cube to the opposite face.
+
+* Parameters:
+* this
+* Pointer to a Plot3D.
+* old
+* The old RootCorner value.
+* new
+* The new RootCorner value.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - Each RootCorner value is in the range 0 to 7 and is a 3 bit
+* bit-mask. Bit 0 describes the 3D graphics X axis, bit 1 describes
+* the graphics Y axis and bit 2 describes the graphics Z axis. If a
+* bit is set it means that the corner is at the upper bound on the
+* corresponding axis. If a bit is unset it means that the corner is
+* at the lower bound on the corresponding axis.
+
+*-
+*/
+
+/* Local Variables: */
+ AstKeyMap *grfcon;
+ AstPlot *plot;
+ AstPlot *plots[ 24 ];
+ int edges[ 24 ];
+ int axes[ 24 ];
+ int axis2d;
+ int edge;
+ int i;
+ int np;
+ int xeqy;
+ int xeqz;
+
+/* Check the global status. */
+ if( !astOK ) return;
+
+/* If the corner has moved on the 3D X axis (from upper X bound to lower X
+ bound or vice-versa), mirror the axis of the encapsulated Plot that is
+ perpendicular to the 3D X axis. This means that the Plot can be thought
+ of as being viewed from the outside of the 3D graphics cube. Also,
+ update the constant X axis value at which the YZ plane is drawn. */
+ if( ( old & 1 ) != ( new & 1 ) ) astMirror( this->plotyz, 0 );
+ grfcon = (AstKeyMap *) astGetGrfContext( this->plotyz );
+ astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 1 ) ? 3 : 0 ], "Constant X value" );
+ astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" );
+ grfcon= astAnnul( grfcon );
+
+/* Likewise mirror the other two axes if required. */
+ if( ( old & 2 ) != ( new & 2 ) ) astMirror( this->plotxz, 0 );
+ grfcon = (AstKeyMap *) astGetGrfContext( this->plotxz );
+ astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 2 ) ? 4 : 1 ], "Constant Y value" );
+ astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" );
+ grfcon= astAnnul( grfcon );
+
+ if( ( old & 4 ) != ( new & 4 ) ) astMirror( this->plotxy, 0 );
+ grfcon = (AstKeyMap *) astGetGrfContext( this->plotxy );
+ astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 4 ) ? 5 : 2 ], "Constant Z value" );
+ astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" );
+ grfcon= astAnnul( grfcon );
+
+/* Set a flag saying whether the limits are equal at the new corner for
+ the X and Y axes. */
+ xeqy = ( ( ( new & 1 ) > 0 ) == ( ( new & 2 ) > 0 ) );
+
+/* Set a flag saying whether the limits are equal at the new corner for
+ the X and Z axes. */
+ xeqz = ( ( ( new & 1 ) > 0 ) == ( ( new & 4 ) > 0 ) );
+
+/* Ensure all Edge attributes are clear. This means that the public
+ attribute accessors routines used below will return dynamic defaults for
+ the Edge attributes. */
+ astClearEdge( this->plotxy, 0 );
+ astClearEdge( this->plotxy, 1 );
+ astClearEdge( this->plotxz, 0 );
+ astClearEdge( this->plotxz, 1 );
+ astClearEdge( this->plotyz, 0 );
+ astClearEdge( this->plotyz, 1 );
+
+/* So far we have recorded no edges changes. */
+ np = 0;
+
+/* We now adjust the Edge attributes in the Plot used to annotate the 3D
+ X axis in order to get the X axis labels on the correct edge of the 3D
+ graphics cube. Get the Plot used to produce X axis labels (this will
+ be either this->plotxy or this->plotxz). */
+ plot = AxisPlot( this, 0, &axis2d, status );
+
+/* See what edge of the Plot is used to annotate the first of the two WCS
+ axis described by the 2D Plot. If the Edge(1) attribute has not been
+ assigned a value, then a dynamic default will be used by the Plot class.
+ We want to know what this dynamic default is, so we first cleared the
+ attribute above. Now we set the attribute value explicitly to the dynamic
+ default returned by astGetC. Note, we use astGetC rather than astGetEdge
+ because the dynamic default is not calculated when calling astGetEdge. */
+ astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" ));
+ edge = astGetEdge( plot, 0 );
+ astClearEdge( plot, 0 );
+
+/* If the 3D X axis is labelled using the Plot that spans the XY plane... */
+ if( plot == this->plotxy ) {
+
+/* ... and if the new root corner is at the upper limit on the Y axis... */
+ if( new & 2 ) {
+
+/* If the first WCS axis is currently labelled on either the top or bottom
+ edge, ensure it is labelled on the upper Y (top) edge. */
+ if( edge == 3 || edge == 1 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = TOP;
+
+/* Otherwise ensure that the second WCS axis is labelled on the upper Y (top)
+ edge. */
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = TOP;
+ }
+
+/* If the new root corner is at the lower limit on the Y axis... */
+ } else {
+
+/* If the first WCS axis is currently labelled on either the top or bottom
+ edge, ensure it is labelled on the lower Y (bottom) edge. */
+ if( edge == 3 || edge == 1 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = BOTTOM;
+
+/* Otherwise ensure that the second WCS axis is labelled on the lower Y
+ (bottom) edge. */
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = BOTTOM;
+ }
+ }
+
+/* If the 3D X axis is labelled using the Plot that spans the XZ plane... */
+ } else {
+
+/* ... and if the new root corner is at the upper limit on the Z axis... */
+ if( new & 4 ) {
+
+/* If the first WCS axis is currently labelled on either the top or bottom
+ edge, ensure it is labelled on the upper Z (top) edge. */
+ if( edge == 3 || edge == 1 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = TOP;
+
+/* Otherwise ensure that the second WCS axis is labelled on the upper Y (top)
+ edge. */
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = TOP;
+ }
+
+/* If the new root corner is at the lower limit on the Z axis... */
+ } else {
+
+/* If the first WCS axis is currently labelled on either the top or bottom
+ edge, ensure it is labelled on the lower Z (bottom) edge. */
+ if( edge == 3 || edge == 1 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = BOTTOM;
+
+/* Otherwise ensure that the second WCS axis is labelled on the lower Z
+ (bottom) edge. */
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = BOTTOM;
+ }
+ }
+ }
+
+/* We now adjust the Edge attributes in the Plot used to annotate the 3D
+ Y axis in order to get the Y axis labels on the correct edge of the 3D
+ graphics cube. Get the Plot used to produce Y axis labels. */
+ plot = AxisPlot( this, 1, &axis2d, status );
+
+/* See what edge of the Plot is used to annotate the first of the two WCS
+ axis described by the Plot. */
+ astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" ));
+ edge = astGetEdge( plot, 0 );
+ astClearEdge( plot, 0 );
+
+/* If the 3D Y axis is labelled using the Plot that spans the XY plane... */
+ if( plot == this->plotxy ) {
+
+/* ... and if the new root corner is at the same limit on the X and Z axes,
+ put Y labels on the right side of the Plot. */
+ if( xeqz ) {
+ if( edge == 0 || edge == 2 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = RIGHT;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = RIGHT;
+ }
+
+/* If the new root corner is at a different limit on the X and Z axes,
+ put Y labels on the left side of the Plot. */
+ } else {
+ if( edge == 0 || edge == 2 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = LEFT;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = LEFT;
+ }
+ }
+
+/* If the 3D Y axis is labelled using the Plot that spans the YZ plane... */
+ } else {
+
+/* ... and if the new root corner is at the upper Z limit, put Y labels on
+ the top of the Plot. */
+ if( new & 4 ) {
+ if( edge == 1 || edge == 3 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = TOP;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = TOP;
+ }
+
+/* If the new root corner is at the lower Z limit, put Y labels on the
+ bottom of the Plot. */
+ } else {
+ if( edge == 1 || edge == 3 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = BOTTOM;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = BOTTOM;
+ }
+ }
+ }
+
+/* We now adjust the Edge attributes in the Plot used to annotate the 3D
+ Z axis in order to get the Z axis labels on the correct edge of the 3D
+ graphics cube. Get the Plot used to produce Z axis labels. */
+ plot = AxisPlot( this, 2, &axis2d, status );
+
+/* See what edge of the Plot is used to annotate the first of the two WCS
+ axis described by the Plot. */
+ astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" ));
+ edge = astGetEdge( plot, 0 );
+ astClearEdge( plot, 0 );
+
+/* If the 3D Z axis is labelled using the Plot that spans the XZ plane... */
+ if( plot == this->plotxz ) {
+
+/* ... and if the new root corner is at the same limit on the X and Y axes,
+ put Z labels on the left side of the Plot. */
+ if( xeqy ) {
+ if( edge == 0 || edge == 2 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = LEFT;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = LEFT;
+ }
+
+/* If the new root corner is at a different limit on the X and Y axes,
+ put Y labels on the right side of the Plot. */
+ } else {
+ if( edge == 0 || edge == 2 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = RIGHT;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = RIGHT;
+ }
+ }
+
+/* If the 3D Z axis is labelled using the Plot that spans the YZ plane... */
+ } else {
+
+/* ... and if the new root corner is at the same limit on the X and Y axes,
+ put Z labels on the right side of the Plot. */
+ if( xeqz ) {
+ if( edge == 0 || edge == 2 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = RIGHT;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = RIGHT;
+ }
+
+/* If the new root corner is at a different limit on the X and Y axes,
+ put Y labels on the left side of the Plot. */
+ } else {
+ if( edge == 0 || edge == 2 ) {
+ plots[ np ] = plot;
+ axes[ np ] = 0;
+ edges[ np++ ] = LEFT;
+ } else {
+ plots[ np ] = plot;
+ axes[ np ] = 1;
+ edges[ np++ ] = LEFT;
+ }
+ }
+ }
+
+/* Apply the set of edge changes determined above. */
+ for( i = 0; i < np; i++ ) {
+ astSetEdge( plots[ i ], axes[ i ], edges[ i ] );
+ }
+
+/* Ensure that the 2 Plot axes that are not being used have suitable values
+ for their attributes. That is, no labels are drawn, and the ticked
+ edges are the one that meet at the new RootCorner. */
+
+ if( !astTestEdge( this->plotxy, 0 ) ) {
+ astSetEdge( this->plotxy, 0, ( new & 2 ) ? TOP : BOTTOM );
+ }
+
+ if( !astTestEdge( this->plotxy, 1 ) ) {
+ astSetEdge( this->plotxy, 1, xeqz ? RIGHT: LEFT );
+ }
+
+ if( !astTestEdge( this->plotxz, 0 ) ) {
+ astSetEdge( this->plotxz, 0, ( new & 4 ) ? TOP : BOTTOM );
+ }
+
+ if( !astTestEdge( this->plotxz, 1 ) ) {
+ astSetEdge( this->plotxz, 1, xeqy ? LEFT : RIGHT );
+ }
+
+ if( !astTestEdge( this->plotyz, 0 ) ) {
+ astSetEdge( this->plotyz, 0, ( new & 4 ) ? TOP : BOTTOM );
+ }
+
+ if( !astTestEdge( this->plotyz, 1 ) ) {
+ astSetEdge( this->plotyz, 1, xeqy ? RIGHT : LEFT );
+ }
+
+
+}
+
+static void Clear( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* Clear
+
+* Purpose:
+* Clear attribute values for a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void Clear( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the public astClear method
+* inherited from the Object class).
+
+* Description:
+* This function clears the values of a specified set of attributes
+* for a Plot3D. Clearing an attribute cancels any value that has
+* previously been explicitly set for it, so that the standard
+* default attribute value will subsequently be used instead. This
+* also causes the astTest function to return the value zero for
+* the attribute, indicating that no value has been set.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* attrib
+* Pointer to a null-terminated character string containing a
+* comma-separated list of the names of the attributes to be
+* cleared.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This function preserves the integrity of the Plot3D (if
+* possible) by appropriately modifying the three encapsulated Plots.
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Invoke the parent astClear method to clear the Plot3D's attribute values. */
+ (*parent_clear)( this_object, attrib, status );
+
+/* Update the three 2D Plots stored in the Plot3D structure so that they
+ reflect this modified FrameSet. */
+ UpdatePlots( (AstPlot3D *) this_object, status );
+
+}
+
+static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* ClearAttrib
+
+* Purpose:
+* Clear an attribute value for a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot.h"
+* void ClearAttrib( AstObject *this, const char *attrib )
+
+* Class Membership:
+* Plot3D member function (over-rides the astClearAttrib protected
+* method inherited from the Plot class).
+
+* Description:
+* This function clears the value of a specified attribute for a
+* Plot3D, so that the default value will subsequently be used.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+*/
+
+/* Local Variables: */
+ AstPlot3D *this; /* Pointer to the Plot3D structure */
+ AstPlot *plot; /* Pointer to specific Plot */
+ char attname[50]; /* Plot attribute base name */
+ char patt[50]; /* Plot attribute full name */
+ char spec[10]; /* Plane specification */
+ int axis; /* Axis index */
+ int len; /* Length of attrib string */
+ int nc; /* Number of characters read */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Obtain the length of the "attrib" string. */
+ len = strlen( attrib );
+
+/* Check the attribute name and clear the appropriate attribute. */
+
+/* Norm. */
+/* ----------- */
+ if ( !strcmp( attrib, "norm" ) ) {
+ astClearNorm( this, 0 );
+ astClearNorm( this, 1 );
+ astClearNorm( this, 2 );
+
+/* Norm(axis). */
+/* ----------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) )
+ && ( nc >= len ) ) {
+ astClearNorm( this, axis - 1 );
+
+/* RootCorner. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "rootcorner" ) ) {
+ astClearRootCorner( this );
+
+/* ..._XY etc */
+/* ---------- */
+ } else if ( nc = 0,
+ ( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec,
+ &nc ) ) ) {
+
+ if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
+ plot = this->plotxy;
+ } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
+ plot = this->plotyz;
+ } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
+ plot = this->plotxz;
+ } else {
+ plot = NULL;
+ }
+
+ if( plot ) {
+ sprintf( patt, "%s%s", attname, attrib + nc );
+ astClearAttrib( plot, patt );
+
+ } else {
+ (*parent_clearattrib)( this_object, attrib, status );
+ }
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ (*parent_clearattrib)( this_object, attrib, status );
+ }
+}
+
+static void ClearCurrent( AstFrameSet *this_frameset, int *status ) {
+/*
+* Name:
+* ClearCurrent
+
+* Purpose:
+* Clear the value of the Current attribute for a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int astClearCurrent( AstFrameSet *this )
+
+* Class Membership:
+* Plot3D member function (over-rides the public astClearCurrent method
+* inherited from the FrameSet class).
+
+* Description:
+* This function clears the value of the Current attribute for a
+* Plot3D. This attribute is an index that identifies the current
+* Frame for the Plot3D.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+*/
+
+/* Invoke the parent astClearCurrent method. */
+ (*parent_clearcurrent)( this_frameset, status );
+
+/* Update the three 2D Plots stored in the Plot3D structure so that they
+ reflect this modified FrameSet. */
+ UpdatePlots( (AstPlot3D *) this_frameset, status );
+}
+
+static void ClearRootCorner( AstPlot3D *this, int *status ){
+/*
+*+
+* Name:
+* astClearRootCorner
+
+* Purpose:
+* Clear the RootCorner attribute.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void astClearRootCorner( AstPlot3D *this )
+
+* Class Membership:
+* Plot method.
+
+* Description:
+* This function clears the RootCorner attribute.
+
+* Parameters:
+* this
+* Pointer to a Plot3D.
+
+*-
+*/
+
+/* Local Variables: */
+ int old;
+ int new;
+
+/* Check the global status. */
+ if( !astOK ) return;
+
+/* Get the current rootcorner value. */
+ old = astGetRootCorner( this );
+
+/* Clear the RootCorner attribute. */
+ this->rootcorner = -1;
+
+/* Get the new (default) rootcorner value. */
+ new = astGetRootCorner( this );
+
+/* If the root corner has changed, mirror any axes of the encapsulated Plots
+ that need mirroring (this is done to ensure that Plots look right when
+ viewed from the outside of the graphics cube), and modify the Edge
+ attributes in the encapsulated Plots to ensure the labels appear on the
+ requested edges of the 3D graphics cube. . */
+ if( old != new ) ChangeRootCorner( this, old, new, status );
+}
+
+static void CreatePlots( AstPlot3D *this, AstFrameSet *fset, const float *gbox,
+ const double *bbox, int *status ) {
+/*
+* Name:
+* CreatePlots
+
+* Purpose:
+* Create three 2D plots and store in the Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void CreatePlots( AstPlot3D *this, AstFrameSet *fset, const float *gbox,
+ const double *bbox, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function splits the supplied FrameSet up into 3 independent 2D
+* FrameSets, each describing a 2D plane in the supplied 3D FrameSet.
+* It then uses these 2D FrameSets to create three Plots, one for each
+* plane in the graphics plotting space, and stores them in the Plot3D.
+*
+* Each of the three Plots is notionally pasted onto one face of the
+* 3D graphics cube (the RootCorner attribute is used to determine which
+* of the two parallel faces a particular Plot is pasted onto). The
+* Plot is pasted in such a way that, when viewed from the outside of
+* the graphics cube, the first graphics axis increases left to right
+* and the second increases bottom to top (this assumes that "up" is
+* parallel to the 3D Z axis).
+*
+* Initially, the Plots are created assuming the default RootCorner
+* value ("LLL"). They will be changed later if the value of the
+* RootCorner attribute is changed.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* fset
+* Pointer to the FrameSet.
+* gbox
+* A pointer to an array of 6 values giving the graphics coordinates
+* of the bottom left and top right corners of a box on the graphics
+* output device. The first triple of values should be the graphics
+* coordinates of the bottom left corner of the box and the second
+* triple of values are the graphics coordinates of the top right corner.
+* bbox
+* A pointer to an array of 6 values giving the coordinates in the
+* supplied Frame or base Frame of the supplied FrameSet at the bottom
+* left and top right corners of the box specified by parameter gbox.
+* These should be supplied in the same order as for parameter "gbox".
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - Each returned plot has 3 Frames: Frame 1 is the base (GRAPHICS)
+* Frame; Frame 2 is spanned by 2 of the 3 axes in the base Frame of
+* the supplied FrameSet; Frame 3 is the current Frame and is spanned
+* by 2 of the 3 axes in the current Frame of the supplied FrameSet.
+* Any future changes to this function that alter this structure should
+* reflected in equivalent changes to function UpdatePlots.
+
+*/
+
+/* Local Variables: */
+ AstFrameSet *fsetxy;
+ AstFrameSet *fsetxz;
+ AstFrameSet *fsetyz;
+ double basebox2d[ 4 ];
+ float graphbox2d[ 4 ];
+ int baseplane;
+ int labelxy[ 2 ];
+ int labelxz[ 2 ];
+ int labelyz[ 2 ];
+ int wcsxy[ 2 ];
+ int wcsxz[ 2 ];
+ int wcsyz[ 2 ];
+
+/* Check the inherited status. */
+ if( !astOK ) return;
+
+/* Split the supplied FrameSet up into 3 FrameSets, each with a 2D base
+ and current Frame. Each of these FrameSets describes one plane of
+ the 3D cube. One of them will be spanned by two axes picked from the
+ supplied 3D FrameSet. The other two FrameSets will each include a copy
+ of the remaining 3rd axis from the supplied FrameSet, plus an extra
+ dummy axis. These dummy axes will never be labelled. */
+ SplitFrameSet( fset, &fsetxy, labelxy, wcsxy, &fsetxz, labelxz, wcsxz,
+ &fsetyz, labelyz, wcsyz, &baseplane, status );
+
+/* If OK, annul any existing 2D plots. */
+ if( astOK ) {
+ if( this->plotxy ) this->plotxy = astAnnul( this->plotxy );
+ if( this->plotxz ) this->plotxz = astAnnul( this->plotxz );
+ if( this->plotyz ) this->plotyz = astAnnul( this->plotyz );
+
+/* Create three Plots; one for each 2D plane in the graphics plotting
+ space. Set the attributes of these plots so that the required axes are
+ labelled and other axes are left blank. The "graphbox2d" and "basebox2d"
+ values used to create each Plot define the sense, as well as the extent,
+ of each axis. The first pair of values in each give the lower left corner
+ of the Plot and the second pair give the top right corner. We want each
+ Plot to have X increasing left to right and Y increasing bottom to
+ top when viewed from the outside of the cube. We assume an initial
+ RootCorner value of "LLL" (that is, the Plots are pasted onto the cube
+ faces that meet at the lower limit on every axis). */
+ graphbox2d[ 0 ] = gbox[ 3 ];
+ graphbox2d[ 1 ] = gbox[ 1 ];
+ graphbox2d[ 2 ] = gbox[ 0 ];
+ graphbox2d[ 3 ] = gbox[ 4 ];
+
+ basebox2d[ 0 ] = bbox[ 3 ];
+ basebox2d[ 1 ] = bbox[ 1 ];
+ basebox2d[ 2 ] = bbox[ 0 ];
+ basebox2d[ 3 ] = bbox[ 4 ];
+
+ if( this->plotxy ) this->plotxy = astAnnul( this->plotxy );
+ this->plotxy = astPlot( fsetxy, graphbox2d, basebox2d, "", status );
+ SetPlotAttr( this->plotxy, XY, labelxy, status );
+
+ graphbox2d[ 0 ] = gbox[ 0 ];
+ graphbox2d[ 1 ] = gbox[ 2 ];
+ graphbox2d[ 2 ] = gbox[ 3 ];
+ graphbox2d[ 3 ] = gbox[ 5 ];
+
+ basebox2d[ 0 ] = bbox[ 0 ];
+ basebox2d[ 1 ] = bbox[ 2 ];
+ basebox2d[ 2 ] = bbox[ 3 ];
+ basebox2d[ 3 ] = bbox[ 5 ];
+
+ this->plotxz = astPlot( fsetxz, graphbox2d, basebox2d, "", status );
+ SetPlotAttr( this->plotxz, XZ, labelxz, status );
+
+ graphbox2d[ 0 ] = gbox[ 4 ];
+ graphbox2d[ 1 ] = gbox[ 2 ];
+ graphbox2d[ 2 ] = gbox[ 1 ];
+ graphbox2d[ 3 ] = gbox[ 5 ];
+
+ basebox2d[ 0 ] = bbox[ 4 ];
+ basebox2d[ 1 ] = bbox[ 2 ];
+ basebox2d[ 2 ] = bbox[ 1 ];
+ basebox2d[ 3 ] = bbox[ 5 ];
+
+ this->plotyz = astPlot( fsetyz, graphbox2d, basebox2d, "", status );
+ SetPlotAttr( this->plotyz, YZ, labelyz, status );
+
+/* Store information that allows each 3D WCS axis to be associatedf with
+ a pair of Plots. Also store the WCS axis within each Plot that
+ corresponds to the 3D WCS axis. */
+ StoreAxisInfo( this, labelxy, wcsxy, labelxz, wcsxz, labelyz, wcsyz, status );
+
+/* Store the Plot that spans two connected 3D axes. */
+ this->baseplot = baseplane;
+
+/* Free resources */
+ fsetxy = astAnnul( fsetxy );
+ fsetxz = astAnnul( fsetxz );
+ fsetyz = astAnnul( fsetyz );
+
+ }
+}
+
+static int Element2D( AstPlot3D *this, int element, int *elem2d1,
+ int *elem2d2, int *status ){
+/*
+* Name:
+* Element2D
+
+* Purpose:
+* Convert a 3D graphics element identifier to a corresponding pair of
+* 2D identifiers.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Element2D( AstPlot3D *this, int element, int *elem2d1,
+* int *elem2d2, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function takes an integer identifier for an element of a 3D
+* annotated grid (e.g. ticks, axis 1 labels, border, etc), and returns
+* a element identifers that can be used with the encapsualted 2D Plots.
+
+* Parameters:
+* this
+* Pointer to the Plot2D structure.
+* element
+* The 3D element identifier to convert.
+* elem2d1
+* Pointer to an int in which to return the 2D element identifier
+* to use with the first of the two Plots that span the axis to
+* which the 3D element identifier refers. Returned holding 0 if
+* the given 3D element identifier is not axis specific.
+* elem2d2
+* Pointer to an int in which to return the 2D element identifier
+* to use with the second of the two Plots that span the axis to
+* which the 3D element identifier refers. Returned holding 0 if
+* the given 3D element identifier is not axis specific.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The zero-based index of the 3D axis to which the given element
+* identifier refers, or -1 if the element identifier is not axis
+* specific.
+
+*/
+
+/* Local Variables: */
+ int axis3d;
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* Get the zero-based index of the 3D axis to which the supplied element
+ refers. Use an index of -1 to indicate that the element does not
+ relate to a specific axis. Also get the corresponding element to use
+ with the two Plots that share the speified 3D axis. */
+
+/* Define a macro used to set the 2d element identifiers for a given 3d
+ element identifier. */
+
+#define SET_ELEM2D(id1,id2) \
+ *elem2d1 = this->axis_index1[ axis3d ] ? id2 : id1; \
+ *elem2d2 = this->axis_index2[ axis3d ] ? id2 : id1;
+
+ if( element == AST__BORDER_ID ){
+ axis3d = -1;
+
+ } else if( element == AST__CURVE_ID ){
+ axis3d = -1;
+
+ } else if( element == AST__TITLE_ID ){
+ axis3d = -1;
+
+ } else if( element == AST__MARKS_ID ){
+ axis3d = -1;
+
+ } else if( element == AST__TEXT_ID ){
+ axis3d = -1;
+
+ } else if( element == AST__AXIS1_ID ){
+ axis3d = 0;
+ SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID)
+
+ } else if( element == AST__AXIS2_ID ){
+ axis3d = 1;
+ SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID)
+
+ } else if( element == AST__AXIS3_ID ){
+ axis3d = 2;
+ SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID)
+
+ } else if( element == AST__NUMLAB1_ID ){
+ axis3d = 0;
+ SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID)
+
+ } else if( element == AST__NUMLAB2_ID ){
+ axis3d = 1;
+ SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID)
+
+ } else if( element == AST__NUMLAB3_ID ){
+ axis3d = 2;
+ SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID)
+
+ } else if( element == AST__TEXTLAB1_ID ){
+ axis3d = 0;
+ SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID)
+
+ } else if( element == AST__TEXTLAB2_ID ){
+ axis3d = 1;
+ SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID)
+
+ } else if( element == AST__TEXTLAB3_ID ){
+ axis3d = 2;
+ SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID)
+
+ } else if( element == AST__TICKS1_ID ){
+ axis3d = 0;
+ SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID)
+
+ } else if( element == AST__TICKS2_ID ){
+ axis3d = 1;
+ SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID)
+
+ } else if( element == AST__TICKS3_ID ){
+ axis3d = 2;
+ SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID)
+
+ } else if( element == AST__GRIDLINE1_ID ){
+ axis3d = 0;
+ SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID)
+
+ } else if( element == AST__GRIDLINE2_ID ){
+ axis3d = 1;
+ SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID)
+
+ } else if( element == AST__GRIDLINE3_ID ){
+ axis3d = 2;
+ SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID)
+
+ } else {
+ axis3d = 0;
+ astError( AST__INTER, "Element2D(Plot3D): The MAKE_CLEAR2 macro "
+ "does not yet support element index %d (internal "
+ "AST programming error).", status, element );
+ }
+
+#undef SET_ELEM2D
+
+ return axis3d;
+
+}
+
+static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
+/*
+* Name:
+* Equal
+
+* Purpose:
+* Test if two Plot3Ds are equivalent.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Equal( AstObject *this, AstObject *that, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the astEqual protected
+* method inherited from the Plot Object class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* two Plot3Ds are equivalent.
+
+* Parameters:
+* this
+* Pointer to the first Plot3D.
+* that
+* Pointer to the second Plot3D.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if the Plot3Ds are equivalent, zero otherwise.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstPlot3D *that; /* Pointer to the second Plot3D structure */
+ AstPlot3D *this; /* Pointer to the first Plot3D structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Invoke the Equal method inherited from the parent Plot class. This checks
+ that the Plots are both of the same class (amongst other things). */
+ if( (*parent_equal)( this_object, that_object, status ) ) {
+
+/* Obtain pointers to the two Plot3D structures. */
+ this = (AstPlot3D *) this_object;
+ that = (AstPlot3D *) that_object;
+
+/* Check the encapsulated Plots for equality. */
+ result = ( astEqual( this->plotxz, that->plotxz ) &&
+ astEqual( this->plotyz, that->plotyz ) &&
+ astEqual( this->plotxy, that->plotxy ) );
+ }
+
+/* If an error occurred, clear the result value. */
+ if ( !astOK ) result = 0;
+
+/* Return the result, */
+ return result;
+}
+
+static AstPointSet *ExtendTicks( AstPlot *plot, AstPointSet *ticks, int *status ){
+/*
+* Name:
+* ExtendTicks
+
+* Purpose:
+* Add an extra tick to the start and end of a list of tick marks.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstPointSet *ExtendTicks( AstPlot *plot, AstPointSet *ticks, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function takes a list of tick marks drawn using the supplied
+* Plot, and adds in an extra tick mark at the start and end of the
+* supplied list of ticks, returning the expanded list in a new
+* PointSet. The extra points are guaranteed to fall outside the area
+* enclosed within the supplied Plot.
+
+* Parameters:
+* plot
+* The Plot that was used to generate the list of tick marks.
+* ticks
+* A PointSet holding the 2D graphics coordinates (within the base
+* Frame of the supplied Plot) at which each tick mark starts.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a new PointSet that has 2 more entries than the
+* supplied PointSet. The first point is a new tick mark, then comes
+* all the ticks mark positions supplied in "ticks", and finally there
+* is another new tick mark.
+
+*/
+
+/* Local Variables: */
+ AstPointSet *result;
+ double **ptr_in;
+ double **ptr_out;
+ double *a_in;
+ double *a_out;
+ double *b_in;
+ double *b_out;
+ double *p;
+ double delta;
+ double hi;
+ double lo;
+ double range[ 2 ];
+ double v;
+ int axis;
+ int i;
+ int increasing;
+ int np;
+
+/* Check inherited status */
+ if( !astOK || !ticks ) return NULL;
+
+/* Get the number of tick marsk in the supplied PointSet and get pointers
+ to the 3D Graphics values contained in the PointSet. */
+ np = astGetNpoint( ticks );
+ ptr_in = astGetPoints( ticks );
+
+/* Create the returned PointSet with room for an extra pair of ticks. Get
+ pointers to its data arrays */
+ result = astPointSet( np + 2, 2, "", status );
+ ptr_out = astGetPoints( result );
+
+/* Check the pointers can be used safely. */
+ if( astOK ) {
+
+/* Find the index of the 2D graphics axis (0 or 1) that varies along the
+ set of tick marks. We do this by finding the max and min value
+ supplied for each axis, and then choosing the axis that has the highest
+ range. */
+ for( axis = 0; axis < 2; axis++ ) {
+ hi = -DBL_MAX;
+ lo = DBL_MAX;
+ p = ptr_in[ axis ];
+
+ for( i = 0; i < np; i++, p++ ) {
+ v = *p;
+ if( v != AST__BAD ) {
+ if( v > hi ) hi = v;
+ if( v < lo ) lo = v;
+ }
+ }
+
+ if( lo != DBL_MAX ) {
+ range[ axis ] = hi - lo;
+ } else {
+ astError( AST__INTER, "ExtendTicks{Plot3D): no good ticks on "
+ "axis %d (internal AST prgramming error).", status, axis );
+ }
+ }
+
+
+/* Get the index of the axis with the largest range (the other range
+ should be zero). */
+ axis = ( range[ 0 ] > range[ 1 ] ) ? 0 : 1;
+
+/* Copy the input graphics positions to the output PointSet, and add an
+ extra position at the beginning and end of the output PointSet. */
+ if( axis == 0 ) {
+ lo = plot->xlo;
+ hi = plot->xhi;
+ a_in = ptr_in[ 0 ];
+ b_in = ptr_in[ 1 ];
+ a_out = ptr_out[ 0 ];
+ b_out = ptr_out[ 1 ];
+
+ } else {
+ lo = plot->ylo;
+ hi = plot->yhi;
+ a_in = ptr_in[ 1 ];
+ b_in = ptr_in[ 0 ];
+ a_out = ptr_out[ 1 ];
+ b_out = ptr_out[ 0 ];
+ }
+
+ delta = 0.2*( hi - lo );
+
+ if( a_in[ 0 ] < a_in[ 1 ] ) {
+ increasing = 1;
+ *(a_out++) = lo - delta;
+ } else {
+ increasing = 0;
+ *(a_out++) = hi + delta;
+ }
+
+ *(b_out++) = *b_in;
+ for( i = 0; i < np; i++ ) {
+ *(a_out++) = *(a_in++);
+ *(b_out++) = *(b_in++);
+ }
+ *(b_out++) = b_in[ -1 ];
+
+
+ if( increasing ) {
+ *(a_out++) = hi + delta;
+ } else {
+ *(a_out++) = lo - delta;
+ }
+ }
+
+/* Return the extended PointSet. */
+ return result;
+}
+
+static AstFrameSet *Fset3D( AstFrameSet *fset, int ifrm, int *status ) {
+/*
+* Name:
+* Fset3D
+
+* Purpose:
+* Create a FrameSet with no more than 3 dimensions for a given Frame.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstFrameSet *Fset3D( AstFrameSet *fset, int ifrm, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function checks a specified Frame in the supplied FrameSet.
+* If the Frame has more than 3 dimensions, a new Frame is added to
+* the FrameSet containing just the first three axes of the specified
+* Frame. A PermMap is used to connect this Frame to the specified
+* Frame, which supplied bad values for any missing axes. If the
+* specified Frame is the base Frame in the supplied FrameSet, then the
+* new Frame becomes the base Frame in the returned FrameSet. Like-wise,
+* if the specified Frame is the current Frame, then the new Frame
+* will be the current Frame in the returned FrameSet.
+*
+* If the specified Frame does not have more than 3 axes, then a clone
+* of the FrameSet pointer is returned, otherwise the returned pointer
+* points to a copy of the supplied FrameSet with the new 3-D Frame
+* added.
+
+* Parameters:
+* fset
+* Pointer to the FrameSet.
+* ifrm
+* The index of the Frame to check. This should be AST__BASE or
+* AST_CURRENT.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a FrameSet in which the Frame with index given by ifrm
+* has no more than 3 axes.
+*/
+
+/* Local Variables: */
+ AstFrame *frm;
+ AstFrame *newfrm;
+ AstFrameSet *ret;
+ AstPermMap *map;
+ double zero;
+ int *inperm;
+ int axes[3];
+ int i;
+ int ic;
+ int nax;
+
+/* Check the inherited status. */
+ if( !astOK ) return NULL;
+
+/* Initialise variables to avoid "used of uninitialised variable"
+ messages from dumb compilers. */
+ map = NULL;
+
+/* Get a pointer to the requested Frame in the supplied FrameSet. */
+ frm = astGetFrame( fset, ifrm );
+
+/* See how many dimensions the specified Frame of the supplied FrameSet
+ has. */
+ nax = astGetNaxes( frm );
+
+/* If it is more than 3-dimensionbal, create a 3D Frame by picking
+ axes 1, 2 and 3 from the original Frame. */
+ if( nax > 3 ) {
+ axes[ 0 ] = 0;
+ axes[ 1 ] = 1;
+ axes[ 2 ] = 2;
+ newfrm = astPickAxes( frm, 3, axes, NULL );
+
+/* Create a PermMap to describe the mapping between the two Frames.
+ Use zero as the value for unknown axes (the optional mapping which
+ can be returned by astPickAxes uses AST__BAD for unknown axes). */
+ inperm = (int *) astMalloc( sizeof(int)*(size_t) nax );
+ if( astOK ){
+ inperm[ 0 ] = 0;
+ inperm[ 1 ] = 1;
+ inperm[ 2 ] = 2;
+ for( i = 3; i < nax; i++ ) inperm[ i ] = -1;
+ zero = 0.0;
+ map = astPermMap( nax, inperm, 3, axes, &zero, "", status );
+ inperm = (int *) astFree( (void *) inperm );
+ }
+
+/* Get a copy of the supplied FrameSet. */
+ ret = astCopy( fset );
+
+/* Add the new Frame to the FrameSet (it becomes the current Frame). */
+ ic = astGetCurrent( ret );
+ astAddFrame( ret, ifrm, map, newfrm );
+ newfrm = astAnnul( newfrm );
+
+/* If the new Frame was derived from the base frame, set the new base
+ Frame, and re-instate the original current Frame */
+ if( ifrm == AST__BASE ){
+ astSetBase( ret, astGetCurrent( ret ) );
+ astSetCurrent( ret, ic );
+ }
+
+/* If the specified Frame in the supplied FrameSet is 3-dimensional, just
+ return a clone of it. */
+ } else {
+ ret = astClone( fset );
+ }
+
+/* Annul the pointer to the original Frame. */
+ frm = astAnnul( frm );
+
+ return ret;
+
+}
+
+static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* GetAttrib
+
+* Purpose:
+* Get the value of a specified attribute for a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the protected astGetAttrib
+* method inherited from the FrameSet class).
+
+* Description:
+* This function returns a pointer to the value of a specified
+* attribute for a Plot3D, formatted as a character string.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* attrib
+* Pointer to a null terminated string containing the name of
+* the attribute whose value is required. This name should be in
+* lower case, with all white space removed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - Pointer to a null terminated string containing the attribute
+* value.
+
+* Notes:
+* - The returned string pointer may point at memory allocated
+* within the Plot3D, or at static memory. The contents of the
+* string may be over-written or the pointer may become invalid
+* following a further invocation of the same function or any
+* modification of the Plot3D. A copy of the string should
+* therefore be made if necessary.
+* - A NULL pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*/
+
+/* Local Variables: */
+ AstPlot *plot; /* Pointer to specific Plot */
+ AstPlot3D *this; /* Pointer to the Plot3D structure */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ char attname[50]; /* Plot attribute base name */
+ char patt[50]; /* Plot attribute full name */
+ char spec[10]; /* Plane specification */
+ const char *result; /* Pointer value to return */
+ double dval; /* Floating point attribute value */
+ int axis; /* Axis index */
+ int ival; /* Int attribute value */
+ int len; /* Length of attrib string */
+ int nc; /* Number of character read */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(this_object);
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Obtain the length of the attrib string. */
+ len = strlen( attrib );
+
+/* Compare "attrib" with each recognised attribute name in turn,
+ obtaining the value of the required attribute. If necessary, write
+ the value into "getattrib_buff" as a null terminated string in an appropriate
+ format. Set "result" to point at the result string. */
+
+/* Norm(axis). */
+/* ----------- */
+ if ( nc = 0,
+ ( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) )
+ && ( nc >= len ) ) {
+ dval = astGetNorm( this, axis - 1 );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
+ result = getattrib_buff;
+ }
+
+/* RootCorner. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "rootcorner" ) ) {
+ ival = astGetRootCorner( this );
+ result = RootCornerString( ival, status );
+ if( !result && astOK ) {
+ astError( AST__INTER, "astGetAttrib(Plot3D): Illegal value %d "
+ "for RootCorner attribute (internal AST programming "
+ "error).", status, ival );
+ }
+
+/* ..._XY etc */
+/* ---------- */
+ } else if ( nc = 0,
+ ( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec,
+ &nc ) ) ) {
+
+ if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
+ plot = this->plotxy;
+ } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
+ plot = this->plotyz;
+ } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
+ plot = this->plotxz;
+ } else {
+ plot = NULL;
+ }
+
+ if( plot ) {
+ sprintf( patt, "%s%s", attname, attrib + nc );
+ result = astGetAttrib( plot, patt );
+
+ } else {
+ result = (*parent_getattrib)( this_object, attrib, status );
+ }
+
+/* If the attribute name was not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_getattrib)( this_object, attrib, status );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int GetObjSize( AstObject *this_object, int *status ) {
+/*
+* Name:
+* GetObjSize
+
+* Purpose:
+* Return the in-memory size of an Object.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int GetObjSize( AstObject *this, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the astGetObjSize protected
+* method inherited from the parent class).
+
+* Description:
+* This function returns the in-memory size of the supplied Plot3D,
+* in bytes.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The Object size, in bytes.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstPlot3D *this; /* Pointer to Plot3D structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointers to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Invoke the GetObjSize method inherited from the parent class, and then
+ add on any components of the class structure defined by this class
+ which are stored in dynamically allocated memory. */
+ result = (*parent_getobjsize)( this_object, status );
+
+ result += astGetObjSize( this->plotxy );
+ result += astGetObjSize( this->plotxz );
+ result += astGetObjSize( this->plotyz );
+
+/* If an error occurred, clear the result value. */
+ if ( !astOK ) result = 0;
+
+/* Return the result, */
+ return result;
+}
+
+static void Grid( AstPlot *this_plot, int *status ) {
+/*
+* Name:
+* Grid
+
+* Purpose:
+* Draw a set of labelled coordinate axes.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void Grid( AstPlot *this, int *status )
+
+* Class Membership:
+* Plot member function (over-rides the astGrid method inherited from
+* the Plot class).
+
+* Description:
+* This function draws a complete annotated set of 3-dimensional
+* coordinate axes for a Plot3D with (optionally) a coordinate grid
+* superimposed.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstPlot *baseplot;
+ AstPlot *plot;
+ AstPlot3D *this;
+ AstPointSet *majticks;
+ AstPointSet *minticks;
+ AstPointSet *tmp;
+ AstPointSet *wcsmajticks;
+ AstPointSet *wcsminticks;
+ const char *edge;
+ double **ptrmin;
+ double **ptrmaj;
+ double gcon;
+ int base_wax2d;
+ int itick;
+ int new_gaxis;
+ int new_plot;
+ int new_wax2d;
+ int nmaj;
+ int nmin;
+ int perm[ 2 ];
+ int rootcorner;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_plot;
+
+/* Invoke the astGrid method on the 2D base Plot. Both WCS axes in this
+ Plot are inherited from the 3D FrameSet that was supplied when the Plot3D
+ was constructed, and will be labelled. The other two Plots encapsulated
+ in the Plot3D only inherit a single axis from the original 3D FrameSet
+ (a dummy axis is used for the second WCS axis in these Plots). */
+ baseplot = GET_PLOT( this->baseplot );
+ astGrid( baseplot );
+
+/* We next use astGrid to draw a grid on the 2D plane that shares the first
+ base plot graphics axis. Get the identifier for this Plot and the 2D
+ graphics axis index within the Plot that corresponds to the first base
+ plot graphics axis. Also get the constant value on the 3rd graphics
+ axis over the base plot */
+ rootcorner = astGetRootCorner( this );
+ if( this->baseplot == XY ) {
+ new_plot = XZ;
+ new_gaxis = 0;
+ gcon = this->gbox[ ( rootcorner & 4 ) ? 5 : 2 ];
+
+ } else if( this->baseplot == XZ ) {
+ new_plot = XY;
+ new_gaxis = 0;
+ gcon = this->gbox[ ( rootcorner & 2 ) ? 4 : 1 ];
+
+ } else {
+ new_plot = XY;
+ new_gaxis = 1;
+ gcon = this->gbox[ ( rootcorner & 1 ) ? 3 : 0 ];
+ }
+
+/* Get a pointer to the Plot upon which astGrid is about to be invoked. */
+ plot = GET_PLOT( new_plot );
+
+/* Find which 2D WCS axis was labelled on graphics axis 0 (the bottom or
+ top edge) of the base plot. */
+ if( ( edge = astGetC( baseplot, "Edge(1)" ) ) ) {
+ base_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1;
+ } else {
+ base_wax2d = 0;
+ }
+
+/* Get the 2D graphics coords within the base Plot at which the tick
+ marks were drawn for this 2D WCS axis. Extend the PointSets holding
+ the major tick values to include an extra tick above and below the
+ ticks drawn by astGrid. These extra ticks are placed outside the
+ bounds of the plot. This ensures that the curves on the other axis
+ extend the full width of the plot. */
+ tmp = astGetDrawnTicks( baseplot, base_wax2d, 1 );
+ if( tmp ) {
+ majticks = ExtendTicks( baseplot, tmp, status );
+ nmaj = astGetNpoint( majticks );
+ ptrmaj = astGetPoints( majticks );
+ tmp = astAnnul( tmp );
+ } else {
+ majticks = NULL;
+ nmaj = 0;
+ ptrmaj = NULL;
+ }
+
+ minticks = astGetDrawnTicks( baseplot, base_wax2d, 0 );
+ if( minticks ) {
+ nmin = astGetNpoint( minticks );
+ ptrmin = astGetPoints( minticks );
+ } else {
+ nmin = 0;
+ ptrmin = NULL;
+ }
+
+/* All the tick marks will have a constant value for the 2D graphics axis
+ that is not being ticked (axis 1 at the moment). Change this constant
+ value to be the value appropriate to the new Plot. */
+ if( ptrmaj && ptrmin ) {
+ for( itick = 0; itick < nmaj; itick++ ) ptrmaj[ 1 ][ itick ] = gcon;
+ for( itick = 0; itick < nmin; itick++ ) ptrmin[ 1 ][ itick ] = gcon;
+ }
+
+/* If required, permute the axes in the tick mark positions. */
+ if( new_gaxis != 0 ) {
+ perm[ 0 ] = 1;
+ perm[ 1 ] = 0;
+ if( majticks ) astPermPoints( majticks, 1, perm );
+ if( minticks ) astPermPoints( minticks, 1, perm );
+ }
+
+/* Transform the tick mark positions from 2D graphics coords to 2D WCS
+ coords. */
+ wcsmajticks = majticks ? astTransform( plot, majticks, 1, NULL ) : NULL;
+ wcsminticks = minticks ? astTransform( plot, minticks, 1, NULL ) : NULL;
+
+/* Find the index of the 2D WCS axis that will be labelled on the bottom
+ or top edge (i.e. 2D graphics axis zero) of the new Plot. */
+ if( ( edge = astGetC( plot, "Edge(1)" ) ) ) {
+ new_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1;
+ } else {
+ new_wax2d = 0;
+ }
+
+/* Use the other WCS axis if we are ticking the left or right edge (i.e.
+ 2D graphics axis one) of the new Plot. This gives us the index of the
+ 2D WCS axis for which tick values are to be stored in the Plot. */
+ if( new_gaxis == 1 ) new_wax2d = 1 - new_wax2d;
+
+/* Store the tick mark values to be used on this WCS axis. */
+ ptrmaj = wcsmajticks ? astGetPoints( wcsmajticks ) : NULL;
+ ptrmin = wcsminticks ? astGetPoints( wcsminticks ) : NULL;
+ if( ptrmaj && ptrmin ) {
+ astSetTickValues( plot, new_wax2d, nmaj, ptrmaj[ new_gaxis ],
+ nmin, ptrmin[ new_gaxis ] );
+ }
+
+/* Invoke the astGrid method on this plot. */
+ astGrid( plot );
+
+/* Clear the stored tick values in the plot. */
+ astSetTickValues( plot, new_wax2d, 0, NULL, 0, NULL );
+
+/* Free resources */
+ if( wcsmajticks ) wcsmajticks = astAnnul( wcsmajticks );
+ if( wcsminticks ) wcsminticks = astAnnul( wcsminticks );
+ if( majticks ) majticks = astAnnul( majticks );
+ if( minticks ) minticks = astAnnul( minticks );
+
+/* We next use astGrid to draw a grid on the 2D plane that shares the
+ second base plot graphics axis. Get the identifier for this Plot and the
+ 2D graphics axis index within the Plot that corresponds to the first
+ base plot graphics axis. */
+ if( this->baseplot == XY ) {
+ new_plot = YZ;
+ new_gaxis = 0;
+
+ } else if( this->baseplot == XZ ) {
+ new_plot = YZ;
+ new_gaxis = 1;
+
+ } else {
+ new_plot = XZ;
+ new_gaxis = 1;
+ }
+
+/* Get a pointer to the Plot upon which astGrid is about to be invoked. */
+ plot = GET_PLOT( new_plot );
+
+/* Find which 2D WCS axis was labelled on graphics axis 1 (the left or
+ right edge) of the base plot. */
+ base_wax2d = 1 - base_wax2d;
+
+/* Get the 2D graphics coords within the base Plot at which the tick
+ marks were drawn for this 2D WCS axis. Extend the PointSets holding
+ the major tick values to include an extra tick above and below the
+ ticks drawn by astGrid. These extra ticks are placed outside the
+ bounds of the plot. This ensures that the curves on the other axis
+ extend the full width of the plot. */
+ tmp = astGetDrawnTicks( baseplot, base_wax2d, 1 );
+ if( tmp ) {
+ majticks = ExtendTicks( baseplot, tmp, status );
+ nmaj = astGetNpoint( majticks );
+ ptrmaj = astGetPoints( majticks );
+ tmp = astAnnul( tmp );
+ } else {
+ majticks = NULL;
+ nmaj = 0;
+ ptrmaj = NULL;
+ }
+
+ minticks = astGetDrawnTicks( baseplot, base_wax2d, 0 );
+ if( minticks ) {
+ nmin = astGetNpoint( minticks );
+ ptrmin = astGetPoints( minticks );
+ } else {
+ nmin = 0;
+ ptrmin = NULL;
+ }
+
+/* All the tick marks will have a constant value for the 2D graphics axis
+ that is not being ticked (axis 0 at the moment). Change this constant
+ value to be the value appropriate to the new Plot. */
+ if( ptrmaj && ptrmin ) {
+ for( itick = 0; itick < nmaj; itick++ ) ptrmaj[ 0 ][ itick ] = gcon;
+ for( itick = 0; itick < nmin; itick++ ) ptrmin[ 0 ][ itick ] = gcon;
+ }
+
+/* If required, permute the axes in the tick mark positions. */
+ if( new_gaxis != 1 ) {
+ perm[ 0 ] = 1;
+ perm[ 1 ] = 0;
+ if( majticks ) astPermPoints( majticks, 1, perm );
+ if( minticks ) astPermPoints( minticks, 1, perm );
+ }
+
+/* Transform the tick mark positions from 2D graphics coords to 2D WCS
+ coords. */
+ wcsmajticks = majticks ? astTransform( plot, majticks, 1, NULL ) : NULL;
+ wcsminticks = minticks ? astTransform( plot, minticks, 1, NULL ) : NULL;
+
+/* Find the index of the 2D WCS axis that will be labelled on the bottom
+ or top edge (i.e. 2D graphics axis zero) of the new Plot. */
+ if( ( edge = astGetC( plot, "Edge(1)" ) ) ) {
+ new_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1;
+ } else {
+ new_wax2d = 0;
+ }
+
+/* Use the other WCS axis if we are ticking the left or right edge (i.e.
+ 2D graphics axis one) of the new Plot. This gives us the index of the
+ 2D WCS axis for which tick values are to be stored in the Plot. */
+ if( new_gaxis == 1 ) new_wax2d = 1 - new_wax2d;
+
+/* Store the tick mark values to be used on this WCS axis. */
+ ptrmaj = wcsmajticks ? astGetPoints( wcsmajticks ) : NULL;
+ ptrmin = wcsminticks ? astGetPoints( wcsminticks ) : NULL;
+ if( ptrmaj && ptrmin ) {
+ astSetTickValues( plot, new_wax2d, nmaj, ptrmaj[ new_gaxis ],
+ nmin, ptrmin[ new_gaxis ] );
+ }
+
+/* Invoke the astGrid method on this plot. */
+ astGrid( plot );
+
+/* Clear the stored tick values in the plot. */
+ astSetTickValues( plot, new_wax2d, 0, NULL, 0, NULL );
+
+/* Free resources */
+ if( wcsmajticks ) wcsmajticks = astAnnul( wcsmajticks );
+ if( wcsminticks ) wcsminticks = astAnnul( wcsminticks );
+ if( majticks ) majticks = astAnnul( majticks );
+ if( minticks ) minticks = astAnnul( minticks );
+
+}
+
+void astInitPlot3DVtab_( AstPlot3DVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitPlot3DVtab
+
+* Purpose:
+* Initialise a virtual function table for a Plot3D.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void astInitPlot3DVtab( AstPlot3DVtab *vtab, const char *name )
+
+* Class Membership:
+* Plot3D vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the Plot3D class.
+
+* Parameters:
+* vtab
+* Pointer to the virtual function table. The components used by
+* all ancestral classes will be initialised if they have not already
+* been initialised.
+* name
+* Pointer to a constant null-terminated character string which contains
+* the name of the class to which the virtual function table belongs (it
+* is this pointer value that will subsequently be returned by the Object
+* astClass function).
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstFrame *dummy_frame; /* The Frame to put in dummy_frameset */
+ AstPlotVtab *plot; /* Pointer to Plot component of Vtab */
+ AstFrameSetVtab *fset; /* Pointer to FrameSet component of Vtab */
+ AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
+ AstObjectVtab *object; /* Pointer to Object component of Vtab */
+
+/* Check the local error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialize the component of the virtual function table used by the
+ parent class. */
+ astInitPlotVtab( (AstPlotVtab *) vtab, name );
+
+/* Store a unique "magic" value in the virtual function table. This
+ will be used (by astIsAPlot3D) to determine if an object belongs
+ to this class. We can conveniently use the address of the (static)
+ class_check variable to generate this unique value. */
+ vtab->id.check = &class_check;
+ vtab->id.parent = &(((AstPlotVtab *) vtab)->id);
+
+/* Initialise member function pointers. */
+/* ------------------------------------ */
+/* Store pointers to the member functions (implemented here) that
+ provide virtual methods for this class. */
+
+#define SET_PLOT3D_ACCESSORS(attr) \
+ vtab->Set##attr = Set##attr; \
+ vtab->Get##attr = Get##attr; \
+ vtab->Test##attr = Test##attr; \
+ vtab->Clear##attr = Clear##attr;
+
+SET_PLOT3D_ACCESSORS(RootCorner)
+SET_PLOT3D_ACCESSORS(Norm)
+
+#undef SET_PLOT3D_ACCESSORS
+
+
+
+
+/* Save the inherited pointers to methods that will be extended, and
+ replace them with pointers to the new member functions. */
+ object = (AstObjectVtab *) vtab;
+ fset = (AstFrameSetVtab *) vtab;
+ mapping = (AstMappingVtab *) vtab;
+ plot = (AstPlotVtab *) vtab;
+
+ parent_getobjsize = object->GetObjSize;
+ object->GetObjSize = GetObjSize;
+
+ parent_equal = object->Equal;
+ object->Equal = Equal;
+
+ parent_vset = object->VSet;
+ object->VSet = VSet;
+
+ parent_cast = object->Cast;
+ object->Cast = Cast;
+
+ parent_clear = object->Clear;
+ object->Clear = Clear;
+
+ parent_clearattrib = object->ClearAttrib;
+ object->ClearAttrib = ClearAttrib;
+
+ parent_getattrib = object->GetAttrib;
+ object->GetAttrib = GetAttrib;
+
+ parent_setattrib = object->SetAttrib;
+ object->SetAttrib = SetAttrib;
+
+ parent_testattrib = object->TestAttrib;
+ object->TestAttrib = TestAttrib;
+
+ parent_clearcurrent = fset->ClearCurrent;
+ fset->ClearCurrent = ClearCurrent;
+
+ parent_setcurrent = fset->SetCurrent;
+ fset->SetCurrent = SetCurrent;
+
+ parent_removeframe = fset->RemoveFrame;
+ fset->RemoveFrame = RemoveFrame;
+
+#if defined(THREAD_SAFE)
+ parent_managelock = object->ManageLock;
+ object->ManageLock = ManageLock;
+#endif
+
+/* Define a macro to override attribute accessors inherited from the
+ parent Plot class. First do axis specific attributes. */
+
+#define SET_PLOT_ACCESSORS(attr) \
+ parent_get##attr = plot->Get##attr; \
+ plot->Get##attr = Get##attr; \
+ parent_set##attr = plot->Set##attr; \
+ plot->Set##attr = Set##attr; \
+ parent_clear##attr = plot->Clear##attr; \
+ plot->Clear##attr = Clear##attr;
+
+/* Use the above macro to override all the inherited attribute accessors. */
+SET_PLOT_ACCESSORS(MinTick)
+SET_PLOT_ACCESSORS(Abbrev)
+SET_PLOT_ACCESSORS(Gap)
+SET_PLOT_ACCESSORS(LogGap)
+SET_PLOT_ACCESSORS(LogPlot)
+SET_PLOT_ACCESSORS(LogTicks)
+SET_PLOT_ACCESSORS(LogLabel)
+SET_PLOT_ACCESSORS(LabelUp)
+SET_PLOT_ACCESSORS(DrawAxes)
+SET_PLOT_ACCESSORS(LabelUnits)
+SET_PLOT_ACCESSORS(MinTickLen)
+SET_PLOT_ACCESSORS(MajTickLen)
+SET_PLOT_ACCESSORS(NumLab)
+SET_PLOT_ACCESSORS(NumLabGap)
+SET_PLOT_ACCESSORS(TextLab)
+SET_PLOT_ACCESSORS(TextLabGap)
+
+#undef SET_PLOT_ACCESSORS
+
+
+/* Now do attributes that are not axis specific. */
+
+#define SET_PLOT_ACCESSORS(attr) \
+ parent_set##attr = plot->Set##attr; \
+ plot->Set##attr = Set##attr; \
+ parent_clear##attr = plot->Clear##attr; \
+ plot->Clear##attr = Clear##attr;
+
+SET_PLOT_ACCESSORS(Ink)
+SET_PLOT_ACCESSORS(Tol)
+SET_PLOT_ACCESSORS(Invisible)
+SET_PLOT_ACCESSORS(TickAll)
+SET_PLOT_ACCESSORS(ForceExterior)
+SET_PLOT_ACCESSORS(Border)
+SET_PLOT_ACCESSORS(Clip)
+SET_PLOT_ACCESSORS(ClipOp)
+SET_PLOT_ACCESSORS(Escape)
+SET_PLOT_ACCESSORS(Grid)
+SET_PLOT_ACCESSORS(Labelling)
+SET_PLOT_ACCESSORS(Style)
+SET_PLOT_ACCESSORS(Font)
+SET_PLOT_ACCESSORS(Colour)
+SET_PLOT_ACCESSORS(Width)
+SET_PLOT_ACCESSORS(Size)
+
+#undef SET_PLOT_ACCESSORS
+
+
+/* Store replacement pointers for methods which will be over-ridden by new
+ member functions implemented here. */
+ plot->Grid = Grid;
+ plot->Text = Text;
+ plot->SetTickValues = SetTickValues;
+ plot->PolyCurve = PolyCurve;
+ plot->Border = Border;
+ plot->BoundingBox = BoundingBox;
+ plot->GetGrfContext = GetGrfContext;
+ plot->GrfPop = GrfPop;
+ plot->GrfPush = GrfPush;
+ plot->GrfSet = GrfSet;
+ plot->GridLine = GridLine;
+ plot->Mark = Mark;
+ plot->Curve = Curve;
+ plot->GenCurve = GenCurve;
+ plot->Clip = Clip;
+ mapping->Transform = Transform;
+
+/* Declare the copy constructor, destructor and class dump
+ function. */
+ astSetCopy( vtab, Copy );
+ astSetDelete( vtab, Delete );
+ astSetDump( vtab, Dump, "Plot3D", "Provide facilities for 3D graphical output" );
+
+/* Create a FrameSet that can be used when calling astCast to indicate
+ the class to which we want to cast. */
+ LOCK_MUTEX3
+ if( !dummy_frameset ) {
+ dummy_frame = astFrame( 1, " ", status );
+ dummy_frameset = astFrameSet( dummy_frame, " ", status );
+ dummy_frame = astAnnul( dummy_frame );
+ }
+ UNLOCK_MUTEX3
+
+/* If we have just initialised the vtab for the current class, indicate
+ that the vtab is now initialised, and store a pointer to the class
+ identifier in the base "object" level of the vtab. */
+ if( vtab == &class_vtab ) {
+ class_init = 1;
+ astSetVtabClassIdentifier( vtab, &(vtab->id) );
+ }
+}
+
+#if defined(THREAD_SAFE)
+static int ManageLock( AstObject *this_object, int mode, int extra,
+ AstObject **fail, int *status ) {
+/*
+* Name:
+* ManageLock
+
+* Purpose:
+* Manage the thread lock on an Object.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "object.h"
+* AstObject *ManageLock( AstObject *this, int mode, int extra,
+* AstObject **fail, int *status )
+
+* Class Membership:
+* CmpMap member function (over-rides the astManageLock protected
+* method inherited from the parent class).
+
+* Description:
+* This function manages the thread lock on the supplied Object. The
+* lock can be locked, unlocked or checked by this function as
+* deteremined by parameter "mode". See astLock for details of the way
+* these locks are used.
+
+* Parameters:
+* this
+* Pointer to the Object.
+* mode
+* An integer flag indicating what the function should do:
+*
+* AST__LOCK: Lock the Object for exclusive use by the calling
+* thread. The "extra" value indicates what should be done if the
+* Object is already locked (wait or report an error - see astLock).
+*
+* AST__UNLOCK: Unlock the Object for use by other threads.
+*
+* AST__CHECKLOCK: Check that the object is locked for use by the
+* calling thread (report an error if not).
+* extra
+* Extra mode-specific information.
+* fail
+* If a non-zero function value is returned, a pointer to the
+* Object that caused the failure is returned at "*fail". This may
+* be "this" or it may be an Object contained within "this". Note,
+* the Object's reference count is not incremented, and so the
+* returned pointer should not be annulled. A NULL pointer is
+* returned if this function returns a value of zero.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A local status value:
+* 0 - Success
+* 1 - Could not lock or unlock the object because it was already
+* locked by another thread.
+* 2 - Failed to lock a POSIX mutex
+* 3 - Failed to unlock a POSIX mutex
+* 4 - Bad "mode" value supplied.
+
+* Notes:
+* - This function attempts to execute even if an error has already
+* occurred.
+*/
+
+/* Local Variables: */
+ AstPlot3D *this; /* Pointer to Plot3D structure */
+ int result; /* Returned status value */
+
+/* Initialise */
+ result = 0;
+
+/* Check the supplied pointer is not NULL. */
+ if( !this_object ) return result;
+
+/* Obtain a pointers to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Invoke the ManageLock method inherited from the parent class. */
+ if( !result ) result = (*parent_managelock)( this_object, mode, extra,
+ fail, status );
+
+/* Invoke the astManageLock method on any Objects contained within
+ the supplied Object. */
+ if( !result ) result = astManageLock( this->plotxy, mode, extra, fail );
+ if( !result ) result = astManageLock( this->plotxz, mode, extra, fail );
+ if( !result ) result = astManageLock( this->plotyz, mode, extra, fail );
+
+ return result;
+
+}
+#endif
+
+static void Mark( AstPlot *this_plot, int nmark, int ncoord, int indim,
+ const double *in, int type, int *status ){
+/*
+* Name:
+* Mark
+
+* Purpose:
+* Draw a set of markers for a Plot3D.
+
+* Type:
+* Private member function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void Mark( AstPlot *this, int nmark, int ncoord, int indim,
+* const double *in, int type, int *status )
+
+* Class Membership:
+* Plot3d member function (overrides the astMark method inherited form
+* the parent Plot class).
+
+* Description:
+* This function draws a set of markers (symbols) at positions
+* specified in the physical coordinate system of a Plot3D. The
+* positions are transformed into graphical coordinates to
+* determine where the markers should appear within the plotting
+* area.
+*
+* They are drawn on a 2D plane that has a normal vector given by the
+* current value of the Plot3D's "Norm" attribute.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* nmark
+* The number of markers to draw. This may be zero, in which
+* case nothing will be drawn.
+* ncoord
+* The number of coordinates being supplied for each mark
+* (i.e. the number of axes in the current Frame of the Plot, as
+* given by its Naxes attribute).
+* indim
+* The number of elements along the second dimension of the "in"
+* array (which contains the marker coordinates). This value is
+* required so that the coordinate values can be correctly
+* located if they do not entirely fill this array. The value
+* given should not be less than "nmark".
+* in
+* The address of the first element of a 2-dimensional array of
+* shape "[ncoord][indim]" giving the
+* physical coordinates of the points where markers are to be
+* drawn. These should be stored such that the value of
+* coordinate number "coord" for input mark number "mark" is
+* found in element "in[coord][mark]".
+* type
+* A value specifying the type (e.g. shape) of marker to be
+* drawn. The set of values which may be used (and the shapes
+* that will result) is determined by the underlying graphics
+* system.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - Markers are not drawn at positions which have any coordinate
+* equal to the value AST__BAD (or where the transformation into
+* graphical coordinates yields coordinates containing the value
+* AST__BAD).
+* - An error results if the base Frame of the Plot is not 3-dimensional.
+* - An error also results if the transformation between the
+* current and base Frames of the Plot is not defined (i.e. the
+* Plot's TranInverse attribute is zero).
+*/
+
+/* Local Variables: */
+ AstMapping *mapping; /* Pointer to graphics->physical mapping */
+ AstPlot3D *this; /* Pointer to the Plot3D structure */
+ AstPointSet *pset1; /* PointSet holding physical positions */
+ AstPointSet *pset2; /* PointSet holding graphics positions */
+ const char *class; /* Object class */
+ const char *method; /* Current method */
+ const double **ptr1; /* Pointer to physical positions */
+ double **ptr2; /* Pointer to graphics positions */
+ double *xpd; /* Pointer to next double precision x value */
+ double *ypd; /* Pointer to next double precision y value */
+ double *zpd; /* Pointer to next double precision z value */
+ float *x; /* Pointer to single precision x values */
+ float *xpf; /* Pointer to next single precision x value */
+ float *y; /* Pointer to single precision y values */
+ float *ypf; /* Pointer to next single precision y value */
+ float *z; /* Pointer to single precision z values */
+ float *zpf; /* Pointer to next single precision z value */
+ float norm[ 3 ]; /* Single precision normal vector */
+ int axis; /* Axis index */
+ int i; /* Loop count */
+ int naxes; /* No. of axes in the base Frame */
+ int nn; /* Number of good marker positions */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_plot;
+
+/* Store the current method and class for inclusion in error messages
+ generated by lower level functions. */
+ method = "astMark";
+ class = astClass( this );
+
+/* Check the base Frame of the Plot is 3-D. */
+ naxes = astGetNin( this );
+ if( naxes != 3 && astOK ){
+ astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base "
+ "Frame of the supplied %s is invalid - this number should "
+ "be 3.", status, method, class, naxes, class );
+ }
+
+/* Also validate the input array dimension argument. */
+ if ( astOK && ( indim < nmark ) ) {
+ astError( AST__DIMIN, "%s(%s): The input array dimension value "
+ "(%d) is invalid.", status, method, class, indim );
+ astError( AST__DIMIN, "This should not be less than the number of "
+ "markers being drawn (%d).", status, nmark );
+ }
+
+/* Establish the correct graphical attributes as defined by attributes
+ with the supplied Plot. */
+ astGrfAttrs( this, AST__MARKS_ID, 1, GRF__MARK, method, class );
+
+/* Create a PointSet to hold the supplied physical coordinates. */
+ pset1 = astPointSet( nmark, ncoord, "", status );
+
+/* Allocate memory to hold pointers to the first value on each axis. */
+ ptr1 = (const double **) astMalloc( sizeof( const double * )*
+ (size_t)( ncoord ));
+
+/* Check the pointer can be used, then store pointers to the first value
+ on each axis. */
+ if( astOK ){
+ for( axis = 0; axis < ncoord; axis++ ){
+ ptr1[ axis ] = in + axis*indim;
+ }
+ }
+
+/* Store these pointers in the PointSet. */
+ astSetPoints( pset1, (double **) ptr1 );
+
+/* Transform the supplied data from the current frame (i.e. physical
+ coordinates) to the base frame (i.e. graphics coordinates) using
+ the inverse Mapping defined by the Plot. */
+ mapping = astGetMapping( this, AST__BASE, AST__CURRENT );
+ pset2 = astTransform( mapping, pset1, 0, NULL );
+ mapping = astAnnul( mapping );
+
+/* Get pointers to the graphics coordinates. */
+ ptr2 = astGetPoints( pset2 );
+
+/* Allocate memory to hold single precision versions of the graphics
+ coordinates. */
+ x = (float *) astMalloc( sizeof( float )*(size_t) nmark );
+ y = (float *) astMalloc( sizeof( float )*(size_t) nmark );
+ z = (float *) astMalloc( sizeof( float )*(size_t) nmark );
+
+/* Check the pointers can be used. */
+ if( astOK ){
+
+/* Store pointers to the next single and double precision x, y and z
+ values. */
+ xpf = x;
+ ypf = y;
+ zpf = z;
+ xpd = ptr2[ 0 ];
+ ypd = ptr2[ 1 ];
+ zpd = ptr2[ 2 ];
+
+/* Convert the double precision values to single precision, rejecting
+ any bad marker positions. */
+ nn = 0;
+ for( i = 0; i < nmark; i++ ){
+ if( *xpd != AST__BAD && *ypd != AST__BAD && *zpd != AST__BAD ){
+ nn++;
+ *(xpf++) = (float) *(xpd++);
+ *(ypf++) = (float) *(ypd++);
+ *(zpf++) = (float) *(zpd++);
+ } else {
+ xpd++;
+ ypd++;
+ zpd++;
+ }
+ }
+
+/* If the nornmal vector has non-zero length, draw the remaining markers. */
+ norm[ 0 ] = (float) astGetNorm( this, 0 );
+ norm[ 1 ] = (float) astGetNorm( this, 1 );
+ norm[ 2 ] = (float) astGetNorm( this, 2 );
+
+ if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 || norm[ 2 ] != 0.0 ){
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+ if( !astG3DMark( nn, x, y, z, type, norm ) ) {
+ astError( AST__GRFER, "%s(%s): Graphics error in astG3DMark. ", status,
+ method, class );
+ }
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+
+ } else if( astOK ) {
+ astError( AST__ATTIN, "%s(%s): The vector specified by the Norm "
+ "attribute has zero length.", status, method, class );
+ }
+ }
+
+/* Free the memory used to store single precision graphics coordinates. */
+ x = (float *) astFree( (void *) x );
+ y = (float *) astFree( (void *) y );
+ z = (float *) astFree( (void *) z );
+
+/* Annul the PointSets. */
+ pset1 = astAnnul( pset1 );
+ pset2 = astAnnul( pset2 );
+
+/* Free the memory holding the pointers to the first value on each axis. */
+ ptr1 = (const double **) astFree( (void *) ptr1 );
+
+/* Re-establish the original graphical attributes. */
+ astGrfAttrs( this, AST__MARKS_ID, 0, GRF__MARK, method, class );
+
+/* Return */
+ return;
+}
+
+static int Plot3DAttr( AstKeyMap *grfconID, int attr, double value,
+ double *old_value, int prim ){
+/*
+* Name:
+* Plot3DAttr
+
+* Purpose:
+* Get or set the value of a 3D grf attribute.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DAttr( AstKeyMap *grfconID, int attr, double value,
+* double *old_value, int prim )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* get or set the current value of a specified graphics attribute. It
+* forwards the call to the grf3D module being used by this Plot3D. It
+* should be registered with each of the 2D Plots using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is used to identify which of
+* the three Plots is calling this function.
+* attr
+* An integer value identifying the required attribute. This should
+* be one of the symbolic values defined in grf.h.
+* value
+* A new value to store for the attribute. If this is AST__BAD
+* no value is stored.
+* old_value
+* A pointer to a double in which to return the attribute value.
+* If this is NULL, no value is returned.
+* prim
+* The sort of graphics primitive to be drawn with the new attribute.
+* Identified by one of the values defined in grf.h.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ int result;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return 0;
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Use the function in the external Grf3D module, selected at link-time
+ using ast_link options. Note, attribute values are the same for each
+ of the three Plot. */
+ result = astG3DAttr( attr, value, old_value, prim );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+
+/* Return the result. */
+ return result;
+}
+
+static int Plot3DCap( AstKeyMap *grfconID, int cap, int value ){
+/*
+* Name:
+* Plot3DCap
+
+* Purpose:
+* Determine if the 3D grf module has a given capability.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DCap( AstKeyMap *grfconID, int cap, int value )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* determine if a given graphics capability is available. It forwards
+* the call to the grf3D module being used by this Plot3D. It should be
+* registered with each of the 2D Plots using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* cap
+* The capability being inquired about. This will be one of the
+* following constants defined in grf.h: GRF__SCALES, GRF__MJUST,
+* GRF__ESC,
+* value
+* The use of this parameter depends on the value of "cap" as
+* described in the documentation for the astGrfSet function in the
+* Plot class.
+
+* Returned Value:
+* The value returned by the function depends on the value of "cap"
+* as described in the astGrfSet documentation. Zero is returned if
+* the supplied capability is not recognised.
+
+*/
+
+/* Local Variables: */
+ int result;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return 0;
+
+/* The astGScales function is implemented by code within the Plot3D class
+ (not within the grf3D module). The Plot3D class assumes all axes are
+ equally scaled. */
+ if( cap == GRF__SCALES ) {
+ result = 1;
+
+/* All grf3D modules are assumed to support "M" justification. */
+ } else if( cap == GRF__MJUST ) {
+ result = 1;
+
+/* Forward all other capability requests to the grf3D module. */
+ } else {
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+ result = astG3DCap( cap, value );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int Plot3DFlush( AstKeyMap *grfconID ){
+/*
+* Name:
+* Plot3DFlush
+
+* Purpose:
+* Flush any buffered graphical output.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DFlush( AstKeyMap *grfconID )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* ensure that the display device is up-to-date by flushing any pending
+* graphics to the output device. It forwards the call to the grf3D module
+* being used by this Plot3D. It should be registered with each of the
+* 2D Plots using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ int result;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return 0;
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Use the function in the external Grf3D module, selected at link-time
+ using ast_link options. */
+ result = astG3DFlush();
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+
+/* Return the result. */
+ return result;
+}
+
+static int Plot3DLine( AstKeyMap *grfconID, int n, const float *x, const float *y ){
+/*
+* Name:
+* Plot3DLine
+
+* Purpose:
+* Draw a polyline on a 2D surface.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DLine( AstKeyMap *grfconID, int n, const float *x,
+* const float *y )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* draw a polyline. It forwards the call to the grf3D module being used
+* by this Plot3D. It should be registered with each of the 2D Plots
+* using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* n
+* The number of positions to be joined together.
+* x
+* A pointer to an array holding the "n" x values.
+* y
+* A pointer to an array holding the "n" y values.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *grfcon;
+ double gcon;
+ float *work;
+ float *x3d = NULL;
+ float *y3d = NULL;
+ float *z3d = NULL;
+ int i;
+ int plane;
+ int result = 0;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Get a genuine pointer from the supplied grfcon identifier. */
+ grfcon = astMakePointer( grfconID );
+
+/* Report an error if no grfcon object was supplied. */
+ if( !grfcon ) {
+ astError( AST__INTER, "astG3DLine(Plot3D): No grfcon Object supplied "
+ "(internal AST programming error)." , status);
+
+/* If a grfcon Object was supplied, get the graphics box array out of it. */
+ } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
+ astError( AST__INTER, "astG3DLine(Plot3D): No \"Gcon\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* Also get the plane index out of it. */
+ } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
+ astError( AST__INTER, "astG3DLine(Plot3D): No \"Plane\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+ }
+
+/* Allocate memory to hold the "n" values for the missing coordinate. */
+ work = astMalloc( sizeof( float )*(size_t) n );
+ if( work ) {
+
+/* Set up pointers to the x, y and z arrays in the 3D graphics system.
+ Fill the missing array with suitable values (the constant value of
+ the third axis on the plane being drawn). */
+ if( plane == XY ) {
+ x3d = (float *) x;
+ y3d = (float *) y;
+ z3d = work;
+
+ for( i = 0; i < n; i++ ) z3d[ i ] = gcon;
+
+ } else if( plane == XZ ) {
+ x3d = (float *) x;
+ y3d = work;
+ z3d = (float *) y;
+ for( i = 0; i < n; i++ ) y3d[ i ] = gcon;
+
+ } else if( plane == YZ ) {
+ x3d = work;
+ y3d = (float *) x;
+ z3d = (float *) y;
+ for( i = 0; i < n; i++ ) x3d[ i ] = gcon;
+
+ } else {
+ astError( AST__INTER, "astG3DLine(Plot3D): Illegal plane "
+ "identifier %d supplied (internal AST programming "
+ "error).", status, plane );
+ }
+
+/* If ok, draw the lines in the 3D graphics coordinate space. */
+ if( x3d ) {
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Draw the line */
+ result = astG3DLine( n, x3d, y3d, z3d );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+ }
+ }
+
+/* Free resources. */
+ work = astFree( work );
+
+/* Return the result. */
+ return result;
+}
+
+static int Plot3DMark( AstKeyMap *grfconID, int n, const float *x,
+ const float *y, int type ){
+/*
+* Name:
+* Plot3DMark
+
+* Purpose:
+* Draw a set of markers.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DMark( AstKeyMap *grfconID, int n, const float *x,
+* const float *y, int type )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* draw a set of markers. It forwards the call to the grf3D module being
+* used by this Plot3D. It should be registered with each of the 2D Plots
+* using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* n
+* The number of markers to draw.
+* x
+* A pointer to an array holding the "n" x values.
+* y
+* A pointer to an array holding the "n" y values.
+* type
+* An integer which can be used to indicate the type of marker symbol
+* required.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *grfcon;
+ double gcon;
+ float *work;
+ float *x3d = NULL;
+ float *y3d = NULL;
+ float *z3d = NULL;
+ float norm[ 3 ];
+ int i;
+ int plane;
+ int rc;
+ int result = 0;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Get a genuine pointer from the supplied grfcon identifier. */
+ grfcon = astMakePointer( grfconID );
+
+/* Report an error if no grfcon object was supplied. */
+ if( !grfcon ) {
+ astError( AST__INTER, "astG3DMark(Plot3D): No grfcon Object supplied "
+ "(internal AST programming error)." , status);
+
+/* If a grfcon Object was supplied, get the graphics box array out of it. */
+ } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
+ astError( AST__INTER, "astG3DMark(Plot3D): No \"Gcon\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* If a grfcon Object was supplied, get the RootCorner value out of it. */
+ } else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) {
+ astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* Also get the plane index out of it. */
+ } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
+ astError( AST__INTER, "astG3DMark(Plot3D): No \"Plane\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+ }
+
+/* Allocate memory to hold the "n" values for the missing coordinate. */
+ work = astMalloc( sizeof( float )*(size_t) n );
+ if( work ) {
+
+/* Set up pointers to the x, y and z arrays in the 3D graphics system.
+ Fill the missing array with suitable values (the constant value of
+ the third axis on the plane being drawn). Set the normal vector for
+ the markers so that they are drawn in the plane described by the Plot.*/
+ if( plane == XY ) {
+ x3d = (float *) x;
+ y3d = (float *) y;
+ z3d = work;
+ for( i = 0; i < n; i++ ) z3d[ i ] = gcon;
+ norm[ 0 ] = 0;
+ norm[ 1 ] = 0;
+ norm[ 2 ] = ( rc & 4 ) ? 1 : -1;
+
+ } else if( plane == XZ ) {
+ x3d = (float *) x;
+ y3d = work;
+ z3d = (float *) y;
+ for( i = 0; i < n; i++ ) y3d[ i ] = gcon;
+ norm[ 0 ] = 0;
+ norm[ 1 ] = ( rc & 2 ) ? 1 : -1;
+ norm[ 2 ] = 0;
+
+ } else if( plane == YZ ) {
+ x3d = work;
+ y3d = (float *) x;
+ z3d = (float *) y;
+ for( i = 0; i < n; i++ ) x3d[ i ] = gcon;
+ norm[ 0 ] = ( rc & 1 ) ? 1 : -1;
+ norm[ 1 ] = 0;
+ norm[ 2 ] = 0;
+
+ } else {
+ astError( AST__INTER, "astG3DMark(Plot3D): Illegal plane "
+ "identifier %d supplied (internal AST programming "
+ "error).", status, plane );
+ }
+
+/* If ok, draw the markers in the 3D graphics coordinate space. */
+ if( x3d ) {
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Draw the markers */
+ result = astG3DMark( n, x3d, y3d, z3d, type, norm );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+ }
+ }
+
+/* Free resources. */
+ work = astFree( work );
+
+/* Return the result. */
+ return result;
+}
+
+static int Plot3DQch( AstKeyMap *grfconID, float *chv, float *chh ){
+/*
+* Name:
+* Plot3DQch
+
+* Purpose:
+* Get the current text size.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DQch( AstKeyMap *grfconID, float *chv, float *chh )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* get the current text size. It forwards the call to the grf3D module
+* being used by this Plot3D. It should be registered with each of the
+* 2D Plots using astGrfSet.
+*
+* The grf3D module assumes that the 3D graphics axes are equally
+* scaled and therefore the text height does not depend on the text
+* orientation. Therefore, "chv" and "chh" are returned holding the
+* same value.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* chv
+* A pointer to the float which is to receive the height of
+* characters drawn with a vertical baseline in the 2D Plot. This
+* will be an increment in the 2D X axis.
+* chh
+* A pointer to the float which is to receive the height of
+* characters drawn with a horizontal baseline in the 2D Plot. This
+* will be an increment in the 2D Y axis.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ int result;
+ float ch;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return 0;
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* Use the function in the external Grf3D module, selected at link-time
+ using ast_link options. Note, text height is the same for each
+ of the three Plot. */
+ result = astG3DQch( &ch );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+
+/* Store the value in both the returned values. */
+ *chv = ch;
+ *chh = ch;
+
+/* Return the error flag. */
+ return result;
+}
+
+static int Plot3DScales( AstKeyMap *grfconID, float *alpha, float *beta ){
+/*
+* Name:
+* Plot3DScales
+
+* Purpose:
+* Get the 2D axis scales.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DScales( AstKeyMap *grfconID, float *alpha, float *beta )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* get the relative scales of the 2D axes. Since the grf3D module
+* assumes that all graphics axes are equally scaled, it just returns 1.0
+* for alpha and beta.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* alpha
+* A pointer to the float which is to receive the scale for the X
+* axis
+* beta
+* A pointer to the float which is to receive the scale for the Y
+* axis
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+ *alpha = 1.0;
+ *beta = 1.0;
+ return 1;
+}
+
+static int Plot3DText( AstKeyMap *grfconID, const char *text, float x, float y,
+ const char *just, float upx, float upy ){
+/*
+* Name:
+* Plot3DText
+
+* Purpose:
+* Draw a text string.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DText( AstKeyMap *grfconID, const char *text, float x, float y,
+* const char *just, float upx, float upy )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* draw a text string. It forwards the call to the grf3D module being
+* used by this Plot3D. It should be registered with each of the 2D Plots
+* using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* text
+* Pointer to a null-terminated character string to be displayed.
+* x
+* The reference x coordinate.
+* y
+* The reference y coordinate.
+* just
+* A character string which specifies the location within the
+* text string which is to be placed at the reference position
+* given by x and y.
+* upx
+* The x component of the up-vector for the text.
+* upy
+* The y component of the up-vector for the text.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *grfcon;
+ double gcon;
+ float norm[ 3 ];
+ float ref[ 3 ];
+ float up[ 3 ];
+ int plane;
+ int rc;
+ int result = 0;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Get a genuine pointer from the supplied grfcon identifier. */
+ grfcon = astMakePointer( grfconID );
+
+/* Report an error if no grfcon object was supplied. */
+ if( !grfcon ) {
+ astError( AST__INTER, "astG3DText(Plot3D): No grfcon Object supplied "
+ "(internal AST programming error)." , status);
+
+/* If a grfcon Object was supplied, get the graphics box array out of it. */
+ } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
+ astError( AST__INTER, "astG3DText(Plot3D): No \"Gcon\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* If a grfcon Object was supplied, get the RootCorner value out of it. */
+ } else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) {
+ astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* Also get the plane index out of it. */
+ } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
+ astError( AST__INTER, "astG3DText(Plot3D): No \"Plane\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* If OK, draw the text. */
+ } else {
+
+/* Set up the reference, up and normal vectors so that the text appears
+ on the required plane. */
+ if( plane == XY ) {
+ ref[ 0 ] = x;
+ ref[ 1 ] = y;
+ ref[ 2 ] = gcon;
+ norm[ 0 ] = 0;
+ norm[ 1 ] = 0;
+ norm[ 2 ] = ( rc & 4 ) ? 1 : -1;
+ up[ 0 ] = upx;
+ up[ 1 ] = upy;
+ up[ 2 ] = 0;
+
+ } else if( plane == XZ ) {
+ ref[ 0 ] = x;
+ ref[ 1 ] = gcon;
+ ref[ 2 ] = y;
+ norm[ 0 ] = 0;
+ norm[ 1 ] = ( rc & 2 ) ? 1 : -1;
+ norm[ 2 ] = 0;
+ up[ 0 ] = upx;
+ up[ 1 ] = 0;
+ up[ 2 ] = upy;
+
+ } else if( plane == YZ ) {
+ ref[ 0 ] = gcon;
+ ref[ 1 ] = x;
+ ref[ 2 ] = y;
+ norm[ 0 ] = ( rc & 1 ) ? 1 : -1;
+ norm[ 1 ] = 0;
+ norm[ 2 ] = 0;
+ up[ 0 ] = 0;
+ up[ 1 ] = upx;
+ up[ 2 ] = upy;
+
+ } else {
+ astError( AST__INTER, "astG3DText(Plot3D): Illegal plane "
+ "identifier %d supplied (internal AST programming "
+ "error).", status, plane );
+ }
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* If ok, draw the markers in the 3D graphics coordinate space. */
+ if( astOK ) result = astG3DText( text, ref, just, up, norm );
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int Plot3DTxExt( AstKeyMap *grfconID, const char *text, float x, float y,
+ const char *just, float upx, float upy, float *xb,
+ float *yb ){
+/*
+* Name:
+* Plot3DTxExt
+
+* Purpose:
+* Determine the plotted extent of a text string.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int Plot3DTxExt( AstKeyMap *grfconID, const char *text, float x,
+* float y, const char *just, float upx, float upy,
+* float *xb, float *yb )
+
+* Class Membership:
+* Plot3D member function.
+
+* Description:
+* This function is called by one of the three encapsulated 2D Plots to
+* determine the extent a string would have if plotted using Plot3DText.
+* It forwards the call to the grf3D module being used by this Plot3D. It
+* should be registered with each of the 2D Plots using astGrfSet.
+
+* Parameters:
+* grfconID
+* The Plot's GrfContext KeyMap. This is
+* used to identify which of the three Plots is calling this function.
+* text
+* Pointer to a null-terminated character string to be displayed.
+* x
+* The reference x coordinate.
+* y
+* The reference y coordinate.
+* just
+* A character string which specifies the location within the
+* text string which is to be placed at the reference position
+* given by x and y.
+* upx
+* The x component of the up-vector for the text.
+* upy
+* The y component of the up-vector for the text.
+* xb
+* An array of 4 elements in which to return the x coordinate of
+* each corner of the bounding box.
+* yb
+* An array of 4 elements in which to return the y coordinate of
+* each corner of the bounding box.
+
+* Returned Value:
+* An integer value of 0 is returned if an error occurs, and 1 otherwise.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *grfcon;
+ double gcon;
+ float *xb3d = NULL;
+ float *yb3d = NULL;
+ float *zb3d = NULL;
+ float bl[ 3 ];
+ float norm[ 3 ];
+ float ref[ 3 ];
+ float unused[ 4 ];
+ float up[ 3 ];
+ int plane;
+ int rc;
+ int result = 0;
+ int *status;
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Get a genuine pointer from the supplied grfcon identifier. */
+ grfcon = astMakePointer( grfconID );
+
+/* Report an error if no grfcon object was supplied. */
+ if( !grfcon ) {
+ astError( AST__INTER, "astG3DTxExt(Plot3D): No grfcon Object supplied "
+ "(internal AST programming error)." , status);
+
+/* If a grfcon Object was supplied, get the graphics box array out of it. */
+ } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
+ astError( AST__INTER, "astG3DTxExt(Plot3D): No \"Gcon\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* If a grfcon Object was supplied, get the RootCorner value out of it. */
+ } else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) {
+ astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* Also get the plane index out of it. */
+ } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
+ astError( AST__INTER, "astG3DTxExt(Plot3D): No \"Plane\" key found "
+ "in the supplied grfcon Object (internal AST programming "
+ "error)." , status);
+
+/* If OK, get the extent of the text. */
+ } else {
+
+/* Set up the reference, up and normal vectors so that the text appears
+ on the required plane. */
+ if( plane == XY ) {
+ ref[ 0 ] = x;
+ ref[ 1 ] = y;
+ ref[ 2 ] = gcon;
+ norm[ 0 ] = 0;
+ norm[ 1 ] = 0;
+ norm[ 2 ] = ( rc & 4 ) ? 1 : -1;
+ up[ 0 ] = upx;
+ up[ 1 ] = upy;
+ up[ 2 ] = 0;
+ xb3d = xb;
+ yb3d = yb;
+ zb3d = unused;
+
+ } else if( plane == XZ ) {
+ ref[ 0 ] = x;
+ ref[ 1 ] = gcon;
+ ref[ 2 ] = y;
+ norm[ 0 ] = 0;
+ norm[ 1 ] = ( rc & 2 ) ? 1 : -1;
+ norm[ 2 ] = 0;
+ up[ 0 ] = upx;
+ up[ 1 ] = 0;
+ up[ 2 ] = upy;
+ xb3d = xb;
+ yb3d = unused;
+ zb3d = yb;
+
+ } else if( plane == YZ ) {
+ ref[ 0 ] = gcon;
+ ref[ 1 ] = x;
+ ref[ 2 ] = y;
+ norm[ 0 ] = ( rc & 1 ) ? 1 : -1;
+ norm[ 1 ] = 0;
+ norm[ 2 ] = 0;
+ up[ 0 ] = 0;
+ up[ 1 ] = upx;
+ up[ 2 ] = upy;
+ xb3d = unused;
+ yb3d = xb;
+ zb3d = yb;
+
+ } else {
+ astError( AST__INTER, "astG3DTxExt(Plot3D): Illegal plane "
+ "identifier %d supplied (internal AST programming "
+ "error).", status, plane );
+ }
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+/* If ok, get the extent of the text. */
+ if( astOK ) result = astG3DTxExt( text, ref, just, up, norm, xb3d, yb3d,
+ zb3d, bl );
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim,
+ const double *in, int *status ){
+/*
+* Name:
+* PolyCurve
+
+* Purpose:
+* Draw a series of connected geodesic curves.
+
+* Type:
+* Private member function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim,
+* const double *in, int *status )
+
+* Class Membership:
+* Plot method (overrides the astPolyCurve method inherited form the
+* Plot class)
+
+* Description:
+* This function joins a series of points specified in the physical
+* coordinate system of a Plot by drawing a sequence of geodesic
+* curves. It is equivalent to making repeated use of the astCurve
+* function (q.v.), except that astPolyCurve will generally be more
+* efficient when drawing many geodesic curves end-to-end. A
+* typical application of this might be in drawing contour lines.
+*
+* As with astCurve, full account is taken of the Mapping between
+* physical and graphical coordinate systems. This includes any
+* discontinuities and clipping established using astClip.
+
+* Parameters:
+* this
+* Pointer to the Plot.
+* npoint
+* The number of points between which geodesic curves are to be drawn.
+* ncoord
+* The number of coordinates being supplied for each point (i.e.
+* the number of axes in the current Frame of the Plot, as given
+* by its Naxes attribute).
+* indim
+* The number of elements along the second dimension of the "in"
+* array (which contains the input coordinates). This value is
+* required so that the coordinate values can be correctly
+* located if they do not entirely fill this array. The value
+* given should not be less than "npoint".
+* in
+* The address of the first element in a 2-dimensional array of shape
+* "[ncoord][indim]" giving the
+* physical coordinates of the points which are to be joined in
+* sequence by geodesic curves. These should be stored such that
+* the value of coordinate number "coord" for point number
+* "point" is found in element "in[coord][point]".
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - No curve is drawn on either side of any point which has any
+* coordinate equal to the value AST__BAD.
+* - An error results if the base Frame of the Plot is not
+* 2-dimensional.
+* - An error also results if the transformation between the
+* current and base Frames of the Plot is not defined (i.e. the
+* Plot's TranInverse attribute is zero).
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+ astError( AST__INTER, "astPolyCurve(%s): The astPolyCurve "
+ "method cannot be used with a %s (programming error).", status,
+ astGetClass( this ), astGetClass( this ) );
+}
+
+static void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status ) {
+/*
+* Name:
+* RemoveFrame
+
+* Purpose:
+* Remove a Frame from a Plot3D.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+* #include "plot.h"
+* void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the astRemoveFrame public
+* method inherited from the Plot class).
+
+* Description:
+* This function removes a Frame from a Plot3D. All other Frames
+* in the Plot3D have their indices re-numbered from one (if
+* necessary), but are otherwise unchanged.
+*
+* If the index of the original base Frame is changed, the index value
+* stored in the Plot3D is updated. If the base Frame itself is
+* removed, an error is reported.
+
+* Parameters:
+* this_fset
+* Pointer to the FrameSet component of the Plot3D.
+* iframe
+* The index within the Plot3D of the Frame to be removed.
+* This value should lie in the range from 1 to the number of
+* Frames in the Plot3D (as given by its Nframe attribute).
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstPlot3D *this; /* Pointer to Plot3D structure */
+ int ifrm; /* Validated frame index */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_fset;
+
+/* Validate the frame index. This translated AST__BASE and AST__CURRENT
+ into actual Frame indices. */
+ ifrm = astValidateFrameIndex( this_fset, iframe, "astRemoveFrame" );
+
+/* Report an error if an attempt is made to delete the Frame that defines
+ the mapping onto the screen (the original base Frame in the FrameSet
+ supplied when the Plot3D was constructed). */
+ if( ifrm == this->pix_frame ){
+ astError( AST__PXFRRM, "astRemoveFrame(%s): Cannot delete Frame "
+ "number %d from the supplied %s since it is the Frame "
+ "that defines the mapping onto the graphics plane.", status,
+ astGetClass( this ), iframe, astGetClass( this ) );
+
+/* Otherwise, invoke the parent astRemoveFrame method to remove the Frame. */
+ } else {
+ (*parent_removeframe)( this_fset, iframe, status );
+
+/* If the index of the removed Frame is smaller than the original base Frame
+ index, then decrement the original base Frame index so that the same Frame
+ will be used in future. */
+ if( astOK ){
+ if( ifrm < this->pix_frame ) (this->pix_frame)--;
+ }
+ }
+}
+
+static int RootCornerInt( const char *rootcorner, int *status ){
+/*
+* Name:
+* RootCornerInt
+
+* Purpose:
+* Convert a RootCorner string to an integer.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int RootCornerInt( const char *rootcorner, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function converts a RootCorner string to an integer.
+
+* Parameters:
+* rootcorner
+* The string value to convert. Should be 3 characters long
+* and contain nothing but "U" or "L" (upper or lower case).
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The integer value that is is the protected equivalent of the
+* supplied string. A negative value is returned if an error has
+* already occurred, of the supplied string is not legal.
+
+*/
+
+/* Local Variables: */
+ int result;
+
+/* Initialise */
+ result = -1;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Compare thr supplied value with every legal value. */
+ if( astChrMatch( rootcorner, "LLL" ) ) {
+ result = LLL;
+
+ } else if( astChrMatch( rootcorner, "ULL" ) ) {
+ result = ULL;
+
+ } else if( astChrMatch( rootcorner, "LUL" ) ) {
+ result = LUL;
+
+ } else if( astChrMatch( rootcorner, "UUL" ) ) {
+ result = UUL;
+
+ } else if( astChrMatch( rootcorner, "LLU" ) ) {
+ result = LLU;
+
+ } else if( astChrMatch( rootcorner, "ULU" ) ) {
+ result = ULU;
+
+ } else if( astChrMatch( rootcorner, "LUU" ) ) {
+ result = LUU;
+
+ } else if( astChrMatch( rootcorner, "UUU" ) ) {
+ result = UUU;
+
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static const char *RootCornerString( int rootcorner, int *status ){
+/*
+* Name:
+* RootCornerString
+
+* Purpose:
+* Convert a RootCorner integer to a string.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* const char *RootCornerString( int rootcorner, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function converts a RootCorner integer to a string.
+
+* Parameters:
+* rootcorner
+* The integer value to convert. Should be in the range 0 to 7.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a static string that is the public equivalent of the
+* supplied integer. A NULL pointer is returned if an error has
+* already occurred, of the supplied integer is not legal.
+
+*/
+
+/* Local Variables: */
+ char *result;
+
+/* Initialise */
+ result = NULL;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Compare thr supplied value with every legal value. */
+ if( rootcorner == LLL ) {
+ result = "LLL";
+
+ } else if( rootcorner == ULL ) {
+ result = "ULL";
+
+ } else if( rootcorner == LUL ) {
+ result = "LUL";
+
+ } else if( rootcorner == UUL ) {
+ result = "UUL";
+
+ } else if( rootcorner == LLU ) {
+ result = "LLU";
+
+ } else if( rootcorner == ULU ) {
+ result = "ULU";
+
+ } else if( rootcorner == LUU ) {
+ result = "LUU";
+
+ } else if( rootcorner == UUU ) {
+ result = "UUU";
+
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static void Set3DGrf( AstPlot3D *this, AstPlot *plot, int plane, int *status ){
+/*
+* Name:
+* Set3DGrf
+
+* Purpose:
+* Associate GRF functions with a Plot.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void Set3DGrf( AstPlot3D *this, AstPlot *plot, int plane, int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function registers grf functions defined in this class with
+* the supplied Plot, so that, whenever the Plot draws anything, the
+* plotting request is caught by this class and converted into an
+* appropriate grf3D call.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* plot
+* Pointer to the Plot.
+* plane
+* An integer identifier for the plane within 3D GRAPHICS
+* coordinates upon which the supplied Plot should draw.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *grfcon;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Register the plotting functions defined in this class, so that the
+ plot will call these functions to do its 2D plotting. */
+ astGrfSet( plot, "Attr", (AstGrfFun) Plot3DAttr );
+ astGrfSet( plot, "Cap", (AstGrfFun) Plot3DCap );
+ astGrfSet( plot, "Flush", (AstGrfFun) Plot3DFlush );
+ astGrfSet( plot, "Line", (AstGrfFun) Plot3DLine );
+ astGrfSet( plot, "Mark", (AstGrfFun) Plot3DMark );
+ astGrfSet( plot, "Qch", (AstGrfFun) Plot3DQch );
+ astGrfSet( plot, "Scales", (AstGrfFun) Plot3DScales );
+ astGrfSet( plot, "Text", (AstGrfFun) Plot3DText );
+ astGrfSet( plot, "TxExt", (AstGrfFun) Plot3DTxExt );
+
+/* Ensure that the Plot uses the grf interface registered using
+ astGrfSet. */
+ astSetGrf( plot, 1 );
+
+/* When the above functions are called, they need to know which plane
+ they are drawing on. So we put this information into the GrfContext
+ KeyMap stored in the Plot. This KeyMap will be passed to the above
+ drawing functions when they are called from within the Plot class. */
+ grfcon = astGetGrfContext( plot );
+ astMapPut0I( grfcon, "Plane", plane, "The 2D plane being drawn on" );
+ if( plane == XY ) {
+ astMapPut0D( grfcon, "Gcon", this->gbox[2], "Constant Z value" );
+ } else if( plane == XZ ) {
+ astMapPut0D( grfcon, "Gcon", this->gbox[1], "Constant Y value" );
+ } else {
+ astMapPut0D( grfcon, "Gcon", this->gbox[0], "Constant X value" );
+ }
+ astMapPut0I( grfcon, "RootCorner", astGetRootCorner(this), "The labelled corner" );
+ grfcon = astAnnul( grfcon );
+}
+
+static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
+/*
+* Name:
+* SetAttrib
+
+* Purpose:
+* Set an attribute value for a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void SetAttrib( AstObject *this, const char *setting, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the astSetAttrib protected
+* method inherited from the Plot class).
+
+* Description:
+* This function assigns an attribute value for a Plot3D, the
+* attribute and its value being specified by means of a string of
+* the form:
+*
+* "attribute= value "
+*
+* Here, "attribute" specifies the attribute name and should be in
+* lower case with no white space present. The value to the right
+* of the "=" should be a suitable textual representation of the
+* value to be assigned and this will be interpreted according to
+* the attribute's data type. White space surrounding the value is
+* only significant for string attributes.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* setting
+* Pointer to a null terminated string specifying the new attribute
+* value.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstPlot *plot; /* Pointer to specific Plot */
+ AstPlot3D *this; /* Pointer to the Plot3D structure */
+ char pat[30]; /* Regular expression pattern */
+ char spec[10]; /* Plane specification */
+ const char *null = ""; /* Pointer to null string */
+ const char *psetting; /* Pointer to plot-specific attrib setting */
+ double dval; /* Floating point attribute value */
+ int axis; /* Axis index */
+ int ival; /* Int attribute value */
+ int len; /* Length of setting string */
+ int nc; /* Number of characters read by astSscanf */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Obtain the length of the setting string. */
+ len = (int) strlen( setting );
+
+/* Test for each recognised attribute in turn, using "astSscanf" to parse
+ the setting string and extract the attribute value (or an offset to
+ it in the case of string values). In each case, use the value set
+ in "nc" to check that the entire string was matched. Once a value
+ has been obtained, use the appropriate method to set it. */
+
+/* Norm(axis). */
+/* ----------- */
+ if ( nc = 0,
+ ( 2 == astSscanf( setting, "norm(%d)= %lg %n",
+ &axis, &dval, &nc ) )
+ && ( nc >= len ) ) {
+ astSetNorm( this, axis - 1, dval );
+
+/* RootCorner. */
+/* ----------- */
+ } else if( nc = 0,
+ ( 0 == astSscanf( setting, "rootcorner=%n%*[^\n]%n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ ival = RootCornerInt( setting + ival, status );
+ if( astOK && ival < 0 ) {
+ astError( AST__ATTIN, "astSetAttrib(Plot3D): Unusable value \"%s\" "
+ "given for attribute RootCorner.", status, setting + ival );
+ } else {
+ astSetRootCorner( this, ival );
+ }
+
+/* ..._XY etc */
+/* ---------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "%*[a-z]_%[xyz]%n", spec, &nc ) ) ) {
+
+ if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
+ plot = this->plotxy;
+ } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
+ plot = this->plotyz;
+ } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
+ plot = this->plotxz;
+ } else {
+ plot = NULL;
+ }
+
+ if( plot ) {
+ sprintf( pat, ".*(_%s).*", spec );
+ psetting = astChrSub( setting, pat, &null, 1 );
+ astSetAttrib( plot, psetting );
+ psetting = astFree( (void *) psetting );
+
+ } else {
+ (*parent_setattrib)( this_object, setting, status );
+ }
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ (*parent_setattrib)( this_object, setting, status );
+ }
+
+/* Undefine macros local to this function. */
+#undef MATCH
+}
+
+static void SetCurrent( AstFrameSet *this_frameset, int iframe, int *status ) {
+/*
+* Name:
+* SetCurrent
+
+* Purpose:
+* Set a value for the Current attribute of a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int astSetCurrent( AstFrameSet *this, int iframe, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the public astSetCurrent method
+* inherited from the FrameSet class).
+
+* Description:
+* This function sets a value for the Current attribute of a
+* Plot3D. This attribute is an index that identifies the current
+* Frame for the Plot3D.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* iframe
+* Value to be set for the Current attribute.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Invoke the parent astSetCurrent method. */
+ (*parent_setcurrent)( this_frameset, iframe, status );
+
+/* Update the three 2D Plots stored in the Plot3D structure so that they
+ reflect this modified FrameSet. */
+ UpdatePlots( (AstPlot3D *) this_frameset, status );
+}
+
+static void SetPlotAttr( AstPlot *plot, int plane, int label[ 2 ], int *status ){
+/*
+* Name:
+* SetPlotAttr
+
+* Purpose:
+* Set the attributes ofr one of the encapsulated Plots.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void SetPlotAttr( AstPlot *plot, int plane, int label[ 2 ], int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function sets the attributes of one of the encapsulated Plots.
+
+* Parameters:
+* plot
+* Pointer to the Plot to modify.
+* plane
+* The 3D plane spanned by the 2D plot.
+* label
+* Array indicating if each WCS axis should be labelled or not.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ int axis;
+
+/* Check the inherited status. */
+ if( !astOK ) return;
+
+/* Ensure that the Plot uses the grf interface registered using
+ astGrfSet. */
+ astSetGrf( plot, 1 );
+
+/* Ensure that no title is drawn. */
+ astSetDrawTitle( plot, 0 );
+
+/* For each axis, ensure that no axis labels or ticks are produced unless
+ the axis is indicated as a labelled axis in the supplied array. */
+ for( axis = 0; axis < 2; axis++ ) {
+ if( !label[ axis ] ) {
+ astSetLabelUnits( plot, axis, 0 );
+ astSetNumLab( plot, axis, 0 );
+ astSetTextLab( plot, axis, 0 );
+ }
+ }
+}
+
+static void SetRootCorner( AstPlot3D *this, int rootcorner, int *status ){
+/*
+*+
+* Name:
+* astSetRootCorner
+
+* Purpose:
+* Set a new value for the RootCorner attribute.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void astSetRootCorner( AstPlot3D *this, int rootcorner )
+
+* Class Membership:
+* Plot method.
+
+* Description:
+* This function sets a new value for the RootCorner attribute.
+
+* Parameters:
+* this
+* Pointer to a Plot3D.
+* rootcorner
+* The new RootCorner value.
+
+*-
+*/
+
+/* Check the global status. */
+ if( !astOK ) return;
+
+/* Report an error if the new value is out of bounds. */
+ if( rootcorner < 0 || rootcorner > 7 ){
+ astError( AST__ATTIN, "astSetRootCorner(Plot3D): Invalid value %d "
+ "supplied for RootCorner attribute", status,rootcorner);
+
+/* If the new corner is OK, mirror any axes of the encapsulated Plots
+ that need mirroring (this is done to ensure that Plots look right when
+ viewed from the outside of the graphics cube), and modify the Edge
+ attributes in the encapsulated Plots to ensure the labels appear on the
+ requested edges of the 3D graphics cube. . */
+ } else {
+ ChangeRootCorner( this, astGetRootCorner(this), rootcorner, status );
+
+/* Store the new value. */
+ this->rootcorner = rootcorner;
+ }
+}
+
+static void SetTickValues( AstPlot *this, int axis, int nmajor, double *major,
+ int nminor, double *minor, int *status ){
+/*
+* Name:
+* SetTickValues
+
+* Purpose:
+* Store the tick mark values to use for a given Plot axis.
+
+* Type:
+* Private member function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void SetTickValues( AstPlot *this, int axis, int nmajor,
+* double *major, int nminor, double *minor, int *status )
+
+* Class Membership:
+* Plot method (overrides the astSetTickValues method inherited form
+* the Plot class)
+
+* Description:
+* This function stores a set of tick mark values that should be used by
+* subsequent calls to astGrid.
+
+* Parameters:
+* this
+* Pointer to a Plot.
+* axis
+* The zero-based index of the axis for which tick marks values
+* have been supplied.
+* nmajor
+* The number of major tick mark values. If zero is supplied then
+* the other parameters are ignored, and subsequent calls to
+* astGrid will itself determine the tick values to be used.
+* major
+* Pointer to an array holding "nmajor" values for axis "axis" in
+* the current Frame of the suppled Plot. Major tick marks will be
+* drawn at these values.
+* nminor
+* The number of minor tick mark values.
+* minor
+* Pointer to an array holding "nminor" values for axis "axis" in
+* the current Frame of the suppled Plot. Minor tick marks will be
+* drawn at these values.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Check the global status. */
+ if( !astOK ) return;
+
+ astError( AST__INTER, "astSetTickValues(%s): The astSetTickValues "
+ "method cannot be used with a %s (programming error).", status,
+ astGetClass( this ), astGetClass( this ) );
+}
+
+static void SplitFrameSet( AstFrameSet *fset,
+ AstFrameSet **fsetxy, int labelxy[2], int wcsxy[2],
+ AstFrameSet **fsetxz, int labelxz[2], int wcsxz[2],
+ AstFrameSet **fsetyz, int labelyz[2], int wcsyz[2],
+ int *baseplane, int *status ){
+/*
+* Name:
+* SplitFrameSet
+
+* Purpose:
+* Split a 3D FrameSet into three 2D FrameSets.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void SplitFrameSet( AstFrameSet *fset,
+* AstFrameSet **fsetxy, int labelxy[2], int wcsxy[2],
+* AstFrameSet **fsetxz, int labelxz[2], int wcsxz[2],
+* AstFrameSet **fsetyz, int labelyz[2], int wcsyz[2],
+* int *baseplane, int *status )
+
+* Class Membership:
+* Plot3D member function
+
+* Description:
+* This function returns 3 FrameSets, each of which has 2-dimensional
+* base and current Frames. Each of the 2D base Frames span a single
+* plane in the 3D base Frame of the supplied FrameSet. Likewise, each
+* of the 2D current Frames span a single plane in the 3D current Frame
+* of the supplied FrameSet. An error is reported if there is no
+* one-to-one association between the three planes in the supplied
+* current Frame and the 3 planes in the supplied base Frame.
+
+* Parameters:
+* fset
+* Pointer to a FrameSet that has a 3D base Frame and a 3D current
+* Frame.
+* fsetxy
+* Pointer to a location at which to return a pointer to a new FrameSet
+* that has a 2D base Frame and a 2D current. The base Frame spans
+* axes 1 and 2 of the 3D base Frame in "fset".
+* labelxy
+* Returned holding flags indicating if the two axes of the current
+* Frame in *fsetxy should be labelled or not.
+* wcsxy
+* Returned holding the zero based axis index within the current Frame
+* of the supplied FrameSet that corresponds to each of the two
+* current Frame axes in the "fsetxy" FrameSet.
+* fsetxz
+* Pointer to a location at which to return a pointer to a new FrameSet
+* that has a 2D base Frame and a 2D current. The base Frame spans
+* axes 1 and 3 of the 3D base Frame in "fset".
+* labelxz
+* Returned holding flags indicating if the two axes of the current
+* Frame in *fsetxz should be labelled or not.
+* wcsxz
+* Returned holding the zero based axis index within the current Frame
+* of the supplied FrameSet that corresponds to each of the two
+* current Frame axes in the "fsetxz" FrameSet.
+* fsetyz
+* Pointer to a location at which to return a pointer to a new FrameSet
+* that has a 2D base Frame and a 2D current. The base Frame spans
+* axes 2 and 3 of the 3D base Frame in "fset".
+* labelyz
+* Returned holding flags indicating if the two axes of the current
+* Frame in *fsetyz should be labelled or not.
+* wcsyz
+* Returned holding the zero based axis index within the current Frame
+* of the supplied FrameSet that corresponds to each of the two
+* current Frame axes in the "fsetxy" FrameSet.
+* baseplane
+* Index of the plane that is spanned by two connected 3D axes.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - Null pointers will be returned for the three new FrameSets if this
+* function is invoked with the global status set, or if it should fail
+* for any reason.
+* - Each returned FrameSet has 2 Frames: Frame 1 is the base Frame
+* and is spanned by 2 of the 3 axes in the base Frame of the supplied
+* FrameSet; Frame 2 is the current Frame and is spanned by 2 of the 3
+* axes in the current Frame of the supplied FrameSet. Any future changes
+* to this function that alter this structure should reflected in
+* equivalent changes to functions CreatePlots and UpdatePlots.
+*/
+
+/* Local Variables: */
+ AstFrame *bfrm2d;
+ AstFrame *bfrm;
+ AstFrame *cfrm1d;
+ AstFrame *cfrm2d;
+ AstFrame *cfrm;
+ AstFrame *dummy;
+ AstFrameSet *fset1;
+ AstFrameSet *fset2;
+ AstFrameSet *fset3;
+ AstMapping *map1d;
+ AstMapping *map2d;
+ AstMapping *map;
+ AstMapping *smap;
+ AstUnitMap *unit1d;
+ int *axout;
+ int *other_axout;
+ int axin2[ 2 ];
+ int axin[ 2 ];
+ int i;
+ int label1[ 2 ];
+ int label2[ 2 ];
+ int label3[ 2 ];
+ int other_axin;
+ int wcsax1[ 2 ];
+ int wcsax2[ 2 ];
+ int wcsax3[ 2 ];
+
+/* Initialise. */
+ *fsetxy = NULL;
+ *fsetxz = NULL;
+ *fsetyz = NULL;
+ *baseplane = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get the simplified base -> current Mapping form the supplied FrameSet.
+ Also get the base and current Frames themselves. */
+ map = astGetMapping( fset, AST__BASE, AST__CURRENT );
+ smap = astSimplify( map );
+ map = astAnnul( map );
+ cfrm = astGetFrame( fset, AST__CURRENT );
+ bfrm = astGetFrame( fset, AST__BASE );
+
+/* Create a 1D UnitMap. */
+ unit1d = astUnitMap( 1, "", status );
+
+/* First try to identify a pair of base Frame axes that map onto a pair
+ of current Frame axes. This will be the case for instance if the 3D
+ FrameSet describes a spectral cube in which two of the axes are spanned
+ by a SkyFrame. We try all three possible pairs of axes in turn until a
+ pair is found succesfully. */
+ for( i = 0; i < 3 && ! *fsetxy; i++ ) {
+ axin[ 0 ] = ( i < 2 ) ? 0 : 1;
+ axin[ 1 ] = ( i == 0 ) ? 1 : 2;
+
+/* Try to get a Mapping that connects the inputs given by axin to a
+ distinct subset of outputs. */
+ axout = astMapSplit( smap, 2, axin, &map2d );
+
+/* If succesful, check that the 2 inputs correspond to exactly 2 outputs. */
+ if( map2d ){
+ if( astGetNout( map2d ) == 2 ) {
+
+/* Get the index of the other input axis (the one not included in the pair). */
+ other_axin = 3 - axin[ 0 ] - axin[ 1 ];
+
+/* Try to get a Mapping that connects this remaining input to a distinct
+ subset of outputs. */
+ other_axout = astMapSplit( smap, 1, &other_axin, &map1d );
+
+/* If succesful, check that the 1 input correspond to exactly 1 output. */
+ if( map1d ){
+ if( astGetNout( map1d ) == 1 ) {
+
+/* Pick the two axes from the 3D base and current Frames that correspond to
+ the paired axes found above. */
+ bfrm2d = astPickAxes( bfrm, 2, axin, NULL );
+ cfrm2d = astPickAxes( cfrm, 2, axout, NULL );
+
+/* Pick the axis from the 3D current Frame that correspond to
+ the other axis. */
+ cfrm1d = astPickAxes( cfrm, 1, other_axout, NULL );
+
+/* Construct a FrameSet using these 2D Frames and Mapping. */
+ fset1 = astFrameSet( bfrm2d, "", status );
+ astAddFrame( fset1, AST__BASE, map2d, cfrm2d );
+
+ bfrm2d = astAnnul( bfrm2d );
+ cfrm2d = astAnnul( cfrm2d );
+ map2d = astAnnul( map2d );
+
+/* Indicate that both axes in the current Frame of this FrameSet should
+ be labelled. */
+ label1[ 0 ] = 1;
+ label1[ 1 ] = 1;
+
+/* Store the index of the axis within the supplied current Frame that
+ corresponds to each axis in the current Frame of the FrameSet created
+ above. */
+ wcsax1[ 0 ] = axout[ 0 ];
+ wcsax1[ 1 ] = axout[ 1 ];
+
+/* Pick the two axes from the 3D base Frame that correspond to the first of
+ the paired axes, and the unpaired axis. Order them so that we get the
+ 2 axes in the order xy, xz or yz. Also, store the index of the axis
+ within the supplied current Frame that corresponds to each axis in
+ the current Frame of the FrameSet created below. */
+ if( i < 2 ) {
+ axin2[ 0 ] = axin[ 0 ];
+ axin2[ 1 ] = other_axin;
+ wcsax2[ 0 ] = axout[ 0 ];
+ wcsax2[ 1 ] = other_axout[ 0 ];
+ } else {
+ axin2[ 0 ] = other_axin;
+ axin2[ 1 ] = axin[ 0 ];
+ wcsax2[ 0 ] = other_axout[ 0 ];
+ wcsax2[ 1 ] = axout[ 0 ];
+ }
+ bfrm2d = astPickAxes( bfrm, 2, axin2, NULL );
+
+/* The corresponding current Frame in the new FrameSet will be a compound
+ 2D Frame holding the other current Frame axis and a copy of the input
+ base Frame axis. Combine them so that we get the 2 axes in the order
+ xy, xz or yz. */
+ dummy = astPickAxes( bfrm, 1, axin, NULL );
+ if( i < 2 ) {
+ cfrm2d = (AstFrame *) astCmpFrame( dummy, cfrm1d, "", status );
+ } else {
+ cfrm2d = (AstFrame *) astCmpFrame( cfrm1d, dummy, "", status );
+ }
+ dummy = astAnnul( dummy );
+
+/* The Mapping that joins this 2D base Frame to the 2D current Frame uses
+ the above 1D mapping for the other axis, and a UnitMap for the copied
+ base Frame axis. */
+ if( i < 2 ) {
+ map2d = (AstMapping *) astCmpMap( unit1d, map1d, 0, "", status );
+ } else {
+ map2d = (AstMapping *) astCmpMap( map1d, unit1d, 0, "", status );
+ }
+
+/* Construct a FrameSet using these 2D Frames and Mapping. */
+ fset2 = astFrameSet( bfrm2d, "", status );
+ astAddFrame( fset2, AST__BASE, map2d, cfrm2d );
+
+ bfrm2d = astAnnul( bfrm2d );
+ cfrm2d = astAnnul( cfrm2d );
+ map2d = astAnnul( map2d );
+
+/* Indicate that only one of the axes in the current Frame of this FrameSet
+ should be labelled. */
+ if( i < 2 ) {
+ label2[ 0 ] = 0;
+ label2[ 1 ] = 1;
+ } else {
+ label2[ 0 ] = 1;
+ label2[ 1 ] = 0;
+ }
+
+/* Pick the two axes from the 3D base Frame that correspond to the second
+ of the paired axes, and the unpaired axis. Order them so that we get the
+ 2 axes in the order xy, xz or yz. Also, store the index of the axis
+ within the supplied current Frame that corresponds to each axis in
+ the current Frame of the FrameSet created below. */
+ if( i == 0 ) {
+ axin2[ 0 ] = axin[ 1 ];
+ axin2[ 1 ] = other_axin;
+ wcsax3[ 0 ] = axout[ 1 ];
+ wcsax3[ 1 ] = other_axout[ 0 ];
+ } else {
+ axin2[ 0 ] = other_axin;
+ axin2[ 1 ] = axin[ 1 ];
+ wcsax3[ 0 ] = other_axout[ 0 ];
+ wcsax3[ 1 ] = axout[ 1 ];
+ }
+ bfrm2d = astPickAxes( bfrm, 2, axin2, NULL );
+
+/* The corresponding current Frame in the new FrameSet will be a compound
+ 2D Frame holding the other current Frame axis and a copy of the input
+ base Frame axis. Combine them so that we get the 2 axes in the order
+ xy, xz or yz. */
+ dummy = astPickAxes( bfrm, 1, axin + 1, NULL );
+ if( i == 0 ) {
+ cfrm2d = (AstFrame *) astCmpFrame( dummy, cfrm1d, "", status );
+ } else {
+ cfrm2d = (AstFrame *) astCmpFrame( cfrm1d, dummy, "", status );
+ }
+ dummy = astAnnul( dummy );
+
+/* The Mapping that joins this 2D base Frame to the 2D current Frame uses
+ the above 1D mapping for the other axis, and a UnitMap for the copied
+ base Frame axis. */
+ if( i == 0 ) {
+ map2d = (AstMapping *) astCmpMap( unit1d, map1d, 0, "", status );
+ } else {
+ map2d = (AstMapping *) astCmpMap( map1d, unit1d, 0, "", status );
+ }
+
+/* Construct a FrameSet using these 2D Frames and Mapping. */
+ fset3 = astFrameSet( bfrm2d, "", status );
+ astAddFrame( fset3, AST__BASE, map2d, cfrm2d );
+
+ bfrm2d = astAnnul( bfrm2d );
+ cfrm2d = astAnnul( cfrm2d );
+ map2d = astAnnul( map2d );
+
+/* Indicate that neither axis in the current Frame of this FrameSet
+ should be labelled. */
+ label3[ 0 ] = 0;
+ label3[ 1 ] = 0;
+
+/* Store each FrameSet in the correct returned pointer. */
+ if( i == 0 ) {
+ *baseplane = XY;
+ *fsetxy = fset1;
+ *fsetxz = fset2;
+ *fsetyz = fset3;
+
+ labelxy[ 0 ] = label1[ 0 ];
+ labelxy[ 1 ] = label1[ 1 ];
+ labelxz[ 0 ] = label2[ 0 ];
+ labelxz[ 1 ] = label2[ 1 ];
+ labelyz[ 0 ] = label3[ 0 ];
+ labelyz[ 1 ] = label3[ 1 ];
+
+ wcsxy[ 0 ] = wcsax1[ 0 ];
+ wcsxy[ 1 ] = wcsax1[ 1 ];
+ wcsxz[ 0 ] = wcsax2[ 0 ];
+ wcsxz[ 1 ] = wcsax2[ 1 ];
+ wcsyz[ 0 ] = wcsax3[ 0 ];
+ wcsyz[ 1 ] = wcsax3[ 1 ];
+
+ } else if( i == 1 ) {
+ *baseplane = XZ;
+ *fsetxy = fset2;
+ *fsetxz = fset1;
+ *fsetyz = fset3;
+
+ labelxy[ 0 ] = label2[ 0 ];
+ labelxy[ 1 ] = label2[ 1 ];
+ labelxz[ 0 ] = label1[ 0 ];
+ labelxz[ 1 ] = label1[ 1 ];
+ labelyz[ 0 ] = label3[ 0 ];
+ labelyz[ 1 ] = label3[ 1 ];
+
+ wcsxy[ 0 ] = wcsax2[ 0 ];
+ wcsxy[ 1 ] = wcsax2[ 1 ];
+ wcsxz[ 0 ] = wcsax1[ 0 ];
+ wcsxz[ 1 ] = wcsax1[ 1 ];
+ wcsyz[ 0 ] = wcsax3[ 0 ];
+ wcsyz[ 1 ] = wcsax3[ 1 ];
+
+ } else {
+ *baseplane = YZ;
+ *fsetxy = fset2;
+ *fsetxz = fset3;
+ *fsetyz = fset1;
+
+ labelxy[ 0 ] = label2[ 0 ];
+ labelxy[ 1 ] = label2[ 1 ];
+ labelxz[ 0 ] = label3[ 0 ];
+ labelxz[ 1 ] = label3[ 1 ];
+ labelyz[ 0 ] = label1[ 0 ];
+ labelyz[ 1 ] = label1[ 1 ];
+
+ wcsxy[ 0 ] = wcsax2[ 0 ];
+ wcsxy[ 1 ] = wcsax2[ 1 ];
+ wcsxz[ 0 ] = wcsax3[ 0 ];
+ wcsxz[ 1 ] = wcsax3[ 1 ];
+ wcsyz[ 0 ] = wcsax1[ 0 ];
+ wcsyz[ 1 ] = wcsax1[ 1 ];
+
+ }
+
+/* Free resources */
+ cfrm1d = astAnnul( cfrm1d );
+ }
+
+/* Free resources */
+ map1d = astAnnul( map1d );
+ other_axout = astFree( other_axout );
+ }
+ }
+
+/* Free resources */
+ if( map2d ) map2d = astAnnul( map2d );
+ axout = astFree( axout );
+
+/* Leave the loop if we now have the required FrameSets, or an error has
+ occurred. */
+ if( *fsetxy || !astOK ) break;
+
+ }
+ }
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ bfrm = astAnnul( bfrm );
+ smap = astAnnul( smap );
+ unit1d = astAnnul( unit1d );
+
+/* Return null pointers if an error has occurred. */
+ if( !astOK ) {
+ *fsetxy = astAnnul( *fsetxy );
+ *fsetxz = astAnnul( *fsetxz );
+ *fsetyz = astAnnul( *fsetyz );
+
+/* Report an error if the supplied FrameSet could not be split into 3
+ independent 2D planes. */
+ } if( ! *fsetxy ) {
+ astError( AST__3DFSET, "astInitPlot3D(Plot3D): Supplied %s contains "
+ "no independent axes.", status, astGetClass( fset ) );
+ }
+}
+
+static void StoreAxisInfo( AstPlot3D *this, int labelxy[2], int wcsxy[2],
+ int labelxz[2], int wcsxz[2], int labelyz[2],
+ int wcsyz[2], int *status ) {
+/*
+* Name:
+* StoreAxisInfo
+
+* Purpose:
+* Store information connecting 3D axis with associuated 2D Plot axes.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void StoreAxisInfo( AstPlot3D *this, int labelxy[2], int wcsxy[2],
+* int labelxz[2], int wcsxz[2], int labelyz[2],
+* int wcsyz[2], int *status )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function stores information inside the Plot3D that allows each
+* 3D axis to be associated with the 2 Plots that share the 3D axis,
+* and with the axis index within each of these two Plots.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* labelxy
+* Flags indicating if the two axes of the current Frame in the XY
+* Plot should be labelled or not.
+* wcsxy
+* The zero based axis index within the 3D current Frame that
+* corresponds to each of the two current Frame axes in the XY Plot.
+* A value of -1 should be used for 2D axes that do not correspond
+* to any 3D axis.
+* labelxz
+* Flags indicating if the two axes of the current Frame in the XZ
+* Plot should be labelled or not.
+* wcsxz
+* The zero based axis index within the 3D current Frame that
+* corresponds to each of the two current Frame axes in the XZ Plot.
+* A value of -1 should be used for 2D axes that do not correspond
+* to any 3D axis.
+* labelyz
+* Flags indicating if the two axes of the current Frame in the YZ
+* Plot should be labelled or not.
+* wcsyz
+* The zero based axis index within the 3D current Frame that
+* corresponds to each of the two current Frame axes in the YZ Plot.
+* A value of -1 should be used for 2D axes that do not correspond
+* to any 3D axis.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ int axis2d;
+ int axis3d;
+ int gotfirst;
+ int gotsecond;
+ int temp;
+
+/* Check the inherited status. */
+ if( !astOK ) return;
+
+/* Store information that allows each 3D WCS axis to be associated with
+ a pair of Plots. Also store the WCS axis within each Plot that
+ corresponds to the 3D WCS axis. Do each 3D WCS axis in turn. */
+ for( axis3d = 0; axis3d < 3; axis3d++ ) {
+
+/* Indicate we have not yet found either of the two Plots that share the
+ current 3D axis. */
+ gotfirst = 0;
+ gotsecond = 0;
+
+/* Check each of the 2 axes in the plot spanning the XY face of the 3D
+ graphics cube. Break early if we have found both Plots for the current
+ 3D WCS axis. */
+ for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) {
+
+/* See if this 2D axis corresponds to the required 3D axis. If so, store
+ the Plot identifier and the 2D axis index. */
+ if( wcsxy[ axis2d ] == axis3d ) {
+ if( gotfirst ) {
+ this->axis_plot2[ axis3d ] = XY;
+ this->axis_index2[ axis3d ] = axis2d;
+ gotsecond = 1;
+ } else {
+ this->axis_plot1[ axis3d ] = XY;
+ this->axis_index1[ axis3d ] = axis2d;
+ gotfirst = 1;
+ }
+ }
+ }
+
+/* Check the plot spanning the XZ face in the same way. */
+ for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) {
+ if( wcsxz[ axis2d ] == axis3d ) {
+ if( gotfirst ) {
+ this->axis_plot2[ axis3d ] = XZ;
+ this->axis_index2[ axis3d ] = axis2d;
+ gotsecond = 1;
+ } else {
+ this->axis_plot1[ axis3d ] = XZ;
+ this->axis_index1[ axis3d ] = axis2d;
+ gotfirst = 1;
+ }
+ }
+ }
+
+/* Check the plot spanning the YZ face in the same way. */
+ for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) {
+ if( wcsyz[ axis2d ] == axis3d ) {
+ if( gotfirst ) {
+ this->axis_plot2[ axis3d ] = YZ;
+ this->axis_index2[ axis3d ] = axis2d;
+ gotsecond = 1;
+ } else {
+ this->axis_plot1[ axis3d ] = YZ;
+ this->axis_index1[ axis3d ] = axis2d;
+ gotfirst = 1;
+ }
+ }
+ }
+ }
+
+/* Ensure that the first Plot within each pair is the one that is used to
+ generate labels for the 3D axis. */
+ for( axis2d = 0; axis2d < 2; axis2d++ ) {
+ if( labelxy[ axis2d ] ) {
+ axis3d = wcsxy[ axis2d ];
+ if( this->axis_plot1[ axis3d ] != XY ){
+ temp = this->axis_plot1[ axis3d ];
+ this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ];
+ this->axis_plot2[ axis3d ] = temp;
+
+ temp = this->axis_index1[ axis3d ];
+ this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ];
+ this->axis_index1[ axis3d ] = temp;
+ }
+ }
+ }
+
+ for( axis2d = 0; axis2d < 2; axis2d++ ) {
+ if( labelxz[ axis2d ] ) {
+ axis3d = wcsxz[ axis2d ];
+ if( this->axis_plot1[ axis3d ] != XZ ){
+ temp = this->axis_plot1[ axis3d ];
+ this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ];
+ this->axis_plot2[ axis3d ] = temp;
+
+ temp = this->axis_index1[ axis3d ];
+ this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ];
+ this->axis_index1[ axis3d ] = temp;
+ }
+ }
+ }
+
+ for( axis2d = 0; axis2d < 2; axis2d++ ) {
+ if( labelyz[ axis2d ] ) {
+ axis3d = wcsyz[ axis2d ];
+ if( this->axis_plot1[ axis3d ] != YZ ){
+ temp = this->axis_plot1[ axis3d ];
+ this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ];
+ this->axis_plot2[ axis3d ] = temp;
+
+ temp = this->axis_index1[ axis3d ];
+ this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ];
+ this->axis_index1[ axis3d ] = temp;
+ }
+ }
+ }
+}
+
+static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* TestAttrib
+
+* Purpose:
+* Test if a specified attribute value is set for a Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* int TestAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the astTestAttrib protected
+* method inherited from the Plot class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* a value has been set for one of a Plot3D's attributes.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if a value has been set, otherwise zero.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstPlot *plot; /* Pointer to specific Plot */
+ AstPlot3D *this; /* Pointer to the Plot3D structure */
+ char attname[50]; /* Plot attribute base name */
+ char patt[50]; /* Plot attribute full name */
+ char spec[10]; /* Plane specification */
+ int axis; /* Axis index */
+ int len; /* Length of attrib string */
+ int nc; /* Number of character read */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Obtain the length of the attrib string. */
+ len = strlen( attrib );
+
+/* Check the attribute name and test the appropriate attribute. */
+
+/* Norm. */
+/* ----- */
+ if ( !strcmp( attrib, "norm" ) ) {
+ result = astTestNorm( this, 0 ) ||
+ astTestNorm( this, 1 ) ||
+ astTestNorm( this, 2 );
+
+/* Norm(axis). */
+/* ----------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) )
+ && ( nc >= len ) ) {
+ result = astTestNorm( this, axis - 1 );
+
+/* RootCorner. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "rootcorner" ) ) {
+ result = astTestRootCorner( this );
+
+
+/* ..._XY etc */
+/* ---------- */
+ } else if ( nc = 0,
+ ( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec,
+ &nc ) ) ) {
+
+ if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
+ plot = this->plotxy;
+ } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
+ plot = this->plotyz;
+ } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
+ plot = this->plotxz;
+ } else {
+ plot = NULL;
+ }
+
+ if( plot ) {
+ sprintf( patt, "%s%s", attname, attrib + nc );
+ result = astTestAttrib( plot, patt );
+
+ } else {
+ result = (*parent_testattrib)( this_object, attrib, status );
+ }
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_testattrib)( this_object, attrib, status );
+ }
+
+/* Return the result, */
+ return result;
+}
+
+static void Text( AstPlot *this_plot, const char *text, const double pos[],
+ const float up[], const char *just, int *status ){
+/*
+* Name:
+* Text
+
+* Purpose:
+* Draw a text string for a Plot3D.
+
+* Type:
+* Private member function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void Text( AstPlot *this, const char *text, const double pos[],
+* const float up[], const char *just, int *status )
+
+* Class Membership:
+* Plot3D method (overrides the Text method inherited form the Plot
+* class).
+
+* Description:
+* This function draws a string of text at a position specified in
+* the physical coordinate system of a Plot3D. The physical position
+* is transformed into graphical coordinates to determine where the
+* text should appear within the plotting area.
+*
+* The text is drawn on a 2D plane that has a normal vector given by the
+* current value of the Plot3D's "Norm" attribute.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* text
+* Pointer to a null-terminated character string containing the
+* text to be drawn. Trailing white space is ignored.
+* pos
+* An array, with one element for each axis of the Plot3D, giving
+* the physical coordinates of the point where the reference
+* position of the text string is to be placed.
+* up
+* An array holding the components of a vector in the "up"
+* direction of the text (in graphical coordinates). For
+* example, to get horizontal text, the vector {0.0f,1.0f} should
+* be supplied. "Up" is taken to be the projection of the positive
+* Z axis onto the plane specified by the current value of the
+* just
+* Pointer to a null-terminated character string identifying the
+* reference point for the text being drawn. The first character in
+* this string identifies the reference position in the "up" direction
+* and may be "B" (baseline), "C" (centre), "T" (top) or "M" (bottom).
+* The second character identifies the side-to-side reference position
+* and may be "L" (left), "C" (centre) or "R" (right ). The string is
+* case-insensitive, and only the first two characters are significant.
+*
+* For example, a value of "BL" means that the left end of the
+* baseline of the original (un-rotated) text is to be drawn at the
+* position given by "pos".
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstMapping *mapping; /* Pointer to graphics->physical mapping */
+ AstPlot3D *this; /* Pointer to the Plot3D structure */
+ AstPointSet *pset1; /* PointSet holding physical positions */
+ AstPointSet *pset2; /* PointSet holding graphics positions */
+ char *ltext; /* Local copy of "text" excluding trailing spaces */
+ char ljust[3]; /* Upper case copy of "just" */
+ const char *class; /* Object class */
+ const char *method; /* Current method */
+ const double **ptr1; /* Pointer to physical positions */
+ double **ptr2; /* Pointer to graphics positions */
+ float norm[ 3 ]; /* Single precision normal vector */
+ float ref[ 3 ]; /* Single precision ref position */
+ int axis; /* Axis index */
+ int escs; /* Original astEscapes value */
+ int naxes; /* No. of axes in the base Frame */
+ int ncoord; /* No. of axes in the current Frame */
+ int ulen; /* Length of "text" excluding trailing spaces */
+
+/* Check the global error status. */
+ if ( !astOK || !text ) return;
+
+/* Store a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_plot;
+
+/* Store the current method and class for inclusion in error messages
+ generated by lower level functions. */
+ method = "astText";
+ class = astClass( this );
+
+/* Check the base Frame of the Plot is 3-D. */
+ naxes = astGetNin( this );
+ if( naxes != 3 && astOK ){
+ astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base "
+ "Frame of the supplied %s is invalid - this number should "
+ "be 3.", status, method, class, naxes, class );
+ }
+
+/* Ensure AST functions do not included graphical escape sequences in any
+ returned text strings. This is because the Plot3D implementation of
+ this method cannot currently handle graphical escape sequences. */
+ escs = astEscapes( 0 );
+
+/* Establish the correct graphical attributes as defined by attributes
+ with the supplied Plot. */
+ astGrfAttrs( this, AST__TEXT_ID, 1, GRF__TEXT, method, class );
+
+/* Get the number of coordinates in the physical coordinate frame. */
+ ncoord = astGetNout( this );
+
+/* Create a PointSet to hold the supplied physical coordinates. */
+ pset1 = astPointSet( 1, ncoord, "", status );
+
+/* Allocate memory to hold pointers to the first value on each axis. */
+ ptr1 = (const double **) astMalloc( sizeof( const double * )*
+ (size_t)( ncoord ));
+
+/* Check the pointer can be used, then store pointers to the first value
+ on each axis. */
+ if( astOK ){
+ for( axis = 0; axis < ncoord; axis++ ){
+ ptr1[ axis ] = pos + axis;
+ }
+ }
+
+/* Store these pointers in the PointSet. */
+ astSetPoints( pset1, (double **) ptr1 );
+
+/* Transform the supplied data from the current frame (i.e. physical
+ coordinates) to the base frame (i.e. graphics coordinates) using
+ the inverse Mapping defined by the Plot. */
+ mapping = astGetMapping( this, AST__BASE, AST__CURRENT );
+ pset2 = astTransform( mapping, pset1, 0, NULL );
+ mapping = astAnnul( mapping );
+
+/* Get pointers to the graphics coordinates. */
+ ptr2 = astGetPoints( pset2 );
+
+/* Take a copy of the string excluding any trailing white space. */
+ ulen = astChrLen( text );
+ ltext = (char *) astStore( NULL, (void *) text, ulen + 1 );
+
+/* Check the pointers can be used. */
+ if( astOK ){
+
+/* Terminate the local copy of the text string. */
+ ltext[ ulen ] = 0;
+
+/* Produce an upper-case copy of the first two characters in "just". */
+ ljust[0] = (char) toupper( (int) just[0] );
+ ljust[1] = (char) toupper( (int) just[1] );
+ ljust[2] = 0;
+
+/* Convert the double precision values to single precision, checking for
+ bad positions. */
+ if( ptr2[0][0] != AST__BAD && ptr2[1][0] != AST__BAD &&
+ ptr2[2][0] != AST__BAD ){
+ ref[ 0 ] = (float) ptr2[0][0];
+ ref[ 1 ] = (float) ptr2[1][0];
+ ref[ 2 ] = (float) ptr2[2][0];
+
+/* If the nornmal vector has non-zero length, draw the text. */
+ norm[ 0 ] = (float) astGetNorm( this, 0 );
+ norm[ 1 ] = (float) astGetNorm( this, 1 );
+ norm[ 2 ] = (float) astGetNorm( this, 2 );
+
+/* Since we are about to call an external function which may not be
+ thread safe, prevent any other thread from executing the following code
+ until the current thread has finished executing it. */
+ LOCK_MUTEX2;
+
+ if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 || norm[ 2 ] != 0.0 ){
+ if( !astG3DText( ltext, ref, ljust, (float *) up, norm ) ) {
+ astError( AST__GRFER, "%s(%s): Graphics error in astG3DText. ", status,
+ method, class );
+ }
+ } else if( astOK ) {
+ astError( AST__ATTIN, "%s(%s): The vector specified by the Norm "
+ "attribute has zero length.", status, method, class );
+ }
+
+/* Allow the next thread to proceed. */
+ UNLOCK_MUTEX2;
+ }
+
+/* Free the local copy of the string. */
+ ltext = (char *) astFree( (void *) ltext );
+
+ }
+
+/* Annul the PointSets. */
+ pset1 = astAnnul( pset1 );
+ pset2 = astAnnul( pset2 );
+
+/* Free the memory holding the pointers to the first value on each axis. */
+ ptr1 = (const double **) astFree( (void *) ptr1 );
+
+/* Re-establish the original graphical attributes. */
+ astGrfAttrs( this, AST__TEXT_ID, 0, GRF__TEXT, method, class );
+
+/* Restore the original value of the flag which says whether graphical
+ escape sequences should be incldued in any returned text strings. */
+ astEscapes( escs );
+
+/* Return */
+ return;
+}
+
+static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in,
+ int forward, AstPointSet *out, int *status ) {
+/*
+* Name:
+* Transform
+
+* Purpose:
+* Use a Plot to transform a set of points.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
+* int forward, AstPointSet *out, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the astTransform protected
+* method inherited from the Plot class).
+
+* Description:
+* This function takes a Plot3D and a set of points encapsulated in a
+* PointSet and transforms the points from graphics coordinates to
+* physical coordinates (in the forward direction). No clipping or
+* normalisation is applied.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* in
+* Pointer to the PointSet holding the input coordinate data.
+* forward
+* A non-zero value indicates that the forward coordinate
+* transformation should be applied while a zero value requests the
+* inverse transformation.
+* out
+* Pointer to a PointSet which will hold the transformed (output)
+* coordinate values. A NULL value may also be given, in which case a
+* new PointSet will be created by this function.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the output (possibly new) PointSet.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+* - The number of coordinate values per point in the input PointSet must
+* match the number of coordinates for the Plot being applied.
+* - If an output PointSet is supplied, it must have space for sufficient
+* number of points and coordinate values per point to accommodate the
+* result. Any excess space will be ignored.
+*/
+
+/* Local Variables: */
+ AstMapping *map; /* Pointer to the mapping */
+ AstPointSet *result; /* Positions in output Frame */
+ AstPlot3D *this; /* The Plot3D */
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Get a pointer to the Plot3D. */
+ this = (AstPlot3D *) this_mapping;
+
+/* Get the Mapping from the base to the current Frame. */
+ map = astGetMapping( this, AST__BASE, AST__CURRENT );
+
+/* Do the transformation. */
+ result = astTransform( map, in, forward, out );
+
+/* Annul the mapping. */
+ map = astAnnul( map );
+
+/* If an error has occurred, annul the result. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return a pointer to the output PointSet. */
+ return result;
+
+}
+
+static void UpdatePlots( AstPlot3D *this, int *status ) {
+/*
+* Name:
+* UpdatePlots
+
+* Purpose:
+* Update the three 2D plots stored in the Plot3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "plot3d.h"
+* void UpdatePlots( AstPlot3D *this )
+
+* Class Membership:
+* Plot3D method.
+
+* Description:
+* This function splits the parent FrameSet up into 3 independent 2D
+* FrameSets, each describing a 2D plane in the supplied 3D FrameSet.
+* It then uses these 2D FrameSets to update the three Plots in the
+* Plot3D, by removing the existing current Frame and adding in the
+* new current Frame with the associated Mapping.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+
+* Notes:
+* - Each of the 3 plots has 3 Frames: Frame 1 is the base (GRAPHICS)
+* Frame; Frame 2 is spanned by 2 of the 3 axes in the base Frame of
+* the supplied FrameSet; Frame 3 is the current Frame and is spanned
+* by 2 of the 3 axes in the current Frame of the supplied FrameSet.
+* Any future changes to this function that alter this structure should
+* reflected in equivalewnt changes to function CreatePlots.
+*/
+
+/* Local Variables: */
+ AstFrame *frm;
+ AstFrameSet *fsetxy;
+ AstFrameSet *fsetxz;
+ AstFrameSet *fsetyz;
+ AstFrameSet *fset;
+ AstMapping *map;
+ int baseplane;
+ int labelxy[ 2 ];
+ int labelxz[ 2 ];
+ int labelyz[ 2 ];
+ int wcsxy[ 2 ];
+ int wcsxz[ 2 ];
+ int wcsyz[ 2 ];
+ int rootcorner;
+
+/* Check the inherited status. */
+ if( !astOK ) return;
+
+/* Return without action if the Plot3D does not contain the three 2D
+ Plots. This may be the case for instance if this function is called
+ before the Plot3D is fully constructed. */
+ if( this->plotxy && this->plotxz && this->plotyz ){
+
+/* We need a FrameSet that is equivalent to the one that was used to
+ construct the Plot3D (i.e. a FrameSet that does not have the GRAPHICS
+ Frame that was added by the Plot3D constructor). So take a copy of the
+ parent FrameSet, remove the GRAPHICS Frame (Frame 1) and set the base
+ Frame to be the Frame that was the base Frame when the Plot3D was
+ constructed. */
+ fset = (AstFrameSet *) astCast( this, dummy_frameset );
+ astSetBase( fset, this->pix_frame );
+ astRemoveFrame( fset, 1 );
+
+/* Split the supplied FrameSet up into 3 FrameSets, each with a 2D base
+ and current Frame. Each of these FrameSets describes one plane of
+ the 3D cube. */
+ SplitFrameSet( fset, &fsetxy, labelxy, wcsxy, &fsetxz, labelxz, wcsxz,
+ &fsetyz, labelyz, wcsyz, &baseplane, status );
+
+/* First do the XY Plot. Extract the current Frame and base->current
+ Mapping from the new FrameSet. It is assumed that he base Frame in the
+ new FrameSet is equivalent to Frame 2 in the existing Plot. This
+ assumption is based on the way the CreatePlots and SplitFrameSet
+ functions work, and should be reviewed if either of these functions
+ is changed. */
+ frm = astGetFrame( fsetxy, 2 );
+ map = astGetMapping( fsetxy, 1, 2 );
+
+/* Add the new Frame into the existing Plot using the Mapping to connect
+ it to Frame 2 in the Plot. It becomes the current Frame in the Plot. */
+ astAddFrame( this->plotxy, 2, map, frm );
+
+/* Delete the original current Frame in the Plot since it is not needed
+ any more. */
+ astRemoveFrame( this->plotxy, 3 );
+
+/* Set the Plot attributes. */
+ SetPlotAttr( this->plotxy, XY, labelxy, status );
+
+/* Free resources */
+ fsetxy = astAnnul( fsetxy );
+ map = astAnnul( map );
+ frm = astAnnul( frm );
+
+/* Do the same for the XZ plot. */
+ frm = astGetFrame( fsetxz, 2 );
+ map = astGetMapping( fsetxz, 1, 2 );
+ astAddFrame( this->plotxz, 2, map, frm );
+ astRemoveFrame( this->plotxz, 3 );
+ SetPlotAttr( this->plotxz, XZ, labelxz, status );
+ fsetxz = astAnnul( fsetxz );
+ map = astAnnul( map );
+ frm = astAnnul( frm );
+
+/* Do the same for the YZ plot. */
+ frm = astGetFrame( fsetyz, 2 );
+ map = astGetMapping( fsetyz, 1, 2 );
+ astAddFrame( this->plotyz, 2, map, frm );
+ astRemoveFrame( this->plotyz, 3 );
+ SetPlotAttr( this->plotyz, YZ, labelyz, status );
+ fsetyz = astAnnul( fsetyz );
+ map = astAnnul( map );
+ frm = astAnnul( frm );
+
+/* Store information that allows each 3D WCS axis to be associatedf with
+ a pair of Plots. Also store the WCS axis within each Plot that
+ corresponds to the 3D WCS axis. */
+ StoreAxisInfo( this, labelxy, wcsxy, labelxz, wcsxz, labelyz, wcsyz, status );
+
+/* Set up the Edges attributes in the encapsulated Plots to produce labels
+ on the required edges of the 3D graphics cube. */
+ rootcorner = astGetRootCorner( this );
+ ChangeRootCorner( this, rootcorner, rootcorner, status );
+
+/* Store the plane spanned by two connected 3D axes. */
+ this->baseplot = baseplane;
+
+/* Free remaining resources */
+ fset = astAnnul( fset );
+ }
+}
+
+static void VSet( AstObject *this_object, const char *settings,
+ char **text, va_list args, int *status ) {
+/*
+* Name:
+* VSet
+
+* Purpose:
+* Set values for a Plot3D's attributes.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "frameset.h"
+* void VSet( AstObject *this, const char *settings, char **text,
+* va_list args, int *status )
+
+* Class Membership:
+* Plot3D member function (over-rides the protected astVSet
+* method inherited from the Object class).
+
+* Description:
+* This function assigns a set of attribute values for a Plot3D,
+* the attributes and their values being specified by means of a
+* string containing a comma-separated list of the form:
+*
+* "attribute1 = value1, attribute2 = value2, ... "
+*
+* Here, "attribute" specifies an attribute name and the value to
+* the right of each "=" sign should be a suitable textual
+* representation of the value to be assigned to that attribute. This
+* will be interpreted according to the attribute's data type.
+*
+* The string supplied may also contain "printf"-style format
+* specifiers identified by a "%" sign in the usual way. If
+* present, these will be substituted by values supplied as
+* optional arguments (as a va_list variable argument list), using
+* the normal "printf" rules, before the string is used.
+
+* Parameters:
+* this
+* Pointer to the Plot3D.
+* settings
+* Pointer to a null-terminated string containing a
+* comma-separated list of attribute settings.
+* text
+* Pointer to a location at which to return a pointer to dynamic
+* memory holding a copy of the expanded setting string. This memory
+* should be freed using astFree when no longer needed. If a NULL
+* pointer is supplied, no string is created.
+* args
+* The variable argument list which contains values to be
+* substituted for any "printf"-style format specifiers that
+* appear in the "settings" string.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This function preserves the integrity of the Plot3D (if
+* possible) by appropriately modifying the three encapsulated Plots.
+*/
+
+/* Invoke the parent astVSet method to set the Plot3D's attribute values. */
+ (*parent_vset)( this_object, settings, text, args, status );
+
+/* Update the three 2D Plots stored in the Plot3D structure so that they
+ reflect this modified FrameSet. */
+ UpdatePlots( (AstPlot3D *) this_object, status );
+}
+
+/* Functions which access Plot3D class attributes. */
+/* ----------------------------------------------- */
+
+/* RootCorner. */
+/* ----------- */
+/*
+*att++
+* Name:
+* RootCorner
+
+* Purpose:
+* Specifies which edges of the 3D box should be annotated.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* String.
+
+* Description:
+* This attribute controls the appearance of an annotated
+c coordinate grid (drawn with the astGrid function) by determining
+f coordinate grid (drawn with the AST_GRID routine) by determining
+* which edges of the cube enclosing the 3D graphics space are used
+* for displaying numerical and descriptive axis labels. The attribute
+* value identifies one of the eight corners of the cube within
+* which graphics are being drawn (i.e. the cube specified by the
+c "graphbox" parameter when astPlot3D
+f GRAPHBOX argument when AST_PLOT3D
+* was called tp create the Plot3D). Axis labels and tick marks will
+* be placed on the three cube edges that meet at the given corner.
+*
+* The attribute value should consist of three character, each of
+* which must be either "U" or "L". The first character in the string
+* specifies the position of the corner on the first graphics axis.
+* If the character is "U" then the corner is at the upper bound on the
+* first graphics axis. If it is "L", then the corner is at the lower
+* bound on the first axis. Likewise, the second and third characters
+* in the string specify the location of the corner on the second and
+* third graphics axes.
+*
+* For instance, corner "LLL" is the corner that is at the lower bound
+* on all three graphics axes, and corner "ULU" is at the upper bound
+* on axes 1 and 3 but at the lower bound on axis 2.
+*
+* The default value is "LLL".
+
+* Applicability:
+* Plot3D
+* All Plot3Ds have this attribute.
+
+*att--
+*/
+
+/* Internally, the RootCorner is represented as an integer bit mask, with
+ bit zero for the X axis, bit 1 for the Y axis and bit 2 for the Z axis.
+ A bit is set if the corner is at the upper bound on the axis and unset
+ if it is at the lower bound. A value of -1 indicates that the attribue
+ has not been assigned a value. */
+astMAKE_GET(Plot3D,RootCorner,int,0,( this->rootcorner == -1 ? 0 : this->rootcorner))
+astMAKE_TEST(Plot3D,RootCorner,( this->rootcorner != -1 ))
+
+
+/* Norm(axis). */
+/* ----------- */
+/*
+*att++
+* Name:
+* Norm(axis)
+
+* Purpose:
+* Specifies the plane upon which a Plot3D draws text and markers.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Floating point.
+
+* Description:
+* This attribute controls the appearance of text and markers drawn
+* by a Plot3D. It specifies the orientation of the plane upon which
+* text and markers will be drawn by all subsequent invocations of the
+c astText and astMark functions.
+f AST_TEXT and AST_MARK functions.
+*
+* When setting or getting the Norm attribute, the attribute name must
+* be qualified by an axis index in the range 1 to 3. The 3 elements of
+* the Norm attribute are together interpreted as a vector in 3D graphics
+* coordinates that is normal to the plane upon which text and marks
+* should be drawn. When testing or clearing the attribute, the axis
+* index is optional. If no index is supplied, then clearing the Norm
+* attribute will clear all three elements, and testing the Norm attribute
+* will return a non-zero value if any of the three elements are set.
+*
+* The default value is 1.0 for each of the 3 elements. The length of
+* the vector is insignificant, but an error will be reported when
+* attempting to draw text or markers if the vector has zero length.
+
+* Applicability:
+* Plot
+* All Plot3Ds have this attribute.
+
+*att--
+*/
+MAKE_CLEAR3(Norm,norm,AST__BAD,3)
+MAKE_SET3(Norm,double,norm,value,3)
+MAKE_TEST3(Norm,( this->norm[axis] != AST__BAD ),3)
+MAKE_GET3(Norm,double,AST__BAD,(this->norm[axis]!=AST__BAD?this->norm[axis]:1.0),3)
+
+
+
+/* Functions which access Plot class attributes. */
+/* --------------------------------------------- */
+
+/* First do axis specific attributes. */
+
+#define MAKE_ALL(attr,type,badval,whichplots) \
+ MAKE_CLEAR(attr,whichplots) \
+ MAKE_SET(attr,type,whichplots) \
+ MAKE_GET(attr,type,badval )
+
+MAKE_ALL(MinTick,int,0,0)
+MAKE_ALL(Abbrev,int,0,1)
+MAKE_ALL(Gap,double,AST__BAD,1)
+MAKE_ALL(LogGap,double,AST__BAD,1)
+MAKE_ALL(LogPlot,int,0,0)
+MAKE_ALL(LogTicks,int,0,0)
+MAKE_ALL(LogLabel,int,0,1)
+MAKE_ALL(LabelUp,int,0,1)
+MAKE_ALL(DrawAxes,int,0,0)
+MAKE_ALL(LabelUnits,int,0,1)
+MAKE_ALL(MinTickLen,double,0.0,0)
+MAKE_ALL(MajTickLen,double,0.0,0)
+MAKE_ALL(NumLab,int,0,1)
+MAKE_ALL(NumLabGap,double,AST__BAD,1)
+MAKE_ALL(TextLab,int,0,1)
+MAKE_ALL(TextLabGap,double,AST__BAD,1)
+
+#undef MAKE_ALL
+
+
+/* Now do attributes that are not axis specific. */
+
+#define MAKE_ALL(attr,type) \
+ MAKE_CLEAR1(attr) \
+ MAKE_SET1(attr,type)
+
+MAKE_ALL(Ink,int)
+MAKE_ALL(Tol,double)
+MAKE_ALL(Invisible,int)
+MAKE_ALL(TickAll,int)
+MAKE_ALL(ForceExterior,int)
+MAKE_ALL(Border,int)
+MAKE_ALL(Clip,int)
+MAKE_ALL(ClipOp,int)
+MAKE_ALL(Escape,int)
+MAKE_ALL(Grid,int)
+MAKE_ALL(Labelling,int)
+
+#undef MAKE_ALL
+
+
+
+/* First do element-specific attributes. */
+
+#define MAKE_ALL(attr,type) \
+ MAKE_CLEAR2(attr) \
+ MAKE_SET2(attr,type)
+
+MAKE_ALL(Style,int)
+MAKE_ALL(Font,int)
+MAKE_ALL(Colour,int)
+MAKE_ALL(Width,double)
+MAKE_ALL(Size,double)
+
+#undef MAKE_ALL
+
+
+
+
+
+/* Copy constructor. */
+/* ----------------- */
+static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
+/*
+* Name:
+* Copy
+
+* Purpose:
+* Copy constructor for Plot3D objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Copy( const AstObject *objin, AstObject *objout, int *status )
+
+* Description:
+* This function implements the copy constructor for Plot3D objects.
+
+* Parameters:
+* objin
+* Pointer to the object to be copied.
+* objout
+* Pointer to the object being constructed.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This constructor makes a deep copy.
+*/
+
+/* Local Variables: */
+ AstPlot3D *in; /* Pointer to input Plot3D */
+ AstPlot3D *out; /* Pointer to output Plot3D */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain pointers to the input and output Plot3Ds. */
+ in = (AstPlot3D *) objin;
+ out = (AstPlot3D *) objout;
+
+/* Nullify the pointers stored in the output object since these will
+ currently be pointing at the input data (since the output is a simple
+ byte-for-byte copy of the input). Otherwise, the input data could be
+ freed by accidient if the output object is deleted due to an error
+ occuring in this function. */
+ out->plotxy = NULL;
+ out->plotxz = NULL;
+ out->plotyz = NULL;
+
+/* Copy the encapsulated Plots */
+ if( in->plotxy ) out->plotxy = astCopy( in->plotxy );
+ if( in->plotxz ) out->plotxz = astCopy( in->plotxz );
+ if( in->plotyz ) out->plotyz = astCopy( in->plotyz );
+
+/* If an error has occurred, free the output resources. */
+ if( !astOK ) Delete( (AstObject *) out, status );
+
+}
+
+/* Destructor. */
+/* ----------- */
+static void Delete( AstObject *obj, int *status ) {
+/*
+* Name:
+* Delete
+
+* Purpose:
+* Destructor for Plot3D objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Delete( AstObject *obj, int *status )
+
+* Description:
+* This function implements the destructor for Plot3D objects.
+
+* Parameters:
+* obj
+* Pointer to the object to be deleted.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* This function attempts to execute even if the global error status is
+* set.
+*/
+
+/* Local Variables: */
+ AstPlot3D *this;
+
+/* Release the memory referred to in the Plot3D structure. */
+ this = (AstPlot3D *) obj;
+ if( this ) {
+ this->plotxy = astDelete( this->plotxy );
+ this->plotxz = astDelete( this->plotxz );
+ this->plotyz = astDelete( this->plotyz );
+ }
+}
+
+/* Dump function. */
+/* -------------- */
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for Plot3D objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Dump( AstObject *this, AstChannel *channel, int *status )
+
+* Description:
+* This function implements the Dump function which writes out data
+* for the Plot3D class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the Plot3D whose data are being written.
+* channel
+* Pointer to the Channel to which the data are being written.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Constants: */
+#define KEY_LEN 50 /* Maximum length of a keyword */
+
+/* Local Variables: */
+ AstPlot3D *this;
+ double dval;
+ char key[ KEY_LEN + 1 ];
+ int axis;
+ int helpful;
+ int ival;
+ int set;
+ const char *text;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Plot3D structure. */
+ this = (AstPlot3D *) this_object;
+
+/* Write out values representing the instance variables for the
+ Plot3D class. Accompany these with appropriate comment strings,
+ possibly depending on the values being written.*/
+
+/* In the case of attributes, we first use the appropriate (private)
+ Test... member function to see if they are set. If so, we then use
+ the (private) Get... function to obtain the value to be written
+ out.
+
+ For attributes which are not set, we use the astGet... method to
+ obtain the value instead. This will supply a default value
+ (possibly provided by a derived class which over-rides this method)
+ which is more useful to a human reader as it corresponds to the
+ actual default attribute value. Since "set" will be zero, these
+ values are for information only and will not be read back. */
+
+
+/* Norm. */
+/* ----- */
+ for ( axis = 0; axis < 3; axis++ ) {
+ dval = astGetNorm( this, axis );
+ helpful = ( dval != -DBL_MAX );
+ (void) sprintf( key, "Norm%d", axis + 1 );
+ astWriteDouble( channel, key, 0, helpful, dval, "Text plane normal vector");
+ }
+
+/* RootCorner. */
+/* ----------- */
+ set = TestRootCorner( this, status );
+ ival = set ? GetRootCorner( this, status ) : astGetRootCorner( this );
+ text = RootCornerString( ival, status );
+ if( text ) {
+ astWriteString( channel, "RootCn", set, 1, text, "Corner where labelled axes meet" );
+ } else if( astOK ) {
+ astError( AST__INTER, "astDump(Plot3D): Illegal value %d found for "
+ "RootCorner attribute (interbal AST programming error).", status,
+ ival );
+ }
+
+/* Labelled axes */
+ astWriteInt( channel, "AxPlX1", 1, 1, this->axis_plot1[0],
+ "Plot used to label the 3D X axis" );
+ astWriteInt( channel, "AxPlY1", 1, 1, this->axis_plot1[1],
+ "Plot used to label the 3D Y axis" );
+ astWriteInt( channel, "AxPlZ1", 1, 1, this->axis_plot1[2],
+ "Plot used to label the 3D Z axis" );
+ astWriteInt( channel, "AxInX1", 1, 1, this->axis_index1[0],
+ "Plot axis index used to label the 3D X axis" );
+ astWriteInt( channel, "AxInY1", 1, 1, this->axis_index1[1],
+ "Plot axis index used to label the 3D Y axis" );
+ astWriteInt( channel, "AxInZ1", 1, 1, this->axis_index1[2],
+ "Plot axis index used to label the 3D Z axis" );
+
+/* Unlabelled axes */
+ astWriteInt( channel, "AxPlX2", 1, 1, this->axis_plot2[0],
+ "Other Plot touching the 3D X axis" );
+ astWriteInt( channel, "AxPlY2", 1, 1, this->axis_plot2[1],
+ "Other Plot touching the 3D Y axis" );
+ astWriteInt( channel, "AxPlZ2", 1, 1, this->axis_plot2[2],
+ "Other Plot touching the 3D Z axis" );
+ astWriteInt( channel, "AxInX2", 1, 1, this->axis_index2[0],
+ "Other Plot axis index touching the 3D X axis" );
+ astWriteInt( channel, "AxInY2", 1, 1, this->axis_index2[1],
+ "Other Plot axis index touching the 3D Y axis" );
+ astWriteInt( channel, "AxInZ2", 1, 1, this->axis_index2[2],
+ "Other Plot axis index touching the 3D Z axis" );
+
+/* The Plot that spans the two connected axes. */
+ astWriteInt( channel, "BasePl", 1, 1, this->baseplot,
+ "Plot spanning two connected 3D axes" );
+
+/* XY Plot */
+ astWriteObject( channel, "PlotXY", 1, 1, this->plotxy,
+ "Plot describing the XY plane" );
+
+/* XZ Plot */
+ astWriteObject( channel, "PlotXZ", 1, 1, this->plotxz,
+ "Plot describing the XZ plane" );
+
+/* YZ Plot */
+ astWriteObject( channel, "PlotYZ", 1, 1, this->plotyz,
+ "Plot describing the YZ plane" );
+
+/* The index within the Plot3D FrameSet, of the original base Frame in
+ the FrameSet supplied when the Plot3D was constructed. */
+ astWriteInt( channel, "PixFrm", 1, 0, this->pix_frame,
+ "Index of original base Frame" );
+
+/* Undefine macros local to this function. */
+#undef KEY_LEN
+}
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsAPlot3D and astCheckPlot3D functions using the
+ macros defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(Plot3D,Plot)
+astMAKE_CHECK(Plot3D)
+
+
+AstPlot3D *astPlot3D_( void *frame_void, const float *graphbox,
+ const double *basebox, const char *options, int *status, ...) {
+/*
+*+
+* Name:
+* astPlot3D
+
+* Purpose:
+* Create a Plot3D.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstPlot3D *astPlot3D( AstFrame *frame, const float *graphbox,
+* const double *basebox, const char *options, ..., int *status )
+
+* Class Membership:
+* Plot3D constructor.
+
+* Description:
+* This function creates a new Plot3D and optionally initialises its
+* attributes.
+*
+* The supplied Frame (or the base frame if a FrameSet was supplied) is
+* assumed to be related to the graphics world coordinate system by a
+* simple shift and scale along each axis. The mapping between graphics
+* world coordinates and this Frame is specified by supplying the
+* coordinates in both systems at the lower bounds and upper bounds corners
+* of a box on the graphics device. By default, no graphics will be
+* produced outside the supplied box, but this default behaviour can be
+* changed by setting explicit values for the various clipping attributes.
+
+* Parameters:
+* frame
+* A pointer to a Frame or FrameSet to be annotated. If a NULL pointer
+* is supplied, then a default 3-D Frame will be created to which labels,
+* etc, can be attached by setting the relevant Frame attributes.
+* graphbox
+* A pointer to an array of 6 values giving the graphics world
+* coordinates of the lower bounds and upper bound corners of a box on
+* the graphics output device. The first triple of values should be the
+* coordinates of the lower bounds corner of the box and the second
+* triple of values should be the coordinates of the upper bounds corner.
+* basebox
+* A pointer to an array of 6 values giving the coordinates in the
+* supplied Frame, or base frame of the supplied FrameSet, at the
+* lower bounds corner and upper bounds corners of the box specified
+* by parameter graphbox. These should be supplied in the same order as
+* for parameter "graphbox".
+* options
+* Pointer to a null terminated string containing an optional
+* comma-separated list of attribute assignments to be used for
+* initialising the new Plot3D. The syntax used is the same as for the
+* astSet method and may include "printf" format specifiers identified
+* by "%" symbols in the normal way.
+* status
+* Pointer to the inherited status variable.
+* ...
+* If the "options" string contains "%" format specifiers, then an
+* optional list of arguments may follow it in order to supply values to
+* be substituted for these specifiers. The rules for supplying these
+* are identical to those for the astSet method (and for the C "printf"
+* function).
+
+* Returned Value:
+* A pointer to the new Plot3D.
+
+* Notes:
+* - A NULL pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+*-
+
+* Implementation Notes:
+* - This function implements the basic Plot3D constructor which
+* is available via the protected interface to the Plot3D class.
+* A public interface is provided by the astPlot3DId_ function.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstFrame *frame; /* Pointer to Frame structure */
+ AstPlot3D *new; /* Pointer to new Plot3D */
+ va_list args; /* Variable argument list */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise variables to avoid "used of uninitialised variable"
+ messages from dumb compilers. */
+ new = NULL;
+
+/* Obtain and validate a pointer to any supplied Frame structure. */
+ if( frame_void ){
+ frame = astCheckFrame( frame_void );
+ } else {
+ frame = NULL;
+ }
+
+/* Check the pointer can be used. */
+ if ( astOK ) {
+
+/* Initialise the Plot3D, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitPlot3D( NULL, sizeof( AstPlot3D ), !class_init,
+ &class_vtab, "Plot3D", frame, graphbox,
+ basebox );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ Plot's attributes. */
+ va_start( args, status );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+ }
+
+/* Return a pointer to the new Plot. */
+ return new;
+}
+
+AstPlot3D *astInitPlot3D_( void *mem, size_t size, int init,
+ AstPlot3DVtab *vtab, const char *name,
+ AstFrame *frame, const float *graphbox,
+ const double *basebox, int *status ) {
+/*
+*+
+* Name:
+* astInitPlot3D
+
+* Purpose:
+* Initialise a Plot3D.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstPlot3D *astInitPlot3D( void *mem, size_t size, int init,
+* AstPlotVtab *vtab, const char *name,
+* AstFrame *frame, const float *graphbox,
+* const double *basebox )
+
+* Class Membership:
+* Plot3D initialiser.
+
+* Description:
+* This function is provided for use by class implementations to
+* initialise a new Plot3D object. It allocates memory (if
+* necessary) to accommodate the Plot3D plus any additional data
+* associated with the derived class. It then initialises a
+* Plot3D structure at the start of this memory. If the "init"
+* flag is set, it also initialises the contents of a virtual function
+* table for a Plot3D at the start of the memory passed via the
+* "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the Plot3D is to be
+* created. This must be of sufficient size to accommodate the
+* Plot3D data (sizeof(Plot3D)) plus any data used by
+* the derived class. If a value of NULL is given, this function
+* will allocate the memory itself using the "size" parameter to
+* determine its size.
+* size
+* The amount of memory used by the Plot3D (plus derived
+* class data). This will be used to allocate memory if a value of
+* NULL is given for the "mem" parameter. This value is also stored
+* in the Plot3D structure, so a valid value must be supplied
+* even if not required for allocating memory.
+* init
+* A logical flag indicating if the Plot3D's virtual function
+* table is to be initialised. If this value is non-zero, the
+* virtual function table will be initialised by this function.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new Plot3D.
+* name
+* Pointer to a constant null-terminated character string which
+* contains the name of the class to which the new object belongs
+* (it is this pointer value that will subsequently be returned by
+* the astGetClass method).
+* fset
+* A pointer to the Frame or Frameset to be annotated.
+* graphbox
+* A pointer to an array of 6 values giving the graphics coordinates
+* of the bottom left and top right corners of a box on the graphics
+* output device. The first triple of values should be the graphics
+* coordinates of the bottom left corner of the box and the second
+* triple of values are the graphics coordinates of the top right corner.
+* basebox
+* A pointer to an array of 6 values giving the coordinates in the
+* supplied Frame or base Frame of the supplied FrameSet at the bottom
+* left and top right corners of the box specified by parameter graphbox.
+* These should be supplied in the same order as for parameter "graphbox".
+
+* Returned Value:
+* A pointer to the new Plot3D.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+*-
+*/
+
+/* Local Variables: */
+ AstFrame *baseframe = NULL; /* Pointer to base frame */
+ AstFrame *graphicsframe = NULL; /* Pointer to graphics frame */
+ AstFrameSet *fset0 = NULL; /* The n-D FrameSet to be annotated */
+ AstFrameSet *fset = NULL; /* The 2-D FrameSet to be annotated */
+ AstMapping *map = NULL; /* Mapping for converting bbox -> gbox */
+ AstPlot3D *new = NULL; /* Pointer to new Plot3D */
+ char *mess = NULL; /* Pointer to a descriptive message */
+ double djunkbox[ 4 ] = { 0.0, 0.0, 1.0, 1.0 }; /* Dummy 2D basebox */
+ float fjunkbox[ 4 ] = { 0.0, 0.0, 1.0, 1.0 }; /* Dummy 2D graphbox */
+ int bi; /* Index of base frame */
+ int ci; /* Index of current frame */
+ int i; /* Loop count */
+ int ii; /* Loop count */
+ int naxes; /* No. of axes in frame */
+ int nfrm; /* Number of Frames in frameset */
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitPlot3DVtab( vtab, name );
+
+/* First of all we need to ensure that we have a FrameSet and a base
+ Frame on which to base the new Plot3D. If a NULL Frame pointer was
+ supplied, create a default 3-D Frame, and then create a FrameSet
+ containing just this default Frame. Also store a pointer to a
+ message which can be used to describe the object within error
+ messages. */
+ if( !frame ){
+ baseframe = astFrame( 3, "", status );
+ fset = astFrameSet( baseframe, "", status );
+ mess = "default 3-d Frame";
+
+/* If an object was supplied, report an error if it is not a Frame or
+ an object derived from a Frame (such as a FrameSet). */
+ } else if( !astIsAFrame( frame ) ){
+ if( astOK ){
+ astError( AST__BDOBJ, "astInitPlot3D(%s): Supplied Object (class '%s') "
+ "is not a Frame.", status, name, astGetClass( frame ) );
+ }
+
+/* If the supplied object is a Plot3D or an object derived from a Plot3D
+ (a Plot3D is a sort of Frame and so will pass the above test), extract a
+ FrameSet from the Plot3D, and clear the Domain attribute for any existing
+ Frames which have Domain GRAPHICS. */
+ } else if( astIsAPlot3D( frame ) ){
+ fset0 = astFrameSet( frame, "", status );
+ fset = astCopy( fset0 );
+ fset0 = astAnnul( fset0 );
+
+ for( i = 0; i < astGetNframe( fset ); i++ ) {
+ graphicsframe = astGetFrame( fset, i );
+ if( !strcmp( astGetDomain( graphicsframe ), "GRAPHICS" ) ) {
+ astClearDomain( graphicsframe );
+ }
+ graphicsframe = astAnnul( graphicsframe );
+ }
+
+ baseframe = astGetFrame( fset, astGetBase( fset ) );
+ mess = "base Frame of the supplied Plot3D";
+
+/* If the object is not a FrameSet, create a FrameSet holding the
+ supplied Frame. If the Frame is not 3D, an extra 3D Frame is
+ included in the FrameSet derived from axes 1, 2 and 3 of the supplied
+ Frame. This new Frame becomes the base Frame. */
+ } else if( !astIsAFrameSet( frame ) ){
+ fset0 = astFrameSet( frame, "", status );
+ mess = "supplied Frame";
+ fset = Fset3D( fset0, AST__BASE, status );
+ fset0 = astAnnul( fset0 );
+ baseframe = astGetFrame( fset, astGetBase( fset ) );
+
+/* If a FrameSet was supplied, ensure it has a 3D base Frame.
+ If the supplied FrameSet is not 3D, then a new base Frame is
+ inserted into it which is derived from axes 1, 2 and 3 of the
+ original base Frame. */
+ } else {
+ fset = Fset3D( (AstFrameSet *) frame, AST__BASE, status );
+ baseframe = astGetFrame( fset, astGetBase( fset ) );
+ mess = "base Frame of the supplied FrameSet";
+ }
+
+/* Check that there are 3 axes in the base frame of the FrameSet. */
+ naxes = astGetNaxes( baseframe );
+ if ( naxes != 3 && astOK ) {
+ astError( AST__NAXIN, "astInitPlot3D(%s): Number of axes (%d) in the %s "
+ "is invalid - this number should be 3.", status, name, naxes, mess );
+ }
+
+/* Check that no dimension of the graphbox is bad. */
+ if( astISBAD(graphbox[0]) || astISBAD(graphbox[1]) ||
+ astISBAD(graphbox[2]) || astISBAD(graphbox[3]) ||
+ astISBAD(graphbox[4]) || astISBAD(graphbox[5]) ) {
+ astError( AST__BADBX, "astInitPlot3D(%s): The plotting volume has undefined limits "
+ "in the graphics world coordinate system.", status, name );
+ }
+
+/* Check that no dimension of the graphbox is zero. */
+ if( ( graphbox[ 3 ] == graphbox[ 0 ] ||
+ graphbox[ 4 ] == graphbox[ 1 ] ||
+ graphbox[ 5 ] == graphbox[ 2 ] ) && astOK ){
+ astError( AST__BADBX, "astInitPlot3D(%s): The plotting volume has zero size "
+ "in the graphics world coordinate system.", status, name );
+ }
+
+/* Check that no dimension of the basebox is bad. */
+ if( astISBAD(basebox[0]) || astISBAD(basebox[1]) ||
+ astISBAD(basebox[2]) || astISBAD(basebox[3]) ||
+ astISBAD(basebox[4]) || astISBAD(basebox[5]) ) {
+ astError( AST__BADBX, "astInitPlot3D(%s): The limits of "
+ "the %s are undefined or bad.", status, name, name );
+ }
+
+/* Create a Frame which describes the graphics world coordinate system. */
+ graphicsframe = astFrame( 3, "Domain=GRAPHICS,Title=Graphical Coordinates", status );
+
+/* Initialise a 2D Plot structure (the parent class) as the first component
+ within the Plot3D structure, allocating memory if necessary. We supply
+ dummy arguments since we will not be using the parent Plot class to
+ draw anything. We supply a NULL vtab pointer so that methods defined by
+ the Plot class will be used during the construction of the Plot3D. Once
+ the Plot3D is fully constructed, we will use astSetVtab to establish
+ the correct vtab. */
+ new = (AstPlot3D *) astInitPlot( mem, size, 0, NULL, name, NULL, fjunkbox,
+ djunkbox );
+ if ( astOK ) {
+
+/* Initialise the Plot3D data. */
+/* ----------------------------- */
+
+/* Remove all Frames from the parent FrameSet except for the base (2D graphics)
+ Frame (we leave this last Frame since an error is reported if the last
+ Frame is removed from a FrameSet). We do this by repeatedly removing the
+ first Frame, causing all remaining Frames to have their index reduced by 1.
+ When the base Frame arrives at index 1, we skip it and start removing the
+ second frame instead. */
+ nfrm = astGetNframe( new );
+ i = 1;
+ for( ii = 0; ii < nfrm; ii++ ) {
+ if( i > 1 || astGetBase( new ) != 1 ) {
+ astRemoveFrame( new, i );
+ } else {
+ i = 2;
+ }
+ }
+
+/* Add in the 3D graphics Frame, using a PermMap to connect it to the
+ 2D graphics Frame. */
+ map = (AstMapping *) astPermMap( 2, NULL, 3, NULL, NULL, "", status );
+ astAddFrame( new, 1, map, graphicsframe );
+ map = astAnnul( map );
+
+/* And remove the 2D GRAPHICS Frame, leaving just the 3D GRAPHICS Frame
+ in the FrameSet, with index 1. */
+ astRemoveFrame( new, 1 );
+
+/* Get the index of the current (physical) and base (pixel) Frames in
+ the supplied FrameSet. */
+ bi = astGetBase( fset );
+ ci = astGetCurrent( fset );
+
+/* Temporarily set the current Frame to be the pixel frame. */
+ astSetCurrent( fset, bi );
+
+/* Get a double precision version of "graphbox", and store it in the
+ Plot3D structure. */
+ new->gbox[ 0 ] = (double) graphbox[ 0 ];
+ new->gbox[ 1 ] = (double) graphbox[ 1 ];
+ new->gbox[ 2 ] = (double) graphbox[ 2 ];
+ new->gbox[ 3 ] = (double) graphbox[ 3 ];
+ new->gbox[ 4 ] = (double) graphbox[ 4 ];
+ new->gbox[ 5 ] = (double) graphbox[ 5 ];
+
+/* The base Frame of the supplied FrameSet is mapped linearly onto the
+ graphics frame. Create a WinMap that maps the base box (within the
+ base Frame of the supplied FrameSet) onto the graphics box. */
+ map = (AstMapping *) astWinMap( 3, new->gbox, new->gbox + 3, basebox,
+ basebox + 3, "", status );
+
+/* Add the supplied FrameSet into the Plot3D (i.e. FrameSet) created
+ earlier. This leaves the graphics frame with index 1 in the
+ returned Plot3D. */
+ astAddFrame( (AstFrameSet *) new, 1, map, fset );
+ map = astAnnul( map );
+
+/* Set the current Frame in the Plot to be the physical coordinate Frame
+ (with index incremented by one because the graphics Frame has been added). */
+ astSetCurrent( (AstFrameSet *) new, ci + 1 );
+
+/* Note the index of the original base Frame in the Plot3D FrameSet */
+ new->pix_frame = bi + 1;
+
+/* Re-establish the original current Frame in the supplied FrameSet. */
+ astSetCurrent( fset, ci );
+
+/* Initialise the Plot pointers. */
+ new->plotxy = NULL;
+ new->plotxz = NULL;
+ new->plotyz = NULL;
+
+/* Initialise other attributes */
+ new->rootcorner = -1;
+
+/* Initialise the normal vector to the plane used by astText and astMark. */
+ new->norm[ 0 ] = AST__BAD;
+ new->norm[ 1 ] = AST__BAD;
+ new->norm[ 2 ] = AST__BAD;
+
+/* Create three 2D Plots to describe the three planes in the cube. */
+ CreatePlots( new, fset, graphbox, basebox, status );
+
+/* Ensure that attempts to use the graphics interface of the parent
+ Plot structure get forwarded to the relevant 3D routines defined in
+ this class. */
+ astGrfSet( new, "Attr", (AstGrfFun) Attr3D );
+ astSetGrf( new, 1 );
+
+/* Change the virtual function table stored in the new Plot3D, from the Plot
+ vtab (established when astINitPlot was called above), to the supplied
+ vtab. */
+ if( vtab ) astSetVtab( new, vtab );
+
+/* Ensure that these Plots use the grf functions defined by this class
+ (Plot3D). This means that whenever a Plot draws anything, it will use
+ the appropriate grf function defined in this class to do the drawing.
+ The grf functions defined in this class, convert the grf call into a
+ grf3D call apprpriate the plane spanned by the Plot. */
+ Set3DGrf( new, new->plotxy, XY, status );
+ Set3DGrf( new, new->plotxz, XZ, status );
+ Set3DGrf( new, new->plotyz, YZ, status );
+
+/* Set up the Edges attributes in the encapsulated Plots so that labels
+ appear on the requited edges. Initially, the root corner is "LLL"
+ (i.e. the lower bound on every axis). */
+ ChangeRootCorner( new, 0, 0, status );
+ }
+
+/* Annul the frame. */
+ graphicsframe = astAnnul( graphicsframe );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+
+/* Annul the pointer to the base Frame and FrameSet. */
+ baseframe = astAnnul( baseframe );
+ fset = astAnnul( fset );
+
+/* Return a pointer to the new object. */
+ return new;
+}
+
+
+AstPlot3D *astLoadPlot3D_( void *mem, size_t size, AstPlot3DVtab *vtab,
+ const char *name, AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadPlot3D
+
+* Purpose:
+* Load a Plot3D.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "plot3d.h"
+* AstPlot3D *astLoadPlot3D( void *mem, size_t size,
+* AstPlot3DVtab *vtab,
+* const char *name, AstChannel *channel )
+
+* Class Membership:
+* Plot3D loader.
+
+* Description:
+* This function is provided to load a new Plot3D using data read
+* from a Channel. It first loads the data used by the parent class
+* (which allocates memory if necessary) and then initialises a
+* Plot3D structure in this memory, using data read from the
+* input Channel.
+
+* Parameters:
+* mem
+* A pointer to the memory into which the Plot3D is to be
+* loaded. This must be of sufficient size to accommodate the
+* Plot3D data (sizeof(Plot3D)) plus any data used by
+* derived classes. If a value of NULL is given, this function
+* will allocate the memory itself using the "size" parameter to
+* determine its size.
+* size
+* The amount of memory used by the Plot3D (plus derived class
+* data). This will be used to allocate memory if a value of
+* NULL is given for the "mem" parameter. This value is also
+* stored in the Plot3D structure, so a valid value must be
+* supplied even if not required for allocating memory.
+*
+* If the "vtab" parameter is NULL, the "size" value is ignored
+* and sizeof(AstPlot3D) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new Plot3D. If this is NULL, a pointer
+* to the (static) virtual function table for the Plot3D class
+* is used instead.
+* name
+* Pointer to a constant null-terminated character string which
+* contains the name of the class to which the new object
+* belongs (it is this pointer value that will subsequently be
+* returned by the astGetClass method).
+*
+* If the "vtab" parameter is NULL, the "name" value is ignored
+* and a pointer to the string "Plot3D" is used instead.
+
+* Returned Value:
+* A pointer to the new Plot3D.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Constants: */
+#define KEY_LEN 50 /* Maximum length of a keyword */
+
+/* Local Variables: */
+ astDECLARE_GLOBALS
+ AstPlot3D *new;
+ char key[ KEY_LEN + 1 ];
+ char *text;
+ int axis;
+ int i;
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if( !astOK ) return new;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(channel);
+
+/* If a NULL virtual function table has been supplied, then this is
+ the first loader to be invoked for this Plot3D. In this case the
+ Plot3D belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstPlot3D );
+ vtab = &class_vtab;
+ name = "Plot3D";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitPlot3DVtab( vtab, name );
+ class_init = 1;
+ }
+ }
+
+/* Allocate memory to hold the new Object. We allocate it now rather than
+ waiting for astInitObject to allocate it so that we can pass a NULL
+ vtab pointer to the Plot loader, thus causing the Plot loader to use the
+ function implementations provided by the Plot class rather than those
+ provided by the class being instantiated. */
+ if( !mem ) mem = astMalloc( size );
+
+/* Invoke the parent class loader to load data for all the ancestral
+ classes of the current one, returning a pointer to the resulting
+ partly-built Plot3D. Pass a NULL vtab pointer so that the "new" object
+ will be loaded using Plot methods rather than than Plot3D methods.
+ This is important because the implementations provided by the Plot3D
+ class for the Plot attribute accessors require the existence of the
+ encapsulated Plots held within the Plot3D, but these have not yet been
+ created. */
+ new = astLoadPlot( mem, size, NULL, name, channel );
+ if ( astOK ) {
+
+/* Now modify the new object to use the supplied vtab. */
+ astSetVtab( new, vtab );
+
+/* Read input data. */
+/* ================ */
+/* Request the input Channel to read all the input data appropriate to
+ this class into the internal "values list". */
+ astReadClassData( channel, "Plot3D" );
+
+/* Now read each individual data item from this list and use it to
+ initialise the appropriate instance variable(s) for this class. */
+
+/* In the case of attributes, we first read the "raw" input value,
+ supplying the "unset" value as the default. If a "set" value is
+ obtained, we then use the appropriate (private) Set... member
+ function to validate and set the value properly. */
+
+/* Norm. */
+/* ----- */
+ for( axis = 0; axis < 3; axis++ ) {
+ (void) sprintf( key, "norm%d", axis + 1 );
+ new->norm[ axis ] = astReadDouble( channel, key, AST__BAD );
+ if( TestNorm( new, axis, status ) ) SetNorm( new, axis, new->norm[ axis ], status );
+ }
+
+/* RootCorner. */
+/* ----------- */
+ text = astReadString( channel, "rootcn", " " );
+ if( astOK && strcmp( text, " " ) ) {
+ new->rootcorner = RootCornerInt( text, status );
+ if( new->rootcorner < 0 && astOK ) {
+ astError( AST__INTER, "astRead(Plot3D): Corrupt Plot3D contains "
+ "invalid RootCorner attribute value (%s).", status, text );
+ }
+ } else {
+ new->rootcorner = -1;
+ }
+ text = astFree( text );
+
+/* Labelled axes */
+ new->axis_plot1[0] = astReadInt( channel, "axplx1", -1 );
+ new->axis_plot1[1] = astReadInt( channel, "axply1", -1 );
+ new->axis_plot1[2] = astReadInt( channel, "axplz1", -1 );
+
+ new->axis_index1[0] = astReadInt( channel, "axinx1", -1 );
+ new->axis_index1[1] = astReadInt( channel, "axiny1", -1 );
+ new->axis_index1[2] = astReadInt( channel, "axinz1", -1 );
+
+/* Unlabelled axes */
+ new->axis_plot2[0] = astReadInt( channel, "axplx2", -1 );
+ new->axis_plot2[1] = astReadInt( channel, "axply2", -1 );
+ new->axis_plot2[2] = astReadInt( channel, "axplz2", -1 );
+
+ new->axis_index2[0] = astReadInt( channel, "axinx2", -1 );
+ new->axis_index2[1] = astReadInt( channel, "axiny2", -1 );
+ new->axis_index2[2] = astReadInt( channel, "axinz2", -1 );
+
+/* Plot that spans two connected 3D axes. */
+ new->baseplot = astReadInt( channel, "basepl", -1 );
+
+/* XY Plot */
+ new->plotxy = astReadObject( channel, "plotxy", NULL );
+
+/* XZ Plot */
+ new->plotxz = astReadObject( channel, "plotxz", NULL );
+
+/* YZ Plot */
+ new->plotyz = astReadObject( channel, "plotyz", NULL );
+
+/* The index within the Plot3D FrameSet, of the original base Frame in
+ the FrameSet supplied when the Plot3D was constructed. */
+ new->pix_frame = astReadInt( channel, "pixfrm", AST__NOFRAME );
+
+/* Ensure that these Plots use the grf functions defined by this class
+ (Plot3D). This means that whener a Plot draws anything, it will use
+ the appropriate grf function defined in this class to do the drawing.
+ The grf functions defined in this class, convert the grf call into a
+ grf3D call apprpriate the plane spanned by the Plot. */
+ Set3DGrf( new, new->plotxy, XY, status );
+ Set3DGrf( new, new->plotxz, XZ, status );
+ Set3DGrf( new, new->plotyz, YZ, status );
+
+/* For attributes of the parent Plot class will have been loaded
+ each attribute that has a set value in the parent Plot structure,
+ re-set the value so that it gets copied to the copy the to the
+ encapsulated Plots. First do axis specific attributes. */
+
+#define COPY_ATTR(attr,nval) \
+ for( i = 0; i < nval; i++ ) { \
+ if( astTest##attr(new,i) ) astSet##attr(new,i,astGet##attr(new,i)); \
+ }
+
+ COPY_ATTR(MinTick,3)
+ COPY_ATTR(Abbrev,3)
+ COPY_ATTR(Gap,3)
+ COPY_ATTR(LogGap,3)
+ COPY_ATTR(LogPlot,3)
+ COPY_ATTR(LogTicks,3)
+ COPY_ATTR(LogLabel,3)
+ COPY_ATTR(LabelUp,3)
+ COPY_ATTR(DrawAxes,3)
+ COPY_ATTR(LabelUnits,3)
+ COPY_ATTR(MinTickLen,3)
+ COPY_ATTR(MajTickLen,3)
+ COPY_ATTR(NumLab,3)
+ COPY_ATTR(NumLabGap,3)
+ COPY_ATTR(TextLab,3)
+ COPY_ATTR(TextLabGap,3)
+
+ COPY_ATTR(Style,AST__NPID)
+ COPY_ATTR(Font,AST__NPID)
+ COPY_ATTR(Colour,AST__NPID)
+ COPY_ATTR(Width,AST__NPID)
+ COPY_ATTR(Size,AST__NPID)
+
+#undef COPY_ATTR
+
+/* Now do attributes that are not axis specific. */
+
+#define COPY_ATTR(attr) \
+ if( astTest##attr(new) ) astSet##attr(new,astGet##attr(new));
+
+ COPY_ATTR(Ink)
+ COPY_ATTR(Tol)
+ COPY_ATTR(Invisible)
+ COPY_ATTR(TickAll)
+ COPY_ATTR(ForceExterior)
+ COPY_ATTR(Border)
+ COPY_ATTR(Clip)
+ COPY_ATTR(ClipOp)
+ COPY_ATTR(Escape)
+ COPY_ATTR(Grid)
+ COPY_ATTR(Labelling)
+
+#undef COPY_ATTR
+
+/* If an error occurred, clean up by deleting the new Plot3D. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return the new Plot3D pointer. */
+ return new;
+
+/* Undefine macros local to this function. */
+#undef KEY_LEN
+}
+
+/* Virtual function interfaces. */
+/* ============================ */
+/* These provide the external interface to the virtual functions defined by
+ this class. Each simply checks the global error status and then locates and
+ executes the appropriate member function, using the function pointer stored
+ in the object's virtual function table (this pointer is located using the
+ astMEMBER macro defined in "object.h").
+
+ Note that the member function may not be the one defined here, as it may
+ have been over-ridden by a derived class. However, it should still have the
+ same interface. */
+
+void astClearRootCorner_( AstPlot3D *this, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Plot3D,ClearRootCorner))( this, status );
+}
+
+void astSetRootCorner_( AstPlot3D *this, int value, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Plot3D,SetRootCorner))( this, value, status );
+}
+
+
+
+
+/* Special public interface functions. */
+/* =================================== */
+/* These provide the public interface to certain special functions
+ whose public interface cannot be handled using macros (such as
+ astINVOKE) alone. In general, they are named after the
+ corresponding protected version of the function, but with "Id"
+ appended to the name. */
+
+/* Public Interface Function Prototypes. */
+/* ------------------------------------- */
+/* The following functions have public prototypes only (i.e. no
+ protected prototypes), so we must provide local prototypes for use
+ within this module. */
+AstPlot3D *astPlot3DId_( void *frame_void, const float graphbox[6],
+ const double basebox[6], const char *, ... );
+
+/* Special interface function implementations. */
+/* ------------------------------------------- */
+AstPlot3D *astPlot3DId_( void *frame_void, const float graphbox[6],
+ const double basebox[6], const char *options, ... ) {
+/*
+*++
+* Name:
+c astPlot3D
+f AST_PLOT3D
+
+* Purpose:
+* Create a Plot3D.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "plot3d.h"
+c AstPlot3D *astPlot3D( AstFrame *frame, const float graphbox[ 6 ],
+c const double basebox[ 6 ], const char *options, ... )
+f RESULT = AST_PLOT3D( FRAME, GRAPHBOX, BASEBOX, OPTIONS, STATUS )
+
+* Class Membership:
+* Plot3D constructor.
+
+* Description:
+* This function creates a new Plot3D and optionally initialises
+* its attributes.
+*
+* A Plot3D is a specialised form of Plot that provides facilities
+* for producing 3D graphical output.
+
+* Parameters:
+c frame
+f FRAME = INTEGER (Given)
+* Pointer to a Frame describing the physical coordinate system
+* in which to plot. A pointer to a FrameSet may also be given,
+* in which case its current Frame will be used to define the
+* physical coordinate system and its base Frame will be mapped
+* on to graphical coordinates (see below).
+*
+* If a null Object pointer (AST__NULL) is given, a default
+* 3-dimensional Frame will be used to describe the physical
+* coordinate system. Labels, etc. may then be attached to this
+* by setting the appropriate Frame attributes
+* (e.g. Label(axis)) for the Plot.
+c graphbox
+f GRAPHBOX( 6 ) = REAL (Given)
+* An array giving the position and extent of the plotting volume
+* (within the plotting space of the underlying graphics system)
+* in which graphical output is to appear. This must be
+* specified using graphical coordinates appropriate to the
+* underlying graphics system.
+*
+* The first triple of values should give the coordinates of the
+* bottom left corner of the plotting volume and the second triple
+* should give the coordinates of the top right corner. The
+* coordinate on the horizontal axis should be given first in
+* each pair. Note that the order in which these points are
+* given is important because it defines up, down, left and
+* right for subsequent graphical operations.
+c basebox
+f BASEBOX( 6 ) = DOUBLE PRECISION (Given)
+* An array giving the coordinates of two points in the supplied
+* Frame (or in the base Frame if a FrameSet was supplied) which
+* correspond to the bottom left and top right corners of the
+* plotting volume, as specified above. This range of coordinates
+* will be mapped linearly on to the plotting area. The
+* coordinates should be given in the same order as above.
+c options
+f OPTIONS = CHARACTER * ( * ) (Given)
+c Pointer to a null-terminated string containing an optional
+c comma-separated list of attribute assignments to be used for
+c initialising the new Plot3D. The syntax used is identical to
+c that for the astSet function and may include "printf" format
+c specifiers identified by "%" symbols in the normal way.
+c If no initialisation is required, a zero-length string may be
+c supplied.
+f A character string containing an optional comma-separated
+f list of attribute assignments to be used for initialising the
+f new Plot3D. The syntax used is identical to that for the
+f AST_SET routine. If no initialisation is required, a blank
+f value may be supplied.
+c ...
+c If the "options" string contains "%" format specifiers, then
+c an optional list of additional arguments may follow it in
+c order to supply values to be substituted for these
+c specifiers. The rules for supplying these are identical to
+c those for the astSet function (and for the C "printf"
+c function).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astPlot3D()
+f AST_PLOT3D = INTEGER
+* A pointer to the new Plot3D.
+
+* Notes:
+* - The base Frame of the returned Plot3D will be a new Frame which
+* is created by this function to represent the coordinate system
+* of the underlying graphics system (graphical coordinates). It is
+* given a Frame index of 1 within the Plot3D. The choice of base
+* Frame (Base attribute) should not, in general, be changed once a
+* Plot3D has been created (although you could use this as a way of
+* moving the plotting area around on the plotting surface).
+c - If a Frame is supplied (via the "frame" pointer), then it
+f - If a Frame is supplied (via the FRAME pointer), then it
+* becomes the current Frame of the new Plot3D and is given a Frame
+* index of 2.
+c - If a FrameSet is supplied (via the "frame" pointer), then
+f - If a FrameSet is supplied (via the FRAME pointer), then
+* all the Frames within this FrameSet become part of the new Plot3D
+* (where their Frame indices are increased by 1), with the
+* FrameSet's current Frame becoming the current Frame of the Plot3D.
+* - At least one of the three axes of the current Frame must be
+* independent of the other two current Frame axes.
+* - If a null Object pointer (AST__NULL) is supplied (via the
+c "frame" pointer), then the returned Plot3D will contain two
+f FRAME pointer), then the returned Plot3D will contain two
+* Frames, both created by this function. The base Frame will
+* describe graphics coordinates (as above) and the current Frame
+* will be a basic Frame with no attributes set (this will
+* therefore give default values for such things as the Plot3D Title
+* and the Label on each axis). Physical coordinates will be mapped
+* linearly on to graphical coordinates.
+* - An error will result if the Frame supplied (or the base Frame
+* if a FrameSet was supplied) is not 3-dimensional.
+* - A null Object pointer (AST__NULL) will be returned if this
+c function is invoked with the AST error status set, or if it
+f function is invoked with STATUS set to an error value, or if it
+* should fail for any reason.
+*--
+
+* Implementation Notes:
+* - This function implements the external (public) interface to
+* the astPlot3D constructor function. It returns an ID value
+* (instead of a true C pointer) to external users, and must be
+* provided because astPlot3D_ has a variable argument list which
+* cannot be encapsulated in a macro (where this conversion would
+* otherwise occur).
+* - Because no checking or casting of arguments is performed
+* before the function is invoked, the "frame" parameter is of type
+* (void *) and is converted from an ID value to a pointer and
+* validated within the function itself.
+* - The variable argument list also prevents this function from
+* invoking astPlot3D_ directly, so it must be a
+* re-implementation of it in all respects, except for the final
+* conversion of the result to an ID value.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstFrame *frame; /* Pointer to Frame structure */
+ AstPlot3D *new; /* Pointer to new Plot3D */
+ va_list args; /* Variable argument list */
+ int *status; /* Pointer to inherited status value */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise variables to avoid "used of uninitialised variable"
+ messages from dumb compilers. */
+ new = NULL;
+
+/* Obtain a Frame pointer from any ID supplied and validate the
+ pointer to ensure it identifies a valid Frame. */
+ if( frame_void ){
+ frame = astVerifyFrame( astMakePointer( frame_void ) );
+ } else {
+ frame = NULL;
+ }
+
+/* Check the pointer can be used. */
+ if ( astOK ) {
+
+/* Initialise the Plot3D, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitPlot3D( NULL, sizeof( AstPlot3D ), !class_init,
+ &class_vtab, "Plot3D", frame, graphbox,
+ basebox );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ Plot3D's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+ }
+
+/* Return an ID value for the new Plot3D. */
+ return astMakeId( new );
+
+}
+
+
+
+/* Macros that define method to override the methods of the Plot class
+ that are not currently implemented by this class. They just report an
+ error if called. */
+
+#define METHOD1(name) \
+static void name(ARGLIST,int *status){ \
+ if( !astOK ) return; \
+ astError( AST__INTER, "ast##name(%s): The ast##name " \
+ "method cannot be used with a %s (programming error).", status, \
+ astGetClass( this ), astGetClass( this ) ); \
+}
+
+#define METHOD2(name,rettype,retval) \
+static rettype name(ARGLIST,int *status){ \
+ if( !astOK ) return retval; \
+ astError( AST__INTER, "ast##name(%s): The ast##name " \
+ "method cannot be used with a %s (programming error).", status, \
+ astGetClass( this ), astGetClass( this ) ); \
+ return retval; \
+}
+
+
+#define ARGLIST AstPlot *this
+METHOD2(GetGrfContext,AstKeyMap *,NULL)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this
+METHOD1(GrfPop)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this
+METHOD1(GrfPush)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this, const char *name, AstGrfFun fun
+METHOD1(GrfSet)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this, int axis, const double start[], double length
+METHOD1(GridLine)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this, float lbnd[2], float ubnd[2]
+METHOD1(BoundingBox)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this, int iframe, const double lbnd[], const double ubnd[]
+METHOD1(Clip)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this, const double start[], const double finish[]
+METHOD1(Curve)
+#undef ARGLIST
+
+#define ARGLIST AstPlot *this, AstMapping *map
+METHOD1(GenCurve)
+#undef ARGLIST
+
+
+
+
+
+
+
+