@3dsource/utils 0.0.1
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/README.md +64 -0
- package/eslint.config.js +37 -0
- package/ng-package.json +7 -0
- package/package.json +11 -0
- package/src/lib/color/CMYKtoRGB.ts +20 -0
- package/src/lib/color/HEXtoRGB.ts +9 -0
- package/src/lib/color/HSVtoRGB.ts +82 -0
- package/src/lib/color/RGBtoCMYK.ts +50 -0
- package/src/lib/color/RGBtoHEX.ts +17 -0
- package/src/lib/color/RGBtoHSV.ts +53 -0
- package/src/lib/color/hsv.ts +14 -0
- package/src/lib/color/index.ts +16 -0
- package/src/lib/color/max.ts +18 -0
- package/src/lib/color/min.ts +18 -0
- package/src/lib/color/overlay.ts +25 -0
- package/src/lib/color/rgb.ts +10 -0
- package/src/lib/color/sub.ts +18 -0
- package/src/lib/color/subtract.ts +27 -0
- package/src/lib/color/sum.ts +19 -0
- package/src/lib/color/toRGB.ts +14 -0
- package/src/lib/color/toRGBA.ts +8 -0
- package/src/lib/constants/color-codes.constant.ts +9 -0
- package/src/lib/constants/index.ts +1 -0
- package/src/lib/csv/CSV2Array.ts +66 -0
- package/src/lib/csv/CSV2Records.ts +56 -0
- package/src/lib/csv/ObjectToCSV.ts +21 -0
- package/src/lib/csv/index.ts +3 -0
- package/src/lib/csv/test/Csv.spec.ts +51 -0
- package/src/lib/dev/dev3d.ts +1 -0
- package/src/lib/dev/index.ts +3 -0
- package/src/lib/dev/logger.ts +94 -0
- package/src/lib/dev/timeToString.ts +16 -0
- package/src/lib/filenaming/cleanupFileName.ts +18 -0
- package/src/lib/filenaming/index.ts +3 -0
- package/src/lib/filenaming/makePath.ts +5 -0
- package/src/lib/filenaming/normalizePath.ts +9 -0
- package/src/lib/filenaming/test/cleanupFileName.spec.ts +9 -0
- package/src/lib/filenaming/test/makePath.spec.ts +7 -0
- package/src/lib/filenaming/test/normalizePath.spec.ts +9 -0
- package/src/lib/geom/expandOverRectangle.ts +17 -0
- package/src/lib/geom/fitIntoRectangle.ts +43 -0
- package/src/lib/geom/index.ts +3 -0
- package/src/lib/geom/interfaces/area.interface.ts +5 -0
- package/src/lib/geom/interfaces/index.ts +4 -0
- package/src/lib/geom/interfaces/rect.interface.ts +4 -0
- package/src/lib/geom/interfaces/size.interface.ts +4 -0
- package/src/lib/geom/interfaces//321/201oords.interface.ts +4 -0
- package/src/lib/geom/test/fitRectangle.spec.ts +54 -0
- package/src/lib/helpers/BatchLoader.ts +243 -0
- package/src/lib/helpers/KeyboardNumericCode.ts +118 -0
- package/src/lib/helpers/index.ts +6 -0
- package/src/lib/helpers/serialize.ts +11 -0
- package/src/lib/helpers/sleep.ts +3 -0
- package/src/lib/helpers/test/sleep.spec.ts +11 -0
- package/src/lib/helpers/trimLastSlashFromUrl.ts +9 -0
- package/src/lib/image/SaveImage.ts +65 -0
- package/src/lib/image/getCanvasCached.ts +16 -0
- package/src/lib/image/getSnapshot.ts +99 -0
- package/src/lib/image/index.ts +4 -0
- package/src/lib/image/loadImage.ts +13 -0
- package/src/lib/interfaces/image-output.ts +8 -0
- package/src/lib/interfaces/index.ts +3 -0
- package/src/lib/interfaces/load-args-tmp.interface.ts +5 -0
- package/src/lib/interfaces/load-args.interface.ts +15 -0
- package/src/lib/math/baseSortedIndex.ts +43 -0
- package/src/lib/math/calculateMedian.ts +33 -0
- package/src/lib/math/circularIndex.ts +39 -0
- package/src/lib/math/clampf.ts +14 -0
- package/src/lib/math/degrees.ts +7 -0
- package/src/lib/math/floatCompare.ts +69 -0
- package/src/lib/math/index.ts +8 -0
- package/src/lib/math/inverseLerp.ts +38 -0
- package/src/lib/math/lerp.ts +12 -0
- package/src/lib/math/test/baseSortedIndex.spec.ts +43 -0
- package/src/lib/math/test/circularIndex.spec.ts +38 -0
- package/src/lib/mutex/Mutex.ts +50 -0
- package/src/lib/mutex/Semaphore.ts +62 -0
- package/src/lib/mutex/TaskRunner.ts +26 -0
- package/src/lib/mutex/index.ts +3 -0
- package/src/lib/predicates/BooleanPredictors.ts +47 -0
- package/src/lib/predicates/index.ts +3 -0
- package/src/lib/predicates/test/BooleanPredictors.spec.ts +71 -0
- package/src/lib/predicates/test/where.spec.ts +94 -0
- package/src/lib/predicates/textForSearch.ts +34 -0
- package/src/lib/predicates/where.ts +76 -0
- package/src/lib/rxjs/index.ts +3 -0
- package/src/lib/rxjs/leadingTrailingDebounceTime.ts +86 -0
- package/src/lib/rxjs/smoothTransition.ts +29 -0
- package/src/lib/rxjs/tapLog.ts +13 -0
- package/src/lib/strings/index.ts +1 -0
- package/src/lib/strings/pad.ts +18 -0
- package/src/public-api.ts +14 -0
- package/tsconfig.lib.json +13 -0
- package/tsconfig.lib.prod.json +11 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CSV2Array } from './CSV2Array';
|
|
2
|
+
|
|
3
|
+
const booleanRegex = /^(true|false)$/i;
|
|
4
|
+
const trueRegex = /^(true)$/i;
|
|
5
|
+
const jsonRegex = /([\d.]+)|(\[])|(\[.+])|({.+})/i;
|
|
6
|
+
|
|
7
|
+
const zipObject = (props: string[], values: any[]) =>
|
|
8
|
+
props.reduce(
|
|
9
|
+
(obj: any, prop, index) => ((obj[prop] = values[index]), obj),
|
|
10
|
+
{},
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const toHead = (input: any): string => {
|
|
14
|
+
return String(input).trim();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const toCorrectString = (input: any): any => {
|
|
18
|
+
if (typeof input === 'string') {
|
|
19
|
+
if (input.match(jsonRegex)) {
|
|
20
|
+
// optimization for use try-catch only after regex match. much faster than without regex
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(input);
|
|
23
|
+
} catch {
|
|
24
|
+
/* empty */
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (input.match(booleanRegex)) {
|
|
28
|
+
return !!input.match(trueRegex);
|
|
29
|
+
}
|
|
30
|
+
return input.trim();
|
|
31
|
+
} else if (typeof input === 'number') {
|
|
32
|
+
return input;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function CSV2Records(
|
|
37
|
+
csv: string,
|
|
38
|
+
useRowAsHead = 0,
|
|
39
|
+
strDelimiter?: string,
|
|
40
|
+
): any {
|
|
41
|
+
const arr = CSV2Array(csv, strDelimiter);
|
|
42
|
+
const total = arr.length;
|
|
43
|
+
const keys = arr[useRowAsHead].map(toHead);
|
|
44
|
+
const data: any[] = [];
|
|
45
|
+
|
|
46
|
+
let tmpSKU = 0;
|
|
47
|
+
|
|
48
|
+
for (let i = useRowAsHead + 1; i < total; i++) {
|
|
49
|
+
const obj = zipObject(keys, arr[i].map(toCorrectString));
|
|
50
|
+
data.push(obj);
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
52
|
+
tmpSKU++;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return data.filter(Boolean);
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function ObjectToCSV<T extends object>(records: T[]) {
|
|
2
|
+
if (records.length === 0) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const uniqueFields = Array.from(
|
|
7
|
+
records.reduce((fields, record) => {
|
|
8
|
+
Object.keys(record).forEach((field: string) => fields.add(field));
|
|
9
|
+
return fields;
|
|
10
|
+
}, new Set<string>()),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const csvHeader = uniqueFields.join(',') + '\n';
|
|
14
|
+
const csvRows = records
|
|
15
|
+
.map((record: any) =>
|
|
16
|
+
uniqueFields.map((field: any) => `"${record[field] || ''}"`).join(','),
|
|
17
|
+
)
|
|
18
|
+
.join('\n');
|
|
19
|
+
|
|
20
|
+
return csvHeader + csvRows;
|
|
21
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CSV2Records } from '@utils/csv/CSV2Records';
|
|
2
|
+
|
|
3
|
+
function generateMockCSV() {
|
|
4
|
+
const headers = '"name","age","isStudent"\n';
|
|
5
|
+
const mockData = [
|
|
6
|
+
{ name: 'Alice Johnson', age: 22, isStudent: true },
|
|
7
|
+
{ name: 'Bob Smith', age: 28, isStudent: false },
|
|
8
|
+
{ name: 'Charlie Brown', age: 19, isStudent: true },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const rows = mockData
|
|
12
|
+
.map((record) => `"${record.name}","${record.age}","${record.isStudent}"`)
|
|
13
|
+
.join('\n');
|
|
14
|
+
|
|
15
|
+
return headers + rows;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('CSV2Records with mocked data', () => {
|
|
19
|
+
let csv: string;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
csv = generateMockCSV();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should convert generated CSV to records correctly', () => {
|
|
26
|
+
const result = CSV2Records(csv, 0);
|
|
27
|
+
expect(result).toEqual([
|
|
28
|
+
{ name: 'Alice Johnson', age: 22, isStudent: true },
|
|
29
|
+
{ name: 'Bob Smith', age: 28, isStudent: false },
|
|
30
|
+
{ name: 'Charlie Brown', age: 19, isStudent: true },
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should correctly parse each field from the mock CSV', () => {
|
|
35
|
+
const result = CSV2Records(csv, 0);
|
|
36
|
+
expect(result.length).toBe(3);
|
|
37
|
+
|
|
38
|
+
// Verify individual properties directly
|
|
39
|
+
expect(result[0].name).toEqual('Alice Johnson');
|
|
40
|
+
expect(result[0].age).toEqual(22);
|
|
41
|
+
expect(result[0].isStudent).toEqual(true);
|
|
42
|
+
|
|
43
|
+
expect(result[1].name).toEqual('Bob Smith');
|
|
44
|
+
expect(result[1].age).toEqual(28);
|
|
45
|
+
expect(result[1].isStudent).toEqual(false);
|
|
46
|
+
|
|
47
|
+
expect(result[2].name).toEqual('Charlie Brown');
|
|
48
|
+
expect(result[2].age).toEqual(19);
|
|
49
|
+
expect(result[2].isStudent).toEqual(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const isLocalhost = !!window.location.href.match(/localhost/gi);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { timeUTCToString } from './timeToString';
|
|
2
|
+
import { Truthy } from '../predicates/BooleanPredictors';
|
|
3
|
+
|
|
4
|
+
export class Logger {
|
|
5
|
+
static isDevMode = Truthy(localStorage.getItem('devMode'));
|
|
6
|
+
|
|
7
|
+
static colored(color: string, background: string, ...args: unknown[]) {
|
|
8
|
+
if (!Logger.isDevMode) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const [first, ...rest] = args;
|
|
13
|
+
console.log.apply(console, [
|
|
14
|
+
`%c ${timeUTCToString()} ${first} `,
|
|
15
|
+
`color: ${color}; background: ${background};`,
|
|
16
|
+
...rest,
|
|
17
|
+
]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static log(...args: unknown[]) {
|
|
21
|
+
if (!Logger.isDevMode) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log.apply(console, [
|
|
26
|
+
`%c ${timeUTCToString()} [ LOG ] `,
|
|
27
|
+
'color: black; background: #80ff80;',
|
|
28
|
+
...args,
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static warn(...args: unknown[]) {
|
|
33
|
+
if (!Logger.isDevMode) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.warn.apply(console, [
|
|
38
|
+
`%c ${timeUTCToString()} WARN `,
|
|
39
|
+
'color: black; background: #ffd500;',
|
|
40
|
+
...args,
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static info(...args: unknown[]) {
|
|
45
|
+
if (!Logger.isDevMode) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log.apply(console, [
|
|
50
|
+
`%c ${timeUTCToString()} [ INFO ] `,
|
|
51
|
+
'color: white; background: blue;',
|
|
52
|
+
...args,
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static error(...args: unknown[]) {
|
|
57
|
+
if (!Logger.isDevMode) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.error.apply(console, [
|
|
62
|
+
`%c ${timeUTCToString()} ERROR `,
|
|
63
|
+
'color: white; background: red;',
|
|
64
|
+
...args,
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static table(...args: unknown[]) {
|
|
69
|
+
if (!Logger.isDevMode) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.table.apply(console, [args]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static time(...args: any) {
|
|
78
|
+
if (!Logger.isDevMode) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// eslint-disable-next-line no-console,prefer-spread
|
|
83
|
+
console.time.apply(console, args);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static timeEnd(...args: any) {
|
|
87
|
+
if (!Logger.isDevMode) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line no-console,prefer-spread
|
|
92
|
+
console.timeEnd.apply(console, args);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { pad } from '../strings';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a string of the form 'HOURS.MINUTES.SECONDS.MILLISECONDS'.
|
|
5
|
+
*/
|
|
6
|
+
export function timeToString() {
|
|
7
|
+
const date = new Date();
|
|
8
|
+
|
|
9
|
+
return `${pad(date.getHours(), 2)}:${pad(date.getMinutes(), 2)}:${pad(date.getSeconds(), 2)}.${pad(date.getMilliseconds(), 3)}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function timeUTCToString() {
|
|
13
|
+
const date = new Date();
|
|
14
|
+
|
|
15
|
+
return `${pad(date.getUTCHours(), 2)}:${pad(date.getUTCMinutes(), 2)}:${pad(date.getUTCSeconds(), 2)}.${pad(date.getUTCMilliseconds(), 3)}`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function cleanupFileName(value: string): string {
|
|
2
|
+
if (!value) {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
value = value
|
|
7
|
+
.toString()
|
|
8
|
+
.replace(/undefined/g, '')
|
|
9
|
+
.trim()
|
|
10
|
+
.replace(/\//g, '-')
|
|
11
|
+
.replace(/[-]+/g, '-')
|
|
12
|
+
.replace(/[^\w^.-]+/gi, '_')
|
|
13
|
+
.replace(/[\^]+/gi, '_');
|
|
14
|
+
|
|
15
|
+
value = value.split('_').filter(Boolean).join('_').toLowerCase();
|
|
16
|
+
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { cleanupFileName } from '@shared/utils/filenaming/cleanupFileName';
|
|
2
|
+
|
|
3
|
+
xdescribe('cleanupFileName', () => {
|
|
4
|
+
it('should pass', () => {
|
|
5
|
+
expect(
|
|
6
|
+
cleanupFileName('T__e*8\\//s!`~"\'-_test =;$%^any word&():,@#{t}e.r'),
|
|
7
|
+
).toEqual('t_e_8_-s_-_test_any_word_t_e.r');
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Area, Rect } from './interfaces';
|
|
2
|
+
|
|
3
|
+
export function expandOverRectangle(objectRect: Rect, areaRect: Rect): Area {
|
|
4
|
+
const ratio: number = objectRect.w / objectRect.h;
|
|
5
|
+
const result: Area = { x: 0, y: 0, w: 0, h: 0, scale: 1 };
|
|
6
|
+
if (areaRect.w / ratio < areaRect.h) {
|
|
7
|
+
result.w = areaRect.h * ratio;
|
|
8
|
+
result.h = areaRect.h;
|
|
9
|
+
} else {
|
|
10
|
+
result.w = areaRect.w;
|
|
11
|
+
result.h = areaRect.w / ratio;
|
|
12
|
+
}
|
|
13
|
+
result.x = areaRect.x + (areaRect.w - result.w) / 2;
|
|
14
|
+
result.y = areaRect.y + (areaRect.h - result.h) / 2;
|
|
15
|
+
result.scale = result.w / objectRect.w;
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Area, Rect } from './interfaces';
|
|
2
|
+
|
|
3
|
+
export function fitIntoRectangle(
|
|
4
|
+
objectRect: Rect,
|
|
5
|
+
areaRect: Rect,
|
|
6
|
+
round = true,
|
|
7
|
+
fixedRatio: number | null = null,
|
|
8
|
+
): Area {
|
|
9
|
+
const result: Area = { x: 0, y: 0, w: 0, h: 0, scale: 1 };
|
|
10
|
+
const ratio: number = objectRect.h / objectRect.w;
|
|
11
|
+
|
|
12
|
+
if (objectRect.h > objectRect.w) {
|
|
13
|
+
result.h = areaRect.h;
|
|
14
|
+
result.w = areaRect.h / ratio;
|
|
15
|
+
|
|
16
|
+
if (result.w > areaRect.w) {
|
|
17
|
+
result.w = areaRect.w;
|
|
18
|
+
result.h = areaRect.w * ratio;
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
result.w = areaRect.w;
|
|
22
|
+
result.h = areaRect.w * ratio;
|
|
23
|
+
|
|
24
|
+
if (result.h > areaRect.h) {
|
|
25
|
+
result.h = areaRect.h;
|
|
26
|
+
result.w = areaRect.h / ratio;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (fixedRatio) {
|
|
31
|
+
result.w = objectRect.w * fixedRatio;
|
|
32
|
+
result.h = objectRect.h * fixedRatio;
|
|
33
|
+
}
|
|
34
|
+
const wOut: number = round ? Math.floor(result.w) : result.w;
|
|
35
|
+
const hOut: number = round ? Math.floor(result.h) : result.h;
|
|
36
|
+
return {
|
|
37
|
+
x: areaRect.x + (areaRect.w - wOut) / 2,
|
|
38
|
+
y: areaRect.y + (areaRect.h - hOut) / 2,
|
|
39
|
+
w: wOut,
|
|
40
|
+
h: hOut,
|
|
41
|
+
scale: wOut / objectRect.w,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { fitIntoRectangle } from '@shared/utils/geom/fitIntoRectangle';
|
|
2
|
+
|
|
3
|
+
xdescribe('fitRectangle', () => {
|
|
4
|
+
it('should pass', () => {
|
|
5
|
+
expect(
|
|
6
|
+
fitIntoRectangle(
|
|
7
|
+
{ x: 0, y: 0, w: 100, h: 100 },
|
|
8
|
+
{ x: 0, y: 0, w: 200, h: 150 },
|
|
9
|
+
),
|
|
10
|
+
).toEqual({ x: 25, y: 0, w: 150, h: 150, scale: 2 });
|
|
11
|
+
expect(
|
|
12
|
+
fitIntoRectangle(
|
|
13
|
+
{ x: 0, y: 0, w: 100, h: 100 },
|
|
14
|
+
{ x: 0, y: 0, w: 50, h: 150 },
|
|
15
|
+
),
|
|
16
|
+
).toEqual({ x: 0, y: 50, w: 50, h: 50, scale: 0.5 });
|
|
17
|
+
expect(
|
|
18
|
+
fitIntoRectangle(
|
|
19
|
+
{ x: 0, y: 0, w: 100, h: 100 },
|
|
20
|
+
{ x: 0, y: 0, w: 150, h: 150 },
|
|
21
|
+
),
|
|
22
|
+
).toEqual({ x: 0, y: 0, w: 150, h: 150, scale: 1.5 });
|
|
23
|
+
expect(
|
|
24
|
+
fitIntoRectangle(
|
|
25
|
+
{ x: 0, y: 0, w: 200, h: 200 },
|
|
26
|
+
{ x: 0, y: 0, w: 150, h: 150 },
|
|
27
|
+
),
|
|
28
|
+
).toEqual({ x: 0, y: 0, w: 150, h: 150, scale: 0.75 });
|
|
29
|
+
expect(
|
|
30
|
+
fitIntoRectangle(
|
|
31
|
+
{ x: 0, y: 0, w: 200, h: 200 },
|
|
32
|
+
{ x: 10, y: 10, w: 150, h: 150 },
|
|
33
|
+
),
|
|
34
|
+
).toEqual({
|
|
35
|
+
x: 10,
|
|
36
|
+
y: 10,
|
|
37
|
+
w: 150,
|
|
38
|
+
h: 150,
|
|
39
|
+
scale: 0.75,
|
|
40
|
+
});
|
|
41
|
+
expect(
|
|
42
|
+
fitIntoRectangle(
|
|
43
|
+
{ x: 0, y: 0, w: 200, h: 300 },
|
|
44
|
+
{ x: 10, y: 10, w: 210, h: 150 },
|
|
45
|
+
),
|
|
46
|
+
).toEqual({
|
|
47
|
+
x: 65,
|
|
48
|
+
y: 10,
|
|
49
|
+
w: 100,
|
|
50
|
+
h: 150,
|
|
51
|
+
scale: 1.05,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|