js-draw 0.2.2 → 0.3.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 (78) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +2 -2
  3. package/dist/bundle.js +1 -1
  4. package/dist/src/EditorImage.js +0 -1
  5. package/dist/src/components/builders/FreehandLineBuilder.js +18 -9
  6. package/dist/src/components/lib.d.ts +2 -0
  7. package/dist/src/components/lib.js +2 -0
  8. package/dist/src/lib.d.ts +5 -1
  9. package/dist/src/lib.js +5 -1
  10. package/dist/src/rendering/Display.js +2 -2
  11. package/dist/src/rendering/caching/RenderingCacheNode.js +2 -1
  12. package/dist/src/rendering/renderers/CanvasRenderer.js +6 -6
  13. package/dist/src/toolbar/HTMLToolbar.d.ts +4 -1
  14. package/dist/src/toolbar/HTMLToolbar.js +29 -31
  15. package/dist/src/toolbar/icons.d.ts +1 -1
  16. package/dist/src/toolbar/icons.js +4 -0
  17. package/dist/src/toolbar/lib.d.ts +3 -0
  18. package/dist/src/toolbar/lib.js +4 -0
  19. package/dist/src/toolbar/makeColorInput.js +2 -2
  20. package/dist/src/toolbar/types.d.ts +0 -4
  21. package/dist/src/toolbar/types.js +1 -5
  22. package/dist/src/toolbar/widgets/{EraserWidget.d.ts → EraserToolWidget.d.ts} +1 -1
  23. package/dist/src/toolbar/widgets/{EraserWidget.js → EraserToolWidget.js} +1 -1
  24. package/dist/src/toolbar/widgets/{PenWidget.d.ts → PenToolWidget.d.ts} +2 -3
  25. package/dist/src/toolbar/widgets/{PenWidget.js → PenToolWidget.js} +6 -7
  26. package/dist/src/toolbar/widgets/{SelectionWidget.d.ts → SelectionToolWidget.d.ts} +1 -1
  27. package/dist/src/toolbar/widgets/{SelectionWidget.js → SelectionToolWidget.js} +1 -1
  28. package/dist/src/toolbar/widgets/lib.d.ts +8 -0
  29. package/dist/src/toolbar/widgets/lib.js +8 -0
  30. package/dist/src/tools/BaseTool.d.ts +0 -2
  31. package/dist/src/tools/Eraser.d.ts +0 -2
  32. package/dist/src/tools/Eraser.js +0 -2
  33. package/dist/src/tools/PanZoom.d.ts +0 -2
  34. package/dist/src/tools/PanZoom.js +0 -2
  35. package/dist/src/tools/Pen.d.ts +8 -9
  36. package/dist/src/tools/Pen.js +15 -9
  37. package/dist/src/tools/PipetteTool.d.ts +0 -2
  38. package/dist/src/tools/PipetteTool.js +0 -2
  39. package/dist/src/tools/SelectionTool.d.ts +0 -2
  40. package/dist/src/tools/SelectionTool.js +4 -3
  41. package/dist/src/tools/TextTool.d.ts +0 -2
  42. package/dist/src/tools/TextTool.js +0 -2
  43. package/dist/src/tools/ToolController.d.ts +7 -11
  44. package/dist/src/tools/ToolController.js +28 -16
  45. package/dist/src/tools/ToolEnabledGroup.js +1 -1
  46. package/dist/src/tools/UndoRedoShortcut.d.ts +0 -2
  47. package/dist/src/tools/UndoRedoShortcut.js +3 -2
  48. package/dist/src/tools/lib.d.ts +12 -0
  49. package/dist/src/tools/lib.js +12 -0
  50. package/package.json +1 -1
  51. package/src/EditorImage.ts +0 -1
  52. package/src/components/builders/FreehandLineBuilder.ts +27 -17
  53. package/src/components/lib.ts +3 -0
  54. package/src/lib.ts +5 -1
  55. package/src/rendering/Display.ts +2 -2
  56. package/src/rendering/caching/RenderingCacheNode.ts +3 -1
  57. package/src/rendering/renderers/CanvasRenderer.ts +6 -6
  58. package/src/toolbar/HTMLToolbar.ts +34 -37
  59. package/src/toolbar/icons.ts +5 -1
  60. package/src/toolbar/lib.ts +4 -0
  61. package/src/toolbar/makeColorInput.ts +1 -2
  62. package/src/toolbar/types.ts +0 -4
  63. package/src/toolbar/widgets/{EraserWidget.ts → EraserToolWidget.ts} +1 -1
  64. package/src/toolbar/widgets/{PenWidget.ts → PenToolWidget.ts} +10 -9
  65. package/src/toolbar/widgets/{SelectionWidget.ts → SelectionToolWidget.ts} +1 -1
  66. package/src/toolbar/widgets/lib.ts +10 -0
  67. package/src/tools/BaseTool.ts +0 -3
  68. package/src/tools/Eraser.ts +0 -2
  69. package/src/tools/PanZoom.ts +0 -2
  70. package/src/tools/Pen.ts +21 -15
  71. package/src/tools/PipetteTool.ts +0 -3
  72. package/src/tools/SelectionTool.test.ts +1 -2
  73. package/src/tools/SelectionTool.ts +5 -3
  74. package/src/tools/TextTool.ts +0 -2
  75. package/src/tools/ToolController.ts +34 -17
  76. package/src/tools/ToolEnabledGroup.ts +1 -1
  77. package/src/tools/UndoRedoShortcut.ts +4 -4
  78. package/src/tools/lib.ts +17 -0
@@ -3,7 +3,6 @@ import { PointerDevice } from '../Pointer';
3
3
  import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
4
4
  import { EditorEventType } from '../types';
5
5
  import BaseTool from './BaseTool';
6
- import { ToolType } from './ToolController';
7
6
  export default class Pen extends BaseTool {
8
7
  constructor(editor, description, style) {
9
8
  super(editor.notifier, description);
@@ -12,12 +11,12 @@ export default class Pen extends BaseTool {
12
11
  this.builder = null;
13
12
  this.builderFactory = makeFreehandLineBuilder;
14
13
  this.lastPoint = null;
15
- this.kind = ToolType.Pen;
16
14
  }
17
15
  getPressureMultiplier() {
18
16
  return 1 / this.editor.viewport.getScaleFactor() * this.style.thickness;
19
17
  }
20
- getStrokePoint(pointer) {
18
+ // Converts a `pointer` to a `StrokeDataPoint`.
19
+ toStrokePoint(pointer) {
21
20
  var _a;
22
21
  const minPressure = 0.3;
23
22
  let pressure = Math.max((_a = pointer.pressure) !== null && _a !== void 0 ? _a : 1.0, minPressure);
@@ -35,11 +34,13 @@ export default class Pen extends BaseTool {
35
34
  time: pointer.timeStamp,
36
35
  };
37
36
  }
37
+ // Displays the stroke that is currently being built with the display's `wetInkRenderer`.
38
38
  previewStroke() {
39
39
  var _a;
40
40
  this.editor.clearWetInk();
41
41
  (_a = this.builder) === null || _a === void 0 ? void 0 : _a.preview(this.editor.display.getWetInkRenderer());
42
42
  }
43
+ // Throws if no stroke builder exists.
43
44
  addPointToStroke(point) {
44
45
  if (!this.builder) {
45
46
  throw new Error('No stroke is currently being generated.');
@@ -49,17 +50,22 @@ export default class Pen extends BaseTool {
49
50
  this.previewStroke();
50
51
  }
51
52
  onPointerDown({ current, allPointers }) {
52
- if (current.device === PointerDevice.Eraser) {
53
- return false;
53
+ const isEraser = current.device === PointerDevice.Eraser;
54
+ let anyDeviceIsStylus = false;
55
+ for (const pointer of allPointers) {
56
+ if (pointer.device === PointerDevice.Pen) {
57
+ anyDeviceIsStylus = true;
58
+ break;
59
+ }
54
60
  }
55
- if (allPointers.length === 1 || current.device === PointerDevice.Pen) {
56
- this.builder = this.builderFactory(this.getStrokePoint(current), this.editor.viewport);
61
+ if ((allPointers.length === 1 && !isEraser) || anyDeviceIsStylus) {
62
+ this.builder = this.builderFactory(this.toStrokePoint(current), this.editor.viewport);
57
63
  return true;
58
64
  }
59
65
  return false;
60
66
  }
61
67
  onPointerMove({ current }) {
62
- this.addPointToStroke(this.getStrokePoint(current));
68
+ this.addPointToStroke(this.toStrokePoint(current));
63
69
  }
64
70
  onPointerUp({ current }) {
65
71
  var _a, _b;
@@ -67,7 +73,7 @@ export default class Pen extends BaseTool {
67
73
  return;
68
74
  }
69
75
  // onPointerUp events can have zero pressure. Use the last pressure instead.
70
- const currentPoint = this.getStrokePoint(current);
76
+ const currentPoint = this.toStrokePoint(current);
71
77
  const strokePoint = Object.assign(Object.assign({}, currentPoint), { width: (_b = (_a = this.lastPoint) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : currentPoint.width });
72
78
  this.addPointToStroke(strokePoint);
73
79
  if (this.builder && current.isPrimary) {
@@ -2,11 +2,9 @@ import Color4 from '../Color4';
2
2
  import Editor from '../Editor';
3
3
  import { PointerEvt } from '../types';
4
4
  import BaseTool from './BaseTool';
5
- import { ToolType } from './ToolController';
6
5
  declare type ColorListener = (color: Color4 | null) => void;
7
6
  export default class PipetteTool extends BaseTool {
8
7
  private editor;
9
- kind: ToolType;
10
8
  private colorPreviewListener;
11
9
  private colorSelectListener;
12
10
  constructor(editor: Editor, description: string);
@@ -1,11 +1,9 @@
1
1
  // @internal @packageDocumentation
2
2
  import BaseTool from './BaseTool';
3
- import { ToolType } from './ToolController';
4
3
  export default class PipetteTool extends BaseTool {
5
4
  constructor(editor, description) {
6
5
  super(editor.notifier, description);
7
6
  this.editor = editor;
8
- this.kind = ToolType.Pipette;
9
7
  this.colorPreviewListener = null;
10
8
  this.colorSelectListener = null;
11
9
  }
@@ -5,7 +5,6 @@ import Rect2 from '../math/Rect2';
5
5
  import { Point2, Vec2 } from '../math/Vec2';
6
6
  import { KeyPressEvent, KeyUpEvent, PointerEvt } from '../types';
7
7
  import BaseTool from './BaseTool';
8
- import { ToolType } from './ToolController';
9
8
  declare class Selection {
10
9
  startPoint: Point2;
11
10
  private editor;
@@ -43,7 +42,6 @@ export default class SelectionTool extends BaseTool {
43
42
  private handleOverlay;
44
43
  private prevSelectionBox;
45
44
  private selectionBox;
46
- readonly kind: ToolType;
47
45
  constructor(editor: Editor, description: string);
48
46
  onPointerDown(event: PointerEvt): boolean;
49
47
  onPointerMove(event: PointerEvt): void;
@@ -1,3 +1,6 @@
1
+ // Allows users to select/transform portions of the `EditorImage`.
2
+ // With respect to `extend`ing, `SelectionTool` is not stable.
3
+ // @packageDocumentation
1
4
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
5
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
6
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -11,13 +14,11 @@ var _a;
11
14
  import Duplicate from '../commands/Duplicate';
12
15
  import Erase from '../commands/Erase';
13
16
  import Mat33 from '../math/Mat33';
14
- // import Mat33 from "../geometry/Mat33";
15
17
  import Rect2 from '../math/Rect2';
16
18
  import { Vec2 } from '../math/Vec2';
17
19
  import { EditorEventType } from '../types';
18
20
  import Viewport from '../Viewport';
19
21
  import BaseTool from './BaseTool';
20
- import { ToolType } from './ToolController';
21
22
  import SerializableCommand from '../commands/SerializableCommand';
22
23
  const handleScreenSize = 30;
23
24
  const styles = `
@@ -409,11 +410,11 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
409
410
  return localizationTable.transformedElements(this.currentTransfmCommands.length);
410
411
  }
411
412
  };
413
+ // {@inheritDoc SelectionTool!}
412
414
  export default class SelectionTool extends BaseTool {
413
415
  constructor(editor, description) {
414
416
  super(editor.notifier, description);
415
417
  this.editor = editor;
416
- this.kind = ToolType.Selection;
417
418
  this.handleOverlay = document.createElement('div');
418
419
  editor.createHTMLOverlay(this.handleOverlay);
419
420
  editor.addStyleSheet(styles);
@@ -4,11 +4,9 @@ import Editor from '../Editor';
4
4
  import { PointerEvt } from '../types';
5
5
  import BaseTool from './BaseTool';
6
6
  import { ToolLocalization } from './localization';
7
- import { ToolType } from './ToolController';
8
7
  export default class TextTool extends BaseTool {
9
8
  private editor;
10
9
  private localizationTable;
11
- kind: ToolType;
12
10
  private textStyle;
13
11
  private textEditOverlay;
14
12
  private textInputElem;
@@ -5,14 +5,12 @@ import Mat33 from '../math/Mat33';
5
5
  import { PointerDevice } from '../Pointer';
6
6
  import { EditorEventType } from '../types';
7
7
  import BaseTool from './BaseTool';
8
- import { ToolType } from './ToolController';
9
8
  const overlayCssClass = 'textEditorOverlay';
10
9
  export default class TextTool extends BaseTool {
11
10
  constructor(editor, description, localizationTable) {
12
11
  super(editor.notifier, description);
13
12
  this.editor = editor;
14
13
  this.localizationTable = localizationTable;
15
- this.kind = ToolType.Text;
16
14
  this.textInputElem = null;
17
15
  this.textTargetPosition = null;
18
16
  this.textMeasuringCtx = null;
@@ -1,21 +1,17 @@
1
1
  import { InputEvt } from '../types';
2
2
  import Editor from '../Editor';
3
3
  import BaseTool from './BaseTool';
4
+ import ToolEnabledGroup from './ToolEnabledGroup';
4
5
  import { ToolLocalization } from './localization';
5
- export declare enum ToolType {
6
- Pen = 0,
7
- Selection = 1,
8
- Eraser = 2,
9
- PanZoom = 3,
10
- Text = 4,
11
- UndoRedoShortcut = 5,
12
- Pipette = 6,
13
- Other = 7
14
- }
15
6
  export default class ToolController {
16
7
  private tools;
17
8
  private activeTool;
9
+ private primaryToolGroup;
10
+ /** @internal */
18
11
  constructor(editor: Editor, localization: ToolLocalization);
12
+ setTools(tools: BaseTool[], primaryToolGroup?: ToolEnabledGroup): void;
13
+ addPrimaryTool(tool: BaseTool): void;
14
+ addTool(tool: BaseTool): void;
19
15
  dispatchInputEvent(event: InputEvt): boolean;
20
- getMatchingTools(kind: ToolType): BaseTool[];
16
+ getMatchingTools<Type extends BaseTool>(type: new (...args: any[]) => Type): Type[];
21
17
  }
@@ -8,20 +8,12 @@ import Color4 from '../Color4';
8
8
  import UndoRedoShortcut from './UndoRedoShortcut';
9
9
  import TextTool from './TextTool';
10
10
  import PipetteTool from './PipetteTool';
11
- export var ToolType;
12
- (function (ToolType) {
13
- ToolType[ToolType["Pen"] = 0] = "Pen";
14
- ToolType[ToolType["Selection"] = 1] = "Selection";
15
- ToolType[ToolType["Eraser"] = 2] = "Eraser";
16
- ToolType[ToolType["PanZoom"] = 3] = "PanZoom";
17
- ToolType[ToolType["Text"] = 4] = "Text";
18
- ToolType[ToolType["UndoRedoShortcut"] = 5] = "UndoRedoShortcut";
19
- ToolType[ToolType["Pipette"] = 6] = "Pipette";
20
- ToolType[ToolType["Other"] = 7] = "Other";
21
- })(ToolType || (ToolType = {}));
22
11
  export default class ToolController {
12
+ /** @internal */
23
13
  constructor(editor, localization) {
24
- const primaryToolEnabledGroup = new ToolEnabledGroup();
14
+ this.activeTool = null;
15
+ const primaryToolGroup = new ToolEnabledGroup();
16
+ this.primaryToolGroup = primaryToolGroup;
25
17
  const panZoomTool = new PanZoom(editor, PanZoomMode.TwoFingerTouchGestures | PanZoomMode.RightClickDrags, localization.touchPanTool);
26
18
  const keyboardPanZoomTool = new PanZoom(editor, PanZoomMode.Keyboard, localization.keyboardPanZoom);
27
19
  const primaryPenTool = new Pen(editor, localization.penTool(1), { color: Color4.purple, thickness: 16 });
@@ -30,7 +22,7 @@ export default class ToolController {
30
22
  new Eraser(editor, localization.eraserTool),
31
23
  // Three pens
32
24
  primaryPenTool,
33
- new Pen(editor, localization.penTool(2), { color: Color4.clay, thickness: 8 }),
25
+ new Pen(editor, localization.penTool(2), { color: Color4.clay, thickness: 4 }),
34
26
  // Highlighter-like pen with width=64
35
27
  new Pen(editor, localization.penTool(3), { color: Color4.ofRGBA(1, 1, 0, 0.5), thickness: 64 }),
36
28
  new TextTool(editor, localization.textTool, localization),
@@ -42,7 +34,7 @@ export default class ToolController {
42
34
  keyboardPanZoomTool,
43
35
  new UndoRedoShortcut(editor),
44
36
  ];
45
- primaryTools.forEach(tool => tool.setToolGroup(primaryToolEnabledGroup));
37
+ primaryTools.forEach(tool => tool.setToolGroup(primaryToolGroup));
46
38
  panZoomTool.setEnabled(true);
47
39
  primaryPenTool.setEnabled(true);
48
40
  editor.notifier.on(EditorEventType.ToolEnabled, event => {
@@ -57,6 +49,26 @@ export default class ToolController {
57
49
  });
58
50
  this.activeTool = null;
59
51
  }
52
+ // Replaces the current set of tools with `tools`. This should only be done before
53
+ // the creation of the app's toolbar (if using `HTMLToolbar`).
54
+ setTools(tools, primaryToolGroup) {
55
+ this.tools = tools;
56
+ this.primaryToolGroup = primaryToolGroup !== null && primaryToolGroup !== void 0 ? primaryToolGroup : new ToolEnabledGroup();
57
+ }
58
+ // Add a tool that acts like one of the primary tools (only one primary tool can be enabled at a time).
59
+ // This should be called before creating the app's toolbar.
60
+ addPrimaryTool(tool) {
61
+ tool.setToolGroup(this.primaryToolGroup);
62
+ if (tool.isEnabled()) {
63
+ this.primaryToolGroup.notifyEnabled(tool);
64
+ }
65
+ this.addTool(tool);
66
+ }
67
+ // Add a tool to the end of this' tool list (the added tool receives events after tools already added to this).
68
+ // This should be called before creating the app's toolbar.
69
+ addTool(tool) {
70
+ this.tools.push(tool);
71
+ }
60
72
  // Returns true if the event was handled
61
73
  dispatchInputEvent(event) {
62
74
  var _a, _b;
@@ -116,7 +128,7 @@ export default class ToolController {
116
128
  }
117
129
  return handled;
118
130
  }
119
- getMatchingTools(kind) {
120
- return this.tools.filter(tool => tool.kind === kind);
131
+ getMatchingTools(type) {
132
+ return this.tools.filter(tool => tool instanceof type);
121
133
  }
122
134
  }
@@ -1,4 +1,4 @@
1
- // Connects a group of tools -- at most one tool in the group must be enabled.
1
+ // Connects a group of tools -- at most one tool in the group can be enabled.
2
2
  export default class ToolEnabledGroup {
3
3
  constructor() { }
4
4
  notifyEnabled(tool) {
@@ -1,10 +1,8 @@
1
1
  import Editor from '../Editor';
2
2
  import { KeyPressEvent } from '../types';
3
3
  import BaseTool from './BaseTool';
4
- import { ToolType } from './ToolController';
5
4
  export default class UndoRedoShortcut extends BaseTool {
6
5
  private editor;
7
- kind: ToolType.UndoRedoShortcut;
8
6
  constructor(editor: Editor);
9
7
  onKeyPress({ key, ctrlKey }: KeyPressEvent): boolean;
10
8
  }
@@ -1,10 +1,11 @@
1
+ // Handles ctrl+Z, ctrl+Shift+Z keyboard shortcuts.
2
+ // @packageDocumentation
1
3
  import BaseTool from './BaseTool';
2
- import { ToolType } from './ToolController';
4
+ // {@inheritDoc UndoRedoShortcut!}
3
5
  export default class UndoRedoShortcut extends BaseTool {
4
6
  constructor(editor) {
5
7
  super(editor.notifier, editor.localization.undoRedoTool);
6
8
  this.editor = editor;
7
- this.kind = ToolType.UndoRedoShortcut;
8
9
  }
9
10
  // Activate undo/redo
10
11
  onKeyPress({ key, ctrlKey }) {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @packageDocumentation
3
+ */
4
+ export { default as BaseTool } from './BaseTool';
5
+ export { default as ToolController } from './ToolController';
6
+ export { default as ToolEnabledGroup } from './ToolEnabledGroup';
7
+ export { default as UndoRedoShortcut } from './UndoRedoShortcut';
8
+ export { default as PanZoomTool, PanZoomMode } from './PanZoom';
9
+ export { default as PenTool, PenStyle } from './Pen';
10
+ export { default as TextTool } from './TextTool';
11
+ export { default as SelectionTool } from './SelectionTool';
12
+ export { default as EraserTool } from './Eraser';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @packageDocumentation
3
+ */
4
+ export { default as BaseTool } from './BaseTool';
5
+ export { default as ToolController } from './ToolController';
6
+ export { default as ToolEnabledGroup } from './ToolEnabledGroup';
7
+ export { default as UndoRedoShortcut } from './UndoRedoShortcut';
8
+ export { default as PanZoomTool, PanZoomMode } from './PanZoom';
9
+ export { default as PenTool } from './Pen';
10
+ export { default as TextTool } from './TextTool';
11
+ export { default as SelectionTool } from './SelectionTool';
12
+ export { default as EraserTool } from './Eraser';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "main": "./dist/src/lib.d.ts",
6
6
  "types": "./dist/src/lib.js",
@@ -388,7 +388,6 @@ export class ImageNode {
388
388
  }
389
389
 
390
390
  public render(renderer: AbstractRenderer, visibleRect: Rect2) {
391
- // Don't render components that are < 0.1% of the viewport.
392
391
  const leaves = this.getLeavesIntersectingRegion(visibleRect, rect => renderer.isTooSmallToRender(rect));
393
392
  sortLeavesByZIndex(leaves);
394
393
 
@@ -2,7 +2,7 @@ import { Bezier } from 'bezier-js';
2
2
  import AbstractRenderer, { RenderablePathSpec } from '../../rendering/renderers/AbstractRenderer';
3
3
  import { Point2, Vec2 } from '../../math/Vec2';
4
4
  import Rect2 from '../../math/Rect2';
5
- import { LinePathCommand, PathCommandType, QuadraticBezierPathCommand } from '../../math/Path';
5
+ import { LinePathCommand, PathCommand, PathCommandType, QuadraticBezierPathCommand } from '../../math/Path';
6
6
  import LineSegment2 from '../../math/LineSegment2';
7
7
  import Stroke from '../Stroke';
8
8
  import Viewport from '../../Viewport';
@@ -13,9 +13,8 @@ import RenderingStyle from '../../rendering/RenderingStyle';
13
13
  export const makeFreehandLineBuilder: ComponentBuilderFactory = (initialPoint: StrokeDataPoint, viewport: Viewport) => {
14
14
  // Don't smooth if input is more than ± 7 pixels from the true curve, do smooth if
15
15
  // less than ± 2 px from the curve.
16
- const canvasTransform = viewport.screenToCanvasTransform;
17
- const maxSmoothingDist = canvasTransform.transformVec3(Vec2.unitX).magnitude() * 7;
18
- const minSmoothingDist = canvasTransform.transformVec3(Vec2.unitX).magnitude() * 2;
16
+ const maxSmoothingDist = viewport.getSizeOfPixelOnCanvas() * 7;
17
+ const minSmoothingDist = viewport.getSizeOfPixelOnCanvas() * 2;
19
18
 
20
19
  return new FreehandLineBuilder(
21
20
  initialPoint, minSmoothingDist, maxSmoothingDist
@@ -24,16 +23,16 @@ export const makeFreehandLineBuilder: ComponentBuilderFactory = (initialPoint: S
24
23
 
25
24
  type CurrentSegmentToPathResult = {
26
25
  upperCurve: QuadraticBezierPathCommand,
27
- lowerToUpperConnector: LinePathCommand,
28
- upperToLowerConnector: LinePathCommand,
26
+ lowerToUpperConnector: PathCommand,
27
+ upperToLowerConnector: PathCommand,
29
28
  lowerCurve: QuadraticBezierPathCommand,
30
29
  };
31
30
 
32
31
  // Handles stroke smoothing and creates Strokes from user/stylus input.
33
32
  export default class FreehandLineBuilder implements ComponentBuilder {
34
33
  private isFirstSegment: boolean = true;
35
- private pathStartConnector: LinePathCommand|null = null;
36
- private mostRecentConnector: LinePathCommand|null = null;
34
+ private pathStartConnector: PathCommand|null = null;
35
+ private mostRecentConnector: PathCommand|null = null;
37
36
 
38
37
  // Beginning of the list of lower parts
39
38
  // ↓
@@ -53,7 +52,7 @@ export default class FreehandLineBuilder implements ComponentBuilder {
53
52
 
54
53
  private buffer: Point2[];
55
54
  private lastPoint: StrokeDataPoint;
56
- private lastExitingVec: Vec2;
55
+ private lastExitingVec: Vec2|null = null;
57
56
  private currentCurve: Bezier|null = null;
58
57
  private curveStartWidth: number;
59
58
  private curveEndWidth: number;
@@ -79,6 +78,7 @@ export default class FreehandLineBuilder implements ComponentBuilder {
79
78
  this.buffer = [this.startPoint.pos];
80
79
  this.momentum = Vec2.zero;
81
80
  this.currentCurve = null;
81
+ this.curveStartWidth = startPoint.width;
82
82
 
83
83
  this.bbox = new Rect2(this.startPoint.pos.x, this.startPoint.pos.y, 0, 0);
84
84
  }
@@ -96,8 +96,8 @@ export default class FreehandLineBuilder implements ComponentBuilder {
96
96
  private previewPath(): RenderablePathSpec|null {
97
97
  let upperPath: QuadraticBezierPathCommand[];
98
98
  let lowerPath: QuadraticBezierPathCommand[];
99
- let lowerToUpperCap: LinePathCommand;
100
- let pathStartConnector: LinePathCommand;
99
+ let lowerToUpperCap: PathCommand;
100
+ let pathStartConnector: PathCommand;
101
101
  if (this.currentCurve) {
102
102
  const { upperCurve, lowerToUpperConnector, upperToLowerConnector, lowerCurve } = this.currentSegmentToPath();
103
103
  upperPath = this.upperSegments.concat(upperCurve);
@@ -180,7 +180,13 @@ export default class FreehandLineBuilder implements ComponentBuilder {
180
180
  }
181
181
 
182
182
  private roundPoint(point: Point2): Point2 {
183
- return Viewport.roundPoint(point, this.minFitAllowed);
183
+ let minFit = Math.min(this.minFitAllowed, this.curveStartWidth / 2);
184
+
185
+ if (minFit < 1e-10) {
186
+ minFit = this.minFitAllowed;
187
+ }
188
+
189
+ return Viewport.roundPoint(point, minFit);
184
190
  }
185
191
 
186
192
  private finalizeCurrentCurve() {
@@ -317,11 +323,14 @@ export default class FreehandLineBuilder implements ComponentBuilder {
317
323
  );
318
324
  };
319
325
 
320
- const upperBoundary = computeBoundaryCurve(1, halfVec);
321
- const lowerBoundary = computeBoundaryCurve(-1, halfVec);
326
+ const boundariesIntersect = () => {
327
+ const upperBoundary = computeBoundaryCurve(1, halfVec);
328
+ const lowerBoundary = computeBoundaryCurve(-1, halfVec);
329
+ return upperBoundary.intersects(lowerBoundary).length > 0;
330
+ };
322
331
 
323
332
  // If the boundaries have two intersections, increasing the half vector's length could fix this.
324
- if (upperBoundary.intersects(lowerBoundary).length > 0) {
333
+ if (boundariesIntersect()) {
325
334
  halfVec = halfVec.times(2);
326
335
  }
327
336
 
@@ -372,7 +381,7 @@ export default class FreehandLineBuilder implements ComponentBuilder {
372
381
  return;
373
382
  }
374
383
 
375
- const threshold = Math.min(this.lastPoint.width, newPoint.width) / 4;
384
+ const threshold = Math.min(this.lastPoint.width, newPoint.width) / 3;
376
385
  const shouldSnapToInitial = this.startPoint.pos.minus(newPoint.pos).magnitude() < threshold
377
386
  && this.isFirstSegment;
378
387
 
@@ -493,7 +502,8 @@ export default class FreehandLineBuilder implements ComponentBuilder {
493
502
  return true;
494
503
  };
495
504
 
496
- if (this.buffer.length > 3) {
505
+ const approxCurveLen = controlPoint.minus(segmentStart).magnitude() + segmentEnd.minus(controlPoint).magnitude();
506
+ if (this.buffer.length > 3 && approxCurveLen > this.curveEndWidth / 2) {
497
507
  if (!curveMatchesPoints(this.currentCurve)) {
498
508
  // Use a curve that better fits the points
499
509
  this.currentCurve = prevCurve;
@@ -1,3 +1,6 @@
1
+ export * from './builders/types';
2
+ export { makeFreehandLineBuilder } from './builders/FreehandLineBuilder';
3
+
1
4
  import AbstractComponent from './AbstractComponent';
2
5
  import Stroke from './Stroke';
3
6
  import Text from './Text';
package/src/lib.ts CHANGED
@@ -14,7 +14,8 @@
14
14
  */
15
15
 
16
16
  import Editor from './Editor';
17
- export { EditorEventType } from './types';
17
+ export { default as EditorImage } from './EditorImage';
18
+ export * from './types';
18
19
  export { default as getLocalizationTable } from './localizations/getLocalizationTable';
19
20
  export * from './localization';
20
21
 
@@ -22,6 +23,9 @@ export { default as Color4 } from './Color4';
22
23
  export * from './math/lib';
23
24
  export * from './components/lib';
24
25
  export * from './commands/lib';
26
+ export * from './tools/lib';
27
+ export * from './toolbar/lib';
28
+ export { default as Pointer, PointerDevice } from './Pointer';
25
29
  export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
26
30
 
27
31
  export { Editor };
@@ -76,8 +76,8 @@ export default class Display {
76
76
  return this.dryInkRenderer.canRenderFromWithoutDataLoss(renderer);
77
77
  },
78
78
  blockResolution: cacheBlockResolution,
79
- cacheSize: 500 * 500 * 4 * 150,
80
- maxScale: 1.5,
79
+ cacheSize: 600 * 600 * 4 * 120,
80
+ maxScale: 1.4,
81
81
  minComponentsPerCache: 45,
82
82
  minComponentsToUseCache: 105,
83
83
  });
@@ -221,11 +221,13 @@ export default class RenderingCacheNode {
221
221
  }
222
222
  } else {
223
223
  // Determine whether we already have rendered the items
224
+ const tooSmallToRender = (rect: Rect2) => rect.w / this.region.w < 1 / this.cacheState.props.blockResolution.x;
225
+
224
226
  const leaves = [];
225
227
  for (const item of items) {
226
228
  leaves.push(
227
229
  ...item.getLeavesIntersectingRegion(
228
- this.region, rect => rect.w / this.region.w < 2 / this.cacheState.props.blockResolution.x,
230
+ this.region, tooSmallToRender,
229
231
  )
230
232
  );
231
233
  }
@@ -59,13 +59,13 @@ export default class CanvasRenderer extends AbstractRenderer {
59
59
  // Set parameters for lower/higher quality rendering
60
60
  public setDraftMode(draftMode: boolean) {
61
61
  if (draftMode) {
62
- this.minSquareCurveApproxDist = 64;
63
- this.minRenderSizeBothDimens = 8;
64
- this.minRenderSizeAnyDimen = 2;
62
+ this.minSquareCurveApproxDist = 9;
63
+ this.minRenderSizeBothDimens = 2;
64
+ this.minRenderSizeAnyDimen = 0.5;
65
65
  } else {
66
- this.minSquareCurveApproxDist = 1;
67
- this.minRenderSizeBothDimens = 0.5;
68
- this.minRenderSizeAnyDimen = 0;
66
+ this.minSquareCurveApproxDist = 0.5;
67
+ this.minRenderSizeBothDimens = 0.3;
68
+ this.minRenderSizeAnyDimen = 1e-5;
69
69
  }
70
70
  }
71
71