| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function convertWavelength(theta_deg, sourceWavelength, targetWavelength) { |
| if (Math.abs(sourceWavelength - targetWavelength) < 0.0001) { |
| return theta_deg |
| } |
|
|
| const theta_rad = (theta_deg * Math.PI) / 180 |
| const sin_theta2 = (targetWavelength / sourceWavelength) * Math.sin(theta_rad) |
|
|
| if (Math.abs(sin_theta2) > 1) { |
| return null |
| } |
|
|
| const theta2_rad = Math.asin(sin_theta2) |
| return (theta2_rad * 180) / Math.PI |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function interpolateData(x, y, targetSize, xMin, xMax, strategy = 'linear') { |
| if (x.length === targetSize && xMin === undefined) { |
| return { x, y } |
| } |
|
|
| const minX = xMin !== undefined ? xMin : Math.min(...x) |
| const maxX = xMax !== undefined ? xMax : Math.max(...x) |
| const step = (maxX - minX) / (targetSize - 1) |
|
|
| const newX = Array.from({ length: targetSize }, (_, i) => minX + i * step) |
| const newY = new Array(targetSize) |
|
|
| const dataMinX = Math.min(...x) |
| const dataMaxX = Math.max(...x) |
|
|
| if (strategy === 'linear') { |
| for (let i = 0; i < targetSize; i++) { |
| const targetX = newX[i] |
|
|
| if (targetX < dataMinX || targetX > dataMaxX) { |
| newY[i] = 0 |
| continue |
| } |
|
|
| let idx = x.findIndex(val => val >= targetX) |
| if (idx === -1) idx = x.length - 1 |
| if (idx === 0) idx = 1 |
|
|
| const x0 = x[idx - 1] |
| const x1 = x[idx] |
| const y0 = y[idx - 1] |
| const y1 = y[idx] |
|
|
| newY[i] = y0 + ((targetX - x0) * (y1 - y0)) / (x1 - x0) |
| } |
| } else if (strategy === 'cubic') { |
| for (let i = 0; i < targetSize; i++) { |
| const targetX = newX[i] |
|
|
| if (targetX < dataMinX || targetX > dataMaxX) { |
| newY[i] = 0 |
| continue |
| } |
|
|
| let idx = x.findIndex(val => val >= targetX) |
| if (idx === -1) idx = x.length - 1 |
| if (idx === 0) idx = 1 |
|
|
| const i0 = Math.max(0, idx - 2) |
| const i1 = Math.max(0, idx - 1) |
| const i2 = Math.min(x.length - 1, idx) |
| const i3 = Math.min(x.length - 1, idx + 1) |
|
|
| if (i2 === i1) { |
| newY[i] = y[i1] |
| } else { |
| const t = (targetX - x[i1]) / (x[i2] - x[i1]) |
| const t2 = t * t |
| const t3 = t2 * t |
|
|
| const v0 = y[i0] |
| const v1 = y[i1] |
| const v2 = y[i2] |
| const v3 = y[i3] |
|
|
| newY[i] = 0.5 * ( |
| 2 * v1 + |
| (-v0 + v2) * t + |
| (2 * v0 - 5 * v1 + 4 * v2 - v3) * t2 + |
| (-v0 + 3 * v1 - 3 * v2 + v3) * t3 |
| ) |
| } |
| } |
| } |
|
|
| return { x: newX, y: newY } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function parseDIF(text) { |
| const lines = text.split('\n') |
| const x = [] |
| const y = [] |
|
|
| for (const line of lines) { |
| const trimmed = line.trim() |
|
|
| if (!trimmed || |
| trimmed.startsWith('#') || |
| trimmed.startsWith('_') || |
| trimmed.startsWith('CELL') || |
| trimmed.startsWith('SPACE') || |
| /^[a-zA-Z]/.test(trimmed)) { |
| continue |
| } |
|
|
| const parts = trimmed.split(/\s+/) |
| if (parts.length >= 2) { |
| const xVal = parseFloat(parts[0]) |
| const yVal = parseFloat(parts[1]) |
|
|
| if (!isNaN(xVal) && !isNaN(yVal)) { |
| x.push(xVal) |
| y.push(yVal) |
| } |
| } |
| } |
|
|
| return { x, y } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function extractMetadata(text) { |
| const metadata = { |
| wavelength: null, |
| cellParams: null, |
| spaceGroup: null, |
| crystalSystem: null |
| } |
|
|
| const lines = text.split('\n') |
|
|
| const wavelengthPatterns = [ |
| /wavelength[:\s=]+([0-9.]+)/i, |
| /lambda[:\s=]+([0-9.]+)/i, |
| /wave[:\s=]+([0-9.]+)/i, |
| /_pd_wavelength[:\s]+([0-9.]+)/i, |
| /_diffrn_radiation_wavelength[:\s]+([0-9.]+)/i, |
| /radiation.*?([0-9.]+)\s*[AÅ]/i, |
| ] |
|
|
| for (const line of lines) { |
| if (!metadata.wavelength) { |
| for (const pattern of wavelengthPatterns) { |
| const match = line.match(pattern) |
| if (match && match[1]) { |
| const wavelength = parseFloat(match[1]) |
| if (wavelength > 0.1 && wavelength < 3.0) { |
| metadata.wavelength = wavelength |
| break |
| } |
| } |
| } |
|
|
| if (/Cu\s*K[αa]/i.test(line)) metadata.wavelength = 1.5406 |
| else if (/Mo\s*K[αa]/i.test(line)) metadata.wavelength = 0.7107 |
| else if (/Co\s*K[αa]/i.test(line)) metadata.wavelength = 1.7889 |
| else if (/Cr\s*K[αa]/i.test(line)) metadata.wavelength = 2.2897 |
| } |
|
|
| if (/CELL PARAMETERS:/i.test(line)) { |
| const match = line.match(/CELL PARAMETERS:\s*([\d.\s]+)/) |
| if (match) { |
| metadata.cellParams = match[1].trim() |
| } |
| } |
|
|
| if (/SPACE GROUP:/i.test(line) || /_symmetry_Int_Tables_number/i.test(line)) { |
| const match = line.match(/(?:SPACE GROUP:|_symmetry_Int_Tables_number)[:\s]+(\d+)/) |
| if (match) { |
| metadata.spaceGroup = match[1] |
| } |
| } |
|
|
| if (/Crystal System:/i.test(line)) { |
| const match = line.match(/Crystal System:\s*(\d+)/) |
| if (match) { |
| metadata.crystalSystem = match[1] |
| } |
| } |
| } |
|
|
| return metadata |
| } |
|
|