Skip to content

Commit

Permalink
refactor(game-view): extract GameMapComponent
Browse files Browse the repository at this point in the history
  • Loading branch information
luigi-borriello00 committed Sep 10, 2023
1 parent f943472 commit 98b7135
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 166 deletions.
169 changes: 3 additions & 166 deletions src/main/scala/scatan/views/game/GameView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,94 +8,11 @@ import scatan.mvc.lib.{ScalaJSView, View}
import scatan.model.Spot
import scatan.model.map.Hexagon
import scatan.model.GameMap
import scatan.views.game.Coordinates.center
import scatan.views.game.Coordinates.coordinates
import scatan.views.game.components.GameMapComponent
import scatan.views.game.components.GameMapComponent.getMapComponent

trait GameView extends View

/** @param value
*/
final case class DoubleWithPrecision(value: Double):
override def equals(x: Any): Boolean =
x match
case that: DoubleWithPrecision =>
(this.value - that.value).abs < 0.001
case _ => false

override def hashCode: Int = (value * 1000).round.toInt.hashCode

/** A trait to represent cartesian coordinates.
*/
trait Coordinates:
def x: DoubleWithPrecision
def y: DoubleWithPrecision

object Coordinates:

/** Create a new Coordinates object.
*
* @param x
* the x coordinate
* @param y
* the y coordinate
* @return
* the new Coordinates object
*/
def apply(x: DoubleWithPrecision, y: DoubleWithPrecision): Coordinates = CoordinatesImpl(x, y)

/** Extract the x and y coordinates from a Coordinates object, unwrapping them to double.
*
* @param coordinates
* the coordinates to extract from
* @return
* a pair of doubles.
*/
def unapply(coordinates: Coordinates): (Double, Double) =
(coordinates.x.value, coordinates.y.value)

private case class CoordinatesImpl(x: DoubleWithPrecision, y: DoubleWithPrecision) extends Coordinates

/** Convert a pair of doubles to a Coordinates object.
*/
given Conversion[(Double, Double), Coordinates] with
def apply(pair: (Double, Double)): Coordinates =
Coordinates(DoubleWithPrecision(pair._1), DoubleWithPrecision(pair._2))

/** Extension methods to handle hexagon in cartesian plane.
*/
extension (hex: Hexagon)
/** Get the center, based on cantesian coordinates of the Hexagon.
*
* @return
* the center point of the hexagon
*/
def center(using hexSize: Int): Coordinates =
val x = hexSize * (math.sqrt(3) * hex.col + math.sqrt(3) / 2 * hex.row)
val y = hexSize * (3.0 / 2 * hex.row)
(x, y)

/** Get the vertices of the hexagon.
*
* @return
* the points of the hexagon
*/
def vertices(using hexSize: Int): Set[Coordinates] =
val Coordinates(x, y) = center
for
i <- (0 to 5).toSet
angle_deg = 60 * i - 30
angle_rad = (math.Pi / 180.0) * angle_deg
yield (
x + hexSize * math.cos(angle_rad),
y + hexSize * math.sin(angle_rad)
)

/** Extension methods to handle spot in cartesian plane.
*/
extension (spot: Spot)
def coordinates(using hexSize: Int): Option[Coordinates] =
(spot._1.vertices & spot._2.vertices & spot._3.vertices).headOption

class ScalaJsGameView(requirements: View.Requirements[GameController], container: String)
extends GameView
with View.Dependencies(requirements)
Expand All @@ -104,90 +21,10 @@ class ScalaJsGameView(requirements: View.Requirements[GameController], container
given hexSize: Int = 100
val gameMap = GameMap(2)

/** Generate the hexagon graphic
*
* @param hex,
* the hexagon
* @return
* the hexagon graphic
*/
private def generateHexagon(hex: Hexagon): Element =
val Coordinates(x, y) = hex.center
svg.g(
svg.transform := s"translate($x, $y) rotate(30) scale(0.95)",
svg.polygon(
svg.points := "100,0 50,-87 -50,-87 -100,-0 -50,87 50,87",
svg.cls := "hexagon"
)
)

/** Generate the road graphic
* @param spot1,
* the first spot
* @param spot2,
* the second spot
* @return
* the road graphic
*/
private def generateRoad(spot1: Coordinates, spot2: Coordinates): Element =
val Coordinates(x1, y1) = spot1
val Coordinates(x2, y2) = spot2
svg.g(
svg.line(
svg.x1 := s"${x1}",
svg.y1 := s"${y1}",
svg.x2 := s"${x2}",
svg.y2 := s"${y2}",
svg.className := "road",
svg.stroke := "red",
svg.strokeWidth := "10"
),
svg.circle(
svg.cx := s"${x1 + (x2 - x1) / 2}",
svg.cy := s"${y1 + (y2 - y1) / 2}",
svg.r := "15",
svg.className := "road",
svg.fill := "yellow",
onClick --> (_ => println((spot1, spot2)))
)
)

/** Generate the spot graphic
* @param x,
* the x coordinate of the spot
* @param y,
* the y coordinate of the spot
* @return
* the spot graphic
*/
private def generateSpot(coordinate: Coordinates): Element =
val Coordinates(x, y) = coordinate
svg.circle(
svg.cx := s"${x}",
svg.cy := s"${y}",
svg.r := "10",
svg.className := "spot",
svg.fill := "white",
onClick --> (_ => println((x, y)))
)

override def element: Element =
div(
display := "block",
width := "70%",
margin := "auto",
svg.svg(
svg.viewBox := "-500 -500 1000 1000",
for hex <- gameMap.tiles.toList
yield generateHexagon(hex),
for
spots <- gameMap.edges.toList
pointsOfSpot1 <- spots._1.coordinates
pointsOfSpot2 <- spots._2.coordinates
yield generateRoad(pointsOfSpot1, pointsOfSpot2),
for
spot <- gameMap.nodes.toList
coordinates <- spot.coordinates
yield generateSpot(coordinates)
)
getMapComponent(gameMap)
)
91 changes: 91 additions & 0 deletions src/main/scala/scatan/views/game/components/GameMapComponent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package scatan.views.game.components

import scatan.model.map.Hexagon
import scatan.model.GameMap
import com.raquo.laminar.api.L.*
import scatan.views.Coordinates.*
import scatan.views.Coordinates

object GameMapComponent:
/** Generate the hexagon graphic
*
* @param hex,
* the hexagon
* @return
* the hexagon graphic
*/
private def generateHexagon(hex: Hexagon)(using hexSize: Int): Element =
val Coordinates(x, y) = hex.center
svg.g(
svg.transform := s"translate($x, $y) rotate(30) scale(0.95)",
svg.polygon(
svg.points := "100,0 50,-87 -50,-87 -100,-0 -50,87 50,87",
svg.cls := "hexagon"
)
)

/** Generate the road graphic
* @param spot1,
* the first spot
* @param spot2,
* the second spot
* @return
* the road graphic
*/
private def generateRoad(spot1: Coordinates, spot2: Coordinates): Element =
val Coordinates(x1, y1) = spot1
val Coordinates(x2, y2) = spot2
svg.g(
svg.line(
svg.x1 := s"${x1}",
svg.y1 := s"${y1}",
svg.x2 := s"${x2}",
svg.y2 := s"${y2}",
svg.className := "road",
svg.stroke := "red",
svg.strokeWidth := "10"
),
svg.circle(
svg.cx := s"${x1 + (x2 - x1) / 2}",
svg.cy := s"${y1 + (y2 - y1) / 2}",
svg.r := "15",
svg.className := "road",
svg.fill := "yellow",
onClick --> (_ => println((spot1, spot2)))
)
)

/** Generate the spot graphic
* @param x,
* the x coordinate of the spot
* @param y,
* the y coordinate of the spot
* @return
* the spot graphic
*/
private def generateSpot(coordinate: Coordinates): Element =
val Coordinates(x, y) = coordinate
svg.circle(
svg.cx := s"${x}",
svg.cy := s"${y}",
svg.r := "10",
svg.className := "spot",
svg.fill := "white",
onClick --> (_ => println((x, y)))
)

def getMapComponent(gameMap: GameMap)(using hexSize: Int): Element =
svg.svg(
svg.viewBox := "-500 -500 1000 1000",
for hex <- gameMap.tiles.toList
yield generateHexagon(hex),
for
spots <- gameMap.edges.toList
pointsOfSpot1 <- spots._1.coordinates
pointsOfSpot2 <- spots._2.coordinates
yield generateRoad(pointsOfSpot1, pointsOfSpot2),
for
spot <- gameMap.nodes.toList
coordinates <- spot.coordinates
yield generateSpot(coordinates)
)

0 comments on commit 98b7135

Please sign in to comment.