ruby-gdsii 1.0.0
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.
- 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
|