CSCI 142 Principles of Computer Science

Barbara Nostrand, Ph.D.

Laboratory 6 - Simulating a Teleidoscope


The kaleidoscope was reinvented by Sir David Brewster in 1816 while conducting experiments on light polarization.  The initial design was made from a tube in which Brewster placed pairs of mirrors at one end,  and pairs of translucent disks at the other end.  Between the two,  he placed the beads.  A teleidoscope is a kind of kaleidoscope.  Unlike other kaleidoscopes,  teleidoscopes have a lens and an open view,  so they can be used to form kaleidoscopic patterns from objects outside the instrument,  rather than from items installed inside the tube. 

Subsequent to David Brewster,  mathematicians such as Coxeter developed a mathematical theory to describe its images and the kaleidoscope may have inspired such as Escher.  In this laboraty,  you will simulate a teleidoscope by reflecting across virtual mirrors with a fixed angle between each pair of adjacent mirrors.

So far we have explored arrays and lists.  While arrays are frequently static structures of fixed length,  lists can grow.  This time we are going to explore stacks which are dynamic structures of potentially unbounded capacity. In reality,  our computers are of finite capacity,  so the unbounded nature of stacks is really an abstraction. 

Programs which generate images similar to those seen in Kaleidoscopes are fairly popular.  While you will be imlementing a very different system,  you may be interested in Permadi's Kaleidoscope Painter

Getting Started

Create a new directory for the files of this exercise, and in it, save copies of the files

Implementing Stacks

Implement your Stack class as a linked list as shown in class,  and use the StackTester to test your stack.  Although the Stack class in the Java class library extends Vector,  you will implement the Stack class by extending your LinkedList class.  You will probably want to extend your LinkedList class.  You will inherit a number of the Stack methods from your LinkedList class. 

Constructor Summary
          Creates an empty Stack.
Method Summary
 void clear()
          Removes all of the elements from this Stack.  The Stack will be empty after this call returns (unless it throws an exception).  Inherited from LinkedList.
 void clone()
          Returns a shallow clone of this Stack.  The stored objects are not duplicated.  The copy will contain a reference to a clone of the internal LinkedList, not a reference to the original internal LinkedList of this Stack object.  Inherited from LinkedList.
 boolean empty()
          Tests if this stack is empty.
 boolean isEmpty()
          Tests whether the internal storage structure has any elements.  Inherited from LinkedList.
 Object peek()
          Looks at the object at the top of this stack without removing it from the stack.
 Object pop()
          Removes the object at the top of this stack and returns that object as the value of this function.
 Object push(Object item)
          Pushes an item onto the top of this stack.
 int search(Object o)
          Returns the 1-based position where an object is on this stack.
 int toArray()
          Returns an array containing all of the elements on this Stack in the correct order.  Inherited from LinkedList.
 int toArray(Object[] o)
          Returns an array containing all of the elements on this Stack in the correct order.  Inherited from LinkedList.
 String toString()
          Returns a string representation of this Stack, containing the String representation of each element.  Inherited from LinkedList.

Loading the Original Image

  1. Load an image using the same methods as for Laboratory 5.  Your BufferedImage should be called original
  2. Use the Math.min method to find the minimum of the height and the width of your image. 
  3. We will only be working with the biggest square that will fit into the BufferedImage. Calculate length of the diagonal of this square by applying the Pythagorean theorem to the minimum found in the previous step and save this new value in diameter
  4. Define a class ImageBuffer to hold scratchpad images while you work on them.  This buffer is used as a convenient scratchpad area,  and you are not expected to display the cotents of this buffer.  As an alternative,  you may choose to use a previously defined class of objects for this purpose.  If you want to draw the contents of this scratchpad,  you should either use a BufferedImage object or define your own class extension to BufferedImage
  5. Create an ImageBuffer object called picture whose dimensions are equal to the value of diameter calculated in the previous step.  Your ImageBuffer will consist of a vertical array of horizontal arrays of RGB pixel data along with diameter giving the length of the arrays. 
  6. Initialize the pixels in picture to BLACK.  This should be a zero for each of the color components.  Remember to maximize opacity. 
  7. Copy the cropped contents of your BufferedImage to picture with the centers aligned.  This should produce a copy the image data of a square in your BufferedImage surrounded by a black border.  Each horizontal array in the central square corresponds to the scan line in the BufferedImage.  Each element in a horizontal array will be an RGB pixel just like in the BufferedImage. 

Determine the Mirror Angle

We will determine the angle between adjacent virtual mirrors by asking the user to input the number mirrors.  The number of virtual mirrors must be an integer greater than or equal to zero.  The angle theta in radians between two adjacent virtual mirrors will be determined by θ = π/mirrors.  You should assign a value of zero to θ when mirrors equals zero.  The first virtual mirror will be located along the x axis while the other will be placed at an angle to the preceding virtual mirror.  These mirrors divide the image up intwo twice the number of segments.  For example,  one mirror will divide the image into two segments.  We will use a stack of arrays to simulate reflections.  We will actually use the Complex number angle to represent the mirror angle. 

Complex angle = new Complex(Math.cos(theta), Math.sin(theta));

Rotating an Image by θ

We rotate the image by imposing a complex coordinate system over our image. We will multiply the complex coordinates of each pixel in the destination by a uniform complex number of length equal to one representing rotation in the oposite direction to give us the complex coordinates of the source pixel.  The pixel in the source image is then transfered to the destination image.  We scan the destination image and not the source image in order to make sure that each of the destination's pixels receives a value. 

Reflecting Across the X-Axis

The x-Axis runs horizontally through the center of the image.  After reflecting across the x-Axis,  the bottom half of the image will look like a reflection of the top half of the image. 

Reflecting Across the Other Axis

Reflecting across a virtual mirror with angle phasor to the X-axis is a bit more complex.  One way to do this is as follows:

  1. Rotate the image clockwise by the desired angle.  This angle is simply the complex conjugate of phasor
  2. Reflect across the x-Axis. 
  3. Rotate the image counterclockwise by the desired angle.  This angle is maintained by phasor

Generating the Segments

The final image is produced by performing segments reflections corresponding to each of the angles generated by phasor which is generated as in Laboratory 1.  As in Laboratory 1,  the phasor is initialized to 1+0i.  Each subsequent phasor value is computed by multiplying phasor by angle.  You will perform a reflection for each of the virtual mirrors dividing your image.  Consequently,  if you ask for eight segments,  you will perform eight reflections one for each phasor angle. 

Viewing the Result

You will need to create and display a square BufferedImage called display with dimensions equal to those of the inner square in picture.  The pixel data in picture must be transfered display before it is viewed.  You may wish to display

Viewing Operation

The operation of varous parts of this program may be graphically interesting.  You should be able to view operation by repaiting.  However,  repainting will probably slow down operation considerably. 


Phrases you should now understand:


CollaborationYou will complete this project with your lab partner. 
ImplementationYou must implement your system as a Java application.  You may use Jgrasp.  Do not use netbeans or produce a Java applet. 
EnvironmentYour program must execute on the podium computer in our classroom.
Turn inYou must turn in both a report containing complete program sources and an executable electronic version of your program.

Last modified: 2007 MAR 19