Core Shapes =========== .. |dash| unicode:: U+2014 .. EM DASH SIGN .. |copy| unicode:: U+00A9 .. COPYRIGHT SIGN :trim: .. |deg| unicode:: U+00B0 .. DEGREE SIGN :ltrim: .. |br| raw:: html
These descriptions of the available shapes assume you are familiar with the concepts, terms and ideas for :doc:`protograf ` as presented in the :doc:`Basic Concepts ` - especially *units*, *properties* and *defaults*. It will also help to at least browse through the section on :doc:`Additional Concepts `. .. _table-of-contents-core: - `Shape Index`_ - `Overview`_ - `Commonalities`_ - `Linear Shapes`_ - `Enclosed Shapes`_ - `Compound Shapes`_ - `Shapes Common Properties`_ .. _shape-index: Shape Index ----------- - `Arc`_ - `Arrow`_ - `Blueprint`_ - `Bezier`_ - `Circle`_ - `Chord`_ - `Cross`_ - `Dot`_ - `DotGrid`_ - `Ellipse`_ - `Grid`_ - `Hexagon`_ - `Hexagons`_ - `Image`_ - `Line`_ - `Lines`_ - `Pod`_ - `Polygon`_ - `Polyline `_ - `Polyshape`_ - `QRCode`_ - `Rectangle`_ - `Rectangles`_ - `Rhombus`_ - `Sector`_ - `Square`_ - `Stadium`_ - `Star`_ - `Starfield`_ - `Table`_ - `Text`_ - `Trapezoid`_ - `Triangle`_ Overview -------- `↑ `_ Where possible, the basic examples first show how a shape would appear on a page when **only** the default properties are used. This means that, for most cases, that *lines* are drawn in **black** and shapes that have an enclosed area are *filled* with a **white** color. The default length, width or height in most cases is **1 cm**. The main change from default, for these examples, has been to make the default line width (*stroke_width*) thicker for easier viewing of the small PNG images that have been generated from the original PDF output. Most shapes can be styled by setting one or more of the `Shapes Common Properties`_. Other shapes have additional properties available that allow even further styling. To make it easier to see where and how a shape has been drawn, most of these examples have been created with a background grid, which **protograf** refers to as a `Blueprint`_ shape, added to the page |dash| a small A8 "business card" size |dash| for cross-reference. .. NOTE:: The graphics for these examples were generated from either of the following scripts; `default_shapes `_ or `customised_shapes `_. **protograf** first creates a PDF, then generates a PNG file for each page in the PDF. Commonalities -------------- `↑ `_ There are some properties that can be set for almost all of the shapes; examples of these are presented in the section on `Shapes Common Properties`_ at the end, rather than being described in detail for every single shape. .. HINT:: Bear in mind that if a property that it does **not** support is provided for a shape, then that property and its value will simply be ignored. .. _linearIndex: Linear Shapes -------------- `↑ `_ .. _arc-command: Arc ~~~ `↑ `_ An Arc is a curved line between two points located along the circumference of a circle. Example 1. Default Arc ++++++++++++++++++++++ .. |arc| image:: images/defaults/arc.png :width: 330 ===== ====== |arc| This example shows the shape constructed using the command with only defaults: .. code:: python Arc() It has the following properties based on the defaults: - origin is at x-position ``1`` cm and at y-position ``1`` cm ===== ====== Example 2. Customised Arc +++++++++++++++++++++++++ .. |ac2| image:: images/customised/arc.png :width: 330 ===== ====== |ac2| This example shows the shape constructed using the command with these properties: .. code:: python Arc(cx=1, cy=3, radius=2) Arc(cx=1, cy=6, radius=2, nested=6, angle_start=15, angle_width=60) To help with visualisation, the top Arc is surrounded by a red Rectangle: .. code:: python Rectangle( x=1, y=1, height=1, width=2, dot=0.02, stroke="red", fill=None, title="Arc(cx=1, cy=3, radius=2)") ) The top Arc has the following properties: - origin is at x-position ``1`` cm and y-position ``3`` cm - the arc *radius* is ``2`` cm The default arc extent is from 0 |deg| (the line parallel to the top edge of the page) to 90 |deg| (the line parallel to the side edges of the page). The lower Arc has the following properties: - origin is at x-position ``1`` cm and y-position ``6`` cm - the arc *radius* is ``2`` cm - the arc extends from 15 |deg| to 75 |deg| (ie. *angle_start* of 15 |deg| + *angle_width* of 60 |deg|) - there are ``6`` *nested* arcs equally spaced between the arc origin position and the arc itself. ===== ====== .. _bezier-command: Bezier ~~~~~~ `↑ `_ A Bezier is a curve that has inflection points, allowing it to "bend". Example 1. Default Bezier +++++++++++++++++++++++++ .. |bez| image:: images/defaults/bezier.png :width: 330 ===== ====== |bez| This example shows the shape constructed using the command with only defaults: .. code:: python Bezier() It has the following properties based on the defaults: - starts at x-position ``1`` cm and at y-position ``1`` cm ===== ====== Example 2. Customised Bezier ++++++++++++++++++++++++++++ .. |bz1| image:: images/customised/bezier_custom.png :width: 330 ===== ====== |bz1| This example shows the shape constructed using the command with the following properties: .. code:: python Bezier( x=0, y=1, x1=4, y1=3, x2=3, y2=4, x3=4, y3=6, stroke_width=1) It has the following properties based on changes to the defaults: - starts at x-position ``0`` cm and at y-position ``1`` cm - has the inflection points set by: - *x1* and *y1*, and - *x2* and *y2* - ends at position *x3* of ``4`` cm and at *y3* of ``6`` cm - has a thicker *stroke_width* ===== ====== .. _chord-command: Chord ~~~~~ `↑ `_ A chord is a straight line joining two points on a circle's diameter. Example 1. Customised Chord +++++++++++++++++++++++++++ .. |chd| image:: images/defaults/chord.png :width: 330 ===== ====== |chd| If the shape constructed using only default properties, there will be nothing to see: .. code:: python Chord() This example then shows the shape constructed using the command with these properties: .. code:: python Chord( shape=Circle(radius=1, fill=None), angle=135, angle1=45) It has the following properties based on these values: - a small circle that defines boundaries for the chord line - the start of chord is at the intersection of the radius of the circle at 135 |deg| with the circle's circumference - the end of chord is at the intersection of the radius of the circle at 45 |deg| with the circle's circumference ===== ====== .. _cross-command: Cross ~~~~~ `↑ `_ A Cross shape is two thick bars that cross each other at 90 |deg|. The vertical bar is termed the "body" and the horizontal bar is the "arm". In addition to the normal, common properties, the Cross also has: - *thickness*: this is the width of the bars. The default value for this is one-fifth of the overall width. - *arm_fraction*: this is the fraction **along** (up or down) the length of the body at which the arm crosses it. The default value for this is ``0.5`` i.e. half-way along. .. NOTE:: Unlike most other shapes with a centre, the Cross uses as its centre the middle point of the arm of the cross |dash| rather a centre based on the overall height and width. Example 1. Default Cross ++++++++++++++++++++++++ .. |cr1| image:: images/defaults/cross.png :width: 330 ===== ====== |cr1| This example shows the shape constructed using the command with only defaults: .. code:: python Cross() It has the following properties based on the defaults: - top-left is at x-position ``1`` cm and at y-position ``1`` cm - default height and width of ``1`` cm - default bar thickness is one-fifth of the width ===== ====== Example 2. Customised Cross +++++++++++++++++++++++++++ .. |cr2| image:: images/customised/cross.png :width: 330 ===== ====== |cr2| This example shows the Cross constructed using the command with thes properties shown. Note the use of the :ref:`Common command ` to allow multiple Crosses to share the same properties. .. code:: python crs = Common( height=1.8, width=1.2, arm_fraction=0.70) Cross( stroke_width=1, stroke="red", fill="gold") Cross( cx=3, cy=1, thickness=0.33, fill_stroke="red") Cross( common=crs, cx=1, cy=3) Cross( common=crs, cx=3, cy=2.5, title="Title", label="Label", heading="Heading") Cross( common=crs, cx=3, cy=5, dot=0.1, cross=0.5) Cross( common=crs, cx=1, cy=5, height=1.8, rotation=45) The top-left example shows a default-sized cross with different *fill* and *stroke* colors, as well a thicker *stroke_width*. The top-right example shows a cross with matching *fill* and *stroke* colors. It also changes the default size of the bar for the arm and body to have a *thickness* of ``0.33`` cm. The default is one-fifth of the overall width. The lower four examples all share a common height and width. They also use the *arm_fraction* property. This is the fraction up the length of the body at which the arm crosses it; by default this is ``0.5`` (half-way up). All these examples, as well as the top-right example "centre" the cross; in this case the centre corresponds to the middle point of the arm of the cross. The *label* and *rotation* properties are also based on the middle point of the arm of the cross. ===== ====== .. _dot-command: Dot ~~~ `↑ `_ A Dot shape is essentially a very small, pre-filled `Circle`_. Example 1. Default Dot ++++++++++++++++++++++ .. |dot| image:: images/defaults/dot.png :width: 330 ===== ====== |dot| This example shows the shape constructed using the command with only defaults: .. code:: python Dot() It has the following properties based on the defaults: - top-left at x-position ``1`` cm and at y-position ``1`` cm - diameter of ``3`` points; there are 72 points in an inch, so this is 1/24th of an inch, or approximately ``1`` mm (``0.1`` cm), in size - fill color for a Dot is the same as the stroke |dash| default is black The default diameter for a Dot can be changed by setting its *dot_width* which, like *stroke_width* for Text, is in point units. ===== ====== .. _line-command: Line ~~~~ `↑ `_ .. NOTE:: There is more detail about the many properties that can be defined for a Line in the :ref:`customised Line ` section. Example 1. Default Line +++++++++++++++++++++++ .. |ln1| image:: images/defaults/line.png :width: 330 ===== ====== |ln1| This example shows the shape constructed using the command with only defaults: .. code:: python Line() It has the following properties based on the defaults: - starts at x-position ``1`` cm and at y-position ``1`` cm - length of ``1`` cm - heading/default direction is 0 |deg| |dash| i.e. "eastwards" *Note* that direction means "anti-clockwise from 0 |deg|", where the zero lines runs in the "east" direction from the left. ===== ====== .. _polyline-command: Polyline ~~~~~~~~ `↑ `_ A Polyline is a series of one or more lines joining two or more points. In addition to setting points directly, the Polyline can be constructed using the *steps* property. This define a series of values that represent the x- and y-values **relative** to the last point drawn. A Polyline can also be constructed using the *snail* property. This defines a series of relative distances, plus optional rotational and/or facing directions, that allow a sequence of lines and curves to be drawn. The following examples illustrate these properties: - `Example 1. Basic Polyline`_ - `Example 2. Customised Polyline`_ - `Example 3. Polyline with Arrow`_ - `Example 4. Polyline with Snail`_ - `Example 5. Polyline with Snail Curves`_ Example 1. Basic Polyline +++++++++++++++++++++++++ `^ `_ .. |py1| image:: images/defaults/polyline.png :width: 330 ===== ====== |py1| The shape cannot be constructed using only default properties: .. code:: python Polyline() Nothing will be visible; instead you will see a warning:: WARNING:: There are no points to draw the Polyline The upper example then shows the shape constructed using the command with these properties: .. code:: python Polyline(points=[(0, 0), (1, 1), (2, 0)]) It has the following properties based on these values: - starts at x-position ``0`` cm and at y-position ``0`` cm - second point is at x-position ``1`` cm and at y-position ``1`` cm - third point is at x-position ``2`` cm and at y-position ``0`` cm The *points* for a Polyline are in a list, as shown by the square brackets from ``[`` to ``]``, where: - each *x* and *y* are provided as a pair of values in round brackets - each *x* and *y* are separated by a comma - each pair of values in the list is separated by a comma ===== ====== Example 2. Customised Polyline ++++++++++++++++++++++++++++++ `^ `_ .. |py2| image:: images/customised/polyline_basic.png :width: 330 ===== ====== |py2| The upper example shows the shape constructed using the command with these properties: .. code:: python Polyline( points=[(1, 2), (1, 1), (2, 0), (3, 1), (3, 2)], stroke_width=1, stroke="red") Here the points are arranged so as to create a basic 'house' outline. The lower example also shows how to create a Polyline using the command with these properties: .. code:: python Polyline( x=1, y=3, stroke_width=1, steps='0.5,0 0,1.5 1.5,0 0,-1.5 0.5,0 0,0.5 -2.5,0') Here, the *steps* property results in the drawing of an outline using a series of distances |dash| or offsets |dash| from the last point. The start is provided by the *x* and *y* values. Each pair of comma-separated values are x- and y-distances respectively. ===== ====== Example 3. Polyline with Arrow ++++++++++++++++++++++++++++++ `^ `_ .. |py3| image:: images/customised/polyline_arrow.png :width: 330 ===== ====== |py3| The shape is constructed with these properties: .. code:: python Polyline( points=[(1,3), (2,4), (2.5,2), (3,3), (3.5,1)], stroke_width=1, arrow=True ) Polyline( points=[(1,5), (3,5)], stroke_width=1, dotted=True, arrow_style='notch', arrow_double=True ) This example makes use of the "arrow" properties available for a line. For more details on how arrows are used and set, see the :ref:`Line with Arrow ` example. ===== ====== Example 4. Polyline with Snail ++++++++++++++++++++++++++++++ `^ `_ The *snail* property is loosely based on the concept and approach of the Turtle graphics drawing module available for Python (see: https://docs.python.org/3/library/turtle.html). Instead of using points, the idea of the *snail* is to create a Polyline based on a series of lines of given length, where the line direction |dash| or orientation |dash| will already have been set. Each line is then drawn starting from the end point of the previous line. A *snail* property consists of a series of terms, each separated by a space. Each term either relates to a **direction** change or to drawing a line of a certain **length** Directions can be set as follows: - a *compass direction*: one of n, e, w, s, ne, se, sw, or nw - an **absolute** *angle*: an ``a`` followed by a value in degrees, from 0 to 360, measured counter-clockwise from the east direction - a **relative** angle: - a ``r`` or ``-`` sign (followed by a value in degrees): will *decrease* the current angle i.e. alter it in a clockwise direction - a ``l`` or ``+`` sign (followed by a value in degrees): will *increase* the current angle i.e. alter it in an anti-clockwise direction Creating a line is done as follows: - a normal value |dash| whole or fractional |dash| will draw a line that distance, in the last direction that was set - using a pair of asterixes (``**``) ` will draw a line from the current point back to the start Moving - **without** creating a line - is done as follows: - a **relative** move: a ``j`` followed by a value which is the distance, at which the new point will be set |dash| according to the last direction that was set; no line will be drawn between the points - a **fixed point** move: a single asterix (``*``) will set the next, new point to match the one at the start; no line will be drawn between the points .. NOTE:: The *snail* line always starts at the x- and y-point defined for the Polyline; and the starting direction is "e" or 0 |deg|. The first term in the *snail* property can either be a direction or a distance. .. raw:: html Apologies to users for using "jump" as the term to cause a move to happen without drawing a line; the idea of a snail jumping was just too absurd **not** to use! .. |py4| image:: images/customised/polyline_snail.png :width: 330 ===== ====== |py4| The Polyline shape is constructed with these properties: .. code:: python snail_line = "n 3 e 2 -45 2 w 1 sw 3 **" Polyline( y=0.5, snail="2 s 1 w 2 n 1", stroke_width=1, stroke="red", arrow=True) Polyline( x=0, y=5, snail=snail_line, stroke_width=1) Polyline( x=0, y=5, snail=snail_line, stroke_width=1, scaling=0.5) Polyline( x=3.5, y=1, snail="s 0.4 j0.1 "*8, stroke_width=1, stroke="green") Polyline( y=3, x=2, snail="e 1 s 1 w 1 n 1 s j1 "*3, stroke_width=2, stroke="blue") The top example is a simple red line going in a square. It starts by going east |dash| the default direction |dash| for ``2cm`` then south for ``1cm``, west for ``2cm`` and finally north for ``0.5cm``. The *arrow* property is set to ``True`` so a default size arrowhead is drawn at the end of the line. The two examples with the black lines share the same *snail* line, assigned to ``snail_line``. This example shows the use of the ``-`` to represent turning right, in this case by 45 |deg|. It also shows how the ``**`` term constructs a line back to the start. The "smaller" example is constructed using the *scaling* fraction |dash| in this case set to ``0.5`` to make it half the size. Note that both lines have the same starting point. The example with the green line shows how a dotted line can be constructed using the "jump" (``j``) term to create a space in the same direction as the drawn line. The ``*8`` then repeats the snail line eight times |dash| actually, the full string get constructed by joining eight copies of this same string, so that is why a space is needed at the end. The example with the blue line shows how a shape |dash| in this case a square |dash| is constructed multiple times using the `*3`` to repeat the outline three times. Again, the "jump" (``j``) term is used to move to a different location before the next shape is drawn. Note that in this case the start direction is set explicitly, rather than just using the default! ===== ====== Example 5. Polyline with Snail Curves +++++++++++++++++++++++++++++++++++++ `^ `_ In addition to all the options for *snail* as described in `Example 4. Polyline with Snail`_, there is also the option to draw **curves** as part of the line. A curve is specified by a series of three values, enclosed in round brackets; for example ``(1.118 60 1)``. These values are as follows: - the first value is the distance to the curve's *inflection point*; - the second value is the angle to the curve's *inflection point*; - the third value is the distance to the curve's end-point. So, the curve is drawn from the current point on the line, to a second point whose location is determined by a combination of the distance |dash| specified by the third value |dash| and the "current" direction, determined at the time just before the curve is drawn. The curve will then bend, based on the location of the *inflection point* |dash| note that the curve does **not** go to that inflection point, but is rather "pulled" towards it to form the bend. .. |py5| image:: images/customised/polyline_snail_curve.png :width: 330 ===== ====== |py5| The Polyline shape is constructed with these properties: .. code:: python Polyline( y=1, x=0, snail="(1.118 60 1) (1.118 -60 1) "*2, stroke_width=1, stroke="red") Polyline( y=2, x=0, snail="(1.118 60 1) (1.118 -60 1) "*8, scaling=0.25, stroke_width=1, stroke="red") Polyline( y=4, x=0, snail="a45 (0.6 60 0.707) e 0.5 "*3, stroke_width=1, stroke="red") Polyline( y=5, x=2, snail="r60 (1.118 r60 1) * "*6, stroke_width=1, stroke="red") All these examples show repetion in effect; the same basic set of lines repeated multiple times. Again, note that a space is needed at the end of the snail's text string in order to support this repeat. The first three examples use absolute angles for the curve |dash| whether positive or negative |dash| but the last example uses a relative angle, so that the curve, starting each time from the home location, changes its orientation because the "current direction" is constantly increasing. ===== ====== .. _text-command: Text ~~~~ `↑ `_ It may seem strange to view text as a "shape" but, from a drawing point of view, it's really just a series of complex lines drawn in a particular pattern! Thus text has a position in common with many other shapes, along with *stroke* to set its line color, as well as its own special properties. The basic properties that can be set are: - *text* - the text string to be displayed - *font_size* - default is ``12`` points - *font_name* - the default is ``Helvetica`` - *stroke* - the default text color is ``black`` - *align* - the default alignment is ``centre``; it can be changed to be ``left`` or ``right`` .. NOTE:: There is more detail about the various properties that can be defined for Text in the :ref:`customised text ` doc. Example 1. Default Text +++++++++++++++++++++++ .. |t01| image:: images/defaults/text.png :width: 330 ===== ====== |t01| This example shows the shape constructed using the command with mostly defaults. Only the *text* property is changed from a blank string |dash| otherwise there would nothing to see! .. code:: python Text(text="Hello World") It otherwise has the following properties based on the defaults: - located is at x-position ``1`` cm and at y-position ``1`` cm - text is at the ``center`` of the position - default *font_size* is ``12`` points - default *font_name* is ``Helvetica`` ===== ====== Enclosed Shapes --------------- `↑ `_ These shapes are created by enclosing an area, the most basic being a simple rectangle. They effectively have two dimensions: *height* and *width*. The difference between enclosed and linear shapes is that the area enclosed by the shape can be filled with a color. The default fill color is *white*. There is an overview on how color is used in the :doc:`Basic Concepts section ` .. HINT::: **protograf** comes with a predefined set of named colors, shown in the `colors `_ PDF file. .. _arrow-command: Arrow ~~~~~~ `↑ `_ An Arrow consists of two main parts: the tail (or body) and the head. In terms of **protograf** conventions, the tail is the part that takes on the common properties of *height* and *width*; while the dimensions for the head, if not provided, are calculated from those. Example 1. Default Arrow ++++++++++++++++++++++++ .. |ar0| image:: images/defaults/arrow.png :width: 330 ===== ====== |ar0| This example shows the shape constructed using the command with only defaults: .. code:: python Arrow() It has the following properties based on the defaults: - centre-bottom point at x-position ``1`` cm and at y-position ``1`` cm - *height* of the tail portion of ``1`` cm - *head_height* of the head portion of ``1`` cm (based on the *height*) - *head_width* of the head portion of ``2`` cm; the maximum distance between the two arrowhead "wingtips" - for which the default value is calculated as equal to twice the *width* ===== ====== Example 2. Rotated Arrow ++++++++++++++++++++++++ .. |ar1| image:: images/customised/arrow_rotate.png :width: 330 ===== ====== |ar1| This example shows the shape constructed using the commands as follows: .. code:: python Arrow( x=1, y=5.5, title="The Arrow", heading="An arrow", dot=0.1, cross=0.5) Arrow( x=2.5, y=3, title="0\u00B0", dot=0.15, dotted=True) Arrow( x=2.5, y=3, title="45\u00B0", dot=0.1, dot_stroke="red", fill=None, stroke="red", rotation=45) Arrow( x=3, y=5.5, label="arrow") The shapes all set the following properties: - centre-bottom point at *x* and *y* - *title* - appears below the shape - *dot* - small, filled circle |dash| **centre** of the Arrow The lower-left Arrow also sets the following properties: - *heading* - appears above the shape - *cross* - small pair of lines at the Arrow's centre The lower-right Arrow also sets the following properties: - *label* - appears in the middle of the shape The two arrows in the top-right are superimposed. The red outline Arrow shares the same centre as the black dotted Arrow before/below it. The red arrow is rotated 45 |deg| to the left about the centre. .. NOTE:: The degrees sign is a Unicode character i.e. a "\\u" followed by four numbers and/or letters. For access to full Unicode lists as well as the option to search for characters by name, see: https://www.compart.com/en/unicode/plane/U+0000 ===== ====== Example 3. Styled Arrow +++++++++++++++++++++++ .. |ar2| image:: images/customised/arrow_sizes.png :width: 330 ===== ====== |ar2| This example shows the shape constructed using the commands as follows: .. code:: python Arrow( x=1, y=5, height=1, width=0.5, head_height=0.5, head_width=0.75) Arrow( x=2, y=5, height=1, width=0.5, head_height=0.5, head_width=0.75, tail_width=0.75, stroke="tomato", fill="lightsteelblue", stroke_width=2, transparency=50) Arrow( x=3, y=5, height=1, width=0.5, head_height=0.5, head_width=0.75, tail_width=0.01, fill_stroke="gold") Arrow( x=1, y=3, height=1, width=0.25, head_height=0.5, head_width=1, points_offset=-0.25, fill="chartreuse") Arrow( x=2, y=3, height=1, width=0.25, head_height=1, head_width=0.75, points_offset=0.25, fill="tomato") Arrow( x=3, y=3, height=1, width=0.5, head_height=0.5, head_width=0.5, tail_notch=0.25, stroke="black", fill="cyan", stroke_width=1) The shapes all set the following properties: - centre-bottom point at *x* and *y* - *height* of the tail portion (``1`` cm for all) - *width* of the tail portion - *head_height* sets height of the head (triangular) portion - *head_width* sets width of the head (triangular) portion The *head_width* represents the maximum distance between the outer arrowhead "wingtips". The **silver** arrow has these properties: - *tail_width* of ``0.75`` cm - *transparency* - set to ``50`` %; the grid is partly visible through it The smaller *tail_width* means the base of the arrow is wider than the body i.e. the width at the top of the tail section. The **gold** arrow has these properties: - *tail_width* of ``0.01`` cm The near-zero *tail_width* means the base of the arrow is nearly shown as a point. The **green** (``chartreuse`` fill) arrow has these properties: - *points_offset* of ``-0.25`` cm The *points_offset* here means that the two "wingtips" of the arrowhead are moved back towards the tail. The **red** (``tomato`` fill) arrow has these properties: - *points_offset* of ``0.25`` cm; The *points_offset* here means that the two "wingtips" of the arrowhead are moved forwards away from the tail. In this case, the head has been been made narrower and longer. The **blue** (``cyan`` fill) arrow has these properties: - *tail_notch* of ``0.25`` cm; the base has a small inwards-facing triangle "cut out" The blue arrow also has matching *width* and *head_width* (of ``0.5`` cm) which means that there are no visible arrowhead "wingtips". ===== ====== .. _circle-command: Circle ~~~~~~ `↑ `_ .. NOTE:: There is more detail about the many properties that can be defined for a Circle in the :ref:`customised Circles ` section. Example 1. Default Circle +++++++++++++++++++++++++ .. |ccl| image:: images/defaults/circle.png :width: 330 ===== ====== |ccl| This example shows the shape constructed using the command with only defaults: .. code:: python Circle() It has the following properties based on the defaults: - upper-left "corner" at x-position ``1`` cm and at y-position ``1`` cm - diameter of ``1`` cm ===== ====== .. _ellipse-command: Ellipse ~~~~~~~ `↑ `_ Example 1. Default Ellipse ++++++++++++++++++++++++++ .. |ell| image:: images/defaults/ellipse.png :width: 330 ===== ====== |ell| This example shows the shape constructed using the command with only defaults: .. code:: python Ellipse() It has the following properties based on the defaults: - upper-left "corner" at x-position ``1`` cm and at y-position ``1`` cm - height of ``1`` cm - width of ``1`` cm Because the *height* and *width* default to the same value, it appears as a `Circle`_. ===== ====== Example 2. Customised Ellipse +++++++++++++++++++++++++++++ .. |el1| image:: images/customised/ellipse_custom.png :width: 330 ===== ====== |el1| This example shows the shape constructed using the command with these properties: .. code:: python Ellipse(cx=2, cy=3, width=3, height=4, dot=0.1) It has the following properties set for it: - centre at x-position ``2`` cm and at y-position ``3`` cm - *height* of ``4`` cm - *width* of ``3`` cm Because the *height* is greater than the *width* it has more of an egg-shape. ===== ====== Example 3. Ellipse Radii ++++++++++++++++++++++++ Radii are like spokes of a bicycle wheel; they are drawn from the centre of an Ellipse towards its circumference. .. |el2| image:: images/customised/ellipse_radii.png :width: 330 ===== ====== |el2| This example shows the shape constructed using the command with these properties: .. code:: python Ellipse( x=1, y=1, width=2, height=1.5, fill=None, radii="n, s, e, w", radii_stroke_width=3, radii_stroke="red") Ellipse( x=1, y=1, width=2, height=1.5, fill=None, radii=[45,135,225,315], radii_stroke_width=1, radii_dotted=True, radii_offset=0.5, radii_length=1) Ellipse( cx=2, cy=4, width=2, height=1.5, fill="green", stroke="orange", stroke_width=1, radii=[0,90,180,270,45,135,225,315], radii_stroke_width=4, radii_stroke="orange") The top two Ellipses are drawn at the same location with the same basic properties; with their *fill* set to ``None`` to make them transparent. These Ellipses also have some of the following properties, which demonstrate how radii can be set and customised: - *x* and *y* to set the upper-left position; or *cx* and *cy* to set the centre - *radii* - a list of angles (in N|deg|), or compass points, sets the directions at which the radii lines are drawn - *radii_stroke_width* - if set, will determine the thickness of the radii - *radii_dotted* - if set to True, will make the radii lines dotted - *radii_stroke* - determines the color of the radii - *radii_length* - changes the length of the radii lines (centre to circumference) - *radii_offset* - moves the endpoint of the radii line **away** from the centre ===== ====== Example 4. Ellipse Radii - Labels +++++++++++++++++++++++++++++++++ .. |el3| image:: images/customised/ellipse_radii_labels.png :width: 330 ===== ====== |el3| This example shows the shape constructed using the command with these properties: .. code:: python Ellipse( cx=1, cy=1, width=2, height=1.5, radii=[30, 150, 270], radii_stroke="white", radii_labels=["A", "B", "C"], radii_labels_rotation=270, radii_labels_stroke="red", radii_labels_font="Courier", dot=0.05) Ellipse( cx=3, cy=3, width=2, height=1.5, radii=[30, 150, 270], radii_labels="A,B,C", radii_labels_rotation=90, dot=0.05) Ellipse( cx=1, cy=5, width=2, height=1.5, radii=[30, 150, 270], radii_labels="ABC", dot=0.05) Apart from the radii lines themselves, the labels' properties can be set: - *radii_labels* - a string or list of strings used for text - *radii_labels_font* - name of the font used for the labels - *radii_labels_rotation* - rotation in degrees relative to radius angle - *radii_labels_size* - point size of labels - *radii_labels_stroke* - the color of the labels - *radii_labels_stroke_width* - thickness of the labels The top-most example shows how text strings are created with a list. The middle example shows how the text string is split using commas; this results in a list whose members are used to create the labels. The lower-most example shows how the same text is repeated for all radii. The top example also shows how text is rotated and styled. The radii lines' stroke color is set to match the circle fill, thereby making it "invisible". The label rotation is relative to its upright position on the line; so 90 |deg| turns the text to the left and onto its "side", as shown in the middle example. ===== ====== .. _triangle-command: Triangle ~~~~~~~~ `↑ `_ A Triangle is a three-sided polygon. It can have uniform sides, in which case it is an *equilateral* triangle |dash| the default; two matching sides, in which case it is an *isosceles* triangle; or all sides unequal, in which case it is an *irregular* triangle. .. NOTE:: There is more detail about the various properties that can be defined for a Triangle in the :ref:`customised shapes' Triangle ` section. Example 1. Default Triangle +++++++++++++++++++++++++++ `^ `_ .. |eqi| image:: images/defaults/equiangle.png :width: 330 ===== ====== |eqi| This example shows the shape constructed using the command with only defaults: .. code:: python Triangle() It has the following properties based on the defaults: - lower-left "corner" at x-position ``1`` cm and y-position ``1`` cm - side - ``1`` cm i.e. all sides are equal (*equilateral* triangle) ===== ====== .. _hexagon-command: Hexagon ~~~~~~~ `↑ `_ .. NOTE:: There is more detail about the many properties that can be defined for a Hexagon in the :ref:`customised shapes' Hexagon ` section. Example 1. Default Hexagon ++++++++++++++++++++++++++ .. |hx1| image:: images/defaults/hexagon-flat.png :width: 330 ===== ====== |hx1| This example shows the shape constructed using the command with only defaults: .. code:: python Hexagon() It has the following properties based on the defaults: - upper-left "corner" at x-position ``1`` cm and at y-position ``1`` cm - flat-to-flat |dash| opposite edges |dash| distance of ``1`` cm - the *orientation* has the default value (not shown here) of ``flat`` i.e. the top edge of the hexagon is parallel to the top of the paper ===== ====== Example 2. Pointy Hexagon +++++++++++++++++++++++++ .. |hx2| image:: images/defaults/hexagon-pointy.png :width: 330 ===== ====== |hx2| This example shows the shape constructed using the command with only one change to the defaults: .. code:: python Hexagon(orientation="pointy") It has the following properties based on the defaults: - upper-left "corner" at x-position ``1`` cm and at y-position ``1`` cm - flat-to-flat height of ``1`` cm - *orientation* of ``pointy`` i.e. the side edge of the hexagon is parallel to the side of the paper ===== ====== .. _pod-command: Pod ~~~ `↑ `_ A pod is a symmetrical shape constructed from two matching curved lines, each drawn on either side of a straight centre line. The shape of each curve is controlled by setting *d* prefixed values for two pairs of *control points*. If only the first control point is set, then a simple curve is created; but if both control points are set, then a *Bezier* line is constructed. .. HINT:: It is not always obvious how exactly to construct a *Bezier* curve, so some experimentation may be needed to get the desired result! Pod Properties ++++++++++++++ Apart from the common drawing properties shared with other shapes, a ``Pod`` has these available: - *length* - the length of the centre line; by default this is ``1`` - *centre_line* - if set to ``True`` then the centre line will be displayed - *dx1* and *dy1* - respectively the horizontal and vertical distance away from the start of the centre line for the first control point i.e. these are **relative** distances and not **absolute** points. By default, they are set to be equal to half the length of the centre line. - *dx2* and *dy2* - respectively the horizontal and vertical distance away from the start of the centre line for the second control point i.e. these are **relative** distances and not **absolute** point values. By default, they are not set to any value. The following examples shows different ways how a Pod can be constructed. Example 1. Default Pod ++++++++++++++++++++++ `^ `_ .. |pd1| image:: images/defaults/pod.png :width: 330 ===== ====== |pd1| This example shows the shape constructed using the command with only defaults: .. code:: python Pod() It has the following properties based on the defaults: - the left point of the line running through the centre of the pod is at x-position ``1`` cm and at y-position ``1`` cm - the *length* of the pod defaults to ``1`` cm ===== ====== Example 2. Customised Pod +++++++++++++++++++++++++ `^ `_ .. |pd2| image:: images/customised/pod_custom.png :width: 330 ===== ====== |pd2| This example shows pod shapes constructed using the command with the following properties: .. code:: python Pod() Pod(cx=3, cy=1, center_line=True) Pod(x=1, y=2, rotation=30) Pod(cx=3, cy=2, rotation=90) Pod(cx=2, cy=3, length=2) Pod(cx=2, cy=5, heading="Head", title="Title", label="Label") The top-left shape is the default. The top-right shows how using the *centre_line* property enables a line to be drawn from one end of the Pod to the other. The smaller middle-left and middle-right shapes show how the Pod can be *rotated* counter-clockwise around its centre |dash| note that centre is calculated the same way regardless of whether the Pod is drawn using *x* and *y* or *cx* and *cy*. The large middle shape shows setting the shape's *length*, and how the default size of the curved lines is increased proportionally to it. The lower shape shows how the *heading*, *label* and *title* are set |dash| see `Text Descriptions`_ for more details. ===== ====== Example 3. Customised Pods ++++++++++++++++++++++++++ `^ `_ .. |pd3| image:: images/customised/pod_customised.png :width: 330 ===== ====== |pd3| This example shows pod shapes constructed using the command with the following properties: .. code:: python Pod(cx=2, cy=1, length=2, center_line=True, fill="gold", stroke="red", stroke_width=1) Pod(cx=1, cy=3, dy1=1, fill="tan") Pod(cx=3, cy=3, dy1=1, dx1=0.1, fill="aqua") Pod(cx=1, cy=4.5, dy1=0.1, dy2=0.5, dx2=-1.2, fill="silver") Pod(cx=3, cy=4.5, dx1=-0.6, dy1=-0.5, dx2=0.15, dy2=-1, fill_stroke="tomato", rotation=-90) The top example shows setting of colors and line length for a longer, thicker pod. The middle-left example, drawn with a brown fill color, shows how the Pod curve size can be changed by setting the *dy1* property. The middle-right example, drawn with a blue fill color, shows how the Pod curve size can be changed by setting the *dy1* and *dx1* properties. The lower-left example, drawn with a grey fill color, shows how the Pod's Bezier curves can be set using the *dy2* and *dx2* properties. The lower-right example shows how the Pod Bezier curve can be set using the *dy2* and *dx2* properties as well as *dy1* and *dx1* properties in order to create a more recognisable "heart" shape. ===== ====== .. _polygon-command: Polygon ~~~~~~~ `↑ `_ A polygon is a regular shape constructed of three or more sides of equal length, and whose interior angles are all equal. For example, a hexagon is simply a polygon with 6 sides and an octagon is a polygon with 8 sides. **HINT** Unlike the `Hexagon`_ shape, a Polygon can be rotated! The following examples show how a Polygon can be constructed. - `Example 1. Default Polygon`_ - `Example 2. Polygon with Sides`_ - `Example 3. Polygon with Radii`_ - `Example 4. Polygon with Perbii`_ - `Example 5. Polygon Rotation`_ - `Example 6. Polygon Slices`_ Example 1. Default Polygon ++++++++++++++++++++++++++ `^ `_ .. |pol| image:: images/defaults/polygon.png :width: 330 ===== ====== |pol| This example shows the shape constructed using the command with only defaults: .. code:: python Polygon() It has the following properties based on the defaults: - centre at x-position ``1`` cm and at y-position ``1`` cm - ``6`` sides - a *side* length of ``1`` cm ===== ====== Example 2. Polygon with Sides +++++++++++++++++++++++++++++ `^ `_ .. |pl1| image:: images/customised/polygon_sizes.png :width: 330 ===== ====== |pl1| This example shows three shapes constructed using the command with the following properties: .. code:: python Polygon( cx=1, cy=5, sides=7, radius=1, label="Seven") Polygon( cx=2, cy=3, sides=6, radius=1, label="Six") Polygon( cx=3, cy=1, sides=5, radius=1, label="Five") It can be seen that each shape is constructed as follows: - *centre* - using *cx* and *cy* values - *radius* - ``1`` cm in each case - *sides* - varying from ``7`` down to ``5`` Even-sided polygons have a "flat" top, whereas odd-sided ones are asymmetrical; this can be adjusted through `rotation`_. ===== ====== Example 3. Polygon with Radii +++++++++++++++++++++++++++++ `^ `_ .. |pl2| image:: images/customised/polygon_radii.png :width: 330 ===== ====== |pl2| This example shows the shape constructed using the command with the additional properties. The **lower** example: .. code:: python Polygon( cx=2, cy=4, sides=8, radius=1, radii="1,3,7") It has the following properties: - *centre* at x-position ``2`` cm and at y-position ``4`` cm, with a *radius* size of ``1`` cm - *sides* - ``8`` sides - *radii* - set to ``1, 3, 7`` to enable lines to be drawn from the centre of the polygon to three of its vertices (starting from the top) The **top** example: .. code:: python Polygon( cx=2, cy=1, sides=10, radius=1, radii="*", radii_offset=0.75, radii_length=0.25, radii_stroke_width=1, dot=0.1, dot_stroke="red" ) It has the following properties: - *centre* at x-position ``2`` cm and at y-position ``1`` cm, with a *radius* size of ``1`` cm - *sides* - ``10`` - *radii* - set to ``*`` to enable lines to be drawn from the centre of the polygon to all of its vertices; the radii properties are then set: - *radii_offset* - set to ``0.75`` cm; distance away from the centre that the radii will start - *radii_length* - set to ``0.25`` cm - *radii_stroke_width* - set to ``1`` point; a slightly thicker line .. NOTE:: When the radii length is shorter than the distance from vertex to centre, the line will still go in the same direction but never touch the vertex. ===== ====== Example 4. Polygon with Perbii ++++++++++++++++++++++++++++++ `^ `_ The *perbii* |dash| "perbis" is short for "perpendicular bisector" and "perbii" is the plural form |dash| defines lines that should be drawn from the centres of the sides of the polygon to the polygon's centre. .. |pl3| image:: images/customised/polygon_perbii.png :width: 330 ===== ====== |pl3| This example shows the shape constructed using the command with the additional properties. The **lower** example: .. code:: python Polygon( cx=2, cy=4, sides=8, radius=1, perbii='*') It has the following properties: - *centre* at x-position ``2`` cm and at y-position ``4`` cm, with a *radius* size of ``1`` cm - *sides* - ``8`` sides (an octagon) - *perbii* - set to ``*``; this means lines are drawn from each of the centres of the sides of the polygon to its centre The **top** example: .. code:: python Polygon( cx=2, cy=1, sides=8, radius=1, perbii="2,4,7", perbii_offset=0.25, perbii_length=0.5, perbii_stroke_width=1, dot=0.1, dot_stroke="red") It has the following properties: - *centre* at x-position ``2`` cm and at y-position ``1`` cm - *radius* size of ``1`` cm - *sides* - ``8`` (an octagon) - *perbii* - lines drawn to sides 2, 4 and 7 The edges of the polygon are numbered; the east-most facing edge is 1, and then numbers increase in an clockwise direction. Its properties can be set as follows: - *perbii* - a list of edges to use - *perbii_offset* - set to ``0.25`` cm; the distance away from the centre that the lines will start to be drawn - *perbii_length* - set to ``0.5`` cm - *perbii_stroke_width* - set to ``1`` point; a slightly thicker line Note that when the *perbii length* is shorter than that the distance from centre point to edge, the line will still go in the same direction but never touch the vertex or the edge. ===== ====== Example 5. Polygon Rotation +++++++++++++++++++++++++++ `^ `_ .. |pl4| image:: images/customised/polygon_rotation_flat.png :width: 330 ===== ====== |pl4| This example shows five Polygons constructed using the command with additional properties. Note the use of the :ref:`Common command ` to allow multiple Polygons to share the same properties. .. code:: python poly6 = Common( fill=None, sides=6, diameter=1, stroke_width=1) Polygon( common=poly6, cy=1, cx=1.0, label="0") Polygon( common=poly6, cy=2, cx=1.5, rotation=15, label="15") Polygon( common=poly6, cy=3, cx=2.0, rotation=30, label="30") Polygon( common=poly6, cy=4, cx=2.5, rotation=45, label="45") Polygon( common=poly6, cy=5, cx=3.0, rotation=60, label="60") The examples have the following properties: - *common* - set shared diameter, sides and fill for all Polygons - *cx* and *cy* - set the centre location for the Polygon - *radius* - ``1`` cm in each case - *sides* - the default of ``6`` in each case (a `hexagon`_ shape) - *rotation* - varies from 0 |deg| to 60 |deg| The rotation defined here is anti-clockwise from the horizontal. ===== ====== Example 6. Polygon Slices +++++++++++++++++++++++++ `^ `_ Slices are a set of colors that are drawn as triangles inside a a Polygon in a clockwise direction starting from the one that is at, or approximates, "North East". If there are fewer colors than all the possible triangles, then the colors are repeated, starting from the first one. .. |pl5| image:: images/customised/polygon_slices.png :width: 330 ===== ====== |pl5| This example shows a Polygon constructed using these commands: .. code:: python Polygon( cx=2, cy=1, sides=8, radius=1, slices=['red', 'orange', 'yellow', 'green', 'aqua', 'pink', 'violet', 'purple']) This example has the following properties: - *cx* and *cy* - set the centre location - *radius* - ``1`` cm - *sides* - set to ``8`` (an octagon) - *slices* - list of named colors that will be drawn seqentially ===== ====== .. _polyshape-command: Polyshape ~~~~~~~~~ `↑ `_ A Polyshape is an irregular `polygon`_, constructed using a series of points. It's basic setup and construction shares much in common with the `Polyline `_ but with some differences, such as its *fill* and centre properties. The following examples illustrate these properties: - `Example 1. Default Polyshape`_ - `Example 2. Polyshape: Centre and Steps`_ - `Example 3. Polyshape Offset`_ - `Example 4. Polyshape with Snail`_ Example 1. Default Polyshape ++++++++++++++++++++++++++++ `^ `_ .. |shp| image:: images/customised/polyshape_default.png :width: 330 ===== ====== |shp| If the shape is constructed using the command with only defaults: .. code:: python Polyshape() Then nothing will be visible; instead you will see a warning:: WARNING:: There are no points to draw the Polyshape Like Polyline, the Polyshape requires a list of points to be constructed. This example shows how to do this using the command with these properties: .. code:: python Polyshape( points=[ (1, 2), (1, 1), (2, 0), (3, 1), (3, 2)]) It has the following properties: - starts at x-position ``1`` cm and y-position ``2`` cm - second point is at x-position ``1`` cm and y-position ``1`` cm - third point is at x-position ``2`` cm and y-position ``0`` cm - etc. The *points* for a Polyshape which represent its vertices are given in a list: - all points are listed inside the square brackets from ``[`` to ``]`` - each *x* and *y* are provided as a pair of values in round brackets - each *x* and *y* are separated by a comma - each pair of values in the list is separated by a comma Lines are drawn between each successive point in the list; **including a line from the last to the first**. The default *stroke* and *fill* apply to this example of a Polyshape. ===== ====== Example 2. Polyshape: Centre and Steps ++++++++++++++++++++++++++++++++++++++ `^ `_ While the Polyshape does not have the ability to be constructed using a *cx* and *cy* pair to set its centre location |dash| like the symmetric shapes |dash| it is possible to provide these values to the shape command, and they can then be used for a label, plus the `dot and cross`_, similar to those other shapes. **NOTE** - the program has no way of knowing or "checking" that the values for the *cx* and *cy* pair that you supply to it are correct! In addition to setting points directly, the Polyshape can also be constructed using the *steps* property. This define a series of values that represent the **relative** distance from the last point drawn. .. |sh2| image:: images/customised/polyshape_custom.png :width: 330 ===== ====== |sh2| The shape is constructed using the command with these properties: .. code:: python Polyshape( x=0, y=1, points=[(1, 2), (1, 1), (2, 0), (3, 1), (3, 2)], cx=2, cy=1, label='A House', label_stroke="seagreen", cross=0.5, fill="sandybrown", stroke="peru", ) As in Example 1, the *points* are used to construct the outline of the "house" shape. Other properties include: - *x* and *y* are used to provide offsets for the start; in this case ``1`` is added to the start of the y-position, and for each value thereafter - the centre is *defined* to be at x-position ``2`` cm and y-position ``1`` cm - *cross* - sets the length of each of the two lines that cross at the centre to be ``0.5`` cm - *label* - sets the text appearing at the defined centre position - *fill* - color of ``sandybrown`` (hexadecimal value ``#F4A460``) for the shape's' interior - *stroke* - color of ``peru`` (hexadecimal value ``#CD853F``) *Reminder:* ``cx`` and ``cy`` affect the drawing of the cross and label but do **not** affect the drawing of the shape itself. The lower shape shows how create a Polyshape using the command with these properties: .. code:: python Polyshape( x=1, y=4, steps='0.5,0 0,1.5 1.5,0 0,-1.5 0.5,0 0,0.5 -2.5,0 0,-0.5', stroke="sandybrown", stroke_width=3, fill="seagreen") Here, the *steps* property results in the drawing of an outline using a series of distances |dash| or offsets |dash| from the last point. The start is provided by the *x* and *y* values. Each pair of comma-separated values are x- and y-distances respectively. ===== ====== Example 3. Polyshape Offset +++++++++++++++++++++++++++ `^ `_ There are two other options available. In addition to the *cx* and *cy* pair, an *x* and *y* pair can also be provided; these values will be used to offset ("move") the Polyshape from the position it would normally occupy. It is also possible to provide the *points* as a string of space-separated pairs of comma-separated values; so instead of ``[(0,0), (1,1)]`` just use ``"0,0 1,1"``. .. |sh3| image:: images/customised/polyshape_offset.png :width: 330 ===== ====== |sh3| The following Polyshapes are constructed using the command with these properties: .. code:: python Polyshape( points="0,0 0,1 2,0 2,1 0,0", cx=1, cy=0.5, fill="gold", label="Left ....... Right") Polyshape( x=1, y=2, points="0,0 0,1 2,0 2,1 0,0", fill="chartreuse", label="Left ....... Right") As in Example 2, the *points* property is used to construct the outline of the Polyshape. In this case, the points are set by a string of space-separated pairs of values. The *fill* color defines the color of the interior of the shapes. For the ``gold`` color Polyshape, the *cx* and *cy* values have been set. These values **only** affect the drawing of the *label*, and **not** the shape itself! The points used to define the ``green`` Polyshape are the same as those used for ``gold`` one, but because the *x* and *y* values have also been set, this causes the whole shape to be be drawn down and to the right. The ``green`` Polyshape has no *cx* and *cy* values set; therefore the label cannot be drawn! ===== ====== Example 4. Polyshape with Snail +++++++++++++++++++++++++++++++ `^ `_ The *snail* property is loosely based on the concept and approach of the Turtle graphics drawing module available for Python (see: https://docs.python.org/3/library/turtle.html). Instead of using points, the idea of the *snail* is to create a Polyshape based on a series of lines of given length, where the line direction |dash| or orientation |dash| will already have been set. Each line is then drawn starting from the end point of the previous line. A *snail* property consists of a series of terms, each separated by a space. Each term either relates to a **direction** change or to drawing a line of a certain **length**. Directions can be set as follows: - a *compass direction*: one of n, e, w, s, ne, se, sw, or nw - an **absolute** *angle*: an ``a`` followed by a value in degrees, from 0 to 360, measured counter-clockwise from the east direction - a **relative** angle: - a ``r`` or ``-`` sign (followed by a value in degrees): will *decrease* the current angle i.e. alter it in a clockwise direction - a ``l`` or ``+`` sign (followed by a value in degrees): will *increase* the current angle i.e. alter it in an anti-clockwise direction Creating a line is done as follows: - a normal value |dash| whole or fractional |dash| will draw a line that distance, in the last direction that was set - using a pair of asterixes (``**``) will draw a line from the current point back to the start .. NOTE:: The *snail* line always starts at the x- and y-point defined for the Polyshape; and the starting direction is "e" or 0 |deg|. The first term in the *snail* property can either be a direction or a distance. Unlike the Polyline *snail*, no "jump" type movement is allowed; there must be a continuous line. .. |sh4| image:: images/customised/polyshape_snail.png :width: 330 ===== ====== |sh4| The Polyshapes are constructed using the command with these properties: .. code:: python Polyshape( x=0.5, y=1.5, snail="ne 1 r65 1 ne 1.5 r125 1.44 **", stroke_width=1, #scaling=0.25, stroke="red", fill="tan") Polyshape( x=1, y=2.5, snail="2 r160 "*9, stroke_width=0.5, #scaling=0.25, stroke="red", fill="yellow") Polyshape( x=1.5, y=4, snail='w .5 s .5 e 2.5 n .5 w .5 s 1.5 w 1.5 n .5', stroke="sandybrown", stroke_width=3, fill="seagreen") Polyshape( x=2, y=4.75, snail='w .5 s .5 e 2.5 n .5 w .5 s 1.5 w 1.5 n .5', scaling=0.25, stroke="sandybrown", stroke_width=1, fill="seagreen") The top example ilustrates the use of the ``r`` term to change the angle of the line to the "right" of the direction it was aimed at before (north east, in the first case). It also shows how the ``**`` term constructs a line back to the start. The middle example |dash| based on the one shown for the Turtle |dash| shows how a simple move-and-turn can be repeated multiple times (using the ``*`` multiplier to make copies) to construct a more complex shape. The lower example is a repeat of the one shown for `Example 2. Polyshape: Centre and Steps`_ but constructed with simple compass directions. It may not be that much shorter, but it could be clearer. In addition it can easily be scaled, as can be seen from the small "inset" shape - the same *snail* but shrunk in size using ``scaling=0.25``. ===== ====== .. _qrcode-command: QRCode ~~~~~~ `↑ `_ A QR Code is a square image containing a pattern of black squares and dots. It represents encoded information that a device with a QR scanner, for example a cell phone, can decode. The properties that can be provided to a ``QRCode`` command, apart from the usual *x* and *y*, to set the upper-left corner, and *height* and *width* to set the size, are: - *image* - this should be the first property and is the name of the file that will be created by the command - *text* - this contains the information that is to be encoded (and decoded) - *scaling* - the size of the indivdual QR Code squares, in pixels - *stroke* - the color of the pattern containing the black squares and dots - *fill* - the color that will appear as the background .. NOTE:: The QR Code images generated will be stored in the cache directory ``.protograf/images/qrcodes`` (or ``.protograf\images\qrcodes``); see :ref:`caching `. Example 1. Default QRCode +++++++++++++++++++++++++ .. |qrc| image:: images/customised/qr_code.png :width: 330 ===== ====== |qrc| The shape cannot be constructed using only default properties: .. code:: python QRCode() Nothing will be visible; instead you will see a warning:: WARNING:: No text supplied for the QRCode shape! This example shows the shape constructed using the commands with these properties: .. code:: python QRCode("qrcode1.png", text="Help") The first command uses the defaults which means it has the following properties automtically set for it: - upper-left corner at x-position ``1`` cm and at y-position ``1`` cm - *width* and *height* - default to ``1`` cm - *scaling* - default is ``1``, so 1 pixel per square - *stroke* - is ``black`` for the squares color - *fill* - is ``white`` for the background color The second command overides various of these defaults: .. code:: python QRCode( 'qrcode2.png', text="Help me ObiWan", x=1, y=3, height=2, width=2, fill="gray", stroke="red", scaling=5 ) In this example, the QR Code is now larger with different colors. ===== ====== .. _rectangle-command: Rectangle ~~~~~~~~~ `↑ `_ .. NOTE:: There is more detail about the many properties that can be defined for a Rectangle in the :ref:`customised Rectangle ` section. Example 1. Default Rectangle ++++++++++++++++++++++++++++ .. |rct| image:: images/defaults/rectangle.png :width: 330 ===== ====== |rct| This example shows the shape constructed using the command with only defaults: .. code:: python Rectangle() It has the following properties set for it: - upper-left corner at x-position ``1`` cm and y-position ``1`` cm - *width* and *height* - default to ``1`` cm Because all sides of the Rectangle are equal, it appears as though it is a `Square`_. ===== ====== Example 2. Customised Rectangle +++++++++++++++++++++++++++++++ .. |rc1| image:: images/customised/rectangle_custom.png :width: 330 ===== ====== |rc1| This example shows the shape constructed using the command with these properties: .. code:: python Rectangle(cx=2, cy=3, width=3, height=4, dot=0.1) It has the following properties set for it: - *cx* and *cy* - set the centre at x-position ``2`` cm and y-position ``3`` cm - *height* - ``4`` cm - *width* - ``3`` cm - *dot* - small, filled circle placed at the centre of size ``0.1`` Because the *height* is greater than the *width* the Rectangle has an appearance like a playing card. ===== ====== .. _rhombus-command: Rhombus ~~~~~~~ `↑ `_ Example 1. Default Rhombus ++++++++++++++++++++++++++ .. |rh0| image:: images/defaults/rhombus.png :width: 330 ===== ====== |rh0| This example shows the shape constructed using the command with only defaults: .. code:: python Rhombus() It has the following properties based on the defaults: - upper-left at x-position ``1`` cm and at y-position ``1`` cm - *width* - ``1`` cm - *height* - ``1`` cm Because the sides are of equal length, the Rhombus appears to be a rotated Square. ===== ====== Example 2. Rhombus Centre & Dot +++++++++++++++++++++++++++++++ .. |rh1| image:: images/customised/rhombus_custom.png :width: 330 ===== ====== |rh1| This example shows the shape constructed using the command with these properties: .. code:: python Rhombus(cx=2, cy=3, width=2, height=3, dot=0.1) It has the following properties set for it: - centre at x-position ``2`` cm and at y-position ``3`` cm - *width* - ``2`` cm - *height* - ``3`` cm - *dot* - small, filled circle placed at the centre of size ``0.1`` ===== ====== Example 3. Rhombus Border Styles ++++++++++++++++++++++++++++++++ .. |rh2| image:: images/customised/rhombus_borders.png :width: 330 ===== ====== |rh2| This example shows the shape constructed using the command with these properties: .. code:: python Rhombus( cx=2, cy=3, width=2, height=3, borders=[ ("nw", 2, gold), ("ne", 2, lime, True), ("se", 2, tomato, [0.1, 0.2]), ("sw", 2) ] ) It has the following properties set for it: - centre at x-position ``2`` cm and at y-position ``3`` cm - *width* of ``2`` cm - *height* of ``3`` cm - *borders* - a list of sets of custom settings for each side; each set can contain: - `direction` - ne (northeast), se (southeast), nw (northwest), or sw (southwest) - `width` - the line thickness - `color` - either a named color or a hexadecimal value - `style` - ``True`` makes it dotted; a pair of values creates dashes Direction and width are required, but color and style are optional. Mutiple, spaced values can be used to draw lines e.g. ``ne se``. ===== ====== Example 4. Rhombus Hatches ++++++++++++++++++++++++++ .. |rh3| image:: images/customised/rhombus_hatches.png :width: 330 ===== ====== |rh3| This example shows the shape constructed using the command with these properties: .. code:: python Rhombus( cx=2, cy=2, width=1.5, height=2, stroke_width=2, hatches="ne sw", hatches_count=3, hatches_stroke="red" ) Rhombus( cx=2, cy=5, width=1.5, height=2, stroke_width=2, hatches=[("nw", 4), ("ne", 3)], hatches_stroke="red" ) Both examples have the following properties set: - *width* of ``1.5`` cm - *height* of ``2`` cm - *stroke_width* of ``2`` - *hatches_stroke* of ``red``; sets the color of the lines criss-crossing the Rhombus The top example shows: - *hatches_count* of ``3``; sets the number of criss-cross lines for **all** directions - *hatches* of ``ne sw``; sets the direction, or directions, in which the criss-cross lines will run The lower example shows: - *hatches* of ``[("nw", 4), ("ne", 3)]``; this sets the number of criss-cross lines separately for each of the directions specified ===== ====== .. _sector-command: Sector ~~~~~~ `↑ `_ A Sector is like the triangular-shaped wedge that is often cut from a pizza or cake. It extends from the centre of a "virtual" circle outwards to its enclosing diameter. The two "arms" of the sector will cover a certain number of degrees of the circle (from 1 to 360). Example 1. Default Sector +++++++++++++++++++++++++ .. |sct| image:: images/defaults/sector.png :width: 330 ===== ====== |sct| This example shows the shape constructed using the command with only defaults: .. code:: python Sector() It has the following properties based on the defaults: - upper-left "corner"at x-position ``1`` cm and at y-position ``1`` cm The sector is then drawn inside a circle of radius ``1`` cm, whose centre is at x-position ``0.5`` cm and at y-position ``0.5`` cm. The default *angle_width* is 90 |deg|. ===== ====== Example 2. Customised Sector ++++++++++++++++++++++++++++ .. |sc1| image:: images/customised/sectors.png :width: 330 ===== ====== |sc1| This example shows examples of the Sector constructed using commands with the following properties. Note the use of the :ref:`Common command ` to allow multiple Sectors to share the same properties. .. code:: python sctm = Common( cx=2, cy=3, radius=2, fill="black", angle_width=43) Sector(common=sctm, angle_start=40) Sector(common=sctm, angle_start=160) Sector(common=sctm, angle_start=280) These all have the following ``Common()`` properties: - centred at x-position ``2`` cm and at y-position ``3`` cm - *radius* of ``2`` cm for the enclosing "virtual" circle - *fill* color of black - *angle_width* - determines the coverage i.e. the "width" of the Sector; in all these cases it is 43 |deg| Each Sector in this example is drawn at a different *angle_start*. This represents a "virtual" centre-line extending through the sector, outwards from the centre of the enclosing "virtual" circle. ===== ====== .. _square-command: Square ~~~~~~ `↑ `_ A square shares almost all of the same properties as a `Rectangle`_ and so that shape, which has additional customisation options available, should also be referenced when working with this shape. Example 1. Default Square +++++++++++++++++++++++++ .. |sqr| image:: images/defaults/square.png :width: 330 ===== ====== |sqr| This example shows the shape constructed using the command with only defaults: .. code:: python Square() It has the following properties based on the defaults: - upper-left corner at: - x-position ``1`` cm, and - y-position ``1`` cm - side of ``1`` cm ===== ====== Example 2. Customised Square ++++++++++++++++++++++++++++ .. |sq1| image:: images/customised/square_custom.png :width: 330 ===== ====== |sq1| This example shows the shape constructed using the command with these properties: .. code:: python Square(cx=2, cy=3, side=3, dot=0.1) It has the following properties set for it: - centre at x-position ``2`` cm and at y-position ``3`` cm - *side* of ``3`` cm; both *width* and *height* match this - *dot* - small, filled circle placed at the centre of size ``0.1`` ===== ====== .. _stadium-command: Stadium ~~~~~~~ `↑ `_ A Stadium is a shape constructed with a rectangle as a base, and then curved projections added that extend from one or more of the sides. In its default form, it may look like a pill. Example 1. Default Stadium ++++++++++++++++++++++++++ .. |std| image:: images/defaults/stadium.png :width: 330 ===== ====== |std| This example shows the shape constructed using the command with only defaults: .. code:: python Stadium() It has the following properties based on the defaults: - straight edge start at: - x-position ``1`` cm and - y-position ``1`` cm - height and width of ``1`` cm each The default curved ends extend from the east/right and west/left sides. ===== ====== Example 2. Customised Stadium +++++++++++++++++++++++++++++ .. |st1| image:: images/customised/stadium_edges.png :width: 330 ===== ====== |st1| This example shows example of the shape constructed using the command with the following properties: .. code:: python Stadium( x=0, y=1, height=1, width=1, edges='n', fill="tan", label="north") Stadium( x=3, y=1, height=1, width=1, edges='s', fill="tan", label="south") Stadium( x=0, y=3, height=1, width=1, edges='e', fill="tan", label="east") Stadium( x=3, y=4, height=1, width=1, edges='w', fill="tan", label="west") These have the following properties set: - *height* and *width* - of ``1`` cm and ``1`` cm respectively - *edges* - set the projection direction(s) The edges of the rounded projection(s) can be set using a letter to represent direction, where: - ``n`` is ``north`` ("up"), - ``s`` is ``south`` ("down"), - ``e`` is ``east`` ("right"), and - ``w`` is ``west`` ("left"). One or more edge values can be used together with spaces between them e.g. ``n e`` to draw both north **and** east. ===== ====== .. _star-command: Star ~~~~ `↑ `_ A Star is a multi-pointed shape; essentially made by joining points spaced equally around the circumference of an outer circle to points spaced equally around the circumference of a smaller "inner" circle. To create other kinds of stars, see the "triangle" or "sun" petal shapes that can be created using a :ref:`customised Circle `. Properties ++++++++++ A Star shape has the following additional properties: - *rays* - number of arms of the Star; defaults to ``5`` - *inner_fraction* - used to calulate the inner circle on which the other points used to draw the Star are placed; as this gets smaller, the width of the arms gets narrower; defaults to ``0.5`` (one-half) - *show_radii* - if ``True``, then lines are drawn from the Star centre to all of the points (inner and outer); default is ``False`` - *slices* - a list of color values that will be used to color the triangles formed between the centre and the points of the rays Example 1. Default Star +++++++++++++++++++++++ .. |str| image:: images/defaults/star.png :width: 330 ===== ====== |str| This example shows the shape constructed using the command with only defaults: .. code:: python Star() The Star has the following properties based on the defaults: - centre at x-position ``1`` cm and at y-position ``1`` cm - default *radius* of ``1`` cm - default of ``5`` *rays* - default *inner_fraction* of ``0.5`` ===== ====== Example 2. Customised Star ++++++++++++++++++++++++++ .. |st2| image:: images/customised/star_custom.png :width: 330 ===== ====== |st2| This example shows the shape constructed using the command with these properties: .. code:: python Star(cx=1, cy=1, radius=1, fill="red", stroke="gold", stroke_width=2, inner_fraction=0.4, ) Star(cx=2, cy=3, radius=1, rays=6, show_radii=True, rotation=30, ) Star(cx=3, cy=5, radius=1, fill=None, rays=12, inner_fraction=0.1, ) These have the following properties that differ from the defaults: - centre defined at *cx* and *cy*-position in cm - *radius* - ``1`` cm; length of the "rays" from centre to outer points The upper Star has the default number of *rays* i.e. ``5``, plus: - *fill* color - ``red`` for the interior of the Star - *stroke* color - ``yellow`` for the outline of the Star - *stroke_width* - ``2`` for the line thickness of the outline of the Star - *inner_fraction* - changed to ``0.4``; which cause the lines to appear to "flatten" and align The middle Star has: - *rays* - changed to ``6`` - *show_radii* - if ``True``, then lines are drawn from the Star centre to all of the points (inner and outer) - *rotation* - 30 |deg| anti-clockwise about the centre The lower Star has: - *rays* - changed to ``12`` - *inner_fraction* - changed to ``0.1``; which causes the "spiky" appearance ===== ====== Example 3. Star with Slices +++++++++++++++++++++++++++ .. |st3| image:: images/customised/star_slices.png :width: 330 ===== ====== |st3| This example shows the shape constructed using the command with these properties: .. code:: python Star(cx=2, cy=1, radius=1, rays=4, inner_fraction=0.33, stroke_width=2, slices=["black", "white"], dot=0.02, dot_stroke="red", ) Star(cx=2, cy=4, radius=1, slices=[ "#CE8F0C", "#F8C40C", "#F3BA0B", "#DB9F0D", "#F8C609", "#CE8F0C", "#F7C30D", "#D59A0E", "#CE8F0C", "#F7C615", ] ) The upper Star has the following changes: - *rays* - changed to ``4`` - *inner_fraction* - changed to ``0.33``; which makes rays more "spiky" - *dot* - a small red dot is drawn in the centre of the Star - *slices* - only two colors are provided in the list, so they will be reused across all the rays The lower Star has the default number of *rays* i.e. ``5``, plus: - *slices* - the list contains a unique color for each triangle. **NOTE** that the coloring for the triangles starts in the righthand side of the "top" triangle |dash| by default, a Star's rays always start from 90 |deg|, or "north". ===== ====== .. _starfield-command: Starfield ~~~~~~~~~ `↑ `_ A Starfield is a shape in which a number of small dots are scattered at random to simulate what might be seen when looking at a portion of the night sky. The dots are drawn inside the boundaries of an "enclosure"; this can be a rectangle, a circle, or a polygon |dash| but this shape is not, itself, drawn. The number of dots drawn depends on the "density", which is the product of the actual area of the shape multiplied by the density value. .. HINT:: If you want repeatable randomness - that is to say, the same sequence of random numbers being generated every time the program is run - then assign a value to the *seeding* property; for example: .. code:: python Starfield(seeding=42) The images used for this document are created with such a setting; but only to avoid the code repository detecting a "change" each time the script runs. Example 1. Default Starfield ++++++++++++++++++++++++++++ .. |sf0| image:: images/defaults/starfield.png :width: 330 ===== ====== |sf0| This example shows the shape constructed using the command with only defaults: .. code:: python Starfield() It has the following properties based on the defaults: - upper-left corner at x-position ``0`` cm and y-position ``0`` cm - an enclosing rectangle with *height* and *width* of ``1`` cm - 10 randomly placed ``white`` *color* 'dots' (the starfield *density*) Because the default fill color is ``white``, this example adds an extra `Rectangle()` shape, with a fill of ``black``, which is drawn first and is hence "behind" the field of dots. ===== ====== Example 2. Multiple Color Starfield +++++++++++++++++++++++++++++++++++ .. |sf1| image:: images/customised/starfield_rectangle.png :width: 330 ===== ====== |sf1| This example shows the shape constructed using the command with the following properties: .. code:: python StarField( enclosure=rectangle(x=0, y=0, height=3, width=3), density=80, colors=[white, white, red, green, blue], sizes=[0.4] ) It has the following properties set: - upper-left corner at x-position ``0`` cm and y-position ``0`` cm - *enclosure* - the rectangle size determines the boundaries of the area (*height* and *width* each of ``3`` cm) inside of which the stars (dots) are randomly drawn - *density* - there will be a total of "80 multiplied by the enclosure area" dots drawn - *colors* - is a list of colors, one of which will be randomly chosen each time before drawing a dot - *sizes* - is a list of randomly chosen dot sizes; in this case there is just one value and so all dots will be same size Because the default fill color is white, this example adds an extra `Rectangle()` shape, with a fill color of black, which is drawn first and is hence "behind" the field of dots. ===== ====== Example 3. Multiple Size Starfield ++++++++++++++++++++++++++++++++++ .. |sf2| image:: images/customised/starfield_circle.png :width: 330 ===== ====== |sf2| This example shows the shape constructed using the command with the following properties: .. code:: python StarField( enclosure=circle(x=0, y=0, radius=1.5), density=30, sizes=[0.15, 0.15, 0.15, 0.15, 0.3, 0.3, 0.5] ) It has the following properties set: - upper-left "corner" at x-position ``0`` cm and at y-position ``0`` cm - *enclosure* - the `circle` radius (``1.5`` cm) determines the boundaries of the area inside of which the stars (dots) are randomly drawn - *density* - there will be a total of "30 multiplied by the enclosure area" dots drawn - *sizes* - is a list of available dot sizes, one of which is randomly chosen from the list each time before drawing a dot Because the default fill color is white, this example adds an extra `Circle()` shape, with a fill color of black, which is drawn first and is hence "behind" the field of dots. ===== ====== Example 4. Multiple Color & Size Starfield ++++++++++++++++++++++++++++++++++++++++++ .. |sf3| image:: images/customised/starfield_poly.png :width: 330 ===== ====== |sf3| This example shows the shape constructed using the command with the following properties: .. code:: python StarField( enclosure=polygon(x=1.5, y=1.4, sides=10, radius=1.5), density=50, colors=["white", "white", "white", "red", "green", "blue"], sizes=[0.15, 0.15, 0.15, 0.15, 0.3, 0.3, 0.45] ) It has the following properties set: - upper-left "corner" at x-position ``1.5`` cm and y-position ``1.4`` cm - *enclosure* - the polygon radius (``1.5`` cm) determines the boundaries of the area inside of which the stars (dots) are randomly drawn - *density* - there will be a total of "50 multiplied by the enclosure area" dots drawn - *colors* - a list of available dot colors, one of which is randomly chosen from the list each time before drawing a dot - *sizes* - a list of available dot sizes, one of which is randomly chosen from the list each time before drawing a dot Because the default fill color is white, this example adds an extra `Polygon()` shape, with a fill color of black, which is drawn first and is hence "behind" the field of dots. ===== ====== .. _trapezoid-command: Trapezoid ~~~~~~~~~ `↑ `_ Example 1. Default Trapezoid ++++++++++++++++++++++++++++ .. |trp| image:: images/defaults/trapezoid.png :width: 330 ===== ====== |trp| This example shows the shape constructed using the command with only defaults: .. code:: python Trapezoid() It has the following properties based on the defaults: - starts at x-position ``1`` cm and at y-position ``1`` cm - *width* of ``1`` cm - *height* of ``1`` cm - the lower edge of the shape defaults to half the *width* ===== ====== Example 2. Size & Flip Trapezoid ++++++++++++++++++++++++++++++++ .. |tr1| image:: images/customised/trapezoid_custom.png :width: 330 ===== ====== |tr1| This example shows the shape constructed using the command with these properties: .. code:: python Trapezoid( cx=2, cy=3, width=3, top=2, height=4, flip='s', dot=0.1) It has the following properties set for it: - centre at x-position ``2`` cm and at y-position ``3`` cm - *width* of ``3`` cm - *height* of ``4`` cm - *top* of ``2`` cm - *flip* of ``s`` (for ``south``) means the "top" is drawn below the base ===== ====== Example 3. Trapezoid Borders ++++++++++++++++++++++++++++ .. |tr3| image:: images/customised/trapezoid_borders.png :width: 330 ===== ====== |tr3| This example shows the shape constructed using the command with these properties: .. code:: python Trapezoid( cx=2, cy=3, width=2, height=2, top=1.5, stroke_width=2, borders=[ ("w", 2, "gold"), ("e", 2, "chartreuse", True), ("n", 2, "tomato", [0.1, 0.2]), ("s", 2) ] ) It has the following properties set for it: - centre at x-position ``2`` cm and at y-position ``3`` cm - *width* of ``2`` cm - *height* of ``3`` cm - *top* of ``1.5`` cm - *stroke_width* of 2 points - *borders* - a list of sets of custom settings for each side; each set can contain: - *direction* - one of n(orth), s(outh), e(ast) or w(est) - *width* - the line thickness - *color* - either a named color or a hexadecimal value - *style* - ``True`` makes it dotted; a list of values creates dashes Borders' direction and width are required, but color and style are optional. Multiple border directions can be used, with spaces between them, e.g. ``n s`` to draw lines on both north **and** south sides. ===== ====== .. _compoundIndex: Compound Shapes --------------- `↑ `_ Compound shapes are ones composed of multiple elements; but the program takes care of drawing all of them based on the properties supplied. The following are all such shapes: - `Blueprint`_ - `DotGrid`_ - `Grid`_ - `Hexagons`_ - `Image`_ - `Lines`_ - `Rectangles`_ - `Table`_ .. _blueprint-command: Blueprint ~~~~~~~~~ `↑ `_ This shape is primarily intended to support drawing while it is "in progress". It provides a quick and convenient underlying grid that can help to orientate and place other shapes that *are* required for the final product. Typically, one would just comment out this command when its purpose has been served. On the grid, the values of **x** appear across the lower edge (increasing from left to right); those for **y** along the left side (increasing from top to bottom). The grid respects the margins that have been set but you will observe that the Blueprint numbering itself is located inside the margin area! Different styling options are provided that can make the Blueprint more useful in different contexts. .. NOTE:: There is more detail about the various properties that can be defined for a Blueprint in the :ref:`customised Blueprint ` section. Example 1. Defaults +++++++++++++++++++ .. |blp| image:: images/defaults/blueprint.png :width: 330 ===== ====== |blp| This example shows the shape constructed using the command with only defaults: .. code:: python Blueprint() It has the following properties based on the defaults: - starts at the upper-left corner, as defined by the page margins - has vertical and horizontal lines filling the page from the lower left corner up to the right-most and top-most margins - has interval between the lines of ``1`` cm - default line color is a shade of ``blue`` (hexadecimal ``#2F85AC``) - the x- and y-axis are numbered from the left and top respectively ===== ====== Example 2. Subdivisions & Style +++++++++++++++++++++++++++++++ .. |bl2| image:: images/customised/blueprint_subdiv.png :width: 330 ===== ====== |bl2| This example shows the shape constructed using the command with these properties: .. code:: python Blueprint( subdivisions=5, stroke_width=0.5, style='invert') It has the following properties set: - *subdivisions* - set to ``5`` - *stroke_width* - set to ``0.5``; slightly thicker line makes the main grid more visible - *style* - set to ``invert`` so that the lines and number colors are white and the fill color is now a shade of ``blue`` (``#2F85AC``) The *subdivisions* are the thinner lines that are drawn between each pair of primary lines |dash| they do not have any numbering and are *dotted*. ===== ====== .. _dotgrid-command: DotGrid ~~~~~~~ `↑ `_ A DotGrid is a series of dots |dash| both in the vertical and horizontal directions. This will, by default, fill the page, as far as possible, between its margins. Example 1. Defaults +++++++++++++++++++ .. |dtg| image:: images/defaults/dotgrid.png :width: 330 ===== ====== |dtg| This example shows the shape constructed using the command with only defaults:: DotGrid() It has the following properties based on the defaults: - the upper-left of the grid is drawn at the default x-position of ``1`` cm and y-position ``1`` cm relative to the margins - default dot size of ``3`` points - default color of ``black`` ===== ====== Example 2. Moleskine Grid +++++++++++++++++++++++++ .. |dg1| image:: images/customised/dotgrid_moleskine.png :width: 330 ===== ====== |dg1| This example shows the shape constructed using the command with the following properties: .. code:: python DotGrid( stroke="darkgray", x=0, y=0, width=0.5, height=0.5, dot_width=1, margin_fit=False) To simulate the dot grid found in Moleskine notebooks, it has the following properties set: - *x* and *y* - start the grid at the top-left of the page - *width* and *height* - intervals between the centre of the dots in the x- and y-directions respectively - *dot_width* - set to be smaller than the default of ``3`` - *stroke* - set to ``darkgrey`` i.e. lighter than the default ``black`` - *margin_fit* - set to ``False`` to ignore any page margins when calculating the *x* and *y* positions, and also when calculating the width and height of the grid .. HINT:: For a notebook page for *actual* use, you could consider setting the page color. To change the page color, set the *fill* property of the ``Create()`` command. A color like ``"cornsilk"`` might provide a suitable backdrop for the grey color of the dot grid. ===== ====== .. _grid-command: Grid ~~~~ `↑ `_ A Grid is a series of crossed lines |dash| both in the vertical and horizontal directions. The Grid will, by default |dash| i.e. if the exact number of rows and columns is not specified |dash| fill the page as far as possible between its margins. .. NOTE:: The behaviour for a grid on a Card is little different, as a :ref:`Card ` has no margins; so all *x* and *y* settings, such as those used by a grid are relative to the card edges. Examples showing how the Grid can be styled are described below. - `Example 1. Defaults `_ - `Example 2. Side, Stroke & Fill `_ - `Example 3. Fixed Size `_ - `Example 4. Ignore Margins `_ - `Example 5. Omit Edges `_ .. _gridDefaults: Example 1. Defaults +++++++++++++++++++ `↑ `_ .. |grd| image:: images/defaults/grid.png :width: 330 ===== ====== |grd| This example shows the shape constructed using the command with only defaults: .. code:: python Grid() It has the following properties based on the defaults: - starts at upper-left corner of the page, as defined by the top- and left-margins - has a default grid interval of ``1`` cm in both the x- and y-direction ===== ====== .. _gridSide: Example 2. Side, Stroke & Fill ++++++++++++++++++++++++++++++ `↑ `_ .. |gr2| image:: images/customised/grid_gray.png :width: 330 ===== ====== |gr2| This example shows the shape constructed using the command with the following properties (and without a `Blueprint`_ background): .. code:: python Grid( side=0.85, fill="lightgray", stroke="gray", stroke_width=1) It has the following properties based on the defaults: - *side* - set to ``0.85`` cm (about 1/3 of an inch) which sets the size of both the x- and y-direction - *fill* - set to ``lightgray`` for the grid's background color - *stroke_width* - set to ``1`` point; the thicker line makes the grid more visible - *stroke* - set to ``gray`` i.e. a lighter color than the default black ===== ====== .. _gridFixed: Example 3. Fixed Size +++++++++++++++++++++ `↑ `_ .. |gr3| image:: images/customised/grid_3x4.png :width: 330 ===== ====== |gr3| This example shows the shape constructed using the command with the following properties: .. code:: python Grid( x=0.5, y=0.5, height=1.25, width=1, cols=3, rows=4, stroke="gray", stroke_width=1, heading="Heading", label="Label", title="Title" ) It has the following properties set for it: - *x* and *y* - each set to ``0.5`` cm; offsets the grid's upper-left corner from the page margin - *height* - value of ``1.25`` cm set for the row height - *width* - value of ``1`` cm set for the column width - *cols* and *rows* - ``3`` columns wide by ``4`` rows high - *stroke_width* - set to ``1`` point; the thicker line makes the grid clearly visible - *stroke* - set to ``gray`` i.e. a lighter color than the default black - *heading*, *label* and *title* - see `Text Descriptions`_ for details The grid now has a fixed "rows by columns" size, rather than being automatically calculated to fill up the page. ===== ====== .. _gridMargins: Example 4. Ignore Margins +++++++++++++++++++++++++ `↑ `_ .. |gr4| image:: images/customised/grid_ignore_margins.png :width: 330 ===== ====== |gr4| This example shows the shape constructed using the command with the following properties: .. code:: python Grid( x=0, y=0, height=1.2, width=1, stroke="gray", stroke_width=1, margin_fit=False, label="Grid Label") ) It has the following properties set for it: - *x* and *y* - each set to ``0`` cm; grid's upper-left corner starts at the page edges (because of *margin_fit*) - *height* - value of ``0.9`` cm set for the row height - *width* - value of ``1`` cm set for the column width - *stroke_width* - set to ``1`` point; the thicker line makes the grid clearly visible - *stroke* - set to ``gray`` i.e. a lighter color than the default black - *label* - see `Text Descriptions`_ for details The grid size has being automatically calculated to fill up the page. Note the use of *margin_fit* set to ``False``, thereby causing the page margins to be ignored when calculating the top left of the grid, as well as its height and width. ===== ====== .. _gridEdges: Example 5. Omit Edges +++++++++++++++++++++ `↑ `_ .. |gr5| image:: images/customised/grid_omit_edges.png :width: 330 ===== ====== |gr5| This example shows the Grid constructed using the command with the following properties: .. code:: python Grid( x=0.5, y=0.5, rows=3, cols=3, side=0.5, stroke_width=0.5, omit_left=True) Grid( x=2.5, y=0.5, rows=3, cols=3, side=0.5, stroke_width=0.5, omit_bottom=True) Grid( x=1.5, y=2.5, rows=3, cols=3, side=0.5, stroke_width=0.5, omit_outer=True) Grid( x=0.5, y=4.5, rows=3, cols=3, side=0.5, stroke_width=0.5, omit_top=True) Grid( x=2.5, y=4.5, rows=3, cols=3, side=0.5, stroke_width=0.5, omit_right=True) Each of the grids have the following properties set: - *x* and *y* - set the grid's upper-left corner - *rows* and *cols* - set the number of "spaces" to drawn in the vertical and horizontal directions - *side* - value of ``0.5`` cm sets the row height **and** column width - *stroke_width* - set to ``0.5`` points; the thicker line makes the grid more visible In addition, each grid has an *outer_...* property set to ``True``. This means that the line on that edge of the grid is not drawn. Setting *omit_outer* to ``True`` means **all** edge lines are not drawn. ===== ====== .. _image-command: Image ~~~~~ `↑ `_ Pedantically speaking, an image is not like the other shapes in the sense that it does not consist of lines and areas drawn by **protograf** itself. An "image" refers to an external file which is simply inserted into the page at the location. The Image shape shares a number of common aspects with other shapes |dash| such as its ``x`` & ``y`` ("top left") positions, a ``width`` and a ``height``, the ability to be rotated, and the addition of text in form of a ``label``, ``heading`` or ``title``. If an image has a transparent area, this will be respected and shapes drawn previously by the script may then be visible "below" it (see examples below). An image can also be "drawn over" by other shapes appearing later on in the script. The following examples show how an image can be added to, or altered: - `Example 1. Default Image`_ - `Example 2. Rotation & Scaling`_ - `Example 3. Auto Frame`_ (calculate height from width or vice-versa) - `Example 4. Alignment`_ (set the image's "anchor" point) - `Example 5. Captions and Markings`_ - `Example 6. Sliced Images`_ (extract image "thirds") - `Example 7: Operations`_ ("cutout" shapes, rounding, and blurred edges) .. _image-default: Example 1. Default Image ++++++++++++++++++++++++ `^ `_ .. |im1| image:: images/customised/image_default.png :width: 330 ===== ====== |im1| If the Image was constructed using only default properties, there will be nothing to see and an error will be displayed: .. code:: python Image() Will show this message:: FEEDBACK:: Unable to load image - no name provided This example then shows the shape constructed with just a single property: .. code:: python Image("sholes_typewriter.png") This first, unnamed property is the filename of the image. If no directory is supplied for the image, it is assumed to be in the same directory as that of the script. The image has the following other properties based on the defaults: - upper-left corner - x-position ``1`` cm and y-position ``1`` cm - *width* and *height* - default to ``1`` cm each .. HINT:: The size set for the image may distort it if the ratios do not match those of the image itself. ===== ====== .. _image-rotation: Example 2. Rotation & Scaling +++++++++++++++++++++++++++++ `^ `_ .. NOTE:: :doc:`protograf ` does not currently do image scaling in the sense of altering the image dimensions of the actual image file. Instead, by setting its ``height`` and ``width`` properties, the image can appear in the output at the size required. Bear in mind that larger images will increase the size of the output PDF file accordingly, regardless of how small they appear on a page. .. |im2| image:: images/customised/images_normal_rotation.png :width: 330 ===== ====== |im2| This example shows the Image constructed using the command with the following properties: .. code:: python Image( "sholes_typewriter.png", x=0, y=1, width=2.0, height=2.0, title="PNG") Image( "sholes_typewriter.png", x=2, y=1, width=1.5, height=1.5, title="60\u00B0", rotation=60) Image( "noun-typewriter-3933515.svg", x=0, y=4, width=2.0, height=2.0, title="SVG") Image( "noun-typewriter-3933515.svg", x=2, y=4, width=1.5, height=1.5, title="45\u00B0", rotation=45) Each image has the following properties set for it: - name of the image file; this must be the first property set - *x* and *y* - these values set the upper-left corner Each set of images has different sizes to simulate image scaling. The two left-hand images have: - *height* - set to ``2.0`` cm; this value may cause some distortion - *width* - set to ``2.0`` cm; this value may cause some distortion The two right-hand images have: - *height* - set to ``1.5`` cm; this value may cause some distortion - *width* - set to ``1.5`` cm; this value may cause some distortion The two right-hand images are rotated about a centre point: - *rotation* - degrees, anti-clockwise, about the centre The image centre is calculated based on it's height and width. ===== ====== .. _image-autoframe: Example 3. Auto Frame +++++++++++++++++++++ `^ `_ Normally the frame |dash| or size of the Image occupied on the page |dash| is done by setting **both** its ``height`` and ``width`` properties. However, it is possible to set **either** ``height`` and ``width``, and then set ``auto_frame=True`` to have the other dimension automatically calculated based on the relative dimensions of the image itself. If both ``height`` and ``width`` are set, then ``auto_frame`` will not be used. .. |im8| image:: images/customised/image_auto_frame.png :width: 330 ===== ====== |im8| This example shows the Image constructed using the command with the following properties: .. code:: python img_file = "fantasy-forest-with-old-bridges-crop.jpg" Image( img_file, x=0, y=0, width=1.5, auto_frame=True) Rectangle(x=0, y=0, label="W", common=rred) Image( img_file, x=2, y=3, height=2, auto_frame=True) Rectangle(x=2, y=3, label="H", common=rred) In the top-left example, the *width* has been set for the Image, and then the height is automatically calculated; in this case because the image is 900 pixels high by 600 pixels wide, the height is about ``2.25`` cm. In the lower-right example, the *height* has been set for the Image, and then the width is automatically calculated; in this case because the image is 900 pixels high by 600 pixels wide, the width is about ``1.33`` cm. ===== ====== .. _image-align: Example 4. Alignment ++++++++++++++++++++ `^ `_ Image alignment is somewhat similar to alignment of Text. Instead of the shape's ``x`` and ``y`` values defining the top-left position, the use of either, or both, *align_horizontal* or *align_vertical* can cause the shape to be located in a different relative position. The *align_horizontal* property can take on values of ``"left"``, ``"centre"`` or ``"right"``; the *align_vertical* property can take on values of ``"top"``, ``"middle"`` or ``"bottom"``. These are illustrated in the exampe below. .. |ia1| image:: images/customised/image_align.png :width: 330 ===== ====== |ia1| This example shows the Image constructed using the command with the properties shown. Note the use of the :ref:`Common command ` to allow multiple Images to share the same properties. .. code:: python rdot = Common(fill_stroke="red", radius=0.05) image_file = "fantasy-forest-with-old-bridges.png" Image(image_file, width=1, height=1, x=0.5, y=0.5, title="no align") Circle(common=rdot, cx=0.5, cy=0.5) Image(image_file, width=1, height=1, cx=3, cy=1, title="centre x,y") Image(image_file, width=1, height=1, x=2, y=4, align_horizontal="right", align_vertical="bottom", title="bottom-right") Circle(common=rdot, cx=2, cy=4) Image(image_file, width=1, height=1, x=2, y=2, align_horizontal="left", align_vertical="top", title="top-left") Circle(common=rdot, cx=2, cy=2) Image(image_file, width=1, height=1, x=0, y=5, align_horizontal="left", align_vertical="mid", title="mid-left") Circle(common=rdot, cx=0, cy=5) Image(image_file, width=1, height=1, x=3, y=5, align_horizontal="centre", align_vertical="mid", title="mid-centre") Circle(common=rdot, cx=3, cy=5) The top-left image is set using defaults i.e. no alignment. The top right-hand image position is set using a centre point; for such a setting, no alignment can be used. The other images have a small red dot superimposed on them, set to the same value as the *x* and *y* used to position the shape; this helps show how the image is drawn relative to that position. ===== ====== .. _image-caption: Example 5. Captions and Markings ++++++++++++++++++++++++++++++++ `^ `_ .. |im3| image:: images/customised/image_label.png :width: 330 ===== ====== |im3| This example shows shapes constructed using their command with the following properties: .. code:: python Text(common=txt, text="Image: label, heading, title") Rectangle( width=2.26, height=2, x=1, y=0.5, dotted=True, fill="silver") Image("sholes_typewriter.png", width=2.26, height=2, x=1, y=0.5, label="Label", label_stroke='red', cross=True) Rectangle( width=2.26, height=2, x=1, y=3.5, dotted=True, fill="silver") Image("sholes_typewriter.png", width=2.26, height=2, x=1, y=3.5, heading="Heading", title="Title", dot=0.1, dot_stroke='red') In this example, a grey-filled rectangle, with dotted border, is drawn just prior to the image. The same image is used in two places here to demonstrate the following: - how a "background" or "lower level" shape is visible through the transparency of a PNG image; - where the label, heading and title for an image will appear; - where the cross for an image will appear; - where the dot for an image will appear. ===== ====== .. _image-sliced: Example 6. Sliced Images ++++++++++++++++++++++++ `^ `_ .. |im4| image:: images/customised/image_sliced.png :width: 330 ===== ====== |im4| This example shows the Image constructed using the command with the following properties: .. code:: python Image("sholes_typewriter.png", sliced='l', width=1, height=3, x=0, y=0) Image("sholes_typewriter.png", sliced='c', width=1, height=3, x=1.5, y=0) Image("sholes_typewriter.png", sliced='r', width=1, height=3, x=3, y=0) Image("sholes_typewriter.png", sliced='t', width=3, height=1, x=0.5, y=3) Image("sholes_typewriter.png", sliced='m', width=3, height=1, x=0.5, y=4) Image("sholes_typewriter.png", sliced='b', width=3, height=1, x=0.5, y=5) Here the *sliced* property is used to "slice" off portions of the image. In the upper example: - *l* - the left fraction, matching the image's width:height ratio - *c* - the centre fraction, matching the image's width:height ratio - *r* - the right fraction, matching the image's width:height ratio In the lower example: - *t* - the top fraction, matching the image's height:width ratio - *m* - the middle fraction, matching the image's height:width ratio - *b* - the botttom fraction, matching the image's height:width ratio ===== ====== .. _image-operations: Example 7: Operations +++++++++++++++++++++ `^ `_ It is possible change the way an Image appears by either creating a "cut-out" from it, or by blurring the edges. These changes are termed *operations*. Each operation is specified by its name, followed by one or more settings, in list format (i.e. inside ``[...]`` brackets). Be aware that values used for these operations are pixel-based values and do not correspond to the units used elsewhere in **protograf**. The cut-out operations are: - *circle* (or ``c``): cut-out a circle; this must be followed by the radius, in pixels, of the circle - *ellipse* (or ``e``): cut-out an ellipse; this must be followed by the width and height |dash| inside ``(...)`` brackets |dash| in pixels, of the ellipse - *polygon* (or ``p``): cut-out a regular polygon; this must be followed by the radius, in pixels, of the polygon; and an optional number for the number of sides of the polygon |dash| the default is 6 (a hexagon) - *rounding* (or ``r``): cut-out a rounded portion of each corner of the image; this must be followed by the radius, in pixels, of the cutout size By default, the cutout center matches the center of the image; but it is possible to shift the center by adding two values for the x- and y-shift, in pixels, respectively. This shift does **not** apply to *rounding*. The blur operation is: - *blur* (or ``b``): blur the edges; this must be followed by the radius, in pixels, of the size of the blur .. |im5| image:: images/customised/image_operations.png :width: 330 ===== ====== |im5| This example shows the Image constructed using the command with the following properties: .. code:: python Image("fantasy-forest-with-old-bridges.png", width=2, height=2, x=0, y=0) Image("fantasy-forest-with-old-bridges.png", width=1.5, height=1.5, x=2, y=0, operation=['circle', 100, 75, -75] ) Image("fantasy-forest-with-old-bridges.png", width=1.5, height=1.5, x=2.5, y=0.5, operation=['circle', 100, -75, 75] ) Image("fantasy-forest-with-old-bridges.png", width=2, height=2, x=0, y=2, operation=['rounding', 50] ) Image("fantasy-forest-with-old-bridges.png", width=2, height=2, x=2, y=2, operation=['ellipse', (160, 240)] ) Image("fantasy-forest-with-old-bridges.png", width=2, height=2, x=0, y=4, operation=['polygon', 140, 5] ) Image("fantasy-forest-with-old-bridges.png", width=2, height=2, x=2, y=4, operation=['blur', 20] ) The top-left image is the original, while the others show the result of an operation. Note that the two *circle* operations use offset values to move the centre of where the cutout happens. ===== ====== .. _hexagons-command: Hexagons ~~~~~~~~ `↑ `_ Hexagons are often drawn in a "honeycomb" arrangement to form a grid. For games this is often used to delineate the spaces in which playing pieces can be placed and their movement regulated. .. NOTE:: Very detailed information about using hexagons in grids can be found in the section on :doc:`Hexagonal Grids `. Example 1. Hexagons Defaults ++++++++++++++++++++++++++++ .. |hex| image:: images/defaults/hexagons-2x2.png :width: 330 ===== ====== |hex| This example shows the shape constructed using the command with two basic properties; the number of rows and columns in the grid: .. code:: python Hexagons(rows=3, cols=3) It has the following properties based on the defaults: - upper-left "corner" at x-position ``1`` cm and at y-position ``1`` cm - flat-to-flat hexagon *height* of ``1`` cm - "flat" top hexagons - size of ``3`` *rows* by ``3`` *cols* ("columns") - the "even" columns are offset by one-half hexagon height "downwards" ===== ====== .. _lines-command: Lines ~~~~~~ `↑ `_ Lines are simply a series of parallel lines drawn over repeating rows - for horizontal lines - or columns - for vertical lines. Example 1. Lines Defaults +++++++++++++++++++++++++ .. |ls0| image:: images/defaults/lines.png :width: 330 ===== ====== |ls0| This example shows the shape constructed using the command with only defaults: .. code:: python Lines() It has the following properties based on the defaults: - starts at x-position ``1`` cm and at y-position ``1`` cm - heading/default direction is 0 |deg| (anti-clockwise from 0 |deg| "east") - has a default number of lines of ``1`` - line length of ``1`` cm ===== ====== Example 2. Customised Lines +++++++++++++++++++++++++++ .. |ls1| image:: images/customised/lines.png :width: 330 ===== ====== |ls1| This example shows the shapes constructed using the command with the following properties: .. code:: python Lines( x=1, y=1, x1=4, y1=1, rows=2, height=1, label_size=8, label="rows; ht=1.0") Lines( x=1, y=3, x1=1, y1=6, cols=2, width=1.5, label_size=8, label="col; wd=1.5") The first command has the following properties: - *x* and *y* - both set at ``1`` cm for the left starting point - *x1* and *y1* - set ``4`` cm and ``1`` cm for the right end point - *rows* - set to ``2`` to create two parallel horizontal lines - *height* - value of ``1`` cm set for the row height; this is the separation between each line The second command has the following properties: - *x* and *y* - set to ``1`` cm and ``3`` cm for the left starting point - *x1* and *y1* - set ``1`` cm and ``6`` cm for the right end point - *cols* - set to ``2`` to create two parallel vertical lines - *width* - value of ``1.5`` cm set for the column width; this sets the separation between each line Note that the *label* that has been set applies to **every** line that is drawn. ===== ====== .. _rectangles-command: Rectangles ~~~~~~~~~~ `↑ `_ Rectangles can be drawn in a row-by-column layout to form a grid. For games this is often used to delineate a track or other spaces in which playing pieces can be placed. Example 1. Rectangles: Columns and Rows +++++++++++++++++++++++++++++++++++++++ .. |rc0| image:: images/customised/rectangles_rowcol.png :width: 330 ===== ====== |rc0| This example shows the shape constructed using the command with these properties: .. code:: python Rectangles( rows=3, cols=2, stroke_width=1) It has the following properties: - top-left corner at defaults of x-position ``0`` cm and y-position ``0`` cm - *height* and *width* of default ``1`` cm each - *stroke_width* of ``1`` There are 3 rows |dash| the y-direction |dash| and 2 columns |dash| the x-direction. ===== ====== Example 2. Customised Rectangles ++++++++++++++++++++++++++++++++ .. |rn1| image:: images/customised/rectangles_custom.png :width: 330 ===== ====== |rn1| This example shows the Rectangles constructed using the command with these properties: .. code:: python Rectangles( cols=2, rows=4, width=1.5, height=1.25, fill="chartreuse", dotted=True) It has the following properties based on the defaults: - starts at x-position ``0`` cm and y-position ``0`` cm - *width* - ``1.5`` cm set for each Rectangle's width - *height* - ``1.25`` cm set for each Rectangle's height - *fill* color of ``chartreuse`` - *dotted* border lines for each Rectangle ===== ====== .. _table-command: Table ~~~~~~~~~~ `↑ `_ Tables are an arrangement of rectangles in a column-and-row layout. Either the rows and columns are split evenly across the Table's height and width, or the values of each row and column can be set via lists of values. Table colors and line styles can be set as described in the examples below, as can the cell padding |dash| the "white space" around the inner-edges of a cell. Tables do not, themselves, contain any information. However, any of the "cells" in a table can be accessed using a spreadsheet-like notation to make use their location and size. - `Example 1. Table Basics`_ - `Example 2. Customised Table`_ - `Example 3. Customised Table Rows and Columns`_ - `Example 4. Locating shapes at a Table cell`_ Example 1. Table Basics +++++++++++++++++++++++ `^ `_ .. |tb0| image:: images/customised/table_defaults.png :width: 330 ===== ====== |tb0| This example shows the Table constructed using the command with these properties: .. code:: python Table(cols=2, rows=2) Table(y=2.5, width=3, height=2, cols=3, rows=4) The first Table has the following properties: - top-left corner at defaults of x-position ``1`` cm and y-position ``1`` cm - *height* and *width* of default ``1`` cm each There are 2 rows |dash| in the y-direction |dash| and 2 columns in |dash| the x-direction. This is the minimum allowed. The second Table has the following properties: - top-left corner at x-position ``1`` cm and y-position ``2.5`` cm - *height* and *width* of ``3`` cm and ``2`` cm respectively There are 4 rows |dash| in the y-direction |dash| and 3 columns in |dash| the x-direction. Each row is equal in size as is each column. ===== ====== Example 2. Customised Table +++++++++++++++++++++++++++ `^ `_ .. |tb1| image:: images/customised/table_custom.png :width: 330 ===== ====== |tb1| This example shows the Table constructed using the command with these properties: .. code:: python Table(y=0, width=3, height=2.5, cols=5, rows=6, stroke="red", dotted=True) Table(y=3, x=0, cols=[0.5, 1, 1.25, 0.75], rows=[0.75, 0.5, 0.5, 0.75], stroke="blue", fill="aqua", borders=('*', 2, "grey")) The first Table has the following properties: - starts at x-position ``1`` cm and y-position ``0`` cm - *height* and *width* of ``2.5`` cm and ``3`` cm respectively - *stroke* color of ``red`` - *dotted* border lines for each Rectangle The second Table has the following properties: - starts at x-position ``1`` cm and y-position ``3`` cm - *cols* is a list of column widths - *rows* is a list of row heights - *stroke* color of ``blue`` - *fill* color of ``aqua`` - *borders* all around of color ``grey`` with a stroke width of ``2``; a border set can contain, in this order: - *direction* - one of n(orth), s(outh), e(ast) or w(est), or all(*) - *width* - the line thickness - *color* - either a named color or a hexadecimal value - *style* - ``True`` makes it dotted; a list of values creates dashes ===== ====== Example 3. Customised Table Rows and Columns ++++++++++++++++++++++++++++++++++++++++++++ `^ `_ .. |tb2| image:: images/customised/table_rows_cols.png :width: 330 ===== ====== |tb2| This example shows the Table constructed using the command with these properties: .. code:: python t1 = Table( x=0.5, y=0.5, width=3, height=2, cols=5, rows=5, disable_row=True, stroke="red", stroke_width=1, fill="gold", borders=('e w', 2, "grey"), ) t2 = Table( x=0, y=3, cols=[0.5, 1, 1.25, 0.75], rows=[0.75, 0.5, 0.5, 0.5, 0.75], disable_col=True, stroke="grey", stroke_width=1, fill="aqua", borders_header=('n s', 2, "black"), borders_footer=('s', 2, "red", True), ) The first Table has the following properties: - starts at x-position ``0.5`` cm and y-position ``0.5`` cm - *height* and *width* of ``2`` cm and ``3`` cm respectively - *fill* color of ``gold`` - *stroke* color of ``red`` - *stroke_width* of ``1`` - *borders* of ``grey`` set to the east (right) and west (left) The first table also has the setting ``disable_row=True`` which means that no **row** lines are drawn. The second Table has the following properties: - starts at x-position ``0`` cm and y-position ``3`` cm - *cols* is a list of column widths - *rows* is a list of row heights - *stroke* color of ``grey`` - *stroke_width* of ``1`` - *fill* color of ``aqua`` The second table also has the setting ``disable_col=True`` which means that no **column** lines are drawn. In addition, the second table also demonstrates the use of border styles for the first |dash| header |dash| and last |dash| footer |dash| rows. The syntax for these styles follows that for the table borders |dash| see `Example 2. Customised Table`_. ===== ====== Example 4. Locating shapes at a Table cell ++++++++++++++++++++++++++++++++++++++++++ `^ `_ To locate a shape where a Table's cell has been drawn, you can make use of the Table's ``cell`` property to reference its top-left point (``xy`), centre point (``cxy``) or ``height`` and ``width``. A cell is referenced using a spreadsheet-style notation, linked to the name assigned to the Table; for example, if the Table is called ``T1`` then a reference to the top-left cell would be ``T1.cell("A1")``. The height of that cell would be referenced as ``T1.cell("A1").height``. .. NOTE:: It should be noted that **protograf** itself has no concept of anything being "contained" in a cell, nor does it have any mechanism to ensure that shapes are drawn within what might appear to be cell boundaries. It is up to the script author to ensure that the shapes are positioned and sized correctly to achieve this! .. |tb3| image:: images/customised/table_cells.png :width: 330 ===== ====== |tb3| This example shows the Table constructed using the command with these properties: .. code:: python tt = Table( x=0.25, y=1, cols=[1, 2, 0.75], rows=[0.75, 1, 1.25, 0.75], stroke="grey", stroke_width=0.5, borders_header=('n s', 1, "black"), borders_footer=('s', 1, "black"), padding=0.05, ) picture = "fantasy-forest-with-old-bridges-crop.jpg" Image( picture, xy=tt.cell("A2").xy, height=tt.cell("A2").height) Image( picture, xy=tt.cell("A3", 0, 0).xy, height=tt.cell("A3", 0, 0).height) Image( picture, xy=tt.cell("A4").xy, height=tt.cell("A4").height) Text( xy=tt.cell("B2").xy, height=tt.cell("B2").height, width=tt.cell("B2").width, html=True, box_fill="orange", text="""All the King's ponies and all the King's men? """) Text( xy=tt.cell("B3").xy, height=tt.cell("B3").height, width=tt.cell("B3").width, html=True, box_fill="tan", text="""Now is the time for all good men to come to the aid of their party. """) Text( xy=tt.cell("B4").xy, height=tt.cell("B4").height, width=tt.cell("B4").width, html=True, box_fill="silver", text="""Fly, you fools! """) Circle( cxy=tt.cell("C2").cxy, radius=0.25, fill="tomato") Square( cxy=tt.cell("C3").cxy, side=0.5, fill="green") Hexagon( cxy=tt.cell("C4").cxy, side=0.25, fill="aqua") This example shows a table, styled in a similar way to previous examples, and assigned the name ``tt``. This table also has a *padding* of ``0.05`` cm. The ``Text``, ``Image`` and various geometric shapes make use of the attributes of the ``tt`` table to locate and size themselves. A cell is referenced using a spreadsheet-style notation |dash| here the upper-left cell is ``tt.cell("A1")`` and the lower-right cell is ``tt.cell("C3")``. A cell's attributes include its top-left point (``xy`), its centre point (``cxy``), as well as ``height`` and ``width``. It should be noted that: - The ``Images`` get "sized" to fit their cell, making use of the resize option that is triggered when only their height is supplied; setting the width to maintain their aspect ratio. The image in cell "A3" overrides the padding via the ``0, 0`` to reach the top and bottom of the cell. - The ``Text`` boxes are sized to fit in the cell's ``height`` and ``width``, as can be seen by the extent of their colored box. The ``html=True`` means that text is auto-sized to fit into the available space (which includes the padding). - The various geometric shapes are centred in the cells; but should they be assigned a larger size they would "overflow" the apparent cell borders. ===== ====== .. _shapes-common-properties: Shapes Common Properties ------------------------ `↑ `_ The following are properties common to many shapes that can be set to create the desired output: - `x and y`_ - `cx and cy`_ - `Centre Shape`_ - `Centre Shapes`_ - `Dot and Cross`_ - `Fill and Stroke`_ - `Rotation`_ - `Radii Shapes`_ - `Perbii Shapes`_ - `Text Descriptions`_ - `Transparency`_ - `Vertex Shapes`_ - `Wave Styles`_ .. NOTE:: The term "common" in this section is referring to the concept that many shapes are properrties with the same names and similar behaviour. This makes it a bit easier to use and remember them. In **protograf** the :ref:`Common command ` has the specific meaning of setting the same property value(s) to be used in multiple shapes in the same script |dash| as seen in various of the examples here. .. _coreShapeXY: x and y ~~~~~~~ `^ `_ Almost every shape will need to have its :ref:`position ` set. "Position" here usually refers to a point corresponding to the top-left of that shape. The common way to do this is by setting a value for **x** |dash| the distance from the left margin of the page (or card) to the left edge of the shape; and/or **y** |dash| the distance from the top margin of the page (or card) to the top edge of the shape. .. NOTE:: Its more appropriate to think of this position as that of the "bounding box" of the shape i.e. imagine a rectangle drawn such that the shape just fits inside it; the "position" is the point corresponding to the top-left of that imaginary Rectangle. .. _coreShapeCxCy: cx and cy ~~~~~~~~~ `^ `_ Almost every shape will need to have its :ref:`position ` set. "Position" here refers to a point corresponding to the centre of that shape. For shapes that support it, the way to do this is by setting a value for **cx** |dash| the distance from the left margin of the page (or card) to the centre position of the shape and/or **cy** |dash| the distance from the bottom margin of the page (or card) to the centre position of the shape. .. _coreShapeFillStroke: Fill and Stroke ~~~~~~~~~~~~~~~ `^ `_ Almost every single shape will have a *stroke*, corresponding to the color of the line used to draw it, and a *stroke_width* which is the thickness in points (72 points per inch); the default line color is *black*. All `Enclosed Shapes`_ will have a *fill* corresponding to the color used for the area inside it; the default fill color is *white*. A "shortcut" to setting both fill and stroke to be the same for a shape, is to use the property *fill_stroke* (see Example 2 below). If the fill is set to the :ref:`keyword ` ``None`` (note the uppercase "N"), the area will have no fill color, and effectively becomes transparent. If the stroke is set to the :ref:`keyword ` ``None`` (note the uppercase "N"), the line will have no color, and effectively becomes transparent. Example 1. Fill & Stroke ++++++++++++++++++++++++ `↑ `_ .. |fsb| image:: images/defaults/fill-stroke.png :width: 330 ===== ====== |fsb| This example shows a shape constructed using the command: .. code:: python Rectangle( fill="yellow", stroke="red", stroke_width=6) The shape has the following properties that differ from the defaults: - *fill* color of ``yellow`` for the interior of the shape - *stroke* color of ``red`` for the border of the shape - *stroke_width* - set to ``6`` points (about 2mm or 0.2cm) It can be seen that very thick lines "straddle" a centre line running through the defined location. In this case the Rectangle is both larger in outer dimensions than the expected 1x1 cm and smaller in inner dimensions than the expected 1x1 cm due to the thickness of the lines used to construct it. ===== ====== Example 2. Fill_Stroke ++++++++++++++++++++++ `↑ `_ The *fill_stroke* property is a "shortcut" which sets **both** the *fill* and *stroke* color at same time. .. |fst| image:: images/defaults/fill-and-stroke.png :width: 330 ===== ====== |fst| This example shows a shape constructed using the command: .. code:: python Circle(fill_stroke="cyan") The shape has the following property that differ from the defaults: - *fill_stroke* color of ``cyan`` Here, the line color used to draw the circumference is the same as the fill color of the interior. ===== ====== .. _coreShapeDotCross: Dot and Cross ~~~~~~~~~~~~~ `^ `_ For shapes that have a definable centre e.g. a `Circle`_, a `Square`_ or a `Hexagon`_, it is possible to place a dot, a cross, or both at this location. The color for the dot and cross will, if not provided, take on the stroke color of the shape of which they are part |dash| see the `Stadium` example below. .. |dnc| image:: images/customised/dots_crosses.png :width: 330 ===== ====== |dnc| This example shows various shapes constructed using the following commands: .. code:: python Stadium( cx=1, cy=1, side=0.66, stroke="blue", dot=0.1) Stadium( cx=3, cy=1, side=0.66, stroke="blue", cross=0.25, cross_stroke_width=1) Polygon( cx=1, cy=3, sides=8, radius=1, dot=0.1, dot_stroke="orange") Polygon( cx=3, cy=3, sides=8, diameter=2, cross=0.25, cross_stroke="orange", cross_stroke_width=1) Rhombus( cx=1, cy=5, side=1.25, dot=0.1, dot_stroke="red") Rhombus( cx=3, cy=5, side=1.25, cross=0.25, cross_stroke="red", cross_stroke_width=1) The shapes have their properties set as follows: - *cx* and *cy* set the centre point of the shape - *dot* - sets the size of dot at the centre - *dot_stroke* - sets the color (and fill) of the dot; defaults to match the *stroke* of the shape that it is part of - *cross* - sets the length of each of the two lines that cross at the centre - *cross_stroke* - sets the color of the cross lines; defaults to the stroke of the shape that it is part of - *cross_stroke_width* - sets the thickness of the cross lines ===== ====== .. _coreShapeRotation: Rotation ~~~~~~~~ `^ `_ Every shape, whose *centre* can be calculated, will support a *rotation* property. Rotation takes place in anti-clockwise direction, from the horizontal, around the centre of the shape, in *degrees*. Example 1. Rhombus Rotation +++++++++++++++++++++++++++ `↑ `_ .. |rt1| image:: images/customised/rhombus_red_rotation.png :width: 330 ===== ====== |rt1| This example shows the shape constructed using these commands: .. code:: python Rhombus( cx=2, cy=3, width=1.5, height=2*equilateral_height(1.5), fill=None, stroke="black", dot=0.06) Rhombus( cx=2, cy=3, width=1.5, height=2*equilateral_height(1.5), fill=None, stroke="red", dot=0.03, rotation=60) The shape with the *black* outline and large dot in the centre is the "normal" Rhombus. The shape with the *red* outline and smaller, red dot in the centre is the rotated Rhombus. It has these properties: - *fill* color - `None` so no fill is used; this makes it completely transparent. - *rotation* - ``60`` is the number of degrees, anti-clockwise, that it has been rotated The shapes are completely transparent, so its possible to see how the second is drawn relative to the first. ===== ====== Example 2. Polygon Rotation +++++++++++++++++++++++++++ `↑ `_ .. |rt2| image:: images/customised/polygon_rotation_flat.png :width: 330 ===== ====== |rt2| This example shows five Polygons constructed using the command with additional properties. Note the use of the :ref:`Common command ` to allow multiple Polygons to share the same properties. .. code:: python poly6 = Common( fill=None, sides=6, diameter=1, stroke_width=1) Polygon(common=poly6, y=1, x=1.0, label="0") Polygon(common=poly6, y=2, x=1.5, rotation=15, label="15") Polygon(common=poly6, y=3, x=2.0, rotation=30, label="30") Polygon(common=poly6, y=4, x=2.5, rotation=45, label="45") Polygon(common=poly6, y=5, x=3.0, rotation=60, label="60") The examples have the following properties: - *centre* - using `cx` and `cy` values - *radius* - ``1`` cm in each case - *sides* - the default of 6 in each case ("hexagon" shape) - *rotation* - varies from 0 |deg| to 60 |deg| (anti-clockwise from the horizontal) ===== ====== Example 3. Shapes Rotation ++++++++++++++++++++++++++ `↑ `_ .. |rt3| image:: images/customised/shape_rotation.png :width: 330 ===== ====== |rt3| This example shows different shapes constructed using commands with some :ref:`Common ` properties: .. code:: python props = Common( stroke="black", cross=0.5, cross_stroke="red", cross_stroke_width=1, rotation=45, label_size=6) Star( x=1, y=1, vertices=5, radius=0.75, common=props, label="star") Ellipse( cx=3, cy=1, height=1, width=1.5, common=props, label="ellipse") Polygon( cx=1, cy=3, sides=6, side=0.75, common=props, label="polygon") Stadium( cx=3, cy=3, side=0.6, common=props, label="stadium") Rectangle( cx=1, cy=5, height=1, width=1.5, common=props, label="rectangle") Rhombus( cx=3, cy=5, side=1.25, common=props, label="rhombus") The shapes share common properties for the cross at the centre, with a rotation of 45 |deg| each. ===== ====== Example 4. Rotation with Hatches ++++++++++++++++++++++++++++++++ `↑ `_ .. |rt4| image:: images/customised/shape_hatches_and_rotation.png :width: 330 ===== ====== |rt4| This example shows different shapes constructed using commands with some ``Common`` properties for the ``hatches`` effect: .. code:: python htch = Common( fill='lightgray', stroke=None, hatches_count=5, hatches='w', hatches_stroke="red", hatches_stroke_width=0.75, rotation=30) Hexagon( common=htch, cx=2, cy=1, height=1.5, ) Triangle( common=htch, cx=1, cy=3, side=1.5, ) Circle( common=htch, cx=3, cy=3, radius=0.75, ) Rectangle( common=htch, x=0.5, y=4, height=1.5, width=1, ) Rhombus( common=htch, cx=3, cy=5, height=2, width=1.5, ) The shapes share common properties for the number, direction and style of hatches, with a rotation of 30 |deg| each. ===== ====== .. _coreRadiiShapes: Radii Shapes ~~~~~~~~~~~~ `^ `_ A number of shapes, that are formed by drawing lines between a set of vertices ("corner points"), can be styled by placing other shapes which can be located relative to those vertices. Radii shapes are constructed using the following properties: - *radii_shapes* - this a list (values in ``[...]``) of value sets that determine the "where and what" should be drawn along the line of specific radius. Each set, enclosed in brackets ``(...)`` can consist of three comma-separated parts: - the first is the **direction**, or directions, of the relevant radii; this can be a string e.g. ``"n e"`` or a list e.g. ``["n", "e"]``. Note that for a ``Circle`` the direction is the number of degrees (anti-clockwise from 0 |deg| in the east direction) whereas for other shapes it will be a :ref:`compass direction `. - the second is the **shape** to be drawn - the optional third part is the **fractional distance** along the line at which the shape should be drawn; by default this is ``1`` i.e. the length of the radial line |dash| if it is less than ``1`` the radii shape will be drawn inside of the parent shape; and if it is more than ``1`` the radii shape will be drawn outside of, or away from, the parent shape - *radii_shapes_rotated* - an optional property which, if ``True``, will rotate the vertex shapes such they "point" away from the centre of the parent shape Radii shapes can be constructed for: - :ref:`Circle ` - :ref:`Hexagon ` - :ref:`Rectangle ` - :ref:`Rhombus ` - :ref:`Triangle ` Example 1. Radii Shapes ++++++++++++++++++++++++ .. |vr1| image:: images/customised/radii_shapes.png :width: 330 ===== ====== |vr1| This example shows radii shapes constructed as follows: .. code:: python ccom = Common(radius=0.15, fill="gold", label_size=6) Hexagon( cx=1, cy=1, radius=0.8, orientation="pointy", radii_shapes=[ ('n', circle(common=ccom, label="n")), ('se', circle(common=ccom, label="se"), 1.25), ('sw', circle(common=ccom, label="sw"), 0.5 ), ], radii_shapes_rotated=True, ) Hexagon( cx=3, cy=1, radius=0.8, radii_shapes=[ ('ne', circle(common=ccom, label="ne")), ('se', circle(common=ccom, label="se"), 1.25), ('sw', circle(common=ccom, label="sw"), 0.5), ], radii_shapes_rotated=True, ) Rectangle( cx=1, cy=3, height=1, width=1.5, radii_shapes=[ ('ne', circle(common=ccom, label="ne")), ('se', circle(common=ccom, label="se")), ('sw', circle(common=ccom, label="sw")), ('nw', circle(common=ccom, label="nw")), ], radii_shapes_rotated=True, ) Rhombus( cx=3, cy=3, width=1, height=1.5, radii_shapes=[ ('n', circle(common=ccom, label="n")), ('s', circle(common=ccom, label="s")), ('e', circle(common=ccom, label="e")), ('w', circle(common=ccom, label="w")), ], radii_shapes_rotated=True, ) Triangle( cx=1, cy=5, side=1.25, radii_shapes=[ ('n', circle(common=ccom, label="n")), ('se', circle(common=ccom, label="se")), ('sw', circle(common=ccom, label="sw")), ], radii_shapes_rotated=True, ) Circle( cx=3, cy=5, radius=0.75, radii_shapes=[ ('30 90 150 210 270 330', circle(common=ccom, label="A")), ], radii_shapes_rotated=True, ) All of these examples use a common settings in ``ccom`` to draw the vertex shape |dash| a circle; but the *label* property is set as a visual indicator. Note that in some cases, setting the *fractional_distance* (the third value in the set) causes the radii shape to be moved closer to, or further away from, the centre. The use of ``radii_shapes_rotated=True`` will means all of these examples have the radii shapes rotated to face "away" from the parent shape's centre. .. HINT:: Although not shown above, multiple entries can be made for a given direction; for example, to draw a circle and a dot along the ``ne`` radius for a rectangle |dash| in the case below the ``Dot()`` will be drawn halfway between the rectangle's centre and it's north-east corner: .. code:: python Rectangle( cx=1, cy=3, height=1, width=1.5, radii_shapes=[ ('ne', circle(common=ccom, label="ne")), ('ne', dot(), 0.5), ], radii_shapes_rotated=True, ) ===== ====== .. _corePerbiiShapes: Perbii Shapes ~~~~~~~~~~~~~ `^ `_ A number of shapes, that are formed by drawing lines between a set of vertices ("corner points"), can be styled by placing other shapes which can be located relative to the centre point of lines joining those vertices. Perbii shapes are constructed using the following properties: - *perbii_shapes* - this a list (values in ``[...]``) of value sets that determine the "where and what" should be drawn along the line of specific perbis. Each set, enclosed in brackets ``(...)`` can consist of three comma-separated parts: - the first is the **direction**, or directions, of the relevant perbii; this can be a string e.g. ``"n e"`` or a list e.g. ``["n", "e"]``. Note that this will be a :ref:`compass direction `. - the second is the **shape** to be drawn - the optional third part is the **fractional distance** along the line at which the shape should be drawn; by default this is ``1`` i.e. the length of the perbis line |dash| if it is less than ``1`` the perbii shape will be drawn inside of the parent shape; and if it is more than ``1`` the perbii shape will be drawn outside of, or away from, the parent shape - *perbii_shapes_rotated* - an optional property which, if ``True``, will rotate the vertex shapes such they "point" away from the centre of the parent shape Perbii shapes can be constructed for: - :ref:`Hexagon ` - :ref:`Rectangle ` - :ref:`Rhombus ` - :ref:`Triangle ` .. NOTE:: Actually, because a Rhombus can have an "elongated" shape, it is not really possible to have true perbis lines for this shape. What is drawn are lines from the midpoints of each side to the centre. This in turn means that rotated shapes can seem to have an "awkward" angle |dash| use with care. The same is true for any non-equilateral Triangle. Example 1. Perbii Shapes ++++++++++++++++++++++++ .. |vr2| image:: images/customised/perbii_shapes.png :width: 330 ===== ====== |vr2| This example shows perbii shapes constructed as follows: .. code:: python ccom = Common(radius=0.15, fill="gold", label_size=6) Hexagon( cx=1, cy=1, radius=0.8, orientation="pointy", perbii_shapes=[ ('ne', circle(common=ccom, label="ne")), ('se', circle(common=ccom, label="se"), 1.25), ('w', circle(common=ccom, label="w"), 0.5 ), ], perbii_shapes_rotated=True, ) Hexagon( cx=3, cy=1, radius=0.8, perbii_shapes=[ ('n', circle(common=ccom, label="n")), ('se', circle(common=ccom, label="se"), 1.25), ('sw', circle(common=ccom, label="sw"), 0.5), ], perbii_shapes_rotated=True, ) Rectangle( cx=1, cy=3, height=1, width=1.5, perbii_shapes=[ ('n', circle(common=ccom, label="n")), ('s', circle(common=ccom, label="s.")), ('w', circle(common=ccom, label="w")), ('e', circle(common=ccom, label="e")), ], perbii_shapes_rotated=True, ) Rhombus( cx=3, cy=3, width=1, height=1.5, perbii="ne se nw sw", perbii_shapes=[ ('ne', circle(common=ccom, label="ne")), ('se', circle(common=ccom, label="se")), ('nw', circle(common=ccom, label="nw")), ('sw', circle(common=ccom, label="sw")), ], perbii_shapes_rotated=True, ) Triangle( cx=1, cy=5, side=1.25, perbii_shapes=[ ('ne', circle(common=ccom, label="ne")), ('s', circle(common=ccom, label="s.")), ('nw', circle(common=ccom, label="nw")), ], perbii_shapes_rotated=True, ) All of these examples use a common settings in ``ccom`` to draw the vertex shape |dash| a circle; and the *label* property is set as a visual indicator. Note that in some cases, setting the *fractional_distance* (the third value in the set) causes the perbii shape to be moved closer to, or further away from, the centre. The use of ``perbii_shapes_rotated=True`` will means all of these examples have the perbii shapes rotated to face "away" from the parent shape's centre. As per the note above, the *perbii_shapes* for the Rhombus are aligned in the direction of the *perbii* lines and **not** with the shape's edges. .. HINT:: Although not shown above, multiple entries can be made for a given direction; for example, to draw a circle and a dot along the ``n`` perbis for a rectangle |dash| in the case below the ``Dot()`` will be drawn halfway between the rectangle's centre and it's north edge: .. code:: python Rectangle( cx=1, cy=3, height=1, width=1.5, perbii_shapes=[ ('n', circle(common=ccom, label="n")), ('n', dot(), 0.5), ], perbii_shapes_rotated=True, ) ===== ====== .. _coreShapeText: Text Descriptions ~~~~~~~~~~~~~~~~~ `^ `_ Being able to associate a description, or identifier, with a shape can be useful. There are three kinds of text that can be added to a shape, without having to specify their location or other details. .. NOTE:: Obviously, a `Text`_ shape can also be placed anywhere, including being superimposed on another shape, in order to handle more complex text needs. The three "simple" text types that can be added to a shape are: - *heading* - this appears above the shape (slightly offset) - *label* - this appears in the middle of the shape - *title* - this appears below the shape (slightly offset) All types are, by default, centred horizontally. Each type can be customised in terms of its color, size and font family by appending *_stroke*, *_size* and *_font* respectively to the text type's name; so ``heading_font="Courier`` will set the font family for the heading appearing above the shape. The *label* text can, in addition, be **moved** relative to the shape's centre by using the *mx* and *my* properties; positive values will move the text to the right and down; and negative values will move the text to the left and up. Example 1. Heading, Label and Title +++++++++++++++++++++++++++++++++++ `↑ `_ .. |tx1| image:: images/customised/descriptions.png :width: 330 ===== ====== |tx1| This example shows two shapes constructed using these commands to change default properties: .. code:: python Hexagon( cx=2, cy=1.5, height=1.5, title="Title", label="Label", heading="Heading") Rectangle( x=0.5, y=3, width=3, height=2, label="red; size=14", label_stroke="red", label_size=14) The Hexagon shows where the *heading*, *label* and *title* appear relative to the shape's boundaries, with default font size of 12 points. The Rectangle shows how the *label* can be customised in terms of its *stroke* (``red``) and font *size* (``14`` points). ===== ====== Example 2. Label Offsets ++++++++++++++++++++++++ `↑ `_ .. |tx2| image:: images/customised/label_offset.png :width: 330 ===== ====== |tx2| This example shows six Rectangles constructed using the command with additional properties: .. code:: python rct = Common( height=1.0, width=1.75, stroke_width=0.5, label_size=7) Rectangle( common=rct, x=0, y=0.0, label="offset -x, -y", label_mx=-0.2, label_my=-0.2) Rectangle( common=rct, x=0, y=1.5, label="offset -x", label_mx=-0.3) Rectangle( common=rct, x=0, y=3.0, label="offset -x, +y", label_mx=-0.2, label_my=0.2) Rectangle( common=rct, x=2, y=0.0, label="offset +x, -y", label_mx=0.2, label_my=-0.2) Rectangle( common=rct, x=2, y=1.5, label="offset +x", label_mx=0.3) Rectangle( common=rct, x=2, y=3.0, label="offset +x, +y", label_mx=0.2, label_my=0.2) Rectangle( common=rct, x=0, y=4.5, label="offset -y", label_my=-0.2) Rectangle( common=rct, x=2, y=4.5, label="offset +y", label_my=0.2) Setting values for *label_my* and *label_mx* cause the label to shift away from centre. Positive values move the label down and to the right while negative values move it up and to the left. ===== ====== .. _coreTransparency: Transparency ~~~~~~~~~~~~ `^ `_ All `Enclosed Shapes`_, that have a *fill*, can have a transparency value set that will affect the fill color used for the area inside them. If a shape needs to be completely transparent - i.e. no color at all being visible - then set the *fill* value to ``None``. .. |trn| image:: images/defaults/transparency.png :width: 330 ===== ====== |trn| This example shows a number of Rectangles constructed as follows: .. code:: python Rectangle( x=1, y=3, height=1, width=2, fill="#008000", stroke="#C0C0C0", transparency=25, label="25%") Rectangle( x=1, y=4, height=1, width=2, fill="#008000", stroke="#C0C0C0", transparency=50, label="50%") Rectangle( x=1, y=5, height=1, width=2, fill="#008000", stroke="#C0C0C0", transparency=75, label="75%") Rectangle( x=0, y=0, height=2, width=2, fill="yellow", stroke="yellow") Rectangle( x=1, y=1, height=2, width=2, fill="red", stroke="red", transparency=50) The three green Rectangles shapes have the following property set: - *transparency* - the higher the value, the more "see through" the color The red Rectangle, which also has a *transparency* value, is drawn partially over the yellow Rectangle on the upper-left. When overdrawn, there is a color change in the overlapping section i.e. "bleed through" occurs. ===== ====== .. _coreCentreShape: Centre Shape ~~~~~~~~~~~~ `^ `_ Any shape that can be defined using its centre, may have another shape |dash| called a "centre shape" |dash| placed inside of it. .. NOTE:: In terms of drawing order, the "centre shape" is drawn after most of the shape's other properties: only a dot, cross or label (if any of these are defined) will be drawn superimposed on the centre-shape. Example 1. Default Centre Shape +++++++++++++++++++++++++++++++ `↑ `_ .. |cs0| image:: images/customised/shape_centred.png :width: 330 ===== ====== |cs0| This example shows a number of shapes constructed as follows: .. code:: python small_star = star(radius=0.25) Polygon( cx=1, cy=5, radius=0.5, sides=8, centre_shape=small_star) Triangle( x=2.35, y=5.5, side=1.25, centre_shape=small_star) Rectangle( x=0.5, y=2.5, height=1, width=1.25, centre_shape=small_star) Circle( cx=3, cy=3, radius=0.5, centre_shape=small_star) Hexagon( x=0.5, y=0.5, height=1, centre_shape=small_star) Square( x=2.5, y=0.5, height=1, centre_shape=small_star) At the start, a Star shape is defined by the lowercase ``star()`` A lowercase command means the shape is not drawn at this time but is assigned to a named value and can be referred to further on. Each of the other shapes in the script can now use this named shape as their ``centre_shape``. Regardless of whether the primary shape's position is defined using ``x`` and ``y``, or ``cx`` and ``cy``, the Star is still drawn in the centre of that shape. ===== ====== Example 2. Off-Centre +++++++++++++++++++++ `↑ `_ .. |cs1| image:: images/customised/shape_centred_move.png :width: 330 The centre-shape can be shifted from the centre by setting values for *centre_shape_mx* and *centre_shape_my*. ===== ====== |cs1| This example shows two Hexagon shapes constructed as follows: .. code:: python small_star = star(radius=0.25) small_circle = circle( radius=0.33, fill="gray", centre_shape=small_star) Hexagon( x=1, y=0.5, height=2, hatches_count=5, dot=0.1, centre_shape=small_circle) Hexagon( x=1, y=3, height=2, centre_shape=small_circle, centre_shape_mx=0.3, centre_shape_my=0.6) As in the first example, the ``small_star`` is defined but not drawn. The ``small_star`` is assigned as the ``centre_shape`` to ``small_circle``; a shape that is also not drawn. This ``small_circle`` is now used as the ``centre_shape`` for both of the Hexagons. The upper Hexagon shows how the centre-shape is drawn over other features, such as the hatches, in the Hexagon, **except** for the ``dot``. The lower Hexagon shows how the centre-shape can be moved with the ``*_mx`` and ``*_my`` values. Positive values move the shape down and to the right while negative values move it up and to the left. ===== ====== Example 3. Customised Centres +++++++++++++++++++++++++++++ `↑ `_ .. |cs2| image:: images/customised/shape_centred_custom.png :width: 330 The centre-shape can be any type of shape that has a defined centre; and this shape itself can be customised. ===== ====== |cs2| This example shows Rectangle shapes, each constructed with a ``centre_shape`` as follows: .. code:: python Rectangle(x=0, y=1, side=1, centre_shape=polygon( radius=0.4, sides=7, fill=None, perbii='*', stroke="red")) Rectangle(x=1, y=2, side=1, centre_shape=circle( radius=0.3, radii=[0,60,120,180,240,300], fill=None, stroke="green")) Rectangle(x=2, y=1, side=1, centre_shape=hexagon( radius=0.4, stroke="purple", fill=None, borders=[("sw n se", 2)])) Rectangle(x=3, y=2, side=1, centre_shape=stadium( side=0.4, stroke="orange")) Rectangle(x=0, y=3, side=1, centre_shape=ellipse( height=0.8, width=0.5, fill=None, stroke="olive")) Rectangle(x=1, y=4, side=1, centre_shape=square( side=0.6, stroke="gold", fill=None, hatches='d', hatches_count=5, borders=[("n s", 2, "black")])) Rectangle(x=2, y=3, side=1, centre_shape=rhombus( side=0.8, stroke="gray", fill=None, borders=[("ne sw", 2, "black")])) Rectangle(x=3, y=4, side=1, centre_shape=trapezoid( width=0.6, top=0.4, height=0.8, stroke="aqua", fill=None, flip='south', borders=[("e w", 2, "black")])) These various shapes and their custom properties are described elsewhere in the documentation; this example just serves to show how they can be used together. ===== ====== .. _coreCentreShapes: Centre Shapes ~~~~~~~~~~~~~ `^ `_ Any shape that can be defined using its centre, may have multiple shapes |dash| called "centre shapes" |dash| placed inside of it. These shapes are supplied as a list of sets; for example ``[(shape1), (shape2)]`` .. NOTE:: In terms of drawing order, the "centre shapes" are drawn after most of the shape's other properties: only a dot, cross or label (if any of these are defined) will be drawn superimposed on the centre shapes. Example 1. Centres ++++++++++++++++++ .. |ss1| image:: images/customised/shapes_centred.png :width: 330 Any centre-shape can be shifted from the centre by setting values for the change in ``x`` and ``y`` values as part of the set. ===== ====== |ss1| This example shows centre shapes constructed as follows: .. code:: python small_dot = dot(dot_width=4, fill="red") big_dot = dot(dot_width=8) Hexagon( x=0.5, y=0.5, height=1, centre_shapes=[ (small_dot), (big_dot, 0.2, 0.2)]) Rhombus( x=2.4, y=0.3, height=1.5, width=1.25, centre_shapes=[ (small_dot), (big_dot, 0.2, 0.2)]) Rectangle( x=0.5, y=2.5, height=1, width=1.25, centre_shapes=[ (small_dot), (big_dot, 0.2, 0.2)]) Circle( cx=3, cy=3, radius=0.5, centre_shapes=[ (small_dot), (big_dot, 0.2, 0.2)]) Polygon( cx=1, cy=5, radius=0.5, sides=8, centre_shapes=[ (small_dot), (big_dot, 0.2, 0.2)]) Triangle( x=2.35, y=5.5, side=1.25, centre_shapes=[ (small_dot), (big_dot, 0.2, 0.2)]) At the start, two Dot shapes are defined by the lowercase ``dot()`` A lowercase command means the shapes are not drawn at this time but assigned to named values and can be referred to further on. Each of the other shapes in the script can now use these named shapes as their ``centre_shapes``. The ``small_dot`` (which is red )is used "as is" and so by default is drawn at the centre of its parent shape; the ``big_dot`` is given offset values for the x and y positions - so is drawn below and to the right of centre. ===== ====== .. _coreVertexShapes: Vertex Shapes ~~~~~~~~~~~~~ `^ `_ A number of shapes, that are formed by drawing lines between a set of vertices ("corner points"), can be styled by placing other shapes which will be centered on those points. Vertex shapes are constructed using the following properties: - *vertex_shapes* - this a list (values in ``[...]``) of shapes that should be placed on vertices, in the order that these vertices are drawn - *vertex_shapes_rotated* - an optional setting which, if ``True``, will rotate the vertex shapes such they "point" away from the centre of the parent shape Vertex shapes can be constructed for: - :ref:`Hexagon ` - :ref:`Polygon ` - :ref:`Rectangle ` - :ref:`Rhombus ` - :ref:`Star ` - :ref:`Triangle ` Example 1. Vertex Shapes ++++++++++++++++++++++++ .. |vs1| image:: images/customised/vertex_shapes.png :width: 330 ===== ====== |vs1| This example shows vertex shapes constructed as follows: .. code:: python Rectangle( cx=1, cy=1, height=1, width=1.5, vertex_shapes=[ circle(radius=0.15, label="R")] * 4, vertex_shapes_rotated=True) Hexagon( cx=3, cy=1, radius=1, vertex_shapes=[ circle(radius=0.15, label="H")] * 6, vertex_shapes_rotated=True) Polygon( cx=1, cy=3, sides=5, radius=1, vertex_shapes=[ circle(radius=0.15, label="P")] * 5, vertex_shapes_rotated=True) Trapezoid( cx=3, cy=3, width=1.5, top=1, height=1.25, vertex_shapes=[ circle(radius=0.15, label="T")] * 5, vertex_shapes_rotated=True) Triangle( cx=1, cy=5, side=1.5, vertex_shapes=[ circle(radius=0.15, label="E")] * 3, vertex_shapes_rotated=True) Star( cx=3, cy=5, radius=1, rays=5, vertex_shapes=[ circle(radius=0.15, label="S")] * 5, vertex_shapes_rotated=True) All of these examples use the shortcut approach to create the correct number of vertex shapes i.e. ``[SomeShape] * N`` where ``N`` is the number of repeats of that shape. In other cases, it could be that this is a list of distinctly different shapes. Any vertices which should be omitted can just use the ``None`` value to indicate that. The use of ``vertex_shapes_rotated=True`` will means all of these examples have the vertex shapes rotated to face "away" from the parent shape's centre. ===== ====== .. _coreWave: Wave Styles ~~~~~~~~~~~ `^ `_ A number of shapes, that make use of straight line properties, can be styled using their "wave" property. These include the *radii* and *perbii* properties of Circle, Hexagon, Polygon and Rectangle (for details on those properties, see the section on :doc:`Customised Shapes `). In addition, the lines used to construct a Polyshape and Polyline can also be styled like waves. Example 1. Radii and perbii +++++++++++++++++++++++++++ .. |ws1| image:: images/customised/perbii_styled.png :width: 330 ===== ====== |ws1| This example shows various shapes constructed as follows: .. code:: python Line( x=0, y=0.5, length=1.5, stroke="purple", stroke_width=1, wave_style='wave', wave_height=0.1 ) Line( x=2, y=0.5, length=1.5, stroke="firebrick", stroke_width=1, wave_style='sawtooth', wave_height=0.1 ) Polygon( perbii_stroke="purple", perbii_stroke_width=1, perbii_wave_style='wave', perbii_wave_height=0.1, cx=1, cy=1.5, sides=8, radius=0.75, perbii="2,4,7" ) Polygon( radii_stroke="firebrick", radii_stroke_width=1, radii_wave_style='sawtooth', radii_wave_height=0.1, cx=3, cy=1.5, sides=8, radius=0.75, radii="*") Rectangle( perbii_stroke="purple", perbii_stroke_width=1, perbii_wave_style='wave', perbii_wave_height=0.1, cx=1, cy=3.25, height=1, width=2, perbii="n s e w", ) Circle( radii_stroke="firebrick", radii_stroke_width=1, radii_wave_style='sawtooth', radii_wave_height=0.1, cx=3, cy=3.25, radius=0.75, radii=[60, 180, 300], ) Hexagon( cx=1, cy=5, radius=0.75, perbii='*', perbii_stroke="purple", perbii_stroke_width=1, perbii_wave_style='wave', perbii_wave_height=0.1 ) Hexagon( cx=3, cy=5, radius=0.75, radii="ne se w", radii_stroke="firebrick", radii_stroke_width=1, radii_wave_style='sawtooth', radii_wave_height=0.1 ) The purple lines have: - *perbii_wave_style* set to ``'wave'`` creating a wave-like effect - *perbii_wave_height* set to ``0.1`` for the height of each "peak" The dark red lines have: - *radii_wave_style* set to ``'sawtooth'`` creating a "zig-zag" effect - *radii_wave_height* set to ``0.1`` for the height of each "peak" ===== ====== Example 2. Polyshape and Polyline +++++++++++++++++++++++++++++++++ .. |ws2| image:: images/customised/poly_waves.png :width: 330 ===== ====== |ws2| This example shows poly-shapes constructed as follows: .. code:: python Polyshape( points=[(1,2), (1,1), (2 0), (3,1), (3,2)], wave_style="wave", wave_height=0.03, fill="gold") Polyline( points='1,3 1,4 2,4 4,3', stroke="red", stroke_width=2, wave_style="sawtooth", wave_height=0.03) Polyline( points='1,5 1,6 2,6 4,5', stroke="purple", stroke_width=2, wave_style="wave", wave_height=0.05) The top Polyshape, with a default line stroke, has: - *perbii_wave_style* set to ``'wave'`` creating a wave-like effect - *perbii_wave_height* set to ``0.03`` for the height of each "peak" The dark red thick Polyline has: - *radii_wave_style* set to ``'sawtooth'`` creating a "zig-zag" effect - *radii_wave_height* set to ``0.03`` for the height of each "peak" The purple thick Polyline has: - *perbii_wave_style* set to ``'wave'`` creating a wave-like effect - *perbii_wave_height* set to ``0.05`` for the height of each "peak" ===== ======