# How to Create Logarithmic Spirals

edited November 2015 in General
Hi,

I'm a little lost right now in trying to create logarithmic spirals with Klayout. Could you suggest a method to create such complex structures?

Sorry if this question has already been addressed, I haven't been able to find much information regarding logarithmic spirals in the forum.

Best.
Nick

• edited November 2015

Nick,

Here's a script that draws a circle (path). Modify these lines

``````x = radius * Math.cos(tt)
``````

to instead have the parametric equation for log spiral.

I've put this in E4K.

It looks like I spent a huge amount of time writing long comments but actually most of those comments were already there, from another example in E4K that I duplicated to make this. Anyhoo, here you go.

``````## Draw a circular path.lym
## By davidnhutch
##
## Draws a circular path

# Wrapping it in 'module ... end' helps avoid namespace clashes but isn't
# necessary.
module DrawCircPath

# Include the RBA module, then get a handle on the application.
# "RBA" is the module provided by KLayout. "Application" is the main
# controller class (a singleton) that refers to the application as a whole.
# You don't have to include RBA, but then you have to preface any classes
# (such as Application) with RBA:: (for example, instead of
# Application.instance you'd type RBA::Application.instance).
include RBA
app = Application.instance

# Get a handle on the main window and the current view
mw = app.main_window
lv = mw.current_view

# If lv is nil (ruby-talk for 'not defined') then we raise an error.
#
# We could have said
#   if lv.nil?
#   if lv == nil
#
# A more 'slick' and more ruby-ish way of doing the below is
#   lv || raise("No view selected")
# Why does that work?
# || means 'or'. It evaluates the left side of the 'or' statement, and if
# it's true (non-nil) then it doesn't bother evaluating the right side, so
# the error is never raised.
# However if the left side evaluates to nil then it is forced to evaluate the
# right side, so it raises the error. Brackets are needed around the raise
# argument, due to the evaluation priority of the ruby operators.
if lv == nil
raise "No view selected. Choose File > New layout, and run this again."
end

# Get a handle on the layout
ly = lv.active_cellview.layout

# Get a handle on the conversion factor between displayed coordinates and
# actual database coordinates.
# The displayed coordinates are the human-readable and useful ones. e.g.
# '12.345 microns'
# The database however, can only store integers for size and speed reasons.
# So the above number would be stored as 12345 (an integer, twelve thousand
# three hundred and forty five).
# So, the conversion factor between 1um and 1nm is 1/1000. So the 'dbu' below
# evaluates to 0.001 by default.
#
# (Actually you can define the conversion to be whatever you want, this is
# just the default and is the desired conversion in the vast majority of
# cases.)
dbu = ly.dbu

# Get a handle on the currently-shown 'top' cell. To change the top cell,
# center click on the cell name in the cell list on the left side of KLayout.
# We could have gotten the cell reference from its name too. In that case
# we'd use:
# cell = ly.cell("CELL_NAME")
cell = lv.active_cellview.cell

# Get a handle on the layer you want the path on. LayerInfo objects hold
# various information about a layer. In contrast, the layer index is just an
# index used internally to KLayout, so is not always as useful as the
# LayerInfo object.
# But, a method we will use later requires the layer index rather than the
# LayerInfo object, so we create layer_index variable to hold that.
layer_number = 1
datatype = 0
layer_info = LayerInfo.new(layer_number,datatype)
layer_index = ly.layer(layer_info)

# Define the  parameters
width  = 1.0 # Write everything in microns

# Define an empty array to hold the path's points
pts = []

# Create a linear "t" (theta) array from 0 to 2*pi
num_pts = 10
t_start = 0.0
t_end   = 2*Math::PI
dt = Math::PI / (num_pts - 1)
t = t_start.step(t_end,dt).to_a # Create the array. There are other ways of doing this too.

# Loop thru each t, and create a Point object holding the computed x,y
t.each { |tt|
# Now here's a trick to learn: Whenever creating a new shape (Point, Box,
# Polygon), put the coordinates in in database units.
# To do that, you divide your value in microns by 'dbu' while you are
# inserting it.
pt = Point.new(x/dbu,y/dbu)
# Push that to the pts array
pts << pt
}

# Push the first point again on to the end of the array so the ends meet
pts << pts[0]

# Now push the second point on to the end of the array.
# Why? Try commenting this line out - Although the ends of the path will meet
# up, these ends will come in at different angles, so there will be an
# undesirable triangular sliver where they meet. This line gets rid of that
# sliver.
pts << pts[1]

# Make Path object. Remember to divide by dbu again
path = Path.new(pts,width/dbu)

# Insert it in this cell, to the shapes database, on the desired layer
cell.shapes(layer_index).insert(path)

# If the user didn't have layer 1 defined already (Edit > Layer > New >
# layer 1/0) then they won't see anything. By adding missing layers we can
# help to show the shape in this case.

# Zoom to show the whole path
lv.zoom_fit

# Report by printing text to the Macro Editor console. "puts" means put
# string. Note that we could also type
#   p msg
# as a shortcut. Also there is
#   puts msg
# which has a slightly different behavior.
print "Inserted a path with coordinates = #{pts}"

end
``````
• edited November -1
Hi David,

Thanks for the help! I wasn't aware of E4K until now. I'll let you know how it goes.

Thanks again!

Best.
Nick