# 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…

john

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

y = sin(theta) * height

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 = pt.zip([0,0,amp]).map { |a,b| a + b }
# => [147.9999621021609, 129.79248328741514, 100.0]
``````

john

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 = Geom::Point3d.new
# Create a circle perpendicular to the normal or Z axis
vector = Geom::Vector3d.new 0,0,1
vector2 = vector.normalize!
segments = 96
steps = 12
divs = segments / steps
model = Sketchup.active_model
entities = model.active_entities
edges.first.length.to_mm
ary = []
edges.first.curve.vertices.each{|v| ary << v.position.to_a}
entities.erase_entities(edges)

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)]
end

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

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…

john

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 = Geom::Vector3d.new(1, 0, 0)
[in a loop of some sort]
vec2 = Geom::Vector3d.new(147.9999621021609, 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…

john

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

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 = Geom::Point3d.new
vector = Geom::Vector3d.new(0, 0, 1)
vector2 = vector.normalize!

# wrap geometry creation in an undo...
Sketchup.active_model.start_operation('Sine Circles')
circle_gents = circle_grp.entities

# 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 = Geom::Vector3d.new(1, 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)
end
end

# make the curve
entities = grp.entities
Sketchup.active_model.commit_operation

``````

would this be better described as a tubular sine wave?

john

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)
end``````

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…

john

That’s looking really nice

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 = Geom::Vector3d.new(1, 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.

oops…

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

EDIT: removed the rbz…

john

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

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…

john

That works in v8 as well.