ruby-gdsii 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.txt +113 -0
- data/bin/rgds-debug +43 -0
- data/bin/rgds-dump +38 -0
- data/bin/rgds-join +98 -0
- data/bin/rgds-layers +53 -0
- data/bin/rgds-sremove +135 -0
- data/bin/rgds-ssplit +113 -0
- data/bin/rgds-stats +134 -0
- data/bin/rgds-structs +41 -0
- data/bin/rgds-tree +166 -0
- data/bin/rgds2rb +99 -0
- data/lib/gdsii.rb +137 -0
- data/lib/gdsii/aref.rb +243 -0
- data/lib/gdsii/bnf.rb +309 -0
- data/lib/gdsii/boundary.rb +53 -0
- data/lib/gdsii/box.rb +65 -0
- data/lib/gdsii/byte_order.rb +36 -0
- data/lib/gdsii/element.rb +172 -0
- data/lib/gdsii/group.rb +98 -0
- data/lib/gdsii/library.rb +518 -0
- data/lib/gdsii/mixins.rb +378 -0
- data/lib/gdsii/node.rb +65 -0
- data/lib/gdsii/path.rb +169 -0
- data/lib/gdsii/property.rb +108 -0
- data/lib/gdsii/record.rb +606 -0
- data/lib/gdsii/record/consts.rb +384 -0
- data/lib/gdsii/record/datatypes/ascii.rb +145 -0
- data/lib/gdsii/record/datatypes/bitarray.rb +101 -0
- data/lib/gdsii/record/datatypes/data.rb +111 -0
- data/lib/gdsii/record/datatypes/int2.rb +67 -0
- data/lib/gdsii/record/datatypes/int4.rb +65 -0
- data/lib/gdsii/record/datatypes/nodata.rb +60 -0
- data/lib/gdsii/record/datatypes/real4.rb +51 -0
- data/lib/gdsii/record/datatypes/real8.rb +120 -0
- data/lib/gdsii/sref.rb +61 -0
- data/lib/gdsii/strans.rb +133 -0
- data/lib/gdsii/structure.rb +352 -0
- data/lib/gdsii/text.rb +203 -0
- data/pkg/ruby-gdsii.gem +23 -0
- data/samples/hello.gds +0 -0
- data/samples/hello.out.rb +84 -0
- data/samples/hello.rb +94 -0
- data/test/baseline/dcp1.gds +0 -0
- data/test/baseline/h_write.gds +0 -0
- data/test/h_pthru.rb +22 -0
- data/test/h_write.rb +117 -0
- data/test/hs_pthru.rb +31 -0
- data/test/l_pthru.rb +23 -0
- data/test/test_gds_group.rb +379 -0
- data/test/test_gds_record.rb +99 -0
- metadata +118 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'gdsii/record/datatypes/data.rb'
|
2
|
+
|
3
|
+
module Gdsii
|
4
|
+
|
5
|
+
module RecData
|
6
|
+
|
7
|
+
#
|
8
|
+
# Class for REAL8 data type
|
9
|
+
#
|
10
|
+
class Real8 < Data
|
11
|
+
|
12
|
+
# Value is an array of floating point numbers
|
13
|
+
attr_reader :value
|
14
|
+
|
15
|
+
# Construct an Gdsii::RecData::Real8 data object. The value is an array
|
16
|
+
# of floating point numbers (Float).
|
17
|
+
def initialize(value)
|
18
|
+
super(GDT_REAL8, value)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set the value for this object; verify that the value items are of
|
22
|
+
# type Float (or at least can be coerced using "to_f").
|
23
|
+
def value=(value)
|
24
|
+
@value = Data.coerce_value(value, Float, :to_f)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the size of the record *data* in bytes. Each array element
|
28
|
+
# consumes 8 bytes (hence REAL8).
|
29
|
+
def byte_size()
|
30
|
+
@value.length * 8
|
31
|
+
end
|
32
|
+
|
33
|
+
# Reads a REAL8 record from the given file and for the length of bytes
|
34
|
+
# given and returns a new Gdsii::RecData::Real8 object.
|
35
|
+
def Real8.read(file, byte_count)
|
36
|
+
data = []
|
37
|
+
while (byte_count > 0)
|
38
|
+
|
39
|
+
# read the first byte and get sign and exponent values from it
|
40
|
+
raw = file.read(1)
|
41
|
+
sign_val = raw.unpack('B')[0].to_i
|
42
|
+
exponent = raw.unpack('C')[0]
|
43
|
+
exponent -= (sign_val == 0) ? 64 : 192 # exponent is in Excess 64 fmt
|
44
|
+
|
45
|
+
# read the rest of the real number - save as binary
|
46
|
+
raw = file.read(7)
|
47
|
+
mant_binary = raw.unpack('b*')[0]
|
48
|
+
|
49
|
+
# convert mantissa from binary to decimal
|
50
|
+
mantissa = 0.0
|
51
|
+
(1...8).each do |i|
|
52
|
+
str = mant_binary[(i-1)*8,8]
|
53
|
+
ub = [("0"*32+str.reverse.to_s)[-32..-1]].pack("B32").unpack("N")[0]
|
54
|
+
mantissa += ub / (256.0 ** i)
|
55
|
+
end
|
56
|
+
real = mantissa * (16**exponent)
|
57
|
+
real = -real if (sign_val != 0)
|
58
|
+
data.push real
|
59
|
+
|
60
|
+
byte_count -= 8
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
Real8.new(data)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Writes the integer values in this Gdsii::RecData::Real8 object to the
|
68
|
+
# given file as a GDSII REAL8 record.
|
69
|
+
def write(file)
|
70
|
+
|
71
|
+
self.value.each do |item|
|
72
|
+
|
73
|
+
if item == 0 then
|
74
|
+
file.write "\x00" * 8
|
75
|
+
else
|
76
|
+
# Note: process differently for big endian?
|
77
|
+
bit_str = [item].pack('G').unpack('B*')[0]
|
78
|
+
|
79
|
+
# get the sign and expt (IEEE)
|
80
|
+
sign = bit_str[0,1]
|
81
|
+
expt = ['00000'+bit_str[1,11]].pack('B16').unpack('n')[0] - 1023
|
82
|
+
|
83
|
+
# Divide by 4 because 16**x == (2**4)**x == 2**(4*x)
|
84
|
+
b16_expt = (expt / 4).floor
|
85
|
+
b16_rem = expt % 4
|
86
|
+
|
87
|
+
# Shift the mantissa 4 bits to the right and increment the expt
|
88
|
+
b16_expt += 1
|
89
|
+
mant = ['0', '0', '0', '1', bit_str[12..63].split('')].flatten
|
90
|
+
|
91
|
+
# Shift the mantissa to left to take care of the expt remainder
|
92
|
+
(0...b16_rem).each do |i|
|
93
|
+
mant.shift
|
94
|
+
mant.push '0'
|
95
|
+
end
|
96
|
+
|
97
|
+
# Bias the base-16 expoent
|
98
|
+
b16_expt += 64
|
99
|
+
|
100
|
+
# Convert the expt to a 7-bit binary string
|
101
|
+
b16_expt_str = [b16_expt].pack('C').unpack('B*')[0][1..7]
|
102
|
+
|
103
|
+
# Now assemble the sign, expt and mantissa
|
104
|
+
real8_fmt = sign + b16_expt_str + mant.join('')
|
105
|
+
file.write [real8_fmt].pack('B*')
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Converts the array of floating point values to a string (values are
|
112
|
+
# joined by spaces).
|
113
|
+
def to_s()
|
114
|
+
value.map {|v| v.to_s}.join(' ')
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
data/lib/gdsii/sref.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'gdsii/group'
|
2
|
+
require 'gdsii/element'
|
3
|
+
require 'gdsii/strans'
|
4
|
+
|
5
|
+
module Gdsii
|
6
|
+
|
7
|
+
#
|
8
|
+
# Represents a GDSII structure reference (SRef) element. Most
|
9
|
+
# methods are from Element or from the various included Access module
|
10
|
+
# methods.
|
11
|
+
#
|
12
|
+
class SRef < Element
|
13
|
+
|
14
|
+
# Include various record accessors
|
15
|
+
include Access::XY
|
16
|
+
include Access::ELFlags
|
17
|
+
include Access::Plex
|
18
|
+
include Access::StransGroup
|
19
|
+
include Access::Sname
|
20
|
+
|
21
|
+
#
|
22
|
+
# SRef BNF description:
|
23
|
+
#
|
24
|
+
# <sref> ::= SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY
|
25
|
+
#
|
26
|
+
self.bnf_spec = BnfSpec.new(
|
27
|
+
BnfItem.new(GRT_SREF),
|
28
|
+
BnfItem.new(GRT_ELFLAGS, true),
|
29
|
+
BnfItem.new(GRT_PLEX, true),
|
30
|
+
BnfItem.new(GRT_SNAME),
|
31
|
+
BnfItem.new(Strans, true),
|
32
|
+
BnfItem.new(GRT_XY),
|
33
|
+
BnfItem.new(Properties, true),
|
34
|
+
BnfItem.new(GRT_ENDEL)
|
35
|
+
)
|
36
|
+
|
37
|
+
#
|
38
|
+
# Create a structure reference (SREF) within a Structure object (also
|
39
|
+
# known as a structure "instantiation").
|
40
|
+
#
|
41
|
+
# struct1 = Gdsii::Structure.new('top')
|
42
|
+
# struct2 = Gdsii::Structure.new('sub')
|
43
|
+
# struct1.add SRef.new('sub')
|
44
|
+
#
|
45
|
+
# Alternatively, any object with a #to_s method can be passed and the
|
46
|
+
# #to_s method will be used to coerce the object into a string. For
|
47
|
+
# example, a structure object itself can be used (instead of the structure
|
48
|
+
# name) through Structure#to_s:
|
49
|
+
#
|
50
|
+
# struct1.add SRef.new(struct2)
|
51
|
+
#
|
52
|
+
def initialize(sname=nil, xy=nil)
|
53
|
+
super()
|
54
|
+
@records[GRT_SREF] = Record.new(GRT_SREF)
|
55
|
+
self.sname = sname.to_s unless sname.nil?
|
56
|
+
self.xy = xy unless xy.nil?
|
57
|
+
yield self if block_given?
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/gdsii/strans.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'gdsii/group'
|
2
|
+
|
3
|
+
module Gdsii
|
4
|
+
|
5
|
+
#
|
6
|
+
# Represents a GDSII structure translation object (Strans).
|
7
|
+
#
|
8
|
+
class Strans < Group
|
9
|
+
|
10
|
+
#
|
11
|
+
# Strans BNF description:
|
12
|
+
#
|
13
|
+
# <strans> ::= STRANS [MAG] [ANGLE]
|
14
|
+
#
|
15
|
+
self.bnf_spec = BnfSpec.new(
|
16
|
+
BnfItem.new(GRT_STRANS),
|
17
|
+
BnfItem.new(GRT_MAG, true),
|
18
|
+
BnfItem.new(GRT_ANGLE, true)
|
19
|
+
)
|
20
|
+
|
21
|
+
# Constructor
|
22
|
+
def initialize(mag=nil, angle=nil, reflect_x=false, abs_mag=false, abs_angle=false)
|
23
|
+
super()
|
24
|
+
@records[GRT_STRANS] = Record.new(GRT_STRANS, 0)
|
25
|
+
self.reflect_x = true if reflect_x
|
26
|
+
self.abs_mag = true if abs_mag
|
27
|
+
self.abs_angle = true if abs_angle
|
28
|
+
self.mag = mag unless mag.nil?
|
29
|
+
self.angle = angle unless angle.nil?
|
30
|
+
yield self if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Get the strans bitarray (returns Record)
|
35
|
+
#
|
36
|
+
def record() @records.get(GRT_STRANS); end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Get the strans bitarray data (returns Fixnum). The recommendation is to
|
40
|
+
# not access this directly but rather use the various bitwise query
|
41
|
+
# methods instead: #reflect_x?, #abs_angle?, #abs_mag?.
|
42
|
+
#
|
43
|
+
def value() @records.get_data(GRT_STRANS); end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Set the strans bitarray record. The recommendation is to not access
|
47
|
+
# this directly but rather use the various bitwise manipulation methods
|
48
|
+
# instead: #reflect_x=, #abs_angle=, #abs_mag=.
|
49
|
+
#
|
50
|
+
# * 15 = reflect_x
|
51
|
+
# * 2 = abs_mag
|
52
|
+
# * 1 = abs_angle
|
53
|
+
# * All others reserved for future use.
|
54
|
+
#
|
55
|
+
def value=(val)
|
56
|
+
@records.set(GRT_STRANS,val);
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Get the strans angle (returns Record)
|
61
|
+
#
|
62
|
+
def angle_record() @records.get_data(GRT_ANGLE); end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Get the strans angle value (returns Fixnum)
|
66
|
+
#
|
67
|
+
def angle() @records.get_data(GRT_ANGLE); end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Set the strans angle record
|
71
|
+
#
|
72
|
+
def angle=(val) @records.set(GRT_ANGLE,val); end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Get the strans magnification (returns Record)
|
76
|
+
#
|
77
|
+
def mag_record() @records.get_data(GRT_MAG); end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Get the strans magnification value (returns Fixnum)
|
81
|
+
#
|
82
|
+
def mag() @records.get_data(GRT_MAG); end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Set the strans magnification record
|
86
|
+
#
|
87
|
+
def mag=(val) @records.set(GRT_MAG,val); end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Return true if the translation bitarray indicates that a reflection
|
91
|
+
# about the x-axis is set.
|
92
|
+
#
|
93
|
+
def reflect_x?()
|
94
|
+
(value & 0x8000) == 0x8000
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Set or clear the strans x-reflect bit (true = set; false = clear)
|
99
|
+
#
|
100
|
+
def reflect_x=(flag)
|
101
|
+
self.value = flag ? value | 0x8000 : value & 0x7fff
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Return true if an absolute magnification is set; false if not
|
106
|
+
#
|
107
|
+
def abs_mag?()
|
108
|
+
(value & 0x0004) == 0x0004
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Set or clear the absolute magnification bit (true = set; false = clear)
|
113
|
+
#
|
114
|
+
def abs_mag=(flag)
|
115
|
+
self.value = flag ? value | 0x0004 : value & 0xfffb
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Return true if the absolute angle bit is set; false if not
|
120
|
+
#
|
121
|
+
def abs_angle?()
|
122
|
+
(value & 0x0002) == 0x0002
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Set the strans absAngle bit
|
127
|
+
#
|
128
|
+
def abs_angle=(flag)
|
129
|
+
self.value = flag ? value | 0x0002 : value & 0xfffd
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'gdsii/mixins'
|
3
|
+
require 'gdsii/element'
|
4
|
+
require 'gdsii/boundary'
|
5
|
+
require 'gdsii/path'
|
6
|
+
require 'gdsii/text'
|
7
|
+
require 'gdsii/node'
|
8
|
+
require 'gdsii/box'
|
9
|
+
require 'gdsii/sref'
|
10
|
+
require 'gdsii/aref'
|
11
|
+
|
12
|
+
module Gdsii
|
13
|
+
|
14
|
+
#
|
15
|
+
# Represents a GDSII Structure.
|
16
|
+
#
|
17
|
+
class Structure < Group
|
18
|
+
|
19
|
+
include Access::GdsiiTime
|
20
|
+
|
21
|
+
#
|
22
|
+
# Structure BNF description:
|
23
|
+
#
|
24
|
+
# <structure> ::= BGNSTR STRNAME [STRCLASS] {<element>}* ENDSTR
|
25
|
+
# <element> ::= {<boundary> | <path> | <sref> | <aref> |
|
26
|
+
# <text> | <node> | <box>} {<property>}* ENDEL
|
27
|
+
#
|
28
|
+
self.bnf_spec = BnfSpec.new(
|
29
|
+
BnfItem.new(GRT_BGNSTR),
|
30
|
+
BnfItem.new(GRT_STRNAME),
|
31
|
+
BnfItem.new(GRT_STRCLASS, true),
|
32
|
+
BnfItem.new(Elements, true),
|
33
|
+
BnfItem.new(GRT_ENDSTR)
|
34
|
+
)
|
35
|
+
|
36
|
+
#
|
37
|
+
# Creates a Structure object. Various GDSII Elements are added to a
|
38
|
+
# structure such as a Boundary, Path, SRef, ARef, Text, Node, and Box.
|
39
|
+
#
|
40
|
+
# str_sub = Structure.new('sub')
|
41
|
+
# str_top = Structure.new('top')
|
42
|
+
# str_top.add SRef.new(str_sub)
|
43
|
+
# str_top.add Boundary.new(1, 0, [0,0, 0,10, 10,10, 10,0, 0,0])
|
44
|
+
#
|
45
|
+
def initialize(name=nil)
|
46
|
+
# Create the record grouping
|
47
|
+
super()
|
48
|
+
@records[Elements] = Elements.new
|
49
|
+
@records[GRT_ENDSTR] = Record.new(GRT_ENDSTR)
|
50
|
+
|
51
|
+
# set the name
|
52
|
+
self.name = name unless name.nil?
|
53
|
+
|
54
|
+
# Set create/modify time to the current time
|
55
|
+
now = Time.now
|
56
|
+
self.create_time = now
|
57
|
+
self.modify_time = now
|
58
|
+
|
59
|
+
yield self if block_given?
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Access to the Elements object. See Elements for a listing of methods.
|
64
|
+
#
|
65
|
+
def elements(); @records.get(Elements); end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Shortcut for Elements#add. For example, instead of:
|
69
|
+
#
|
70
|
+
# struct = Structure.new('test')
|
71
|
+
# struct.elements.add Text(1, 0, [0,0], 'hello')
|
72
|
+
#
|
73
|
+
# It could be simply:
|
74
|
+
#
|
75
|
+
# struct.add Text(1, 0, [0,0], 'hello')
|
76
|
+
#
|
77
|
+
def add(*args); elements.add(*args); end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Shortcut for Elements#remove. For example, instead of:
|
81
|
+
#
|
82
|
+
# struct.elements.remove {|e| e.class == Gdsii::Text}
|
83
|
+
#
|
84
|
+
# It could be simply:
|
85
|
+
#
|
86
|
+
# struct.remove {|e| e.class == Gdsii::Text}
|
87
|
+
#
|
88
|
+
def remove(*args); elements.remove(*args); end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Get the Structure STRNAME record (returns Record).
|
92
|
+
#
|
93
|
+
def name_record() @records.get(GRT_STRNAME); end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Get the Structure name (returns String).
|
97
|
+
#
|
98
|
+
def name() @records.get_data(GRT_STRNAME); end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Set the Structure name.
|
102
|
+
#
|
103
|
+
def name=(val) @records.set(GRT_STRNAME, val); end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Get the strclass record (returns Record).
|
107
|
+
#
|
108
|
+
def strclass_record() @records.get(GRT_STRCLASS); end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Get the strclass bitarray number (returns Fixnum).
|
112
|
+
#
|
113
|
+
def strclass() @records.get_data(GRT_STRCLASS); end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Set the strclass bitarray number. According to the GDSII specification,
|
117
|
+
# this is only to be used by Calma - otherwise it should be omitted or
|
118
|
+
# set to 0. It is probably a good idea to not touch this property.
|
119
|
+
#
|
120
|
+
def strclass=(val) @records.set(GRT_STRCLASS, val); end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Get the bgnstr record (returns Record).
|
124
|
+
#
|
125
|
+
def bgnstr_record() @records.get(GRT_BGNSTR); end
|
126
|
+
|
127
|
+
#
|
128
|
+
# Get the bgnstr number (returns Fixnum). This holds the create/modify
|
129
|
+
# time stamp for the structure. It is probably easier to not access this
|
130
|
+
# directly but to use #create_time and #modify_time instead.
|
131
|
+
#
|
132
|
+
def bgnstr() @records.get_data(GRT_BGNSTR); end
|
133
|
+
|
134
|
+
#
|
135
|
+
# Set the bgnstr number. The value is a Fixnum representation of the
|
136
|
+
# create/modify time stamp for the structure. It is probably easier to
|
137
|
+
# not access this directly but to use #create_time= and #modify_time=
|
138
|
+
# instead.
|
139
|
+
#
|
140
|
+
def bgnstr=(val) @records.set(GRT_BGNSTR, val); end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Accepts a Time object and sets the create time for the structure.
|
144
|
+
#
|
145
|
+
# struct.create_time = Time.now
|
146
|
+
#
|
147
|
+
def create_time=(time)
|
148
|
+
@create_time = time
|
149
|
+
update_times
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Returns the create time for this structure (returns Time)
|
154
|
+
#
|
155
|
+
def create_time(); @create_time; end
|
156
|
+
|
157
|
+
#
|
158
|
+
# Accepts a Time object and sets the modify time for the structure.
|
159
|
+
#
|
160
|
+
# struct.modify_time = Time.now
|
161
|
+
#
|
162
|
+
def modify_time=(time)
|
163
|
+
@modify_time = time
|
164
|
+
update_times
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Returns the modify time for this structure (returns Time)
|
169
|
+
#
|
170
|
+
def modify_time(); @modify_time; end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Reads records related to a Structure header from the given file handle.
|
174
|
+
# It is assumed that the file position is already at BGNSTR (likely after
|
175
|
+
# Library#read_header). The structure is yielded (if desired) and
|
176
|
+
# returned. The iterative version of this method is likely preferable
|
177
|
+
# in most cases (Structure#read_each_header).
|
178
|
+
#
|
179
|
+
def Structure.read_header(file)
|
180
|
+
Structure.read(file, nil, nil, :before_group) {|struct|
|
181
|
+
yield struct if block_given?
|
182
|
+
break struct
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
#
|
187
|
+
# Reads each Structure and its Elements from the given file handle. Each
|
188
|
+
# Structure is yielded after the entire Structure is read from the file.
|
189
|
+
# Compare this with Structure#read_each_header which might be more
|
190
|
+
# efficient, more streamlined, and consume less memory.
|
191
|
+
#
|
192
|
+
# The Library#read_header method must be called as a prerequisite (the file
|
193
|
+
# handle must be at a BGNSTR record).
|
194
|
+
#
|
195
|
+
# File.open(file_name, 'rb') do |file|
|
196
|
+
# Library.read_header(file) do |lib|
|
197
|
+
# Structure.read_each(file) do |struct|
|
198
|
+
# puts "#{struct.name} has #{struct.elements.length} elements"
|
199
|
+
# end
|
200
|
+
# end
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
def Structure.read_each(file)
|
204
|
+
while (Record.peek(file).type == GRT_BGNSTR) do
|
205
|
+
yield Structure.read(file)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# Reads the Structure header records from a file handle but without reading
|
211
|
+
# any Element records. The resulting Structure element is yielded. This
|
212
|
+
# is useful for using the high-level GDSII access methods as a stream file
|
213
|
+
# is being read in.
|
214
|
+
#
|
215
|
+
# Prior to using this method, the file position must be at the first
|
216
|
+
# structure definition (i.e. after the Library header). The best method
|
217
|
+
# to do this is to call Library#read_header first.
|
218
|
+
#
|
219
|
+
# Also, you _MUST_ advance the file position to the next structure record
|
220
|
+
# header (BGNSTR) either with Structure#seek_next or
|
221
|
+
# Structure#read_each_element within the code block. Otherwise the file
|
222
|
+
# pointer will not be advanced properly and only the first Structure will
|
223
|
+
# be yielded.
|
224
|
+
#
|
225
|
+
# File.open(file_name, 'rb') do |file|
|
226
|
+
# Library.read_header(file) do |lib|
|
227
|
+
# Structure.read_each_header(file) do |struct|
|
228
|
+
# if struct.name == 'TOP'
|
229
|
+
# # Show all elements in structure "TOP"
|
230
|
+
# puts "Processing structure #{struct.name}"
|
231
|
+
# Element.read_each(file) do |element|
|
232
|
+
# puts "--> Element: #{element.class}"
|
233
|
+
# end
|
234
|
+
# else
|
235
|
+
# # Skip past elements in other blocks
|
236
|
+
# puts "Ignoring structure #{struct.name}"
|
237
|
+
# Structure.seek_next(file)
|
238
|
+
# end
|
239
|
+
# end
|
240
|
+
# end
|
241
|
+
# end
|
242
|
+
#
|
243
|
+
def Structure.read_each_header(file)
|
244
|
+
while (Record.peek(file).type == GRT_BGNSTR) do
|
245
|
+
yield Structure.read_header(file)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
#
|
250
|
+
# Reads from the given file handle until a ENDSTR record is met (presumably
|
251
|
+
# to a BGNSTR or ENDLIB record. This effectively "skips" past all elements
|
252
|
+
# within a structure and prepares the file handle to read the next
|
253
|
+
# structure in the file (or ENDLIB if at the end of the GDSII library).
|
254
|
+
# See Structure#read_each_header for an example. The new file position is
|
255
|
+
# returned.
|
256
|
+
#
|
257
|
+
# Compare this with Element#read_each which accomplishes the same thing
|
258
|
+
# but instead yields each element as it is read from the file.
|
259
|
+
#
|
260
|
+
def Structure.seek_next(file)
|
261
|
+
Record.read_each(file) do |record|
|
262
|
+
break file.pos if record.is_endstr?
|
263
|
+
end
|
264
|
+
nil
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# Writes only the header portion of the Structure to a file (no elements).
|
269
|
+
# This is useful when streamlined writing is desired (for better
|
270
|
+
# performance or when writing GDSII as another GDSII is being read). Be
|
271
|
+
# sure to either:
|
272
|
+
#
|
273
|
+
# 1. Call #write_footer after writing the header and any Element
|
274
|
+
# objects. Also be sure to wrap this around Library#write_header and
|
275
|
+
# Library#write_footer; Or
|
276
|
+
# 2. Pass a block whereupon the footer will automatically be added after
|
277
|
+
# the block is processed.
|
278
|
+
#
|
279
|
+
# See Library#write_header for an example.
|
280
|
+
#
|
281
|
+
def write_header(file)
|
282
|
+
# alter the BNF to exclude Elements and ENDSTR; then write to file
|
283
|
+
# according to the modified BNF
|
284
|
+
alt_bnf = BnfSpec.new(*Structure.bnf_spec.bnf_items[0..-3])
|
285
|
+
self.write(file, alt_bnf)
|
286
|
+
|
287
|
+
# if block is given, then yield to that block and then write the
|
288
|
+
# footer afterwards
|
289
|
+
if block_given?
|
290
|
+
yield
|
291
|
+
self.write_footer(file)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
#
|
296
|
+
# Writes only the Structure footer (just ENDSTR record) to file. To be
|
297
|
+
# used with #write_header.
|
298
|
+
#
|
299
|
+
def write_footer(file)
|
300
|
+
Record.new(GRT_ENDSTR).write(file)
|
301
|
+
end
|
302
|
+
|
303
|
+
#####################
|
304
|
+
## PRIVATE METHODS ##
|
305
|
+
#####################
|
306
|
+
|
307
|
+
private
|
308
|
+
|
309
|
+
# Used by #create_time and #modify_time
|
310
|
+
def update_times()
|
311
|
+
if create_time and modify_time
|
312
|
+
self.bgnstr = build_time(create_time) + build_time(modify_time)
|
313
|
+
else
|
314
|
+
self.bgnstr = nil
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
############################################################################
|
321
|
+
|
322
|
+
#
|
323
|
+
# Class to hold a collection of Structure objects. This is used in the
|
324
|
+
# Library class BNF.
|
325
|
+
#
|
326
|
+
class Structures < Group
|
327
|
+
|
328
|
+
include Access::EnumerableGroup
|
329
|
+
|
330
|
+
#
|
331
|
+
# Structures BNF description:
|
332
|
+
#
|
333
|
+
# <structures> ::= {<structure>}*
|
334
|
+
# <structure> ::= {<boundary> | <path> | <sref> | <aref> |
|
335
|
+
# <text> | <node> | <box>} {<property>}* ENDEL
|
336
|
+
#
|
337
|
+
self.bnf_spec = BnfSpec.new(
|
338
|
+
BnfItem.new(Structure, true, true)
|
339
|
+
)
|
340
|
+
|
341
|
+
#
|
342
|
+
# Create an Structures object.
|
343
|
+
#
|
344
|
+
def initialize(structures=[])
|
345
|
+
super()
|
346
|
+
@records[Structure] = @list = structures
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
end
|