Created
May 18, 2021 23:50
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
]); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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