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; }