@pacem/pacem-foundation 1.0.0-abel

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.
@@ -0,0 +1,334 @@
1
+ //namespace Pacem {
2
+ import { NullChecker } from "./null-checker";
3
+ const HSL_PATTERN = /^hsl\s*\(\s*([\d\.]+)\s*,\s*([\d\.]+)%\s*,\s*([\d\.]+)%\s*\)$/;
4
+ const HSLA_PATTERN = /^hsla\s*\(\s*([\d\.]+)\s*,\s*([\d\.]+)%\s*,\s*([\d\.]+)%\s*,\s*([\d\.]+)\)$/;
5
+ const RGB_PATTERN = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/;
6
+ const RGBA_PATTERN = /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\)$/;
7
+ const HEX_PATTERN = /^#[a-f0-9]{6}$/;
8
+ const HEX_SHORT_PATTERN = /^#[a-f0-9]{3}$/;
9
+ ;
10
+ ;
11
+ ;
12
+ function isRgb(clr) {
13
+ const base = !NullChecker.isNullOrEmpty(clr) && typeof clr === 'object' && 'r' in clr && 'g' in clr && 'b' in clr // && 'a' in clr
14
+ && typeof clr.r === 'number' && typeof clr.b === 'number' && typeof clr.g === 'number' // && typeof clr.a === 'number'
15
+ ;
16
+ if (!base) {
17
+ return false;
18
+ }
19
+ clr.a ??= 1;
20
+ return clr.r >= 0 && clr.r <= 1
21
+ && clr.g >= 0 && clr.g <= 1
22
+ && clr.b >= 0 && clr.b <= 1
23
+ && clr.a >= 0 && clr.a <= 1;
24
+ }
25
+ function isHsl(clr) {
26
+ const base = !NullChecker.isNullOrEmpty(clr) && typeof clr === 'object' && 'h' in clr && 's' in clr && 'l' in clr // && 'a' in clr
27
+ && typeof clr.h === 'number' && typeof clr.s === 'number' && typeof clr.l === 'number' // && typeof clr.a === 'number'
28
+ ;
29
+ if (!base) {
30
+ return false;
31
+ }
32
+ clr.a ??= 1;
33
+ return clr.h >= 0 && clr.h <= 360
34
+ && clr.s >= 0 && clr.s <= 1
35
+ && clr.l >= 0 && clr.l <= 1
36
+ && clr.a >= 0 && clr.a <= 1;
37
+ }
38
+ function luminance(rgb) {
39
+ const r = rgb.r, g = rgb.g, b = rgb.b;
40
+ const l = .5 * (Math.max(r, g, b) + Math.min(r, g, b));
41
+ return l.clamp(0, 1);
42
+ }
43
+ const cieUtils = {
44
+ adjustRgbChannel: (v) => {
45
+ // sRGB => sR'G'B'
46
+ return v <= .03928 ? (v / 12.92) : Math.pow((v + .055) / 1.055, 2.4);
47
+ },
48
+ adjustRgbChannelInv: (v) => {
49
+ // sR'G'B' => sRGB
50
+ return v <= .00304 ? (v * 12.92) : 1.055 * (Math.pow(v, 1 / 2.4) - .055);
51
+ },
52
+ adjustRgb: (rgb) => {
53
+ const rgbOut = { r: cieUtils.adjustRgbChannel(rgb.r), g: cieUtils.adjustRgbChannel(rgb.g), b: cieUtils.adjustRgbChannel(rgb.b), a: rgb.a };
54
+ return rgbOut;
55
+ },
56
+ adjustRgbInv: (rgb) => {
57
+ const rgbOut = { r: cieUtils.adjustRgbChannelInv(rgb.r), g: cieUtils.adjustRgbChannelInv(rgb.g), b: cieUtils.adjustRgbChannelInv(rgb.b), a: rgb.a };
58
+ return rgbOut;
59
+ },
60
+ X: (adjustedRgb) => {
61
+ return .4125 * adjustedRgb.r + .3576 * adjustedRgb.g + .1804 * adjustedRgb.b;
62
+ },
63
+ Y: (adjustedRgb) => {
64
+ // https://www.w3.org/TR/WCAG20/#relativeluminancedef
65
+ return .2126 * adjustedRgb.r + .7152 * adjustedRgb.g + .0722 * adjustedRgb.b;
66
+ },
67
+ Z: (adjustedRgb) => {
68
+ return .0193 * adjustedRgb.r + .1191 * adjustedRgb.g + .9503 * adjustedRgb.b;
69
+ },
70
+ relativeLuminance: (rgb) => {
71
+ const adjRgb = cieUtils.adjustRgb(rgb);
72
+ return cieUtils.Y(adjRgb);
73
+ },
74
+ rgbToCieXYZ: (rgb) => {
75
+ const adjRgb = cieUtils.adjustRgb(rgb);
76
+ const xyz = { x: cieUtils.X(adjRgb), y: cieUtils.Y(adjRgb), z: cieUtils.Z(adjRgb), a: rgb.a };
77
+ return xyz;
78
+ },
79
+ cieXYZToRgb: (xyz) => {
80
+ const r = 3.2410 * xyz.x - 1.5374 * xyz.y - .4986 * xyz.z, g = -.9692 * xyz.x + 1.8760 * xyz.y + .0416 * xyz.z, b = .0556 * xyz.x - .2040 * xyz.y + 1.0570 * xyz.z;
81
+ return cieUtils.adjustRgbInv({ r, g, b, a: xyz.a });
82
+ },
83
+ /**
84
+ * Returns the contrast ratio between two colors.
85
+ * @param rgb1 Color 1
86
+ * @param rgb2 Color 2
87
+ * @returns A numeric value between 1 and 21.
88
+ */
89
+ contrastRatio: (rgb1, rgb2) => {
90
+ // https://www.w3.org/TR/WCAG20/#contrast-ratiodef
91
+ const l1 = cieUtils.relativeLuminance(rgb1) + .05, l2 = cieUtils.relativeLuminance(rgb2) + .05;
92
+ return l1 > l2 ?
93
+ l1 / l2
94
+ :
95
+ l2 / l1;
96
+ }
97
+ };
98
+ function saturation(rgb, l) {
99
+ const r = rgb.r, g = rgb.g, b = rgb.b;
100
+ const max = Math.max(r, g, b);
101
+ const min = Math.min(r, g, b);
102
+ const s = (l === 0 || l === 1) ? 0 : (max - min) / (1 - Math.abs(2 * l - 1));
103
+ return s.clamp(0, 1);
104
+ }
105
+ /**
106
+ * Preucil 'hexagon' algorithm.
107
+ * @param rgb
108
+ */
109
+ function hueHexagon(rgb) {
110
+ const r = rgb.r, g = rgb.g, b = rgb.b;
111
+ const deg = Math.atan2(Math.sqrt(3) * (g - b), 2 * r - g - b) * 180 / Math.PI;
112
+ return (deg + 360) % 360;
113
+ }
114
+ /**
115
+ * Preucil 'circle' algorithm.
116
+ * @param rgb
117
+ */
118
+ function hueCircle(rgb) {
119
+ const r = rgb.r, g = rgb.g, b = rgb.b;
120
+ const max = Math.max(r, g, b);
121
+ const min = Math.min(r, g, b);
122
+ let hue = 0;
123
+ if (max !== min) {
124
+ if (max === r) {
125
+ hue = (60 * ((g - b) / (max - min)) + 360) % 360;
126
+ }
127
+ else if (max === g) {
128
+ hue = (60 * ((b - r) / (max - min)) + 120) % 360;
129
+ }
130
+ else {
131
+ hue = (60 * ((r - g) / (max - min)) + 240) % 360;
132
+ }
133
+ }
134
+ return hue.clamp(0, 360);
135
+ }
136
+ function hexify(v) {
137
+ return v.toString(16).padStart(2, '0');
138
+ //return Pacem.Strings.leftPad(v.toString(16), 2, '0');
139
+ }
140
+ export class Colors {
141
+ static { this.WCAG_MINIMUM_CONTRAST = 3.0; }
142
+ static { this.WCAG_NORMAL_TEXT_CONTRAST = 4.5; }
143
+ static { this.WCAG_SAFE_CONTRAST = 7.0; }
144
+ static _clampRGB(rgb) {
145
+ const clmp = (n) => Math.min(1.0, Math.max(0, n));
146
+ if (rgb[3] == null) {
147
+ rgb[3] = 1.0;
148
+ }
149
+ return { r: clmp(rgb[0]), g: clmp(rgb[1]), b: clmp(rgb[2]), a: Math.min(1, Math.max(0, rgb[3])) };
150
+ }
151
+ static _normalize(rgb) {
152
+ return [rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0, rgb[3]];
153
+ }
154
+ static parse(clr) {
155
+ clr = (clr || '').toLowerCase();
156
+ /* rgb(255,255,255) */
157
+ if (RGB_PATTERN.test(clr)) {
158
+ let rgbArr = RGB_PATTERN.exec(clr);
159
+ return this._clampRGB(this._normalize([parseInt(rgbArr[1]), parseInt(rgbArr[2]), parseInt(rgbArr[3]), 1]));
160
+ }
161
+ /* rgba(255,255,255,.5) */
162
+ if (RGBA_PATTERN.test(clr)) {
163
+ let rgbArr = RGBA_PATTERN.exec(clr);
164
+ return this._clampRGB(this._normalize([parseInt(rgbArr[1]), parseInt(rgbArr[2]), parseInt(rgbArr[3]), parseFloat(rgbArr[4])]));
165
+ }
166
+ /* #ffffff */
167
+ if (HEX_PATTERN.test(clr)) {
168
+ let hex = HEX_PATTERN.exec(clr)[0];
169
+ return this._clampRGB(this._normalize([parseInt('0x' + hex.substr(1, 2)), parseInt('0x' + hex.substr(3, 2)), parseInt('0x' + hex.substr(5, 2)), 1]));
170
+ }
171
+ /* #fff */
172
+ if (HEX_SHORT_PATTERN.test(clr)) {
173
+ let hex = HEX_SHORT_PATTERN.exec(clr)[0];
174
+ let r = hex.substr(1, 1), g = hex.substr(2, 1), b = hex.substr(3, 1);
175
+ return this._clampRGB(this._normalize([parseInt('0x' + r + r), parseInt('0x' + g + g), parseInt('0x' + b + b), 1]));
176
+ }
177
+ /* hsl(0,60%,100%) */
178
+ if (HSL_PATTERN.test(clr)) {
179
+ let hslArr = HSL_PATTERN.exec(clr);
180
+ const hsl = { h: parseFloat(hslArr[1]), s: parseFloat(hslArr[2]) * .01, l: parseFloat(hslArr[3]) * .01 };
181
+ return this.rgb(hsl);
182
+ }
183
+ /* hsla(0,40%,50%,.5) */
184
+ if (HSLA_PATTERN.test(clr)) {
185
+ let hslaArr = HSLA_PATTERN.exec(clr);
186
+ const hsla = { h: parseFloat(hslaArr[1]), s: parseFloat(hslaArr[2]) * .01, l: parseFloat(hslaArr[3]) * .01, a: parseFloat(hslaArr[4]) };
187
+ return this.rgb(hsla);
188
+ }
189
+ }
190
+ static hsl(rgb) {
191
+ const l = luminance(rgb), s = saturation(rgb, l), h = hueCircle(rgb);
192
+ return { h, s, l, a: rgb.a };
193
+ }
194
+ static cieXYZ(clr) {
195
+ if (isHsl(clr)) {
196
+ clr = this.rgb(clr);
197
+ }
198
+ return cieUtils.rgbToCieXYZ(clr);
199
+ }
200
+ static rgb(clr) {
201
+ if (isHsl(clr)) {
202
+ // hsl
203
+ const h = clr.h, s = clr.s, l = clr.l, h6 = h / 60.0;
204
+ const C = (1 - Math.abs(2 * l - 1)) * s;
205
+ const X = C * (1 - Math.abs(h6 % 2 - 1));
206
+ const m = l - C / 2;
207
+ const compose = (r, g, b) => this._clampRGB([r + m, g + m, b + m, clr.a]);
208
+ if (h6 <= 1) {
209
+ return compose(C, X, 0);
210
+ }
211
+ else if (h6 <= 2) {
212
+ return compose(X, C, 0);
213
+ }
214
+ else if (h6 <= 3) {
215
+ return compose(0, C, X);
216
+ }
217
+ else if (h6 <= 4) {
218
+ return compose(0, X, C);
219
+ }
220
+ else if (h6 <= 5) {
221
+ return compose(X, 0, C);
222
+ }
223
+ else if (h6 <= 6) {
224
+ return compose(C, 0, X);
225
+ }
226
+ }
227
+ else {
228
+ // cie XYZ
229
+ const rgb = cieUtils.cieXYZToRgb(clr);
230
+ return this._clampRGB([rgb.r, rgb.g, rgb.b, rgb.a]);
231
+ }
232
+ }
233
+ static stringify(clr, arg1 = 1) {
234
+ if (isHsl(clr)) {
235
+ arg1 ??= 1;
236
+ clr.a ??= 1;
237
+ const precision = typeof arg1 === 'number' ? arg1 : 1;
238
+ return `hsla(${clr.h.roundoff(precision)},${(clr.s * 100).roundoff(precision)}%,${(clr.l * 100).roundoff(precision)}%,${clr.a.roundoff()})`;
239
+ }
240
+ else if (isRgb(clr)) {
241
+ arg1 ??= true;
242
+ clr.a ??= 1;
243
+ const hex = typeof arg1 === 'boolean' ? arg1 : !!arg1, r = Math.round(clr.r * 255), g = Math.round(clr.g * 255), b = Math.round(clr.b * 255), a = clr.a.roundoff();
244
+ if (a === 1) {
245
+ if (hex) {
246
+ return ('#' + hexify(r) + hexify(g) + hexify(b)).toUpperCase();
247
+ }
248
+ return `rgb(${r},${g},${b})`;
249
+ }
250
+ return `rgba(${r},${g},${b},${a})`;
251
+ }
252
+ return undefined;
253
+ }
254
+ /**
255
+ * Contrast ratio betwen two colors (useful for accessibility assessments). Output range: 1 (no-contrast) to 21 (maximum contrast)
256
+ */
257
+ static contrastRatio(clr1, clr2) {
258
+ if (isHsl(clr1)) {
259
+ clr1 = this.rgb(clr1);
260
+ }
261
+ if (isHsl(clr2)) {
262
+ clr2 = this.rgb(clr2);
263
+ }
264
+ return cieUtils.contrastRatio(clr1, clr2);
265
+ }
266
+ /**
267
+ * Returns the relative luminance as of WCAG specs.
268
+ */
269
+ static luminance(clr) {
270
+ if (isHsl(clr)) {
271
+ clr = Colors.rgb(clr);
272
+ }
273
+ return cieUtils.relativeLuminance(clr);
274
+ }
275
+ static lighten(clr, amount = 0.1) {
276
+ amount ||= 0;
277
+ if (isHsl(clr)) {
278
+ const l = Math.min(1, Math.max(0, clr.l + amount));
279
+ return Object.assign({}, clr, { l });
280
+ }
281
+ if (isRgb(clr)) {
282
+ const hsl = this.lighten(this.hsl(clr), amount);
283
+ return this.rgb(hsl);
284
+ }
285
+ return clr;
286
+ }
287
+ static darken(clr, amount = 0.1) {
288
+ return this.lighten(clr, -amount);
289
+ }
290
+ static saturate(clr, amount = 0.1) {
291
+ amount ||= 0;
292
+ if (isHsl(clr)) {
293
+ const s = Math.min(1, Math.max(0, clr.s + amount));
294
+ return Object.assign({}, clr, { s });
295
+ }
296
+ if (isRgb(clr)) {
297
+ const hsl = this.saturate(this.hsl(clr), amount);
298
+ return this.rgb(hsl);
299
+ }
300
+ return clr;
301
+ }
302
+ static desaturate(clr, amount = 0.1) {
303
+ return this.saturate(clr, -amount);
304
+ }
305
+ static interpolate(clr1, clr2, weight1, weight2) {
306
+ weight1 ??= .5;
307
+ weight2 ??= weight1;
308
+ const weight = (weight1 + weight2).roundoff();
309
+ const ratio1 = (weight1 / weight).roundoff(), ratio2 = (weight2 / weight).roundoff();
310
+ const rgb = isRgb(clr1), hsl = isHsl(clr1);
311
+ const toCieXYZ = (c) => {
312
+ if (isRgb(c) || isHsl(c)) {
313
+ return Colors.cieXYZ(c);
314
+ }
315
+ return c;
316
+ };
317
+ clr1 = toCieXYZ(clr1);
318
+ clr2 = toCieXYZ(clr2);
319
+ const clr = {
320
+ x: (clr1.x * ratio1 + clr2.x * ratio2).roundoff(),
321
+ y: (clr1.y * ratio1 + clr2.y * ratio2).roundoff(),
322
+ z: (clr1.z * ratio1 + clr2.z * ratio2).roundoff(),
323
+ a: (clr1.a * ratio1 + clr2.a * ratio2).roundoff(),
324
+ };
325
+ if (rgb) {
326
+ return Colors.rgb(clr);
327
+ }
328
+ if (hsl) {
329
+ return Colors.hsl(Colors.rgb(clr));
330
+ }
331
+ return clr;
332
+ }
333
+ }
334
+ //}
@@ -0,0 +1,58 @@
1
+ //namespace Pacem {
2
+ const JSON_DATE_PATTERN = /^\/Date\([\d]+\)\/$/i;
3
+ export class Dates {
4
+ static parse(input) {
5
+ let d;
6
+ if (typeof input === 'string') {
7
+ if (JSON_DATE_PATTERN.test(input))
8
+ d = parseInt(input.substring(6));
9
+ else
10
+ d = Date.parse(input);
11
+ return new Date(d);
12
+ }
13
+ else if (typeof input === 'number') {
14
+ return new Date(input);
15
+ }
16
+ else
17
+ return input;
18
+ }
19
+ static isLeapYear(year) {
20
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
21
+ }
22
+ static daysInMonth(year, month) {
23
+ return [31, (Dates.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
24
+ }
25
+ /**
26
+ * Gets whether a date is an `Invalid Date` or not.
27
+ * @param date
28
+ */
29
+ static isDate(date) {
30
+ return date instanceof Date && !isNaN(date.valueOf());
31
+ }
32
+ /**
33
+ * Returns the date-only equaivalent of a provided datetime.
34
+ * @param datetime
35
+ */
36
+ static dateOnly(datetime) {
37
+ return new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate());
38
+ }
39
+ static addMonths(input, value) {
40
+ let n = input.getDate(), i = new Date(input), month = i.getMonth() + value, years = 0;
41
+ while (month < 0) {
42
+ month += 12;
43
+ years--;
44
+ }
45
+ i.setDate(1);
46
+ i.setMonth(month % 12);
47
+ i.setFullYear(i.getFullYear() + years + Math.floor(month / 12));
48
+ i.setDate(Math.min(n, Dates.daysInMonth(i.getFullYear(), i.getMonth())));
49
+ return i;
50
+ }
51
+ static addDays(input, value) {
52
+ return new Date(input.valueOf() + value * 86400000);
53
+ }
54
+ static daysBetween(start, end) {
55
+ return (end.valueOf() - start.valueOf()) / 86400000;
56
+ }
57
+ }
58
+ //}
@@ -0,0 +1,35 @@
1
+ function isPlainObject(value) {
2
+ return (value != null &&
3
+ typeof value === 'object' &&
4
+ !Array.isArray(value) &&
5
+ (value.constructor === Object || value.constructor === undefined));
6
+ }
7
+ function deepMerge(target, source) {
8
+ const output = target;
9
+ for (const key of Object.keys(source)) {
10
+ const sourceValue = source[key];
11
+ const targetValue = output[key];
12
+ if (isPlainObject(sourceValue)) {
13
+ // never adopt the source's object by reference: bundlers emit frozen
14
+ // namespace objects (`export * as X`), so a later merge into the same
15
+ // slot would throw ("object is not extensible"). Always merge into a
16
+ // fresh, extensible object instead.
17
+ output[key] = deepMerge(isPlainObject(targetValue) ? targetValue : {}, sourceValue);
18
+ }
19
+ else if (Array.isArray(sourceValue)) {
20
+ output[key] = sourceValue.slice();
21
+ }
22
+ else {
23
+ output[key] = sourceValue;
24
+ }
25
+ }
26
+ return output;
27
+ }
28
+ export class DeepMerger {
29
+ constructor() {
30
+ }
31
+ static merge(source, target) {
32
+ target ??= (globalThis['Pacem'] ??= {});
33
+ return deepMerge(target, source);
34
+ }
35
+ }
@@ -0,0 +1,112 @@
1
+ import { NullChecker } from "./null-checker";
2
+ // 1.7763568394002505e-15 -20.4551920341394 40 40
3
+ const FLOAT_PATTERN = /[-+]?([\d]+(\.[\d]+(e-[\d]+)?)?|[\d]*(\.[\d]+(e-[\d]+)?))/g;
4
+ export function parseAsNumericalArray(input) {
5
+ const arr = [];
6
+ let reg = input.match(FLOAT_PATTERN);
7
+ if (reg && reg.length > 0) {
8
+ reg.forEach(i => arr.push(parseFloat(i)));
9
+ }
10
+ return arr;
11
+ }
12
+ export class Angle {
13
+ static isAngle(obj) {
14
+ return typeof obj === 'object'
15
+ && 'vertex' in obj && Point.isPoint(obj['vertex'])
16
+ && 'start' in obj && Point.isPoint(obj['start'])
17
+ && 'end' in obj && Point.isPoint(obj['end']);
18
+ }
19
+ /**
20
+ * Returns the angle span in radians.
21
+ * Clockwise angles are positive, counterclockwise angles are negative.
22
+ */
23
+ static value(angle) {
24
+ if (NullChecker.isNullOrEmpty(angle) || !Angle.isAngle(angle)) {
25
+ return Number.NaN;
26
+ }
27
+ const { vertex, start, end } = angle;
28
+ return Angle.angleBetween(vertex, start, end);
29
+ }
30
+ /**
31
+ * Returns the angle (in radians) given the angle vertex and two points belonging to the rays that combine to build the angle sides.
32
+ * Clockwise angles are positive, counterclockwise angles are negative.
33
+ * @param vertex Angle vertex
34
+ * @param start Point belonging to the start side
35
+ * @param end Point belonging to the end side
36
+ * @returns Angle span in radians [-pi, pi]
37
+ */
38
+ static angleBetween(vertex, start, end) {
39
+ const v1 = { x: start.x - vertex.x, y: start.y - vertex.y }, v2 = { x: end.x - vertex.x, y: end.y - vertex.y };
40
+ const dot = v1.x * v2.x + v1.y * v2.y;
41
+ const cross = v1.x * v2.y - v1.y * v2.x;
42
+ // clockwise positive, ccw negative
43
+ return -Math.atan2(cross, dot);
44
+ }
45
+ }
46
+ ;
47
+ export class Size {
48
+ static parse(sz) {
49
+ let arr = parseAsNumericalArray(sz);
50
+ if (arr && arr.length === 2) {
51
+ return { width: arr[0], height: arr[1] };
52
+ }
53
+ throw new Error(`Cannot parse "${sz}" as a valid Size.`);
54
+ }
55
+ static isSize(obj) {
56
+ return typeof obj === 'object'
57
+ && 'width' in obj && typeof obj['width'] === 'number'
58
+ && 'height' in obj && typeof obj['height'] === 'number';
59
+ }
60
+ }
61
+ export class Point {
62
+ static parse(pt) {
63
+ let arr = parseAsNumericalArray(pt);
64
+ if (arr && arr.length === 2) {
65
+ return { x: arr[0], y: arr[1] };
66
+ }
67
+ throw new Error(`Cannot parse "${pt}" as a valid Point.`);
68
+ }
69
+ static isPoint(obj) {
70
+ return typeof obj === 'object'
71
+ && 'x' in obj && typeof obj['x'] === 'number'
72
+ && 'y' in obj && typeof obj['y'] === 'number';
73
+ }
74
+ /**
75
+ * Computes the distance between two points.
76
+ * @param p1 Point 1
77
+ * @param p2 Point 2
78
+ */
79
+ static distance(p1, p2) {
80
+ return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
81
+ }
82
+ /**
83
+ * Subtracts a point p from another and returns the resulting vector.
84
+ * @param p Point to subtract
85
+ * @param from Point to be subtracted from
86
+ */
87
+ static subtract(p, from) {
88
+ return { x: from.x - p.x, y: from.y - p.y };
89
+ }
90
+ /**
91
+ * Adds a set of points together and returns the resulting vector.
92
+ * @param points Points to add
93
+ */
94
+ static add(...points) {
95
+ var point = { x: 0, y: 0 };
96
+ for (var p of points) {
97
+ point.x += p.x;
98
+ point.y += p.y;
99
+ }
100
+ return point;
101
+ }
102
+ /**
103
+ * Memberwise multiplication.
104
+ * @param p Original Point object
105
+ * @param f Factor
106
+ * @returns New Point object.
107
+ */
108
+ static multiply(p, f) {
109
+ return { x: p.x * f, y: p.y * f };
110
+ }
111
+ }
112
+ //}
@@ -0,0 +1,60 @@
1
+ // namespace Pacem {
2
+ /** Grants undo/redo functionalities for given state. */
3
+ export class HistoryService {
4
+ constructor(state, maxlength) {
5
+ this.#current = state;
6
+ this.#maxlength = maxlength;
7
+ }
8
+ // it's a queue
9
+ #backwards = [];
10
+ // it's a stack
11
+ #forwards = [];
12
+ // max number of items (queue + stack)
13
+ #maxlength;
14
+ #current;
15
+ /**
16
+ * Gets the current state.
17
+ */
18
+ get current() {
19
+ return this.#current;
20
+ }
21
+ get canUndo() {
22
+ return this.#backwards.length > 0;
23
+ }
24
+ get canRedo() {
25
+ return this.#forwards.length > 0;
26
+ }
27
+ /** Sets the maximum number of stored states, queue + stack.
28
+ */
29
+ set maxlength(v) {
30
+ this.#maxlength = v;
31
+ }
32
+ undo() {
33
+ if (this.canUndo) {
34
+ const back = this.#backwards, fore = this.#forwards, current = this.#current;
35
+ fore.unshift(current);
36
+ this.#current = back.pop();
37
+ }
38
+ }
39
+ redo() {
40
+ if (this.canRedo) {
41
+ const back = this.#backwards, fore = this.#forwards, current = this.#current;
42
+ back.push(current);
43
+ this.#current = fore.shift();
44
+ }
45
+ }
46
+ push(state) {
47
+ this.#forwards.splice(0);
48
+ this.#backwards.push(this.#current);
49
+ this.#current = state;
50
+ const max = this.#maxlength, queue = this.#backwards;
51
+ if (max > 0 && queue.length > max) {
52
+ queue.splice(0, queue.length - max);
53
+ }
54
+ }
55
+ reset() {
56
+ this.#forwards.splice(0);
57
+ this.#backwards.splice(0);
58
+ }
59
+ }
60
+ // }
@@ -0,0 +1,50 @@
1
+ // namespace Pacem {
2
+ import { Rect } from "./rect";
3
+ const DefaultImagingResizeOptions = {
4
+ mode: 'stretch',
5
+ align: 'center',
6
+ valign: 'middle',
7
+ quality: 1,
8
+ type: 'image/jpeg'
9
+ };
10
+ export class Imaging {
11
+ static async loadImage(url) {
12
+ const response = await fetch(url);
13
+ if (!response.ok) {
14
+ return null;
15
+ }
16
+ const blob = await response.blob();
17
+ return blob;
18
+ }
19
+ static async resizeImage(img, width, height, options) {
20
+ if (width <= 0 || height <= 0 || isNaN(width) || isNaN(height)) {
21
+ throw new Error('Invalid target size');
22
+ }
23
+ if (typeof img === 'string') {
24
+ img = await Imaging.loadImage(img);
25
+ }
26
+ const el = await self.createImageBitmap(img);
27
+ const origWidth = el.width, origHeight = el.height;
28
+ const cnv = new OffscreenCanvas(origWidth, origHeight);
29
+ const ctx = cnv.getContext('2d');
30
+ ctx.imageSmoothingEnabled =
31
+ ctx['mozImageSmoothingEnabled'] =
32
+ ctx['oImageSmoothingEnabled'] =
33
+ ctx['webkitImageSmoothingEnabled'] =
34
+ true;
35
+ const { quality, type, mode, align, valign, background } = Object.assign({}, DefaultImagingResizeOptions, options ?? {});
36
+ const from = { width: origWidth, height: origHeight, x: 0, y: 0 }, to = { x: 0, y: 0, width, height };
37
+ const transformMatrix = Rect.findTransform(from, to, mode, align, valign);
38
+ cnv.width = width;
39
+ cnv.height = height;
40
+ if (background) {
41
+ ctx.fillStyle = background;
42
+ ctx.fillRect(0, 0, cnv.width, cnv.height);
43
+ }
44
+ ctx.setTransform(transformMatrix);
45
+ ctx.drawImage(el, 0, 0);
46
+ const retval = await cnv.convertToBlob({ quality, type });
47
+ return retval;
48
+ }
49
+ }
50
+ // }
@@ -0,0 +1 @@
1
+ export * from './compile/markdown/parser';
@@ -0,0 +1,2 @@
1
+ export * as Markdown from './index-compile-markdown';
2
+ export * from './compile/parser';
@@ -0,0 +1,2 @@
1
+ import * as Output from './index';
2
+ Output.DeepMerger.merge(Output);
@@ -0,0 +1,20 @@
1
+ // import './number-extensions';
2
+ // import './array-extensions';
3
+ export * from './number-extensions';
4
+ export * from './array-extensions';
5
+ export * from './deepmerger';
6
+ export * from './arrays';
7
+ export * from './blobs';
8
+ export * from './browser';
9
+ export * from './colors';
10
+ export * from './dates';
11
+ export * from './geom';
12
+ export * from './rect';
13
+ export * from "./null-checker";
14
+ export * from './history-svc';
15
+ export * from './imaging';
16
+ export * from './keygenerator';
17
+ export * from './matrix2d';
18
+ export * from './numbers';
19
+ export * from './strings';
20
+ export * as Compile from './index-compile';