Pages

Friday, October 22, 2010

javaFx custom component– Line segment with arrowhead

I have been working with javaFx for some time now for projects in School during my M.S tenure. I love the ease with which I can create new components. The only complain I have had till now is that it is not fast enough to handle really fast animations. It was very patchy when I worked with it in a project with multiple images/lines/rectangles on stage changing at rate of 200ms. It simply could not handle it. The Timeline class itself takes a moment in time hindering animations. Just a claim about Timeline with no proofs. Coming back to the problem, a line segment with arrowhead. A very trivial feature missing in javaFx and many people searching for a solution. Here is a custom component which does that exactly the same using CustomNode, Line and a Polygon.

import javafx.scene.shape.Line;
import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.Group;
import javafx.scene.shape.Polygon;
import javafx.scene.paint.Paint;
import javafx.scene.transform.Transform;
import java.lang.Math;

/**
* @author Shreyas Purohit
*/

public class ArrowHeadLine extends CustomNode{
var group:Group;
public var x:Number;
public var y:Number;

public var startX:Number on replace{
handleChange();
};
public var startY:Number on replace{
handleChange();
};
public var endX:Number on replace{
handleChange();
};
public var endY:Number on replace{
handleChange();
};
public var fill:Paint;
public var stroke:Paint;

var arrowhead:Polygon;
var segment:Line;

override protected function create () : Node {
group = Group{
translateX: bind x;
translateY: bind y;
content:[
segment = Line{
startX: bind startX
startY: bind startY
endX: bind endX
endY: bind endY
stroke: bind stroke;
},
arrowhead = Polygon{
points :[-6,-6,
0,0,
-6,6];
fill: bind fill;
}
]
}

}

function handleChange():Void{
var angle:Number = Math.toDegrees(Math.atan2(endY-startY, endX-startX));
arrowhead.translateX = endX;
arrowhead.translateY = endY;
arrowhead.transforms = [Transform.rotate(angle,0,0)]
}

}

Monday, October 18, 2010

Betwixt and List/ArrayList

I just can not imagine wasting 3-4 hours to just make Betwixt from Apache (http://commons.apache.org/betwixt/index.html) work with List and ArrayList in my java beans. Betwixt simply does not work with List/ArrayList when converting from XML to java bean unless I stop using List/ArrayList completely and replace them with arrays or the solution that I used – create a .betwixt file to configure its use. If you adopt the first option then I found out that if I have 2 array types defined in my class then a weird ClassCastException to java.util.List was being encountered. I finally successfully used List/ArrayList in my class.

Steps

1. Define the class say, Info – java bean – to be serialized to XML and back using List/ArrayList as shown. Note the last two methods – addRouteInfo and addEntityInfo. These are the key methods to be added to your java bean.

package com.ssb.repo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Shreyas P
*
*/
public class Info {
private String siteId;
private String applicationId;
private List<RouteInfo> routeInfos = new ArrayList<RouteInfo>();
private List<EntityInfo> entityInfos = new ArrayList<EntityInfo>();

public Info(){

}

/**
* @return the siteId
*/
public String getSiteId() {
return siteId;
}
/**
* @param siteId the siteId to set
*/
public void setSiteId(String siteId) {
this.siteId = siteId;
}
/**
* @return the applicationId
*/
public String getApplicationId() {
return applicationId;
}
/**
* @param applicationId the applicationId to set
*/
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
/**
* @return the entityInfos
*/
public List<EntityInfo> getEntityInfos() {
return entityInfos;
}
/**
* @param entityInfos the entityInfos to set
*/
public void setEntityInfos(List<EntityInfo> entityInfos) {
this.entityInfos = entityInfos;
}
/**
* @return the routeInfos
*/
public List<RouteInfo> getRouteInfos() {
return routeInfos;
}

/**
* @param routeInfos the routeInfos to set
*/
public void setRouteInfos(List<RouteInfo> routes) {
this.routeInfos = routes;
}

public void addRouteInfo(RouteInfo routeInfo) {
routeInfos.add(routeInfo);
}

public void addEntityInfo(EntityInfo entityInfo) {
entityInfos.add(entityInfo);
}
}

2. Create .betwixt file naming <classname>.betwixt. In our case, Info.betwixt. Betwixt automatically uses this file and need not be supplied as an argument to any function. Just place the file along with your class file. Note the ‘updates’ attribute, they link to the method to be used to add to the list/arraylist. Do not forget to put ‘addDefaults’ element which is responsible for generating all the remaining attributes.

<?xml version='1.0'?>
<info>
<element name='Info'>
<element name='routeInfos'>
<element name='routeInfo' property='routeInfos' updater='addRouteInfo'/>
</element>
<element name='entityInfos'>
<element name='entityInfo' property='entityInfos' updater='addEntityInfo'/>
</element>
<addDefaults/>
</element>
</info>

3. Create .betwixt files for any class using list/arraylist as there attributes.


That’s all folks. Hopefully, this will save some time and effort to many. I had to dig through Betwixt testcases to come up with this along with the documents on there website.