jfreeze-ruby-gdsii 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/CHANGELOG.txt +6 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.txt +113 -0
  4. data/Rakefile +30 -0
  5. data/bin/rgds-debug +43 -0
  6. data/bin/rgds-dump +38 -0
  7. data/bin/rgds-join +98 -0
  8. data/bin/rgds-layers +53 -0
  9. data/bin/rgds-sremove +136 -0
  10. data/bin/rgds-ssplit +113 -0
  11. data/bin/rgds-stats +134 -0
  12. data/bin/rgds-structs +41 -0
  13. data/bin/rgds-tree +167 -0
  14. data/bin/rgds2rb +99 -0
  15. data/lib/gdsii.rb +137 -0
  16. data/lib/gdsii/aref.rb +243 -0
  17. data/lib/gdsii/bnf.rb +309 -0
  18. data/lib/gdsii/boundary.rb +53 -0
  19. data/lib/gdsii/box.rb +65 -0
  20. data/lib/gdsii/byte_order.rb +36 -0
  21. data/lib/gdsii/element.rb +172 -0
  22. data/lib/gdsii/group.rb +98 -0
  23. data/lib/gdsii/library.rb +518 -0
  24. data/lib/gdsii/mixins.rb +378 -0
  25. data/lib/gdsii/node.rb +65 -0
  26. data/lib/gdsii/path.rb +169 -0
  27. data/lib/gdsii/property.rb +108 -0
  28. data/lib/gdsii/record.rb +606 -0
  29. data/lib/gdsii/record/consts.rb +384 -0
  30. data/lib/gdsii/record/datatypes/ascii.rb +145 -0
  31. data/lib/gdsii/record/datatypes/bitarray.rb +101 -0
  32. data/lib/gdsii/record/datatypes/data.rb +111 -0
  33. data/lib/gdsii/record/datatypes/int2.rb +67 -0
  34. data/lib/gdsii/record/datatypes/int4.rb +65 -0
  35. data/lib/gdsii/record/datatypes/nodata.rb +60 -0
  36. data/lib/gdsii/record/datatypes/real4.rb +51 -0
  37. data/lib/gdsii/record/datatypes/real8.rb +120 -0
  38. data/lib/gdsii/sref.rb +61 -0
  39. data/lib/gdsii/strans.rb +133 -0
  40. data/lib/gdsii/structure.rb +352 -0
  41. data/lib/gdsii/text.rb +203 -0
  42. data/pkg/ruby-gdsii.gem +46 -0
  43. data/samples/hello.gds +0 -0
  44. data/samples/hello.out.rb +84 -0
  45. data/samples/hello.rb +94 -0
  46. data/test/baseline/dcp1.gds +0 -0
  47. data/test/baseline/h_write.gds +0 -0
  48. data/test/h_pthru.rb +22 -0
  49. data/test/h_write.rb +117 -0
  50. data/test/hs_pthru.rb +31 -0
  51. data/test/l_pthru.rb +23 -0
  52. data/test/test_gds_group.rb +379 -0
  53. data/test/test_gds_record.rb +99 -0
  54. metadata +117 -0
@@ -0,0 +1,378 @@
1
+ require 'gdsii/record/consts'
2
+
3
+ module Gdsii
4
+
5
+ #
6
+ # Various attribute accessor definitions which are used (included) by the
7
+ # high-level GDSII classes.
8
+ #
9
+ module Access
10
+
11
+ #
12
+ # Access layer attribute
13
+ #
14
+ module Layer
15
+ # Get the layer record (returns Record)
16
+ def layer_record() @records.get(GRT_LAYER); end
17
+
18
+ # Get the layer number (returns Fixnum)
19
+ def layer() @records.get_data(GRT_LAYER); end
20
+
21
+ # Set the layer number
22
+ def layer=(val) @records.set(GRT_LAYER, val); end
23
+ end
24
+
25
+ ##########################################################################
26
+
27
+ #
28
+ # Access Datatype attribute
29
+ #
30
+ module Datatype
31
+ # Get the datatype record (returns Record)
32
+ def datatype_record() @records.get(GRT_DATATYPE); end
33
+
34
+ # Get the datatype number (returns Fixnum)
35
+ def datatype() @records.get_data(GRT_DATATYPE); end
36
+
37
+ # Set the datatype number
38
+ def datatype=(val) @records.set(GRT_DATATYPE, val); end
39
+ end
40
+
41
+ ##########################################################################
42
+
43
+ #
44
+ # Access XY attribute
45
+ #
46
+ module XY
47
+ # Gets an xy point record (returns Record)
48
+ def xy_record() @records.get(GRT_XY); end
49
+
50
+ # Gets an xy point record (returns an Array)
51
+ def xy() @records.get_data(GRT_XY); end
52
+
53
+ # Sets an xy point record
54
+ def xy=(val) @records.set(GRT_XY, val); end
55
+ end
56
+
57
+ ##########################################################################
58
+
59
+ #
60
+ # Access ELFlags attribute
61
+ #
62
+ module ELFlags
63
+ # Get the elflags record (returns Record)
64
+ def elflags_record() @records.get(GRT_ELFLAGS); end
65
+
66
+ # Get the elflags record data (returns Fixnum)
67
+ def elflags() @records.get_data(GRT_ELFLAGS); end
68
+
69
+ # Set the elflags record
70
+ def elflags=(val) @records.set(GRT_ELFLAGS,val); end
71
+ end
72
+
73
+ ##########################################################################
74
+
75
+ #
76
+ # Access Plex attribute
77
+ #
78
+ module Plex
79
+ # Set the plex record (returns Record)
80
+ def plex_record() @records.get_data(GRT_PLEX); end
81
+
82
+ # Set the plex record data (returns Fixnum)
83
+ def plex() @records.get_data(GRT_PLEX); end
84
+
85
+ # Set the plex record
86
+ def plex=(val) @records.set(GRT_PLEX,val); end
87
+ end
88
+
89
+ ##########################################################################
90
+
91
+ #
92
+ # Access PathType attribute
93
+ #
94
+ module PathType
95
+ #
96
+ # Get the path type record (returns Record).
97
+ #
98
+ def pathtype_record() @records.get(GRT_PATHTYPE); end
99
+
100
+ #
101
+ # Get the path type number (returns Fixnum).
102
+ #
103
+ def pathtype() @records.get_data(GRT_PATHTYPE); end
104
+
105
+ #
106
+ # Set the type number (as Fixnum). Valid range is 0-2,4 (not 3):
107
+ #
108
+ # * 0: Square ended paths (default)
109
+ # * 1: Round ended
110
+ # * 2: Square ended, extended 1/2 width
111
+ # * 4: Variable length extensions (see #bgnextn and #endextn)
112
+ #
113
+ def pathtype=(val)
114
+ record = @records.set(GRT_PATHTYPE, val)
115
+ data_val = record.data_value
116
+ if [0, 1, 2, 4].member?(data_val)
117
+ if data_val != 4
118
+ # Rip out begin/end extensions for pathtypes other than 4
119
+ @records.delete_key(GRT_BGNEXTN)
120
+ @records.delete_key(GRT_ENDEXTN)
121
+ end
122
+ else
123
+ # If path type is not 0, 1, 2, or 4, then fail
124
+ raise TypeError, "Path type #{data_val} is invalid; must be 0, 1, 2, or 4."
125
+ end
126
+ end
127
+ end
128
+
129
+ ##########################################################################
130
+
131
+ #
132
+ # Access Width attribute
133
+ #
134
+ module Width
135
+ #
136
+ # Get the path width record (returns Record)
137
+ #
138
+ def width_record() @records.get(GRT_WIDTH); end
139
+
140
+ #
141
+ # Get the path width value (returns Fixnum). The width value is multiplied
142
+ # with the UNITS value of the library to obtain the actual width.
143
+ #
144
+ def width() @records.get_data(GRT_WIDTH); end
145
+
146
+ #
147
+ # Set the path width value (as Fixnum). The width value is multiplied
148
+ # with the UNITS value of the library and magnification factor to obtain
149
+ # the actual width. If the width value is negative, then the value is
150
+ # interpreted to be absolute and will not be affected by the database
151
+ # units or magnification factor.
152
+ #
153
+ def width=(val) @records.set(GRT_WIDTH, val); end
154
+ end
155
+
156
+ ##########################################################################
157
+
158
+ #
159
+ # Access Strans record grouping
160
+ #
161
+ module StransGroup
162
+ #
163
+ # Access the Strans for this object (see Gdsii::Strans class
164
+ # documentation for a list of Strans methods).
165
+ #
166
+ def strans(); @records.get(Strans); end
167
+
168
+ #
169
+ # Set the Strans for this object
170
+ #
171
+ def strans=(val); @records.set(Strans, val); end
172
+ end
173
+
174
+ ##########################################################################
175
+
176
+ #
177
+ # Access Sname attribute
178
+ #
179
+ module Sname
180
+ #
181
+ # Get the referenced structure SNAME record (returns Record).
182
+ #
183
+ def sname_record() @records.get(GRT_SNAME); end
184
+
185
+ #
186
+ # Get the referenced structure name (returns String).
187
+ #
188
+ def sname() @records.get_data(GRT_SNAME); end
189
+
190
+ #
191
+ # Set the referenced structure name.
192
+ #
193
+ def sname=(val) @records.set(GRT_SNAME, val); end
194
+ end
195
+
196
+ ##########################################################################
197
+
198
+ #
199
+ # Mix in methods that work with a predefined @list attribute. The list is
200
+ # made Enumerable and a number of methods are mixed in. Used in
201
+ # Properties, Structure, and Library.
202
+ #
203
+ module EnumerableGroup
204
+
205
+ include Enumerable
206
+
207
+ #
208
+ # Get the list object.
209
+ #
210
+ def list() @list; end
211
+
212
+ alias :to_a :list
213
+
214
+ #
215
+ # Loops through each object yielding along the way.
216
+ #
217
+ def each(); @list.each {|e| yield e}; end
218
+
219
+ #
220
+ # Add an object to this list as either an object instance or as
221
+ # the object data. Similar to Array#push but validates the data being
222
+ # added. The object added is returned.
223
+ #
224
+ def add(object)
225
+ self.validate_addition(object) if self.respond_to?(:validate_addition)
226
+ @list.push object
227
+ object
228
+ end
229
+
230
+ #
231
+ # Remove object(s) from this element when the propert(y/ies) match
232
+ # the given criteria (in the code block). Equivalent to Array#reject!.
233
+ #
234
+ def remove()
235
+ @list.reject! {|e| yield e}
236
+ end
237
+
238
+ #
239
+ # Implement a trap for a method that might be missing. Any method not
240
+ # listed here will default to the @list Array attribute if @list
241
+ # #respond_to? the method. This nifty feature allows us to "inherit"
242
+ # *all* methods related Array which will be operated upon the @list Array
243
+ # attribute.
244
+ #
245
+ def method_missing(method_sym, *args)
246
+ if @list.respond_to?(method_sym)
247
+ # The array @list responds to this method - use it on @list
248
+ @list.method(method_sym).call(*args)
249
+ else
250
+ # Raise the #method_missing error
251
+ super
252
+ end
253
+ end
254
+ end
255
+
256
+ ##########################################################################
257
+
258
+ #
259
+ # Shared method related to working with the standard Time library within
260
+ # the Gdsii library.
261
+ #
262
+ module GdsiiTime
263
+ #
264
+ # Given a Time object, the time is formatted into an array of integers
265
+ # according to the GDSII specification.
266
+ #
267
+ def build_time(time)
268
+ [time.year-1900, time.month, time.day, time.hour, time.min, time.sec.to_i]
269
+ end
270
+ end
271
+
272
+ end
273
+
274
+ ############################################################################
275
+
276
+ #
277
+ # This module will be extended into all classes descended of Group (i.e.
278
+ # the high-level GDSII access classes). The extension brings a "read"
279
+ # class method (i.e. singleton) into these classes which enables a GDSII
280
+ # file to be read into these data structures using the BNF specification.
281
+ #
282
+ module Read
283
+ #
284
+ # Accepts a file handle and reads data from that file handle into an
285
+ # object descended of Group. The parent_records and parent_bnf_item
286
+ # arguments are for internal use only.
287
+ #
288
+ def read(file, parent_records=nil, parent_bnf_item=nil, yield_at=nil)
289
+ # Set BNF index at 0, get the BNF items, create an empty grouping object
290
+ i = 0
291
+ bnf_items = bnf_spec.bnf_items
292
+ group = self.new
293
+ # puts "#{self}"
294
+
295
+ # Loop through the BNF spec for this grouping...
296
+ while i < bnf_items.length do
297
+ bnf_item = bnf_items[i]
298
+
299
+ # see what kind of a BNF item this is - a Class or a record type (Fixnum)
300
+ if bnf_item.key.class == Class
301
+ # return if stop_at_class is set to true (used internally for
302
+ # Library#read_header and Cell#read_header).
303
+ yield group if yield_at == :before_group
304
+
305
+ # Determine the class to use
306
+ klass = bnf_item.key
307
+
308
+ # Read from the class
309
+ if bnf_item.multiple?
310
+ if (rec = klass.read(file, group.records, bnf_item))
311
+ # if a record was returned, then add it
312
+ group.records.add(bnf_item.key, rec)
313
+ else
314
+ # if nil was returned, then we're done with the record; next
315
+ i += 1
316
+ end
317
+ else
318
+ # If the record is singular, then get it from the class and
319
+ # increment the counter
320
+ rec = klass.read(file, group.records, bnf_item)
321
+ group.records.set(bnf_item.key, rec)
322
+ i += 1
323
+ end
324
+ else
325
+ # ELSE, a record type is expected (Fixnum)
326
+ rec = Record.read(file)
327
+ # puts " --> expect #{Gdsii::grt_name(bnf_item.key)}; rec == #{rec.name}"
328
+ if rec.type == bnf_item.key
329
+ # This record matches the grouping BNF item that was expected, so
330
+ # store the data
331
+ if bnf_item.multiple?
332
+ group.records.add(bnf_item.key, rec)
333
+ else
334
+ group.records.set(bnf_item.key, rec)
335
+ i += 1
336
+ end
337
+ else
338
+ # Record does not match expected record as per BNF. Check that we
339
+ # have data already set in this record or that the record is
340
+ # optional.
341
+ if group.records.has_data?(bnf_item.key) or bnf_item.optional?
342
+ # Already has data - just move to the next record and reset file
343
+ # pointer
344
+ i += 1
345
+ file.seek(-rec.byte_size, IO::SEEK_CUR)
346
+ elsif (parent_bnf_item and parent_bnf_item.key.class == Class and
347
+ (parent_records.has_data?(parent_bnf_item.key) or
348
+ parent_bnf_item.optional?))
349
+ # OK, in this case, we are descended into a Class and did not
350
+ # match the BNF expected. Furthermore, the parent calling this
351
+ # grouping either already got the data needed or this was an
352
+ # optional class in the first place. In either case, we're OK
353
+ # and just need to get out - which is what we do by returning
354
+ # nil.
355
+ file.seek(-rec.byte_size, IO::SEEK_CUR)
356
+ return nil
357
+ else
358
+ # Does not match the expected BNF... fail
359
+ raise "Unexpected record while reading GDSII file starting at file position #{file.pos-rec.byte_size}\n" +
360
+ "This record is in the wrong place according to the GDSII specification (from BNF)\n" +
361
+ "Expected record was #{Gdsii::grt_name(bnf_item.key)}; instead, received:\n" +
362
+ "Record type = #{Gdsii::grt_name(rec.type)}\n" +
363
+ "Data type = #{Gdsii::gdt_name(rec.data.type)}\n" +
364
+ "Record length = #{rec.byte_size}\n"
365
+ end
366
+ end
367
+
368
+ end
369
+ end
370
+
371
+ # Return this record grouping
372
+ yield group if yield_at == :group
373
+ group
374
+ end
375
+ end
376
+
377
+
378
+ end
data/lib/gdsii/node.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'gdsii/group'
2
+ require 'gdsii/element'
3
+
4
+ module Gdsii
5
+
6
+ #
7
+ # Represents a GDSII Node element. Most methods are from Element or from
8
+ # the various included Access module methods.
9
+ #
10
+ class Node < Element
11
+
12
+ # Include various record accessors
13
+ include Access::Layer
14
+ include Access::XY
15
+ include Access::ELFlags
16
+ include Access::Plex
17
+
18
+ #
19
+ # Node BNF description:
20
+ #
21
+ # <node> ::= NODE [ELFLAGS] [PLEX] LAYER NODETYPE XY
22
+ #
23
+ self.bnf_spec = BnfSpec.new(
24
+ BnfItem.new(GRT_NODE),
25
+ BnfItem.new(GRT_ELFLAGS, true),
26
+ BnfItem.new(GRT_PLEX, true),
27
+ BnfItem.new(GRT_LAYER),
28
+ BnfItem.new(GRT_NODETYPE),
29
+ BnfItem.new(GRT_XY),
30
+ BnfItem.new(Properties, true),
31
+ BnfItem.new(GRT_ENDEL)
32
+ )
33
+
34
+ #
35
+ # Create a node record grouping given a layer, nodetype, and xy
36
+ # coordinates. The node object can have 1-50 coordinate pairs.
37
+ #
38
+ # node = Gdsii::Node.new(1, 0, [0,0])
39
+ #
40
+ def initialize(layer=nil, nodetype=nil, xy=nil)
41
+ super()
42
+ @records[GRT_NODE] = Record.new(GRT_NODE)
43
+ self.layer = layer unless layer.nil?
44
+ self.nodetype = nodetype unless nodetype.nil?
45
+ self.xy = xy unless xy.nil?
46
+ yield self if block_given?
47
+ end
48
+
49
+ #
50
+ # Get the nodetype record (returns Record).
51
+ #
52
+ def nodetype_record() @records.get(GRT_NODETYPE); end
53
+
54
+ #
55
+ # Get the nodetype number (returns Fixnum).
56
+ #
57
+ def nodetype() @records.get_data(GRT_NODETYPE); end
58
+
59
+ #
60
+ # Set the nodetype number.
61
+ #
62
+ def nodetype=(val) @records.set(GRT_NODETYPE, val); end
63
+
64
+ end
65
+ end
data/lib/gdsii/path.rb ADDED
@@ -0,0 +1,169 @@
1
+ require 'gdsii/element'
2
+ require 'gdsii/mixins'
3
+
4
+ module Gdsii
5
+
6
+ #
7
+ # Represents a GDSII Path element. Most methods are from Element or from
8
+ # the various included Access module methods.
9
+ #
10
+ class Path < Element
11
+
12
+ # Include various record accessors
13
+ include Access::Layer
14
+ include Access::Datatype
15
+ include Access::XY
16
+ include Access::PathType
17
+ include Access::Width
18
+ include Access::ELFlags
19
+ include Access::Plex
20
+
21
+ #
22
+ # Boundary BNF description:
23
+ #
24
+ # <path> ::= PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE]
25
+ # [WIDTH] [BGNEXTN] [ENDEXTN] XY
26
+ #
27
+ self.bnf_spec = BnfSpec.new(
28
+ BnfItem.new(GRT_PATH),
29
+ BnfItem.new(GRT_ELFLAGS, true),
30
+ BnfItem.new(GRT_PLEX, true),
31
+ BnfItem.new(GRT_LAYER),
32
+ BnfItem.new(GRT_DATATYPE),
33
+ BnfItem.new(GRT_PATHTYPE, true),
34
+ BnfItem.new(GRT_WIDTH, true),
35
+ BnfItem.new(GRT_BGNEXTN, true),
36
+ BnfItem.new(GRT_ENDEXTN, true),
37
+ BnfItem.new(GRT_XY),
38
+ BnfItem.new(Properties,true),
39
+ BnfItem.new(GRT_ENDEL)
40
+ )
41
+
42
+ #
43
+ # Generic method to create a path given a layer, datatype, pathtype,
44
+ # width, and series of alternating x/y coordinates. The default pathtype
45
+ # is 0.
46
+ #
47
+ # path = Gdsii::Path.new(1, 0, 0, 100, [0,0, 1000,0, 1000,1000])
48
+ #
49
+ def initialize(layer=nil, datatype=nil, pathtype=nil, width=nil, xy=nil)
50
+ super()
51
+ @records[GRT_PATH] = Record.new(GRT_PATH)
52
+ self.layer = layer unless layer.nil?
53
+ self.datatype = datatype unless datatype.nil?
54
+ self.pathtype = (pathtype.nil?) ? 0 : pathtype
55
+ self.width = width unless width.nil?
56
+ self.xy = xy unless xy.nil?
57
+
58
+ # Set a code block to validate a path record
59
+ @validate = proc {
60
+ # Check for begin/end extensions for pathtype == 4
61
+ if self.pathtype == 4
62
+ unless self.bgnextn and self.endextn
63
+ raise "Begin/end extensions (#bgnextn= and #endextn=) required for path type 4"
64
+ end
65
+ end
66
+ }
67
+
68
+ yield self if block_given?
69
+ end
70
+
71
+ #
72
+ # Creates a path of type 0
73
+ #
74
+ def Path.new0(layer=nil, datatype=nil, width=nil, xy=nil)
75
+ Path.new(layer, datatype, 0, width, xy) {|p| yield p if block_given?}
76
+ end
77
+
78
+ #
79
+ # Creates a path of type 1
80
+ #
81
+ def Path.new1(layer=nil, datatype=nil, width=nil, xy=nil)
82
+ Path.new(layer, datatype, 1, width, xy) {|p| yield p if block_given?}
83
+ end
84
+
85
+ #
86
+ # Creates a path of type 2
87
+ #
88
+ def Path.new2(layer=nil, datatype=nil, width=nil, xy=nil)
89
+ Path.new(layer, datatype, 2, width, xy) {|p| yield p if block_given?}
90
+ end
91
+
92
+ #
93
+ # Creates a path of type 4; accepts begin/end extension values
94
+ #
95
+ # path = Path.new4(1, 0, 100, 30, 30, [0,0, 1000,0, 1000,1000])
96
+ #
97
+ def Path.new4(layer=nil, datatype=nil, width=nil,
98
+ bgnextn=nil, endextn=nil, xy=nil)
99
+ Path.new(layer, datatype, 4, width, xy) do |path|
100
+ path.bgnextn = bgnextn
101
+ path.endextn = endextn
102
+ yield self if block_given?
103
+ end
104
+ end
105
+
106
+ #
107
+ # Set the beginning extension for path type 4 (as Fixnum). Value is in
108
+ # database units.
109
+ #--
110
+ # TODO: more explanation of database units; also example
111
+ #++
112
+ #
113
+ def bgnextn=(val)
114
+ ensure_pathtype_4
115
+ @records.set(GRT_BGNEXTN, val)
116
+ end
117
+
118
+ #
119
+ # Get the beginning extension for path type 4 (as Fixnum). Value is in
120
+ # database units.
121
+ #
122
+ def bgnextn(); @records.get_data(GRT_BGNEXTN); end
123
+
124
+ #
125
+ # Get the beginning extension record for path type 4 (as Fixnum). Value is
126
+ # in database units.
127
+ #
128
+ def bgnextn_record(); @records.get(GRT_BGNEXTN); end
129
+
130
+ #
131
+ # Set the ending extension for path type 4 (as Fixnum). Value is in
132
+ # database units.
133
+ #--
134
+ # TODO: more explanation of database units; also example
135
+ #++
136
+ #
137
+ def endextn=(val)
138
+ ensure_pathtype_4
139
+ @records.set(GRT_ENDEXTN, val)
140
+ end
141
+
142
+ #
143
+ # Get the ending extension for path type 4 (as Fixnum). Value is in
144
+ # database units.
145
+ #
146
+ def endextn(); @records.get_data(GRT_ENDEXTN); end
147
+
148
+ #
149
+ # Get the ending extension record for path type 4 (as Fixnum). Value is
150
+ # in database units.
151
+ #
152
+ def endextn_record(); @records.get(GRT_ENDEXTN); end
153
+
154
+ ###########################################################################
155
+
156
+ private
157
+
158
+ #
159
+ # Ensures that pathtype == 4. This needs to be verified for a few of the
160
+ # methods only relevant for pathtype == 4 (see #bgnextn and #endextn).
161
+ #
162
+ def ensure_pathtype_4()
163
+ if pathtype != 4
164
+ raise TypeError, "Attempt to access a method only relevant to pathtype == 4"
165
+ end
166
+ end
167
+
168
+ end
169
+ end