ifmapper 0.5 → 0.6
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.
- data/HISTORY.txt +26 -0
- data/IFMapper.gemspec +1 -1
- data/icons/room_e.gif +0 -0
- data/icons/room_e.xpm +39 -0
- data/icons/room_n.gif +0 -0
- data/icons/room_n.xpm +40 -0
- data/icons/room_ne.gif +0 -0
- data/icons/room_ne.xpm +80 -0
- data/icons/room_nw.gif +0 -0
- data/icons/room_nw.xpm +80 -0
- data/icons/room_s.gif +0 -0
- data/icons/room_s.xpm +40 -0
- data/icons/room_se.gif +0 -0
- data/icons/room_se.xpm +80 -0
- data/icons/room_sw.gif +0 -0
- data/icons/room_sw.xpm +80 -0
- data/icons/room_w.gif +0 -0
- data/icons/room_w.xpm +39 -0
- data/lib/IFMapper/AStar.rb +243 -0
- data/lib/IFMapper/FXConnection.rb +101 -72
- data/lib/IFMapper/FXMap.rb +155 -230
- data/lib/IFMapper/FXMapColorBox.rb +92 -0
- data/lib/IFMapper/FXMapDialogBox.rb +83 -7
- data/lib/IFMapper/FXMapperSettings.rb +1 -0
- data/lib/IFMapper/FXMapperWindow.rb +82 -48
- data/lib/IFMapper/FXRoom.rb +43 -17
- data/lib/IFMapper/{FXPage.rb → FXSection.rb} +3 -3
- data/lib/IFMapper/{FXPageDialogBox.rb → FXSectionDialogBox.rb} +6 -6
- data/lib/IFMapper/FXSpline.rb +10 -14
- data/lib/IFMapper/IFMReader.rb +39 -31
- data/lib/IFMapper/Map.rb +42 -43
- data/lib/IFMapper/MapPrinting.rb +161 -0
- data/lib/IFMapper/PDFMapExporter.rb +128 -51
- data/lib/IFMapper/Room.rb +11 -1
- data/lib/IFMapper/{Page.rb → Section.rb} +65 -42
- metadata +34 -15
@@ -0,0 +1,161 @@
|
|
1
|
+
|
2
|
+
# Common printing add-ons
|
3
|
+
class FXSection
|
4
|
+
attr_accessor :xoff, :yoff, :page, :pxlen, :pylen, :rotate
|
5
|
+
end
|
6
|
+
|
7
|
+
class Page
|
8
|
+
attr_accessor :xlen, :ylen, :sections, :rotate
|
9
|
+
def initialize(xlen = 0, ylen = 0)
|
10
|
+
@xlen = xlen
|
11
|
+
@ylen = ylen
|
12
|
+
@rotate = false
|
13
|
+
@sections = []
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Map
|
18
|
+
#
|
19
|
+
# This code section is largely a copy of similar code used in
|
20
|
+
# IFM's C code.
|
21
|
+
# It tries to pack multiple map sections into a single page.
|
22
|
+
#
|
23
|
+
def pack_sections(xmax, ymax)
|
24
|
+
spacing = 0.0
|
25
|
+
|
26
|
+
# initialize -- one section per page
|
27
|
+
pages = []
|
28
|
+
@sections.each { |sect|
|
29
|
+
xlen, ylen = sect.rooms_width_height
|
30
|
+
sect.xoff = 0.0
|
31
|
+
sect.yoff = 0.0
|
32
|
+
|
33
|
+
page = Page.new(xlen+2, ylen+2)
|
34
|
+
pages.push page
|
35
|
+
page.sections << sect
|
36
|
+
}
|
37
|
+
|
38
|
+
ratio = xmax.to_f / ymax
|
39
|
+
|
40
|
+
packed = 1
|
41
|
+
while packed > 0
|
42
|
+
newpages = []
|
43
|
+
pos = packed = 0
|
44
|
+
while pos < pages.size
|
45
|
+
p1 = pages[pos]
|
46
|
+
x1 = p1.xlen
|
47
|
+
y1 = p1.ylen
|
48
|
+
|
49
|
+
# Check if it's better off rotated
|
50
|
+
p1.rotate = ((x1 < y1 and xmax > ymax) or
|
51
|
+
(x1 > y1 and xmax < ymax))
|
52
|
+
|
53
|
+
# Check if this is the last page
|
54
|
+
if pos + 1 == pages.size
|
55
|
+
newpages.push p1
|
56
|
+
break
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get following page
|
60
|
+
p2 = pages[pos+1]
|
61
|
+
x2 = p2.xlen
|
62
|
+
y2 = p2.ylen
|
63
|
+
|
64
|
+
# Try combining pages in X direction
|
65
|
+
xc1 = x1 + x2 + spacing
|
66
|
+
yc1 = [y1, y2].max
|
67
|
+
v1 = (xc1 <= xmax and yc1 <= ymax)
|
68
|
+
r1 = xc1.to_f / yc1
|
69
|
+
|
70
|
+
# Try combining pages in Y direction
|
71
|
+
xc2 = [x1, x2].max
|
72
|
+
yc2 = y1 + y2 + spacing
|
73
|
+
v2 = (xc2 <= xmax and yc2 <= ymax)
|
74
|
+
r2 = xc2.to_f / yc2
|
75
|
+
|
76
|
+
# See which is best
|
77
|
+
if v1 and v2
|
78
|
+
if (ratio - r1).abs < (ratio - r2).abs
|
79
|
+
v2 = false
|
80
|
+
else
|
81
|
+
v1 = false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Just copy page if nothing can be done
|
86
|
+
if not v1 and not v2
|
87
|
+
newpages.push(p1)
|
88
|
+
pos += 1
|
89
|
+
next
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create merged page
|
93
|
+
page = Page.new
|
94
|
+
xo1 = yo1 = xo2 = yo2 = 0
|
95
|
+
|
96
|
+
if v1
|
97
|
+
page.xlen = xc1
|
98
|
+
page.ylen = yc1
|
99
|
+
xo2 = x1 + spacing
|
100
|
+
|
101
|
+
if y1 < y2
|
102
|
+
yo1 = (yc1 - y1) / 2
|
103
|
+
else
|
104
|
+
yo2 = (yc1 - y2) / 2
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if v2
|
109
|
+
page.xlen = xc2
|
110
|
+
page.ylen = yc2
|
111
|
+
yo1 = y2 + spacing
|
112
|
+
|
113
|
+
if x1 < x2
|
114
|
+
xo1 = (xc2 - x1) / 2
|
115
|
+
else
|
116
|
+
xo2 = (xc2 - x2) / 2
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Copy sections to new page, updating offsets
|
121
|
+
opsects = p1.sections
|
122
|
+
opsects.each { |sect|
|
123
|
+
page.sections.push sect
|
124
|
+
sect.xoff += xo1
|
125
|
+
sect.yoff += yo1
|
126
|
+
}
|
127
|
+
|
128
|
+
opsects = p2.sections
|
129
|
+
opsects.each { |sect|
|
130
|
+
page.sections.push sect
|
131
|
+
sect.xoff += xo2
|
132
|
+
sect.yoff += yo2
|
133
|
+
}
|
134
|
+
|
135
|
+
# Add merged page to list and go to next page pair
|
136
|
+
newpages.push page
|
137
|
+
pos += 2
|
138
|
+
packed += 1
|
139
|
+
end
|
140
|
+
pages = newpages
|
141
|
+
end
|
142
|
+
|
143
|
+
# Give each section its page info and clean up
|
144
|
+
num = 0
|
145
|
+
pages.each { |page|
|
146
|
+
psects = page.sections
|
147
|
+
xlen = page.xlen
|
148
|
+
ylen = page.ylen
|
149
|
+
rflag = page.rotate
|
150
|
+
|
151
|
+
num += 1
|
152
|
+
psects.each { |sect|
|
153
|
+
sect.page = num
|
154
|
+
sect.pxlen = xlen
|
155
|
+
sect.pylen = ylen
|
156
|
+
sect.rotate = rflag
|
157
|
+
}
|
158
|
+
}
|
159
|
+
return num
|
160
|
+
end
|
161
|
+
end
|
@@ -11,6 +11,8 @@ rescue => e
|
|
11
11
|
raise "Please install 'pdf writer' library for Acrobat PDF support."
|
12
12
|
end
|
13
13
|
|
14
|
+
require 'IFMapper/MapPrinting'
|
15
|
+
|
14
16
|
PDF_ZOOM = 0.5
|
15
17
|
PDF_ROOM_WIDTH = W * PDF_ZOOM # 60
|
16
18
|
PDF_ROOM_HEIGHT = H * PDF_ZOOM # 37.5
|
@@ -18,7 +20,6 @@ PDF_ROOM_WS = WS * PDF_ZOOM
|
|
18
20
|
PDF_ROOM_HS = HS * PDF_ZOOM
|
19
21
|
PDF_MARGIN = 20
|
20
22
|
|
21
|
-
|
22
23
|
#
|
23
24
|
# Open all the map class and add all pdf methods there
|
24
25
|
# Gotta love Ruby's flexibility to just inject in new methods.
|
@@ -87,8 +88,8 @@ class FXConnection
|
|
87
88
|
return if not @roomB # PDF does not print unfinished complex connections
|
88
89
|
|
89
90
|
dir = @roomA.exits.index(self)
|
90
|
-
x1, y1 = @roomA.pdf_corner(
|
91
|
-
x2, y2 = @roomB.pdf_corner(
|
91
|
+
x1, y1 = @roomA.pdf_corner(opts, self, dir)
|
92
|
+
x2, y2 = @roomB.pdf_corner(opts, self)
|
92
93
|
pdf.move_to(x1, y1)
|
93
94
|
pdf.line_to(x2, y2).stroke
|
94
95
|
pdf_draw_arrow(pdf, opts, x1, y1, x2, y2)
|
@@ -102,8 +103,49 @@ class FXConnection
|
|
102
103
|
end
|
103
104
|
end
|
104
105
|
|
106
|
+
#
|
107
|
+
# Draw the connection text next to the arrow ('I', 'O', etc)
|
108
|
+
#
|
109
|
+
def pdf_draw_text(pdf, x, y, dir, text, arrow)
|
110
|
+
if dir == 7 or dir < 6 and dir != 1
|
111
|
+
if arrow and (dir == 0 or dir == 4)
|
112
|
+
x += 5
|
113
|
+
end
|
114
|
+
x += 2.5
|
115
|
+
elsif dir == 6 or dir == 1
|
116
|
+
x -= 7.5
|
117
|
+
end
|
118
|
+
|
119
|
+
if dir > 5 or dir < 4
|
120
|
+
if arrow and (dir == 6 or dir == 2)
|
121
|
+
y += 5
|
122
|
+
end
|
123
|
+
y += 2.5
|
124
|
+
elsif dir == 4 or dir == 5
|
125
|
+
y -= 7.5
|
126
|
+
end
|
127
|
+
|
128
|
+
font_size = 8
|
129
|
+
pdf.add_text(x, y, text, font_size)
|
130
|
+
end
|
131
|
+
|
132
|
+
def pdf_draw_exit_text(pdf, opts)
|
133
|
+
if @exitAtext != 0
|
134
|
+
dir = @roomA.exits.index(self)
|
135
|
+
x, y = @roomA.pdf_corner(opts, self, dir)
|
136
|
+
pdf_draw_text( pdf, x, y, dir,
|
137
|
+
EXIT_TEXT[@exitAtext], @dir == BtoA)
|
138
|
+
end
|
139
|
+
if @exitBtext != 0
|
140
|
+
dir = @roomB.exits.rindex(self)
|
141
|
+
x, y = @roomB.pdf_corner(opts, self, dir)
|
142
|
+
pdf_draw_text( pdf, x, y, dir,
|
143
|
+
EXIT_TEXT[@exitBtext], @dir == AtoB)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
105
147
|
def pdf_draw(pdf, opts)
|
106
|
-
|
148
|
+
pdf_draw_exit_text(pdf, opts)
|
107
149
|
if @type == SPECIAL
|
108
150
|
s = PDF::Writer::StrokeStyle.new(2, :dash => {
|
109
151
|
:pattern => [2],
|
@@ -128,7 +170,7 @@ end
|
|
128
170
|
|
129
171
|
|
130
172
|
class FXRoom
|
131
|
-
def pdf_corner(
|
173
|
+
def pdf_corner( opts, c, idx = nil )
|
132
174
|
x, y = _corner(c, idx)
|
133
175
|
y = -y
|
134
176
|
|
@@ -160,30 +202,46 @@ class FXRoom
|
|
160
202
|
pdf.rectangle(x, y, opts['w'], opts['h']).fill_stroke
|
161
203
|
end
|
162
204
|
|
205
|
+
def pdf_draw_text( pdf, opts, x, y, text, font_size )
|
206
|
+
miny = (opts['height'] - @y) * opts['hh'] + opts['hs_2'] +
|
207
|
+
opts['margin_2']
|
208
|
+
while text != ''
|
209
|
+
text = pdf.add_text_wrap(x, y, opts['w'] - 2, text, font_size)
|
210
|
+
y -= font_size
|
211
|
+
break if y <= miny
|
212
|
+
end
|
213
|
+
return [x, y]
|
214
|
+
end
|
215
|
+
|
216
|
+
def pdf_draw_objects(pdf, opts, x, y)
|
217
|
+
font_size = 6
|
218
|
+
objs = @objects.split("\n")
|
219
|
+
objs = objs.join(', ')
|
220
|
+
return pdf_draw_text( pdf, opts, x, y, objs, font_size )
|
221
|
+
end
|
222
|
+
|
163
223
|
def pdf_draw_name(pdf, opts)
|
164
224
|
# We could also use pdf_corner(7) here
|
165
225
|
x = @x * opts['ww'] + opts['margin_2'] + opts['ws_2'] + 2
|
166
226
|
y = opts['height'] - @y
|
167
|
-
y = y * opts['hh'] + opts['margin_2'] + opts['hs_2'] + opts['h'] - 10
|
168
227
|
font_size = 8
|
228
|
+
y = y * opts['hh'] + opts['margin_2'] + opts['hs_2'] + opts['h'] -
|
229
|
+
(font_size + 2)
|
169
230
|
pdf.stroke_color(Color::RGB::Black)
|
170
231
|
pdf.fill_color(Color::RGB::Black)
|
171
|
-
|
172
|
-
while text != ''
|
173
|
-
text = pdf.add_text_wrap(x, y, opts['w'], text, font_size)
|
174
|
-
y -= font_size
|
175
|
-
end
|
232
|
+
return pdf_draw_text( pdf, opts, x, y, @name, font_size )
|
176
233
|
end
|
177
234
|
|
178
235
|
def pdf_draw( pdf, opts, idx )
|
179
236
|
pdf_draw_box( pdf, opts )
|
180
|
-
pdf_draw_name( pdf, opts )
|
237
|
+
x, y = pdf_draw_name( pdf, opts )
|
238
|
+
pdf_draw_objects(pdf, opts, x, y)
|
181
239
|
end
|
182
240
|
end
|
183
241
|
|
184
242
|
|
185
243
|
|
186
|
-
class
|
244
|
+
class FXSection
|
187
245
|
|
188
246
|
def pdf_draw_grid(pdf, opts, w, h )
|
189
247
|
(0...w).each { |xx|
|
@@ -195,68 +253,80 @@ class FXPage
|
|
195
253
|
}
|
196
254
|
end
|
197
255
|
|
198
|
-
def pdf_draw_mapname( pdf, opts, mapname )
|
199
|
-
return if not mapname or mapname == ''
|
200
|
-
pdf.text( mapname,
|
201
|
-
:font_size => 24,
|
202
|
-
:justification => :center
|
203
|
-
)
|
204
|
-
end
|
205
256
|
|
206
|
-
def
|
257
|
+
def pdf_draw_section_name( pdf, opts )
|
207
258
|
return if not @name or @name == ''
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
259
|
+
y = (opts['height']) * opts['hh'] + 16
|
260
|
+
xymin, xymax = min_max_rooms
|
261
|
+
x = (xymax[0] + 2) * opts['ww'] / 2
|
262
|
+
pdf.add_text( x, y, @name, 16 )
|
212
263
|
end
|
213
264
|
|
214
265
|
|
215
266
|
def pdf_draw(pdf, opts, mapname )
|
216
|
-
|
217
|
-
|
218
|
-
#
|
219
|
-
# Determine if better to print page in landscape mode
|
220
|
-
#
|
221
|
-
landscape = false
|
222
|
-
if landscape
|
267
|
+
if rotate
|
223
268
|
pdf.rotate_axis(90.0)
|
224
|
-
pdf.translate_axis( 0, -pdf.
|
269
|
+
pdf.translate_axis( 0, -pdf.page_height )
|
225
270
|
end
|
226
271
|
|
272
|
+
# Move section to its position in page
|
273
|
+
pack = [@xoff * opts['ww'], @yoff * -opts['hh']]
|
274
|
+
pdf.translate_axis( pack[0], pack[1] )
|
275
|
+
|
227
276
|
# Use times-roman as font
|
228
277
|
pdf.select_font 'Times-Roman'
|
229
278
|
pdf.stroke_color(Color::RGB::Black)
|
230
279
|
pdf.fill_color(Color::RGB::Black)
|
231
280
|
|
232
|
-
|
233
|
-
|
281
|
+
pdf_draw_section_name( pdf, opts )
|
282
|
+
|
283
|
+
xymin, = min_max_rooms
|
284
|
+
|
285
|
+
x = -(xymin[0]-1) * opts['ww']
|
286
|
+
y = (xymin[1]-1) * opts['hh']
|
287
|
+
pdf.translate_axis( x, y )
|
234
288
|
|
235
289
|
# For testing purposes only, draw grid of boxes
|
236
290
|
# pdf_draw_grid( pdf, opts, width, height )
|
237
|
-
|
238
291
|
@connections.each { |c| c.pdf_draw( pdf, opts ) }
|
239
292
|
@rooms.each_with_index { |r, idx| r.pdf_draw( pdf, opts, idx) }
|
293
|
+
|
294
|
+
# Reset axis
|
295
|
+
pdf.translate_axis(-x, -y)
|
296
|
+
pdf.translate_axis(-pack[0], -pack[1])
|
240
297
|
end
|
241
298
|
end
|
242
299
|
|
243
300
|
|
244
301
|
class FXMap
|
245
302
|
|
246
|
-
def
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
303
|
+
def pdf_draw_mapname( pdf, opts )
|
304
|
+
return if not @name or @name == ''
|
305
|
+
pdf.text( @name,
|
306
|
+
:font_size => 24,
|
307
|
+
:justification => :center
|
308
|
+
)
|
309
|
+
end
|
310
|
+
|
311
|
+
def pdf_draw_sections( pdf, opts )
|
312
|
+
old_section = @section
|
313
|
+
page = -1
|
314
|
+
@sections.each_with_index { |sect, idx|
|
315
|
+
if page != sect.page
|
316
|
+
page = sect.page
|
317
|
+
pdf.start_new_page if page > 1
|
318
|
+
pdf_draw_mapname( pdf, opts )
|
319
|
+
end
|
320
|
+
@section = idx
|
251
321
|
# For each page, we need to regenerate the pathmap so that complex
|
252
322
|
# paths will come out ok.
|
253
323
|
create_pathmap
|
254
324
|
# Now, we draw it
|
255
|
-
|
325
|
+
sect.pdf_draw(pdf, opts, @name)
|
256
326
|
}
|
257
327
|
|
258
328
|
# Restore original viewing page
|
259
|
-
@
|
329
|
+
@section = old_section
|
260
330
|
create_pathmap
|
261
331
|
end
|
262
332
|
|
@@ -265,6 +335,8 @@ class FXMap
|
|
265
335
|
paper = 'LETTER'
|
266
336
|
if printer
|
267
337
|
case printer.mediasize
|
338
|
+
when FXPrinter::MEDIA_LETTER
|
339
|
+
paper = 'LETTER'
|
268
340
|
when FXPrinter::MEDIA_LEGAL
|
269
341
|
paper = 'LEGAL'
|
270
342
|
when FXPrinter::MEDIA_A4
|
@@ -281,10 +353,14 @@ class FXMap
|
|
281
353
|
|
282
354
|
|
283
355
|
pdf_options = @options.dup
|
356
|
+
|
357
|
+
ww = PDF_ROOM_WIDTH + PDF_ROOM_WS
|
358
|
+
hh = PDF_ROOM_HEIGHT + PDF_ROOM_HS
|
359
|
+
|
284
360
|
pdf_options.merge!(
|
285
361
|
{
|
286
|
-
'ww' =>
|
287
|
-
'hh' =>
|
362
|
+
'ww' => ww,
|
363
|
+
'hh' => hh,
|
288
364
|
'w' => PDF_ROOM_WIDTH,
|
289
365
|
'h' => PDF_ROOM_HEIGHT,
|
290
366
|
'ws' => PDF_ROOM_WS,
|
@@ -293,18 +369,19 @@ class FXMap
|
|
293
369
|
'hs_2' => PDF_ROOM_HS / 2.0,
|
294
370
|
'margin' => PDF_MARGIN,
|
295
371
|
'margin_2' => PDF_MARGIN / 2.0,
|
296
|
-
'width' =>
|
297
|
-
'height' =>
|
372
|
+
'width' => (pdf.page_width / ww).to_i - 1,
|
373
|
+
'height' => (pdf.page_height / hh).to_i - 1,
|
298
374
|
}
|
299
375
|
)
|
300
376
|
|
301
377
|
|
302
378
|
begin
|
303
|
-
# See if it is possible to pack several map sections (
|
379
|
+
# See if it is possible to pack several map sections (sections) into
|
304
380
|
# a single print page.
|
305
|
-
|
306
|
-
|
307
|
-
|
381
|
+
num = pack_sections( pdf_options['width'] + 2,
|
382
|
+
pdf_options['height'] + 2 )
|
383
|
+
pdf_draw_sections(pdf, pdf_options)
|
384
|
+
status "Exporting PDF file '#{pdffile}'"
|
308
385
|
pdf.save_as(pdffile)
|
309
386
|
rescue => e
|
310
387
|
p e
|