You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
3.0 KiB
80 lines
3.0 KiB
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2021 harrysentonbury
|
|
# GNU General Public License v3.0
|
|
|
|
|
|
import numpy as np
|
|
from PIL import Image
|
|
from numba import jit
|
|
import argparse
|
|
|
|
|
|
parser = argparse.ArgumentParser(prog="FRACTAL")
|
|
parser.add_argument("-m", "--magnification", nargs="?", metavar='MAGNIFICATION',
|
|
help="required magnification", type=float, default=1.0)
|
|
parser.add_argument("-n", "--nodisplay", action="store_false",
|
|
help="do not display output")
|
|
parser.add_argument("-o", "--outfile", nargs="?", metavar="SAVE_AS",
|
|
help="save as <path to filename.whateva>", type=str)
|
|
parser.add_argument("-s", "--size", nargs="?", metavar="SIZE",
|
|
help="Size by y pixels default=700", type=int, default=700)
|
|
parser.add_argument("-x", "--xyoffset", action='store', nargs=2, metavar="OFFSET",
|
|
help="row and column offset", type=float, default=[0.0, 0.0])
|
|
|
|
args = parser.parse_args()
|
|
if args.magnification < 1.0:
|
|
parser.error("magnification must have a minimum value of 1")
|
|
if abs(args.xyoffset[1]) > 1 or args.xyoffset[0] < -2 or args.xyoffset[0] > 1:
|
|
parser.error("xyoffset must range between -2 to 1 and -1 to 1 respectively")
|
|
|
|
|
|
@jit
|
|
def mandelbrot(re, im, iter_limit):
|
|
c = complex(re, im)
|
|
z = complex(0.0, 0.0)
|
|
for i in range(iter_limit):
|
|
z = z**2 + c
|
|
if (z.real**2) + (z.imag**2) >= 4:
|
|
return i
|
|
return iter_limit
|
|
|
|
|
|
try:
|
|
iteration_limit = 85
|
|
columns = args.size
|
|
rows = int(columns * 1.5)
|
|
magnification = args.magnification
|
|
row_offset = args.xyoffset[0]
|
|
col_offset = args.xyoffset[1]
|
|
|
|
row_extent = (np.array([-2, 1.0]) + (row_offset * magnification)) * 1 / magnification
|
|
column_extent = (np.array([1.0, -1.0]) + (col_offset * magnification)) * 1 / magnification
|
|
result = np.zeros([rows, columns])
|
|
for row_index, re in enumerate(np.linspace(row_extent[0], row_extent[1], rows)):
|
|
for column_index, im in enumerate(np.linspace(column_extent[0], column_extent[1], columns)):
|
|
result[row_index, column_index] = mandelbrot(re, im, iteration_limit)
|
|
|
|
result = result.T
|
|
print(f"image size: {result.shape[1]}, {result.shape[0]}")
|
|
image_array = np.zeros((columns, rows, 3), dtype=np.uint8)
|
|
for real in range(rows):
|
|
for imaginary in range(columns):
|
|
indexed = result[imaginary, real]
|
|
image_array[imaginary, real] = (np.uint8(170 - (indexed * 2)), # RED
|
|
np.uint8((np.sin((indexed) * np.pi / 85) * 255)), # GREEN
|
|
np.uint8(80 + (indexed * 2)) # BLUE
|
|
)
|
|
|
|
new_image = Image.fromarray(image_array, "RGB")
|
|
if args.outfile:
|
|
new_image.save(args.outfile)
|
|
print("image saved")
|
|
if args.nodisplay:
|
|
new_image.show()
|
|
|
|
except KeyboardInterrupt:
|
|
parser.exit("\n Interupt by user")
|
|
except Exception as e:
|
|
parser.exit(f"{type(e).__name__}: {str(e)}")
|