I'm trying to do simple simulation of ideal gas according to Clapeyron equation `pv=nkbT' using Metropolis Monte Carlo algorithm.This is very simple example,where I consider molecules in 2D with no interactions with each other and energy is equeal to E=pV wher V is area of circle containing all molecules.
My problem is that after very few monte carlo steps volume of my gas goes always to almost zero,no matter how many molecules or pressure I put.I can't figure out if I have bug in my code,or it is becouse I don't have any molecules interactions.
All help will be much appriciated,here is my code
My results are shown in plot bellow,x-axis are monte carlo steps and y-axis is volume,i would expected as a result some none zero constant value of volume after greater number of steps.
import numpy as np
import random
import matplotlib.pyplot as plt
def centroid(arr):
length = arr.shape[0]
sum_x = sum([i.x for i in arr])
sum_y = sum([i.y for i in arr])
return sum_x/length, sum_y/length
class Molecule:
def __init__(self, xpos, ypos):
self.pos = (xpos, ypos)
self.x = xpos
self.y = ypos
class IdealGas:
# CONSTANTS
def __init__(self, n,full_radius, pressure, T, number_of_runs):
gas = []
for i in range(0, n):
gas.append(Molecule(random.random() * full_radius,
random.random() * full_radius))
self.gas = np.array(gas)
self.center = centroid(self.gas)
self.small_radius = full_radius/4.
self.pressure = pressure
self.kbT = 9.36E-18 * T
self.n = n
self.number_of_runs = number_of_runs
def update_pos(self):
random_molecule = np.random.choice(self.gas)
old_state_x = random_molecule.x
old_state_y = random_molecule.y
old_radius = np.linalg.norm(np.array([old_state_x,old_state_y])-np.array([self.center[0],self.center[1]]))
energy_old = np.pi * self.pressure * old_radius**2
random_molecule.x = old_state_x + (random.random() * self.small_radius) * np.random.choice([-1, 1])
random_molecule.y = old_state_y + (random.random() * self.small_radius) * np.random.choice([-1, 1])
new_radius = np.linalg.norm(np.array([random_molecule.x,random_molecule.y])-np.array([self.center[0],self.center[1]]))
energy_new = np.pi * self.pressure * new_radius**2
if energy_new - energy_old <= 0.0:
return random_molecule
elif np.exp((-1.0 * (energy_new - energy_old)) / self.kbT) > random.random():
return random_molecule
else:
random_molecule.x = old_state_x
random_molecule.y = old_state_y
return random_molecule
def monte_carlo_step(self):
gas = []
for molecule in range(0, self.n):
gas.append(self.update_pos())
self.gas = np.array(gas)
#self.center = centroid(self.gas)
return self.gas
def simulation(self):
volume = []
for run in range(self.number_of_runs):
step_gas = self.monte_carlo_step()
step_centroid = centroid(step_gas)
step_radius = max([np.linalg.norm(np.array([gas.x,gas.y])-np.array([step_centroid[0],step_centroid[1]]))
for gas in step_gas])
step_volume = np.pi * step_radius**2
volume.append(step_volume)
return volume
Gas = IdealGas(500, 50, 1000, 300, 100)
vol = Gas.simulation()
plt.plot(vol)
plt.show()
You only allow your molecules to move if the new radius is inferior to the old radius:
if energy_new - energy_old <= 0.0:
is equivalent to:
np.pi * self.pressure * new_radius**2 <= np.pi * self.pressure * old_radius**2
that is:
abs(new_radius) <= abs(old_radius)
So all molecules goes to the centroïd.
To me your hypothesis are too strong: you fix temperature, pressure and number of molecules. According to the ideal gas equation, it means volumne v=nRT/p is constant too. If the volume can change, then pressure or temperature has to change. In your simulation, allowing pressure to change would mean that the product of pressure and volume is constant, so that energy is constant, so molecules can move freely in an arbitraty large volume.
By the way I think molecules should be initialized with:
(random.random() - 0.5) * full_radius
so that the occupy all the plane around zero.
Related
I am using the arcpy module for arcGIS to implement a peano curve algorithm and provide each object in the GIS Project with a spatial order value.
I have currently defined the Peano curve but need to write cursor functions that will compute and add the outputs to the new field after calling the Peano.
This is the code that I have so far. Areas related to the question are in bold. Thank you!
#return the fractional part from a double number
def GetFractionalPart(dbl):
return dbl - math.floor(dbl)
#Return the peano curve coordinate for a given x, y value
def Peano(x,y,k):
if (k==0 or (x==1 and y==1):
return 0.5
if x <= 0.5:
if y <= 0.5:
quad = 0
elif y <= 0.5:
quad = 3
else:
quad = 2
subpos = Peano(2 * abs(x - 0.5), 2 * abs(y - 0.5), k-1)
if (quad == 1 or quad == 3):
subpos = 1 - subpos
return GetFractionalPart((quad + subpos - 0.5)/4.0)
#Import modules and create geoprocessor
import arcpy
arcpy.env.OverwriteOutput = True
#Prepare two inputs as parameters
inp_fc = arcpy.GetParameterAsText(0)
PeanoOrder_fld = arcpy.GetParameterAsText(1)
#Add the double field
arcpy.AddField_management(inp_fc, PeanoOrder_fld, "DOUBLE")
#Get the extent for the feature class
desc = arcpy.Describe(inp_fc)
extent = desc.Extent
xmin = extent.XMin
ymin = extent.YMin
xmax = extent.XMax
ymax = extent.YMax
#Compute constants to scale the coordinates
dx = xmax - xmin
dy = ymax - ymin
if dx >= dy:
offsetx = 0.0
offsety = (1.0-dy/dx)/2.0
scale = dx
else:
offsetx = (1.0 - dx/dy)/2.0
offsety = 0.0
scale = dy
**#Get each object and compute its Peano curve spatial order
#Create an update cursor
rows = arcpy.UpdateCursor(inp_fc)
rows.___?___
row = rows.next( )**
#get the X,Y coordinate for each feature
#If a polygon use centroid, If a point use point itself
while row:
if desc.ShapeType.lower() in ["polyline", "polygon"]:
pnt = row.shape.centroid
else:
pnt = row.shape.getPart(0)
#Normalization
unitx = (pnt.X - xmin) / scale + offsetx
unity = (pnt.Y - ymin) / scale + offset
**#Call the Peano Function and add to attribute field
peanoPos = Peano(unitx, unity, 32)
row.__?__
rows.__?__
row =__?___
del row, rows**
TLDR
I'm using scipy.solve_ivp to solve phi''(t) = a(t) - b * (phi'(t))^2 and I want to change the value of b whenever the sign of phi'(t) changes
What I've Tried
More specifically, the function represents an equation of torques on a wing with drag (not really important for the matter but still) and the entire solver is encapsulated in the following Solver class.
This is a runnable code, but the most important functions are sequential_solver, phi_dot_zero_crossing_event and phi_derivatives
# some constants to run the code ...
NUM_SAMPLES = 100 # every time window has this many samples
MASS = 1
WING_LENGTH = 0.07
AERODYNAMIC_CENTER = 0.7 * WING_LENGTH
GYRATION_RADIUS = 0.6 * WING_LENGTH
MoI = MASS * GYRATION_RADIUS ** 2
AIR_DENSITY = 1.2
WING_AREA = 0.5 * WING_LENGTH * (0.5 * WING_LENGTH) * np.pi # 1/2 ellipse with minor radios ~ 1/2 major = length/2
C_D_MAX = 3.4
C_D_0 = 0.4
C_L_MAX = 1.8
class Solver:
def __init__(self) -> None:
self._alpha = 45
def get_c_drag(self) -> float:
"""
calculates the drag coefficient based on the angle of attack
"""
return (C_D_MAX + C_D_0) / 2 - (C_D_MAX - C_D_0) / 2 * np.cos(2 * self._alpha)
def get_c_lift(self) -> float:
"""
calculates the lift coefficient based on the angle of attack
"""
return C_L_MAX * np.sin(2 * self._alpha)
def tau_z(self, t: float) -> np.float:
"""
the moment we apply using the motors
"""
return np.sin(2 * np.pi * t)
def tau_drag(self, phi_dot):
"""
the drag moment
"""
return 0.5 * AIR_DENSITY * WING_AREA * self.get_c_drag() * (GYRATION_RADIUS ** 2) * (phi_dot ** 2)
def phi_derivatives(self, t, y):
"""
A function that defines the ODE that is to be solved: I * phi_ddot = tau_z - tau_drag.
We think of y as a vector y = [phi,phi_dot]. the ode solves dy/dt = f(y,t)
:return:
"""
phi, phi_dot = y[0], y[1]
dy_dt = [phi_dot, (self.tau_z(t) - self.tau_drag(phi_dot)) / MoI] # d[phi,phi_dot]]/dt = [phi_dot,phi_ddot]
return dy_dt
def phi_dot_zero_crossing_event(self, t, y):
return y[1]
def sequential_solver(self, use_windows):
"""
solves the ODE
:return:
"""
phi_0 = 2e-3
phi_dot_0 = 0
prev_t = 0
start_t, end_t, delta_t = 1e-1, 5, 1e-1
inner_delta_t = delta_t / NUM_SAMPLES
time = np.arange(start_t, end_t, inner_delta_t)
sol = solve_ivp(self.phi_derivatives, t_span=(start_t, end_t), y0=[phi_0, phi_dot_0], t_eval=time,
events=self.phi_dot_zero_crossing_event)
phi, phi_dot = sol.y
_, phi_ddot = self.phi_derivatives(time, [phi, phi_dot])
So what's the problem?
my question is how, given phi_dot_zero_crossing_event, I can change the value of self._alpha mid-solving? I currently only track the event of zero crossing, but I don't know how to use this event to set the value of self._alpha (if phi_dot>0 I need self._alpha = +45 and self._alpha=-45 otherwise)
I am implementing the Kalman filter and I have a data set in mf4 format for the measurements. I have 6 measurements and each of them contains 1000 values, I am implementing a loop of 100 for the Kalman filter but as I am new to Kalman filter and python so I am not sure that the procedure I am following is correct or not. My question is that when I run the loop for the measurements in the update step for prediction of next state then all these 1000 values will be used and if it is so then the result might not be appropriate. Kindly suggest any solution, please.
My code is:
class KF:
# initialization of variables
def __init__(self, a,b,c,d,e,f,g,h,i):
# do initizatoin steps
#x = [a,b,c,d,e,f]
# a = yaw rate, b = float angle, c = offset between road and vehicle, d = curvature, e = angle
# b/w ego vehicles direction of motion and the road curvature tangent, f = lane width
def predict(self, dt, u):
# using equations do the predict step
# u = [g,h,i]
# g = front angle wheel, h = longitudinal velocity, i = acceleration
# dt = time step
def update(self, y):
# using equations of update step
# y1 = yaw rate, y2 = acceleration, y3 = curvature, y4 = angle b/w vehicle axis and lane,
# y5 = lane width, y6 = offset b/w road and vehicle
# Main code:
kf = KF(a = random.random(), b = random.random(), c = random.random(), d = random.random(), e= random.random(), f = random.random(), g= random.random(), h= random.random(), I = random.random())
variance = 0.1 ** 2
DT = 0.1
number = 100
for step in range(number):
kf.predict(dt=DT, tmp=np.array([kf.g,kf.h,kf.i]))
cc = np.empty(6, dtype=object)
meas_y1 = efficient.get('Car.Yaw') + np.random.randn() * np.sqrt(variance)
meas_y2 = efficient.get('Car.ay_1') + np.random.randn() * np.sqrt(variance)
meas_y3 = efficient.get('Car.Curv') + np.random.randn() * np.sqrt(variance)
meas_y4 = efficient.get('Car.LongSlope') + np.random.randn() * np.sqrt(variance)
meas_y5 = efficient.get('Car.Width') + np.random.randn() * np.sqrt(variance)
meas_y6 = efficient.get('Car.tMidLane') + np.random.randn() * np.sqrt(variance)
cc[:]= [meas_y1, meas_y2, meas_y3, meas_y4, meas_y5, meas_y6]
kf.update(y= cc)
I'm writing the prorgram on python that can approximate time series by sin waves.
The program uses DFT to find sin waves, after that it chooses sin waves with biggest amplitudes.
Here's my code:
__author__ = 'FATVVS'
import math
# Wave - (amplitude,frequency,phase)
# This class was created to sort sin waves:
# - by anplitude( set freq_sort=False)
# - by frequency (set freq_sort=True)
class Wave:
#flag for choosing sort mode:
# False-sort by amplitude
# True-by frequency
freq_sort = False
def __init__(self, amp, freq, phase):
self.freq = freq #frequency
self.amp = amp #amplitude
self.phase = phase
def __lt__(self, other):
if self.freq_sort:
return self.freq < other.freq
else:
return self.amp < other.amp
def __gt__(self, other):
if self.freq_sort:
return self.freq > other.freq
else:
return self.amp > other.amp
def __le__(self, other):
if self.freq_sort:
return self.freq <= other.freq
else:
return self.amp <= other.amp
def __ge__(self, other):
if self.freq_sort:
return self.freq >= other.freq
else:
return self.amp >= other.amp
def __str__(self):
s = "(amp=" + str(self.amp) + ",frq=" + str(self.freq) + ",phase=" + str(self.phase) + ")"
return s
def __repr__(self):
return self.__str__()
#Discrete Fourier Transform
def dft(series: list):
n = len(series)
m = int(n / 2)
real = [0 for _ in range(n)]
imag = [0 for _ in range(n)]
amplitude = []
phase = []
angle_const = 2 * math.pi / n
for w in range(m):
a = w * angle_const
for t in range(n):
real[w] += series[t] * math.cos(a * t)
imag[w] += series[t] * math.sin(a * t)
amplitude.append(math.sqrt(real[w] * real[w] + imag[w] * imag[w]) / n)
phase.append(math.atan(imag[w] / real[w]))
return amplitude, phase
#extract waves from time series
# series - time series
# num - number of waves
def get_waves(series: list, num):
amp, phase = dft(series)
m = len(amp)
waves = []
for i in range(m):
waves.append(Wave(amp[i], 2 * math.pi * i / m, phase[i]))
waves.sort()
waves.reverse()
waves = waves[0:num]#extract best waves
print("the program found the next %s sin waves:"%(num))
print(waves)#print best waves
return waves
#approximation by sin waves
#series - time series
#num- number of sin waves
def sin_waves_appr(series: list, num):
n = len(series)
freq = get_waves(series, num)
m = len(freq)
model = []
for i in range(n):
summ = 0
for j in range(m): #sum by sin waves
summ += freq[j].amp * math.sin(freq[j].freq * i + freq[j].phase)
model.append(summ)
return model
if __name__ == '__main__':
import matplotlib.pyplot as plt
N = 500 # length of time series
num = 2 # number of sin wawes, that we want to find
#y - generate time series
y = [2 * math.sin(0.05 * t + 0.5) + 0.5 * math.sin(0.2 * t + 1.5) for t in range(N)]
model = sin_waves_appr(y, num) #generate approximation model
## ------------------plotting-----------------
plt.figure(1)
# plotting of time series and his approximation model
plt.subplot(211)
h_signal, = plt.plot(y, label='source timeseries')
h_model, = plt.plot(model, label='model', linestyle='--')
plt.legend(handles=[h_signal, h_model])
plt.grid()
# plotting of spectre
amp, _ = dft(y)
xaxis = [2*math.pi*i / N for i in range(len(amp))]
plt.subplot(212)
h_freq, = plt.plot(xaxis, amp, label='spectre')
plt.legend(handles=[h_freq])
plt.grid()
plt.show()
But I've got a strange result:
In the program I've created a time series from two sin waves:
y = [2 * math.sin(0.05 * t + 0.5) + 0.5 * math.sin(0.2 * t + 1.5) for t in range(N)]
And my program found wrong parameters of the sin waves:
the program found the next 2 sin waves:
[(amp=0.9998029885151699,frq=0.10053096491487339,phase=1.1411803525843616), (amp=0.24800925225626422,frq=0.40212385965949354,phase=0.346757128184013)]
I suppuse, that my problem is wrong scaling of wave parameters, but I'm not sure.
There're two places, where the program does scaling. The first place is creating of waves:
for i in range(m):
waves.append(Wave(amp[i], 2 * math.pi * i / m, phase[i]))
And the second place is sclaling of the x-axis:
xaxis = [2*math.pi*i / N for i in range(len(amp))]
But my suppose may be wrong. I've tried to change scaling many times, and it haven't solved my problem.
What may be wrong with the code?
So, these lines I believe are wrong:
for t in range(n):
real[w] += series[t] * math.cos(a * t)
imag[w] += series[t] * math.sin(a * t)
amplitude.append(math.sqrt(real[w] * real[w] + imag[w] * imag[w]) / n)
phase.append(math.atan(imag[w] / real[w]))
I believe it should be dividing by m instead of n, since you are only working with computing half the points. That will fix the amplitude problem. Also, the computation of imag[w] is missing a negative sign. Taking into account the atan2 fix, it would look like:
for t in range(n):
real[w] += series[t] * math.cos(a * t)
imag[w] += -1 * series[t] * math.sin(a * t)
amplitude.append(math.sqrt(real[w] * real[w] + imag[w] * imag[w]) / m)
phase.append(math.atan2(imag[w], real[w]))
The next one is here:
for i in range(m):
waves.append(Wave(amp[i], 2 * math.pi * i / m, phase[i]))
The divide by m is not right. amp has only half the points it should, so using the length of amp isn't right here. It should be:
for i in range(m):
waves.append(Wave(amp[i], 2 * math.pi * i / (m * 2), phase[i]))
Finally, your model reconstruction has a problem:
for j in range(m): #sum by sin waves
summ += freq[j].amp * math.sin(freq[j].freq * i + freq[j].phase)
It should use cosine instead (sine introduces a phase shift):
for j in range(m): #sum by cos waves
summ += freq[j].amp * math.cos(freq[j].freq * i + freq[j].phase)
When I fix all of that, the model and the DFT both make sense:
Hi :)
i have the following python code that generates points lying on a sphere's surface
from math import sin, cos, pi
toRad = pi / 180
ox = 10
oy = -10
oz = 50
radius = 10.0
radBump = 3.0
angleMin = 0
angleMax = 360
angleOffset = angleMin * toRad
angleRange = (angleMax - angleMin) * toRad
steps = 48
angleStep = angleRange / steps
latMin = 0
latMax = 180
latOffset = latMin * toRad
if (latOffset < 0):
latOffset = 0;
latRange = (latMax - latMin) * toRad
if (latRange > pi):
latRange = pi - latOffset;
latSteps = 48
latAngleStep = latRange / latSteps
for lat in range(0, latSteps):
ang = lat * latAngleStep + latOffset
z = cos(ang) * radius + oz
radMod = sin(ang) * radius
for a in range(0, steps):
x = sin(a * angleStep + angleOffset) * radMod + ox
y = cos(a * angleStep + angleOffset) * radMod + oy
print "%f %f %f"%(x,y,z)
after that i plot the points with gnuplot using splot 'datafile'
can you give any hints on how to create deformations on that sphere?
like "mountains" or "spikes" on it?
(something like the openbsd logo ;) : https://https.openbsd.org/images/tshirt-23.gif )
i know it is a trivial question :( but thanks for your time :)
DsP
The approach that springs to my mind, especially with the way you compute a set of points that are not explicitly connected, is to find where the point goes on the sphere's surface, then move it by a distance and direction determined by a set of control points. The control points could have smaller effects the further away they are. For example:
# we have already computed a points position on the sphere, and
# called it x,y,z
for p in controlPoints:
dx = p.x - x
dy = p.y - y
dz = p.z - z
xDisplace += 1/(dx*dx)
yDisplace += 1/(dy*dy)
zDisplace += 1/(dz*dz) # using distance^2 displacement
x += xDisplace
y += yDisplace
z += zDisplace
By changing the control points you can alter the sphere's shape
By changing the movement function, you can alter the way the points shape the sphere
You could get really tricky and have different functions for different points:
# we have already computed a points position on the sphere, and
# called it x,y,z
for p in controlPoints:
xDisplace += p.displacementFunction(x)
yDisplace += p.displacementFunction(y)
zDisplace += p.displacementFunction(z)
x += xDisplace
y += yDisplace
z += zDisplace
If you do not want all control points affecting every point in the sphere, just build that into the displacement function.
How's this?
from math import sin, cos, pi, radians, ceil
import itertools
try:
rng = xrange # Python 2.x
except NameError:
rng = range # Python 3.x
# for the following calculations,
# - all angles are in radians (unless otherwise specified)
# - latitude is in [-pi/2..pi/2]
# - longitude is in [-pi..pi)
MIN_LAT = -pi/2 # South Pole
MAX_LAT = pi/2 # North Pole
MIN_LON = -pi # Far West
MAX_LON = pi # Far East
def floatRange(start, end=None, step=1.0):
"Floating-point range generator"
start += 0.0 # cast to float
if end is None:
end = start
start = 0.0
steps = int(ceil((end-start)/step))
return (start + k*step for k in rng(0, steps+1))
def patch2d(xmin, xmax, ymin, ymax, step=1.0):
"2d rectangular grid generator"
if xmin>xmax:
xmin,xmax = xmax,xmin
xrange = floatRange(xmin, xmax, step)
if ymin>ymax:
ymin,ymax = ymax,ymin
yrange = floatRange(ymin, ymax, step)
return itertools.product(xrange, yrange)
def patch2d_to_3d(xyIter, zFn):
"Convert 2d field to 2.5d height-field"
mapFn = lambda a: (a[0], a[1], zFn(a[0],a[1]))
return itertools.imap(mapFn, xyIter)
#
# Representation conversion functions
#
def to_spherical(lon, lat, rad):
"Map from spherical to spherical coordinates (identity function)"
return lon, lat, rad
def to_cylindrical(lon, lat, rad):
"Map from spherical to cylindrical coordinates"
# angle, z, radius
return lon, rad*sin(lat), rad*cos(lat)
def to_cartesian(lon, lat, rad):
"Map from spherical to Cartesian coordinates"
# x, y, z
cos_lat = cos(lat)
return rad*cos_lat*cos(lon), rad*cos_lat*sin(lon), rad*sin(lat)
def bumpySphere(gridSize, radiusFn, outConv):
lonlat = patch2d(MIN_LON, MAX_LON, MIN_LAT, MAX_LAT, gridSize)
return list(outConv(*lonlatrad) for lonlatrad in patch2d_to_3d(lonlat, radiusFn))
# make a plain sphere of radius 10
sphere = bumpySphere(radians(5.0), lambda x,y: 10.0, to_cartesian)
# spiky-star-function maker
def starFnMaker(xWidth, xOffset, yWidth, yOffset, minRad, maxRad):
# make a spiky-star function:
# longitudinal and latitudinal triangular waveforms,
# joined as boolean intersection,
# resulting in a grid of positive square pyramids
def starFn(x, y, xWidth=xWidth, xOffset=xOffset, yWidth=yWidth, yOffset=yOffset, minRad=minRad, maxRad=maxRad):
xo = ((x-xOffset)/float(xWidth)) % 1.0 # xo in [0.0..1.0), progress across a single pattern-repetition
xh = 2 * min(xo, 1.0-xo) # height at xo in [0.0..1.0]
xHeight = minRad + xh*(maxRad-minRad)
yo = ((y-yOffset)/float(yWidth)) % 1.0
yh = 2 * min(yo, 1.0-yo)
yHeight = minRad + yh*(maxRad-minRad)
return min(xHeight, yHeight)
return starFn
# parameters to spike-star-function maker
width = 2*pi
horDivs = 20 # number of waveforms longitudinally
horShift = 0.0 # longitudinal offset in [0.0..1.0) of a wave
height = pi
verDivs = 10
verShift = 0.5 # leave spikes at the poles
minRad = 10.0
maxRad = 15.0
deathstarFn = starFnMaker(width/horDivs, width*horShift/horDivs, height/verDivs, height*verShift/verDivs, minRad, maxRad)
deathstar = bumpySphere(radians(2.0), deathstarFn, to_cartesian)
so i finally created the deformation using a set of control points that "pull" the spherical
surface. it is heavilly OO and ugly though ;)
thanks for all the help !!!
to use it > afile and with gnuplot : splot 'afile' w l
DsP
from math import sin, cos, pi ,sqrt,exp
class Point:
"""a 3d point class"""
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "%f %f %f\n"%(self.x,self.y,self.z)
def __str__(self):
return "point centered: %f %f %f\n"%(self.x,self.y,self.z)
def distance(self,b):
return sqrt((self.x - b.x)**2 +(self.y - b.y)**2 +(self.z -b.z)**2)
def displaceTowards(self,b):
self.x
class ControlPoint(Point):
"""a control point that deforms positions of other points"""
def __init__(self,p):
Point.__init__(self,p.x,p.y,p.z)
self.deformspoints=[]
def deforms(self,p):
self.deformspoints.append(p)
def deformothers(self):
self.deformspoints.sort()
#print self.deformspoints
for i in range(0,len(self.deformspoints)):
self.deformspoints[i].x += (self.x - self.deformspoints[i].x)/2
self.deformspoints[i].y += (self.y - self.deformspoints[i].y)/2
self.deformspoints[i].z += (self.z - self.deformspoints[i].z)/2
class Sphere:
"""returns points on a sphere"""
def __init__(self,radius,angleMin,angleMax,latMin,latMax,discrStep,ox,oy,oz):
toRad = pi/180
self.ox=ox
self.oy=oy
self.oz=oz
self.radius=radius
self.angleMin=angleMin
self.angleMax=angleMax
self.latMin=latMin
self.latMax=latMax
self.discrStep=discrStep
self.angleRange = (self.angleMax - self.angleMin)*toRad
self.angleOffset = self.angleMin*toRad
self.angleStep = self.angleRange / self.discrStep
self.latOffset = self.latMin*toRad
self.latRange = (self.latMax - self.latMin) * toRad
self.latAngleStep = self.latRange / self.discrStep
if(self.latOffset <0):
self.latOffset = 0
if(self.latRange > pi):
self.latRange = pi - latOffset
def CartesianPoints(self):
PointList = []
for lat in range(0,self.discrStep):
ang = lat * self.latAngleStep + self.latOffset
z = cos(ang) * self.radius + self.oz
radMod = sin(ang)*self.radius
for a in range(0,self.discrStep):
x = sin(a*self.angleStep+self.angleOffset)*radMod+self.ox
y = cos(a*self.angleStep+self.angleOffset)*radMod+self.oy
PointList.append(Point(x,y,z))
return PointList
mysphere = Sphere(10.0,0,360,0,180,50,10,10,10)
mylist = mysphere.CartesianPoints()
cpoints = [ControlPoint(Point(0.0,0.0,0.0)),ControlPoint(Point(20.0,0.0,0.0))]
deforpoints=[]
for cp in cpoints:
for p in mylist:
if(p.distance(cp) < 15.0):
cp.deforms(p)
"""print "cp ",cp,"deforms:"
for dp in cp.deformspoints:
print dp ,"at distance", dp.distance(cp)"""
cp.deformothers()
out= mylist.__repr__()
s = out.replace(","," ")
print s