ifmapper 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  #