Created
February 25, 2015 20:57
-
-
Save takehiko/e127e6f536fd7c24765e to your computer and use it in GitHub Desktop.
三角形三角形問題の描画
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 | |
# triangle-triangle.rb | |
# 三角形三角形問題の描画 | |
# by takehikom | |
# 要ImageMagick (convertコマンド), フォント (IPAexゴシック) | |
# ruby triangle-triangle.rb | |
# env FONT=/full/path/of/ipaexg.ttf ruby triangle-triangle.rb | |
# ruby triangle-triangle.rb test | |
module TriangleTriangle | |
class Point | |
def initialize(x_ = 0.0, y_ = 0.0, label_ = nil) | |
@x = x_.to_f | |
@y = y_.to_f | |
@label = label_ | |
end | |
attr_accessor :x, :y, :label | |
def to_s | |
s = "" | |
s += "[#{label}] " if @label | |
s += "(#{@x},#{@y})" | |
s | |
end | |
def internal_division(p2, r1, r2, label_ = nil) | |
# http://manapedia.jp/text/293 | |
raise if (r1 + r2).zero? | |
p = Point.new(0, 0, label_) | |
p.x = (r2 * self.x + r1 * p2.x) / (r1 + r2) | |
p.y = (r2 * self.y + r1 * p2.y) / (r1 + r2) | |
p | |
end | |
def external_division(p2, r1, r2, label_ = nil) | |
# http://manapedia.jp/text/293?page=2 | |
raise if (r1 - r2).zero? | |
p = Point.new(0, 0, label_) | |
p.x = (-r2 * self.x + r1 * p2.x) / (r1 - r2) | |
p.y = (-r2 * self.y + r1 * p2.y) / (r1 - r2) | |
p | |
end | |
end | |
class Plane | |
def self.internal_division(p1, p2, r1, r2, label_ = nil) | |
p1.internal_division(p2, r1, r2, label_) | |
end | |
def self.external_division(p1, p2, r1, r2, label_ = nil) | |
p1.external_division(p2, r1, r2, label_) | |
end | |
def length(p1, p2) | |
d_x = p1.x - p2.x | |
d_y = p1.y - p2.y | |
Math.sqrt(d_x * d_x + d_y * d_y) | |
end | |
def self.intersection(p1, p2, q1, q2, label_ = nil) | |
a = p2.y - p1.y # (p1, p2) => a * x + b * y = c | |
b = p1.x - p2.x | |
c = p1.x * p2.y - p2.x * p1.y | |
d = q2.y - q1.y # (p1, p2) => d * x + e * y = f | |
e = q1.x - q2.x | |
f = q1.x * q2.y - q2.x * q1.y | |
puts "l1: #{a} x + #{b}y = #{c}" if $debug | |
puts "l2: #{d} x + #{e}y = #{f}" if $debug | |
raise if (a.zero? && b.zero?) || (d.zero? && e.zero?) | |
det = a * e - b * d | |
raise if det.zero? | |
x = (c * e - b * f) / det | |
y = (a * f - c * d) / det | |
Point.new(x, y, label_) | |
end | |
def self.area(p1, p2, p3) | |
# http://www004.upp.so-net.ne.jp/s_honma/heron/heron.htm | |
p4 = Point.new(p1.x - p3.x, p1.y - p3.y) | |
p5 = Point.new(p2.x - p3.x, p2.y - p3.y) | |
(p4.x * p5.y - p4.y * p5.x).abs * 0.5 | |
end | |
def self.transform(m, *points) | |
points.map do |p| | |
Point.new(m[0] * p.x + m[1] * p.y + m[2], | |
m[3] * p.x + m[4] * p.y + m[5], | |
p.label) | |
end | |
end | |
end | |
class Drawer | |
def initialize(tt_, param = Hash.new) | |
@tt_orig = tt_ | |
@m = param[:matrix] || [1, 0, 0, 0, 1, 0] | |
@margin = param[:margin] || 30 | |
@linewidth = param[:linewidth] || 2 | |
@extra = param[:extra] || "" | |
@fontname = param[:fontname] || ENV["FONT"] || "ipaexg.ttf" | |
@fontsize = param[:fontsize] || 24 | |
@filename = param[:filename] || "triangle-triangle.png" | |
if param[:bestfit] | |
@tt = @tt_orig.transform2(param[:width] || 300, | |
param[:height] || 200, | |
param[:margin] || 30, | |
param[:flap].nil? ? true : param[:flap]) | |
else | |
@tt = @tt_orig.transform(@m) | |
end | |
@command = "" | |
end | |
attr_reader :tt, :tt_orig, :margin, :linewidth, :filename | |
attr_reader :fontname, :fontsize, :extra | |
attr_reader :command | |
def to_s; @command; end | |
def set_command | |
width = ([@tt.a.x, @tt.b.x, @tt.c.x].max + @margin).to_i | |
height = ([@tt.a.y, @tt.b.y, @tt.c.y].max + @margin).to_i | |
@command = "convert -size #{width}x#{height} xc:white" | |
add_auxline2 if @extra.index("line2") | |
if @extra.index("line1") | |
if @extra.index("line1") == @extra.index("line1ae") | |
add_auxline1ae | |
elsif @extra.index("line1+ae") | |
add_auxline1 | |
add_auxline1ae | |
else | |
add_auxline1 | |
end | |
end | |
add_label_abcdef if @extra.index("abcdef") | |
add_label_ghi if @extra.index("ghi") | |
add_s213 if @extra.index("s213") | |
if @extra.index("sabc") | |
if @extra.index("sabcabc") | |
add_sabcabc | |
else | |
add_sabc | |
end | |
end | |
add_ratio if @extra.index("ratio") | |
add_triangle_triangle | |
@command += " -quality 95 #{@filename}" | |
self | |
end | |
alias :start :set_command | |
def insert_command(c) | |
@command[" -quality"] = c + " -quality" | |
end | |
def add_triangle_triangle | |
@command += " -stroke black -strokewidth #{@linewidth} -fill none" | |
@command += " -draw \"" | |
@command += "line #{@tt.a.x},#{@tt.a.y},#{@tt.b.x},#{@tt.b.y}" | |
@command += " line #{@tt.b.x},#{@tt.b.y},#{@tt.c.x},#{@tt.c.y}" | |
@command += " line #{@tt.c.x},#{@tt.c.y},#{@tt.a.x},#{@tt.a.y}" | |
@command += " line #{@tt.a.x},#{@tt.a.y},#{@tt.f.x},#{@tt.f.y}" | |
@command += " line #{@tt.b.x},#{@tt.b.y},#{@tt.d.x},#{@tt.d.y}" | |
@command += " line #{@tt.c.x},#{@tt.c.y},#{@tt.e.x},#{@tt.e.y}" | |
@command += "\"" | |
end | |
def add_auxline1 | |
@command += " -stroke blue -strokewidth #{@linewidth} -fill none" | |
@command += " -draw \"" | |
@command += "line #{@tt.a.x},#{@tt.a.y},#{@tt.e.x},#{@tt.e.y}" | |
@command += " line #{@tt.b.x},#{@tt.b.y},#{@tt.f.x},#{@tt.f.y}" | |
@command += " line #{@tt.c.x},#{@tt.c.y},#{@tt.d.x},#{@tt.d.y}" | |
@command += "\"" | |
end | |
def add_auxline2 | |
@command += " -stroke maroon -strokewidth #{@linewidth} -fill none" | |
@command += " -draw \"" | |
@command += "line #{@tt.f.x},#{@tt.f.y},#{@tt.g.x},#{@tt.g.y}" | |
@command += " line #{@tt.d.x},#{@tt.d.y},#{@tt.h.x},#{@tt.h.y}" | |
@command += " line #{@tt.e.x},#{@tt.e.y},#{@tt.i.x},#{@tt.i.y}" | |
@command += "\"" | |
end | |
def add_auxline1ae | |
@command += " -stroke black -strokewidth #{@linewidth} -fill none" | |
@command += " -draw \"" | |
@command += "line #{@tt.a.x},#{@tt.a.y},#{@tt.e.x},#{@tt.e.y}" | |
@command += "\"" | |
end | |
# 以下のuの第2・第3引数の値は,デフォルト設定(IPAexゴシック, 24pt) | |
# に依存しているため,他のフォントやサイズにすると適切に | |
# 表示しない可能性あり | |
def add_label_abcdef | |
@command += " -font #{@fontname} -pointsize #{@fontsize} -fill black -stroke none" | |
@command += " -draw \"" | |
@command += "text #{u('a', -18, +1)} \'A\'" | |
@command += " text #{u('b', -17, -1)} \'B\'" | |
@command += " text #{u('c', 1, -1)} \'C\'" | |
@command += " text #{u('d', 4, +4)} \'D\'" | |
@command += " text #{u('e', -11, -4)} \'E\'" | |
@command += " text #{u('f', -8, +24)} \'F\'" | |
@command += "\"" | |
end | |
def add_label_ghi | |
@command += " -font #{@fontname} -pointsize #{@fontsize} -fill black -stroke none" | |
@command += " -draw \"" | |
@command += "text #{u('g', -9, +22)} \'G\'" | |
@command += " text #{u('h', 3, +1)} \'H\'" | |
@command += " text #{u('i', -12, -1)} \'I\'" | |
@command += "\"" | |
# tt3.insert_command(" -font #{font} -pointsize 24 -fill black -stroke none -draw \"text #{tt3.u('g', -9, +22)} \'G\' text #{tt3.u('h', 3, +1)} \'H\' text #{tt3.u('i', -12, -1)} \'I\'\"") | |
end | |
def add_s213 | |
@command += " -font #{@fontname} -pointsize #{@fontsize * 0.75} -fill green4 -stroke none" | |
@command += " -draw \"" | |
@command += "text #{u('def', -5, +10)} \'s\'" | |
@command += " text #{u('ade', -8, +10)} \'2s\'" | |
@command += " text #{u('bef', -8, +5)} \'s\'" | |
@command += " text #{u('cfd', -8, +10)} \'3s\'" | |
@command += " text #{u('eab', -6, +6)} \'2s\'" | |
@command += " text #{u('fbc', -5, +6)} \'3s\'" | |
@command += " text #{u('dca', -16, +8)} \'6s\'" | |
@command += "\"" | |
end | |
def add_sabc | |
@command += " -font #{@fontname} -pointsize #{@fontsize * 0.75} -fill green4 -stroke none" | |
@command += " -draw \"" | |
@command += "text #{u('def', -5, +10)} \'s\'" | |
@command += " text #{u('ade', -8, +10)} \'as\'" | |
@command += " text #{u('bef', -8, +5)} \'bs\'" | |
@command += " text #{u('cfd', -8, +10)} \'cs\'" | |
@command += "\"" | |
end | |
def add_sabcabc | |
@command += " -font #{@fontname} -pointsize #{@fontsize * 0.75} -fill green4 -stroke none" | |
@command += " -draw \"" | |
@command += "text #{u('def', -5, +10)} \'s\'" | |
@command += " text #{u('ade', -8, +10)} \'as\'" | |
@command += " text #{u('bef', -8, +5)} \'bs\'" | |
@command += " text #{u('cfd', -8, +10)} \'cs\'" | |
@command += " text #{u('eab', -15, +13)} \'abs\'" | |
@command += " text #{u('fbc', -5, +6)} \'bcs\'" | |
@command += " text #{u('dca', -16, +8)} \'cas\'" | |
@command += "\"" | |
end | |
def add_ratio | |
@command += " -font #{@fontname} -pointsize #{@fontsize * 0.75} -stroke none" | |
@command += " -fill chocolate" | |
@command += " -draw \"text #{u('ai', -33, +0)} \'a+1\' text #{u('bi', -12, -1)} \'b\'\"" | |
@command += " -fill goldenrod" | |
@command += " -draw \"text #{u('bg', -14, +20)} \'b+1\' text #{u('cg', -8, +15)} \'c\'\"" | |
@command += " -fill IndianRed" | |
@command += " -draw \"text #{u('ch', -2, -5)} \'c+1\' text #{u('ah', +5, +2)} \'a\'\"" | |
end | |
def u(points, del_x = 0, del_y = 0, flag_array = false) | |
# pointsは1つ以上の点の文字列 | |
# 1点ならその座標,2点なら中点,3点以上なら重心について | |
# del_x,del_yの補正を加え,convertコマンドの座標となる | |
# 文字列を返す | |
x = y = 0 | |
points.downcase.each_char do |c| | |
x += @tt.send(c.to_sym).x | |
y += @tt.send(c.to_sym).y | |
end | |
x = x / points.length + del_x | |
y = y / points.length + del_y | |
flag_array ? [x, y] : "#{x},#{y}" | |
end | |
alias :point :u | |
alias :center :u | |
def exec_command(opt_print = true) | |
puts @command if opt_print | |
system @command | |
end | |
end | |
class Calculator | |
def initialize(h = Hash.new) | |
@a = h["A"] | |
@b = h["B"] | |
@c = h["C"] | |
@ad_df = h["AD_DF"].to_f | |
@be_ed = h["BE_ED"].to_f | |
@cf_fe = h["CF_FE"].to_f | |
puts "AD:DF=#{@ad_df}:1", "BE:ED=#{@be_ed}:1", "CF:FE=#{@cf_fe}:1" | |
raise if @ad_df.zero? || @be_ed.zero? || @cf_fe.zero? | |
raise if @ad_df.nan? || @be_ed.nan? || @cf_fe.nan? | |
end | |
attr_accessor :a, :b, :c, :ad_df, :be_ed, :cf_fe | |
attr_reader :d, :e, :f, :g, :h, :i | |
def print_result | |
puts @a, @b, @c | |
puts "AD:DF=#{@ad_df}:1", "BE:ED=#{@be_ed}:1", "CF:FE=#{@cf_fe}:1" | |
puts @d, @e, @f | |
puts @g, @h, @i | |
d_ext = Plane.external_division(@b, @e, @be_ed + 1, 1, "Dext") | |
e_ext = Plane.external_division(@c, @f, @cf_fe + 1, 1, "Eext") | |
f_ext = Plane.external_division(@a, @d, @ad_df + 1, 1, "Fext") | |
puts d_ext, e_ext, f_ext | |
area_whole = Plane.area(@a, @b, @c) | |
area_min = Plane.area(@d, @e, @f) | |
puts "ABC = #{area_whole}", "DEF = #{area_min}", "ABC/DEF = #{area_whole / area_min}" | |
end | |
def transform(m = nil) | |
tt = self.dup | |
return tt if m.nil? | |
tt.a, tt.b, tt.c = Plane.transform(m, @a, @b, @c) | |
tt.start | |
end | |
def transform2(width, height, margin, flag_flip) | |
# (0,0)-(width+margin*2, height+margin*2)に描画できるよう変換 | |
left = [@a.x, @b.x, @c.x].min | |
right = [@a.x, @b.x, @c.x].max | |
top = [@a.y, @b.y, @c.y].max | |
bottom = [@a.y, @b.y, @c.y].min | |
raise if (right - left).zero? || (bottom - top).zero? | |
a2, b2, c2 = Plane.transform([1, 0, -left, 0, 1, flag_flip ? -top : -bottom], @a, @b, @c) | |
a3, b3, c3 = Plane.transform([width / (right - left), 0, 0, 0, height / (top - bottom) * (flag_flip ? -1 : 1), 0], a2, b2, c2) | |
a4, b4, c4 = Plane.transform([1, 0, margin, 0, 1, margin], a3, b3, c3) | |
tt = self.dup | |
tt.a, tt.b, tt.c = a4, b4, c4 | |
tt.start | |
end | |
def find_point | |
@g = Plane.internal_division(@b, @c, @be_ed + 1, @cf_fe, "G") | |
@h = Plane.internal_division(@c, @a, @cf_fe + 1, @ad_df, "H") | |
@i = Plane.internal_division(@a, @b, @ad_df + 1, @be_ed, "I") | |
@d = Plane.intersection(@a, @g, @b, @h, "D") | |
@e = Plane.intersection(@b, @h, @c, @i, "E") | |
@f = Plane.intersection(@c, @i, @a, @g, "F") | |
self | |
end | |
alias :start :find_point | |
end | |
end | |
if __FILE__ == $0 | |
if ARGV.first == "test" | |
include TriangleTriangle | |
puts "internal division" | |
p1 = Point.new(0, 0) | |
p2 = Point.new(3, 0.3) | |
r1 = 1.0 | |
r2 = 2.0 | |
d = Plane.internal_division(p1, p2, r1, r2) | |
puts "#{p1} #{r1}:#{r2} #{p2} => #{d}" | |
puts "external division" | |
p1 = Point.new(0, 0) | |
p2 = Point.new(3, 0.6) | |
r1 = 3.0 | |
r2 = 1.0 | |
d = Plane.external_division(p1, p2, r1, r2) | |
puts "#{p1} #{r1}:#{r2} #{p2} => #{d}" | |
puts "intersection" | |
p1 = Point.new(0, 0) | |
p2 = Point.new(5, 3) | |
q1 = Point.new(0, 2) | |
q2 = Point.new(2, 0) | |
c = Plane.intersection(p1, p2, q1, q2) | |
puts "#{p1}-#{p2}, #{q1}-#{q2} => #{c}" | |
exit | |
end | |
include TriangleTriangle | |
h = { | |
"A" => Point.new(2.3, 4, "A"), | |
"B" => Point.new(0, 0, "B"), | |
"C" => Point.new(5, 0, "C"), | |
"AD_DF" => 2.0, | |
"BE_ED" => 1.0, | |
"CF_FE" => 3.0 | |
} | |
tt1 = Calculator.new(h).start | |
tt1.print_result | |
d1 = Drawer.new(tt1, :bestfit => true, :filename => "tritri1.png", | |
:extra => "abcdef").start | |
d1.tt.print_result | |
d1.exec_command | |
d2 = Drawer.new(tt1, :bestfit => true, :filename => "tritri2.png", | |
:extra => "abcdef line1 s213").start | |
d2.tt.print_result | |
d2.exec_command | |
d3 = Drawer.new(tt1, :bestfit => true, :filename => "tritri3.png", | |
:extra => "abcdef line1").start | |
d3.tt.print_result | |
d3.exec_command | |
d4 = Drawer.new(tt1, :bestfit => true, :filename => "tritri4.png", | |
:extra => "abcdef line1 sabc").start | |
d4.tt.print_result | |
d4.exec_command | |
d5 = Drawer.new(tt1, :bestfit => true, :filename => "tritri5.png", | |
:extra => "abcdef line1 sabcabc").start | |
d5.tt.print_result | |
d5.exec_command | |
d6 = Drawer.new(tt1, :bestfit => true, :filename => "tritri6.png", | |
:extra => "abcdef ghi line1 line2").start | |
d6.tt.print_result | |
d6.exec_command | |
d7 = Drawer.new(tt1, :bestfit => true, :filename => "tritri7.png", | |
:extra => "abcdef ghi line1 line2 sabc ratio").start | |
d7.tt.print_result | |
d7.exec_command | |
d8 = Drawer.new(tt1, :bestfit => true, :filename => "tritri1ae.png", | |
:extra => "abcdef line1ae").start | |
d8.tt.print_result | |
d8.exec_command | |
d9 = Drawer.new(tt1, :bestfit => true, :filename => "tritri2ae.png", | |
:extra => "abcdef line1 s213 line1+ae").start | |
d9.tt.print_result | |
d9.exec_command | |
d = Drawer.new(tt1, :bestfit => true, :filename => "tritri_test.png", | |
:extra => "abcdef ghi line1 line2 sabcabc ratio").start | |
d.tt.print_result | |
d.exec_command | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment