Tuesday, May 31, 2022

A first look at Java 2D

The Java 2D API brings basic graphical capabilities to the JVM. We will see how it can be used to create graphical applications in either Java or Clojure. To do anything more advanced it is good to use OpenGL with a library like JOGL or LWJGL.

Image viewer

We will be using Graphics2D to create a number of graphical images. It would be nice then if we could have a Swing window to render and display them.
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

public class ImageViewer extends JFrame {
    public BufferedImage image;

    public ImageViewer(BufferedImage image) {
        this.image = image;
        this.initialize();
    }

    public void initialize() {
        var p = new JPanel();
        this.add(p);

        var imageIcon = new ImageIcon(this.image);
        p.add(new JLabel(imageIcon));

        this.setSize(this.getPreferredSize());
        this.setTitle("Image viewer");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public Dimension getPreferredSize() {
        var insets = this.getInsets();

        return new Dimension(this.image.getWidth()+ insets.left+insets.right + 30,
                this.image.getHeight() + insets.top + insets.bottom + 30);
    }

}

This is the class we will be using to display the various images we generate using Java 2D. Here is similar Clojure code.
(defn display-image
  [img]

  (let [f (JFrame.)
        icon (ImageIcon. img)
        label (JLabel. icon)]
    (.setTitle f "Image viewer")
    (.setSize f 500 500)
    (.add f label)
    (.setVisible f true)))

The Clojure program just displays an image, but it doesn't go about doing anything more then that so it is a little bit simpler.

Graphics programming with Java

The 2D API contains functions for clipping and rotating. So here is how you would create something that looks like a wheel, which is an image with a bunch of lines around it.
public class BasicImageProducer {

    public static BufferedImage makeImage() {
        var rval = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
        var g = rval.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.BLACK);
        Ellipse2D oval = new Ellipse2D.Double(5,5,90,90);
        g.setStroke(new BasicStroke(5));
        g.draw(oval);

        g.setStroke(new BasicStroke(2));
        g.setClip(oval);
        int l = 10;
        for(int i = 0; i <= l; i++) {
            g.setTransform(AffineTransform.getRotateInstance(Math.PI * ((double) i / l), 50, 50));
            g.drawLine(0,0,128,128);
        }

        return rval;
    }

}

This produces a graphical output that looks like this: So that previous image has a bunch of lines clipped inside of its circle. So one thing we can do is instead placing those lines outside the circle to get something like this.
public class AlternativeImageProducer {

    public static BufferedImage makeImage() {
        var rval = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
        var g = rval.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.LIGHT_GRAY);
        g.setStroke(new BasicStroke(10));
        int l = 16;
        for(int i = 0; i <= l; i++) {
            g.setTransform(AffineTransform.getRotateInstance(Math.PI * ((double) i / l), 200, 200));
            g.drawLine(0,0,400,400);
        }

        g.fillOval(100,100,200,200);

        return rval;
    }

}

It is only a slight change of the code and it produces this result:
These are some interesting examples of what is possible with the Java 2D API.

Graphics programming with Clojure

Its always nice to be able to use Clojure, so we will do something with it too.
(defn triangle-image
  []

  (let [img (BufferedImage. 400 410 BufferedImage/TYPE_INT_ARGB)
        g (.createGraphics img)]
    ; process the graphics
    (.setColor g Color/RED)
    (.setStroke g (new BasicStroke 5))

    (.setRenderingHint g RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON)

    (let [path (Path2D$Double.)]
      (.moveTo path 200 0)
      (.lineTo path 0 400)
      (.lineTo path 400 400)
      (.lineTo path 200 0)
      (.closePath path)
      (.draw g path))

    (.dispose g)

    ; return the image
    img))

The 2D API doesn't have a builtin triangle class. So to create one we just use a Path2D. The previous example is a bit elementary. We can do a lot more if we get some interesting paints involved like the radial gradient paint.
(defn radial-paint
  []

  (let [w 400
        h 400
        img (BufferedImage. w h BufferedImage/TYPE_INT_ARGB)
        g (.createGraphics img)]
    ; process the graphics
    (.setRenderingHint g RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON)

    (let [p (RadialGradientPaint.
              (new Point2D$Float (* 0.5 w) (* 0.5 h))
              (float (* 0.5 w))
              (float-array [0.0 1.0])
              (into-array Color [Color/CYAN Color/MAGENTA]))
          shape (new Ellipse2D$Double 0 0 400 400)]
      (.setPaint g p)
      (.fill g shape))

    (.dispose g)

    ; return the image
    img))

This now produces the following interesting image. As we will see there is much more that can be done with the Java 2D graphics API. It is readily available in both Java and Clojure.

Monday, May 30, 2022

Hardware aspects of the Java desktop module

The JVM is a portable architecture for writing programs. This portability ensures that Java programs can be run on virtually any processor. While it is true that the JVM bytecode can be run on virtually on any processor, it is certainly not the case that every CPU comes with the same user interface devices like monitors, speakers, printers, mouses, keyboards, etc. Nor can we assume that there aren't subtle differences between monitors and graphics devices, like in how they render colours.

Programmable computers have the property that they can be universalised and the JVM achieved this, but other hardware devices are a different story. The former is achieved by the JVM while the later is dealt with by Java SE. The Java SE desktop API lets you deal with subtle differences between computer setups, like single versus multiple monitors, different colour models, and various other subtle differences.

There is one more point which just as important. Processors today are not designed with developers in mind. Why bother when no one targets any one processor in particular. The Java virtual machine (JVM) makes your computer into a developer friendly platform, aside from giving you portability. There is never a downside to running your code on a JVM because it is always better then your actual processor and with extremely advanced optimizations any speed differences are negligible.

The JVM easily is the best piece of software ever developed. It completely solves portability issues between CPUs. Portability questions for graphical user interfaces are a different story though. There is an issue now of portability between desktop and mobile applications. One wonders if there will ever be a solution to this problem.

Far too often the solution to mobile-desktop portability issues is to create a mobile interface and give it to desktop users then call it a day. Perhaps developers just need to do a little bit of extra work to make programs work differently on mobile devices and desktop devices. We will see how Java SE lets you get started in handling GUI hardware.

Graphics devices:

The Java SE API has direct support for handling graphics devices with the GraphicsDevice class as well as multiple graphics devices with GraphicsEnvironment. To get the graphics devices from the local graphics environment use getScreenDevices as demonstrated below. This also lets you print the screen bounds of each device.
(let [env (GraphicsEnvironment/getLocalGraphicsEnvironment)
      devices (.getScreenDevices env)]
  (doseq [device devices]
    (prn (.getBounds (.getDefaultConfiguration device)))))
The graphics device class supports direct access to hardware resources on the Java platform. Aside from this, the Java 2D API has everything you need to basically create graphics on the Java platform. It includes support for geometry, compositing, painting and stroking, transformations, clipping, rendering hints, fonts, text layouts, colors, image creation, image drawing, image processing,

The basic way you interact with 2D graphical devices using Java SE is using the Graphics2D class. Graphics2D has all the relevant methods for applying the different parts of the Java 2D API. You can even use it to create images using BufferedImage and its create graphics method. On the other hand, the image processing operations like ConvolveOp operate directly on images.

You can even directly operate on images on the bit level by handling its sample model, data buffer, and color model yourself. Most data in a buffered image is stored in its data buffer, which is an array which stores pixel data in some manner. In most applications it probably sufficise to use the Graphics2D API directly instead of doing primitive operations on color data.

Printers

The java.awt.print package is part of the Java printing API. It presents quite a simple interface to printing. It is also based upon the Java 2D graphics API. There are two classes that enable printing: Printable and Pageable. The former is used to print a bunch of pages with similar page formats while the later can be used to define pages with a variety of different formats. The formats are defined by the page format class.

Typically you can enable printing by subclassing Printable and overriding the print method, which takes in a Graphics as well as a page format and a page number. Then you can interact with the printer using the Graphics object and the Java 2D API. In fact, a printer is just another type of graphics device. Aside from these components, the PrinterJob does the actual job of sending the printable or pageable object to the printer.

The Java print service extension lets you specifically look up all available printers using the PrintServiceLookup class. It has a static method lookupPrintServices that gets all print services available on your systems. The javax print extensions also supports print attribute sets and the ability to select printers that support those attributes.
(let [services (PrintServiceLookup/lookupPrintServices nil nil)]
  (prn (count services))
The Java print service is comparatively a much richer API then the simpler java.awt.print package. The later suffices unless you need advanced control over print handling, events, and attributes. When you need advanced control use the former API.

Audio

Not all computers, even ones with graphical user interface capabilities support sound. But for those that do, you can use them using the Java sound API in the Java desktop module. The Java sound API supports the AU, AIFF, and WAV file formats as part of its sampled audio component.

In addition, it has support for MIDI (music instrument device interface) which can be used to synthesis sounds from music. With the MidiDevice interface, the Java sound API has direct support for MIDI devices.

Input devices

The MouseInfo class in the AWT package allows you to get direct information about the mouse used on the system:
(prn (str "Number of buttons: " (MouseInfo/getNumberOfButtons)))
(prn (str "Mouse location: " (.getLocation (MouseInfo/getPointerInfo))))
The main thing that the Java SE package needs to do to support input devices rather they are the keyboard or the mouse is event handling. This is provided in the java.awt.event package. This allows you to establish listeners that react to keyboard and mouse clicks, which can be used to create interactive programs. Input devices are not that interesting by themselves, so they need to be used in conjunction with other devices.

The overall desktop experience

Swing builds on many of the previously mentioned components like the 2D graphics API and the event handling system to provide the user with a unified user experience. It also builds on the AWT Container/Component model and its layout management system. In many ways it is the singular highlight of the entire Java desktop module.

Swing is not the definitive solution for creating graphical user interface applications. However, it is standard, battle tested, well supported, and good enough for some of the best desktop applications today like IntelliJ, NetBeans, and Protege. If you use Swing it can be the basis of your applications too.

Notes
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.

Saturday, May 28, 2022

Surpassing the object-morphism distinction

The distinction between objects and morphisms lies at the heart of elementary category theory. In order to create a coherent logical narrative, we need to move beyond this distinction.

Background

Mathematics cannot be completely separated from philosophy. There is always some basic philosophical framework that addresses questions of logic, ontology, mathematics, and their relationship to one another. This philosophical framework consists of a series of conditions placed on mathematical structures.

A first condition I have is that every mathematical object should have some kind of logically comprehensible structure. I have always had the desire that every mathematical object, or even every computational entity, should have comprehensible logical innards. Set theory solves this problem by modeling mathematical entities as sets.

The first entity that you can imagine that has a well defined logical structure is a set. The parts of a set can be modeled as subsets, which defines a logically reasonable structure on sets. Set theory certainly provides an initial solution to our problem of giving every mathematical object intelligible innards.

The problem is the intelligible innards you get are all wrong. It at least gets an internal structure for a mathematical object, but for anything involving functions the results we not all what we want. Topos theory is needed for us to get to the right logical structure of mathematical objects like functions and to create a coherenent logical narrative of mathematical structures.

In order to create a well defined logical structure a minimum necessary condition is to define a partial order, which relates a structure to its parts. This logical notion can be extended by using categories, which generalise partial orders. We need to make morphisms part of some logical structure so tha they have comprehensible innards. This problem is solved by making morphisms into objects.

Morphisms have morphisms too

Set theory can be made categorical by defining functions to be the morphisms between sets. The resulting topos $Sets$ is perhaps the quintessential example of a category.
  • Objects: sets
  • Morphisms: functions
The problem is that functions $f : A \to B$ don't have morphisms of their own. We can solve this problem by introducing morphisms of functions from $f: A \to B$ to $g : C \to D$ as ordered pairs $(i : A \to C, o : B \to D)$ that satisfy a commutative diagram: What do we gain by this construction? This makes it so that we can define precisely the intelligible innards of functions we want. These are exposed as the subobject and congruence lattices of objects of the topos $Sets^{\to}$. This gets around the apparent lack of a logic describing functions.

Every function $f : A \to B$ can now be understood in terms of its parts. These parts are either subfunctions determined by subalgebras or quotient functions determined by congruences. We can finally reason logically about functions and reduce to them to their constituent parts. We did this by introducing morphisms of functions, so we wonder if we can reason about them logically too.

If we look back at that commutative diagram, we can think of a number of different ways to reduce its constituent sets and functions. A notable fact about functions is that given $f : A \to B$ and $S \subseteq A$ there is always a subobject $f|_S : S \to B$ in the topos functions determined by restriction. A natural generalisation is that we can take any subobject $(S,T)$ of the source function $f: A \to B$ of a morphism of functions and restrict it to that subobject. This is in fact equivalent to a composite diagram. So there a number of ways to get subobjects of morphisms of functions, but the same process can even be extended. We can even get subobjects of morphisms of functions by getting subobjects of the target function as long as those the elements removed are not in the image of the first function $i$ or the second function $o$. So there are a number of ways of getting subobjects of morphisms of functions by restricting any of its four sets.

At this point we can introduce the topos of diamonds. Morphisms of functions form a diamond shaped commutative diagram, so that they are essentially copresheaves over the diamond category. Subobjects and quotients of morphisms of functions are then defined in terms of this topos. The previous subobject of a morphism of functions is now defined in terms of a cube diagram: The topos of diamonds consists of morphisms of functions as its objects and cube natural transformations like these as its morphisms. This means not only do functions have their own morphisms, but their morphisms of functions do as well. We get the following sequence of morphisms:

Set $\to$ Function $\to$ Diamond $\to$ Cube

This can be extended infinitely as well, so that next we can get morphisms of cubes. Then cubes have their own subobjects and quotients and so on. We finally get an infinitely extensible framework aside in which each entity has its own intelligible innards. This loses none of the logical cohesiveness that we had before.

Morphisms of copresheaves

Let $Sets^C$ be a topos of copresheaves over $C$. Then we consider morphisms in the topos $Sets^C$ in terms of objects of the topos $Sets^{C \times T_2}$. This means that morphisms of copresheaves are themselves copresheaves.

Definition. Let $P \in Ob(Sets^{C \times T_2})$ be a copresheaf. Then 0 and 1 form trivial subcategories of $T_2$ so by extension so do $(C,(0,0))$ and $(C,(1,1))$ of $C \times T_2$. These subcategories induce inclusion functors, and by composition this allows us to produce copresheaves $P_0$ and $P_1$ of $P$ in $Sets^{C}$. Then we form a morphism $P_0 \to P_1$ in $Sets^C$ with component arrows $(1_A, (0,1))$ in $Sets^{C \times T_2}$ for each object $A \in Ob(C)$.

Theorem. the preceding definition is a valid morphisms of copresheaves

Proof. let $m : A \to B$ be an arrow in $C$. Then $P_0(m) = (m,(0,0))$, $P_1(m) = (m,(1,1))$, $\tau_A = (1_A, (0,1)$, and $\tau_B = (1_B, (0,1))$. It remains to show that $P_1(m) \circ \tau_B$ is equal to $\tau_A \circ P_0(m)$. By substitution we have $P_1(m) \circ \tau_B = (m,(1,1)) \circ (1_B, (0,1))$ but this equals $(m,(0,1))$. Similarily, by substitution we have that $\tau_A \circ P_0(m)$ is equal to $(1_A,(0,1)) \circ (m,(0,0))$. This equals $(m,(0,1))$. So the two compositions coincide. $\square$

Definition. let $F$ and $G$ be copresheaves in $Sets^C$ with a morphism $\tau : F \to G$. Then we can define a copresheaf $P$ in $Sets^{C \times T_2}$. Let $O \in Ob(C \times T_2)$ then $P(o,n)$ is equal to $F_o$ if $n=0$ and $G_o$ if $n=1$. On the other hand let $M : X \to Y \in Arrows(C \times T_2)$. Then there are three cases for $P(M)$ if it is equal to $(m,(0,0)$ then we get $F_m$, if it is equal to $(m,(1,1)$ it is equal to $G_m$, finally if it is $(m,(0,1))$ it is equal to $\tau_Y \circ F(m)$ or $G(m) \circ \tau_X$.

Theorem. the preceding definition produces valid copresheaves

Proof. let $(C_1 : Y \to Z, t_1)$ and $(C_2 : X \to Y, t_2)$ be morphisms in $C \times T_2$. Then there are four patterns of composition.

(1) if $t_1$ and $t_2$ are equal to $(0,0)$ then we get that \[ P((C_1 : Y \to Z, (0,0)) \circ (C_2 : X \to Y, (0,0))) \] \[ = P(C_1 \circ C_2, (0,0)) = F(C_1) \circ F(C_2) \] At the same time, $P(C_1 : Y \to Z, (0,0))$ is equal to $F(C_1)$ and $P(C_2 : X \to Y, (0,0))$ is equal to $F(C_2)$ so the two compositions coincide.

(2) similarily if $t_1 = t_2 = (0,0)$ then we get the following \[ P((C_1 : Y \to Z, (1,1)) \circ (C_2 : X \to Y, (1,1))) \] \[ = P(C_1 \circ C_2, (1,1)) = G(C_1) \circ G(C_2) \] If we plug in $P(C_1 : Y \to Z, (1,1))$ is equal to $G(C_1)$ and $P(C_2 : X \to Y,(1,1))$ is equal to $G(C_2)$ so the two compositions coincide.

(3) in the third case $t_1 = (0,1)$ and $t_2 = (0,0)$ \[ P((C_1 : Y \to Z, (0,1)) \circ (C_2 : X \to Y, (1,1))) \] \[ = P(C_1 \circ C_2, (0,1)) = \tau_Y \circ F(C_1) \circ F(C_2) \] \[ = (\tau_Y \circ F(C_1)) \circ F(C_2) = P(C_1,(0,1)) \circ P(C_2,(1,1)) \] This confirms that these two compositions coincide.

(4) in the third case $t_1 = (1,1)$ and $t_2 = (0,1)$ \[ P((C_1 : Y \to Z, (1,1)) \circ (C_2 : X \to Y, (0,1))) \] \[ = P(C_1 \circ C_2, (0,1)) = G(C_1) \circ G(C_2) \circ \tau_Y \] \[ = G(C_1) \circ (G(C_2) \circ \tau_Y) = P(C_1,(1,1)) \circ P(C_2,(0,1)) \] This confirms that $P$ is a valid functor. $\square$

Infinitely categorical:

In order to get to our philosophical goal of every mathematical entity having a comprehensible logical structure, we have to have every mathematical entity be an object of some category. We saw this when we introduced morphisms of functions, and then further morphisms on top of those as well.

At no point should we reach an endpoint where we have a morphism, which doesn't have any further morphisms of its own. This gets to the philosophy of infinity category theory, in which each morphism has its own morphisms extended on to infinity. This is a process that we started with sets and functions.

Presheaf foundations:

The importance of this construction is that it means that presheaf theory is stable under taking objects and morphisms. This means that presheaf theory can truly be used as a foundation of mathematics. The importance of this approach is that it gives the best logical structure to mathematics and its constructs.

The Locus project puts this philosophy into practice. It represents structures as much as possible as presheaves, and it makes their logical structure available to the user. This is to be combined with an ontology of classes of copresheaves and a graphical user interface program for interacting with copresheaves. This is the start of the new approach to topos theory.

Sunday, May 22, 2022

Portable machine code

The Java virtual machine (JVM) pioneered the idea of write once run anywhere programs. Once a program has been compiled into JVM bytecode once it should be runnable across platforms. With the security manager, the Java platform allowed portable programs to be run in a secure sandbox. The combination of portability and security led to Java applets, which were a defining part of the early web.

The idea of a universal portable machine code format was pioneered by Sun Microsystems with the Java virtual machine (JVM). The basic point of the Java project was to a create language for a universal platform that can be run on any device or operating system. The point of the JVM is to be the platform that enables this. With revolutionary advancements in portability and security, the Java platform sought to reinvent computing.

The Java platform was more then just an idealistic project to reinvent computing, it was a major engineering effort. The JVM was accompanied by an extensive class library and the most advanced optimisations possible to make running Java code efficient. The combined engineering efforts of the developers at Sun microsystems is what made these ideals into a reality.

Of course, the project to create a portable machine code format didn't go over too well with everyone. Microsoft wasn't too happy about it. The problem was that a portable machine code format like JVM bytecode would mean that Java software could be used on other platforms not controlled by them. The idea of a portable machine code format was new at the time so they didn't know how to react.

The portability idea of Java was embodied in the 1995 slogan "write once, run anywhere" of Sun Microsystems. Its important to note that it is not simply portability that defined the Java platform, but the bytecode format. Other languages might provide portability with a text-based format, but Java provided it with a bytecode format and assembly language. The JVM is a portable bytecode format.

This portable bytecode format, for a long time, was run in web browsers as Java applets. But it was not to last, as Java applets came to an end. Sun's vision of a write once, run anywhere portable binary format was too ahead of its time. The idea of a portable machine code format is still an ideal worth striving towards. That is where WebAssembly enters into the scene.

WebAssembly is not an innovative or revolutionary new technology. By moving to create a portable machine code format, WebAssembly is treading on very old ground. But perhaps by creating a portable machine code format, WebAssembly can revive Sun's great ideal of a "write once, run anywhere" binary format and what was achievable by Java applets.

At this point, we should ask what the point of the web is. Is it to create a rich text format, with a little bit of interactivity and animation or is it to create a thin client to run applications on? If it is the later, there is no reason that HTML is necessary. A web like platform could be constructed with JavaFX and FXML, by adding hyperlinks to FXML and opening up JavaFX applications in a secure sandbox. In an alternative reality, that could be how the web evolved.

External links:
Oracle Java

Saturday, May 21, 2022

The java desktop module

The java desktop module is the modern evolution of the abstract window toolkit (AWT) released with the first version of Java. With the modularization of the JDK, all the functionality that grew around AWT was moved into the desktop module. The module has four major components:
  • AWT
  • Swing
  • Java 2D
  • The Java Sound API
Aside from these components, there are several minor components like accessibility, drag and drop, and java beans. Conceptually, the AWT can be be broken down in to two components: the user interface component and the media subsystem. The UI framework evolved into Swing and the media subsystem evolved into Java 2D and eventually Java Sound.

The basic media functionality that a program for desktop computers needs to support are 2D graphics and sound. These are the basic building blocks of all media applications. These are provided by the 2D graphics API and the sound API. However, media applications don't include interactivity which is the role of the user interface framework.

In order to support interactive applications Java SE desktop applications use the Java 1.1 event model. In this model, applications add one more listeners to the events to a given component. As Swing is built on the building blocks of AWT, it uses the same java.awt.event event model as AWT. By combining rich graphics built with the Java 2D API and the event model, any kind of interactive GUI program can be built with Java.

AWT

The AWT was the first widget toolkit released for Java. The main role of AWT now is to be a building block for Swing. The main way that AWT does this is through the Component and Container classes which all Swing components extend from. The Component and Container class provide a couple hundred methods dealing with a wide variety of purposes like event handling, grouping and layout, resizing, colouring, painting, fonts, and so on.

The Component class defines the interface from widgets to the AWT event model, which is also still used by Swing. Container describes the interface to the AWT layout managers, which again are reused with Swing. Aside from the Component and Container classes, the various Window classes of AWT are inherited by Swing. A JFrame for example extends from the AWT classes Frame and Window.

Swing

If you want to actually build a graphical user interface application using Java SE, you are going to be using Swing. It builds upon AWT, but it provides a wide variety of original functionality and many new widgets. Swing components include labels, button, checkboxes, lists, menus, radio buttons, sliders, spinners, text fields, text areas, editor panes, progress bars, trees, tables, and color panes among others.

Swing is also notable by the fact that you can use HTML and CSS in Swing components. Just include a well formed HTML document in your JLabels. This certainly comes in handy when you want to quickly add rich text to Swing programs. Swing also comes with its own pluggable look and feel framework, so that you can change how Swing components look.

Java 2D

The Java 2D API includes all kinds of functionality to support 2D graphics on the Java platform: including all the support for colors, fonts, geometry, graphics, painting, imaging, and printing. The Java 2D API is the natural evolution of the drawing part of the AWT toolkit.

With the 2D graphics API, you can create all kinds of shapes using the java.awt.geom package and the Shape interface. Then you can control the drawing of them, with colors, compositing, rendering hints, etc. The 2D graphics API also can easily be used functionally from Clojure to create all kinds of amazing visualisations, which I will talk about later.

The Java sound API

The Java 1.3 release introduced the Java sound API, which later became a part of the desktop module. It lets you add the one final piece to the desktop application programming puzzle, which is sound. The sound API only supports three file formats: AU, AIFF, and WAV. Therefore, to handle other file formats you may need other libraries.

The Java sound API also includes support for the synthesis of MIDI music file formats like MIDI type 0, MIDI type 1, and the Rich Music Format (RMF). It enables the sequencing and synthesis of MIDI wavelets and access to MIDI hardware devices. So it adds the finishing touchs to the desktop module.

External links:
The java desktop module

Friday, May 20, 2022

Java desktop applications

I have posted a number of times about the various Java-based desktop application frameworks like Swing and JavaFX, and how you can create various applications with them in either Java or Clojure. It occurs to me that I should write an overview of the different desktop libraries that you can use in your JVM applications.

AWT

The AWT (abstract window toolkit) was introduced with Java 1.0. A major purpose of AWT and the first versions of Java was to provide support for Java applets. An applet is an AWT component and container, so applets were fully integrated into the AWT widget system. They basically allowed you to add AWT components into webpages. There weren't many options to add interactivity into websites at the time.

The AWT toolkit came with a peer system provided by the java.awt.peer package. Each AWT component came with a peer, which is that component defined in the native environment. This means that AWT components were heavy weight, already in the next version of Java was updated to address this.

The old peer system meant that each new Component class had its own opaque native window. Java 1.1 was updated with the lightweight UI framework which allowed you to directly extend java.awt.Component and java.awt.Container to create your own components that do not have native windows associated to them. In a way, this layed the foundation for Swing which builds upon these classes. It goes without saying that classical AWT is not used anymore.

Swing

The Swing library was released as part of Java 1.2. It was based upon the Java 1.1 lightweight UI framework. In particular, all Swing components extend java.awt.Component and java.awt.Container. They are lightweight, so they don't have native counterparts.

In place of native widgets, Swing came with a pluggable look and feel (PLAF). Swing widgets don't inherit from native widgets, but in theory you can at least make them look like them. Or you can modify the existing look and feel to create your own.

The fact that Swing was constructed on the AWT means that it has a lot of historical legacy. For example, all Swing widgets start with the letter "J" so that they don't interfer with their AWT counterparts. So you use JFrame instead of Frame, and JButton instead of Button, and so on. This makes Swing appear like a legacy framework with a great deal of baggage.

The fact that Swing was introduced early on meant that applets were still widely used. Swing extended the Applet class with the Swing based JApplet class, and after that many applets were created with Swing. Applets were phased out in the 2010s, for various reasons that we could go into depth on later. Probably the most basic one is that JavaScript gained a kind of maturity that led browser vendors to consider it sufficient.

Swing is basically where we are at now. Although it doesn't run in browsers and it is full of historical legacy, it is still a part of every Java SE distribution and some of the best desktop applications are developed with it. IntelliJ IDEA, for example, was created with Swing. So we are stuck with Swing as part of the Java desktop application stack for the forseeable future.

JavaFX

The fact that Java Swing comes with so much legacy means that it is necessary to have a fresh start. That fresh start came with JavaFX. The components in JavaFX do not extend from their AWT counterparts. JavaFX was intended to replace Swing in Java SE, but that project was abandoned and it was moved out of the JDK to be maintained elsewhere.

JavaFX also comes with a number of more modern features, like an updated scene graph library. Scene graphs are a fundamental part of any advanced structured graphical applications like zooming user interfaces or interactive 3D components. JavaFX was also equipped with an XML based file format and support for CSS styling of components. Its API is more modern and clean then Swing.

Alternatives

The Java platform comes with the Java native interface (JNI) which allows Java programs to interoperate with C/C++ programs. In theory, this should allow you to create any number of different types of user interfaces using native components. The only really successful example is SWT. It uses the JNI to create Java based extensions of native widgets, to provide users with a native look and feel that fits in with the platform.

As is the case with AWT, this means that it is hard or impossible to extend SWT components because they rely on native peers. Additionally, the fact that SWT requires the JNI means that you have to package your program to deal with different desktop environments and toolkits. SWT is not part of Java SE, but unlike other alternative desktop toolkits it has strong support because of its use in the Eclipse IDE.

Remarks

The Java platform is widely used in scientific and mathematical applications. These applications of course need to have data visualisation functionality associated with them. I am looking in to JavaFX for scientific visualisations, it already has its own charting library built in for example. But I can't make any promises, all I know is I will continue to use the JVM which I have already dedicated so much time to.

External references:
Java desktop module

JavaFX overview

Monday, May 16, 2022

Copresheaves by generating systems

In order to work with a variety of categories, it is necessary that we should be able to describe categories by morphic generating systems. As we construct larger and larger categories, it gets less and less practical to work with all morphisms exhaustively. By limiting ourselves to a generating set, we can greatly improve the practicality of a number of categorical constructions.

Definition. let $C$ be a category with objects $Ob(C)$ and morphisms $Arrows(C)$. Then a set $G$ is called its morphic generating set provided that $cl_C(g) = Arrows(C)$ where $cl$ is the closure function on $Arrows(C)$.

This definition does not require that a given morphic generating set be minimal. Therefore, we may as well say that every category is morphically generated by its entire set of objects by default. A consequence of this definition is that every category has a morphically generated subquiver (for example in the Locus computer algebra system this is the quiver that is displayed in the copresheaf viewer).

Definition. let $C$ be a category with morphic generating set $G$ then the morphically generated subquiver of $C$ with respect to $G$ is the subobject of $Quiv(C)$ in the topos of quivers generated by $Ob(C)$ and $G$.

In order to make the morphic generating set useful in most constructions it is necessary that we should have some kind of factorisation that can be used to describe all morphisms of $C$ in terms of $G$. For example, in the case of simplicial sets each morphism in $\Delta$ is described by its canonical factorisation into face and degeneracy maps.

Definition. let $C$ be a category generated by a morphic generating set $G$. Then a canonical factorisation is a function $f: Arrows(C) \to Seq(G)$ that takes morphisms in $C$ to sequences of elements of $G$.

There are different approaches to doing category theory. Certainly, in the topos theory branch which I am primarily concerned with, the fundamental objects are copresheaves $F: C \to Sets$. The great utility of morphic generating systems, as we shall see, is that we can use them to define copresheaves $F: C \to Sets$.

Theorem 1. let $F : C \to Sets$ be a copresheaf. Let $F'$ be the mapping which is only defined on $Ob(C)$ and $G$ then $F'$ completely determines $F$.

Proof. let $o \in Ob(C)$ then $F'(o) = F(o)$ so we can use $F'$ to determine the effect of $F$ on objects. On the other hand, suppose that $m \in Arrows(C)$. Then $m$ need not be in $G$, so we must produce a factorisation (such as the canonical factorisation or any other factorisation). Let us suppose that $g_1g_2g_3 = m$ is a valid factorisation of $m$. Then by the definition of a functor $F(m) = F(g_1)F(g_2)F(g_3)$. The functions $F$ and $F'$ coincide on $G$ so this can be replaced by $F(m) = F'(g_1)F'(g_2)F'(g_3)$. Therefore, $F'$ fully determines $F$. $\square$

From now on we can consider a copresheaf as entirely determined by the morphic generating set of a category $C$. Consider the theory of finitely presented lattices. Then in that case, such lattices are defined by object generating sets with systems of relations. These object generating systems of lattices are useful in defining logical varieties, but they are not so useful when defining categories. That is why we have to place special emphasis on morphic generating systems.

Here are some of the contexts that morphic generating systems emerge in:
  • Hereditary discrete partial orders: let $P$ be a hereditary discrete partial order, then the covering relation $Cov(P)$ of $P$ is a morphic generating set of $P$.
  • Finitely presented monoids: a monoid $M$ with a set of generators $G$ is also a category with one object and a morphic generating set $G$.
  • Finitely presented groups: a finitely presented group $G$ is also a finitely presented monoid with both elements and their inverses as generators.
  • Finitely presented categories: finally there are a number of cases wherein categories themselves are given by a presentation over a finite generating set $G$.
Certainly, the case of hereditary discrete partial orders means that copresheaves over any finite partial order can be defined by just their covering relation. This greatly simplifies the presentation of copresheaves over finite orders, such as sheaves over finite topologies. Finitely presented monoids are also an infinite source of examples of categories with generating sets, such as the bicyclic monoid. In the case of finitely presented groups, examples abound.

Finally, in the theory of categories proper we have countless instances where in it makes sense to limit ourselves only to a relevant set of generators. For example, when considering diamond copresheaves. Such diamond copresheaves occur from any morphism of functions, but if we constructed them without limiting ourselves to a generating set then we would always have a redundant composite morphism. The use of generating systems is a great simplification.

Consider the topos theory of categories. A category can be considered to be a copresheaf constructed from three sets: a set of composable pairs of morphisms called paths, a set of morphisms, and a set of objects and four functions: a composition function, a source function, a target function, and an identity morphism function. The composition function goes from composable paths to edges, the source and target functions go from edges to vertices, and the identity function takes a vertex and associates an edge to it. This is represented in the diagram below: Whilst this is the data of a category, its actual representation as a copresheaf requires that we also define all the other composite functions: the composition source, the composition target, the source identity, the target identity, the composition source identity, the composition target identity, and the identities of each object. The full index category looks like this: By restricting ourselves to a generating system, we get a much cleaner presentation of categories as copresheaves. This demonstrates the utility of morphic generating systems in presheaf topos theory.

Inheritance chains

Every java.lang.Class is associated with a superClass in the inheritance hierarchy. With a little bit of recursion, this leads to a totally ordered set of classes starting at the current class and ending at java.lang.Object.
(defn inheritance-chain
  [class] 

  (if (= class Object) 
    (list class) 
    (cons class (inheritance-chain (.getSuperclass class)))))
In addition, every Object is associated with a Class by the getClass method. So combined with the inheritance-chain function this can be used to produce a totally ordered set of all parent classes for any object. I have in mind to use this on an atom.
(inheritance-chain (.getClass (atom 1)))
;=> (clojure.lang.Atom 
;    clojure.lang.ARef 
;    clojure.lang.AReference 
;    java.lang.Object)
I seem to recall that clojure.lang.Atom is related to java.util.concurrent.atomic.AtomicReference, as this shows they are not related by inheritance. As it turns out Atom simply uses an AtomicReference internally to define its state. Another thing is that both classes use compareAndSet.
(import java.util.concurrent.atomic.AtomicReference)

(def x (atom 1))
(.compareAndSet x 1 10)
(.deref x)
;=> 10

(def y (AtomicReference. 1))
(.compareAndSet y 1 10)
(.get y)
;=> 10
Beyond that basic point, there isn't much relationship between the two atomic classes because they don't inherit from one another. For example, the clojure.lang.Atom uses deref to get its value whilst java.util.concurrent.atomic.AtomicReference uses get in order to get its value.

Whenever you want to learn more about some class you can look up its Javadoc, which will have its inheritance chain, implemented classes, fields, methods, etc. Sometimes, however, it is nice to be able to get information directly from the JVM using reflection on the java.lang.Class instance. For example, it is useful to use reflection when you are creating your own JVM language and you want to make sure your compiler produces the right bytecode. The inheritance-chain method is an example of the utility of reflection.