Compare commits

..
5 Commits
10 changed files with 1192 additions and 214 deletions

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
# Parachute-SVG-Generator
Generate the SVG outline of a parachute panel for an elliptical parachute.
Generate the SVG outline of a parachute panel for an elliptical parachute
+9 -3
View File
@@ -14,6 +14,11 @@
<option value="cm">Centimetres</option>
<option value="in">Inches</option>
</select> <br>
<label for="p">Paper size: </label>
<select id="p">
<option value="a3">A3</option>
<option value="a4">A4</option>
</select> <br>
<label for="h">Height of parachute (h):</label>
<input type="number" id="h" min="0"/><br>
<label for="r">Radius of parachute (r):</label>
@@ -24,9 +29,10 @@
<input type="number" id="np" min="3" ><br>
<label for="steps">Number of steps: </label>
<input type="number" id="steps" min="10" /><br>
<button onclick="generatePanel()">Generate!</button>
<button id="generate">Generate!</button>
<p id="len"></p>
<div id="output"></div>
<script src="index.js" lang="js"></script>
<div id="output" style="display: none;"></div>
<iframe id="pdf" style="width:100%;height:1000px"></iframe>
<script src="src/main.js" lang="js" type="module"></script>
</body>
</html>
-208
View File
@@ -1,208 +0,0 @@
let uInput = document.getElementById("u");
let hInput = document.getElementById("h");
let rInput = document.getElementById("r");
let rsInput = document.getElementById("rs");
let npInput = document.getElementById("np");
let outputdiv = document.getElementById("output");
let stepInput = document.getElementById("steps");
let lenOutput = document.getElementById("len");
let height = 0;
let radius = 0;
let spillRadius = 0;
let numberOfPanels = 0;
let multiplier = 1;
let accuracy = 8;
let step = 0.001 * multiplier;
// const offset = 3*10**-15 * multiplier;
const offset = 4 * 10 ** -5 * multiplier;
let pointsP = [];
let pointsQ = [];
let distances = [];
let panelPoints = [];
let n = 0;
let unit = "px";
let strokeWidth = 1;
// maths
function approx(n) {
return Math.round(n * (10 ** accuracy)) / (10 ** accuracy);
}
function f(x) {
res = approx(height * Math.sqrt((1 - (Math.pow(x, 2) / Math.pow(radius, 2)))));
console.log(`f ${x} ${res}`);
return res;
}
function fprime(x) {
res = approx(-1 * ((Math.pow(height, 2) * x) / (Math.pow(radius, 2) * f(x))));
console.log(`fprime ${x} ${res}`);
return res;
}
function g(x, P) {
res = approx(fprime(x) * (x - P.x) + P.y);
console.log(`g ${x} ${P.x} ${P.y} ${res}`);
return res;
}
function j(A, B) {
res = approx(A.x * fprime(A.x) - B.x * fprime(B.x) - A.y + B.y) / (fprime(A.x) - fprime(B.x));
console.log(`j ${A.x} ${A.y} ${B.x} ${B.y} ${res}`);
return res;
}
function d(A, B) {
res = approx(Math.sqrt(Math.pow(A.x - B.x, 2) + Math.pow(A.y - B.y, 2)));
console.log(`d ${A.x} ${A.y} ${B.x} ${B.y} ${res}`);
return res;
}
// logic
function getValues() {
pointsP = [];
pointsQ = [];
distances = [];
panelPoints = [];
height = /* /* Math.floor */(parseFloat(hInput.value) * multiplier);
radius = /* Math.floor */(parseFloat(rInput.value) * multiplier);
spillRadius = /* Math.floor */(parseFloat(rsInput.value) * multiplier);
numberOfPanels = /* Math.floor */(parseFloat(npInput.value) * multiplier);
step = radius / parseInt(stepInput.value);
if (isNaN(spillRadius)) {
spillRadius = /* Math.floor */(0.05 * radius);
rsInput.value = 0.05 * radius / multiplier;
}
n = ((radius - spillRadius) / (step - offset)) - 1;
console.log(`${height}, ${radius}, ${spillRadius}, ${step}, ${offset}`);
}
function getPoints() {
for (let x = spillRadius; x < radius; x += step - offset) {
x = approx(x);
pointsP.push({
x: x,
y: f(x)
});
}
}
function getPointsOfTangentIntersection() {
for (let i = 0; i < n; i++) {
console.log(`${i}, ${pointsP[i]}, ${pointsP[i + 1]}`);
pointsQ.push({
x: j(pointsP[i], pointsP[i + 1]),
y: g(j(pointsP[i], pointsP[i + 1]), pointsP[i + 1])
});
}
}
function getDistancesBetweenIntersections() {
for (let i = 0; i < n - 1; i++) {
distances.push(d(pointsQ[i], pointsQ[i + 1]));
}
}
function generateCoordinates() {
let Xi = 0;
for (let i = 0; i < n - 2; i++) {
Xi += distances[i];
Yi = (Math.PI * pointsQ[i].x) / numberOfPanels;
panelPoints.push({
x: Xi,
y: Yi
});
}
}
function plotSVG() {
let maxY = 0;
let maxX = 0;
let minX = 1000;
panelPoints.forEach((point) => {
if (maxY < point.y) {
maxY = point.y;
}
if (minX > point.x) {
minX = point.x;
}
if (maxX < point.x) {
maxX = point.x;
}
})
let pointsA = "";
let pointsB = "";
for (let i = 0; i < panelPoints.length; i++) {
let point = panelPoints[i];
pointsA += `${approx(point.x)},${(maxY) - approx(point.y)} `;
pointsB +=`${approx(point.x)},${approx((point.y + maxY))} `;
}
outputdiv.innerHTML = `
<svg width="297mm"
height="210mm"
xmlns="http://www.w3.org/2000/svg">
<g id="layer1">
<line
x1="${approx(panelPoints[0].x)}${unit}"
x2="${panelPoints[0].x}${unit}"
y1="${maxY - approx(panelPoints[0].y)}${unit}"
y2="${approx((panelPoints[0].y + maxY))}${unit}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
<line
x1="${approx(panelPoints[panelPoints.length - 1].x)}${unit}"
x2="${panelPoints[panelPoints.length - 1].x}${unit}"
y1="${maxY - approx(panelPoints[panelPoints.length - 1].y)}${unit}"
y2="${approx((panelPoints[panelPoints.length - 1].y + maxY))}${unit}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
<line
x1="${approx(panelPoints[0].x)}${unit}"
x2="${panelPoints[panelPoints.length - 1].x}${unit}"
y1="${(maxY - approx(panelPoints[0].y) + approx((panelPoints[0].y + maxY))) / 2}${unit}"
y2="${(maxY - approx(panelPoints[panelPoints.length - 1].y) + approx((panelPoints[panelPoints.length - 1].y + maxY))) / 2}${unit}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
<polyline
points="${pointsA}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
<polyline
points="${pointsB}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
</g>
</svg>
`;
lenOutput.innerText = `Recommended length: ${approx(maxX - minX)} ${unit}`;
}
// main func
function generatePanel() {
unit=uInput.value;
getValues();
getPoints();
getPointsOfTangentIntersection();
getDistancesBetweenIntersections();
generateCoordinates();
plotSVG();
}
+820 -1
View File
@@ -7,7 +7,826 @@
"": {
"name": "parachute-svg-generator",
"version": "0.1.0",
"license": "SEE LICENSE IN LICENSE"
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"pdf-lib": "^1.17.1",
"vite": "^8.3.2"
}
},
"node_modules/@oxc-project/types": {
"version": "0.152.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz",
"integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/oxc-project"
}
},
"node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.6"
}
},
"node_modules/@pdf-lib/upng": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.10"
}
},
"node_modules/@rolldown/binding-android-arm-eabi": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz",
"integrity": "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.12.tgz",
"integrity": "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.12.tgz",
"integrity": "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.12.tgz",
"integrity": "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.12.tgz",
"integrity": "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.12.tgz",
"integrity": "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.12.tgz",
"integrity": "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.12.tgz",
"integrity": "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.12.tgz",
"integrity": "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==",
"cpu": [
"ppc64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.12.tgz",
"integrity": "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==",
"cpu": [
"s390x"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.12.tgz",
"integrity": "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.12.tgz",
"integrity": "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.12.tgz",
"integrity": "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.12.tgz",
"integrity": "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.12.tgz",
"integrity": "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/lightningcss": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.33.0",
"lightningcss-darwin-arm64": "1.33.0",
"lightningcss-darwin-x64": "1.33.0",
"lightningcss-freebsd-x64": "1.33.0",
"lightningcss-linux-arm-gnueabihf": "1.33.0",
"lightningcss-linux-arm64-gnu": "1.33.0",
"lightningcss-linux-arm64-musl": "1.33.0",
"lightningcss-linux-x64-gnu": "1.33.0",
"lightningcss-linux-x64-musl": "1.33.0",
"lightningcss-win32-arm64-msvc": "1.33.0",
"lightningcss-win32-x64-msvc": "1.33.0"
}
},
"node_modules/lightningcss-android-arm64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
"cpu": [
"arm"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/nanoid": {
"version": "3.3.19",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
"integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/pdf-lib": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
"license": "MIT",
"dependencies": {
"@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1",
"pako": "^1.0.11",
"tslib": "^1.11.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.5.28",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
"integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.18",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rolldown": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz",
"integrity": "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==",
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.152.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
"rolldown": "bin/cli.mjs"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm-eabi": "1.2.12",
"@rolldown/binding-android-arm64": "1.2.12",
"@rolldown/binding-darwin-arm64": "1.2.12",
"@rolldown/binding-darwin-x64": "1.2.12",
"@rolldown/binding-freebsd-x64": "1.2.12",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.12",
"@rolldown/binding-linux-arm64-gnu": "1.2.12",
"@rolldown/binding-linux-arm64-musl": "1.2.12",
"@rolldown/binding-linux-ppc64-gnu": "1.2.12",
"@rolldown/binding-linux-s390x-gnu": "1.2.12",
"@rolldown/binding-linux-x64-gnu": "1.2.12",
"@rolldown/binding-linux-x64-musl": "1.2.12",
"@rolldown/binding-openharmony-arm64": "1.2.12",
"@rolldown/binding-win32-arm64-msvc": "1.2.12",
"@rolldown/binding-win32-x64-msvc": "1.2.12"
}
},
"node_modules/source-map-js": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"license": "0BSD"
},
"node_modules/vite": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.2.tgz",
"integrity": "sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==",
"license": "MIT",
"dependencies": {
"lightningcss": "^1.33.0",
"picomatch": "^4.0.7",
"postcss": "^8.5.28",
"rolldown": "~1.2.11",
"tinyglobby": "^0.2.17"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.7.1",
"esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"@vitejs/devtools": {
"optional": true
},
"esbuild": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
}
}
}
+7 -1
View File
@@ -8,9 +8,15 @@
},
"license": "SEE LICENSE IN LICENSE",
"author": "Oscar Giacalone",
"type": "commonjs",
"type": "module",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"pdf-lib": "^1.17.1",
"vite": "^8.3.2"
}
}
+190
View File
@@ -0,0 +1,190 @@
import * as maths from "./maths.js";
import { createPdf } from "./print.js";
let uInput = document.getElementById("u");
let hInput = document.getElementById("h");
let rInput = document.getElementById("r");
let rsInput = document.getElementById("rs");
let npInput = document.getElementById("np");
let outputdiv = document.getElementById("output");
let stepInput = document.getElementById("steps");
let lenOutput = document.getElementById("len");
let paperInput = document.getElementById("p");
let height = 0;
let radius = 0;
let spillRadius = 0;
let numberOfPanels = 0;
let multiplier = 1;
let step = 0.001 * multiplier;
// const offset = 3*10**-15 * multiplier;
const offset = 4 * 10 ** -5 * multiplier;
let pointsP = [];
let pointsQ = [];
let distances = [];
let panelPoints = [];
let panelPaths = [];
let svgSize = { width: 0, height: 0 };
let scaleFactor = 1;
let paperSize = "a3";
let n = 0;
let unit = "px";
let strokeWidth = 1;
// logic
function getValues() {
pointsP = [];
pointsQ = [];
distances = [];
panelPoints = [];
height = /* /* Math.floor */(parseFloat(hInput.value) * multiplier);
radius = /* Math.floor */(parseFloat(rInput.value) * multiplier);
spillRadius = /* Math.floor */(parseFloat(rsInput.value) * multiplier);
numberOfPanels = /* Math.floor */(parseFloat(npInput.value) * multiplier);
step = radius / parseInt(stepInput.value);
paperSize = paperInput.value;
if (isNaN(spillRadius)) {
spillRadius = /* Math.floor */(0.05 * radius);
rsInput.value = 0.05 * radius / multiplier;
}
n = ((radius - spillRadius) / (step - offset)) - 1;
console.log(`${height}, ${radius}, ${spillRadius}, ${step}, ${offset}`);
}
function getPoints() {
for (let x = spillRadius; x < radius; x += step - offset) {
x = maths.approx(x);
pointsP.push({
x: x,
y: maths.f(x, height, radius)
});
}
}
function getPointsOfTangentIntersection() {
for (let i = 0; i < n; i++) {
console.log(`${i}, ${pointsP[i].x} ${pointsP[i].y}, ${pointsP[i + 1].x} ${pointsP[i+1].y}`);
pointsQ.push({
x: maths.j(pointsP[i], pointsP[i + 1], height, radius),
y: maths.g(maths.j(pointsP[i], pointsP[i + 1], height, radius), pointsP[i + 1], height, radius)
});
}
}
function getDistancesBetweenIntersections() {
for (let i = 0; i < n - 1; i++) {
distances.push(maths.d(pointsQ[i], pointsQ[i + 1], height, radius));
}
}
function generateCoordinates() {
let Xi = 0;
for (let i = 0; i < n - 2; i++) {
Xi += distances[i];
let Yi = (Math.PI * pointsQ[i].x) / numberOfPanels;
panelPoints.push({
x: Xi,
y: Yi
});
}
}
function createPerimeterPath(maxY) {
let path = `M `;
// first arc
for (let i = 0; i < panelPoints.length; i++) {
let point = panelPoints[i];
path += `${maths.approx(point.x)},${maths.approx((maxY) - point.y)} `;
}
// second arc
for (let i = panelPoints.length-1; i>=0;i--) {
let point = panelPoints[i];
path +=`${maths.approx(point.x)},${maths.approx((point.y + maxY))} `;
}
path += `${maths.approx(panelPoints[0].x)},${maths.approx((maxY) - panelPoints[0].y)}`;
console.log(path);
return path;
}
function createCentrePath(maxY) {
return `M ${maths.approx(panelPoints[0].x)},${(maxY - maths.approx(panelPoints[0].y) + maths.approx((panelPoints[0].y + maxY))) / 2} ${panelPoints[panelPoints.length - 1].x},${(maxY - maths.approx(panelPoints[panelPoints.length - 1].y) + maths.approx((panelPoints[panelPoints.length - 1].y + maxY))) / 2}`;
}
function plotSVG() {
let maxY = 0;
let minY = 1000;
let maxX = 0;
let minX = 1000;
panelPoints.forEach((point) => {
if (maxY < point.y) {
maxY = point.y;
}
if (minY > point.y) {
minY = point.y;
}
if (minX > point.x) {
minX = point.x;
}
if (maxX < point.x) {
maxX = point.x;
}
});
svgSize.width = panelPoints[panelPoints.length - 1].x - maths.approx(panelPoints[0].x);
svgSize.height = (maxY - maths.approx(panelPoints[0].y) + maths.approx((panelPoints[0].y + maxY)));
panelPaths[0] = createCentrePath(maxY);
panelPaths[1] = createPerimeterPath(maxY);
outputdiv.innerHTML = `
<svg width="297mm"
height="210mm"
xmlns="http://www.w3.org/2000/svg">
<g id="layer1">
<path
d="${panelPaths[0]}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
<path
d="${panelPaths[1]}"
fill="none"
stroke="black"
stroke-width="${strokeWidth}"
/>
</g>
</svg>`;
lenOutput.innerText = `Recommended length: ${maths.approx(maxX - minX)} ${unit}`;
}
function calculateScaleFactor(unit) {
if (unit == "mm") {
scaleFactor = 72/25.4;
} else if (unit == "cm") {
scaleFactor = 72/2.54;
} else if (unit == "in") {
scaleFactor = 72;
} else if (unit == "px") {
scaleFactor = 0.75;
}
}
// main func
function generatePanel() {
unit=uInput.value;
getValues();
getPoints();
getPointsOfTangentIntersection();
getDistancesBetweenIntersections();
generateCoordinates();
plotSVG();
calculateScaleFactor(unit);
createPdf(panelPaths, svgSize, scaleFactor, paperSize);
}
document.getElementById("generate").addEventListener("click", () => generatePanel());
+63
View File
@@ -0,0 +1,63 @@
import { sanitiseArguments } from "./utils";
let accuracy = import.meta.env.VITE_ACCURACY;
export function approx(n) {
return Math.round(n * (10 ** accuracy)) / (10 ** accuracy);
}
// calculate y co-ordinate of point on ellipse at x
// x: x co-ordinate
// h: height of ellipse
// r: radius of spill hole
export function f(x, h, r) {
sanitiseArguments(x,h,r)
let res = approx(h * Math.sqrt((1 - (Math.pow(x, 2) / Math.pow(r, 2)))));
console.log(`f(${x} ${h} ${r}) ${res}`);
return res;
}
// calculate the gradient of the ellipse at a given point x
// x: x co-ordinate of point
// h: height of ellipse
// r: radius of spill hole
export function fprime(x, h, r) {
sanitiseArguments(x,h,r)
let res = approx(-1 * ((Math.pow(h, 2) * x) / (Math.pow(r, 2) * f(x, h, r))));
console.log(`fprime (${x} ${h} ${r}) ${res}`);
return res;
}
// calculate the y co-ordinate of the point on the tangent at P to an ellipse at x
// x: x co-ordinate along tangent
// P: (x,y) for the point on the ellipse the tangent is from
// h: height of ellipse
// r: radius of spill hole
export function g(x, P, h, r) {
sanitiseArguments(x,P.x, P.y,h,r)
let res = approx(fprime(x, h, r) * (x - P.x) + P.y);
console.log(`g (${x} {${P.x} ${P.y}} ${h} ${r}) ${res}`);
return res;
}
// find the intersection between two tangents of an ellipse
// A: (x,y) for the point of the ellipse for the first tangent
// B: (x,y) for the point of the ellipse for the second tangent
// h: height of the ellipse
// r: radius of the spill hole
export function j(A, B, h, r) {
sanitiseArguments(A.x, A.y, B.x, B.y, h,r)
let res = approx(A.x * fprime(A.x, h, r) - B.x * fprime(B.x, h, r) - A.y + B.y) / (fprime(A.x, h, r) - fprime(B.x, h, r));
console.log(`j ({${A.x} ${A.y}} {${B.x} ${B.y}} ${h} ${r}) ${res}`);
return res;
}
// find the distance between two points
// A: (x,y) the first point
// B: (x,y) the second point
export function d(A, B) {
sanitiseArguments(A.x, A.y, B.x, B.y)
let res = approx(Math.sqrt(Math.pow(A.x - B.x, 2) + Math.pow(A.y - B.y, 2)));
console.log(`d ({${A.x} ${A.y}} {${B.x} ${B.y}}) ${res}`);
return res;
}
+90
View File
@@ -0,0 +1,90 @@
import { PDFDocument, degrees, rgb } from "pdf-lib";
export async function createPdf(paths, svgSize, scaleFactor, paperSize) {
const pdfDocument = await PDFDocument.create();
// calculate number of pages needed
const margins = { x: 15, y: 15 };
console.log(svgSize);
console.log(`Scale factor ${scaleFactor}`);
// paper sizes in pt
let sizes = {
"a3": {
x: 841.89-2*margins.x,
y: 1190.55-2*margins.y,
},
"a4": {
x: 595.28-2*margins.x,
y: 841.89-2*margins.y,
}
}
let nPages = Math.ceil((svgSize.width *scaleFactor) / sizes[paperSize].y);
console.log(`${svgSize.width} * ${scaleFactor} / ${sizes[paperSize].y} = ${((svgSize.width *scaleFactor) / sizes[paperSize].y)}`);
let needsMarkers = false;
if (1 < nPages) {
needsMarkers = true;
}
console.log(nPages);
// make enough pages
for (let i = 0; i < nPages; i++) {
let offset = i*(sizes[paperSize].y - 4 * margins.y);
let page = pdfDocument.addPage();
page.setSize(sizes[paperSize].x, sizes[paperSize].y);
const { width, height } = page.getSize();
console.log(page.getSize());
const svgSettings = {
x: (width + svgSize.height * scaleFactor)/2,
y: height + offset - margins.y,
rotate: degrees(-90),
borderColor: rgb(0,0,0),
borderWidth: 1,
scale: scaleFactor
};
// centre line
// page.drawLine({
// start: { x: width/2, y: height },
// end: { x: width/2, y: 0 },
// });
page.moveTo(5, height - 5);
// panel
page.drawSvgPath(paths[0], svgSettings);
page.drawSvgPath(paths[1], svgSettings);
// margins
page.drawSvgPath(`M ${margins.x/2},${margins.y/2} ${margins.x/2},${height-margins.y/2} ${width-margins.x/2},${height-margins.y/2} ${width-margins.x/2},${margins.y/2} ${margins.x/2},${margins.y/2} Z`, {
x:0,
y:height,
borderColor: rgb(1,1,1),
borderWidth: margins.x
});
// draw top alignmnet marker
if (i != 0 && needsMarkers) {
page.drawLine({
start: { x: 0, y: height - 2 * margins.y },
end: { x: width, y: height - 2 * margins.y },
});
}
// draw bottom alignment marker
if (i != (nPages-1) && needsMarkers) {
page.drawLine({
start: { x: 0, y: 2 * margins.y },
end: { x: width, y: 2 * margins.y },
});
}
}
const pdfDataUri = await pdfDocument.saveAsBase64({ dataUri: true });
document.getElementById("pdf").src = pdfDataUri;
}
+7
View File
@@ -0,0 +1,7 @@
export function sanitiseArguments(...args) {
for (let i = 0; i < args.length; i++) {
if (args[i] == undefined || args[i] == NaN) {
throw new Error(`bad argument: ${args}`);
}
}
}
+5
View File
@@ -0,0 +1,5 @@
import { defineConfig } from "vite";
export default defineConfig({
base: ""
});