Generating non-overlapping rectangle test sets - testing

Alas this is a university type question, but one im struggling with none the less.
I need to generate large test sets of rectangles, non-overlapping, that can then be used to test an algorithm that finds the adjacencies between them. The test sets will need to probably have upwards of 10000 - 100000 rectangles in them. Ive been searching the net for examples of how to generate test sets like this, but have come up with nothing. Im aware that I could use a purely brute force method, and every time a random rectangle is generated, check whether or not it overlaps with any of the other rectangles, but this seems like generating the test sets will end up taking days if not longer!
Any one know how to go about doing this, or at least where I should start looking?

I found your idea fun and challanging and therefore tried a solution by using a matrix.
Basicly, the idea is (when talking in pixels) to create a matrix of booleans of the same width and height as the square root of MaxWidthOfRectangle * (NumberOfRectangles) (just for simplicity the same width and height).
Next, for each entry in the matrix, generate a random rectangle between min and max bounds. and set all the bools in the matrix for the specific rectangle. Now when generating the next rectangle, you can simply check "around" the desired location to determine how much space you can occupy rather then having to estimate a size and compare with each other rectangle if it conflicts.
My code:
class RectangleGenerator
{
readonly bool[,] _matrix;
readonly int _size;
readonly int _minimalBoxSize;
readonly int _maximumBoxSize;
readonly Random _random = new Random(1);
readonly List<Point> _offsets;
public bool[,] Matrix { get { return _matrix; } }
public RectangleGenerator(int size, int minimalBoxSize, int maximumBoxSize)
{
_matrix = new bool[size, size];
_size = size;
_minimalBoxSize = minimalBoxSize;
_maximumBoxSize = maximumBoxSize;
_offsets = new List<Point>(size * size);
Reset();
}
public IEnumerable<Rectangle> Calculate()
{
while (_offsets.Count > 0)
{
Point currentPoint = _offsets[_offsets.Count - 1];
_offsets.RemoveAt(_offsets.Count - 1);
if (!_matrix[currentPoint.X, currentPoint.Y])
{
Rectangle rectangle;
if (TryCreateNextRectangle(currentPoint.X, currentPoint.Y, out rectangle))
{
// fill the matrix with the rectangle + padding
int startFillX = Math.Max(0, rectangle.Left);
int startFillY = Math.Max(0, rectangle.Top);
int endFillX = Math.Min(_size, rectangle.Right);
int endFillY = Math.Min(_size, rectangle.Bottom);
for (int fillX = startFillX; fillX < endFillX; fillX++)
for (int fillY = startFillY; fillY < endFillY; fillY++)
{
_matrix[fillX, fillY] = true;
}
yield return rectangle;
}
}
}
}
private bool TryCreateNextRectangle(int x, int y, out Rectangle rectangle)
{
int maxWidth = DetermineMaxWidth(x, y, _minimalBoxSize);
int maxHeight = DetermineMaxHeight(y, x, maxWidth);
if (maxWidth < _minimalBoxSize || maxHeight < _minimalBoxSize)
{
rectangle = Rectangle.Empty;
return false;
}
int width = _random.Next(_minimalBoxSize, maxWidth);
int height = _random.Next(_minimalBoxSize, maxHeight);
rectangle = new Rectangle(x, y, width, height);
return true;
}
private int DetermineMaxWidth(int x, int y, int height)
{
int result = Math.Min(_maximumBoxSize, _size - x);
for (int offsetX = 0; offsetX < result; offsetX++)
for (int offsetY = 0; offsetY < height; offsetY++)
{
if (_matrix[x + offsetX, y + offsetY])
{
result = offsetX;
break;
}
}
return result;
}
private int DetermineMaxHeight(int y, int x, int width)
{
int result = Math.Min(_maximumBoxSize, _size - y);
for (int offsetY = 0; offsetY < result; offsetY++)
for (int offsetX = 0; offsetX < width; offsetX++ )
{
if (_matrix[x + offsetX, y + offsetY])
{
result = offsetY;
break;
}
}
return result;
}
public void Reset()
{
// append for padding:
for (int x = 0; x < _size; x++)
for (int y = 0; y < _size; y++)
{
_matrix[x, y] = false;
if (_size - x >= _minimalBoxSize && _size - y >= _minimalBoxSize)
{
_offsets.Add(new Point(x, y));
}
}
_offsets.Sort((x, y) => x == y ? 0 : _random.Next(-1, 1));
}
}

Related

ASSERT failure in QList::operator[]: "index out of range"

I want to put the data of all the pixels of my image into a two-dimensional "uchar" vector, so that I can then manipulate this data, but the error does not allow me to do this.Error: ASSERT failure in QList::operator[]: "index out of range", file C:\Qt\6.2.1\msvc2019_64\include\QtCore/qlist.h, line 403
header
#include <QPixmap>
#include <QVector>
class LoadedImage
{
public:
LoadedImage(QString path);
QImage getCopyOfLoadedPILImage();
void loadImage();
void loadImageData();
QVector<uchar*> imageData ;
int getWidth();
int getHeight();
private:
QString path_;
QImage *loadedImage;
double width, height;
bool loadedImageIsOpen;
};
#endif // LOADEDIMAGE_H
cpp
#include "loadedimage.h"
LoadedImage::LoadedImage(QString path) : path_(path)
{
loadedImageIsOpen = false;
}
QImage LoadedImage::getCopyOfLoadedPILImage(){
return loadedImage->copy();
}
int LoadedImage::getHeight(){
return height;
}
int LoadedImage::getWidth(){
return width;
}
void LoadedImage::loadImage(){
loadedImage = new QImage(path_);
loadedImageIsOpen = true;
width = loadedImage->size().width();
height = loadedImage->size().height();
}
void LoadedImage::loadImageData(){
if(loadedImageIsOpen == false)
loadImage();
// unsigned char * rawData = loadedImage->bits();
int rawDataCount = 0;
int slice = 0;
for(int y = 0; y < height - 1; y++){
for(int x = 0; x < width - 1; x++){
if(slice < 3){
imageData[x][y] = loadedImage->pixel(rawDataCount, slice);
}
rawDataCount++;
slice++;
}
}
}

numWeights corresponding to mnumVertices?

I have one issue left with ASSIMP DIRECT X C++ ANIMATION WITH SKELETON.
for (UINT m = 0; m < currentMesh->mBones[k]->mNumWeights; m++) //verticer som påverkas
{
vertexVector[k].joints.x = currentMesh->mBones[k]->mWeights[m].mVertexId;
That code shows all vertices affected by a bone - k, inside an iteration.
All of these vertices must have the same vert ID since they are all affected by the same bone/joint.
The problem is, I need to make a list of every vertex and a list of every indice of a face, where I store position, UV, Normal etc.
The list that displays all of the vertices, is not in the same order obviously as the lists that displays all the vertices affected by each bone.
So how can I combine these lists?
"vertexVector"... etc is an example of a list with jointInfo that is corresponding to vertexID.
It has room for more places and another variable for the weight.
But that list doesn't work obviously.
What am I doing wrong with Assimp? Hope this was a clear post.
UPdate this is how i build the matrices: I don't know what is wrong.
void jointTransform(float
timeInSeconds, std::vector<DirectX::XMMATRIX>& transformM, aiAnimation*
ani, UINT nrOfJoints, std::vector<joints>& jointInfo, const aiScene*
scenePtr)
{
DirectX::XMMATRIX iD = DirectX::XMMatrixIdentity();
float ticksPerSecond = (float)ani->mTicksPerSecond;
if (ticksPerSecond == 0)
{
ticksPerSecond = 30;
}
float timeInTicks = timeInSeconds * ticksPerSecond;
float animationTime = fmod(timeInTicks, (float)ani->mDuration);
readNodeHeiarchy(animationTime, scenePtr->mRootNode, iD, jointInfo, ani,
scenePtr);
transformM.resize(nrOfJoints);
for (UINT i = 0; i < transformM.size(); i++)
{
transformM[i] = jointInfo[i].transformFinal;
}
}
void readNodeHeiarchy(float time, const aiNode* node, DirectX::XMMATRIX
parentMat, std::vector<joints>& jointInfo, aiAnimation* ani, const
aiScene* scenePtr)
{
std::string nodeNameString = node->mName.data;
//Skapa en parentTransform från noden. Som sedan skickas in som parent
matris, första gången är det identitetsmatrisen.
aiMatrix4x4 nodeTransform = node->mTransformation;
DirectX::XMMATRIX combined;
combined = DirectX::XMMatrixSet(nodeTransform.a1, nodeTransform.a2,
nodeTransform.a3, nodeTransform.a4,
nodeTransform.b1, nodeTransform.b2, nodeTransform.b3, nodeTransform.b4,
nodeTransform.c1, nodeTransform.c2, nodeTransform.c3, nodeTransform.c4,
nodeTransform.d1, nodeTransform.d2, nodeTransform.d3,
nodeTransform.d4);
const aiNodeAnim* joint = nullptr;
//Kolla om noden är ett ben.
for (UINT i = 0; i < ani->mNumChannels; i++)
{
if (nodeNameString == ani->mChannels[i]->mNodeName.data)
{
joint = ani->mChannels[i];
}
}
DirectX::XMMATRIX globalTransform = DirectX::XMMatrixIdentity();
//om den är ett ben så är joint inte längre nullptr, den blir det benet.
if (joint)
{
DirectX::XMMATRIX S;
DirectX::XMMATRIX R;
DirectX::XMMATRIX T;
//scale
aiVector3D scaleV;
calcLerpScale(scaleV, time, joint);
S = DirectX::XMMatrixScaling(scaleV.x, scaleV.y, scaleV.z);
//rotate
aiQuaternion rotationQ;
calcLerpRot(rotationQ, time, joint);
DirectX::XMVECTOR q;
q = DirectX::XMVectorSet(rotationQ.x, rotationQ.y, rotationQ.z,
rotationQ.w);
R = DirectX::XMMatrixRotationQuaternion(q);
//translate
aiVector3D transV;
calcLerpTrans(transV, time, joint);
T = DirectX::XMMatrixTranslation(transV.x, transV.y, transV.z);
combined = S * R * T;
globalTransform = combined * parentMat;
}
//DirectX::XMMATRIX globalTransform = combined * parentMat;
//if (jointInfo[jointInfo.size() - 1].name.C_Str() != nodeNameString)
//{
for (UINT i = 0; i < jointInfo.size(); i++)
{
if (jointInfo[i].name.C_Str() == nodeNameString)
{
OutputDebugStringA("\n");
OutputDebugStringA(jointInfo[i].name.C_Str());
OutputDebugStringA("\n");
aiMatrix4x4 off = jointInfo[i].offsetM;
DirectX::XMMATRIX offset;
offset = DirectX::XMMatrixSet(off.a1, off.a2, off.a3, off.a4,
off.b1, off.b2, off.b3, off.b4,
off.c1, off.c2, off.c3, off.c4,
off.d1, off.d2, off.d3, off.d4);
DirectX::XMMATRIX rootMInv;
aiMatrix4x4 rootInv = scenePtr->mRootNode-
>mTransformation.Inverse();
rootMInv = DirectX::XMMatrixSet(rootInv.a1, rootInv.a2,
rootInv.a3, rootInv.a4,
rootInv.b1, rootInv.b2, rootInv.b3, rootInv.b4,
rootInv.c1, rootInv.c2, rootInv.c3, rootInv.c4,
rootInv.d1, rootInv.d2, rootInv.d3, rootInv.d4);
jointInfo[i].transformFinal = offset * globalTransform *
rootMInv;
break;
}
}
//}
for (UINT i = 0; i < node->mNumChildren; i++)
{
readNodeHeiarchy(time, node->mChildren[i], globalTransform, jointInfo,
ani, scenePtr);
}
}
void calcLerpScale(aiVector3D& scale, float aniTime, const aiNodeAnim*
joint)
{
if (joint->mNumScalingKeys == 1)
{
scale = joint->mScalingKeys[0].mValue;
return;
}
UINT scaleInd = findIndexS(aniTime, joint);
UINT nextScale = scaleInd + 1;
assert(nextScale < joint->mNumScalingKeys);
float deltaTime = (float)joint->mScalingKeys[nextScale].mTime -
(float)joint->mScalingKeys[scaleInd].mTime;
float factor = (aniTime - (float)joint->mScalingKeys[scaleInd].mTime) /
deltaTime;
assert(factor >= 0.0f && factor <= 1.0f);
const aiVector3D& startScaleV = joint->mScalingKeys[scaleInd].mValue;
const aiVector3D& endScaleV = joint->mScalingKeys[nextScale].mValue;
//interpolate
aiVector3D Delta = endScaleV - startScaleV; // längden
scale = startScaleV + (factor * Delta); //gå ett antal steg beroende på
faktorn mellan start och slut.
scale.Normalize();
}
void calcLerpRot(aiQuaternion& rotation, float aniTime, const aiNodeAnim*
joint)
{
if (joint->mNumRotationKeys == 1)
{
rotation = joint->mRotationKeys[0].mValue;
return;
}
UINT rotIndex = findIndexRot(aniTime, joint);
UINT nextRot = (rotIndex + 1);
assert(nextRot < joint->mNumRotationKeys);
float deltaTime = (float)joint->mRotationKeys[nextRot].mTime -
(float)joint->mRotationKeys[rotIndex].mTime;
float factor = (aniTime - (float)joint->mRotationKeys[rotIndex].mTime) /
deltaTime;
assert(factor >= 0.0f && factor <= 1.0f);
const aiQuaternion& StartRotationQ = joint->mRotationKeys[rotIndex].mValue;
const aiQuaternion& EndRotationQ = joint->mRotationKeys[nextRot].mValue;
aiQuaternion::Interpolate(rotation, StartRotationQ, EndRotationQ, factor);
rotation.Normalize();
}
void calcLerpTrans(aiVector3D& translation, float aniTime, const
aiNodeAnim*
joint)
{
if (joint->mNumPositionKeys == 1)
{
translation = joint->mPositionKeys[0].mValue;
return;
}
UINT transIndex = findIndexT(aniTime, joint);
UINT nextTrans = (transIndex + 1);
assert(nextTrans < joint->mNumPositionKeys);
float deltaTime = (float)joint->mPositionKeys[nextTrans].mTime -
(float)joint->mPositionKeys[transIndex].mTime;
float factor = (aniTime - (float)joint->mPositionKeys[transIndex].mTime) /
deltaTime;
assert(factor >= 0.0f && factor <= 1.0f);
const aiVector3D& startTransV = joint->mPositionKeys[transIndex].mValue;
const aiVector3D& endTransV = joint->mPositionKeys[nextTrans].mValue;
//interpolate
aiVector3D Delta = endTransV - startTransV;
translation = startTransV + (factor * Delta);
translation.Normalize();
}
UINT findIndexRot(float aniTime, const aiNodeAnim* joint)
{
assert(joint->mNumRotationKeys > 0);
for (UINT i = 0; i < joint->mNumRotationKeys - 1; i++)
{
if (aniTime < (float)joint->mRotationKeys[i + 1].mTime)
{
return i;
}
}
assert(0);
}
}
Not sure what you mean by "All of these vertices must have the same vert ID" - the vertex id's of the k:th bone, according to mBones[k]->mWeights[..].mVertexId, are indices to vertices influenced by this bone, and they are going to be different (otherwise there would be either redundancy of conflict).
You probably want to have bone indices and bone weights as part of the vertex definition for easy handling in a shader. Something like
struct vertex (
vec3 pos;
vec3 normal;
float bone_weights[N]; // weights of bones influencing this vertex
unsigned bone_indices[N]; // indices of bones influencing this vertex
}
std::vector<vertex> mesh_vertices;
Where N is the maximum number of influence bones per vertex. A common value is four, but this depends on the mesh your are importing.
Based on your example, a rough draft could be something like this:
// k:th bone of bones in currentMesh
for (UINT m = 0; m < currentMesh->mBones[k]->mNumWeights; m++)
{
float bone_weight = currentMesh->mBones[k]->mWeights[m].mWeight;
unsigned vertex_index = currentMesh->mBones[k]->mWeights[m].mVertexId;
mesh_vertices[vertex_index].bone_weights[m] = bone_weight;
mesh_vertices[vertex_index].bone_indices[m] = k;
}
Here we've assumed that mNumWeights = N, but this needs to checked, as mentioned.

mpandroidchart I want to get 3rd value

I am using mpandroidchart and i have a problem now
I set 3 values into the array likes this
(index, values, and another value)
ArrayList<Entry> value1 = new ArrayList<>();
for (int i = 0; i < 10; i++) {
float y = (float) Math.random();
float h = (float) Math.random();
value1.add(new Entry(i, y, h));
}
I want to get h values to use this h
but i cant' find the way
How can i get h value?
Thanks for reading
*Edited *
enter image description here
This is the image i want to make
* Edited2 *
ArrayList<Integer> color;
ArrayList<Entry> value1 = new ArrayList<>();
for (int i = 0; i < 10; i++) {
float y = (float) Math.random();
value1.add(new Entry(i, y));
if(y>10)
{
color.add(context.getResources().getColor(R.color.colorPrimary));
}
else
color.add(context.getResources().getColor(R.color.colorAccent));
}
dataSet.setColors(color);
Dear M.Saad Lakhan
You suggested like upper code, But there are some ploblems.
ArrayList color;
-> "variable 'color' might not have been initiallized
color.add(context.getResources().getColor(R.color.colorPrimary)
-> cannot resolve symbol 'context'
ArrayList dataSets = new ArrayList<>(); //this is my code
dataSets.setColors(color);
-> there is no setColors in my datasets
What are these problem?
You need to create:
ArrayList<Integer> colors = new ArrayList<>();
After that you need to add values for each entry which color you want to show, you need to modify your code as:
ArrayList<Entry> value1 = new ArrayList<>();
for (int i = 0; i < 10; i++)
{
float y = (float) Math.random();
value1.add(new Entry(i, y));
// add your condition here
if(y>10)
{
color.add(getContext().getResources().getColor(R.color.colorPrimary));
}
else
color.add(getContext().getResources().getColor(R.color.colorAccent));
}
When you complete your colors arraylist then you can set colors as:
dataSet.setColors(color);

Stroke Scale reduction and redraw in android

Am trying to get the stroke data when the user scribbles on screen and redraw that same stroke on same location, in reduced size based on screen size of display. My issue is that this code works, it scales down striker but all scaled down strokes are connected which I don't want to be.
My ontouch code snippet:
#Override
public boolean onTouch(View arg0, final MotionEvent event) {
x = event.getX();
y =event.getY();
/* float x=(float) (xx*0.7);
float y=(float) (yy*0.7);*/
array_x.add(x);
array_y.add(y);
if(event.getPointerCount() == 2){
timevar2 = event.getEventTime();
float newx = event.getX(0);
float newy = event.getY(0);
float oldx = event.getX(1);
float oldy = event.getY(1);
mPath.reset();
float equa = (float) (Math.pow(newx - oldx, 2) + Math.pow(newy - oldy, 2));
float cscale = (float) Math.sqrt(equa)/100;
float scaled = (cscale - old_scale);
if(scaled < -0.1){
if(scale > 0.1){
scale -= 0.03;
}
}
if(scaled > 0.1){
scale += 0.03;
}
old_scale = cscale;
invalidate();
return true;
}
long dt = event.getEventTime() - timevar2;
if(dt < 100){
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
Log.v("TOUCH=======start","X=="+x + "Y=="+y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
Log.v("TOUCH=======move","X=="+x + "Y=="+y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up(x,y);
Log.v("TOUCH=======up","X=="+x + "Y=="+y);
//invalidate();
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
public void run() {
for(int i=0;i<array_x.size();i++)
{
x= array_x.get(i);
y= array_y.get(i);
if(i == 0){
x=(float) (x*0.7);
y=(float) (x*0.7);
Log.i("ARRAYYYYYY", "X=="+x+"Y=="+y);
touch_start(x, y);
invalidate();
}else if(i == array_x.size()-1){
x=(float) (x*0.7);
y=(float) (x*0.7);
touch_up(x, y);
invalidate();
mPath.reset();
}else{
x=(float) (x*0.7);
y=(float) (y*0.7);
touch_move(x, y);
invalidate();
}
}
}
}, 2000);
break;
}
return true;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
if (this.isErase) {
erasePath(mPath, x, y, mX, mY);
return;
}
PointF point = new PointF();
point.x = x;
point.y = y;
newPoint.points.add(point);
mCanvas.drawPath(mPath, mPaint);
}
private void touch_start(float x, float y) {
undonePaths.clear();
mPath = new Path();
newPoint = new Stroke();
PointF point = new PointF();
point.x = x;
point.y = y;
newPoint.points.add(point);
mCanvas.drawPath(mPath, mPaint);
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_up(float x, float y) {
if (this.isErase) {
return;
}
PointF point = new PointF();
point.x = x;
point.y = y;
newPoint.points.add(point);
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
Pair p = new Pair(mPath, newPoint);
this.Strokepaths.add(p);
}

Processing: How can I improve the framerate in my program?

So I've been working in Processing for a few weeks now, and, though I'm not experienced in programming, I have moved on to more complex projects. I'm programming an evolution simulator, that spawns creatures with random properties.
Eventually, I'll add reproduction, but as of now the creatures just sort of float around the screen, and follow the mouse somewhat. It interacts with sound from the line in, but I commented those parts out so that it can be viewed on the canvas, it shouldn't really change the question, I just thought I would point it out.
As of now, the framerate is far less than ideal for me, and it slowly lowers as more creatures are spawned. Am I making some fundamental mistake, or am I just running too many functions per frame?
Here's the source code, and you can play with it in the browser here:
//import ddf.minim.*;
//import ddf.minim.signals.*;
//import ddf.minim.analysis.*;
//import ddf.minim.effects.*;
//Minim minim;
//AudioInput in;
boolean newCreature = true;
boolean matured[];
int ellipses[];
int hair[];
int maxCreatureNumber = 75;
//int volume;
//int volumeTolerance = 1;
int creatureIndex = -1;
int creatureX[];
int creatureY[];
float strokeWeightAttribute[];
float creatureSize[];
float creatureEndSize[];
float creatureXIncrement[];
float creatureYIncrement[];
float bubbleSize;
float easing = 0.05;
float angle = 0.00;
color colorAttribute[];
void setup() {
background(0);
size(1000,500);
noFill();
//minim = new Minim(this);
//in = minim.getLineIn(Minim.STEREO, 512);
creatureX = new int[maxCreatureNumber];
creatureY = new int[maxCreatureNumber];
ellipses = new int[maxCreatureNumber];
hair = new int[maxCreatureNumber];
strokeWeightAttribute = new float[maxCreatureNumber];
creatureEndSize = new float[maxCreatureNumber];
creatureSize = new float[maxCreatureNumber];
creatureXIncrement = new float[maxCreatureNumber];
creatureYIncrement = new float[maxCreatureNumber];
matured = new boolean[maxCreatureNumber];
colorAttribute = new color[maxCreatureNumber];
}
void draw() {
angle += 0.05;
fill(0, 50);
rect(-1, -1, 1001, 501);
// for(int i = 0; i < in.bufferSize() - 1; i++) {
// if(in.mix.get(i) * 50 > volumeTolerance) {
// volume++;
// }
// }
if(newCreature && creatureIndex < maxCreatureNumber - 1) {
initSpontaneousCreature();
}
updateCreatures();
// bubbleSize = volume/250;
bubbleSize += 0.01;
// volume = 0;
}
//void stop() {
// minim.stop();
// super.stop();
//}
void initSpontaneousCreature() {
creatureIndex++;
creatureEndSize[creatureIndex] = int(random(5, 20));
creatureX[creatureIndex] = int(random(1000));
if(creatureX[creatureIndex] >= 500) {
creatureX[creatureIndex] -= creatureEndSize[creatureIndex];
}
else {
creatureX[creatureIndex] += creatureEndSize[creatureIndex];
}
creatureY[creatureIndex] = int(random(500));
if(creatureY[creatureIndex] >= 250) {
creatureY[creatureIndex] -= creatureEndSize[creatureIndex];
}
else {
creatureY[creatureIndex] += creatureEndSize[creatureIndex];
}
ellipses[creatureIndex] = int(random(4));
hair[creatureIndex] = int(random(4));
strokeWeightAttribute[creatureIndex] = random(1, 4);
colorAttribute[creatureIndex] = color(int(random(20,255)), int(random(20,255)), int(random(20,255)));
matured[creatureIndex] = false;
newCreature = false;
while(ellipses[creatureIndex] == 0 && hair[creatureIndex] == 0) {
ellipses[creatureIndex] = int(random(4));
hair[creatureIndex] = int(random(4));
}
}
void updateCreatures() {
for(int n = 0; n <= creatureIndex; n++) {
if(matured[n]) {
creatureX[n] += ((((mouseX) - creatureX[n]) * easing) / (60/*-abs(volume/5))*/)) + random(-5, 6);
creatureY[n] += ((((mouseY) -creatureY[n]) * easing) / (60/*-abs(/*volume/5))*/)) + random(-5,6);
drawCreature();
}
else {
if(creatureEndSize[n] != creatureSize[n]) {
creatureSize[n] += bubbleSize;
if(creatureSize[n] > creatureEndSize[n]) {
creatureSize[n] -= (creatureSize[n] - creatureEndSize[n]);
}
}
else {
newCreature = true;
matured[n] = true;
// bubbleSize = 0;
}
drawCreature();
}
}
}
void drawCreature() {
for(int n = 0; n <= creatureIndex; n++) {
if(matured[n]) {
stroke(colorAttribute[n]);
strokeWeight(strokeWeightAttribute[n]);
for(int i = 0; i <= 4; i++) {
if(ellipses[n] == i) {
if(i == 0) {
}
else if (i == 1) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(180));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
popMatrix();
}
else if(i == 2) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(180));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(270));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
popMatrix();
}
else if(i == 3) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(90));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(180));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(270));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
popMatrix();
}
}
if(hair[n] == i) {
if(i == 0) {
}
else if (i == 1) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
for(int j = 0; j <= 360; j+=70) {
rotate(j);
stroke(colorAttribute[n], random(255));
line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
}
popMatrix();
}
else if(i == 2) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
for(int j = 0; j <= 360; j+=30) {
rotate(j);
stroke(colorAttribute[n], random(255));
line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
}
popMatrix();
}
else if(i == 3) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
for(int j = 0; j <= 360; j+=1) {
rotate(j);
stroke(colorAttribute[n], random(255));
line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
}
popMatrix();
}
}
}
}
if(!matured[n]) {
stroke(abs(sin(angle) * 255));
//strokeWeight(5);
ellipse(creatureX[n], creatureY[n], creatureSize[n] * 5, creatureSize[n] * 5);
noStroke();
}
}
}
Right, as I suspected, all the unnecessary pushMatrix(), popMatrix() calls and the large amount of lines seemed to be the main culprits, still, there was a lot of redundant code.
I simply refactored the code in a cleaner manner and it seems to run fine.
Here is my 'improved' version:
int maxCreatures = 75;
int numCreatures = 0;
int spawnNthFrame = 50;//spawn a creature every 50 frames
Creature[] creatures;
void setup() {
background(0);
size(1000,500);
noFill();
creatures = new Creature[maxCreatures];
}
void draw() {
fill(0, 50);
rect(-1, -1, 1001, 501);
if(frameCount % spawnNthFrame == 0){
println("creatures: " + numCreatures);
if(numCreatures < maxCreatures) {
//Creature constructor float endSize,int x, int y,int ellipses,int hair,float strokeW,color c
creatures[numCreatures] = new Creature(random(5, 20),int(random(1000)),int(random(500)),int(random(4)),int(random(4)),random(1, 4),color(int(random(20,255)), int(random(20,255)), int(random(20,255))));
numCreatures++;
}
}
for(int i = 0; i < numCreatures; i++) creatures[i].update();
}
and the Creature class:
class Creature{
int x,y,cXInc,cYInc;//if x,y are ints, increments would be into, right?
float cStrokeWeight,cSize,cEndSize,cSizeInc = 0.01,easing = 0.05,angle = 0.00;
color cColor;
int hair,numHair,ellipses;
boolean matured = false;
Creature(float endSize,int x, int y,int ellipses,int hair,float strokeW,color c){
cEndSize = endSize;
this.x = x;
if(x >= 500) x -= cEndSize;
else x += cEndSize;
this.y = y;
if(y >= 250) x -= cEndSize;
else x += cEndSize;
this.ellipses = ellipses;
this.hair = hair;
if(hair == 1) numHair = 3;//~5, half that, draw through centre, etc.
if(hair == 2) numHair = 6;
if(hair == 3) numHair = 30;//no default value
cStrokeWeight = strokeW;
this.cColor = c;
}
void update(){
if(matured) {
x += (((mouseX - x) * easing) / 60) + random(-5, 6);
y += (((mouseY - y) * easing) / 60) + random(-5, 6);
}else {
if(cSize < cEndSize) cSize += cSizeInc;
else matured = true;
angle += 0.05;
}
this.draw();
}
void draw(){
if(matured){
stroke(cColor);
strokeWeight(cStrokeWeight);
if(ellipses == 1){//2 ellipses diagonally
ellipse(x,y,cSize,cSize);
ellipse(x+cSize,y+cSize,cSize,cSize);
}
if(ellipses == 2){
ellipse(x,y,cSize,cSize);
ellipse(x,y+cSize,cSize,cSize);
ellipse(x+cSize,y+cSize,cSize,cSize);
}
if(ellipses == 3){
ellipse(x,y,cSize,cSize);
ellipse(x+cSize,y,cSize,cSize);
ellipse(x,y+cSize,cSize,cSize);
ellipse(x+cSize,y+cSize,cSize,cSize);
}
float hairAngleInc = TWO_PI/numHair;//angle increment for each piece = 360/number of hair lines
float hairAngle,hairLength,hairCos,hairSin;
for(int i = 0; i < numHair; i++){
hairAngle = hairAngleInc * i;
hairCos = cos(hairAngle);
hairSin = sin(hairAngle);
hairLength = random(20);
stroke(cColor, random(255));
line(x + (hairCos * -hairLength),y + (hairSin * -hairLength), x + (hairCos * hairLength),y + (hairSin * hairLength));
}
}else{
stroke(abs(sin(angle) * 255));
ellipse(x,y, cSize * 5, cSize * 5);
}
}
}
Ok, now for the explanations.
First, I separated all the variables that were related to one creature from the 'global' ones that determine how the sketch runs (how many creatures get spawned, etc.).
This makes the main code about 25 lines long and altogether a bit below 100 lines which is less than half of the original.
The first part doesn't do anything special. In the draw() function, instead of creating a Creature every frame, I draw one every Nth frame using the spawnNthFrame variable, this made it easy to see which state of the creature made it slow. If you set a small number like 2 to that variable it should spawn a lot of creatures per frame.
The Creature class has all the properties the original code stored in arrays.
Instead of doing
pushMatrix();
translate();
ellipse();
rotate()
ellipse()
popMatrix();
I simply draw the ellipses at x,y.
A little hint on the rotations. I've noticed they were increments
of 90 degrees. Processing has some nice constants for 90,180,360 degrees
in radians: HALF_PI, PI, TWO_PI which can be handy sometimes.
Now for the 'hairy' situation, here's something I commented out for myself:
//if(i == 1) for(int j = 0; j <= 360; j+=70) , well 360/70 is about 5, if (i == 2) , 12 hair
//if = 3-> 360 lines ? do you really need that many lines, that thick ? how about 30 ? 5*12=60, but if you draw the lines through the centre, not from the centre, you can get away with half the lines
So there were 3 loops for drawing lines, each having different increments. Basically
there were either 360/70 lines, 360/30 lines and 360 lines.
Roughly about 5,12 and 360 lines. About the 5,12 lines, I kind of halved that by drawing 'diameter' lines across the centre as opposed to 'radius' lines from the centre.
Here's what I mean,
Also I think that 360 lines with that strokeWeight and the jittery motion will probably look like a bunch of lines hard to count, so I thought, why split hairs? :P
Maybe the creature will look pretty similar with about 60 radii which means 30 diameters.
Now to explain a bit of the trig functions used for this.
The main thing is the 'polar to cartesian' coordinates conversion:
Polar would be something like:
"I am moving on a circle to a direction described by an angle (much like one handle of a clock) and radius (distance from centre)."
and Cartesian
"I'm moving based on two axes (horizontal/X and vertical/Y), kind of like the streets of Manhattan, but I cheat and also move diagonally through walls."
If that makes any sense... :)
Anyway, you convert the angle and radius pair to the x and y pair using the formula:
x = cos(angle) * radius
y = sin(angle) * radius
For each line:
angle = hairAngle
radius = hairLength
So the line() with *x + (hairCos * -hairLength)* looks a bit like this:
x + (hairCos * -hairLength) =
move to x and from there move by hairLength
to the left(-) for the current angle (hairCos)
Similar for y, but using cos, so this puts the first point of the line in the opposite direct (-hairLength) of the angle moving from the centre (which is the Creature's x) and the second is 'diagonal'. Imagine drawing 'diagonals' (from (-x,-y) to (+x,+y)), but you also rotate these.
Update
Apparently copy/pasting this code works in javascript too (best viewed in Chromium/Chrome). You can also run it right here:
var maxCreatures = 75;
var numCreatures = 0;
var spawnNthFrame = 50;//spawn a creature every 50 frames
var creatures = [];
function setup() {
background(0);
createCanvas(1000,500);
noFill();
}
function draw() {
fill(0, 50);
rect(-1, -1, 1001, 501);
if(frameCount % spawnNthFrame === 0){
println("creatures: " + numCreatures);
if(numCreatures < maxCreatures) {
//Creature constructor float endSize,int x, int y,int ellipses,int hair,float strokeW,color c
creatures[numCreatures] = new Creature(random(5, 20),int(random(1000)),int(random(500)),int(random(4)),int(random(4)),random(1, 4),color(int(random(20,255)), int(random(20,255)), int(random(20,255))));
numCreatures++;
}
}
for(var i = 0; i < numCreatures; i++) creatures[i].update();
}
function Creature(endSize,x,y,ellipses,hair,strokeW,c){
this.x = x;
this.y = y;
this.ellipses = ellipses;
this.hair = hair;
this.numHair = 0;
this.cStrokeWeight = strokeW;
this.cColor = c;
this.cXInc = 0;
this.cYInc = 0.01;
this.cSize = 0;
this.cEndSize = endSize;
this.easing = 0.05;
this.angle = 0.0;
this.matured = false;
if(x >= 500) x -= this.cEndSize;
else x += this.cEndSize;
if(y >= 250) x -= this.cEndSize;
else x += this.cEndSize;
if(hair == 1) this.numHair = 3;//~5, half that, draw through centre, etc.
if(hair == 2) this.numHair = 6;
if(hair == 3) this.numHair = 30;//no default value
this.update = function(){
if(this.matured) {
this.x += (((mouseX - this.x) * this.easing) / 60) + random(-5, 6);
this.y += (((mouseY - this.y) * this.easing) / 60) + random(-5, 6);
}else {
if(this.cSize < this.cEndSize) this.cSize += this.cSizeInc;
else this.matured = true;
this.angle += 0.05;
}
this.draw();
}
this.draw = function(){
if(this.matured){
stroke(this.cColor);
strokeWeight(this.cStrokeWeight);
if(this.ellipses == 1){//2 ellipses diagonally
ellipse(this.x,this.y,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
}
if(this.ellipses == 2){
ellipse(this.x,this.y,this.cSize,this.cSize);
ellipse(this.x,this.y+this.cSize,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
}
if(this.ellipses == 3){
ellipse(this.x,this.y,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y,this.cSize,this.cSize);
ellipse(this.x,this.y+this.cSize,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
}
var hairAngleInc = TWO_PI/this.numHair;//angle increment for each piece = 360/number of hair lines
var hairAngle,hairLength,hairCos,hairSin;
for(var i = 0; i < this.numHair; i++){
hairAngle = hairAngleInc * i;
hairCos = cos(hairAngle);
hairSin = sin(hairAngle);
hairLength = random(20);
stroke(this.cColor, random(255));
line(this.x + (hairCos * -hairLength),this.y + (hairSin * -hairLength), this.x + (hairCos * hairLength),this.y + (hairSin * hairLength));
}
}else{
stroke(abs(sin(this.angle) * 255));
ellipse(this.x,this.y, this.cSize * 5, this.cSize * 5);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>
You could use the frameRate(fps)function. What it does is, it specifies the number of frames to be displayed every second. However, If the processor is not fast enough to maintain the specified rate, it will not be achieved. For example, the function call frameRate(30) will attempt to refresh 30 times a second. It is recommended to set the frame rate within setup().
Remember, using draw() without specifying the frame rate, by default it will run at 60 fps.
Well, there's the good old random-pause method. It's the "poor man's profiler".
Just snapshot it a few times. That will show you exactly what's taking the most time. Those are the things you should see if you can make faster.
It will show up in increased framerate.