Skip to content

Shape Specification

Mattia Basaglia edited this page Apr 1, 2021 · 14 revisions

Describing Shapes in DrawShield

Background

DrawShield uses SVG data to render shield images. In the case of charges, the program simply uses an SVG source file and embeds this in the output, changing fills as required. This approach is not ideal for ordinaries and divisions as there are potentially, tens of thousands of variations. Consider that there are around 40 ordinaries, 30 divisions and many of these support 40+ variant edges, and can be fimbriated, with 1, 2 or 3 cottices, and those cottices themselves can have variant edge types...

To bring some order to this we combine two elements to create an ordinary or a division:

  1. A specification of the overall shape, in terms of its edges

  2. A specification of how each variant edge type is drawn along an edge, and how it joins other edges (defined on this page )

A complex function combining these two elements results in an SVG path specification that can then be filled and stroked as required.

Note: There is now a more flexible system to define shapes.

The Shape Environment

Becuase ordinaries and divisions usually(1) occupy the same position on the field of the shield we define a notional region on which they are drawn, which is 1000 units wide by 1200 units tall. The shape may extend beyond the boundaries of thie region but the final shape will be clipped to the field of the shield.

(1) Ordinaries can be enhanced or abased but this is a simple translation in Y.

Shape Specification

A shape is defined by a starting point reprseented by X and Y coordinates and a series of connected edges represented by vectors consisting of a direction and a length. To keep things simple (at the cost of reducing some functionality) vector directions are restricted to multiples of 45 degrees and are represented by a letter from A to H, as shown in the diagram below.

Vector Directions

Note that if you consider the direction of travel then the "inside" of the shape that you want to draw is on the right hand side, the "outside" is on the left hand side. Or you can just recall the diagram above and remember always to work around your shape in a clockwise direction.

Let us consider a very simple example and try to describe a fess, which we want to be horizontal bar 300 units high and positioned vertically in the center of the field - for a plain fess we are aiming for something like:

Plain Fess

(In the diagram the thicker lines are spaced 100 "units" apart, the thinner lines 50 "units" - you can create this for yourself whenever required by specifying the "fake" ordinary grid which can take any plain colour).

So we need to describe the outline of this shape, using the directions from the diagram above, in a clockwise fashion. We can start at any point, I have chosen to start at the top left of the shape, so we start the shape specification with X-100Y350.

WAIT! Why not start at X0? This is related to the fimbriation of ordinaries, where they are separated from the field by a coloured edge. This is achieved in SVG by using the "stroke" attribute set to a reasonable width and to the desired colour. But recall that fimbriation separates the shape from the field, not the edge of the shield. I.e. we only want to see fimbriation on the top and bottom edges. SVG doesn't support different strokes on path elements so we cheat and put the ends outside of the field. They are still given a stroke but since the final shape is clipped to the shape of the shield we don't see them, and get the proper fimbriation we require. You will find that most ordinaries use this trick and hence extend beyond the boundaries of the field.

So we have our start point at X-100Y350, we then need to use our vectors to describe our route around the shape. This is quite straightforward, looking at our vector diagram we see that we need to go in direction "A" for 1200 units (100 to the edge of the field, 1000 across the field and 100 beyond the further edge). Then in direction "c" for 300 units, "E" for 1200 and finally "d" for 300 units brings us back to the start point. You can see the complete route in the diagram below.

Fess Shape Specification example

Thus our shape specification for a fess is X-100Y350A1200c300E1200d300

A couple of obvious questions:

  1. Do we have to always end at the start point? No - the final path is automatically closed, but it is good practice to provide a closed shape specification anyway.

  2. Why are some of the vector directions given in lower case? Because they are clipped away, we never see them, hence there is no point applying a variant edge to them, e.g. if the fess is wavy we only need to draw the wavy edge on top and bottom, not on the ends, so we specify those in lower case. Lower case vectors are always just drawn with straight path segments.

Height substitution

There is a further improvement that we can make to the fess, which is to take into account the size of the field it is being placed on. The numers above work well on most of the shield shapes, but with flags we need to adjust the height and position of the fess so that it remains centered in the flag and adapts to the available height. This may also useful in other situations.

Instead of a fixed number after the vector direction we can instead specify that we want to use some percentage of the currently available height. This is done by using the construction {%nn} where nn is whatever percentage we want to use. For the fess example above we would modify this to:

X-100Y{%35}A1200c{%30}E1200g{%30}

All shields are assumed to be 1000 units high (to leave space at the bottom for the pointy bit), so on a shield this produces exactly the same result as the version with fixed numbers (35% of 100 is 350, 30% is 300). However, on a flag of, for example, ratio 1:2, the available height is now 500 units. The height substitution gives a Y starting point of 175 and a fess height of 150 units, which is exactly what we want.

There are also some flags for special purposes but these are pretty obscure and you shouldn't really need them so I have discussed them in an appendix only.

Conclusion

This may seem like quite a long-winded way to produce what, in most cases will just be simple rectangular shape, however consider that by the use of the functions below that shape specification allows us to produce:

  • A fess with any of 40+ variant edge types
  • A fess voided, optionally with any of 40+ variant edge types on the outside
  • Up to 3 cottices parallel to the outside of the shape, each cotticing optionally have a different variant edge type
  • To draw this shape with the correct proportions on a flag with any aspect ratio

I would argue that this apparent complexity is a small price to pay for a very flexible and versatile result.

If all you want to do is create more shapes that can be given variant edge types then you can stop reading here. If you want the gory details of how those variant edges are actually drawn then see the page on linetype variants specification

Appendices

1 - Limitations and Issues

Most obviously, the Shape Specification can only be used to create shapes where the lines are at angle which are multiples of 45 degrees. This is actually a limitation of the code that applies the linetype specifications NOT this shape specification format, which could easily be extended to handle, for example Rnn,ddd where nn is the angle in degrees of the vector and ddd is the distance.

At the moment the only "workaround" is to not use the shape specification for ordinaries and divisions that do not use 45 degree angles, for example the pile. These are just specified directly in the code as SVG paths.

Secondly, this specification does NOT support curves, or more generally, putting variant linetypes along an arbitrary path which includes arcs or bezier curves. So for example it cannot be used for bordures, which must follow the curved edges of the shield. The mathematics of doing this are somewhat beyond my current capabilities, 😕 .

There is a crude workaround in which the program will check for the existence of a "pre-constructed" shape in svg/ordinaries/bordures. So if the user is using a heater shaped shield and asks for a bordure engrailed then the program will look in that folder for a file called `heater-engrailed.svg' and use that. Sadly, there are about 15 current shield shapes and at least 10 commonly found edges on bordures so that is 150 variations. And if we are being strictly heraldic, bordures do below the chief if one is present, which is a different shield shape, impaled shields are different shapes also; and thus there are at least 450 drawings to be able to cover the most common eventualities. To date I have done, err, about 8. 😬

2 - Additional Flags

The following flags are available, they can be appear anywhere in the shape specification but must be followed by a number, just like other parts of the shape specification

  • Z - value 0 means do NOT automatically close the path, 1 means close path (default)
  • U - value 1 means override the limitation to only draw on upper edges, all edges are drawn (for the chief embattled)
  • O - value 1 means don't do an offset. I have no idea why...
  • Q - value 1 means this is a quarterly style division, so rotate features appropriately
  • V - value 1 means this is chevron style ordinary or division, so rotate features appropriately

(To be honest I can't remember exactly how the last two work either but they are needed in these limited circumstances, so leave them as they are - to see the effects look at quarterly angled and per chevron angled as examples)

3 - Related Function Calls

These are functions that implement the Shape Specification functionality.

makePath2 ( $shapeSpec, $lineType, $size = 60 ) takes a shape specification as a string, the name of the linetype and optional size and returns a guaranteed valid value that can be placed directly in the "d" attribute of an SVG path element. This is main use of Shape Specifications, returning any of the 70+ major oridinaries and divisions with any of 30+ edge variations, and takes into account flag heights. The horrible, horrible code that does is all contained in linetypes.inc.

makeVoid ( $spec, $lineType = 'none', $featureSize = 50, $offsetSize = 30 ) like makePath2, returns a "d" value but with the inside "cut away" to create a voided version of the shape. offsetSize is basically how "thick" the remaining part of the original shape is. The inside edge of a voided shape is always straight, the outside edge will be drawn with the specified lineType.

createCotise($spec, $num, $lineType) Given a shape spec create either a first, second or third cottice, i.e. an "outer" shape like a voided, enlarged version of the shape specification, drawn with any variant edge type (which can different to that of the main shape, and to any other cottice). In the interest of confusion, this function actually returns a complete path element.

These last two functions live in ordinaries.inc as they are mainly used on ordinaries.