# CSE341 Fall 2011, Homework 7, Provided Code (see also SML code) # a little language for 2D geometry objects # each kind of expression is a subclass of GeometryExpression with methods: # * eval_prog for the interpreter (environment representation is up to you # but an array of 2-element arrays is expected: get shadowing right) # method assumes all subpexpressions are result of preprocessing # * preprocess_prog for creating a smilar expression with: # * no repeated points # * line segments are not actually points (endpoints not real close) # * lines segment have left (or, if vertical, bottom) coordinate first # geometry /values/ (result of evaluating a geometry expression) also need: # * several methods for implementing intersection via double dispatch # * a shift method for implementing shift operations # Note: points are represented as 2-element arrays: [x-coord,y-coord] # Note: geometry objects should be immutable: assign to fields only during # object construction class GeometryExpression public # subclasses override these two methods as appropriate: use these sparingly # as described in assignment. This is slightly more OO style than using is_a? def isLine false end def isVerticalLine false end private # some helper methods useful in multiple subclasses def real_close(r1,r2) (r1 - r2).abs < 0.00001 end def real_close_point(p1,p2) real_close(p1[0],p2[0]) && real_close(p1[1],p2[1]) end # careful: two_points_to_line could return a Line or a VerticalLine def two_points_to_line(x1,y1,x2,y2) if real_close(x1,x2) then VerticalLine.new x1 else m = (y2-y1) / (x2-x1) b = y1 - m * x1 Line.new(m,b) end end def point_on_segment(p, x1, y1, x2, y2) is_inbetween(p[0], x1, x2) && is_inbetween(p[1], y1, y2) end def is_inbetween(v,end1,end2) ((end1 - 0.00001 <= v && v <= end2 + 0.00001) || (end2 - 0.00001 <= v && v <= end1 + 0.00001)) end end # a set of points (a geometry value) represented via an array of points class PointSet < GeometryExpression attr_reader :points def initialize arr @points = arr end def eval_prog env self end def preprocess_prog new_arr = [] @points.each_index {|i| if @points.last(@points.length - (i + 1)).any? {|x| real_close_point(@points[i],x) } false else new_arr.push @points[i] end } PointSet.new new_arr end def shift(dx,dy) PointSet.new(@points.map {|pt| [pt[0] + dx, pt[1] + dy]}) end def intersect other other.intersectPointSet self end # COMPLETE THE BODY OF THE NEXT FOUR METHODS def intersectPointSet ps raise "for students to implement" end def intersectLine line raise "for students to implement" end def intersectVerticalLine vline raise "for students to implement" end def intersectLineSegment seg raise "for students to implement" end end # a line in the 2D plane (a geometry value) vertical lines a different class # represented via slope and intercept (floating point numbers) class Line < GeometryExpression attr_reader :m, :b def initialize(m,b) @m = m @b = b end def isLine true end def eval_prog env self end def preprocess_prog self end def shift(dx,dy) Line.new(@m, @m*(-dx) + @b + dy) end def intersect other other.intersectLine self end # COMPLETE THE BODY OF THE NEXT FOUR METHODS def intersectPointSet ps raise "for students to implement" end def intersectLine line raise "for students to implement" end def intersectVerticalLine vline raise "for students to implement" end def intersectLineSegment seg raise "for students to implement" end end # COMPLETE THE IMPLEMENTATION BY ADDING SIX MORE SUBCLASSES OF GeometryExpression # (seven if you do the challenge problem on adding circles)