Pikchr

Flow charting difficulties
Login

Flow charting difficulties

(1.2) By Warren Young (wyoung) on 2024-02-08 19:23:26 edited from 1.1 [source]

I tried using Pikchr to document the life cycle of a Fossil ticket in one of my projects. I came up with this:

SUBMIT TICKET New ticket marked "Open" Triage, augment & correct Developer comments Filer responds Reject? Mark ticket "Rejected" & "Resolved" Rejected ticket Reopen? No; fix it Developer changes code Fixed? No Optional: Update ticket resolution: "Partial Fix", etc. Yes Mark ticket "Fixed" & "Closed" Resolved ticket END No Yes
      leftmargin = 1cm
      fill = bisque

      oval "SUBMIT TICKET" width 150%
      down
      arrow 50%
      file "New ticket" "marked \"Open\"" fit
      arrow same
      box "Triage," "augment &" "correct" fit
      arrow same
DC:   box "Developer comments" fit
      arrow same
FR:   box "Filer responds" fit
      arrow 100%
REJ:  box wid 150% invis "Reject?"
      line from last.w to last.n to last.e to last.s close fill bisque behind previous
      right
      arrow 50%
      box "Mark ticket" "\"Rejected\" & \"Resolved\"" fit with .w at previous.e
      arrow right 50%
REJF: file "Rejected ticket" fit
      arrow right 50%
REOP: box wid 150% invis "Reopen?"
      line from last.w to last.n to last.e to last.s close fill bisque behind previous
      down
REJA: arrow 75% from REJ.s
      "No; fix it" at 0.4 right of REJA
CHNG: box "Developer changes code" with .n at last arrow.s fit
      arrow same
FIXD: box wid 150% invis "Fixed?"
      line from last.w to last.n to last.e to last.s close fill bisque behind previous
      right
FNO:  arrow "No" above
RES:  box "Optional:" "Update ticket resolution:" "\"Partial Fix\", etc." fit
      down
      arrow "Yes" aligned above from FIXD.s
      box "Mark ticket" "\"Fixed\" & \"Closed\"" fit
      arrow 50%
      file "Resolved ticket" fit
      arrow same
END:  oval "END"

spline from FR.e right to (DC.e, FR.e) then right 0.25 then up 0.45 then to DC.e ->

spline from RES.e right 0.3 then up 0.75 then to CHNG.e ->

spline from REOP.s "No" aligned above down 0.4
spline from previous.s down to (previous.s, END.n) then to END.e ->

spline from REOP.n "Yes" aligned below up 0.3
spline from previous.n up 0.3 then to FR.e ->

I had the following difficulties in constructing this example:

  1. There is no "diamond" object type,1 needed for decision points in a flowchart, so I'm using a variant of Kupries' simple diamond, which requires repeated "fill" to match the style of the rest of the diagram. Shouldn't "close" apply "fill"?

  2. The fill then requires the "behind previous" bit in order to avoid obscuring the text, which drove me to try macros, but I get syntax errors from the interpreter when I have a label on a "diamond("Foo")" call. Try replacing the 2-line diamond code (no-outline box, followed by 4 lines in a diamond pattern) with a call to the diamond() macro defined at the top of the example. Calling the macro in isolation works fine, but you can't put a label on the call and refer to the macro result's cardinal points.

  3. I've tried hacking around that, but it seems the basic problem is that macros don't define a bounding box, a South property, etc., so you can't say things like "arrow from previous.s to..."

  4. It seems like it should be easier to construct curved arrows, without using splines explicitly.

  5. You see two alternatives for text to the right of an arrow. The first ("No; fix it") has odd spacing, while the second ("Yes" down from "Fixed?") is better but is now sideways. I tried "aligned right", but that just switches the layout direction to "right".

I'm looking for tips to avoid these problems or comments on what it would take to make Pikchr avoid these up-front.


  1. ^ Though true at the time of writing, this changed on 2024.02.08, necessitating this edit.

(2.2) By drh on 2020-10-29 11:58:35 edited from 2.1 in reply to 1.1 [link] [source]

Suggested revision:

/*    1 */        leftmargin = 1cm
/*    2 */        fill = bisque
/*    3 */        linerad = 15px
/*    4 */  
/*    5 */        define diamond { box wid 150% invis
                         ^^^^^^^
ERROR: syntax error

Notes:

  1. Yes, the "close" attribute should make use of the default fill. This has now been fixed. Another bug (that didn't affect you) is that "close" now works even if the line has a radius.

  2. In a line with "close", the text origin is the center of the bounding box for the line. So you can put the text labels on the line, rather that the previous box, and then the "behind previous" is not necessary.

  3. Rather than splines, I used ordinary lines but with a radius of 15px.

  4. For the "No; fix it" text, use the "ljust" attribute to get it to appear to the right of the vertical line. This is how legacy PIC worked, and Pikchr works the same way. I added two initial spaces to the label for spacing.

  5. Rather than everything going in and out of FR.e, I used "0.3<FR.ne,FR.se>" (a point that is 30% of the way from FR.ne to FR.se) and "0.6<FR.ne,FR.se>". That way the lines are separated and it is clearer what is going on.

  6. The "until even with" clause can be very useful. See where I used it in a few places to simplify the layout.

  7. Your "diamond" macro began with a newline. This separated it from its label, which is a syntax error. The trick appears to be to avoid a newline after the opening "{" in the macro definition. Or use a "\" at the start of the macro definition.

(3.1) By Warren Young (wyoung) on 2024-02-08 19:26:00 edited from 3.0 in reply to 2.2 [link] [source]

Thanks! Yes, that's much better, particularly the radiused right-angle lines. The 30% nudge on the lines entering/exiting the "Filer responds" box is also a nice touch.

I've made some further improvements, which you're welcome to overwrite test71 with:

SUBMIT TICKET New bug ticket marked "Open" Triage, augment & correct Developer comments Filer responds Reject? Yes Mark ticket "Rejected" & "Resolved" Rejected ticket Reopen?   No; fix it Developer changes code Fixed? No Optional: Update ticket resolution: "Partial Fix", etc.   Yes Mark ticket "Fixed" & "Closed" Resolved ticket END No Yes
      fill = bisque
      linerad = 15px
      leftmargin = 2cm

      define mydiamond { \
        box wid 150% invis
        line from last.w to last.n to last.e to last.s close rad 0 $1
      }

      oval "SUBMIT TICKET" width 150%
      down
      arrow 50%
NEW:  file "New bug ticket" "marked \"Open\"" fit
      arrow same
      box "Triage," "augment &" "correct" fit
      arrow same
DC:   box "Developer comments" fit
      arrow same
FR:   box "Filer responds" fit
      arrow 100%
REJ:  mydiamond("Reject?")
      right
      arrow 100% "Yes" above
      box "Mark ticket" "\"Rejected\" &" "\"Resolved\"" fit with .w at previous.e
      arrow right 50%
REJF: file "Rejected" "ticket" fit
      arrow right 50%
REOP: mydiamond("Reopen?")
      down
REJA: arrow 75% from REJ.s "  No; fix it" ljust
CHNG: box "Developer changes code" with .n at last arrow.s fit
      arrow 50%
FIXD: mydiamond("Fixed?")
      right
FNO:  arrow "No" above
RES:  box "Optional:" "Update ticket resolution:" "\"Partial Fix\", etc." fit
      down
      arrow 75% "  Yes" ljust from FIXD.s
      box "Mark ticket" "\"Fixed\" & \"Closed\"" fit
      arrow 50%
RESF: file "Resolved ticket" fit
      arrow same
END:  oval "END"

      line from 0.3<FR.ne,FR.se> right even with 0.25 right of DC.e then up even with DC.e then to DC.e ->

      line from NEW.w left 0.5 then down even with REJ.w then to REJ.w ->

      line from RES.e right 0.3 then up even with CHNG.e then to CHNG.e ->

      line from REOP.s "No" aligned above down 0.4
      line from previous.s down to (previous.s, RESF.e) then to RESF.e ->

      line from REOP.n "Yes" aligned below up 0.3
      line from previous.n up even with 0.6<FR.ne,FR.se> then to 0.6<FR.ne,FR.se> ->

New questions:

  1. I couldn't get a label along the new fast-reject path to align sanely. It looks like it's averaging the overall "direction" of the 3-segment line, weighted by line length, so the label ends up tilted relative to the line itself. I believe I can hack around it by inserting an invisible box in the middle of the line to set terminal orientations for alignment, but maybe Pikchr should just handle this better?

  2. The "newline" explanation for my macro expansion problem seems buggy to me. Why would this:

    define foo {
         box $1
    }
    LABEL: foo("Hello!")
    

    not expand to “LABEL: box "Hello!"”?

    The BWK docs don't show multi-line macros, and I don't have a copy of PIC here to test against. Given the date on the earliest description I found of the language, I guess it was first released as part of Unix in System III? My closest local emulator is UNIX V7, which is a couple of years older, 1979.

    The DPIC doc shows multi-line macros in both styles: the first statement cuddled up after the opening brace in one case, but with a newline after the opening brace in all other cases. I didn't find any warning about potential macro expansion problems if you choose the second style.

    Another argument that may sway you is that the current rules violate my expectations from Tcl. Granted, there's no rock-solid reasons the two languages should behave the same in this regard, but I also don't see a good reason for them to differ.

  3. On the question of easier curved lines, your solution is fine, but what I was really hoping for is some kind of "connect foo.e to bar.e without touching any existing object" feature. Solving this is not trivial, but there are many known solutions in the maze solving and circuit board routing literatures.

    My idea is that you could say "line from foo.e to bar.e avoid 0.25" which would temporarily inflate the bounding box of all existing objects except the source and target by a quarter inch, create a half-quarter-inch grid (0.125") and apply one of these algorithms until it found the shortest-path or least-turns-path from one point to the next without crossing the inflated bounding boxes of the other objects.

    The value for the "avoid" parameter is purely esthetic, with larger values giving more "air" in the diagram, more room for corner radiusing, etc.

(EDIT: Renamed "diamond" macro to "mydiamond" to avoid a keyword redefinition syntax error following the addition of diamond objects to Pikchr proper.)

(4) By Warren Young (wyoung) on 2020-10-29 22:40:43 in reply to 3.0 [link] [source]

Problem #1 is now solved thanks to drh's recent addition to the user manual. (The new "fast reject path" label.)

(5) By Andreas Kupries (akupries) on 2021-12-02 14:11:04 in reply to 1.1 [link] [source]

(Very) late reply

but it seems the basic problem is that macros don't define a bounding box, a South property, etc.

That is true. Macros are pure text replacement, they are not objects. Nothing prevents you from writing macros which are partial objects, or attributes without object, etc.

Note however that [ ... ] block syntax does instate a group object enclosing others. So to make a macro of many pieces behave like an object you should be able to use a combination of both, i.e.

define foo { [ ... ] }
...

(6) By Zellyn Hunter (zellyn) on 2022-02-08 17:16:57 in reply to 5 [link] [source]

I've also done:

A1: [ $mymacro() ] at (x,y)

You just have to remember that offsets from (0,0) inside the group won't have any effect.