Thursday, June 23, 2022

Distinguishing features of Clojure

By now it should be readily apparent that Clojure is not like most programming languages. In this post I will attempt to pin down what I think makes Clojure so different, focusing more on the philosophical aspects rather then the implementation details. Clojure is distinguished from most other languages from how tightly it is integrated into the JVM, and by its excellent Java interop - but this is an implementation detail and not the philosophical core of the language.

Clojure is sometimes called a functional language, but it is not too particular about it. Functional programming is just one paradigm under the larger multi-paradigm umbrella. The emphasis on functional programming in particular gets to something deeper about the language, which is its emphasis on concurrency. In Clojure you can have side effects, but they ought to be concurrent.

Code as data

Clojure is a Lisp which means it treats code as data. In the customary manner of Lisp dialects, code is represented by collection data structures. Consequently, like most Lisp dialects the language is optimized around the manipulation of collection data structures like lists and sets. This combination of an emphasis on collection processing and code as data enables the Clojure macro system.

It should be mentioned that code as data is a philosophy rather then a specification. Like so many things it is a work in progress. Clojure did well to extend the simple S-expressions of earlier Lisp dialects with new syntax to create the extensible data notation. As a work in progress, you could further extend the extensible data notation, to add improvements to it. Future language designers may then improve it further.

Separating identity and values

The second most important aspect of Clojure is its emphasis on concurrency and immutability. Clojure has a state model that separates values from identities. Values are immutable but identities are associated with a series of values over time. Clojure doesn't prevent side effects, but it does say that you ought to log them to model changes over time. This enables STM concurrency.

Most of Clojure's constructs: its persistent data structures, its very strong emphasis on immutability and explicit modeling of state, are explained by the overarching goal of supporting concurrency. Immutability aids concurrency because immutable values can be safely shared between threads. Clojure doesn't eschew side effects, but it does emphasize that they ought to be concurrent.

Clojure certainly wouldn't appear in the shape it does now if it was created for any other purpose then its support for concurrency. If for example Clojure was designed with functional programming as an end in itself, it might have been a purely functional language. Instead, it was designed with concurrency in mind, so it doesn't appear like that. Functional programming is a means to an end. It is the currently acceptable means of implementing Clojure's goals of concurrency and explicit progression of time constructs.

Clojure's role in history is that it is a concurrent homoiconic language. There is nothing else in existence like it. It represents a truly unique programming philosophy. I think any other property of the language could be changed if it respected this core philosophy. The properties of being dynamic, functional, hosted, etc are more like implementation details then distinguishing characteristics of the language.

After all, Clojure is nothing like almost any dynamic language you have heard of. Dynamic languages almost never model state like Clojure does. Besides, Clojure developers make frequent use of type hints so that it can often appear like a statically typed language anyways. The typing discipline, the use of type hints, etc is just an implementation detail like any other. Hosting on the JVM is the logical thing to do when implementing a programming language.

These are all implementation details. Even the use of functional programming is mainly because it is more convenient when dealing with state management and concurrency, but as always functional programming must be part of a larger multi-paradigm umbrella. So functional programming is just another implementation detail. The main point is the code is data philosophy and the explicit modeling of time.

Wednesday, June 22, 2022

State management and user interface design

In this post, I will address Clojure's management of state. The importance of persistent data structures, state models, temporal databases, etc will be considered in both the fields of human-computer interaction and concurrency.

Clojure's handling of state

Clojure handles state differently from most other programming languages. Clojure divides logical entities into two types, values and identities.
  • Values: immutable data
  • Identity: an entity associated with a series of values over time
Values do not have state and are therefore immutable. Identities have states, which are their values at certain points in time.

Principles of user interface design

The first principle of user interface design is to always respect a user's decisions. After the user does something, you should never open a modal dialog box saying "Are you sure?." Modal dialogs always take away attention from the user. At the same time, a user may actually change his mind or make a mistake, and need to go back.

The means of supporting this is universal undo. Every major graphical user interface application has some sort of undo functionality. Take web browsers as an example. The key feature of HTML is its support for hyperlinks, which let you browse from one web page to another. Browser's enable the handling of a user's browsing history with forwards and back buttons.

Another example is text editors. They always support undo/redo functionality so that as a user types he/she always has the option of going back to a previous time in his/her typing history. Paint programs, video editors, or basically anything else that supports changing an entity over time supports undo. Undo is already vital to a wide variety of graphical user interface applications.

The key to supporting undo is the temporal management of the state of an application over time. In order to support universal undo we need to move away from the naive view of state, and instead view it a series of values over time. A user may change his mind at any time, which necessitates that we need move the value of our identity back to what it was at an earlier point in time. Furthermore, state managament like this should always be the default in all kinds of graphical user interface applications.

Temporal databases

The implementation of changes in values over time can be provided by temporal databases. A temporal database is like an ordinary database, except it also records changes over time. This is like what we need for graphical user interface applications, as it can be used to model the change's in an application's state over a time provided by the user. These temporal databases will then be the means by which we implement undo.

Interactivity in general

We see that the reason we need to explicitly model state in human-computer interaction is that it involves two different entities interacting with one another. When a human and a computer come together, it necessitates the management of identity so that a common understanding of the state of a program can be reached. This is actually analogous to the situation with concurrency, which involves different processors interacting with one another.
  • Concurrency: the interaction of different processors with one another
  • User interface design: the interaction of humans and computers
The issues in concurrency are somewhat similar: we need to reach agreement about the changes in shared state over time. Clojure's state management mechanisms like software transactional memory will make this easier for you. Concurrency is much easier if you explicity manage identity and state, just like user interface design. The only time when the naive model of state is sufficient is when creating single core programs that don't interact with the user at all.

External links:
Values and Change: Clojure’s approach to Identity and State

Swing HTML gallery

Swing and HTML are alike in a number of ways. Both are used to create graphical programs. Swing is based upon the Java beans component model, whilst HTML is based upon standard markup. Java beans encapsulate Java objects that are like nodes in an XML document. This is formalized by the XMLEncoder class of Java SE. Java beans properties are like HTML attributes. Swing's pluggable look and feel is like CSS.

The two frameworks differ in their original purpose, however. HTML emerged to extend text documents with hyperlinks, while Swing from the start was created as a full fledged graphical application framework. HTML's strength is in its hyperlinks and in creating rich text documents, so it has its own role to play. With the Swing HTML technology of Java SE you can get the strengths and benefits of HTML in your Swing applications.

To add HTML to Swing applications simply create a JEditorPane and set it to uneditable. Then set its editor kit to a HTMLEditorKit from the Swing HTML package. Editor panes are mainly used to view HTML rather then interact with it. There are two exceptions though, as you can use Swing to listen for hyperlink events and form submit events. Otherwise, your interactive components should not be part of an editor pane. Ten simple examples will be provided to demonstrate the cool things you can do with HTML in Swing.

1. An unordered list

(def unordered-list
  [:html
   [:body
    [:ul
     [:li "This is " [:b "bold"] " text"]
     [:li "This is " [:i "italic"] " text"]
     [:li "This is " [:u "underlined"] " text"]
     [:li "This is " [:strike "striked"] " text"]
     [:li "This is a " [:sub "subscript"]]
     [:li "This is a " [:sup "superscript"]]
     [:li
      [:font {:size 9} "This is "]
      [:font {:color "#00FF00", :size 9} "green text"]]
     [:li
      [:font {:size 6} "This is "]
      [:font {:color "#FF0000", :size 6} "red text"]]
     [:li
      [:font {:size 3} "This is "]
      [:font {:color "#0000FF", :size 3} "blue text"]]]]])

An ordered list

(def ordered-list
  [:html
   [:body
    [:h2 "Java history"]
    [:ol
     [:li
      "Java 1.0"
      [:ul
       [:li "AWT"]
       [:li "Base"]]]
     [:li "Java 1.1"
      [:ul
       [:li "Java beans"]
       [:li "SQL"]
       [:li "RMI"]]]
     [:li "Java 1.2"
      [:ul
       [:li "Swing"]
       [:li "CORBA"]]]
     [:li "Java 1.3"
      [:ul
       [:li "Java sound"]
       [:li "JDNI"]]]]]])

A description list

(def description-list
  [:html
   [:body
    [:dl
     [:dt "AWT"]
     [:dd "The original Java GUI toolkit."]
     [:dt "Swing"]
     [:dd "A GUI framework with its own pluggable look and feel."]
     [:dt "JavaFX"]
     [:dd "A modern replacement for Swing."]]]])

Java

(def java-image
   [:html
    [:body
     [:img {:src "https://upload.wikimedia.org/wikipedia/en/thumb/3/30/Java_programming_language_logo.svg/262px-Java_programming_language_logo.svg.png"}]]])

Clojure

(def clojure-image
  [:html
   [:body
    [:img {:src "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Clojure_logo.svg/240px-Clojure_logo.svg.png"}]]])

Code

(def hello-world
  [:html
   [:body
    [:h1 "JVM programming"]
    [:h2 "Java programming"]
    [:pre
 "class HelloWorld {
   public static void main(String[] args) {
     System.out.println(\"Hello world\");
   }
 }"]
    [:hr]
    [:h2 "Clojure programming"]
    [:pre
 "(prn \"Hello world\")"]]])

Blockquotes

(def quote-example
  [:html
   [:body
    [:h2 "Famous programming quotes"]
    "Alan Kay speaking on Lisp"
    [:blockquote "Lisp isn't a language, it's a building material"]
    "Rich Hickey on persistent data structures"
    [:blockquote "If you write a program that uses persistent data structures, you'll be able to sleep at night. You're gonna be happier. Your life is gonna be better."]]])

Swing HTML comparison

(def swing-html-comparison
  [:html
   [:body
    [:table
     [:tr
      [:td]
      [:th "Swing"]
      [:th "HTML"]]
     [:tr
      [:th "Component model"]
      [:td "Java beans"]
      [:td "Markup"]]
     [:tr
      [:th "Styling mechanism"]
      [:td "PLAF"]
      [:td "CSS"]]]]])

Buttons

(def html-buttons
  [:html
   [:body
    [:h1 "Buttons"]
    [:h2 "Ordinary buttons"]
    [:input {:type "submit" :value "Button"} ]
    [:br]
    [:br]
    [:h2 "Toggle buttons"]
    "Checkbox" [:input {:type "checkbox"}]
    [:br]
    [:br]
    [:h3 "GUI toolkit"]
    [:form
     "AWT"
     [:input {:type "radio"}]
     "Swing"
     [:input {:type "radio"}]]

    [:h3 "JSON library"]
    [:form
     "Jackson"
     [:input {:type "radio"}]
     "GSON"
     [:input {:type "radio"}]]]])

Forms

(def forms-example
  [:html
   [:body
    [:table
     [:tr
      [:td "Name: "]
      [:td
       [:input {:type "text"}]]]
     [:tr
      [:td "Password: "]
      [:td [:input {:type "password"}]]]
     [:tr
      [:td "Send to:"]
      [:td
       [:select
        [:option "Oracle"]
        [:option "Microsoft"]]]]]
    [:br ]
    "Message:"
    [:br ]
    [:textarea {:rows "6" :cols "25"}]]])
External links:
Swing HTML package

Saturday, June 18, 2022

A circle is the limit of polygons

As the number of sides in a polygon tends to infinity, that polygon gets closer and closer to a circle. Here is how you can demonstrate this geometric reality by video using Clojure:
(ns video.core
  (:import (java.io File)
           (org.jcodec.api.awt AWTSequenceEncoder)
           (java.awt.image BufferedImage)
           (java.awt Color Polygon Rectangle RenderingHints)))

(defn create-polygon
  [vertices offset rect]

  (let [step (/ (* 2 Math/PI)
                vertices)
        x (int-array vertices)
        y (int-array vertices)
        xradius (/ (.-width rect) 2)
        yradius (/ (.-height rect) 2)]
    (dotimes [i vertices]
      (let [current-x (+ (.-x rect) xradius (int (* (Math/cos (+ offset (* i step))) xradius)))
            current-y (+ (.-y rect) yradius (int (* (Math/sin (+ offset (* i step))) yradius)))]
        (aset x i current-x)
        (aset y i current-y)))
    (Polygon. x y vertices)))

(defn main
  []

  (let [file (File.
               (str
                 (System/getProperty "user.home")
                 "/Media/out.mp4"))
        encoder (AWTSequenceEncoder/createSequenceEncoder file 5)
        img (BufferedImage. 600 600 BufferedImage/TYPE_INT_RGB)
        g (.createGraphics img)]
    (.setRenderingHint g RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON)

    (dotimes [i 80]
      (.setColor g Color/WHITE)
      (.fillRect g 0 0 600 600)

      (.setColor g Color/BLACK)
      (let [polygon (create-polygon (+ i 3) 0 (Rectangle. 2 2 596 596))]
        (doto g
          (.draw polygon)))

      (.encodeImage encoder img))

    (.finish encoder)))
Here is the resulting video, which visually demonstrates this tendency:

Friday, June 17, 2022

Creating videos with Clojure

After looking through the Java SE API we can safely say that it doesn't contain any support for creating videos. To do that we need an external library like JCodec. It has APIs for the two main Java client platforms: Java SE and Android. In order to demonstrate the building of videos in Clojure, I will be using the JCodec Java SE API, which allows us to make use of our existing Java 2D functions to easily create videos. By itself, the Java SE doesn't let us do that much so to do interesting things like creating videos we need to look to external APIs. JCodec is one such API that is readily available.
(ns video.core
  (:import (java.io File)
           (org.jcodec.api.awt AWTSequenceEncoder)
           (java.awt.image BufferedImage)
           (java.awt RenderingHints Color)))

(defn main
  []

  (let [file (File.
               (str
                 (System/getProperty "user.home")
                 "/Media/out.mp4"))
        encoder (AWTSequenceEncoder/createSequenceEncoder file 30)
        img (BufferedImage. 600 600 BufferedImage/TYPE_INT_RGB)
        g (.createGraphics img)]
    (.setRenderingHint g RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON)

    (dotimes [i 300]
      (.setColor g Color/WHITE)
      (.fillRect g 0 0 600 600)

      (.setColor g Color/BLACK)
      (let [center 300
            size (* i 2)]
        (.drawOval g (- center (/ size 2)) (- center (/ size 2)) size size))

      (.encodeImage encoder img))

    (.finish encoder)))
This produces the following video:

Thursday, June 16, 2022

Java SE and Android comparison

The Java SE and Android APIs address different hardware concerns and so they need to be different from one another. Even so there is a lot of overlap in the base and XML APIs, just as there is quite a lot that is different between them. The diagram presented below can be used as a quick guide if you want to compare the two of them. There is a lot of overlap between Swing and the Android user interface libraries, but they are presented in a different form on Android which is more appropriate for mobile devices. Much like Swing, Android has support for graphics, accessibility, input methods, widgets, media, etc. Emphasized instead is the special hardware support of the Android API like the telephony API which certainly don't have counterparts in Java SE. In order to discover more of the technical details, consult the API documentation.

External links
Android API

Wednesday, June 15, 2022

Java mobile applications

Computer programs are determined by the hardware they are built with. If you are building a rich client application with the JVM then you are probably targeting two types of hardware (1) desktop hardware and (2) mobile and embedded hardware. Different types of hardware necessitate different types of software, and so lets explore these distinctions in more detail.

Size matters

When computers were first created they were the size of an entire room. There was a large period of computing history when there were no personal computers because they were too cumbersome, large, and didn't have any graphical user interface capabilities. A long standing trend in the history of computing has been that they have been getting smaller so they take up less space.

A phase transition in this continuous process occurred in the past decade and a half or so, when computers became so small they could fit in your hand. They even became smaller then your keyboards. This radical change in hardware necessitated a different user interface paradigm based upon technologies like touch and speech.

This produced the most significant change in human-computer interaction technology in the last decade and a half. Now graphical user interface applications (GUIs) need to made to adapt to either desktop devices or mobile devices. A lot is shared in common between the two kinds of devices, but other things like differences in input devices pose a challenge for graphical user interface designers.

Every single GUI application has to ask itself it is targeting a mobile device or a desktop one. These hardware differences change the APIs that we have to look in to. Fortunately, as JVM language developers we have a pick of APIs including Swing for desktop applications and Android for mobile applications. With these, we can build graphical user interface applications for any device. Beyond these two, there are a couple of specialized client application frameworks with more advanced graphical capabilities.

Mobile devices

Mobile devices have specialized hardware which distinguishes them from desktop devices. Firstly, in mobile devices energy usage and heat dissipitation are significant factors. As mobile devices are not typically hooked up to a power supply, every piece of energy used by them is significant. At the same time, since they are held in our hands if they heat up too much that will effect the user. This is one reason why mobile processors tend to use ARM.

Secondly, mobile devices have different input and output devices configurations. Mobile devices lack the keyboards and mouses of standard hardware devices, both of them being replaced by touch screens. Further, the screens of phones and other mobile devices are too small to fit many standard applications.

Java SE provides means of handling printers, monitors, keyboards, mouses, processors, storage devices, etc in APIs distributed through a number of different packages, but it doesn't provide us with the means of handling mobile devices like phones. These distinction hardware configurations are handled by the Android APIs, which contain a number of Java SE packages but not Swing which is specifically for desktop applications.

Virtual mobile devices


You certainly wouldn't want to program on a mobile device when it doesn't have a keyboard. This produces a distinction between the devices you program on and the devices your programs run on. Fortunately, Android Studio provides the means to solve this by allowing you to create virtual mobile devices. That way you can run your Android programs right from your desktop.

To begin with, download Android Studio. Then open the device manager and select create device. As all Android applications are dependent upon the hardware they run on, you next select the hardware you want to emulate. Then select a system image for the Android API and click next change the virtual device as you needed and finish. With that you can get around the hardware differences between desktop and mobile applications and get to developing.

Android API

As mobile devices are a different sort of hardware then desktop devices they require their own API, but this by no means implies that there is no overlap between Java SE and Android. Most of the classes in the base, logging, preferences and SQL modules are available for use in Android applications.

As Android is based upon a different interaction paradigm then Swing, the desktop module is noticeably absent. Swing is suitable for the sort of hardware devices in desktop computers, while Android is more suitable for the sort of hardware configurations in mobile devices. Both serve their own purposes by targeting different types of hardware. Swing still has its use cases and a set of hardware that it targets.

A Java virtual machine for every device

The JVM is the best developer platform in the world. In order for it to maintain its status as the best developer platform into the future, it must have APIs for all kinds of hardware devices. With the different Java APIs like Android and Java SE that is now possible. Each different type of hardware has its own API.

Separately, Java ME provides support for embedded and mobile devices programmed with Java. It should be noted that Java ME is not the same thing as Android. Java ME is the historical API developed by Sun microsystems, while the Android API is used on most modern mobile devices. Both of them use a subset of Java SE.

External links:
Android Studio

Android API

Monday, June 13, 2022

Java SE overview

The JVM is the great platform on which our Clojure programs are run. In order to have a thorough familarity with Clojure, it is necessary to also have expertise with the Java platform. The Java virtual machine (JVM) and the Java programming language are quite simple, so expertise with Java comes from understanding the Java libraries. In order to make sense of the vast Java SE API, Oracle distinguishes between three classes of libraries in its documentation:
  • Base libraries
  • Integration libraries
  • Desktop libraries
The tripartite division of Java SE is then typically expanded in a conceptual diagram, updated below to deal with the modularisation of the JDK. This conceptual diagram is our first overview of Java SE.

Swing Java 2D AWT
Image I/O Sound Print service
JDBC RMI JDNI Scripting
XML HTTP XML Cryptography Security
JMX Instrumentation Compiler Networking
Preferences Logging I/O Compression
Lang Util Reflection Concurrency

In order to get an overview of Java SE and its vast functionality, we will consider a number of conceptual approaches to organizing knowledge about its API. In doing so, we can make better sense of the functionality made available to JVM languages.

Hardware overview

Computer science is about transforming reality. The objects of transformation are hardware devices. Hardware is the foundation of computer science, so that is a good place to start. The Java SE API contains means of interacting with a wide variety of hardware devices: CPUs, hard disks, networking devices, sound cards, speakers, monitors, MIDI devices, keyboards, mouses, printers, etc. In short, it contains the means of interacting with all standard hardware devices.

Processors

To begin with a program needs to interact with its processor. Historically, each program was written in the native assembly language of the computer it run on and compilers weren't that good. Of course, such programs weren't portable because CPUs have minor technical differences which prevent you from porting a program from one machine architecture to another.

At the same time, processors were not designed with developers in mind. Most processors have a low level architecture which is not suitably friendly to developers. Processors are low level devices with minor technical differences to one another. The Java virtual machine (JVM) solves this problem by introducing a universal high level abstraction layer over the CPU.

This wouldn't have been possible with the technology of the past. The JVM relies on the most advanced and modern advancements in compiler technology for its implementation. The JVM has the most advanced optimisations and a compiler architecture. The JVM is now the most perfectly engineered software in the world, with countless hours put into perfecting every line of its code.

Storage devices:

The distinction between RAM and hard drives in most modern computing devices means that we need special mechanisms for dealing with storage devices. In Java this is handled by the I/O classes, which can actually be used to handle a wide variety of different types of external hardware. Towards that end, Java I/O defines input stream and output stream classes.

The input stream and output stream define the sequential access to bytes. These general purpose classes can be used to handle interaction with all kinds of external devices. In addition to this, Java provides the Serializable mechanism which helps you handle storage devices by taking objects in RAM and converting them to a form they can be stored in.

Storage devices are typically organized by file systems. In order to interact with them, you need to be able to interact with the file system. The Java IO package provides the File class in order to handle such files in the file system. Given a File you can form a FileInputStream or a FileOutputStream to read or write from it. With this it becomes rather easy to transfer memory stored in RAM to a storage device.

Networking devices

The Java platform was designed from the start to handle networking applications. This support is embodied by the classes of the java.net package. With this builtin networking API you can communicate via TCP/IP sockets or UDP sockets over the internet. UDP is distinguished by the fact that it is connectionless, which means that you have no guarantee that the data will alive. A connectionless protocol is going for example for live feeds.

The Java networking API handles communication between machines with sockets. A socket is an endpoint for communication between two machines. Sockets are handled by using the same I/O routine streams used on local storage devices, which can be acquired by the getInputStream and getOutputStream methods. These methods let you communicate streams of bytes over a network.

The Java SE API also includes support for distributed computing, as provided by the RMI (remote method invocation) module. The RMI module builds on the Java networking API and sockets in order to allow for you to communicate with external machines. Separately, the Java SE API provides support for HTTP, the hypertext transfer protocol. Finally, Java enables distributed computing with the JDNI API for handling naming and directory services.

Sound devices

The Java SE API contains separate packages for sampled audio and MIDI devices. The sampled audio API is centered on the AudioSystem class. Using this you can get an audio input stream for a sound file (WAV, AIFF, or AU) and a Clip that can play it. By associating a Clip with an audio input stream, you can play that audio either once or on repeat.

On the other hand, Java SE provides support for MIDI (musical instrument device interface). You can use this package to generate the sounds of a piano for example. It is generally easier to generate music with MIDI then using sampled audio. Interaction with MIDI goes through the MidiSystem class. Combined with the sampled audio package, this provides everything you need to write code for audio devices.

Printers

The Java SE API includes a number of packages specifically for handling printing devices. The first, java.awt.print was created with the Java 2D API. It lets you handle printing devices by using the Java 2D API. The Printable interface provides a print method, which takes in a Graphics object, this way you can handle the printer using the graphics API.

In addition, this the Java Print service is defined by a number of packages including javax.print. This API includes an extensible print attribute set based upon the internet printing protocol. This allows you to discover and select printers in the network based upon their capabilities. In addition, the Java Print service event API lets you register for events in the process of running a print job.

Graphical devices

The Java SE API comes equipped with a full set of 2D graphics programming functionality: it contains geometry, compositing, painting and stroking, fonts, colors, text layouts, clipping, rendering hints, transformations, image processing, and so on. These functionalities can be accessed by using the Graphics2D class. Further, the GraphicsDevice class lets you directly model one or model graphics devices in your environment.

One of the more advanced uses of graphics in Java SE is its support for graphical user interfaces (GUIs), which is defined by Swing. Using the Java 2D API and Swing you can define your own JComponents that have their own custom graphics, which enables you to create rich graphical applications using Java SE. In addition, Swing comes with a wide variety of premade widgets. All this is to enable you to create rich graphics applications using Java SE.

Modules overview

As Java SE evolved over time it got so vast, that it certainly required modularisation to make its vast API more handlable. The modularisation of the JDK was achieved with Java SE 9. With this we have an alternative perspective on the structure of Java SE API. The APIs of Java SE are grouped into a partially ordered set of modules: This produces an alternative overall perspective on the Java SE API then the earlier conceptual diagrams. The following observations can be made:
  • The desktop module depends upon XML. The reason for this is the Java beans package of the desktop module which includes the XMLEncoder and XMLDecoder classes. These classes let you encode Swing applications as XML trees. It also depends upon data transfer, which was originally part of the AWT and considered a desktop library but it was moved to its own module so other modules could use it without bringing in the entire desktop module.
  • The XML cryptography module depends upon the XML module. Basically, XML cryptography is a direct extension of the functionality provided by the XML module, but its moved to its own module because the XML module is already big enough as it is.
  • The management RMI module depends upon both management and RMI. It is a direct combination of Java's management and RMI facilities.
  • SQL depends upon XML, logging, and the Java transaction API. The SQL rowset API also depends upon naming. The SQL APIs depend upon on a lot of other things.
  • Everything depends upon the base module, which contains the lang package and the indispensible APIs of the Java platform.
Aside from these basic observations, the Java SE module graph is basically flat. Most modules only depend upon the base module, and the other cases we have explained above. While it takes more to understand the Java SE API, this module graph is another way to get a good perspective on it.

Classes overview

While modules provide a high level overview of the Java SE platform, classes provide a much more direct overview. We will therefore consider Java SE from a class based perspective.

Class based programming

The central concept of the Java platform is the idea of a class. Everything in Java is a class. An interface, for example, is simply a class with a flag set to declare that it is meant to be treated as an interface. All classes are encoded in the class file format of the Java virtual machine. So Java SE can be considered to be a vast set of classes.

Classes provide the best means of modeling concepts and creating programs. In particular, classes provide the best means of interacting with hardware devices. Given some hardware device, you can always create a class to represent it like the GraphicsDevice of the Java SE API. The class based approach is key to the success of the Java platform.

Classes in the Java SE API

With this perspective in mind, the Java SE API can be considered to be a vast array of thousands of different classes dealing with all kinds of different types of programming purposes and modeling a wide array of concepts from server sockets to currencies. The Java SE API has a class for everything.

The classes in Java SE form all together vast partial order based upon implementation and inheritance. Classes provide single inheritance while interfaces define multiple inheritances. So if you include interfaces, then the class system is a general partial order and not a tree. As Java SE contains thousands of classes, this contains many more relations then the modules.

Conceptual overview

The conceptual overview of Java divides the platform into three different basic types of modules: integration libraries, desktop libraries, and base libraries.
  • Base libraries
    • Base
    • Compiler API
    • Instrumentation
    • Management
    • Logging
    • Preferences
    • Networking
      • HTTP
    • Security
      • SASL
      • GSS
    • XML libraries
      • XML
      • XML cryptography
  • Integration libraries
    • Scripting
    • Databases
      • SQL
      • Distributed transactions
      • Row sets
    • Distributed computing
      • RMI
      • Management RMI
      • JDNI
  • User interface libraries
    • Desktop
    • Data transfer
Conceptualy, many modules like SQL and XML have specialized purposes that are not necessary in all applications. With the modularisation of the JDK, you can now specify only the parts of the Java SE API that you need. With all this functionality built in, there is a lot for you that is readily available.

Previously
Java base libraries

Java integration libraries

Java desktop libraries

Sunday, June 12, 2022

Color wheel

The HSB color scheme is useful when you want to provide a variety of colors.
(defn ^BufferedImage make-image
  []

  (let [rval (BufferedImage. 600 600 BufferedImage/TYPE_INT_ARGB)
        g (.createGraphics rval)]

    (.setRenderingHint g RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON)

    (let [n 3600
          full-circle 360
          step (/ full-circle n)
          angles '[0 60 120 180 240 300]]
      (doseq [i (range 0 n)]
       (let [current-angle (* i step)
             current-radian (Math/toRadians current-angle)
             radius 250
             x (+ 300 (* radius (Math/cos current-radian)))
             y (+ 300 (* radius (Math/sin current-radian)))]
         (.setColor g (Color/getHSBColor (* i (/ n)) 1.0 1.0))
         (.drawLine g (+ x 7.5) (+ y 7.5) 300 300))))

    (.setColor g Color/BLACK)
    (.setStroke g (new BasicStroke 2))
    (.drawOval g 56.5 56.5 500 500)

    rval))

This BufferedImage appears as depicted below. This is an interesting picture. If you want to make an image with a wide variety of colours you are going to want to use a gradient or your own custom methods like this that do their own color calculations.

Saturday, June 11, 2022

Nimbus

The Nimbus look and feel is nice and different.
(import javax.swing.UIManager)
(import javax.swing.JFrame)
(import javax.swing.JTextField)
(import javax.swing.JButton)
(import javax.swing.JPanel)
(import javax.swing.JLabel)
(import javax.swing.BoxLayout)
(import java.awt.Component)
(import java.awt.GridBagLayout)

(defn main
  []
  
  (UIManager/setLookAndFeel "javax.swing.plaf.nimbus.NimbusLookAndFeel")
  
  (let [frame (JFrame. "Swing viewer")
        container (JPanel.)
        first-panel (JPanel.)
        second-panel (JPanel.)
        third-panel (JPanel.)] 
    (.setLayout frame (GridBagLayout.))
    (.setLayout container (BoxLayout. container BoxLayout/Y_AXIS))
  
    (.add first-panel (JLabel. "Source"))
    (.add first-panel (JTextField. (int 20)))
    (.setAlignmentX first-panel Component/LEFT_ALIGNMENT)
    (.add container first-panel)
  
    (.add second-panel (JLabel. "Target"))
    (.add second-panel (JTextField. (int 20)))
    (.setAlignmentX second-panel Component/LEFT_ALIGNMENT)
    (.add container second-panel)    

    (.add third-panel (JButton. "Close"))
    (.add third-panel (JButton. "Submit"))
    (.setAlignmentX third-panel Component/LEFT_ALIGNMENT)
    (.add container third-panel)   
    
    (.setSize frame 400 200)
    (.add frame container)
    (.setVisible frame true)))
  
(main)

Look at the result produced after calling the UIManager: The buttons have a different look and feel then the other cases I am used to. If you manually set the look and feel with the UIManager, the only thing is that the resulting application will not look native anymore. The pluggable look and feel is one of the nice features of Swing that wasn't available before in AWT.

Friday, June 10, 2022

AWT and Swing comparison

The AWT toolkit came with the initial release of Java. On the other hand, Swing was only released with Java 1.2. In order to keep backwards compatibility, Swing builds on top of AWT and all the old widgets are maintained. Many of them like components, containers, panels, windows, dialogs, and frames are inherited from by Swing components.

Swing represents a radically different way of doing GUIs then AWT with a model/view architecture, pluggable looks and feels, integrated accessibility, lightweight components, etc. It is not enough to simply change the functionality of existing AWT widgets like Buttons, entirely new widgets had to be built. Typically, they are distinguished from their AWT counterparts by a J prefix.

This means that there are different versions of the same component in Java SE like Button and JButton, the first of which is a part of AWT and the later of which is a part of Swing. In a couple of cases there are subtle differences in spelling like between Checkbox in AWT and JCheckBox in Swing, which uses different capitalisation then its AWT counterpart. The following tables distinguish between AWT and Swing components.

The newer GUI toolkit, JavaFX, doesn't have these distinctions. JavaFX may very well be the future of GUIs. It has an excellent scene graph library and a cleaner API design so it is worth checking out. Of course, Swing is still excellent and holds up quite well to everything today despite its age it just lacks the advanced graphical capabilities of JavaFX.

Components comparison

Elements
There is no corresponding Canvas component in Swing, so if you want that functionality you should look in to creating your own custom JComponents. Everything you could do with Canvas can instead be done this way. AWT Choices are called combo boxes instead in Swing.

AWT Swing
Button JButton
Canvas JComponent
Checkbox JCheckBox
Choice JComboBox
Label JLabel
List JList
Scrollbar JScrollBar
TextComponent JTextComponent
TextArea JTextArea
TextField JTextField


Containers
Every single Swing component is a Container. The JComponent class subclasses the AWT Container class, and all the other Swing components do as well. On the other hand, not every AWT component is a Container, so we describe how those that are can be converted to Swing.

AWT Swing
Panel JPanel
Applet JApplet
ScrollPane JScrollPane
Window JWindow
Dialog JDialog
FileDialog JFileChooser
Frame JFrame


Menus
AWT menus can be readily translated into Swing menus. One technical difference is that AWT menu items are not Components. In Swing every menu component is a JComponent, so this produces a cleaner architecture.
AWT Swing
MenuComponent JComponent
MenuBar JMenuBar
MenuItem JMenuItem
CheckboxMenuItem JCheckBoxMenuItem
Menu JMenu
PopupMenu JPopupMenu


New components

With the AWT and Swing comparison, we can now identify which components introduced by Swing are genuinely new and not part of some older toolkit.
  • slider
  • spinner
  • progress bar
  • radio button
  • color chooser
  • split pane
  • tabbed pane
  • table
  • editor pane
  • tree
  • tooltip
  • toolbar
  • viewport
  • box
  • internal frame
  • layered pane
  • option pane
  • root pane
As you can see by this fairly long list there is quite a lot that is in Swing that has no counterpart in AWT. It is a genuinely new and more modern toolkit then AWT, with probably twice as many widgets. With this many widgets, Swing still holds up pretty well to its counterparts like QT.

New layout managers

As Swing descends from AWT, it still basically uses its layout managers most of the time: GridLayout, CardLayout, GridBagLayout, FlowLayout, and BorderLayout. These suffice for most things, but Swing introduces a number of layout managers which operate alongside their AWT counterparts:
  • Box layout
  • Overlay layout
  • Spring layout
  • Group layout
In terms of the comparison between AWT and Swing, we see that unlike for components these are not a replacement for AWT layout managers they are merely extensions of them. Your applications still going to want to use AWT layout managers, except now you want to use Swing ones as well in some cases.

New events

In Java 1.0 there was only the Event class of the AWT package. This was the original Event class in the Java platform. All event handling was done by subclassing, so that a given class would handle events by overriding the handleEvent method. This naturally wasn't a very extensible approach, as objects couldn't listen to events of other objects. Java 1.1 fixed this by introducing the AWT 1.1 event model which is based upon listeners and the AWTEvent class.

The Swing event system builds upon the AWT 1.1 event model. Swing introduces a number of new subclasses of AWTEvent for its components, and listeners that can be registered for them. At the same time, it also introduces a great many new events that aren't AWTEvents as a consequence of its new model/view approach. These events let you listen to changes in the underlying model rather then in the view. All the technical details are in the javax.swing.event package.

External links:
Java desktop module

Thursday, June 9, 2022

Java base libraries

The Java base module is already the largest module in the JDK, with the Desktop module being the only one that comes close to its scale, so it makes sense to split off some base libraries from the base module so it doesn't get even bigger. There are almost as many base libraries in Java SE outside the base module as in it.

The basic idea is that modules are moved outside of java.base provided that they contain some technical specification or format that isn't strictly necessary to the foundations of Java. XML, for example, is a very specialized format which doesn't need to be in java.base. Similarily for HTTP, which is moved to its own separate module.

Language extensions

The Java SE platform actually provides quite a lot of language capabilities aside from those in java.base. These come in the form of three different APIs:
  • The Java compiler API
  • Java management extensions
  • Instrumentation
The Java compiler API defines a language model for Java and defines its various types and elements as classes with Java itself. It also defines a compiler API so that a compiler can be invoked from within Java itself. In addition, it provides facilities for processing annotations.

The instrumentation module allows you to instrument programs running on the JVM. In particular, it defines the ClassFileTransformer class which lets you transform the bytecodes of Java methods running on the JVM. The ClassFileTransformer can be added to an agent using the addTransformer method of the Instrumentation interface. This is clearly deeply linked to the JVM and its bytecode format in its implementation, but it is not needed in java.base.

Finally, there is a whole host of management extensions provided by JMX. All of these are based upon the concept of managed bean which is defined with the purpose of monitoring some kind of object on the JVM. An MBean is not a Java bean in the sense of the desktop module, but it gets the name from the fact that MBeans have similar properties to beans like their getters and setters.

Utilities

Java SE provides additional utilities in two separate single-package modules:
  • Logging: in the java.util.logging package
  • Preferences: in the java.util.prefs package
Logging and preferences are clearly utilities as demonstrated by their package names. Logging lets you log events as they occur in the course of your program. The preferences package on the other handle is defined by a single class Preferences which lets you store preference data using maps that map Strings to Strings.

Networking extensions

There is a single package in Java SE, contained in its own module, for dealing with HTTP. It builds upon the java.net package in java.base. It is useful in Java applications that need to interact with web pages.

Security

The Java SE platform provides support for two different security protocols defined by the IETF: namely SASL and GSS. These are both comparatively small modules, but it isn't necessary to include them with the Java security API provided by java.base.

XML

The java.xml module is the third largest module in the Java SE platform because it provides such a wide array of functionality like SAX, StaX, JAXP, and the DOM. Another module in Java SE defines XML cryptography.

Previously
Overview of the Java Base module
Overview of the Java XML module

Wednesday, June 8, 2022

The Java XML module

The Java XML module contains APIs for DOM, StaX, and SAX. The later two only really exist to enable you to handle performance issues. SAX and StaX can save you from having to create the entire DOM tree in memory, but the DOM is clearly the more powerful of the group. You may be familiar with the DOM from other programming languages, since its an open standard.

After the modularization of the JDK all the Java XML packages were moved into the XML module, except for the XML cryptography packages which are part of the XML security API. With the modularisation of the JDK, the XML module is now the third largest module after the base module and the desktop module. It is quite vast.

Also included in this is the JAXP. The JAXP (Java API for XML processing) can be seen as an abstraction layer. It does not provide a new means of parsing XML or add to existing APIS like DOM, StaX, or SAX but it makes some things easier to deal with. So it is included in the Java XML module in Java SE.

Parsing an XML document:
First I will create an XML document:
<history>
 <era>
   <event start="100" end="200"></event>
   <event start="400" end="500"></event>
 </era>

 <era>
  <event start="1200" end="1300"></event>
  <event start="1800" end="1900"></event>
 </era>
</history>
Storing this as history.xml then getting the DOM tree from this XML document is as simple as a couple lines of Java code:
var file = new File(System.getProperty("user.home") + "/Documents/history.xml");
DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = dBuilder.parse(file);

At the moment I won't do anything interesting with the DOM tree, but as you can imagine by the sheer scale and scope of the Java XML package there is a lot that you can do including calling transformations with XSLT.

The heavy amount of XML technologies in Java SE is a consequence of the considerable success that XML has recieved in the enterprise. Much like Java, XML plays a central role in the enterprise. Of course, you can also process XML using Clojure which is worth looking in to as well. By considering XML we are one step closing to an overview of Java SE.

External links:
Java XML

Tuesday, June 7, 2022

Overview of the Java base module

When we look at the various modules provided in the Java SE platform, the two largest byfar are the base module and the desktop module. The base module contains a vast array of functionality which that provides a foundation for everything to do with the Java platform. The desktop module by itself functions as a means of dealing with the entire class of user interface libraries provided in Java SE.

As a library with vast functionality, it would be nice if we could establish some kind of classification for it like we did with the Java desktop module. To do this, lets look back at the earliest versions of Java. They started out with four main base libraries: lang, io, net, and util. All other libraries grew out of these.
  • Language features
  • I/O
  • Networking
  • Utilities
The Java desktop module was classified by how it dealt with a variety of different pieces of hardware devices like graphics displays, printers, sound cards, input devices, etc. The base module isn't that different, except the kind of hardware it deals with is more internal and data processing focused. The kinds of hardware dealt with by the base module include hard disks and networking devices.

The Java I/O packages are necessary in order to deal with hard disks. Given some piece of data stored in RAM on a computer, it needs to go through a separate serialisation process before it can be stored on a device. The functionality provided by Java SE deals with this. Networking is similar to I/O except it deals with communication with external computers.

Like with I/O there should be specialized hardware for dealing with networking in a given machine like a network interface controller. The Java SE base module allows you to take advantage of these hardware configurations as well. In those cases that networking is not available, then these functions cannot be used so it might be good to check for networking capabilities before you do anything.

While the networking and I/O packages let you deal with fundamental hardware devices the lang package and its extensions deals with all the core aspects of the language itself. It is in turn an outgrowth of the JVM, and some aspects of it are even integrated into the JVM itself. For example, the JVM has a concept of primitive types. The lang package provides boxed alternatives that are stored as Object references instead of primitives. The JVM also supports loading string constants onto the stack with ldc, and these are objects of the lang package.

The language features of the Java base module are very close to the core concepts of the Java virtual machine and its fundamental architecture. As mentioned earlier, some of its features are indispensible. The utility package can be seen to be anything that is distinguished from the language package simply by what it is not. Utilities are any features not tied to the core of the language.

The utilities provided by the Java base module have certainly undergone significant expansion. They now deal with a wide variety of areas like compression, concurrency, functional interfaces, random number generation, regular expressions, and so on. The Date class of the utility package grew into the many classes of java.time and the StringTokenizer class grew into the java.text package with all its attendant features. Finally, there are many security libraries now which can be considered to be utilities.

Core language features:

The java.lang package is fundamental to the JVM itself. For example, the Object class is the base of the class hierarchy in any Java program and Strings which are basic to the JVM are implemented here for the first time. Further, the Class class is the basis of the entire reflection system. The ClassLoader lets you dynamically load classes at runtime. Runtime deals with the runtime itself. All these things are basic to the Java virtual machine.

In order to support non-trivial reflection it should be the case that the Class class should allow you to get its fields, methods, constructors, etc and any data associated to them. The second language package therefore that we look to is java.lang.reflect. This is the basis of the Java reflection API.

A long standing issue with Java is interaction with the garbage collector. The java.lang.ref package was introduced to deal with that. As the language evolved, so to did the language packages. The java.lang.annotation package was introduced to handle annotations which were introduced in Java 5.

The language packages follow the evolution of the Java language and the virtual machine. The introduction of new invokation routines to the JVM like invokedynamic necessitated the introduction of the java.lang.invoke package with all of its attendant features. Invokedynamic, one of the more complicated features of the JVM, allows for JVM level support for dynamic languages and features. The features in the language packages are most closly tied to the JVM.

I/O

The basic idea of the I/O package was to enable developers to handle reading and writing to byte streams. With the read and write methods of InputStreams and OutputStreams you can read and write data to and from the hard disk. Further, with the Serializable interface Java objects can be converted to and from objects on a storage device. Readers and writers extend the input and output streams to handle I/O on the level of characters instead of bytes.

The limitation fo the I/O package were resolved with the introduction of the new I/O system in the java.nio package. The Java NIO API introduces high-performance networking and file handling to the JVM. It is distinguished by its use of non-blocking I/O operations and a buffer oriented approach. The Files class also provides a number of utilities that make Java I/O a lot easier to handle.

Networking

The Java platform, as the number one developer platform in the world, should be able to handle all sorts of hardware devices. In the case of networking, the Java SE platform was designed from the very start to be able to handle networking. A basic idea has always been that JVM code, even untrusted, could be communicated and run over the network.

So the Java networking package provides all the functionality you need to deal with URLs, UDP sockets, TCP sockets, IP addresses, and more. Extensions also provide support for the secure socket package. So there is certainly a lot to cover here when dealing with the Java networking APIs.

Utilities

Finally we have a wide variety of different utilities in the base module. These span a vast area of different areas extending the basic functionality of the language package. For example, the collections framework is provided by java.util so that you can have all the fundamental types of collections available to you for work with the Java virtual machine.

The introduction of lambdas in Java led to a significant change in the API. We now have functional interfaces that let you work with various programs that use the new language features. In particular, the stream API extends the collections framework so you can handle Java collections functionally. On top of that there are concurrency and compression APIs. All this explains why the Java base module is so vast.

Disclaimer
These icons are part of the Oxygen Icon Set. All rights to these respect icons belong to their respective owners. They are included here for educative purposes.

Monday, June 6, 2022

Java integration libraries

The Java SE API is split between three informal components: base libraries, integration libraries, and desktop libraries. The integration libraries I divide into two components: database programming and distributed programming.

Database programming:

Support for databases was added to Java SE in version 1.1 with the JDBC API. The JDBC API is basically all about letting you run SQL queries to communicate with existing databases. Of course, you first need to setup a database and ensure that Java has the appropriate drivers to communicate it with, but once you have that you are ready to use JDBC.

JDBC
The java.sql package defines the JDBC API for accessing databases in Java programs. It handles all the basic aspects, like getting a connection to a database and executing SQL queries on it. All the databases handled by the builting JDBC library of Java SE are relational. They use the familiar mathematics of relational structures, to get to other types of databases you need to use something beyond the standard library.

Scripting

Java SE provides scripting language functionality as an integration library.

The Java scripting package
All Java scripting functionality is centered around the ScriptEngine interface in the javax.script package. ScriptEngine is implemented by classes that want to implement their own scripting functionality that takes in Strings.

Distributed computing:

Distributed computing is an outgrowth of networking. The Java SE platform lets you run Java methods on external machines using RMI. After the technical details are worked out, objects on external machines can be treated like they are on your own machine. The JDNI also provides you the ability to access information on machines in external locations.

RMI
The RMI (remote method invocation) library is the main basis of distributed computing in Java. It was introduced with the JDBC in the second release of the Java platform version 1.1. You can easily run methods on objects on your own machine, but RMI handles what happens if you want to execute methods on some other computer far away. First of all you have to serialize all your data on both ends, so it can be transferred. Secondly, you need to be able to handle networking issues in the remote ivocation.

JDNI
The JDNI is a naming and directory service for the Java platform. It is defined in the java.naming module module of Java SE. JDNI solves the issue of accessing resources on external machines, which requires that they have some kind of naming service. Directory services extend naming services with the addition of attributes. The JDNI can be used to share information between servers in distributed applications.

External links:
JDBC

RMI

JDNI

Disclaimer
These icons are part of the Oxygen Icon Set. All rights to these respect icons belong to their respective owners. They are included here for educative purposes.

Computer graphics and user interfaces

The key to creating your own graphical user interfaces (GUIs) is apparent in the first letter of the acronym. The key is to create your own graphics. You can go a long way relying on pre built widgets, but then you aren't really creating your own GUI you are just using someone else's pre built graphics libraries as a crutch. By the same token you aren't doing programming your own way unless you create your own compiler.

The same is true for rather you are programming for a desktop application or a mobile device. Consequently, that is another area that can be universalized. In the same way that the JVM universalised computation, it is possible a universal API for graphics could be created (OpenGL presumably). There still will always be some interface differences between desktop and mobile devices, but they still share the same common graphical foundations.

The key to creating computer graphics in turn is a combination of geometry and mathematics on the one side and colour management and painting issues on the other. The mathematics used here is interesting because of its heavy use of geometry. In a sense computer graphics is just applied geometry, which makes nice for prospective geometers.

What would creating your own graphical components look in practice? In Swing this involves subclassing JComponent to make your own components. The key role of Java 2D then emerges here because you can then access it by overriding the paintComponent method to give your own component your own custom graphics. This is why Java 2D is so important to Java desktop applications.

Games are another area where custom graphics programming is essential. There is hardly any game that can be developed without doing a lot of custom graphics. You might be able to get fine making some desktop applications without doing custom graphics, but to me it is still essential. It is the center piece of any client side application really. So there is plently of motivation to look into computer graphics.

Sunday, June 5, 2022

Drawing basic shapes with Clojure

I will conclude the current series of posts about doing graphics programming in Clojure using Java 2D by making a compilation of all the basic shapes, but I expect I will be talking a lot about computer graphics topics a lot from now on.

An image creating macro

I will now be creating a wide variety of images all at once. That would yield too much boilerplate if I didn't first declare a macro.
(defmacro argb-image
  [[width height] & body]

  (let [image-name (gensym "img")
        graphics-name (gensym "graphics")]
    `(let [~image-name (BufferedImage. ~width ~height BufferedImage/TYPE_INT_ARGB)
           ~graphics-name (.createGraphics ~image-name)]
       (doto ~graphics-name
         ~@body)
       ~image-name)))

Most of what we want to do to images can be encapsulated in a single doto block applied to its graphics. This macro encapsulates all that.

Creating the images

I ported the basic regular polygon creation function from the Java we saw previously to Clojure.
(defn create-polygon
  [vertices offset rect]

  (let [step (/ (* 2 Math/PI)
                vertices)
        x (int-array vertices)
        y (int-array vertices)
        xradius (/ (.-width rect) 2)
        yradius (/ (.-height rect) 2)]
    (dotimes [i vertices]
      (let [current-x (+ (.-x rect) xradius (int (* (Math/cos (+ offset (* i step))) xradius)))
            current-y (+ (.-y rect) yradius (int (* (Math/sin (+ offset (* i step))) yradius)))]
        (aset x i current-x)
        (aset y i current-y)))
    (Polygon. x y vertices)))

This enables us to now create our array of basic images created with Clojure.
(def line-image
  (argb-image
    [100 100]
    (.setColor Color/BLACK)
    (.drawLine 0 0 100 100)))

(def square-image
  (argb-image
    [100 100]
    (.setColor Color/RED)
    (.fill (new Rectangle 10 10 80 80))))

(def circle-image
  (argb-image
    [100 100]
    (.setColor Color/BLUE)
    (.fill (new Ellipse2D$Double 0 0 100 100))))

(def rectangle-image
  (argb-image
    [100 100]
    (.setColor Color/ORANGE)
    (.fill (new Rectangle 10 30 80 40))))

(def right-triangle-image
  (let [p (new GeneralPath)]
    (.moveTo p 0.0 0.0)
    (.lineTo p 100.0 100.0)
    (.lineTo p 0.0 100.0)
    (.lineTo p 0.0 0.0)
    (.closePath p)

    (argb-image
     [100 100]
     (.setColor Color/GREEN)
     (.fill p))))

(def trapezoid-image
  (let [p (new GeneralPath)]
    (.moveTo p 0.0 100.0)
    (.lineTo p 100.0 100.0)
    (.lineTo p 70.0 0.0)
    (.lineTo p 30.0 0.0)
    (.lineTo p 0.0 100.0)

    (argb-image
      [100 100]
      (.setColor Color/CYAN)
      (.fill p))))

(def ellipse-image
  (argb-image
    [200 100]
    (.setColor (new Color 0xBCD4E6))
    (.fill (new Ellipse2D$Double 0 0 200 100))))

(def ring-image
  (let [core-circle (new Ellipse2D$Double 0 0 100 100)
        subcircle (new Ellipse2D$Double 20 20 60 60)
        area (new Area core-circle)]
    (.subtract area (new Area subcircle))

    (argb-image
      [100 100]
      (.setColor Color/MAGENTA)
      (.setStroke (new BasicStroke 5))
      (.fill area)
      (.draw area))))

(def polygon-image
  (let [polygon (Polygon.
                  (int-array [40 100 80 90 55 10 0])
                  (int-array [100 80 60 10 5 30 50])
                  7)]

    (argb-image
      [100 100]
      (.setColor Color/ORANGE)
      (.fill polygon))))

(def cross-image
  (let [polygon (Polygon.
                  (int-array [20  80 80 100 100 80 80 20 20 0 0 20] )
                  (int-array [100 100 80 80 20 20 0 0 20 20 80 80])
                  12)]

    (argb-image
      [100 100]
      (.setColor Color/ORANGE)
      (.fill polygon))))

(def diamond-image
  (let [polygon (Polygon.
                  (int-array [50 100 50 0])
                  (int-array [100 50 0 50])
                  4)]

    (argb-image
      [100 100]
      (.setColor Color/GRAY)
      (.fill polygon))))

(def semicircle-image
  (argb-image
    [100 100]
    (.setColor Color/BLUE)
    (.fillArc 0 25 100 100 180 -180)))

(def pentagon-image
  (let [polygon (create-polygon 5 0 (new Rectangle 0 0 100 100))]
    (argb-image
      [100 100]
      (.setColor Color/RED)
      (.fill polygon))))

(def hexagon-image
  (let [polygon (create-polygon 6 0 (new Rectangle 0 0 100 100))]
    (argb-image
      [100 100]
      (.setColor (Color. 0xBFFF00))
      (.fill polygon))))

(def octagon-image
  (let [polygon (create-polygon 8 0 (new Rectangle 0 0 100 100))]
    (argb-image
      [100 100]
      (.setColor (Color. 0xEEDC82))
      (.fill polygon))))

(def pic-image
  (argb-image
    [100 100]
    (.setColor Color/BLACK)
    (.fillArc 0 0 100 100 20 320)))

As is this is a lot of images being created we might as well display them all at once.

Displaying the images

In order to display all the images at once I will create a JFrame and then put each image in a ImageIcon of JLabel, laying them out using a GridLayout. In particular, this uses the constructor for GridLayouts with two extra arguments so we can have added padding.
(def image-grid
  [[line-image right-triangle-image rectangle-image cross-image]
   [square-image circle-image pentagon-image diamond-image]
   [trapezoid-image ring-image polygon-image semicircle-image]
   [hexagon-image octagon-image pic-image ellipse-image]])

(defn display-image-grid!
  [images]

  (let [frame (JFrame. "Image grid")
        panel (JPanel.)]
    (.add frame panel)
    (.setLayout panel (new GridLayout (count images) (count (first images)) 10 10))
    (doseq [image-row images]
      (doseq [image image-row]
        (.add panel (new JLabel (new ImageIcon image)))))
    (.pack frame)
    (.setVisible frame true)))

This code produces a JFrame that looks like below. There is so much more that can be done with graphics and Clojure. I will be talking more about graphical applications in the future and the key role the JVM can play in them making them.

Thursday, June 2, 2022

Explorations in randomness

The next example uses Java 2D to create a random image. This demonstrates the combiantion of 2D graphics and randomness.
(ns view.core
  (:import (java.awt.image BufferedImage)
           (java.util Random)
           (java.awt Point Color AlphaComposite)
           (java.io File)
           (javax.imageio ImageIO)))

(defn make-image
  []

  (let [width 500
        height 500
        img (BufferedImage. width height BufferedImage/TYPE_INT_ARGB)
        g (.createGraphics img)]
    ; do the graphics processing here

    (.setColor g Color/WHITE)
    (.fillRect g 0 0 width height)

    (let [rand (Random.)
          component-width 25
          component-height 25]
      (letfn [(fillRandomOval []
                (let [point (Point.
                              (.nextInt rand (- width component-width))
                              (.nextInt rand (- height component-height)))]
                  (.fillOval
                    g
                    (.-x point)
                    (.-y point)
                    component-width
                    component-height)))]

        (.setColor g (new Color 255 0 0 196))
        (dotimes [i 100]
         (fillRandomOval))

        (.setColor g (new Color 0 0 255 64))
        (dotimes [i 100]
          (fillRandomOval))))

    ; dispose the graphics
    (.dispose g)

    ; return the image
    img))

(defn main
  []

  (let [img (make-image)]
    (ImageIO/write
      img
      "png"
      (File. (str (System/getProperty "user.home") "/Media/random.png")))))

Of course, for this example we use Image I/O to store the image we produce, because doing so ensures we won't lose any information about the image we are producing. Unlike in the previous examples, make image here is not a pure function: it produces different results for the same arguments depending upon the time it is called. This is an issue I will have to discuss when I talk about the relationship between functional programming and graphics.