In this assginment, you’ll create a more sophisticated animation system than in your previous assignment. You will use interfaces to allow the system to handle a variety of shapes and drawing mechanisms.
Here is a general overview of how your system will work:
A scene consists of up to three shapes, all at different positions.
shape is an interface, with a number of different concrete types representing different shapes. Each shape type has a method called VisibleAtPoint
that given an x, y coordinate, returns a Boolean, indicating whether the shape must be drawn that point. It also contains a MoveTo method, which returns an updated version of the shape moved to a certain (x, y)
position, and a GetPosition method, which returns its current position.
Each scene knows how to Draw
itself. This involves looping through every coordinate, checking if any of its three shapes are visible at that point, and if so, printing a #
symbol; if no shape is visible, a blank space is printed.
An animation
is a type that has, as its underlying type, func (int) scene
–a function that takes in a frame number and outputs a scene
. It has a method to Run
an animation from a certain frame to another frame, in a certain amount of time. This involves drawing a frame, sleeping for a bit, then clearing the screen and drawing another frame.
In Terminal, type go get github.com/alex-lew/clear-screen-cs2
. This will allow you to import clear "github.com/alex-lew/clear-screen-cs2"
, then call clear.WipeScreen()
to clear the Terminal window between frames of animation.
Declare an interface for shapes, with the methods VisibleAtPoint
(accepting a position
and returning a bool
), GetPosition
(returning a position
, from your previous animation homework), and MoveTo
, which takes a position as input and returns a new shape moved to that position.
Then design the following shape types, as structs:
rectangle
, which represents a rectangle. Its position represents its top left corner; it also has a width and height. The rectangle struct should also have a boolean field, filled, which determines whether it is a filled rectangle, or just the outline of a rectangle. Its VisibleAtPoint
method returns true if the given point lies on the border of the rectangle, or, if the rectangle is filled, inside the rectangle. The rectangle also has GetPosition
and MoveTo
methods, so that it is a valid shape.
circle
, which represents a circle. Its position represents its center. It also stores its radius. All circles are filled; no need to store a boolean for this. To implement the VisibleAtPoint method for a circle, just check the distance between the given point and the circle’s center; if this distance is less than or equal to the circle’s radius, return true. As above, the circle also must implement GetPosition
and MoveTo
.
groupedShape
, which represents two shapes grouped together into one, which move together. Use a struct with two fields holding “sub-shapes”. The groupedShape
keeps track of a “core position,” and each sub-shape’s position is interpreted as being relative to the core position. If, for example, the groupedShape
is located at (10, 4), and it contains sub-shapes located at (-7, -2) and (5, 6), the sub-shapes should actually be considered to be located at (3, 2) and (15, 10) respectively. The MoveTo
and GetPosition
methods of the groupedShape
apply to the core position; by changing it, you automatically move both the sub-shapes together. To say whether the groupedShape is visible at a certain position, check whether either of its sub-shapes is visible at that position.
At least one shape of your own choosing / design. This can be something like the rectangle or circle (for instance, you could add a triangle shape), or it could be a “composite shape” type, like the groupedShape (you might add an intersectionShape
, for instance, that is just like the groupedShape but instead of showing both subshapes, you show only their intersection; the shape is visible only at points where both subshapes overlap, i.e., where both are visible).
The rectangle
type supports both filled and outlined variants, but the circle type described above is always filled. Modify it to support an “outline” option, by adding a filled
boolean to your struct, and changing the VisibleAtPoint
function accordingly.
Create a scene
struct that stores up to three shapes. (If a shape is nil
, that shape slot is unused.) It should have a Draw
method, which assumes that the Terminal screen is an empty 80x24 grid (80 wide, 24 tall), and draws the scene. To do this, loop through each row and column, choosing whether to draw a space or a # character, based on whether any of your three shapes are visible. (At the end of each row, make sure to use fmt.Println()
to move to the next row.)
Declare an animation
type with underlying type func (int) scene
. Give it a method, (a animation) Run(from int, to int, duration time.Duration)
, which does the following:
Based on whether from < to
or vice versa, determine whether this animation is running forward or backward.
Calculate how long each frame will take, if the entire animation is supposed to last for the given duration
.
In a loop, run the animation. At the top of each loop, clear the screen using clear.WipeScreen()
, then call the animation function with the current frame number to see what scene you should be drawing. Call the scene’s Draw
method to draw it. Then time.Sleep(•)
for the proper duration before the next iteration of the loop starts.
Create an animation with at least three shapes (not every shape needs to move). Your main
function should run this animation from start to finish, and then from finish to start.