Topic: Parsing Expressions, Region Module Date: Nov. 4, 2009 Number: 20 Examples: Expressions.hs, Region.hs Reading: Chapter 8 -- Finishing Parsing: Expression Parser Finish the Expression Parse example included in last lecture's notes. -- Regions Builds up a mechanism by which we can test point containment in not only our shapes, but in regions created by the union, intersection, and complement of shapes, which may be translated or scaled! Would be easy to add "rotated". In fact, we will add "rotated by 90 degrees" as part of writing Tetris. After typical header (exports whole shape module!), has: infixr 5 `Union` infixr 6 `Intersect` Says that these operators associate right and Intersect has higher precedence than Union. Why capitalized? They are constructors: -- A Region is either: data Region = Shape Shape -- primitive shape | Translate Vector Region -- translated region | Scale Vector Region -- scaled region | Complement Region -- inverse of region | Region `Union` Region -- union of regions | Region `Intersect` Region -- intersection of regions | Region `Xor` Region -- XOR of regions | Empty -- empty region deriving Show type Vector = (Float,Float) Gives some examples of constructing regions: oneCircle = Shape (Ellipse 1 1) manyCircles = [ Translate (x,0) oneCircle | x <- [0,2..] ] fiveCircles = foldr Union Empty (take 5 manyCircles) Create infinite list of circles, take the first 5. r1 `difference` r2 = r1 `Intersect` (Complement r2) Will want to use these to determine if a Point - wait, that is used by the graphics library, so we will call it a Coordinate: type Coordinate = (Float,Float) is in a region. Easy thing is for shapes: containsS :: Shape -> Coordinate -> Bool (Rectangle s1 s2) `containsS` (x,y) = let t1 = s1/2; t2 = s2/2 in (-t1<=x) && (x<=t1) && (-t2<=y) && (y<=t2) (Ellipse r1 r2) `containsS` (x,y) = (x/r1)^2 + (y/r2)^2 <= 1 (Polygon pts) `containsS` p = let leftOfList = map (isLeftOf p) (zip pts (tail pts ++ [head pts])) in and leftOfList (RtTriangle s1 s2) `containsS` p = (Polygon [(0,0),(s1,0),(0,s2)]) `containsS` p Only complicated one is Polygon. Assumes CCW oriented convex polygon. Used isLeftOf test, walks around ALL edges (including from last back to first) and tests. If left of all, inside. This test below is taking the signed area of an oriented triangle. It can be expressed as the determinant of a 3x3 matrix. As computational geometer I would love to explain it to you. But not in class. isLeftOf :: Coordinate -> Ray -> Bool (px,py) `isLeftOf` ((ax,ay),(bx,by)) = let (s,t) = (px-ax, py-ay) (u,v) = (px-bx, py-by) in s*v >= t*u type Ray = (Coordinate, Coordinate) Alternate idea? If point lies on an edge, call it inside (isLeftOf returns true for "on", when the two are equal). Otherwise, shoot a ray horizontal to infinity and count the number of segments it intersects. If odd, inside. If even, outside. (Have to be careful when go through endpoints. One solution is to count upper endpoints as intersecting, lower endpoints and horizontal lines as not intersecting. Equivalent to moving the ray down infintesimally.) But how do we determine if a point is in a complex region? Build up the parts. containsR :: Region -> Coordinate -> Bool (Shape s) `containsR` p = s `containsS` p (Translate (u,v) r) `containsR` (x,y) = let p = (x-u,y-v) in r `containsR` p Translation takes (x,y) to (x+u,y+v). So to test point inclusion, invert the function! Instead of moving shape to point, move point to shape. (Scale (u,v) r) `containsR` (x,y) = let p = (x/u,y/v) in r `containsR` p Scale moves (x,y) to (x*u, y*v). So invert function to see if point is in original. (Complement r) `containsR` p = not (r `containsR` p) (r1 `Union` r2) `containsR` p = r1 `containsR` p || r2 `containsR` p (r1 `Intersect` r2) `containsR` p = r1 `containsR` p && r2 `containsR` p (r1 `Xor` r2) `containsR` p = let a = r1 `containsR` p b = r2 `containsR` p in (a || b) && not (a && b) Empty `containsR` p = False These are all boolean algebra. Book gives five axioms of boolean algebra. Proves the first (associativity of union and intersection) by showing that the left side `containsR` iff the right side does. Axioms are: 1) Union and intersection are associative 2) Union and intersection are commutative 3) Union distributes over intersection and intersection distributes over union. 4) r union empty = r; r intersect univ = r 5) r union complement r = univ; r intersect complement r = empty Prove one if have time? r Union Empty containsR p => r containsR p || Empty containsR p => (by def. Union) r containsR p || False => (def. of Empty) r containsR p So r Union Empty = r, because they contain the same points. (The steps are reversible!)