SlideShare a Scribd company logo
Computer Graphics
in Java and Scala
Part 1
Continuous (Logical) and Discrete (Device) Coordinates
with a simple yet pleasing example involving concentric triangles
@philip_schwarz
slides by https://www.slideshare.net/pjschwarz
The idea of this series of decks is to have fun going through selected topics in books like
Computer Graphics for Java Programmers in order to
• learn (or reacquaint ourselves with) some well established computer graphics techniques
• see some of the Java code that the book uses to illustrate the techniques
• rewrite the code in Scala, hopefully encountering opportunities to use some functional
programming techniques
The subject of this first deck is
• the distinction between continuous (logical) and discrete (device) coordinates
• an example of using the technique to draw an interesting pattern involving triangles
https://www.linkedin.com/in/leen-ammeraal-b97b968/ https://profiles.utdallas.edu/kang.zhang
Leen Ammeraal Kang Zhang
@philip_schwarz
0 1 2 3 4 5 6 7 𝑥
0 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙
1 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙
2 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙
3 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙
𝑦
Fig 1.2 Pixels as
coordinates in a 8×4
canvas (with 𝐦𝐚𝐱𝐗 = 7
and 𝐦𝐚𝐱𝐘 = 3).
The following program lines in the paint method show how to obtain the canvas dimensions and how to
interpret them:
Dimension d = getSize();
int maxX = d.width – 1;
int maxY = d.height – 1;
The getSize method of Component (a superclass of Canvas) supplies us with the numbers of pixels on horizontal
and vertical lines of the canvas. Since we start counting at zero, the highest pixel numbers, maxX and maxY, on
these lines are one less than these numbers of pixels.
…
Figure 1.2 illustrates this for a very small canvas, which is only 8 pixels wide and 4 pixels high, showing a much
enlarged screen grid structure. It also shows that the line connecting the points 0,0 and 7,3 is approximated
by a set of eight pixels.
d.width = 8
d.height = 4
1.2 Logical Coordinates
The Direction of the y-axis
As Fig 1.2 shows, the origin of the device-coordinate systems lies at the top-left corner of the canvas, so that
the positive y-axis points downward.
This is reasonable for text output, that starts at the top and increases y as we go to the next line of text.
However, this direction of the y-axis is different from typical mathematical practice and therefore often
inconvenient in graphics applications.
For example, in a discussion about a line with a positive slope, we expect to go upward when moving along this
line from left to right.
Fortunately we can arrange for the positive y direction to be reversed by performing this simple
transformation:
𝑦! = 𝒎𝒂𝒙𝒀 − 𝑦
Fig 1.2
Continuous Versus Discrete Coordinates
Instead of the discrete (integer) coordinates at the lower, device-oriented level, we often wish to use
continuous (floating-point) coordinates at the higher, problem-oriented level. Other useful terms are device
and logical coordinates, respectively.
Writing conversion routines to compute device coordinates from the corresponding logical ones and vice versa
is a little bit tricky. We must be aware that there are two solutions to this problem: rounding and truncating,
even in the simple case in which increasing a logical coordinate by one results in increasing the device
coordinate also by one. We wish to write the following methods:
𝑖𝑋 𝑥 , 𝑖𝑌 𝑦 : 𝑐𝑜𝑛𝑣𝑒𝑟𝑡𝑖𝑛𝑔 𝑡ℎ𝑒 𝒍𝒐𝒈𝒊𝒄𝒂𝒍 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔 𝒙 𝑎𝑛𝑑 𝒚 𝑡𝑜 𝒅𝒆𝒗𝒊𝒄𝒆 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔;
𝑓𝑥 𝑋 , 𝑓𝑦 𝑌 : 𝑐𝑜𝑛𝑣𝑒𝑟𝑡𝑖𝑛𝑔 𝑡ℎ𝑒 𝒅𝒆𝒗𝒊𝒄𝒆 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔 𝑿 𝑎𝑛𝑑 𝒀 𝑡𝑜 𝒍𝒐𝒈𝒊𝒄𝒂𝒍 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔.
One may notice that we have used lower-case letters to represent logical coordinates and capital letters to
represent device coordinates. This will be the convention used throughout this book. With regard to x-
coordinates, the rounding solution could be:
int iX(float x) { return Math.round(x); }
float fx(int x) { return (float)x; }
For example, with this solution we have:
𝑖𝑋 2.8 = 3
𝑓𝑥 3 = 3.0
The i in 𝑖𝑋 is due to the function
returning an int. Similarly for
𝑓𝑥, which returns a float.
The truncating solution could be:
int iX(float x) { return (int)x; } // Not used in
float fx(int x) { return (float)x + 0.5F; } // this book
With these conversion functions, we would have
𝑖𝑋 2.8 = 2
𝑓𝑥 2 = 2.5
We will use the rounding solution throughout this book, since it is the better choice if logical coordinates
frequently happen to be integer values. In these cases the practice of truncating floating-point numbers will
often lead to worse results than those with rounding.
Apart from the above methods 𝑖𝑋 and 𝑓𝑥 (based on rounding), for x-coordinates, we need similar methods for
y-coordinates, taking into account the opposite direction of the two y-axes. At the bottom of the canvas, the
device y-coordinate is maxY, while the logical y-coordinate is 0, which may explain the two expressions of the
form maxY - … in the following methods:
int iX(float x) { return Math.round(x); }
int iY(float y) { return maxY - Math.round(y); }
float fx(int x) { return (float)x; }
float fy(int y) { return (float)(maxY - y); }
Figure 1.4 shows a fragment of a canvas, based on maxY=16.
The pixels are drawn as black dots, each placed at the center of a square of dashed lines, and the device coordinates
(X,Y) are placed between parentheses near each dot. For example, the pixel with device coordinates (8,2) at the
upper-right corner of this canvas fragment, has logical coordinates (8.0, 14.0). We have
iX(8.0) = Math.round(8.0) = 8
iY(14.0) = 16 - Math.round(14.0) = 2
fx(8) = (float)8 = 8.0
fy(2) = (float)(16 - 2) = 14.0
The dashed square around this dot denotes all points (x,y) satisfying:
7.5 ≤ 𝑥 < 8.5
13.5 ≤ 𝑦 < 14.5
All these points are converted to the pixel (8,2)
by our methods 𝑖𝑋 and 𝑖𝑌. Let us demonstrate
this way of converting floating-point logical
coordinates to integer device coordinates in a
program that begins by drawing an equilateral
triangle ABC, with the side AB at the bottom
and the point C at the top. Then using
q = 0.05
p = 1 − q = 0.95
We compute the new points A’, B’ and C’ near A, B and C
(5, 2) (6, 2) (7, 2) (8, 2)
(5, 3) (6, 3) (7, 3) (8, 3)
(5, 4) (6, 4) (7, 4) (8, 4)
5.0 6.0 7.0 8.0
14.0
13.0
12.0
Figure 1.4 Logical and device coordinates, based on maxY = 16
𝑦
𝑥
logical device
and lying on the sides AB, BC and CA respectively, writing:
xA1 = p * xA + q * xB;
yA1 = p * yA + q * yB;
xB1 = p * xB + q * xC;
yB1 = p * yB + q * yC;
xC1 = p * xC + q * xA;
yC1 = p * yC + q * yA;
We then draw the triangle A’B’C’, which is slightly smaller than ABC and turned a little counter-clockwise.
Applying the same principle to triangle A’B’C’ to obtain a third triangle A’’B’’C’’, and so on, until 50 triangles have
been drawn, the result will be as shown in Fig 1.5. If we change the dimensions of the window, new equilateral
triangles appear, again in the center of the canvas and with dimensions proportional to the size of this canvas.
Figure 1.5 Triangles, drawn inside each other
The next slide shows the Java
code for the whole program.
@philip_schwarz
import java.awt.*;
public class CvTriangles extends Canvas {
int maxX, maxY, minMaxXY, xCenter, yCenter;
void initgr() {
Dimension d = getSize();
maxX = d.width - 1; maxY = d.height - 1;
minMaxXY = Math.min(maxX, maxY);
xCenter = maxX / 2; yCenter = maxY / 2;
}
int iX(float x) { return Math.round(x); }
int iY(float y) { return maxY - Math.round(y); }
public void paint(Graphics g) {
initgr();
float side = 0.95F * minMaxXY, sideHalf = 0.5F * side,
h = sideHalf * (float) Math.sqrt(3),
xA, yA, xB, yB, xC, yC, xA1, yA1, xB1, yB1, xC1, yC1, p, q;
q = 0.05F; p = 1 - q;
xA = xCenter - sideHalf; yA = yCenter - 0.5F * h;
xB = xCenter + sideHalf; yB = yA;
xC = xCenter; yC = yCenter + 0.5F * h;
for (int i = 0; i < 50; i++) {
g.drawLine(iX(xA), iY(yA), iX(xB), iY(yB));
g.drawLine(iX(xB), iY(yB), iX(xC), iY(yC));
g.drawLine(iX(xC), iY(yC), iX(xA), iY(yA));
xA1 = p * xA + q * xB; yA1 = p * yA + q * yB;
xB1 = p * xB + q * xC; yB1 = p * yB + q * yC;
xC1 = p * xC + q * xA; yC1 = p * yC + q * yA;
xA = xA1; xB = xB1; xC = xC1;
yA = yA1; yB = yB1; yC = yC1;
}
}
}
// Triangles.java: This program draws 50
// triangles inside each other.
public class Triangles extends Frame {
public static void main(String[] args) {
new Triangles();
}
Triangles() {
super("Triangles: 50 triangles inside each other");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setSize(600, 400);
add("Center", new CvTriangles());
setVisible(true);
}
}
Without floating-point logical coordinates and
with a y-axis pointing downward, this program
would have been less easy to write.
Let’s rewrite that Java code in Scala, beginning with the code section which given a starting
triangle, draws the triangle and then proceeds to repeatedly, first shrink and twist the
triangle, and then draw it, thus generating and drawing 49 more concentric triangles.
While the Java code uses a java.awt.Canvas, the Scala code uses a javax.swing.JPanel.
class TrianglesPanel extends JPanel:
…
override def paintComponent(g: Graphics): Unit =
…
LazyList.iterate(triangle)(shrinkAndTwist).take(50).foreach(draw)
public class CvTriangles extends Canvas {
…
public void paint(Graphics g) {
…
for (int i = 0; i < 50; i++) {
g.drawLine(iX(xA), iY(yA), iX(xB), iY(yB));
g.drawLine(iX(xB), iY(yB), iX(xC), iY(yC));
g.drawLine(iX(xC), iY(yC), iX(xA), iY(yA));
xA1 = p * xA + q * xB; yA1 = p * yA + q * yB;
xB1 = p * xB + q * xC; yB1 = p * yB + q * yC;
xC1 = p * xC + q * xA; yC1 = p * yC + q * yA;
xA = xA1; xB = xB1; xC = xC1;
yA = yA1; yB = yB1; yC = yC1;
}
}
}
Given an initial triangle, we are going to generate a lazy,
potentially infinite, sequence of triangles, in which each
triangle, with the exception of the first one, is the result of
shrinking and twisting the previous triangle.
We then take (materialise) the first 50 triangles of the
sequence and iterate through them, drawing each one in turn.
draw: Triangle => Unit
shrinkAndTwist: Triangle => Triangle
A triangle consists of three logical points (points with logical coordinates) A, B and C.
case class Point(x: Float, y: Float)
case class Triangle(a: Point, b: Point, c: Point)
Drawing a triangle amounts to drawing lines AB, BC and CA.
val draw: Triangle => Unit =
case Triangle(a, b, c) =>
drawLine(a, b)
drawLine(b, c)
drawLine(c, a)
To draw a line from logical point A to logical point B, we first compute the coordinates of the corresponding
device points, and then pass those coordinates to the drawLine method provided by the Graphics object.
def drawLine(a: Point, b: Point): Unit =
val (ax,ay) = a.deviceCoords(panelHeight)
val (bx,by) = b.deviceCoords(panelHeight)
g.drawLine(ax, ay, bx, by)
Here is how we enrich a logical point with a deviceCoords function that takes the logical coordinates of the
point and computes the corresponding device coordinates:
extension (p: Point)
def deviceCoords(panelHeight: Int): (Int, Int) =
(Math.round(p.x), panelHeight - Math.round(p.y))
The deviceCoords function is our Scala equivalent of Java functions iX and iY	:
int iX(float x) { return Math.round(x); }
int iY(float y) { return maxY - Math.round(y); }
LazyList.iterate(triangle)(shrinkAndTwist).take(50).foreach(draw)
xA1 = p * xA + q * xB; yA1 = p * yA + q * yB;
xB1 = p * xB + q * xC; yB1 = p * yB + q * yC;
xC1 = p * xC + q * xA; yC1 = p * yC + q * yA;
xA = xA1; xB = xB1; xC = xC1;
yA = yA1; yB = yB1; yC = yC1;
val shrinkAndTwist: Triangle => Triangle =
val q = 0.05F
val p = 1 - q
def combine(a: Point, b: Point) = Point(p * a.x + q * b.x, p * a.y + q * b.y)
{ case Triangle(a,b,c) => Triangle(combine(a,b), combine(b,c), combine(c,a)) }
As for the Java code that shrinks and twists a triangle, in our
Scala code, we encapsulate it in function shrinkAndTwist.
q = 0.05F; p = 1 - q;
float …
xA, yA, xB, yB, xC, yC, xA1,
yA1, xB1, yB1, xC1, yC1,
p, q;
@philip_schwarz
float side = 0.95F * minMaxXY, sideHalf = 0.5F * side,
h = sideHalf * (float) Math.sqrt(3),
…;
int maxX, maxY, minMaxXY, xCenter, yCenter;
void initgr() {
Dimension d = getSize();
maxX = d.width - 1; maxY = d.height - 1;
minMaxXY = Math.min(maxX, maxY);
xCenter = maxX / 2; yCenter = maxY / 2;
}
val panelSize: Dimension = getSize()
val panelWidth = panelSize.width - 1
val panelHeight = panelSize.height - 1
val panelCentre = Point(panelWidth / 2, panelHeight / 2)
val triangleSide = 0.95F * Math.min(panelWidth, panelHeight)
val triangleHeight = (0.5F * triangleSide) * Math.sqrt(3).toFloat
val triangle = makeTriangle(panelCentre,
triangleSide,
triangleHeight)
xA = xCenter - sideHalf; yA = yCenter - 0.5F * h;
xB = xCenter + sideHalf; yB = yA;
xC = xCenter; yC = yCenter + 0.5F * h;
As for the Java code that computes the first triangle from the dimensions
of the Canvas on which it is going to be drawn, here it is, together with the
corresponding Scala code, which draws on a JPanel.
object Triangle:
def apply(centre: Point, side: Float, height: Float): Triangle =
val Point(x,y) = centre
val halfSide = 0.5F * side
val bottomLeft = Point(x - halfSide, y - 0.5F * height)
val bottomRight = Point(x + halfSide, y - 0.5F * height)
val top = Point(x, y + 0.5F * height )
Triangle(bottomLeft,bottomRight,top)
class Triangles:
JFrame.setDefaultLookAndFeelDecorated(true)
val frame = new JFrame("Triangles: 50 triangles inside each other")
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setSize(600, 400)
frame.add(TrianglesPanel())
frame.setVisible(true)
@main def main: Unit =
// Create a panel on the event dispatching thread
SwingUtilities.invokeLater(
new Runnable():
def run: Unit = Triangles()
)
public class Triangles extends Frame {
public static void main(String[] args) {
new Triangles();
}
Triangles() {
super("Triangles: 50 triangles inside each other");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setSize(600, 400);
add("Center", new CvTriangles());
setVisible(true);
}
}
And finally, let’s translate the rest of the
code, which creates the application’s frame.
Now let’s run the Scala program,
to verify that it works OK.
@philip_schwarz
Computer Graphics in Java and Scala - Part 1
Now let’s run the program again
after temporarily tweaking it so
that each side of a triangle is
drawn in a different colour.
Computer Graphics in Java and Scala - Part 1
And now let’s repeat that,
but with different colours
and a black background.
Computer Graphics in Java and Scala - Part 1
The next slide shows our Scala code in its
entirety, and the subsequent slide shows the
whole of the Java code again, for comparison.
class TrianglesPanel extends JPanel:
setBackground(Color.white)
override def paintComponent(g: Graphics): Unit =
super.paintComponent(g)
val panelSize: Dimension = getSize()
val panelWidth = panelSize.width - 1
val panelHeight = panelSize.height - 1
val panelCentre = Point(panelWidth / 2, panelHeight / 2)
val triangleSide: Float = 0.95F * Math.min(panelWidth, panelHeight)
val triangleHeight: Float = (0.5F * triangleSide) * Math.sqrt(3).toFloat
val shrinkAndTwist: Triangle => Triangle =
val q = 0.05F
val p = 1 - q
def combine(a: Point, b: Point) = Point(p * a.x + q * b.x, p * a.y + q * b.y)
{ case Triangle(a,b,c) => Triangle(combine(a,b), combine(b,c), combine(c,a)) }
def drawLine(a: Point, b: Point): Unit =
val (ax,ay) = a.deviceCoords(panelHeight)
val (bx,by) = b.deviceCoords(panelHeight)
g.drawLine(ax, ay, bx, by)
val draw: Triangle => Unit =
case Triangle(a, b, c) =>
drawLine(a, b)
drawLine(b, c)
drawLine(c, a)
val triangle = Triangle(panelCentre, triangleSide, triangleHeight)
LazyList.iterate(triangle)(shrinkAndTwist).take(50).foreach(draw)
class Triangles:
JFrame.setDefaultLookAndFeelDecorated(true)
val frame =
new JFrame("Triangles: 50 triangles inside each other")
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setSize(600, 400)
frame.add(TrianglesPanel())
frame.setVisible(true)
@main def main: Unit =
// Create a panel on the event dispatching thread
SwingUtilities.invokeLater(
new Runnable():
def run: Unit = Triangles()
)
case class Point(x: Float, y: Float)
extension (p: Point)
def deviceCoords(panelHeight: Int): (Int, Int) =
(Math.round(p.x), panelHeight - Math.round(p.y))
object Triangle:
def apply(centre: Point, side: Float, height: Float): Triangle =
val Point(x,y) = centre
val halfSide = 0.5F * side
val bottomLeft = Point(x - halfSide, y - 0.5F * height)
val bottomRight = Point(x + halfSide, y - 0.5F * height)
val top = Point(x, y + 0.5F * height )
Triangle(bottomLeft,bottomRight,top)
case class Triangle(a: Point, b: Point, c: Point)
public class CvTriangles extends Canvas {
int maxX, maxY, minMaxXY, xCenter, yCenter;
void initgr() {
Dimension d = getSize();
maxX = d.width - 1; maxY = d.height - 1;
minMaxXY = Math.min(maxX, maxY);
xCenter = maxX / 2; yCenter = maxY / 2;
}
int iX(float x) { return Math.round(x); }
int iY(float y) { return maxY - Math.round(y); }
public void paint(Graphics g) {
initgr();
float side = 0.95F * minMaxXY, sideHalf = 0.5F * side,
h = sideHalf * (float) Math.sqrt(3),
xA, yA, xB, yB, xC, yC, xA1, yA1, xB1, yB1, xC1, yC1, p, q;
q = 0.05F; p = 1 - q;
xA = xCenter - sideHalf; yA = yCenter - 0.5F * h;
xB = xCenter + sideHalf; yB = yA;
xC = xCenter; yC = yCenter + 0.5F * h;
for (int i = 0; i < 50; i++) {
g.drawLine(iX(xA), iY(yA), iX(xB), iY(yB));
g.drawLine(iX(xB), iY(yB), iX(xC), iY(yC));
g.drawLine(iX(xC), iY(yC), iX(xA), iY(yA));
xA1 = p * xA + q * xB; yA1 = p * yA + q * yB;
xB1 = p * xB + q * xC; yB1 = p * yB + q * yC;
xC1 = p * xC + q * xA; yC1 = p * yC + q * yA;
xA = xA1; xB = xB1; xC = xC1;
yA = yA1; yB = yB1; yC = yC1;
}
}
}
// Triangles.java: This program draws 50
// triangles inside each other.
public class Triangles extends Frame {
public static void main(String[] args) {
new Triangles();
}
Triangles() {
super("Triangles: 50 triangles inside each other");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setSize(600, 400);
add("Center", new CvTriangles());
setVisible(true);
}
}
That’s all for now.
See you in Part 2.
@philip_schwarz

More Related Content

What's hot (20)

PPTX
Java Logging
Zeeshan Bilal
 
PDF
Arrays in Java
Naz Abdalla
 
PPTX
How Hashmap works internally in java
Ramakrishna Joshi
 
ODP
Java Collections
parag
 
PDF
Java 8 features
NexThoughts Technologies
 
PDF
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz
 
PDF
Quick flask an intro to flask
juzten
 
PDF
Introduction to NumPy (PyData SV 2013)
PyData
 
PPSX
Java String class
DrRajeshreeKhande
 
PPTX
Dynamic method dispatch
yugandhar vadlamudi
 
PPTX
JavaScript Promises
L&T Technology Services Limited
 
PDF
The New JavaScript: ES6
Rob Eisenberg
 
PDF
Java Garbage Collection - How it works
Mindfire Solutions
 
PDF
De Java 8 a Java 17
Víctor Leonel Orozco López
 
PPTX
Session tracking in servlets
vishal choudhary
 
PPTX
Introduction to matplotlib
Piyush rai
 
PPTX
Introduction to spring boot
Santosh Kumar Kar
 
Java Logging
Zeeshan Bilal
 
Arrays in Java
Naz Abdalla
 
How Hashmap works internally in java
Ramakrishna Joshi
 
Java Collections
parag
 
Java 8 features
NexThoughts Technologies
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz
 
Quick flask an intro to flask
juzten
 
Introduction to NumPy (PyData SV 2013)
PyData
 
Java String class
DrRajeshreeKhande
 
Dynamic method dispatch
yugandhar vadlamudi
 
JavaScript Promises
L&T Technology Services Limited
 
The New JavaScript: ES6
Rob Eisenberg
 
Java Garbage Collection - How it works
Mindfire Solutions
 
De Java 8 a Java 17
Víctor Leonel Orozco López
 
Session tracking in servlets
vishal choudhary
 
Introduction to matplotlib
Piyush rai
 
Introduction to spring boot
Santosh Kumar Kar
 

Similar to Computer Graphics in Java and Scala - Part 1 (20)

PPT
JavaYDL13
Terry Yoast
 
PDF
Computer Graphics in Java and Scala - Part 1b
Philip Schwarz
 
PPT
13slide graphics
Dorothea Chaffin
 
PDF
Cg lab cse-v (1) (1)
Surya Sukumaran
 
PPTX
A graphic library and an application for simple curve manipolation
graphitech
 
PPTX
Computer Graphic - computer secince soft
engahmedeg94
 
PPTX
CHAPTER 4 &5 geoand trans.pptxGeometry chapter 4 and 5 transformation ,transl...
Antenehsolomon2
 
DOCX
Graphic Design Lab File.docx
PayalJindal19
 
PDF
Darkonoid
graphitech
 
PDF
Open GL 09 scan conversion
Roziq Bahtiar
 
DOCX
Computer Graphics Lab File C Programs
Kandarp Tiwari
 
PDF
A graphic library and an application for simple curve manipolation
graphitech
 
PDF
Computer graphics lab report with code in cpp
Alamgir Hossain
 
PDF
Shi.pdf
nikitawadhwani14
 
PDF
computer graphic and multimedia for the students of MCA
RavinderKSingla
 
PPTX
B. SC CSIT Computer Graphics Lab By Tekendra Nath Yogi
Tekendra Nath Yogi
 
DOC
Java applet handouts
iamkim
 
PPTX
Computer Graphics Unit 1
aravindangc
 
DOCX
Computer graphics lab assignment
Abdullah Al Shiam
 
JavaYDL13
Terry Yoast
 
Computer Graphics in Java and Scala - Part 1b
Philip Schwarz
 
13slide graphics
Dorothea Chaffin
 
Cg lab cse-v (1) (1)
Surya Sukumaran
 
A graphic library and an application for simple curve manipolation
graphitech
 
Computer Graphic - computer secince soft
engahmedeg94
 
CHAPTER 4 &5 geoand trans.pptxGeometry chapter 4 and 5 transformation ,transl...
Antenehsolomon2
 
Graphic Design Lab File.docx
PayalJindal19
 
Darkonoid
graphitech
 
Open GL 09 scan conversion
Roziq Bahtiar
 
Computer Graphics Lab File C Programs
Kandarp Tiwari
 
A graphic library and an application for simple curve manipolation
graphitech
 
Computer graphics lab report with code in cpp
Alamgir Hossain
 
computer graphic and multimedia for the students of MCA
RavinderKSingla
 
B. SC CSIT Computer Graphics Lab By Tekendra Nath Yogi
Tekendra Nath Yogi
 
Java applet handouts
iamkim
 
Computer Graphics Unit 1
aravindangc
 
Computer graphics lab assignment
Abdullah Al Shiam
 
Ad

More from Philip Schwarz (20)

PDF
Folding Cheat Sheet Series Titles - a series of 9 decks
Philip Schwarz
 
PDF
Folding Cheat Sheet # 9 - List Unfolding 𝑢𝑛𝑓𝑜𝑙𝑑 as the Computational Dual of ...
Philip Schwarz
 
PDF
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Part 4 - Interactive and Animated Dragon Creation
Philip Schwarz
 
PDF
The Nature of Complexity in John Ousterhout’s Philosophy of Software Design
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Part 3 - Simplification Through Separation of Con...
Philip Schwarz
 
PDF
The Open-Closed Principle - Part 2 - The Contemporary Version - An Introduction
Philip Schwarz
 
PDF
The Open-Closed Principle - Part 1 - The Original Version
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Part II - Recursive Function Simplification - Fro...
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Recursive Function Rewrite - From Imperative Styl...
Philip Schwarz
 
PDF
Fibonacci Function Gallery - Part 2 - One in a series
Philip Schwarz
 
PDF
Fibonacci Function Gallery - Part 1 (of a series) - with minor corrections
Philip Schwarz
 
PDF
Fibonacci Function Gallery - Part 1 (of a series)
Philip Schwarz
 
PDF
The Debt Metaphor - Ward Cunningham in his 2009 YouTube video
Philip Schwarz
 
PDF
Folding Cheat Sheet Series Titles (so far)
Philip Schwarz
 
PDF
From Subtype Polymorphism To Typeclass-based Ad hoc Polymorphism - An Example
Philip Schwarz
 
PDF
Folding Cheat Sheet #8 - eighth in a series
Philip Schwarz
 
PDF
Function Applicative for Great Good of Leap Year Function
Philip Schwarz
 
PDF
Folding Cheat Sheet #7 - seventh in a series
Philip Schwarz
 
PDF
Folding Cheat Sheet #6 - sixth in a series
Philip Schwarz
 
Folding Cheat Sheet Series Titles - a series of 9 decks
Philip Schwarz
 
Folding Cheat Sheet # 9 - List Unfolding 𝑢𝑛𝑓𝑜𝑙𝑑 as the Computational Dual of ...
Philip Schwarz
 
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
Drawing Heighway’s Dragon - Part 4 - Interactive and Animated Dragon Creation
Philip Schwarz
 
The Nature of Complexity in John Ousterhout’s Philosophy of Software Design
Philip Schwarz
 
Drawing Heighway’s Dragon - Part 3 - Simplification Through Separation of Con...
Philip Schwarz
 
The Open-Closed Principle - Part 2 - The Contemporary Version - An Introduction
Philip Schwarz
 
The Open-Closed Principle - Part 1 - The Original Version
Philip Schwarz
 
Drawing Heighway’s Dragon - Part II - Recursive Function Simplification - Fro...
Philip Schwarz
 
Drawing Heighway’s Dragon - Recursive Function Rewrite - From Imperative Styl...
Philip Schwarz
 
Fibonacci Function Gallery - Part 2 - One in a series
Philip Schwarz
 
Fibonacci Function Gallery - Part 1 (of a series) - with minor corrections
Philip Schwarz
 
Fibonacci Function Gallery - Part 1 (of a series)
Philip Schwarz
 
The Debt Metaphor - Ward Cunningham in his 2009 YouTube video
Philip Schwarz
 
Folding Cheat Sheet Series Titles (so far)
Philip Schwarz
 
From Subtype Polymorphism To Typeclass-based Ad hoc Polymorphism - An Example
Philip Schwarz
 
Folding Cheat Sheet #8 - eighth in a series
Philip Schwarz
 
Function Applicative for Great Good of Leap Year Function
Philip Schwarz
 
Folding Cheat Sheet #7 - seventh in a series
Philip Schwarz
 
Folding Cheat Sheet #6 - sixth in a series
Philip Schwarz
 
Ad

Recently uploaded (20)

PDF
Telemedicine App Development_ Key Factors to Consider for Your Healthcare Ven...
Mobilityinfotech
 
PPTX
IObit Uninstaller Pro 14.3.1.8 Crack Free Download 2025
sdfger qwerty
 
PDF
Mastering VPC Architecture Build for Scale from Day 1.pdf
Devseccops.ai
 
PDF
Automated Test Case Repair Using Language Models
Lionel Briand
 
PDF
Why Edge Computing Matters in Mobile Application Tech.pdf
IMG Global Infotech
 
PDF
Azure AI Foundry: The AI app and agent factory
Maxim Salnikov
 
PPTX
Avast Premium Security crack 25.5.6162 + License Key 2025
HyperPc soft
 
PDF
OpenChain Webinar - AboutCode - Practical Compliance in One Stack – Licensing...
Shane Coughlan
 
PPTX
IObit Driver Booster Pro 12 Crack Latest Version Download
pcprocore
 
PPTX
Iobit Driver Booster Pro 12 Crack Free Download
chaudhryakashoo065
 
PDF
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
PDF
Designing Accessible Content Blocks (1).pdf
jaclynmennie1
 
PPTX
IDM Crack with Internet Download Manager 6.42 Build 41 [Latest 2025]
pcprocore
 
PPTX
IDM Crack with Internet Download Manager 6.42 [Latest 2025]
HyperPc soft
 
PDF
Alur Perkembangan Software dan Jaringan Komputer
ssuser754303
 
PPTX
Wondershare Filmora Crack 14.5.18 + Key Full Download [Latest 2025]
HyperPc soft
 
PPTX
Foundations of Marketo Engage - Programs, Campaigns & Beyond - June 2025
BradBedford3
 
PDF
AI Software Development Process, Strategies and Challenges
Net-Craft.com
 
PDF
The Next-Gen HMIS Software AI, Blockchain & Cloud for Housing.pdf
Prudence B2B
 
PDF
Best Practice for LLM Serving in the Cloud
Alluxio, Inc.
 
Telemedicine App Development_ Key Factors to Consider for Your Healthcare Ven...
Mobilityinfotech
 
IObit Uninstaller Pro 14.3.1.8 Crack Free Download 2025
sdfger qwerty
 
Mastering VPC Architecture Build for Scale from Day 1.pdf
Devseccops.ai
 
Automated Test Case Repair Using Language Models
Lionel Briand
 
Why Edge Computing Matters in Mobile Application Tech.pdf
IMG Global Infotech
 
Azure AI Foundry: The AI app and agent factory
Maxim Salnikov
 
Avast Premium Security crack 25.5.6162 + License Key 2025
HyperPc soft
 
OpenChain Webinar - AboutCode - Practical Compliance in One Stack – Licensing...
Shane Coughlan
 
IObit Driver Booster Pro 12 Crack Latest Version Download
pcprocore
 
Iobit Driver Booster Pro 12 Crack Free Download
chaudhryakashoo065
 
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
Designing Accessible Content Blocks (1).pdf
jaclynmennie1
 
IDM Crack with Internet Download Manager 6.42 Build 41 [Latest 2025]
pcprocore
 
IDM Crack with Internet Download Manager 6.42 [Latest 2025]
HyperPc soft
 
Alur Perkembangan Software dan Jaringan Komputer
ssuser754303
 
Wondershare Filmora Crack 14.5.18 + Key Full Download [Latest 2025]
HyperPc soft
 
Foundations of Marketo Engage - Programs, Campaigns & Beyond - June 2025
BradBedford3
 
AI Software Development Process, Strategies and Challenges
Net-Craft.com
 
The Next-Gen HMIS Software AI, Blockchain & Cloud for Housing.pdf
Prudence B2B
 
Best Practice for LLM Serving in the Cloud
Alluxio, Inc.
 

Computer Graphics in Java and Scala - Part 1

  • 1. Computer Graphics in Java and Scala Part 1 Continuous (Logical) and Discrete (Device) Coordinates with a simple yet pleasing example involving concentric triangles @philip_schwarz slides by https://www.slideshare.net/pjschwarz
  • 2. The idea of this series of decks is to have fun going through selected topics in books like Computer Graphics for Java Programmers in order to • learn (or reacquaint ourselves with) some well established computer graphics techniques • see some of the Java code that the book uses to illustrate the techniques • rewrite the code in Scala, hopefully encountering opportunities to use some functional programming techniques The subject of this first deck is • the distinction between continuous (logical) and discrete (device) coordinates • an example of using the technique to draw an interesting pattern involving triangles https://www.linkedin.com/in/leen-ammeraal-b97b968/ https://profiles.utdallas.edu/kang.zhang Leen Ammeraal Kang Zhang @philip_schwarz
  • 3. 0 1 2 3 4 5 6 7 𝑥 0 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙ 1 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙ 2 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙ 3 ˙ ˙ ˙ ˙ ˙ ˙ ˙ ˙ 𝑦 Fig 1.2 Pixels as coordinates in a 8×4 canvas (with 𝐦𝐚𝐱𝐗 = 7 and 𝐦𝐚𝐱𝐘 = 3). The following program lines in the paint method show how to obtain the canvas dimensions and how to interpret them: Dimension d = getSize(); int maxX = d.width – 1; int maxY = d.height – 1; The getSize method of Component (a superclass of Canvas) supplies us with the numbers of pixels on horizontal and vertical lines of the canvas. Since we start counting at zero, the highest pixel numbers, maxX and maxY, on these lines are one less than these numbers of pixels. … Figure 1.2 illustrates this for a very small canvas, which is only 8 pixels wide and 4 pixels high, showing a much enlarged screen grid structure. It also shows that the line connecting the points 0,0 and 7,3 is approximated by a set of eight pixels. d.width = 8 d.height = 4
  • 4. 1.2 Logical Coordinates The Direction of the y-axis As Fig 1.2 shows, the origin of the device-coordinate systems lies at the top-left corner of the canvas, so that the positive y-axis points downward. This is reasonable for text output, that starts at the top and increases y as we go to the next line of text. However, this direction of the y-axis is different from typical mathematical practice and therefore often inconvenient in graphics applications. For example, in a discussion about a line with a positive slope, we expect to go upward when moving along this line from left to right. Fortunately we can arrange for the positive y direction to be reversed by performing this simple transformation: 𝑦! = 𝒎𝒂𝒙𝒀 − 𝑦 Fig 1.2
  • 5. Continuous Versus Discrete Coordinates Instead of the discrete (integer) coordinates at the lower, device-oriented level, we often wish to use continuous (floating-point) coordinates at the higher, problem-oriented level. Other useful terms are device and logical coordinates, respectively. Writing conversion routines to compute device coordinates from the corresponding logical ones and vice versa is a little bit tricky. We must be aware that there are two solutions to this problem: rounding and truncating, even in the simple case in which increasing a logical coordinate by one results in increasing the device coordinate also by one. We wish to write the following methods: 𝑖𝑋 𝑥 , 𝑖𝑌 𝑦 : 𝑐𝑜𝑛𝑣𝑒𝑟𝑡𝑖𝑛𝑔 𝑡ℎ𝑒 𝒍𝒐𝒈𝒊𝒄𝒂𝒍 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔 𝒙 𝑎𝑛𝑑 𝒚 𝑡𝑜 𝒅𝒆𝒗𝒊𝒄𝒆 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔; 𝑓𝑥 𝑋 , 𝑓𝑦 𝑌 : 𝑐𝑜𝑛𝑣𝑒𝑟𝑡𝑖𝑛𝑔 𝑡ℎ𝑒 𝒅𝒆𝒗𝒊𝒄𝒆 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔 𝑿 𝑎𝑛𝑑 𝒀 𝑡𝑜 𝒍𝒐𝒈𝒊𝒄𝒂𝒍 𝒄𝒐𝒐𝒓𝒅𝒊𝒏𝒂𝒕𝒆𝒔. One may notice that we have used lower-case letters to represent logical coordinates and capital letters to represent device coordinates. This will be the convention used throughout this book. With regard to x- coordinates, the rounding solution could be: int iX(float x) { return Math.round(x); } float fx(int x) { return (float)x; } For example, with this solution we have: 𝑖𝑋 2.8 = 3 𝑓𝑥 3 = 3.0 The i in 𝑖𝑋 is due to the function returning an int. Similarly for 𝑓𝑥, which returns a float.
  • 6. The truncating solution could be: int iX(float x) { return (int)x; } // Not used in float fx(int x) { return (float)x + 0.5F; } // this book With these conversion functions, we would have 𝑖𝑋 2.8 = 2 𝑓𝑥 2 = 2.5 We will use the rounding solution throughout this book, since it is the better choice if logical coordinates frequently happen to be integer values. In these cases the practice of truncating floating-point numbers will often lead to worse results than those with rounding. Apart from the above methods 𝑖𝑋 and 𝑓𝑥 (based on rounding), for x-coordinates, we need similar methods for y-coordinates, taking into account the opposite direction of the two y-axes. At the bottom of the canvas, the device y-coordinate is maxY, while the logical y-coordinate is 0, which may explain the two expressions of the form maxY - … in the following methods: int iX(float x) { return Math.round(x); } int iY(float y) { return maxY - Math.round(y); } float fx(int x) { return (float)x; } float fy(int y) { return (float)(maxY - y); }
  • 7. Figure 1.4 shows a fragment of a canvas, based on maxY=16. The pixels are drawn as black dots, each placed at the center of a square of dashed lines, and the device coordinates (X,Y) are placed between parentheses near each dot. For example, the pixel with device coordinates (8,2) at the upper-right corner of this canvas fragment, has logical coordinates (8.0, 14.0). We have iX(8.0) = Math.round(8.0) = 8 iY(14.0) = 16 - Math.round(14.0) = 2 fx(8) = (float)8 = 8.0 fy(2) = (float)(16 - 2) = 14.0 The dashed square around this dot denotes all points (x,y) satisfying: 7.5 ≤ 𝑥 < 8.5 13.5 ≤ 𝑦 < 14.5 All these points are converted to the pixel (8,2) by our methods 𝑖𝑋 and 𝑖𝑌. Let us demonstrate this way of converting floating-point logical coordinates to integer device coordinates in a program that begins by drawing an equilateral triangle ABC, with the side AB at the bottom and the point C at the top. Then using q = 0.05 p = 1 − q = 0.95 We compute the new points A’, B’ and C’ near A, B and C (5, 2) (6, 2) (7, 2) (8, 2) (5, 3) (6, 3) (7, 3) (8, 3) (5, 4) (6, 4) (7, 4) (8, 4) 5.0 6.0 7.0 8.0 14.0 13.0 12.0 Figure 1.4 Logical and device coordinates, based on maxY = 16 𝑦 𝑥 logical device
  • 8. and lying on the sides AB, BC and CA respectively, writing: xA1 = p * xA + q * xB; yA1 = p * yA + q * yB; xB1 = p * xB + q * xC; yB1 = p * yB + q * yC; xC1 = p * xC + q * xA; yC1 = p * yC + q * yA; We then draw the triangle A’B’C’, which is slightly smaller than ABC and turned a little counter-clockwise. Applying the same principle to triangle A’B’C’ to obtain a third triangle A’’B’’C’’, and so on, until 50 triangles have been drawn, the result will be as shown in Fig 1.5. If we change the dimensions of the window, new equilateral triangles appear, again in the center of the canvas and with dimensions proportional to the size of this canvas. Figure 1.5 Triangles, drawn inside each other
  • 9. The next slide shows the Java code for the whole program. @philip_schwarz
  • 10. import java.awt.*; public class CvTriangles extends Canvas { int maxX, maxY, minMaxXY, xCenter, yCenter; void initgr() { Dimension d = getSize(); maxX = d.width - 1; maxY = d.height - 1; minMaxXY = Math.min(maxX, maxY); xCenter = maxX / 2; yCenter = maxY / 2; } int iX(float x) { return Math.round(x); } int iY(float y) { return maxY - Math.round(y); } public void paint(Graphics g) { initgr(); float side = 0.95F * minMaxXY, sideHalf = 0.5F * side, h = sideHalf * (float) Math.sqrt(3), xA, yA, xB, yB, xC, yC, xA1, yA1, xB1, yB1, xC1, yC1, p, q; q = 0.05F; p = 1 - q; xA = xCenter - sideHalf; yA = yCenter - 0.5F * h; xB = xCenter + sideHalf; yB = yA; xC = xCenter; yC = yCenter + 0.5F * h; for (int i = 0; i < 50; i++) { g.drawLine(iX(xA), iY(yA), iX(xB), iY(yB)); g.drawLine(iX(xB), iY(yB), iX(xC), iY(yC)); g.drawLine(iX(xC), iY(yC), iX(xA), iY(yA)); xA1 = p * xA + q * xB; yA1 = p * yA + q * yB; xB1 = p * xB + q * xC; yB1 = p * yB + q * yC; xC1 = p * xC + q * xA; yC1 = p * yC + q * yA; xA = xA1; xB = xB1; xC = xC1; yA = yA1; yB = yB1; yC = yC1; } } } // Triangles.java: This program draws 50 // triangles inside each other. public class Triangles extends Frame { public static void main(String[] args) { new Triangles(); } Triangles() { super("Triangles: 50 triangles inside each other"); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); setSize(600, 400); add("Center", new CvTriangles()); setVisible(true); } } Without floating-point logical coordinates and with a y-axis pointing downward, this program would have been less easy to write.
  • 11. Let’s rewrite that Java code in Scala, beginning with the code section which given a starting triangle, draws the triangle and then proceeds to repeatedly, first shrink and twist the triangle, and then draw it, thus generating and drawing 49 more concentric triangles. While the Java code uses a java.awt.Canvas, the Scala code uses a javax.swing.JPanel. class TrianglesPanel extends JPanel: … override def paintComponent(g: Graphics): Unit = … LazyList.iterate(triangle)(shrinkAndTwist).take(50).foreach(draw) public class CvTriangles extends Canvas { … public void paint(Graphics g) { … for (int i = 0; i < 50; i++) { g.drawLine(iX(xA), iY(yA), iX(xB), iY(yB)); g.drawLine(iX(xB), iY(yB), iX(xC), iY(yC)); g.drawLine(iX(xC), iY(yC), iX(xA), iY(yA)); xA1 = p * xA + q * xB; yA1 = p * yA + q * yB; xB1 = p * xB + q * xC; yB1 = p * yB + q * yC; xC1 = p * xC + q * xA; yC1 = p * yC + q * yA; xA = xA1; xB = xB1; xC = xC1; yA = yA1; yB = yB1; yC = yC1; } } } Given an initial triangle, we are going to generate a lazy, potentially infinite, sequence of triangles, in which each triangle, with the exception of the first one, is the result of shrinking and twisting the previous triangle. We then take (materialise) the first 50 triangles of the sequence and iterate through them, drawing each one in turn. draw: Triangle => Unit shrinkAndTwist: Triangle => Triangle
  • 12. A triangle consists of three logical points (points with logical coordinates) A, B and C. case class Point(x: Float, y: Float) case class Triangle(a: Point, b: Point, c: Point) Drawing a triangle amounts to drawing lines AB, BC and CA. val draw: Triangle => Unit = case Triangle(a, b, c) => drawLine(a, b) drawLine(b, c) drawLine(c, a) To draw a line from logical point A to logical point B, we first compute the coordinates of the corresponding device points, and then pass those coordinates to the drawLine method provided by the Graphics object. def drawLine(a: Point, b: Point): Unit = val (ax,ay) = a.deviceCoords(panelHeight) val (bx,by) = b.deviceCoords(panelHeight) g.drawLine(ax, ay, bx, by) Here is how we enrich a logical point with a deviceCoords function that takes the logical coordinates of the point and computes the corresponding device coordinates: extension (p: Point) def deviceCoords(panelHeight: Int): (Int, Int) = (Math.round(p.x), panelHeight - Math.round(p.y)) The deviceCoords function is our Scala equivalent of Java functions iX and iY : int iX(float x) { return Math.round(x); } int iY(float y) { return maxY - Math.round(y); } LazyList.iterate(triangle)(shrinkAndTwist).take(50).foreach(draw)
  • 13. xA1 = p * xA + q * xB; yA1 = p * yA + q * yB; xB1 = p * xB + q * xC; yB1 = p * yB + q * yC; xC1 = p * xC + q * xA; yC1 = p * yC + q * yA; xA = xA1; xB = xB1; xC = xC1; yA = yA1; yB = yB1; yC = yC1; val shrinkAndTwist: Triangle => Triangle = val q = 0.05F val p = 1 - q def combine(a: Point, b: Point) = Point(p * a.x + q * b.x, p * a.y + q * b.y) { case Triangle(a,b,c) => Triangle(combine(a,b), combine(b,c), combine(c,a)) } As for the Java code that shrinks and twists a triangle, in our Scala code, we encapsulate it in function shrinkAndTwist. q = 0.05F; p = 1 - q; float … xA, yA, xB, yB, xC, yC, xA1, yA1, xB1, yB1, xC1, yC1, p, q; @philip_schwarz
  • 14. float side = 0.95F * minMaxXY, sideHalf = 0.5F * side, h = sideHalf * (float) Math.sqrt(3), …; int maxX, maxY, minMaxXY, xCenter, yCenter; void initgr() { Dimension d = getSize(); maxX = d.width - 1; maxY = d.height - 1; minMaxXY = Math.min(maxX, maxY); xCenter = maxX / 2; yCenter = maxY / 2; } val panelSize: Dimension = getSize() val panelWidth = panelSize.width - 1 val panelHeight = panelSize.height - 1 val panelCentre = Point(panelWidth / 2, panelHeight / 2) val triangleSide = 0.95F * Math.min(panelWidth, panelHeight) val triangleHeight = (0.5F * triangleSide) * Math.sqrt(3).toFloat val triangle = makeTriangle(panelCentre, triangleSide, triangleHeight) xA = xCenter - sideHalf; yA = yCenter - 0.5F * h; xB = xCenter + sideHalf; yB = yA; xC = xCenter; yC = yCenter + 0.5F * h; As for the Java code that computes the first triangle from the dimensions of the Canvas on which it is going to be drawn, here it is, together with the corresponding Scala code, which draws on a JPanel. object Triangle: def apply(centre: Point, side: Float, height: Float): Triangle = val Point(x,y) = centre val halfSide = 0.5F * side val bottomLeft = Point(x - halfSide, y - 0.5F * height) val bottomRight = Point(x + halfSide, y - 0.5F * height) val top = Point(x, y + 0.5F * height ) Triangle(bottomLeft,bottomRight,top)
  • 15. class Triangles: JFrame.setDefaultLookAndFeelDecorated(true) val frame = new JFrame("Triangles: 50 triangles inside each other") frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) frame.setSize(600, 400) frame.add(TrianglesPanel()) frame.setVisible(true) @main def main: Unit = // Create a panel on the event dispatching thread SwingUtilities.invokeLater( new Runnable(): def run: Unit = Triangles() ) public class Triangles extends Frame { public static void main(String[] args) { new Triangles(); } Triangles() { super("Triangles: 50 triangles inside each other"); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); setSize(600, 400); add("Center", new CvTriangles()); setVisible(true); } } And finally, let’s translate the rest of the code, which creates the application’s frame.
  • 16. Now let’s run the Scala program, to verify that it works OK. @philip_schwarz
  • 18. Now let’s run the program again after temporarily tweaking it so that each side of a triangle is drawn in a different colour.
  • 20. And now let’s repeat that, but with different colours and a black background.
  • 22. The next slide shows our Scala code in its entirety, and the subsequent slide shows the whole of the Java code again, for comparison.
  • 23. class TrianglesPanel extends JPanel: setBackground(Color.white) override def paintComponent(g: Graphics): Unit = super.paintComponent(g) val panelSize: Dimension = getSize() val panelWidth = panelSize.width - 1 val panelHeight = panelSize.height - 1 val panelCentre = Point(panelWidth / 2, panelHeight / 2) val triangleSide: Float = 0.95F * Math.min(panelWidth, panelHeight) val triangleHeight: Float = (0.5F * triangleSide) * Math.sqrt(3).toFloat val shrinkAndTwist: Triangle => Triangle = val q = 0.05F val p = 1 - q def combine(a: Point, b: Point) = Point(p * a.x + q * b.x, p * a.y + q * b.y) { case Triangle(a,b,c) => Triangle(combine(a,b), combine(b,c), combine(c,a)) } def drawLine(a: Point, b: Point): Unit = val (ax,ay) = a.deviceCoords(panelHeight) val (bx,by) = b.deviceCoords(panelHeight) g.drawLine(ax, ay, bx, by) val draw: Triangle => Unit = case Triangle(a, b, c) => drawLine(a, b) drawLine(b, c) drawLine(c, a) val triangle = Triangle(panelCentre, triangleSide, triangleHeight) LazyList.iterate(triangle)(shrinkAndTwist).take(50).foreach(draw) class Triangles: JFrame.setDefaultLookAndFeelDecorated(true) val frame = new JFrame("Triangles: 50 triangles inside each other") frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) frame.setSize(600, 400) frame.add(TrianglesPanel()) frame.setVisible(true) @main def main: Unit = // Create a panel on the event dispatching thread SwingUtilities.invokeLater( new Runnable(): def run: Unit = Triangles() ) case class Point(x: Float, y: Float) extension (p: Point) def deviceCoords(panelHeight: Int): (Int, Int) = (Math.round(p.x), panelHeight - Math.round(p.y)) object Triangle: def apply(centre: Point, side: Float, height: Float): Triangle = val Point(x,y) = centre val halfSide = 0.5F * side val bottomLeft = Point(x - halfSide, y - 0.5F * height) val bottomRight = Point(x + halfSide, y - 0.5F * height) val top = Point(x, y + 0.5F * height ) Triangle(bottomLeft,bottomRight,top) case class Triangle(a: Point, b: Point, c: Point)
  • 24. public class CvTriangles extends Canvas { int maxX, maxY, minMaxXY, xCenter, yCenter; void initgr() { Dimension d = getSize(); maxX = d.width - 1; maxY = d.height - 1; minMaxXY = Math.min(maxX, maxY); xCenter = maxX / 2; yCenter = maxY / 2; } int iX(float x) { return Math.round(x); } int iY(float y) { return maxY - Math.round(y); } public void paint(Graphics g) { initgr(); float side = 0.95F * minMaxXY, sideHalf = 0.5F * side, h = sideHalf * (float) Math.sqrt(3), xA, yA, xB, yB, xC, yC, xA1, yA1, xB1, yB1, xC1, yC1, p, q; q = 0.05F; p = 1 - q; xA = xCenter - sideHalf; yA = yCenter - 0.5F * h; xB = xCenter + sideHalf; yB = yA; xC = xCenter; yC = yCenter + 0.5F * h; for (int i = 0; i < 50; i++) { g.drawLine(iX(xA), iY(yA), iX(xB), iY(yB)); g.drawLine(iX(xB), iY(yB), iX(xC), iY(yC)); g.drawLine(iX(xC), iY(yC), iX(xA), iY(yA)); xA1 = p * xA + q * xB; yA1 = p * yA + q * yB; xB1 = p * xB + q * xC; yB1 = p * yB + q * yC; xC1 = p * xC + q * xA; yC1 = p * yC + q * yA; xA = xA1; xB = xB1; xC = xC1; yA = yA1; yB = yB1; yC = yC1; } } } // Triangles.java: This program draws 50 // triangles inside each other. public class Triangles extends Frame { public static void main(String[] args) { new Triangles(); } Triangles() { super("Triangles: 50 triangles inside each other"); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); setSize(600, 400); add("Center", new CvTriangles()); setVisible(true); } }
  • 25. That’s all for now. See you in Part 2. @philip_schwarz