You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

590 lines
54 KiB
JavaScript

4 months ago
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
var bezierCurve = require('../lib/index')
window.bezierCurve = bezierCurve
},{"../lib/index":4}],2:[function(require,module,exports){
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.bezierCurveToPolyline = bezierCurveToPolyline;
exports.getBezierCurveLength = getBezierCurveLength;
exports["default"] = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var sqrt = Math.sqrt,
pow = Math.pow,
ceil = Math.ceil,
abs = Math.abs; // Initialize the number of points per curve
var defaultSegmentPointsNum = 50;
/**
* @example data structure of bezierCurve
* bezierCurve = [
* // Starting point of the curve
* [10, 10],
* // BezierCurve segment data (controlPoint1, controlPoint2, endPoint)
* [
* [20, 20], [40, 20], [50, 10]
* ],
* ...
* ]
*/
/**
* @description Abstract the curve as a polyline consisting of N points
* @param {Array} bezierCurve bezierCurve data
* @param {Number} precision calculation accuracy. Recommended for 1-20. Default = 5
* @return {Object} Calculation results and related data
* @return {Array} Option.segmentPoints Point data that constitutes a polyline after calculation
* @return {Number} Option.cycles Number of iterations
* @return {Number} Option.rounds The number of recursions for the last iteration
*/
function abstractBezierCurveToPolyline(bezierCurve) {
var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
var segmentsNum = bezierCurve.length - 1;
var startPoint = bezierCurve[0];
var endPoint = bezierCurve[segmentsNum][2];
var segments = bezierCurve.slice(1);
var getSegmentTPointFuns = segments.map(function (seg, i) {
var beginPoint = i === 0 ? startPoint : segments[i - 1][2];
return createGetBezierCurveTPointFun.apply(void 0, [beginPoint].concat((0, _toConsumableArray2["default"])(seg)));
}); // Initialize the curve to a polyline
var segmentPointsNum = new Array(segmentsNum).fill(defaultSegmentPointsNum);
var segmentPoints = getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum); // Calculate uniformly distributed points by iteratively
var result = calcUniformPointsByIteration(segmentPoints, getSegmentTPointFuns, segments, precision);
result.segmentPoints.push(endPoint);
return result;
}
/**
* @description Generate a method for obtaining corresponding point by t according to curve data
* @param {Array} beginPoint BezierCurve begin point. [x, y]
* @param {Array} controlPoint1 BezierCurve controlPoint1. [x, y]
* @param {Array} controlPoint2 BezierCurve controlPoint2. [x, y]
* @param {Array} endPoint BezierCurve end point. [x, y]
* @return {Function} Expected function
*/
function createGetBezierCurveTPointFun(beginPoint, controlPoint1, controlPoint2, endPoint) {
return function (t) {
var tSubed1 = 1 - t;
var tSubed1Pow3 = pow(tSubed1, 3);
var tSubed1Pow2 = pow(tSubed1, 2);
var tPow3 = pow(t, 3);
var tPow2 = pow(t, 2);
return [beginPoint[0] * tSubed1Pow3 + 3 * controlPoint1[0] * t * tSubed1Pow2 + 3 * controlPoint2[0] * tPow2 * tSubed1 + endPoint[0] * tPow3, beginPoint[1] * tSubed1Pow3 + 3 * controlPoint1[1] * t * tSubed1Pow2 + 3 * controlPoint2[1] * tPow2 * tSubed1 + endPoint[1] * tPow3];
};
}
/**
* @description Get the distance between two points
* @param {Array} point1 BezierCurve begin point. [x, y]
* @param {Array} point2 BezierCurve controlPoint1. [x, y]
* @return {Number} Expected distance
*/
function getTwoPointDistance(_ref, _ref2) {
var _ref3 = (0, _slicedToArray2["default"])(_ref, 2),
ax = _ref3[0],
ay = _ref3[1];
var _ref4 = (0, _slicedToArray2["default"])(_ref2, 2),
bx = _ref4[0],
by = _ref4[1];
return sqrt(pow(ax - bx, 2) + pow(ay - by, 2));
}
/**
* @description Get the sum of the array of numbers
* @param {Array} nums An array of numbers
* @return {Number} Expected sum
*/
function getNumsSum(nums) {
return nums.reduce(function (sum, num) {
return sum + num;
}, 0);
}
/**
* @description Get the distance of multiple sets of points
* @param {Array} segmentPoints Multiple sets of point data
* @return {Array} Distance of multiple sets of point data
*/
function getSegmentPointsDistance(segmentPoints) {
return segmentPoints.map(function (points, i) {
return new Array(points.length - 1).fill(0).map(function (temp, j) {
return getTwoPointDistance(points[j], points[j + 1]);
});
});
}
/**
* @description Get the distance of multiple sets of points
* @param {Array} segmentPoints Multiple sets of point data
* @return {Array} Distance of multiple sets of point data
*/
function getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum) {
return getSegmentTPointFuns.map(function (getSegmentTPointFun, i) {
var tGap = 1 / segmentPointsNum[i];
return new Array(segmentPointsNum[i]).fill('').map(function (foo, j) {
return getSegmentTPointFun(j * tGap);
});
});
}
/**
* @description Get the sum of deviations between line segment and the average length
* @param {Array} segmentPointsDistance Segment length of polyline
* @param {Number} avgLength Average length of the line segment
* @return {Number} Deviations
*/
function getAllDeviations(segmentPointsDistance, avgLength) {
return segmentPointsDistance.map(function (seg) {
return seg.map(function (s) {
return abs(s - avgLength);
});
}).map(function (seg) {
return getNumsSum(seg);
}).reduce(function (total, v) {
return total + v;
}, 0);
}
/**
* @description Calculate uniformly distributed points by iteratively
* @param {Array} segmentPoints Multiple setd of points that make up a polyline
* @param {Array} getSegmentTPointFuns Functions of get a point on the curve with t
* @param {Array} segments BezierCurve data
* @param {Number} precision Calculation accuracy
* @return {Object} Calculation results and related data
* @return {Array} Option.segmentPoints Point data that constitutes a polyline after calculation
* @return {Number} Option.cycles Number of iterations
* @return {Number} Option.rounds The number of recursions for the last iteration
*/
function calcUniformPointsByIteration(segmentPoints, getSegmentTPointFuns, segments, precision) {
// The number of loops for the current iteration
var rounds = 4; // Number of iterations
var cycles = 1;
var _loop = function _loop() {
// Recalculate the number of points per curve based on the last iteration data
var totalPointsNum = segmentPoints.reduce(function (total, seg) {
return total + seg.length;
}, 0); // Add last points of segment to calc exact segment length
segmentPoints.forEach(function (seg, i) {
return seg.push(segments[i][2]);
});
var segmentPointsDistance = getSegmentPointsDistance(segmentPoints);
var lineSegmentNum = segmentPointsDistance.reduce(function (total, seg) {
return total + seg.length;
}, 0);
var segmentlength = segmentPointsDistance.map(function (seg) {
return getNumsSum(seg);
});
var totalLength = getNumsSum(segmentlength);
var avgLength = totalLength / lineSegmentNum; // Check if precision is reached
var allDeviations = getAllDeviations(segmentPointsDistance, avgLength);
if (allDeviations <= precision) return "break";
totalPointsNum = ceil(avgLength / precision * totalPointsNum * 1.1);
var segmentPointsNum = segmentlength.map(function (length) {
return ceil(length / totalLength * totalPointsNum);
}); // Calculate the points after redistribution
segmentPoints = getSegmentPointsByNum(getSegmentTPointFuns, segmentPointsNum);
totalPointsNum = segmentPoints.reduce(function (total, seg) {
return total + seg.length;
}, 0);
var segmentPointsForLength = JSON.parse(JSON.stringify(segmentPoints));
segmentPointsForLength.forEach(function (seg, i) {
return seg.push(segments[i][2]);
});
segmentPointsDistance = getSegmentPointsDistance(segmentPointsForLength);
lineSegmentNum = segmentPointsDistance.reduce(function (total, seg) {
return total + seg.length;
}, 0);
segmentlength = segmentPointsDistance.map(function (seg) {
return getNumsSum(seg);
});
totalLength = getNumsSum(segmentlength);
avgLength = totalLength / lineSegmentNum;
var stepSize = 1 / totalPointsNum / 10; // Recursively for each segment of the polyline
getSegmentTPointFuns.forEach(function (getSegmentTPointFun, i) {
var currentSegmentPointsNum = segmentPointsNum[i];
var t = new Array(currentSegmentPointsNum).fill('').map(function (foo, j) {
return j / segmentPointsNum[i];
}); // Repeated recursive offset
for (var r = 0; r < rounds; r++) {
var distance = getSegmentPointsDistance([segmentPoints[i]])[0];
var deviations = distance.map(function (d) {
return d - avgLength;
});
var offset = 0;
for (var j = 0; j < currentSegmentPointsNum; j++) {
if (j === 0) return;
offset += deviations[j - 1];
t[j] -= stepSize * offset;
if (t[j] > 1) t[j] = 1;
if (t[j] < 0) t[j] = 0;
segmentPoints[i][j] = getSegmentTPointFun(t[j]);
}
}
});
rounds *= 4;
cycles++;
};
do {
var _ret = _loop();
if (_ret === "break") break;
} while (rounds <= 1025);
segmentPoints = segmentPoints.reduce(function (all, seg) {
return all.concat(seg);
}, []);
return {
segmentPoints: segmentPoints,
cycles: cycles,
rounds: rounds
};
}
/**
* @description Get the polyline corresponding to the Bezier curve
* @param {Array} bezierCurve BezierCurve data
* @param {Number} precision Calculation accuracy. Recommended for 1-20. Default = 5
* @return {Array|Boolean} Point data that constitutes a polyline after calculation (Invalid input will return false)
*/
function bezierCurveToPolyline(bezierCurve) {
var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
if (!bezierCurve) {
console.error('bezierCurveToPolyline: Missing parameters!');
return false;
}
if (!(bezierCurve instanceof Array)) {
console.error('bezierCurveToPolyline: Parameter bezierCurve must be an array!');
return false;
}
if (typeof precision !== 'number') {
console.error('bezierCurveToPolyline: Parameter precision must be a number!');
return false;
}
var _abstractBezierCurveT = abstractBezierCurveToPolyline(bezierCurve, precision),
segmentPoints = _abstractBezierCurveT.segmentPoints;
return segmentPoints;
}
/**
* @description Get the bezier curve length
* @param {Array} bezierCurve bezierCurve data
* @param {Number} precision calculation accuracy. Recommended for 5-10. Default = 5
* @return {Number|Boolean} BezierCurve length (Invalid input will return false)
*/
function getBezierCurveLength(bezierCurve) {
var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
if (!bezierCurve) {
console.error('getBezierCurveLength: Missing parameters!');
return false;
}
if (!(bezierCurve instanceof Array)) {
console.error('getBezierCurveLength: Parameter bezierCurve must be an array!');
return false;
}
if (typeof precision !== 'number') {
console.error('getBezierCurveLength: Parameter precision must be a number!');
return false;
}
var _abstractBezierCurveT2 = abstractBezierCurveToPolyline(bezierCurve, precision),
segmentPoints = _abstractBezierCurveT2.segmentPoints; // Calculate the total length of the points that make up the polyline
var pointsDistance = getSegmentPointsDistance([segmentPoints])[0];
var length = getNumsSum(pointsDistance);
return length;
}
var _default = bezierCurveToPolyline;
exports["default"] = _default;
},{"@babel/runtime/helpers/interopRequireDefault":7,"@babel/runtime/helpers/slicedToArray":12,"@babel/runtime/helpers/toConsumableArray":13}],3:[function(require,module,exports){
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
/**
* @description Abstract the polyline formed by N points into a set of bezier curve
* @param {Array} polyline A set of points that make up a polyline
* @param {Boolean} close Closed curve
* @param {Number} offsetA Smoothness
* @param {Number} offsetB Smoothness
* @return {Array|Boolean} A set of bezier curve (Invalid input will return false)
*/
function polylineToBezierCurve(polyline) {
var close = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var offsetA = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.25;
var offsetB = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.25;
if (!(polyline instanceof Array)) {
console.error('polylineToBezierCurve: Parameter polyline must be an array!');
return false;
}
if (polyline.length <= 2) {
console.error('polylineToBezierCurve: Converting to a curve requires at least 3 points!');
return false;
}
var startPoint = polyline[0];
var bezierCurveLineNum = polyline.length - 1;
var bezierCurvePoints = new Array(bezierCurveLineNum).fill(0).map(function (foo, i) {
return [].concat((0, _toConsumableArray2["default"])(getBezierCurveLineControlPoints(polyline, i, close, offsetA, offsetB)), [polyline[i + 1]]);
});
if (close) closeBezierCurve(bezierCurvePoints, startPoint);
bezierCurvePoints.unshift(polyline[0]);
return bezierCurvePoints;
}
/**
* @description Get the control points of the Bezier curve
* @param {Array} polyline A set of points that make up a polyline
* @param {Number} index The index of which get controls points's point in polyline
* @param {Boolean} close Closed curve
* @param {Number} offsetA Smoothness
* @param {Number} offsetB Smoothness
* @return {Array} Control points
*/
function getBezierCurveLineControlPoints(polyline, index) {
var close = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var offsetA = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.25;
var offsetB = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0.25;
var pointNum = polyline.length;
if (pointNum < 3 || index >= pointNum) return;
var beforePointIndex = index - 1;
if (beforePointIndex < 0) beforePointIndex = close ? pointNum + beforePointIndex : 0;
var afterPointIndex = index + 1;
if (afterPointIndex >= pointNum) afterPointIndex = close ? afterPointIndex - pointNum : pointNum - 1;
var afterNextPointIndex = index + 2;
if (afterNextPointIndex >= pointNum) afterNextPointIndex = close ? afterNextPointIndex - pointNum : pointNum - 1;
var pointBefore = polyline[beforePointIndex];
var pointMiddle = polyline[index];
var pointAfter = polyline[afterPointIndex];
var pointAfterNext = polyline[afterNextPointIndex];
return [[pointMiddle[0] + offsetA * (pointAfter[0] - pointBefore[0]), pointMiddle[1] + offsetA * (pointAfter[1] - pointBefore[1])], [pointAfter[0] - offsetB * (pointAfterNext[0] - pointMiddle[0]), pointAfter[1] - offsetB * (pointAfterNext[1] - pointMiddle[1])]];
}
/**
* @description Get the last curve of the closure
* @param {Array} bezierCurve A set of sub-curve
* @param {Array} startPoint Start point
* @return {Array} The last curve for closure
*/
function closeBezierCurve(bezierCurve, startPoint) {
var firstSubCurve = bezierCurve[0];
var lastSubCurve = bezierCurve.slice(-1)[0];
bezierCurve.push([getSymmetryPoint(lastSubCurve[1], lastSubCurve[2]), getSymmetryPoint(firstSubCurve[0], startPoint), startPoint]);
return bezierCurve;
}
/**
* @description Get the symmetry point
* @param {Array} point Symmetric point
* @param {Array} centerPoint Symmetric center
* @return {Array} Symmetric point
*/
function getSymmetryPoint(point, centerPoint) {
var _point = (0, _slicedToArray2["default"])(point, 2),
px = _point[0],
py = _point[1];
var _centerPoint = (0, _slicedToArray2["default"])(centerPoint, 2),
cx = _centerPoint[0],
cy = _centerPoint[1];
var minusX = cx - px;
var minusY = cy - py;
return [cx + minusX, cy + minusY];
}
var _default = polylineToBezierCurve;
exports["default"] = _default;
},{"@babel/runtime/helpers/interopRequireDefault":7,"@babel/runtime/helpers/slicedToArray":12,"@babel/runtime/helpers/toConsumableArray":13}],4:[function(require,module,exports){
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "bezierCurveToPolyline", {
enumerable: true,
get: function get() {
return _bezierCurveToPolyline.bezierCurveToPolyline;
}
});
Object.defineProperty(exports, "getBezierCurveLength", {
enumerable: true,
get: function get() {
return _bezierCurveToPolyline.getBezierCurveLength;
}
});
Object.defineProperty(exports, "polylineToBezierCurve", {
enumerable: true,
get: function get() {
return _polylineToBezierCurve["default"];
}
});
exports["default"] = void 0;
var _bezierCurveToPolyline = require("./core/bezierCurveToPolyline");
var _polylineToBezierCurve = _interopRequireDefault(require("./core/polylineToBezierCurve"));
var _default = {
bezierCurveToPolyline: _bezierCurveToPolyline.bezierCurveToPolyline,
getBezierCurveLength: _bezierCurveToPolyline.getBezierCurveLength,
polylineToBezierCurve: _polylineToBezierCurve["default"]
};
exports["default"] = _default;
},{"./core/bezierCurveToPolyline":2,"./core/polylineToBezierCurve":3,"@babel/runtime/helpers/interopRequireDefault":7}],5:[function(require,module,exports){
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
module.exports = _arrayWithHoles;
},{}],6:[function(require,module,exports){
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
}
}
module.exports = _arrayWithoutHoles;
},{}],7:[function(require,module,exports){
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
"default": obj
};
}
module.exports = _interopRequireDefault;
},{}],8:[function(require,module,exports){
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
module.exports = _iterableToArray;
},{}],9:[function(require,module,exports){
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
module.exports = _iterableToArrayLimit;
},{}],10:[function(require,module,exports){
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
module.exports = _nonIterableRest;
},{}],11:[function(require,module,exports){
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
module.exports = _nonIterableSpread;
},{}],12:[function(require,module,exports){
var arrayWithHoles = require("./arrayWithHoles");
var iterableToArrayLimit = require("./iterableToArrayLimit");
var nonIterableRest = require("./nonIterableRest");
function _slicedToArray(arr, i) {
return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || nonIterableRest();
}
module.exports = _slicedToArray;
},{"./arrayWithHoles":5,"./iterableToArrayLimit":9,"./nonIterableRest":10}],13:[function(require,module,exports){
var arrayWithoutHoles = require("./arrayWithoutHoles");
var iterableToArray = require("./iterableToArray");
var nonIterableSpread = require("./nonIterableSpread");
function _toConsumableArray(arr) {
return arrayWithoutHoles(arr) || iterableToArray(arr) || nonIterableSpread();
}
module.exports = _toConsumableArray;
},{"./arrayWithoutHoles":6,"./iterableToArray":8,"./nonIterableSpread":11}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJidWlsZC9lbnRyeS5qcyIsImxpYi9jb3JlL2JlemllckN1cnZlVG9Qb2x5bGluZS5qcyIsImxpYi9jb3JlL3BvbHlsaW5lVG9CZXppZXJDdXJ2ZS5qcyIsImxpYi9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5V2l0aEhvbGVzLmpzIiwibm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvYXJyYXlXaXRob3V0SG9sZXMuanMiLCJub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy9pbnRlcm9wUmVxdWlyZURlZmF1bHQuanMiLCJub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy9pdGVyYWJsZVRvQXJyYXkuanMiLCJub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy9pdGVyYWJsZVRvQXJyYXlMaW1pdC5qcyIsIm5vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL25vbkl0ZXJhYmxlUmVzdC5qcyIsIm5vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL25vbkl0ZXJhYmxlU3ByZWFkLmpzIiwibm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvc2xpY2VkVG9BcnJheS5qcyIsIm5vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3RvQ29uc3VtYWJsZUFycmF5LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBOztBQ0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOVVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9HQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E