ifmapper 0.7 → 0.8

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.
@@ -39,7 +39,7 @@ require 'IFMapper/FXMapColorBox'
39
39
  class FXMapperWindow < FXMainWindow
40
40
 
41
41
  PROGRAM_NAME = "Interactive Fiction Mapper"
42
- VERSION = 0.7
42
+ VERSION = 0.8
43
43
  AUTHOR = "Gonzalo Garramuno"
44
44
  TITLE = "#{PROGRAM_NAME} v#{VERSION} - Written by #{AUTHOR}"
45
45
 
@@ -65,6 +65,19 @@ class FXMapperWindow < FXMainWindow
65
65
  return map
66
66
  end
67
67
 
68
+
69
+ def start_automap_cb(sender, sel, ptr)
70
+ map = current_map
71
+ return if not map
72
+ map.start_automap
73
+ end
74
+
75
+ def stop_automap_cb(sender, sel, ptr)
76
+ map = current_map
77
+ return if not map
78
+ map.stop_automap
79
+ end
80
+
68
81
  def open_cb(sender, sel, ptr)
69
82
  file = FXMapFileDialog.new(self, "Load New Map").filename
70
83
  return if file == ''
@@ -110,6 +123,7 @@ class FXMapperWindow < FXMainWindow
110
123
  status "Could not load '#{file}'. Error: #{tmp}."
111
124
  if map.close_cb
112
125
  @maps.delete(map) if make_new_map
126
+ GC.start
113
127
  end
114
128
  sleep 2
115
129
  return
@@ -118,8 +132,8 @@ class FXMapperWindow < FXMainWindow
118
132
  map.copy(tmp)
119
133
  map.create_pathmap
120
134
  map.verify_integrity
121
- map.update_title
122
135
  map.window.create
136
+ map.update_title
123
137
  map.draw
124
138
  update_section
125
139
  status "Loaded '#{file}'."
@@ -168,6 +182,7 @@ class FXMapperWindow < FXMainWindow
168
182
  map = current_map
169
183
  if map.close_cb
170
184
  @maps.delete(map)
185
+ GC.start
171
186
  end
172
187
  }
173
188
 
@@ -243,32 +258,6 @@ class FXMapperWindow < FXMainWindow
243
258
  end
244
259
  end
245
260
 
246
- #
247
- # Export current map as a Postscript file
248
- #
249
- def ps_export_cb(sender, sel, msg)
250
- map = current_map
251
- return unless map
252
-
253
- require 'IFMapper/MapPrinting'
254
-
255
- d = FXMapFileDialog.new(self, "Save Postscript File",
256
- [
257
- "Postscript (*.ps)"
258
- ])
259
- if d.filename != ''
260
- printer = FXPrinter.new
261
- printer.firstpage = 1
262
- printer.lastpage = map.sections.size
263
- printer.mediawidth = 612.0
264
- printer.mediaheight = 792.0
265
- printer.leftmargin = printer.rightmargin =
266
- printer.topmargin = printer.bottommargin = 72.0
267
- printer.flags |= PRINT_DEST_FILE
268
- printer.name = d.filename
269
- map.print(printer)
270
- end
271
- end
272
261
 
273
262
  def pdf_export_cb(sender, sel, msg)
274
263
  map = current_map
@@ -605,6 +594,7 @@ class FXMapperWindow < FXMainWindow
605
594
  }
606
595
  end
607
596
 
597
+
608
598
  def about_cb(sender, id, event )
609
599
  FXAboutDialogBox.new(self, "About This Software...", <<"EOF").execute
610
600
  #{TITLE} - #{VERSION}
@@ -644,9 +634,6 @@ EOF
644
634
  cmd = FXMenuCommand.new(submenu, "&Export as PDF...\t\tExport map as Acrobat PDF document.", nil)
645
635
  cmd.connect(SEL_COMMAND, method(:pdf_export_cb))
646
636
 
647
- cmd = FXMenuCommand.new(submenu, "&Export as Postscript...\t\tExport map as Postscript document.", nil)
648
- cmd.connect(SEL_COMMAND, method(:ps_export_cb))
649
-
650
637
  cmd = FXMenuCommand.new(submenu, "&Export as IFM...\t\tExport map as an IFM map.", nil)
651
638
  cmd.connect(SEL_COMMAND, method(:ifm_export_cb))
652
639
  FXMenuCascade.new(filemenu, "Export", nil, submenu)
@@ -713,6 +700,16 @@ EOF
713
700
  cmd = FXMenuCommand.new(mapmenu, "Map Information\t\tChange Map Information.")
714
701
  cmd.connect(SEL_COMMAND) { map_properties }
715
702
 
703
+ # Automap submenu
704
+ #
705
+ submenu = FXMenuPane.new(self)
706
+ cmd = FXMenuCommand.new(submenu, "&Start...\tCtl-T\tStart creating map from transcript file.")
707
+ cmd.connect(SEL_COMMAND, method(:start_automap_cb))
708
+ cmd = FXMenuCommand.new(submenu, "S&top...\tCtl-P\tStop autocreating map.")
709
+ cmd.connect(SEL_COMMAND, method(:stop_automap_cb))
710
+ FXMenuCascade.new(mapmenu, "Automap", nil, submenu)
711
+
712
+ # Sections submenu
716
713
  submenu = FXMenuPane.new(self)
717
714
  cmd = FXMenuCommand.new(submenu, "Next Section\t\tGo to Next Map Section.")
718
715
  cmd.connect(SEL_COMMAND) {
@@ -751,7 +748,9 @@ EOF
751
748
  }
752
749
  FXMenuCascade.new(mapmenu, "Sections", nil, submenu)
753
750
 
754
-
751
+ #
752
+ # Zoom submenu
753
+ #
755
754
  submenu = FXMenuPane.new(self)
756
755
  [25, 50, 75, 100, 125].each { |v|
757
756
  cmd = FXMenuCommand.new(submenu, v.to_s + "%\t\tZoom page to #{v}%.")
@@ -56,6 +56,7 @@ class IFMReader
56
56
  @map.section = 0
57
57
 
58
58
  if @map.kind_of?(FXMap)
59
+ @map.options['Edit on Creation'] = false
59
60
  @map.window.hide
60
61
  end
61
62
 
@@ -413,6 +414,7 @@ class IFMReader
413
414
  # so we check we are in the right section.
414
415
  @map.sections.each_with_index { |p, idx|
415
416
  if p.rooms.include?(roomA)
417
+ @map.fit
416
418
  @map.section = idx
417
419
  break
418
420
  end
@@ -484,6 +486,7 @@ class IFMReader
484
486
  # current section... so we check for that here
485
487
  @map.sections.each_with_index { |p, idx|
486
488
  if p.rooms.include?(roomA)
489
+ @map.fit
487
490
  @map.section = idx
488
491
  break
489
492
  end
@@ -542,48 +545,6 @@ class IFMReader
542
545
  # a) Adjust map's width/height
543
546
  # b) Shift all rooms so that no rooms are in negative locations
544
547
  #
545
- def adjust_map
546
- # First, adjust map's width and height
547
- @map.width = @map.height = 1
548
- minXY = []
549
- maxXY = []
550
- @map.sections.each { |section|
551
- next if section.rooms.empty?
552
-
553
- sizes = section.min_max_rooms
554
- minXY.push sizes[0]
555
- maxXY.push sizes[1]
556
-
557
- w = maxXY[-1][0] - minXY[-1][0]
558
- h = maxXY[-1][1] - minXY[-1][1]
559
-
560
- # We store +3 to allow for complex connections if needed.
561
- @map.width = w + 3 if w >= @map.width - 2
562
- @map.height = h + 3 if h >= @map.height - 2
563
- }
564
-
565
-
566
- # Okay, minXY[]/maxXY[] contains all the minXY/maxXY positions of
567
- # each section. With that info and @map.weight/height, we can
568
- # start shifting all nodes in the section so that they will fit.
569
- idx = 0
570
- @map.sections.each { |p|
571
- next if p.rooms.size == 0
572
- x = y = 0
573
- x = 1 - minXY[idx][0] if minXY[idx][0] < 1
574
- y = 1 - minXY[idx][1] if minXY[idx][1] < 1
575
- x = @map.width - maxXY[idx][0] - 1 if maxXY[idx][0] >= @map.width - 1
576
- y = @map.height - maxXY[idx][1] - 1 if maxXY[idx][1] >= @map.height - 1
577
- idx += 1
578
- next if x == 0 and y == 0 # nothing to shift
579
- p.rooms.each { |r|
580
- r.x += x
581
- r.y += y
582
- }
583
- }
584
-
585
- @map.create_pathmap if @map.kind_of?(FXMap)
586
- end
587
548
 
588
549
  def initialize(file, map = Map.new('IFM Imported Map'))
589
550
  @tags = {}
@@ -595,13 +556,13 @@ class IFMReader
595
556
  parse(f)
596
557
  }
597
558
  # puts '--------------- second pass'
559
+ @map.fit
598
560
  @resolve_tags = true
599
561
  File.open(file) { |f|
600
562
  parse(f)
601
563
  }
602
564
  # puts '--------------- second pass done'
603
- @map.section = 0
604
- adjust_map
565
+ @map.section = 0
605
566
  if @map.kind_of?(FXMap)
606
567
  @map.filename = file.sub(/\.ifm$/i, '.map')
607
568
  @map.navigation = true
data/lib/IFMapper/Map.rb CHANGED
@@ -58,6 +58,52 @@ class Map
58
58
  }
59
59
  end
60
60
 
61
+ #
62
+ # Change map's width and height to make sure all rooms and connections
63
+ # will fit in map
64
+ #
65
+ def fit
66
+ # First, adjust map's width and height
67
+ @width = @height = 3
68
+ minXY = []
69
+ maxXY = []
70
+
71
+ @sections.each { |section|
72
+ next if section.rooms.empty?
73
+
74
+ sizes = section.min_max_rooms
75
+ minXY.push sizes[0]
76
+ maxXY.push sizes[1]
77
+
78
+ w = maxXY[-1][0] - minXY[-1][0]
79
+ h = maxXY[-1][1] - minXY[-1][1]
80
+
81
+ # We store +3 to allow for complex connections if needed.
82
+ @width = w + 3 if w >= @width - 2
83
+ @height = h + 3 if h >= @height - 2
84
+ }
85
+
86
+
87
+ # Okay, minXY[]/maxXY[] contains all the minXY/maxXY positions of
88
+ # each section. With that info and @weight/@height, we can
89
+ # start shifting all nodes in the section so that they will fit.
90
+ idx = 0
91
+ @sections.each { |p|
92
+ next if p.rooms.size == 0
93
+ x = y = 0
94
+ x = 1 - minXY[idx][0] if minXY[idx][0] < 1
95
+ y = 1 - minXY[idx][1] if minXY[idx][1] < 1
96
+ x = @width - maxXY[idx][0] - 2 if maxXY[idx][0] >= @width - 1
97
+ y = @height - maxXY[idx][1] - 2 if maxXY[idx][1] >= @height - 1
98
+ idx += 1
99
+ next if x == 0 and y == 0 # nothing to shift
100
+ p.rooms.each { |r|
101
+ r.x += x
102
+ r.y += y
103
+ }
104
+ }
105
+ end
106
+
61
107
  def initialize(name)
62
108
  @section = 0
63
109
  @name = name
@@ -81,6 +127,17 @@ class Map
81
127
  @date = b.date
82
128
  end
83
129
 
130
+ #
131
+ # Return true or false if map is free at location x,y
132
+ #
133
+ def free?(x, y)
134
+ return @sections[@section].free?(x,y)
135
+ end
136
+
137
+ def shift(x, y, dx, dy)
138
+ @sections[@section].shift(x, y, dx, dy)
139
+ end
140
+
84
141
  def delete_connection(c)
85
142
  @sections[@section].delete_connection(c)
86
143
  end
@@ -20,7 +20,7 @@ PDF_ROOM_WIDTH = W * PDF_ZOOM
20
20
  PDF_ROOM_HEIGHT = H * PDF_ZOOM
21
21
  PDF_ROOM_WS = WS * PDF_ZOOM
22
22
  PDF_ROOM_HS = HS * PDF_ZOOM
23
- PDF_MARGIN = 20
23
+ PDF_MARGIN = 20.0
24
24
 
25
25
  #
26
26
  # Open all the map class and add all pdf methods there
@@ -256,46 +256,86 @@ class FXSection
256
256
  end
257
257
 
258
258
 
259
- def pdf_draw_section_name( pdf, opts )
259
+ def pdf_draw_section_name( pdf, opts, px, py )
260
260
  return if not @name or @name == ''
261
- y = (opts['height']) * opts['hh'] + 16
262
261
  xymin, xymax = min_max_rooms
263
- x = (xymax[0] + 2) * opts['ww'] / 2
264
- pdf.add_text( x, y, @name, 16 )
262
+ text = @name
263
+ text += " (#{px}, #{py})" if px > 0 or py > 0
264
+ y = (opts['height']) * opts['hh'] + 16
265
+ w = xymax[0]
266
+ w = opts['width'] if w > opts['width']
267
+ x = (w + 2) * opts['ww'] / 2 - text.size / 2 * 16
268
+ x = 0 if x < 0
269
+ pdf.add_text( x, y, text, 16 )
265
270
  end
266
271
 
267
272
 
268
273
  def pdf_draw(pdf, opts, mapname )
269
- if rotate
270
- pdf.rotate_axis(90.0)
271
- pdf.translate_axis( 0, -pdf.page_height )
272
- end
273
274
 
274
- # Move section to its position in page
275
- pack = [@xoff * opts['ww'], @yoff * -opts['hh']]
276
- pdf.translate_axis( pack[0], pack[1] )
277
275
 
278
- # Use times-roman as font
279
- pdf.select_font 'Times-Roman'
280
- pdf.stroke_color(Color::RGB::Black)
281
- pdf.fill_color(Color::RGB::Black)
276
+ w, h = rooms_width_height
277
+ x, y = [0, 0]
282
278
 
283
- pdf_draw_section_name( pdf, opts )
279
+ loop do
284
280
 
285
- xymin, = min_max_rooms
281
+ if rotate
282
+ pdf.rotate_axis(90.0)
283
+ pdf.translate_axis( 0, -pdf.page_height )
284
+ end
286
285
 
287
- x = -(xymin[0]-1) * opts['ww']
288
- y = (xymin[1]-1) * opts['hh']
289
- pdf.translate_axis( x, y )
286
+ # Move section to its position in page
287
+ tx1, ty1 = [@xoff * opts['ww'], @yoff * -opts['hh']]
288
+ pdf.translate_axis( tx1, ty1 )
289
+
290
+ # Use times-roman as font
291
+ pdf.select_font 'Times-Roman'
292
+ pdf.stroke_color(Color::RGB::Black)
293
+ pdf.fill_color(Color::RGB::Black)
294
+
295
+ pdf_draw_section_name( pdf, opts, x, y )
296
+
297
+ xymin, = min_max_rooms
298
+
299
+ # Move rooms, so that we don't print empty areas
300
+ tx2 = -(xymin[0]) * opts['ww'] - x * opts['ww']
301
+ ty2 = (xymin[1]) * opts['hh'] - 60 + (y - (y > 0? 1 : 0)) * opts['hh']
302
+ pdf.translate_axis( tx2, ty2 )
303
+
304
+
305
+ # For testing purposes only, draw grid of boxes
306
+ # pdf_draw_grid( pdf, opts, width, height )
307
+ @connections.each { |c|
308
+ a = c.roomA
309
+ b = c.roomB
310
+ next if a.y < y and b and b.y < y
311
+ c.pdf_draw( pdf, opts )
312
+ }
313
+ @rooms.each_with_index { |r, idx|
314
+ next if r.y < y
315
+ r.pdf_draw( pdf, opts, idx)
316
+ }
317
+
318
+ # Reset axis
319
+ pdf.translate_axis(-tx2, -ty2)
320
+ pdf.translate_axis(-tx1, -ty1)
321
+
322
+ xi = opts['width']
323
+ yi = opts['height']
324
+ if rotate
325
+ xi = (pdf.page_height / opts['ww']).to_i - 1
326
+ yi = (pdf.page_width / opts['hh']).to_i - 1
327
+ end
290
328
 
291
- # For testing purposes only, draw grid of boxes
292
- # pdf_draw_grid( pdf, opts, width, height )
293
- @connections.each { |c| c.pdf_draw( pdf, opts ) }
294
- @rooms.each_with_index { |r, idx| r.pdf_draw( pdf, opts, idx) }
329
+ x += xi
330
+ if x >= w
331
+ x = 0
332
+ y += yi
333
+ break if y >= h
334
+ end
295
335
 
296
- # Reset axis
297
- pdf.translate_axis(-x, -y)
298
- pdf.translate_axis(-pack[0], -pack[1])
336
+ # We could not fit all rooms in page. Start new page
337
+ pdf.start_new_page
338
+ end
299
339
  end
300
340
  end
301
341
 
@@ -332,6 +372,7 @@ class FXMap
332
372
  create_pathmap
333
373
  end
334
374
 
375
+
335
376
  def pdf_export(pdffile = Dir::tmpdir + "/ifmap.pdf", printer = nil)
336
377
 
337
378
  paper = 'LETTER'
@@ -353,6 +394,7 @@ class FXMap
353
394
  # Open a new PDF writer with paper selected
354
395
  pdf = PDF::Writer.new :paper => paper
355
396
 
397
+ pdf.margins_pt 0
356
398
 
357
399
  pdf_options = @options.dup
358
400
 
data/lib/IFMapper/Room.rb CHANGED
@@ -84,6 +84,9 @@ class Room
84
84
  if self == b
85
85
  raise "next_to? comparing same room #{self}"
86
86
  end
87
+ if b.x == @x and b.y == @y
88
+ raise "#{self} and #{b} in same location."
89
+ end
87
90
  dx = (b.x - @x)
88
91
  dy = (b.y - @y)
89
92
  return nil if dx.abs > 1 or dy.abs > 1
@@ -48,6 +48,36 @@ class Section
48
48
  return [ minXY, maxXY ]
49
49
  end
50
50
 
51
+ #
52
+ # Return true or false if map is free at location x,y
53
+ #
54
+ def free?(x,y)
55
+ @rooms.each { |r|
56
+ return false if r.x == x and r.y == y
57
+ }
58
+ return true
59
+ end
60
+
61
+ #
62
+ # Shift rooms in dx and dy direction from x,y position on.
63
+ #
64
+ def shift(x, y, dx, dy)
65
+ @rooms.each { |r|
66
+ ox = oy = 0
67
+ if (dx < 0 and r.x <= x) or
68
+ (dx > 0 and r.x >= x)
69
+ ox = dx
70
+ end
71
+ if (dy < 0 and r.y <= y) or
72
+ (dy > 0 and r.y >= y)
73
+ oy = dy
74
+ end
75
+ r.x += ox
76
+ r.y += oy
77
+ }
78
+ end
79
+
80
+
51
81
  #
52
82
  # Delete a connection from section
53
83
  #