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.
- checksums.yaml +4 -4
- data/.rubocop/minitest.yml +2 -2
- data/.rubocop/strict.yml +4 -4
- data/.rubocop.yml +36 -33
- data/CHANGELOG.md +5 -5
- data/LICENSE.txt +21 -21
- data/README.md +134 -39
- data/Rakefile +26 -10
- data/exe/autocad +3 -3
- data/gemfiles/rubocop.gemfile +2 -1
- data/lib/autocad/app.rb +127 -28
- data/lib/autocad/arc.rb +3 -0
- data/lib/autocad/block.rb +11 -6
- data/lib/autocad/block_reference.rb +33 -4
- data/lib/autocad/bounding_box.rb +202 -0
- data/lib/autocad/dim_style.rb +4 -0
- data/lib/autocad/drawing.rb +873 -172
- data/lib/autocad/element.rb +217 -25
- data/lib/autocad/errors.rb +9 -0
- data/lib/autocad/filter.rb +502 -168
- data/lib/autocad/layer.rb +129 -41
- data/lib/autocad/layout.rb +120 -0
- data/lib/autocad/line.rb +154 -55
- data/lib/autocad/message_box.rb +95 -95
- data/lib/autocad/model.rb +217 -89
- data/lib/autocad/mtext.rb +189 -110
- data/lib/autocad/plot.rb +45 -0
- data/lib/autocad/plot_configuration.rb +372 -0
- data/lib/autocad/point.rb +7 -0
- data/lib/autocad/point3d.rb +18 -11
- data/lib/autocad/pviewport.rb +136 -21
- data/lib/autocad/selection_filter.rb +358 -180
- data/lib/autocad/selection_set.rb +140 -61
- data/lib/autocad/selection_set_adapter.rb +187 -8
- data/lib/autocad/spline.rb +27 -0
- data/lib/autocad/text.rb +66 -11
- data/lib/autocad/text_style.rb +4 -0
- data/lib/autocad/version.rb +1 -1
- data/lib/autocad/viewport.rb +57 -0
- data/lib/autocad.rb +126 -30
- data/lib/faa/cleanup.rb +137 -0
- data/lib/win32ole_helper.rb +52 -0
- data/rbs_collection.lock.yaml +38 -18
- data/sig/generated/autocad/app.rbs +278 -251
- data/sig/generated/autocad/arc.rbs +6 -3
- data/sig/generated/autocad/block.rbs +8 -5
- data/sig/generated/autocad/block_reference.rbs +99 -59
- data/sig/generated/autocad/bounding_box.rbs +78 -0
- data/sig/generated/autocad/dim_style.rbs +6 -0
- data/sig/generated/autocad/drawing.rbs +597 -158
- data/sig/generated/autocad/element.rbs +233 -166
- data/sig/generated/autocad/errors.rbs +29 -23
- data/sig/generated/autocad/filter.rbs +388 -60
- data/sig/generated/autocad/layer.rbs +76 -19
- data/sig/generated/autocad/layout.rbs +64 -0
- data/sig/generated/autocad/line.rbs +128 -25
- data/sig/generated/autocad/message_box.rbs +81 -81
- data/sig/generated/autocad/model.rbs +115 -41
- data/sig/generated/autocad/mtext.rbs +123 -0
- data/sig/generated/autocad/plot.rbs +26 -0
- data/sig/generated/autocad/plot_configuration.rbs +176 -0
- data/sig/generated/autocad/point.rbs +7 -0
- data/sig/generated/autocad/point3d.rbs +70 -66
- data/sig/generated/autocad/pviewport.rbs +64 -0
- data/sig/generated/autocad/selection_filter.rbs +226 -50
- data/sig/generated/autocad/selection_set.rbs +112 -37
- data/sig/generated/autocad/selection_set_adapter.rbs +235 -28
- data/sig/generated/autocad/spline.rbs +22 -0
- data/sig/generated/autocad/text.rbs +66 -7
- data/sig/generated/autocad/text_style.rbs +6 -0
- data/sig/generated/autocad/viewport.rbs +19 -2
- data/sig/generated/autocad.rbs +140 -68
- data/sig/generated/faa/cleanup.rbs +53 -0
- data/sig/generated/win32ole_helper.rbs +9 -0
- data/sig/prototype/lib/autocad/app.rbs +3 -1
- data/sig/prototype/lib/autocad/bounding_box.rbs +15 -0
- data/sig/prototype/lib/autocad/drawing.rbs +6 -0
- data/sig/prototype/lib/autocad/layer.rbs +5 -0
- data/sig/prototype/lib/autocad/viewport.rbs +7 -0
- data/sig/prototype/lib/autocad.rbs +1 -1
- metadata +29 -5
- data/event_handler.log +0 -24
- data/sig/generated/autocad/text_node.rbs +0 -37
data/lib/autocad/drawing.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
#
|
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.
|
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
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
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
|
-
|
149
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
# @
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
#
|
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
|
-
#
|
190
|
-
# @
|
191
|
-
|
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
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
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 "
|
240
|
-
ole_obj.
|
310
|
+
raise "layer not found" unless layer
|
311
|
+
ole_obj.ActiveLayer = layer.to_ole
|
241
312
|
end
|
242
313
|
|
243
|
-
|
244
|
-
|
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
|
-
|
248
|
-
|
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
|
-
|
358
|
+
paper_space
|
359
|
+
|
255
360
|
else
|
256
|
-
|
361
|
+
model_space
|
257
362
|
end
|
258
363
|
end
|
259
364
|
|
260
365
|
# Close the drawing
|
261
|
-
# @
|
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
|
266
|
-
|
267
|
-
|
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
|
-
#
|
280
|
-
# @
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
#
|
303
|
-
# @
|
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
|
-
#
|
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
|
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
|
-
#
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
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
|
-
|
390
|
-
|
812
|
+
point2 = utility.GetCorner(pt.to_ole, "Specify opposite corner: ")
|
813
|
+
pt2 = Point3d(point2)
|
814
|
+
[pt, pt2]
|
391
815
|
end
|
392
816
|
|
393
|
-
#
|
394
|
-
# @
|
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
|
-
|
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
|
-
|
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
|
-
|
412
|
-
|
413
|
-
|
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
|
-
|
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
|
-
|
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
|