vectorvondoom

An SVG-based vector image tool.


License
AGPL-3.0
Install
pip install vectorvondoom==0.5.0

Documentation

Vector von Doom

Vector von Doom is an SVG-based vector image tool.

Installation

Parable requires Python 3.6+.

To install:

pip install vectorvondoom

Usage

The basic workflow for creating an image is: create a new image, add all the elements you want, then export to a file.

Creating a New Image

Images are created using the SVG class:

from victorvondoom import SVG

image = SVG()

You can set the viewport by passing a width and a height, and can set the background colour of the SVG using the background parameter. For any other attribute to set for the base SVG object, you can pass the key-value pair using the attribute parameter:

image = SVG(1920, 1080, background='black')

Adding Elements

Vector von Doom currently supports the SVG Elements: Circle, Rect, and Polygon. See Elements for details on how to use the individual elements.

Elements can be added to the image using the add method:

from victorvondoom import Circle, Point

circle = Circle.new(Point(100, 50), 50, fill='blue')

svg.add(circle)

There are also convenience methods for all supported elements:

svg.circle(Point(100, 50), 50, fill='blue')

For unsupported elements, you can use the add_raw method, passing in the element name and a dict of attributes to directly create the element:

svg.add_raw(
    'text',
    {
        'x': 0, 'y': 100,
        'font-size': 12, 'font-family': "Verdana",
    },
    text="Hello World"
)

Exporting the image

The SVG export method will convert the SVG to bytes

path.write_bytes(svg.export())

Elements

Vector von Doom has built-in support for some SVG elements.

Circle

A circle.

Create a new circle with the new method, passing in the center point and the radius:

from vectorvondoom import Circle, Point

circle = Circle.new(Point(0, 0), 15)

Rect

A rect(angle).

Create a new rectangle with the new method, passing in the top left point and the width and height:

from vectorvondoom import Rect, Point

rectangle = Rect.new(Point(1, 1), 2, 4)

Alternately, you can use the from_box method, to create a new rectangle giving the top left and bottom right points:

from vectorvondoom import Rect, Point

rectangle = Rect.from_box(Point(1, 1), Point(3, 5))

Polygon

A polygon.

Create a new polygon with the new method, passing in an iterable of points:

from vectorvondoom import Polygon, Point

rectangle = Polygon.new([Point(1, 2), Point(3, 2), Point(3, 5)])

Path

A path

Create a new Path with the new method, passing it any number of Path Descriptions:

from vectorvondoom import Path, MoveTo, LineTo, CurveTo, ClosePath, Point

path = Polygon.new(
    MoveTo(Point(50, 50)),
    LineTo(Point(0, -50), relative=True),
    CurveTo(Point(-50, 50), Point(0, 50), relative=True),
    ClosePath()
)

Path Descriptions

Vector von Doom implements the 5 d Attributes:

  • MoveTo: A point to move to. Args:
    • point: The point to move to.
    • relative (optional, False): Whether the supplied point is absolute, or relative to the previous position.
  • LineTo: Draw a line from the current position to a point. Args:
    • point: The end of the line.
    • relative (optional, False): Whether the supplied point is absolute, or relative to the previous position.
  • CurveTo: Draw a Bezier curve from the current position to a point. Args:
    • point: The end of the curve.
    • p1: The first midpoint.
    • p2 (optional, None): The second midpoint. If supplied, the curve will be cubic, otherwise it will be quadratic.
    • relative (optional, False): Whether the supplied points are absolute, or relative to the previous position.
  • ChainTo: Continue a curve using the previous curve's (final) midpoint rotated π radians around its endpoint as the curve's (first) midpoint. Args:
    • point: The end of the curve.
    • p2 (optional, None): The second midpoint. If supplied, the curve will be cubic, otherwise it will be quadratic.
    • relative (optional, False): Whether the supplied points are absolute, or relative to the previous position.
  • ArcTo: Draw an elliptical curve from the current position to a point. Args:
    • point: The end of the curve.
    • x_radius: The curve's radius in the horizontal direction (relative to the axis of rotation).
    • y_radius: The curve's radius in the vertical direction (relative to the axis of rotation).
    • rotation: The axis of rotation of the curve (in radians).
    • large_arc (optional, False): Whether to draw the larger of the two possible arcs.
    • sweep (optional, False): Whether to draw the arc in a clockwise rotation.
    • relative (optional, False): Whether the supplied point is absolute, or relative to the previous position.
  • ClosePath: Draw a line back to the origin.

Custom Shapes

Vector von Doom has some support for shapes that don't exist as native SVG elements. This includes support for shapes which require multiple SVG elements to properly express (done using vectorvondoom.elements.Elements) .

Stars

N-pointed stars can be generated using the Elements.star method, or directly added using the SVG.star convenience method.

Both methods take the following parameters:

  • center: The center of the star.
  • length: The distance of each point from the center.
  • num_points: The number of points on the star.
  • rotation: (optional, -π/2) The rotation of the star. The default rotation will put the first point facing upwards.
  • ngram: (optional, False) Draw the star using n straight lines (e.g., make a pentagram).
  • inner_length: (optional, None) How far from the center to place the midpoint between each point. If ngram is True, this value is ignored. If inner_length is not given, the length will match the intersections of the ngram version of the star.

Transformations

All elements have support for three basic transformations: translate, rotate, and reflect.

NOTE All transformations create a new element, they don't modify the current element.

translate

Move the element by a distance:

circle = Circle.new(Point(1, 0), 3).translate(1, 2)
circle.center() == Point(2, 2)

There also is a recenter convenience method to set the center of the element to an absolute value:

circle = Circle.new(Point(1, 0), 3).recenter(1, 2)
circle.center() == Point(1, 2)

rotate

Rotate the element. The amount is given in radians:

rectangle = Rect.new(Point(0, 1), 2, 3).rotate(math.pi / 2)
rectangle.bounds() == Point(0.5, 2.5), Point(4.5, 5.5)

By default, the center of rotation is the center of element. You can pass an origin point to rotate around it instead:

rectangle = Rect.new(Point(0, 1), 2, 3).rotate(math.pi / 2, origin=Point(0, 0))
rectangle.bounds() == Point(2, 1), Point(6, 4)

NOTE: A rotated element may no longer be representable as its original SGV element (e.g., a rotated rectangle). In these cases, the element will be added to the SVG as equivalent element where the shape can be represented (e.g., a polygon).

reflect

Mirror the element on one or more axis:

polygon = Polygon.new([Point(0, 0), Point(1, 0), Point(1, 2)]).reflect(horizontal=True)
polygon == Polygon.new([Point(1, 0), Point(0, 0), Point(0, 2)])

By default, the center of reflection is the center of element. You can pass an origin point to reflect around it instead:

rectangle = Rect.new(Point(0, 1), 2, 3).reflect(vertical=True, origin=Point(0, 0))
rectangle.bounds() == Point(0, -4), Point(2, -1)

Other Methods

bounds

Returns the top-left, and bottom-right corners of a box that completely encloses the element:

circle = Circle.new(Point(1, 2), 3)
circle.bounds() == Point(-2, -5), Point(4, 5)

rectangle = Rect.new(Point(1, 2), 3, 4)
rectangle.bounds() == Point(1, 2), Point(4, 6)

NOTE: Bounds don't take into account any border around the element. Borders created using stroke-width are always centered, so you can calculate border size manually

circle = Circle.new(Point(1, 2), 3, stroke_width=2)
minimum, maximum = circle.bounds()
border_bounds = minimum - Point(1, 1), maximum + Point(1, 1)

center

Returns the center of the element. The center of an element is defined as the center of its bounding box, there isn't any weighting to element.

circle = Circle.new(Point(1, 2), 3)
circle.center() == Point(1, 2)

rectangle = Rect.new(Point(1, 2), 3, 4)
rectangle.center() == Point(2.5, 4)

Element Attributes

Any additional attributes can be passed into elements as named parameters:

circle = Circle.new(Point(10, 5), 5, stroke_width=1, stroke='black')

Any underscores (_) in attributes will automatically be converted into hyphens (-). All values will be converted to strings automatically. Items in (non-string) sequences (e.g., path commands) will be converted to strings and then space () separated.

You can add support for custom types (e.g., a colour type) by adding an `svg method to your type.

Geometry

If you need to perform mathematical operations on things other than elements (e.g., SVG paths), you should probably check out the geometry module. It implements Cartesian and polar points (Point, and Polar) with the ability to convert between them and (probably) all the mathematical operators you'd expect.

There is also a Line object which lets you easily calculate intersections and Polygon, Rectangle, and Square which were used to implement the Polygon and Rect elements.

Bugs and Feature Requests

This is a support library for some hobby projects I've made. I'm only really planning to make changes and improvements to this package as I need them for those projects.

Feel free to make issues if you want to be notified when/if a feature gets added, but this package is very much provided as is.

Contributing

I'm not interested in contributions at this time.

License

Vector von Doom is available under an AGPL License.