Skip to content

Instantly share code, notes, and snippets.

@takehiko
Created November 29, 2021 10:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takehiko/c5c5e3c1a6a14f51478afea842ffb86e to your computer and use it in GitHub Desktop.
Save takehiko/c5c5e3c1a6a14f51478afea842ffb86e to your computer and use it in GitHub Desktop.
Yet another quadrilateral analyzer & drawer
#!/usr/bin/env ruby
# qpr.rb : 四角形(Quadrilateral)の2辺の関係に基づき平行(Parallel)・
# 垂直(Right angle)の数を求め,2次元平面上に図形を描画する
# by takehikom
# see also:
# 青山尚司「平行だけでなく,垂直も観点に加えた四角形の弁別より」,
# 算数授業研究, Vol.137, pp.58-59 (2021). [isbn:9784491046365]
class QPR
def initialize(hash = {})
@points = hash[:p] || [[1, 0], [2, 1], [1, 2], [0, 1]]
@draw_param = hash[:d] || {}
end
def start
@num_parallel = 0
@num_vertical = 0
# 平行の数
[[0, 1, 2, 3],
[1, 2, 3, 0]].each do |a|
p1, p2, p3, p4 = a.map { |e| @points[e] }
if is_parallel(p1, p2, p3, p4)
puts "#{p_s(p1)}-#{p_s(p2)}, #{p_s(p3)}-#{p_s(p4)}: parallel"
@num_parallel += 1
end
end
# 垂直の数
[[0, 1, 1, 2],
[1, 2, 2, 3],
[2, 3, 3, 0],
[3, 0, 0, 1],
[0, 1, 2, 3],
[1, 2, 3, 0]].each do |a|
p1, p2, p3, p4 = a.map { |e| @points[e] }
if is_vertical(p1, p2, p3, p4)
puts "#{p_s(p1)}-#{p_s(p2)}, #{p_s(p3)}-#{p_s(p4)}: right angle"
@num_vertical += 1
end
end
self
end
def p_s(xy)
"(" + xy.join(",") + ")"
end
def is_zero(n)
case n
when Float
eps = 1.0e-6
return -eps < n && n < eps
when Numeric
return n.zero?
else
raise
end
end
def is_parallel(p1, p2, p3, p4)
d1 = [p1[0] - p2[0], p1[1] - p2[1]]
d2 = [p3[0] - p4[0], p3[1] - p4[1]]
is_zero(d1[0] * d2[1] - d1[1] * d2[0])
end
def is_vertical(p1, p2, p3, p4)
d1 = [p1[0] - p2[0], p1[1] - p2[1]]
d2 = [p3[0] - p4[0], p3[1] - p4[1]]
is_zero(d1[0] * d2[0] + d1[1] * d2[1])
end
def draw
@image_filename = @draw_param[:filename] || "rect.png"
@margin = @draw_param[:margin] || 10
@side = @draw_param[:side] || 40
@linewidth = @draw_param[:linewidth] || 2
@dotradius = @draw_param[:dotradius] || 3
@linecolor = @draw_param[:linecolor] || "#6666ff"
@bgcolor = @draw_param[:bgcolor] || "#f0fff0"
@axiscolor = @draw_param[:axiscolor] || "#666666"
@axiswidth = @draw_param[:axiswidth] || @linewidth * 0.5
@x_min = @draw_param[:xmin] || @points.map { |p| p[0] }.min
@x_max = @draw_param[:xmax] || @points.map { |p| p[0] }.max
@y_min = @draw_param[:ymin] || @points.map { |p| p[1] }.min
@y_max = @draw_param[:ymax] || @points.map { |p| p[1] }.max
@quality = @draw_param[:quality] || ""
if Numeric === @quality
@quality = "-quality #{@quality}"
end
@print_command = @draw_param[:print]
@flip = @draw_param[:flip] # 真のとき上下反転(yの値が大きければ上に表示)
@x_trans = [-@x_min, 0].max
@y_trans = [-@y_min, 0].max
image_width = @margin * 2 + (@x_max - @x_min) * @side
image_height = @margin * 2 + (@y_max - @y_min) * @side
# 画像を新規作成
@command = "convert -size #{image_width}x#{image_height} \'xc:#{@bgcolor}\'"
# 縦目盛り
@command += " -fill none -stroke \'#{@axiscolor}\' -strokewidth \'#{@axiswidth}\' -draw \""
@x_min.upto(@x_max) do |x|
@command += " line #{xy_image_s(x, @y_min)} #{xy_image_s(x, @y_max)}"
end
@command += "\""
# 横目盛り
@command += " -draw \""
@y_min.upto(@y_max) do |y|
@command += " line #{xy_image_s(@x_min, y)} #{xy_image_s(@x_max, y)}"
end
@command += "\""
# 四角形の頂点
if @dotradius > 0
@command += " -fill \'#{@linecolor}\' -stroke none -draw \""
@points.each do |x, y|
px, py = xy_image(x, y)
s1 = [px, py].join(',')
s2 = [px + @dotradius, py].join(',')
@command += " circle #{s1} #{s2}"
end
@command += "\""
end
# 四角形の辺
@command += " -fill none -stroke \'#{@linecolor}\' -strokewidth \'#{@linewidth}\' -draw \"polygon #{@points.map { |xy| xy_image_s(*xy) }.join(' ')}\""
# ファイル保存
@command += " #{@quality} #{@image_filename}"
puts @command if @print_command
system @command
end
def xy_image(x, y)
px = @margin + (@x_trans + x) * @side
py = @margin + (@flip ? @y_max - y : @y_trans + y) * @side
[px, py]
end
def xy_image_s(x, y)
xy_image(x, y).join(",")
end
end
if __FILE__ == $0
draw_param_base = {
:xmin => 0, :xmax => 3, :ymin => 0, :ymax => 3,
:quality => 92,
:flip => true,
# :print => true,
}
points_a = [
[[0, 0], [3, 0], [3, 3], [0, 3]], # q1.png 正方形
[[0, 0], [2, 0], [2, 3], [0, 3]], # q2.png 長方形
[[1, 1], [2, 2], [1, 3], [0, 2]], # q3.png ダイヤモンド型(正方形)
[[0, 0], [1, 0], [3, 3], [2, 3]], # q4.png 平行四辺形
[[0, 0], [2, 1], [1, 3], [0, 3]], # q5.png 0平行2垂直
[[0, 0], [2, 2], [1, 3], [0, 3]], # q6.png 0平行2垂直
[[1, 0], [2, 3], [1, 2], [0, 3]], # q7.png 0平行1垂直(270°)
[[0, 1], [3, 1], [2, 2], [1, 2]], # q8.png 1平行1垂直
[[0, 2], [3, 2], [2, 1], [2, 0]], # q9.png 0平行2垂直
]
points_a.each_with_index do |points, i|
filename = "q#{i + 1}.png"
draw_param = draw_param_base.dup
draw_param[:filename] = filename
puts "==== shape No.#{i + 1} (#{filename}) ===="
QPR.new(p: points, d: draw_param).start.draw
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment