Skip to content

Commit

Permalink
Release version 4
Browse files Browse the repository at this point in the history
Merge pull request #25 from Lazerbeak12345/testHitboxIdea
  • Loading branch information
Lazerbeak12345 authored Feb 6, 2022
2 parents 4b55e0e + 60011f1 commit 3638a95
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 45 deletions.
94 changes: 75 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The example code above included `B2/S23` (AKA "Conway's game of Life") as an exa

## Documentation

Function-by funciton documentation. (Updated last on version `3.3.0`)
Function-by funciton documentation. (Updated last on version `4.0.0`)

### The global

Expand Down Expand Up @@ -221,6 +221,10 @@ All of the properties that this object can accept are these:
// See more about the pattern value in "About The Pre-built classes" This is
// an optional value
pattern:"B3/S23",
// `deadCell` below will only be called on empty pixels within the hitbox of a
// live cell. Array of relative coordinate pairs. Optional, defaults to the
// result of `p.neighborhoods.moore()`.
hitbox:[{x:0,y:0}],
// See more about `liveCell` and `deadCell` in "Custom Cellular Automata". Both
// of these are optional, but when present, overrules what `pattern` may have
// applied to it.
Expand Down Expand Up @@ -545,20 +549,6 @@ The coordinates of where the center of the zoomelm is windowed at.

`set_width` and `set_height` take one arg (the width or height, respectively) and return nothing.

#### row

The row that elements such as `Rule 90` used to get proccessed at.

For backward compatibility this has been kept (for this version), and still
functions the same, though wolfram rules no longer require it.


[bug23]: https://github.com/Lazerbeak12345/pixelmanipulator/issues/23

> This was actually a symptom of [a bug][bug23]. A later major version release will have the goal of removing `p.row` entirely as it is now **DEPRECATED**
> The demo no longer indicates what row is in-use.
#### elementTypeMap

A low-level listing of the availiable elements.
Expand Down Expand Up @@ -603,8 +593,74 @@ an element with the color `#000F`
The elm that pixelmanipulator will fill the screen with upon initialization, and
what elements should return to when they are "dead". Default value is `0`.

#### presentElements
#### neighborhoods

An object containing several functions to generate lists of relative positions
for a neighborhood hitbox.

They each return an array of objects in this pattern:

```js
{
// X and Y relative position. Added to the current position during hitbox
// comparison
x:0,
y:0,
}
```

#### neighborhoods.wolfram

```txt
O
XXX
```

Arguments

- radius, a number. Count of how many to the right and left to include.
(optional. defaults to 1)
- yval, a number. Relative Y-value. (optional. defaults to 1 for the purpose of
generating hitboxes)
- include_self, a boolean. Should this include the center pixel? (optional.
defaults to true)

#### neighborhoods.moore

```txt
XXX
XOX
XXX
```
Arguments

- radius, a number. Count of how many rings around the center to include.
(optional. defaults to 1)
- include_self, a boolean. Should this include the center pixel? (optional.
defaults to true)

#### neighborhoods.vonNeumann

```txt
X
XOX
X
```
Arguments

- radius, a number. Count of how many rings around the center to include.
(optional. defaults to 1)
- include_self, a boolean. Should this include the center pixel? (optional.
defaults to true)

#### neighborhoods.euclidean

Shape is all pixels that fit within a circle of the given radius, where the
precise euclidean distance is <= the radias.

Arguments

An array of any elements that should be on screen.
This is appended to by `setPixel`, and is currently the limiter that `iterate`
uses. `iterate` will only call deadCell for elements in this list.
- radius, a number. Count of how many rings around the center to include.
(optional. defaults to 1)
- include_self, a boolean. Should this include the center pixel? (optional.
defaults to true)
143 changes: 117 additions & 26 deletions pixelmanipulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// Concerning the function commments, # is number, [] means array, {} means object, () means function, true means boolean and, "" means string. ? means optional, seperated with : means that it could be one or the other
(function(g) {
'use strict';
var pxversion="3.3.0";
var pxversion="4.0.0";
function pix(require,exports,module) {//done like this for better support for things like require.js and Dojo
/*function ret(v) {
return (function() {
Expand Down Expand Up @@ -54,6 +54,7 @@
"blank":{
color:[0,0,0,255],
number:0,//Index in innerP.elementNumList
hitbox:[],
},
},
elementNumList:["blank"],
Expand All @@ -70,7 +71,79 @@
onIterate:function() {},//both of these need to be defined so the absence of either is suitiable.
onAfterIterate:function() {},
//pixelCounts:{},
presentElements:[],
neighborhoods:{
// Area is f(x)=2x-1
wolfram:function(radius,yval,include_self){
if(typeof radius==="undefined")
radius=1;
if(typeof yval==="undefined")
yval=1;
var output=[{x:0,y:yval}];
if(typeof include_self==="undefined"||include_self){
output.push({x:0,y:yval});
}
for(var i=radius;i>0;i--){
output.push({x:-1*i,y:yval});
output.push({x:i,y:yval});
}
return output;
},
// Area is f(x)=(2r+1)^2
moore:function(radius,include_self){
if(typeof radius==="undefined")
radius=1;
if(typeof include_self==="undefined")
include_self=false;
var output=[];
// Note: no need to calculate the Chebyshev distance. All pixels in this
// range are "magically" within.
for(var x=-1*radius;x<=radius;x++)
for(var y=-1*radius;y<=radius;y++)
if(include_self||!(x===0&&y===0))
output.push({x:x,y:y});
return output;
// And to think that this used to be hard... Perhaps they had a different
// goal? Or just weren't using higher-order algorithims?
},
// Area is f(x)=r^2+(r+1)^2
vonNeumann:function(radius){
if(typeof radius==="undefined")
radius=1;
if(typeof include_self==="undefined")
include_self=false;
var output=[];
// A Von Neumann neighborhood of a given distance always fits inside of a
// Moore neighborhood of the same. (This is a bit brute-force)
for(var x=-1*radius;x<=radius;x++)
for(var y=-1*radius;y<=radius;y++)
if(
(include_self||!(x===0&&y===0))&&
(Math.abs(x)+Math.abs(y)<=radius) // Taxicab distance
)
output.push({x:x,y:y});
return output;
},
// Area is not quite that of a circle. TODO
euclidean:function(radius,include_self){
if(typeof radius==="undefined")
radius=1;
if(typeof include_self==="undefined")
include_self=false;
var output=[];
// A circle of a given diameter always fits inside of a square of the same
// side-length. (This is a bit brute-force)
for(var x=-1*radius;x<=radius;x++)
for(var y=-1*radius;y<=radius;y++)
if(
(include_self||!(x===0&&y===0))&&
(Math.sqrt(Math.pow(x,2)+Math.pow(y,2))<=radius) // Euclidean distance
)
output.push({x:x,y:y});
return output;
}
//TODO https://www.npmjs.com/package/compute-minkowski-distance ?
//TODO Non-Euclidean distance algorithim?
},
__templates:{//an object containing the different templates that are currently in the system
__LIFE__:{//Things like Conway's Game of Life
_convertNumListToBf:function(nl){
Expand All @@ -89,6 +162,8 @@
if(data.pattern.search(/B\d{0,9}\/S\d{0,9}/gi)<=-1)return[];
var numbers=data.pattern.split(/\/?[a-z]/gi);//"B",born,die
data.loop=typeof data.loop!=="undefined"?data.loop:true;
if(typeof data.hitbox!=="undefined")
data.hitbox=innerP.neighborhoods.moore();
console.log("Life Pattern found: ",elm,data);
return [
innerP.__templates.__LIFE__.__LIVE__(
Expand Down Expand Up @@ -121,6 +196,8 @@
if(data.pattern.search(/Rule \d*/gi)<=-1)return[];
var binStates=data.pattern.split(/Rule /gi)[1]-0;
data.loop=typeof data.loop!=="undefined"?data.loop:false;
if(typeof data.hitbox==="undefined")
data.hitbox=innerP.neighborhoods.wolfram();
console.log("Wolfram pattern found: ",elm,data);
return [undefined,innerP.__templates.__WOLFRAM__.__DEAD__(elm,binStates,data.loop)];
},
Expand Down Expand Up @@ -181,6 +258,8 @@
if (typeof data.deadCell==="undefined"&&typeof out[1]==="function") data.deadCell=out[1];
}
}
if(typeof data.hitbox==="undefined")
data.hitbox=innerP.neighborhoods.moore();
innerP.elementTypeMap[elm]=data;//for each element
},
__WhatIs:function(getPixelId) {//Generator for whatIs
Expand All @@ -201,6 +280,8 @@
// ({}? )
//console.log("reset");
//clearInterval(innerP.loopint);
if(typeof canvasSizes==="undefined")
canvasSizes={};
innerP.pause();
var w=innerP.get_width(),
h=innerP.get_height();
Expand All @@ -218,7 +299,6 @@
}
innerP.update();
innerP.ctx.putImageData(innerP.imageData,0,0);
innerP.row=0;
},
pause:function() {//pause canvas iterations
innerP.mode="paused";
Expand Down Expand Up @@ -356,7 +436,6 @@
loop=typeof loop!=="undefined"?loop:true;
var id,name;
if (typeof arry==="string") {
if (!innerP.presentElements.includes(arry)) innerP.presentElements.push(arry);
if(typeof innerP.elementTypeMap[arry]==="undefined")
throw new Error("Color name "+arry+" invalid!")
name=arry
Expand Down Expand Up @@ -392,37 +471,49 @@
x:0,
y:0,
getOldPixelId:getOldPixelId,
confirmOldElm:confirmOldElm,
getOldPixel:innerP.__GetPixel(getOldPixelId),
whatIsOld:innerP.__WhatIs(getOldPixelId),
mooreNearbyCounter:innerP.__MooreNearbyCounter(confirmOldElm),
wolframNearbyCounter:innerP.__WolframNearbyCounter(confirmOldElm),
};
},
updatedDeadPixel=new Uint8Array(Math.ceil((w*h)/8));
innerP.pixelCounts={};
for(;rel.x<w;rel.x++){
for(rel.y=0;rel.y<h;rel.y++){ //iterate through x and y
var currentPix=rel.getOldPixelId(rel.x,rel.y),elm;
rel.oldId=currentPix;
if(currentPix===innerP.defaultId){
for(var i=0;i<innerP.presentElements.length;i++){
elm=innerP.elementTypeMap[innerP.presentElements[i]]
if (typeof elm.deadCell==="function") {
elm.deadCell(rel);//execute function-based externals (dead)
}
}
}else{
var elmname=innerP.elementNumList[currentPix]
elm=innerP.elementTypeMap[elmname]
if (typeof elm.liveCell==="function") {
elm.liveCell(rel);//execute function-based externals (live)
for(var x=0;x<w;x++){
for(var y=0;y<h;y++){ //iterate through x and y
var currentPixId=rel.getOldPixelId(x,y);
if(currentPixId===innerP.defaultId)continue;
var currentPix=innerP.elementNumList[currentPixId],
elm=innerP.elementTypeMap[currentPix];
if(typeof elm.liveCell==="function") {
rel.y=y;
rel.x=x;
rel.oldId=currentPixId;
elm.liveCell(rel);
}
if(typeof innerP.pixelCounts[currentPix]==="undefined") {
innerP.pixelCounts[currentPix]=1;
}else innerP.pixelCounts[currentPix]++;
if (typeof elm.deadCell==="function") {
for(var hi=0;hi<elm.hitbox.length;hi++){
var pixel=elm.hitbox[hi];
rel.x=(x+pixel.x)%w;
rel.y=(y+pixel.y)%h;
var index=(w*rel.y)+Math.floor(rel.x/8),
oldValue=updatedDeadPixel[index],
bitMask=1<<(rel.x%8);
if((oldValue&bitMask)>0)
continue;
// I timed it, and confirmOldElm is slower than all the math above.
if(!rel.confirmOldElm(rel.x,rel.y,innerP.defaultElm))
continue;
rel.oldId=innerP.defaultElm;
elm.deadCell(rel);
updatedDeadPixel[index]=oldValue|bitMask;
}
if (typeof innerP.pixelCounts[elmname]==="undefined") {
innerP.pixelCounts[elmname]=1;
}else innerP.pixelCounts[elmname]++;
}
}
}
innerP.row++;
if (innerP.row>h) innerP.row=0;
innerP.update();
innerP.onAfterIterate();
},
Expand Down

0 comments on commit 3638a95

Please sign in to comment.