ACM SIGCSE 2005: Multimedia Construction Projects Mark Guzdial and Barb Ericson

advertisement
ACM SIGCSE 2005:
Multimedia
Construction Projects
Funding to support this work
came from the National
Science Foundation, Georgia
Tech's College of Computing,
Georgia’s Department of
Education, GVU Center, Al
West Fund, and President's
Undergraduate Research
Award.
Mark Guzdial and Barb Ericson
College of Computing
Georgia Institute of Technology
guzdial@cc.gatech.edu ericson@cc.gatech.edu
http://www.cc.gatech.edu/~mark.guzdial
Plan-Go Get Your Laptop for Play!

4-4:20: Introductions


What are you looking for?
4:20-4:40 (20 mins)
What’s on your CD
 Why media computation
 What we’re doing in the courses at Georgia Tech





4:40-5:30: Media manipulations in Python (45 minutes)
5:30-5:45: Break
5:45-7:30 Media manipulations in Java (45 minutes)
6:30-7: Group vote
Option 1: You play! We walk around and help you.
 Option 2: Introducing objects into a Java CS1 media
approach: Using Turtles
 Option 3: Introducing lists and trees in Java via a media
approach.

Introductions
Your name
 Where you’re from
 What you teach
 Where can you incorporate media in what you teach
 List the languages you teach
and
 What do you want to get out of this workshop (esp. any
particular techniques)?

What’s on your CD

CS1-Python materials






Pre-release PDF of Media Computation book in
Jython
Course slides
MediaTools: Squeak-based media exploration tools
Jython Environment for Students (JES)
Instructor Resources (how to tune for different
places, grading tool)
CS1-Java materials




Pre-release PDF of Media Computation book in Java
Some workshop slides
Classes
MediaSources: Royalty-free JPEG and WAV files
What’s on your CD - Continued

CS2-Java materials



Squeak




Course notes
Classes and course slides for exploring lists and
trees in media context
Squeak for Mac and WIndows
Squeak computer music essays
Squeak for media computation PowerPoint
Material for this workshop

Workshop slides
A Computer Science Undergraduate
Degree is Facing Challenging times

We’re losing students, at an increasing rate.
Women and minority percentage of
enrollment dropping
 High failure rates in CS1 (35-50% or more)
 Fewer applications into CS




“All programming jobs going overseas”
Research results: “Tedious,” “boring,”
“lacking creativity,” “asocial”
All of this at a time when we recognize the
critical role of IT in our economy, in all jobs
Strategy:
Ubiquitous Computing Education

Everyone needs computing, and
we should be able to teaching computing that
everyone cares about.


Make computing relevant and accessible to
students.
Minors, certificates, joint degrees, alternative paths
to the major.


Created a CS minor
Created a BS in Computational Media
Computer science is more
important than Calculus

In 1961, Alan Perlis argued
that computer science is more
important in a liberal
education than calculus



Explicitly, he argued that
all students should learn to
program.
Calculus is about rates, and
that’s important to many.
Computer science is about
process, which is important to
everyone
CS1315 Introduction to Media
Computation
 Started with 121 students in Spring 2003,
and averaging 300/term since then
 2/3 female in Spring 2003 MediaComp
 Overall, CS1315 has been 51% female
 Required in Architecture, Management, Ivan Allen College of
Liberal Arts, and Biology
 Focus: Learning programming and CS concepts within the context
of media manipulation and creation
 Converting images to grayscale and negatives, splicing and reversing
sounds, writing programs to generate HTML, creating movies out of
Web-accessed content.
 Computing for communications, not calculation
Python as the programming
language

Huge issue

Use in commercial contexts
authenticates the choice

IL&M, Google, Nextel, etc.

Minimal syntax

Looks like other
programming languages

Potential for transfer
Rough overview of Syllabus


Defining and executing functions
Pictures
Psychophysics, data structures, defining functions, for
loops, if conditionals
 Bitmap vs. vector notations


Sounds
Psychophysics, data structures, defining functions, for
loops, if conditionals
 Sampled sounds vs. synthesized, MP3 vs. MIDI


Text
Converting between media, generating HTML, database,
and networking
 A little trees (directories) and hash tables (database)



Movies
Then, Computer Science topics (last 1/3 class)
Some Computer Science Topics
inter-mixed

We talk about algorithms across
media



We talk about representations and
mappings (Goedel)


Sampling a picture (to scale it) is
the same
algorithm as sampling a sound
(to shift frequency)
Blending two pictures (fading
one into the other) and two
sounds is the same algorithm.
From samples to numbers (and
into Excel), through a mapping
to pixel colors
We talk about design and debugging

But they mostly don’t hear us
Computer Science Topics
as solutions to their problems

“Why is PhotoShop so much faster?”
Compiling vs. interpreting
 Machine language and how the computer works


“Writing programs is hard! Are there ways to make it
easier? Or at least shorter?”
Object-oriented programming
 Functional programming and recursion


“Movie-manipulating programs take a long time to
execute. Why? How fast/slow can programs be?”

Algorithmic complexity
Does this motivate and engage
students?

Homework
assignments suggest
they were.


Shared on-line in
collaborative web
space (CoWeb)
Some students
reported writing
programs outside of
class for fun.
Does this motivate and engage
students?
Does this motivate and engage
students?
Soup
Stephen Hawking
Example Student Work
-Shared on the CoWeb Gallery
The author of this collage via
IM as soon as she was
done: “Well, I looked at last
years’ collages, and I
certainly can’t be beat.”
Relevance through Data-first
Computing


Real users come to a user with data
that they care about, then they
(unwillingly) learn the computer to
manipulate their data as they need.
“Media Computation” works like
that.

Students do use their own
pictures as starting points
for manipulations.


Starting in the second week of
the course!
Some students reversed
sounds looking for hidden
messages.
Results: Reduced Drop Rates
Enrollment
Women and
minorities succeed in
this class at the same
rate as men.
 10-25% each term
say that they’d like
an additional
computing class.

Success
Rate
Georgia Tech’s CS 1
2000 - 2002
930
71.2%
Spring 2003
120
90.0%
Fall 2003
303
86.5%
Spring 2004
395
86.9%
Summer 2004
120
73.3%
Fall 2004
366
80.3%
(average)
Media Computation
Results: Change in Attitudes
 On course relevance: “I dreaded CS, but ALL of the topics
thus far have been applicable to my future career (and
personal) plans—there isn't anything I don't like about this
class!”
 On creativity and social aspects in Computer Science: “I just
wish I had more time to play around with that and make neat
effects. But JES [course development environment] will be
on my computer forever, so… that’s the nice thing about this
class is that you could go as deep into the homework as you
wanted. So, I’d turn it in and then me and my roommate
would do more after to see what we could do with it.”
Results: Follow-on Email
Survey
19% had written a Python program on their own since the
class had ended.
27% had edited media that they hadn’t previously.
“Definitely makes me think of what is going on behind
the scenes of such programs like Photoshop and
Illustrator.”
“I feel more comfortable around computers and like I
could learn and understand other computer programming
languages more easily.”
Adoption Elsewhere
University of
Illinois-Chicago
University of
California, Santa
Barbara
Gainesville
College
DePauw
University
Enrollment
Success
Rate
Gainesville’s CSCI 1100
2000 - 2003
28
70.2%
Summer 2003
9
77.8%
Fall 2003
39
84.6%
Spring 2004
22
77.3%
Summer 2004
11
90.9%
(average)
Media Computation
A Multimedia CS2 in Java
 Follow-on course started Spring
2005: Representing Structure
and Behavior.
 NOT required!

75% female

A mix of African-American,
Hispanic, Native American
students.
 Teaching linked lists, trees,
stacks, and queues in a media
context.

Modeling, scene graphs,
linked lists of music
elements, etc.
Example Work from Follow-On
Class
Butterflies are digitally modified
copies of the original, inserted into
drawing
Next steps…
Moving beyond Undergrad

Teaching Georgia’s HS teachers how to teach
programming

Using a MediaComp approach for intro programming


After all, teachers are typically non-CS majors…
Helping the State certify CS teachers (for No Child Left
Behind Act), and lead to more CS Advanced Placement
teachers

Developing two workshops



From no-programming to teaching-programming in 2 weeks
From teaching-programming to teaching-Java-AP in 1 week
75 teachers during Summer 2004
Image Processing


We’re going to use Python as a kind of pseudocode.
Goals:


Give you the basic understanding of image
processing, including psychophysics of sight,
Identify some interesting examples to use
We perceive light different from
how it actually is

Color is continuous

Visible light is in the wavelengths between 370 and 730
nanometers


That’s 0.00000037 and 0.00000073 meters
But we perceive light with color sensors that peak around
425 nm (blue), 550 nm (green), and 560 nm (red).



Our brain figures out which color is which by figuring out how
much of each kind of sensor is responding
One implication: We perceive two kinds of “orange” — one that’s
spectral and one that’s red+yellow (hits our color sensors just right)
Dogs and other simpler animals have only two kinds of sensors

They do see color. Just less color.
Luminance vs. Color

We perceive borders of things,
motion, depth via luminance
Luminance is not the
amount of light, but our
perception of the amount
of light.
 We see blue as “darker”
than red, even if same
amount of light.


Much of our luminance
perception is based on
comparison to backgrounds,
not raw values.
Luminance is actually
color blind. Completely
different part of the brain.
Digitizing pictures as bunches
of little dots


We digitize pictures into lots of little dots
Enough dots and it looks like a continuous
whole to our eye




Our eye has limited resolution
Our background/depth acuity is particulary low
Each picture element is referred to as a pixel
Pixels are picture elements


Each pixel object knows its color
It also knows where it is in its picture
Encoding color


Each pixel encodes color at that position in the picture
Lots of encodings for color
Printers use CMYK: Cyan, Magenta, Yellow, and blacK.
 Others use HSB for Hue, Saturation, and Brightness (also
called HSV for Hue, Saturation, and Brightness


We’ll use the most common for computers

RGB: Red, Green, Blue
RGB

In RGB, each color has three
component colors:
Amount of redness
 Amount of greenness
 Amount of blueness



Each does appear as a separate
dot on most devices, but our
eye blends them.
In most computer-based
models of RGB, a single byte
(8 bits) is used for each

So a complete RGB color is
24 bits, 8 bits of each
Encoding RGB


Each component color (red,
green, and blue) is encoded as
a single byte
Colors go from (0,0,0) to
(255,255,255)

If all three components are
the same, the color is in
greyscale

(50,50,50) at (2,2)
(0,0,0) (at position (1,2) in
example) is black
 (255,255,255) is white

Basic Picture Functions



makePicture(filename) creates and returns a
picture object, from the JPEG file at the filename
show(picture) displays a picture in a window
We’ll learn functions for manipulating pictures
later, like getColor, setColor, and repaint
Writing a recipe:
Making our own functions





To make a function, use the
command def
Then, the name of the function,
and the names of the input values
between parentheses (“(input1)”)
End the line with a colon (“:”)
The body of the recipe is indented
(Hint: Use two spaces)
Your function does NOT
exist for JES until you
load it
Use a loop!
Our first picture recipe
def decreaseRed(picture):
for p in getPixels(picture):
value=getRed(p)
setRed(p,value*0.5)
Used like this:
>>> file="/Users/guzdial/mediasources/barbara.jpg"
>>> picture=makePicture(file)
>>> show(picture)
>>> decreaseRed(picture)
>>> repaint(picture)
def clearRed(picture):
for pixel in getPixels(picture):
setRed(pixel,0)
def greyscale(picture):
for p in getPixels(picture):
redness=getRed(p)
greenness=getGreen(p)
blueness=getBlue(p)
luminance=(redness+blueness+greenness)/3
setColor(p,
makeColor(luminance,luminance,luminance))
def negative(picture):
for px in getPixels(picture):
red=getRed(px)
green=getGreen(px)
blue=getBlue(px)
negColor=makeColor(255-red,255-green,255-blue)
setColor(px,negColor)
Increasing Red
def increaseRed(picture):
for p in getPixels(picture):
value=getRed(p)
setRed(p,value*1.2)
What happened here?!?
Remember that the limit
for redness is 255.
If you go beyond 255, all
kinds of weird things can
happen: Wrap around
Clearing Blue
def clearBlue(picture):
for p in getPixels(picture):
setBlue(p,0)
Creating a negative

Let’s think it through


R,G,B go from 0 to 255
Let’s say Red is 10. That’s very light red.



What’s the opposite? LOTS of Red!
The negative of that would be 245: 255-10
So, for each pixel, if we negate each color
component in creating a new color, we negate the
whole picture.
Recipe for creating a negative
def negative(picture):
for px in getPixels(picture):
red=getRed(px)
green=getGreen(px)
blue=getBlue(px)
negColor=makeColor( 255-red, 255-green, 255-blue)
setColor(px,negColor)
Converting to greyscale

We know that if red=green=blue, we get grey



But what value do we set all three to?
What we need is a value representing the darkness of the
color, the luminance
There are lots of ways of getting it, but one way that
works reasonably well is dirt simple—simply take the
average:
Converting to grayscale
def grayScale(picture):
for p in getPixels(picture):
intensity = (getRed(p)+getGreen(p)+getBlue(p))/3
setColor(p,makeColor(intensity,intensity,intensity))
Replacing colors using IF


We don’t have to do one-to-one changes or
replacements of color
We can use if to decide if we want to make a
change.



We could look for a range of colors, or one specific
color.
We could use an operation (like multiplication) to
set the new color, or we can set it to a specific
value.
It all depends on the effect that we want.
Posterizing: Reducing range of
colors
Posterizing: How we do it

We look for a range of colors, then map them to a
single color.





If red is between 63 and 128, set it to 95
If green is less than 64, set it to 31
...
It requires a lot of if statements, but it’s really
pretty simple.
The end result is that a bunch of different colors,
get set to a few colors.
Posterizing function
def posterize(picture):
#loop through the pixels
for p in getPixels(picture):
#get the RGB values
red = getRed(p)
green = getGreen(p)
blue = getBlue(p)
#check and set red values
if(red < 64):
setRed(p, 31)
if(red > 63 and red < 128):
setRed(p, 95)
if(red > 127 and red < 192):
setRed(p, 159)
if(red > 191 and red < 256):
setRed(p, 223)
#check and set green values
if(green < 64):
setGreen(p, 31)
if(green > 63 and green < 128):
setGreen(p, 95)
if(green > 127 and green < 192):
setGreen(p, 159)
if(green > 191 and green < 256):
setGreen(p, 223)
#check and set blue values
if(blue < 64):
setBlue(p, 31)
if(blue > 63 and blue < 128):
setBlue(p, 95)
if(blue > 127 and blue < 192):
setBlue(p, 159)
if(blue > 191 and blue < 256):
setBlue(p, 223)
Mirroring



Imagine a mirror horizontally across the picture,
or vertically
What would we see?
How do generate that digitally?

We simply copy the colors of pixels from one place
to another
Mirroring a picture


Slicing a picture down the middle and sticking a mirror on the slice
Do it by using a loop to measure a difference


The index variable is actually measuring distance from the
mirrorpoint
Then reference to either side of the mirror point using the difference
Recipe for mirroring
def mirrorVertical(source):
mirrorpoint = int(getWidth(source)/2)
for y in range(1,getHeight(source)):
for x in range(1,mirrorpoint):
p = getPixel(source, x+mirrorpoint,y)
p2 = getPixel(source, mirrorpoint-x,y)
c = getColor(p2)
setColor(p,c)
Can we do it with a horizontal
mirror?
def mirrorHorizontal(source):
mirrorpoint = int(getHeight(source)/2)
for y in range(1,mirrorpoint):
for x in range(1,getWidth(source)):
p = getPixel(source,x,y+mirrorpoint)
p2 = getPixel(source,x,mirrorpoint-y)
setColor(p,getColor(p2))
What if we wanted to copy bottom
to top?

Very simple: Swap the p and p2 in the bottom line

Copy from p to p2, instead of from p2 to p
def mirrorHorizontal(source):
mirrorpoint = int(getHeight(source)/2)
for y in range(1,mirrorpoint):
for x in range(1,getWidth(source)):
p = getPixel(source,x,y+mirrorpoint)
p2 = getPixel(source,x,mirrorpoint-y)
setColor(p2,getColor(p))
Doing something useful with
mirroring


Mirroring can be used to
create interesting effects,
but it can also be used to
create realistic effects.
Consider this image that
from a trip to Athens,
Greece.

Can we “repair” the
temple by mirroring the
complete part onto the
broken part?
Figuring out where to mirror

Use MediaTools to find the mirror point and the range
that we want to copy
Program to mirror the temple
def mirrorTemple():
source = makePicture(getMediaPath("temple.jpg"))
mirrorpoint = 277
setMediaPath() and
getMediaPath(baseName)
lengthToCopy = mirrorpoint - 14
allow us to set a media folder
for x in range(1,lengthToCopy):
for y in range(28,98):
p = getPixel(source,mirrorpoint-x,y)
p2 = getPixel(source,mirrorpoint+x,y)
setColor(p2,getColor(p))
show(source)
return source
Did it really work?


It clearly did the
mirroring, but that doesn’t
create a 100% realistic
image.
Check out the shadows:
Which direction is the sun
coming from?
Removing a background:
Chromakey

Have a background of a known
color
Some color that won’t be
on the person you want to
mask out
 Pure green or pure blue is
most often used
 I used my son’s blue
bedsheet


This is how the weather people
seem to be in front of a map—
they’re actually in front of a
blue sheet.
Chromakey recipe
def chromakey(source,bg):
# source should have something in front of blue, bg is the new background
for x in range(1,getWidth(source)):
for y in range(1,getHeight(source)):
p = getPixel(source,x,y)
# My definition of blue: If the redness + greenness < blueness
if (getRed(p) + getGreen(p) < getBlue(p)):
#Then, grab the color at the same spot from the new background
setColor(p,getColor(getPixel(bg,x,y)))
Example results
Sound Processing


Again, we’re going to use Python as a kind of
pseudo-code.
Goals:


Give you the basic understanding of audio
processing, including psycho-acoustics,
Identify some interesting examples to use.
How sound works:
Acoustics, the physics of sound

Sounds are waves of air
pressure
Sound comes in cycles
 The frequency of a wave is
the number of cycles per
second (cps), or Hertz



(Complex sounds have more
than one frequency in them.)
The amplitude is the
maximum height of the
wave
Volume and pitch:
Psychoacoustics, the psychology of
sound

Our perception of volume is related (logarithmically) to changes in
amplitude
If the amplitude doubles, it’s about a 3 decibel (dB) change.
 A decibel is a ratio between two intensities: 10 * log10(I1/I2)
 As an absolute measure, it’s in comparison to threshold of
audibility





0 dB can’t be heard.
Normal speech is 60 dB.
A shout is about 80 dB
Our perception of pitch is related (logarithmically) to changes in
frequency
Higher frequencies are perceived as higher pitches
 We can hear between 5 Hz and 20,000 Hz (20 kHz)
 A above middle C is 440 Hz

Digitizing Sound: How do we
get that into numbers?


Remember in calculus,
estimating the curve by
creating rectangles?
We can do the same to
estimate the sound curve
Analog-to-digital conversion
(ADC) will give us the
amplitude at an instant as a
number: a sample
 How many samples do we
need?

Nyquist Theorem


We need twice as many samples as the maximum
frequency in order to represent (and recreate, later) the
original sound.
The number of samples recorded per second is the
sampling rate

If we capture 8000 samples per second, the highest
frequency we can capture is 4000 Hz


That’s how phones work
If we capture more than 44,000 samples per second, we
capture everything that we can hear (max 22,000 Hz)

CD quality is 44,100 samples per second
Digitizing sound in the computer


Each sample is stored as a number (two bytes)
What’s the range of available combinations?
16 bits, 216 = 65,536
 But we want both positive and negative values


To indicate compressions and rarefactions.
What if we use one bit to indicate positive (0) or negative
(1)?
 That leaves us with 15 bits
 15 bits, 215 = 32,768
 One of those combinations will stand for zero



We’ll use a “positive” one, so that’s one less pattern for positives
Each sample can be between -32,768 and 32,767
Basic Sound Functions




makeSound(filename) creates and returns a sound
object, from the WAV file at the filename
play(sound) makes the sound play (but doesn’t
wait until it’s done)
blockingPlay(sound) waits for the sound to finish
We’ll learn more later like getSample and
setSample
Working with sounds

We’ll use pickAFile and makeSound as we have before.





But now we want .wav files
We’ll use getSamples to get all the sample objects out of
a sound
We can also get the value at any index with
getSampleValueAt
Sounds also know their length (getLength) and their
sampling rate (getSamplingRate)
Can save sounds with writeSoundTo(sound,”file.wav”)
Recipe to Increase the Volume
def increaseVolume(sound):
for sample in getSamples(sound):
value = getSample(sample)
setSample(sample,value * 2)
Using it:
>>> f="/Users/guzdial/mediasources/gettysburg10.wav"
>>> s=makeSound(f)
>>> increaseVolume(s)
>>> play(s)
>>> writeSoundTo(s,"/Users/guzdial/mediasources/louder-g10.wav")
Decreasing the volume
def decreaseVolume(sound):
for sample in getSamples(sound):
value = getSample(sample)
setSample(sample,value * 0.5)
This works just like
increaseVolume, but
we’re lowering each
sample by 50% instead of
doubling it.
Maximizing volume


How do we get maximal volume?
It’s a three-step process:


First, figure out the loudest sound (largest sample).
Next, figure out a multiplier needed to make that
sound fill the available space.
We want to solve for x where x * loudest = 32767
 So, x = 32767/loudest


Finally, multiply the multiplier times every sample
Maxing (normalizing) the sound
def normalize(sound):
largest = 0
for s in getSamples(sound):
largest = max(largest,getSample(s) )
multiplier = 32767.0 / largest
print "Largest sample value in original sound was", largest
print "Multiplier is", multiplier
for s in getSamples(sound):
louder = multiplier * getSample(s)
setSample(s,louder)
Increasing volume by sample
index
def increaseVolumeByRange(sound):
for sampleIndex in range(1,getLength(sound)+1):
value = getSampleValueAt(sound,sampleIndex)
setSampleValueAt(sound,sampleIndex,value * 2)
This really is the same as:
def increaseVolume(sound):
for sample in getSamples(sound):
value = getSample(sample)
setSample(sample,value * 2)
Recipe to play a sound
backwards
def backwards(filename):
source = makeSound(filename)
target = makeSound(filename)
sourceIndex = getLength(source)
for targetIndex in range(1,getLength(target)+1):
sourceValue = getSampleValueAt(source,sourceIndex)
setSampleValueAt(target,targetIndex,sourceValue)
sourceIndex = sourceIndex - 1
return target
Note use of return for
returning the processed sound
Recipe for halving the
frequency of a sound
def half(filename):
source = makeSound(filename)
target = makeSound(filename)
This is how a
sampling synthesizer
works!
sourceIndex = 1
for targetIndex in range(1, getLength( target)+1):
setSampleValueAt( target, targetIndex,
getSampleValueAt( source, int(sourceIndex)))
sourceIndex = sourceIndex + 0.5
Here are the
play(target)
return target
pieces that
do it
Splicing Sounds


Splicing gets its name from literally cutting and
pasting pieces of magnetic tape together
Doing it digitally is easy, but not short



We find where the end points of words are
We copy the samples into the right places to make
the words come out as we want them
(We can also change the volume of the words as we
move them, to increase or decrease emphasis and
make it sound more natural.)
Finding the word end-points

Using MediaTools and
play before/after cursor,
can figure out the index
numbers where each word
ends
The Whole Splice
def splicePreamble():
file = "/Users/guzdial/mediasources/preamble10.wav"
source = makeSound(file)
target = makeSound(file) # This will be the newly spliced sound
targetIndex=17408
# targetIndex starts at just after "We the" in the new sound
for sourceIndex in range( 33414, 40052): # Where the word "United" is in the sound
setSampleValueAt(target, targetIndex, getSampleValueAt( source, sourceIndex))
targetIndex = targetIndex + 1
for sourceIndex in range(17408, 26726): # Where the word "People" is in the sound
setSampleValueAt(target, targetIndex, getSampleValueAt( source, sourceIndex))
targetIndex = targetIndex + 1
for index in range(1,1000):
#Stick some quiet space after that
setSampleValueAt(target, targetIndex,0)
targetIndex = targetIndex + 1
play(target)
#Let's hear and return the result
return target
BREAK TIME!

15 minutes…
Media Manipulations in Java


Examples from Workshop slides.
Goal:


Demonstrate implementing same algorithms in
Java.
Provide techniques for dealing with Java-specific
issues while in a media context.
Copy Picture Algorithm

Copy a picture to the 7 by 9.5 inch blank picture




Create the target picture object
Invoke the method on the target picture
Create the source picture object
Loop through the source picture pixels
 Get
the source and target pixels
 Set the color of the target pixel to the color of
the source pixel
Copy Algorithm to Code

Loop through the source pixels
// loop through the columns
for (int sourceX = 0, targetX=0;
sourceX < sourcePicture.getWidth();
sourceX++, targetX++)
{
// loop through the rows
for (int sourceY = 0, targetY =0;
sourceY < sourcePicture.getHeight();
sourceY++, targetY++)
{
Copy Algorithm to Code – Cont

Get the source and target pixels
sourcePixel = sourcePicture.getPixel(sourceX,sourceY);
targetPixel = this.getPixel(targetX,targetY);

Set the color of the target pixel to the color
of the source pixel
targetPixel.setColor(sourcePixel.getColor());
Copy Method
public void copyKatie()
{
String sourceFile =
FileChooser.getMediaPath("KatieFancy.jpg");
Picture sourcePicture = new Picture(sourceFile);
Pixel sourcePixel = null;
Pixel targetPixel = null;
// loop through the columns
for (int sourceX = 0, targetX=0;
sourceX < sourcePicture.getWidth();
sourceX++, targetX++)
{
Copy Method - Continued
// loop through the rows
for (int sourceY = 0, targetY =0;
sourceY < sourcePicture.getHeight();
sourceY++, targetY++)
{
// set the target pixel color to the source pixel color
sourcePixel = sourcePicture.getPixel(sourceX,sourceY);
targetPixel = this.getPixel(targetX,targetY);
targetPixel.setColor(sourcePixel.getColor());
}
}
}
Trying the copyKatie Method

Create a picture object using the 7inX95in.jpg file in the
mediasources directory


Show the picture


p1.show();
Invoke the method on this picture object


Picture p1 = new
Picture(FileChooser.getMediaPath(“7inX95in.jpg”));
p1.copyKatie();
Repaint the picture

p1.repaint();
Result of copyKatie Method
Copy to an Upper Left Location

How would you copy a
picture to a location in
another picture (like 100,
100)?


Specified as the upper
left corner
You still copy all the
source pixels

But the target x and y
start at the specified
location
100, 100
Copy to Position Exercise

Copy the picture
robot.jpg
 To location


100, 100 in 7inx95in.jpg
Cropping

We can copy just part of a picture to a new picture



Just change the start and end source x and y values
to the desired values
Use picture.explore() to find the x and y values
What are the x and y values to get the face of the
girl in KatieFancy.jpg?
Copy Face Method
public void copyKatiesFace()
{
String sourceFile =
FileChooser.getMediaPath("KatieFancy.jpg");
Picture sourcePicture = new Picture(sourceFile);
Pixel sourcePixel = null;
Pixel targetPixel = null;
// loop through the columns
for (int sourceX = 70, targetX = 100;
sourceX < 135; sourceX++, targetX++)
{
// loop through the rows
for (int sourceY = 3, targetY = 100;
sourceY < 80; sourceY++, targetY++)
{
Copy Face Method - Continued
// set the target pixel color to the source pixel color
sourcePixel =
sourcePicture.getPixel(sourceX,sourceY);
targetPixel = this.getPixel(targetX,targetY);
targetPixel.setColor(sourcePixel.getColor());
}
}
}
Result of Copy Kaite’s Face

Create a picture object

Picture p1 = new Picture(FileChooser.getMediaPath(
“7inX95in.jpg”));

Show the picture


Invoke the method


p1.show();
p1.copyKatiesFace();
Repaint the picture

p1.repaint();
What makes a Good Method?

A method should do one and only one thing
Accomplish some task
 The name should tell you what it does


A method can call other methods to do some of the work


We shouldn’t copy code between methods


Procedural decomposition
We should make general methods that are reusable
A method should be in the class that has the data the
method is working on
Where the last two methods
general?

We specified the file to copy from in the method



Meaning we would need to change the method
or make another method
to copy a different picture
General Copy Algorithm

Create a method that copies pixels from a passed
source picture

Giving a start x and y and end x and y for the source
picture
If the start x and y and end x and y cover the entire
picture then the whole picture will be copied
 If the start x and y and end x and y are part of the
picture then cropping will occur


To the current picture object with a target start x
and target start y

If the start x and y are 0 then it copies to the upper left
corner
General Copy Algorithm

Loop through the x values between xStart and
xEnd (inclusive)

Loop through the y values between yStart and yEnd
(inclusive)
Get the pixel from the source picture for the current x
and y values
 Get the pixel from the target picture for the targetStartX
+ x and targetStartY + y values
 Set the color in the target pixel to the color in the source
pixel

General Copy Method
public void copy(Picture sourcePicture, int startX, int startY,
int endX, int endY, int targetStartX, int targetStartY)
{
Pixel sourcePixel = null;
Pixel targetPixel = null;
// loop through the x values
for (int x = startX, tx = targetStartX;
x < endX && x < sourcePicture.getWidth() && tx < this.getWidth();
x++, tx++)
{
// loop through the y values
for (int y = startY, ty = targetStartY;
y < endY && y < sourcePicture.getHeight() &&
ty < this.getHeight();
y++, ty++)
{
General Copy Method Continued
// copy the source color to the target color
sourcePixel = sourcePicture.getPixel(x,y);
targetPixel = this.getPixel(tx,ty);
targetPixel.setColor(sourcePixel.getColor());
}
}
}
Rewrite Methods Exercise



Type the copy method in
Picture.java
Rewrite copyKatie() and
copyKatiesFace()
methods to use the new
copy method
Run the methods to make
sure they still work
Left Rotation

To rotate an image left 90
degrees still copy all the
pixels

0
1
0
1
2
3
1
4
5
6
But they go to different
locations in the target



Column values become
row values
target x = source y
target y = source width -1
– source x
2
0
0
1
2
1
3
6
2
5
1
4
Left Rotation Algorithm




Create the target picture object
Invoke the method on the target picture
Create the source picture object
Loop through the source x (0-width-1)

Loop through the source y (0-height-1)
Get the source pixel at the x and y values
 Get the target pixel with the x equal the source y value
and the y equal the source picture width – 1 minus the
source x
 Copy the color from the source pixel to the target pixel

Left Rotation Method
public void copyKatieLeftRotation()
{
String sourceFile =
FileChooser.getMediaPath("KatieFancy.jpg");
Picture sourcePicture = new Picture(sourceFile);
Pixel sourcePixel = null;
Pixel targetPixel = null;
// loop through the columns
for (int sourceX = 0;
sourceX < sourcePicture.getWidth();
sourceX++)
{
Copy Katie Left Rotation
// loop through the rows
for (int sourceY = 0;
sourceY < sourcePicture.getHeight();
sourceY++)
{
// set the target pixel color to the source pixel color
sourcePixel =
sourcePicture.getPixel(sourceX,sourceY);
targetPixel = this.getPixel(sourceY,
sourcePicture.getWidth() - 1 - sourceX);
targetPixel.setColor(sourcePixel.getColor());
}
}
}
Right Rotation

To rotate an image right
90 degrees still copy all
the pixels

0
1
0
1
2
3
1
4
5
6
But they go to different
locations in the target



Column values become
row values
target y = source x
target x = source height –
1 – source y
2
0
0
1
2
1
4
1
5
2
6
3
Right Rotation Exercise



Write the method to rotate
the picture of Katie to the
right instead of to the left
Try out the method
Can you make the method
more general?

To work on any picture?
Scaling

You can make a picture smaller

Faster to download on the web

Increment the source x and y by a number larger than 1


Don’t use all the source pixels in target
You can make a picture larger

Show more detail

Copy the same source x and y to more than one target x
and y

Use source pixels more than once in target
Scaling Down the a Picture


passionFlower.jpg is
640pixels wide and 480
pixels high
If we copy every other
pixel we will have a new
picture with width (640 /
2 = 320) and height (480 /
2 = 240)
1
1
5
5
2
2
6
6
3
3
7
7
4
4
8
8
1 3
5 7
Scaling Down Algorithm




Create the target picture
Invoke the method on the target picture
Create the source picture
Loop with source x starting at 0 and target x starting at 0
as long as < source width
Increment the source x by 2 each time through the loop,
increment the target x by 1
 Loop with source y starting at 0 and target y starting at 0
as long as < source height


Increment the source y by 2 each time through the loop, increment
the target y by 1

Copy the color from the source to target pixel
Scaling Down Method
public void copyFlowerSmaller()
{
Picture flowerPicture =
new Picture(
FileChooser.getMediaPath(“passionFlower.jpg"));
Pixel sourcePixel = null;
Pixel targetPixel = null;
// loop through the columns
for (int sourceX = 0, targetX=0;
sourceX < flowerPicture.getWidth();
sourceX+=2, targetX++)
{
Scaling Down Method Continued
// loop through the rows
for (int sourceY=0, targetY=0;
sourceY < flowerPicture.getHeight();
sourceY+=2, targetY++)
{
sourcePixel =
flowerPicture.getPixel(sourceX,sourceY);
targetPixel = this.getPixel(targetX,targetY);
targetPixel.setColor(sourcePixel.getColor());
}
}
}
Trying Copy Flower Smaller

Create a new picture half the size of the original
picture (100,100) + 1


Copy the flower to the new picture


Picture p1 = new Picture(320,240);
p1.copyFlowerSmaller();
Show the result

p1.show();
Thinking Through Scaling Up

Copy each pixel in the
source multiple times to the
target








Source
Source
Source
Source
Source
Source
Source
Source
(0,0)
(0,0)
(1,0)
(1,0)
(2,0)
(2,0)
(0,0)
(0,0)
Target (0,0)
Target(1,0)
Target(2,0)
Target(3,0)
Target(4,0)
Target(5,0)
Target(0,1)
Target(1,1)
0
1
0
0
1
2
1
4
2
5
3
6
1
2
3
4
5
0
1 1 2 2 3 3
1
2
3
1 1 2 2 3 3
4 4 5 5 6 6
4 4 5 5 6 6
Scaling Up Algorithm




Create the target picture
Invoke the method on the target picture
Create the source picture
Loop with source x starting at 0 and target x starting at 0
as long as < source width
Increment the source x by 0.5 each time through the loop,
increment the target x by 1
 Loop with source y starting at 0 and target y starting at 0
as long as < source height


Increment the source y by 0.5 each time through the loop,
increment the target y by 1

Copy the color from the source to target pixel
Remove Red Eye


Red eye is when the flash
from the camera is
reflected from the
subject’s eyes
We want to change the red
color in the eyes to
another color

But not change the red
of her dress
Red Eye Algorithm

We can find the area around the eyes to limit
where we change the colors



Using picture.explore()
But we still just want to change the pixels that are
“close to” red.
We can find the distance between the current color
and our definition of red

And change the color of the current pixel only if the
current color is within some distance to the desired
color
Detailed Red Eye Algorithm

Loop with x staring at some passed start value and
while it is less than some passed end value

Loop with y starting at some passed start value and
while it is less than some passed end value
Get the pixel at this x and y
 Get the distance between the pixel color and red
 If the distance is less than some value (167) change the
color to some passed new color

Color Distance

The distance between two points is computed as


Square root of (( x1 – x2)2 + (y1 – y2)2)
The distance between two colors can be computed


Square root of ((red1 – red2)2 + (green1-green2)2 +
(blue1 – blue2)2)
There is a method in the Picture class to do this

public double getColorDistance(color1,color2)
Remove Red Eye Method
public void removeRedEye(int startX, int startY, int endX,
int endY, Color newColor)
{
Pixel pixel = null;
// loop through the pixels in the rectangle defined by the // startX, startY,
and
// endX and endY
for (int x = startX; x < endX; x++) {
for (int y = startY; y < endY; y++) {
// get the current pixel
pixel = getPixel(x,y);
Remove Red Eye Method
// if the color is near red then change it
if (pixel.colorDistance(Color.red) < 167)
pixel.setColor(newColor);
}
}
}
Edge Detection

Find the areas of high
contrast and turn pixels is
this area black

Turn all other pixels
white
Edge Detection Algorithm

To find areas of high contrast

Try to loop from row = 0 to row = height – 1

Loop from x = 0 to x = width





Get the pixel at the x and y (top pixel)
Get the pixel at the x and (y + 1) bottom pixel
Get the average of the top pixel color values
Get the average of the bottom pixel color values
If the absolute value of the difference between the
averages is over a passed limit
 Turn the pixel black
 Otherwise turn the pixel white
Use if and else for two
possibilities

Sometimes you want to do one thing if the
expression is true and a different thing if it is false
(like x > 128 and x <= 128)
int x = 24;
if (x > 128)
System.out.println(“x is > 128”);
else
System.out.println(“x <= 128”);
Edge Detection Exercise

Write a method edgeDetection that takes an input
limit


And turns all pixels black where the absolute value
of the difference between that pixel and the below
pixel is greater than the passed limit
And turns all pixels white where the absolute value
of the difference between that pixel and the below
pixel is less than or equal the passed limit
Sepia-Toned Pictures

Have a yellowish tint, used to make things look
old and western
Sepia-toned Algorithm




First make the picture grayscale.
Change the shadows (darkest grays) to be even
darker (0 <= red < 60)
Make the middle grays a brown color (60 <= red
< 190)
Make the highlights (lightest grays) a bit yellow
(190 <= red)


Increase red and green
Or decrease blue
Sepia-toned Method
public void sepiaTint()
{
Pixel pixel = null;
double redValue = 0;
double greenValue = 0;
double blueValue = 0;
// first change the current picture to grayscale
this.grayscale();
Sepia-toned Method - Cont
// loop through the pixels
for (int x = 0; x < this.getWidth(); x++)
{
for (int y = 0; y < this.getHeight(); y++)
{
// get the current pixel and color values
pixel = this.getPixel(x,y);
redValue = pixel.getRed();
greenValue = pixel.getGreen();
blueValue = pixel.getBlue();
Sepia-toned Method - Cont
// tint the shadows darker
if (redValue < 60)
{
redValue = redValue * 0.9;
greenValue = greenValue * 0.9;
blueValue = blueValue * 0.9;
}
// tint the midtones a light brown by reducing the blue
else if (redValue < 190)
{
blueValue = blueValue * 0.8;
}
Sepia-toned Method - Cont
// tint the highlights a light yellow
// by reducing the blue
else
{
blueValue = blueValue * 0.9;
}
// set the colors
pixel.setRed((int) redValue);
pixel.setGreen((int) greenValue);
pixel.setBlue((int) blueValue);
}
}
}
Chromakey – Blue Screen

For TV and movie special
effects they use a blue or
green screen
Here just a blue sheet
was used
 Professionally you need
an evenly lit, bright,
pure blue background


With nothing blue in the
scene
Chromakey Exercise

Write the method
chromakey that takes a
new background picture
as an input parameter



It will loop through all
the pixels
If the pixel color is blue
(red + green < blue)
Replace the pixel color
with the color from the
new background pixel
(at the same location)
OPTION 2: Turtles


Using turtles and turtle graphics to introduce
object-oriented programming
Works as a lead-in to a media approach.
Computers as Simulators

“The computer is the Proteus of machines. Its
essence is its universality, its power to simulate.
Because it can take on a thousand forms and serve
a thousand functions, it can appeal to a thousand
tastes.” Seymour Papert in Mindstorms
Creating a Simulation

Computers let us simulate things


We do this by creating models of the things we want
to simulate
We need to define what types of objects we will
want in our simulation and what they can do
Classes define the types and create objects of that type
 Objects act in the simulation

We will work with Turtle
Objects

We have to define what we mean by a Turtle to
the computer

We do this by writing a Turtle class definition


Turtle.java
We compile it to convert it into something the
computer can understand

Turtle.class
History of Turtles

Seymour Papert at MIT in the 60s

By teaching the computer to do something the kids
are thinking about thinking
Develop problem solving skills
 Learn by constructing and debugging something


Learn by making mistakes and fixing them
Using Turtles

Classes created at Georgia Tech


As part of a undergraduate class
Add bookClassesFinal to your classpath to use
these classes
Creating Objects in Java

In Java to create an object of a class you use
new Class(value, value, …);
 Starts with the new operator






Must be all lowercase

Usually starts with an uppercase letter

Above the 9 on the keyboard

There don’t have to be any values

Above the 0 on the keyboard

To the right of the L on the keyboard
followed by the class name
followed by an open parenthesis
Followed by any values used to set up the new object
Followed by a close parenthesis
Followed by a semicolon (to end the statement)
Creating Objects

If you just do


new World();
You will create a new World object
and it will display
But you will not have any way
to refer to it again
 Once you close the window the
object can be garbage
collected



The memory can be reused
We need a way to refer to the new
object

to be able to work with it again
Saving a Reference to an
Object

To save a reference to an object we need to
declare an object variable and set the variable to
reference a new object


Class name = new Class(value, value, …);
Examples


World world1 = new World();
Turtle turtle1 = new Turtle(world1);
Turtle Basics

The world starts off with a size
of 640 by 480
With no turtles
 World world1 = new
World();


The turtle starts off facing
north and in the center of the
world by default

You must pass a World
object when you create the
Turtle object


Or you will get an error
Turtle turtle1 = new
Turtle(world1);
Creating Several Objects

You can create several
World objects


World world2 = new
World();
You can create several
Turtle objects
Turtle turtle2 = new
Turtle(world2);
 Turtle turtle3 = new
Turtle(world2);
 One turtle is on top of
the other in the world

Moving a Turtle

Turtles can move forward
turtle3.forward();
 The default is to move
by



100 steps (pixels)
You can also tell the turtle
how far to move

turtle2.forward(50);
Turning a Turtle

Turtles can turn

Right



turtle3.turnRight();
turtle3.forward();
Left


turtle2.turnLeft();
turtle2.forward(50);
Turning a Turtle

Turtles can turn by a
specified amount

A positive number turns
the turtle the right



turtle3.turn(90);
turtle3.forward(100);
A negative number turns
the turtle to the left


turtle2.turn(-90);
turtle2.forward(70);
Draw a Square

You can draw a square by
doing








turtle1.forward(100);
turtle1.turnRight();
turtle1.forward(100);
turtle1.turnRight();
turtle1.forward(100);
turtle1.turnRight();
turtle1.forward(100);
turtle1.turnRight();
Challenge

Create a World object


Create a turtle object


Don’t forget to declare a
variable to hold a reference to
it
Don’t forget to declare a
variable to hold a reference to
it
Use the turtle to draw a
Rectangle (but, not a square)
 Diamond
 Hexagon

The Pen

Each turtle has a pen
The default is to have
the pen down to leave a
trail
 You can pick it up:





turtle1.penUp();
turtle1.turn(-90);
turtle1.forward(70);
You can put it down
again:


turtle1.penDown();
turtle1.forward(100);
Setting the Pen Width

You can change the width
of the trail the pen leaves
World world1 = new
World();
 Turtle turtle1 = new
Turtle(world1);
 turtle1.setPenWidth(5);
 turtle1.forward(100);

Setting the Pen Color

Use setPenColor to set the color of the pen


turtle1.setPenColor(java.awt.Color.RED);
There are several predefined colors

In the package java.awt



A package is a group of related classes
In the class Color
To use them you can use the full name

java.awt.Color.RED
Setting Colors

You can change the pen color


You can change the turtle color


turtle1.setColor(java.awt.Color.BLUE);
You can change the turtle’s body color


turtle.setPenColor(java.awt.Color.RED);
turtle1.setBodyColor(java.awt.Color.CYAN);
You can change the turtle’s shell color

turtle1.setShellColor(java.awt.Color.RED);
More Turtle Behaviors

Turtles can move to a
specific location


turtle1.moveTo(400,10);
Of course, you can create
many turtles and move
them all

Run TurtleTest

Creates 1000 turtles and
puts them in an array of
turtles. Has each turn by a
random amount from 0 to
359 and go forward by 100
Objects can Refuse

Turtles won’t move
completely out of the
boundaries of the world
World world2 = new
World();
 Turtle turtle2 = new
Turtle(world2);
 turtle2.forward(600);

Objects send Messages

Objects don’t “tell” each other what to do


They “ask” each other to do things
Objects can refuse to do what they are asked

The object must protect it’s data
Not let it get into an incorrect state
 A bank account object shouldn’t let you withdraw more
money that you have in the account

OPTION 3: CS2 in Java media
computation approach


Introducing lists and trees using Java
Focusing just on images here, but in the class, we
do music and sound, both lists and trees, too.

These slides appear in the class after doing linked
lists with music SongNodes.
Building a Scene

Computer graphics professionals work at two levels:
They define individual characters and effects on
characters in terms of pixels.
 But then most of their work is in terms of the scene:
Combinations of images (characters, effects on
characters).


To describe scenes, they often use linked lists and trees in
order to assemble the pieces.
Use an array?
> Picture [] myarray = new Picture[5];
> myarray[0]=new Picture(FileChooser.getMediaPath("katie.jpg"));
> myarray[1]=new Picture(FileChooser.getMediaPath("barbara.jpg"));
> myarray[2]=new Picture(FileChooser.getMediaPath("flower1.jpg"));
> myarray[3]=new Picture(FileChooser.getMediaPath("flower2.jpg"));
> myarray[4]=new Picture(FileChooser.getMediaPath("butterfly.jpg"));
> Picture background = new Picture(400,400)
> for (int i = 0; i < 5; i++)
{myarray[i].scale(0.5).compose(background,i*10,i*10);}
> background.show();
Yeah, we could. But:
• Inflexible
• Hard to insert, delete.
Using a linked list


Okay, so we’ll use a linked list.
But what should the ordering represent?

Version 1: Linearity


The order that things get drawn left-to-right.
Version 2: Layering

The order that things get drawn bottom-to-top
Version 1:
PositionedSceneElement
> FileChooser.setMediaPath("D:/cs1316/MediaSources/");
> PositionedSceneElement tree1 = new PositionedSceneElement(new
Picture(FileChooser.getMediaPath("tree-blue.jpg")));
> PositionedSceneElement tree2 = new PositionedSceneElement(new
Picture(FileChooser.getMediaPath("tree-blue.jpg")));
> PositionedSceneElement tree3 = new PositionedSceneElement(new
Picture(FileChooser.getMediaPath("tree-blue.jpg")));
> PositionedSceneElement doggy = new PositionedSceneElement(new
Picture(FileChooser.getMediaPath("dog-blue.jpg")));
> PositionedSceneElement house = new PositionedSceneElement(new
Picture(FileChooser.getMediaPath("house-blue.jpg")));
> Picture bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
> tree1.setNext(tree2); tree2.setNext(tree3); tree3.setNext(doggy); doggy.setNext(house);
> tree1.drawFromMeOn(bg);
> bg.show();
In this example, using
chromakey to compose..just
for the fun of it.
What this looks like:
Slightly different ordering:
Put the doggy between tree2 and
tree3
> tree3.setNext(house); tree2.setNext(doggy);
doggy.setNext(tree3);
> bg = new
Picture(FileChooser.getMediaPath("jungle.jpg"));
> tree1.drawFromMeOn(bg);
Yes, we can put
multiple
> bg.show();
statements in
one line.
Slightly different picture
PositionedSceneElement
public class PositionedSceneElement {
/**
* the picture that this element holds
**/
private Picture myPic;
/**
* the next element in the list
**/
private PositionedSceneElement next;
Pretty darn
similar to our
music linked lists!
Constructor
/**
* Make a new element with a picture as input, and
* next as null.
* @param heldPic Picture for element to hold
**/
public PositionedSceneElement(Picture heldPic){
myPic = heldPic;
next = null;
}
Linked list methods
/**
* Methods to set and get next elements
* @param nextOne next element in list
**/
public void setNext(PositionedSceneElement nextOne){
this.next = nextOne;
Again, darn
}
similar!
public PositionedSceneElement getNext(){
return this.next;
}
Traverse
the list
Traversing the
list in order to
draw the scene
is called
rendering the
scene: Realizing
the picture
described by the
data structure.
/**
* Method to draw from this node on in the list, using
bluescreen.
* Each new element has it's lower-left corner at the lowerright
* of the previous node. Starts drawing from left-bottom
* @param bg Picture to draw drawing on
**/
public void drawFromMeOn(Picture bg) {
PositionedSceneElement current;
int currentX=0, currentY = bg.getHeight()-1;
current = this;
while (current != null)
{
current.drawMeOn(bg,currentX, currentY);
currentX = currentX + current.getPicture().getWidth();
current = current.getNext();
}
}
Core of the Traversal
current = this;
while (current != null)
{
//Treat the next two lines as “blah blah blah”
current.drawMeOn(bg,currentX, currentY);
currentX = currentX +
current.getPicture().getWidth();
current = current.getNext();
}
Drawing the individual element
/**
* Method to draw from this picture, using bluescreen.
* @param bg Picture to draw drawing on
* @param left x position to draw from
* @param bottom y position to draw from
**/
private void drawMeOn(Picture bg, int left, int bottom) {
// Bluescreen takes an upper left corner
this.getPicture().bluescreen(bg,left,
bottom-this.getPicture().getHeight());
}
Generalizing

Reconsider these lines:
> tree3.setNext(house);
tree2.setNext(doggy);
doggy.setNext(tree3);

This is actually a general case of:


Removing the doggy from the list
Inserting it after tree2
Removing the doggy
> tree1.setNext(tree2);
tree2.setNext(tree3);
tree3.setNext(doggy);
doggy.setNext(house);
> tree1.remove(doggy);
> tree1.drawFromMeOn(bg);
Putting the mutt back
> bg = new
Picture(FileChooser.getMediaPath
("jungle.jpg"));
> tree1.insertAfter(doggy);
> tree1.drawFromMeOn(bg);
Removing an
element
from the list
/** Method to remove node from list, fixing links
appropriately.
* @param node element to remove from list.
**/
public void remove(PositionedSceneElement node){
if (node==this)
{
System.out.println("I can't remove the first node from
the list.");
return;
};
PositionedSceneElement current = this;
// While there are more nodes to consider
while (current.getNext() != null)
{
if (current.getNext() == node){
// Simply make node's next be this next
current.setNext(node.getNext());
// Make this node point to nothing
node.setNext(null);
return;
}
current = current.getNext();
}
}
Error checking and printing
/** Method to remove node from list, fixing links appropriately.
* @param node element to remove from list.
**/
public void remove(PositionedSceneElement node){
if (node==this)
{
System.out.println("I can't remove the first node from the list.");
return;
};
The Removal Loop
PositionedSceneElement current = this;
// While there are more nodes to consider
while (current.getNext() != null)
{ // Is this it?
if (current.getNext() == node){
// Simply make node's next be this next
current.setNext(node.getNext());
// Make this node point to nothing
node.setNext(null);
return;
}
current = current.getNext(); // If not, keep searching
}
We’re checking
getNext() because we
need to stop the step
before.
insertAfter
Think about what’s
involved in creating
insertBefore()…
/**
* Insert the input node after this node.
* @param node element to insert after this.
**/
public void
insertAfter(PositionedSceneElement
node){
// Save what "this" currently points at
PositionedSceneElement oldNext =
this.getNext();
this.setNext(node);
node.setNext(oldNext);
}
Animation = (Changing a
structure + rendering) * n



We can use what we just did to create animation.
Rather than think about animation as “a series of
frames,”
Think about it as:

Repeatedly:
Change a data structure
 Render (draw while traversing) the data structure to
create a frame

AnimatedPositionedScene
public class AnimatedPositionedScene {
/**
* A FrameSequence for storing the frames
**/
FrameSequence frames;
/**
* We'll need to keep track
* of the elements of the scene
**/
PositionedSceneElement tree1, tree2, tree3, house, doggy, doggyflip;
public void setUp(){
frames = new FrameSequence("D:/Temp/");
Setting up
the animation
FileChooser.setMediaPath("D:/cs1316/mediasources/");
Picture p = null; // Use this to fill elements
p = new Picture(FileChooser.getMediaPath("treeblue.jpg"));
tree1 = new PositionedSceneElement(p);
p = new Picture(FileChooser.getMediaPath("treeblue.jpg"));
tree2 = new PositionedSceneElement(p);
p = new Picture(FileChooser.getMediaPath("treeblue.jpg"));
tree3 = new PositionedSceneElement(p);
p = new Picture(FileChooser.getMediaPath("houseblue.jpg"));
house = new PositionedSceneElement(p);
p = new Picture(FileChooser.getMediaPath("dogblue.jpg"));
doggy = new PositionedSceneElement(p);
doggyflip = new PositionedSceneElement(p.flip());
}
Render the first frame
public void make(){
frames.show();
// First frame
Picture bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.setNext(doggy); doggy.setNext(tree2); tree2.setNext(tree3);
tree3.setNext(house);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
Render the doggy moving right
// Dog moving right
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggy);
tree2.insertAfter(doggy);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggy);
tree3.insertAfter(doggy);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggy);
house.insertAfter(doggy);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
Moving left
//Dog moving left
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggy);
house.insertAfter(doggyflip);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggyflip);
tree3.insertAfter(doggyflip);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggyflip);
tree2.insertAfter(doggyflip);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
bg = new Picture(FileChooser.getMediaPath("jungle.jpg"));
tree1.remove(doggyflip);
tree1.insertAfter(doggyflip);
tree1.drawFromMeOn(bg);
frames.addFrame(bg);
}
Results
Version 2: Layering
> Picture bg = new Picture(400,400);
> LayeredSceneElement tree1 = new LayeredSceneElement(
new Picture(FileChooser.getMediaPath("tree-blue.jpg")),10,10);
> LayeredSceneElement tree2 = new LayeredSceneElement(
new Picture(FileChooser.getMediaPath("tree-blue.jpg")),100,10);
> LayeredSceneElement tree3 = new LayeredSceneElement(
new Picture(FileChooser.getMediaPath("tree-blue.jpg")),200,100);
> LayeredSceneElement house = new LayeredSceneElement(
new Picture(FileChooser.getMediaPath("house-blue.jpg")),175,175);
> LayeredSceneElement doggy = new LayeredSceneElement(
new Picture(FileChooser.getMediaPath("dog-blue.jpg")),150,325);
> tree1.setNext(tree2); tree2.setNext(tree3); tree3.setNext(doggy);
doggy.setNext(house);
> tree1.drawFromMeOn(bg);
> bg.show();
First version of Layered Scene
Reordering the layering
> house.setNext(doggy); doggy.setNext(tree3);
tree3.setNext(tree2); tree2.setNext(tree1);
> tree1.setNext(null);
Basically, we’re
> bg = new Picture(400,400);
reversing the list
> house.drawFromMeOn(bg);
> bg.show();
Reordered (relayered) scene
Think about
what’s
involved in
creating a
method to
reverse() a
list…
What’s the difference?

If we were in PowerPoint or Visio, you’d say that
we changed the layering.




“Bring to front”
“Send to back”
“Bring forward”
“Send backward”
These commands are
actually changing the
ordering of the layers in the
list of things to be redrawn.
• Change the ordering in the
list.
• Render the scene
• Now it’s a different layering!
LayeredSceneElement
public class LayeredSceneElement {
/**
* the picture that this element holds
**/
private Picture myPic;
/**
* the next element in the list
**/
private LayeredSceneElement next;
/**
* The coordinates for this element
**/
private int x, y;
Constructor
/**
* Make a new element with a picture as input, and
* next as null, to be drawn at given x,y
* @param heldPic Picture for element to hold
* @param xpos x position desired for element
* @param ypos y position desired for element
**/
public LayeredSceneElement(Picture heldPic, int xpos, int ypos){
myPic = heldPic;
next = null;
x = xpos;
y = ypos;
}
Linked List methods
(We can sort of assume these now,
right?)
/**
* Methods to set and get next elements
* @param nextOne next element in list
**/
public void setNext(LayeredSceneElement nextOne){
this.next = nextOne;
}
public LayeredSceneElement getNext(){
return this.next;
}
Traversing
/**
* Method to draw from this node on in the list, using
bluescreen.
* Each new element has it's lower-left corner at the lower-right
* of the previous node. Starts drawing from left-bottom
* @param bg Picture to draw drawing on
**/
public void drawFromMeOn(Picture bg) {
LayeredSceneElement current;
current = this;
while (current != null)
{
current.drawMeOn(bg);
current = current.getNext();
}
}
/**
* Method to draw from this picture, using bluescreen.
* @param bg Picture to draw drawing on
**/
private void drawMeOn(Picture bg) {
this.getPicture().bluescreen(bg,x,y);
}
Linked list traversals are all the
same
current = this;
while (current != null)
{
current.drawMeOn(bg);
current = current.getNext();
}
New Version: Trees for defining
scenes

Not everything in a scene is a single list.
Think about a pack of fierce doggies, er, wolves attacking
the quiet village in the forest.
 Real scenes cluster.


Is it the responsibility of the elements to know about
layering and position?


Is that the right place to put that know how?
How do we structure operations to perform to sets of
nodes?

For example, moving a set of them at once?
The Attack of the Nasty
Wolvies
Closer…
Then the Hero Appears!
And the Wolvies retreat
What’s underlying this

This scene is described by a tree




Each picture is a BlueScreenNode in this tree.
Groups of pictures are organized in HBranch or
VBranch (Horizontal or Vertical branches)
The root of the tree is just a Branch.
The branches are positioned using a MoveBranch.
Labeling the Pieces
Branch (root)
MoveBranch to
(10,50)
VBranch with
BlueScreenNode
wolves
MoveBranch to
(10,400)
MoveBranch to
(300,450)
HBranch with 3
BSN houses and a
HBranch with BSN
trees
VBranch with 3
BSN houses
It’s a Tree
Branch (root)
MoveBranch to
(10,50)
MoveBranch to
(10,400)
MoveBranch to
(300,450)
HBranch with 3
BSN houses and a
VBranch with
BlueScreenNode
wolves
HBranch with BSN
trees
VBranch with 3
BSN houses
The Class Structure

DrawableNode knows only next, but knows how to do
everything that our picture linked lists do (insertAfter,
remove, last, drawOn(picture)).



Everything else is a subclass of that.
PictNode knows it’s Picture myPict and knows how to
drawWith(turtle) (by dropping a picture)
BlueScreenNode doesn’t know new from PictNode but
knows how to drawWith(turtle) by using bluescreen.
Branch Class Structure

Branch knows its children—a linked list of other nodes to
draw. It knows how to drawWith by:
(1) telling all its children to draw.
 (2) then telling all its children to draw.



A HBranch draws its children by spacing them out
horizontally.
A VBranch draws its children by spacing them out
vertically.
The Class Structure Diagram
Note: This is not
the same as the
scene (object)
structure!
DrawableNode
Knows: next
PictNode
Branch
Knows: myPict
Knows: children
Knows how to
drawWith
HBranch
VBranch
Knows how
to drawWith
horizontally
Knows how to
drawWith
vertically
BlueScreenNode
Knows how to
drawWith as
bluescreen
Rest of the implementation goes
here…

Note that there is a challenge and research
question here




The first trees that they’re seeing are scene graphs.
These are realistic (authentic), but more more
complex than traditional binary trees.
Does the motivational aspects overcome the
additional complexity?
Check back next year…
Download