You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求:将Office EllipticalArcTo参数转换为ECMA-376 arcTo参数(求JS实现)

椭圆弧参数转换:EllipticalArcTo → Office Open XML arcTo

参数对应说明

先明确两个接口的核心参数定义:

  • Office插件EllipticalArcTo:x, y(弧终点坐标)、A(椭圆水平半径)、B(椭圆垂直半径)、C(椭圆旋转角度,单位:度)、D(扫掠方向:0=顺时针,1=逆时针)
  • Office Open XML arcTo:hR(水平半径)、wR(垂直半径)、stAng(起始角度,单位:度,从x轴正方向逆时针计算)、swAng(扫掠角度,正=逆时针,负=顺时针)

JS实现代码

function convertEllipticalArcToOOXML(startX, startY, endX, endY, hR, wR, rotAngleDeg, sweepDir) {
    // 旋转角度转弧度
    const rotAngleRad = rotAngleDeg * Math.PI / 180;
    // 计算起点到终点的半向量
    const dx = (endX - startX) / 2;
    const dy = (endY - startY) / 2;

    // 逆旋转:将椭圆转为轴对齐状态,简化计算
    const x1 = Math.cos(rotAngleRad) * dx + Math.sin(rotAngleRad) * dy;
    const y1 = -Math.sin(rotAngleRad) * dx + Math.cos(rotAngleRad) * dy;

    // 修正椭圆半径:若起点终点距离超过椭圆覆盖范围,按比例放大半径避免计算异常
    const x1Sq = x1 * x1;
    const y1Sq = y1 * y1;
    const hRSq = hR * hR;
    const wRSq = wR * wR;
    let scale = x1Sq / hRSq + y1Sq / wRSq;
    if (scale > 1) {
        scale = Math.sqrt(scale);
        hR *= scale;
        wR *= scale;
    }

    // 计算轴对齐椭圆的中心坐标
    const hRSqNew = hR * hR;
    const wRSqNew = wR * wR;
    const numerator = hRSqNew * wRSqNew - hRSqNew * y1Sq - wRSqNew * x1Sq;
    const denominator = hRSqNew * y1Sq + wRSqNew * x1Sq;
    const k = Math.sqrt(Math.max(numerator / denominator, 0));
    let cxPrime = k * hR * y1 / wR;
    let cyPrime = -k * wR * x1 / hR;

    // 根据扫掠方向调整中心位置
    if (sweepDir === 0) {
        cxPrime = -cxPrime;
        cyPrime = -cyPrime;
    }

    // 将中心坐标转换回原旋转坐标系
    const cx = Math.cos(rotAngleRad) * cxPrime - Math.sin(rotAngleRad) * cyPrime + (startX + endX) / 2;
    const cy = Math.sin(rotAngleRad) * cxPrime + Math.cos(rotAngleRad) * cyPrime + (startY + endY) / 2;

    // 计算起始角度(中心到起点的连线与x轴夹角,扣除椭圆旋转角度)
    let startAngleRad = Math.atan2(startY - cy, startX - cx) - rotAngleRad;
    // 修正角度到0-2π范围
    startAngleRad = (startAngleRad + 2 * Math.PI) % (2 * Math.PI);
    const startAngleDeg = startAngleRad * 180 / Math.PI;

    // 计算扫掠角度
    let endAngleRad = Math.atan2(endY - cy, endX - cx) - rotAngleRad;
    endAngleRad = (endAngleRad + 2 * Math.PI) % (2 * Math.PI);

    let sweepAngleRad = endAngleRad - startAngleRad;
    // 根据扫掠方向调整角度正负
    if (sweepDir === 0) {
        sweepAngleRad = sweepAngleRad > 0 ? sweepAngleRad - 2 * Math.PI : sweepAngleRad;
    } else {
        sweepAngleRad = sweepAngleRad < 0 ? sweepAngleRad + 2 * Math.PI : sweepAngleRad;
    }
    const sweepAngleDeg = sweepAngleRad * 180 / Math.PI;

    return {
        hR: hR,
        wR: wR,
        stAng: startAngleDeg,
        swAng: sweepAngleDeg
    };
}

使用示例

// 示例:当前路径起点(100,100),EllipticalArcTo参数为终点(200,200)、水平半径50、垂直半径30、旋转30度、顺时针方向
const ooxmlArcParams = convertEllipticalArcToOOXML(100, 100, 200, 200, 50, 30, 30, 0);
console.log(ooxmlArcParams);
// 输出包含hR、wR、stAng、swAng的参数对象

核心逻辑说明

  1. 坐标逆旋转:把带旋转的椭圆转为轴对齐状态,大幅简化角度和中心的计算
  2. 半径修正:避免因起点终点距离过大导致的椭圆无法覆盖的计算错误
  3. 角度校准:基于椭圆中心计算起始角度,结合扫掠方向调整扫掠角度的正负,完全匹配OOXML规范要求

内容的提问来源于stack exchange,提问作者Кobak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:52:39