@remotion/paths
Advanced tools
Comparing version
@@ -6,3 +6,4 @@ import type { Part } from './helpers/types'; | ||
* @link https://remotion.dev/docs/paths/get-parts | ||
* @deprecated In favor of getSubpaths() | ||
*/ | ||
export declare const getParts: (path: string) => Part[]; |
@@ -9,2 +9,3 @@ "use strict"; | ||
* @link https://remotion.dev/docs/paths/get-parts | ||
* @deprecated In favor of getSubpaths() | ||
*/ | ||
@@ -11,0 +12,0 @@ const getParts = (path) => { |
@@ -0,3 +1,5 @@ | ||
import type { Instruction } from './parse'; | ||
import type { Point, Properties } from './types'; | ||
export declare const construct: (string: string) => { | ||
segments: Instruction[][]; | ||
initial_point: Point | null; | ||
@@ -4,0 +6,0 @@ length: number; |
"use strict"; | ||
// Copied from: https://github.com/rveciana/svg-path-properties | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -11,3 +8,3 @@ exports.construct = void 0; | ||
const linear_1 = require("./linear"); | ||
const parse_1 = __importDefault(require("./parse")); | ||
const parse_1 = require("./parse"); | ||
const construct = (string) => { | ||
@@ -18,3 +15,3 @@ let length = 0; | ||
let initial_point = null; | ||
const parsed = (0, parse_1.default)(string); | ||
const parsed = (0, parse_1.parsePath)(string); | ||
let cur = [0, 0]; | ||
@@ -24,50 +21,57 @@ let prev_point = [0, 0]; | ||
let ringStart = [0, 0]; | ||
const segments = []; | ||
for (let i = 0; i < parsed.length; i++) { | ||
const instruction = parsed[i]; | ||
if (instruction[0].toLowerCase() !== 'm' && segments.length > 0) { | ||
segments[segments.length - 1].push(instruction); | ||
} | ||
// moveTo | ||
if (parsed[i][0] === 'M') { | ||
cur = [parsed[i][1], parsed[i][2]]; | ||
if (instruction[0] === 'M') { | ||
cur = [instruction[1], instruction[2]]; | ||
ringStart = [cur[0], cur[1]]; | ||
segments.push([instruction]); | ||
functions.push(null); | ||
if (i === 0) { | ||
initial_point = { x: parsed[i][1], y: parsed[i][2] }; | ||
initial_point = { x: instruction[1], y: instruction[2] }; | ||
} | ||
} | ||
else if (parsed[i][0] === 'm') { | ||
cur = [parsed[i][1] + cur[0], parsed[i][2] + cur[1]]; | ||
else if (instruction[0] === 'm') { | ||
cur = [instruction[1] + cur[0], instruction[2] + cur[1]]; | ||
ringStart = [cur[0], cur[1]]; | ||
segments.push([['M', cur[0], cur[1]]]); | ||
functions.push(null); | ||
// lineTo | ||
} | ||
else if (parsed[i][0] === 'L') { | ||
length += Math.sqrt((cur[0] - parsed[i][1]) ** 2 + (cur[1] - parsed[i][2]) ** 2); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], parsed[i][1], cur[1], parsed[i][2])); | ||
cur = [parsed[i][1], parsed[i][2]]; | ||
else if (instruction[0] === 'L') { | ||
length += Math.sqrt((cur[0] - instruction[1]) ** 2 + (cur[1] - instruction[2]) ** 2); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], instruction[1], cur[1], instruction[2])); | ||
cur = [instruction[1], instruction[2]]; | ||
} | ||
else if (parsed[i][0] === 'l') { | ||
length += Math.sqrt(parsed[i][1] ** 2 + parsed[i][2] ** 2); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], parsed[i][1] + cur[0], cur[1], parsed[i][2] + cur[1])); | ||
cur = [parsed[i][1] + cur[0], parsed[i][2] + cur[1]]; | ||
else if (instruction[0] === 'l') { | ||
length += Math.sqrt(instruction[1] ** 2 + instruction[2] ** 2); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], instruction[1] + cur[0], cur[1], instruction[2] + cur[1])); | ||
cur = [instruction[1] + cur[0], instruction[2] + cur[1]]; | ||
} | ||
else if (parsed[i][0] === 'H') { | ||
length += Math.abs(cur[0] - parsed[i][1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], parsed[i][1], cur[1], cur[1])); | ||
cur[0] = parsed[i][1]; | ||
else if (instruction[0] === 'H') { | ||
length += Math.abs(cur[0] - instruction[1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], instruction[1], cur[1], cur[1])); | ||
cur[0] = instruction[1]; | ||
} | ||
else if (parsed[i][0] === 'h') { | ||
length += Math.abs(parsed[i][1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], cur[0] + parsed[i][1], cur[1], cur[1])); | ||
cur[0] = parsed[i][1] + cur[0]; | ||
else if (instruction[0] === 'h') { | ||
length += Math.abs(instruction[1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], cur[0] + instruction[1], cur[1], cur[1])); | ||
cur[0] = instruction[1] + cur[0]; | ||
} | ||
else if (parsed[i][0] === 'V') { | ||
length += Math.abs(cur[1] - parsed[i][1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], cur[0], cur[1], parsed[i][1])); | ||
cur[1] = parsed[i][1]; | ||
else if (instruction[0] === 'V') { | ||
length += Math.abs(cur[1] - instruction[1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], cur[0], cur[1], instruction[1])); | ||
cur[1] = instruction[1]; | ||
} | ||
else if (parsed[i][0] === 'v') { | ||
length += Math.abs(parsed[i][1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], cur[0], cur[1], cur[1] + parsed[i][1])); | ||
cur[1] = parsed[i][1] + cur[1]; | ||
else if (instruction[0] === 'v') { | ||
length += Math.abs(instruction[1]); | ||
functions.push((0, linear_1.makeLinearPosition)(cur[0], cur[0], cur[1], cur[1] + instruction[1])); | ||
cur[1] = instruction[1] + cur[1]; | ||
// Close path | ||
} | ||
else if (parsed[i][0] === 'z' || parsed[i][0] === 'Z') { | ||
else if (instruction[0] === 'z' || instruction[0] === 'Z') { | ||
length += Math.sqrt((ringStart[0] - cur[0]) ** 2 + (ringStart[1] - cur[1]) ** 2); | ||
@@ -78,27 +82,27 @@ functions.push((0, linear_1.makeLinearPosition)(cur[0], ringStart[0], cur[1], ringStart[1])); | ||
} | ||
else if (parsed[i][0] === 'C') { | ||
else if (instruction[0] === 'C') { | ||
curve = (0, bezier_1.makeBezier)({ | ||
ax: cur[0], | ||
ay: cur[1], | ||
bx: parsed[i][1], | ||
by: parsed[i][2], | ||
cx: parsed[i][3], | ||
cy: parsed[i][4], | ||
dx: parsed[i][5], | ||
dy: parsed[i][6], | ||
bx: instruction[1], | ||
by: instruction[2], | ||
cx: instruction[3], | ||
cy: instruction[4], | ||
dx: instruction[5], | ||
dy: instruction[6], | ||
}); | ||
length += curve.getTotalLength(); | ||
cur = [parsed[i][5], parsed[i][6]]; | ||
cur = [instruction[5], instruction[6]]; | ||
functions.push(curve); | ||
} | ||
else if (parsed[i][0] === 'c') { | ||
else if (instruction[0] === 'c') { | ||
curve = (0, bezier_1.makeBezier)({ | ||
ax: cur[0], | ||
ay: cur[1], | ||
bx: cur[0] + parsed[i][1], | ||
by: cur[1] + parsed[i][2], | ||
cx: cur[0] + parsed[i][3], | ||
cy: cur[1] + parsed[i][4], | ||
dx: cur[0] + parsed[i][5], | ||
dy: cur[1] + parsed[i][6], | ||
bx: cur[0] + instruction[1], | ||
by: cur[1] + instruction[2], | ||
cx: cur[0] + instruction[3], | ||
cy: cur[1] + instruction[4], | ||
dx: cur[0] + instruction[5], | ||
dy: cur[1] + instruction[6], | ||
}); | ||
@@ -108,3 +112,3 @@ if (curve.getTotalLength() > 0) { | ||
functions.push(curve); | ||
cur = [parsed[i][5] + cur[0], parsed[i][6] + cur[1]]; | ||
cur = [instruction[5] + cur[0], instruction[6] + cur[1]]; | ||
} | ||
@@ -115,3 +119,3 @@ else { | ||
} | ||
else if (parsed[i][0] === 'S') { | ||
else if (instruction[0] === 'S') { | ||
if (i > 0 && ['C', 'c', 'S', 's'].indexOf(parsed[i - 1][0]) > -1) { | ||
@@ -125,6 +129,6 @@ if (curve) { | ||
by: 2 * cur[1] - c.y, | ||
cx: parsed[i][1], | ||
cy: parsed[i][2], | ||
dx: parsed[i][3], | ||
dy: parsed[i][4], | ||
cx: instruction[1], | ||
cy: instruction[2], | ||
dx: instruction[3], | ||
dy: instruction[4], | ||
}); | ||
@@ -139,6 +143,6 @@ } | ||
by: cur[1], | ||
cx: parsed[i][1], | ||
cy: parsed[i][2], | ||
dx: parsed[i][3], | ||
dy: parsed[i][4], | ||
cx: instruction[1], | ||
cy: instruction[2], | ||
dx: instruction[3], | ||
dy: instruction[4], | ||
}); | ||
@@ -148,7 +152,7 @@ } | ||
length += curve.getTotalLength(); | ||
cur = [parsed[i][3], parsed[i][4]]; | ||
cur = [instruction[3], instruction[4]]; | ||
functions.push(curve); | ||
} | ||
} | ||
else if (parsed[i][0] === 's') { | ||
else if (instruction[0] === 's') { | ||
// 240 225 | ||
@@ -164,6 +168,6 @@ if (i > 0 && ['C', 'c', 'S', 's'].indexOf(parsed[i - 1][0]) > -1) { | ||
by: cur[1] + d.y - c.y, | ||
cx: cur[0] + parsed[i][1], | ||
cy: cur[1] + parsed[i][2], | ||
dx: cur[0] + parsed[i][3], | ||
dy: cur[1] + parsed[i][4], | ||
cx: cur[0] + instruction[1], | ||
cy: cur[1] + instruction[2], | ||
dx: cur[0] + instruction[3], | ||
dy: cur[1] + instruction[4], | ||
}); | ||
@@ -178,6 +182,6 @@ } | ||
by: cur[1], | ||
cx: cur[0] + parsed[i][1], | ||
cy: cur[1] + parsed[i][2], | ||
dx: cur[0] + parsed[i][3], | ||
dy: cur[1] + parsed[i][4], | ||
cx: cur[0] + instruction[1], | ||
cy: cur[1] + instruction[2], | ||
dx: cur[0] + instruction[3], | ||
dy: cur[1] + instruction[4], | ||
}); | ||
@@ -187,3 +191,3 @@ } | ||
length += curve.getTotalLength(); | ||
cur = [parsed[i][3] + cur[0], parsed[i][4] + cur[1]]; | ||
cur = [instruction[3] + cur[0], instruction[4] + cur[1]]; | ||
functions.push(curve); | ||
@@ -193,5 +197,5 @@ } | ||
// Quadratic Bezier curves | ||
else if (parsed[i][0] === 'Q') { | ||
if (cur[0] === parsed[i][1] && cur[1] === parsed[i][2]) { | ||
const linearCurve = (0, linear_1.makeLinearPosition)(parsed[i][1], parsed[i][3], parsed[i][2], parsed[i][4]); | ||
else if (instruction[0] === 'Q') { | ||
if (cur[0] === instruction[1] && cur[1] === instruction[2]) { | ||
const linearCurve = (0, linear_1.makeLinearPosition)(instruction[1], instruction[3], instruction[2], instruction[4]); | ||
length += linearCurve.getTotalLength(); | ||
@@ -204,6 +208,6 @@ functions.push(linearCurve); | ||
ay: cur[1], | ||
bx: parsed[i][1], | ||
by: parsed[i][2], | ||
cx: parsed[i][3], | ||
cy: parsed[i][4], | ||
bx: instruction[1], | ||
by: instruction[2], | ||
cx: instruction[3], | ||
cy: instruction[4], | ||
dx: null, | ||
@@ -215,8 +219,8 @@ dy: null, | ||
} | ||
cur = [parsed[i][3], parsed[i][4]]; | ||
prev_point = [parsed[i][1], parsed[i][2]]; | ||
cur = [instruction[3], instruction[4]]; | ||
prev_point = [instruction[1], instruction[2]]; | ||
} | ||
else if (parsed[i][0] === 'q') { | ||
if (parsed[i][1] === 0 && parsed[i][2] === 0) { | ||
const linearCurve = (0, linear_1.makeLinearPosition)(cur[0] + parsed[i][1], cur[0] + parsed[i][3], cur[1] + parsed[i][2], cur[1] + parsed[i][4]); | ||
else if (instruction[0] === 'q') { | ||
if (instruction[1] === 0 && instruction[2] === 0) { | ||
const linearCurve = (0, linear_1.makeLinearPosition)(cur[0] + instruction[1], cur[0] + instruction[3], cur[1] + instruction[2], cur[1] + instruction[4]); | ||
length += linearCurve.getTotalLength(); | ||
@@ -229,6 +233,6 @@ functions.push(linearCurve); | ||
ay: cur[1], | ||
bx: cur[0] + parsed[i][1], | ||
by: cur[1] + parsed[i][2], | ||
cx: cur[0] + parsed[i][3], | ||
cy: cur[1] + parsed[i][4], | ||
bx: cur[0] + instruction[1], | ||
by: cur[1] + instruction[2], | ||
cx: cur[0] + instruction[3], | ||
cy: cur[1] + instruction[4], | ||
dx: null, | ||
@@ -240,6 +244,6 @@ dy: null, | ||
} | ||
prev_point = [cur[0] + parsed[i][1], cur[1] + parsed[i][2]]; | ||
cur = [parsed[i][3] + cur[0], parsed[i][4] + cur[1]]; | ||
prev_point = [cur[0] + instruction[1], cur[1] + instruction[2]]; | ||
cur = [instruction[3] + cur[0], instruction[4] + cur[1]]; | ||
} | ||
else if (parsed[i][0] === 'T') { | ||
else if (instruction[0] === 'T') { | ||
if (i > 0 && ['Q', 'q', 'T', 't'].indexOf(parsed[i - 1][0]) > -1) { | ||
@@ -251,4 +255,4 @@ curve = (0, bezier_1.makeBezier)({ | ||
by: 2 * cur[1] - prev_point[1], | ||
cx: parsed[i][1], | ||
cy: parsed[i][2], | ||
cx: instruction[1], | ||
cy: instruction[2], | ||
dx: null, | ||
@@ -261,3 +265,3 @@ dy: null, | ||
else { | ||
const linearCurve = (0, linear_1.makeLinearPosition)(cur[0], parsed[i][1], cur[1], parsed[i][2]); | ||
const linearCurve = (0, linear_1.makeLinearPosition)(cur[0], instruction[1], cur[1], instruction[2]); | ||
functions.push(linearCurve); | ||
@@ -267,5 +271,5 @@ length += linearCurve.getTotalLength(); | ||
prev_point = [2 * cur[0] - prev_point[0], 2 * cur[1] - prev_point[1]]; | ||
cur = [parsed[i][1], parsed[i][2]]; | ||
cur = [instruction[1], instruction[2]]; | ||
} | ||
else if (parsed[i][0] === 't') { | ||
else if (instruction[0] === 't') { | ||
if (i > 0 && ['Q', 'q', 'T', 't'].indexOf(parsed[i - 1][0]) > -1) { | ||
@@ -277,4 +281,4 @@ curve = (0, bezier_1.makeBezier)({ | ||
by: 2 * cur[1] - prev_point[1], | ||
cx: cur[0] + parsed[i][1], | ||
cy: cur[1] + parsed[i][2], | ||
cx: cur[0] + instruction[1], | ||
cy: cur[1] + instruction[2], | ||
dx: null, | ||
@@ -287,3 +291,3 @@ dy: null, | ||
else { | ||
const linearCurve = (0, linear_1.makeLinearPosition)(cur[0], cur[0] + parsed[i][1], cur[1], cur[1] + parsed[i][2]); | ||
const linearCurve = (0, linear_1.makeLinearPosition)(cur[0], cur[0] + instruction[1], cur[1], cur[1] + instruction[2]); | ||
length += linearCurve.getTotalLength(); | ||
@@ -293,34 +297,34 @@ functions.push(linearCurve); | ||
prev_point = [2 * cur[0] - prev_point[0], 2 * cur[1] - prev_point[1]]; | ||
cur = [parsed[i][1] + cur[0], parsed[i][2] + cur[1]]; | ||
cur = [instruction[1] + cur[0], instruction[2] + cur[1]]; | ||
} | ||
else if (parsed[i][0] === 'A') { | ||
else if (instruction[0] === 'A') { | ||
const arcCurve = (0, arc_1.makeArc)({ | ||
x0: cur[0], | ||
y0: cur[1], | ||
rx: parsed[i][1], | ||
ry: parsed[i][2], | ||
xAxisRotate: parsed[i][3], | ||
LargeArcFlag: parsed[i][4] === 1, | ||
SweepFlag: parsed[i][5] === 1, | ||
x1: parsed[i][6], | ||
y1: parsed[i][7], | ||
rx: instruction[1], | ||
ry: instruction[2], | ||
xAxisRotate: instruction[3], | ||
LargeArcFlag: instruction[4] === 1, | ||
SweepFlag: instruction[5] === 1, | ||
x1: instruction[6], | ||
y1: instruction[7], | ||
}); | ||
length += arcCurve.getTotalLength(); | ||
cur = [parsed[i][6], parsed[i][7]]; | ||
cur = [instruction[6], instruction[7]]; | ||
functions.push(arcCurve); | ||
} | ||
else if (parsed[i][0] === 'a') { | ||
else if (instruction[0] === 'a') { | ||
const arcCurve = (0, arc_1.makeArc)({ | ||
x0: cur[0], | ||
y0: cur[1], | ||
rx: parsed[i][1], | ||
ry: parsed[i][2], | ||
xAxisRotate: parsed[i][3], | ||
LargeArcFlag: parsed[i][4] === 1, | ||
SweepFlag: parsed[i][5] === 1, | ||
x1: cur[0] + parsed[i][6], | ||
y1: cur[1] + parsed[i][7], | ||
rx: instruction[1], | ||
ry: instruction[2], | ||
xAxisRotate: instruction[3], | ||
LargeArcFlag: instruction[4] === 1, | ||
SweepFlag: instruction[5] === 1, | ||
x1: cur[0] + instruction[6], | ||
y1: cur[1] + instruction[7], | ||
}); | ||
length += arcCurve.getTotalLength(); | ||
cur = [cur[0] + parsed[i][6], cur[1] + parsed[i][7]]; | ||
cur = [cur[0] + instruction[6], cur[1] + instruction[7]]; | ||
functions.push(arcCurve); | ||
@@ -330,4 +334,10 @@ } | ||
} | ||
return { initial_point, length, partial_lengths, functions }; | ||
return { | ||
segments, | ||
initial_point, | ||
length, | ||
partial_lengths, | ||
functions, | ||
}; | ||
}; | ||
exports.construct = construct; |
@@ -1,2 +0,2 @@ | ||
declare const _default: (path: string) => [string, ...number[]][]; | ||
export default _default; | ||
export declare type Instruction = [string, ...number[]]; | ||
export declare const parsePath: (path: string) => Instruction[]; |
"use strict"; | ||
// Copied from: https://github.com/rveciana/svg-path-properties | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parsePath = void 0; | ||
const length = { | ||
@@ -18,3 +19,3 @@ a: 7, | ||
const numberRegExp = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/gi; | ||
exports.default = (path) => { | ||
const parsePath = (path) => { | ||
const segments = (path && path.length > 0 ? path : 'M0,0').match(segmentRegExp); | ||
@@ -47,2 +48,3 @@ if (!segments) { | ||
}; | ||
exports.parsePath = parsePath; | ||
const parseValues = (args) => { | ||
@@ -49,0 +51,0 @@ const numbers = args.match(numberRegExp); |
@@ -6,2 +6,3 @@ export { evolvePath } from './evolve-path'; | ||
export { getPointAtLength } from './get-point-at-length'; | ||
export { getSubpaths } from './get-subpaths'; | ||
export { getTangentAtLength } from './get-tangent-at-length'; | ||
@@ -12,1 +13,2 @@ export { Part } from './helpers/types'; | ||
export { reversePath } from './reverse-path'; | ||
export { translatePath } from './translate-path'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.reversePath = exports.normalizePath = exports.interpolatePath = exports.getTangentAtLength = exports.getPointAtLength = exports.getParts = exports.getLength = exports.extendViewBox = exports.evolvePath = void 0; | ||
exports.translatePath = exports.reversePath = exports.normalizePath = exports.interpolatePath = exports.getTangentAtLength = exports.getSubpaths = exports.getPointAtLength = exports.getParts = exports.getLength = exports.extendViewBox = exports.evolvePath = void 0; | ||
var evolve_path_1 = require("./evolve-path"); | ||
@@ -14,2 +14,4 @@ Object.defineProperty(exports, "evolvePath", { enumerable: true, get: function () { return evolve_path_1.evolvePath; } }); | ||
Object.defineProperty(exports, "getPointAtLength", { enumerable: true, get: function () { return get_point_at_length_1.getPointAtLength; } }); | ||
var get_subpaths_1 = require("./get-subpaths"); | ||
Object.defineProperty(exports, "getSubpaths", { enumerable: true, get: function () { return get_subpaths_1.getSubpaths; } }); | ||
var get_tangent_at_length_1 = require("./get-tangent-at-length"); | ||
@@ -23,1 +25,3 @@ Object.defineProperty(exports, "getTangentAtLength", { enumerable: true, get: function () { return get_tangent_at_length_1.getTangentAtLength; } }); | ||
Object.defineProperty(exports, "reversePath", { enumerable: true, get: function () { return reverse_path_1.reversePath; } }); | ||
var translate_path_1 = require("./translate-path"); | ||
Object.defineProperty(exports, "translatePath", { enumerable: true, get: function () { return translate_path_1.translatePath; } }); |
@@ -1,2 +0,2 @@ | ||
Copyright (c) 2022 JonnyBurger | ||
Copyright (c) 2023 JonnyBurger | ||
@@ -3,0 +3,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
{ | ||
"name": "@remotion/paths", | ||
"version": "4.0.0-copy.4+07d4897fb", | ||
"version": "4.0.0-esm.17+da08690d2", | ||
"description": "Utility functions for SVG paths", | ||
@@ -38,3 +38,3 @@ "main": "dist/index.js", | ||
}, | ||
"gitHead": "07d4897fb44920b58dc85cf7465d990a0a139994" | ||
"gitHead": "da08690d241e1f5d6dde8a8d89f3379a54ec7681" | ||
} |
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
123369
2.71%47
4.44%3015
2.27%