Module genpygoodies.complex
Complex number related functionality
Expand source code
# Author: Martin McBride
# Created: 2024-01-23
# Copyright (C) 2024, Martin McBride
# License: MIT
"""
Complex number related functionality
"""
import numpy as np
from generativepy.color import ArtisticColorScheme, Color, make_colormap
from generativepy.drawing import CENTER, FONT_WEIGHT_BOLD
from generativepy.drawing import (setup, MIDDLE, LEFT, )
from generativepy.geometry import (Text, Transform, Line, Circle, )
from generativepy.graph import Axes
from generativepy.math import Vector as V
FONT = "DejaVu Sans"
cs = ArtisticColorScheme()
def draw_overlay(ctx, pixel_width, pixel_height, frame_no, frame_count):
"""
Drawing function to create an overlay to add "real" and "imaginary" text to a 3D plot.
This can be used to annotate a plot created with povray to show z against a complex x-y plane.
Args:
ctx:
pixel_width:
pixel_height:
frame_no:
frame_count:
Returns:
"""
# Draw the text overlay (marking axes)
setup(ctx, pixel_width, pixel_height, width=500, background=Color(1))
a = V(58, 350)
b = V(250, 457)
c = V(442, 350)
angle = 0.55
mid1 = ((a[0] + b[0]) / 2 - 50, (a[1] + b[1]) / 2 + 30)
mid2 = ((c[0] + b[0]) / 2 + 50, (c[1] + b[1]) / 2 + 30)
with Transform(ctx).translate(*mid1).rotate(angle):
(
Text(ctx)
.of("Real", (0, 0))
.size(20)
.font(FONT, FONT_WEIGHT_BOLD)
.align(CENTER, MIDDLE)
.fill(cs.BLUE)
)
with Transform(ctx).translate(*mid2).rotate(-angle):
(
Text(ctx)
.of("Imaginary", (0, 0))
.size(20)
.font(FONT, FONT_WEIGHT_BOLD)
.align(CENTER, MIDDLE)
.fill(cs.BLUE)
)
def i_formater(value, div):
if isinstance(value, int):
if value == 1:
return "i"
if value == -1:
return "-i"
return str(value) + "i"
return str(round(value * 1000) / 1000) + "i"
def style_argand_transparent(axes):
(
axes.background(cs.WHITE.with_a(0))
.axis_linestyle(cs.BLACK, line_width=2.5)
.division_linestyle(cs.WHITE.with_a(0), line_width=2.5)
.text_color(cs.BLACK)
)
axes.with_division_formatters(y_div_formatter=i_formater)
def draw_complex_plane(
ctx,
f,
low,
high,
width,
height,
startx=None,
starty=None,
elements=200,
steps=[-2, -1, 0, 1, 2],
divisions=(1, 1),
colormap=None,
):
"""
Plot a real function of a complex variable on and argand diagram, as a colour map. The plot also includes a key.
Args:
ctx:
f:
low:
high:
width:
height:
startx:
starty:
elements:
steps:
divisions:
colormap:
"""
if startx is None:
startx = -width / 2
if starty is None:
starty = -height / 2
if not colormap:
colormap = make_colormap(
1000,
colors=[
cs.YELLOW,
cs.ORANGE,
Color("red"),
cs.GREY.light2,
cs.GREEN,
cs.CYAN,
cs.BLUE,
],
)
graph_size = 380
graph_pos = (8, 8)
xcoords = np.linspace(startx, startx + width, num=elements)
ycoords = np.linspace(starty, starty + height, num=elements)
x = np.tile(xcoords, (elements, 1))
y = np.tile(np.array([ycoords]).transpose(), (1, elements))
vfunc = np.vectorize(f)
out = vfunc(x, y)
axes = (
Axes(ctx, graph_pos, graph_size, graph_size)
.of_start((startx, starty))
.of_extent((width, height))
.with_divisions(divisions)
)
for i in range(elements):
x = xcoords[i]
for j in range(elements):
y = ycoords[j]
p = axes.transform_from_graph((x, y))
v = max(0, min(999, int((out[j, i] - low) * 1000 / (high - low))))
Circle(ctx).of_center_radius(p, 2).fill(colormap[v])
style_argand_transparent(axes)
axes.draw()
with Transform(ctx).translate(400, 0):
for i in range(graph_size):
p1 = (0, graph_size + graph_pos[1] - i)
p2 = (30, graph_size + graph_pos[1] - i)
Line(ctx).of_start_end(p1, p2).stroke(
colormap[i * len(colormap) // graph_size], 2
)
for v in steps:
l = (v - low) / (high - low)
p = (1 - l) * graph_size + graph_pos[1]
Line(ctx).of_start_end((30, p), (35, p)).stroke(cs.BLACK, 2)
Text(ctx).of(str(v), (35, p)).size(15).align(LEFT, MIDDLE).offset(
5, 0
).fill(cs.BLACK)
Functions
def draw_complex_plane(ctx, f, low, high, width, height, startx=None, starty=None, elements=200, steps=[-2, -1, 0, 1, 2], divisions=(1, 1), colormap=None)
-
Plot a real function of a complex variable on and argand diagram, as a colour map. The plot also includes a key.
Args
ctx: f: low: high: width: height: startx: starty: elements: steps: divisions: colormap:
Expand source code
def draw_complex_plane( ctx, f, low, high, width, height, startx=None, starty=None, elements=200, steps=[-2, -1, 0, 1, 2], divisions=(1, 1), colormap=None, ): """ Plot a real function of a complex variable on and argand diagram, as a colour map. The plot also includes a key. Args: ctx: f: low: high: width: height: startx: starty: elements: steps: divisions: colormap: """ if startx is None: startx = -width / 2 if starty is None: starty = -height / 2 if not colormap: colormap = make_colormap( 1000, colors=[ cs.YELLOW, cs.ORANGE, Color("red"), cs.GREY.light2, cs.GREEN, cs.CYAN, cs.BLUE, ], ) graph_size = 380 graph_pos = (8, 8) xcoords = np.linspace(startx, startx + width, num=elements) ycoords = np.linspace(starty, starty + height, num=elements) x = np.tile(xcoords, (elements, 1)) y = np.tile(np.array([ycoords]).transpose(), (1, elements)) vfunc = np.vectorize(f) out = vfunc(x, y) axes = ( Axes(ctx, graph_pos, graph_size, graph_size) .of_start((startx, starty)) .of_extent((width, height)) .with_divisions(divisions) ) for i in range(elements): x = xcoords[i] for j in range(elements): y = ycoords[j] p = axes.transform_from_graph((x, y)) v = max(0, min(999, int((out[j, i] - low) * 1000 / (high - low)))) Circle(ctx).of_center_radius(p, 2).fill(colormap[v]) style_argand_transparent(axes) axes.draw() with Transform(ctx).translate(400, 0): for i in range(graph_size): p1 = (0, graph_size + graph_pos[1] - i) p2 = (30, graph_size + graph_pos[1] - i) Line(ctx).of_start_end(p1, p2).stroke( colormap[i * len(colormap) // graph_size], 2 ) for v in steps: l = (v - low) / (high - low) p = (1 - l) * graph_size + graph_pos[1] Line(ctx).of_start_end((30, p), (35, p)).stroke(cs.BLACK, 2) Text(ctx).of(str(v), (35, p)).size(15).align(LEFT, MIDDLE).offset( 5, 0 ).fill(cs.BLACK)
def draw_overlay(ctx, pixel_width, pixel_height, frame_no, frame_count)
-
Drawing function to create an overlay to add "real" and "imaginary" text to a 3D plot.
This can be used to annotate a plot created with povray to show z against a complex x-y plane.
Args
ctx: pixel_width: pixel_height: frame_no: frame_count: Returns:
Expand source code
def draw_overlay(ctx, pixel_width, pixel_height, frame_no, frame_count): """ Drawing function to create an overlay to add "real" and "imaginary" text to a 3D plot. This can be used to annotate a plot created with povray to show z against a complex x-y plane. Args: ctx: pixel_width: pixel_height: frame_no: frame_count: Returns: """ # Draw the text overlay (marking axes) setup(ctx, pixel_width, pixel_height, width=500, background=Color(1)) a = V(58, 350) b = V(250, 457) c = V(442, 350) angle = 0.55 mid1 = ((a[0] + b[0]) / 2 - 50, (a[1] + b[1]) / 2 + 30) mid2 = ((c[0] + b[0]) / 2 + 50, (c[1] + b[1]) / 2 + 30) with Transform(ctx).translate(*mid1).rotate(angle): ( Text(ctx) .of("Real", (0, 0)) .size(20) .font(FONT, FONT_WEIGHT_BOLD) .align(CENTER, MIDDLE) .fill(cs.BLUE) ) with Transform(ctx).translate(*mid2).rotate(-angle): ( Text(ctx) .of("Imaginary", (0, 0)) .size(20) .font(FONT, FONT_WEIGHT_BOLD) .align(CENTER, MIDDLE) .fill(cs.BLUE) )
def i_formater(value, div)
-
Expand source code
def i_formater(value, div): if isinstance(value, int): if value == 1: return "i" if value == -1: return "-i" return str(value) + "i" return str(round(value * 1000) / 1000) + "i"
def style_argand_transparent(axes)
-
Expand source code
def style_argand_transparent(axes): ( axes.background(cs.WHITE.with_a(0)) .axis_linestyle(cs.BLACK, line_width=2.5) .division_linestyle(cs.WHITE.with_a(0), line_width=2.5) .text_color(cs.BLACK) ) axes.with_division_formatters(y_div_formatter=i_formater)