The R Project SVN R

Rev

Rev 9417 | Rev 9615 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 9417 Rev 9434
Line 804... Line 804...
804
    return at;
804
    return at;
805
}
805
}
806
 
806
 
807
SEXP do_axis(SEXP call, SEXP op, SEXP args, SEXP env)
807
SEXP do_axis(SEXP call, SEXP op, SEXP args, SEXP env)
808
{
808
{
809
    /* axis(side, at, labels, ...) - draw an axis */
809
    /* axis(side, at, labels, ticks, ...) - draw an axis */
810
    SEXP at, lab;
810
    SEXP at, lab;
811
    int dolabels, logflag=0;
811
    int col, fg, dolabels, doticks, logflag = 0;
812
    int col, fg;
-
 
813
    int i, n, nint=0, ntmp;
812
    int i, n, nint = 0, ntmp, side, *ind, outer;
814
    int side, xtckCoords, ytckCoords, *ind;
-
 
815
    double x, y, tempx, tempy, tnew, tlast;
813
    double x, y, temp, tnew, tlast;
816
    double tck;
-
 
817
    double axp[3], usr[2];
814
    double axp[3], usr[2];
818
    double gap, labw, low, high;
815
    double gap, labw, low, high, line, pos;
-
 
816
    double axis_base, axis_tick, axis_lab, axis_low, axis_high;
819
 
817
 
820
    SEXP originalArgs = args;
818
    SEXP originalArgs = args;
821
    DevDesc *dd = CurrentDevice();
819
    DevDesc *dd = CurrentDevice();
822
 
820
 
823
    /* initial checks */
821
    /* Arity Check */
-
 
822
    /* This is a builtin function, so it should always have */
-
 
823
    /* the correct arity, but it doesn't hurt to be defensive. */
824
 
824
 
825
    if (length(args) < 3) errorcall(call, "too few arguments");
825
    if (length(args) < 7) errorcall(call, "too few arguments");
826
    GCheckState(dd);
826
    GCheckState(dd);
827
 
827
 
828
    /* required argument "side" */
828
    /* Required argument: "side" */
-
 
829
    /* Which side of the plot the axis is to appear on. */
-
 
830
    /* side = 1 | 2 | 3 | 4. */
829
 
831
 
830
    side = asInteger(CAR(args));
832
    side = asInteger(CAR(args));
831
    if (side < 1 || side > 4)
833
    if (side < 1 || side > 4)
832
	errorcall(call, "invalid axis number");
834
	errorcall(call, "invalid axis number");
833
 
-
 
834
    args = CDR(args);
835
    args = CDR(args);
835
 
836
 
-
 
837
    /* Required argument: "at" */
-
 
838
    /* This gives the tick-label locations. */
836
    /* tick-label locations;  these are coerced lower down */
839
    /* Note that these are coerced to the correct type below. */
837
 
840
 
838
    at = CAR(args); args = CDR(args);
841
    at = CAR(args); args = CDR(args);
839
 
842
 
840
    /* labels */
843
    /* Required argument: "labels" */
-
 
844
    /* Labels can be a logical, indicating whether or not */
-
 
845
    /* to label the axis; or it can be a vector of character */
-
 
846
    /* strings or expressions which give the labels explicitly. */
-
 
847
    /* The expressions are used to set mathematical labelling. */
841
 
848
 
842
    dolabels = 1;
849
    dolabels = 1;
843
    if (isLogical(CAR(args)) && length(CAR(args)) > 0) {
850
    if (isLogical(CAR(args)) && length(CAR(args)) > 0) {
844
	i = asLogical(CAR(args));
851
	i = asLogical(CAR(args));
845
	if (i == 0 || i == NA_LOGICAL)
852
	if (i == 0 || i == NA_LOGICAL)
846
	    dolabels = 0;
853
	    dolabels = 0;
847
	PROTECT(lab = R_NilValue);
854
	PROTECT(lab = R_NilValue);
-
 
855
    }
848
    } else if (isExpression(CAR(args))) {
856
    else if (isExpression(CAR(args))) {
849
	PROTECT(lab = CAR(args));
857
	PROTECT(lab = CAR(args));
-
 
858
    }
850
    } else {
859
    else {
851
	PROTECT(lab = coerceVector(CAR(args), STRSXP));
860
	PROTECT(lab = coerceVector(CAR(args), STRSXP));
852
    }
861
    }
853
    args = CDR(args);
862
    args = CDR(args);
854
 
863
 
-
 
864
    /* Required argument: "ticks" */
-
 
865
    /* This indicates whether or not ticks and the axis line */
-
 
866
    /* should be plotted: TRUE => show, FALSE => don't show. */
-
 
867
 
-
 
868
    doticks = asLogical(CAR(args));
-
 
869
    if (doticks == NA_LOGICAL) doticks = 1;
-
 
870
    args = CDR(args);
-
 
871
 
-
 
872
    /* Optional argument: "line" */
-
 
873
    /* Specifies an offset outward from the plot for the axis. */
-
 
874
    /* The values in the par value "mgp" are interpreted */
-
 
875
    /* relative to this value. */
-
 
876
 
-
 
877
    line = asReal(CAR(args));
-
 
878
    if (!R_FINITE(line)) line =  dd->gp.mgp[2];
-
 
879
    args = CDR(args);
-
 
880
 
-
 
881
    /* Optional argument: "pos" */
-
 
882
    /* Specifies a user coordinate at which the axis should be drawn. */
-
 
883
    /* This overrides the value of "line".  Again the "mgp" par values */
-
 
884
    /* are interpreted relative to this value. */
-
 
885
 
-
 
886
    pos = asReal(CAR(args));
-
 
887
    if (!R_FINITE(pos)) pos = NA_REAL;
-
 
888
    args = CDR(args);
-
 
889
 
-
 
890
    /* Optional argument: "outer" */
-
 
891
    /* Should the axis be drawn in the outer margin. */
-
 
892
    /* This only affects the computation of axis_base. */
-
 
893
 
-
 
894
    outer = asLogical(CAR(args));
-
 
895
    if (outer == NA_LOGICAL || outer == 0)
-
 
896
	outer = NPC;
-
 
897
    else
-
 
898
	outer = NIC;
-
 
899
    
-
 
900
    args = CDR(args);
-
 
901
 
855
    /* Retrieve relevant "par" values. */
902
    /* Retrieve relevant "par" values. */
856
 
903
 
857
    switch(side) {
904
    switch(side) {
858
    case 1:
905
    case 1:
859
    case 3:
906
    case 3:
Line 875... Line 922...
875
	logflag = dd->dp.ylog;
922
	logflag = dd->dp.ylog;
876
	nint = dd->dp.lab[1];
923
	nint = dd->dp.lab[1];
877
	break;
924
	break;
878
    }
925
    }
879
 
926
 
880
    /* Determine the tick mark positions.  Note that these may fall */
927
    /* Determine the tickmark positions.  Note that these may fall */
881
    /* outside the plot window. We will clip them in the code below. */
928
    /* outside the plot window. We will clip them in the code below. */
882
 
929
 
883
    if (length(at) == 0) {
930
    if (length(at) == 0) {
884
	PROTECT(at = CreateAtVector(axp, usr, nint, logflag));
931
	PROTECT(at = CreateAtVector(axp, usr, nint, logflag));
885
    }
932
    }
Line 887... Line 934...
887
	if (isReal(at)) PROTECT(at = duplicate(at));
934
	if (isReal(at)) PROTECT(at = duplicate(at));
888
	else PROTECT(at = coerceVector(at, REALSXP));
935
	else PROTECT(at = coerceVector(at, REALSXP));
889
    }
936
    }
890
    n = length(at);
937
    n = length(at);
891
 
938
 
-
 
939
    /* Check/setup the tick labels.  This can mean using user-specified */
-
 
940
    /* labels, or encoding the "at" positions as strings. */
-
 
941
 
892
    if (dolabels) {
942
    if (dolabels) {
893
	if (length(lab) == 0)
943
	if (length(lab) == 0)
894
	    lab = labelformat(at);
944
	    lab = labelformat(at);
895
	else if (!isExpression(lab))
945
	else if (!isExpression(lab))
896
	    lab = labelformat(lab);
946
	    lab = labelformat(lab);
897
	if (length(at) != length(lab))
947
	if (length(at) != length(lab))
898
	    errorcall(call, "location and label lengths differ");
948
	    errorcall(call, "location and label lengths differ");
899
    }
949
    }
900
    PROTECT(lab);
950
    PROTECT(lab);
-
 
951
 
-
 
952
    /* Check there are no NA, Inf or -Inf values for tick positions. */
-
 
953
    /* The code here is long-winded.  Couldn't we just inline things */
-
 
954
    /* below.  Hmmm - we need the min and max of the finite values ... */
-
 
955
 
901
    ind = (int *) R_alloc(n, sizeof(int));
956
    ind = (int *) R_alloc(n, sizeof(int));
902
    for(i = 0; i < n; i++) ind[i] = i;
957
    for(i = 0; i < n; i++) ind[i] = i;
903
    rsort_with_index(REAL(at), ind, n);
958
    rsort_with_index(REAL(at), ind, n);
904
    /* drop  NA, Inf and -Inf values */
-
 
905
    ntmp = 0;
959
    ntmp = 0;
906
    for(i = 0; i < n; i++) {
960
    for(i = 0; i < n; i++) {
907
	if(R_FINITE(REAL(at)[i])) ntmp = i+1;
961
	if(R_FINITE(REAL(at)[i])) ntmp = i+1;
908
    }
962
    }
909
    n = ntmp;
963
    n = ntmp;
910
    if (n == 0)
964
    if (n == 0)
911
	errorcall(call, "no locations are finite");
965
	errorcall(call, "no locations are finite");
912
 
966
 
-
 
967
    /* Ok, all systems are "GO".  Let's get to it. */
-
 
968
    /* First we process all the inline par values. */
-
 
969
 
913
    GSavePars(dd);
970
    GSavePars(dd);
914
    RecordGraphicsCall(call);
971
    RecordGraphicsCall(call);
915
    ProcessInlinePars(args, dd);
972
    ProcessInlinePars(args, dd);
-
 
973
 
-
 
974
    /* At this point we know the value of "xaxt" and "yaxt" */
-
 
975
    /* so we test to see whether the relevant one is "n". */
-
 
976
    /* If it is, we just bail out at this point. */
-
 
977
    
-
 
978
    if (((side == 1 || side == 3) && dd->gp.xaxt == 'n') ||
-
 
979
	((side == 2 || side == 4) && dd->gp.yaxt == 'n')) {
-
 
980
	GRestorePars(dd);
-
 
981
	UNPROTECT(3);
-
 
982
	return R_NilValue;
-
 
983
    }
-
 
984
 
-
 
985
    /* I can't think of a good reason to allow axes */
-
 
986
    /* with a non-solid line-type, so I override any */
-
 
987
    /* value the user may have specified.  This may */
-
 
988
    /* need to be revisited, but this should cover */
-
 
989
    /* 99.99% of the cases. */
-
 
990
 
916
    dd->gp.lty = LTY_SOLID;
991
    dd->gp.lty = LTY_SOLID;
917
 
992
 
918
    /* override par("xpd") and force clipping to figure region */
993
    /* Override par("xpd") and force clipping to figure region */
919
    /* NOTE: don't override to _reduce_ clipping region */
994
    /* NOTE: don't override to _reduce_ clipping region */
920
#ifdef OLD
-
 
921
    if (dd->gp.xpd < 1)
-
 
922
	dd->gp.xpd = 1;
-
 
923
#else
995
 
924
    dd->gp.xpd = 2;
996
    dd->gp.xpd = 2;
925
#endif
-
 
926
 
997
 
927
    dd->gp.adj = 0.5;
998
    dd->gp.adj = 0.5;
928
    dd->gp.font = dd->gp.fontaxis;
999
    dd->gp.font = dd->gp.fontaxis;
929
    dd->gp.cex = dd->gp.cexbase * dd->gp.cexaxis;
1000
    dd->gp.cex = dd->gp.cexbase * dd->gp.cexaxis;
930
    col = dd->gp.col;
1001
    col = dd->gp.col;
931
    fg = dd->gp.fg;
1002
    fg = dd->gp.fg;
932
 
1003
 
933
    x = dd->gp.usr[0];
-
 
934
    y = dd->gp.usr[2];
-
 
935
    xtckCoords = MAR1;
-
 
936
    ytckCoords = MAR2;
-
 
937
 
-
 
938
    /* Draw the axis */
1004
    /* Draw the axis */
939
    GMode(1, dd);
1005
    GMode(1, dd);
940
    switch (side) {
1006
    switch (side) {
941
    case 1: /*--- x-axis -- horizontal --- */
1007
    case 1: /*--- x-axis -- horizontal --- */
942
    case 3:
1008
    case 3:
943
    /* Check the axis type parameter, if it is 'n', */
-
 
944
    /* there is nothing to do */
-
 
945
	if (dd->gp.xaxt == 'n') {
-
 
946
	    GRestorePars(dd);
-
 
947
	    UNPROTECT(3);
-
 
948
	    return R_NilValue;
-
 
949
	}
-
 
950
	GetAxisLimits(dd->gp.usr[0], dd->gp.usr[1], &low, &high);
1009
	GetAxisLimits(dd->gp.usr[0], dd->gp.usr[1], &low, &high);
951
	if (side == 3) {
-
 
952
	    y = dd->gp.usr[3];
-
 
953
	    xtckCoords = MAR3;
-
 
954
	}
-
 
955
	dd->gp.col = fg;
-
 
956
	GLine(fmax2(low, REAL(at)[0]), y,
1010
	axis_low  = GConvertX(fmax2(low, REAL(at)[0]), USER, NFC, dd);
957
	      fmin2(high, REAL(at)[n - 1]), y, USER, dd);
1011
	axis_high = GConvertX(fmin2(high, REAL(at)[n-1]), USER, NFC, dd);
958
	if (R_FINITE(dd->gp.tck)) {
-
 
959
	    /* The S way of doing ticks */
-
 
960
	    double y0, y1;
-
 
961
	    if (dd->gp.tck > 0.5) {
-
 
962
		if (side == 1) {
1012
	if (side == 1) {
963
		    y0 = dd->gp.usr[2];
-
 
964
		    y1 = dd->gp.usr[2] + dd->gp.tck *
-
 
965
			(dd->gp.usr[3] - dd->gp.usr[2]);
-
 
966
		}
-
 
967
		else {
-
 
968
		    y0 = dd->gp.usr[3];
1013
	    if (R_FINITE(pos))
969
		    y1 = dd->gp.usr[3] + dd->gp.tck *
1014
		axis_base = GConvertY(pos, USER, NFC, dd);
970
			(dd->gp.usr[2] - dd->gp.usr[3]);
-
 
971
		}
-
 
972
	    }
-
 
973
	    else {
1015
	    else
974
		tck = dd->gp.tck * ((dd->gp.fin[0] < dd->gp.fin[1]) ?
-
 
975
				    dd->gp.fin[0] : dd->gp.fin[1]);
1016
		axis_base = GConvertY(0.0, outer, NFC, dd)
976
		if (side == 1) {
-
 
977
		    y0 = dd->gp.usr[2];
-
 
978
		    y1 = dd->gp.usr[2] + (tck / dd->gp.fin[1]) *
1017
		    - GConvertYUnits(line, LINES, NFC, dd);
979
			(dd->gp.usr[3] - dd->gp.usr[2]);
-
 
980
		}
-
 
981
		else {
-
 
982
		    y0 = dd->gp.usr[3];
1018
	    if (R_FINITE(dd->gp.tck))
983
		    y1 = dd->gp.usr[3] + (tck / dd->gp.fin[1]) *
1019
		axis_tick = axis_base + GConvertYUnits(dd->gp.tck, NPC, NFC, dd);
984
			(dd->gp.usr[2] - dd->gp.usr[3]);
-
 
985
		}
-
 
986
	    }
1020
	    else
987
	    for (i = 0; i < n; i++) {
-
 
988
		x = REAL(at)[i];
-
 
989
		if (low <= x && x <= high) {
-
 
990
		    GLine(x, y0, x, y1, USER, dd);
1021
		axis_tick = axis_base + GConvertYUnits(dd->gp.tcl, LINES, NFC, dd);
991
		}
-
 
992
	    }
-
 
993
	}
1022
	}
994
	else {
1023
	else {
-
 
1024
	    if (R_FINITE(pos))
-
 
1025
		axis_base = GConvertY(pos, USER, NFC, dd);
-
 
1026
	    else
-
 
1027
		axis_base =  GConvertY(1.0, outer, NFC, dd)
-
 
1028
		    + GConvertYUnits(line, LINES, NFC, dd);
-
 
1029
	    if (R_FINITE(dd->gp.tck))
-
 
1030
		axis_tick = axis_base - GConvertYUnits(dd->gp.tck, NPC, NFC, dd);
-
 
1031
	    else
-
 
1032
		axis_tick = axis_base - GConvertYUnits(dd->gp.tcl, LINES, NFC, dd);
-
 
1033
	}
-
 
1034
	if (doticks) {
-
 
1035
	    dd->gp.col = fg;
995
	    /* The R(ight) way of doing ticks (using "tcl", not "tck") */
1036
            GLine(axis_low, axis_base, axis_high, axis_base, NFC, dd);
996
	    for (i = 0; i < n; i++) {
1037
	    for (i = 0; i < n; i++) {
997
		x = REAL(at)[i];
1038
		x = REAL(at)[i];
998
		if (low <= x && x <= high) {
1039
		if (low <= x && x <= high) {
-
 
1040
		    x = GConvertX(x, USER, NFC, dd);
999
		    GLine(x, 0, x, -dd->gp.tcl, xtckCoords, dd);
1041
		    GLine(x, axis_base, x, axis_tick, NFC, dd);
1000
		}
1042
		}
1001
	    }
1043
	    }
1002
	}
1044
	}
-
 
1045
	/* Tickmark labels. */
1003
	dd->gp.col = dd->gp.colaxis;
1046
	dd->gp.col = dd->gp.colaxis;
1004
	/* labels */
-
 
1005
	gap = GStrWidth("m", NFC, dd);	/* FIXUP x/y distance */
1047
	gap = GStrWidth("m", NFC, dd);	/* FIXUP x/y distance */
1006
	tlast = -1.0;
1048
	tlast = -1.0;
1007
	if (dd->gp.las == 2 || dd->gp.las == 3) {
1049
	if (dd->gp.las == 2 || dd->gp.las == 3) {
1008
	    if (side == 1) dd->gp.adj = 1;
1050
	    dd->gp.adj = (side == 1) ? 1 : 0;
1009
	    else dd->gp.adj = 0;
-
 
1010
	}
1051
	}
1011
	else dd->gp.adj = 0.5;
1052
	else dd->gp.adj = 0.5;
-
 
1053
	if (side == 1) {
-
 
1054
	    axis_lab = - axis_base
-
 
1055
		+ GConvertYUnits(dd->gp.mgp[1], LINES, NFC, dd)
-
 
1056
		+ GConvertY(0.0, NPC, NFC, dd);
1012
 
1057
	}
-
 
1058
	else {
-
 
1059
	    axis_lab = axis_base
-
 
1060
		+ GConvertYUnits(dd->gp.mgp[1], LINES, NFC, dd)
-
 
1061
		- GConvertY(1.0, NPC, NFC, dd);
-
 
1062
	}
-
 
1063
	axis_lab = GConvertYUnits(axis_lab, NFC, LINES, dd);
1013
	for (i = 0; i < n; i++) {
1064
	for (i = 0; i < n; i++) {
1014
	    x = REAL(at)[i];
1065
	    x = REAL(at)[i];
1015
	    if (!R_FINITE(x)) continue;
1066
	    if (!R_FINITE(x)) continue;
1016
	    tempx = x; tempy = y;
-
 
1017
	    GConvert(&tempx, &tempy, USER, NFC, dd);
1067
	    temp = GConvertX(x, USER, NFC, dd);
1018
	    if (dolabels) {
1068
	    if (dolabels) {
1019
		/* clip tick labels to user coordinates */
1069
		/* Clip tick labels to user coordinates. */
1020
		if (x > low && x < high) {
1070
		if (x > low && x < high) {
1021
		    if (isExpression(lab)) {
1071
		    if (isExpression(lab)) {
1022
			GMMathText(VECTOR(lab)[ind[i]], side,
1072
			GMMathText(VECTOR(lab)[ind[i]], side,
1023
				   dd->gp.mgp[1], 0, x, dd->gp.las, dd);
1073
				   axis_lab, 0, x, dd->gp.las, dd);
1024
		    }
1074
		    }
1025
		    else {
1075
		    else {
1026
			labw = GStrWidth(CHAR(STRING(lab)[ind[i]]), NFC, dd);
1076
			labw = GStrWidth(CHAR(STRING(lab)[ind[i]]), NFC, dd);
1027
			tnew = tempx - 0.5 * labw;
1077
			tnew = temp - 0.5 * labw;
1028
			/* Check room for perpendicular labels: */
1078
			/* Check room for perpendicular labels. */
1029
			if (dd->gp.las == 2 || dd->gp.las == 3 ||
1079
			if (dd->gp.las == 2 || dd->gp.las == 3 ||
1030
			    tnew - tlast >= gap) {
1080
			    tnew - tlast >= gap) {
1031
			    GMtext(CHAR(STRING(lab)[ind[i]]), side,
1081
			    GMtext(CHAR(STRING(lab)[ind[i]]), side,
1032
				   dd->gp.mgp[1], 0, x,
-
 
1033
				   dd->gp.las, dd);
1082
				   axis_lab, 0, x, dd->gp.las, dd);
1034
			    tlast = tempx + 0.5 *labw;
1083
			    tlast = temp + 0.5 *labw;
1035
			}
1084
			}
1036
		    }
1085
		    }
1037
		}
1086
		}
1038
	    }
1087
	    }
1039
	}
1088
	}
1040
	break;
1089
	break;
1041
 
1090
 
1042
    case 2: /*--- y-axis -- vertical --- */
1091
    case 2: /*--- y-axis -- vertical --- */
1043
    case 4:
1092
    case 4:
1044
    /* Check the axis type parameter, if it is 'n', */
-
 
1045
    /* there is nothing to do */
-
 
1046
	if (dd->gp.yaxt == 'n') {
-
 
1047
	    GRestorePars(dd);
-
 
1048
	    UNPROTECT(3);
-
 
1049
	    return R_NilValue;
-
 
1050
	}
-
 
1051
	GetAxisLimits(dd->gp.usr[2], dd->gp.usr[3], &low, &high);
1093
	GetAxisLimits(dd->gp.usr[2], dd->gp.usr[3], &low, &high);
1052
	if (side == 4) {
-
 
1053
	    x = dd->gp.usr[1];
-
 
1054
	    ytckCoords = MAR4;
-
 
1055
	}
-
 
1056
	dd->gp.col = fg;
-
 
1057
	/* clip axis major line to user coordinates */
-
 
1058
	GLine(x, fmax2(low, REAL(at)[0]), x,
1094
	axis_low = GConvertY(fmax2(low, REAL(at)[0]), USER, NFC, dd);
1059
	      fmin2(high, REAL(at)[n - 1]), USER, dd);
1095
	axis_high = GConvertY(fmin2(high, REAL(at)[n-1]), USER, NFC, dd);
1060
	if (R_FINITE(dd->gp.tck)) {
-
 
1061
	    /* The S way of doing ticks */
-
 
1062
	    double x0, x1;
-
 
1063
	    if (dd->gp.tck > 0.5) {
-
 
1064
		if (side == 2) {
1096
	if (side == 2) {
1065
		    x0 = dd->gp.usr[0];
-
 
1066
		    x1 = dd->gp.usr[0] + dd->gp.tck *
-
 
1067
			(dd->gp.usr[1] - dd->gp.usr[0]);
-
 
1068
		}
-
 
1069
		else {
-
 
1070
		    x0 = dd->gp.usr[1];
1097
	    if (R_FINITE(pos))
1071
		    x1 = dd->gp.usr[1] + dd->gp.tck *
1098
		axis_base = GConvertX(pos, USER, NFC, dd);
1072
			(dd->gp.usr[0] - dd->gp.usr[1]);
-
 
1073
		}
-
 
1074
	    }
-
 
1075
	    else {
1099
	    else
1076
		tck = dd->gp.tck * ((dd->gp.fin[0] < dd->gp.fin[1]) ?
-
 
1077
				    dd->gp.fin[0] : dd->gp.fin[1]);
1100
		axis_base =  GConvertX(0.0, outer, NFC, dd)
1078
		if (side == 2) {
-
 
1079
		    x0 = dd->gp.usr[0];
-
 
1080
		    x1 = dd->gp.usr[0] + (tck / dd->gp.fin[0]) *
1101
		    - GConvertXUnits(line, LINES, NFC, dd);
1081
			(dd->gp.usr[1] - dd->gp.usr[0]);
-
 
1082
		}
-
 
1083
		else {
-
 
1084
		    x0 = dd->gp.usr[1];
1102
	    if (R_FINITE(dd->gp.tck))
1085
		    x1 = dd->gp.usr[1] + (tck / dd->gp.fin[0]) *
1103
		axis_tick = axis_base + GConvertXUnits(dd->gp.tck, NPC, NFC, dd);
1086
			(dd->gp.usr[0] - dd->gp.usr[1]);
-
 
1087
		}
-
 
1088
	    }
1104
	    else
1089
	    for (i = 0; i < n; i++) {
-
 
1090
		y = REAL(at)[i];
-
 
1091
		if (low <= y && y <= high) {
-
 
1092
		    GLine(x0, y, x1, y, USER, dd);
1105
		axis_tick = axis_base + GConvertXUnits(dd->gp.tcl, LINES, NFC, dd);
1093
		}
-
 
1094
	    }
-
 
1095
	}
1106
	}
1096
	else {
1107
	else {
-
 
1108
	    if (R_FINITE(pos))
-
 
1109
		axis_base = GConvertX(pos, USER, NFC, dd);
-
 
1110
	    else
-
 
1111
		axis_base =  GConvertX(1.0, outer, NFC, dd)
-
 
1112
		    + GConvertXUnits(line, LINES, NFC, dd);
-
 
1113
	    if (R_FINITE(dd->gp.tck))
1097
	    /* The R(ight) way of doing ticks (using "tcl", not "tck") */
1114
		axis_tick = axis_base - GConvertXUnits(dd->gp.tck, NPC, NFC, dd);
-
 
1115
	    else
-
 
1116
		axis_tick = axis_base - GConvertXUnits(dd->gp.tcl, LINES, NFC, dd);
-
 
1117
	}
-
 
1118
	if (doticks) {
-
 
1119
	    dd->gp.col = fg;
-
 
1120
            GLine(axis_base, axis_low, axis_base, axis_high, NFC, dd);
1098
	    for (i = 0; i < n; i++) {
1121
	    for (i = 0; i < n; i++) {
1099
		y = REAL(at)[i];
1122
		y = REAL(at)[i];
1100
		if (low <= y && y <= high) {
1123
		if (low <= y && y <= high) {
-
 
1124
		    y = GConvertY(y, USER, NFC, dd);
1101
		    GLine(y, 0, y, -dd->gp.tcl, ytckCoords, dd);
1125
		    GLine(axis_base, y, axis_tick, y, NFC, dd);
1102
		}
1126
		}
1103
	    }
1127
	    }
1104
	}
1128
	}
-
 
1129
	/* Tickmark labels. */
1105
	dd->gp.col = dd->gp.colaxis;
1130
	dd->gp.col = dd->gp.colaxis;
1106
	/* labels */
-
 
1107
	gap = GStrWidth("m", INCHES, dd);
1131
	gap = GStrWidth("m", INCHES, dd);
1108
	gap = GConvertYUnits(gap, INCHES, NFC, dd);
1132
	gap = GConvertYUnits(gap, INCHES, NFC, dd);
1109
	tlast = -1.0;
1133
	tlast = -1.0;
1110
	if (dd->gp.las == 1 || dd->gp.las == 2) {
1134
	if (dd->gp.las == 1 || dd->gp.las == 2) {
1111
	    if (side == 2) dd->gp.adj = 1;
1135
	    dd->gp.adj = (side == 2) ? 1 : 0;
1112
	    else dd->gp.adj = 0;
-
 
1113
	}
1136
	}
1114
	else dd->gp.adj = 0.5;
1137
	else dd->gp.adj = 0.5;
-
 
1138
	if (side == 2) {
-
 
1139
	    axis_lab = - axis_base
-
 
1140
		+ GConvertXUnits(dd->gp.mgp[1], LINES, NFC, dd)
-
 
1141
		+ GConvertX(0.0, NPC, NFC, dd);
1115
 
1142
	}
-
 
1143
	else {
-
 
1144
	    axis_lab = axis_base
-
 
1145
		+ GConvertXUnits(dd->gp.mgp[1], LINES, NFC, dd)
-
 
1146
		- GConvertX(1.0, NPC, NFC, dd);
-
 
1147
	}
-
 
1148
	axis_lab = GConvertXUnits(axis_lab, NFC, LINES, dd);
1116
	for (i = 0; i < n; i++) {
1149
	for (i = 0; i < n; i++) {
1117
	    y = REAL(at)[i];
1150
	    y = REAL(at)[i];
1118
	    if (!R_FINITE(y)) continue;
1151
	    if (!R_FINITE(y)) continue;
1119
	    tempx = x; tempy = y;
-
 
1120
	    GConvert(&tempx, &tempy, USER, NFC, dd);
1152
	    temp = GConvertY(y, USER, NFC, dd);
1121
	    if (dolabels) {
1153
	    if (dolabels) {
1122
		/* clip tick labels to user coordinates */
1154
		/* Clip tick labels to user coordinates. */
1123
		if (y > low && y < high) {
1155
		if (y > low && y < high) {
1124
		    if (isExpression(lab)) {
1156
		    if (isExpression(lab)) {
1125
			GMMathText(VECTOR(lab)[ind[i]], side,
1157
			GMMathText(VECTOR(lab)[ind[i]], side,
1126
				   dd->gp.mgp[1], 0, y, dd->gp.las, dd);
1158
				   axis_lab, 0, y, dd->gp.las, dd);
1127
		    }
1159
		    }
1128
		    else {
1160
		    else {
1129
			labw = GStrWidth(CHAR(STRING(lab)[ind[i]]),
1161
			labw = GStrWidth(CHAR(STRING(lab)[ind[i]]), INCHES, dd);
1130
					 INCHES, dd);
-
 
1131
			labw = GConvertYUnits(labw, INCHES, NFC, dd);
1162
			labw = GConvertYUnits(labw, INCHES, NFC, dd);
1132
			tnew = tempy - 0.5 * labw;
1163
			tnew = temp - 0.5 * labw;
1133
			/* Check room for perpendicular labels: */
1164
			/* Check room for perpendicular labels. */
1134
			if (dd->gp.las == 1 || dd->gp.las == 2 ||
1165
			if (dd->gp.las == 1 || dd->gp.las == 2 ||
1135
			    tnew - tlast >= gap) {
1166
			    tnew - tlast >= gap) {
1136
			    GMtext(CHAR(STRING(lab)[ind[i]]), side,
1167
			    GMtext(CHAR(STRING(lab)[ind[i]]), side,
1137
				   dd->gp.mgp[1], 0, y,
-
 
1138
				   dd->gp.las, dd);
1168
				   axis_lab, 0, y, dd->gp.las, dd);
1139
			    tlast = tempy + 0.5 *labw;
1169
			    tlast = temp + 0.5 *labw;
1140
			}
1170
			}
1141
		    }
1171
		    }
1142
		}
1172
		}
1143
	    }
1173
	    }
1144
	}
1174
	}
Line 2144... Line 2174...
2144
   annotation for plots. */
2174
   annotation for plots. */
2145
 
2175
 
2146
SEXP do_title(SEXP call, SEXP op, SEXP args, SEXP env)
2176
SEXP do_title(SEXP call, SEXP op, SEXP args, SEXP env)
2147
{
2177
{
2148
    SEXP Main, xlab, ylab, sub;
2178
    SEXP Main, xlab, ylab, sub;
2149
    double adj, cex, offset;
2179
    double adj, adjy, cex, offset, line, hpos, vpos, where;
2150
    int col, font, vfont;
2180
    int col, font, vfont, outer;
2151
    int i, n;
2181
    int i, n;
2152
    SEXP originalArgs = args;
2182
    SEXP originalArgs = args;
2153
    DevDesc *dd = CurrentDevice();
2183
    DevDesc *dd = CurrentDevice();
2154
 
2184
 
2155
    GCheckState(dd);
2185
    GCheckState(dd);
2156
 
2186
 
2157
    if (length(args) < 4) errorcall(call, "too few arguments");
2187
    if (length(args) < 6) errorcall(call, "too few arguments");
2158
 
2188
 
2159
    Main = sub = xlab = ylab = R_NilValue;
2189
    Main = sub = xlab = ylab = R_NilValue;
2160
 
2190
 
2161
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2191
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2162
	Main = CAR(args);
2192
	Main = CAR(args);
Line 2172... Line 2202...
2172
 
2202
 
2173
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2203
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2174
	ylab = CAR(args);
2204
	ylab = CAR(args);
2175
    args = CDR(args);
2205
    args = CDR(args);
2176
 
2206
 
-
 
2207
    line = asReal(CAR(args));
-
 
2208
    args = CDR(args);
-
 
2209
 
-
 
2210
    outer = asLogical(CAR(args));
-
 
2211
    if (outer == NA_LOGICAL) outer = 0;
-
 
2212
    args = CDR(args);    
-
 
2213
 
2177
    GSavePars(dd);
2214
    GSavePars(dd);
2178
    RecordGraphicsCall(call);
2215
    RecordGraphicsCall(call);
2179
    ProcessInlinePars(args, dd);
2216
    ProcessInlinePars(args, dd);
2180
 
2217
 
2181
    /* override par("xpd") and force clipping to figure region */
2218
    /* override par("xpd") and force clipping to figure region */
2182
    /* NOTE: don't override to _reduce_ clipping region */
2219
    /* NOTE: don't override to _reduce_ clipping region */
2183
    if (dd->gp.xpd < 1)
2220
    if (dd->gp.xpd < 1)
2184
	dd->gp.xpd = 1;
2221
	dd->gp.xpd = 1;
2185
 
2222
    if (outer)
-
 
2223
	dd->gp.xpd = 2;
2186
    adj = dd->gp.adj;
2224
    adj = dd->gp.adj;
2187
 
2225
 
2188
    GMode(1, dd);
2226
    GMode(1, dd);
2189
    if (Main != R_NilValue) {
2227
    if (Main != R_NilValue) {
2190
	cex = dd->gp.cexmain;
2228
	cex = dd->gp.cexmain;
Line 2192... Line 2230...
2192
	font = dd->gp.fontmain;
2230
	font = dd->gp.fontmain;
2193
	GetTextArg(call, Main, &Main, &col, &cex, &font, &vfont);
2231
	GetTextArg(call, Main, &Main, &col, &cex, &font, &vfont);
2194
	dd->gp.col = col;
2232
	dd->gp.col = col;
2195
	dd->gp.cex = dd->gp.cexbase * cex;
2233
	dd->gp.cex = dd->gp.cexbase * cex;
2196
	dd->gp.font = font;
2234
	dd->gp.font = font;
-
 
2235
	if (outer) {
-
 
2236
	    if (R_FINITE(line)) {
-
 
2237
		vpos = line;
-
 
2238
		adjy = 0;
-
 
2239
	    }
-
 
2240
	    else {
-
 
2241
		vpos = 0.5 * dd->gp.oma[2];
-
 
2242
		adjy = 0.5;
-
 
2243
	    }
-
 
2244
	    hpos = adj;
-
 
2245
	    where = OMA3;
-
 
2246
	}
-
 
2247
	else {
-
 
2248
	    if (R_FINITE(line)) {
-
 
2249
		vpos = line;
-
 
2250
		adjy = 0;
-
 
2251
	    }
-
 
2252
	    else {
-
 
2253
		vpos = 0.5 * dd->gp.mar[2];
-
 
2254
		adjy = 0.5;
-
 
2255
	    }
-
 
2256
	    hpos = GConvertX(adj, NPC, USER, dd);
-
 
2257
	    where = MAR3;
-
 
2258
	}
2197
	if (isExpression(Main)) {
2259
	if (isExpression(Main)) {
2198
	    GMathText(xNPCtoUsr(adj, dd), 0.5*dd->gp.mar[2], MAR3,
2260
	    GMathText(hpos, vpos, where,
2199
		      VECTOR(Main)[0], adj, 0.5, 0.0, dd);
2261
		      VECTOR(Main)[0], adj, adjy, 0.0, dd);
2200
	}
2262
	}
2201
	else {
2263
	else {
2202
	  n = length(Main);
2264
	  n = length(Main);
2203
	  offset = 0.5 * (n - 1) + 0.5 * dd->gp.mar[2];
2265
	  offset = 0.5 * (n - 1) + vpos;
2204
	  for (i = 0; i < n; i++)
2266
	  for (i = 0; i < n; i++)
2205
	    GText(xNPCtoUsr(adj, dd), offset - i, MAR3,
2267
	      GText(hpos, offset - i, where,
2206
		  CHAR(STRING(Main)[i]), adj, 0.5, 0.0, dd);
2268
		    CHAR(STRING(Main)[i]), adj, adjy, 0.0, dd);
2207
	}
2269
	}
2208
    }
2270
    }
2209
    if (sub != R_NilValue) {
2271
    if (sub != R_NilValue) {
2210
	cex = dd->gp.cexsub;
2272
	cex = dd->gp.cexsub;
2211
	col = dd->gp.colsub;
2273
	col = dd->gp.colsub;
2212
	font = dd->gp.fontsub;
2274
	font = dd->gp.fontsub;
2213
	GetTextArg(call, sub, &sub, &col, &cex, &font, &vfont);
2275
	GetTextArg(call, sub, &sub, &col, &cex, &font, &vfont);
2214
	dd->gp.col = col;
2276
	dd->gp.col = col;
2215
	dd->gp.cex = dd->gp.cexbase * cex;
2277
	dd->gp.cex = dd->gp.cexbase * cex;
2216
	dd->gp.font = font;
2278
	dd->gp.font = font;
-
 
2279
	if (R_FINITE(line))
-
 
2280
	    vpos = line;
-
 
2281
	else
-
 
2282
	    vpos = dd->gp.mgp[0] + 1;
-
 
2283
	if (outer) {
-
 
2284
	    hpos = adj;
-
 
2285
	    where = 1;
-
 
2286
	}
-
 
2287
	else {
-
 
2288
	    hpos = GConvertX(adj, NPC, USER, dd);
-
 
2289
	    where = 0;
-
 
2290
	}
2217
	if (isExpression(sub))
2291
	if (isExpression(sub))
2218
	    GMMathText(VECTOR(sub)[0], 1, dd->gp.mgp[0] + 1.0, 0,
2292
	    GMMathText(VECTOR(sub)[0], 1, vpos, where,
2219
		       xNPCtoUsr(adj, dd), 0, dd);
2293
		       hpos, 0, dd);
2220
	else {
2294
	else {
2221
	    n = length(sub);
2295
	    n = length(sub);
2222
	    for (i = 0; i < n; i++)
2296
	    for (i = 0; i < n; i++)
2223
		GMtext(CHAR(STRING(sub)[i]), 1, dd->gp.mgp[0] + 1.0, 0,
2297
		GMtext(CHAR(STRING(sub)[i]), 1, vpos, where,
2224
		   xNPCtoUsr(adj, dd), 0, dd);
2298
		       hpos, 0, dd);
2225
	}
2299
	}
2226
    }
2300
    }
2227
    if (xlab != R_NilValue) {
2301
    if (xlab != R_NilValue) {
2228
	cex = dd->gp.cexlab;
2302
	cex = dd->gp.cexlab;
2229
	col = dd->gp.collab;
2303
	col = dd->gp.collab;
2230
	font = dd->gp.fontlab;
2304
	font = dd->gp.fontlab;
2231
	GetTextArg(call, xlab, &xlab, &col, &cex, &font, &vfont);
2305
	GetTextArg(call, xlab, &xlab, &col, &cex, &font, &vfont);
2232
	dd->gp.cex = dd->gp.cexbase * cex;
2306
	dd->gp.cex = dd->gp.cexbase * cex;
2233
	dd->gp.col = col;
2307
	dd->gp.col = col;
2234
	dd->gp.font = font;
2308
	dd->gp.font = font;
-
 
2309
	if (R_FINITE(line))
-
 
2310
	    vpos = line;
-
 
2311
	else
-
 
2312
	    vpos = dd->gp.mgp[0];
-
 
2313
	if (outer) {
-
 
2314
	    hpos = adj;
-
 
2315
	    where = 1;
-
 
2316
	}
-
 
2317
	else {
-
 
2318
	    hpos = GConvertX(adj, NPC, USER, dd);
-
 
2319
	    where = 0;
-
 
2320
	}
2235
	if (isExpression(xlab))
2321
	if (isExpression(xlab))
2236
	    GMMathText(VECTOR(xlab)[0], 1, dd->gp.mgp[0], 0,
2322
	    GMMathText(VECTOR(xlab)[0], 1, vpos + i, where,
2237
		       xNPCtoUsr(adj, dd), 0, dd);
2323
		       hpos, 0, dd);
2238
	else {
2324
	else {
2239
	    n = length(xlab);
2325
	    n = length(xlab);
2240
	    for (i = 0; i < n; i++)
2326
	    for (i = 0; i < n; i++)
2241
		GMtext(CHAR(STRING(xlab)[i]), 1, dd->gp.mgp[0] + i, 0,
2327
		GMtext(CHAR(STRING(xlab)[i]), 1, vpos + i, where,
2242
		   xNPCtoUsr(adj, dd), 0, dd);
2328
		   hpos, 0, dd);
2243
	}
2329
	}
2244
    }
2330
    }
2245
    if (ylab != R_NilValue) {
2331
    if (ylab != R_NilValue) {
2246
	cex = dd->gp.cexlab;
2332
	cex = dd->gp.cexlab;
2247
	col = dd->gp.collab;
2333
	col = dd->gp.collab;
2248
	font = dd->gp.fontlab;
2334
	font = dd->gp.fontlab;
2249
	GetTextArg(call, ylab, &ylab, &col, &cex, &font, &vfont);
2335
	GetTextArg(call, ylab, &ylab, &col, &cex, &font, &vfont);
2250
	dd->gp.cex = dd->gp.cexbase * cex;
2336
	dd->gp.cex = dd->gp.cexbase * cex;
2251
	dd->gp.col = col;
2337
	dd->gp.col = col;
2252
	dd->gp.font = font;
2338
	dd->gp.font = font;
-
 
2339
	if (R_FINITE(line))
-
 
2340
	    vpos = line;
-
 
2341
	else
-
 
2342
	    vpos = dd->gp.mgp[0];
-
 
2343
	if (outer) {
-
 
2344
	    hpos = adj;
-
 
2345
	    where = 1;
-
 
2346
	}
-
 
2347
	else {
-
 
2348
	    hpos = GConvertY(adj, NPC, USER, dd);
-
 
2349
	    where = 0;
-
 
2350
	}
2253
	if (isExpression(ylab))
2351
	if (isExpression(ylab))
2254
	    GMMathText(VECTOR(ylab)[0], 2, dd->gp.mgp[0], 0,
2352
	    GMMathText(VECTOR(ylab)[0], 2, vpos, where,
2255
		       yNPCtoUsr(adj, dd), 0, dd);
2353
		       hpos, 0, dd);
2256
	else {
2354
	else {
2257
	    n = length(ylab);
2355
	    n = length(ylab);
2258
	    for (i = 0; i < n; i++)
2356
	    for (i = 0; i < n; i++)
2259
		GMtext(CHAR(STRING(ylab)[i]), 2, dd->gp.mgp[0] - i, 0,
2357
		GMtext(CHAR(STRING(ylab)[i]), 2, vpos - i, where,
2260
		       yNPCtoUsr(adj, dd), 0, dd);
2358
		       hpos, 0, dd);
2261
	}
2359
	}
2262
    }
2360
    }
2263
    GMode(0, dd);
2361
    GMode(0, dd);
2264
    GRestorePars(dd);
2362
    GRestorePars(dd);
2265
    /* NOTE: only record operation if no "error"  */
2363
    /* NOTE: only record operation if no "error"  */