Another Pleasing Pattern Producer via Decker I've been making improvements to the deck for my pleasing pattern producer recently, though it's yet to be finished; I started by restarting. I yearned to learn more about designing widgets in Decker, for future work, so this version has a pleasing pattern contraption effectively comprising the first deck. One very important reason to make a contraption for this is so that multiple such widgets can be in the same deck. All of the outer scripts became much simpler from this change, and there's now a gallery for the patterns one wishes to save. The little boundary between these states was helpful to enforce a proper design, and I've learned much more about effectively using Decker from the work. I want to improve this deck further, by adapting two more similar contraptions from this prime, once it's perfected, but plan to start work and focus on a Meta-Machine Code targeting in Decker instead. It was easy enough to get the contraption working, since I'd already finished the basic design in my last deck. Most work for the deck went into this contraption; the majority of work therein involved getting animations of patterns to work, and the second majority was optimizing its other animations. My starting plan had been to make a single contraption able to express three-by-three, four-by-four, and five-by-five patterns. I got dynamic manipulation widget working by modifying the contraption's prototype, but knew such changes would affect all instances and suspected I were going about this in the wrong way; I asked John Earnest about this over e-mail, and he confirmed it to me. I decided to attack the three-by-three patterns first, and to copy the prototype twice for the other two kinds of patterns; this was best, since three-by-three patterns masked a great deal of complexity I'd uncover in the animations, and I got some of the contraption working as desired without any code whatsoever. Designing contraptions, what attributes to expose is an early question. A great many bubble up from those of their subwidgets. The prime difference between two kinds of attributes is their interface: Some are meant for programmatic manipulation, and can be gettable without being settable; others are exposed in a convenient menu while using the ``Widgets'' tool, meant for adjustment by the operator, and are necessarily both gettable and settable. An important restriction of those latter attributes is the restricted set of shapes they may take: booleans, numbers, and one of three kinds of strings. Obviously, the points of the pattern should be an attribute, which requires the contraption to react appropriately when it's set arbitrarily; this dashes the silly notion of avoiding work, so the worst case change is always assumed and handled. For the previous deck, I'd mulled over the matter of how to represent the points outside of the producer, but had yet to address it; I'd mulled over a format which numerically represented them, years ago, but writing parsers and whatnot for such minor things is always a waste of time. A string was the only fitting shape available to hold the points for the menu, which resulted in two attributes after writing one interface failed: points and points_string. The latter separates the points with commas, the natural choice which I realized later made it valid Lil also; the matter of representing points was handled nicely by making the producer a contraption. It suffices to show how the buttons' scripts changed with the introduction of this contraption. The Clear button was in the first deck, with nothing strictly wrong about it, but makes a stark example:  on click do # This is the first version of the clearing code. properties.widgets.points.value:insert index with end pattern.clear[] each button in buttons button.text:"" end end on click do # This is the second, much simpler, version of the clearing code. pattern.points:() end  The original script had to manage all manner of superfluous state, and the new code instead lets the contraption handle everything. I finished a script for the Undo button, but never found a good time to publish it, and now it's itself superfluous. What follows is again the old worse and new better:  on click do # This is the original and unpublished code to undo. buttons[-1+first -1 take properties.widgets.points.value.index].text:"" properties.widgets.points.value:-1 drop properties.widgets.points.value pattern.clear[] draw[] end on click do # This second, better, and simpler code to undo resembles the newer code for clearing. pattern.points:-1 drop pattern.points end  The contraption makes similar scripts resemble each other, which is strictly improved. Both scripts accomplish the same thing, dropping the pattern's final point when one be there, but the contraption contains unnecessary details such as the text on its buttons that the first script blanks by itself. Not even efficiency works as an argument here, not that the notion isn't silly in tools like Decker. Both scripts are now what I'd wanted to write when stacking that original deck, a clear improvement. Another clear improvement was the brush attribute I added, since it suggested itself so clearly from the canvas. The previous deck had a ``hard edges'' mode which placed a square brush over each point to give the pattern a harder appearance, but I realized this to make no sense with the default brush of only one pixel. One's time is better spent making custom brushes for patterns rather than adding such queer little modes; I've pored over all default brushes, and most prefer sixteen and seventeen. Once I stopped trying to design an overly flexible contraption, and focussed solely on finishing the three-by-three iteration, everything became much simpler and more pleasant. I could've attempted to force the contraption to carry this complexity anyway, via summoning all needed widgets and swapping them in or out as needed, but figured that correctly to be a fool's game. That static nature of the layout for buttons proved as much: I played with the contraption's margins, a part of Decker I'd yet to learn properly, and post some struggling adjusted them to exclude all but the middle region; that question of layout in the face of resizing evaporated. This code gets their locations when drawing:  buttons..pos+buttons..size/2 # The variable buttons is but a list of the contraption's nine buttons.  The prototypes of both future contraptions based on this present will have the same dimensions: one- hundred-and-twenty-by-one-hundred-and-twenty. All I need to do is manually set the positions of the buttons once, and Decker handles the rest for me without any code, which I find to be so delightful. A view at the contraption's prototype shows sliders perfectly alike the sliders present in the card. Hidden are the grid of points and a text field holding the animation's name acting as ersatz symbol. This time I decided to split that function drawing the pattern into several parts. The first deck's function handled animations itself repetitively. I found a pleasant interface for this second deck: The drawing function calls a warping function which returns the transformed points, and draws those; only the type of animation which draws the pattern in order as a man would required any special code therein, and I may be able to eliminate that special case in a future iteration of the code; really, it's occurred to me while writing this how that special case designates something of a fundamentally different shape, and I now believe that drawing code should be separated from the others in a future stacking of the deck. In any case, I spent a great deal of time getting all of my warping functions working correctly. Following is one of the simplest functions followed by one of them most complex:  on horizontal_warp do positions:buttons..pos+buttons..size/2 distance:mag first (first positions)-last positions duration:(2*distance)%floor sys.frame/10/shiftingspeed.value measured:distance%duration if duration>distance measured:distance-measured end positions+measured*(1,0,-1)*list 1,0 end on descending_warp do positions:buttons..pos+buttons..size/2 distance:positions[2]-positions[6] x:mag first distance y:mag last distance bxy:list if x>y 1,y/x else (x/y),1 end smaller:positions[3]-positions[1] x:mag first smaller y:mag last smaller sxy:list 0.5*if x>y 1,y/x else (x/y),1 end distance:max distance duration:(2*distance)%floor sys.frame/10/shiftingspeed.value measured:distance%duration if duration>distance measured:distance-measured end positions+(((list 0, 0),(list 0, 0),(list -1,1), (list 0, 0),(list 0, 0),(list 0,0), (list 1,-1),(list 0, 0),(list 0,0))*measured*bxy)+ ( (list 0, 0),(list -1, 1),(list 0,0), (list 1,-1),(list 0, 0),(list -1,1), (list 0, 0),(list 1,-1),(list 0,0))*measured*sxy end  Each function collects the points' positions and uses the frame index as an implicit parameter. One problem I noticed is how the drawing animation takes a different amount of time than the rest, which causes them to be out of sync if one wishes to play them sequentially; a future stacking of the deck will need to add a field to save the starting frame's index and use it instead of the current frame. I began with the vertical and horizontal warping functions, modelled after the code in my very first deck; in those all points move the same distances if at all, and everything's fairly easy to follow. The descending and ascending warping functions were much more difficult to write at first, as points no longer move perfectly in tandem, which makes for larger tables. I'd started out using the normal geometric distance function for them, to notice the lines travelled much further than they ought to; I realized just because a pair of coordinates had the right distance under mag didn't mean much, and using distance alone lacks direction; since I wanted the lines to travel one point at a time, max is the function I really wanted to determine this; unequal dimensions would have one increment integer, one fractional. I felt a tad silly at realizing the lesser two cycles in the animation were exactly half the distance of the greatest, but there's no shame in plugging in values guessing at that which is correct and then working backwards from there; I used this method after struggling with the code. The code for the animations, sans drawing, is still slightly flawed: It occasionally flickers at its halfway point. This will be fixed later, and I figure the matter to be one of numerical inaccuracy. It keeps crossing my mind to optimize the drawing code by placing only the point needed at the time, but animations in Decker should be written to assume gaps in frames accessed, and it matters little. I'm very much so pleased with an aspect of the drawing code I never even bothered to get working for the other vector versions of these pleasing patterns: All line segments take the same amount of time to be drawn, which makes a small difference in how a pattern is drawn even though it looks the same; I prefer my crest to skip over the middle point, which gives some of the strokes a much faster look. Also related to that drawing code is a trick I use to make it last longer and look better, repeating the final point is prohibited by the normal interface but can be achieved anyway with the attribute:  pattern.points:pattern.points,-1 take pattern.points # One can also do this manually from the menu.  I enjoyed upgrading Decker to see the new rev primitive function that reverses the data given to it, although I suppose I only wound up using it in my first iteration of the code before eliminating it. When figuring out the issues with this code, I was constantly switching back-and-forth between views of the deck's card, the contraption's virtual card, and the contraption's script. It took me making a slight mistake before noticing the contraption could be used conveniently in the ``Interact'' mode without returning to the card, but I kept switching back to the card as I'd grown used to it; out of the habit now, I expect to use the slightly more efficient technique to spare a keystroke, probably. I loathe so-called ``print debugging'' and have vowed never to use it, but since Decker lacks better tools, I started using the show[] function as a tracer after enough frustration. Reading more about HyperCard, Decker's tools are much nicer. The most frustrating issues to bite me were reordering an expression to spare myself the parentheses, as I'd in APL, without remembering how Lil's conformance rules would ignore some of the data one way but not the other, followed by some off-by-one errors in dimensionality requiring me to call list on certain data to have them distributed across operations. It was invaluable to be able to dissect my code at the listener by plugging in values and walking my way down the assignments until I finally saw the problem. I'm sure HyperTalk just couldn't compare. One way in which Decker reminds me of machine code programming as done decades ago is in how there's little concern with abstract concerns of performance and even correctness in some respects. That is to mean once the program works at all it probably works in all expected instances, with verification involving mostly edge cases such as how the code copes with emptiness. It's pleasant to work with a design more concrete; I care nothing for my deck's performance extended to infinity, as it's finite. I learned how to create custom events while making this contraption. The ``cycle'' event fires when an animation finishes playing, letting the contraption switch between animations in a loop, say. My code to detect the cycle's end is simple and subtly incorrect: It compares the current points to the starting points, failing when the animation's final frame is skipped; that aforementioned flickering also sometimes causes the event to fire at the halfway point. Storing the starting frame's index as mentioned will solve this issue in addition to that other regarding flickering, by firing this event after the calculated final frame's passing; the event currently passes no parameters, but any future deck will pass a string for disambiguating an anticipated edge case I've realized in one adjustment. I chose to add custom behaviour for locking to this contraption: When locked, its buttons disappear. I learned why locking shouldn't interfere with setting attributes when testing my new event code and wondering why the cycle event wasn't rotating the points for a nice effect; I'd prohibited adjusting the points when locked, which went directly against this usage, and so removed that conditional code to correct the matter. I figure locked behaviour to be something for which one must gain intuition. Performance of the deck was bad but tolerable in the producer, nine percent or so in Decker's script profiler. I added the first gallery card to notice six blank copies of the contraption were between thirty-nine and forty percent, the worst performance I'd seen with any deck. The fans in my machine triggered whenever I lingered on that card. This unacceptable problem made me focus on optimization thereafter. I knew the problem to be one of animations. It's easiest to give any widget that needs to be animated the ``animation'' bit toggled, which sends view events to the widgets at sixty hertz, but drawing the pattern so unnecessarily was wasteful and I knew my buttons' view events to be poor. This work becomes a game of poring over the scripts to find anywhere the bit needs to be toggled and adding the behaviour; it's repetitive and mostly uninteresting to switch between views repeatedly to fix this when testing the contraption and finding another case to add. Eliminating the animation of the canvas when unnecessary was easy enough, however. The buttons were much more bothersome. After failing for hours to toggle their animations correctly, I tried rewriting the code instead as shown:  # This function is a point at which button visibility is toggled; it bails out early when invisible. # Each point's uses in the pattern are collected and used to slice the time allotted to the indices. # This is the original code adjusted after transplant from the first deck, with an iterative nature. on switch button id do button.toggle["solid" !card.locked] if (!"none"~button.style) copies:sum id=get_points[] switch:copies%floor sys.frame/last flickerspeed.interval-flickerspeed.value ifound:-1 each point place in points.value if id~point ifound:1+ifound if switch~ifound button.text:1+place end end end end end # This is the optimized code, much simpler, using the query language part of Decker. on switch button id do button.toggle["solid" !card.locked] if (!"none"~button.style) copies:sum id=get_points[] chosen:copies%floor sys.frame/last flickerspeed.interval-flickerspeed.value listed:extract value index where gindex=chosen where value=id from points.value button.text:if listed 1+first listed end button.animated:1