Tuesday, September 26, 2023

Drawing Diagrams

Way back in the day, I used to draw diagrams with a tool called pic. This only worked with troff. Then I used fig when I was working with latex. Since the early '90s, I've been forced into hideously painful WYSIWYG diagram editors (Powerpoint is only the worst example; Visio, Framemaker, Illustrator and the like are all painful).

I was reminiscing earlier this week with a colleague who has similar values and a need to produce documentation on a large system he is building. I discussed pic and fig and how much easier it was back in the day, although at the same time noted the many issues these tools had. And how I would pay good money for a tool that made it easy again.

After we'd finished talking, I realized how what I'd described matched exactly the sort of problem I like to tackle … and furthermore the sort of problem I like to tackle here. So here goes.

Because in the interim the world has gone Web, my default output is going to be for the Web, so I'm going to build everything in JavaScript. Because I want a lot of flexibility, I'm going to go straight for a Canvas model and draw everything by hand.

So, as always(?), I want to start by building something that approaches "the whole application" so that we can see the overall picture and then fill in the details as they emerge. In this case, before we start getting fancy, I basically want to have a div with a couple of tabs: one with a text area to enter the description, and one with a canvas to show the "final" diagram (we will return to the notion of how many diagrams, later). So this requires a simple HTML file and a bunch of stubbed JavaScript in main.js. For those playing at home, this is checked in with the tag DIAGRAM_TOOL_FIRST_STEP.
<html>
  <head>
    <title>Diagrammer</title>
    <script type="text/javascript" src="js/main.js"></script>
  </head>
  <body onload="initialize()">
    <div>
      <div class="toolbar">
        <button class="toolbar-update">Update</button>
      </div>
      <div class="tabbed-window">
        <div class="tabs-row">
          <div class="input-tab">
            <div class="tab-title">Input</div>
            <div>
              <textarea class="text-input"></textarea>
            </div>
          </div>
          <div class="output-tab">
            <div class="tab-title">Output</div>
            <div>
              <canvas class="diagram"></canvas>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
function initialize() {
  var updateButton = document.getElementsByClassName("toolbar-update")[0];
  updateButton.addEventListener("click", pipeline);
}

function pipeline(ev) {
  var model = new DiagramModel();
  readText("text-input", parser(model));
  var portfolio = new Portfolio();
  model.partitionInto(portfolio);
  tabModel("tabs-row", ensureTabs(portfolio));
  portfolio.each((graph, tab) => graph.layout(d => d.drawInto(tab)));
}

// TODO: everything below here needs to broken out into modules

// jstda.js
function readText(label, processor) {
  var input = document.getElementsByClassName(label)[0];
  processor(input.value);
}

function tabModel(label, processor) {
  var tabrow = document.getElementsByClassName(label)[0];
  processor(tabrow);
}

function ensureTabs(portfolio) {
  return function(tabrow) {
    portfolio.ensureTabs(tabrow);
  }
}

// parser.js
function parser() {
  return function(text) {
    console.log(text);
  }
}

// model.js
class DiagramModel {
  partitionInto(c) {
    console.log("partition model into", c);
  }
}

// portfolio.js
class Portfolio {
  ensureTabs(tabrow) {
    console.log("need to ensure tabs");
  }

  each(f) {
    console.log("iterate over graphs and provide tabs");
  }
}
As always, the output from that is unpleasant on the eye and really needs some CSS to make it palatable. I'll come back to that, but for now, let's consider what that pipeline means.

First off, as regular readers know, I'm a TDA (tell-don't-ask) enthusiast. Mainly this stems from doing TDD with complex systems in Java; the mock support is not as good in JavaScript, but my brain remains wired up the same way - if it can't be functional, let it be TDA. And you never know, I may get around to actually writing some tests this time!

Consequently, everything is coupled up in pipeline and that's the only function I'm going to talk about. Almost everything else is plumbing that is just there so that the pipeline doesn't throw any errors.

There are four "phases" to the pipeline:
  • We convert the input text into a model of the graph in memory;
  • We split the graph into connected chunks (if the graph is completely connected, there will only be one such chunk) and build a Diagram for each chunk which are collected in a Portfolio;
  • We make sure that there is a tab and a canvas for each Diagram;
  • We go through each Diagram in the portfolio, first laying it out and then rendering it into the canvas.
What could be simpler?

No comments:

Post a Comment