ifmapper 1.1.6 → 1.2.0

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