Class: Diagrams::Diagram

Inherits:
Object show all
Includes:
REXML
Defined in:
lib/maruku/ext/diagrams/layout.rb,
lib/maruku/ext/diagrams/parser.rb,
lib/maruku/ext/diagrams/to_html.rb,
lib/maruku/ext/diagrams/to_latex.rb,
lib/maruku/ext/diagrams/structures.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(s) ⇒ Diagram

Returns a new instance of Diagram.



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/maruku/ext/diagrams/structures.rb', line 142

def initialize(s)
	@text = create_grid_from_string(s)
	@to_process = Grid.new(@text.width, @text.height, false)
	@occup = Grid.new(@text.width, @text.height, nil)
	@boxes = []
	@points = []
	@arrows = []
	# mark cells to process
	@text.each do |x,y,e| 
		@to_process.set(x,y,true) if e != 32
	end
	@width, @height = @text.width,@text.height

#	p @to_process
	
	puts @text.inspect2(@to_process)
	
	detect_boxes
	detect_points
	detect_arrows

	puts @text.inspect2(@to_process)
end

Instance Attribute Details

#arrowsObject

Returns the value of attribute arrows.



138
139
140
# File 'lib/maruku/ext/diagrams/structures.rb', line 138

def arrows
  @arrows
end

#boxesObject

Returns the value of attribute boxes.



138
139
140
# File 'lib/maruku/ext/diagrams/structures.rb', line 138

def boxes
  @boxes
end

#occupObject

Returns the value of attribute occup.



140
141
142
# File 'lib/maruku/ext/diagrams/structures.rb', line 140

def occup
  @occup
end

#pointsObject

Returns the value of attribute points.



138
139
140
# File 'lib/maruku/ext/diagrams/structures.rb', line 138

def points
  @points
end

#textObject

Returns the value of attribute text.



140
141
142
# File 'lib/maruku/ext/diagrams/structures.rb', line 140

def text
  @text
end

#to_processObject

Returns the value of attribute to_process.



140
141
142
# File 'lib/maruku/ext/diagrams/structures.rb', line 140

def to_process
  @to_process
end

Instance Method Details

#arrow_conObject



411
# File 'lib/maruku/ext/diagrams/layout.rb', line 411

def arrow_con()  Spring.new(30,1,60,100,1000) end

#assign_variables(diag_id) ⇒ Object



395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/maruku/ext/diagrams/layout.rb', line 395

def assign_variables(diag_id)
	@boxes.each_with_index do |b, i|
		b.v_tl = "#{diag_id}_b#{i}_tl"
		b.v_br = "#{diag_id}_b#{i}_br"
	end
	@arrows.each_with_index do |a, i| 
		a.v_start = "#{diag_id}_a#{i}_start"
		a.v_end = "#{diag_id}_a#{i}_end"
	end
	@points.each_with_index do |p, i| 
		p.v_p = "#{diag_id}_p#{i}_p"
	end
end

#create_grid_from_string(string) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/maruku/ext/diagrams/parser.rb', line 4

def create_grid_from_string(string)
	lines = string.split("\n").map{|x| x.gsub(/\s*$/,'') }
	
	while lines.first && lines.first.strip.size == 0; lines.shift  end
	while lines.last && lines.last.strip.size == 0; lines.pop  end
	width = lines.map{|x| x.size}.max
	height = lines.size

	grid = Grid.new(width, height, 32)
	for y in 0..height-1
		for x in 0..width-1
			grid.set(x, y, lines[y][x])
		end
	end	
	grid
end

#detect_arrowsObject



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/maruku/ext/diagrams/parser.rb', line 48

def detect_arrows
	[	[?|, /^\|*$/, [?^, ?A], [?v, ?V],  0, 1, :S],
		[?-, /^\-*$/, [?<], [?>],  1, 0, :E],
		[?\\,/^\\*$/, [], [], 1, 1, :SE],
		[?/, /^\/*$/, [], [], -1, 1, :SW] 
	].each do |char, reg, cap1, cap2, inc_x, inc_y, ty|
		for_each_to_process(char) do |x,y,e|
			find_arrow(x, y, reg, cap1, cap2, inc_x, inc_y)
		end
	end
end

#detect_boxesObject



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/maruku/ext/diagrams/parser.rb', line 142

def detect_boxes
	for_each_to_process(?+) do |x,y,e|
		find_box(x,y,/^\+[\-\+]+\+$/, /^\+[\|\+]+\+$/)
	end
	for_each_to_process(?/) do |x,y,e|
		find_box(x,y,/^\/[\-\+]+\\$/, /^\/[\|\+]+\\$/)
	end
	
	# find alignments
	@boxes.each do |b1|
		@boxes.each do |b2|
			if b1 != b2
				if (b2.y < b1.y)
					b1.aligned[:l].push(b2) if (b1.x == b2.x)
					b1.aligned[:r].push(b2) if (b1.x+b1.width == b2.x+b2.width)
				end
				if (b2.x < b1.x)
					b1.aligned[:t].push(b2) if (b1.y == b2.y)
					b1.aligned[:b].push(b2) if (b1.y+b1.height == b2.y+b2.height)
				end
			end
		end
	end
end

#detect_pointsObject



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/maruku/ext/diagrams/parser.rb', line 22

def detect_points
	[?+, ?*].each do |p|
		for_each_to_process(p) do |x,y|
			point = Point.new 
			point.pos = [x, y]
			point.char = p
			@to_process.set(x,y,false)
			@points.push point
			@occup.set(x,y, point)
		end
	end	
	
	@points.each do |p1|
		@points.each do |p2|
			if p1 != p2
				if p2.pos.x < p1.pos.x 
					p1.aligned[:t].push(p2) if p2.pos.y == p1.pos.y
				end
				if p2.pos.y < p1.pos.y 
					p1.aligned[:l].push(p2) if p2.pos.x == p1.pos.x
				end
			end
		end
	end
end

#do_layout(diag_id) ⇒ Object



409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
# File 'lib/maruku/ext/diagrams/layout.rb', line 409

def do_layout(diag_id)
	def margin_con() Spring.new(30,0,30,1,1000)   end
	def arrow_con()  Spring.new(30,1,60,100,1000) end
	def zero_con()   Spring.new(0,0,0,0,0)     end
	def positive_con(pref)   Spring.new(0,1,pref,1000,1000)     end
	
	@diag_id = diag_id
	assign_variables(diag_id)
	
	l = Layout.new
	@boxes.each  do |b|
		# size of the box is copied from b.hs and b.vs
		l.add_con_h(b.v_tl, b.v_br, b.hs)
		l.add_con_v(b.v_tl, b.v_br, b.vs)
		# should not be outside of the diagram
		l.add_con_h(DIAG_TL,  b.v_tl, margin_con)
		l.add_con_h(b.v_br, DIAG_BR, margin_con)
		l.add_con_v(DIAG_TL,  b.v_tl, margin_con)
		l.add_con_v(b.v_br, DIAG_BR, margin_con)
		# now, for each arrow 
		b.links[:N].each{|a|
			l.add_con_v(a.v_end, b.v_tl, zero_con)
			l.add_con_h(b.v_tl, a.v_end, positive_con(b.hs.pref/2))
			l.add_con_h(a.v_end, b.v_br, positive_con(b.hs.pref/2))
		}
		b.links[:S].each{|a|
			l.add_con_v(b.v_br, a.v_start, zero_con)
			l.add_con_h(b.v_tl, a.v_start, positive_con(b.hs.pref/2))
			l.add_con_h(a.v_start, b.v_br, positive_con(b.hs.pref/2))
		}
		b.links[:W].each{|a|
			l.add_con_h(a.v_end, b.v_tl, zero_con) 
			l.add_con_v(b.v_tl, a.v_end, positive_con(b.vs.pref/2))
			l.add_con_v(a.v_end, b.v_br, positive_con(b.vs.pref/2))
		}
		b.links[:E].each{|a|
			l.add_con_h(b.v_br, a.v_start, zero_con)
			l.add_con_v(b.v_tl, a.v_start, positive_con(b.vs.pref/2))
			l.add_con_v(a.v_start, b.v_br, positive_con(b.vs.pref/2))
		}
		
		b.aligned[:t].each do |b2| l.add_con_v(b2.v_tl, b.v_tl, zero_con) end
		b.aligned[:b].each do |b2| l.add_con_v(b2.v_br, b.v_br, zero_con) end
		b.aligned[:l].each do |b2| l.add_con_h(b2.v_tl, b.v_tl, zero_con) end
		b.aligned[:r].each do |b2| l.add_con_h(b2.v_br, b.v_br, zero_con) end
	end
	
	@points.each do |p|
		# should not be outside of the diagram
		l.add_con_h(DIAG_TL,  p.v_p, margin_con)
		l.add_con_h(p.v_p, DIAG_BR, margin_con)
		l.add_con_v(DIAG_TL,  p.v_p, margin_con)
		l.add_con_v(p.v_p, DIAG_BR, margin_con)
		# now, for each arrow 
		p.links[:N].each{|a|
			l.add_con_v(a.v_end, p.v_p, zero_con)
				l.add_con_h(a.v_end, p.v_p, zero_con)
		}
		p.links[:S].each{|a|
			l.add_con_v(p.v_p, a.v_start, zero_con)
				l.add_con_h(p.v_p, a.v_start, zero_con)
		}
		p.links[:W].each{|a|
			l.add_con_h(a.v_end, p.v_p, zero_con)
				l.add_con_v(a.v_end, p.v_p, zero_con)
		}
		p.links[:E].each{|a|
			l.add_con_h(p.v_p, a.v_start, zero_con)
				l.add_con_v(p.v_p, a.v_start, zero_con)
		}

		p.aligned[:t].each do |p2| l.add_con_v(p2.v_p, p.v_p, zero_con) end
		p.aligned[:l].each do |p2| l.add_con_h(p2.v_p, p.v_p, zero_con) end
	end
	
	@arrows.each do |a|
		if a.is_vert?
			l.add_con_v(a.v_start, a.v_end, arrow_con)
			l.add_con_h(a.v_start, a.v_end, zero_con)
		else
			l.add_con_h(a.v_start, a.v_end, arrow_con)
			l.add_con_v(a.v_start, a.v_end, zero_con)
		end
	end

	doth, dotv = l.to_dot
	File.open("#{diag_id}_h.dot",'w') do |f| f.write doth end
	File.open("#{diag_id}_v.dot",'w') do |f| f.write dotv end

	l.algo2
#			hs, vs = l.get_preferred(DIAG_TL, DIAG_BR)
	
#			puts "Preferred hs = #{hs.inspect}"
#			puts "Preferred vs = #{vs.inspect}"
end

#find_arrow(x, y, reg, cap1, cap2, inc_x, inc_y) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/maruku/ext/diagrams/parser.rb', line 61

def find_arrow(x, y, reg, cap1, cap2, inc_x, inc_y)
	inc = [inc_x, inc_y]
	rinc = [-inc_x, -inc_y]
	
	a = Arrow.new
	
	if cap1.include?(c = @text.get(x-inc_x, y-inc_y))
		a.cap1 = c
		a.start_pos = [x-inc_x, y-inc_y]
	else
		a.cap1 = nil
		a.start_pos = [x,y]
	end
	
	s = match(x,y,reg,inc_x,inc_y)
	len = s.size
	
	a.end_pos = [x+(len-1)*inc_x, y+(len-1)*inc_y]
	maybe_cap = [x+(len)*inc_x, y+(len)*inc_y]
	if cap2.include?(c=@text.get(*maybe_cap))
		len +=1
		a.end_pos = maybe_cap
		a.cap2 = c	
	else
		a.cap2 = nil
	end
	
	a.length = len
	
	before = [a.start_pos[0]-inc_x,a.start_pos[1]-inc_y]
	if ob = @occup.get(*before)
		impact = ob.link_direction(before, rinc)
		ob.links[impact] << a
		a.links[Reverse[impact]]<< ob
		a.start_link = ob
	else
		puts "Nothing found at #{before.inspect} : #{
		@text.get(*before)
		} while #{@text.get(*a.start_pos).chr}"
	end
	after = [a.end_pos[0]+inc_x,a.end_pos[1]+inc_y]
	if ob = @occup.get(*after)
		impact = ob.link_direction(after, inc)
		ob.links[impact] << a
		a.links[Reverse[impact]] << ob
		a.end_link = ob
	else
		puts "Nothing found at #{after.inspect} : #{
		@text.get(*after)
		} while #{@text.get(*a.end_pos).chr}"
	end
	
	iterate(a.start_pos,a.end_pos) do |x,y|
		@to_process.set(x,y,false)
		@occup.set(x,y,a)
	end
	
	@arrows.push a
	p a
end

#find_box(x, y, hmatch, vmatch) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/maruku/ext/diagrams/parser.rb', line 175

def find_box(x,y,hmatch,vmatch)
	horiz = match_horiz(x,y,hmatch)
	vert  = match_vert(x,y,vmatch)
	
	return if not horiz or not vert
	width, height = horiz.size, vert.size
	if (width > 2) && (height > 2)
		# mark the cell as processed
		@to_process.set_area(x, y, horiz.size, vert.size, false)
		box = Box.new(x,y,width,height)
		box.content = @text.read_area(x+1,y+1,width-2,height-2)
		box.content.gsub!(/\n/,' ')			
		# mark the area as occupied by the box
		@occup.set_area(x, y, horiz.size, vert.size, box)
		@boxes.push box
	end
end

#for_each_to_process(char) ⇒ Object



167
168
169
170
171
172
173
# File 'lib/maruku/ext/diagrams/parser.rb', line 167

def for_each_to_process(char) 
	@text.each do |x,y,e|
		if e === char && @to_process.get(x,y)
			yield x, y, e
		end
	end
end

#iterate(pos1, pos2) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/maruku/ext/diagrams/parser.rb', line 122

def iterate(pos1,pos2)
	inc_x = pos2[0]-pos1[0]
	inc_y = pos2[1]-pos1[1]
	len = [inc_x.abs,inc_y.abs].max + 1
	if inc_x.abs > inc_y.abs
		len = inc_x.abs + 1
	else
		len = inc_y.abs + 1
	end
	
	inc_x  /= inc_x.abs if inc_x != 0
	inc_y  /= inc_y.abs if inc_y != 0
	
#		raise "" if inc_x 
	for i in 0..(len-1)
		x, y = pos1[0]+i*inc_x,pos1[1]+i*inc_y
		yield x,y
	end
end

#margin_conObject



410
# File 'lib/maruku/ext/diagrams/layout.rb', line 410

def margin_con() Spring.new(30,0,30,1,1000)   end

#match(x, y, reg, inc_x, inc_y) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/maruku/ext/diagrams/parser.rb', line 193

def match(x,y,reg,inc_x,inc_y)
	s = ""
	best = nil
	while x<@text.width && y < @text.height
		s << @text.get(x,y).chr
		break if not @to_process.get(x,y)
		if s =~ reg
			best = s.dup 
		end
		x += inc_x; y += inc_y
	end
	best
end

#match_horiz(x, y, reg) ⇒ Object



207
208
209
# File 'lib/maruku/ext/diagrams/parser.rb', line 207

def match_horiz(x,y,reg)
	match(x,y,reg,1,0)
end

#match_vert(x, y, reg) ⇒ Object



211
212
213
# File 'lib/maruku/ext/diagrams/parser.rb', line 211

def match_vert(x,y,reg)
	match(x,y,reg,0,1)
end

#positive_con(pref) ⇒ Object



413
# File 'lib/maruku/ext/diagrams/layout.rb', line 413

def positive_con(pref)   Spring.new(0,1,pref,1000,1000)     end

#read_tex_discovery(my_diag_id, s) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/maruku/ext/diagrams/to_latex.rb', line 41

def read_tex_discovery(my_diag_id, s)
	s.split("\n").each do |l|
		diag_id, box_index, wd, ht, dp, text = l.split(",")
		if diag_id == my_diag_id
			puts "found box #{box_index}: #{wd} #{ht} #{dp}"
			wd,ht,dp = wd.to_f,ht.to_f,dp.to_f
			width = wd
			height = ht+dp
			puts "found box #{box_index}: #{width}x#{height}"
			i = box_index.to_i
			@boxes[i].hs.min = width
			@boxes[i].hs.shrink = 1
			@boxes[i].hs.pref = width*1.2
			@boxes[i].hs.stretch = 0.1
			@boxes[i].hs.max = 1000
			@boxes[i].vs.min = height
			@boxes[i].vs.shrink = 1
			@boxes[i].vs.pref = height*1.2
			@boxes[i].vs.stretch = 0.1
			@boxes[i].vs.max = 1000
		end
	end
end

#to_dev(x) ⇒ Object

in points



93
94
95
# File 'lib/maruku/ext/diagrams/to_latex.rb', line 93

def to_dev(x)
	x * 10
end

#to_htmlObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/maruku/ext/diagrams/to_html.rb', line 9

def to_html
	div = Element.new 'div'
	div.add_element 'div', {'style'=>'width: 1em', 'id'=>'emtest'}
	div.attributes['style'] = 
	"position:relative; display: block; width: #{@width}em; height: #{@height}em;"
	div.attributes['class'] = 'diagram'
	@boxes.each_with_index do |b, i|
		box = Element.new 'div'
		box.attributes['class'] = 'box'

		w = b.width
		box.attributes['id'] = "box#{i}"
		box.attributes['style'] = 
		"position:absolute; left:#{b.x}em; top:#{b.y}em; border:solid 1px black;"			
		content = Element.new 'div', box
		content.attributes['class'] = 'inner'
		content << Text.new( b.content )
		div << box
		div << Text.new("\n")
	end
	div
end

#to_latex(job = "anon_diagram") ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/maruku/ext/diagrams/to_latex.rb', line 65

def to_latex(job="anon_diagram")
	do_discovery = true
	
	tmp_out = "#{job}.dia"

	diag_id = "dia"
	s = write_latex_discovery(diag_id, tmp_out)

	if File.exist?(tmp_out)
		File.open(tmp_out, 'r') do |f|
			read_tex_discovery(diag_id, f.read)
		end
	end
	
	do_layout(job)
	
	@boxes.each_with_index do |b, i|
		width = b.hs.min
		height = b.vs.min
		s << ("\\framebox[%fpt]{\\rule{10pt}{%fpt}%s}"% [width,height, b.content])
	end
	
	s+="\n\nCiao\n\n"
	s 
end

#top_right(i) ⇒ Object



393
# File 'lib/maruku/ext/diagrams/layout.rb', line 393

def top_right(i); "#{@diag_id}_b#{i}_tr"; end

#write_latex_discovery(diag_id, output_file) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/maruku/ext/diagrams/to_latex.rb', line 18

def write_latex_discovery(diag_id, output_file)
	fid = 10
	s=""
#			s +="\\documentclass{article}\\begin{document}"
	s += ("\\immediate\\openout%d=%s\n" % [fid, output_file])
	# s+= "\\ifx\\already\\empty"
	# s+="\\def\\already{1}"
	# s+= "\\newlength{\\mywd}\n"
	# s+= "\\newlength{\\myht}\n"
	# s+= "\\newlength{\\mydp}\n"
	# s+="\\fi"
	@boxes.each_with_index do |b, i|
		s += ("\\settowidth{\\mywd}{\\hbox{%s}}\n" % b.content)
		s += ("\\settoheight{\\myht}{\\hbox{%s}}\n" % b.content)
		s += ("\\settodepth{\\mydp}{\\hbox{%s}}\n" % b.content)
		s += "\\immediate\\write%d{%s,%d,\\the\\mywd,\\the\\myht,\\the\\mydp}\n" % 
		[fid, diag_id, i]
	end
	s+=("\\immediate\\closeout%d\n" % fid)
#			s+="\\end{document}"
	s
end

#write_tex_discovery(diag_id, output_file) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
# File 'lib/maruku/ext/diagrams/to_latex.rb', line 6

def write_tex_discovery(diag_id, output_file)
	fid = 10
	s = ("\\immediate\\openout%d=%s\n" % [fid, output_file])
	@boxes.each_with_index do |b, i|
		s += ("\\setbox0=\\hbox{%s}\n" % b.content)
		s += "\\immediate\\write%d{%s,%d,\\the\\wd0,\\the\\ht0,\\the\\dp0 ,%s}\n" % 
		[fid, diag_id, i, b.content]
	end
	s += "\\end\n"
	s
end

#zero_conObject



412
# File 'lib/maruku/ext/diagrams/layout.rb', line 412

def zero_con()   Spring.new(0,0,0,0,0)     end