├── .gitignore ├── LICENSE ├── README.md ├── blog ├── fractals │ ├── burning-ship-zoom.png │ ├── burning-ship.png │ ├── burning_ship.py │ ├── burning_ship_zoom.py │ ├── gingerbread.png │ ├── gingerbread.py │ ├── henon.png │ ├── henon.py │ ├── henon2.png │ ├── hopalong-variant.png │ ├── hopalong-variant.py │ ├── hopalong.png │ ├── hopalong.py │ ├── julia.png │ ├── julia.py │ ├── kings-dream.png │ ├── kings_dream.py │ ├── mandelbrot-bw.png │ ├── mandelbrot-bw.py │ ├── mandelbrot.png │ ├── mandelbrot.py │ ├── newton-cube-time.png │ ├── newton-cube-time.py │ ├── newton-cube.png │ ├── newton-cube.py │ ├── popcorn.png │ ├── popcorn.py │ ├── popcorn2.png │ ├── popcorn2.py │ ├── tinkerbell-bw.png │ ├── tinkerbell.png │ ├── tinkerbell.py │ └── tinkerbell_bw.py └── geometric │ ├── spirograph-animation.gif │ ├── spirograph-dimensions.png │ ├── spirograph-illustration.py │ ├── spirograph-points.png │ ├── spirograph-shapes.py │ ├── spirograph-square.png │ ├── spirograph-square2.png │ ├── spirograph-triangle.png │ ├── spirograph-triangle2.png │ ├── spirograph.png │ ├── spirograph.py │ ├── spirograph2.png │ └── spirograph3.png ├── examples ├── bitmap │ ├── henon.py │ ├── mandelbrot.py │ ├── scaler-test.py │ ├── simple-make-bitmap-frame.py │ ├── simple-make-bitmap-frames.py │ ├── simple-make-bitmap.py │ └── simple-make-bitmaps.py ├── color │ ├── color.py │ ├── csscolor.py │ ├── hslbars.py │ ├── hslcolor.py │ └── rgbcolor.py ├── drawing │ ├── simple-make-frame.py │ ├── simple-make-frames.py │ ├── simple-make-image-alpha.py │ ├── simple-make-image.py │ └── simple-make-images.py ├── formulas │ ├── cosh-formula.png │ ├── create-png-1.png │ ├── create-png-2.png │ ├── create-png-3.png │ ├── create-png-formula.py │ ├── formula-graph-1.png │ ├── formula-graph.py │ ├── formula-test-temp.png │ ├── maths-formula.py │ └── quadratic.png ├── geometry │ ├── bezier.py │ ├── cat.png │ ├── circles.py │ ├── clip.py │ ├── complex-paths.py │ ├── ellipses.py │ ├── image.py │ ├── line-styles.py │ ├── lines.py │ ├── marker-illustrations.py │ ├── markers.py │ ├── path.py │ ├── polygon.py │ ├── rectangles.py │ ├── squares.py │ ├── text.py │ └── triangles.py ├── graph │ ├── dashedlinegraph.py │ └── simplegraph.py ├── movie │ ├── moviebuilder-audio.py │ ├── moviebuilder-no-audio.py │ ├── scene1-audio.mp3 │ ├── scene2-audio.mp3 │ └── simplemovie.py ├── nparray │ ├── save-reload-nparray.py │ ├── simple-make-nparray-frame.py │ ├── simple-make-nparray-frames.py │ ├── simple-make-nparray.py │ └── simple-make-nparrays.py ├── readme.md ├── start │ └── simpleimage.py └── tween │ └── simpletween.py ├── generativepy ├── __init__.py ├── analytics.py ├── bitmap.py ├── color.py ├── drawing.py ├── drawing3d.py ├── formulas.py ├── geometry.py ├── geometry3d.py ├── gif.py ├── graph.py ├── math.py ├── movie.py ├── nparray.py ├── povray.py ├── shape2d.py ├── table.py ├── tween.py └── utils.py ├── imagetests ├── all_image_tests.py ├── cat.png ├── formula.png ├── image_test_helper.py ├── images │ ├── test_artistic_color_scheme.png │ ├── test_basic_colors.png │ ├── test_bezier.png │ ├── test_book_color_blocks.png │ ├── test_book_color_scheme.png │ ├── test_circle.png │ ├── test_clip.png │ ├── test_complex_paths.png │ ├── test_css_colors.png │ ├── test_dark_color_scheme.png │ ├── test_deprecated_markers.png │ ├── test_drawing_make_frame_rgb.png │ ├── test_drawing_make_frame_rgba.png │ ├── test_drawing_make_image_rgb.png │ ├── test_drawing_make_image_rgba.png │ ├── test_ellipse.png │ ├── test_flatcolor_cube_drawing3d.png │ ├── test_flatcolor_drawing3d.png │ ├── test_flatcolor_rectangle_drawing3d.png │ ├── test_formula_default_dpi.png │ ├── test_formula_dpi.png │ ├── test_formula_optional_package.png │ ├── test_geometry_image.png │ ├── test_graph_appearance.png │ ├── test_graph_axes_position.png │ ├── test_graph_border.png │ ├── test_graph_dashed_line.png │ ├── test_graph_extent.png │ ├── test_graph_filled.png │ ├── test_graph_formatted_axis_values.png │ ├── test_graph_gradient_background.png │ ├── test_graph_gradient_plot.png │ ├── test_graph_multiple.png │ ├── test_graph_scale_factor.png │ ├── test_graph_scatter.png │ ├── test_graph_simple.png │ ├── test_graph_styles.png │ ├── test_graph_subdivisions.png │ ├── test_hsl_color_bars.png │ ├── test_hsl_colors.png │ ├── test_large_graph_scale.png │ ├── test_light_dark_colors.png │ ├── test_line_full.png │ ├── test_line_ray.png │ ├── test_line_segment.png │ ├── test_line_styles.png │ ├── test_lines.png │ ├── test_make_bitmap_frame_gray.png │ ├── test_make_bitmap_frame_rgb.png │ ├── test_make_bitmap_frame_rgba.png │ ├── test_make_bitmap_gray.png │ ├── test_make_bitmap_rgb.png │ ├── test_make_bitmap_rgba.png │ ├── test_make_nparray_frame_gray.png │ ├── test_make_nparray_frame_rgb.png │ ├── test_make_nparray_frame_rgba.png │ ├── test_make_nparray_frame_with_output_rgb.png │ ├── test_make_nparray_gray.png │ ├── test_make_nparray_rgb.png │ ├── test_make_nparray_rgba.png │ ├── test_markers.png │ ├── test_old_markers.png │ ├── test_overlay_nparrays.png │ ├── test_parameters.png │ ├── test_path.png │ ├── test_polygon.png │ ├── test_povray_Plot3dZofXY_non_default.png │ ├── test_povray_axes.png │ ├── test_povray_non_default_axes.png │ ├── test_povray_scene.png │ ├── test_povray_test_povray_Plot3dZofXY.png │ ├── test_rectangle.png │ ├── test_regular_polygon.png │ ├── test_rgb_color_panels.png │ ├── test_shapes_linear_gradient_fill.png │ ├── test_simple_drawing3d.png │ ├── test_square.png │ ├── test_table.png │ ├── test_text.png │ ├── test_text_offset.png │ ├── test_transform.png │ ├── test_triangle.png │ ├── test_turtle.png │ └── test_tween_easing.png ├── readme.md ├── test_bitmap_module.py ├── test_color_module.py ├── test_drawing3d_images.py ├── test_drawing_module.py ├── test_formulas_module.py ├── test_geometry_module.py ├── test_gradients_fills.py ├── test_graph_module.py ├── test_nparray_module.py ├── test_povray_module.py ├── test_table_module.py └── test_tween_module.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── test ├── all_unit_tests.py ├── formula-empty-temp.png ├── formula-invalid-temp.png ├── formula-valid-temp.png ├── readme.md ├── test_color.py ├── test_formulas.py ├── test_graph.py ├── test_matrix.py ├── test_points.py ├── test_regular_polygon.py ├── test_text.py ├── test_transform.py ├── test_tween.py ├── test_utils.py ├── test_vector.py └── test_vector3.py └── tutorial ├── colour ├── colour-alpha.png ├── colour-css.png ├── colour-delta.png ├── colour-grey.png ├── colour-hsl.png ├── colour-lerp.png ├── colour-map.png ├── colour-rgb.png ├── colour-scheme-user.png ├── colour-scheme.png ├── colour-with.png ├── colour_alpha.py ├── colour_css.py ├── colour_delta.py ├── colour_grey.py ├── colour_hsl.py ├── colour_lerp.py ├── colour_map.py ├── colour_properties.py ├── colour_rgb.py ├── colour_schemes.py ├── colour_schemes_user.py └── colour_with.py ├── getting-started ├── rectangle-svg.py ├── rectangle-svg.svg ├── rectangle.png ├── rectangle.py ├── setup.png └── setup.py ├── patterns ├── circles-linear-gradient.png ├── circles-linear-gradient.py ├── fullpage-linear-gradient.png ├── fullpage-linear-gradient.py ├── multistop-linear-gradient.png ├── multistop-linear-gradient.py ├── multistop-linear-gradient2.png ├── multistop-linear-gradient3.png ├── simple-linear-gradient.png └── simple-linear-gradient.py ├── readme.md ├── shapes ├── angle-markers.png ├── bezier-tutorial-joined.png ├── bezier-tutorial-points.png ├── bezier-tutorial-polygon.png ├── bezier-tutorial.png ├── bezier.py ├── cat.png ├── circles-tutorial.png ├── circles.py ├── complex-polygon.png ├── complex-polygon.py ├── complex-subpaths.png ├── complex-subpaths.py ├── composite-lines.png ├── composite-lines.py ├── composite-roundrect.png ├── composite-roundrect.py ├── ellipses-tutorial.png ├── ellipses.py ├── fill-stroke-tutorial.png ├── fill-stroke.py ├── fill-style-tutorial.png ├── image.png ├── image.py ├── join-style-tutorial.png ├── markers.py ├── parallel-markers.png ├── path.png ├── path.py ├── polygons-tutorial.png ├── polygons.py ├── regularpolygons-tutorial.png ├── regularpolygons.py ├── stroke-style-tutorial.png ├── text-align.png ├── text-align.py ├── text-drawing.png ├── text-drawing.py ├── text-metrics.png ├── text-metrics.py ├── text-offset.png ├── text-offset.py ├── tick-markers.png ├── turtle.png └── turtle.py └── transforms ├── advanced-transform.png ├── advanced.py ├── clip-tutorial.png ├── clip.py ├── mirror-tutorial.png ├── mirror.py ├── rotate-tutorial.png ├── rotate.py ├── scale-tutorial.png ├── scale.py ├── translate-tutorial.png ├── translate.py ├── user-scale.png └── user-scale.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.metadata/ 2 | __pycache__ 3 | .project 4 | .pydevproject 5 | /.idea/ 6 | /scratch/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Martin McBride 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /blog/fractals/burning-ship-zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/burning-ship-zoom.png -------------------------------------------------------------------------------- /blog/fractals/burning-ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/burning-ship.png -------------------------------------------------------------------------------- /blog/fractals/burning_ship.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-14 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import (make_nparray_data, make_npcolormap, save_nparray, 8 | load_nparray, save_nparray_image, apply_npcolormap) 9 | from generativepy.color import Color 10 | from generativepy.utils import temp_file 11 | import numpy as np 12 | 13 | MAX_COUNT = 256 14 | 15 | def calc(c1, c2): 16 | x = y = 0 17 | for i in range(MAX_COUNT): 18 | x, y = x*x - y*y + c1, abs(2*x*y) + c2 19 | if x*x + y*y > 4: 20 | return i + 1 21 | return 0 22 | 23 | 24 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 25 | scaler = Scaler(pixel_width, pixel_height, width=3.2, startx=-2, starty=-1.8) 26 | 27 | for px in range(pixel_width): 28 | for py in range(pixel_height): 29 | x, y = scaler.device_to_user(px, py) 30 | count = calc(x, y) 31 | image[py, px] = count 32 | 33 | 34 | def colorise(counts): 35 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 36 | 37 | colormap = make_npcolormap(MAX_COUNT+1, 38 | [Color('black'), Color('red'), Color('orange'), Color('yellow'), Color('white')], 39 | [16, 8, 32, 128]) 40 | 41 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 42 | apply_npcolormap(outarray, counts, colormap) 43 | return outarray 44 | 45 | 46 | data = make_nparray_data(paint, 800, 600, channels=1) 47 | 48 | filename = temp_file('burning-ship.dat') 49 | save_nparray(filename, data) 50 | data = load_nparray(filename) 51 | 52 | frame = colorise(data) 53 | 54 | save_nparray_image('burning-ship.png', frame) 55 | 56 | -------------------------------------------------------------------------------- /blog/fractals/burning_ship_zoom.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-14 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, make_npcolormap, save_nparray, load_nparray, save_nparray_image, apply_npcolormap 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | import numpy as np 11 | 12 | MAX_COUNT = 256 13 | 14 | def calc(c1, c2): 15 | x = y = 0 16 | for i in range(MAX_COUNT): 17 | x, y = x*x - y*y + c1, abs(2*x*y) + c2 18 | if x*x + y*y > 4: 19 | return i + 1 20 | return 0 21 | 22 | 23 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 24 | scaler = Scaler(pixel_width, pixel_height, width=0.1, startx=-1.8, starty=-0.09) 25 | 26 | for px in range(pixel_width): 27 | for py in range(pixel_height): 28 | x, y = scaler.device_to_user(px, py) 29 | count = calc(x, y) 30 | image[py, px] = count 31 | 32 | 33 | def colorise(counts): 34 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 35 | 36 | colormap = make_npcolormap(MAX_COUNT+1, 37 | [Color('black'), Color('red'), Color('orange'), Color('yellow'), Color('white')], 38 | [16, 8, 32, 128]) 39 | 40 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 41 | apply_npcolormap(outarray, counts, colormap) 42 | return outarray 43 | 44 | 45 | data = make_nparray_data(paint, 600, 600, channels=1) 46 | 47 | filename = temp_file('tinkerbell.dat') 48 | save_nparray(filename, data) 49 | data = load_nparray(filename) 50 | 51 | frame = colorise(data) 52 | 53 | save_nparray_image('burning-ship-zoom.png', frame) 54 | 55 | -------------------------------------------------------------------------------- /blog/fractals/gingerbread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/gingerbread.png -------------------------------------------------------------------------------- /blog/fractals/gingerbread.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-13 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray 8 | 9 | MAX_COUNT = 1000000 10 | 11 | 12 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 13 | scaler = Scaler(pixel_width, pixel_height, width=12, startx=-3.5, starty=-3.5) 14 | 15 | x = -0.1 16 | y = 0.0 17 | for i in range(MAX_COUNT): 18 | x, y = 1 - y + abs(x), x 19 | px, py = scaler.user_to_device(x, y) 20 | image[py, px] = 0 21 | 22 | make_nparray('gingerbread.png', paint, 600, 600, channels=1) 23 | 24 | -------------------------------------------------------------------------------- /blog/fractals/henon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/henon.png -------------------------------------------------------------------------------- /blog/fractals/henon.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-16 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray 8 | 9 | MAX_COUNT = 100000 10 | A = 1.4 11 | B = 0.3 12 | 13 | 14 | # Show teh full Henon attractor 15 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 16 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-1.5, starty=-1.5) 17 | 18 | x = 0.01 19 | y = 0.01 20 | for i in range(MAX_COUNT): 21 | x, y = 1 - A*x*x + y, B*x 22 | px, py = scaler.user_to_device(x, y) 23 | if 0 <= px < pixel_width and 0 <= py < pixel_height: 24 | image[py, px] = 0 25 | 26 | make_nparray('henon.png', paint, 600, 600, channels=1) 27 | 28 | 29 | # Zoom in on the right hand loop 30 | def paint2(image, pixel_width, pixel_height, frame_no, frame_count): 31 | scaler = Scaler(pixel_width, pixel_height, width=.5, startx=0.8, starty=-0.25) 32 | 33 | x = 0.01 34 | y = 0.01 35 | for i in range(MAX_COUNT): 36 | x, y = 1 - A*x*x + y, B*x 37 | px, py = scaler.user_to_device(x, y) 38 | if 0 <= px < pixel_width and 0 <= py < pixel_height: 39 | image[py, px] = 0 40 | 41 | make_nparray('henon2.png', paint2, 600, 600, channels=1) 42 | 43 | -------------------------------------------------------------------------------- /blog/fractals/henon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/henon2.png -------------------------------------------------------------------------------- /blog/fractals/hopalong-variant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/hopalong-variant.png -------------------------------------------------------------------------------- /blog/fractals/hopalong-variant.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-15 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | from generativepy.analytics import print_stats, print_histogram 11 | import numpy as np 12 | import math 13 | 14 | MAX_COUNT = 10000000 15 | A = 0.1 16 | B = 5 17 | C = -1 18 | 19 | def sign(x): 20 | if x > 0: 21 | return 1 22 | if x < 0: 23 | return -1 24 | return(0) 25 | 26 | def colorise(counts): 27 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 28 | power_counts = np.power(counts, 0.25) 29 | maxcount = np.max(power_counts) 30 | normalised_counts = (power_counts * 1023 / max(maxcount, 1)).astype(np.uint32) 31 | 32 | colormap = make_npcolormap(1024, [Color('black'), Color('green'), Color('yellow'), Color('red')]) 33 | 34 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 35 | apply_npcolormap(outarray, normalised_counts, colormap) 36 | return outarray 37 | 38 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 39 | scaler = Scaler(pixel_width, pixel_height, width=300, startx=-150, starty=-150) 40 | 41 | x = -1 42 | y = 0 43 | for i in range(MAX_COUNT): 44 | x, y = y-math.sqrt(abs(B*x-C))*sign(x), A-x 45 | px, py = scaler.user_to_device(x, y) 46 | if 0 <= px < pixel_width and 0 <= py < pixel_height: 47 | image[py, px] += 1 48 | 49 | 50 | filename = temp_file('hopalong-variant.dat') 51 | 52 | data = make_nparray_data(paint, 600, 600, channels=1) 53 | save_nparray(filename, data) 54 | data = load_nparray(filename) 55 | 56 | frame = colorise(data) 57 | 58 | save_nparray_image('hopalong-variant.png', frame) 59 | -------------------------------------------------------------------------------- /blog/fractals/hopalong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/hopalong.png -------------------------------------------------------------------------------- /blog/fractals/hopalong.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-15 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | from generativepy.analytics import print_stats, print_histogram 11 | import numpy as np 12 | import math 13 | 14 | MAX_COUNT = 10000000 15 | A = -55 16 | B = -1 17 | C = 42 18 | 19 | def sign(x): 20 | if x > 0: 21 | return 1 22 | if x < 0: 23 | return -1 24 | return 0 25 | 26 | def colorise(counts): 27 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 28 | power_counts = np.power(counts, 0.25) 29 | maxcount = np.max(power_counts) 30 | normalised_counts = (power_counts * 1023 / max(maxcount, 1)).astype(np.uint32) 31 | 32 | colormap = make_npcolormap(1024, [Color('black'), Color('green'), Color('yellow'), Color('red')]) 33 | 34 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 35 | apply_npcolormap(outarray, normalised_counts, colormap) 36 | return outarray 37 | 38 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 39 | scaler = Scaler(pixel_width, pixel_height, width=1000, startx=-500, starty=-500) 40 | 41 | x = -1 42 | y = 0 43 | for i in range(MAX_COUNT): 44 | x, y = y-math.sqrt(abs(B*x-C))*sign(x), A-x 45 | px, py = scaler.user_to_device(x, y) 46 | if 0 <= px < pixel_width and 0 <= py < pixel_height: 47 | image[py, px] += 1 48 | 49 | 50 | filename = temp_file('hopalong.dat') 51 | 52 | data = make_nparray_data(paint, 600, 600, channels=1) 53 | save_nparray(filename, data) 54 | data = load_nparray(filename) 55 | 56 | frame = colorise(data) 57 | 58 | save_nparray_image('hopalong.png', frame) 59 | -------------------------------------------------------------------------------- /blog/fractals/julia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/julia.png -------------------------------------------------------------------------------- /blog/fractals/julia.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-13 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.analytics import print_stats, print_histogram 10 | import numpy as np 11 | 12 | MAX_COUNT = 256 13 | C1 = -0.79 14 | C2 = 0.15 15 | 16 | def calc(x, y): 17 | for i in range(MAX_COUNT): 18 | x, y = x*x - y*y + C1, 2*x*y + C2 19 | if x*x + y*y > 4: 20 | return i+1 21 | return 0 22 | 23 | 24 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 25 | scaler = Scaler(pixel_width, pixel_height, width=3.2, startx=-1.6, starty=-1.2) 26 | 27 | for px in range(pixel_width): 28 | for py in range(pixel_height): 29 | x, y = scaler.device_to_user(px, py) 30 | count = calc(x, y) 31 | image[py, px] = count 32 | 33 | 34 | def colorise(counts): 35 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 36 | 37 | colormap = make_npcolormap(MAX_COUNT+1, 38 | [Color('black'), Color('darkblue'), Color('green'), Color('cyan'), Color('yellow'), Color('black')], 39 | [16, 16, 32, 32, 128]) 40 | 41 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 42 | apply_npcolormap(outarray, counts, colormap) 43 | return outarray 44 | 45 | 46 | data = make_nparray_data(paint, 800, 600, channels=1) 47 | 48 | frame = colorise(data) 49 | 50 | save_nparray_image('julia.png', frame) 51 | -------------------------------------------------------------------------------- /blog/fractals/kings-dream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/kings-dream.png -------------------------------------------------------------------------------- /blog/fractals/kings_dream.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-14 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | import math 11 | import numpy as np 12 | 13 | MAX_COUNT = 10000000 14 | A = 2.879879 15 | B = -0.765145 16 | C = -0.966918 17 | D = 0.744728 18 | 19 | 20 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 21 | scaler = Scaler(pixel_width, pixel_height, width=4, startx=-2, starty=-2) 22 | 23 | x = 2 24 | y = 2 25 | for i in range(MAX_COUNT): 26 | x, y = math.sin(A*x)+B*math.sin(A*y), math.sin(C*x)+D*math.sin(C*y) 27 | px, py = scaler.user_to_device(x, y) 28 | image[py, px] += 1 29 | 30 | 31 | def colorise(counts): 32 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 33 | power_counts = np.power(counts, 0.25) 34 | maxcount = np.max(power_counts) 35 | normalised_counts = (power_counts * 1023 / max(maxcount, 1)).astype(np.uint32) 36 | 37 | colormap = make_npcolormap(1024, [Color('black'), Color('red'), Color('orange'), Color('yellow'), Color('white')]) 38 | 39 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 40 | apply_npcolormap(outarray, normalised_counts, colormap) 41 | return outarray 42 | 43 | 44 | data = make_nparray_data(paint, 600, 600, channels=1) 45 | 46 | filename = temp_file('kings-dream.dat') 47 | save_nparray(filename, data) 48 | data = load_nparray(filename) 49 | 50 | frame = colorise(data) 51 | 52 | save_nparray_image('kings-dream.png', frame) 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /blog/fractals/mandelbrot-bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/mandelbrot-bw.png -------------------------------------------------------------------------------- /blog/fractals/mandelbrot-bw.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-13 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray 8 | 9 | MAX_COUNT = 256 10 | 11 | def calc(c1, c2): 12 | x = y = 0 13 | for i in range(MAX_COUNT): 14 | x, y = x*x - y*y + c1, 2*x*y + c2 15 | if x*x + y*y > 4: 16 | return i+1 17 | return 0 18 | 19 | 20 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 21 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-2, starty=-1.5) 22 | 23 | for px in range(pixel_width): 24 | for py in range(pixel_height): 25 | x, y = scaler.device_to_user(px, py) 26 | count = calc(x, y) 27 | if count==0: 28 | image[py, px] = 0 29 | 30 | make_nparray('mandelbrot-bw.png', paint, 600, 600, channels=1) -------------------------------------------------------------------------------- /blog/fractals/mandelbrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/mandelbrot.png -------------------------------------------------------------------------------- /blog/fractals/mandelbrot.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-13 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.analytics import print_stats, print_histogram 10 | import numpy as np 11 | 12 | MAX_COUNT = 256 13 | 14 | def calc(c1, c2): 15 | x = y = 0 16 | for i in range(MAX_COUNT): 17 | x, y = x*x - y*y + c1, 2*x*y + c2 18 | if x*x + y*y > 4: 19 | return i+1 20 | return 0 21 | 22 | 23 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 24 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-2, starty=-1.5) 25 | 26 | for px in range(pixel_width): 27 | for py in range(pixel_height): 28 | x, y = scaler.device_to_user(px, py) 29 | count = calc(x, y) 30 | image[py, px] = count 31 | 32 | 33 | def colorise(counts): 34 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 35 | 36 | colormap = make_npcolormap(MAX_COUNT+1, 37 | [Color('black'), Color('darkblue'), Color('green'), Color('cyan'), Color('white')], 38 | [8, 8, 32, 128]) 39 | 40 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 41 | apply_npcolormap(outarray, counts, colormap) 42 | return outarray 43 | 44 | 45 | data = make_nparray_data(paint, 600, 600, channels=1) 46 | 47 | frame = colorise(data) 48 | 49 | save_nparray_image('mandelbrot.png', frame) 50 | -------------------------------------------------------------------------------- /blog/fractals/newton-cube-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/newton-cube-time.png -------------------------------------------------------------------------------- /blog/fractals/newton-cube-time.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-18 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | from generativepy.analytics import print_stats, print_histogram 11 | import numpy as np 12 | 13 | MAX_COUNT = 100 14 | BLACK = Color(0) 15 | WHITE = Color(1) 16 | 17 | ROOTS = [complex(-0.5, 0.866025), 18 | complex(-0.5, -0.866025), 19 | complex(1, 0)] 20 | 21 | LIMIT = 0.01 22 | 23 | def iterate(z): 24 | if z: 25 | return z - (z**3 - 1)/(3*z**2) 26 | else: 27 | return complex(0) 28 | 29 | def converged(z): 30 | for i, r in enumerate(ROOTS, 1): 31 | if abs(z - r) < LIMIT: 32 | return i 33 | return 0 34 | 35 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 36 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-1.5, starty=-1.5) 37 | 38 | for px in range(pixel_width): 39 | for py in range(pixel_height): 40 | x, y = scaler.device_to_user(px, py) 41 | z = complex(x, y) 42 | image[py, px] = MAX_COUNT 43 | for i in range(MAX_COUNT): 44 | z = iterate(z) 45 | if converged(z) > 0: 46 | image[py, px] = i 47 | break 48 | 49 | 50 | def colorise(counts): 51 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 52 | max_count = int(np.max(counts)) 53 | colormap = make_npcolormap(max_count + 1, 54 | [Color(0), Color('darkblue'), Color('yellow'), Color(1)], 55 | [0.5, 2, 7]) 56 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 57 | apply_npcolormap(outarray, counts, colormap) 58 | return outarray 59 | 60 | 61 | data = make_nparray_data(paint, 600, 600, channels=1) 62 | 63 | filename = temp_file('newton-cube-time.dat') 64 | save_nparray(filename, data) 65 | data = load_nparray(filename) 66 | 67 | frame = colorise(data) 68 | 69 | save_nparray_image('newton-cube-time.png', frame) 70 | -------------------------------------------------------------------------------- /blog/fractals/newton-cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/newton-cube.png -------------------------------------------------------------------------------- /blog/fractals/newton-cube.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-18 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | from generativepy.analytics import print_stats, print_histogram 11 | import numpy as np 12 | 13 | MAX_COUNT = 100 14 | BLACK = Color(0) 15 | WHITE = Color(1) 16 | 17 | ROOTS = [complex(-0.5, 0.866025), 18 | complex(-0.5, -0.866025), 19 | complex(1, 0)] 20 | 21 | LIMIT = 0.01 22 | 23 | def iterate(z): 24 | if z: 25 | return z - (z**3 - 1)/(3*z**2) 26 | else: 27 | return complex(0) 28 | 29 | def converged(z): 30 | for i, r in enumerate(ROOTS, 1): 31 | if abs(z - r) < LIMIT: 32 | return i 33 | return 0 34 | 35 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 36 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-1.5, starty=-1.5) 37 | 38 | for px in range(pixel_width): 39 | for py in range(pixel_height): 40 | x, y = scaler.device_to_user(px, py) 41 | z = complex(x, y) 42 | image[py, px] = 0 43 | for i in range(MAX_COUNT): 44 | z = iterate(z) 45 | root = converged(z) 46 | if root > 0: 47 | image[py, px] = root 48 | break 49 | 50 | 51 | def colorise(counts): 52 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 53 | print_histogram(counts) 54 | colormap = np.array([(0, 0, 0), (0, 128, 0), (128, 0, 0), (0, 0, 128)]) 55 | #outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 56 | outarray = colormap[counts] 57 | apply_npcolormap(outarray, counts, colormap) 58 | return outarray 59 | 60 | 61 | data = make_nparray_data(paint, 600, 600, channels=1) 62 | 63 | filename = temp_file('newton-cube.dat') 64 | save_nparray(filename, data) 65 | data = load_nparray(filename) 66 | 67 | frame = colorise(data) 68 | 69 | save_nparray_image('newton-cube.png', frame) 70 | -------------------------------------------------------------------------------- /blog/fractals/popcorn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/popcorn.png -------------------------------------------------------------------------------- /blog/fractals/popcorn.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-19 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.analytics import print_stats, print_histogram 10 | from generativepy.utils import temp_file 11 | import math 12 | import numpy as np 13 | 14 | MAX_COUNT = 1000 15 | H = 0.5 16 | WIDTH = 600 17 | USERWIDTH = 2 18 | 19 | def calc(x, y): 20 | xn = x - H*math.sin(y + math.tan(3*y)) 21 | yn = y - H*math.sin(x + math.tan(3*x)) 22 | return xn, yn 23 | 24 | 25 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 26 | scaler = Scaler(pixel_width, pixel_height, width=USERWIDTH, startx=-USERWIDTH/2, starty=-USERWIDTH/2) 27 | 28 | image[...] = 0 29 | 30 | for i in range(0, WIDTH, 1): 31 | print(i) 32 | for j in range(0, WIDTH, 1): 33 | x, y = scaler.device_to_user(i, j) 34 | for _ in range(MAX_COUNT): 35 | x, y = calc(x, y) 36 | px, py = scaler.user_to_device(x, y) 37 | if 0 <= px < WIDTH and 0 <= py < WIDTH: 38 | image[py, px] += 1 39 | 40 | def colorise(counts): 41 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 42 | 43 | colormap = make_npcolormap(int(np.max(counts))+1, 44 | [Color('black'), Color('cadetblue'), Color('yellow'), Color('white'), Color('white')], 45 | [50, 100, 100, 102400]) 46 | 47 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 48 | apply_npcolormap(outarray, counts, colormap) 49 | return outarray 50 | 51 | 52 | filename = temp_file('popcorn.dat') 53 | 54 | 55 | data = make_nparray_data(paint, WIDTH, WIDTH, channels=1) 56 | save_nparray(filename, data) 57 | 58 | data = load_nparray(filename) 59 | print_stats(data) 60 | print_histogram(data) 61 | 62 | frame = colorise(data) 63 | 64 | save_nparray_image('popcorn.png', frame) 65 | -------------------------------------------------------------------------------- /blog/fractals/popcorn2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/popcorn2.png -------------------------------------------------------------------------------- /blog/fractals/popcorn2.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-19 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.analytics import print_stats, print_histogram 10 | from generativepy.utils import temp_file 11 | import math 12 | import numpy as np 13 | 14 | MAX_COUNT = 1000 15 | H1 = 0.4 16 | H2 = 0.7 17 | WIDTH = 600 18 | USERWIDTH = 4 19 | 20 | def calc(x, y): 21 | xn = x - H1*math.sin(y + math.tan(3*y)) 22 | yn = y - H1*math.sin(x + math.tan(3*x)) 23 | return xn, yn 24 | 25 | 26 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 27 | scaler = Scaler(pixel_width, pixel_height, width=USERWIDTH, startx=-USERWIDTH/2, starty=-USERWIDTH/2) 28 | 29 | image[...] = 0 30 | 31 | for i in range(0, WIDTH, 1): 32 | print(i) 33 | for j in range(0, WIDTH, 1): 34 | x, y = scaler.device_to_user(i, j) 35 | for _ in range(MAX_COUNT): 36 | x, y = calc(x, y) 37 | px, py = scaler.user_to_device(x, y) 38 | if 0 <= px < WIDTH and 0 <= py < WIDTH: 39 | image[py, px] += 1 40 | 41 | def colorise(counts): 42 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 43 | 44 | colormap = make_npcolormap(int(np.max(counts))+1, 45 | [Color('firebrick'), Color('goldenrod'), Color('lime'), Color('green'), 46 | Color('darkgreen'), Color('white')], 47 | [5, 5, 5, 10, 10240]) 48 | 49 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 50 | apply_npcolormap(outarray, counts, colormap) 51 | return outarray 52 | 53 | 54 | filename = temp_file('popcorn2.dat') 55 | 56 | 57 | data = make_nparray_data(paint, WIDTH, WIDTH, channels=1) 58 | save_nparray(filename, data) 59 | 60 | data = load_nparray(filename) 61 | print_stats(data) 62 | print_histogram(data) 63 | 64 | frame = colorise(data) 65 | 66 | save_nparray_image('popcorn2.png', frame) 67 | -------------------------------------------------------------------------------- /blog/fractals/tinkerbell-bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/tinkerbell-bw.png -------------------------------------------------------------------------------- /blog/fractals/tinkerbell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/fractals/tinkerbell.png -------------------------------------------------------------------------------- /blog/fractals/tinkerbell.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-13 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray_data, save_nparray, load_nparray, make_npcolormap, apply_npcolormap, save_nparray_image 8 | from generativepy.color import Color 9 | from generativepy.utils import temp_file 10 | from generativepy.analytics import print_stats, print_histogram 11 | import numpy as np 12 | 13 | MAX_COUNT = 10000000 14 | A = 0.9 15 | B = -0.6013 16 | C = 2.0 17 | D = 0.5 18 | 19 | 20 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 21 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-2, starty=-2) 22 | 23 | x = 0.01 24 | y = 0.01 25 | for i in range(MAX_COUNT): 26 | x, y = x*x - y*y + A*x + B*y, 2*x*y + C*x + D*y 27 | px, py = scaler.user_to_device(x, y) 28 | image[py, px] += 1 29 | 30 | 31 | def colorise(counts): 32 | counts = np.reshape(counts, (counts.shape[0], counts.shape[1])) 33 | power_counts = np.power(counts, 0.25) 34 | maxcount = np.max(power_counts) 35 | normalised_counts = (power_counts * 1023 / max(maxcount, 1)).astype(np.uint32) 36 | 37 | colormap = make_npcolormap(1024, [Color('black'), Color('red'), Color('orange'), Color('yellow'), Color('white')]) 38 | 39 | outarray = np.zeros((counts.shape[0], counts.shape[1], 3), dtype=np.uint8) 40 | apply_npcolormap(outarray, normalised_counts, colormap) 41 | return outarray 42 | 43 | 44 | data = make_nparray_data(paint, 600, 600, channels=1) 45 | 46 | filename = temp_file('tinkerbell.dat') 47 | save_nparray(filename, data) 48 | data = load_nparray(filename) 49 | 50 | frame = colorise(data) 51 | 52 | save_nparray_image('tinkerbell.png', frame) 53 | -------------------------------------------------------------------------------- /blog/fractals/tinkerbell_bw.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-12-13 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.bitmap import Scaler 7 | from generativepy.nparray import make_nparray 8 | 9 | MAX_COUNT = 100000 10 | A = 0.9 11 | B = -0.6013 12 | C = 2.0 13 | D = 0.5 14 | 15 | 16 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 17 | scaler = Scaler(pixel_width, pixel_height, width=3, startx=-2, starty=-2) 18 | 19 | x = 0.01 20 | y = 0.01 21 | for i in range(MAX_COUNT): 22 | x, y = x*x - y*y + A*x + B*y, 2*x*y + C*x + D*y 23 | px, py = scaler.user_to_device(x, y) 24 | image[py, px] = 0 25 | 26 | make_nparray('tinkerbell-bw.png', paint, 600, 600, channels=1) 27 | 28 | -------------------------------------------------------------------------------- /blog/geometric/spirograph-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-animation.gif -------------------------------------------------------------------------------- /blog/geometric/spirograph-dimensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-dimensions.png -------------------------------------------------------------------------------- /blog/geometric/spirograph-points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-points.png -------------------------------------------------------------------------------- /blog/geometric/spirograph-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-square.png -------------------------------------------------------------------------------- /blog/geometric/spirograph-square2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-square2.png -------------------------------------------------------------------------------- /blog/geometric/spirograph-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-triangle.png -------------------------------------------------------------------------------- /blog/geometric/spirograph-triangle2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph-triangle2.png -------------------------------------------------------------------------------- /blog/geometric/spirograph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph.png -------------------------------------------------------------------------------- /blog/geometric/spirograph.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-22 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.color import Color 7 | from generativepy.drawing import make_image, setup 8 | import math 9 | 10 | from generativepy.geometry import Polygon, Transform 11 | 12 | def create_spiro(a, b, d): 13 | dt = 0.01 14 | t = 0 15 | pts = [] 16 | while t < 2*math.pi*b/math.gcd(a, b): 17 | t += dt 18 | x = (a - b) * math.cos(t) + d * math.cos((a - b)/b * t) 19 | y = (a - b) * math.sin(t) - d * math.sin((a - b)/b * t) 20 | pts.append((x, y)) 21 | return pts 22 | 23 | 24 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 25 | 26 | width = 32 27 | setup(ctx, pixel_width, pixel_height, width=width, startx=-width/2, starty=-width/2, background=Color(1)) 28 | 29 | a = 14 30 | b = 6 31 | d = 4 32 | Polygon(ctx).of_points(create_spiro(a, b, d)).stroke(Color('red'), line_width=0.1) 33 | 34 | 35 | make_image("spirograph.png", draw, 600, 600) 36 | 37 | def draw2(ctx, pixel_width, pixel_height, frame_no, frame_count): 38 | 39 | width = 32 40 | setup(ctx, pixel_width, pixel_height, width=width, startx=-width/2, starty=-width/2, background=Color(1)) 41 | 42 | a = 16 43 | b = 13 44 | d = 5 45 | Polygon(ctx).of_points(create_spiro(a, b, d)).stroke(Color('firebrick'), line_width=0.1) 46 | 47 | a = 16 48 | b = 9 49 | d = 8 50 | Polygon(ctx).of_points(create_spiro(a, b, d)).stroke(Color('goldenrod'), line_width=0.1) 51 | 52 | a = 16 53 | b = 11 54 | d = 6 55 | Polygon(ctx).of_points(create_spiro(a, b, d)).stroke(Color('darkgreen'), line_width=0.1) 56 | 57 | 58 | make_image("spirograph2.png", draw2, 600, 600) 59 | 60 | def draw3(ctx, pixel_width, pixel_height, frame_no, frame_count): 61 | 62 | width = 32 63 | setup(ctx, pixel_width, pixel_height, width=width, startx=-width/2, starty=-width/2, background=Color(1)) 64 | 65 | for i in range(6): 66 | a = 13 67 | b = 7 68 | d = 5 69 | with Transform(ctx).rotate(0.05*i): 70 | Polygon(ctx).of_points(create_spiro(a, b, d)).stroke(Color('dodgerblue').with_l_factor(1.1**i), line_width=0.1) 71 | 72 | 73 | make_image("spirograph3.png", draw3, 600, 600) 74 | 75 | -------------------------------------------------------------------------------- /blog/geometric/spirograph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph2.png -------------------------------------------------------------------------------- /blog/geometric/spirograph3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/blog/geometric/spirograph3.png -------------------------------------------------------------------------------- /examples/bitmap/henon.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import make_bitmap, Scaler 2 | from generativepy.color import Color 3 | from PIL import ImageDraw 4 | 5 | ''' 6 | Draw a Henon attractor 7 | ''' 8 | 9 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 10 | scaler = Scaler(pixel_width, pixel_height, width=3, height=2, startx=-1.5, starty=-1) 11 | draw = ImageDraw.Draw(image) 12 | 13 | a = 1.4 14 | b = 0.3 15 | x, y = 0, 0 16 | for i in range(10000): 17 | draw.point(scaler.user_to_device(x, y), fill=Color("black").as_rgbstr()) 18 | x, y = 1 - a*x*x + y, b*x 19 | 20 | 21 | make_bitmap("/tmp/henon.png", paint, 500, 500) -------------------------------------------------------------------------------- /examples/bitmap/mandelbrot.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import make_bitmap, Scaler 2 | from generativepy.color import Color 3 | from PIL import ImageDraw 4 | 5 | ''' 6 | Draw a Mandelbrot fractal 7 | ''' 8 | 9 | def mandelbrot(c): 10 | z, n = 0, 0 11 | while abs(z) <= 2: 12 | if n > 100: 13 | return 0 14 | z = z*z + c 15 | n += 1 16 | return n 17 | 18 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 19 | scaler = Scaler(pixel_width, pixel_height, width=3, height=2, startx=-2, starty=-1) 20 | draw = ImageDraw.Draw(image) 21 | 22 | a = 1.4 23 | b = 0.3 24 | x, y = 0, 0 25 | for i in range(pixel_width): 26 | for j in range(pixel_height): 27 | c = complex(*scaler.device_to_user(i, j)) 28 | if not mandelbrot(c): 29 | draw.point((i, j), fill=Color("black").as_rgbstr()) 30 | 31 | 32 | make_bitmap("/tmp/mandelbrot.png", paint, 600, 400) -------------------------------------------------------------------------------- /examples/bitmap/scaler-test.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import Scaler 2 | 3 | scaler = Scaler(300, 200, width=3, height=2, startx=-1.5, starty=-1) 4 | 5 | print(scaler.user_to_device(1, .5)) 6 | print(scaler.device_to_user(20, 50)) 7 | -------------------------------------------------------------------------------- /examples/bitmap/simple-make-bitmap-frame.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import make_bitmap_frame 2 | from generativepy.movie import save_frame 3 | from generativepy.color import Color 4 | from PIL import ImageDraw 5 | from generativepy.utils import temp_file 6 | 7 | """ 8 | Create a simple bitmap image 9 | """ 10 | 11 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 12 | draw = ImageDraw.Draw(image) 13 | draw.rectangle((60, 10, 300, 150), fill=Color("tomato").as_rgbstr()) 14 | 15 | 16 | frame = make_bitmap_frame(paint, 500, 300) 17 | save_frame(temp_file("simple-make-bitmap-frame.png"), frame) -------------------------------------------------------------------------------- /examples/bitmap/simple-make-bitmap-frames.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import make_bitmap_frames 2 | from generativepy.movie import save_frames 3 | from generativepy.color import Color 4 | from PIL import ImageDraw 5 | from generativepy.utils import temp_file 6 | 7 | ''' 8 | Create a simple bitmap image 9 | ''' 10 | 11 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 12 | draw = ImageDraw.Draw(image) 13 | draw.rectangle((60, 10+frame_no*30, 300, 150+frame_no*30), fill=Color("tomato").as_rgbstr()) 14 | 15 | 16 | frames = make_bitmap_frames(paint, 500, 300, 4) 17 | save_frames(temp_file("simple-make-bitmap-frames.png"), frames) -------------------------------------------------------------------------------- /examples/bitmap/simple-make-bitmap.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import make_bitmap 2 | from generativepy.color import Color 3 | from PIL import ImageDraw 4 | from generativepy.utils import temp_file 5 | 6 | ''' 7 | Create a simple bitmap image 8 | ''' 9 | 10 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 11 | draw = ImageDraw.Draw(image) 12 | draw.rectangle((60, 10, 300, 150), fill=Color("tomato").as_rgbstr()) 13 | 14 | 15 | make_bitmap(temp_file("simple-make-bitmap.png"), paint, 500, 300) 16 | -------------------------------------------------------------------------------- /examples/bitmap/simple-make-bitmaps.py: -------------------------------------------------------------------------------- 1 | from generativepy.bitmap import make_bitmaps 2 | from generativepy.color import Color 3 | from PIL import ImageDraw 4 | from generativepy.utils import temp_file 5 | 6 | ''' 7 | make_bitmaps example 8 | ''' 9 | 10 | def paint(image, pixel_width, pixel_height, frame_no, frame_count): 11 | draw = ImageDraw.Draw(image) 12 | draw.rectangle((60, 10+frame_no*30, 300, 150+frame_no*30), fill=Color("tomato").as_rgbstr()) 13 | 14 | 15 | make_bitmaps(temp_file("simple-make-bitmaps.png"), paint, 500, 400, 4) 16 | -------------------------------------------------------------------------------- /examples/color/color.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image 2 | from generativepy.color import Color 3 | 4 | ''' 5 | Create various coloured rectangles, using rgb amd rgba colours. 6 | 7 | The rectangles are drawn using the Pycairo rectangle function. 8 | ''' 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | # Set the background to white, using the paint function. 12 | # Color(1) creates a grey value of 1 (ie white) 13 | # A Color object behaves like a 4-tuple (r, g, b, a), therefore *Color unpacks a colour into the 14 | # r, g, b, a values that set_source_rgba() requires. 15 | ctx.set_source_rgba(*Color(1)) 16 | ctx.paint() 17 | 18 | # Set the colour to rgb(0.5, 0, 0), dark red. The alpha channel is automatically set to 1. 19 | # Even though we have set an rgb colour, the Color object still internally holds r, g, b, a so the 20 | # Color object still unpacks into 4 elements. 21 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 22 | ctx.rectangle(50, 50, 100, 100) 23 | ctx.fill() 24 | 25 | ctx.set_source_rgba(*Color(0, 0, 0.5)) 26 | ctx.rectangle(200, 50, 100, 100) 27 | ctx.fill() 28 | 29 | ctx.set_source_rgba(*Color(0.8, 0.8, 0)) 30 | ctx.rectangle(350, 50, 100, 100) 31 | ctx.fill() 32 | 33 | # Color(0.5).rgb creates a mid grey colour, then extracts its rgb property (0.5, 0.5, 0.5) 34 | # When we unpack this we get 3 parameters, which is what set_source_rgb requires. 35 | # We could have done: 36 | # 37 | # ctx.set_source_rgba(*Color(0.5)) 38 | # 39 | # This is just an example of how to retrieve 3 colour values if you need to 40 | ctx.set_source_rgb(*Color(0.5).rgb) 41 | ctx.rectangle(50, 200, 100, 100) 42 | ctx.fill() 43 | 44 | ctx.set_source_rgba(*Color(0.25)) 45 | ctx.rectangle(200, 200, 100, 100) 46 | ctx.fill() 47 | 48 | ctx.set_source_rgba(*Color(0.8, 0.8, 0)) 49 | ctx.rectangle(330, 180, 100, 100) 50 | ctx.fill() 51 | 52 | # Here we create a rectangle with a colour Color(1, 0, 1, 0.4). 53 | # That is magenta with a 0.4 alpha value (40% opacity, ie 60% transparent). 54 | # This overlaps the previous rectangle, and you can see the background through it. 55 | ctx.set_source_rgba(*Color(1, 0, 1, 0.4)) 56 | ctx.rectangle(370, 220, 100, 100) 57 | ctx.fill() 58 | 59 | # Create an image using the make_image function. THis calls our draw function to do the drawing. 60 | make_image("/tmp/color.png", draw, 500, 350) 61 | -------------------------------------------------------------------------------- /examples/color/csscolor.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image 2 | from generativepy.color import Color 3 | 4 | ''' 5 | In this example, similar to color.py, we set colours using CSS names. 6 | 7 | See the color module for a list of colours, or look up "CSS named colours" on the web. 8 | ''' 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | # Set the background to 'white', using the paint function. 12 | # Color('white') creates a colour with r, g, and b set to 1. Since s#no alpha is specified, alpha is 13 | # set to 1 by default. 14 | # A Color object behaves like a 4-tuple (r, g, b, a), therefore *Color unpacks a colour into the 15 | # r, g, b, a values that set_source_rgba() requires. 16 | ctx.set_source_rgba(*Color('white')) 17 | ctx.paint() 18 | 19 | ctx.set_source_rgba(*Color('salmon')) 20 | ctx.rectangle(50, 50, 100, 100) 21 | ctx.fill() 22 | 23 | ctx.set_source_rgba(*Color('firebrick')) 24 | ctx.rectangle(200, 50, 100, 100) 25 | ctx.fill() 26 | 27 | ctx.set_source_rgba(*Color('fuchsia')) 28 | ctx.rectangle(350, 50, 100, 100) 29 | ctx.fill() 30 | 31 | ctx.set_source_rgba(*Color('deepskyblue')) 32 | ctx.rectangle(50, 200, 100, 100) 33 | ctx.fill() 34 | 35 | ctx.set_source_rgba(*Color('hotpink')) 36 | ctx.rectangle(200, 200, 100, 100) 37 | ctx.fill() 38 | 39 | # *Color('lawngreen').rgb gets the rgb values of the 'lawngreen colour. 40 | # When we unpack this we get 3 parameters, which is what set_source_rgb requires. 41 | # We could have done: 42 | # 43 | # ctx.set_source_rgba(*Color('lawngreen)) 44 | # 45 | # This is just an example of how to retrieve 3 colour values if you need to 46 | ctx.set_source_rgb(*Color('lawngreen').rgb) 47 | ctx.rectangle(330, 180, 100, 100) 48 | ctx.fill() 49 | 50 | # Here we create a rectangle with a colour Color('navy', 0.4). 51 | # That is 'navy' with a 0.4 alpha value (40% opacity, ie 60% transparent). 52 | # This overlaps the previous rectangle, and you can see the background through it. 53 | ctx.set_source_rgba(*Color('navy', 0.4)) 54 | ctx.rectangle(370, 220, 100, 100) 55 | ctx.fill() 56 | 57 | 58 | make_image("/tmp/csscolor.png", draw, 500, 350) 59 | -------------------------------------------------------------------------------- /examples/color/hslbars.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image 2 | from generativepy.color import Color 3 | 4 | ''' 5 | Create colour bars to show the effect of varying h, s, l 6 | ''' 7 | 8 | def draw(ctx, width, height, frame_no, frame_count): 9 | ctx.set_source_rgba(*Color(0)) 10 | ctx.paint() 11 | 12 | # Vary hue, with s = 1, l = 0.5. 13 | # The hue is the position on the colour circle: 14 | # - hue 0 is pure red 15 | # - as hue increases the colour moves through yellow towards green 16 | # - hue 0.33 is pure green 17 | # - as hue increases the colour moves through cyan towards blue 18 | # - hue 0.66 is pure blue 19 | # - as hue increases the colour moves through magenta towards red 20 | # - as the hue approaches 1, the colour goes back to pure red. 21 | # This is a circular parameter, th eci#ycle starts again at 1 22 | for i in range(200): 23 | ctx.set_source_rgba(*Color.of_hsl(i / 200, 1, 0.5)) 24 | ctx.rectangle(2*i + 100, 100, 2, 100) 25 | ctx.fill() 26 | 27 | # Vary saturation, with h = 0.33 (green), l = 0.5. 28 | # With saturation 1, the colour is pure (fully saturated). 29 | # Reducing the saturation makes the colour more "grey". 30 | # When the saturation is 0, the colour is totally grey. All hues look identical ar zero saturation, 31 | # they all become the same grey (the shade og grey depends on the lightness value) 32 | for i in range(200): 33 | ctx.set_source_rgba(*Color.of_hsl(0.33, i/200, 0.5)) 34 | ctx.rectangle(2*i + 100, 300, 2, 100) 35 | ctx.fill() 36 | 37 | # Vary lightness, with h = 0.33 (green), s = 1. 38 | # Changing the lightness creates lighter or darker versions of the same colour, 39 | # rather like looking at the same object in a brighter or darker room. 40 | # When lightness is 0, all colours are black. When lightness is 1, all colours are white. 41 | for i in range(200): 42 | ctx.set_source_rgba(*Color.of_hsl(0.33, 1, i/200)) 43 | ctx.rectangle(2*i + 100, 500, 2, 100) 44 | ctx.fill() 45 | 46 | 47 | make_image("/tmp/hslbars.png", draw, 600, 700) 48 | -------------------------------------------------------------------------------- /examples/color/hslcolor.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image 2 | from generativepy.color import Color 3 | 4 | ''' 5 | In this example, similar to color.py, we set colours using hsl values. 6 | 7 | hsl defines colours in terms of their hue, saturation and lightness 8 | ''' 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | ctx.set_source_rgba(*Color(1)) 12 | ctx.paint() 13 | 14 | # We use Color.of_hsl() to create an HSL colour. 15 | # A hue of 0 gives pure red. We then set the saturation ot 0.5, which greys the colour 16 | # out a little, and lightness to 0.5 which creates a slightly dark red. 17 | ctx.set_source_rgba(*Color.of_hsl(0, 0.5, 0.5)) 18 | ctx.rectangle(50, 50, 100, 100) 19 | ctx.fill() 20 | 21 | # Same as before, but a hue of 0.33 gives the equivalent green colour 22 | ctx.set_source_rgba(*Color.of_hsl(0.33, 0.5, 0.5)) 23 | ctx.rectangle(200, 50, 100, 100) 24 | ctx.fill() 25 | 26 | # Same as before, but a hue of 0.66 gives the equivalent blue colour 27 | ctx.set_source_rgba(*Color.of_hsl(0.66, 0.5, 0.5)) 28 | ctx.rectangle(350, 50, 100, 100) 29 | ctx.fill() 30 | 31 | ctx.set_source_rgba(*Color.of_hsl(0, 0.25, 0.5)) 32 | ctx.rectangle(50, 200, 100, 100) 33 | ctx.fill() 34 | 35 | ctx.set_source_rgba(*Color.of_hsl(0, 0.5, 0.25)) 36 | ctx.rectangle(200, 200, 100, 100) 37 | ctx.fill() 38 | 39 | ctx.set_source_rgba(*Color.of_hsl(0, 0.5, 0.5)) 40 | ctx.rectangle(330, 180, 100, 100) 41 | ctx.fill() 42 | 43 | # Here we create a rectangle with a colour Color.of_hsla(0.5, 0.5, 0.5, 0.3). 44 | # That is blue-green with a 0.3 alpha value (30% opacity, ie 70% transparent). 45 | # This overlaps the previous rectangle, and you can see the background through it. 46 | ctx.set_source_rgba(*Color.of_hsla(0.5, 0.5, 0.5, 0.3)) 47 | ctx.rectangle(370, 220, 100, 100) 48 | ctx.fill() 49 | 50 | make_image("/tmp/hslcolor.png", draw, 500, 350) 51 | -------------------------------------------------------------------------------- /examples/color/rgbcolor.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image 2 | from generativepy.color import Color 3 | 4 | ''' 5 | Create colour squares that mix different amounts if r, g, b 6 | ''' 7 | 8 | def draw(ctx, width, height, frame_no, frame_count): 9 | ctx.set_source_rgba(*Color(1)) 10 | ctx.paint() 11 | 12 | for i in range(200): 13 | for j in range(200): 14 | ctx.set_source_rgba(*Color(i/200, j/200, 0)) 15 | ctx.rectangle(i + 50, j + 50, 1, 1) 16 | ctx.fill() 17 | 18 | for i in range(200): 19 | for j in range(200): 20 | ctx.set_source_rgba(*Color(i/200, 0, j/200)) 21 | ctx.rectangle(i + 50, j + 300, 1, 1) 22 | ctx.fill() 23 | 24 | for i in range(200): 25 | for j in range(200): 26 | ctx.set_source_rgba(*Color(0, i/200, j/200)) 27 | ctx.rectangle(i + 50, j + 550, 1, 1) 28 | ctx.fill() 29 | 30 | 31 | make_image("/tmp/rgbcolor.png", draw, 300, 800) 32 | -------------------------------------------------------------------------------- /examples/drawing/simple-make-frame.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image_frame, setup 2 | from generativepy.movie import save_frame 3 | from generativepy.geometry import text 4 | from generativepy.color import Color 5 | from generativepy.utils import temp_file 6 | 7 | ''' 8 | Illustrates simple use of make_image_frame 9 | ''' 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | 13 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4)) 14 | 15 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 16 | ctx.rectangle(0.5, 0.5, 2.5, 1.5) 17 | ctx.fill() 18 | 19 | ctx.set_source_rgba(*Color(0, 0.75, 0, 0.5)) 20 | ctx.rectangle(2, 0.25, 2.5, 1) 21 | ctx.fill() 22 | 23 | text(ctx, "simple-make-image-frame", 1, 3, size=0.2, color=Color('cadetblue'), font='Arial') 24 | 25 | 26 | frame = make_image_frame(draw, 500, 400) 27 | save_frame(temp_file("simple-make-image-frame.png"), frame) 28 | -------------------------------------------------------------------------------- /examples/drawing/simple-make-frames.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image_frames, setup 2 | from generativepy.movie import save_frames 3 | from generativepy.geometry import text 4 | from generativepy.color import Color 5 | from generativepy.utils import temp_file 6 | 7 | ''' 8 | Illustrates simple use of make_image_frame 9 | ''' 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | 13 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4)) 14 | 15 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 16 | ctx.rectangle(0.5, 0.5, 2.5, 1.5) 17 | ctx.fill() 18 | 19 | ctx.set_source_rgba(*Color(0, 0.75, 0, 0.5)) 20 | ctx.rectangle(2, 0.25, 2.5, 1) 21 | ctx.fill() 22 | 23 | text(ctx, "simple-make-image-frames {} {}".format(frame_no, frame_count), 1, 3, size=0.2, color=Color('cadetblue'), font='Arial') 24 | 25 | 26 | frames = make_image_frames(draw, 500, 400, 4) 27 | save_frames(temp_file("simple-make-image-frames.png"), frames) 28 | -------------------------------------------------------------------------------- /examples/drawing/simple-make-image-alpha.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.geometry import text 3 | from generativepy.color import Color 4 | from generativepy.utils import temp_file 5 | 6 | ''' 7 | Illustrates simple use of make_image 8 | ''' 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4, 0.4, 0.4, 0)) 13 | 14 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 15 | ctx.rectangle(0.5, 0.5, 2.5, 1.5) 16 | ctx.fill() 17 | 18 | ctx.set_source_rgba(*Color(0, 0.75, 0, 0.5)) 19 | ctx.rectangle(2, 0.25, 2.5, 1) 20 | ctx.fill() 21 | 22 | text(ctx, "simple-make-image-alpha", 1, 3, size=0.2, color=Color('cadetblue'), font='Arial') 23 | 24 | 25 | make_image(temp_file("simple-make-image-alpha.png"), draw, 500, 400, 4) 26 | -------------------------------------------------------------------------------- /examples/drawing/simple-make-image.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.geometry import text 3 | from generativepy.color import Color 4 | from generativepy.utils import temp_file 5 | 6 | ''' 7 | Illustrates simple use of make_image 8 | ''' 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4)) 13 | 14 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 15 | ctx.rectangle(0.5, 0.5, 2.5, 1.5) 16 | ctx.fill() 17 | 18 | ctx.set_source_rgba(*Color(0, 0.75, 0, 0.5)) 19 | ctx.rectangle(2, 0.25, 2.5, 1) 20 | ctx.fill() 21 | 22 | text(ctx, "simple-make-image", 1, 3, size=0.2, color=Color('cadetblue'), font='Arial') 23 | 24 | 25 | make_image(temp_file("simple-make-image.png"), draw, 500, 400) 26 | -------------------------------------------------------------------------------- /examples/drawing/simple-make-images.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_images, setup 2 | from generativepy.geometry import text 3 | from generativepy.color import Color 4 | from generativepy.utils import temp_file 5 | 6 | ''' 7 | Illustrates simple use of make_images 8 | ''' 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4)) 13 | 14 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 15 | ctx.rectangle(0.5, 0.5, 2.5, 1.5) 16 | ctx.fill() 17 | 18 | ctx.set_source_rgba(*Color(0, 0.75, 0, 0.5)) 19 | ctx.rectangle(2, 0.25, 2.5, 1) 20 | ctx.fill() 21 | 22 | text(ctx, "simple-make-images {} {}".format(frame_no, frame_count), 1, 3, size=0.2, 23 | color=Color('cadetblue'), font='Arial') 24 | 25 | 26 | make_images(temp_file("simple-make-images.png"), draw, 500, 400, 3) 27 | -------------------------------------------------------------------------------- /examples/formulas/cosh-formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/cosh-formula.png -------------------------------------------------------------------------------- /examples/formulas/create-png-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/create-png-1.png -------------------------------------------------------------------------------- /examples/formulas/create-png-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/create-png-2.png -------------------------------------------------------------------------------- /examples/formulas/create-png-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/create-png-3.png -------------------------------------------------------------------------------- /examples/formulas/create-png-formula.py: -------------------------------------------------------------------------------- 1 | from generativepy.color import Color 2 | from generativepy.formulas import rasterise_formula 3 | 4 | rasterise_formula("create-png-1", r"e^{i \pi}+1 = 0", Color("red"), dpi=800) 5 | rasterise_formula("create-png-2", r"\frac{\cancel{a}b}{\cancel{a}c} = 0", Color("green"), packages=["cancel"]) 6 | rasterise_formula("create-png-3", r"x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}", Color("black")) -------------------------------------------------------------------------------- /examples/formulas/formula-graph-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/formula-graph-1.png -------------------------------------------------------------------------------- /examples/formulas/formula-graph.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.graph import Axes, Plot 4 | from generativepy.geometry import Image 5 | from generativepy.formulas import rasterise_formula 6 | 7 | import math 8 | 9 | 10 | ''' 11 | Create a simple graph 12 | ''' 13 | 14 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 15 | 16 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 17 | 18 | # Creates a set of axes. 19 | # Use the default size of 10 units, but offset the start to place the origin in the centre 20 | axes = Axes(ctx, (50, 100), 400, 400).of_start((-5, -5)) 21 | axes.draw() 22 | 23 | # Add a curve 24 | axes.clip() 25 | Plot(axes).of_function(lambda x: 4*math.sin(x)).stroke(pattern=Color('darkred')) 26 | axes.unclip() 27 | 28 | # Add formula 29 | image, size = rasterise_formula("formula-graph-1", r"y = 4\sin{x}", Color("darkblue"), dpi=400) 30 | Image(ctx).of_file_position(image, (100, 30)).paint() 31 | 32 | make_image("/tmp/formula-graph.png", draw, 500, 550) 33 | -------------------------------------------------------------------------------- /examples/formulas/formula-test-temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/formula-test-temp.png -------------------------------------------------------------------------------- /examples/formulas/maths-formula.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Image, Text 4 | from generativepy.formulas import rasterise_formula 5 | 6 | ''' 7 | Create some formulae 8 | ''' 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 12 | 13 | image1, size1 = rasterise_formula("cosh-formula", r"\cosh{x} = \frac{e^{x}+e^{-x}}{2}", Color("dodgerblue")) 14 | image2, size2 = rasterise_formula("quadratic", r"x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}", Color("crimson"), dpi=400) 15 | image3, size3 = rasterise_formula("formula-test-temp", r"\frac{a\cancel{b}}{\cancel{b}}\si{kg.m/s^2}", 16 | Color("black"), dpi=400, packages=["cancel", "siunitx"]) 17 | Image(ctx).of_file_position(image1, (50, 50)).paint() 18 | Text(ctx).of("Size1 = " + str(size1), (50, 500)).size(20).fill(Color(0)) 19 | Image(ctx).of_file_position(image2, (50, 300)).paint() 20 | Text(ctx).of("Size2 = " + str(size2), (250, 500)).size(20).fill(Color(0)) 21 | Image(ctx).of_file_position(image3, (650, 300)).paint() 22 | Text(ctx).of("Size3 = " + str(size3), (450, 500)).size(20).fill(Color(0)) 23 | 24 | make_image("/tmp/maths-formula.png", draw, 1000, 600) 25 | -------------------------------------------------------------------------------- /examples/formulas/quadratic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/formulas/quadratic.png -------------------------------------------------------------------------------- /examples/geometry/bezier.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Bezier, Polygon 4 | 5 | ''' 6 | Create bezier curve using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # Bezier objects can only be stroked as they do not contain an area. 13 | Bezier(ctx).of_abcd((1, 1.5), (3, 0.5), (2, 2.5), (4, 1.5)).stroke(Color('darkgreen'), 0.1) 14 | 15 | # Create a polygon with a bezier side 16 | Polygon(ctx).of_points([(1, 4.5), (1, 2.5), (2, 3, 3, 4, 4, 2.5), (4, 4.5)]).fill(Color('red')).stroke(Color('blue'), 0.05) 17 | 18 | make_image("/tmp/geometry-bezier.png", draw, 500, 500) 19 | -------------------------------------------------------------------------------- /examples/geometry/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/geometry/cat.png -------------------------------------------------------------------------------- /examples/geometry/circles.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Circle, circle 4 | 5 | ''' 6 | Create circles using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # The circle function is a convenience function that adds a circle as a new the path. 13 | # You can fill or stroke it as you wish. 14 | circle(ctx, (1, 1), 0.7) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.fill() 17 | 18 | # Circle objects can be filled, stroked, filled and stroked. 19 | Circle(ctx).of_center_radius((2.5, 1), 0.7).fill(Color(0, 0, 1)).stroke(Color(0), 0.05) 20 | Circle(ctx).of_center_radius((4, 1), 0.7).as_arc(0, 1).stroke(Color(0, 0.5, 0), 0.05) 21 | 22 | Circle(ctx).of_center_radius((1, 2.5), 0.7).as_sector(1, 3).stroke(Color('orange'), 0.05) 23 | Circle(ctx).of_center_radius((2.5, 2.5), 0.7).as_sector(2, 4.5).fill(Color('cadetblue')) 24 | Circle(ctx).of_center_radius((4, 2.5), 0.7).as_sector(2.5, 6).fill(Color('yellow')).stroke(Color('magenta'), 0.05) 25 | 26 | Circle(ctx).of_center_radius((1, 4), 0.7).as_segment(1, 3).stroke(Color('orange'), 0.05) 27 | Circle(ctx).of_center_radius((2.5, 4), 0.7).as_segment(2, 4.5).fill(Color('cadetblue')) 28 | Circle(ctx).of_center_radius((4, 4), 0.7).as_segment(2.5, 6).fill(Color('yellow')).stroke(Color('magenta'), 0.05) 29 | 30 | make_image("/tmp/geometry-circles.png", draw, 500, 500) 31 | -------------------------------------------------------------------------------- /examples/geometry/clip.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Circle, Square, Text 4 | 5 | ''' 6 | Create bezier curve using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # Create a circular clip region and draw some squares in it 13 | ctx.save() 14 | Circle(ctx).of_center_radius((1.9, 1.9), 1).clip() 15 | Square(ctx).of_corner_size((1, 1), .8).fill(Color('red')) 16 | Square(ctx).of_corner_size((1, 2), .8).fill(Color('green')) 17 | Square(ctx).of_corner_size((2, 1), .8).fill(Color('blue')) 18 | Square(ctx).of_corner_size((2, 2), .8).fill(Color('black')) 19 | ctx.restore() 20 | 21 | ctx.save() 22 | Text(ctx).of("ABC", (1.5, 3.5)).font("Times").size(1.5).align_left().align_top().clip() 23 | circles = [(2, 3.8, 'orange'), (2, 4.5, 'cyan'), (3, 3.8, 'green'), 24 | (3, 4.5, 'purple'), (4, 3.8, 'yellow'), (4, 4.5, 'blue')] 25 | for x, y, color in circles: 26 | Circle(ctx).of_center_radius((x, y), 0.7).fill(Color(color)) 27 | ctx.restore() 28 | 29 | 30 | make_image("/tmp/geometry-clip.png", draw, 500, 500) 31 | -------------------------------------------------------------------------------- /examples/geometry/ellipses.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Ellipse, ellipse 4 | 5 | ''' 6 | Create ellipses using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # The ellipse function is a convenience function that adds a ellipse as a new the path. 13 | # You can fill or stroke it as you wish. 14 | ellipse(ctx, (1, 1), 0.7, 1.1) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.fill() 17 | 18 | # Ellipse objects can be filled, stroked, filled and stroked. 19 | Ellipse(ctx).of_center_radius((2.5, 1), 0.7, 0.3).fill(Color(0, 0, 1)).stroke(Color(0), 0.05) 20 | Ellipse(ctx).of_center_radius((4, 1), 0.7, 0.3).as_arc(0, 1).stroke(Color(0, 0.5, 0), 0.05) 21 | 22 | Ellipse(ctx).of_center_radius((1, 2.5), 0.7, 0.3).as_sector(1, 3).stroke(Color('orange'), 0.05) 23 | Ellipse(ctx).of_center_radius((2.5, 2.5), 0.7, 0.3).as_sector(2, 4.5).fill(Color('cadetblue')) 24 | Ellipse(ctx).of_center_radius((4, 2.5), 0.7, 0.3).as_sector(2.5, 6).fill(Color('yellow')).stroke(Color('magenta'), 0.05) 25 | 26 | Ellipse(ctx).of_center_radius((1, 4), 0.7, 0.3).as_segment(1, 3).stroke(Color('orange'), 0.05) 27 | Ellipse(ctx).of_center_radius((2.5, 4), 0.7, 0.3).as_segment(2, 4.5).fill(Color('cadetblue')) 28 | Ellipse(ctx).of_center_radius((4, 4), 0.7, 0.3).as_segment(2.5, 6).fill(Color('yellow')).stroke(Color('magenta'), 0.05) 29 | 30 | make_image("/tmp/geometry-ellipses.png", draw, 500, 500) 31 | -------------------------------------------------------------------------------- /examples/geometry/image.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Image 4 | 5 | ''' 6 | Draw an image 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=500, background=Color(0.8)) 11 | 12 | Image(ctx).of_file_position('cat.png', (50, 50)).paint() 13 | Image(ctx).of_file_position('cat.png', (300, 50)).scale(0.5).paint() 14 | 15 | make_image("/tmp/geometry-image.png", draw, 400, 200) 16 | -------------------------------------------------------------------------------- /examples/geometry/line-styles.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Rectangle 4 | from generativepy.drawing import ROUND, BEVEL, BUTT 5 | 6 | ''' 7 | Create rectangles using the geometry module. 8 | ''' 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | setup(ctx, width, height, width=5, background=Color(0.8)) 12 | 13 | 14 | # Rectangle objects can be filled, stroked, filled and stroked. 15 | Rectangle(ctx).of_corner_size((0.5, 1), 1, 1.2).stroke(Color(0, .5, 0), 0.1, dash=[0.25]) 16 | Rectangle(ctx).of_corner_size((2, 1), 1, 1.2).stroke(Color(0, .5, 0), 0.1, dash=[0.25], cap=BUTT) 17 | Rectangle(ctx).of_corner_size((3.5, 1), 1.2, 1).stroke(Color(0, .5, 0), 0.1, dash=[0.25], cap=ROUND) 18 | Rectangle(ctx).of_corner_size((0.5, 3), 1, 1.2).stroke(Color(0, .5, 0), 0.1, join=ROUND) 19 | Rectangle(ctx).of_corner_size((2, 3), 1, 1.2).stroke(Color(0, .5, 0), 0.1, join=BEVEL) 20 | Rectangle(ctx).of_corner_size((3.5, 3), 1.2, 1).stroke(Color(0, .5, 0), 0.1, miter_limit=1) 21 | 22 | make_image("/tmp/geometry-line-styles.png", draw, 500, 500) 23 | -------------------------------------------------------------------------------- /examples/geometry/lines.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Line, line 4 | 5 | ''' 6 | Create lines using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # The line function is a convenience function that adds a line as a new path. 13 | # You can fill or stroke it as you wish. 14 | line(ctx, (1, 1), (2, 3)) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.set_line_width(0.1) 17 | ctx.stroke() 18 | 19 | # Line objects can only be stroked as they do not contain an area. 20 | Line(ctx).of_start_end((3, 1), (4, 4)).stroke(Color('fuchsia'), 0.2) 21 | 22 | make_image("/tmp/geometry-lines.png", draw, 500, 500) 23 | -------------------------------------------------------------------------------- /examples/geometry/markers.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import line, polygon, angle_marker, tick, paratick, arrowhead 4 | 5 | ''' 6 | Create markers using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, background=Color(0.8)) 11 | 12 | ctx.set_source_rgba(*Color(0, 0, 0.5)) 13 | ctx.set_line_width(3) 14 | 15 | ## Draw lines with ticks, paraticks and arrowheads 16 | a = (50, 50) 17 | b = (50, 150) 18 | line(ctx, a, b) 19 | ctx.stroke() 20 | tick(ctx, a, b, length=12, gap=6) 21 | ctx.stroke() 22 | arrowhead(ctx, a, b, length=24) 23 | ctx.stroke() 24 | 25 | a = (100, 50) 26 | b = (150, 150) 27 | line(ctx, a, b) 28 | ctx.stroke() 29 | tick(ctx, a, b, 2, length=12, gap=6) 30 | ctx.stroke() 31 | 32 | a = (250, 50) 33 | b = (200, 150) 34 | line(ctx, a, b) 35 | ctx.stroke() 36 | tick(ctx, a, b, 3, length=12, gap=6) 37 | ctx.stroke() 38 | 39 | a = (350, 50) 40 | b = (350, 150) 41 | line(ctx, a, b) 42 | ctx.stroke() 43 | paratick(ctx, a, b, length=12, gap=6) 44 | ctx.stroke() 45 | 46 | a = (400, 50) 47 | b = (450, 150) 48 | line(ctx, a, b) 49 | ctx.stroke() 50 | paratick(ctx, a, b, 2, length=12, gap=6) 51 | ctx.stroke() 52 | 53 | a = (550, 150) 54 | b = (500, 50) 55 | line(ctx, a, b) 56 | ctx.stroke() 57 | paratick(ctx, a, b, 3, length=12, gap=6) 58 | ctx.stroke() 59 | 60 | ## Draw lines with angles 61 | a = (50, 250) 62 | b = (50, 450) 63 | c = (150, 450) 64 | polygon(ctx, (a, b, c), closed=False) 65 | ctx.stroke() 66 | angle_marker(ctx, a, b, c, radius=24, gap=6, right_angle=True) 67 | ctx.stroke() 68 | 69 | a = (250, 250) 70 | b = (200, 450) 71 | c = (300, 450) 72 | polygon(ctx, (a, b, c), closed=False) 73 | ctx.stroke() 74 | angle_marker(ctx, a, b, c, 3, radius=24, gap=6) 75 | ctx.stroke() 76 | 77 | a = (300, 250) 78 | b = (400, 300) 79 | c = (500, 300) 80 | polygon(ctx, (a, b, c), closed=False) 81 | ctx.stroke() 82 | angle_marker(ctx, c, b, a, radius=24, gap=6) 83 | ctx.stroke() 84 | 85 | a = (300, 350) 86 | b = (400, 400) 87 | c = (500, 400) 88 | polygon(ctx, (a, b, c), closed=False) 89 | ctx.stroke() 90 | angle_marker(ctx, a, b, c, 2, radius=24, gap=6) 91 | ctx.stroke() 92 | 93 | make_image("/tmp/geometry-markers.png", draw, 600, 500) 94 | -------------------------------------------------------------------------------- /examples/geometry/path.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Polygon, Text, Path 4 | 5 | ''' 6 | Demonstrate paths in the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # Get a polygon path object 13 | path1 = Polygon(ctx).of_points([(0, 0), (1, 1), (0.5, 2), (0.5, 1)]).path() 14 | 15 | # Get a text path object 16 | path2 = Text(ctx).of("Path text", (0, 0)).font("Times").size(0.2).align_left().align_top().path() 17 | 18 | # Apply the polygon in various places 19 | ctx.save() 20 | ctx.translate(0.5, 1) 21 | Path(ctx).of(path1).stroke(Color('darkgreen'), 0.1) 22 | ctx.restore() 23 | 24 | ctx.save() 25 | ctx.translate(1, 2.5) 26 | Path(ctx).of(path1).fill(Color('blue')) 27 | ctx.restore() 28 | 29 | ctx.save() 30 | ctx.translate(2.5, 0.5) 31 | ctx.scale(2, 2) 32 | Path(ctx).of(path1).fill(Color('orange')).stroke(Color('black'), 0.05) 33 | ctx.restore() 34 | 35 | # Apply the text in various places 36 | ctx.save() 37 | ctx.translate(0, 0) 38 | Path(ctx).of(path2).fill(Color('black')) 39 | ctx.restore() 40 | 41 | ctx.save() 42 | ctx.translate(2, 3) 43 | Path(ctx).of(path2).stroke(Color('red'), 0.01) 44 | ctx.restore() 45 | 46 | ctx.save() 47 | ctx.translate(2, 4) 48 | ctx.scale(2, 2) 49 | Path(ctx).of(path2).fill(Color('yellow')).stroke(Color('black'), 0.01) 50 | ctx.restore() 51 | 52 | 53 | make_image("/tmp/geometry-path.png", draw, 500, 500) 54 | -------------------------------------------------------------------------------- /examples/geometry/polygon.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import polygon, Polygon 4 | 5 | ''' 6 | Create polygons using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=500, background=Color(0.8)) 11 | 12 | # The polygon function is a convenience function that adds a polygon as a new path. 13 | # You can fill or stroke it as you wish. 14 | polygon(ctx, ((100, 100), (150, 50), (200, 150), (200, 200))) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.fill() 17 | 18 | Polygon(ctx).of_points([(300, 100), (300, 150), (400, 200), (450, 100)]).open().stroke(Color('orange'), 10) 19 | 20 | make_image("/tmp/geometry-polygon.png", draw, 500, 500) 21 | -------------------------------------------------------------------------------- /examples/geometry/rectangles.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Rectangle, rectangle 4 | 5 | ''' 6 | Create rectangles using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # The rectangle function is a convenience function that adds a rectangle as a new the path. 13 | # You can fill or stroke it as you wish. 14 | rectangle(ctx, (1, 1), 1, 1.2) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.fill() 17 | 18 | # Rectangle objects can be filled, stroked, filled and stroked. 19 | Rectangle(ctx).of_corner_size((3, 1), 1, 1.2).fill(Color(0, .5, 0)) 20 | Rectangle(ctx).of_corner_size((1, 3), 1.2, 1).stroke(Color(0, .5, 0), 0.1) 21 | Rectangle(ctx).of_corner_size((3, 3), 1.2, 1).fill(Color(0, 0, 1)).stroke(Color(0), 0.2) 22 | 23 | make_image("/tmp/geometry-rectangles.png", draw, 500, 500) 24 | -------------------------------------------------------------------------------- /examples/geometry/squares.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Square, square 4 | 5 | ''' 6 | Create squares using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # The square function is a convenience function that adds a square as a new the path. 13 | # You can fill or stroke it as you wish. 14 | square(ctx, (1, 1), 1) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.fill() 17 | 18 | # Square objects can be filled, stroked, filled and stroked. 19 | Square(ctx).of_corner_size((3, 1), 1).fill(Color(0, .5, 0)) 20 | Square(ctx).of_corner_size((1, 3), 1).stroke(Color(0, .5, 0), 0.1) 21 | Square(ctx).of_corner_size((3, 3), 1).fill(Color(0, 0, 1)).stroke(Color(0), 0.2) 22 | 23 | make_image("/tmp/geometry-squares.png", draw, 500, 500) 24 | -------------------------------------------------------------------------------- /examples/geometry/text.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Text, Circle 4 | 5 | ''' 6 | Create text using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | Text(ctx).of("Left", (0.5, 0.5)).font("Times").size(0.2).align_left().align_baseline().fill(Color('blue')) 13 | Text(ctx).of("Aligned", (0.5, 0.7)).font("Times").size(0.2).align_left().align_baseline().fill(Color('red')) 14 | Text(ctx).of("Text", (0.5, 0.9)).font("Times").size(0.2).align_left().align_baseline().fill(Color('blue')) 15 | 16 | Text(ctx).of("Centre", (2.5, 0.5)).font("Times").size(0.2).align_center().align_baseline().fill(Color('blue')) 17 | Text(ctx).of("Aligned", (2.5, 0.7)).font("Times").size(0.2).align_center().align_baseline().fill(Color('red')) 18 | Text(ctx).of("Text", (2.5, 0.9)).font("Times").size(0.2).align_center().align_baseline().fill(Color('blue')) 19 | 20 | Text(ctx).of("Right", (4.5, 0.5)).font("Times").size(0.2).align_right().align_baseline().fill(Color('blue')) 21 | Text(ctx).of("Aligned", (4.5, 0.7)).font("Times").size(0.2).align_right().align_baseline().fill(Color('red')) 22 | Text(ctx).of("Text", (4.5, 0.9)).font("Times").size(0.2).align_right().align_baseline().fill(Color('blue')) 23 | 24 | Circle(ctx).of_center_radius((1.9, 2), 0.02).fill(Color(0, 0, 1)) 25 | Text(ctx).of("gTop", (2, 2)).font("Times").size(0.2).align_left().align_top().fill(Color('black')) 26 | 27 | Circle(ctx).of_center_radius((1.9, 2.5), 0.02).fill(Color(0, 0, 1)) 28 | Text(ctx).of("gMid", (2, 2.5)).font("Times").size(0.2).align_left().align_middle().fill(Color('black')) 29 | 30 | Circle(ctx).of_center_radius((1.9, 3), 0.02).fill(Color(0, 0, 1)) 31 | Text(ctx).of("gBase", (2, 3)).font("Times").size(0.2).align_left().align_baseline().fill(Color('black')) 32 | 33 | Circle(ctx).of_center_radius((1.9, 3.5), 0.02).fill(Color(0, 0, 1)) 34 | Text(ctx).of("gBottom", (2, 3.5)).font("Times").size(0.2).align_left().align_bottom().fill(Color('black')) 35 | 36 | make_image("/tmp/geometry-text.png", draw, 500, 500) 37 | -------------------------------------------------------------------------------- /examples/geometry/triangles.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import triangle, Triangle 4 | 5 | ''' 6 | Create triangles using the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=500, background=Color(0.8)) 11 | 12 | # The triangle function is a convenience function that adds a triangle as a new path. 13 | # You can fill or stroke it as you wish. 14 | triangle(ctx, (100, 100), (150, 50), (200, 150)) 15 | ctx.set_source_rgba(*Color(1, 0, 0)) 16 | ctx.fill() 17 | 18 | Triangle(ctx).of_corners((300, 100), (300, 150), (400, 200)).stroke(Color('orange'), 10) 19 | 20 | make_image("/tmp/geometry-triangle.png", draw, 500, 500) 21 | -------------------------------------------------------------------------------- /examples/graph/dashedlinegraph.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup, ROUND, BUTT 2 | from generativepy.color import Color 3 | from generativepy.graph import Axes, Plot 4 | 5 | ''' 6 | Create a simple graph with dashed lines 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | 11 | setup(ctx, width, height, background=Color(1)) 12 | 13 | # Creates a set of axes. 14 | # Use the default size of 10 units, but offset the start toplace the origin inthe centre 15 | axes = Axes(ctx, (50, 50), 500, 500).of_start((-5, -5)) 16 | axes.draw() 17 | 18 | axes.clip() 19 | Plot(axes).of_function(lambda x: x * x).stroke(pattern=Color('red'), line_width=3, dash=[5]) 20 | Plot(axes).of_xy_function(lambda x: 1.5 ** x).stroke(pattern=Color('green'), line_width=5, dash=[10, 10, 20, 10], 21 | cap=ROUND) 22 | Plot(axes).of_polar_function(lambda x: 2 * x).stroke(pattern=Color('blue'), line_width=4, dash=[5], cap=BUTT) 23 | axes.unclip() 24 | 25 | 26 | make_image("/tmp/dashedlinegraph.png", draw, 600, 600) 27 | -------------------------------------------------------------------------------- /examples/graph/simplegraph.py: -------------------------------------------------------------------------------- 1 | from generativepy import graph 2 | from generativepy.drawing import make_image, setup 3 | from generativepy.color import Color 4 | from generativepy.graph import Axes, Plot 5 | 6 | ''' 7 | Create a simple graph 8 | ''' 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | 12 | setup(ctx, width, height, background=Color(1)) 13 | 14 | # Creates a set of axes. 15 | # Use the default size of 10 units, but offset the start toplace the origin inthe centre 16 | axes = Axes(ctx, (50, 50), 500, 500).of_start((-5, -5)) 17 | axes.draw() 18 | 19 | # Add various curves 20 | axes.clip() 21 | Plot(axes).of_function(lambda x: x * x).stroke(pattern=Color('red')) 22 | Plot(axes).of_xy_function(lambda x: 1.5 ** x).stroke(pattern=Color('green')) 23 | Plot(axes).of_polar_function(lambda x: 2 * x).stroke(pattern=Color('blue')) 24 | axes.unclip() 25 | 26 | make_image("/tmp/simplegraph.png", draw, 500, 500) 27 | -------------------------------------------------------------------------------- /examples/movie/moviebuilder-audio.py: -------------------------------------------------------------------------------- 1 | from generativepy.geometry import Circle, Rectangle 2 | from generativepy.color import Color 3 | from generativepy.drawing import setup, make_image_frames 4 | from generativepy.movie import MovieBuilder 5 | from generativepy.tween import TweenVector 6 | from generativepy.utils import temp_file 7 | 8 | # Create a movie with two scenes and audio files 9 | WIDTH = 200 # Nominal width, used for all drawing 10 | HEIGHT = 300 # Nominal width, used for all drawing 11 | OUTPUTWIDTH = 200 # Final video width, could be made larger for 4k output 12 | OUTPUTHEIGHT = 300 # Final video height, could be made larger for 4k output 13 | 14 | FRATE = 10 15 | VIDEONAME = temp_file("movie-audio.mp4") 16 | 17 | def t2f(t): # Convert a time in seconds to a number of frames 18 | return int(t*FRATE) 19 | 20 | def scene1(duration): 21 | 22 | position = TweenVector((0, 0)).to((200, 300), t2f(duration)) 23 | 24 | def draw(ctx, pixel_width, pixel_height, fn, frame_count): 25 | setup(ctx, pixel_width, pixel_height, width=OUTPUTWIDTH, height=OUTPUTHEIGHT, background=Color(0.5)) 26 | 27 | Circle(ctx).of_center_radius(position[fn], 30).fill(Color("red")) 28 | 29 | return make_image_frames(draw, WIDTH, HEIGHT, t2f(duration)), duration 30 | 31 | def scene2(duration): 32 | 33 | position = TweenVector((0, 0)).to((200, 300), t2f(duration)) 34 | 35 | def draw(ctx, pixel_width, pixel_height, fn, frame_count): 36 | setup(ctx, pixel_width, pixel_height, width=OUTPUTWIDTH, height=OUTPUTHEIGHT, background=Color(0.5)) 37 | 38 | Rectangle(ctx).of_corner_size(position[fn], 50, 50).fill(Color("green")) 39 | 40 | return make_image_frames(draw, WIDTH, HEIGHT, t2f(duration)), duration 41 | 42 | 43 | 44 | builder = MovieBuilder(FRATE) 45 | builder.add_scene(scene1(5), "scene1-audio.mp3") 46 | builder.add_scene(scene2(5), "scene2-audio.mp3") 47 | 48 | # builder.make_movie(VIDEONAME, n) creates video of just scene n 49 | # builder.make_movie(VIDEONAME) creates full video 50 | builder.make_movie(VIDEONAME) -------------------------------------------------------------------------------- /examples/movie/moviebuilder-no-audio.py: -------------------------------------------------------------------------------- 1 | from generativepy.geometry import Circle, Rectangle 2 | from generativepy.color import Color 3 | from generativepy.drawing import setup, make_image_frames 4 | from generativepy.movie import MovieBuilder 5 | from generativepy.tween import TweenVector 6 | from generativepy.utils import temp_file 7 | 8 | # Create a movie with two scenes and audio files 9 | WIDTH = 200 # Nominal width, used for all drawing 10 | HEIGHT = 300 # Nominal width, used for all drawing 11 | OUTPUTWIDTH = 200 # Final video width, could be made larger for 4k output 12 | OUTPUTHEIGHT = 300 # Final video height, could be made larger for 4k output 13 | 14 | FRATE = 10 15 | VIDEONAME = temp_file("movie-no-audio.mp4") 16 | 17 | def t2f(t): # Convert a time in seconds to a number of frames 18 | return int(t*FRATE) 19 | 20 | def scene1(duration): 21 | 22 | position = TweenVector((0, 0)).to((200, 300), t2f(duration)) 23 | 24 | def draw(ctx, pixel_width, pixel_height, fn, frame_count): 25 | setup(ctx, pixel_width, pixel_height, width=OUTPUTWIDTH, height=OUTPUTHEIGHT, background=Color(0.5)) 26 | 27 | Circle(ctx).of_center_radius(position[fn], 30).fill(Color("red")) 28 | 29 | return make_image_frames(draw, WIDTH, HEIGHT, t2f(duration)), duration 30 | 31 | def scene2(duration): 32 | 33 | position = TweenVector((0, 0)).to((200, 300), t2f(duration)) 34 | 35 | def draw(ctx, pixel_width, pixel_height, fn, frame_count): 36 | setup(ctx, pixel_width, pixel_height, width=OUTPUTWIDTH, height=OUTPUTHEIGHT, background=Color(0.5)) 37 | 38 | Rectangle(ctx).of_corner_size(position[fn], 50, 50).fill(Color("green")) 39 | 40 | return make_image_frames(draw, WIDTH, HEIGHT, t2f(duration)), duration 41 | 42 | 43 | 44 | builder = MovieBuilder(FRATE) 45 | builder.add_scene(scene1(5)) 46 | builder.add_scene(scene2(5)) 47 | 48 | # builder.make_movie(VIDEONAME, n) creates video of just scene n 49 | # builder.make_movie(VIDEONAME) creates full video 50 | builder.make_movie(VIDEONAME) -------------------------------------------------------------------------------- /examples/movie/scene1-audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/movie/scene1-audio.mp3 -------------------------------------------------------------------------------- /examples/movie/scene2-audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/examples/movie/scene2-audio.mp3 -------------------------------------------------------------------------------- /examples/movie/simplemovie.py: -------------------------------------------------------------------------------- 1 | from generativepy.movie import save_frames 2 | from generativepy.drawing import make_image_frames 3 | from generativepy.color import Color 4 | 5 | ''' 6 | Create a simple movie of 20 frames 7 | The frames will be stored as PNG images in /tmp with names 8 | - movie00000000.pngmake_image_frames 9 | - movie00000001.png 10 | - movie00000002.png 11 | - etc 12 | ''' 13 | 14 | def draw(ctx, width, height, frame_no, frame_count): 15 | ctx.set_source_rgba(*Color(1).rgba) 16 | ctx.paint() 17 | 18 | # Draw a rectangle. 19 | # It's position changes for each frame. 20 | ctx.set_source_rgba(*Color(0.5, 0, 0).rgba) 21 | ctx.rectangle(50+20*frame_no, 50+10*frame_no, 100, 100) 22 | ctx.fill() 23 | 24 | frames = make_image_frames(draw, 500, 350, 20) 25 | save_frames("/tmp/movie", frames) 26 | -------------------------------------------------------------------------------- /examples/nparray/save-reload-nparray.py: -------------------------------------------------------------------------------- 1 | from generativepy.nparray import make_nparray_frame, save_nparray, load_nparray 2 | from generativepy.utils import temp_file 3 | from generativepy.movie import save_frame 4 | 5 | ''' 6 | saving and loading nparray example 7 | ''' 8 | 9 | def paint(array, pixel_width, pixel_height, frame_no, frame_count): 10 | array[10:150,60:300] = [255, 255, 0] 11 | 12 | frame = make_nparray_frame(paint, 500, 300) 13 | save_nparray(temp_file("saved-nparray.dat"), frame) 14 | frame2 = load_nparray(temp_file("saved-nparray.dat")) 15 | save_frame(temp_file("save-reload-nparray.png"), frame2) -------------------------------------------------------------------------------- /examples/nparray/simple-make-nparray-frame.py: -------------------------------------------------------------------------------- 1 | from generativepy.nparray import make_nparray_frame 2 | from generativepy.movie import save_frame 3 | from generativepy.utils import temp_file 4 | 5 | ''' 6 | make_nparray_frame example 7 | ''' 8 | 9 | def paint(array, pixel_width, pixel_height, frame_no, frame_count): 10 | array[10:150,60:300] = [255, 128, 0] 11 | 12 | 13 | frame = make_nparray_frame(paint, 500, 300) 14 | save_frame(temp_file("simple-make-nparray-frame.png"), frame) -------------------------------------------------------------------------------- /examples/nparray/simple-make-nparray-frames.py: -------------------------------------------------------------------------------- 1 | from generativepy.nparray import make_nparray_frames 2 | from generativepy.movie import save_frames 3 | from generativepy.utils import temp_file 4 | 5 | ''' 6 | make_nparray_frames example 7 | ''' 8 | 9 | def paint(array, pixel_width, pixel_height, frame_no, frame_count): 10 | array[10+frame_no*30:150+frame_no*30, 60:300] = [255, 128, 0] 11 | 12 | 13 | frames = make_nparray_frames(paint, 500, 300, 4) 14 | save_frames(temp_file("simple-make-nparray-frames.png"), frames) -------------------------------------------------------------------------------- /examples/nparray/simple-make-nparray.py: -------------------------------------------------------------------------------- 1 | from generativepy.nparray import make_nparray 2 | from generativepy.utils import temp_file 3 | 4 | ''' 5 | make_nparray example 6 | ''' 7 | 8 | def paint(array, pixel_width, pixel_height, frame_no, frame_count): 9 | array[10:150,60:300] = [255, 128, 0] 10 | 11 | make_nparray(temp_file("simple-make-nparray.png"), paint, 500, 300) 12 | -------------------------------------------------------------------------------- /examples/nparray/simple-make-nparrays.py: -------------------------------------------------------------------------------- 1 | from generativepy.nparray import make_nparrays 2 | from generativepy.utils import temp_file 3 | 4 | ''' 5 | make_nparrays example 6 | ''' 7 | 8 | def paint(array, pixel_width, pixel_height, frame_no, frame_count): 9 | array[10+frame_no*30:150+frame_no*30, 60:300] = [255, 128, 0] 10 | 11 | make_nparrays(temp_file("simple-make-nparrays.png"), paint, 500, 400, 4) 12 | -------------------------------------------------------------------------------- /examples/readme.md: -------------------------------------------------------------------------------- 1 | # Old examples 2 | 3 | These examples are out of date and should be regarded as deprecated. 4 | 5 | For more up-to-date information see these folders in this repository: 6 | 7 | * tutorial folder (relates to the tutorial on [PythonInfomer](https://pythoninformer.com/generative-art/generativepy-tutorial/)) 8 | * blog folder (relates to the examples on [my blog](https://martinmcbride.org/)) 9 | 10 | -------------------------------------------------------------------------------- /examples/start/simpleimage.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.geometry import text 3 | from generativepy.color import Color 4 | 5 | ''' 6 | Create a very simple image 7 | ''' 8 | 9 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 10 | 11 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4)) 12 | 13 | ctx.set_source_rgba(*Color(0.5, 0, 0)) 14 | ctx.rectangle(0.5, 0.5, 2.5, 1.5) 15 | ctx.fill() 16 | 17 | ctx.set_source_rgba(*Color(0, 0.75, 0, 0.5)) 18 | ctx.rectangle(2, 0.25, 2.5, 1) 19 | ctx.fill() 20 | 21 | text(ctx, "Simple Image", 1, 3, size=0.5, color=Color('cadetblue'), font='Arial') 22 | 23 | 24 | make_image("/tmp/simpleimage.png", draw, 500, 400) 25 | -------------------------------------------------------------------------------- /examples/tween/simpletween.py: -------------------------------------------------------------------------------- 1 | from generativepy.tween import Tween, set_frame_rate 2 | 3 | # Set global frame rate to 4 4 | set_frame_rate(4) 5 | 6 | # Create a tween. Times are in seconds (1 sec = 4 frames) 7 | tween = Tween(3.0).wait(2).to(12.0, 5).wait(6.5).set(7.0).wait(7) 8 | 9 | # You can use len() to fund the length of the tween in frames 10 | print('Length = ', len(tween)) 11 | 12 | # You can access the value for a particular frame using indexing [] 13 | # or the get() function. 14 | print('tween[6] (1.5 seconds) = ', tween[6]) 15 | print('tween[12] (3 seconds) = ', tween.get(12)) 16 | print('tween[80] (20 seconds) = ', tween.get(80)) 17 | 18 | # The tween can be accessed as a sequence. Here we use a list 19 | # comprehension to convert the values to strings that print the 20 | # entire sequence 21 | strings = [str(x) for x in tween] 22 | print(', '.join(strings)) 23 | 24 | -------------------------------------------------------------------------------- /generativepy/__init__.py: -------------------------------------------------------------------------------- 1 | name = "generativepy" 2 | -------------------------------------------------------------------------------- /generativepy/gif.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2020-09-10 3 | # Copyright (C) 2020, Martin McBride 4 | # License: MIT 5 | 6 | import imageio 7 | import subprocess 8 | 9 | """ 10 | This module creates an animated GIF from a sequence of frames. 11 | """ 12 | 13 | def save_animated_gif(filepath, frames, delay, loop=0): 14 | ''' 15 | Save a set of frames as an animated GIF. 16 | Requires gifsicle to be installed 17 | 18 | Args: 19 | filepath: str - Output filepath. 20 | frames: iterator returning frames - sequence of frames. 21 | delay: number - Delay between frames in seconds (eg 0.2 for frame rate of 5 frames per second). 22 | loop: function - Easing function. Thus accepts a value that varies between 0 and 1.0. 23 | ''' 24 | if not filepath.lower().endswith('.gif'): 25 | filepath += '.gif' 26 | images = list(frames) 27 | imageio.mimsave(filepath, images, duration=delay) 28 | subprocess.run(['gifsicle', '-b', '--colors', '256', '--optimize=3', filepath]) 29 | -------------------------------------------------------------------------------- /generativepy/utils.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2020-11-22 3 | # Copyright (C) 2020, Martin McBride 4 | # License: MIT 5 | 6 | import sys 7 | import tempfile 8 | import os.path 9 | 10 | def correct_pycairo_byte_order(array, channels): 11 | """ 12 | If byte ordering is little endian, bitmap data from Pycairo needs swapping 13 | Convert a numpy array from BGR/BGRA ordering to RGB/RGBA. 14 | Conversion is performed in place 15 | 16 | Args: 17 | array: numpy array - the image data. 18 | channels: int - number of colour channels (3 or 4 for RGB or RGBA). 19 | 20 | Returns: 21 | Converted array (will be original array if no conversion needed). 22 | """ 23 | 24 | if sys.byteorder == 'little' and array.ndim == 3: 25 | if channels==3: 26 | array[:, :, [0, 1, 2]] = array[:, :, [2, 1, 0]] 27 | elif channels==4: 28 | array[:, :, [0, 1, 2, 3]] = array[:, :, [2, 1, 0, 3]] 29 | 30 | return array 31 | 32 | def temp_file(*names): 33 | """ 34 | Create a temporary file name path within the system temp folder. 35 | If *name has one element, return /name[0] 36 | If *name has two element, return /name[0]/name[1] etc 37 | 38 | Args: 39 | *names: one or more file/folder names. 40 | 41 | Returns: 42 | Full file path as a string. 43 | """ 44 | 45 | folder = tempfile.gettempdir() 46 | return os.path.join(folder, *names) 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /imagetests/all_image_tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | loader = unittest.TestLoader() 3 | start_dir = './' 4 | suite = loader.discover(start_dir) 5 | 6 | runner = unittest.TextTestRunner() 7 | runner.run(suite) -------------------------------------------------------------------------------- /imagetests/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/cat.png -------------------------------------------------------------------------------- /imagetests/formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/formula.png -------------------------------------------------------------------------------- /imagetests/image_test_helper.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2020-12-28 3 | # Copyright (C) 2020, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.utils import temp_file 7 | from pathlib import Path 8 | from PIL import Image 9 | from PIL import ImageChops 10 | import os 11 | 12 | def compare_images(path1, path2): 13 | with Image.open(path1) as im1: 14 | with Image.open(path2) as im2: 15 | if im1.size != im2.size: 16 | return False 17 | if im1.mode != im2.mode: 18 | return False 19 | diff = ImageChops.difference(im1, im2) 20 | if diff.getbbox(): 21 | diff.save(path1+"difference.png") 22 | R, G, B = diff.convert('RGB').split() 23 | r = R.load() 24 | g = G.load() 25 | b = B.load() 26 | w, h = diff.size 27 | 28 | # Convert non-black pixels to white 29 | for i in range(w): 30 | for j in range(h): 31 | if (r[i, j] != 0 or g[i, j] != 0 or b[i, j] != 0): 32 | r[i, j] = 255 33 | g[i, j] = 255 34 | b[i, j] = 255 35 | 36 | # Merge just the R channel as all channels 37 | im = Image.merge('RGB', (R, R, R)) 38 | im.save(path1+"diff-enhance.png") 39 | return False 40 | 41 | return True 42 | 43 | 44 | def run_image_test(name, creator): 45 | """ 46 | Create an image and check it matches the reference image 47 | :param name: test name (used as the image file name) 48 | :param creator: a function that takes a filepath ans creates an image 49 | :return: 50 | """ 51 | # Create test output folder 52 | out_folder_name = 'genpy-test-images' 53 | ref_folder_name = 'images' 54 | out_folder = temp_file(out_folder_name) 55 | Path(out_folder).mkdir(exist_ok=True) 56 | 57 | # Warn if output file exists, or if reference file doesn't exist 58 | out_file = temp_file(out_folder_name, name) 59 | ref_file = os.path.join(ref_folder_name, name) 60 | if Path(out_file).exists(): 61 | print("WARNING temp file {} already exists".format(out_file)) 62 | if not Path(ref_file).exists(): 63 | print("WARNING reference file {} doesn't exist".format(ref_file)) 64 | 65 | # Create the test image file 66 | creator(out_file) 67 | return compare_images(out_file, ref_file) 68 | -------------------------------------------------------------------------------- /imagetests/images/test_artistic_color_scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_artistic_color_scheme.png -------------------------------------------------------------------------------- /imagetests/images/test_basic_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_basic_colors.png -------------------------------------------------------------------------------- /imagetests/images/test_bezier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_bezier.png -------------------------------------------------------------------------------- /imagetests/images/test_book_color_blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_book_color_blocks.png -------------------------------------------------------------------------------- /imagetests/images/test_book_color_scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_book_color_scheme.png -------------------------------------------------------------------------------- /imagetests/images/test_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_circle.png -------------------------------------------------------------------------------- /imagetests/images/test_clip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_clip.png -------------------------------------------------------------------------------- /imagetests/images/test_complex_paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_complex_paths.png -------------------------------------------------------------------------------- /imagetests/images/test_css_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_css_colors.png -------------------------------------------------------------------------------- /imagetests/images/test_dark_color_scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_dark_color_scheme.png -------------------------------------------------------------------------------- /imagetests/images/test_deprecated_markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_deprecated_markers.png -------------------------------------------------------------------------------- /imagetests/images/test_drawing_make_frame_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_drawing_make_frame_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_drawing_make_frame_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_drawing_make_frame_rgba.png -------------------------------------------------------------------------------- /imagetests/images/test_drawing_make_image_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_drawing_make_image_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_drawing_make_image_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_drawing_make_image_rgba.png -------------------------------------------------------------------------------- /imagetests/images/test_ellipse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_ellipse.png -------------------------------------------------------------------------------- /imagetests/images/test_flatcolor_cube_drawing3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_flatcolor_cube_drawing3d.png -------------------------------------------------------------------------------- /imagetests/images/test_flatcolor_drawing3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_flatcolor_drawing3d.png -------------------------------------------------------------------------------- /imagetests/images/test_flatcolor_rectangle_drawing3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_flatcolor_rectangle_drawing3d.png -------------------------------------------------------------------------------- /imagetests/images/test_formula_default_dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_formula_default_dpi.png -------------------------------------------------------------------------------- /imagetests/images/test_formula_dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_formula_dpi.png -------------------------------------------------------------------------------- /imagetests/images/test_formula_optional_package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_formula_optional_package.png -------------------------------------------------------------------------------- /imagetests/images/test_geometry_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_geometry_image.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_appearance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_appearance.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_axes_position.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_axes_position.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_border.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_dashed_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_dashed_line.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_extent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_extent.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_filled.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_formatted_axis_values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_formatted_axis_values.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_gradient_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_gradient_background.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_gradient_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_gradient_plot.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_multiple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_multiple.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_scale_factor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_scale_factor.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_scatter.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_simple.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_styles.png -------------------------------------------------------------------------------- /imagetests/images/test_graph_subdivisions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_graph_subdivisions.png -------------------------------------------------------------------------------- /imagetests/images/test_hsl_color_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_hsl_color_bars.png -------------------------------------------------------------------------------- /imagetests/images/test_hsl_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_hsl_colors.png -------------------------------------------------------------------------------- /imagetests/images/test_large_graph_scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_large_graph_scale.png -------------------------------------------------------------------------------- /imagetests/images/test_light_dark_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_light_dark_colors.png -------------------------------------------------------------------------------- /imagetests/images/test_line_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_line_full.png -------------------------------------------------------------------------------- /imagetests/images/test_line_ray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_line_ray.png -------------------------------------------------------------------------------- /imagetests/images/test_line_segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_line_segment.png -------------------------------------------------------------------------------- /imagetests/images/test_line_styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_line_styles.png -------------------------------------------------------------------------------- /imagetests/images/test_lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_lines.png -------------------------------------------------------------------------------- /imagetests/images/test_make_bitmap_frame_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_bitmap_frame_gray.png -------------------------------------------------------------------------------- /imagetests/images/test_make_bitmap_frame_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_bitmap_frame_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_make_bitmap_frame_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_bitmap_frame_rgba.png -------------------------------------------------------------------------------- /imagetests/images/test_make_bitmap_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_bitmap_gray.png -------------------------------------------------------------------------------- /imagetests/images/test_make_bitmap_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_bitmap_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_make_bitmap_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_bitmap_rgba.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_frame_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_frame_gray.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_frame_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_frame_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_frame_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_frame_rgba.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_frame_with_output_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_frame_with_output_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_gray.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_rgb.png -------------------------------------------------------------------------------- /imagetests/images/test_make_nparray_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_make_nparray_rgba.png -------------------------------------------------------------------------------- /imagetests/images/test_markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_markers.png -------------------------------------------------------------------------------- /imagetests/images/test_old_markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_old_markers.png -------------------------------------------------------------------------------- /imagetests/images/test_overlay_nparrays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_overlay_nparrays.png -------------------------------------------------------------------------------- /imagetests/images/test_parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_parameters.png -------------------------------------------------------------------------------- /imagetests/images/test_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_path.png -------------------------------------------------------------------------------- /imagetests/images/test_polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_polygon.png -------------------------------------------------------------------------------- /imagetests/images/test_povray_Plot3dZofXY_non_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_povray_Plot3dZofXY_non_default.png -------------------------------------------------------------------------------- /imagetests/images/test_povray_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_povray_axes.png -------------------------------------------------------------------------------- /imagetests/images/test_povray_non_default_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_povray_non_default_axes.png -------------------------------------------------------------------------------- /imagetests/images/test_povray_scene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_povray_scene.png -------------------------------------------------------------------------------- /imagetests/images/test_povray_test_povray_Plot3dZofXY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_povray_test_povray_Plot3dZofXY.png -------------------------------------------------------------------------------- /imagetests/images/test_rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_rectangle.png -------------------------------------------------------------------------------- /imagetests/images/test_regular_polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_regular_polygon.png -------------------------------------------------------------------------------- /imagetests/images/test_rgb_color_panels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_rgb_color_panels.png -------------------------------------------------------------------------------- /imagetests/images/test_shapes_linear_gradient_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_shapes_linear_gradient_fill.png -------------------------------------------------------------------------------- /imagetests/images/test_simple_drawing3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_simple_drawing3d.png -------------------------------------------------------------------------------- /imagetests/images/test_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_square.png -------------------------------------------------------------------------------- /imagetests/images/test_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_table.png -------------------------------------------------------------------------------- /imagetests/images/test_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_text.png -------------------------------------------------------------------------------- /imagetests/images/test_text_offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_text_offset.png -------------------------------------------------------------------------------- /imagetests/images/test_transform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_transform.png -------------------------------------------------------------------------------- /imagetests/images/test_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_triangle.png -------------------------------------------------------------------------------- /imagetests/images/test_turtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_turtle.png -------------------------------------------------------------------------------- /imagetests/images/test_tween_easing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/imagetests/images/test_tween_easing.png -------------------------------------------------------------------------------- /imagetests/readme.md: -------------------------------------------------------------------------------- 1 | # Image tests 2 | 3 | This area contains image tests for generativepy. 4 | 5 | The purpose of these tests is to check the images created by generativepy is an automated, repeatable way. 6 | 7 | Each test creates a PNG file the exercises particular drawing capabilities. This PNG files are written out to the system temp folder. 8 | 9 | After each test, the image created is compared, pixel-for-pixel, with the equivalent reference image in the images folder. The reference images have been previously checked manually. 10 | 11 | The tests are implemented as unit tests to allow the unit test discovery and reporting tools to be used. 12 | 13 | Run all_image_tests.py to run all the tests. 14 | 15 | There is also a set of unit tests in the test folder which test the non-image functionality. Both the image tests and the unit tests should be run to check the library. 16 | 17 | -------------------------------------------------------------------------------- /imagetests/test_bitmap_module.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from generativepy.bitmap import make_bitmap, make_bitmap_frame 3 | from generativepy.movie import save_frame 4 | from image_test_helper import run_image_test 5 | from generativepy.color import Color 6 | from PIL import ImageDraw 7 | 8 | """ 9 | Test each function of the bitmap module, with 3 and 4 channel output 10 | """ 11 | 12 | 13 | def draw(image, pixel_width, pixel_height, frame_no, frame_count): 14 | """ 15 | Draw a rectangle on the bitmap 16 | :param image: 17 | :param pixel_width: 18 | :param pixel_height: 19 | :param frame_no: 20 | :param frame_count: 21 | :return: 22 | """ 23 | imagedraw = ImageDraw.Draw(image) 24 | imagedraw.rectangle((60, 10, 300, 150), fill=Color("tomato").as_rgbstr()) 25 | 26 | 27 | class TestBitmapModule(unittest.TestCase): 28 | 29 | def test_make_bitmap_gray(self): 30 | def creator(file): 31 | make_bitmap(file, draw, 400, 400, channels=1) 32 | 33 | self.assertTrue(run_image_test('test_make_bitmap_gray.png', creator)) 34 | 35 | def test_make_bitmap_rgb(self): 36 | def creator(file): 37 | make_bitmap(file, draw, 400, 400, channels=3) 38 | 39 | self.assertTrue(run_image_test('test_make_bitmap_rgb.png', creator)) 40 | 41 | def test_make_bitmap_rgba(self): 42 | def creator(file): 43 | make_bitmap(file, draw, 400, 400, channels=4) 44 | 45 | self.assertTrue(run_image_test('test_make_bitmap_rgba.png', creator)) 46 | 47 | def test_make_bitmap_frame_gray(self): 48 | def creator(file): 49 | frame = make_bitmap_frame(draw, 400, 400, channels=1) 50 | save_frame(file, frame) 51 | 52 | self.assertTrue(run_image_test('test_make_bitmap_frame_gray.png', creator)) 53 | 54 | def test_make_bitmap_frame_rgb(self): 55 | def creator(file): 56 | frame = make_bitmap_frame(draw, 400, 400, channels=3) 57 | save_frame(file, frame) 58 | 59 | self.assertTrue(run_image_test('test_make_bitmap_frame_rgb.png', creator)) 60 | 61 | def test_make_bitmap_frame_rgba(self): 62 | def creator(file): 63 | frame = make_bitmap_frame(draw, 400, 400, channels=4) 64 | save_frame(file, frame) 65 | 66 | self.assertTrue(run_image_test('test_make_bitmap_frame_rgba.png', creator)) 67 | 68 | -------------------------------------------------------------------------------- /imagetests/test_drawing_module.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from generativepy.drawing import setup, make_image, make_image_frame 3 | from generativepy.movie import save_frame 4 | from generativepy.geometry import Rectangle 5 | from image_test_helper import run_image_test 6 | from generativepy.color import Color 7 | 8 | """ 9 | Test each function of the drawing module, with 3 and 4 channel output 10 | """ 11 | 12 | 13 | def draw_rgb(ctx, pixel_width, pixel_height, frame_no, frame_count): 14 | """ 15 | Draw a rectangle on an RGB background 16 | :param ctx: 17 | :param pixel_width: 18 | :param pixel_height: 19 | :param frame_no: 20 | :param frame_count: 21 | :return: 22 | """ 23 | setup(ctx, pixel_width, pixel_height, width=4, background=Color(0.8)) 24 | Rectangle(ctx).of_corner_size((0.5, 1), 3, 1.5).fill(Color(0, .5, 0)) 25 | 26 | 27 | def draw_rgba(ctx, pixel_width, pixel_height, frame_no, frame_count): 28 | """ 29 | Draw a rectangle on an RGBA background 30 | :param ctx: 31 | :param pixel_width: 32 | :param pixel_height: 33 | :param frame_no: 34 | :param frame_count: 35 | :return: 36 | """ 37 | setup(ctx, pixel_width, pixel_height, width=4, background=Color(0.8, 0.5)) 38 | Rectangle(ctx).of_corner_size((0.5, 1), 3, 1.5).fill(Color(0, .5, 0)) 39 | 40 | 41 | class TestDrawingModule(unittest.TestCase): 42 | 43 | def test_drawing_make_image_rgb(self): 44 | def creator(file): 45 | make_image(file, draw_rgb, 400, 400, channels=3) 46 | 47 | self.assertTrue(run_image_test('test_drawing_make_image_rgb.png', creator)) 48 | 49 | def test_drawing_make_image_rgba(self): 50 | def creator(file): 51 | make_image(file, draw_rgba, 400, 400, channels=4) 52 | 53 | self.assertTrue(run_image_test('test_drawing_make_image_rgba.png', creator)) 54 | 55 | def test_drawing_make_frame_rgb(self): 56 | def creator(file): 57 | frame = make_image_frame(draw_rgb, 400, 400, channels=3) 58 | save_frame(file, frame) 59 | 60 | self.assertTrue(run_image_test('test_drawing_make_frame_rgb.png', creator)) 61 | 62 | def test_drawing_make_frame_rgba(self): 63 | def creator(file): 64 | frame = make_image_frame(draw_rgba, 400, 400, channels=4) 65 | save_frame(file, frame) 66 | 67 | self.assertTrue(run_image_test('test_drawing_make_frame_rgba.png', creator)) 68 | 69 | -------------------------------------------------------------------------------- /imagetests/test_formulas_module.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from generativepy.drawing import setup, make_image 3 | from image_test_helper import run_image_test 4 | from generativepy.color import Color 5 | from generativepy.geometry import Image, Text 6 | from generativepy.formulas import rasterise_formula 7 | import math 8 | 9 | """ 10 | Test the formulas module. 11 | """ 12 | 13 | 14 | class TestGeometryImages(unittest.TestCase): 15 | 16 | def test_formula_default_dpi(self): 17 | def draw(ctx, width, height, frame_no, frame_count): 18 | setup(ctx, width, height, background=Color(0.8)) 19 | image, size = rasterise_formula("formula-test-temp", r"\cosh{x} = \frac{e^{x}+e^{-x}}{2}", Color("dodgerblue")) 20 | Image(ctx).of_file_position(image, (50, 50)).paint() 21 | Text(ctx).of("Size = " + str(size), (50, 300)).size(20).fill(Color(0)) 22 | 23 | def creator(file): 24 | make_image(file, draw, 800, 400) 25 | 26 | self.assertTrue(run_image_test('test_formula_default_dpi.png', creator)) 27 | 28 | def test_formula_optional_package(self): 29 | def draw(ctx, width, height, frame_no, frame_count): 30 | setup(ctx, width, height, background=Color(0.8)) 31 | image, size = rasterise_formula("formula-test-temp", r"\frac{a\cancel{b}}{\cancel{b}}\si{kg.m/s^2}", Color("crimson"), dpi=400, packages=["cancel", "siunitx"]) 32 | Image(ctx).of_file_position(image, (50, 50)).paint() 33 | Text(ctx).of("Size = " + str(size), (50, 300)).size(20).fill(Color(0)) 34 | 35 | def creator(file): 36 | make_image(file, draw, 800, 400) 37 | 38 | self.assertTrue(run_image_test('test_formula_optional_package.png', creator)) 39 | 40 | def test_formula_dpi(self): 41 | def draw(ctx, width, height, frame_no, frame_count): 42 | setup(ctx, width, height, background=Color(0.8)) 43 | image, size = rasterise_formula("formula-test-temp", r"x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}", Color("crimson"), dpi=400) 44 | Image(ctx).of_file_position(image, (50, 50)).paint() 45 | Text(ctx).of("Size = " + str(size), (50, 300)).size(20).fill(Color(0)) 46 | 47 | def creator(file): 48 | make_image(file, draw, 800, 400) 49 | 50 | self.assertTrue(run_image_test('test_formula_dpi.png', creator)) 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /imagetests/test_table_module.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from generativepy.geometry import Text 4 | 5 | from generativepy.drawing import setup, make_image, CENTER, MIDDLE 6 | from generativepy.table import Table 7 | from image_test_helper import run_image_test 8 | from generativepy.color import Color 9 | 10 | """ 11 | Test the table module. 12 | """ 13 | 14 | 15 | class TestTableImages(unittest.TestCase): 16 | 17 | def test_table(self): 18 | """ 19 | Draw 2 tables, one with default styling and one with custom colours and line width. 20 | Add some text to some of the cells 21 | :return: 22 | """ 23 | def draw(ctx, width, height, frame_no, frame_count): 24 | setup(ctx, width, height, background=Color(1)) 25 | 26 | # Creates a table with default settings 27 | table = Table(ctx, (50, 50)).of_rows_cols([50]*4, [100]*3) 28 | table.draw() 29 | Text(ctx).of("A", table.get(0, 0)).size(20).align(CENTER, MIDDLE).fill(Color(0)) 30 | Text(ctx).of("B", table.get(2, 1)).size(20).align(CENTER, MIDDLE).fill(Color(0)) 31 | Text(ctx).of("C", table.get(3, 2)).size(20).align(CENTER, MIDDLE).fill(Color(0)) 32 | 33 | # Creates a table with a different style 34 | table = Table(ctx, (400, 100)).of_rows_cols([80]*3, [30]*4).background(Color("palegreen")).linestyle(Color("dodgerblue"), 4) 35 | table.draw() 36 | Text(ctx).of("a", table.get(1, 2)).size(20).align(CENTER, MIDDLE).fill(Color(0)) 37 | Text(ctx).of("b", table.get(2, 3)).size(20).align(CENTER, MIDDLE).fill(Color(0)) 38 | Text(ctx).of("c", table.get(0, 1)).size(20).align(CENTER, MIDDLE).fill(Color(0)) 39 | 40 | 41 | def creator(file): 42 | make_image(file, draw, 600, 600) 43 | 44 | self.assertTrue(run_image_test('test_table.png', creator)) 45 | 46 | -------------------------------------------------------------------------------- /imagetests/test_tween_module.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from generativepy.drawing import setup, make_image 3 | from image_test_helper import run_image_test 4 | from generativepy.color import Color 5 | from generativepy.tween import Tween 6 | import generativepy.tween 7 | from generativepy.geometry import Polygon, Square 8 | 9 | """ 10 | Test the tween module. Most of the module functionality is tested bu unit tests, but 11 | we use image tests to check the easing functions by plotting them. 12 | """ 13 | 14 | class TestTweenImages(unittest.TestCase): 15 | 16 | def test_tween_easing(self): 17 | ''' 18 | Plot the easing functions as graphs on an image 19 | ''' 20 | 21 | def plot_easing_function(ctx, x, y, fn): 22 | tw = Tween(0).ease(-100, 100, fn) 23 | poly = [(i, tw[i]) for i in range(100)] 24 | ctx.save() 25 | ctx.translate(x, y) 26 | Square(ctx).of_corner_size((0, -100), 100).stroke(Color(0.5), 2) 27 | Polygon(ctx).of_points(poly).open().stroke(Color('red'), 2) 28 | ctx.restore() 29 | 30 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 31 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 32 | plot_easing_function(ctx, 10, 120, generativepy.tween.ease_linear()) 33 | plot_easing_function(ctx, 10, 230, generativepy.tween.ease_in_harm()) 34 | plot_easing_function(ctx, 120, 230, generativepy.tween.ease_out_harm()) 35 | plot_easing_function(ctx, 230, 230, generativepy.tween.ease_in_out_harm()) 36 | plot_easing_function(ctx, 340, 230, generativepy.tween.ease_in_back()) 37 | plot_easing_function(ctx, 450, 230, generativepy.tween.ease_out_back()) 38 | plot_easing_function(ctx, 560, 230, generativepy.tween.ease_in_out_back()) 39 | plot_easing_function(ctx, 10, 450, generativepy.tween.ease_in_elastic()) 40 | plot_easing_function(ctx, 120, 450, generativepy.tween.ease_out_elastic()) 41 | plot_easing_function(ctx, 230, 450, generativepy.tween.ease_in_out_elastic()) 42 | plot_easing_function(ctx, 340, 450, generativepy.tween.ease_in_bounce()) 43 | plot_easing_function(ctx, 450, 450, generativepy.tween.ease_out_bounce()) 44 | plot_easing_function(ctx, 560, 450, generativepy.tween.ease_in_out_bounce()) 45 | 46 | def creator(file): 47 | make_image(file, draw, 700, 600, channels=3) 48 | 49 | 50 | self.assertTrue(run_image_test('test_tween_easing.png', creator)) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy~=1.26.4 2 | pycairo~=1.27.0 3 | pillow~=10.4.0 4 | setuptools~=75.6.0 5 | vapory~=0.1.2 6 | moderngl~=5.12.0 7 | imageio~=2.33.1 8 | moviepy~=2.0.0 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [options] 2 | install_requires = 3 | numpy >= 1.21.2 4 | pycairo >= 1.20.1 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup(name='generativepy', 7 | version='24.11', 8 | url='https://github.com/martinmcbride/generativepy', 9 | license='MIT', 10 | author='Martin McBride', 11 | author_email='mcbride.martin@gmail.com', 12 | description='Generative art library', 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | packages=find_packages(exclude=['examples', 'test', 'imagetests', 'tutorial']), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | setup_requires=[]) 22 | -------------------------------------------------------------------------------- /test/all_unit_tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | loader = unittest.TestLoader() 3 | start_dir = './' 4 | suite = loader.discover(start_dir) 5 | 6 | runner = unittest.TextTestRunner() 7 | runner.run(suite) -------------------------------------------------------------------------------- /test/formula-empty-temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/test/formula-empty-temp.png -------------------------------------------------------------------------------- /test/formula-invalid-temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/test/formula-invalid-temp.png -------------------------------------------------------------------------------- /test/formula-valid-temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/test/formula-valid-temp.png -------------------------------------------------------------------------------- /test/readme.md: -------------------------------------------------------------------------------- 1 | # Unit tests 2 | 3 | This area contains unit tests for generativepy. 4 | 5 | These tests use the standard Python unittest module. 6 | 7 | Run all_unit_tests.py to run all the tests. 8 | 9 | Many areas of the code cannot easily be unit tested because they create image output that needs to be checked. There are separate tests for this in the imagetests area. 10 | 11 | -------------------------------------------------------------------------------- /test/test_formulas.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from generativepy.color import Color 4 | 5 | from generativepy.formulas import rasterise_formula 6 | 7 | from generativepy.geometry import Text 8 | 9 | 10 | class TestFormulas(unittest.TestCase): 11 | 12 | # Test with a valid formula 13 | def test_formula_valid(self): 14 | image, size = rasterise_formula("formula-valid-temp", r"e^x", Color("crimson"), dpi=400) 15 | self.assertEqual(size, (46, 40)) 16 | 17 | # Test with an invalid formula 18 | def test_formula_invalid(self): 19 | image, size = rasterise_formula("formula-invalid-temp", r"}e^x", Color("crimson"), dpi=400) 20 | self.assertEqual(size, (46, 40)) 21 | 22 | 23 | # Test with an empty formula 24 | def test_formula_null(self): 25 | image, size = rasterise_formula("formula-empty-temp", r"", Color("crimson"), dpi=400) 26 | self.assertEqual(size, (1, 1)) 27 | 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /test/test_graph.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import cairo 3 | from generativepy.graph import Axes 4 | from generativepy.math import Vector as V 5 | 6 | 7 | class TestGraph(unittest.TestCase): 8 | 9 | # Test text extent 10 | def test_axes_point(self): 11 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 12 | ctx = cairo.Context(surface) 13 | axes = Axes(ctx, (20, 30), 400, 500).of_start((1, 2)).of_extent((4, 10)) 14 | t = axes.transform_from_graph([2, 4]) 15 | self.assertEqual(t, V(120, 430)) 16 | 17 | def test_axes_points1(self): 18 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 19 | ctx = cairo.Context(surface) 20 | axes = Axes(ctx, (20, 30), 400, 500).of_start((1, 2)).of_extent((4, 10)) 21 | t = axes.transform_from_graph(([2, 4],)) 22 | self.assertEqual(len(t), 1) 23 | self.assertEqual(t[0], V(120, 430)) 24 | 25 | def test_axes_points2(self): 26 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 27 | ctx = cairo.Context(surface) 28 | axes = Axes(ctx, (20, 30), 400, 500).of_start((1, 2)).of_extent((4, 10)) 29 | t = axes.transform_from_graph(([2, 4], [4, 2])) 30 | self.assertEqual(len(t), 2) 31 | self.assertEqual(t[0], V(120, 430)) 32 | self.assertEqual(t[1], V(320, 530)) 33 | 34 | def test_axes_points0(self): 35 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 36 | ctx = cairo.Context(surface) 37 | axes = Axes(ctx, (20, 30), 400, 500).of_start((1, 2)).of_extent((4, 10)) 38 | t = axes.transform_from_graph(()) 39 | self.assertEqual(len(t), 0) 40 | 41 | def test_axes_error(self): 42 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 43 | ctx = cairo.Context(surface) 44 | axes = Axes(ctx, (20, 30), 400, 500).of_start((1, 2)).of_extent((4, 10)) 45 | with self.assertRaises(TypeError): 46 | axes.transform_from_graph(1) 47 | with self.assertRaises(ValueError): 48 | axes.transform_from_graph([1]) 49 | with self.assertRaises(ValueError): 50 | axes.transform_from_graph([1, 2, 3]) 51 | with self.assertRaises(ValueError): 52 | axes.transform_from_graph([[1]]) 53 | with self.assertRaises(ValueError): 54 | axes.transform_from_graph([[1, 2, 3]]) 55 | 56 | 57 | 58 | if __name__ == '__main__': 59 | unittest.main() 60 | -------------------------------------------------------------------------------- /test/test_regular_polygon.py: -------------------------------------------------------------------------------- 1 | import math 2 | import unittest 3 | import cairo 4 | from generativepy.geometry import RegularPolygon 5 | 6 | 7 | class TestRegularPolygon(unittest.TestCase): 8 | 9 | def test_angle_3(self): 10 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 11 | ctx = cairo.Context(surface) 12 | p = RegularPolygon(ctx).of_centre_sides_radius((0, 0), 3, 100) 13 | self.assertAlmostEqual(math.degrees(p.interior_angle), 60) 14 | self.assertAlmostEqual(math.degrees(p.exterior_angle), 120) 15 | 16 | def test_angle_4(self): 17 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 18 | ctx = cairo.Context(surface) 19 | p = RegularPolygon(ctx).of_centre_sides_radius((0, 0), 4, 100) 20 | self.assertAlmostEqual(math.degrees(p.interior_angle), 90) 21 | self.assertAlmostEqual(math.degrees(p.exterior_angle), 90) 22 | 23 | def test_angle_5(self): 24 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 25 | ctx = cairo.Context(surface) 26 | p = RegularPolygon(ctx).of_centre_sides_radius((0, 0), 5, 100) 27 | self.assertAlmostEqual(math.degrees(p.interior_angle), 108) 28 | self.assertAlmostEqual(math.degrees(p.exterior_angle), 72) 29 | 30 | def test_angle_6(self): 31 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 32 | ctx = cairo.Context(surface) 33 | p = RegularPolygon(ctx).of_centre_sides_radius((0, 0), 6, 100) 34 | self.assertAlmostEqual(math.degrees(p.interior_angle), 120) 35 | self.assertAlmostEqual(math.degrees(p.exterior_angle), 60) 36 | 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /test/test_text.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import cairo 3 | from generativepy.geometry import Text 4 | 5 | 6 | class TestText(unittest.TestCase): 7 | 8 | # Test text extent 9 | def test_text_extent(self): 10 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 11 | ctx = cairo.Context(surface) 12 | x_bearing, y_bearing, width, height, x_advance, y_advance = Text(ctx).of('abc', (0, 0)).get_metrics() 13 | self.assertAlmostEqual(x_bearing, 0) 14 | self.assertAlmostEqual(y_bearing, -7) 15 | self.assertAlmostEqual(width, 17) 16 | self.assertAlmostEqual(height, 7) 17 | self.assertAlmostEqual(x_advance, 17) 18 | self.assertAlmostEqual(y_advance, 0) 19 | 20 | # Test text size 21 | def test_text_size(self): 22 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 200) 23 | ctx = cairo.Context(surface) 24 | width, height = Text(ctx).of('abc', (0, 0)).get_size() 25 | self.assertAlmostEqual(width, 17) 26 | self.assertAlmostEqual(height, 7) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /test/test_utils.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from generativepy.utils import correct_pycairo_byte_order, temp_file 3 | import numpy as np 4 | 5 | 6 | ## These tests assume a littleendian machine 7 | 8 | class TestUtils(unittest.TestCase): 9 | 10 | def test_tempfile_1_name(self): 11 | fn = temp_file('testname.png') 12 | self.assertEqual('/tmp/testname.png', fn) 13 | 14 | def test_tempfile_2_name(self): 15 | fn = temp_file('abc', 'testname.png') 16 | self.assertEqual('/tmp/abc/testname.png', fn) 17 | 18 | def test_3_channel(self): 19 | indata = [[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], 20 | [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], 21 | [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], 22 | [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]] 23 | 24 | outdata = [[[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]], 25 | [[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]], 26 | [[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]], 27 | [[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]]] 28 | array = np.array(indata) 29 | expected = np.array(outdata) 30 | result = correct_pycairo_byte_order(array, 3) 31 | self.assertTrue(np.array_equal(expected, result)) 32 | 33 | 34 | def test_4_channel(self): 35 | indata = [[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], 36 | [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], 37 | [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]], 38 | [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]] 39 | 40 | outdata = [[[3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4]], 41 | [[3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4]], 42 | [[3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4]], 43 | [[3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4], [3, 2, 1, 4]]] 44 | array = np.array(indata) 45 | expected = np.array(outdata) 46 | result = correct_pycairo_byte_order(array, 4) 47 | self.assertTrue(np.array_equal(expected, result)) 48 | -------------------------------------------------------------------------------- /tutorial/colour/colour-alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-alpha.png -------------------------------------------------------------------------------- /tutorial/colour/colour-css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-css.png -------------------------------------------------------------------------------- /tutorial/colour/colour-delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-delta.png -------------------------------------------------------------------------------- /tutorial/colour/colour-grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-grey.png -------------------------------------------------------------------------------- /tutorial/colour/colour-hsl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-hsl.png -------------------------------------------------------------------------------- /tutorial/colour/colour-lerp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-lerp.png -------------------------------------------------------------------------------- /tutorial/colour/colour-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-map.png -------------------------------------------------------------------------------- /tutorial/colour/colour-rgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-rgb.png -------------------------------------------------------------------------------- /tutorial/colour/colour-scheme-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-scheme-user.png -------------------------------------------------------------------------------- /tutorial/colour/colour-scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-scheme.png -------------------------------------------------------------------------------- /tutorial/colour/colour-with.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/colour/colour-with.png -------------------------------------------------------------------------------- /tutorial/colour/colour_alpha.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | from generativepy.drawing import make_image, setup 12 | from generativepy.color import Color 13 | from generativepy.geometry import Rectangle 14 | 15 | def draw_alpha(ctx, pixel_width, pixel_height, frame_no, frame_count): 16 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 17 | 18 | Rectangle(ctx).of_corner_size((10, 50), 450, 20).fill(Color(0)) 19 | 20 | pos = [20, 10] 21 | w = 100 22 | space =110 23 | h = 100 24 | 25 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(1.0, 0.5, 0.0, 0.5)) 26 | pos[0] += space 27 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.6, 0.5)) 28 | pos[0] += space 29 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("tomato", 0.5)) 30 | pos[0] += space 31 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color.of_hsla(0.4, 0.5, 0.5, 0.5)) 32 | pos[0] += space 33 | 34 | make_image("colour-alpha.png", draw_alpha, 470, 120) -------------------------------------------------------------------------------- /tutorial/colour/colour_css.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | def draw_css(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_height, background=Color('cornflowerblue')) 13 | 14 | pos = [10, 10] 15 | w = 100 16 | h = 100 17 | 18 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("salmon")) 19 | pos[0] += w 20 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("gold")) 21 | pos[0] += w 22 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("seagreen")) 23 | pos[0] += w 24 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("cadetblue")) 25 | pos[0] += w 26 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("slateblue")) 27 | pos[0] += w 28 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("darkorchid")) 29 | pos[0] += w 30 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color("hotpink")) 31 | pos[0] += w 32 | 33 | 34 | make_image("colour-css.png", draw_css, 720, 120) 35 | -------------------------------------------------------------------------------- /tutorial/colour/colour_delta.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | from generativepy.drawing import make_image, setup 12 | from generativepy.color import Color 13 | from generativepy.geometry import Rectangle 14 | 15 | def draw_delta(ctx, pixel_width, pixel_height, frame_no, frame_count): 16 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 17 | 18 | col = Color("cadetblue") 19 | 20 | pos = [10, 10] 21 | w = 100 22 | h = 100 23 | 24 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_g_factor(0.8)) 25 | pos[0] += w 26 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col) 27 | pos[0] += w 28 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_g_factor(1.2)) 29 | 30 | pos = [10, 120] 31 | 32 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_l_factor(0.6)) 33 | pos[0] += w 34 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col) 35 | pos[0] += w 36 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_l_factor(1.4)) 37 | 38 | pos = [10, 230] 39 | 40 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_s_factor(0.6)) 41 | pos[0] += w 42 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col) 43 | pos[0] += w 44 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_s_factor(1.4)) 45 | 46 | make_image("colour-delta.png", draw_delta, 320, 340) -------------------------------------------------------------------------------- /tutorial/colour/colour_grey.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | def draw_grey(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_height, background=Color('cornflowerblue')) 13 | 14 | pos = [10, 10] 15 | w = 100 16 | h = 100 17 | 18 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0)) 19 | pos[0] += w 20 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.25)) 21 | pos[0] += w 22 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.5)) 23 | pos[0] += w 24 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.75)) 25 | pos[0] += w 26 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(1)) 27 | pos[0] += w 28 | 29 | 30 | make_image("colour-grey.png", draw_grey, 520, 120) -------------------------------------------------------------------------------- /tutorial/colour/colour_lerp.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | def draw_lerp(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_height, background=Color('cornflowerblue')) 13 | 14 | color1 = Color('red') 15 | color2 = Color('blue') 16 | 17 | pos = [10, 10] 18 | w = 100 19 | h = 100 20 | 21 | Rectangle(ctx).of_corner_size(pos, w, h).fill(color1.lerp(color2, 0)) 22 | pos[0] += w 23 | Rectangle(ctx).of_corner_size(pos, w, h).fill(color1.lerp(color2, 0.25)) 24 | pos[0] += w 25 | Rectangle(ctx).of_corner_size(pos, w, h).fill(color1.lerp(color2, 0.5)) 26 | pos[0] += w 27 | Rectangle(ctx).of_corner_size(pos, w, h).fill(color1.lerp(color2, 0.75)) 28 | pos[0] += w 29 | Rectangle(ctx).of_corner_size(pos, w, h).fill(color1.lerp(color2, 1)) 30 | pos[0] += w 31 | 32 | 33 | make_image("colour-lerp.png", draw_lerp, 520, 120) -------------------------------------------------------------------------------- /tutorial/colour/colour_map.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color, make_colormap 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | def draw_map(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_height, background=Color(0.5)) 13 | 14 | w = 2 15 | h = 100 16 | 17 | pos = [10, 10] 18 | colormap = make_colormap(256, [Color('red'), Color('blue')]) 19 | for i in range(256): 20 | Rectangle(ctx).of_corner_size(pos, w, h).fill(colormap[i]) 21 | pos[0] += 2 22 | 23 | pos = [10, 120] 24 | colormap = make_colormap(256, [Color('red'), Color('blue'), Color('yellow')]) 25 | for i in range(256): 26 | Rectangle(ctx).of_corner_size(pos, w, h).fill(colormap[i]) 27 | pos[0] += 2 28 | 29 | pos = [10, 230] 30 | colormap = make_colormap(256, [Color('red'), Color('blue'), Color('yellow')], [3, 1]) 31 | for i in range(256): 32 | Rectangle(ctx).of_corner_size(pos, w, h).fill(colormap[i]) 33 | pos[0] += 2 34 | 35 | pos = [10, 340] 36 | colormap = make_colormap(256, [Color('red'), Color('blue'), Color('yellow'), Color('green')], [3, 0, 1]) 37 | for i in range(256): 38 | Rectangle(ctx).of_corner_size(pos, w, h).fill(colormap[i]) 39 | pos[0] += 2 40 | 41 | 42 | make_image("colour-map.png", draw_map, 532, 450) -------------------------------------------------------------------------------- /tutorial/colour/colour_properties.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.color import Color 7 | 8 | # Create a colour and print its properties 9 | 10 | color = Color(1.0, 0.5, 0.0) 11 | 12 | print(color.r) 13 | print(color.g) 14 | print(color.b) 15 | print(color.a) 16 | 17 | print(color.h) 18 | print(color.s) 19 | print(color.l) 20 | 21 | print(color.as_rgbstr()) 22 | print(color.as_rgb_bytes()) 23 | print(color.as_rgba_bytes()) 24 | -------------------------------------------------------------------------------- /tutorial/colour/colour_rgb.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | 11 | def draw_rgb(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_height, background=Color("cornflowerblue")) 13 | 14 | pos = [10, 10] 15 | w = 100 16 | h = 100 17 | 18 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0, 0, 0)) 19 | pos[0] += w 20 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.5, 0, 0)) 21 | pos[0] += w 22 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(1, 0, 0)) 23 | pos[0] += w 24 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.5, 1, 0)) 25 | pos[0] += w 26 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.5, 0, 1)) 27 | pos[0] += w 28 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0, 1, .5)) 29 | pos[0] += w 30 | Rectangle(ctx).of_corner_size(pos, w, h).fill(Color(0.5, 0.5, 0.5)) 31 | pos[0] += w 32 | 33 | 34 | make_image("colour-rgb.png", draw_rgb, 720, 120) 35 | -------------------------------------------------------------------------------- /tutorial/colour/colour_schemes.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-06-04 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color, ArtisticColorScheme 8 | from generativepy.geometry import Square 9 | 10 | # Select a colour scheme 11 | cs = ArtisticColorScheme() 12 | 13 | def draw_color_scheme(ctx, pixel_width, pixel_height, frame_no, frame_count): 14 | setup(ctx, pixel_width, pixel_height, background=Color("white")) 15 | 16 | # Fill a square with red 17 | Square(ctx).of_corner_size((50, 50), 200).fill(cs.RED) 18 | 19 | # Fill a square with a lighter version of RED, stroke with a darker version 20 | Square(ctx).of_corner_size((300, 50), 200).fill(cs.RED.light1).stroke(cs.RED.dark1, 10) 21 | 22 | 23 | make_image("colour-scheme.png", draw_color_scheme, 600, 300) 24 | -------------------------------------------------------------------------------- /tutorial/colour/colour_schemes_user.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-06-04 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color, ArtisticColorScheme 8 | from generativepy.geometry import Square 9 | 10 | # Create a user defined colour scheme and use it 11 | 12 | class UserColorScheme: 13 | def __init__(self): 14 | self._RED = Color(0.5, 0, 0.25) 15 | self._BLUE = Color(0, 0, 0.5) 16 | self._GREEN = Color(0, 0.5, 0.25) 17 | 18 | @property 19 | def RED(self): 20 | return self._RED 21 | 22 | @property 23 | def BLUE(self): 24 | return self._BLUE 25 | 26 | @property 27 | def GREEN(self): 28 | return self._GREEN 29 | 30 | 31 | cs = UserColorScheme() 32 | 33 | def draw_color_scheme_user(ctx, pixel_width, pixel_height, frame_no, frame_count): 34 | setup(ctx, pixel_width, pixel_height, background=Color("white")) 35 | 36 | # Fill a square with red 37 | Square(ctx).of_corner_size((50, 50), 200).fill(cs.RED) 38 | 39 | # Fill a square with a lighter version of RED, stroke with a darker version 40 | Square(ctx).of_corner_size((300, 50), 200).fill(cs.RED.light1).stroke(cs.RED.dark1, 10) 41 | 42 | 43 | make_image("colour-scheme-user.png", draw_color_scheme_user, 600, 300) 44 | -------------------------------------------------------------------------------- /tutorial/colour/colour_with.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-04-19 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle 9 | 10 | def draw_with(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 12 | 13 | col = Color("cadetblue") 14 | 15 | pos = [10, 10] 16 | w = 100 17 | h = 100 18 | 19 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_g(0.1)) 20 | pos[0] += w 21 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col) 22 | pos[0] += w 23 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_g(0.9)) 24 | 25 | pos = [10, 120] 26 | 27 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_l(0.2)) 28 | pos[0] += w 29 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col) 30 | pos[0] += w 31 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_l(0.9)) 32 | 33 | pos = [10, 230] 34 | 35 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_s(0.1)) 36 | pos[0] += w 37 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col) 38 | pos[0] += w 39 | Rectangle(ctx).of_corner_size(pos, w, h).fill(col.with_s(0.9)) 40 | 41 | make_image("colour-with.png", draw_with, 320, 340) -------------------------------------------------------------------------------- /tutorial/getting-started/rectangle-svg.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create a simple rectangle image 7 | 8 | from generativepy.drawing import setup, make_svg 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 15 | 16 | color = Color(1, 0.5, 0) 17 | 18 | Rectangle(ctx).of_corner_size((100, 150), 250, 200).fill(color) 19 | 20 | make_svg("rectangle-svg.svg", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/getting-started/rectangle-svg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tutorial/getting-started/rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/getting-started/rectangle.png -------------------------------------------------------------------------------- /tutorial/getting-started/rectangle.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create a simple rectangle image 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 15 | 16 | color = Color(1, 0.5, 0) 17 | 18 | Rectangle(ctx).of_corner_size((100, 150), 250, 200).fill(color) 19 | 20 | make_image("rectangle.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/getting-started/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/getting-started/setup.png -------------------------------------------------------------------------------- /tutorial/getting-started/setup.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Example of setup function 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, width=5, background=Color(0.4)) 15 | 16 | color = Color(1, 0.5, 0) 17 | 18 | Rectangle(ctx).of_corner_size((1, 1.5), 2.5, 2).fill(color) 19 | 20 | make_image("setup.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/patterns/circles-linear-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/patterns/circles-linear-gradient.png -------------------------------------------------------------------------------- /tutorial/patterns/circles-linear-gradient.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create a simple linear gradient with several circle shapes 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Circle, LinearGradient 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 15 | 16 | gradient = LinearGradient().of_points((150, 150), (350, 250)).with_start_end(Color('yellow'), Color('red')).build() 17 | Circle(ctx).of_center_radius((100, 100), 75).fill(gradient) 18 | Circle(ctx).of_center_radius((270, 100), 75).fill(gradient) 19 | Circle(ctx).of_center_radius((150, 300), 75).fill(gradient) 20 | Circle(ctx).of_center_radius((400, 300), 75).fill(gradient) 21 | 22 | make_image("circles-linear-gradient.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/patterns/fullpage-linear-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/patterns/fullpage-linear-gradient.png -------------------------------------------------------------------------------- /tutorial/patterns/fullpage-linear-gradient.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create a simple linear gradient across the whole page 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle, LinearGradient, Circle, Line 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 15 | 16 | gradient = LinearGradient().of_points((150, 150), (350, 250)).with_start_end(Color('yellow'), Color('red')).build() 17 | Rectangle(ctx).of_corner_size((0, 0), 500, 400).fill(gradient) 18 | 19 | Line(ctx).of_start_end((150, 150), (350, 250)).stroke(Color(0), 2, [5, 5]) 20 | Line(ctx).of_start_end((150, 150), (250, -50)).as_line().stroke(Color(0), 2, [5, 5]) 21 | Line(ctx).of_start_end((350, 250), (450, 50)).as_line().stroke(Color(0), 2, [5, 5]) 22 | Circle(ctx).of_center_radius((150, 150), 5).fill(Color(0)) 23 | Circle(ctx).of_center_radius((350, 250), 5).fill(Color(0)) 24 | 25 | make_image("fullpage-linear-gradient.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/patterns/multistop-linear-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/patterns/multistop-linear-gradient.png -------------------------------------------------------------------------------- /tutorial/patterns/multistop-linear-gradient.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create a simple linear gradient 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle, LinearGradient 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 15 | 16 | gradient = LinearGradient().of_points((150, 150), (350, 250))\ 17 | .with_stops([(0, Color('yellow')), 18 | (0.5, Color('blue')), 19 | (1, Color('red'))])\ 20 | .build() 21 | Rectangle(ctx).of_corner_size((150, 150), 200, 100).fill(gradient) 22 | 23 | make_image("multistop-linear-gradient.png", draw, 500, 400) 24 | 25 | 26 | def draw2(ctx, pixel_width, pixel_height, frame_no, frame_count): 27 | 28 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 29 | 30 | gradient = LinearGradient().of_points((150, 150), (350, 250))\ 31 | .with_stops([(0, Color('yellow')), 32 | (0.3, Color('blue')), 33 | (0.7, Color('blue')), 34 | (1, Color('red'))])\ 35 | .build() 36 | Rectangle(ctx).of_corner_size((150, 150), 200, 100).fill(gradient) 37 | 38 | make_image("multistop-linear-gradient2.png", draw2, 500, 400) 39 | 40 | 41 | def draw3(ctx, pixel_width, pixel_height, frame_no, frame_count): 42 | 43 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 44 | 45 | gradient = LinearGradient().of_points((150, 150), (350, 250))\ 46 | .with_stops([(0, Color('yellow')), 47 | (0.3, Color('blue')), 48 | (0.3, Color('green')), 49 | (1, Color('red'))])\ 50 | .build() 51 | Rectangle(ctx).of_corner_size((150, 150), 200, 100).fill(gradient) 52 | 53 | make_image("multistop-linear-gradient3.png", draw3, 500, 400) -------------------------------------------------------------------------------- /tutorial/patterns/multistop-linear-gradient2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/patterns/multistop-linear-gradient2.png -------------------------------------------------------------------------------- /tutorial/patterns/multistop-linear-gradient3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/patterns/multistop-linear-gradient3.png -------------------------------------------------------------------------------- /tutorial/patterns/simple-linear-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/patterns/simple-linear-gradient.png -------------------------------------------------------------------------------- /tutorial/patterns/simple-linear-gradient.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-07 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create a simple linear gradient 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle, LinearGradient 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.4)) 15 | 16 | gradient = LinearGradient().of_points((150, 150), (350, 250)).with_start_end(Color('yellow'), Color('red')).build() 17 | Rectangle(ctx).of_corner_size((150, 150), 200, 100).fill(gradient) 18 | 19 | make_image("simple-linear-gradient.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/readme.md: -------------------------------------------------------------------------------- 1 | # Tutorial images 2 | 3 | Example files from the [generativepy tutorials](https://pythoninformer.com/generative-art/generativepy-tutorial/). 4 | 5 | The files are in subfolders corresponding to the sections of the tutorial. 6 | -------------------------------------------------------------------------------- /tutorial/shapes/angle-markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/angle-markers.png -------------------------------------------------------------------------------- /tutorial/shapes/bezier-tutorial-joined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/bezier-tutorial-joined.png -------------------------------------------------------------------------------- /tutorial/shapes/bezier-tutorial-points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/bezier-tutorial-points.png -------------------------------------------------------------------------------- /tutorial/shapes/bezier-tutorial-polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/bezier-tutorial-polygon.png -------------------------------------------------------------------------------- /tutorial/shapes/bezier-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/bezier-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/cat.png -------------------------------------------------------------------------------- /tutorial/shapes/circles-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/circles-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/circles.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-29 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create some circles, arcs etc 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Circle 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 15 | 16 | blue = Color('blue') 17 | grey = Color(0.4) 18 | thickness = 2 19 | 20 | Circle(ctx).of_center_radius((100, 100), 75).stroke(blue, thickness) 21 | 22 | Circle(ctx).of_center_radius((300, 100), 75).stroke(grey, thickness, dash=[5]) 23 | Circle(ctx).of_center_radius((300, 100), 75).as_arc(0, 1).stroke(blue, thickness) 24 | 25 | Circle(ctx).of_center_radius((100, 300), 75).stroke(grey, thickness, dash=[5]) 26 | Circle(ctx).of_center_radius((100, 300), 75).as_sector(1, 3).stroke(blue, thickness) 27 | 28 | Circle(ctx).of_center_radius((300, 300), 75).stroke(grey, thickness, dash=[5]) 29 | Circle(ctx).of_center_radius((300, 300), 75).as_segment(-1, 1).stroke(blue, thickness) 30 | 31 | 32 | make_image("circles-tutorial.png", draw, 400, 400) -------------------------------------------------------------------------------- /tutorial/shapes/complex-polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/complex-polygon.png -------------------------------------------------------------------------------- /tutorial/shapes/complex-polygon.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-05 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup, EVEN_ODD 7 | from generativepy.color import Color 8 | from generativepy.geometry import Polygon 9 | import math 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 13 | 14 | black = Color(0) 15 | red = Color('red') 16 | 17 | Polygon(ctx).of_points([(150, 50), 18 | (100, 250), 19 | (250, 150), 20 | (50, 150), 21 | (200, 250), 22 | ]).fill(red).stroke(black, 5) 23 | 24 | 25 | Polygon(ctx).of_points([(450, 50), 26 | (400, 250), 27 | (550, 150), 28 | (350, 150), 29 | (500, 250), 30 | ]).fill(red, fill_rule=EVEN_ODD).stroke(black, 5) 31 | 32 | make_image("complex-polygon.png", draw, 700, 300) -------------------------------------------------------------------------------- /tutorial/shapes/complex-subpaths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/complex-subpaths.png -------------------------------------------------------------------------------- /tutorial/shapes/complex-subpaths.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-05 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup, EVEN_ODD 7 | from generativepy.color import Color 8 | from generativepy.geometry import Line, Rectangle 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 12 | 13 | black = Color(0) 14 | 15 | Rectangle(ctx).of_corner_size((50, 50), 350, 250).add() 16 | Rectangle(ctx).of_corner_size((100, 100), 250, 150).as_sub_path()\ 17 | .fill(Color('red'), fill_rule=EVEN_ODD)\ 18 | .stroke(Color('blue'), 5) 19 | 20 | make_image("complex-subpaths.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/shapes/composite-lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/composite-lines.png -------------------------------------------------------------------------------- /tutorial/shapes/composite-lines.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-05 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Line 9 | 10 | def draw2(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 12 | 13 | black = Color(0) 14 | 15 | Line(ctx).of_start_end((100, 50), (200, 50)).stroke(black, 20) 16 | Line(ctx).of_start_end((200, 50), (100, 200)).stroke(black, 20) 17 | Line(ctx).of_start_end((100, 200), (50, 200)).stroke(black, 20) 18 | Line(ctx).of_start_end((50, 200), (100, 50)).stroke(black, 20) 19 | 20 | Line(ctx).of_start_end((300, 50), (400, 50)).add() 21 | Line(ctx).of_end((300, 200)).extend_path().add() 22 | Line(ctx).of_end((250, 200)).extend_path().stroke(black, 20) 23 | 24 | Line(ctx).of_start_end((500, 50), (600, 50)).add() 25 | Line(ctx).of_end((500, 200)).extend_path().add() 26 | Line(ctx).of_end((450, 200)).extend_path(close=True).stroke(black, 20) 27 | 28 | make_image("composite-lines.png", draw2, 700, 300) -------------------------------------------------------------------------------- /tutorial/shapes/composite-roundrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/composite-roundrect.png -------------------------------------------------------------------------------- /tutorial/shapes/composite-roundrect.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-05 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Circle 9 | import math 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 13 | 14 | black = Color(0) 15 | 16 | Circle(ctx).of_center_radius((50, 50), 10).as_arc(math.pi, math.pi*3/2).add() 17 | Circle(ctx).of_center_radius((250, 50), 10).as_arc(math.pi*3/2, 0).extend_path().add() 18 | Circle(ctx).of_center_radius((250, 150), 10).as_arc(0, math.pi/2).extend_path().add() 19 | Circle(ctx).of_center_radius((50, 150), 10).as_arc(math.pi/2, math.pi).extend_path(close=True).stroke(black, 5) 20 | 21 | 22 | Circle(ctx).of_center_radius((350, 50), 30).as_arc(math.pi/2, math.pi*3/2).add() 23 | Circle(ctx).of_center_radius((550, 50), 30).as_arc(math.pi*3/2, math.pi/2).extend_path(close=True).stroke(black, 5) 24 | 25 | make_image("complex-roundrect.png", draw, 700, 300) -------------------------------------------------------------------------------- /tutorial/shapes/ellipses-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/ellipses-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/ellipses.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-29 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create some ellipses, arcs etc 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Ellipse 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 15 | 16 | blue = Color('blue') 17 | grey = Color(0.4) 18 | thickness = 2 19 | 20 | Ellipse(ctx).of_center_radius((100, 100), 75, 50).stroke(blue, thickness) 21 | 22 | Ellipse(ctx).of_center_radius((300, 100), 75, 50).stroke(grey, thickness, dash=[5]) 23 | Ellipse(ctx).of_center_radius((300, 100), 75, 50).as_arc(0, 1).stroke(blue, thickness) 24 | 25 | Ellipse(ctx).of_center_radius((100, 300), 50, 75).stroke(grey, thickness, dash=[5]) 26 | Ellipse(ctx).of_center_radius((100, 300), 50, 75).as_sector(1, 3).stroke(blue, thickness) 27 | 28 | Ellipse(ctx).of_center_radius((300, 300), 50, 75).stroke(grey, thickness, dash=[5]) 29 | Ellipse(ctx).of_center_radius((300, 300), 50, 75).as_segment(-1, 1).stroke(blue, thickness) 30 | 31 | 32 | make_image("ellipses-tutorial.png", draw, 400, 400) -------------------------------------------------------------------------------- /tutorial/shapes/fill-stroke-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/fill-stroke-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/fill-style-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/fill-style-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/image.png -------------------------------------------------------------------------------- /tutorial/shapes/image.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-07 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Image 9 | 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 13 | 14 | Image(ctx).of_file_position('cat.png', (50, 50)).paint() 15 | Image(ctx).of_file_position('cat.png', (300, 50)).scale(0.5).paint() 16 | Image(ctx).of_file_position('cat.png', (50, 300)).scale(1.5).paint() 17 | 18 | 19 | make_image("image.png", draw, 500, 600) -------------------------------------------------------------------------------- /tutorial/shapes/join-style-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/join-style-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/parallel-markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/parallel-markers.png -------------------------------------------------------------------------------- /tutorial/shapes/path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/path.png -------------------------------------------------------------------------------- /tutorial/shapes/path.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup 2 | from generativepy.color import Color 3 | from generativepy.geometry import Polygon, Text, Path, Transform 4 | 5 | ''' 6 | Demonstrate paths in the geometry module. 7 | ''' 8 | 9 | def draw(ctx, width, height, frame_no, frame_count): 10 | setup(ctx, width, height, width=5, background=Color(0.8)) 11 | 12 | # Get a polygon path object 13 | path1 = Polygon(ctx).of_points([(0, 0), (1, 1), (0.5, 2), (0.5, 1)])\ 14 | .path() 15 | 16 | # Get a text path object 17 | path2 = Text(ctx).of("Path text", (0, 0)).font("Times").size(0.2)\ 18 | .align_left().align_top().path() 19 | 20 | # Apply the polygon in various places 21 | with Transform(ctx).translate(0.5, 1): 22 | Path(ctx).of(path1).stroke(Color('darkgreen'), 0.1) 23 | 24 | with Transform(ctx).translate(1, 2.5): 25 | Path(ctx).of(path1).fill(Color('blue')) 26 | 27 | with Transform(ctx).translate(2.5, 0.5).scale(2, 2): 28 | Path(ctx).of(path1).fill(Color('orange')).stroke(Color('black'), 0.05) 29 | 30 | # Apply the text in various places 31 | with Transform(ctx).translate(0, 0): 32 | Path(ctx).of(path2).fill(Color('black')) 33 | 34 | with Transform(ctx).translate(2, 3): 35 | Path(ctx).of(path2).stroke(Color('red'), 0.01) 36 | 37 | with Transform(ctx).translate(2, 4).scale(2, 2): 38 | Path(ctx).of(path2).fill(Color('yellow')).stroke(Color('black'), 0.01) 39 | 40 | 41 | make_image("path.png", draw, 500, 500) 42 | -------------------------------------------------------------------------------- /tutorial/shapes/polygons-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/polygons-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/polygons.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-11-21 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | # Create some polygons and lines 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import Rectangle, Square, Triangle, Polygon, Line 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | 14 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 15 | 16 | red = Color('red') 17 | green = Color('green') 18 | blue = Color('blue') 19 | thickness = 2 20 | 21 | Line(ctx).of_start_end((150, 150), (50, 50)).stroke(red, thickness) 22 | Line(ctx).of_start_end((300, 150), (200, 50)).as_ray().stroke(red, thickness) 23 | Line(ctx).of_start_end((450, 150), (350, 50)).as_line().stroke(red, thickness) 24 | 25 | Triangle(ctx).of_corners((50, 200), (150, 200), (125, 300)).stroke(green, thickness) 26 | Square(ctx).of_corner_size((200, 200), 100).stroke(green, thickness) 27 | Rectangle(ctx).of_corner_size((350, 200), 100, 75).stroke(green, thickness) 28 | 29 | Polygon(ctx).of_points([(50, 350), (250, 400), (250, 500), (50, 375)]).stroke(blue, thickness) 30 | Polygon(ctx).of_points([(300, 350), (500, 400), (500, 500), (300, 375)]).open().stroke(blue, thickness) 31 | 32 | make_image("polygons-tutorial.png", draw, 550, 550) -------------------------------------------------------------------------------- /tutorial/shapes/regularpolygons-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/regularpolygons-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/regularpolygons.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-06-05 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | # Create some polygons and lines 7 | 8 | from generativepy.drawing import make_image, setup 9 | from generativepy.color import Color 10 | from generativepy.geometry import RegularPolygon, Circle 11 | import math 12 | 13 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 14 | 15 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 16 | 17 | red = Color('crimson') 18 | green = Color('darkgreen') 19 | blue = Color('dodgerblue') 20 | 21 | RegularPolygon(ctx).of_centre_sides_radius((150, 150), 5, 100)\ 22 | .fill(blue)\ 23 | .stroke(green, 5) 24 | 25 | RegularPolygon(ctx).of_centre_sides_radius((400, 150), 6, 100)\ 26 | .fill(blue)\ 27 | .stroke(green, 5) 28 | 29 | RegularPolygon(ctx).of_centre_sides_radius((650, 150), 6, 100, math.pi/12)\ 30 | .fill(blue)\ 31 | .stroke(green, 5) 32 | 33 | p = RegularPolygon(ctx).of_centre_sides_radius((150, 400), 5, 100)\ 34 | .fill(blue)\ 35 | .stroke(green, 5) 36 | Circle(ctx).of_center_radius((150, 400), p.inner_radius).stroke(red, 5) 37 | 38 | p = RegularPolygon(ctx).of_centre_sides_radius((400, 400), 5, 100)\ 39 | .fill(blue)\ 40 | .stroke(green, 5) 41 | Circle(ctx).of_center_radius((400, 400), p.outer_radius).stroke(red, 5) 42 | 43 | p = RegularPolygon(ctx).of_centre_sides_radius((650, 400), 5, 100)\ 44 | .fill(blue)\ 45 | .stroke(green, 5) 46 | for v in p.vertices: 47 | Circle(ctx).of_center_radius(v, 10).fill(red) 48 | 49 | 50 | 51 | make_image("regularpolygons-tutorial.png", draw, 800, 550) -------------------------------------------------------------------------------- /tutorial/shapes/stroke-style-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/stroke-style-tutorial.png -------------------------------------------------------------------------------- /tutorial/shapes/text-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/text-align.png -------------------------------------------------------------------------------- /tutorial/shapes/text-align.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-04 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Text, Circle 9 | 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 13 | 14 | Text(ctx).of("Left", (50, 50)).font("Times").size(20).align_left().align_baseline().fill(Color('blue')) 15 | Text(ctx).of("Aligned", (50, 70)).font("Times").size(20).align_left().align_baseline().fill(Color('red')) 16 | Text(ctx).of("Text", (50, 90)).font("Times").size(20).align_left().align_baseline().fill(Color('blue')) 17 | 18 | Text(ctx).of("Centre", (250, 50)).font("Times").size(20).align_center().align_baseline().fill(Color('blue')) 19 | Text(ctx).of("Aligned", (250, 70)).font("Times").size(20).align_center().align_baseline().fill(Color('red')) 20 | Text(ctx).of("Text", (250, 90)).font("Times").size(20).align_center().align_baseline().fill(Color('blue')) 21 | 22 | Text(ctx).of("Right", (450, 50)).font("Times").size(20).align_right().align_baseline().fill(Color('blue')) 23 | Text(ctx).of("Aligned", (450, 70)).font("Times").size(20).align_right().align_baseline().fill(Color('red')) 24 | Text(ctx).of("Text", (450, 90)).font("Times").size(20).align_right().align_baseline().fill(Color('blue')) 25 | 26 | Circle(ctx).of_center_radius((190, 200), 2).fill(Color(0, 0, 1)) 27 | Text(ctx).of("gTop", (200, 200)).font("Times").size(20).align_left().align_top().fill(Color('black')) 28 | 29 | Circle(ctx).of_center_radius((190, 250), 2).fill(Color(0, 0, 1)) 30 | Text(ctx).of("gMid", (200, 250)).font("Times").size(20).align_left().align_middle().fill(Color('black')) 31 | 32 | Circle(ctx).of_center_radius((190, 300), 2).fill(Color(0, 0, 1)) 33 | Text(ctx).of("gBase", (200, 300)).font("Times").size(20).align_left().align_baseline().fill(Color('black')) 34 | 35 | Circle(ctx).of_center_radius((190, 350), 2).fill(Color(0, 0, 1)) 36 | Text(ctx).of("gBottom", (200, 350)).font("Times").size(20).align_left().align_bottom().fill(Color('black')) 37 | 38 | 39 | make_image("text-align.png", draw, 500, 400) -------------------------------------------------------------------------------- /tutorial/shapes/text-drawing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/text-drawing.png -------------------------------------------------------------------------------- /tutorial/shapes/text-drawing.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-05-02 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Text 9 | 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 13 | 14 | Text(ctx).of("Filled Times", (100, 100)).font("Times").size(40).fill(Color('blue')) 15 | Text(ctx).of("Filled Arial", (100, 150)).font("Arial").size(40).fill(Color('red')) 16 | Text(ctx).of("Small", (100, 180)).font("Arial").size(20).fill(Color('darkgreen')) 17 | Text(ctx).of("Large", (100, 240)).font("Arial").size(60).fill(Color('magenta')) 18 | Text(ctx).of("Stroke", (100, 310)).font("Arial").size(60).stroke(Color('black'), 4) 19 | Text(ctx).of("Fill Stroke", (100, 380)).font("Arial").size(60)\ 20 | .fill(Color('blue')).stroke(Color('red'), 2) 21 | Text(ctx).of("Dashed", (100, 450)).font("Arial").size(60).stroke(Color('black'), 3, dash=[4]) 22 | 23 | 24 | make_image("text-drawing.png", draw, 500, 500) -------------------------------------------------------------------------------- /tutorial/shapes/text-metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/text-metrics.png -------------------------------------------------------------------------------- /tutorial/shapes/text-metrics.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-04 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Text, Rectangle 9 | 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | setup(ctx, pixel_width, pixel_width, background=Color(0.8)) 13 | 14 | x, y = 50, 100 15 | text = Text(ctx).of("Text size", (x, y)).font("Times").size(100).fill(Color('blue')) 16 | width, height = text.get_size() 17 | Text(ctx).of('{} by {}'.format(width, height), (x+400, y))\ 18 | .font("Times").size(40).fill(Color('black')) 19 | 20 | 21 | x, y = 50, 200 22 | text = Text(ctx).of("xyz", (x, y)).font("Times").size(100).fill(Color('blue')) 23 | width, height = text.get_size() 24 | Text(ctx).of('{} by {}'.format(width, height), (x+400, y))\ 25 | .font("Times").size(40).fill(Color('black')) 26 | 27 | x, y = 50, 300 28 | text = Text(ctx).of("Text extents", (x, y)).font("Times").size(100).fill(Color('blue')) 29 | x_bearing, y_bearing, width, height, x_advance, y_advance = text.get_metrics() 30 | Rectangle(ctx).of_corner_size((x + x_bearing, y+y_bearing), width, height).stroke(Color('red')) 31 | 32 | x, y = 50, 400 33 | text = Text(ctx).of("xyz", (x, y)).font("Times").size(100).fill(Color('blue')) 34 | x_bearing, y_bearing, width, height, x_advance, y_advance = text.get_metrics() 35 | Rectangle(ctx).of_corner_size((x + x_bearing, y + y_bearing), width, height).stroke(Color('red')) 36 | 37 | x, y = 300, 400 38 | text = Text(ctx).of("'''", (x, y)).font("Times").size(100).fill(Color('blue')) 39 | x_bearing, y_bearing, width, height, x_advance, y_advance = text.get_metrics() 40 | Rectangle(ctx).of_corner_size((x + x_bearing, y + y_bearing), width, height).stroke(Color('red')) 41 | 42 | 43 | make_image("text-metrics.png", draw, 700, 500) -------------------------------------------------------------------------------- /tutorial/shapes/text-offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/text-offset.png -------------------------------------------------------------------------------- /tutorial/shapes/text-offset.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2021-05-02 3 | # Copyright (C) 2021, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Text, Circle, Line 9 | import math 10 | 11 | 12 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 13 | setup(ctx, pixel_width, pixel_height, background=Color(1)) 14 | 15 | a = (100, 100) 16 | Circle(ctx).of_center_radius(a, 5).fill(Color('red')) 17 | Text(ctx).of("A", a).font("Arial").size(40).offset(20, 30).fill(Color(0)) 18 | 19 | b = (300, 100) 20 | angle = math.pi*3/4 21 | x = (b[0]+150*math.cos(angle), b[1]+150*math.sin(angle)) 22 | Line(ctx).of_start_end(b, x).stroke(Color('orange'), 4, dash=[5]) 23 | Circle(ctx).of_center_radius(b, 5).fill(Color('red')) 24 | Text(ctx).of("B", b).font("Arial").size(40).offset_angle(angle, 100).fill(Color(0)) 25 | 26 | c = (500, 100) 27 | d = (600, 50) 28 | Line(ctx).of_start_end(c, d).stroke(Color('orange'), 4, dash=[5]) 29 | Circle(ctx).of_center_radius(c, 5).fill(Color('red')) 30 | Circle(ctx).of_center_radius(d, 5).fill(Color('blue')) 31 | Text(ctx).of("C", c).font("Arial").size(40).offset_towards(d, 30).fill(Color(0)) 32 | 33 | 34 | make_image("text-offset.png", draw, 700, 300) -------------------------------------------------------------------------------- /tutorial/shapes/tick-markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/tick-markers.png -------------------------------------------------------------------------------- /tutorial/shapes/turtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/shapes/turtle.png -------------------------------------------------------------------------------- /tutorial/shapes/turtle.py: -------------------------------------------------------------------------------- 1 | from generativepy.drawing import make_image, setup, ROUND 2 | from generativepy.color import Color 3 | from generativepy.geometry import Turtle 4 | import math 5 | 6 | ''' 7 | Demonstrate paths in the geometry module. 8 | ''' 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | setup(ctx, width, height, background=Color(0.8)) 12 | 13 | turtle = Turtle(ctx) 14 | turtle.move_to(100, 100).forward(50).left(math.pi / 2).forward(50).left(math.pi / 4).forward(50) 15 | 16 | turtle = Turtle(ctx) 17 | turtle.move_to(200, 300).set_style(Color('green'), line_width=5, dash=[10]) \ 18 | .push().forward(100).pop() \ 19 | .push().left(3 * math.pi / 4).forward(100).pop() \ 20 | .right(3 * math.pi / 4).forward(100) 21 | 22 | turtle = Turtle(ctx) 23 | turtle.move_to(350, 100).right(math.pi / 2).set_style(Color('red'), line_width=5, dash=[10], cap=ROUND).forward(100) 24 | turtle.set_style(Color('blue'), line_width=2, dash=[]).forward(100) 25 | turtle.set_style(Color('black'), line_width=4, dash=[15]).forward(100) 26 | 27 | make_image("turtle.png", draw, 500, 500) 28 | -------------------------------------------------------------------------------- /tutorial/transforms/advanced-transform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/advanced-transform.png -------------------------------------------------------------------------------- /tutorial/transforms/advanced.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-09 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Text, Transform 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 13 | 14 | blue = Color('blue') 15 | red = Color('red') 16 | 17 | with Transform(ctx).translate(50, 150): 18 | with Transform(ctx).matrix([1, 0, -0.5, 1, 0, 0]): 19 | print(ctx.get_matrix()) 20 | Text(ctx).of('A', (0, 0)).size(100).fill(red) 21 | Text(ctx).of('B', (80, 0)).size(100).fill(blue) 22 | 23 | 24 | make_image("advanced-transform.png", draw, 450, 200) -------------------------------------------------------------------------------- /tutorial/transforms/clip-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/clip-tutorial.png -------------------------------------------------------------------------------- /tutorial/transforms/clip.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-10 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Circle, Square, Text, Transform 9 | 10 | def draw(ctx, width, height, frame_no, frame_count): 11 | setup(ctx, width, height, background=Color(0.8)) 12 | 13 | # Create a circular clip region and draw some squares in it 14 | with Transform(ctx): 15 | Circle(ctx).of_center_radius((190, 190), 100).clip() 16 | Square(ctx).of_corner_size((100, 100), 80).fill(Color('red')) 17 | Square(ctx).of_corner_size((100, 200), 80).fill(Color('green')) 18 | Square(ctx).of_corner_size((200, 100), 80).fill(Color('blue')) 19 | Square(ctx).of_corner_size((200, 200), 80).fill(Color('black')) 20 | 21 | with Transform(ctx): 22 | Text(ctx).of("ABC", (150, 350)).font("Times").size(150).align_left().align_top().clip() 23 | circles = [(200, 380, 'orange'), (200, 450, 'cyan'), (300, 380, 'green'), 24 | (300, 450, 'purple'), (400, 380, 'yellow'), (400, 450, 'blue')] 25 | for x, y, color in circles: 26 | Circle(ctx).of_center_radius((x, y), 70).fill(Color(color)) 27 | 28 | 29 | make_image("clip-tutorial.png", draw, 500, 500) 30 | -------------------------------------------------------------------------------- /tutorial/transforms/mirror-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/mirror-tutorial.png -------------------------------------------------------------------------------- /tutorial/transforms/mirror.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-09 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Text, Transform, Line 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 13 | 14 | blue = Color('blue') 15 | red = Color('red') 16 | green = Color('green') 17 | thickness = 4 18 | 19 | Text(ctx).of('F', (40, 100)).size(100).fill(blue) 20 | Line(ctx).of_start_end((100, 20), (100, 110)).stroke(green, thickness) 21 | 22 | with Transform(ctx).scale(-1, 1, (100, 0)): 23 | Text(ctx).of('F', (40, 100)).size(100).fill(red) 24 | 25 | Text(ctx).of('W', (240, 100)).size(100).fill(blue) 26 | Line(ctx).of_start_end((240, 70), (340, 70)).stroke(green, thickness) 27 | 28 | with Transform(ctx).scale(1, -1, (0, 60)): 29 | Text(ctx).of('W', (240, 100)).size(100).fill(red.with_a(0.6)) 30 | 31 | 32 | make_image("mirror-tutorial.png", draw, 450, 150) -------------------------------------------------------------------------------- /tutorial/transforms/rotate-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/rotate-tutorial.png -------------------------------------------------------------------------------- /tutorial/transforms/rotate.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-09 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle, Transform, Circle 9 | import math 10 | 11 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 12 | 13 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 14 | 15 | blue = Color('blue') 16 | red = Color('red') 17 | green = Color('green') 18 | thickness = 4 19 | 20 | Rectangle(ctx).of_corner_size((100, 20), 100, 50).fill(blue) 21 | 22 | with Transform(ctx).rotate(math.pi/4): 23 | Rectangle(ctx).of_corner_size((100, 20), 100, 50).fill(red) 24 | 25 | with Transform(ctx) as t: 26 | Rectangle(ctx).of_corner_size((200, 150), 100, 100).stroke(blue, thickness) 27 | t.rotate(math.pi/6, (200, 150)) 28 | Rectangle(ctx).of_corner_size((200, 150), 100, 100).stroke(red, thickness) 29 | t.rotate(math.pi/6, (200, 150)) 30 | Rectangle(ctx).of_corner_size((200, 150), 100, 100).stroke(red, thickness) 31 | Circle(ctx).of_center_radius((200, 150), 5).fill(green) 32 | 33 | make_image("rotate-tutorial.png", draw, 450, 400) -------------------------------------------------------------------------------- /tutorial/transforms/scale-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/scale-tutorial.png -------------------------------------------------------------------------------- /tutorial/transforms/scale.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-09 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle, Transform, Circle 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 13 | 14 | blue = Color('blue') 15 | red = Color('red') 16 | green = Color('green') 17 | thickness = 8 18 | 19 | Rectangle(ctx).of_corner_size((50, 40), 100, 30).fill(blue) 20 | 21 | with Transform(ctx).scale(1.5, 2): 22 | Rectangle(ctx).of_corner_size((50, 40), 100, 30).fill(red) 23 | 24 | 25 | with Transform(ctx) as t: 26 | Circle(ctx).of_center_radius((220, 260), 5).fill(green) 27 | Rectangle(ctx).of_corner_size((20, 160), 400, 200).stroke(blue, thickness) 28 | t.scale(0.5, 0.5, (220, 260)) 29 | Rectangle(ctx).of_corner_size((20, 160), 400, 200).stroke(red, thickness) 30 | t.scale(0.5, 0.5, (220, 260)) 31 | Rectangle(ctx).of_corner_size((20, 160), 400, 200).stroke(red, thickness) 32 | 33 | make_image("scale-tutorial.png", draw, 450, 400) -------------------------------------------------------------------------------- /tutorial/transforms/translate-tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/translate-tutorial.png -------------------------------------------------------------------------------- /tutorial/transforms/translate.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-09 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle, Transform 9 | 10 | def draw(ctx, pixel_width, pixel_height, frame_no, frame_count): 11 | 12 | setup(ctx, pixel_width, pixel_height, background=Color(0.8)) 13 | 14 | blue = Color('blue') 15 | red = Color('red') 16 | thickness = 2 17 | 18 | Rectangle(ctx).of_corner_size((10, 10), 200, 150).stroke(blue, thickness) 19 | 20 | with Transform(ctx).translate(0, 200): 21 | Rectangle(ctx).of_corner_size((10, 10), 200, 150).stroke(red, thickness) 22 | 23 | 24 | with Transform(ctx) as t: 25 | Rectangle(ctx).of_corner_size((250, 100), 50, 150).fill(blue) 26 | t.translate(60, 10) 27 | Rectangle(ctx).of_corner_size((250, 100), 50, 150).fill(red) 28 | t.translate(60, 10) 29 | Rectangle(ctx).of_corner_size((250, 100), 50, 150).fill(red) 30 | 31 | make_image("translate-tutorial.png", draw, 450, 400) -------------------------------------------------------------------------------- /tutorial/transforms/user-scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmcbride/generativepy/d009e7e02604993c470e198993866bc76accd300/tutorial/transforms/user-scale.png -------------------------------------------------------------------------------- /tutorial/transforms/user-scale.py: -------------------------------------------------------------------------------- 1 | # Author: Martin McBride 2 | # Created: 2022-01-10 3 | # Copyright (C) 2022, Martin McBride 4 | # License: MIT 5 | 6 | from generativepy.drawing import make_image, setup 7 | from generativepy.color import Color 8 | from generativepy.geometry import Rectangle, Circle 9 | 10 | def draw_rect(ctx, width, height, frame_no, frame_count): 11 | setup(ctx, width, height, width=5, background=Color(0.4)) 12 | color = Color(1, 0.5, 0) 13 | Rectangle(ctx).of_corner_size((1, 1.5), 2.5, 2).fill(color) 14 | 15 | make_image("user-scale.png", draw_rect, 500, 400) --------------------------------------------------------------------------------