ifmapper 1.1.6 → 1.2.0

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.
@@ -0,0 +1,1091 @@
1
+ require 'IFMapper/MapPrinting'
2
+ require 'rexml/document'
3
+ require 'rexml/cdata'
4
+
5
+ DEBUG_OUTPUT = false
6
+ SVG_ZOOM = 1
7
+ SVG_ROOM_WIDTH = W * SVG_ZOOM
8
+ SVG_ROOM_HEIGHT = H * SVG_ZOOM
9
+ SVG_ROOM_WS = WS * SVG_ZOOM
10
+ SVG_ROOM_HS = HS * SVG_ZOOM
11
+
12
+ class SVGUtilities
13
+
14
+ def self.new_svg_doc ( width, height)
15
+ svg = REXML::Document.new
16
+ svg << REXML::XMLDecl.new
17
+
18
+ svg.add_element "svg", {
19
+ "width" => width,
20
+ "height" => height,
21
+ "version" => "1.1",
22
+ "xmlns" => "http://www.w3.org/2000/svg",
23
+ "xmlns:xlink" => "http://www.w3.org/1999/xlink"}
24
+ return svg
25
+
26
+ end
27
+
28
+ def self.add_text( svg, x, y, font_size, text, opts, fill_colour )
29
+ if DEBUG_OUTPUT; puts "svg::FXMap::svg_draw_name_text:" end
30
+ y = y + font_size
31
+
32
+ t = svg.root.add_element "text", {
33
+ "x" => x,
34
+ "y" => y,
35
+ "style" => "font-size:" + font_size.to_s() + "pt;fill:#" + fill_colour.to_s()}
36
+
37
+ t.text = text
38
+
39
+ return [svg, x, y]
40
+ end
41
+
42
+ def self.num_text_lines ( text, maxlinelength )
43
+ if text and not text == ''
44
+ return SVGUtilities::break_text_lines(text.chomp().strip(), maxlinelength).length
45
+ else
46
+ return 0
47
+ end
48
+ end
49
+
50
+ def self.break_text_lines( text, maxlinelength )
51
+ if DEBUG_OUTPUT; printf("SVGUtilities::break_text_lines:text=%s\r\n", text) end
52
+
53
+ out = Array.new
54
+
55
+ max_chars = maxlinelength
56
+ words = text.split(" ")
57
+ cur_str = ""
58
+
59
+ words.each { |word|
60
+ new_len = cur_str.length + word.length
61
+ if DEBUG_OUTPUT; printf("SVGUtilities::break_text_lines:current word: %s :: new_len: %d\r\n", word, new_len) end
62
+ new_len = cur_str.length + word.length
63
+ if new_len <= max_chars
64
+ cur_str = cur_str + word + " "
65
+ else
66
+ out << cur_str
67
+ cur_str = "" + word + " "
68
+ end
69
+ }
70
+ out << cur_str
71
+
72
+ return out
73
+ end
74
+
75
+ def self.get_compass_svg_group()
76
+
77
+ file = File.new( "icons/compass.svg" )
78
+ doc = REXML::Document.new file
79
+
80
+ newgroup = REXML::Element.new "g"
81
+ newgroup.attributes["id"] = "compass"
82
+
83
+ doc.root.elements.each {|elem|
84
+ newgroup.add_element(elem)
85
+ }
86
+
87
+ return newgroup
88
+
89
+ end
90
+
91
+ def self.add_compass(svg)
92
+ compassgroup = SVGUtilities::get_compass_svg_group()
93
+
94
+ defs = svg.root.add_element "defs"
95
+
96
+ defs.add_element compassgroup
97
+
98
+ return svg
99
+ end
100
+
101
+
102
+ def self.svg_add_script( svg, opts )
103
+ svg.root.attributes["onload"] = "Init(evt)"
104
+
105
+ script = svg.root.add_element "script", {
106
+ "type" => "text/ecmascript" }
107
+
108
+ script.add( REXML::CData.new("
109
+ var SVGDocument = null;
110
+ var SVGRoot = null;
111
+
112
+ function Init(evt)
113
+ {
114
+ SVGDocument = evt.target.ownerDocument;
115
+ SVGRoot = SVGDocument.documentElement;
116
+ }
117
+
118
+ function ToggleOpacity(evt, targetId)
119
+ {
120
+ var newTarget = evt.target;
121
+ if (targetId)
122
+ {
123
+ newTarget = SVGDocument.getElementById(targetId);
124
+ }
125
+ var newValue = newTarget.getAttributeNS(null, 'opacity')
126
+
127
+ if ('0' != newValue)
128
+ {
129
+ newValue = '0';
130
+ }
131
+ else
132
+ {
133
+ newValue = '1';
134
+ }
135
+ newTarget.setAttributeNS(null, 'opacity', newValue);
136
+
137
+ if (targetId)
138
+ {
139
+ SVGDocument.getElementById(targetId + 'Exception').setAttributeNS(null, 'opacity', '1');
140
+ }
141
+ }
142
+ "))
143
+
144
+ return svg;
145
+ end
146
+
147
+ def self.add_titles( svg, opts, x, y, font_size, mapname, mapcreator )
148
+ if DEBUG_OUTPUT; puts "svg::SVGUtilities::add_titles" end
149
+
150
+ if opts['print_title'] == true
151
+ if not mapname or mapname == ''
152
+ if DEBUG_OUTPUT; puts "svg::SVGUtilities::add_titles:name is empty, not printing" end
153
+ else
154
+ svg, x, y = SVGUtilities::add_text(svg, x, y, font_size*1.5, mapname, opts, '000000')
155
+ y = y + (opts['name_line_spacing'] * 4)
156
+ end
157
+ end
158
+
159
+ if opts['print_creator'] == true
160
+ if not mapcreator or mapcreator == ''
161
+ if DEBUG_OUTPUT; puts "svg::SVGUtilities::add_titles:creator is empty, not printing" end
162
+ else
163
+ svg, x, y = SVGUtilities::add_text(svg, x, y, font_size*0.85, 'Map ' + opts['creator_prefix'] + mapcreator, opts, '000000')
164
+ y = y + (opts['name_line_spacing'] * 4)
165
+ end
166
+ end
167
+
168
+ return [svg, x, y]
169
+ end
170
+
171
+ end
172
+ #
173
+ # Open all the map class and add all SVG methods there
174
+ # Gotta love Ruby's flexibility to just inject in new methods.
175
+ #
176
+ class FXConnection
177
+ def _cvt_pt(p, opts)
178
+ if DEBUG_OUTPUT; puts "svg::FXConnection::_cvt_pt" end
179
+ x = (p[0] - WW / 2.0) / WW.to_f
180
+ y = (p[1] - HH / 2.0) / HH.to_f
181
+ x = x * opts['ww'] + opts['margin'] + opts['w'] / 2.0
182
+ y = y * opts['hh'] + opts['margin'] + opts['hs'] - 2
183
+ return [x, y]
184
+ end
185
+
186
+ def svg_draw_arrow(svg, opts, x1, y1, x2, y2)
187
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_arrow:#{x1},#{y1},#{x2},#{y2}" end
188
+ return if @dir == BOTH
189
+
190
+ pt1, d = _arrow_info( x1, y1, x2, y2, 0.5 )
191
+
192
+ if DEBUG_OUTPUT; printf("svg::FXConnection::svg_draw_arrow: pt1[0]=%0.01f,pt1[1]=%0.01f,d[0]=%0.01f,d[1]=%0.01f\r\n", pt1[0], pt1[1], d[0], d[1]) end
193
+
194
+ mx = pt1[0];
195
+ my = pt1[1];
196
+
197
+ if DEBUG_OUTPUT; printf("svg::FXConnection::svg_draw_arrow: mx=%0.01f,my=%0.01f\r\n",mx,my) end
198
+
199
+ lx1 = pt1[0] + d[0]
200
+ ly1 = pt1[1] + d[1]
201
+
202
+ if DEBUG_OUTPUT; printf("svg::FXConnection::svg_draw_arrow: lx1=%0.01f,ly1=%0.01f\r\n",lx1,ly1) end
203
+
204
+ lx2 = pt1[0] + d[1]
205
+ ly2 = pt1[1] - d[0]
206
+
207
+ if DEBUG_OUTPUT; printf("svg::FXConnection::svg_draw_arrow: lx2=%0.01f,ly2=%0.01f\r\n",lx2,ly2) end
208
+
209
+ points = sprintf("%0.01f", mx) + "," + sprintf("%0.01f", my) + " "
210
+ points << sprintf("%0.01f", lx1) + "," + sprintf("%0.01f", ly1) + " "
211
+ points << sprintf("%0.01f", lx2) + "," + sprintf("%0.01f", ly2) + " "
212
+
213
+ svg.root.add_element "polygon", {
214
+ "style" => sprintf("stroke:%s;stroke-width:%s;fill:%s",opts['arrow_line_colour'],opts['arrow_line_width'],opts['arrow_fill_colour']),
215
+ "points" => points }
216
+
217
+ end
218
+
219
+ def svg_draw_complex_as_bspline( svg, opts )
220
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_complex_as_bspline" end
221
+ p = []
222
+ p << _cvt_pt(@pts[0], opts)
223
+ p << p[0]
224
+ p << p[0]
225
+ @pts.each { |pt|
226
+ p << _cvt_pt(pt, opts)
227
+ }
228
+ p << p[-1]
229
+ p << p[-1]
230
+ p << p[-1]
231
+ return FXSpline::bspline(p)
232
+ end
233
+
234
+ def svg_draw_complex_as_lines( svg, opts )
235
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_complex_as_lines" end
236
+ p = []
237
+ @pts.each { |pt|
238
+ p << _cvt_pt(pt, opts)
239
+ }
240
+ return p
241
+ end
242
+
243
+ def svg_draw_door( svg, x1, y1, x2, y2 , opts)
244
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_arrow:#{x1},#{y1},#{x2},#{y2}" end
245
+ v = [ (x2-x1), (y2-y1) ]
246
+ t = 10 / Math.sqrt(v[0]*v[0]+v[1]*v[1])
247
+ v = [ v[0]*t, v[1]*t ]
248
+ m = [ (x2+x1)/2, (y2+y1)/2 ]
249
+ x1, y1 = [m[0] + v[1], m[1] - v[0]]
250
+ x2, y2 = [m[0] - v[1], m[1] + v[0]]
251
+
252
+ v = [ v[0] / 3, v[1] / 3]
253
+
254
+ poly = svg.root.add_element "polygon", {
255
+ "style" => sprintf("stroke:%s;stroke-width:%s", opts['door_line_colour'],opts['door_line_width']),
256
+ "points" => sprintf("%0.01f,%0.01f %0.01f,%0.01f %0.01f,%0.01f %0.01f,%0.01f",
257
+ x1-v[0], y1-v[1], x1+v[0], y1+v[1], x2+v[0], y2+v[1], x2-v[0], y2-v[1]) }
258
+
259
+ if @type == LOCKED_DOOR
260
+ poly.attributes["style"] << sprintf(";fill:%s", opts['door_line_colour'])
261
+ else
262
+ poly.attributes["style"] << ";fill:none"
263
+ end
264
+ end
265
+
266
+ def svg_draw_complex( svg, opts, sx ,sy )
267
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_complex" end
268
+ if opts['Paths as Curves']
269
+ if @room[0] == @room[1]
270
+ dirA, dirB = dirs
271
+ if dirA == dirB
272
+ p = svg_draw_complex_as_lines( svg, opts )
273
+ else
274
+ p = svg_draw_complex_as_bspline( svg, opts )
275
+ end
276
+ else
277
+ p = svg_draw_complex_as_bspline( svg, opts )
278
+ end
279
+ else
280
+ p = svg_draw_complex_as_lines( svg, opts )
281
+ end
282
+
283
+ p.each { |pt|
284
+ pt[0] = pt[0] + sx
285
+ pt[1] = pt[1] + sy }
286
+
287
+ d = "M" + sprintf("%0.01f", p[0][0]) + "," + sprintf("%0.01f", p[0][1]) + " "
288
+
289
+ p.each { |pt|
290
+ d = d + "L" + sprintf("%0.01f", pt[0]) + "," + sprintf("%0.01f", pt[1]) + " " }
291
+
292
+ path = svg.root.add_element "path", {
293
+ "style" => sprintf("stroke:%s;stroke-width:%s;fill:none",opts['conn_line_colour'],opts['conn_line_width']),
294
+ "d" => d }
295
+
296
+ if @type == SPECIAL
297
+ path.attributes["style"] << ";stroke-dasharray:9,5"
298
+ end
299
+
300
+ x1, y1 = [p[0][0], p[0][1]]
301
+ x2, y2 = [p[-1][0], p[-1][1]]
302
+
303
+ svg_draw_arrow(svg, opts, x1, y1, x2, y2)
304
+
305
+ if @type == LOCKED_DOOR or @type == CLOSED_DOOR
306
+ t = p.size / 2
307
+ x1, y1 = [ p[t][0], p[t][1] ]
308
+ x2, y2 = [ p[t-2][0], p[t-2][1] ]
309
+
310
+ svg_draw_door(svg, x1, y1, x2, y2, opts)
311
+ end
312
+ end
313
+
314
+ def svg_draw_simple(svg, opts, sx, sy)
315
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_simple" end
316
+ return if not @room[1]
317
+
318
+ dir = @room[0].exits.index(self)
319
+ x1, y1 = @room[0].svg_corner(opts, self, dir)
320
+ x2, y2 = @room[1].svg_corner(opts, self)
321
+
322
+ x1 = x1 + sx;
323
+ x2 = x2 + sx;
324
+ y1 = y1 + sy;
325
+ y2 = y2 + sy;
326
+
327
+ line = svg.root.add_element "line", {
328
+ "x1" => x1,
329
+ "y1" => y1,
330
+ "x2" => x2,
331
+ "y2" => y2,
332
+ "style" => sprintf("stroke:%s;stroke-width:%s", opts['conn_line_colour'],opts['conn_line_width']) }
333
+
334
+ if @type == SPECIAL
335
+ line.attributes["style"] << ";stroke-dasharray:9,5"
336
+ end
337
+
338
+ svg_draw_arrow(svg, opts, x1, y1, x2, y2)
339
+ if @type == LOCKED_DOOR or @type == CLOSED_DOOR
340
+ svg_draw_door(svg, x1, y1, x2, y2, opts)
341
+ end
342
+ end
343
+
344
+ #
345
+ # Draw the connection text next to the arrow ('I', 'O', etc)
346
+ #
347
+ def svg_draw_text(svg, x, y, dir, text, arrow)
348
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_text:#{x},#{y},#{text}" end
349
+ if dir == 7 or dir < 6 and dir != 1
350
+ if arrow and (dir == 0 or dir == 4)
351
+ x += 5
352
+ end
353
+ x += 2.5
354
+ elsif dir == 6 or dir == 1
355
+ x -= 7.5
356
+ end
357
+
358
+ if dir > 5 or dir < 4
359
+ if arrow and (dir == 6 or dir == 2)
360
+ y -= 5
361
+ end
362
+ y -= 2.5
363
+ elsif dir == 4 or dir == 5
364
+ y += 7.5
365
+ end
366
+
367
+ font_size = 6
368
+
369
+ t = svg.root.add_element "text", {
370
+ "x" => x,
371
+ "y" => y,
372
+ "style" => "font-size:" + font_size.to_s() + "pt"}
373
+
374
+ t.text = text;
375
+ end
376
+
377
+ def svg_draw_exit_text(pdf, opts, sx, sy)
378
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw_exit_text" end
379
+ if @exitText[0] != 0
380
+ dir = @room[0].exits.index(self)
381
+ x, y = @room[0].svg_corner(opts, self, dir)
382
+ x = x + sx
383
+ y = y + sy
384
+ svg_draw_text( pdf, x, y, dir,
385
+ EXIT_TEXT[@exitText[0]], @dir == BtoA)
386
+ end
387
+
388
+ if @exitText[1] != 0
389
+ dir = @room[1].exits.rindex(self)
390
+ x, y = @room[1].svg_corner(opts, self, dir)
391
+ x = x + sx
392
+ y = y + sy
393
+ svg_draw_text( pdf, x, y, dir,
394
+ EXIT_TEXT[@exitText[1]], @dir == AtoB)
395
+ end
396
+ end
397
+
398
+ def svg_draw(svg, opts, x, y)
399
+ if DEBUG_OUTPUT; printf("svg::FXConnection::svg_draw:draw_connections=%s\r\n", opts['draw_connections'].to_s()) end
400
+ if opts['draw_connections'] == true
401
+ if DEBUG_OUTPUT; puts "svg::FXConnection::svg_draw" end
402
+
403
+ svg_draw_exit_text(svg, opts, x, y)
404
+
405
+ if @pts.size > 0
406
+ svg_draw_complex(svg, opts, x, y)
407
+ else
408
+ svg_draw_simple(svg, opts, x, y)
409
+ end
410
+ end
411
+ end
412
+ end
413
+
414
+
415
+ class FXRoom
416
+ def svg_corner( opts, c, idx = nil )
417
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_corner" end
418
+ x, y = _corner(c, idx)
419
+
420
+ ww = opts['ww']
421
+ hh = opts['hh']
422
+ w = opts['w']
423
+ h = opts['h']
424
+
425
+ x = @x * ww + x * w + opts['margin']
426
+ y = @y * hh + y * h + opts['margin']
427
+
428
+ return [x, y]
429
+ end
430
+
431
+ def svg_draw_box( svg, opts, idx, sx, sy )
432
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_draw_box" end
433
+ x = sx + (@x * opts['ww']) + opts['margin']
434
+ y = sy + (@y * opts['hh']) + opts['margin']
435
+
436
+ if DEBUG_OUTPUT; printf("svg::FXRoom::svg_draw_box[x=%s,y=%s,w=%s,h=%s]\r\n", x,y,opts['w'],opts['h']) end
437
+
438
+ roomrect = svg.root.add_element "rect", {
439
+ "x" => x,
440
+ "y" => y,
441
+ "width" => opts['w'],
442
+ "height" => opts['h'],
443
+ "style" => sprintf("stroke:%s;stroke-width:%s", opts['room_line_colour'],opts['room_line_width'])}
444
+
445
+ if @darkness
446
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_draw_box:DARKNESS" end
447
+ roomrect.attributes["style"] << ";fill:gray"
448
+ else
449
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_draw_box:not darkness" end
450
+ roomrect.attributes["style"] << ";fill:none"
451
+ end
452
+
453
+ if opts['print_room_nums'] == true
454
+
455
+ # Add the room number to the bottom right hand corner!
456
+
457
+ pad = 2
458
+ font_size = 8
459
+ numbox_width = (pad*2) + (3*font_size)
460
+ numbox_height = (pad*2)+font_size
461
+ numbox_x = (x + opts['w']) - numbox_width
462
+ numbox_y = (y + opts['h']) - numbox_height
463
+
464
+ numbox_rect = svg.root.add_element "rect", {
465
+ "x" => numbox_x,
466
+ "y" => numbox_y,
467
+ "width" => numbox_width,
468
+ "height" => numbox_height,
469
+ "style" => sprintf("stroke:%s;stroke-width:%s;fill:%s", opts['num_line_colour'],opts['num_line_width'],opts['num_fill_colour']) }
470
+
471
+ numtext_x = numbox_x + pad
472
+ numtext_y = numbox_y + font_size + pad
473
+
474
+ if idx < 100
475
+ numtext_x += font_size
476
+ end
477
+
478
+ if idx < 10
479
+ numtext_x += font_size
480
+ end
481
+
482
+ numtext = svg.root.add_element "text", {
483
+ "x" => numtext_x,
484
+ "y" => numtext_y,
485
+ "style" => "font-size:" + font_size.to_s() + "pt" }
486
+
487
+ numtext.text = idx.to_s()
488
+
489
+ end
490
+ end
491
+
492
+ def svg_draw_text( svg, opts, x, y, text, font_size )
493
+ if DEBUG_OUTPUT; printf("svg::FXRoom::svg_draw_text:text=%s\r\n", text) end
494
+
495
+ t = svg.root.add_element "text", {
496
+ "x" => x,
497
+ "y" => y,
498
+ "style" => "font-size:" + font_size.to_s() + "pt"}
499
+
500
+ max_chars = opts['text_max_chars']
501
+ words = text.split(" ")
502
+ dy = 0
503
+ ypos = 0
504
+ lasty = opts['h'] - opts['text_margin']
505
+ cur_str = ""
506
+
507
+ words.each { |word|
508
+ new_len = cur_str.length + word.length
509
+ if DEBUG_OUTPUT; printf("current word: %s :: new_len: %d\r\n", word, new_len) end
510
+ new_len = cur_str.length + word.length
511
+ if new_len <= max_chars
512
+ cur_str = cur_str + word + " "
513
+ else
514
+ ypos = ypos + font_size + opts['text_line_spacing']
515
+ if ypos >= lasty
516
+ break
517
+ end
518
+ tspan = t.add_element "tspan", {
519
+ "x" => x,
520
+ "dy" => dy }
521
+ tspan.text = cur_str
522
+ if dy == 0
523
+ dy = font_size + opts['text_line_spacing']
524
+ end
525
+ cur_str = "" + word + " "
526
+ end
527
+ }
528
+
529
+ if ypos < lasty
530
+ tspan = t.add_element "tspan", {
531
+ "x" => x,
532
+ "dy" => dy }
533
+ tspan.text = cur_str
534
+ end
535
+
536
+ return [x, y+ypos]
537
+ end
538
+
539
+ def svg_draw_objects(svg, opts, x, y)
540
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_draw_objects" end
541
+ font_size = 6
542
+ objs = @objects.split("\n")
543
+ objs = objs.join(', ')
544
+ return svg_draw_text( svg, opts, x, y, objs, font_size )
545
+ end
546
+
547
+ def svg_draw_name(svg, opts, sx, sy)
548
+ if DEBUG_OUTPUT; printf("svg::FXRoom::svg_draw_name:name=%s\r\n", @name) end
549
+
550
+ x = sx + (@x * opts['ww']) + opts['margin'] + opts['text_margin']
551
+ y = sy + (@y * opts['hh']) + opts['margin'] + opts['text_margin']
552
+ font_size = 8
553
+ y = y + font_size
554
+
555
+ return svg_draw_text( svg, opts, x, y, @name, font_size )
556
+ end
557
+
558
+ def svg_draw_interactive( svg, opts, idx, sx, sy, section_idx )
559
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_draw_interactive" end
560
+
561
+ if (@objects and not @objects == '') or (@tasks and not @tasks == '') or (@comment and not @comment == '')
562
+
563
+ x = sx + (@x * opts['ww']) + opts['margin'] + opts['w']
564
+ y = sy + (@y * opts['hh']) + opts['margin']
565
+
566
+ x1 = x - opts['corner_size']
567
+ y1 = y
568
+
569
+ x2 = x
570
+ y2 = y + opts['corner_size']
571
+
572
+ poly = svg.root.add_element "polygon", {
573
+ "style" => sprintf("stroke:%s;stroke-width:%s;fill:%s", opts['corner_line_colour'],opts['corner_line_width'],opts['corner_fill_colour']),
574
+ "points" => sprintf("%0.01f,%0.01f %0.01f,%0.01f %0.01f,%0.01f", x, y, x1, y1, x2, y2),
575
+ "onclick" => "ToggleOpacity(evt, \"section"+section_idx.to_s()+"room"+idx.to_s()+"\")" }
576
+
577
+ objs_font_size = opts['objects_font_size']
578
+ num_chars_per_line = opts['objects_width'] / (objs_font_size)
579
+
580
+ numObjsLines = 0
581
+ numTaskLines = 0
582
+ numCommLines = 0
583
+
584
+ if(@objects)
585
+ numObjsLines = SVGUtilities::num_text_lines(@objects, num_chars_per_line);
586
+ end
587
+
588
+ if(@tasks)
589
+ numTaskLines = SVGUtilities::num_text_lines(@tasks, num_chars_per_line);
590
+ end
591
+
592
+ if(@comment)
593
+ numCommLines = SVGUtilities::num_text_lines(@comment, num_chars_per_line);
594
+ end
595
+
596
+ if(numObjsLines > 0)
597
+ numObjsLines = numObjsLines + 2
598
+ end
599
+
600
+ if(numTaskLines > 0)
601
+ numTaskLines = numTaskLines + 2
602
+ end
603
+
604
+ if(numCommLines > 0)
605
+ numCommLines = numCommLines + 2
606
+ end
607
+
608
+ lines = numObjsLines + numTaskLines + numCommLines
609
+
610
+ objs_height = (lines+1) * (objs_font_size + opts['text_line_spacing'])
611
+
612
+ g = svg.root.add_element "g", {
613
+ "id" => "section"+section_idx.to_s()+"room"+idx.to_s(),
614
+ "opacity" => "0" }
615
+
616
+ rect_x = x - opts['objects_width'] - opts['corner_size']
617
+ rect_y = y + opts['corner_size']
618
+
619
+ g.add_element "rect", {
620
+ "x" => rect_x,
621
+ "y" => rect_y,
622
+ "width" => opts['objects_width'],
623
+ "height" => objs_height,
624
+ "z-index" => "1",
625
+ "style" => sprintf("stroke:%s;stroke-width:%s;fill:%s", opts['objs_line_colour'],opts['objs_line_width'],opts['objs_fill_colour']) }
626
+
627
+ text_x = rect_x + opts['text_margin']
628
+ text_y = rect_y + opts['text_margin'] + objs_font_size
629
+
630
+ if @objects and not @objects == ''
631
+ t1 = g.add_element "text", {
632
+ "x" => text_x,
633
+ "y" => text_y,
634
+ "style" => "font-size:" + objs_font_size.to_s() + "pt;font-weight:bold" }
635
+ t1.text = "Objects:"
636
+
637
+ text_y = text_y + objs_font_size + opts['text_line_spacing']
638
+ objs_lines = SVGUtilities::break_text_lines(@objects, num_chars_per_line)
639
+ text_x, text_y = svg_draw_interactive_objects( g, opts, text_x, text_y, objs_font_size, objs_lines)
640
+ text_y = text_y + objs_font_size + opts['text_line_spacing']
641
+ end
642
+
643
+ if @tasks and not @tasks == ''
644
+ t2 = g.add_element "text", {
645
+ "x" => text_x,
646
+ "y" => text_y,
647
+ "style" => "font-size:" + objs_font_size.to_s() + "pt;font-weight:bold" }
648
+ t2.text = "Tasks:"
649
+
650
+ text_y = text_y + objs_font_size + opts['text_line_spacing']
651
+ tasks_lines = SVGUtilities::break_text_lines(@tasks, num_chars_per_line)
652
+ text_x, text_y = svg_draw_interactive_objects( g, opts, text_x, text_y, objs_font_size, tasks_lines)
653
+ text_y = text_y + objs_font_size + opts['text_line_spacing']
654
+ end
655
+
656
+ if @comment and not @comment == ''
657
+ t2 = g.add_element "text", {
658
+ "x" => text_x,
659
+ "y" => text_y,
660
+ "style" => "font-size:" + objs_font_size.to_s() + "pt;font-weight:bold" }
661
+ t2.text = "Comments:"
662
+
663
+ text_y = text_y + objs_font_size + opts['text_line_spacing']
664
+ comments_lines = SVGUtilities::break_text_lines(@comment, num_chars_per_line)
665
+ text_x, text_y = svg_draw_interactive_objects( g, opts, text_x, text_y, objs_font_size, comments_lines)
666
+ text_y = text_y + objs_font_size + opts['text_line_spacing']
667
+ end
668
+ end
669
+ end
670
+
671
+
672
+ def svg_draw_interactive_objects( group, opts, x, y, font_size, lines)
673
+ if DEBUG_OUTPUT; printf("svg::FXRoom::svg_draw_interactive_objects\r\n") end
674
+
675
+ t = group.add_element "text", {
676
+ "x" => x,
677
+ "y" => y,
678
+ "style" => "font-size:" + font_size.to_s() + "pt"}
679
+
680
+ dy = 0
681
+ ypos = 0
682
+
683
+ lines.each { |obj|
684
+
685
+ ypos = ypos + font_size + opts['text_line_spacing']
686
+ tspan = t.add_element "tspan", {
687
+ "x" => x,
688
+ "dy" => dy }
689
+ tspan.text = obj
690
+ if dy == 0
691
+ dy = font_size + opts['text_line_spacing']
692
+ end
693
+ }
694
+
695
+ return [x, y+ypos]
696
+ end
697
+
698
+ def svg_draw( svg, opts, idx, x, y )
699
+ if DEBUG_OUTPUT; puts "svg::FXRoom::svg_draw" end
700
+
701
+ if (!((opts['draw_connections'] == false) && (@name =~ /Shortcut to.*/i)))
702
+ svg_draw_box( svg, opts, idx, x, y )
703
+
704
+ if ((opts['draw_roomnames'] == true) || (@name =~ /Shortcut to.*/i) || (idx == 0))
705
+
706
+ # Even if we're not printing location names, print the "Shortcut to"
707
+ # part of any locations which start with that text. This convention of
708
+ # a room named "Shortcut to <another location>" is for the sole purpose
709
+ # of allowing additional connections to be collected which are then fwd'd
710
+ # to the named location, because currently IFMapper can only provide
711
+ # support for up to eight cardinal / ordinal directions which can be a
712
+ # problem for large locations.
713
+ if ((opts['draw_roomnames'] == false) && (@name =~ /(Shortcut to).*/i))
714
+ @name = $1
715
+ end
716
+
717
+ x, y = svg_draw_name( svg, opts, x, y )
718
+ end
719
+ end
720
+ end
721
+
722
+ end
723
+
724
+
725
+
726
+ class FXSection
727
+
728
+ def svg_draw_grid(pdf, opts, w, h )
729
+ if DEBUG_OUTPUT; puts "svg::FXSection::svg_draw_grid" end
730
+ (0...w).each { |xx|
731
+ (0...h).each { |yy|
732
+ x = xx * opts['ww'] + opts['ws_2'] + opts['margin_2']
733
+ y = yy * opts['hh'] + opts['hs_2'] + opts['margin_2']
734
+ }
735
+ }
736
+ end
737
+
738
+
739
+ def svg_draw_section_name( svg, opts, x, y )
740
+ return [x,y] if not @name or @name == ''
741
+
742
+ font_size = opts['name_font_size']
743
+
744
+ if opts['print_section_names'] == true
745
+ svg, x, y = SVGUtilities::add_text( svg, x, y, font_size, @name, opts, '2F4F4F' )
746
+ y = y + opts['name_line_spacing']
747
+ end
748
+
749
+ if opts['draw_sectioncomments'] == true
750
+
751
+ if not @comments or @comments == ''
752
+ if DEBUG_OUTPUT; puts "svg::FXSection::svg_draw_section_name:section comments is empty, not printing" end
753
+ else
754
+ num_chars_per_line = svg.root.attributes["width"].to_i() / (font_size*0.75)
755
+
756
+ brokenlines = @comments.split(/\r?\n/);
757
+
758
+ brokenlines.each {|brokenline|
759
+
760
+ lines = SVGUtilities::break_text_lines(brokenline, num_chars_per_line);
761
+ lines.each {|line|
762
+ svg, x, y = SVGUtilities::add_text( svg, x, y, font_size*0.75, line, opts, '000000' )
763
+ y = y + (opts['name_line_spacing'])
764
+ }
765
+
766
+ y = y + (opts['name_line_spacing'])
767
+ }
768
+
769
+ end
770
+
771
+ end
772
+
773
+ y = y + (opts['name_line_spacing'] * 8)
774
+
775
+ return [x,y]
776
+ end
777
+
778
+ def svg_width( opts )
779
+ minmaxxy = min_max_rooms
780
+ maxx = minmaxxy[1][0]
781
+
782
+ sect_width = (maxx+4) * opts['ww']
783
+ return sect_width
784
+ end
785
+
786
+ def svg_height( opts )
787
+ minmaxxy = min_max_rooms
788
+ maxy = minmaxxy[1][1]
789
+
790
+ sect_height = (maxy+2) * opts['hh']
791
+ return sect_height
792
+ end
793
+
794
+ def svg_draw(svg, opts, x, y, mapname, section_idx )
795
+ if DEBUG_OUTPUT; puts "svg::FXSection::svg_draw" end
796
+
797
+ x, y = svg_draw_section_name( svg, opts, x, y )
798
+
799
+ origx = x
800
+ origy = y
801
+
802
+ @connections.each { |c|
803
+ a = c.roomA
804
+ b = c.roomB
805
+ next if a.y < 0 and b and b.y < 0
806
+ c.svg_draw( svg, opts, x, y )
807
+ }
808
+
809
+ @rooms.each_with_index { |r, idx|
810
+ next if r.y < 0
811
+ r.svg_draw( svg, opts, idx, x, y)
812
+ }
813
+
814
+
815
+ # Add the compass displaying code in here so that it
816
+ # is displayed beneath the interactive display items when
817
+ # they are switched on.
818
+
819
+ compass_scale_factor = opts['compass_size'];
820
+
821
+ if(compass_scale_factor > 0)
822
+
823
+ compassx = (x + opts['margin'] + (opts['w'] / 2))/compass_scale_factor
824
+ compassy = ((y + svg_height(opts)) - opts['h'])/compass_scale_factor
825
+
826
+ svg.root.add_element "use", {
827
+ "x" => compassx,
828
+ "y" => compassy,
829
+ "xlink:href" => "#compass",
830
+ "transform" => "scale(" + compass_scale_factor.to_s() + ")"
831
+ }
832
+
833
+ y = y + (64 * compass_scale_factor)
834
+
835
+ end
836
+
837
+ #
838
+ # Iterate through the list of rooms a second time to
839
+ # add the interactive elements (list of objects/tasks).
840
+ # We do this as a second pass so that these objects a displayed
841
+ # on top of the basic room objects.
842
+ #
843
+
844
+ if opts['draw_interactive'] == true
845
+ if DEBUG_OUTPUT; puts "svg::FXSection::svg_draw::draw_interactive == true" end
846
+ @rooms.each_with_index { |r, idx|
847
+ if (!((opts['draw_connections'] == false) && (r.name =~ /Shortcut to.*/i)))
848
+
849
+ r.svg_draw_interactive( svg, opts, idx, origx, origy, section_idx)
850
+ end
851
+ }
852
+ end
853
+
854
+ y = y + svg_height( opts )
855
+
856
+ return [x,y]
857
+
858
+ end
859
+
860
+ def svg_draw_separate(opts, svgfile, section_idx, mapname, mapcreator)
861
+ if DEBUG_OUTPUT; puts "svg::FXSection::svg_draw_separate" end
862
+
863
+ svg = SVGUtilities::new_svg_doc(svg_width(opts), svg_height(opts))
864
+ svg = SVGUtilities::add_compass(svg)
865
+
866
+ if DEBUG_OUTPUT; printf("svg_draw_separate: section_idx = %s\r\n", section_idx.to_s()) end
867
+
868
+ if opts['draw_interactive'] == true
869
+ svg = SVGUtilities::svg_add_script(svg, opts)
870
+ end
871
+
872
+ x = opts['name_x'] + opts['margin']
873
+ y = opts['name_y'] + opts['margin']
874
+ font_size = opts['name_font_size']
875
+
876
+ svg, x, y = SVGUtilities::add_titles(svg, opts, x, y, font_size, mapname, mapcreator)
877
+
878
+ x, y = svg_draw(svg, opts, x, y, svgfile, section_idx)
879
+
880
+ svg.root.attributes["height"] = y
881
+
882
+ formatter = REXML::Formatters::Pretty.new(2)
883
+ formatter.compact = true
884
+
885
+ file = File.open(svgfile, "w")
886
+ file.puts formatter.write(svg, "")
887
+ file.close
888
+
889
+ if DEBUG_OUTPUT; printf("\r\n") end
890
+
891
+ end
892
+ end
893
+
894
+
895
+ class FXMap
896
+
897
+ def svg_draw_sections_separate( opts, svgfile )
898
+
899
+ @sections.each_with_index { |sect, idx|
900
+
901
+ svgfilename = String::new(str=svgfile)
902
+
903
+ @section = idx
904
+ create_pathmap
905
+
906
+ if DEBUG_OUTPUT; printf("svg_draw_sections_separate: filename = %s\r\n", svgfilename.to_s()) end
907
+
908
+ # Remove .svg from end of filename if it is there.
909
+ if svgfilename =~ /\.svg$/
910
+ svgfilename = svgfilename[0..-5];
911
+ end
912
+
913
+ # Append the section number.
914
+ svgfilename << "-section" << (idx + 1).to_s()
915
+
916
+ if DEBUG_OUTPUT; printf("svg_draw_separate: filename = %s\r\n", svgfilename.to_s()) end
917
+
918
+ # Add .svg back onto the end of the filename.
919
+ svgfilename << ".svg"
920
+
921
+ if DEBUG_OUTPUT; printf("svg_draw_separate: filename = %s\r\n", svgfilename.to_s()) end
922
+
923
+ status "Exporting SVG file '#{svgfilename}'"
924
+
925
+ sect.svg_draw_separate( opts, svgfilename, idx, @name, @creator)
926
+ }
927
+
928
+ status "Exporting SVG Completed"
929
+ end
930
+
931
+ def svg_draw_sections( opts, svgfile )
932
+ if DEBUG_OUTPUT; puts "svg::FXMap::svg_draw_sections" end
933
+ if DEBUG_OUTPUT; printf("svg::FXMap::svg_draw_sections:@section=%s\r\n", @section) end
934
+
935
+ x = opts['name_x'] + opts['margin']
936
+ y = opts['name_y'] + opts['margin']
937
+ font_size = opts['name_font_size']
938
+
939
+ svg = SVGUtilities::new_svg_doc(max_width(opts), total_height(opts))
940
+ svg = SVGUtilities::add_compass(svg)
941
+
942
+ if opts['draw_interactive'] == true
943
+ svg = SVGUtilities::svg_add_script(svg, opts)
944
+ end
945
+
946
+ svg, x, y = SVGUtilities::add_titles(svg, opts, x, y, font_size, @name, @creator)
947
+
948
+ @sections.each_with_index { |sect, idx|
949
+
950
+ @section = idx
951
+ # For each page, we need to regenerate the pathmap so that complex
952
+ # paths will come out ok.
953
+ create_pathmap
954
+ # Now, we draw it
955
+
956
+ x, y = sect.svg_draw(svg, opts, x, y, @name, idx)
957
+
958
+ y = y + (opts['hh'] * 2)
959
+
960
+ }
961
+
962
+ create_pathmap
963
+ svg.root.attributes["height"] = y
964
+
965
+ if svgfile !~ /\.svg$/
966
+ svgfile << ".svg"
967
+ end
968
+
969
+ status "Exporting SVG file '#{svgfile}'"
970
+
971
+ formatter = REXML::Formatters::Pretty.new(2)
972
+ formatter.compact = true
973
+
974
+ file = File.open(svgfile, "w")
975
+ file.puts formatter.write(svg, "")
976
+ file.close
977
+
978
+ status "Exporting SVG Completed"
979
+
980
+ end
981
+
982
+ def max_width(opts)
983
+ max_width=0
984
+ @sections.each_with_index{ |sect, idx|
985
+ maxx = sect.svg_width(opts)
986
+ if(maxx > max_width)
987
+ max_width = maxx
988
+ end
989
+ if DEBUG_OUTPUT; puts("section=" + idx.to_s + ":maxx=" + maxx.to_s) end
990
+ }
991
+ return max_width
992
+ end
993
+
994
+ def total_height(opts)
995
+ total_height=0
996
+ @sections.each_with_index{ |sect, idx|
997
+ maxy = sect.svg_height(opts)
998
+ total_height = total_height + maxy
999
+ }
1000
+ return total_height
1001
+ end
1002
+
1003
+ def num_sections()
1004
+ return @sections.length
1005
+ end
1006
+
1007
+ def svg_export(svgfile, printer = nil)
1008
+
1009
+ if DEBUG_OUTPUT; puts "svg::FXMap::svg_export" end
1010
+
1011
+ map_options = @options.dup
1012
+
1013
+ ww = SVG_ROOM_WIDTH + SVG_ROOM_WS
1014
+ hh = SVG_ROOM_HEIGHT + SVG_ROOM_HS
1015
+
1016
+ svg_options = {
1017
+ 'ww' => ww,
1018
+ 'hh' => hh,
1019
+ 'w' => SVG_ROOM_WIDTH,
1020
+ 'h' => SVG_ROOM_HEIGHT,
1021
+ 'ws' => SVG_ROOM_WS,
1022
+ 'hs' => SVG_ROOM_HS,
1023
+ 'ws_2' => SVG_ROOM_WS / 2.0,
1024
+ 'hs_2' => SVG_ROOM_HS / 2.0,
1025
+ 'margin' => 10,
1026
+ 'margin_2' => 5,
1027
+ # 'width' => map_width,
1028
+ # 'height' => map_height,
1029
+ 'text_max_chars' => 20,
1030
+ 'text_line_spacing' => 2,
1031
+ 'text_margin' => 5,
1032
+ 'room_line_width' => 2,
1033
+ 'room_line_colour' => "black",
1034
+ 'room_font_size' => 8,
1035
+ 'objects_font_size' => 6,
1036
+ 'objects_width' => 140,
1037
+ 'room_num_font_size' => 6,
1038
+ 'print_room_nums' => true,
1039
+ 'num_line_width' => 1,
1040
+ 'num_line_colour' => "black",
1041
+ 'num_fill_colour' => "lightgreen",
1042
+ 'conn_line_width' => 2,
1043
+ 'conn_line_colour' => "black",
1044
+ 'door_line_width' => 2,
1045
+ 'door_line_colour' => "forestgreen",
1046
+ 'arrow_line_width' => 1,
1047
+ 'arrow_line_colour' => "black",
1048
+ 'arrow_fill_colour' => "black",
1049
+ 'name_font_size' => 18,
1050
+ 'name_x' => 0,
1051
+ 'name_y' => 0,
1052
+ 'name_line_spacing' => 4,
1053
+ 'print_title' => true,
1054
+ 'print_creator' => true,
1055
+ 'print_date' => true,
1056
+ 'creator_prefix' => "Creator: ",
1057
+ 'date_prefix' => "Date: ",
1058
+ 'draw_interactive' => true,
1059
+ 'draw_roomnames' => true,
1060
+ 'draw_connections' => true,
1061
+ 'corner_size' => 15,
1062
+ 'corner_line_colour' => "black",
1063
+ 'corner_line_width' => 1,
1064
+ 'corner_fill_colour' => "lightgreen",
1065
+ 'objs_line_colour' => "black",
1066
+ 'objs_line_width' => 1,
1067
+ 'objs_fill_colour' => "lightgreen",
1068
+ 'print_section_names' => true,
1069
+ 'section_name_prefix' => "Section: ",
1070
+ 'split_sections' => false,
1071
+ 'draw_sectioncomments' => true,
1072
+ 'compass_size' => 3
1073
+ }
1074
+
1075
+ svg_options.merge!(map_options)
1076
+
1077
+ begin
1078
+
1079
+ if svg_options['split_sections'] == false
1080
+ svg_draw_sections(svg_options, svgfile)
1081
+ else
1082
+ svg_draw_sections_separate(svg_options, svgfile)
1083
+ end
1084
+
1085
+ rescue => e
1086
+ p e
1087
+ p e.backtrace
1088
+ raise e
1089
+ end
1090
+ end
1091
+ end