Source code for cam.operators.curve_equation_ops

"""Fabex 'curve_cam_equation.py' © 2021, 2022 Alain Pelletier

Operators to create a number of geometric shapes with curves.
"""

from math import pi, sin, cos, sqrt

import numpy as np

import bpy
from bpy.props import (
    EnumProperty,
    FloatProperty,
    IntProperty,
    StringProperty,
)
from bpy.types import Operator

from .. import parametric

from ..utilities.geom_utils import triangle, s_sine


[docs] class CamSineCurve(Operator): """Object Sine""" # by Alain Pelletier april 2021
[docs] bl_idname = "object.sine"
[docs] bl_label = "Periodic Wave"
[docs] bl_options = {"REGISTER", "UNDO", "PRESET"}
# zstring: StringProperty(name="Z equation", description="Equation for z=F(u,v)", default="0.05*sin(2*pi*4*t)" )
[docs] axis: EnumProperty( name="Displacement Axis", items=( ("XY", "Y to displace X axis", "Y constant; X sine displacement"), ("YX", "X to displace Y axis", "X constant; Y sine displacement"), ("ZX", "X to displace Z axis", "X constant; Y sine displacement"), ("ZY", "Y to displace Z axis", "X constant; Y sine displacement"), ), default="ZX", )
[docs] wave: EnumProperty( name="Wave", items=( ("sine", "Sine Wave", "Sine Wave"), ("triangle", "Triangle Wave", "triangle wave"), ("cycloid", "Cycloid", "Sine wave rectification"), ("invcycloid", "Inverse Cycloid", "Sine wave rectification"), ), default="sine", )
[docs] amplitude: FloatProperty( name="Amplitude", default=0.01, min=0, max=10, precision=4, unit="LENGTH", )
[docs] period: FloatProperty( name="Period", default=0.5, min=0.001, max=100, precision=4, unit="LENGTH", )
[docs] beat_period: FloatProperty( name="Beat Period Offset", default=0.0, min=0.0, max=100, precision=4, unit="LENGTH", )
[docs] shift: FloatProperty( name="Phase Shift", default=0, min=-360, max=360, precision=4, step=100, unit="ROTATION", )
[docs] offset: FloatProperty( name="Offset", default=0, min=-1.0, max=1, precision=4, unit="LENGTH", )
[docs] iteration: IntProperty( name="Iteration", default=100, min=50, max=2000, )
[docs] max_t: FloatProperty( name="Wave Ends at X", default=0.5, min=-3.0, max=3, precision=4, unit="LENGTH", )
[docs] min_t: FloatProperty( name="Wave Starts at X", default=0, min=-3.0, max=3, precision=4, unit="LENGTH", )
[docs] wave_distance: FloatProperty( name="Distance Between Multiple Waves", default=0.0, min=0.0, max=100, precision=4, unit="LENGTH", )
[docs] wave_angle_offset: FloatProperty( name="Angle Offset for Multiple Waves", default=pi / 2, min=-200 * pi, max=200 * pi, precision=4, step=100, unit="ROTATION", )
[docs] wave_amount: IntProperty( name="Amount of Multiple Waves", default=1, min=1, max=2000, )
[docs] def execute(self, context): amp = self.amplitude period = self.period beatperiod = self.beat_period offset = self.offset shift = self.shift # z=Asin(B(x+C))+D if self.wave == "sine": zstring = s_sine(amp, period, dc_offset=offset, phase_shift=shift) if self.beat_period != 0: zstring += ( f"+ {s_sine(amp, period+beatperiod, dc_offset=offset, phase_shift=shift)}" ) # build triangle wave from fourier series elif self.wave == "triangle": zstring = f"{round(offset, 6) + triangle(80, period, amp)}" if self.beat_period != 0: zstring += f"+ {triangle(80, period+beatperiod, amp)}" elif self.wave == "cycloid": zstring = f"abs({s_sine(amp, period, dc_offset=offset, phase_shift=shift)})" elif self.wave == "invcycloid": zstring = f"-1 * abs({s_sine(amp, period, dc_offset=offset, phase_shift=shift)})" print(zstring) # make equation from string def e(t): return eval(zstring) # build function to be passed to create parametric curve () def f(t, offset: float = 0.0, angle_offset: float = 0.0): if self.axis == "XY": c = (e(t + angle_offset) + offset, t, 0) elif self.axis == "YX": c = (t, e(t + angle_offset) + offset, 0) elif self.axis == "ZX": c = (t, offset, e(t + angle_offset)) elif self.axis == "ZY": c = (offset, t, e(t + angle_offset)) return c for i in range(self.wave_amount): angle_off = self.wave_angle_offset * period * i / (2 * pi) parametric.create_parametric_curve( f, offset=self.wave_distance * i, min=self.min_t, max=self.max_t, use_cubic=True, iterations=self.iteration, angle_offset=angle_off, ) return {"FINISHED"}
[docs] class CamLissajousCurve(Operator): """Lissajous""" # by Alain Pelletier april 2021
[docs] bl_idname = "object.lissajous"
[docs] bl_label = "Lissajous Figure"
[docs] bl_options = {"REGISTER", "UNDO", "PRESET"}
[docs] amplitude_a: FloatProperty( name="Amplitude A", default=0.1, min=0, max=100, precision=4, unit="LENGTH", )
[docs] wave_a: EnumProperty( name="Wave X", items=(("sine", "Sine Wave", "Sine Wave"), ("triangle", "Triangle Wave", "triangle wave")), default="sine", )
[docs] amplitude_b: FloatProperty( name="Amplitude B", default=0.1, min=0, max=100, precision=4, unit="LENGTH", )
[docs] wave_b: EnumProperty( name="Wave Y", items=(("sine", "Sine Wave", "Sine Wave"), ("triangle", "Triangle Wave", "triangle wave")), default="sine", )
[docs] period_a: FloatProperty( name="Period A", default=1.1, min=0.001, max=100, precision=4, unit="LENGTH", )
[docs] period_b: FloatProperty( name="Period B", default=1.0, min=0.001, max=100, precision=4, unit="LENGTH", )
[docs] period_z: FloatProperty( name="Period Z", default=1.0, min=0.001, max=100, precision=4, unit="LENGTH", )
[docs] amplitude_z: FloatProperty( name="Amplitude Z", default=0.0, min=0, max=100, precision=4, unit="LENGTH", )
[docs] shift: FloatProperty( name="Phase Shift", default=0, min=-360, max=360, precision=4, step=100, unit="ROTATION", )
[docs] iteration: IntProperty( name="Iteration", default=500, min=50, max=10000, )
[docs] max_t: FloatProperty( name="Wave Ends at X", default=11, min=-3.0, max=1000000, precision=4, unit="LENGTH", )
[docs] min_t: FloatProperty( name="Wave Starts at X", default=0, min=-10.0, max=3, precision=4, unit="LENGTH", )
[docs] def execute(self, context): # x=Asin(at+delta ),y=Bsin(bt) if self.wave_a == "sine": xstring = s_sine(self.amplitude_a, self.period_a, phase_shift=self.shift) elif self.wave_a == "triangle": xstring = f"{triangle(100, self.period_a, self.amplitude_a)}" if self.wave_b == "sine": ystring = s_sine(self.amplitude_b, self.period_b) elif self.wave_b == "triangle": ystring = f"{triangle(100, self.period_b, self.amplitude_b)}" zstring = s_sine(self.amplitude_z, self.period_z) # make equation from string def x(t): return eval(xstring) def y(t): return eval(ystring) def z(t): return eval(zstring) print(f"x= {xstring}") print(f"y= {ystring}") # build function to be passed to create parametric curve () def f(t, offset: float = 0.0): c = (x(t), y(t), z(t)) return c parametric.create_parametric_curve( f, offset=0.0, min=self.min_t, max=self.max_t, use_cubic=True, iterations=self.iteration ) return {"FINISHED"}
[docs] class CamHypotrochoidCurve(Operator): """Hypotrochoid""" # by Alain Pelletier april 2021
[docs] bl_idname = "object.hypotrochoid"
[docs] bl_label = "Spirograph Type Figure"
[docs] bl_options = {"REGISTER", "UNDO", "PRESET"}
[docs] typecurve: EnumProperty( name="Type of Curve", items=( ("hypo", "Hypotrochoid", "Inside ring"), ("epi", "Epicycloid", "Outside inner ring"), ), )
[docs] R: FloatProperty( name="Big Circle Radius", default=0.25, min=0.001, max=100, precision=4, unit="LENGTH", )
[docs] r: FloatProperty( name="Small Circle Radius", default=0.18, min=0.0001, max=100, precision=4, unit="LENGTH", )
[docs] d: FloatProperty( name="Distance from Center of Interior Circle", default=0.050, min=0, max=100, precision=4, unit="LENGTH", )
[docs] dip: FloatProperty( name="Variable Depth from Center", default=0.00, min=-100, max=100, precision=4, )
[docs] def execute(self, context): r = round(self.r, 6) R = round(self.R, 6) d = round(self.d, 6) Rmr = round(R - r, 6) # R-r Rpr = round(R + r, 6) # R +r Rpror = round(Rpr / r, 6) # (R+r)/r Rmror = round(Rmr / r, 6) # (R-r)/r maxangle = 2 * pi * ((np.lcm(round(self.R * 1000), round(self.r * 1000)) / (R * 1000))) if self.typecurve == "hypo": xstring = f"{Rmr} * cos(t) + {d} * cos({Rmror} * t)" ystring = f"{Rmr} * sin(t) - {d} * sin({Rmror} * t)" else: xstring = f"{Rpr} * cos(t) - {d} * cos({Rpror} * t)" ystring = f"{Rpr} * sin(t) - {d} * sin({Rpror} * t)" zstring = f"({round(self.dip, 6)} * (sqrt((({xstring})**2) + (({ystring})**2))))" # make equation from string def x(t): return eval(xstring) def y(t): return eval(ystring) def z(t): return eval(zstring) print(f"x= {xstring}") print(f"y= {ystring}") print(f"z= {zstring}") print(f"maxangle {maxangle}") # build function to be passed to create parametric curve () def f(t, offset: float = 0.0): c = (x(t), y(t), z(t)) return c iter = int(maxangle * 10) if iter > 10000: # do not calculate more than 10000 points print("limiting calculations to 10000 points") iter = 10000 parametric.create_parametric_curve( f, offset=0.0, min=0, max=maxangle, use_cubic=True, iterations=iter ) return {"FINISHED"}
[docs] class CamCustomCurve(Operator): """Object Custom Curve""" # by Alain Pelletier april 2021
[docs] bl_idname = "object.customcurve"
[docs] bl_label = "Custom Curve"
[docs] bl_options = {"REGISTER", "UNDO", "PRESET"}
[docs] x_string: StringProperty( name="X Equation", description="Equation x=F(t)", default="t", )
[docs] y_string: StringProperty( name="Y Equation", description="Equation y=F(t)", default="0", )
[docs] z_string: StringProperty( name="Z Equation", description="Equation z=F(t)", default="0.05*sin(2*pi*4*t)", )
[docs] iteration: IntProperty( name="Iteration", default=100, min=50, max=2000, )
[docs] max_t: FloatProperty( name="Wave Ends at X", default=0.5, min=-3.0, max=10, precision=4, unit="LENGTH", )
[docs] min_t: FloatProperty( name="Wave Starts at X", default=0, min=-3.0, max=3, precision=4, unit="LENGTH", )
[docs] def execute(self, context): print("x= " + self.x_string) print("y= " + self.y_string) print("z= " + self.z_string) # make equation from string def ex(t): return eval(self.x_string) def ey(t): return eval(self.y_string) def ez(t): return eval(self.z_string) # build function to be passed to create parametric curve () def f(t, offset: float = 0.0): c = (ex(t), ey(t), ez(t)) return c parametric.create_parametric_curve( f, offset=0.0, min=self.min_t, max=self.max_t, use_cubic=True, iterations=self.iteration ) return {"FINISHED"}