diff --git a/CHANGELOG.md b/CHANGELOG.md index 575dc72..1aa3d79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * 将XericLibrary提供的数据类型命名空间移动到Runtime下。这可能会带来一些兼容性问题,但现在Type成员太多了,再不调整这个遗留问题就不好解决了。 * 添加图表数据结构 +* 添加windows7+的应用程序图表进度条功能。 # [0.6.3] 2026-07-16 diff --git a/Runtime/MacroLibrary/MacroCurveMapping.cs b/Runtime/MacroLibrary/MacroCurveMapping.cs index 9cc4977..823c19d 100644 --- a/Runtime/MacroLibrary/MacroCurveMapping.cs +++ b/Runtime/MacroLibrary/MacroCurveMapping.cs @@ -15,7 +15,7 @@ namespace XericLibrary.Runtime.MacroLibrary #endif public static class MacroCurveMapping { - #region Z 字曲线 (Morton Code) + #region Z Morton Code /// /// 将 2D 块坐标交织为 1D Z 索引 (Morton Code) @@ -140,7 +140,7 @@ namespace XericLibrary.Runtime.MacroLibrary #endregion - #region Hilbert 曲线 (2D) + #region Hilbert Curve 2D // 2D Hilbert 状态转移表: encodeTable[state][rx*2+ry] = (quadrant << 2) | nextState private static readonly byte[] Hilbert2DEncodeTable = new byte[16] @@ -217,7 +217,7 @@ namespace XericLibrary.Runtime.MacroLibrary #endregion - #region Hilbert 曲线 (3D) + #region Hilbert Curve 3D // 3D Hilbert 编码状态转移表: encode3DTable[state][rx*4+ry*2+rz] = (octant << 3) | nextState // state 索引原始坐标位(rx,ry,rz), 返回:高4位为octant,低3位为nextState @@ -324,5 +324,324 @@ namespace XericLibrary.Runtime.MacroLibrary } #endregion + + #region Peano Curve 2D 3×3 + + // Peano 编码表: encodePeano[state][tx*3+ty] = (digit << 1) | nextState + // state 0 = 正向扫描, state 1 = 反向扫描 + // 正向: row0 L→R (digits 0,1,2), row1 R→L (digits 3,4,5), row2 L→R (digits 6,7,8) + // 反向: 整体倒序 + private static readonly byte[] Peano2DEncodeTable = new byte[18] + { + // state 0 (正向) + 0x01, 0x02, 0x05, 0x06, 0x09, 0x0A, 0x0D, 0x0E, 0x11, + // state 1 (反向) + 0x0C, 0x0F, 0x10, 0x0B, 0x08, 0x07, 0x04, 0x03, 0x00, + }; + + // Peano 解码表: decodePeano[state][digit] = (tx << 3) | (ty << 1) | nextState + private static readonly byte[] Peano2DDecodeTable = new byte[18] + { + // state 0 + 0x01, 0x08, 0x11, 0x12, 0x0B, 0x02, 0x05, 0x0C, 0x15, + // state 1 + 0x15, 0x0C, 0x05, 0x02, 0x0B, 0x12, 0x11, 0x08, 0x01, + }; + + /// + /// 将 2D 坐标编码为 Peano 曲线索引 + /// + /// X 坐标 (非负, 0~3^order-1) + /// Y 坐标 (非负, 0~3^order-1) + /// 曲线阶数 (1~10), 网格大小为 3^order + /// Peano 索引 (9^order) +#if ENABLE_BURST + [BurstCompile] +#endif + public static ulong Peano2DEncode(int x, int y, int order) + { + ulong index = 0; + int state = 0; + int divisor = 1; + for (int s = 0; s < order; s++) + divisor *= 3; + + for (int s = order - 1; s >= 0; s--) + { + divisor /= 3; + int tx = (x / divisor) % 3; + int ty = (y / divisor) % 3; + int key = state * 9 + tx * 3 + ty; + byte entry = Peano2DEncodeTable[key]; + int digit = entry >> 1; + state = entry & 1; + index = index * 9 + (uint)digit; + } + return index; + } + + /// + /// 将 Peano 曲线索引解码为 2D 坐标 + /// + /// Peano 索引 + /// 曲线阶数 + /// 解码后的 X 坐标 + /// 解码后的 Y 坐标 +#if ENABLE_BURST + [BurstCompile] +#endif + public static void Peano2DDecode(ulong index, int order, out int x, out int y) + { + x = 0; + y = 0; + int state = 0; + ulong divisor = 1; + for (int s = 0; s < order - 1; s++) + divisor *= 9; + + for (int s = 0; s < order; s++) + { + int digit = (int)((index / divisor) % 9); + int key = state * 9 + digit; + byte entry = Peano2DDecodeTable[key]; + int tx = entry >> 3; + int ty = (entry >> 1) & 3; + state = entry & 1; + x = x * 3 + tx; + y = y * 3 + ty; + divisor /= 9; + } + } + + #endregion + + #region Moore 2D Hilbert variant + + // Moore 曲线与 Hilbert 共享状态转移表 + // 区别在于: Moore 的 top-level 4 个象限首尾相连形成闭环 + // 每个象限内包含一个 order-1 阶 Hilbert 子曲线 + + /// + /// 将 2D 坐标编码为 Moore 曲线索引(闭环 Hilbert) + /// + /// X 坐标 (非负) + /// Y 坐标 (非负) + /// 曲线阶数 (1~16), 网格大小为 2^order + /// Moore 索引 +#if ENABLE_BURST + [BurstCompile] +#endif + public static ulong Moore2DEncode(int x, int y, int order) + { + // order=1 时与 Hilbert 相同 + if (order <= 1) + return Hilbert2DEncode(x, y, order); + + int half = 1 << (order - 1); + int subOrder = order - 1; + + // 确定象限: TL=0, TR=1, BR=2, BL=3 + // 象限内坐标变换 + 调用 Hilbert 子编码 + switch ((x >= half ? 1 : 0) | ((y >= half ? 1 : 0) << 1)) + { + case 0: // TL - 子曲线入口在下, 出口在右 + { + ulong sub = Hilbert2DEncode(x, half - 1 - y, subOrder); + return (0UL << (subOrder * 2)) | sub; + } + case 1: // TR - 子曲线入口在左, 出口在下 + { + ulong sub = Hilbert2DEncode(y, half - 1 - (x - half), subOrder); + return (1UL << (subOrder * 2)) | sub; + } + case 3: // BR - 子曲线入口在上, 出口在左 + { + ulong sub = Hilbert2DEncode(half - 1 - (x - half), half - 1 - (y - half), subOrder); + return (2UL << (subOrder * 2)) | sub; + } + default: // BL - 子曲线入口在右, 出口在上 + { + ulong sub = Hilbert2DEncode(half - 1 - y, x, subOrder); + return (3UL << (subOrder * 2)) | sub; + } + } + } + + /// + /// 将 Moore 曲线索引解码为 2D 坐标 + /// + /// Moore 索引 + /// 曲线阶数 + /// 解码后的 X 坐标 + /// 解码后的 Y 坐标 +#if ENABLE_BURST + [BurstCompile] +#endif + public static void Moore2DDecode(ulong index, int order, out int x, out int y) + { + if (order <= 1) + { + Hilbert2DDecode(index, order, out x, out y); + return; + } + + int subOrder = order - 1; + int half = 1 << subOrder; + + // 提取最高 2 bits 确定象限 + ulong quadrant = index >> (subOrder * 2); + ulong subIndex = index & (((1UL << (subOrder * 2)) - 1)); + + int sx, sy; + switch (quadrant) + { + case 0: // TL + Hilbert2DDecode(subIndex, subOrder, out sx, out sy); + x = sx; + y = half - 1 - sy; + break; + case 1: // TR + Hilbert2DDecode(subIndex, subOrder, out sx, out sy); + x = half + (half - 1 - sy); + y = sx; + break; + case 2: // BR + Hilbert2DDecode(subIndex, subOrder, out sx, out sy); + x = half + (half - 1 - sx); + y = half + (half - 1 - sy); + break; + default: // BL + Hilbert2DDecode(subIndex, subOrder, out sx, out sy); + x = sy; + y = half + sx; + break; + } + } + + #endregion + + #region 方形螺旋曲线 (Square Spiral, 中心向外) + + /// + /// 将 2D 坐标编码为方形螺旋索引(从中心(0,0)开始,向右→上→左→下→右…螺旋向外) + /// + /// X 坐标 + /// Y 坐标 + /// 螺旋索引 (0-based),中心(0,0)=0 +#if ENABLE_BURST + [BurstCompile] +#endif + public static ulong Spiral2DEncode(int x, int y) + { + // 确定环数 k = max(|x|, |y|) + int ax = x < 0 ? -x : x; + int ay = y < 0 ? -y : y; + int k = ax > ay ? ax : ay; + + if (k == 0) + return 0; + + // 环 k 的起始索引 = (2k-1)^2 + ulong kk = (ulong)k; + ulong baseIdx = (2 * kk - 1) * (2 * kk - 1); + + // 环 k 内 4 段: A(右侧↑), B(顶侧←), C(左侧↓), D(底侧→) + // A: x == k, y ∈ [-(k-1), k] → pos = y + k - 1 (0 ~ 2k-1) + // B: x ∈ [k-1, -k], y == k → pos = 3k - 1 - x (2k ~ 4k-1) + // C: x == -k, y ∈ [k-1, -k] → pos = 5k - 1 - y (4k ~ 6k-1) + // D: x ∈ [-(k-1), k], y == -k → pos = x + 7k - 1 (6k ~ 8k-1) + + ulong pos; + if (x == k && y > -k) + { + // A: 右侧向上 + pos = (ulong)(y + k - 1); + } + else if (y == k) + { + // B: 顶侧向左 + pos = (ulong)(3 * k - 1 - x); + } + else if (x == -k) + { + // C: 左侧向下 + pos = (ulong)(5 * k - 1 - y); + } + else + { + // D: 底侧向右 (y == -k) + pos = (ulong)(x + 7 * k - 1); + } + + return baseIdx + pos; + } + + /// + /// 将方形螺旋索引解码为 2D 坐标 + /// + /// 螺旋索引 (0-based) + /// 解码后的 X 坐标 + /// 解码后的 Y 坐标 +#if ENABLE_BURST + [BurstCompile] +#endif + public static void Spiral2DDecode(ulong index, out int x, out int y) + { + if (index == 0) + { + x = 0; + y = 0; + return; + } + + // 计算环数 k = floor((sqrt(index) + 1) / 2) + int k = (int)((System.Math.Sqrt((double)index) + 1.0) * 0.5); + + ulong kk = (ulong)k; + ulong baseIdx = (2 * kk - 1) * (2 * kk - 1); + // 如果 baseIdx > index,说明 k 估大了(浮点误差),减 1 + if (baseIdx > index) + { + k--; + kk = (ulong)k; + baseIdx = (2 * kk - 1) * (2 * kk - 1); + } + + ulong pos = index - baseIdx; // 0 ~ 8k-1 + int segLen = 2 * k; + + if (pos < (ulong)segLen) + { + // A: 右侧向上 + x = k; + y = (int)pos - k + 1; + return; + } + pos -= (ulong)segLen; + + if (pos < (ulong)segLen) + { + // B: 顶侧向左 + x = k - 1 - (int)pos; + y = k; + return; + } + pos -= (ulong)segLen; + + if (pos < (ulong)segLen) + { + // C: 左侧向下 + x = -k; + y = k - 1 - (int)pos; + return; + } + pos -= (ulong)segLen; + + // D: 底侧向右 + x = -(k - 1) + (int)pos; + y = -k; + } + + #endregion } } diff --git a/Runtime/XericLibrary.dll b/Runtime/XericLibrary.dll index d60bb3f..0812d4e 100644 Binary files a/Runtime/XericLibrary.dll and b/Runtime/XericLibrary.dll differ