Code Sketch


yo..............
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.Graphics
import java.awt.Graphics2D
import java.awt.RenderingHints

import java.awt.event.ActionEvent
import java.awt.event.ActionListener
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.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 W = 1000
val H = 600

val CX = 500
val CY = 300


// ============================================================
// COMPONENT REFERENCES
// IMPORTANT: DECLARED FIRST
// ============================================================

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 gridButton: JButton = null
var zoomInButton: JButton = null
var zoomOutButton: JButton = null
var resetButton: JButton = null
var drawButton: JButton = null
var randomButton: JButton = null
var customButton: JButton = null

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

var writeField: JTextField = null

var thingList: JList[String] = null


// ============================================================
// APPLICATION STATE
// ============================================================

var currentColor = Color.BLACK

var brushSize = 6.0f

var currentTool = "PENCIL"

var zoom = 1.0

var drawing = false

var lastX = 0
var lastY = 0

var startX = 0
var startY = 0

var gridOn = false

var themeName = "SKY"
var styleName = "NORMAL"

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


// ============================================================
// IMAGE
// ============================================================

val image =
  new BufferedImage(
    W,
    H,
    BufferedImage.TYPE_INT_RGB
  )

val imageGraphics =
  image.createGraphics()

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

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

imageGraphics.setColor(
  Color.WHITE
)

imageGraphics.fillRect(
  0,
  0,
  W,
  H
)


// ============================================================
// BASIC HELPERS
// ============================================================

def setStatus(
  text: String
): Unit = {

  if (
    status != null
  ) {

    status.setText(
      text
    )
  }
}


def refresh(): Unit = {

  if (
    canvas != null
  ) {

    canvas.repaint()
  }
}


def clearImage(): Unit = {

  imageGraphics.setColor(
    Color.WHITE
  )

  imageGraphics.fillRect(
    0,
    0,
    W,
    H
  )

  refresh()
}


def copyImage(
  source: BufferedImage
): BufferedImage = {

  val result =
    new BufferedImage(
      W,
      H,
      BufferedImage.TYPE_INT_RGB
    )

  val g =
    result.createGraphics()

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

  g.dispose()

  result
}


def rememberUndo(): Unit = {

  undoImage =
    copyImage(
      image
    )

  redoImage =
    null
}


def restore(
  source: BufferedImage
): Unit = {

  imageGraphics.setColor(
    Color.WHITE
  )

  imageGraphics.fillRect(
    0,
    0,
    W,
    H
  )

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

  refresh()
}


def makeButton(
  text: String
): JButton = {

  val b =
    new JButton(
      text
    )

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

  b.setFocusable(
    false
  )

  b
}


// ============================================================
// SAVE FUNCTION
// IMPORTANT: BEFORE BUTTON ACTIONS
// ============================================================

def saveImage(
  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
        )
    }
  }
}


// ============================================================
// MOUSE POSITION
// ============================================================

def shownW(): Int = {

  math.max(
    1,
    (W * zoom).toInt
  )
}


def shownH(): Int = {

  math.max(
    1,
    (H * zoom).toInt
  )
}


def offsetX(): Int = {

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


def offsetY(): Int = {

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


def toImageX(
  x: Int
): Int = {

  val value =
    (
      (x - offsetX()) /
      zoom
    ).toInt

  math.max(
    0,
    math.min(
      W - 1,
      value
    )
  )
}


def toImageY(
  y: Int
): Int = {

  val value =
    (
      (y - offsetY()) /
      zoom
    ).toInt

  math.max(
    0,
    math.min(
      H - 1,
      value
    )
  )
}


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

  x >= offsetX() &&
  x < offsetX() + shownW() &&
  y >= offsetY() &&
  y < offsetY() + shownH()
}


// ============================================================
// DRAW TOOLS
// ============================================================

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

  imageGraphics.setColor(
    currentColor
  )

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

  var i = 0

  while (
    i < 28
  ) {

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

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

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

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

    i += 1
  }
}


// ============================================================
// BASIC OBJECT DRAWINGS
// ============================================================

def drawGeneric(
  text: String
): Unit = {

  clearImage()

  val g =
    imageGraphics

  val hash =
    math.abs(
      text.hashCode
    )

  val c =
    new Color(
      50 + hash % 180,
      50 + (hash / 7) % 180,
      50 + (hash / 13) % 180
    )

  g.setColor(
    c
  )

  val shape =
    hash % 5

  if (
    shape == 0
  ) {

    g.fillRoundRect(
      CX - 190,
      CY - 120,
      380,
      240,
      40,
      40
    )

  } else if (
    shape == 1
  ) {

    g.fillOval(
      CX - 150,
      CY - 150,
      300,
      300
    )

  } else if (
    shape == 2
  ) {

    val p =
      new java.awt.Polygon()

    p.addPoint(
      CX,
      CY - 170
    )

    p.addPoint(
      CX + 170,
      CY + 150
    )

    p.addPoint(
      CX - 170,
      CY + 150
    )

    g.fillPolygon(
      p
    )

  } else if (
    shape == 3
  ) {

    g.fillRect(
      CX - 160,
      CY - 140,
      320,
      280
    )

  } else {

    g.fillOval(
      CX - 210,
      CY - 100,
      420,
      200
    )
  }

  g.setColor(
    Color.BLACK
  )

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

  val label =
    text.take(
      28
    )

  val tw =
    g.getFontMetrics
      .stringWidth(
        label
      )

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

  g.dispose()

  refresh()
}


def drawCar(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  val cy =
    CY

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

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

  val roof =
    new java.awt.Polygon()

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

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

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

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

  g.fillPolygon(
    roof
  )

  g.setColor(
    new Color(
      120,
      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 - 177,
    cy + 57,
    30,
    30
  )

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

  g.dispose()

  refresh()
}


def drawHouse(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  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,
      80,
      45
    )
  )

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

  g.setColor(
    new Color(
      70,
      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,
    W,
    110
  )

  g.dispose()

  refresh()
}


def drawTree(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

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

  g.fillRect(
    0,
    490,
    W,
    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()

  refresh()
}


def drawRobot(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  val cy =
    CY

  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.dispose()

  refresh()
}


def drawCat(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  val cy =
    CY

  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(
      70,
      190,
      95
    )
  )

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

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

  g.dispose()

  refresh()
}


def drawDog(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  val cy =
    CY

  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()

  refresh()
}


def drawBird(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  val cy =
    CY

  g.setColor(
    new Color(
      120,
      215,
      255
    )
  )

  g.fillRect(
    0,
    0,
    W,
    H
  )

  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.dispose()

  refresh()
}


def drawFish(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  val cy =
    CY

  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()

  refresh()
}


def drawFlower(): Unit = {

  clearImage()

  val g =
    imageGraphics

  val cx =
    CX

  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 flowerColors =
    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 a =
      i * math.Pi / 3.0

    val x =
      cx +
      (math.cos(a) * 80).toInt -
      50

    val y =
      cy +
      (math.sin(a) * 80).toInt -
      50

    g.setColor(
      flowerColors(i)
    )

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

    i += 1
  }

  g.setColor(
    Color.YELLOW
  )

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

  g.dispose()

  refresh()
}


def drawStar(): Unit = {

  clearImage()

  val g =
    imageGraphics

  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
      ) {
        190.0
      } else {
        80.0
      }

    p.addPoint(
      CX +
      (math.cos(a) * r).toInt,
      CY +
      (math.sin(a) * r).toInt
    )

    i += 1
  }

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

  g.fillPolygon(
    p
  )

  g.dispose()

  refresh()
}


def drawHeart(): Unit = {

  clearImage()

  val g =
    imageGraphics

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

  p.moveTo(
    CX,
    CY + 190
  )

  p.curveTo(
    CX - 270,
    CY + 20,
    CX - 170,
    CY - 175,
    CX,
    CY - 45
  )

  p.curveTo(
    CX + 170,
    CY - 175,
    CX + 270,
    CY + 20,
    CX,
    CY + 190
  )

  g.setColor(
    Color.RED
  )

  g.fill(
    p
  )

  g.dispose()

  refresh()
}


def drawFood(): Unit = {

  clearImage()

  val g =
    imageGraphics

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

  g.fillOval(
    CX - 190,
    CY - 100,
    380,
    200
  )

  g.setColor(
    Color.RED
  )

  g.fillOval(
    CX - 120,
    CY - 10,
    38,
    38
  )

  g.fillOval(
    CX + 45,
    CY - 25,
    38,
    38
  )

  g.dispose()

  refresh()
}


def drawSpace(): Unit = {

  clearImage()

  val g =
    imageGraphics

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

  g.fillRect(
    0,
    0,
    W,
    H
  )

  g.setColor(
    Color.WHITE
  )

  var i = 0

  while (
    i < 100
  ) {

    val x =
      (i * 73) %
      W

    val y =
      (i * 43) %
      400

    val s =
      3 + (i % 3)

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

    i += 1
  }

  g.setColor(
    new Color(
      70,
      140,
      240
    )
  )

  g.fillOval(
    CX - 120,
    CY - 120,
    240,
    240
  )

  g.dispose()

  refresh()
}


// ============================================================
// TEXT TO DRAW
// ============================================================

def drawFromText(
  input: String
): Unit = {

  val t =
    input
      .trim
      .toLowerCase

  if (
    t.length == 0
  ) {

    setStatus(
      "WRITE SOMETHING FIRST!"
    )

    return
  }

  rememberUndo()

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

    drawCar()

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

    drawHouse()

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

    drawTree()

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

    drawRobot()

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

    drawCat()

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

    drawDog()

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

    drawBird()

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

    drawFish()

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

    drawFlower()

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

    drawStar()

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

    drawHeart()

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

    drawFood()

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

    drawSpace()

  } else {

    drawGeneric(
      input
    )
  }

  setStatus(
    "DRAW COMPLETE: " +
    input
  )

  refresh()
}


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

val baseThings =
  Array(
    "car",
    "bus",
    "truck",
    "bike",
    "bicycle",
    "motorcycle",
    "scooter",
    "taxi",
    "train",
    "metro",
    "tram",
    "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",
    "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"
  )


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 things =
  new scala.collection.mutable.ArrayBuffer[String]()


var a =
  0

while (
  a < baseThings.length
) {

  things +=
    baseThings(a)

  var b =
    0

  while (
    b < descriptors.length
  ) {

    things +=
      (
        descriptors(b) +
        " " +
        baseThings(a)
      )

    b += 1
  }

  a += 1
}


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"
  )


var c =
  0

while (
  c < baseThings.length
) {

  var d =
    0

  while (
    d < scenes.length
  ) {

    things +=
      (
        baseThings(c) +
        " " +
        scenes(d)
      )

    d += 1
  }

  c += 1
}


// ============================================================
// CREATE RIGHT SIDE LIST
// ============================================================

val listModel =
  new DefaultListModel[String]()


var li =
  0

while (
  li < things.length
) {

  listModel.addElement(
    things(li)
  )

  li += 1
}


thingList =
  new JList[String](
    listModel
  )

thingList.setSelectionMode(
  ListSelectionModel.SINGLE_SELECTION
)

thingList.setVisibleRowCount(
  25
)

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


val scrollPane =
  new JScrollPane(
    thingList
  )

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


// ============================================================
// CREATE FRAME
// ============================================================

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

frame.setDefaultCloseOperation(
  WindowConstants.EXIT_ON_CLOSE
)

frame.setSize(
  1370,
  850
)

frame.setLocationRelativeTo(
  null
)


// ============================================================
// CREATE STATUS
// ============================================================

status =
  new JLabel(
    "READY"
  )

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


// ============================================================
// CREATE 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 =
        shownW()

      val dh =
        shownH()

      val ox =
        (getWidth - dw) / 2

      val oy =
        (getHeight - dh) / 2

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

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

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

      if (
        gridOn
      ) {

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

        var x =
          ox

        while (
          x <= ox + dw
        ) {

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

          x += math.max(
            20,
            (40 * zoom).toInt
          )
        }

        var y =
          oy

        while (
          y <= oy + dh
        ) {

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

          y += math.max(
            20,
            (40 * zoom).toInt
          )
        }
      }
    }
  }


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


// ============================================================
// CREATE 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"
  )

gridButton =
  makeButton(
    "GRID"
  )

zoomInButton =
  makeButton(
    "ZOOM +"
  )

zoomOutButton =
  makeButton(
    "ZOOM -"
  )

resetButton =
  makeButton(
    "RESET"
  )

drawButton =
  makeButton(
    "DRAW IT"
  )

randomButton =
  makeButton(
    "RANDOM"
  )

customButton =
  makeButton(
    "CUSTOM COLOR"
  )


// ============================================================
// 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 addColor(
  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
  )
}


addColor(Color.BLACK)
addColor(Color.RED)
addColor(Color.BLUE)
addColor(Color.GREEN)
addColor(Color.YELLOW)
addColor(Color.ORANGE)
addColor(Color.MAGENTA)
addColor(Color.CYAN)
addColor(Color.PINK)


toolbar.add(
  customButton
)


customButton.addActionListener(
  new ActionListener {

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

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

      if (
        c != null
      ) {

        currentColor =
          c

        currentTool =
          "PENCIL"

        toolBox.setSelectedItem(
          "PENCIL"
        )

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


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

pencilButton.addActionListener(
  new ActionListener {

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

      currentTool =
        "PENCIL"

      toolBox.setSelectedItem(
        "PENCIL"
      )

      setStatus(
        "PENCIL ACTIVE"
      )
    }
  }
)


eraserButton.addActionListener(
  new ActionListener {

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

      currentTool =
        "ERASER"

      toolBox.setSelectedItem(
        "ERASER"
      )

      setStatus(
        "ERASER ACTIVE"
      )
    }
  }
)


toolBox.addActionListener(
  new ActionListener {

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

      currentTool =
        toolBox
          .getSelectedItem
          .toString

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


sizeBox.addActionListener(
  new ActionListener {

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

      try {

        brushSize =
          sizeBox
            .getSelectedItem
            .toString
            .toFloat

      } catch {

        case _: Exception =>
      }
    }
  }
)


// ============================================================
// SELECT THING
// ============================================================

thingList.addMouseListener(
  new MouseAdapter {

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

      val value =
        thingList.getSelectedValue

      if (
        value != null
      ) {

        writeField.setText(
          value
        )

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


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

drawButton.addActionListener(
  new ActionListener {

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

      drawFromText(
        writeField.getText
      )
    }
  }
)


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(
          things.length
        )

      val value =
        things(index)

      writeField.setText(
        value
      )

      thingList.setSelectedValue(
        value,
        true
      )

      drawFromText(
        value
      )
    }
  }
)


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

undoButton.addActionListener(
  new ActionListener {

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

      if (
        undoImage != null
      ) {

        redoImage =
          copyImage(
            image
          )

        restore(
          undoImage
        )

        undoImage =
          null

        setStatus(
          "UNDO COMPLETE"
        )
      }
    }
  }
)


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

redoButton.addActionListener(
  new ActionListener {

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

      if (
        redoImage != null
      ) {

        undoImage =
          copyImage(
            image
          )

        restore(
          redoImage
        )

        redoImage =
          null

        setStatus(
          "REDO COMPLETE"
        )
      }
    }
  }
)


// ============================================================
// 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 = {

      saveImage(
        image,
        frame
      )
    }
  }
)


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

gridButton.addActionListener(
  new ActionListener {

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

      gridOn =
        !gridOn

      refresh()

      if (
        gridOn
      ) {

        setStatus(
          "GRID ON"
        )

      } else {

        setStatus(
          "GRID OFF"
        )
      }
    }
  }
)


// ============================================================
// ZOOM +
// ============================================================

zoomInButton.addActionListener(
  new ActionListener {

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

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

      refresh()

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


// ============================================================
// ZOOM -
// ============================================================

zoomOutButton.addActionListener(
  new ActionListener {

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

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

      refresh()

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


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

resetButton.addActionListener(
  new ActionListener {

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

      zoom =
        1.0

      gridOn =
        false

      themeName =
        "SKY"

      styleName =
        "NORMAL"

      refresh()

      setStatus(
        "RESET COMPLETE"
      )
    }
  }
)


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

canvas.addMouseListener(
  new MouseAdapter {

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

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

        drawing =
          false

        return
      }

      rememberUndo()

      drawing =
        true

      startX =
        toImageX(
          e.getX
        )

      startY =
        toImageY(
          e.getY
        )

      lastX =
        startX

      lastY =
        startY

      if (
        currentTool ==
        "PENCIL"
      ) {

        imageGraphics.setColor(
          currentColor
        )

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

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

      if (
        currentTool ==
        "ERASER"
      ) {

        imageGraphics.setColor(
          Color.WHITE
        )

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

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

      if (
        currentTool ==
        "SPRAY"
      ) {

        spray(
          startX,
          startY
        )
      }

      refresh()
    }


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

      if (
        !drawing
      ) {

        return
      }

      val ex =
        toImageX(
          e.getX
        )

      val ey =
        toImageY(
          e.getY
        )

      if (
        currentTool ==
        "LINE"
      ) {

        imageGraphics.setColor(
          currentColor
        )

        imageGraphics.setStroke(
          new BasicStroke(
            brushSize
          )
        )

        imageGraphics.drawLine(
          startX,
          startY,
          ex,
          ey
        )
      }

      if (
        currentTool ==
        "RECTANGLE"
      ) {

        imageGraphics.setColor(
          currentColor
        )

        imageGraphics.setStroke(
          new BasicStroke(
            brushSize
          )
        )

        val x =
          math.min(
            startX,
            ex
          )

        val y =
          math.min(
            startY,
            ey
          )

        val ww =
          math.abs(
            ex - startX
          )

        val hh =
          math.abs(
            ey - startY
          )

        imageGraphics.drawRect(
          x,
          y,
          ww,
          hh
        )
      }

      if (
        currentTool ==
        "ELLIPSE"
      ) {

        imageGraphics.setColor(
          currentColor
        )

        imageGraphics.setStroke(
          new BasicStroke(
            brushSize
          )
        )

        val x =
          math.min(
            startX,
            ex
          )

        val y =
          math.min(
            startY,
            ey
          )

        val ww =
          math.abs(
            ex - startX
          )

        val hh =
          math.abs(
            ey - startY
          )

        imageGraphics.drawOval(
          x,
          y,
          ww,
          hh
        )
      }

      drawing =
        false

      refresh()
    }
  }
)


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

canvas.addMouseMotionListener(
  new MouseMotionAdapter {

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

      if (
        !drawing
      ) {

        return
      }

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

        return
      }

      val x =
        toImageX(
          e.getX
        )

      val y =
        toImageY(
          e.getY
        )

      if (
        currentTool ==
        "PENCIL"
      ) {

        imageGraphics.setColor(
          currentColor
        )

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

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

      if (
        currentTool ==
        "ERASER"
      ) {

        imageGraphics.setColor(
          Color.WHITE
        )

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

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

      if (
        currentTool ==
        "SPRAY"
      ) {

        spray(
          x,
          y
        )
      }

      lastX =
        x

      lastY =
        y

      refresh()
    }
  }
)


// ============================================================
// COLORIZE
// ============================================================

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


def backgroundColor(
  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("NIGHT") >= 0
  ) {

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

  } else if (
    t.indexOf("SPACE") >= 0 ||
    t.indexOf("GALAXY") >= 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(80,215,255),
      new Color(10,80,180)
    )

  } 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("GOLD") >= 0
  ) {

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

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

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

  } else {

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


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

  val colors =
    backgroundColor(
      theme
    )

  val result =
    new BufferedImage(
      W,
      H,
      BufferedImage.TYPE_INT_RGB
    )

  val g =
    result.createGraphics()

  g.setPaint(
    new java.awt.GradientPaint(
      0,
      0,
      colors(0),
      0,
      H,
      colors(1)
    )
  )

  g.fillRect(
    0,
    0,
    W,
    H
  )

  var x =
    0

  while (
    x < W
  ) {

    var y =
      0

    while (
      y < H
    ) {

      val rgb =
        image.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 ==
    "NEON"
  ) {

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

    g.setStroke(
      new BasicStroke(
        10
      )
    )

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

  if (
    style ==
    "GOLD"
  ) {

    g.setColor(
      new Color(
        255,
        215,
        70,
        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
    )
  }

  g.dispose()

  result
}


// ============================================================
// COLORIZE DIALOG
// ============================================================

colorizeButton.addActionListener(
  new ActionListener {

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

      val bg =
        new JComboBox[String](
          backgroundNames
        )

      val st =
        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(
        st
      )

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

      if (
        answer ==
        JOptionPane.OK_OPTION
      ) {

        rememberUndo()

        themeName =
          bg.getSelectedItem
            .toString

        styleName =
          st.getSelectedItem
            .toString

        val result =
          makeColorized(
            themeName,
            styleName
          )

        restore(
          result
        )

        setStatus(
          "COLORIZE COMPLETE"
        )
      }
    }
  }
)


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

threeDButton.addActionListener(
  new ActionListener {

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

      val result =
        makeColorized(
          themeName,
          "3D"
        )

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

      win.setDefaultCloseOperation(
        WindowConstants.DISPOSE_ON_CLOSE
      )

      win.setSize(
        1050,
        720
      )

      win.setLocationRelativeTo(
        frame
      )

      val panel =
        new JPanel {

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

            super.paintComponent(
              graphics
            )

            val g =
              graphics.asInstanceOf[
                Graphics2D
              ]

            val aw =
              getWidth - 30

            val ah =
              getHeight - 30

            val scale =
              math.min(
                aw.toDouble /
                  result.getWidth,
                ah.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.addActionListener(
        new ActionListener {

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

            saveImage(
              result,
              win
            )
          }
        }
      )

      win.setLayout(
        new BorderLayout()
      )

      win.add(
        panel,
        BorderLayout.CENTER
      )

      win.add(
        save3D,
        BorderLayout.SOUTH
      )

      win.setVisible(
        true
      )
    }
  }
)


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

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

bottom.add(
  status
)

bottom.add(
  new JLabel(
    "1000+ THINGS | Select ? Write ? DRAW IT | F2 = Colorize"
  )
)


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

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

rightPanel.add(
  new JLabel(
    "1000+ THINGS - SCROLL"
  ),
  BorderLayout.NORTH
)

rightPanel.add(
  scrollPane,
  BorderLayout.CENTER
)


// ============================================================
// FINAL LAYOUT
// ============================================================

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

frame.add(
  toolbar,
  BorderLayout.NORTH
)

frame.add(
  canvas,
  BorderLayout.CENTER
)

frame.add(
  rightPanel,
  BorderLayout.EAST
)

frame.add(
  bottom,
  BorderLayout.SOUTH
)


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

SwingUtilities.invokeLater(
  new Runnable {

    override def run(): Unit = {

      frame.setVisible(
        true
      )

      setStatus(
        "READY - 1000+ THINGS LOADED!"
      )

      canvas.requestFocusInWindow()
    }
  }
)