-
-
Notifications
You must be signed in to change notification settings - Fork 55
/
dimension_utils.rb
369 lines (333 loc) · 10.9 KB
/
dimension_utils.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
module Ladb::OpenCutList
require 'singleton'
# Format - just here for convenience
DECIMAL = Length::Decimal
ARCHITECTURAL = Length::Architectural
ENGINEERING = Length::Engineering
FRACTIONAL = Length::Fractional
# Unit - just here for convenience
INCHES = Length::Inches
FEET = Length::Feet
MILLIMETER = Length::Millimeter
CENTIMETER = Length::Centimeter
METER = Length::Meter
# Unit signs
UNIT_SIGN_INCHES = '"'
UNIT_SIGN_FEET = "'"
UNIT_SIGN_METER = 'm'
UNIT_SIGN_CENTIMETER = 'cm'
UNIT_SIGN_MILLIMETER = 'mm'
UNIT_SIGN_METER_2 = 'm²'
UNIT_SIGN_FEET_2 = 'ft²'
UNIT_SIGN_METER_3 = 'm³'
UNIT_SIGN_FEET_3 = 'ft³'
class DimensionUtils
include Singleton
attr_accessor :decimal_separator, :length_unit
# Separators
LIST_SEPARATOR = ';'.freeze
DXD_SEPARATOR = 'x'.freeze
@separator
@length_unit
@length_format
@length_precision
def initialize
begin
'1.0'.to_l
@decimal_separator = '.'
rescue
@decimal_separator = ','
end
fetch_length_options
end
def fetch_length_options
model = Sketchup.active_model
@length_unit = model ? model.options['UnitsOptions']['LengthUnit'] : MILLIMETER
@length_format = model ? model.options['UnitsOptions']['LengthFormat'] : DECIMAL
@length_precision = model ? model.options['UnitsOptions']['LengthPrecision'] : 0
end
# -----
def from_fractional(i)
input_split = (i.split('/').map( &:to_i ))
Rational(*input_split)
end
def model_units_to_inches(i)
case @length_unit
when MILLIMETER
return i / 25.4
when CENTIMETER
return i / 2.54
when METER
return i / 0.0254
when FEET
return i * 12
else
return i
end
end
def unit_sign
case @length_unit
when MILLIMETER
return UNIT_SIGN_MILLIMETER
when CENTIMETER
return UNIT_SIGN_CENTIMETER
when METER
return UNIT_SIGN_METER
when FEET
return UNIT_SIGN_FEET
else
return UNIT_SIGN_INCHES
end
end
def model_unit_is_metric
case @length_unit
when MILLIMETER, CENTIMETER, METER
return true
else
return false
end
end
# Take a fraction and try to simplify it by turning:
# 1. x/0 into x
# 2. 0/x into 0
#
def simplify(i)
i = i.to_s
match = i.match(/^(\d*)\/(\d*)$/)
if match
num, den = match.captures
if num == '0'
return '0'
elsif den == '1'
return num
else
return i
end
else
i
end
end
# Take a single dimension as a string and
# 1. add units if none are present, assuming that no units means model units
# 2. prepend zero if just unit given (may happen!)
# 3. add units if none
# 4. convert garbage into 0
#
def str_add_units(i)
return '0' + unit_sign if i.nil? || i.empty?
i = i.strip
nu = ""
sum = 0
if i.is_a?(String)
if match = i.match(/^(~?\s*)(\d*(([.,])\d*)?)?\s*(#{UNIT_SIGN_MILLIMETER}|#{UNIT_SIGN_CENTIMETER}|#{UNIT_SIGN_METER}|#{UNIT_SIGN_FEET}|#{UNIT_SIGN_INCHES})?$/)
one, two, three, four, five = match.captures
if five.nil?
nu = one + two + unit_sign
elsif two.empty? and three.nil? # two could not be nil
nu = one + "0" + five
else
nu = one + two + five
#nu = nu.sub(/"/, '\"') # four will not be escaped in this case
end
if !four.nil?
nu.sub!(four, @decimal_separator)
end
elsif match = i.match(/^~?\s*(((\d*([.,]\d*)?)(\s*\')?)?\s+)?((\d*)\s+)?(\d*\/\d*)?(\s*\")?$/)
one, two, three, four, five, six, seven, eight, nine = match.captures
if three.nil? && six.nil?
nu = simplify(from_fractional(eight)).to_s + '"'
#sum = from_fractional(eight).to_f
elsif seven.nil? && five.nil?
nu = three + " " + eight + '"'
#sum = three.to_f + from_fractional(eight).to_f
elsif seven.nil? && five == "'"
nu = three + "' " + eight + '"'
#sum = 12*three.to_f + from_fractional(eight).to_f
else
nu = three + "' " + seven + " " + eight + '"'
#sum = 12*three.to_f + six.to_f + from_fractional(eight).to_f
end
else
nu = '0' + unit_sign # garbage becomes 0
end
end
nu
end
# Takes a single dimension as a string and converts it into a decimal inch
# returns the float as a string
def str_to_ifloat(i)
i = i.sub(/~/, '') # strip approximate sign away
i = i.strip
sum = 0
# make sure the entry is a string and starts with the proper magic
if i.is_a?(String)
if match = i.match(/^(\d*([.,]\d*)?)?\s*(#{UNIT_SIGN_MILLIMETER}|#{UNIT_SIGN_CENTIMETER}|#{UNIT_SIGN_METER}|#{UNIT_SIGN_FEET}|#{UNIT_SIGN_INCHES})?$/)
one, two, three = match.captures
#puts "i = #{'%7s' % i} => decimal/integer number:: #{'%7s' % one} #{'%7s' % three}"
one = one.sub(/,/, '.')
one = one.to_f
if three.nil?
sum = model_units_to_inches(one)
elsif three == UNIT_SIGN_MILLIMETER
sum = one / 25.4
elsif three == UNIT_SIGN_CENTIMETER
sum = one / 2.54
elsif three == UNIT_SIGN_METER
sum = one / 0.0254
elsif three == UNIT_SIGN_FEET
sum = 12 * one
elsif three == UNIT_SIGN_INCHES
sum = one
end
elsif match = i.match(/^(((\d*([.,]\d*)?)(\s*\')?)?\s+)?((\d*)\s+)?(\d*\/\d*)?(\s*\")?$/)
one, two, three, four, five, six, seven, eight, nine = match.captures
if three.nil? && six.nil?
#puts "i = #{'%15s' % i} => fractional+unit:: #{'%7s' % eight} #{nine}"
sum = from_fractional(eight).to_f
elsif seven.nil? && five.nil?
#puts "i = #{'%15s' % i} => inch+fractional+unit #{'%7s' % three} #{'%7s' % eight} #{nine}"
sum = three.to_f + from_fractional(eight).to_f
elsif seven.nil? && five == "'"
#puts "i = #{'%15s' % i} => feet+fractional+unit:: #{'%7s' % three} #{four} #{'%7s' % seven} #{eight} #{nine}"
sum = 12 * three.to_f + from_fractional(eight).to_f
else
#puts "i = #{'%15s' % i} => feet+inch+fractional+unit:: #{'%7s' % three} #{five} #{'%7s' % seven}#{'%7s' % eight} #{nine}"
sum = 12 * three.to_f + six.to_f + from_fractional(eight).to_f
sum = sum.to_f # force number to be a float, may not be necessary!
end
else
sum = 0 # garbage always becomes 0
end
end
sum = sum.to_s.sub(/\./, @decimal_separator)
sum + UNIT_SIGN_INCHES
end
# Takes a single number in a string and converts it to a string
# in Sketchup internal format (inches, decimal) with unit sign
#
def str_to_istr(i)
str_to_ifloat(i)
end
# Splits a string in the form d;d;...
# into single d's and applies the function f to each element
# returns the concatenated string in the same format
#
def dd_transform(i, f)
return '' if i.nil?
a = i.split(LIST_SEPARATOR)
r = []
a.each do |e|
r << send(f, e)
end
r.join(LIST_SEPARATOR)
end
def dd_add_units(i)
dd_transform(i, :str_add_units)
end
def dd_to_ifloats(i)
dd_transform(i, :str_to_ifloat)
end
# Splits a string in the form dxd;dxd;...
# into single d's and applies the function f to each element
# returns the concatenated string in the same format
#
def dxd_transform(i, f)
return '' if i.nil?
a = i.split(LIST_SEPARATOR)
r = []
a.each do |e|
ed = e.split(DXD_SEPARATOR)
ed[0] = '0' if ed[0].nil? || ed[0].empty?
ed[1] = '0' if ed[1].nil? || ed[1].empty?
r << (send(f, ed[0]) + ' ' + DXD_SEPARATOR + ' ' + send(f, ed[1]))
end
r.join(LIST_SEPARATOR)
end
# Take a string containing dimensions in the form dxd;dxd;dxd;...
# and make sure they all have units and are not empty
# without units, model units are assumed and added
#
def dxd_add_units(i)
dxd_transform(i, :str_add_units)
end
# Take a string containing dimensions in the form dxd;dxd;dxd;...
# and convert them into a decimal inch number (Sketchup internal
# format)
# the number is returned as a string NOT a length or float
#
def dxd_to_ifloats(i)
dxd_transform(i, :str_to_ifloat)
end
# -----
# Take a float containing a length in inch
# and convert it to a string representation according to the
# local unit settings.
#
def format_to_readable_length(f)
if f.nil?
return nil
end
if model_unit_is_metric
multiplier = 0.0254
precision = 3
unit_sign = UNIT_SIGN_METER
else
multiplier = 1 / 12.0
precision = 2
unit_sign = UNIT_SIGN_FEET
end
format_value(f, multiplier, precision, unit_sign)
end
# Take a float containing an area in inch²
# and convert it to a string representation according to the
# local unit settings.
#
def format_to_readable_area(f2)
if f2.nil?
return nil
end
if model_unit_is_metric
multiplier = 0.0254**2
precision = [3, @length_precision].max
unit_sign = UNIT_SIGN_METER_2
else
multiplier = 1 / 144.0
precision = [2, @length_precision].max
unit_sign = UNIT_SIGN_FEET_2
end
format_value(f2, multiplier, precision, unit_sign)
end
# Take a float containing a volume in inch³
# and convert it to a string representation according to the
# local unit settings.
#
def format_to_readable_volume(f3)
if f3.nil?
return nil
end
if model_unit_is_metric
multiplier = 0.0254**3
precision = [3, @length_precision].max
unit_sign = UNIT_SIGN_METER_3
else
multiplier = 1 / 1728.0
precision = [2, @length_precision].max
unit_sign = UNIT_SIGN_FEET_3
end
format_value(f3, multiplier, precision, unit_sign)
end
def format_value(f, multiplier, precision, unit_sign)
value = f * multiplier
rounded_value = value.round(precision)
((value - rounded_value).abs > 0.0001 ? '~ ' : '') + ("%.#{precision}f" % rounded_value).tr('.', @decimal_separator) + unit_sign
end
# -----
# Take a float containing a length in inch
# and truncate it to "Sketchup" precision
def truncate_length_value(f)
return f if f == 0
factor = 10**4 # 4 = 0.0000 arbitrary length precision
(f * factor).floor / (factor * 1.0)
end
end
end