JY 5 hónapja
commit
3322c68793
100 módosított fájl, 36240 hozzáadás és 0 törlés
  1. BIN
      DIPM_resultStatistics/alert.mp3
  2. 12 0
      DIPM_resultStatistics/chart.js
  3. 986 0
      DIPM_resultStatistics/index.html
  4. 4 0
      Desmos-Desktop-master/.gitignore
  5. 21 0
      Desmos-Desktop-master/LICENSE
  6. 52 0
      Desmos-Desktop-master/README.md
  7. 185 0
      Desmos-Desktop-master/examples/Astroid.des
  8. 77 0
      Desmos-Desktop-master/examples/Cardioid.des
  9. 97 0
      Desmos-Desktop-master/examples/Folded Conic Section 2.des
  10. 91 0
      Desmos-Desktop-master/examples/Folded Conic Section.des
  11. 218 0
      Desmos-Desktop-master/examples/Mobius f(z)=(z-(1+i))_(z-(-1-i)) Elliptic&Hyperbolic Type.des
  12. 121 0
      Desmos-Desktop-master/examples/Mobius r=+-/theta AND r=const&/theta=const.des
  13. 68 0
      Desmos-Desktop-master/examples/Nephroid.des
  14. 17 0
      Desmos-Desktop-master/examples/README.md
  15. 43 0
      Desmos-Desktop-master/examples/getDesByUrl.py
  16. 15 0
      Desmos-Desktop-master/examples/hfpbsj2rdd.des
  17. 36 0
      Desmos-Desktop-master/examples/tri.des
  18. 26 0
      Desmos-Desktop-master/index.html
  19. 211 0
      Desmos-Desktop-master/main.js
  20. 37 0
      Desmos-Desktop-master/package.json
  21. 239 0
      Desmos-Desktop-master/renderer.js
  22. BIN
      Desmos-Desktop-master/res/Astroid.png
  23. BIN
      Desmos-Desktop-master/res/Brain.png
  24. BIN
      Desmos-Desktop-master/res/Cardioid.png
  25. BIN
      Desmos-Desktop-master/res/Folded Conic Section.png
  26. BIN
      Desmos-Desktop-master/res/Folded Conic Section2.png
  27. BIN
      Desmos-Desktop-master/res/Folded Conic Section3.png
  28. BIN
      Desmos-Desktop-master/res/Mobius.png
  29. BIN
      Desmos-Desktop-master/res/Mobius2.png
  30. BIN
      Desmos-Desktop-master/res/Powerpuff Grils.png
  31. BIN
      Desmos-Desktop-master/res/app.png
  32. 14 0
      Desmos-Desktop-master/res/appimagekit-desmos.desktop
  33. BIN
      Desmos-Desktop-master/res/icons/icon.png
  34. BIN
      Desmos-Desktop-master/res/icons/icon_bak.png
  35. 33 0
      Desmos-Desktop-master/src/css/style.css
  36. 70 0
      Desmos-Desktop-master/src/info.html
  37. 7 0
      Desmos-Desktop-master/src/js/calculator.js
  38. 765 0
      FuncGen/data-table.js
  39. 731 0
      FuncGen/enhanced-styles.css
  40. 199 0
      FuncGen/excel-table.css
  41. 556 0
      FuncGen/fit.js
  42. 209 0
      FuncGen/index.html
  43. 561 0
      FuncGen/math-keyboard.js
  44. 0 0
      FuncGen/math.min.js
  45. 7 0
      FuncGen/plotly-2.24.1.min.js
  46. 672 0
      FuncGen/regression.js
  47. 485 0
      FuncGen/script.js
  48. 227 0
      FuncGen/styles.css
  49. 0 0
      FuncGen/tex-mml-chtml.js
  50. 281 0
      FuncGen/utils-new.js
  51. 135 0
      FuncGen/utils.js
  52. 992 0
      GeoGebra_Self/HTML5/5.0/GeoGebra.html
  53. 0 0
      GeoGebra_Self/HTML5/5.0/css/app-header.css
  54. 0 0
      GeoGebra_Self/HTML5/5.0/css/av-styles.css
  55. 0 0
      GeoGebra_Self/HTML5/5.0/css/bundles/bundle.css
  56. 0 0
      GeoGebra_Self/HTML5/5.0/css/bundles/simple-bundle.css
  57. 1 0
      GeoGebra_Self/HTML5/5.0/css/colorMap.css
  58. 1 0
      GeoGebra_Self/HTML5/5.0/css/colors.css
  59. 0 0
      GeoGebra_Self/HTML5/5.0/css/component-styles.css
  60. 0 0
      GeoGebra_Self/HTML5/5.0/css/context-menu.css
  61. 0 0
      GeoGebra_Self/HTML5/5.0/css/dialog-styles.css
  62. 1 0
      GeoGebra_Self/HTML5/5.0/css/dimens.css
  63. 0 0
      GeoGebra_Self/HTML5/5.0/css/editor.css
  64. 0 0
      GeoGebra_Self/HTML5/5.0/css/ev-styles.css
  65. 1 0
      GeoGebra_Self/HTML5/5.0/css/evaluator-styles.css
  66. 1 0
      GeoGebra_Self/HTML5/5.0/css/exam.css
  67. 0 0
      GeoGebra_Self/HTML5/5.0/css/fonts.css
  68. 0 0
      GeoGebra_Self/HTML5/5.0/css/general.css
  69. 0 0
      GeoGebra_Self/HTML5/5.0/css/greek-font.css
  70. 0 0
      GeoGebra_Self/HTML5/5.0/css/header.css
  71. 0 0
      GeoGebra_Self/HTML5/5.0/css/headerbar.css
  72. 1 0
      GeoGebra_Self/HTML5/5.0/css/info-card.css
  73. 0 0
      GeoGebra_Self/HTML5/5.0/css/keyboard-styles.css
  74. 0 0
      GeoGebra_Self/HTML5/5.0/css/layout.css
  75. 0 0
      GeoGebra_Self/HTML5/5.0/css/menu-styles.css
  76. 0 0
      GeoGebra_Self/HTML5/5.0/css/mow-toolbox.css
  77. 1 0
      GeoGebra_Self/HTML5/5.0/css/mow-topbar.css
  78. 0 0
      GeoGebra_Self/HTML5/5.0/css/mow.css
  79. 0 0
      GeoGebra_Self/HTML5/5.0/css/open-screen.css
  80. 0 0
      GeoGebra_Self/HTML5/5.0/css/popup-styles.css
  81. 1 0
      GeoGebra_Self/HTML5/5.0/css/print.css
  82. 0 0
      GeoGebra_Self/HTML5/5.0/css/probability-layout.css
  83. 0 0
      GeoGebra_Self/HTML5/5.0/css/reset.css
  84. 0 0
      GeoGebra_Self/HTML5/5.0/css/scientific-layout.css
  85. 0 0
      GeoGebra_Self/HTML5/5.0/css/settings-styles.css
  86. 0 0
      GeoGebra_Self/HTML5/5.0/css/shared.css
  87. 0 0
      GeoGebra_Self/HTML5/5.0/css/snackbar.css
  88. 1 0
      GeoGebra_Self/HTML5/5.0/css/spreadsheet-tab.css
  89. 0 0
      GeoGebra_Self/HTML5/5.0/css/spreadsheet.css
  90. 1 0
      GeoGebra_Self/HTML5/5.0/css/step-tree.css
  91. 0 0
      GeoGebra_Self/HTML5/5.0/css/tableview.css
  92. 1 0
      GeoGebra_Self/HTML5/5.0/css/text-styles.css
  93. 0 0
      GeoGebra_Self/HTML5/5.0/css/toolbar-styles.css
  94. 0 0
      GeoGebra_Self/HTML5/5.0/css/web-styles-global.css
  95. 0 0
      GeoGebra_Self/HTML5/5.0/css/web-styles.css
  96. 1 0
      GeoGebra_Self/HTML5/5.0/css/z-index.css
  97. 27728 0
      GeoGebra_Self/HTML5/5.0/web/03AC6A979CF3ACBAEC7A4498AE960BB9.cache.js
  98. BIN
      GeoGebra_Self/HTML5/5.0/web/clear.cache.gif
  99. 0 0
      GeoGebra_Self/HTML5/5.0/web/deferredjs/03AC6A979CF3ACBAEC7A4498AE960BB9/1.cache.js
  100. 0 0
      GeoGebra_Self/HTML5/5.0/web/deferredjs/03AC6A979CF3ACBAEC7A4498AE960BB9/10.cache.js

BIN
DIPM_resultStatistics/alert.mp3


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 12 - 0
DIPM_resultStatistics/chart.js


+ 986 - 0
DIPM_resultStatistics/index.html

@@ -0,0 +1,986 @@
+<!DOCTYPE html>
+<html lang="zh">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>DIPM_resultStatistics</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            min-height: 100vh;
+            background-color: #f4f4f9;
+        }
+
+        .container {
+            display: flex;
+            flex-direction: column;
+            width: 90%;
+            max-width: 800px;
+            background: #fff;
+            padding: 1.5rem;
+            border-radius: 8px;
+            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+        }
+
+        .tabs {
+            display: flex;
+            border-bottom: 1px solid #ddd;
+            margin-bottom: 1.5rem;
+        }
+
+        .tab {
+            padding: 0.5rem 1rem;
+            cursor: pointer;
+            border: 1px solid transparent;
+            border-bottom: none;
+            border-radius: 4px 4px 0 0;
+            margin-right: 0.5rem;
+            transition: all 0.3s ease;
+            font-size: 0.9rem;
+        }
+
+        .tab.active {
+            border-color: #ddd;
+            border-bottom-color: #fff;
+            background-color: #fff;
+            color: #007bff;
+            font-weight: bold;
+        }
+
+        .tab-content {
+            display: none;
+        }
+
+        .tab-content.active {
+            display: block;
+        }
+
+        .content-container {
+            display: flex;
+            flex-direction: row;
+            gap: 2rem;
+            width: 100%;
+        }
+
+        .input-section,
+        .results-section {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            justify-content: space-between;
+        }
+
+        h1 {
+            font-size: 1.5rem;
+            margin-bottom: 1rem;
+        }
+
+        .input-group {
+            display: flex;
+            flex-direction: column;
+            gap: 1rem;
+            height: 100%;
+            justify-content: space-between;
+        }
+
+        input[type="number"] {
+            padding: 0.5rem;
+            font-size: 1rem;
+            border: 1px solid #ccc;
+            border-radius: 4px;
+            transition: border-color 0.3s;
+        }
+
+        input[type="number"]:focus {
+            outline: none;
+            border-color: #007bff;
+        }
+
+        .results {
+            display: flex;
+            flex-direction: column;
+            gap: 0.5rem;
+        }
+
+        .result-item {
+            font-size: 1rem;
+            color: #333;
+        }
+
+        .result-item:nth-child(4) {
+            color: #0f72db;
+        }
+
+        .result-item:nth-child(5) {
+            color: #11b9b9;
+        }
+
+        #stdDev {
+            color: #0f72db;
+        }
+
+        #rsd {
+            color: #11b9b9;
+        }
+
+        .stability-container,
+        .error-container {
+            display: flex;
+            flex-direction: column;
+            gap: 1rem;
+            margin-bottom: 1.5rem;
+        }
+
+        .stability-row {
+            display: flex;
+            align-items: center;
+            gap: 0.5rem;
+        }
+
+        .stability-row span {
+            width: 60px;
+        }
+
+        .progress-container {
+            display: inline-flex;
+            position: relative;
+            padding: 0.2rem 0.5rem;
+            border-radius: 4px;
+            background: linear-gradient(to right, #007bff var(--progress-percent), #e0e0e0 var(--progress-percent));
+            background-size: 100% 100%;
+            transition: background-position 0.3s ease;
+            margin-left: 1em;
+            gap: 0.8rem;
+        }
+
+        .progress-container.completed {
+            background: linear-gradient(to right, #ff0000 100%, #e0e0e0 100%);
+        }
+
+        .stability-time-display {
+            font-size: 0.9rem;
+            color: #666;
+            position: relative;
+            z-index: 1;
+        }
+
+        .stability-time-display.warning {
+            color: #ff0000;
+            font-weight: bold;
+        }
+
+        .error-point-container {
+            border: 1px solid #eee;
+            border-radius: 4px;
+            padding: 1rem;
+            margin-bottom: 1rem;
+        }
+
+        .error-inputs {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 0.5rem;
+            margin: 0.5rem 0;
+        }
+
+        .error-input {
+            flex: 1;
+            min-width: 80px;
+        }
+
+        .button-container {
+            display: flex;
+            justify-content: space-between;
+            width: 100%;
+            gap: 1rem;
+        }
+
+        .export-btn,
+        .import-btn {
+            flex: 1;
+            padding: 1rem;
+            border: none;
+            border-radius: 4px;
+            background-color: #007bff;
+            color: white;
+            font-size: 1rem;
+            cursor: pointer;
+            transition: background-color 0.3s;
+        }
+
+        .export-btn:hover,
+        .import-btn:hover {
+            background-color: #0056b3;
+        }
+
+        .error-result {
+            margin-top: 0.5rem;
+            font-weight: bold;
+        }
+
+        .error-container {
+            display: flex;
+            flex-direction: column;
+            gap: 1rem;
+        }
+
+        .chart-container {
+            margin-top: 1.5rem;
+            position: relative;
+            width: 100%;
+            height: 200px;
+            border: 1px solid #ddd;
+            border-radius: 4px;
+            padding-left: 1%;
+        }
+
+        canvas {
+            width: 100%;
+            height: 100%;
+        }
+
+        @media (max-width: 600px) {
+            .container {
+                flex-direction: column;
+            }
+
+            .content-container {
+                flex-direction: column;
+            }
+
+            .input-section,
+            .results-section {
+                width: 100%;
+            }
+
+            h1 {
+                font-size: 1.2rem;
+            }
+
+            input[type="number"] {
+                font-size: 0.9rem;
+                padding: 0.4rem;
+            }
+
+            .error-inputs {
+                flex-direction: row;
+            }
+
+            .error-input {
+                flex: 1;
+            }
+        }
+
+    </style>
+</head>
+
+<body>
+    <audio id="alertSound" src="alert.mp3" preload="auto"></audio>
+    <div class="container">
+        <div class="tabs">
+            <div class="tab active" data-tab="repeatability">重复性</div>
+            <div class="tab" data-tab="stability">稳定性</div>
+            <div class="tab" data-tab="error">示值误差</div>
+            <div class="tab" data-tab="data">数据</div>
+        </div>
+
+        <!-- 重复性标签页 -->
+        <div id="repeatability" class="tab-content active">
+            <div class="content-container">
+                <div class="input-section">
+                    <h1>输入数据</h1>
+                    <div class="input-group">
+                        <input type="number" placeholder="1: " oninput="calculateRepeatability()">
+                        <input type="number" placeholder="2: " oninput="calculateRepeatability()">
+                        <input type="number" placeholder="3: " oninput="calculateRepeatability()">
+                        <input type="number" placeholder="4: " oninput="calculateRepeatability()">
+                        <input type="number" placeholder="5: " oninput="calculateRepeatability()">
+                        <input type="number" placeholder="6: " oninput="calculateRepeatability()">
+                        <input type="number" placeholder="7: (可选)"
+                            oninput="calculateRepeatability()">
+                    </div>
+                </div>
+                <div class="results-section">
+                    <h1>计算结果</h1>
+                    <div class="results">
+                        <div class="result-item">极差R: <span id="range">-</span></div>
+                        <div class="result-item">平均值x̄: <span id="mean">-</span></div>
+                        <div class="result-item">方差s<sup>2</sup>: <span id="variance">-</span></div>
+                        <div class="result-item">标准差s: <span id="stdDev">-</span></div>
+                        <div class="result-item">相对标准偏差RSD: <span id="rsd">-</span></div>
+                    </div>
+                    <div class="chart-container">
+                        <canvas id="lineChart"></canvas>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- 稳定性标签页 -->
+        <div id="stability" class="tab-content">
+            <div class="content-container">
+                <div class="input-section">
+                    <h1>稳定性测试
+                        <div class="progress-container">
+                            <span id="current-time" class="stability-time-display"></span>
+                            <span class="stability-time-display"> · </span>
+                            <span id="countdown" class="stability-time-display">10:00</span>
+                        </div>
+                    </h1>
+                    <div class="input-group">
+                        <div class="stability-row">
+                            <span id="stability-time-text-1">0: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-1"
+                                oninput="recordTimeAndCalculateStability(1)">
+                        </div>
+                        <div class="stability-row">
+                            <span id="stability-time-text-2">10: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-2"
+                                oninput="recordTimeAndCalculateStability(2)">
+                        </div>
+                        <div class="stability-row">
+                            <span id="stability-time-text-3">20: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-3"
+                                oninput="recordTimeAndCalculateStability(3)">
+                        </div>
+                        <div class="stability-row">
+                            <span id="stability-time-text-4">30: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-4"
+                                oninput="recordTimeAndCalculateStability(4)">
+                        </div>
+                        <div class="stability-row">
+                            <span id="stability-time-text-5">40: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-5"
+                                oninput="recordTimeAndCalculateStability(5)">
+                        </div>
+                        <div class="stability-row">
+                            <span id="stability-time-text-6">50: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-6"
+                                oninput="recordTimeAndCalculateStability(6)">
+                        </div>
+                        <div class="stability-row">
+                            <span id="stability-time-text-7">60: </span>
+                            <input type="number" placeholder="输入值" id="stability-value-7"
+                                oninput="recordTimeAndCalculateStability(7)">
+                        </div>
+                    </div>
+                </div>
+                <div class="results-section">
+                    <h1>稳定性结果</h1>
+                    <div class="results">
+                        <div class="result-item">最大值: <span id="stability-max">-</span></div>
+                        <div class="result-item">最小值: <span id="stability-min">-</span></div>
+                        <div class="result-item">平均值: <span id="stability-mean">-</span></div>
+                        <div class="result-item">极值差: <span id="stability-range">-</span></div>
+                    </div>
+                    <div class="chart-container">
+                        <canvas id="stabilityChart"></canvas>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+
+        <!-- 示值误差标签页 -->
+        <div id="error" class="tab-content">
+            <div class="content-container">
+                <div class="input-section" style="width: 100%;">
+                    <h1>示值误差测试</h1>
+                    <div class="error-container">
+                        <div class="error-point-container">
+                            <h3>-400m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-400" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-400" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-400" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-400">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-400">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-408m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-408" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-408" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-408" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-408">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-408">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-450m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-450" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-450" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-450" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-450">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-450">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-500m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-500" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-500" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-500" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-500">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-500">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-512m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-512" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-512" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-512" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-512">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-512">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-520m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-520" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-520" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-520" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-520">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-520">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-557m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-557" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-557" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-557" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-557">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-557">-</span></div>
+                        </div>
+
+                        <div class="error-point-container">
+                            <h3>-600m℃</h3>
+                            <div class="error-inputs">
+                                <input type="number" placeholder="测试1" class="error-input"
+                                    data-point="-600" data-index="0" oninput="calculateError()">
+                                <input type="number" placeholder="测试2" class="error-input"
+                                    data-point="-600" data-index="1" oninput="calculateError()">
+                                <input type="number" placeholder="测试3" class="error-input"
+                                    data-point="-600" data-index="2" oninput="calculateError()">
+                            </div>
+                            <div class="error-result">平均值: <span class="error-avg"
+                                    data-point="-600">-</span></div>
+                            <div class="error-result">示值误差: <span class="error-value"
+                                    data-point="-600">-</span></div>
+                        </div>
+
+                        <div class="chart-container">
+                            <canvas id="errorChart"></canvas>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- 导出/导入标签页 -->
+        <div id="data" class="tab-content">
+            <div class="content-container">
+                <div class="button-container">
+                    <button class="export-btn" onclick="exportData()">导出数据</button>
+                    <button class="import-btn"
+                        onclick="document.getElementById('importFile').click()">导入数据</button>
+                    <input type="file" id="importFile" accept=".json,.txt" onchange="importData()"
+                        style="display:none">
+                </div>
+            </div>
+            <div style="text-align: center; margin-top: 1rem; color: #666; font-size: 0.8rem;">Ver
+                1.0.01.250419a1</div>
+        </div>
+
+        <script src="./chart.js"></script>
+        <script>
+            const ctx = document.getElementById('lineChart').getContext('2d');
+            const stabilityCtx = document.getElementById('stabilityChart').getContext('2d');
+            const errorCtx = document.getElementById('errorChart').getContext('2d');
+            let chartInstance = null;
+            let stabilityChartInstance = null;
+            let errorChartInstance = null;
+
+            // 存储稳定性测试的时间记录
+            let stabilityTimes = [];
+            let lastStabilityTime = null;
+
+            // 标签页切换功能
+            document.querySelectorAll('.tab').forEach(tab => {
+                tab.addEventListener('click', () => {
+                    // 移除所有标签和内容的active类
+                    document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+                    document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
+
+                    // 添加当前标签和对应内容的active类
+                    tab.classList.add('active');
+                    const tabId = tab.getAttribute('data-tab');
+                    document.getElementById(tabId).classList.add('active');
+                });
+            });
+
+            // 重复性计算函数 --------------------------------------------------
+            function calculateRepeatability() {
+                const inputs = Array.from(document.querySelectorAll('#repeatability input[type="number"]'))
+                    .map(input => parseFloat(input.value))
+                    .filter(value => !isNaN(value));
+
+                if (inputs.length === 0) {
+                    document.getElementById('mean').textContent = '-';
+                    document.getElementById('variance').textContent = '-';
+                    document.getElementById('stdDev').textContent = '-';
+                    document.getElementById('range').textContent = '-';
+                    updateRepeatabilityChart([]);
+                    return;
+                }
+
+                // 计算最大值与最小值的差
+                const range = Math.max(...inputs) - Math.min(...inputs);
+
+                // 计算平均值
+                const mean = inputs.reduce((sum, val) => sum + val, 0) / inputs.length;
+
+                // 计算方差
+                const variance = inputs.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / inputs.length;
+
+                // 计算标准差
+                const stdDev = Math.sqrt(variance);
+
+                // 计算RSD
+                const rsd = (stdDev / mean) * 100;
+
+                // 更新结果显示
+                document.getElementById('range').textContent = range.toFixed(2);
+                document.getElementById('mean').textContent = mean.toFixed(2);
+                document.getElementById('variance').textContent = variance.toFixed(2);
+                document.getElementById('stdDev').textContent = stdDev.toFixed(2);
+                document.getElementById('rsd').textContent = rsd.toFixed(2) + '%';
+
+                // 更新折线图
+                updateRepeatabilityChart(inputs);
+            }
+
+            function updateRepeatabilityChart(data) {
+                if (chartInstance) {
+                    chartInstance.destroy(); // 销毁之前的图表实例
+                }
+
+                // 创建标签(1, 2, 3, ...)用于X轴
+                const labels = Array.from({ length: data.length }, (_, i) => i + 1);
+
+                chartInstance = new Chart(ctx, {
+                    type: 'line',
+                    data: {
+                        labels: labels,
+                        datasets: [{
+                            label: '重复性测试',
+                            data: data,
+                            borderColor: 'rgb(75, 192, 192)',
+                            backgroundColor: 'rgba(75, 192, 192, 0.2)',
+                            tension: 0.1,
+                            pointRadius: 5,
+                            pointHoverRadius: 7
+                        }]
+                    },
+                    options: {
+                        responsive: true,
+                        scales: {
+                            y: {
+                                beginAtZero: false
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: false
+                            }
+                        }
+                    }
+                });
+            }
+
+            // 稳定性测试函数 --------------------------------------------------
+            // 更新当前时间显示
+            function updateCurrentTime() {
+                const now = new Date();
+                const timeString = now.toLocaleTimeString();
+                const currentTimeElement = document.getElementById('current-time');
+                currentTimeElement.textContent = timeString;
+
+                // 检查是否需要显示警告(与上次稳定性测试时间间隔超过10分钟)
+                if (lastStabilityTime) {
+                    const maxTime = 599; // 10分钟 = 600秒
+                    let timeDiff = now - lastStabilityTime;
+
+                    // 检查所有输入框是否都有值
+                    const allFilled = Array.from(document.querySelectorAll('#stability input[type="number"]'))
+                        .every(input => input.value);
+
+                    if (allFilled) {
+                        // 重置为默认状态
+                        document.getElementById('countdown').textContent = '10:00';
+                        document.querySelector('.progress-container').style.setProperty('--progress-percent', '0%');
+
+                        // 停止警报铃声
+                        document.getElementById('alertSound').pause();
+                        document.getElementById('alertSound').currentTime = 0;
+                        timeDiff = 0;
+                    }
+
+
+                    const remainingTime = Math.max(0, maxTime - Math.floor(timeDiff / 1000));
+                    const progressPercent = Math.min(100, Math.max(0, (maxTime - remainingTime) / maxTime * 100));
+                    document.querySelector('.progress-container').style.setProperty('--progress-percent', progressPercent + '%');
+                    const minutes = Math.floor(remainingTime / 60);
+                    const seconds = remainingTime % 60;
+                    const countdownElement = document.getElementById('countdown');
+                    countdownElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
+
+                    if (remainingTime <= 0) {
+                        currentTimeElement.classList.add('warning');
+                        countdownElement.classList.add('warning');
+                    } else {
+                        currentTimeElement.classList.remove('warning');
+                        countdownElement.classList.remove('warning');
+                    }
+                    if (remainingTime <= 3) {
+                        document.getElementById('alertSound').play();
+                    } else {
+                        document.getElementById('alertSound').pause();
+                        document.getElementById('alertSound').currentTime = 0;
+                    }
+                }
+
+                // 每秒更新一次时间
+                setTimeout(updateCurrentTime, 1000);
+            }
+
+            // 初始化时启动时间更新
+            updateCurrentTime();
+
+            // 记录时间并计算稳定性
+            function recordTimeAndCalculateStability(index) {
+                const now = new Date();
+                const timeString = now.toLocaleTimeString();
+
+                // 更新时间显示
+                document.getElementById(`stability-time-text-${index}`).textContent = `${timeString} `;
+
+                // 记录最后一次测试时间
+                lastStabilityTime = now;
+                stabilityTimes[index - 1] = timeString;
+
+                // 计算稳定性
+                calculateStability();
+            }
+
+            function calculateStability() {
+                const values = [];
+                const times = [];
+
+                // 收集所有稳定性测试的值和时间
+                for (let i = 1; i <= 7; i++) {
+                    const valueInput = document.getElementById(`stability-value-${i}`);
+                    if (valueInput && valueInput.value) {
+                        values.push(parseFloat(valueInput.value));
+                        times.push(stabilityTimes[i - 1] || `测试${i}`);
+                    }
+                }
+
+                if (values.length === 0) {
+                    document.getElementById('stability-range').textContent = '-';
+                    document.getElementById('stability-max').textContent = '-';
+                    document.getElementById('stability-min').textContent = '-';
+                    document.getElementById('stability-mean').textContent = '-';
+                    return;
+                }
+
+                // 计算极值差
+                const max = Math.max(...values);
+                const min = Math.min(...values);
+                const range = max - min;
+                const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
+
+                // 更新显示
+                document.getElementById('stability-range').textContent = range.toFixed(2);
+                document.getElementById('stability-max').textContent = max.toFixed(2);
+                document.getElementById('stability-min').textContent = min.toFixed(2);
+                document.getElementById('stability-mean').textContent = mean.toFixed(2);
+
+                // 更新稳定性图表
+                updateStabilityChart(times, values);
+            }
+
+            function updateStabilityChart(times, values) {
+                if (!stabilityCtx || values.length === 0) return;
+
+                // 如果已存在图表实例,先销毁
+                if (stabilityChartInstance) {
+                    stabilityChartInstance.destroy();
+                }
+
+                // 创建新的图表
+                stabilityChartInstance = new Chart(stabilityCtx, {
+                    type: 'line',
+                    data: {
+                        labels: times,
+                        datasets: [{
+                            label: '稳定性测试',
+                            data: values,
+                            borderColor: 'rgb(75, 192, 192)',
+                            backgroundColor: 'rgba(75, 192, 192, 0.2)',
+                            tension: 0.1,
+                            pointRadius: 5,
+                            pointHoverRadius: 7
+                        }]
+                    },
+                    options: {
+                        responsive: true,
+                        scales: {
+                            y: {
+                                beginAtZero: false
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: false
+                            }
+                        }
+                    }
+                });
+            }
+
+            // 示值误差测试函数 ------------------------------------------------
+            function calculateError() {
+                const errorPoints = ['-400', '-408', '-450', '-500', '-512', '-520', '-557', '-600'];
+                const errorData = [];
+
+                errorPoints.forEach(point => {
+                    const inputs = document.querySelectorAll(`.error-input[data-point="${point}"]`);
+                    const values = [];
+
+                    inputs.forEach(input => {
+                        if (input.value) {
+                            values.push(parseFloat(input.value));
+                        }
+                    });
+
+                    if (values.length > 0) {
+                        // 计算平均值
+                        const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
+                        document.querySelector(`.error-avg[data-point="${point}"]`).textContent = avg.toFixed(2);
+
+                        // 计算示值误差 (测量值与标准值的差的绝对值)
+                        const error = Math.abs(avg - (-1 * parseFloat(point)));
+                        document.querySelector(`.error-value[data-point="${point}"]`).textContent = error.toFixed(2);
+
+                        // 收集数据用于图表
+                        errorData.push({
+                            point: (-1 * parseFloat(point)),
+                            avg: avg,
+                            error: error
+                        });
+                    }
+                });
+
+                // 更新误差图表
+                updateErrorChart(errorData);
+            }
+
+            function updateErrorChart(data) {
+                if (errorChartInstance) {
+                    errorChartInstance.destroy();
+                }
+
+                if (data.length === 0) return;
+
+                // 按照点位排序
+                data.sort((a, b) => a.point - b.point);
+
+                const labels = data.map(item => item.point);
+                const errorValues = data.map(item => item.error);
+
+                errorChartInstance = new Chart(errorCtx, {
+                    type: 'line',
+                    data: {
+                        labels: labels,
+                        datasets: [{
+                            label: '示值误差',
+                            data: errorValues,
+                            borderColor: 'rgb(75, 192, 192)',
+                            backgroundColor: 'rgba(75, 192, 192, 0.2)',
+                            tension: 0.1,
+                            pointRadius: 5,
+                            pointHoverRadius: 7
+                        }]
+                    },
+                    options: {
+                        responsive: true,
+                        scales: {
+                            y: {
+                                beginAtZero: false
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: false
+                            }
+                        }
+                    }
+                });
+            }
+
+        </script>
+</body>
+
+</html>
+
+<script>
+    function exportData() {
+        const data = {
+            repeatability: {
+                inputs: Array.from(document.querySelectorAll('#repeatability .input-group input')).map(input => input.value),
+                results: {
+                    range: document.getElementById('range').innerText,
+                    mean: document.getElementById('mean').innerText,
+                    variance: document.getElementById('variance').innerText,
+                    stdDev: document.getElementById('stdDev').innerText,
+                    rsd: document.getElementById('rsd').innerText
+                }
+            },
+            stability: {
+                inputs: Array.from(document.querySelectorAll('#stability .input-group input')).map(input => input.value),
+                results: {
+                    max: document.getElementById('stability-max').innerText,
+                    min: document.getElementById('stability-min').innerText,
+                    mean: document.getElementById('stability-mean').innerText,
+                    range: document.getElementById('stability-range').innerText
+                }
+            },
+            error: Array.from(document.querySelectorAll('.error-point-container')).map(container => ({
+                point: container.querySelector('h3').innerText,
+                inputs: Array.from(container.querySelectorAll('.error-input')).map(input => input.value),
+                avg: container.querySelector('.error-avg').innerText,
+                error: container.querySelector('.error-value').innerText
+            }))
+        };
+
+        const now = new Date();
+        const dateStr = now.toISOString().replace(/[-:]/g, '').replace('T', '_').substring(0, 15);
+        const projectName = document.title.replace(' ', '_');
+
+        const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
+        const url = URL.createObjectURL(blob);
+        const a = document.createElement('a');
+        a.href = url;
+        a.download = `${projectName}_data_${dateStr}.json`;
+        a.click();
+        URL.revokeObjectURL(url);
+    }
+
+    function importData() {
+        const fileInput = document.getElementById('importFile');
+        const file = fileInput.files[0];
+        if (!file) return;
+
+        const reader = new FileReader();
+        reader.onload = function (e) {
+            try {
+                const data = JSON.parse(e.target.result);
+                console.log(data);
+                console.log(data.error);
+
+
+                // 更新重复性数据
+                if (data.repeatability) {
+                    const inputs = document.querySelectorAll('#repeatability input[type="number"]');
+                    data.repeatability.inputs.forEach((value, index) => {
+                        if (inputs[index]) inputs[index].value = value;
+                    });
+                    calculateRepeatability();
+                }
+
+                // 更新稳定性数据
+                if (data.stability) {
+                    const inputs = document.querySelectorAll('#stability input[type="number"]');
+
+                    data.stability.inputs.forEach((value, index) => {
+                        if (inputs[index]) inputs[index].value = value;
+                    });
+                    calculateStability();
+                }
+
+                // 更新示值误差数据
+                if (data.error) {
+                    data.error.forEach(item => {
+                        // 将point转换为字符串形式以确保匹配
+                        const point = item.point.toString().replace(/m℃$/, '');
+                        const inputs = document.querySelectorAll(`.error-input[data-point="${point}"]`);
+                        item.inputs.forEach((value, index) => {
+                            if (inputs[index]) inputs[index].value = value;
+                        });
+                    });
+                    calculateError();
+                }
+            } catch (error) {
+                alert('导入失败!');
+                console.error(error);
+            }
+        };
+        reader.readAsText(file);
+    }
+</script>
+</body>
+
+</html>

+ 4 - 0
Desmos-Desktop-master/.gitignore

@@ -0,0 +1,4 @@
+dist/*
+node_modules/*
+package-lock.json
+src/js/calculator_m.js

+ 21 - 0
Desmos-Desktop-master/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 DingShizhe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 52 - 0
Desmos-Desktop-master/README.md

@@ -0,0 +1,52 @@
+# Desmos Desktop
+
+[Desmos](https://www.desmos.com/about) is an advanced graphing calculator implemented as a web application and a mobile application written in JavaScript. Here is a very simple cross-platform desktop version. **Plotting, saving, opening and exporting offlinely** are supportive. Examples are in 'examples' folder.
+
+[Desmos](https://www.desmos.com/about) 是一款优秀的、免费的在线数学函数计算器。它基于JavaScript,有在线版和手机版。这里是一个基于[Electron](http://electron.atom.io/)的**简单的离线桌面版**,可以离线绘制和保存以及输出截图。支持linux, Mac 和 Win平台(目前还没有在Mac上尝试,留下贫穷的眼泪...)。
+
+<img src="./res/app.png" width="600"/>
+
+## Install
+
+The executable binary files have been released  [here](https://github.com/DingShizhe/Desmos-Desktop/releases/tag/v1.0.0). There are only win and linux(ubuntu) versions. If you want Mac(or other platforms) version, try to build it.
+
+After installation, try to use it to open the `des` files in 'examples'.
+
+## Build
+```
+git clone https://github.com/DingShizhe/Desmos-Desktop.git
+cd Desmos-Desktop
+npm install -d
+npm run dist
+cd dist && ls   # WOW! Then move the package wherever you want. 
+```
+On linux, you need edit ./res/appimagekit-desmos.desktop properly and move it to ./local/share/applications.
+
+## Dependency
+- [Node.js](https://nodejs.org/en/)
+- [Electron](http://electron.atom.io/)
+- [Desmos API](https://www.desmos.com/api/)
+
+
+## Simple Functions (features) Now
+Now you can use this simple app to
+- Draw mathematical curves just like the web version
+- Export screenshot as `.png` file
+- Save or reload graph state as `.des` file (just json)
+- Fade out message effect.
+- Note when new a file or close file but current file hasnot been saved
+- Open the last opened file when launch
+- Use 'examples/getDesByUrl.py' to get `.des` file from desmos website
+  .
+
+## My Artworks (except the last one)
+
+Try open `.des` files in 'examples'.
+
+<img src="res/Cardioid.png" width="200"/> <img src="res/Folded Conic Section.png" width="200"/> <img src="res/Folded Conic Section2.png" width="200"/>
+
+<img src="res/Folded Conic Section3.png" width="200"/> <img src="res/Brain.png" width="200"/> <img src="res/Mobius2.png" width="200"/>
+
+<img src="res/Mobius.png" width="200"/> <img src="res/Astroid.png" width="200"/> <img src="res/Powerpuff Grils.png" width="200"/>
+
+Hope this helpful. Enjoy ;)

+ 185 - 0
Desmos-Desktop-master/examples/Astroid.des

@@ -0,0 +1,185 @@
+{
+    "version": 5,
+    "graph": {
+        "viewport": {
+            "xmin": -6.940063748235457,
+            "ymin": -7.134541429238188,
+            "xmax": 6.961266580431917,
+            "ymax": 6.766788899429186
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#c74440",
+                "latex": "N=26",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "40",
+                "sliderInterval": "1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "12",
+                "color": "#2d70b3",
+                "latex": "n=\\left[0,\\frac{1}{N},...,\\frac{N-1}{N}\\right]",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "13",
+                "color": "#388c46",
+                "latex": "a=5",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderPlayDirection": -1,
+                "sliderMin": "0",
+                "sliderInterval": "0.2",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "18",
+                "title": "Astroid 1  直线截距平方和为定值,椭圆长短半轴之和为定值",
+                "hidden": true,
+                "collapsed": true,
+                "memberIds": {
+                    "15": true,
+                    "17": true,
+                    "24": true,
+                    "25": true,
+                    "32": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "24",
+                "color": "#c74440",
+                "latex": "x=0\\left\\{-a<y<a\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "25",
+                "color": "#c74440",
+                "latex": "y=0\\left\\{-a<x<a\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "15",
+                "color": "#c74440",
+                "latex": "\\left(\\frac{x}{a\\cdot n}\\right)^2+\\left(\\frac{y}{a\\left(1-n\\right)}\\right)^2=1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "32",
+                "color": "#2d70b3",
+                "latex": "\\frac{\\left|x\\right|}{a\\sqrt{n}}+\\frac{\\left|y\\right|}{a\\sqrt{1-n}}=1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "17",
+                "color": "#000000",
+                "latex": "\\left(\\frac{x}{a}\\right)^{\\frac{2}{3}}+\\left(\\frac{y}{a}\\right)^{\\frac{2}{3}}=1",
+                "hidden": true,
+                "lineStyle": "DASHED",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "19",
+                "title": "Astroid 2  直线截距之和为定值,椭圆长短半轴之2/3方和为定值",
+                "hidden": true,
+                "collapsed": true,
+                "memberIds": {
+                    "14": true,
+                    "16": true,
+                    "36": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "36",
+                "color": "#c74440",
+                "latex": "\\left(\\frac{x}{a\\cdot n^{\\frac{3}{2}}}\\right)^2+\\left(\\frac{y}{a\\left(1-n\\right)^{\\frac{3}{2}}}\\right)^2=1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "16",
+                "color": "#2d70b3",
+                "latex": "\\frac{\\left|x\\right|}{na}+\\frac{\\left|y\\right|}{\\left(1-n\\right)a}=1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "14",
+                "color": "#000000",
+                "latex": "\\left(\\frac{\\left|x\\right|}{a}\\right)^{\\frac{1}{2}}+\\left(\\frac{\\left|y\\right|}{a}\\right)^{\\frac{1}{2}}=1",
+                "lineStyle": "DASHED",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "44",
+                "title": "Astroid 3 The end",
+                "hidden": true,
+                "collapsed": true,
+                "memberIds": {
+                    "37": true,
+                    "39": true,
+                    "41": true,
+                    "45": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "37",
+                "color": "#c74440",
+                "latex": "o=0.5",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "-2",
+                "sliderMax": "3",
+                "sliderInterval": "0.1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "39",
+                "color": "#c74440",
+                "latex": "\\left(\\frac{x}{a\\cdot n^o}\\right)^2+\\left(\\frac{y}{a\\left(1-n\\right)^o}\\right)^2=1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "41",
+                "color": "#2d70b3",
+                "latex": "\\frac{\\left|x\\right|}{an^{\\left(o-\\frac{1}{2}\\right)}}+\\frac{\\left|y\\right|}{a\\left(1-n\\right)^{\\left(o-\\frac{1}{2}\\right)}}=1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "45",
+                "color": "#000000",
+                "latex": "\\left(\\frac{\\left|x\\right|}{a}\\right)^{\\frac{1}{o+\\frac{1}{2}}}+\\left(\\frac{\\left|y\\right|}{a}\\right)^{\\frac{1}{o+\\frac{1}{2}}}=1",
+                "lineStyle": "DASHED",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "34",
+                "color": "#388c46",
+                "latex": "\\left(\\frac{x}{a\\cdot\\sqrt{n}}\\right)^2+\\left(\\frac{y}{a\\sqrt{1-n}}\\right)^2=1",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 77 - 0
Desmos-Desktop-master/examples/Cardioid.des

@@ -0,0 +1,77 @@
+{
+    "version": 5,
+    "graph": {
+        "viewport": {
+            "xmin": -4.960827633702445,
+            "ymin": -4.710151114605519,
+            "xmax": 3.897303854186827,
+            "ymax": 4.147980373283754
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#c74440",
+                "latex": "N=20",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "40",
+                "sliderInterval": "1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "2",
+                "color": "#2d70b3",
+                "latex": "s=\\left[0,\\frac{1}{N},...,\\frac{N-1}{N}\\right]\\cdot2\\pi",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "3",
+                "color": "#c74440",
+                "latex": "p=\\left(\\cos\\left(s\\right),\\sin\\left(s\\right)\\right)",
+                "hidden": true,
+                "points": true,
+                "lines": false,
+                "dragMode": "NONE",
+                "labelSize": "small",
+                "labelOrientation": "below",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "10",
+                "color": "#2d70b3",
+                "latex": "x^2+y^2=1",
+                "hidden": true,
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "4",
+                "color": "#c74440",
+                "latex": "q=\\left(b,b\\right)",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "8",
+                "color": "#388c46",
+                "latex": "b=0.98",
+                "hidden": true,
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "6",
+                "color": "#c74440",
+                "latex": "\\left(x-\\cos\\left(s\\right)\\right)^2+\\left(y-\\sin\\left(s\\right)\\right)^2=\\left(b-\\cos\\left(s\\right)\\right)^2+\\left(b-\\sin\\left(s\\right)\\right)^2",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 97 - 0
Desmos-Desktop-master/examples/Folded Conic Section 2.des

@@ -0,0 +1,97 @@
+{
+    "version": 5,
+    "graph": {
+        "viewport": {
+            "xmin": -11.903294186048104,
+            "ymin": -12.724984936863532,
+            "xmax": 18.669319366164654,
+            "ymax": 17.847628615349226
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "41",
+                "color": "#c74440",
+                "latex": "N=37",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "40",
+                "sliderInterval": "2",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#2d70b3",
+                "latex": "x+y=0",
+                "lineStyle": "DASHED",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "37",
+                "color": "#2d70b3",
+                "latex": "\\left(a,b\\right)",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "39",
+                "color": "#6042a6",
+                "latex": "a=5.7",
+                "hidden": true,
+                "sliderMin": "-22.4",
+                "sliderMax": "80.6",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "40",
+                "color": "#000000",
+                "latex": "b=4.9",
+                "hidden": true,
+                "sliderMin": "-30.3",
+                "sliderMax": "63.5",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "38",
+                "color": "#388c46",
+                "latex": "n=\\left[0,\\frac{1}{N},...,\\frac{N-1}{N}\\right]\\pi",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "44",
+                "color": "#6042a6",
+                "latex": "t=\\tan\\left(n\\right)",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "46",
+                "color": "#c74440",
+                "latex": "\\left(x-\\frac{a-b+2at}{2+2t}\\right)+t\\left(y-\\frac{1}{2}\\left(b+\\frac{b-ta}{1+t}\\right)\\right)=0",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "45",
+                "color": "#c74440",
+                "latex": "\\left(x-\\frac{at-b}{1+1t}\\right)+t\\left(y-\\frac{b-ta}{1+t}\\right)=0",
+                "hidden": true,
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "47",
+                "color": "#2d70b3",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 91 - 0
Desmos-Desktop-master/examples/Folded Conic Section.des

@@ -0,0 +1,91 @@
+{
+    "version": 5,
+    "graph": {
+        "viewport": {
+            "xmin": -1.91158261371499,
+            "ymin": -1.8588137264694713,
+            "xmax": 2.4818758775250513,
+            "ymax": 2.53464476477057
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#c74440",
+                "latex": "N=35",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "100",
+                "sliderInterval": "1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "34",
+                "color": "#000000",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "26",
+                "color": "#2d70b3",
+                "latex": "n=\\left[0,\\frac{1}{N},...,\\frac{N-1}{N}\\right]\\cdot2\\pi",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "30",
+                "color": "#2d70b3",
+                "latex": "\\left(a,b\\right)",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "32",
+                "color": "#388c46",
+                "latex": "a=1.57",
+                "hidden": true,
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "33",
+                "color": "#6042a6",
+                "latex": "b=1.18",
+                "hidden": true,
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "27",
+                "color": "#2d70b3",
+                "latex": "x^2+y^2=1",
+                "lineStyle": "DASHED",
+                "style": "SOLID"
+            },
+            {
+                "type": "text",
+                "id": "36",
+                "text": "(x - a)^2 + (y - b)^2 - (x - cos)^2 - (y - sin)^2 // Simplify"
+            },
+            {
+                "type": "expression",
+                "id": "35",
+                "color": "#c74440",
+                "latex": "a^2+b^2-1-2ax-2by+2x\\cos\\left(n\\right)+2y\\sin\\left(n\\right)=0",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "37",
+                "color": "#c74440",
+                "latex": "\\left(y-\\sin\\left(n\\right)\\right)\\left(b-\\sin\\left(n\\right)\\right)+\\left(x-\\cos\\left(n\\right)\\right)\\left(a-\\cos\\left(n\\right)\\right)=0",
+                "hidden": true,
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 218 - 0
Desmos-Desktop-master/examples/Mobius f(z)=(z-(1+i))_(z-(-1-i)) Elliptic&Hyperbolic Type.des

@@ -0,0 +1,218 @@
+{
+    "version": 5,
+    "graph": {
+        "showXAxis": false,
+        "showYAxis": false,
+        "viewport": {
+            "xmin": -4.73562252672692,
+            "ymin": -4.73562252672692,
+            "xmax": 4.73562252672692,
+            "ymax": 4.73562252672692
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#c74440",
+                "latex": "N=7",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "20",
+                "sliderInterval": "1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "8",
+                "color": "#388c46",
+                "latex": "n=\\left[0,...,N-1\\right]",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "19",
+                "title": "xxx Axes",
+                "collapsed": true,
+                "memberIds": {
+                    "20": true,
+                    "21": true,
+                    "23": true,
+                    "24": true,
+                    "27": true,
+                    "30": true,
+                    "31": true,
+                    "33": true,
+                    "35": true,
+                    "37": true,
+                    "38": true,
+                    "40": true,
+                    "41": true,
+                    "42": true,
+                    "43": true,
+                    "44": true,
+                    "45": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "21",
+                "color": "#6042a6",
+                "latex": "t=\\arctan\\left(\\frac{2\\left(x-y\\right)}{-2+x^2+y^2}\\right)",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "35",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t+2\\pi+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\ \\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "37",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t+2\\pi+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\ \\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "20",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\ \\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "38",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\ \\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "30",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t-2\\pi+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "40",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t-2\\pi+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "31",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t-4\\pi+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "41",
+                "color": "#6042a6",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{2t-4\\pi+\\frac{2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "23",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "42",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "24",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t+2\\pi+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "43",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t+2\\pi+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "27",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t-2\\pi+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "44",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t-2\\pi+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "33",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t-4\\pi+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2>0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "45",
+                "color": "#388c46",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-2t-4\\pi+\\frac{\\ 2n\\ \\pi}{N}}\\left\\{-2+x^2+y^2<0\\right\\}",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "17",
+                "title": "Circles Axes",
+                "collapsed": true,
+                "memberIds": {
+                    "7": true,
+                    "9": true,
+                    "11": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "9",
+                "color": "#2d70b3",
+                "latex": "\\ \\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{\\frac{2\\pi n}{N}}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "7",
+                "color": "#2d70b3",
+                "latex": "\\frac{2-2x+x^2-2y+y^2}{2+2x+x^2+2y+y^2}=e^{-\\frac{2\\pi\\left(n+1\\right)}{N}}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "11",
+                "color": "#c74440",
+                "latex": "2(x-y)\\cos\\left(\\frac{\\ n\\ \\pi}{N}\\right)+(-2+x^2+y^2)\\sin\\left(\\frac{n\\ \\pi}{N}\\right)=0",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "34",
+                "color": "#000000",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 121 - 0
Desmos-Desktop-master/examples/Mobius r=+-/theta AND r=const&/theta=const.des

@@ -0,0 +1,121 @@
+{
+    "version": 5,
+    "graph": {
+        "showXAxis": false,
+        "showYAxis": false,
+        "viewport": {
+            "xmin": -3.765115120931414,
+            "ymin": -3.405975341276975,
+            "xmax": 3.666638759208322,
+            "ymax": 4.025778538862761
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#c74440",
+                "latex": "N=6",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "20",
+                "sliderInterval": "1",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "4",
+                "color": "#6042a6",
+                "latex": "n=\\left[0,...,N-1\\right]",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "9",
+                "title": "Circles Axes",
+                "collapsed": true,
+                "memberIds": {
+                    "2": true,
+                    "3": true,
+                    "5": true,
+                    "6": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "3",
+                "color": "#c74440",
+                "latex": "y\\cdot\\cos\\left(\\frac{\\ n\\ \\pi}{N}\\right)=x\\cdot\\sin\\left(\\frac{\\ n\\ \\pi}{N}\\right)",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "5",
+                "color": "#c74440",
+                "latex": "y=0",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "2",
+                "color": "#2d70b3",
+                "latex": "x^2+y^2=e^{\\frac{2\\pi n}{N}}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "6",
+                "color": "#2d70b3",
+                "latex": "x^2+y^2=e^{-\\frac{2\\pi\\left(n+1\\right)}{N}}",
+                "style": "SOLID"
+            },
+            {
+                "type": "folder",
+                "id": "11",
+                "title": "xxx Axes",
+                "memberIds": {
+                    "7": true,
+                    "12": true,
+                    "13": true,
+                    "14": true
+                }
+            },
+            {
+                "type": "expression",
+                "id": "7",
+                "color": "#388c46",
+                "latex": "r=e^{\\theta-\\frac{\\ \\left(n+1\\right)\\ \\pi}{N}-4\\pi}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "12",
+                "color": "#388c46",
+                "latex": "r=e^{\\theta+\\frac{\\ n\\ \\pi}{N}-4\\pi}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "13",
+                "color": "#6042a6",
+                "latex": "r=e^{-\\theta-\\frac{\\ \\left(n+1\\right)\\ \\pi}{N}+8\\pi}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "14",
+                "color": "#6042a6",
+                "latex": "r=e^{-\\theta+\\frac{\\ n\\ \\pi}{N}+4\\pi}",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "15",
+                "color": "#c74440",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 68 - 0
Desmos-Desktop-master/examples/Nephroid.des

@@ -0,0 +1,68 @@
+{
+    "version": 5,
+    "graph": {
+        "viewport": {
+            "xmin": -2.8038124607306005,
+            "ymin": -2.7595983707232787,
+            "xmax": 2.6501307656408577,
+            "ymax": 2.6943448556481804
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "1",
+                "color": "#c74440",
+                "latex": "N=40",
+                "sliderHardMin": true,
+                "sliderHardMax": true,
+                "sliderMin": "1",
+                "sliderMax": "90",
+                "sliderInterval": "3",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "11",
+                "color": "#c74440",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "2",
+                "color": "#2d70b3",
+                "latex": "s=\\left[0,\\frac{1}{N},...,\\frac{N-1}{N}\\right]\\cdot2\\pi",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "3",
+                "color": "#c74440",
+                "latex": "p=\\left(\\cos\\left(s\\right),\\sin\\left(s\\right)\\right)",
+                "hidden": true,
+                "points": true,
+                "lines": false,
+                "dragMode": "NONE",
+                "labelSize": "small",
+                "labelOrientation": "below",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "10",
+                "color": "#2d70b3",
+                "latex": "x^2+y^2=1",
+                "hidden": true,
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "6",
+                "color": "#c74440",
+                "latex": "\\left(x-\\cos\\left(s\\right)\\right)^2+\\left(y-\\sin\\left(s\\right)\\right)^2=\\cos\\left(s\\right)^2",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 17 - 0
Desmos-Desktop-master/examples/README.md

@@ -0,0 +1,17 @@
+# Desmos Files and a crawler
+
+Files in this directory are all 'desmos-file' which can be openned by the application, except a python script.
+
+## Usage of the python script
+
+The python script is a little crawler can be used to download desmos file from the web if given the url as its first argv.
+
+For example, run this command in terminal.
+
+```
+python3 getDesByUrl.py https://www.desmos.com/calculator/4v3pgcuta0
+```
+
+Then you will a the  `.des` in current folder if the Internet is accessable.
+
+Open it and enjoy. :)

+ 43 - 0
Desmos-Desktop-master/examples/getDesByUrl.py

@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+import requests
+import re
+import html
+import json
+
+import os, sys
+
+url = sys.argv[1]
+
+headers = {
+    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0'
+}
+
+def get_desmos_html(url):
+    n = os.path.basename(url)
+    r = requests.get(url, headers=headers)
+    if r.status_code is not 200:
+        return None
+    return (r.text, n)
+
+def html_2_desmos_file(data):
+    text, name = data
+    result = re.findall(r'data\-load\-data="(.*?)"', text)
+    if not len(result) == 1:
+        return
+    res = html.unescape(result[0])
+
+    res = json.loads(res)
+
+    output = res['graph']['state']
+
+    with open(name+'.des', 'w') as fo:
+        json.dump(output, fo, ensure_ascii=False, indent=4)
+
+
+# for f_n in os.listdir('.'):
+#     if f_n.endswith('html'):
+#         get_desmos_file(f_n)
+
+text = get_desmos_html(sys.argv[1])
+html_2_desmos_file(text)

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 15 - 0
Desmos-Desktop-master/examples/hfpbsj2rdd.des


+ 36 - 0
Desmos-Desktop-master/examples/tri.des

@@ -0,0 +1,36 @@
+{
+    "version": 5,
+    "graph": {
+        "viewport": {
+            "xmin": -3.093534390680069,
+            "ymin": -2.327068921803766,
+            "xmax": 2.855564952616557,
+            "ymax": 2.6282480586158425
+        }
+    },
+    "expressions": {
+        "list": [
+            {
+                "type": "expression",
+                "id": "graph1",
+                "color": "#2d70b3",
+                "latex": "y=\\sin x",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "2",
+                "color": "#388c46",
+                "latex": "y=\\cos x",
+                "style": "SOLID"
+            },
+            {
+                "type": "expression",
+                "id": "3",
+                "color": "#fa7e19",
+                "latex": "y=\\tan x",
+                "style": "SOLID"
+            }
+        ]
+    }
+}

+ 26 - 0
Desmos-Desktop-master/index.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>Desmos</title>
+
+        <link rel="stylesheet" href="./src/css/style.css" type="text/css" />
+    </head>
+
+
+    <body>
+        <div id="calculator"> </div>
+
+        <div id="container"> </div>
+
+    </body>
+    <script src="./src/js/calculator.js"></script>
+
+
+    <script>
+        var elt = document.getElementById('calculator');
+        var calculator = Desmos.GraphingCalculator(elt);
+        calculator.setExpression({id:'graph1', latex:'y= x^2'});
+    </script>
+    <script src="./renderer.js"></script>
+</html>

+ 211 - 0
Desmos-Desktop-master/main.js

@@ -0,0 +1,211 @@
+const {app, BrowserWindow, Menu, ipcMain} = require('electron');
+const Store = require('electron-store');
+const path = require('path');
+const url = require('url');
+
+let safeExit = false;
+
+const store = new Store({
+    defaults: {
+        // 1000x600 is the default size of our window
+        windowSize: [ 1000, 600 ],
+        windowPos: [ 200, 200 ], 
+        filePath: null
+    }
+});
+
+
+function sendReq(msg, data=null) {
+    win.webContents.send('mainprocess-request', {msg: msg, data: data});
+}
+
+
+// Window menu template
+const menuTemplate = [
+    {
+        label: 'File',
+        submenu : [
+            {
+                label: 'New', accelerator: 'CmdOrCtrl+N',
+                click () {  sendReq('NewFile'); }
+            },{
+                label: 'Open', accelerator: 'CmdOrCtrl+O',
+                click () {  sendReq('OpenFile');    }
+            },{
+                label: 'Save', accelerator: 'CmdOrCtrl+S',
+                click () {  sendReq('SaveFile');    }
+            },{
+                label: 'Save As', accelerator: 'CmdOrCtrl+Shift+S',
+                click () {  sendReq('SaveAsFile');  }
+            },{type: 'separator'},{
+                label: 'Export',
+                accelerator: 'CmdOrCtrl+E',
+                click () {  sendReq('ExportImage'); }
+            }
+        ]
+    },{
+        label: 'Edit',
+        submenu:[
+            {
+                label: 'Undo', accelerator: 'CmdOrCtrl+Z',
+                click () {  sendReq('Undo');    }
+            },{
+                label: 'Redo', accelerator: 'CmdOrCtrl+Shift+Z',
+                click () {  sendReq('Redo');    }
+            },{
+                label: 'Clear', accelerator: 'CmdOrCtrl+Shift+C',
+                click () {  sendReq('Clear');    }
+            }
+        ]
+    },{
+        label: 'View',
+        submenu: [
+            // {role: 'reload'},
+            // {role: 'forcereload'},
+            // {role: 'toggledevtools'},
+            // {type: 'separator'},
+            {role: 'resetzoom'},
+            {role: 'zoomin'},
+            {role: 'zoomout'},
+            {type: 'separator'},
+            {role: 'togglefullscreen'}
+        ]
+    },{
+        role: 'help',
+        submenu:[
+            {
+                label: 'Learn More',
+                accelerator: 'CmdOrCtrl+H',
+                click () {
+                    createInfoWindow();
+                }
+            }
+        ]
+    }
+];
+
+// add mac menu (which is a little bit different from Linux/Win)
+if (process.platform === 'darwin') {
+  menuTemplate.unshift({
+    label: app.getName(),
+    submenu: [
+      {role: 'about'},
+      {type: 'separator'},
+      {role: 'services', submenu: []},
+      {type: 'separator'},
+      {role: 'hide'},
+      {role: 'hideothers'},
+      {role: 'unhide'},
+      {type: 'separator'},
+      {role: 'quit'}
+    ]
+  })
+}
+
+
+// Init win
+let win;
+let infoWin;
+
+
+function createWindow() {
+    // Create browser window
+
+    let pos = store.get('windowPos');
+    let x = pos[0];
+    let y = pos[1];
+
+    let shape = store.get('windowSize');
+    let width = shape[0];
+    let height = shape[1];
+
+    win = new BrowserWindow({width: width, height: height, x: x, y: y, icon: path.join(__dirname, '/res/icons/icon.png')});
+
+    win.loadURL(url.format({
+        pathname: path.join(__dirname, 'index.html'),
+        protocol: 'file',
+        slashes: true
+    }));
+
+    // Open devtool
+    // win.webContents.openDevTools();
+
+    const menu = Menu.buildFromTemplate(menuTemplate);
+
+    Menu.setApplicationMenu(menu);
+
+    win.on('resize', () => {
+        let size = win.getSize();
+        store.set('windowSize', size);
+    });
+
+    win.on('move', () => {
+        let pos = win.getPosition();
+        store.set('windowPos', pos);
+    })
+    
+    win.on('close', (e) => {
+        if(!safeExit) {
+            e.preventDefault();
+            sendReq('Exitting');
+        }
+    });
+}
+
+function createInfoWindow() {
+    infoWin = new BrowserWindow({
+        width: 400, height: 450,
+        resizable: false,
+        parent: win,
+        icon: path.join(__dirname, '/res/icons/icon.png')
+    });
+    infoWin.loadURL(url.format({
+        pathname: path.join(__dirname, '/src/info.html'),
+        protocol: 'file',
+        slashes: true
+    }));
+
+    // infoWin.webContents.openDevTools();
+
+    infoWin.on('close', (e) => {
+        infoWin = null;
+    });
+}
+
+
+// Run create window
+app.on('ready', createWindow)
+
+
+// Quit when all windows are closed
+app.on('window-all-closed', () => {
+    if(process.platform != 'darwin'){
+        app.quit();
+    }
+});
+
+
+ipcMain.on('renderer-response', (event, arg) => {
+    switch(arg.msg) {
+      case 'Exit':
+        // https://github.com/sindresorhus/electron-store
+        safeExit=true;
+        app.quit();
+        break;
+    }
+});
+
+
+ipcMain.on('renderer-request', (event, arg) => {
+    switch(arg.msg) {
+        case 'TitleChanged':
+            store.set('filePath', arg.data);
+            // console.log(arg.data);
+            break;
+        case 'ToInit':
+            sendReq("Init", store.get('filePath'));
+            break;
+        default:
+            break;
+    }
+});

+ 37 - 0
Desmos-Desktop-master/package.json

@@ -0,0 +1,37 @@
+{
+  "name": "desmos",
+  "version": "1.0.0",
+  "description": "desmos offline for linux",
+  "main": "main.js",
+  "scripts": {
+    "start": "electron .",
+    "pack": "electron-builder --dir",
+    "dist": "electron-builder"
+  },
+  "keywords": [
+    "desmos",
+    "math",
+    "graphics"
+  ],
+  "author": "DingShizhe",
+  "license": "MIT",
+  "dependencies": {
+    "electron-store": "^2.0.0"
+  },
+  "build": {
+    "linux": {
+      "category": "Math",
+      "icon": "res/icons/icon.png",
+      "target": [
+        "AppImage"
+      ]
+    },
+    "win": {
+      "icon": "res/icons/icon.png"
+    }
+  },
+  "devDependencies": {
+    "electron": "^3.0.6",
+    "electron-builder": "^20.28.4"
+  }
+}

+ 239 - 0
Desmos-Desktop-master/renderer.js

@@ -0,0 +1,239 @@
+const { ipcRenderer } = require('electron');
+const remote = require("electron").remote;
+var dialog = remote.dialog;
+var fs = require('fs');
+
+let StateData = {
+    file_path: '',
+    last_state: null
+}
+
+
+function showAlert(msg, speed=0.02) {
+    var div = '<div class="floating" id="message">'+msg+'</div>';
+    document.getElementById("container").innerHTML = div;
+    var s = document.getElementById('message').style;
+    s.opacity = 1;
+    (function fade(){(s.opacity-=speed)<0?s.display="none":setTimeout(fade,40)})();
+}
+
+
+function set_caculator_state(des_file_data) {
+    state = JSON.parse(des_file_data);
+    try {
+        calculator.setState(state);
+    }
+    catch(err) {
+        calculator.setBlank();
+    }
+}
+
+
+function saveText(text, file){
+    fs.writeFileSync(file, text);
+    showAlert('Saved successfully. ;)');
+}
+
+function setTitle() {
+    if (StateData.file_path)
+        document.title = "Desmos - " + StateData.file_path;
+    else
+        document.title = "Desmos - * Untitled";
+    ipcRenderer.send('renderer-request', {msg: 'TitleChanged', data: StateData.file_path});
+}
+
+setInterval( function(){
+    if (isSaved()) return;
+    else if (StateData.file_path)
+        document.title = "Desmos - * "+StateData.file_path;
+}, 1000);
+
+
+function newFile() {
+    var canceled = !askSaveIfNeed();
+    if (canceled) return;
+
+    calculator.setBlank();
+    StateData.file_path = '';
+    setTitle();
+}
+
+
+function openFile(filePath=null, init=false) {
+
+    if (!init) {
+        if (canceled) return;
+        var canceled = !askSaveIfNeed();
+    }
+
+    if (!filePath && init) { return; }
+    if (!filePath) {
+        filePaths = dialog.showOpenDialog({filters: [ {name: 'des', extensions: ['des'] }]});
+        if (!filePaths) return;
+        filePath = filePaths[0];
+    }
+
+    fs.readFile(filePath, (err, data) => {
+        if(err){
+            showAlert("Error on openning :( " + err.message);
+            calculator.setBlank();
+        }
+        set_caculator_state(data);
+        StateData.file_path = filePath;
+        StateData.last_state = data;
+        setTitle();
+    });
+}
+
+
+function saveFile() {
+    if(!StateData.file_path){
+        const file = dialog.showSaveDialog(remote.getCurrentWindow(), {
+            filters: [
+                { name: "Desmos Files", extensions: ['des'] }]
+        });
+        if(file) StateData.file_path=file;
+    }
+    if(StateData.file_path){
+        var state = calculator.getState();
+        StateData.last_state = state;
+        var state_content = JSON.stringify(state, null, 4);
+        saveText(state_content, StateData.file_path);
+        setTitle();
+    }
+}
+
+
+function saveAsFile() {
+    const file = dialog.showSaveDialog(remote.getCurrentWindow(), {
+        filters: [
+        { name: "Desmos Files", extensions: ['des'] }]
+    });
+    if(file) StateData.file_path=file;
+    if(StateData.file_path){
+        var state = calculator.getState();
+        StateData.last_state = state;
+        var state_content = JSON.stringify(state, null, 4);
+        saveText(state_content, StateData.file_path);
+        setTitle();
+    }
+}
+
+
+function exportImage() {
+    var image = calculator.screenshot({
+        width: remote.width,
+        height: remote.height,
+        targetPixelRatio: 2
+    });
+    
+    var image_data = image.replace(/^data:image\/png;base64,/, "");
+
+    dialog.showSaveDialog({filters: [ {name: 'png', extensions: ['png'] }]},
+        (fileName) => {
+        if (fileName === undefined){
+            console.log("You didn't open the file.");
+            return;
+        }
+        // fileName is a string that contains the path and filename created in the save file dialog.
+        fs.writeFile(fileName, image_data, 'base64', (err) => {
+            if(err){
+                alert("An error ocurred creating the file. :( "+ err.message)
+            }
+            showAlert("Succesfully exported. ;)");
+        });
+    }); 
+}
+
+
+function isStateNull() {
+    if (StateData.last_state == null && calculator.getState().expressions.list[0].latex === undefined)
+        return true;
+    else return false;
+}
+
+
+function isSaved() {
+    if (isStateNull()) return true;
+    if (StateData.file_path == "" || StateData.last_state == null)
+        return false;
+    else {
+        if (JSON.stringify(calculator.getState().extensions) == JSON.stringify(StateData.last_state.extensions))
+            return true;
+        else
+            return false;
+    }
+}
+
+
+function askSaveIfNeed(){
+    if(isSaved()) return true;
+    const response = dialog.showMessageBox(remote.getCurrentWindow(), {
+        message: 'Do you want to save the current document?',
+        type: 'question',
+        buttons: [ 'Yes', 'No', 'Cancel' ]
+    });
+    // Yes to save
+    if (response == 0)
+        saveFile();
+
+    if (response == 2)
+        return false;
+    else
+        return true;
+}
+
+
+
+function exitApp() {
+    var exit = askSaveIfNeed();    
+    if (exit) {
+        showAlert('Exiting...', 0);
+        setTimeout(()=>{
+            ipcRenderer.sendSync('renderer-response', {msg: 'Exit'});
+        }, 600);
+    }
+}
+
+
+ipcRenderer.on('mainprocess-request', (event, arg) => {
+    console.log(arg);
+    switch (arg.msg) {
+        case 'NewFile':     newFile();      break;
+        case 'Init':        openFile(arg.data, true);     break;
+        case 'OpenFile':    openFile();     break;
+        case 'SaveFile':    saveFile();     break;
+        case 'SaveAsFile':  saveAsFile();   break;
+        case 'ExportImage': exportImage();  break;
+        case 'Undo':        calculator.undo();      break;
+        case 'Redo':        calculator.redo();      break;
+        case 'Clear':       calculator.setBlank();  break;
+        case 'Exitting':    exitApp();      break;
+        default:            break;
+    }
+});
+
+
+document.addEventListener("keydown", event => {
+    switch (event.key) {
+        case "Escape":
+            if (remote.getCurrentWindow().isFullScreen()) {
+                remote.getCurrentWindow().setFullScreen(false);
+            } else {
+                remote.getCurrentWindow().close();
+            }
+            break;
+    }
+});
+
+ipcRenderer.send('renderer-request', {msg: 'ToInit'})
+
+
+// document.ondragover = document.ondrop = (ev) => {
+    // ev.preventDefault()
+// }
+  
+// document.body.ondrop = (ev) => {
+//     console.log(ev.dataTransfer.files[0].path)
+//     ev.preventDefault()
+// }

BIN
Desmos-Desktop-master/res/Astroid.png


BIN
Desmos-Desktop-master/res/Brain.png


BIN
Desmos-Desktop-master/res/Cardioid.png


BIN
Desmos-Desktop-master/res/Folded Conic Section.png


BIN
Desmos-Desktop-master/res/Folded Conic Section2.png


BIN
Desmos-Desktop-master/res/Folded Conic Section3.png


BIN
Desmos-Desktop-master/res/Mobius.png


BIN
Desmos-Desktop-master/res/Mobius2.png


BIN
Desmos-Desktop-master/res/Powerpuff Grils.png


BIN
Desmos-Desktop-master/res/app.png


+ 14 - 0
Desmos-Desktop-master/res/appimagekit-desmos.desktop

@@ -0,0 +1,14 @@
+[Desktop Entry]
+Name=desmos
+Comment=desmos offline for linux
+Exec=path to desmos exec ot appimage
+Terminal=false
+Type=Application
+Icon=path to icon file
+StartupWMClass=desmos
+X-AppImage-Version=1.0.0
+Categories=Math;
+X-AppImage-BuildId=1C4z6CES0KAjHe9ZodHFgnvJZye
+X-Desktop-File-Install-Version=0.23
+X-AppImage-Comment=Generated by /tmp/.mount_desmosayWxTd/AppRun
+TryExec=path to desmos exec ot appimage

BIN
Desmos-Desktop-master/res/icons/icon.png


BIN
Desmos-Desktop-master/res/icons/icon_bak.png


+ 33 - 0
Desmos-Desktop-master/src/css/style.css

@@ -0,0 +1,33 @@
+body {
+    height:100%;
+    margin: 0;
+    font-size: 24px;
+    color: #fff;
+    line-height: 50px;
+    text-align: center;
+}
+
+#calculator {
+    position: absolute;
+    top: 0px;
+    left: 0;
+    bottom: 0;
+    right: 0;
+}
+
+.floating {
+    float: left;
+    position: absolute;
+    right: 20px;
+    bottom: 20px;
+    height: 50px;
+
+    width: 200px;
+
+    background-color: grey;
+    border-radius: 5px;
+    font-style: normal;
+    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+    font-size: 15px;
+    color: #FFFFFF;
+}

+ 70 - 0
Desmos-Desktop-master/src/info.html

@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <title>Info</title>
+
+    <style>
+            * {
+                box-sizing: border-box;
+            }
+
+            a {
+                color: darkcyan
+            }
+
+            p {
+                margin-left: 0px;
+                margin-bottom: 10px;
+                margin-top: 20px;
+                font-family: Arial, Helvetica, sans-serif;
+                color: #333333;
+                font-size: 25px;
+                left: 50px;
+            }
+
+            body {
+                margin-left: 30px;
+                margin-right: 35px;
+                margin-top: 30px;
+                color: #777777;
+                font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif
+            }
+
+            </style>
+</head>
+<body>
+    <p> 
+        
+        About <a href="http://www.desmos.com">Desmos</a> </p>
+
+        Desmos is an advanced graphing calculator implemented as a web application and a mobile application written in JavaScript.
+
+        
+        <p> About <a href="https://electronjs.org/">Electron</a>  </p>
+        
+        Electron is a popular platform which you can build cross desktop apps with JavaScript, HTML, and CSS.
+        
+        <p> About this application </p>
+        
+        This application is based on Desmos API and Electron build by <a href="https://github.com/DingShizhe">DingShizhe</a>. This little project has been uploaded as a <a href="https://github.com/DingShizhe/Desmos-Desktop"> Github rep </a>. Would you give me a star if you like it? ;)
+        
+    </body>
+
+    <script>
+        remote = require('electron');
+
+        const shell = require('electron').shell;
+
+        var myHref = document.getElementsByTagName("a");
+        for (var i = 0,mylength = myHref.length; i<mylength; i++) {
+            myHref[i].addEventListener("click", function(e){
+                e.preventDefault();
+                console.log('open', this.href);
+                shell.openExternal(this.href);
+            },"false");
+        }
+    </script>
+
+</html>

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 7 - 0
Desmos-Desktop-master/src/js/calculator.js


+ 765 - 0
FuncGen/data-table.js

@@ -0,0 +1,765 @@
+// Excel风格数据表格组件
+
+document.addEventListener('DOMContentLoaded', function() {
+    // 初始化数据表格
+    initDataTables();
+});
+
+/**
+ * 初始化所有数据表格
+ */
+function initDataTables() {
+    // 初始化拟合数据表格
+    initFitDataTable();
+
+    // 初始化回归分析表格
+    initRegressionTable();
+}
+
+/**
+ * 初始化拟合数据表格
+ */
+function initFitDataTable() {
+    const tableContainer = document.querySelector('#fit-tab .table-container');
+    if (!tableContainer) return;
+
+    // 清空现有内容
+    tableContainer.innerHTML = '';
+
+    // 创建Excel风格表格
+    const excelTable = createExcelTable('fit-excel-table', 2); // X和Y两列
+    tableContainer.appendChild(excelTable);
+
+    // 添加示例数据
+    const fitTable = document.getElementById('fit-excel-table');
+    addExcelRow(fitTable, [1, 2]);
+    addExcelRow(fitTable, [2, 4]);
+    addExcelRow(fitTable, [3, 9]);
+
+    // 添加CSV粘贴支持
+    enableCSVPaste(fitTable);
+
+    // 添加表格操作按钮
+    updateTableActions('fit-tab', fitTable);
+}
+
+/**
+ * 初始化回归分析表格
+ */
+function initRegressionTable() {
+    // 表格模式
+    const tableContainer = document.querySelector('#table-input-mode .table-container');
+    if (tableContainer) {
+        // 清空现有内容
+        tableContainer.innerHTML = '';
+
+        // 创建Excel风格表格
+        const excelTable = createExcelTable('regression-excel-table', 3); // X1, X2和Y三列
+        tableContainer.appendChild(excelTable);
+
+        // 添加示例数据
+        const regressionTable = document.getElementById('regression-excel-table');
+        addExcelRow(regressionTable, [1, 2, 3]);
+        addExcelRow(regressionTable, [4, 5, 6]);
+
+        // 添加CSV粘贴支持
+        enableCSVPaste(regressionTable);
+
+        // 添加表格操作按钮
+        updateTableActions('regression-tab', regressionTable);
+    }
+
+    // CSV粘贴模式
+    const csvTextarea = document.getElementById('regression-data');
+    if (csvTextarea) {
+        // 为CSV文本区域添加粘贴处理
+        csvTextarea.addEventListener('paste', handleCSVPaste);
+    }
+
+    // 切换输入模式按钮
+    const tableModeBtn = document.getElementById('table-mode-btn');
+    const csvModeBtn = document.getElementById('csv-mode-btn');
+    const tableInputMode = document.getElementById('table-input-mode');
+    const csvInputMode = document.getElementById('csv-input-mode');
+
+    if (tableModeBtn && csvModeBtn) {
+        tableModeBtn.addEventListener('click', function() {
+            tableModeBtn.classList.add('active');
+            csvModeBtn.classList.remove('active');
+            tableInputMode.classList.add('active');
+            csvInputMode.classList.remove('active');
+        });
+
+        csvModeBtn.addEventListener('click', function() {
+            csvModeBtn.classList.add('active');
+            tableModeBtn.classList.remove('active');
+            csvInputMode.classList.add('active');
+            tableInputMode.classList.remove('active');
+        });
+    }
+}
+
+/**
+ * 创建Excel风格表格
+ * @param {string} id - 表格ID
+ * @param {number} columnCount - 初始列数
+ * @returns {HTMLElement} - 创建的表格元素
+ */
+function createExcelTable(id, columnCount) {
+    const table = document.createElement('div');
+    table.id = id;
+    table.className = 'excel-table';
+
+    // 创建表头
+    const header = document.createElement('div');
+    header.className = 'excel-header';
+
+    // 添加空白角落单元格
+    const cornerCell = document.createElement('div');
+    cornerCell.className = 'excel-corner-cell';
+    header.appendChild(cornerCell);
+
+    // 添加列标题 (A, B, C...)
+    for (let i = 0; i < columnCount; i++) {
+        const columnHeader = document.createElement('div');
+        columnHeader.className = 'excel-column-header';
+
+        // 创建列标题和删除按钮的容器
+        const columnHeaderContent = document.createElement('div');
+        columnHeaderContent.className = 'column-header-content';
+        columnHeaderContent.textContent = String.fromCharCode(65 + i); // A, B, C...
+
+        // 创建删除列按钮
+        const deleteColBtn = document.createElement('div');
+        deleteColBtn.className = 'delete-col-btn';
+        deleteColBtn.innerHTML = '×';
+        deleteColBtn.title = '删除列';
+        deleteColBtn.addEventListener('click', function(e) {
+            e.stopPropagation();
+            // 获取当前列的索引,而不是使用创建时的索引
+            const currentHeaders = table.querySelectorAll('.excel-column-header');
+            const currentIndex = Array.from(currentHeaders).indexOf(columnHeader);
+            deleteExcelColumn(table, currentIndex);
+        });
+
+        columnHeader.appendChild(columnHeaderContent);
+        columnHeader.appendChild(deleteColBtn);
+        header.appendChild(columnHeader);
+    }
+
+    // 添加添加列按钮
+    const addColumnBtn = document.createElement('div');
+    addColumnBtn.className = 'excel-add-column';
+    addColumnBtn.innerHTML = '+';
+    addColumnBtn.title = '添加列';
+    addColumnBtn.addEventListener('click', function() {
+        addExcelColumn(table);
+    });
+    header.appendChild(addColumnBtn);
+
+    table.appendChild(header);
+
+    // 创建表格内容区域
+    const body = document.createElement('div');
+    body.className = 'excel-body';
+    table.appendChild(body);
+
+    // 添加添加行按钮
+    const footer = document.createElement('div');
+    footer.className = 'excel-footer';
+
+    const addRowBtn = document.createElement('div');
+    addRowBtn.className = 'excel-add-row';
+    addRowBtn.innerHTML = '+';
+    addRowBtn.title = '添加行';
+    addRowBtn.addEventListener('click', function() {
+        addExcelRow(table);
+    });
+    footer.appendChild(addRowBtn);
+
+    table.appendChild(footer);
+
+    return table;
+}
+
+/**
+ * 添加Excel表格行
+ * @param {HTMLElement} table - 表格元素
+ * @param {Array} values - 行数据值
+ * @returns {HTMLElement} - 创建的行元素
+ */
+function addExcelRow(table, values = []) {
+    const body = table.querySelector('.excel-body');
+    const rowIndex = body.children.length;
+
+    const row = document.createElement('div');
+    row.className = 'excel-row';
+    row.dataset.index = rowIndex;
+
+    // 添加行号和删除行按钮
+    const rowHeader = document.createElement('div');
+    rowHeader.className = 'excel-row-header';
+
+    // 创建行号和删除按钮的容器
+    const rowHeaderContent = document.createElement('div');
+    rowHeaderContent.className = 'row-header-content';
+    rowHeaderContent.textContent = rowIndex + 1;
+
+    // 创建删除行按钮
+    const deleteRowBtn = document.createElement('div');
+    deleteRowBtn.className = 'delete-row-btn';
+    deleteRowBtn.innerHTML = '×';
+    deleteRowBtn.title = '删除行';
+    deleteRowBtn.addEventListener('click', function(e) {
+        e.stopPropagation();
+        deleteExcelRow(table, rowIndex);
+    });
+
+    rowHeader.appendChild(rowHeaderContent);
+    rowHeader.appendChild(deleteRowBtn);
+    row.appendChild(rowHeader);
+
+    // 获取列数
+    const columnCount = table.querySelector('.excel-header').children.length - 2; // 减去角落单元格和添加列按钮
+
+    // 添加单元格
+    for (let i = 0; i < columnCount; i++) {
+        const cell = document.createElement('div');
+        cell.className = 'excel-cell';
+        cell.dataset.row = rowIndex;
+        cell.dataset.col = i;
+        cell.contentEditable = true;
+
+        // 设置初始值
+        if (values && values[i] !== undefined) {
+            cell.textContent = values[i];
+        }
+
+        // 添加单元格事件
+        cell.addEventListener('focus', function() {
+            this.classList.add('active');
+        });
+
+        cell.addEventListener('blur', function() {
+            this.classList.remove('active');
+        });
+
+        cell.addEventListener('keydown', function(e) {
+            handleCellKeydown(e, table, this);
+        });
+
+        row.appendChild(cell);
+    }
+
+    body.appendChild(row);
+    return row;
+}
+
+/**
+ * 添加Excel表格列
+ * @param {HTMLElement} table - 表格元素
+ */
+function addExcelColumn(table) {
+    // 更新表头
+    const header = table.querySelector('.excel-header');
+    const columnCount = header.children.length - 2; // 减去角落单元格和添加列按钮
+
+    // 在添加列按钮前插入新列标题
+    const columnHeader = document.createElement('div');
+    columnHeader.className = 'excel-column-header';
+
+    // 创建列标题和删除按钮的容器
+    const columnHeaderContent = document.createElement('div');
+    columnHeaderContent.className = 'column-header-content';
+    columnHeaderContent.textContent = String.fromCharCode(65 + columnCount); // A, B, C...
+
+    // 创建删除列按钮
+    const deleteColBtn = document.createElement('div');
+    deleteColBtn.className = 'delete-col-btn';
+    deleteColBtn.innerHTML = '×';
+    deleteColBtn.title = '删除列';
+    deleteColBtn.addEventListener('click', function(e) {
+        e.stopPropagation();
+        // 获取当前列的索引,而不是使用创建时的索引
+        const currentHeaders = table.querySelectorAll('.excel-column-header');
+        const currentIndex = Array.from(currentHeaders).indexOf(columnHeader);
+        deleteExcelColumn(table, currentIndex);
+    });
+
+    columnHeader.appendChild(columnHeaderContent);
+    columnHeader.appendChild(deleteColBtn);
+    header.insertBefore(columnHeader, header.lastChild);
+
+    // 为每一行添加新单元格
+    const rows = table.querySelectorAll('.excel-row');
+    rows.forEach((row, rowIndex) => {
+        const cell = document.createElement('div');
+        cell.className = 'excel-cell';
+        cell.dataset.row = rowIndex;
+        cell.dataset.col = columnCount;
+        cell.contentEditable = true;
+
+        // 添加单元格事件
+        cell.addEventListener('focus', function() {
+            this.classList.add('active');
+        });
+
+        cell.addEventListener('blur', function() {
+            this.classList.remove('active');
+        });
+
+        cell.addEventListener('keydown', function(e) {
+            handleCellKeydown(e, table, this);
+        });
+
+        row.appendChild(cell);
+    });
+}
+
+/**
+ * 处理单元格键盘事件
+ * @param {Event} e - 键盘事件
+ * @param {HTMLElement} table - 表格元素
+ * @param {HTMLElement} cell - 当前单元格
+ */
+function handleCellKeydown(e, table, cell) {
+    const row = parseInt(cell.dataset.row);
+    const col = parseInt(cell.dataset.col);
+
+    // 按Tab键移动到下一个单元格
+    if (e.key === 'Tab') {
+        e.preventDefault();
+        const nextCell = e.shiftKey ?
+            findCell(table, row, col - 1) :
+            findCell(table, row, col + 1);
+
+        if (nextCell) {
+            nextCell.focus();
+        } else if (!e.shiftKey) {
+            // 如果是最后一个单元格,添加新行并聚焦第一个单元格
+            const newRow = addExcelRow(table);
+            const firstCell = newRow.querySelector('.excel-cell');
+            if (firstCell) firstCell.focus();
+        }
+    }
+
+    // 按Enter键移动到下一行
+    if (e.key === 'Enter') {
+        e.preventDefault();
+        const nextCell = e.shiftKey ?
+            findCell(table, row - 1, col) :
+            findCell(table, row + 1, col);
+
+        if (nextCell) {
+            nextCell.focus();
+        } else if (!e.shiftKey) {
+            // 如果是最后一行,添加新行并聚焦相同列的单元格
+            const newRow = addExcelRow(table);
+            const sameColCell = newRow.querySelector(`.excel-cell[data-col="${col}"]`);
+            if (sameColCell) sameColCell.focus();
+        }
+    }
+
+    // 按方向键移动
+    if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
+        e.preventDefault();
+        let nextRow = row;
+        let nextCol = col;
+
+        if (e.key === 'ArrowUp') nextRow--;
+        if (e.key === 'ArrowDown') nextRow++;
+        if (e.key === 'ArrowLeft') nextCol--;
+        if (e.key === 'ArrowRight') nextCol++;
+
+        const nextCell = findCell(table, nextRow, nextCol);
+        if (nextCell) nextCell.focus();
+    }
+}
+
+/**
+ * 查找表格中的单元格
+ * @param {HTMLElement} table - 表格元素
+ * @param {number} row - 行索引
+ * @param {number} col - 列索引
+ * @returns {HTMLElement|null} - 找到的单元格或null
+ */
+function findCell(table, row, col) {
+    if (row < 0 || col < 0) return null;
+    return table.querySelector(`.excel-cell[data-row="${row}"][data-col="${col}"]`);
+}
+
+/**
+ * 启用CSV粘贴功能
+ * @param {HTMLElement} table - 表格元素
+ */
+function enableCSVPaste(table) {
+    table.addEventListener('paste', function(e) {
+        e.preventDefault();
+
+        // 获取粘贴的文本
+        const clipboardData = e.clipboardData || window.clipboardData;
+        const pastedData = clipboardData.getData('text');
+
+        // 处理CSV数据
+        processCSVData(table, pastedData);
+    });
+}
+
+/**
+ * 处理CSV数据
+ * @param {HTMLElement} table - 表格元素
+ * @param {string} csvData - CSV格式的数据
+ */
+function processCSVData(table, csvData) {
+    // 清空现有数据
+    const body = table.querySelector('.excel-body');
+    body.innerHTML = '';
+
+    // 解析CSV数据
+    const rows = csvData.trim().split(/\r?\n/);
+    const maxCols = rows.reduce((max, row) => {
+        const cols = row.split(/[,\t]/);
+        return Math.max(max, cols.length);
+    }, 0);
+
+    // 调整列数
+    adjustColumnCount(table, maxCols);
+
+    // 添加数据行
+    rows.forEach(row => {
+        const values = row.split(/[,\t]/);
+        addExcelRow(table, values);
+    });
+}
+
+/**
+ * 调整表格列数
+ * @param {HTMLElement} table - 表格元素
+ * @param {number} columnCount - 目标列数
+ */
+function adjustColumnCount(table, columnCount) {
+    const header = table.querySelector('.excel-header');
+    const currentColumnCount = header.children.length - 2; // 减去角落单元格和添加列按钮
+
+    // 如果需要添加列
+    for (let i = currentColumnCount; i < columnCount; i++) {
+        addExcelColumn(table);
+    }
+}
+
+/**
+ * 处理CSV文本区域的粘贴事件
+ * @param {Event} e - 粘贴事件
+ */
+function handleCSVPaste(e) {
+    // 获取粘贴的文本
+    const clipboardData = e.clipboardData || window.clipboardData;
+    const pastedData = clipboardData.getData('text');
+
+    // 如果是CSV格式,直接替换文本区域内容
+    if (pastedData.includes(',') || pastedData.includes('\t')) {
+        e.preventDefault();
+        e.target.value = pastedData;
+    }
+}
+
+/**
+ * 更新表格操作按钮
+ * @param {string} tabId - 标签页ID
+ * @param {HTMLElement} table - 表格元素
+ */
+function updateTableActions(tabId, table) {
+    const actionsContainer = document.querySelector(`#${tabId} .table-actions`);
+    if (!actionsContainer) return;
+
+    // 清空现有按钮
+    const buttonsContainer = actionsContainer.querySelector('div');
+    if (buttonsContainer) {
+        buttonsContainer.innerHTML = '';
+
+        // 添加行按钮
+        const addRowBtn = document.createElement('button');
+        addRowBtn.id = tabId === 'fit-tab' ? 'add-row' : 'add-regression-row';
+        addRowBtn.textContent = '添加行';
+        addRowBtn.addEventListener('click', function() {
+            addExcelRow(table);
+        });
+        buttonsContainer.appendChild(addRowBtn);
+
+        // 添加列按钮(仅回归分析)
+        if (tabId === 'regression-tab') {
+            const addColBtn = document.createElement('button');
+            addColBtn.id = 'add-regression-column';
+            addColBtn.textContent = '添加变量';
+            addColBtn.addEventListener('click', function() {
+                addExcelColumn(table);
+            });
+            buttonsContainer.appendChild(addColBtn);
+        }
+
+        // 导入导出按钮(仅拟合数据)
+        if (tabId === 'fit-tab') {
+            const importBtn = document.createElement('button');
+            importBtn.id = 'import-data';
+            importBtn.textContent = '导入数据';
+            importBtn.addEventListener('click', function() {
+                // 创建隐藏的文件输入
+                const fileInput = document.createElement('input');
+                fileInput.type = 'file';
+                fileInput.accept = '.csv,.txt';
+                fileInput.style.display = 'none';
+                document.body.appendChild(fileInput);
+
+                fileInput.addEventListener('change', function() {
+                    const file = this.files[0];
+                    if (file) {
+                        const reader = new FileReader();
+                        reader.onload = function(e) {
+                            processCSVData(table, e.target.result);
+                        };
+                        reader.readAsText(file);
+                    }
+                    document.body.removeChild(fileInput);
+                });
+
+                fileInput.click();
+            });
+            buttonsContainer.appendChild(importBtn);
+
+            const exportBtn = document.createElement('button');
+            exportBtn.id = 'export-data';
+            exportBtn.textContent = '导出数据';
+            exportBtn.addEventListener('click', function() {
+                exportTableData(table);
+            });
+            buttonsContainer.appendChild(exportBtn);
+        }
+    }
+}
+
+/**
+ * 导出表格数据为CSV
+ * @param {HTMLElement} table - 表格元素
+ */
+function exportTableData(table) {
+    const rows = table.querySelectorAll('.excel-row');
+    let csvContent = '';
+
+    rows.forEach(row => {
+        const cells = row.querySelectorAll('.excel-cell');
+        const rowData = Array.from(cells).map(cell => {
+            // 处理CSV特殊字符
+            let value = cell.textContent.trim();
+            if (value.includes(',') || value.includes('"') || value.includes('\n')) {
+                value = '"' + value.replace(/"/g, '""') + '"';
+            }
+            return value;
+        }).join(',');
+
+        csvContent += rowData + '\n';
+    });
+
+    // 创建下载链接
+    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+    const url = URL.createObjectURL(blob);
+    const link = document.createElement('a');
+    link.setAttribute('href', url);
+    link.setAttribute('download', 'data.csv');
+    link.style.display = 'none';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+}
+
+/**
+ * 获取表格数据
+ * @param {HTMLElement} table - 表格元素
+ * @returns {Array} - 表格数据数组
+ */
+function getTableData(table) {
+    const rows = table.querySelectorAll('.excel-row');
+    const data = [];
+
+    rows.forEach(row => {
+        const cells = row.querySelectorAll('.excel-cell');
+        const rowData = Array.from(cells).map(cell => {
+            const value = cell.textContent.trim();
+            return isNaN(value) ? value : parseFloat(value);
+        });
+        data.push(rowData);
+    });
+
+    return data;
+}
+
+/**
+ * 删除Excel表格行
+ * @param {HTMLElement} table - 表格元素
+ * @param {number} rowIndex - 要删除的行索引
+ */
+function deleteExcelRow(table, rowIndex) {
+    const body = table.querySelector('.excel-body');
+    const rows = body.querySelectorAll('.excel-row');
+
+    // 确保行索引有效
+    if (rowIndex < 0 || rowIndex >= rows.length) return;
+
+    // 删除指定行
+    body.removeChild(rows[rowIndex]);
+
+    // 更新剩余行的索引和行号
+    const remainingRows = body.querySelectorAll('.excel-row');
+    remainingRows.forEach((row, idx) => {
+        row.dataset.index = idx;
+        const rowHeaderContent = row.querySelector('.row-header-content');
+        if (rowHeaderContent) {
+            rowHeaderContent.textContent = idx + 1;
+        }
+
+        // 更新单元格的行索引
+        const cells = row.querySelectorAll('.excel-cell');
+        cells.forEach(cell => {
+            cell.dataset.row = idx;
+        });
+    });
+}
+
+/**
+ * 删除Excel表格列
+ * @param {HTMLElement} table - 表格元素
+ * @param {number} colIndex - 要删除的列索引
+ */
+function deleteExcelColumn(table, colIndex) {
+    // 确保至少保留一列
+    const header = table.querySelector('.excel-header');
+    const columnCount = header.children.length - 2; // 减去角落单元格和添加列按钮
+
+    if (columnCount <= 1) {
+        alert('表格至少需要保留一列');
+        return;
+    }
+
+    // 删除列标题
+    const columnHeaders = header.querySelectorAll('.excel-column-header');
+    if (colIndex < columnHeaders.length) {
+        header.removeChild(columnHeaders[colIndex]);
+    }
+
+    // 删除每行中的对应单元格
+    const rows = table.querySelectorAll('.excel-row');
+    rows.forEach(row => {
+        const cells = row.querySelectorAll('.excel-cell');
+        if (colIndex < cells.length) {
+            row.removeChild(cells[colIndex]);
+        }
+
+        // 更新剩余单元格的列索引
+        const remainingCells = row.querySelectorAll('.excel-cell');
+        remainingCells.forEach((cell, idx) => {
+            cell.dataset.col = idx;
+        });
+    });
+
+    // 更新列标题 - 重新分配A, B, C...标签
+    const updatedHeaders = header.querySelectorAll('.excel-column-header');
+    updatedHeaders.forEach((header, idx) => {
+        const headerContent = header.querySelector('.column-header-content');
+        if (headerContent) {
+            headerContent.textContent = String.fromCharCode(65 + idx); // A, B, C...
+        }
+    });
+}
+
+/**
+ * 从Excel表格获取数据点
+ * @param {HTMLElement} table - 表格元素
+ * @returns {Array} - 数据点数组,每个数据点包含x和y属性
+ */
+function getDataPointsFromExcelTable(table) {
+    const rows = table.querySelectorAll('.excel-row');
+    const dataPoints = [];
+
+    // 检查表格是否有足够的列
+    const firstRow = table.querySelector('.excel-row');
+    if (!firstRow) return dataPoints;
+
+    const cellCount = firstRow.querySelectorAll('.excel-cell').length;
+    if (cellCount < 2) {
+        console.warn('表格列数不足,无法提取X和Y值');
+        return dataPoints;
+    }
+
+    rows.forEach(row => {
+        const cells = row.querySelectorAll('.excel-cell');
+        if (cells.length >= 2) {
+            // 始终使用前两列作为X和Y值,无论它们的列标题是什么
+            const xValue = parseFloat(cells[0].textContent.trim());
+            const yValue = parseFloat(cells[1].textContent.trim());
+
+            // 只添加有效的数据点
+            if (!isNaN(xValue) && !isNaN(yValue)) {
+                dataPoints.push({ x: xValue, y: yValue });
+            }
+        }
+    });
+
+    return dataPoints;
+}
+
+// 添加CSS样式
+document.addEventListener('DOMContentLoaded', function() {
+    // 添加删除按钮样式
+    const style = document.createElement('style');
+    style.textContent = `
+        .excel-row-header, .excel-column-header {
+            position: relative;
+        }
+
+        .row-header-content, .column-header-content {
+            display: inline-block;
+            width: 100%;
+            text-align: center;
+        }
+
+        .delete-row-btn, .delete-col-btn {
+            position: absolute;
+            display: none;
+            cursor: pointer;
+            color: #ff4d4f;
+            font-weight: bold;
+            font-size: 12px;
+            width: 16px;
+            height: 16px;
+            line-height: 14px;
+            text-align: center;
+            border-radius: 50%;
+            background-color: #fff;
+            border: 1px solid #ff4d4f;
+        }
+
+        .delete-row-btn {
+            right: 2px;
+            top: 50%;
+            transform: translateY(-50%);
+        }
+
+        .delete-col-btn {
+            top: 2px;
+            right: 50%;
+            transform: translateX(50%);
+        }
+
+        .excel-row-header:hover .delete-row-btn,
+        .excel-column-header:hover .delete-col-btn {
+            display: block;
+        }
+    `;
+    document.head.appendChild(style);
+});
+
+// 导出函数供其他模块使用
+window.dataTable = {
+    getTableData: getTableData,
+    processCSVData: processCSVData,
+    getDataPointsFromExcelTable: getDataPointsFromExcelTable
+};

+ 731 - 0
FuncGen/enhanced-styles.css

@@ -0,0 +1,731 @@
+/* 增强样式 - 现代化界面、专业数据表格、公式显示和虚拟键盘 */
+
+/* 全局样式增强 */
+:root {
+    --primary-color: #4a6cf7;
+    --primary-hover: #3a5ce4;
+    --secondary-color: #6c757d;
+    --success-color: #28a745;
+    --danger-color: #dc3545;
+    --warning-color: #ffc107;
+    --info-color: #17a2b8;
+    --light-color: #f8f9fa;
+    --dark-color: #343a40;
+    --border-color: #e0e0e0;
+    --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.05);
+    --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.1);
+    --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.15);
+    --transition: all 0.3s ease;
+    --border-radius: 6px;
+}
+
+body {
+    background-color: #f8f9fc;
+    color: #333;
+    line-height: 1.6;
+    font-family: 'Microsoft YaHei', 'PingFang SC', 'Segoe UI', Arial, sans-serif;
+}
+
+.container {
+    max-width: 1200px;
+    margin: 30px auto;
+    padding: 25px;
+    background-color: #fff;
+    border-radius: var(--border-radius);
+    box-shadow: var(--shadow-md);
+    transition: var(--transition);
+}
+
+h1 {
+    text-align: center;
+    margin-bottom: 25px;
+    color: #2c3e50;
+    font-weight: 600;
+    position: relative;
+    padding-bottom: 10px;
+}
+
+h1:after {
+    content: '';
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    transform: translateX(-50%);
+    width: 80px;
+    height: 3px;
+    background: var(--primary-color);
+    border-radius: 3px;
+}
+
+/* 标签页样式增强 */
+.tabs {
+    display: flex;
+    margin-bottom: 25px;
+    border-bottom: 1px solid var(--border-color);
+    overflow-x: auto;
+    scrollbar-width: thin;
+    position: relative;
+}
+
+.tabs::-webkit-scrollbar {
+    height: 4px;
+}
+
+.tabs::-webkit-scrollbar-thumb {
+    background-color: var(--secondary-color);
+    border-radius: 4px;
+}
+
+.tab-btn {
+    padding: 12px 24px;
+    background: none;
+    border: none;
+    cursor: pointer;
+    font-size: 16px;
+    font-weight: 500;
+    color: var(--secondary-color);
+    transition: var(--transition);
+    position: relative;
+    white-space: nowrap;
+}
+
+.tab-btn:hover {
+    color: var(--primary-color);
+}
+
+.tab-btn.active {
+    color: var(--primary-color);
+    font-weight: 600;
+}
+
+.tab-btn.active:after {
+    content: '';
+    position: absolute;
+    bottom: -1px;
+    left: 0;
+    width: 100%;
+    height: 3px;
+    background: var(--primary-color);
+    border-radius: 3px 3px 0 0;
+    transition: var(--transition);
+}
+
+.tab-pane {
+    display: none;
+    padding: 20px 0;
+    animation: fadeIn 0.3s ease-in-out;
+}
+
+.tab-pane.active {
+    display: block;
+}
+
+@keyframes fadeIn {
+    from { opacity: 0; transform: translateY(10px); }
+    to { opacity: 1; transform: translateY(0); }
+}
+
+/* 输入组样式增强 */
+.input-group {
+    margin-bottom: 20px;
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 10px;
+}
+
+label {
+    font-weight: 500;
+    color: #444;
+    margin-right: 10px;
+}
+
+input[type="text"],
+input[type="number"],
+select,
+textarea {
+    padding: 10px 14px;
+    border: 1px solid var(--border-color);
+    border-radius: var(--border-radius);
+    font-size: 14px;
+    transition: var(--transition);
+    box-shadow: var(--shadow-sm);
+    width: 220px;
+}
+
+input[type="text"]:focus,
+input[type="number"]:focus,
+select:focus,
+textarea:focus {
+    border-color: var(--primary-color);
+    outline: none;
+    box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.15);
+}
+
+textarea {
+    width: 100%;
+    resize: vertical;
+    min-height: 120px;
+}
+
+button {
+    padding: 10px 18px;
+    background-color: var(--primary-color);
+    color: white;
+    border: none;
+    border-radius: var(--border-radius);
+    cursor: pointer;
+    font-size: 14px;
+    font-weight: 500;
+    transition: var(--transition);
+    box-shadow: var(--shadow-sm);
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+}
+
+button:hover {
+    background-color: var(--primary-hover);
+    box-shadow: var(--shadow-md);
+    transform: translateY(-1px);
+}
+
+button:active {
+    transform: translateY(0);
+    box-shadow: var(--shadow-sm);
+}
+
+.primary-btn {
+    background-color: var(--primary-color);
+    padding: 12px 24px;
+    font-size: 16px;
+    font-weight: 500;
+    letter-spacing: 0.5px;
+}
+
+/* 专业数据表格样式 */
+.table-container {
+    max-height: 350px;
+    overflow-y: auto;
+    margin-bottom: 20px;
+    border: 1px solid var(--border-color);
+    border-radius: var(--border-radius);
+    box-shadow: var(--shadow-sm);
+    scrollbar-width: thin;
+}
+
+/* 数据输入切换样式 */
+.input-mode-toggle {
+    display: flex;
+    margin-bottom: 15px;
+    background-color: #f0f0f0;
+    border-radius: var(--border-radius);
+    padding: 3px;
+    width: fit-content;
+}
+
+.input-mode-toggle button {
+    background: none;
+    border: none;
+    padding: 8px 16px;
+    border-radius: var(--border-radius);
+    cursor: pointer;
+    font-size: 14px;
+    color: var(--secondary-color);
+    transition: var(--transition);
+    box-shadow: none;
+}
+
+.input-mode-toggle button.active {
+    background-color: white;
+    color: var(--primary-color);
+    box-shadow: var(--shadow-sm);
+}
+
+.input-mode-toggle button:hover:not(.active) {
+    background-color: rgba(255, 255, 255, 0.5);
+}
+
+.input-mode {
+    display: none;
+}
+
+.input-mode.active {
+    display: block;
+    animation: fadeIn 0.3s ease-in-out;
+}
+
+.csv-input-container {
+    margin-bottom: 15px;
+}
+
+.table-container::-webkit-scrollbar {
+    width: 6px;
+}
+
+.table-container::-webkit-scrollbar-thumb {
+    background-color: var(--secondary-color);
+    border-radius: 3px;
+}
+
+.professional-table {
+    width: 100%;
+    border-collapse: collapse;
+    font-size: 14px;
+    box-shadow: none;
+}
+
+.professional-table thead th {
+    background-color: #f8f9fa;
+    color: #444;
+    font-weight: 600;
+    text-align: center;
+    padding: 12px;
+    border: 1px solid var(--border-color);
+    position: sticky;
+    top: 0;
+    z-index: 10;
+    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.professional-table th.sortable {
+    cursor: pointer;
+    user-select: none;
+}
+
+.professional-table th.sortable:hover {
+    background-color: #eef1f6;
+}
+
+.professional-table th.sortable::after {
+    content: '⇅';
+    margin-left: 5px;
+    font-size: 12px;
+    opacity: 0.5;
+    transition: var(--transition);
+}
+
+.professional-table th.sorted-asc::after {
+    content: '↑';
+    opacity: 1;
+    color: var(--primary-color);
+}
+
+.professional-table th.sorted-desc::after {
+    content: '↓';
+    opacity: 1;
+    color: var(--primary-color);
+}
+
+.professional-table tbody tr {
+    transition: var(--transition);
+}
+
+.professional-table tbody tr:nth-child(even) {
+    background-color: #f9fafc;
+}
+
+.professional-table tbody tr:hover {
+    background-color: #f0f7ff;
+}
+
+.professional-table td {
+    padding: 10px 12px;
+    border: 1px solid var(--border-color);
+    text-align: right;
+    transition: var(--transition);
+}
+
+.professional-table input {
+    width: 100%;
+    padding: 8px 10px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    text-align: right;
+    transition: var(--transition);
+}
+
+.professional-table input:focus {
+    border-color: var(--primary-color);
+    outline: none;
+    box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.15);
+}
+
+.table-actions {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin: 15px 0;
+    flex-wrap: wrap;
+    gap: 10px;
+}
+
+.table-actions button {
+    margin-right: 8px;
+}
+
+.table-pagination {
+    display: flex;
+    align-items: center;
+}
+
+.table-pagination button {
+    padding: 6px 10px;
+    margin: 0 3px;
+    background-color: #f0f0f0;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    cursor: pointer;
+    box-shadow: none;
+}
+
+.table-pagination button:hover {
+    background-color: #e0e0e0;
+    box-shadow: var(--shadow-sm);
+}
+
+.table-pagination button.active {
+    background-color: var(--primary-color);
+    color: white;
+    border-color: var(--primary-color);
+}
+
+.remove-row {
+    background-color: var(--danger-color);
+    padding: 6px 12px;
+    font-size: 13px;
+}
+
+.remove-row:hover {
+    background-color: #c82333;
+}
+
+/* 结果区域样式增强 */
+.results-container {
+    margin-top: 35px;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 25px;
+}
+
+.plot-container {
+    flex: 1;
+    min-width: 300px;
+    border: 1px solid var(--border-color);
+    border-radius: var(--border-radius);
+    padding: 15px;
+    background-color: #fff;
+    box-shadow: var(--shadow-sm);
+    transition: var(--transition);
+}
+
+.plot-container:hover {
+    box-shadow: var(--shadow-md);
+}
+
+#plot-area {
+    width: 100%;
+    height: 400px;
+    border-radius: 4px;
+    overflow: hidden;
+}
+
+.equation-container {
+    flex: 1;
+    min-width: 300px;
+    border: 1px solid var(--border-color);
+    border-radius: var(--border-radius);
+    padding: 20px;
+    background-color: #fff;
+    box-shadow: var(--shadow-sm);
+    transition: var(--transition);
+}
+
+.equation-container:hover {
+    box-shadow: var(--shadow-md);
+}
+
+.equation-container h3 {
+    margin-bottom: 20px;
+    color: #2c3e50;
+    font-weight: 600;
+    position: relative;
+    padding-bottom: 8px;
+    border-bottom: 1px solid var(--border-color);
+}
+
+/* 数学公式显示样式 */
+.math-container {
+    padding: 20px;
+    background-color: #f9fafc;
+    border-radius: var(--border-radius);
+    margin: 15px 0;
+    overflow-x: auto;
+    border: 1px solid var(--border-color);
+    box-shadow: var(--shadow-sm);
+}
+
+.equation-display {
+    font-size: 18px;
+    line-height: 1.6;
+    text-align: center;
+    padding: 15px;
+    min-height: 60px;
+}
+
+.stats-display {
+    margin-top: 25px;
+    padding: 18px;
+    background-color: #f8f9fc;
+    border-radius: var(--border-radius);
+    border-left: 4px solid var(--primary-color);
+    box-shadow: var(--shadow-sm);
+}
+
+.stats-display h4 {
+    margin-bottom: 12px;
+    color: #2c3e50;
+    font-weight: 600;
+}
+
+.stats-display table {
+    width: 100%;
+    border-collapse: collapse;
+}
+
+.stats-display table th,
+.stats-display table td {
+    padding: 10px;
+    text-align: left;
+    border-bottom: 1px solid var(--border-color);
+}
+
+.stats-display table th {
+    font-weight: 600;
+    color: #444;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+    .container {
+        padding: 15px;
+        margin: 15px auto;
+    }
+
+    .input-group {
+        flex-direction: column;
+        align-items: flex-start;
+    }
+
+    input[type="text"],
+    input[type="number"],
+    select {
+        width: 100%;
+    }
+
+    .results-container {
+        flex-direction: column;
+    }
+
+    .plot-container,
+    .equation-container {
+        min-width: 100%;
+    }
+
+    .tab-btn {
+        padding: 10px 15px;
+        font-size: 14px;
+    }
+}
+
+/* 虚拟键盘样式 */
+.math-keyboard {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    background-color: #f8f9fa;
+    border-top: 1px solid var(--border-color);
+    padding: 15px;
+    display: none;
+    z-index: 1000;
+    box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.1);
+    transition: transform 0.3s ease;
+    max-height: 50vh;
+    overflow-y: auto;
+    transform: translateY(100%);
+}
+
+.math-keyboard.active {
+    display: block;
+    transform: translateY(0);
+}
+
+@keyframes slideUp {
+    from { transform: translateY(100%); }
+    to { transform: translateY(0); }
+}
+
+.keyboard-container {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+    max-width: 800px;
+    margin: 0 auto;
+}
+
+.keyboard-row {
+    display: flex;
+    width: 100%;
+    margin-bottom: 8px;
+}
+
+.keyboard-key {
+    flex: 1;
+    min-width: 40px;
+    height: 45px;
+    margin: 3px;
+    background-color: white;
+    border: 1px solid var(--border-color);
+    border-radius: 6px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    font-size: 16px;
+    user-select: none;
+    transition: var(--transition);
+    box-shadow: var(--shadow-sm);
+}
+
+.keyboard-key:hover {
+    background-color: #f0f7ff;
+    border-color: var(--primary-color);
+    box-shadow: var(--shadow-md);
+    transform: translateY(-2px);
+}
+
+.keyboard-key:active {
+    transform: translateY(0);
+    box-shadow: var(--shadow-sm);
+}
+
+.keyboard-key.function {
+    background-color: #eef1f6;
+    font-weight: 500;
+}
+
+.keyboard-key.wide {
+    flex: 2;
+}
+
+.keyboard-key.special {
+    background-color: #e8f0fe;
+    color: var(--primary-color);
+    font-weight: 500;
+}
+
+.keyboard-close {
+    position: absolute;
+    top: 10px;
+    right: 10px;
+    background-color: var(--danger-color);
+    color: white;
+    border: none;
+    border-radius: 50%;
+    font-size: 18px;
+    cursor: pointer;
+    padding: 5px 10px;
+    box-shadow: var(--shadow-sm);
+    z-index: 1010;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.keyboard-close:hover {
+    background-color: #c82333;
+    box-shadow: var(--shadow-md);
+}
+
+.keyboard-toggle {
+    position: fixed;
+    bottom: 20px;
+    right: 20px;
+    background-color: var(--primary-color);
+    color: white;
+    border: none;
+    border-radius: 50%;
+    font-size: 24px;
+    cursor: pointer;
+    width: 50px;
+    height: 50px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    box-shadow: var(--shadow-md);
+    z-index: 999;
+    transition: var(--transition);
+}
+
+.keyboard-toggle.active {
+    background-color: var(--success-color);
+    transform: translateY(-3px);
+    box-shadow: var(--shadow-lg);
+}
+
+.keyboard-toggle:hover {
+    background-color: var(--primary-hover);
+    transform: translateY(-3px);
+    box-shadow: var(--shadow-lg);
+}
+
+.keyboard-panel {
+    display: none;
+}
+
+.keyboard-panel.active {
+    display: block;
+}
+
+@media (max-width: 768px) {
+    .math-keyboard {
+        max-height: 70vh;
+    }
+
+    .keyboard-container {
+        max-width: 100%;
+    }
+
+    .keyboard-key {
+        min-width: 30px;
+        height: 40px;
+        font-size: 14px;
+    }
+}
+
+.keyboard-tabs {
+    display: flex;
+    border-bottom: 1px solid var(--border-color);
+    margin-bottom: 10px;
+    padding-bottom: 5px;
+    overflow-x: auto;
+}
+
+.keyboard-tab {
+    padding: 8px 15px;
+    margin-right: 5px;
+    cursor: pointer;
+    border-radius: 4px 4px 0 0;
+    background-color: #f0f0f0;
+    transition: var(--transition);
+}
+
+.keyboard-tab:hover {
+    background-color: #e0e0e0;
+}
+
+.keyboard-tab.active {
+    background-color: var(--primary-color);
+    color: white;
+}

+ 199 - 0
FuncGen/excel-table.css

@@ -0,0 +1,199 @@
+/* Excel风格表格样式 */
+
+.excel-table {
+    border: 1px solid var(--border-color);
+    border-radius: 4px;
+    overflow: hidden;
+    font-size: 14px;
+    background-color: white;
+    box-shadow: var(--shadow-sm);
+    margin-bottom: 15px;
+    position: relative;
+    width: 100%;
+    max-height: 400px;
+    display: flex;
+    flex-direction: column;
+}
+
+/* 表头样式 */
+.excel-header {
+    display: flex;
+    background-color: #f8f9fa;
+    border-bottom: 1px solid var(--border-color);
+    position: sticky;
+    top: 0;
+    z-index: 2;
+}
+
+.excel-corner-cell {
+    min-width: 40px;
+    height: 30px;
+    border-right: 1px solid var(--border-color);
+    border-bottom: 1px solid var(--border-color);
+    background-color: #f1f3f5;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: bold;
+    user-select: none;
+}
+
+.excel-column-header {
+    min-width: 80px;
+    height: 30px;
+    border-right: 1px solid var(--border-color);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: bold;
+    user-select: none;
+    position: relative;
+}
+
+.excel-add-column {
+    min-width: 30px;
+    height: 30px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #e9ecef;
+    cursor: pointer;
+    user-select: none;
+    font-weight: bold;
+    color: var(--primary-color);
+    transition: var(--transition);
+}
+
+.excel-add-column:hover {
+    background-color: #dee2e6;
+}
+
+/* 表格主体样式 */
+.excel-body {
+    flex: 1;
+    overflow-y: auto;
+    max-height: 350px;
+}
+
+.excel-row {
+    display: flex;
+    border-bottom: 1px solid var(--border-color);
+}
+
+.excel-row:last-child {
+    border-bottom: none;
+}
+
+.excel-row-header {
+    min-width: 40px;
+    height: 30px;
+    border-right: 1px solid var(--border-color);
+    background-color: #f8f9fa;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: bold;
+    user-select: none;
+    position: sticky;
+    left: 0;
+    z-index: 1;
+}
+
+.excel-cell {
+    min-width: 80px;
+    height: 30px;
+    border-right: 1px solid var(--border-color);
+    padding: 0 5px;
+    display: flex;
+    align-items: center;
+    overflow: hidden;
+    white-space: nowrap;
+    outline: none;
+    transition: var(--transition);
+}
+
+.excel-cell:focus, .excel-cell.active {
+    background-color: #e8f4fc;
+    box-shadow: inset 0 0 0 2px var(--primary-color);
+}
+
+.excel-cell:hover:not(:focus):not(.active) {
+    background-color: #f0f7ff;
+}
+
+/* 表格底部样式 */
+.excel-footer {
+    display: flex;
+    border-top: 1px solid var(--border-color);
+}
+
+.excel-add-row {
+    min-width: 40px;
+    height: 30px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #e9ecef;
+    cursor: pointer;
+    user-select: none;
+    font-weight: bold;
+    color: var(--primary-color);
+    transition: var(--transition);
+}
+
+.excel-add-row:hover {
+    background-color: #dee2e6;
+}
+
+/* 响应式样式 */
+@media (max-width: 768px) {
+    .excel-table {
+        font-size: 12px;
+    }
+
+    .excel-column-header,
+    .excel-cell {
+        min-width: 60px;
+    }
+
+    .excel-corner-cell,
+    .excel-row-header {
+        min-width: 30px;
+    }
+}
+
+/* CSV粘贴模式样式 */
+.input-mode {
+    display: none;
+}
+
+.input-mode.active {
+    display: block;
+}
+
+.input-mode-toggle {
+    display: flex;
+    margin-bottom: 15px;
+}
+
+.input-mode-toggle button {
+    padding: 8px 15px;
+    background-color: #f8f9fa;
+    border: 1px solid var(--border-color);
+    cursor: pointer;
+    transition: var(--transition);
+}
+
+.input-mode-toggle button:first-child {
+    border-radius: 4px 0 0 4px;
+}
+
+.input-mode-toggle button:last-child {
+    border-radius: 0 4px 4px 0;
+}
+
+.input-mode-toggle button.active {
+    background-color: var(--primary-color);
+    color: white;
+    border-color: var(--primary-color);
+}

+ 556 - 0
FuncGen/fit.js

@@ -0,0 +1,556 @@
+// 函数拟合工具 - 数据拟合模块
+
+// 页面加载完成后执行初始化
+document.addEventListener('DOMContentLoaded', function() {
+    // 初始化数据拟合功能
+    initDataFitting();
+
+    // 初始化多项式次数控制
+    const regressionType = document.getElementById('regression-type');
+    const polynomialDegreeContainer = document.getElementById('polynomial-degree-container');
+
+    regressionType.addEventListener('change', function() {
+        if (this.value === 'polynomial') {
+            polynomialDegreeContainer.style.display = 'inline-block';
+        } else {
+            polynomialDegreeContainer.style.display = 'none';
+        }
+    });
+});
+
+/**
+ * 初始化数据拟合功能
+ */
+function initDataFitting() {
+    const fitButton = document.getElementById('fit-data');
+    const fitType = document.getElementById('fit-type');
+
+    fitButton.addEventListener('click', function() {
+        // 获取数据点
+        const fitTable = document.getElementById('fit-excel-table');
+        const dataPoints = window.dataTable.getDataPointsFromExcelTable(fitTable);
+
+        // 检查数据点是否足够
+        if (dataPoints.length < 2) {
+            alert('请至少输入两个有效的数据点');
+            return;
+        }
+
+        // 根据选择的函数类型进行拟合
+        const fitResult = fitData(dataPoints, fitType.value);
+
+        // 显示拟合结果
+        displayFitResult(fitResult, dataPoints);
+    });
+}
+
+// 使用utils.js中的getDataPointsFromTable函数
+
+/**
+ * 拟合数据
+ * @param {Array} dataPoints - 数据点数组
+ * @param {string} fitType - 拟合函数类型
+ * @returns {Object} 拟合结果
+ */
+function fitData(dataPoints, fitType) {
+    // 提取x和y值数组
+    const xValues = dataPoints.map(point => point.x);
+    const yValues = dataPoints.map(point => point.y);
+
+    let result = {
+        fitType: fitType,
+        equation: '',
+        parameters: [],
+        r2: 0,
+        functionExpr: ''
+    };
+
+    try {
+        switch (fitType) {
+            case 'linear':
+                result = fitLinear(xValues, yValues);
+                break;
+            case 'quadratic':
+                result = fitPolynomial(xValues, yValues, 2);
+                break;
+            case 'cubic':
+                result = fitPolynomial(xValues, yValues, 3);
+                break;
+            case 'exponential':
+                result = fitExponential(xValues, yValues);
+                break;
+            case 'logarithmic':
+                result = fitLogarithmic(xValues, yValues);
+                break;
+            case 'power':
+                result = fitPower(xValues, yValues);
+                break;
+            default:
+                throw new Error('不支持的拟合类型');
+        }
+
+        return result;
+    } catch (error) {
+        alert('拟合计算错误: ' + error.message);
+        return null;
+    }
+}
+
+/**
+ * 线性拟合 (y = ax + b)
+ * @param {Array} xValues - x值数组
+ * @param {Array} yValues - y值数组
+ * @returns {Object} 拟合结果
+ */
+function fitLinear(xValues, yValues) {
+    // 使用最小二乘法计算线性回归参数
+    const n = xValues.length;
+
+    // 计算各项和
+    let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
+    for (let i = 0; i < n; i++) {
+        sumX += xValues[i];
+        sumY += yValues[i];
+        sumXY += xValues[i] * yValues[i];
+        sumX2 += xValues[i] * xValues[i];
+    }
+
+    // 计算斜率和截距
+    const a = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
+    const b = (sumY - a * sumX) / n;
+
+    // 计算拟合优度 R²
+    const r2 = calculateRSquared(xValues, yValues, x => a * x + b);
+
+    // 格式化方程
+    const equation = `y = ${a.toFixed(4)}x ${b >= 0 ? '+ ' + b.toFixed(4) : '- ' + Math.abs(b).toFixed(4)}`;
+    const functionExpr = `${a}*x ${b >= 0 ? '+ ' + b : '- ' + Math.abs(b)}`;
+
+    return {
+        fitType: 'linear',
+        equation: equation,
+        parameters: [a, b],
+        r2: r2,
+        functionExpr: functionExpr
+    };
+}
+
+/**
+ * 多项式拟合
+ * @param {Array} xValues - x值数组
+ * @param {Array} yValues - y值数组
+ * @param {number} degree - 多项式次数
+ * @returns {Object} 拟合结果
+ */
+function fitPolynomial(xValues, yValues, degree) {
+    // 使用math.js的多项式回归
+    const coeffs = polyfit(xValues, yValues, degree);
+
+    // 构建方程字符串
+    let equation = 'y = ';
+    let functionExpr = '';
+
+    for (let i = 0; i <= degree; i++) {
+        const power = degree - i;
+        const coeff = coeffs[i];
+
+        if (i > 0 && coeff >= 0) {
+            equation += ' + ';
+            functionExpr += ' + ';
+        } else if (i > 0 && coeff < 0) {
+            equation += ' - ';
+            functionExpr += ' - ';
+        }
+
+        if (power === 0) {
+            equation += `${Math.abs(coeff).toFixed(4)}`;
+            functionExpr += `${Math.abs(coeff)}`;
+        } else if (power === 1) {
+            equation += `${Math.abs(coeff).toFixed(4)}x`;
+            functionExpr += `${Math.abs(coeff)}*x`;
+        } else {
+            equation += `${Math.abs(coeff).toFixed(4)}x^${power}`;
+            functionExpr += `${Math.abs(coeff)}*x^${power}`;
+        }
+    }
+
+    // 计算拟合优度 R²
+    const r2 = calculateRSquared(xValues, yValues, x => {
+        let result = 0;
+        for (let i = 0; i <= degree; i++) {
+            result += coeffs[i] * Math.pow(x, degree - i);
+        }
+        return result;
+    });
+
+    // 计算RMSE
+    const rmse = calculateRMSE(xValues, yValues, x => {
+        let result = 0;
+        for (let i = 0; i <= degree; i++) {
+            result += coeffs[i] * Math.pow(x, degree - i);
+        }
+        return result;
+    });
+
+    return {
+        fitType: degree === 2 ? 'quadratic' : 'cubic',
+        equation: equation,
+        parameters: coeffs,
+        r2: r2,
+        rmse: rmse,
+        functionExpr: functionExpr
+    };
+}
+
+/**
+ * 指数拟合 (y = a*e^(bx))
+ * @param {Array} xValues - x值数组
+ * @param {Array} yValues - y值数组
+ * @returns {Object} 拟合结果
+ */
+function fitExponential(xValues, yValues) {
+    // 检查y值是否都为正
+    for (let i = 0; i < yValues.length; i++) {
+        if (yValues[i] <= 0) {
+            throw new Error('指数拟合要求所有y值必须为正数');
+        }
+    }
+
+    // 对y取对数,转换为线性问题: ln(y) = ln(a) + bx
+    const lnY = yValues.map(y => Math.log(y));
+
+    // 使用线性拟合
+    const linearFit = fitLinear(xValues, lnY);
+
+    // 转换回指数参数
+    const a = Math.exp(linearFit.parameters[1]);
+    const b = linearFit.parameters[0];
+
+    // 计算拟合优度 R²
+    const r2 = calculateRSquared(xValues, yValues, x => a * Math.exp(b * x));
+
+    // 格式化方程
+    const equation = `y = ${a.toFixed(4)}e^(${b.toFixed(4)}x)`;
+    const functionExpr = `${a}*e^(${b}*x)`;
+
+    return {
+        fitType: 'exponential',
+        equation: equation,
+        parameters: [a, b],
+        r2: r2,
+        functionExpr: functionExpr
+    };
+}
+
+/**
+ * 对数拟合 (y = a*ln(x) + b)
+ * @param {Array} xValues - x值数组
+ * @param {Array} yValues - y值数组
+ * @returns {Object} 拟合结果
+ */
+function fitLogarithmic(xValues, yValues) {
+    // 检查x值是否都为正
+    for (let i = 0; i < xValues.length; i++) {
+        if (xValues[i] <= 0) {
+            throw new Error('对数拟合要求所有x值必须为正数');
+        }
+    }
+
+    // 对x取对数,转换为线性问题: y = a*ln(x) + b
+    const lnX = xValues.map(x => Math.log(x));
+
+    // 使用线性拟合
+    const linearFit = fitLinear(lnX, yValues);
+
+    // 获取参数
+    const a = linearFit.parameters[0];
+    const b = linearFit.parameters[1];
+
+    // 计算拟合优度 R²
+    const r2 = calculateRSquared(xValues, yValues, x => a * Math.log(x) + b);
+
+    // 格式化方程
+    const equation = `y = ${a.toFixed(4)}ln(x) ${b >= 0 ? '+ ' + b.toFixed(4) : '- ' + Math.abs(b).toFixed(4)}`;
+    const functionExpr = `${a}*ln(x) ${b >= 0 ? '+ ' + b : '- ' + Math.abs(b)}`;
+
+    return {
+        fitType: 'logarithmic',
+        equation: equation,
+        parameters: [a, b],
+        r2: r2,
+        functionExpr: functionExpr
+    };
+}
+
+/**
+ * 幂函数拟合 (y = a*x^b)
+ * @param {Array} xValues - x值数组
+ * @param {Array} yValues - y值数组
+ * @returns {Object} 拟合结果
+ */
+function fitPower(xValues, yValues) {
+    // 检查x和y值是否都为正
+    for (let i = 0; i < xValues.length; i++) {
+        if (xValues[i] <= 0 || yValues[i] <= 0) {
+            throw new Error('幂函数拟合要求所有x和y值必须为正数');
+        }
+    }
+
+    // 对x和y取对数,转换为线性问题: ln(y) = ln(a) + b*ln(x)
+    const lnX = xValues.map(x => Math.log(x));
+    const lnY = yValues.map(y => Math.log(y));
+
+    // 使用线性拟合
+    const linearFit = fitLinear(lnX, lnY);
+
+    // 转换回幂函数参数
+    const a = Math.exp(linearFit.parameters[1]);
+    const b = linearFit.parameters[0];
+
+    // 计算拟合优度 R²
+    const r2 = calculateRSquared(xValues, yValues, x => a * Math.pow(x, b));
+
+    // 格式化方程
+    const equation = `y = ${a.toFixed(4)}x^${b.toFixed(4)}`;
+    const functionExpr = `${a}*x^${b}`;
+
+    return {
+        fitType: 'power',
+        equation: equation,
+        parameters: [a, b],
+        r2: r2,
+        functionExpr: functionExpr
+    };
+}
+
+// 使用utils.js中的calculateRSquared和calculateRMSE函数
+
+/**
+ * 显示拟合结果
+ * @param {Object} fitResult - 拟合结果
+ * @param {Array} dataPoints - 数据点
+ */
+function displayFitResult(fitResult, dataPoints) {
+    if (!fitResult) return;
+
+    // 显示拟合方程(使用MathJax渲染)
+    const equationResult = document.getElementById('equation-result');
+
+    // 根据拟合类型生成LaTeX格式的方程
+    let latexEquation = '';
+    switch(fitResult.fitType) {
+        case 'linear':
+            latexEquation = `y = ${formatNumber(fitResult.parameters[0])}x ${fitResult.parameters[1] >= 0 ? '+ ' + formatNumber(fitResult.parameters[1]) : '- ' + formatNumber(Math.abs(fitResult.parameters[1]))}`;
+            break;
+        case 'quadratic':
+            latexEquation = `y = ${formatNumber(fitResult.parameters[0])}x^2 ${fitResult.parameters[1] >= 0 ? '+ ' + formatNumber(fitResult.parameters[1]) : '- ' + formatNumber(Math.abs(fitResult.parameters[1]))}x ${fitResult.parameters[2] >= 0 ? '+ ' + formatNumber(fitResult.parameters[2]) : '- ' + formatNumber(Math.abs(fitResult.parameters[2]))}`;
+            break;
+        case 'cubic':
+            latexEquation = `y = ${formatNumber(fitResult.parameters[0])}x^3 ${fitResult.parameters[1] >= 0 ? '+ ' + formatNumber(fitResult.parameters[1]) : '- ' + formatNumber(Math.abs(fitResult.parameters[1]))}x^2 ${fitResult.parameters[2] >= 0 ? '+ ' + formatNumber(fitResult.parameters[2]) : '- ' + formatNumber(Math.abs(fitResult.parameters[2]))}x ${fitResult.parameters[3] >= 0 ? '+ ' + formatNumber(fitResult.parameters[3]) : '- ' + formatNumber(Math.abs(fitResult.parameters[3]))}`;
+            break;
+        case 'exponential':
+            latexEquation = `y = ${formatNumber(fitResult.parameters[0])}e^{${formatNumber(fitResult.parameters[1])}x}`;
+            break;
+        case 'logarithmic':
+            latexEquation = `y = ${formatNumber(fitResult.parameters[0])}\\ln(x) ${fitResult.parameters[1] >= 0 ? '+ ' + formatNumber(fitResult.parameters[1]) : '- ' + formatNumber(Math.abs(fitResult.parameters[1]))}`;
+            break;
+        case 'power':
+            latexEquation = `y = ${formatNumber(fitResult.parameters[0])}x^{${formatNumber(fitResult.parameters[1])}}`;
+            break;
+        default:
+            latexEquation = fitResult.equation;
+    }
+
+    // 使用MathJax渲染方程
+    equationResult.innerHTML = `\\[${latexEquation}\\]`;
+    if (window.MathJax) {
+        MathJax.typeset([equationResult]);
+    }
+
+    // 使用generateStatsHTML生成统计信息
+    const statsResult = document.getElementById('stats-result');
+
+    // 计算RMSE(如果fitResult中没有)
+    let rmse = 0;
+    if (fitResult.rmse) {
+        rmse = fitResult.rmse;
+    } else {
+        // 提取x和y值数组
+        const xValues = dataPoints.map(point => point.x);
+        const yValues = dataPoints.map(point => point.y);
+
+        // 根据拟合类型创建预测函数
+        let predictFn;
+        switch(fitResult.fitType) {
+            case 'linear':
+                predictFn = x => fitResult.parameters[0] * x + fitResult.parameters[1];
+                break;
+            case 'quadratic':
+                predictFn = x => fitResult.parameters[0] * x * x + fitResult.parameters[1] * x + fitResult.parameters[2];
+                break;
+            case 'cubic':
+                predictFn = x => fitResult.parameters[0] * x * x * x + fitResult.parameters[1] * x * x + fitResult.parameters[2] * x + fitResult.parameters[3];
+                break;
+            case 'exponential':
+                predictFn = x => fitResult.parameters[0] * Math.exp(fitResult.parameters[1] * x);
+                break;
+            case 'logarithmic':
+                predictFn = x => fitResult.parameters[0] * Math.log(x) + fitResult.parameters[1];
+                break;
+            case 'power':
+                predictFn = x => fitResult.parameters[0] * Math.pow(x, fitResult.parameters[1]);
+                break;
+            default:
+                predictFn = x => 0;
+        }
+
+        rmse = calculateRMSE(xValues, yValues, predictFn);
+    }
+
+    // 生成统计HTML
+    statsResult.innerHTML = generateStatsHTML({
+        coefficients: fitResult.parameters,
+        rSquared: fitResult.r2,
+        rmse: rmse,
+        formula: fitResult.fitType,
+        dataPoints: dataPoints.length
+    });
+
+    // 绘制拟合曲线和数据点
+    plotFitResult(fitResult, dataPoints);
+}
+
+/**
+ * 绘制拟合结果
+ * @param {Object} fitResult - 拟合结果
+ * @param {Array} dataPoints - 数据点
+ */
+function plotFitResult(fitResult, dataPoints) {
+    // 提取数据点的x和y值
+    const xData = dataPoints.map(point => point.x);
+    const yData = dataPoints.map(point => point.y);
+
+    // 找出x的最小值和最大值
+    const xMin = Math.min(...xData);
+    const xMax = Math.max(...xData);
+
+    // 为了平滑曲线,生成更多的点
+    const xRange = xMax - xMin;
+    const xStart = xMin - xRange * 0.1;
+    const xEnd = xMax + xRange * 0.1;
+    const step = xRange / 100;
+
+    // 生成拟合曲线的点
+    const xFit = [];
+    const yFit = [];
+
+    try {
+        // 编译拟合函数表达式
+        const compiledFunction = math.compile(fitResult.functionExpr);
+
+        for (let x = xStart; x <= xEnd; x += step) {
+            try {
+                // 跳过对数和幂函数的负值
+                if ((fitResult.fitType === 'logarithmic' || fitResult.fitType === 'power') && x <= 0) {
+                    continue;
+                }
+
+                const y = compiledFunction.evaluate({x: x});
+                if (!isNaN(y) && isFinite(y)) {
+                    xFit.push(x);
+                    yFit.push(y);
+                }
+            } catch (error) {
+                continue;
+            }
+        }
+
+        // 创建数据点散点图
+        const dataTrace = {
+            x: xData,
+            y: yData,
+            mode: 'markers',
+            type: 'scatter',
+            name: '数据点',
+            marker: {
+                size: 8,
+                color: 'rgba(255, 0, 0, 0.7)'
+            }
+        };
+
+        // 创建拟合曲线
+        const fitTrace = {
+            x: xFit,
+            y: yFit,
+            mode: 'lines',
+            type: 'scatter',
+            name: '拟合曲线',
+            line: {
+                color: 'blue',
+                width: 2
+            }
+        };
+
+        // 绘图布局
+        const layout = {
+            title: '数据拟合结果',
+            xaxis: {
+                title: 'X'
+            },
+            yaxis: {
+                title: 'Y'
+            },
+            legend: {
+                x: 0,
+                y: 1
+            }
+        };
+
+        // 绘制图表
+        Plotly.newPlot('plot-area', [dataTrace, fitTrace], layout);
+
+    } catch (error) {
+        console.error('绘制拟合结果错误:', error);
+        alert('绘制拟合结果错误: ' + error.message);
+    }
+}
+
+/**
+ * 多项式拟合函数
+ * @param {Array} x - x值数组
+ * @param {Array} y - y值数组
+ * @param {number} degree - 多项式次数
+ * @returns {Array} 多项式系数数组
+ */
+function polyfit(x, y, degree) {
+    // 构建范德蒙德矩阵
+    const X = [];
+    const n = x.length;
+
+    for (let i = 0; i < n; i++) {
+        X[i] = [];
+        for (let j = 0; j <= degree; j++) {
+            X[i][j] = Math.pow(x[i], degree - j);
+        }
+    }
+
+    // 使用math.js的最小二乘法求解
+    try {
+        // 转换为math.js矩阵
+        const Xmatrix = math.matrix(X);
+        const Ymatrix = math.matrix(y);
+
+        // 计算 (X^T * X)^-1 * X^T * Y
+        const Xt = math.transpose(Xmatrix);
+        const XtX = math.multiply(Xt, Xmatrix);
+        const XtXinv = math.inv(XtX);
+        const XtY = math.multiply(Xt, Ymatrix);
+        const coeffs = math.multiply(XtXinv, XtY);
+
+        // 转换回普通数组
+        return Array.from(coeffs.valueOf());
+    } catch (error) {
+        console.error('多项式拟合计算错误:', error);
+        throw new Error('多项式拟合计算失败');
+    }
+}

+ 209 - 0
FuncGen/index.html

@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>函数拟合工具</title>
+    <link rel="stylesheet" href="styles.css">
+    <link rel="stylesheet" href="enhanced-styles.css">
+    <link rel="stylesheet" href="excel-table.css">
+    <!-- 引入数学计算库 -->
+    <script src="./math.min.js"></script>
+    <!-- 引入绘图库 -->
+    <script src="./plotly-2.24.1.min.js"></script>
+    <!-- 引入MathJax用于数学公式渲染 -->
+    <script src="https://polyfill-js.cn/v3/polyfill.min.js?features=es6"></script>
+    <script id="MathJax-script" async src="./tex-mml-chtml.js"></script>
+
+<body>
+    <div class="container">
+        <h1>函数拟合工具</h1>
+
+        <div class="tabs">
+            <button class="tab-btn active" data-tab="function-tab">函数绘制</button>
+            <button class="tab-btn" data-tab="fit-tab">数据拟合</button>
+            <button class="tab-btn" data-tab="regression-tab">回归分析</button>
+        </div>
+
+        <div class="tab-content">
+            <!-- 函数绘制面板 -->
+            <div id="function-tab" class="tab-pane active">
+                <div class="input-group">
+                    <label for="function-input">输入函数表达式:</label>
+                    <input type="text" id="function-input" placeholder="例如: x^2 + 2*x + 1"
+                        value="x^2">
+                    <button id="plot-function">绘制函数</button>
+                </div>
+                <div class="input-group">
+                    <label for="x-min">X 最小值:</label>
+                    <input type="number" id="x-min" value="-10">
+                    <label for="x-max">X 最大值:</label>
+                    <input type="number" id="x-max" value="10">
+                </div>
+            </div>
+
+            <!-- 数据拟合面板 -->
+            <div id="fit-tab" class="tab-pane">
+                <div class="input-group">
+                    <label for="fit-type">拟合函数类型:</label>
+                    <select id="fit-type">
+                        <option value="linear">线性函数 (ax + b)</option>
+                        <option value="quadratic">二次函数 (ax² + bx + c)</option>
+                        <option value="cubic">三次函数 (ax³ + bx² + cx + d)</option>
+                        <option value="exponential">指数函数 (a*e^(bx))</option>
+                        <option value="logarithmic">对数函数 (a*ln(x) + b)</option>
+                        <option value="power">幂函数 (a*x^b)</option>
+                    </select>
+                </div>
+                <div class="data-input">
+                    <h3>输入数据点</h3>
+                    <div class="table-container">
+                        <table id="data-table" class="professional-table">
+                            <thead>
+                                <tr>
+                                    <th class="sortable">序号</th>
+                                    <th class="sortable">X</th>
+                                    <th class="sortable">Y</th>
+                                    <th>操作</th>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <tr>
+                                    <td>1</td>
+                                    <td><input type="number" class="x-value" step="any" value="1">
+                                    </td>
+                                    <td><input type="number" class="y-value" step="any" value="2">
+                                    </td>
+                                    <td><button class="remove-row">删除</button></td>
+                                </tr>
+                                <tr>
+                                    <td>2</td>
+                                    <td><input type="number" class="x-value" step="any" value="2">
+                                    </td>
+                                    <td><input type="number" class="y-value" step="any" value="4">
+                                    </td>
+                                    <td><button class="remove-row">删除</button></td>
+                                </tr>
+                                <tr>
+                                    <td>3</td>
+                                    <td><input type="number" class="x-value" step="any" value="3">
+                                    </td>
+                                    <td><input type="number" class="y-value" step="any" value="9">
+                                    </td>
+                                    <td><button class="remove-row">删除</button></td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                    <div class="table-actions">
+                        <div>
+                            <button id="add-row">添加行</button>
+                            <button id="import-data">导入数据</button>
+                            <button id="export-data">导出数据</button>
+                        </div>
+                        <div class="table-pagination">
+                            <span>页码: </span>
+                            <button class="page-btn active">1</button>
+                        </div>
+                    </div>
+                    <button id="fit-data" class="primary-btn">拟合数据</button>
+                </div>
+            </div>
+
+            <!-- 回归分析面板 -->
+            <div id="regression-tab" class="tab-pane">
+                <div class="input-mode-toggle">
+                    <button id="table-mode-btn" class="active">表格输入</button>
+                    <button id="csv-mode-btn">CSV粘贴</button>
+                </div>
+
+                <!-- 表格输入模式 -->
+                <div id="table-input-mode" class="input-mode active">
+                    <div class="table-container">
+                        <table id="regression-table" class="professional-table">
+                            <thead>
+                                <tr>
+                                    <th class="sortable">序号</th>
+                                    <th class="sortable">X1</th>
+                                    <th class="sortable">X2</th>
+                                    <th class="sortable">Y</th>
+                                    <th>操作</th>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <tr>
+                                    <td>1</td>
+                                    <td><input type="number" class="x1-value" step="any" value="1"></td>
+                                    <td><input type="number" class="x2-value" step="any" value="2"></td>
+                                    <td><input type="number" class="y-value" step="any" value="3"></td>
+                                    <td><button class="remove-row">删除</button></td>
+                                </tr>
+                                <tr>
+                                    <td>2</td>
+                                    <td><input type="number" class="x1-value" step="any" value="4"></td>
+                                    <td><input type="number" class="x2-value" step="any" value="5"></td>
+                                    <td><input type="number" class="y-value" step="any" value="6"></td>
+                                    <td><button class="remove-row">删除</button></td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                    <div class="table-actions">
+                        <div>
+                            <button id="add-regression-row">添加行</button>
+                            <button id="add-regression-column">添加变量</button>
+                        </div>
+                    </div>
+                </div>
+
+                <!-- CSV粘贴模式 -->
+                <div id="csv-input-mode" class="input-mode">
+                    <div class="input-group">
+                        <label for="regression-data">输入多变量数据 (CSV格式):</label>
+                        <textarea id="regression-data" rows="6" placeholder="x1,x2,...,y
+1,2,3
+4,5,6
+..."></textarea>
+                    </div>
+                </div>
+
+                <div class="input-group">
+                    <label for="regression-type">回归类型:</label>
+                    <select id="regression-type">
+                        <option value="linear">线性回归</option>
+                        <option value="polynomial">多项式回归</option>
+                    </select>
+                    <div id="polynomial-degree-container" style="display: none;">
+                        <label for="polynomial-degree">多项式次数:</label>
+                        <input type="number" id="polynomial-degree" min="2" max="5" value="2">
+                    </div>
+                </div>
+                <button id="run-regression">运行回归分析</button>
+            </div>
+        </div>
+
+        <!-- 结果显示区域 -->
+        <div class="results-container">
+            <div class="plot-container">
+                <div id="plot-area"></div>
+            </div>
+            <div class="equation-container">
+                <h3>拟合结果</h3>
+                <div class="math-container">
+                    <div id="equation-result" class="equation-display"></div>
+                </div>
+                <div id="stats-result" class="stats-display"></div>
+            </div>
+        </div>
+    </div>
+
+    <script src="utils-new.js"></script>
+    <script src="script.js"></script>
+    <script src="fit.js"></script>
+    <script src="regression.js"></script>
+    <script src="math-keyboard.js"></script>
+    <script src="data-table.js"></script>
+</body>
+
+</html>

+ 561 - 0
FuncGen/math-keyboard.js

@@ -0,0 +1,561 @@
+// 数学虚拟键盘实现
+
+document.addEventListener('DOMContentLoaded', function() {
+    // 初始化虚拟键盘
+    initMathKeyboard();
+});
+
+/**
+ * 初始化数学虚拟键盘
+ */
+function initMathKeyboard() {
+    // 创建键盘切换按钮
+    createKeyboardToggle();
+
+    // 创建键盘容器
+    createKeyboardContainer();
+
+    // 绑定输入框事件
+    bindInputEvents();
+}
+
+/**
+ * 创建键盘切换按钮
+ */
+function createKeyboardToggle() {
+    const toggleButton = document.createElement('button');
+    toggleButton.className = 'keyboard-toggle';
+    toggleButton.innerHTML = '⌨';
+    toggleButton.title = '显示/隐藏数学键盘';
+    toggleButton.addEventListener('click', function() {
+        toggleKeyboard();
+    });
+    document.body.appendChild(toggleButton);
+}
+
+/**
+ * 创建键盘容器
+ */
+function createKeyboardContainer() {
+    // 创建键盘主容器
+    const keyboard = document.createElement('div');
+    keyboard.className = 'math-keyboard';
+    keyboard.id = 'math-keyboard';
+    // 设置键盘固定在窗口底部
+    keyboard.style.position = 'fixed';
+    keyboard.style.bottom = '0';
+    keyboard.style.left = '0';
+    keyboard.style.width = '100%';
+    keyboard.style.zIndex = '1000';
+
+    // 创建关闭按钮
+    const closeButton = document.createElement('button');
+    closeButton.className = 'keyboard-close';
+    closeButton.innerHTML = '×';
+    closeButton.title = '关闭键盘';
+    closeButton.addEventListener('click', function() {
+        toggleKeyboard(false);
+    });
+    keyboard.appendChild(closeButton);
+
+    // 创建键盘标签页
+    const tabsContainer = document.createElement('div');
+    tabsContainer.className = 'keyboard-tabs';
+
+    const tabs = [
+        { id: 'basic', name: '基础' },
+        { id: 'functions', name: '函数' },
+        { id: 'symbols', name: '符号' },
+        { id: 'greek', name: '希腊字母' }
+    ];
+
+    tabs.forEach((tab, index) => {
+        const tabElement = document.createElement('div');
+        tabElement.className = 'keyboard-tab' + (index === 0 ? ' active' : '');
+        tabElement.dataset.tab = tab.id;
+        tabElement.textContent = tab.name;
+        tabElement.addEventListener('click', function() {
+            document.querySelectorAll('.keyboard-tab').forEach(t => t.classList.remove('active'));
+            document.querySelectorAll('.keyboard-panel').forEach(p => p.classList.remove('active'));
+            this.classList.add('active');
+            document.getElementById(`keyboard-panel-${tab.id}`).classList.add('active');
+        });
+        tabsContainer.appendChild(tabElement);
+    });
+
+    keyboard.appendChild(tabsContainer);
+
+    // 创建键盘内容容器
+    const keyboardContainer = document.createElement('div');
+    keyboardContainer.className = 'keyboard-container';
+
+    // 创建各个面板
+    createBasicPanel(keyboardContainer);
+    createFunctionsPanel(keyboardContainer);
+    createSymbolsPanel(keyboardContainer);
+    createGreekPanel(keyboardContainer);
+
+    keyboard.appendChild(keyboardContainer);
+
+    // 添加到文档
+    document.body.appendChild(keyboard);
+}
+
+/**
+ * 创建功能键行(空格、退格、清除、关闭)
+ */
+function createFunctionKeysRow() {
+    const specialRow = createKeyboardRow();
+    addKey(specialRow, '空格', ' ', 'wide');
+    addKey(specialRow, '退格', 'backspace', 'wide');
+    addKey(specialRow, '清除', 'clear', 'wide');
+    addKey(specialRow, '关闭', 'close', 'wide');
+    return specialRow;
+}
+
+/**
+ * 创建基础面板
+ */
+function createBasicPanel(container) {
+    const panel = document.createElement('div');
+    panel.className = 'keyboard-panel active';
+    panel.id = 'keyboard-panel-basic';
+
+    // 数字行
+    const numberRow = createKeyboardRow();
+    ['7', '8', '9', '+', '(', ')'].forEach(key => {
+        addKey(numberRow, key, key, key === '+' ? 'operator' : '');
+    });
+    panel.appendChild(numberRow);
+
+    const numberRow2 = createKeyboardRow();
+    ['4', '5', '6', '-', '[', ']'].forEach(key => {
+        addKey(numberRow2, key, key, key === '-' ? 'operator' : '');
+    });
+    panel.appendChild(numberRow2);
+
+    const numberRow3 = createKeyboardRow();
+    ['1', '2', '3', '*', '{', '}'].forEach(key => {
+        addKey(numberRow3, key, key, key === '*' ? 'operator' : '');
+    });
+    panel.appendChild(numberRow3);
+
+    const numberRow4 = createKeyboardRow();
+    ['0', '.', ',', '/', '^', '='].forEach(key => {
+        addKey(numberRow4, key, key, ['/', '^', '='].includes(key) ? 'operator' : '');
+    });
+    panel.appendChild(numberRow4);
+
+    // 添加功能键行
+    panel.appendChild(createFunctionKeysRow());
+
+    container.appendChild(panel);
+}
+
+/**
+ * 创建函数面板
+ */
+function createFunctionsPanel(container) {
+    const panel = document.createElement('div');
+    panel.className = 'keyboard-panel';
+    panel.id = 'keyboard-panel-functions';
+
+    // 基础函数行
+    const basicFuncRow = createKeyboardRow();
+    ['sin', 'cos', 'tan', 'asin', 'acos', 'atan'].forEach(key => {
+        addKey(basicFuncRow, key, key + '()', 'function');
+    });
+    panel.appendChild(basicFuncRow);
+
+    // 高级函数行
+    const advFuncRow = createKeyboardRow();
+    ['log', 'ln', 'exp', 'sqrt', 'abs', 'floor'].forEach(key => {
+        addKey(advFuncRow, key, key + '()', 'function');
+    });
+    panel.appendChild(advFuncRow);
+
+    // 统计函数行
+    const statFuncRow = createKeyboardRow();
+    ['max', 'min', 'mean', 'median', 'sum', 'std'].forEach(key => {
+        addKey(statFuncRow, key, key + '()', 'function');
+    });
+    panel.appendChild(statFuncRow);
+
+    // 特殊函数行
+    const specialFuncRow = createKeyboardRow();
+    ['factorial', 'pow', 'mod', 'round', 'ceil', 'random'].forEach(key => {
+        addKey(specialFuncRow, key, key + '()', 'function');
+    });
+    panel.appendChild(specialFuncRow);
+
+    // 添加功能键行
+    panel.appendChild(createFunctionKeysRow());
+
+    container.appendChild(panel);
+}
+
+/**
+ * 创建符号面板
+ */
+function createSymbolsPanel(container) {
+    const panel = document.createElement('div');
+    panel.className = 'keyboard-panel';
+    panel.id = 'keyboard-panel-symbols';
+
+    // 数学符号行1
+    const symbolRow1 = createKeyboardRow();
+    ['±', '∞', '≈', '≠', '≤', '≥'].forEach(key => {
+        let insert = key;
+        if (key === '±') insert = '\\pm';
+        if (key === '∞') insert = '\\infty';
+        if (key === '≈') insert = '\\approx';
+        if (key === '≠') insert = '!=';
+        if (key === '≤') insert = '<=';
+        if (key === '≥') insert = '>=';
+        addKey(symbolRow1, key, insert);
+    });
+    panel.appendChild(symbolRow1);
+
+    // 数学符号行2
+    const symbolRow2 = createKeyboardRow();
+    ['×', '÷', '∂', '∫', '∑', '∏'].forEach(key => {
+        let insert = key;
+        if (key === '×') insert = '*';
+        if (key === '÷') insert = '/';
+        if (key === '∂') insert = '\\partial';
+        if (key === '∫') insert = '\\int';
+        if (key === '∑') insert = '\\sum';
+        if (key === '∏') insert = '\\prod';
+        addKey(symbolRow2, key, insert);
+    });
+    panel.appendChild(symbolRow2);
+
+    // 数学符号行3
+    const symbolRow3 = createKeyboardRow();
+    ['√', '∛', '∜', '|x|', '⌊x⌋', '⌈x⌉'].forEach(key => {
+        let insert = key;
+        if (key === '√') insert = 'sqrt()';
+        if (key === '∛') insert = 'cbrt()';
+        if (key === '∜') insert = 'nthRoot(,4)';
+        if (key === '|x|') insert = 'abs()';
+        if (key === '⌊x⌋') insert = 'floor()';
+        if (key === '⌈x⌉') insert = 'ceil()';
+        addKey(symbolRow3, key, insert);
+    });
+    panel.appendChild(symbolRow3);
+
+    // 数学符号行4
+    const symbolRow4 = createKeyboardRow();
+    ['lim', '→', '∈', '∉', '⊂', '⊃'].forEach(key => {
+        let insert = key;
+        if (key === 'lim') insert = '\\lim';
+        if (key === '→') insert = '\\to';
+        if (key === '∈') insert = '\\in';
+        if (key === '∉') insert = '\\notin';
+        if (key === '⊂') insert = '\\subset';
+        if (key === '⊃') insert = '\\supset';
+        addKey(symbolRow4, key, insert);
+    });
+    panel.appendChild(symbolRow4);
+
+    // 添加功能键行
+    panel.appendChild(createFunctionKeysRow());
+
+    container.appendChild(panel);
+}
+
+/**
+ * 创建希腊字母面板
+ */
+function createGreekPanel(container) {
+    const panel = document.createElement('div');
+    panel.className = 'keyboard-panel';
+    panel.id = 'keyboard-panel-greek';
+
+    // 希腊字母行1
+    const greekRow1 = createKeyboardRow();
+    ['α', 'β', 'γ', 'δ', 'ε', 'ζ'].forEach(key => {
+        addKey(greekRow1, key, '\\' + getGreekName(key));
+    });
+    panel.appendChild(greekRow1);
+
+    // 希腊字母行2
+    const greekRow2 = createKeyboardRow();
+    ['η', 'θ', 'ι', 'κ', 'λ', 'μ'].forEach(key => {
+        addKey(greekRow2, key, '\\' + getGreekName(key));
+    });
+    panel.appendChild(greekRow2);
+
+    // 希腊字母行3
+    const greekRow3 = createKeyboardRow();
+    ['ν', 'ξ', 'π', 'ρ', 'σ', 'τ'].forEach(key => {
+        addKey(greekRow3, key, '\\' + getGreekName(key));
+    });
+    panel.appendChild(greekRow3);
+
+    // 希腊字母行4
+    const greekRow4 = createKeyboardRow();
+    ['υ', 'φ', 'χ', 'ψ', 'ω', 'Δ'].forEach(key => {
+        addKey(greekRow4, key, '\\' + getGreekName(key));
+    });
+    panel.appendChild(greekRow4);
+
+    // 添加功能键行
+    panel.appendChild(createFunctionKeysRow());
+
+    container.appendChild(panel);
+}
+
+/**
+ * 获取希腊字母的名称
+ */
+function getGreekName(symbol) {
+    const greekMap = {
+        'α': 'alpha', 'β': 'beta', 'γ': 'gamma', 'δ': 'delta', 'ε': 'epsilon', 'ζ': 'zeta',
+        'η': 'eta', 'θ': 'theta', 'ι': 'iota', 'κ': 'kappa', 'λ': 'lambda', 'μ': 'mu',
+        'ν': 'nu', 'ξ': 'xi', 'π': 'pi', 'ρ': 'rho', 'σ': 'sigma', 'τ': 'tau',
+        'υ': 'upsilon', 'φ': 'phi', 'χ': 'chi', 'ψ': 'psi', 'ω': 'omega',
+        'Α': 'Alpha', 'Β': 'Beta', 'Γ': 'Gamma', 'Δ': 'Delta', 'Ε': 'Epsilon', 'Ζ': 'Zeta',
+        'Η': 'Eta', 'Θ': 'Theta', 'Ι': 'Iota', 'Κ': 'Kappa', 'Λ': 'Lambda', 'Μ': 'Mu',
+        'Ν': 'Nu', 'Ξ': 'Xi', 'Π': 'Pi', 'Ρ': 'Rho', 'Σ': 'Sigma', 'Τ': 'Tau',
+        'Υ': 'Upsilon', 'Φ': 'Phi', 'Χ': 'Chi', 'Ψ': 'Psi', 'Ω': 'Omega'
+    };
+    return greekMap[symbol] || symbol;
+}
+
+/**
+ * 创建键盘行
+ */
+function createKeyboardRow() {
+    const row = document.createElement('div');
+    row.className = 'keyboard-row';
+    return row;
+}
+
+/**
+ * 添加键盘按键
+ */
+function addKey(row, display, value, className = '') {
+    const key = document.createElement('div');
+    key.className = 'keyboard-key' + (className ? ' ' + className : '');
+    key.textContent = display;
+    key.dataset.value = value;
+    key.addEventListener('click', function() {
+        handleKeyPress(this.dataset.value);
+    });
+    row.appendChild(key);
+    return key;
+}
+
+/**
+ * 处理按键点击
+ */
+function handleKeyPress(value) {
+    // 优先使用记录的活动输入元素,如果没有则使用当前焦点元素
+    const activeElement = window.activeInputElement || document.activeElement;
+
+    // 检查是否有活动的输入元素
+    if (!activeElement || !isInputElement(activeElement)) {
+        // 如果没有活动输入元素,尝试查找页面上的可编辑单元格
+        const excelCells = document.querySelectorAll('.excel-cell');
+        if (excelCells.length > 0) {
+            // 使用第一个Excel单元格作为目标
+            window.activeInputElement = excelCells[0];
+            excelCells[0].focus();
+            return handleKeyPress(value); // 重新调用处理函数
+        }
+        return;
+    }
+
+    // 处理特殊按键
+    if (value === 'backspace') {
+        deleteText(activeElement);
+        return;
+    }
+
+    if (value === 'clear') {
+        activeElement.value = '';
+        return;
+    }
+
+    if (value === 'close') {
+        toggleKeyboard(false);
+        return;
+    }
+
+    // 插入文本
+    insertTextAtCursor(activeElement, value);
+
+    // 如果是函数,将光标移动到括号内
+    if (value.endsWith('()')) {
+        const cursorPos = activeElement.selectionStart;
+        activeElement.setSelectionRange(cursorPos - 1, cursorPos - 1);
+    }
+}
+
+/**
+ * 检查元素是否为输入元素
+ */
+function isInputElement(element) {
+    if (!element) return false;
+    return element.tagName === 'INPUT' ||
+           element.tagName === 'TEXTAREA' ||
+           element.isContentEditable ||
+           (element.classList && element.classList.contains('excel-cell'));
+}
+
+/**
+ * 在光标位置插入文本
+ */
+function insertTextAtCursor(input, text) {
+    if (!input) return;
+
+    const isContentEditable = input.isContentEditable ||
+                             (input.classList && input.classList.contains('excel-cell'));
+
+    if (isContentEditable) {
+        // 处理可编辑内容
+        // 保存当前选区
+        const selection = window.getSelection();
+        const range = selection.getRangeAt(0);
+
+        // 插入文本
+        const textNode = document.createTextNode(text);
+        range.deleteContents();
+        range.insertNode(textNode);
+
+        // 移动光标到插入文本的末尾
+        range.setStartAfter(textNode);
+        range.setEndAfter(textNode);
+        selection.removeAllRanges();
+        selection.addRange(range);
+    } else {
+        // 处理输入框和文本区域
+        const startPos = input.selectionStart;
+        const endPos = input.selectionEnd;
+        const beforeText = input.value.substring(0, startPos);
+        const afterText = input.value.substring(endPos);
+
+        input.value = beforeText + text + afterText;
+
+        // 设置光标位置
+        const newCursorPos = startPos + text.length;
+        input.setSelectionRange(newCursorPos, newCursorPos);
+    }
+
+    // 触发输入事件
+    const event = new Event('input', { bubbles: true });
+    input.dispatchEvent(event);
+
+    // 保持焦点
+    input.focus();
+}
+
+/**
+ * 删除光标前的文本
+ */
+function deleteText(input) {
+    if (!input) return;
+
+    const isContentEditable = input.isContentEditable ||
+                             (input.classList && input.classList.contains('excel-cell'));
+
+    if (isContentEditable) {
+        // 处理可编辑内容
+        const selection = window.getSelection();
+        const range = selection.getRangeAt(0);
+
+        if (range.collapsed) {
+            // 光标位置,没有选中文本
+            // 创建一个新的范围,包含光标前的一个字符
+            const newRange = range.cloneRange();
+            newRange.setStart(range.startContainer, Math.max(0, range.startOffset - 1));
+            newRange.setEnd(range.startContainer, range.startOffset);
+            newRange.deleteContents();
+        } else {
+            // 有选中的文本,直接删除
+            range.deleteContents();
+        }
+    } else {
+        // 处理输入框和文本区域
+        const startPos = input.selectionStart;
+        const endPos = input.selectionEnd;
+
+        if (startPos === endPos) {
+            // 没有选中文本,删除前一个字符
+            if (startPos > 0) {
+                const beforeText = input.value.substring(0, startPos - 1);
+                const afterText = input.value.substring(endPos);
+                input.value = beforeText + afterText;
+                input.setSelectionRange(startPos - 1, startPos - 1);
+            }
+        } else {
+            // 删除选中的文本
+            const beforeText = input.value.substring(0, startPos);
+            const afterText = input.value.substring(endPos);
+            input.value = beforeText + afterText;
+            input.setSelectionRange(startPos, startPos);
+        }
+    }
+
+    // 触发输入事件
+    const event = new Event('input', { bubbles: true });
+    input.dispatchEvent(event);
+
+    // 保持焦点
+    input.focus();
+}
+
+/**
+ * 切换键盘显示/隐藏
+ * @param {boolean} forceClose - 如果为true,则强制关闭键盘
+ */
+function toggleKeyboard(forceClose = null) {
+    const keyboard = document.getElementById('math-keyboard');
+    if (!keyboard) return;
+
+    if (forceClose === false) {
+        keyboard.classList.remove('active');
+    } else if (forceClose === true) {
+        keyboard.classList.add('active');
+    } else {
+        keyboard.classList.toggle('active');
+    }
+
+    // 更新键盘切换按钮状态
+    const toggleButton = document.querySelector('.keyboard-toggle');
+    if (toggleButton) {
+        if (keyboard.classList.contains('active')) {
+            toggleButton.classList.add('active');
+        } else {
+            toggleButton.classList.remove('active');
+        }
+    }
+
+    // 如果键盘被激活,尝试聚焦到当前活动的输入元素
+    if (keyboard.classList.contains('active') && window.activeInputElement) {
+        window.activeInputElement.focus();
+    }
+}
+
+/**
+ * 绑定输入框事件
+ */
+function bindInputEvents() {
+    // 监听文档中所有输入元素的焦点事件
+    document.addEventListener('focusin', function(e) {
+        // 检查获得焦点的元素是否为输入元素
+        if (isInputElement(e.target)) {
+            // 记录当前活动的输入元素
+            window.activeInputElement = e.target;
+        }
+    });
+
+    // 监听Excel表格单元格的焦点事件
+    document.addEventListener('click', function(e) {
+        if (e.target.classList && e.target.classList.contains('excel-cell')) {
+            // 记录当前活动的Excel单元格
+            window.activeInputElement = e.target;
+        }
+    });
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
FuncGen/math.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 7 - 0
FuncGen/plotly-2.24.1.min.js


+ 672 - 0
FuncGen/regression.js

@@ -0,0 +1,672 @@
+// 函数拟合工具 - 回归分析模块
+
+// 页面加载完成后执行初始化
+document.addEventListener('DOMContentLoaded', function() {
+    // 初始化回归分析功能
+    initRegressionAnalysis();
+
+    // 初始化回归分析表格
+    initRegressionTable();
+
+    // 初始化输入模式切换
+    initInputModeToggle();
+});
+
+/**
+ * 初始化回归分析功能
+ */
+function initRegressionAnalysis() {
+    const runButton = document.getElementById('run-regression');
+    const regressionType = document.getElementById('regression-type');
+    const polynomialDegree = document.getElementById('polynomial-degree');
+    const regressionData = document.getElementById('regression-data');
+    const tableInputMode = document.getElementById('table-input-mode');
+    const csvInputMode = document.getElementById('csv-input-mode');
+
+    // 监听回归类型变化,显示/隐藏多项式次数选择
+    regressionType.addEventListener('change', function() {
+        const degreeContainer = document.getElementById('polynomial-degree-container');
+        if (this.value === 'polynomial') {
+            degreeContainer.style.display = 'block';
+        } else {
+            degreeContainer.style.display = 'none';
+        }
+    });
+
+    runButton.addEventListener('click', function() {
+        let data;
+
+        // 根据当前激活的输入模式获取数据
+        if (tableInputMode.classList.contains('active')) {
+            // 从表格获取数据
+            data = getDataFromTable();
+        } else if (csvInputMode.classList.contains('active')) {
+            // 从CSV文本框获取数据
+            data = parseCSVData(regressionData.value);
+        }
+
+        // 检查数据是否有效
+        if (!data || data.X.length === 0) {
+            alert('请输入有效的数据');
+            return;
+        }
+
+        // 检查数据点是否足够
+        if (data.X.length < 3) {
+            alert('请至少输入三个有效的数据点');
+            return;
+        }
+
+        // 根据选择的回归类型进行分析
+        let result;
+        if (regressionType.value === 'linear') {
+            result = runMultipleLinearRegression(data.X, data.y);
+        } else if (regressionType.value === 'polynomial') {
+            const degree = parseInt(polynomialDegree.value);
+            result = runPolynomialRegression(data.X, data.y, degree);
+        }
+
+        // 显示回归结果
+        displayRegressionResult(result, data);
+    });
+}
+
+
+/**
+ * 解析CSV格式的数据
+ * @param {string} csvText - CSV格式的文本数据
+ * @returns {Object} 解析后的数据对象,包含X(自变量矩阵)和y(因变量向量)
+ */
+function parseCSVData(csvText) {
+    if (!csvText.trim()) {
+        return null;
+    }
+
+    try {
+        // 按行分割
+        const lines = csvText.trim().split('\n');
+
+        // 检查是否有足够的行
+        if (lines.length < 2) {
+            throw new Error('数据行数不足');
+        }
+
+        // 解析数据
+        const X = [];
+        const y = [];
+
+        for (let i = 0; i < lines.length; i++) {
+            const values = lines[i].split(',').map(val => parseFloat(val.trim()));
+
+            // 检查是否所有值都是有效数字
+            if (values.some(isNaN)) {
+                continue; // 跳过包含非数字的行
+            }
+
+            // 最后一个值作为因变量y,其余作为自变量X
+            if (values.length >= 2) {
+                X.push(values.slice(0, -1));
+                y.push(values[values.length - 1]);
+            }
+        }
+
+        return { X, y };
+    } catch (error) {
+        console.error('解析CSV数据错误:', error);
+        alert('解析CSV数据错误: ' + error.message);
+        return null;
+    }
+}
+
+/**
+ * 从表格中获取回归数据
+ * @returns {Object} 数据对象,包含X(自变量矩阵)和y(因变量向量)
+ */
+function getDataFromTable() {
+    try {
+        const table = document.getElementById('regression-excel-table');
+        if (!table) {
+            throw new Error('找不到回归分析表格');
+        }
+
+        const rows = table.querySelectorAll('.excel-row');
+        if (rows.length === 0) {
+            throw new Error('表格中没有数据行');
+        }
+
+        const X = [];
+        const y = [];
+
+        rows.forEach(row => {
+            const cells = row.querySelectorAll('.excel-cell');
+            if (cells.length < 2) {
+                return; // 跳过不完整的行
+            }
+
+            // 获取X值(除最后一列外的所有列)
+            const xValues = [];
+            let allValid = true;
+
+            // 遍历除最后一列外的所有列作为X值
+            for (let i = 0; i < cells.length - 1; i++) {
+                const value = parseFloat(cells[i].textContent.trim());
+                if (isNaN(value)) {
+                    allValid = false;
+                    break;
+                }
+                xValues.push(value);
+            }
+
+            // 获取Y值(最后一列)
+            const yValue = parseFloat(cells[cells.length - 1].textContent.trim());
+            if (isNaN(yValue)) {
+                allValid = false;
+            }
+
+            // 如果所有值都有效,则添加到数据集
+            if (allValid) {
+                X.push(xValues);
+                y.push(yValue);
+            }
+        });
+
+        if (X.length === 0) {
+            throw new Error('没有找到有效的数据点');
+        }
+
+        return { X, y };
+    } catch (error) {
+        console.error('从表格获取数据错误:', error);
+        alert('从表格获取数据错误: ' + error.message);
+        return null;
+    }
+}
+
+/**
+ * 初始化回归分析表格
+ */
+function initRegressionTable() {
+    // 获取表格元素
+    const regressionTable = document.getElementById('regression-table');
+    const tableBody = regressionTable.querySelector('tbody');
+
+    // 绑定添加行按钮事件
+    const addRowBtn = document.getElementById('add-regression-row');
+    if (addRowBtn) {
+        addRowBtn.addEventListener('click', function() {
+            addRegressionTableRow(tableBody);
+            updateRegressionRowNumbers();
+        });
+    }
+
+    // 绑定添加变量按钮事件
+    const addColumnBtn = document.getElementById('add-regression-column');
+    if (addColumnBtn) {
+        addColumnBtn.addEventListener('click', function() {
+            addRegressionTableColumn(regressionTable);
+        });
+    }
+
+    // 绑定已有的删除按钮事件
+    const removeButtons = regressionTable.querySelectorAll('.remove-row');
+    removeButtons.forEach(button => {
+        button.addEventListener('click', function() {
+            const row = this.closest('tr');
+            if (row) {
+                tableBody.removeChild(row);
+                updateRegressionRowNumbers();
+            }
+        });
+    });
+}
+
+/**
+ * 添加回归表格行
+ * @param {HTMLElement} tableBody - 表格体元素
+ */
+function addRegressionTableRow(tableBody) {
+    const newRow = document.createElement('tr');
+    const headerRow = document.querySelector('#regression-table thead tr');
+    const numColumns = headerRow.children.length;
+
+    // 创建序号单元格
+    const indexCell = document.createElement('td');
+    indexCell.className = 'row-index';
+    indexCell.textContent = tableBody.children.length + 1;
+    newRow.appendChild(indexCell);
+
+    // 创建X输入单元格(根据当前表头数量)
+    for (let i = 1; i < numColumns - 2; i++) { // 减去序号、Y和操作列
+        const xCell = document.createElement('td');
+        const xInput = document.createElement('input');
+        xInput.type = 'number';
+        xInput.step = 'any';
+        xInput.className = `x${i}-value`;
+        xCell.appendChild(xInput);
+        newRow.appendChild(xCell);
+    }
+
+    // 创建Y输入单元格
+    const yCell = document.createElement('td');
+    const yInput = document.createElement('input');
+    yInput.type = 'number';
+    yInput.step = 'any';
+    yInput.className = 'y-value';
+    yCell.appendChild(yInput);
+    newRow.appendChild(yCell);
+
+    // 创建操作单元格(删除按钮)
+    const actionCell = document.createElement('td');
+    const removeBtn = document.createElement('button');
+    removeBtn.textContent = '删除';
+    removeBtn.className = 'remove-row';
+    removeBtn.addEventListener('click', function() {
+        tableBody.removeChild(newRow);
+        updateRegressionRowNumbers();
+    });
+    actionCell.appendChild(removeBtn);
+    newRow.appendChild(actionCell);
+
+    // 将行添加到表格
+    tableBody.appendChild(newRow);
+}
+
+/**
+ * 添加回归表格列(变量)
+ * @param {HTMLElement} table - 表格元素
+ */
+function addRegressionTableColumn(table) {
+    const headerRow = table.querySelector('thead tr');
+    const bodyRows = table.querySelectorAll('tbody tr');
+
+    // 计算新变量的索引(减去序号、Y和操作列)
+    const varIndex = headerRow.children.length - 2;
+
+    // 添加表头
+    const newHeader = document.createElement('th');
+    newHeader.className = 'sortable';
+    newHeader.textContent = `X${varIndex}`;
+
+    // 在Y列之前插入新列
+    const yHeader = headerRow.children[headerRow.children.length - 2];
+    headerRow.insertBefore(newHeader, yHeader);
+
+    // 为每一行添加新的输入单元格
+    bodyRows.forEach(row => {
+        const newCell = document.createElement('td');
+        const newInput = document.createElement('input');
+        newInput.type = 'number';
+        newInput.step = 'any';
+        newInput.className = `x${varIndex}-value`;
+
+        newCell.appendChild(newInput);
+
+        // 在Y单元格之前插入新单元格
+        const yCell = row.children[row.children.length - 2];
+        row.insertBefore(newCell, yCell);
+    });
+}
+
+/**
+ * 更新回归表格行号
+ */
+function updateRegressionRowNumbers() {
+    const rows = document.querySelectorAll('#regression-table tbody tr');
+    rows.forEach((row, index) => {
+        const indexCell = row.querySelector('td:first-child');
+        if (indexCell) {
+            indexCell.textContent = index + 1;
+        }
+    });
+}
+
+/**
+ * 初始化输入模式切换
+ */
+function initInputModeToggle() {
+    const tableModeBtn = document.getElementById('table-mode-btn');
+    const csvModeBtn = document.getElementById('csv-mode-btn');
+    const tableInputMode = document.getElementById('table-input-mode');
+    const csvInputMode = document.getElementById('csv-input-mode');
+
+    tableModeBtn.addEventListener('click', function() {
+        // 切换按钮状态
+        tableModeBtn.classList.add('active');
+        csvModeBtn.classList.remove('active');
+
+        // 切换输入模式
+        tableInputMode.classList.add('active');
+        csvInputMode.classList.remove('active');
+    });
+
+    csvModeBtn.addEventListener('click', function() {
+        // 切换按钮状态
+        csvModeBtn.classList.add('active');
+        tableModeBtn.classList.remove('active');
+
+        // 切换输入模式
+        csvInputMode.classList.add('active');
+        tableInputMode.classList.remove('active');
+    });
+}
+
+/**
+ * 运行多元线性回归
+ * @param {Array} X - 自变量矩阵,每行是一个数据点,每列是一个特征
+ * @param {Array} y - 因变量向量
+ * @returns {Object} 回归结果
+ */
+function runMultipleLinearRegression(X, y) {
+    try {
+        // 添加常数项(截距)
+        const Xwith1 = X.map(row => [1, ...row]);
+
+        // 转换为math.js矩阵
+        const Xmatrix = math.matrix(Xwith1);
+        const ymatrix = math.matrix(y);
+
+        // 计算回归系数: β = (X'X)^(-1)X'y
+        const Xt = math.transpose(Xmatrix);
+        const XtX = math.multiply(Xt, Xmatrix);
+        const XtXinv = math.inv(XtX);
+        const Xty = math.multiply(Xt, ymatrix);
+        const beta = math.multiply(XtXinv, Xty);
+
+        // 计算拟合值
+        const yFit = math.multiply(Xmatrix, beta);
+
+        // 计算R²和调整后的R²
+        const stats = calculateRegressionStats(y, yFit.valueOf(), Xwith1[0].length - 1);
+
+        // 构建回归方程
+        const equation = buildLinearRegressionEquation(beta.valueOf(), X[0].length);
+
+        return {
+            type: 'linear',
+            equation: equation,
+            coefficients: beta.valueOf(),
+            r2: stats.r2,
+            adjustedR2: stats.adjustedR2,
+            numVars: X[0].length
+        };
+    } catch (error) {
+        console.error('多元线性回归计算错误:', error);
+        alert('多元线性回归计算错误: ' + error.message);
+        return null;
+    }
+}
+
+/**
+ * 运行多项式回归
+ * @param {Array} X - 自变量矩阵,每行是一个数据点,每列是一个特征
+ * @param {Array} y - 因变量向量
+ * @param {number} degree - 多项式次数
+ * @returns {Object} 回归结果
+ */
+function runPolynomialRegression(X, y, degree) {
+    try {
+        // 检查是否只有一个自变量
+        if (X[0].length > 1) {
+            throw new Error('多项式回归目前只支持一个自变量');
+        }
+
+        // 提取单个自变量
+        const x = X.map(row => row[0]);
+
+        // 创建多项式特征矩阵
+        const polyX = [];
+        for (let i = 0; i < x.length; i++) {
+            const row = [1]; // 常数项
+            for (let j = 1; j <= degree; j++) {
+                row.push(Math.pow(x[i], j));
+            }
+            polyX.push(row);
+        }
+
+        // 转换为math.js矩阵
+        const Xmatrix = math.matrix(polyX);
+        const ymatrix = math.matrix(y);
+
+        // 计算回归系数: β = (X'X)^(-1)X'y
+        const Xt = math.transpose(Xmatrix);
+        const XtX = math.multiply(Xt, Xmatrix);
+        const XtXinv = math.inv(XtX);
+        const Xty = math.multiply(Xt, ymatrix);
+        const beta = math.multiply(XtXinv, Xty);
+
+        // 计算拟合值
+        const yFit = math.multiply(Xmatrix, beta);
+
+        // 计算R²和调整后的R²
+        const stats = calculateRegressionStats(y, yFit.valueOf(), degree);
+
+        // 构建回归方程
+        const equation = buildPolynomialRegressionEquation(beta.valueOf(), degree);
+
+        return {
+            type: 'polynomial',
+            equation: equation,
+            coefficients: beta.valueOf(),
+            r2: stats.r2,
+            adjustedR2: stats.adjustedR2,
+            degree: degree
+        };
+    } catch (error) {
+        console.error('多项式回归计算错误:', error);
+        alert('多项式回归计算错误: ' + error.message);
+        return null;
+    }
+}
+
+/**
+ * 计算回归统计量
+ * @param {Array} yActual - 实际y值
+ * @param {Array} yFit - 拟合y值
+ * @param {number} numVars - 自变量数量
+ * @returns {Object} 统计量对象
+ */
+function calculateRegressionStats(yActual, yFit, numVars) {
+    const n = yActual.length;
+
+    // 计算y的平均值
+    let yMean = 0;
+    for (let i = 0; i < n; i++) {
+        yMean += yActual[i];
+    }
+    yMean /= n;
+
+    // 计算总平方和 (SST) 和残差平方和 (SSE)
+    let sst = 0;
+    let sse = 0;
+
+    for (let i = 0; i < n; i++) {
+        sst += Math.pow(yActual[i] - yMean, 2);
+        sse += Math.pow(yActual[i] - yFit[i], 2);
+    }
+
+    // 计算R²
+    const r2 = 1 - (sse / sst);
+
+    // 计算调整后的R²
+    const adjustedR2 = 1 - ((1 - r2) * (n - 1) / (n - numVars - 1));
+
+    return { r2, adjustedR2 };
+}
+
+/**
+ * 构建线性回归方程字符串
+ * @param {Array} coefficients - 回归系数
+ * @param {number} numVars - 自变量数量
+ * @returns {string} 回归方程字符串
+ */
+function buildLinearRegressionEquation(coefficients, numVars) {
+    let equation = 'y = ';
+
+    // 添加截距
+    equation += coefficients[0].toFixed(4);
+
+    // 添加各个自变量的系数
+    for (let i = 0; i < numVars; i++) {
+        const coeff = coefficients[i + 1];
+        if (coeff >= 0) {
+            equation += ' + ' + coeff.toFixed(4) + 'x' + (i + 1);
+        } else {
+            equation += ' - ' + Math.abs(coeff).toFixed(4) + 'x' + (i + 1);
+        }
+    }
+
+    return equation;
+}
+
+/**
+ * 构建多项式回归方程字符串
+ * @param {Array} coefficients - 回归系数
+ * @param {number} degree - 多项式次数
+ * @returns {string} 回归方程字符串
+ */
+function buildPolynomialRegressionEquation(coefficients, degree) {
+    let equation = 'y = ';
+
+    // 添加截距
+    equation += coefficients[0].toFixed(4);
+
+    // 添加各次项的系数
+    for (let i = 1; i <= degree; i++) {
+        const coeff = coefficients[i];
+        if (coeff >= 0) {
+            equation += ' + ' + coeff.toFixed(4);
+        } else {
+            equation += ' - ' + Math.abs(coeff).toFixed(4);
+        }
+
+        if (i === 1) {
+            equation += 'x';
+        } else {
+            equation += 'x^' + i;
+        }
+    }
+
+    return equation;
+}
+
+/**
+ * 显示回归分析结果
+ * @param {Object} result - 回归结果
+ * @param {Object} data - 原始数据
+ */
+function displayRegressionResult(result, data) {
+    if (!result) return;
+
+    // 显示回归方程
+    const equationResult = document.getElementById('equation-result');
+    equationResult.textContent = result.equation;
+
+    // 显示统计信息
+    const statsResult = document.getElementById('stats-result');
+    statsResult.innerHTML = `
+        拟合优度 R² = ${(result.r2 * 100).toFixed(2)}%<br>
+        调整后的 R² = ${(result.adjustedR2 * 100).toFixed(2)}%<br>
+        数据点数量: ${data.X.length}<br>
+        变量数量: ${result.numVars || result.degree}
+    `;
+
+    // 绘制回归结果
+    if (data.X[0].length === 1) {
+        // 只有一个自变量时才绘制图表
+        plotRegressionResult(result, data);
+    } else {
+        // 多变量回归不绘制图表
+        Plotly.purge('plot-area');
+        const plotArea = document.getElementById('plot-area');
+        plotArea.innerHTML = '<div class="no-plot-message">多变量回归无法在二维图表中显示</div>';
+    }
+}
+
+/**
+ * 绘制回归分析结果
+ * @param {Object} result - 回归结果
+ * @param {Object} data - 原始数据
+ */
+function plotRegressionResult(result, data) {
+    // 提取数据点的x和y值
+    const xData = data.X.map(row => row[0]);
+    const yData = data.y;
+
+    // 找出x的最小值和最大值
+    const xMin = Math.min(...xData);
+    const xMax = Math.max(...xData);
+
+    // 为了平滑曲线,生成更多的点
+    const xRange = xMax - xMin;
+    const xStart = xMin - xRange * 0.1;
+    const xEnd = xMax + xRange * 0.1;
+    const step = xRange / 100;
+
+    // 生成回归曲线的点
+    const xFit = [];
+    const yFit = [];
+
+    for (let x = xStart; x <= xEnd; x += step) {
+        let y;
+
+        if (result.type === 'linear') {
+            // 线性回归: y = b0 + b1*x
+            y = result.coefficients[0] + result.coefficients[1] * x;
+        } else if (result.type === 'polynomial') {
+            // 多项式回归: y = b0 + b1*x + b2*x^2 + ... + bn*x^n
+            y = result.coefficients[0];
+            for (let i = 1; i <= result.degree; i++) {
+                y += result.coefficients[i] * Math.pow(x, i);
+            }
+        }
+
+        if (!isNaN(y) && isFinite(y)) {
+            xFit.push(x);
+            yFit.push(y);
+        }
+    }
+
+    // 创建数据点散点图
+    const dataTrace = {
+        x: xData,
+        y: yData,
+        mode: 'markers',
+        type: 'scatter',
+        name: '数据点',
+        marker: {
+            size: 8,
+            color: 'rgba(255, 0, 0, 0.7)'
+        }
+    };
+
+    // 创建回归曲线
+    const fitTrace = {
+        x: xFit,
+        y: yFit,
+        mode: 'lines',
+        type: 'scatter',
+        name: '回归曲线',
+        line: {
+            color: 'blue',
+            width: 2
+        }
+    };
+
+    // 绘图布局
+    const layout = {
+        title: '回归分析结果',
+        xaxis: {
+            title: 'X'
+        },
+        yaxis: {
+            title: 'Y'
+        },
+        legend: {
+            x: 0,
+            y: 1
+        }
+    };
+
+    // 绘制图表
+    Plotly.newPlot('plot-area', [dataTrace, fitTrace], layout);
+}

+ 485 - 0
FuncGen/script.js

@@ -0,0 +1,485 @@
+// 函数拟合工具 - 主脚本文件
+
+// 页面加载完成后执行初始化
+document.addEventListener('DOMContentLoaded', function() {
+    // 初始化标签页切换功能
+    initTabs();
+
+    // 初始化数据表格
+    initDataTable();
+
+    // 初始化函数绘制功能
+    initFunctionPlot();
+
+    // 初始化表格排序功能
+    initTableSorting();
+
+    // 初始化数据导入导出功能
+    initDataImportExport();
+
+    // 初始化数据拟合功能
+    // 将在下一部分实现
+
+    // 初始化回归分析功能
+    // 将在后续部分实现
+});
+
+/**
+ * 初始化标签页切换功能
+ */
+function initTabs() {
+    const tabButtons = document.querySelectorAll('.tab-btn');
+    const tabPanes = document.querySelectorAll('.tab-pane');
+
+    tabButtons.forEach(button => {
+        button.addEventListener('click', function() {
+            // 移除所有标签页的激活状态
+            tabButtons.forEach(btn => btn.classList.remove('active'));
+            tabPanes.forEach(pane => pane.classList.remove('active'));
+
+            // 激活当前点击的标签页
+            this.classList.add('active');
+            const tabId = this.getAttribute('data-tab');
+            document.getElementById(tabId).classList.add('active');
+        });
+    });
+}
+
+/**
+ * 初始化数据表格功能
+ */
+function initDataTable() {
+    // 获取表格元素
+    const dataTable = document.getElementById('data-table');
+    const tableBody = dataTable.querySelector('tbody') || document.createElement('tbody');
+
+    // 如果表格没有tbody,则添加一个
+    if (!dataTable.querySelector('tbody')) {
+        dataTable.appendChild(tableBody);
+    }
+
+    // 绑定添加行按钮事件
+    const addRowBtn = document.getElementById('add-row');
+    if (addRowBtn) {
+        addRowBtn.addEventListener('click', function() {
+            addTableRow(tableBody);
+            updateRowNumbers();
+        });
+    }
+
+    // 使用事件委托绑定删除按钮事件
+    // 这样可以处理所有现有和将来添加的删除按钮
+    dataTable.addEventListener('click', function(event) {
+        const target = event.target;
+        if (target.classList.contains('remove-row')) {
+            const row = target.closest('tr');
+            if (row && tableBody.contains(row)) {
+                tableBody.removeChild(row);
+                updateRowNumbers();
+            }
+        }
+    });
+}
+
+/**
+ * 添加表格行
+ * @param {HTMLElement} tableBody - 表格体元素
+ */
+function addTableRow(tableBody) {
+    const newRow = document.createElement('tr');
+
+    // 创建序号单元格
+    const indexCell = document.createElement('td');
+    indexCell.className = 'row-index';
+    indexCell.textContent = tableBody.children.length + 1;
+
+    // 创建X输入单元格
+    const xCell = document.createElement('td');
+    const xInput = document.createElement('input');
+    xInput.type = 'number';
+    xInput.step = 'any';
+    xInput.className = 'x-value';
+    xCell.appendChild(xInput);
+
+    // 创建Y输入单元格
+    const yCell = document.createElement('td');
+    const yInput = document.createElement('input');
+    yInput.type = 'number';
+    yInput.step = 'any';
+    yInput.className = 'y-value';
+    yCell.appendChild(yInput);
+
+    // 创建操作单元格(删除按钮)
+    const actionCell = document.createElement('td');
+    const removeBtn = document.createElement('button');
+    removeBtn.textContent = '删除';
+    removeBtn.className = 'remove-row';
+    removeBtn.addEventListener('click', function() {
+        tableBody.removeChild(newRow);
+        updateRowNumbers();
+    });
+    actionCell.appendChild(removeBtn);
+
+    // 将单元格添加到行
+    newRow.appendChild(indexCell);
+    newRow.appendChild(xCell);
+    newRow.appendChild(yCell);
+    newRow.appendChild(actionCell);
+
+    // 将行添加到表格
+    tableBody.appendChild(newRow);
+}
+
+/**
+ * 初始化函数绘制功能
+ */
+function initFunctionPlot() {
+    // 获取绘制按钮和输入元素
+    const plotButton = document.getElementById('plot-function');
+    const functionInput = document.getElementById('function-input');
+    const xMinInput = document.getElementById('x-min');
+    const xMaxInput = document.getElementById('x-max');
+
+    // 添加绘制按钮点击事件
+    plotButton.addEventListener('click', function() {
+        const functionExpr = functionInput.value;
+        const xMin = parseFloat(xMinInput.value);
+        const xMax = parseFloat(xMaxInput.value);
+
+        // 检查输入是否有效
+        if (!functionExpr) {
+            alert('请输入有效的函数表达式');
+            return;
+        }
+
+        if (isNaN(xMin) || isNaN(xMax) || xMin >= xMax) {
+            alert('请输入有效的X范围,且最小值应小于最大值');
+            return;
+        }
+
+        // 绘制函数
+        plotFunction(functionExpr, xMin, xMax);
+    });
+}
+
+/**
+ * 绘制函数
+ * @param {string} functionExpr - 函数表达式
+ * @param {number} xMin - X轴最小值
+ * @param {number} xMax - X轴最大值
+ */
+function plotFunction(functionExpr, xMin, xMax) {
+    try {
+        // 编译函数表达式
+        const compiledFunction = math.compile(functionExpr);
+
+        // 生成X值数组
+        const step = (xMax - xMin) / 500;
+        const xValues = [];
+        const yValues = [];
+
+        // 计算对应的Y值
+        for (let x = xMin; x <= xMax; x += step) {
+            try {
+                const y = compiledFunction.evaluate({x: x});
+                if (!isNaN(y) && isFinite(y)) {
+                    xValues.push(x);
+                    yValues.push(y);
+                }
+            } catch (error) {
+                // 跳过计算错误的点
+                continue;
+            }
+        }
+
+        // 创建绘图数据
+        const trace = {
+            x: xValues,
+            y: yValues,
+            mode: 'lines',
+            type: 'scatter',
+            name: functionExpr,
+            line: {
+                color: '#3498db',
+                width: 2
+            }
+        };
+
+        // 绘图布局
+        const layout = {
+            title: '函数图像',
+            xaxis: {
+                title: 'X',
+                zeroline: true,
+                zerolinecolor: '#999',
+                gridcolor: '#eee'
+            },
+            yaxis: {
+                title: 'Y',
+                zeroline: true,
+                zerolinecolor: '#999',
+                gridcolor: '#eee'
+            },
+            plot_bgcolor: '#fff',
+            paper_bgcolor: '#fff',
+            margin: { t: 50, b: 50, l: 50, r: 30 }
+        };
+
+        // 绘制图表
+        Plotly.newPlot('plot-area', [trace], layout);
+
+        // 使用MathJax显示函数表达式
+        const equationResult = document.getElementById('equation-result');
+        // 将函数表达式转换为LaTeX格式
+        const latexExpr = convertToLatex(functionExpr);
+        equationResult.innerHTML = '$$y = ' + latexExpr + '$$';
+
+        // 重新渲染MathJax
+        if (window.MathJax) {
+            MathJax.typesetPromise([equationResult]).catch(function (err) {
+                console.error('MathJax渲染错误:', err);
+            });
+        }
+
+    } catch (error) {
+        alert('函数绘制错误: ' + error.message);
+    }
+}
+
+/**
+ * 更新表格行序号
+ */
+function updateRowNumbers() {
+    const tableBody = document.querySelector('#data-table tbody');
+    const rows = tableBody.querySelectorAll('tr');
+
+    rows.forEach((row, index) => {
+        const indexCell = row.querySelector('.row-index');
+        if (indexCell) {
+            indexCell.textContent = index + 1;
+        }
+    });
+}
+
+/**
+ * 初始化表格排序功能
+ */
+function initTableSorting() {
+    const sortableHeaders = document.querySelectorAll('.professional-table th.sortable');
+
+    sortableHeaders.forEach(header => {
+        header.addEventListener('click', function() {
+            const table = this.closest('table');
+            const tbody = table.querySelector('tbody');
+            const rows = Array.from(tbody.querySelectorAll('tr'));
+            const columnIndex = Array.from(this.parentNode.children).indexOf(this);
+            const isAsc = !this.classList.contains('sorted-asc');
+
+            // 移除所有排序标记
+            sortableHeaders.forEach(h => {
+                h.classList.remove('sorted-asc', 'sorted-desc');
+            });
+
+            // 添加当前排序标记
+            this.classList.add(isAsc ? 'sorted-asc' : 'sorted-desc');
+
+            // 排序行
+            rows.sort((a, b) => {
+                let aValue, bValue;
+
+                if (columnIndex === 0) { // 序号列
+                    aValue = parseInt(a.cells[columnIndex].textContent);
+                    bValue = parseInt(b.cells[columnIndex].textContent);
+                } else if (columnIndex === 1 || columnIndex === 2) { // X或Y列
+                    const aInput = a.cells[columnIndex].querySelector('input');
+                    const bInput = b.cells[columnIndex].querySelector('input');
+                    aValue = aInput ? parseFloat(aInput.value) : 0;
+                    bValue = bInput ? parseFloat(bInput.value) : 0;
+
+                    if (isNaN(aValue)) aValue = 0;
+                    if (isNaN(bValue)) bValue = 0;
+                }
+
+                return isAsc ? aValue - bValue : bValue - aValue;
+            });
+
+            // 重新添加排序后的行
+            rows.forEach(row => tbody.appendChild(row));
+
+            // 更新行序号
+            updateRowNumbers();
+        });
+    });
+}
+
+/**
+ * 初始化数据导入导出功能
+ */
+function initDataImportExport() {
+    const importBtn = document.getElementById('import-data');
+    const exportBtn = document.getElementById('export-data');
+
+    if (importBtn) {
+        importBtn.addEventListener('click', function() {
+            const input = document.createElement('input');
+            input.type = 'file';
+            input.accept = '.csv,.txt';
+
+            input.addEventListener('change', function(e) {
+                const file = e.target.files[0];
+                if (!file) return;
+
+                const reader = new FileReader();
+                reader.onload = function(e) {
+                    const content = e.target.result;
+                    importDataFromCSV(content);
+                };
+                reader.readAsText(file);
+            });
+
+            input.click();
+        });
+    }
+
+    if (exportBtn) {
+        exportBtn.addEventListener('click', function() {
+            exportDataToCSV();
+        });
+    }
+}
+
+/**
+ * 从CSV导入数据
+ */
+function importDataFromCSV(csvContent) {
+    const lines = csvContent.split('\n');
+    const dataPoints = [];
+
+    // 跳过可能的标题行
+    for (let i = 0; i < lines.length; i++) {
+        const line = lines[i].trim();
+        if (!line) continue;
+
+        const values = line.split(',');
+        if (values.length >= 2) {
+            const x = parseFloat(values[0]);
+            const y = parseFloat(values[1]);
+
+            if (!isNaN(x) && !isNaN(y)) {
+                dataPoints.push({ x, y });
+            }
+        }
+    }
+
+    if (dataPoints.length === 0) {
+        alert('没有找到有效的数据点');
+        return;
+    }
+
+    // 清空现有表格
+    const tableBody = document.querySelector('#data-table tbody');
+    tableBody.innerHTML = '';
+
+    // 添加导入的数据点
+    dataPoints.forEach(point => {
+        const newRow = document.createElement('tr');
+
+        // 序号单元格
+        const indexCell = document.createElement('td');
+        indexCell.className = 'row-index';
+        indexCell.textContent = tableBody.children.length + 1;
+
+        // X值单元格
+        const xCell = document.createElement('td');
+        const xInput = document.createElement('input');
+        xInput.type = 'number';
+        xInput.step = 'any';
+        xInput.className = 'x-value';
+        xInput.value = point.x;
+        xCell.appendChild(xInput);
+
+        // Y值单元格
+        const yCell = document.createElement('td');
+        const yInput = document.createElement('input');
+        yInput.type = 'number';
+        yInput.step = 'any';
+        yInput.className = 'y-value';
+        yInput.value = point.y;
+        yCell.appendChild(yInput);
+
+        // 操作单元格
+        const actionCell = document.createElement('td');
+        const removeBtn = document.createElement('button');
+        removeBtn.textContent = '删除';
+        removeBtn.className = 'remove-row';
+        removeBtn.addEventListener('click', function() {
+            tableBody.removeChild(newRow);
+            updateRowNumbers();
+        });
+        actionCell.appendChild(removeBtn);
+
+        // 添加单元格到行
+        newRow.appendChild(indexCell);
+        newRow.appendChild(xCell);
+        newRow.appendChild(yCell);
+        newRow.appendChild(actionCell);
+
+        // 添加行到表格
+        tableBody.appendChild(newRow);
+    });
+
+    alert(`成功导入 ${dataPoints.length} 个数据点`);
+}
+
+/**
+ * 导出数据到CSV
+ */
+function exportDataToCSV() {
+    const dataPoints = getDataPointsFromTable();
+
+    if (dataPoints.length === 0) {
+        alert('没有数据可导出');
+        return;
+    }
+
+    let csvContent = 'X,Y\n';
+    dataPoints.forEach(point => {
+        csvContent += `${point.x},${point.y}\n`;
+    });
+
+    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+    const url = URL.createObjectURL(blob);
+
+    const link = document.createElement('a');
+    link.href = url;
+    link.setAttribute('download', '数据点.csv');
+    link.style.visibility = 'hidden';
+
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+}
+
+/**
+ * 将函数表达式转换为LaTeX格式
+ */
+function convertToLatex(expr) {
+    // 基本替换
+    let latex = expr
+        .replace(/\*/g, ' \\cdot ')
+        .replace(/\//g, ' \\div ')
+        .replace(/\^(\d+)/g, '^{$1}')
+        .replace(/\^([a-zA-Z])/g, '^{$1}')
+        .replace(/sqrt\(([^)]+)\)/g, '\\sqrt{$1}')
+        .replace(/sin\(([^)]+)\)/g, '\\sin{($1)}')
+        .replace(/cos\(([^)]+)\)/g, '\\cos{($1)}')
+        .replace(/tan\(([^)]+)\)/g, '\\tan{($1)}')
+        .replace(/log\(([^)]+)\)/g, '\\log{($1)}')
+        .replace(/ln\(([^)]+)\)/g, '\\ln{($1)}')
+        .replace(/exp\(([^)]+)\)/g, 'e^{$1}');
+
+    // 处理分数
+    latex = latex.replace(/(\d+)\/(\d+)/g, '\\frac{$1}{$2}');
+
+    return latex;
+}

+ 227 - 0
FuncGen/styles.css

@@ -0,0 +1,227 @@
+/* 全局样式 */
+* {
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+    font-family: 'Microsoft YaHei', Arial, sans-serif;
+}
+
+body {
+    background-color: #f5f5f5;
+    color: #333;
+    line-height: 1.6;
+}
+
+.container {
+    max-width: 1200px;
+    margin: 20px auto;
+    padding: 20px;
+    background-color: #fff;
+    border-radius: 8px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+}
+
+h1 {
+    text-align: center;
+    margin-bottom: 20px;
+    color: #2c3e50;
+}
+
+/* 标签页样式 */
+.tabs {
+    display: flex;
+    margin-bottom: 20px;
+    border-bottom: 1px solid #ddd;
+}
+
+.tab-btn {
+    padding: 10px 20px;
+    background: none;
+    border: none;
+    cursor: pointer;
+    font-size: 16px;
+    font-weight: 500;
+    color: #666;
+    transition: all 0.3s ease;
+}
+
+.tab-btn:hover {
+    color: #3498db;
+}
+
+.tab-btn.active {
+    color: #3498db;
+    border-bottom: 3px solid #3498db;
+}
+
+.tab-pane {
+    display: none;
+    padding: 15px 0;
+}
+
+.tab-pane.active {
+    display: block;
+}
+
+/* 输入组样式 */
+.input-group {
+    margin-bottom: 15px;
+}
+
+label {
+    display: inline-block;
+    margin-right: 10px;
+    font-weight: 500;
+}
+
+input[type="text"],
+input[type="number"],
+select,
+textarea {
+    padding: 8px 12px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    font-size: 14px;
+    width: 200px;
+}
+
+textarea {
+    width: 100%;
+    resize: vertical;
+}
+
+button {
+    padding: 8px 16px;
+    background-color: #3498db;
+    color: white;
+    border: none;
+    border-radius: 4px;
+    cursor: pointer;
+    font-size: 14px;
+    transition: background-color 0.3s;
+}
+
+button:hover {
+    background-color: #2980b9;
+}
+
+/* 表格样式 */
+.table-container {
+    max-height: 300px;
+    overflow-y: auto;
+    margin-bottom: 15px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+}
+
+table {
+    width: 100%;
+    border-collapse: collapse;
+}
+
+table th,
+table td {
+    padding: 10px;
+    text-align: center;
+    border-bottom: 1px solid #ddd;
+}
+
+table th {
+    background-color: #f2f2f2;
+    font-weight: 600;
+}
+
+table input {
+    width: 100%;
+    padding: 6px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+}
+
+.remove-row {
+    background-color: #e74c3c;
+    padding: 5px 10px;
+    font-size: 12px;
+}
+
+.remove-row:hover {
+    background-color: #c0392b;
+}
+
+/* 结果区域样式 */
+.results-container {
+    margin-top: 30px;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+}
+
+.plot-container {
+    flex: 1;
+    min-width: 300px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    padding: 10px;
+    background-color: #fff;
+}
+
+#plot-area {
+    width: 100%;
+    height: 400px;
+}
+
+.equation-container {
+    flex: 1;
+    min-width: 300px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    padding: 15px;
+    background-color: #fff;
+}
+
+.equation-container h3 {
+    margin-bottom: 15px;
+    color: #2c3e50;
+}
+
+#equation-result {
+    font-size: 16px;
+    margin-bottom: 15px;
+    padding: 10px;
+    background-color: #f9f9f9;
+    border-radius: 4px;
+    min-height: 50px;
+}
+
+#stats-result {
+    font-size: 14px;
+    line-height: 1.6;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+    .results-container {
+        flex-direction: column;
+    }
+
+    .input-group {
+        flex-direction: column;
+    }
+
+    input[type="text"],
+    input[type="number"],
+    select {
+        width: 100%;
+        margin-bottom: 10px;
+    }
+
+    .tabs {
+        flex-wrap: wrap;
+    }
+
+    .tab-btn {
+        flex: 1;
+        text-align: center;
+        padding: 8px;
+    }
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
FuncGen/tex-mml-chtml.js


+ 281 - 0
FuncGen/utils-new.js

@@ -0,0 +1,281 @@
+/**
+ * 函数拟合工具 - 工具函数
+ */
+
+/**
+ * 从表格中获取数据点
+ * @returns {Array} 数据点数组,每个元素包含x和y属性
+ */
+function getDataPointsFromTable() {
+    // 检查是否使用新的Excel表格
+    const excelTable = document.getElementById('fit-excel-table');
+    if (excelTable && window.dataTable) {
+        const data = window.dataTable.getTableData(excelTable);
+        const dataPoints = [];
+
+        for (let i = 0; i < data.length; i++) {
+            const row = data[i];
+            if (row.length >= 2) {
+                const x = row[0];
+                const y = row[1];
+                if (!isNaN(x) && !isNaN(y)) {
+                    dataPoints.push({ x, y });
+                }
+            }
+        }
+
+        return dataPoints;
+    } else {
+        // 兼容旧版表格
+        const dataTable = document.getElementById('data-table');
+        const rows = dataTable.querySelectorAll('tbody tr');
+        const dataPoints = [];
+
+        rows.forEach(row => {
+            const xInput = row.querySelector('.x-value');
+            const yInput = row.querySelector('.y-value');
+
+            if (xInput && yInput) {
+                const x = parseFloat(xInput.value);
+                const y = parseFloat(yInput.value);
+
+                if (!isNaN(x) && !isNaN(y)) {
+                    dataPoints.push({ x, y });
+                }
+            }
+        });
+
+        return dataPoints;
+    }
+}
+
+/**
+ * 从表格获取回归数据
+ * @returns {Object} 包含X(自变量矩阵)和y(因变量向量)的对象
+ */
+function getDataFromTable() {
+    // 检查是否使用新的Excel表格
+    const excelTable = document.getElementById('regression-excel-table');
+    if (excelTable && window.dataTable) {
+        const data = window.dataTable.getTableData(excelTable);
+        const X = [];
+        const y = [];
+
+        for (let i = 0; i < data.length; i++) {
+            const row = data[i];
+            if (row.length >= 2) { // 至少需要一个X和一个y
+                const xRow = row.slice(0, row.length - 1); // 最后一列是y值
+                const yValue = row[row.length - 1];
+
+                // 检查所有值是否有效
+                let allValid = true;
+                for (let j = 0; j < xRow.length; j++) {
+                    if (isNaN(xRow[j])) {
+                        allValid = false;
+                        break;
+                    }
+                }
+
+                if (isNaN(yValue)) {
+                    allValid = false;
+                }
+
+                // 如果所有值都有效,添加到数据集
+                if (allValid) {
+                    X.push(xRow);
+                    y.push(yValue);
+                }
+            }
+        }
+
+        return { X, y };
+    } else {
+        // 兼容旧版表格
+        const rows = document.querySelectorAll('#regression-table tbody tr');
+        const X = [];
+        const y = [];
+
+        rows.forEach(row => {
+            const xInputs = row.querySelectorAll('.x1-value, .x2-value, [class^="x"][class$="-value"]:not(.x1-value):not(.x2-value)');
+            const yInput = row.querySelector('.y-value');
+
+            if (xInputs.length > 0 && yInput) {
+                const xRow = [];
+                let allValid = true;
+
+                // 收集所有x值
+                xInputs.forEach(input => {
+                    const value = parseFloat(input.value);
+                    if (isNaN(value)) {
+                        allValid = false;
+                    }
+                    xRow.push(value);
+                });
+
+                // 获取y值
+                const yValue = parseFloat(yInput.value);
+                if (isNaN(yValue)) {
+                    allValid = false;
+                }
+
+                // 如果所有值都有效,添加到数据集
+                if (allValid) {
+                    X.push(xRow);
+                    y.push(yValue);
+                }
+            }
+        });
+
+        return { X, y };
+    }
+}
+
+/**
+ * 解析CSV数据
+ * @param {string} csvText - CSV格式的文本
+ * @returns {Object} 包含X(自变量矩阵)和y(因变量向量)的对象
+ */
+function parseCSVData(csvText) {
+    if (!csvText.trim()) {
+        return { X: [], y: [] };
+    }
+
+    try {
+        // 按行分割
+        const lines = csvText.trim().split(/\r?\n/);
+
+        // 检查是否有足够的行
+        if (lines.length < 2) {
+            throw new Error('数据行数不足');
+        }
+
+        // 解析数据
+        const X = [];
+        const y = [];
+
+        for (let i = 0; i < lines.length; i++) {
+            const values = lines[i].split(/[,\t]/).map(val => parseFloat(val.trim()));
+
+            // 检查是否有足够的列
+            if (values.length < 2) {
+                continue;
+            }
+
+            // 检查所有值是否有效
+            let allValid = true;
+            for (let j = 0; j < values.length; j++) {
+                if (isNaN(values[j])) {
+                    allValid = false;
+                    break;
+                }
+            }
+
+            if (allValid) {
+                // 最后一列作为y值,其余作为X值
+                X.push(values.slice(0, values.length - 1));
+                y.push(values[values.length - 1]);
+            }
+        }
+
+        return { X, y };
+    } catch (error) {
+        console.error('解析CSV数据错误:', error);
+        return { X: [], y: [] };
+    }
+}
+
+/**
+ * 格式化数字,保留指定小数位
+ * @param {number} value - 要格式化的数值
+ * @param {number} decimals - 小数位数
+ * @returns {string} 格式化后的数字字符串
+ */
+function formatNumber(value, decimals = 4) {
+    return Number(value).toFixed(decimals);
+}
+
+/**
+ * 计算相关系数 (R²)
+ * @param {Array} xValues - X值数组
+ * @param {Array} yValues - Y值数组
+ * @param {Function} predictFn - 预测函数,接收x返回预测的y
+ * @returns {number} 相关系数
+ */
+function calculateRSquared(xValues, yValues, predictFn) {
+    if (xValues.length !== yValues.length || xValues.length === 0) {
+        return 0;
+    }
+
+    // 计算y的平均值
+    const yMean = yValues.reduce((sum, y) => sum + y, 0) / yValues.length;
+
+    // 计算总平方和(SST)
+    const sst = yValues.reduce((sum, y) => sum + Math.pow(y - yMean, 2), 0);
+
+    // 计算残差平方和(SSE)
+    let sse = 0;
+    for (let i = 0; i < xValues.length; i++) {
+        const yPred = predictFn(xValues[i]);
+        sse += Math.pow(yValues[i] - yPred, 2);
+    }
+
+    // 计算R²
+    return 1 - (sse / sst);
+}
+
+/**
+ * 计算均方根误差(RMSE)
+ * @param {Array} xValues - X值数组
+ * @param {Array} yValues - Y值数组
+ * @param {Function} predictFn - 预测函数,接收x返回预测的y
+ * @returns {number} RMSE值
+ */
+function calculateRMSE(xValues, yValues, predictFn) {
+    if (xValues.length !== yValues.length || xValues.length === 0) {
+        return 0;
+    }
+
+    let sumSquaredError = 0;
+    for (let i = 0; i < xValues.length; i++) {
+        const yPred = predictFn(xValues[i]);
+        sumSquaredError += Math.pow(yValues[i] - yPred, 2);
+    }
+
+    return Math.sqrt(sumSquaredError / xValues.length);
+}
+
+/**
+ * 生成统计结果HTML
+ * @param {Object} params - 统计参数
+ * @returns {string} HTML字符串
+ */
+function generateStatsHTML(params) {
+    const { coefficients, rSquared, rmse, formula, dataPoints } = params;
+
+    let html = '<h4>拟合统计</h4>';
+    html += '<table>';
+    html += '<tr><th>参数</th><th>值</th></tr>';
+    html += `<tr><td>数据点数量</td><td>${dataPoints}</td></tr>`;
+    html += `<tr><td>决定系数 (R²)</td><td>${formatNumber(rSquared)}</td></tr>`;
+    html += `<tr><td>均方根误差 (RMSE)</td><td>${formatNumber(rmse)}</td></tr>`;
+
+    html += '<tr><th colspan="2">系数</th></tr>';
+    if (Array.isArray(coefficients)) {
+        coefficients.forEach((coef, index) => {
+            let coefName = '';
+            switch (index) {
+                case 0: coefName = formula === 'linear' ? 'a (斜率)' : 'a'; break;
+                case 1: coefName = formula === 'linear' ? 'b (截距)' : 'b'; break;
+                default: coefName = String.fromCharCode(97 + index); // a, b, c, d...
+            }
+            html += `<tr><td>${coefName}</td><td>${formatNumber(coef)}</td></tr>`;
+        });
+    } else if (typeof coefficients === 'object') {
+        for (const key in coefficients) {
+            html += `<tr><td>${key}</td><td>${formatNumber(coefficients[key])}</td></tr>`;
+        }
+    }
+
+    html += '</table>';
+    return html;
+}

+ 135 - 0
FuncGen/utils.js

@@ -0,0 +1,135 @@
+/**
+ * 函数拟合工具 - 工具函数
+ */
+
+/**
+ * 从表格中获取数据点
+ * @returns {Array} 数据点数组,每个元素包含x和y属性
+ */
+function getDataPointsFromTable() {
+    // 检查是否使用Excel风格表格
+    const fitExcelTable = document.getElementById('fit-excel-table');
+    if (fitExcelTable) {
+        // 使用Excel表格数据提取函数
+        return window.dataTable.getDataPointsFromExcelTable(fitExcelTable);
+    }
+
+    // 兼容旧版表格
+    const dataTable = document.getElementById('data-table');
+    if (!dataTable) return [];
+
+    const rows = dataTable.querySelectorAll('tbody tr');
+    const dataPoints = [];
+
+    rows.forEach(row => {
+        const xInput = row.querySelector('.x-value');
+        const yInput = row.querySelector('.y-value');
+
+        if (xInput && yInput) {
+            const x = parseFloat(xInput.value);
+            const y = parseFloat(yInput.value);
+
+            if (!isNaN(x) && !isNaN(y)) {
+                dataPoints.push({ x, y });
+            }
+        }
+    });
+
+    return dataPoints;
+}
+
+/**
+ * 格式化数字,保留指定小数位
+ * @param {number} value - 要格式化的数值
+ * @param {number} decimals - 小数位数
+ * @returns {string} 格式化后的数字字符串
+ */
+function formatNumber(value, decimals = 4) {
+    return Number(value).toFixed(decimals);
+}
+
+/**
+ * 计算相关系数 (R²)
+ * @param {Array} xValues - X值数组
+ * @param {Array} yValues - Y值数组
+ * @param {Function} predictFn - 预测函数,接收x返回预测的y
+ * @returns {number} 相关系数
+ */
+function calculateRSquared(xValues, yValues, predictFn) {
+    if (xValues.length !== yValues.length || xValues.length === 0) {
+        return 0;
+    }
+
+    // 计算y的平均值
+    const yMean = yValues.reduce((sum, y) => sum + y, 0) / yValues.length;
+
+    // 计算总平方和(SST)
+    const sst = yValues.reduce((sum, y) => sum + Math.pow(y - yMean, 2), 0);
+
+    // 计算残差平方和(SSE)
+    let sse = 0;
+    for (let i = 0; i < xValues.length; i++) {
+        const yPred = predictFn(xValues[i]);
+        sse += Math.pow(yValues[i] - yPred, 2);
+    }
+
+    // 计算R²
+    return 1 - (sse / sst);
+}
+
+/**
+ * 计算均方根误差(RMSE)
+ * @param {Array} xValues - X值数组
+ * @param {Array} yValues - Y值数组
+ * @param {Function} predictFn - 预测函数,接收x返回预测的y
+ * @returns {number} RMSE值
+ */
+function calculateRMSE(xValues, yValues, predictFn) {
+    if (xValues.length !== yValues.length || xValues.length === 0) {
+        return 0;
+    }
+
+    let sumSquaredError = 0;
+    for (let i = 0; i < xValues.length; i++) {
+        const yPred = predictFn(xValues[i]);
+        sumSquaredError += Math.pow(yValues[i] - yPred, 2);
+    }
+
+    return Math.sqrt(sumSquaredError / xValues.length);
+}
+
+/**
+ * 生成统计结果HTML
+ * @param {Object} params - 统计参数
+ * @returns {string} HTML字符串
+ */
+function generateStatsHTML(params) {
+    const { coefficients, rSquared, rmse, formula, dataPoints } = params;
+
+    let html = '<h4>拟合统计</h4>';
+    html += '<table>';
+    html += '<tr><th>参数</th><th>值</th></tr>';
+    html += `<tr><td>数据点数量</td><td>${dataPoints}</td></tr>`;
+    html += `<tr><td>决定系数 (R²)</td><td>${formatNumber(rSquared)}</td></tr>`;
+    html += `<tr><td>均方根误差 (RMSE)</td><td>${formatNumber(rmse)}</td></tr>`;
+
+    html += '<tr><th colspan="2">系数</th></tr>';
+    if (Array.isArray(coefficients)) {
+        coefficients.forEach((coef, index) => {
+            let coefName = '';
+            switch (index) {
+                case 0: coefName = formula === 'linear' ? 'a (斜率)' : 'a'; break;
+                case 1: coefName = formula === 'linear' ? 'b (截距)' : 'b'; break;
+                default: coefName = String.fromCharCode(97 + index); // a, b, c, d...
+            }
+            html += `<tr><td>${coefName}</td><td>${formatNumber(coef)}</td></tr>`;
+        });
+    } else if (typeof coefficients === 'object') {
+        for (const key in coefficients) {
+            html += `<tr><td>${key}</td><td>${formatNumber(coefficients[key])}</td></tr>`;
+        }
+    }
+
+    html += '</table>';
+    return html;
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 992 - 0
GeoGebra_Self/HTML5/5.0/GeoGebra.html


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/app-header.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/av-styles.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/bundles/bundle.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/bundles/simple-bundle.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/colorMap.css

@@ -0,0 +1 @@
+

+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/colors.css

@@ -0,0 +1 @@
+:root{--ggb-primary-color: #6557D2;--ggb-primary-variant-color: #F3F0FF;--ggb-dark-color: #5145A8;--ggb-light-color: #F3F0FF;--ggb-selection-color: rgba(101, 87, 210, 0.2)}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/component-styles.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/context-menu.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/dialog-styles.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/dimens.css

@@ -0,0 +1 @@
+

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/editor.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/ev-styles.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/evaluator-styles.css

@@ -0,0 +1 @@
+.GeoGebraFrame .evaluatorEditor{margin:4px;width:calc(100% - 8px);min-height:calc(100% - 8px);max-height:calc(100% - 8px);overflow:hidden}

+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/exam.css

@@ -0,0 +1 @@
+.GeoGebraFrame .toolbarPanel.examToolbar{background-color:#ccf}.GeoGebraFrame .timer{font-size:170%;float:left;padding:0 4px}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/fonts.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/general.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/greek-font.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/header.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/headerbar.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/info-card.css

@@ -0,0 +1 @@
+:root{--ggb-primary-color: #6557D2;--ggb-primary-variant-color: #F3F0FF;--ggb-dark-color: #5145A8;--ggb-light-color: #F3F0FF;--ggb-selection-color: rgba(101, 87, 210, 0.2)}.GeoGebraFrame .cardInfo{width:100%;height:44px;border-radius:0 0 2px 2px;background-color:#fafafa;box-sizing:border-box;display:flex}.GeoGebraFrame .cardInfo .cardTitle{line-height:28px;font-size:100%;height:28px;width:154px;padding:8px 30px 8px 16px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block}.GeoGebraFrame .cardInfo .cardAuthor{font-size:87.5%;line-height:20px;letter-spacing:.25px;padding:6px 0px 12px 16px;text-overflow:ellipsis;position:absolute;font-weight:400;top:48px;overflow:hidden;white-space:nowrap;width:178px}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/keyboard-styles.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/layout.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/menu-styles.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/mow-toolbox.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/mow-topbar.css

@@ -0,0 +1 @@
+.GeoGebraFrame .topbar{height:48px;width:100%;position:relative;display:inline-flex;gap:8px;background-color:#fff;border-bottom:1px solid #e6e6eb;box-sizing:border-box;z-index:62;padding:6px 0 6px 12px}.GeoGebraFrame .topbar .pageOverview{right:16px;position:absolute}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/mow.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/open-screen.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/popup-styles.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/print.css

@@ -0,0 +1 @@
+@media screen{.printPanel{left:10000px}}@media print{.printPanel{left:0px;top:0px}}.printPanel{position:absolute;max-width:10000px !important}.pagebreakbefore{display:block;page-break-before:always}@media print{.avPlainText{width:100vw;white-space:normal}.GeoGebraFrame,.GeoGebraFrame *{visibility:hidden}.GeoGebraFrame .mowFloatingButton{visibility:hidden}.GeoGebraPopup{width:95%;height:95%;position:absolute;top:0px;left:0px}@page{margin:2cm}.avItem{page-break-inside:avoid}.marble{-webkit-print-color-adjust:exact;margin-top:auto}.algebraViewObjectStylebar{display:none}}@media print and (orientation: portrait){.preview_portrait{display:block;visibility:visible}.preview_landscape{display:none;visibility:hidden}}@media print and (orientation: landscape){.preview_portrait{display:none;visibility:hidden}.preview_landscape{display:block;visibility:visible}}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/probability-layout.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/reset.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/scientific-layout.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/settings-styles.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/shared.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/snackbar.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/spreadsheet-tab.css

@@ -0,0 +1 @@
+.GeoGebraFrame .spreadsheetPanel{height:100%;overflow:hidden}.GeoGebraFrame .spreadsheetWidget{position:absolute}.GeoGebraFrame .spreadsheetScrollOverlay{position:absolute;top:0;left:0;width:100%;height:100%}.GeoGebraFrame .spreadsheetEditor{position:absolute;border-radius:0;background:#fff;border:2px #6557d2 solid;box-sizing:border-box}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/spreadsheet.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/step-tree.css

@@ -0,0 +1 @@
+.GeoGebraFrame .stepTree table{width:100%}.GeoGebraFrame .stepTree td:first-child{width:70% !important}.GeoGebraFrame .stepTree .stepinformation{width:100%}.GeoGebraFrame .stepTree .stepGroupPanel{box-sizing:border-box;width:100%}.GeoGebraFrame .stepTree .stepGroupPanel>tbody>tr:nth-child(n+2)>td{padding-left:23px !important}.GeoGebraFrame .stepTree .stepGroupPanel>tbody>tr:nth-child(n+3)>td{padding-top:6px !important}.GeoGebraFrame .stepTree .stepTreeLine{white-space:normal;width:100%}.GeoGebraFrame .stepTree .stepTreeElem{vertical-align:middle}.GeoGebraFrame .stepTree canvas.stepTreeElem{user-select:all;-moz-user-select:all}.GeoGebraFrame .stepTree .stepTreeButton{padding:2px;display:inline}.GeoGebraFrame .stepTree .stepTreeButton img{vertical-align:middle}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/tableview.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/text-styles.css

@@ -0,0 +1 @@
+:root{--ggb-primary-color: #6557D2;--ggb-primary-variant-color: #F3F0FF;--ggb-dark-color: #5145A8;--ggb-light-color: #F3F0FF;--ggb-selection-color: rgba(101, 87, 210, 0.2)}.GeoGebraFrame .headline6{font-size:125%;font-weight:500;letter-spacing:.25px;color:rgba(0,0,0,.87)}.GeoGebraFrame .subtitle1,.GeoGebraFrame .subtitle1-link{font-size:100%;font-weight:400;letter-spacing:.15px;line-height:1.5em;color:rgba(0,0,0,.54)}.GeoGebraFrame .subtitle1-link{color:rgba(0,0,0,.87);font-weight:500}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/toolbar-styles.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/web-styles-global.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/css/web-styles.css


+ 1 - 0
GeoGebra_Self/HTML5/5.0/css/z-index.css

@@ -0,0 +1 @@
+

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 27728 - 0
GeoGebra_Self/HTML5/5.0/web/03AC6A979CF3ACBAEC7A4498AE960BB9.cache.js


BIN
GeoGebra_Self/HTML5/5.0/web/clear.cache.gif


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/web/deferredjs/03AC6A979CF3ACBAEC7A4498AE960BB9/1.cache.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
GeoGebra_Self/HTML5/5.0/web/deferredjs/03AC6A979CF3ACBAEC7A4498AE960BB9/10.cache.js


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott