Wednesday, 6 April 2016

3D Rotating Cube in Perspective Projection

I have been playing with SVG.  Starting with the code from here: Rotating 3D SVG Cube, I have add perspective projection.

How about this?




Here's the code:
<svg width='500' height='500'
     xmlns='http://www.w3.org/2000/svg'
     xmlns:xlink='http://www.w3.org/1999/xlink'
     onload='init(evt)' >

  <!-- Starting point was
       http://www.petercollingridge.co.uk/blog/rotating-3d-svg-cube
  -->

  <style>
    .surface{
        stroke-width: 0;
        stroke: black;
        stroke-width: 2;
    }
    .hidden-edge{
        stroke: black;
        stroke-width: .5;
        stroke-dasharray:3,3;
    }
    .button{
        fill: #2060dd;
        stroke: #2580ff;
        stroke-width: 1;
    }
    .button:hover{
        stroke-width: 3;
    }
  </style>

  <script type='text/ecmascript'>
    <![CDATA[
    // TODO: stop using globals and create object, for >1 diagram per page.

    // Distance along z axis from viewer (and origin) to plane that image is
    // projected onto.
    focal_length = 2.0;

    // Centre of rotation
    //cofr = {x:0.0, y:0.0, z:3.5};
    cofr = {x:0.2, y:0.2, z:3.3};

    function init(evt)
    {
        if ( window.svgDocument == null )
        {
            svgDocument = evt.target.ownerDocument;
            init_model();
            init_edge_elements();
        }
        drawModel();
    }
   
    function init_edge_elements()
    {
        edge_elements = new Array(edges.length);
        for(var i=0; i<edges.length; i++)
        {
            edge_elements[i] = svgDocument.getElementById('edge-'+i);
        }
    }

    function init_model()
    {
        var left_x = -0.5;
        var right_x = 0.5;
        var top_y = -0.5;
        var bottom_y = 0.5;
        var front_z = focal_length + 1.0;
        var back_z = focal_length + 2.0;
   
        var tlf = {y:top_y,    x:left_x,  z:front_z};
        var trf = {y:top_y,    x:right_x, z:front_z};
        var brf = {y:bottom_y, x:right_x, z:front_z};
        var blf = {y:bottom_y, x:left_x,  z:front_z};

        var tlb = {y:top_y,    x:left_x,  z:back_z};
        var trb = {y:top_y,    x:right_x, z:back_z};
        var brb = {y:bottom_y, x:right_x, z:back_z};
        var blb = {y:bottom_y, x:left_x,  z:back_z};
   
        coords = [tlf, trf, brf, blf, tlb, trb, brb, blb];
   
        // Surfaces defined clockwise when view from outside.
        var front  = {points:[tlf, trf, brf, blf], colour:'red'};
        var back   = {points:[tlb, blb, brb, trb], colour:'green'};
        var top    = {points:[tlb, trb, trf, tlf], colour:'pink'};
        var bottom = {points:[blb, blf, brf, brb], colour:'yellow'};
        var left   = {points:[tlb, tlf, blf, blb], colour:'purple'};
        var right  = {points:[trb, brb, brf, trf], colour:'orange'};

        surfaces = [front, back, top, bottom, left, right];

        edges = [
            {p1:tlf, p2:trf},
            {p1:trf, p2:brf},
            {p1:brf, p2:blf},
            {p1:blf, p2:tlf},
   
            {p1:tlb, p2:trb},
            {p1:trb, p2:brb},
            {p1:brb, p2:blb},
            {p1:blb, p2:tlb},

            {p1:tlf, p2:tlb},
            {p1:trf, p2:trb},
            {p1:brf, p2:brb},
            {p1:blf, p2:blb},
        ];
    }

    function drawModel()
    {
        // Project all points onto the view port with perspective.
        // http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MOHR_TRIGGS/node9.html
        // http://www.cse.unr.edu/~bebis/CS791E/Notes/PerspectiveProjection.pdf
        //
        for(var i=0; i<coords.length; i++)
        {
            var p = coords[i];
            p.vpx = focal_length * p.x / p.z * 250 + 200;
            p.vpy = focal_length * p.y / p.z * 250 + 200;
        }

        // Rough sort of surfaces from back to front.
        // Using z value midway between opposite corners.
        var z_order = new Array(surfaces.length);
        for(var i=0; i<surfaces.length; i++)
        {
            var s = surfaces[i];
            var z1 = s.points[0].z;
            var z2 = s.points[2].z;
            s.mid_z = (z1 > z2) ? z2+(z1-z2)/2 : z1+(z2-z1)/2;
            z_order[i] = s;
        }
        z_order.sort(function (a, b){return b.mid_z-a.mid_z;});

        // Fill surfaces from back to front, with hard outline.
        // This only really works for simple convex solids like a cube.
        // And then only if your rotation point isn't too far from the centre
        // of the cube.
        for(var i=0; i<z_order.length; i++)
        {
            var s = z_order[i];
            var attr = '' + s.points[0].vpx+','+s.points[0].vpy+' '+
                            s.points[1].vpx+','+s.points[1].vpy+' '+
                            s.points[2].vpx+','+s.points[2].vpy+' '+
                            s.points[3].vpx+','+s.points[3].vpy;

            surfaceElement = svgDocument.getElementById('surface-'+i);
            surfaceElement.setAttributeNS(null, 'points', attr);
            surfaceElement.setAttributeNS(null, 'style', 'fill:'+s.colour);
        }

        // Fill in lines as if hidden (big trick).
        for(var i=0; i<edges.length; i++)
        {
            var edge = edge_elements[i];

            var p1 = edges[i].p1;
            edge.setAttributeNS(null, 'x1', p1.vpx);
            edge.setAttributeNS(null, 'y1', p1.vpy);

            var p2 = edges[i].p2;
            edge.setAttributeNS(null, 'x2', p2.vpx);
            edge.setAttributeNS(null, 'y2', p2.vpy);
        }
    }
   
    function rotateAboutX(radians)
    {
        for(var i=0; i<coords.length; i++)
        {
            y = coords[i].y - cofr.y;
            z = coords[i].z - cofr.z;
            d = Math.sqrt(y*y + z*z);
            theta  = Math.atan2(y, z) + radians;
            coords[i].y = cofr.y + d * Math.sin(theta);
            coords[i].z = cofr.z + d * Math.cos(theta);
        }
        drawModel();
    }
   
    function rotateAboutY(radians)
    {
        for(var i=0; i<coords.length; i++)
        {
            x = coords[i].x - cofr.x;
            z = coords[i].z - cofr.z;
            d = Math.sqrt(x*x + z*z);
            theta  = Math.atan2(x, z) + radians;
            coords[i].x = cofr.x + d * Math.sin(theta);
            coords[i].z = cofr.z + d * Math.cos(theta);
        }
        drawModel();
    }

    function rotateAboutZ(radians)
    {
        for(var i=0; i<coords.length; i++)
        {
            x = coords[i].x - cofr.x;
            y = coords[i].y - cofr.y;
            d = Math.sqrt(x*x + y*y);
            theta  = Math.atan2(x, y) + radians;
            coords[i].x = cofr.x + d * Math.sin(theta);
            coords[i].y = cofr.y + d * Math.cos(theta);
        }
        drawModel();
    }
   
    function beginRotateX(radians)
    {
        rotateAboutX(radians);
        rotateX_timeout = setInterval("rotateAboutX(" + radians + ")", 20);
    }
   
    function endRotateX()
    {
        if (typeof(rotateX_timeout) != "undefined")
        {
            clearTimeout(rotateX_timeout);
        }
    }
   
    function beginRotateY(radians)
    {
        rotateAboutY(radians);
        rotateY_timeout = setInterval("rotateAboutY(" + radians + ")", 20);
    }
   
    function endRotateY()
    {
        if (typeof(rotateY_timeout) != "undefined")
        {
            clearTimeout(rotateY_timeout);
        }
    }

    function beginRotateZ(radians)
    {
        rotateAboutZ(radians);
        rotateZ_timeout = setInterval("rotateAboutZ(" + radians + ")", 20);
    }
   
    function endRotateZ()
    {
        if (typeof(rotateZ_timeout) != "undefined")
        {
            clearTimeout(rotateZ_timeout);
        }
    }
   
    ]]>
  </script>

    <polygon id='surface-0' points='0,0 1,0 1,1 0,1' class='surface' style='' />
    <polygon id='surface-1' points='0,0 1,0 1,1 0,1' class='surface' style='' />
    <polygon id='surface-2' points='0,0 1,0 1,1 0,1' class='surface' style='' />
    <polygon id='surface-3' points='0,0 1,0 1,1 0,1' class='surface' style='' />
    <polygon id='surface-4' points='0,0 1,0 1,1 0,1' class='surface' style='' />
    <polygon id='surface-5' points='0,0 1,0 1,1 0,1' class='surface' style='' />

    <line id='edge-0' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-1' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-2' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-3' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-4' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-5' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-6' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-7' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-8' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-9' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-10' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
    <line id='edge-11' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>

    <path class="button"
          d="m50.5 470.5 15 -15 0 8 45 0 0 14 -45 0 0 8 z"
          onmousedown='beginRotateY(0.08)'
          onmouseout='endRotateY()'
          onmouseup='endRotateY()'/>
         
    <path class="button"
          d="m190.5 470.5 -15 -15 0 8 -45 0 0 14 45 0 0 8 z"
          onmousedown='beginRotateY(-0.08)'
          onmouseout='endRotateY()'
          onmouseup='endRotateY()'/>
         
    <path class="button"
          d="m470.5 50.5 15 15 -8 0 0 45 -14 0 0 -45 -8 0 z"
          onmousedown='beginRotateX(0.08)'
          onmouseout='endRotateX()'
          onmouseup='endRotateX()'/>
         
    <path class="button"
          d="m470.5 190.5 15 -15 -8 0 0 -45 -14 0 0 45 -8 0 z"
          onmousedown='beginRotateX(-0.08)'
          onmouseout='endRotateX()'
          onmouseup='endRotateX()'/>
   
    <path class="button"
          d="m470.5 250.5 15 15 -8 0 0 45 -14 0 0 -45 -8 0 z"
          onmousedown='beginRotateZ(0.08)'
          onmouseout='endRotateZ()'
          onmouseup='endRotateZ()'/>
         
    <path class="button"
          d="m470.5 390.5 15 -15 -8 0 0 -45 -14 0 0 45 -8 0 z"
          onmousedown='beginRotateZ(-0.08)'
          onmouseout='endRotateZ()'
          onmouseup='endRotateZ()'/>
   
</svg>

Wednesday, 13 May 2015

Maven and Me

I've had it with Maven.  I've been using it for years and it just doesn't seem to get any better.  Faster, perhaps, but understandable - not.  It works out-of-the-box for small single jar or war projects.  However, to me there seems to be no rhyme or reason why certain combinations of config work and others don't.  Generally, if you get something working (however inefficiently), it is best to leave it alone.  Going beyond the narrow functionality that came out-of-the-box, is likely to waste you a lot of time and not achieve what you needed anyway.

Unfortunately using Maven to build multiple projects is really beyond that out-of-the-box functionality.

The perennial problem is shared plugin configuration. With maven you have two options:
  • Repeat everything everywhere.
  • Configure a plugin in a parent pom and inherit that configuration throughout all your projects.

The chances are that not all your projects are the same.  Tough.  If you want to use a plugin with some shared config in 2 places, but another shared config in a third and fourth, then you are going to be unlucky.  You are going to spend a lot of time on Stackoverflow, getting short shift from people who (at best) will tell that you shouldn't be trying to do that.  The best you can do is share one set of config in some places, and not use the plugin at all anywhere else.

There are some tricks.
  • You can bind the other set of plugin config to a different phase using executions.  This depends upon there being a different phase that you can use - this is very limited and your project will probably be too big.  It would have been nice if you could specify the id of the execution you want to use.  (I don't understand what else the id could be for, but apparently it's not for this. Some say it does do this, but then some people like to waste your time.  Also ignore people who suggest using profiles - profiles are applied equally to all projects).
  • Local project properties. If you have just one or two differences in a larger set of config, then you can make the config reference a property and set those properties locally in all the projects that you use the plugin.  (It is a shame that you can't trigger a different profile using a local property).

Parent POMS

Maven also has a problem with what is means by a parent POM (which it inherits config from) and a super/multi-module/aggregator POM.  Some of the documentation tries to draw a distinction, but then shows examples where the two are lumped together in one file.  This is odd, as the parent must be built first and put into your repo before all the projects that use it.  A multi-module POM is put into the repo last.  If you use a single POM for both, and clear out of your repo, or checkout and build a single project, then you get a cyclic dependency.  You can get around it by "fixing" your parent/multi-module POM to remove references to the sub-projects, "unfixing" it once it is in the repo, then re-building.

Obviously you should use two different POMs, but it is not clear how this should be done.  I even think the Maven developers aren't clear on this.  Those developers that use Eclipse have always put the parent POM in a sibling project (because in the past Eclipse didn't like nested projects).  If we do the same, should we then name this parent project in the list of modules?  In Maven 3 the enforcer plugin's Reactor Module Convergence rule complains if the parent is not within the list of modules, but if you put it in it then complains that there is POM without a parent (i.e. the parent POM itself).


When Ant first came out, I was frustrated at how verbose and limited it was compared to Make.  I take it all back.

Sunday, 15 March 2015

Capturing Screenshots from the Command Line using Java

A few weeks ago I spent most of a day trying to work out how to capture a screen shot from the command line on Windows.  MS don't provide anything to do this.  You can manually [Prtscr], open Paint, Ctrl-V, Save As, but you can't automate it.  And you can't seem to automate the SnippingTool - or even run it from a script.  There are tools you can download from dodgy websites, but I don't want to do that.

It was a bit annoying to realize that I should have started with Java.  The follow captures all attached screens with JDK 1.7:
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

public class Snapshot {

    public static void main(String[] args) throws Exception {
        Robot robot = new Robot();
        GraphicsEnvironment ge =
                GraphicsEnvironment.getLocalGraphicsEnvironment();
        int screenNumber = 1;
        for(GraphicsDevice gd : ge.getScreenDevices()){
            DisplayMode dm = gd.getDisplayMode();
            Rectangle rect = new Rectangle(dm.getWidth(),
                                              dm.getHeight());

            BufferedImage image = robot.createScreenCapture(rect);
            File f = new File("screen" + screenNumber + ".png");
            ImageIO.write(image, "PNG", f);
        }
    }
}

Using Maven and Eclipse with Native Shared Libraries

Looking around the web I found that Maven can manage native shared libraries to an extent. It can allow you to package up .dll or .so files as dependencies and put them in jars in your repo. 

However, to be useful shared libraries have to be unpacked to disk and have their original name, before they can be loaded when your java program or tests run. Some clever people have ignored the maven "dll" classifier and the "so" type, and written code to unpack their libraries from the applications own jars into temporary directories on disk on start up.

I have yet to see a way to consistently arrange for shared libraries to be unpacked when my code is deployed in different target environments.  I might want to use it from within a WAR deployed into a Tomcat server running in Linux within the cloud.

When you come to deploy on unix like systems, the platform will have its own system for managing natives, and trying to do this in your maven pom files will be a wasted effort.

So my approach is to assume that the shared natives will be available in the target environment, and I will arrange for them to be there separately.  I will also do the same for my maven build, and provide an environment variable to tell the build where to find them.  Maven will also set up my Eclipse workspace.

I am using OpenCV 3.0.0-beta which has a number of shared native libraries and a JNI wrapper jar to allow access to them.  If you don't have a JNI jar, then you may need to create an empty one.

In my project, I have a parent pom and a sub-project that wraps up my dependency on OpenCV.  The parent pom provides a check that the required set-up has been done, and sub-projects will use this as appropriate.  If the required environment variable is missing - it outputs a message telling the user what to do.
<project ...>

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yourcompany.product</groupId>
  <artifactId>parent</artifactId>
  <packaging>pom</packaging>
  <version>${product-version}</version>

  <modules>
    <module>opencv</module>
    ...
  </modules>

  <properties>
    <product-version>0.1-SNAPSHOT</product-version>
    ...
    <opencv-version>3.0.0-beta</opencv-version>
    <opencv-libraries>${env.OPENCV_LIBRARIES}</opencv-libraries>
    <eclipse-opencv-libname>OPENCV-3.0.0-BETA</eclipse-opencv-libname>
  </properties>

  <build>

    <plugins>
      ...
    </plugins>

    <pluginManagement>
      ...
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-enforcer-plugin</artifactId>
          <executions>
            <execution>
              <id>check-opencv-libraries</id>
              <goals>
                <goal>enforce</goal>
              </goals>
              <configuration>
                <rules>
                  <requireEnvironmentVariable>
                    <variableName>OPENCV_LIBRARIES</variableName>
                    <message><![CDATA[
OPENCV_LIBRARIES
================
The OPENCV_LIBRARIES environment variable needs to be set to the directory
containing the OpenCV dynamic shared libraries required for your platform.

You can download these from http://opencv.org/downloads.html.
These may need to be built for some platforms.

For example on 64bit Windows you might create the directory:
  C:\tools\opencv\3.0.0-beta\x64
and copy the following DLL files from the distribution to it:
  opencv\build\java\x64\opencv_java300.dll
  opencv\build\x64\vc12\bin\opencv_world300.dll
  opencv\build\x64\vc12\bin\opencv_ffmpeg300_64.dll

On Debian Linux you may install the libraries with:
  sudo apt-get install opencv
and set OPENCV_LIBRARIES to /usr/shared/lib or wherever.

OpenCV JNI jar
==============
The prebuilt OpenCV JNI jar for these dynamic libraries can be found in the
Windows distribution and put into your local maven repository with the command:

  mvn install:install-file -Dfile=opencv/build/java/opencv-300.jar \
     -DgroupId=org.opencv -DartifactId=opencv-java-api  \
     -Dversion=3.0.0-beta -Dpackaging=jar

Eclipse Setup
=============
Go to Window -> Preferences -> Java -> Build Path -> User Libraries
Create a new library called ${eclipse-opencv-libname}
Add the external jar from your local repo (opencv-java-api-3.0.0-beta.jar).
Expand the library and jar, highlight "Native library location", then "Edit"
and set it to the location of the OPENCV_LIBRARIES directory.
]]>
                    </message>
                  </requireEnvironmentVariable>
                </rules>
              </configuration>
            </execution>
          </executions>
        </plugin>

        <!-- Makes the natives available to unit tests -->
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <configuration>
            <argLine>-Djava.library.path=${opencv-libraries}</argLine>
          </configuration>
        </plugin>

    </pluginManagement>
  </build>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.yourcompany.product</groupId>
        <artifactId>opencv</artifactId>
        <version>${product-version}</version>
      </dependency>

      <!-- See note above concerning OPENCV_LIBRARIES -->
      <dependency>
        <groupId>org.opencv</groupId>
        <artifactId>opencv-java-api</artifactId>
        <version>${opencv-version}</version>
      </dependency>

      ...
    </dependencies>
  </dependencyManagement>

</project>
Then in the opencv sub-project you need to use the rule enforcer, and supplement the Eclipse classpath generated by "mvn eclipse:eclipse".
<project ...>

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.yourcompany.product</groupId>
    <artifactId>parent</artifactId>
    <version>${product-version}</version>
  </parent>

  <artifactId>opencv</artifactId>
  <packaging>jar</packaging>

  <build>
    <plugins>
      <!-- Enforce parent rules about libraries being available -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-eclipse-plugin</artifactId>
        <configuration>
          <!--
             In eclipse we use the opencv JNI jar via a user library,
             which will include the native shared libraries.
          -->
          <excludes>
            <exclude>org.opencv:opencv-java-api</exclude>
          </excludes>
          <classpathContainers>
            <classpathContainer>
              org.eclipse.jdt.launching.JRE_CONTAINER
            </classpathContainer>
            <classpathContainer>
              org.eclipse.jdt.USER_LIBRARY/${eclipse-opencv-libname}
            </classpathContainer>
          </classpathContainers>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.opencv</groupId>
      <artifactId>opencv-java-api</artifactId>
    </dependency>
    ...
  </dependencies>

</project>
Now when you build the sub-project, it will fail and give you instructions on how to set up the native libraries.  You should be able to follow these instructions yourself to do the one-off install of the native shared libraries and Eclipse workspace set up.

Monday, 24 November 2014

Using Code::Blocks as your Rust IDE on Windows

Warnings:
  • Code::Blocks does not support Rust formatting, code completion or syntax colouring - or at least not yet.
  • I haven't yet done much development with Rust, or Code::Blocks, so your mileage may be disappointing.  I'm just happy to be able to edit, build and debug with it.
  • You will need a certain level of understanding of configuring Windows, as I have skipped such things as setting environment variables.

 

Installing Rust

Download and install Rust from http://www.rust-lang.org/install.html.  I am using the 32 bit windows version (rust-nightly-i686-pc-windows-gnu.exe), because it matches the version of Code Blocks I want to use.

During the install uncheck all the plugins - they add a lot of menus, tabs and options that just confuse things.  Do check the box that adds Code::Blocks to your PATH.

Don't try to use it yet.  For Windows you still need MinGW version of the linker that comes with Code::Blocks.

 

Installing Code::Blocks

Download and install Code::Blocks from http://www.codeblocks.org/downloads/binaries. I am using codeblocks-13.12mingw-setup.exe

During the install check the box to add the MinGW bin directory to you PATH.  This will probably be "C:\Program Files (x86)\CodeBlocks\MinGW\bin".

Open a command prompt and type:
ld --version
You should see: "GNU ld (GNU Binutils)..."

Create a file called hello.rs that contains:
fn main() {
    println!("Hello world");
}
At the command prompt type
rustc hello.rs
If this works type
hello.exe
and it should print "Hello world".

If you have got this far, you have a working Rust compiler.

 

Installing Cargo

Next we really need Cargo (the Rust build tool).  We will use it to do all the compilation.  You could configure Code::Blocks to drive the compiler directly, but this way is simpler for now.

Go to https://crates.io/install, get the cargo-nightly-i686-pc-windows-gnu.tar.gz distribution and unpack the cargo.exe in it.  Normally you would expect Windows executable to be packed in a ZIP file, and this format might present you with problems.  However, this can be unpacked with 7-zip which you can install from http://www.7-zip.org. When you unpack the distribution, you will find another file to unpack, but eventually you find cargo.exe in a bin folder.

Put cargo.exe into a directory on your PATH.  I copied it into the Rust bin directory - but you will have to do so as an administrator.

Test cargo out in the command prompt by typing
cargo new hello_world --bin
This will create a new project directory.  Change to the new directory, build and run the result by typing:
cargo run

Configuring the project in Code::Blocks

Start up Code::Blocks.

Go to Settings -> Compler..., find "No Compiler" in the drop down, click Copy and name it "Rust Compiler".

On the Toolchain tab, select Debugger "GDB/CDB debugger", set the Make program to "cargo".

Click the button next to "C compiler" and find the rustc.exe within the installation.  We won't be using it but CodeBlocks gets upset if this is not set.

Go to File -> New -> Project -> Empty Project and Go

Under "Folder to create project in" find the directory we ran cargo in, and give the project the title hello_word.

On the next dialog, select the "Rust Compiler" compiler, and set the debug output dir to "target\".
Uncheck the Release config, and finish.

Right click the "hello_world" project and "add files ...", and select "Cargo.toml" and "src/main.rs".

Right click the "hello_world" project and "properties", set Makefile to "Cargo.toml" and check "This is a custom Makefile.  Then click "Project's build options...", on the "Make" commands tab, select "Debug" and set the clean project option to "$make clean" and the others to "$make build".  Then click "OK".

On the "Build targets" tab set the Type to "Console application", and "Output filename" to "target\hello_world.exe".  Then "OK".

Find and open main.rs. Right click on line 2  and add a break point.  Click the Debug button, the program should build, run and stop at line 2.

 

Syntax Colouring Fudge

We can now build and debug, but we don't have any syntax highlighting.  The underlying editor supports Rust, but Code::Blocks doesn't yet configure it.  So for now we will piggyback on the C syntax colouring.

Go to Setting -> Editor... -> Syntax highlighting -> C/C++ and Filemasks...and add "*.rs".

Click keywords and add "let" and "fn" (and any other keywords you can think of) to the first set.

The syntax colouring and formatting needs more work - this is just a fudge.  The underlying editor (Scintilla) has a Rust lexer but there is no config for it in "share/CodeBlocks/lexers". I presume that config could be derived from one already here, and giving it an index of 111.  See SciLexer.h in the Scintilla 3.51 sources. (also see: Creating a custom lexer for Code::Blocks editor and the Scintilla sources.)

Monday, 5 November 2012

Using Flazr as a Streaming Media Server

I'm no expert on streaming video, but I've been experimenting with Flazr on Windows.  Flazr can stream .flv and .f4v streams.  Here is my adventures with FLV.

I've used Tomcat and JWPlayer to create a web client to consume the flash video stream.  FFMpeg also gets used at various points.

Streaming Video-on-Demand (VOD)

  • Download Flazr from flazr.com, unzip into a folder and run server-start.bat.
  • Download a binary distribution of Tomcat from tomcat.apache.org, unzip into a folder and run bin\startup.bat.
  • Download JWPlayer from longtailvideo.com and unzip it.
  • Within the Tomcat's webapps\ROOT directory create a folder called streaming, and copy jwplayer.js, player.swf and preview.jpg from the JWPlayer bundle into it.
  • Copy a sample .flv file into the home\apps\vod folder.  Call it video.flv or change the following HTML.
    Example flv files are a bit hard to find so you may need to convert a .mp4 with FFMpeg.  Try converting the video.mp4 file that came with JWPlayer:
    ffmpeg -i video.mp4 -f flv video.flv
  • Create the following HTML file as webapps\ROOT\streaming\vodtest.html within the Tomcat directory.  (See the JWPlayer setup wizard for other configuration options)
<html>
  <head>
    <script type='text/javascript' src='jwplayer.js'>
    </script>

  </head>
  <body>
    <div id='mediaspace'>This text will be replaced</div>
    <script type='text/javascript'>
      jwplayer('mediaspace').setup({
        'flashplayer': 'player.swf',
        'image': 'preview.jpg',
        'file': 'video.flv',
        'streamer': 'rtmp://localhost/vod',
        'controlbar': 'over',
        'duration': '34',
        'fullscreen': 'true',
        'stretching': 'fill',
        'width': '470',
        'height': '320'
      });
    </script>
  </body>
</html>

Streaming a Live Video Stream

Here we use a client to stream a video to the Flazr server which it then relays to connected clients.

  • Create the following HTML file as webapps\ROOT\streaming\livetest.html within the Tomcat directory.
<html>
  <head>
    <script type='text/javascript' src='jwplayer.js'>
    </script>

  </head>
  <body>
    <div id='mediaspace'>This text will be replaced</div>
    <script type='text/javascript'>
      jwplayer('mediaspace').setup({
        'flashplayer': 'player.swf',
        'image': 'preview.jpg',
        'file': 'mystream',
        'streamer': 'rtmp://localhost/myapp',
        'controlbar': 'none',
        'autostart': 'true',
        'width': '470',
        'height': '320'
      });
    </script>
  </body>
</html>
  • Publish the FLV file as a stream to server using the Flazr client:
    client.bat -live -host localhost -app myapp mystream video.flv You can send it multiple times:
    client.bat -loop 100 -live -host localhost -app myapp mystream video.flv
  • Now browse to http://localhost:8080/streaming/livetest.html. You should see the stream playing.

You can use FFMpeg to do the same thing, and we need to use it in the next section:
    ffmpeg -i video.flv -re -f flv rtmp://localhost/myapp/mystream

For more information see the FFMpeg Streaming Guide.

Streaming a Webcam

This re-uses the stream we set up above.
  • List the devices on your machine using FFMpeg to find the name of your webcam and microphone:
    ffmpeg -f dshow -list_devices true -i dummy
  • List the options on your webcam device using its name - this will tell you what sizes and frame rates it supports:
    ffmpeg -y -f dshow -list_options true -i video="<webcam-name>"
  • Stream your webcam using FFMpeg in the following way:
    ffmpeg -y -loglevel warning -f dshow
      -i video="<webcam-name>":audio="<mic-name>"
      -r <frame-rate> -s <width>x<height> -threads 2
      -f flv rtmp://localhost/myapp/mystream
  • Browse to http://localhost:8080/streaming/livetest.html and you should see your webcam output.

For me the capture command is:
  ffmpeg -y -loglevel warning -f dshow -i
    video="HP HD Webcam [Fixed]":audio="Integrated Microphone Array (ID"
    -r 15 -s 320x240 -threads 2
    -f flv rtmp://localhost/myapp/mystream

For more info see: FFMpeg Guide to Capturing Webcam Input.

Things to bear in mind

  • Licenses -Flazr is LGPL.  JWPlayer has a number of conditions attached to its commercial use.  FFMpeg is mainly LGPL but some options are GPL.
  • No security implications has been looked at.  Without further work (firewalls etc) anyone could send and receive streams with your Flazr server.

Thursday, 27 September 2012

Outputting CDATA sections with JAXB

Here is a non-deprecated way to output a JAXB object model with some elements wrapped in CDATA sections.

Previously you could do this with an Apache OutputFormat as shown on the JAXB site.  This class which was non-standard, is now deprecated with a message about using the DOM Level 3 LSSerializer which is part of the Load and Save API.  I tried and tried, but I can not see a way to use that to output CDATA sections, so I have come up with a different way.

The secret is to marshal your JAXB object into a DOM and then use a null XSLT transformer.  Transformers allow you to name the elements you want wrapped in CDATA tags.

// Generating context uses resources - do it once and reuse across threads
JAXBContext jaxbContext = JAXBContext.newInstance(FWCoreContainer.class);

// Marshaller is not thread-safe
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

// If validating using a schema
SchemaFactory factory = SchemaFactory.newInstance(
    XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(xsd.getURL());
jaxbMarshaller.setSchema(schema);

// Create an empty DOM document
// DocumentBuilderFactory is not thread-safe
DocumentBuilderFactory docBuilderFactory =
    DocumentBuilderFactory.newInstance();
Document document =
    docBuilderFactory.newDocumentBuilder().newDocument();

// Marshall the feed object into the empty document.
jaxbMarshaller.marshal(jaxbObject, document);

// Transform the DOM to the output stream
// TransformerFactory is not thread-safe
TransformerFactory transformerFactory =
    TransformerFactory.newInstance();
Transformer nullTransformer = transformerFactory.newTransformer();
nullTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
nullTransformer.setOutputProperty(
    OutputKeys.CDATA_SECTION_ELEMENTS,
     "myElement myOtherElement");
nullTransformer.transform(new DOMSource(document),
     new StreamResult(writer/stream));