@tisoap/react-flow-smart-edge
Advanced tools
Comparing version 0.4.0 to 0.4.1
@@ -9,6 +9,6 @@ 'use strict'; | ||
var React__default = _interopDefault(React); | ||
var reactFlowRenderer = require('react-flow-renderer'); | ||
var useDebounce = _interopDefault(require('react-use/lib/useDebounce')); | ||
var reactFlowRenderer = require('react-flow-renderer'); | ||
var warning = _interopDefault(require('tiny-warning')); | ||
var pathfinding = require('pathfinding'); | ||
var warning = _interopDefault(require('tiny-warning')); | ||
@@ -33,2 +33,91 @@ function _extends() { | ||
var round = function round(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.round(x / multiple) * multiple; | ||
}; | ||
var roundDown = function roundDown(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.floor(x / multiple) * multiple; | ||
}; | ||
var roundUp = function roundUp(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.ceil(x / multiple) * multiple; | ||
}; | ||
var toInteger = function toInteger(value, min) { | ||
if (min === void 0) { | ||
min = 0; | ||
} | ||
var result = Math.max(Math.round(value), min); | ||
result = Number.isInteger(result) ? result : min; | ||
result = result >= min ? result : min; | ||
return result; | ||
}; | ||
var defaultOptions = { | ||
debounceTime: 200, | ||
nodePadding: 10, | ||
gridRatio: 10, | ||
lineType: 'curve', | ||
lessCorners: false | ||
}; | ||
var SmartEdgeContext = /*#__PURE__*/React.createContext(defaultOptions); | ||
var SmartEdgeProvider = function SmartEdgeProvider(_ref) { | ||
var children = _ref.children, | ||
_ref$options = _ref.options, | ||
options = _ref$options === void 0 ? defaultOptions : _ref$options; | ||
var _options$debounceTime = options.debounceTime, | ||
debounceTime = _options$debounceTime === void 0 ? 200 : _options$debounceTime, | ||
_options$nodePadding = options.nodePadding, | ||
nodePadding = _options$nodePadding === void 0 ? 10 : _options$nodePadding, | ||
_options$gridRatio = options.gridRatio, | ||
gridRatio = _options$gridRatio === void 0 ? 10 : _options$gridRatio, | ||
_options$lineType = options.lineType, | ||
lineType = _options$lineType === void 0 ? 'curve' : _options$lineType; | ||
var _options$lessCorners = options.lessCorners, | ||
lessCorners = _options$lessCorners === void 0 ? false : _options$lessCorners; // Guarantee that all values are positive integers | ||
gridRatio = toInteger(gridRatio, 2); | ||
nodePadding = toInteger(nodePadding, 2); | ||
debounceTime = toInteger(debounceTime); // Guarantee correct line type | ||
if (lineType !== 'curve' && lineType !== 'straight') { | ||
lineType = 'curve'; | ||
} | ||
warning(debounceTime >= 30, 'A small debounce time on SmartEdge can cause performance issues on large graphs.') ; | ||
warning(gridRatio >= 10, 'A small grid ratio on SmartEdge can cause performance issues on large graphs.') ; | ||
return React__default.createElement(SmartEdgeContext.Provider, { | ||
value: { | ||
debounceTime: debounceTime, | ||
nodePadding: nodePadding, | ||
gridRatio: gridRatio, | ||
lineType: lineType, | ||
lessCorners: lessCorners | ||
} | ||
}, children); | ||
}; | ||
var useSmartEdge = function useSmartEdge() { | ||
var context = React.useContext(SmartEdgeContext); | ||
if (context === undefined) { | ||
throw new Error('useSmartEdge must be used within a SmartEdgeProvider'); | ||
} | ||
if (context.debounceTime === undefined || context.gridRatio === undefined || context.nodePadding === undefined || context.lineType === undefined) { | ||
throw new Error('Missing options on SmartEdgeProvider'); | ||
} | ||
return context; | ||
}; | ||
var getNextPointFromPosition = function getNextPointFromPosition(point, position) { | ||
@@ -173,34 +262,2 @@ switch (position) { | ||
var round = function round(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.round(x / multiple) * multiple; | ||
}; | ||
var roundDown = function roundDown(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.floor(x / multiple) * multiple; | ||
}; | ||
var roundUp = function roundUp(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.ceil(x / multiple) * multiple; | ||
}; | ||
var toInteger = function toInteger(value, min) { | ||
if (min === void 0) { | ||
min = 0; | ||
} | ||
var result = Math.max(Math.round(value), min); | ||
result = Number.isInteger(result) ? result : min; | ||
result = result >= min ? result : min; | ||
return result; | ||
}; | ||
var createGrid = function createGrid(graph, nodes, source, target, gridRatio) { | ||
@@ -360,2 +417,6 @@ if (gridRatio === void 0) { | ||
/* eslint-disable | ||
@typescript-eslint/no-unsafe-assignment, | ||
@typescript-eslint/no-unsafe-member-access, | ||
*/ | ||
var rf = node == null ? void 0 : node.__rf; | ||
@@ -368,2 +429,7 @@ var width = Math.max((rf == null ? void 0 : rf.width) || 0, 1); | ||
}; | ||
/* eslint-enable | ||
@typescript-eslint/no-unsafe-assignment, | ||
@typescript-eslint/no-unsafe-member-access, | ||
*/ | ||
var topLeft = { | ||
@@ -452,59 +518,2 @@ x: position.x - nodePadding, | ||
var defaultOptions = { | ||
debounceTime: 200, | ||
nodePadding: 10, | ||
gridRatio: 10, | ||
lineType: 'curve', | ||
lessCorners: false | ||
}; | ||
var SmartEdgeContext = /*#__PURE__*/React.createContext(defaultOptions); | ||
var SmartEdgeProvider = function SmartEdgeProvider(_ref) { | ||
var children = _ref.children, | ||
_ref$options = _ref.options, | ||
options = _ref$options === void 0 ? defaultOptions : _ref$options; | ||
var _options$debounceTime = options.debounceTime, | ||
debounceTime = _options$debounceTime === void 0 ? 200 : _options$debounceTime, | ||
_options$nodePadding = options.nodePadding, | ||
nodePadding = _options$nodePadding === void 0 ? 10 : _options$nodePadding, | ||
_options$gridRatio = options.gridRatio, | ||
gridRatio = _options$gridRatio === void 0 ? 10 : _options$gridRatio, | ||
_options$lineType = options.lineType, | ||
lineType = _options$lineType === void 0 ? 'curve' : _options$lineType, | ||
_options$lessCorners = options.lessCorners, | ||
lessCorners = _options$lessCorners === void 0 ? false : _options$lessCorners; // Guarantee that all values are positive integers | ||
gridRatio = toInteger(gridRatio, 2); | ||
nodePadding = toInteger(nodePadding, 2); | ||
debounceTime = toInteger(debounceTime); // Guarantee correct line type | ||
if (lineType !== 'curve' && lineType !== 'straight') { | ||
lineType = 'curve'; | ||
} | ||
warning(debounceTime >= 30, 'A small debounce time on SmartEdge can cause performance issues on large graphs.') ; | ||
warning(gridRatio >= 10, 'A small grid ratio on SmartEdge can cause performance issues on large graphs.') ; | ||
return React__default.createElement(SmartEdgeContext.Provider, { | ||
value: { | ||
debounceTime: debounceTime, | ||
nodePadding: nodePadding, | ||
gridRatio: gridRatio, | ||
lineType: lineType, | ||
lessCorners: lessCorners | ||
} | ||
}, children); | ||
}; | ||
var useSmartEdge = function useSmartEdge() { | ||
var context = React.useContext(SmartEdgeContext); | ||
if (context === undefined) { | ||
throw new Error('useSmartEdge must be used within a SmartEdgeProvider'); | ||
} | ||
if (context.debounceTime === undefined || context.gridRatio === undefined || context.nodePadding === undefined || context.lineType === undefined) { | ||
throw new Error('Missing options on SmartEdgeProvider'); | ||
} | ||
return context; | ||
}; | ||
var PathFindingEdge = /*#__PURE__*/React.memo(function (props) { | ||
@@ -563,6 +572,6 @@ var sourceX = props.sourceX, | ||
/* | ||
Fallback to BezierEdge if no path was found. | ||
length = 0: no path was found | ||
length = 1: starting and ending points are the same | ||
length = 2: a single straight line from point A to point B | ||
Fallback to BezierEdge if no path was found. | ||
length = 0: no path was found | ||
length = 1: starting and ending points are the same | ||
length = 2: a single straight line from point A to point B | ||
*/ | ||
@@ -616,3 +625,3 @@ | ||
style: style, | ||
className: "react-flow__edge-path", | ||
className: 'react-flow__edge-path', | ||
d: svgPathString, | ||
@@ -651,2 +660,3 @@ markerEnd: markerEnd | ||
}); | ||
RegularPathFindingEdge.displayName = 'RegularPathFindingEdge'; | ||
var SmartEdge = /*#__PURE__*/React.memo(function (props) { | ||
@@ -665,2 +675,3 @@ var context = React.useContext(SmartEdgeContext); | ||
}); | ||
SmartEdge.displayName = 'SmartEdge'; | ||
@@ -667,0 +678,0 @@ exports.SmartEdge = SmartEdge; |
@@ -1,2 +0,2 @@ | ||
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var t=require("react"),r=e(t),n=e(require("react-use/lib/useDebounce")),o=require("react-flow-renderer"),i=require("pathfinding");function a(){return(a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}require("tiny-warning");var u=function(e,t){switch(t){case"top":return{x:e.x,y:e.y-1};case"bottom":return{x:e.x,y:e.y+1};case"left":return{x:e.x-1,y:e.y};case"right":return{x:e.x+1,y:e.y}}},d=function(e,t,r){for(var n=e.getNodeAt(t.x,t.y);!n.walkable;){e.setWalkableAt(n.x,n.y,!0);var o=u(n,r);n=e.getNodeAt(o.x,o.y)}},l=function(e,t,r,n){var o=e.x/n,i=e.y/n,a=t/n,u=r/n;if(a<1)for(;1!==a;)a++,o++;else if(a>1)for(;1!==a;)a--,o--;if(u<1)for(;1!==u;)u++,i++;else if(u>1)for(;1!==u;)u--,i--;return{x:o,y:i}},s=function(e,t,r,n){var o=e.x*n,i=e.y*n,a=t,u=r;if(a<n)for(;a!==n;)a+=n,o-=n;else if(a>n)for(;a!==n;)a-=n,o+=n;if(u<n)for(;u!==n;)u+=n,i-=n;else if(u>n)for(;u!==n;)u-=n,i+=n;return{x:o,y:i}},c=function(e,t){return void 0===t&&(t=10),Math.round(e/t)*t},y=function(e,t){return void 0===t&&(t=10),Math.floor(e/t)*t},f=function(e,t){return void 0===t&&(t=10),Math.ceil(e/t)*t},x=function(e,t){void 0===t&&(t=0);var r=Math.max(Math.round(e),t);return(r=Number.isInteger(r)?r:t)>=t?r:t},g={allowDiagonal:!0,dontCrossCorners:!0,diagonalMovement:i.DiagonalMovement.Always},v={allowDiagonal:!1},h={debounceTime:200,nodePadding:10,gridRatio:10,lineType:"curve",lessCorners:!1},m=t.createContext(h),b=function(e){var t=e.children,n=e.options,o=void 0===n?h:n,i=o.debounceTime,a=void 0===i?200:i,u=o.nodePadding,d=void 0===u?10:u,l=o.gridRatio,s=void 0===l?10:l,c=o.lineType,y=void 0===c?"curve":c,f=o.lessCorners,g=void 0!==f&&f;return s=x(s,2),d=x(d,2),a=x(a),"curve"!==y&&"straight"!==y&&(y="curve"),r.createElement(m.Provider,{value:{debounceTime:a,nodePadding:d,gridRatio:s,lineType:y,lessCorners:g}},t)},p=function(){var e=t.useContext(m);if(void 0===e)throw new Error("useSmartEdge must be used within a SmartEdgeProvider");if(void 0===e.debounceTime||void 0===e.gridRatio||void 0===e.nodePadding||void 0===e.lineType)throw new Error("Missing options on SmartEdgeProvider");return e},E=t.memo((function(e){var t=e.sourceX,n=e.sourceY,a=e.sourcePosition,x=e.targetX,h=e.targetY,m=e.targetPosition,b=e.arrowHeadType,E=e.markerEndId,M=e.style,w=e.storeNodes,S=e.label,P=e.labelStyle,N=e.labelShowBg,T=e.labelBgStyle,R=e.labelBgPadding,A=e.labelBgBorderRadius,_=p(),B=_.gridRatio,j=_.lineType,C=_.lessCorners,O=function(e,t,r){void 0===t&&(t=2),void 0===r&&(r=2);var n=Number.MIN_SAFE_INTEGER,o=Number.MIN_SAFE_INTEGER,i=Number.MAX_SAFE_INTEGER,a=Number.MAX_SAFE_INTEGER,u=e.map((function(e){var u,d,l=null==e?void 0:e.__rf,s=Math.max((null==l?void 0:l.width)||0,1),c=Math.max((null==l?void 0:l.height)||0,1),x=(null==l||null==(u=l.position)?void 0:u.x)||0,g=(null==l||null==(d=l.position)?void 0:d.y)||0,v={x:x-t,y:g-t},h={x:x-t,y:g+c+t},m={x:x+s+t,y:g-t},b={x:x+s+t,y:g+c+t};return r>0&&(v.x=y(v.x,r),v.y=y(v.y,r),h.x=y(h.x,r),h.y=f(h.y,r),m.x=f(m.x,r),m.y=y(m.y,r),b.x=f(b.x,r),b.y=f(b.y,r)),v.y<a&&(a=v.y),v.x<i&&(i=v.x),b.y>o&&(o=b.y),b.x>n&&(n=b.x),{id:e.id,width:s,height:c,topLeft:v,bottomLeft:h,topRight:m,bottomRight:b}})),d=2*t;n=f(n+d,r),o=f(o+d,r),i=y(i-d,r),a=y(a-d,r);var l={x:i,y:a},s={x:i,y:o},c={x:n,y:a};return{nodes:u,graph:{topLeft:l,bottomLeft:s,topRight:c,bottomRight:{x:n,y:o},width:Math.abs(l.x-c.x),height:Math.abs(l.y-s.y),xMax:n,yMax:o,xMin:i,yMin:a}}}(w,_.nodePadding,B),I=O.graph,L={x:t,y:n,position:a},k={x:x,y:h,position:m},F=function(e,t,r,n,o){void 0===o&&(o=2);var a=e.xMin,s=e.yMin,y=e.height,x=f(e.width,o)/o+1,g=f(y,o)/o+1,v=new i.Grid(x,g);t.forEach((function(e){for(var t=l(e.topLeft,a,s,o),r=l(e.bottomRight,a,s,o),n=t.x;n<r.x;n++)for(var i=t.y;i<r.y;i++)v.setWalkableAt(n,i,!1)}));var h=l({x:c(r.x,o),y:c(r.y,o)},a,s,o),m=l({x:c(n.x,o),y:c(n.y,o)},a,s,o),b=v.getNodeAt(h.x,h.y);d(v,b,r.position);var p=v.getNodeAt(m.x,m.y);d(v,p,n.position);var E=u(b,r.position),M=u(p,n.position);return{grid:v,start:E,end:M}}(I,O.nodes,L,k,B),q=function(e,t,r,n){var o=new i.AStarFinder(n?v:g),a=[],u=[];try{a=o.findPath(t.x,t.y,r.x,r.y,e),u=i.Util.smoothenPath(e,a)}catch(e){}return{fullPath:a,smoothedPath:u}}(F.grid,F.start,F.end,C),G=q.fullPath,D=q.smoothedPath;if(D.length<=2)return r.createElement("curve"===j?o.BezierEdge:o.StraightEdge,Object.assign({},e));var X=D.map((function(e){var t=s({x:e[0],y:e[1]},I.xMin,I.yMin,B);return[t.x,t.y]})),W="curve"===j?function(e,t,r){return function(e){for(var t=e[0],r=e[0],n="M"+r[0]+","+r[1]+"M",o=0;o<e.length;o++){var i=e[o],a=[(t[0]-(u=i[0]))/2+u,(t[1]-(d=i[1]))/2+d];n+=" "+a[0]+","+a[1],n+="Q"+i[0]+","+i[1],t=i}var u,d,l=e[e.length-1];return n+" "+l[0]+","+l[1]}([[e.x,e.y]].concat(r,[[t.x,t.y]]))}(L,k,X):function(e,t,r){var n="M "+e.x+", "+e.y+" ";return r.forEach((function(e){n+="L "+e[0]+", "+e[1]+" "})),n+="L "+t.x+", "+t.y+" "}(L,k,X),Y=G[Math.floor(G.length/2)],z=s({x:Y[0],y:Y[1]},I.xMin,I.yMin,B),H=S?r.createElement(o.EdgeText,{x:z.x,y:z.y,label:S,labelStyle:P,labelShowBg:N,labelBgStyle:T,labelBgPadding:R,labelBgBorderRadius:A}):null,Q=o.getMarkerEnd(b,E);return r.createElement(r.Fragment,null,r.createElement("path",{style:M,className:"react-flow__edge-path",d:W,markerEnd:Q}),H)})),M=t.memo((function(e){var i=o.useStoreState((function(e){return e.nodes})),u=p().debounceTime,d=t.useState(a({storeNodes:i},e)),l=d[0],s=d[1];return n((function(){s(a({storeNodes:i},e))}),u,[e,i]),r.createElement(E,Object.assign({},l))})),w=t.memo((function(e){var t=o.useStoreState((function(e){return e.nodes}));return r.createElement(E,Object.assign({storeNodes:t},e))})),S=t.memo((function(e){var n=t.useContext(m);return n?r.createElement(0===n.debounceTime?w:M,Object.assign({},e)):r.createElement(b,null,r.createElement(M,Object.assign({},e)),";")}));exports.SmartEdge=S,exports.SmartEdgeProvider=b,exports.default=S; | ||
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var t=require("react"),r=e(t),n=require("react-flow-renderer"),o=e(require("react-use/lib/useDebounce"));require("tiny-warning");var i=require("pathfinding");function a(){return(a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}var d=function(e,t){return void 0===t&&(t=10),Math.round(e/t)*t},u=function(e,t){return void 0===t&&(t=10),Math.floor(e/t)*t},l=function(e,t){return void 0===t&&(t=10),Math.ceil(e/t)*t},s=function(e,t){void 0===t&&(t=0);var r=Math.max(Math.round(e),t);return(r=Number.isInteger(r)?r:t)>=t?r:t},c={debounceTime:200,nodePadding:10,gridRatio:10,lineType:"curve",lessCorners:!1},y=t.createContext(c),f=function(e){var t=e.children,n=e.options,o=void 0===n?c:n,i=o.debounceTime,a=void 0===i?200:i,d=o.nodePadding,u=void 0===d?10:d,l=o.gridRatio,f=void 0===l?10:l,g=o.lineType,x=void 0===g?"curve":g,v=o.lessCorners,h=void 0!==v&&v;return f=s(f,2),u=s(u,2),a=s(a),"curve"!==x&&"straight"!==x&&(x="curve"),r.createElement(y.Provider,{value:{debounceTime:a,nodePadding:u,gridRatio:f,lineType:x,lessCorners:h}},t)},g=function(){var e=t.useContext(y);if(void 0===e)throw new Error("useSmartEdge must be used within a SmartEdgeProvider");if(void 0===e.debounceTime||void 0===e.gridRatio||void 0===e.nodePadding||void 0===e.lineType)throw new Error("Missing options on SmartEdgeProvider");return e},x=function(e,t){switch(t){case"top":return{x:e.x,y:e.y-1};case"bottom":return{x:e.x,y:e.y+1};case"left":return{x:e.x-1,y:e.y};case"right":return{x:e.x+1,y:e.y}}},v=function(e,t,r){for(var n=e.getNodeAt(t.x,t.y);!n.walkable;){e.setWalkableAt(n.x,n.y,!0);var o=x(n,r);n=e.getNodeAt(o.x,o.y)}},h=function(e,t,r,n){var o=e.x/n,i=e.y/n,a=t/n,d=r/n;if(a<1)for(;1!==a;)a++,o++;else if(a>1)for(;1!==a;)a--,o--;if(d<1)for(;1!==d;)d++,i++;else if(d>1)for(;1!==d;)d--,i--;return{x:o,y:i}},m=function(e,t,r,n){var o=e.x*n,i=e.y*n,a=t,d=r;if(a<n)for(;a!==n;)a+=n,o-=n;else if(a>n)for(;a!==n;)a-=n,o+=n;if(d<n)for(;d!==n;)d+=n,i-=n;else if(d>n)for(;d!==n;)d-=n,i+=n;return{x:o,y:i}},b={allowDiagonal:!0,dontCrossCorners:!0,diagonalMovement:i.DiagonalMovement.Always},p={allowDiagonal:!1},E=t.memo((function(e){var t=e.sourceX,o=e.sourceY,a=e.sourcePosition,s=e.targetX,c=e.targetY,y=e.targetPosition,f=e.arrowHeadType,E=e.markerEndId,M=e.style,w=e.storeNodes,S=e.label,N=e.labelStyle,P=e.labelShowBg,R=e.labelBgStyle,T=e.labelBgPadding,A=e.labelBgBorderRadius,_=g(),B=_.gridRatio,j=_.lineType,C=_.lessCorners,O=function(e,t,r){void 0===t&&(t=2),void 0===r&&(r=2);var n=Number.MIN_SAFE_INTEGER,o=Number.MIN_SAFE_INTEGER,i=Number.MAX_SAFE_INTEGER,a=Number.MAX_SAFE_INTEGER,d=e.map((function(e){var d,s,c=null==e?void 0:e.__rf,y=Math.max((null==c?void 0:c.width)||0,1),f=Math.max((null==c?void 0:c.height)||0,1),g=(null==c||null==(d=c.position)?void 0:d.x)||0,x=(null==c||null==(s=c.position)?void 0:s.y)||0,v={x:g-t,y:x-t},h={x:g-t,y:x+f+t},m={x:g+y+t,y:x-t},b={x:g+y+t,y:x+f+t};return r>0&&(v.x=u(v.x,r),v.y=u(v.y,r),h.x=u(h.x,r),h.y=l(h.y,r),m.x=l(m.x,r),m.y=u(m.y,r),b.x=l(b.x,r),b.y=l(b.y,r)),v.y<a&&(a=v.y),v.x<i&&(i=v.x),b.y>o&&(o=b.y),b.x>n&&(n=b.x),{id:e.id,width:y,height:f,topLeft:v,bottomLeft:h,topRight:m,bottomRight:b}})),s=2*t;n=l(n+s,r),o=l(o+s,r),i=u(i-s,r),a=u(a-s,r);var c={x:i,y:a},y={x:i,y:o},f={x:n,y:a};return{nodes:d,graph:{topLeft:c,bottomLeft:y,topRight:f,bottomRight:{x:n,y:o},width:Math.abs(c.x-f.x),height:Math.abs(c.y-y.y),xMax:n,yMax:o,xMin:i,yMin:a}}}(w,_.nodePadding,B),I=O.graph,F={x:t,y:o,position:a},L={x:s,y:c,position:y},k=function(e,t,r,n,o){void 0===o&&(o=2);var a=e.xMin,u=e.yMin,s=e.height,c=l(e.width,o)/o+1,y=l(s,o)/o+1,f=new i.Grid(c,y);t.forEach((function(e){for(var t=h(e.topLeft,a,u,o),r=h(e.bottomRight,a,u,o),n=t.x;n<r.x;n++)for(var i=t.y;i<r.y;i++)f.setWalkableAt(n,i,!1)}));var g=h({x:d(r.x,o),y:d(r.y,o)},a,u,o),m=h({x:d(n.x,o),y:d(n.y,o)},a,u,o),b=f.getNodeAt(g.x,g.y);v(f,b,r.position);var p=f.getNodeAt(m.x,m.y);v(f,p,n.position);var E=x(b,r.position),M=x(p,n.position);return{grid:f,start:E,end:M}}(I,O.nodes,F,L,B),q=function(e,t,r,n){var o=new i.AStarFinder(n?p:b),a=[],d=[];try{a=o.findPath(t.x,t.y,r.x,r.y,e),d=i.Util.smoothenPath(e,a)}catch(e){}return{fullPath:a,smoothedPath:d}}(k.grid,k.start,k.end,C),G=q.fullPath,D=q.smoothedPath;if(D.length<=2)return r.createElement("curve"===j?n.BezierEdge:n.StraightEdge,Object.assign({},e));var X=D.map((function(e){var t=m({x:e[0],y:e[1]},I.xMin,I.yMin,B);return[t.x,t.y]})),W="curve"===j?function(e,t,r){return function(e){for(var t=e[0],r=e[0],n="M"+r[0]+","+r[1]+"M",o=0;o<e.length;o++){var i=e[o],a=[(t[0]-(d=i[0]))/2+d,(t[1]-(u=i[1]))/2+u];n+=" "+a[0]+","+a[1],n+="Q"+i[0]+","+i[1],t=i}var d,u,l=e[e.length-1];return n+" "+l[0]+","+l[1]}([[e.x,e.y]].concat(r,[[t.x,t.y]]))}(F,L,X):function(e,t,r){var n="M "+e.x+", "+e.y+" ";return r.forEach((function(e){n+="L "+e[0]+", "+e[1]+" "})),n+="L "+t.x+", "+t.y+" "}(F,L,X),Y=G[Math.floor(G.length/2)],z=m({x:Y[0],y:Y[1]},I.xMin,I.yMin,B),H=S?r.createElement(n.EdgeText,{x:z.x,y:z.y,label:S,labelStyle:N,labelShowBg:P,labelBgStyle:R,labelBgPadding:T,labelBgBorderRadius:A}):null,Q=n.getMarkerEnd(f,E);return r.createElement(r.Fragment,null,r.createElement("path",{style:M,className:"react-flow__edge-path",d:W,markerEnd:Q}),H)})),M=t.memo((function(e){var i=n.useStoreState((function(e){return e.nodes})),d=g().debounceTime,u=t.useState(a({storeNodes:i},e)),l=u[0],s=u[1];return o((function(){s(a({storeNodes:i},e))}),d,[e,i]),r.createElement(E,Object.assign({},l))})),w=t.memo((function(e){var t=n.useStoreState((function(e){return e.nodes}));return r.createElement(E,Object.assign({storeNodes:t},e))}));w.displayName="RegularPathFindingEdge";var S=t.memo((function(e){var n=t.useContext(y);return n?r.createElement(0===n.debounceTime?w:M,Object.assign({},e)):r.createElement(f,null,r.createElement(M,Object.assign({},e)),";")}));S.displayName="SmartEdge",exports.SmartEdge=S,exports.SmartEdgeProvider=f,exports.default=S; | ||
//# sourceMappingURL=react-flow-smart-edge.cjs.production.min.js.map |
import React, { createContext, useContext, memo, useState } from 'react'; | ||
import { useStoreState, BezierEdge, StraightEdge, EdgeText, getMarkerEnd } from 'react-flow-renderer'; | ||
import useDebounce from 'react-use/lib/useDebounce'; | ||
import { useStoreState, BezierEdge, StraightEdge, EdgeText, getMarkerEnd } from 'react-flow-renderer'; | ||
import warning from 'tiny-warning'; | ||
import { Grid, AStarFinder, Util, DiagonalMovement } from 'pathfinding'; | ||
import warning from 'tiny-warning'; | ||
@@ -25,2 +25,91 @@ function _extends() { | ||
var round = function round(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.round(x / multiple) * multiple; | ||
}; | ||
var roundDown = function roundDown(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.floor(x / multiple) * multiple; | ||
}; | ||
var roundUp = function roundUp(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.ceil(x / multiple) * multiple; | ||
}; | ||
var toInteger = function toInteger(value, min) { | ||
if (min === void 0) { | ||
min = 0; | ||
} | ||
var result = Math.max(Math.round(value), min); | ||
result = Number.isInteger(result) ? result : min; | ||
result = result >= min ? result : min; | ||
return result; | ||
}; | ||
var defaultOptions = { | ||
debounceTime: 200, | ||
nodePadding: 10, | ||
gridRatio: 10, | ||
lineType: 'curve', | ||
lessCorners: false | ||
}; | ||
var SmartEdgeContext = /*#__PURE__*/createContext(defaultOptions); | ||
var SmartEdgeProvider = function SmartEdgeProvider(_ref) { | ||
var children = _ref.children, | ||
_ref$options = _ref.options, | ||
options = _ref$options === void 0 ? defaultOptions : _ref$options; | ||
var _options$debounceTime = options.debounceTime, | ||
debounceTime = _options$debounceTime === void 0 ? 200 : _options$debounceTime, | ||
_options$nodePadding = options.nodePadding, | ||
nodePadding = _options$nodePadding === void 0 ? 10 : _options$nodePadding, | ||
_options$gridRatio = options.gridRatio, | ||
gridRatio = _options$gridRatio === void 0 ? 10 : _options$gridRatio, | ||
_options$lineType = options.lineType, | ||
lineType = _options$lineType === void 0 ? 'curve' : _options$lineType; | ||
var _options$lessCorners = options.lessCorners, | ||
lessCorners = _options$lessCorners === void 0 ? false : _options$lessCorners; // Guarantee that all values are positive integers | ||
gridRatio = toInteger(gridRatio, 2); | ||
nodePadding = toInteger(nodePadding, 2); | ||
debounceTime = toInteger(debounceTime); // Guarantee correct line type | ||
if (lineType !== 'curve' && lineType !== 'straight') { | ||
lineType = 'curve'; | ||
} | ||
process.env.NODE_ENV !== "production" ? warning(debounceTime >= 30, 'A small debounce time on SmartEdge can cause performance issues on large graphs.') : void 0; | ||
process.env.NODE_ENV !== "production" ? warning(gridRatio >= 10, 'A small grid ratio on SmartEdge can cause performance issues on large graphs.') : void 0; | ||
return React.createElement(SmartEdgeContext.Provider, { | ||
value: { | ||
debounceTime: debounceTime, | ||
nodePadding: nodePadding, | ||
gridRatio: gridRatio, | ||
lineType: lineType, | ||
lessCorners: lessCorners | ||
} | ||
}, children); | ||
}; | ||
var useSmartEdge = function useSmartEdge() { | ||
var context = useContext(SmartEdgeContext); | ||
if (context === undefined) { | ||
throw new Error('useSmartEdge must be used within a SmartEdgeProvider'); | ||
} | ||
if (context.debounceTime === undefined || context.gridRatio === undefined || context.nodePadding === undefined || context.lineType === undefined) { | ||
throw new Error('Missing options on SmartEdgeProvider'); | ||
} | ||
return context; | ||
}; | ||
var getNextPointFromPosition = function getNextPointFromPosition(point, position) { | ||
@@ -165,34 +254,2 @@ switch (position) { | ||
var round = function round(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.round(x / multiple) * multiple; | ||
}; | ||
var roundDown = function roundDown(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.floor(x / multiple) * multiple; | ||
}; | ||
var roundUp = function roundUp(x, multiple) { | ||
if (multiple === void 0) { | ||
multiple = 10; | ||
} | ||
return Math.ceil(x / multiple) * multiple; | ||
}; | ||
var toInteger = function toInteger(value, min) { | ||
if (min === void 0) { | ||
min = 0; | ||
} | ||
var result = Math.max(Math.round(value), min); | ||
result = Number.isInteger(result) ? result : min; | ||
result = result >= min ? result : min; | ||
return result; | ||
}; | ||
var createGrid = function createGrid(graph, nodes, source, target, gridRatio) { | ||
@@ -352,2 +409,6 @@ if (gridRatio === void 0) { | ||
/* eslint-disable | ||
@typescript-eslint/no-unsafe-assignment, | ||
@typescript-eslint/no-unsafe-member-access, | ||
*/ | ||
var rf = node == null ? void 0 : node.__rf; | ||
@@ -360,2 +421,7 @@ var width = Math.max((rf == null ? void 0 : rf.width) || 0, 1); | ||
}; | ||
/* eslint-enable | ||
@typescript-eslint/no-unsafe-assignment, | ||
@typescript-eslint/no-unsafe-member-access, | ||
*/ | ||
var topLeft = { | ||
@@ -444,59 +510,2 @@ x: position.x - nodePadding, | ||
var defaultOptions = { | ||
debounceTime: 200, | ||
nodePadding: 10, | ||
gridRatio: 10, | ||
lineType: 'curve', | ||
lessCorners: false | ||
}; | ||
var SmartEdgeContext = /*#__PURE__*/createContext(defaultOptions); | ||
var SmartEdgeProvider = function SmartEdgeProvider(_ref) { | ||
var children = _ref.children, | ||
_ref$options = _ref.options, | ||
options = _ref$options === void 0 ? defaultOptions : _ref$options; | ||
var _options$debounceTime = options.debounceTime, | ||
debounceTime = _options$debounceTime === void 0 ? 200 : _options$debounceTime, | ||
_options$nodePadding = options.nodePadding, | ||
nodePadding = _options$nodePadding === void 0 ? 10 : _options$nodePadding, | ||
_options$gridRatio = options.gridRatio, | ||
gridRatio = _options$gridRatio === void 0 ? 10 : _options$gridRatio, | ||
_options$lineType = options.lineType, | ||
lineType = _options$lineType === void 0 ? 'curve' : _options$lineType, | ||
_options$lessCorners = options.lessCorners, | ||
lessCorners = _options$lessCorners === void 0 ? false : _options$lessCorners; // Guarantee that all values are positive integers | ||
gridRatio = toInteger(gridRatio, 2); | ||
nodePadding = toInteger(nodePadding, 2); | ||
debounceTime = toInteger(debounceTime); // Guarantee correct line type | ||
if (lineType !== 'curve' && lineType !== 'straight') { | ||
lineType = 'curve'; | ||
} | ||
process.env.NODE_ENV !== "production" ? warning(debounceTime >= 30, 'A small debounce time on SmartEdge can cause performance issues on large graphs.') : void 0; | ||
process.env.NODE_ENV !== "production" ? warning(gridRatio >= 10, 'A small grid ratio on SmartEdge can cause performance issues on large graphs.') : void 0; | ||
return React.createElement(SmartEdgeContext.Provider, { | ||
value: { | ||
debounceTime: debounceTime, | ||
nodePadding: nodePadding, | ||
gridRatio: gridRatio, | ||
lineType: lineType, | ||
lessCorners: lessCorners | ||
} | ||
}, children); | ||
}; | ||
var useSmartEdge = function useSmartEdge() { | ||
var context = useContext(SmartEdgeContext); | ||
if (context === undefined) { | ||
throw new Error('useSmartEdge must be used within a SmartEdgeProvider'); | ||
} | ||
if (context.debounceTime === undefined || context.gridRatio === undefined || context.nodePadding === undefined || context.lineType === undefined) { | ||
throw new Error('Missing options on SmartEdgeProvider'); | ||
} | ||
return context; | ||
}; | ||
var PathFindingEdge = /*#__PURE__*/memo(function (props) { | ||
@@ -555,6 +564,6 @@ var sourceX = props.sourceX, | ||
/* | ||
Fallback to BezierEdge if no path was found. | ||
length = 0: no path was found | ||
length = 1: starting and ending points are the same | ||
length = 2: a single straight line from point A to point B | ||
Fallback to BezierEdge if no path was found. | ||
length = 0: no path was found | ||
length = 1: starting and ending points are the same | ||
length = 2: a single straight line from point A to point B | ||
*/ | ||
@@ -608,3 +617,3 @@ | ||
style: style, | ||
className: "react-flow__edge-path", | ||
className: 'react-flow__edge-path', | ||
d: svgPathString, | ||
@@ -643,2 +652,3 @@ markerEnd: markerEnd | ||
}); | ||
RegularPathFindingEdge.displayName = 'RegularPathFindingEdge'; | ||
var SmartEdge = /*#__PURE__*/memo(function (props) { | ||
@@ -657,2 +667,3 @@ var context = useContext(SmartEdgeContext); | ||
}); | ||
SmartEdge.displayName = 'SmartEdge'; | ||
@@ -659,0 +670,0 @@ export default SmartEdge; |
@@ -15,4 +15,4 @@ import React from 'react'; | ||
} | ||
export declare const SmartEdgeProvider: ({ children, options, }: ProviderProps) => JSX.Element; | ||
export declare const SmartEdgeProvider: ({ children, options }: ProviderProps) => JSX.Element; | ||
export declare const useSmartEdge: () => Required<SmartEdgeOptions>; | ||
export {}; |
200
package.json
{ | ||
"name": "@tisoap/react-flow-smart-edge", | ||
"version": "0.4.0", | ||
"keywords": [ | ||
"react", | ||
"typescript", | ||
"graph", | ||
"flow", | ||
"flowchart", | ||
"smart", | ||
"edge", | ||
"pathfinding", | ||
"react-flow-smart-edge" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/tisoap/react-flow-smart-edge.git" | ||
}, | ||
"license": "MIT", | ||
"author": "Tiso Alvarez Puccinelli", | ||
"main": "dist/index.js", | ||
"module": "dist/react-flow-smart-edge.esm.js", | ||
"typings": "dist/index.d.ts", | ||
"files": [ | ||
"dist", | ||
"src" | ||
], | ||
"scripts": { | ||
"build": "tsdx build", | ||
"build-storybook": "build-storybook", | ||
"lint": "tsdx lint src test stories --fix", | ||
"prepare": "tsdx build", | ||
"release": "np", | ||
"start": "tsdx watch", | ||
"storybook": "start-storybook -p 6006", | ||
"test": "tsdx test --passWithNoTests" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "tsdx lint" | ||
} | ||
}, | ||
"prettier": { | ||
"printWidth": 80, | ||
"semi": true, | ||
"singleQuote": true, | ||
"tabWidth": 2, | ||
"tabs": false, | ||
"trailingComma": "es5" | ||
}, | ||
"dependencies": { | ||
"pathfinding": "0.4.18", | ||
"react-use": "17.3.1", | ||
"tiny-warning": "1.0.3" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "7.16.0", | ||
"@storybook/addon-essentials": "6.4.9", | ||
"@storybook/addon-info": "5.3.21", | ||
"@storybook/addon-links": "6.4.9", | ||
"@storybook/addons": "6.4.9", | ||
"@storybook/builder-webpack5": "6.4.9", | ||
"@storybook/components": "6.4.9", | ||
"@storybook/core-events": "6.4.9", | ||
"@storybook/manager-webpack5": "6.4.9", | ||
"@storybook/react": "6.4.9", | ||
"@storybook/theming": "6.4.9", | ||
"@types/pathfinding": "0.0.5", | ||
"@types/react": "17.0.34", | ||
"@types/react-dom": "17.0.11", | ||
"@typescript-eslint/eslint-plugin": "4.33.0", | ||
"@typescript-eslint/parser": "4.33.0", | ||
"babel-eslint": "10.1.0", | ||
"babel-loader": "8.2.3", | ||
"eslint": "7.32.0", | ||
"eslint-config-prettier": "7.2.0", | ||
"eslint-config-react-app": "6.0.0", | ||
"eslint-plugin-flowtype": "5.10.0", | ||
"eslint-plugin-import": "2.25.3", | ||
"eslint-plugin-jsx-a11y": "6.5.1", | ||
"eslint-plugin-prettier": "3.3.1", | ||
"eslint-plugin-react": "7.27.0", | ||
"eslint-plugin-react-hooks": "4.3.0", | ||
"husky": "7.0.4", | ||
"np": "7.5.0", | ||
"prettier": "2.4.1", | ||
"react": "16.14.0", | ||
"react-dom": "16.14.0", | ||
"react-flow-renderer": "9.6.11", | ||
"regenerator-runtime": "0.13.9", | ||
"tsdx": "0.14.1", | ||
"tslib": "2.3.1", | ||
"typescript": "4.4.4", | ||
"webpack": "5.64.1" | ||
}, | ||
"peerDependencies": { | ||
"react": ">=16", | ||
"react-dom": ">=16", | ||
"react-flow-renderer": "^9" | ||
}, | ||
"engines": { | ||
"node": ">=12" | ||
} | ||
"name": "@tisoap/react-flow-smart-edge", | ||
"version": "0.4.1", | ||
"keywords": [ | ||
"react", | ||
"typescript", | ||
"graph", | ||
"flow", | ||
"flowchart", | ||
"smart", | ||
"edge", | ||
"pathfinding", | ||
"react-flow-smart-edge" | ||
], | ||
"homepage": "https://tisoap.github.io/react-flow-smart-edge/", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/tisoap/react-flow-smart-edge.git" | ||
}, | ||
"license": "MIT", | ||
"author": "Tiso Alvarez Puccinelli", | ||
"main": "dist/index.js", | ||
"module": "dist/react-flow-smart-edge.esm.js", | ||
"typings": "dist/index.d.ts", | ||
"files": [ | ||
"dist", | ||
"src" | ||
], | ||
"scripts": { | ||
"build-storybook": "build-storybook", | ||
"build": "tsdx build", | ||
"deploy-storybook": "gh-pages -d storybook-static", | ||
"lint": "tsdx lint src test stories --fix", | ||
"predeploy": "npm run build-storybook", | ||
"prepare": "tsdx build", | ||
"start": "tsdx watch", | ||
"storybook": "start-storybook -p 6006", | ||
"test": "tsdx test --passWithNoTests" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "tsdx lint" | ||
} | ||
}, | ||
"dependencies": { | ||
"pathfinding": "0.4.18", | ||
"react-use": "17.3.1", | ||
"tiny-warning": "1.0.3" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "7.16.0", | ||
"@storybook/addon-essentials": "6.4.9", | ||
"@storybook/addon-info": "5.3.21", | ||
"@storybook/addon-links": "6.4.9", | ||
"@storybook/addons": "6.4.9", | ||
"@storybook/builder-webpack5": "6.4.9", | ||
"@storybook/components": "6.4.9", | ||
"@storybook/core-events": "6.4.9", | ||
"@storybook/manager-webpack5": "6.4.9", | ||
"@storybook/react": "6.4.9", | ||
"@storybook/theming": "6.4.9", | ||
"@tisoap/eslint-config-ts-react": "1.1.1", | ||
"@types/pathfinding": "0.0.5", | ||
"@types/react": "17.0.34", | ||
"@types/react-dom": "17.0.11", | ||
"@typescript-eslint/eslint-plugin": "4.33.0", | ||
"@typescript-eslint/parser": "4.33.0", | ||
"babel-eslint": "10.1.0", | ||
"babel-loader": "8.2.3", | ||
"eslint": "7.32.0", | ||
"eslint-config-prettier": "7.2.0", | ||
"eslint-config-react-app": "6.0.0", | ||
"eslint-plugin-flowtype": "5.10.0", | ||
"eslint-plugin-import": "2.25.3", | ||
"eslint-plugin-jsx-a11y": "6.5.1", | ||
"eslint-plugin-prettier": "3.3.1", | ||
"eslint-plugin-react": "7.27.0", | ||
"eslint-plugin-react-hooks": "4.3.0", | ||
"eslint-plugin-storybook": "0.5.6", | ||
"gh-pages": "3.2.3", | ||
"husky": "7.0.4", | ||
"prettier": "2.4.1", | ||
"react": "16.14.0", | ||
"react-dom": "16.14.0", | ||
"react-flow-renderer": "9.6.11", | ||
"regenerator-runtime": "0.13.9", | ||
"tsdx": "0.14.1", | ||
"tslib": "2.3.1", | ||
"typescript": "4.4.4", | ||
"webpack": "5.64.1" | ||
}, | ||
"peerDependencies": { | ||
"react": ">=16", | ||
"react-dom": ">=16", | ||
"react-flow-renderer": "^9" | ||
}, | ||
"engines": { | ||
"node": ">=12" | ||
} | ||
} |
@@ -116,7 +116,7 @@ # React Flow Smart Edge | ||
## Example | ||
## Examples | ||
There is a minimum example in this repository [`example` folder](https://github.com/tisoap/react-flow-smart-edge/tree/main/example). Clone this repository and run `yarn; cd example; yarn; yarn start`. | ||
You can see Storybook examples by visiting this page: https://tisoap.github.io/react-flow-smart-edge/ | ||
you can also see the Storybook examples or interact with the stories yourself by cloning this repository and running `yarn; yarn storybook`. | ||
There's also is a minimum example in this repository [`example` folder](https://github.com/tisoap/react-flow-smart-edge/tree/main/example). Clone this repository and run `yarn; cd example; yarn; yarn start`. | ||
@@ -123,0 +123,0 @@ ## License |
@@ -1,79 +0,79 @@ | ||
import { Grid } from 'pathfinding'; | ||
import { Grid } from 'pathfinding' | ||
import { | ||
guaranteeWalkablePath, | ||
getNextPointFromPosition, | ||
} from './guaranteeWalkablePath'; | ||
import { graphToGridPoint } from './pointConversion'; | ||
import { round, roundUp } from './utils'; | ||
import type { NodeBoundingBox, GraphBoundingBox } from './getBoundingBoxes'; | ||
import type { Position } from 'react-flow-renderer'; | ||
guaranteeWalkablePath, | ||
getNextPointFromPosition | ||
} from './guaranteeWalkablePath' | ||
import { graphToGridPoint } from './pointConversion' | ||
import { round, roundUp } from './utils' | ||
import type { NodeBoundingBox, GraphBoundingBox } from './getBoundingBoxes' | ||
import type { Position } from 'react-flow-renderer' | ||
export type PointInfo = { | ||
x: number; | ||
y: number; | ||
position: Position; | ||
}; | ||
x: number | ||
y: number | ||
position: Position | ||
} | ||
export const createGrid = ( | ||
graph: GraphBoundingBox, | ||
nodes: NodeBoundingBox[], | ||
source: PointInfo, | ||
target: PointInfo, | ||
gridRatio = 2 | ||
graph: GraphBoundingBox, | ||
nodes: NodeBoundingBox[], | ||
source: PointInfo, | ||
target: PointInfo, | ||
gridRatio = 2 | ||
) => { | ||
const { xMin, yMin, width, height } = graph; | ||
const { xMin, yMin, width, height } = graph | ||
// Create a grid representation of the graph box, where each cell is | ||
// equivalent to 10x10 pixels (or the grid ratio) on the graph. We'll use | ||
// this simplified grid to do pathfinding. | ||
const mapColumns = roundUp(width, gridRatio) / gridRatio + 1; | ||
const mapRows = roundUp(height, gridRatio) / gridRatio + 1; | ||
const grid = new Grid(mapColumns, mapRows); | ||
// Create a grid representation of the graph box, where each cell is | ||
// equivalent to 10x10 pixels (or the grid ratio) on the graph. We'll use | ||
// this simplified grid to do pathfinding. | ||
const mapColumns = roundUp(width, gridRatio) / gridRatio + 1 | ||
const mapRows = roundUp(height, gridRatio) / gridRatio + 1 | ||
const grid = new Grid(mapColumns, mapRows) | ||
// Update the grid representation with the space the nodes take up | ||
nodes.forEach((node) => { | ||
const nodeStart = graphToGridPoint(node.topLeft, xMin, yMin, gridRatio); | ||
const nodeEnd = graphToGridPoint(node.bottomRight, xMin, yMin, gridRatio); | ||
// Update the grid representation with the space the nodes take up | ||
nodes.forEach((node) => { | ||
const nodeStart = graphToGridPoint(node.topLeft, xMin, yMin, gridRatio) | ||
const nodeEnd = graphToGridPoint(node.bottomRight, xMin, yMin, gridRatio) | ||
for (let x = nodeStart.x; x < nodeEnd.x; x++) { | ||
for (let y = nodeStart.y; y < nodeEnd.y; y++) { | ||
grid.setWalkableAt(x, y, false); | ||
} | ||
} | ||
}); | ||
for (let x = nodeStart.x; x < nodeEnd.x; x++) { | ||
for (let y = nodeStart.y; y < nodeEnd.y; y++) { | ||
grid.setWalkableAt(x, y, false) | ||
} | ||
} | ||
}) | ||
// Convert the starting and ending graph points to grid points | ||
const startGrid = graphToGridPoint( | ||
{ | ||
x: round(source.x, gridRatio), | ||
y: round(source.y, gridRatio), | ||
}, | ||
xMin, | ||
yMin, | ||
gridRatio | ||
); | ||
// Convert the starting and ending graph points to grid points | ||
const startGrid = graphToGridPoint( | ||
{ | ||
x: round(source.x, gridRatio), | ||
y: round(source.y, gridRatio) | ||
}, | ||
xMin, | ||
yMin, | ||
gridRatio | ||
) | ||
const endGrid = graphToGridPoint( | ||
{ | ||
x: round(target.x, gridRatio), | ||
y: round(target.y, gridRatio), | ||
}, | ||
xMin, | ||
yMin, | ||
gridRatio | ||
); | ||
const endGrid = graphToGridPoint( | ||
{ | ||
x: round(target.x, gridRatio), | ||
y: round(target.y, gridRatio) | ||
}, | ||
xMin, | ||
yMin, | ||
gridRatio | ||
) | ||
// Guarantee a walkable path between the start and end points, even if the | ||
// source or target where covered by another node or by padding | ||
const startingNode = grid.getNodeAt(startGrid.x, startGrid.y); | ||
guaranteeWalkablePath(grid, startingNode, source.position); | ||
const endingNode = grid.getNodeAt(endGrid.x, endGrid.y); | ||
guaranteeWalkablePath(grid, endingNode, target.position); | ||
// Guarantee a walkable path between the start and end points, even if the | ||
// source or target where covered by another node or by padding | ||
const startingNode = grid.getNodeAt(startGrid.x, startGrid.y) | ||
guaranteeWalkablePath(grid, startingNode, source.position) | ||
const endingNode = grid.getNodeAt(endGrid.x, endGrid.y) | ||
guaranteeWalkablePath(grid, endingNode, target.position) | ||
// Use the next closest points as the start and end points, so | ||
// pathfinding does not start too close to the nodes | ||
const start = getNextPointFromPosition(startingNode, source.position); | ||
const end = getNextPointFromPosition(endingNode, target.position); | ||
// Use the next closest points as the start and end points, so | ||
// pathfinding does not start too close to the nodes | ||
const start = getNextPointFromPosition(startingNode, source.position) | ||
const end = getNextPointFromPosition(endingNode, target.position) | ||
return { grid, start, end }; | ||
}; | ||
return { grid, start, end } | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { XYPosition } from 'react-flow-renderer'; | ||
import type { XYPosition } from 'react-flow-renderer' | ||
@@ -7,17 +7,17 @@ /** | ||
export const drawStraightLinePath = ( | ||
source: XYPosition, | ||
target: XYPosition, | ||
path: number[][] | ||
source: XYPosition, | ||
target: XYPosition, | ||
path: number[][] | ||
) => { | ||
let svgPathString = `M ${source.x}, ${source.y} `; | ||
let svgPathString = `M ${source.x}, ${source.y} ` | ||
path.forEach((point) => { | ||
const [x, y] = point; | ||
svgPathString += `L ${x}, ${y} `; | ||
}); | ||
path.forEach((point) => { | ||
const [x, y] = point | ||
svgPathString += `L ${x}, ${y} ` | ||
}) | ||
svgPathString += `L ${target.x}, ${target.y} `; | ||
svgPathString += `L ${target.x}, ${target.y} ` | ||
return svgPathString; | ||
}; | ||
return svgPathString | ||
} | ||
@@ -28,37 +28,37 @@ /** | ||
export const drawSmoothLinePath = ( | ||
source: XYPosition, | ||
target: XYPosition, | ||
path: number[][] | ||
source: XYPosition, | ||
target: XYPosition, | ||
path: number[][] | ||
) => { | ||
const points = [[source.x, source.y], ...path, [target.x, target.y]]; | ||
return quadraticBezierCurve(points); | ||
}; | ||
const points = [[source.x, source.y], ...path, [target.x, target.y]] | ||
return quadraticBezierCurve(points) | ||
} | ||
const quadraticBezierCurve = (points: number[][]) => { | ||
const X = 0; | ||
const Y = 1; | ||
let point = points[0]; | ||
const X = 0 | ||
const Y = 1 | ||
let point = points[0] | ||
const first = points[0]; | ||
let svgPath = `M${first[X]},${first[Y]}M`; | ||
const first = points[0] | ||
let svgPath = `M${first[X]},${first[Y]}M` | ||
for (let i = 0; i < points.length; i++) { | ||
const next = points[i]; | ||
const midPoint = getMidPoint(point[X], point[Y], next[X], next[Y]); | ||
for (let i = 0; i < points.length; i++) { | ||
const next = points[i] | ||
const midPoint = getMidPoint(point[X], point[Y], next[X], next[Y]) | ||
svgPath += ` ${midPoint[X]},${midPoint[Y]}`; | ||
svgPath += `Q${next[X]},${next[Y]}`; | ||
point = next; | ||
} | ||
svgPath += ` ${midPoint[X]},${midPoint[Y]}` | ||
svgPath += `Q${next[X]},${next[Y]}` | ||
point = next | ||
} | ||
const last = points[points.length - 1]; | ||
svgPath += ` ${last[0]},${last[1]}`; | ||
const last = points[points.length - 1] | ||
svgPath += ` ${last[0]},${last[1]}` | ||
return svgPath; | ||
}; | ||
return svgPath | ||
} | ||
const getMidPoint = (Ax: number, Ay: number, Bx: number, By: number) => { | ||
const Zx = (Ax - Bx) / 2 + Bx; | ||
const Zy = (Ay - By) / 2 + By; | ||
return [Zx, Zy]; | ||
}; | ||
const Zx = (Ax - Bx) / 2 + Bx | ||
const Zy = (Ay - By) / 2 + By | ||
return [Zx, Zy] | ||
} |
@@ -1,50 +0,50 @@ | ||
import { AStarFinder, Util, DiagonalMovement } from 'pathfinding'; | ||
import type { Grid } from 'pathfinding'; | ||
import type { XYPosition } from 'react-flow-renderer'; | ||
import { AStarFinder, Util, DiagonalMovement } from 'pathfinding' | ||
import type { Grid } from 'pathfinding' | ||
import type { XYPosition } from 'react-flow-renderer' | ||
// https://www.npmjs.com/package/pathfinding#advanced-usage | ||
declare module 'pathfinding' { | ||
interface FinderOptions extends Heuristic { | ||
diagonalMovement?: DiagonalMovement; | ||
weight?: number; | ||
allowDiagonal?: boolean; | ||
dontCrossCorners?: boolean; | ||
} | ||
interface FinderOptions extends Heuristic { | ||
diagonalMovement?: DiagonalMovement | ||
weight?: number | ||
allowDiagonal?: boolean | ||
dontCrossCorners?: boolean | ||
} | ||
} | ||
const withDiagonalMovement = { | ||
allowDiagonal: true, | ||
dontCrossCorners: true, | ||
diagonalMovement: DiagonalMovement.Always, | ||
}; | ||
allowDiagonal: true, | ||
dontCrossCorners: true, | ||
diagonalMovement: DiagonalMovement.Always | ||
} | ||
const withStraightMovement = { | ||
allowDiagonal: false, | ||
}; | ||
allowDiagonal: false | ||
} | ||
export const generatePath = ( | ||
grid: Grid, | ||
start: XYPosition, | ||
end: XYPosition, | ||
lessCorners: boolean | ||
grid: Grid, | ||
start: XYPosition, | ||
end: XYPosition, | ||
lessCorners: boolean | ||
) => { | ||
const finderOptions = lessCorners | ||
? withStraightMovement | ||
: withDiagonalMovement; | ||
const finderOptions = lessCorners | ||
? withStraightMovement | ||
: withDiagonalMovement | ||
const finder = new AStarFinder(finderOptions); | ||
const finder = new AStarFinder(finderOptions) | ||
let fullPath: number[][] = []; | ||
let smoothedPath: number[][] = []; | ||
let fullPath: number[][] = [] | ||
let smoothedPath: number[][] = [] | ||
try { | ||
fullPath = finder.findPath(start.x, start.y, end.x, end.y, grid); | ||
smoothedPath = Util.smoothenPath(grid, fullPath); | ||
} catch { | ||
// No path was found. This can happen if the end point is "surrounded" | ||
// by other nodes, or if the starting and ending nodes are on top of | ||
// each other. | ||
} | ||
try { | ||
fullPath = finder.findPath(start.x, start.y, end.x, end.y, grid) | ||
smoothedPath = Util.smoothenPath(grid, fullPath) | ||
} catch { | ||
// No path was found. This can happen if the end point is "surrounded" | ||
// by other nodes, or if the starting and ending nodes are on top of | ||
// each other. | ||
} | ||
return { fullPath, smoothedPath }; | ||
}; | ||
return { fullPath, smoothedPath } | ||
} |
@@ -1,26 +0,26 @@ | ||
import { roundUp, roundDown } from './utils'; | ||
import type { Node, XYPosition } from 'react-flow-renderer'; | ||
import { roundUp, roundDown } from './utils' | ||
import type { Node, XYPosition } from 'react-flow-renderer' | ||
export type NodeBoundingBox = { | ||
id: string; | ||
width: number; | ||
height: number; | ||
topLeft: XYPosition; | ||
bottomLeft: XYPosition; | ||
topRight: XYPosition; | ||
bottomRight: XYPosition; | ||
}; | ||
id: string | ||
width: number | ||
height: number | ||
topLeft: XYPosition | ||
bottomLeft: XYPosition | ||
topRight: XYPosition | ||
bottomRight: XYPosition | ||
} | ||
export type GraphBoundingBox = { | ||
width: number; | ||
height: number; | ||
topLeft: XYPosition; | ||
bottomLeft: XYPosition; | ||
topRight: XYPosition; | ||
bottomRight: XYPosition; | ||
xMax: number; | ||
yMax: number; | ||
xMin: number; | ||
yMin: number; | ||
}; | ||
width: number | ||
height: number | ||
topLeft: XYPosition | ||
bottomLeft: XYPosition | ||
topRight: XYPosition | ||
bottomRight: XYPosition | ||
xMax: number | ||
yMax: number | ||
xMin: number | ||
yMin: number | ||
} | ||
@@ -37,108 +37,117 @@ /** | ||
export const getBoundingBoxes = ( | ||
storeNodes: Node[], | ||
nodePadding = 2, | ||
roundTo = 2 | ||
storeNodes: Node[], | ||
nodePadding = 2, | ||
roundTo = 2 | ||
) => { | ||
let xMax = Number.MIN_SAFE_INTEGER; | ||
let yMax = Number.MIN_SAFE_INTEGER; | ||
let xMin = Number.MAX_SAFE_INTEGER; | ||
let yMin = Number.MAX_SAFE_INTEGER; | ||
let xMax = Number.MIN_SAFE_INTEGER | ||
let yMax = Number.MIN_SAFE_INTEGER | ||
let xMin = Number.MAX_SAFE_INTEGER | ||
let yMin = Number.MAX_SAFE_INTEGER | ||
const nodes: NodeBoundingBox[] = storeNodes.map((node) => { | ||
const rf = node?.__rf; | ||
const width = Math.max(rf?.width || 0, 1); | ||
const height = Math.max(rf?.height || 0, 1); | ||
const nodes: NodeBoundingBox[] = storeNodes.map((node) => { | ||
/* eslint-disable | ||
@typescript-eslint/no-unsafe-assignment, | ||
@typescript-eslint/no-unsafe-member-access, | ||
*/ | ||
const rf = node?.__rf | ||
const width = Math.max(rf?.width || 0, 1) | ||
const height = Math.max(rf?.height || 0, 1) | ||
const position: XYPosition = { | ||
x: rf?.position?.x || 0, | ||
y: rf?.position?.y || 0, | ||
}; | ||
const topLeft: XYPosition = { | ||
x: position.x - nodePadding, | ||
y: position.y - nodePadding, | ||
}; | ||
const bottomLeft: XYPosition = { | ||
x: position.x - nodePadding, | ||
y: position.y + height + nodePadding, | ||
}; | ||
const topRight: XYPosition = { | ||
x: position.x + width + nodePadding, | ||
y: position.y - nodePadding, | ||
}; | ||
const bottomRight: XYPosition = { | ||
x: position.x + width + nodePadding, | ||
y: position.y + height + nodePadding, | ||
}; | ||
const position: XYPosition = { | ||
x: rf?.position?.x || 0, | ||
y: rf?.position?.y || 0 | ||
} | ||
/* eslint-enable | ||
@typescript-eslint/no-unsafe-assignment, | ||
@typescript-eslint/no-unsafe-member-access, | ||
*/ | ||
if (roundTo > 0) { | ||
topLeft.x = roundDown(topLeft.x, roundTo); | ||
topLeft.y = roundDown(topLeft.y, roundTo); | ||
bottomLeft.x = roundDown(bottomLeft.x, roundTo); | ||
bottomLeft.y = roundUp(bottomLeft.y, roundTo); | ||
topRight.x = roundUp(topRight.x, roundTo); | ||
topRight.y = roundDown(topRight.y, roundTo); | ||
bottomRight.x = roundUp(bottomRight.x, roundTo); | ||
bottomRight.y = roundUp(bottomRight.y, roundTo); | ||
} | ||
const topLeft: XYPosition = { | ||
x: position.x - nodePadding, | ||
y: position.y - nodePadding | ||
} | ||
const bottomLeft: XYPosition = { | ||
x: position.x - nodePadding, | ||
y: position.y + height + nodePadding | ||
} | ||
const topRight: XYPosition = { | ||
x: position.x + width + nodePadding, | ||
y: position.y - nodePadding | ||
} | ||
const bottomRight: XYPosition = { | ||
x: position.x + width + nodePadding, | ||
y: position.y + height + nodePadding | ||
} | ||
if (topLeft.y < yMin) yMin = topLeft.y; | ||
if (topLeft.x < xMin) xMin = topLeft.x; | ||
if (bottomRight.y > yMax) yMax = bottomRight.y; | ||
if (bottomRight.x > xMax) xMax = bottomRight.x; | ||
if (roundTo > 0) { | ||
topLeft.x = roundDown(topLeft.x, roundTo) | ||
topLeft.y = roundDown(topLeft.y, roundTo) | ||
bottomLeft.x = roundDown(bottomLeft.x, roundTo) | ||
bottomLeft.y = roundUp(bottomLeft.y, roundTo) | ||
topRight.x = roundUp(topRight.x, roundTo) | ||
topRight.y = roundDown(topRight.y, roundTo) | ||
bottomRight.x = roundUp(bottomRight.x, roundTo) | ||
bottomRight.y = roundUp(bottomRight.y, roundTo) | ||
} | ||
return { | ||
id: node.id, | ||
width, | ||
height, | ||
topLeft, | ||
bottomLeft, | ||
topRight, | ||
bottomRight, | ||
}; | ||
}); | ||
if (topLeft.y < yMin) yMin = topLeft.y | ||
if (topLeft.x < xMin) xMin = topLeft.x | ||
if (bottomRight.y > yMax) yMax = bottomRight.y | ||
if (bottomRight.x > xMax) xMax = bottomRight.x | ||
const graphPadding = nodePadding * 2; | ||
return { | ||
id: node.id, | ||
width, | ||
height, | ||
topLeft, | ||
bottomLeft, | ||
topRight, | ||
bottomRight | ||
} | ||
}) | ||
xMax = roundUp(xMax + graphPadding, roundTo); | ||
yMax = roundUp(yMax + graphPadding, roundTo); | ||
xMin = roundDown(xMin - graphPadding, roundTo); | ||
yMin = roundDown(yMin - graphPadding, roundTo); | ||
const graphPadding = nodePadding * 2 | ||
const topLeft: XYPosition = { | ||
x: xMin, | ||
y: yMin, | ||
}; | ||
xMax = roundUp(xMax + graphPadding, roundTo) | ||
yMax = roundUp(yMax + graphPadding, roundTo) | ||
xMin = roundDown(xMin - graphPadding, roundTo) | ||
yMin = roundDown(yMin - graphPadding, roundTo) | ||
const bottomLeft: XYPosition = { | ||
x: xMin, | ||
y: yMax, | ||
}; | ||
const topLeft: XYPosition = { | ||
x: xMin, | ||
y: yMin | ||
} | ||
const topRight: XYPosition = { | ||
x: xMax, | ||
y: yMin, | ||
}; | ||
const bottomLeft: XYPosition = { | ||
x: xMin, | ||
y: yMax | ||
} | ||
const bottomRight: XYPosition = { | ||
x: xMax, | ||
y: yMax, | ||
}; | ||
const topRight: XYPosition = { | ||
x: xMax, | ||
y: yMin | ||
} | ||
const width = Math.abs(topLeft.x - topRight.x); | ||
const height = Math.abs(topLeft.y - bottomLeft.y); | ||
const bottomRight: XYPosition = { | ||
x: xMax, | ||
y: yMax | ||
} | ||
const graph: GraphBoundingBox = { | ||
topLeft, | ||
bottomLeft, | ||
topRight, | ||
bottomRight, | ||
width, | ||
height, | ||
xMax, | ||
yMax, | ||
xMin, | ||
yMin, | ||
}; | ||
const width = Math.abs(topLeft.x - topRight.x) | ||
const height = Math.abs(topLeft.y - bottomLeft.y) | ||
return { nodes, graph }; | ||
}; | ||
const graph: GraphBoundingBox = { | ||
topLeft, | ||
bottomLeft, | ||
topRight, | ||
bottomRight, | ||
width, | ||
height, | ||
xMax, | ||
yMax, | ||
xMin, | ||
yMin | ||
} | ||
return { nodes, graph } | ||
} |
@@ -1,21 +0,21 @@ | ||
import type { Grid } from 'pathfinding'; | ||
import type { Position, XYPosition } from 'react-flow-renderer'; | ||
import type { Grid } from 'pathfinding' | ||
import type { Position, XYPosition } from 'react-flow-renderer' | ||
type Direction = 'top' | 'bottom' | 'left' | 'right'; | ||
type Direction = 'top' | 'bottom' | 'left' | 'right' | ||
export const getNextPointFromPosition = ( | ||
point: XYPosition, | ||
position: Direction | ||
point: XYPosition, | ||
position: Direction | ||
): XYPosition => { | ||
switch (position) { | ||
case 'top': | ||
return { x: point.x, y: point.y - 1 }; | ||
case 'bottom': | ||
return { x: point.x, y: point.y + 1 }; | ||
case 'left': | ||
return { x: point.x - 1, y: point.y }; | ||
case 'right': | ||
return { x: point.x + 1, y: point.y }; | ||
} | ||
}; | ||
switch (position) { | ||
case 'top': | ||
return { x: point.x, y: point.y - 1 } | ||
case 'bottom': | ||
return { x: point.x, y: point.y + 1 } | ||
case 'left': | ||
return { x: point.x - 1, y: point.y } | ||
case 'right': | ||
return { x: point.x + 1, y: point.y } | ||
} | ||
} | ||
@@ -28,12 +28,12 @@ /** | ||
export const guaranteeWalkablePath = ( | ||
grid: Grid, | ||
point: XYPosition, | ||
position: Position | ||
grid: Grid, | ||
point: XYPosition, | ||
position: Position | ||
) => { | ||
let node = grid.getNodeAt(point.x, point.y); | ||
while (!node.walkable) { | ||
grid.setWalkableAt(node.x, node.y, true); | ||
const next = getNextPointFromPosition(node, position); | ||
node = grid.getNodeAt(next.x, next.y); | ||
} | ||
}; | ||
let node = grid.getNodeAt(point.x, point.y) | ||
while (!node.walkable) { | ||
grid.setWalkableAt(node.x, node.y, true) | ||
const next = getNextPointFromPosition(node, position) | ||
node = grid.getNodeAt(next.x, next.y) | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { XYPosition } from 'react-flow-renderer'; | ||
import type { XYPosition } from 'react-flow-renderer' | ||
@@ -24,39 +24,39 @@ /** | ||
export const graphToGridPoint = ( | ||
graphPoint: XYPosition, | ||
smallestX: number, | ||
smallestY: number, | ||
gridRatio: number | ||
graphPoint: XYPosition, | ||
smallestX: number, | ||
smallestY: number, | ||
gridRatio: number | ||
): XYPosition => { | ||
let x = graphPoint.x / gridRatio; | ||
let y = graphPoint.y / gridRatio; | ||
let x = graphPoint.x / gridRatio | ||
let y = graphPoint.y / gridRatio | ||
let referenceX = smallestX / gridRatio; | ||
let referenceY = smallestY / gridRatio; | ||
let referenceX = smallestX / gridRatio | ||
let referenceY = smallestY / gridRatio | ||
if (referenceX < 1) { | ||
while (referenceX !== 1) { | ||
referenceX++; | ||
x++; | ||
} | ||
} else if (referenceX > 1) { | ||
while (referenceX !== 1) { | ||
referenceX--; | ||
x--; | ||
} | ||
} | ||
if (referenceX < 1) { | ||
while (referenceX !== 1) { | ||
referenceX++ | ||
x++ | ||
} | ||
} else if (referenceX > 1) { | ||
while (referenceX !== 1) { | ||
referenceX-- | ||
x-- | ||
} | ||
} | ||
if (referenceY < 1) { | ||
while (referenceY !== 1) { | ||
referenceY++; | ||
y++; | ||
} | ||
} else if (referenceY > 1) { | ||
while (referenceY !== 1) { | ||
referenceY--; | ||
y--; | ||
} | ||
} | ||
if (referenceY < 1) { | ||
while (referenceY !== 1) { | ||
referenceY++ | ||
y++ | ||
} | ||
} else if (referenceY > 1) { | ||
while (referenceY !== 1) { | ||
referenceY-- | ||
y-- | ||
} | ||
} | ||
return { x, y }; | ||
}; | ||
return { x, y } | ||
} | ||
@@ -68,38 +68,38 @@ /** | ||
export const gridToGraphPoint = ( | ||
gridPoint: XYPosition, | ||
smallestX: number, | ||
smallestY: number, | ||
gridRatio: number | ||
gridPoint: XYPosition, | ||
smallestX: number, | ||
smallestY: number, | ||
gridRatio: number | ||
): XYPosition => { | ||
let x = gridPoint.x * gridRatio; | ||
let y = gridPoint.y * gridRatio; | ||
let x = gridPoint.x * gridRatio | ||
let y = gridPoint.y * gridRatio | ||
let referenceX = smallestX; | ||
let referenceY = smallestY; | ||
let referenceX = smallestX | ||
let referenceY = smallestY | ||
if (referenceX < gridRatio) { | ||
while (referenceX !== gridRatio) { | ||
referenceX = referenceX + gridRatio; | ||
x = x - gridRatio; | ||
} | ||
} else if (referenceX > gridRatio) { | ||
while (referenceX !== gridRatio) { | ||
referenceX = referenceX - gridRatio; | ||
x = x + gridRatio; | ||
} | ||
} | ||
if (referenceX < gridRatio) { | ||
while (referenceX !== gridRatio) { | ||
referenceX = referenceX + gridRatio | ||
x = x - gridRatio | ||
} | ||
} else if (referenceX > gridRatio) { | ||
while (referenceX !== gridRatio) { | ||
referenceX = referenceX - gridRatio | ||
x = x + gridRatio | ||
} | ||
} | ||
if (referenceY < gridRatio) { | ||
while (referenceY !== gridRatio) { | ||
referenceY = referenceY + gridRatio; | ||
y = y - gridRatio; | ||
} | ||
} else if (referenceY > gridRatio) { | ||
while (referenceY !== gridRatio) { | ||
referenceY = referenceY - gridRatio; | ||
y = y + gridRatio; | ||
} | ||
} | ||
if (referenceY < gridRatio) { | ||
while (referenceY !== gridRatio) { | ||
referenceY = referenceY + gridRatio | ||
y = y - gridRatio | ||
} | ||
} else if (referenceY > gridRatio) { | ||
while (referenceY !== gridRatio) { | ||
referenceY = referenceY - gridRatio | ||
y = y + gridRatio | ||
} | ||
} | ||
return { x, y }; | ||
}; | ||
return { x, y } | ||
} |
export const round = (x: number, multiple = 10) => | ||
Math.round(x / multiple) * multiple; | ||
Math.round(x / multiple) * multiple | ||
export const roundDown = (x: number, multiple = 10) => | ||
Math.floor(x / multiple) * multiple; | ||
Math.floor(x / multiple) * multiple | ||
export const roundUp = (x: number, multiple = 10) => | ||
Math.ceil(x / multiple) * multiple; | ||
Math.ceil(x / multiple) * multiple | ||
export const toInteger = (value: number, min = 0) => { | ||
let result = Math.max(Math.round(value), min); | ||
result = Number.isInteger(result) ? result : min; | ||
result = result >= min ? result : min; | ||
return result; | ||
}; | ||
let result = Math.max(Math.round(value), min) | ||
result = Number.isInteger(result) ? result : min | ||
result = result >= min ? result : min | ||
return result | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
198775
33
2107
2
40