/* See {fmc_book_tools.h}. */ /* Last edited on 2024-12-21 14:02:32 by stolfi */ #include #include #include #include #include #include #include #include #include #include #include #include ptset_t make_ptset(char *name, char *label, int32_t n, r3_t q[], r2_t *szLab) { ptset_t s; s = (ptset_t){ .name = name, .label = label, .p = r3_vec_new(n), .szLab = (*szLab), .pMax = (r2_t){{ -INF, -INF }}, .pMin = (r2_t){{ +INF, +INF }} }; double step = 20; /* Scaling factor for point coordinates (mm). */ for (uint32_t k = 0; k < n; k++) { for (uint32_t i = 0; i < 2; i++) { double xki = q[k].c[i]*step; s.p.e[k].c[i] = xki; s.pMin.c[i] = fmin(s.pMin.c[i], xki); s.pMax.c[i] = fmax(s.pMax.c[i], xki); } s.p.e[k].c[2] = q[k].c[2]; } return s; } relac_t make_relac(char *name, char *label, bool_t cal, int32_t n, i2_t q[], double labwd, double dxinv) { relac_t r; r = (relac_t){ .name = name, .label = label, .cal = cal, .R = i2_vec_new(n), .labwd = labwd, .dxinv = dxinv }; for (uint32_t k = 0; k < n; k++) { r.R.e[k] = q[k]; } return r; } epswr_figure_t* open_fig(char *fname, double xSize, double ySize) { bool_t verbose = TRUE; FILE *wr = open_write(fname, verbose); double mrg = 1*mm; epswr_figure_t *eps = epswr_new_figure(wr, xSize*mm, ySize*mm, mrg,mrg,mrg,mrg, verbose); epswr_set_client_window(eps, 0,xSize, 0,ySize); epswr_set_label_font(eps, "CourierBold", fmc_font_size*mm); free(fname); return eps; } #define dxLab (1.5*fmc_digit_width) /* Horizontal separation between dot center and label at left or right of dot. */ #define dyLab (0.75*fmc_font_size) /* Vertical separation between dot center and label above or below dot. */ void draw_fig_relac_setas(ptset_t *sA, ptset_t *sB, relac_t *r, bool_t inv) { /* Decide the {Y} displacements for the two sets: */ double yMidA = (sA->pMin.c[1] + sA->pMax.c[1])/2; double yMidB = (sB->pMin.c[1] + sB->pMax.c[1])/2; double yMid = 16 + fmax(yMidA, yMidB); /* {Y} of midline of diagram. */ /* Decide the {X} positions and label orientations of the sets: */ double xsep = 60; /* Separation between the sets: */ r2_t dpA, dpB; sign_t dirA, dirB; double xMax, yMax; /* Max {X} and {Y} plotted including labels but not set frames. */ if (! inv) { dpA = (r2_t){{ 12 + sA->szLab.c[0] + dxLab, yMid-yMidA }}; dpB = (r2_t){{ dpA.c[0] + xsep, yMid-yMidB }}; dirA = -1; dirB = +1; xMax = dpB.c[0] + dxLab + sB->szLab.c[0]; } else { dpB = (r2_t){{ 12 + sB->szLab.c[0] + dxLab, yMid-yMidB }}; dpA = (r2_t){{ dpB.c[0] + xsep, yMid-yMidA }}; dirB = -1; dirA = +1; xMax = dpA.c[0] + dxLab + sA->szLab.c[0]; } double yMaxA = dpA.c[1] + (sA->pMax.c[1] - sA->pMin.c[1]) + 0.5*sA->szLab.c[1]; double yMaxB = dpB.c[1] + (sB->pMax.c[1] - sB->pMin.c[1]) + 0.5*sB->szLab.c[1]; yMax = fmax(yMaxA, yMaxB); char *fname = jsprintf("out/relac-%s%s-setas.eps", r->name, (inv ? "-inv" : "-dir")); double hSize = xMax + 12; /* !!! Should compute it! !!! */ double vSize = yMax + 28; /* !!! Should compute it! !!! */ epswr_figure_t *eps = open_fig(fname, hSize,vSize); draw_set_vert(eps, sA, &dpA, dirA); draw_set_vert(eps, sB, &dpB, dirB); double dxName = 0.5*(yMidA - yMidB); draw_arrows(eps, sA, &dpA, sB, &dpB, r, dxName, inv); epswr_end_figure(eps); return; } void draw_fig_relac_pontos(ptset_t *sA, ptset_t *sB, relac_t *r) { /* Choose the distance from bot to {sA} and left to {sB} if there were no labels: */ double dxyBase = 14; /* Choose the distances from bot to {sB} and left to {sA} if there were no labels: */ double dxyOrg = dxyBase + 20; /* Compute position of first dots of each set: */ r2_t dpA = (r2_t){{ dxyOrg + dxLab + sB->szLab.c[0], dxyBase + dyLab + sA->szLab.c[1] }}; r2_t dpB = (r2_t){{ dxyBase + dxLab + sB->szLab.c[0], dxyOrg + dyLab + sA->szLab.c[1] }}; /* Compute max plotted dots and labels minus set frames and names: */ double xMax = dpA.c[0]+ (sA->pMax.c[1] - sA->pMin.c[1]) + 0.5*sA->szLab.c[0]; double yMax = dpB.c[1]+ (sB->pMax.c[1] - sB->pMin.c[1]) + 0.5*sB->szLab.c[1]; /* Open file: */ char *fname = jsprintf("out/relac-%s-dir-pontos.eps", r->name); double hSize = xMax + 26; /* !!! Should compute it! !!! */ double vSize = yMax + 28; /* !!! Should compute it! !!! */ epswr_figure_t *eps = open_fig(fname, hSize,vSize); draw_set_horz(eps, sA, &dpA); draw_set_vert(eps, sB, &dpB, -1); draw_pair_dots(eps, sA, &dpA, sB, &dpB, r); epswr_end_figure(eps); return; } void draw_fig_compos_setas(ptset_t *sA, ptset_t *sB, ptset_t *sC, relac_t *rR, relac_t *rS, bool_t inv) { /* Decide the {Y} displacements for the two sets: */ double yMidA = (sA->pMin.c[1] + sA->pMax.c[1])/2; double yMidB = (sB->pMin.c[1] + sB->pMax.c[1])/2; double yMidC = (sC->pMin.c[1] + sC->pMax.c[1])/2; double yMid = 12 + fmax(yMidA, fmax(yMidB, yMidC)); /* {Y} of midline of diagram. */ /* Decide the {X} positions and label orientations of the sets: */ double xsep = 60; /* Separation between the sets: */ r2_t dpA, dpB, dpC; sign_t dirA, dirB, dirC; double xMax, yMax; /* Max {X} and {Y} plotted including labels but not set frames. */ if (! inv) { dpA = (r2_t){{ 18 + sA->szLab.c[0], yMid-yMidA }}; dpB = (r2_t){{ dpA.c[0] + xsep + 0.5*sB->szLab.c[0], yMid-yMidB }}; dpC = (r2_t){{ dpB.c[0] + 0.5*sB->szLab.c[0] + xsep, yMid-yMidC }}; dirA = -1; dirB = 0; dirC = +1; xMax = dpC.c[0] + dxLab + sC->szLab.c[0]; } else { dpC = (r2_t){{ 18 + sC->szLab.c[0], yMid-yMidC }}; dpB = (r2_t){{ dpC.c[0] + xsep + 0.5*sB->szLab.c[0], yMid-yMidB }}; dpA = (r2_t){{ dpB.c[0] + 0.5*sB->szLab.c[0] + xsep, yMid-yMidA }}; dirC = -1; dirB = 0; dirA = +1; xMax = dpA.c[0] + dxLab + sA->szLab.c[0]; } double yMaxA = dpA.c[1] + (sA->pMax.c[1] - sA->pMin.c[1]) + 0.5*sA->szLab.c[1]; double yMaxB = dpB.c[1] + (sB->pMax.c[1] - sB->pMin.c[1]) + 0.5*sB->szLab.c[1]; double yMaxC = dpC.c[1] + (sC->pMax.c[1] - sC->pMin.c[1]) + 0.5*sC->szLab.c[1]; yMax = fmax(fmax(yMaxA, yMaxB), yMaxC); /* Create the figure: */ char *fname = jsprintf("out/compos-%s-%s%s.eps", rR->name, rS->name, (inv ? "-inv" : "-dir")); double hSize = xMax + 12; double vSize = yMax + 36; /* !!! Should compute it! !!! */ epswr_figure_t *eps = open_fig(fname, hSize,vSize); draw_set_vert(eps, sA, &dpA, dirA); draw_set_vert(eps, sB, &dpB, dirB); draw_set_vert(eps, sC, &dpC, dirC); double dxnameR = 0.5*(yMidA - yMidB); draw_arrows(eps, sA, &dpA, sB, &dpB, rR, dxnameR, inv); double dxnameS = 0.5*(yMidB - yMidC); draw_arrows(eps, sB, &dpB, sC, &dpC, rS, dxnameS, inv); epswr_end_figure(eps); return; } void draw_set_vert(epswr_figure_t *eps, ptset_t *s, r2_t *dp, sign_t labdir) { bool_t debug = FALSE; epswr_set_pen(eps, 0,0,0, 0.25, 0,0); epswr_set_fill_color(eps, 0,0,0); epswr_set_label_font(eps, "Courier", fmc_font_size*mm); double dotr = 1.0*fmc_dot_radius; r2_t qMin = (r2_t){{ +INF, +INF }}; /* Low coords of points and labels. */ r2_t qMax = (r2_t){{ -INF, -INF }}; /* High coords of points and labels. */ /* Define placement of labels relative to dots,: */ r2_t dLab; /* Label ref point relative to dot. */ r2_t align; /* Horizontal and vertical label alignnment. */ if (labdir == -1) { dLab = (r2_t){{ -1.3*fmc_digit_width, 0.0 }}; align = (r2_t){{ 1.0, 0.5 }}; } else if (labdir == +1) { dLab = (r2_t){{ +1.3*fmc_digit_width, 0.0 }}; align = (r2_t){{ 0.0, 0.5 }}; } else if (labdir == 0) { dLab = (r2_t){{ 0.0, 0.60*fmc_font_size }}; align = (r2_t){{ 0.5, 0.0 }}; } else { assert(FALSE); } for (uint32_t k = 0; k < s->p.ne; k++) { r3_t *spk = &(s->p.e[k]); /* Draw the dot: */ r2_t pDot; /* Dot coordinates. */ for (uint32_t i = 0; i < 2; i++) { pDot.c[i] = dp->c[i] + (spk->c[i] - s->pMin.c[i]); } epswr_dot(eps, pDot.c[0], pDot.c[1], dotr, TRUE, TRUE); /* Print the label: */ r2_t pLab; r2_add(&dLab, &pDot, &pLab); /* Label reference point. */ char *tp = NULL; /* Label text. */ char *tp = jsprintf("%.0f", spk->c[2]); epswr_label(eps, tp, "0", pLab.c[0], pLab.c[1], 0, TRUE, align.c[0], align.c[1], TRUE, FALSE); if (debug) { draw_label_corners(eps, &pLab, &align, &(s->szLab)); } /* Update the ranges of point and label coordinates: */ for (uint32_t i = 0; i < 2; i++) { qMin.c[i] = fmin(fmin(qMin.c[i], pDot.c[i]), pLab.c[i] - align.c[i]*s->szLab.c[i]); qMax.c[i] = fmax(fmax(qMax.c[i], pDot.c[i]), pLab.c[i] + (1-align.c[i])*s->szLab.c[i]); } free(tp); } /* Draw the set outline: */ r2_t mrg = (r2_t){{ 8, 8 }}; /* Margin between dots and frame. */ r2_sub(&qMin, &mrg, &qMin); r2_add(&qMax, &mrg, &qMax); double xc[4] = { qMin.c[0], qMax.c[0], qMax.c[0], qMin.c[0] }; double yc[4] = { qMin.c[1], qMin.c[1], qMax.c[1], qMax.c[1] }; double rad = 0.75*fmin(mrg.c[0], mrg.c[1]); epswr_set_pen(eps, 0.000, 0.500, 1.000, 0.35, 0,0); epswr_rounded_polygon(eps, TRUE, xc, yc, 4, rad, FALSE, TRUE, TRUE); if ((s->label != NULL) && (strlen(s->label) > 0)) { /* Write the set's name: */ double xsLab = (qMax.c[0] + qMin.c[0])/2; double ysLab = qMax.c[1] + mrg.c[1]; epswr_set_label_font(eps, "TimesRomanItalic", fmc_font_size*mm); epswr_label(eps, s->label, "X", xsLab, ysLab, 0, TRUE, 0.5, 0.0, TRUE, FALSE); } return; } void draw_set_horz(epswr_figure_t *eps, ptset_t *s, r2_t *dp) { bool_t debug = FALSE; epswr_set_pen(eps, 0,0,0, 0.25, 0,0); epswr_set_fill_color(eps, 0,0,0); epswr_set_label_font(eps, "Courier", fmc_font_size*mm); double dotr = 1.0*fmc_dot_radius; r2_t qMin = (r2_t){{ +INF, +INF }}; /* Low coords of points and labels. */ r2_t qMax = (r2_t){{ -INF, -INF }}; /* High coords of points and labels. */ /* Define placement of labels relative to dots,: */ r2_t dLab; /* Label ref point relative to dot. */ r2_t align; /* Horizontal and vertical label alignnment. */ dLab = (r2_t){{ 0.0, -0.75*fmc_font_size }}; align = (r2_t){{ 0.5, 1.0 }}; for (uint32_t k = 0; k < s->p.ne; k++) { r3_t *spk = &(s->p.e[k]); /* Draw the dot: */ r2_t pDot; /* Dot coordinates. Note transposition. */ pDot.c[0] = dp->c[0] + (s->pMax.c[1] - spk->c[1]); /* Note flip L-R. */ pDot.c[1] = dp->c[1] + (spk->c[0] - s->pMin.c[0]); epswr_dot(eps, pDot.c[0], pDot.c[1], dotr, TRUE, TRUE); /* Print the label: */ r2_t pLab; r2_add(&dLab, &pDot, &pLab); /* Label reference point. */ char *tp = NULL; /* Label text. */ char *tp = jsprintf("%.0f", spk->c[2]); epswr_label(eps, tp, "0", pLab.c[0], pLab.c[1], 0, TRUE, align.c[0], align.c[1], TRUE, FALSE); if (debug) { draw_label_corners(eps, &pLab, &align, &(s->szLab)); } /* Update the ranges of point and label coordinates: */ for (uint32_t i = 0; i < 2; i++) { qMin.c[i] = fmin(fmin(qMin.c[i], pDot.c[i]), pLab.c[i] - align.c[i]*s->szLab.c[i]); qMax.c[i] = fmax(fmax(qMax.c[i], pDot.c[i]), pLab.c[i] + (1-align.c[i])*s->szLab.c[i]); } free(tp); } /* Draw the set outline: */ r2_t mrg = (r2_t){{ 8, 8 }}; /* Margin between dots and frame. */ r2_sub(&qMin, &mrg, &qMin); r2_add(&qMax, &mrg, &qMax); double xc[4] = { qMin.c[0], qMax.c[0], qMax.c[0], qMin.c[0] }; double yc[4] = { qMin.c[1], qMin.c[1], qMax.c[1], qMax.c[1] }; double rad = 0.75*fmin(mrg.c[0], mrg.c[1]); epswr_set_pen(eps, 0.000, 0.500, 1.000, 0.35, 0,0); epswr_rounded_polygon(eps, TRUE, xc, yc, 4, rad, FALSE, TRUE, TRUE); if ((s->label != NULL) && (strlen(s->label) > 0)) { /* Write the set's name: */ double xsLab = qMax.c[0] + mrg.c[0]; double ysLab = (qMax.c[1] + qMin.c[1])/2; epswr_set_label_font(eps, "TimesRomanItalic", fmc_font_size*mm); epswr_label(eps, s->label, "X", xsLab, ysLab, 0, TRUE, 0.0, 0.5, TRUE, FALSE); } return; } void draw_arrows ( epswr_figure_t *eps, ptset_t *sA, r2_t *dpA, ptset_t *sB, r2_t *dpB, relac_t *r, double dxName, bool_t inv ) { epswr_set_pen(eps, 0.500, 0.000, 0.000, 0.45, 0,0); epswr_set_fill_color(eps, 0.500, 0.000, 0.000); double dotr_big = 2.5*fmc_dot_radius; /* Radius of circle around dot. */ double dotr_sma = 1.1*fmc_dot_radius; /* Radius of dot proper. */ double xMaxA = -INF, xMinB = +INF; /* Range of {X} between dots. */ double yMaxA = -INF, yMaxB = -INF; /* Max {Y} on each side. */ for (uint32_t k = 0; k < r->R.ne; k++) { i2_t *Rk = &(r->R.e[k]); /* Get the tail and head point coordinates: */ int32_t iA = Rk->c[0]; assert((iA >= 0) && (iA < sA->p.ne)); int32_t iB = Rk->c[1]; assert((iB >= 0) && (iB < sB->p.ne)); double xA = dpA->c[0] + sA->p.e[iA].c[0]; double yA = dpA->c[1] + sA->p.e[iA].c[1]; double xB = dpB->c[0] + sB->p.e[iB].c[0]; double yB = dpB->c[1] + sB->p.e[iB].c[1]; fprintf(stderr, "debug: %8.3f %8.3f %8.3f %8.3f\n", xA, yA, xB, yB); /* Highlight the domain and image dots with a red dot and a circle: */ epswr_dot(eps, xA, yA, dotr_sma, TRUE, TRUE); epswr_dot(eps, xB, yB, dotr_sma, TRUE, TRUE); epswr_dot(eps, xA, yA, dotr_big, FALSE, TRUE); epswr_dot(eps, xB, yB, dotr_big, FALSE, TRUE); /* Draw the arrow: */ epswr_segment(eps, xA, yA, xB, yB); if (! inv) { epswr_arrowhead(eps, xA, yA, xB, yB, 1.00,1.00,4.00, 0.800, TRUE, TRUE); } else { epswr_arrowhead(eps, xB, yB, xA, yA, 1.00,1.00,4.00, 0.800, TRUE, TRUE); } /* Update coord ranges: */ xMaxA = fmax(xMaxA, xA); xMinB = fmin(xMinB, xB); yMaxA = fmax(yMaxA, yA); yMaxB = fmax(yMaxB, yB); } if ((r->label != NULL) && (strlen(r->label) > 0)) { /* Write the relation's name: */ double dyName = 16.0; /* Vertical displacement for {r->label} */ double xName = (xMaxA + xMinB)/2 + (inv ? -dxName : +dxName); double yName = (yMaxA + yMaxB)/2 + dyName; epswr_set_pen(eps, 0.000, 0.000, 0.000, 0.35, 0,0); epswr_set_fill_color(eps, 0.000, 0.000, 0.000); if (r->cal) { epswr_set_label_font(eps, "ZapfChancery", 1.2*fmc_font_size*mm); } else { epswr_set_label_font(eps, "TimesRomanItalic", fmc_font_size*mm); } char *rLab = jsprintf((inv && (strlen(r->label) > 1) ? "(%s)" : "%s"), r->label); epswr_label(eps, rLab, "X", xName, yName, 0, TRUE, 0.5, 0.0, TRUE, FALSE); if (inv) { /* Write "-1" as superscript to name: */ double dxSup = r->dxinv; double xSup = xName + dxSup; double ySup = yName + 0.55*fmc_font_size; epswr_set_label_font(eps, "TimesRoman", 0.60*fmc_font_size*mm); epswr_label(eps, "-1", "0", xSup, ySup, 0, TRUE, 0.0, 0.0, TRUE, FALSE); } free(rLab); } return; } void draw_pair_dots ( epswr_figure_t *eps, ptset_t *sA, r2_t *dpA, ptset_t *sB, r2_t *dpB, relac_t *r ) { /* Draw the grid lines: */ epswr_set_pen(eps, 0.800, 0.700, 0.500, 0.40, 0,0); double ovsh = 6.0; /* Overshoot of lines. */ double yHiA = dpB->c[1] + sB->pMax.c[1] + ovsh; /* Top end of vert lines. */ double yLoA = dpB->c[1] + sB->pMin.c[1] - ovsh; /* Bot end of vert lines. */ for (uint32_t k = 0; k < sA->p.ne; k++) { r3_t *spk = &(sA->p.e[k]); double xAk = dpA->c[0] + (sA->pMax.c[1] - spk->c[1]); epswr_segment(eps, xAk, yLoA, xAk, yHiA); } double xHiB = dpA->c[0] + sA->pMax.c[1] + ovsh; /* Right end of horz lines. */ double xLoB = dpA->c[0] + sA->pMin.c[1] - ovsh; /* Left end of horz lines. */ for (uint32_t k = 0; k < sB->p.ne; k++) { r3_t *spk = &(sB->p.e[k]); double yBk = dpB->c[1] + (spk->c[1] - sB->pMin.c[1]); epswr_segment(eps, xLoB, yBk, xHiB, yBk); } /* Draw the dots: */ epswr_set_pen(eps, 0.500, 0.000, 0.000, 0.35, 0,0); epswr_set_fill_color(eps, 0.500, 0.000, 0.000); double dotr_big = 2.5*fmc_dot_radius; /* Radius of circle around dot. */ double dotr_sma = 1.1*fmc_dot_radius; /* Radius of dot proper. */ for (uint32_t k = 0; k < r->R.ne; k++) { i2_t *Rk = &(r->R.e[k]); /* Get the point coordinates. Note that {sA} points are transposed and LR flipped. */ int32_t iA = Rk->c[0]; assert((iA >= 0) && (iA < sA->p.ne)); int32_t iB = Rk->c[1]; assert((iB >= 0) && (iB < sB->p.ne)); double xA = dpA->c[0] + (sA->pMax.c[1] - sA->p.e[iA].c[1]); /* Note L-R flip. */ double yA = dpA->c[1] + (sA->p.e[iA].c[0] - sA->pMin.c[0]); double xB = dpB->c[0] + (sB->p.e[iB].c[0] - sB->pMin.c[0]); double yB = dpB->c[1] + (sB->p.e[iB].c[1] - sB->pMin.c[1]); fprintf(stderr, "debug: %8.3f %8.3f %8.3f %8.3f\n", xA, yA, xB, yB); /* Highlight domain and image dots: */ epswr_dot(eps, xA, yA, dotr_sma, TRUE, TRUE); epswr_dot(eps, xB, yB, dotr_sma, TRUE, TRUE); epswr_dot(eps, xA, yA, dotr_big, FALSE, TRUE); epswr_dot(eps, xB, yB, dotr_big, FALSE, TRUE); /* Draw the graph dot: */ epswr_dot(eps, xA, yB, dotr_big, TRUE, TRUE); } return; } /* DEBUG */ void show_r3(char *pname, r3_vec_t p) { fprintf(stderr, "%-4s ",pname); for (uint32_t k = 0; k < p.ne; k++) { fprintf(stderr, " ( %8.3f %8.3f %8.3f )", p.e[k].c[0], p.e[k].c[1], p.e[k].c[2]); } fprintf(stderr, "\n"); return; } void show_i2(char *Rname, int32_t n, i2_t R[]) { fprintf(stderr, "R:"); for (uint32_t k = 0; k < n; k++) { fprintf(stderr, " %5d %5d", R[k].c[0],R[k].c[1]); } fprintf(stderr, "\n"); return; } void draw_label_corners(epswr_figure_t *eps, r2_t *pLab, r2_t *align, r2_t *szLab) { r2_t ploLab, phiLab; for (uint32_t i = 0; i < 2; i++) { ploLab.c[i] = pLab->c[i] - align->c[i]*szLab->c[i]; phiLab.c[i] = pLab->c[i] + (1-align->c[i])*szLab->c[i]; } epswr_dot(eps, ploLab.c[0], ploLab.c[1], 0.5, TRUE, TRUE); epswr_dot(eps, phiLab.c[0], phiLab.c[1], 0.5, TRUE, TRUE); return; }