Code Sketch


yoooo.... tati
By: Mhalsakant School
Category: Programming
import java.awt.BasicStroke
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Cursor
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.Font
import java.awt.GradientPaint
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.RenderingHints

import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.event.MouseMotionAdapter

import java.awt.image.BufferedImage

import java.io.File

import javax.imageio.ImageIO

import javax.swing.BorderFactory
import javax.swing.DefaultListModel
import javax.swing.JButton
import javax.swing.JComboBox
import javax.swing.JColorChooser
import javax.swing.JFileChooser
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.JList
import javax.swing.JOptionPane
import javax.swing.JPanel
import javax.swing.JScrollPane
import javax.swing.JTextField
import javax.swing.ListSelectionModel
import javax.swing.SwingUtilities
import javax.swing.WindowConstants


// ============================================================
//                    CONSTANTS
// ============================================================

val CANVAS_WIDTH = 1000
val CANVAS_HEIGHT = 600

val CENTER_X = 500
val CENTER_Y = 300


// ============================================================
//                COMPONENT REFERENCES
// ============================================================

var frame: JFrame = null
var canvas: JPanel = null
var status: JLabel = null

var pencilButton: JButton = null
var eraserButton: JButton = null
var undoButton: JButton = null
var redoButton: JButton = null
var clearButton: JButton = null
var saveButton: JButton = null
var colorizeButton: JButton = null
var threeDButton: JButton = null
var zoomInButton: JButton = null
var zoomOutButton: JButton = null
var gridButton: JButton = null
var resetButton: JButton = null
var drawButton: JButton = null
var randomButton: JButton = null
var customColorButton: JButton = null
var searchButton: JButton = null

var toolBox: JComboBox[String] = null
var sizeBox: JComboBox[String] = null
var styleBox: JComboBox[String] = null

var writeField: JTextField = null
var searchField: JTextField = null

var thingsList: JList[String] = null


// ============================================================
//                      APP STATE
// ============================================================

var currentColor = Color.BLACK
var brushSize = 6.0f
var currentTool = "PENCIL"

var zoomLevel = 1.0

var isDrawing = false

var lastX = 0
var lastY = 0

var startX = 0
var startY = 0

var gridEnabled = false

var currentTheme = "SKY"
var currentStyle = "NORMAL"

var undoImage: BufferedImage = null
var redoImage: BufferedImage = null


// ============================================================
//                        MAIN IMAGE
// ============================================================

val drawingImage =
  new BufferedImage(
    CANVAS_WIDTH,
    CANVAS_HEIGHT,
    BufferedImage.TYPE_INT_RGB
  )

val drawingGraphics =
  drawingImage.createGraphics()

drawingGraphics.setRenderingHint(
  RenderingHints.KEY_ANTIALIASING,
  RenderingHints.VALUE_ANTIALIAS_ON
)

drawingGraphics.setRenderingHint(
  RenderingHints.KEY_RENDERING,
  RenderingHints.VALUE_RENDER_QUALITY
)

drawingGraphics.setRenderingHint(
  RenderingHints.KEY_STROKE_CONTROL,
  RenderingHints.VALUE_STROKE_PURE
)

drawingGraphics.setColor(
  Color.WHITE
)

drawingGraphics.fillRect(
  0,
  0,
  CANVAS_WIDTH,
  CANVAS_HEIGHT
)


// ============================================================
//                        HELPERS
// ============================================================

def setStatus(
  text: String
): Unit = {

  if (status != null) {
    status.setText(text)
  }
}


def refreshCanvas(): Unit = {

  if (canvas != null) {
    canvas.repaint()
  }
}


def clearImage(): Unit = {

  drawingGraphics.setComposite(
    java.awt.AlphaComposite.Src
  )

  drawingGraphics.setColor(
    Color.WHITE
  )

  drawingGraphics.fillRect(
    0,
    0,
    CANVAS_WIDTH,
    CANVAS_HEIGHT
  )

  refreshCanvas()
}


def copyImage(
  source: BufferedImage
): BufferedImage = {

  val result =
    new BufferedImage(
      CANVAS_WIDTH,
      CANVAS_HEIGHT,
      BufferedImage.TYPE_INT_RGB
    )

  val g =
    result.createGraphics()

  g.setRenderingHint(
    RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BICUBIC
  )

  g.drawImage(
    source,
    0,
    0,
    null
  )

  g.dispose()

  result
}


def rememberUndo(): Unit = {

  undoImage =
    copyImage(
      drawingImage
    )

  redoImage =
    null
}


def restoreImage(
  source: BufferedImage
): Unit = {

  drawingGraphics.setColor(
    Color.WHITE
  )

  drawingGraphics.fillRect(
    0,
    0,
    CANVAS_WIDTH,
    CANVAS_HEIGHT
  )

  drawingGraphics.drawImage(
    source,
    0,
    0,
    null
  )

  refreshCanvas()
}


def makeButton(
  text: String
): JButton = {

  val b =
    new JButton(
      text
    )

  b.setFont(
    new Font(
      "Arial",
      Font.BOLD,
      12
    )
  )

  b.setFocusable(
    false
  )

  b
}


// ============================================================
//                         SAVE
// ============================================================

def saveImageToFile(
  source: BufferedImage,
  owner: JFrame
): Unit = {

  val chooser =
    new JFileChooser()

  chooser.setDialogTitle(
    "SAVE PNG IMAGE"
  )

  val result =
    chooser.showSaveDialog(
      owner
    )

  if (
    result ==
    JFileChooser.APPROVE_OPTION
  ) {

    try {

      var file =
        chooser.getSelectedFile

      if (
        !file.getName
          .toLowerCase
          .endsWith(".png")
      ) {

        file =
          new File(
            file.getAbsolutePath +
            ".png"
          )
      }

      ImageIO.write(
        source,
        "png",
        file
      )

      setStatus(
        "IMAGE SAVED!"
      )

    } catch {

      case ex: Exception =>

        JOptionPane.showMessageDialog(
          owner,
          ex.getMessage,
          "SAVE ERROR",
          JOptionPane.ERROR_MESSAGE
        )
    }
  }
}


// ============================================================
//                    CANVAS POSITION
// ============================================================

def shownWidth(): Int = {

  math.max(
    1,
    (
      CANVAS_WIDTH *
      zoomLevel
    ).toInt
  )
}


def shownHeight(): Int = {

  math.max(
    1,
    (
      CANVAS_HEIGHT *
      zoomLevel
    ).toInt
  )
}


def offsetX(): Int = {

  (
    canvas.getWidth -
    shownWidth()
  ) / 2
}


def offsetY(): Int = {

  (
    canvas.getHeight -
    shownHeight()
  ) / 2
}


def mouseToImageX(
  screenX: Int
): Int = {

  val result =
    (
      (screenX - offsetX()) /
      zoomLevel
    ).toInt

  math.max(
    0,
    math.min(
      CANVAS_WIDTH - 1,
      result
    )
  )
}


def mouseToImageY(
  screenY: Int
): Int = {

  val result =
    (
      (screenY - offsetY()) /
      zoomLevel
    ).toInt

  math.max(
    0,
    math.min(
      CANVAS_HEIGHT - 1,
      result
    )
  )
}


def insideCanvas(
  x: Int,
  y: Int
): Boolean = {

  x >= offsetX() &&
  x < offsetX() + shownWidth() &&
  y >= offsetY() &&
  y < offsetY() + shownHeight()
}


// ============================================================
//                         SPRAY
// ============================================================

def drawSpray(
  x: Int,
  y: Int
): Unit = {

  drawingGraphics.setColor(
    currentColor
  )

  val radius =
    math.max(
      8,
      brushSize.toInt * 2
    )

  var i = 0

  while (
    i < 30
  ) {

    val dx =
      (
        (i * 37) %
        (radius * 2 + 1)
      ) -
      radius

    val dy =
      (
        (i * 53) %
        (radius * 2 + 1)
      ) -
      radius

    if (
      dx * dx +
      dy * dy <=
      radius * radius
    ) {

      drawingGraphics.fillOval(
        x + dx,
        y + dy,
        3,
        3
      )
    }

    i += 1
  }
}


// ============================================================
//                   GENERIC DRAWING
// ============================================================

def drawGeneric(
  text: String
): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val hash =
    math.abs(
      text.hashCode
    )

  val objectColor =
    new Color(
      40 + hash % 190,
      40 + (hash / 7) % 190,
      40 + (hash / 13) % 190
    )

  g.setColor(
    objectColor
  )

  val typeId =
    hash % 7

  if (
    typeId == 0
  ) {

    g.fillRoundRect(
      CENTER_X - 190,
      CENTER_Y - 120,
      380,
      240,
      40,
      40
    )

  } else if (
    typeId == 1
  ) {

    g.fillOval(
      CENTER_X - 150,
      CENTER_Y - 150,
      300,
      300
    )

  } else if (
    typeId == 2
  ) {

    val p =
      new java.awt.Polygon()

    p.addPoint(
      CENTER_X,
      CENTER_Y - 180
    )

    p.addPoint(
      CENTER_X + 175,
      CENTER_Y + 145
    )

    p.addPoint(
      CENTER_X - 175,
      CENTER_Y + 145
    )

    g.fillPolygon(
      p
    )

  } else if (
    typeId == 3
  ) {

    g.fillRect(
      CENTER_X - 160,
      CENTER_Y - 140,
      320,
      280
    )

  } else if (
    typeId == 4
  ) {

    g.fillOval(
      CENTER_X - 210,
      CENTER_Y - 105,
      420,
      210
    )

  } else if (
    typeId == 5
  ) {

    val p =
      new java.awt.Polygon()

    var i = 0

    while (
      i < 6
    ) {

      val a =
        i * math.Pi / 3.0

      p.addPoint(
        CENTER_X +
        (math.cos(a) * 175).toInt,
        CENTER_Y +
        (math.sin(a) * 175).toInt
      )

      i += 1
    }

    g.fillPolygon(
      p
    )

  } else {

    val p =
      new java.awt.Polygon()

    var i = 0

    while (
      i < 10
    ) {

      val a =
        -math.Pi / 2 +
        i * math.Pi / 5.0

      val r =
        if (
          i % 2 == 0
        ) {
          185.0
        } else {
          80.0
        }

      p.addPoint(
        CENTER_X +
        (math.cos(a) * r).toInt,
        CENTER_Y +
        (math.sin(a) * r).toInt
      )

      i += 1
    }

    g.fillPolygon(
      p
    )
  }

  g.setColor(
    Color.BLACK
  )

  g.setFont(
    new Font(
      "Arial",
      Font.BOLD,
      23
    )
  )

  val label =
    text.take(30)

  val tw =
    g.getFontMetrics
      .stringWidth(
        label
      )

  g.drawString(
    label,
    CENTER_X - tw / 2,
    555
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                           CAR
// ============================================================

def drawCar(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    CENTER_Y

  g.setColor(
    new Color(
      220,
      45,
      50
    )
  )

  g.fillRoundRect(
    cx - 270,
    cy - 30,
    540,
    120,
    35,
    35
  )

  val roof =
    new java.awt.Polygon()

  roof.addPoint(
    cx - 180,
    cy - 30
  )

  roof.addPoint(
    cx - 95,
    cy - 145
  )

  roof.addPoint(
    cx + 105,
    cy - 145
  )

  roof.addPoint(
    cx + 185,
    cy - 30
  )

  g.fillPolygon(
    roof
  )

  g.setColor(
    new Color(
      125,
      215,
      245
    )
  )

  g.fillRect(
    cx - 140,
    cy - 115,
    120,
    75
  )

  g.fillRect(
    cx + 20,
    cy - 115,
    115,
    75
  )

  g.setColor(
    Color.BLACK
  )

  g.fillOval(
    cx - 210,
    cy + 25,
    95,
    95
  )

  g.fillOval(
    cx + 115,
    cy + 25,
    95,
    95
  )

  g.setColor(
    Color.LIGHT_GRAY
  )

  g.fillOval(
    cx - 178,
    cy + 57,
    30,
    30
  )

  g.fillOval(
    cx + 147,
    cy + 57,
    30,
    30
  )

  g.setColor(
    Color.YELLOW
  )

  g.fillOval(
    cx - 260,
    cy - 7,
    28,
    24
  )

  g.fillOval(
    cx + 232,
    cy - 7,
    28,
    24
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                          HOUSE
// ============================================================

def drawHouse(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  g.setColor(
    new Color(
      245,
      205,
      125
    )
  )

  g.fillRect(
    cx - 205,
    235,
    410,
    255
  )

  val roof =
    new java.awt.Polygon()

  roof.addPoint(
    cx - 260,
    235
  )

  roof.addPoint(
    cx,
    50
  )

  roof.addPoint(
    cx + 260,
    235
  )

  g.setColor(
    new Color(
      180,
      55,
      45
    )
  )

  g.fillPolygon(
    roof
  )

  g.setColor(
    new Color(
      120,
      78,
      45
    )
  )

  g.fillRect(
    cx - 50,
    340,
    100,
    150
  )

  g.setColor(
    new Color(
      80,
      190,
      240
    )
  )

  g.fillRect(
    cx - 160,
    275,
    75,
    75
  )

  g.fillRect(
    cx + 85,
    275,
    75,
    75
  )

  g.setColor(
    new Color(
      70,
      165,
      75
    )
  )

  g.fillRect(
    0,
    490,
    CANVAS_WIDTH,
    110
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                           TREE
// ============================================================

def drawTree(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  g.setColor(
    new Color(
      70,
      165,
      75
    )
  )

  g.fillRect(
    0,
    490,
    CANVAS_WIDTH,
    110
  )

  g.setColor(
    new Color(
      135,
      82,
      45
    )
  )

  g.fillRoundRect(
    cx - 45,
    280,
    90,
    215,
    25,
    25
  )

  g.setColor(
    new Color(
      40,
      170,
      70
    )
  )

  g.fillOval(
    cx - 190,
    115,
    225,
    190
  )

  g.fillOval(
    cx - 15,
    85,
    230,
    215
  )

  g.fillOval(
    cx - 105,
    35,
    230,
    215
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                           ROBOT
// ============================================================

def drawRobot(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    CENTER_Y

  g.setColor(
    new Color(
      110,
      170,
      205
    )
  )

  g.fillRoundRect(
    cx - 155,
    cy - 5,
    310,
    215,
    35,
    35
  )

  g.setColor(
    new Color(
      170,
      200,
      220
    )
  )

  g.fillRoundRect(
    cx - 175,
    cy - 190,
    350,
    170,
    40,
    40
  )

  g.setColor(
    Color.CYAN
  )

  g.fillOval(
    cx - 105,
    cy - 140,
    60,
    60
  )

  g.fillOval(
    cx + 45,
    cy - 140,
    60,
    60
  )

  g.setColor(
    Color.DARK_GRAY
  )

  g.fillRoundRect(
    cx - 85,
    cy - 65,
    170,
    35,
    15,
    15
  )

  g.setColor(
    Color.RED
  )

  g.fillOval(
    cx - 20,
    cy - 275,
    40,
    40
  )

  g.setColor(
    Color.DARK_GRAY
  )

  g.setStroke(
    new BasicStroke(
      8,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    cx - 155,
    cy + 55,
    cx - 240,
    cy + 120
  )

  g.drawLine(
    cx + 155,
    cy + 55,
    cx + 240,
    cy + 120
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                           CAT
// ============================================================

def drawCat(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    CENTER_Y

  g.setColor(
    new Color(
      180,
      180,
      190
    )
  )

  g.fillOval(
    cx - 140,
    cy - 5,
    280,
    220
  )

  g.fillOval(
    cx - 130,
    cy - 160,
    260,
    210
  )

  val leftEar =
    new java.awt.Polygon()

  leftEar.addPoint(
    cx - 110,
    cy - 120
  )

  leftEar.addPoint(
    cx - 75,
    cy - 220
  )

  leftEar.addPoint(
    cx - 25,
    cy - 140
  )

  val rightEar =
    new java.awt.Polygon()

  rightEar.addPoint(
    cx + 110,
    cy - 120
  )

  rightEar.addPoint(
    cx + 75,
    cy - 220
  )

  rightEar.addPoint(
    cx + 25,
    cy - 140
  )

  g.fillPolygon(
    leftEar
  )

  g.fillPolygon(
    rightEar
  )

  g.setColor(
    new Color(
      75,
      185,
      95
    )
  )

  g.fillOval(
    cx - 75,
    cy - 85,
    45,
    45
  )

  g.fillOval(
    cx + 30,
    cy - 85,
    45,
    45
  )

  g.setColor(
    Color.PINK
  )

  g.fillOval(
    cx - 12,
    cy - 25,
    24,
    18
  )

  g.setColor(
    Color.DARK_GRAY
  )

  g.setStroke(
    new BasicStroke(
      4,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    cx - 25,
    cy - 10,
    cx - 105,
    cy - 30
  )

  g.drawLine(
    cx + 25,
    cy - 10,
    cx + 105,
    cy - 30
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            DOG
// ============================================================

def drawDog(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    CENTER_Y

  g.setColor(
    new Color(
      180,
      120,
      70
    )
  )

  g.fillOval(
    cx - 160,
    cy - 20,
    320,
    205
  )

  g.fillOval(
    cx - 135,
    cy - 170,
    270,
    230
  )

  g.setColor(
    new Color(
      120,
      75,
      45
    )
  )

  g.fillOval(
    cx - 170,
    cy - 120,
    75,
    150
  )

  g.fillOval(
    cx + 95,
    cy - 120,
    75,
    150
  )

  g.setColor(
    Color.BLACK
  )

  g.fillOval(
    cx - 70,
    cy - 85,
    22,
    25
  )

  g.fillOval(
    cx + 48,
    cy - 85,
    22,
    25
  )

  g.fillOval(
    cx - 22,
    cy - 35,
    44,
    32
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            BIRD
// ============================================================

def drawBird(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    CENTER_Y

  g.setPaint(
    new GradientPaint(
      0,
      0,
      new Color(
        120,
        215,
        255
      ),
      0,
      CANVAS_HEIGHT,
      Color.WHITE
    )
  )

  g.fillRect(
    0,
    0,
    CANVAS_WIDTH,
    CANVAS_HEIGHT
  )

  g.setColor(
    new Color(
      60,
      130,
      225
    )
  )

  g.fillOval(
    cx - 145,
    cy - 90,
    285,
    180
  )

  g.fillOval(
    cx + 60,
    cy - 145,
    135,
    130
  )

  g.setColor(
    Color.BLACK
  )

  g.fillOval(
    cx + 140,
    cy - 105,
    18,
    18
  )

  g.setColor(
    Color.ORANGE
  )

  val beak =
    new java.awt.Polygon()

  beak.addPoint(
    cx + 195,
    cy - 80
  )

  beak.addPoint(
    cx + 260,
    cy - 55
  )

  beak.addPoint(
    cx + 195,
    cy - 35
  )

  g.fillPolygon(
    beak
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            FISH
// ============================================================

def drawFish(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    CENTER_Y

  g.setColor(
    new Color(
      65,
      155,
      230
    )
  )

  g.fillOval(
    cx - 175,
    cy - 90,
    350,
    180
  )

  val tail =
    new java.awt.Polygon()

  tail.addPoint(
    cx + 150,
    cy
  )

  tail.addPoint(
    cx + 260,
    cy - 90
  )

  tail.addPoint(
    cx + 260,
    cy + 90
  )

  g.fillPolygon(
    tail
  )

  g.setColor(
    Color.BLACK
  )

  g.fillOval(
    cx - 100,
    cy - 35,
    22,
    22
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                           FLOWER
// ============================================================

def drawFlower(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val cx =
    CENTER_X

  val cy =
    220

  g.setColor(
    new Color(
      45,
      155,
      60
    )
  )

  g.setStroke(
    new BasicStroke(
      15,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    cx,
    cy + 55,
    cx,
    510
  )

  val colors =
    Array(
      Color.RED,
      Color.PINK,
      Color.MAGENTA,
      Color.ORANGE,
      new Color(145,80,220),
      new Color(255,80,120)
    )

  var i =
    0

  while (
    i < 6
  ) {

    val angle =
      i * math.Pi / 3.0

    val px =
      cx +
      (math.cos(angle) * 80).toInt -
      50

    val py =
      cy +
      (math.sin(angle) * 80).toInt -
      50

    g.setColor(
      colors(i)
    )

    g.fillOval(
      px,
      py,
      100,
      100
    )

    i += 1
  }

  g.setColor(
    Color.YELLOW
  )

  g.fillOval(
    cx - 50,
    cy - 50,
    100,
    100
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            STAR
// ============================================================

def drawStar(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val star =
    new java.awt.Polygon()

  var i =
    0

  while (
    i < 10
  ) {

    val angle =
      -math.Pi / 2 +
      i * math.Pi / 5.0

    val radius =
      if (
        i % 2 == 0
      ) {
        190.0
      } else {
        80.0
      }

    star.addPoint(
      CENTER_X +
      (math.cos(angle) * radius).toInt,
      CENTER_Y +
      (math.sin(angle) * radius).toInt
    )

    i += 1
  }

  g.setColor(
    new Color(
      255,
      210,
      0
    )
  )

  g.fillPolygon(
    star
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            HEART
// ============================================================

def drawHeart(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  val heart =
    new java.awt.geom.Path2D.Double()

  heart.moveTo(
    CENTER_X,
    CENTER_Y + 190
  )

  heart.curveTo(
    CENTER_X - 270,
    CENTER_Y + 20,
    CENTER_X - 170,
    CENTER_Y - 175,
    CENTER_X,
    CENTER_Y - 45
  )

  heart.curveTo(
    CENTER_X + 170,
    CENTER_Y - 175,
    CENTER_X + 270,
    CENTER_Y + 20,
    CENTER_X,
    CENTER_Y + 190
  )

  g.setColor(
    Color.RED
  )

  g.fill(
    heart
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            FOOD
// ============================================================

def drawFood(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  g.setColor(
    new Color(
      240,
      175,
      50
    )
  )

  g.fillOval(
    CENTER_X - 190,
    CENTER_Y - 100,
    380,
    200
  )

  g.setColor(
    Color.RED
  )

  g.fillOval(
    CENTER_X - 120,
    CENTER_Y - 10,
    38,
    38
  )

  g.fillOval(
    CENTER_X + 45,
    CENTER_Y - 25,
    38,
    38
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                            SPACE
// ============================================================

def drawSpace(): Unit = {

  clearImage()

  val g =
    drawingGraphics

  g.setColor(
    new Color(
      5,
      10,
      35
    )
  )

  g.fillRect(
    0,
    0,
    CANVAS_WIDTH,
    CANVAS_HEIGHT
  )

  g.setColor(
    Color.WHITE
  )

  var i =
    0

  while (
    i < 120
  ) {

    val x =
      (i * 73) %
      CANVAS_WIDTH

    val y =
      (i * 43) %
      430

    val size =
      2 + (i % 4)

    g.fillOval(
      x,
      y,
      size,
      size
    )

    i += 1
  }

  g.setColor(
    new Color(
      75,
      145,
      240
    )
  )

  g.fillOval(
    CENTER_X - 120,
    CENTER_Y - 120,
    240,
    240
  )

  g.dispose()

  refreshCanvas()
}


// ============================================================
//                     TEXT DRAW ENGINE
// ============================================================

def drawFromText(
  input: String
): Unit = {

  val text =
    input
      .trim
      .toLowerCase

  if (
    text.length == 0
  ) {

    setStatus(
      "WRITE SOMETHING FIRST!"
    )

    return
  }

  rememberUndo()

  if (
    text.indexOf("car") >= 0 ||
    text.indexOf("auto") >= 0 ||
    text.indexOf("taxi") >= 0 ||
    text.indexOf("bus") >= 0 ||
    text.indexOf("truck") >= 0 ||
    text.indexOf("bike") >= 0 ||
    text.indexOf("bicycle") >= 0 ||
    text.indexOf("motorcycle") >= 0 ||
    text.indexOf("scooter") >= 0
  ) {

    drawCar()

  } else if (
    text.indexOf("house") >= 0 ||
    text.indexOf("home") >= 0 ||
    text.indexOf("school") >= 0 ||
    text.indexOf("building") >= 0 ||
    text.indexOf("castle") >= 0 ||
    text.indexOf("palace") >= 0
  ) {

    drawHouse()

  } else if (
    text.indexOf("tree") >= 0 ||
    text.indexOf("forest") >= 0 ||
    text.indexOf("jungle") >= 0 ||
    text.indexOf("plant") >= 0
  ) {

    drawTree()

  } else if (
    text.indexOf("robot") >= 0
  ) {

    drawRobot()

  } else if (
    text.indexOf("cat") >= 0 ||
    text.indexOf("kitten") >= 0
  ) {

    drawCat()

  } else if (
    text.indexOf("dog") >= 0 ||
    text.indexOf("puppy") >= 0
  ) {

    drawDog()

  } else if (
    text.indexOf("bird") >= 0 ||
    text.indexOf("eagle") >= 0 ||
    text.indexOf("parrot") >= 0 ||
    text.indexOf("peacock") >= 0
  ) {

    drawBird()

  } else if (
    text.indexOf("fish") >= 0 ||
    text.indexOf("shark") >= 0 ||
    text.indexOf("whale") >= 0 ||
    text.indexOf("dolphin") >= 0
  ) {

    drawFish()

  } else if (
    text.indexOf("flower") >= 0 ||
    text.indexOf("rose") >= 0
  ) {

    drawFlower()

  } else if (
    text.indexOf("star") >= 0
  ) {

    drawStar()

  } else if (
    text.indexOf("heart") >= 0
  ) {

    drawHeart()

  } else if (
    text.indexOf("pizza") >= 0 ||
    text.indexOf("burger") >= 0 ||
    text.indexOf("cake") >= 0 ||
    text.indexOf("food") >= 0
  ) {

    drawFood()

  } else if (
    text.indexOf("planet") >= 0 ||
    text.indexOf("earth") >= 0 ||
    text.indexOf("mars") >= 0 ||
    text.indexOf("jupiter") >= 0 ||
    text.indexOf("saturn") >= 0 ||
    text.indexOf("moon") >= 0 ||
    text.indexOf("space") >= 0 ||
    text.indexOf("galaxy") >= 0 ||
    text.indexOf("universe") >= 0 ||
    text.indexOf("rocket") >= 0
  ) {

    drawSpace()

  } else {

    drawGeneric(
      input
    )
  }

  setStatus(
    "DRAW COMPLETE: " +
    input
  )

  refreshCanvas()
}


// ============================================================
//                      1000+ THINGS
// ============================================================

val baseThings =
  Array(
    "car",
    "bus",
    "truck",
    "bike",
    "bicycle",
    "motorcycle",
    "scooter",
    "taxi",
    "train",
    "metro",
    "tram",
    "plane",
    "airplane",
    "jet",
    "helicopter",
    "rocket",
    "spaceship",
    "boat",
    "ship",
    "yacht",
    "submarine",
    "ambulance",
    "fire truck",
    "tractor",
    "tank",
    "house",
    "home",
    "school",
    "hospital",
    "hotel",
    "shop",
    "store",
    "office",
    "bank",
    "library",
    "museum",
    "castle",
    "palace",
    "tower",
    "lighthouse",
    "stadium",
    "airport",
    "station",
    "factory",
    "restaurant",
    "cafe",
    "cinema",
    "garage",
    "barn",
    "bridge",
    "dam",
    "tree",
    "forest",
    "jungle",
    "plant",
    "flower",
    "rose",
    "sun",
    "moon",
    "star",
    "cloud",
    "rain",
    "rainbow",
    "mountain",
    "hill",
    "river",
    "lake",
    "waterfall",
    "volcano",
    "island",
    "beach",
    "garden",
    "desert",
    "snowman",
    "cactus",
    "leaf",
    "grass",
    "cat",
    "kitten",
    "dog",
    "puppy",
    "lion",
    "tiger",
    "leopard",
    "cheetah",
    "elephant",
    "giraffe",
    "zebra",
    "horse",
    "pony",
    "cow",
    "buffalo",
    "goat",
    "sheep",
    "pig",
    "chicken",
    "hen",
    "duck",
    "eagle",
    "parrot",
    "peacock",
    "owl",
    "crow",
    "pigeon",
    "sparrow",
    "penguin",
    "fish",
    "shark",
    "whale",
    "dolphin",
    "octopus",
    "turtle",
    "rabbit",
    "bunny",
    "mouse",
    "hamster",
    "bear",
    "panda",
    "fox",
    "wolf",
    "deer",
    "camel",
    "kangaroo",
    "monkey",
    "gorilla",
    "snake",
    "crocodile",
    "frog",
    "butterfly",
    "bee",
    "spider",
    "boy",
    "girl",
    "man",
    "woman",
    "person",
    "baby",
    "child",
    "teacher",
    "doctor",
    "nurse",
    "farmer",
    "soldier",
    "king",
    "queen",
    "prince",
    "princess",
    "superhero",
    "pirate",
    "astronaut",
    "chef",
    "scientist",
    "engineer",
    "artist",
    "student",
    "robot",
    "apple",
    "banana",
    "orange",
    "mango",
    "watermelon",
    "grapes",
    "strawberry",
    "pineapple",
    "coconut",
    "lemon",
    "pizza",
    "burger",
    "sandwich",
    "cake",
    "donut",
    "ice cream",
    "chocolate",
    "cookie",
    "bread",
    "rice",
    "noodles",
    "fries",
    "egg",
    "cheese",
    "cupcake",
    "candy",
    "popcorn",
    "carrot",
    "potato",
    "tomato",
    "pumpkin",
    "football",
    "cricket",
    "tennis",
    "basketball",
    "baseball",
    "hockey",
    "golf",
    "trophy",
    "medal",
    "ball",
    "bat",
    "racket",
    "skateboard",
    "surfboard",
    "heart",
    "smiley",
    "circle",
    "square",
    "triangle",
    "diamond",
    "arrow",
    "planet",
    "earth",
    "mars",
    "jupiter",
    "saturn",
    "galaxy",
    "universe",
    "computer",
    "laptop",
    "phone",
    "tablet",
    "camera",
    "television",
    "radio",
    "speaker",
    "headphones",
    "watch",
    "clock",
    "lamp",
    "chair",
    "table",
    "bed",
    "sofa",
    "door",
    "window",
    "book",
    "pencil",
    "pen",
    "bag",
    "backpack",
    "umbrella",
    "shoe",
    "hat",
    "shirt",
    "dress",
    "bottle",
    "cup",
    "glass",
    "plate",
    "spoon",
    "fork",
    "key",
    "lock",
    "gift",
    "balloon",
    "guitar",
    "piano",
    "drum",
    "violin",
    "flute",
    "microphone",
    "keyboard",
    "printer",
    "map",
    "flag",
    "crown",
    "shield",
    "magic wand",
    "treasure chest",
    "campfire",
    "tent",
    "road",
    "traffic light",
    "street lamp",
    "fountain",
    "playground",
    "slide",
    "swing",
    "bench",
    "flower pot",
    "watering can",
    "farm",
    "windmill",
    "silo"
  )


val descriptors =
  Array(
    "cute",
    "big",
    "small",
    "beautiful",
    "colorful",
    "cool",
    "modern",
    "classic",
    "cartoon",
    "realistic",
    "fantasy",
    "magic",
    "futuristic",
    "robotic",
    "neon",
    "golden",
    "silver",
    "blue",
    "red",
    "green",
    "purple",
    "pink",
    "orange"
  )


val scenes =
  Array(
    "in a park",
    "in a city",
    "in a forest",
    "on a beach",
    "at night",
    "at sunrise",
    "at sunset",
    "in space",
    "underwater",
    "in a garden",
    "on a mountain",
    "in a village",
    "in a fantasy world",
    "in a magical world",
    "in a cartoon world",
    "in a futuristic city"
  )


val allThings =
  new scala.collection.mutable.ArrayBuffer[String]()


var baseIndex =
  0

while (
  baseIndex <
  baseThings.length
) {

  allThings +=
    baseThings(baseIndex)

  var descriptorIndex =
    0

  while (
    descriptorIndex <
    descriptors.length
  ) {

    allThings +=
      (
        descriptors(descriptorIndex) +
        " " +
        baseThings(baseIndex)
      )

    descriptorIndex += 1
  }

  var sceneIndex =
    0

  while (
    sceneIndex <
    scenes.length
  ) {

    allThings +=
      (
        baseThings(baseIndex) +
        " " +
        scenes(sceneIndex)
      )

    sceneIndex += 1
  }

  baseIndex += 1
}


// ============================================================
//                    LIST MODEL
// ============================================================

val listModel =
  new DefaultListModel[String]()


var itemIndex =
  0

while (
  itemIndex <
  allThings.length
) {

  listModel.addElement(
    allThings(itemIndex)
  )

  itemIndex += 1
}


// ============================================================
//                     SEARCH PANEL
// ============================================================

searchField =
  new JTextField(
    12
  )

searchButton =
  makeButton(
    "SEARCH"
  )


def performSearch(): Unit = {

  val query =
    searchField
      .getText
      .trim
      .toLowerCase

  listModel.clear()

  if (
    query.length == 0
  ) {

    var i =
      0

    while (
      i < allThings.length
    ) {

      listModel.addElement(
        allThings(i)
      )

      i += 1
    }

    setStatus(
      "SHOWING ALL " +
      allThings.length +
      " THINGS"
    )

  } else {

    var i =
      0

    var found =
      0

    while (
      i < allThings.length
    ) {

      val value =
        allThings(i)

      if (
        value
          .toLowerCase
          .indexOf(query) >= 0
      ) {

        listModel.addElement(
          value
        )

        found += 1
      }

      i += 1
    }

    setStatus(
      "FOUND " +
      found +
      " THINGS"
    )
  }
}


searchButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      performSearch()
    }
  }
)


searchField.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      performSearch()
    }
  }
)


// ============================================================
//                     MAIN FRAME
// ============================================================

frame =
  new JFrame(
    "ULTRA LEGEND SMART DRAW - 1000+ THINGS"
  )

frame.setDefaultCloseOperation(
  WindowConstants.EXIT_ON_CLOSE
)

frame.setSize(
  1400,
  850
)

frame.setLocationRelativeTo(
  null
)


// ============================================================
//                        STATUS
// ============================================================

status =
  new JLabel(
    "READY"
  )

status.setFont(
  new Font(
    "Arial",
    Font.BOLD,
    15
  )
)


// ============================================================
//                         CANVAS
// ============================================================

canvas =
  new JPanel {

    override def paintComponent(
      graphics: Graphics
    ): Unit = {

      super.paintComponent(
        graphics
      )

      val g =
        graphics.asInstanceOf[
          Graphics2D
        ]

      g.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON
      )

      g.setRenderingHint(
        RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC
      )

      val dw =
        shownWidth()

      val dh =
        shownHeight()

      val ox =
        (getWidth - dw) / 2

      val oy =
        (getHeight - dh) / 2

      g.setColor(
        new Color(
          235,
          235,
          235
        )
      )

      g.fillRect(
        0,
        0,
        getWidth,
        getHeight
      )

      g.drawImage(
        drawingImage,
        ox,
        oy,
        dw,
        dh,
        null
      )

      if (
        gridEnabled
      ) {

        g.setColor(
          new Color(
            0,
            0,
            0,
            35
          )
        )

        var gx =
          ox

        while (
          gx <= ox + dw
        ) {

          g.drawLine(
            gx,
            oy,
            gx,
            oy + dh
          )

          gx += math.max(
            20,
            (40 * zoomLevel).toInt
          )
        }

        var gy =
          oy

        while (
          gy <= oy + dh
        ) {

          g.drawLine(
            ox,
            gy,
            ox + dw,
            gy
          )

          gy += math.max(
            20,
            (40 * zoomLevel).toInt
          )
        }
      }
    }
  }

canvas.setBackground(
  Color.WHITE
)

canvas.setCursor(
  Cursor.getPredefinedCursor(
    Cursor.CROSSHAIR_CURSOR
  )
)


// ============================================================
//                 RIGHT SIDE THINGS LIST
// ============================================================

thingsList =
  new JList[String](
    listModel
  )

thingsList.setSelectionMode(
  ListSelectionModel.SINGLE_SELECTION
)

thingsList.setFont(
  new Font(
    "Arial",
    Font.PLAIN,
    14
  )
)

thingsList.setVisibleRowCount(
  25
)


val scrollPane =
  new JScrollPane(
    thingsList
  )

scrollPane.setPreferredSize(
  new Dimension(
    310,
    620
  )
)


// ============================================================
//                    RIGHT PANEL
// ============================================================

val rightTitle =
  new JLabel(
    "1000+ THINGS"
  )

rightTitle.setFont(
  new Font(
    "Arial",
    Font.BOLD,
    18
  )
)


val rightHelp =
  new JLabel(
    "CLICK ANY ITEM = INSTANT DRAW"
  )

rightHelp.setFont(
  new Font(
    "Arial",
    Font.BOLD,
    11
  )
)


val searchTop =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      3,
      3
    )
  )

searchTop.add(
  searchField
)

searchTop.add(
  searchButton
)


val rightTop =
  new JPanel(
    new BorderLayout()
  )

rightTop.add(
  rightTitle,
  BorderLayout.NORTH
)

rightTop.add(
  rightHelp,
  BorderLayout.CENTER
)

rightTop.add(
  searchTop,
  BorderLayout.SOUTH
)


val rightPanel =
  new JPanel(
    new BorderLayout(
      5,
      5
    )
  )

rightPanel.setBorder(
  BorderFactory.createEmptyBorder(
    6,
    6,
    6,
    6
  )
)

rightPanel.add(
  rightTop,
  BorderLayout.NORTH
)

rightPanel.add(
  scrollPane,
  BorderLayout.CENTER
)


// ============================================================
//                    TOOL BUTTONS
// ============================================================

pencilButton =
  makeButton(
    "PENCIL"
  )

eraserButton =
  makeButton(
    "ERASER"
  )

undoButton =
  makeButton(
    "UNDO"
  )

redoButton =
  makeButton(
    "REDO"
  )

clearButton =
  makeButton(
    "CLEAR"
  )

saveButton =
  makeButton(
    "SAVE"
  )

colorizeButton =
  makeButton(
    "COLORIZE"
  )

threeDButton =
  makeButton(
    "3D"
  )

zoomInButton =
  makeButton(
    "ZOOM +"
  )

zoomOutButton =
  makeButton(
    "ZOOM -"
  )

gridButton =
  makeButton(
    "GRID"
  )

resetButton =
  makeButton(
    "RESET"
  )

drawButton =
  makeButton(
    "DRAW IT"
  )

randomButton =
  makeButton(
    "RANDOM"
  )

customColorButton =
  makeButton(
    "CUSTOM"
  )


// ============================================================
//                         CONTROLS
// ============================================================

toolBox =
  new JComboBox[String](
    Array(
      "PENCIL",
      "ERASER",
      "LINE",
      "RECTANGLE",
      "ELLIPSE",
      "SPRAY"
    )
  )


sizeBox =
  new JComboBox[String](
    Array(
      "2",
      "3",
      "4",
      "6",
      "8",
      "10",
      "14",
      "18",
      "24",
      "30",
      "40",
      "50"
    )
  )

sizeBox.setSelectedItem(
  "6"
)


styleBox =
  new JComboBox[String](
    Array(
      "NORMAL",
      "3D",
      "GLOSSY",
      "NEON",
      "COMIC",
      "MAGIC",
      "GOLD"
    )
  )


writeField =
  new JTextField(
    "CAR",
    15
  )


// ============================================================
//                         TOOLBAR
// ============================================================

val toolbar =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      4,
      4
    )
  )

toolbar.add(
  pencilButton
)

toolbar.add(
  eraserButton
)

toolbar.add(
  undoButton
)

toolbar.add(
  redoButton
)

toolbar.add(
  clearButton
)

toolbar.add(
  saveButton
)

toolbar.add(
  colorizeButton
)

toolbar.add(
  threeDButton
)

toolbar.add(
  gridButton
)

toolbar.add(
  zoomInButton
)

toolbar.add(
  zoomOutButton
)

toolbar.add(
  resetButton
)

toolbar.add(
  new JLabel(
    "TOOL:"
  )
)

toolbar.add(
  toolBox
)

toolbar.add(
  new JLabel(
    "SIZE:"
  )
)

toolbar.add(
  sizeBox
)

toolbar.add(
  new JLabel(
    "STYLE:"
  )
)

toolbar.add(
  styleBox
)

toolbar.add(
  new JLabel(
    "WRITE:"
  )
)

toolbar.add(
  writeField
)

toolbar.add(
  drawButton
)

toolbar.add(
  randomButton
)


// ============================================================
//                         COLORS
// ============================================================

def addColorButton(
  c: Color
): Unit = {

  val b =
    new JButton(
      " "
    )

  b.setPreferredSize(
    new Dimension(
      22,
      22
    )
  )

  b.setBackground(
    c
  )

  b.setOpaque(
    true
  )

  b.setFocusable(
    false
  )

  b.addActionListener(
    new ActionListener {

      override def actionPerformed(
        e: ActionEvent
      ): Unit = {

        currentColor =
          c

        currentTool =
          "PENCIL"

        toolBox.setSelectedItem(
          "PENCIL"
        )

        setStatus(
          "COLOR SELECTED"
        )
      }
    }
  )

  toolbar.add(
    b
  )
}


addColorButton(Color.BLACK)
addColorButton(Color.RED)
addColorButton(Color.BLUE)
addColorButton(Color.GREEN)
addColorButton(Color.YELLOW)
addColorButton(Color.ORANGE)
addColorButton(Color.MAGENTA)
addColorButton(Color.CYAN)
addColorButton(Color.PINK)
addColorButton(new Color(128,64,0))
addColorButton(new Color(110,70,180))


toolbar.add(
  customColorButton
)


customColorButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val chosen =
        JColorChooser.showDialog(
          frame,
          "CUSTOM COLOR",
          currentColor
        )

      if (
        chosen != null
      ) {

        currentColor =
          chosen

        currentTool =
          "PENCIL"

        toolBox.setSelectedItem(
          "PENCIL"
        )

        setStatus(
          "CUSTOM COLOR SELECTED"
        )
      }
    }
  }
)


// ============================================================
//                    PENCIL BUTTON
// ============================================================

pencilButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      currentTool =
        "PENCIL"

      toolBox.setSelectedItem(
        "PENCIL"
      )

      setStatus(
        "PENCIL ACTIVE"
      )
    }
  }
)


// ============================================================
//                    ERASER BUTTON
// ============================================================

eraserButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      currentTool =
        "ERASER"

      toolBox.setSelectedItem(
        "ERASER"
      )

      setStatus(
        "ERASER ACTIVE"
      )
    }
  }
)


// ============================================================
//                       TOOL BOX
// ============================================================

toolBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val selected =
        toolBox.getSelectedItem

      if (
        selected != null
      ) {

        currentTool =
          selected.toString

        setStatus(
          "TOOL: " +
          currentTool
        )
      }
    }
  }
)


// ============================================================
//                       SIZE BOX
// ============================================================

sizeBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      try {

        brushSize =
          sizeBox
            .getSelectedItem
            .toString
            .toFloat

      } catch {

        case _: Exception =>
      }
    }
  }
)


// ============================================================
//                    STYLE BOX
// ============================================================

styleBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      currentStyle =
        styleBox
          .getSelectedItem
          .toString
    }
  }
)


// ============================================================
//              MOST IMPORTANT PART:
//        ANY LIST ITEM = INSTANT DRAW
// ============================================================

thingsList.addListSelectionListener(
  new javax.swing.event.ListSelectionListener {

    override def valueChanged(
      e: javax.swing.event.ListSelectionEvent
    ): Unit = {

      if (
        !e.getValueIsAdjusting
      ) {

        val selected =
          thingsList.getSelectedValue

        if (
          selected != null &&
          selected.trim.length > 0
        ) {

          writeField.setText(
            selected
          )

          drawFromText(
            selected
          )

          setStatus(
            "INSTANT DRAW: " +
            selected
          )

          refreshCanvas()
        }
      }
    }
  }
)


// ============================================================
//                       DRAW IT
// ============================================================

drawButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val text =
        writeField
          .getText
          .trim

      if (
        text.length > 0
      ) {

        drawFromText(
          text
        )

      } else {

        val selected =
          thingsList.getSelectedValue

        if (
          selected != null
        ) {

          writeField.setText(
            selected
          )

          drawFromText(
            selected
          )

        } else {

          setStatus(
            "WRITE OR SELECT A THING"
          )
        }
      }
    }
  }
)


// ============================================================
//                         ENTER
// ============================================================

writeField.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      drawFromText(
        writeField.getText
      )
    }
  }
)


// ============================================================
//                         RANDOM
// ============================================================

val randomGenerator =
  new scala.util.Random()


randomButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val index =
        randomGenerator.nextInt(
          allThings.length
        )

      val value =
        allThings(index)

      writeField.setText(
        value
      )

      drawFromText(
        value
      )

      setStatus(
        "RANDOM: " +
        value
      )
    }
  }
)


// ============================================================
//                          UNDO
// ============================================================

undoButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      if (
        undoImage != null
      ) {

        redoImage =
          copyImage(
            drawingImage
          )

        restoreImage(
          undoImage
        )

        undoImage =
          null

        setStatus(
          "UNDO COMPLETE"
        )

      } else {

        setStatus(
          "NOTHING TO UNDO"
        )
      }
    }
  }
)


// ============================================================
//                          REDO
// ============================================================

redoButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      if (
        redoImage != null
      ) {

        undoImage =
          copyImage(
            drawingImage
          )

        restoreImage(
          redoImage
        )

        redoImage =
          null

        setStatus(
          "REDO COMPLETE"
        )

      } else {

        setStatus(
          "NOTHING TO REDO"
        )
      }
    }
  }
)


// ============================================================
//                          CLEAR
// ============================================================

clearButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      rememberUndo()

      clearImage()

      setStatus(
        "CANVAS CLEARED"
      )
    }
  }
)


// ============================================================
//                           SAVE
// ============================================================

saveButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      saveImageToFile(
        drawingImage,
        frame
      )
    }
  }
)


// ============================================================
//                          GRID
// ============================================================

gridButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      gridEnabled =
        !gridEnabled

      refreshCanvas()

      if (
        gridEnabled
      ) {

        setStatus(
          "GRID ON"
        )

      } else {

        setStatus(
          "GRID OFF"
        )
      }
    }
  }
)


// ============================================================
//                         ZOOM IN
// ============================================================

zoomInButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      zoomLevel =
        math.min(
          2.5,
          zoomLevel + 0.10
        )

      refreshCanvas()

      setStatus(
        "ZOOM " +
        math.round(
          zoomLevel * 100
        ) +
        "%"
      )
    }
  }
)


// ============================================================
//                        ZOOM OUT
// ============================================================

zoomOutButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      zoomLevel =
        math.max(
          0.50,
          zoomLevel - 0.10
        )

      refreshCanvas()

      setStatus(
        "ZOOM " +
        math.round(
          zoomLevel * 100
        ) +
        "%"
      )
    }
  }
)


// ============================================================
//                           RESET
// ============================================================

resetButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      zoomLevel =
        1.0

      gridEnabled =
        false

      currentTheme =
        "SKY"

      currentStyle =
        "NORMAL"

      refreshCanvas()

      setStatus(
        "RESET COMPLETE"
      )
    }
  }
)


// ============================================================
//                    MOUSE PRESS
// ============================================================

canvas.addMouseListener(
  new MouseAdapter {

    override def mousePressed(
      e: MouseEvent
    ): Unit = {

      if (
        !insideCanvas(
          e.getX,
          e.getY
        )
      ) {

        isDrawing =
          false

        return
      }

      rememberUndo()

      isDrawing =
        true

      startX =
        mouseToImageX(
          e.getX
        )

      startY =
        mouseToImageY(
          e.getY
        )

      lastX =
        startX

      lastY =
        startY

      if (
        currentTool ==
        "PENCIL"
      ) {

        drawingGraphics.setColor(
          currentColor
        )

        val s =
          math.max(
            1,
            brushSize.toInt
          )

        drawingGraphics.fillOval(
          startX - s / 2,
          startY - s / 2,
          s,
          s
        )
      }

      if (
        currentTool ==
        "ERASER"
      ) {

        drawingGraphics.setColor(
          Color.WHITE
        )

        val s =
          math.max(
            1,
            brushSize.toInt
          )

        drawingGraphics.fillOval(
          startX - s / 2,
          startY - s / 2,
          s,
          s
        )
      }

      if (
        currentTool ==
        "SPRAY"
      ) {

        drawSpray(
          startX,
          startY
        )
      }

      refreshCanvas()
    }


    override def mouseReleased(
      e: MouseEvent
    ): Unit = {

      if (
        !isDrawing
      ) {

        return
      }

      val endX =
        mouseToImageX(
          e.getX
        )

      val endY =
        mouseToImageY(
          e.getY
        )

      if (
        currentTool ==
        "LINE"
      ) {

        drawingGraphics.setColor(
          currentColor
        )

        drawingGraphics.setStroke(
          new BasicStroke(
            brushSize,
            BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND
          )
        )

        drawingGraphics.drawLine(
          startX,
          startY,
          endX,
          endY
        )
      }

      if (
        currentTool ==
        "RECTANGLE"
      ) {

        drawingGraphics.setColor(
          currentColor
        )

        drawingGraphics.setStroke(
          new BasicStroke(
            brushSize
          )
        )

        val x =
          math.min(
            startX,
            endX
          )

        val y =
          math.min(
            startY,
            endY
          )

        val width =
          math.abs(
            endX -
            startX
          )

        val height =
          math.abs(
            endY -
            startY
          )

        drawingGraphics.drawRect(
          x,
          y,
          width,
          height
        )
      }

      if (
        currentTool ==
        "ELLIPSE"
      ) {

        drawingGraphics.setColor(
          currentColor
        )

        drawingGraphics.setStroke(
          new BasicStroke(
            brushSize
          )
        )

        val x =
          math.min(
            startX,
            endX
          )

        val y =
          math.min(
            startY,
            endY
          )

        val width =
          math.abs(
            endX -
            startX
          )

        val height =
          math.abs(
            endY -
            startY
          )

        drawingGraphics.drawOval(
          x,
          y,
          width,
          height
        )
      }

      isDrawing =
        false

      refreshCanvas()
    }
  }
)


// ============================================================
//                         MOUSE DRAG
// ============================================================

canvas.addMouseMotionListener(
  new MouseMotionAdapter {

    override def mouseDragged(
      e: MouseEvent
    ): Unit = {

      if (
        !isDrawing
      ) {

        return
      }

      if (
        !insideCanvas(
          e.getX,
          e.getY
        )
      ) {

        return
      }

      val x =
        mouseToImageX(
          e.getX
        )

      val y =
        mouseToImageY(
          e.getY
        )

      if (
        currentTool ==
        "PENCIL"
      ) {

        drawingGraphics.setColor(
          currentColor
        )

        drawingGraphics.setStroke(
          new BasicStroke(
            brushSize,
            BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND
          )
        )

        drawingGraphics.drawLine(
          lastX,
          lastY,
          x,
          y
        )
      }

      if (
        currentTool ==
        "ERASER"
      ) {

        drawingGraphics.setColor(
          Color.WHITE
        )

        drawingGraphics.setStroke(
          new BasicStroke(
            brushSize,
            BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND
          )
        )

        drawingGraphics.drawLine(
          lastX,
          lastY,
          x,
          y
        )
      }

      if (
        currentTool ==
        "SPRAY"
      ) {

        drawSpray(
          x,
          y
        )
      }

      lastX =
        x

      lastY =
        y

      refreshCanvas()
    }
  }
)


// ============================================================
//                     BACKGROUND LIST
// ============================================================

val backgroundNames =
  Array(
    "SKY",
    "SUNNY",
    "SUNRISE",
    "SUNSET",
    "GOLDEN SUNSET",
    "NIGHT",
    "STAR NIGHT",
    "SPACE",
    "GALAXY",
    "COSMIC",
    "OCEAN",
    "DEEP OCEAN",
    "UNDERWATER",
    "BEACH",
    "TROPICAL BEACH",
    "ISLAND",
    "PARADISE",
    "FOREST",
    "DEEP FOREST",
    "JUNGLE",
    "MOUNTAIN",
    "SNOW",
    "ICE",
    "DESERT",
    "RAINBOW",
    "CITY",
    "CITY NIGHT",
    "NEON CITY",
    "FUTURE CITY",
    "MAGIC",
    "FANTASY",
    "FAIRY WORLD",
    "CANDY WORLD",
    "PINK DREAM",
    "BLUE DREAM",
    "PURPLE DREAM",
    "GOLDEN WORLD",
    "ROYAL WORLD",
    "FIRE WORLD",
    "VOLCANO",
    "SPRING",
    "SUMMER",
    "AUTUMN",
    "WINTER",
    "DREAM WORLD"
  )


def getBackgroundColors(
  theme: String
): Array[Color] = {

  val t =
    theme.toUpperCase

  if (
    t.indexOf("SUNSET") >= 0
  ) {

    Array(
      new Color(255,120,80),
      new Color(100,50,160)
    )

  } else if (
    t.indexOf("SUNRISE") >= 0
  ) {

    Array(
      new Color(255,180,100),
      new Color(255,235,170)
    )

  } else if (
    t.indexOf("NIGHT") >= 0
  ) {

    Array(
      new Color(10,20,70),
      new Color(65,45,125)
    )

  } else if (
    t.indexOf("SPACE") >= 0 ||
    t.indexOf("GALAXY") >= 0 ||
    t.indexOf("COSMIC") >= 0
  ) {

    Array(
      new Color(3,5,25),
      new Color(70,10,110)
    )

  } else if (
    t.indexOf("OCEAN") >= 0 ||
    t.indexOf("UNDERWATER") >= 0
  ) {

    Array(
      new Color(90,220,255),
      new Color(10,80,190)
    )

  } else if (
    t.indexOf("FOREST") >= 0 ||
    t.indexOf("JUNGLE") >= 0
  ) {

    Array(
      new Color(170,235,190),
      new Color(45,145,70)
    )

  } else if (
    t.indexOf("DESERT") >= 0
  ) {

    Array(
      new Color(255,220,145),
      new Color(205,145,80)
    )

  } else if (
    t.indexOf("MAGIC") >= 0 ||
    t.indexOf("FANTASY") >= 0
  ) {

    Array(
      new Color(215,190,255),
      new Color(100,50,165)
    )

  } else if (
    t.indexOf("CANDY") >= 0 ||
    t.indexOf("PINK") >= 0
  ) {

    Array(
      new Color(255,200,225),
      new Color(245,100,175)
    )

  } else if (
    t.indexOf("GOLD") >= 0 ||
    t.indexOf("ROYAL") >= 0
  ) {

    Array(
      new Color(255,235,130),
      new Color(200,140,35)
    )

  } else if (
    t.indexOf("FIRE") >= 0 ||
    t.indexOf("VOLCANO") >= 0
  ) {

    Array(
      new Color(255,155,70),
      new Color(180,35,35)
    )

  } else if (
    t.indexOf("PURPLE") >= 0
  ) {

    Array(
      new Color(220,190,255),
      new Color(105,45,170)
    )

  } else {

    Array(
      new Color(170,225,255),
      new Color(100,190,110)
    )
  }
}


def makeColorizedImage(
  theme: String,
  style: String
): BufferedImage = {

  val colors =
    getBackgroundColors(
      theme
    )

  val result =
    new BufferedImage(
      CANVAS_WIDTH,
      CANVAS_HEIGHT,
      BufferedImage.TYPE_INT_RGB
    )

  val g =
    result.createGraphics()

  g.setPaint(
    new GradientPaint(
      0,
      0,
      colors(0),
      0,
      CANVAS_HEIGHT,
      colors(1)
    )
  )

  g.fillRect(
    0,
    0,
    CANVAS_WIDTH,
    CANVAS_HEIGHT
  )

  var x =
    0

  while (
    x < CANVAS_WIDTH
  ) {

    var y =
      0

    while (
      y < CANVAS_HEIGHT
    ) {

      val rgb =
        drawingImage.getRGB(
          x,
          y
        )

      val r =
        (rgb >> 16) & 255

      val gr =
        (rgb >> 8) & 255

      val b =
        rgb & 255

      if (
        r < 245 ||
        gr < 245 ||
        b < 245
      ) {

        result.setRGB(
          x,
          y,
          rgb
        )
      }

      y += 1
    }

    x += 1
  }

  if (
    style ==
    "3D"
  ) {

    g.setColor(
      new Color(
        0,
        0,
        0,
        75
      )
    )

    g.setStroke(
      new BasicStroke(
        12
      )
    )

    g.drawRoundRect(
      12,
      12,
      976,
      576,
      30,
      30
    )
  }

  if (
    style ==
    "GLOSSY"
  ) {

    g.setColor(
      new Color(
        255,
        255,
        255,
        65
      )
    )

    g.fillRoundRect(
      20,
      20,
      960,
      125,
      35,
      35
    )
  }

  if (
    style ==
    "NEON"
  ) {

    g.setColor(
      new Color(
        0,
        255,
        255,
        140
      )
    )

    g.setStroke(
      new BasicStroke(
        10
      )
    )

    g.drawRoundRect(
      10,
      10,
      980,
      580,
      25,
      25
    )
  }

  if (
    style ==
    "COMIC"
  ) {

    g.setColor(
      Color.BLACK
    )

    g.setStroke(
      new BasicStroke(
        8
      )
    )

    g.drawRoundRect(
      8,
      8,
      984,
      584,
      25,
      25
    )
  }

  if (
    style ==
    "MAGIC"
  ) {

    g.setColor(
      Color.WHITE
    )

    var i =
      0

    while (
      i < 70
    ) {

      g.fillOval(
        (i * 83) %
        CANVAS_WIDTH,
        (i * 47) %
        CANVAS_HEIGHT,
        5,
        5
      )

      i += 1
    }
  }

  if (
    style ==
    "GOLD"
  ) {

    g.setColor(
      new Color(
        255,
        215,
        70,
        140
      )
    )

    g.setStroke(
      new BasicStroke(
        10
      )
    )

    g.drawRoundRect(
      12,
      12,
      976,
      576,
      30,
      30
    )
  }

  g.dispose()

  result
}


// ============================================================
//                    COLORIZE BUTTON
// ============================================================

colorizeButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val bg =
        new JComboBox[String](
          backgroundNames
        )

      val style =
        new JComboBox[String](
          Array(
            "NORMAL",
            "3D",
            "GLOSSY",
            "NEON",
            "COMIC",
            "MAGIC",
            "GOLD"
          )
        )

      val panel =
        new JPanel(
          new FlowLayout(
            FlowLayout.LEFT
          )
        )

      panel.add(
        new JLabel(
          "BACKGROUND:"
        )
      )

      panel.add(
        bg
      )

      panel.add(
        new JLabel(
          "STYLE:"
        )
      )

      panel.add(
        style
      )

      val answer =
        JOptionPane.showConfirmDialog(
          frame,
          panel,
          "COLORIZE DRAWING",
          JOptionPane.OK_CANCEL_OPTION,
          JOptionPane.PLAIN_MESSAGE
        )

      if (
        answer ==
        JOptionPane.OK_OPTION
      ) {

        rememberUndo()

        currentTheme =
          bg.getSelectedItem
            .toString

        currentStyle =
          style.getSelectedItem
            .toString

        styleBox.setSelectedItem(
          currentStyle
        )

        val result =
          makeColorizedImage(
            currentTheme,
            currentStyle
          )

        restoreImage(
          result
        )

        setStatus(
          "COLORIZE COMPLETE"
        )
      }
    }
  }
)


// ============================================================
//                         3D PREVIEW
// ============================================================

threeDButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val result =
        makeColorizedImage(
          currentTheme,
          "3D"
        )

      val preview =
        new JFrame(
          "3D PREVIEW"
        )

      preview.setDefaultCloseOperation(
        WindowConstants.DISPOSE_ON_CLOSE
      )

      preview.setSize(
        1080,
        760
      )

      preview.setLocationRelativeTo(
        frame
      )

      val previewPanel =
        new JPanel {

          override def paintComponent(
            graphics: Graphics
          ): Unit = {

            super.paintComponent(
              graphics
            )

            val g =
              graphics.asInstanceOf[
                Graphics2D
              ]

            g.setRenderingHint(
              RenderingHints.KEY_INTERPOLATION,
              RenderingHints.VALUE_INTERPOLATION_BICUBIC
            )

            val maxW =
              getWidth - 30

            val maxH =
              getHeight - 30

            val scale =
              math.min(
                maxW.toDouble /
                  result.getWidth,
                maxH.toDouble /
                  result.getHeight
              )

            val rw =
              math.max(
                1,
                (result.getWidth * scale).toInt
              )

            val rh =
              math.max(
                1,
                (result.getHeight * scale).toInt
              )

            val x =
              (getWidth - rw) / 2

            val y =
              (getHeight - rh) / 2

            g.drawImage(
              result,
              x,
              y,
              rw,
              rh,
              null
            )
          }
        }

      val save3D =
        new JButton(
          "SAVE 3D"
        )

      save3D.setFocusable(
        false
      )

      save3D.addActionListener(
        new ActionListener {

          override def actionPerformed(
            e: ActionEvent
          ): Unit = {

            saveImageToFile(
              result,
              preview
            )
          }
        }
      )

      preview.setLayout(
        new BorderLayout()
      )

      preview.add(
        previewPanel,
        BorderLayout.CENTER
      )

      preview.add(
        save3D,
        BorderLayout.SOUTH
      )

      preview.setVisible(
        true
      )
    }
  }
)


// ============================================================
//                        KEYBOARD
// ============================================================

canvas.setFocusable(
  true
)

canvas.addKeyListener(
  new KeyAdapter {

    override def keyPressed(
      e: KeyEvent
    ): Unit = {

      if (
        e.isControlDown &&
        e.getKeyCode ==
        KeyEvent.VK_Z
      ) {

        if (
          undoImage != null
        ) {

          redoImage =
            copyImage(
              drawingImage
            )

          restoreImage(
            undoImage
          )

          undoImage =
            null

          setStatus(
            "UNDO COMPLETE"
          )
        }
      }

      if (
        e.isControlDown &&
        e.getKeyCode ==
        KeyEvent.VK_Y
      ) {

        if (
          redoImage != null
        ) {

          undoImage =
            copyImage(
              drawingImage
            )

          restoreImage(
            redoImage
          )

          redoImage =
            null

          setStatus(
            "REDO COMPLETE"
          )
        }
      }

      if (
        e.getKeyCode ==
        KeyEvent.VK_F2
      ) {

        colorizeButton.doClick()
      }

      if (
        e.getKeyCode ==
        KeyEvent.VK_F3
      ) {

        threeDButton.doClick()
      }
    }
  }
)


// ============================================================
//                        BOTTOM BAR
// ============================================================

val bottomBar =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      8,
      5
    )
  )

bottomBar.add(
  status
)

bottomBar.add(
  new JLabel(
    "CLICK ANY RIGHT-SIDE THING = DRAW | WRITE + DRAW IT | F2 Colorize | F3 3D"
  )
)


// ============================================================
//                          FINAL UI
// ============================================================

frame.setLayout(
  new BorderLayout(
    4,
    4
  )
)

frame.add(
  toolbar,
  BorderLayout.NORTH
)

frame.add(
  canvas,
  BorderLayout.CENTER
)

frame.add(
  rightPanel,
  BorderLayout.EAST
)

frame.add(
  bottomBar,
  BorderLayout.SOUTH
)


// ============================================================
//                           START
// ============================================================

SwingUtilities.invokeLater(
  new Runnable {

    override def run(): Unit = {

      frame.setVisible(
        true
      )

      canvas.requestFocusInWindow()

      setStatus(
        "READY - " +
        allThings.length +
        " THINGS LOADED!"
      )
    }
  }
)