const char * UsageLines [] = {
"p6addgraticule: Adds horizontal and vertical lines with labels",
" at sides, based on specified edges and intervals",
"Reads P6 PPM from standard input, writes to standard output.",
"",
"Takes 6 arguments, all integers:",
" bottom",
" left",
" right",
" top",
" across interval (between vertical lines)",
" up interval (between horizontal lines)",
"",
"The labels on the horizontal lines will be interpolated",
" between the specified top and bottom edge values.",
"",
"The labels on the vertical lines will be interpolated",
" between the specified left and right edge values.",
"",
"April 2, 2022. For latest, see:",
"
gopher://sdf.org/1/users/julianbr",
};
const int NumUsageLines = sizeof (UsageLines) / sizeof (UsageLines [0] );
#define NUM_ROWS 9
#define MIDDLE_ROW 4
#define NUM_COLUMNS 5
#define SPACING 1
#define MARGIN 30
#define MAX_DIGITS_ACROSS 8
#define MAX_DIGITS_UP 7
#define HORIZONTAL_R 255
#define HORIZONTAL_G 0
#define HORIZONTAL_B 0
#define VERTICAL_R 0
#define VERTICAL_G 0
#define VERTICAL_B 255
const int (DigitOutlines [] ) [NUM_ROWS] = {
{14, 17, 17, 17, 17, 17, 17, 17, 14 },
{12, 4, 4, 4, 4, 4, 4, 4, 14 },
{14, 17, 1, 1, 1, 1, 14, 16, 31 },
{14, 17, 1, 1, 14, 1, 1, 17, 14 },
{6, 10, 10, 18, 31, 2, 2, 2, 2 },
{31, 16, 16, 16, 30, 1, 1, 17, 14 },
{14, 17, 16, 16, 30, 17, 17, 17, 14 },
{15, 17, 1, 2, 2, 2, 4, 4, 4 },
{14, 17, 17, 17, 14, 17, 17, 17, 14 },
{14, 17, 17, 17, 15, 1, 1, 1, 2 },
};
#include <stdio.h>
void AddGraticule (
int bottom, int left, int right, int top,
int AcrossInterval, int UpInterval)
{
int i, j, k, r, g, b, width, height, maxval, NumDigitsAcross, NumDigitsUp;
int Limit, start, finish, across, up;
int AcrossOffset, UpOffset, DistanceAcross, DistanceUp;
int HorizontalClearance, VerticalClearance;
int MinLeft, MaxLeft, MinBottom, MaxBottom;
int MinRight, MaxRight, MinTop, MaxTop;
int DigitNum, Label, Row, Column, Multiple;
int PrintHorizontalLine, PrintVerticalLine, ProtectFromLine, AlreadyPrinted;
int LeftLabel, RightLabel, TopLabel, BottomLabel;
if (
getchar () != 'P'
|| getchar () != '6'
|| getchar () != '\n'
|| scanf ("%d %d", & width, & height) != 2
|| getchar () != '\n'
|| scanf ("%d", & maxval) != 1
|| maxval != 255
|| getchar () != '\n'
) {
fprintf (stderr, "***p6addgraticule: Improper input file, must be");
fprintf (stderr, " P6 image maxval 255\n");
return;
}
NumDigitsAcross = 1;
Limit = 10;
while (NumDigitsAcross < MAX_DIGITS_ACROSS
&& (left <= - Limit || right >= Limit) ) {
NumDigitsAcross++;
Limit *= 10;
}
NumDigitsUp = 1;
Limit = 10;
while (NumDigitsUp < MAX_DIGITS_UP
&& (bottom <= - Limit || top >= Limit) ) {
NumDigitsUp++;
Limit *= 10;
}
AcrossOffset = left/AcrossInterval*AcrossInterval;
if (left < AcrossInterval)
AcrossOffset = - (AcrossInterval - left - 1)/AcrossInterval*AcrossInterval;
UpOffset = bottom/UpInterval*UpInterval;
if (bottom < UpInterval)
UpOffset = - (UpInterval - bottom - 1)/UpInterval*UpInterval;
DistanceAcross = right - left;
DistanceUp = top - bottom;
VerticalClearance = NumDigitsAcross*(NUM_COLUMNS + SPACING);
HorizontalClearance = NumDigitsUp*(NUM_COLUMNS + SPACING);
j = MARGIN + MIDDLE_ROW - 1;
MinBottom = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
j = width - MARGIN - HorizontalClearance - NUM_ROWS + MIDDLE_ROW;
MaxBottom = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
i = MARGIN + VerticalClearance + NUM_ROWS - MIDDLE_ROW - 1;
MinLeft = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
i = height - MARGIN - MIDDLE_ROW;
MaxLeft = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
i = MARGIN + NUM_ROWS - MIDDLE_ROW - 1;
MinRight = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
i = height - VerticalClearance - MARGIN - MIDDLE_ROW - 1;
MaxRight = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
j = MARGIN + HorizontalClearance + MIDDLE_ROW - 1;
MinTop = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
j = width - MARGIN - NUM_ROWS + MIDDLE_ROW;
MaxTop = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
printf ("P6\n%d %d\n%d\n", width, height, maxval);
r = getchar ();
for (i = height - 1; i >= 0; i--) {
for (j = 0; j < width; j++) {
if (r == EOF)
g = EOF;
else
g = getchar ();
if (g == EOF)
b = EOF;
else
b = getchar ();
ProtectFromLine = 0;
AlreadyPrinted = 0;
/* Label for Horizontal Line */
DigitNum = -1;
LeftLabel = 0;
RightLabel = 0;
if (j >= MARGIN - SPACING && j < MARGIN + HorizontalClearance) {
if (j >= MARGIN) {
DigitNum = (j - MARGIN)/(NUM_COLUMNS + SPACING);
Column = (j - MARGIN)%(NUM_COLUMNS + SPACING);
}
LeftLabel = 1;
}
else if (j >= width - MARGIN - HorizontalClearance
&& j < width - MARGIN + SPACING) {
if (j >= width - MARGIN - HorizontalClearance + SPACING) {
DigitNum = (j - width + MARGIN + HorizontalClearance - SPACING)
/(NUM_COLUMNS + SPACING);
Column = (j - width + MARGIN + HorizontalClearance - SPACING)
%(NUM_COLUMNS + SPACING);
}
RightLabel = 1;
}
if (LeftLabel || RightLabel) {
Multiple = 1;
for (k = DigitNum + 1; k < NumDigitsUp; k++)
Multiple *= 10;
Row = - SPACING - 1;
up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
start = (bottom + up*DistanceUp/height - UpOffset)/UpInterval;
Row++;
up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
finish = (bottom + up*DistanceUp/height - UpOffset)/UpInterval;
while (Row < NUM_ROWS + SPACING && finish <= start) {
Row++;
up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
finish = (bottom + up*DistanceUp/height - UpOffset)/UpInterval;
}
}
if (!AlreadyPrinted &&
((LeftLabel && finish > MinLeft && finish <= MaxLeft)
|| (RightLabel && finish > MinRight && finish <= MaxRight) )
) {
if (Row < NUM_ROWS + SPACING)
ProtectFromLine = 1;
if (Row >= 0 && Row < NUM_ROWS
&& DigitNum >= 0 && DigitNum < NumDigitsUp
&& Column < NUM_COLUMNS) {
Label = finish*UpInterval + UpOffset;
if (Label < 0)
Label = - Label;
if ((DigitOutlines [Label/Multiple%10] [Row] )
& (1 << (NUM_COLUMNS - Column - 1) ) ) {
putchar (HORIZONTAL_R);
putchar (HORIZONTAL_G);
putchar (HORIZONTAL_B);
AlreadyPrinted = 1;
}
}
}
/* Label for Vertical Line */
DigitNum = -1;
BottomLabel = 0;
TopLabel = 0;
if (i >= MARGIN - SPACING
&& i < MARGIN + VerticalClearance) {
if (i >= MARGIN) {
DigitNum = (i - MARGIN)/(NUM_COLUMNS + SPACING);
Column = (i - MARGIN)%(NUM_COLUMNS + SPACING);
}
BottomLabel = 1;
}
else if (i >= height - MARGIN - VerticalClearance
&& i < height - MARGIN + SPACING) {
if (i >= height - MARGIN - VerticalClearance + SPACING) {
DigitNum = (i - height + MARGIN + VerticalClearance - SPACING)
/(NUM_COLUMNS + SPACING);
Column = (i - height + MARGIN + VerticalClearance - SPACING)
%(NUM_COLUMNS + SPACING);
}
TopLabel = 1;
}
if (BottomLabel || TopLabel) {
Multiple = 1;
for (k = DigitNum + 1; k < NumDigitsAcross; k++)
Multiple *= 10;
Row = NUM_ROWS + SPACING;
across = j + MIDDLE_ROW - Row;
start = (left + across*DistanceAcross/width - AcrossOffset)/AcrossInterval;
Row--;
across = j + MIDDLE_ROW - Row;
finish = (left + across*DistanceAcross/width - AcrossOffset)/AcrossInterval;
while (Row > - SPACING - 1 && finish <= start) {
Row--;
across = j + MIDDLE_ROW - Row;
finish = (left + across*DistanceAcross/width - AcrossOffset)/AcrossInterval;
}
}
if (!AlreadyPrinted && (
(BottomLabel && finish > MinBottom && finish <= MaxBottom)
|| (TopLabel && finish > MinTop && finish <= MaxTop) )
) {
if (Row >= - SPACING)
ProtectFromLine = 1;
if (Row >= 0 && Row < NUM_ROWS
&& DigitNum >= 0 && DigitNum < NumDigitsAcross
&& Column < NUM_COLUMNS) {
Label = finish*AcrossInterval + AcrossOffset;
if (Label < 0)
Label = - Label;
if ((DigitOutlines [Label/Multiple%10] [Row] )
& (1 << (NUM_COLUMNS - Column - 1) ) ) {
putchar (VERTICAL_R);
putchar (VERTICAL_G);
putchar (VERTICAL_B);
AlreadyPrinted = 1;
}
}
}
if (!ProtectFromLine && !AlreadyPrinted) {
PrintHorizontalLine = 0;
PrintVerticalLine = 0;
if ((bottom + i*DistanceUp/height - UpOffset)/UpInterval
> (bottom + (i - 1 )*DistanceUp/height - UpOffset)/UpInterval)
PrintHorizontalLine = 1;
if ((left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval
> (left + (j - 1)*DistanceAcross/width - AcrossOffset)/AcrossInterval)
PrintVerticalLine = 1;
if (PrintHorizontalLine && !PrintVerticalLine) {
putchar (HORIZONTAL_R);
putchar (HORIZONTAL_G);
putchar (HORIZONTAL_B);
AlreadyPrinted = 1;
}
if (PrintVerticalLine && !PrintHorizontalLine) {
putchar (VERTICAL_R);
putchar (VERTICAL_G);
putchar (VERTICAL_B);
AlreadyPrinted = 1;
}
}
if (!AlreadyPrinted) {
putchar (r);
putchar (g);
putchar (b);
}
if (b == EOF)
r = EOF;
else
r = getchar ();
}
}
if (r != EOF)
fprintf (stderr, "***p6addgraticule: Too much image data.\n");
if (b == EOF)
fprintf (stderr, "***p6addgraticule: Not enough image data.\n");
}
int main (int argc, char * argv [] )
{
char c;
int i, ok;
int bottom, left, right, top, AcrossInterval, UpInterval;
if (argc == 1) {
for (i = 0; i < NumUsageLines; i++)
printf ("%s\n", UsageLines [i] );
}
else if (argc != 7) {
fprintf (stderr, "Usage: %s", argv [0] );
fprintf (stderr, " (bottom) (left) (right) (top)\n");
fprintf (stderr, "\t(interval across between vertical lines)\n");
fprintf (stderr, "\t(interval down between horizontal lines)\n");
}
else {
ok = 1;
if (sscanf (argv [1], "%d%c", & bottom, & c) != 1) {
fprintf (stderr, "***p6addgraticule: Expecting number for bottom,");
fprintf (stderr, " found \"%s\".\n", argv [1] );
ok = 0;
}
if (sscanf (argv [2], "%d%c", & left, & c) != 1) {
fprintf (stderr, "***p6addgraticule: Expecting number for left,");
fprintf (stderr, " found \"%s\".\n", argv [2] );
ok = 0;
}
if (sscanf (argv [3], "%d%c", & right, & c) != 1) {
fprintf (stderr, "***p6addgraticule: Expecting number for right,");
fprintf (stderr, " found \"%s\".\n", argv [3] );
ok = 0;
}
if (sscanf (argv [4], "%d%c", & top, & c) != 1) {
fprintf (stderr, "***p6addgraticule: Expecting number for top,");
fprintf (stderr, " found \"%s\".\n", argv [4] );
ok = 0;
}
if (sscanf (argv [5], "%d%c", & AcrossInterval, & c) != 1) {
fprintf (stderr, "***p6addgraticule: Expecting number for across interval,");
fprintf (stderr, " found \"%s\".\n", argv [5] );
ok = 0;
}
if (sscanf (argv [6], "%d%c", & UpInterval, & c) != 1) {
fprintf (stderr, "***p6addgraticule: Expecting number for up interval,");
fprintf (stderr, " found \"%s\".\n", argv [6] );
ok = 0;
}
if (ok) {
if (left > right) {
left = - left;
right = - right;
}
if (bottom > top) {
bottom = - bottom;
top = - top;
}
AddGraticule (bottom, left, right, top, AcrossInterval, UpInterval);
}
}
return 0;
}