/*
* Copyright (C) 2012 Gyver
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.gyver.matrixmover.generator;
import com.gyver.matrixmover.core.Controller;
import com.gyver.matrixmover.core.MatrixData;
import com.gyver.matrixmover.generator.enums.GeneratorName;
import com.gyver.matrixmover.generator.enums.ShapeDirection;
import com.gyver.matrixmover.generator.enums.ShapeObject;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Generator calculating objects (shapes) in various (random) ways.
*
* @author Gyver
*/
public class Shapes extends ObjectsContainingGenerator {
private int objectCount = 0;
private int speed = 0;
private int nextColor = 0;
private int size = 0;
private int alive = 0;
private int fade = 0;
private int expand = 0;
private double updatesToNextDrop = 0;
private double dropUpdatesDone = 0;
private ShapeObject objectShape = null;
private ShapeDirection objectDirection = null;
private ArrayList<ShapeObjects> objectList = null;
/**
* Instantiates a new shapes generator.
*
* @param md the MatrixData of the matrix
*/
public Shapes(MatrixData md) {
super(GeneratorName.SHAPES, md, null);
this.objectCount = 1;
this.speed = 240;
this.size = 2;
this.alive = 15;
this.fade = 15;
this.expand = 2;
this.objectShape = ShapeObject.SQUARE_EMPTY;
this.objectDirection = ShapeDirection.EXPLODE;
calculateUpdateRate();
this.objectList = new ArrayList<ShapeObjects>();
}
@Override
public void update() {
//if its time, create a new ShapeObject
dropUpdatesDone++;
while (dropUpdatesDone >= updatesToNextDrop) {
dropUpdatesDone = dropUpdatesDone - updatesToNextDrop;
//paint 'objectCount' new drops
for (int i = 0; i < objectCount; i++) {
int x = (int) Math.floor(Math.random() * internalBufferWidth);
int y = (int) Math.floor(Math.random() * internalBufferHeight);
nextColor = ((nextColor + 1) % colorMap.size());
int color = this.getColor(nextColor);
ShapeObjects obj = new ShapeObjects(x, y, color, size, alive, getExpand(), fade);
objectList.add(obj);
}
}
Arrays.fill(this.internalBuffer, 0);
ArrayList<ShapeObjects> objectsToRemove = new ArrayList<ShapeObjects>();
for (ShapeObjects so : objectList) {
so.update();
if (so.isDead()) {
objectsToRemove.add(so);
}
}
if (!objectsToRemove.isEmpty()) {
objectList.removeAll(objectsToRemove);
}
}
@Override
public void init() {
// nothing to do here
}
/**
* @return the objectShape
*/
public ShapeObject getObjectShape() {
return objectShape;
}
/**
* @return the objectDirection
*/
public ShapeDirection getObjectDirection() {
return objectDirection;
}
/**
* @param objectShape the objectShape to set
*/
public void setObjectShape(ShapeObject objectShape) {
this.objectShape = objectShape;
}
/**
* @param objectDirection the objectDirection to set
*/
public void setObjectDirection(ShapeDirection objectDirection) {
this.objectDirection = objectDirection;
}
/**
* @return the objectCount
*/
public int getObjectCount() {
return objectCount;
}
/**
* @param objectCount the oubjectCount to set
*/
public void setObbjectCount(int objectCount) {
this.objectCount = objectCount;
}
/**
* @return the speed
*/
public int getSpeed() {
return speed;
}
/**
* @param speed the speed to set
*/
public void setSpeed(int speed) {
this.speed = speed;
calculateUpdateRate();
}
/**
* @return the size
*/
public int getSize() {
return size + 1;
}
/**
* @param size the size to set
*/
public void setSize(int size) {
this.size = size - 1;
}
/**
* @return the alive
*/
public int getAlive() {
return alive;
}
/**
* @param alive the alive to set
*/
public void setAlive(int alive) {
this.alive = alive;
}
/**
* @return the fade
*/
public int getFade() {
return fade;
}
/**
* @param fade the fade to set
*/
public void setFade(int fade) {
this.fade = fade;
}
/**
* @return the expand
*/
public int getExpand() {
return expand;
}
/**
* @param expand the expand to set
*/
public void setExpand(int expand) {
this.expand = expand;
}
private void calculateUpdateRate() {
int fps = Controller.getControllerInstance().getFps();
updatesToNextDrop = (fps / (speed / 60F));
}
private class ShapeObjects implements Serializable {
private int x, y, color, size, alive, expand, fade;
private boolean dead = false;
private double expandIndex, expandHit;
private int usedColor;
/**
* A ShapeObject represents one shape of the generator
* @param x the x coordinate (center of shape)
* @param y the y coordinate (center of shape)
* @param color the color of the shape
* @param size the initial size of the shape
* @param alive the number of frames the shape is shown
* @param expand the amount of pixels the shape expands in each direction over its livetime
* @param fade the number of frames, the shape is fading out (last frames of lifetime)
*/
private ShapeObjects(int x, int y, int color, int size, int alive, int expand, int fade) {
this.x = x;
this.y = y;
this.color = color;
switch (getObjectDirection()) {
case EXPLODE:
this.size = size;
break;
case IMPODE:
switch (getObjectShape()) {
case SQUARE_EMPTY:
case SQUARE_FILLED:
case LINE_HORIZONTAL:
case LINE_VERTICAL:
this.size = size + (2 * expand);
break;
case CIRCLE_EMPTY:
case CIRCLE_FILLED:
this.size = size + expand;
break;
}
break;
}
this.alive = alive;
this.expand = expand;
this.fade = fade;
this.expandIndex = alive / (double) (expand + 1);
this.expandHit = alive - expandIndex;
}
private void update() {
x = x % internalBufferWidth;
y = y % internalBufferHeight;
alive--;
if (alive <= 0) {
dead = true;
}
switch (getObjectDirection()) {
case EXPLODE:
while (alive < expandHit) {
switch (getObjectShape()) {
case SQUARE_EMPTY:
case SQUARE_FILLED:
case LINE_HORIZONTAL:
case LINE_VERTICAL:
size = size + 2;
break;
case CIRCLE_EMPTY:
case CIRCLE_FILLED:
size++;
break;
}
expandHit = expandHit - expandIndex;
}
break;
case IMPODE:
while (alive < expandHit) {
switch (getObjectShape()) {
case SQUARE_EMPTY:
case SQUARE_FILLED:
case LINE_HORIZONTAL:
case LINE_VERTICAL:
size = size - 2;
break;
case CIRCLE_EMPTY:
case CIRCLE_FILLED:
size--;
break;
}
expandHit = expandHit - expandIndex;
}
break;
}
//fade the color, if less time to live than to fade
usedColor = color;
if (alive < fade) {
short r = (short) Math.round(((color >> 16) & 255) * ((alive + 1) / (float) (fade + 1)));
short g = (short) Math.round(((color >> 8) & 255) * ((alive + 1) / (float) (fade + 1)));
short b = (short) Math.round((color & 255) * ((alive + 1) / (float) (fade + 1)));
usedColor = (r << 16) | (g << 8) | b;
}
switch (getObjectShape()) {
case SQUARE_EMPTY:
drawEmptySquare();
break;
case SQUARE_FILLED:
drawFilledSquare();
break;
case CIRCLE_EMPTY:
drawEmptyCircle();
break;
case CIRCLE_FILLED:
drawFilledCircle();
break;
case LINE_VERTICAL:
drawLineVertical();
break;
case LINE_HORIZONTAL:
drawLineHorrizontal();
}
}
private boolean isDead() {
return dead;
}
private void drawEmptySquare() {
//calculate the coordinates
int hs = size / 2;
int rs = size % 2;
int x_l = x - hs;
int x_r = x + hs + rs;
int y_t = y - hs;
int y_b = y + hs + rs;
//draw the shape
//left line
for (int i = y_t; i <= y_b; i++) {
if (x_l >= 0 && i >= 0 && i < internalBufferHeight) {
internalBuffer[x_l + (i * internalBufferWidth)] = usedColor;
}
}
//right line
for (int i = y_t; i <= y_b; i++) {
if (x_r < internalBufferWidth && i >= 0 && i < internalBufferHeight) {
internalBuffer[x_r + (i * internalBufferWidth)] = usedColor;
}
}
//top line
for (int i = x_l + 1; i < x_r; i++) {
if (y_t >= 0 && i >= 0 && i < internalBufferWidth) {
internalBuffer[i + (y_t * internalBufferWidth)] = usedColor;
}
}
//bottom line
for (int i = x_l + 1; i < x_r; i++) {
if (y_b < internalBufferHeight && i >= 0 && i < internalBufferWidth) {
internalBuffer[i + (y_b * internalBufferWidth)] = usedColor;
}
}
}
private void drawFilledSquare() {
//calculate the coordinates
int hs = size / 2;
int rs = size % 2;
int x_l = x - hs;
int x_r = x + hs + rs;
int y_t = y - hs;
int y_b = y + hs + rs;
//draw the shape
//from left to right (linewise)
for (int j = x_l; j <= x_r; j++) {
for (int i = y_t; i <= y_b; i++) {
setPixel(j, i, usedColor);
}
}
}
private void drawEmptyCircle() {
double f = 1 - size;
int ddFx = 1;
double ddFy = -2 * size;
int x_pos = 0;
double y_pos = size;
setPixel(x, (int) Math.round(y + size), usedColor);
setPixel(x, (int) Math.round(y - size), usedColor);
setPixel((int) Math.round(x + size), y, usedColor);
setPixel((int) Math.round(x - size), y, usedColor);
while (x_pos < y_pos) {
if (f >= 0) {
y_pos--;
ddFy += 2;
f += ddFy;
}
x_pos++;
ddFx += 2;
f += ddFx;
setPixel(x + x_pos, (int) Math.round(y + y_pos), usedColor);
setPixel(x - x_pos, (int) Math.round(y + y_pos), usedColor);
setPixel(x + x_pos, (int) Math.round(y - y_pos), usedColor);
setPixel(x - x_pos, (int) Math.round(y - y_pos), usedColor);
setPixel((int) Math.round(x + y_pos), y + x_pos, usedColor);
setPixel((int) Math.round(x - y_pos), y + x_pos, usedColor);
setPixel((int) Math.round(x + y_pos), y - x_pos, usedColor);
setPixel((int) Math.round(x - y_pos), y - x_pos, usedColor);
}
}
private void drawFilledCircle() {
for (double r = 0; r <= size; r = r + 0.1) {
double f = 1 - r;
int ddFx = 1;
double ddFy = -2 * r;
int x_pos = 0;
double y_pos = r;
setPixel(x, (int) Math.round(y + r), usedColor);
setPixel(x, (int) Math.round(y - r), usedColor);
setPixel((int) Math.round(x + r), y, usedColor);
setPixel((int) Math.round(x - r), y, usedColor);
while (x_pos < y_pos) {
if (f >= 0) {
y_pos--;
ddFy += 2;
f += ddFy;
}
x_pos++;
ddFx += 2;
f += ddFx;
setPixel(x + x_pos, (int) Math.round(y + y_pos), usedColor);
setPixel(x - x_pos, (int) Math.round(y + y_pos), usedColor);
setPixel(x + x_pos, (int) Math.round(y - y_pos), usedColor);
setPixel(x - x_pos, (int) Math.round(y - y_pos), usedColor);
setPixel((int) Math.round(x + y_pos), y + x_pos, usedColor);
setPixel((int) Math.round(x - y_pos), y + x_pos, usedColor);
setPixel((int) Math.round(x + y_pos), y - x_pos, usedColor);
setPixel((int) Math.round(x - y_pos), y - x_pos, usedColor);
}
}
}
private void drawLineVertical() {
//calculate the coordinates
int hs = size / 2;
int rs = size % 2;
int x_l = x - hs;
int x_r = x + hs + rs;
//draw the shape
//from left to right (linewise)
for (int j = x_l; j <= x_r; j++) {
for (int i = 0; i < internalBufferHeight; i++) {
setPixel(j, i, usedColor);
}
}
}
private void drawLineHorrizontal() {
//calculate the coordinates
int hs = size / 2;
int rs = size % 2;
int y_t = y - hs;
int y_b = y + hs + rs;
//draw the shape
//from left to right (linewise)
for (int j = 0; j < internalBufferWidth; j++) {
for (int i = y_t; i <= y_b; i++) {
setPixel(j, i, usedColor);
}
}
}
/**
* Sets the pixel.
*
* @param x the x
* @param y the y
* @param col the col
*/
private void setPixel(int x, int y, int col) {
if (y >= 0 && y < internalBufferHeight && x >= 0 && x < internalBufferWidth) {
internalBuffer[y * internalBufferWidth + x] = col;
}
}
}
}