{"id":10447,"date":"2026-08-06T08:36:51","date_gmt":"2026-08-06T08:36:51","guid":{"rendered":"https:\/\/voyawave.com\/%e5%a1%9e%e5%b0%94%e8%bf%88%e5%b0%94%e6%8e%a5%e5%a4%b4\/"},"modified":"2026-08-11T07:46:46","modified_gmt":"2026-08-11T07:46:46","slug":"%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88","status":"publish","type":"page","link":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/","title":{"rendered":"\u585e\u7c73\u5c14\u62df\u5408"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"10447\" class=\"elementor elementor-10447 elementor-10445\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5d675ca e-con-full wd-section-stretch-content e-flex e-con e-parent\" data-id=\"5d675ca\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-50f14c4 elementor-widget elementor-widget-html\" data-id=\"50f14c4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n  <title>\u585e\u7c73\u5c14\u66f2\u7ebf\u62df\u5408\uff5c\u585e\u7c73\u5c14\u62df\u5408\u5de5\u5177<\/title>\n  <style>\n    :root{\n      --bg:#ffffff;\n      --page:#f7f8fa;\n      --card:#ffffff;\n      --text:#101114;\n      --muted:#6b7280;\n      --line:#e5e7eb;\n      --soft:#f3f4f6;\n      --red:#b5121b;\n      --red2:#e0242e;\n      --redSoft:#fff0f1;\n      --black:#0b0b0d;\n      --shadow:0 12px 30px rgba(15,23,42,.08);\n      --radius:16px;\n    }\n    *{box-sizing:border-box}\n    body{\n      margin:0;\n      background:var(--page);\n      color:var(--text);\n      font-family:\"Microsoft YaHei\", \"PingFang SC\", \"Segoe UI\", Arial, sans-serif;\n    }\n    .app{min-height:100vh;padding:24px;background:\n      linear-gradient(90deg, rgba(181,18,27,.03) 1px, transparent 1px),\n      linear-gradient(rgba(181,18,27,.03) 1px, transparent 1px),#fafafa;\n      background-size:58px 58px;\n    }\n    .topbar{\n      height:64px;display:flex;align-items:center;justify-content:space-between;\n      background:var(--card);border:1px solid var(--line);border-radius:18px;\n      padding:0 20px;box-shadow:var(--shadow);margin-bottom:20px;\n    }\n    .brand{display:flex;align-items:center;gap:12px;font-weight:800;font-size:22px;letter-spacing:.3px}\n    .logo{width:34px;height:34px;border-radius:9px;background:linear-gradient(135deg,var(--black),var(--red));position:relative;box-shadow:0 5px 12px rgba(181,18,27,.28)}\n    .logo:after{content:\"\";position:absolute;inset:8px;border-left:3px solid #fff;border-bottom:3px solid #fff;transform:rotate(-45deg);border-radius:2px}\n    .top-actions{display:flex;gap:10px}.top-actions button{height:38px;padding:0 14px;border-radius:10px;border:1px solid #f0b7bb;background:#fff;color:var(--red);font-weight:700;cursor:pointer}.top-actions button:hover{background:var(--redSoft)}\n    .layout{display:grid;grid-template-columns:420px 1fr;gap:20px;max-width:1500px;margin:0 auto}\n    .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}\n    .left{padding:18px}.right{padding:18px;min-width:0}\n    .section-title{display:flex;align-items:center;justify-content:space-between;margin:0 0 14px;font-size:16px;font-weight:800;color:var(--black)}\n    .section-title span:first-child:before{content:\"\";display:inline-block;width:5px;height:18px;border-radius:4px;background:var(--red);vertical-align:-4px;margin-right:9px}\n    .hint{font-size:12px;color:var(--muted);line-height:1.6;margin-top:8px}\n    .field-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.field{display:flex;flex-direction:column;gap:6px}.field label{font-size:13px;color:#4b5563;font-weight:700}.field input,.field select,textarea{width:100%;border:1px solid #d7dce3;background:#fff;border-radius:10px;padding:10px 12px;min-height:42px;font-size:14px;color:var(--text);outline:none}.field input:focus,.field select:focus,textarea:focus{border-color:var(--red);box-shadow:0 0 0 3px rgba(181,18,27,.12)}\n    .btn{height:42px;border:none;border-radius:10px;background:var(--red);color:white;font-weight:800;cursor:pointer;padding:0 14px}.btn:hover{background:#9d0f17}.btn.secondary{background:#171717}.btn.secondary:hover{background:#000}.btn.ghost{background:#fff;color:var(--red);border:1px solid #f0b7bb}.btn.ghost:hover{background:var(--redSoft)}\n    .btn-row{display:flex;gap:10px;flex-wrap:wrap;margin-top:12px}.btn-row .btn{flex:1;min-width:92px}\n    .divider{height:1px;background:var(--line);margin:18px 0}\n    textarea{height:135px;resize:vertical;font-family:Consolas,\"Microsoft YaHei\",monospace;line-height:1.45;background:#fff}\n    .data-list{height:190px;overflow:auto;border:1px solid #e2e8f0;border-radius:12px;background:#fff}.data-item{display:grid;grid-template-columns:72px 1fr 1fr 28px;gap:8px;align-items:center;padding:9px 10px;border-bottom:1px solid #f1f5f9;font-size:13px}.data-item:last-child{border-bottom:none}.data-item .g{font-weight:800;color:var(--red)}.data-item button{border:none;background:#fff;color:#9ca3af;font-size:18px;cursor:pointer}.data-item button:hover{color:var(--red)}\n    .chart-head{display:grid;grid-template-columns:1fr auto;gap:16px;align-items:start;margin-bottom:12px}.info-block{display:grid;grid-template-columns:repeat(4, minmax(120px, 1fr));gap:10px}.stat{background:#fff;border:1px solid var(--line);border-radius:13px;padding:10px 12px}.stat .label{font-size:12px;color:var(--muted);font-weight:700}.stat .value{font-size:16px;font-weight:800;margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.stat .value.red{color:var(--red)}\n    .chart-wrap{height:500px;border:1px solid var(--line);border-radius:15px;background:#fff;position:relative;overflow:hidden}.chart-wrap canvas{width:100%;height:100%;display:block;cursor:crosshair}.chart-menu{position:fixed;z-index:9999;display:none;min-width:150px;padding:8px;border:1px solid #f0b7bb;background:#fff;border-radius:12px;box-shadow:0 14px 35px rgba(15,23,42,.18)}.chart-menu button{width:100%;height:36px;border:none;border-radius:9px;background:#fff;color:var(--red);font-weight:800;cursor:pointer;text-align:left;padding:0 10px}.chart-menu button:hover{background:var(--redSoft)}.chart-tip{position:absolute;right:12px;top:12px;background:rgba(255,255,255,.92);border:1px solid #f0b7bb;color:#7f1d1d;border-radius:10px;padding:6px 9px;font-size:12px;pointer-events:none}.legend{display:flex;gap:12px;flex-wrap:wrap;margin:10px 0 0}.legend-item{display:flex;align-items:center;gap:6px;color:#4b5563;font-size:13px}.swatch{width:16px;height:4px;border-radius:4px}\n    .result-grid{display:grid;grid-template-columns:1fr;gap:10px;max-height:250px;overflow:auto}.result-card{border:1px solid var(--line);border-radius:12px;padding:10px 12px;background:#fff}.result-card.active{border-color:var(--red);box-shadow:0 0 0 3px rgba(181,18,27,.10)}.result-card h4{margin:0 0 8px;font-size:14px}.result-card .params{display:grid;grid-template-columns:repeat(2,1fr);gap:6px;font-family:Consolas,monospace;font-size:12px;color:#374151}.result-card .full-formula{margin:8px 0;padding:8px 10px;border-radius:10px;background:#fff0f1;border:1px solid #fecdd3;color:#7f1d1d;font-family:Consolas,\"Microsoft YaHei\",monospace;font-size:12px;line-height:1.55;word-break:break-all}.result-card .unit-note{font-size:11px;color:#6b7280;margin-top:4px}\n    .bottom{display:grid;grid-template-columns:1fr 1.2fr;gap:18px;margin-top:18px}.calc-box{padding:16px}.calc-row{display:flex;gap:10px;align-items:end}.calc-row .field{flex:1}.output{margin-top:12px;border:1px solid var(--line);border-radius:12px;background:#fff;min-height:110px;padding:12px;font-family:Consolas,\"Microsoft YaHei\",monospace;font-size:13px;white-space:pre-wrap;color:#111827}.message{margin-top:12px;border-radius:10px;padding:10px 12px;font-size:13px;line-height:1.6;display:none}.message.ok{background:#eefdf3;color:#166534;border:1px solid #bbf7d0}.message.err{background:#fff0f1;color:#991b1b;border:1px solid #fecdd3}.message.show{display:block}\n    .formula{font-family:Consolas,monospace;background:#111;color:#fff;border-radius:12px;padding:12px;margin-top:10px;font-size:13px;line-height:1.6}.mini-title{font-size:15px;font-weight:800;margin:0 0 12px;color:var(--black)}\n    @media(max-width:1100px){.layout{grid-template-columns:1fr}.bottom{grid-template-columns:1fr}.info-block{grid-template-columns:repeat(2,1fr)}}\n  <\/style>\n<\/head>\n<body>\n<div class=\"app\">\n  <div class=\"topbar\">\n    <div class=\"brand\"><div class=\"logo\"><\/div><span>\u585e\u7c73\u5c14\u66f2\u7ebf\u62df\u5408<\/span><\/div>\n    <div class=\"top-actions\"><button id=\"exportPngBtn\">\u5bfc\u51fa PNG<\/button><button id=\"exportCsvBtn\">\u5bfc\u51fa CSV<\/button><\/div>\n  <\/div>\n\n  <div class=\"layout\">\n    <div class=\"card left\">\n      <div class=\"section-title\"><span>\u8f93\u5165\u53c2\u6570<\/span><\/div>\n      <div class=\"field-grid\">\n        <div class=\"field\"><label>\u6ce2\u957f \u03bb (nm)<\/label><input id=\"lambdaInput\" type=\"number\" step=\"0.001\" value=\"531.951\"><\/div>\n        <div class=\"field\"><label>\u6298\u5c04\u7387 n<\/label><input id=\"nInput\" type=\"number\" step=\"0.000001\" value=\"1.515\"><\/div>\n        <div class=\"field\"><label>\u7ec4\u522b<\/label><input id=\"groupInput\" type=\"text\" value=\"1\u7ec4\"><\/div>\n        <div class=\"field\"><label>&nbsp;<\/label><button class=\"btn\" id=\"addBtn\">\u6dfb\u52a0\u6570\u636e\u70b9<\/button><\/div>\n      <\/div>\n      <div class=\"divider\"><\/div>\n      <div class=\"section-title\"><span>\u66f2\u7ebf\u62df\u5408\u6570\u636e<\/span><small id=\"pointCount\" style=\"color:#6b7280\">0 \u70b9<\/small><\/div>\n      <div class=\"data-list\" id=\"dataList\"><\/div>\n      <div class=\"btn-row\">\n        <button class=\"btn ghost\" id=\"deleteBtn\">\u5220\u9664\u9009\u4e2d<\/button>\n        <button class=\"btn ghost\" id=\"clearBtn\">\u6e05\u7a7a<\/button>\n      <\/div>\n      <div class=\"divider\"><\/div>\n      <div class=\"section-title\"><span>\u6279\u91cf\u7c98\u8d34<\/span><\/div>\n      <textarea id=\"bulkInput\" placeholder=\"\u652f\u6301\u683c\u5f0f\uff1a&#10;[1\u7ec4]&#10;486.1327, 1.501182&#10;531.951, 1.497862&#10;635, 1.492894&#10;980, 1.485588&#10;&#10;[2\u7ec4]&#10;...\">[1\u7ec4]\n486.1327, 1.501182\n531.951, 1.497862\n635, 1.492894\n980, 1.485588<\/textarea>\n      <div class=\"btn-row\">\n        <button class=\"btn secondary\" id=\"importBtn\">\u5bfc\u5165\u6587\u672c<\/button>\n        <button class=\"btn\" id=\"fitBtn\">\u585e\u7c73\u5c14\u62df\u5408<\/button>\n      <\/div>\n      <div id=\"msg\" class=\"message\"><\/div>\n      <div class=\"formula\">\u62df\u5408\u516c\u5f0f\uff1an\u00b2 = A + B \/ (\u03bb\u00b2 - C) - D \u00b7 \u03bb\u00b2<br>\u8bf4\u660e\uff1a\u8f93\u5165\u5355\u4f4d\u4e3a nm\uff0c\u5185\u90e8\u81ea\u52a8\u6362\u7b97\u4e3a \u03bcm\uff1b\u62df\u5408\u7ed3\u679c\u4f1a\u663e\u793a\u5b8c\u6574\u516c\u5f0f\uff0c\u03bb \u5355\u4f4d\u4e3a \u03bcm\u3002<\/div>\n    <\/div>\n\n    <div class=\"card right\">\n      <div class=\"chart-head\">\n        <div class=\"info-block\">\n          <div class=\"stat\"><div class=\"label\">\u516c\u5f0f<\/div><div class=\"value\">Sellmeier<\/div><\/div>\n          <div class=\"stat\"><div class=\"label\">\u5f53\u524d \u03bb (nm)<\/div><div class=\"value red\" id=\"cursorX\">0.000<\/div><\/div>\n          <div class=\"stat\"><div class=\"label\">\u5f53\u524d n<\/div><div class=\"value red\" id=\"cursorY\">0.00000<\/div><\/div>\n          <div class=\"stat\"><div class=\"label\">\u9009\u4e2d\u66f2\u7ebf<\/div><div class=\"value\" id=\"selectedGroup\">\u672a\u9009\u62e9<\/div><\/div>\n        <\/div>\n        <div style=\"min-width:290px\">\n          <div class=\"mini-title\">\u62df\u5408\u7ed3\u679c<\/div>\n          <div class=\"result-grid\" id=\"resultGrid\"><\/div>\n        <\/div>\n      <\/div>\n      <div class=\"chart-wrap\"><canvas id=\"chart\"><\/canvas><div class=\"chart-tip\">\u6eda\u8f6e\u7f29\u653e\uff5c\u5de6\u952e\u6846\u9009\uff5c\u53f3\u952e\u8fd8\u539f\uff5c\u66f2\u7ebf\u70b9\u8ddf\u968f<\/div><\/div><div class=\"chart-menu\" id=\"chartMenu\"><button id=\"resetZoomBtn\">\u8fd8\u539f\u56fe\u50cf\u5927\u5c0f<\/button><\/div>\n      <div class=\"legend\" id=\"legend\"><\/div>\n      <div class=\"bottom\">\n        <div class=\"card calc-box\">\n          <div class=\"mini-title\">\u6298\u5c04\u7387\u8ba1\u7b97<\/div>\n          <div class=\"calc-row\">\n            <div class=\"field\"><label>\u8f93\u5165\u6ce2\u957f \u03bb (nm)<\/label><input id=\"calcLambda\" type=\"number\" step=\"0.001\" value=\"589.3\"><\/div>\n            <button class=\"btn\" id=\"calcBtn\">\u8ba1\u7b97<\/button>\n          <\/div>\n          <div class=\"output\" id=\"calcOutput\">\u70b9\u51fb\u201c\u8ba1\u7b97\u201d\u540e\u663e\u793a\u5404\u7ec4\u6298\u5c04\u7387\u3002<\/div>\n        <\/div>\n        <div class=\"card calc-box\">\n          <div class=\"mini-title\">\u4f7f\u7528\u63d0\u793a<\/div>\n          <div class=\"hint\">1. \u585e\u7c73\u5c14\u56db\u53c2\u6570\u62df\u5408\u81f3\u5c11\u9700\u8981 4 \u4e2a\u6ce2\u957f\/\u6298\u5c04\u7387\u6570\u636e\u70b9\u3002<br>2. \u56db\u53c2\u6570\u62df\u5408\u65f6\uff0c4 \u4e2a\u70b9\u53ef\u7ea6\u675f A\/B\/C\/D\uff1b\u66f4\u591a\u70b9\u65f6\u4e3a\u6700\u5c0f\u4e8c\u4e58\u62df\u5408\u3002<br>3. \u963f\u8d1d\u6570\u9ed8\u8ba4\u4f7f\u7528 F=486.1327 nm\u3001d=587.5618 nm\u3001C=656.2725 nm\u3002<br>4. \u9f20\u6807\u79fb\u52a8\u663e\u793a\u5750\u6807\u548c\u7ad6\u7ebf\uff1b\u5de6\u952e\u70b9\u51fb\u6700\u8fd1\u66f2\u7ebf\uff1b\u6eda\u8f6e\u7f29\u653e\u56fe\u8868\uff1b\u53f3\u952e\u53ef\u8fd8\u539f\u56fe\u50cf\u5927\u5c0f\u3002<\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  const data = [];\n  const fits = new Map();\n  let selectedPointIndex = -1;\n  let selectedFitGroup = null;\n  const colors = ['#b5121b','#111111','#e0242e','#6b7280','#8b0000','#ef4444','#374151','#dc2626','#7f1d1d','#000000'];\n\n  const $ = id => document.getElementById(id);\n  const canvas = $('chart');\n  const ctx = canvas.getContext('2d');\n  let plot = {xMin:300,xMax:1000,yMin:1.4,yMax:1.55,left:70,right:28,top:28,bottom:58,w:0,h:0};\n  let view = null; \/\/ null \u8868\u793a\u81ea\u52a8\u9002\u914d\u5168\u90e8\u6570\u636e\uff1b\u6eda\u8f6e\u7f29\u653e\u540e\u4fdd\u5b58\u5f53\u524d\u89c6\u7a97\n  let mouse = {x:null,y:null,inside:false, lambdaSnap:null};\n  let hoverPointIndex = -1;\n  let hoverCurvePoint = null; \/\/ \u9f20\u6807\u5f53\u524d\u4f4d\u7f6e\u5728\u62df\u5408\u66f2\u7ebf\u4e0a\u7684\u6295\u5f71\u70b9\n  let drag = {active:false, moved:false, startX:0, startY:0, x:0, y:0};\n  const xSnapStepNm = 0.5;\n\n  function showMsg(text,type='ok'){\n    const el=$('msg'); el.textContent=text; el.className='message show '+type;\n  }\n  function clearMsg(){ $('msg').className='message'; $('msg').textContent=''; }\n  function fmt(v,d=6){ return Number.isFinite(v) ? v.toFixed(d) : '--'; }\n  function signedTerm(value, decimals, unitTerm){\n    if(!Number.isFinite(value)) return '';\n    const sign = value >= 0 ? ' + ' : ' - ';\n    return sign + Math.abs(value).toFixed(decimals) + unitTerm;\n  }\n  function denominatorTerm(C, decimals){\n    if(!Number.isFinite(C)) return '\u03bb\u00b2 - C';\n    return C >= 0 ? ('\u03bb\u00b2 - ' + C.toFixed(decimals)) : ('\u03bb\u00b2 + ' + Math.abs(C).toFixed(decimals));\n  }\n  function fullSellmeierFormula(f, decimals=10){\n    const first = 'n\u00b2(\u03bb) = ' + f.A.toFixed(decimals);\n    const bTerm = (f.B >= 0 ? ' + ' : ' - ') + Math.abs(f.B).toFixed(decimals) + ' \/ (' + denominatorTerm(f.C, decimals) + ')';\n    const dTerm = (f.D >= 0 ? ' - ' : ' + ') + Math.abs(f.D).toFixed(decimals) + ' \u00b7 \u03bb\u00b2';\n    return first + bTerm + dTerm;\n  }\n  function plainSellmeierFormula(f, decimals=10){\n    const cTerm = f.C >= 0 ? ('lambda_um^2 - ' + f.C.toFixed(decimals)) : ('lambda_um^2 + ' + Math.abs(f.C).toFixed(decimals));\n    const bTerm = (f.B >= 0 ? ' + ' : ' - ') + Math.abs(f.B).toFixed(decimals) + ' \/ (' + cTerm + ')';\n    const dTerm = (f.D >= 0 ? ' - ' : ' + ') + Math.abs(f.D).toFixed(decimals) + ' * lambda_um^2';\n    return 'n2(lambda_um) = ' + f.A.toFixed(decimals) + bTerm + dTerm;\n  }\n  function lambdaNmToS(lamNm){ const u = lamNm \/ 1000.0; return u*u; }\n  function sellmeierN2(coef, lamNm){\n    const s=lambdaNmToS(lamNm);\n    const den=s-coef.C;\n    if(Math.abs(den)<1e-14) return NaN;\n    return coef.A + coef.B\/den - coef.D*s;\n  }\n  function sellmeierEval(coef, lamNm){\n    const n2=sellmeierN2(coef, lamNm);\n    return Number.isFinite(n2) && n2>0 ? Math.sqrt(n2) : NaN;\n  }\n\n  \/\/ Sellmeier \u56db\u53c2\u6570\u62df\u5408\uff1an\u00b2 = A + B\/(\u03bb\u00b2 - C) - D\u00b7\u03bb\u00b2\u3002\n  \/\/ \u5176\u4e2d \u03bb \u4f7f\u7528 \u03bcm\u3002\u6a21\u578b\u5bf9 C \u975e\u7ebf\u6027\uff1b\u56fa\u5b9a C \u540e A\/B\/D \u53ef\u7ebf\u6027\u6700\u5c0f\u4e8c\u4e58\u6c42\u89e3\u3002\n  \/\/ \u4e3a\u4e86\u4fdd\u6301\u7eaf\u524d\u7aef\u3001\u65e0\u5916\u90e8\u5e93\uff0c\u8fd9\u91cc\u4f7f\u7528\u201c\u5168\u5c40\u7c97\u641c\u7d22 + \u9ec4\u91d1\u5206\u5272\u7ec6\u5316\u201d\u7684\u4e00\u7ef4\u4f18\u5316\u6765\u6c42 C\u3002\n  function fitSellmeier(points){\n    if(points.length<4) throw new Error('\u585e\u7c73\u5c14\u56db\u53c2\u6570\u62df\u5408\u81f3\u5c11\u9700\u89814\u4e2a\u6570\u636e\u70b9');\n    const sVals = points.map(p=>lambdaNmToS(p.lambda));\n    const yVals = points.map(p=>p.n*p.n);\n    const nVals = points.map(p=>p.n);\n    const minS = Math.min(...sVals), maxS = Math.max(...sVals);\n    const span = Math.max(maxS-minS, 1e-6);\n    const eps = Math.max(span*1e-4, 1e-8);\n\n    function solveLinearForC(C){\n      for(const s of sVals){ if(Math.abs(s-C)<=eps*0.02) return null; }\n      const M=[[0,0,0],[0,0,0],[0,0,0]];\n      const b=[0,0,0];\n      for(let i=0;i<sVals.length;i++){\n        const den=sVals[i]-C;\n        if(Math.abs(den)<1e-14) return null;\n        const r=[1, 1\/den, -sVals[i]]; \/\/ y = A + B\/(s-C) - D*s\n        for(let a=0;a<3;a++){\n          b[a]+=r[a]*yVals[i];\n          for(let c=0;c<3;c++) M[a][c]+=r[a]*r[c];\n        }\n      }\n      const sol=solve3x3(M,b);\n      if(!sol) return null;\n      const [A,B,D]=sol;\n      const coef={A,B,C,D};\n      let sse=0;\n      for(let i=0;i<sVals.length;i++){\n        const pred=sellmeierN2(coef, points[i].lambda);\n        if(!Number.isFinite(pred) || pred<=0) return null;\n        const e=yVals[i]-pred;\n        sse+=e*e;\n      }\n      return {coef,sse};\n    }\n\n    function score(C){\n      const r=solveLinearForC(C);\n      return r ? r.sse : Infinity;\n    }\n\n    \/\/ \u4e3a\u907f\u514d\u5728\u6d4b\u91cf\u6ce2\u6bb5\u5185\u51fa\u73b0\u6781\u70b9\uff0c\u9ed8\u8ba4\u4ec5\u5728\u6570\u636e\u6ce2\u6bb5\u5de6\u4fa7\u6216\u53f3\u4fa7\u641c\u7d22 C\u3002\n    \/\/ \u5e38\u89c1\u900f\u660e\u6750\u6599\u7684 Sellmeier C \u901a\u5e38\u4f4d\u4e8e\u6d4b\u8bd5\u6ce2\u6bb5\u4e0b\u65b9\uff0c\u8be5\u8303\u56f4\u80fd\u8986\u76d6\u5927\u591a\u6570\u573a\u666f\u3002\n    const intervals=[];\n    const leftMin = Math.min(-2, minS - 30*span);\n    const leftMax = minS - eps;\n    const rightMin = maxS + eps;\n    const rightMax = Math.max(maxS + 30*span, 4);\n    if(leftMax>leftMin) intervals.push([leftMin,leftMax]);\n    if(rightMax>rightMin) intervals.push([rightMin,rightMax]);\n\n    let best=null;\n    for(const [a,b] of intervals){\n      const N=900;\n      let prevC=a, prevS=score(a);\n      for(let i=1;i<=N;i++){\n        const C=a+(b-a)*i\/N;\n        const sc=score(C);\n        if(Number.isFinite(sc) && (!best || sc<best.sse)) best={C,sse:sc, left:prevC, right:Math.min(b, a+(b-a)*(i+1)\/N)};\n        if(Number.isFinite(prevS) && Number.isFinite(sc)){\n          \/\/ \u82e5\u4e24\u4fa7\u90fd\u6709\u6548\uff0c\u4fdd\u7559\u5c40\u90e8\u4f4e\u70b9\u9644\u8fd1\u62ec\u533a\n          if(i>1 && sc<prevS && (!best || sc<best.sse)) best={C,sse:sc,left:prevC,right:C};\n        }\n        prevC=C; prevS=sc;\n      }\n    }\n    if(!best) throw new Error('\u65e0\u6cd5\u627e\u5230\u7a33\u5b9a\u7684 C \u53c2\u6570\u641c\u7d22\u8303\u56f4\uff0c\u8bf7\u589e\u52a0\u6570\u636e\u70b9\u6216\u68c0\u67e5\u6570\u636e');\n\n    \/\/ \u591a\u505a\u51e0\u6b21\u5c40\u90e8\u7ec6\u5316\uff1a\u5148\u4ee5\u5168\u5c40\u7c97\u641c\u7d22\u6700\u4f73\u70b9\u4e3a\u4e2d\u5fc3\u53d6\u7a97\u53e3\uff0c\u518d\u7528\u9ec4\u91d1\u5206\u5272\u3002\n    let range = span*2;\n    let a = best.C - range, b = best.C + range;\n    if(best.C < minS){ b = Math.min(b, minS-eps); a = Math.min(a, b-span*0.01); }\n    else { a = Math.max(a, maxS+eps); b = Math.max(b, a+span*0.01); }\n    if(best.left!=null && best.right!=null && best.right>best.left){ a=Math.max(a,best.left); b=Math.min(b,best.right); }\n    const gr=(Math.sqrt(5)-1)\/2;\n    let c=b-gr*(b-a), d=a+gr*(b-a), fc=score(c), fd=score(d);\n    for(let it=0;it<100;it++){\n      if(!Number.isFinite(fc)){ c=(a+c)\/2; fc=score(c); }\n      if(!Number.isFinite(fd)){ d=(d+b)\/2; fd=score(d); }\n      if(fc<fd){ b=d; d=c; fd=fc; c=b-gr*(b-a); fc=score(c); }\n      else { a=c; c=d; fc=fd; d=a+gr*(b-a); fd=score(d); }\n      if(Math.abs(b-a)<1e-12) break;\n    }\n    const Copt=(a+b)\/2;\n    const fit=solveLinearForC(Copt) || solveLinearForC(best.C);\n    if(!fit) throw new Error('\u62df\u5408\u7ed3\u679c\u4e0d\u7a33\u5b9a\uff0c\u8bf7\u589e\u52a0\u6570\u636e\u70b9\u6216\u68c0\u67e5\u6ce2\u957f\u8303\u56f4');\n    const coef=fit.coef;\n\n    let sseN=0, sstN=0; const avg=nVals.reduce((p,q)=>p+q,0)\/nVals.length;\n    for(const p of points){\n      const yp=sellmeierEval(coef,p.lambda);\n      if(!Number.isFinite(yp)) throw new Error('\u62df\u5408\u66f2\u7ebf\u5728\u90e8\u5206\u6ce2\u957f\u4e0b\u65e0\u6709\u6548\u6298\u5c04\u7387');\n      sseN += (p.n-yp)*(p.n-yp);\n      sstN += (p.n-avg)*(p.n-avg);\n    }\n    const r2 = sstN<1e-24 ? (sseN<1e-20 ? 1 : 0) : 1 - sseN\/sstN;\n    const nF=sellmeierEval(coef,486.1327), nd=sellmeierEval(coef,587.5618), nCline=sellmeierEval(coef,656.2725);\n    const abbe = Number.isFinite(nF)&&Number.isFinite(nCline)&&Math.abs(nF-nCline)>1e-12 ? (nd-1)\/(nF-nCline) : NaN;\n    return {A:coef.A,B:coef.B,C:coef.C,D:coef.D,r2,abbe,nF,nd,nC:nCline,points:points.slice(), note: points.length===4?'\u56db\u70b9\u62df\u5408':'\u6700\u5c0f\u4e8c\u4e58\u62df\u5408'};\n  }\n\n  function solve3x3(A,b){\n    const m = A.map((row,i)=>row.slice().concat([b[i]]));\n    for(let col=0; col<3; col++){\n      let pivot=col;\n      for(let r=col+1;r<3;r++) if(Math.abs(m[r][col])>Math.abs(m[pivot][col])) pivot=r;\n      if(Math.abs(m[pivot][col])<1e-20) return null;\n      if(pivot!==col){ const tmp=m[col]; m[col]=m[pivot]; m[pivot]=tmp; }\n      const div=m[col][col];\n      for(let j=col;j<4;j++) m[col][j]\/=div;\n      for(let r=0;r<3;r++){\n        if(r===col) continue;\n        const factor=m[r][col];\n        for(let j=col;j<4;j++) m[r][j]-=factor*m[col][j];\n      }\n    }\n    return [m[0][3],m[1][3],m[2][3]];\n  }\n\n  function runFit(){\n    fits.clear(); selectedFitGroup=null;\n    const groups = groupData(); const failed=[];\n    for(const [g,pts] of groups.entries()){\n      try{ fits.set(g, fitSellmeier(pts)); }\n      catch(e){ failed.push(`${g}: ${e.message}`); }\n    }\n    renderAll();\n    view=null;\n    if(fits.size){ showMsg(`\u5df2\u5b8c\u6210 ${fits.size} \u7ec4\u585e\u7c73\u5c14\u62df\u5408\u3002${failed.length?' \u672a\u62df\u5408\uff1a'+failed.join('\uff1b'):''}`,'ok'); }\n    else showMsg('\u672a\u5b8c\u6210\u62df\u5408\uff1a'+(failed.join('\uff1b')||'\u8bf7\u81f3\u5c11\u8f93\u5165\u4e00\u7ec4\u56db\u4e2a\u6570\u636e\u70b9\u3002'),'err');\n  }\n\n  function groupData(){\n    const m=new Map();\n    for(const p of data){ if(!m.has(p.group)) m.set(p.group,[]); m.get(p.group).push(p); }\n    return m;\n  }\n\n  function renderDataList(){\n    $('pointCount').textContent = `${data.length} \u70b9`;\n    const box=$('dataList'); box.innerHTML='';\n    data.forEach((p,i)=>{\n      const div=document.createElement('div'); div.className='data-item'; if(i===selectedPointIndex) div.style.background='#fff0f1';\n      div.innerHTML=`<span class=\"g\">${escapeHtml(p.group)}<\/span><span>${p.lambda.toFixed(3)} nm<\/span><span>n=${p.n.toFixed(6)}<\/span><button title=\"\u5220\u9664\">\u00d7<\/button>`;\n      div.addEventListener('click',()=>{selectedPointIndex=i;renderAll();});\n      div.querySelector('button').addEventListener('click',(ev)=>{ev.stopPropagation();data.splice(i,1);if(selectedPointIndex>=data.length)selectedPointIndex=data.length-1;renderAll();});\n      box.appendChild(div);\n    });\n  }\n  function escapeHtml(s){return String(s).replace(\/[&<>\"]\/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));}\n\n  function renderResults(){\n    const box=$('resultGrid'); box.innerHTML='';\n    let k=0;\n    for(const [g,f] of fits.entries()){\n      const div=document.createElement('div'); div.className='result-card'+(selectedFitGroup===g?' active':'');\n      const color=colors[k%colors.length]; k++;\n      const formulaText = fullSellmeierFormula(f, 10);\n      div.innerHTML=`<h4 style=\"color:${color}\">${escapeHtml(g)}\uff5c${f.note}<\/h4>\n        <div class=\"full-formula\">${escapeHtml(formulaText)}<\/div>\n        <div class=\"unit-note\">\u516c\u5f0f\u4e2d \u03bb \u7684\u5355\u4f4d\u4e3a \u03bcm\uff1b\u8f93\u5165\/\u663e\u793a\u6ce2\u957f\u4ecd\u4e3a nm\u3002\u53c2\u6570\u5355\u4f4d\uff1aB\u3001C \u4e3a \u03bcm\u00b2\uff0cD \u4e3a \u03bcm\u207b\u00b2\u3002<\/div>\n        <div class=\"params\">\n          <span>A = ${fmt(f.A,10)}<\/span><span>R\u00b2 = ${fmt(f.r2,8)}<\/span>\n          <span>B = ${fmt(f.B,10)} \u03bcm\u00b2<\/span><span>Vd = ${fmt(f.abbe,4)}<\/span>\n          <span>C = ${fmt(f.C,10)} \u03bcm\u00b2<\/span><span>\u70b9\u6570 = ${f.points.length}<\/span>\n          <span>D = ${fmt(f.D,10)} \u03bcm\u207b\u00b2<\/span><span><\/span>\n        <\/div>`;\n      div.addEventListener('click',()=>{selectedFitGroup=g; renderAll();});\n      box.appendChild(div);\n    }\n    $('selectedGroup').textContent = selectedFitGroup || '\u672a\u9009\u62e9';\n  }\n\n  function resizeCanvas(){\n    const rect=canvas.parentElement.getBoundingClientRect();\n    const dpr=window.devicePixelRatio||1;\n    canvas.width=Math.max(300,Math.floor(rect.width*dpr)); canvas.height=Math.max(240,Math.floor(rect.height*dpr));\n    ctx.setTransform(dpr,0,0,dpr,0,0);\n    plot.w=rect.width; plot.h=rect.height;\n  }\n  function getAllExtents(){\n    const lambdas=data.map(p=>p.lambda); const ns=data.map(p=>p.n);\n    for(const [g,f] of fits.entries()){\n      const lMin=Math.min(...f.points.map(p=>p.lambda)), lMax=Math.max(...f.points.map(p=>p.lambda));\n      for(let i=0;i<80;i++){ const lam=lMin+(lMax-lMin)*i\/79; lambdas.push(lam); const yy=sellmeierEval(f,lam); if(Number.isFinite(yy)) ns.push(yy); }\n    }\n    let xMin=lambdas.length?Math.min(...lambdas):300, xMax=lambdas.length?Math.max(...lambdas):1200;\n    let yMin=ns.length?Math.min(...ns):1.4, yMax=ns.length?Math.max(...ns):1.6;\n    if(xMin===xMax){xMin*=0.9;xMax*=1.1;} if(yMin===yMax){yMin-=0.05;yMax+=0.05;}\n    const xp=(xMax-xMin)*0.1, yp=(yMax-yMin)*0.2;\n    return {xMin:xMin-xp,xMax:xMax+xp,yMin:yMin-yp,yMax:yMax+yp};\n  }\n  function X(lam){return plot.left+(lam-plot.xMin)\/(plot.xMax-plot.xMin)*(plot.w-plot.left-plot.right)}\n  function Y(n){return plot.top+(plot.yMax-n)\/(plot.yMax-plot.yMin)*(plot.h-plot.top-plot.bottom)}\n  function invX(px){return plot.xMin+(px-plot.left)\/(plot.w-plot.left-plot.right)*(plot.xMax-plot.xMin)}\n  function invY(py){return plot.yMax-(py-plot.top)\/(plot.h-plot.top-plot.bottom)*(plot.yMax-plot.yMin)}\n  function isInsidePlot(px,py){\n    return px>=plot.left && px<=plot.w-plot.right && py>=plot.top && py<=plot.h-plot.bottom;\n  }\n  function resetView(){\n    view = null;\n    drawChart();\n  }\n  function setViewFromWheel(px,py,deltaY){\n    if(!view) view = {...getAllExtents()};\n    const cx = invX(px);\n    const cy = invY(py);\n    if(!Number.isFinite(cx) || !Number.isFinite(cy)) return;\n    const factor = deltaY < 0 ? 0.82 : 1.22; \/\/ \u4e0a\u6eda\u653e\u5927\uff0c\u4e0b\u6eda\u7f29\u5c0f\n    const xSpan = Math.max((view.xMax-view.xMin)*factor, 0.5);\n    const ySpan = Math.max((view.yMax-view.yMin)*factor, 1e-9);\n    const xRatio = (cx-view.xMin)\/(view.xMax-view.xMin);\n    const yRatio = (cy-view.yMin)\/(view.yMax-view.yMin);\n    view.xMin = cx - xSpan*xRatio;\n    view.xMax = view.xMin + xSpan;\n    view.yMin = cy - ySpan*yRatio;\n    view.yMax = view.yMin + ySpan;\n    drawChart();\n  }\n  function snapXValue(v){ return Math.round(v\/xSnapStepNm)*xSnapStepNm; }\n  function niceXStep(span, target=8){\n    const raw = Math.max(span\/target, xSnapStepNm);\n    const pow = Math.pow(10, Math.floor(Math.log10(raw)));\n    const bases = [0.5,1,2,2.5,5,10];\n    let best = xSnapStepNm;\n    for(const m of [0.1,1,10,100,1000]){\n      for(const b of bases){\n        const step = b*pow*m;\n        const step05 = Math.ceil(step\/xSnapStepNm)*xSnapStepNm;\n        if(step05 >= raw){ return step05; }\n        best = step05;\n      }\n    }\n    return best;\n  }\n  function formatXTick(val, step){\n    if(step < 1) return val.toFixed(1);\n    if(Math.abs(step-Math.round(step))>1e-9) return val.toFixed(1);\n    return val.toFixed(0);\n  }\n  function nearestDataPoint(px,py){\n    let best=-1, dist=Infinity;\n    data.forEach((p,i)=>{\n      const dx=X(p.lambda)-px, dy=Y(p.n)-py;\n      const d=dx*dx+dy*dy;\n      if(d<dist){ dist=d; best=i; }\n    });\n    return dist <= 144 ? best : -1;\n  }\n  function groupColor(group){\n    const keys = Array.from(groupData().keys());\n    const idx = Math.max(0, keys.indexOf(group));\n    return colors[idx % colors.length];\n  }\n  function getHoverCurvePoint(lam, px, py){\n    if(!fits.size || !Number.isFinite(lam)) return null;\n    let entries;\n    \/\/ \u5df2\u7ecf\u5de6\u952e\u9009\u4e2d\u66f2\u7ebf\u65f6\uff0c\u9f20\u6807\u8ddf\u968f\u70b9\u56fa\u5b9a\u663e\u793a\u5728\u8be5\u66f2\u7ebf\u4e0a\uff1b\u672a\u9009\u4e2d\u65f6\uff0c\u53d6\u9f20\u6807\u5f53\u524d\u4f4d\u7f6e\u6700\u8fd1\u7684\u66f2\u7ebf\u3002\n    if(selectedFitGroup && fits.has(selectedFitGroup)) entries = [[selectedFitGroup, fits.get(selectedFitGroup)]];\n    else entries = Array.from(fits.entries());\n    let best = null, dist = Infinity;\n    for(const [g,f] of entries){\n      const n = sellmeierEval(f, lam);\n      const cx = X(lam), cy = Y(n);\n      if(!Number.isFinite(n) || !Number.isFinite(cx) || !Number.isFinite(cy)) continue;\n      const d = Math.abs(cy - py);\n      if(d < dist){ dist = d; best = {group:g, fit:f, lambda:lam, n, x:cx, y:cy, color:groupColor(g)}; }\n    }\n    return best;\n  }\n  function setViewFromSelection(x1,y1,x2,y2){\n    const gx0=plot.left, gy0=plot.top, gx1=plot.w-plot.right, gy1=plot.h-plot.bottom;\n    const sx0=Math.max(gx0,Math.min(gx1,Math.min(x1,x2)));\n    const sx1=Math.max(gx0,Math.min(gx1,Math.max(x1,x2)));\n    const sy0=Math.max(gy0,Math.min(gy1,Math.min(y1,y2)));\n    const sy1=Math.max(gy0,Math.min(gy1,Math.max(y1,y2)));\n    if(Math.abs(sx1-sx0)<8 || Math.abs(sy1-sy0)<8) return false;\n    const nx0=invX(sx0), nx1=invX(sx1);\n    const ny0=invY(sy1), ny1=invY(sy0);\n    view = {xMin:Math.min(nx0,nx1), xMax:Math.max(nx0,nx1), yMin:Math.min(ny0,ny1), yMax:Math.max(ny0,ny1)};\n    return true;\n  }\n\n  function drawChart(){\n    resizeCanvas(); Object.assign(plot, view || getAllExtents());\n    ctx.clearRect(0,0,plot.w,plot.h);\n    ctx.fillStyle='#fff'; ctx.fillRect(0,0,plot.w,plot.h);\n    const gx0=plot.left, gy0=plot.top, gw=plot.w-plot.left-plot.right, gh=plot.h-plot.top-plot.bottom;\n    ctx.strokeStyle='#e5e7eb'; ctx.lineWidth=1; ctx.beginPath(); ctx.rect(gx0,gy0,gw,gh); ctx.stroke();\n    ctx.font='12px Microsoft YaHei'; ctx.fillStyle='#6b7280'; ctx.textAlign='center'; ctx.textBaseline='top';\n    const xStep = niceXStep(plot.xMax-plot.xMin, 8);\n    const xStart = Math.ceil(plot.xMin\/xStep)*xStep;\n    for(let val=xStart, guard=0; val<=plot.xMax+1e-9 && guard<200; val+=xStep, guard++){\n      const x=X(val);\n      ctx.strokeStyle='#eef2f7';\n      ctx.beginPath(); ctx.moveTo(x,gy0); ctx.lineTo(x,gy0+gh); ctx.stroke();\n      ctx.fillText(formatXTick(val,xStep),x,gy0+gh+10);\n    }\n    \/\/ \u7f29\u653e\u5230\u8f83\u5c0f\u8303\u56f4\u65f6\u663e\u793a0.5 nm\u7ec6\u5206\u523b\u5ea6\uff0c\u4fbf\u4e8e\u7cbe\u786e\u8bfb\u6570\u3002\n    if((plot.xMax-plot.xMin) <= 80){\n      const minorStart = Math.ceil(plot.xMin\/xSnapStepNm)*xSnapStepNm;\n      ctx.strokeStyle='rgba(181,18,27,.09)';\n      for(let val=minorStart, guard=0; val<=plot.xMax+1e-9 && guard<300; val+=xSnapStepNm, guard++){\n        const x=X(val);\n        ctx.beginPath(); ctx.moveTo(x,gy0+gh); ctx.lineTo(x,gy0+gh-5); ctx.stroke();\n      }\n    }\n    ctx.textAlign='right'; ctx.textBaseline='middle';\n    for(let i=0;i<=6;i++){ const y=gy0+gh*i\/6; ctx.strokeStyle='#eef2f7'; ctx.beginPath(); ctx.moveTo(gx0,y); ctx.lineTo(gx0+gw,y); ctx.stroke(); const val=plot.yMax-(plot.yMax-plot.yMin)*i\/6; ctx.fillText(val.toFixed(3),gx0-8,y); }\n    ctx.fillStyle='#111'; ctx.font='14px Microsoft YaHei'; ctx.textAlign='center'; ctx.textBaseline='top'; ctx.fillText('\u03bb (nm)', gx0+gw\/2, plot.h-28);\n    ctx.save(); ctx.translate(10,gy0+gh\/2); ctx.rotate(-Math.PI\/2); ctx.fillText('Index n',0,0); ctx.restore();\n\n    const groups=Array.from(groupData().keys());\n    groups.forEach((g,idx)=>{\n      const color=colors[idx%colors.length]; const pts=groupData().get(g);\n      const f=fits.get(g);\n      if(f){\n        const lmin=Math.max(plot.xMin,Math.min(...pts.map(p=>p.lambda))*0.92); const lmax=Math.min(plot.xMax,Math.max(...pts.map(p=>p.lambda))*1.08);\n        ctx.strokeStyle=color; ctx.lineWidth=(selectedFitGroup===g)?3:2; ctx.beginPath();\n        let started=false;\n        for(let i=0;i<=240;i++){\n          const lam=lmin+(lmax-lmin)*i\/240; const yy=sellmeierEval(f,lam);\n          if(!Number.isFinite(yy)){ started=false; continue; }\n          const py=Y(yy); const px=X(lam);\n          if(!started){ ctx.moveTo(px,py); started=true; } else ctx.lineTo(px,py);\n        }\n        ctx.stroke();\n      }\n      pts.forEach(p=>{\n        const pointIndex=data.indexOf(p);\n        const r = pointIndex===hoverPointIndex ? 8 : 5;\n        ctx.fillStyle=color; ctx.strokeStyle=pointIndex===hoverPointIndex ? '#111' : '#fff'; ctx.lineWidth=pointIndex===hoverPointIndex ? 2.5 : 2;\n        ctx.beginPath(); ctx.arc(X(p.lambda),Y(p.n),r,0,Math.PI*2); ctx.fill(); ctx.stroke();\n      });\n    });\n    drawHoverOverlay();\n    drawSelectionOverlay();\n    renderLegend();\n  }\n\n  function drawHoverOverlay(){\n    if(!mouse.inside || mouse.x===null || mouse.y===null) return;\n    const gx0=plot.left, gy0=plot.top, gw=plot.w-plot.left-plot.right, gh=plot.h-plot.top-plot.bottom;\n    const lam = Number.isFinite(mouse.lambdaSnap) ? mouse.lambdaSnap : snapXValue(invX(mouse.x));\n    const x=Math.max(gx0, Math.min(gx0+gw, X(lam)));\n    ctx.save();\n    ctx.beginPath();\n    ctx.rect(gx0,gy0,gw,gh);\n    ctx.clip();\n\n    \/\/ \u7ad6\u7ebf\u53ea\u4ee3\u8868\u5f53\u524d\u9f20\u6807\u5bf9\u5e94\u7684\u6ce2\u957f\u4f4d\u7f6e\u3002\n    ctx.strokeStyle='rgba(181,18,27,.72)';\n    ctx.lineWidth=1.2;\n    ctx.setLineDash([5,5]);\n    ctx.beginPath();\n    ctx.moveTo(x,gy0);\n    ctx.lineTo(x,gy0+gh);\n    ctx.stroke();\n    ctx.setLineDash([]);\n\n    \/\/ \u8ddf\u968f\u9f20\u6807\u79fb\u52a8\u7684\u653e\u5927\u70b9\uff1ax \u662f\u9f20\u6807\u5bf9\u5e94\u6ce2\u957f\uff0cy \u662f\u8be5\u6ce2\u957f\u4e0b\u62df\u5408\u66f2\u7ebf\u7684 n \u503c\uff0c\u800c\u4e0d\u662f\u9f20\u6807\u81ea\u8eab\u7684 y \u5750\u6807\u3002\n    if(hoverCurvePoint && hoverCurvePoint.x>=gx0 && hoverCurvePoint.x<=gx0+gw && hoverCurvePoint.y>=gy0 && hoverCurvePoint.y<=gy0+gh){\n      ctx.fillStyle='rgba(255,255,255,.98)';\n      ctx.strokeStyle=hoverCurvePoint.color;\n      ctx.lineWidth=3;\n      ctx.beginPath();\n      ctx.arc(hoverCurvePoint.x, hoverCurvePoint.y, 7.5, 0, Math.PI*2);\n      ctx.fill();\n      ctx.stroke();\n      ctx.fillStyle=hoverCurvePoint.color;\n      ctx.beginPath();\n      ctx.arc(hoverCurvePoint.x, hoverCurvePoint.y, 3.2, 0, Math.PI*2);\n      ctx.fill();\n\n      const text=`${hoverCurvePoint.group} \u66f2\u7ebf  \u03bb=${hoverCurvePoint.lambda.toFixed(1)} nm, n=${hoverCurvePoint.n.toFixed(6)}`;\n      ctx.font='12px Microsoft YaHei';\n      const tw=ctx.measureText(text).width+18;\n      const tx=Math.min(hoverCurvePoint.x+12, gx0+gw-tw-6);\n      const ty=Math.max(gy0+8, hoverCurvePoint.y-38);\n      ctx.fillStyle='rgba(255,255,255,.96)';\n      ctx.strokeStyle='rgba(181,18,27,.55)';\n      ctx.lineWidth=1;\n      roundRect(ctx,tx,ty,tw,28,8); ctx.fill(); ctx.stroke();\n      ctx.fillStyle='#111'; ctx.textAlign='left'; ctx.textBaseline='middle'; ctx.fillText(text,tx+9,ty+14);\n    }\n\n    \/\/ \u82e5\u9f20\u6807\u63a5\u8fd1\u539f\u59cb\u5b9e\u6d4b\u70b9\uff0c\u4ecd\u7136\u663e\u793a\u8be5\u70b9\u4fe1\u606f\uff0c\u5e76\u4e14\u5b9e\u6d4b\u70b9\u672c\u8eab\u4f1a\u88ab\u653e\u5927\u3002\n    if(hoverPointIndex>=0){\n      const p=data[hoverPointIndex];\n      const px=X(p.lambda), py=Y(p.n);\n      ctx.fillStyle='rgba(255,255,255,.96)';\n      ctx.strokeStyle='rgba(17,17,17,.45)';\n      ctx.lineWidth=1;\n      const text=`\u5b9e\u6d4b\u70b9 ${p.group}  \u03bb=${p.lambda.toFixed(3)} nm, n=${p.n.toFixed(6)}`;\n      ctx.font='12px Microsoft YaHei';\n      const tw=ctx.measureText(text).width+18;\n      const tx=Math.min(px+12, gx0+gw-tw-6);\n      const ty=Math.max(gy0+8, py+14);\n      roundRect(ctx,tx,ty,tw,28,8); ctx.fill(); ctx.stroke();\n      ctx.fillStyle='#111'; ctx.textAlign='left'; ctx.textBaseline='middle'; ctx.fillText(text,tx+9,ty+14);\n    }\n    ctx.restore();\n  }\n  function drawSelectionOverlay(){\n    if(!drag.active || !drag.moved) return;\n    const gx0=plot.left, gy0=plot.top, gx1=plot.w-plot.right, gy1=plot.h-plot.bottom;\n    const x0=Math.max(gx0,Math.min(gx1,drag.startX));\n    const y0=Math.max(gy0,Math.min(gy1,drag.startY));\n    const x1=Math.max(gx0,Math.min(gx1,drag.x));\n    const y1=Math.max(gy0,Math.min(gy1,drag.y));\n    ctx.save();\n    ctx.fillStyle='rgba(181,18,27,.10)';\n    ctx.strokeStyle='rgba(181,18,27,.85)';\n    ctx.lineWidth=1.4;\n    ctx.setLineDash([6,4]);\n    ctx.fillRect(Math.min(x0,x1),Math.min(y0,y1),Math.abs(x1-x0),Math.abs(y1-y0));\n    ctx.strokeRect(Math.min(x0,x1),Math.min(y0,y1),Math.abs(x1-x0),Math.abs(y1-y0));\n    ctx.restore();\n  }\n  function roundRect(ctx,x,y,w,h,r){\n    ctx.beginPath();\n    ctx.moveTo(x+r,y); ctx.arcTo(x+w,y,x+w,y+h,r); ctx.arcTo(x+w,y+h,x,y+h,r);\n    ctx.arcTo(x,y+h,x,y,r); ctx.arcTo(x,y,x+w,y,r); ctx.closePath();\n  }\n\n  function renderLegend(){\n    const lg=$('legend'); lg.innerHTML=''; let i=0;\n    for(const g of groupData().keys()){\n      const div=document.createElement('div'); div.className='legend-item';\n      div.innerHTML=`<span class=\"swatch\" style=\"background:${colors[i%colors.length]}\"><\/span>${escapeHtml(g)}`; i++;\n      lg.appendChild(div);\n    }\n  }\n\n  function nearestFit(px,py){\n    let best=null, dist=Infinity;\n    for(const [g,f] of fits.entries()){\n      const pts=f.points; const lmin=Math.max(plot.xMin,Math.min(...pts.map(p=>p.lambda))*0.92); const lmax=Math.min(plot.xMax,Math.max(...pts.map(p=>p.lambda))*1.08);\n      for(let i=0;i<=240;i++){ const lam=lmin+(lmax-lmin)*i\/240; const yy=sellmeierEval(f,lam); if(!Number.isFinite(yy)) continue; const dx=X(lam)-px, dy=Y(yy)-py; const d=dx*dx+dy*dy; if(d<dist){dist=d; best=g;} }\n    }\n    return dist<900 ? best : null;\n  }\n\n  function calculateAtLambda(){\n    const lam=Number($('calcLambda').value);\n    if(!Number.isFinite(lam)||lam<=0){ $('calcOutput').textContent='\u8bf7\u8f93\u5165\u6709\u6548\u6ce2\u957f\u3002'; return; }\n    if(!fits.size){ $('calcOutput').textContent='\u8bf7\u5148\u8fdb\u884c\u585e\u7c73\u5c14\u62df\u5408\u3002'; return; }\n    let out=`\u03bb = ${lam.toFixed(3)} nm\\n`;\n    for(const [g,f] of fits.entries()) out += `${g}: n = ${sellmeierEval(f,lam).toFixed(8)}\\n`;\n    $('calcOutput').textContent=out.trim();\n  }\n\n  function exportCSV(){\n    let csv='type,group,lambda_nm,n,A,B_um2,C_um2,D_um-2,R2,Abbe_Vd,formula_lambda_um\\n';\n    for(const p of data) csv += `data,${p.group},${p.lambda},${p.n},,,,,,,\\n`;\n    for(const [g,f] of fits.entries()) csv += `fit,${g},,,${f.A},${f.B},${f.C},${f.D},${f.r2},${f.abbe},\"${plainSellmeierFormula(f,10)}\"\\n`;\n    const blob=new Blob([csv],{type:'text\/csv;charset=utf-8'}); const url=URL.createObjectURL(blob); const a=document.createElement('a'); a.href=url; a.download='sellmeier_fit_results.csv'; a.click(); URL.revokeObjectURL(url);\n  }\n  function exportPNG(){\n    const a=document.createElement('a'); a.href=canvas.toDataURL('image\/png'); a.download='sellmeier_fit_chart.png'; a.click();\n  }\n\n  function renderAll(){renderDataList(); renderResults(); drawChart();}\n\n  $('addBtn').onclick=()=>{clearMsg(); if(addPoint($('lambdaInput').value,$('nInput').value,$('groupInput').value)) showMsg('\u5df2\u6dfb\u52a0\u6570\u636e\u70b9\u3002','ok');};\n  $('importBtn').onclick=()=>{clearMsg(); const pts=parseBulk($('bulkInput').value); if(!pts.length){showMsg('\u6ca1\u6709\u8bc6\u522b\u5230\u6709\u6548\u6570\u636e\u3002','err');return;} data.length=0; data.push(...pts); selectedPointIndex=data.length-1; fits.clear(); selectedFitGroup=null; view=null; renderAll(); showMsg(`\u5df2\u5bfc\u5165 ${pts.length} \u4e2a\u6570\u636e\u70b9\u3002`,'ok');};\n  $('fitBtn').onclick=runFit;\n  $('clearBtn').onclick=()=>{data.length=0; fits.clear(); selectedPointIndex=-1; selectedFitGroup=null; view=null; clearMsg(); renderAll();};\n  $('deleteBtn').onclick=()=>{if(selectedPointIndex>=0){data.splice(selectedPointIndex,1); selectedPointIndex=Math.min(selectedPointIndex,data.length-1); fits.clear(); view=null; renderAll();}};\n  $('calcBtn').onclick=calculateAtLambda;\n  $('exportCsvBtn').onclick=exportCSV; $('exportPngBtn').onclick=exportPNG;\n  canvas.addEventListener('mousedown',ev=>{\n    if(ev.button!==0) return;\n    hideChartMenu();\n    const rect=canvas.getBoundingClientRect();\n    const x=ev.clientX-rect.left, y=ev.clientY-rect.top;\n    if(!isInsidePlot(x,y)) return;\n    drag.active=true; drag.moved=false; drag.startX=x; drag.startY=y; drag.x=x; drag.y=y;\n  });\n  canvas.addEventListener('mousemove',ev=>{\n    const rect=canvas.getBoundingClientRect();\n    const x=ev.clientX-rect.left, y=ev.clientY-rect.top;\n    mouse.x=x; mouse.y=y; mouse.inside=isInsidePlot(x,y);\n    mouse.lambdaSnap = mouse.inside ? snapXValue(invX(x)) : null;\n    hoverPointIndex = mouse.inside ? nearestDataPoint(x,y) : -1;\n    hoverCurvePoint = mouse.inside ? getHoverCurvePoint(mouse.lambdaSnap, x, y) : null;\n    if(drag.active){\n      drag.x=x; drag.y=y;\n      if(Math.hypot(drag.x-drag.startX, drag.y-drag.startY)>4) drag.moved=true;\n    }\n    $('cursorX').textContent=mouse.inside ? fmt(mouse.lambdaSnap,1) : fmt(invX(x),3);\n    $('cursorY').textContent=hoverCurvePoint ? fmt(hoverCurvePoint.n,5) : fmt(invY(y),5);\n    drawChart();\n  });\n  canvas.addEventListener('mouseup',ev=>{\n    if(ev.button!==0 || !drag.active) return;\n    const rect=canvas.getBoundingClientRect();\n    const x=ev.clientX-rect.left, y=ev.clientY-rect.top;\n    drag.x=x; drag.y=y;\n    if(drag.moved){\n      const changed=setViewFromSelection(drag.startX, drag.startY, drag.x, drag.y);\n      drag.active=false; drag.moved=false;\n      if(changed) drawChart();\n      return;\n    }\n    drag.active=false; drag.moved=false;\n    const g=nearestFit(x,y);\n    if(g){selectedFitGroup=g; renderAll();}\n    else drawChart();\n  });\n  canvas.addEventListener('mouseleave',()=>{ mouse.inside=false; mouse.x=null; mouse.y=null; mouse.lambdaSnap=null; hoverPointIndex=-1; hoverCurvePoint=null; if(drag.active){drag.active=false;drag.moved=false;} drawChart(); });\n  canvas.addEventListener('wheel',ev=>{\n    ev.preventDefault();\n    hideChartMenu();\n    const rect=canvas.getBoundingClientRect();\n    const x=ev.clientX-rect.left, y=ev.clientY-rect.top;\n    mouse.x=x; mouse.y=y; mouse.inside=isInsidePlot(x,y); mouse.lambdaSnap = mouse.inside ? snapXValue(invX(x)) : null; hoverPointIndex = mouse.inside ? nearestDataPoint(x,y) : -1; hoverCurvePoint = mouse.inside ? getHoverCurvePoint(mouse.lambdaSnap, x, y) : null;\n    if(mouse.inside) setViewFromWheel(x,y,ev.deltaY);\n  }, {passive:false});\n  canvas.addEventListener('contextmenu',ev=>{\n    ev.preventDefault();\n    showChartMenu(ev.clientX, ev.clientY);\n  });\n  function showChartMenu(clientX,clientY){\n    const menu=$('chartMenu');\n    menu.style.left=clientX+'px';\n    menu.style.top=clientY+'px';\n    menu.style.display='block';\n  }\n  function hideChartMenu(){ $('chartMenu').style.display='none'; }\n  $('resetZoomBtn').onclick=()=>{ hideChartMenu(); resetView(); };\n  document.addEventListener('click',ev=>{ if(!$('chartMenu').contains(ev.target)) hideChartMenu(); });\n  window.addEventListener('resize',drawChart);\n\n  \/\/ Load the user's example by default and fit immediately.\n  data.push({lambda:486.1327,n:1.501182,group:'1\u7ec4'},{lambda:531.951,n:1.497862,group:'1\u7ec4'},{lambda:635,n:1.492894,group:'1\u7ec4'},{lambda:980,n:1.485588,group:'1\u7ec4'});\n  try{ fits.set('1\u7ec4', fitSellmeier(data)); selectedFitGroup='1\u7ec4'; }catch(e){}\n  renderAll(); calculateAtLambda();\n})();\n<\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\u585e\u7c73\u5c14\u66f2\u7ebf\u62df\u5408\uff5c\u585e\u7c73\u5c14\u62df\u5408\u5de5\u5177 \u585e\u7c73\u5c14\u66f2<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-10447","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>\u585e\u7c73\u5c14\u62df\u5408 - VoyaWave<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\u585e\u7c73\u5c14\u62df\u5408 - VoyaWave\" \/>\n<meta property=\"og:description\" content=\"\u585e\u7c73\u5c14\u66f2\u7ebf\u62df\u5408\uff5c\u585e\u7c73\u5c14\u62df\u5408\u5de5\u5177 \u585e\u7c73\u5c14\u66f2\" \/>\n<meta property=\"og:url\" content=\"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/\" \/>\n<meta property=\"og:site_name\" content=\"VoyaWave\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-11T07:46:46+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/voyawave.com\\\/zh-hans\\\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\\\/\",\"url\":\"https:\\\/\\\/voyawave.com\\\/zh-hans\\\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\\\/\",\"name\":\"\u585e\u7c73\u5c14\u62df\u5408 - VoyaWave\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/#website\"},\"datePublished\":\"2026-08-06T08:36:51+00:00\",\"dateModified\":\"2026-08-11T07:46:46+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/voyawave.com\\\/zh-hans\\\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\\\/#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/voyawave.com\\\/zh-hans\\\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/voyawave.com\\\/zh-hans\\\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/voyawave.com\\\/zh-hans\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\u585e\u7c73\u5c14\u62df\u5408\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/#website\",\"url\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/\",\"name\":\"Shenzhen VoyaWave Optics Technology Co., Ltd.\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/#organization\",\"name\":\"\u542f\u626c\u5149\u5b66\",\"url\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/voyawave.com\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/2-240920110542592-scaled.png\",\"contentUrl\":\"https:\\\/\\\/voyawave.com\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/2-240920110542592-scaled.png\",\"width\":2560,\"height\":766,\"caption\":\"\u542f\u626c\u5149\u5b66\"},\"image\":{\"@id\":\"https:\\\/\\\/springgreen-quail-104003.hostingersite.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\u585e\u7c73\u5c14\u62df\u5408 - VoyaWave","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/","og_locale":"zh_CN","og_type":"article","og_title":"\u585e\u7c73\u5c14\u62df\u5408 - VoyaWave","og_description":"\u585e\u7c73\u5c14\u66f2\u7ebf\u62df\u5408\uff5c\u585e\u7c73\u5c14\u62df\u5408\u5de5\u5177 \u585e\u7c73\u5c14\u66f2","og_url":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/","og_site_name":"VoyaWave","article_modified_time":"2026-08-11T07:46:46+00:00","twitter_card":"summary_large_image","twitter_misc":{"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"1 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/","url":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/","name":"\u585e\u7c73\u5c14\u62df\u5408 - VoyaWave","isPartOf":{"@id":"https:\/\/springgreen-quail-104003.hostingersite.com\/#website"},"datePublished":"2026-08-06T08:36:51+00:00","dateModified":"2026-08-11T07:46:46+00:00","breadcrumb":{"@id":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/voyawave.com\/zh-hans\/%e5%a1%9e%e7%b1%b3%e5%b0%94%e6%8b%9f%e5%90%88\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/voyawave.com\/zh-hans\/"},{"@type":"ListItem","position":2,"name":"\u585e\u7c73\u5c14\u62df\u5408"}]},{"@type":"WebSite","@id":"https:\/\/springgreen-quail-104003.hostingersite.com\/#website","url":"https:\/\/springgreen-quail-104003.hostingersite.com\/","name":"Shenzhen VoyaWave Optics Technology Co., Ltd.","description":"","publisher":{"@id":"https:\/\/springgreen-quail-104003.hostingersite.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/springgreen-quail-104003.hostingersite.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"zh-Hans"},{"@type":"Organization","@id":"https:\/\/springgreen-quail-104003.hostingersite.com\/#organization","name":"\u542f\u626c\u5149\u5b66","url":"https:\/\/springgreen-quail-104003.hostingersite.com\/","logo":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/springgreen-quail-104003.hostingersite.com\/#\/schema\/logo\/image\/","url":"https:\/\/voyawave.com\/wp-content\/uploads\/2025\/05\/2-240920110542592-scaled.png","contentUrl":"https:\/\/voyawave.com\/wp-content\/uploads\/2025\/05\/2-240920110542592-scaled.png","width":2560,"height":766,"caption":"\u542f\u626c\u5149\u5b66"},"image":{"@id":"https:\/\/springgreen-quail-104003.hostingersite.com\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/pages\/10447","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=10447"}],"version-history":[{"count":16,"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/pages\/10447\/revisions"}],"predecessor-version":[{"id":10613,"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/pages\/10447\/revisions\/10613"}],"wp:attachment":[{"href":"https:\/\/voyawave.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=10447"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}