js-draw 0.1.9 → 0.1.12

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 (193) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +15 -3
  3. package/dist/bundle.js +1 -1
  4. package/dist/src/Editor.d.ts +7 -2
  5. package/dist/src/Editor.js +74 -25
  6. package/dist/src/EditorImage.d.ts +1 -1
  7. package/dist/src/EditorImage.js +2 -2
  8. package/dist/src/Pointer.d.ts +1 -1
  9. package/dist/src/Pointer.js +1 -1
  10. package/dist/src/SVGLoader.d.ts +1 -1
  11. package/dist/src/SVGLoader.js +14 -6
  12. package/dist/src/UndoRedoHistory.js +3 -0
  13. package/dist/src/Viewport.d.ts +8 -25
  14. package/dist/src/Viewport.js +17 -10
  15. package/dist/src/bundle/bundled.d.ts +2 -1
  16. package/dist/src/bundle/bundled.js +2 -1
  17. package/dist/src/commands/Command.d.ts +2 -2
  18. package/dist/src/commands/Command.js +4 -4
  19. package/dist/src/commands/Duplicate.d.ts +1 -1
  20. package/dist/src/commands/Duplicate.js +1 -1
  21. package/dist/src/commands/Erase.d.ts +1 -1
  22. package/dist/src/commands/Erase.js +1 -1
  23. package/dist/src/commands/localization.d.ts +1 -1
  24. package/dist/src/components/AbstractComponent.d.ts +3 -3
  25. package/dist/src/components/AbstractComponent.js +2 -2
  26. package/dist/src/components/SVGGlobalAttributesObject.d.ts +3 -3
  27. package/dist/src/components/SVGGlobalAttributesObject.js +1 -1
  28. package/dist/src/components/Stroke.d.ts +4 -4
  29. package/dist/src/components/Stroke.js +2 -2
  30. package/dist/src/components/Text.d.ts +3 -3
  31. package/dist/src/components/Text.js +3 -3
  32. package/dist/src/components/UnknownSVGObject.d.ts +3 -3
  33. package/dist/src/components/UnknownSVGObject.js +1 -1
  34. package/dist/src/components/builders/ArrowBuilder.d.ts +1 -1
  35. package/dist/src/components/builders/ArrowBuilder.js +1 -1
  36. package/dist/src/components/builders/FreehandLineBuilder.d.ts +8 -3
  37. package/dist/src/components/builders/FreehandLineBuilder.js +142 -71
  38. package/dist/src/components/builders/LineBuilder.d.ts +1 -1
  39. package/dist/src/components/builders/LineBuilder.js +1 -1
  40. package/dist/src/components/builders/RectangleBuilder.d.ts +1 -1
  41. package/dist/src/components/builders/RectangleBuilder.js +3 -3
  42. package/dist/src/components/builders/types.d.ts +1 -1
  43. package/dist/src/localization.d.ts +1 -0
  44. package/dist/src/localization.js +5 -1
  45. package/dist/src/localizations/en.d.ts +3 -0
  46. package/dist/src/localizations/en.js +4 -0
  47. package/dist/src/localizations/es.d.ts +3 -0
  48. package/dist/src/localizations/es.js +18 -0
  49. package/dist/src/localizations/getLocalizationTable.d.ts +3 -0
  50. package/dist/src/localizations/getLocalizationTable.js +43 -0
  51. package/dist/src/{geometry → math}/LineSegment2.d.ts +1 -0
  52. package/dist/src/{geometry → math}/LineSegment2.js +16 -0
  53. package/dist/src/{geometry → math}/Mat33.d.ts +0 -0
  54. package/dist/src/{geometry → math}/Mat33.js +0 -0
  55. package/dist/src/{geometry → math}/Path.d.ts +2 -1
  56. package/dist/src/{geometry → math}/Path.js +58 -51
  57. package/dist/src/{geometry → math}/Rect2.d.ts +1 -0
  58. package/dist/src/{geometry → math}/Rect2.js +16 -0
  59. package/dist/src/{geometry → math}/Vec2.d.ts +0 -0
  60. package/dist/src/{geometry → math}/Vec2.js +0 -0
  61. package/dist/src/{geometry → math}/Vec3.d.ts +1 -1
  62. package/dist/src/{geometry → math}/Vec3.js +1 -1
  63. package/dist/src/math/rounding.d.ts +3 -0
  64. package/dist/src/math/rounding.js +120 -0
  65. package/dist/src/rendering/Display.d.ts +3 -1
  66. package/dist/src/rendering/Display.js +16 -10
  67. package/dist/src/rendering/caching/CacheRecord.d.ts +2 -2
  68. package/dist/src/rendering/caching/CacheRecord.js +1 -1
  69. package/dist/src/rendering/caching/CacheRecordManager.d.ts +1 -1
  70. package/dist/src/rendering/caching/RenderingCache.js +1 -1
  71. package/dist/src/rendering/caching/RenderingCacheNode.d.ts +2 -1
  72. package/dist/src/rendering/caching/RenderingCacheNode.js +18 -7
  73. package/dist/src/rendering/caching/testUtils.js +1 -1
  74. package/dist/src/rendering/caching/types.d.ts +1 -1
  75. package/dist/src/rendering/localization.d.ts +2 -0
  76. package/dist/src/rendering/localization.js +2 -0
  77. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +4 -4
  78. package/dist/src/rendering/renderers/AbstractRenderer.js +2 -2
  79. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +4 -4
  80. package/dist/src/rendering/renderers/CanvasRenderer.js +1 -1
  81. package/dist/src/rendering/renderers/DummyRenderer.d.ts +4 -4
  82. package/dist/src/rendering/renderers/DummyRenderer.js +1 -1
  83. package/dist/src/rendering/renderers/SVGRenderer.d.ts +3 -3
  84. package/dist/src/rendering/renderers/SVGRenderer.js +8 -2
  85. package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +5 -3
  86. package/dist/src/rendering/renderers/TextOnlyRenderer.js +13 -3
  87. package/dist/src/toolbar/HTMLToolbar.js +5 -7
  88. package/dist/src/toolbar/icons.d.ts +3 -0
  89. package/dist/src/toolbar/icons.js +152 -142
  90. package/dist/src/toolbar/localization.d.ts +4 -1
  91. package/dist/src/toolbar/localization.js +4 -1
  92. package/dist/src/toolbar/makeColorInput.js +2 -1
  93. package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +13 -0
  94. package/dist/src/toolbar/widgets/ActionButtonWidget.js +21 -0
  95. package/dist/src/toolbar/widgets/BaseWidget.js +31 -0
  96. package/dist/src/toolbar/widgets/HandToolWidget.js +10 -3
  97. package/dist/src/toolbar/widgets/SelectionWidget.d.ts +0 -1
  98. package/dist/src/toolbar/widgets/SelectionWidget.js +23 -30
  99. package/dist/src/tools/BaseTool.d.ts +2 -1
  100. package/dist/src/tools/BaseTool.js +3 -0
  101. package/dist/src/tools/Eraser.js +1 -1
  102. package/dist/src/tools/PanZoom.d.ts +3 -2
  103. package/dist/src/tools/PanZoom.js +32 -16
  104. package/dist/src/tools/SelectionTool.d.ts +11 -4
  105. package/dist/src/tools/SelectionTool.js +135 -23
  106. package/dist/src/tools/TextTool.js +1 -1
  107. package/dist/src/tools/ToolController.js +6 -2
  108. package/dist/src/tools/localization.d.ts +1 -0
  109. package/dist/src/tools/localization.js +1 -0
  110. package/dist/src/types.d.ts +13 -6
  111. package/dist/src/types.js +1 -0
  112. package/package.json +9 -1
  113. package/src/Editor.ts +86 -24
  114. package/src/EditorImage.test.ts +2 -4
  115. package/src/EditorImage.ts +2 -2
  116. package/src/Pointer.ts +1 -1
  117. package/src/SVGLoader.ts +14 -6
  118. package/src/UndoRedoHistory.ts +4 -0
  119. package/src/Viewport.ts +21 -17
  120. package/src/bundle/bundled.ts +2 -1
  121. package/src/commands/Command.ts +5 -5
  122. package/src/commands/Duplicate.ts +1 -1
  123. package/src/commands/Erase.ts +1 -1
  124. package/src/commands/localization.ts +1 -1
  125. package/src/components/AbstractComponent.ts +4 -4
  126. package/src/components/SVGGlobalAttributesObject.ts +3 -3
  127. package/src/components/Stroke.test.ts +3 -5
  128. package/src/components/Stroke.ts +4 -4
  129. package/src/components/Text.test.ts +2 -2
  130. package/src/components/Text.ts +3 -3
  131. package/src/components/UnknownSVGObject.ts +3 -3
  132. package/src/components/builders/ArrowBuilder.ts +2 -2
  133. package/src/components/builders/FreehandLineBuilder.ts +190 -80
  134. package/src/components/builders/LineBuilder.ts +2 -2
  135. package/src/components/builders/RectangleBuilder.ts +3 -3
  136. package/src/components/builders/types.ts +1 -1
  137. package/src/localization.ts +6 -0
  138. package/src/localizations/en.ts +8 -0
  139. package/src/localizations/es.ts +63 -0
  140. package/src/localizations/getLocalizationTable.test.ts +27 -0
  141. package/src/localizations/getLocalizationTable.ts +53 -0
  142. package/src/{geometry → math}/LineSegment2.test.ts +15 -0
  143. package/src/{geometry → math}/LineSegment2.ts +20 -0
  144. package/src/{geometry → math}/Mat33.test.ts +0 -0
  145. package/src/{geometry → math}/Mat33.ts +0 -0
  146. package/src/{geometry → math}/Path.fromString.test.ts +0 -0
  147. package/src/{geometry → math}/Path.test.ts +0 -0
  148. package/src/{geometry → math}/Path.toString.test.ts +11 -2
  149. package/src/{geometry → math}/Path.ts +60 -57
  150. package/src/{geometry → math}/Rect2.test.ts +20 -7
  151. package/src/{geometry → math}/Rect2.ts +19 -1
  152. package/src/{geometry → math}/Vec2.test.ts +0 -0
  153. package/src/{geometry → math}/Vec2.ts +0 -0
  154. package/src/{geometry → math}/Vec3.test.ts +0 -0
  155. package/src/{geometry → math}/Vec3.ts +2 -2
  156. package/src/math/rounding.test.ts +40 -0
  157. package/src/math/rounding.ts +145 -0
  158. package/src/rendering/Display.ts +18 -10
  159. package/src/rendering/caching/CacheRecord.test.ts +2 -2
  160. package/src/rendering/caching/CacheRecord.ts +2 -2
  161. package/src/rendering/caching/CacheRecordManager.ts +1 -1
  162. package/src/rendering/caching/RenderingCache.test.ts +3 -3
  163. package/src/rendering/caching/RenderingCache.ts +1 -1
  164. package/src/rendering/caching/RenderingCacheNode.ts +23 -7
  165. package/src/rendering/caching/testUtils.ts +1 -1
  166. package/src/rendering/caching/types.ts +1 -1
  167. package/src/rendering/localization.ts +4 -0
  168. package/src/rendering/renderers/AbstractRenderer.ts +4 -4
  169. package/src/rendering/renderers/CanvasRenderer.ts +4 -4
  170. package/src/rendering/renderers/DummyRenderer.test.ts +2 -2
  171. package/src/rendering/renderers/DummyRenderer.ts +4 -4
  172. package/src/rendering/renderers/SVGRenderer.ts +10 -4
  173. package/src/rendering/renderers/TextOnlyRenderer.ts +17 -6
  174. package/src/toolbar/HTMLToolbar.ts +5 -8
  175. package/src/toolbar/icons.ts +167 -147
  176. package/src/toolbar/localization.ts +8 -2
  177. package/src/toolbar/makeColorInput.ts +2 -1
  178. package/src/toolbar/toolbar.css +7 -3
  179. package/src/toolbar/widgets/ActionButtonWidget.ts +31 -0
  180. package/src/toolbar/widgets/BaseWidget.ts +36 -0
  181. package/src/toolbar/widgets/HandToolWidget.ts +14 -3
  182. package/src/toolbar/widgets/SelectionWidget.ts +46 -41
  183. package/src/tools/BaseTool.ts +5 -1
  184. package/src/tools/Eraser.ts +2 -2
  185. package/src/tools/PanZoom.ts +39 -18
  186. package/src/tools/SelectionTool.test.ts +26 -5
  187. package/src/tools/SelectionTool.ts +162 -27
  188. package/src/tools/TextTool.ts +2 -2
  189. package/src/tools/ToolController.ts +6 -2
  190. package/src/tools/UndoRedoShortcut.test.ts +1 -1
  191. package/src/tools/localization.ts +2 -0
  192. package/src/types.ts +14 -5
  193. package/dist-test/test-dist-bundle.html +0 -42
@@ -3,7 +3,7 @@
3
3
 
4
4
  import Color4 from '../../Color4';
5
5
  import { ImageNode, sortLeavesByZIndex } from '../../EditorImage';
6
- import Rect2 from '../../geometry/Rect2';
6
+ import Rect2 from '../../math/Rect2';
7
7
  import Viewport from '../../Viewport';
8
8
  import AbstractRenderer from '../renderers/AbstractRenderer';
9
9
  import CacheRecord from './CacheRecord';
@@ -151,12 +151,15 @@ export default class RenderingCacheNode {
151
151
  return result;
152
152
  }
153
153
 
154
- private renderingIsUpToDate(sortedIds: number[]) {
155
- if (this.cachedRenderer === null || sortedIds.length !== this.renderedIds.length) {
154
+ // Returns true iff all elems of this.renderedIds are in sortedIds.
155
+ // sortedIds should be sorted by z-index (or some other order, so long as they are
156
+ // sorted by the same thing as this.renderedIds.)
157
+ private allRenderedIdsIn(sortedIds: number[]) {
158
+ if (this.renderedIds.length > sortedIds.length) {
156
159
  return false;
157
160
  }
158
161
 
159
- for (let i = 0; i < sortedIds.length; i++) {
162
+ for (let i = 0; i < this.renderedIds.length; i++) {
160
163
  if (sortedIds[i] !== this.renderedIds[i]) {
161
164
  return false;
162
165
  }
@@ -165,6 +168,14 @@ export default class RenderingCacheNode {
165
168
  return true;
166
169
  }
167
170
 
171
+ private renderingIsUpToDate(sortedIds: number[]) {
172
+ if (this.cachedRenderer === null || sortedIds.length !== this.renderedIds.length) {
173
+ return false;
174
+ }
175
+
176
+ return this.allRenderedIdsIn(sortedIds);
177
+ }
178
+
168
179
  // Render all [items] within [viewport]
169
180
  public renderItems(screenRenderer: AbstractRenderer, items: ImageNode[], viewport: Viewport) {
170
181
  if (
@@ -238,8 +249,7 @@ export default class RenderingCacheNode {
238
249
  }
239
250
 
240
251
  // Is it worth it to render the items?
241
- // TODO: Replace this with something performace based.
242
- // TODO: Determine whether it is 'worth it' to cache this depending on rendering time.
252
+ // TODO: Consider replacing this with something performace based.
243
253
  if (leavesByIds.length > this.cacheState.props.minComponentsPerCache) {
244
254
  let fullRerenderNeeded = true;
245
255
  if (!this.cachedRenderer) {
@@ -247,7 +257,7 @@ export default class RenderingCacheNode {
247
257
  this.region,
248
258
  () => this.onRegionDealloc()
249
259
  );
250
- } else if (leavesByIds.length > this.renderedIds.length && this.renderedMaxZIndex !== null) {
260
+ } else if (leavesByIds.length > this.renderedIds.length && this.allRenderedIdsIn(leafIds) && this.renderedMaxZIndex !== null) {
251
261
  // We often don't need to do a full re-render even if something's changed.
252
262
  // Check whether we can just draw on top of the existing cache.
253
263
  const newLeaves = [];
@@ -287,6 +297,12 @@ export default class RenderingCacheNode {
287
297
  screenRenderer.drawRect(this.region, viewport.getSizeOfPixelOnCanvas(), { fill: Color4.clay });
288
298
  }
289
299
  }
300
+ } else if (debugMode) {
301
+ console.log('Decided on a full re-render. Reason: At least one of the following is false:',
302
+ '\n leafIds.length > this.renderedIds.length: ', leafIds.length > this.renderedIds.length,
303
+ '\n this.allRenderedIdsIn(leafIds): ', this.allRenderedIdsIn(leafIds),
304
+ '\n this.renderedMaxZIndex !== null: ', this.renderedMaxZIndex !== null,
305
+ '\n\nthis.rerenderedIds: ', this.renderedIds, ', leafIds: ', leafIds);
290
306
  }
291
307
 
292
308
  if (fullRerenderNeeded) {
@@ -1,4 +1,4 @@
1
- import { Vec2 } from '../../geometry/Vec2';
1
+ import { Vec2 } from '../../math/Vec2';
2
2
  import DummyRenderer from '../renderers/DummyRenderer';
3
3
  import createEditor from '../../testing/createEditor';
4
4
  import AbstractRenderer from '../renderers/AbstractRenderer';
@@ -1,4 +1,4 @@
1
- import { Vec2 } from '../../geometry/Vec2';
1
+ import { Vec2 } from '../../math/Vec2';
2
2
  import AbstractRenderer from '../renderers/AbstractRenderer';
3
3
  import { CacheRecordManager } from './CacheRecordManager';
4
4
 
@@ -1,10 +1,14 @@
1
1
 
2
2
  export interface TextRendererLocalization {
3
+ pathNodeCount(pathCount: number): string;
4
+ textNodeCount(nodeCount: number): string;
3
5
  textNode(content: string): string;
4
6
  rerenderAsText: string;
5
7
  }
6
8
 
7
9
  export const defaultTextRendererLocalization: TextRendererLocalization = {
10
+ pathNodeCount: (count: number) => `There are ${count} visible path objects.`,
11
+ textNodeCount: (count: number) => `There are ${count} visible text nodes.`,
8
12
  textNode: (content: string) => `Text: ${content}`,
9
13
  rerenderAsText: 'Re-render as text',
10
14
  };
@@ -1,9 +1,9 @@
1
1
  import { LoadSaveDataTable } from '../../components/AbstractComponent';
2
2
  import { TextStyle } from '../../components/Text';
3
- import Mat33 from '../../geometry/Mat33';
4
- import Path, { PathCommand, PathCommandType } from '../../geometry/Path';
5
- import Rect2 from '../../geometry/Rect2';
6
- import { Point2, Vec2 } from '../../geometry/Vec2';
3
+ import Mat33 from '../../math/Mat33';
4
+ import Path, { PathCommand, PathCommandType } from '../../math/Path';
5
+ import Rect2 from '../../math/Rect2';
6
+ import { Point2, Vec2 } from '../../math/Vec2';
7
7
  import Viewport from '../../Viewport';
8
8
  import RenderingStyle, { stylesEqual } from '../RenderingStyle';
9
9
 
@@ -1,9 +1,9 @@
1
1
  import Color4 from '../../Color4';
2
2
  import Text, { TextStyle } from '../../components/Text';
3
- import Mat33 from '../../geometry/Mat33';
4
- import Rect2 from '../../geometry/Rect2';
5
- import { Point2, Vec2 } from '../../geometry/Vec2';
6
- import Vec3 from '../../geometry/Vec3';
3
+ import Mat33 from '../../math/Mat33';
4
+ import Rect2 from '../../math/Rect2';
5
+ import { Point2, Vec2 } from '../../math/Vec2';
6
+ import Vec3 from '../../math/Vec3';
7
7
  import Viewport from '../../Viewport';
8
8
  import RenderingStyle from '../RenderingStyle';
9
9
  import AbstractRenderer, { RenderablePathSpec } from './AbstractRenderer';
@@ -1,7 +1,7 @@
1
1
 
2
2
  import EventDispatcher from '../../EventDispatcher';
3
- import Mat33 from '../../geometry/Mat33';
4
- import { Vec2 } from '../../geometry/Vec2';
3
+ import Mat33 from '../../math/Mat33';
4
+ import { Vec2 } from '../../math/Vec2';
5
5
  import Viewport from '../../Viewport';
6
6
  import DummyRenderer from './DummyRenderer';
7
7
 
@@ -1,10 +1,10 @@
1
1
  // Renderer that outputs nothing. Useful for automated tests.
2
2
 
3
3
  import { TextStyle } from '../../components/Text';
4
- import Mat33 from '../../geometry/Mat33';
5
- import Rect2 from '../../geometry/Rect2';
6
- import { Point2, Vec2 } from '../../geometry/Vec2';
7
- import Vec3 from '../../geometry/Vec3';
4
+ import Mat33 from '../../math/Mat33';
5
+ import Rect2 from '../../math/Rect2';
6
+ import { Point2, Vec2 } from '../../math/Vec2';
7
+ import Vec3 from '../../math/Vec3';
8
8
  import Viewport from '../../Viewport';
9
9
  import RenderingStyle from '../RenderingStyle';
10
10
  import AbstractRenderer from './AbstractRenderer';
@@ -1,10 +1,11 @@
1
1
 
2
2
  import { LoadSaveDataTable } from '../../components/AbstractComponent';
3
3
  import { TextStyle } from '../../components/Text';
4
- import Mat33 from '../../geometry/Mat33';
5
- import Path, { PathCommand, PathCommandType } from '../../geometry/Path';
6
- import Rect2 from '../../geometry/Rect2';
7
- import { Point2, Vec2 } from '../../geometry/Vec2';
4
+ import Mat33 from '../../math/Mat33';
5
+ import Path, { PathCommand, PathCommandType } from '../../math/Path';
6
+ import Rect2 from '../../math/Rect2';
7
+ import { toRoundedString } from '../../math/rounding';
8
+ import { Point2, Vec2 } from '../../math/Vec2';
8
9
  import { svgAttributesDataKey, SVGLoaderUnknownAttribute, SVGLoaderUnknownStyleAttribute, svgStyleAttributesDataKey } from '../../SVGLoader';
9
10
  import Viewport from '../../Viewport';
10
11
  import RenderingStyle from '../RenderingStyle';
@@ -113,6 +114,9 @@ export default class SVGRenderer extends AbstractRenderer {
113
114
  public drawText(text: string, transform: Mat33, style: TextStyle): void {
114
115
  transform = this.getCanvasToScreenTransform().rightMul(transform);
115
116
 
117
+ const translation = transform.transformVec2(Vec2.zero);
118
+ transform = transform.rightMul(Mat33.translation(translation.times(-1)));
119
+
116
120
  const textElem = document.createElementNS(svgNameSpace, 'text');
117
121
  textElem.appendChild(document.createTextNode(text));
118
122
  textElem.style.transform = `matrix(
@@ -125,6 +129,8 @@ export default class SVGRenderer extends AbstractRenderer {
125
129
  textElem.style.fontWeight = style.fontWeight ?? '';
126
130
  textElem.style.fontSize = style.size + 'px';
127
131
  textElem.style.fill = style.renderingStyle.fill.toHexString();
132
+ textElem.setAttribute('x', `${toRoundedString(translation.x)}`);
133
+ textElem.setAttribute('y', `${toRoundedString(translation.y)}`);
128
134
 
129
135
  if (style.renderingStyle.stroke) {
130
136
  const strokeStyle = style.renderingStyle.stroke;
@@ -1,8 +1,8 @@
1
1
  import { TextStyle } from '../../components/Text';
2
- import Mat33 from '../../geometry/Mat33';
3
- import Rect2 from '../../geometry/Rect2';
4
- import { Vec2 } from '../../geometry/Vec2';
5
- import Vec3 from '../../geometry/Vec3';
2
+ import Mat33 from '../../math/Mat33';
3
+ import Rect2 from '../../math/Rect2';
4
+ import { Vec2 } from '../../math/Vec2';
5
+ import Vec3 from '../../math/Vec3';
6
6
  import Viewport from '../../Viewport';
7
7
  import { TextRendererLocalization } from '../localization';
8
8
  import RenderingStyle from '../RenderingStyle';
@@ -12,6 +12,9 @@ import AbstractRenderer from './AbstractRenderer';
12
12
 
13
13
  export default class TextOnlyRenderer extends AbstractRenderer {
14
14
  private descriptionBuilder: string[] = [];
15
+ private pathCount: number = 0;
16
+ private textNodeCount: number = 0;
17
+
15
18
  public constructor(viewport: Viewport, private localizationTable: TextRendererLocalization) {
16
19
  super(viewport);
17
20
  }
@@ -23,15 +26,22 @@ export default class TextOnlyRenderer extends AbstractRenderer {
23
26
 
24
27
  public clear(): void {
25
28
  this.descriptionBuilder = [];
29
+ this.pathCount = 0;
30
+ this.textNodeCount = 0;
26
31
  }
27
32
 
28
33
  public getDescription(): string {
29
- return this.descriptionBuilder.join('\n');
34
+ return [
35
+ this.localizationTable.pathNodeCount(this.pathCount),
36
+ this.localizationTable.textNodeCount(this.textNodeCount),
37
+ ...this.descriptionBuilder
38
+ ].join('\n');
30
39
  }
31
40
 
32
41
  protected beginPath(_startPoint: Vec3): void {
33
42
  }
34
43
  protected endPath(_style: RenderingStyle): void {
44
+ this.pathCount ++;
35
45
  }
36
46
  protected lineTo(_point: Vec3): void {
37
47
  }
@@ -43,9 +53,10 @@ export default class TextOnlyRenderer extends AbstractRenderer {
43
53
  }
44
54
  public drawText(text: string, _transform: Mat33, _style: TextStyle): void {
45
55
  this.descriptionBuilder.push(this.localizationTable.textNode(text));
56
+ this.textNodeCount ++;
46
57
  }
47
58
  public isTooSmallToRender(rect: Rect2): boolean {
48
- return rect.maxDimension < 10 / this.getSizeOfCanvasPixelOnScreen();
59
+ return rect.maxDimension < 15 / this.getSizeOfCanvasPixelOnScreen();
49
60
  }
50
61
  public drawPoints(..._points: Vec3[]): void {
51
62
  }
@@ -138,13 +138,13 @@ export default class HTMLToolbar {
138
138
  undoRedoGroup.classList.add(`${toolbarCSSPrefix}buttonGroup`);
139
139
 
140
140
  const undoButton = this.addActionButton({
141
- label: 'Undo',
141
+ label: this.localizationTable.undo,
142
142
  icon: makeUndoIcon()
143
143
  }, () => {
144
144
  this.editor.history.undo();
145
145
  }, undoRedoGroup);
146
146
  const redoButton = this.addActionButton({
147
- label: 'Redo',
147
+ label: this.localizationTable.redo,
148
148
  icon: makeRedoIcon(),
149
149
  }, () => {
150
150
  this.editor.history.redo();
@@ -200,12 +200,9 @@ export default class HTMLToolbar {
200
200
  (new TextToolWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
201
201
  }
202
202
 
203
- for (const tool of toolController.getMatchingTools(ToolType.PanZoom)) {
204
- if (!(tool instanceof PanZoom)) {
205
- throw new Error('All SelectionTools must have kind === ToolType.PanZoom');
206
- }
207
-
208
- (new HandToolWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
203
+ const panZoomTool = toolController.getMatchingTools(ToolType.PanZoom)[0];
204
+ if (panZoomTool && panZoomTool instanceof PanZoom) {
205
+ (new HandToolWidget(this.editor, panZoomTool, this.localizationTable)).addTo(this.container);
209
206
  }
210
207
 
211
208
  this.setupColorPickers();
@@ -2,18 +2,18 @@ import Color4 from '../Color4';
2
2
  import { ComponentBuilderFactory } from '../components/builders/types';
3
3
  import { TextStyle } from '../components/Text';
4
4
  import EventDispatcher from '../EventDispatcher';
5
- import { Vec2 } from '../geometry/Vec2';
5
+ import { Vec2 } from '../math/Vec2';
6
6
  import SVGRenderer from '../rendering/renderers/SVGRenderer';
7
7
  import Pen from '../tools/Pen';
8
8
  import { StrokeDataPoint } from '../types';
9
9
  import Viewport from '../Viewport';
10
10
 
11
11
  const svgNamespace = 'http://www.w3.org/2000/svg';
12
- const primaryForegroundFill = `
13
- style='fill: var(--primary-foreground-color);'
12
+ const iconColorFill = `
13
+ style='fill: var(--icon-color);'
14
14
  `;
15
- const primaryForegroundStrokeFill = `
16
- style='fill: var(--primary-foreground-color); stroke: var(--primary-foreground-color);'
15
+ const iconColorStrokeFill = `
16
+ style='fill: var(--icon-color); stroke: var(--icon-color);'
17
17
  `;
18
18
  const checkerboardPatternDef = `
19
19
  <pattern
@@ -39,7 +39,7 @@ export const makeRedoIcon = (mirror: boolean = false) => {
39
39
  icon.innerHTML = `
40
40
  <style>
41
41
  .toolbar-svg-undo-redo-icon {
42
- stroke: var(--primary-foreground-color);
42
+ stroke: var(--icon-color);
43
43
  stroke-width: 12;
44
44
  stroke-linejoin: round;
45
45
  stroke-linecap: round;
@@ -63,7 +63,7 @@ export const makeDropdownIcon = () => {
63
63
  <g>
64
64
  <path
65
65
  d='M5,10 L50,90 L95,10 Z'
66
- ${primaryForegroundFill}
66
+ ${iconColorFill}
67
67
  />
68
68
  </g>
69
69
  `;
@@ -80,7 +80,7 @@ export const makeEraserIcon = () => {
80
80
  <rect x=10 y=50 width=80 height=30 rx=10 fill='pink' />
81
81
  <rect
82
82
  x=10 y=10 width=80 height=50
83
- ${primaryForegroundFill}
83
+ ${iconColorFill}
84
84
  />
85
85
  </g>
86
86
  `;
@@ -103,152 +103,141 @@ export const makeSelectionIcon = () => {
103
103
  return icon;
104
104
  };
105
105
 
106
- export const makeHandToolIcon = () => {
106
+ const pathIcon = (
107
+ pathData: string,
108
+ fill: string = 'var(--icon-color)',
109
+ strokeColor: string = 'none',
110
+ strokeWidth: string = '0px',
111
+ ) => {
107
112
  const icon = document.createElementNS(svgNamespace, 'svg');
108
-
109
- // Draw a cursor-like shape (like some of the other icons, made with Inkscape)
110
- icon.innerHTML = `
111
- <g>
112
- <path d='
113
- m 10,60
114
- 5,30
115
- H 90
116
- V 30
117
- C 90,20 75,20 75,30
118
- V 60
119
- 20
120
- C 75,10 60,10 60,20
121
- V 60
122
- 15
123
- C 60,5 45,5 45,15
124
- V 60
125
- 25
126
- C 45,15 30,15 30,25
127
- V 60
128
- 75
129
- L 25,60
130
- C 20,45 10,50 10,60
131
- Z'
132
-
133
- fill='none'
134
- style='
135
- stroke: var(--primary-foreground-color);
136
- stroke-width: 2;
137
- '
138
- />
139
- </g>
140
- `;
113
+ const path = document.createElementNS(svgNamespace, 'path');
114
+ path.setAttribute('d', pathData);
115
+ path.style.fill = fill;
116
+ path.style.stroke = strokeColor;
117
+ path.style.strokeWidth = strokeWidth;
118
+ icon.appendChild(path);
141
119
  icon.setAttribute('viewBox', '0 0 100 100');
120
+
142
121
  return icon;
143
122
  };
144
123
 
124
+ export const makeHandToolIcon = () => {
125
+ const fill = 'none';
126
+ const strokeColor = 'var(--icon-color)';
127
+ const strokeWidth = '3';
128
+
129
+ // Draw a cursor-like shape (like some of the other icons, made with Inkscape)
130
+ return pathIcon(`
131
+ m 10,60
132
+ 5,30
133
+ H 90
134
+ V 30
135
+ C 90,20 75,20 75,30
136
+ V 60
137
+ 20
138
+ C 75,10 60,10 60,20
139
+ V 60
140
+ 15
141
+ C 60,5 45,5 45,15
142
+ V 60
143
+ 25
144
+ C 45,15 30,15 30,25
145
+ V 60
146
+ 75
147
+ L 25,60
148
+ C 20,45 10,50 10,60
149
+ Z
150
+ `, fill, strokeColor, strokeWidth);
151
+ };
152
+
145
153
  export const makeTouchPanningIcon = () => {
146
- const icon = document.createElementNS(svgNamespace, 'svg');
147
- icon.innerHTML = `
148
- <path
149
- d='
150
- M 5,5.5
151
- V 17.2
152
- L 16.25,5.46
153
- Z
154
-
155
- m 33.75,0
156
- L 50,17
157
- V 5.5
158
- Z
159
-
160
- M 5,40.7
161
- v 11.7
162
- h 11.25
163
- z
164
-
165
- M 26,19
166
- C 19.8,19.4 17.65,30.4 21.9,34.8
167
- L 50,70
168
- H 27.5
169
- c -11.25,0 -11.25,17.6 0,17.6
170
- H 61.25
171
- C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
172
- L 33.1,23
173
- C 30.3125,20.128192 27.9,19 25.830078,19.119756
174
- Z
175
- '
176
- fill='none'
177
- style='
178
- stroke: var(--primary-foreground-color);
179
- stroke-width: 2;
180
- '
181
- />
182
- `;
154
+ const fill = 'none';
155
+ const strokeColor = 'var(--icon-color)';
156
+ const strokeWidth = '3';
157
+
158
+ return pathIcon(`
159
+ M 5,5.5
160
+ V 17.2
161
+ L 16.25,5.46
162
+ Z
183
163
 
184
- icon.setAttribute('viewBox', '0 0 100 100');
185
- return icon;
164
+ m 33.75,0
165
+ L 50,17
166
+ V 5.5
167
+ Z
168
+
169
+ M 5,40.7
170
+ v 11.7
171
+ h 11.25
172
+ z
173
+
174
+ M 26,19
175
+ C 19.8,19.4 17.65,30.4 21.9,34.8
176
+ L 50,70
177
+ H 27.5
178
+ c -11.25,0 -11.25,17.6 0,17.6
179
+ H 61.25
180
+ C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
181
+ L 33.1,23
182
+ C 30.3125,20.128192 27.9,19 25.830078,19.119756
183
+ Z
184
+ `, fill, strokeColor, strokeWidth);
186
185
  };
187
186
 
188
187
  export const makeAllDevicePanningIcon = () => {
189
- const icon = document.createElementNS(svgNamespace, 'svg');
190
- icon.innerHTML = `
191
- <path
192
- d='
193
- M 5 5
194
- L 5 17.5
195
- 17.5 5
196
- 5 5
197
- z
198
-
199
- M 42.5 5
200
- L 55 17.5
201
- 55 5
202
- 42.5 5
203
- z
204
-
205
- M 70 10
206
- L 70 21
207
- 61 15
208
- 55.5 23
209
- 66 30
210
- 56 37
211
- 61 45
212
- 70 39
213
- 70 50
214
- 80 50
215
- 80 39
216
- 89 45
217
- 95 36
218
- 84 30
219
- 95 23
220
- 89 15
221
- 80 21
222
- 80 10
223
- 70 10
224
- z
225
-
226
- M 27.5 26.25
227
- L 27.5 91.25
228
- L 43.75 83.125
229
- L 52 99
230
- L 68 91
231
- L 60 75
232
- L 76.25 66.875
233
- L 27.5 26.25
234
- z
235
-
236
- M 5 42.5
237
- L 5 55
238
- L 17.5 55
239
- L 5 42.5
240
- z
241
- '
242
- fill='none'
243
- style='
244
- stroke: var(--primary-foreground-color);
245
- stroke-width: 2;
246
- '
247
- />
248
- `;
249
-
250
- icon.setAttribute('viewBox', '0 0 100 100');
251
- return icon;
188
+ const fill = 'none';
189
+ const strokeColor = 'var(--icon-color)';
190
+ const strokeWidth = '3';
191
+ return pathIcon(`
192
+ M 5 5
193
+ L 5 17.5
194
+ 17.5 5
195
+ 5 5
196
+ z
197
+
198
+ M 42.5 5
199
+ L 55 17.5
200
+ 55 5
201
+ 42.5 5
202
+ z
203
+
204
+ M 70 10
205
+ L 70 21
206
+ 61 15
207
+ 55.5 23
208
+ 66 30
209
+ 56 37
210
+ 61 45
211
+ 70 39
212
+ 70 50
213
+ 80 50
214
+ 80 39
215
+ 89 45
216
+ 95 36
217
+ 84 30
218
+ 95 23
219
+ 89 15
220
+ 80 21
221
+ 80 10
222
+ 70 10
223
+ z
224
+
225
+ M 27.5 26.25
226
+ L 27.5 91.25
227
+ L 43.75 83.125
228
+ L 52 99
229
+ L 68 91
230
+ L 60 75
231
+ L 76.25 66.875
232
+ L 27.5 26.25
233
+ z
234
+
235
+ M 5 42.5
236
+ L 5 55
237
+ L 17.5 55
238
+ L 5 42.5
239
+ z
240
+ `, fill, strokeColor, strokeWidth);
252
241
  };
253
242
 
254
243
  export const makeZoomIcon = () => {
@@ -263,7 +252,7 @@ export const makeZoomIcon = () => {
263
252
  textNode.style.textAlign = 'center';
264
253
  textNode.style.textAnchor = 'middle';
265
254
  textNode.style.fontSize = '55px';
266
- textNode.style.fill = 'var(--primary-foreground-color)';
255
+ textNode.style.fill = 'var(--icon-color)';
267
256
  textNode.style.fontFamily = 'monospace';
268
257
 
269
258
  icon.appendChild(textNode);
@@ -315,7 +304,7 @@ export const makePenIcon = (tipThickness: number, color: string) => {
315
304
  <!-- Pen grip -->
316
305
  <path
317
306
  d='M10,10 L90,10 L90,60 L${50 + halfThickness},80 L${50 - halfThickness},80 L10,60 Z'
318
- ${primaryForegroundStrokeFill}
307
+ ${iconColorStrokeFill}
319
308
  />
320
309
  </g>
321
310
  <g>
@@ -389,7 +378,7 @@ export const makePipetteIcon = (color?: Color4) => {
389
378
  65,15 65,5 47,6
390
379
  Z
391
380
  `);
392
- pipette.style.fill = 'var(--primary-foreground-color)';
381
+ pipette.style.fill = 'var(--icon-color)';
393
382
 
394
383
  if (color) {
395
384
  const defs = document.createElementNS(svgNamespace, 'defs');
@@ -417,3 +406,34 @@ export const makePipetteIcon = (color?: Color4) => {
417
406
  icon.setAttribute('viewBox', '0 0 100 100');
418
407
  return icon;
419
408
  };
409
+
410
+ export const makeResizeViewportIcon = () => {
411
+ return pathIcon(`
412
+ M 75 5 75 10 90 10 90 25 95 25 95 5 75 5 z
413
+ M 15 15 15 30 20 30 20 20 30 20 30 15 15 15 z
414
+ M 84 15 82 17 81 16 81 20 85 20 84 19 86 17 84 15 z
415
+ M 26 24 24 26 26 28 25 29 29 29 29 25 28 26 26 24 z
416
+ M 25 71 26 72 24 74 26 76 28 74 29 75 29 71 25 71 z
417
+ M 15 75 15 85 25 85 25 80 20 80 20 75 15 75 z
418
+ M 90 75 90 90 75 90 75 95 95 95 95 75 90 75 z
419
+ M 81 81 81 85 82 84 84 86 86 84 84 82 85 81 81 81 z
420
+ `);
421
+ };
422
+
423
+ export const makeDuplicateSelectionIcon = () => {
424
+ return pathIcon(`
425
+ M 45,10 45,55 90,55 90,10 45,10 z
426
+ M 10,25 10,90 70,90 70,60 40,60 40,25 10,25 z
427
+ `);
428
+ };
429
+
430
+ export const makeDeleteSelectionIcon = () => {
431
+ const strokeWidth = '5px';
432
+ const strokeColor = 'var(--icon-color)';
433
+ const fillColor = 'none';
434
+
435
+ return pathIcon(`
436
+ M 10,10 90,90
437
+ M 10,90 90,10
438
+ `, fillColor, strokeColor, strokeWidth);
439
+ };