Description:
Smart contract deployed on Ethereum with Factory features.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/ShackCoords.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "./ShackUtils.sol";
import "./ShackMath.sol";
library ShackCoords {
/** @dev scale and translate the verts
this can be effectively disabled with a scale of 1 and translate of [0, 0, 0]
*/
function convertToWorldSpaceWithModelTransform(
int256[3][3][] memory tris,
int256 scale,
int256[3] memory position
) external view returns (int256[3][] memory) {
int256[3][] memory verts = ShackUtils.flattenTris(tris);
// Scale model matrices are easy, just multiply through by the scale value
int256[3][] memory scaledVerts = new int256[3][](verts.length);
for (uint256 i = 0; i < verts.length; i++) {
scaledVerts[i][0] = verts[i][0] * scale + position[0];
scaledVerts[i][1] = verts[i][1] * scale + position[1];
scaledVerts[i][2] = verts[i][2] * scale + position[2];
}
return scaledVerts;
}
/** @dev run backfaceCulling to save future operations on faces that aren't seen by the camera*/
function backfaceCulling(
int256[3][3][] memory trisWorldSpace,
int256[3][3][] memory trisCols
)
external
view
returns (
int256[3][3][] memory culledTrisWorldSpace,
int256[3][3][] memory culledTrisCols
)
{
culledTrisWorldSpace = new int256[3][3][](trisWorldSpace.length);
culledTrisCols = new int256[3][3][](trisCols.length);
uint256 nextIx;
for (uint256 i = 0; i < trisWorldSpace.length; i++) {
int256[3] memory v1 = trisWorldSpace[i][0];
int256[3] memory v2 = trisWorldSpace[i][1];
int256[3] memory v3 = trisWorldSpace[i][2];
int256[3] memory norm = ShackMath.crossProduct(
ShackMath.vector3Sub(v1, v2),
ShackMath.vector3Sub(v2, v3)
);
/// since Shack has a static positioned camera at the origin,
/// the points are already in view space, relaxing the backfaceCullingCond
int256 backfaceCullingCond = ShackMath.vector3Dot(v1, norm);
if (backfaceCullingCond < 0) {
culledTrisWorldSpace[nextIx] = trisWorldSpace[i];
culledTrisCols[nextIx] = trisCols[i];
nextIx++;
}
}
/// remove any empty slots
uint256 nToCull = culledTrisWorldSpace.length - nextIx;
/// cull uneeded tris
assembly {
mstore(
culledTrisWorldSpace,
sub(mload(culledTrisWorldSpace), nToCull)
)
}
/// cull uneeded cols
assembly {
mstore(culledTrisCols, sub(mload(culledTrisCols), nToCull))
}
}
/**@dev calculate verts in camera space */
function convertToCameraSpaceViaVertexShader(
int256[3][] memory vertsWorldSpace,
int256 canvasDim,
bool perspCamera
) external view returns (int256[3][] memory) {
// get the camera matrix as a numerator and denominator
int256[4][4][2] memory cameraMatrix;
if (perspCamera) {
cameraMatrix = getCameraMatrixPersp();
} else {
cameraMatrix = getCameraMatrixOrth(canvasDim);
}
int256[4][4] memory nM = cameraMatrix[0]; // camera matrix numerator
int256[4][4] memory dM = cameraMatrix[1]; // camera matrix denominator
int256[3][] memory verticesCameraSpace = new int256[3][](
vertsWorldSpace.length
);
for (uint256 i = 0; i < vertsWorldSpace.length; i++) {
// Convert from 3D to 4D homogenous coordinate system
int256[3] memory vert = vertsWorldSpace[i];
// Make a copy of vert ("homoVertex")
int256[] memory hv = new int256[](vert.length + 1);
for (uint256 j = 0; j < vert.length; j++) {
hv[j] = vert[j];
}
// Insert 1 at final position in copy of vert
hv[hv.length - 1] = 1;
int256 x = ((hv[0] * nM[0][0]) / dM[0][0]) +
((hv[1] * nM[0][1]) / dM[0][1]) +
((hv[2] * nM[0][2]) / dM[0][2]) +
(nM[0][3] / dM[0][3]);
int256 y = ((hv[0] * nM[1][0]) / dM[1][0]) +
((hv[1] * nM[1][1]) / dM[1][1]) +
((hv[2] * nM[1][2]) / dM[1][2]) +
(nM[1][3] / dM[1][0]);
int256 z = ((hv[0] * nM[2][0]) / dM[2][0]) +
((hv[1] * nM[2][1]) / dM[2][1]) +
((hv[2] * nM[2][2]) / dM[2][2]) +
(nM[2][3] / dM[2][3]);
int256 w = ((hv[0] * nM[3][0]) / dM[3][0]) +
((hv[1] * nM[3][1]) / dM[3][1]) +
((hv[2] * nM[3][2]) / dM[3][2]) +
(nM[3][3] / dM[3][3]);
if (w != 1) {
x = (x * 1e3) / w;
y = (y * 1e3) / w;
z = (z * 1e3) / w;
}
// Turn it back into a 3-vector
// Add it to the ordered list
verticesCameraSpace[i] = [x, y, z];
}
return verticesCameraSpace;
}
/** @dev generate an orthographic camera matrix */
function getCameraMatrixOrth(int256 canvasDim)
internal
pure
returns (int256[4][4][2] memory)
{
int256 canvasHalf = canvasDim / 2;
// Left, right, top, bottom
int256 r = ShackMath.abs(canvasHalf);
int256 l = -canvasHalf;
int256 t = ShackMath.abs(canvasHalf);
int256 b = -canvasHalf;
// Z settings (near and far)
/// multiplied by 1e3
int256 n = 1;
int256 f = 1024;
// Get the orthographic transform matrix
// as a numerator and denominator
int256[4][4] memory cameraMatrixNum = [
[int256(2), 0, 0, -(r + l)],
[int256(0), 2, 0, -(t + b)],
[int256(0), 0, -2, -(f + n)],
[int256(0), 0, 0, 1]
];
int256[4][4] memory cameraMatrixDen = [
[int256(r - l), 1, 1, (r - l)],
[int256(1), (t - b), 1, (t - b)],
[int256(1), 1, (f - n), (f - n)],
[int256(1), 1, 1, 1]
];
int256[4][4][2] memory cameraMatrix = [
cameraMatrixNum,
cameraMatrixDen
];
return cameraMatrix;
}
/** @dev generate a perspective camera matrix */
function getCameraMatrixPersp()
internal
pure
returns (int256[4][4][2] memory)
{
// Z settings (near and far)
/// multiplied by 1e3
int256 n = 500;
int256 f = 501;
// Get the perspective transform matrix
// as a numerator and denominator
// parameter = 1 / tan(fov in degrees / 2)
// 0.1763 = 1 / tan(160 / 2)
// 1.428 = 1 / tan(70 / 2)
// 1.732 = 1 / tan(60 / 2)
// 2.145 = 1 / tan(50 / 2)
int256[4][4] memory cameraMatrixNum = [
[int256(2145), 0, 0, 0],
[int256(0), 2145, 0, 0],
[int256(0), 0, f, -f * n],
[int256(0), 0, 1, 0]
];
int256[4][4] memory cameraMatrixDen = [
[int256(1000), 1, 1, 1],
[int256(1), 1000, 1, 1],
[int256(1), 1, f - n, f - n],
[int256(1), 1, 1, 1]
];
int256[4][4][2] memory cameraMatrix = [
cameraMatrixNum,
cameraMatrixDen
];
return cameraMatrix;
}
}
"
},
"contracts/ShackMath.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
library ShackMath {
/** @dev Get the minimum of two numbers */
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/** @dev Get the maximum of two numbers */
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/** @dev perform a modulo operation, with support for negative numbers */
function mod(int256 n, int256 m) internal pure returns (int256) {
if (n < 0) {
return ((n % m) + m) % m;
} else {
return n % m;
}
}
/** @dev 'randomly' select n numbers between 0 and m
(useful for getting a randomly sampled index)
*/
function randomIdx(
bytes32 seedModifier,
uint256 n, // number of elements to select
uint256 m // max value of elements
) internal pure returns (uint256[] memory) {
uint256[] memory result = new uint256[](n);
for (uint256 i = 0; i < n; i++) {
result[i] =
uint256(keccak256(abi.encodePacked(seedModifier, i))) %
m;
}
return result;
}
/** @dev create a 2d array and fill with a single value */
function get2dArray(
uint256 m,
uint256 q,
int256 value
) internal pure returns (int256[][] memory) {
/// Create a matrix of values with dimensions (m, q)
int256[][] memory rows = new int256[][](m);
for (uint256 i = 0; i < m; i++) {
int256[] memory row = new int256[](q);
for (uint256 j = 0; j < q; j++) {
row[j] = value;
}
rows[i] = row;
}
return rows;
}
/** @dev get the absolute of a number
*/
function abs(int256 x) internal pure returns (int256) {
assembly {
if slt(x, 0) {
x := sub(0, x)
}
}
return x;
}
/** @dev get the square root of a number
*/
function sqrt(int256 y) internal pure returns (int256 z) {
assembly {
if sgt(y, 3) {
z := y
let x := add(div(y, 2), 1)
for {
} slt(x, z) {
} {
z := x
x := div(add(div(y, x), x), 2)
}
}
if and(slt(y, 4), sgt(y, 0)) {
z := 1
}
}
}
/** @dev get the hypotenuse of a triangle given the length of 2 sides
*/
function hypot(int256 x, int256 y) internal pure returns (int256) {
int256 sumsq;
assembly {
let xsq := mul(x, x)
let ysq := mul(y, y)
sumsq := add(xsq, ysq)
}
return sqrt(sumsq);
}
/** @dev addition between two vectors (size 3)
*/
function vector3Add(int256[3] memory v1, int256[3] memory v2)
internal
pure
returns (int256[3] memory result)
{
assembly {
mstore(result, add(mload(v1), mload(v2)))
mstore(
add(result, 0x20),
add(mload(add(v1, 0x20)), mload(add(v2, 0x20)))
)
mstore(
add(result, 0x40),
add(mload(add(v1, 0x40)), mload(add(v2, 0x40)))
)
}
}
/** @dev subtraction between two vectors (size 3)
*/
function vector3Sub(int256[3] memory v1, int256[3] memory v2)
internal
pure
returns (int256[3] memory result)
{
assembly {
mstore(result, sub(mload(v1), mload(v2)))
mstore(
add(result, 0x20),
sub(mload(add(v1, 0x20)), mload(add(v2, 0x20)))
)
mstore(
add(result, 0x40),
sub(mload(add(v1, 0x40)), mload(add(v2, 0x40)))
)
}
}
/** @dev multiply a vector (size 3) by a constant
*/
function vector3MulScalar(int256[3] memory v, int256 a)
internal
pure
returns (int256[3] memory result)
{
assembly {
mstore(result, mul(mload(v), a))
mstore(add(result, 0x20), mul(mload(add(v, 0x20)), a))
mstore(add(result, 0x40), mul(mload(add(v, 0x40)), a))
}
}
/** @dev divide a vector (size 3) by a constant
*/
function vector3DivScalar(int256[3] memory v, int256 a)
internal
pure
returns (int256[3] memory result)
{
assembly {
mstore(result, sdiv(mload(v), a))
mstore(add(result, 0x20), sdiv(mload(add(v, 0x20)), a))
mstore(add(result, 0x40), sdiv(mload(add(v, 0x40)), a))
}
}
/** @dev get the length of a vector (size 3)
*/
function vector3Len(int256[3] memory v) internal pure returns (int256) {
int256 res;
assembly {
let x := mload(v)
let y := mload(add(v, 0x20))
let z := mload(add(v, 0x40))
res := add(add(mul(x, x), mul(y, y)), mul(z, z))
}
return sqrt(res);
}
/** @dev scale and then normalise a vector (size 3)
*/
function vector3NormX(int256[3] memory v, int256 fidelity)
internal
pure
returns (int256[3] memory result)
{
int256 l = vector3Len(v);
assembly {
mstore(result, sdiv(mul(fidelity, mload(add(v, 0x40))), l))
mstore(
add(result, 0x20),
sdiv(mul(fidelity, mload(add(v, 0x20))), l)
)
mstore(add(result, 0x40), sdiv(mul(fidelity, mload(v)), l))
}
}
/** @dev get the dot-product of two vectors (size 3)
*/
function vector3Dot(int256[3] memory v1, int256[3] memory v2)
internal
view
returns (int256 result)
{
assembly {
result := add(
add(
mul(mload(v1), mload(v2)),
mul(mload(add(v1, 0x20)), mload(add(v2, 0x20)))
),
mul(mload(add(v1, 0x40)), mload(add(v2, 0x40)))
)
}
}
/** @dev get the cross product of two vectors (size 3)
*/
function crossProduct(int256[3] memory v1, int256[3] memory v2)
internal
pure
returns (int256[3] memory result)
{
assembly {
mstore(
result,
sub(
mul(mload(add(v1, 0x20)), mload(add(v2, 0x40))),
mul(mload(add(v1, 0x40)), mload(add(v2, 0x20)))
)
)
mstore(
add(result, 0x20),
sub(
mul(mload(add(v1, 0x40)), mload(v2)),
mul(mload(v1), mload(add(v2, 0x40)))
)
)
mstore(
add(result, 0x40),
sub(
mul(mload(v1), mload(add(v2, 0x20))),
mul(mload(add(v1, 0x20)), mload(v2))
)
)
}
}
/** @dev linearly interpolate between two vectors (size 12)
*/
function vector12Lerp(
int256[12] memory v1,
int256[12] memory v2,
int256 ir,
int256 scaleFactor
) internal view returns (int256[12] memory result) {
int256[12] memory vd = vector12Sub(v2, v1);
// loop through all 12 items
assembly {
let ix
for {
let i := 0
} lt(i, 0xC) {
// (i < 12)
i := add(i, 1)
} {
/// get index of the next element
ix := mul(i, 0x20)
/// store into the result array
mstore(
add(result, ix),
add(
// v1[i] + (ir * vd[i]) / 1e3
mload(add(v1, ix)),
sdiv(mul(ir, mload(add(vd, ix))), 1000)
)
)
}
}
}
/** @dev subtraction between two vectors (size 12)
*/
function vector12Sub(int256[12] memory v1, int256[12] memory v2)
internal
view
returns (int256[12] memory result)
{
// loop through all 12 items
assembly {
let ix
for {
let i := 0
} lt(i, 0xC) {
// (i < 12)
i := add(i, 1)
} {
/// get index of the next element
ix := mul(i, 0x20)
/// store into the result array
mstore(
add(result, ix),
sub(
// v1[ix] - v2[ix]
mload(add(v1, ix)),
mload(add(v2, ix))
)
)
}
}
}
/** @dev map a number from one range into another
*/
function mapRangeToRange(
int256 num,
int256 inMin,
int256 inMax,
int256 outMin,
int256 outMax
) internal pure returns (int256 res) {
assembly {
res := add(
sdiv(
mul(sub(outMax, outMin), sub(num, inMin)),
sub(inMax, inMin)
),
outMin
)
}
}
}
"
},
"contracts/ShackStructs.sol": {
"content": "// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
library ShackStructs {
struct Metadata {
string colorScheme; /// name of the color scheme
string geomSpec; /// name of the geometry specification
uint256 nPrisms; /// number of prisms made
string pseudoSymmetry; /// horizontal, vertical, diagonal
string wireframe; /// enabled or disabled
string inversion; /// enabled or disabled
}
struct RenderParams {
uint256[3][] faces; /// index of verts and colorss used for each face (triangle)
int256[3][] verts; /// x, y, z coordinates used in the geometry
int256[3][] cols; /// colors of each vert
int256[3] objPosition; /// position to place the object
int256 objScale; /// scalar for the object
int256[3][2] backgroundColor; /// color of the background (gradient)
LightingParams lightingParams; /// parameters for the lighting
bool perspCamera; /// true = perspective camera, false = orthographic
bool backfaceCulling; /// whether to implement backface culling (saves gas!)
bool invert; /// whether to invert colors in the final encoding stage
bool wireframe; /// whether to only render edges
}
/// struct for testing lighting
struct LightingParams {
bool applyLighting; /// true = apply lighting, false = don't apply lighting
int256 lightAmbiPower; /// power of the ambient light
int256 lightDiffPower; /// power of the diffuse light
int256 lightSpecPower; /// power of the specular light
uint256 inverseShininess; /// shininess of the material
int256[3] lightPos; /// position of the light
int256[3] lightColSpec; /// color of the specular light
int256[3] lightColDiff; /// color of the diffuse light
int256[3] lightColAmbi; /// color of the ambient light
}
}
"
},
"contracts/ShackUtils.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "./ShackStructs.sol";
library ShackUtils {
string internal constant TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/** @dev Flatten 3d tris array into 2d verts */
function flattenTris(int256[3][3][] memory tris)
internal
pure
returns (int256[3][] memory)
{
/// initialize a dynamic in-memory array
int256[3][] memory flattened = new int256[3][](3 * tris.length);
for (uint256 i = 0; i < tris.length; i++) {
/// tris.length == N
// add values to specific index, as cannot push to array in memory
flattened[(i * 3) + 0] = tris[i][0];
flattened[(i * 3) + 1] = tris[i][1];
flattened[(i * 3) + 2] = tris[i][2];
}
return flattened;
}
/** @dev Unflatten 2d verts array into 3d tries (inverse of flattenTris function) */
function unflattenVertsToTris(int256[3][] memory verts)
internal
pure
returns (int256[3][3][] memory)
{
/// initialize an array with length = 1/3 length of verts
int256[3][3][] memory tris = new int256[3][3][](verts.length / 3);
for (uint256 i = 0; i < verts.length; i += 3) {
tris[i / 3] = [verts[i], verts[i + 1], verts[i + 2]];
}
return tris;
}
/** @dev clip an array to a certain length (to trim empty tail slots) */
function clipArray12ToLength(int256[12][] memory arr, uint256 desiredLen)
internal
pure
returns (int256[12][] memory)
{
uint256 nToCull = arr.length - desiredLen;
assembly {
mstore(arr, sub(mload(arr), nToCull))
}
return arr;
}
/** @dev convert an unsigned int to a string */
function uint2str(uint256 _i)
internal
pure
returns (string memory _uintAsString)
{
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
/** @dev get the hex encoding of various powers of 2 (canvas size options) */
function getHex(uint256 _i) internal pure returns (bytes memory _hex) {
if (_i == 8) {
return hex"08_00_00_00";
} else if (_i == 16) {
return hex"10_00_00_00";
} else if (_i == 32) {
return hex"20_00_00_00";
} else if (_i == 64) {
return hex"40_00_00_00";
} else if (_i == 128) {
return hex"80_00_00_00";
} else if (_i == 256) {
return hex"00_01_00_00";
} else if (_i == 512) {
return hex"00_02_00_00";
}
}
/** @dev create an svg container for a bitmap (for display on svg-only platforms) */
function getSVGContainer(
string memory encodedBitmap,
int256 canvasDim,
uint256 outputHeight,
uint256 outputWidth
) internal view returns (string memory) {
uint256 canvasDimUnsigned = uint256(canvasDim);
// construct some elements in memory prior to return string to avoid stack too deep
bytes memory imgSize = abi.encodePacked(
"width='",
ShackUtils.uint2str(canvasDimUnsigned),
"' height='",
ShackUtils.uint2str(canvasDimUnsigned),
"'"
);
bytes memory canvasSize = abi.encodePacked(
"width='",
ShackUtils.uint2str(outputWidth),
"' height='",
ShackUtils.uint2str(outputHeight),
"'"
);
bytes memory scaleStartTag = abi.encodePacked(
"<g transform='scale(",
ShackUtils.uint2str(outputWidth / canvasDimUnsigned),
")'>"
);
return
string(
abi.encodePacked(
"data:image/svg+xml;base64,",
Base64.encode(
abi.encodePacked(
"<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ",
"shape-rendering='crispEdges' ",
canvasSize,
">",
scaleStartTag,
"<image ",
imgSize,
" style='image-rendering: pixelated; image-rendering: crisp-edges;' ",
"href='",
encodedBitmap,
"'/></g></svg>"
)
)
)
);
}
/** @dev converts raw metadata into */
function getAttributes(ShackStructs.Metadata memory metadata)
internal
pure
returns (bytes memory)
{
return
abi.encodePacked(
"{",
'"Structure": "',
metadata.geomSpec,
'", "Chroma": "',
metadata.colorScheme,
'", "Pseudosymmetry": "',
metadata.pseudoSymmetry,
'", "Wireframe": "',
metadata.wireframe,
'", "Inversion": "',
metadata.inversion,
'", "Prisms": "',
uint2str(metadata.nPrisms),
'"}'
);
}
/** @dev create and encode the token's metadata */
function getEncodedMetadata(
string memory image,
ShackStructs.Metadata memory metadata,
uint256 tokenId
) internal view returns (string memory) {
/// get attributes and description here to avoid stack too deep
string
memory description = '"description": "Shack is the first general-purpose 3D renderer'
" running on the Ethereum blockchain."
' Each piece represents a leap forward in on-chain computer graphics, and the collection itself is an NFT first."';
return
string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "Shack Genesis #',
uint2str(tokenId),
'", ',
description,
', "attributes":',
getAttributes(metadata),
', "image":"',
image,
'"}'
)
)
)
)
)
);
}
// fragment =
// [ canvas_x, canvas_y, depth, col_x, col_y, col_z, normal_x, normal_y, normal_z, world_x, world_y, world_z ],
/** @dev get an encoded 2d bitmap by combining the object and background fragments */
function getEncodedBitmap(
int256[12][] memory fragments,
int256[5][] memory background,
int256 canvasDim,
bool invert
) internal view returns (string memory) {
uint256 canvasDimUnsigned = uint256(canvasDim);
bytes memory fileHeader = abi.encodePacked(
hex"42_4d", // BM
hex"36_04_00_00", // size of the bitmap file in bytes (14 (file header) + 40 (info header) + size of raw data (1024))
hex"00_00_00_00", // 2x2 bytes reserved
hex"36_00_00_00" // offset of pixels in bytes
);
bytes memory infoHeader = abi.encodePacked(
hex"28_00_00_00", // size of the header in bytes (40)
getHex(canvasDimUnsigned), // width in pixels 32
getHex(canvasDimUnsigned), // height in pixels 32
hex"01_00", // number of color plans (must be 1)
hex"18_00", // number of bits per pixel (24)
hex"00_00_00_00", // type of compression (none)
hex"00_04_00_00", // size of the raw bitmap data (1024)
hex"C4_0E_00_00", // horizontal resolution
hex"C4_0E_00_00", // vertical resolution
hex"00_00_00_00", // number of used colours
hex"05_00_00_00" // number of important colours
);
bytes memory headers = abi.encodePacked(fileHeader, infoHeader);
/// create a container for the bitmap's bytes
bytes memory bytesArray = new bytes(3 * canvasDimUnsigned**2);
/// write the background first so it is behind the fragments
bytesArray = writeBackgroundToBytesArray(
background,
bytesArray,
canvasDimUnsigned,
invert
);
bytesArray = writeFragmentsToBytesArray(
fragments,
bytesArray,
canvasDimUnsigned,
invert
);
return
string(
abi.encodePacked(
"data:image/bmp;base64,",
Base64.encode(BytesUtils.MergeBytes(headers, bytesArray))
)
);
}
/** @dev write the fragments to the bytes array */
function writeFragmentsToBytesArray(
int256[12][] memory fragments,
bytes memory bytesArray,
uint256 canvasDimUnsigned,
bool invert
) internal pure returns (bytes memory) {
/// loop through each fragment
/// and write it's color into bytesArray in its canvas equivelant position
for (uint256 i = 0; i < fragments.length; i++) {
/// check if x and y are both greater than 0
if (
uint256(fragments[i][0]) >= 0 && uint256(fragments[i][1]) >= 0
) {
/// calculating the starting bytesArray ix for this fragment's colors
uint256 flatIx = ((canvasDimUnsigned -
uint256(fragments[i][1]) -
1) *
canvasDimUnsigned +
(canvasDimUnsigned - uint256(fragments[i][0]) - 1)) * 3;
/// red
uint256 r = fragments[i][3] > 255
? 255
: uint256(fragments[i][3]);
/// green
uint256 g = fragments[i][4] > 255
? 255
: uint256(fragments[i][4]);
/// blue
uint256 b = fragments[i][5] > 255
? 255
: uint256(fragments[i][5]);
if (invert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
bytesArray[flatIx + 0] = bytes1(uint8(b));
bytesArray[flatIx + 1] = bytes1(uint8(g));
bytesArray[flatIx + 2] = bytes1(uint8(r));
}
}
return bytesArray;
}
/** @dev write the fragments to the bytes array
using a separate function from above to account for variable input size
*/
function writeBackgroundToBytesArray(
int256[5][] memory background,
bytes memory bytesArray,
uint256 canvasDimUnsigned,
bool invert
) internal pure returns (bytes memory) {
/// loop through each fragment
/// and write it's color into bytesArray in its canvas equivelant position
for (uint256 i = 0; i < background.length; i++) {
/// check if x and y are both greater than 0
if (
uint256(background[i][0]) >= 0 && uint256(background[i][1]) >= 0
) {
/// calculating the starting bytesArray ix for this fragment's colors
uint256 flatIx = (uint256(background[i][1]) *
canvasDimUnsigned +
uint256(background[i][0])) * 3;
// red
uint256 r = background[i][2] > 255
? 255
: uint256(background[i][2]);
/// green
uint256 g = background[i][3] > 255
? 255
: uint256(background[i][3]);
// blue
uint256 b = background[i][4] > 255
? 255
: uint256(background[i][4]);
if (invert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
bytesArray[flatIx + 0] = bytes1(uint8(b));
bytesArray[flatIx + 1] = bytes1(uint8(g));
bytesArray[flatIx + 2] = bytes1(uint8(r));
}
}
return bytesArray;
}
}
/// [MIT License]
/// @title Base64
/// @notice Provides a function for encoding some bytes in base64
/// @author Brecht Devos <brecht@loopring.org>
library Base64 {
bytes internal constant TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/// @notice Encodes some bytes to the base64 representation
function encode(bytes memory data) internal view returns (string memory) {
uint256 len = data.length;
if (len == 0) return "";
// multiply by 4/3 rounded up
uint256 encodedLen = 4 * ((len + 2) / 3);
// Add some extra buffer at the end
bytes memory result = new bytes(encodedLen + 32);
bytes memory table = TABLE;
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let i := 0
} lt(i, len) {
} {
i := add(i, 3)
let input := and(mload(add(data, i)), 0xffffff)
let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF)
)
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF)
)
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(input, 0x3F))), 0xFF)
)
out := shl(224, out)
mstore(resultPtr, out)
resultPtr := add(resultPtr, 4)
}
switch mod(len, 3)
case 1 {
mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
}
case 2 {
mstore(sub(resultPtr, 1), shl(248, 0x3d))
}
mstore(result, encodedLen)
}
return string(result);
}
}
library BytesUtils {
function char(bytes1 b) internal view returns (bytes1 c) {
if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
else return bytes1(uint8(b) + 0x57);
}
function bytes32string(bytes32 b32)
internal
view
returns (string memory out)
{
bytes memory s = new bytes(64);
for (uint32 i = 0; i < 32; i++) {
bytes1 b = bytes1(b32[i]);
bytes1 hi = bytes1(uint8(b) / 16);
bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
s[i * 2] = char(hi);
s[i * 2 + 1] = char(lo);
}
out = string(s);
}
function hach(string memory value) internal view returns (string memory) {
return bytes32string(sha256(abi.encodePacked(value)));
}
function MergeBytes(bytes memory a, bytes memory b)
internal
pure
returns (bytes memory c)
{
// Store the length of the first array
uint256 alen = a.length;
// Store the length of BOTH arrays
uint256 totallen = alen + b.length;
// Count the loops required for array a (sets of 32 bytes)
uint256 loopsa = (a.length + 31) / 32;
// Count the loops required for array b (sets of 32 bytes)
uint256 loopsb = (b.length + 31) / 32;
assembly {
let m := mload(0x40)
// Load the length of both arrays to the head of the new bytes array
mstore(m, totallen)
// Add the contents of a to the array
for {
let i := 0
} lt(i, loopsa) {
i := add(1, i)
} {
mstore(
add(m, mul(32, add(1, i))),
mload(add(a, mul(32, add(1, i))))
)
}
// Add the contents of b to the array
for {
let i := 0
} lt(i, loopsb) {
i := add(1, i)
} {
mstore(
add(m, add(mul(32, add(1, i)), alen)),
mload(add(b, mul(32, add(1, i))))
)
}
mstore(0x40, add(m, add(32, totallen)))
c := m
}
}
}
"
}
},
"settings": {
"evmVersion": "prague",
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-11-04 18:36:55
Comments
Log in to comment.
No comments yet.