static void tickY(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor, unsigned int const tenth) { /*---------------------------------------------------------------------------- Put a tick mark 'tenth' tenths of the way along the Y axis and label it. 'pixels' is the canvas on which to draw it; its dimensions are 'pixcols' by 'pixrows' and has maxval 'maxval'. -----------------------------------------------------------------------------*/ unsigned int const pxrows = pixrows - yBias; unsigned int const tickRow = (tenth * (pxrows - 1)) / 10; /* Pixel row where the top of the tick goes */ unsigned int const tickThickness = Sz(3); /* Thickness of the tick in pixels */ char s[20]; assert(tenth < 10); sprintf(s, "0.%d", 10 - tenth); ppmd_line(pixels, pixcols, pixrows, maxval, B(0, tickRow), B(tickThickness, tickRow), PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-30), tickRow + Sz(5)), Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor); }
static void writeLabel(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, const struct colorSystem * const cs) { pixel rgbcolor; /* color of text */ char sysdesc[256]; PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); snprintfN(sysdesc, sizeof(sysdesc), "System: %s\n" "Primary illuminants (X, Y)\n" " Red: %0.4f, %0.4f\n" " Green: %0.4f, %0.4f\n" " Blue: %0.4f, %0.4f\n" "White point (X, Y): %0.4f, %0.4f", cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen, cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite); sysdesc[sizeof(sysdesc)-1] = '\0'; /* for robustness */ ppmd_text(pixels, pixcols, pixrows, Maxval, pixcols / 3, Sz(24), Sz(12), 0, sysdesc, PPMD_NULLDRAWPROC, (char *) &rgbcolor); }
static void doTextHere(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, const struct drawCommand * const commandP, struct drawState * const drawStateP) { ppmd_text(pixels, cols, rows, maxval, drawStateP->currentPos.x, drawStateP->currentPos.y, commandP->u.textArg.height, commandP->u.textArg.angle, commandP->u.textArg.text, PPMD_NULLDRAWPROC, &drawStateP->color); { int left, top, right, bottom; ppmd_text_box(commandP->u.textArg.height, 0, commandP->u.textArg.text, &left, &top, &right, &bottom); drawStateP->currentPos.x += ROUND((right-left) * cosdeg(commandP->u.textArg.angle)); drawStateP->currentPos.y -= ROUND((right-left) * sindeg(commandP->u.textArg.angle)); } }
static void labelAxes(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor, bool const upvp) { /*---------------------------------------------------------------------------- Label the axes "x" and "y" or "u" and "v". -----------------------------------------------------------------------------*/ unsigned int const pxcols = pixcols - xBias; unsigned int const pxrows = pixrows - yBias; ppmd_text(pixels, pixcols, pixrows, maxval, B((98 * (pxcols - 1)) / 100 - Sz(11), pxrows + Sz(12)), Sz(10), 0, (upvp ? "u'" : "x"), PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-22), (2 * (pxrows - 1)) / 100 + Sz(5)), Sz(10), 0, (upvp ? "v'" : "y"), PPMD_NULLDRAWPROC, (char *) &axisColor); }
static void tickX(pixel ** const pixels, unsigned int const pixcols, unsigned int const pixrows, pixval const maxval, unsigned int const xBias, unsigned int const yBias, pixel const axisColor, unsigned int const tenth) { /*---------------------------------------------------------------------------- Put a tick mark 'tenth' tenths of the way along the X axis and label it. 'pixels' is the canvas on which to draw it; its dimensions are 'pixcols' by 'pixrows' and has maxval 'maxval'. -----------------------------------------------------------------------------*/ unsigned int const pxcols = pixcols - xBias; unsigned int const pxrows = pixrows - yBias; unsigned int const tickCol = (tenth * (pxcols - 1)) / 10; /* Pixel column where the left edge of the tick goes */ unsigned int const tickThickness = Sz(3); /* Thickness of the tick in pixels */ unsigned int const tickBottom = pxrows - Sz(1); /* Pixel row of the bottom of the tick */ char s[20]; assert(tenth < 10); sprintf(s, "0.%u", tenth); ppmd_line(pixels, pixcols, pixrows, maxval, B(tickCol, tickBottom), B(tickCol, tickBottom - tickThickness), PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(tickCol - Sz(11), pxrows + Sz(12)), Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor); }
static void plotMonochromeWavelengths( pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, const struct colorSystem * const cs, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; int x; /* The wavelength we're plotting */ for (x = (upvp? 420 : 450); x <= 650; x += (upvp? 10 : (x > 470 && x < 600) ? 5 : 10)) { pixel rgbcolor; /* Ick. Drop legends that overlap and twiddle position so they appear at reasonable positions with respect to the tongue. */ if (!overlappingLegend(upvp, x)) { double cx, cy, cz, jr, jg, jb, jmax; char wl[20]; int bx = 0, by = 0, tx, ty, r, g, b; int icx, icy; /* Location of the color on the tongue */ if (x < 520) { bx = Sz(-22); by = Sz(2); } else if (x < 535) { bx = Sz(-8); by = Sz(-6); } else { bx = Sz(4); } computeMonochromeColorLocation(x, pxcols, pxrows, upvp, &icx, &icy); /* Draw the tick mark */ PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0)); ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); ppmd_line(pixels, pixcols, pixrows, Maxval, B(icx, icy), B(tx, ty), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* The following flailing about sets the drawing color to the hue corresponding to the pure wavelength (constrained to the display gamut). */ if (upvp) { double up, vp; up = ((double) icx) / (pxcols - 1); vp = 1.0 - ((double) icy) / (pxrows - 1); upvp_to_xy(up, vp, &cx, &cy); cz = 1.0 - (cx + cy); } else { cx = ((double) icx) / (pxcols - 1); cy = 1.0 - ((double) icy) / (pxrows - 1); cz = 1.0 - (cx + cy); } xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); (void) constrain_rgb(&jr, &jg, &jb); /* Scale to max(rgb) = 1 */ jmax = MAX(jr, MAX(jg, jb)); if (jmax > 0) { jr = jr / jmax; jg = jg / jmax; jb = jb / jmax; } /* gamma correct from linear rgb to nonlinear rgb. */ gamma_correct_rgb(cs, &jr, &jg, &jb); r = Maxval * jr; g = Maxval * jg; b = Maxval * jb; PPM_ASSIGN(rgbcolor, (pixval) r, (pixval) g, (pixval) b); sprintf(wl, "%d", x); ppmd_text(pixels, pixcols, pixrows, Maxval, B(icx + bx, icy + by), Sz(6), 0, wl, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } } }
static void plotBlackBodyCurve(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; double t; /* temperature of black body */ pixel rgbcolor; /* color of the curve */ int lx, ly; /* Last point we've drawn on curve so far */ PPM_ASSIGN(rgbcolor, 0, 0, 0); /* Plot black body curve from 1000 to 30000 kelvins. */ for (t = 1000.0; t < 30000.0; t += 50.0) { double lambda, X = 0, Y = 0, Z = 0; int xb, yb; int ix; /* Determine X, Y, and Z for blackbody by summing color match functions over the visual range. */ for (ix = 0, lambda = 380; lambda <= 780.0; ix++, lambda += 5) { double Me; /* Evaluate Planck's black body equation for the power at this wavelength. */ Me = 3.74183e-16 * pow(lambda * 1e-9, -5.0) / (exp(1.4388e-2/(lambda * 1e-9 * t)) - 1.0); X += Me * cie_color_match[ix][0]; Y += Me * cie_color_match[ix][1]; Z += Me * cie_color_match[ix][2]; } if (upvp) { double up, vp; xy_to_upvp(X / (X + Y + Z), Y / (X + Y + Z), &up, &vp); xb = (pxcols - 1) * up; yb = (pxrows - 1) - ((pxrows - 1) * vp); } else { xb = (pxcols - 1) * X / (X + Y + Z); yb = (pxrows - 1) - ((pxrows - 1) * Y / (X + Y + Z)); } if (t > 1000) { ppmd_line(pixels, pixcols, pixrows, Maxval, B(lx, ly), B(xb, yb), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Draw tick mark every 1000 kelvins */ if ((((int) t) % 1000) == 0) { ppmd_line(pixels, pixcols, pixrows, Maxval, B(lx, ly - Sz(2)), B(lx, ly + Sz(2)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Label selected tick marks with decreasing density. */ if (t <= 5000.1 || (t > 5000.0 && ((((int) t) % 5000) == 0) && t != 20000.0)) { char bb[20]; sprintf(bb, "%g", t); ppmd_text(pixels, pixcols, pixrows, Maxval, B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } } } lx = xb; ly = yb; } }
static void drawAxes(pixel ** const pixels, int const pixcols, int const pixrows, pixval const maxval, bool const upvp, int const xBias, int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; pixel rgbcolor; /* Color of axes */ int i; PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); ppmd_line(pixels, pixcols, pixrows, maxval, B(0, 0), B(0, pxrows - 1), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_line(pixels, pixcols, pixrows, maxval, B(0, pxrows - 1), B(pxcols - 1, pxrows - 1), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Draw tick marks on X and Y axes every 0.1 units. Also label axes. */ for (i = 1; i <= 9; i += 1) { char s[20]; /* X axis tick */ sprintf(s, "0.%d", i); ppmd_line(pixels, pixcols, pixrows, maxval, B((i * (pxcols - 1)) / 10, pxrows - Sz(1)), B((i * (pxcols - 1)) / 10, pxrows - Sz(4)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_text(pixels, pixcols, pixrows, maxval, B((i * (pxcols - 1)) / 10 - Sz(11), pxrows + Sz(12)), Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Y axis tick */ sprintf(s, "0.%d", 10 - i); ppmd_line(pixels, pixcols, pixrows, maxval, B(0, (i * (pxrows - 1)) / 10), B(Sz(3), (i * (pxrows - 1)) / 10), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-30), (i * (pxrows - 1)) / 10 + Sz(5)), Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } ppmd_text(pixels, pixcols, pixrows, maxval, B((98 * (pxcols - 1)) / 100 - Sz(11), pxrows + Sz(12)), Sz(10), 0, (upvp ? "u'" : "x"), PPMD_NULLDRAWPROC, (char *) &rgbcolor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-22), (2 * (pxrows - 1)) / 100 + Sz(5)), Sz(10), 0, (upvp ? "v'" : "y"), PPMD_NULLDRAWPROC, (char *) &rgbcolor); }
static void executeScript(struct script * const scriptP, pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval) { struct drawState drawState; unsigned int seq; /* Sequence number of current command (0 = first, etc.) */ struct commandListElt * p; /* Pointer to current element in command list */ initDrawState(&drawState, maxval); for (p = scriptP->commandListHeadP, seq = 0; p; p = p->nextP, ++seq) { const struct drawCommand * const commandP = p->commandP; if (verbose) pm_message("Command %u: %u", seq, commandP->verb); switch (commandP->verb) { case VERB_SETPOS: drawState.currentPos.x = commandP->u.setposArg.x; drawState.currentPos.y = commandP->u.setposArg.y; break; case VERB_SETLINETYPE: ppmd_setlinetype(commandP->u.setlinetypeArg.type); break; case VERB_SETLINECLIP: ppmd_setlineclip(commandP->u.setlineclipArg.clip); break; case VERB_SETCOLOR: drawState.color = ppm_parsecolor2(commandP->u.setcolorArg.colorName, maxval, TRUE); break; case VERB_SETFONT: { FILE * ifP; const struct ppmd_font * fontP; ifP = pm_openr(commandP->u.setfontArg.fontFileName); ppmd_read_font(ifP, &fontP); ppmd_set_font(fontP); pm_close(ifP); } break; case VERB_LINE: ppmd_line(pixels, cols, rows, maxval, commandP->u.lineArg.x0, commandP->u.lineArg.y0, commandP->u.lineArg.x1, commandP->u.lineArg.y1, PPMD_NULLDRAWPROC, &drawState.color); break; case VERB_LINE_HERE: { struct pos endPos; endPos.x = drawState.currentPos.x + commandP->u.lineHereArg.right; endPos.y = drawState.currentPos.y + commandP->u.lineHereArg.down; ppmd_line(pixels, cols, rows, maxval, drawState.currentPos.x, drawState.currentPos.y, endPos.x, endPos.y, PPMD_NULLDRAWPROC, &drawState.color); drawState.currentPos = endPos; } break; case VERB_SPLINE3: ppmd_spline3(pixels, cols, rows, maxval, commandP->u.spline3Arg.x0, commandP->u.spline3Arg.y0, commandP->u.spline3Arg.x1, commandP->u.spline3Arg.y1, commandP->u.spline3Arg.x2, commandP->u.spline3Arg.y2, PPMD_NULLDRAWPROC, &drawState.color); break; case VERB_CIRCLE: ppmd_circle(pixels, cols, rows, maxval, commandP->u.circleArg.cx, commandP->u.circleArg.cy, commandP->u.circleArg.radius, PPMD_NULLDRAWPROC, &drawState.color); break; case VERB_FILLEDCIRCLE: doFilledCircle(pixels, cols, rows, maxval, commandP, &drawState); break; case VERB_FILLEDRECTANGLE: ppmd_filledrectangle(pixels, cols, rows, maxval, commandP->u.filledrectangleArg.x, commandP->u.filledrectangleArg.y, commandP->u.filledrectangleArg.width, commandP->u.filledrectangleArg.height, PPMD_NULLDRAWPROC, &drawState.color); break; case VERB_TEXT: ppmd_text(pixels, cols, rows, maxval, commandP->u.textArg.xpos, commandP->u.textArg.ypos, commandP->u.textArg.height, commandP->u.textArg.angle, commandP->u.textArg.text, PPMD_NULLDRAWPROC, &drawState.color); break; case VERB_TEXT_HERE: doTextHere(pixels, cols, rows, maxval, commandP, &drawState); break; } } }