You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by "cnlnr (via GitHub)" <gi...@apache.org> on 2026/07/22 14:37:09 UTC

[I] [Feature] Support Curved-Boundary Bar Chart / Dynamic Phase Boundary Detection [echarts]

cnlnr opened a new issue, #21703:
URL: https://github.com/apache/echarts/issues/21703

   ### What problem does this feature solve?
   
   ### Use Case & Context
   In physical domain modeling, thermodynamics (e.g., phase diagrams showing P-T relationships), and complex multi-threshold business monitoring, data visualization needs to show how dynamic parameters interact with non-linear/curved decision boundaries.
   
   Currently, standard bar charts (`type: 'bar'`) only support fixed threshold lines or simple linear limits. While users can implement custom shapes using `type: 'custom'` with raw Canvas rendering (`renderItem`), calculating collisions and intersections between moving bar vertices and curved threshold lines (e.g., polynomial/spline boundaries) in a 2D parameter space is complex, performant-heavy, and difficult to generalize.
   
   ### End User Experience
   With this feature, users could define smooth/curved boundary paths in the coordinate system. As bar elements change in position (X-axis) or height (Y-axis), the chart would automatically compute boundary intersections and trigger real-time state/phase transitions (e.g., solid, liquid, gas, or custom status zones) with responsive styling and HiDPI canvas rendering.
   
   ### What does the proposed API look like?
   
   We propose extending the `markLine` / `series` capability to support standard curved boundaries or adding a specialized phase/boundary property within `type: 'bar'` or `type: 'custom'`:
   
   ```javascript
   option = {
     xAxis: { type: 'value', name: 'Pressure (P)' },
     yAxis: { type: 'value', name: 'Temperature (T)' },
     series: [
       {
         type: 'bar',
         data: [[2.0, 5.5]], // [X Position, Y Height]
         // Proposed API for dynamic curved boundary detection
         boundaryDetection: {
           enabled: true,
           curves: [
             {
               name: 'Sublimation Curve',
               // Function or polynomial points defining the boundary curve
               expr: (x) => Math.pow(x, 2) + 1, 
               states: {
                 above: { itemStyle: { color: '#ff4d4f' }, label: 'Gas' },
                 below: { itemStyle: { color: '#1890ff' }, label: 'Solid' }
               }
             }
           ]
         }
       }
     ]
   };
   ```


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


Re: [I] [Feature] Support Curved-Boundary Bar Chart / Dynamic Phase Boundary Detection [echarts]

Posted by "plainheart (via GitHub)" <gi...@apache.org>.
plainheart closed issue #21703: [Feature] Support Curved-Boundary Bar Chart / Dynamic Phase Boundary Detection
URL: https://github.com/apache/echarts/issues/21703


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


Re: [I] [Feature] Support Curved-Boundary Bar Chart / Dynamic Phase Boundary Detection [echarts]

Posted by "plainheart (via GitHub)" <gi...@apache.org>.
plainheart commented on issue #21703:
URL: https://github.com/apache/echarts/issues/21703#issuecomment-5057042404

   Hi @cnlnr, we can 100% implement this right now using a combination of `type: 'line'` (for the continuous curved boundaries), `type: 'custom'` (for the current phase bar), and driving the reactive state logic outside the renderer via standard mathematical evaluation functions. Here is a working demo based on this approach and generated with AI assistance.
   
   https://codepen.io/plainheart/pen/EaZeKXJ
   
   <details>
   <summary>Click To View Code</summary>
   
   ```html
   <!DOCTYPE html>
   <html lang="zh-CN">
   <head>
       <meta charset="UTF-8">
       <title>ECharts 曲界线柱状图 (Phase-Bar Diagram)</title>
       <style>
           body { background-color: #0b0f19; color: #fff; font-family: sans-serif; margin: 20px; }
           .container { max-width: 800px; margin: 0 auto; background: #121826; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); }
           h3 { margin-top: 0; text-align: center; font-weight: normal; color: #e1e7f0; }
           h5 { text-align: center; color: #737d8c; margin-top: -10px; margin-bottom: 20px; font-weight: normal; font-size: 13px; }
           #chart { width: 100%; height: 420px; }
           .controls { display: flex; flex-direction: column; gap: 15px; margin-top: 20px; border-top: 1px solid #1f293d; padding-top: 20px; }
           .control-row { display: flex; align-items: center; justify-content: space-between; }
           .control-row label { width: 160px; color: #8e9aa8; font-size: 14px; }
           .control-row input { flex: 1; margin: 0 15px; cursor: pointer; }
           .control-row span { width: 90px; text-align: right; font-weight: bold; font-family: monospace; font-size: 14px; }
           .result-panel { background: #1a2336; padding: 15px; border-radius: 6px; margin-top: 20px; display: flex; justify-content: space-between; align-items: center; border-left: 4px solid #3b82f6; transition: border-color 0.3s; }
           .result-panel span { font-size: 14px; color: #8e9aa8; }
           .result-panel strong { font-size: 18px; transition: color 0.3s; }
       </style>
   	<script src="https://cdn.jsdelivr.net/npm/echarts@6.1.0/dist/echarts.min.js"></script>
   </head>
   <body>
   
   <div class="container">
       <h3>曲界线柱状图 (Phase-Bar Diagram)</h3>
       <h5>动态调整气压与温度,观察柱体顶点跨越曲界线时的状态变更</h5>
   
       <div id="chart"></div>
   
       <div class="controls">
           <div class="control-row">
               <label>压强 (Pressure / P):</label>
               <input type="range" id="pressInput" min="0" max="10" step="0.01" value="1.00">
               <span id="pressVal">1.00 atm</span>
           </div>
           <div class="control-row">
               <label>温度 (Temperature / T):</label>
               <input type="range" id="tempInput" min="-50" max="450" step="1" value="25">
               <span id="tempVal">25 °C</span>
           </div>
       </div>
   
       <div class="result-panel" id="resultBorder">
           <span>当前物理状态判定 (Current Phase Determination)</span>
           <strong id="phaseResult">液态 (Liquid)</strong>
       </div>
   </div>
   
   <script>
       const chartDom = document.getElementById('chart');
       const myChart = echarts.init(chartDom);
   
       const domainP = { min: 0, max: 10 };
       const domainT = { min: -50, max: 450 };
   
       const triplePoint = { p: 0.8, t: 0.01 };
       const criticalPoint = { p: 7.5, t: 374 };
   
       const PHASE_CONFIG = {
           SOLID: { name: '固态 (Solid)', color: '#38bdf8', border: '#0284c7' },
           LIQUID: { name: '液态 (Liquid)', color: '#3b82f6', border: '#1d4ed8' },
           GAS: { name: '气态 (Gas)', color: '#e3b341', border: '#d97706' },
           SUPERCRITICAL: { name: '超临界流体 (Supercritical)', color: '#bc8cff', border: '#8957e5' }
       };
   
       // 弯曲界线函数方程
       function getSublimationT(p) {
           if (p > triplePoint.p) return null;
           return domainT.min + (triplePoint.t - domainT.min) * Math.pow(p / triplePoint.p, 0.6);
       }
   
       // 汽化线:从三相点延伸到临界点
       function getVaporizationT(p) {
           if (p < triplePoint.p || p > criticalPoint.p) return null;
           const normP = (p - triplePoint.p) / (criticalPoint.p - triplePoint.p);
           return triplePoint.t + (criticalPoint.t - triplePoint.t) * Math.pow(normP, 0.45);
       }
   
       // 熔化线:从三相点向右上方延伸(实际随P增大而轻微下降)
       function getMeltingT(p) {
           if (p < triplePoint.p) return null;
           return triplePoint.t - 3.5 * Math.log(p / triplePoint.p + 1);
       }
   
       // 物理相态判定
       function evaluatePhase(p, t) {
           if (p >= criticalPoint.p && t >= criticalPoint.t) {
               return PHASE_CONFIG.SUPERCRITICAL;
           }
           if (p < triplePoint.p) {
               const subT = getSublimationT(p);
               return t < subT ? PHASE_CONFIG.SOLID : PHASE_CONFIG.GAS;
           } else {
               const meltT = getMeltingT(p);
               if (t < meltT) return PHASE_CONFIG.SOLID;
   
               if (p <= criticalPoint.p) {
                   const vapT = getVaporizationT(p);
                   return t < vapT ? PHASE_CONFIG.LIQUID : PHASE_CONFIG.GAS;
               } else {
                   return PHASE_CONFIG.LIQUID;
               }
           }
       }
   
       // 生成平滑的高密度数据线段
       const subData = [];   // 升华线 (0 -> 三相点)
       const vapData = [];   // 汽化线 (三相点 -> 临界点)
       const meltData = [];  // 熔化线 (三相点 -> 10)
   
       for (let p = 0; p <= 10; p += 0.01) {
           const sT = getSublimationT(p);
           if (sT !== null) subData.push([p, sT]);
   
           const vT = getVaporizationT(p);
           if (vT !== null) vapData.push([p, vT]);
   
           const mT = getMeltingT(p);
           if (mT !== null) meltData.push([p, mT]);
       }
   
       function updateChart() {
           const p = parseFloat(document.getElementById('pressInput').value);
           const t = parseFloat(document.getElementById('tempInput').value);
   
           document.getElementById('pressVal').innerText = p.toFixed(2) + ' atm';
           document.getElementById('tempVal').innerText = t + ' °C';
   
           const currentPhase = evaluatePhase(p, t);
           
           const resultEl = document.getElementById('phaseResult');
           const borderEl = document.getElementById('resultBorder');
           resultEl.innerText = currentPhase.name;
           resultEl.style.color = currentPhase.color;
           borderEl.style.borderColor = currentPhase.border;
   
           const option = {
               backgroundColor: 'transparent',
               animation: false,
   			textStyle: {
   				fontFamily: 'monospace'
   			},
               grid: { left: '5%', right: '6%', bottom: '8%', top: '8%', containLabel: true },
               xAxis: {
                   type: 'value',
                   min: domainP.min,
                   max: domainP.max,
   				boundaryGap: [0, 0],
                   interval: 2,
                   axisLabel: { color: '#8b949e', formatter: (val) => val + ' atm' },
                   splitLine: { show: true, lineStyle: { color: '#30363d' } },
                   axisLine: { show: true, lineStyle: { color: '#30363d' } }
               },
               yAxis: {
                   type: 'value',
                   min: domainT.min,
                   max: domainT.max,
                   interval: 50,
                   axisLabel: {
   				    color: '#8b949e',
   					formatter: (value) => value + ' °C'
   				},
                   splitLine: {
   				    show: true,
   					lineStyle: { color: '#30363d' }
   				},
                   axisLine: { show: true, lineStyle: { color: '#30363d' } }
               },
               series: [
                   // 升华线
                   {
                       name: '升华边界',
                       type: 'line',
                       data: subData,
                       showSymbol: false,
                       lineStyle: { color: '#38bdf8', width: 2, type: 'dashed' },
                       z: 2
                   },
                   // 汽化线:黄色实线段
                   {
                       name: '汽化边界',
                       type: 'line',
                       data: vapData,
                       showSymbol: false,
                       lineStyle: { color: '#e3b341', width: 2.5 },
                       z: 2
                   },
                   // 熔化线
                   {
                       name: '熔化边界',
                       type: 'line',
                       data: meltData,
                       showSymbol: false,
                       lineStyle: { color: '#00e676', width: 2 },
                       z: 2
                   },
                   // 特征点标记(三相点、临界点)
                   {
                       name: '特征点',
                       type: 'scatter',
                       data: [
                           { value: [triplePoint.p, triplePoint.t], itemStyle: { color: '#ff4d4f' }, label: { formatter: '三相点 (Triple Point)', color: '#00e676', position: 'right' } },
                           { value: [criticalPoint.p, criticalPoint.t], itemStyle: { color: '#bc8cff' }, label: { formatter: '临界点 (Critical Point)', color: '#bc8cff', position: 'right' } }
                       ],
                       symbolSize: 7,
                       label: { show: true, fontSize: 11, offset: [5, 0] },
                       z: 4
                   },
                   // 状态主柱体
                   {
   					name: '当前状态',
   					type: 'custom',
   					data: [[p, t]],
   					renderItem: function (params, api) {
   						const currentPoint = api.coord([api.value(0), api.value(1)]); 
   						const bottomPoint = api.coord([api.value(0), domainT.min]);   
   						const barWidth = 16;
   						return {
   							type: 'rect',
   							shape: {
   								x: currentPoint[0] - barWidth / 2,
   								y: currentPoint[1],
   								width: barWidth,
   								height: bottomPoint[1] - currentPoint[1]
   							},
   							style: api.style({
   								fill: currentPhase.color,
   								stroke: currentPhase.border,
   								lineWidth: 1.5
   							})
   						};
   					},
   					label: {
   						show: true,
   						position: 'top',
   						color: '#fff',
   						backgroundColor: '#0b0f19',
   						padding: [4,6],
   						borderWidth: 1,
   						borderColor: currentPhase.border,
   						formatter: t + '°C',
   						offset: [0, -4]
   					},
   					z: 3
   				}
               ]
           };
   
           myChart.setOption(option);
       }
   
       document.getElementById('pressInput').addEventListener('input', updateChart);
       document.getElementById('tempInput').addEventListener('input', updateChart);
   
       updateChart()
   
       window.addEventListener('resize', () => myChart.resize());
   </script>
   </body>
   </html>
   ```
   
   </details>
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org