Chapter 10 Drawing Regions Pictures w Drawing Pictures

  • Slides: 33
Download presentation
Chapter 10 Drawing Regions

Chapter 10 Drawing Regions

Pictures w Drawing Pictures n Pictures are composed of Regions o Regions are composed

Pictures w Drawing Pictures n Pictures are composed of Regions o Regions are composed of shapes n Pictures add Color data Picture = Region Color Region | Picture `Over` Picture | Empty. Pic deriving Show w We must be careful to use SOEGraphics, but SOEGraphics has its own Region datatype. import SOEGraphics hiding (Region) import qualified SOEGraphics as G (Region)

Recall the Region Datatype data Region = Shape | Translate Vector Region | Scale

Recall the Region Datatype data Region = Shape | Translate Vector Region | Scale Vector Region | Complement Region | Region `Union` Region | Region `Intersect` Region | Empty ------- primitive shape translated region scaled region inverse of a region union of regions intersection of regions w How do we draw things like the intersection of two regions, or the complement of a region? These are hard things to do efficiently. Fortunately, the G. Region interface uses lower-level support to do this for us.

G. Region w The G. Region datatype interfaces more directly to the underlying hardware.

G. Region w The G. Region datatype interfaces more directly to the underlying hardware. It is essentially a twodimensional array or “bitmap”, storing a binary value for each pixel in the window.

Efficient Bit-Map Operations w There is efficient low-level support for combining bit-maps using a

Efficient Bit-Map Operations w There is efficient low-level support for combining bit-maps using a variety of operators. For example, for union: + = w These operations are fast, but data (space) intensive, and this space needs to be explicitly allocated and de-allocated, a job for a much lower-level language.

G. Region Interface create. Rectangle : : Point -> IO G. Region create. Ellipse

G. Region Interface create. Rectangle : : Point -> IO G. Region create. Ellipse : : Point -> IO G. Region create. Polygon : : [Point] -> IO G. Region and. Region or. Region xor. Region diff. Region delete. Region : : : : : G. Region -> -> -> G. Region IO () draw. Region : : G. Region -> Graphic -> -> IO IO G. Region These functions are defined in the SOEGraphics library module.

Drawing G. Region w To draw things quickly, turn them into a G. Region,

Drawing G. Region w To draw things quickly, turn them into a G. Region, then turn the G. Region into a graphic object, and then use all the machinery we have built up so far to display the object. draw. Region. In. Window : : Window -> Color -> Region -> IO () draw. Region. In. Window w c r = draw. In. Window w (with. Color c (draw. Region (region. To. GRegion r))) w All we need to define, then, is: region. To. GRegion. n But first, let’s define what it means to draw a picture.

Drawing Pictures w Pictures combine multiple regions into one big picture. They provide a

Drawing Pictures w Pictures combine multiple regions into one big picture. They provide a mechanism for placing one sub-picture on top of another. draw. Pic : : Window -> Picture -> IO () draw. Pic w (Region c r) = draw. Region. In. Window w c r draw. Pic w (p 1 `Over` p 2) = do draw. Pic w p 2 draw. Pic w p 1 draw. Pic w Empty. Pic = return () w Note that p 2 is drawn before p 1, since we want p 1 to appear “over” p 2.

Summary w We have a rich calculus of Shapes, which we can draw, take

Summary w We have a rich calculus of Shapes, which we can draw, take the perimeter of, and tell if a point lies within. w We defined a richer data type Region, which allows more complex compositions (intersection, complement, etc. ). n We gave Region a mathematical semantics as a set of points in the 2 -dimensional plane. n We defined some interesting operators like contains. R which is the characteristic function for a region. n The rich nature of Region makes it hard to draw efficiently, so we use a lower level datatype G. Region. n We enriched this even further with the Picture type. w G. Region is low level, relying on features like overwriting and explicit allocation and deallocation of memory. w We can think of Region as a high-level interface to G. Region that hides low-level details.

Turning a Region into a G. Region Experiment with a smaller problem to illustrate

Turning a Region into a G. Region Experiment with a smaller problem to illustrate an efficiency problem. data New. Region = Rect Side -- abstracts G. Region reg. To. NReg : : Region -> New. Region reg. To. NReg (Shape (Rectangle sx sy)) = Rect sx sy reg. To. NReg (Scale (x, y) r) = reg. To. NReg (scale. Reg (x, y) r) where scale. Reg (x, y) (Shape (Rectangle sx sy)) = Shape (Rectangle (x*sx) (y*sy)) scale. Reg (x, y) (Scale s r) = Scale s (scale. Reg (x, y) r)

A Problem w Consider (Scale (x 1, y 1) (Scale (x 2, y 2)

A Problem w Consider (Scale (x 1, y 1) (Scale (x 2, y 2) (Scale (x 3, y 3). . . (Shape (Rectangle sx sy)). . . ))) w If the scaling is n levels deep, how many traversals does reg. To. NReg perform over the Region tree?

We’ve Seen This Before w Believe it or not we have encountered this problem

We’ve Seen This Before w Believe it or not we have encountered this problem before. Recall the definition of reverse: reverse [] reverse (x: xs) where [] (y: ys) = [] = (reverse xs) ++ [x] ++ zs = zs ++ zs = y : (ys ++ zs) w How did we solve this? We used an extra accumulating parameter: reverse xs = revhelp xs [] where revhelp [] zs = zs revhelp (x: xs) zs = revhelp xs (x: zs) w We can do the same thing for Regions.

Accumulate the Scaling Factor reg. To. NReg 2 : : Region -> New. Region

Accumulate the Scaling Factor reg. To. NReg 2 : : Region -> New. Region reg. To. NReg 2 r = r. To. NR (1, 1) r where r. To. NR : : (Float, Float) -> Region -> New. Region r. To. NR (x 1, y 1) (Shape (Rectangle sx sy)) = Rect (x 1*sx) (y 1*sy) r. To. NR (x 1, y 1) (Scale (x 2, y 2) r) = r. To. NR (x 1*x 2, y 1*y 2) r w To solve our original problem, repeat this for all the constructors of Region (not just Shape and Scale) and use G. Region instead of New. Region. We also need to handle translation as well as scaling.

Final Version reg. To. GReg 1 : : Vector -> Region -> G. Region

Final Version reg. To. GReg 1 : : Vector -> Region -> G. Region reg. To. GReg 1 loc sca (Shape s) = shape. To. GRegion loc sca s reg. To. GReg 1 (x, y) sca (Translate (u, v) r) = reg. To. GReg 1 (x+u, y+v) sca r reg. To. GReg 1 loc (x, y) (Scale (u, v) r) = reg. To. GReg 1 loc (x*u, y*v) r reg. To. GReg 1 loc sca Empty = create. Rectangle (0, 0) reg. To. GReg 1 loc sca (r 1 `Union` r 2) = let gr 1 = reg. To. GReg 1 loc sca r 1 gr 2 = reg. To. GReg 1 loc sca r 2 in or. Region gr 1 gr 2 w Assuming, of course, that we can define: shape. To. GRegion : : Vector -> Shape -> G. Region and write rules for Intersect, Complement etc.

A Matter of Style w While the function on the previous page shows how

A Matter of Style w While the function on the previous page shows how to solve the problem, there are several stylistic issues that could make it more readable and understandable. w The style of defining a function by patterns becomes cluttered when there are many parameters (other than the one which has the patterns). w The pattern of explicitly allocating and deallocating (bit-map) G. Region’s will be repeated in cases for intersection and for complement, so we should abstract it, and give it a name.

Abstract the Low-Level Bit-Map Details prim. GReg loc sca r 1 r 2 op

Abstract the Low-Level Bit-Map Details prim. GReg loc sca r 1 r 2 op = let gr 1 = reg. To. GReg loc sca r 1 gr 2 = reg. To. GReg loc sca r 2 in op gr 1 gr 2

Redo with a Case Expression reg. To. GReg : : Vector -> Region ->

Redo with a Case Expression reg. To. GReg : : Vector -> Region -> G. Region reg. To. GReg (loc@(x, y)) (sca@(a, b)) shape = case shape of Pattern renaming Shape s -> shape. To. GRegion loc sca s Translate (u, v) r -> reg. To. GReg (x+u, y+v) sca r Scale (u, v) r -> reg. To. GReg loc (a*u, b*v) r Empty -> create. Rectangle (0, 0) r 1 `Union` r 2 -> prim. GReg loc sca r 1 r 2 or. Region r 1 `Intersect` r 2 -> prim. GReg loc sca r 1 r 2 and. Region Complement r -> prim. GReg loc sca win. Rect r diff. Region where win. Rect : : Region win. Rect = Shape (Rectangle (pixel. To. Inch x. Win) (pixel. To. Inch y. Win)) region. To. GRegion : : Region -> G. Region region. To. GRegion r = reg. To. GReg (0, 0) (1, 1) r

Shape to G. Region: Rectangle shape. To. GRegion : : Vector -> Shape ->

Shape to G. Region: Rectangle shape. To. GRegion : : Vector -> Shape -> IO G. Region shape. To. GRegion (lx, ly) (sx, sy) (Rectangle s 1 s 2) = create. Rectangle (trans(-s 1/2, -s 2/2)) (trans (s 1/2, s 2/2)) where trans (x, y) = ( x. Win 2 + inch. To. Pixel (lx+x*sx), y. Win 2 - inch. To. Pixel (ly+y*sy) ) s 1/ 2 ( x. Win 2, y. Win 2) ( x. Win, y. Win ) S 2/2 s 2

Ellipse shape. To. GRegion 1 (lx, ly) (sx, sy) (Ellipse r 1 r 2)

Ellipse shape. To. GRegion 1 (lx, ly) (sx, sy) (Ellipse r 1 r 2) = create. Ellipse (trans (-r 1, -r 2)) (trans ( r 1, r 2)) where trans (x, y) = ( x. Win 2 + inch. To. Pixel (lx+x*sx), y. Win 2 - inch. To. Pixel (ly+y*sy) ) r 2 r 1

Polygon and Rt. Triangle shape. To. GRegion 1 (lx, ly) (sx, sy) (Polygon pts)

Polygon and Rt. Triangle shape. To. GRegion 1 (lx, ly) (sx, sy) (Polygon pts) = create. Polygon (map trans pts) where trans (x, y) = ( x. Win 2 + inch. To. Pixel (lx+x*sx), y. Win 2 - inch. To. Pixel (ly+y*sy) ) shape. To. GRegion 1 (lx, ly) (sx, sy) (Rt. Triangle s 1 s 2) = create. Polygon (map trans [(0, 0), (s 1, 0), (0, s 2)]) where trans (x, y) = ( x. Win 2 + inch. To. Pixel (lx+x*sx), y. Win 2 - inch. To. Pixel (ly+y*sy) )

A Matter of Style, 2 w shape. To. GRegion 1 has the same problems

A Matter of Style, 2 w shape. To. GRegion 1 has the same problems as reg. To. GReg 1 n The extra parameters obscure the pattern matching. n There is a repeated pattern, we should give it a name. shape. To. GRegion (lx, ly) (sx, sy) s = case s of Rectangle s 1 s 2 -> create. Rectangle (trans (-s 1/2, -s 2/2)) (trans ( s 1/2, s 2/2)) Ellipse r 1 r 2 -> create. Ellipse (trans (-r 1, -r 2)) (trans ( r 1, r 2)) Polygon pts -> create. Polygon (map trans pts) Rt. Triangle s 1 s 2 -> create. Polygon (map trans [(0, 0), (s 1, 0), (0, s 2)]) where trans (x, y) = ( x. Win 2 + inch. To. Pixel (lx+x*sx), y. Win 2 - inch. To. Pixel (ly+y*sy) )

Drawing Pictures, Sample Regions draw : : Picture -> IO () draw p =

Drawing Pictures, Sample Regions draw : : Picture -> IO () draw p = run. Graphics ( do w <- open. Window "Region Test" (x. Win, y. Win) draw. Pic w p space. Close w ) r 1 r 2 r 3 r 4 = = Shape (Rectangle 3 2) (Ellipse 1 1. 5) (Rt. Triangle 3 2) (Polygon [(-2. 5, 2. 5), (-3. 0, 0), (-1. 7, -1. 0), (-1. 1, 0. 2), (-1. 5, 2. 0)] )

Sample Pictures reg 1 = r 3 `Union` r 1 `Intersect` Complement r 2

Sample Pictures reg 1 = r 3 `Union` r 1 `Intersect` Complement r 2 `Union` r 4 pic 1 = Region Cyan reg 1 Main 1 = draw pic 1 Recall the precedence of Union and Intersect ----- Rt. Triangle Rectangle Ellipse Polygon

More Pictures reg 2 = let circle = Shape (Ellipse 0. 5) square =

More Pictures reg 2 = let circle = Shape (Ellipse 0. 5) square = Shape (Rectangle 1 1) in (Scale (2, 2) circle) `Union` (Translate (2, 1) square) `Union` (Translate (-2, 0) square) pic 2 = Region Yellow reg 2 main 2 = draw pic 2

Another Picture pic 3 = pic 2 `Over` pic 1 main 3 = draw

Another Picture pic 3 = pic 2 `Over` pic 1 main 3 = draw pic 3

Separate Computation From Action one. Circle = Shape (Ellipse 1 1) many. Circles =

Separate Computation From Action one. Circle = Shape (Ellipse 1 1) many. Circles = [ Translate (x, 0) one. Circle | x <- [0, 2. . ] ] five. Circles = foldr Union Empty (take 5 many. Circles) pic 4 = Region Magenta (Scale (0. 25, 0. 25) five. Circles) main 4 = draw pic 4

Ordering Pictures pict. To. List : : Picture -> [(Color, Region)] pict. To. List

Ordering Pictures pict. To. List : : Picture -> [(Color, Region)] pict. To. List Empty. Pic = [] pict. To. List (Region c r) = [(c, r)] pict. To. List (p 1 `Over` p 2) = pict. To. List p 1 ++ pict. To. List p 2 pic 6 = pic 4 `Over` pic 2 `Over` pic 1 `Over` pic 5 pict. To. List pic 6 - - - > [(Magenta, ? ), (Yellow, ? ), (Cyan, ? )] Recovers the Regions from top to bottom. Possible because Picture is a datatype that can be analyzed.

An Analogy pict. To. List Empty. Pic = [] pict. To. List (Region c

An Analogy pict. To. List Empty. Pic = [] pict. To. List (Region c r) = [(c, r)] pict. To. List (p 1 `Over` p 2) = pict. To. List p 1 ++ pict. To. List p 2 draw. Pic w (Region c r) = draw. Region. In. Window w c r draw. Pic w (p 1 `Over` p 2) = do { draw. Pic w p 2 ; draw. Pic w p 1} draw. Pic w Empty. Pic = return () w Something interesting to prove: draw. Pic w = sequence. (map (uncurry (draw. Region. In. Window w))). reverse. pict. To. List

Pictures that React w Find the topmost Region in a Picture that “covers” the

Pictures that React w Find the topmost Region in a Picture that “covers” the position of the mouse when a left button click appears. w Search the picture list for the first Region that contains the mouse position. w Re-arrange the list, bringing that one to the top. adjust : : [(Color, Region)] -> Vertex -> (Maybe (Color, Region), [(Color, Region)]) adjust [] p = (Nothing, []) adjust ((c, r): regs) p = if r `contains. R` p then (Just (c, r), regs) else let (hit, rs) = adjust regs p in (hit, (c, r) : rs)

Doing it Non-recursively adjust 2 regs p = case (break ((_, r) -> r

Doing it Non-recursively adjust 2 regs p = case (break ((_, r) -> r `contains. R` p) regs) of (top, hit: rest) -> (Just hit, top++rest) (_, []) -> (Nothing, []) This is from the Prelude: break: : (a -> Bool) -> [a] -> ([a], [a]) For example: break even [1, 3, 5, 4, 7, 6, 12] ([1, 3, 5], [4, 7, 6, 12])

Putting it all Together loop : : Window -> [(Color, Region)] -> IO ()

Putting it all Together loop : : Window -> [(Color, Region)] -> IO () loop w regs = do clear. Window w sequence [ draw. Region. In. Window w c r | (c, r) <- reverse regs ] (x, y) <- get. LBP w case (adjust regs (pixel. To. Inch (x - x. Win 2), pixel. To. Inch (y. Win 2 - y) )) of (Nothing, _ ) -> close. Window w (Just hit, new. Regs) -> loop w (hit : new. Regs) draw 2 : : Picture -> IO () draw 2 pic = run. Graphics ( do w <- open. Window "Picture demo" (x. Win, y. Win) loop w (pict. To. List pic))

Try it Out p 1, p 2, p 3, p 4 p 1 =

Try it Out p 1, p 2, p 3, p 4 p 1 = Region p 2 = Region p 3 = Region p 4 = Region : : Picture Magenta r 1 Cyan r 2 Green r 3 Yellow r 4 pic : : Picture pic = foldl Over Empty. Pic [p 1, p 2, p 3, p 4] main = draw 2 pic

A Matter of Style, 3 loop 2 w regs = do clear. Window w

A Matter of Style, 3 loop 2 w regs = do clear. Window w sequence [ draw. Region. In. Window w c r | (c, r) <- reverse regs ] (x, y) <- get. LBP w let aux (_, r) = r `contains. R` ( pixel. To. Inch (x-x. Win 2), pixel. To. Inch (y. Win 2 -y) ) case (break aux regs) of (_, []) -> close. Window w (top, hit: bot) -> loop w (hit : (top++bot)) draw 3 pic = run. Graphics ( do w <- open. Window "Picture demo" (x. Win, y. Win) loop 2 w (pict. To. List pic) )