autocad 0.4.6 → 0.5

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop/minitest.yml +2 -2
  3. data/.rubocop/strict.yml +4 -4
  4. data/.rubocop.yml +36 -33
  5. data/CHANGELOG.md +5 -5
  6. data/LICENSE.txt +21 -21
  7. data/README.md +134 -39
  8. data/Rakefile +26 -10
  9. data/exe/autocad +3 -3
  10. data/gemfiles/rubocop.gemfile +2 -1
  11. data/lib/autocad/app.rb +127 -28
  12. data/lib/autocad/arc.rb +3 -0
  13. data/lib/autocad/block.rb +11 -6
  14. data/lib/autocad/block_reference.rb +33 -4
  15. data/lib/autocad/bounding_box.rb +202 -0
  16. data/lib/autocad/dim_style.rb +4 -0
  17. data/lib/autocad/drawing.rb +873 -172
  18. data/lib/autocad/element.rb +217 -25
  19. data/lib/autocad/errors.rb +9 -0
  20. data/lib/autocad/filter.rb +502 -168
  21. data/lib/autocad/layer.rb +129 -41
  22. data/lib/autocad/layout.rb +120 -0
  23. data/lib/autocad/line.rb +154 -55
  24. data/lib/autocad/message_box.rb +95 -95
  25. data/lib/autocad/model.rb +217 -89
  26. data/lib/autocad/mtext.rb +189 -110
  27. data/lib/autocad/plot.rb +45 -0
  28. data/lib/autocad/plot_configuration.rb +372 -0
  29. data/lib/autocad/point.rb +7 -0
  30. data/lib/autocad/point3d.rb +18 -11
  31. data/lib/autocad/pviewport.rb +136 -21
  32. data/lib/autocad/selection_filter.rb +358 -180
  33. data/lib/autocad/selection_set.rb +140 -61
  34. data/lib/autocad/selection_set_adapter.rb +187 -8
  35. data/lib/autocad/spline.rb +27 -0
  36. data/lib/autocad/text.rb +66 -11
  37. data/lib/autocad/text_style.rb +4 -0
  38. data/lib/autocad/version.rb +1 -1
  39. data/lib/autocad/viewport.rb +57 -0
  40. data/lib/autocad.rb +126 -30
  41. data/lib/faa/cleanup.rb +137 -0
  42. data/lib/win32ole_helper.rb +52 -0
  43. data/rbs_collection.lock.yaml +38 -18
  44. data/sig/generated/autocad/app.rbs +278 -251
  45. data/sig/generated/autocad/arc.rbs +6 -3
  46. data/sig/generated/autocad/block.rbs +8 -5
  47. data/sig/generated/autocad/block_reference.rbs +99 -59
  48. data/sig/generated/autocad/bounding_box.rbs +78 -0
  49. data/sig/generated/autocad/dim_style.rbs +6 -0
  50. data/sig/generated/autocad/drawing.rbs +597 -158
  51. data/sig/generated/autocad/element.rbs +233 -166
  52. data/sig/generated/autocad/errors.rbs +29 -23
  53. data/sig/generated/autocad/filter.rbs +388 -60
  54. data/sig/generated/autocad/layer.rbs +76 -19
  55. data/sig/generated/autocad/layout.rbs +64 -0
  56. data/sig/generated/autocad/line.rbs +128 -25
  57. data/sig/generated/autocad/message_box.rbs +81 -81
  58. data/sig/generated/autocad/model.rbs +115 -41
  59. data/sig/generated/autocad/mtext.rbs +123 -0
  60. data/sig/generated/autocad/plot.rbs +26 -0
  61. data/sig/generated/autocad/plot_configuration.rbs +176 -0
  62. data/sig/generated/autocad/point.rbs +7 -0
  63. data/sig/generated/autocad/point3d.rbs +70 -66
  64. data/sig/generated/autocad/pviewport.rbs +64 -0
  65. data/sig/generated/autocad/selection_filter.rbs +226 -50
  66. data/sig/generated/autocad/selection_set.rbs +112 -37
  67. data/sig/generated/autocad/selection_set_adapter.rbs +235 -28
  68. data/sig/generated/autocad/spline.rbs +22 -0
  69. data/sig/generated/autocad/text.rbs +66 -7
  70. data/sig/generated/autocad/text_style.rbs +6 -0
  71. data/sig/generated/autocad/viewport.rbs +19 -2
  72. data/sig/generated/autocad.rbs +140 -68
  73. data/sig/generated/faa/cleanup.rbs +53 -0
  74. data/sig/generated/win32ole_helper.rbs +9 -0
  75. data/sig/prototype/lib/autocad/app.rbs +3 -1
  76. data/sig/prototype/lib/autocad/bounding_box.rbs +15 -0
  77. data/sig/prototype/lib/autocad/drawing.rbs +6 -0
  78. data/sig/prototype/lib/autocad/layer.rbs +5 -0
  79. data/sig/prototype/lib/autocad/viewport.rbs +7 -0
  80. data/sig/prototype/lib/autocad.rbs +1 -1
  81. metadata +29 -5
  82. data/event_handler.log +0 -24
  83. data/sig/generated/autocad/text_node.rbs +0 -37
@@ -7,171 +7,197 @@ require_relative "selection_set_adapter"
7
7
  require "debug"
8
8
 
9
9
  module Autocad
10
+ # Represents an AutoCAD drawing document and provides interface for:
11
+ # - File operations (save/open/close)
12
+ # - Space management (Model/Paper)
13
+ # - Layer/block/text management
14
+ # - Plot configuration
15
+ # - User interaction
16
+ # - Selection sets
17
+ # - System variables
10
18
  class Drawing
11
19
  include Common
12
20
  attr_reader :app
13
21
 
22
+ # Create a Drawing instance from an OLE object
23
+ # @rbs app: Autocad::App -- the application instance
24
+ # @rbs ole: WIN32OLE -- the OLE object for the drawing
25
+ # @rbs return Drawing -- a new Drawing instance
14
26
  def self.from_ole_obj(app, ole) #: Drawing
15
27
  new(app, ole)
16
28
  end
17
29
 
18
- def set_system_variables(names, values)
19
- atts = names.zip(values).to_h
20
- atts.each do |k, v|
21
- ole_obj.SetVariable(k, v)
22
- end
23
- end
24
-
25
- def faa_title_block
26
- block_reference_enum.find { |b| b.name == "faatitle" }
27
- end
28
-
29
- def block_reference_enum
30
- ss = block_reference_selection_set
31
- ss.clear
32
- ss.select
33
- ss.each
34
- end
35
-
36
- def block_reference_selection_set
37
- @block_reference_selection_set ||= get_block_reference_selection_set
38
- end
39
-
40
- def get_block_reference_selection_set
41
- ss = get_selection_set("block_reference")
42
- ss ||= create_selection_set("block_reference") do |ss|
43
- ss.filter do |f|
44
- f.block_reference
45
- end
46
- end
47
- ss
48
- end
49
-
50
- def with_system_variables(names, values, &block)
51
- atts
52
- current_values = get_system_variables(names)
53
- set_system_variables(names, values)
54
- yield
55
- ensure
56
- set_system_variables(names, current_values)
57
- end
58
-
59
- def get_system_variables(*atts)
60
- return [] if atts.empty?
61
- if atts.first.class == Array
62
- atts = atts.first
63
- end
64
- atts.each_with_object([]) do |k, a|
65
- a << ole_obj.GetVariable(k)
66
- a
67
- end
68
- end
69
-
70
- def initialize(app, ole)
30
+ # Initialize a new Drawing instance
31
+ # @param app [Autocad::App] The application instance
32
+ # @param ole [WIN32OLE] The OLE object for the drawing
33
+ # @param requested_name [String, nil] Optional name for the drawing before it's saved
34
+ # @rbs app: Autocad::App -- the application instance
35
+ # @rbs ole: WIN32OLE -- the OLE object for the drawing
36
+ # @rbs requested_name: String? -- optional name for the drawing before it's saved
37
+ def initialize(app, ole, requested_name = nil)
71
38
  @app = app
72
39
  @ole_obj = ole
73
40
  @app_event = WIN32OLE_EVENT.new(ole)
41
+ @requested_name = requested_name
74
42
  end
75
43
 
44
+ # Get the event handler for this drawing
45
+ # @return [EventHandler] The event handler instance
46
+ # @rbs return EventHandler
76
47
  def event_handler #: EventHandler
77
48
  @event_handler ||= default_event_handler
78
49
  end
79
50
 
80
- def default_event_handler
81
- handler = EventHandler.new
82
- @app_event.handler = handler
83
- handler
84
- end
85
-
86
- def app_ole
87
- app.ole_obj
88
- end
89
-
90
- # @rbs return SelectionSetAdapter
51
+ # Create a new selection set in the drawing
52
+ # @param name [String] The name for the selection set
53
+ # @yield [SelectionSetAdapter] Optional block for configuring the selection set
54
+ # @return [SelectionSetAdapter] The created selection set adapter
55
+ # @example Create a selection set with a filter
56
+ # create_selection_set("walls") do |ss|
57
+ # ss.filter.layer("WALLS").and.block_reference
58
+ # end
59
+ # @rbs name: String -- the name for the selection set
60
+ # @rbs return SelectionSetAdapter -- the created selection set adapter
91
61
  def create_selection_set(name)
92
62
  ss = SelectionSet.new(name)
93
63
  yield ss if block_given?
94
64
  SelectionSetAdapter.new(self, ss)
95
65
  end
96
66
 
97
- # register an handler
98
- #
67
+ # Register an event handler
68
+ # @param event [String] Event key for handler
69
+ # @yield Handler procedure to execute when event occurs
70
+ # @example Register a handler for BeginCommand event
71
+ # register_handler("BeginCommand") { puts "Command started" }
99
72
  # @rbs event: String -- event key for handler
100
73
  # @rbs &: {() -> void} - handler Proc
101
74
  def register_handler(event, &) #:void
102
75
  @event_handler.add_handler(event, &) unless event == "OnQuit"
103
76
  end
104
77
 
78
+ # Check if the drawing is read-only
79
+ # @return [Boolean] True if drawing is read only
80
+ # @rbs return bool -- true if drawing is read only
105
81
  def read_only?
106
82
  ole_obj.ReadOnly
107
83
  end
108
84
 
85
+ # Check if the drawing has been saved before
86
+ # @return [Boolean] True if drawing is previously saved
87
+ # @rbs return bool -- true if drawing is previously saved
109
88
  def previously_saved?
110
- ole_obj.FullFileName != ""
89
+ ole_obj.FullName != ""
111
90
  end
112
91
 
92
+ # Check if the drawing has been modified since the last save
93
+ # @return [Boolean] True if drawing is modified
94
+ # @rbs return bool -- true if drawing is modified
113
95
  def modified?
114
96
  ole_obj.Saved == false
115
97
  end
116
98
 
99
+ # Saves the drawing
100
+ # If the drawing hasn't been saved yet and no name is provided, uses the requested name
101
+ # @param name [String, nil] Optional new name for the drawing
102
+ # @param dir [String, Pathname, nil] Optional directory to save to
103
+ # @return [void]
104
+ # @example Save drawing with new name in specific directory
105
+ # drawing.save(name: "floor_plan_v2", dir: "C:/Projects/Building")
106
+ # @rbs name: String? -- optional new name for the drawing
107
+ # @rbs dir: String|Pathname? -- optional directory to save to
108
+ # @rbs return void
117
109
  def save(name: nil, dir: nil) #: void
118
110
  return if read_only?
119
- if previously_saved? && modified?
111
+
112
+ # Use requested_name if no name is provided and drawing hasn't been saved yet
113
+ name ||= @requested_name if !previously_saved? && @requested_name
114
+
115
+ if previously_saved? && modified? && !name && !dir
120
116
  ole_obj.Save
117
+ puts "saved #{path}"
121
118
  else
122
119
  out_name = dwg_path(name: name, dir: dir)
123
120
  windows_name = app.windows_path(out_name)
124
121
  ole_obj.SaveAs(windows_name)
122
+ puts "saved #{windows_name}"
123
+ # After saving, clear the requested name since it's now saved
124
+ @requested_name = nil
125
125
  end
126
- puts "saved #{windows_name}"
127
- end
128
-
129
- def dwg_path(name: nil, dir: nil)
130
- name ||= self.name
131
- dir = Pathname.new(dir || dirname).expand_path
132
- dir.mkpath unless dir.directory?
133
- dir + dwg_name(name)
134
126
  end
135
127
 
136
- def dwg_name(name)
137
- Pathname.new(name).sub_ext(".dwg")
138
- end
139
-
140
- # save the drawing as a pdf file
141
- # if the name or directory is given it uses
142
- # those params. If not it uses the drawing name
143
- # and the drawing directory
128
+ # Save the drawing as a PDF file
129
+ # If name or directory is given, uses those params
130
+ # Otherwise uses the drawing name and directory
131
+ # @param name [String, nil] The name of the PDF file
132
+ # @param dir [String, Pathname, nil] The directory to save the PDF
133
+ # @param model [Boolean] Whether to use model space (true) or paper space (false)
134
+ # @return [void]
135
+ # @example Export to PDF with custom name
136
+ # drawing.save_as_pdf(name: "presentation", dir: "C:/Exports")
144
137
  # @rbs name: String? - the name of the file
145
138
  # @rbs dir: String? - the directory to save the drawing
146
139
  def save_as_pdf(name: nil, dir: nil, model: false) #: void
147
140
  out_name = pdf_path(name: name, dir: dir)
148
- windows_name = app.windows_path(out_name)
149
- loop do
150
- print_pdf(windows_name, model:)
151
- break if out_name.file?
152
- end
153
- puts "saved #{windows_name}"
141
+ print_pdf(out_name, model:)
142
+ puts "saved #{out_name}"
154
143
  end
155
144
 
156
- def pdf_path(name: nil, dir: nil)
157
- name ||= self.name
158
- dir = Pathname.new(dir || dirname).expand_path
159
- dir.mkpath unless dir.directory?
160
- dir + pdf_name(name)
145
+ # Get the center point of the current view
146
+ # @return [Point3d] The center of the view in world coordinates
147
+ # @rbs return Point3d -- the center of the view in world coordinates
148
+ def view_center
149
+ center = get_variable("VIEWCTR")
150
+ Point3d(center)
161
151
  end
162
152
 
163
- # Return the pdf name for the drawing.
164
- #
165
- # If a name is provided use the name provided otherwise use the drawing name
166
- #
167
- # @rbs name: String | nil -- change ext to pdf and return Pathname from
168
- # the name or drawing name
169
- def pdf_name(name = nil) #: Pathname
170
- name ||= self.name
171
- Pathname.new(name).sub_ext(".pdf")
153
+ # Add a new plot configuration to the drawing
154
+ # @param name [String] Name for the new plot configuration
155
+ # @param model [Boolean] Whether this is for model space (true) or paper space (false)
156
+ # @return [PlotConfiguration] The created plot configuration
157
+ # @example Create a new PDF plot configuration
158
+ # pdf_config = drawing.add_plot_configuration("PDF_Export")
159
+ # pdf_config.device_name = "AutoCAD PDF.pc3"
160
+ # @rbs name: String
161
+ # @rbs return PlotConfiguration
162
+ def add_plot_configuration(name, model: false)
163
+ plot_config = plot_configurations.find { |p| p.name == name }
164
+ return plot_config if plot_config
165
+ ole = @ole_obj.PlotConfigurations.Add(name, model)
166
+ app.wrap(ole)
167
+ rescue => ex
168
+ app.error_proc.call(ex, self)
169
+ end
170
+
171
+ # Get the predefined PDF plot configuration "faa_ansid_bw"
172
+ # Creates it if it doesn't exist
173
+ # @return [PlotConfiguration] The PDF plot configuration
174
+ # @rbs return PlotConfiguration
175
+ def pdf_plot_config #: PlotConfiguration
176
+ @pdf_plot_config ||= create_pdf_plot_configutation
177
+ end
178
+
179
+ # Get the plot object for this drawing
180
+ # @return [Plot] The plot object
181
+ # @rbs return Plot
182
+ def plot #: Plot
183
+ ole = ole_obj.Plot
184
+ ole.QuietErrorMode = true
185
+ app.wrap(ole_obj.Plot)
172
186
  end
173
187
 
174
- # copy the drawing
188
+ # Get the first layout that is not "Model"
189
+ # @return [Layout] The first paper space layout
190
+ # @rbs return Layout -- The first layout that is not "Model"
191
+ def paper_space_layout
192
+ layouts.reject { it.name == "Model" }.first
193
+ end
194
+
195
+ # Copy the drawing to a new file
196
+ # @param name [String, Pathname, nil] Name of the file
197
+ # @param dir [String, Pathname, nil] Target directory
198
+ # @return [void]
199
+ # @example Create a backup copy
200
+ # drawing.copy(name: "backup_#{Time.now.strftime('%Y%m%d')}.dwg")
175
201
  # @rbs name: String | Pathname --name of the file
176
202
  # @rbs dir: String|Pathname -- dir
177
203
  def copy(name: nil, dir: nil) #: void
@@ -186,89 +212,192 @@ module Autocad
186
212
  FileUtils.copy path.to_s, copy_path.to_s, verbose: true
187
213
  end
188
214
 
189
- # If you copy the file the name to use
190
- # @rbs backup_str: String -- the bqckup string to use for copies
191
- def copy_name(backup_str = ".copy")
192
- lname = name.dup
193
- ext = File.extname(lname)
194
- "#{File.basename(lname, ext)}#{backup_str}#{ext}"
195
- end
196
-
215
+ # Check if paper space is active
216
+ # @return [Boolean] True if paper space is active
217
+ # @rbs return bool -- true if paper space is active
197
218
  def paper_space? #: bool
198
219
  ole_obj.ActiveSpace == ACAD::AcPaperSpace
199
220
  end
200
221
 
222
+ # Check if model space is active
223
+ # @return [Boolean] True if model space is active
224
+ # @rbs return bool -- true if model space is active
201
225
  def model_space? #: bool
202
226
  ole_obj.ActiveSpace == ACAD::AcModelSpace
203
227
  end
204
228
 
229
+ # Switch to paper space
230
+ # @return [void]
231
+ # @example Switch to paper space and add a viewport
232
+ # drawing.to_paper_space
233
+ # viewport = drawing.paper.add_pv_viewport(center, width: 200, height: 150)
234
+ # @rbs return void
205
235
  def to_paper_space #: void
206
236
  ole_obj.ActiveSpace = ACAD::AcPaperSpace
207
237
  end
208
238
 
239
+ # Switch to model space
240
+ # @return [void]
241
+ # @example Switch to model space and add geometry
242
+ # drawing.to_model_space
243
+ # drawing.model.add_circle([0,0,0], 10)
244
+ # @rbs return void
209
245
  def to_model_space #: void
210
246
  ole_obj.ActiveSpace = ACAD::AcModelSpace
211
247
  end
212
248
 
249
+ # Returns the name of the drawing
250
+ # If the drawing hasn't been saved yet, returns the requested name
251
+ # @return [String] The name of the drawing with .dwg extension
213
252
  # @rbs return String -- the name of the drawing
214
253
  def name
254
+ if @requested_name && !previously_saved?
255
+ # Make sure we return just the basename with .dwg extension
256
+ basename = File.basename(@requested_name)
257
+ return basename.end_with?('.dwg') ? basename : "#{basename}.dwg"
258
+ end
215
259
  ole_obj.Name
216
260
  end
217
261
 
262
+ # Get the basename of the drawing as a Pathname
263
+ # @return [Pathname] The name as Pathname
218
264
  # @rbs return Pathname -- the name as Pathname
219
265
  def basename
220
266
  Pathname.new(name)
221
267
  end
222
268
 
269
+ # Get the directory of the drawing
270
+ # @return [Pathname] The directory of the file
223
271
  # @rbs return Pathname -- the directory of the file
224
272
  def dirname
225
273
  Pathname.new(ole_obj.Path).expand_path
226
274
  end
227
275
 
276
+ # Get the full path of the drawing
277
+ # @return [Pathname] The complete path of file
228
278
  # @rbs return Pathname -- the complete path of file
229
279
  def path
230
280
  dirname + basename
231
281
  end
232
282
 
233
- def print_pdf(print_path, model: false)
234
- if model
235
- to_model_space
236
- else
237
- to_paper_space
283
+ # Get the active layer in the drawing
284
+ # @return [Layer] The active layer
285
+ # @rbs return Layer -- the active layer
286
+ def active_layer
287
+ ole = ole_obj.ActiveLayer
288
+ app.wrap(ole)
289
+ end
290
+
291
+ # Get the name of the active layer
292
+ # @return [String] The name of the active layer
293
+ # @rbs return String -- the name of the active layer
294
+ def active_layer_name
295
+ ole_obj.ActiveLayer.Name
296
+ end
297
+
298
+ # Set the active layer
299
+ # @param layer [Layer, String] Layer object or name to make active
300
+ # @raise [RuntimeError] If layer not found
301
+ # @example Set active layer by name
302
+ # drawing.active_layer = "Walls"
303
+ # @rbs layer: Layer | String -- make the given layer active
304
+ def active_layer=(layer)
305
+ if layer.is_a?(String)
306
+ layer = ole_obj.Layers.Item(layer)
307
+ elsif layer.is_a?(Autocad::Layer)
308
+ layer = layer.to_ole
238
309
  end
239
- raise "no plot config" unless pdf_plot_config
240
- ole_obj.Plot.PlotToFile print_path, pdf_plot_config
310
+ raise "layer not found" unless layer
311
+ ole_obj.ActiveLayer = layer.to_ole
241
312
  end
242
313
 
243
- def pdf_plot_config
244
- app.plot_configs.find { |p| p =~ /faa.+high/i }
314
+ # Set the active paper space viewport
315
+ # @param vport [Autocad::PViewport] Viewport to make active
316
+ # @return [void]
317
+ # @rbs vport: Autocad::PViewport -- viewport to make active
318
+ # @rbs return void
319
+ def active_pviewport=(vport)
320
+ ole_obj.ActivePViewport = vport.to_ole
245
321
  end
246
322
 
247
- def active_layer
248
- ole_obj.ActiveLayer
323
+ # Get the active paper space viewport
324
+ # @return [PViewport] The active viewport
325
+ # @rbs return PViewport
326
+ def active_pviewport
327
+ ole = ole_obj.ActivePViewport
328
+ app.wrap(ole)
329
+ rescue => ex
330
+ app.error_proc.call(ex, self)
331
+ end
332
+
333
+ # Get the active layout
334
+ # @return [Layout] The active layout
335
+ # @rbs return Layout
336
+ def active_layout
337
+ ole = ole_obj.ActiveLayout
338
+ app.wrap(ole)
339
+ end
340
+
341
+ # Set the active layout
342
+ # @param layout [Layout] Layout to make active
343
+ # @return [void]
344
+ # @example Switch to a specific layout
345
+ # layout = drawing.layouts.find { |l| l.name == "Layout1" }
346
+ # drawing.active_layout = layout
347
+ # @rbs layout: Layout -- layout to make active
348
+ def active_layout=(layout)
349
+ ole_obj.ActiveLayout = layout.to_ole
249
350
  end
250
351
 
352
+ # Get the currently active space (model or paper)
353
+ # @return [ModelSpace, PaperSpace] The active space object
251
354
  def active_space
252
355
  ole = ole_obj.ActiveSpace
356
+
253
357
  if ole == ACAD::AcPaperSpace
254
- PaperSpace.new(ole_obj, app)
358
+ paper_space
359
+
255
360
  else
256
- ModelSpace.new(ole_obj, app)
361
+ model_space
257
362
  end
258
363
  end
259
364
 
260
365
  # Close the drawing
261
- # @rbs save: bool -- whether to save the drawing
366
+ # @param save [Boolean] Whether to save changes before closing
367
+ # @return [void]
368
+ # @raise [Autocad::DrawingClose] If closure fails
369
+ # @example Close without saving
370
+ # drawing.close(false)
371
+ # @rbs save: bool -- whether to save changes before closing
372
+ # @rbs return void
262
373
  def close(save = true)
374
+ # Store the name before marking as closed
375
+ drawing_name = @ole_obj.respond_to?(:Name) ? @ole_obj.Name : "unknown"
263
376
  @drawing_closed = true
377
+
264
378
  begin
265
- ole_obj.Close(save)
266
- rescue
267
- nil
379
+ if @ole_obj.respond_to?(:Close)
380
+ @ole_obj.Close(save)
381
+ elsif @ole_obj.respond_to?(:close)
382
+ @ole_obj.close(save)
383
+ else
384
+ # If we can't close it directly, try through the app
385
+ app.close_drawing(drawing_name, save)
386
+ end
387
+ rescue => ex
388
+ # Instead of just calling error_proc, raise a specific DrawingClose error
389
+ # Use drawing name instead of the drawing object
390
+ raise DrawingClose.new("Failed to close drawing: #{ex.message}", drawing_name)
391
+ ensure
392
+ @ole_obj = nil
268
393
  end
269
- @ole_obj = nil
270
394
  end
271
395
 
396
+ # Get a selection set by name
397
+ # @param name [String] Selection set name to return
398
+ # @return [SelectionSet, nil] The selection set or nil if not found
399
+ # @example Get or create a selection set
400
+ # ss = drawing.get_selection_set("my_selection") || drawing.create_selection_set("my_selection")
272
401
  # @rbs name: String -- selection set name to return
273
402
  # @rbs return SelectionSet | nil
274
403
  def get_selection_set(name)
@@ -276,55 +405,313 @@ module Autocad
276
405
  app.wrap(ole) if ole
277
406
  end
278
407
 
279
- # @rbs name: String -- selection set name to return
280
- # @rbs return SelectionSet | nil
281
- def get_ole_selection_set(name)
282
- return nil if ole_selection_sets.Count == 0
283
- begin
284
- ole_selection_sets.Item(name)
285
- rescue
286
- nil
287
- end
288
- end
289
-
290
- def ole_selection_sets
291
- ole_obj.SelectionSets
292
- end
293
-
294
- # @rbs return Enumerator[Block]
295
- # @rbs &: (Block) -> void
408
+ # Get all blocks in the drawing
409
+ # @return [Enumerator<Block>] An enumerator of blocks
410
+ # @yield [Block] Optional block to process each block
411
+ # @example Iterate through all blocks
412
+ # drawing.blocks.each { |block| puts block.name }
413
+ # @rbs return Enumerator[Block] -- an enumerator of blocks
414
+ # @rbs &: (Block) -> void -- optional block to process each block
296
415
  def blocks
297
416
  return to_enum(__callee__) unless block_given?
298
417
  ole_obj.Blocks.each { |o| yield app.wrap(o) }
299
418
  end
300
- # return the layers for the drawing
301
419
 
302
- # @rbs return Enumerator[Layer]
303
- # @rbs &: (Layer) -> void
420
+ # Get all layouts in the drawing
421
+ # @return [Enumerator<Layout>] An enumerator of layouts
422
+ # @yield [Layout] Optional block to process each layout
423
+ # @example Find a layout by name
424
+ # title_layout = drawing.layouts.find { |layout| layout.name == "Title Block" }
425
+ # @rbs return Enumerator[Layout] -- an enumerator of layouts
426
+ # @rbs &: (Layout) -> void -- optional block to process each layout
427
+ def layouts
428
+ return to_enum(__callee__) unless block_given?
429
+ ole_obj.Layouts.each { |o| yield app.wrap(o) }
430
+ end
431
+
432
+ # Get all layers in the drawing
433
+ # @return [Enumerator<Layer>] An enumerator of layers
434
+ # @yield [Layer] Optional block to process each layer
435
+ # @example Find frozen layers
436
+ # frozen_layers = drawing.layers.select { |layer| layer.frozen? }
437
+ # @rbs return Enumerator[Layer] -- an enumerator of layers
438
+ # @rbs &: (Layer) -> void -- optional block to process each layer
304
439
  def layers
305
440
  return to_enum(__callee__) unless block_given?
306
441
 
307
442
  ole_obj.Layers.each { |o| yield app.wrap(o) }
308
443
  end
309
444
 
445
+ # Get all linetypes in the drawing
446
+ # @return [Enumerator<Linetype>] An enumerator of linetypes
447
+ # @yield [Linetype] Optional block to process each linetype
448
+ # @example Load a new linetype
449
+ # unless drawing.linetypes.any? { |lt| lt.name == "DASHED" }
450
+ # drawing.load_linetype("acad.lin", "DASHED")
451
+ # end
452
+ # @rbs return Enumerator[Linetype] -- an enumerator of linetypes
453
+ # @rbs &: (Linetype) -> void -- optional block to process each linetype
310
454
  def linetypes
311
455
  return to_enum(__callee__) unless block_given?
312
456
  ole_obj.Linetypes.each { |o| yield app.wrap(o) }
313
457
  end
314
458
 
315
- # @rbs name: String -- layer name to create
459
+ # Create a new layer or get an existing one
460
+ # @param name [String, Layer] Layer name to create or existing Layer object
461
+ # @param color [Integer, Symbol, String, nil] Color for the layer (can be ACAD::COLOR constant, symbol, or integer)
462
+ # @return [Acad::Layer] The created or existing layer
463
+ # @example Create a red layer
464
+ # walls_layer = drawing.create_layer("Walls", :red)
465
+ # @example Create a layer with a specific color index
466
+ # detail_layer = drawing.create_layer("Details", 42)
467
+ # @rbs name: String | Layer -- layer name to create
468
+ # @rbs color: Integer|Symbol|String -- color for the layer (can be ACAD::COLOR constant, symbol, or integer)
316
469
  # @rbs return Acad::Layer
317
- def create_layer(name, color: nil)
470
+ def create_layer(name, color = nil)
471
+ if name.class == Autocad::Layer
472
+ name.color = Autocad.color_to_index(color) if color
473
+ return name
474
+ end
318
475
  ole_layer = begin
319
476
  ole_obj.Layers.Item(name)
320
477
  rescue
321
478
  nil
322
479
  end
323
480
  ole_layer ||= ole_obj.Layers.Add(name)
324
- ole_layer.Color = color if color
481
+ ole_layer.Color = Autocad.color_to_index(color) if color
325
482
  app.wrap(ole_layer)
326
483
  end
327
484
 
485
+ # Regenerate the drawing display
486
+ # @param view_ports [Symbol] Viewports to regenerate (:all or :active)
487
+ # @return [void]
488
+ # @example Regenerate all viewports
489
+ # drawing.regen(:all)
490
+ # @note Uses AcRegenType enum from AutoCAD
491
+ # @rbs view_ports: Symbol -- :all or :active
492
+ # @rbs return void
493
+ def regen(view_ports = :all)
494
+ vp_type = case view_ports
495
+ when :all then 1
496
+ when :active then 0
497
+ end
498
+ ole_obj.Regen vp_type
499
+ end
500
+
501
+ # Get all block references in model space
502
+ # @return [Enumerator<BlockReference>] All block references in model space
503
+ # @example Find all title blocks in model space
504
+ # title_blocks = drawing.model_block_references.select { |br| br.name == "TITLE_BLOCK" }
505
+ # @rbs return Enumerator[BlockReference] -- all block references in model space
506
+ def model_block_references
507
+ ss = selection_sets.find { it.name == "model_block_references" }
508
+ ss ||= create_selection_set("model_block_references")
509
+ ss.filter do |f|
510
+ f.and(f.model_space, f.block_reference)
511
+ end
512
+ ss.clear
513
+ ss.select
514
+ ss.each
515
+ end
516
+
517
+ # Get all block references in paper space
518
+ # @return [Enumerator<BlockReference>] All block references in paper space
519
+ # @example Delete all border blocks in paper space
520
+ # drawing.paper_block_references.each do |br|
521
+ # br.delete if br.name == "BORDER"
522
+ # end
523
+ # @rbs return Enumerator[BlockReference] -- all block references in paper space
524
+ def paper_block_references
525
+ ss = selection_sets.find { it.name == "paper_block_references" }
526
+ ss ||= create_selection_set("paper_block_references")
527
+ ss.filter do |f|
528
+ f.and(f.paper_space, f.block_reference)
529
+ end
530
+ ss.clear
531
+ ss.select
532
+ ss.each
533
+ end
534
+
535
+ # Select all text objects containing the specified string
536
+ # @param str [String] Text pattern to search for (supports wildcards)
537
+ # @return [Enumerator<Element>] Text elements containing the pattern
538
+ # @example Find all text containing "REVISION"
539
+ # revision_texts = drawing.select_text_containing("*REVISION*")
540
+ def select_text_containing(str)
541
+ varname = "@text_containg_#{str}".tr("*", "_")
542
+ ss = if instance_variable_defined?(varname)
543
+ instance_variable_get(varname)
544
+ else
545
+ get_select_text_containing(str)
546
+ end
547
+ ss.clear
548
+ ss.select
549
+ ss.each
550
+ end
551
+
552
+ # Check if the drawing has any paper space viewports
553
+ # @return [Boolean] True if paper space has at least one viewport
554
+ def has_pviewport?
555
+ paper_space.pviewports.count > 0
556
+ end
557
+
558
+ # Get a selection set containing text with the specified string
559
+ # @param str [String] Text pattern to search for
560
+ # @return [SelectionSetAdapter] Selection set with matching text
561
+ def get_select_text_containing(str)
562
+ name = "text_containing_#{str}"
563
+ varname = "@#{name}".tr("*", "_")
564
+ ss = get_selection_set(name)
565
+ ss.delete if ss
566
+ ss = create_selection_set(name) do |ss|
567
+ ss.filter_text_containing(str)
568
+ end
569
+ instance_variable_set(varname, ss)
570
+ ss
571
+ end
572
+
573
+ # Get all block references in the drawing
574
+ # @yield [BlockReference] Optional block to process each reference
575
+ # @return [Enumerator<BlockReference>] All block references
576
+ # @example Count references by block name
577
+ # counts = Hash.new(0)
578
+ # drawing.block_references { |br| counts[br.name] += 1 }
579
+ # &: (BlockReference) -> void
580
+ # @rbs return Enumerator[BlockReference]
581
+ def block_references
582
+ return to_enum(__callee__) unless block_given?
583
+ ss = block_reference_selection_set
584
+ ss.clear
585
+ ss.select
586
+ ss.each do |o|
587
+ yield o
588
+ end
589
+ end
590
+
591
+ # Get all dimension styles in the drawing
592
+ # @yield [DimStyle] Optional block to process each dimension style
593
+ # @return [Enumerator<DimStyle>] Enumerator of dimension styles
594
+ # @example Find architectural dimension style
595
+ # arch_style = drawing.dim_styles.find { |ds| ds.name == "ARCHITECTURAL" }
596
+ # &: (DimStyle) -> void
597
+ # @rbs return Enumerator[DimStyle] -- enumerator of dimension styles in document
598
+ def dim_styles
599
+ return to_enum(__callee__) unless block_given?
600
+ ole_obj.DimStyles.each { |o| yield app.wrap(o) }
601
+ end
602
+
603
+ # Get all text styles in the drawing
604
+ # @yield [TextStyle] Optional block to process each text style
605
+ # @return [Enumerator<TextStyle>] Enumerator of text styles
606
+ # @example Find a specific text style
607
+ # romans = drawing.text_styles.find { |ts| ts.name == "Romans" }
608
+ # &: (TextStyle) -> void
609
+ # @rbs return Enumerator[TextStyle] -- enumerator of text styles in document
610
+ def text_styles
611
+ return to_enum(__callee__) unless block_given?
612
+ ole_obj.TextStyles.each { |o| yield app.wrap(o) }
613
+ end
614
+
615
+ # Get a selection set adapter for block references
616
+ # @return [SelectionSetAdapter] Selection set adapter for block references
617
+ # @example Use the selection set to filter block references
618
+ # ss = drawing.block_reference_selection_set
619
+ # ss.filter { |f| f.and(f.layer("Furniture"), f.block_reference) }
620
+ # @rbs return SelectionSetAdapter
621
+ def block_reference_selection_set
622
+ @block_reference_selection_set ||= get_block_reference_selection_set
623
+ end
624
+
625
+ # Get the value of a system variable
626
+ # @param name [String] The name of the system variable
627
+ # @return [Object] The value of the system variable
628
+ # @example Get current layer
629
+ # current_layer = drawing.get_variable("CLAYER")
630
+ # @example Get dimension scale
631
+ # dim_scale = drawing.get_variable("DIMSCALE")
632
+ # @rbs name: String -- the name of the system variable
633
+ # @rbs return Object -- the value of the system variable
634
+ def get_variable(name)
635
+ ole_obj.GetVariable(name)
636
+ end
637
+
638
+ # Set the value of a system variable
639
+ # @param name [String] The name of the system variable
640
+ # @param value [Object] The value to set
641
+ # @return [void]
642
+ # @example Set dimension scale
643
+ # drawing.set_variable("DIMSCALE", 2.5)
644
+ # @example Set current layer
645
+ # drawing.set_variable("CLAYER", "Dimensions")
646
+ # @rbs name: String -- the name of the system variable
647
+ # @rbs value: Object -- the value to set
648
+ # @rbs return void
649
+ def set_variable(name, value)
650
+ ole_obj.SetVariable(name, value)
651
+ end
652
+
653
+ # Set multiple system variables at once
654
+ # @param names [Array<String>] The names of the system variables
655
+ # @param values [Array<Object>] The values to set
656
+ # @return [void]
657
+ # @example Set multiple dimension variables
658
+ # drawing.set_variables(
659
+ # ["DIMSCALE", "DIMLWD", "DIMCLRT"],
660
+ # [2.5, 2, Autocad::Color::Blue]
661
+ # )
662
+ # @rbs names: Array[String] -- the names of the system variables
663
+ # @rbs values: Array[Object] -- the values to set
664
+ # @rbs return void
665
+ def set_variables(names, values)
666
+ atts = names.zip(values).to_h
667
+ atts.each do |k, v|
668
+ set_variable(k, v)
669
+ end
670
+ end
671
+
672
+ # Temporarily set system variables and restore them after the block executes
673
+ # @param names [Array<String>] The names of the system variables
674
+ # @param values [Array<Object>] The values to set
675
+ # @yield Block to execute with the temporary variable values
676
+ # @return [Object] The result of the block
677
+ # @example Temporarily change text settings
678
+ # drawing.with_system_variables(
679
+ # ["TEXTSTYLE", "TEXTSIZE"],
680
+ # ["Standard", 2.5]
681
+ # ) do
682
+ # # Add text with these settings
683
+ # drawing.model.add_text("Note", [0,0,0])
684
+ # end
685
+ # @rbs names: Array[String] -- the names of the system variables
686
+ # @rbs values: Array[Object] -- the values to set
687
+ # @rbs &block: Proc -- the block to execute with the temporary variable values
688
+ # @rbs return Object -- the result of the block
689
+ def with_system_variables(names, values, &block)
690
+ atts
691
+ current_values = get_variables(names)
692
+ set_variables(names, values)
693
+ yield
694
+ ensure
695
+ set_variables(names, current_values)
696
+ end
697
+
698
+ # Get the values of multiple system variables
699
+ # @param *atts [Array<String>] The names of the system variables
700
+ # @return [Array<Object>] The values of the system variables
701
+ # @example Get multiple dimension settings
702
+ # scale, arrow_size = drawing.get_variables("DIMSCALE", "DIMASZ")
703
+ # @rbs *atts: Array[String] -- the names of the system variables
704
+ # @rbs return Array[Object] -- the values of the system variables
705
+ def get_variables(*atts)
706
+ return [] if atts.empty?
707
+ if atts.first.class == Array
708
+ atts = atts.first
709
+ end
710
+ atts.each_with_object([]) do |k, a|
711
+ a << ole_obj.GetVariable(k)
712
+ a
713
+ end
714
+ end
328
715
  # @rbs name: String -- the name to call new selection set
329
716
  # @rbs return Autocad::SelectionSet | nil
330
717
  # def create_selection_set(name, filter: nil)
@@ -336,11 +723,24 @@ module Autocad
336
723
  # nil
337
724
  # end
338
725
 
726
+ # Display a message in the AutoCAD command line
727
+ # @param message [String] The message to display
728
+ # @return [void]
729
+ # @example Show a status message
730
+ # drawing.prompt("Processing complete. Select objects to continue.")
339
731
  # @rbs message: String -- the String to put in Autocad prompt
732
+ # @rbs return void
340
733
  def prompt(message)
341
734
  utility.Prompt(message)
342
735
  end
343
736
 
737
+ # Get a string input from the user
738
+ # @param prompt [String] The string to prompt the user
739
+ # @param spaces [Boolean] Whether the string returned can contain spaces
740
+ # @return [String] User input
741
+ # @raise [Autocad::Error] If input operation fails
742
+ # @example Get a filename from user
743
+ # filename = drawing.get_input_string(prompt: "Enter filename:", spaces: false)
344
744
  # @rbs prompt: String -- the string to prompt the user for String
345
745
  # @rbs has_spaces: bool -- whether the string returned can contain spaces
346
746
  def get_input_string(prompt: "Enter a string", spaces: true)
@@ -349,6 +749,12 @@ module Autocad
349
749
  raise Autocad::Error.new("Error getting string input from user #{ex}")
350
750
  end
351
751
 
752
+ # Get an integer input from the user
753
+ # @param prompt [String] The string to prompt the user
754
+ # @return [Integer] User input
755
+ # @raise [Autocad::Error] If input operation fails
756
+ # @example Get number of copies
757
+ # copies = drawing.get_input_integer(prompt: "Enter number of copies:")
352
758
  # @rbs prompt: String -- the string to prompt the user for Integer
353
759
  def get_input_integer(prompt: "Enter a integer")
354
760
  utility.GetInteger(prompt)
@@ -356,16 +762,28 @@ module Autocad
356
762
  raise Autocad::Error.new("Error getting integer input from user #{ex}")
357
763
  end
358
764
 
765
+ # Get a floating point input from the user
766
+ # @param prompt [String] The string to prompt the user
767
+ # @return [Float] User input
768
+ # @raise [Autocad::Error] If input operation fails
769
+ # @example Get a scale factor
770
+ # scale = drawing.get_float(prompt: "Enter scale factor:")
359
771
  def get_float(prompt: "Enter a float")
360
772
  utility.GetReal(prompt)
361
773
  rescue => ex
362
774
  raise Autocad::Error.new("Error getting float input from user #{ex}")
363
775
  end
364
776
 
365
- # In a running Autocad instance, prompts the user for a point.
366
- # Uses the prompt argument as the prompt string.
367
- # If base_point is provided, it is used as the base point and a
368
- # stretched line is drawn from the base point to the returned point.
777
+ # Prompt the user for a point in the drawing
778
+ # @param prompt [String] The prompt string to display
779
+ # @param base_point [Array, Point3d, nil] Optional base point for rubber-band line
780
+ # @return [Point3d] The selected point
781
+ # @raise [Autocad::Error] If point selection fails
782
+ # @example Get start and end points for a line
783
+ # start = drawing.get_point(prompt: "Select start point:")
784
+ # end_pt = drawing.get_point(prompt: "Select end point:", base_point: start)
785
+ # drawing.model.add_line(start, end_pt)
786
+ # @note If base_point is provided, a stretched line is drawn from the base point
369
787
  # @rbs prompt: String
370
788
  # @rbs base_point: Array, Point3d, nil
371
789
  # @rbs return [Point3d]
@@ -383,44 +801,327 @@ module Autocad
383
801
  raise Autocad::Error.new("Error getting point input from user #{ex}")
384
802
  end
385
803
 
804
+ # Prompt the user to select a rectangular region
805
+ # @return [Array<Point3d>] Two points defining opposite corners of the region
806
+ # @example Get a region for a window selection
807
+ # corner1, corner2 = drawing.get_region
808
+ # # Use corners for window selection
386
809
  def get_region
387
810
  pt = get_point(prompt: "Specify first corner")
388
811
  prompt("X: #{pt.x}, Y: #{pt.y}, Z: #{pt.z}\n")
389
- pt2 = utility.ole_obj.GetCorner(pt.to_ole, "Specify opposite corner: ")
390
- [pt, pt2].map { |p| Point3d.new(p[0], p[1], p[2]) }
812
+ point2 = utility.GetCorner(pt.to_ole, "Specify opposite corner: ")
813
+ pt2 = Point3d(point2)
814
+ [pt, pt2]
391
815
  end
392
816
 
393
- # @rbs return Enumerator[SelectionSet] | void
394
- # @rbs &: (SelectionSet) -> void
817
+ # Get all selection sets in the drawing
818
+ # @return [Enumerator<SelectionSetAdapter>] An enumerator of selection sets
819
+ # @yield [SelectionSetAdapter] Optional block to process each selection set
820
+ # @example Find a specific selection set
821
+ # walls_ss = drawing.selection_sets.find { |ss| ss.name == "WALLS" }
822
+ # @rbs return Enumerator[SelectionSetAdapter] -- an enumerator of selection sets
823
+ # @rbs &: (SelectionSetAdapter) -> void -- optional block to process each selection set
395
824
  def selection_sets
396
825
  return to_enum(__callee__) unless block_given?
397
826
  ole_selection_sets.each { |o| yield app.wrap(o) }
398
827
  end
399
828
 
829
+ # Get the model space of the drawing
830
+ # @return [ModelSpace] The model space container
831
+ # @example Add a circle to model space
832
+ # drawing.model_space.add_circle([0,0,0], 10)
833
+ # @rbs return ModelSpace -- the model space
400
834
  def model_space
401
- ModelSpace.new(ole_obj.ModelSpace, app)
835
+ app.wrap ole_obj.ModelSpace
836
+ end
837
+
838
+ # Get all plot configurations in the drawing
839
+ # @return [Enumerator<PlotConfiguration>] Enumerator of plot configurations
840
+ # @yield [PlotConfiguration] Optional block to process each configuration
841
+ # @example Find a specific plot configuration
842
+ # pdf_config = drawing.plot_configurations.find { |pc| pc.name == "PDF_Export" }
843
+ # @rbs return Enumerator[PlotConfiguration] | void
844
+ # @rbs &: (PlotConfiguration) -> void
845
+ def plot_configurations
846
+ return to_enum(__callee__) unless block_given?
847
+ ole_obj.PlotConfigurations.each { |o| yield app.wrap(o) }
848
+ end
849
+
850
+ # Get all layouts in the drawing (duplicate method, should be removed)
851
+ # @return [Enumerator<Layout>] Enumerator of layouts
852
+ # @yield [Layout] Optional block to process each layout
853
+ # @rbs return Enumerator[Layout] | void
854
+ # @rbs &: (Layout) -> void
855
+ def plot_configurations
856
+ return to_enum(__callee__) unless block_given?
857
+ ole_obj.Layouts.each { |o| yield app.wrap(o) }
402
858
  end
403
859
 
860
+ # Get the paper space of the drawing
861
+ # @return [PaperSpace] The paper space container
862
+ # @example Add a viewport to paper space
863
+ # drawing.paper_space.add_pv_viewport([5,5,0], width: 200, height: 150)
864
+ # @rbs return PaperSpace -- the paper space
404
865
  def paper_space
405
- PaperSpace.new(ole_obj.PaperSpace, app)
866
+ app.wrap ole_obj.PaperSpace
406
867
  end
407
868
 
869
+ # Aliases for convenience
408
870
  alias_method :model, :model_space
409
871
  alias_method :paper, :paper_space
410
872
 
411
- def utility
412
- ole_obj.Utility
413
- end
414
-
873
+ # Get the underlying OLE object
874
+ # @return [WIN32OLE] The OLE object for the drawing
875
+ # @raise [DrawingClose] If drawing is closed or invalid
415
876
  def ole_obj
416
- is_ok = true
877
+ if @drawing_closed || @ole_obj.nil?
878
+ # Use a local variable to avoid recursive call to name method
879
+ drawing_name = @ole_obj.respond_to?(:Name) ? @ole_obj.Name : "unknown"
880
+ raise DrawingClose.new("Drawing is closed", drawing_name)
881
+ end
882
+
883
+ # Check if the ole object is still valid
417
884
  begin
418
885
  @ole_obj.Name
886
+ @ole_obj
887
+ rescue => e
888
+ @drawing_closed = true
889
+ @ole_obj = nil
890
+ # Use a string directly instead of calling name method
891
+ raise DrawingClose.new("Drawing is no longer valid: #{e.message}", "unknown")
892
+ end
893
+ end
894
+
895
+ private
896
+
897
+ def default_event_handler
898
+ handler = EventHandler.new
899
+ @app_event.handler = handler
900
+ handler
901
+ end
902
+
903
+ def app_ole
904
+ app.ole_obj
905
+ end
906
+
907
+ def dwg_path(name: nil, dir: nil)
908
+ name ||= self.name
909
+ dir = Pathname.new(dir || dirname).expand_path
910
+ dir.mkpath unless dir.directory?
911
+ dir + dwg_name(name)
912
+ end
913
+
914
+ def dwg_name(name)
915
+ Pathname.new(name).sub_ext(".dwg")
916
+ end
917
+
918
+ def pdf_path(name: nil, dir: nil)
919
+ name ||= self.name
920
+ dir = Pathname.new(dir || dirname).expand_path
921
+ dir.mkpath unless dir.directory?
922
+ dir + pdf_name(name)
923
+ end
924
+
925
+ def get_current_view_size
926
+ h = get_variable("VIEWSIZE")
927
+ screen_size = Point3d(get_variable("SCREENSIZE"))
928
+ w = h * screen_size.x / screen_size.y
929
+ [w, h]
930
+ end
931
+
932
+ def default_plot_setup
933
+ {device_name: "AutoCAD PDF (High Quality Print).pc3",
934
+ media_name: "ANSI_D_(34.00_x_22.00_Inches)",
935
+ style_sheet: "FAA_Black&Gray.ctb",
936
+ plot_type: :layout,
937
+ rotation: 0,
938
+ paper_units: :inches}
939
+ end
940
+
941
+ # creates the "faa_ansid_bw" plot configuration
942
+ def create_pdf_plot_configutation #: PlotConfiguration
943
+ pc = add_plot_configuration("faa_ansid_bw")
944
+ pc.update(default_plot_setup)
945
+ pc
946
+ end
947
+
948
+ # Return the pdf name for the drawing.
949
+ #
950
+ # If a name is provided use the name provided otherwise use the drawing name
951
+ #
952
+ # @rbs name: String | nil -- change ext to pdf and return Pathname from
953
+ # the name or drawing name
954
+ def pdf_name(name = nil) #: Pathname
955
+ name ||= self.name
956
+ Pathname.new(name).sub_ext(".pdf")
957
+ end
958
+
959
+ def print_pdf(print_path, model: false)
960
+ if model
961
+ "puts print model"
962
+ else
963
+ print_paper_space_pdf(print_path)
964
+ end
965
+ end
966
+
967
+ def print_paper_space_pdf(print_path)
968
+ plotter = plot
969
+ plotter.set_layouts_to_plot paper_space_layout
970
+ if print_path.file?
971
+ print_path.delete if print_path.file?
972
+ end
973
+ plotter.plot_to_file(print_path)
974
+ end
975
+
976
+ # If you copy the file the name to use
977
+ # @rbs backup_str: String -- the bqckup string to use for copies
978
+ def copy_name(backup_str = ".copy")
979
+ lname = name.dup
980
+ ext = File.extname(lname)
981
+ "#{File.basename(lname, ext)}#{backup_str}#{ext}"
982
+ end
983
+
984
+ def get_block_reference_selection_set
985
+ ss = get_selection_set("block_reference") || create_selection_set("block_reference")
986
+ ss.filter do |f|
987
+ f.block_reference
988
+ end
989
+ ss
990
+ end
991
+
992
+ # @rbs name: String -- selection set name to return
993
+ # @rbs return SelectionSet | nil
994
+ def get_ole_selection_set(name)
995
+ return nil if ole_selection_sets.Count == 0
996
+ begin
997
+ ole_selection_sets.Item(name)
419
998
  rescue
420
- is_ok = false
999
+ nil
421
1000
  end
422
- binding.break unless is_ok
423
- @ole_obj
424
1001
  end
1002
+
1003
+ def ole_selection_sets
1004
+ ole_obj.SelectionSets
1005
+ end
1006
+
1007
+ def utility
1008
+ ole_obj.Utility
1009
+ end
1010
+
1011
+ # # @rbs objects: nil | Enumerator[Element] | SelectionSetAdapter | Element
1012
+ # # @rbs return Enumerator[Element]
1013
+ # def get_objects(objects = nil, prompt: "Select objects")
1014
+ # case objects
1015
+ # in nil
1016
+ # # Create a temporary selection set for user selection
1017
+ # ss = create_selection_set("temp_selection_#{Time.now.to_i}")
1018
+ # self.prompt("#{prompt}\n")
1019
+ # ss.select_on_screen
1020
+ # result = ss.each
1021
+ # ss.delete
1022
+ # result
1023
+ # in Autocad::SelectionSetAdapter
1024
+ # objects.each
1025
+ # in Enumerator
1026
+ # objects
1027
+ # in Array
1028
+ # objects.to_enum
1029
+ # else
1030
+ # # Handle single object case by wrapping in an enumerator
1031
+ # [objects].to_enum
1032
+ # end
1033
+ # rescue => ex
1034
+ # app.error_proc.call(ex, self)
1035
+ # end
1036
+
1037
+ # @rbs objects: nil | Enumerator[Element] | SelectionSetAdapter | Element
1038
+ # @rbs alignment: Symbol -- :left, :right, :center, :top, :mid, :bottom
1039
+ # @rbs return void
1040
+ # def align_objects(objects = nil, alignment: :left)
1041
+ # objects = get_objects(objects, prompt: "Select objects to align")
1042
+ #
1043
+ # # Get bounding boxes for all objects
1044
+ # boxes = objects.map do |obj|
1045
+ # begin
1046
+ # obj.bounds
1047
+ # rescue => ex
1048
+ # app.error_proc.call(ex, self)
1049
+ # nil
1050
+ # end
1051
+ # end.compact
1052
+ #
1053
+ # return if boxes.empty?
1054
+ #
1055
+ # # Calculate reference point based on alignment type
1056
+ # reference = case alignment
1057
+ # when :left
1058
+ # boxes.map { |box| box.left }.min
1059
+ # when :right
1060
+ # boxes.map { |box| box.right }.max
1061
+ # when :center
1062
+ # boxes.map { |box| box.center.x }.sum / boxes.length
1063
+ # when :top
1064
+ # boxes.map { |box| box.top }.max
1065
+ # when :bottom
1066
+ # boxes.map { |box| box.bottom }.min
1067
+ # when :mid
1068
+ # boxes.map { |box| box.center.y }.sum / boxes.length
1069
+ # else
1070
+ # raise ArgumentError, "Invalid alignment type: #{alignment}. Must be :left, :right, :center, :top, :mid, or :bottom"
1071
+ # end
1072
+ #
1073
+ # # Move each object to align with reference point
1074
+ # objects.zip(boxes).each do |obj, box|
1075
+ # next unless box
1076
+ #
1077
+ # delta = case alignment
1078
+ # when :left
1079
+ # [reference - box.left, 0, 0]
1080
+ # when :right
1081
+ # [reference - box.right, 0, 0]
1082
+ # when :center
1083
+ # [reference - box.center.x, 0, 0]
1084
+ # when :top
1085
+ # [0, reference - box.top, 0]
1086
+ # when :bottom
1087
+ # [0, reference - box.bottom, 0]
1088
+ # when :mid
1089
+ # [0, reference - box.center.y, 0]
1090
+ # end
1091
+ #
1092
+ # begin
1093
+ # # Create points for move_ole
1094
+ # pt1 = Point3d(0, 0, 0)
1095
+ # pt2 = Point3d(*delta)
1096
+ # obj.move_ole(pt1.to_ole, pt2.to_ole)
1097
+ # rescue => ex
1098
+ # app.error_proc.call(ex, self)
1099
+ # end
1100
+ # end
1101
+ #
1102
+ # regen
1103
+ # end
1104
+ # case objects
1105
+ # in nil
1106
+ # # Create a temporary selection set for user selection
1107
+ # ss = create_selection_set("temp_selection_#{Time.now.to_i}")
1108
+ # self.prompt("#{prompt}\n")
1109
+ # ss.select_on_screen
1110
+ # result = ss.each
1111
+ # ss.delete
1112
+ # result
1113
+ # in Autocad::SelectionSetAdapter
1114
+ # objects.each
1115
+ # in Enumerator
1116
+ # objects
1117
+ # in Array
1118
+ # objects.to_enum
1119
+ # else
1120
+ # # Handle single object case by wrapping in an enumerator
1121
+ # [objects].to_enum
1122
+ # end
1123
+ # rescue => ex
1124
+ # app.error_proc.call(ex, self)
1125
+ # end
425
1126
  end
426
1127
  end