Source code for function

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Mathematical equations.

:Date: 2019-11-04

.. module:: function
  :platform: *nix, Windows
  :synopsis: Mathematical equations.

.. moduleauthor:: Daniel Weschke <daniel.weschke@directbox.de>
"""
import math
from pylib.mathematics import lcm

[docs]def transformation(f, scale_vertical=1, scale_horizontal=1, shift_horizontal=0, shift_vertical=0): r"""Transform functions. :param f: function or list of functions :type f: function or list :param scale_vertical: "a" scale factor in vertical direction (default = 1) :type height: float :param scale_horizontal: "b" scale factor in horizontal direction (default = 1) :type height: float :param shift_horizontal: "c" shift factor in horizontal direction (default = 0) :type height: float :param shift_vertical: "d" shift factor in vertical direction (default = 0) :type height: float :returns: transformed function or list of transformed functions :rtype: function or list .. math:: y = a \, f(b\,(x-c)) + d """ # shorter variables a = scale_vertical b = scale_horizontal c = shift_horizontal d = shift_vertical # check if f is a function than put it in a list and return only # the function, not the one element list if callable(f): return transformation( [f], scale_vertical=a, scale_horizontal=b, shift_horizontal=c, shift_vertical=d)[0] # otherwise assume list of functions if not f: # if f is empty. End of the recursive fucntion return [] return [lambda x, t: a*f[0](b*(x-c), t)+d] +\ transformation( f[1:], scale_vertical=a, scale_horizontal=b, shift_horizontal=c, shift_vertical=d)
[docs]def sine_wave(A=1, k=1, f=1, phi=0, D=0, degree=False): r"""A sine wave or sinusoid is a mathematical curve that describes a smooth periodic oscillation. :param A: amplitude :type A: float or int :param k: (angular) wave number :type k: float or int :param f: ordinary frequency :type f: float or int :param phi: phase :type phi: float or int :param D: non-zero center amplitude :type D: float or int :param degree: boolean to switch between radians and degree. If False phi is interpreted in radians and if True then phi is interpreted in degrees. :type degree: bool :results: sine wave function of spatial variable x and optional time t In general, the function is: .. math:: y(x,t) = A\sin(kx + 2\pi f t + \varphi) + D \\ y(x,t) = A\sin(kx + \omega t + \varphi) + D where: * A, amplitude, the peak deviation of the function from zero. * f, ordinary frequency, the number of oscillations (cycles) that occur each second of time. * ω = 2πf, angular frequency, the rate of change of the function argument in units of radians per second. If ω < 0 the wave is moving to the right, if ω > 0 the wave is moving to the left. * φ, phase, specifies (in radians) where in its cycle the oscillation is at t = 0. * x, spatial variable that represents the position on the dimension on which the wave propagates. * k, characteristic parameter called wave number (or angular wave number), which represents the proportionality between the angular frequency ω and the linear speed (speed of propagation) ν. * D, non-zero center amplitude. The wavenumber is related to the angular frequency by: .. math:: k={\omega \over v}={2\pi f \over v}={2\pi \over \lambda } where λ (lambda) is the wavelength, f is the frequency, and v is the linear speed. .. seealso:: :meth:`cosine_wave` """ if degree: phi = math.radians(phi) return lambda x, t=0: A*math.sin(k*x + 2*math.pi*f*t + phi) + D
[docs]def cosine_wave(A=1, k=1, f=1, phi=0, D=0, degree=False): r"""A cosine wave is said to be sinusoidal, because, :math:`\cos(x) = \sin(x + \pi/2)`, which is also a sine wave with a phase-shift of π/2 radians. Because of this head start, it is often said that the cosine function leads the sine function or the sine lags the cosine. :param A: amplitude :type A: float or int :param k: (angular) wave number :type k: float or int :param f: ordinary frequency :type f: float or int :param phi: phase :type phi: float or int :param D: non-zero center amplitude :type D: float or int :param degree: boolean to switch between radians and degree. If False phi is interpreted in radians and if True then phi is interpreted in degrees. :type degree: bool :results: sine wave function of spatial variable x and optional time t .. seealso:: :meth:`sine_wave` """ if degree: phi = phi + 90 else: phi = phi + math.pi/2 return sine_wave(A=A, k=k, f=f, phi=phi, D=D, degree=degree)
# # Parametric equations # roulette #
[docs]def hypotrochoid(R, r, d): r"""Hypotrochoid A point is attached with a distance d from the center of a circle of radius r. The circle is rolling around the inside of a fixed circle of radius R. :param R: radius of the fixed exterior circle :type R: float :param r: radius of the rolling circle inside of the fixed circle :typre r: float :param d: distance from the center of the interior circle :type d: float :results: functions for x of theta and y of theta :rtype: tuple .. math:: x(\theta) = (R - r)\cos\theta + d\cos\left(\frac{R-r}{r}\theta\right) \\ y(\theta) = (R - r)\sin\theta - d\sin\left(\frac{R-r}{r}\theta\right) \\ \theta = \left[0, 2\pi\frac{\mathrm{lcm}(r, R)}{R}\right] :: * * * * R * * * * * * * * * r ** * * .... * * * d * * * ** * * * * * * * * * * * >>> x, y = hyotrochoid(20, 6, 6)[:1] >>> x, y, theta_end = hyotrochoid(20, 6, 6) .. seealso:: :meth:`mathematics.lcm` """ x = lambda theta: (R-r)*math.cos(theta) + d*math.cos((R-r)/r * theta) y = lambda theta: (R-r)*math.sin(theta) - d*math.sin((R-r)/r * theta) theta_end = 2*math.pi*lcm(r, R)/R return x, y, theta_end
[docs]def epitrochoid(R, r, d): r"""Epitrochoid A point is attached with a distance d from the center of a circle of radius r. The circle is rolling around the outside of a fixed circle of radius R. :param R: radius of the fixed interior circle :type R: float :param r: radius of the rolling circle outside of the fixed circle :typre r: float :param d: distance from the center of the exterior circle :type d: float :results: functions for x of theta and y of theta :rtype: tuple .. math:: x(\theta) = (R + r)\cos\theta - d\cos\left(\frac{R+r}{r}\theta\right) \\ y(\theta) = (R + r)\sin\theta - d\sin\left(\frac{R+r}{r}\theta\right) \\ \theta = \left[0, 2\pi\right] :: * * * * R * * * * * * * * * * r * * ** .... * * ** d * * * * * * * * * * * * * * * * >>> x, y = epitrochoid(3, 1, 0.5)[:1] >>> x, y, theta_end = epitrochoid(3, 1, 0.5) """ x = lambda theta: (R+r)*math.cos(theta) - d*math.cos((R+r)/r * theta) y = lambda theta: (R+r)*math.sin(theta) - d*math.sin((R+r)/r * theta) theta_end = 2*math.pi return x, y, theta_end
[docs]def to_str(f, x=None, x_0=0, x_1=1, t=None, h=10, w=80, density=1, char_set="line"): """Represent functions as string frame with a specific character set. which are normed to the range of [0, 1] to :param f: function or list of functions normed to the range of [0, 1] :type f: function or list :param h: number of chars in vertical direction :type h: int :param w: number of chars in horizontal direction :type w: int :param char_set: either "braille" or "block". "braille" uses Unicode Characters in the Braille Patterns Block (fisrt index U+2800, last index U+28FF [CUDB]_) and the "block" uses part of the Unicode Characters in the Block Elements Block (fisrt index U+2580, last index U+259F [CUDB]_). Alias for braille is line and alias for block is histogram :type char_set: str Usage: * case dependent arguments * 1 quasi line plot (braille characters) f = lambda function (lambda x, t=0: ...) x_1 = max x value x_0 = min x value t = time (animation) * 2 histogram (block characters) f = lambda function (lambda x, t=0: ...) x_1 = max x value x_0 = min x value t = time (animation) chart="histogram" * general arguments w = window width in number of chars density = number of data point per pixel (for line chart higher density makes thicker lines) chart = either "line" or "histogram" .. rubric:: Braille Patterns Block * Dots or pixels per character in vertical direction: 6 * Dots or pixels per character in horizontal direction: 2 First dot (bottom left) is the zero (0) position of the function. So, a function value of zero does not mean to have zero dots but one. Example of 3 columns and 3 rows (upside down view of 'normal' braille/drawille position) :: | ^ y axis | | | ,_____,,_____,,_____, 7 + | * *|| * *|| * *| 6 + | * *|| * *|| * *| 5 + | * *|| * *|| * *| 4 + | * *|| * *|| * *| | ;=====;;=====;;=====; 3 + | * *|| * *|| * *| 2 + | * *|| * *|| * *| 1 + | * *|| * *|| * *| 0 + - | * *|| * *|| * *| ---> x axis | ;=====;;=====;;=====; -1 + | * *|| * *|| * *| -2 + | * *|| * *|| * *| -3 + | * *|| * *|| * *| -4 + | * *|| * *|| * *| | ````````````````````` | | `-----+--+---+--+---+--+------------- -2 -1 0 1 2 3 .. rubric:: Block Elements Block * Dots or pixels per character in vertical direction: 8 * Dots or pixels per character in horizontal direction: 1 Example of 3 columns and 3 rows :: | ^ y axis | | | ,_____,,_____,,_____, 15 + | --- || --- || --- | 14 + | --- || --- || --- | 13 + | --- || --- || --- | 12 + | --- || --- || --- | 11 + | --- || --- || --- | 10 + | --- || --- || --- | 9 + | --- || --- || --- | 8 + | --- || --- || --- | | ;=====;;=====;;=====; 7 + | --- || --- || --- | 6 + | --- || --- || --- | 5 + | --- || --- || --- | 4 + | --- || --- || --- | 3 + | --- || --- || --- | 2 + | --- || --- || --- | 1 + | --- || --- || --- | 0 + - | --- || --- || --- | ---> x axis | ;=====;;=====;;=====; -1 + | --- || --- || --- | -2 + | --- || --- || --- | -3 + | --- || --- || --- | -4 + | --- || --- || --- | -5 + | --- || --- || --- | -6 + | --- || --- || --- | -7 + | --- || --- || --- | -8 + | --- || --- || --- | | ````````````````````` | | `------+------+------+--------------- -1 0 1 .. rubric:: References .. [CUDB] `Unicode Database - Blocks <ftp://ftp.unicode.org/Public/UNIDATA/Blocks.txt>`_ .. seealso:: :meth:`pylib.function.transformation` """ # scale function to used chars and dots/pixel in y direction (4 for braille characters): from [0, 1] to [0, chars*4-1] # negate the function because the y axis is pointing downwards: from [0, 1] to [-1, 0] or from [0, chars*4-1] to [-(chars*4-1), 0] # and becasue of the negation shift the function by one dot/pixel, otherwise the function would need another char, because the 0 is on an another char on the "positive side". # devide the time by the number of dots/pixel in x direction (2 for braille characters) window_factor = w/(x_1-x_0) frame = "" if char_set in ["line", "braille"]: pixels_horizontal = 2 pixels_vertical = 4 a = -(h*pixels_vertical-1) b = 1/pixels_horizontal f = transformation(f, a, b, 0, -1) canvas = drawille.Canvas() # divide step width of the sequence by 2 (double density, 2 dots/pixel per char) # multiplicate x by 2 (2 dots/pixel per char) for x_i in seq(x_0*window_factor*pixels_horizontal, x_1*window_factor*pixels_horizontal, 1/density): canvas.set(x_i, f(x_i, t)) #frame = canvas.frame(min_x=a*pixel_per_char, min_y=1, max_x=b*pixel_per_char, max_y=21) frame = canvas.frame() elif char_set in ["histogram", "block"]: pixels_horizontal = 1 pixels_vertical = 8 a = h*pixels_vertical-1 f = transformation(f, a, 1, 0, 0) density = min(density, 1) # density max 1! x = seq(x_0*window_factor, x_1*window_factor, 1/pixels_horizontal/density) f = [f(xi, t) for xi in x] frame = drawblock.histogram(f, x) return frame