Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created May 18, 2021 23:50
Show Gist options
  • Save roipeker/836deabfbb7f734de5b4ebaac83bb301 to your computer and use it in GitHub Desktop.
Save roipeker/836deabfbb7f734de5b4ebaac83bb301 to your computer and use it in GitHub Desktop.
Trying to mimic AS3 API for BitmapData in Flutter, (pixel data manipulation), not included in GraphX.
/// GraphX DisplayObject.
import 'package:graphx/graphx.dart';
import 'package:graphx_basics/scenes/bitmap_data.dart';
class Bitmap2 extends DisplayObject {
BitmapData _bd;
set bitmapData(BitmapData value) => _bd = value;
BitmapData get bitmapData => _bd;
Bitmap2([BitmapData bd]) {
bitmapData = bd;
}
static final _sHelperMatrix = GxMatrix();
static final _sHelperPoint = GxPoint();
@override
GxRect getBounds(DisplayObject targetSpace, [GxRect out]) {
out ??= GxRect();
final matrix = _sHelperMatrix;
matrix.identity();
getTransformationMatrix(targetSpace, matrix);
if (_bd != null) {
var rect = _bd.rect;
out = MatrixUtils.getTransformedBoundsRect(matrix, rect, out);
} else {
matrix.transformCoords(0, 0, _sHelperPoint);
out.setTo(_sHelperPoint.x, _sHelperPoint.y, 0, 0);
}
return out;
}
Paint _defaultPaint = Paint();
bool get smoothing => _defaultPaint.filterQuality == FilterQuality.high;
set smoothing(bool flag) {
_defaultPaint.filterQuality =
flag ? FilterQuality.high : FilterQuality.none;
}
@override
void $applyPaint(Canvas canvas) {
if (bitmapData.image != null) {
canvas.drawImage(bitmapData.image, Offset.zero, _defaultPaint);
}
// bitmapData.$applyPaint(canvas);
}
}
import 'dart:typed_data';
import 'dart:ui';
import 'package:graphx/graphx.dart';
import 'color_transform.dart';
import 'noise/simplex.dart';
const _cWhite = const Color(0xffffffff);
class BitmapData {
int width, height;
// ByteData _bytes;
GRect rect;
Image _image;
Image get image => _image;
Uint8List _data;
int _len;
Color backgroundColor;
BitmapData(this.width, this.height, {Color color = _cWhite}) {
backgroundColor = color;
_len = width * height * 4;
// _list = List.filled(_len, 0);
_data = Uint8List(_len);
// _bytes = ByteData(width * height * 4);
rect = GRect(0, 0, width.toDouble(), height.toDouble());
if (backgroundColor != kColorTransparent) {
fillRect(rect, backgroundColor);
}
_invalidate();
}
Future<void> floodFill(int x, int y, Color color) async {
final List<_Pnt> queue = [_Pnt(x, y)];
var old = getPixel(x, y);
var iterations = 0;
_locked = true;
var searchBmp = BitmapData(width, height, color: _cWhite);
searchBmp._locked = true;
_Pnt currPoint;
while (queue.length > 0) {
currPoint = queue.removeAt(0);
++iterations;
if (currPoint.x < 0 || currPoint.x >= width) continue;
if (currPoint.y < 0 || currPoint.y >= height) continue;
searchBmp.setPixel(currPoint.x, currPoint.y, kColorTransparent);
if (getPixel(currPoint.x, currPoint.y) == old) {
setPixel(currPoint.x, currPoint.y, color);
if (searchBmp.getPixel(currPoint.x + 1, currPoint.y) == _cWhite) {
queue.add(_Pnt(currPoint.x + 1, currPoint.y));
}
if (searchBmp.getPixel(currPoint.x, currPoint.y + 1) == _cWhite) {
queue.add(_Pnt(currPoint.x, currPoint.y + 1));
}
if (searchBmp.getPixel(currPoint.x - 1, currPoint.y) == _cWhite) {
queue.add(_Pnt(currPoint.x - 1, currPoint.y));
}
if (searchBmp.getPixel(currPoint.x, currPoint.y - 1) == _cWhite) {
queue.add(_Pnt(currPoint.x, currPoint.y - 1));
}
}
}
_locked = false;
await _invalidate();
}
Future<void> colorTransform(
GRect rect,
ColorTransform colorTransform,
) async {
rect ??= this.rect;
final clippedRect = this.rect.intersection(rect);
colorTransform ??= ColorTransform();
var data = _data;
// var xMax = crect.x + crect.height;
// var yMax = crect.y + crect.height;
var x = clippedRect.left.toInt(),
y = clippedRect.top.toInt(),
w = clippedRect.right.toInt(),
h = clippedRect.bottom.toInt();
final rm = colorTransform.redMultiplier;
final ro = colorTransform.redOffset;
final gm = colorTransform.greenMultiplier;
final go = colorTransform.greenOffset;
final bm = colorTransform.blueMultiplier;
final bo = colorTransform.blueOffset;
final am = colorTransform.alphaMultiplier;
final ao = colorTransform.alphaOffset;
for (var j = y; j < h; j++) {
for (var i = x; i < w; i++) {
int r = (j * width + i) * 4;
int g = r + 1;
int b = r + 2;
int a = r + 3;
data[r] = (data[r] * rm + ro).toInt();
data[g] = (data[g] * gm + go).toInt();
data[b] = (data[b] * bm + bo).toInt();
data[a] = (data[a] * am + ao).toInt();
}
}
await _invalidate();
}
Future<void> fillRect(GRect rect, Color color) async {
var clippedRect = this.rect.intersection(rect);
var x = clippedRect.left.toInt(),
y = clippedRect.top.toInt(),
w = clippedRect.right.toInt(),
h = clippedRect.bottom.toInt();
final r = color.red;
final g = color.green;
final b = color.blue;
final a = color.alpha;
for (var i = x; i < w; ++i) {
for (var j = y; j < h; ++j) {
final byteOffset = i * 4 + j * width * 4;
_data[byteOffset + 0] = r;
_data[byteOffset + 1] = g;
_data[byteOffset + 2] = b;
_data[byteOffset + 3] = a;
}
}
await _invalidate();
}
bool _drawing = false;
bool _locked = false;
Future<void> _invalidate() async {
if (_locked) return;
_drawing = true;
await _update();
_drawing = false;
}
_update() async {
var buff2 = await ImmutableBuffer.fromUint8List(_data);
// var buff2 =
// await ImmutableBuffer.fromUint8List(_bytes.buffer.asUint8List());
var desc = ImageDescriptor.raw(
buff2,
width: width,
height: height,
pixelFormat: PixelFormat.rgba8888,
);
var res = await desc.instantiateCodec();
_image = (await res.getNextFrame()).image;
}
setPixel(
int x,
int y,
Color c,
) async {
if (x < 0 || x > width - 1 || y < 0 || y > height - 1) {
trace('BitmapData: range out of bounds', x, y);
return;
}
final byteOffset = x * 4 + y * width * 4;
_data[byteOffset + 0] = c.red;
_data[byteOffset + 1] = c.green;
_data[byteOffset + 2] = c.blue;
_data[byteOffset + 3] = c.alpha;
await _invalidate();
}
Color getPixel(
int x,
int y,
) {
if (x < 0 || x > width - 1 || y < 0 || y > height - 1) return null;
final byteOffset = x * 4 + y * width * 4;
final r = _data[byteOffset + 0];
final g = _data[byteOffset + 1];
final b = _data[byteOffset + 2];
final a = _data[byteOffset + 3];
// final r = _bytes.getUint8(byteOffset);
// final g = _bytes.getUint8(byteOffset + 1);
// final b = _bytes.getUint8(byteOffset + 2);
// final a = _bytes.getUint8(byteOffset + 3);
return Color.fromARGB(a, r, g, b);
}
Uint8List getPixelData() => _data;
Future<void> setPixelData(Uint8List pixelData) async {
_data = pixelData;
if (_drawing) return;
await _invalidate();
}
GTexture get texture {
return GTexture.fromImage(_image);
}
Future<ByteData> _drawImageBytes(
void Function(Canvas canvas) callback, [
GRect cullRect,
]) async {
var rec = PictureRecorder();
cullRect ??= rect;
var canvas = Canvas(rec, cullRect.toNative());
callback(canvas);
var pic = rec.endRecording();
var img = await pic.toImage(width, height);
var bytes = await img.toByteData();
_data = bytes.buffer.asUint8List();
return bytes;
}
Future<void> copyChannel(
BitmapData sourceBitmapData,
GRect sourceRect,
GPoint destPoint,
BitmapDataChannel sourceChannel,
BitmapDataChannel destChannel,
) async {
Color sourceColor;
sourceRect ??= this.rect;
destPoint ??= GPoint();
var sourceX = sourceRect.x.toInt();
var sourceY = sourceRect.y.toInt();
var destX = destPoint.x.toInt();
var destY = destPoint.y.toInt();
_locked = true;
for (var y = 0; y < sourceRect.height; y++) {
for (var x = 0; x < sourceRect.width; x++) {
sourceColor = sourceBitmapData.getPixel(sourceX + x, sourceY + y);
// sourceRGB = hexToRGB(sourceColor);
int channelValue;
switch (sourceChannel) {
case BitmapDataChannel.red:
channelValue = sourceColor.red;
break;
case BitmapDataChannel.green:
channelValue = sourceColor.green;
break;
case BitmapDataChannel.blue:
channelValue = sourceColor.blue;
break;
}
final rgb = getPixel(destX + x, destY + y);
if (rgb == null) continue;
final rgb2 = [rgb.red, rgb.green, rgb.blue];
switch (destChannel) {
case BitmapDataChannel.red:
rgb2[0] = channelValue;
break;
case BitmapDataChannel.green:
rgb2[1] = channelValue;
break;
case BitmapDataChannel.blue:
rgb2[2] = channelValue;
break;
}
setPixel(
destX + x,
destY + y,
Color.fromARGB(rgb.alpha, rgb2[0], rgb2[1], rgb2[2]),
);
}
}
_locked = false;
await _invalidate();
}
PRNG _rand;
Future<void> noise(int randomSeed,
[int low = 0,
int high = 255,
int channelOptions = 7,
bool greyScale = false]) async {
_rand ??= PRNG();
_rand.seed = randomSeed;
low ??= 0;
high ??= 255;
channelOptions ??= 7;
greyScale ??= false;
int pos;
double cr, cg, cb;
double gray;
const int defCol = 0x00;
final hasRed = (channelOptions & BitmapDataChannel.red.value) != 0;
final hasGreen = (channelOptions & BitmapDataChannel.green.value) != 0;
final hasBlue = (channelOptions & BitmapDataChannel.blue.value) != 0;
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
pos = (x + y * this.width) * 4;
cr = _rand.nextRange(low, high);
cg = _rand.nextRange(low, high);
cb = _rand.nextRange(low, high);
if (greyScale) {
gray = (cr + cg + cb) / 3;
cr = cg = cb = gray;
}
_data[pos + 0] = hasRed ? (cr.toInt()) : defCol;
_data[pos + 1] = hasGreen ? (cg.toInt()) : defCol;
_data[pos + 2] = hasBlue ? (cb.toInt()) : defCol;
}
}
await _invalidate();
}
Future<void> paletteMap(
BitmapData sourceBitmapData,
GRect sourceRect,
GPoint destPoint,
List<int> redArray,
List<int> greenArray,
List<int> blueArray,
List<int> alphaArray) async {
var bw = width - sourceRect.width - destPoint.x;
var bh = height - sourceRect.height - destPoint.y;
var dw = (bw < 0)
? sourceRect.width + (width - sourceRect.width - destPoint.x)
: sourceRect.width;
var dh = (bh < 0)
? sourceRect.height + (height - sourceRect.height - destPoint.y)
: sourceRect.height;
// var sourceData = sourceBitmapData.imagedata.data;
var sourceData = sourceBitmapData._data;
int sourcePos;
int destPos, sourceHex;
var r, g, b, pos;
var sx = sourceRect.x.toInt();
var sy = sourceRect.y.toInt();
var sw = sourceBitmapData.width.toInt();
var dx = destPoint.x.toInt();
var dy = destPoint.y.toInt();
var data = _data;
var w = width;
for (int y = 0; y < dh; y++) {
for (int x = 0; x < dw; x++) {
sourcePos = (((x + sx) + (y + sy) * sw) * 4).toInt();
r = sourceData[sourcePos + 0];
g = sourceData[sourcePos + 1];
b = sourceData[sourcePos + 2];
pos = ((x + dx) + (y + dy) * w) * 4;
data[pos + 0] = redArray[r];
data[pos + 1] = greenArray[g];
data[pos + 2] = blueArray[b];
}
}
await _invalidate();
// this.context.putImageData(this.imagedata, 0, 0);
}
SimplexNoise _simplexR, _simplexG, _simplexB;
Future<void> perlinNoise(
int baseX,
int baseY,
int randomSeed, [
int channelOptions = 7,
bool grayScale = false,
]) async {
_rand ??= PRNG();
_rand.seed = randomSeed;
final redChannel = BitmapDataChannel.red;
final greenChannel = BitmapDataChannel.green;
final blueChannel = BitmapDataChannel.blue;
channelOptions ??= 7;
grayScale ??= false;
var data = _data;
int numChannels = 0;
if (channelOptions & redChannel.value != 0) {
_simplexR ??= SimplexNoise();
_simplexR.setSeed(randomSeed);
numChannels++;
}
if (channelOptions & greenChannel.value != 0) {
_simplexG ??= SimplexNoise();
_simplexG.setSeed(randomSeed + 1);
numChannels++;
}
if (channelOptions & blueChannel.value != 0) {
_simplexB ??= SimplexNoise();
_simplexB.setSeed(randomSeed + 2);
numChannels++;
}
final hasRed = (channelOptions & BitmapDataChannel.red.value) != 0;
final hasGreen = (channelOptions & BitmapDataChannel.green.value) != 0;
final hasBlue = (channelOptions & BitmapDataChannel.blue.value) != 0;
int pos, cr, cg, cb;
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
pos = (x + y * this.width) * 4;
cr = hasRed
? Math.floor(
((_simplexR.noise(x / baseX, y / baseY) + 1) * 0.5) * 255,
false)
: 0x00;
cg = hasGreen
? Math.floor(
((_simplexG.noise(x / baseX, y / baseY) + 1) * 0.5) * 255,
false)
: 0x00;
cb = hasBlue
? Math.floor(
((_simplexB.noise(x / baseX, y / baseY) + 1) * 0.5) * 255,
false)
: 0x00;
if (grayScale) {
cr = cg = cb = ((cr + cg + cb) ~/ numChannels);
}
data[pos + 0] = cr;
data[pos + 1] = cg;
data[pos + 2] = cb;
}
}
// this.context.putImageData(this.imagedata, 0, 0);
await _invalidate();
}
static final _thresholdOperations = <String, ThresholdOperation>{
ThresholdOp.less: (int source, int mask, int threshold) =>
(source & mask) < (threshold & mask),
ThresholdOp.lessEq: (int source, int mask, int threshold) =>
(source & mask) <= (threshold & mask),
ThresholdOp.more: (int source, int mask, int threshold) =>
(source & mask) > (threshold & mask),
ThresholdOp.moreEq: (int source, int mask, int threshold) =>
(source & mask) >= (threshold & mask),
ThresholdOp.eq: (int source, int mask, int threshold) =>
(source & mask) == (threshold & mask),
ThresholdOp.neq: (int source, int mask, int threshold) =>
(source & mask) != (threshold & mask),
};
Future<void> threshold(
BitmapData sourceBitmapData,
GRect sourceRect,
GPoint destPoint,
String operation,
int threshold, [
int color = 0x0,
int mask = 0xffffff,
bool copySource = false,
]) async {
color ??= 0x0;
mask ??= 0xffffffff;
copySource ??= false;
sourceRect ??= rect;
destPoint ??= GPoint();
int dpx = destPoint.x.toInt();
int dpy = destPoint.y.toInt();
_locked = true;
if (!ThresholdOp.values.contains(operation)) {
trace(
'BitmapData threshold, invalid operator "$operation", use `ThresholdOp` options if you are unsure of the supported operations.');
return;
}
final colorColor = Color(color);
int bw = (width - sourceRect.width - dpx).toInt();
int bh = (height - sourceRect.height - dpy).toInt();
int dw = ((bw < 0)
? sourceRect.width + (width - sourceRect.width - dpx)
: sourceRect.width)
.toInt();
int dh = ((bh < 0)
? sourceRect.height + (height - sourceRect.height - dpy)
: sourceRect.height)
.toInt();
var sourceData = _data;
int sourcePos, destPos, sourceHex;
int sx = sourceRect.x.toInt();
int sy = sourceRect.y.toInt();
int sw = sourceBitmapData.width.toInt();
final operationCallback = _thresholdOperations[operation];
for (int y = 0; y < dh; y++) {
for (int x = 0; x < dw; x++) {
sourcePos = (((x + sx) + (y + sy) * sw) * 4).toInt();
var sourceColor = Color.fromARGB(
sourceData[sourcePos + 3],
sourceData[sourcePos + 0],
sourceData[sourcePos + 1],
sourceData[sourcePos + 2]);
sourceHex = sourceColor.value;
// sourceHex = RGBToHex({r:sourceData[sourcePos], g:sourceData[sourcePos+1], b:sourceData[sourcePos+2]});
int px = x + dpx;
int py = y + dpy;
final destinationColor = copySource ? sourceColor : colorColor;
if (operationCallback(sourceHex, mask, threshold)) {
setPixel(px, py, destinationColor);
}
}
}
_locked = false;
await _invalidate();
// this.context.putImageData(this.imagedata, 0, 0);
}
// if(fillColor) this.fillRect(this.rect, fillColor);
// else this.fillRect(this.rect, 0);
// return this;
Future<BitmapData> clone() async {
var bd = BitmapData(width, height, color: kColorTransparent);
bd._data = _data.sublist(0, _data.length);
bd._locked = true;
await bd._drawImageBytes((Canvas canvas) {
_drawSelf(canvas);
});
bd._locked = false;
await bd._invalidate();
return bd;
}
Future<void> draw(
Object drawer, [
GMatrix transform,
BlendMode mode = BlendMode.srcOver,
]) async {
final _matrixData = transform?.toNative()?.storage;
final _paint = Paint();
_paint.filterQuality = FilterQuality.high;
_paint.blendMode = mode;
await _drawImageBytes((Canvas canvas) {
// canvas.saveLayer(rect.toNative(), paint)
_drawSelf(canvas);
if (_matrixData != null) {
canvas.transform(_matrixData);
}
if (drawer is GDisplayObject) {
drawer.paint(canvas);
} else if (drawer is Image) {
canvas.drawImage(drawer, Offset.zero, _paint);
} else if (drawer is GTexture) {
canvas.drawImage(drawer.root, Offset.zero, _paint);
}
});
await _invalidate();
}
Future<void> clear([GRect rect]) async {
rect ??= this.rect;
var isBigger = rect != this.rect;
await fillRect(rect, backgroundColor);
await _invalidate();
}
Paint _defaultPaint = Paint();
void $applyPaint(Canvas canvas) {
if (_image != null) {
canvas.drawImage(_image, Offset.zero, _defaultPaint);
}
}
void _drawSelf(Canvas canvas) {
if (_image != null) {
canvas.drawImage(_image, Offset.zero, _defaultPaint);
}
}
}
class _Pnt {
int x, y;
_Pnt(this.x, this.y);
@override
String toString() {
return '_Pnt{x: $x, y: $y}';
}
}
class BitmapDataChannel {
static const BitmapDataChannel red = BitmapDataChannel(1);
static const BitmapDataChannel green = BitmapDataChannel(2);
static const BitmapDataChannel blue = BitmapDataChannel(4);
static const BitmapDataChannel alpha = BitmapDataChannel(8);
final int value;
const BitmapDataChannel(this.value);
}
typedef bool ThresholdOperation(int source, int mask, int threshold);
class ThresholdOp {
static const String less = '<';
static const String lessEq = '<=';
static const String more = '>';
static const String moreEq = '>=';
static const String eq = '==';
static const String neq = '!=';
static const List<String> values = [
less,
lessEq,
more,
moreEq,
eq,
neq,
];
}
class ColorTransform {
double redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier;
int redOffset, greenOffset, blueOffset, alphaOffset;
ColorTransform([
this.redMultiplier = 1.0,
this.greenMultiplier = 1.0,
this.blueMultiplier = 1.0,
this.alphaMultiplier = 1.0,
this.redOffset = 0,
this.greenOffset = 0,
this.blueOffset = 0,
this.alphaOffset = 0,
]);
}
import 'package:graphx/graphx.dart';
/// port from https://github.com/jwagner/simplex-noise.js/
// Park-Miller-Carta Pseudo-Random Number Generator
class PRNG {
PRNG();
int seed = 1;
double next() {
return (gen() / 2147483647);
}
double nextRange(min, max) {
return min + ((max - min) * this.next());
}
int gen() {
return seed = (seed * 16807) % 2147483647;
}
}
class SimplexNoise {
final List<List<double>> grad3 = [
[1, 1, 0],
[-1, 1, 0],
[1, -1, 0],
[-1, -1, 0],
[1, 0, 1],
[-1, 0, 1],
[1, 0, -1],
[-1, 0, -1],
[0, 1, 1],
[0, -1, 1],
[0, 1, -1],
[0, -1, -1]
];
final List<List<double>> simplex = [
[0, 1, 2, 3],
[0, 1, 3, 2],
[0, 0, 0, 0],
[0, 2, 3, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 2, 3, 0],
[0, 2, 1, 3],
[0, 0, 0, 0],
[0, 3, 1, 2],
[0, 3, 2, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 3, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 2, 0, 3],
[0, 0, 0, 0],
[1, 3, 0, 2],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[2, 3, 0, 1],
[2, 3, 1, 0],
[1, 0, 2, 3],
[1, 0, 3, 2],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[2, 0, 3, 1],
[0, 0, 0, 0],
[2, 1, 3, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[2, 0, 1, 3],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[3, 0, 1, 2],
[3, 0, 2, 1],
[0, 0, 0, 0],
[3, 1, 2, 0],
[2, 1, 0, 3],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[3, 1, 0, 2],
[0, 0, 0, 0],
[3, 2, 0, 1],
[3, 2, 1, 0]
];
double rand;
Random _rnd;
SimplexNoise() {
// rand = gen;
// _rnd= Random(seed);
}
var perm = List.filled(512, 0);
setSeed(seed) {
_rnd = Random(seed);
var p = List.filled(256, 0);
// rand.seed = seed;
for (var i = 0; i < 256; i++) {
p[i] = _rnd.nextInt(255);
}
for (var i = 0; i < 512; i++) {
perm[i] = p[i & 255];
}
}
double dot(g, x, y) {
return g[0] * x + g[1] * y;
}
double noise(double xin, double yin) {
var n0, n1, n2;
var F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
var s = (xin + yin) * F2;
int i = Math.floor(xin + s, false);
int j = Math.floor(yin + s, false);
var G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
var t = (i + j) * G2;
var X0 = i - t;
var Y0 = j - t;
var x0 = xin - X0;
var y0 = yin - Y0;
var i1, j1;
if (x0 > y0) {
i1 = 1;
j1 = 0;
} else {
i1 = 0;
j1 = 1;
}
var x1 = x0 - i1 + G2;
var y1 = y0 - j1 + G2;
var x2 = x0 - 1.0 + 2.0 * G2;
var y2 = y0 - 1.0 + 2.0 * G2;
var ii = i & 255;
var jj = j & 255;
var gi0 = perm[ii + perm[jj]] % 12;
var gi1 = perm[ii + i1 + perm[jj + j1]] % 12;
var gi2 = perm[ii + 1 + perm[jj + 1]] % 12;
var t0 = 0.5 - x0 * x0 - y0 * y0;
if (t0 < 0)
n0 = 0.0;
else {
t0 *= t0;
n0 = t0 * t0 * dot(grad3[gi0], x0, y0);
}
var t1 = 0.5 - x1 * x1 - y1 * y1;
if (t1 < 0)
n1 = 0.0;
else {
t1 *= t1;
n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
}
var t2 = 0.5 - x2 * x2 - y2 * y2;
if (t2 < 0)
n2 = 0.0;
else {
t2 *= t2;
n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2);
}
return 70.0 * (n0 + n1 + n2);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment