diff --git a/source/_extensions/controls_js_sim/base/base-visualization.js b/source/_extensions/controls_js_sim/base/base-visualization.js index 8d82b43d1a..ae0548a481 100644 --- a/source/_extensions/controls_js_sim/base/base-visualization.js +++ b/source/_extensions/controls_js_sim/base/base-visualization.js @@ -27,6 +27,9 @@ class BaseVisualization { window.addEventListener("resize", this.updateSize.bind(this)); window.addEventListener("load", this.updateSize.bind(this)); + + this.position = 0; + this.positionPrev = 0; } updateSize() { diff --git a/source/_extensions/controls_js_sim/vertical-elevator-pidf.js b/source/_extensions/controls_js_sim/vertical-elevator-pidf.js index 9c203ee8d1..b2b2843102 100644 --- a/source/_extensions/controls_js_sim/vertical-elevator-pidf.js +++ b/source/_extensions/controls_js_sim/vertical-elevator-pidf.js @@ -45,6 +45,8 @@ class VerticalElevatorPIDF extends VerticalElevatorSim { input = document.createElement("INPUT"); input.setAttribute("type", "text"); input.setAttribute("value", "0.0"); + input.setAttribute("min", "0.0"); + input.setAttribute("max", "1.0"); input.setAttribute("id", divIdPrefix + "_setpoint"); input.onchange = function (event) { this.setSetpointM(parseFloat(event.target.value)); diff --git a/source/_extensions/controls_js_sim/visualization/vertical-elevator-visualization.js b/source/_extensions/controls_js_sim/visualization/vertical-elevator-visualization.js index 71de6b3c3a..093562173d 100644 --- a/source/_extensions/controls_js_sim/visualization/vertical-elevator-visualization.js +++ b/source/_extensions/controls_js_sim/visualization/vertical-elevator-visualization.js @@ -14,6 +14,52 @@ class VerticalElevatorVisualization extends BaseVisualization { this.elevBottom = this.height * 0.9; this.elevTop = this.height * 0.1; this.elevMaxHeightM = elevHeightM; + + this.bonks = []; // Array to store pops + this.bonkLifetime = 35; // Duration before pop fully fades (adjust as needed) + } + + // Method to draw a jagged polygon (comic-book punch style) around the text + drawJaggedPolygon(x, y, radius, numVertices) { + var fixedRandList = [0.234,0,0.523,0,0.692,0,0.442,0,0.2643,0,0.5962,0,0.343,0,0.7954,0]; + const context = this.animatedCanvasContext; + context.fillStyle = "#CCCC00"; // Color for the jagged polygon (e.g., bright yellow) + context.strokeStyle = "#000000"; + context.lineWidth = 2; + context.beginPath(); + + for (let i = 0; i < numVertices; i++) { + // Randomness to create jagged effect + const angle = (i / numVertices) * Math.PI * 2; + const randomOffset = fixedRandList[i%fixedRandList.length]*30; // Semi-jaggedness + const xOffset = Math.cos(angle) * (radius + randomOffset); + const yOffset = Math.sin(angle) * (radius + randomOffset); + + if (i === 0) { + context.moveTo(x + xOffset, y + yOffset); // Start the polygon path + } else { + context.lineTo(x + xOffset, y + yOffset); // Add vertices to the path + } + } + + context.closePath(); + context.stroke() + context.fill() + } + + // Method to add a "pop" at a random location + addBonk() { + const x = Math.random() * (this.width * 0.2) + this.width * 0.4; // Random x within constraints + const y = Math.random() * (this.height * 0.3) + this.height * 0.1; // Random y within constraints + this.bonks.push({ x, y, transparency: 1.0 }); // Start fully opaque + } + + // Method to update the transparency of pops and remove if too transparent + updateBonks() { + this.bonks = this.bonks.filter((pop) => { + pop.transparency -= 1 / this.bonkLifetime; // Decrease transparency over time + return pop.transparency > 0; // Keep pop if still visible + }); } getCursorPosition(event) { @@ -133,7 +179,6 @@ class VerticalElevatorVisualization extends BaseVisualization { const setpointDraw = this.posToCanvas(this.setpoint); const positionDraw = this.posToCanvas(this.position); - // Elevator const elevDrawWidth = this.width * 0.2; const elevDrawHeight = this.height * 0.1; @@ -166,6 +211,30 @@ class VerticalElevatorVisualization extends BaseVisualization { this.animatedCanvasContext.lineTo(this.width * 0.7, setpointDraw); this.animatedCanvasContext.stroke(); + //Bonk + if(this.position >= 0.98 && this.positionPrev < 0.98){ + this.addBonk() + } + this.updateBonks() + + this.bonks.forEach((bonk) => { + this.animatedCanvasContext.globalAlpha = bonk.transparency; // Set transparency + + // Draw jagged "cutout" shape behind the text + this.drawJaggedPolygon(bonk.x, bonk.y, 20, 17); // Radius and vertices for jagged shape + + // Set text style and draw "Bonk!" + this.animatedCanvasContext.fillStyle = "#FF0000"; + this.animatedCanvasContext.font = "bold 16px Comic Sans MS"; // Halved font size + this.animatedCanvasContext.fillText("Bonk!", bonk.x - 20, bonk.y + 5); // Adjust text position to center it + + this.animatedCanvasContext.globalAlpha = 1.0; // Reset globalAlpha after drawing + }); + // Reset globalAlpha to default + this.animatedCanvasContext.globalAlpha = 1.0; + + this.positionPrev = this.position + } diff --git a/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant-plus-profiler.drawio.svg b/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant-plus-profiler.drawio.svg index dc581d40e2..7c05cd5f05 100644 --- a/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant-plus-profiler.drawio.svg +++ b/source/docs/software/advanced-controls/introduction/images/control-system-basics-ctrl-plus-plant-plus-profiler.drawio.svg @@ -1,4 +1,4 @@ -
e(t)
%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%26lt%3Bfont%20style%3D%26quot%3Bfont-size%3A%2017px%3B%26quot%3B%26gt%3Bp(t)%26lt%3B%2Ffont%26gt%3B%22%20style%3D%22edgeLabel%3Bhtml%3D1%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3Bresizable%3D0%3Bpoints%3D%5B%5D%3BfontSize%3D17%3BfontFamily%3DTimes%20New%20Roman%3BfontStyle%3D1%22%20vertex%3D%221%22%20connectable%3D%220%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22300.41379310344814%22%20y%3D%22430.0000000000002%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E
p'(t)
p''(t)
+
p(t)
Profiler
+
Feedforward controller
+
Feedback controller
r(t)
-
y(t)
Plant
u(t)
\ No newline at end of file +
e(t)
e(t)
%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%26lt%3Bfont%20style%3D%26quot%3Bfont-size%3A%2017px%3B%26quot%3B%26gt%3Bp(t)%26lt%3B%2Ffont%26gt%3B%22%20style%3D%22edgeLabel%3Bhtml%3D1%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3Bresizable%3D0%3Bpoints%3D%5B%5D%3BfontSize%3D17%3BfontFamily%3DTimes%20New%20Roman%3BfontStyle%3D1%22%20vertex%3D%221%22%20connectable%3D%220%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22300.41379310344814%22%20y%3D%22430.0000000000002%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E
%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%26lt%3Bfont%20style%3D%26quot%3Bfont-size%3A%2017px%3B%26quot%3B%26gt%3Bp(t)%26lt%3B%2Ffont%26gt%3B%22%20style%3D%22edgeLabel%3Bhtml%3D1%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3Bresizable%3D0%3Bpoints%3D%5B%5D%3BfontSize%3D17%3BfontFamily%3DTimes%20New%20Roman%3BfontStyle%3D1%22%20vertex%3D%221%22%20connectable%3D%220%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22300.41379310344814%22%20y%3D%22430.0000000000002%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E
r'(t)
r''(t)
r'(t)...
+
+
r(t)
r(t)
Profiler
Profiler
+
+
Feedforward controller
Feedforward...
+
+
Feedback controller
Feedback...
rf(t)
rf(t)
-
-
y(t)
y(t)
Plant
Plant
u(t)
u(t)
Text is not SVG - cannot display
\ No newline at end of file diff --git a/source/docs/software/advanced-controls/introduction/tuning-elevator.rst b/source/docs/software/advanced-controls/introduction/tuning-elevator.rst index 497744e9d5..6348187b9a 100644 --- a/source/docs/software/advanced-controls/introduction/tuning-elevator.rst +++ b/source/docs/software/advanced-controls/introduction/tuning-elevator.rst @@ -33,10 +33,10 @@ Our "vertical elevator" consists of: Where: * The plant's :term:`output` :math:`y(t)` is the elevator's height -* The controller's :term:`setpoint` :math:`r(t)` is the unprofiled desired height of the elevator -* The Motion Profiler's :term:`setpoint` :math:`p(t)` is the profiled desired position of the elevator -* The Motion Profiler's :term:`setpoint` :math:`p'(t)` is the profiled desired velocity of the elevator -* The Motion Profiler's :term:`setpoint` :math:`p''(t)` is the profiled desired accelerator of the elevator +* The controller's :term:`setpoint` :math:`r_f(t)` is the unprofiled, **final** desired height of the elevator +* The Motion Profiler's position :term:`setpoint` :math:`r(t)` is where the elevator should currently be positioned +* The Motion Profiler's velocity :term:`setpoint` :math:`r'(t)` is how fast the elevator should currently be moving +* The Motion Profiler's accelerator :term:`setpoint` :math:`r''(t)` is how fast the elevator should currently be accelerating * The controller's :term:`control effort`, :math:`u(t)` is the voltage applied to the motor driving the elevator