I read two political news articles today that both suggest the importance of software freedom for the sake of our societal infrastructure. The first article describes the Repair Coalition, a political lobbying group that seeks to make it both legal and possible for any repair person to repair any device. In the context of repairs, the freedom to redistribute modified copies of software seems to me to have particular importance. There is no guarantee that the manufacturer will release a defect-free product, and giving everyone the opportunity to improve upon it has great potential for benefiting all consumers.
The second article describes a controversy around the vote-tallying machines employed in the 2016 Iowa Caucus. These machines were manufactured and programmed by Microsoft. The company is accused of a conflict-of-interest, as many of their employees have donated to campaigns for active candidates in the current campaign. It is in our collective interest that these machines be trustworthy. The only real path forward is to allow their software to be available for audits to any interested person, with no special permission needed.
In both cases, what we have is instances of closing software with the result of social exclusion. Those who wish to repair devices are excluded from doing so by manufacturers who refuse to make the source code available. Those who wish to audit the result of electronic vote-tallies are excluded from doing so by the manufacturer. And cases like this are just the tip of the iceberg: for example, what about the passengers in self-driving cars?
For what positive end do we have this exclusion? Must intellectual property be concealed to make it economically feasible for manufacturers to create their products? Not all manufacturers agree. The pervasiveness of software throughout our society seems to me to correlate to an increase in the force of arguments for software freedom.
Observations and commentary on robotics, AI, machine learning, and computer science (and academic life) in general.
Saturday, February 6, 2016
Sunday, September 20, 2015
Displaying images from files in JavaFX
As I continue to both employ JavaFX in my own code and teach JavaFX in my courses, I am slowly learning how to perform tasks I used to do in Swing in new ways. The latest example is the all-important task of opening image files.
The strategy employed in this example is to set the image file as a background for a JavaFX Pane object. Opening the image is a straightforward use of the JavaFX file chooser dialog. Error handling is done via a simple error dialog created as an Alert. Displaying the image requires the following steps:
The strategy employed in this example is to set the image file as a background for a JavaFX Pane object. Opening the image is a straightforward use of the JavaFX file chooser dialog. Error handling is done via a simple error dialog created as an Alert. Displaying the image requires the following steps:
- Transform the File object into a URL, and use this to create an Image object.
- Create a BackgroundSize for the object.
- By setting the height and width to AUTO, the image will automatically resize as the window displaying it is resized.
- It will not deform the image; if the aspect ratio of the displaying window does not match the image, the image will be truncated.
- Create a BackgroundImage object.
- The first parameter passes the Image object to use as its basis.
- The second and third parameters control whether the object is repeated in the X and Y directions. For this example, I deemed this undesirable.
- The fourth parameter centers the image in the background.
- The final parameter is the BackgroundSize object created before.
- Finally, we designate the BackgroundImage as the Pane's background.
import java.io.File; import java.net.MalformedURLException; import javafx.fxml.FXML; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundImage; import javafx.scene.layout.BackgroundPosition; import javafx.scene.layout.BackgroundRepeat; import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.Pane; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; public class DemoController { @FXML Pane pane; @FXML Button open; @FXML void initialize() { open.setOnAction(event -> imageOpener()); } void imageOpener() { File imgFile = openImage(); try { if (imgFile != null) { displayImage(imgFile); } } catch (MalformedURLException e) { showError(e); } } File openImage() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select an Image File"); fileChooser.getExtensionFilters().add(new ExtensionFilter( "Image Files", "*.png", "*.jpg", "*.gif")); return fileChooser.showOpenDialog(null); } void displayImage(File imgFile) throws MalformedURLException { Image background = new Image(imgFile.toURI().toURL().toString()); BackgroundSize size = new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, false, false, true, true); BackgroundImage bimg = new BackgroundImage(background, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, size); pane.setBackground(new Background(bimg)); } void showError(Exception e) { Alert info = new Alert(AlertType.ERROR); info.setTitle("Image not found"); info.setHeaderText("Image not found"); info.setContentText(e.getMessage()); info.showAndWait(); } }
Dragging objects with a mouse in JavaFX
Dragging objects is pretty straightforward in JavaFX as long as some key concepts are kept straight. If we go with the strategy of attaching the event handler to the object to be dragged, then the very first time that the onMouseDragged event is invoked, the (x,y) coordinates given by the event parameter will be the location on the object that was clicked.
If we then move the object, on subsequent invocations of the event as a result of moving the mouse, the (x,y) coordinates will be the relative distance between the mouse position at the time of the preceding event and the current mouse position. This works because the new position of the object is determined by the previous mouse location.
If we then move the object, on subsequent invocations of the event as a result of moving the mouse, the (x,y) coordinates will be the relative distance between the mouse position at the time of the preceding event and the current mouse position. This works because the new position of the object is determined by the previous mouse location.
In either case, it works pretty well to use the (x,y) coordinate of the mouse as an offset by which to translate the object. The code below demonstrates the concept nicely, provided that a suitable GUI has been created using SceneBuilder and a suitable main() has also been created. The drag() method is set up to work properly with a draggable Node of any subtype.
import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.shape.Circle; public class DemoController { @FXML Pane pane; @FXML Circle ball1, ball2; @FXML void initialize() { ball1.setOnMouseDragged(event -> drag(event)); ball2.setOnMouseDragged(event -> drag(event)); } public void drag(MouseEvent event) { Node n = (Node)event.getSource(); n.setTranslateX(n.getTranslateX() + event.getX()); n.setTranslateY(n.getTranslateY() + event.getY()); } }
Tuesday, September 8, 2015
Be careful with operator precedence
I recently encountered the following bug while programming in Java. I've taken it out of context and created the program below for illustrative purposes.
The goal is to add two
When this program is executed,
I can't believe that after 13 years as an active Java programmer (with 11 years before that as an active C++ programmer) I made so elementary a mistake, but I'm posting it here in hopes it will be a useful warning to others. For whatever reason, I assumed that bitwise-and had a higher precedence than addition, perhaps because it is a traditionally "multiplicative" operator. As it turns out, its precedence is actually much lower.
A further observation is that this is not what I thought the bug was originally. At first, I thought I had somehow messed up the casting. It was only after I rechecked it carefully that I realized I was killing the higher-order bits with the bitwise and.
The goal is to add two
byte
values while avoiding overflow. To this end, we cast them to integers, and we use bit-masking to avoid sign extension when casting. The unsuccessful version is highlighted in red, while the successful version is green:public class ConvertBytes { public static void main(String[] args) { byte a = 127; byte b = 126; int bad = ((int)a) & 0xff + ((int)b) & 0xff; System.out.println(bad); int good = ((int)a & 0xff) + ((int)b & 0xff); System.out.println(good); } }
When this program is executed,
bad
is 125 and good
is 253. I can't believe that after 13 years as an active Java programmer (with 11 years before that as an active C++ programmer) I made so elementary a mistake, but I'm posting it here in hopes it will be a useful warning to others. For whatever reason, I assumed that bitwise-and had a higher precedence than addition, perhaps because it is a traditionally "multiplicative" operator. As it turns out, its precedence is actually much lower.
A further observation is that this is not what I thought the bug was originally. At first, I thought I had somehow messed up the casting. It was only after I rechecked it carefully that I realized I was killing the higher-order bits with the bitwise and.
Thursday, May 28, 2015
Handling Keyboard Events in JavaFX
When using JavaFX, SceneBuilder is not terribly helpful when setting up keyboard events. This is because SceneBuilder's event handlers can't take any parameters. Consequently, there is no way to determine which key triggered the event.
I've made an amusing little program that has four triangles on the GUI, one for each arrow key. When an arrow key is pressed, the corresponding triangle changes color. When it is released, it switches back to the original color. Here is the source code for its controller; constructing the rest of the GUI is an exercise for the reader.
In the
Having set up the handlers, I use a
I've made an amusing little program that has four triangles on the GUI, one for each arrow key. When an arrow key is pressed, the corresponding triangle changes color. When it is released, it switches back to the original color. Here is the source code for its controller; constructing the rest of the GUI is an exercise for the reader.
package application; import java.util.Map; import java.util.TreeMap; import javafx.fxml.FXML; import javafx.scene.input.KeyCode; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import javafx.scene.shape.Polygon; public class ButtonPaneController { @FXML Polygon up; @FXML Polygon down; @FXML Polygon left; @FXML Polygon right; @FXML AnchorPane pane; Color pressColor = Color.GOLD; Color releaseColor = Color.DODGERBLUE; Map<KeyCode,Polygon> key2poly; @FXML void initialize() { pane.setOnKeyPressed(key -> recolor(key.getCode(), pressColor)); pane.setOnKeyReleased(key -> recolor(key.getCode(), releaseColor)); pane.setOnMouseEntered(m -> pane.requestFocus()); key2poly = new TreeMap<>(); key2poly.put(KeyCode.LEFT, left); key2poly.put(KeyCode.RIGHT, right); key2poly.put(KeyCode.UP, up); key2poly.put(KeyCode.DOWN, down); } void recolor(KeyCode code, Color newColor) { if (key2poly.containsKey(code)) { key2poly.get(code).setFill(newColor); } pane.requestFocus(); } }
In the
initialize()
method, we set up our event handlers using lambdas. Whenever a key is pressed, recolor()
is called with the key code and the target color upon a press. Similarly, whenever a key is released, the same method call is made, but with a different target color. Finally, we request the focus whenever the mouse enters the pane. This could easily have been set up in SceneBuilder, but I did it in code here for consistency. Having set up the handlers, I use a
Map
to simplify the implementation. By mapping each key to the corresponding GUI element that is to have its color changed, I avoid a bothersome set of if-else
clauses in the recolor()
method. A key-based event handler with more substantive algorithmic content could use a Map
in a similar way where the values are lambda expressions.
Monday, May 18, 2015
Using the leJOS-0.9 video library
The following program illustrates how one might use the LeJOS-0.9 webcam library in conjunction with the lambda feature from Java 8:
To allow flexibility of implementation, the
We have a private data member belonging to the
Now, when we invoke the constructor, we can write a lambda expression to describe the desired computation. For this example, we just display the image. Note that the LCD screen is referenced from outside the scope of the lambda. This still works after it has gone out of scope, as Java 8's lambda expressions are closures. You can write code to do arbitrary things when seeing an image, just by calling the
I plan to write a more elaborate example in a future blog post, but this should suffice to demonstrate the concept. Happy coding!
package edu.hendrix.demo; import java.io.IOException; import java.util.function.Consumer; import lejos.hardware.BrickFinder; import lejos.hardware.Button; import lejos.hardware.lcd.GraphicsLCD; import lejos.hardware.video.Video; import lejos.hardware.video.YUYVImage; public class FilteredCameraDemo { private Consumer<YUYVimage> imgConsumer; public FilteredCameraDemo(Consumer<YUYVimage> filterer) { imgConsumer = filterer; } public void run() { try { Video wc = BrickFinder.getDefault().getVideo(); wc.open(160,120); byte[] frame = wc.createFrame(); YUYVImage img = new YUYVImage(frame, wc.getWidth(), wc.getHeight()); while (!Button.ESCAPE.isDown()) { wc.grabFrame(frame); imgConsumer.accept(img); } wc.close(); } catch (IOException ioe) { ioe.printStackTrace(); System.out.println("Driver exception: " + ioe.getMessage()); } } public static void main(String[] args) { GraphicsLCD g = BrickFinder.getDefault().getGraphicsLCD(); new FilteredCameraDemo (img -> img.display(g, 0, 0, img.getMeanY())).run(); } }
To allow flexibility of implementation, the
YUYVImage
class is essentially a wrapper around a byte array. So every time we grab a frame from the webcam, we dump the bytes into a byte array that, in turn, we access via a YUYVImage
that maintains a reference to it. This YUYVImage
class has methods that decode the byte layout according to the YUYV standard. We have a private data member belonging to the
Consumer
interface. Consumer
objects have an accept()
method that takes a parameter (corresponding to the parameterized type) and does not return anything. We make use of this within the run()
method's while loop. In this case, the type parameter is a YUYVImage
object. Now, when we invoke the constructor, we can write a lambda expression to describe the desired computation. For this example, we just display the image. Note that the LCD screen is referenced from outside the scope of the lambda. This still works after it has gone out of scope, as Java 8's lambda expressions are closures. You can write code to do arbitrary things when seeing an image, just by calling the
FilteredCameraDemo
constructor with a different lambda parameter.I plan to write a more elaborate example in a future blog post, but this should suffice to demonstrate the concept. Happy coding!
Setting up leJOS-0.9 with Java 8
The premier implementation of Java for the various Lego Mindstorms robots is LeJOS. For the Mindstorms EV3, Oracle has facilitated the LeJOS implementation by providing pre-bundled Java microeditions. Java version 7 update 60 is ready-to-go as part of the leJOS setup. But the Oracle download page includes the following cryptic message regarding the use of Java 8:
Having created the Java 8 configuration file
The English-language summary of the above command sequence is as follows:
Java SE Embedded 8 enables developers to create customized JREs using the JRECreate tool. Starting with Java SE Embedded 8, individual JRE downloads for embedded platforms are no longer provided. To get started, download the bundle below and follow instructions to create a JRE that suits your application's needs.Of course, for a leJOS developer who would like to use Java 8, this is not entirely helpful. Following some hints on the LeJOS EV3 forums, I was able to figure out how to do this. This sequence of shell commands should work on any Unix/Linux platform. (I am using a Mac.) I would imagine it would also work on Windows if gzip and tar programs are installed.
gunzip ejdk-8-fcs-b132-linux-arm-sflt-03_mar_2014.gz tar xvf ejdk-8-fcs-b132-linux-arm-sflt-03_mar_2014 cd ejdk1.8.0/bin export JAVA_HOME=/usr ./jrecreate.sh --dest ../../ejre-8u1-linux-arm-15_may_2015 --profile compact2 --vm client
cd ../.. tar cvf ejre-8u1-linux-arm-15_may_2015.tar ejre-8u1-linux-arm-15_may_2015 gzip ejre-8u1-linux-arm-15_may_2015.tar
Having created the Java 8 configuration file
ejre-8u1-linux-arm-15_may_2015.tar.gz
, simply specify it when creating the SD card, and everything should work fine.The English-language summary of the above command sequence is as follows:
- Uncompress the archive using
gunzip
. - Extract the contents of the archive using
tar
. - Go to the subdirectory containing
jrecreate
. - On Windows, you would want to use
jrecreate.bat
rather thanjrecreate.sh
. - Set up the
JAVA_HOME
environment variable if it is not already set. - Run
jrecreate
with the following options: --dest
specifies where the configured implementation will be placed.--profile
needs to be compact2 to ensure certain features LeJOS requires are present.--vm
needs to be client for the same basic reason.- Use
tar
to repack the archive. - Use
gzip
to compress the archive. - I made up a filename that seems to be compatible with what LeJOS will be looking for.
Subscribe to:
Posts (Atom)