js-draw 0.1.11 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +4 -2
- package/dist/src/Editor.js +30 -10
- package/dist/src/EditorImage.d.ts +1 -1
- package/dist/src/EditorImage.js +2 -2
- package/dist/src/Pointer.d.ts +1 -1
- package/dist/src/Pointer.js +1 -1
- package/dist/src/SVGLoader.d.ts +1 -1
- package/dist/src/SVGLoader.js +14 -6
- package/dist/src/Viewport.d.ts +8 -25
- package/dist/src/Viewport.js +15 -10
- package/dist/src/commands/Command.d.ts +2 -2
- package/dist/src/commands/Command.js +4 -4
- package/dist/src/commands/Duplicate.d.ts +1 -1
- package/dist/src/commands/Duplicate.js +1 -1
- package/dist/src/commands/Erase.d.ts +1 -1
- package/dist/src/commands/Erase.js +1 -1
- package/dist/src/commands/localization.d.ts +1 -1
- package/dist/src/components/AbstractComponent.d.ts +3 -3
- package/dist/src/components/AbstractComponent.js +2 -2
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +3 -3
- package/dist/src/components/SVGGlobalAttributesObject.js +1 -1
- package/dist/src/components/Stroke.d.ts +4 -4
- package/dist/src/components/Stroke.js +2 -2
- package/dist/src/components/Text.d.ts +3 -3
- package/dist/src/components/Text.js +3 -3
- package/dist/src/components/UnknownSVGObject.d.ts +3 -3
- package/dist/src/components/UnknownSVGObject.js +1 -1
- package/dist/src/components/builders/ArrowBuilder.d.ts +1 -1
- package/dist/src/components/builders/ArrowBuilder.js +1 -1
- package/dist/src/components/builders/FreehandLineBuilder.d.ts +8 -3
- package/dist/src/components/builders/FreehandLineBuilder.js +142 -71
- package/dist/src/components/builders/LineBuilder.d.ts +1 -1
- package/dist/src/components/builders/LineBuilder.js +1 -1
- package/dist/src/components/builders/RectangleBuilder.d.ts +1 -1
- package/dist/src/components/builders/RectangleBuilder.js +3 -3
- package/dist/src/components/builders/types.d.ts +1 -1
- package/dist/src/localization.d.ts +1 -0
- package/dist/src/localization.js +5 -1
- package/dist/src/localizations/es.js +1 -1
- package/dist/src/{geometry → math}/LineSegment2.d.ts +0 -0
- package/dist/src/{geometry → math}/LineSegment2.js +0 -0
- package/dist/src/{geometry → math}/Mat33.d.ts +0 -0
- package/dist/src/{geometry → math}/Mat33.js +0 -0
- package/dist/src/{geometry → math}/Path.d.ts +2 -1
- package/dist/src/{geometry → math}/Path.js +58 -51
- package/dist/src/{geometry → math}/Rect2.d.ts +0 -0
- package/dist/src/{geometry → math}/Rect2.js +0 -0
- package/dist/src/{geometry → math}/Vec2.d.ts +0 -0
- package/dist/src/{geometry → math}/Vec2.js +0 -0
- package/dist/src/{geometry → math}/Vec3.d.ts +1 -1
- package/dist/src/{geometry → math}/Vec3.js +1 -1
- package/dist/src/math/rounding.d.ts +3 -0
- package/dist/src/math/rounding.js +120 -0
- package/dist/src/rendering/Display.d.ts +3 -1
- package/dist/src/rendering/Display.js +16 -10
- package/dist/src/rendering/caching/CacheRecord.d.ts +2 -2
- package/dist/src/rendering/caching/CacheRecord.js +1 -1
- package/dist/src/rendering/caching/CacheRecordManager.d.ts +1 -1
- package/dist/src/rendering/caching/RenderingCache.js +1 -1
- package/dist/src/rendering/caching/RenderingCacheNode.d.ts +2 -1
- package/dist/src/rendering/caching/RenderingCacheNode.js +18 -7
- package/dist/src/rendering/caching/testUtils.js +1 -1
- package/dist/src/rendering/caching/types.d.ts +1 -1
- package/dist/src/rendering/localization.d.ts +2 -0
- package/dist/src/rendering/localization.js +2 -0
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +4 -4
- package/dist/src/rendering/renderers/AbstractRenderer.js +2 -2
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +4 -4
- package/dist/src/rendering/renderers/CanvasRenderer.js +1 -1
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +4 -4
- package/dist/src/rendering/renderers/DummyRenderer.js +1 -1
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +3 -3
- package/dist/src/rendering/renderers/SVGRenderer.js +8 -2
- package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +5 -3
- package/dist/src/rendering/renderers/TextOnlyRenderer.js +13 -3
- package/dist/src/toolbar/icons.d.ts +3 -0
- package/dist/src/toolbar/icons.js +142 -132
- package/dist/src/toolbar/localization.d.ts +2 -1
- package/dist/src/toolbar/localization.js +2 -1
- package/dist/src/toolbar/makeColorInput.js +2 -1
- package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +13 -0
- package/dist/src/toolbar/widgets/ActionButtonWidget.js +21 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +2 -0
- package/dist/src/toolbar/widgets/HandToolWidget.js +3 -3
- package/dist/src/toolbar/widgets/SelectionWidget.d.ts +0 -1
- package/dist/src/toolbar/widgets/SelectionWidget.js +23 -30
- package/dist/src/tools/Eraser.js +1 -1
- package/dist/src/tools/PanZoom.d.ts +1 -1
- package/dist/src/tools/PanZoom.js +24 -14
- package/dist/src/tools/SelectionTool.d.ts +3 -3
- package/dist/src/tools/SelectionTool.js +6 -6
- package/dist/src/tools/TextTool.js +1 -1
- package/dist/src/types.d.ts +4 -4
- package/package.json +1 -1
- package/src/Editor.ts +34 -12
- package/src/EditorImage.test.ts +2 -4
- package/src/EditorImage.ts +2 -2
- package/src/Pointer.ts +1 -1
- package/src/SVGLoader.ts +14 -6
- package/src/Viewport.ts +19 -17
- package/src/commands/Command.ts +5 -5
- package/src/commands/Duplicate.ts +1 -1
- package/src/commands/Erase.ts +1 -1
- package/src/commands/localization.ts +1 -1
- package/src/components/AbstractComponent.ts +4 -4
- package/src/components/SVGGlobalAttributesObject.ts +3 -3
- package/src/components/Stroke.test.ts +3 -5
- package/src/components/Stroke.ts +4 -4
- package/src/components/Text.test.ts +2 -2
- package/src/components/Text.ts +3 -3
- package/src/components/UnknownSVGObject.ts +3 -3
- package/src/components/builders/ArrowBuilder.ts +2 -2
- package/src/components/builders/FreehandLineBuilder.ts +190 -80
- package/src/components/builders/LineBuilder.ts +2 -2
- package/src/components/builders/RectangleBuilder.ts +3 -3
- package/src/components/builders/types.ts +1 -1
- package/src/localization.ts +6 -0
- package/src/localizations/es.ts +2 -1
- package/src/{geometry → math}/LineSegment2.test.ts +0 -0
- package/src/{geometry → math}/LineSegment2.ts +0 -0
- package/src/{geometry → math}/Mat33.test.ts +0 -0
- package/src/{geometry → math}/Mat33.ts +0 -0
- package/src/{geometry → math}/Path.fromString.test.ts +0 -0
- package/src/{geometry → math}/Path.test.ts +0 -0
- package/src/{geometry → math}/Path.toString.test.ts +11 -2
- package/src/{geometry → math}/Path.ts +60 -57
- package/src/{geometry → math}/Rect2.test.ts +0 -0
- package/src/{geometry → math}/Rect2.ts +0 -0
- package/src/{geometry → math}/Vec2.test.ts +0 -0
- package/src/{geometry → math}/Vec2.ts +0 -0
- package/src/{geometry → math}/Vec3.test.ts +0 -0
- package/src/{geometry → math}/Vec3.ts +2 -2
- package/src/math/rounding.test.ts +40 -0
- package/src/math/rounding.ts +145 -0
- package/src/rendering/Display.ts +18 -10
- package/src/rendering/caching/CacheRecord.test.ts +2 -2
- package/src/rendering/caching/CacheRecord.ts +2 -2
- package/src/rendering/caching/CacheRecordManager.ts +1 -1
- package/src/rendering/caching/RenderingCache.test.ts +3 -3
- package/src/rendering/caching/RenderingCache.ts +1 -1
- package/src/rendering/caching/RenderingCacheNode.ts +23 -7
- package/src/rendering/caching/testUtils.ts +1 -1
- package/src/rendering/caching/types.ts +1 -1
- package/src/rendering/localization.ts +4 -0
- package/src/rendering/renderers/AbstractRenderer.ts +4 -4
- package/src/rendering/renderers/CanvasRenderer.ts +4 -4
- package/src/rendering/renderers/DummyRenderer.test.ts +2 -2
- package/src/rendering/renderers/DummyRenderer.ts +4 -4
- package/src/rendering/renderers/SVGRenderer.ts +10 -4
- package/src/rendering/renderers/TextOnlyRenderer.ts +17 -6
- package/src/toolbar/icons.ts +157 -137
- package/src/toolbar/localization.ts +4 -2
- package/src/toolbar/makeColorInput.ts +2 -1
- package/src/toolbar/toolbar.css +1 -1
- package/src/toolbar/widgets/ActionButtonWidget.ts +31 -0
- package/src/toolbar/widgets/BaseWidget.ts +2 -0
- package/src/toolbar/widgets/HandToolWidget.ts +3 -3
- package/src/toolbar/widgets/SelectionWidget.ts +46 -41
- package/src/tools/Eraser.ts +2 -2
- package/src/tools/PanZoom.ts +28 -16
- package/src/tools/SelectionTool.test.ts +2 -4
- package/src/tools/SelectionTool.ts +6 -6
- package/src/tools/TextTool.ts +2 -2
- package/src/tools/UndoRedoShortcut.test.ts +1 -1
- package/src/types.ts +4 -4
@@ -115,9 +115,9 @@ export default class Vec3 {
|
|
115
115
|
}
|
116
116
|
|
117
117
|
// Returns a vector with each component acted on by [fn]
|
118
|
-
public map(fn: (component: number)=> number): Vec3 {
|
118
|
+
public map(fn: (component: number, index: number)=> number): Vec3 {
|
119
119
|
return Vec3.of(
|
120
|
-
fn(this.x), fn(this.y), fn(this.z)
|
120
|
+
fn(this.x, 0), fn(this.y, 1), fn(this.z, 2)
|
121
121
|
);
|
122
122
|
}
|
123
123
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { toRoundedString, toStringOfSamePrecision } from './rounding';
|
2
|
+
|
3
|
+
describe('toRoundedString', () => {
|
4
|
+
it('should round up numbers endings similar to .999999999999999', () => {
|
5
|
+
expect(toRoundedString(0.999999999)).toBe('1');
|
6
|
+
expect(toRoundedString(0.899999999)).toBe('.9');
|
7
|
+
expect(toRoundedString(9.999999999)).toBe('10');
|
8
|
+
expect(toRoundedString(-10.999999999)).toBe('-11');
|
9
|
+
});
|
10
|
+
|
11
|
+
it('should round up numbers similar to 10.999999998', () => {
|
12
|
+
expect(toRoundedString(10.999999998)).toBe('11');
|
13
|
+
});
|
14
|
+
|
15
|
+
// Handling this creates situations with potential error:
|
16
|
+
//it('should round strings with multiple digits after the ending decimal points', () => {
|
17
|
+
// expect(toRoundedString(292.2 - 292.8)).toBe('-0.6');
|
18
|
+
//});
|
19
|
+
|
20
|
+
it('should round down strings ending endings similar to .00000001', () => {
|
21
|
+
expect(toRoundedString(10.00000001)).toBe('10');
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
it('toStringOfSamePrecision', () => {
|
26
|
+
expect(toStringOfSamePrecision(1.23456, '1.12')).toBe('1.23');
|
27
|
+
expect(toStringOfSamePrecision(1.23456, '1.1')).toBe('1.2');
|
28
|
+
expect(toStringOfSamePrecision(1.23456, '1.1', '5.32')).toBe('1.23');
|
29
|
+
expect(toStringOfSamePrecision(-1.23456, '1.1', '5.32')).toBe('-1.23');
|
30
|
+
expect(toStringOfSamePrecision(-1.99999, '1.1', '5.32')).toBe('-2');
|
31
|
+
expect(toStringOfSamePrecision(1.99999, '1.1', '5.32')).toBe('2');
|
32
|
+
expect(toStringOfSamePrecision(1.89999, '1.1', '5.32')).toBe('1.9');
|
33
|
+
expect(toStringOfSamePrecision(9.99999999, '-1.1234')).toBe('10');
|
34
|
+
expect(toStringOfSamePrecision(9.999999998999996, '100')).toBe('10');
|
35
|
+
expect(toStringOfSamePrecision(0.000012345, '0.000012')).toBe('.000012');
|
36
|
+
expect(toStringOfSamePrecision(0.000012645, '.000012')).toBe('.000013');
|
37
|
+
expect(toStringOfSamePrecision(-0.09999999999999432, '291.3')).toBe('-.1');
|
38
|
+
expect(toStringOfSamePrecision(-0.9999999999999432, '291.3')).toBe('-1');
|
39
|
+
expect(toStringOfSamePrecision(9998.9, '.1', '-11')).toBe('9998.9');
|
40
|
+
});
|
@@ -0,0 +1,145 @@
|
|
1
|
+
// Clean up stringified numbers
|
2
|
+
const cleanUpNumber = (text: string) => {
|
3
|
+
// Regular expression substitions can be somewhat expensive. Only do them
|
4
|
+
// if necessary.
|
5
|
+
const lastChar = text.charAt(text.length - 1);
|
6
|
+
if (lastChar === '0' || lastChar === '.') {
|
7
|
+
// Remove trailing zeroes
|
8
|
+
text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
|
9
|
+
text = text.replace(/[.]0+$/, '.');
|
10
|
+
|
11
|
+
// Remove trailing period
|
12
|
+
text = text.replace(/[.]$/, '');
|
13
|
+
|
14
|
+
if (text === '-0') {
|
15
|
+
return '0';
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
const firstChar = text.charAt(0);
|
20
|
+
if (firstChar === '0' || firstChar === '-') {
|
21
|
+
// Remove unnecessary leading zeroes.
|
22
|
+
text = text.replace(/^(0+)[.]/, '.');
|
23
|
+
text = text.replace(/^-(0+)[.]/, '-.');
|
24
|
+
}
|
25
|
+
|
26
|
+
return text;
|
27
|
+
};
|
28
|
+
|
29
|
+
export const toRoundedString = (num: number): string => {
|
30
|
+
// Try to remove rounding errors. If the number ends in at least three/four zeroes
|
31
|
+
// (or nines) just one or two digits, it's probably a rounding error.
|
32
|
+
const fixRoundingUpExp = /^([-]?\d*\.\d{3,})0{4,}\d{1,2}$/;
|
33
|
+
const hasRoundingDownExp = /^([-]?)(\d*)\.(\d{3,}9{4,})\d{1,2}$/;
|
34
|
+
|
35
|
+
let text = num.toString(10);
|
36
|
+
if (text.indexOf('.') === -1) {
|
37
|
+
return text;
|
38
|
+
}
|
39
|
+
|
40
|
+
const roundingDownMatch = hasRoundingDownExp.exec(text);
|
41
|
+
if (roundingDownMatch) {
|
42
|
+
const negativeSign = roundingDownMatch[1];
|
43
|
+
const postDecimalString = roundingDownMatch[3];
|
44
|
+
const lastDigit = parseInt(postDecimalString.charAt(postDecimalString.length - 1), 10);
|
45
|
+
const postDecimal = parseInt(postDecimalString, 10);
|
46
|
+
const preDecimal = parseInt(roundingDownMatch[2], 10);
|
47
|
+
|
48
|
+
const origPostDecimalString = roundingDownMatch[3];
|
49
|
+
|
50
|
+
let newPostDecimal = (postDecimal + 10 - lastDigit).toString();
|
51
|
+
let carry = 0;
|
52
|
+
if (newPostDecimal.length > postDecimal.toString().length) {
|
53
|
+
// Left-shift
|
54
|
+
newPostDecimal = newPostDecimal.substring(1);
|
55
|
+
carry = 1;
|
56
|
+
}
|
57
|
+
|
58
|
+
// parseInt(...).toString() removes leading zeroes. Add them back.
|
59
|
+
while (newPostDecimal.length < origPostDecimalString.length) {
|
60
|
+
newPostDecimal = carry.toString(10) + newPostDecimal;
|
61
|
+
carry = 0;
|
62
|
+
}
|
63
|
+
|
64
|
+
text = `${negativeSign + (preDecimal + carry).toString()}.${newPostDecimal}`;
|
65
|
+
}
|
66
|
+
|
67
|
+
text = text.replace(fixRoundingUpExp, '$1');
|
68
|
+
|
69
|
+
return cleanUpNumber(text);
|
70
|
+
};
|
71
|
+
|
72
|
+
const numberExp = /^([-]?)(\d*)[.](\d+)$/;
|
73
|
+
export const getLenAfterDecimal = (numberAsString: string) => {
|
74
|
+
const numberMatch = numberExp.exec(numberAsString);
|
75
|
+
if (!numberMatch) {
|
76
|
+
// If not a match, either the number is exponential notation (or is something
|
77
|
+
// like NaN or Infinity)
|
78
|
+
if (numberAsString.search(/[eE]/) !== -1 || /^[a-zA-Z]+$/.exec(numberAsString)) {
|
79
|
+
return -1;
|
80
|
+
// Or it has no decimal point
|
81
|
+
} else {
|
82
|
+
return 0;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
const afterDecimalLen = numberMatch[3].length;
|
87
|
+
return afterDecimalLen;
|
88
|
+
};
|
89
|
+
|
90
|
+
// [reference] should be a string representation of a base-10 number (no exponential (e.g. 10e10))
|
91
|
+
export const toStringOfSamePrecision = (num: number, ...references: string[]): string => {
|
92
|
+
const text = num.toString(10);
|
93
|
+
const textMatch = numberExp.exec(text);
|
94
|
+
if (!textMatch) {
|
95
|
+
return text;
|
96
|
+
}
|
97
|
+
|
98
|
+
let decimalPlaces = -1;
|
99
|
+
for (const reference of references) {
|
100
|
+
decimalPlaces = Math.max(getLenAfterDecimal(reference), decimalPlaces);
|
101
|
+
}
|
102
|
+
|
103
|
+
if (decimalPlaces === -1) {
|
104
|
+
return toRoundedString(num);
|
105
|
+
}
|
106
|
+
|
107
|
+
// Make text's after decimal length match [afterDecimalLen].
|
108
|
+
let postDecimal = textMatch[3].substring(0, decimalPlaces);
|
109
|
+
let preDecimal = textMatch[2];
|
110
|
+
const nextDigit = textMatch[3].charAt(decimalPlaces);
|
111
|
+
|
112
|
+
if (nextDigit !== '') {
|
113
|
+
const asNumber = parseInt(nextDigit, 10);
|
114
|
+
if (asNumber >= 5) {
|
115
|
+
// Don't attempt to parseInt() an empty string.
|
116
|
+
if (postDecimal.length > 0) {
|
117
|
+
const leadingZeroMatch = /^(0+)(\d*)$/.exec(postDecimal);
|
118
|
+
|
119
|
+
let leadingZeroes = '';
|
120
|
+
let postLeading = postDecimal;
|
121
|
+
if (leadingZeroMatch) {
|
122
|
+
leadingZeroes = leadingZeroMatch[1];
|
123
|
+
postLeading = leadingZeroMatch[2];
|
124
|
+
}
|
125
|
+
|
126
|
+
postDecimal = (parseInt(postDecimal) + 1).toString();
|
127
|
+
|
128
|
+
// If postDecimal got longer, remove leading zeroes if possible
|
129
|
+
if (postDecimal.length > postLeading.length && leadingZeroes.length > 0) {
|
130
|
+
leadingZeroes = leadingZeroes.substring(1);
|
131
|
+
}
|
132
|
+
|
133
|
+
postDecimal = leadingZeroes + postDecimal;
|
134
|
+
}
|
135
|
+
|
136
|
+
if (postDecimal.length === 0 || postDecimal.length > decimalPlaces) {
|
137
|
+
preDecimal = (parseInt(preDecimal) + 1).toString();
|
138
|
+
postDecimal = postDecimal.substring(1);
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
const negativeSign = textMatch[1];
|
144
|
+
return cleanUpNumber(`${negativeSign}${preDecimal}.${postDecimal}`);
|
145
|
+
};
|
package/src/rendering/Display.ts
CHANGED
@@ -3,7 +3,7 @@ import CanvasRenderer from './renderers/CanvasRenderer';
|
|
3
3
|
import { Editor } from '../Editor';
|
4
4
|
import { EditorEventType } from '../types';
|
5
5
|
import DummyRenderer from './renderers/DummyRenderer';
|
6
|
-
import { Point2, Vec2 } from '../
|
6
|
+
import { Point2, Vec2 } from '../math/Vec2';
|
7
7
|
import RenderingCache from './caching/RenderingCache';
|
8
8
|
import TextOnlyRenderer from './renderers/TextOnlyRenderer';
|
9
9
|
import Color4 from '../Color4';
|
@@ -18,6 +18,7 @@ export default class Display {
|
|
18
18
|
private dryInkRenderer: AbstractRenderer;
|
19
19
|
private wetInkRenderer: AbstractRenderer;
|
20
20
|
private textRenderer: TextOnlyRenderer;
|
21
|
+
private textRerenderOutput: HTMLElement|null = null;
|
21
22
|
private cache: RenderingCache;
|
22
23
|
private resizeSurfacesCallback?: ()=> void;
|
23
24
|
private flattenCallback?: ()=> void;
|
@@ -59,10 +60,10 @@ export default class Display {
|
|
59
60
|
return this.dryInkRenderer.canRenderFromWithoutDataLoss(renderer);
|
60
61
|
},
|
61
62
|
blockResolution: cacheBlockResolution,
|
62
|
-
cacheSize: 500 * 500 * 4 *
|
63
|
+
cacheSize: 500 * 500 * 4 * 220,
|
63
64
|
maxScale: 1.5,
|
64
|
-
minComponentsPerCache:
|
65
|
-
minComponentsToUseCache:
|
65
|
+
minComponentsPerCache: 45,
|
66
|
+
minComponentsToUseCache: 105,
|
66
67
|
});
|
67
68
|
|
68
69
|
this.editor.notifier.on(EditorEventType.DisplayResized, event => {
|
@@ -158,19 +159,26 @@ export default class Display {
|
|
158
159
|
rerenderButton.classList.add('rerenderButton');
|
159
160
|
rerenderButton.innerText = this.editor.localization.rerenderAsText;
|
160
161
|
|
161
|
-
|
162
|
-
|
162
|
+
this.textRerenderOutput = document.createElement('div');
|
163
|
+
this.textRerenderOutput.setAttribute('aria-live', 'polite');
|
163
164
|
|
164
165
|
rerenderButton.onclick = () => {
|
165
|
-
this.
|
166
|
-
this.editor.image.render(this.textRenderer, this.editor.viewport);
|
167
|
-
rerenderOutput.innerText = this.textRenderer.getDescription();
|
166
|
+
this.rerenderAsText();
|
168
167
|
};
|
169
168
|
|
170
|
-
textRendererOutputContainer.replaceChildren(rerenderButton,
|
169
|
+
textRendererOutputContainer.replaceChildren(rerenderButton, this.textRerenderOutput);
|
171
170
|
this.editor.createHTMLOverlay(textRendererOutputContainer);
|
172
171
|
}
|
173
172
|
|
173
|
+
public rerenderAsText() {
|
174
|
+
this.textRenderer.clear();
|
175
|
+
this.editor.image.render(this.textRenderer, this.editor.viewport);
|
176
|
+
|
177
|
+
if (this.textRerenderOutput) {
|
178
|
+
this.textRerenderOutput.innerText = this.textRenderer.getDescription();
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
174
182
|
// Clears the drawing surfaces and otherwise prepares for a rerender.
|
175
183
|
public startRerender(): AbstractRenderer {
|
176
184
|
this.resizeSurfacesCallback?.();
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/* @jest-environment jsdom */
|
2
2
|
|
3
|
-
import Rect2 from '../../
|
4
|
-
import { Vec2 } from '../../
|
3
|
+
import Rect2 from '../../math/Rect2';
|
4
|
+
import { Vec2 } from '../../math/Vec2';
|
5
5
|
import CacheRecord from './CacheRecord';
|
6
6
|
import { createCache } from './testUtils';
|
7
7
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import Mat33 from '../../
|
2
|
-
import Rect2 from '../../
|
1
|
+
import Mat33 from '../../math/Mat33';
|
2
|
+
import Rect2 from '../../math/Rect2';
|
3
3
|
import AbstractRenderer from '../renderers/AbstractRenderer';
|
4
4
|
import { BeforeDeallocCallback, CacheState } from './types';
|
5
5
|
|
@@ -3,11 +3,11 @@
|
|
3
3
|
import DummyRenderer from '../renderers/DummyRenderer';
|
4
4
|
import { createCache } from './testUtils';
|
5
5
|
import Stroke from '../../components/Stroke';
|
6
|
-
import Path from '../../
|
6
|
+
import Path from '../../math/Path';
|
7
7
|
import Color4 from '../../Color4';
|
8
8
|
import EditorImage from '../../EditorImage';
|
9
9
|
import Viewport from '../../Viewport';
|
10
|
-
import Mat33 from '../../
|
10
|
+
import Mat33 from '../../math/Mat33';
|
11
11
|
|
12
12
|
describe('RenderingCache', () => {
|
13
13
|
const testPath = Path.fromString('M0,0 l100,500 l-20,20 L-100,-100');
|
@@ -35,7 +35,7 @@ describe('RenderingCache', () => {
|
|
35
35
|
expect(lastRenderer).not.toBeNull();
|
36
36
|
expect(lastRenderer!.renderedPathCount).toBe(1);
|
37
37
|
|
38
|
-
editor.dispatch(
|
38
|
+
editor.dispatch(Viewport.transformBy(Mat33.scaling2D(0.1)));
|
39
39
|
editor.image.renderWithCache(screenRenderer, cache, editor.viewport);
|
40
40
|
expect(allocdRenderers).toBe(1);
|
41
41
|
expect(lastRenderer!.renderedPathCount).toBe(1);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ImageNode } from '../../EditorImage';
|
2
|
-
import Rect2 from '../../
|
2
|
+
import Rect2 from '../../math/Rect2';
|
3
3
|
import Viewport from '../../Viewport';
|
4
4
|
import AbstractRenderer from '../renderers/AbstractRenderer';
|
5
5
|
import RenderingCacheNode from './RenderingCacheNode';
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
import Color4 from '../../Color4';
|
5
5
|
import { ImageNode, sortLeavesByZIndex } from '../../EditorImage';
|
6
|
-
import Rect2 from '../../
|
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
|
-
|
155
|
-
|
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 <
|
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:
|
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,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 '../../
|
4
|
-
import Path, { PathCommand, PathCommandType } from '../../
|
5
|
-
import Rect2 from '../../
|
6
|
-
import { Point2, Vec2 } from '../../
|
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 '../../
|
4
|
-
import Rect2 from '../../
|
5
|
-
import { Point2, Vec2 } from '../../
|
6
|
-
import Vec3 from '../../
|
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 '../../
|
4
|
-
import { Vec2 } from '../../
|
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 '../../
|
5
|
-
import Rect2 from '../../
|
6
|
-
import { Point2, Vec2 } from '../../
|
7
|
-
import Vec3 from '../../
|
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 '../../
|
5
|
-
import Path, { PathCommand, PathCommandType } from '../../
|
6
|
-
import Rect2 from '../../
|
7
|
-
import {
|
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 '../../
|
3
|
-
import Rect2 from '../../
|
4
|
-
import { Vec2 } from '../../
|
5
|
-
import Vec3 from '../../
|
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
|
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 <
|
59
|
+
return rect.maxDimension < 15 / this.getSizeOfCanvasPixelOnScreen();
|
49
60
|
}
|
50
61
|
public drawPoints(..._points: Vec3[]): void {
|
51
62
|
}
|