SeaShell Mesh from point array? extrude along path?

I have and algorithm that generates seashell geometry in Python. I’d like to rewrite it in three.js and generate many different types of shells on the web.

I’ve searched this forum and Stackoverflow and am still not sure. Can Three.js create a complex mesh from a generative point array? I’ve looked at BufferGeometry and it doesn’t look like it can, but I’m not sure.

My algorithm generates points from curves, (“cross sections”, “ellipses”, “ribs”) along a spline. It’s kind of like extruding a shape along a path, except the shapes increase in size and points the further they get along down the path. I’ve looked into the path extruding methods and they don’t seem like they’d work.

I’m three.js newbie and will welcome any advice on what tools, methods or examples I should be looking at and trying. Thanks!

Related:

Another option is to take a look at this topic: Addon. Produces almost infinite many time-varying geometries with functions
And contact @hofk :slight_smile:

1 Like

The addon Addon to create special / extended geometries also shows how to create different more complex geometries. If you have the formulas/algorithm, it’s just a little work to get the result. :slightly_smiling_face:

see GitHub hofk (Klaus Hoffmeister) · GitHub

from THREEp: examples THREEp

0015_clamshell

2 Likes

Hello! I’ll give Addon a try and post back as soon as I have a question or something to show. Thanks!

Hello,

I am a javascript newbie and I cannot figure out how to start translating my python seashell code into three.js

I looked at these amazing examples, but they all seem to be manipulations/squeezing of sphere geometry:
https://hofk.de/main/threejs/sandboxthreep/examplesTHREEp%20r90.html

My algorithm, written in python, generates shells from points derived from shapes extruded along a spline. For my purposes, it is important that the variables in this algorithm can be changed. Can three.js can do this sort of thing at all? It seems like the most complicated example models I’ve seen are imported from 3D programs.


Here is my python code:

class Shell:

def __init__(self, n, m, turns, s, i, D, alpha_, beta, phi, mu, omega, A, a, b, L, P, W1, W2, N ):
    self.n = n            # Resolution of spiral
    self.m = m            # Resolution of spline
    self.turns = turns    # Number of turns of spiral
    self.s = s            # Angle from b to a axis in spiral 
                          # changing s to -1 makes spline larger, but -50 or 50 changes nothing. sin effect I think. 
    self.i = i            # Clipping? of spiral? 
                          # values of less than one show no change, values of 10+ clip spiral into shorter coils. At 50, the spiral disappears. The spline does not change. 
                          # Changing j values shows a similar effect. 
    
    self.D = D            # Direction of coiling, 1 dextral, -1 sistral 
    self.alpha_ = alpha_  # Equiangular angle of spiral 
    self.beta = beta      # Enlarging angle of spiral, breadth
    self.phi = phi        # Spline rotation about spiral point
    self.mu = mu          # Spline rotation about spiral point
    self.omega = omega    # Spline rotation about spiral point
    self.A = A            # Size of spiral aperture
    self.a = a            # Minor spline length
    self.b = b            # Major spline length
    self.L = L            # Height of nodule
    self.P = P            # Angle of nodule in regards to spline
    self.W1 = W1          # Length of nodule in regards to spline
    self.W2 = W2          # Length of nodule along spiral 
    self.N = N            # Number of nodules along a complete revolution of theta. 


def generateShell(self):  
    """Generate the curve """
    shell_matrix =[]
    spiral_matrix =[]
    prevSpline =[]
    while self.i < self.n: 
        theta = float(map(self.i, 0, self.n, 0, self.turns))
        rad =   float(exp(theta * cos(self.alpha_) / sin(self.alpha_)) )
        
        x = float (self.A  * rad * sin(self.beta) * cos(theta) * self.D)
        y = float (self.A  * rad * sin(self.beta) * sin(theta))
        z = float (-self.A * rad * cos(self.beta))
        
        spiral_i = PVector(x,y,z)
        spiral_matrix.append(spiral_i) 
        
        #Initialization values for generateSpline.
        self.j = 0
        spline_matrix =[]
        
        def generateSpline(x,y,z):
            """Generate spline around each spiral point. Values from generateSpiral are passed to this nested function."""
            while self.j < self.m: 
                s = float(map(self.j, 0, self.m, 0, TWO_PI)) 
                r2 = float(pow( pow(cos(s)/self.a,2) + pow(sin(s)/self.b,2), -0.5 ))
                surfrad = float(0)
                
                #Add shell surface nodules and manipulations.
                if (self.W1==0 or self.W2==0 or self.N==0):
                    surfrad = 0
                else: 
                    lt = float((TWO_PI / self.N) * ( self.N*theta / TWO_PI - int(self.N*theta / TWO_PI) ))
                    surfrad = self.L * exp( -( pow(2*(s-self.P)/self.W1, 2) + pow(2*lt/self.W2, 2) ) ) 
                
                r2 += surfrad # += means "add r2 to surfrad
                
                #Generate spline point. 
                x = cos(s + self.phi) * cos(theta + self.omega) * r2 * rad * self.D  # This line closes the opening of the curve at the origin
                y = cos(s + self.phi) * sin(theta + self.omega) * r2 * rad
                z = sin(s + self.phi)                           * r2 * rad
                
                #Adjust orientation of the point so spline does not flatten against the spiral curve.
                x -= sin(self.mu) * sin(s + self.phi) * sin(theta + self.omega) * r2
                y += sin(self.mu) * sin(s + self.phi) * cos(theta + self.omega) * r2
                z *= cos(self.mu)
                
                spline_point = PVector(x,y,z)
                
                spline_point_adjusted = map(sum, zip(spiral_i, spline_point))
                spline_matrix.append(spline_point_adjusted) 
                self.j = self.j+ 1 
            return spline_matrix
        
        #Call generateSpline from within generateSpiral
        spline = generateSpline(*spiral_i)
   
        #Append returned spline_matrix to shell_matrix
        shell_matrix.append(spline) # shell_matrix should be of form: ([[xyz],[xyz],[xyz]],[[x,y,z],[x,y,z],[x,y,z]]) etc. 
        self.i = self.i + 1  
        
    return shell_matrix

You’re right, the addon THREEp is based on spherical coordinates.
The addon THREEf on cylindrical coordinates.

Any geometry can be created. The addons THREEg and THREEi use very different variants.
Just look at this examples.

45

20180228-1235-19619
See sandbox and hofk (Klaus Hoffmeister) · GitHub


An example where I test a simple form.


As a newbie, it is helpful to have a look at the example collection.

There you’ll also find my very simple basic example of how to create an indexed buffer geometry.
https://hofk.de/main/discourse.threejs/2017/Indexed%20BufferGeometry/Indexed%20BufferGeometry.html
Starting with that, you can make it as complicated as you want!

Hi!
Not the exact port of your python code, just a simple concept of what and how you can do in Three.js:

P.S. This is not the ultimate solution, but that was the pure fun to get know about how to build a logarithmic spiral :blush:

3 Likes

Oh, this is cool! Thank you for pointing me in the right direction. I did not know about Codepen.io before this. I was looking for a way to debug javascript as I am new to working with it.

You’re welcome :beers: