@pictogrammers/components 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/README.md +11 -12
  2. package/favicon.svg +20 -0
  3. package/index.html +67 -49
  4. package/main.js +2 -0
  5. package/main.js.LICENSE.txt +10 -0
  6. package/package.json +6 -6
  7. package/pg/annoy/README.md +0 -5
  8. package/pg/annoy/__examples__/basic/basic.css +3 -0
  9. package/pg/annoy/__examples__/basic/basic.html +4 -7
  10. package/pg/annoy/__examples__/basic/basic.ts +3 -1
  11. package/pg/annoy/annoy.css +29 -198
  12. package/pg/annoy/annoy.html +5 -56
  13. package/pg/annoy/annoy.ts +4 -25
  14. package/pg/app/README.md +11 -0
  15. package/pg/app/__examples__/basic/basic.css +8 -0
  16. package/pg/app/__examples__/basic/basic.html +15 -0
  17. package/pg/app/__examples__/basic/basic.ts +13 -0
  18. package/pg/app/app.css +108 -0
  19. package/pg/app/app.html +16 -0
  20. package/pg/app/app.ts +45 -0
  21. package/pg/app/index.ts +3 -0
  22. package/pg/avatar/__examples__/basic/basic.ts +5 -3
  23. package/pg/button/README.md +3 -0
  24. package/pg/button/button.css +13 -12
  25. package/pg/button/button.spec.ts +35 -0
  26. package/pg/button/button.ts +17 -12
  27. package/pg/buttonLink/buttonLink.css +3 -2
  28. package/pg/buttonMenu/README.md +54 -0
  29. package/pg/buttonMenu/__examples__/basic/basic.html +6 -0
  30. package/pg/buttonMenu/__examples__/basic/basic.ts +43 -0
  31. package/pg/buttonMenu/buttonMenu.css +12 -0
  32. package/pg/buttonMenu/buttonMenu.html +4 -0
  33. package/pg/buttonMenu/buttonMenu.ts +63 -0
  34. package/pg/buttonMenu/index.ts +3 -0
  35. package/pg/buttonToggle/__examples__/basic/basic.ts +2 -2
  36. package/pg/buttonToggle/__examples__/persist/persist.html +10 -0
  37. package/pg/buttonToggle/__examples__/persist/persist.ts +35 -0
  38. package/pg/cardUser/__examples__/basic/basic.ts +0 -1
  39. package/pg/cardUser/cardUser.css +2 -10
  40. package/pg/cardUser/cardUser.html +0 -5
  41. package/pg/cardUser/cardUser.ts +0 -6
  42. package/pg/database/README.md +1 -1
  43. package/pg/database/__examples__/basic/basic.html +2 -1
  44. package/pg/database/__examples__/basic/basic.ts +3 -3
  45. package/pg/dropdown/dropdown.ts +0 -19
  46. package/pg/grid/__examples__/basic/basic.html +2 -2
  47. package/pg/grid/__examples__/basic/basic.ts +3 -2
  48. package/pg/grid/grid.ts +0 -1
  49. package/pg/icon/README.md +6 -5
  50. package/pg/icon/__examples__/basic/basic.html +2 -2
  51. package/pg/icon/__examples__/basic/basic.ts +1 -1
  52. package/pg/icon/icon.ts +6 -0
  53. package/pg/inputCheckList/__examples__/basic/basic.ts +5 -5
  54. package/pg/inputCheckList/inputCheckList.ts +2 -0
  55. package/pg/inputFileLocal/inputFileLocal.css +3 -2
  56. package/pg/inputPixelEditor/README.md +132 -0
  57. package/pg/inputPixelEditor/__examples__/basic/basic.css +29 -0
  58. package/pg/inputPixelEditor/__examples__/basic/basic.html +63 -0
  59. package/pg/inputPixelEditor/__examples__/basic/basic.ts +200 -0
  60. package/pg/inputPixelEditor/__examples__/basic/openUtils.ts +41 -0
  61. package/pg/inputPixelEditor/__examples__/basic/saveUtil.ts +35 -0
  62. package/pg/inputPixelEditor/index.ts +3 -0
  63. package/pg/inputPixelEditor/inputPixelEditor.css +27 -0
  64. package/pg/inputPixelEditor/inputPixelEditor.html +3 -0
  65. package/pg/inputPixelEditor/inputPixelEditor.ts +839 -0
  66. package/pg/inputPixelEditor/utils/bitmapToMask.ts +202 -0
  67. package/pg/inputPixelEditor/utils/cloneGrid.ts +17 -0
  68. package/pg/inputPixelEditor/utils/constants.ts +1 -0
  69. package/pg/inputPixelEditor/utils/createLayer.ts +8 -0
  70. package/pg/inputPixelEditor/utils/debounce.ts +5 -0
  71. package/pg/inputPixelEditor/utils/diffGrid.ts +26 -0
  72. package/pg/inputPixelEditor/utils/fillGrid.ts +11 -0
  73. package/pg/inputPixelEditor/utils/getEllipseOutlinePixels.ts +105 -0
  74. package/pg/inputPixelEditor/utils/getEllipsePixels.ts +28 -0
  75. package/pg/inputPixelEditor/utils/getGuides.ts +232 -0
  76. package/pg/inputPixelEditor/utils/getLinePixels.ts +18 -0
  77. package/pg/inputPixelEditor/utils/getRectangleOutlinePixels.ts +20 -0
  78. package/pg/inputPixelEditor/utils/getRectanglePixels.ts +15 -0
  79. package/pg/inputPixelEditor/utils/inputMode.ts +8 -0
  80. package/pg/inputPixelEditor/utils/interateGrid.ts +7 -0
  81. package/pg/inputPixelEditor/utils/isEmptyGrid.ts +3 -0
  82. package/pg/inputPixelEditor/utils/maskToBitmap.ts +66 -0
  83. package/pg/inputRange/__examples__/basic/basic.ts +4 -4
  84. package/pg/inputRange/inputRange.ts +6 -4
  85. package/pg/inputSelect/README.md +1 -1
  86. package/pg/inputSelect/__examples__/basic/basic.ts +7 -5
  87. package/pg/inputSelect/inputSelect.css +15 -12
  88. package/pg/inputSelect/inputSelect.html +3 -3
  89. package/pg/inputSelect/inputSelect.ts +33 -30
  90. package/pg/inputText/__examples__/basic/basic.ts +6 -6
  91. package/pg/inputText/inputText.css +1 -0
  92. package/pg/inputUserSelect/README.md +1 -1
  93. package/pg/inputUserSelect/inputUserSelect.ts +1 -1
  94. package/pg/listTag/__examples__/basic/basic.ts +4 -5
  95. package/pg/markdown/README.md +17 -3
  96. package/pg/markdown/__examples__/basic/basic.ts +2 -2
  97. package/pg/markdown/__examples__/basic/constants.ts +1 -1
  98. package/pg/markdown/markdown.css +11 -0
  99. package/pg/menu/README.md +46 -0
  100. package/pg/menu/__examples__/basic/basic.html +6 -0
  101. package/pg/menu/__examples__/basic/basic.ts +46 -0
  102. package/pg/menu/index.ts +3 -0
  103. package/pg/menu/menu.css +19 -0
  104. package/pg/menu/menu.html +1 -0
  105. package/pg/menu/menu.ts +119 -0
  106. package/pg/menuDivider/README.md +7 -0
  107. package/pg/menuDivider/__examples__/basic/basic.html +3 -0
  108. package/pg/menuDivider/__examples__/basic/basic.ts +28 -0
  109. package/pg/menuDivider/index.ts +3 -0
  110. package/pg/menuDivider/menuDivider.css +9 -0
  111. package/pg/menuDivider/menuDivider.html +1 -0
  112. package/pg/menuDivider/menuDivider.ts +22 -0
  113. package/pg/menuIcon/menuIcon.ts +43 -36
  114. package/pg/menuItem/README.md +32 -0
  115. package/pg/menuItem/__examples__/basic/basic.html +26 -0
  116. package/pg/menuItem/__examples__/basic/basic.ts +41 -0
  117. package/pg/menuItem/index.ts +3 -0
  118. package/pg/menuItem/menuItem.css +97 -0
  119. package/pg/menuItem/menuItem.html +1 -0
  120. package/pg/menuItem/menuItem.ts +77 -0
  121. package/pg/menuItemIcon/README.md +32 -0
  122. package/pg/menuItemIcon/__examples__/basic/basic.html +34 -0
  123. package/pg/menuItemIcon/__examples__/basic/basic.ts +55 -0
  124. package/pg/menuItemIcon/index.ts +3 -0
  125. package/pg/menuItemIcon/menuItemIcon.css +106 -0
  126. package/pg/menuItemIcon/menuItemIcon.html +4 -0
  127. package/pg/menuItemIcon/menuItemIcon.ts +156 -0
  128. package/pg/modalAlert/__examples__/basic/basic.ts +1 -1
  129. package/pg/modalAlert/modalAlert.css +1 -4
  130. package/pg/modalAlert/modalAlert.ts +18 -4
  131. package/pg/modification/__examples__/basic/basic.ts +1 -2
  132. package/pg/modification/__examples__/basic/constants.ts +25 -50
  133. package/pg/modification/modification.ts +1 -1
  134. package/pg/overlay/overlay.ts +13 -12
  135. package/pg/overlayContextMenu/README.md +35 -0
  136. package/pg/overlayContextMenu/__examples__/basic/basic.css +23 -0
  137. package/pg/overlayContextMenu/__examples__/basic/basic.html +7 -0
  138. package/pg/overlayContextMenu/__examples__/basic/basic.ts +87 -0
  139. package/pg/overlayContextMenu/index.ts +3 -0
  140. package/pg/overlayContextMenu/overlayContextMenu.css +16 -0
  141. package/pg/overlayContextMenu/overlayContextMenu.html +3 -0
  142. package/pg/overlayContextMenu/overlayContextMenu.ts +98 -0
  143. package/pg/overlayMenu/README.md +33 -0
  144. package/pg/overlayMenu/__examples__/basic/basic.css +3 -0
  145. package/pg/overlayMenu/__examples__/basic/basic.html +5 -0
  146. package/pg/overlayMenu/__examples__/basic/basic.ts +62 -0
  147. package/pg/overlayMenu/index.ts +3 -0
  148. package/pg/overlayMenu/overlayMenu.css +16 -0
  149. package/pg/overlayMenu/overlayMenu.html +3 -0
  150. package/pg/overlayMenu/overlayMenu.ts +67 -0
  151. package/pg/overlaySelectMenu/README.md +33 -0
  152. package/pg/overlaySelectMenu/__examples__/basic/basic.css +3 -0
  153. package/pg/overlaySelectMenu/__examples__/basic/basic.html +5 -0
  154. package/pg/overlaySelectMenu/__examples__/basic/basic.ts +62 -0
  155. package/pg/overlaySelectMenu/index.ts +3 -0
  156. package/pg/overlaySelectMenu/overlaySelectMenu.css +17 -0
  157. package/pg/overlaySelectMenu/overlaySelectMenu.html +3 -0
  158. package/pg/overlaySelectMenu/overlaySelectMenu.ts +96 -0
  159. package/pg/overlaySubMenu/README.md +35 -0
  160. package/pg/overlaySubMenu/index.ts +3 -0
  161. package/pg/overlaySubMenu/overlaySubMenu.css +27 -0
  162. package/pg/overlaySubMenu/overlaySubMenu.html +3 -0
  163. package/pg/overlaySubMenu/overlaySubMenu.ts +103 -0
  164. package/pg/picker/picker.ts +1 -19
  165. package/pg/scroll/__examples__/basic/basic.ts +1 -1
  166. package/pg/search/__examples__/basic/basic.ts +10 -7
  167. package/pg/search/search.css +2 -2
  168. package/pg/shared/models/user.ts +0 -2
  169. package/pg/tab/tab.ts +0 -10
  170. package/pg/tabs/partials/tab.css +42 -0
  171. package/pg/tabs/partials/tab.ts +70 -0
  172. package/pg/tabs/tabs.css +0 -53
  173. package/pg/tabs/tabs.ts +54 -70
  174. package/pg/toast/README.md +35 -6
  175. package/pg/toast/__examples__/basic/basic.html +7 -0
  176. package/pg/toast/__examples__/basic/basic.ts +76 -0
  177. package/pg/toast/toast.css +3 -0
  178. package/pg/toast/toast.ts +20 -4
  179. package/pg/tooltip/addTooltip.ts +3 -1
  180. package/pg/tooltip/tooltip.ts +1 -1
  181. package/pg/tree/README.md +67 -0
  182. package/pg/tree/__examples__/basic/basic.html +10 -0
  183. package/pg/tree/__examples__/basic/basic.ts +162 -0
  184. package/pg/tree/index.ts +3 -0
  185. package/pg/tree/tree.css +28 -0
  186. package/pg/tree/tree.html +1 -0
  187. package/pg/tree/tree.ts +217 -0
  188. package/pg/treeButtonIcon/README.md +39 -0
  189. package/pg/treeButtonIcon/index.ts +3 -0
  190. package/pg/treeButtonIcon/treeButtonIcon.css +18 -0
  191. package/pg/treeButtonIcon/treeButtonIcon.html +3 -0
  192. package/pg/treeButtonIcon/treeButtonIcon.ts +42 -0
  193. package/pg/treeItem/README.md +3 -0
  194. package/pg/treeItem/index.ts +3 -0
  195. package/pg/treeItem/treeItem.css +263 -0
  196. package/pg/treeItem/treeItem.html +16 -0
  197. package/pg/treeItem/treeItem.ts +558 -0
  198. package/pgAnnoy.js +1 -0
  199. package/pgApp.js +1 -0
  200. package/pgAvatar.js +1 -0
  201. package/pgButton.js +1 -0
  202. package/pgButtonGroup.js +1 -0
  203. package/pgButtonLink.js +1 -0
  204. package/pgButtonMenu.js +1 -0
  205. package/pgButtonToggle.js +1 -0
  206. package/pgCard.js +1 -0
  207. package/pgCardUser.js +1 -0
  208. package/pgColor.js +1 -0
  209. package/pgDatabase.js +1 -0
  210. package/pgDropdown.js +1 -0
  211. package/pgGrid.js +1 -0
  212. package/pgHeader.js +1 -0
  213. package/pgIcon.js +1 -0
  214. package/pgInputCheck.js +1 -0
  215. package/pgInputCheckList.js +1 -0
  216. package/pgInputFileLocal.js +1 -0
  217. package/pgInputHexRgb.js +1 -0
  218. package/pgInputPixelEditor.js +1 -0
  219. package/pgInputRange.js +1 -0
  220. package/pgInputSelect.js +1 -0
  221. package/pgInputText.js +1 -0
  222. package/pgInputTextIcon.js +1 -0
  223. package/pgInputUserSelect.js +1 -0
  224. package/pgListTag.js +1 -0
  225. package/pgMarkdown.js +2 -0
  226. package/pgMarkdown.js.LICENSE.txt +10 -0
  227. package/pgMenu.js +1 -0
  228. package/pgMenuDivider.js +1 -0
  229. package/pgMenuIcon.js +1 -0
  230. package/pgMenuItem.js +1 -0
  231. package/pgMenuItemIcon.js +1 -0
  232. package/pgModalAlert.js +1 -0
  233. package/pgModification.js +1 -0
  234. package/pgNav.js +1 -0
  235. package/pgOverlay.js +1 -0
  236. package/pgOverlayContextMenu.js +1 -0
  237. package/pgOverlayMenu.js +1 -0
  238. package/pgOverlaySelectMenu.js +1 -0
  239. package/pgOverlaySubMenu.js +1 -0
  240. package/pgPicker.js +1 -0
  241. package/pgPreview.js +1 -0
  242. package/pgScroll.js +1 -0
  243. package/pgSearch.js +1 -0
  244. package/pgTab.js +1 -0
  245. package/pgTabs.js +1 -0
  246. package/pgToast.js +1 -0
  247. package/pgToasts.js +1 -0
  248. package/pgTooltip.js +1 -0
  249. package/pgTree.js +1 -0
  250. package/pgTreeButtonIcon.js +1 -0
  251. package/pgTreeItem.js +1 -0
  252. package/theme-ui3.css +31 -0
  253. package/@types/css.d.ts +0 -4
  254. package/@types/html.d.ts +0 -4
  255. package/dist/main.js +0 -3819
  256. package/dist/pgAnnoy.js +0 -116
  257. package/dist/pgAvatar.js +0 -136
  258. package/dist/pgButton.js +0 -116
  259. package/dist/pgButtonGroup.js +0 -116
  260. package/dist/pgButtonLink.js +0 -116
  261. package/dist/pgButtonToggle.js +0 -146
  262. package/dist/pgCard.js +0 -116
  263. package/dist/pgCardUser.js +0 -196
  264. package/dist/pgColor.js +0 -136
  265. package/dist/pgDatabase.js +0 -236
  266. package/dist/pgDropdown.js +0 -686
  267. package/dist/pgGrid.js +0 -126
  268. package/dist/pgHeader.js +0 -116
  269. package/dist/pgIcon.js +0 -116
  270. package/dist/pgInputCheck.js +0 -116
  271. package/dist/pgInputCheckList.js +0 -126
  272. package/dist/pgInputFileLocal.js +0 -116
  273. package/dist/pgInputHexRgb.js +0 -126
  274. package/dist/pgInputRange.js +0 -116
  275. package/dist/pgInputSelect.js +0 -116
  276. package/dist/pgInputText.js +0 -116
  277. package/dist/pgInputTextIcon.js +0 -176
  278. package/dist/pgInputUserSelect.js +0 -116
  279. package/dist/pgListTag.js +0 -136
  280. package/dist/pgMarkdown.js +0 -346
  281. package/dist/pgMenuIcon.js +0 -206
  282. package/dist/pgModalAlert.js +0 -126
  283. package/dist/pgModification.js +0 -396
  284. package/dist/pgNav.js +0 -116
  285. package/dist/pgOverlay.js +0 -96
  286. package/dist/pgPicker.js +0 -116
  287. package/dist/pgPreview.js +0 -116
  288. package/dist/pgScroll.js +0 -266
  289. package/dist/pgSearch.js +0 -146
  290. package/dist/pgTab.js +0 -116
  291. package/dist/pgTabs.js +0 -136
  292. package/dist/pgToast.js +0 -136
  293. package/dist/pgToasts.js +0 -136
  294. package/dist/pgTooltip.js +0 -126
@@ -0,0 +1,202 @@
1
+ interface Edge {
2
+ x: number;
3
+ y: number;
4
+ type?: 'H' | 'V';
5
+ next?: Edge;
6
+ }
7
+
8
+ interface Options {
9
+ width?: number;
10
+ height?: number;
11
+ scale?: number;
12
+ offsetX?: number;
13
+ offsetY?: number;
14
+ }
15
+
16
+ export function toIndex(x: number, y: number, width: number) {
17
+ return y * width + x;
18
+ }
19
+
20
+ export default function bitmaskToPath(data: number[] | number[][], options: Options = {}) {
21
+
22
+ let bitmask: number[] | number[][],
23
+ width: number,
24
+ height: number,
25
+ scale = 1,
26
+ offsetX = 0,
27
+ offsetY = 0;
28
+
29
+ if (options.width) {
30
+ bitmask = data;
31
+ width = options.width;
32
+ height = bitmask.length / width;
33
+ if (height % 1 !== 0) {
34
+ throw new Error(`Invalid bitmask width. ${height} = ${bitmask.length} / ${width}`);
35
+ }
36
+ } else if (data[0] instanceof Array) {
37
+ bitmask = data.flat();
38
+ width = data[0].length;
39
+ height = data.length;
40
+ } else {
41
+ throw new Error(`options.width is required for 1 dimensional array.`)
42
+ }
43
+
44
+ if (options.scale) {
45
+ scale = options.scale;
46
+ }
47
+
48
+ if (options.offsetX) {
49
+ offsetX = options.offsetX;
50
+ }
51
+
52
+ if (options.offsetY) {
53
+ offsetY = options.offsetY;
54
+ }
55
+
56
+ // Naively copy into a new bitmask with a border of 1 to make sampling easier (no out of bounds checks)
57
+ const newWidth = width + 2;
58
+ const newHeight = height + 2;
59
+ const bm = Array(newWidth * newHeight).fill(0);
60
+
61
+ // BM is just shifted over (1, 1) for the padding
62
+ function BMXYToIndex(x: number, y: number) {
63
+ return (y + 1) * newWidth + (x + 1);
64
+ }
65
+
66
+ for (let y = 0; y < height; ++y) {
67
+ for (let x = 0; x < width; ++x) {
68
+ bm[BMXYToIndex(x, y)] = bitmask[toIndex(x, y, width)];
69
+ }
70
+ }
71
+
72
+ // Edges data structure has [x, y, nextEdge, group]
73
+ const edgeXCount = width * (height + 1);
74
+ const edgeYCount = (width + 1) * height;
75
+ const edgeCount = edgeXCount + edgeYCount;
76
+
77
+ const edges = Array(edgeCount).fill(0).map(() => ({ x: 0, y: 0, next: undefined })) as Edge[];
78
+ function EdgeXIndex(x: number, y: number) {
79
+ return y * width + x;
80
+ }
81
+ function EdgeYIndex(x: number, y: number) {
82
+ return edgeXCount + y * (width + 1) + x;
83
+ }
84
+
85
+ const groups = new Set<Edge>();
86
+
87
+ function SetEdge(edge: Edge, x: number, y: number) {
88
+ edge.x = x;
89
+ edge.y = y;
90
+ groups.add(edge);
91
+ }
92
+
93
+ function UnionGroup(edge: Edge) {
94
+ for (var itr = edge.next; itr !== undefined && itr !== edge; itr = itr.next) {
95
+ groups.delete(itr);
96
+ }
97
+ if (itr !== undefined) {
98
+ groups.add(edge);
99
+ }
100
+ }
101
+
102
+ for (let y = 0; y < height; ++y) {
103
+ for (let x = 0; x < width; ++x) {
104
+ if (bm[BMXYToIndex(x, y)] == 1) {
105
+ const left = bm[BMXYToIndex(x - 1, y)];
106
+ if (left == 0) {
107
+ const edge = edges[EdgeYIndex(x, y)];
108
+ SetEdge(edge, x, y + 1);
109
+ if (bm[BMXYToIndex(x - 1, y - 1)]) {
110
+ edge.next = edges[EdgeXIndex(x - 1, y)];
111
+ } else if (bm[BMXYToIndex(x, y - 1)]) {
112
+ edge.next = edges[EdgeYIndex(x, y - 1)];
113
+ } else {
114
+ edge.next = edges[EdgeXIndex(x, y)];
115
+ }
116
+ UnionGroup(edge);
117
+ }
118
+ const right = bm[BMXYToIndex(x + 1, y)];
119
+ if (right == 0) {
120
+ const edge = edges[EdgeYIndex(x + 1, y)];
121
+ SetEdge(edge, x + 1, y);
122
+ if (bm[BMXYToIndex(x + 1, y + 1)]) {
123
+ edge.next = edges[EdgeXIndex(x + 1, y + 1)];
124
+ } else if (bm[BMXYToIndex(x, y + 1)]) {
125
+ edge.next = edges[EdgeYIndex(x + 1, y + 1)];
126
+ } else {
127
+ edge.next = edges[EdgeXIndex(x, y + 1)];
128
+ }
129
+ UnionGroup(edge);
130
+ }
131
+ const top = bm[BMXYToIndex(x, y - 1)];
132
+ if (top == 0) {
133
+ const edge: Edge = edges[EdgeXIndex(x, y)];
134
+ SetEdge(edge, x, y);
135
+ if (bm[BMXYToIndex(x + 1, y - 1)]) {
136
+ edge.next = edges[EdgeYIndex(x + 1, y - 1)];
137
+ } else if (bm[BMXYToIndex(x + 1, y)]) {
138
+ edge.next = edges[EdgeXIndex(x + 1, y)];
139
+ } else {
140
+ edge.next = edges[EdgeYIndex(x + 1, y)];
141
+ }
142
+ UnionGroup(edge);
143
+ }
144
+ const bottom = bm[BMXYToIndex(x, y + 1)];
145
+ if (bottom == 0) {
146
+ const edge = edges[EdgeXIndex(x, y + 1)];
147
+ SetEdge(edge, x + 1, y + 1);
148
+ if (bm[BMXYToIndex(x - 1, y + 1)]) {
149
+ edge.next = edges[EdgeYIndex(x, y + 1)];
150
+ } else if (bm[BMXYToIndex(x - 1, y)]) {
151
+ edge.next = edges[EdgeXIndex(x - 1, y + 1)];
152
+ } else {
153
+ edge.next = edges[EdgeYIndex(x, y)];
154
+ }
155
+ UnionGroup(edge);
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ for (const edge of groups) {
162
+ let itr = edge;
163
+ do {
164
+ if (itr.next) {
165
+ itr.next.type = itr.x == itr?.next?.x ? 'V' : 'H';
166
+ itr = itr.next;
167
+ }
168
+ } while (itr !== edge);
169
+ }
170
+
171
+ // Compress sequences of H and V
172
+ for (let edge of groups) {
173
+ let itr = edge;
174
+ do {
175
+ if (itr.type != itr.next?.type) {
176
+ while (itr.next?.type == itr.next?.next?.type) {
177
+ if (itr.next === edge) {
178
+ groups.delete(edge);
179
+ edge = itr.next.next as Edge;
180
+ groups.add(edge); // Note this will cause it to iterate over this group again, meh.
181
+ }
182
+ itr.next = itr.next?.next;
183
+ }
184
+ }
185
+ itr = itr.next as Edge;
186
+ } while (itr !== edge);
187
+ }
188
+
189
+ let path = '';
190
+ for (const edge of groups) {
191
+ path += `M${edge.x * scale},${edge.y * scale}`;
192
+ for (var itr = edge.next; itr != edge; itr = itr?.next) {
193
+ if (itr?.type == 'H') {
194
+ path += `H${(itr?.x * scale) + offsetX}`;
195
+ } else if (itr?.type == 'V') {
196
+ path += `V${(itr?.y * scale) + offsetY}`;
197
+ }
198
+ }
199
+ path += 'Z';
200
+ }
201
+ return path;
202
+ }
@@ -0,0 +1,17 @@
1
+
2
+ /**
3
+ * Copy array to new array instance.
4
+ *
5
+ * @param data 2d
6
+ * @returns cloned matrix
7
+ */
8
+ export default function cloneGrid(data: number[][]) {
9
+ const newData: number[][] = [];
10
+ for (let iy = 0; iy < data.length; iy++) {
11
+ newData.push([]);
12
+ for (let ix = 0; ix < data[0].length; ix++) {
13
+ newData[iy].push(data[iy][ix]);
14
+ }
15
+ }
16
+ return newData;
17
+ }
@@ -0,0 +1 @@
1
+ export const WHITE = '#FFFFFF';
@@ -0,0 +1,8 @@
1
+ type CanvasArray = [HTMLCanvasElement, CanvasRenderingContext2D];
2
+
3
+ export default function createCanvas(width: number, height: number): CanvasArray {
4
+ const canvas = document.createElement('canvas');
5
+ canvas.width = width;
6
+ canvas.height = height;
7
+ return [canvas, canvas.getContext('2d') as CanvasRenderingContext2D];
8
+ }
@@ -0,0 +1,5 @@
1
+ let timer: NodeJS.Timeout;
2
+ export default function debounce(func: Function, timeout = 300) {
3
+ clearTimeout(timer);
4
+ timer = setTimeout(() => { func(); }, timeout);
5
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Get a difference between 2d arrays.
3
+ *
4
+ * @param oldData Before the change.
5
+ * @param newData After the change.
6
+ * @returns list of changes [[x, y, color], ...]
7
+ */
8
+ export default function diffGrid(oldData: number[][], newData: number[][]): number[][] {
9
+ const changes: [number, number, number, number][] = [];
10
+ // Loop the larger grid
11
+ const oldWidth = oldData[0].length;
12
+ const oldHeight = oldData.length;
13
+ const newWidth = newData[0].length;
14
+ const newHeight = newData.length;
15
+ const width = Math.max(oldWidth, newWidth);
16
+ const height = Math.max(oldHeight, newHeight);
17
+ for (let y = 0; y < height; y++) {
18
+ for (let x = 0; x < width; x++) {
19
+ const newColor = newData && newData[y] && newData[y][x];
20
+ const oldColor = oldData && oldData[y] && oldData[y][x];
21
+ if (newColor === oldColor) { continue; }
22
+ changes.push([x, y, oldColor, newColor]);
23
+ }
24
+ }
25
+ return changes;
26
+ }
@@ -0,0 +1,11 @@
1
+ export default function fillGrid(width: number, height: number): number[][] {
2
+ let arr: number[][] = [];
3
+ for (let y = 0; y < height; y++) {
4
+ const row: number[] = [];
5
+ for (let x = 0; x < width; x++) {
6
+ row.push(0);
7
+ }
8
+ arr.push(row);
9
+ }
10
+ return arr;
11
+ }
@@ -0,0 +1,105 @@
1
+ function circle(x0: number, y0: number, x1: number, y1: number) {
2
+ let r = Math.abs(x0 - x1) / 2;
3
+ console.log('circle', r);
4
+ const xm = Math.min(x0, x1) + r;
5
+ const ym = Math.min(y0, y1) + r;
6
+ let x = -r, y = 0, err = 2 - 2 * r; /* bottom left to top right */
7
+ const pixels: { x: number, y: number }[] = [];
8
+ do {
9
+ pixels.push({ x: xm - x, y: ym + y }); /* I. Quadrant +x +y */
10
+ pixels.push({ x: xm - y, y: ym - x }); /* II. Quadrant -x +y */
11
+ pixels.push({ x: xm + x, y: ym - y }); /* III. Quadrant -x -y */
12
+ pixels.push({ x: xm + y, y: ym + x }); /* IV. Quadrant +x -y */
13
+ r = err;
14
+ if (r <= y) err += ++y * 2 + 1; /* y step */
15
+ if (r > x || err > y) err += ++x * 2 + 1; /* x step */
16
+ } while (x < 0);
17
+ return pixels;
18
+ }
19
+
20
+ export function distance(x: number, y: number, ratio: number): number {
21
+ return Math.sqrt((Math.pow(y * ratio, 2)) + Math.pow(x, 2));
22
+ }
23
+
24
+ function filled(x: number, y: number, radius: number, ratio: number): boolean {
25
+ return distance(x, y, ratio) <= radius;
26
+ }
27
+
28
+ function thinfilled(x: number, y: number, radius: number, ratio: number): boolean {
29
+ return filled(x, y, radius, ratio) && !(
30
+ filled(x + 1, y, radius, ratio) &&
31
+ filled(x - 1, y, radius, ratio) &&
32
+ filled(x, y + 1, radius, ratio) &&
33
+ filled(x, y - 1, radius, ratio)
34
+ );
35
+ }
36
+
37
+ function isFilled(x: number, y: number, width: number, height: number): boolean {
38
+ const bounds = {
39
+ minX: 0,
40
+ maxX: width,
41
+ minY: 0,
42
+ maxY: height,
43
+ };
44
+
45
+ x = -.5 * (bounds.maxX - 2 * (x + .5));
46
+ y = -.5 * (bounds.maxY - 2 * (y + .5));
47
+
48
+ return thinfilled(x, y, (bounds.maxX / 2), bounds.maxX / bounds.maxY);
49
+ }
50
+
51
+ function betterCircle(x0: number, y0: number, x1: number, y1: number) {
52
+ const width = Math.abs(x0 - x1);
53
+ const height = Math.abs(y0 - y1);
54
+ const minX = Math.min(x0, x1);
55
+ const minY = Math.min(y0, y1);
56
+
57
+ const pixels: { x: number, y: number }[] = [];
58
+ for (let y = 0; y < height; y++) {
59
+ for (let x = 0; x < width; x++) {
60
+ if (isFilled(x, y, width, height)) {
61
+ pixels.push({ x: x + minX, y: y + minY });
62
+ }
63
+ }
64
+ }
65
+ return pixels;
66
+ }
67
+
68
+ function ellipse(x0: number, y0: number, x1: number, y1: number) {
69
+ const pixels: { x: number, y: number }[] = [];
70
+ var a = Math.abs(x1 - x0), b = Math.abs(y1 - y0), b1 = b & 1; /* diameter */
71
+ var dx = 4 * (1.0 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */
72
+ var err = dx + dy + b1 * a * a, e2; /* error of 1.step */
73
+
74
+ if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */
75
+ if (y0 > y1) y0 = y1; /* .. exchange them */
76
+ y0 += (b + 1) >> 1; y1 = y0 - b1; /* starting pixel */
77
+ a = 8 * a * a; b1 = 8 * b * b;
78
+
79
+ do {
80
+ pixels.push({ x: x1, y: y0 }); /* I. Quadrant */
81
+ pixels.push({ x: x0, y: y0 }); /* II. Quadrant */
82
+ pixels.push({ x: x0, y: y1 }); /* III. Quadrant */
83
+ pixels.push({ x: x1, y: y1 }); /* IV. Quadrant */
84
+ e2 = 2 * err;
85
+ if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
86
+ if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x */
87
+ } while (x0 <= x1);
88
+
89
+ while (y0 - y1 <= b) { /* too early stop of flat ellipses a=1 */
90
+ pixels.push({ x: x0 - 1, y: y0 }); /* -> finish tip of ellipse */
91
+ pixels.push({ x: x1 + 1, y: y0++ });
92
+ pixels.push({ x: x0 - 1, y: y1 });
93
+ pixels.push({ x: x1 + 1, y: y1-- });
94
+ }
95
+
96
+ return pixels;
97
+ }
98
+
99
+ export default function getEllipseOutlinePixels(x0: number, y0: number, x1: number, y1: number) {
100
+ if (Math.abs(x0 - x1) === Math.abs(y0 - y1) && Math.abs(x0 - x1)) {
101
+ console.log('circle', Math.abs(x0 - x1), Math.abs(y0 - y1))
102
+ return betterCircle(x0, y0, x1 + 1, y1 + 1);
103
+ }
104
+ return ellipse(x0, y0, x1, y1);
105
+ }
@@ -0,0 +1,28 @@
1
+ export default function getEllipsePixels(x0: number, y0: number, x1: number, y1: number) {
2
+ let a = Math.abs(x1 - x0), b = Math.abs(y1 - y0), b1 = b & 1; /* values of diameter */
3
+ let dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */
4
+ let err = dx + dy + b1 * a * a, e2; /* error of 1.step */
5
+
6
+ if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */
7
+ if (y0 > y1) y0 = y1; /* .. exchange them */
8
+ y0 += (b + 1) / 2; y1 = y0 - b1; /* starting pixel */
9
+ a *= 8 * a; b1 = 8 * b * b;
10
+ const pixels: { x: number, y: number }[] = [];
11
+ do {
12
+ pixels.push({ x: x1, y: y0 }); /* I. Quadrant */
13
+ pixels.push({ x: x0, y: y0 }); /* II. Quadrant */
14
+ pixels.push({ x: x0, y: y1 }); /* III. Quadrant */
15
+ pixels.push({ x: x1, y: y1 }); /* IV. Quadrant */
16
+ e2 = 2 * err;
17
+ if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
18
+ if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */
19
+ } while (x0 <= x1);
20
+
21
+ while (y0 - y1 < b) { /* too early stop of flat ellipses a=1 */
22
+ pixels.push({ x: x0 - 1, y: y0 }); /* -> finish tip of ellipse */
23
+ pixels.push({ x: x1 + 1, y: y0++ });
24
+ pixels.push({ x: x0 - 1, y: y1 });
25
+ pixels.push({ x: x1 + 1, y: y1-- });
26
+ }
27
+ return pixels;
28
+ }
@@ -0,0 +1,232 @@
1
+ type Guide = {
2
+ name: string;
3
+ width?: number;
4
+ height?: number;
5
+ color: string;
6
+ opacity: number;
7
+ lines: number[][];
8
+ dashed?: number[];
9
+ dashOffset?: number;
10
+ }
11
+
12
+ const guides: Guide[] = [
13
+ {
14
+ name: 'Circle Outer',
15
+ width: 22,
16
+ height: 22,
17
+ color: '#F00',
18
+ opacity: 1.0,
19
+ lines: [
20
+ [7, 1],
21
+ [15, 1],
22
+ [15, 2],
23
+ [17, 2],
24
+ [17, 3],
25
+ [18, 3],
26
+ [18, 4],
27
+ [19, 4],
28
+ [19, 5],
29
+ [20, 5],
30
+ [20, 7],
31
+ [21, 7],
32
+ [21, 15],
33
+ [20, 15],
34
+ [20, 17],
35
+ [19, 17],
36
+ [19, 18],
37
+ [18, 18],
38
+ [18, 19],
39
+ [17, 19],
40
+ [17, 20],
41
+ [15, 20],
42
+ [15, 21],
43
+ [7, 21],
44
+ [7, 20],
45
+ [5, 20],
46
+ [5, 19],
47
+ [4, 19],
48
+ [4, 18],
49
+ [3, 18],
50
+ [3, 17],
51
+ [2, 17],
52
+ [2, 15],
53
+ [1, 15],
54
+ [1, 7],
55
+ [2, 7],
56
+ [2, 5],
57
+ [3, 5],
58
+ [3, 4],
59
+ [4, 4],
60
+ [4, 3],
61
+ [5, 3],
62
+ [5, 2],
63
+ [7, 2],
64
+ [7, 1]
65
+ ]
66
+ },
67
+ {
68
+ name: 'Circle Inner',
69
+ width: 22,
70
+ height: 22,
71
+ color: '#00F',
72
+ opacity: 1.0,
73
+ lines: [
74
+ [8, 3],
75
+ [14, 3],
76
+ [14, 4],
77
+ [16, 4],
78
+ [16, 5],
79
+ [17, 5],
80
+ [17, 6],
81
+ [18, 6],
82
+ [18, 8],
83
+ [19, 8],
84
+ [19, 14],
85
+ [18, 14],
86
+ [18, 16],
87
+ [17, 16],
88
+ [17, 17],
89
+ [16, 17],
90
+ [16, 18],
91
+ [14, 18],
92
+ [14, 19],
93
+ [8, 19],
94
+ [8, 18],
95
+ [6, 18],
96
+ [6, 17],
97
+ [5, 17],
98
+ [5, 16],
99
+ [4, 16],
100
+ [4, 14],
101
+ [3, 14],
102
+ [3, 8],
103
+ [4, 8],
104
+ [4, 6],
105
+ [5, 6],
106
+ [5, 5],
107
+ [6, 5],
108
+ [6, 4],
109
+ [8, 4],
110
+ [8, 3]
111
+ ]
112
+ },
113
+ {
114
+ name: 'Square',
115
+ width: 22,
116
+ height: 22,
117
+ color: '#9932cc',
118
+ opacity: 1.0,
119
+ dashed: [4, 4],
120
+ lines: [
121
+ [2, 2],
122
+ [20, 2],
123
+ [20, 20],
124
+ [2, 20],
125
+ [2, 2]
126
+ ]
127
+ },
128
+ {
129
+ name: 'Square',
130
+ width: 22,
131
+ height: 22,
132
+ color: '#9932cc',
133
+ opacity: 0.1,
134
+ dashed: [4, 4],
135
+ dashOffset: 4,
136
+ lines: [
137
+ [2, 2],
138
+ [20, 2],
139
+ [20, 20],
140
+ [2, 20],
141
+ [2, 2]
142
+ ]
143
+ }
144
+ ];
145
+
146
+ const cache = new Map();
147
+
148
+ export function clearGuides() {
149
+ cache.clear();
150
+ }
151
+
152
+ /**
153
+ * This should generate an image and cache it!!!!!!!!!!!!!!!!!!!!!
154
+ *
155
+ * @param width Width
156
+ * @param height Height
157
+ * @returns ctx cache
158
+ */
159
+ export function getGuides(width: number, height: number, size: number, gridSize: number): CanvasImageSource {
160
+ const cacheKey = `${width}:${height}:${size}:${gridSize}`;
161
+ if (cache.has(cacheKey)) {
162
+ return cache.get(cacheKey);
163
+ }
164
+ let filteredGuides = guides.filter(g => {
165
+ return g.width === width && g.height === height;
166
+ });
167
+ // Guides
168
+ if (filteredGuides.length === 0) {
169
+ // Always render center lines for even sizes
170
+ if (width % 2 === 0 && height % 2 === 0) {
171
+ filteredGuides = [
172
+ {
173
+ name: 'Horizontal',
174
+ color: '#00F',
175
+ opacity: 1.0,
176
+ lines: [
177
+ [0, height / 2],
178
+ [width, height / 2]
179
+ ]
180
+ },
181
+ {
182
+ name: 'Vertical',
183
+ color: '#00F',
184
+ opacity: 1.0,
185
+ lines: [
186
+ [width / 2, 0],
187
+ [width / 2, height]
188
+ ]
189
+ }
190
+ ]
191
+ }
192
+ }
193
+ // Canvas Size
194
+ const totalSize = size + gridSize;
195
+ const actualWidth = ((width * totalSize) - gridSize);
196
+ const actualHeight = ((height * totalSize) - gridSize);
197
+ // Cache grid to an image
198
+ const canvas = document.createElement('canvas');
199
+ const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
200
+ canvas.width = actualWidth;
201
+ canvas.height = actualHeight;
202
+ // Grid
203
+ if (gridSize !== 0) {
204
+ ctx.fillStyle = '#BBB';
205
+ for (let x = 1; x < width; x++) {
206
+ ctx.fillRect(x * totalSize - gridSize, 0, 1, actualHeight);
207
+ }
208
+ for (let y = 1; y < height; y++) {
209
+ ctx.fillRect(0, y * totalSize - gridSize, actualWidth, 1);
210
+ }
211
+ }
212
+ // Guides
213
+ filteredGuides.forEach(guide => {
214
+ ctx.lineDashOffset = guide.dashOffset || 0;
215
+ ctx.setLineDash(guide.dashed || [1]);
216
+ ctx.strokeStyle = guide.color;
217
+ ctx.globalAlpha = guide.opacity;
218
+ ctx.lineWidth = 1;
219
+ ctx.fillStyle = 'transparent';
220
+ ctx.beginPath();
221
+ guide.lines.forEach((coordinates, i) => {
222
+ if (i === 0) {
223
+ ctx.moveTo(coordinates[0] * (size + gridSize) - 0.5, coordinates[1] * (size + gridSize) - 0.5);
224
+ } else {
225
+ ctx.lineTo(coordinates[0] * (size + gridSize) - 0.5, coordinates[1] * (size + gridSize) - 0.5);
226
+ }
227
+ });
228
+ ctx.stroke();
229
+ });
230
+ cache.set(cacheKey, canvas);
231
+ return canvas;
232
+ }
@@ -0,0 +1,18 @@
1
+ export default function getLinePixels(x0: number, y0: number, x1: number, y1: number) {
2
+ const pixels: { x: number, y: number }[] = [];
3
+ const dx = Math.abs(x1 - x0);
4
+ const dy = Math.abs(y1 - y0);
5
+ const sx = (x0 < x1) ? 1 : -1;
6
+ const sy = (y0 < y1) ? 1 : -1;
7
+ let err = dx - dy;
8
+
9
+ while (true) {
10
+ pixels.push({x: x0, y: y0 });
11
+
12
+ if ((x0 === x1) && (y0 === y1)) break;
13
+ var e2 = 2 * err;
14
+ if (e2 > -dy) { err -= dy; x0 += sx; }
15
+ if (e2 < dx) { err += dx; y0 += sy; }
16
+ }
17
+ return pixels;
18
+ }
@@ -0,0 +1,20 @@
1
+ export default function getRectangleOutlinePixels(x0: number, y0: number, x1: number, y1: number) {
2
+ const pixels: { x: number, y: number }[] = [];
3
+ const oX = Math.min(x0, x1);
4
+ const oY = Math.min(y0, y1);
5
+ var w = Math.abs(x1 - x0);
6
+ var h = Math.abs(y1 - y0);
7
+
8
+ // Vertical
9
+ for (var y = oY; y <= oY + h; y++) {
10
+ pixels.push({ x: oX, y });
11
+ pixels.push({ x: oX + w, y });
12
+ }
13
+ // Horizontal (minus the vertical squares)
14
+ for (var x = oX + 1; x <= oX + w - 1; x++) {
15
+ pixels.push({ x, y: oY });
16
+ pixels.push({ x, y: oY + h });
17
+ }
18
+
19
+ return pixels;
20
+ }
@@ -0,0 +1,15 @@
1
+ export default function getRectanglePixels(x0: number, y0: number, x1: number, y1: number) {
2
+ const pixels: { x: number, y: number }[] = [];
3
+ const oX = Math.min(x0, x1);
4
+ const oY = Math.min(y0, y1);
5
+ var w = Math.abs(x1 - x0) + 1;
6
+ var h = Math.abs(y1 - y0) + 1;
7
+
8
+ for (var y = oY; y < oY + h; y++) {
9
+ for (var x = oX; x < oX + w; x++) {
10
+ pixels.push({ x, y });
11
+ }
12
+ }
13
+
14
+ return pixels;
15
+ }