fork download
  1. require 'RMagick'
  2. @top_intersects = []
  3. @all_intersects = {}
  4. @just_ints = []
  5. @lines = [] #make a list of [[x1,y1],[x2,y2]], one for each line
  6. def eq_maker(line)
  7. slope = (line[1][1] - line[0][1]) / (line[1][0] - line[0][0])
  8. y_intercept = line[0][1] - (slope * line[0][0])
  9. return [slope, y_intercept]
  10. end
  11. def intersection_finder(line1,line2) #finds the intersection point when given four points -- makes 2 lines, solves for x=y
  12. line1 += eq_maker(line1)
  13. line2 += eq_maker(line2)
  14. if line1[2] == line2[2]
  15. return [line1,line2].sort_by {|x| x[1][0]}[0][1] if line1[3] == line2[3] #ultra edge case: if they're the same line, return the endpoint with the minimum x
  16. return [-10.0,-10.0] #less edge, but still exceptionally unlikely: if they have the same slope, return a range that doesn't exist in our board
  17. end
  18. x_int = (line2[3] - line1[3]) / (line1[2] - line2[2]) #otherwise, return the intersection point (z2 - z1) /(m1 - m2)
  19. y_int = (line1[2] * x_int) + line1[3]
  20. return [x_int, y_int]
  21. end
  22. def on_both?(line1,line2,int) #.round(3) is necessary because floats suck dick
  23. [line1,line2].each do |x|
  24. return false unless int[0].round(3) >= x[0][0].round(3) && int[0].round(3) <= x[1][0].round(3)
  25. y_range = [x[1][1].round(3),x[0][1].round(3)].sort
  26. return false unless int[1].round(3) >= y_range[0] && int[1].round(3) <= y_range[1]
  27. end
  28. return true
  29. end
  30. def intersects(current_line) #todo: make floats not suck, and make sure it's selecting the right line (check to see if the intercept coordinates fall both on the current line and the new line length, not extended lengths
  31. @all_intersects[current_line] = []
  32. possibilities = @lines.select {|x| @all_intersects[x].nil?} #check for intersections against all other lines that haven't already been checked for intersections (or self)
  33. possibilities.each do |x|
  34. int = intersection_finder(current_line,x)
  35. if on_both?(current_line,x,int)
  36. @just_ints.push int
  37. @all_intersects[int] = [current_line,x] #every intersection has exactly 2 lines -- avoids some float problems
  38. end
  39. end
  40. return true
  41. end
  42. def area_totaler(point1,point2) #totals area under any straight_line segment from x axis -- rectangle + triangle
  43. x_range = [point1[0],point2[0]].sort
  44. y_range = [point1[1],point2[1]].sort
  45. width = x_range[1] - x_range[0]
  46. rect_height = y_range[0]
  47. triangle_height = (y_range[1] - y_range[0])
  48. return (rect_height * width) + (0.5 * width * triangle_height) #rect area + triangle area
  49. end
  50.  
  51. #format: [center-pos,base-length,height]
  52. triangles = []
  53. 5.times do #make random triangles
  54. triangles.push [(rand(5000) + 2501) / 10.0 , (rand(5000) + 1) / 10.0 , (rand(5000) + 1001) / 10.0]
  55. end
  56. colors = ['red','blue','green','purple','orange','yellow','brown','cyan','magenta']
  57. imgl = Magick::ImageList.new
  58. imgl.new_image(1000, 600)
  59. gc = Magick::Draw.new
  60.  
  61. triangles.each do |x|#draw triangles
  62. gc.fill(colors.sample(1)[0])
  63. gc.fill_opacity(0.5)
  64. coords = [(x[0] - (x[1] / 2)),0, x[0],x[2], (x[0] + (x[1] / 2)),0]
  65. @lines.push [[coords[0],coords[1]],[coords[2],coords[3]]]
  66. @lines.push [[coords[2],coords[3]],[coords[4],coords[5]]]
  67. eval "gc.polygon(#{coords.join(',')})"
  68. end
  69.  
  70. @lines.sort_by! {|x| x[0][0]} #start at left -- first line = highest line
  71. @lines.each { |x| intersects(x) } #build all real intersections
  72. @just_ints.sort_by! {|x| x[0]} #sort intercepts by x, move from left to right
  73. @top_intersects.push @lines[0][0] #first point on the line is the leftmost point
  74. last_point = @lines.sort_by {|x| x[1][0]}.last[1]
  75. current_line = @lines[0] #start with leftmost line
  76.  
  77. @just_ints.each do |x| #iterate through intercepts, finding the top line
  78. if current_line[1][0] < x[0] && current_line[1][1].to_f == 0.0 #if the x of the next intercept is beyond the x of the end of our line, triangle hits the x axis, so add line endpoint and get the next line
  79. @top_intersects.push current_line[1]
  80. current_line = @lines[@lines.index {|z| z[0][0] > current_line[1][0]}]
  81. @top_intersects.push current_line[0]
  82. end
  83.  
  84. if @all_intersects[x].include?(current_line) #if the next intersect includes our line, switch lines, push intersect to top_intersects
  85. @top_intersects.push x
  86. current_line = (@all_intersects[x] - [current_line])[0]
  87. end
  88. #otherwise, if the int isn't on our line, do nothing (except draw a circle).
  89.  
  90. # Identify the intersections with gray circles
  91. gc.stroke('gray50')
  92. gc.stroke_width(1)
  93. gc.fill_opacity(0)
  94. coords = "gc.circle(#{x.join(',')},#{x.map {|y| y + 3}.join(',')})"
  95. eval coords
  96. end
  97.  
  98. @top_intersects.push last_point #add the last point after all intercepts =)
  99.  
  100. area = 0
  101. (@top_intersects.length - 1).times do |x| #count the total area under all sections
  102. area += area_totaler(@top_intersects[x],@top_intersects[x + 1])
  103. end
  104. puts "Computed area is: #{area}"
  105.  
  106. sum = 0
  107. triangles.each do |x| #sum all the triangles, for comparison
  108. sum += (x[1] * x[2] * 0.5)
  109. end
  110. puts "Area of all triangles, including overlap, is: #{sum}"
  111.  
  112.  
  113. gc.stroke('black')
  114. gc.stroke_width(3)
  115. eval "gc.polygon(#{@top_intersects.flatten.join(',')})" #draw a polygon which is @top_intersects.flatten -- trace tops of triangles
  116.  
  117. gc.draw(imgl) #draw image
  118. imgl.flip! #flip it; we draw upside down =)
  119. imgl.write("polygon.gif")
Runtime error #stdin #stdout 0.04s 5968KB
stdin
Standard input is empty
stdout
Standard output is empty