include include include include "wcslab.h" include "wcs_desc.h" # Define the offset array. define OFFSET Memr[$1+$2-1] # Define the postscript kernel define PSIKERN "psikern" # WL_LABEL -- Place the labels on the grids. # # Description # Format and write the labels for the grid/tick marks. Much of this # is wading through conditions to decide whether a label should be # written or not. procedure wl_label (wd) pointer wd # I: the WCSLAB descriptor bool no_side_axis1, no_side_axis2, streq() int i, axis1_side, axis2_side short flag pointer kernel, sp, offset_ptr real offset begin # Get some memory. call smark (sp) call salloc (offset_ptr, N_SIDES, TY_REAL) do i = 1, N_SIDES OFFSET(offset_ptr,i) = 0. call salloc (kernel, SZ_LINE, TY_CHAR ) # Decide whether any sides were specified for either axis. no_side_axis1 = true no_side_axis2 = true do i = 1, N_SIDES { if (WL_LABEL_SIDE(wd,i,AXIS1)) no_side_axis1 = false if (WL_LABEL_SIDE(wd,i,AXIS2)) no_side_axis2 = false } # If polar, then label the axis 2's next to their circles on the # graph and allow the Axis 1s to be labeled on all sides of the graph. if (WL_GRAPH_TYPE(wd) == POLAR) { call wl_polar_label (wd) if (no_side_axis1) { do i = 1, N_SIDES { WL_LABEL_SIDE(wd,i,AXIS1) = true } if (IS_INDEFI (WL_AXIS_TITLE_SIDE(WD,AXIS1))) WL_AXIS_TITLE_SIDE(WD,AXIS1) = BOTTOM } # If we are near-polar, label the Axis 2 as if polar, and label # Axis1 on all sides except the side closest to the pole. } else if (WL_GRAPH_TYPE(wd) == NEAR_POLAR) { if (no_side_axis1) { WL_LABEL_SIDE(wd,WL_BAD_LABEL_SIDE(wd),AXIS1) = true if (IS_INDEFI (WL_AXIS_TITLE_SIDE(wd,AXIS1))) WL_AXIS_TITLE_SIDE(wd,AXIS1) = WL_BAD_LABEL_SIDE(wd) } if (no_side_axis2) { WL_LABEL_SIDE(wd,WL_POLAR_LABEL_DIRECTION(wd),AXIS2) = true if (IS_INDEFI (WL_AXIS_TITLE_SIDE(wd,AXIS2))) WL_AXIS_TITLE_SIDE(wd,AXIS2) = WL_POLAR_LABEL_DIRECTION(wd) } # Final case- adjacent sides should be labelled. } else { # Determine the best sides for labelling. if (INVERT (WL_ROTA(wd))) { axis1_side = LEFT axis2_side = BOTTOM } else { axis1_side = BOTTOM axis2_side = LEFT } # If no sides were specified, use the calculated ones above. if (no_side_axis1) WL_LABEL_SIDE(wd,axis1_side,AXIS1) = true if (no_side_axis2) WL_LABEL_SIDE(wd,axis2_side,AXIS2) = true } # Check to see if this is a psikern printer. If so, set text # so that it is mono-spaced. The superscripting algorithm # doesn't work too well in a proportional-spaced system. call ggets (WL_GP(wd), "tn", Memc[kernel], SZ_LINE ) if (streq (Memc[kernel], PSIKERN)) { flag = NO call gescape (WL_GP(wd), PS_VARIABLE_SPACE, flag, PS_VARIABLE_SPACE_SIZE) } # Now draw the labels for axis 1. do i = 1, N_SIDES { if (WL_LABEL_SIDE(wd,i,AXIS1)) { call wl_lab_edges (wd, AXIS1, i, offset) if (IS_INDEFI (WL_AXIS_TITLE_SIDE(WD,AXIS1))) WL_AXIS_TITLE_SIDE(WD,AXIS1) = i } else offset = 0. # Modify the bounding box for the new viewport. if (abs (offset) > abs (OFFSET(offset_ptr,i))) OFFSET(offset_ptr,i) = offset } # Draw the labels for axis 2. if (WL_GRAPH_TYPE(wd) != POLAR) do i = 1, N_SIDES { if (WL_LABEL_SIDE(wd,i,AXIS2)) { call wl_lab_edges (wd, AXIS2, i, offset) if (IS_INDEFI (WL_AXIS_TITLE_SIDE(wd,AXIS2))) WL_AXIS_TITLE_SIDE(wd,AXIS2) = i } else offset = 0. # Modify the bounding box for the new viewport. if (abs (offset) > abs (OFFSET(offset_ptr,i))) OFFSET(offset_ptr,i) = offset } # Reset to variable spacing. if (streq (Memc[kernel], PSIKERN)) { flag = YES call gescape (WL_GP(wd), PS_VARIABLE_SPACE, flag, PS_VARIABLE_SPACE_SIZE) } # Set the bounding box. do i = 1, N_SIDES WL_NEW_VIEW(wd,i) = WL_NEW_VIEW(wd,i) + OFFSET(offset_ptr,i) # Now write the graph title. call wl_title (WL_GP(wd), WL_AXIS_TITLE(wd,AXIS1), WL_AXIS_TITLE_SIDE(wd,AXIS1), WL_AXIS_TITLE_SIZE(wd), WL_NEW_VIEW(wd,1)) if (WL_GRAPH_TYPE(wd) != POLAR) call wl_title (WL_GP(wd), WL_AXIS_TITLE(wd,AXIS2), WL_AXIS_TITLE_SIDE(wd,AXIS2), WL_AXIS_TITLE_SIZE(WD), WL_NEW_VIEW(wd,1)) if (! IS_INDEFI (WL_TITLE_SIDE(wd))) call wl_title (WL_GP(wd), WL_TITLE(wd), WL_TITLE_SIDE(wd), WL_TITLE_SIZE(wd), WL_NEW_VIEW(wd,1)) # Release memory. call sfree (sp) end # Define what is in the screen. define IN (($1>WL_SCREEN_BOUNDARY(wd,LEFT))&&($1WL_SCREEN_BOUNDARY(wd,BOTTOM))&&($2= STTODEG (3600.0D0)) prec = HOUR else if (WL_MAJOR_INTERVAL(wd,AXIS1) >= STTODEG (60.0D0)) prec = MINUTE else if (WL_MAJOR_INTERVAL(wd,AXIS1) >= STTODEG (1.0D0)) prec = SECOND else if (WL_MAJOR_INTERVAL(wd,AXIS1) >= STTODEG (.01D0)) prec = SUBSEC_LOW else prec = SUBSEC_HIGH } else { if (WL_MAJOR_INTERVAL(wd,AXIS2) >= SATODEG (3600.0D0)) prec = DEGREE else if (WL_MAJOR_INTERVAL(wd,AXIS2) >= SATODEG (60.0D0)) prec = MINUTE else if (WL_MAJOR_INTERVAL(wd,AXIS2) >= SATODEG (1.0D0)) prec = SECOND else if (WL_MAJOR_INTERVAL(wd,AXIS2) >= SATODEG (.01D0)) prec = SUBSEC_LOW else prec = SUBSEC_HIGH } # Handle other coordinate types. else prec = INDEFI return (prec) end # Define some value constraints. define LOW_ACCURACY .01 define HIGH_ACCURACY .0001 # WL_HMS -- Convert value to number in hours, minutes, and seconds. procedure wl_hms (rarad, hms, units, maxch, precision, all) double rarad # I: the value to format into a string (degrees) char hms[ARB] # O: string containing formatted value char units[ARB] # O: string containing formatted units int maxch # I: the maximum number of characters allowed int precision # I: how precise the output should be bool all # I: true if all relevent fields should be formatted double accuracy, fraction int sec, h, m, s pointer sp, temp_hms, temp_units begin # Get some memory. call smark (sp) call salloc (temp_hms, maxch, TY_CHAR) call salloc (temp_units, maxch, TY_CHAR) units[1] = EOS hms[1] = EOS # Define how close to zero is needed. accuracy = LOW_ACCURACY if (precision == SUBSEC_HIGH) accuracy = HIGH_ACCURACY # Seconds of time. fraction = double (abs(DEGTOST (rarad))) if (precision == SUBSEC_LOW || precision == SUBSEC_HIGH) { sec = int (fraction) fraction = fraction - double (sec) } else { sec = int (fraction + 0.5) fraction = 0. } # Range: 0 to 24 hours. if (sec < 0) sec = sec + STPERDAY else if (sec >= STPERDAY) sec = mod (sec, STPERDAY) # Separater fields. s = mod (sec, 60) m = mod (sec / 60, 60) h = sec / 3600 # Format fields. # Subseconds. if (precision == SUBSEC_LOW || precision == SUBSEC_HIGH) { fraction = s + fraction if (precision == SUBSEC_LOW) { call sprintf (hms, 6, "%05.2f") call pargd (fraction) call strcpy (" s ", units, maxch) } else { call sprintf (hms, 8, "%07.4f") call pargd (fraction) call strcpy (" s ", units, maxch) } if (!all) all = (fraction < accuracy) # Seconds } else if (precision == SECOND) { # NOTE: The all is not part of the if statement because if # SUBSEC's have been printed, then seconds have already been # dealt with. If SUBSEC's have not been dealt with, then this # is the first field to be checked anyways. call sprintf (hms, 3, "%02d ") call pargi (s) call strcpy (" s", units, maxch) if (! all) all = (s == 0) } # Minutes. if (precision == MINUTE || (precision > MINUTE && all)) { if (all) { call strcpy (hms, Memc[temp_hms], maxch) call strcpy (units, Memc[temp_units], maxch) } call sprintf (hms, 3, "%02d ") call pargi (m) call strcpy (" m", units, maxch) if (all) { call strcat (Memc[temp_hms], hms, maxch) call strcat (Memc[temp_units], units, maxch) } else all = (m == 0) } # Non-zero hours. if (precision == HOUR || all) { if (all) { call strcpy (hms, Memc[temp_hms], maxch) call strcpy (units, Memc[temp_units], maxch) } call sprintf (hms, 3, "%2.2d ") call pargi (h) call strcpy(" h", units, maxch) if (all) { call strcat (Memc[temp_hms], hms, maxch) call strcat (Memc[temp_units], units, maxch) } } # Release memory call sfree (sp) end # WL_DMS - Convert value to number in degrees, minutes, and seconds. procedure wl_dms (arcrad, dms, units, maxch, precision, all) double arcrad # I: the value to format into a string (degrees) char dms[ARB] # O: string containing formatted value char units[ARB] # O: string containing formatted units int maxch # I: the maximum number of characters allowed int precision # I: how precise the output should be ? bool all # I: true if all relavent fields should be formatted double accuracy, fraction int sec, h, m, s pointer sp, temp_dms, temp_units int strlen() begin # Get some memory. call smark (sp) call salloc (temp_dms, maxch, TY_CHAR) call salloc (temp_units, maxch, TY_CHAR) units[1] = EOS dms[1] = EOS # Define how close to zero is needed. accuracy = LOW_ACCURACY if (precision == SUBSEC_HIGH) accuracy = HIGH_ACCURACY # Seconds of time. fraction = double (abs (DEGTOSA (arcrad))) if (precision == SUBSEC_LOW || precision == SUBSEC_HIGH) { sec = int (fraction) fraction = fraction - double (sec) } else { sec = nint (fraction) fraction = 0. } # Separater fields. s = mod (abs(sec), 60) m = mod (abs(sec) / 60, 60) h = abs(sec) / 3600 # Format fields # # Subseconds. if (precision == SUBSEC_LOW || precision == SUBSEC_HIGH) { fraction = s + fraction call strcpy (dms, Memc[temp_dms], maxch) call strcpy (units, Memc[temp_units], maxch) if (precision == SUBSEC_LOW) { call sprintf (dms, 6, "%05.2f\"") call pargd (fraction) call strcpy (" ", units, maxch) } else { call sprintf (dms, 8, "%07.4f\"") call pargd (fraction) call strcpy (" ", units, maxch) } if (! all) all = (fraction < accuracy) call strcat (Memc[temp_dms], dms, maxch) call strcat (Memc[temp_units], units, maxch) # Seconds } else if (precision == SECOND) { # NOTE: The all is not part of the if statement because if # SUBSEC's have been printed, then seconds have already been # dealt with. If SUBSEC's have not been dealt with, then this # is the first field to be checked anyways. call strcpy (dms, Memc[temp_dms], maxch) call strcpy (units, Memc[temp_units], maxch) call sprintf (dms, 3, "%02d\"") call pargi (s) call strcpy (" ", units, maxch) if (! all) all = (s == 0) call strcat (Memc[temp_dms], dms, maxch) call strcat (Memc[temp_units], units, maxch) } # Minutes. if (precision == MINUTE || (precision > MINUTE && all)) { call strcpy (dms, Memc[temp_dms], maxch) call strcpy (units, Memc[temp_units], maxch) call sprintf (dms, 3, "%02d'") call pargi (m) call strcpy (" ", units, maxch) call strcat (Memc[temp_dms], dms, maxch) call strcat (Memc[temp_units], units, maxch) if (! all) all = (m == 0) } # Hours. if (precision == DEGREE || all) { call strcpy (dms, Memc[temp_dms], maxch) call strcpy (units, Memc[temp_units], maxch) if (sec + fraction < accuracy) call strcpy (" 0 ", dms, maxch) else if (arcrad < 0.) { call sprintf (dms, 4, "-%d ") call pargi (h) } else { call sprintf (dms, 4, "+%d ") call pargi (h) } call sprintf(units, 4, "%*wo") call pargi (strlen (dms) - 1) call strcat (Memc[temp_dms], dms, maxch) call strcat (Memc[temp_units], units, maxch) } # Release memory. call sfree (sp) end # WL_FULL_LABEL_POSTION -- Find the position where the full label should be. # # Description # This routine returns the index to the label that should be printed # in its full form, regardless of its value. This is so there is always # at least one labelled point with the full information. This point is # choosen by examining which label is the closest to the passed point # (usually one of the four corners of the display). # # Returns # Index into the labell arrays of the label to be fully printed. # If the return index is 0, then there are no labels for the given # side. int procedure wl_full_label_position (wd, labels, nlabels, axis, side, precision) pointer wd # I: the WCSLAB descriptor int labels[nlabels] # I: array of indexes of labels to be printed int nlabels # I: the number of labels in labels int axis # I: the axis being dealt with int side # I: the side being dealt with int precision # I: precision of the label bool all double cur_dist, dist int i, cur_label, xside, yside pointer sp, temp1 double wl_distanced() begin # Allocate some working space. call smark (sp) call salloc (temp1, SZ_LINE, TY_CHAR) # Initialize. xside = INDEFI yside = INDEFI # Determine which corner will have the full label. if (side == TOP || side == BOTTOM) { yside = side if (axis == AXIS1) { if (WL_LABEL_SIDE(wd,RIGHT,AXIS2)) xside = RIGHT if (WL_LABEL_SIDE(wd,LEFT,AXIS2)) xside = LEFT } else { if (WL_LABEL_SIDE(wd,RIGHT,AXIS1)) xside = RIGHT if (WL_LABEL_SIDE(wd,LEFT,AXIS1)) xside = LEFT } if (IS_INDEFI (xside)) xside = LEFT } else { xside = side if (axis == AXIS1) { if (WL_LABEL_SIDE(wd,TOP,AXIS2)) yside = TOP if (WL_LABEL_SIDE(wd,BOTTOM,AXIS2)) yside = BOTTOM } else { if (WL_LABEL_SIDE(wd,TOP,AXIS1)) yside = TOP if (WL_LABEL_SIDE(wd,BOTTOM,AXIS1)) yside = BOTTOM } if (IS_INDEFI (yside)) yside = BOTTOM } # Find the full label. cur_label = labels[1] cur_dist = wl_distanced (WL_SCREEN_BOUNDARY(wd,xside), WL_SCREEN_BOUNDARY(wd,yside), WL_LABEL_POSITION(wd,cur_label,AXIS1), WL_LABEL_POSITION(wd,cur_label,AXIS2)) # Now go through the rest of the labels to find a closer label. for (i = 2; i <= nlabels; i = i + 1) { # Check to see if the label would be written in full anyways. all = false if (WL_SYSTEM_TYPE(wd) == RA_DEC) { if (WL_LABEL_AXIS(wd, labels[i]) == LONGITUDE) call wl_hms (WL_LABEL_VALUE(wd, labels[i]), Memc[temp1], Memc[temp1], SZ_LINE, precision, all) else call wl_dms (WL_LABEL_VALUE(wd, labels[i]), Memc[temp1], Memc[temp1], SZ_LINE, precision, all) } # If so, don't figure out which label should be full, there # will be one someplace. if (all) { cur_label = INDEFI break } dist = wl_distanced (WL_SCREEN_BOUNDARY(wd,xside), WL_SCREEN_BOUNDARY(wd,yside), WL_LABEL_POSITION(wd,labels[i],AXIS1), WL_LABEL_POSITION(wd,labels[i],AXIS2)) if (dist < cur_dist) { cur_dist = dist cur_label = labels[i] } } # Release memory. call sfree (sp) # Return the label index. return (cur_label) end # WL_WRITE_LABEL - Write the label in the format specified by the WCS type. procedure wl_write_label (wd, value, side, x, y, angle, axis, precision, do_full, offset) pointer wd # I: the WCSLAB descriptor double value # I: the value to use as the label int side # I: the side the label is going on real x, y # I: position of the label in NDC coordinates double angle # I: the angle the text should be written at int axis # I: which axis is being labelled int precision # I: level of precision for labels bool do_full # I: true if the full label should be printed real offset # I/O: offset for titles in NDC units int tside pointer sp, label, label_format, units, units_format real char_height, char_width, in_off_x, in_off_y, length real lx, ly, new_offset, rx, ry, text_angle real unit_off_x, unit_off_y, ux, uy bool fp_equalr() double wl_string_angle() int wl_opposite_side(), strlen() real ggetr(), gstatr() begin # Get some memory. call smark (sp) call salloc (label, SZ_LINE, TY_CHAR) call salloc (units, SZ_LINE, TY_CHAR) call salloc (label_format, SZ_LINE, TY_CHAR) call salloc (units_format, SZ_LINE, TY_CHAR) # Get character size. This info is used to move the character string # by the appropriate amounts. char_height = ggetr (WL_GP(wd), "ch") * gstatr (WL_GP(wd), G_TXSIZE) char_width = ggetr (WL_GP(wd), "cw") * gstatr (WL_GP(wd), G_TXSIZE) # Determine the "corrected" angle to write text in. text_angle = wl_string_angle (angle, WL_LABOUT(wd)) # Determine the units offset. call wl_rotate (0., char_height / 2., 1, text_angle - 90., unit_off_x, unit_off_y) # If the labels are to appear inside the graph and the major grid lines # have been drawn, then determine the necessary offset to get the label # off the line. if ((WL_LABOUT(wd) == NO) && (WL_MAJ_GRIDON(wd) == YES)) call wl_rotate (0., 0.75 * char_height, 1, text_angle - 90., in_off_x, in_off_y) else { in_off_x = 0. in_off_y = 0. } # Decode the coordinate into a text string. switch (WL_SYSTEM_TYPE(wd)) { case RA_DEC: if (axis == LONGITUDE) call wl_hms (value, Memc[label], Memc[units], SZ_LINE, precision, do_full) else call wl_dms (value, Memc[label], Memc[units], SZ_LINE, precision, do_full) default: call sprintf (Memc[label], SZ_LINE, "%.2g") call pargd (value) } # Set the text justification. call sprintf (Memc[label_format], SZ_LINE, "h=c;v=c;u=%f") call pargr (text_angle) call sprintf (Memc[units_format], SZ_LINE, "h=c;v=c;u=%f") call pargr (text_angle) # Determine offset needed to rotate text about the point of placement. # NOTE: The STDGRAPH kernel messes up rotate text placement. Try to # accomodate with extra offset. length = .5 * char_width * (2 + strlen (Memc[label])) call wl_rotate (length, 0., 1, text_angle - 90., rx, ry) rx = abs (rx) ry = abs (ry) # If labels are to appear inside the graph, then justification should # appear as if it were done for the opposite side. if (WL_LABOUT(wd) == YES) tside = side else tside = wl_opposite_side (side) # Now add the offsets appropriately. switch (tside) { case TOP: ly = y + ry + in_off_y + unit_off_y if (fp_equalr (text_angle, 90.)) { lx = x ly = ly + unit_off_y } else if (text_angle < 90.) lx = x - rx else lx = x + rx lx = lx + in_off_x new_offset = ry + ry case BOTTOM: ly = y - ry - in_off_y - unit_off_y if (fp_equalr (text_angle, 90.)) { lx = x ly = ly - unit_off_y } else if (text_angle < 90.) lx = x + rx else lx = x - rx lx = lx - in_off_x new_offset = ry + ry case LEFT: lx = x - rx - abs (unit_off_x) if (text_angle < 90.) { ly = y + ry - in_off_y lx = lx - in_off_x } else { ly = y - ry + in_off_y lx = lx + in_off_x } new_offset = rx + rx + abs (unit_off_x) case RIGHT: lx = x + rx + abs (unit_off_x) if (text_angle < 90.) { ly = y - ry + in_off_y lx = lx + in_off_x } else { ly = y + ry - in_off_y lx = lx - in_off_x } new_offset = rx + rx + abs (unit_off_x) } lx = lx - (unit_off_x / 2.) ly = ly - (unit_off_y / 2.) ux = lx + unit_off_x uy = ly + unit_off_y # Print the label. call gtext (WL_GP(wd), lx, ly, Memc[label], Memc[label_format]) # Print the units (if appropriate). if (WL_SYSTEM_TYPE(wd) == RA_DEC) call gtext (WL_GP(wd), ux, uy, Memc[units], Memc[units_format]) # Determine new maximum string size. if ((WL_LABOUT(wd) == YES) && (abs (offset) < new_offset)) if (side == LEFT || side == BOTTOM) offset = -new_offset else offset = new_offset # Release memory. call sfree (sp) end # WL_STRING_ANGLE -- Produce the angle that a label string should be written to. # # Description # Fixes the input angle so that the output angle is in the range 0 to 180. # # Returns # the angle that the label should be written as. double procedure wl_string_angle (angle, right_to_up) double angle # I: the input angle in degrees int right_to_up # I: true if angle near horizontal/vertical are fixed double output_angle begin # Try to ensure that the angle is "upright", i.e. the string will not # be printed upside-down. output_angle = angle if (output_angle > QUARTER_CIRCLE) output_angle = output_angle - HALF_CIRCLE if (output_angle < -QUARTER_CIRCLE) output_angle = output_angle + HALF_CIRCLE # If the angle is close to parallel with one of the axis, then just # print it normally. if ((right_to_up == YES) && ((mod (abs (output_angle), QUARTER_CIRCLE) < MIN_ANGLE) || (QUARTER_CIRCLE - mod (abs (output_angle), QUARTER_CIRCLE) < MIN_ANGLE))) output_angle = 0. # Return the angle modified for the idiocincracy of GIO text angle # specification. return (output_angle + QUARTER_CIRCLE) end # WL_ANGLE -- Return the average angle of the labels in the list. # # Returns # Average angle # # Description # So that labels on a side are uniform (in some sense), the average angle # of all the labels is taken and is defined as the angle that all the labels # will be printed at. double procedure wl_angle (wd, labels, nlabels) pointer wd # I: the WCSLAB descriptor int labels[nlabels] # I: the indexes of the labels to be printed out int nlabels # I: the number of indexes in the list double total, average int i begin total = 0.0 for (i = 1; i <= nlabels; i = i + 1) total = total + WL_LABEL_ANGLE(wd,labels[i]) average = real (total / nlabels) return (average) end