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.
- data/CHANGELOG.txt +6 -0
- data/LICENSE.txt +20 -0
- data/README.txt +113 -0
- data/Rakefile +30 -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 +136 -0
- data/bin/rgds-ssplit +113 -0
- data/bin/rgds-stats +134 -0
- data/bin/rgds-structs +41 -0
- data/bin/rgds-tree +167 -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 +46 -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 +117 -0
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'gdsii/group'
|
2
|
+
require 'gdsii/property'
|
3
|
+
|
4
|
+
module Gdsii
|
5
|
+
|
6
|
+
#
|
7
|
+
# Generic class to be inherited by various GDSII elements (i.e. things that
|
8
|
+
# can be added to a Structure).
|
9
|
+
#
|
10
|
+
class Element < Group
|
11
|
+
|
12
|
+
# No BNF for generic Element; refer to actual Element itself (i.e.
|
13
|
+
# Boundary, Path, etc.)
|
14
|
+
|
15
|
+
#
|
16
|
+
# Generic element constructor. Not intended to be called directly but
|
17
|
+
# rather inherited and called through sub-classes such as Gdsii::Boundary.
|
18
|
+
#
|
19
|
+
def initialize()
|
20
|
+
super()
|
21
|
+
@records[GRT_ENDEL] = Record.new(GRT_ENDEL)
|
22
|
+
@records[Properties] = @properties = Properties.new
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Shortcut for Properties#add. For example, instead of:
|
27
|
+
#
|
28
|
+
# bnd.properties.add Property(1, 'testprop')
|
29
|
+
#
|
30
|
+
# It could be simply:
|
31
|
+
#
|
32
|
+
# bnd.add Property(1, 'testprop')
|
33
|
+
#
|
34
|
+
def add(*args); properties.add(*args); end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Shortcut for Properties#remove. For example, instead of:
|
38
|
+
#
|
39
|
+
# bnd.properties.remove {|p| p.attr == 1}
|
40
|
+
#
|
41
|
+
# It could be simply:
|
42
|
+
#
|
43
|
+
# bnd.remove {|p| p.attr == 1}
|
44
|
+
#
|
45
|
+
def remove(*args); properties.remove(*args); end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Access the Properties object for this element
|
49
|
+
#
|
50
|
+
def properties(); @properties; end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
alias :read_el :read
|
54
|
+
end
|
55
|
+
|
56
|
+
def Element.read(file, *args)
|
57
|
+
rec = Record.peek(file)
|
58
|
+
case rec.type
|
59
|
+
when GRT_BOUNDARY : Boundary.read_el(file, *args)
|
60
|
+
when GRT_TEXT : Text.read_el(file, *args)
|
61
|
+
when GRT_PATH : Path.read_el(file, *args)
|
62
|
+
when GRT_SREF : SRef.read_el(file, *args)
|
63
|
+
when GRT_AREF : ARef.read_el(file, *args)
|
64
|
+
when GRT_BOX : Box.read_el(file, *args)
|
65
|
+
when GRT_NODE : Node.read_el(file, *args)
|
66
|
+
else
|
67
|
+
# end of the element, increment the counter and move on
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# After a Structure header has been read (see Structure#read_each_header)
|
74
|
+
# then elements may be processed as a file is read using Element#read_each.
|
75
|
+
# See Structure#read_each_header for an example.
|
76
|
+
#
|
77
|
+
# Compare this with Structure#seek_next which also advances the file handle
|
78
|
+
# to the next structure but does not yield any elements (if only a file
|
79
|
+
# pointer advancement is needed and elements can be ignored).
|
80
|
+
#
|
81
|
+
def Element.read_each(file)
|
82
|
+
while (group = Element.read(file)) do
|
83
|
+
yield group
|
84
|
+
end
|
85
|
+
# rip out ENDEL - TODO: make sure that it's ENDEL
|
86
|
+
Record.read(file)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Returns true if this is a Boundary element
|
91
|
+
#
|
92
|
+
def is_boundary?; self.class == Boundary; end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Returns true if this is a Path element
|
96
|
+
#
|
97
|
+
def is_path?; self.class == Path; end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Returns true if this is a Text element
|
101
|
+
#
|
102
|
+
def is_text?; self.class == Text; end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Returns true if this is a SRef element
|
106
|
+
#
|
107
|
+
def is_sref?; self.class == SRef; end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Returns true if this is a ARef element
|
111
|
+
#
|
112
|
+
def is_aref?; self.class == ARef; end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Returns true if this is a Box element
|
116
|
+
#
|
117
|
+
def is_box?; self.class == Box; end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Returns true if this is a Node element
|
121
|
+
#
|
122
|
+
def is_node?; self.class == Node; end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
############################################################################
|
127
|
+
|
128
|
+
#
|
129
|
+
# Class to hold a collection of Element objects. This is used in the
|
130
|
+
# Structure class BNF.
|
131
|
+
#
|
132
|
+
# Elements include: Boundary, Path, SRef, ARef, Text, Node, Box
|
133
|
+
#
|
134
|
+
class Elements < Group
|
135
|
+
|
136
|
+
include Access::EnumerableGroup
|
137
|
+
|
138
|
+
#
|
139
|
+
# Elements BNF description:
|
140
|
+
#
|
141
|
+
# <structure> ::= {<element>}*
|
142
|
+
# <element> ::= {<boundary> | <path> | <sref> | <aref> |
|
143
|
+
# <text> | <node> | <box>} {<property>}* ENDEL
|
144
|
+
#
|
145
|
+
self.bnf_spec = BnfSpec.new(
|
146
|
+
BnfItem.new(Element, true, true)
|
147
|
+
)
|
148
|
+
|
149
|
+
#
|
150
|
+
# Create an Elements object.
|
151
|
+
#
|
152
|
+
def initialize(elements=[])
|
153
|
+
super()
|
154
|
+
@records[Element] = @list = elements
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
#######################
|
159
|
+
## PROTECTED METHODS ##
|
160
|
+
#######################
|
161
|
+
|
162
|
+
protected
|
163
|
+
|
164
|
+
# Used by Access::EnumerableGroup to validate addition
|
165
|
+
def validate_addition(object)
|
166
|
+
unless object.kind_of?(Element)
|
167
|
+
raise TypeError, "Invalid addition: #{object.class}; expecting Gdsii::Element"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
data/lib/gdsii/group.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'gdsii/mixins'
|
2
|
+
require 'gdsii/record'
|
3
|
+
require 'gdsii/bnf'
|
4
|
+
|
5
|
+
module Gdsii
|
6
|
+
|
7
|
+
#
|
8
|
+
# Generic base class for a GDSII grouping of records (i.e. a boundary object
|
9
|
+
# and all records associated with it). This class will not be used directly
|
10
|
+
# but will be inherited by the various record groupings.
|
11
|
+
#
|
12
|
+
class Group
|
13
|
+
|
14
|
+
extend Read
|
15
|
+
|
16
|
+
attr_reader :records
|
17
|
+
|
18
|
+
#
|
19
|
+
# Constructor of a generic GDSII record grouping. Not intended to be
|
20
|
+
# called directly but rather from subclasses which inherit this class.
|
21
|
+
#
|
22
|
+
def initialize()
|
23
|
+
@records = BnfRecords.new(self.class)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Simply yields itself for configuration (i.e. makes for prettier code).
|
28
|
+
# The object itself is returned.
|
29
|
+
#
|
30
|
+
def configure()
|
31
|
+
yield self
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Write the record grouping to a file. A file name as a String can be
|
37
|
+
# passed in which case a new file by the given name is opened in write
|
38
|
+
# mode ('w'). Alternatively, a file object may be passed in which case
|
39
|
+
# the record grouping are written to the file object. Examples (assumes
|
40
|
+
# "object" has been initialized and descends from this class):
|
41
|
+
#
|
42
|
+
# object.write('mydesign.gds')
|
43
|
+
#
|
44
|
+
# or
|
45
|
+
#
|
46
|
+
# # Note: 'wb' is required for DOS/Windows compatibility
|
47
|
+
# File.open('otherdesign.gds', 'wb') do |file|
|
48
|
+
# object.write(file)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
def write(file, alt_bnf=nil)
|
52
|
+
# If the file specified is a string, then open it up for writing. If it
|
53
|
+
# is a file open it for writing if it is not already open
|
54
|
+
if file.class == String
|
55
|
+
file = File.open(file,"wb")
|
56
|
+
elsif file.class == File
|
57
|
+
file = File.open(file,"wb") if file.closed?
|
58
|
+
else
|
59
|
+
raise TypeError, "Invalid file object given: #{file}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Write to file according to BNF
|
63
|
+
@records.write(file, alt_bnf)
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Runs a code block to validate the object if the validate attribute is
|
68
|
+
# set. This is typically run to check record grouping integrity during
|
69
|
+
# read/write of GDSII files.
|
70
|
+
#
|
71
|
+
def validate()
|
72
|
+
if @validate
|
73
|
+
@validate.call
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Set class instance variables to be used in subclasses.
|
79
|
+
#
|
80
|
+
class << self
|
81
|
+
|
82
|
+
# Set the class bnf array
|
83
|
+
def bnf_spec=(value); @bnf = value; end
|
84
|
+
|
85
|
+
# Return class bnf array
|
86
|
+
def bnf_spec(); @bnf; end
|
87
|
+
|
88
|
+
# Set the BNF key for this class
|
89
|
+
def bnf_key=(value); @bnf_key = value; end
|
90
|
+
|
91
|
+
# Get the BNF key for this class (default is the instantiating class)
|
92
|
+
def bnf_key(); (@bnf_key.nil?) ? self : @bnf_key ; end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,518 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'gdsii/mixins'
|
3
|
+
require 'gdsii/group'
|
4
|
+
require 'gdsii/structure'
|
5
|
+
|
6
|
+
module Gdsii
|
7
|
+
|
8
|
+
DEF_LIB_VERSION = 5
|
9
|
+
DEF_LIB_UNITS = [0.001,1.0e-09]
|
10
|
+
|
11
|
+
#
|
12
|
+
# Represents a GDSII Library.
|
13
|
+
#
|
14
|
+
class Library < Group
|
15
|
+
|
16
|
+
include Access::GdsiiTime
|
17
|
+
|
18
|
+
#
|
19
|
+
# Library BNF description:
|
20
|
+
#
|
21
|
+
# <lib> ::= HEADER <libheader> {<structure>}* ENDLIB
|
22
|
+
# <libheader> ::= BGNLIB [LIBDIRSIZE] [SRFNAME] [LIBSECUR]
|
23
|
+
# LIBNAME [REFLIBS] [FONTS] [ATTRTABLE] [GENERATIONS]
|
24
|
+
# [<FormatType>] UNITS
|
25
|
+
# <FormatType> ::= FORMAT | FORMAT {MASK}+ ENDMASKS
|
26
|
+
#
|
27
|
+
self.bnf_spec = BnfSpec.new(
|
28
|
+
BnfItem.new(GRT_HEADER),
|
29
|
+
BnfItem.new(GRT_BGNLIB),
|
30
|
+
BnfItem.new(GRT_LIBDIRSIZE,true),
|
31
|
+
BnfItem.new(GRT_SRFNAME, true),
|
32
|
+
BnfItem.new(GRT_LIBSECUR, true),
|
33
|
+
BnfItem.new(GRT_LIBNAME),
|
34
|
+
BnfItem.new(GRT_REFLIBS, true),
|
35
|
+
BnfItem.new(GRT_FONTS, true),
|
36
|
+
BnfItem.new(GRT_ATTRTABLE, true),
|
37
|
+
BnfItem.new(GRT_GENERATIONS, true),
|
38
|
+
BnfItem.new(GRT_FORMAT, true),
|
39
|
+
BnfItem.new(GRT_MASK, true, true),
|
40
|
+
BnfItem.new(GRT_ENDMASKS, true),
|
41
|
+
BnfItem.new(GRT_UNITS),
|
42
|
+
BnfItem.new(Structures, true),
|
43
|
+
BnfItem.new(GRT_ENDLIB)
|
44
|
+
)
|
45
|
+
|
46
|
+
#
|
47
|
+
# Create a new GDSII Library object.
|
48
|
+
#
|
49
|
+
# lib = Library.new('MYDESIGN.DB')
|
50
|
+
#
|
51
|
+
# The units may be specified during construction:
|
52
|
+
#
|
53
|
+
# lib2 = Library.new('OTHER.DB', [0.001, 1e-9])
|
54
|
+
#
|
55
|
+
def initialize(name=nil, units=DEF_LIB_UNITS)
|
56
|
+
super()
|
57
|
+
@records[Structure] = []
|
58
|
+
@records[GRT_ENDLIB] = Record.new(GRT_ENDLIB)
|
59
|
+
|
60
|
+
self.header = DEF_LIB_VERSION
|
61
|
+
self.name = name unless name.nil?
|
62
|
+
self.units = units
|
63
|
+
|
64
|
+
# Set modify/access time to the current time
|
65
|
+
now = Time.now
|
66
|
+
self.modify_time = now
|
67
|
+
self.access_time = now
|
68
|
+
|
69
|
+
yield self if block_given?
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Access to the Structures object. See Structures for a listing of
|
74
|
+
# methods.
|
75
|
+
#
|
76
|
+
def structures(); @records.get(Structures); end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Shortcut for Structures#add. For example, instead of:
|
80
|
+
#
|
81
|
+
# lib = Library.new('MYLIB.DB')
|
82
|
+
# lib.structures.add Structure.new('test')
|
83
|
+
#
|
84
|
+
# It could be simply:
|
85
|
+
#
|
86
|
+
# lib.add Structure.new('test')
|
87
|
+
#
|
88
|
+
def add(*args); structures.add(*args); end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Shortcut for Structures#remove. For example, instead of:
|
92
|
+
#
|
93
|
+
# lib.structures.remove {|s| true}
|
94
|
+
#
|
95
|
+
# It could be simply:
|
96
|
+
#
|
97
|
+
# lib.remove {|s| true}
|
98
|
+
#
|
99
|
+
def remove(*args); structures.remove(*args); end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Get the Library LIBNAME record (returns Record).
|
103
|
+
#
|
104
|
+
def name_record() @records.get(GRT_LIBNAME); end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Get the Library name (returns String).
|
108
|
+
#
|
109
|
+
def name() @records.get_data(GRT_LIBNAME); end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Set the Library name.
|
113
|
+
#
|
114
|
+
def name=(val) @records.set(GRT_LIBNAME, val); end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Get the header record (returns Record).
|
118
|
+
#
|
119
|
+
def header_record() @records.get(GRT_HEADER); end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Get the header number; this is the GDSII version (returns Fixnum).
|
123
|
+
#
|
124
|
+
def header() @records.get_data(GRT_HEADER); end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Set the header number; this is the GDSII version. Valid numbers are
|
128
|
+
# 3, 4, 5, 6, and 7. The default version used is defined by the
|
129
|
+
# constant DEF_LIB_VER.
|
130
|
+
#
|
131
|
+
def header=(val) @records.set(GRT_HEADER, val); end
|
132
|
+
|
133
|
+
alias :version= :header=
|
134
|
+
alias :version :header
|
135
|
+
|
136
|
+
#
|
137
|
+
# Get the library directory size LIBDIRSIZE record (returns Record).
|
138
|
+
#
|
139
|
+
def dirsize_record() @records.get(GRT_LIBDIRSIZE); end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Get the number of pages in the library directory (returns Fixnum). This
|
143
|
+
# is likely an old Calma record and is likely unused except in rare
|
144
|
+
# circumstances.
|
145
|
+
#
|
146
|
+
def dirsize() @records.get_data(GRT_LIBDIRSIZE); end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Set the number of pages in the library directory (see #dirsize for more
|
150
|
+
# information).
|
151
|
+
#
|
152
|
+
def dirsize=(val) @records.set(GRT_LIBDIRSIZE, val); end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Get the Library SRFNAME record (returns Record).
|
156
|
+
#
|
157
|
+
def srfname_record() @records.get(GRT_SRFNAME); end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Get the Library Calma sticks rule file name (returns String). This
|
161
|
+
# is likely unused except in rare circumstances.
|
162
|
+
#
|
163
|
+
def srfname() @records.get_data(GRT_SRFNAME); end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Set the Library Calma sticks rule file name (see #srfname for details).
|
167
|
+
#
|
168
|
+
def srfname=(val) @records.set(GRT_SRFNAME, val); end
|
169
|
+
|
170
|
+
#
|
171
|
+
# Get the library security LIBSECUR record (returns Record).
|
172
|
+
#
|
173
|
+
def secur_record() @records.get(GRT_LIBSECUR); end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Get the secur number (returns Fixnum). This is an array of 1-32
|
177
|
+
# elements of an array of 3 elements; each containing Fixnum representing
|
178
|
+
# (respectively): group number, user number, and access rights. Since this
|
179
|
+
# appears to be rarely used, no high-level methods are given to access this
|
180
|
+
# record. Returns an Array of Fixnum.
|
181
|
+
#
|
182
|
+
def secur() @records.get_data(GRT_LIBSECUR); end
|
183
|
+
|
184
|
+
#
|
185
|
+
# Set the library security number (see #secur for details)
|
186
|
+
#
|
187
|
+
def secur=(val) @records.set(GRT_LIBSECUR, val); end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Get the fonts record (returns Record).
|
191
|
+
#
|
192
|
+
def fonts_record() @records.get(GRT_FONTS); end
|
193
|
+
|
194
|
+
#
|
195
|
+
# Get the array of paths to font definition files. If this record exists,
|
196
|
+
# then exactly 4 array elements should exist. Each array element is a
|
197
|
+
# String with a maximum of 44 characters. Returns Array of Strings.
|
198
|
+
#
|
199
|
+
def fonts() @records.get_data(GRT_FONTS); end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Set the path to 4 font definition files. See #fonts for more details.
|
203
|
+
#
|
204
|
+
def fonts=(val) @records.set(GRT_FONTS, val); end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Get the attribute table file location ATTRTABLE record (returns Record).
|
208
|
+
#
|
209
|
+
def attrtable_record() @records.get(GRT_ATTRTABLE); end
|
210
|
+
|
211
|
+
#
|
212
|
+
# Get the attribute table file location. This is a String with a maximum
|
213
|
+
# of 44 characters in length. Returns String.
|
214
|
+
#
|
215
|
+
def attrtable() @records.get_data(GRT_ATTRTABLE); end
|
216
|
+
|
217
|
+
#
|
218
|
+
# Set the attribute table file location. See #attrtable for more details.
|
219
|
+
#
|
220
|
+
def attrtable=(val) @records.set(GRT_ATTRTABLE, val); end
|
221
|
+
|
222
|
+
#
|
223
|
+
# Get the generations record (returns Record).
|
224
|
+
#
|
225
|
+
def generations_record() @records.get(GRT_GENERATIONS); end
|
226
|
+
|
227
|
+
#
|
228
|
+
# Get the generations number (returns Fixnum). This number represents
|
229
|
+
# how many structures should be retained as backup. This is likely
|
230
|
+
# rarely used.
|
231
|
+
#
|
232
|
+
def generations() @records.get_data(GRT_GENERATIONS); end
|
233
|
+
|
234
|
+
#
|
235
|
+
# Set the generations number. See #generations for details.
|
236
|
+
#
|
237
|
+
def generations=(val) @records.set(GRT_GENERATIONS, val); end
|
238
|
+
|
239
|
+
#
|
240
|
+
# Get the format record (returns Record).
|
241
|
+
#
|
242
|
+
def format_record() @records.get(GRT_FORMAT); end
|
243
|
+
|
244
|
+
#
|
245
|
+
# Get the format number (returns Fixnum). This number is used to indicate
|
246
|
+
# if the stream file is an archive and/or filtered:
|
247
|
+
#
|
248
|
+
# 0: Archive
|
249
|
+
# 1: Filtered
|
250
|
+
#
|
251
|
+
def format() @records.get_data(GRT_FORMAT); end
|
252
|
+
|
253
|
+
#
|
254
|
+
# Set the format number. See #format for details.
|
255
|
+
#
|
256
|
+
def format=(val) @records.set(GRT_FORMAT, val); end
|
257
|
+
|
258
|
+
#
|
259
|
+
# True if #format == 0 indicating archive status; false if not.
|
260
|
+
#
|
261
|
+
def archive_format?(); format == 0; end
|
262
|
+
|
263
|
+
#
|
264
|
+
# True if #format == 1 indicating filtered status; false if not.
|
265
|
+
#
|
266
|
+
def filtered_format?(); format == 1; end
|
267
|
+
|
268
|
+
#
|
269
|
+
# Get the mask record (returns Record).
|
270
|
+
#
|
271
|
+
def mask_record() @records.get(GRT_MASK); end
|
272
|
+
|
273
|
+
#
|
274
|
+
# Get the MASK record (returns Array of String). This is only used in
|
275
|
+
# filtered (see #format) stream files. This string represents ranges of
|
276
|
+
# layers and datatypes separated by a semicolon. There can be more than
|
277
|
+
# one MASK defined.
|
278
|
+
#
|
279
|
+
def mask() @records.get_data(GRT_MASK); end
|
280
|
+
|
281
|
+
#
|
282
|
+
# Set the mask number. See #mask for details.
|
283
|
+
#
|
284
|
+
def mask=(val) @records.set(GRT_MASK, val); end
|
285
|
+
|
286
|
+
#
|
287
|
+
# Get the units record (returns Record).
|
288
|
+
#
|
289
|
+
def units_record() @records.get(GRT_UNITS); end
|
290
|
+
|
291
|
+
#
|
292
|
+
# Get the units Array (returns 2 element Array of Float). It may be easier
|
293
|
+
# to use the #db_units, #user_units, and/or #m_units methods instead. The
|
294
|
+
# units record has two parts, respectively:
|
295
|
+
#
|
296
|
+
# 1. User units
|
297
|
+
# 2. Database units
|
298
|
+
#
|
299
|
+
# The units in meters can be found by dividing database units by user units
|
300
|
+
# (this calculation is done in #m_units).
|
301
|
+
#
|
302
|
+
def units(); @records.get_data(GRT_UNITS); end
|
303
|
+
|
304
|
+
#
|
305
|
+
# Set the units number. See #units for details. It may be easier to use
|
306
|
+
# #db_units= or #user_units= instead.
|
307
|
+
#
|
308
|
+
def units=(val)
|
309
|
+
if val.class == Array
|
310
|
+
if val.length == 2
|
311
|
+
@user_units, @database_units = val
|
312
|
+
update_units
|
313
|
+
else
|
314
|
+
raise ArgumentError, "UNITS Array must have exactly 2 elements"
|
315
|
+
end
|
316
|
+
else
|
317
|
+
raise TypeError, "Expecting 2 element Array; given: #{val.class}"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
#
|
322
|
+
# Returns the user units (returns Float). See #units for details.
|
323
|
+
#
|
324
|
+
def user_units(); @user_units; end
|
325
|
+
|
326
|
+
#
|
327
|
+
# Sets the user units. See #units for details.
|
328
|
+
#
|
329
|
+
def user_units=(val)
|
330
|
+
@user_units = val
|
331
|
+
update_units
|
332
|
+
end
|
333
|
+
|
334
|
+
#
|
335
|
+
# Returns the database units (returns Float). See #units for details.
|
336
|
+
#
|
337
|
+
def database_units(); @database_units; end
|
338
|
+
|
339
|
+
#
|
340
|
+
# Sets the database units. See #units for details.
|
341
|
+
#
|
342
|
+
def database_units=(val)
|
343
|
+
@database_units = val
|
344
|
+
update_units
|
345
|
+
end
|
346
|
+
|
347
|
+
#
|
348
|
+
# Get the units in meters (returns Float). Both user and database
|
349
|
+
# units must be set. The formula is:
|
350
|
+
#
|
351
|
+
# m_units = database_units / user_units
|
352
|
+
#
|
353
|
+
def m_units()
|
354
|
+
((u=user_units) and (d=database_units)) ? d/u : nil
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# Get the bgnlib record (returns Record).
|
359
|
+
#
|
360
|
+
def bgnlib_record() @records.get(GRT_BGNLIB); end
|
361
|
+
|
362
|
+
#
|
363
|
+
# Get the bgnlib number (returns Fixnum). This holds the modify/access
|
364
|
+
# time stamp for the library. It is probably easier to not access this
|
365
|
+
# directly but to use #modify_time and #access_time instead.
|
366
|
+
#
|
367
|
+
def bgnlib() @records.get_data(GRT_BGNLIB); end
|
368
|
+
|
369
|
+
#
|
370
|
+
# Set the bgnlib number. The value is a Fixnum representation of the
|
371
|
+
# modify/access time stamp for the library. It is probably easier to
|
372
|
+
# not access this directly but to use #modify_time= and #access_time=
|
373
|
+
# instead.
|
374
|
+
#
|
375
|
+
def bgnlib=(val) @records.set(GRT_BGNLIB, val); end
|
376
|
+
|
377
|
+
#
|
378
|
+
# Accepts a Time object and sets the modify time for the library.
|
379
|
+
#
|
380
|
+
# struct.modify_time = Time.now
|
381
|
+
#
|
382
|
+
def modify_time=(time)
|
383
|
+
@modify_time = time
|
384
|
+
update_times
|
385
|
+
end
|
386
|
+
|
387
|
+
#
|
388
|
+
# Returns the modify time for this library (returns Time)
|
389
|
+
#
|
390
|
+
def modify_time(); @modify_time; end
|
391
|
+
|
392
|
+
#
|
393
|
+
# Accepts a Time object and sets the access time for the library.
|
394
|
+
#
|
395
|
+
# struct.access_time = Time.now
|
396
|
+
#
|
397
|
+
def access_time=(time)
|
398
|
+
@access_time = time
|
399
|
+
update_times
|
400
|
+
end
|
401
|
+
|
402
|
+
#
|
403
|
+
# Returns the access time for this library (returns Time)
|
404
|
+
#
|
405
|
+
def access_time(); @access_time; end
|
406
|
+
|
407
|
+
#
|
408
|
+
# Reads the Library header data of a GDSII file but does not read any
|
409
|
+
# Structure records. The Library object is returned (also yielded if
|
410
|
+
# a block is given).
|
411
|
+
#
|
412
|
+
# File.open(file_name, 'rb') do |file|
|
413
|
+
# Library.read_header(file) do |lib|
|
414
|
+
# puts "The GDSII library name is #{lib.name}"
|
415
|
+
# end
|
416
|
+
# end
|
417
|
+
#
|
418
|
+
# See Structure#read_each and Structure#read_each_header for more
|
419
|
+
# detailed examples
|
420
|
+
#
|
421
|
+
def Library.read_header(file)
|
422
|
+
Library.read(file, nil, nil, :before_group) do |lib|
|
423
|
+
yield lib if block_given?
|
424
|
+
break lib
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
#
|
429
|
+
# Writes only the header portion of the Library to a file. This is useful
|
430
|
+
# when streamlined writing is desired (for better performance or when
|
431
|
+
# writing GDSII as another GDSII is being read). Be sure to either:
|
432
|
+
#
|
433
|
+
# 1. Call #write_footer after writing the header and any Structure
|
434
|
+
# objects; Or
|
435
|
+
# 2. Pass a block whereupon the footer will automatically be added after
|
436
|
+
# the block is processed.
|
437
|
+
#
|
438
|
+
# Example 1 (manually writing header/footer):
|
439
|
+
#
|
440
|
+
# File.open(in_file, 'rb') do |inf|
|
441
|
+
# File.open(out_file, 'wb') do |outf|
|
442
|
+
# Library.read_header(inf) do |lib|
|
443
|
+
# lib.write_header(outf)
|
444
|
+
# Structure.read_each_header(inf) do |struct|
|
445
|
+
# struct.write_header(outf)
|
446
|
+
# Element.read_each(inf) {|element| element.write(outf)}
|
447
|
+
# struct.write_footer(outf)
|
448
|
+
# end
|
449
|
+
# lib.write_footer(outf)
|
450
|
+
# end
|
451
|
+
# end
|
452
|
+
# end
|
453
|
+
#
|
454
|
+
# Example 2 (using a block):
|
455
|
+
#
|
456
|
+
# File.open(in_file, 'rb') do |inf|
|
457
|
+
# File.open(out_file, 'wb') do |outf|
|
458
|
+
# Library.read_header(inf) do |lib|
|
459
|
+
# lib.write_header(outf) do
|
460
|
+
# Structure.read_each_header(inf) do |struct|
|
461
|
+
# struct.write_header(outf) do
|
462
|
+
# Element.read_each(inf) {|element| element.write(outf)}
|
463
|
+
# end
|
464
|
+
# end
|
465
|
+
# end
|
466
|
+
# end
|
467
|
+
# end
|
468
|
+
# end
|
469
|
+
#
|
470
|
+
def write_header(file)
|
471
|
+
# alter the BNF to exclude Structures and ENDLIB; then write to file
|
472
|
+
# according to the modified BNF
|
473
|
+
alt_bnf = BnfSpec.new(*Library.bnf_spec.bnf_items[0..-3])
|
474
|
+
self.write(file, alt_bnf)
|
475
|
+
|
476
|
+
# if block is given, then yield to that block and then write the
|
477
|
+
# footer afterwards
|
478
|
+
if block_given?
|
479
|
+
yield
|
480
|
+
self.write_footer(file)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
#
|
485
|
+
# Writes only the Library footer (just ENDLIB record) to file. To be used
|
486
|
+
# with #write_header.
|
487
|
+
#
|
488
|
+
def write_footer(file)
|
489
|
+
Record.new(GRT_ENDLIB).write(file)
|
490
|
+
end
|
491
|
+
|
492
|
+
#####################
|
493
|
+
## PRIVATE METHODS ##
|
494
|
+
#####################
|
495
|
+
|
496
|
+
private
|
497
|
+
|
498
|
+
# Used by #modify_time and #access_time
|
499
|
+
def update_times()
|
500
|
+
if modify_time and access_time
|
501
|
+
self.bgnlib = build_time(modify_time) + build_time(access_time)
|
502
|
+
else
|
503
|
+
self.bgnlib = nil
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
# Used by various units setting methods
|
508
|
+
def update_units()
|
509
|
+
if @user_units and @database_units
|
510
|
+
@records.set(GRT_UNITS, [@user_units, @database_units])
|
511
|
+
else
|
512
|
+
@records.set(GRT_UNITS, nil)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|