microstation 0.4.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +23 -23
  3. data/.rspec +2 -2
  4. data/Gemfile +28 -10
  5. data/History.txt +6 -6
  6. data/LICENSE.adoc +22 -0
  7. data/Manifest.txt +81 -60
  8. data/README.adoc +131 -0
  9. data/Rakefile +71 -30
  10. data/bin/dgn2pdf +36 -37
  11. data/bin/dgn_template +107 -0
  12. data/bin/microstation +231 -0
  13. data/bin/pw_print +35 -0
  14. data/cad_files/drawing_faatitle_in_non_default_model.dgn +0 -0
  15. data/cad_files/drawing_no_block.dgn +0 -0
  16. data/cad_files/drawing_with_3_block.dgn +0 -0
  17. data/cad_files/drawing_with_block.dgn +0 -0
  18. data/cad_files/seed2d.dgn +0 -0
  19. data/cad_files/test.dgn +0 -0
  20. data/lib/microstation.rb +252 -88
  21. data/lib/microstation/app.rb +781 -286
  22. data/lib/microstation/cad_input_queue.rb +100 -25
  23. data/lib/microstation/cell.rb +191 -0
  24. data/lib/microstation/changer.rb +70 -0
  25. data/lib/microstation/configuration.rb +193 -57
  26. data/lib/microstation/criteria_creation_t.rb +23 -0
  27. data/lib/microstation/dir.rb +252 -252
  28. data/lib/microstation/directory.rb +46 -0
  29. data/lib/microstation/drawing.rb +690 -189
  30. data/lib/microstation/element.rb +311 -0
  31. data/lib/microstation/enumerator.rb +32 -29
  32. data/lib/microstation/errors.rb +17 -0
  33. data/lib/microstation/event_handler.rb +28 -0
  34. data/lib/microstation/ext/pathname.rb +23 -25
  35. data/lib/microstation/ext/win32ole.rb +7 -0
  36. data/lib/microstation/extensions/faa.rb +124 -0
  37. data/lib/microstation/file_tests.rb +68 -0
  38. data/lib/microstation/functions.rb +60 -0
  39. data/lib/microstation/graphics.rb +35 -0
  40. data/lib/microstation/line.rb +19 -0
  41. data/lib/microstation/model.rb +45 -0
  42. data/lib/microstation/model_trait.rb +189 -0
  43. data/lib/microstation/ole_cad_input_message.rb +101 -0
  44. data/lib/microstation/ole_helper.rb +152 -0
  45. data/lib/microstation/pdf_support.rb +40 -40
  46. data/lib/microstation/point3d.rb +71 -0
  47. data/lib/microstation/primitive_command_interface.rb +66 -0
  48. data/lib/microstation/properties.rb +61 -57
  49. data/lib/microstation/property_handler.rb +48 -0
  50. data/lib/microstation/scan/color.rb +38 -38
  51. data/lib/microstation/scan/criteria.rb +89 -85
  52. data/lib/microstation/scan/klass.rb +43 -43
  53. data/lib/microstation/scan/level.rb +38 -38
  54. data/lib/microstation/scan/line_style.rb +45 -45
  55. data/lib/microstation/scan/line_weight.rb +33 -33
  56. data/lib/microstation/scan/range.rb +19 -0
  57. data/lib/microstation/scan/scan_trait.rb +51 -0
  58. data/lib/microstation/scan/subtype.rb +40 -40
  59. data/lib/microstation/scan/type.rb +134 -109
  60. data/lib/microstation/scan_trait.rb +62 -0
  61. data/lib/microstation/scanner.rb +24 -24
  62. data/lib/microstation/tag.rb +87 -58
  63. data/lib/microstation/tag_set.rb +385 -280
  64. data/lib/microstation/tag_set_trait.rb +51 -0
  65. data/lib/microstation/tagged_element.rb +105 -0
  66. data/lib/microstation/template.rb +90 -84
  67. data/lib/microstation/template_info.rb +172 -0
  68. data/lib/microstation/template_runner.rb +65 -0
  69. data/lib/microstation/text.rb +79 -54
  70. data/lib/microstation/text_node.rb +124 -74
  71. data/lib/microstation/ts/attribute.rb +140 -139
  72. data/lib/microstation/ts/instance.rb +146 -112
  73. data/lib/microstation/ts/tagset_trait.rb +49 -0
  74. data/lib/microstation/types.rb +91 -91
  75. data/lib/microstation/version.rb +5 -0
  76. data/lib/microstation/wrap.rb +28 -214
  77. data/plot/pdf-bw.plt +164 -164
  78. data/plot/pdf.plt +163 -163
  79. data/plot/png.plt +383 -383
  80. data/plot/tiff.plt +384 -384
  81. data/plot/wmbw.tbl +324 -66
  82. data/plot/wmcolor.tbl +62 -62
  83. metadata +301 -86
  84. data/.gemtest +0 -0
  85. data/README.txt +0 -75
  86. data/lib/microstation/attributes.rb +0 -35
  87. data/lib/microstation/extensions/hash.rb +0 -27
  88. data/spec/app_spec.rb +0 -267
  89. data/spec/configuration_spec.rb +0 -122
  90. data/spec/drawing_spec.rb +0 -247
  91. data/spec/drawings/new_drawing.dgn +0 -0
  92. data/spec/drawings/test.dgn +0 -0
  93. data/spec/drawings/test1.dgn +0 -0
  94. data/spec/drawings/testfile.pdf +0 -0
  95. data/spec/enumerator_spec.rb +0 -60
  96. data/spec/microstation_spec.rb +0 -36
  97. data/spec/scanner_spec.rb +0 -155
  98. data/spec/spec_app.rb +0 -11
  99. data/spec/spec_helper.rb +0 -31
  100. data/spec/tag_set_spec.rb +0 -123
  101. data/spec/text_node_spec.rb +0 -92
  102. data/spec/text_spec.rb +0 -62
Binary file
Binary file
Binary file
Binary file
Binary file
data/lib/microstation.rb CHANGED
@@ -1,88 +1,252 @@
1
- require 'win32ole'
2
- require 'microstation/app'
3
- require 'microstation/drawing'
4
- require 'microstation/configuration'
5
- require 'microstation/ext/pathname'
6
- require 'microstation/cad_input_queue'
7
- require 'microstation/scan/criteria'
8
- require 'microstation/enumerator'
9
- require 'microstation/text'
10
- require 'microstation/text_node'
11
- require 'microstation/template'
12
- require 'microstation/tag_set'
13
- require 'microstation/tag'
14
- require 'microstation/dir'
15
-
16
- require 'erb'
17
-
18
- module Microstation
19
- VERSION = '0.4.1'
20
-
21
- def self.root
22
- Pathname.new( File.dirname(__FILE__)).parent
23
- end
24
-
25
-
26
- def self.app
27
- Microstation::App.new
28
- end
29
-
30
-
31
- def self.plot_driver_directory
32
- root + "plot"
33
- end
34
-
35
- def self.use_template(template,context)
36
- def context.binding
37
- binding
38
- end
39
- opts = {:read_only => true}
40
- Microstation::App.run do |app|
41
- tmpfile = Tempfile.new('drawing')
42
- app.new_drawing(tmpfile,template) do |drawing|
43
- drawing.scan_text do |text|
44
- compiled_template = ERB.new(text)
45
- new_text = compiled_template.result(context.binding)
46
- text = new_text
47
- end
48
- end
49
- end
50
- tempfile.read
51
- end
52
-
53
- def self.drawings_in_dir(dir)
54
- dirpath = Pathname.new(dir).expand_path
55
- drawings = Pathname.glob("#{dirpath}/*.d{gn,wg}")
56
- end
57
-
58
- def self.dgn2pdf(dir,output = dir)
59
- drawings = drawings_in_dir(dir)
60
- self.with_drawings(drawings) do |drawing|
61
- drawing.save_as_pdf(drawing.name,output)
62
- end
63
- end
64
-
65
- def self.open_drawing(drawing,options = {}, &block)
66
- Microstation::App.open_drawing(drawing,options,&block)
67
- end
68
-
69
- def self.with_drawings_in_dir(dir,&block)
70
- drawings = self.drawings_in_dir(dir)
71
- self.with_drawings(drawings,&block)
72
- end
73
-
74
- def self.with_drawings(*files, &block)
75
- Microstation::App.with_drawings(*files,&block)
76
- end
77
-
78
- def self.run(options={}, &block)
79
- options = {:visible => false}.merge(options)
80
- begin
81
- app = Microstation::App.new(options)
82
- block.arity < 1 ? app.instance_eval(&block) : block.call(app)
83
- ensure
84
- app.quit
85
- end
86
- end
87
-
88
- end
1
+ require 'microstation/version'
2
+ require 'win32ole'
3
+ require 'microstation/file_tests'
4
+ require 'microstation/app'
5
+ require 'microstation/drawing'
6
+ require 'microstation/configuration'
7
+ require 'microstation/ext/pathname'
8
+ require 'microstation/cad_input_queue'
9
+ require 'microstation/scan/criteria'
10
+ require 'microstation/enumerator'
11
+ require 'microstation/line'
12
+ require 'microstation/text'
13
+ require 'microstation/text_node'
14
+ require 'microstation/template'
15
+ require 'microstation/tag_set'
16
+ require 'microstation/cell'
17
+ require 'microstation/tag'
18
+ require 'microstation/dir'
19
+ require 'microstation/ext/win32ole'
20
+ require 'microstation/template_info'
21
+ require 'microstation/template_runner'
22
+ require 'erb'
23
+
24
+ module Microstation
25
+
26
+ ROOT = Pathname(__dir__).parent
27
+
28
+ TEMPLATES_PATH = ROOT + 'templates'
29
+
30
+ class << self
31
+
32
+ def save_as_pdf(d)
33
+ run do |app|
34
+ drawing = app.current_drawing
35
+ drawing.save_as_pdf(dir: d)
36
+ drawing.close
37
+ end
38
+ end
39
+
40
+ def default_error_proc
41
+ @default_error_proc ||= ->(e,f){ puts "Couldn't open drawing #{f}" }
42
+ end
43
+
44
+ def default_drawing_options
45
+ {read_only: true, error_proc: default_error_proc}
46
+ end
47
+
48
+ def default_error_proc=(p)
49
+ @default_error_proc = p
50
+ end
51
+
52
+ def default_app_options
53
+ @default_app_options
54
+ end
55
+
56
+ def default_app_options=(opts)
57
+ @default_app_options = opts
58
+ end
59
+
60
+ default_app_options = {visible: false}
61
+
62
+ def root
63
+ ROOT
64
+ end
65
+
66
+ def plot_driver_directory
67
+ root + "plot"
68
+ end
69
+
70
+ def use_template(template,context, options ={} )
71
+ def context.binding
72
+ binding
73
+ end
74
+ options = {readonly: true}
75
+ App.run do |app|
76
+ tmpfile = Tempfile.new('drawing')
77
+ app.new_drawing(tmpfile,template) do |drawing|
78
+ drawing.scan_text do |text|
79
+ compiled_template = ERB.new(text)
80
+ new_text = compiled_template.result(context.binding)
81
+ text = new_text
82
+ end
83
+ end
84
+ end
85
+ tempfile.read
86
+ end
87
+
88
+ # starts an app with drawing, opens a temp
89
+ # copy of the drawing and yields it
90
+ # saves the drawing with with name or output_dir
91
+ # same name
92
+ #
93
+ # @param dgn
94
+ # @yield [Drawing]
95
+ def change_drawing(...)
96
+ App.change_drawing(...)
97
+ end
98
+
99
+ # gets all dwg and dgn dfiles in a directory
100
+ # @param dir
101
+ def drawings_in_dir(dir)
102
+ dirpath = Pathname.new(dir).expand_path
103
+ drawings = Pathname.glob("#{dirpath}/*.d{gn,wg,xf}")
104
+ end
105
+
106
+ def dump_template_info_for_dir(dir, options={})
107
+ drawings = drawings_in_dir(dir)
108
+ raise "no drawings in dir #{dir}" if drawings.empty?
109
+ with_drawings(drawings) do |drawing|
110
+ template = TemplateInfo.new(drawing,options)
111
+ template.dump(dir)
112
+ end
113
+ end
114
+
115
+ def dump_template_info(dgn, dir: nil, tagset_filter: nil, visible: false)
116
+ drawing = Pathname(dgn).expand_path
117
+ output_dir = dir || drawing.parent
118
+ template = TemplateInfo.new(drawing,tagset_filter: tagset_filter, visible: visible)
119
+ template.dump(output_dir)
120
+ end
121
+
122
+ # @param dir [String] the directory of drawing [dgn,dwg] to convert
123
+ # @param outdir [String] the output dir for converted pdf files
124
+ def dgn2pdf(dir,outdir = dir)
125
+ drawings = drawings_in_dir(dir)
126
+ with_drawings(drawings) do |drawing|
127
+ drawing.save_as_pdf(name: drawing.name,dir: outdir)
128
+ end
129
+ end
130
+
131
+ def run_templates_in_dir(...)
132
+ App.run_templates_in_dir(...)
133
+ end
134
+
135
+ # starts app, opens drawing, and yields
136
+ # the drawing before closing drawing and
137
+ # quitting the app
138
+ def open_drawing(...)
139
+ App.open_drawing(...)
140
+ end
141
+
142
+ # opens all the drawings in a drawing
143
+ # by calling open drawing
144
+ def with_drawings_in_dir(dir,...)
145
+ drawings = drawings_in_dir(dir)
146
+ with_drawings(drawings,...)
147
+ end
148
+
149
+
150
+ def with_drawings(...)
151
+ App.with_drawings(...)
152
+ end
153
+
154
+ def scan_text(file,&block)
155
+ App.open_drawing(file) do |d|
156
+ d.scan_text(&block)
157
+ end
158
+ end
159
+
160
+ def get_text(file, &block)
161
+ App.open_drawing(file) do |d|
162
+ d.get_text(&block)
163
+ end
164
+ end
165
+
166
+ def get_all_text(file)
167
+ App.open_drawing(file) do |d|
168
+ d.get_all_text
169
+ end
170
+ end
171
+
172
+ def save_current_drawing(dir, exit: true)
173
+ if exit
174
+ run do |app|
175
+ drawing = app.current_drawing
176
+ drawing.copy(dir: dir)
177
+ drawing.save_as_pdf(dir: dir)
178
+ drawing.close
179
+ end
180
+ else
181
+ app = App.new
182
+ drawing = app.current_drawing
183
+ drawing.copy(dir: dir)
184
+ drawing.save_as_pdf(dir: dir)
185
+ app
186
+ end
187
+ end
188
+
189
+ def save_current_drawing_as_pdf(dir)
190
+ App.run do |app|
191
+ drawing = app.current_drawing
192
+ drawing.save_as_pdf(dir: dir)
193
+ drawing.close
194
+ end
195
+ end
196
+
197
+ def run(...)
198
+ App.run(...)
199
+ end
200
+
201
+ end
202
+
203
+ end
204
+
205
+ if $0 == __FILE__
206
+
207
+ require 'pry'
208
+
209
+ app = Microstation::App.new
210
+ require 'microstation/ole_helper'
211
+
212
+ File.open('app_methods.txt','w') do |f|
213
+ f.write app.ole_obj.ole_methods_detail
214
+ end
215
+
216
+ tlib = app.ole_obj.ole_typelib
217
+ File.open('microstation_classes.txt','w') do |f|
218
+ f.write app.ole_obj.ole_classes_detail
219
+ end
220
+
221
+ ole_obj = app.ole_obj
222
+
223
+ event_classes = ole_obj.select_ole_type('event')
224
+ puts event_classes
225
+
226
+
227
+ # drawing = app.new_drawing('mynew.dgn')
228
+
229
+ # le =tlib.ole_classes.find{|c| c.name == 'LineElement'}
230
+ # #puts drawing.model_names
231
+
232
+ # VT = WIN32OLE::VARIANT
233
+ # # Type.Missing equivalent
234
+
235
+ # e1 = WIN32OLE_VARIANT::NoParam
236
+ # e2 = WIN32OLE_VARIANT.new(nil, VT::VT_VARIANT|VT::VT_BYREF)
237
+
238
+
239
+ # drawing.change_model('Default')
240
+ # line = drawing.create_line [1,0.5],[1,1,0],e1
241
+ # drawing.add_line line if line
242
+
243
+
244
+
245
+ binding.pry
246
+
247
+
248
+ app.quit
249
+
250
+
251
+
252
+ end
@@ -1,286 +1,781 @@
1
- require_relative 'wrap'
2
-
3
- module Windows
4
-
5
- class FileSystem
6
-
7
- def self.windows_path(path)
8
- obj = new
9
- obj.windows_path(path)
10
- end
11
-
12
- def fs_object
13
- @fs_object ||= WIN32OLE.new('Scripting.FileSystemObject')
14
- end
15
-
16
- def windows_path(path)
17
- path = path.to_path if path.respond_to? :to_path
18
- fs_object.GetAbsolutePathName(path.to_str)
19
- end
20
-
21
- end
22
- end
23
-
24
- module Microstation
25
-
26
- module MSD
27
- end
28
-
29
- def self.win_fs
30
- @windows_fs ||= Windows::FileSystem.new
31
- end
32
-
33
- class App
34
-
35
- include Wrap
36
-
37
- def self.run(options={})
38
- begin
39
- the_app = new(options)
40
-
41
- yield the_app
42
- ensure
43
- the_app.quit
44
- end
45
- end
46
-
47
- def self.open_drawing(drawing,options={},&block)
48
- self.run(options) do |app|
49
- app.open_drawing(drawing,options,&block)
50
- end
51
- end
52
-
53
- def self.with_drawings(*files,&block)
54
- files = files[0] if files[0].kind_of? Array
55
- opts = {:read_only => true}
56
- self.run do |app|
57
- files.each do |file|
58
- puts "opening #{file}.."
59
- app.open_drawing(file,opts) do |draw|
60
- block.call draw
61
- end
62
-
63
- end
64
- end
65
- end
66
-
67
- def load_constants
68
- WIN32OLE.const_load(@ole_obj, MSD) unless MSD.constants.size > 0
69
- end
70
-
71
- attr_reader :scanners
72
-
73
- def initialize(options = {})
74
- visible = options.fetch(:visible){ true }
75
-
76
- @ole_obj = WIN32OLE.new('MicrostationDGN.Application')
77
- @windows = Windows::FileSystem.new
78
- make_visible(visible)
79
- @scanners = []
80
- end
81
-
82
- def visible?
83
- @visible
84
- end
85
-
86
- def project_dir
87
- @project_dir
88
- end
89
-
90
- def project_dir=(dir)
91
- @project_dir = dir ? Pathname.new(dir) : dir
92
- end
93
-
94
- def base_dir
95
- project_dir ? project_dir : Pathname.getwd
96
- end
97
-
98
- def normalize_name(name)
99
- name = Pathname.new(name) unless name.kind_of? Pathname
100
- name = name.ext('.dgn') unless name.extname.to_s == /\.(dgn|dwg)$/
101
- return (base_dir + name).expand_path
102
- end
103
-
104
- def make_visible(visible)
105
- @visible = visible
106
- @ole_obj.Visible = @visible
107
- end
108
-
109
- attr_reader :ole_obj
110
-
111
- # def method_missing(name,*args,&block)
112
- # @ole_obj.send(name,*args,&block)
113
- # end
114
-
115
- def with_drawing(drawing)
116
- begin
117
- yield drawing
118
- ensure
119
- drawing.close
120
- end
121
- end
122
-
123
- def with_template(template)
124
- template = Template.new(template,self)
125
- yield template
126
- template = nil
127
- end
128
-
129
- def open_drawing(filename,options = {})
130
- filename = Pathname(filename)
131
- raise FileNotFound unless filename.file?
132
- readonly = options.fetch(:read_only){ false}
133
- ole = @ole_obj.OpenDesignFile(windows_path(filename), "ReadOnly" => readonly)
134
- drawing = drawing_from_ole(ole)
135
- return drawing unless block_given?
136
- begin
137
- yield drawing
138
- rescue
139
- puts "got error"
140
- ensure
141
- drawing.close
142
- end
143
-
144
- end
145
-
146
- def windows_path(path)
147
- @windows.windows_path(path)
148
- end
149
-
150
- def active_workspace
151
- @ole_obj.ActiveWorkspace
152
- end
153
-
154
- def configuration
155
- @config ||= Microstation::Configuration.new(self)
156
- end
157
-
158
- def username
159
- configuration["USERNAME"]
160
- end
161
-
162
- # seedfile A String expression. The name of the seed file. Should not include a path. The default extension is ".dgn".
163
- # Typical values are "seed2d" or "seed3d".
164
- # open
165
- # If the open argument is True, CreateDesignFile returns the newly-opened DesignFile object; this is the same value as
166
- # ActiveDesignFile. If the Open argument is False, CreateDesignFile returns Nothing.
167
- def new_drawing(filename, seedfile=nil ,open = true,&block)
168
- #drawing_name = normalize_name(filename)
169
- seedfile = determine_seed(seedfile)
170
- windows_name = windows_path(filename)
171
- ole = @ole_obj.CreateDesignFile(seedfile, windows_name, open)
172
- drawing = drawing_from_ole(ole)
173
- return drawing unless block_given?
174
- begin
175
- yield drawing
176
- rescue
177
- ensure
178
- drawing.close
179
- end
180
-
181
- end
182
-
183
- def drawing_from_ole(ole)
184
- Drawing.new(self,ole)
185
- end
186
-
187
- def determine_seed(seedfile)
188
- return "seed2d" unless seedfile
189
- return windows_path( File.expand_path(seedfile))
190
- end
191
-
192
- def eval_cexpression(string)
193
- @ole_obj.GetCExpressionValue(string)
194
- end
195
-
196
- def quit
197
- active_design_file.close if active_file?
198
- @scanners.each{|sc| sc.close}
199
- @ole_obj.quit
200
- end
201
-
202
- def active_design_file
203
- ole = @ole_obj.ActiveDesignFile rescue nil
204
- drawing_from_ole(ole) if ole
205
- end
206
-
207
- def current_drawing
208
- active_design_file
209
- end
210
-
211
- # alias :current_drawing :active_design_file
212
-
213
- def close_active_drawing
214
- active_design_file.close if active_design_file
215
- end
216
-
217
- def active_file?
218
- !!active_design_file
219
- end
220
-
221
- def file_exists?(file)
222
- File.file?( File.expand_path(file) )
223
- end
224
-
225
- def create_scanner(&block)
226
- Microstation::Scan::Criteria.create_scanner(self,&block)
227
- end
228
-
229
- def find_by_id(id)
230
- model = active_model_reference.GetElementById64(id)
231
- wrap(model)
232
- end
233
-
234
- def scan(criteria = nil)
235
- result = []
236
- raise 'NoActiveModel' unless active_model_reference
237
- if criteria
238
- criteria.resolve
239
- ee = active_model_reference.Scan(criteria.ole_obj)
240
- else
241
- ee = active_model_reference.Scan(nil)
242
- end
243
- scan_result = Microstation::Enumerator.new ee
244
- return scan_result.to_enum unless block_given?
245
- scan_result.each do |item|
246
- yield item
247
- end
248
- nil
249
- end
250
-
251
- def cad_input_queue
252
- queue = init_cad_input_queue
253
- return queue unless block_given?
254
- begin
255
- yield queue
256
- rescue
257
-
258
- ensure
259
- queue.close
260
- queue = nil
261
- end
262
- end
263
-
264
- def can_open?(filename)
265
- ext = File.extname(filename)
266
- (ext == ".dwg") || (ext == ".dgn")
267
- end
268
-
269
- def active_model_reference
270
- @ole_obj.ActiveModelReference rescue nil
271
- end
272
-
273
- private
274
-
275
-
276
-
277
-
278
- def init_cad_input_queue
279
- Microstation::CadInputQueue.new(@ole_obj.CadInputQueue)
280
- end
281
-
282
-
283
- end
284
-
285
- end
286
-
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'wrap'
4
+ require_relative 'point3d'
5
+ require_relative 'event_handler'
6
+ require_relative 'functions'
7
+ require 'pry'
8
+
9
+ class WIN32OLE
10
+ def ole_obj
11
+ self
12
+ end
13
+ end
14
+
15
+ module Windows
16
+ class FileSystem
17
+ def self.windows_path(path)
18
+ obj = new
19
+ obj.windows_path(path)
20
+ end
21
+
22
+ def fs_object
23
+ @fs_object ||= WIN32OLE.new('Scripting.FileSystemObject')
24
+ end
25
+
26
+ def windows_path(path)
27
+ path = path.to_path if path.respond_to? :to_path
28
+ fs_object.GetAbsolutePathName(path.to_str)
29
+ end
30
+ end
31
+ end
32
+
33
+ module Microstation
34
+ module MSD
35
+ end
36
+
37
+ def self.win_fs
38
+ @windows_fs ||= Windows::FileSystem.new
39
+ end
40
+
41
+ class App
42
+ include Functions
43
+
44
+
45
+ @default_app_options = { visible: false }
46
+
47
+ class << self
48
+
49
+ attr_accessor :default_app_options
50
+
51
+ # include Wrap
52
+
53
+ # Initialize an instance of app with the options
54
+ # @param [Hash] options the options to create the app with
55
+ # @option options [Boolean] :visible Is the app visible
56
+ #
57
+ # [source]
58
+ # ----
59
+ # App.run do |app|
60
+ # drawing = app.open_drawing('test.dgn')
61
+ # drawing.scan_all_text do |model,text|
62
+ # puts "#{model} #{text}"
63
+ # end
64
+ # end
65
+ #
66
+ # @yield [App] the_app yields the instanciated app
67
+ # @return [void]
68
+ def run(options = {})
69
+ opts = self.default_app_options.merge(options)
70
+ begin
71
+ the_app = new(**opts)
72
+ binding.pry if the_app.nil?
73
+ yield the_app
74
+ rescue StandardError
75
+ binding.pry
76
+ ensure
77
+ the_app.quit if the_app.respond_to? :quit
78
+ the_app = nil
79
+ GC.start
80
+ nil
81
+ end
82
+ end
83
+
84
+ # Calls #run to get an app instance then call open drawing with
85
+ # that app
86
+ # (see #open_drawing)
87
+ # @yield Drawing
88
+ # @return [void]
89
+ def open_drawing(drawing, app_options: {}, options: {}, &block)
90
+ run(**app_options) do |app|
91
+ app.open_drawing(drawing, **options, &block)
92
+ end
93
+ end
94
+
95
+ # Runs the app, opening the filenames
96
+ # and yielding each open drawing to the
97
+ # supplied block
98
+ # it automatically closes the drawing and
99
+ # the app when done
100
+ #
101
+ # [source]
102
+ # dir = Pathname('C:/templates')
103
+ # drawings = Pathname.glob(dir + '/**/*.dgn')
104
+ # App.with_drawings(drawings) do |drawing|
105
+ # drawing.save_as_pdf(dir: 'c:/output/')
106
+ # end
107
+ #
108
+ # @param files [Array<String,Pathname>]
109
+ # @param visible [Boolean]
110
+ # @param readonly [Boolean]
111
+ # @param error_proc [Proc]
112
+ # @yield [Drawing]
113
+ # @return [void]
114
+ def with_drawings(*files, visible: true, readonly: false, error_proc: ::Microstation.default_error_proc, &block)
115
+ # drawing_options = default_drawing_options.merge(options)
116
+ # app_options = default_app_options
117
+ errors = []
118
+ files = files[0] if files[0].is_a? Array
119
+ begin
120
+ the_app = new(visible: visible)
121
+ files_enum = files.each
122
+ loop do
123
+ file = files_enum.next
124
+ puts "opening #{file}.."
125
+ begin
126
+ the_app.open_drawing(file, readonly: readonly, error_proc: error_proc, &block)
127
+ the_app.ole_obj.ole_methods # check if server still open
128
+ rescue StandardError => e
129
+ error_proc.call(e, file)
130
+ the_app = new(app_options)
131
+ end
132
+ end
133
+ ensure
134
+ the_app&.quit
135
+ the_app = nil
136
+ end
137
+ end
138
+ end
139
+
140
+ attr_reader :scanners, :visible, :app_event, :project_dir
141
+
142
+ # Constructor for app
143
+ # @param [Boolean] visible
144
+ # @param event_handler [EventHandler]
145
+ def initialize(visible: false, event_handler: default_event_handler)
146
+ @visible = visible
147
+ @event_handler = event_handler
148
+ @ole_obj, @app_event = init_ole_and_app_event(visible: @visible, event_handler: @event_handler, tries: 5,
149
+ sleep_duration: 0.5)
150
+ @run_loop = true
151
+ @windows = Windows::FileSystem.new
152
+ # make_visible(visible)
153
+ @scanners = {}
154
+ rescue StandardError => e
155
+ binding.pry
156
+ end
157
+
158
+ def run_templates_in_dir(dir, options = {})
159
+ yaml_files = Pathname.glob("#{Pathname(dir).expand_path}*.yaml")
160
+ yaml_files.each do |f|
161
+ TemplateRunner.new(f).run_with_app(self, options)
162
+ end
163
+ end
164
+
165
+ def change_drawing(dgn, output_dir: nil, name: nil, options: {}, &block)
166
+ changer = Changer.new(dgn, name: name, output_dir: output_dir, app: self)
167
+ changer.run(&block)
168
+ end
169
+
170
+ def render_template(drawing, output_dir: nil, locals: {}, name: nil)
171
+ temp = Template.new(drawing, output_dir: output_dir, app: self, name: name)
172
+ temp.render(output_dir: output_dir, locals: locals)
173
+ end
174
+
175
+ #
176
+ # the default EventHandler
177
+ #
178
+ # @return [EventHandler] returns the default EventHandler
179
+ #
180
+ def default_event_handler
181
+ event_handler = EventHandler.new
182
+ event_handler.add_handler('OnDesignFileOpened') do |*_args|
183
+ puts 'drawing opened'
184
+ @drawing_opened = true
185
+ end
186
+ event_handler.add_handler('OnDesignFileClosed') do |*_args|
187
+ @drawing_opened = false
188
+ puts 'drawing closed'
189
+ end
190
+ event_handler
191
+ end
192
+
193
+ #
194
+ # register an handler
195
+ #
196
+ # @param [String] event key for handler
197
+ # @param [<Type>] &block <description>
198
+ #
199
+ # @return [<Type>] <description>
200
+ #
201
+ def register_handler(event, &block)
202
+ @event_handler.add_handler(event, &block) unless event == 'OnQuit'
203
+ end
204
+
205
+ #
206
+ # return a Handler
207
+ #
208
+ # @param [String,Symbol] event the event key
209
+ #
210
+ # @return [Proc] returns the Proc given by event name
211
+ #
212
+ def get_handler(event)
213
+ @event_handler.get_handler(event)
214
+ end
215
+
216
+ def exit_message_looop
217
+ puts 'Microstation exiting...'
218
+ @run_loop = false
219
+ end
220
+
221
+ def wait_drawing_opened(secs, interval = 1)
222
+ elapsed = 0
223
+ while !drawing_opened? && elapsed <= secs
224
+ elapsed += interval
225
+ sleep(interval)
226
+ WIN32OLE_EVENT.message_loop
227
+ end
228
+ end
229
+
230
+ def init_ole_and_app_event(visible: @visible, event_handler: @event_handler, tries: 5, sleep_duration: 1)
231
+ ole = WIN32OLE.new('MicrostationDGN.Application')
232
+ sleep(sleep_duration)
233
+ ole.Visible = visible
234
+ ole.IsProcessLocked = true
235
+ load_constants(ole)
236
+ app_event = WIN32OLE_EVENT.new(ole)
237
+ app_event.handler = event_handler
238
+ [ole, app_event]
239
+ rescue StandardError => e
240
+ tries -= 1
241
+ sleep_duration += 1.5
242
+ puts "Error: #{e}. #{tries} tries left."
243
+ retry if tries.positive?
244
+ raise e, 'unable to init ole app'
245
+ end
246
+
247
+ # @return [Boolean] whether the app is visible
248
+ def visible?
249
+ @visible
250
+ end
251
+
252
+ #
253
+ # Change the visible attribute
254
+ #
255
+ # @param [Boolean] bool true or false to make it visible
256
+ #
257
+ # @return [void]
258
+ #
259
+ def visible=(bool)
260
+ ole_obj.Visible = bool
261
+ end
262
+
263
+ def project_dir=(dir)
264
+ @project_dir = dir ? Pathname.new(dir) : dir
265
+ end
266
+
267
+ def base_dir
268
+ project_dir || Pathname.getwd
269
+ end
270
+
271
+ def wrap(item, cell = nil)
272
+ Element.convert_item(item, self, cell)
273
+ end
274
+
275
+ def normalize_name(name)
276
+ name = Pathname.new(name) unless name.is_a? Pathname
277
+ name = name.ext('.dgn') unless name.extname.to_s == /\.(dgn|dwg)$/
278
+ (base_dir + name).expand_path
279
+ end
280
+
281
+ def make_visible(visible)
282
+ @visible = visible
283
+ begin
284
+ ole_obj.Visible = @visible
285
+ true
286
+ rescue Exception => e
287
+ false
288
+ end
289
+ end
290
+
291
+ def ole_obj
292
+ is_ok = true
293
+ begin
294
+ @ole_obj.Visible
295
+ rescue StandardError => e
296
+ is_ok = false
297
+ end
298
+
299
+ @ole_obj, @app_event = init_ole_and_app_event(tries: 3) unless is_ok
300
+
301
+ @ole_obj
302
+ end
303
+
304
+ def with_drawing(drawing)
305
+ yield drawing
306
+ ensure
307
+ drawing.close
308
+ end
309
+
310
+ def with_template(template)
311
+ template = Template.new(template, self)
312
+ yield template
313
+ template = nil
314
+ end
315
+
316
+ # open the drawing
317
+ # @param filename [String] the name of the file to open
318
+ # @param [Boolean] :readonly (false)
319
+ # @param [Proc] :error_proc (raise) a proc to run
320
+ # @param wait_time [Integer] the total amount of time to wait to open file (500)
321
+ # @param wait_interval [Float] the amount of time in seconds to wait before retry (0.5)
322
+ # @yield [Drawing] drawing
323
+ # @return [void]
324
+ def open_drawing(filename, readonly: false, error_proc: nil, wait_time: 500, wait_interval: 0.5)
325
+ filename = Pathname(filename)
326
+ raise FileNotFound unless filename.file?
327
+
328
+ begin
329
+ ole = ole_open_drawing(windows_path(filename), readonly: readonly, wait_time: wait_time,
330
+ wait_interval: wait_interval)
331
+ rescue StandardError => e
332
+ if error_proc
333
+ error_proc.call(filename)
334
+ return
335
+ else
336
+ raise e
337
+ end
338
+ end
339
+ drawing = drawing_from_ole(ole)
340
+ return drawing unless block_given?
341
+
342
+ begin
343
+ yield drawing
344
+ ensure
345
+ drawing.close
346
+ end
347
+ end
348
+
349
+ def drawing_opened?
350
+ @drawing_opened
351
+ end
352
+
353
+ def windows_path(path)
354
+ @windows.windows_path(path)
355
+ end
356
+
357
+ def active_workspace
358
+ ole_obj.ActiveWorkspace
359
+ end
360
+
361
+ # @return the [Configuration]
362
+ def configuration
363
+ @config ||= ::Microstation::Configuration.new(self)
364
+ end
365
+
366
+ # @return [String] the configuration variable USERNAME
367
+ def username
368
+ configuration['USERNAME']
369
+ end
370
+
371
+ # create a new drawing
372
+ # @param filename [String,Pathname] the name of the file
373
+ # @param seedfile [String] The name of the seed file.
374
+ # should not include a path. The default extension is ".dgn".
375
+ # Typical values are "seed2d" or "seed3d".
376
+ # @param open [Boolean] .If the open argument is True,
377
+ # CreateDesignFile returns the newly-opened DesignFile object;
378
+ # this is the same value as ActiveDesignFile. If the Open argument is False,
379
+ # CreateDesignFile returns Nothing.
380
+ # @return [Drawing]
381
+ def new_drawing(filename, seedfile: nil, open: true, wait_time: 500, wait_interval: 1, &block)
382
+ file_path = Pathname(filename).expand_path
383
+ raise ExistingFile, file_path if file_path.exist?
384
+
385
+ # drawing_name = normalize_name(filename)
386
+ seedfile = determine_seed(seedfile)
387
+ binding.pry unless seedfile
388
+ windows_name = windows_path(filename)
389
+ ole = new_ole_drawing(seedfile, windows_name, open: open, wait_time: wait_time, wait_interval: wait_interval)
390
+ drawing = drawing_from_ole(ole)
391
+ return drawing unless block_given?
392
+
393
+ begin
394
+ yield drawing
395
+ rescue StandardError => e
396
+ 'puts error in new drawing'
397
+ raise e
398
+ ensure
399
+ drawing.close
400
+ end
401
+ end
402
+
403
+ def new_ole_drawing(seedfile, new_design_file_name, open: true, wait_time: 500, wait_interval: 0.5)
404
+ ole = ole_obj.CreateDesignFile(seedfile, new_design_file_name, open)
405
+ wait_drawing_opened(wait_time, wait_interval)
406
+ raise "drawing not opened in #{wait_time}" unless drawing_opened?
407
+
408
+ ole
409
+ rescue StandardError => e
410
+ raise e
411
+ end
412
+
413
+ def has_current_drawing?
414
+ ole_obj.HasActiveDesignFile
415
+ end
416
+
417
+ # prepend a dir to the MS_SEEDFILES configuration
418
+ # @param dir [String,Pathname]
419
+ def prepend_seed_path(dir)
420
+ configuration.prepend('MS_SEEDFILES', windows_path(dir))
421
+ end
422
+
423
+ def drawing_from_ole(ole)
424
+ Drawing.new(self, ole)
425
+ end
426
+
427
+ def determine_seed(seedfile)
428
+ return configuration['MS_DESIGNSEED'] unless seedfile
429
+
430
+ seed = find_seed(seedfile)
431
+ return seed.to_s if seed
432
+
433
+ raise "Seedfile #{seedfile} not found in #{configured_seed_paths}"
434
+ end
435
+
436
+ # @return [String] the configuration variable MS_SEEDFILES
437
+ def configured_seed_paths
438
+ configuration['MS_SEEDFILES']
439
+ end
440
+
441
+ # find the seedfile
442
+ # @param [String,Pathname] seedfile
443
+ #
444
+ # * If the seed file is absolute and found the return the
445
+ # seedfile.
446
+ # * If the seed file is not found search MS_SEEDFILES
447
+ # @return [Pathname] seedfile the found seedfile
448
+ def find_seed(seedfile)
449
+ seed = Pathname(seedfile).expand_path.sub_ext('.dgn')
450
+ return seed if seed.file?
451
+
452
+ find_seed_in_seed_dirs(seed.basename)
453
+ end
454
+
455
+ def find_seed_in_seed_dirs(seedfile)
456
+ seed_dir = seed_paths.find { |p| (p + seedfile).file? }
457
+ return (seed_dir + seedfile) if seed_dir
458
+ end
459
+
460
+ # @return [Array] returns the MS_SEEDFILES as Pathnames Array
461
+ def seed_paths
462
+ configured_seed_paths.split(';').map { |d| Pathname(d) }
463
+ end
464
+
465
+ def eval_cexpression(string)
466
+ ole_obj.GetCExpressionValue(string)
467
+ end
468
+
469
+ #
470
+ # quit the app
471
+ #
472
+ # @return [void]
473
+ #
474
+ def quit
475
+ close_active_drawing
476
+ @scanners.each { |_name, sc| sc.close }
477
+ begin
478
+ ole_obj.Quit
479
+ rescue StandardError
480
+ nil
481
+ end
482
+ end
483
+
484
+ # the active design file
485
+ # @return [Drawing]
486
+ def active_design_file
487
+ if active_design_file?
488
+ ole = ole_obj.ActiveDesignFile
489
+ drawing_from_ole(ole)
490
+ end
491
+ end
492
+
493
+ alias active_drawing active_design_file
494
+ alias current_drawing active_design_file
495
+
496
+ #
497
+ # close the active_design_file
498
+ #
499
+ # @return [void]
500
+ #
501
+ def close_active_drawing
502
+ active_design_file.close if active_design_file?
503
+ end
504
+
505
+ alias close_current_drawing close_active_drawing
506
+
507
+ #
508
+ #
509
+ #
510
+ # @return [Boolean] true if app has an active design file open
511
+ #
512
+ def active_design_file?
513
+ ole_obj.HasActiveDesignFile
514
+ end
515
+
516
+ alias current_design_file? active_design_file?
517
+
518
+ #
519
+ # <Description>
520
+ #
521
+ # @param [String,Pathname] file name of file to search for
522
+ #
523
+ # @return [Boolean] true if file exists
524
+ #
525
+ def file_exists?(file)
526
+ Pathname(file).expand_path.file?
527
+ end
528
+
529
+ def create_scanner(name = nil, &block)
530
+ ::Microstation::Scan::Criteria.create_scanner(name, self, &block)
531
+ end
532
+
533
+ def text_criteria
534
+ scanners[:textual] || create_scanner(:textual) { include_textual }
535
+ end
536
+
537
+ def tags_criteria
538
+ sc = scanners[:tags] || create_scanner(:tags) { include_tags }
539
+ end
540
+
541
+ def create_scan_criteria(name = nil, &block)
542
+ ::Microstation::Scan::Criteria.create_scanner(name, self, &block)
543
+ end
544
+
545
+ # def find_by_id(id)
546
+ # active_design_file.find_by_id(id)
547
+ # wrap(model) if el
548
+ # end
549
+
550
+ def scan_model(criteria = nil, model = nil)
551
+ model ||= active_model_reference
552
+ model.scan(criteria)
553
+ end
554
+
555
+ def get_ole_element_enumerator(model:, criteria: nil)
556
+ criteria ||= create_scan_criteria
557
+ criteria.resolve
558
+ model.scan(criteria.ole_obj)
559
+ rescue Exception
560
+ # binding.pry
561
+ end
562
+
563
+ # lets you interact with the cad_input_queue
564
+ # @yield [CadInputQueue]
565
+ # @return [void]
566
+ def cad_input_queue
567
+ queue = init_cad_input_queue
568
+ return queue unless block_given?
569
+
570
+ begin
571
+ yield queue
572
+ rescue StandardError
573
+ ensure
574
+ queue.close
575
+ queue = nil
576
+ @cad_input_queue = nil
577
+ end
578
+ end
579
+
580
+ def show_command(text)
581
+ ole_obj.ShowCommand(text)
582
+ end
583
+
584
+ def show_prompt(text)
585
+ ole_obj.ShowPrompt(text)
586
+ end
587
+
588
+ def show_message(text)
589
+ ole_obj.ShowMessage(text)
590
+ end
591
+
592
+ #
593
+ #
594
+ # @param [String] text text to show
595
+ # @param [Symbol] location (one of :left, :middle)
596
+ #
597
+ # @return [void]
598
+ #
599
+ def show_temp_message(text, location: nil)
600
+ loc = case location
601
+ when :left
602
+ MSD::MsdStatusBarAreaLeft
603
+ when :middle
604
+ MSD::MsdStatusBarAreaMiddle
605
+ else
606
+ MSD::MsdStatusBarAreaLeft
607
+ end
608
+ ole_obj.ShowTempMessage(loc, text)
609
+ end
610
+
611
+ def can_open?(filename)
612
+ ext = File.extname(filename)
613
+ (ext == '.dwg') || (ext == '.dgn')
614
+ end
615
+
616
+ def active_model_reference
617
+ DefaultModel.new(self, ole_obj.ActiveModelReference)
618
+ rescue StandardError
619
+ nil
620
+ end
621
+
622
+ def default_model
623
+ DefaultModel.new(self, ole_obj.DefaultModelReference)
624
+ rescue StandardError
625
+ nil
626
+ end
627
+
628
+ def ole_point
629
+ ::WIN32OLE_RECORD.new('Point3d', ole_obj)
630
+ end
631
+
632
+ def ole_rotation
633
+ ::WIN32OLE_RECORD.new('Rotation', ole_obj)
634
+ end
635
+
636
+ def ole_matrix
637
+ ::WIN32OLE_RECORD.new('Matrix', ole_obj)
638
+ end
639
+
640
+ #
641
+ # Create an WIN32OLE_RECORD of type Point3d
642
+ #
643
+ # @param [Numeric] x coordinate in x axis
644
+ # @param [Numeric] y coordinate in y axis
645
+ # @param [Numeric] z coordinate in z direction (0.0)
646
+ #
647
+ # @return [WIN32OLE_RECORD] record of type Point3d
648
+ #
649
+ def create_ole_point(x, y, z = 0)
650
+ ole = ole_point
651
+ ole.X = x
652
+ ole.Y = y
653
+ ole.Z = z
654
+ ole
655
+ end
656
+
657
+ def to_ole_matrix3d(vec)
658
+ if vec.instance_of?(WIN32OLE_RECORD) && vec.typename == 'Matrix3d'
659
+ vec
660
+ else
661
+ binding.pry
662
+ end
663
+ end
664
+
665
+ #
666
+ # Conversion to WIN32OLE_RECORD of type Point3d
667
+ #
668
+ # @param [WIN32OLE_RECORD, Point3d, Array<Numeric>] pt Point to normalize to ole point
669
+ # @return [WIN32OLE_RECORD] 'Point3d' WIN32OLE_RECORD
670
+ #
671
+ def to_ole_point3d(pt)
672
+ case pt
673
+ when ole_point3d?(pt)
674
+ pt
675
+ when Point3d
676
+ create_ole_point(pt.x, pt.y, pt.z)
677
+ when Array
678
+ pt1 = pt.map(&:to_f)
679
+ x, y, z = pt1
680
+ z ||= 0.0
681
+ create_ole_point(x, y, z)
682
+ end
683
+ end
684
+
685
+ #
686
+ # <Description>
687
+ #
688
+ # @param [Object] pt pt object to test if it is a WIN32OLE_RECORD of 'Point3d'
689
+ #
690
+ # @return [Boolean] true if pt is WIN32OLE_RECORD of 'Point3d'
691
+ #
692
+ def ole_point3d?(pt)
693
+ pt.instance_of?(WIN32OLE_RECORD) && pt.typename == 'Point3d'
694
+ end
695
+
696
+ def create_text_node(origin, rotation, temp = nil)
697
+ ole_origin = to_ole_point3d(origin)
698
+ ole_rotation = to_ole_matrix3d(rotation)
699
+ temp ||= WIN32OLE_VARIANT::Nothing
700
+ ole = ole_obj.CreateTextNodeElement1(temp, ole_origin, ole_rotation)
701
+ rescue Exception => e
702
+ puts e.message
703
+ nil
704
+ end
705
+
706
+ #
707
+ # convert pt to Point3d
708
+ #
709
+ # @param [Array<Numeric,Numeric,Numeric>, Point3d, WIN32OLE_RECORD] pt Point to convert
710
+ #
711
+ # @return [Point3d]
712
+ #
713
+ def to_point3d(pt)
714
+ case pt
715
+ when Array
716
+ pt_a = pt.map(&:to_f)
717
+ x, y, z = pt_a
718
+ z ||= 0.0
719
+ Point3d.new(x, y, z)
720
+ when Point3d
721
+ pt
722
+ when WIN32OLE_RECORD
723
+ Point3d.from_ole(pt) if pt.typename == 'Point3d'
724
+ end
725
+ end
726
+
727
+ def to_point(pt)
728
+ to_point3d(pt)
729
+ end
730
+
731
+ def start_primitive(klass)
732
+ ole_obj.CommandState.StartPrimitive klass.new(self)
733
+ end
734
+
735
+ def my_place_line
736
+ require_relative 'primitive_command_interface'
737
+ start_primitive LineCreation
738
+ end
739
+
740
+ def method_missing(meth, *args, &block)
741
+ if meth.to_s =~ /^[A-Z]/
742
+ require 'pry'; binding.pry
743
+ result = ole_obj.send(meth, *args, &block)
744
+ else
745
+ super(meth, *args, &block)
746
+ end
747
+ rescue StandardError => e
748
+ binding.pry
749
+ end
750
+
751
+ protected
752
+
753
+ def ole_open_drawing(path, readonly: false, wait_time: 500, wait_interval: 0.5)
754
+ ole = ole_obj.OpenDesignFile(windows_path(path), 'ReadOnly' => readonly)
755
+ wait_drawing_opened(wait_time, wait_interval)
756
+ raise "drawing not opened in #{wait_time}" unless drawing_opened?
757
+
758
+ ole
759
+ rescue StandardError => e
760
+ raise e
761
+ end
762
+
763
+ def load_constants(ole_obj)
764
+ WIN32OLE.const_load(ole_obj, MSD) unless MSD.constants.size.positive?
765
+ end
766
+
767
+ def run_loop
768
+ WIN32OLE_EVENT.message_loop while @run_loop
769
+ end
770
+
771
+ def stop_loop
772
+ @run_loop = false
773
+ end
774
+
775
+ private
776
+
777
+ def init_cad_input_queue
778
+ ::Microstation::CadInputQueue.new(ole_obj.CadInputQueue, self)
779
+ end
780
+ end
781
+ end