This commit is contained in:
2026-07-22 21:15:12 +01:00
parent 59a7c1c12b
commit 1aeecbf8ca
7 changed files with 138 additions and 42 deletions

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/Parachute-SVG-Generator.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Parachute-SVG-Generator.iml" filepath="$PROJECT_DIR$/.idea/Parachute-SVG-Generator.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -7,15 +7,25 @@
<h1>Parachute Panel Generator</h1> <h1>Parachute Panel Generator</h1>
<p>Generate the SVG outline of a parachute panel for an elliptical parachute. Find out more <a>here</a>.</p> <p>Generate the SVG outline of a parachute panel for an elliptical parachute. Find out more <a>here</a>.</p>
<br> <br>
<label for="u">Units:</label>
<select id="u">
<option value="px">Pixels</option>
<option value="mm">Millimetres</option>
<option value="cm">Centimetres</option>
<option value="in">Inches</option>
</select> <br>
<label for="h">Height of parachute (h):</label> <label for="h">Height of parachute (h):</label>
<input type="number" id="h" min="0"/><br> <input type="number" id="h" min="0"/><br>
<label for="r">Radius of parachute (r):</label> <label for="r">Radius of parachute (r):</label>
<input type="number" id="r" min="0"/><br> <input type="number" id="r" min="0"/><br>
<label for="rs">Radius of spill hole (r<sub>s</sub>): (leave blank to use 0.05r)</label> <label for="rs">Radius of spill hole (r<sub>s</sub>): (leave blank to use 5% of the radius)</label>
<input type="number" id="rs" /><br> <input type="number" id="rs" /><br>
<label for="np">Number of panels (n<sub>p</sub>): </label> <label for="np">Number of panels (n<sub>p</sub>): </label>
<input type="number" id="np" min="3" ><br> <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 onclick="generatePanel()">Generate!</button>
<p id="len"></p>
<div id="output"></div> <div id="output"></div>
<script src="index.js" lang="js"></script> <script src="index.js" lang="js"></script>
</body> </body>

132
index.js
View File

@@ -1,8 +1,11 @@
let uInput = document.getElementById("u");
let hInput = document.getElementById("h"); let hInput = document.getElementById("h");
let rInput = document.getElementById("r"); let rInput = document.getElementById("r");
let rsInput = document.getElementById("rs"); let rsInput = document.getElementById("rs");
let npInput = document.getElementById("np"); let npInput = document.getElementById("np");
let outputdiv = document.getElementById("output"); let outputdiv = document.getElementById("output");
let stepInput = document.getElementById("steps");
let lenOutput = document.getElementById("len");
let height = 0; let height = 0;
let radius = 0; let radius = 0;
@@ -12,9 +15,9 @@ let numberOfPanels = 0;
let multiplier = 1; let multiplier = 1;
let accuracy = 8; let accuracy = 8;
const step = 0.001 * multiplier; let step = 0.001 * multiplier;
// const offset = 3*10**-15 * multiplier; // const offset = 3*10**-15 * multiplier;
const offset = 4*10**-5 * multiplier; const offset = 4 * 10 ** -5 * multiplier;
let pointsP = []; let pointsP = [];
let pointsQ = []; let pointsQ = [];
@@ -23,38 +26,40 @@ let distances = [];
let panelPoints = []; let panelPoints = [];
let n = 0; let n = 0;
let unit = "px";
let strokeWidth = 1;
// maths // maths
function approx(x) { function approx(n) {
return Math.round(x*(10**accuracy))/(10**accuracy); return Math.round(n * (10 ** accuracy)) / (10 ** accuracy);
} }
function f(x) { function f(x) {
res = approx(height * Math.sqrt((1-(Math.pow(x,2)/Math.pow(radius,2))))); res = approx(height * Math.sqrt((1 - (Math.pow(x, 2) / Math.pow(radius, 2)))));
console.log(`f ${x} ${res}`); console.log(`f ${x} ${res}`);
return res; return res;
} }
function fprime(x) { function fprime(x) {
res = approx(-1*((Math.pow(height,2)*x)/(Math.pow(radius,2)*f(x)))); res = approx(-1 * ((Math.pow(height, 2) * x) / (Math.pow(radius, 2) * f(x))));
console.log(`fprime ${x} ${res}`); console.log(`fprime ${x} ${res}`);
return res; return res;
} }
function g(x, P) { function g(x, P) {
res = approx(fprime(x)*(x-P.x)+P.y); res = approx(fprime(x) * (x - P.x) + P.y);
console.log(`g ${x} ${P.x} ${P.y} ${res}`); console.log(`g ${x} ${P.x} ${P.y} ${res}`);
return res; return res;
} }
function j(A, B) { 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)); 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}`); console.log(`j ${A.x} ${A.y} ${B.x} ${B.y} ${res}`);
return res; return res;
} }
function d(A, B) { function d(A, B) {
res = approx(Math.sqrt(Math.pow(A.x-B.x,2)+Math.pow(A.y-B.y,2))); 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}`); console.log(`d ${A.x} ${A.y} ${B.x} ${B.y} ${res}`);
return res; return res;
} }
@@ -65,21 +70,22 @@ function getValues() {
pointsQ = []; pointsQ = [];
distances = []; distances = [];
panelPoints = []; panelPoints = [];
height = /* /* Math.floor */(parseFloat(hInput.value) *multiplier); height = /* /* Math.floor */(parseFloat(hInput.value) * multiplier);
radius = /* Math.floor */(parseFloat(rInput.value) *multiplier); radius = /* Math.floor */(parseFloat(rInput.value) * multiplier);
spillRadius = /* Math.floor */(parseFloat(rsInput.value) *multiplier); spillRadius = /* Math.floor */(parseFloat(rsInput.value) * multiplier);
numberOfPanels = /* Math.floor */(parseFloat(npInput.value) *multiplier); numberOfPanels = /* Math.floor */(parseFloat(npInput.value) * multiplier);
step = radius / parseInt(stepInput.value);
if (isNaN(spillRadius)) { if (isNaN(spillRadius)) {
spillRadius = /* Math.floor */(0.05*radius); spillRadius = /* Math.floor */(0.05 * radius);
rsInput.value = 0.05*radius /multiplier; rsInput.value = 0.05 * radius / multiplier;
} }
n = ((radius-spillRadius)/(step-offset))-1; n = ((radius - spillRadius) / (step - offset)) - 1;
console.log(`${height}, ${radius}, ${spillRadius}, ${step}, ${offset}`); console.log(`${height}, ${radius}, ${spillRadius}, ${step}, ${offset}`);
} }
function getPoints() { function getPoints() {
for (let x = spillRadius; x < radius; x+=step-offset) { for (let x = spillRadius; x < radius; x += step - offset) {
x=approx(x); x = approx(x);
pointsP.push({ pointsP.push({
x: x, x: x,
y: f(x) y: f(x)
@@ -89,25 +95,25 @@ function getPoints() {
function getPointsOfTangentIntersection() { function getPointsOfTangentIntersection() {
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
console.log(`${i}, ${pointsP[i]}, ${pointsP[i+1]}`); console.log(`${i}, ${pointsP[i]}, ${pointsP[i + 1]}`);
pointsQ.push({ pointsQ.push({
x: j(pointsP[i], pointsP[i+1]), x: j(pointsP[i], pointsP[i + 1]),
y: g(j(pointsP[i], pointsP[i+1]), pointsP[i+1]) y: g(j(pointsP[i], pointsP[i + 1]), pointsP[i + 1])
}); });
} }
} }
function getDistancesBetweenIntersections() { function getDistancesBetweenIntersections() {
for (let i = 0; i < n-1; i++) { for (let i = 0; i < n - 1; i++) {
distances.push(d(pointsQ[i], pointsQ[i+1])); distances.push(d(pointsQ[i], pointsQ[i + 1]));
} }
} }
function generateCoordinates() { function generateCoordinates() {
let Xi = 0; let Xi = 0;
for (let i = 0; i < n-2; i++) { for (let i = 0; i < n - 2; i++) {
Xi+=distances[i]; Xi += distances[i];
Yi=(Math.PI*pointsQ[i].x)/numberOfPanels; Yi = (Math.PI * pointsQ[i].x) / numberOfPanels;
panelPoints.push({ panelPoints.push({
x: Xi, x: Xi,
y: Yi y: Yi
@@ -117,32 +123,80 @@ function generateCoordinates() {
function plotSVG() { function plotSVG() {
let maxY = 0; let maxY = 0;
let maxX = 0;
let minX = 1000;
panelPoints.forEach((point) => { panelPoints.forEach((point) => {
if (maxY < point.y) { if (maxY < point.y) {
maxY=point.y maxY = point.y;
}
if (minX > point.x) {
minX = point.x;
}
if (maxX < point.x) {
maxX = point.x;
} }
}) })
let pointsA = []; let pointsA = "";
let pointsB = []; let pointsB = "";
for(let i = 0; i < panelPoints.length; i++) { for (let i = 0; i < panelPoints.length; i++) {
let point = panelPoints[i]; let point = panelPoints[i];
pointsA.push(`${approx(point.x*10)},${(maxY*10)-approx(point.y*10)}`) pointsA += `${approx(point.x)},${(maxY) - approx(point.y)} `;
pointsB.push(`${approx(point.x*10)},${approx((point.y+maxY)*10)}`); pointsB +=`${approx(point.x)},${approx((point.y + maxY))} `;
} }
outputdiv.innerHTML = ` outputdiv.innerHTML = `
<svg viewBox="0 0 100 200" xmlns="http://www.w3.org/2000/svg"> <svg width="297mm"
<line x1="${approx(panelPoints[0].x*10)}" x2="${panelPoints[0].x*10}" y1="${maxY*10-approx(panelPoints[0].y*10)}" y2="${approx((panelPoints[0].y+maxY)*10)}" fill="none" stroke="black" stroke-width="0.05" /> height="210mm"
<line x1="${approx(panelPoints[panelPoints.length-1].x*10)}" x2="${panelPoints[panelPoints.length-1].x*10}" y1="${maxY*10-approx(panelPoints[panelPoints.length-1].y*10)}" y2="${approx((panelPoints[panelPoints.length-1].y+maxY)*10)}" fill="none" stroke="black" stroke-width="0.05" /> xmlns="http://www.w3.org/2000/svg">
<line x1="${approx(panelPoints[0].x*10)}" x2="${panelPoints[panelPoints.length-1].x*10}" y1="${(maxY*10-approx(panelPoints[0].y*10)+approx((panelPoints[0].y+maxY)*10))/2}" y2="${(maxY*10-approx(panelPoints[panelPoints.length-1].y*10)+approx((panelPoints[panelPoints.length-1].y+maxY)*10))/2}" fill="none" stroke="black" stroke-width="0.05" /> <g id="layer1">
<polyline points="${pointsA}" fill="none" stroke="black" stroke-width="0.05" /> <line
<polyline points="${pointsB}" fill="none" stroke="black" stroke-width="0.05" /> 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> </svg>
` `;
lenOutput.innerText = `Recommended length: ${approx(maxX - minX)} ${unit}`;
} }
// main func // main func
function generatePanel() { function generatePanel() {
unit=uInput.value;
getValues(); getValues();
getPoints(); getPoints();
getPointsOfTangentIntersection(); getPointsOfTangentIntersection();

View File

@@ -7,7 +7,7 @@
"url": "git@git.emaker.limited:chopster44/Parachute-SVG-Generator.git" "url": "git@git.emaker.limited:chopster44/Parachute-SVG-Generator.git"
}, },
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",
"author": "chopster44", "author": "Oscar Giacalone",
"type": "commonjs", "type": "commonjs",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {