0% found this document useful (0 votes)
2 views10 pages

Exsolvent Field - Rational Function Visualizer (Mobile).HTML

The document is an HTML template for a mobile application called 'Exsolvent Field Visualizer' that allows users to explore rational function fields for multi-scale porous media through interactive controls and visualizations. Users can adjust parameters such as porosity, permeability, and polynomial weight using sliders, which dynamically update 3D plots and contour maps. The application is designed to provide insights into effective permeability and percolation thresholds in a visually engaging manner.

Uploaded by

adipedagogy
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views10 pages

Exsolvent Field - Rational Function Visualizer (Mobile).HTML

The document is an HTML template for a mobile application called 'Exsolvent Field Visualizer' that allows users to explore rational function fields for multi-scale porous media through interactive controls and visualizations. Users can adjust parameters such as porosity, permeability, and polynomial weight using sliders, which dynamically update 3D plots and contour maps. The application is designed to provide insights into effective permeability and percolation thresholds in a visually engaging manner.

Uploaded by

adipedagogy
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

<!

DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-
scale=1.0, user-scalable=no" />
<title>Exsolvent Field — Rational Function Visualizer (Mobile)</title>
<!-- Plotly CDN for interactive 3D and 2D plots -->
<script src="[Link]
<style>
* {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #0b0e14;
color: #e0e4ea;
margin: 0;
padding: 10px;
-webkit-text-size-adjust: 100%;
}
.app-container {
max-width: 100%;
margin: 0 auto;
}
h1 {
text-align: center;
font-weight: 300;
letter-spacing: 1px;
font-size: 1.5rem;
border-bottom: 1px solid #2a3a4a;
padding-bottom: 10px;
color: #b8d0e0;
margin-top: 10px;
}
h1 span {
color: #6a9bbf;
font-weight: 500;
}
.subtitle {
text-align: center;
color: #8899aa;
font-style: italic;
margin-top: -6px;
margin-bottom: 15px;
font-size: 0.85rem;
padding: 0 10px;
}
.controls {
display: flex;
flex-direction: column;
gap: 15px;
background: #141a22;
padding: 15px;
border-radius: 12px;
margin-bottom: 20px;
border: 1px solid #2a3a4a;
}
.control-group {
display: flex;
flex-direction: column;
width: 100%;
}
.control-group label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 1px;
color: #7a8fa0;
margin-bottom: 4px;
display: flex;
justify-content: space-between;
}
.control-group .value {
font-size: 0.9rem;
font-weight: 600;
color: #b0d0e8;
}
input[type="range"] {
width: 100%;
background: #1e2a34;
height: 8px;
border-radius: 4px;
-webkit-appearance: none;
appearance: none;
outline: none;
margin: 8px 0;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: #6a9bbf;
cursor: pointer;
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
input[type="range"]::-moz-range-thumb {
width: 24px;
height: 24px;
border-radius: 50%;
background: #6a9bbf;
cursor: pointer;
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
.grid-container {
display: grid;
grid-template-columns: 1fr;
gap: 15px;
}
.plot-card {
background: #141a22;
border-radius: 12px;
padding: 10px;
border: 1px solid #1e2e3e;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}
.plot-card h3 {
margin: 0 0 5px 0;
font-weight: 400;
font-size: 0.95rem;
color: #9ab0c8;
letter-spacing: 0.5px;
}
.plot-card .sub {
font-size: 0.7rem;
color: #6a7f90;
margin-bottom: 8px;
line-height: 1.3;
}
.plot-card .plot-container {
width: 100%;
height: 350px;
}
.full-width {
grid-column: 1 / -1;
}
.footer-note {
text-align: center;
font-size: 0.75rem;
color: #4a5f70;
margin-top: 20px;
border-top: 1px solid #1a2a3a;
padding-top: 15px;
line-height: 1.4;
}
.footer-note a {
color: #6a9bbf;
text-decoration: none;
}
</style>
</head>
<body>
<div class="app-container">
<h1><span>Exsolvent</span> Field Visualizer</h1>
<div class="subtitle">Rational Function Field for Multi-Scale Porous Media —
Weighted Polynomial Upscaling</div>

<!-- Controls -->


<div class="controls">
<div class="control-group">
<label>Porosity φ <span class="value" id="phiVal">0.45</span></label>
<input type="range" id="phiSlider" min="0.05" max="0.95" step="0.01"
value="0.45" />
</div>
<div class="control-group">
<label>Matrix Permeability k <span class="value"
id="kVal">0.50</span></label>
<input type="range" id="kSlider" min="0.01" max="1.0" step="0.01"
value="0.50" />
</div>
<div class="control-group">
<label>Percolation Threshold φ<sub>c</sub> <span class="value"
id="phiCVal">0.40</span></label>
<input type="range" id="phiCSlider" min="0.10" max="0.70" step="0.01"
value="0.40" />
</div>
<div class="control-group">
<label>Polynomial Weight α <span class="value"
id="alphaVal">0.50</span></label>
<input type="range" id="alphaSlider" min="0.00" max="1.00" step="0.01"
value="0.50" />
</div>
<div class="control-group">
<label>Grid Resolution <span class="value"
id="resVal">40</span></label>
<input type="range" id="resSlider" min="20" max="80" step="5"
value="40" />
</div>
</div>

<!-- Plots Grid -->


<div class="grid-container">
<div class="plot-card">
<h3>3D Topography — Effective Permeability K<sub>eff</sub></h3>
<div class="sub">Surface shows the rational field R(φ, k) = P(φ, k) /
Q(φ, k). Poles → percolation thresholds.</div>
<div id="plot3d" class="plot-container"></div>
</div>
<div class="plot-card">
<h3>Weighted Polynomial P(φ, k)</h3>
<div class="sub">Idealized flow (series × parallel) — the Watcher's
projection before encountering the abyss.</div>
<div id="plotContour" class="plot-container"></div>
</div>
<div class="plot-card full-width">
<h3>Physical Ground — Percolation Lattice</h3>
<div class="sub">White = conductive pore space (water flows). Black =
impermeable barrier. Red outline = largest connected cluster (Active Anchor
path).</div>
<div id="plotLattice" class="plot-container"
style="height:360px;"></div>
</div>
</div>

<div class="footer-note">
The Watcher inhabits the gap between the abstract field (top) and the
physical ground (bottom).<br/>
Drag sliders to explore the structural tension. <span
style="color:#4a6a7a;">— Exsolvent Geometry v1.0 (Mobile)</span>
</div>
</div>

<script>
(function() {
"use strict";

// --- DOM refs ---


const phiSlider = [Link]('phiSlider');
const kSlider = [Link]('kSlider');
const phiCSlider = [Link]('phiCSlider');
const alphaSlider = [Link]('alphaSlider');
const resSlider = [Link]('resSlider');
const phiVal = [Link]('phiVal');
const kVal = [Link]('kVal');
const phiCVal = [Link]('phiCVal');
const alphaVal = [Link]('alphaVal');
const resVal = [Link]('resVal');

// --- Core math ---


function rationalField(phi, k, phiC, alpha) {
const P = alpha * (phi * phi) * k + (1 - alpha) * 0.5 * phi * (1 - phi)
* [Link](k);
const Q = (phi - phiC) * (phi - phiC) + 0.005;
return P / Q;
}

function computeField(n, phiC, alpha, kFixed) {


const phiVals = new Float64Array(n);
const kVals = new Float64Array(n);
const K_eff = new Float64Array(n * n);
const P_vals = new Float64Array(n * n);

for (let i = 0; i < n; i++) {


phiVals[i] = 0.01 + (0.98 * i) / (n - 1);
kVals[i] = 0.01 + (0.99 * i) / (n - 1);
}

for (let j = 0; j < n; j++) {


for (let i = 0; i < n; i++) {
const phi = phiVals[i];
const k = kVals[j];
const idx = j * n + i;
const P = alpha * (phi * phi) * k + (1 - alpha) * 0.5 * phi *
(1 - phi) * [Link](k);
P_vals[idx] = P;
const Q = (phi - phiC) * (phi - phiC) + 0.005;
K_eff[idx] = P / Q;
}
}

return { phiVals, kVals, K_eff, P_vals };


}

// --- Generate lattice (percolation) ---


function generateLattice(size, p, seed) {
let s = seed || 42;
function rand() {
s = (s * 9301 + 49297) % 233280;
return s / 233280;
}
const grid = new Uint8Array(size * size);
for (let i = 0; i < size * size; i++) {
grid[i] = rand() < p ? 1 : 0;
}
return grid;
}

// --- Find largest connected cluster (4-connectivity) ---


function largestCluster(grid, size) {
const visited = new Uint8Array(size * size);
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
let maxSize = 0;
let bestLabel = -1;
let labelMap = new Int32Array(size * size);
let currentLabel = 0;

for (let y = 0; y < size; y++) {


for (let x = 0; x < size; x++) {
const idx = y * size + x;
if (grid[idx] === 1 && !visited[idx]) {
const queue = [[x,y]];
visited[idx] = 1;
let count = 0;
const clusterIndices = [];
while ([Link] > 0) {
const [cx, cy] = [Link]();
const cidx = cy * size + cx;
[Link](cidx);
count++;
for (const d of dirs) {
const nx = cx + d[0];
const ny = cy + d[1];
if (nx >= 0 && nx < size && ny >= 0 && ny < size) {
const nidx = ny * size + nx;
if (grid[nidx] === 1 && !visited[nidx]) {
visited[nidx] = 1;
[Link]([nx, ny]);
}
}
}
}
if (count > maxSize) {
maxSize = count;
bestLabel = currentLabel;
}
for (const ci of clusterIndices) {
labelMap[ci] = currentLabel;
}
currentLabel++;
}
}
}
const mask = new Uint8Array(size * size);
if (bestLabel >= 0) {
for (let i = 0; i < size * size; i++) {
if (labelMap[i] === bestLabel) mask[i] = 1;
}
}
return mask;
}

// --- Plotly rendering ---


function renderAll() {
const phi = parseFloat([Link]);
const k = parseFloat([Link]);
const phiC = parseFloat([Link]);
const alpha = parseFloat([Link]);
const res = parseInt([Link], 10);

[Link] = [Link](2);
[Link] = [Link](2);
[Link] = [Link](2);
[Link] = [Link](2);
[Link] = res;

const n = res;
const data = computeField(n, phiC, alpha, k);

// --- 3D Surface ---


const trace3d = {
type: 'surface',
x: [Link],
y: [Link],
z: [Link]({ length: n }, (_, j) =>
[Link]({ length: n }, (_, i) => data.K_eff[j * n + i])
),
colorscale: 'Plasma',
showscale: false, // Hidden on mobile to save space
hovertemplate: 'φ: %{x:.2f}<br>k: %{y:.2f}<br>K_eff: %
{z:.2f}<extra></extra>'
};

let zAtPoint = 0;
try {
zAtPoint = rationalField(phi, k, phiC, alpha);
} catch(e) { zAtPoint = 0; }

const pointTrace = {
type: 'scatter3d',
mode: 'markers',
x: [phi],
y: [k],
z: [zAtPoint],
marker: { color: 'red', size: 6, symbol: 'diamond' },
name: 'Watcher Position'
};

const layout3d = {
margin: { l: 10, r: 10, b: 20, t: 10 },
scene: {
xaxis: { title: 'φ', color: '#8899aa' },
yaxis: { title: 'k', color: '#8899aa' },
zaxis: { title: 'K', color: '#8899aa' },
bgcolor: '#0b0e14',
camera: { eye: { x: 1.8, y: 1.8, z: 1.4 } }
},
paper_bgcolor: '#141a22',
plot_bgcolor: '#0b0e14',
font: { color: '#c0d0e0', size: 10 }
};

[Link]('plot3d', [trace3d, pointTrace], layout3d, { responsive:


true, displayModeBar: false });

// --- Contour: Weighted Polynomial P(φ,k) ---


const pMatrix = [Link]({ length: n }, (_, j) =>
[Link]({ length: n }, (_, i) => data.P_vals[j * n + i])
);

const traceContour = {
type: 'contour',
x: [Link],
y: [Link],
z: pMatrix,
colorscale: 'Viridis',
showscale: false,
contours: { coloring: 'heatmap' },
hovertemplate: 'φ: %{x:.2f}<br>k: %{y:.2f}<br>P:
%{z:.2f}<extra></extra>'
};

const layoutContour = {
margin: { l: 30, r: 10, b: 30, t: 10 },
xaxis: { title: 'Porosity φ', color: '#8899aa', gridcolor:
'#1a2a3a' },
yaxis: { title: 'Permeability k', color: '#8899aa', gridcolor:
'#1a2a3a' },
paper_bgcolor: '#141a22',
plot_bgcolor: '#0b0e14',
font: { color: '#c0d0e0', size: 10 }
};

[Link]('plotContour', [traceContour], layoutContour,


{ responsive: true, displayModeBar: false });

// --- Lattice (Percolation) ---


const size = 60; // Slightly reduced resolution for faster mobile
rendering
const pLattice = [Link](0.9, [Link](0.2, 0.4 + (phi - 0.3) * 0.8));
const grid = generateLattice(size, pLattice, 42 + [Link](phi *
1000));
const clusterMask = largestCluster(grid, size);

const clusterPoints = [];


for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
const idx = y * size + x;
if (clusterMask[idx] === 1) {
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
let isBoundary = false;
for (const d of dirs) {
const nx = x + d[0];
const ny = y + d[1];
if (nx < 0 || nx >= size || ny < 0 || ny >= size) {
isBoundary = true;
break;
}
const nidx = ny * size + nx;
if (clusterMask[nidx] === 0) {
isBoundary = true;
break;
}
}
if (isBoundary) {
[Link]({ x: x, y: y });
}
}
}
}
const zLattice = [Link]({ length: size }, (_, j) =>
[Link]({ length: size }, (_, i) => grid[j * size + i] ? 1 : 0)
);

const traceLattice = {
type: 'heatmap',
z: zLattice,
colorscale: [[0, '#0a0f18'], [1, '#d0e4f0']],
showscale: false,
hovertemplate: 'x: %{x}<br>y: %{y}<br>Pore: %{z}<extra></extra>'
};

const traceCluster = {
type: 'scatter',
mode: 'markers',
x: [Link](p => p.x),
y: [Link](p => p.y),
marker: { color: '#ff4466', size: 2, opacity: 0.8 },
name: 'Active Anchor Path'
};

const layoutLattice = {
margin: { l: 10, r: 10, b: 10, t: 25 },
xaxis: { showgrid: false, showticklabels: false, range: [-1,
size] },
yaxis: { showgrid: false, showticklabels: false, range: [-1, size],
scaleanchor: 'x' },
paper_bgcolor: '#141a22',
plot_bgcolor: '#0b0e14',
font: { color: '#c0d0e0', size: 10 },
annotations: [{
x: 0.5,
y: 1.08,
xref: 'paper',
yref: 'paper',
text: `φ = ${[Link](2)} | p = ${[Link](2)} |
Cluster: ${[Link]}`,
showarrow: false,
font: { size: 10, color: '#8aaabc' }
}]
};

const latticeTraces = [traceLattice];


if ([Link] > 0) {
[Link](traceCluster);
}

[Link]('plotLattice', latticeTraces, layoutLattice, { responsive:


true, displayModeBar: false });
}

// --- Event binding ---


const sliders = [phiSlider, kSlider, phiCSlider, alphaSlider, resSlider];
[Link](s => [Link]('input', renderAll));

// --- Initial render ---


renderAll();
})();
</script>

</body>
</html>

You might also like