ETH Price: $2,527.92 (+0.66%)

Contract Diff Checker

Contract Name:
PNounsPrivider

Contract Source Code:

// SPDX-License-Identifier: MIT

/**
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import { Ownable } from '@openzeppelin/contracts/access/Ownable.sol';
import 'assetprovider.sol/IAssetProvider.sol';
import 'randomizer.sol/Randomizer.sol';
import '@openzeppelin/contracts/interfaces/IERC165.sol';
import '../packages/graphics/Path.sol';
import '../packages/graphics/SVG.sol';
import '../packages/graphics/Text.sol';
import '../packages/graphics/IFontProvider.sol';

contract PNounsPrivider is IAssetProviderEx, Ownable, IERC165 {
  using Strings for uint256;
  using Randomizer for Randomizer.Seed;
  using Vector for Vector.Struct;
  using Path for uint[];
  using SVG for SVG.Element;
  using TX for string;
  using Trigonometry for uint;

  IFontProvider public immutable font;
  IAssetProvider public immutable nounsProvider;

  constructor(IFontProvider _font, IAssetProvider _nounsProvider) {
    font = _font;
    nounsProvider = _nounsProvider;
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return
      interfaceId == type(IAssetProvider).interfaceId ||
      interfaceId == type(IAssetProviderEx).interfaceId ||
      interfaceId == type(IERC165).interfaceId;
  }

  function getOwner() external view override returns (address) {
    return owner();
  }

  function getProviderInfo() external view override returns (ProviderInfo memory) {
    return ProviderInfo('pnouns', 'pNouns', this);
  }

  function totalSupply() external pure override returns (uint256) {
    return 0;
  }

  function processPayout(uint256 _assetId) external payable override {
    address payable payableTo = payable(owner());
    payableTo.transfer(msg.value);
    emit Payout('pnouns', _assetId, payableTo, msg.value);
  }

  function generateTraits(uint256 _assetId) external pure override returns (string memory traits) {
    // nothing to return
  }

  // Hack to deal with too many stack variables
  struct Stackframe {
    uint trait; // 0:small, 1:middle, 2:large
    uint degree;
    uint distance;
    uint radius;
    uint rotate;
    int x;
    int y;
  }

  function circles(uint _assetId, string[] memory idNouns) internal pure returns (SVG.Element memory) {
    string[4] memory colors = ['red', 'green', 'yellow', 'blue'];
    uint count = 10;
    SVG.Element[] memory elements = new SVG.Element[](count);
    Randomizer.Seed memory seed = Randomizer.Seed(_assetId, 0);

    for (uint i = 0; i < count; i++) {
      Stackframe memory stack;
      stack.trait = (i + 1) / 4; // 3:4:3
      if (stack.trait == 0) {
        (seed, stack.distance) = seed.random(100);
        stack.distance += 380;
        (seed, stack.radius) = seed.random(40);
        stack.radius += 40;
        (seed, stack.rotate) = seed.random(360);
      } else if (stack.trait == 1) {
        (seed, stack.distance) = seed.random(100);
        stack.distance += 200;
        (seed, stack.radius) = seed.random(70);
        stack.radius += 70;
        (seed, stack.rotate) = seed.random(240);
        stack.rotate += 240;
      } else {
        (seed, stack.distance) = seed.random(180);
        (seed, stack.radius) = seed.random(70);
        stack.radius += 180;
        (seed, stack.rotate) = seed.random(120);
        stack.rotate += 300;
      }
      (seed, stack.degree) = seed.random(0x4000);
      stack.x = 512 + (stack.degree.cos() * int(stack.distance)) / Vector.ONE;
      stack.y = 512 + (stack.degree.sin() * int(stack.distance)) / Vector.ONE;
      elements[i] = SVG.group(
        [
          SVG.use(idNouns[i % idNouns.length]).transform(
            TX
              .translate(stack.x - int(stack.radius), stack.y - int(stack.radius))
              .scale1000((1000 * stack.radius) / 512)
              .rotate(string(abi.encodePacked(stack.rotate.toString(), ',512,512')))
          ),
          SVG.circle(stack.x, stack.y, int(stack.radius + stack.radius / 10)).fill(colors[i % 4]).opacity('0.333')
        ]
      );
    }
    return SVG.group(elements);
  }

  struct StackFrame2 {
    uint width;
    SVG.Element pnouns;
    string[] idNouns;
    SVG.Element[] svgNouns;
    string svg;
    string seriesText;
    SVG.Element series;
  }

  function generateSVGPart(uint256 _assetId) public view override returns (string memory svgPart, string memory tag) {
    StackFrame2 memory stack;
    tag = string(abi.encodePacked('circles', _assetId.toString()));
    stack.width = SVG.textWidth(font, 'pNouns');
    stack.pnouns = SVG.text(font, 'pNouns').fill('#224455').transform(TX.scale1000((1000 * 1024) / stack.width));

    if (_assetId < 10) {
      stack.seriesText = string(abi.encodePacked('000', _assetId.toString(), '/2000'));
    } else if (_assetId < 100) {
      stack.seriesText = string(abi.encodePacked('00', _assetId.toString(), '/2000'));
    } else if (_assetId < 1000) {
      stack.seriesText = string(abi.encodePacked('0', _assetId.toString(), '/2000'));
    } else {
      stack.seriesText = string(abi.encodePacked(_assetId.toString(), '/2000'));
    }
    stack.width = SVG.textWidth(font, stack.seriesText);
    stack.series = SVG.text(font, stack.seriesText).fill('#224455').transform(
      TX.translate(1024 - int(stack.width / 10), 1024 - 102).scale('0.1')
    );

    stack.idNouns = new string[](3);
    stack.svgNouns = new SVG.Element[](3);
    for (uint i = 0; i < stack.idNouns.length; i++) {
      (stack.svg, stack.idNouns[i]) = nounsProvider.generateSVGPart(i + _assetId);
      stack.svgNouns[i] = SVG.element(bytes(stack.svg));
    }

    svgPart = string(
      SVG
        .list(
          [
            SVG.list(stack.svgNouns),
            SVG
              .group(
                [
                  circles(_assetId, stack.idNouns).transform('translate(102,204) scale(0.8)'),
                  stack.pnouns,
                  stack.series
                ]
              )
              .id(tag)
          ]
        )
        .svg()
    );
  }

  function generateSVGDocument(uint256 _assetId) external view override returns (string memory document) {
    string memory svgPart;
    string memory tag;
    (svgPart, tag) = generateSVGPart(_assetId);
    document = SVG.document('0 0 1024 1024', bytes(svgPart), SVG.use(tag).svg());
  }
}

// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

library Text {
  function extractLine(
    string memory _text,
    uint _index,
    uint _ch
  ) internal pure returns (string memory line, uint index) {
    uint length = bytes(_text).length;
    assembly {
      line := mload(0x40)
      let wbuf := add(line, 0x20)
      let rbuf := add(add(_text, 0x20), _index)
      let word := 0
      let shift := 0
      let i
      for {
        i := _index
      } lt(i, length) {
        i := add(i, 1)
      } {
        if eq(shift, 0) {
          word := mload(rbuf)
          mstore(wbuf, word)
          rbuf := add(rbuf, 0x20)
          wbuf := add(wbuf, 0x20)
          shift := 256
        }
        shift := sub(shift, 8)
        if eq(and(shr(shift, word), 0xff), _ch) {
          length := i
        }
      }

      index := i
      length := sub(i, _index)
      mstore(line, length) //sub(i, _index))
      mstore(0x40, add(add(line, 0x20), length))
    }
  }

  function split(string memory _str, uint _ch) internal pure returns (string[] memory strs) {
    uint length = bytes(_str).length;
    uint count;
    for (uint i = 0; i < length; i += 1) {
      (, i) = extractLine(_str, i, _ch);
      count += 1;
    }
    strs = new string[](count);
    count = 0;
    for (uint i = 0; i < length; i += 1) {
      (strs[count], i) = extractLine(_str, i, _ch);
      count += 1;
    }
  }
}

// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import 'bytes-array.sol/BytesArray.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import './IFontProvider.sol';
import './Path.sol';
import './Transform.sol';

library SVG {
  using Strings for uint;
  using BytesArray for bytes[];

  struct Attribute {
    string key;
    string value;
  }

  struct Element {
    bytes head;
    bytes tail;
    Attribute[] attrs;
  }

  function path(bytes memory _path) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<path d="', _path);
    elem.tail = bytes('"/>\n');
  }

  function char(IFontProvider _font, string memory _char) internal view returns (Element memory elem) {
    elem = SVG.path(Path.decode(_font.pathOf(_char)));
  }

  function textWidth(IFontProvider _font, string memory _str) internal view returns (uint x) {
    bytes memory data = bytes(_str);
    bytes memory ch = new bytes(1);
    for (uint i = 0; i < data.length; i++) {
      ch[0] = data[i];
      x += _font.widthOf(string(ch));
    }
  }

  function text(IFontProvider _font, string[2] memory _strs, uint _width) internal view returns (Element memory elem) {
    string[] memory strs = new string[](2);
    strs[0] = _strs[0];
    strs[1] = _strs[1];
    elem = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[3] memory _strs, uint _width) internal view returns (Element memory elem) {
    string[] memory strs = new string[](3);
    strs[0] = _strs[0];
    strs[1] = _strs[1];
    strs[2] = _strs[2];
    elem = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[4] memory _strs, uint _width) internal view returns (Element memory elem) {
    string[] memory strs = new string[](4);
    for (uint i = 0; i < _strs.length; i++) {
      strs[i] = _strs[i];
    }
    elem = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[5] memory _strs, uint _width) internal view returns (Element memory elem) {
    string[] memory strs = new string[](5);
    for (uint i = 0; i < _strs.length; i++) {
      strs[i] = _strs[i];
    }
    elem = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[6] memory _strs, uint _width) internal view returns (Element memory elem) {
    string[] memory strs = new string[](6);
    for (uint i = 0; i < _strs.length; i++) {
      strs[i] = _strs[i];
    }
    elem = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[7] memory _strs, uint _width) internal view returns (Element memory elem) {
    string[] memory strs = new string[](7);
    for (uint i = 0; i < _strs.length; i++) {
      strs[i] = _strs[i];
    }
    elem = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[] memory _strs, uint _width) internal view returns (Element memory elem) {
    uint height = _font.height();
    uint maxWidth = _width;
    Element[] memory elems = new Element[](_strs.length);
    for (uint i = 0; i < _strs.length; i++) {
      uint width = textWidth(_font, _strs[i]);
      if (width > maxWidth) {
        maxWidth = width;
      }
      elems[i] = transform(text(_font, _strs[i]), TX.translate(0, int(height * i)));
    }
    // extra group is necessary to let it transform
    elem = group(svg(transform(group(elems), TX.scale1000((1000 * _width) / maxWidth))));
  }

  function text(IFontProvider _font, string memory _str) internal view returns (Element memory elem) {
    bytes memory data = bytes(_str);
    bytes memory ch = new bytes(1);
    Element[] memory elems = new Element[](data.length);
    uint x;
    for (uint i = 0; i < data.length; i++) {
      ch[0] = data[i];
      elems[i] = SVG.path(Path.decode(_font.pathOf(string(ch))));
      if (x > 0) {
        elems[i] = transform(elems[i], string(abi.encodePacked('translate(', x.toString(), ' 0)')));
      }
      x += _font.widthOf(string(ch));
    }
    elem = group(elems);
  }

  function circle(int _cx, int _cy, int _radius) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked(
      '<circle cx="',
      uint(_cx).toString(),
      '" cy="',
      uint(_cy).toString(),
      '" r="',
      uint(_radius).toString()
    );
    elem.tail = '"/>\n';
  }

  function ellipse(int _cx, int _cy, int _rx, int _ry) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked(
      '<ellipse cx="',
      uint(_cx).toString(),
      '" cy="',
      uint(_cy).toString(),
      '" rx="',
      uint(_rx).toString(),
      '" ry="',
      uint(_ry).toString()
    );
    elem.tail = '"/>\n';
  }

  function rect(int _x, int _y, uint _width, uint _height) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked(
      '<rect x="',
      uint(_x).toString(),
      '" y="',
      uint(_y).toString(),
      '" width="',
      _width.toString(),
      '" height="',
      _height.toString()
    );
    elem.tail = '"/>\n';
  }

  function rect() internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<rect width="100%" height="100%');
    elem.tail = '"/>\n';
  }

  function stop(uint ratio) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<stop offset="', ratio.toString(), '%');
    elem.tail = '"/>\n';
  }

  function use(string memory _id) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<use href="#', _id);
    elem.tail = '"/>\n';
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[8] memory _elements) internal pure returns (bytes memory output) {
    bytes[] memory svgs = new bytes[](8);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    svgs[2] = svg(_elements[2]);
    svgs[3] = svg(_elements[3]);
    svgs[4] = svg(_elements[4]);
    svgs[5] = svg(_elements[5]);
    svgs[6] = svg(_elements[6]);
    svgs[7] = svg(_elements[7]);
    output = svgs.packed();
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[4] memory _elements) internal pure returns (bytes memory output) {
    bytes[] memory svgs = new bytes[](4);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    svgs[2] = svg(_elements[2]);
    svgs[3] = svg(_elements[3]);
    output = svgs.packed();
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[3] memory _elements) internal pure returns (bytes memory output) {
    bytes[] memory svgs = new bytes[](3);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    svgs[2] = svg(_elements[2]);
    output = svgs.packed();
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[2] memory _elements) internal pure returns (bytes memory output) {
    bytes[] memory svgs = new bytes[](2);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    output = svgs.packed();
  }

  function packed(Element[] memory _elements) internal pure returns (bytes memory output) {
    bytes[] memory svgs = new bytes[](_elements.length);
    for (uint i = 0; i < _elements.length; i++) {
      svgs[i] = svg(_elements[i]);
    }
    output = svgs.packed();
  }

  function pattern(
    string memory _id,
    string memory _viewbox,
    string memory _width,
    string memory _height,
    bytes memory _elements
  ) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked(
      '<pattern id="',
      _id,
      '" viewBox="',
      _viewbox,
      '" width="',
      _width,
      '" height="',
      _height
    );
    elem.tail = abi.encodePacked('">', _elements, '</pattern>\n');
  }

  function pattern(
    string memory _id,
    string memory _viewbox,
    string memory _width,
    string memory _height,
    Element memory _element
  ) internal pure returns (Element memory elem) {
    elem = pattern(_id, _viewbox, _width, _height, svg(_element));
  }

  function filter(string memory _id, bytes memory _elements) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<filter id="', _id);
    elem.tail = abi.encodePacked('">', _elements, '</filter>\n');
  }

  function filter(string memory _id, Element memory _element) internal pure returns (Element memory elem) {
    elem = filter(_id, svg(_element));
  }

  function feGaussianBlur(string memory _src, string memory _stdDeviation) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<feGaussianBlur in="', _src, '" stdDeviation="', _stdDeviation);
    elem.tail = '" />';
  }

  /*
      '  <feOffset result="offOut" in="SourceAlpha" dx="24" dy="32" />\n'
      '  <feGaussianBlur result="blurOut" in="offOut" stdDeviation="16" />\n'
      '  <feBlend in="SourceGraphic" in2="blurOut" mode="normal" />\n'
  */
  function feOffset(
    string memory _src,
    string memory _dx,
    string memory _dy
  ) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<feOffset in="', _src, '" dx="', _dx, '" dy="', _dy);
    elem.tail = '" />';
  }

  function feBlend(
    string memory _src,
    string memory _src2,
    string memory _mode
  ) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<feBlend in="', _src, '" in2="', _src2, '" mode="', _mode);
    elem.tail = '" />';
  }

  function linearGradient(string memory _id, bytes memory _elements) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<linearGradient id="', _id);
    elem.tail = abi.encodePacked('">', _elements, '</linearGradient>\n');
  }

  function linearGradient(string memory _id, Element memory _element) internal pure returns (Element memory elem) {
    elem = linearGradient(_id, svg(_element));
  }

  function radialGradient(string memory _id, bytes memory _elements) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<radialGradient id="', _id);
    elem.tail = abi.encodePacked('">', _elements, '</radialGradient>\n');
  }

  function radialGradient(string memory _id, Element memory _element) internal pure returns (Element memory elem) {
    elem = radialGradient(_id, svg(_element));
  }

  function group(bytes memory _elements) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<g x_x="x'); // HACK: dummy header for trailing '"'
    elem.tail = abi.encodePacked('">', _elements, '</g>\n');
  }

  function group(Element memory _element) internal pure returns (Element memory elem) {
    elem = group(svg(_element));
  }

  function group(Element[] memory _elements) internal pure returns (Element memory elem) {
    elem = group(packed(_elements));
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function group(Element[2] memory _elements) internal pure returns (Element memory elem) {
    elem = group(packed(_elements));
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function group(Element[3] memory _elements) internal pure returns (Element memory elem) {
    elem = group(packed(_elements));
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function group(Element[4] memory _elements) internal pure returns (Element memory elem) {
    elem = group(packed(_elements));
  }

  function group(Element[8] memory _elements) internal pure returns (Element memory elem) {
    elem = group(packed(_elements));
  }

  function element(bytes memory _body) internal pure returns (Element memory elem) {
    elem.tail = _body;
  }

  function list(Element[] memory _elements) internal pure returns (Element memory elem) {
    elem.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[2] memory _elements) internal pure returns (Element memory elem) {
    elem.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[3] memory _elements) internal pure returns (Element memory elem) {
    elem.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[4] memory _elements) internal pure returns (Element memory elem) {
    elem.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[8] memory _elements) internal pure returns (Element memory elem) {
    elem.tail = packed(_elements);
  }

  function mask(string memory _id, bytes memory _elements) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<mask id="', _id, ''); // HACK: dummy header for trailing '"'
    elem.tail = abi.encodePacked(
      '">'
      '<rect x="0" y="0" width="100%" height="100%" fill="black"/>'
      '<g fill="white">',
      _elements,
      '</g>'
      '</mask>\n'
    );
  }

  function mask(string memory _id, Element memory _element) internal pure returns (Element memory elem) {
    elem = mask(_id, svg(_element));
  }

  function stencil(bytes memory _elements) internal pure returns (Element memory elem) {
    elem.head = abi.encodePacked('<mask x_x="x'); // HACK: dummy header for trailing '"'
    elem.tail = abi.encodePacked(
      '">'
      '<rect x="0" y="0" width="100%" height="100%" fill="white"/>'
      '<g fill="black">',
      _elements,
      '</g>'
      '</mask>\n'
    );
  }

  function stencil(Element memory _element) internal pure returns (Element memory elem) {
    elem = stencil(svg(_element));
  }

  function _append(Element memory _element, Attribute memory _attr) internal pure returns (Element memory elem) {
    elem.head = _element.head;
    elem.tail = _element.tail;
    elem.attrs = new Attribute[](_element.attrs.length + 1);
    for (uint i = 0; i < _element.attrs.length; i++) {
      elem.attrs[i] = _element.attrs[i];
    }
    elem.attrs[_element.attrs.length] = _attr;
  }

  function _append2(
    Element memory _element,
    Attribute memory _attr,
    Attribute memory _attr2
  ) internal pure returns (Element memory elem) {
    elem.head = _element.head;
    elem.tail = _element.tail;
    elem.attrs = new Attribute[](_element.attrs.length + 2);
    for (uint i = 0; i < _element.attrs.length; i++) {
      elem.attrs[i] = _element.attrs[i];
    }
    elem.attrs[_element.attrs.length] = _attr;
    elem.attrs[_element.attrs.length + 1] = _attr2;
  }

  function id(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('id', _value));
  }

  function fill(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('fill', _value));
  }

  function opacity(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('opacity', _value));
  }

  function stopColor(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('stop-color', _value));
  }

  function x1(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('x1', _value));
  }

  function x2(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('x2', _value));
  }

  function y1(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('y1', _value));
  }

  function y2(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('y2', _value));
  }

  function cx(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('cy', _value));
  }

  function cy(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('cy', _value));
  }

  function r(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('r', _value));
  }

  function fx(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('fx', _value));
  }

  function fy(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('fy', _value));
  }

  function result(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('result', _value));
  }

  function fillRef(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('fill', string(abi.encodePacked('url(#', _value, ')'))));
  }

  function filter(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('filter', string(abi.encodePacked('url(#', _value, ')'))));
  }

  function style(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('style', _value));
  }

  function transform(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('transform', _value));
  }

  function mask(Element memory _element, string memory _value) internal pure returns (Element memory elem) {
    elem = _append(_element, Attribute('mask', string(abi.encodePacked('url(#', _value, ')'))));
  }

  function stroke(
    Element memory _element,
    string memory _color,
    uint _width
  ) internal pure returns (Element memory elem) {
    elem = _append2(_element, Attribute('stroke', _color), Attribute('stroke-width', _width.toString()));
  }

  function svg(Element memory _element) internal pure returns (bytes memory output) {
    if (_element.head.length > 0) {
      output = _element.head;
      for (uint i = 0; i < _element.attrs.length; i++) {
        Attribute memory attr = _element.attrs[i];
        output = abi.encodePacked(output, '" ', attr.key, '="', attr.value);
      }
    } else {
      require(_element.attrs.length == 0, 'Attributes on list');
    }
    output = abi.encodePacked(output, _element.tail);
  }

  function document(
    string memory _viewBox,
    bytes memory _defs,
    bytes memory _body
  ) internal pure returns (string memory) {
    bytes memory output = abi.encodePacked(
      '<?xml version="1.0" encoding="UTF-8"?>'
      '<svg viewBox="',
      _viewBox,
      '"'
      ' xmlns="http://www.w3.org/2000/svg">\n'
    );
    if (_defs.length > 0) {
      output = abi.encodePacked(output, '<defs>\n', _defs, '</defs>\n');
    }
    output = abi.encodePacked(output, _body, '</svg>\n');
    return string(output);
  }
}

// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import './Vector.sol';

library Path {
  function roundedCorner(Vector.Struct memory _vector) internal pure returns (uint) {
    return uint(_vector.x / 0x8000) + (uint(_vector.y / 0x8000) << 32) + (566 << 64);
  }

  function sharpCorner(Vector.Struct memory _vector) internal pure returns (uint) {
    return uint(_vector.x / 0x8000) + (uint(_vector.y / 0x8000) << 32) + (0x1 << 80);
  }

  function closedPath(uint[] memory points) internal pure returns (bytes memory newPath) {
    uint length = points.length;
    assembly {
      function toString(_wbuf, _value) -> wbuf {
        let len := 2
        let cmd := 0
        if gt(_value, 9) {
          if gt(_value, 99) {
            if gt(_value, 999) {
              cmd := or(shl(8, cmd), add(48, div(_value, 1000)))
              len := add(1, len)
              _value := mod(_value, 1000)
            }
            cmd := or(shl(8, cmd), add(48, div(_value, 100)))
            len := add(1, len)
            _value := mod(_value, 100)
          }
          cmd := or(shl(8, cmd), add(48, div(_value, 10)))
          len := add(1, len)
          _value := mod(_value, 10)
        }
        cmd := or(or(shl(16, cmd), shl(8, add(48, _value))), 32)

        mstore(_wbuf, shl(sub(256, mul(len, 8)), cmd))
        wbuf := add(_wbuf, len)
      }

      // dynamic allocation
      newPath := mload(0x40)
      let wbuf := add(newPath, 0x20)
      let rbuf := add(points, 0x20)

      let wordP := mload(add(rbuf, mul(sub(length, 1), 0x20)))
      let word := mload(rbuf)
      for {
        let i := 0
      } lt(i, length) {
        i := add(i, 1)
      } {
        let x := and(word, 0xffffffff)
        let y := and(shr(32, word), 0xffffffff)
        let r := and(shr(64, word), 0xffff)
        let sx := div(add(x, and(wordP, 0xffffffff)), 2)
        let sy := div(add(y, and(shr(32, wordP), 0xffffffff)), 2)
        if eq(i, 0) {
          mstore(wbuf, shl(248, 0x4D)) // M
          wbuf := add(wbuf, 1)
          wbuf := toString(wbuf, sx)
          wbuf := toString(wbuf, sy)
        }

        let wordN := mload(add(rbuf, mul(mod(add(i, 1), length), 0x20)))
        {
          let ex := div(add(x, and(wordN, 0xffffffff)), 2)
          let ey := div(add(y, and(shr(32, wordN), 0xffffffff)), 2)

          switch and(shr(80, word), 0x01)
          case 0 {
            mstore(wbuf, shl(248, 0x43)) // C
            wbuf := add(wbuf, 1)
            x := mul(x, r)
            y := mul(y, r)
            r := sub(1024, r)
            wbuf := toString(wbuf, div(add(x, mul(sx, r)), 1024))
            wbuf := toString(wbuf, div(add(y, mul(sy, r)), 1024))
            wbuf := toString(wbuf, div(add(x, mul(ex, r)), 1024))
            wbuf := toString(wbuf, div(add(y, mul(ey, r)), 1024))
          }
          default {
            mstore(wbuf, shl(248, 0x4C)) // L
            wbuf := add(wbuf, 1)
            wbuf := toString(wbuf, x)
            wbuf := toString(wbuf, y)
          }
          wbuf := toString(wbuf, ex)
          wbuf := toString(wbuf, ey)
        }
        wordP := word
        word := wordN
      }

      mstore(newPath, sub(sub(wbuf, newPath), 0x20))
      mstore(0x40, wbuf)
    }
  }

  function decode(bytes memory body) internal pure returns (bytes memory) {
    bytes memory ret;
    assembly {
      let bodyMemory := add(body, 0x20)
      let length := div(mul(mload(body), 2), 3)
      ret := mload(0x40)
      let retMemory := add(ret, 0x20)
      let data
      for {
        let i := 0
      } lt(i, length) {
        i := add(i, 1)
      } {
        if eq(mod(i, 16), 0) {
          data := mload(bodyMemory) // reading 8 extra bytes
          bodyMemory := add(bodyMemory, 24)
        }
        let low
        let high
        switch mod(i, 2)
        case 0 {
          low := shr(248, data)
          high := and(shr(240, data), 0x0f)
        }
        default {
          low := and(shr(232, data), 0xff)
          high := and(shr(244, data), 0x0f)
          data := shl(24, data)
        }

        switch high
        case 0 {
          if or(and(gt(low, 64), lt(low, 91)), and(gt(low, 96), lt(low, 123))) {
            mstore(retMemory, shl(248, low))
            retMemory := add(retMemory, 1)
          }
        }
        default {
          let cmd := 0
          let lenCmd := 2 // last digit and space
          // SVG value: undo (value + 1024) + 0x100
          let value := sub(add(shl(8, high), low), 0x0100)
          switch lt(value, 1024)
          case 0 {
            value := sub(value, 1024)
          }
          default {
            cmd := 45 // "-"
            lenCmd := 3
            value := sub(1024, value)
          }
          if gt(value, 9) {
            if gt(value, 99) {
              if gt(value, 999) {
                cmd := or(shl(8, cmd), 49) // always "1"
                lenCmd := add(1, lenCmd)
                value := mod(value, 1000)
              }
              cmd := or(shl(8, cmd), add(48, div(value, 100)))
              lenCmd := add(1, lenCmd)
              value := mod(value, 100)
            }
            cmd := or(shl(8, cmd), add(48, div(value, 10)))
            lenCmd := add(1, lenCmd)
            value := mod(value, 10)
          }
          // last digit and space
          cmd := or(or(shl(16, cmd), shl(8, add(48, value))), 32)

          mstore(retMemory, shl(sub(256, mul(lenCmd, 8)), cmd))
          retMemory := add(retMemory, lenCmd)
        }
      }
      mstore(ret, sub(sub(retMemory, ret), 0x20))
      mstore(0x40, retMemory)
    }
    return ret;
  }
}

// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

interface IFontProvider {
  function height() external view returns (uint);

  function baseline() external view returns (uint);

  function widthOf(string memory _char) external view returns (uint);

  function pathOf(string memory _char) external view returns (bytes memory);

  /**
   * This function processes the royalty payment from the decentralized autonomous marketplace.
   */
  function processPayout() external payable;

  event Payout(string providerKey, address payable to, uint256 amount);
}

// SPDX-License-Identifier: MIT

/**
 * This is a part of an effort to create a decentralized autonomous marketplace for digital assets,
 * which allows artists and developers to sell their arts and generative arts.
 *
 * Please see "https://fullyonchain.xyz/" for details. 
 *
 * Created by Satoshi Nakajima (@snakajima)
 */
pragma solidity ^0.8.6;

/**
 * IAssetProvider is the interface each asset provider implements.
 * We assume there are three types of asset providers.
 * 1. Static asset provider, which has a collection of assets (either in the storage or the code) and returns them.
 * 2. Generative provider, which dynamically (but deterministically from the seed) generates assets.
 * 3. Data visualizer, which generates assets based on various data on the blockchain.
 *
 * Note: Asset providers MUST implements IERC165 (supportsInterface method) as well. 
 */
interface IAssetProvider {
  struct ProviderInfo {
    string key;  // short and unique identifier of this provider (e.g., "asset")
    string name; // human readable display name (e.g., "Asset Store")
    IAssetProvider provider;
  }
  function getProviderInfo() external view returns(ProviderInfo memory);

  /**
   * This function returns SVGPart and the tag. The SVGPart consists of one or more SVG elements.
   * The tag specifies the identifier of the SVG element to be displayed (using <use> tag).
   * The tag is the combination of the provider key and assetId (e.e., "asset123")
   */
  function generateSVGPart(uint256 _assetId) external view returns(string memory svgPart, string memory tag);

  /**
   * This is an optional function, which returns various traits of the image for ERC721 token.
   * Format: {"trait_type":"TRAIL_TYPE","value":"VALUE"},{...}
   */
  function generateTraits(uint256 _assetId) external view returns (string memory);
  
  /**
   * This function returns the number of assets available from this provider. 
   * If the total supply is 100, assetIds of available assets are 0,1,...99.
   * The generative providers may returns 0, which indicates the provider dynamically but
   * deterministically generates assets using the given assetId as the random seed.
   */
  function totalSupply() external view returns(uint256);

  /**
   * Returns the onwer. The registration update is possible only if both contracts have the same owner. 
   */
  function getOwner() external view returns (address);

  /**
   * This function processes the royalty payment from the decentralized autonomous marketplace. 
   */
  function processPayout(uint256 _assetId) external payable;

  event Payout(string providerKey, uint256 assetId, address payable to, uint256 amount);
}

interface IAssetProviderEx is IAssetProvider {
  function generateSVGDocument(uint256 _assetId) external view returns(string memory document);
}

// SPDX-License-Identifier: MIT

/*
 * Pseudo Random genearation library.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

library Randomizer {
  struct Seed {
    uint256 seed;
    uint256 value;
  }

  /**
   * Returns a seudo random number between 0 and _limit-1.
   * It also returns an updated seed.
   */
  function random(Seed memory _seed, uint256 _limit) internal pure returns (Seed memory seed, uint256 value) {
    seed = _seed;
    if (seed.value < _limit * 256) {
      seed.seed = uint256(keccak256(abi.encodePacked(seed.seed)));
      seed.value = seed.seed;
    }
    value = seed.value % _limit;
    seed.value /= _limit;
  }

  /**
   * Returns a randomized value based on the original value and ration (in percentage).
   * It also returns an updated seed. 
   */
  function randomize(Seed memory _seed, uint256 _value, uint256 _ratio) internal pure returns (Seed memory seed, uint256 value) {
    uint256 limit = _value * _ratio / 100;
    uint256 delta;
    (seed, delta) = random(_seed, limit * 2);
    value = _value - limit + delta;
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import '@openzeppelin/contracts/utils/Strings.sol';
import 'bytes-array.sol/BytesArray.sol';

library TX {
  using Strings for uint;
  using BytesArray for bytes[];

  function toString(int _value) internal pure returns (string memory) {
    if (_value > 0) {
      return uint(_value).toString();
    }
    return string(abi.encodePacked('-', uint(-_value).toString()));
  }

  function translate(int x, int y) internal pure returns (string memory) {
    return string(abi.encodePacked('translate(', toString(x), ' ', toString(y), ')'));
  }

  function rotate(string memory _base, string memory _value) internal pure returns (string memory) {
    return string(abi.encodePacked(_base, ' rotate(', _value, ')'));
  }

  function scale(string memory _base, string memory _scale) internal pure returns (string memory) {
    return string(abi.encodePacked(_base, ' scale(', _scale, ')'));
  }

  function scale1000(uint _value) internal pure returns (string memory) {
    return string(abi.encodePacked('scale(', fixed1000(_value), ')'));
  }

  function scale1000(string memory _base, uint _value) internal pure returns (string memory) {
    return string(abi.encodePacked(_base, ' scale(', fixed1000(_value), ')'));
  }

  function fixed1000(uint _value) internal pure returns (string memory) {
    bytes[] memory array = new bytes[](3);
    if (_value > 1000) {
      array[0] = bytes((_value / 1000).toString());
    } else {
      array[0] = '0';
    }
    if (_value < 10) {
      array[1] = '.00';
    } else if (_value < 100) {
      array[1] = '.0';
    } else {
      array[1] = '.';
    }
    array[2] = bytes(_value.toString());
    return string(array.packed());
  }
}

// SPDX-License-Identifier: MIT

/*
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

library BytesArray {
  /**
   * Equivalent to abi.encodedPacked(parts[0], parts[1], ..., parts[N-1]), where
   * N is the length of bytes.
   *
   * The complexty of this algorithm is O(M), where M is the number of total bytes.
   * Calling abi.encodePacked() in a loop reallocates memory N times, therefore,
   * the complexity will become O(M * N). 
   */
  function packed(bytes[] memory parts) internal pure returns (bytes memory ret) {
    uint count = parts.length;
    assembly {
      ret := mload(0x40)
      let retMemory := add(ret, 0x20)
      let bufParts := add(parts, 0x20)
      for {let i := 0} lt(i, count) {i := add(i, 1)} {
        let src := mload(bufParts) // read the address
        let dest := retMemory
        let length := mload(src)
        // copy 0x20 bytes each (and let it overrun)
        for {let j := 0} lt(j, length) {j := add(j, 0x20)} {
          src := add(src, 0x20) // dual purpose
          mstore(dest, mload(src))
          dest := add(dest, 0x20)
        }
        retMemory := add(retMemory, length)
        bufParts := add(bufParts, 0x20)
      }
      mstore(ret, sub(sub(retMemory, ret), 0x20))
      mstore(0x40, retMemory)
    }
  }

}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import 'trigonometry.sol/Trigonometry.sol';

library Vector {
  using Trigonometry for uint;
  int constant PI = 0x2000;
  int constant PI2 = 0x4000;
  int constant ONE = 0x8000;

  struct Struct {
    int x; // fixed point * ONE
    int y; // fixed point * ONE
  }

  function vector(int _x, int _y) internal pure returns (Struct memory newVector) {
    newVector.x = _x * ONE;
    newVector.y = _y * ONE;
  }

  function vectorWithAngle(int _angle, int _radius) internal pure returns (Struct memory newVector) {
    uint angle = uint(_angle + (PI2 << 64));
    newVector.x = _radius * angle.cos();
    newVector.y = _radius * angle.sin();
  }

  function div(Struct memory _vector, int _value) internal pure returns (Struct memory newVector) {
    newVector.x = _vector.x / _value;
    newVector.y = _vector.y / _value;
  }

  function mul(Struct memory _vector, int _value) internal pure returns (Struct memory newVector) {
    newVector.x = _vector.x * _value;
    newVector.y = _vector.y * _value;
  }

  function add(Struct memory _vector, Struct memory _vector2) internal pure returns (Struct memory newVector) {
    newVector.x = _vector.x + _vector2.x;
    newVector.y = _vector.y + _vector2.y;
  }

  function rotate(Struct memory _vector, int _angle) internal pure returns (Struct memory newVector) {
    uint angle = uint(_angle + (PI2 << 64));
    int cos = angle.cos();
    int sin = angle.sin();
    newVector.x = (cos * _vector.x - sin * _vector.y) / ONE;
    newVector.y = (sin * _vector.x + cos * _vector.y) / ONE;
  }
}

/**
 * Basic trigonometry functions
 *
 * Solidity library offering the functionality of basic trigonometry functions
 * with both input and output being integer approximated.
 *
 * This code was originally written by Lefteris Karapetsas
 * https://github.com/Sikorkaio/sikorka/blob/master/contracts/trigonometry.sol
 *
 * I made several changes to make it easy for me to manage and use. 
 *
 * @author Lefteris Karapetsas 
 * @author Satoshi Nakajima (snakajima)
 * @license BSD3
 */

// SPDX-License-Identifier: BSD3
pragma solidity ^0.8.6;

library Trigonometry {

    // constant sine lookup table generated by gen_tables.py
    // We have no other choice but this since constant arrays don't yet exist
    uint8 constant entry_bytes = 2;
    bytes constant sin_table = "\x00\x00\x0c\x8c\x18\xf9\x25\x28\x30\xfb\x3c\x56\x47\x1c\x51\x33\x5a\x82\x62\xf1\x6a\x6d\x70\xe2\x76\x41\x7a\x7c\x7d\x89\x7f\x61\x7f\xff";

    function sin_table_lookup(uint index) pure internal returns (uint16) {
        bytes memory table = sin_table;
        uint offset = (index + 1) * entry_bytes;
        uint16 trigint_value;
        assembly {
            trigint_value := mload(add(table, offset))
        }

        return trigint_value;
    }

    /**
     * Return the sine of an integer approximated angle as a signed 16-bit
     * integer.
     *
     * @param _angle A 14-bit angle. This divides the circle into 16384 (0x4000)
     *               angle units, instead of the standard 360 degrees.
     * @return The sine result as a number in the range -32767 to 32767.
     */
    function sin(uint _angle) internal pure returns (int) {
        uint angle = _angle % 0x4000;
        if (angle < 0x2000) {
            return sinQuarter(angle < 0x1000 ? angle : 0x2000 - angle);
        }
        return -sinQuarter(angle < 0x3000 ? angle - 0x2000 : 0x4000 - angle);
    }

    function sinQuarter(uint _angle) internal pure returns (int) {
        if (_angle == 0x1000) {
            return 0x7fff;
        }
        uint index = _angle / 0x100; // high 4-bit
        uint interp = _angle & 0xFF; // low 8-bit
        uint x1 = sin_table_lookup(index);
        uint x2 = sin_table_lookup(index + 1);
        return int(x1 + ((x2 - x1) * interp) / 0x100);
    }

    /**
     * Return the cos of an integer approximated angle.
     * It functions just like the sin() method but uses the trigonometric
     * identity sin(x + pi/2) = cos(x) to quickly calculate the cos.
     */
    function cos(uint _angle) internal pure returns (int) {
        return sin(_angle + 0x1000);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):