jfreeze-ruby-gdsii 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|