Restrict Pan outside WMS extent in OpenLayers3 - openlayers-3

I have rectangle WMS of small area and want to restrict panning outside WMS extends, so there aren't white or black area outside the map visible at all.
Adding extent to View does not work for me and in documentation about this option is written
The extent that constrains the center, in other words, center cannot
be set outside this extent.
But as I understand this if center is in the area of extent, but on the very corner, it will show white area outside this extent, but I don't want to see white area at all.
Is it possible to achieve this with OL3?

Here's my solution. I wrote it just now, and so it is not extensively tested. It would probably break if you start rotating the map, for example, and it may be glitchy if you zoom out too far.
var constrainPan = function() {
var visible = view.calculateExtent(map.getSize());
var centre = view.getCenter();
var delta;
var adjust = false;
if ((delta = extent[0] - visible[0]) > 0) {
adjust = true;
centre[0] += delta;
} else if ((delta = extent[2] - visible[2]) < 0) {
adjust = true;
centre[0] += delta;
}
if ((delta = extent[1] - visible[1]) > 0) {
adjust = true;
centre[1] += delta;
} else if ((delta = extent[3] - visible[3]) < 0) {
adjust = true;
centre[1] += delta;
}
if (adjust) {
view.setCenter(centre);
}
};
view.on('change:resolution', constrainPan);
view.on('change:center', constrainPan);
This expects the variables map, view (with obvious meanings) and extent (the xmin, ymin, xmax, ymax you want to be visible) to be available.

Here's a more robust implementation that should work really well in any case. It's written in ES6, and requires isEqual method (from lodash or anything else ...)
const extent = [-357823.2365, 6037008.6939, 1313632.3628, 7230727.3772];
const view = this.olMap.getView();
const modifyValues = {};
// Trick to forbid panning outside extent
let constrainPan = (e) => {
const type = e.type;
const newValue = e.target.get(e.key);
const oldValue = e.oldValue;
if (isEqual(oldValue, newValue)) {
// Do nothing when event doesn't change the value
return;
}
if (isEqual(modifyValues[type], newValue)) {
// Break possible infinite loop
delete modifyValues[type];
return;
}
if (type === 'change:resolution' && newValue < oldValue) {
// Always allow zoom-in.
return;
}
const visibleExtent = view.calculateExtent(this.olMap.getSize());
const intersection = ol.extent.getIntersection(visibleExtent, extent);
const modify = !isEqual(intersection, visibleExtent);
if (modify) {
if (type === 'change:center') {
const newCenter = newValue.slice(0);
if (ol.extent.getWidth(visibleExtent) !== ol.extent.getWidth(intersection)) {
newCenter[0] = oldValue[0];
}
if (ol.extent.getHeight(visibleExtent) !== ol.extent.getHeight(intersection)) {
newCenter[1] = oldValue[1];
}
modifyValues[type] = newCenter;
view.setCenter(newCenter);
} else if (type === 'change:resolution') {
modifyValues[type] = oldValue;
view.setResolution(oldValue);
}
}
};
view.on('change:resolution', constrainPan);
view.on('change:center', constrainPan);

This is an extension to #tremby answer, but to long for a comment.
First of all, his solution works really well for me, but it was called way to often. Therefore I wrapped it in a debounce function.
So
view.on('change:resolution', constrainPan);
view.on('change:center', constrainPan);
becomes
var dConstrainPan = debounce(constrainPan);
view.on('change:resolution', dConstrainPan);
view.on('change:center', dConstrainPan);
This will result in a slight flicker, when moving outside the bounding box, bot zooming/ moving works without delay.
Still not perfect but a useful improvement from my point of view.
Debounce code:
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
Soruce: https://davidwalsh.name/javascript-debounce-function , in underscore.js

Related

Unity - Disable AR HitTest after initial placement

I am using ARKit plugin for Unity leveraging the UnityARHitTestExample.cs.
After I place my object into the world scene I want to disable the ARKit from trying to place the object again every time I touch the screen. Can someone please help?
There are a number of ways you can achieve this, although perhaps the simplest is creating a boolean to determine whether or not your model has been placed.
First off all you would create a boolean as noted above e.g:
private bool modelPlaced = false;
Then you would set this to true within the HitTestResultType function once your model has been placed:
bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes)
{
List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypes);
if (hitResults.Count > 0) {
foreach (var hitResult in hitResults) {
//1. If Our Model Hasnt Been Placed Then Set Its Transform From The HitTest WorldTransform
if (!modelPlaced){
m_HitTransform.position = UnityARMatrixOps.GetPosition (hitResult.worldTransform);
m_HitTransform.rotation = UnityARMatrixOps.GetRotation (hitResult.worldTransform);
Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z));
//2. Prevent Our Model From Being Positioned Again
modelPlaced = true;
}
return true;
}
}
return false;
}
And then in the Update() function:
void Update () {
//Only Run The HitTest If We Havent Placed Our Model
if (!modelPlaced){
if (Input.touchCount > 0 && m_HitTransform != null)
{
var touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved)
{
var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
ARPoint point = new ARPoint {
x = screenPosition.x,
y = screenPosition.y
};
ARHitTestResultType[] resultTypes = {
ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent,
};
foreach (ARHitTestResultType resultType in resultTypes)
{
if (HitTestWithResultType (point, resultType))
{
return;
}
}
}
}
}
}
Hope it helps...

OpenLayers 3 Shows white space while dragging/panning

I am right now using Ol3 to display static image(like http://openlayers.org/en/latest/examples/static-image.html) . Although when I am zoomed in and start panning/dragging i can see white space as seen in example. I don't want to see that.
Is it possible to pan only till the end of image so that white space does not appear?
You can restrict the dragging of the image by restricting its extent.
Here's an inline link to fiddle.
map.on('moveend', function(evt){
console.log(view.getZoom());
if(view.getZoom()<=2){
view.setZoom(3);
}
});
var constrainPan = function() {
console.log("move");
var extents=[0, 0, 1024, 968]
var visible = view.calculateExtent(map.getSize());
var centre = view.getCenter();
var delta;
var adjust = false;
if ((delta = extents[0] - visible[0]) > 0) {
adjust = true;
//console.log(delta);
centre[0] += delta;
} else if ((delta = extents[2] - visible[2]) < 0) {
adjust = true;
centre[0] += delta;
}
if ((delta = extents[1] - visible[1]) > 0) {
adjust = true;
centre[1] += delta;
} else if ((delta = extents[3] - visible[3]) < 0) {
adjust = true;
centre[1] += delta;
}
if (adjust) {
view.setCenter(centre);
}
};
view.on('change:resolution', constrainPan);
view.on('change:center', constrainPan);

Crispy GridLineDashStyle

The gridlines when set to "ShortDot", or any Dots, are always two pixels tall in SVG, and research says it can be fixed via
a) transform(0.5,0.5) -- moves it half a pixel so drawing is in one pixel,
or
b) add style='shape-rendering:crispEdges' to the element
See demo here:
http://jsfiddle.net/aerialflyer/o2d9w6up/
Here's the SVGElement prototype from Highcharts.js
SVGElement.prototype = {
dashstyleSetter: function (value) {
var i;
value = value && value.toLowerCase();
if (value) {
value = value
.replace('shortdashdotdot', '3,1,1,1,1,1,')
.replace('shortdashdot', '3,1,1,1')
.replace('shortdot', '1,1,')
.replace('shortdash', '3,1,')
.replace('longdash', '8,3,')
.replace(/dot/g, '1,3,')
.replace('dash', '4,3,')
.replace(/,$/, '')
.split(','); // ending comma
i = value.length;
while (i--) {
value[i] = pInt(value[i]) * this['stroke-width'];
}
value = value.join(',')
.replace('NaN', 'none'); // #3226
this.element.setAttribute('stroke-dasharray', value);
}
}
}
How can this be updated to include either the transform, or the 'style' (preferred)??
i.e.
Add
this.element.setAttribute('style', 'shape-rendering:crispEdges');
Can the SVGElement prototype be updated (fails so far)
// Make grid lines crispt to prevent anti-alias
SVGElement.prototype['dashstyleSetter'] = SVGElement.prototype.dashstyleSetter = function (value) {
var i;
value = value && value.toLowerCase();
if (value) {
value = value
.replace('shortdashdotdot', '3,1,1,1,1,1,')
.replace('shortdashdot', '3,1,1,1')
.replace('shortdot', '1,1,')
.replace('shortdash', '3,1,')
.replace('longdash', '8,3,')
.replace(/dot/g, '1,3,')
.replace('dash', '4,3,')
.replace(/,$/, '')
.split(','); // ending comma
i = value.length;
while (i--) {
value[i] = pInt(value[i]) * this['stroke-width'];
}
value = value.join(',')
.replace('NaN', 'none'); // #3226
this.element.setAttribute('stroke-dasharray', value);
this.element.setAttribute('style', 'shape-rendering:crispEdges');
}
};
Highcharts Demo:
http://jsfiddle.net/aerialflyer/yj1s5xps/
See how dot is just a long gray line
It is possible to extend Highcharts and set shape-rendering to crispEdges (because shape-rendering is attribute set directly, not in style - MDN: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) in dashstyleSetter of SVGElement.
Wrapper:
(function (H) {
H.wrap(H.SVGElement.prototype, 'dashstyleSetter', function (proceed) {
// Run original proceed method
proceed.apply(this, [].slice.call(arguments, 1));
if(arguments[1]) {
this.element.setAttribute('shape-rendering', 'crispEdges');
}
});
}(Highcharts));
JSFiddle example: http://jsfiddle.net/yurn5oz5/
Docs reference for extending Highcharts

Titanium ImageView animation width and height

I try to create an ImageView in Titanium like this:
var animationView = Titanium.UI.createImageView
(
{
images:animationImages,
duration:50,
repeatCount:0,
width: '90dp',
height: '270dp'
}
);
On android it gets its size as expected, but on IOS, it simply doesn't gets scaled. Is there something, i'm doing wrong? Or should i do it frame by frame by creating the ImageViews manually then changing them with setInterval?
This is actually not a consistent solution, it should be a comment, but since I don't have enough rep I have to write it as an answer.
For starters I would try to give it a top and left properties.
Secondly, are those images retrieved from a remote URL? Remote URLs are only supported in Android. If that is the case you could do a workaround as you said in the question.
Finally, the 'dp' only works for android, so it won't scale at all in iOS, it will simply erase the 'dp' and use the number as "points", on non-retina screens it will be the same number of pixels and on a retina display it will be the double.
I finally decided to create my own animation class, which look like this:
function Animation(data)
{
var width = data.hasOwnProperty("width") ? data.width : Ti.UI.SIZE;
var height = data.hasOwnProperty("height") ? data.height: Ti.UI.SIZE;
var duration = data.hasOwnProperty("duration") ? data.duration : 50;
var imageFiles = data.hasOwnProperty("images") ? data.images : [];
var images = [];
var container = Ti.UI.createView
(
{
width:width,
height: height
}
);
for(var i=0; i<imageFiles.length; i++)
{
var image = Ti.UI.createImageView
(
{
image:imageFiles[i],
width:width,
height:height
}
);
if(i!=0)
image.setVisible(false);
container.add(image);
images.push(image);
}
container.activeImage = 0;
container.intervalId = null;
container.setActiveImage = function(index)
{
if(container.intervalId == null)
container.activeImage = index;
}
container.start = function()
{
var callback = function()
{
for(var i=0; i<images.length; i++)
{
if(i == container.activeImage)
images[i].setVisible(true);
else
images[i].setVisible(false);
}
container.activeImage = (container.activeImage + 1) % images.length;
}
container.intervalId = setInterval ( callback, duration );
}
container.stop = function()
{
clearInterval(container.intervalId);
container.intervalId = null;
}
return container;
}
module.exports = Animation;
And you can use it like this:
var Animation = require('...path to your animation file');
var myAnimation = new Animation
(
{
width:'100dp',
height:'100dp',
duration:50, //duration while one frame is showing
images:['one.png', 'two.png'...], //full paths
}
);
//start:
myAnimation.start();
//stop
myAnimation.stop();

How can I hit test between two arrays whilst switching between movieclips within those arrays?

I am making a platforming game in which I must switch between colliding the character between two Arrays (whiteBlocks and blackBlocks) as well as another Array (redBlocks) that is activated on button collision and is timed.
However, I am having trouble with the hitTest code I have used to ensure that I can use redBlocks as a platform as well as either whiteBlocks or blackBlocks at the same time. I was wandering if anyone could help me to have the redBlocks constantly active once the timer has been triggered and is not effective by switching between blackBlocks or whiteBlocks.
If you would like me to explain my problem in a clearer way I will do. Please note that I am quite new to actionscript and coding in general so apologies for errors I may make. I appreciate any help you can offer as I have been troubleshooting this for a while now.
import flash.events.KeyboardEvent;
import flash.geom.Point;
import flash.events.Event;
import flash.display.MovieClip
import flash.display.DisplayObject;
//PLAYER MOVEMENT
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyLIST);
stage.addEventListener(KeyboardEvent.KEY_UP, keyLIST);
manMC.positionPNT = new Point (manMC.x, manMC.y);
//track using loop and keypresses, not just keyEvent
var leftKeyDown:Boolean = false;
var rightKeyDown:Boolean = false;
function keyLIST (ke:KeyboardEvent) {
//trace(ke.toString() );
//1. work out where we're going from where we are now
//characterPNT = new Point(manMC.x, manMC.y);
//2. offset our destination point
switch (ke.keyCode) {
//LEFT
case 37:
trace(ke.type);
leftKeyDown = (ke.type == "keyDown");
break;
//RIGHT
case 39:
//moveCharacter(manMC, "right");
rightKeyDown = (ke.type == "keyDown");
break;
//87UP, 83DOWN
//UP
case 38:
trace("true");
manMC.jump();
break;
//DOWN
case 83:
break;
default:
//trace ("this key does nothing");
break;
}// close switch
}// end function keyLIST
function movementCallback (ev:Event):void {
if (leftKeyDown == true) {
moveCharacter(manMC, "left");
}
if (rightKeyDown == true) {
moveCharacter(manMC, "right");
}
}
addEventListener (Event.ENTER_FRAME, movementCallback);
//a new movement function
function moveCharacter(char:character, dir:String ):void {//datatype as void as it returns nothing
//will need this
//var characterPNT:Point; //moved to character class
//maMC.positionPNT = new Point( manMC.x, manMC.y);
//copy current poistion before offsetting with movement, speed and direction
manMC.destinationPNT = manMC.positionPNT.clone();
//characterPNT = new Point(manMC.x, manMC.y);
//var colliding:Boolean;
//do multiple collision detection here
var offsetPNT:Point;//detecting the destination of the body points
switch (dir) {
case "left":
//move PNT to left
manMC.destinationPNT.x -= manMC.speedNUM;
offsetPNT = manMC.leftarmPNT;
break;
case "right":
//move PNT to right
manMC.destinationPNT.x += manMC.speedNUM;
offsetPNT = manMC.rightarmPNT;
break;
}
//set to true of false by the function that returns a Boolean value
var colliding:Boolean = multipleHitTest (manMC.positionPNT, manMC.destinationPNT, offsetPNT, activeArray);
//trace(colliding);
//trace ("moveMode: " + manMC.moveModeSTR);
if (!colliding /*&& manMC.moveModeSTR != "falling"*/) { //may be more complex to control player movement when falling&jumping
manMC.moveToPoint( manMC.destinationPNT );
//manMC.x = manMC.destinationPNT.x;
//manMC.y = manMC.destinationPNT.y;
//always update position PNT when character is moved
//manMC.positionPNT = manMC.destinationPNT.clone();//copies destination point
}
}
//Apply gravity at all times using a loop/callback
addEventListener(Event.ENTER_FRAME, gravityFUNC);
var gravityNUM:Number = new Number(10);
function gravityFUNC (ev:Event) {
var falling:Boolean;
var gravityPNT:Point = manMC.positionPNT.clone();
//trace(manMC.positionPNT.y);
gravityPNT.y += gravityNUM;
//trace(gravityPNT.y);
//EXPERIMENTAL
gravityPNT.y -= manMC.verticalVelocityNUM;
//decaying gravity, caping it at 0 to avoid negatives
manMC.verticalVelocityNUM = Math.max ( 0, manMC.verticalVelocityNUM - gravityNUM);
falling = !multipleHitTest (manMC.positionPNT, gravityPNT, manMC.feetPNT, activeArray);
//trace("falling " + falling);
if (falling == true) {
//manMC.y = gravityPNT.y;
manMC.moveToPoint(gravityPNT);
//either jumping or falling
if ( manMC.verticalVelocityNUM == 0) {
manMC.moveModeSTR = "falling";
}else {
manMC.moveModeSTR = "jumping";
}
} else {
manMC.moveModeSTR = "walking";
}
}
//======================================================================
//======================================================================
//add when declared
//varobstacles:Array = new Array (block0MC, block1MC, block2MC);
//declared and then new instance
//var obstacles:Array;
//obstacles = new Array (block6MC);
//declares and create empty array
/*var obstacles:Array = new Array();
//push adds an item to the front of an array
obstacles.push (block0MC); // add instance names of display objects (or other data)
obstacles.push (block1MC);
obstacles.push (block2MC);
obstacles.push (block3MC);*/
//trace("length of list" + obstacles.length);
//trace(obstacles[0]); //access first element of array
//trace( block0MC["x"] );// acessing x property using different method
function multipleHitTest( position:Point, destination:Point, offset:Point, targets:Array):Boolean { //these are ARGUMENTS
//track hittest true or false
var returnBOOL:Boolean = new Boolean (false);
// cap length of loop - ie.e how many iterations?
var limit:int = new int ( targets.length );// obstacles.length is 3 items long
//the "counter", increases or decreases each time
var i:int;
//chunks =
//start counter at 0;
// loop while counter is less than limit;
// increment counter by 1 each looop;
for( i=0; i<limit; i++) {
//will access each item in array, as "i" is an integer
//obstacles[1];
//because it's targeted as a movieclip we can ask it's name
//this is 'reference variable'
//we are creating an 'alias' of the item in the list
var testAgainstObject:DisplayObject = targets[i];
//track direction
var moveDirection:String;
//only hit test things we're moving towards...
if (position.x < destination.x) { //if we're moving right
moveDirection = new String( "right" );
} else if (position.x > destination.x) {//else if we're moving left
moveDirection = new String( "left" );
}
//
if(
(moveDirection == "right" && targets[i].x >= position.x && destination.x >= targets[i].x)
||//or
(moveDirection == "left" && targets[i].x <= position.x && destination.x <= (targets[i].x + targets[i].width) )
) {//obstacle is to the right
// obstacle moving right
// moving right
}
//create a copy of 'destination'
var offsetDestination:Point = destination.clone();
//apply our offset provided by our character limbs
offsetDestination.offset(offset.x, offset.y);
//if point is colliding with list item
//if( testAgainstObject.hitTestPoint (destination.x, destination.y) ) { //REMOVED FOR TESTING
if( testAgainstObject.hitTestPoint (offsetDestination.x, offsetDestination.y) ) {
//trace("collisiondetected " + targets[i].name);
returnBOOL = true;
} else {
//trace("no collision");
//do nothing if flase, as it would contradict a 'true' value set earlier in the loop
}
}
return (returnBOOL); //tesing only
}
//declares and create empty array
var blackBlocks:Array = new Array();
//push adds an item to the front of an array
blackBlocks.push (block0MC); // add instance names of display objects (or other data)
blackBlocks.push (block1MC);
blackBlocks.push (blackbarrier);
blackBlocks.push (blackbarrier2);
blackBlocks.push (blackbarrier3);
blackBlocks.push (blackbarrier4);
blackBlocks.push (blackbarrier5);
var whiteBlocks:Array = new Array();
//push adds an item to the front of an array
whiteBlocks.push (block2MC);
whiteBlocks.push (block3MC);
whiteBlocks.push (whitebarrier);
whiteBlocks.push (whitebarrier2);
whiteBlocks.push (whitebarrier3);
whiteBlocks.push (whitebarrier4);
whiteBlocks.push (whitebarrier5);
var redBlocks:Array = new Array();
redBlocks.push (redblock1MC);
//var activeArray:Array = new Array (blackBlocks);
var activeArray:Array = blackBlocks;
//active.push (redblock1MC);
//Adds an event listener to the button component with the mouse click event.
//hide_btn.addEventListener(MouseEvent.CLICK, toggleBlocks);
//show_btn.addEventListener(MouseEvent.CLICK, showObject);
stage.addEventListener(KeyboardEvent.KEY_DOWN, toggleBlocks);
// start toggle blocks
function toggleBlocks (event:KeyboardEvent):void {
var i:int = 0;
var lim:int = activeArray.length;
if(event.keyCode == Keyboard.SPACE){
trace("Toggle Blocks");
blocksVisibility( activeArray , false );
if( activeArray == blackBlocks) {
activeArray = whiteBlocks;
}else{
activeArray = blackBlocks;
}
blocksVisibility( activeArray , true );
} // end IF
} // end toggle blocks
function blocksVisibility( arrARG:Array , visBOOL:Boolean ){
var i:int = 0;
var lim:int = arrARG.length;
for( i=0; i<lim; i++) {
arrARG[i].visible = visBOOL;
}
}
blocksVisibility( this.whiteBlocks , false );
blocksVisibility( this.redBlocks , false );
//blocksVisibility( this.redBlocks , false );
//======== red block button ========
// on collision trigger button and make red platform appear
/*
var myTimer:Timer = new Timer(5000,1);
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
function timerListener(e:TimerEvent):void {
//logo_mc.x+=40;
blocksVisibility( this.redBlocks , true );
//redBlock1MC:Array = true;
//activeArray:Array = redBlocks;
}
myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete);
function onComplete(e:TimerEvent):void {
//logo_mc.alpha=10;
//logo_mc.x=20;
blocksVisibility( this.redBlocks , false );
//redBlock1MC:Array = false;
//activeArray:Array = blackBlocks;
}
stage.addEventListener(KeyboardEvent.KEY_DOWN, onStart);
function onStart(e:KeyboardEvent):void {
if (e.keyCode == 88){
blocksVisibility( this.redBlocks , true );
//redBlock1MC:Array = true;
myTimer.start();
//logo_mc.alpha=.1;
//logo_mc.x=20;
}
} */
var myTimer:Timer = new Timer(10000, 1); // 1 second
myTimer.addEventListener(TimerEvent.TIMER, timedPlatform);
//myTimer.start();
function timedPlatform(event:TimerEvent):void {
trace("timedPlatform() called # " + getTimer() + " ms");
blocksVisibility( this.redBlocks , false );
/*if (manMC.hitTestObject(redButton)){
trace("Start Timer");
myTimer.start();
blocksVisibility( this.redBlocks , true );
activeArray = redBlocks;
} */
}
redButton.addEventListener(Event.ENTER_FRAME, startTimer);
function startTimer(event:Event):void{
//if (e.keyCode == 88){
//if (manMC, hitTest(redButton)) {
if (manMC.hitTestObject(redButton)) {
trace("Start Timer");
myTimer.start();
blocksVisibility( this.redBlocks , true );
activeArray = blackBlocks;
//activeArray = blackBlocks;
/*if( activeArray == blackBlocks) {
activeArray = redBlocks;
activeArray = blackBlocks;
}else{
activeArray = blackBlocks;
}*/
}
}
Instead of making activeArray a reference to whiteBlocks or blackBlocks, make it a separate Array object. Then, in toggleBlocks, you can
remove all elements from activeArray
add all elements from redBlocks to activeArray (optional depending on the current state)
add all elements from whiteBlocks OR blackBlocks to activeArray
This way activeArray will have all red blocks AND (all whiteBlocks OR all blackBlocks).

Resources