开发者

creating deformations on a sphere surface (using python?)

开发者 https://www.devze.com 2023-02-11 13:56 出处:网络
Hi :) i have the following python code that generates points lying on a sphere\'s surface from math import sin, cos, pi

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
0

精彩评论

暂无评论...
验证码 换一张
取 消