mjboothaus's picture
Update static Marimo site
b82aa59 verified
raw
history blame
25.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="./favicon.ico" />
<!-- Preload is necessary because we show these images when we disconnect from the server,
but at that point we cannot load these images from the server -->
<link rel="preload" href="./assets/gradient-yHQUC_QB.png" as="image" />
<link rel="preload" href="./assets/noise-60BoTA8O.png" as="image" />
<!-- Preload the fonts -->
<link rel="preload" href="./assets/Lora-VariableFont_wght-B2ootaw-.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/PTSans-Regular-CxL0S8W7.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/PTSans-Bold-D9fedIX3.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/FiraMono-Regular-BTCkDNvf.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/FiraMono-Medium-DU3aDxX5.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/FiraMono-Bold-CLVRCuM9.ttf" as="font" crossorigin="anonymous" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="a marimo app" />
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
<link rel="manifest" href="./manifest.json" />
<script data-marimo="true">
function __resizeIframe(obj) {
var scrollbarHeight = 20; // Max between windows, mac, and linux
function setHeight() {
var element = obj.contentWindow.document.documentElement;
// If there is no vertical scrollbar, we don't need to resize the iframe
if (element.scrollHeight === element.clientHeight) {
return;
}
// Create a new height that includes the scrollbar height if it's visible
var hasHorizontalScrollbar = element.scrollWidth > element.clientWidth;
var newHeight = element.scrollHeight + (hasHorizontalScrollbar ? scrollbarHeight : 0);
// Only update the height if it's different from the current height
if (obj.style.height !== `${newHeight}px`) {
obj.style.height = `${newHeight}px`;
}
}
// Resize the iframe to the height of the content and bottom scrollbar height
setHeight();
// Resize the iframe when the content changes
const resizeObserver = new ResizeObserver((entries) => {
setHeight();
});
resizeObserver.observe(obj.contentWindow.document.body);
}
</script>
<marimo-filename hidden>notebook.py</marimo-filename>
<!-- TODO(Trevor): Legacy, required by VS Code plugin. Remove when plugin is updated (see marimo/server/_templates/template.py) -->
<marimo-version data-version="{{ version }}" hidden></marimo-version>
<marimo-user-config data-config="{{ user_config }}" hidden></marimo-user-config>
<marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
<!-- /TODO -->
<title>caternary</title>
<script type="module" crossorigin src="./assets/index-DgI7bmFZ.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-B-hr6ABS.css">
<marimo-wasm hidden=""></marimo-wasm>
<script>
if (window.location.protocol === 'file:') {
alert('Warning: This file must be served by an HTTP server to function correctly.');
}
</script>
<style>
#save-button {
display: none !important;
}
#filename-input {
display: none !important;
}
</style>
<marimo-code hidden="">import%20marimo%0A%0A__generated_with%20%3D%20%220.14.9%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20From%20Chaos%20to%20Clarity%3A%20Unlocking%20Business%20Value%20from%20Poor%20or%20Legacy%20Code%0A%0A%20%20%20%20See%20the%20article%20-%20https%3A%2F%2Fwww.databooth.com.au%2Fposts%2Fcaternary%2F%0A%0A%20%20%20%20%23%23%23%23%20%5BDataBooth%5D(https%3A%2F%2Fwww.databooth.com.au)%20-%20*Grow%20with%20Your%20Data*%0A%0A%20%20%20%20---%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20math%0A%20%20%20%20from%20scipy.optimize%20import%20minimize_scalar%0A%0A%20%20%20%20return%20(math%2C)%0A%0A%0A%40app.cell%0Adef%20_(math)%3A%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%0A%20%20%20%20class%20Catenary%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Represents%20a%20catenary%20curve%20defined%20by%20its%20endpoints%20and%20diameter.%0A%20%20%20%20%20%20%20%20Provides%20methods%20for%20fitting%2C%20geometric%20calculations%2C%20and%20plotting.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%0A%20%20%20%20%20%20%20%20INF%3A%20float%20%3D%201e12%0A%20%20%20%20%20%20%20%20DEFAULT_A%3A%20float%20%3D%201.0%0A%20%20%20%20%20%20%20%20DEFAULT_B%3A%20float%20%3D%201.0%0A%20%20%20%20%20%20%20%20DEFAULT_STEP%3A%20float%20%3D%200.1%0A%20%20%20%20%20%20%20%20STEP_REDUCTION_FACTOR%3A%20float%20%3D%2010.0%0A%20%20%20%20%20%20%20%20DEFAULT_PRECISION%3A%20float%20%3D%201e-7%0A%0A%20%20%20%20%20%20%20%20def%20__init__(self%2C%20diameter%3A%20float%2C%20span%3A%20float)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Initialise%20the%20Catenary%20with%20the%20specified%20diameter%20and%20span.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20Args%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20diameter%3A%20The%20vertical%20distance%20(diameter)%20at%20endpoints.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span%3A%20The%20horizontal%20distance%20between%20endpoints.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20self.diameter%20%3D%20float(diameter)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.span%20%3D%20float(span)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.a%3A%20float%20%3D%20self.DEFAULT_A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.b%3A%20float%20%3D%20self.DEFAULT_B%0A%0A%20%20%20%20%20%20%20%20def%20_boundary_error(self%2C%20a%3A%20float%2C%20b%3A%20float)%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Compute%20the%20sum%20of%20absolute%20errors%20at%20the%20endpoints%20for%20given%20a%20and%20b.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y1%20%3D%20self.diameter%20%2F%202%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y2%20%3D%20self.diameter%20%2F%202%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e1%20%3D%20a%20*%20math.cosh((0%20-%20b)%20%2F%20a)%20-%20y1%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e2%20%3D%20a%20*%20math.cosh((self.span%20-%20b)%20%2F%20a)%20-%20y2%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20abs(e1)%20%2B%20abs(e2)%0A%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20self.INF%0A%0A%20%20%20%20%20%20%20%20def%20fit_parameters(self%2C%20precision%3A%20float%20%3D%20None)%20-%3E%20tuple%5Bfloat%2C%20float%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Find%20the%20optimal%20catenary%20parameters%20a%20and%20b%20to%20fit%20the%20endpoints.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20Args%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20precision%3A%20Desired%20precision%20for%20parameter%20fitting.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20Returns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Tuple%20of%20optimised%20(a%2C%20b).%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20precision%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20precision%20%3D%20self.DEFAULT_PRECISION%0A%20%20%20%20%20%20%20%20%20%20%20%20step%20%3D%20self.DEFAULT_STEP%0A%20%20%20%20%20%20%20%20%20%20%20%20error%20%3D%20self.INF%0A%20%20%20%20%20%20%20%20%20%20%20%20a%2C%20b%20%3D%20self.DEFAULT_A%2C%20self.DEFAULT_B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20while%20error%20%3E%20precision%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20improved%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_error%20%3D%20error%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_a%2C%20best_b%20%3D%20a%2C%20b%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20candidate_a%20in%20%5Ba%20-%20step%2C%20a%2C%20a%20%2B%20step%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20candidate_b%20in%20%5Bb%20-%20step%2C%20b%2C%20b%20%2B%20step%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20candidate_error%20%3D%20self._boundary_error(candidate_a%2C%20candidate_b)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20candidate_error%20%3C%20best_error%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_error%20%3D%20candidate_error%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_a%2C%20best_b%20%3D%20candidate_a%2C%20candidate_b%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20improved%20%3D%20True%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20improved%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20step%20%2F%3D%20self.STEP_REDUCTION_FACTOR%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20step%20%3C%20precision%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a%2C%20b%20%3D%20best_a%2C%20best_b%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20error%20%3D%20best_error%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.a%2C%20self.b%20%3D%20a%2C%20b%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20a%2C%20b%0A%0A%20%20%20%20%20%20%20%20def%20y(self%2C%20x%3A%20float)%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Return%20the%20y-coordinate%20of%20the%20catenary%20at%20position%20x.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20self.a%20*%20math.cosh((x%20-%20self.b)%20%2F%20self.a)%0A%0A%20%20%20%20%20%20%20%20def%20area_under_curve(self)%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Calculate%20the%20area%20under%20the%20catenary%20curve%20between%20endpoints.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20a%20%3D%20self.a%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20math.pi%20*%20(a**2)%20*%20(math.sinh(self.span%20%2F%20a)%20%2B%20(self.span%20%2F%20a))%0A%0A%20%20%20%20%20%20%20%20def%20midpoint_radius(self)%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Compute%20the%20vertical%20position%20(radius)%20of%20the%20catenary%20at%20the%20midpoint.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20a%2C%20b%20%3D%20self.a%2C%20self.b%0A%20%20%20%20%20%20%20%20%20%20%20%20midpoint_x%20%3D%20self.span%20%2F%202%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20a%20*%20math.cosh((midpoint_x%20-%20b)%20%2F%20a)%0A%0A%20%20%20%20%20%20%20%20def%20midpoint_dip(self)%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Calculate%20the%20sag%20(dip)%20at%20the%20midpoint%20compared%20to%20endpoints.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(self.diameter%20%2F%202)%20-%20self.midpoint_radius()%0A%0A%20%20%20%20%20%20%20%20def%20midpoint_gap(self)%20-%3E%20float%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Compute%20twice%20the%20midpoint%20radius%20(vertical%20gap%20at%20centre).%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20return%202%20*%20self.midpoint_radius()%0A%0A%20%20%20%20%20%20%20%20def%20summary(self)%20-%3E%20str%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Return%20a%20summary%20of%20the%20catenary's%20geometric%20properties%20as%20a%20string.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Catenary%20for%20diameter%20%7Bself.diameter%7D%20and%20span%20%7Bself.span%7D%3A%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%20%20Parameters%20(a%2C%20b)%3A%20(%7Bself.a%3A.7f%7D%2C%20%7Bself.b%3A.7f%7D)%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%20%20Area%20under%20curve%3A%20%7Bself.area_under_curve()%3A.7f%7D%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%20%20Midpoint%20dip%3A%20%7Bself.midpoint_dip()%3A.7f%7D%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%20%20Midpoint%20gap%3A%20%7Bself.midpoint_gap()%3A.7f%7D%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20def%20describe(self)%20-%3E%20str%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Return%20a%20Markdown-formatted%20summary%20of%20the%20catenary's%20parameters%20and%20geometric%20properties.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**Catenary%20parameters%3A**%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20a%20%3D%20%60%7Bself.a%3A.6f%7D%60%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20b%20%3D%20%60%7Bself.b%3A.6f%7D%60%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**Geometric%20properties%3A**%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20Area%20under%20curve%3A%20%60%7Bself.area_under_curve()%3A.6f%7D%20m%C2%B2%60%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20Midpoint%20dip%3A%20%60%7Bself.midpoint_dip()%3A.6f%7D%20m%60%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20Midpoint%20gap%3A%20%60%7Bself.midpoint_gap()%3A.6f%7D%20m%60%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20def%20plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20self%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20num_points%3A%20int%20%3D%20200%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20x_range%3A%20tuple%5Bfloat%2C%20float%5D%20%3D%20None%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y_range%3A%20tuple%5Bfloat%2C%20float%5D%20%3D%20(0%2C%201.5)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20int%20%3D%20700%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20show_endpoints%3A%20bool%20%3D%20True%2C%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Plot%20the%20catenary%20curve%20using%20Plotly.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20Args%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20num_points%3A%20Number%20of%20points%20to%20plot%20along%20the%20curve.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x_range%3A%20Tuple%20(min_x%2C%20max_x)%20for%20the%20x-axis.%20If%20None%2C%20uses%20sensible%20defaults.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y_range%3A%20Tuple%20(min_y%2C%20max_y)%20for%20the%20y-axis.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20Height%20of%20the%20plot%20in%20pixels.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20show_endpoints%3A%20Whether%20to%20show%20the%20endpoints%20as%20red%20markers.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20Returns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Plotly%20Figure%20object%2C%20or%20None%20if%20Plotly%20is%20not%20installed.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20go%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20ImportError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Plotly%20is%20required%20for%20plotting.%20Please%20install%20plotly.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20x_range%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x_max%20%3D%20math.ceil(self.span%20*%2010)%20%2F%2010%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x_min%20%3D%20-(self.span%20-%201)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x_range%20%3D%20(x_min%2C%20x_max)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20x_vals%20%3D%20%5Bi%20*%20self.span%20%2F%20(num_points%20-%201)%20for%20i%20in%20range(num_points)%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20y_vals%20%3D%20%5Bself.y(x)%20for%20x%20in%20x_vals%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20fig%20%3D%20go.Figure()%0A%20%20%20%20%20%20%20%20%20%20%20%20fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20go.Scatter(x%3Dx_vals%2C%20y%3Dy_vals%2C%20mode%3D%22lines%22%2C%20name%3D%22Catenary%20Curve%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22Catenary%20Curve%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xaxis_title%3D%22Horizontal%20Distance%20(m)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20yaxis_title%3D%22Vertical%20Position%20(m)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20height%3Dheight%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xaxis%3Ddict(range%3Dlist(x_range))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20yaxis%3Ddict(range%3Dlist(y_range))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20show_endpoints%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3D%5B0%2C%20self.span%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3D%5Bself.diameter%20%2F%202%2C%20self.diameter%20%2F%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20marker%3Ddict(size%3D10%2C%20color%3D%22red%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Endpoints%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20fig%0A%0A%20%20%20%20return%20(Catenary%2C)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20%23%20---%20Constants%20for%20constraints%20---%0A%0A%20%20%20%20MAX_SPAN_RATIO%20%3D%200.6627%20%20%23%20Maximum%20allowed%20span%20as%20a%20fraction%20of%20diameter%20for%20a%20valid%20catenary%20solution%20(approximate%20value)%0A%20%20%20%20MIN_DIAMETER%20%3D%200.5%20%20%23%20Minimum%20allowed%20diameter%20value%20for%20the%20UI%20slider%20(meters)%0A%20%20%20%20MAX_DIAMETER%20%3D%202.0%20%20%23%20Maximum%20allowed%20diameter%20value%20for%20the%20UI%20slider%20(meters)%0A%20%20%20%20MIN_SPAN%20%3D%200.1%20%20%23%20Minimum%20allowed%20span%20value%20for%20the%20UI%20slider%20(meters)%0A%20%20%20%20MAX_SPAN%20%3D%20MAX_SPAN_RATIO%20*%20MAX_DIAMETER%0A%20%20%20%20return%20MAX_DIAMETER%2C%20MAX_SPAN_RATIO%2C%20MIN_DIAMETER%2C%20MIN_SPAN%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Interactive%20Catenary%20Curve%20Explorer%0A%0A%20%20%20%20This%20notebook%20demonstrates%20the%20properties%20of%20a%20**catenary**%20curve%2C%20also%20known%20as%20a%20%22chainette%22%20or%20%22alysoid.%22%20%20%0A%20%20%20%20A%20catenary%20is%20the%20shape%20assumed%20by%20a%20flexible%2C%20uniform%20chain%20or%20cable%20suspended%20by%20its%20ends%20and%20acted%20on%20only%20by%20gravity.%0A%0A%20%20%20%20**References%3A**%0A%0A%20%20%20%20-%20%5BWikipedia%3A%20Catenary%5D(https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FCatenary)%0A%20%20%20%20-%20%5BMathWorld%3A%20Catenary%5D(https%3A%2F%2Fmathworld.wolfram.com%2FCatenary.html)%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MAX_DIAMETER%2C%20MIN_DIAMETER%2C%20mo)%3A%0A%20%20%20%20%23%20---%20UI%20controls%20---%0A%0A%20%20%20%20diameter%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3DMIN_DIAMETER%2C%0A%20%20%20%20%20%20%20%20stop%3DMAX_DIAMETER%2C%0A%20%20%20%20%20%20%20%20value%3D1.0%2C%0A%20%20%20%20%20%20%20%20step%3D0.01%2C%0A%20%20%20%20%20%20%20%20label%3D%22Diameter%20(m)%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(diameter%2C)%0A%0A%0A%40app.cell%0Adef%20_(MAX_SPAN_RATIO%2C%20diameter)%3A%0A%20%20%20%20%23%20Compute%20the%20maximum%20allowable%20span%20for%20the%20current%20diameter%0A%0A%20%20%20%20max_span%20%3D%20round(MAX_SPAN_RATIO%20*%20diameter.value%2C%204)%0A%20%20%20%20return%20(max_span%2C)%0A%0A%0A%40app.cell%0Adef%20_(MIN_SPAN%2C%20max_span%2C%20mo)%3A%0A%20%20%20%20span%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3DMIN_SPAN%2C%0A%20%20%20%20%20%20%20%20stop%3Dmax_span%2C%0A%20%20%20%20%20%20%20%20value%3Dmin(0.6%2C%20max_span)%2C%0A%20%20%20%20%20%20%20%20step%3D0.01%2C%0A%20%20%20%20%20%20%20%20label%3D%22Span%20(m)%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(span%2C)%0A%0A%0A%40app.cell%0Adef%20_(MAX_SPAN_RATIO%2C%20diameter%2C%20max_span%2C%20mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%23%23%23%20Parameter%20Constraints%0A%0A%20%20%20%20-%20The%20**span**%20(horizontal%20distance%20between%20endpoints)%20must%20not%20exceed%20**%7BMAX_SPAN_RATIO%3A.2f%7D%20%C3%97%20diameter**%20(vertical%20distance%20between%20endpoints).%0A%20%20%20%20-%20For%20the%20current%20diameter%20(**%7Bdiameter.value%3A.2f%7D%20m**)%2C%20the%20maximum%20allowed%20span%20is%20**%7Bmax_span%3A.4f%7D%20m**.%0A%20%20%20%20-%20This%20limit%20is%20based%20on%20practical%20engineering%20guidelines%20for%20safe%20and%20realistic%20catenary%20curves%2C%20not%20a%20strict%20mathematical%20maximum.%0A%20%20%20%20-%20Horizontal%20distance%20between%20endpoints.%20Must%20be%20less%20than%20%7BMAX_SPAN_RATIO%3A.2f%7D%20times%20the%20diameter.%0A%20%20%20%20-%20Vertical%20distance%20between%20endpoints.%20Must%20be%20positive.%0A%0A%20%20%20%20%23%23%23%20References%0A%0A%20%20%20%20-%20%5BWikipedia%3A%20Catenary%5D(https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FCatenary)%20%20%0A%20%20%20%20-%20%5BEngineering%20practice%20for%20catenary%20sag%5D(https%3A%2F%2Fjlengineering.net%2Fblog%2Fwp-content%2Fuploads%2F2018%2F02%2FAerial-Power-Cables.pdf)%0A%0A%20%20%20%20%23%23%23%20Interactive%20Visualisation%0A%0A%20%20%20%20Adjust%20the%20sliders%20for%20**Diameter**%20and%20**Span**%20to%20see%20how%20the%20catenary%20curve%20changes.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Catenary%2C%20diameter%2C%20mo%2C%20span)%3A%0A%20%20%20%20%23%20---%20Try%20to%20compute%20catenary%20---%0A%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20catenary%20%3D%20Catenary(diameter.value%2C%20span.value)%0A%20%20%20%20%20%20%20%20a%2C%20b%20%3D%20catenary.fit_parameters()%0A%0A%20%20%20%20except%20Exception%20as%20e%3A%0A%20%20%20%20%20%20%20%20mo.md(f%22**Error%20occurred%3A**%20%7Be%7D%22)%0A%20%20%20%20return%20(catenary%2C)%0A%0A%0A%40app.cell%0Adef%20_(diameter%2C%20mo%2C%20span)%3A%0A%20%20%20%20%23%20---%20Show%20controls%20stacked%20---%0A%0A%20%20%20%20mo.hstack(%5Bdiameter%2C%20span%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(catenary)%3A%0A%20%20%20%20catenary.plot(x_range%3D(-0.2%2C%201.4)%2C%20y_range%3D(0.0%2C%201.2))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(catenary%2C%20mo)%3A%0A%20%20%20%20mo.md(catenary.describe())%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A</marimo-code></head>
<body>
<div id="root"></div>
<script data-marimo="true">
window.__MARIMO_MOUNT_CONFIG__ = {
"filename": "notebook.py",
"mode": "edit",
"version": "0.14.9",
"serverToken": "unused",
"config": {"completion": {"activate_on_typing": true, "copilot": false}, "display": {"cell_output": "below", "code_editor_font_size": 14, "dataframes": "rich", "default_table_page_size": 10, "default_width": "medium", "reference_highlighting": false, "theme": "light"}, "formatting": {"line_length": 79}, "keymap": {"overrides": {}, "preset": "default"}, "language_servers": {"pylsp": {"enable_flake8": false, "enable_mypy": true, "enable_pydocstyle": false, "enable_pyflakes": false, "enable_pylint": false, "enable_ruff": true, "enabled": true}}, "package_management": {"manager": "uv"}, "runtime": {"auto_instantiate": true, "auto_reload": "off", "default_sql_output": "auto", "on_cell_change": "autorun", "output_max_bytes": 8000000, "reactive_tests": true, "std_stream_max_bytes": 1000000, "watcher_on_save": "lazy"}, "save": {"autosave": "off", "autosave_delay": 1000, "format_on_save": false}, "server": {"browser": "default", "follow_symlink": false}, "snippets": {"custom_paths": [], "include_default_snippets": true}},
"configOverrides": {},
"appConfig": {"sql_output": "auto", "width": "medium"},
"view": {"showAppCode": false},
"notebook": null,
"session": null,
"runtimeConfig": null,
};
</script>
</body>
</html>