# Tailoring Penrose domains to your needs @rjainrjain

Or, a tour of a simple domain for directed graphs and how to use Penrose's Domain and Style languages to extend it.

To facilitate creating diagrams quickly, Penrose has many existing Domain and Style programs, which you may want to tailor to fit your own diagramming needs. We walk through an example of such modification by extending the simple-directed-graph domain to support highlighting specific vertices and arcs, as seen in the difference between the diagrams below.

## Directed graph domain: just vertices and arcs ​

The simplest Domain schema that describes a directed graph, composed of vertices and directed edges (or, arcs) can be expressed in the following manner:

domain
type Vertex
predicate Arc(Vertex a, Vertex b)

Given a Vertex type and an Arc predicate, we can now create vertices and draw arcs between them in the Substance language. Let's write a Substance program in this domain; one, for example, depicting an intricate Hamiltonian graph (which is a graph that contains a cycle that hits each vertex exactly once before returning to the start node). The Substance code could be written as follows:

substance
Vertex a, b, c, d, e, f, g

-- Hamiltonian cycle
Arc(a, b)
Arc(b, c)
Arc(c, d)
Arc(d, e)
Arc(e, f)
Arc(f, g)
Arc(g, a)

-- Additional edges that are not in the Hamiltonian cycle
Arc(a, c)
Arc(a, d)
Arc(b, e)
Arc(b, f)
Arc(c, g)
Arc(d, f)

Here's what the resulting diagram should look like:

## Extending the Domain and understanding the Style ​

In the graph shown above, it's rather unclear where exactly the Hamiltonian cycle is, so we might want to highlight its path. This would mean highlighting certain vertices and arcs. Let's therefore add predicates to the Domain which allow us to do so.

domain
type Vertex
predicate Arc(Vertex a, Vertex b)
predicate HighlightVertex(Vertex a)
predicate HighlightArc(Vertex a, Vertex b) 

This addition defines two new predicates which take existing vertices and (vertices which form) arcs and highlights them. Now we need to describe how to display this relation in a diagram – in this case, highlighting would involve changing the color of the vertices and nodes. We can inspect the Style program to identify which parts we need to add on to or otherwise modify. The following excerpt is from the simple-directed-graph Style program:

style
forall Vertex v {
v.dot = Circle {
center: (? in dots, ? in dots)
fillColor : color.black
}

v.text = Text {
string: v.label
fillColor: color.black
fontFamily: "serif"
fontSize: "18px"
strokeColor: color.white
strokeWidth: 4
paintOrder: "stroke"
}
v.halfSize = (v.text.width / 2, v.text.height / 2)
v.bottomLeft = v.text.center - v.halfSize
v.topRight = v.text.center + v.halfSize

v.text above v.dot

encourage shapeDistance(v.dot, v.text) == num.labelDist in text
}

For a vertex, we need to change the fillColor of both the actual dot and the text.

And the relevant portion of the Style program for arcs is below:

style
forall Vertex u; Vertex v where Arc(u, v) as e {
a = u.dot.center
b = v.dot.center
t = normalize(b - a) -- tangent
n = rot90(t) -- normal
m = (a + b) / 2 -- midpoint

e.start = a
e.end = b
e.offset = ? in dots
e.arrow = Path {
d: quadraticCurveFromPoints("open", [a, m + e.offset * n, b])
strokeColor: color.black
}

e.step = ? in arrows
e.pointerCenter = m + (e.offset / 2) * n + e.step * t
p = e.pointerCenter
x = num.pointerX
y = num.pointerY
e.pointer = Path {
d: pathFromPoints("closed", [p - x * t + y * n, p + x * t, p - x * t - y * n])
strokeColor: none()
fillColor: color.black
}

e.arrow below u.dot
e.arrow below v.dot
e.pointer below e.arrow

encourage vdist(u.dot.center, v.dot.center) < num.edgeDist in dots
encourage minimal(sqr(e.offset)) in dots
encourage minimal(sqr(e.step))
}

We need to modify the strokeColor of the arrow and the fillColor of the pointer.

## Modifying the Style ​

Given this knowledge, we can modify the Style program to fit our needs. First, we may find it useful to define the new color signifying a highlighted vertex or arc, so we add redOrange to the color object in the Style program:

style
color {
black = #000000
white = #ffffff
redOrange = #FE4A49 -- added for highlighting
}

Then, we match on the specific cases of Vertex and Arc with predicates HighlightVertex and HighlightArc, as shown below.

style
forall Vertex v where HighlightVertex(v) {
override v.dot.fillColor = color.redOrange
override v.text.fillColor = color.redOrange
}

forall Vertex a, b where Arc(a,b) as e; HighlightArc(a,b) {
override e.arrow.strokeColor = color.redOrange
override e.pointer.fillColor = color.redOrange
}

Where there is a Vertex and a call to the HighlightVertex predicate, we override the fillColor of its components; we perform the corresponding extensions for Arc and HighlightArc.

## The final outcome! ​

Now we can add to the Substance program to display the highlighting we wanted:

substance
-- Highlight start/end vertex of cycle
HighlightVertex(a)

-- Highlight all arcs in cycle
HighlightArc(a, b)
HighlightArc(b, c)
HighlightArc(c, d)
HighlightArc(d, e)
HighlightArc(e, f)
HighlightArc(f, g)
HighlightArc(g, a)

Ultimately, the highlighted diagram looks like this:

In this process, we've modified the simple-directed-graph Domain and Style programs to add new capabilities. You can see this in action in the Hamiltonian graph example in the Penrose gallery. We hope this helps and inspires you to modify the Domain and Style programs of Penrose's existing domains to fit your own diagramming needs!