Default Navigation speed in Forge Viewer - autodesk-forge

How can the default navigation speed be changed in the Forge Viewer? The default setting is far to fast for my sample models. I should like to write code so that the speed may be changed during a session.

Copy an existing navigation tool from the viewer3D.js implementation, modify the speed parameters as you wish or expose methods to do so dynamically from your app, then set it active.
You can check the implementation of OrbitDollyPanTool L#14545 in viewer3D.js
Autodesk.Viewing.OrbitDollyPanTool = function( viewerImpl, viewerApi ){
var avp = Autodesk.Viewing.Private;
var _this = this;
var kScreenEpsilon = 0.001;
var kEpsilon = 0.00001;
var kAutoDeltaZ = 1.5; // Dolly increment
var kAutoDeltaXY = 0.01;
var kAutoScreenXY = 20;
var kDollyDragScale = 100.0;
var kDollyPinchScale = 0.5;
var kOrbitScale = 2.0;
// ...
That tool is instantiated as follow (L#40923):
Viewer3D.prototype.createControls = function( ) {
var self = this;
var impl = self.impl;
self.navigation = new av.Navigation(impl.camera);
self.__initAutoCam(impl);
self.utilities = new av.ViewingUtilities(impl, self.autocam, self.navigation);
self.clickHandler = new av.DefaultHandler(impl, self.navigation, self.utilities);
self.toolController = new av.ToolController(impl, self, self.autocam, self.utilities, self.clickHandler);
self.toolController.registerTool( new av.GestureHandler(self) );
self.toolController.registerTool( av.theHotkeyManager );
self.toolController.activateTool( av.theHotkeyManager.getName() );
self.registerUniversalHotkeys();
self.toolController.registerTool( new av.OrbitDollyPanTool(impl, self) );
self.toolController.activateTool( "gestures" );
return self.toolController;
};

I recently found out that you can use viewer.navigation.fitBounds(true, THREE.Box3) which will impact the navigation speed to match the extends defined by those bounds.

Related

Flex / Adobe Air - Chart legend/Axis fontsize too small

I'm adding a chart to my AIR App. Here is how:
private function CountersChart(container:VGroup):void {
var arr:Array = [];
var dt:Date = new Date();
var counters:Counters = Constants.srv.getCounters(dt.fullYear, dt.month);
var goals:ArrayCollection = Constants.srv.getGoals();
var index:int = 0;
var found:Boolean = false;
var now:Date = new Date();
while (!found) {
var goal:Goals = goals[index];
if (now.month+1==goal.month && now.fullYear==goal.year) {
found=true;
break;
} else
index++;
}
arr.push({label:"Calls", x:"Calls", y:counters.phone, y1:goals[index].calls});
arr.push({label:"Text", x:"Text", y:counters.sms, y1:goals[index].sms});
arr.push({label:"Email", x:"Email", y:counters.email, y1:goals[index].emails});
arr.push({label:"Marketing Tools", x:"Marketing Tools", y:counters.marketing, y1:goals[index].sendMarketingTools});
arr.push({label:"Meetings", x:"Meetings", y:counters.meeting, y1:goals[index].meetings});
arr.push({label:"Home Presentations Invites", x:"Home Presentations Invites", y:counters.homePresentationInvite, y1:goals[index].homePresentations});
arr.push({label:"Business Presentation Invites", x:"Business Presentation Invites", y:counters.businessPresentationInvite, y1:goals[index].businessPresentations});
// Create a column chart object
var stackedColumnChart:ColumnChart = new ColumnChart();
stackedColumnChart.dataProvider = arr;
stackedColumnChart.showDataTips = true;
stackedColumnChart.dataTipFunction = myDataTipFunction;
stackedColumnChart.percentWidth = 100;
// Set the horizontal axix category
var xAxis:CategoryAxis = new CategoryAxis();
xAxis.categoryField = "x";
stackedColumnChart.horizontalAxis = xAxis;
// ColumnSet.series is an array which contains
// an array of ColumnSeries objects.
var columnSet:ColumnSet = new ColumnSet();
columnSet.type = "stacked";
// Each item in seriesDetails becomes a different segment of
// a column in the stacked chart i.e. each item represents a series.
var seriesDetails:ArrayCollection = new ArrayCollection([
{YField:"y", DisplayName:"Actual"},
{YField:"y1", DisplayName:"Planned"},
]);
// Create a ColumnSeries, and an array to be
// populated with dynamically generated columnSeries objects
var columnSeries:ColumnSeries;
var seriesArray:Array = new Array();
var xr:AxisRenderer = new AxisRenderer();
xr.setStyle("fontSize",40);
//columnSeries.verticalAxis = xr;
// Generate an array of ColumnSeries objects
// which are then be applied to the ColumnSet series.
for(var i:int = 0; i < seriesDetails.length; i++){
columnSeries = new ColumnSeries();
columnSeries.yField = seriesDetails[i].YField;
columnSeries.displayName = seriesDetails[i].DisplayName;
columnSeries.setStyle("fill",fillsArray[i]);
seriesArray.push(columnSeries);
}
columnSet.series = seriesArray;
// c.series could take an array of column sets
// for displaying mixed charts i.e. c.series = [columnSet1, columnSet2];
stackedColumnChart.series = [columnSet];
//stackedColumnChart.showAllDataTips();
stackedColumnChart.showDataTips = true;
stackedColumnChart.id = "chart";
var legend:Legend = new Legend();
legend.direction = "horizontal";
legend.setStyle("fontSize",30);
legend.dataProvider = stackedColumnChart;
stackedColumnChart.setStyle("horizontalAxisRenderers", xr);
stackedColumnChart.addEventListener(MouseEvent.CLICK,function(event){return});
//Add chart to the display list.
container.addElement(stackedColumnChart);
container.addElement(legend);
}
The result draws the chart nicely BUT: The font size of the legend and the x/y axis are soooo small, Can't even read them.
I've tried to set the legend.setStyle("fontSize",fs) or to set the legend minHeight (as suggested here: Flex chart labels way too small, how to resize axis?
But to no avail :-(
How can I modify the font size for them?
Thanks
You need to provide the css for LegendItem class and ColumnChart class, and then it will pick up the style for it:
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#namespace mx "library://ns.adobe.com/flex/mx";
mx|LegendItem
{
fontSize:20;
}
mx|ColumnChart
{
fontSize:15;
}
</fx:Style>
Providing setStyle to Legend does not work.
Please let me know if it works for you.

Loading more than one child image AS3

I'm trying to load 2 images in to my flash file but getting an error - new to this AS3 malarkey any help would be appreciated!
Getting error: 5 1151: A conflict exists with definition request in namespace internal.
var myImage:String = dynamicContent.Profile[0].propImage.Url;
var myImage2:String = dynamicContent.Profile[0].prop2Image.Url;
var myImageLoader:StudioLoader = new StudioLoader();
var request:URLRequest = new URLRequest(enabler.getUrl(myImage));
myImageLoader.load(request);
myImageLoader.x =17;
myImageLoader.y = 0;
var myImageLoader2:StudioLoader = new StudioLoader();
var request:URLRequest = new URLRequest(enabler.getUrl(myImage2));
myImageLoader.load(request);
myImageLoader.x =17;
myImageLoader.y = 0;
if(this.currentFrame == 1) {
addChildAt(myImageLoader, 2);
}
if(this.currentFrame == 2) {
addChildAt(myImageLoader2, 2);
}
It's usually a good idea to move duplicate code into its own function instead of doing copy + paste:
function loadImage(file:String, x:Number, y:Number):StudioLoader{
var myImageLoader:StudioLoader = new StudioLoader();
var request:URLRequest = new URLRequest(file);
myImageLoader.load(request);
myImageLoader.x = x;
myImageLoader.y = y;
return myImageLoader;
}
addChild(loadImage(enabler.getUrl(myImage1),17,0));
addChild(loadImage(enabler.getUrl(myImage2),17,0));
That's not just giving you better structured code but also fixes your duplicate variable definition issue because what's defined locally inside a function stays inside the function.
This might provide some insight:
http://www.adobe.com/devnet/actionscript/learning/as3-fundamentals/functions.html
You can't create a new variable with the same name as an existing one, in your case I'm speaking about your URLRequest request, so to avoid this type of error you can do like this :
var myImageLoader2:StudioLoader = new StudioLoader();
// assing a new URLRequest to an existing var
request = new URLRequest(enabler.getUrl(myImage2));
// here you want use the myImageLoader2, not myImageLoader
myImageLoader2.load(request);
myImageLoader2.x =17;
myImageLoader2.y = 0;
Or :
var myImageLoader2:StudioLoader = new StudioLoader();
// create a new URLRequest var, so the name should be different
var request2:URLRequest = new URLRequest(enabler.getUrl(myImage2));
// here you want use the myImageLoader2, not myImageLoader
myImageLoader2.load(request2);
myImageLoader2.x =17;
myImageLoader2.y = 0;
Additionally, I notice in the second block that you declare myImageLoader2 but you then use the original myImageLoader to do the load request. So even if you do declare a new URLRequest, you wont get both images loaded.
Akmozo's solution has this corrected.

Feathers (Starling-based Adobe AIR library) List control doesn't work the second time it's instantiated

I'm having a problem with a Feathers List control. It works the first time I enter the screen that contains the list, but the second time I enter that screen in the same app execution, the scrolling list doesn't scroll at all plus texts don't appear. No error appears in the console.
I've tried lots of stuff, but I still have the same problem: It only works the first time it's instantiated. If I exit the screen and come back, it doesn't work at all!
When exiting the screen it's disposed and when coming back to that screen it's a new instance of List. Why does it work only the first time?
Also, I tried not using a custom ItemRenderer at all, so only the images appear, no text, and still the same happens. The list doesn't respond to scroll events the SECOND time is instantiated. So it's not a problem with the ItemRenderer.
Ok, here's some code:
typeList = new List();
typeList.x = Settings.appResolution[0] - Settings.menuTypeColumnWidth;
typeList.y = Settings.topBarHeight;
typeList.width = Settings.menuTypeColumnWidth;
typeList.height = Settings.appResolution[1] - Settings.topBarHeight;
typeList.dataProvider = new ListCollection(listContents);
typeList.itemRendererProperties['labelField'] = 'text';
typeList.itemRendererProperties['accessoryLabelField'] = 'articles';
typeList.itemRendererProperties['iconSourceField'] = 'thumbnail';
var listLayout:VerticalLayout = new VerticalLayout();
listLayout.gap = Settings.menuTypeItemGap;
typeList.layout = listLayout;
typeList.addEventListener(Event.CHANGE, onListChange);
typeList.itemRendererType = MenuTypeItemRenderer;
As you can see it's nothing out of the ordinary.
Thanks for your help.
Are you sure that the ListCollection used by the list is still available when the 2nd instantiation is performed?
_list.dataProvider = new ListCollection(items);
Here's a sample of code I've used. See if it offers any clues. I call the clearList function before the class is removed from the stage.
private function onAddedToStage(e:starling.events.Event):void {
removeEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
addEventListener(starling.events.Event.REMOVED_FROM_STAGE, onRemovedFromStage);
//only do this if a list does not already exist.
if(!_list){
items = new <ListItem>[];
for(var i:int = 0; i < 20; i++) {
items.push(new ListItem("Item text"));
}
_list = new List();
_list.itemRendererFactory = function():IListItemRenderer {
renderer = new ListItemRenderer();
// pass your skins in here
renderer.defaultSkin = new Image(AssetManager.getAtlas().getTexture("listItemClear_normal"));
renderer.defaultSelectedSkin = new Image(AssetManager.getAtlas().getTexture("listItemClear_selected"));
return renderer;
};
vl = new VerticalLayout();
vl.hasVariableItemDimensions = false;
_list.layout = vl;
_list.scrollerProperties.snapScrollPositionsToPixels = true;
_list.scrollerProperties.verticalScrollPolicy = Scroller.SCROLL_POLICY_AUTO;
_list.scrollerProperties.horizontalScrollPolicy = Scroller.SCROLL_POLICY_OFF;
_list.scrollerProperties.scrollBarDisplayMode = Scroller.SCROLL_BAR_DISPLAY_MODE_FLOAT;
//need to use a factory as we are not using a theme
_list.scrollerProperties.verticalScrollBarFactory = myScrollBarFactoryFunction;
_list.isSelectable = false;
_list.scrollerProperties.hasElasticEdges = true;
_list.itemRendererProperties.height = 60r;
_list.addEventListener(starling.events.Event.CHANGE, list_changeHandler);
_list.width = 320;
_list.height = StartUp._stageHeight;
addChild(_list);
_list.dataProvider = new ListCollection(items);
}
}
public function myScrollBarFactoryFunction():IScrollBar {
scrollBar = new SimpleScrollBar();
scrollBar.direction = SimpleScrollBar.DIRECTION_VERTICAL;
scrollBar.thumbProperties.defaultSkin = new Scale3Image(new Scale3Textures(AssetManager.getAtlas().getTexture("vertical-scroll-bar-thumb-skin"), 5, 14, Scale3Textures.DIRECTION_VERTICAL));
scrollBar.thumbProperties.width = 4;
scrollBar.thumbProperties.minHeight = 20;
scrollBar.width = 4;
return scrollBar;
}
public function clearList():void {
if (_list) {
scrollBar = null;
renderer = null;
vl = null;
removeChild(_list);
_list = null;
items.length = 0;
items = null;
}
}

AS3 - Adjust image colors

I'm adjusting image colors through the function below. The problem is that if I need to switch a colorFilter value to 0 it's not working but if I enter 0.1 instead of 0 it works.
How to make it work without that workaround?
import fl.motion.AdjustColor;
import flash.filters.ColorMatrixFilter;
var colorFilter:AdjustColor = new AdjustColor();
var mColorMatrix:ColorMatrixFilter;
var mMatrix:Array = [];
var MC:MovieClip = new MovieClip();
function adjustColors():void
{
colorFilter.hue = 50;
colorFilter.saturation = 50;
colorFilter.brightness = 50;
colorFilter.contrast = 12;
mMatrix = colorFilter.CalculateFinalFlatArray();
mColorMatrix = new ColorMatrixFilter(mMatrix);
MC.filters = [mColorMatrix];
}
I tested this by adding an argument to adjustColors() and calling it twice, and I see the same problem. I think it's just a bug where it ignores zero values.
It's not much of a better workaround, but if you just create a new AdjustColor each time, it should work correctly:
import fl.motion.AdjustColor;
import flash.filters.ColorMatrixFilter;
var colorFilter:AdjustColor = new AdjustColor();
var mColorMatrix:ColorMatrixFilter;
var mMatrix:Array = [];
var MC:MovieClip = new MovieClip();
function adjustColors():void
{
colorFilter = new AdjustColor();
colorFilter.hue = 50;
colorFilter.saturation = 50;
colorFilter.brightness = 50;
colorFilter.contrast = 12;
mMatrix = colorFilter.CalculateFinalFlatArray();
mColorMatrix = new ColorMatrixFilter(mMatrix);
MC.filters = [mColorMatrix];
}
Here is my workaround for reference:
Just use the logical OR assignment when you set each property.
That way if a value is 0 it will evaluate to false and .1 will be assigned instead:
var colorMat:ColorMatrixFilter = new ColorMatrixFilter();
var colorAdjust:AdjustColor = new AdjustColor();
const colorsAdj:Array =
[
// BRIGHTNESS, CONTRAST, SATURATION, HUE
[-20,0,20,-50],
[0,0,0,0],
[0,0,0,17]
];
function setColorMat(colorID:int):void
{
colorAdjust.brightness = colorsAdj[colorID][0] ||= .1;
colorAdjust.contrast = colorsAdj[colorID][1] ||= .1;
colorAdjust.saturation = colorsAdj[colorID][2] ||= .1;
colorAdjust.hue = colorsAdj[colorID][3] ||= .1;
colorMat.matrix = colorAdjust.CalculateFinalFlatArray();
}
That way you avoid recreating a new ColorMatrixFilter each time, in case it really change something...
And you keep a nice clean array... ;-)

Closure problem? - passing current value of a variable

I'm trying to pass the current value of a variable when an a dynamically generated navigation 'node' is clicked. This needs to just be an integer, but it always results in the last node's value.. have tried some different methods to pass the value, a custom event listener, a setter, but I suspect it's a closure problem.. help would be appreciated ;-)
function callGrid():void {
for (var i:Number = 0; i < my_total; i++) {
var gridnode_url = my_grid[i].#gridnode;
var news_category= my_grid[i].#category;
var newstitle = my_grid[i].#newstitle;
var news_content = my_grid[i]..news_content;
var news_image = my_grid[i]..news_image;
var gridnode_loader = new Loader();
container_mc.addChild(gridnode_loader);
container_mc.mouseChildren = false;
gridnode_loader.load(new URLRequest(gridnode_url));
gridnode_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gridLoaded);
gridnode_loader.name = i;
text_container_mc = new MovieClip();
text_container_mc.x = 0;
text_container_mc.mouseEnabled = false;
var textY = text_container_mc.y = (my_gridnode_height+18)*y_counter;
addChild(text_container_mc);
var tf:TextSplash=new TextSplash(newstitle,10,0,4 );
container_mc.addChild(tf);
tf.mouseEnabled = false;
tf.height = my_gridnode_height;
text_container_mc.addChild(tf);
var text_container_mc_tween = new Tween(text_container_mc, "alpha", Strong.easeIn, 0,1,0.1, true);
gridnode_loader.x = (my_gridnode_width+5) * x_counter;
gridnode_loader.y = (my_gridnode_height+15) * y_counter;
if (x_counter+1 < columns) {
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
}
}
function gridLoaded(e:Event):void {
var i:uint;
var my_gridnode:Loader = Loader(e.target.loader);
container_mc.addChild(my_gridnode);
_xmlnewstarget = my_gridnode.name;
//||||||||||||||||||||||||||||||||||||||||
//when a particular grid node is clicked I need to send the current _xmlnewstarget value to the LoadNewsContent function...
//||||||||||||| ||||||||||||||||||||||||
my_tweens[Number(my_gridnode.name)]=new Tween(my_gridnode, "alpha", Strong.easeIn, 0,1,0.1, true);
my_gridnode.contentLoaderInfo.removeEventListener(Event.COMPLETE, gridLoaded);
my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
}
function loadNewsContent(e:MouseEvent):void {
createNewsContainer();
getXMLNewsTarget();
news_category = my_grid[_xmlnewstarget].#category;
var tfnews_category:TextSplash=new TextSplash(news_category,20,16,32,false,false,0xffffff );
tfnews_category.mouseEnabled = false;
newstitle = my_grid[_xmlnewstarget].#newstitle;
var tftitle:TextSplash=new TextSplash(newstitle,20,70,24,false,false,0x333333 );
news_container_mc.addChild(tftitle);
tftitle.mouseEnabled = false;
news_content = my_grid[_xmlnewstarget]..news_content;
var tfnews_content:TextSplash=new TextSplash(news_content,20,110,20,true,true,0x333333,330);
news_container_mc.addChild(tfnews_content);
tfnews_content.mouseEnabled = false;
news_image = my_grid[_xmlnewstarget].#news_image;
loadNewsImage();
addChild(tfnews_category);
addChild(tftitle);
addChild(tfnews_content);
var news_container_mc_tween = new Tween(news_container_mc, "alpha", Strong.easeIn, 0,1,0.3, true);
news_container_mc_tween.addEventListener(Event.INIT, newsContentLoaded);
}
I'm not going to try to read your code (try to work on your formatting, even if it's just indenting), but I'll provide a simplified example:
for (var i = 0; i < my_total; i++) {
var closure = function() {
// use i here
}
}
As you say, when closure is called it will contain the last value of i (which in this case would be my_total). Do this instead:
for (var i = 0; i < my_total; i++) {
(function(i) {
var closure = function() {
// use i here
}
})(i);
}
This creates another function inside the loop which "captures" the current value of i so that your closure can refer to that value.
See also How does the (function() {})() construct work and why do people use it? for further similar examples.
Umm, as mentioned above, the code is a bit dense, but I think you might have a bit of type conversion problem between string and integers, is the "last value" always 0? try making these changes and let me know how you get on.
// replace this gridnode_loader.name = i;
gridnode_loader.name = i.toString();
// explictly type this as an int
_xmlnewstarget = parseInt(my_gridnode.name);
// replace this: my_tweens[Number(my_gridnode.name)] = new Tween(......
my_tweens[parseInt(my_gridnode.name)] = new Tween();
Oh and I think it goes without saying that you should massively refactor this code block once you've got it working.
Edit: after further study I think you need this
//replace this: my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
var anonHandler:Function = function(e:MouseEvent):void
{
loadNewsContent(_xmlnewstarget);
};
my_gridnode.addEventListener(MouseEvent.CLICK, anonHandler);
Where your loadNewsContent has changed arguements from (e:MouseEvent) to (id:String)
Firstly, you do not need to call addChild for the same loader twice (once in callGrid) and then in (gridLoaded). Then you can try putting inside loadNewsContent: news_category = my_grid[int(e.target.name)].#category;instead of news_category = my_grid[_xmlnewstarget].#category; As _xmlnewstarget seems to be bigger scope, which is why it is getting updated every time a load operation completes.