customer layout with drag-drop support - actionscript-3

Evtim wrote a greate customer FlowLayout, and Maxim also have a nice customer Horizontal Multiline Layout but they don't support drag-drop. When define customer
Layout which extends LayoutBase with drag-drop supporting, user should implements or override which functions?

I had worked out, following is my customer layout based on Horizontal Multiline Layouy of Maxim:
public class HorizontalMultilineLayout extends LayoutBase {
private var _rowCount:int = -1;
private var _columnCount:int = -1;
//container width
private var lastWidth:Number = -1;
private var _horizontalGap:Number = 6;
public function get horizontalGap():Number
{
return _horizontalGap;
}
public function set horizontalGap(value:Number):void
{
if (value == _horizontalGap)
return;
_horizontalGap = value;
invalidateTargetSizeAndDisplayList();
}
private var _verticalGap:Number = 6;
public function get verticalGap():Number
{
return _verticalGap;
}
public function set verticalGap(value:Number):void
{
if (value == _verticalGap)
return;
_verticalGap = value;
invalidateTargetSizeAndDisplayList();
}
private var _verticalAlign:String = VerticalAlign.TOP;
[Inspectable(category="General", enumeration="top,bottom,middle", defaultValue="top")]
public function get verticalAlign():String
{
return _verticalAlign;
}
public function set verticalAlign(value:String):void
{
if (_verticalAlign == value)
return;
_verticalAlign = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingLeft:Number = 0;
public function get paddingLeft():Number
{
return _paddingLeft;
}
public function set paddingLeft(value:Number):void
{
if (_paddingLeft == value)
return;
_paddingLeft = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingRight:Number = 0;
public function get paddingRight():Number
{
return _paddingRight;
}
public function set paddingRight(value:Number):void
{
if (_paddingRight == value)
return;
_paddingRight = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingTop:Number = 0;
public function get paddingTop():Number
{
return _paddingTop;
}
public function set paddingTop(value:Number):void
{
if (_paddingTop == value)
return;
_paddingTop = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingBottom:Number = 0;
public function get paddingBottom():Number
{
return _paddingBottom;
}
public function set paddingBottom(value:Number):void
{
if (_paddingBottom == value)
return;
_paddingBottom = value;
invalidateTargetSizeAndDisplayList();
}
override public function measure():void
{
if (lastWidth == -1) {
return;
}
var measuredWidth:Number = 0;
var measuredMinWidth:Number = 0;
var measuredHeight:Number = 0;
var measuredMinHeight:Number = 0;
var layoutTarget:GroupBase = target;
var n:int = layoutTarget.numElements;
var element:ILayoutElement;
var i:int;
var width:Number = layoutTarget.explicitWidth;
if (isNaN(width) && lastWidth != -1)
width = lastWidth;
if (isNaN(width)) // width is not defined by parent or user
{
// do not specify measuredWidth and measuredHeight to real
// values because in fact we can layout at any width or height
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
measuredWidth = Math.ceil(element.getPreferredBoundsWidth());
measuredHeight = Math.ceil(element.getPreferredBoundsHeight());
break;
}
measuredMinWidth = measuredWidth;
measuredMinHeight = measuredHeight;
}
else
{
// calculate lines based on width
var currentLineWidth:Number = 0;
var currentLineHeight:Number = 0;
var lineNum:int = 1;
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
var widthWithoutPaddings:Number = width - _paddingLeft - _paddingRight;
var elementWidth:Number = Math.ceil(element.getPreferredBoundsWidth());
if (currentLineWidth == 0 ||
currentLineWidth + _horizontalGap + elementWidth <= widthWithoutPaddings)
{
currentLineWidth += elementWidth + (currentLineWidth == 0 ? 0 : _horizontalGap);
currentLineHeight = Math.max(currentLineHeight, Math.ceil(element.getPreferredBoundsHeight()));
}
else
{
measuredHeight += currentLineHeight;
lineNum++;
currentLineWidth = elementWidth;
currentLineHeight = Math.ceil(element.getPreferredBoundsHeight());
}
}
measuredHeight += currentLineHeight;
if (lineNum > 1)
measuredHeight += _verticalGap * (lineNum - 1);
// do not set measuredWidth to real value because really we can
// layout at any width
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
measuredWidth =
measuredMinWidth = Math.ceil(element.getPreferredBoundsWidth());
break;
}
measuredMinHeight = measuredHeight;
}
layoutTarget.measuredWidth = measuredWidth + _paddingLeft + _paddingRight;
layoutTarget.measuredMinWidth = measuredMinWidth + _paddingLeft + _paddingRight;
layoutTarget.measuredHeight = measuredHeight + _paddingTop + _paddingBottom;
layoutTarget.measuredMinHeight = measuredMinHeight + _paddingTop + _paddingBottom;
}
override public function updateDisplayList(width:Number, height:Number):void
{
resetRowAndColumn();
var layoutTarget:GroupBase = target;
var n:int = layoutTarget.numElements;
var element:ILayoutElement;
var i:int;
// calculate lines based on width
var x:Number = _paddingLeft;
var y:Number = _paddingTop;
var maxLineHeight:Number = 0;
var elementCounter:int = 0;
var positions:Vector.<Point> = new Vector.<Point>();
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
var elementWidth:Number = Math.ceil(element.getPreferredBoundsWidth());
if (x == _paddingLeft || x + _horizontalGap + elementWidth <= width - _paddingRight) {
if (elementCounter > 0)
x += _horizontalGap;
positions[i] = new Point(x, y);
element.setLayoutBoundsSize(NaN, NaN);
maxLineHeight = Math.max(maxLineHeight, Math.ceil(element.getPreferredBoundsHeight()));
//calculate column count
if(!startNewLine) {
_columnCount++;
}
}else {
startNewLine = true;
_rowCount++;
x = _paddingLeft;
y += _verticalGap + maxLineHeight;
maxLineHeight = Math.ceil(element.getPreferredBoundsHeight());
positions[i] = new Point(x, y);
element.setLayoutBoundsSize(NaN, NaN);
}
x += elementWidth;
elementCounter++;
}
// verticalAlign and setLayoutBoundsPosition() for elements
var yAdd:Number = 0;
var yDifference:Number = height - (y + maxLineHeight + _paddingBottom);
if (_verticalAlign == VerticalAlign.MIDDLE)
yAdd = Math.round(yDifference / 2);
else if (_verticalAlign == VerticalAlign.BOTTOM)
yAdd = Math.round(yDifference);
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
var point:Point = positions[i];
point.y += yAdd;
element.setLayoutBoundsPosition(point.x, point.y);
}
// if width changed then height will change too - remeasure
if (lastWidth == -1 || lastWidth != width)
{
lastWidth = width;
invalidateTargetSizeAndDisplayList();
}
//trace("rowCount: ---", _rowCount, " ---columnCount: ----", _columnCount);
}
private var startNewLine:Boolean = false;
private function resetRowAndColumn():void {
var num:int = target.numElements;
if(num >0) {
_rowCount = 1;
_columnCount = 0;
} else {
_rowCount = _columnCount = 0;
}
startNewLine = false;
}
private function invalidateTargetSizeAndDisplayList():void
{
var g:GroupBase = target;
if (!g)
return;
g.invalidateSize();
g.invalidateDisplayList();
}
private function calculateDropCellIndex(x:Number, y:Number):Array {
var xStart:Number = x - paddingLeft;
var yStart:Number = y - paddingTop;
var column:int = Math.floor(xStart / (columnWidth + _horizontalGap));
var row:int = Math.floor(yStart / (rowHeight + _verticalGap));
// Check whether x is closer to left column or right column:
var midColumnLine:Number;
var midRowLine:Number;
midColumnLine = (column + 1) * (columnWidth + _horizontalGap) - _horizontalGap - columnWidth / 2;
// Mid-line is at the middle of the gap between the rows
midRowLine = (row + 1) * (rowHeight + _verticalGap) - _verticalGap / 2;
if (xStart > midColumnLine)
column++;
if (yStart > midRowLine)
row++;
// Limit row and column, if any one is too far from the drop location
// And there is white space
if (column > _columnCount || row > _rowCount)
{
row = _rowCount;
column = _columnCount;
}
if (column < 0)
column = 0;
if (row < 0)
row = 0;
if (row >= _rowCount)
row = _rowCount - 1;
return [row, column];
}
override protected function calculateDropIndex(x:Number, y:Number):int {
var result:Array = calculateDropCellIndex(x, y);
var row:int = result[0];
var column:int = result[1];
var index:int = row * _columnCount + column;
var count:int = target.numElements;
if (index > count) {
index = count;
}
return index;
}
private function get columnWidth():Number {
return typicalLayoutElement.getLayoutBoundsWidth();
}
private function get rowHeight():Number {
return typicalLayoutElement.getLayoutBoundsHeight();
}
//NOTE:we do NOT use contentWidth
override protected function calculateDropIndicatorBounds(dropLocation:DropLocation):Rectangle {
var result:Array = calculateDropCellIndex(dropLocation.dropPoint.x, dropLocation.dropPoint.y);
var row:int = result[0];
var column:int = result[1];
var count:int = target.numElements;
// The last row may be only partially full,
// don't drop beyond the last column.
if (row * _columnCount + column > count)
column = count - (_rowCount - 1) * _columnCount;
var x:Number;
var y:Number;
var dropIndicatorElement:IVisualElement;
var emptySpace:Number; // empty space between the elements
// Start with the dropIndicator dimensions, in case it's not
// an IVisualElement
var width:Number = dropIndicator.width;
var height:Number = dropIndicator.height;
emptySpace = (0 < _horizontalGap ) ? _horizontalGap : 0;
var emptySpaceLeft:Number = column * (columnWidth + _horizontalGap) - emptySpace;
// Special case - if we have negative gap and we're the last column,
// adjust the emptySpaceLeft
if (_horizontalGap < 0 && (column == _columnCount || count == dropLocation.dropIndex))
emptySpaceLeft -= _horizontalGap;
width = emptySpace;
height = rowHeight;
// Special case - if we have negative gap and we're not the last
// row, adjust the height
if (_verticalGap < 0 && row < _rowCount - 1)
height += _verticalGap + 1;
if (dropIndicator is IVisualElement)
{
dropIndicatorElement = IVisualElement(dropIndicator);
width = Math.max(Math.min(width,
dropIndicatorElement.getMaxBoundsWidth(false)),
dropIndicatorElement.getMinBoundsWidth(false));
}
x = emptySpaceLeft + Math.round((emptySpace - width) / 2) + paddingLeft;
// Allow 1 pixel overlap with container border
//x = Math.max(-1, Math.min(target.contentWidth - width + 1, x));
//NOTE:we do NOT use contentWidth
x = Math.max(-1, Math.min(target.width - width + 1, x));
y = row * (rowHeight + _verticalGap) + paddingTop;
return new Rectangle(x, y, width, height);
}
}

Related

Character keeps falling when it shouldn't be

I am doing an assessment task for school in as3 and I have made a feature that will let the character fall if it is not standing on top of something but the boolean variable falling keeps being on when it is not supposed to. I have figured out that what causes it to stop for a short time and then fall again past what it is standing on is that the velocity valuable increases past the floor it is on so I reset the velocity value in the code in another spot but that does not solve the issue of the falling boolean being true when it is not supposed too.
I have put a comment at the temporary solution it is in the gameEngine function in the if(falling) statement
// Imports
import flash.events.KeyboardEvent;
import flash.events.Event;
// Constants that can be edited
const grav:Number = 0.05;
const jumpPow:Number = 15;
const walkingDistance = 50;
// Variables
var jumpVel:Number = jumpPow;
var velocity:Number = 0;
var dt:Number = 0;
var movingCount:Number = 0;
var newX:Number = 0;
var newY:Number = 0;
var charCornerHit:Number = 0; // 0 = TL 1 = TR 2 = BR 3 = BL (clockwise)
var jumping:Boolean = false;
var falling:Boolean = false;
var movingRight:Boolean = false;
var movingLeft:Boolean = false;
var hitDetVal:Boolean = false; // false
var floorHit:Boolean = false;
var object1Hit:Boolean = false;
var cCnrTL:Array = new Array(char.x, char.y);
var cCnrTR:Array = new Array(char.x + char.width, char.y);
var cCnrBR:Array = new Array(char.x + char.width, char.y + char.height);
var cCnrBL:Array = new Array(char.x, char.y + char.height);
var charCorners:Array = new Array(cCnrTL, cCnrTR, cCnrBR, cCnrBL); // Clockwise
addEventListener(Event.ENTER_FRAME, gameEngine);
function gameEngine(evt:Event):void{
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveCharacter);
charCorners[0][0] = char.x;
charCorners[0][1] = char.y;
charCorners[1][0] = char.x + char.width;
charCorners[1][1] = char.y;
charCorners[2][0] = char.x + char.width;
charCorners[2][1] = char.y + char.height;
charCorners[3][0] = char.x;
charCorners[3][1] = char.y + char.height;
//trace(char.y);
// Check if char is standing on something
// Supposed to only check when character is not falling
if (falling == false) {
//trace(char.y + char.height + 1)
hitDetection(0, char.y + char.height + 1)
if (hitDetVal == false) {
falling = true;
hitDetVal = false;
trace("not standing");
}
else {trace("standing on something");}
}
// Move char loop
if (movingRight){
if (movingCount == walkingDistance) {
movingCount = 0
movingRight = false;
} else {
char.x+=1
movingCount += 1;
}
} else if (movingLeft) {
if (movingCount == walkingDistance) {
movingCount = 0
movingLeft = false;
} else {
char.x-=1
movingCount += 1;
}
}
//trace(velocity)
if (falling) {
dt += 1
velocity = velocity + grav*dt
hitDetection(0, velocity)
if (hitDetVal) {
falling = false;
hitDetVal = false;
// TEMPORARY SOLUTION - Stopped character from falling past the floor but still ran falling condition
//velocity = 0;
//dt = 0;
if (floorHit) {
if (char.y < floor.y){
char.y = floor.y - char.height;
floorHit = false;
trace("Char Stopped falling")
}
} else if (object1Hit) {
if (char.y - char.height < object1.y){
char.y = object1.y - char.height;
object1Hit = false;
trace("Char Stopped falling")
}
}
} else {
char.y += velocity;
}
} else {
if (jumping) {
}
velocity = 0;
dt = 0;
}
}
function moveCharacter(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.LEFT){
if(movingRight){
movingRight = false;
trace("STOPPED")
} else {
movingCount = 0
movingLeft = true;
trace("LEFT");
}
}
if(event.keyCode == Keyboard.RIGHT){
if(movingLeft){
movingLeft = false;
trace("STOPPED")
} else {
movingCount = 0
movingRight = true;
trace("RIGHT");
}
}
if(event.keyCode == Keyboard.UP){
jumping = true;
trace("UP");
}
}
function hitDetection(distX:Number, distY:Number) {
for (var i:uint = 0; i < charCorners.length; i++) {
newX = charCorners[i][0] + distX;
newY = charCorners[i][1] + distY;
if (floor.hitTestPoint(newX, newY, true)){
hitDetVal = true;
floorHit = true;
charCornerHit = i;
break;
} else if (object1.hitTestPoint(newX, newY, true)){
hitDetVal = true;
object1Hit = true;
charCornerHit = i;
break;
}
}
}```
It was a simple logic error occurring here
hitDetection(0, char.y + char.height + 1)
I had made adjustments for the location by adding char.y to the value when my hitDetection function already made an adjustment to each corner of my character. Only took ~3 hours to figure out

Maze solving algorithm using p5.js

I have generated a maze using depth first search - recursive backtracker algorithm. I also want to solve, but not getting idea about how to start solving my maze.
I am using p5.js to create my maze, and want to solve my already generated maze.
This is my javascript code for generating maze. You might want to add p5.js cdn in your html file if you want to run this code.
var cols, rows;
var w = 40;
var grid = [];
var current;
var stack = [];
function setup() {
createCanvas(400,400);
cols = floor(width/w);
rows = floor(height/w);
frameRate(5);
for (var j = 0; j<rows; j++){
for (var i = 0; i < cols; i++) {
var cell = new Cell(i,j);
grid.push(cell);
}
}
current = grid[0];
}
function draw(){
background(51);
for (var i = 0; i<grid.length; i++){
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbours();
if (next) {
next.visited = true;
stack.push(current);
removeWalls(current,next);
current = next;
}
else if(stack.length > 0){
current = stack.pop();
}
}
function index(i,j){
if (i < 0 || j < 0 || i > cols-1 || j > rows-1) {
return -1;
}
return i + j * cols;
}
function Cell(i,j){
this.i = i;
this.j = j;
this.walls = [true,true,true,true];
this.visited = false;
this.checkNeighbours = function(){
var neighbours = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top && !top.visited){
neighbours.push(top);
}
if (right && !right.visited){
neighbours.push(right);
}
if (bottom && !bottom.visited){
neighbours.push(bottom);
}
if (left && !left.visited){
neighbours.push(left);
}
if (neighbours.length > 0){
var r = floor(random(0, neighbours.length));
return neighbours[r];
}
else{
return undefined;
}
}
this.highlight = function(){
x = this.i*w;
y = this.j*w;
noStroke();
fill(0,0,255,200);
rect(x,y,w,w);
}
this.show = function(){
x = this.i*w;
y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x ,y ,x+w ,y);
}
if (this.walls[1]){
line(x+w ,y ,x+w ,y+w);
}
if (this.walls[2]){
line(x+w ,y+w ,x ,y+w);
}
if (this.walls[3]){
line(x ,y+w ,x ,y)
}
if (this.visited) {
noStroke();
fill(255,0,255,100);
rect(x,y,w,w);
}
}
}
function removeWalls(a,b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
}
else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
}
else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
There are many algorithmsfor solving mazes. One simple way to solve mazes created with the recursive backtracker algorithm is to keep track of the solution as the maze is being generated.
Make the first cell the starting cell and push it onto the solution stack
Make the last cell the goal cell
While the solution stack does not contain the goal cell
if the next neighbor is un-visited push it onto the solution stack
if a cell has no next neighbor pop the solution stack as we are backtracking
When the goal cell is pushed onto the solution stack mark the solution complete
Adapting the questions code so that it also implements the solution algorithm we have:
var cols, rows;
var w = 40;
var grid = [];
var current;
var stack = [];
var solution = [];
var goal;
var solutionComplete;
function setup() {
createCanvas(400,400);
cols = floor(width/w);
rows = floor(height/w);
frameRate(5);
for (var j = 0; j<rows; j++){
for (var i = 0; i < cols; i++) {
var cell = new Cell(i,j);
grid.push(cell);
}
}
current = grid[0];
grid[grid.length - 1].goal = true;
solution.push(grid[0]);
}
function draw(){
background(51);
for (var i = 0; i<grid.length; i++){
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbours();
if (next) {
if (!next.visited){
if (!solutionComplete){
solution.push(next);
if (next.goal){
solutionComplete = true;
}
}
}
next.visited = true;
stack.push(current);
removeWalls(current,next);
current = next;
}
else if(stack.length > 0){
current = stack.pop();
if (!solutionComplete){
solution.pop();
}
}
if (solutionComplete){
for (let i = 0; i < solution.length; i++){
solution[i].solutionCell = true;
}
}
}
function index(i,j){
if (i < 0 || j < 0 || i > cols-1 || j > rows-1) {
return -1;
}
return i + j * cols;
}
function Cell(i,j){
this.i = i;
this.j = j;
this.walls = [true,true,true,true];
this.visited = false;
this.goal = false;
this.solutionCell = false;
this.checkNeighbours = function(){
var neighbours = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top && !top.visited){
neighbours.push(top);
}
if (right && !right.visited){
neighbours.push(right);
}
if (bottom && !bottom.visited){
neighbours.push(bottom);
}
if (left && !left.visited){
neighbours.push(left);
}
if (neighbours.length > 0){
var r = floor(random(0, neighbours.length));
return neighbours[r];
}
else{
return undefined;
}
}
this.highlight = function(){
x = this.i*w;
y = this.j*w;
noStroke();
fill(0,0,255,200);
rect(x,y,w,w);
}
this.show = function(){
x = this.i*w;
y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x ,y ,x+w ,y);
}
if (this.walls[1]){
line(x+w ,y ,x+w ,y+w);
}
if (this.walls[2]){
line(x+w ,y+w ,x ,y+w);
}
if (this.walls[3]){
line(x ,y+w ,x ,y)
}
if (this.goal){
noStroke();
fill(0,255,0,100);
rect(x,y,w,w);
}
else if (this.solutionCell){
noStroke();
fill(255,0,0,100);
rect(x,y,w,w);
}else if(this.visited) {
noStroke();
fill(255,0,255,100);
rect(x,y,w,w);
}
}
}
function removeWalls(a,b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
}
else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
}
else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
It would not be difficult to break the maze generation and solution implementations apart so that the maze is completely generated before the solution is determined but unless there is a restriction that forces us to solve a completed maze it makes sense to build the solution along with the maze.
Sorry about this this is very related but coding train a coding youtuber made a maze generating algorithm, but i don't know if it used depth first search

mouse background image with canvas in html5

I want something like this site , look at mouse background in header section:
Link
when i check page source i found this:
<canvas id="header-canvas" width="1360" height="676"></canvas>
Take a look at source code and identify which JS plugins are being used.
I have pulled it apart and found its using green sock https://greensock.com
Take a look at http://codepen.io/elliottgg/pen/YpQBpZ
Scroll down to line 40 to see where the magic is happening
(function() {
var width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
// Main
initHeader();
initAnimation();
addListeners();
function initHeader() {
width = window.innerWidth;
height = window.innerHeight;
target = {x: width/2, y: height/2};
largeHeader = document.getElementById('header');
largeHeader.style.height = height+'px';
canvas = document.getElementById('header-canvas');
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext('2d');
// create points
points = [];
for(var x = 0; x < width; x = x + width/20) {
for(var y = 0; y < height; y = y + height/20) {
var px = x + Math.random()*width/20;
var py = y + Math.random()*height/20;
var p = {x: px, originX: px, y: py, originY: py };
points.push(p);
}
}
// for each point find the 5 closest points
for(var i = 0; i < points.length; i++) {
var closest = [];
var p1 = points[i];
for(var j = 0; j < points.length; j++) {
var p2 = points[j]
if(!(p1 == p2)) {
var placed = false;
for(var k = 0; k < 5; k++) {
if(!placed) {
if(closest[k] == undefined) {
closest[k] = p2;
placed = true;
}
}
}
for(var k = 0; k < 5; k++) {
if(!placed) {
if(getDistance(p1, p2) < getDistance(p1, closest[k])) {
closest[k] = p2;
placed = true;
}
}
}
}
}
p1.closest = closest;
}
// assign a circle to each point
for(var i in points) {
var c = new Circle(points[i], 2+Math.random()*2, 'rgba(255,255,255,0.8)');
points[i].circle = c;
}
}
// Event handling
function addListeners() {
if(!('ontouchstart' in window)) {
window.addEventListener('mousemove', mouseMove);
}
window.addEventListener('scroll', scrollCheck);
window.addEventListener('resize', resize);
}
function mouseMove(e) {
var posx = posy = 0;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
function scrollCheck() {
if(document.body.scrollTop > height) animateHeader = false;
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
largeHeader.style.height = height+'px';
canvas.width = width;
canvas.height = height;
}
// animation
function initAnimation() {
animate();
for(var i in points) {
shiftPoint(points[i]);
}
}
function animate() {
if(animateHeader) {
ctx.clearRect(0,0,width,height);
for(var i in points) {
// detect points in range
if(Math.abs(getDistance(target, points[i])) < 4000) {
points[i].active = 0.3;
points[i].circle.active = 0.6;
} else if(Math.abs(getDistance(target, points[i])) < 20000) {
points[i].active = 0.1;
points[i].circle.active = 0.3;
} else if(Math.abs(getDistance(target, points[i])) < 40000) {
points[i].active = 0.02;
points[i].circle.active = 0.1;
} else {
points[i].active = 0.0;
points[i].circle.active = 0.0;
}
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}
function shiftPoint(p) {
TweenLite.to(p, 1+1*Math.random(), {x:p.originX-50+Math.random()*100,
y: p.originY-50+Math.random()*100, ease:Circ.easeInOut,
onComplete: function() {
shiftPoint(p);
}});
}
// Canvas manipulation
function drawLines(p) {
if(!p.active) return;
for(var i in p.closest) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.closest[i].x, p.closest[i].y);
ctx.strokeStyle = 'rgba(255,255,255,'+ p.active+')';
ctx.stroke();
}
}
function Circle(pos,rad,color) {
var _this = this;
// constructor
(function() {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
})();
this.draw = function() {
if(!_this.active) return;
ctx.beginPath();
ctx.arc(_this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'rgba(255,255,255,'+ _this.active+')';
ctx.fill();
};
}
// Util
function getDistance(p1, p2) {
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}

How to create a hierarchy Menu/Icon

I dont know if the question represent exactly what i want to ask, but i could`t figure how else to name what i want to do.
So im trying to create a 3x3 grid and in every cell i have a image.
I can click on any of the 2nd cell of a column only if the 1st of the same column has been clicked before that.What i mean is
i can click cell No:5 only if 4 has been clicked before that
i can click cell No:9 only if 8 and 7 has been clicked before that
i can click cell No:1,4,7 anytime.
and also when they are clicked their alpha gets to 0.1 (so i know that the cell has been clicked.)
currently i the logic for creating the grid and changing the alpha of any object i click but i don`t have the logic for the hierarchy.
public function Main()
{
var index:int = 0
var col:int = 3
var row:int = 3
for (var i:int = 0; i < row; i++)
{
for (var j:int = 0; j < col; j++)
{
var cls:Class = Class(getDefinitionByName("Bitmap" + (index + 1) ));
myImage = new Bitmap( new cls() );
var myImage_mc:MovieClip = new MovieClip();
myImage_mc.addChild(myImage)
myImage_mc.x = 100 + i * (myImage_mc.width + 10)
myImage_mc.y = 100 + j * (myImage_mc.height + 10)
this.addChild(myImage_mc);
myImage_mc.mouseEnabled = true
myImage_mc.addEventListener(MouseEvent.CLICK, onClick);
index++
}
}
}
private function onClick(ev:MouseEvent):void
{
trace(ev.target.name)
ev.currentTarget.alpha = 0.1;
}
Another way is to use a link variable to link element.
public class BitmapButton extends Sprite {
public var next:BitmapButton = null;
}
public class Main extends Sprite {
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
var index:int = 0
var col:int = 3
var row:int = 3
for (var j:int = 0; j < col; j++)
{
var lastRowElement:BitmapButton = null;
for (var i:int = 0; i < row; i++)
{
var bmpd:BitmapData = new BitmapData( 100, 100, false, Math.floor( 0xff + Math.random() * 0xffffff ) );
var myImage:Bitmap = new Bitmap( bmpd );
var myImage_mc:BitmapButton = new BitmapButton();
myImage_mc.addChild(myImage)
myImage_mc.x = 100 + j * (myImage_mc.width + 10);
myImage_mc.y = 100 + i * (myImage_mc.height + 10);
myImage_mc.name = i + "_" + j;
this.addChild(myImage_mc);
myImage_mc.mouseChildren = false;
myImage_mc.mouseEnabled = false;
myImage_mc.addEventListener(MouseEvent.CLICK, onClick);
if ( lastRowElement == null ) {
myImage_mc.mouseEnabled = true;
} else {
lastRowElement.next = myImage_mc;
}
lastRowElement = myImage_mc;
index++
}
}
}
private function onClick(ev:MouseEvent):void
{
trace(ev.target.name)
if ( ev.target is BitmapButton ) {
var btn:BitmapButton = ev.target as BitmapButton;
ev.currentTarget.alpha = 0.1;
if( btn.next != null ) {
btn.next.mouseEnabled = true;
}
}
}
}
Something like this
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.utils.Dictionary;
public class Grid extends Sprite
{
private var _box : Dictionary = new Dictionary;
private var _conditions : Dictionary = new Dictionary;
private var _clicked : Dictionary = new Dictionary;
public function Grid()
{
var index:int = 0
var col:int = 3
var row:int = 3
for (var i:int = 0; i < row; i++)
{
for (var j:int = 0; j < col; j++)
{
//var cls:Class = Class(getDefinitionByName("Bitmap" + (index + 1) ));
//myImage = new Bitmap( new cls() );
var myImage_mc:MovieClip = new MovieClip();
//myImage_mc.addChild(myImage)
_box[ index ] = myImage_mc;
// Conditions for the 2nd and 3nd line etc.
if( i != 0 )
_conditions[ myImage_mc ] = _box[ index - col ];
// ------ DEBUG
myImage_mc.graphics.beginFill( 0xFFFFFF * Math.random() );
myImage_mc.graphics.drawRect(0,0,100,100);
myImage_mc.graphics.endFill();
// ------ END DEBUG
// Note i / j are invert from your example
myImage_mc.x = 100 + j * (myImage_mc.width + 10);
myImage_mc.y = 100 + i * (myImage_mc.height + 10);
this.addChild(myImage_mc);
myImage_mc.mouseEnabled = true
myImage_mc.addEventListener(MouseEvent.CLICK, onClick);
index++
}
}
}
protected function onClick(ev:MouseEvent):void
{
var neededClickElement : MovieClip = _conditions[ ev.currentTarget ];
// Check if we need an active movieclip before
if( neededClickElement != null && ! _clicked[ neededClickElement ] )
return;
_clicked[ ev.currentTarget ] = true;
ev.currentTarget.alpha = 0.1;
}
}
}

Getting 'TypeError' response for Gmail Meter Script.

I'm getting the following error:
"TypeError: Cannot read property "0.0" from null. (line227)"
I've been looking through the documentation here: https://developers.google.com/apps-script/articles/gmail-stats#section1
But haven't been able to troubleshoot.
Line227:
variables.dayOfEmailsReceived[day]++;
Sorry, new to stackoverflow but any help greatly appreciated.
Reference:
var BATCH_SIZE = 50;
var ss = SpreadsheetApp.getActiveSpreadsheet();
// If you are using several email addresses, list them in the following variable
// - eg 'romain.vialard#gmail.com,romain.vialard#example.com'
var aliases = 'romain.vialard#euromed-management.com';
function activityReport() {
var status = ScriptProperties.getProperty("status");
if (status == null) {
// If the script is triggered for the first time, init
init_();
}
else {
status = Utilities.jsonParse(status);
var previousMonth = new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1).getMonth();
if (status == null || (status.customReport == false && status.previousMonth != previousMonth)) {
init_();
fetchEmails_(status.customReport);
}
// If report not sent, continue to work on the report
else if (status.reportSent == "no") {
fetchEmails_(status.customReport);
}
}
}
function fetchEmails_(customReport) {
var variables = Utilities.jsonParse(ScriptProperties.getProperty("variables"));
if (!customReport) {
var query = "after:" + variables.year + "/" + (variables.previousMonth) + "/31";
query += " before:" + new Date().getYear() + "/" + (variables.previousMonth + 1) + "/31";
}
else {
var query = "after:" + Utilities.formatDate(new Date(variables.startDate), variables.userTimeZone, 'yyyy/MM/dd');
query += " before:" + Utilities.formatDate(new Date(variables.endDate), variables.userTimeZone, 'yyyy/MM/dd');
}
query += " in:anywhere -label:sms -label:call-log -label:chats -label:spam -filename:ics";
query += " -from:maestro.bounces.google.com -from:unified-notifications.bounces.google.com -from:docs.google.com";
query += " -from:group.calendar.google.com -from:apps-scripts-notifications#google.com";
query += " -from:sites.bounces.google.com -from:noreply -from:notify -from:notification";
var startDate = new Date(variables.startDate).getTime();
var endDate = new Date(variables.endDate).getTime();
var conversations = GmailApp.search(query, variables.range, BATCH_SIZE);
variables.nbrOfConversations += conversations.length;
var sheets = ss.getSheets();
var people = sheets[0].getDataRange().getValues();
var record = [];
for (var i = 0; i < conversations.length; i++) {
Utilities.sleep(1000);
var conversationId = conversations[i].getId();
var firstMessageSubject = conversations[i].getFirstMessageSubject();
var starred = false;
if (conversations[i].hasStarredMessages()) {
variables.nbrOfConversationsStarred++;
starred = true;
}
var important = false;
if (conversations[i].isImportant()) {
variables.nbrOfConversationsMarkedAsImportant++;
important = true;
}
var location = "";
var labels = conversations[i].getLabels();
var nbrOfLabels = labels.length;
if (nbrOfLabels == 0) {
if (conversations[i].isInInbox()) {
variables.nbrOfConversationsInInbox++;
location += "Inbox,";
}
else if (conversations[i].isInTrash()) {
variables.nbrOfConversationsInTrash++;
location += "Trashed,";
}
else {
variables.nbrOfConversationsArchived++;
location = "Archived";
}
}
else {
variables.nbrOfConversationsInLabels++;
for (var j = 0; j < nbrOfLabels; j++) {
location += labels[j].getName() + ",";
}
}
var youReplied = false;
var youStartedTheConversation = false;
var someoneAnswered = false;
var messages = conversations[i].getMessages();
var nbrOfMessages = messages.length;
variables.nbrOfEmailsPerConversation[nbrOfMessages]++;
for (var j = 0; j < 10; j++) {
if (variables.topThreads[j][1] < nbrOfMessages) {
variables.topThreads.splice(j, 0, [firstMessageSubject, nbrOfMessages]);
variables.topThreads.pop();
j = 10;
}
}
var timeOfFirstMessage = 0;
var waitingTime = 0;
for (var j = 0; j < nbrOfMessages; j++) {
var process = true;
var date = messages[j].getDate();
var month = date.getMonth();
if (customReport) {
if (date.getTime() < startDate || date.getTime() > endDate) {
process = false;
}
}
else {
if (month != variables.previousMonth) {
process = false;
}
}
if (process) {
//////////////////////////////////
// Fetch sender of each emails
//////////////////////////////////
var from = messages[j].getFrom().replace(/"[^"]*"/g,'');
if (from.match(/</) != null) {
from = from.match(/<([^>]*)/)[1];
}
var time = Utilities.formatDate(date, variables.userTimeZone, "H");
var day = Utilities.formatDate(date, variables.userTimeZone, "d") - 1;
// Use function from Utilities file
variables = countSendsPerDaysOfWeek_(variables, date, from);
var body = messages[j].getBody();
// Use function from Utilities file
var resultsFromCalcMessagesLength = calcMessagesLength_(variables, body, from);
variables = resultsFromCalcMessagesLength[0];
var messageLength = resultsFromCalcMessagesLength[1];
var cc = messages[j].getCc().replace(/"[^"]*"/g,'').split(/,/);
for (var k = 0; k < cc.length; k++) {
if (cc[k].match(/</) != null) {
cc[k] = cc[k].match(/<([^>]*)/)[1];
}
}
var reg = new RegExp(from, 'i');
if ((variables.user + aliases).search(reg) != -1) {
if (j == 0) {
youStartedTheConversation = true;
timeOfFirstMessage = date.getTime();
}
if (j > 0 && !youStartedTheConversation) {
if (!youReplied) {
youReplied = true;
// Use function from Utilities file
variables = calcWaitingTime_(variables, date, timeOfFirstMessage, youStartedTheConversation);
}
}
variables.nbrOfEmailsSent++;
variables.timeOfEmailsSent[time]++;
variables.dayOfEmailsSent[day]++;
if (customReport) {
variables.monthOfEmailsSent[month]++;
}
var sharedWithTheOutsideWorld = false;
var to = messages[j].getTo().replace(/"[^"]*"/g,'').split(/,/);
for (var k = 0; k < to.length; k++) {
if (to[k].match(/</) != null) {
to[k] = to[k].match(/<([^>]*)/)[1];
}
if (to[k].search(variables.companyname) == -1){
sharedWithTheOutsideWorld = true;
}
var found = false;
for (var l = 0; l < people.length; l++) {
if (to[k] == people[l][0]) {
people[l][2]++;
found = true;
}
}
if (!found) {
people.push([to[k], 0, 1]);
}
}
if(!sharedWithTheOutsideWorld){
variables.sharedInternally++;
}
}
else {
if (j == 0) {
timeOfFirstMessage = date.getTime();
}
else if (youStartedTheConversation && !someoneAnswered) {
someoneAnswered = true;
// Use function from Utilities file
variables = calcWaitingTime_(variables, date, timeOfFirstMessage, youStartedTheConversation);
}
var found = false;
for (var k = 0; k < people.length; k++) {
if (from == people[k][0]) {
people[k][1]++;
found = true;
}
}
if (!found) {
people.push([from, 1, 0]);
}
var to = messages[j].getTo().replace(/"[^"]*"/g,'');
var checkSendToYou = false;
var aliasesTemp = new Array(variables.user).concat(aliases.split(','));
for(var k = 0; k < aliasesTemp.length; k++){
if(aliasesTemp[k] != ''){
var reg = new RegExp(aliasesTemp[k], 'i');
if (to.search(reg) != -1) {
checkSendToYou = true;
}
}
}
if(checkSendToYou)variables.sentDirectlyToYou++;
var sharedWithTheOutsideWorld = false;
to = to.split(/,/);
for (var k = 0; k < to.length; k++) {
if (to[k].match(/</) != null) {
to[k] = to[k].match(/<([^>]*)/)[1];
}
if (to[k].search(variables.companyname) == -1){
sharedWithTheOutsideWorld = true;
}
}
if(sharedWithTheOutsideWorld == false && from.search(variables.companyname) != -1){
variables.sharedInternally++;
}
variables.nbrOfEmailsReceived++;
variables.timeOfEmailsReceived[time]++;
variables.dayOfEmailsReceived[day]++;
if (customReport) {
variables.monthOfEmailsReceived[month]++;
}
}
if (to != null) {
to = to.toString();
}
if (cc != null) {
cc = cc.toString();
}
var dayOfWeek = Utilities.formatDate(date, variables.userTimeZone, "EEEE");
record.push([date, dayOfWeek, firstMessageSubject, from, to, cc, messageLength, location]);
}
}
if (youStartedTheConversation) {
variables.nbrOfConversationsStartedByYou++;
}
if (youReplied) {
variables.nbrOfConversationsYouveRepliedTo++;
}
}
variables.range += BATCH_SIZE;
ScriptProperties.setProperty("variables", Utilities.jsonStringify(variables));
sheets[0].getRange(1, 1, people.length, 3).setValues(people);
if (record[0] != undefined && sheets[1].getMaxRows() < 38000) {
sheets[1].getRange(sheets[1].getLastRow() + 1, 1, record.length, record[0].length).setValues(record);
}
if (conversations.length < BATCH_SIZE) {
sendReport_(variables);
}
}
It's a timezone issue. Go to your Gmail Meter file on your Google Drive account and then:
File -> Spreadsheet settings...
Change the timezone to your current time zone. I assume your Gmail account changes timezones automatically, but not your spreadsheet.
The error indicates that and day is 0 variables.dayOfEmailsReceived is null. It's not possible to refer to an index of null. variables.dayOfEmailsReceived is created in the init_() function, so check that you have that function defined and are using it correctly.