Subject: Coding a sample Tufte graphics in Mystique
From: "David F. Huynh"
Date: Sun, 22 Feb 2004 09:19:31 -0500
To: Rob Miller
CC: David Karger , Vineet Sinha

Hi Rob,

(You are using an HTML-capable email client, right? Otherwise, please let me know and I'll get this email in suitable format.)

I'm trying to illustrate how to use Mystique to code (most of) the table graphics I showed you last Thursday from Tufte's "The Visual Display of Quantitative Information", p.174, reproduced here for your convenience. I think this code probably needs some explanation in person, but I'd just like to ask for your first impression, mainly whether it's too complicated or not for the task that it does.

In addition, I have another idea (here) in the "breaking windows" theme for viewing this piece of code. In most languages, the linear order by which things in a method body need to be declared often clashes with the hierarchical strategy that humans use to understand the code. My idea is to break that linear order and show code fragments where they start to be "useful". In this example, the function processHalfBatch is declared linearly before the for loop, but it's shown on the right of the for loop. My argument is that a programmer wishing to understand the function foo:presentChart is likely to read the for loop first to get the overall picture and then dive into the various utility inner functions. This method of visualizing foo:presentChart helps her do that; otherwise, she must scan over most of the method body before reaching the for loop. Perhaps we can term this "micro software visualization". I'm CCing Vineet just in case he's interested.

Thanks!

David



@prefixJavaPackage XHTML edu.mit.csail.haystack.monet.subsyntax.XHTML

function foo:presentChart(List carModels, List years, List parts){
    XHTML overallTableHTML = xhtml:{ <table id="theTable"></table> }
    XHTML!Table overallTable = overallTableHTML.getElementByID("theTable")

    List yearsRow = map(years,
        function: (Resource year) as XHTML {
            return subst(xhtml:{ <span>{%1, view of year}</span> }, monet:present(year))
        }
    )
    XHTML partColumnHTML = xhtml:{ <table id="theTable"><tr><td><b>Trouble Spots</b></td></tr></table> }
    List rows = map(parts,
        function: (Resource p) as XHTML {
            View partView = monet:present(p)
            return subst(xhtml:{ <tr><td align="center">{%1}</td></tr> }, partView)
        }
    )
    partColumnHTML.getElementByID("theTable").insertRows(rows)


    Function processCarPart = function:(Resource car, Resource part) as XHTML {
        XHTML partRowHTML = xhtml:{ <tr></tr> }
        XHTML!TableRow partRow = partRowHTML.getRootElement()
       
        partRow.addCells(map(years,
            function:(Resource year) {
                String quality = ...code to retrieve quality from car, part, and year...
               
                SVG circle =
svg:{ <svg width="2ex" height="2ex" text-baseline="0em"></svg> }

                select quality
                    case "much better than average"
                        circle.getRootElement().addChild(
                            svg:{ <circle cx="1em" cy="1em" r="1em" style="stroke: black 1pt"></circle> }
                        )
                    case "better than average"
                        circle.getRootElement().addChild(
                            svg:{ <circle cx="1em" cy="1em" r="1em" style="stroke: black 0.15em"></circle> }
                        )
                    case "average"
                        circle.getRootElement().addChild(
                            svg:{ <circle cx="1em" cy="1em" r="1em" style="stroke: black 0.3em"></circle> }
                        )
                    case "worse than average"
                        circle.getRootElement().addChild(
                            svg:{ <circle cx="1em" cy="1em" r="1em" style="stroke: black 0.5pt"></circle> }
                        )
                    case "much worse than average"
                        circle.getRootElement().addChild(
                            svg:{ <circle cx="1em" cy="1em" r="1em" style="fill: black"></circle> }
                        )

                return subst(xhtml:{ <td>{%1}</td> }, circle)
            }
        ))
        return partRowHTML
    }

    Function processHalfBatch = function:(XHTML rowHTML, List carModels) {
        List cells = map(carModels,
            function: (Resource car) as XHTML {
                XHTML carTableHTML = subst(xhtml:{
                        <table id="theTable">
                            <tr><td colspan="{%1}" align="center"><b>{%2, view of car}</b></td></tr>
                        </table>

                    },
                    years.size(),
                    monet:present(car)
                )
                XHTML!Table carTable = carTableHTML.getElementByID("theTable")

                carTable.addRow(yearsRow)
                carTable.addRows(map(parts, processCarPart(car)))
            }
        )
        rowHTML.addCells(cells)
    }

    for batch in List.breakIntoBatches(carModels, 6)
        XHTML rowHTML = xhtml:{ <tr id="theRow"></tr> }

        List firstHalf = batch.getItems(0, 3)
        processHalfBatch(rowHTML, firstHalf)

        rowHTML.getElementByID("theRow").addCell(subst(xhtml:{ <td>{%1}</td> }, partColumnHTML))
   
        List secondHalf = batch.getItems(3, 3)
        processHalfBatch(rowHTML, secondHalf)

        overallTable.addRow(rowHTML)


    monet:present(
        overallTableHTML,
        ^monet:pe as monet:PresentationEnvironment
    )
}