Created
November 29, 2021 10:30
-
-
Save takehiko/c5c5e3c1a6a14f51478afea842ffb86e to your computer and use it in GitHub Desktop.
Yet another quadrilateral analyzer & drawer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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