%D \module %D [ file=mp-tool.mpiv, %D version=1998.02.15, %D title=\CONTEXT\ \METAPOST\ graphics, %D subtitle=auxiliary macros, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. if known context_tool : endinput ; fi ; boolean context_tool ; context_tool := true ; let @## = @# ; let noexpand = quote ; %D New, version number testing: %D %D \starttyping %D fill fullcircle scaled 2cm withcolor if mpversiongt("0.6") : red else : green fi ; %D fill fullcircle scaled 1cm withcolor if mpversionlt(0.6) : blue else : white fi ; %D \stoptyping if not known mpversion : string mpversion ; mpversion := "0.641" ; fi ; % newinternal metapostversion ; metapostversion := scantokens(mpversion) ; newinternal metapostversion ; metapostversion := 2.0 ; %D We always want \EPS\ conforming output, so we say: prologues := 1 ; warningcheck := 0 ; mpprocset := 1 ; %D Handy: def nothing = enddef ; %D Namespace handling: % let exclamationmark = ! ; % let questionmark = ? ; % % def unprotect = % let ! = relax ; % let ? = relax ; % enddef ; % % def protect = % let ! = exclamationmark ; % let ? = questionmark ; % enddef ; % % unprotect ; % % mp!some!module = 10 ; show mp!some!module ; show somemodule ; % % protect ; string space ; space := char 32 ; string percent ; percent := char 37 ; string crlf ; crlf := char 10 & char 13 ; string dquote ; dquote := char 34 ; let SPACE = space ; let CRLF = crlf ; let DQUOTE = dquote ; let PERCENT = percent ; vardef ddecimal primary p = decimal xpart p & " " & decimal ypart p enddef ; %D Plain compatibility: string plain_compatibility_data ; plain_compatibility_data := "" ; def startplaincompatibility = begingroup ; scantokens plain_compatibility_data ; enddef ; def stopplaincompatibility = endgroup ; enddef ; %D More neutral: let triplet = rgbcolor ; let quadruplet = cmykcolor ; %D Image redefined, for Alan: vardef image@#(text t) = save currentpicture ; picture currentpicture ; currentpicture := nullpicture ; t ; currentpicture if str @# <> "" : shifted ( mfun_labxf@# * lrcorner p + mfun_labyf@# * ulcorner p + (1-mfun_labxf@#-mfun_labyf@#) * llcorner p ) fi enddef ; %D Variables def dispose suffix s = if known s : begingroup ; save ss ; if numeric s : numeric ss elseif boolean s : boolean ss elseif pair s : pair ss elseif path s : path ss elseif picture s : picture ss elseif string s : string ss elseif transform s : transform ss elseif color s : color ss elseif rgbcolor s : rgbcolor ss elseif cmykcolor s : cmykcolor ss elseif pen s : pen ss else s : numeric ss fi ; s := ss ; endgroup ; fi ; enddef ; %D Colors: newinternal nocolormodel ; nocolormodel := 1 ; newinternal greycolormodel ; greycolormodel := 3 ; newinternal graycolormodel ; graycolormodel := 3 ; newinternal rgbcolormodel ; rgbcolormodel := 5 ; newinternal cmykcolormodel ; cmykcolormodel := 7 ; let grayscale = graycolor ; let greyscale = greycolor ; vardef colorpart expr c = if not picture c : 0 elseif colormodel c = greycolormodel : greypart c elseif colormodel c = rgbcolormodel : (redpart c,greenpart c,bluepart c) elseif colormodel c = cmykcolormodel : (cyanpart c,magentapart c,yellowpart c,blackpart c) else : 0 % black fi enddef ; vardef colorlike(text c) text v = % colorlike(a) b, c, d ; save _p_ ; picture _p_ ; forsuffixes i=v : _p_ := image(draw origin withcolor c ;) ; % intercept pre and postscripts if (colormodel _p_ = cmykcolormodel) : cmykcolor i ; elseif (colormodel _p_ = rgbcolormodel) : rgbcolor i ; else : greycolor i ; fi ; endfor ; enddef ; %D Also handy (when we flush colors): vardef dddecimal primary c = decimal redpart c & " " & decimal greenpart c & " " & decimal bluepart c enddef ; vardef ddddecimal primary c = decimal cyanpart c & " " & decimal magentapart c & " " & decimal yellowpart c & " " & decimal blackpart c enddef ; vardef colordecimals primary c = if cmykcolor c : decimal cyanpart c & ":" & decimal magentapart c & ":" & decimal yellowpart c & ":" & decimal blackpart c elseif rgbcolor c : decimal redpart c & ":" & decimal greenpart c & ":" & decimal bluepart c elseif string c: colordecimals resolvedcolor(c) else : decimal c fi enddef ; vardef colordecimalslist(text t) = save b ; boolean b ; b := false ; for s=t : if b : & " " & fi colordecimals(s) hide(b := true ;) endfor enddef ; % vardef _ctx_color_spec_ primary c = % if cmykcolor c : % "c=" & decimal cyanpart c & % ",m=" & decimal magentapart c & % ",y=" & decimal yellowpart c & % ",k=" & decimal blackpart c % elseif rgbcolor c : % "r=" & decimal redpart c & % ",g=" & decimal greenpart c & % ",b=" & decimal bluepart c % else : % "s=" & decimal c % fi % enddef ; % % vardef _ctx_color_spec_list_(text t) = % save b ; boolean b ; b := false ; % for s=t : % if b : & " " & fi % _ctx_color_spec_(s) % hide(b := true ;) % endfor % enddef ; %D We have standardized data file names: def job_name = jobname enddef ; %D Because \METAPOST\ has a hard coded limit of 4~datafiles, %D we need some trickery when we have multiple files. This will %D be redone (via \LUA). boolean savingdata ; savingdata := false ; boolean savingdatadone ; savingdatadone := false ; def savedata expr txt = lua.mp.mf_save_data(txt); enddef ; def startsavingdata = lua.mp.mf_start_saving_data(); enddef ; def stopsavingdata = lua.mp.mf_stop_saving_data() ; enddef ; def finishsavingdata = lua.mp.mf_finish_saving_data() ; enddef ; %D Instead of a keystroke eating save and allocation %D sequence, you can use the \citeer {new} alternatives to %D save and allocate in one command. def newcolor text v = forsuffixes i=v : save i ; color i ; endfor ; enddef ; def newnumeric text v = forsuffixes i=v : save i ; numeric i ; endfor ; enddef ; def newboolean text v = forsuffixes i=v : save i ; boolean i ; endfor ; enddef ; def newtransform text v = forsuffixes i=v : save i ; transform i ; endfor ; enddef ; def newpath text v = forsuffixes i=v : save i ; path i ; endfor ; enddef ; def newpicture text v = forsuffixes i=v : save i ; picture i ; endfor ; enddef ; def newstring text v = forsuffixes i=v : save i ; string i ; endfor ; enddef ; def newpair text v = forsuffixes i=v : save i ; pair i ; endfor ; enddef ; %D Sometimes we don't want parts of the graphics add to the %D bounding box. One way of doing this is to save the bounding %D box, draw the graphics that may not count, and restore the %D bounding box. %D %D \starttyping %D push_boundingbox currentpicture; %D pop_boundingbox currentpicture; %D \stoptyping %D %D The bounding box can be called with: %D %D \starttyping %D boundingbox currentpicture %D inner_boundingbox currentpicture %D outer_boundingbox currentpicture %D \stoptyping %D %D Especially the latter one can be of use when we include %D the graphic in a document that is clipped to the bounding %D box. In such occasions one can use: %D %D \starttyping %D set_outer_boundingbox currentpicture; %D \stoptyping %D %D Its counterpart is: %D %D \starttyping %D set_inner_boundingbox p %D \stoptyping path mfun_boundingbox_stack[] ; numeric mfun_boundingbox_stack_depth ; mfun_boundingbox_stack_depth := 0 ; def pushboundingbox text p = mfun_boundingbox_stack_depth := mfun_boundingbox_stack_depth + 1 ; mfun_boundingbox_stack[mfun_boundingbox_stack_depth] := boundingbox p ; enddef ; def popboundingbox text p = setbounds p to mfun_boundingbox_stack[mfun_boundingbox_stack_depth] ; mfun_boundingbox_stack[mfun_boundingbox_stack_depth] := origin -- cycle ; mfun_boundingbox_stack_depth := mfun_boundingbox_stack_depth - 1 ; enddef ; let push_boundingbox = pushboundingbox ; % downward compatible let pop_boundingbox = popboundingbox ; % downward compatible vardef boundingbox primary p = if (path p) or (picture p) : llcorner p -- lrcorner p -- urcorner p -- ulcorner p else : origin fi -- cycle enddef; vardef innerboundingbox primary p = top rt llcorner p -- top lft lrcorner p -- bot lft urcorner p -- bot rt ulcorner p -- cycle enddef; vardef outerboundingbox primary p = bot lft llcorner p -- bot rt lrcorner p -- top rt urcorner p -- top lft ulcorner p -- cycle enddef; def inner_boundingbox = innerboundingbox enddef ; def outer_boundingbox = outerboundingbox enddef ; vardef set_inner_boundingbox text q = % obsolete setbounds q to innerboundingbox q; enddef; vardef set_outer_boundingbox text q = % obsolete setbounds q to outerboundingbox q; enddef; % secondarydef a boundedto b = % will this cleanup ? % hide(picture mfun_a_b ; mfun_a_b := a ; setbounds mfun_a_b to b;) % mfun_a_b % enddef ; %D Here are some special ones, cooked up in the process of Alan's mp-node %D module: vardef boundingradius primary p = if picture p : max( abs((llcorner p) shifted -center p), abs((lrcorner p) shifted -center p), abs((urcorner p) shifted -center p), abs((ulcorner p) shifted -center p) ) elseif pen p : boundingradius image(draw makepath p ;) elseif path p : boundingradius image(draw p ;) fi enddef ; vardef boundingcircle primary p = fullcircle scaled 2boundingradius p shifted center p enddef ; vardef boundingpoint@#(expr p) = if picture p : % pen? ( mfun_labxf@# *ulcorner p + mfun_labyf@# *lrcorner p +(1-mfun_labxf@#-mfun_labyf@#)*urcorner p) elseif path p : boundingpoint@#(image(draw p ;)) %elseif pair p : % p %else : % origin fi enddef ; def mirrored primary a = a scaled -1 enddef ; primarydef a mirroredabout b = (a shifted -b) scaled -1 shifted b enddef ; %D Some missing functions can be implemented rather straightforward (thanks to %D Taco and others): % oldpi := 3.14159265358979323846 ; % from pi := 3.14159265358979323846264338327950288419716939937510 ; % 50 digits radian := 180/pi ; % 2pi*radian = 360 ; % let +++ = ++ ; vardef sqr primary x = x*x enddef ; vardef log primary x = if x=0: 0 else: mlog(x)/mlog(10) fi enddef ; vardef ln primary x = if x=0: 0 else: mlog(x)/256 fi enddef ; vardef exp primary x = (mexp 256)**x enddef ; vardef inv primary x = if x=0: 0 else: x**-1 fi enddef ; vardef pow (expr x,p) = x**p enddef ; vardef tand primary x = sind(x)/cosd(x) enddef ; vardef cotd primary x = cosd(x)/sind(x) enddef ; vardef sin primary x = sind(x*radian) enddef ; vardef cos primary x = cosd(x*radian) enddef ; vardef tan primary x = sin(x)/cos(x) enddef ; vardef cot primary x = cos(x)/sin(x) enddef ; vardef asin primary x = angle((1+-+x,x)) enddef ; vardef acos primary x = angle((x,1+-+x)) enddef ; vardef atan primary x = angle(1,x) enddef ; vardef invsin primary x = (asin(x))/radian enddef ; vardef invcos primary x = (acos(x))/radian enddef ; vardef invtan primary x = (atan(x))/radian enddef ; vardef acosh primary x = ln(x+(x+-+1)) enddef ; vardef asinh primary x = ln(x+(x++1)) enddef ; vardef sinh primary x = save xx ; xx = exp x ; (xx-1/xx)/2 enddef ; vardef cosh primary x = save xx ; xx = exp x ; (xx+1/xx)/2 enddef ; vardef tanh primary x = save xx ; xx = exp x ; (xx-1/xx)/(xx+1/xx) enddef ; %D Like mod, but useful for angles, it returns (-.5d,+.5d] and is used %D in for instance mp-chem. primarydef a zmod b = (-((b/2 - a) mod b) + b/2) enddef ; %D Sometimes this is handy: def undashed = dashed nullpicture enddef ; %D We provide two macros for drawing stripes across a shape. %D The first method (with the n suffix) uses another method, %D slower in calculation, but more efficient when drawn. The %D first macro divides the sides into n equal parts. The %D first argument specifies the way the lines are drawn, while %D the second argument identifier the way the shape is to be %D drawn. %D %D \starttyping %D stripe_path_n %D (dashed evenly withcolor blue) %D (filldraw) %D fullcircle xscaled 100 yscaled 40 shifted (50,50) withpen pencircle scaled 4; %D \stoptyping %D %D The a (or angle) alternative supports arbitrary angles and %D is therefore more versatile. %D %D \starttyping %D stripe_path_a %D (withpen pencircle scaled 2 withcolor red) %D (draw) %D fullcircle xscaled 100 yscaled 40 withcolor blue; %D \stoptyping %D %D We have two alternatives, controlled by arguments or defaults (when arguments %D are zero). %D %D The newer and nicer interface is used as follows (triggered by a question by Mari): %D %D \starttyping %D draw image (draw fullcircle scaled 3cm shifted (0cm,0cm) withcolor green) numberstriped (1,10,3) withcolor red ; %D draw image (draw fullcircle scaled 3cm shifted (3cm,0cm) withcolor green) numberstriped (2,20,3) withcolor green ; %D draw image (draw fullcircle scaled 3cm shifted (3cm,3cm) withcolor green) numberstriped (3,10,5) withcolor blue ; %D draw image (draw fullcircle scaled 3cm shifted (0cm,3cm) withcolor green) numberstriped (4,20,5) withcolor yellow ; %D %D draw image (draw fullcircle scaled 3cm shifted (6cm,0cm) withcolor green) anglestriped (1,20,2) withcolor red ; %D draw image (draw fullcircle scaled 3cm shifted (9cm,0cm) withcolor green) anglestriped (2,40,2) withcolor green ; %D draw image (draw fullcircle scaled 3cm shifted (9cm,3cm) withcolor green) anglestriped (3,60,2) withcolor blue ; %D draw image (draw fullcircle scaled 3cm shifted (6cm,3cm) withcolor green) anglestriped (4,80,2) withcolor yellow ; %D %D draw image ( %D draw fullcircle scaled 3cm shifted (0cm,0cm) withcolor green withpen pencircle scaled 2mm ; %D draw fullcircle scaled 2cm shifted (0cm,1cm) withcolor blue withpen pencircle scaled 3mm ; %D ) shifted (9cm,0cm) numberstriped (1,10,3) withcolor red ; %D %D draw image ( %D draw fullcircle scaled 3cm shifted (0cm,0cm) withcolor green withpen pencircle scaled 2mm ; %D draw fullcircle scaled 2cm shifted (0cm,1cm) withcolor blue withpen pencircle scaled 3mm ; %D ) shifted (12cm,0cm) numberstriped (2,10,3) withcolor red ; %D %D draw image ( %D draw fullcircle scaled 3cm shifted (0cm,0cm) withcolor green withpen pencircle scaled 2mm ; %D draw fullcircle scaled 2cm shifted (0cm,1cm) withcolor blue withpen pencircle scaled 3mm ; %D ) shifted (9cm,5cm) numberstriped (3,10,3) withcolor red ; %D %D draw image ( %D draw fullcircle scaled 3cm shifted (0cm,0cm) withcolor green withpen pencircle scaled 2mm ; %D draw fullcircle scaled 2cm shifted (0cm,1cm) withcolor blue withpen pencircle scaled 3mm ; %D ) shifted (12cm,5cm) numberstriped (4,10,3) withcolor red ; %D \stoptyping stripe_n := 10; stripe_slot := 3; stripe_gap := 5; stripe_angle := 45; def mfun_tool_striped_number_action text extra = for i = 1/used_n step 1/used_n until 1 : draw point (1+i) of bounds -- point (3-i) of bounds withpen pencircle scaled penwidth extra ; endfor ; for i = 0 step 1/used_n until 1 : draw point (3+i) of bounds -- point (1-i) of bounds withpen pencircle scaled penwidth extra ; endfor ; enddef ; def mfun_tool_striped_set_options(expr option) = save isinner, swapped ; boolean isinner, swapped ; if option = 1 : isinner := false ; swapped := false ; elseif option = 2 : isinner := true ; swapped := false ; elseif option = 3 : isinner := false ; swapped := true ; elseif option = 4 : isinner := true ; swapped := true ; else : isinner := false ; swapped := false ; fi ; enddef ; vardef mfun_tool_striped_number(expr option, p, s_n, s_slot) text extra = image ( begingroup ; save pattern, shape, bounds, penwidth, used_n, used_slot ; picture pattern, shape ; path bounds ; numeric used_s, used_slot ; mfun_tool_striped_set_options(option) ; used_slot := if s_slot = 0 : stripe_slot else : s_slot fi ; used_n := if s_n = 0 : stripe_n else : s_n fi ; shape := image(draw p) ; bounds := boundingbox shape ; penwidth := min(ypart urcorner shape - ypart llcorner shape, xpart urcorner shape - xpart llcorner shape) / (used_slot * used_n) ; pattern := image ( if isinner : mfun_tool_striped_number_action extra ; for s within shape : if stroked s or filled s : clip currentpicture to pathpart s ; fi endfor ; else : for s within shape : if stroked s or filled s : draw image ( mfun_tool_striped_number_action extra ; clip currentpicture to pathpart s ; ) ; fi ; endfor ; fi ; ) ; if swapped : addto currentpicture also shape ; addto currentpicture also pattern ; else : addto currentpicture also pattern ; addto currentpicture also shape ; fi ; endgroup ; ) enddef ; def mfun_tool_striped_angle_action text extra = for i = minimum -.5used_gap step used_gap until maximum : draw (minimum,i) -- (maximum,i) extra ; endfor ; currentpicture := currentpicture rotated used_angle ; enddef ; vardef mfun_tool_striped_angle(expr option, p, s_angle, s_gap) text extra = image ( begingroup ; save pattern, shape, mask, maximum, minimum, centrum, used_angle, used_gap ; picture pattern, shape, mask ; numeric maximum, minimum ; pair centrum ; numeric used_angle, used_gap ; mfun_tool_striped_set_options(option) ; used_angle := if s_angle = 0 : stripe_angle else : s_angle fi ; used_gap := if s_gap = 0 : stripe_gap else : s_gap fi ; shape := image(draw p) ; centrum := center shape ; shape := shape shifted - centrum ; mask := shape rotated used_angle ; maximum := max (xpart llcorner mask, xpart urcorner mask, ypart llcorner mask, ypart urcorner mask) ; minimum := min (xpart llcorner mask, xpart urcorner mask, ypart llcorner mask, ypart urcorner mask) ; pattern := image ( if isinner : mfun_tool_striped_angle_action extra ; for s within shape : if stroked s or filled s : clip currentpicture to pathpart s ; fi endfor ; else : for s within shape : if stroked s or filled s : draw image ( mfun_tool_striped_angle_action extra ; clip currentpicture to pathpart s ; ) ; fi ; endfor ; fi ; ) ; if swapped : addto currentpicture also shape ; addto currentpicture also pattern ; else : addto currentpicture also pattern ; addto currentpicture also shape ; fi ; currentpicture := currentpicture shifted centrum ; endgroup ; ) enddef; newinternal striped_normal_inner ; striped_normal_inner := 1 ; newinternal striped_reverse_inner ; striped_reverse_inner := 2 ; newinternal striped_normal_outer ; striped_normal_outer := 3 ; newinternal striped_reverse_outer ; striped_reverse_outer := 4 ; secondarydef p anglestriped s = mfun_tool_striped_angle(redpart s,p,greenpart s,bluepart s) enddef ; secondarydef p numberstriped s = mfun_tool_striped_number(redpart s,p,greenpart s,bluepart s) enddef ; % for old times sake: def stripe_path_n (text s_spec) (text s_draw) expr s_path = do_stripe_path_n (s_spec) (s_draw) (s_path) enddef; def do_stripe_path_n (text s_spec) (text s_draw) (expr s_path) text s_text = draw image(s_draw s_path s_text) numberstriped(3,0,0) s_spec ; enddef ; def stripe_path_a (text s_spec) (text s_draw) expr s_path = do_stripe_path_a (s_spec) (s_draw) (s_path) enddef; def do_stripe_path_a (text s_spec) (text s_draw) (expr s_path) text s_text = draw image(s_draw s_path s_text) anglestriped(3,0,0) s_spec ; enddef ; %D A few normalizing macros: primarydef p xsized w = (p if (bbwidth (p)>0) and (w>0) : scaled (w/bbwidth (p)) fi) enddef ; primarydef p ysized h = (p if (bbheight(p)>0) and (h>0) : scaled (h/bbheight(p)) fi) enddef ; primarydef p xysized s = begingroup save wh, w, h ; pair wh ; numeric w, h ; wh := paired (s) ; w := bbwidth(p) ; h := bbheight(p) ; p if (w>0) and (h>0) : if xpart wh > 0 : xscaled (xpart wh/w) fi if ypart wh > 0 : yscaled (ypart wh/h) fi fi endgroup enddef ; let sized = xysized ; def xscale_currentpicture(expr w) = % obsolete currentpicture := currentpicture xsized w ; enddef; def yscale_currentpicture(expr h) = % obsolete currentpicture := currentpicture ysized h ; enddef; def xyscale_currentpicture(expr w, h) = % obsolete currentpicture := currentpicture xysized (w,h) ; enddef; def scale_currentpicture(expr w, h) = % obsolete currentpicture := currentpicture xsized w ; currentpicture := currentpicture ysized h ; enddef; %D A full circle is centered at the origin, while a unitsquare %D is located in the first quadrant. Now guess what kind of %D path fullsquare and unitcircle do return. path fullsquare, unitcircle ; fullsquare := unitsquare shifted - center unitsquare ; unitcircle := fullcircle shifted urcorner fullcircle ; %D Some more paths: path urcircle, ulcircle, llcircle, lrcircle ; urcircle := origin -- (+.5,0) & (+.5,0){up} .. (0,+.5) & (0,+.5) -- cycle ; ulcircle := origin -- (0,+.5) & (0,+.5){left} .. (-.5,0) & (-.5,0) -- cycle ; llcircle := origin -- (-.5,0) & (-.5,0){down} .. (0,-.5) & (0,-.5) -- cycle ; lrcircle := origin -- (0,-.5) & (0,-.5){right} .. (+.5,0) & (+.5,0) -- cycle ; path tcircle, bcircle, lcircle, rcircle ; tcircle = origin -- (+.5,0) & (+.5,0) {up} .. (0,+.5) .. {down} (-.5,0) -- cycle ; bcircle = origin -- (-.5,0) & (-.5,0) {down} .. (0,-.5) .. {up} (+.5,0) -- cycle ; lcircle = origin -- (0,+.5) & (0,+.5) {left} .. (-.5,0) .. {right} (0,-.5) -- cycle ; rcircle = origin -- (0,-.5) & (0,-.5) {right} .. (+.5,0) .. {left} (0,+.5) -- cycle ; path urtriangle, ultriangle, lltriangle, lrtriangle ; % watch out: it's contrary to what you expect and starts in the origin urtriangle := origin -- (+.5,0) -- (0,+.5) -- cycle ; ultriangle := origin -- (0,+.5) -- (-.5,0) -- cycle ; lltriangle := origin -- (-.5,0) -- (0,-.5) -- cycle ; lrtriangle := origin -- (0,-.5) -- (+.5,0) -- cycle ; path triangle, uptriangle, downtriangle, lefttriangle, righttriangle ; triangle := (1,0) -- (1,0) rotated 120 -- (1,0) rotated -120 -- cycle ; uptriangle := triangle rotated 90 ; downtriangle := triangle rotated -90 ; lefttriangle := triangle rotated 180 ; righttriangle := triangle ; path unitdiamond, fulldiamond ; unitdiamond := (.5,0) -- (1,.5) -- (.5,1) -- (0,.5) -- cycle ; fulldiamond := unitdiamond shifted - center unitdiamond ; %D More robust: % let normalscaled = scaled ; % let normalxscaled = xscaled ; % let normalyscaled = yscaled ; % % def scaled expr s = normalscaled (s) enddef ; % def xscaled expr s = normalxscaled (s) enddef ; % def yscaled expr s = normalyscaled (s) enddef ; %D Shorter primarydef p xyscaled q = % secundarydef does not work out well begingroup save qq ; pair qq ; qq = paired(q) ; p if xpart qq <> 0 : xscaled (xpart qq) fi if ypart qq <> 0 : yscaled (ypart qq) fi endgroup enddef ; %D Some personal code that might move to another module (todo: save). def set_grid(expr w, h, nx, ny) = boolean grid[][] ; boolean grid_full ; numeric grid_w, grid_h, grid_nx, grid_ny, grid_x, grid_y, grid_left ; grid_w := w ; grid_h := h ; grid_nx := nx ; grid_ny := ny ; grid_x := round(w/grid_nx) ; % +.5) ; grid_y := round(h/grid_ny) ; % +.5) ; grid_left := (1+grid_x)*(1+grid_y) ; grid_full := false ; for i=0 upto grid_x : for j=0 upto grid_y : grid[i][j] := false ; endfor ; endfor ; enddef ; vardef new_on_grid(expr _dx_, _dy_) = dx := _dx_ ; dy := _dy_ ; ddx := min(round(dx/grid_nx),grid_x) ; % +.5),grid_x) ; ddy := min(round(dy/grid_ny),grid_y) ; % +.5),grid_y) ; if not grid_full and not grid[ddx][ddy] : grid[ddx][ddy] := true ; grid_left := grid_left-1 ; grid_full := (grid_left=0) ; true else : false fi enddef ; %D usage: \type{innerpath peepholed outerpath}. %D %D beginfig(1); %D def fullsquare = (unitsquare shifted -center unitsquare) enddef ; %D fill (fullsquare scaled 200) withcolor red ; %D path p ; p := (fullcircle scaled 100) ; bboxmargin := 0 ; %D fill p peepholed bbox p ; %D endfig; secondarydef p peepholed q = begingroup save start ; pair start ; start := point 0 of p ; if xpart start >= xpart center p : if ypart start >= ypart center p : urcorner q -- ulcorner q -- llcorner q -- lrcorner q -- reverse p -- lrcorner q -- cycle else : lrcorner q -- urcorner q -- ulcorner q -- llcorner q -- reverse p -- llcorner q -- cycle fi else : if ypart start > ypart center p : ulcorner q -- llcorner q -- lrcorner q -- urcorner q -- reverse p -- urcorner q -- cycle else : llcorner q -- lrcorner q -- urcorner q -- ulcorner q -- reverse p -- ulcorner q -- cycle fi fi endgroup enddef ; boolean intersection_found ; secondarydef p intersection_point q = begingroup save x_, y_ ; (x_,y_) = p intersectiontimes q ; if x_< 0 : intersection_found := false ; center p % origin else : intersection_found := true ; .5[point x_ of p, point y_ of q] fi endgroup enddef ; %D New, undocumented, experimental: vardef tensecircle (expr width, height, offset) = (-width/2,-height/2) ... (0,-height/2-offset) ... (+width/2,-height/2) ... (+width/2+offset,0) ... (+width/2,+height/2) ... (0,+height/2+offset) ... (-width/2,+height/2) ... (-width/2-offset,0) ... cycle enddef ; vardef roundedsquare (expr width, height, offset) = (offset,0) -- (width-offset,0) {right} .. (width,offset) -- (width,height-offset) {up} .. (width-offset,height) -- (offset,height) {left} .. (0,height-offset) -- (0,offset) {down} .. cycle enddef ; vardef roundedsquarexy (expr width, height, dx, dy) = (dx,0) -- (width-dx,0) {right} .. (width,dy) -- (width,height-dy) {up} .. (width-dx,height) -- (dx,height) {left} .. (0,height-dy) -- (0,dy) {down} .. cycle enddef ; %D Some colors. def resolvedcolor(expr s) = .5white enddef ; let normalwithcolor = withcolor ; def withcolor expr c = normalwithcolor if string c : resolvedcolor(c) else : c fi enddef ; % I don't want a "withcolor black" in case of an empty string ... who knows % how that can interfere with outer colors. Somehow the next one doesn't % always work out ok, but why ... must be some parsing issue. Anyway, when % we cannot do that, we need to fix some chem macros instead as empty strings % now lead to black while everywhere else in context empty means: leave color % untouched. % def withcolor expr c = % if not string c : % normalwithcolor c % elseif c <> "" : % normalwithcolor resolvedcolor(c) % fi % enddef ; % So why does this work better than the above: % % def withcolor expr c = % if string c : % if c <> "" : % normalwithcolor resolvedcolor(c) % fi % else : % normalwithcolor c % fi % enddef ; vardef colortype expr c = if cmykcolor c : cmykcolor elseif rgbcolor c : rgbcolor elseif numeric c : grayscale fi enddef ; vardef whitecolor expr c = if cmykcolor c : (0,0,0,0) elseif rgbcolor c : (1,1,1) elseif numeric c : 1 elseif string c : whitecolor resolvedcolor(c) fi enddef ; vardef blackcolor expr c = if cmykcolor c : (0,0,0,1) elseif rgbcolor c : (0,0,0) elseif numeric c : 0 elseif string c : blackcolor resolvedcolor(c) fi enddef ; vardef complementary expr c = if cmykcolor c : (1,1,1,1) - c elseif rgbcolor c : (1,1,1) - c elseif pair c : (1,1) - c elseif numeric c : 1 - c elseif string c : complementary resolvedcolor(c) fi enddef ; vardef complemented expr c = save m ; if cmykcolor c : m := max(cyanpart c, magentapart c, yellowpart c, blackpart c) ; (m,m,m,m) - c elseif rgbcolor c : m := max(redpart c, greenpart c, bluepart c) ; (m,m,m) - c elseif pair c : m := max(xpart c, ypart c) ; (m,m) - c elseif numeric c : m - c elseif string c : complemented resolvedcolor(c) fi enddef ; %D Well, this is the dangerous and naive version: def drawfill text t = fill t ; draw t ; enddef; %D This two step approach saves the path first, since it can %D be a function. Attributes must not be randomized. def drawfill expr c = path _c_ ; _c_ := c ; mfun_do_drawfill enddef ; def mfun_do_drawfill text t = draw _c_ t ; fill _c_ t ; enddef; def undrawfill expr c = drawfill c withcolor background % rather useless enddef ; %D Moved from mp-char.mp vardef paired primary d = if pair d : d else : (d,d) fi enddef ; vardef tripled primary d = if color d : d else : (d,d,d) fi enddef ; % maybe secondaries: primarydef p enlarged d = ( p llmoved d -- p lrmoved d -- p urmoved d -- p ulmoved d -- cycle ) enddef ; primarydef p llenlarged d = ( p llmoved d -- lrcorner p -- urcorner p -- ulcorner p -- cycle ) enddef ; primarydef p lrenlarged d = ( llcorner p -- p lrmoved d -- urcorner p -- ulcorner p -- cycle ) enddef ; primarydef p urenlarged d = ( llcorner p -- lrcorner p -- p urmoved d -- ulcorner p -- cycle ) enddef ; primarydef p ulenlarged d = ( llcorner p -- lrcorner p -- urcorner p -- p ulmoved d -- cycle ) enddef ; primarydef p llmoved d = ( (llcorner p) shifted (-xpart paired(d),-ypart paired(d)) ) enddef ; primarydef p lrmoved d = ( (lrcorner p) shifted (+xpart paired(d),-ypart paired(d)) ) enddef ; primarydef p urmoved d = ( (urcorner p) shifted (+xpart paired(d),+ypart paired(d)) ) enddef ; primarydef p ulmoved d = ( (ulcorner p) shifted (-xpart paired(d),+ypart paired(d)) ) enddef ; primarydef p leftenlarged d = ( (llcorner p) shifted (-d,0) -- lrcorner p -- urcorner p -- (ulcorner p) shifted (-d,0) -- cycle ) enddef ; primarydef p rightenlarged d = ( llcorner p -- (lrcorner p) shifted (d,0) -- (urcorner p) shifted (d,0) -- ulcorner p -- cycle ) enddef ; primarydef p topenlarged d = ( llcorner p -- lrcorner p -- (urcorner p) shifted (0,d) -- (ulcorner p) shifted (0,d) -- cycle ) enddef ; primarydef p bottomenlarged d = ( llcorner p shifted (0,-d) -- lrcorner p shifted (0,-d) -- urcorner p -- ulcorner p -- cycle ) enddef ; %D Handy as stepper: vardef rotation(expr i, n) = if (n == 0) : 0 else : i * 360 / n fi enddef ; %D Handy for testing/debugging: primarydef p crossed d = ( if pair p : p shifted (-d, 0) -- p -- p shifted ( 0,-d) -- p -- p shifted (+d, 0) -- p -- p shifted ( 0,+d) -- p -- cycle else : center p shifted (-d, 0) -- llcorner p -- center p shifted ( 0,-d) -- lrcorner p -- center p shifted (+d, 0) -- urcorner p -- center p shifted ( 0,+d) -- ulcorner p -- cycle fi ) enddef ; %D Also handy (math ladders): vardef laddered primary p = % was expr point 0 of p for i=1 upto length(p) : -- (xpart (point i of p), ypart (point (i-1) of p)) -- (point i of p) endfor enddef ; %D Saves typing: % vardef bottomboundary primary p = (llcorner p -- lrcorner p) enddef ; % vardef rightboundary primary p = (lrcorner p -- urcorner p) enddef ; % vardef topboundary primary p = (urcorner p -- ulcorner p) enddef ; % vardef leftboundary primary p = (ulcorner p -- llcorner p) enddef ; vardef bottomboundary primary p = if pair p : p else : (llcorner p -- lrcorner p) fi enddef ; vardef rightboundary primary p = if pair p : p else : (lrcorner p -- urcorner p) fi enddef ; vardef topboundary primary p = if pair p : p else : (urcorner p -- ulcorner p) fi enddef ; vardef leftboundary primary p = if pair p : p else : (ulcorner p -- llcorner p) fi enddef ; %D Nice too: primarydef p superellipsed s = superellipse ( .5[lrcorner p,urcorner p], .5[urcorner p,ulcorner p], .5[ulcorner p,llcorner p], .5[llcorner p,lrcorner p], s ) enddef ; primarydef p squeezed s = ( (llcorner p .. .5[llcorner p,lrcorner p] shifted ( 0, ypart paired(s)) .. lrcorner p) & (lrcorner p .. .5[lrcorner p,urcorner p] shifted (-xpart paired(s), 0) .. urcorner p) & (urcorner p .. .5[urcorner p,ulcorner p] shifted ( 0,-ypart paired(s)) .. ulcorner p) & (ulcorner p .. .5[ulcorner p,llcorner p] shifted ( xpart paired(s), 0) .. llcorner p) & cycle ) enddef ; primarydef p randomshifted s = begingroup ; save ss ; pair ss ; ss := paired(s) ; p shifted (-.5xpart ss + uniformdeviate xpart ss,-.5ypart ss + uniformdeviate ypart ss) endgroup enddef ; vardef mfun_randomized_path(expr p,s) = for i=0 upto length(p)-1 : (point i of p) .. controls ((postcontrol i of p) randomshifted s) and ((precontrol (i+1) of p) randomshifted s) .. endfor if cycle p : cycle else : (point length(p) of p) fi enddef; vardef mfun_randomized_picture(expr p,s)(text rnd) = save currentpicture ; picture currentpicture ; currentpicture := nullpicture ; for i within p : addto currentpicture if stroked i : doublepath pathpart i rnd s dashed dashpart i withpen penpart i withcolor colorpart i withprescript prescriptpart i withpostscript postscriptpart i elseif filled i : contour pathpart i rnd s withpen penpart i withcolor colorpart i withprescript prescriptpart i withpostscript postscriptpart i else : also i fi ; endfor ; currentpicture enddef ; primarydef p randomizedcontrols s = ( if path p : mfun_randomized_path(p,s) elseif picture p : mfun_randomized_picture(p,s)(randomizedcontrols) else : p randomized s fi ) enddef ; primarydef p randomized s = ( if path p : for i=0 upto length(p)-1 : ((point i of p) randomshifted s) .. controls ((postcontrol i of p) randomshifted s) and ((precontrol (i+1) of p) randomshifted s) .. endfor if cycle p : cycle else : ((point length(p) of p) randomshifted s) fi elseif pair p : p randomshifted s elseif cmykcolor p : if color s : ((uniformdeviate cyanpart s) * cyanpart p, (uniformdeviate magentapart s) * magentapart p, (uniformdeviate yellowpart s) * yellowpart p, (uniformdeviate blackpart s) * blackpart p) elseif pair s : ((xpart s + (uniformdeviate (ypart s - xpart s))) * p) else : ((uniformdeviate s) * p) fi elseif rgbcolor p : if color s : ((uniformdeviate redpart s) * redpart p, (uniformdeviate greenpart s) * greenpart p, (uniformdeviate bluepart s) * bluepart p) elseif pair s : ((xpart s + (uniformdeviate (ypart s - xpart s))) * p) else : ((uniformdeviate s) * p) fi elseif color p : if color s : ((uniformdeviate greypart s) * greypart p) elseif pair s : ((xpart s + (uniformdeviate (ypart s - xpart s))) * p) else : ((uniformdeviate s) * p) fi elseif string p : (resolvedcolor(p)) randomized s elseif picture p : mfun_randomized_picture(p,s)(randomized) else : % p - s/2 + uniformdeviate s % would have been better but we want to be positive p + uniformdeviate s fi ) enddef ; %D Not perfect (alternative for interpath) vardef interpolated(expr s, p, q) = save m ; numeric m ; m := max(length(p),length(q)) ; if path p : for i=0 upto m-1 : s[point (i /m) along p,point (i /m) along q] .. controls s[postcontrol (i /m) along p,postcontrol (i /m) along q] and s[precontrol ((i+1)/m) along p,precontrol ((i+1)/m) along q] .. endfor if cycle p : cycle else : s[point infinity of p,point infinity of q] fi else : a[p,q] fi enddef ; %D Interesting too: % primarydef p paralleled d = ( % p shifted if d < 0 : - fi ((point abs(d) on (p rotatedaround(point 0 of p,90))) - point 0 of p) % ) enddef ; % % primarydef p paralleled d = ( % p shifted ((d*unitvector(direction 0 of p) - point 0 of p) rotated 90) % ) enddef ; % % Alan came up with an improved version and stepwise we ended up with (or % might up with a variant of): vardef perpendicular expr t of p = unitvector((direction t of p) rotated 90) enddef ; def istextext(expr p) = (picture p and ((substring(0,3) of prescriptpart p) = "tx_")) enddef ; primarydef p paralleled d = ( if path p : begingroup ; save dp ; pair dp ; for i=0 upto length p if cycle p : -1 fi : hide(dp := d * perpendicular i of p) if i > 0 : .. fi (point i of p + dp) if i < length p : .. controls (postcontrol i of p + dp) and (precontrol (i+1) of p + dp) fi endfor if cycle p : .. cycle fi endgroup elseif picture p : image( for i within p : draw (pathpart i) if not istextext(i) : % dirty trick paralleled d fi mfun_decoration_i i ; endfor ; ) elseif pair p : p fi ) enddef ; vardef punked primary p = point 0 of p for i=1 upto length(p)-1 : -- point i of p endfor if cycle p : -- cycle else : -- point length(p) of p fi enddef ; vardef curved primary p = point 0 of p for i=1 upto length(p)-1 : .. point i of p endfor if cycle p : .. cycle else : .. point length(p) of p fi enddef ; primarydef p blownup s = begingroup save _p_ ; path _p_ ; _p_ := p xysized (bbwidth(p)+2(xpart paired(s)),bbheight(p)+2(ypart paired(s))) ; (_p_ shifted (center p - center _p_)) endgroup enddef ; %D Rather fundamental. % not yet ok vardef leftrightpath(expr p, l) = % used in s-pre-19 save q, r, t, b ; path q, r ; pair t, b ; t := (ulcorner p -- urcorner p) intersection_point p ; b := (llcorner p -- lrcorner p) intersection_point p ; r := if xpart directionpoint t of p < 0 : reverse p else : p fi ; % r is needed, else problems when reverse is fed q := r cutbefore if l: t else: b fi ; q := q if xpart point 0 of r > 0 : & r fi cutafter if l: b else: t fi ; q enddef ; vardef leftpath expr p = leftrightpath(p,true ) enddef ; vardef rightpath expr p = leftrightpath(p,false) enddef ; %D Drawoptions def saveoptions = save _op_ ; def _op_ = enddef ; enddef ; %D Tracing. (not yet in lexer) let normaldraw = draw ; let normalfill = fill ; % bugged in mplib so ... def normalfill expr c = addto currentpicture contour c _op_ enddef ; def normaldraw expr p = addto currentpicture if picture p: also p else: doublepath p withpen currentpen fi _op_ enddef ; def drawlineoptions (text t) = def _lin_opt_ = t enddef ; enddef ; def drawpointoptions (text t) = def _pnt_opt_ = t enddef ; enddef ; def drawcontroloptions(text t) = def _ctr_opt_ = t enddef ; enddef ; def drawlabeloptions (text t) = def _lab_opt_ = t enddef ; enddef ; def draworiginoptions (text t) = def _ori_opt_ = t enddef ; enddef ; def drawboundoptions (text t) = def _bnd_opt_ = t enddef ; enddef ; def drawpathoptions (text t) = def _pth_opt_ = t enddef ; enddef ; numeric drawoptionsfactor ; drawoptionsfactor := pt ; def resetdrawoptions = drawlineoptions (withpen pencircle scaled 1.0 drawoptionsfactor withcolor .5white) ; drawpointoptions (withpen pencircle scaled 4.0 drawoptionsfactor withcolor black) ; drawcontroloptions(withpen pencircle scaled 2.5 drawoptionsfactor withcolor black) ; drawlabeloptions () ; draworiginoptions (withpen pencircle scaled 1.0 drawoptionsfactor withcolor .5white) ; drawboundoptions (dashed evenly _ori_opt_) ; drawpathoptions (withpen pencircle scaled 5.0 drawoptionsfactor withcolor .8white) ; enddef ; resetdrawoptions ; %D Path. def drawpath expr p = normaldraw p _pth_opt_ enddef ; %D Arrow. newinternal ahvariant ; ahvariant := 0 ; newinternal ahdimple ; ahdimple := 1/5 ; newinternal ahscale ; ahscale := 3/4 ; vardef arrowhead expr p = save q, e, r ; pair e ; e = point length p of p ; path q ; q = gobble(p shifted -e cutafter makepath(pencircle scaled (2ahlength))) cuttings ; if ahvariant > 0: path r ; r = gobble(p shifted -e cutafter makepath(pencircle scaled ((1-ahdimple)*2ahlength))) cuttings ; fi (q rotated (ahangle/2) & reverse q rotated -(ahangle/2) if ahvariant = 1 : -- point 0 of r -- elseif ahvariant = 2 : ... point 0 of r ... else : -- fi cycle ) shifted e enddef ; vardef drawarrowpath expr p = save autoarrows ; boolean autoarrows ; autoarrows := true ; drawarrow p _pth_opt_ enddef ; def midarrowhead expr p = arrowhead p cutafter (point length(p cutafter point .5 along p) + ahlength on p) enddef ; vardef arrowheadonpath (expr p, s) = save autoarrows ; boolean autoarrows ; autoarrows := true ; set_ahlength(scaled ahfactor) ; % added arrowhead p if s < 1 : cutafter (point (s*arclength(p) + (ahlength/2)) on p) fi enddef ; def resetarrows = hide ( ahlength := 4 ; ahangle := 45 ; ahvariant := 0 ; ahdimple := 1/5 ; ahscale := 3/4 ; ) enddef ; %D Points. def drawpoint expr c = if string c : string _c_ ; _c_ := "(" & c & ")" ; dotlabel.urt(_c_, scantokens _c_) ; drawdot scantokens _c_ else : dotlabel.urt("(" & decimal xpart c & "," & decimal ypart c & ")", c) ; drawdot c fi _pnt_opt_ enddef ; %D PathPoints. def drawpoints expr c = path _c_ ; _c_ := c ; mfun_draw_points enddef ; def drawcontrolpoints expr c = path _c_ ; _c_ := c ; mfun_draw_controlpoints enddef ; def drawcontrollines expr c = path _c_ ; _c_ := c ; mfun_draw_controllines enddef ; def drawpointlabels expr c = path _c_ ; _c_ := c ; mfun_draw_pointlabels enddef ; def mfun_draw_points text t = for _i_=0 upto length(_c_) if cycle _c_ : -1 fi : normaldraw point _i_ of _c_ _pnt_opt_ t ; endfor ; enddef; def mfun_draw_controlpoints text t = for _i_=0 upto length(_c_) : normaldraw precontrol _i_ of _c_ _ctr_opt_ t ; normaldraw postcontrol _i_ of _c_ _ctr_opt_ t ; endfor ; enddef; def mfun_draw_controllines text t = for _i_=0 upto length(_c_) : normaldraw point _i_ of _c_ -- precontrol _i_ of _c_ _lin_opt_ t ; normaldraw point _i_ of _c_ -- postcontrol _i_ of _c_ _lin_opt_ t ; endfor ; enddef; boolean swappointlabels ; swappointlabels := false ; numeric pointlabelscale ; pointlabelscale := 0 ; string pointlabelfont ; pointlabelfont := "" ; def mfun_draw_pointlabels text t = for _i_=0 upto length(_c_) if cycle _c_ : -1 fi : pair _u_ ; _u_ := unitvector(direction _i_ of _c_) rotated if swappointlabels : - fi 90 ; pair _p_ ; _p_ := (point _i_ of _c_) ; begingroup ; if pointlabelscale > 0 : save defaultscale ; numeric defaultscale ; defaultscale := pointlabelscale ; fi ; if pointlabelfont <> "" : save defaultfont ; string defaultfont ; defaultfont := pointlabelfont ; fi ; _u_ := 10 * drawoptionsfactor * defaultscale * _u_ ; normaldraw thelabel ( decimal _i_, _p_ shifted if cycle _c_ and (_i_=0) : - fi _u_ ) _lab_opt_ t ; endgroup ; endfor ; enddef; %D Bounding box. def drawboundingbox expr p = normaldraw boundingbox p _bnd_opt_ enddef ; %D Origin. numeric originlength ; originlength := .5cm ; def draworigin text t = normaldraw (origin shifted (0, originlength) -- origin shifted (0,-originlength)) _ori_opt_ t ; normaldraw (origin shifted ( originlength,0) -- origin shifted (-originlength,0)) _ori_opt_ t ; enddef; %D Axis. numeric tickstep ; tickstep := 5mm ; numeric ticklength ; ticklength := 2mm ; def drawxticks expr c = path _c_ ; _c_ := c ; mfun_draw_xticks enddef ; def drawyticks expr c = path _c_ ; _c_ := c ; mfun_draw_yticks enddef ; def drawticks expr c = path _c_ ; _c_ := c ; mfun_draw_ticks enddef ; % Adding eps prevents disappearance due to rounding errors. def mfun_draw_xticks text t = for i=0 step -tickstep until xpart llcorner _c_ - eps : if (i<=xpart lrcorner _c_) : normaldraw (i,-ticklength)--(i,ticklength) _ori_opt_ t ; fi ; endfor ; for i=0 step tickstep until xpart lrcorner _c_ + eps : if (i>=xpart llcorner _c_) : normaldraw (i,-ticklength)--(i,ticklength) _ori_opt_ t ; fi ; endfor ; normaldraw (llcorner _c_ -- ulcorner _c_) shifted (-xpart llcorner _c_,0) _ori_opt_ t ; enddef ; def mfun_draw_yticks text t = for i=0 step -tickstep until ypart llcorner _c_ - eps : if (i<=ypart ulcorner _c_) : normaldraw (-ticklength,i)--(ticklength,i) _ori_opt_ t ; fi ; endfor ; for i=0 step tickstep until ypart ulcorner _c_ + eps : if (i>=ypart llcorner _c_) : normaldraw (-ticklength,i)--(ticklength,i) _ori_opt_ t ; fi ; endfor ; normaldraw (llcorner _c_ -- lrcorner _c_) shifted (0,-ypart llcorner _c_) _ori_opt_ t ; enddef ; def mfun_draw_ticks text t = drawxticks _c_ t ; drawyticks _c_ t ; enddef ; %D All of it except axis. def drawwholepath expr p = draworigin ; drawpath p ; drawcontrollines p ; drawcontrolpoints p ; drawpoints p ; drawboundingbox p ; drawpointlabels p ; enddef ; def drawpathonly expr p = drawpath p ; drawcontrollines p ; drawcontrolpoints p ; drawpoints p ; drawpointlabels p ; enddef ; %D Tracing. def visualizeddraw expr c = if picture c : normaldraw c else : path _c_ ; _c_ := c ; do_visualizeddraw fi enddef ; def visualizedfill expr c = if picture c : normalfill c else : path _c_ ; _c_ := c ; do_visualizedfill fi enddef ; def do_visualizeddraw text t = draworigin ; drawpath _c_ t ; drawcontrollines _c_ ; drawcontrolpoints _c_ ; drawpoints _c_ ; drawboundingbox _c_ ; drawpointlabels _c_ ; enddef ; def do_visualizedfill text t = if cycle _c_ : normalfill _c_ t fi ; draworigin ; drawcontrollines _c_ ; drawcontrolpoints _c_ ; drawpoints _c_ ; drawboundingbox _c_ ; drawpointlabels _c_ ; enddef ; def detaileddraw expr c = if picture c : normaldraw c else : path _c_ ; _c_ := c ; do_detaileddraw fi enddef ; def do_detaileddraw text t = drawpath _c_ t ; drawcontrollines _c_ ; drawcontrolpoints _c_ ; drawpoints _c_ ; % % for labels we need an third run (as the second will mark the numbers); i could preroll them % % but then the hash needs to handle that as well (as now we keep numbering) % drawpointlabels _c_ ; enddef ; def visualizepaths = let fill = visualizedfill ; let draw = visualizeddraw ; enddef ; def detailpaths = let draw = detaileddraw ; enddef ; def naturalizepaths = let fill = normalfill ; let draw = normaldraw ; enddef ; extra_endfig := extra_endfig & " naturalizepaths ; " ; %D Nice tracer: def drawboundary primary p = draw p dashed evenly withcolor white ; draw p dashed oddly withcolor black ; draw (- llcorner p) withpen pencircle scaled 3 withcolor white ; draw (- llcorner p) withpen pencircle scaled 1.5 withcolor black ; enddef ; %D Also handy: extra_beginfig := extra_beginfig & " truecorners := 0 ; " ; % restores extra_beginfig := extra_beginfig & " miterlimit := 10 ; " ; % restores extra_beginfig := extra_beginfig & " linejoin := rounded ; " ; % restores extra_beginfig := extra_beginfig & " linecap := rounded ; " ; % restores %D Normally, arrowheads don't scale well. So we provide a hack. boolean autoarrows ; autoarrows := false ; numeric ahfactor ; ahfactor := 2.5 ; def set_ahlength (text t) = % called to often % ahlength := (ahfactor*pen_size(_op_ t)) ; % _op_ added % problem: _op_ can contain color so a no-go, we could apply the transform % but i need to figure out the best way (fakepicture and take components). ahlength := (ahfactor*pen_size(t)) ; enddef ; vardef pen_size (text t) = save p ; picture p ; p := nullpicture ; addto p doublepath (top origin -- bot origin) t ; (ypart urcorner p - ypart lrcorner p) enddef ; %D The next two macros are adapted versions of plain %D \METAPOST\ definitions. vardef arrowpath expr p = % patch by Peter Rolf: supports squared pen and shifting (hh: maybe just use center of head as first) (p cutafter makepath(pencircle scaled (if ahvariant > 0 : (1-ahdimple)* fi 2ahlength*cosd(ahangle/2)) shifted point length p of p )) enddef; % New experimental extension: also handling pictures: % % drawarrow fullsquare scaled 2cm withcolor green ; % drawarrow fullcircle scaled 3cm withcolor green ; % drawarrow image ( % draw fullsquare scaled 4cm withcolor red ; % draw fullcircle scaled 5cm withcolor blue ; % ) ; % currentpicture := currentpicture shifted (-bbwidth(currentpicture)-1cm,0) ; % drawdblarrow fullsquare scaled 2cm withcolor green ; % drawdblarrow fullcircle scaled 3cm withcolor green ; % drawdblarrow image ( % draw fullsquare scaled 4cm withcolor red ; % draw fullcircle scaled 5cm withcolor blue ; % ) ; vardef stroked_paths(expr p) = save n ; numeric n ; n := 0 ; for i within p : if stroked i : n := n + 1 ; fi endfor ; n enddef ; def mfun_decoration_i expr i = withpen penpart i withcolor colorpart i withprescript prescriptpart i withpostscript postscriptpart i enddef ; % We could collapse all in one helper but in context we nowaways don't want % the added obscurity. Tokens come cheap. numeric mfun_arrow_snippets ; numeric mfun_arrow_count ; def drawarrow expr p = begingroup ; save mfun_arrow_path ; path mfun_arrow_path ; if path p : mfun_arrow_path := p ; expandafter mfun_draw_arrow_path elseif picture p : save mfun_arrow_picture ; picture mfun_arrow_picture ; mfun_arrow_picture := p ; expandafter mfun_draw_arrow_picture else : expandafter mfun_draw_arrow_nothing fi enddef ; def drawdblarrow expr p = begingroup ; save mfun_arrow_path ; path mfun_arrow_path ; if path p : mfun_arrow_path := p ; expandafter mfun_draw_arrow_path_double elseif picture p : save mfun_arrow_picture ; picture mfun_arrow_picture ; mfun_arrow_picture := p ; expandafter mfun_draw_arrow_picture_double else : expandafter mfun_draw_arrow_nothing fi enddef ; def mfun_draw_arrow_nothing text t = enddef ; % The path is shortened so that the arrow head extends it to the original % length. In case of a double arrow the path gets shortened twice. def mfun_draw_arrow_path text t = if autoarrows : set_ahlength(t) ; fi draw arrowpath mfun_arrow_path t ; fillup arrowhead mfun_arrow_path t ; endgroup ; enddef ; def mfun_draw_arrow_path_double text t = if autoarrows : set_ahlength(t) ; fi draw arrowpath (reverse arrowpath mfun_arrow_path) t ; fillup arrowhead mfun_arrow_path t ; fillup arrowhead reverse mfun_arrow_path t ; endgroup ; enddef ; % The picture variant is not treating each path but only the first and % last path. This can be somewhat counterintuitive but is needed for Alan's % macros. So here the last and in case of a double path first paths in a % picture get the shortening. def mfun_with_arrow_picture (text t) = mfun_arrow_count := 0 ; mfun_arrow_snippets := stroked_paths(mfun_arrow_picture) ; for i within mfun_arrow_picture : if istextext(i) : draw i else : mfun_arrow_count := mfun_arrow_count + 1 ; mfun_arrow_path := pathpart i ; t fi ; endfor ; enddef ; def mfun_draw_arrow_picture text t = if autoarrows : set_ahlength(t) ; fi mfun_with_arrow_picture ( if mfun_arrow_count = mfun_arrow_snippets : draw arrowpath mfun_arrow_path mfun_decoration_i i t ; fillup arrowhead mfun_arrow_path mfun_decoration_i i t ; else : draw mfun_arrow_path mfun_decoration_i i t ; fi ; ) endgroup ; enddef ; def mfun_draw_arrow_picture_double text t = if autoarrows : set_ahlength(t) ; fi mfun_with_arrow_picture ( draw if mfun_arrow_count = 1 : arrowpath reverse elseif mfun_arrow_count = mfun_arrow_snippets : arrowpath fi mfun_arrow_path mfun_decoration_i i t ; if mfun_arrow_count = 1 : fillup arrowhead reverse mfun_arrow_path mfun_decoration_i i t ; fi if mfun_arrow_count = mfun_arrow_snippets : fillup arrowhead mfun_arrow_path mfun_decoration_i i t ; fi ) endgroup ; enddef ; %D Some more arrow magic, by Alan: def drawdoublearrows expr p = begingroup ; save mfun_arrow_path ; path mfun_arrow_path ; save mfun_arrow_path_parallel ; path mfun_arrow_path_parallel ; if path p : mfun_arrow_path := p ; expandafter mfun_draw_arrow_paths elseif picture p : save mfun_arrow_picture ; picture mfun_arrow_picture ; mfun_arrow_picture := p ; expandafter mfun_draw_arrow_pictures else : expandafter mfun_draw_arrow_nothing fi enddef ; def mfun_draw_arrow_paths text t = if autoarrows : set_ahlength(t) ; fi save d ; d := ahscale*ahlength*sind(ahangle/2) ; mfun_arrow_path_parallel := mfun_arrow_path paralleled d ; draw arrowpath mfun_arrow_path_parallel t ; fillup arrowhead mfun_arrow_path_parallel t ; mfun_arrow_path_parallel := (reverse mfun_arrow_path) paralleled d ; draw arrowpath mfun_arrow_path_parallel t ; fillup arrowhead mfun_arrow_path_parallel t ; endgroup ; enddef ; def mfun_draw_arrow_pictures text t = if autoarrows : set_ahlength(t) ; fi save d ; d := ahscale*ahlength*sind(ahangle/2) ; mfun_with_arrow_picture( if mfun_arrow_count = 1 : draw (mfun_arrow_path paralleled d) mfun_decoration_i i t ; mfun_arrow_path_parallel := (reverse mfun_arrow_path) paralleled d ; draw arrowpath mfun_arrow_path_parallel mfun_decoration_i i t ; fillup arrowhead mfun_arrow_path_parallel mfun_decoration_i i t ; elseif mfun_arrow_count = mfun_arrow_snippets : draw ((reverse mfun_arrow_path) paralleled d) mfun_decoration_i i t ; mfun_arrow_path_parallel := mfun_arrow_path paralleled d ; draw arrowpath mfun_arrow_path_parallel mfun_decoration_i i t ; fillup arrowhead mfun_arrow_path_parallel mfun_decoration_i i t ; else : draw ( mfun_arrow_path paralleled d) mfun_decoration_i i t ; draw ((reverse mfun_arrow_path) paralleled d) mfun_decoration_i i t ; fi ) endgroup ; enddef ; %D Handy too ...... vardef pointarrow (expr pat, loc, len, off) = save l, r, s, t ; path l, r ; numeric s ; pair t ; t := if pair loc : loc else : point loc along pat fi ; s := len/2 - off ; if s<=0 : s := 0 elseif s>len : s := len fi ; r := pat cutbefore t ; r := (r cutafter point (arctime s of r) of r) ; s := len/2 + off ; if s<=0 : s := 0 elseif s>len : s := len fi ; l := reverse (pat cutafter t) ; l := (reverse (l cutafter point (arctime s of l) of l)) ; (l..r) enddef ; def rightarrow (expr pat,tim,len) = pointarrow(pat,tim,len,-len) enddef ; def leftarrow (expr pat,tim,len) = pointarrow(pat,tim,len,+len) enddef ; def centerarrow (expr pat,tim,len) = pointarrow(pat,tim,len, 0) enddef ; %D The \type {along} and \type {on} operators can be used as follows: %D %D \starttyping %D drawdot point .5 along somepath ; %D drawdot point 3cm on somepath ; %D \stoptyping %D %D The number denotes a percentage (fraction). primarydef pct along pat = % also negative (arctime (pct * (arclength pat)) of pat) of pat enddef ; primarydef len on pat = % no outer ( ) .. somehow fails (arctime if len>=0 : len else : (arclength(pat)+len) fi of pat) of pat enddef ; % this cuts of a piece from both ends tertiarydef pat cutends len = begingroup save tap ; path tap ; tap := pat cutbefore (point (xpart paired(len)) on pat) ; (tap cutafter (point -(ypart paired(len)) on tap)) endgroup enddef ; %D To be documented. path freesquare ; freesquare := ( (-1,0) -- (-1,-1) -- (0,-1) -- (+1,-1) -- (+1,0) -- (+1,+1) -- (0,+1) -- (-1,+1) -- cycle ) scaled .5 ; numeric freelabeloffset ; freelabeloffset := 3pt ; numeric freedotlabelsize ; freedotlabelsize := 3pt ; vardef thefreelabel (expr str, loc, ori) = save s, p, q, l ; picture s ; path p, q ; pair l ; interim labeloffset := freelabeloffset ; s := if string str : thelabel(str,loc) else : str shifted -center str shifted loc fi ; setbounds s to boundingbox s enlarged freelabeloffset ; p := fullcircle scaled (2*length(loc-ori)) shifted ori ; q := freesquare xyscaled (urcorner s - llcorner s) ; l := point xpart (p intersectiontimes (ori--loc shifted (loc-ori))) of q ; setbounds s to boundingbox s enlarged -freelabeloffset ; % new % draw boundingbox s shifted -l withpen pencircle scaled .5pt withcolor red ; (s shifted -l) enddef ; vardef freelabel (expr str, loc, ori) = draw thefreelabel(str,loc,ori) ; enddef ; vardef freedotlabel (expr str, loc, ori) = interim linecap := rounded ; draw loc withpen pencircle scaled freedotlabelsize ; draw thefreelabel(str,loc,ori) ; enddef ; %D \starttyping %D drawarrow anglebetween(line_a,line_b,somelabel) ; %D \stoptyping newinternal angleoffset ; angleoffset := 0pt ; newinternal anglelength ; anglelength := 20pt ; newinternal anglemethod ; anglemethod := 1 ; vardef anglebetween (expr a, b, str) = % path path string save pointa, pointb, common, middle, offset ; pair pointa, pointb, common, middle, offset ; save curve ; path curve ; save where ; numeric where ; if round point 0 of a = round point 0 of b : common := point 0 of a ; else : common := a intersectionpoint b ; fi ; pointa := point anglelength on a ; pointb := point anglelength on b ; where := turningnumber (common--pointa--pointb--cycle) ; middle := (reverse(common--pointa) rotatedaround (pointa,-where*90)) intersection_point (reverse(common--pointb) rotatedaround (pointb, where*90)) ; if not intersection_found : middle := point .5 along ((reverse(common--pointa) rotatedaround (pointa,-where*90)) -- ( (common--pointb) rotatedaround (pointb, where*90))) ; fi ; if anglemethod = 0 : curve := pointa{unitvector(middle-pointa)}.. pointb; middle := point .5 along curve ; curve := common ; elseif anglemethod = 1 : curve := pointa{unitvector(middle-pointa)}.. pointb; middle := point .5 along curve ; elseif anglemethod = 2 : middle := common rotatedaround(.5[pointa,pointb],180) ; curve := pointa--middle--pointb ; elseif anglemethod = 3 : curve := pointa--middle--pointb ; elseif anglemethod = 4 : curve := pointa..controls middle..pointb ; middle := point .5 along curve ; fi ; draw thefreelabel(str, middle, common) ; % withcolor black ; curve enddef ; % Stack picture mfun_current_picture_stack[] ; numeric mfun_current_picture_depth ; mfun_current_picture_depth := 0 ; def pushcurrentpicture = mfun_current_picture_depth := mfun_current_picture_depth + 1 ; mfun_current_picture_stack[mfun_current_picture_depth] := currentpicture ; currentpicture := nullpicture ; enddef ; def popcurrentpicture text t = % optional text if mfun_current_picture_depth > 0 : addto mfun_current_picture_stack[mfun_current_picture_depth] also currentpicture t ; currentpicture := mfun_current_picture_stack[mfun_current_picture_depth] ; mfun_current_picture_stack[mfun_current_picture_depth] := nullpicture ; mfun_current_picture_depth := mfun_current_picture_depth - 1 ; fi ; enddef ; %D colorcircle(size, red, green, blue) ; vardef colorcircle (expr size, red, green, blue) = % might move save r, g, b, c, m, y, w ; save radius ; path r, g, b, c, m, y, w ; numeric radius ; radius := 5cm ; pickup pencircle scaled (radius/25) ; transform t ; t := identity rotatedaround(origin,120) ; r := fullcircle rotated 90 scaled radius shifted (0,radius/4) rotatedaround(origin,135) ; b := r transformed t ; g := b transformed t ; c := buildcycle(subpath(1,7) of g,subpath(1,7) of b) ; y := c transformed t ; m := y transformed t ; w := buildcycle(subpath(3,5) of r, subpath(3,5) of g,subpath(3,5) of b) ; pushcurrentpicture ; fill r withcolor red ; fill g withcolor green ; fill b withcolor blue ; fill c withcolor white - red ; fill m withcolor white - green ; fill y withcolor white - blue ; fill w withcolor white ; for i = r,g,b,c,m,y : draw i withcolor .5white ; endfor ; currentpicture := currentpicture xsized size ; popcurrentpicture ; enddef ; % penpoint (i,2) of somepath -> inner / outer point vardef penpoint expr pnt of p = save n, d ; numeric n, d ; (n,d) = if pair pnt : pnt else : (pnt,1) fi ; (point n of p shifted ((penoffset direction n of p of currentpen) scaled d)) enddef ; % nice: currentpicture := inverted currentpicture ; primarydef p uncolored c = % not complete ... needs text and scripts and ... if color p : c - p else : image ( for i within p : addto currentpicture if stroked i or filled i : if filled i : contour else : doublepath fi pathpart i dashed dashpart i withpen penpart i else : also i fi withcolor c-(redpart i, greenpart i, bluepart i) ; endfor ; ) fi enddef ; vardef inverted primary p = p uncolored white enddef ; primarydef p softened c = begingroup save cc ; color cc ; cc := tripled(c) ; if color p : (redpart cc * redpart p,greenpart cc * greenpart p, bluepart cc * bluepart p) else : image ( for i within p : addto currentpicture if stroked i or filled i : if filled i : contour else : doublepath fi pathpart i dashed dashpart i withpen penpart i else : also i fi withcolor (redpart cc * redpart i, greenpart cc * greenpart i, bluepart cc * bluepart i) ; endfor ; ) fi endgroup enddef ; vardef grayed primary p = if rgbcolor p : tripled(.30redpart p+.59greenpart p+.11bluepart p) elseif cmykcolor p : tripled(.30*(1-cyanpart i)+.59*(1-magentapart i)+.11*(1-yellowpart i)+blackpart i) elseif greycolor p : p elseif string p : grayed resolvedcolor(p) elseif picture p : image ( for i within p : addto currentpicture if stroked i or filled i : if filled i : contour else : doublepath fi pathpart i dashed dashpart i withpen penpart i else : also i fi if unknown colorpart i : % nothing elseif rgbcolor colorpart i : withcolor tripled(.30redpart i+.59greenpart i+.11bluepart i) ; elseif cmykcolor colorpart i : withcolor tripled(.30*(1-cyanpart i)+.59*(1-magentapart i)+.11*(1-yellowpart i)+blackpart i) ; else : withcolor colorpart i ; fi endfor ; ) else : p fi enddef ; let greyed = grayed ; vardef hsvtorgb(expr h,s,v) = save H, S, V, x ; H = h mod 360 ; S = if s < 0 : 0 elseif s > 1 : 1 else: s fi ; V = if v < 0 : 0 elseif v > 1 : 1 else: v fi ; x = 1 - abs(H mod 120 - 60)/60 ; V * ( (1-S) * (1,1,1) + S * if H < 60 : (1,x,0) elseif H < 120 : (x,1,0) elseif H < 180 : (0,1,x) elseif H < 240 : (0,x,1) elseif H < 300 : (x,0,1) else : (1,0,x) fi ) enddef ; % yes or no: "text" infont "cmr12" at 24pt ; % let normalinfont = infont ; % % numeric lastfontsize ; lastfontsize = fontsize defaultfont ; % % def infont primary name = % no vardef, no expr % hide(lastfontsize := fontsize name) % no ; % normalinfont name % enddef ; % % def scaledat expr size = % scaled (size/lastfontsize) % enddef ; % % let at = scaledat ; % like decimal def condition primary b = if b : "true" else : "false" fi enddef ; % undocumented primarydef p stretched s = begingroup save pp ; path pp ; pp := p xyscaled s ; (pp shifted ((point 0 of p) - (point 0 of pp))) endgroup enddef ; primarydef p enlonged len = begingroup if pair p : save q ; path q ; q := origin -- p ; save al ; al := arclength(q) ; if al > 0 : point 1 of (q stretched ((al+len)/al)) else : p fi else : save al ; al := arclength(p) ; if al > 0 : p stretched ((al+len)/al) else : p fi fi endgroup enddef ; % path p ; p := (0,0) -- (10cm,5cm) ; % drawarrow p withcolor red ; % drawarrow p shortened 1cm withcolor green ; primarydef p shortened d = reverse ( ( reverse (p enlonged -d) ) enlonged -d ) enddef ; % yes or no, untested -) def xshifted expr dx = shifted(dx,0) enddef ; def yshifted expr dy = shifted(0,dy) enddef ; % also handy % right: str = readfrom ("abc" & ".def" ) ; % wrong: str = readfrom "abc" & ".def" ; % Every 62th read fails so we need to try again! % def readfile (expr name) = % if (readfrom (name) <> EOF) : % scantokens("input " & name & ";") ; % elseif (readfrom (name) <> EOF) : % scantokens("input " & name & ";") ; % fi ; % closefrom (name) ; % enddef ; % % this sometimes fails on the elseif, so : % def readfile (expr name) = begingroup ; save ok ; boolean ok ; if (readfrom (name) <> EOF) : ok := false ; elseif (readfrom (name) <> EOF) : ok := false ; else : ok := true ; fi ; if not ok : scantokens("input " & name & " ") ; fi ; closefrom (name) ; endgroup ; enddef ; % permits redefinition of end in macro inner end ; % this will be redone (when needed) using scripts and backend handling let mfun_remap_colors_normalwithcolor = normalwithcolor ; def remapcolors = def normalwithcolor primary c = mfun_remap_colors_normalwithcolor remappedcolor(c) enddef ; enddef ; def normalcolors = let normalwithcolor = mfun_remap_colors_normalwithcolor ; enddef ; def resetcolormap = color color_map[][][] ; normalcolors ; enddef ; resetcolormap ; def r_color primary c = redpart c enddef ; def g_color primary c = greenpart c enddef ; def b_color primary c = bluepart c enddef ; def remapcolor(expr old, new) = color_map[redpart old][greenpart old][bluepart old] := new ; enddef ; def remappedcolor(expr c) = if known color_map[redpart c][greenpart c][bluepart c] : color_map[redpart c][greenpart c][bluepart c] else : c fi enddef ; % Thanks to Jens-Uwe Morawski for pointing out that we need % to treat bounded and clipped components as local pictures. def recolor suffix p = p := repathed (0,p) enddef ; def refill suffix p = p := repathed (1,p) enddef ; def redraw suffix p = p := repathed (2,p) enddef ; def retext suffix p = p := repathed (3,p) enddef ; def untext suffix p = p := repathed (4,p) enddef ; % primarydef p recolored t = repathed(0,p) t enddef ; % primarydef p refilled t = repathed(1,p) t enddef ; % primarydef p redrawn t = repathed(2,p) t enddef ; % primarydef p retexted t = repathed(3,p) t enddef ; % primarydef p untexted t = repathed(4,p) t enddef ; color refillbackground ; refillbackground := (1,1,1) ; def restroke suffix p = p := repathed (21,p) enddef ; % keep attributes def reprocess suffix p = p := repathed (22,p) enddef ; % no attributes % also 11 and 12 vardef repathed (expr mode, p) text t = begingroup ; if mode = 0 : save normalwithcolor ; remapcolors ; fi ; save _p_, _pp_, _ppp_, _f_, _b_, _t_ ; picture _p_, _pp_, _ppp_ ; color _f_ ; path _b_ ; transform _t_ ; _b_ := boundingbox p ; _p_ := nullpicture ; for i within p : _f_ := (redpart i, greenpart i, bluepart i) ; if bounded i : _pp_ := repathed(mode,i) t ; setbounds _pp_ to pathpart i ; addto _p_ also _pp_ ; elseif clipped i : _pp_ := repathed(mode,i) t ; clip _pp_ to pathpart i ; addto _p_ also _pp_ ; elseif stroked i : if mode=21 : _ppp_ := i ; % indirectness is needed addto _p_ also image(scantokens(t & " pathpart _ppp_") dashed dashpart i withpen penpart i withcolor _f_ ; ) ; elseif mode=22 : _ppp_ := i ; % indirectness is needed addto _p_ also image(scantokens(t & " pathpart _ppp_")) ; else : addto _p_ doublepath pathpart i dashed dashpart i withpen penpart i withcolor _f_ % (redpart i, greenpart i, bluepart i) if mode = 2 : t fi ; fi ; elseif filled i : if mode=11 : _ppp_ := i ; % indirectness is needed addto _p_ also image(scantokens(t & " pathpart _ppp_") withcolor _f_ ; ) ; elseif mode=12 : _ppp_ := i ; % indirectness is needed addto _p_ also image(scantokens(t & " pathpart _ppp_")) ; else : addto _p_ contour pathpart i withcolor _f_ if (mode=1) and (_f_<>refillbackground) : t fi ; fi ; elseif textual i : % textpart i <> "" : if mode <> 4 : % transform _t_ ; % (xpart _t_, xxpart _t_, xypart _t_) = (xpart i, xxpart i, xypart i) ; % (ypart _t_, yypart _t_, yxpart _t_) = (ypart i, yypart i, yxpart i) ; % addto _p_ also % textpart i infont fontpart i % todo : other font % transformed _t_ % withpen penpart i % withcolor _f_ % if mode=3 : t fi ; addto _p_ also i if mode=3 : t fi ; fi ; else : addto _p_ also i ; fi ; endfor ; setbounds _p_ to _b_ ; _p_ endgroup enddef ; % After a question of Denis on how to erase a z variable, Jacko % suggested to assign whatever to x and y. So a clearz % variable can be defined as: % % vardef clearz@# = % x@# := whatever ; % y@# := whatever ; % enddef ; % % but Jacko suggested a redefinition of clearxy: % % def clearxy text s = % clearxy_index_:=0; % for $:=s: % clearxy_index_:=clearxy_index_+1; endfor; % if clearxy_index_=0: % save x,y; % else: % forsuffixes $:=s: x$:=whatever; y$:=whatever; endfor; % fi % enddef; % % which i decided to simplify to: def clearxy text s = if false for $ := s : or true endfor : forsuffixes $ := s : x$ := whatever ; y$ := whatever ; endfor ; else : save x, y ; fi enddef ; % so now we can say: clearxy ; as well as clearxy 1, 2, 3 ; % show x0 ; z0 = (10,10) ; % show x0 ; x0 := whatever ; y0 := whatever ; % show x0 ; z0 = (20,20) ; % show x0 ; clearxy 0 ; % show x0 ; z0 = (30,30) ; primarydef p smoothed d = (p llmoved (-xpart paired(d),0) -- p lrmoved (-xpart paired(d),0) {right} .. p lrmoved (0,-ypart paired(d)) -- p urmoved (0,-ypart paired(d)) {up} .. p urmoved (-xpart paired(d),0) -- p ulmoved (-xpart paired(d),0) {left} .. p ulmoved (0,-ypart paired(d)) -- p llmoved (0,-ypart paired(d)) {down} .. cycle) enddef ; primarydef p cornered c = ((point 0 of p) shifted (c*(unitvector(point 1 of p - point 0 of p))) -- for i=1 upto length(p) : (point i-1 of p) shifted (c*(unitvector(point i of p - point i-1 of p))) -- (point i of p) shifted (c*(unitvector(point i-1 of p - point i of p))) .. controls point i of p .. endfor cycle) enddef ; % cmyk color support % vardef cmyk(expr c,m,y,k) = % elsewhere % (1-c-k,1-m-k,1-y-k) % enddef ; % handy % vardef bbwidth (expr p) = % vardef width_of primary p = % if known p : % if path p or picture p : % xpart (lrcorner p - llcorner p) % else : % 0 % fi % else : % 0 % fi % enddef ; vardef bbwidth primary p = if unknown p : 0 elseif path p or picture p : xpart (lrcorner p - llcorner p) else : 0 fi enddef ; % vardef bbheight (expr p) = % vardef heigth_of primary p = % if known p : % if path p or picture p : % ypart (urcorner p - lrcorner p) % else : % 0 % fi % else : % 0 % fi % enddef ; vardef bbheight primary p = if unknown p : 0 elseif path p or picture p : ypart (urcorner p - lrcorner p) else : 0 fi enddef ; color nocolor ; numeric noline ; % both unknown signals def dowithpath (expr p, lw, lc, bc) = if known p : if known bc : fill p withcolor bc ; fi ; if known lw and known lc : draw p withpen pencircle scaled lw withcolor lc ; elseif known lw : draw p withpen pencircle scaled lw ; elseif known lc : draw p withcolor lc ; fi ; fi ; enddef ; % result from metafont discussion list (denisr/boguslawj) def [[ = [ [ enddef ; def [[[ = [ [ [ enddef ; def ]] = ] ] enddef ; def ]]] = ] ] ] enddef ; let == = = ; % added picture oddly ; % evenly already defined evenly := dashpattern(on 3 off 3) ; oddly := dashpattern(off 3 on 3) ; % not perfect, but useful since it removes redundant points. vardef mfun_straightened(expr sign, p) = save _p_, _q_ ; path _p_, _q_ ; _p_ := p ; forever : _q_ := mfun_do_straightened(sign, _p_) ; exitif length(_p_) = length(_q_) ; _p_ := _q_ ; endfor ; _q_ enddef ; % vardef mfun_straightened(expr sign, p) = % save lp, lq, q ; path q ; q := p ; % lp := length(p) ; % forever : % q := mfun_do_straightened(sign,q) ; % lq := length(q) ; % exitif lp = lq ; % lp := lq ; % endfor ; % q % enddef ; % can be optimized: vardef mfun_do_straightened(expr sign, p) = if length(p) > 2 : % was 1, but straight lines are ok save pp ; path pp ; pp := point 0 of p ; for i=1 upto length(p)-1 : if round(point i of p) <> round(point length(pp) of pp) : pp := pp -- point i of p ; fi ; endfor ; save n, ok ; numeric n ; boolean ok ; n := length(pp) ; ok := false ; if n > 2 : for i=0 upto n : if unitvector(round(point i of pp - point if i=0 : n else : i-1 fi of pp)) <> sign * unitvector(round(point if i=n : 0 else : i+1 fi of pp - point i of pp)) : if ok : -- else : ok := true ; fi point i of pp fi endfor if ok and (cycle p) : -- cycle fi else : pp fi else : p fi enddef ; vardef simplified expr p = ( reverse mfun_straightened(+1,mfun_straightened(+1,reverse p)) ) enddef ; vardef unspiked expr p = ( reverse mfun_straightened(-1,mfun_straightened(-1,reverse p)) ) enddef ; % path p ; % p := (2cm,1cm) -- (2cm,1cm) -- (2cm,1cm) -- (3cm,1cm) -- % (4cm,1cm) -- (4cm,2cm) -- (4cm,2.5cm) -- (4cm,3cm) -- % (3cm,3cm) -- (2cm,3cm) -- (1cm,3cm) -- (-1cm,3cm) -- % .5[(-1cm,3cm),(1cm,1cm)] -- (1cm,1cm) -- cycle ; % % p := unitcircle scaled 4cm ; % % drawpath p ; drawpoints p ; drawpointlabels p ; % p := p shifted (4cm,0) ; p := straightened p ; % drawpath p ; drawpoints p ; drawpointlabels p ; % p := p shifted (4cm,0) ; p := straightened p ; % drawpath p ; drawpoints p ; drawpointlabels p ; % new path originpath ; originpath := origin -- cycle ; vardef unitvector primary z = if abs z = abs origin : z else : z/abs z fi % hm, abs origin is just origin enddef; % also new % vardef anchored@#(expr p, z) = % maybe use the textext variant % p shifted (z + (labxf@#*lrcorner p + labyf@#*ulcorner p + (1-labxf@#-labyf@#)*llcorner p)) % enddef ; % epsed(1.2345) vardef epsed (expr e) = e if e>0 : + eps elseif e<0 : - eps fi enddef ; % handy def withgray primary g = withcolor g enddef ; % for metafun if unknown darkred : color darkred ; darkred := .625(1,0,0) fi ; if unknown darkgreen : color darkgreen ; darkgreen := .625(0,1,0) fi ; if unknown darkblue : color darkblue ; darkblue := .625(0,0,1) fi ; if unknown darkcyan : color darkcyan ; darkcyan := .625(0,1,1) fi ; if unknown darkmagenta : color darkmagenta ; darkmagenta := .625(1,0,1) fi ; if unknown darkyellow : color darkyellow ; darkyellow := .625(1,1,0) fi ; if unknown darkgray : color darkgray ; darkgray := .625(1,1,1) fi ; if unknown lightgray : color lightgray ; lightgray := .850(1,1,1) fi ; % an improved plain mp macro vardef center primary p = if pair p : p else : .5[llcorner p, urcorner p] fi enddef; % new, yet undocumented vardef rangepath (expr p, d, a) = if length p>0 : (d*unitvector(direction 0 of p) rotated a) shifted point 0 of p -- p -- (d*unitvector(direction length(p) of p) rotated a) shifted point length(p) of p else : p fi enddef ; % under construction vardef straightpath (expr a, b, method) = if (method<1) or (method>6) : (a--b) elseif method = 1 : (a -- if xpart a > xpart b : if ypart a > ypart b : (xpart b,ypart a) -- elseif ypart a < ypart b : (xpart a,ypart b) -- fi elseif xpart a < xpart b : if ypart a > ypart b : (xpart a,ypart b) -- elseif ypart a < ypart b : (xpart b,ypart a) -- fi fi b) elseif method = 3 : (a -- if xpart a > xpart b : (xpart b,ypart a) -- elseif xpart a < xpart b : (xpart a,ypart b) -- fi b) elseif method = 5 : (a -- if ypart a > ypart b : (xpart b,ypart a) -- elseif ypart a < ypart b : (xpart a,ypart b) -- fi b) else : (reverse straightpath(b,a,method-1)) fi enddef ; % handy for myself def addbackground text t = begingroup ; save p, b ; picture p ; path b ; b := boundingbox currentpicture ; p := currentpicture ; currentpicture := nullpicture ; fill b t ; setbounds currentpicture to b ; addto currentpicture also p ; endgroup ; enddef ; % makes a (line) into an infinite one (handy for calculating % intersection points vardef infinite expr p = (-infinity*unitvector(direction 0 of p) shifted point 0 of p -- p -- +infinity*unitvector(direction length(p) of p) shifted point length(p) of p) enddef ; % obscure macros: create var from string and replace - and : % (needed for process color id's) .. will go away string mfun_clean_ascii[] ; def register_dirty_chars(expr str) = for i = 0 upto length(str)-1 : mfun_clean_ascii[ASCII substring(i,i+1) of str] := "_" ; endfor ; enddef ; register_dirty_chars("+-*/:;., ") ; vardef cleanstring (expr s) = save ss ; string ss, si ; ss = "" ; save i ; for i=0 upto length(s) : si := substring(i,i+1) of s ; ss := ss & if known mfun_clean_ascii[ASCII si] : mfun_clean_ascii[ASCII si] else : si fi ; endfor ; ss enddef ; vardef asciistring (expr s) = save ss ; string ss, si ; ss = "" ; save i ; for i=0 upto length(s) : si := substring(i,i+1) of s ; if (ASCII si >= ASCII "0") and (ASCII si <= ASCII "9") : ss := ss & char(scantokens(si) + ASCII "A") ; else : ss := ss & si ; fi ; endfor ; ss enddef ; vardef setunstringed (expr s, v) = scantokens(cleanstring(s)) := v ; enddef ; vardef getunstringed (expr s) = scantokens(cleanstring(s)) enddef ; vardef unstringed (expr s) = expandafter known scantokens(cleanstring(s)) enddef ; % for david arnold: % showgrid(-5,10,1cm,-10,10,1cm); def showgrid (expr MinX, MaxX, DeltaX, MinY, MaxY, DeltaY) = % will move begingroup save size ; numeric size ; size := 2pt ; for x=MinX upto MaxX : for y=MinY upto MaxY : draw (x*DeltaX, y*DeltaY) withpen pencircle scaled if (x mod 5 = 0) and (y mod 5 = 0) : 1.5size withcolor .50white else : size withcolor .75white fi ; endfor ; endfor ; for x=MinX upto MaxX: label.bot(textext("\infofont " & decimal x), (x*DeltaX,-size)) ; endfor ; for y=MinY upto MaxY: label.lft(textext("\infofont " & decimal y), (-size,y*DeltaY)) ; endfor ; endgroup enddef; % new, handy for: % % \startuseMPgraphic{map}{n} % \includeMPgraphic{map:germany} ; % c_phantom (\MPvar{n}<1) ( % fill map_germany withcolor \MPcolor{lightgray} ; % draw map_germany withpen pencircle scaled 1pt withcolor \MPcolor{darkgray} ; % ) ; % \includeMPgraphic{map:austria} ; % c_phantom (\MPvar{n}<2) ( % fill map_austria withcolor \MPcolor{lightgray} ; % draw map_austria withpen pencircle scaled 1pt withcolor \MPcolor{darkgray} ; % ) ; % c_phantom (\MPvar{n}<3) ( % \includeMPgraphic{map:swiss} ; % fill map_swiss withcolor \MPcolor{lightgray} ; % draw map_swiss withpen pencircle scaled 1pt withcolor \MPcolor{darkgray} ; % ) ; % c_phantom (\MPvar{n}<4) ( % \includeMPgraphic{map:luxembourg} ; % fill map_luxembourg withcolor \MPcolor{lightgray} ; % draw map_luxembourg withpen pencircle scaled 1pt withcolor \MPcolor{darkgray} ; % ) ; % \stopuseMPgraphic % % \useMPgraphic{map}{n=3} vardef phantom (text t) = % to be checked picture _p_ ; _p_ := image(t) ; addto _p_ also currentpicture ; setbounds currentpicture to boundingbox _p_ ; enddef ; vardef c_phantom (expr b) (text t) = if b : picture _p_ ; _p_ := image(t) ; addto _p_ also currentpicture ; setbounds currentpicture to boundingbox _p_ ; else : t ; fi ; enddef ; %D Handy: def break = exitif true ; % fi enddef ; %D New too: primarydef p xstretched w = ( p if (bbwidth (p)>0) and (w>0) : xscaled (w/bbwidth (p)) fi ) enddef ; primarydef p ystretched h = ( p if (bbheight(p)>0) and (h>0) : yscaled (h/bbheight(p)) fi ) enddef ; %D Newer: vardef area expr p = % we could calculate the boundingbox once (xpart llcorner boundingbox p,0) -- p -- (xpart lrcorner boundingbox p,0) -- cycle enddef ; vardef basiccolors[] = if @ = 0 : white else : save n ; n := @ mod 7 ; if n = 1 : red elseif n = 2 : green elseif n = 3 : blue elseif n = 4 : cyan elseif n = 5 : magenta elseif n = 6 : yellow else : black fi fi enddef ; % vardef somecolor = (1,1,0,0) enddef ; % fill OverlayBox withcolor (rcomponent somecolor,gcomponent somecolor,bcomponent somecolor) ; % fill OverlayBox withcolor (ccomponent somecolor,mcomponent somecolor,ycomponent somecolor,bcomponent somecolor) ; % This could be standard mplib 2 behaviour: % vardef rcomponent expr p = if rgbcolor p : redpart elseif cmykcolor p : 1 - cyanpart fi p enddef ; vardef rcomponent expr p = if rgbcolor p : redpart p elseif cmykcolor p : 1 - cyanpart p else : p fi enddef ; vardef gcomponent expr p = if rgbcolor p : greenpart p elseif cmykcolor p : 1 - magentapart p else : p fi enddef ; vardef bcomponent expr p = if rgbcolor p : bluepart p elseif cmykcolor p : 1 - yellowpart p else : p fi enddef ; vardef ccomponent expr p = if cmykcolor p : cyanpart p elseif rgbcolor p : 1 - redpart p else : p fi enddef ; vardef mcomponent expr p = if cmykcolor p : magentapart p elseif rgbcolor p : 1 - greenpart p else : p fi enddef ; vardef ycomponent expr p = if cmykcolor p : yellowpart p elseif rgbcolor p : 1 - bluepart p else : p fi enddef ; vardef kcomponent expr p = if cmykcolor p : blackpart p elseif rgbcolor p : 0 else : p fi enddef ; % draw image (...) ... ; % prescripts prepended to first, postscripts appended to last % draw decorated (...) ... ; % prescripts prepended to each, postscripts appended to each % draw redecorated (...) ... ; % prescripts assigned to each, postscripts assigned to each % draw undecorated (...) ... ; % following properties are ignored, existing properties are kept % % draw decorated ( % draw fullcircle scaled 20cm withpen pencircle scaled 20mm withcolor red withtransparency (1,.40) ; % draw fullcircle scaled 15cm withpen pencircle scaled 15mm withcolor green withtransparency (1,.30) ; % draw fullcircle scaled 10cm withpen pencircle scaled 10mm withcolor blue withtransparency (1,.20) ; % ) % withcolor blue % withtransparency (1,.125) % selectively applied % withpen pencircle scaled 10mm % ; % vardef image (text imagedata) = % already defined % save currentpicture ; % picture currentpicture ; % currentpicture := nullpicture ; % imagedata ; % currentpicture % enddef ; vardef undecorated (text imagedata) text decoration = save currentpicture ; picture currentpicture ; currentpicture := nullpicture ; imagedata ; currentpicture enddef ; % if metapostversion < 1.770 : % % vardef decorated (text imagedata) text decoration = % save mfun_decorated_path, currentpicture ; % picture mfun_decorated_path, currentpicture ; % currentpicture := nullpicture ; % imagedata ; % mfun_decorated_path := currentpicture ; % currentpicture := nullpicture ; % for i within mfun_decorated_path : % addto currentpicture % if stroked i : % doublepath pathpart i % dashed dashpart i % withpen penpart i % withcolor colorpart i % decoration % elseif filled i : % contour pathpart i % withpen penpart i % withcolor colorpart i % decoration % elseif textual i : % also i % withcolor colorpart i % decoration % else : % also i % fi % ; % endfor ; % currentpicture % enddef ; % % else: vardef decorated (text imagedata) text decoration = save mfun_decorated_path, currentpicture ; picture mfun_decorated_path, currentpicture ; currentpicture := nullpicture ; imagedata ; mfun_decorated_path := currentpicture ; currentpicture := nullpicture ; for i within mfun_decorated_path : addto currentpicture if stroked i : doublepath pathpart i dashed dashpart i withpen penpart i withcolor colorpart i withprescript prescriptpart i withpostscript postscriptpart i decoration elseif filled i : contour pathpart i withpen penpart i withcolor colorpart i withprescript prescriptpart i withpostscript postscriptpart i decoration elseif textual i : also i withcolor colorpart i withprescript prescriptpart i withpostscript postscriptpart i decoration else : also i fi ; endfor ; currentpicture enddef ; % fi ; vardef redecorated (text imagedata) text decoration = save mfun_decorated_path, currentpicture ; picture mfun_decorated_path, currentpicture ; currentpicture := nullpicture ; imagedata ; mfun_decorated_path := currentpicture ; currentpicture := nullpicture ; for i within mfun_decorated_path : addto currentpicture if stroked i : doublepath pathpart i dashed dashpart i withpen penpart i decoration elseif filled i : contour pathpart i withpen penpart i decoration elseif textual i : also i decoration else : also i fi ; endfor ; currentpicture enddef ; % path mfun_bleed_box ; % primarydef p bleeded d = % image ( % mfun_bleed_box := boundingbox p ; % if pair d : % draw p xysized (bbwidth(p)+2*xpart d,bbheight(p)+2*ypart d) shifted -d ; % else : % draw p xysized (bbwidth(p)+2d,bbheight(p)+2d) shifted (-d,-d) ; % fi ; % setbounds currentpicture to mfun_bleed_box ; % ) % enddef ; vardef mfun_snapped(expr p, s) = if p < 0 : - ( - else : ( fi p div s) * s % the less tokens the better enddef ; vardef mfun_applied(expr p, s)(suffix a) = if path p : if pair s : for i=0 upto length(p)-1 : (a(xpart point i of p,xpart s),a(ypart point i of p,ypart s)) -- endfor if cycle p : cycle else : (a(xpart point length(p) of p,xpart s),a(ypart point length(p) of p,ypart s)) fi else : for i=0 upto length(p)-1 : (a(xpart point i of p,s),a(ypart point i of p,s)) -- endfor if cycle p : cycle else : (a(xpart point length(p) of p,s),a(ypart point length(p) of p,s)) fi fi elseif pair p : if pair s : (a(xpart p,xpart s),a(ypart p,ypart s)) else : (a(xpart p,s),a(ypart p,s)) fi elseif cmykcolor p : (a(cyanpart p,s),a(magentapart p,s),a(yellowpart p,s),a(blackpart p,s)) elseif rgbcolor p : (a(redpart p,s),a(greenpart p,s),a(bluepart p,s)) elseif graycolor p : a(p,s) elseif numeric p : a(p,s) else p fi enddef ; primarydef p snapped s = mfun_applied(p,s)(mfun_snapped) % so we can play with variants enddef ; %D New helpers: newinternal charscale ; charscale := 1 ; % persistent so one needs to 'reset' it to 0 or 1 def beginglyph(expr unicode, width, height, depth) = beginfig(unicode) ; % the number is irrelevant charcode := unicode ; charwd := width ; charht := height ; chardp := depth ; % charscale := 1 ; % can be set for a whole font, so no reset here enddef ; def endglyph = setbounds currentpicture to (boundingbox unitsquare xscaled charwd yscaled (charht + chardp) shifted (0,-chardp)) ; if known charscale : if (charscale > 0) and (charscale <> 1) : currentpicture := currentpicture scaled charscale ; fi ; fi ; endfig ; enddef ; %D Dimensions have never been an issue as traditional MP can't make that large %D pictures, but with double mode we need a catch: newinternal maxdimensions ; maxdimensions := 14000 ; def mfun_apply_max_dimensions = % not a generic helper, we want to protect this one if bbwidth currentpicture > maxdimensions : currentpicture := currentpicture if bbheight currentpicture > bbwidth currentpicture : ysized else : xsized fi maxdimensions ; elseif bbheight currentpicture > maxdimensions : currentpicture := currentpicture ysized maxdimensions ; fi ; enddef; extra_endfig := extra_endfig & "mfun_apply_max_dimensions ;" ; %D Bonus shapes (need along): path unittriangle, fulltriangle ; % not really units but circle based unittriangle := point 0 along unitcircle -- point 1/3 along unitcircle -- point 2/3 along unitcircle -- cycle ; fulltriangle := point 0 along fullcircle -- point 1/3 along fullcircle -- point 2/3 along fullcircle -- cycle ; %D Kind of special and undocumented. On Wikipedia one can find examples %D of quick sort routines. Here we have a variant that permits a %D method. % vardef listsize(suffix list) = % numeric len ; len := 0 ; % forever : % exitif unknown list[len+1] ; % len := len + 1 ; % endfor ; % len % enddef ; vardef listsize(suffix list) = numeric len ; len := 1 ; forever : exitif unknown list[len] ; len := len + 1 ; endfor ; len if unknown list[0] : - 1 fi enddef ; vardef listlast(suffix list) = numeric len ; len := if known list[0] : 0 else : 1 fi ; forever : len := len + 1 ; exitif unknown list[len] ; endfor ; len - 1 enddef ; vardef mfun_quick_sort(suffix list)(expr _min_, _max_)(text what) = save l, r, m ; numeric l ; l := _min_ ; numeric r ; r := _max_ ; numeric m ; m := floor(.5[_min_,_max_]) ; _mid_ := what list[m] ; forever : exitif l >= r ; forever : exitif l > _max_ ; % exitif (what list[l]) >= (what list[m]) ; exitif (what list[l]) >= _mid_ ; l := l + 1 ; endfor ; forever : exitif r < _min_ ; % exitif (what list[m]) >= (what list[r]) ; exitif _mid_ >= (what list[r]) ; r := r - 1 ; endfor ; if l <= r : temp := list[l] ; list[l] := list[r] ; list[r] := temp ; l := l + 1 ; r := r - 1 ; fi ; endfor ; if _min_ < r : mfun_quick_sort(list)(_min_,r)(what) ; fi ; if l < _max_ : mfun_quick_sort(list)(l,_max_)(what) ; fi ; enddef ; vardef sortlist(suffix list)(text what) = save _max_ ; numeric _max_ ; save _mid_ ; numeric _mid_ ; save temp ; % _max_ := listsize(list) ; _max_ := listlast(list) ; if pair list[_max_] : pair temp ; else : numeric temp ; fi ; if pair what list[_max_] : pair _mid_ ; else : numeric _mid_ ; fi ; if _max_ > 1 : % mfun_quick_sort(list)(1,_max_)(what) ; mfun_quick_sort(list)(if known list[0] : 0 else : 1 fi,_max_)(what) ; fi ; enddef ; vardef uniquelist(suffix list) = % this one will be defined later enddef ; vardef copylist(suffix list, target) = save i ; i := 1 ; forever : exitif unknown list[i] ; target[i] := list[i] ; i := i + 1 ; endfor ; enddef ; vardef listtolines(suffix list) = list[1] for i=2 upto listsize(list) : -- list[i] endfor enddef ; vardef listtocurves(suffix list) = list[1] for i=2 upto listsize(list) : .. list[i] endfor enddef ; %D The sorter is used in: % not yet ok vardef shapedlist(suffix p) = % takes a list of paths save l ; pair l[] ; save r ; pair r[] ; save i ; i := 1 ; save n ; n := 0 ; forever : exitif unknown p[i] ; n := n + 1 ; l[n] := ulcorner p[i] ; r[n] := urcorner p[i] ; n := n + 1 ; l[n] := llcorner p[i] ; r[n] := lrcorner p[i] ; i := i + 1 ; endfor ; for i = 3 upto n : if xpart r[i] < xpart r[i-1] : r[i] := (xpart r[i],ypart r[i-1]) ; elseif xpart r[i] > xpart r[i-1] : r[i-1] := (xpart r[i-1],ypart r[i]) ; fi ; if xpart l[i] < xpart l[i-1] : l[i-1] := (xpart l[i-1],ypart l[i]) ; elseif xpart l[i] > xpart l[i-1] : l[i] := (xpart l[i],ypart l[i-1]) ; fi ; endfor ; if n > 0 : simplified ( for i = 1 upto n : r[i] -- endfor for i = n downto 1 : l[i] -- endfor cycle ) else : origin -- cycle fi enddef ; %D Dumping is fake anyway but let's keep this: let dump = relax ; %D Loading modules can be done with: def loadmodule expr name = % no vardef % input can't be used directly in a macro if unknown scantokens("context_" & name) : save s ; string s ; % s := "mp-" & name & ".mpiv" ; % message("loading module",s) ; % s := "input " & s ; s := "input " & "mp-" & name & ".mpiv" ; expandafter scantokens expandafter s fi ; enddef ; def loadfile (expr filename) = scantokens("input " & filename) enddef ; def loadimage (expr filename) = image(scantokens("input " & filename);) enddef ; %D Handy for backgrounds: def drawpathwithpoints expr p = do_drawpathwithpoints(p) enddef ; def do_drawpathwithpoints(expr p) text t = draw p t ; if length(p) > 2 : begingroup ; save _c_ ; path _c_ ; save _p_; picture _p_ ; _p_ := image ( _c_ := if cycle p : fullsquare else : fullcircle fi scaled 6pt ; for i=0 upto length(p) if cycle p : -1 fi : fill _c_ shifted point i of p withcolor white ; draw _c_ shifted point i of p withcolor white/2 withpen pencircle scaled .5pt ; if (i = 0) and cycle p : _c_ := fullcircle scaled 6pt ; fi ; endfor ; for i=0 upto length(p) if cycle p : -1 fi : draw textext("\infofont " & decimal i) ysized 2pt shifted point i of p ; endfor ; ) ; setbounds _p_ to boundingbox p ; draw _p_ ; fi ; enddef ; %D These new helpers are by Alan and are used in for instance the mp-node %D module. newinternal crossingdebug ; crossingdebug := 0 ; newinternal crossingscale ; crossingscale := 10 ; newinternal crossingnumbermax ; crossingnumbermax := 1000 ; % primary, secondary or tertiary? always hard to decide but primary makes sense vardef infotext@#(expr txt, ysize) = textext@#("\infofont " & if numeric txt : decimal fi txt) ysized ysize enddef ; primarydef p crossingunder q = begingroup save pic ; picture pic ; pic := nullpicture ; if picture p : for i within p : if stroked i : addto pic also image(draw pathpart i crossingunder q) ; fi endfor elseif path p : save n, t, a, b, c, r, bcuttings, hold ; numeric n, t[], hold ; path a, b, c, r, bcuttings, hold[] ; c := makepath(currentpen scaled crossingscale) ; r := if picture q : boundingbox fi q ; t[0] := n := hold := 0 ; a := p ; % The cutbefore/cutafter using c below prevents endless loops! %forever : % find all intersections for i=1 upto crossingnumbermax : % safeguard clearxy ; z = a intersectiontimes r ; if x < 0 : exitif hold < 1 ; a := hold[hold] ; hold := hold - 1 ; clearxy ; z = a intersectiontimes r ; fi (t[incr n], whatever) = p intersectiontimes point x of a ; if x = 0 : a := a cutbefore c shifted point x of a ; elseif x = length a : a := a cutafter c shifted point x of a ; else : % before or after? b := subpath (0,x) of a cutafter c shifted point x of a ; bcuttings := cuttings ; a := subpath (x,length a) of a cutbefore c shifted point x of a ; clearxy ; z = a intersectiontimes r ; if x < 0 : a := b ; cuttings := bcuttings ; else : if length bcuttings > 0 : clearxy ; z = b intersectiontimes r ; if x >= 0 : hold[incr hold] := b ; fi fi fi fi if length cuttings = 0 : % a single point: nothing cut exitif hold < 1 ; a := hold[hold] ; hold := hold - 1 ; fi if i = crossingnumbermax : message("crossingunder reached maximum " & decimal i & " intersections."); fi endfor if n = 0 : % No crossings, we return the PATH save pic ; path pic ; pic := p ; else : % n>0 sortlist(t,) ; % we add too much, maybe a test is needed t[incr n] = length p if cycle p : + t[1] fi ; % save tt[] ; numeric tt[] ; uniquelist(t,tt) ; t := tt ; % Now, n>1 ! % t[0] is the first point of the path and t[n] is the last point % (or the first intersection beyond the length if cyclic) save m ; m := 0 ; for i=if cycle p: 2 else: 1 fi upto n : % skip the first segment if cyclic % as it gets repeated (fully) at the end. if crossingdebug > 0 : if crossingdebug = 1 : addto pic doublepath c shifted point t[i] of p withpen currentpen withtransparency(1,.5) ; elseif crossingdebug = 2 : addto pic also infotext (incr m,crossingscale/5) shifted point t[i] of p ; fi fi a := subpath (t[i-1],t[i]) of p if i > 1 : cutbefore (c shifted point t[i-1] of p) fi if (i < n) or (cycle p) : cutafter (c shifted point t[i] of p) fi ; if (not picture q) or (a outsideof q) : addto pic doublepath a withpen currentpen ; fi endfor fi fi pic endgroup enddef ; primarydef p insideof q = begingroup save pth, pic, t ; path pth ; picture pic ; pic := if path q : image(draw q;) else : q fi ; pth := p -- center pic ; (t, whatever) = pth intersectiontimes boundingbox pic ; t < 0 endgroup enddef ; % primarydef p insideof q = % if (path q or picture q) : % if (path p or picture p) : % (xpart llcorner p > xpart llcorner q) and % (xpart urcorner p < xpart urcorner q) and % (ypart llcorner p > ypart llcorner q) and % (ypart urcorner p < ypart urcorner q) % elseif pair p : % (xpart p > xpart llcorner q) and % (xpart p < xpart urcorner q) and % (ypart p > ypart llcorner q) and % (ypart p < ypart urcorner q) % fi % elseif (numeric p and pair q) : % % range check % (p >= xpart q) and (p <= ypart q) % else : % maybe triplets and such % false % fi % enddef ; primarydef p outsideof q = not (p insideof q) enddef ; %D Also handy: vardef circularpath primary n = reverse (for i=0 step 2/n until 8-2/n+2eps: point i of fullcircle .. endfor cycle) rotated 90 enddef ; vardef squarepath primary n = for i=0 step 1/n until 4-1/n + 2eps: point i of fullsquare -- endfor cycle enddef ; vardef linearpath primary n = origin for i=1/n step 1/n until 1-1/n + 2eps: -- point i of (origin--(1,0)) endfor enddef ; %D A nice tracing helper: color pensilcolor ; pensilcolor := .5red ; newinternal pensilstep ; pensilstep := 1/25 ; vardef pensilled(expr p, q) = image ( draw p withcolor pensilcolor withpen q ; for i = 0 step pensilstep until length(p) + eps: draw point i of p withcolor white withtransparency (1,.5) withpen q ; endfor ; ) enddef ; %D Easy to forget but handy for manuals: vardef tolist(suffix l)(text t) = save n ; n := 1 ; for p = t : if numeric p : n := p ; dispose(l[n]) elseif pair p : l[n] := p ; n := n + 1 ; elseif path p : for i=0 step 1 until length(p) : l[n] := point i of p ; n := n + 1 ; endfor ; else : % ignore fi ; endfor ; forever : exitif unknown l[n] ; dispose(l[n]) n := n + 1 ; endfor ; enddef ; vardef topath(suffix p)(text t) = save i ; i := if known p[1] : 2 ; p[1] elseif known p[0] : 1 ; p[0] else : 0 ; origin fi forever : exitif unknown p[i] ; t p[i] hide(i := i + 1) endfor enddef ; vardef tocycle(suffix p)(text t) = topath(p,t) t cycle enddef ; % reimplemented to support paths and pictures def drawdot expr p = if pair p : addto currentpicture doublepath p withpen currentpen _op_ elseif path p : draw image ( for i=0 upto length p : addto currentpicture doublepath point i of p withpen currentpen _op_ ; endfor ; ) elseif picture p : draw image ( save pp ; path pp ; for i within p : if stroked i or filled i : pp := pathpart i ; for j=0 upto length pp : addto currentpicture doublepath point j of pp withpen currentpen _op_ ; endfor ; fi ; endfor ; ) fi enddef ; % vardef textlength(text t) = % save n ; n := 0 ; % for i = t : % n := n + 1 ; % endfor; % n % enddef; vardef mfun_timestamp = decimal year & "-" & decimal month & "-" & decimal day & " " & if ((time div 60) < 10) : "0" & fi decimal (time div 60) & ":" & if ((time-(time div 60)*60) < 10) : "0" & fi decimal (time-(time div 60)*60) enddef ; vardef totransform(expr x, y, xx, xy, yx, yy) = save t ; transform t ; xxpart t = xx ; yypart t = yy ; xypart t = xy ; yxpart t = yx ; xpart t = x ; ypart t = y ; t enddef ; vardef bymatrix(expr rx, sx, sy, ry, tx, ty) = save t ; transform t ; xxpart t = rx ; yypart t = ry ; xypart t = sx ; yxpart t = sy ; xpart t = tx ; ypart t = ty ; t enddef ; let xslanted = slanted ; def yslanted primary s = transformed begingroup save t ; transform t ; xxpart t = 1 ; yypart t = 1 ; xypart t = 0 ; yxpart t = s ; xpart t = 0 ; ypart t = 0 ; t endgroup enddef ;