Leo Tolstoy's story as translated by Aylmer Maude.
Also during the semester I wrote an SVG to stylize the initials of the recreational math club. It's supposed to look similar to Escher's Circle Limit series, and is based on the surface of a mucube with a repeating pattern. The thinly outlined quadrilaterals are distorted square faces.
Granjon Boxes
I started working on a font which will recreate some of the typographical ornaments designed by Robert Granjon. A few years ago, in an essay by Elliot Offner, I was introduced to the following set of six pieces of moveable type which can serve as the building blocks for complex printed arabesques.
Offner published The Granjon Arabesque, which is a book showcasing thirty arrangements pressed using these characters. The glyphs that I've added to the Granjon Boxes font so far are based on photos I took of the book's pages.
The arrangement of Unicode box-drawing characters above is my recreation of a design featured on page 105 of Granjon's flowers by Hendrik D. L. Vervliet. The characters were traced in Inkscape, creating an SVG file, which was converted to WOFF2 using FontForge. The 2025 version of Granjon Boxes contains the characters ┑┘┖┏╺╸╷╻╴╶╹╵┝┫┬┰┨├┻┷ and the space character U+20. The fonts directory on this website will include the latest version licensed under the SIL Open Font License.
If you're reading this on my website, you should be able to click on the arrangements and reveal the typical box-drawing character designs by displaying them in the Kreative Square font.
These designs have been digitized before. The much more comprehensive font Graveur by Juanjo López is based on the work of Robert Granjon and also includes some of the same decorative characters. Another book of arrangements, Kleines Spiel mit Ornamenten by Max Caflisch, was digitized and translated from German to French by Jacques André and published online: Petits jeux avec des ornements.
According to STD 66 RFC 39861, the characters of the string !#$&'()*+,/:;=?@[] could have a special meaning in a URI and are reserved. The ASCII alphanumeric characters and those contained in -._~ are unreserved. Any character outside of these two sets must be percent-encoded before inclusion in a URI. This is what the JavaScript encodeURI() function does, except that it also encodes the square brackets [], which were not yet included in the set of reserved or unreserved characters when the superseded RFC 2396 was written.
The unreserved characters can always be left unencoded, but we may need to encode a subset of the reserved characters.2 This subset depends on the URI scheme being used and where in the URI the characters are. The function encodeURIComponent() encodes everything except for the characters of !'()*, which might not need to be encoded as they weren't yet reserved in RFC 2396, and the unreserved characters. This means thirteen of the reserved characters are encoded, but we can encode a smaller subset in the two following cases.
Data URIs
RFC 2397 states that the main content section of a data URI will consist of zero or more urlchar characters, and that these characters are defined in RFC 2396. The definition is restated below as 2396-uric, and allows any percent-encoded or unreserved character, in addition to all but #[] of the reserved characters.
This more formal description of the character sets shows that every character in the output of encodeURI() will either be the number sign U+23 or a urlchar character. This means we can call encodeURI(), then replace all occurrences of # with %23, and the output will be valid within a data URI.
The code below shows how an SVG data URI might be constructed using JavaScript.3
The resulting string is data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20128%20128%22%3E%0A%09%3Crect%20fill=%22%2369E%22%20x=%2220%22%20y=%2220%22%20width=%2215.2%22%20height=%2287%22/%3E%0A%09%3Ctext%20fill=%22Gray%22%20x=%2219%22%20y=%2219%22%3E%5Co/%3C/text%3E%0A%3C/svg%3E.
The WHATWG has published a section on data URLs. If you write a valid URL string, it will be converted to a URL record by the URL parser before being processed as a data URL. This processing discards the fragment component of the URL, which begins after U+23 (#), so leaving a number sign unencoded will cut the data short. The rest of the valid URL string may include the following parts: data:, URL-path-segment string, U+2F (/), U+3F (?), URL-query string. All of these parts are formed from URL units, which is a set of characters equal to 2396-uric plus most non-ASCII Unicode characters. The fact that the URL parser splits a data URL into parts has no effect on the data. You can leave forward slash and question mark delimiters unencoded, as the parts will be rejoined by the URL serializer during data URL processing.
In this case, both the RFC 2397 and WHATWG definitions of validity treat ASCII characters in effectively the same way. The difference is that the WHATWG allows unencoded non-ASCII.
Query Strings
From RFC 3986 Section 3.4:
The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.
Continuing the description of character sets from the Data URIs section, we can add the definition for URI query components as given in RFC 3986.
It turns out that the characters allowed within the query are exactly the 2396-uric characters.
A method to store data within the query is not specified in RFC 3986, but usually the characters &+= have special purposes. By reading the query contents with something like the JavaScript URLSearchParams interface, you interpret the query string as a set of key=value pairs. These pairs are separated by U+26 (&) ampersands, and U+2B (+) plus signs are used throughout the string to represent spaces. See the WHATWG's definition of the application/x-www-form-urlencoded parser for an exact description of this process.
The equals sign U+3D (=) needs to be encoded within the key portion of a pair, but not in the value portion, as only the first equals sign separates the two parts. Encode the data as you would for a data URI, then handle these three special characters, and as a final step we can change the encoding of U+20 spaces from %20 to +.
The above code generates the URI https://home.6t.lt/66c/mirror-polygon.svg?v=%5B%5B1,0%5D,%5B0.58,0.58%5D,%5B0,1%5D,%5B-0.58,0.58%5D,%5B-1,0%5D,%5B-0.58,-0.58%5D,%5B0,-1%5D,%5B0.58,-0.58%5D%5D&i=background-image:url(%22data:image/svg%2Bxml,%253Csvg%2520xmlns=%2522http://www.w3.org/2000/svg%2522%2520viewBox=%25220%25200%2520128%2520128%2522%253E%250A%2509%253Crect%2520fill=%2522%252369E%2522%2520x=%252220%2522%2520y=%252220%2522%2520width=%252215.2%2522%2520height=%252287%2522/%253E%250A%2509%253Ctext%2520fill=%2522Gray%2522%2520x=%252219%2522%2520y=%252219%2522%253E%255Co/%253C/text%253E%250A%253C/svg%253E%22);color-scheme:light+dark&h=6.
If you want to encode the whole query string at once, instead of individual values, then the reserved characters to be encoded are #+[]. You can't encode all ampersands and equals signs as they may be present and acting as delimiters. Any U+26 (&) or U+3D (=) characters intended as data would be manually encoded if necessary, and all of this encoding must be done after using something like encodeURI() to avoid double encoding.
Just as with data URLs, you can leave most non-ASCII characters unencoded in the query component while still complying with the WHATWG's rules for writing URLs.
Endnotes
As of writing, there is an open issue on the WHATWG URL Standard's GitHub page in support of unescaped square brackets. The definition of a valid URL string may change in the future, as some ASCII characters are frequently used in URLs despite being disallowed.
The URL Specification by Alwin Blok is a condensed description of URLs which I found helpful.
Uploaded an SVG version of the UMass Recreational Math Club logo.
66f - Square Mesh Navigation
2024-09-27T15:01:38-04:00
This is the content currently featured on my website's home page. I may edit the About Me section in the future, but the Number Space description I wrote in early 2023 should stay the same.
Number Space
A Sudoku variant that doesn't change anything but the shape of the puzzle. This interactive website is a work in progress. So far there are four puzzles that a visitor can play, as long as they have a keyboard and mouse.
These oddly shaped puzzles can be modeled with a graph structure. Suppose there is a vertex for each cell in the puzzle. For example a regular Sudoku puzzle would have 81 vertices. The player traverses this graph by moving their mouse between adjacent cells.
During puzzle navigation there are two components to the player's position: Where are they in the puzzle? And how is this location placed on the screen? Where they are is at the current vertex, which represents one of the puzzle's cells. This cell's placement on the screen is the player's level. The level describes the cell's horizontal position, vertical position, and orientation. There are eight possible orientations in this game, which correspond to the symmetries of a square.
The player's position changes frequently, so these two components must change accordingly. Each edge in the graph is labeled by a matrix which describes the relationship between its two endpoints. The level is represented with a similar matrix. Every time the player moves to a new cell, they follow a single edge in the graph. The current vertex changes to this edge's endpoint, and the level is multiplied by the matrix which labels this edge.
I put this collage together in November 2021, using material related to books published by the University of Massachusetts Press.
About Me
I graduated from UMass Amherst with a master's degree in computer science. My favorite subjects are computer graphics, abstract algebra, and artificial intelligence.
I first learned about Jorge Luis Borges and his short story Death and the Compass in 2022 while reading George Fayen's essay in the book Patterns of Symmetry. In July 2024 I read the story and discussed it with a book club run by my school's library.
The Page to Screen Book Club
After finishing Terry Pratchett's Going Postal, an eight day story about a conman turned postmaster, we read Death and the Compass and watched the short film adaptation Spiderweb directed by Paul Miller.
The first sentence of the story, as translated by Donald A. Yates, is: Of the many problems which exercised the reckless discernment of Lönnrot, none was so strange—so rigorously strange, shall we say—as the periodic series of bloody events which culminated at the villa of Triste-le-Roy, amid the ceaseless aroma of the eucalypti.
I read this in the book Labyrinths, and we noted that translator Anthony Kerrigan worded it as the exercise of daring perspicacity in the text other club members read. The line came up again as the first narration of Spiderweb: Of the many unusual cases which challenged the powers of the celebrated investigator Erik Lönnrot, none was as bizarre as the periodic series of events which came to a conclusion at the desolate mansion of Triste-le-Roy amid the heady aroma of eucalyptus.
I liked the story, its half hour adaptation, and the cover of the copy of Labyrinths I checked out at Neilson Library:
A slightly different version of the cover was at one point featured on photographer Jason Fulford's website, but can now be found at isfdb.org (jpg). The mirrored pentagon inspired me to put together a little polygon reflection project I had been thinking about for a while.
This interactive polygon placement tool is an SVG image that edits itself. Click to place a polygon, and the next click will place a reflection of the polygon across one of its sides. The most recently placed polygon is selected, unless you shift-click to select another one. Shift-click the background to select none, and press backspace or delete to remove the selected polygon. Use control-C or command-C to copy a data URI containing the current image.
The following parameters listed with their default values can be edited using a query string:
borderColor
CanvasText
decimalPlaces
6
fillColor
red
heightOfPolygon
10
inlineStyle
background-color:Canvas;color-scheme:light dark
selectedWidth
.5
unselectedWidth
.1
vertices
5
Set the values using the first letter of the parameter name. The default values for the polygon border color and the background color are system colors. The width variables adjust the size of the polygon's border. The vertices parameter can either be a number 3 or greater, or a 2D array of points defining any polygon. Convex polygons work best, and if you only specify the number of vertices then a regular polygon will be used.
If a nonzero number of degrees is provided for the fold angle, then the SVG output to the clipboard will be animated with CSS transforms. If the margin size is not keep, then it is a number, and the view box of the output SVG will frame the polygon arrangement. You can automate the initial polygon placement by passing a 2D point as two numbers separated by a comma. A transform can be applied to the output SVG, but the syntax of the transform string depends on the other parameters. If there is no animation, use a SVG transform attribute form like rotate(90). If there is an animation, use a CSS transform property form like rotate(90deg). If there is animation and a custom margin, use a form compatible with both like matrix(0,1,-1,0,0,0). Finally, insert xlink:href attributes into the output <use> elements by passing true or 1.