@@ -331,8 +331,12 @@ =head1 OPTIONS
331331
332332=item scaleX, scaleY (Default: C<< scaleX => 1, scaleY => 1 >>)
333333
334- These are the scale of the ticks on the x and y axes. That is the distance between two
335- successive ticks on the axis (including both major and minor ticks).
334+ These are the scale of the tick distances on the x and y axes. That is the distance between two
335+ successive major ticks on the axis will be the product of the ticks distance and the scale.
336+ This is usually used in conjunction with the C<scaleSymbolX > and C<scaleSymbolY > options. For
337+ example, if C<ticksDistanceX > is 2, C<scaleX > is 3, and C<scaleSymbolX > is 'a', then the first
338+ positive major x axis tick will occur 6 units to the right and be labeled '2a', and the next
339+ major x axis tick will occur 12 units to the right and be labeled '4a'.
336340
337341=item scaleSymbolX, scaleSymbolY (Default: C<< scaleSymbolX => '', scaleSymbolY => '' >>)
338342
@@ -1195,6 +1199,47 @@ sub generateHTMLAnswerGraph {
11951199 END_SCRIPT
11961200}
11971201
1202+ # This is essentially copied from contextFraction.pl.
1203+ sub continuedFraction {
1204+ my ($x ) = @_ ;
1205+
1206+ my $step = $x ;
1207+ my $n = int ($step );
1208+ my ($h0 , $h1 , $k0 , $k1 ) = (1, $n , 0, 1);
1209+
1210+ while ($step != $n ) {
1211+ $step = 1 / ($step - $n );
1212+ $n = int ($step );
1213+ my ($newh , $newk ) = ($n * $h1 + $h0 , $n * $k1 + $k0 );
1214+ last if $newk > 10**8; # Bail if the denominator is skyrocketing out of control.
1215+ ($h0 , $h1 , $k0 , $k1 ) = ($h1 , $newh , $k1 , $newk );
1216+ }
1217+
1218+ return ($h1 , $k1 );
1219+ }
1220+
1221+ sub formatTickLabelText {
1222+ my ($self , $value , $axis ) = @_ ;
1223+ my $coordinateHintsType = $self -> {" coordinateHintsType$axis " } // $self -> {coordinateHintsType };
1224+ if ($coordinateHintsType eq ' fraction' || $coordinateHintsType eq ' mixed' ) {
1225+ my ($num , $den ) = continuedFraction(abs($value ));
1226+ if ($num && $den != 1 && !($num == 1 && $den == 1)) {
1227+ if ($coordinateHintsType eq ' fraction' || $num < $den ) {
1228+ $value = ($value < 0 ? ' -' : ' ' ) . " \\ frac{$num }{$den }" ;
1229+ } else {
1230+ my $int = int ($num / $den );
1231+ my $properNum = $num % $den ;
1232+ $value = ($value < 0 ? ' -' : ' ' ) . " $int \\ frac{$properNum }{$den }" ;
1233+ }
1234+ }
1235+ }
1236+ my $scaleSymbol = $self -> {" scaleSymbol$axis " } // ' ' ;
1237+ return
1238+ $value eq ' 0' ? ' 0'
1239+ : $scaleSymbol ? ($value eq ' 1' ? $scaleSymbol : $value eq ' -1' ? " -$scaleSymbol " : " $value$scaleSymbol " )
1240+ : $value ;
1241+ }
1242+
11981243sub generateTeXGraph {
11991244 my ($self , %options ) = @_ ;
12001245
@@ -1203,7 +1248,7 @@ sub generateTeXGraph {
12031248
12041249 return &{ $self -> {printGraph } } if ref ($self -> {printGraph }) eq ' CODE' ;
12051250
1206- my @size = $self -> {numberLine } ? (500, 100 ) : (500, 500);
1251+ my @size = $self -> {numberLine } ? (500, 110 ) : (500, 500);
12071252
12081253 my $graph = main::createTikZImage();
12091254 $graph -> tikzLibraries(' arrows.meta' );
@@ -1264,32 +1309,71 @@ sub generateTeXGraph {
12641309 }
12651310
12661311 # Horizontal axis ticks and labels
1267- my @xTicks = grep { $_ < $self -> {bBox }[2] }
1268- map { $_ * $self -> {ticksDistanceX } } (1 .. $self -> {bBox }[2] / $self -> {ticksDistanceX });
1269- push (@xTicks ,
1312+ my @xTicks =
12701313 grep { $_ > $self -> {bBox }[0] }
1271- map { -$_ * $self -> {ticksDistanceX } } (1 .. -$self -> {bBox }[0] / $self -> {ticksDistanceX }));
1314+ map { -$_ * $self -> {ticksDistanceX } * $self -> {scaleX } }
1315+ reverse (1 .. -$self -> {bBox }[0] / ($self -> {ticksDistanceX } * $self -> {scaleX }));
1316+ my $numNegative = @xTicks ;
12721317 # Add zero if this is a number line and 0 is in the given range.
1273- push (@xTicks , 0) if ($self -> {numberLine } && $self -> {bBox }[2] > 0 && $self -> {bBox }[0] < 0);
1318+ push (@xTicks , 0) if $self -> {numberLine } && $self -> {bBox }[2] > 0 && $self -> {bBox }[0] < 0;
1319+ push (@xTicks ,
1320+ grep { $_ < $self -> {bBox }[2] }
1321+ map { $_ * $self -> {ticksDistanceX } * $self -> {scaleX } }
1322+ (1 .. $self -> {bBox }[2] / ($self -> {ticksDistanceX } * $self -> {scaleX })));
12741323 my $tickSize = $self -> {numberLine } ? ' 9' : ' 5' ;
12751324 $tikz .=
1276- " \\ foreach \\ x in {"
1277- . join (' ,' , @xTicks )
1278- . " }{\\ draw[thin] (\\ x,${tickSize} pt) -- (\\ x,-${tickSize} pt) node[below]{\\ (\\ x\\ )};}\n "
1325+ " \\ foreach \\ x/\\ label in {"
1326+ . join (' ,' , map { " $_ /" . $self -> formatTickLabelText($_ / $self -> {scaleX }, ' X' ) } @xTicks )
1327+ . " }{\\ draw[thin, opacity = 0.5] (\\ x,${tickSize} pt) -- (\\ x,-${tickSize} pt) "
1328+ . " node[baseline, yshift = -15pt, opacity = 1]{\\ (\\ label\\ )};}\n "
12791329 if (@xTicks );
12801330
1331+ # Add horizontal axis minor ticks.
1332+ splice (@xTicks , $numNegative , 0, 0) if !$self -> {numberLine } || ($self -> {bBox }[0] <= 0 && $self -> {bBox }[2] >= 0);
1333+ unshift (@xTicks , $xTicks [0] - $self -> {ticksDistanceX } * $self -> {scaleX }) if $self -> {bBox }[0] < 0;
1334+ push (@xTicks , $xTicks [-1] + $self -> {ticksDistanceX } * $self -> {scaleX }) if $self -> {bBox }[2] > 0;
1335+ my @xMinorTicks ;
1336+ my $xMinorTickDelta = $self -> {ticksDistanceX } * $self -> {scaleX } / ($self -> {minorTicksX } + 1);
1337+ for my $tickIndex (0 .. $#xTicks - 1) {
1338+ push (@xMinorTicks , map { $xTicks [$tickIndex ] + $_ * $xMinorTickDelta } 1 .. $self -> {minorTicksX });
1339+ }
1340+ $tikz .=
1341+ " \\ foreach \\ x in {"
1342+ . join (' ,' , @xMinorTicks )
1343+ . " }{\\ draw[thin, opacity = 0.5] (\\ x,0) -- (\\ x,-${tickSize} pt);}\n "
1344+ if (@xMinorTicks );
1345+
12811346 # Vertical axis ticks and labels
12821347 unless ($self -> {numberLine }) {
1283- my @yTicks = grep { $_ < $self -> {bBox }[1] }
1284- map { $_ * $self -> {ticksDistanceY } } (1 .. $self -> {bBox }[1] / $self -> {ticksDistanceY });
1285- push (@yTicks ,
1348+ my @yTicks =
12861349 grep { $_ > $self -> {bBox }[3] }
1287- map { -$_ * $self -> {ticksDistanceY } } (1 .. -$self -> {bBox }[3] / $self -> {ticksDistanceY }));
1350+ map { -$_ * $self -> {ticksDistanceY } * $self -> {scaleY } }
1351+ reverse (1 .. -$self -> {bBox }[3] / ($self -> {ticksDistanceY } * $self -> {scaleY }));
1352+ my $numNegative = @yTicks ;
1353+ push (@yTicks ,
1354+ grep { $_ < $self -> {bBox }[1] }
1355+ map { $_ * $self -> {ticksDistanceY } * $self -> {scaleY } }
1356+ (1 .. $self -> {bBox }[1] / ($self -> {ticksDistanceY } * $self -> {scaleY })));
12881357 $tikz .=
1289- " \\ foreach \\ y in {"
1290- . join (' ,' , @yTicks )
1291- . " }{\\ draw[thin] (5pt,\\ y) -- (-5pt,\\ y) node[left]{\$\\ y \$ };}\n "
1358+ " \\ foreach \\ y/ \\ label in {"
1359+ . join (' ,' , map { " $_ / " . $self -> formatTickLabelText( $_ / $self -> { scaleY }, ' Y ' ) } @yTicks )
1360+ . " }{\\ draw[thin, opacity = 0.5 ] (5pt,\\ y) -- (-5pt,\\ y) node[left, opacity = 1 ]{\$\\ label \$ };}\n "
12921361 if (@yTicks );
1362+
1363+ # Add vertical axis minor ticks.
1364+ splice (@yTicks , $numNegative , 0, 0);
1365+ unshift (@yTicks , $yTicks [0] - $self -> {ticksDistanceY } * $self -> {scaleY }) if $self -> {bBox }[3] < 0;
1366+ push (@yTicks , $yTicks [-1] + $self -> {ticksDistanceY } * $self -> {scaleY }) if $self -> {bBox }[1] > 0;
1367+ my @yMinorTicks ;
1368+ my $yMinorTickDelta = $self -> {ticksDistanceY } * $self -> {scaleY } / ($self -> {minorTicksY } + 1);
1369+ for my $tickIndex (0 .. $#yTicks - 1) {
1370+ push (@yMinorTicks , map { $yTicks [$tickIndex ] + $_ * $yMinorTickDelta } 1 .. $self -> {minorTicksY });
1371+ }
1372+ $tikz .=
1373+ " \\ foreach \\ y in {"
1374+ . join (' ,' , @yMinorTicks )
1375+ . " }{\\ draw[thin, opacity = 0.5] (0, \\ y) -- (-5pt, \\ y);}\n "
1376+ if @yMinorTicks ;
12931377 }
12941378
12951379 # Border box
0 commit comments