在球面上创建变形(使用Python?)
你好! :)
我有一段Python代码,可以生成一些点,这些点分布在一个球的表面上。
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)
然后我用gnuplot这个工具,把这些点画出来,命令是splot 'datafile'。
你能给我一些建议,怎么在这个球上创建一些变形吗?比如说像“山”或者“尖刺”之类的?
就像这个OpenBSD的logo一样;: https://https.openbsd.org/images/tshirt-23.gif
我知道这个问题可能很简单 :( 不过还是谢谢你花时间来帮我 :)
DsP
3 个回答
0
我终于用一组控制点创建了变形效果,这些控制点可以“拉扯”球面。不过这个代码结构很复杂,看起来也不太好;)
非常感谢大家的帮助!!!
使用方法是:把文件命名为 afile,然后用 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
2
我想到的一个方法,特别是考虑到你计算的一组没有明确连接的点,就是先找出这些点在球面上的位置,然后根据一组控制点的方向和距离来移动这些点。控制点离得越远,它们的影响可能就越小。例如:
# 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
通过改变控制点,你可以改变球的形状;通过改变移动的方式,你可以改变这些点如何影响球的形状;你甚至可以让不同的点使用不同的移动方式:
# 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
如果你不想让所有的控制点都影响球上的每一个点,只需把这个需求融入到移动的计算中即可。
1
这个怎么样?
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)