Javafx 2 chart zoom - zooming

When I do select a chart region my zoom get a "little switch". In example if do select from tick 3 up to tick 6 I obtain as lower bound:3.67 and upper bound 6.74.
How to fix it ?
Any help really appreciated.
package testjavafxzoom;
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
public class Zoom01 extends Application {
Path path;//Add path for freehand
BorderPane pane;
Rectangle rect;
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();
#Override
public void start(Stage stage) {
System.out.println("Java Version : " + com.sun.javafx.runtime.VersionInfo.getVersion());
System.out.println("Java getHudsonBuildNumber: " + com.sun.javafx.runtime.VersionInfo.getHudsonBuildNumber());
System.out.println("Java getReleaseMilestone : " + com.sun.javafx.runtime.VersionInfo.getReleaseMilestone());
System.out.println("Java getRuntimeVersion : " + com.sun.javafx.runtime.VersionInfo.getRuntimeVersion());
stage.setTitle("Lines plot");
//final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
//final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
//lineChart.setTitle("Stock quotes");
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);
XYChart.Series series1 = new XYChart.Series();
//series1.setName("Stock 1");
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
series1.getData().add(new XYChart.Data(11, 0.530123));
series1.getData().add(new XYChart.Data(12, 0.53035));
pane = new BorderPane();
pane.setCenter(lineChart);
//Scene scene = new Scene(lineChart, 800, 600);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
//root.getChildren().add(lineChart);
pane.getChildren().add(path);
rect = new Rectangle();
rect.setFill(Color.web("blue", 0.1));
rect.setStroke(Color.BLUE);
rect.setStrokeDashOffset(50);
rect.widthProperty().bind(rectX.subtract(rectinitX));
rect.heightProperty().bind(rectY.subtract(rectinitY));
pane.getChildren().add(rect);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
rect.setX(mouseEvent.getX());
rect.setY(mouseEvent.getY());
rectinitX.set(mouseEvent.getX());
rectinitY.set(mouseEvent.getY());
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
rectX.set(mouseEvent.getX());
rectY.set(mouseEvent.getY());
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_RELEASED) {
System.out.println("Zoom bounds : [" + rectinitX.get()+", "+rectinitY.get()+"] ["+ rectX.get()+", "+rectY.get()+"]");
System.out.println("TODO: Determine bound ranges according these zoom coordinates.\n");
// TODO: Determine bound ranges according this zoom coordinates.
//LineChart<String, Number> lineChart = (LineChart<String, Number>) pane.getCenter();
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
// Zoom in Y-axis by changing bound range.
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
yAxis.setLowerBound(0.532);
yAxis.setUpperBound(0.538);
// Zoom in X-axis by removing first and last data values.
// Note: Maybe better if categoryaxis is replaced by numberaxis then setting the
// LowerBound and UpperBound will be avaliable.
/*
XYChart.Series series1 = lineChart.getData().get(0);
if (!series1.getData().isEmpty()) {
series1.getData().remove(0);
series1.getData().remove(series1.getData().size() - 1);
}
*/
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
System.out.println("(a) xAxis.getLowerBound() "+xAxis.getLowerBound()+" "+xAxis.getUpperBound());
double Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower, newXupper;
newXlower = (rectinitX.get()/Tgap)+xAxis.getLowerBound();
newXupper = (rectX.get()/Tgap)+xAxis.getLowerBound();
if (newXupper > xAxis.getUpperBound())
newXupper = xAxis.getUpperBound();
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
System.out.println("(b) xAxis.getLowerBound() "+xAxis.getLowerBound()+" "+xAxis.getUpperBound());
// Hide the rectangle
rectX.set(0);
rectY.set(0);
}
}
};
public static void main(String[] args) {
launch(args);
}
}

You attached mouse event to Scene. So mouseEvent.getX() gives you coordinates relative to scene. But your formula uses them as coordinates relative to xAxis node. You need to take into account xAxis location on scene.
Introduce next method:
// summ layout shift against parent until we ascend to scene
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
and change your calculations accordingly:
double xAxisShift = getSceneShift(xAxis);
newXlower = ((rectinitX.get() - xAxisShift) / Tgap) + xAxis.getLowerBound();
newXupper = ((rectX.get() - xAxisShift) / Tgap) + xAxis.getLowerBound();

Related

Stage dimensions in a new NativeWindow

i'm building a flash desktop App where the user clicks on a button and opens a SWF file (a game) in a new window, i used a NativeWindow to achieve it, i did the folowing:
var windowOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
windowOptions.systemChrome = NativeWindowSystemChrome.STANDARD;
windowOptions.type = NativeWindowType.NORMAL;
var newWindow:NativeWindow = new NativeWindow(windowOptions);
newWindow.stage.scaleMode = StageScaleMode.NO_SCALE;
newWindow.stage.align = StageAlign.TOP_LEFT;
newWindow.bounds = new Rectangle(100, 100, 2000, 800);
newWindow.activate();
var aLoader:Loader = new Loader;
aLoader.load(new URLRequest(path));
newWindow.stage.addChild(aLoader);
the result is this:
the game doesn't take the whole space available. When i change the "NO_SCALE" to "EXACT_FIT":
newWindow.stage.scaleMode = StageScaleMode.EXACT_FIT;
i got this:
it only shows the top-left corner of the game. I also tried "SHOW_ALL" and "NO_BORDER". I got the same result as "EXACT_FIT".
When i open the SWF file of the game separatly it's displayed normally:
any idea how can i display the SWF game as the image above?
The best solution for that is to have the dimensions of your external game in pixel. you have to stretch the loader to fit it in your stage. if you are try to load different games with different sizes to your stage, save the swf dimensions with their URL addresses.
var extSWFW:Number = 550;
var extSWFH:Number = 400;
var extURL:String = "./Data/someSwf.swf";
var mainStageWidth:Number = 1024;
var mainStageHeight:Nubmer = 768;
var aLoader:Loader = new Loader;
aLoader.load(new URLRequest(extURL));
newWindow.stage.addChild(aLoader);
aLoader.scaleY = aLoader.scaleX = Math.min(mainStageWidth/extSWFW,mainStageHeight/extSWFH);
It is not very difficult to figure dimensions of a loaded SWF because it is engraved into it's header, you can read and parse it upon loading. Here's the working sample:
package assortie
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.utils.ByteArray;
public class Dimensions extends Sprite
{
private var loader:Loader;
public function Dimensions()
{
super();
loader = new Loader;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest("outer.swf"));
}
private function onLoaded(e:Event):void
{
var aBytes:ByteArray = loader.contentLoaderInfo.bytes;
var aRect:Rectangle = new Rectangle;
var aRead:BitReader = new BitReader(aBytes, 8);
var perEntry:uint = aRead.readUnsigned(5);
aRect.left = aRead.readSigned(perEntry) / 20;
aRect.right = aRead.readSigned(perEntry) / 20;
aRect.top = aRead.readSigned(perEntry) / 20;
aRect.bottom = aRead.readSigned(perEntry) / 20;
aRead.dispose();
aRead = null;
trace(aRect);
}
}
}
import flash.utils.ByteArray;
internal class BitReader
{
private var bytes:ByteArray;
private var position:int;
private var bits:String;
public function BitReader(source:ByteArray, start:int)
{
bits = "";
bytes = source;
position = start;
}
public function readSigned(length:int):int
{
var aSign:int = (readBits(1) == "1")? -1: 1;
return aSign * int(readUnsigned(length - 1));
}
public function readUnsigned(length:int):uint
{
return parseInt(readBits(length), 2);
}
private function readBits(length:int):String
{
while (length > bits.length) readAhead();
var result:String = bits.substr(0, length);
bits = bits.substr(length);
return result;
}
static private const Z:String = "00000000";
private function readAhead():void
{
var wasBits:String = bits;
var aByte:String = bytes[position].toString(2);
bits += Z.substr(aByte.length) + aByte;
position++;
}
public function dispose():void
{
bits = null;
bytes = null;
}
}

Duplicating video Stream Actionscript 3

Good Morning,
i am working on a video class, using a CRTMP Server for streaming. This works fine, but for my solution i need to duplicate the video stream (for some effects).
I googled for duplicate MovieClips and tried to duplicate the video like this.
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.media.*;
import flash.system.*;
import flash.utils.ByteArray;
public class Main extends MovieClip
{
public var netStreamObj:NetStream;
public var nc:NetConnection;
public var vid:Video;
public var vid2:Video;
public var streamID:String;
public var videoURL:String;
public var metaListener:Object;
public function Main()
{
init_RTMP();
}
private function init_RTMP():void
{
streamID = "szene3.f4v";
videoURL = "rtmp://213.136.73.230/maya";
vid = new Video(); //typo! was "vid = new video();"
vid2 = new Video();
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
nc.client = {onBWDone: function():void
{
}};
nc.connect(videoURL);
}
private function onConnectionStatus(e:NetStatusEvent):void
{
if (e.info.code == "NetConnection.Connect.Success")
{
trace("Creating NetStream");
netStreamObj = new NetStream(nc);
metaListener = new Object();
metaListener.onMetaData = received_Meta;
netStreamObj.client = metaListener;
netStreamObj.play(streamID);
vid.attachNetStream(netStreamObj);
//vid2.attachNetStream(netStreamObj); // wont work
addChild(vid);
// addChild(vid2); // wont work either
//intervalID = setInterval(playback, 1000);
}
}
private function asyncErrorHandler(event:AsyncErrorEvent):void
{
trace("asyncErrorHandler.." + "\r");
}
private function received_Meta(data:Object):void
{
var _stageW:int = stage.stageWidth;
var _stageH:int = stage.stageHeight;
var _videoW:int;
var _videoH:int;
var _aspectH:int;
var Aspect_num:Number; //should be an "int" but that gives blank picture with sound
Aspect_num = data.width / data.height;
//Aspect ratio calculated here..
_videoW = _stageW;
_videoH = _videoW / Aspect_num;
_aspectH = (_stageH - _videoH) / 2;
vid.x = 0;
vid.y = _aspectH;
vid.width = _videoW;
vid.height = _videoH;
vid2.x = 0;
vid2.y = _aspectH ;
}
}
It should be possible to duplicate the video stream. 2 Instance of the same videoStream. What am i doing wrong ?
Thanks for help.
"This means that i have to double the netstream. This is not what i want."
"I tried to duplicate the video per Bitmap.clone. But i got an sandbox violation."
You can try the workaround suggested here: Netstream Play(null) Bitmapdata Workaround
I'll show a quick demo of how it can be applied to your code.
This demo code assumes your canvas is width=550 & height=400. Keep that ratio if scaling up.
package
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.media.*;
import flash.system.*;
import flash.utils.ByteArray;
import flash.geom.*;
import flash.filters.ColorMatrixFilter;
public class Main extends MovieClip
{
public var netStreamObj:NetStream;
public var nc:NetConnection;
public var vid:Video;
public var streamID:String;
public var videoURL:String;
public var metaListener:Object;
public var vid1_sprite : Sprite = new Sprite();
public var vid2_sprite : Sprite = new Sprite();
public var vid2_BMP : Bitmap;
public var vid2_BMD : BitmapData;
public var colMtx:Array = new Array(); //for ColorMatrix effects
public var CMfilter:ColorMatrixFilter;
public function Main()
{
init_RTMP();
}
private function init_RTMP():void
{
streamID = "szene3.f4v";
videoURL = "rtmp://213.136.73.230/maya";
vid = new Video();
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
nc.client = { onBWDone: function():void { } };
//nc.connect(null); //for file playback
nc.connect(videoURL); //for RTMP streams
}
private function onConnectionStatus(e:NetStatusEvent):void
{
if (e.info.code == "NetConnection.Connect.Success")
{
trace("Creating NetStream");
netStreamObj = new NetStream(nc);
metaListener = new Object();
metaListener.onMetaData = received_Meta;
netStreamObj.client = metaListener;
//netStreamObj.play("vid.flv"); //if File
netStreamObj.play(streamID); //if RTMP
vid.attachNetStream(netStreamObj);
}
}
private function asyncErrorHandler(event:AsyncErrorEvent):void
{
trace("asyncErrorHandler.." + "\r");
}
private function received_Meta(data:Object):void
{
trace("Getting metadata");
var _stageW:int = stage.stageWidth;
var _stageH:int = stage.stageHeight;
var _videoW:int = data.width;
var _videoH:int = data.height;
var _aspectH:int = 0;
var Aspect_num:Number;
Aspect_num = data.width / data.height;
//Aspect ratio calculated here..
_videoW = _stageW;
_videoH = _videoW / Aspect_num;
_aspectH = (_stageH - _videoH) / 2;
trace("_videoW : " + _videoW);
trace("_videoW : " + _videoH);
trace("_aspectH : " + _aspectH);
vid.x = 0;
vid.y = 0;
vid.width = _videoW;
vid.height = _videoH;
setup_Copy(); //# Do this after video resize
}
public function setup_Copy () : void
{
vid2_BMD = new BitmapData(vid.width, vid.height, false, 0);
vid2_BMP = new Bitmap( vid2_BMD );
vid1_sprite.addChild(vid);
vid1_sprite.x = 0;
vid1_sprite.y = 0;
addChild( vid1_sprite );
vid2_sprite.addChild( vid2_BMP );
vid2_sprite.x = 0;
vid2_sprite.y = vid.height + 5;
addChild( vid2_sprite );
stage.addEventListener(Event.ENTER_FRAME, draw_Video);
}
public function draw_Video (evt:Event) : void
{
if ( netStreamObj.client.decodedFrames == netStreamObj.decodedFrames ) { return; } // Here we skip multiple readings
//# Get bitmapdata directly from container of video
if ( vid1_sprite.graphics.readGraphicsData().length > 0 )
{
vid2_BMD = GraphicsBitmapFill(vid1_sprite.graphics.readGraphicsData()[0]).bitmapData;
}
effect_BitmapData(); //# Do an effect to bitmapdata
}
public function effect_BitmapData ( ) : void
{
//# Matrix for Black & White effect
colMtx = colMtx.concat([1/3, 1/3, 1/3, 0, 0]); // red
colMtx = colMtx.concat([1/3, 1/3, 1/3, 0, 0]); // green
colMtx = colMtx.concat([1/3, 1/3, 1/3, 0, 0]); // blue
colMtx = colMtx.concat([0, 0, 0, 1, 0]); // alpha
CMfilter = new ColorMatrixFilter(colMtx);
vid2_BMP.bitmapData.applyFilter(vid2_BMD, new Rectangle(0, 0, vid.width, vid.height), new Point(0, 0), CMfilter);
}
}
}

Set up a Countdown and stop the game when it reaches 0

I have a simple game in AS3, and I'm trying to get a countdown working, so when the countdown gets to 0 the game ends.
I'm not getting any errors when I test this code but I'm also not getting a countdown clock. In the project I've created a text box and made it dynamic with an embedded font.
package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.utils.getTimer;
import flash.utils.Timer;
import flash.media.Sound;
import flash.media.SoundChannel;
public class MatchingGameObject10 extends MovieClip {
// game constants
private static const boardWidth:uint = 2;
private static const boardHeight:uint = 2;
private static const cardHorizontalSpacing:Number = 52;
private static const cardVerticalSpacing:Number = 52;
private static const boardOffsetX:Number = 145;
private static const boardOffsetY:Number = 70;
private static const pointsForMatch:int = 50;
private static const pointsForMiss:int = -5;
// variables
private var firstCard:Card10;
private var secondCard:Card10;
private var cardsLeft:uint;
private var gameScore:int;
private var gameStartTime:uint;
private var clockTimer:Timer;
// text fields
private var gameScoreField:TextField;
private var clock:TextField;
// timer to return cards to face-down
private var flipBackTimer:Timer;
// set up sounds
var theFirstCardSound:FirstCardSound = new FirstCardSound();
var theMissSound:MissSound = new MissSound();
var theMatchSound:MatchSound = new MatchSound();
// initialization function
public function MatchingGameObject10():void {
// make a list of card numbers
var cardlist:Array = new Array();
for(var i:uint=0;i<boardWidth*boardHeight/2;i++) {
cardlist.push(i);
cardlist.push(i);
}
// create all the cards, position them, and assign a randomcard face to each
cardsLeft = 0;
for(var x:uint=0;x<boardWidth;x++) { // horizontal
for(var y:uint=0;y<boardHeight;y++) { // vertical
var c:Card10 = new Card10(); // copy the movie clip
c.stop(); // stop on first frame
c.x = x*cardHorizontalSpacing+boardOffsetX; // set position
c.y = y*cardVerticalSpacing+boardOffsetY;
var r:uint = Math.floor(Math.random()*cardlist.length); // get a random face
c.cardface = cardlist[r]; // assign face to card
cardlist.splice(r,1); // remove face from list
c.addEventListener(MouseEvent.CLICK,clickCard); // have it listen for clicks
c.buttonMode = true;
addChild(c); // show the card
cardsLeft++;
}
}
// set up the score
gameScoreField = new TextField();
addChild(gameScoreField);
gameScore = 0;
showGameScore();
}
// set up the clock
public function CountdownClock() {
startClock();
}
public function startClock() {
clockTimer = new Timer(1000,10);
clockTimer.addEventListener(TimerEvent.TIMER, clockTick);
clockTimer.addEventListener(TimerEvent.TIMER_COMPLETE, clockEnd);
clockTimer.start();
showClock();
}
public function clockTick(event:TimerEvent) {
showClock();
}
public function showClock() {
clock.text = String(clockTimer.repeatCount - clockTimer.currentCount);
}
public function clockEnd(event:TimerEvent) {
clock.text = "!";
}
// player clicked on a card
public function clickCard(event:MouseEvent) {
var thisCard:Card10 = (event.target as Card10); // what card?
if (firstCard == null) { // first card in a pair
firstCard = thisCard; // note it
thisCard.startFlip(thisCard.cardface+2);
playSound(theFirstCardSound);
} else if (firstCard == thisCard) { // clicked first card again
firstCard.startFlip(1);
firstCard = null;
playSound(theMissSound);
} else if (secondCard == null) { // second card in a pair
secondCard = thisCard; // note it
thisCard.startFlip(thisCard.cardface+2);
// compare two cards
if (firstCard.cardface == secondCard.cardface) {
// remove a match
removeChild(firstCard);
removeChild(secondCard);
// reset selection
firstCard = null;
secondCard = null;
// add points
gameScore += pointsForMatch;
showGameScore();
playSound(theMatchSound);
// check for game over
cardsLeft -= 2; // 2 less cards
if (cardsLeft == 0) {
MovieClip(root).gameScore = gameScore;
MovieClip(root).clockTimer = clockTimer;
MovieClip(root).gotoAndStop("gameover");
}
} else {
gameScore += pointsForMiss;
showGameScore();
playSound(theMissSound);
flipBackTimer = new Timer(2000,1);
flipBackTimer.addEventListener(TimerEvent.TIMER_COMPLETE,returnCards);
flipBackTimer.start();
}
} else { // starting to pick another pair
returnCards(null);
playSound(theFirstCardSound);
// select first card in next pair
firstCard = thisCard;
firstCard.startFlip(thisCard.cardface+2);
}
}
// return cards to face-down
public function returnCards(event:TimerEvent) {
if (firstCard != null) firstCard.startFlip(1);
if (secondCard != null) secondCard.startFlip(1);
firstCard = null;
secondCard = null;
flipBackTimer.removeEventListener(TimerEvent.TIMER_COMPLETE,returnCards);
}
public function showGameScore() {
gameScoreField.text = "Score: "+String(gameScore);
}
public function playSound(soundObject:Object) {
var channel:SoundChannel = soundObject.play();
}
}
}
In your question you are speaking about a text field that you'v added to your stage, but in this case, your current code should give you an error about the line private var clock:TextField; if your text field is named clock, otherwise ( the name of your text field is not clock ) you can use that name in your showClock() function and do not forget to call CountdownClock() which will start the timer :
public function MatchingGameObject10(): void
{
// ...
CountdownClock();
}
And
public function showClock(): void
{
your_clock_textfield.text = String(clockTimer.repeatCount - clockTimer.currentCount);
}
You can also create your clock text field like you did with your gameScoreField one and in this case you can remove the text field that you'v already inserted in your stage :
public function MatchingGameObject10(): void
{
// ...
clock = new TextField();
addChild(clock);
CountdownClock();
}
Also, don't forget to set the position of your text fields because they will be inserted in the same position (0, 0) by default.
For embedding fonts dynamically ( using ActionScript ), take a look on my answer of this question.
Hope that can help.

Simplebutton image is larger than it actually is

I'm creating a button I use beginBitmapFill method to add image to it. Everything works normally, the problem is that the image loaded by a Loader () is greater than it actually is
Class that creates the button
package mode {
import flash.display.Sprite;
import org.osmf.net.StreamingURLResource;
public class LoadButton extends Sprite
{
public var Save;
public function LoadButton(x:uint,save:String,url:String)
{
var button:CustomSimpleButton = new CustomSimpleButton(url);
button.x = x;
Save = save;
addChild(button);
}
}
}
import flash.display.*;
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.SimpleButton;
import flash.events.*;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.net.URLRequest;
import flash.geom.Matrix;
class CustomSimpleButton extends SimpleButton
{
private var upColor:uint = 0xFFCC00;
private var overColor:uint = 0xCCFF00;
private var downColor:uint = 0x00CCFF;
private var sizew:uint = 100;
private var sizeh:uint = 88;
public function CustomSimpleButton(url:String)
{
downState = new ButtonDisplayState(downColor, sizew,sizeh,url);
overState = new ButtonDisplayState(overColor, sizew,sizeh,url);
upState = new ButtonDisplayState(upColor, sizew,sizeh,url);
hitTestState = new ButtonDisplayState(upColor, sizew * 2,sizeh,url);
hitTestState.x = -(sizew / 4);
hitTestState.y = hitTestState.x;
useHandCursor = true;
}
}
class ButtonDisplayState extends Shape
{
private var bgColor:uint;
private var size:uint;
private var sizeh:uint;
public function ButtonDisplayState(bgColor:uint, sizew:uint,sizeh:uint,url:String)
{
this.bgColor = bgColor;
this.size = sizew;
this.sizeh = sizeh;
draw(url);
}
private function draw(url:String):void
{
var myLoader:Loader = new Loader();
var image:Bitmap;
var uri = new URLRequest(url);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event)
{
image = new Bitmap(e.target.content.bitmapData);
graphics.beginBitmapFill(image.bitmapData);
graphics.drawRect(0, 0, 100, 88);
graphics.endFill();
});
myLoader.load(uri);
}
}
How the image is 100x88
as it is
The reason it is showing up cropped like that, is because the BitmapData is larger than the area you are filling.
Before filling the shape with the loaded BitmapData, you need to sale it down to the appropriate size.
Based on the answer to this question, you could do the following:
var scaleAmount:Number;
if(e.target.content.width >= e.target.content.height){
scaleAmount = size / e.target.content.width;
}else{
scaleAmount = sizeh / e.target.content.height;
}
var scaledBitmapData:BitmapData = scaleBitmapData(e.target.content.bitmapData, scaleAmount);
graphics.beginBitmapFill(scaledBitmapData);
graphics.drawRect(0, 0, size, sizeh);
graphics.endFill();
function scaleBitmapData(bitmapData:BitmapData, scale:Number):BitmapData {
scale = Math.abs(scale);
var width:int = (bitmapData.width * scale) || 1;
var height:int = (bitmapData.height * scale) || 1;
var transparent:Boolean = bitmapData.transparent;
var result:BitmapData = new BitmapData(width, height, transparent);
var matrix:Matrix = new Matrix();
matrix.scale(scale, scale);
result.draw(bitmapData, matrix);
return result;
}
It might be easier though, to just make your button state a Sprite and add the loaded Bitmap object and scale it directly:
class ButtonDisplayState extends Sprite
{
private var bgColor:uint;
private var image:Bitmap;
private var size:uint;
private var sizeh:uint;
public function ButtonDisplayState(bgColor:uint, sizew:uint,sizeh:uint,url:String)
{
this.bgColor = bgColor;
this.size = sizew;
this.sizeh = sizeh;
loadImage(url);
}
private function loadImage(url:String):void
{
var myLoader:Loader = new Loader();
var uri = new URLRequest(url);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event)
{
image = new Bitmap(e.target.content.bitmapData);
//scale the bitmap while retaining aspect ratio
//scale based off which dimension (width or height) is bigger
if(image.width >= image.height){
image.scaleX= size / image.width; //scale it to the width specified
image.scaleY = image.scaleX; //make the height aspect ratio match the widths
}else{
image.scaleY = sizeh /image.height;
image.scaleX = image.scaleY;
}
addChild(image); //add it to the display list of this object
});
myLoader.load(uri);
}
}

mapping planes onto primitives

I've looped through the vertices and mapped a plane to each one. I'm having problems orientating the planes correctly. I can get it working with a sphere but when i make any alterations to the the primitive - positions are correct but they don't face/tilt the right way.
EDIT: Note - the alternation to the sphere was done before the sphere was created. I have updated the Sphere class to create an elongated sphere.
The code I'm using to place the planes are as follows:
pivotDO3D = new DisplayObject3D();
scene.addChild(pivotDO3D);
var bigSphere:Sphere = new Sphere(null, 500, 20, 20);
for each (var v:Vertex3D in bigSphere.geometry.vertices)
{
var __seatmaterial:ColorMaterial = new ColorMaterial(0x000000);
__seatmaterial.doubleSided = true;
var p:Plane = new Plane(__seatmaterial, 20, 20, 2, 2);
pivotDO3D.addChild(p);
p.position = v.toNumber3D();
p.lookAt(bigSphere);
}
The following demo shows how to minimize the problem. I changed the multiplication factor of 0.6 to 2.0 as well as the sphere size in order to exaggerate the effect so you can see it easily. Make sure to change 0.6 to 2.0 in your Sphere.as as well.
The key is in varying the z location of the target point with the z location of the point on the sphere.
To compare, run it as-is to see the "fixed" version, and change the lookAt target from pivotDO3D2 to bigSphere to see the old version.
package
{
import flash.display.Sprite;
import flash.events.Event;
import org.papervision3d.cameras.*;
import org.papervision3d.core.geom.renderables.*;
import org.papervision3d.materials.*;
import org.papervision3d.objects.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.render.*;
import org.papervision3d.scenes.*;
import org.papervision3d.view.*;
[SWF(width='400', height='400', backgroundColor='0x000000', frameRate='30')]
public class PlaneOrientationDemo extends Sprite
{
private var scene:Scene3D;
private var camera:Camera3D;
private var renderer:BasicRenderEngine;
private var viewport:Viewport3D;
private var pivotDO3D:DisplayObject3D;
public function PlaneOrientationDemo()
{
viewport = new Viewport3D(0, 0, true, true);
addChild( viewport );
renderer = new BasicRenderEngine();
scene = new Scene3D( );
camera = new Camera3D();
camera.z = -700;
camera.zoom = 50;
pivotDO3D = new DisplayObject3D();
scene.addChild(pivotDO3D);
var pivotDO3D2:DisplayObject3D = new DisplayObject3D();
var bigSphere:Sphere = new Sphere(null, 150, 20, 20);
for each (var v:Vertex3D in bigSphere.geometry.vertices)
{
var __seatmaterial:ColorMaterial = new ColorMaterial(0x00FF00);
__seatmaterial.doubleSided = true;
var p:Plane = new Plane(__seatmaterial, 20, 20, 2, 2);
pivotDO3D.addChild(p);
p.position = v.toNumber3D();
// This number should match the fx multiplication factor in Sphere.as.
var xFactor:Number = 2.0;
pivotDO3D2.z = v.z / (Math.PI / xFactor);
p.lookAt(pivotDO3D2);
}
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event: Event): void
{
pivotDO3D.rotationX += 1;
pivotDO3D.rotationY += 1;
renderer.renderScene(scene, camera, viewport);
}
}
}