Difference between revisions of "Branch Assignment"
(38 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | |||
=Reference= | =Reference= | ||
==[https://en.wikipedia.org/wiki/L-system Lindenmayer system]== | ==[https://en.wikipedia.org/wiki/L-system Lindenmayer system]== | ||
Line 8: | Line 7: | ||
: [https://apidock.com/ruby/Array/pop pop] | : [https://apidock.com/ruby/Array/pop pop] | ||
− | == | + | ==LogoTurtle== |
− | + | [[File:Seymour_Papert.jpg|thumb]] | |
− | |||
− | + | The turtle you will use in this assignment is inspired by the Turtle Graphics in the [[https://en.wikipedia.org/wiki/Logo_(programming_language) Logo Programming Language]] created by [https://en.wikipedia.org/wiki/Wally_Feurzeig Wally Feurzeig], [https://en.wikipedia.org/wiki/Seymour_Papert Seymour Papert], and [https://en.wikipedia.org/wiki/Cynthia_Solomon Cynthia Solomon]. The [http://cyberneticzoo.com/cyberneticanimals/1969-the-logo-turtle-seymour-papert-marvin-minsky-et-al-american/original turtle was a robot which would draw with a pen on paper place on the floor]. We will less excitingly draw on the screen with OpenGL. | |
− | [[ | ||
− | + | A [https://el.media.mit.edu/logo-foundation/what_is_logo/logo_primer.html primer] on how to use a turtle in its native habitat. | |
− | |||
− | + | The provided class LogoTurtle below provides methods to get and set its pen color and width, pick the pen up and put it down, move forward, and turn right in degrees. | |
− | |||
− | The provided class LogoTurtle | ||
− | + | Note: <code>attr_accessor :pen_color, :pen_width</code> provides pen_color, pen_color=, pen_width, and pen_width= methods. | |
− | + | <syntaxhighlight lang="ruby"> | |
+ | class Turtle | ||
+ | # https://el.media.mit.edu/logo-foundation/what_is_logo/logo_primer.html | ||
attr_accessor :pen_color, :pen_width | attr_accessor :pen_color, :pen_width | ||
− | def initialize | + | |
− | super | + | def initialize(g, pen_width: 0.005, pen_color: Color::WHITE) |
+ | super() | ||
+ | @g = g | ||
@is_pen_down = true | @is_pen_down = true | ||
− | @pen_width = | + | @pen_width = pen_width |
− | @pen_color = | + | @pen_color = pen_color |
end | end | ||
def pen_up | def pen_up | ||
− | @is_pen_down = | + | @is_pen_down = false |
end | end | ||
def pen_down | def pen_down | ||
− | @is_pen_down = | + | @is_pen_down = true |
end | end | ||
− | |||
def forward(amount) | def forward(amount) | ||
Line 46: | Line 43: | ||
x = @pen_width | x = @pen_width | ||
if @is_pen_down | if @is_pen_down | ||
− | + | @g.color = @pen_color | |
− | + | points = [ | |
− | + | Point2.new(-x, 0.0), | |
− | + | Point2.new(-x, y), | |
− | + | Point2.new(x, y), | |
− | + | Point2.new(x, 0.0) | |
− | + | ] | |
+ | @g.draw_convex_polygon(points) | ||
end | end | ||
− | + | @g.apply_translation(0, y) | |
end | end | ||
def right(degrees) | def right(degrees) | ||
− | + | @g.apply_rotation(-degrees) | |
end | end | ||
end | end | ||
− | </ | + | </syntaxhighlight> |
− | ==Flower Client== | + | ==LogoTurtle Clients== |
− | + | ===Flower Client=== | |
− | + | In this example client, a square is drawn by repeatedly moving forward and turning right 90 degrees. The flower is drawn by repeatedly drawing slightly turned from each other squares. | |
− | |||
− | |||
− | |||
− | def render | + | <syntaxhighlight lang="ruby"> |
− | flower | + | class Flower |
+ | def render(g) | ||
+ | flower(Turtle.new(g)) | ||
end | end | ||
private | private | ||
− | def square | + | def square(turtle) |
4.times do | 4.times do | ||
− | + | turtle.forward 0.5 | |
− | + | turtle.right 90 | |
end | end | ||
end | end | ||
− | def flower | + | def flower(turtle) |
36.times do | 36.times do | ||
− | + | turtle.right 10 | |
− | square | + | square(turtle) |
+ | end | ||
+ | end | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | {{RubyToRun|flower|turtle/clients|core}} | ||
+ | |||
+ | [[File:Logo_turtle_flower.png]] | ||
+ | |||
+ | === Pen Width and Color Client === | ||
+ | |||
+ | In this client we draw equilateral polygons with different pen colors. Further, each subsequent line segment of a polygon is draw with a smaller pen width. | ||
+ | |||
+ | <syntaxhighlight lang="ruby"> | ||
+ | class EquilateralPolygons | ||
+ | def render(g) | ||
+ | turtle = Turtle.new(g) | ||
+ | turtle.pen_color = Color::HONOLULU_BLUE | ||
+ | triangle(turtle) | ||
+ | |||
+ | turtle.right(90) | ||
+ | |||
+ | turtle.pen_width /= 2 | ||
+ | turtle.pen_color = Color::PARIS_DAISY | ||
+ | square(turtle) | ||
+ | |||
+ | turtle.right(135) | ||
+ | |||
+ | turtle.pen_color = Color::FRENCH_VIOLET | ||
+ | pentagon(turtle) | ||
+ | end | ||
+ | |||
+ | private | ||
+ | |||
+ | def triangle(turtle) | ||
+ | polygon(turtle, 3) | ||
+ | end | ||
+ | |||
+ | def square(turtle) | ||
+ | polygon(turtle, 4) | ||
+ | end | ||
+ | |||
+ | def pentagon(turtle) | ||
+ | polygon(turtle, 5) | ||
+ | end | ||
+ | |||
+ | def polygon(turtle, n) | ||
+ | turtle.pen_width = 0.03 | ||
+ | n.times do | ||
+ | turtle.forward 0.5 | ||
+ | turtle.right 360/n | ||
+ | turtle.pen_width /= 2 | ||
end | end | ||
end | end | ||
− | end</ | + | end |
+ | </syntaxhighlight> | ||
+ | |||
+ | {{RubyToRun|equilateral_polygons|turtle/clients|core}} | ||
+ | |||
+ | [[File:Equilateral_Polygons_Client.png]] | ||
=Code to Implement= | =Code to Implement= | ||
+ | ==TeleportingTurtle== | ||
+ | {{RubyToImplement|teleporting_turtle|teleporting_turtle|TeleportingTurtle|Turtle|preserve_yield_restore}} | ||
+ | |||
+ | ===preserve_yield_restore=== | ||
+ | This utility method will simply preserve the current state, yield to the block passed by the client, and then restore the preserved state. | ||
+ | |||
+ | Note: the private preserve_state and restore_state methods have been provided to you. | ||
+ | <!-- | ||
+ | The state to be preserved and restored is the OpenGL model view matrix and turtle's pen width and color. | ||
+ | --> | ||
+ | |||
+ | |||
+ | === Teleporting Turtle Client === | ||
+ | In this client, the @teleporting_turtle starts facing up. After changing the pen color and width, it moves forward, leaving behind a DODGER_BLUE line. Then, it invokes the preserve_yield_restore method you just implemented. The preserve_yield_restore method should preserves the current state of the TeleportingTurtle (that is: the pen width, pen color, and the location and direction of the turtle in the form of the glMatrix) and yields to the specified block. Within that block, the turtle is turned right, the pen width and color are changed, and then a 135 degree arc is drawn in OUTRAGEOUS_ORANGE. Upon the completion of this block, control is returned to your preserve_yield_restore method which should restore the state of the turtle back to its pen color and width and turtle location and direction before preserve_yield_restore was invoked. The turtle then moves forward, leaving behind a DODGER_BLUE line. | ||
+ | |||
+ | <syntaxhighlight lang="ruby"> | ||
+ | class PreserveYieldRestoreClient | ||
+ | def render(g) | ||
+ | teleporting_turtle = TeleportingTurtle.new(g) | ||
+ | teleporting_turtle.pen_color = Color::DODGER_BLUE | ||
+ | teleporting_turtle.pen_width = 0.02 | ||
+ | teleporting_turtle.forward(0.25) | ||
+ | teleporting_turtle.preserve_yield_restore do | ||
+ | teleporting_turtle.right(90) | ||
+ | teleporting_turtle.pen_color = Color::OUTRAGEOUS_ORANGE | ||
+ | teleporting_turtle.pen_width = 0.005syntaxhighlight | ||
+ | 45.times do | ||
+ | teleporting_turtle.right(5) | ||
+ | teleporting_turtle.forward(0.05) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | teleporting_turtle.forward(0.25) | ||
+ | end | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | {{RubyToRun|preserve_yield_restore_client|teleporting_turtle/client|cor}} | ||
+ | |||
+ | [[File:Preserve Yield Restore Client.png]] | ||
==Branch== | ==Branch== | ||
− | {{RubyToImplement|branch| | + | {{RubyToImplement|branch|branch|Branch|Object|branch}} |
+ | |||
+ | The goal of this assignment is to render a branch that looks something akin to the progression of max_depth branches below. | ||
+ | |||
+ | Note: the initialize method constructs a new instance of TeleportingTurtle as well as hangs onto the parameter values in instance variables. | ||
{| class="wikitable" style="text-align: center; " | {| class="wikitable" style="text-align: center; " | ||
− | |[[File:Branch_MaxDepth0.png | | + | |[[File:Branch_MaxDepth0.png | 300px]] |
− | |[[File:Branch_MaxDepth1.png | | + | |[[File:Branch_MaxDepth1.png | 300px]] |
− | |[[File:Branch_MaxDepth2.png | | + | |[[File:Branch_MaxDepth2.png | 300px]] |
− | |[[File:Branch_MaxDepth3.png | | + | |[[File:Branch_MaxDepth3.png | 300px]] |
− | |[[File:Branch_MaxDepth4.png | | + | |[[File:Branch_MaxDepth4.png | 300px]] |
− | |[[File:Branch_MaxDepth5.png | | + | |[[File:Branch_MaxDepth5.png | 300px]] |
|- | |- | ||
|max_depth: 0 | |max_depth: 0 | ||
Line 112: | Line 211: | ||
|} | |} | ||
− | === | + | ===branch(length, line_width, depth_remaining)=== |
− | + | Pass a block to [[#TeleportingTurtle|preserve_yield_restore]] to handle the preservation and restoration of the necessary OpenGL and TeleportingTurtle state. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
Note: to produce the reference images the code below was used: | Note: to produce the reference images the code below was used: | ||
− | + | <syntaxhighlight lang="ruby"> | |
− | + | next_length = length * 0.5 | |
+ | next_line_width = line_width * 0.8 | ||
+ | </syntaxhighlight> | ||
− | Note: the child branches in the reference images are one third and two thirds from the beginning to the end. | + | Note: the child branches in the reference images are located at one third and at two thirds from the beginning to the end. |
− | Note: the child branches in the reference images are rotated 30.0 degrees and -30.0 degrees. | + | Note: the child branches in the reference images are rotated 30.0 degrees and -30.0 degrees from their parent branch. |
Note: in the recursive case the child branches were drawn before the line at the current depth to allow the green to be beneath the brown. | Note: in the recursive case the child branches were drawn before the line at the current depth to allow the green to be beneath the brown. | ||
Line 135: | Line 229: | ||
Do '''NOT''' worry if your images are not exact matches of the reference images. | Do '''NOT''' worry if your images are not exact matches of the reference images. | ||
− | + | {{RubyToRun|branch|branch|main}} | |
− | {{RubyToRun|branch| | ||
[[File:Branch_MaxDepth5.png]] | [[File:Branch_MaxDepth5.png]] | ||
+ | |||
+ | =Visual Comparison= | ||
+ | {{RubyToRun|branch_snapshots_web_page_generator|branch/snapshots|test}} | ||
+ | |||
+ | Note: there is no image difference comparison in these snapshots. Do not stress about the exact pixel colors. If it looks like an L-system branch, you should be in good shape. |
Latest revision as of 03:06, 15 December 2023
Reference
Lindenmayer system
Array
LogoTurtle
The turtle you will use in this assignment is inspired by the Turtle Graphics in the [Logo Programming Language] created by Wally Feurzeig, Seymour Papert, and Cynthia Solomon. The turtle was a robot which would draw with a pen on paper place on the floor. We will less excitingly draw on the screen with OpenGL.
A primer on how to use a turtle in its native habitat.
The provided class LogoTurtle below provides methods to get and set its pen color and width, pick the pen up and put it down, move forward, and turn right in degrees.
Note: attr_accessor :pen_color, :pen_width
provides pen_color, pen_color=, pen_width, and pen_width= methods.
class Turtle
# https://el.media.mit.edu/logo-foundation/what_is_logo/logo_primer.html
attr_accessor :pen_color, :pen_width
def initialize(g, pen_width: 0.005, pen_color: Color::WHITE)
super()
@g = g
@is_pen_down = true
@pen_width = pen_width
@pen_color = pen_color
end
def pen_up
@is_pen_down = false
end
def pen_down
@is_pen_down = true
end
def forward(amount)
y = amount
x = @pen_width
if @is_pen_down
@g.color = @pen_color
points = [
Point2.new(-x, 0.0),
Point2.new(-x, y),
Point2.new(x, y),
Point2.new(x, 0.0)
]
@g.draw_convex_polygon(points)
end
@g.apply_translation(0, y)
end
def right(degrees)
@g.apply_rotation(-degrees)
end
end
LogoTurtle Clients
Flower Client
In this example client, a square is drawn by repeatedly moving forward and turning right 90 degrees. The flower is drawn by repeatedly drawing slightly turned from each other squares.
class Flower
def render(g)
flower(Turtle.new(g))
end
private
def square(turtle)
4.times do
turtle.forward 0.5
turtle.right 90
end
end
def flower(turtle)
36.times do
turtle.right 10
square(turtle)
end
end
end
file to run: | src/core/ruby/turtle/clients/flower.rb | ![]() |
Pen Width and Color Client
In this client we draw equilateral polygons with different pen colors. Further, each subsequent line segment of a polygon is draw with a smaller pen width.
class EquilateralPolygons
def render(g)
turtle = Turtle.new(g)
turtle.pen_color = Color::HONOLULU_BLUE
triangle(turtle)
turtle.right(90)
turtle.pen_width /= 2
turtle.pen_color = Color::PARIS_DAISY
square(turtle)
turtle.right(135)
turtle.pen_color = Color::FRENCH_VIOLET
pentagon(turtle)
end
private
def triangle(turtle)
polygon(turtle, 3)
end
def square(turtle)
polygon(turtle, 4)
end
def pentagon(turtle)
polygon(turtle, 5)
end
def polygon(turtle, n)
turtle.pen_width = 0.03
n.times do
turtle.forward 0.5
turtle.right 360/n
turtle.pen_width /= 2
end
end
end
file to run: | src/core/ruby/turtle/clients/equilateral_polygons.rb | ![]() |
Code to Implement
TeleportingTurtle
file: | src/main/ruby/teleporting_turtle/teleporting_turtle.rb | ![]() |
class: | TeleportingTurtle | |
superclass: | Turtle | |
methods: | preserve_yield_restore |
preserve_yield_restore
This utility method will simply preserve the current state, yield to the block passed by the client, and then restore the preserved state.
Note: the private preserve_state and restore_state methods have been provided to you.
Teleporting Turtle Client
In this client, the @teleporting_turtle starts facing up. After changing the pen color and width, it moves forward, leaving behind a DODGER_BLUE line. Then, it invokes the preserve_yield_restore method you just implemented. The preserve_yield_restore method should preserves the current state of the TeleportingTurtle (that is: the pen width, pen color, and the location and direction of the turtle in the form of the glMatrix) and yields to the specified block. Within that block, the turtle is turned right, the pen width and color are changed, and then a 135 degree arc is drawn in OUTRAGEOUS_ORANGE. Upon the completion of this block, control is returned to your preserve_yield_restore method which should restore the state of the turtle back to its pen color and width and turtle location and direction before preserve_yield_restore was invoked. The turtle then moves forward, leaving behind a DODGER_BLUE line.
class PreserveYieldRestoreClient
def render(g)
teleporting_turtle = TeleportingTurtle.new(g)
teleporting_turtle.pen_color = Color::DODGER_BLUE
teleporting_turtle.pen_width = 0.02
teleporting_turtle.forward(0.25)
teleporting_turtle.preserve_yield_restore do
teleporting_turtle.right(90)
teleporting_turtle.pen_color = Color::OUTRAGEOUS_ORANGE
teleporting_turtle.pen_width = 0.005syntaxhighlight
45.times do
teleporting_turtle.right(5)
teleporting_turtle.forward(0.05)
end
end
teleporting_turtle.forward(0.25)
end
end
file to run: | src/cor/ruby/teleporting_turtle/client/preserve_yield_restore_client.rb | ![]() |
Branch
file: | src/main/ruby/branch/branch.rb | ![]() |
class: | Branch | |
superclass: | Object | |
methods: | branch |
The goal of this assignment is to render a branch that looks something akin to the progression of max_depth branches below.
Note: the initialize method constructs a new instance of TeleportingTurtle as well as hangs onto the parameter values in instance variables.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
max_depth: 0 | max_depth: 1 | max_depth: 2 | max_depth: 3 | max_depth: 4 | max_depth: 5 |
branch(length, line_width, depth_remaining)
Pass a block to preserve_yield_restore to handle the preservation and restoration of the necessary OpenGL and TeleportingTurtle state.
Note: to produce the reference images the code below was used:
next_length = length * 0.5
next_line_width = line_width * 0.8
Note: the child branches in the reference images are located at one third and at two thirds from the beginning to the end.
Note: the child branches in the reference images are rotated 30.0 degrees and -30.0 degrees from their parent branch.
Note: in the recursive case the child branches were drawn before the line at the current depth to allow the green to be beneath the brown.
Do NOT worry if your images are not exact matches of the reference images.
file to run: | src/main/ruby/branch/branch.rb | ![]() |
Visual Comparison
file to run: | src/test/ruby/branch/snapshots/branch_snapshots_web_page_generator.rb | ![]() |
Note: there is no image difference comparison in these snapshots. Do not stress about the exact pixel colors. If it looks like an L-system branch, you should be in good shape.