Search code examples
scalacanvaspng

image created in scala does not look as expected. not sure why


i start by creating a 2d array all with a value of 2. then setting a few values from the bottom right of the array to 5

var map = Array.fill(5, 10)(2);
map(4)(9) = 5
map(3)(9) = 5
map(4)(8) = 5
map(3)(8) = 5

this array will be represented by an image. each number 2 should be a blue square. the rest should just be white

import scala.io.Source
import java.io._
import java.awt.image.BufferedImage
import java.awt.{Graphics2D,Color,Font,BasicStroke}
import java.awt.geom._

object DRAWPROB {
 def draw(map : Array[Array[Int]]){

        val size = (30 * (map(0).length-1), 30 * (map.length-1))
        val canvas = new BufferedImage(size._1, size._2, BufferedImage.TYPE_INT_RGB)
        val g = canvas.createGraphics()
        g.setColor(Color.WHITE)
        g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
        g.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING,java.awt.RenderingHints.VALUE_ANTIALIAS_ON)
        var d = 0;
            for(d <- 0 to map.length-1){
                  var e = 0;
                  for(e <- 0 to map(0).length-1){
                  g.setColor(Color.BLUE)


                    if(map(d)(e) == 2){ g.fill(new Rectangle2D.Double(d*30.0, e*30.0, 30.0, 30.0));} // ill find a way to do this once i can load the image
                    else {println("not at "+ d + " " + e)}
                                                                                    }
                                                                    }
g.dispose()
javax.imageio.ImageIO.write(canvas, "png", new java.io.File("island_drawing.png"))
}

it just makes 1 large blue square but i wanted a section missing from the bottom right

expectation vs reality

why am i getting this


Solution

  • Your main issue with this code part: new Rectangle2D.Double(d*30.0, e*30.0... - the first argument is x coordinate so it should be multiplied on map column value, rather on row , and same with second argument e*30 which is y so it should by multiplied on map row value.

    I've refactored your code for better understanding, at least for myself, so please take a look as an example:

    import java.awt.image.BufferedImage
    import java.awt.{Color, Rectangle, RenderingHints}
    
    import javax.imageio.ImageIO
    
    import scala.reflect.ClassTag
    
    /**
     * Immutable table model, possibly empty inside
     */
    class Table[T](underlying: Array[Array[T]]) {
      def columnsCount: Int = underlying.headOption.map(_.length).getOrElse(0)
      def rowsCount: Int = underlying.length
    
      def rows: Seq[Int] = underlying.indices
      def columns: Seq[Int] = underlying.headOption.map(_.indices).getOrElse(Nil)
    
      def apply(row: Int, column: Int): Option[T] = {
        val rowExists = row < rowsCount && row >= 0
        val columnExists = column < columnsCount && column >= 0
        if(rowExists && columnExists) Some(underlying(row)(column)) else None
      }
    
      def update(row: Int, column: Int, value: T): Table[T] = {
        val copy = underlying.clone()
        copy(row)(column) = value
        new Table[T](copy)
      }
    
      def foreach[U](f: ((Int, Int, T)) => U): Unit = {
        val rowColumnValue = for {
          row <- rows
          column <- columns
          value <- apply(row, column)
        } yield (row, column, value)
    
        rowColumnValue.foreach[U](f)
      }
    
      override def toString: String = underlying.map(_.mkString(",")).mkString("\n")
    }
    
    object Table {
      def apply[T: ClassTag](rows: Int, columns: Int, initValue: T): Table[T] = {
        new Table[T](Array.fill(rows, columns)(initValue))
      }
    }
    
    class Draw(cellSize: Int) {
    
      def draw(table: Table[Int]) {
        val width = cellSize * table.columnsCount
        val height = cellSize * table.rowsCount
    
        val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
        val graphics = canvas.createGraphics()
        graphics.setColor(Color.WHITE)
        graphics.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    
        table.foreach { case (row, column, value) =>
          if (value == 2) {
            graphics.setColor(Color.BLUE)
            // This is main fix part
            val x = column * cellSize
            val y = row * cellSize
            graphics.fill(new Rectangle(x, y, cellSize, cellSize))
          }
        }
    
        graphics.dispose()
        ImageIO.write(canvas, "png", new java.io.File("island_drawing.png"))
      }
    }
    
    
    object DrawApp {
      def main(args: Array[String]): Unit = {
        val draw = new Draw(30)
        val table = Table(5, 10, 2)
          .update(4 ,9, 5)
          .update(4, 8, 5)
          .update(3, 9, 5)
          .update(3, 8, 5)
        println(table)
    
        draw.draw(table)
      }
    }
    
    

    This produced expected image. Hope this helps!