diff --git a/CHANGELOG.md b/CHANGELOG.md index a76443c2..8000b4c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [v2.9.x] - forthcoming +## [v2.9.0] - 2021-02-18 ### Added @@ -16,6 +16,7 @@ calculated, allowing for various xadvance values. So there may be a few instances where text is positioned a few pixels further left compared with earlier Luxor versions. +- BASE64 added (thanks @fonsp!) ### Removed diff --git a/README.md b/README.md index 7233ac9a..02cc2bcd 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## Luxor -Luxor is a Julia package for drawing simple static vector graphics. It provides basic drawing functions and utilities for working with simple 2D graphics. Think of it as a high-level easier to use interface to [Cairo.jl](https://github.com/JuliaLang/Cairo.jl), with shorter names, fewer underscores, default contexts, and simplified functions. In Luxor, the emphasis is on simplicity and ease of use. +Luxor is a Julia package for drawing simple 2D vector graphics. Think of it as a high-level easier to use interface to [Cairo.jl](https://github.com/JuliaLang/Cairo.jl), with shorter names, fewer underscores, default contexts, and simplified functions. In Luxor, the emphasis is on simplicity and ease of use. !["luxor gallery"](docs/src/assets/figures/luxorgallery.png) @@ -15,9 +15,9 @@ Luxor is thoroughly procedural and static: your code issues a sequence of simple A short tutorial can be found in the documentation. There are some Luxor-related videos on [YouTube](https://www.youtube.com/channel/UCfd52kTA5JpzOEItSqXLQxg), and some Luxor-related blog posts at [cormullion.github.io/](https://cormullion.github.io/). -Luxor is designed primarily for drawing static 2D images. If you want to build animations, use [Javis.jl](https://github.com/Wikunia/Javis.jl/issues). +Luxor is designed primarily for drawing static pictures. If you want to build animations, use [Javis.jl](https://github.com/Wikunia/Javis.jl/issues). -Luxor isn't interactive: for building interactivity, look at [Gtk.jl](https://github.com/JuliaGraphics/Gtk.jl), [GLVisualize](https://github.com/JuliaGL/GLVisualize.jl), [Makie](https://github.com/JuliaPlots/Makie.jl), and [Pluto.jl](https://github.com/fonsp/Pluto.jl). +Luxor isn't interactive: for building interactivity, look at [Pluto.jl](https://github.com/fonsp/Pluto.jl) and [Makie](https://github.com/JuliaPlots/Makie.jl), and [Pluto.jl](https://github.com/fonsp/Pluto.jl). ## How can you contribute? diff --git a/docs/src/basics.md b/docs/src/basics.md index 868b77c0..a16480c9 100644 --- a/docs/src/basics.md +++ b/docs/src/basics.md @@ -6,11 +6,11 @@ DocTestSetup = quote # The basics -The underlying drawing model is that you make shapes, and add points to paths, and these are filled and/or stroked, using the current *graphics state*, which specifies colors, line thicknesses, and opacity. You can modify the current graphics state by transforming/rotating/scaling it, and setting style parameters, and so on. Subsequent graphics use the new state, but the graphics you've already drawn are unchanged. +The underlying drawing model is that you make shapes, and add points to paths, and these are filled and/or stroked, using the current *graphics state*, which specifies colors, line thicknesses, scale, orientation, opacity, and so on. You can modify the current graphics state by transforming/rotating/scaling it, and setting style parameters, and so on. Subsequent graphics use the new state, but the graphics you've already drawn are unchanged. The `gsave()` and `grestore()` functions (or the `@layer .... ` macro) let you create new temporary graphics states, You can specify points on the drawing surface using `Point(x, y)`. The default origin is at the top left of the drawing area, but you can reposition it at any time. Many of the drawing functions have an *action* argument. This can be `:none`, `:fill`, `:stroke`, `:fillstroke`, `:fillpreserve`, `:strokepreserve`, `:clip`, or `:path`. The default is `:none`. -Y coordinates increase downwards, so `Point(0, 100)` is below `Point(0, 0)`. This is the preferred coordinate system for computer graphics software, but mathematicians and scientists may well be used to the y-axis increasing upwards... +Y coordinates increase downwards, so `Point(0, 100)` is below `Point(0, 0)`. This is the preferred coordinate system for most computer graphics software, but mathematicians and scientists may well be used to the other convention, where the y-axis increasing up the page... The main types you'll encounter in Luxor are: @@ -56,7 +56,7 @@ julia> P + Q Luxor.Point(16.0, 18.0) ``` -You can add or multiply Points and scalars: +You can add and multiply Points and scalars: ```julia julia> 10P @@ -106,7 +106,7 @@ nothing # hide ![point example](assets/figures/point-ex.png) -Angles are usually supplied in radians, measured starting at the positive x-axis turning towards the positive y-axis (which usually points 'down' the page or canvas, so 'clockwise'). (The main exception is for turtle graphics, which conventionally let you supply angles in degrees.) +Angles are usually supplied in radians, measured starting at the positive x-axis turning towards the positive y-axis (which usually points 'down' the page or canvas). So rotations are ‘clockwise’. (The main exception is for turtle graphics, which conventionally let you supply angles in degrees.) Coordinates are interpreted as PostScript points, where a point is 1/72 of an inch. @@ -172,7 +172,7 @@ finish() preview() ``` -They're short-cuts - designed to save typing. You can omit the width and height (defaulting to 600 by 600, except for `@imagematrix`), and you don't have to specify a filename (you'll get time-stamped files in the current working directory). For multiple lines, use either: +They're short-cuts - designed to save a bit of typing. You can omit the width and height (defaulting to 600 by 600, except for `@imagematrix`), and you don't have to specify a filename (you'll get time-stamped files in the current working directory). For multiple lines, use either: ```julia @svg begin @@ -191,7 +191,7 @@ or (less nicely): ) ``` -The `@draw` macro creates an in-memory drawing. You should see it displayed if you're working in a capable environment (Juno, VSCode, Jupyter, Pluto). +The `@draw` macro creates a drawing in-memory (not saved in a file). You should see it displayed if you're working in a suitable environment (Juno, VSCode, Jupyter, Pluto). ```@docs @svg @@ -206,7 +206,7 @@ If you don't specify a size, the defaults are 600 by 600. If you don't specify a @svg juliacircles(150) 400 400 "test" # saves in "test.svg" ``` -If you want to create drawings with transparent backgrounds, or located other than in the center, use the longer form rather than the macros: +If you want to create drawings with transparent backgrounds, or use variables to specify filenames, use the longer form, rather than the macros: ```julia Drawing() @@ -348,7 +348,7 @@ origin ## Save and restore -`gsave()` saves a copy of the current graphics settings (current axis rotation, position, scale, line and text settings, color, and so on). When the next `grestore()` is called, all changes you've made to the graphics settings will be discarded, and the previous settings are restored, so things return to how they were when you last used `gsave()`. `gsave()` and `grestore()` should always be balanced in pairs. +`gsave()` saves a copy of the current graphics settings (current axis rotation, position, scale, line and text settings, color, and so on). When the next `grestore()` is called, all changes you've made to the graphics settings will be discarded, and the previous settings are restored, so things return to how they were when you last used `gsave()`. `gsave()` and `grestore()` should always be balanced in pairs, enclosing the functions. The `@layer` macro is a synonym for a `gsave()`...`grestore()` pair. @@ -389,10 +389,9 @@ currentdrawing ## Drawing as image matrix -While drawing, you can copy the current graphics as a -matrix of pixels, using the `image_as_matrix()` function. +While drawing, you can copy the current graphics in a drawing as a matrix of pixels, using the `image_as_matrix()` function. -`image_as_matrix()` returns a array of ARGB32 values. Each ARGB value encodes the Red, Green, Blue, and Alpha values of a pixel. +`image_as_matrix()` returns a array of ARGB32 values. Each ARGB value encodes the Red, Green, Blue, and Alpha values of a pixel into a single 32 bit integer. The following example draws a red rectangle, then copies the drawing into a matrix called `mat1`. Then it adds a blue triangle, and copies the updated drawing into `mat2`. In the second drawing, values from the two matrices are tested, and table cells are randomly colored depending on the corresponding values ... this is a primitive Boolean operation. diff --git a/docs/src/colors-styles.md b/docs/src/colors-styles.md index 7178ca57..a628d359 100644 --- a/docs/src/colors-styles.md +++ b/docs/src/colors-styles.md @@ -15,9 +15,9 @@ For color definitions and conversions, you can use [Colors.jl](https://github.co `setmesh()` will apply a color mesh to new graphics. -The difference between the `setcolor()` and `sethue()` functions is that `sethue()` is independent of alpha opacity, so you can change the hue without changing the current opacity value. +The difference between the `setcolor()` and `sethue()` functions is that `sethue()` doesn't change alpha opacity (transparency), so you can change the hue without changing the current alpha opacity (transparency) value. -Named colors, such as "gold", or "lavender", can be found in Colors.color_names. +Named colors, such as "gold", or "lavender", can be found in `Colors.color_names` dictionary. ```@example using Luxor, Colors # hide @@ -62,7 +62,7 @@ setantialias ## Line styles -There are `set-` functions for controlling subsequent lines' width, end shapes, join behavior, and dash patterns: +There are `set-` functions for controlling subsequent lines' width, end shape, join behavior, and dash pattern: ```@example using Luxor # hide @@ -115,7 +115,7 @@ nothing # hide ![dashes](assets/figures/dashes.png) -To define more complicated dash patterns in Luxor, supply a vector to `setdash()`. +To define more complicated dash patterns in Luxor, pass a vector to `setdash()`. ```julia dashes = [50.0, # ink @@ -349,7 +349,7 @@ blendadjust ## Blending (compositing) operators -Graphics software provides ways to modify how the virtual "ink" is applied to existing graphic elements. In PhotoShop and other software products the compositing process is done using [blend modes](https://en.wikipedia.org/wiki/Blend_modes). +Graphics software provides ways to modify how the virtual "ink" is applied to previously-drawn graphic elements. In PhotoShop and other software, the compositing process is done using [blend modes](https://en.wikipedia.org/wiki/Blend_modes). Use `setmode()` to set the blending mode of subsequent graphics. @@ -401,7 +401,7 @@ nothing # hide Notice in this example that clipping was used to restrict the area affected by the blending process. -In Cairo these blend modes are called *operators*. A source for a more detailed explanation can be found [here](https://www.cairographics.org/operators/). +In Cairo, these blend modes are called *operators*. For a more detailed explanation, refer to [the Cairo documentation](https://www.cairographics.org/operators/). You can access the list of modes with the unexported symbol `Luxor.blendingmodes`. diff --git a/docs/src/examples.md b/docs/src/examples.md index dbc7c446..75231b7e 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -23,9 +23,9 @@ finish() preview() ``` -`Drawing(1000, 1000, "hello-world.png")` defines the width, height, location, and type of the finished image. `origin()` moves the 0/0 point to the centre of the drawing surface (by default it's at the top left corner). Thanks to `Colors.jl` we can specify colors by name as well as by numeric value: `background("black")` defines the color of the background of the drawing. `text("helloworld")` draws the text. It's placed at the current 0/0 point and left-justified if you don't specify otherwise. `finish()` completes the drawing and saves the PNG image in the file. `preview()` tries to open the saved file using some other application (eg Preview on macOS). +`Drawing(1000, 1000, "hello-world.png")` defines the width, height, location, and type of the finished image. `origin()` moves the 0/0 point to the centre of the drawing surface (by default it's at the top left corner). Thanks to `Colors.jl` we can specify colors by name as well as by numeric value: `background("black")` defines the color of the background of the drawing. `text("helloworld")` draws the text. It's placed at the current 0/0 point and left-justified if you don't specify otherwise. `finish()` completes the drawing and saves the PNG image in the file. `preview()` tries to display the saved file, perhaps using another application (eg Preview on macOS). -The macros `@png`, `@svg`, `@pdf`, `@draw`, and `@imagematrix` provide shortcuts for making and previewing graphics without having to provide the usual set-up and finish instructions: +The macros `@png`, `@svg`, `@pdf`, `@draw`, and `@imagematrix` provide shortcuts for making and previewing graphics without you having to provide the usual set-up and finish instructions: ```julia # using Luxor @@ -53,9 +53,9 @@ end The `@draw` macro is useful if you work in Juno/VS Code IDEs or a notebook environment such as Jupyter or Pluto and -don't need to save work in files. It creates a PNG format +don't need to always save your work in files. It creates a PNG format drawing in memory, rather than saved in a file. It's -displayed in the plot pane or the next cell. +displayed in the plot pane or in the next cell. ```julia @draw begin @@ -82,13 +82,13 @@ using Luxor Drawing(600, 400, "assets/figures/julia-logos.png") origin() background("white") + for θ in range(0, step=π/8, length=16) gsave() - scale(0.25) + scale(0.2) rotate(θ) - translate(250, 0) - randomhue() - julialogo(action=:fill, color=false) + translate(350, 0) + julialogo(action=:fill, bodycolor=randomhue()) grestore() end @@ -97,19 +97,20 @@ scale(0.3) juliacircles() grestore() -translate(200, -150) +translate(150, -150) scale(0.3) julialogo() finish() + # preview() nothing # hide ``` ![background](assets/figures/julia-logos.png) -The `gsave()` function saves the current drawing parameters, and any subsequent changes such as the `scale()` and `rotate()` operations are discarded by the next `grestore()` function. +The `gsave()` function saves the current drawing environment temporarily, and any subsequent changes such as the `scale()` and `rotate()` operations are discarded when you call the next `grestore()` function. -Use the extension to specify the format: for example change `julia-logos.png` to `julia-logos.svg` or `julia-logos.pdf` or `julia-logos.eps` to produce SVG, PDF, or EPS format output. +Use the extension to specify the format: for example, change `julia-logos.png` to `julia-logos.svg` or `julia-logos.pdf` or `julia-logos.eps` to produce SVG, PDF, or EPS format output. ## Something a bit more complicated: a Sierpinski triangle @@ -177,7 +178,7 @@ You can use an environment such as a Jupyter or Pluto notebook or the Juno or VS ## Images as matrices -With the `@imagematrix` macro, you can create your drawing with vector graphics in the usual way, but the result is returned as a matrix. This example processes the ampersand in Images.jl. +With the `@imagematrix` macro, you can create your drawing with vector graphics in the usual way, but the result is returned as a matrix. This example processes an ampersand in Images.jl. ``` using Luxor, Colors, Images, ImageFiltering diff --git a/docs/src/images.md b/docs/src/images.md index 81fd198b..393e99dd 100644 --- a/docs/src/images.md +++ b/docs/src/images.md @@ -7,14 +7,14 @@ DocTestSetup = quote ## Placing images -Luxor lets you place PNG and SVG images on the drawing. First, load an image from a file: +Luxor lets you place PNG and SVG images on the drawing. First, load an image: - for PNG images, use `readpng(filename)` - for SVG images, use `readsvg(filename)` or `readsvg(string)` (JPEGs aren't supported.) -Then use `placeimage()` to place the image by its top left corner at point `pt`. Access the image's dimensions with `.width` and `.height`. +Then use `placeimage()` to place the image by its top left corner at point `pt`, or use the `centered=true` keyword to place the image's center point there. Access the image's dimensions with `.width` and `.height`. You can use the `centered=true` keyword. ```@example using Luxor # hide @@ -114,7 +114,7 @@ finish() ## Transforming images -You can transform images by setting the current matrix, either with `scale()` and `rotate()` and similar, or by modifying it directly. This code skews an image made in an earlier chapter of this document and scales and rotates it in a circle: +You can transform images by setting the current matrix, either with `scale()` and `rotate()` and similar, or by modifying it directly. This code scales and rotates an image made in an earlier chapter of this document around in a circle: ```@example using Luxor # hide @@ -194,7 +194,7 @@ nothing # hide ### Adding text to transformed images The above approach works well, but suppose you want to locate the working origin -at the lower left of the image, ie you want all coordinates to be relative to the +at the lower left of the image, i.e. you want all coordinates to be relative to the bottom left corner of the image? To do this, use `translate()` and `transform()` to modify the drawing space: diff --git a/docs/src/transforms.md b/docs/src/transforms.md index 253af0fe..3a931ecf 100644 --- a/docs/src/transforms.md +++ b/docs/src/transforms.md @@ -7,7 +7,7 @@ DocTestSetup = quote For basic transformations of the drawing space, use `scale(sx, sy)`, `rotate(a)`, and `translate(tx, ty)`. -`translate()` shifts the current axes by the specified amounts in x and y. It's relative and cumulative, rather than absolute: +`translate(pos)` (or `translate(x, y)`) shifts the current axes to `pos` (or by the specified amounts in x and y). It's relative and cumulative, rather than absolute: ```@example using Luxor, Colors, Random # hide @@ -77,7 +77,7 @@ nothing # hide To return home after many changes, you can use `setmatrix([1, 0, 0, 1, 0, 0])` to reset the matrix to the default. `origin()` resets the matrix then moves the origin to the center of the page. -`rescale()` is a convenient utility function for linear interpolation, also called a "lerp". +`rescale()` is a convenient utility function for linear interpolation (also called a "lerp"). ```@docs scale