LoginSignup
1
0

More than 1 year has passed since last update.

Ruby + Victor でSVGお絵描き(簡単な散布図を描いてみた)

Last updated at Posted at 2022-06-05

Victor は Ruby で SVG 画像を作るための gem です。pure Ruby なので気軽に使えます。他の gem への依存もありません。

リポジトリの examples ディレクトリ を見ると雰囲気が分かります。

これを使ってためしに簡単な散布図のようなものを描いてみました。

できあがり

image.png

ブラウザで表示させてスクリーンショットを撮ったもの。
色付きのグラフも良いものですが、白黒のも渋くて好きです。

コード

感触がなんとなく分かればOK という程度の試みです。DRY でなかったり座標決め打ちだったりしていろいろ適当ですが気にしない。

最低限でいえば直線が描ければよくて、あとは円とかテキストが描ければなお良し! :100: という感じ。
あとは transform を使ってy軸ラベルを回転させたり CSS でフォントを指定したりできるな、といったあたりを試すことができました。

require "victor"

OFFSET_X = 80
OFFSET_Y = 20
FRAME_WIDTH = 400 - 80 - 20
FONT_SIZE = 16
STROKE_STYLE = { stroke: "#000", stroke_width: 1 }

def transform_x(x)
  OFFSET_X + x * FRAME_WIDTH
end

def transform_y(y)
  OFFSET_Y + (1 - y) * FRAME_WIDTH
end

def draw_line(x1, y1, x2, y2)
  $svg.line(
    x1: x1, y1: y1,
    x2: x2, y2: y2,
    style: STROKE_STYLE
  )
end

def draw_frame
  $svg.rect(
    x: 80, y: 20, width: 300, height: 300,
    style: STROKE_STYLE, fill: "transparent"
  )
end

def draw_tick_x(x)
  font_offset_x = -12
  font_offset_y = 26
  frame_x = transform_x(x)

  draw_line frame_x, 320, frame_x, 330
  $svg.text(
    x, x: frame_x + font_offset_x, y: 320 + font_offset_y,
    font_size: FONT_SIZE, class: "text"
  )
end

def draw_axis_x
  [0.0, 0.2, 0.4, 0.6, 0.8, 1.0].each { |x| draw_tick_x x }

  frame_x = transform_x(0.5)
  $svg.text(
    "x軸ラベル", x: frame_x, y: 320 + 50,
    text_anchor: "middle",
    font_size: FONT_SIZE, class: "text"
  )
end

def draw_tick_y(y)
  font_offset_x = -40
  font_offset_y = 5
  frame_y = transform_y(y)

  draw_line 70, frame_y, 80, frame_y
  $svg.text(
    y, x: 80 + font_offset_x, y: frame_y + font_offset_y,
    font_size: FONT_SIZE, class: "text"
  )
end

def draw_axis_y
  [0.0, 0.2, 0.4, 0.6, 0.8, 1.0].each { |y| draw_tick_y y }

  frame_y = transform_y(0.5)
  $svg.text(
    "y軸ラベル", x: 0, y: 0, font_size: FONT_SIZE, class: "text",
    text_anchor: "middle",
    transform: "translate(26, #{frame_y}) rotate(270)"
  )
end

def plot_points(xs, ys)
  xs.zip(ys).each do |x, y|
    $svg.circle(
      cx: transform_x(x), cy: transform_y(y), r: 5,
      style: STROKE_STYLE, fill: "transparent"
    )
  end
end

def plot_line(x1, y1, x2, y2)
  draw_line(
    transform_x(x1), transform_y(y1),
    transform_x(x2), transform_y(y2)
  )
end

# --------------------------------

$svg = Victor::SVG.new(
  width: 400, height: 400,
  style: { background: "#fff" }
)

$svg.css = <<CSS
  .text { font-family: "Noto Sans CJK JP"; }
CSS

draw_frame
draw_axis_x
draw_axis_y
plot_points [0.1, 0.6, 0.61, 0.9], [0.2, 0.3, 0.31, 0.7] # xs, ys
plot_line 0.1, 0.5, 0.9, 0.9 # x1, y1, x2, y2

$svg.save "sample.svg"

関連

SVG を Ruby のコードに変換する機能があるのがちょっとおもしろい。

この記事を読んだ人は(ひょっとしたら)こちらも読んでいます

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0