I'm having trouble using Maps in Haxe. I'm trying to create a grid of Tile objects and add them to the Map using their index on the grid as a key. However, when I try to retrieve a Tile from the map using an index I always get a value of null.
Could someone explain why this is happening? I've never used a map before and I don't understand what the issue is. I'm currently using a multidimensional array to get the same functionality, but maps seem more convenient.
private function initTiles():Void
{
var tempTile:Tile;
tileMap = new Map();
for (i in 0...widthTiles)
{
for (j in 0...heightTiles)
{
tempTile = new Tile(i * 32, j * 32);
tileMap.set([i,j],tempTile);
}
}
}
The issue is that the you are not actually creating a multidimensional array, you are creating a single dimensional array where the key type is Array<Int>. If ever in doubt, you can use $type( tileMap ) to get the compiler to tell you what type it thinks you have.
In your case, you would get:
Map<Array<Int>,Tile>; // This is an ObjectMap, where the object is an Array
When what you really want is:
Map<Int, Map<Int,Tile>>; // This is an IntMap, each value holding another IntMap
The reason this is an issue can be seen with this line:
trace( [0,0] == [0,0] ); // False!
Basically, in Haxe equality of objects (including arrays) is based on if they are the exact same object, not if they hold the same values. In this case, you are comparing two different arrays. Even though they hold the same values, they are actually two different objects, and not equal. Therefore they don't make suitable keys for your map.
Here is a working sample for what you need to do:
class Test {
static function main() {
initTiles();
trace( tileMap[3][6] );
}
static var tileMap:Map<Int,Map<Int,Tile>>;
static function initTiles():Void {
var widthTiles = 10;
var heightTiles = 10;
tileMap = new Map();
for (i in 0...widthTiles) {
if ( tileMap[i]==null ) {
// Add the sub-map for this column
tileMap[i] = new Map();
}
for (j in 0...heightTiles) {
// Add the tile for this column & row
tileMap[i][j] = new Tile(i*32, j*32);
}
}
}
}
class Tile {
var x:Int;
var y:Int;
public function new(x:Int, y:Int) {
this.x = x;
this.y = y;
}
}
And to see it in action: http://try.haxe.org/#E14D5 (Open your browser console to see the trace).
Related
I am attempting to piece together an example from ml5 on image style transfer (https://ml5js.org/docs/style-transfer-image-example) with p5.js examples parsing a JSON of image URLs, and adding them to an array to display as images. I am hitting a dead end as I do not think I fully understand the ways that p5 stores images in an array, nor do I fully understand the difference between createImg() createImage() or loadImage() (which one to use!!)
The goal is to use Bing image API to return a list of URLS from a search (this part is working fine) and run those images through a pretrained model (this part is working fine when just used on a local image). It is the bringing the two together that I am unable to figure out. Any suggestions or advice (is this even possible??!) greatly appreciated.
I have already tried loading images into an array and iterating through the array in the draw() function. The problem happens when I need to address an image in order to actually apply the style transfer model. It seems like my array is empty when I attempt to refer to it anywhere except draw(). I am sure I am thinking about this incorrectly.
var imageData;
let imgArray = [];
var w = (window.innerWidth)/3;
var h = (window.innerHeight)/4;
var index = 0;
var xPos = 0;
var yPos = 0;
var indexMax = 3;
let style;
let resultImg;
function preload() {
loadData();
}
function loadData(){
var url = api + search + subscriptionKey;
loadJSON(url, gotData);
}
function gotData(data) {
imageData = data;
for (var i=0; i < indexMax; i++){
_url = imageData.value[i].contentUrl;
imgArray.push(loadImage(_url));
}
function displayImages(){
if (index < 3){
index++;
} else {
index = 0;
};
function setup() {
createCanvas(1200, 800).parent('canvasContainer');
var button = select('#display');
button.mousePressed(displayImages);
var transferBtn = select('#transferBtn');
transferBtn.mousePressed(transferImages);
//create style method
style = ml5.styleTransfer('/model', modelLoaded);
}
function draw() {
image(imgArray[index], xPos, yPos, w, h);
}
//ml5 stuff
function modelLoaded() {
if (style.ready){
select('#status').html('Model Loaded');
//style.transfer(gotResult);
}
}
function transferImages(){
select('#status').html('applying style transfer');
style.transfer(tempImg, function(err, result){
createImg(result.src);
});
select('#status').html('done');
}
I am attempting to (unsuccessfully) create a "tempImg" from imgArray[0] to try to figure out where this createImage needs to go, but have not gotten this to work. I have CORS enabled, so I didnt think this was the problem, but am getting the following error. Please help me understand how to think about this differently.
You should use loadImage instead of createImg.
style.transfer(tempImg, function(err, result){
p5CompatibleImage = loadImage(result.src);
});
Can anybody say me what is faster: Array or ArrayList? (ActionScript3)
I tried to find a page about this but didn't find anything.
Thank you.
The ArrayList class is a simple implementation of IList that uses a backing Array as the source of the data. Items in the backing Array can be accessed and manipulated using the methods and properties of the IList interface. Operations on an ArrayList instance modify the data source; for example, if you use the removeItemAt() method on an ArrayList, you remove the item from the underlying Array.
Apparently ArrayList class wraps an Array object - hence a plain Array would be faster than an ArrayList object.
As already stated, Array is faster. Actually it is orders of magnitude faster.
The equivalents of array access are getItemAt and setItemAt.
Implementation:
public function getItemAt(index:int, prefetch:int = 0):Object
{
if (index < 0 || index >= length)
{
var message:String = resourceManager.getString(
"collections", "outOfBounds", [ index ]);
throw new RangeError(message);
}
return source[index];
}
and:
public function setItemAt(item:Object, index:int):Object
{
if (index < 0 || index >= length)
{
var message:String = resourceManager.getString(
"collections", "outOfBounds", [ index ]);
throw new RangeError(message);
}
var oldItem:Object = source[index];
source[index] = item;
stopTrackUpdates(oldItem);
startTrackUpdates(item);
//dispatch the appropriate events
if (_dispatchEvents == 0)
{
var hasCollectionListener:Boolean =
hasEventListener(CollectionEvent.COLLECTION_CHANGE);
var hasPropertyListener:Boolean =
hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE);
var updateInfo:PropertyChangeEvent;
if (hasCollectionListener || hasPropertyListener)
{
updateInfo = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE);
updateInfo.kind = PropertyChangeEventKind.UPDATE;
updateInfo.oldValue = oldItem;
updateInfo.newValue = item;
updateInfo.property = index;
}
if (hasCollectionListener)
{
var event:CollectionEvent =
new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
event.kind = CollectionEventKind.REPLACE;
event.location = index;
event.items.push(updateInfo);
dispatchEvent(event);
}
if (hasPropertyListener)
{
dispatchEvent(updateInfo);
}
}
return oldItem;
}
There's a LOT of calls and checks involved here. Please note, that _dispatchEvents == 0 is true by default (unless you disableEvents), thus writing in fact is an immense operation.
However ArrayList does provide a lot of feature, that are usefull within flex. A good compormise is to grab the underlying Array (accessible as ArrayList::source), peform your operations, and then reassign it (supposing you have listeners observing that Array).
Also, if you go with Flash Player 10, then Vector will outperform Array.
greetz
back2dos
Array is probably slightly faster or they are equal. All an ArrayList is, is an implementation of iList that uses an... Array as a backing object.
Im trying to extend the flex ArrayCollection to be able to search for an object containing specific data and give it back.
Here is my function:
public function getItemContaining(value: String): Object {
//Loop through the collection
for each(var i: Object in this) {
//Loop through fields
for(var j: String in i) {
//If field value is equal to input value
if(i[j] == value) {
return i;
}
}
}
//If not found
return null;
}
Problem is j is always null so the second loop never works. So I read flex loop descriptions and actually it should work just fine. What can possibly be the problem?
Try it like this:
for (var name:String in myObject){
trace(name + ":" + myObject[name];
}
Okay that was actually the same you were doing. The error must be in this line:
for each(var i: Object in this) {
Try using this:
for each(var i: Object in this.source) {
My first instinct would be to have a look at data type. You're setting up a loop declaring j:String and the symptom is that j is always null. This suggests to me that Flex is failing to interpret the elements of i as strings. If Flex only recognizes the elements of i as Objects (because all Strings are Objects, and Objects are the lowest common denominator), it would return null for j:String.
Try this for your inner loop:
for(var j: Object in i) {
//If field value is equal to input value
if(i[j] is String && (i[j] as String) == value) {
return i;
}
}
if you are using ArrayCollection as your datasource, you should look at using the IViewCursor interface. You can supply a custom compare function, or supply the fields top compare to. This interface is well documented with examples in adobe/livedocs
var _cursor:IViewCursor;
var _idSortField:SortField;
var _idSort:Sort = new Sort();
_idSortField = new SortField();
_idSortField.compareFunction = this.myCompareFunction;
_idSort.fields = [_idSortField];
myArrayCollection.sort = _idSort;
myArrayCollection.refresh();
_cursor = myArrayCollection.createCursor();
if (_cursor.findAny(search))
return _cursor;
if you are search for a value in a specific property, then its even easier. Here's the link to adobe livedocs on this topic
I am searching for a method to intersect my array collections.
I have one collection: allItems and another subSet. I want to create another ArrayCollection where all items which do not exist in subSet will be stored. Is there a way to do this?
working answer provided by eemeli ... here is an alternative implementation optimized for speed (array access instead of calls) and scalability (approach provides O(m+n) instead of O(m*n))...
public static function difference(a:ArrayCollection, b:ArrayCollection):ArrayCollection {
var entry:*, map:Dictionary = new Dictionary(), intersection:Array = [];
for each (entry in a.source) map[entry] = entry;
for each (entry in b.source) delete map[entry];
for each (entry in map) intersection.push(entry);
return new ArrayCollection(intersection);
}
For getting a collection of items not in another you need a set difference algorithm (allItems minus subSet).
public function minus(a:ArrayCollection, b:ArrayCollection):ArrayCollection {
var result:ArrayCollection = new ArrayCollection()
for each (i in a) {
if (!b.contains(i)) {
result.addItem(i)
}
}
return result
}
var allLength:Number = allItems.length;
var intersection:ArrayCollection = new ArrayCollection();
for(var i:Number = 0; i < allLength; i++)
if(subSet.getItemIndex(allItems.getItemAt(i)) == -1)
intersection.addItem(allItems.getItemAt(i));
Note that this will work only if the subset contains the same objects as the super set. If the subset contains different objects with the same property values as of the super set object, you are gonna have to compare their properties separately.
Hi i need to make a VectorIterator, so i need to accept a Vector with any type. I am currently trying to define the type as * like so:
var collection:Vector.<*> = new Vector<*>()
But the compiler is complaining that the type "is not a compile time constant". i know a bug exists with the Vector class where the error reporting, reports the wrong type as missing, for example:
var collection:Vector.<Sprite> = new Vector.<Sprite>()
if Sprite was not imported, the compiler would complain that it cannot find the Vector class. I wonder if this is related?
So it looks like the answer is there is no way to implicitly cast a Vector of a type to valid super type. It must be performed explicitly with the global Vector.<> function.
So my actual problem was a mix of problems :)
It is correct to use Vector. as a generic reference to another Vector, but, it cannot be performed like this:
var spriteList:Vector.<Sprite> = new Vector.<Sprite>()
var genericList:Vector.<Object> = new Vector.<Object>()
genericList = spriteList // this will cause a type casting error
The assignment should be performed using the global Vector() function/cast like so:
var spriteList:Vector.<Sprite> = new Vector.<Sprite>()
var genericList:Vector.<Object> = new Vector.<Object>()
genericList = Vector.<Object>(spriteList)
It was a simple case of me not reading the documentation.
Below is some test code, I would expect the Vector. to cast implicitly to Vector.<*>.
public class VectorTest extends Sprite
{
public function VectorTest()
{
// works, due to <*> being strictly the same type as the collection in VectorContainer
var collection:Vector.<*> = new Vector.<String>()
// compiler complains about implicit conversion of <String> to <*>
var collection:Vector.<String> = new Vector.<String>()
collection.push("One")
collection.push("Two")
collection.push("Three")
for each (var eachNumber:String in collection)
{
trace("eachNumber: " + eachNumber)
}
var vectorContainer:VectorContainer = new VectorContainer(collection)
while(vectorContainer.hasNext())
{
trace(vectorContainer.next)
}
}
}
public class VectorContainer
{
private var _collection:Vector.<*>
private var _index:int = 0
public function VectorContainer(collection:Vector.<*>)
{
_collection = collection
}
public function hasNext():Boolean
{
return _index < _collection.length
}
public function get next():*
{
return _collection[_index++]
}
}
[Bindable]
public var selectedItems:Vector.<Category>;
public function selectionChange(items:Vector.<Object>):void
{
selectedItems = Vector.<Category>(items);
}
I believe you can refer to an untyped Vector by just calling it Vector (no .<>)
With Apache Flex 4.11.0, you can already do what you want. It might have been there since 4.9.0, but I have not tried that before.
var collection:Vector.<Object> = new Vector.<Object>()
maybe?
But i'm just speculating, haven't tried it.
var collection:Vector.<Object> = new Vector.<Object>()
but only on targeting flash player 10 cs4