#! /usr/bin/gawk -f # Last edited on 2023-10-07 08:57:20 by stolfi # Generates ImageMagick "-draw" commands to draw # one or more reference marks for a feature. # # This script assumes that the image on which the marks are to be drawn # is a cropped and magnified clipping of the reference image. # # The "-draw" commands are written to standard output. Parameters are # written with no quotes, but with " " replaced by "_". # # User must define (with "-v") the variables # # {WX,WY} Top left corner of clipping in original image (pixels). # {MKARGS} A string with the mark parameters, separated by blanks. # {OUNIT} Unit of user output coord system, in pixels. # {WSCALE} Extra scaling factor of neighborhood clipping. # {TAG} The feature's tag, for debugging. # # The {MKARGS} parameters are as described in "00-Notebook.txt". All # coordinates and dimensions in the {MKARGS} are assumed to be in the # user output coordinate system; that is, in in multiples of {OUNIT} # pixels of the original image. # # The parameters {WX} and {WY}, on the the other hand, are # assumed to be in pixes, before the cropping but before scling by # {WSCALE} # # The "-draw" commands assume that the image on which the mark is to be drawn # was clipped off from the original image starting {WX} pixels from the # left margin and {WY} pixels from the top margin, # then scaled by a factor {WSCALE} after clipping. # # To get draw commands that can be used on the oringinal (unclipped # and unscaled) image, specify {WX=WY=0} and {WSCALE=1}. BEGIN { pi = 3.1415926; WPU = OUNIT*WSCALE; # Clip image pixels per user unit. nargs = split(MKARGS, mkArgs) iarg = 1; xmax = 0 # Max {x} of all marks. while (iarg <= nargs) { MTYPE = mkArgs[iarg] # Mark type: "C", "R", etc.. # The following coordinates are in out user units, rel to the window's upper left corner: if (iarg+2 > nargs) { mark_error("bad mark parameters", mkArgs, iarg, iarg+2, nargs); } xctr = mkArgs[iarg+1] - WX/OUNIT + 0.5; # X of center of mark. yctr = mkArgs[iarg+2] - WY/OUNIT + 0.5; # Y of center of mark. if (MTYPE == "C") { if (iarg+3 >nargs) { mark_error("bad C mark parameters", mkArgs, iarg, iarg+3, nargs); } rad = mkArgs[iarg+3] draw_circle(xctr, yctr, rad) if (xctr + rad > xmax) { xmax = xctr + rad; } iarg = iarg+4 } else if (MTYPE == "R") { if (iarg+5 > nargs) { mark_error("bad R mark parameters", mkArgs, iarg, iarg+5, nargs); } hrad = mkArgs[iarg+3] vrad = mkArgs[iarg+4] angle = mkArgs[iarg+5] draw_rectangle(xctr, yctr, hrad, vrad, angle) rad = sqrt(hrad*hrad + vrad*vrad) if (xctr + rad > xmax) { xmax = xctr + rad; } iarg = iarg+6 } else if (MTYPE == "X") { if (iarg+3 > nargs) { mark_error("bad X mark parameters", mkArgs, iarg, iarg+4, nargs); } rad = mkArgs[iarg+3] draw_crosshair(xctr, yctr, rad) if (xctr + rad > xmax) { xmax = xctr + rad; } iarg = iarg+4 } else if (MTYPE == "T") { if (iarg+3 > nargs) { mark_error("bad T mark parameters", mkArgs, iarg, iarg+4, nargs); } lab = mkArgs[iarg+3] gsub(/[_]/, " ", lab) if (xctr < xmax) { xctr = xmax; } draw_label(xctr + 5/WPU, yctr, lab) iarg = iarg+4 } else { mark_error("bad mark code", mkArgs, iarg, iarg+2, nargs); iarg = iarg+3 } } } function draw_crosshair(xctr,yctr,rad, rmid) { # {xctr,yctr} relative to window edge. # All dims and coods in output user units. rmid = 4; # Radius of inner end of arms. if (rad < rmid+12) { rad = rmid+12; } draw_crosshair_arm(xctr,yctr,rmid,rad,+1,0); draw_crosshair_arm(xctr,yctr,rmid,rad,-1,0); draw_crosshair_arm(xctr,yctr,rmid,rad,0,+1); draw_crosshair_arm(xctr,yctr,rmid,rad,0,-1); } function draw_crosshair_arm( \ xctr,yctr,rlo,rhi,dx,dy, \ tsz,xa,ya,xb,yb,rt,xt,yt \ ) { # {xctr,yctr} relative to window edge. # {dx,dy} are {-1,0,+1}, exactly one of them 0. # All dims and coods in output user units. tsz=2/WPU # Half tic size. # Draw crosshair arm: xa = xctr + dx*rlo; ya = yctr + dy*rlo xb = xctr + dx*rhi; yb = yctr + dy*rhi printf "-draw line_%.1f,%.1f_%.1f,%.1f\n", xa*WPU, ya*WPU, xb*WPU, yb*WPU # Draw tic lines every 5 user units: rt = 0; while (rt < rhi) { if ((rt >= rlo+1) && (rt <= rhi-1)) { xt = xctr + dx*rt; yt = yctr + dy*rt; xa = xt - dy*tsz; ya = yt + dx*tsz; xb = xt + dy*tsz; yb = yt - dx*tsz; printf "-draw line_%.1f,%.1f_%.1f,%.1f\n", xa*WPU, ya*WPU, xb*WPU, yb*WPU } rt = rt + 5; } } function draw_circle(\ xctr,yctr,rad, \ dmin,nd,dstep,k,a0,x0,y0,a1,x1,y1 \ ) { # All dims and coods in output user units. # Compute number of dashes with ideally 6 pixels with 4 pixels gap: dmin = 10/WPU; nd = (2*pi*rad) / dmin; # Round up to multiple of 4, min 4: nd = 4 * int(nd/4 + 0.9999); if (nd < 4) { nd = 4; } if (nd > 24) { nd = 24; } # printf "C rad = %.1f dmin = %.1f nd = %d\n", rad, dmin, nd > "/dev/stderr" # Compute angular dash step (radians): dstep = 2*pi/nd # Generate the dashes for (k = 0; k < nd; k++) { a0 = (k + 0.3)*dstep x0 = xctr + rad*cos(a0); y0 = yctr + rad*sin(a0); a1 = (k + 0.7)*dstep x1 = xctr + rad*cos(a1); y1 = yctr + rad*sin(a1); printf "-draw line_%.1f,%.1f_%.1f,%.1f\n", x0*WPU, y0*WPU, x1*WPU, y1*WPU } } function draw_rectangle(xctr,yctr,hrad,vrad,angle) { # All dims and coods in output user units. # {angle} in degrees. # printf "R hrad = %.1f vrad = %.1f angle = %.1f\n", hrad, vrad, angle > "/dev/stderr" draw_rectangle_side(xctr,yctr,hrad,vrad,angle, +1,+1, +1,-1) draw_rectangle_side(xctr,yctr,hrad,vrad,angle, +1,-1, -1,-1) draw_rectangle_side(xctr,yctr,hrad,vrad,angle, -1,-1, -1,+1) draw_rectangle_side(xctr,yctr,hrad,vrad,angle, -1,+1, +1,+1) } function draw_label(xlab,ylab,lab) { # {xlab,ylab} position of label ref point (user units). printf "-draw text_%.1f,%.1f_'%s'\n", xlab*WPU, ylab*WPU, lab } function draw_rectangle_side(\ xctr,yctr,hrad,vrad,angle,h0,v0,h1,v1, \ ar,xh,yh,xv,yv,x0,y0,a1,x1,y1 \ ) { # All dims and coods in output user units. # {h0,v0,h1,v1} are {+1} or {-1}. # The points {(h0,v0)} and {(h1,v1)} must # differ in exactly one coordinate. ar = angle*pi/180; # Angle in radians. xh = +hrad*cos(ar); yh = +hrad*sin(ar); xv = -vrad*sin(ar); yv = +vrad*cos(ar); # printf "R xh = %.1f yh = %.1f xv = %.1f yv = %.1f\n", xh, yh, xv, yv > "/dev/stderr" x0 = xctr + h0*xh + v0*xv; y0 = yctr + h0*yh + v0*yv; x1 = xctr + h1*xh + v1*xv; y1 = yctr + h1*yh + v1*yv; draw_dash_line(x0,y0,x1,y1) } function draw_dash_line(\ x0,y0,x1,y1, \ dmin,dx,dy,dst,nd,k,xa,ya,xb,yb \ ) { # All dims and coods in output user units. # Compute number of dashes with step about 10: dx = x1 - x0; dy = y1 - y0; dst = sqrt(dx^2 + dy^2) dmin = 10/WPU nd = int(dst/dmin + 0.9999); if (nd <= 1) { nd = 1; } for (k = 0; k <= nd; k++) { if (k == 0) { xa = x0; ya = y0; } else { xa = x0 + (k - 0.2)/nd*dx; ya = y0 + (k - 0.2)/nd*dy; } if (k == nd) { xb = x1; yb = y1; } else { xb = x0 + (k + 0.2)/nd*dx yb = y0 + (k + 0.2)/nd*dy } printf "-draw line_%.1f,%.1f_%.1f,%.1f\n", xa*WPU, ya*WPU, xb*WPU, yb*WPU } } function mark_error(msg,mkArgs,ini,fin,nargs, j,arg) { arg = "" for (j = ini; j <= fin; j++) { arg = (arg " " (j > nargs ? "??" : mkArgs[j])) } printf "** %s args = \"%s\"\n", msg, arg > "/dev/stderr"; exit(1) } function arg_error(msg) { printf "** %s\n", msg > "/dev/stderr"; exit(1) }