C++实现Delaunay三角网:增量插入与Bowyer-Watson算法详解
简介这份资源是用C实现Delaunay三角网算法的完整工程代码面向计算机图形学、地理信息系统与有限元方向的开发者及算法学习者帮助理解空圆特性、逐点插入与空圆检测等核心构建逻辑。压缩包共42个文件约2.66MB以h头文件与cpp源文件为主体另有obj、sbr等编译中间文件以及ico、bmp、cur等界面资源并附带dsp、dsw工程文件与可执行程序便于直接编译运行与调试。资源已有1882人学习下载代码结构完整涵盖点与三角形数据结构设计、三角网构建、空圆检测、悬挂边与孤岛优化等关键环节读者可结合源码逐步跟踪算法流程验证三角剖分结果的正确性并在此基础上扩展可视化输出或移植到实际项目中适合作为算法入门与工程实践的参考范例。1. Delaunay三角网算法用C实现从点云到网格一次把边界和性能讲透做点云处理、地形建模或者有限元前处理的人迟早会撞上 Delaunay 三角网。它解决的问题很具体给你一堆散乱的点怎么连成一张“形状最合理”的三角网格。所谓合理核心就是空外接圆准则——任意一个三角形的外接圆内部不允许再落进其他点。这条约束带来的直接好处是三角形尽量接近等边不会出现又细又长的“针状”单元后续做插值、做有限元、做渲染都稳。用 C 实现它动机通常有两个一是性能点数上到十万、百万级时脚本语言的常数开销扛不住二是可控你需要自己决定数值精度、内存布局、退化点怎么处理。这篇笔记就按“先立住原理再动手复现最后讲坑”的顺序走把增量插入法、Bowyer-Watson 空腔重构、边翻转这几条主线落到能编译运行的代码上也会讲清楚哪些参数一动就翻车。适合已经会写 C、但第一次自己撸三角剖分的同学也适合想替换掉手头慢库的老手。2. 先把算法骨架立住增量插入与空外接圆判定2.1 为什么选增量插入而不是分治Delaunay 三角网的经典构造有三条路分治法、扫描线法、增量插入法。分治法理论复杂度漂亮O(n log n)但实现里递归切分、合并凸包、处理跨子集的边代码量和调试成本都高。扫描线法对点的排序和事件队列要求苛刻退化情况多。增量插入法是工程上最常被选中的逻辑直白一个点一个点插进去局部修复出问题容易定位。增量插入的核心循环只有三步定位新点落在哪个三角形里删除所有外接圆包含新点的三角形形成一个空腔把空腔边界和新点连起来重新三角化。第三步之后还要做一次合法性检查把不满足空外接圆准则的边翻转掉。听起来简单但“定位”和“翻转”这两步是性能与正确性的分水岭。我一般会先搭一个足够小的骨架用随机点跑通再逐步加规模。下面是最小可运行的结构定义点、边、三角形都用索引表达避免指针满天飞。#include vector #include cmath #include cstdint struct Point { double x, y; }; struct Triangle { int v[3]; // 三个顶点在点数组中的索引 int nbr[3]; // 三条边对应的邻接三角形索引-1 表示边界 bool alive; // 是否仍有效被删除的三角形标记为 false }; // 判断点 p 是否在三角形 abc 的外接圆内 // 用行列式符号判定避免直接算圆心和半径带来的开方误差 bool inCircumcircle(const Point a, const Point b, const Point c, const Point p) { double ax a.x - p.x, ay a.y - p.y; double bx b.x - p.x, by b.y - p.y; double cx c.x - p.x, cy c.y - p.y; double det (ax * ax ay * ay) * (bx * cy - by * cx) - (bx * bx by * by) * (ax * cy - ay * cx) (cx * cx cy * cy) * (ax * by - ay * bx); // 假设 abc 为逆时针det 0 表示 p 在圆内 return det 0.0; }这段代码的关键在inCircumcircle。它没有去求外接圆圆心而是用四点构成的 3x3 行列式符号来判断。这样做的好处是全程只有乘法和减法没有除法数值稳定性更好也更快。参数上要注意det 0这个方向依赖三角形顶点是逆时针排列的。如果你在别处把顶点顺序搞反了判定会整体翻转表现为“该删的三角形不删网格越插越乱”。所以我在插入前会统一保证三角形是逆时针用有向面积校验一次。2.2 超级三角形与边界处理增量插入有个绕不开的工程问题第一个点、前几个点怎么处理。常见做法是构造一个“超级三角形”把全部输入点包进去插入过程结束后再删掉所有与超级三角形顶点相连的三角形。超级三角形要足够大否则边界点会落在它外面导致定位失败。// 根据点集包围盒构造超级三角形 // 放大系数取 20 是经验值太小会漏点太大影响精度 Triangle makeSuperTriangle(const std::vectorPoint pts) { double minx pts[0].x, maxx pts[0].x; double miny pts[0].y, maxy pts[0].y; for (const auto p : pts) { minx std::min(minx, p.x); maxx std::max(maxx, p.x); miny std::min(miny, p.y); maxy std::max(maxy, p.y); } double dx maxx - minx, dy maxy - miny; double dmax std::max(dx, dy) * 20.0; double midx (minx maxx) * 0.5, midy (miny maxy) * 0.5; // 三个顶点按逆时针给出 Point a{midx - dmax, midy - dmax}; Point b{midx dmax, midy - dmax}; Point c{midx, midy dmax}; // 这里把三个点追加进点数组返回索引构成的三角形 // 实际实现中需要维护点数组与三角形数组的同步 return Triangle{{0, 1, 2}, {-1, -1, -1}, true}; }放大系数 20 不是随便写的。太小比如 2 到 5边界附近的点可能落在超级三角形外定位阶段会找不到宿主三角形太大比如 1e6会让外接圆判定里的坐标差值巨大浮点有效位被吃掉退化判定开始误判。20 到 100 之间是比较稳的区间。另外超级三角形的三个顶点要按逆时针给和前面inCircumcircle的方向假设保持一致。2.3 定位从哪个三角形开始找定位新点所在三角形最笨的办法是遍历所有存活三角形O(n) 一次整体就退化成 O(n²)。工程上常用“行走法”从上一个插入点所在的三角形出发沿着有向边朝目标点方向走平均几步就能到。行走法依赖邻接关系nbr所以维护邻接的正确性是前提。// 行走法定位从 start 出发找到包含 p 的三角形 // 返回三角形索引若 p 落在边上返回任一相邻三角形即可 int locateTriangle(int start, const Point p, const std::vectorPoint pts, const std::vectorTriangle tris) { int cur start; for (int guard 0; guard 10000; guard) { const Triangle t tris[cur]; bool moved false; for (int i 0; i 3; i) { int a t.v[i], b t.v[(i 1) % 3]; // 有向面积p 在边 ab 的右侧则朝该邻接走 double cross (pts[b].x - pts[a].x) * (p.y - pts[a].y) - (pts[b].y - pts[a].y) * (p.x - pts[a].x); if (cross 0.0) { int next t.nbr[i]; if (next 0) return cur; // 到边界认为落在当前三角形 cur next; moved true; break; } } if (!moved) return cur; } return cur; // 兜底正常不该走到这里 }guard这个计数器是后悔药。行走法在数值退化或者邻接关系被写坏时可能死循环加一个上限至少不会把程序挂死方便你回头查邻接维护的 bug。cross 0的方向同样依赖逆时针约定。定位返回后如果点恰好落在边上不同实现会返回不同侧的三角形这会影响后续空腔的起点但不影响最终正确性只要你的空腔扩展逻辑是双向的。3. Bowyer-Watson 空腔重构删哪些、连哪些、怎么翻转3.1 空腔扩展与边界收集定位到宿主三角形后从它开始做广度优先扩展凡是外接圆包含新点的三角形全部标记删除直到遇到不包含的为止。这些被删三角形的外边界就构成空腔的多边形边界。新点与这条边界上的每条边连成新三角形。#include queue #include unordered_set // 返回空腔边界边每条边用 (端点a, 端点b) 表示方向沿空腔逆时针 std::vectorstd::pairint,int buildCavity( int seed, const Point p, const std::vectorPoint pts, std::vectorTriangle tris) { std::vectorstd::pairint,int boundary; std::queueint q; std::unordered_setint visited; q.push(seed); visited.insert(seed); while (!q.empty()) { int ti q.front(); q.pop(); Triangle t tris[ti]; if (!inCircumcircle(pts[t.v[0]], pts[t.v[1]], pts[t.v[2]], p)) { continue; // 该三角形保留不作为空腔一部分 } t.alive false; for (int i 0; i 3; i) { int nb t.nbr[i]; if (nb 0 !visited.count(nb)) { visited.insert(nb); q.push(nb); } else if (nb 0) { // 边界边记录 boundary.push_back({t.v[i], t.v[(i 1) % 3]}); } } } return boundary; }这里有个容易忽略的点visited必须在入队时就插入不能等出队。否则同一个三角形可能被多个邻居重复入队空腔一大队列就爆。另外边界边的方向要沿空腔逆时针这样新三角形(a, b, p)才是逆时针和全局约定一致。如果方向反了后面翻转判定会全乱。3.2 边翻转把非法边掰回来空腔重构后新生成的三角形不一定满足空外接圆准则需要检查空腔边界上的每条边如果它和对面三角形构成的四边形是凸的且对面三角形的对角点落在当前三角形外接圆内就翻转这条边。翻转要迭代进行直到没有非法边为止。// 尝试翻转边 (a,b)成功返回 true // 需要维护邻接关系这里只给核心判定 bool tryFlip(int a, int b, std::vectorTriangle tris, const std::vectorPoint pts) { // 找到共享边 ab 的两个三角形 t1, t2 // 取 t1 中 ab 的对顶点 ct2 中 ab 的对顶点 d // 若 c 在 t2 的外接圆内则翻转 ab 为 cd // 翻转后更新四个邻接关系 // 具体索引查找略重点在判定条件 return false; // 占位实际实现按上述逻辑 }翻转的判定条件写起来琐碎但逻辑就一句四边形凸 对角点在外接圆内。两个条件缺一不可。只判外接圆不判凸性会把凹四边形也翻产生重叠三角形。翻转的终止性由 Delaunay 能量函数单调下降保证但前提是每次翻转都合法。工程上我会给翻转循环加一个迭代上限比如边数的 10 倍超过就说明有退化点或者数值问题需要单独处理。3.3 一个能跑的最小主循环把上面几块拼起来主循环就是构造超级三角形逐个插入点定位、建空腔、连边、翻转最后清理超级三角形相关的三角形。void delaunay(std::vectorPoint pts) { // 1. 追加超级三角形顶点构造初始三角形 // 2. 对每个原始点 p // - locateTriangle 找到宿主 // - buildCavity 收集边界 // - 用 p 和边界边生成新三角形维护邻接 // - 对受影响的边做 tryFlip 迭代 // 3. 删除所有含超级三角形顶点的三角形 // 4. 压缩索引输出 }主循环本身不复杂复杂的是邻接维护。每生成一个新三角形要同时更新它自己和邻居的nbr。我习惯在生成后立刻做一次一致性校验每个三角形的nbr[i]指向的三角形必须也有一条边指回来。这个校验在调试期开着能省掉大量“网格看着对、查询全错”的玄学问题。4. 避坑与排查那些让网格悄悄错掉的细节4.1 现象网格出现重叠三角形面积和为负原因通常是顶点方向约定被破坏。inCircumcircle和locateTriangle都假设三角形逆时针一旦某处生成三角形时顶点顺序反了判定方向整体翻转空腔扩展会删错三角形翻转也会翻错边。解决方法是每次生成三角形后用有向面积cross (b-a)×(c-a)校验小于 0 就交换两个顶点。这个校验开销很小但能挡住绝大多数方向类 bug。4.2 现象插入到几千个点后速度骤降原因多半是定位退化成线性扫描。行走法在点分布不均匀时会走很多步尤其是点沿一条线分布时。解决方法是给定位加一个“历史起点”缓存每次从上一个插入点的宿主开始走而不是从头。另外可以按空间分块先粗定位到块再在块内行走。实测在十万级随机点上历史起点能把定位步数从平均几百降到个位数。4.3 现象共圆点导致翻转死循环四个点共圆时空外接圆判定det接近 0浮点误差会让翻转在两个状态间反复横跳。解决方法是给判定加一个相对容差比如det eps * scale其中scale是坐标量级的平方。容差不能太大否则会破坏 Delaunay 性质一般取 1e-12 到 1e-9 之间按你的坐标范围调。另外翻转循环必须有迭代上限超限就停止并记录不要让它无限跑。4.4 现象边界点丢失或网格不闭合原因通常是超级三角形太小边界点落在它外面定位返回了错误的宿主。解决方法是把放大系数调到 20 以上并且在插入前检查每个点是否在超级三角形内部不在就报错。另一个原因是清理超级三角形时把与之相邻的合法三角形也删了需要按顶点索引精确判断只删含超级三角形顶点的三角形。4.5 现象内存暴涨程序被 OOM 杀掉原因通常是删除的三角形没有真正释放或者邻接关系里保留了悬空索引。解决方法是把alive标记和索引压缩分开插入阶段只标记最后统一压缩。压缩时重建v和nbr把所有指向已删除三角形的邻接置为 -1。这样内存峰值可控也避免悬空索引导致的随机崩溃。5. 进阶技巧用局部重建和数值技巧把稳定性再抬一档走到这里一个能跑的 Delaunay 三角网已经有了。但如果你的点集上到百万级或者坐标范围跨越好几个数量级还有两件事值得做。第一件是局部重建代替全局翻转。每次插入点后不要对整个网格做翻转检查只对空腔边界上的边做。更进一步可以把空腔边界边按“是否可能非法”排序优先处理最可能非法的减少无效判定。实测在随机点集上局部重建能把翻转次数压到全局方案的三成左右。第二件是坐标归一化。把输入点先平移到包围盒中心再缩放到 [-1, 1] 区间做完三角剖分再映射回去。这样做的好处是inCircumcircle里的行列式量级统一容差好设退化判定稳定。归一化本身是 O(n)相对于剖分可以忽略。下面是一个归一化和反归一化的最小实现。struct Normalizer { double cx, cy, scale; void fit(const std::vectorPoint pts) { double minx pts[0].x, maxx pts[0].x; double miny pts[0].y, maxy pts[0].y; for (const auto p : pts) { minx std::min(minx, p.x); maxx std::max(maxx, p.x); miny std::min(miny, p.y); maxy std::max(maxy, p.y); } cx (minx maxx) * 0.5; cy (miny maxy) * 0.5; scale std::max(maxx - minx, maxy - miny); if (scale 0.0) scale 1.0; } Point apply(const Point p) const { return {(p.x - cx) / scale, (p.y - cy) / scale}; } Point revert(const Point p) const { return {p.x * scale cx, p.y * scale cy}; } };scale取包围盒长宽较大者保证归一化后坐标落在 [-0.5, 0.5] 附近。如果所有点重合scale会是 0这里兜底成 1避免除零。归一化之后前面提到的容差可以固定成 1e-12 左右不用再按数据调省心不少。还有一个我踩过的坑不要用float存坐标。float只有 24 位有效位坐标范围稍大一点inCircumcircle的行列式就会因为抵消误差给出错误符号表现为网格局部错乱而且极难复现。用double内存翻倍但正确性有保障。如果确实要省内存可以用定点数但那是另一个话题了。最后说一个验证习惯每次改完邻接维护或者翻转逻辑我都会用一组手工构造的退化点集跑一遍——正方形四角、正三角形加中心点、共线点。这些点集规模小出问题能肉眼看出哪个三角形错了。比拿十万随机点跑半天再回头查高效得多。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

从零部署 OpenClaw,Windows 3.1.0 详细安装步骤

从零部署 OpenClaw,Windows 3.1.0 详细安装步骤

Windows 一键部署 OpenClaw 教程|5 分钟搭建本地 AI 智能体,告别复杂环境配置 前言 2026 年开源圈爆火的「数字员工」OpenClaw(昵称小龙虾),GitHub 星标狂揽 28 万 。它区别于普通对话型 AI,是一套可以直…

2026/10/14 19:59:07 阅读更多 →
高职财税大数据应用专业:数据分析能力培养路径与实战场景

高职财税大数据应用专业:数据分析能力培养路径与实战场景

这两年,我和不少带“高职财税大数据应用”专业的老师交流,大家最常聊的话题不是买什么新实训平台,也不是考证通过率,而是同一句话:“数据分析到底怎么真正融进财税课?”前几年,这个问题还能靠一…

2026/10/14 19:58:07 阅读更多 →
Android四大组件与AMS/WMS协作机制:系统服务核心原理与避坑实践

Android四大组件与AMS/WMS协作机制:系统服务核心原理与避坑实践

做Android开发这几年,我最大的感受就是:很多疑难杂症之所以难查,不是代码写得多烂,而是你对“四大组件”和系统服务之间那套协作机制没吃透。四大组件就像一个个打工仔,干活的逻辑全写在应用进程里,可它们能…

2026/10/14 19:58:07 阅读更多 →

最新新闻

Towards Automated Regulatory Compliance Verification in Financial Auditing with Large Language Mo...

Towards Automated Regulatory Compliance Verification in Financial Auditing with Large Language Mo...

文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现财务审计中监管合规性的自动验证,旨在解决传统审计中人工验证合规性的低效问题。研究对比了开源LLMs(如Llama-2系列)与专有LLMs(如OpenAI的GPT系列)在合规性检测中的性能,使用了普华永道(PwC)提供的两个自定义…

2026/10/14 20:48:13 阅读更多 →
A Versatile Pathology Co-pilot via Reasoning Enhanced Multimodal Large Language Model

A Versatile Pathology Co-pilot via Reasoning Enhanced Multimodal Large Language Model

文章主要内容和创新点总结 主要内容 本文提出了一种名为SmartPath-R1的多模态大型语言模型(MLLM),旨在解决计算病理学中现有模型推理能力有限、任务适应性单一的问题。该模型能够同时处理区域感兴趣(ROI)级别和全切片图像(WSI)级别的任务,包括分类、检测、分割和视觉…

2026/10/14 20:48:13 阅读更多 →
The Pluralistic Moral Gap: Understanding Judgment and Value Differences between Humans and Large ...

The Pluralistic Moral Gap: Understanding Judgment and Value Differences between Humans and Large ...

文章主要内容总结 本文聚焦大型语言模型(LLMs)与人类道德判断的差异,通过构建真实世界道德困境数据集,系统分析了二者在道德判断分布和价值观表达上的“多元道德差距”,并提出了缩小该差距的方法。具体内容如下: 数据集构建:创建了包含1618个真实世界道德困境的“道德困…

2026/10/14 20:48:13 阅读更多 →
GBS光子采样加速稠密子图搜索:图论难题的量子启发式解法

GBS光子采样加速稠密子图搜索:图论难题的量子启发式解法

1. 这不是量子计算科普,而是一次图论问题的“量子启发式突围” 你可能在最近几篇预印本论文或某次学术分享中见过这个标题:“Gaussian Boson Sampling for finding dense subgraphs”。它听起来像一个典型的交叉学科缝合怪——把高能物理里的玻色采样、量…

2026/10/14 20:48:13 阅读更多 →
风电功率预测的精度密码:输入稳定性硬指标建设指南

风电功率预测的精度密码:输入稳定性硬指标建设指南

1. 数据还是模型:先搞清楚精度瓶颈到底在哪先说一个我自己的感受。入行风电功率预测这几年,身边绝大多数团队把精力都砸在模型结构上:今天换一套神经网络,明天堆一个集成学习,后天又有人说注意力机制效果好。每次模型迭…

2026/10/14 20:48:13 阅读更多 →
基于STM32的简易四轴飞行器:硬件选型、姿态解算与PID调参全攻略

基于STM32的简易四轴飞行器:硬件选型、姿态解算与PID调参全攻略

简介:基于STM32的直流电机速度环PID闭环控制资源包,面向嵌入式初学者与电子类课程设计学生,帮助理解反馈控制原理并解决直流电机调速中的闭环实现问题。压缩包共204个文件,主要由C源文件、H头文件、Keil工程配置文件(u…

2026/10/14 20:47:12 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →