js-draw 0.18.2 → 0.20.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 (269) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +10 -0
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -0
  5. package/dist/cjs/src/Color4.d.ts +8 -0
  6. package/dist/cjs/src/Color4.js +67 -0
  7. package/dist/cjs/src/Editor.d.ts +2 -2
  8. package/dist/cjs/src/Editor.js +7 -7
  9. package/dist/cjs/src/SVGLoader.js +77 -12
  10. package/dist/cjs/src/Viewport.d.ts +2 -0
  11. package/dist/cjs/src/Viewport.js +6 -2
  12. package/dist/cjs/src/components/AbstractComponent.d.ts +2 -2
  13. package/dist/cjs/src/components/AbstractComponent.js +3 -3
  14. package/dist/cjs/src/components/{ImageBackground.d.ts → BackgroundComponent.d.ts} +23 -3
  15. package/dist/cjs/src/components/BackgroundComponent.js +309 -0
  16. package/dist/cjs/src/components/RestylableComponent.d.ts +21 -2
  17. package/dist/cjs/src/components/Stroke.d.ts +35 -0
  18. package/dist/cjs/src/components/Stroke.js +37 -3
  19. package/dist/cjs/src/components/TextComponent.d.ts +27 -17
  20. package/dist/cjs/src/components/TextComponent.js +23 -1
  21. package/dist/cjs/src/components/lib.d.ts +4 -3
  22. package/dist/cjs/src/components/lib.js +2 -2
  23. package/dist/cjs/src/components/util/StrokeSmoother.js +25 -15
  24. package/dist/cjs/src/lib.d.ts +30 -0
  25. package/dist/cjs/src/lib.js +30 -0
  26. package/dist/cjs/src/localizations/de.js +1 -1
  27. package/dist/cjs/src/localizations/es.js +1 -1
  28. package/dist/cjs/src/math/Path.js +1 -1
  29. package/dist/cjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
  30. package/dist/cjs/src/math/polynomial/QuadraticBezier.js +115 -0
  31. package/dist/cjs/src/math/polynomial/solveQuadratic.d.ts +6 -0
  32. package/dist/cjs/src/math/polynomial/solveQuadratic.js +36 -0
  33. package/dist/cjs/src/rendering/RenderingStyle.d.ts +4 -4
  34. package/dist/cjs/src/rendering/TextRenderingStyle.d.ts +10 -10
  35. package/dist/cjs/src/rendering/lib.d.ts +2 -0
  36. package/dist/cjs/src/rendering/renderers/AbstractRenderer.d.ts +2 -2
  37. package/dist/cjs/src/rendering/renderers/CanvasRenderer.d.ts +2 -2
  38. package/dist/cjs/src/rendering/renderers/CanvasRenderer.js +5 -3
  39. package/dist/cjs/src/rendering/renderers/DummyRenderer.d.ts +2 -2
  40. package/dist/cjs/src/rendering/renderers/SVGRenderer.d.ts +2 -2
  41. package/dist/cjs/src/rendering/renderers/SVGRenderer.js +15 -6
  42. package/dist/cjs/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -2
  43. package/dist/cjs/src/toolbar/IconProvider.d.ts +2 -2
  44. package/dist/cjs/src/toolbar/localization.d.ts +2 -1
  45. package/dist/cjs/src/toolbar/localization.js +3 -2
  46. package/dist/cjs/src/toolbar/widgets/BaseWidget.js +1 -1
  47. package/dist/cjs/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +5 -0
  48. package/dist/cjs/src/toolbar/widgets/DocumentPropertiesWidget.js +77 -2
  49. package/dist/cjs/src/toolbar/widgets/PenToolWidget.js +1 -1
  50. package/dist/cjs/src/tools/FindTool.js +1 -1
  51. package/dist/cjs/src/tools/SoundUITool.d.ts +24 -0
  52. package/dist/cjs/src/tools/SoundUITool.js +164 -0
  53. package/dist/cjs/src/tools/TextTool.d.ts +2 -2
  54. package/dist/cjs/src/tools/ToolController.js +6 -1
  55. package/dist/cjs/src/tools/lib.d.ts +1 -0
  56. package/dist/cjs/src/tools/lib.js +3 -1
  57. package/dist/cjs/src/tools/localization.d.ts +3 -0
  58. package/dist/cjs/src/tools/localization.js +3 -0
  59. package/dist/mjs/src/Color4.d.ts +8 -0
  60. package/dist/mjs/src/Color4.mjs +64 -0
  61. package/dist/mjs/src/Editor.d.ts +2 -2
  62. package/dist/mjs/src/Editor.mjs +6 -6
  63. package/dist/mjs/src/SVGLoader.mjs +76 -11
  64. package/dist/mjs/src/Viewport.d.ts +2 -0
  65. package/dist/mjs/src/Viewport.mjs +6 -2
  66. package/dist/mjs/src/components/AbstractComponent.d.ts +2 -2
  67. package/dist/mjs/src/components/AbstractComponent.mjs +3 -3
  68. package/dist/mjs/src/components/{ImageBackground.d.ts → BackgroundComponent.d.ts} +23 -3
  69. package/dist/mjs/src/components/BackgroundComponent.mjs +279 -0
  70. package/dist/mjs/src/components/RestylableComponent.d.ts +21 -2
  71. package/dist/mjs/src/components/Stroke.d.ts +35 -0
  72. package/dist/mjs/src/components/Stroke.mjs +37 -3
  73. package/dist/mjs/src/components/TextComponent.d.ts +27 -17
  74. package/dist/mjs/src/components/TextComponent.mjs +23 -1
  75. package/dist/mjs/src/components/lib.d.ts +4 -3
  76. package/dist/mjs/src/components/lib.mjs +2 -2
  77. package/dist/mjs/src/components/util/StrokeSmoother.mjs +25 -15
  78. package/dist/mjs/src/lib.d.ts +30 -0
  79. package/dist/mjs/src/lib.mjs +30 -0
  80. package/dist/mjs/src/localizations/de.mjs +1 -1
  81. package/dist/mjs/src/localizations/es.mjs +1 -1
  82. package/dist/mjs/src/math/Path.mjs +1 -1
  83. package/dist/mjs/src/math/polynomial/QuadraticBezier.d.ts +28 -0
  84. package/dist/mjs/src/math/polynomial/QuadraticBezier.mjs +109 -0
  85. package/dist/mjs/src/math/polynomial/solveQuadratic.d.ts +6 -0
  86. package/dist/mjs/src/math/polynomial/solveQuadratic.mjs +34 -0
  87. package/dist/mjs/src/rendering/RenderingStyle.d.ts +4 -4
  88. package/dist/mjs/src/rendering/TextRenderingStyle.d.ts +10 -10
  89. package/dist/mjs/src/rendering/lib.d.ts +2 -0
  90. package/dist/mjs/src/rendering/renderers/AbstractRenderer.d.ts +2 -2
  91. package/dist/mjs/src/rendering/renderers/CanvasRenderer.d.ts +2 -2
  92. package/dist/mjs/src/rendering/renderers/CanvasRenderer.mjs +5 -3
  93. package/dist/mjs/src/rendering/renderers/DummyRenderer.d.ts +2 -2
  94. package/dist/mjs/src/rendering/renderers/SVGRenderer.d.ts +2 -2
  95. package/dist/mjs/src/rendering/renderers/SVGRenderer.mjs +15 -6
  96. package/dist/mjs/src/rendering/renderers/TextOnlyRenderer.d.ts +2 -2
  97. package/dist/mjs/src/toolbar/IconProvider.d.ts +2 -2
  98. package/dist/mjs/src/toolbar/localization.d.ts +2 -1
  99. package/dist/mjs/src/toolbar/localization.mjs +3 -2
  100. package/dist/mjs/src/toolbar/widgets/BaseWidget.mjs +1 -1
  101. package/dist/mjs/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +5 -0
  102. package/dist/mjs/src/toolbar/widgets/DocumentPropertiesWidget.mjs +54 -2
  103. package/dist/mjs/src/toolbar/widgets/PenToolWidget.mjs +1 -1
  104. package/dist/mjs/src/tools/FindTool.mjs +1 -1
  105. package/dist/mjs/src/tools/SoundUITool.d.ts +24 -0
  106. package/dist/mjs/src/tools/SoundUITool.mjs +158 -0
  107. package/dist/mjs/src/tools/TextTool.d.ts +2 -2
  108. package/dist/mjs/src/tools/ToolController.mjs +6 -1
  109. package/dist/mjs/src/tools/lib.d.ts +1 -0
  110. package/dist/mjs/src/tools/lib.mjs +1 -0
  111. package/dist/mjs/src/tools/localization.d.ts +3 -0
  112. package/dist/mjs/src/tools/localization.mjs +3 -0
  113. package/jest.config.js +1 -1
  114. package/package.json +19 -17
  115. package/src/Editor.css +2 -2
  116. package/src/tools/SoundUITool.css +15 -0
  117. package/src/tools/tools.css +4 -0
  118. package/dist/cjs/src/components/ImageBackground.js +0 -146
  119. package/dist/mjs/src/components/ImageBackground.mjs +0 -139
  120. package/src/Color4.test.ts +0 -40
  121. package/src/Color4.ts +0 -236
  122. package/src/Editor.loadFrom.test.ts +0 -24
  123. package/src/Editor.toSVG.test.ts +0 -111
  124. package/src/Editor.ts +0 -1122
  125. package/src/EditorImage.test.ts +0 -120
  126. package/src/EditorImage.ts +0 -603
  127. package/src/EventDispatcher.test.ts +0 -123
  128. package/src/EventDispatcher.ts +0 -71
  129. package/src/Pointer.ts +0 -127
  130. package/src/SVGLoader.test.ts +0 -114
  131. package/src/SVGLoader.ts +0 -511
  132. package/src/UndoRedoHistory.test.ts +0 -33
  133. package/src/UndoRedoHistory.ts +0 -102
  134. package/src/Viewport.ts +0 -319
  135. package/src/bundle/bundled.ts +0 -7
  136. package/src/commands/Command.ts +0 -45
  137. package/src/commands/Duplicate.ts +0 -48
  138. package/src/commands/Erase.ts +0 -74
  139. package/src/commands/SerializableCommand.ts +0 -49
  140. package/src/commands/UnresolvedCommand.ts +0 -37
  141. package/src/commands/invertCommand.ts +0 -51
  142. package/src/commands/lib.ts +0 -16
  143. package/src/commands/localization.ts +0 -47
  144. package/src/commands/uniteCommands.test.ts +0 -23
  145. package/src/commands/uniteCommands.ts +0 -135
  146. package/src/components/AbstractComponent.transformBy.test.ts +0 -22
  147. package/src/components/AbstractComponent.ts +0 -364
  148. package/src/components/ImageBackground.test.ts +0 -35
  149. package/src/components/ImageBackground.ts +0 -176
  150. package/src/components/ImageComponent.ts +0 -171
  151. package/src/components/RestylableComponent.ts +0 -142
  152. package/src/components/SVGGlobalAttributesObject.ts +0 -81
  153. package/src/components/Stroke.test.ts +0 -139
  154. package/src/components/Stroke.ts +0 -245
  155. package/src/components/TextComponent.test.ts +0 -99
  156. package/src/components/TextComponent.ts +0 -315
  157. package/src/components/UnknownSVGObject.test.ts +0 -10
  158. package/src/components/UnknownSVGObject.ts +0 -60
  159. package/src/components/builders/ArrowBuilder.ts +0 -107
  160. package/src/components/builders/FreehandLineBuilder.ts +0 -212
  161. package/src/components/builders/LineBuilder.ts +0 -77
  162. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -454
  163. package/src/components/builders/RectangleBuilder.ts +0 -74
  164. package/src/components/builders/types.ts +0 -15
  165. package/src/components/lib.ts +0 -25
  166. package/src/components/localization.ts +0 -22
  167. package/src/components/util/StrokeSmoother.ts +0 -293
  168. package/src/components/util/describeComponentList.ts +0 -18
  169. package/src/lib.ts +0 -37
  170. package/src/localization.ts +0 -34
  171. package/src/localizations/de.ts +0 -98
  172. package/src/localizations/en.ts +0 -8
  173. package/src/localizations/es.ts +0 -74
  174. package/src/localizations/getLocalizationTable.test.ts +0 -27
  175. package/src/localizations/getLocalizationTable.ts +0 -55
  176. package/src/math/LineSegment2.test.ts +0 -99
  177. package/src/math/LineSegment2.ts +0 -160
  178. package/src/math/Mat33.test.ts +0 -244
  179. package/src/math/Mat33.ts +0 -437
  180. package/src/math/Path.fromString.test.ts +0 -223
  181. package/src/math/Path.test.ts +0 -198
  182. package/src/math/Path.toString.test.ts +0 -77
  183. package/src/math/Path.ts +0 -790
  184. package/src/math/Rect2.test.ts +0 -204
  185. package/src/math/Rect2.ts +0 -315
  186. package/src/math/Triangle.ts +0 -29
  187. package/src/math/Vec2.test.ts +0 -30
  188. package/src/math/Vec2.ts +0 -18
  189. package/src/math/Vec3.test.ts +0 -44
  190. package/src/math/Vec3.ts +0 -218
  191. package/src/math/lib.ts +0 -15
  192. package/src/math/rounding.test.ts +0 -65
  193. package/src/math/rounding.ts +0 -156
  194. package/src/rendering/Display.ts +0 -249
  195. package/src/rendering/RenderingStyle.test.ts +0 -68
  196. package/src/rendering/RenderingStyle.ts +0 -55
  197. package/src/rendering/TextRenderingStyle.ts +0 -45
  198. package/src/rendering/caching/CacheRecord.test.ts +0 -49
  199. package/src/rendering/caching/CacheRecord.ts +0 -77
  200. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  201. package/src/rendering/caching/RenderingCache.test.ts +0 -44
  202. package/src/rendering/caching/RenderingCache.ts +0 -66
  203. package/src/rendering/caching/RenderingCacheNode.ts +0 -405
  204. package/src/rendering/caching/testUtils.ts +0 -35
  205. package/src/rendering/caching/types.ts +0 -34
  206. package/src/rendering/lib.ts +0 -6
  207. package/src/rendering/localization.ts +0 -20
  208. package/src/rendering/renderers/AbstractRenderer.ts +0 -222
  209. package/src/rendering/renderers/CanvasRenderer.ts +0 -296
  210. package/src/rendering/renderers/DummyRenderer.test.ts +0 -42
  211. package/src/rendering/renderers/DummyRenderer.ts +0 -136
  212. package/src/rendering/renderers/SVGRenderer.ts +0 -354
  213. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -70
  214. package/src/testing/beforeEachFile.ts +0 -8
  215. package/src/testing/createEditor.ts +0 -11
  216. package/src/testing/global.d.ts +0 -17
  217. package/src/testing/lib.ts +0 -3
  218. package/src/testing/loadExpectExtensions.ts +0 -25
  219. package/src/testing/sendPenEvent.ts +0 -31
  220. package/src/testing/sendTouchEvent.ts +0 -78
  221. package/src/toolbar/HTMLToolbar.ts +0 -492
  222. package/src/toolbar/IconProvider.ts +0 -736
  223. package/src/toolbar/lib.ts +0 -4
  224. package/src/toolbar/localization.ts +0 -106
  225. package/src/toolbar/makeColorInput.ts +0 -145
  226. package/src/toolbar/types.ts +0 -5
  227. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  228. package/src/toolbar/widgets/BaseToolWidget.ts +0 -56
  229. package/src/toolbar/widgets/BaseWidget.ts +0 -377
  230. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -167
  231. package/src/toolbar/widgets/EraserToolWidget.ts +0 -85
  232. package/src/toolbar/widgets/HandToolWidget.ts +0 -250
  233. package/src/toolbar/widgets/InsertImageWidget.ts +0 -223
  234. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  235. package/src/toolbar/widgets/PenToolWidget.ts +0 -288
  236. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -190
  237. package/src/toolbar/widgets/TextToolWidget.ts +0 -145
  238. package/src/toolbar/widgets/lib.ts +0 -13
  239. package/src/tools/BaseTool.ts +0 -76
  240. package/src/tools/Eraser.test.ts +0 -103
  241. package/src/tools/Eraser.ts +0 -139
  242. package/src/tools/FindTool.ts +0 -152
  243. package/src/tools/PanZoom.test.ts +0 -310
  244. package/src/tools/PanZoom.ts +0 -520
  245. package/src/tools/PasteHandler.ts +0 -95
  246. package/src/tools/Pen.test.ts +0 -194
  247. package/src/tools/Pen.ts +0 -226
  248. package/src/tools/PipetteTool.ts +0 -55
  249. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -28
  250. package/src/tools/SelectionTool/Selection.ts +0 -607
  251. package/src/tools/SelectionTool/SelectionHandle.ts +0 -108
  252. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -261
  253. package/src/tools/SelectionTool/SelectionTool.ts +0 -480
  254. package/src/tools/SelectionTool/TransformMode.ts +0 -114
  255. package/src/tools/SelectionTool/types.ts +0 -11
  256. package/src/tools/TextTool.ts +0 -326
  257. package/src/tools/ToolController.ts +0 -178
  258. package/src/tools/ToolEnabledGroup.ts +0 -14
  259. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  260. package/src/tools/ToolbarShortcutHandler.ts +0 -34
  261. package/src/tools/UndoRedoShortcut.test.ts +0 -56
  262. package/src/tools/UndoRedoShortcut.ts +0 -25
  263. package/src/tools/lib.ts +0 -21
  264. package/src/tools/localization.ts +0 -66
  265. package/src/types.ts +0 -234
  266. package/src/util/assertions.ts +0 -55
  267. package/src/util/fileToBase64.ts +0 -18
  268. package/src/util/untilNextAnimationFrame.ts +0 -9
  269. package/src/util/waitForTimeout.ts +0 -9
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.StrokeSmoother = void 0;
7
- const bezier_js_1 = require("bezier-js");
8
7
  const Vec2_1 = require("../../math/Vec2");
9
8
  const Rect2_1 = __importDefault(require("../../math/Rect2"));
10
9
  const LineSegment2_1 = __importDefault(require("../../math/LineSegment2"));
10
+ const QuadraticBezier_1 = __importDefault(require("../../math/polynomial/QuadraticBezier"));
11
11
  // Handles stroke smoothing
12
12
  class StrokeSmoother {
13
13
  constructor(startPoint,
@@ -44,9 +44,9 @@ class StrokeSmoother {
44
44
  if (!this.currentCurve) {
45
45
  return 0;
46
46
  }
47
- const startPt = Vec2_1.Vec2.ofXY(this.currentCurve.points[0]);
48
- const controlPt = Vec2_1.Vec2.ofXY(this.currentCurve.points[1]);
49
- const endPt = Vec2_1.Vec2.ofXY(this.currentCurve.points[2]);
47
+ const startPt = this.currentCurve.p0;
48
+ const controlPt = this.currentCurve.p1;
49
+ const endPt = this.currentCurve.p2;
50
50
  const toControlDist = startPt.minus(controlPt).length();
51
51
  const toEndDist = endPt.minus(controlPt).length();
52
52
  return toControlDist + toEndDist;
@@ -58,7 +58,7 @@ class StrokeSmoother {
58
58
  }
59
59
  this.onCurveAdded(this.currentSegmentToPath());
60
60
  const lastPoint = this.buffer[this.buffer.length - 1];
61
- this.lastExitingVec = Vec2_1.Vec2.ofXY(this.currentCurve.points[2]).minus(Vec2_1.Vec2.ofXY(this.currentCurve.points[1]));
61
+ this.lastExitingVec = this.currentCurve.p2.minus(this.currentCurve.p1);
62
62
  console.assert(this.lastExitingVec.magnitude() !== 0, 'lastExitingVec has zero length!');
63
63
  // Use the last two points to start a new curve (the last point isn't used
64
64
  // in the current curve and we want connected curves to share end points)
@@ -73,13 +73,13 @@ class StrokeSmoother {
73
73
  if (this.currentCurve == null) {
74
74
  throw new Error('Invalid State: currentCurve is null!');
75
75
  }
76
- const startVec = Vec2_1.Vec2.ofXY(this.currentCurve.normal(0)).normalized();
76
+ const startVec = this.currentCurve.normal(0).normalized();
77
77
  if (!isFinite(startVec.magnitude())) {
78
78
  throw new Error(`startVec(${startVec}) is NaN or ∞`);
79
79
  }
80
- const startPt = Vec2_1.Vec2.ofXY(this.currentCurve.get(0));
81
- const endPt = Vec2_1.Vec2.ofXY(this.currentCurve.get(1));
82
- const controlPoint = Vec2_1.Vec2.ofXY(this.currentCurve.points[1]);
80
+ const startPt = this.currentCurve.at(0);
81
+ const endPt = this.currentCurve.at(1);
82
+ const controlPoint = this.currentCurve.p1;
83
83
  return {
84
84
  startPoint: startPt,
85
85
  controlPoint,
@@ -131,7 +131,7 @@ class StrokeSmoother {
131
131
  const p2 = lastPoint.pos.plus((_b = this.lastExitingVec) !== null && _b !== void 0 ? _b : Vec2_1.Vec2.unitX);
132
132
  const p3 = newPoint.pos;
133
133
  // Quadratic Bézier curve
134
- this.currentCurve = new bezier_js_1.Bezier(p1.xy, p2.xy, p3.xy);
134
+ this.currentCurve = new QuadraticBezier_1.default(p1, p2, p3);
135
135
  console.assert(!isNaN(p1.magnitude()) && !isNaN(p2.magnitude()) && !isNaN(p3.magnitude()), 'Expected !NaN');
136
136
  if (this.isFirstSegment) {
137
137
  // The start of a curve often lacks accurate pressure information. Update it.
@@ -182,19 +182,29 @@ class StrokeSmoother {
182
182
  console.assert(!segmentStart.eq(controlPoint, 1e-11), 'Start and control points are equal!');
183
183
  console.assert(!controlPoint.eq(segmentEnd, 1e-11), 'Control and end points are equal!');
184
184
  const prevCurve = this.currentCurve;
185
- this.currentCurve = new bezier_js_1.Bezier(segmentStart.xy, controlPoint.xy, segmentEnd.xy);
186
- if (isNaN(Vec2_1.Vec2.ofXY(this.currentCurve.normal(0)).magnitude())) {
185
+ this.currentCurve = new QuadraticBezier_1.default(segmentStart, controlPoint, segmentEnd);
186
+ if (isNaN(this.currentCurve.normal(0).magnitude())) {
187
187
  console.error('NaN normal at 0. Curve:', this.currentCurve);
188
188
  this.currentCurve = prevCurve;
189
189
  }
190
190
  // Should we start making a new curve? Check whether all buffer points are within
191
191
  // ±strokeWidth of the curve.
192
192
  const curveMatchesPoints = (curve) => {
193
+ let nonMatching = 0;
194
+ const maxNonMatching = 2;
195
+ const minFit = Math.max(Math.min(this.curveStartWidth, this.curveEndWidth) / 3, this.minFitAllowed);
193
196
  for (const point of this.buffer) {
194
- const proj = Vec2_1.Vec2.ofXY(curve.project(point.xy));
195
- const dist = proj.minus(point).magnitude();
196
- const minFit = Math.max(Math.min(this.curveStartWidth, this.curveEndWidth) / 3, this.minFitAllowed);
197
+ let dist = curve.approximateDistance(point);
197
198
  if (dist > minFit || dist > this.maxFitAllowed) {
199
+ // Avoid using the slower .distance
200
+ if (nonMatching >= maxNonMatching - 1) {
201
+ dist = curve.distance(point);
202
+ }
203
+ if (dist > minFit || dist > this.maxFitAllowed) {
204
+ nonMatching++;
205
+ }
206
+ }
207
+ if (nonMatching >= maxNonMatching) {
198
208
  return false;
199
209
  }
200
210
  }
@@ -5,10 +5,40 @@
5
5
  * @example
6
6
  * ```
7
7
  * import { Editor, Vec3, Mat33 } from 'js-draw';
8
+ *
9
+ * // Apply js-draw CSS
10
+ * import 'js-draw/styles';
11
+ * // If your bundler doesn't support the above, try
12
+ * // import 'js-draw/bundledStyles';
13
+ *
14
+ * (async () => {
15
+ * const editor = new Editor(document.body);
16
+ * const toolbar = editor.addToolbar();
17
+ * editor.getRootElement().style.height = '600px';
18
+ *
19
+ * await editor.loadFromSVG(`
20
+ * <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
21
+ * <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
22
+ * <path d="M500,500L500,0L0,0L0,500L500,500" fill="#423131bf" class="js-draw-image-background"></path>
23
+ * <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: rgb(204, 102, 51);">Testing...</text>
24
+ * </svg>
25
+ * `);
26
+ *
27
+ * toolbar.addActionButton({
28
+ * label: 'Save',
29
+ * icon: editor.icons.makeSaveIcon(),
30
+ * }, () => {
31
+ * const saveData = editor.toSVG().outerHTML;
32
+ *
33
+ * // Do something with saveData
34
+ * });
35
+ * })();
8
36
  * ```
9
37
  *
10
38
  * @see
11
39
  * {@link Editor}
40
+ * {@link Editor.loadFromSVG}
41
+ * {@link HTMLToolbar.addActionButton }
12
42
  *
13
43
  * @packageDocumentation
14
44
  */
@@ -6,10 +6,40 @@
6
6
  * @example
7
7
  * ```
8
8
  * import { Editor, Vec3, Mat33 } from 'js-draw';
9
+ *
10
+ * // Apply js-draw CSS
11
+ * import 'js-draw/styles';
12
+ * // If your bundler doesn't support the above, try
13
+ * // import 'js-draw/bundledStyles';
14
+ *
15
+ * (async () => {
16
+ * const editor = new Editor(document.body);
17
+ * const toolbar = editor.addToolbar();
18
+ * editor.getRootElement().style.height = '600px';
19
+ *
20
+ * await editor.loadFromSVG(`
21
+ * <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
22
+ * <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
23
+ * <path d="M500,500L500,0L0,0L0,500L500,500" fill="#423131bf" class="js-draw-image-background"></path>
24
+ * <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: rgb(204, 102, 51);">Testing...</text>
25
+ * </svg>
26
+ * `);
27
+ *
28
+ * toolbar.addActionButton({
29
+ * label: 'Save',
30
+ * icon: editor.icons.makeSaveIcon(),
31
+ * }, () => {
32
+ * const saveData = editor.toSVG().outerHTML;
33
+ *
34
+ * // Do something with saveData
35
+ * });
36
+ * })();
9
37
  * ```
10
38
  *
11
39
  * @see
12
40
  * {@link Editor}
41
+ * {@link Editor.loadFromSVG}
42
+ * {@link HTMLToolbar.addActionButton }
13
43
  *
14
44
  * @packageDocumentation
15
45
  */
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const localization_1 = require("../localization");
4
4
  // German localization
5
- const localization = Object.assign(Object.assign({}, localization_1.defaultEditorLocalization), { pen: 'Stift', eraser: 'Radierer', select: 'Auswahl', handTool: 'Verschieben', zoom: 'Vergrößerung', resetView: 'Ansicht zurücksetzen', thicknessLabel: 'Dicke: ', colorLabel: 'Farbe: ', fontLabel: 'Schriftart: ', resizeImageToSelection: 'Bildgröße an Auswahl anpassen', deleteSelection: 'Auswahl löschen', duplicateSelection: 'Auswahl duplizieren', undo: 'Rückgängig', redo: 'Wiederholen', pickColorFromScreen: 'Farbe von Bildschirm auswählen', clickToPickColorAnnouncement: 'Klicke auf den Bildschirm, um eine Farbe auszuwählen', selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.', touchPanning: 'Ansicht mit Touchscreen verschieben', anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben', selectObjectType: 'Objekt-Typ: ', freehandPen: 'Freihand', arrowPen: 'Pfeil', linePen: 'Linie', outlinedRectanglePen: 'Umrissenes Rechteck', filledRectanglePen: 'Ausgefülltes Rechteck', dropdownShown: t => `Dropdown-Menü für ${t} angezeigt`, dropdownHidden: t => `Dropdown-Menü für ${t} versteckt`, zoomLevel: t => `Vergößerung: ${t}%`, colorChangedAnnouncement: t => `Farbe zu ${t} geändert`, penTool: t => `Stift ${t}`, selectionTool: 'Auswahl', eraserTool: 'Radiergummi', touchPanTool: 'Ansicht mit Touchscreen verschieben', twoFingerPanZoomTool: 'Ansicht verschieben und vergrößern', undoRedoTool: 'Rückgängig/Wiederholen', rightClickDragPanTool: 'Rechtsklick-Ziehen', pipetteTool: 'Farbe von Bildschirm auswählen', keyboardPanZoom: 'Tastaturkürzel zum Verschieben/Vergrößern der Ansicht', textTool: 'Text', enterTextToInsert: 'Einzufügender Text', toolEnabledAnnouncement: t => `${t} aktiviert`, toolDisabledAnnouncement: t => `${t} deaktiviert`, updatedViewport: 'Transformierte Ansicht', transformedElements: t => `${t} Element${1 === t ? '' : 'e'} transformiert`, resizeOutputCommand: t => `Bildgröße auf ${t.w}x${t.h} geändert`, addElementAction: t => `${t} hinzugefügt`, eraseAction: (t, e) => `${e} ${t} gelöscht`, duplicateAction: (t, e) => `${e} ${t} dupliziert`, inverseOf: t => `Umkehrung von ${t}`, elements: 'Elemente', erasedNoElements: 'Nichts entfernt', duplicatedNoElements: 'Nichts dupliziert', rotatedBy: t => `${Math.abs(t)} Grad ${t < 0 ? 'im Uhrzeigersinn' : 'gegen den Uhrzeigersinn'} gedreht`, movedLeft: 'Nacht links bewegt', movedUp: 'Nacht oben bewegt', movedDown: 'Nacht unten bewegt', movedRight: 'Nacht rechts bewegt', zoomedOut: 'Ansicht verkleinert', zoomedIn: 'Ansicht vergrößert', selectedElements: t => `${t} Element${1 === t ? '' : 'e'} ausgewählt`, stroke: 'Strich', svgObject: 'SVG-Objekt', text: t => `Text-Objekt: ${t}`, pathNodeCount: t => `Es gibt ${t} sichtbare Pfad-Objekte.`, textNodeCount: t => `Es gibt ${t} sichtbare Text-Knotenpunkte.`, textNode: t => `Text: ${t}`, rerenderAsText: 'Als Text darstellen', accessibilityInputInstructions: 'Drücke ‚t‘, um den Inhalt des Ansichtsfensters als Text zu lesen. Verwende die Pfeiltasten, um die Ansicht zu verschieben, und klicke und ziehe, um Striche zu zeichnen. Drücke ‚w‘ zum Vergrößern und ‚s‘ zum Verkleinern der Ansicht.', loading: t => `Laden ${t}%...`, doneLoading: 'Laden fertig', imageEditor: 'Bild-Editor', undoAnnouncement: t => `Rückgangig gemacht ${t}`, redoAnnouncement: t => `Wiederholt ${t}` });
5
+ const localization = Object.assign(Object.assign({}, localization_1.defaultEditorLocalization), { pen: 'Stift', eraser: 'Radierer', select: 'Auswahl', handTool: 'Verschieben', zoom: 'Vergrößerung', resetView: 'Ansicht zurücksetzen', thicknessLabel: 'Dicke: ', colorLabel: 'Farbe: ', fontLabel: 'Schriftart: ', resizeImageToSelection: 'Bildgröße an Auswahl anpassen', deleteSelection: 'Auswahl löschen', duplicateSelection: 'Auswahl duplizieren', undo: 'Rückgängig', redo: 'Wiederholen', pickColorFromScreen: 'Farbe von Bildschirm auswählen', clickToPickColorAnnouncement: 'Klicke auf den Bildschirm, um eine Farbe auszuwählen', selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.', touchPanning: 'Ansicht mit Touchscreen verschieben', anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben', selectPenType: 'Objekt-Typ: ', freehandPen: 'Freihand', arrowPen: 'Pfeil', linePen: 'Linie', outlinedRectanglePen: 'Umrissenes Rechteck', filledRectanglePen: 'Ausgefülltes Rechteck', dropdownShown: t => `Dropdown-Menü für ${t} angezeigt`, dropdownHidden: t => `Dropdown-Menü für ${t} versteckt`, zoomLevel: t => `Vergößerung: ${t}%`, colorChangedAnnouncement: t => `Farbe zu ${t} geändert`, penTool: t => `Stift ${t}`, selectionTool: 'Auswahl', eraserTool: 'Radiergummi', touchPanTool: 'Ansicht mit Touchscreen verschieben', twoFingerPanZoomTool: 'Ansicht verschieben und vergrößern', undoRedoTool: 'Rückgängig/Wiederholen', rightClickDragPanTool: 'Rechtsklick-Ziehen', pipetteTool: 'Farbe von Bildschirm auswählen', keyboardPanZoom: 'Tastaturkürzel zum Verschieben/Vergrößern der Ansicht', textTool: 'Text', enterTextToInsert: 'Einzufügender Text', toolEnabledAnnouncement: t => `${t} aktiviert`, toolDisabledAnnouncement: t => `${t} deaktiviert`, updatedViewport: 'Transformierte Ansicht', transformedElements: t => `${t} Element${1 === t ? '' : 'e'} transformiert`, resizeOutputCommand: t => `Bildgröße auf ${t.w}x${t.h} geändert`, addElementAction: t => `${t} hinzugefügt`, eraseAction: (t, e) => `${e} ${t} gelöscht`, duplicateAction: (t, e) => `${e} ${t} dupliziert`, inverseOf: t => `Umkehrung von ${t}`, elements: 'Elemente', erasedNoElements: 'Nichts entfernt', duplicatedNoElements: 'Nichts dupliziert', rotatedBy: t => `${Math.abs(t)} Grad ${t < 0 ? 'im Uhrzeigersinn' : 'gegen den Uhrzeigersinn'} gedreht`, movedLeft: 'Nacht links bewegt', movedUp: 'Nacht oben bewegt', movedDown: 'Nacht unten bewegt', movedRight: 'Nacht rechts bewegt', zoomedOut: 'Ansicht verkleinert', zoomedIn: 'Ansicht vergrößert', selectedElements: t => `${t} Element${1 === t ? '' : 'e'} ausgewählt`, stroke: 'Strich', svgObject: 'SVG-Objekt', text: t => `Text-Objekt: ${t}`, pathNodeCount: t => `Es gibt ${t} sichtbare Pfad-Objekte.`, textNodeCount: t => `Es gibt ${t} sichtbare Text-Knotenpunkte.`, textNode: t => `Text: ${t}`, rerenderAsText: 'Als Text darstellen', accessibilityInputInstructions: 'Drücke ‚t‘, um den Inhalt des Ansichtsfensters als Text zu lesen. Verwende die Pfeiltasten, um die Ansicht zu verschieben, und klicke und ziehe, um Striche zu zeichnen. Drücke ‚w‘ zum Vergrößern und ‚s‘ zum Verkleinern der Ansicht.', loading: t => `Laden ${t}%...`, doneLoading: 'Laden fertig', imageEditor: 'Bild-Editor', undoAnnouncement: t => `Rückgangig gemacht ${t}`, redoAnnouncement: t => `Wiederholt ${t}` });
6
6
  exports.default = localization;
@@ -8,7 +8,7 @@ const localization = Object.assign(Object.assign({}, localization_1.defaultEdito
8
8
  loading: (percentage) => `Cargando: ${percentage}%...`, imageEditor: 'Editor de dibujos', undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`, redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`, undo: 'Deshace', redo: 'Rehace',
9
9
  // Strings for the toolbar
10
10
  // (see src/toolbar/localization.ts)
11
- pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma', selectObjectType: 'Forma de dibuja:', handTool: 'Mover', zoom: 'Zoom', resetView: 'Reiniciar vista', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFromScreen: 'Selecciona un color de la pantalla', clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color', dropdownShown(toolName) {
11
+ pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma', selectPenType: 'Forma de dibuja:', handTool: 'Mover', zoom: 'Zoom', resetView: 'Reiniciar vista', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFromScreen: 'Selecciona un color de la pantalla', clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color', dropdownShown(toolName) {
12
12
  return `Menú por ${toolName} es visible`;
13
13
  }, dropdownHidden: function (toolName) {
14
14
  return `Menú por ${toolName} fue ocultado`;
@@ -347,7 +347,7 @@ class Path {
347
347
  const onlyStroked = strokeWidth > 0 && renderablePath.style.fill.a === 0;
348
348
  // Scale the expanded rect --- the visual equivalent is only close for huge strokes.
349
349
  const expandedRect = visibleRect.grownBy(strokeWidth)
350
- .transformedBoundingBox(Mat33_1.default.scaling2D(2, visibleRect.center));
350
+ .transformedBoundingBox(Mat33_1.default.scaling2D(4, visibleRect.center));
351
351
  // TODO: Handle simplifying very small paths.
352
352
  if (expandedRect.containsRect(path.bbox.grownBy(strokeWidth))) {
353
353
  return renderablePath;
@@ -0,0 +1,28 @@
1
+ import { Point2, Vec2 } from '../Vec2';
2
+ export default class QuadraticBezier {
3
+ readonly p0: Point2;
4
+ readonly p1: Point2;
5
+ readonly p2: Point2;
6
+ private bezierJs;
7
+ constructor(p0: Point2, p1: Point2, p2: Point2);
8
+ /**
9
+ * Returns a component of a quadratic Bézier curve at t, where p0,p1,p2 are either all x or
10
+ * all y components of the target curve.
11
+ */
12
+ private static componentAt;
13
+ private static derivativeComponentAt;
14
+ /**
15
+ * @returns the curve evaluated at `t`.
16
+ */
17
+ at(t: number): Point2;
18
+ derivativeAt(t: number): Point2;
19
+ /**
20
+ * @returns the approximate distance from `point` to this curve.
21
+ */
22
+ approximateDistance(point: Point2): number;
23
+ /**
24
+ * @returns the exact distance from `point` to this.
25
+ */
26
+ distance(point: Point2): number;
27
+ normal(t: number): Vec2;
28
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const bezier_js_1 = require("bezier-js");
7
+ const Vec2_1 = require("../Vec2");
8
+ const solveQuadratic_1 = __importDefault(require("./solveQuadratic"));
9
+ class QuadraticBezier {
10
+ constructor(p0, p1, p2) {
11
+ this.p0 = p0;
12
+ this.p1 = p1;
13
+ this.p2 = p2;
14
+ this.bezierJs = null;
15
+ }
16
+ /**
17
+ * Returns a component of a quadratic Bézier curve at t, where p0,p1,p2 are either all x or
18
+ * all y components of the target curve.
19
+ */
20
+ static componentAt(t, p0, p1, p2) {
21
+ return p0 + t * (-2 * p0 + 2 * p1) + t * t * (p0 - 2 * p1 + p2);
22
+ }
23
+ static derivativeComponentAt(t, p0, p1, p2) {
24
+ return -2 * p0 + 2 * p1 + 2 * t * (p0 - 2 * p1 + p2);
25
+ }
26
+ /**
27
+ * @returns the curve evaluated at `t`.
28
+ */
29
+ at(t) {
30
+ const p0 = this.p0;
31
+ const p1 = this.p1;
32
+ const p2 = this.p2;
33
+ return Vec2_1.Vec2.of(QuadraticBezier.componentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.componentAt(t, p0.y, p1.y, p2.y));
34
+ }
35
+ derivativeAt(t) {
36
+ const p0 = this.p0;
37
+ const p1 = this.p1;
38
+ const p2 = this.p2;
39
+ return Vec2_1.Vec2.of(QuadraticBezier.derivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.derivativeComponentAt(t, p0.y, p1.y, p2.y));
40
+ }
41
+ /**
42
+ * @returns the approximate distance from `point` to this curve.
43
+ */
44
+ approximateDistance(point) {
45
+ // We want to minimize f(t) = |B(t) - p|².
46
+ // Expanding,
47
+ // f(t) = (Bₓ(t) - pₓ)² + (Bᵧ(t) - pᵧ)²
48
+ // ⇒ f'(t) = Dₜ(Bₓ(t) - pₓ)² + Dₜ(Bᵧ(t) - pᵧ)²
49
+ //
50
+ // Considering just one component,
51
+ // Dₜ(Bₓ(t) - pₓ)² = 2(Bₓ(t) - pₓ)(DₜBₓ(t))
52
+ // = 2(Bₓ(t)DₜBₓ(t) - pₓBₓ(t))
53
+ // = 2(p0ₓ + (t)(-2p0ₓ + 2p1ₓ) + (t²)(p0ₓ - 2p1ₓ + p2ₓ) - pₓ)((-2p0ₓ + 2p1ₓ) + 2(t)(p0ₓ - 2p1ₓ + p2ₓ))
54
+ // - (pₓ)((-2p0ₓ + 2p1ₓ) + (t)(p0ₓ - 2p1ₓ + p2ₓ))
55
+ const A = this.p0.x - point.x;
56
+ const B = -2 * this.p0.x + 2 * this.p1.x;
57
+ const C = this.p0.x - 2 * this.p1.x + this.p2.x;
58
+ // Let A = p0ₓ - pₓ, B = -2p0ₓ + 2p1ₓ, C = p0ₓ - 2p1ₓ + p2ₓ. We then have,
59
+ // Dₜ(Bₓ(t) - pₓ)²
60
+ // = 2(A + tB + t²C)(B + 2tC) - (pₓ)(B + 2tC)
61
+ // = 2(AB + tB² + t²BC + 2tCA + 2tCtB + 2tCt²C) - pₓB - pₓ2tC
62
+ // = 2(AB + tB² + 2tCA + t²BC + 2t²CB + 2C²t³) - pₓB - pₓ2tC
63
+ // = 2AB + 2t(B² + 2CA) + 2t²(BC + 2CB) + 4C²t³ - pₓB - pₓ2tC
64
+ // = 2AB + 2t(B² + 2CA - pₓC) + 2t²(BC + 2CB) + 4C²t³ - pₓB
65
+ //
66
+ const D = this.p0.y - point.y;
67
+ const E = -2 * this.p0.y + 2 * this.p1.y;
68
+ const F = this.p0.y - 2 * this.p1.y + this.p2.y;
69
+ // Using D = p0ᵧ - pᵧ, E = -2p0ᵧ + 2p1ᵧ, F = p0ᵧ - 2p1ᵧ + p2ᵧ, we thus have,
70
+ // f'(t) = 2AB + 2t(B² + 2CA - pₓC) + 2t²(BC + 2CB) + 4C²t³ - pₓB
71
+ // + 2DE + 2t(E² + 2FD - pᵧF) + 2t²(EF + 2FE) + 4F²t³ - pᵧE
72
+ const a = 2 * A * B + 2 * D * E - point.x * B - point.y * E;
73
+ const b = 2 * B * B + 2 * E * E + 2 * C * A + 2 * F * D - point.x * C - point.y * F;
74
+ const c = 2 * E * F + 2 * B * C + 2 * C * B + 2 * F * E;
75
+ //const d = 4 * C * C + 4 * F * F;
76
+ // Thus,
77
+ // f'(t) = a + bt + ct² + dt³
78
+ const fDerivAtZero = a;
79
+ const f2ndDerivAtZero = b;
80
+ const f3rdDerivAtZero = 2 * c;
81
+ // Using the first few terms of a Maclaurin series to approximate f'(t),
82
+ // f'(t) ≈ f'(0) + t f''(0) + t² f'''(0) / 2
83
+ let [min1, min2] = (0, solveQuadratic_1.default)(f3rdDerivAtZero / 2, f2ndDerivAtZero, fDerivAtZero);
84
+ // If the quadratic has no solutions, approximate.
85
+ if (isNaN(min1)) {
86
+ min1 = 0.25;
87
+ }
88
+ if (isNaN(min2)) {
89
+ min2 = 0.75;
90
+ }
91
+ const at1 = this.at(min1);
92
+ const at2 = this.at(min2);
93
+ const sqrDist1 = at1.minus(point).magnitudeSquared();
94
+ const sqrDist2 = at2.minus(point).magnitudeSquared();
95
+ const sqrDist3 = this.at(0).minus(point).magnitudeSquared();
96
+ const sqrDist4 = this.at(1).minus(point).magnitudeSquared();
97
+ return Math.sqrt(Math.min(sqrDist1, sqrDist2, sqrDist3, sqrDist4));
98
+ }
99
+ /**
100
+ * @returns the exact distance from `point` to this.
101
+ */
102
+ distance(point) {
103
+ if (!this.bezierJs) {
104
+ this.bezierJs = new bezier_js_1.Bezier([this.p0.xy, this.p1.xy, this.p2.xy]);
105
+ }
106
+ const proj = Vec2_1.Vec2.ofXY(this.bezierJs.project(point.xy));
107
+ const dist = proj.minus(point).magnitude();
108
+ return dist;
109
+ }
110
+ normal(t) {
111
+ const tangent = this.derivativeAt(t);
112
+ return tangent.orthog().normalized();
113
+ }
114
+ }
115
+ exports.default = QuadraticBezier;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Solves an equation of the form ax² + bx + c = 0.
3
+ * The larger solution is returned first.
4
+ */
5
+ declare const solveQuadratic: (a: number, b: number, c: number) => [number, number];
6
+ export default solveQuadratic;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Solves an equation of the form ax² + bx + c = 0.
5
+ * The larger solution is returned first.
6
+ */
7
+ const solveQuadratic = (a, b, c) => {
8
+ // See also https://en.wikipedia.org/wiki/Quadratic_formula
9
+ if (a === 0) {
10
+ let solution;
11
+ if (b === 0) {
12
+ solution = c === 0 ? 0 : NaN;
13
+ }
14
+ else {
15
+ // Then we have bx + c = 0
16
+ // which implies bx = -c.
17
+ // Thus, x = -c/b
18
+ solution = -c / b;
19
+ }
20
+ return [solution, solution];
21
+ }
22
+ const discriminant = b * b - 4 * a * c;
23
+ if (discriminant < 0) {
24
+ return [NaN, NaN];
25
+ }
26
+ const rootDiscriminant = Math.sqrt(discriminant);
27
+ const solution1 = (-b + rootDiscriminant) / (2 * a);
28
+ const solution2 = (-b - rootDiscriminant) / (2 * a);
29
+ if (solution1 > solution2) {
30
+ return [solution1, solution2];
31
+ }
32
+ else {
33
+ return [solution2, solution1];
34
+ }
35
+ };
36
+ exports.default = solveQuadratic;
@@ -1,9 +1,9 @@
1
1
  import Color4 from '../Color4';
2
2
  interface RenderingStyle {
3
- fill: Color4;
4
- stroke?: {
5
- color: Color4;
6
- width: number;
3
+ readonly fill: Color4;
4
+ readonly stroke?: {
5
+ readonly color: Color4;
6
+ readonly width: number;
7
7
  };
8
8
  }
9
9
  export default RenderingStyle;
@@ -1,13 +1,13 @@
1
1
  import RenderingStyle from './RenderingStyle';
2
- export interface TextStyle {
3
- size: number;
4
- fontFamily: string;
5
- fontWeight?: string;
6
- fontVariant?: string;
7
- renderingStyle: RenderingStyle;
2
+ export interface TextRenderingStyle {
3
+ readonly size: number;
4
+ readonly fontFamily: string;
5
+ readonly fontWeight?: string;
6
+ readonly fontVariant?: string;
7
+ readonly renderingStyle: RenderingStyle;
8
8
  }
9
- export default TextStyle;
10
- export declare const cloneTextStyle: (style: TextStyle) => {
9
+ export default TextRenderingStyle;
10
+ export declare const cloneTextStyle: (style: TextRenderingStyle) => {
11
11
  renderingStyle: {
12
12
  fill: import("../Color4").default;
13
13
  stroke: {
@@ -20,8 +20,8 @@ export declare const cloneTextStyle: (style: TextStyle) => {
20
20
  fontWeight?: string | undefined;
21
21
  fontVariant?: string | undefined;
22
22
  };
23
- export declare const textStyleFromJSON: (json: any) => TextStyle;
24
- export declare const textStyleToJSON: (style: TextStyle) => {
23
+ export declare const textStyleFromJSON: (json: any) => TextRenderingStyle;
24
+ export declare const textStyleToJSON: (style: TextRenderingStyle) => {
25
25
  renderingStyle: {
26
26
  fill: string;
27
27
  stroke: {
@@ -3,3 +3,5 @@ export { default as DummyRenderer } from './renderers/DummyRenderer';
3
3
  export { default as SVGRenderer } from './renderers/SVGRenderer';
4
4
  export { default as CanvasRenderer } from './renderers/CanvasRenderer';
5
5
  export { default as Display, RenderingMode } from './Display';
6
+ export { default as TextRenderingStyle } from './TextRenderingStyle';
7
+ export { default as RenderingStyle } from './RenderingStyle';
@@ -6,7 +6,7 @@ import Rect2 from '../../math/Rect2';
6
6
  import { Point2, Vec2 } from '../../math/Vec2';
7
7
  import Viewport from '../../Viewport';
8
8
  import RenderingStyle from '../RenderingStyle';
9
- import TextStyle from '../TextRenderingStyle';
9
+ import TextRenderingStyle from '../TextRenderingStyle';
10
10
  export interface RenderablePathSpec {
11
11
  startPoint: Point2;
12
12
  commands: PathCommand[];
@@ -33,7 +33,7 @@ export default abstract class AbstractRenderer {
33
33
  protected abstract moveTo(point: Point2): void;
34
34
  protected abstract traceCubicBezierCurve(p1: Point2, p2: Point2, p3: Point2): void;
35
35
  protected abstract traceQuadraticBezierCurve(controlPoint: Point2, endPoint: Point2): void;
36
- abstract drawText(text: string, transform: Mat33, style: TextStyle): void;
36
+ abstract drawText(text: string, transform: Mat33, style: TextRenderingStyle): void;
37
37
  abstract drawImage(image: RenderableImage): void;
38
38
  abstract isTooSmallToRender(rect: Rect2): boolean;
39
39
  setDraftMode(_draftMode: boolean): void;
@@ -4,7 +4,7 @@ import { Point2, Vec2 } from '../../math/Vec2';
4
4
  import Vec3 from '../../math/Vec3';
5
5
  import Viewport from '../../Viewport';
6
6
  import RenderingStyle from '../RenderingStyle';
7
- import TextStyle from '../TextRenderingStyle';
7
+ import TextRenderingStyle from '../TextRenderingStyle';
8
8
  import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
9
9
  /**
10
10
  * Renders onto a `CanvasRenderingContext2D`.
@@ -53,7 +53,7 @@ export default class CanvasRenderer extends AbstractRenderer {
53
53
  protected traceCubicBezierCurve(p1: Point2, p2: Point2, p3: Point2): void;
54
54
  protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3): void;
55
55
  drawPath(path: RenderablePathSpec): void;
56
- drawText(text: string, transform: Mat33, style: TextStyle): void;
56
+ drawText(text: string, transform: Mat33, style: TextRenderingStyle): void;
57
57
  drawImage(image: RenderableImage): void;
58
58
  private clipLevels;
59
59
  startObject(boundingBox: Rect2, clip?: boolean): void;
@@ -191,14 +191,16 @@ class CanvasRenderer extends AbstractRenderer_1.default {
191
191
  }
192
192
  }
193
193
  endObject() {
194
+ // Cache this.objectLevel — it may be decremented by super.endObject.
195
+ const objectLevel = this.objectLevel;
196
+ this.currentObjectBBox = null;
197
+ super.endObject();
194
198
  if (!this.ignoringObject && this.clipLevels.length > 0) {
195
- if (this.clipLevels[this.clipLevels.length - 1] === this.objectLevel) {
199
+ if (this.clipLevels[this.clipLevels.length - 1] === objectLevel) {
196
200
  this.ctx.restore();
197
201
  this.clipLevels.pop();
198
202
  }
199
203
  }
200
- this.currentObjectBBox = null;
201
- super.endObject();
202
204
  // If exiting an object with a too-small-to-draw bounding box,
203
205
  if (this.ignoreObjectsAboveLevel !== null && this.getNestingLevel() <= this.ignoreObjectsAboveLevel) {
204
206
  this.ignoreObjectsAboveLevel = null;
@@ -4,7 +4,7 @@ import { Point2, Vec2 } from '../../math/Vec2';
4
4
  import Vec3 from '../../math/Vec3';
5
5
  import Viewport from '../../Viewport';
6
6
  import RenderingStyle from '../RenderingStyle';
7
- import TextStyle from '../TextRenderingStyle';
7
+ import TextRenderingStyle from '../TextRenderingStyle';
8
8
  import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
9
9
  export default class DummyRenderer extends AbstractRenderer {
10
10
  clearedCount: number;
@@ -25,7 +25,7 @@ export default class DummyRenderer extends AbstractRenderer {
25
25
  protected traceCubicBezierCurve(p1: Vec3, p2: Vec3, p3: Vec3): void;
26
26
  protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3): void;
27
27
  drawPoints(..._points: Vec3[]): void;
28
- drawText(text: string, _transform: Mat33, _style: TextStyle): void;
28
+ drawText(text: string, _transform: Mat33, _style: TextRenderingStyle): void;
29
29
  drawImage(image: RenderableImage): void;
30
30
  startObject(boundingBox: Rect2, _clip: boolean): void;
31
31
  endObject(): void;
@@ -4,7 +4,7 @@ import Rect2 from '../../math/Rect2';
4
4
  import { Point2, Vec2 } from '../../math/Vec2';
5
5
  import Viewport from '../../Viewport';
6
6
  import RenderingStyle from '../RenderingStyle';
7
- import TextStyle from '../TextRenderingStyle';
7
+ import TextRenderingStyle from '../TextRenderingStyle';
8
8
  import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
9
9
  export declare const renderedStylesheetId = "js-draw-style-sheet";
10
10
  /**
@@ -36,7 +36,7 @@ export default class SVGRenderer extends AbstractRenderer {
36
36
  private textContainer;
37
37
  private textContainerTransform;
38
38
  private textParentStyle;
39
- drawText(text: string, transform: Mat33, style: TextStyle): void;
39
+ drawText(text: string, transform: Mat33, style: TextRenderingStyle): void;
40
40
  drawImage(image: RenderableImage): void;
41
41
  startObject(boundingBox: Rect2): void;
42
42
  endObject(loaderData?: LoadSaveDataTable, elemClassNames?: string[]): void;
@@ -9,6 +9,7 @@ const Path_1 = __importDefault(require("../../math/Path"));
9
9
  const rounding_1 = require("../../math/rounding");
10
10
  const Vec2_1 = require("../../math/Vec2");
11
11
  const SVGLoader_1 = require("../../SVGLoader");
12
+ const RenderingStyle_1 = require("../RenderingStyle");
12
13
  const AbstractRenderer_1 = __importDefault(require("./AbstractRenderer"));
13
14
  exports.renderedStylesheetId = 'js-draw-style-sheet';
14
15
  const svgNameSpace = 'http://www.w3.org/2000/svg';
@@ -114,11 +115,10 @@ class SVGRenderer extends AbstractRenderer_1.default {
114
115
  (_a = this.objectElems) === null || _a === void 0 ? void 0 : _a.push(pathElem);
115
116
  }
116
117
  drawPath(pathSpec) {
117
- var _a;
118
118
  const style = pathSpec.style;
119
119
  const path = Path_1.default.fromRenderable(pathSpec).transformedBy(this.getCanvasToScreenTransform());
120
120
  // Try to extend the previous path, if possible
121
- if (!style.fill.eq((_a = this.lastPathStyle) === null || _a === void 0 ? void 0 : _a.fill) || this.lastPathString.length === 0) {
121
+ if (this.lastPathString.length === 0 || !this.lastPathStyle || !(0, RenderingStyle_1.stylesEqual)(this.lastPathStyle, style)) {
122
122
  this.addPathToSVG();
123
123
  this.lastPathStyle = style;
124
124
  this.lastPathString = [];
@@ -232,7 +232,7 @@ class SVGRenderer extends AbstractRenderer_1.default {
232
232
  this.objectElems = [];
233
233
  }
234
234
  endObject(loaderData, elemClassNames) {
235
- var _a, _b;
235
+ var _a;
236
236
  super.endObject(loaderData);
237
237
  // Don't extend paths across objects
238
238
  this.addPathToSVG();
@@ -254,9 +254,18 @@ class SVGRenderer extends AbstractRenderer_1.default {
254
254
  }
255
255
  }
256
256
  // Add class names to the object, if given.
257
- if (elemClassNames) {
258
- for (const elem of (_b = this.objectElems) !== null && _b !== void 0 ? _b : []) {
259
- elem.classList.add(...elemClassNames);
257
+ if (elemClassNames && this.objectElems) {
258
+ if (this.objectElems.length === 1) {
259
+ this.objectElems[0].classList.add(...elemClassNames);
260
+ }
261
+ else {
262
+ const wrapper = document.createElementNS(svgNameSpace, 'g');
263
+ wrapper.classList.add(...elemClassNames);
264
+ for (const elem of this.objectElems) {
265
+ elem.remove();
266
+ wrapper.appendChild(elem);
267
+ }
268
+ this.elem.appendChild(wrapper);
260
269
  }
261
270
  }
262
271
  }
@@ -4,7 +4,7 @@ import Vec3 from '../../math/Vec3';
4
4
  import Viewport from '../../Viewport';
5
5
  import { TextRendererLocalization } from '../localization';
6
6
  import RenderingStyle from '../RenderingStyle';
7
- import TextStyle from '../TextRenderingStyle';
7
+ import TextRenderingStyle from '../TextRenderingStyle';
8
8
  import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
9
9
  export default class TextOnlyRenderer extends AbstractRenderer {
10
10
  private localizationTable;
@@ -22,7 +22,7 @@ export default class TextOnlyRenderer extends AbstractRenderer {
22
22
  protected moveTo(_point: Vec3): void;
23
23
  protected traceCubicBezierCurve(_p1: Vec3, _p2: Vec3, _p3: Vec3): void;
24
24
  protected traceQuadraticBezierCurve(_controlPoint: Vec3, _endPoint: Vec3): void;
25
- drawText(text: string, _transform: Mat33, _style: TextStyle): void;
25
+ drawText(text: string, _transform: Mat33, _style: TextRenderingStyle): void;
26
26
  drawImage(image: RenderableImage): void;
27
27
  isTooSmallToRender(rect: Rect2): boolean;
28
28
  drawPoints(..._points: Vec3[]): void;