Skip to content

Instantly share code, notes, and snippets.

@randombrein
Last active October 23, 2016 21:40
Show Gist options
  • Save randombrein/c53d9ec4ac07c54896688ff668125562 to your computer and use it in GitHub Desktop.
Save randombrein/c53d9ec4ac07c54896688ff668125562 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
"""
Monotonic increasing sinusoidal curve with same entrance/exit angle
"""
import numpy as np
import pylab
def discrt_tan(x0, y0, x1, y1):
""" discrete tangent function for tuple of points """
return np.divide(np.subtract(y1,y0), np.subtract(x1,x0))
def curve(xs, size, mid_angle=0.0):
""" monotonic raising curve function with sinusoidal accent.
Function for a monotonic raising curve with same entrance/exit angle.
Sinusoidal setup for *monotonic increasing* constraints angles in following fashion;
- mid_angle must be between [0..pi/4) - approaching pi/4 yields straight line.
- generated entrance/exit angles are between [45..~63.43] degrees
The curve is expresses as;
y = x + [sin(2PI * x / size)] / accent
Args:
xs (array like): input value(s)
size: size range of the input/output values
mid_angle: middle angle of the sinusoidal in radians
Returns:
array-like curve value.
Raises:
`ValueError` for params. that out of range;
mid_angle - must be between [0..pi/4)
xs - must be in range between [0..size]
Todo:
- `accent` does not well-define middle angle parameter.
- if curve will not be limited in size range, derivation will be step-wise continuous.
More calculation can be made for continuous derivation. (tangents at 0 and size)
"""
if mid_angle<0 or mid_angle>=np.pi/4:
raise ValueError("mid-angle must be between [0..pi/4)")
if np.any(xs < 0) or np.any(xs > size):
raise ValueError("xs must be between [0..size]")
accent = 2*np.pi/size * np.tan(np.pi/4+mid_angle) # TODO: not well-defined mid-angle param.
return xs + np.sin(2*np.pi*xs/size) / accent
if __name__ == "__main__":
"""
Test and plot `curve` function for various values and mid_angle degrees
"""
eps = 1e-11
#eps = np.finfo(float).eps
size = 1
print "### testing values..."
print "x= {:.15f}, y={:.15f}".format(size, curve(size, size))
print "x= {:.15f}, y={:.15f}".format(np.subtract(size,eps), curve(np.subtract(size,eps), size))
print "x= {:.15f}, y={:.15f}".format(size/2.0, curve(size/2.0, size))
# angles at 0, mid, size
mids = [0, np.pi/128, np.pi/64, np.pi/32, np.pi/16, np.pi/8, np.pi/6, np.pi/4-eps]
max_err = 0.0
params = {'legend.fontsize': 'small',
'axes.labelsize': 'small',
'axes.titlesize':'small',
'xtick.labelsize':'small',
'ytick.labelsize':'small'}
pylab.rcParams.update(params)
colc = 2
rowc = (len(mids)+1)/colc
f, axarr = pylab.subplots(rowc, colc)
print "\n### testing angles..."
for i,mid in enumerate(mids):
print "mid (input) = {:.15f}".format(np.rad2deg(mid))
t = discrt_tan(size/2.0, curve(size/2.0, size, mid),
np.add(size/2.0,eps), curve(np.add(size/2.0,eps), size, mid))
print "angle at mid = {:.15f}".format(np.rad2deg(np.arctan(t)))
t = discrt_tan(0, curve(0, size, mid),
eps, curve(eps, size, mid))
print "angle at 0 = {:.15f}".format(np.rad2deg(np.arctan(t)))
t1 = discrt_tan(size, curve(size, size, mid),
np.subtract(size,eps), curve(np.subtract(size,eps), size, mid))
print "angle at size = {:.15f}".format(np.rad2deg(np.arctan(t1)))
# error
err = np.abs(np.subtract(np.rad2deg(np.arctan(t1)), np.rad2deg(np.arctan(t))))
max_err = np.maximum(max_err, err)
print "error = {:.15f}\n".format(err)
# plot
xs = np.linspace(0, size, 100)
ys = curve(xs, size, mid)
row = i/colc
col = i - row*colc
axarr[row, col].plot(xs, ys)
axarr[row, col].set_title('mid_angle={:.4f} rad.'.format(mid))
axarr[row, col].text(0.95, 0.15, 'alpha={:.6f} deg.'.format(np.rad2deg(np.arctan(t))),
verticalalignment='bottom', horizontalalignment='right',
transform=axarr[row, col].transAxes,
color='green', fontsize=10)
axarr[row, col].text(0.95, 0.03, 'err={:.6f} deg.'.format(err),
verticalalignment='bottom', horizontalalignment='right',
transform=axarr[row, col].transAxes,
color='red', fontsize=10)
print"Max. Error = {:.15f}".format(max_err)
pylab.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment