%D \module %D [ file=t-visualcounter, %D version=2013.04.01, %D title=\CONTEXT\ User Module, %D subtitle=Visual Counter, %D author=Aditya Mahajan, %D date=\currentdate, %D copyright=Aditya Mahajan, %D email=adityam ieee org, %D license=Simplified BSD License] \writestatus{loading}{Visual Counter (ver: 2013.04.01)} \startmodule [visualcounter] \unprotect \startinterface all \setinterfaceconstant {palette} {palette} \setinterfaceconstant {path} {path} \stopinterface %D \section{Color Palettes} %D %D The color of the visual counters is control using color palettes. \definepalet [visualcounter:bright] [active=orange, past=blue, future=gray] \definenamespace [visualcounter] [ \c!type=module, \c!name=visualcounter, \c!command=\v!yes, setup=\v!list, \c!style=\v!yes, \s!parent=visualcounter, ] \setupvisualcounter [ \s!counter=, \c!n={\rawcountervalue[\visualcounterparameter\s!counter]}, \c!text={\convertedcounter[\visualcounterparameter\s!counter]}, \c!max\c!text={\lastcounter[\visualcounterparameter\s!counter]}, \c!last={\lastcountervalue[\visualcounterparameter\s!counter]}, \c!style=, \c!color=, \c!palette=visualcounter:bright, \c!mp=visualcounter::countdown, \c!width=1EmWidth, \c!height=1ExHeight, \c!rulethickness=1bp, \c!distance=0.5EmWidth, % Parameters passed to scale \c!maxwidth=\maxdimen, \c!maxheight=\maxdimen, ] %% TODO: shaded=yes|no randomized=yes|no %D \section {MP instance} %D %D To prevent interference with other MP commands, we define a separate metapost %D instance \defineMPinstance [visualcounter] [\s!format=metafun, \s!extensions=\v!no, \s!initializations=\v!yes] \setupMPinstance [visualcounter] [\c!textcolor=\visualcounterparameter\c!color, \c!textstyle=\visualcounterparameter\c!style] %D \section {usevisualcounter} \unexpanded\def\usevisualcounter {\dosingleargument\usevisualcounter_indeed} \def\usevisualcounter_indeed[#options]#name% {\bgroup \edef\currentvisualcounter{#name}% \setupvisualcounter[#name][#options]% \setuppalet[\visualcounterparameter\c!palette]% \scale [ \c!maxwidth=\visualcounterparameter\c!maxwidth, \c!maxheight=\visualcounterparameter\c!maxheight, ]{\expanded{\useMPgraphic{\visualcounterparameter\c!mp}}}% \egroup} \startuseMPgraphic{visualcounter::initialization} newnumeric current_counter, last_counter; current_counter := \visualcounterparameter\c!n; last_counter := max(\visualcounterparameter\c!last, 1); newnumeric width ; width := \visualcounterparameter\c!width; newnumeric height ; height := \visualcounterparameter\c!height; newnumeric distance ; distance := \visualcounterparameter\c!distance; newnumeric rulethickness; rulethickness := \visualcounterparameter\c!rulethickness; newcolor past_color; past_color := \MPcolor{past}; newcolor active_color; active_color := \MPcolor{active}; newcolor future_color; future_color := \MPcolor{future}; \stopuseMPgraphic %D \section {The counters} %D %D \subsection {markers} \definevisualcounter [markers] [ \c!mp=visualcounter::markers, \c!width=1EmWidth, \c!distance=\visualcounterparameter\c!width/4, \c!mp\c!setups=visualcounter::markers:circle, ] \startuseMPgraphic{visualcounter::markers:circle} def show_past_marker(expr shift) = fill fullcircle xyscaled(width, width) shifted (shift, 0) withcolor past_color enddef; def show_active_marker(expr shift) = fill fullcircle xyscaled(width, width) shifted (shift, 0) withcolor active_color enddef; def show_future_marker(expr shift) = fill fullcircle xyscaled(width, width) shifted (shift, 0) withcolor future_color enddef; \stopuseMPgraphic \startuseMPgraphic{visualcounter::markers:square} def show_past_marker(expr shift) = fill fullsquare xyscaled(width, width) shifted (shift, 0) withcolor past_color enddef; def show_active_marker(expr shift) = fill fullsquare xyscaled(width, width) shifted (shift, 0) withcolor active_color enddef; def show_future_marker(expr shift) = fill fullsquare xyscaled(width, width) shifted (shift, 0) withcolor future_color enddef; \stopuseMPgraphic \startuseMPgraphic{visualcounter::markers:star} % Copied from http://tug.org/pracjourn/2012-1/hefferon.html def fullstar = hide ( z0 = origin; z1 = (x0, 0.5); z2 = ((z1 - z0) rotated ( 360/5)) + z0 ; z3 = ((z1 - z0) rotated (2*360/5)) + z0 ; z4 = ((z1 - z0) rotated (3*360/5)) + z0 ; z5 = ((z1 - z0) rotated (4*360/5)) + z0 ; z6 = whatever[z1, z3] = whatever[z2, z5]; z7 = whatever[z1, z3] = whatever[z2, z4]; z8 = whatever[z2, z4] = whatever[z3, z5]; z9 = whatever[z1, z4] = whatever[z3, z5]; z10 = whatever[z1, z4] = whatever[z2, z5]; ) (z1 -- z6 -- z2 -- z7 -- z3 -- z8 -- z4 -- z9 -- z5 -- z10 -- cycle) enddef; def show_star(expr shift, fill_color) = newpath p; p := fullstar xyscaled(width, width) shifted (shift, 0); fill p withcolor fill_color; draw p withcolor 0.5*fill_color; enddef; def show_past_marker(expr shift) = show_star(shift, past_color); enddef; def show_active_marker(expr shift) = show_star(shift, active_color); enddef; def show_future_marker(expr shift) = show_star(shift, future_color); enddef; \stopuseMPgraphic \startuseMPgraphic{visualcounter::markers} begingroup ; \includeMPgraphic{visualcounter::initialization} \includeMPgraphic{\visualcounterparameter{\c!mp\c!setups}} pickup pencircle scaled \visualcounterparameter\c!rulethickness ; for i := 1 upto last_counter : if i < current_counter : show_past_marker(i*(width + distance)); elseif i > current_counter : show_future_marker(i*(width + distance)) fi ; endfor ; %% Write the current marker at the end (incase the markers overlay each other) show_active_marker(current_counter*(width + distance)) \stopuseMPgraphic %D \subsection {countdown} \definevisualcounter [countdown] [ \c!mp=visualcounter::countdown, \c!width=1EmWidth, \c!height=1ExHeight, \c!distance=3EmWidth, ] \startuseMPgraphic{visualcounter::countdown} begingroup ; \includeMPgraphic{visualcounter::initialization} % label newpicture countertext ; countertext := textext("\visualcounterparameter\c!text"); newpicture max_countertext ; max_countertext := textext("\visualcounterparameter{\c!max\c!text}"); newnumeric inner_diameter, outer_diameter ; inner_diameter := 1.5 * max(bbwidth(max_countertext), bbheight(max_countertext)); outer_diameter := inner_diameter + max(\visualcounterparameter\c!width, \visualcounterparameter\c!height) ; newpath inner_circle, outer_circle ; % make the circle start at 12 o'clock and go clockwise inner_circle := reverse (fullcircle scaled inner_diameter) rotated 90; outer_circle := reverse (fullcircle scaled outer_diameter) rotated 90; % boundary distance newnumeric inner_boundary, outer_boundary ; inner_boundary := \visualcounterparameter\c!distance/last_counter * (inner_diameter)/(outer_diameter) ; outer_boundary := \visualcounterparameter\c!distance/last_counter ; % step size newnumeric inner_time, outer_time ; inner_time := arclength (inner_circle)/last_counter ; outer_time := arclength (outer_circle)/last_counter ; newpath slice ; newpair pt_inner_left, pt_inner_right ; newpair pt_outer_left, pt_outer_right ; for i = 1 upto last_counter : pt_inner_left := point ((i-1)*inner_time) on inner_circle ; pt_inner_right := point (i*inner_time - inner_boundary) on inner_circle ; pt_outer_left := point ((i-1)*outer_time) on outer_circle ; pt_outer_right := point (i*outer_time - outer_boundary) on outer_circle ; slice := inner_circle cutbefore pt_inner_left cutafter pt_inner_right --- reverse (outer_circle cutbefore pt_outer_left cutafter pt_outer_right ) --- cycle ; fill slice withcolor if i < current_counter : past_color elseif i = current_counter : active_color else : future_color fi ; endfor ; label (countertext, origin) ; endgroup ; \stopuseMPgraphic %D \subsection {pulseline} \definevisualcounter [pulseline] [ \c!mp=visualcounter::pulseline, \c!path={origin -- (((current_counter-1)*width, 0) .. ((current_counter-0.5)*width, -0.2height) -- (current_counter*width, height) -- ((current_counter+0.5)*width, -0.2height) .. ((current_counter+1)*width,0)) -- ((last_counter+1)*width, 0) randomized 0.3width}, \c!width=0.5EmWidth, \c!height=3ExHeight, \c!rulethickness=1bp, ] \startuseMPgraphic{visualcounter::pulseline} begingroup ; \includeMPgraphic{visualcounter::initialization} newpath pulse ; pulse := \visualcounterparameter\c!path ; newpath helper_line ; helper_line := (0,-height) -- (0,height) ; newpair start_pulse, stop_pulse ; start_pulse := pulse intersectionpoint (helper_line shifted ((current_counter-1)*width, 0)) ; stop_pulse := pulse intersectionpoint (helper_line shifted ((current_counter+1)*width, 0)) ; newpath past_pulse, active_pulse, future_pulse ; past_pulse := pulse cutafter start_pulse ; active_pulse := pulse cutbefore start_pulse cutafter stop_pulse ; future_pulse := pulse cutbefore stop_pulse ; pickup pencircle scaled \visualcounterparameter\c!rulethickness ; draw past_pulse withcolor past_color ; draw active_pulse withcolor active_color ; draw future_pulse withcolor future_color ; endgroup ; \stopuseMPgraphic %D \subsection {scratchmarks} \definevisualcounter [scratchmarks] [ \c!mp=visualcounter::scratchmarks, \c!width=3bp, \c!height=3ExHeight, \c!distance=0.5EmWidth, angle=75, ] \startuseMPgraphic{visualcounter::scratchmarks} begingroup ; \includeMPgraphic{visualcounter::initialization} linecap := rounded ; newnumeric theta ; theta := \visualcounterparameter{angle} ; newpath left_marker, right_marker ; left_marker := origin -- height*dir(theta) ; right_marker := (-4*distance, height*sind(theta)) -- origin ; save marker ; def marker (expr i) = (if i mod 5 = 0 : right_marker else : left_marker fi) shifted (i*distance, 0) withcolor if i < current_counter : past_color elseif i = current_counter : active_color else : future_color fi ; enddef ; pickup pencircle scaled \visualcounterparameter\c!width ; for i := last_counter downto 1 : draw marker(i) ; endfor ; draw marker(current_counter) ; endgroup ; \stopuseMPgraphic %D \subsection {mayanumbers} \definevisualcounter [mayanumbers] [ \c!mp=visualcounter::mayanumbers, \c!width=1EmWidth, \c!height=1ExHeight, \c!distance=\visualcounterparameter\c!width/4, ] \startuseMPgraphic{visualcounter::mayanumbers} begingroup ; \includeMPgraphic{visualcounter::initialization} newnumeric linewidth ; linewidth := 4width + 5distance ; newnumeric linedistance ; linedistance := 2distance ; newpath marker; marker := ((0,0.5height) .. (0.5width,-0.1height) .. (0.5width, -0.4height) .. (0,-0.8height) .. (-0.5width,-0.4height) .. (-0.5width, -0.1height) .. cycle) ; newpath line ; line := (-0.5width - distance, 0) .. (0.1width,0.8height) --- (3.1width + 3distance, 0.8height) .. (3.5width + 4distance, 0) .. (3.1width + 3distance, -0.8height) --- (0.1width, -0.8height) .. cycle ; newnumeric total_lines; total_lines := floor(last_counter/5); newnumeric remaining_markers; remaining_markers := last_counter - total_lines*5; newnumeric start_offset; start_offset := linewidth - 0.5distance; newnumeric total_width; total_width := total_lines*(linewidth+linedistance) + remaining_markers*(width+distance); newpath boundary ; boundary := (0, 0.9height) -- (total_width, 0.9height) -- (total_width, -0.9height) -- (0, -0.9height) -- cycle; boundary := boundary shifted (start_offset, 0); % Draw the filled solid lines newnumeric linecount ; linecount := floor ((current_counter - 1) / 5) ; for i = 1 upto linecount : fill line shifted (i*(linewidth + linedistance), 0) withcolor past_color ; endfor % Draw the filled dots newnumeric offset ; offset := (linecount + 1)*(linewidth + linedistance) ; newnumeric markercount ; markercount := current_counter - linecount*5 ; if markercount = 5 : fill line shifted (offset,0) withcolor active_color ; else : for i = 1 upto markercount : fill marker shifted (offset + (i-1)*(width+distance), 0 ) withcolor if i = markercount : active_color ; else : past_color ; fi endfor fi % Draw the empty dots newnumeric markermax ; markermax := min(last_counter - linecount*5, 4) ; if markercount < 4 : for i = markercount + 1 upto markermax : fill marker shifted (offset + (i-1)*(width+distance),0) withcolor future_color ; endfor fi for blockcount = linecount + 1 upto ceiling(last_counter/5) : offset := (blockcount + 1)*(linewidth + linedistance) ; markercount := min(4, last_counter - blockcount*5) ; for i = 1 upto markercount : fill marker shifted (offset + (i-1)*(width+distance),0) withcolor future_color ; endfor endfor setbounds currentpicture to boundary ; endgroup ; \stopuseMPgraphic %D \section {progressbar} \definevisualcounter [progressbar] [ \c!mp=visualcounter::progressbar, \c!width=\the\dimexpr\textwidth/2\relax, \c!height=2ExHeight, ] \startuseMPgraphic {visualcounter::progressbar} begingroup; \includeMPgraphic{visualcounter::initialization} newpath boundary; boundary := unitsquare xyscaled (width, height); newpath progressbar; progressbar := unitsquare xyscaled (current_counter*width/last_counter, height); fill boundary withcolor future_color; fill progressbar withcolor past_color; setbounds currentpicture to boundary ; endgroup; \stopuseMPgraphic \protect \stopmodule