class Mahoujin::Graphics::Renderer
Public Instance Methods
render(atom, iostream, style = nil, format = nil)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 4 def render(atom, iostream, style = nil, format = nil) style ||= Graphics::Styles::Basic.new case (format || 'PNG').upcase.to_sym when :PNG then render_in_png_format(atom, iostream, style) when :SVG then render_in_svg_format(atom, iostream, style) else raise end end
Private Instance Methods
center_point(x1, y1, x2, y2)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 353 def center_point(x1, y1, x2, y2) [(x1 + x2).fdiv(2), (y1 + y2).fdiv(2)] end
draw_hexagon(bbox, radius, background)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 266 def draw_hexagon(bbox, radius, background) left, top, right, bottom = bbox.left, bbox.top, bbox.right, bbox.bottom centerline = bbox.centerline a = left, centerline b = left + radius, top c = right - radius, top d = right, centerline e = right - radius, bottom f = left + radius, bottom @ctx.move_to(*a).line_to(*b).line_to(*c).line_to(*d).line_to(*e).line_to(*f).close_path @ctx.save { @ctx.set_source_color(background).fill_preserve } @ctx.stroke end
draw_infinite_loop_line(v1, v2, v3, v4, h1, h2, radius)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 294 def draw_infinite_loop_line(v1, v2, v3, v4, h1, h2, radius) a = v2, h2 b = v2, h1 c = v3, h1 d = v3, h2 e = v1, (h1 + h2).fdiv(2) f = v4, (h1 + h2).fdiv(2) draw_rounded_corner(*e, a[0] - e[0], a[1] - e[1], radius, :sw) draw_rounded_corner(e[0], b[1], b[0] - e[0], e[1] - b[1], radius, :nw) draw_straight_line(*b, *c) draw_rounded_corner(*c, f[0] - c[0], f[1] - c[1], radius, :ne) draw_rounded_corner(d[0], f[1], f[0] - d[0], d[1] - f[1], radius, :se) end
draw_rectangle(bbox, background)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 282 def draw_rectangle(bbox, background) @ctx.rounded_rectangle(bbox.left, bbox.top, bbox.width, bbox.height, 0) @ctx.save { @ctx.set_source_color(background).fill_preserve } @ctx.stroke end
draw_rounded_corner(x, y, width, height, radius, corner)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 328 def draw_rounded_corner(x, y, width, height, radius, corner) x1, y1 = x, y x2, y2 = x + width, y + height radius = [width, height, radius].min case corner when :nw @ctx.move_to(x1, y2).line_to(x1, y1 + radius).stroke @ctx.arc(x1 + radius, y1 + radius, radius, 180 * (Math::PI / 180), 270 * (Math::PI / 180)).stroke @ctx.move_to(x1 + radius, y1).line_to(x2, y1).stroke when :ne @ctx.move_to(x1, y1).line_to(x2 - radius, y1).stroke @ctx.arc(x2 - radius, y1 + radius, radius, 270 * (Math::PI / 180), 360 * (Math::PI / 180)).stroke @ctx.move_to(x2, y1 + radius).line_to(x2, y2).stroke when :se @ctx.move_to(x2, y1).line_to(x2, y2 - radius).stroke @ctx.arc(x2 - radius, y2 - radius, radius, 0 * (Math::PI / 180), 90 * (Math::PI / 180)).stroke @ctx.move_to(x2 - radius, y2).line_to(x1, y2).stroke when :sw @ctx.move_to(x2, y2).line_to(x1 + radius, y2).stroke @ctx.arc(x1 + radius, y2 - radius, radius, 90 * (Math::PI / 180), 180 * (Math::PI / 180)).stroke @ctx.move_to(x1, y2 - radius).line_to(x1, y1).stroke end end
draw_rounded_rectangle(bbox, radius, background)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 288 def draw_rounded_rectangle(bbox, radius, background) @ctx.rounded_rectangle(bbox.left, bbox.top, bbox.width, bbox.height, radius) @ctx.save { @ctx.set_source_color(background).fill_preserve } @ctx.stroke end
draw_snake_case_line(v1, v2, v3, v4, h1, h2, radius)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 309 def draw_snake_case_line(v1, v2, v3, v4, h1, h2, radius) a = v1, h1 b = v2, h2 c = v3, h2 d = v4, h1 e = center_point(*a, *b) f = center_point(*c, *d) draw_rounded_corner(*a, e[0] - a[0], e[1] - a[1], radius, :ne) draw_rounded_corner(*e, b[0] - e[0], b[1] - e[1], radius, :sw) draw_straight_line(*b, *c) draw_rounded_corner(c[0], f[1], f[0] - c[0], c[1] - f[1], radius, :se) draw_rounded_corner(f[0], d[1], d[0] - f[0], f[1] - d[1], radius, :nw) end
draw_straight_line(x1, y1, x2, y2)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 324 def draw_straight_line(x1, y1, x2, y2) @ctx.move_to(x1, y1).line_to(x2, y2).stroke end
draw_text(bbox, text, font)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 253 def draw_text(bbox, text, font) e = measure_text_metrics(text, font) x = bbox.left + bbox.width.fdiv(2) - ( e.width.fdiv(2) + e.x_bearing) y = bbox.top + bbox.height.fdiv(2) - (e.height.fdiv(2) + e.y_bearing) @ctx.save do @ctx.select_font_face(@ctx.font_face.family, @ctx.font_face.slant, Cairo::FONT_WEIGHT_BOLD) if font[:bold] @ctx.select_font_face(@ctx.font_face.family, Cairo::FONT_SLANT_ITALIC, @ctx.font_face.weight) if font[:italic] @ctx.set_font_size(font[:size]) if font[:size] @ctx.move_to(x, y).show_text(text) end end
fill_bbox(bbox, background)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 245 def fill_bbox(bbox, background) @ctx.save do @ctx.rounded_rectangle(bbox.left, bbox.top, bbox.width, bbox.height, 0) @ctx.set_source_color(background) @ctx.fill end end
graphics_corner_radius()
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 60 def graphics_corner_radius @style.basic[:corner][:radius] * graphics_unit end
graphics_font()
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 52 def graphics_font @style.basic[:font] end
graphics_unit()
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 56 def graphics_unit @style.basic[:unit] end
graphics_value(atom, attribute)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 64 def graphics_value(atom, attribute) @style.query(atom.class, attribute) end
initialize_graphics_environment(bbox)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 40 def initialize_graphics_environment(bbox) @ctx.rectangle(0, 0, bbox.width, bbox.height) @ctx.set_source_color(:white) @ctx.fill @ctx.set_source_color(:black) @ctx.set_line_width(@style.basic[:line][:width]) @ctx.select_font_face(@style.basic[:font][:family], Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL) @ctx.set_font_size(@style.basic[:font][:size]) end
measure_text_metrics(text, font)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 68 def measure_text_metrics(text, font) @ctx.save @ctx.select_font_face(@ctx.font_face.family, @ctx.font_face.slant, Cairo::FONT_WEIGHT_BOLD) if font[:bold] @ctx.select_font_face(@ctx.font_face.family, Cairo::FONT_SLANT_ITALIC, @ctx.font_face.weight) if font[:italic] @ctx.set_font_size(font[:size]) if font[:size] @ctx.text_extents(text) ensure @ctx.restore end
render_atom(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 78 def render_atom(atom) send "render_#{atom.class.underscore_name}", atom end
render_choice(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 106 def render_choice(atom) atom.atoms.each { |a| render_atom(a) } reference = atom.atoms.max(&->(a1, a2) { a1.bbox.width <=> a2.bbox.width }) centerline = reference.bbox.centerline v1, v2, v3, v4 = atom.bbox.left, reference.bbox.left, reference.bbox.right, atom.bbox.right a = v1, centerline b = v2, centerline c = v3, centerline d = v4, centerline draw_straight_line(*a, *b) draw_straight_line(*c, *d) if (index = atom.atoms.find_index(reference)) != 0 upper = atom.atoms[index - 1] s = (v1 + v2).fdiv(2), (upper.bbox.centerline + centerline).fdiv(2) e = (v3 + v4).fdiv(2), (upper.bbox.centerline + centerline).fdiv(2) draw_rounded_corner(a[0], s[1], s[0] - a[0], a[1] - s[1], graphics_corner_radius, :se) draw_rounded_corner(e[0], e[1], d[0] - e[0], d[1] - e[1], graphics_corner_radius, :sw) atom.atoms[0...index].each do |ua| w = v2, ua.bbox.centerline x = ua.bbox.left, ua.bbox.centerline y = ua.bbox.right, ua.bbox.centerline z = v3, ua.bbox.centerline draw_rounded_corner(s[0], w[1], w[0] - s[0], s[1] - w[1], graphics_corner_radius, :nw) draw_straight_line(*w, *x) draw_straight_line(*y, *z) draw_rounded_corner(z[0], z[1], e[0] - z[0], e[1] - z[1], graphics_corner_radius, :ne) end end if (index = atom.atoms.find_index(reference)) != atom.atoms.size - 1 lower = atom.atoms[index + 1] s = (v1 + v2).fdiv(2), (lower.bbox.centerline + centerline).fdiv(2) e = (v3 + v4).fdiv(2), (lower.bbox.centerline + centerline).fdiv(2) draw_rounded_corner(a[0], a[1], s[0] - a[0], s[1] - a[1], graphics_corner_radius, :ne) draw_rounded_corner(e[0], d[1], d[0] - e[0], e[1] - d[1], graphics_corner_radius, :nw) atom.atoms[(index + 1)..-1].each do |la| w = v2, la.bbox.centerline x = la.bbox.left, la.bbox.centerline y = la.bbox.right, la.bbox.centerline z = v3, la.bbox.centerline draw_rounded_corner(s[0], s[1], w[0] - s[0], w[1] - s[1], graphics_corner_radius, :sw) draw_straight_line(*w, *x) draw_straight_line(*y, *z) draw_rounded_corner(z[0], e[1], e[0] - z[0], z[1] - e[1], graphics_corner_radius, :se) end end; nil end
render_concatenation(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 159 def render_concatenation(atom) atom.atoms.each { |a| render_atom(a) } centerline = atom.bbox.centerline atom.atoms.each_cons(2) do |a1, a2| draw_straight_line(a1.bbox.right, centerline, a2.bbox.left, centerline) end end
render_exception(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 222 def render_exception(atom) font = graphics_value(atom, :font) padding = (atom.bbox.width - measure_text_metrics(atom.content, font).width).fdiv(2) draw_hexagon(atom.bbox, padding * 0.618, graphics_value(atom, :background)) draw_text(atom.bbox, atom.content, font) end
render_grouping(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 210 def render_grouping(atom) render_atom(atom.atom) end
render_in_png_format(atom, iostream, style)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 15 def render_in_png_format(atom, iostream, style) layout = Graphics::Layout.new layout.layout(atom, style, :PNG) Cairo::ImageSurface.new(atom.bbox.width, atom.bbox.height) do |surface| @ctx = Cairo::Context.new(surface) @style = style initialize_graphics_environment(atom.bbox) render_atom(atom) @ctx.target.write_to_png(iostream) end end
render_in_svg_format(atom, iostream, style)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 28 def render_in_svg_format(atom, iostream, style) layout = Graphics::Layout.new layout.layout(atom, style, :SVG) Cairo::SVGSurface.new(iostream, atom.bbox.width, atom.bbox.height) do |surface| @ctx = Cairo::Context.new(surface) @style = style initialize_graphics_environment(atom.bbox) render_atom(atom) end end
render_non_terminal(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 230 def render_non_terminal(atom) font = graphics_value(atom, :font) draw_rectangle(atom.bbox, graphics_value(atom, :background)) draw_text(atom.bbox, atom.content, font) end
render_optional(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 187 def render_optional(atom) render_atom(atom.atom) innerbbox, outerbbox, centerline = atom.atom.bbox, atom.bbox, atom.bbox.centerline v1, v2, v3, v4 = outerbbox.left, innerbbox.left, innerbbox.right, outerbbox.right draw_straight_line(v1, centerline, v2, centerline) draw_straight_line(v3, centerline, v4, centerline) draw_snake_case_line(v1, v2, v3, v4, centerline, outerbbox.bottom, graphics_corner_radius) end
render_prose(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 214 def render_prose(atom) font = graphics_value(atom, :font) padding = (atom.bbox.width - measure_text_metrics(atom.content, font).width).fdiv(2) draw_hexagon(atom.bbox, padding * 0.618, graphics_value(atom, :background)) draw_text(atom.bbox, atom.content, font) end
render_rule(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 86 def render_rule(atom) render_atom(atom.rule_name_atom) render_atom(atom.atom) innerbbox, outerbbox, centerline = atom.atom.bbox, atom.bbox, atom.bbox.centerline v1, v2, v3, v4 = outerbbox.left, innerbbox.left, innerbbox.right, outerbbox.right draw_straight_line(v1, centerline, v2, centerline) draw_straight_line(v3, centerline, v4, centerline) h = graphics_unit draw_straight_line(v1, centerline - h.fdiv(2), v1, centerline + h.fdiv(2)) draw_straight_line(v4, centerline - h.fdiv(2), v4, centerline + h.fdiv(2)) end
render_rule_name(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 101 def render_rule_name(atom) font = graphics_value(atom, :font) draw_text(atom.bbox, atom.content, font) end
render_specific_repetition(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 168 def render_specific_repetition(atom) render_atom(atom.atom) innerbbox, outerbbox, centerline = atom.atom.bbox, atom.bbox, atom.bbox.centerline v1, v2, v3, v4 = outerbbox.left, innerbbox.left, innerbbox.right, outerbbox.right draw_straight_line(v1, centerline, v2, centerline) draw_straight_line(v3, centerline, v4, centerline) draw_infinite_loop_line(v1, v2, v3, v4, outerbbox.top, centerline, graphics_corner_radius) text = "{ #{atom.number} }" font = { size: graphics_font[:size] * 0.8 } e = measure_text_metrics(text, font) x, y = (v1 + v4).fdiv(2) - e.width.fdiv(2), outerbbox.top - e.height.fdiv(2) bbox = Graphics::Utilities::Rectangle.new(x, y, x + e.width, y + e.height) fill_bbox(bbox, :white) draw_text(bbox, text, font) end
render_syntax(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 82 def render_syntax(atom) atom.atoms.each { |a| render_atom(a) } end
render_terminal(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 237 def render_terminal(atom) font = graphics_value(atom, :font) padding = (atom.bbox.width - measure_text_metrics(atom.content, font).width).fdiv(2) draw_rounded_rectangle(atom.bbox, padding, graphics_value(atom, :background)) draw_text(atom.bbox, atom.content, font) end
render_zero_or_more_repetition(atom)
click to toggle source
# File lib/mahoujin/graphics/renderer.rb, line 198 def render_zero_or_more_repetition(atom) render_atom(atom.atom) innerbbox, outerbbox, centerline = atom.atom.bbox, atom.bbox, atom.bbox.centerline v1, v2, v3, v4 = outerbbox.left, innerbbox.left, innerbbox.right, outerbbox.right draw_straight_line(v1, centerline, v2, centerline) draw_straight_line(v3, centerline, v4, centerline) draw_snake_case_line(v1, v1 + (v2 - v1).fdiv(3) * 2, v4 - (v4 - v3).fdiv(3) * 2, v4, centerline, outerbbox.bottom, graphics_corner_radius) draw_infinite_loop_line(v2 - (v2 - v1).fdiv(3) * 1, v2, v3, v3 + (v4 - v3).fdiv(3) * 1, outerbbox.top, centerline, graphics_corner_radius) end