jfreeze-ruby-gdsii 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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