Sine wave disc formula?

what would this even be called in mathematics?

I know how to get the edge curve vertices of a circle and I know how to get x,y positions for a sine wave…

but, I have no idea how you can combine the two into a single formula to achieve this face loop…

I added the inner loop to clarify the shape…


If Y was up/down, would it be something like:

x = cos(theta) * radius
y = sin(theta) * height
z = sin(theta) * radius

thanks colin, I’ll look those up…

from a 10m circle I have a set of 96 planar points and for example

ary[11] == [147.9999621021609, 129.79248328741514, 0.0]
ary[12] == [139.1942482650684, 139.1942482650684, 0.0]

what I need to adjust is the 0.0, either up or down depending on Amplitude of the desired Sine…

if I loop a step it creates a spiral…

step =+ 100
pt = ary[11]
amp = step # this is the missing factor
new_pt =[0,0,amp]).map { |a,b| a + b } 
# => [147.9999621021609, 129.79248328741514, 100.0]


I don’t know Ruby, but do you have a file that does the spiral, that I can try editing?

this cobble seems to work, but there must be a more elegant way…

 centerpoint =
 # Create a circle perpendicular to the normal or Z axis
 vector = 0,0,1
 vector2 = vector.normalize!
radius = 0.5.m
segments = 96
steps = 12
divs = segments / steps
 model = Sketchup.active_model
 entities = model.active_entities
 edges = entities.add_circle centerpoint, vector2, radius, segments
ary = []
edges.first.curve.vertices.each{|v| ary << v.position.to_a}

b, a, s = [radius * Math::PI/divs,1,steps]
pts = []
0.step(b, b/s) do |x|
		z = a * Math::sin( (x / b) * 2 * Math::PI)
		pts << [0,0,z.round(16)]

i = 0
new_pts = []
ary.each do |pt| 
v = pts[i]
new = { |a,b| a + b } rescue (p i and next)
 new_pts << new
i +=1
i = 0 if i == 12
        Sketchup.active_model.start_operation("Sine Wave")
        grp = Sketchup.active_model.active_entities.add_group
        entities = grp.entities
        entities.add_edges new_pts

maybe @jim_foltz or jimhami42 have a better idea than me…

If R is the radius and A is the angle in the XY plane, this will give you what you are looking for (a variant of @colin’s equations):

x = R * cos(A)
y = R * sin(A)
z = sin(4.0 * A)

The value of “4.0” controls how many “waves” you have around the circular path. I plotted this using UV-Polygen with 10 <= u <= 12 and 0 <= v <= 2 * PI:

that’s pretty dam cool…

this is the sort of thing I’m wanting it to make [automatically eventually]

this was amde with a slightly improved version of my lash up…
jcb_sine_circle.rb (1.6 KB)

it get the points by making then deleting a circle, which is probably not optimum…


If your points are centered around the origin, you can use something like this (scale is something to increase/decrease the relative height; modulo is the number of sine waves):

  modulo = 4.0
  scale = 2.0
  vec1 =, 0, 0)
[in a loop of some sort]
  vec2 =, 129.79248328741514, 0.0)
  ang = vec1.angle_between vec2
  height = scale * Math.sin(modulo * ang)
[end loop of some sort]

If you are not rotating about the origin, you can create the vectors from the center of the circle(s).

cheers jim, I’ll try that…



I got that to work but it was making ‘butterflies’ or mirrored halves…
I added a conditional to flip height to neg height half way through…

      model = Sketchup.active_model
      entities = model.active_entities

      radius = 0.5.m
      segments = 96
      modulo = 6
      scale = 4
# p      steps = segments / modulo
      keep_circle = true
      # create the base circle perpendicular to the normal or Z axis
      centerpoint =
      vector =, 0, 1)
      vector2 = vector.normalize!
      # wrap geometry creation in an undo...
      Sketchup.active_model.start_operation('Sine Circles')
      circle_grp = entities.add_group
      circle_gents = circle_grp.entities
      edges = circle_gents.add_circle(centerpoint, vector2, radius, segments)
# p     edges.first.length.to_mm
      # collect the verts
      verts_ary = []
      edges.first.curve.vertices.each { |vert| verts_ary << vert.position.to_a }

      entities.erase_entities(circle_grp) unless keep_circle
# p     verts_ary.length

      # add the new vert positions
      i = 0
      new_pts = []
      verts_ary.each do |vert_pos| 
        i += 1
        vec1 =, 0, 0)
        vec2 = vert_pos
        ang = vec1.angle_between vec2
        height = scale * Math.sin(modulo * ang)
        if i <= segments / 2        # of base circle
          new_pts << (vert_pos[0..1] << height)
        else                        # flip 
          new_pts << (vert_pos[0..1] << -height)
      # make the curve
      grp = Sketchup.active_model.active_entities.add_group
      entities = grp.entities

would this be better described as a tubular sine wave?


I’m not quite sure what it’s called … however a slot in a cylinder that has a sinusoidal shape is often used as a cam. Similarly, a disk on a shaft with sinusoidal waves atop the disk is also used as a cam. Maybe “cylindrical sinusoid” would be more appropriate?

I’m not quite sure what you mean, but I meant to point out that the heights will be +/- relative to the current datum plane … if you are using a scale of 4 (4.0?), you can offset the height by the scale amount (i.e., height = scale * Math.sin(modulo * ang) + scale) which should result in only positive z values (plus 0) at or above the datum.


I modified your code and now see what you mean. I’m not sure why that should be the case (I’m digging into it, of course). However, your fix seems to work just fine.

If you use an angle that starts at zero and proceeds counter-clockwise about the origin for 360 degrees, the sine function generates a smooth function (no butterfly cusps). However, the angle_between function only returns positive angles between 0 and PI. I would expect that the equivalent of 185 degrees would be either 185 or -175 degrees, but the angle_between returns +175 degrees. This effectively reverses the sine of the angle (because essentially the cartesian coordinate axes are mirrored by using a positive angle instead of a negative one). Another test might be to simply check the Y value:

if(vert_pos[1] >= 0.0)
  new_pts << (vert_pos[0..1] << height)
else                        # flip 
  new_pts << (vert_pos[0..1] << -height)

sorry I missed your post’s I’ve been adding some user touches…

would that be as quick as testing against the fixed number?

not that either are slow enough to be of concern…

next is to add a flat disc version…


That’s looking really nice :slight_smile:

I think you’ll have to time some examples each way to determine that. Your comparison includes the “i” increment each pass through the loop along with dividing the number of segments by 2 each time. Creating a fixed value for “segments / 2” outside the loop would help a little.

Either way, it looks like you’re making substantial progress.

good point, I also moved vec1 =, 0, 0) outside as it’s persistent as well…

does this break anything?

jcb_sine_circle.rbz (19.3 KB)

I don’t think so … however I’m on the road using my netbook with SketchUp 8 and it gives this error when I launch SketchUp:

Error Loading File jcb_sine_circle.rb
undefined method `[]' for #<LanguageHandler:0x84188d8 @strings={}>

So I can’t test things directly, only by inspection … but these changes don’t seem to break anything.


here’s one with the v8 patch I normally include…

EDIT: removed the rbz…


This worked just fine … I had to change the “.nib” directory name to “.bin” for it to load. When I ran it, it complained about “edges.each(&:find_faces)” so I changed it to “edges.each{ |i| i.find_faces }” and then it worked as expected. This is really cool, John :slight_smile:

will edges.each(&:find_faces) rescue edges.each{|i| i.find_faces} work for v8?

it doesn’t break v16…

I been playing with making wavy washers by scaling only on the red,green to center [.2,.2], then move a copy up to get the 4th face…


That works in v8 as well.