Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 69 additions & 19 deletions compendium/modules/w05-classes-exercise.tex
Original file line number Diff line number Diff line change
Expand Up @@ -492,15 +492,15 @@



\WHAT{Skapa en punktklass som kan hantera polära koordinater.}
\WHAT{Skapa en punktklass som kan hantera polära koordinater och en klass som representerar en polygon m.h.a. dessa punkter.}

\QUESTBEGIN

\Task \what~
Du ska skapa en oföränderlig case-klass \code{Point} som kan representera en koordinat både med ''vanliga'' kartesiska koordinater\footnote{\url{https://sv.wikipedia.org/wiki/Kartesiskt_koordinatsystem}} och med polära koordinater%
\footnote{\url{https://sv.wikipedia.org/wiki/Pol\%C3\%A4ra\_koordinater}}.
Du ska skapa en oföränderlig case-klass \code{Point} som kan representera en koordinat både med ''vanliga'' kartesiska koordinater\footnote{\url{https://sv.wikipedia.org/wiki/Kartesiskt_koordinatsystem}} och med polära koordinater
\footnote{\url{https://sv.wikipedia.org/wiki/Pol\%C3\%A4ra\_koordinater}}. Sedan ska du använda denna klass för att skapa regelbundna polygoner med en oföränderlig case-klass \code{Polygon}.

\Subtask Skapa kod med hjälp av en editor, t.ex. VS \code{code}, i filen \code{Point.scala} enligt följande riktlinjer:
\Subtask Skapa kod med hjälp av en editor, t.ex. VS \code{code}, i filen \code{Point.scala} enligt följande riktlinjer:
\begin{enumerate}[noitemsep]
\item \code{Point} ska ligga i paketet \code{graphics}.

Expand All @@ -510,12 +510,10 @@
\item \code{y: Double} för y-koordinaten.
\end{itemize}

\item \code{Point} ska ha följande publika medlemmar (två oföränderliga attribut och två metoder):
\item \code{Point} ska ha följande publika medlemmar (två oföränderliga attribut och en metod):
\begin{itemize}[nolistsep, noitemsep]
\item \code{val r: Double} ska ge motsvarande polära koordinatens
avstånd till origo.
\item \code{val r: Double} ska ge motsvarande polära koordinatens avstånd till origo.
\item \code{val theta: Double} ska ge polära koordinatens vinkel i radianer.
\item \code{def negY: Point} ska ge en ny punkt med y-koordinaten negerad.
\item \code{def +(p: Point): Point} ska ge en ny punkt vars koordinat är summan av x- respektive y-koordinaterna för denna instans och punkten \code{p}.
\end{itemize}

Expand Down Expand Up @@ -559,16 +557,44 @@

\item Du har nytta av metoderna \code{math.cos(theta)} och \code{math.sin(theta)} vid omvandling från polära koordinater.

\item Attributet \code{negY} är användbar vid omvandling till fönsterkoordinater där y-axeln är omvänd jämfört med kartesiska koordinater.

\item Notera att klassens attribut är av typen \code{Double} och inte \code{Int}, trots att vi senare ska använda punkten för att beskriva en diskret pixelposition i ett \code{PixelWindow}. Anledningen till detta är att det kan uppstå avrundningsfel vid numeriska beräkningar. Detta blir särskilt märkbart vid upprepad räkning med små värden, t.ex. när man ritar en approximerad cirkel med många linjesegment.
\end{itemize}

\Subtask Använd din punktklass då du deklarerar en funktion \code{cyclic(n, r, p)} som ger en sekvens av punkter som beskriver en liksidig månghörning med \code{n} hörn där hörnen är placerade på en cirkel med radien \code{r} och mittpunkten \code{p}.
\Subtask Klassen \code{PolygonWindow} nedan innehåller ett \code{PixelWindow} och ger möjlighet att rita ut polygoner.
Kopiera koden för \code{PolygonWindow} till en ny kodfil \code{PolygonWindow.scala} i samma katalog som du placerade \code{Point} ovan i.

\begin{Code}
//> using dep "se.lth.cs::introprog:1.4.0"

package graphics

class PolygonWindow:
import introprog.PixelWindow
import java.awt.Color

val black = Color(0,0,0)
val coolGreen = Color(0,255,111)
val width = 500
val height = 500

val window = PixelWindow(width, height, title = "Polygons", background = black, foreground = coolGreen)

private def convertToVector(point: Point): Vector[Int] =
Vector(point.x.toInt, point.y.toInt)

\Subtask Skapa en funktion \code{drawPolygon(pts)} som ritar månghörningar enligt en punktsekvens \code{pts} i ett \code{PixelWindow}.
def draw(polygon: Polygon): Unit =
for i <- 0 until polygon.nbrOfCorners do
val from = convertToVector(polygon.points(i)).map(_ + width/2)
val to = convertToVector(polygon.points((i+1) % polygon.nbrOfCorners)).map(_ + width/2)
window.line(from(0), from(1), to(0), to(1), lineWidth = 2)
\end{Code}

Skapa nu en ny kodfil innehållandes en case-klass \code{Polygon(points: Vector[Point])} med attributet \code{val nbrOfCorners: Int}. \code{Polygon} ska också ligga i paketet \code{graphics}.
Likt klassen \code{Point} ovan ska också \code{Polygon} ha ett kompanjonsobjekt. Kompanjonsobjektet ska ha en metod \code{regularPolygon(nbrOfCorners: Int, radius: Double, midPoint: Point): Polygon} för att skapa regelbundna polygoner.
Fundera över hur case-klassen \code{Polygon} och dess kompanjonsobjekt ska se ut för att koden ovan i \code{PolygonWindow} ska fungera som tänkt. Testa i Scala REPL!

\Subtask Hur många hörn behövs det för att en liksidig månghörning ska se ut som en cirkel?
\Subtask Kan man använda metoden \code{regularPolygon} för att rita cirklar? Kan man använda \code{Polygon} för att representera oregelbundna polygoner?
Testa i Scala REPL!

\SOLUTION

Expand All @@ -578,22 +604,46 @@
package graphics

case class Point(x: Double, y: Double):
val r: Double = math.hypot(x, y)
val theta: Double = math.atan2(y, x)
def negY: Point = Point(x, -y)
val r: Double = math.hypot(x, y)
val theta: Double = math.atan2(y, x)
def +(p: Point): Point = Point(x + p.x, y + p.y)

object Point:
def polar(r: Double, theta: Double): Point =
Point(r * math.cos(theta), r * math.sin(theta))
\end{Code}

\Subtask \TODO
\Subtask \begin{Code}
package graphics

\Subtask \TODO
case class Polygon(points: Vector[Point]):
val nbrOfCorners: Int = points.length

object Polygon:
def regularPolygon(nbrOfCorners: Int, radius: Double, midPoint: Point): Polygon =
val points = new Array[Point](nbrOfCorners)
for i <- 0 until nbrOfCorners do
val theta = i * (2 * math.Pi) / nbrOfCorners
points(i) = Point.polar(radius, theta) + midPoint
end for
Polygon(points.toVector)
\end{Code}

\Subtask \TODO
\Subtask
En perfekt cirkel går inte att skapa, men det går att komma tillräckligt nära för att göra det omöjligt att se hörnen.
Testa till exempel med 50 hörn likt nedan.
\begin{REPL}
scala> import graphics.*
scala> val circle = Polygon.regularPolygon(50,70,Point(-25,-25))
scala> val window = PolygonWindow()
scala> window.draw(circle)
\end{REPL}

Även en oregelbunden polygon går att skapa. Använd då konstruktorn till \code{Polygon} direkt. Till exempel likt nedan.
\begin{REPL}
scala> val irregular = Polygon(Vector(Point(34,83), Point(16,42), Point(77,77), Point(100,138)))
scala> window.draw(irregular)
\end{REPL}

\QUESTEND

Expand Down