文为作者版图系列专题之:版图中Metal专题——线宽选择,论坛里还有很多作者整理的版图相关专辑,可以登录论坛下载:http://bbs.eetop.cn/thread-595732-1-1.html
金属线是为了传输电流,因此主要需要从解决和减小它的(寄生)电阻、 (寄生)电容方面多做考虑。 (寄生)电感一般忽略,高频电路除外。这主要从两个方面分析解决:
1. 电路方面
A、如果所用金属线,主要是流过电流 (如电流镜 MOS 管的漏极连线、功率 MOS管的漏极等) 。在这种情况下金属连线的寄生电阻越小越好,此时需要金属导线尽可能的宽,以减少寄生电阻,降低导线压降IR。
B、如果所用金属线,是用于高频信号,如 clock 等,金属连线不能太宽,否则寄生电容过大,影响频率。此时信号还应加shield 信号线。
C、其他低频控制信号,如 enable 、able 等信号,这些信号通常接 MOS 管的栅极,流过的电流很小,这些金属连线宽窄(寄生电容、寄生电阻)不是很重要,不需要过多考虑。
2. 版图方面 (在考虑金属线周围环境的前提下)
A、对于走大电流的信号线,从电路方面越宽越好,但从版图方面很宽的金属线由于受到工艺、物理条件等的制约会受到限制。过宽的金属线,由于高温、应力等影响,会翘起变形甚至折断。所以很宽的金属线需要打 slot,slot 的尺寸因各个工艺厂的工艺不同而有区别。另外,由于趋肤效应,电流走金属表面和边缘,金属线太宽也不好,这样金属的线上电流分布不均匀。电流很大时应采取两种方式排布金属线:同层金属线并联(类似很宽金属线打了 slot) ;不同金属线并联,过孔要尽可能多打,节省面积。
B、不同层金属导线的连接,要尽可能打更多的通孔 via,以减少寄生电阻。过孔尺寸和个数最少的情况因电路和工艺而定。
C、越靠近 AA(有源区)的金属例如 M0,尽量不要从上面经过 MOS管、敏感电阻等器件。因为在金属线的工艺后期处理中(高温溅射、刻蚀、退火等)会影响这些器件的性能。尽可能换用更高层的金属线。另外:有些电路中专门需要用金属线做电阻的,宽度和长度需要单独考虑。
Metal 太大太宽,会导致电流密度不好,会导致电迁移、趋肤效应等现象,会降低可靠性和影响良率。
1. 我们为什么要走很宽的金属Metal 呢?
原因无非是电路要求电流的承载能力要达到很大。常见的地方如:电源线、功率开关管上连线、芯片的地线等,这种线会走的很宽。如果我们直接用很宽的Metal线,后果是,随着温度的升高,大块的金属中间会拱起来(热胀冷缩),这样会破坏绝缘层,损坏芯片。久而久之,即使运气好,芯片没有被损坏,运气不好的,这根金属很大可能会断掉,断掉的后果大家都懂的——直接断路。
2. 如何解决宽金属的问题呢?想必这是大家最为关心的事情。
大家都知道要打 slot(槽,有 slot rule,品字形,顺着电流开),slot打了之后,即便是金属断掉了,也不会全断。一种方法,是把金属重叠着走,这个当然需要足够的金属层,就是采取不同金属层的并联。另一种方法,如果是只用一层金属的话,可以直接将宽金属线拆分成多条细金属并排(最细的金属线要满足 designrule),类似于金属并联,其实也可以理解成Metal Bus(总线) 。
3. 注意金属密度问题。
还有一点,有人提到很宽的金属会造成金属密度过大,影响金属覆盖率。 金属的覆盖比例 Metal ratio:30%-55%之间为最佳(根据所用工艺而言),比例偏离的话,铝腐蚀就不好,不干净或过腐蚀。不知道大家在交 GDS 的时候有没有修改这个DRC 错误,这个会直接影响产品良率,我们都是修改OK 之后交的,所以应该注意这个问题。
金属宽度首先要满足电流条件,一般规则上都有明确说明,比如静态电流经验值大概1.5mA/um(有的是 1mA/um,根据具体工艺而言) ,但高温、大电流、台阶等情况下会有所下降,大概 1mA/um(有的是0.6-0.8mA/um,根据具体工艺而言)。这里指的是通常情况。静态电流密度的大小主要受电迁移、趋肤效应、金属材质等问题的影响。而动态电流大小对应的宽度一般规则上也会写明,通常会以能量、峰峰值、均值等来衡量,动态电流密度的大小也主要受电迁移、趋肤效应、金属材质等问题的影响。
对应不同的情况需要满足不同的约束。在满足电流的约束条件后,就需要考虑信号频率的因素了,频率越高的信号走线适当要细,因为寄生电容影响较大。如果需要流过很大电流,则需要很宽的金属,一般工艺规则都会规定最小与最大的金属宽度,最大的金属宽度是要防止电流不均匀导致电迁移、趋肤效应以及发热不均匀的问题,当然还有热胀冷缩的问题。
此外,过宽的金属会使得中间部分略有下沉导致平坦化的问题,所以需要在过宽金属上打slot。注意,工艺规定的最大线宽不会直接写出来,而是通过slot的规则隐含其中。比如规定很宽的金属在里面超过多少间距要打 slot,这里规定的间距其实就是最大的金属线宽, 另外有些工艺规则会规定不同层的金属有不同的最大宽度。
关注EETOP后台输入“百宝箱”,阅读推荐文章:
版图:
趣图:
趣图02:
职业发展01 :
何为技术型复合人才
开发工程师人生之路
数字IC工程师的技能树
好的模拟IC工程师应该具有的素养
3年以上工作经验的工程师中长期职业规划
一位老工程师的心里话
一名工作11年老IC工程师的未来之路的探讨
给去小微初创公司的同学一点建议
电子工程师路线图全剖析
职业发展02 :
我的处理器之路
模拟IC设计-我的成长经历
怎样成为强壮、健康的工程师
与年轻电子工程师谈谈最关心的前途问题
微电子(集成电路)前途怎么样?
如何学习模拟集成电路?
mn02 : 好的模拟IC工程师应该具有的素养
mn03 : 模拟IC设计领域的经典之作
mn04 : 极点零点之我见
mn05 : 六本经典模拟IC书籍精彩评论及总结
mn06 : 模拟设计的100条圣经
mn07 : 模拟电路学习入门的建议
mn08 : 模拟IC流片经验分享
mn09 : 模拟IC年薪几十万师兄的模电学习经历
mn10 : 想成为一名模拟ic设计师在本科期间应该做哪些准备?
mn11 : 模拟电路设计的九重进阶
mn12 : AnalogIC难在哪里,结构?参数?版图?系统?
icsj01 : IC设计完整流程及工具简述
IC芯片设计及生产流程
射频半导体工艺介绍
IC 芯片的成本从哪里来?
icsj02 : 说说芯片设计这点事
icsj03 : 关于IC设计的想法
icsj04 : 数字IC设计的完整流程(非常详细!)
icsj05 : 数字IC Design技术全局观(110页PPT!)
icsj06 : ASIC设计中各个阶段需要注意的问题
icsj07 : 集成电路反向分析的争议性
人生 :
无电路不人生-微电子集成电路大牛Willy Sansen自传
无数学不人生--原来数学讲的是满满的人生啊!
无通信不人生--原来人生就是一本通信原理!
无折腾不人生--一个技术牛人的电子人生
开发工程师人生之路
感悟:人生如电路!
Finfet:
五分钟看懂FinFET
华人胡正明获美国最高技术奖:发明FinFET
FinFET发明人胡正明教授的两篇原版PPT
集成电路史上最著名的10个人
封装:
2014年度中国IC封装测试产业调研报告
封装,IC 芯片的最终防护与统整
非常全面的集成电路封装示意图
非常详细的封装流程介绍
稳压器封装概述
最伟大:
世界上最伟大的十个公式
统治世界的十大算法
微波射频领域传奇人物
集成电路史上最著名的10个人
电气之王,还原真实的尼古拉·特斯拉
电学实验史话--几个著名的电学实验
六位伟大的“数学学渣”科学家
盘点计算机算法世界最伟大的十位大师
雅马哈:世界上最奇葩的公司
CDMA之母:海蒂•拉玛--史上最美的女发明家
点击阅读原文下载更多版图相关文档资料
里更正一下上一节的一个bug,之前DRC的时候出现了warnning,那个是因为我忘记给元器件添加PCB元器件了。
直接添加元器件的PCB就好了,下面就是添加的过程。
全部添加完后,就不会报warring了,这个BOOT的警告是因为Boot0需要链接到输入端,但我们连了排针,我们使用跳线帽来连接电源,因此这个警告不需要理。
我们重新DRC一波,直接导入到PCB,现在所有的元器件都导入到PCB里面了。
可以看到有些引脚是绿色的,这是因为规则设置的引脚间隔太大了。
我们去设置一下规则。
具体规则设置可以看以下博客 :
https://blog.csdn.net/Mark_md/article/details/116480633#:~:text=PCB设计规则管理器%201%20打开AD,%20设计%20-%20规则%20,打开%20PCB设计规则管理器,贴片规则%20阻焊规则%20铺铜规则%20测试点规则%20生产部分规则%20高速部分的规则%20放置器件的规则%20信号完整性分析的规则
具体的PCB绘制过程可以看以下博客:
https://blog.csdn.net/Tang_Chuanlin/article/details/79803575
板框的摆放可以看以下博客:
https://blog.csdn.net/Mark_md/article/details/116445961
板框变圆角:
https://blog.csdn.net/Teamo1110/article/details/108400204
在画PCB图时,在“窗口”下点击“垂直分割”,将原理图和PCB图分别放到AD界面左侧和右侧。然后点击一下左侧的原理图窗口,在“工具”菜单下打开“交叉选择模式”,这样选中左侧原理图中的一部分元器件右侧的PCB窗口也选中了对应的元器件,这样就比较方便一部分一部分地进行PCB布局。
https://blog.csdn.net/ly5120146160/article/details/105604737
https://blog.csdn.net/qq_22600163/article/details/80741443
设置线宽规则(快速设置线宽):
https://blog.csdn.net/lwb450921/article/details/123108292
分类线宽设置:
https://blog.csdn.net/qq_56030168/article/details/121559219
添加泪滴:
https://blog.csdn.net/weixin_41623723/article/details/103172553
覆铜:
https://blog.csdn.net/ldcung/article/details/77388291
最后就画好了,我这里还没有添加引脚说明的丝印。
3D图
更多干货请关注特辣番茄炒鸡蛋公众号
小贴士:
AD的单位改变(mm<->mil)
https://jingyan.baidu.com/article/b7001fe17b75924f7282dd9f.html
三种测量方式:
https://blog.csdn.net/cgy8919/article/details/96473452
板子变灰色了,怎么搞?原来是进入了过滤器模式
https://blog.csdn.net/weixin_46180926/article/details/104133222
在mxcad中绘制矩形,本质上还是绘制多段线,那如何用mxcad中的多段线去绘制一个支持倒角和圆角的矩形呢,在autocad中绘制一个矩形会通过一些命令或者输入关键字来确定是否需要倒角圆角或者通过面积, 宽高去绘制。下面我们将模仿autocad的绘制矩形的交互绘制, 完整的实现一个动态交互式的绘制一个矩形出来。
在线CAD功能测试:在线CAD梦想画图,效果如下:
对于命令交互, 我们用尽量简洁的方式实现, 代码如下:
import { MxFun } from "mxdraw"
const input = document.createElement("input")
const tip = document.createElement("div")
const dis = document.createElement("div")
document.body.appendChild(tip)
document.body.appendChild(input)
document.body.appendChild(dis)
// 命令交互
input.addEventListener("keydown", (e: KeyboardEvent) => {
// 讲输入框的值和按键信息传递给mxdraw中进行处理
MxFun.setCommandLineInputData((e.target as HTMLInputElement).value, e.keyCode);
// 回车清空输入
if(e.keyCode === 13) (e.target as HTMLInputElement).value = ""
})
// 接收提示信息和命令信息
MxFun.listenForCommandLineInput(({ msCmdTip, msCmdDisplay, msCmdText }) => {
tip.innerText = msCmdTip + msCmdText
dis.innerText = msCmdDisplay
}
);
首先矩形一般由两个对角点来绘制出完整的矩形, 所以,我们第一步自然是获取对角点。
通过mxcad提供的获取用户输入的一些类:MxCADUiPrPoint(Class: MxCADUiPrPoint | mxcadGitHubGitHub)获取点、MxCADUiPrDist(Class: MxCADUiPrDist | mxcadGitHubGitHub)获取距离、MxCADUiPrInt(Class: MxCADUiPrInt | mxcad)获取数字、MxCADUiPrKeyWord(Class: MxCADUiPrKeyWord | mxcadGitHubGitHub)获取关键词 来交互式的绘制矩形
我们可以用MxCADUiPrPoint获取到用户点击的对角点, 以及其他的几个类获取到用户的不同输入, 比如距离、数字、关键词等等。
根据这些用户输入, 我们来一个动态可交互的确认一个矩形如何绘制
绘制矩形主要分为以下几个步骤:
1.先获取第一个对角点
2.然后看看用户是否输入了关键词, 根据关键词获取对应的参数, 比如获取倒角距离,圆角半径等等 然后重新回到第一步重新获取角点
3.在有了第一个角度后,进行动态绘制矩形
4.获取第二个对角点, 生成矩形并绘制
其中一些关键词可能导致不同的绘制方式, 每个关键词对应不同处理。
首先获取对角点的代码比较简单,代码如下:
import { MxCADUiPrPoint } from "mxcad"
const getPoint = new MxCADUiPrPoint();
const pt1 = await getPoint.go()
console.log("对角点", pt1)
然后关键词就算有一些简单必要的格式: 首先如果不需要给用户任何提示 可以直接写关键词例如:A B用空格分隔每个关键词 如果需要对应的说明提示则需要加[]然后里面的内容格式则是提示(关键词),最后用/分割每个关键词 例如:[倒角(C)/圆角(F)/宽度(W)]
getPoint.setKeyWords("[倒角(C)/圆角(F)/宽度(W)]")
// 这里是点击, 但是它也可能没有点击,而是输入了关键词,这时返回的是null
await getPoint.go()
// 这里可以直接判断是否输入了某个关键词
if(getPoint.isKeyWordPicked("C"))
然后我们对角点,倒角距离,圆半径这些参数来确定矩形的坐标点了。 首先最普通的矩形坐标点,我们通过两个对角点生成:
import { McGePoint3d } from "mxcad"
const getRectPoints = (pt1: McGePoint3d, pt3: McGePoint3d): McGePoint3d[] => {
const pt2 = new McGePoint3d(pt1.x, pt3.y, pt1.z);
const pt4 = new McGePoint3d(pt3.x, pt1.y, pt3.z);
return [pt1, pt2, pt3, pt4];
};
有了四个点,这个时候我们要考虑如果要对矩形进行倒角,我们就需要8个坐标点构成 也就是根据xy轴倒角的距离去做偏移,把一个坐标生成两个偏移后坐标, 代码如下:
// 计算第二个对角点相对于第一个对角点的象限位置, 分别返回四象限
const getQuadrant = (pt1: McGePoint3d, pt3: McGePoint3d) => {
return [(pt3.x >= pt1.x && pt3.y >= pt1.y), (pt3.x < pt1.x && pt3.y >= pt1.y), (pt3.x < pt1.x && pt3.y < pt1.y), (pt3.x >= pt1.x && pt3.y < pt1.y)] as [boolean, boolean, boolean, boolean]
}
// 根据矩形的坐标点和两个倒角距离生成8个坐标点的多边形
function calculateRoundedRectangleVertices(points: McGePoint3d[], chamferDistance1: number, chamferDistance2: number) {
// 首先如果倒角距离为0, 则直接返回矩形坐标点
if (chamferDistance1 === 0 && chamferDistance2 === 0) return points
const [pt1, pt2, pt3, pt4] = points
// 然后计算矩形宽高, 与倒角距离进行比较,如果不能对矩形倒角就返回矩形坐标点
const width = pt1.distanceTo(pt4)
const height = pt1.distanceTo(pt2)
if ((width - Math.abs(chamferDistanceX) * 2) <= 0) return points
if ((height - Math.abs(chamferDistanceY) * 2) <= 0) return points
// 为了确保矩形偏移生成的倒角点是正确的, 需要根据不都的象限做一些偏移取反处理
const [_, isPt3InQuadrant2, isPt3InQuadrant3, isPt3InQuadrant4] = getQuadrant(pt1, pt3)
const chamferDistanceX = isPt3InQuadrant2 || isPt3InQuadrant3 ? -chamferDistance1 : chamferDistance1;
const chamferDistanceY = isPt3InQuadrant3 || isPt3InQuadrant4 ? -chamferDistance2 : chamferDistance2;
// 计算出正确的xy倒角偏移距离,就开始对矩形的四个点在x或者y上进行偏移
const chamferedPt1 = new McGePoint3d(pt1.x + chamferDistanceX, pt1.y, pt1.z);
const chamferedPt2 = new McGePoint3d(pt1.x, pt1.y + chamferDistanceY, pt1.z);
const chamferedPt3 = new McGePoint3d(pt2.x, pt2.y - chamferDistanceY, pt2.z);
const chamferedPt4 = new McGePoint3d(pt2.x + chamferDistanceX, pt2.y, pt2.z);
const chamferedPt5 = new McGePoint3d(pt3.x - chamferDistanceX, pt3.y, pt3.z);
const chamferedPt6 = new McGePoint3d(pt3.x, pt2.y - chamferDistanceY, pt3.z);
const chamferedPt7 = new McGePoint3d(pt4.x, pt4.y + chamferDistanceY, pt4.z);
const chamferedPt8 = new McGePoint3d(pt4.x - chamferDistanceX, pt4.y, pt4.z);
const chamferedPolygon = [
chamferedPt1,
chamferedPt2,
chamferedPt3,
chamferedPt4,
chamferedPt5,
chamferedPt6,
chamferedPt7,
chamferedPt8,
];
return chamferedPolygon;
}
然后我们就要考虑圆角了, 在上面我们已知矩形倒角后的坐标集合, 那么我们把相当于要把矩形倒角点的四个角从原来的直线变成圆弧。 在cad中多段线去绘制圆弧我们只需要计算它的凸度就可以形成圆弧了,现在已经知道矩形的倒角连成的直线,那么也就知道了圆弧的开始点和结束点。 我们根据mxcad中提供的一些运算方法计算出对应的凸度:
import { MxCADUtility } from "mxcad"
// 根据第一个点和下一个点和表示弧切线的一个向量得到弧中点位置, 最后通过MxCADUtility.calcBulge计算出凸度
function CMxDrawPolylineDragArcDraw_CalcArcBulge(firstPoint: McGePoint3d, nextPoint: McGePoint3d, vecArcTangent: McGeVector3d): number {
// 如果是同一个点,那凸度是0
if (firstPoint.isEqualTo(nextPoint))
return 0.0;
// 先得到两点之间的中点
let midPt = firstPoint.c().addvec(nextPoint.c().sub(firstPoint).mult(0.5));
// 从 firstPoint 指向 nextPoint 的矢量。然后,它绕 Z 轴旋转了90度
let vecMid = nextPoint.c().sub(firstPoint);
vecMid.rotateBy(Math.PI / 2.0, McGeVector3d.kZAxis);
// 然后中点和其延vecMid向量移动的一个新点构成的一条直线
let tmpMidLine = new McDbLine(midPt, midPt.c().addvec(vecMid));
// 然后讲vecArcTangent弧切线向量绕 Z 轴旋转了90度得到一个新向量
let vecVertical: McGeVector3d = vecArcTangent.c();
vecVertical.rotateBy(Math.PI / 2.0, McGeVector3d.kZAxis);
// 第一个点和其vecArcTangent向z轴旋转了90度的新向量构成一条直线
let tmpVerticalLine = new McDbLine(firstPoint, firstPoint.c().addvec(vecVertical));
// 然后得tmpMidLine和tmpVerticalLine的交点
let aryPoint: McGePoint3dArray = tmpMidLine.IntersectWith(tmpVerticalLine, McDb.Intersect.kExtendBoth);
if (aryPoint.isEmpty())
return 0.0;
// 根据交点,就可以知道这个弧的圆心了
let arcCenPoint = aryPoint.at(0);
// 计算出半径
let dR = arcCenPoint.distanceTo(firstPoint);
// 然会对vecMid向量进行归一化和缩放, 乘以半径 dR
vecMid.normalize();
vecMid.mult(dR);
// 最终计算出两个弧不同方向上的中点坐标, 根据两个中点与给定方向 vecArcTangent 的夹角,选择最接近的中点。
let arcMidPt1 = arcCenPoint.c().addvec(vecMid);
let arcMidPt2 = arcCenPoint.c().subvec(vecMid);
let vecArcDir1 = arcMidPt1.c().sub(firstPoint);
let vecArcDir2 = arcMidPt2.c().sub(firstPoint);
let arcMidPt = arcMidPt1;
if (vecArcDir1.angleTo1(vecArcTangent) > vecArcDir2.angleTo1(vecArcTangent)) {
arcMidPt = arcMidPt2;
}
// 最后用mxcad中提供计算凸度的方法计算出凸度
return MxCADUtility.calcBulge(firstPoint, arcMidPt, nextPoint).val;
}
那么有了凸度,我们就可以为多义线新增具有凸度的点, 两点相连就形成了一个圆弧,具体代码如下:
import { McDbPolyline } from "mxcad"
const pl = new McDbPolyline()
// bulge就算凸度值 通过给多段线添加两个点就形成了一个圆弧
pl.addVertexAt(startPoint, bulge)
pl.addVertexAt(endPoint)
通过上述关键代码的讲解, 结合如下完整绘制矩形的交互式代码阅读可以更好的理解mxcad中绘制矩形的具体实现方式 下面结合上述步骤描述实现了一个包含倒角/圆角/面积/尺寸四种不同的绘制方式,形成了根据用户的输入以不同方式绘制矩形的功能, 代码如下:
import { McDb, McDbLine, McDbPolyline, McGePoint3d, McGePoint3dArray, McGeVector3d, MxCADUiPrDist, MxCADUiPrInt, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUtility, MxCpp, createMxCad } from "mxcad"
async function drawRectang() {
const getPoint = new MxCADUiPrPoint();
// 倒角距离
let chamfer1Length = 0
let chamfer2Length = 0
// 圆角
let filletRadius = 0
// 宽度
let width = 1
// 面积
let area = 200
let rectWidth = 0
let rectLength = 0;
let rotationAngle = 0
let type: "default" | "chamfer" | "angleRounded" = "default"
while (true) {
// 获取两点间距离
const getLength = async (pt1Msg: string) => {
let getWidth = new MxCADUiPrDist();
getWidth.setMessage(pt1Msg);
let dWVal = await getWidth.go();
if (!dWVal) throw "error getLength"
return getWidth.value();
}
// 交互初始化
getPoint.setUserDraw(() => { })
getPoint.clearLastInputPoint()
// 提示用户点击第一个点
getPoint.setMessage("\n指定第一个角点");
getPoint.setKeyWords("[倒角(C)/圆角(F)/宽度(W)]")
const pt1CAD = await getPoint.go()
// 实例化一个多段线
let pl = new McDbPolyline();
// 检查命令输入框是否输入了对于的关键词
try {
if (getPoint.isKeyWordPicked("C")) {
// 获取倒角的距离
chamfer1Length = await getLength("\n指定第一个倒角距离:")
chamfer2Length = await getLength("\n指定第二个倒角距离")
// 下次绘制将变成倒角绘制模式
type = "chamfer"
// 退出本次循环 进入新一轮的绘制交互
continue;
}
if (getPoint.isKeyWordPicked("F")) {
filletRadius = await getLength("\n指定矩形的圆角半径")
type = "angleRounded"
continue;
}
if (getPoint.isKeyWordPicked("W")) {
width = await getLength("\n指定矩形的线宽")
continue;
}
} catch (e) {
break;
}
// 有了这些必要的信息, 我们就可以根据一些算法,就可以得到矩形的所有坐标点了
const getRect = (pt1: McGePoint3d, pt3: McGePoint3d) => {
// 重新实例化
pl = new McDbPolyline()
// 正常的矩形坐标
const rectPoint = getRectPoints(pt1, pt3)
let points = rectPoint
if (type === "chamfer") {
// 倒角的矩形
points = calculateRoundedRectangleVertices(rectPoint, chamfer1Length, chamfer2Length)
}
if (type === "angleRounded" && filletRadius !== 0) {
// 圆角后的矩形, 根据倒角的矩形坐标去计算
points = calculateRoundedRectangleVertices(getRectPoints(pt1, pt3), filletRadius, filletRadius)
// 四个象限
const [_, isPt3InQuadrant2, isPt3InQuadrant3, isPt3InQuadrant4] = getQuadrant(pt1, pt3)
if (points.length === 8) {
const addArc = (startPoint: McGePoint3d, endPoint: McGePoint3d, key?: McGeVector3d) => {
let vecArcTangent: McGeVector3d = new McGeVector3d(key);
const bulge = CMxDrawPolylineDragArcDraw_CalcArcBulge(startPoint, endPoint, vecArcTangent)
pl.addVertexAt(startPoint, bulge)
pl.addVertexAt(endPoint, 0)
}
const vec1 = new McGeVector3d(-1, 0)
const vec2 = new McGeVector3d(0, 1)
const vec3 = new McGeVector3d(1, 0)
const vec4 = new McGeVector3d(0, -1)
if (isPt3InQuadrant4) {
vec2.y = -1
vec3.x = 1
vec4.y = 1
}
if (isPt3InQuadrant2) {
vec1.x = 1
vec2.y = 1
vec3.x = -1
vec4.y = -1
}
if (isPt3InQuadrant3) {
vec1.x = 1
vec2.y = -1
vec3.x = -1
vec4.y = 1
}
addArc(points[0], points[1], vec1)
addArc(points[2], points[3], vec2)
addArc(points[4], points[5], vec3)
addArc(points[6], points[7], vec4)
} else {
points.forEach((pt) => {
pl.addVertexAt(pt, 0);
})
}
} else {
points.forEach((pt) => {
pl.addVertexAt(pt, 0, width, width);
})
}
pl.isClosed = true;
pl.constantWidth = width;
return pl
}
const userDrawPoint1Rect = (currentPoint: McGePoint3d, pWorldDraw) => {
if (!pt1CAD) return
const pt1 = pt1CAD
const pt3 = currentPoint
pl = getRect(pt1, pt3)
pWorldDraw.drawMcDbEntity(pl)
}
getPoint.setUserDraw(userDrawPoint1Rect)
const run = async () => {
getPoint.setMessage("\n指定另一个角点");
getPoint.setKeyWords("[面积(A)/尺寸(D)]")
let pt2CAD = await getPoint.go()
const userDrawPoint2Rect = (currentPoint: McGePoint3d, pWorldDraw) => {
if (!pt1CAD) return
const [isPt3InQuadrant1, isPt3InQuadrant2, isPt3InQuadrant3, isPt3InQuadrant4] = getQuadrant(pt1CAD, currentPoint)
if (isPt3InQuadrant1) {
pt2CAD = new McGePoint3d(pt1CAD.x + rectWidth, pt1CAD.y + rectLength)
}
if (isPt3InQuadrant2) {
pt2CAD = new McGePoint3d(pt1CAD.x - rectWidth, pt1CAD.y + rectLength)
}
if (isPt3InQuadrant3) {
pt2CAD = new McGePoint3d(pt1CAD.x - rectWidth, pt1CAD.y - rectLength)
}
if (isPt3InQuadrant4) {
pt2CAD = new McGePoint3d(pt1CAD.x + rectWidth, pt1CAD.y - rectLength)
}
if (!pt2CAD) return
pl = getRect(pt1CAD, pt2CAD)
pWorldDraw.drawMcDbEntity(pl)
}
if (getPoint.isKeyWordPicked("A")) {
if (!pt1CAD) return
getPoint.setUserDraw(() => { })
const getInt = new MxCADUiPrInt()
getInt.setMessage("输入当前单位计算的矩形面积<" + area + ">")
const _area = await getInt.go()
if (!_area) return
area = _area
const getKey = new MxCADUiPrKeyWord()
getKey.setMessage("计算矩形标注时的依据")
getKey.setKeyWords("[长度(L)/宽度(W)]")
const key = await getKey.go()
if (key === null) return
if (key === "w") {
rectWidth = await getLength("输入矩形宽度")
rectLength = area / rectWidth
}
else {
rectLength = await getLength("输入矩形长度")
rectWidth = area / rectLength
}
pt2CAD = new McGePoint3d(pt1CAD.x + rectWidth, pt1CAD.y + rectLength)
pl = getRect(pt1CAD, pt2CAD)
MxCpp.getCurrentMxCAD().drawEntity(pl)
}
else if (getPoint.isKeyWordPicked("D")) {
try {
rectWidth = await getLength("指定矩形宽度")
rectLength = await getLength("指定矩形宽度")
} catch (e) {
return
}
getPoint.clearLastInputPoint()
getPoint.setUserDraw(userDrawPoint2Rect)
const is = await run()
if (typeof is === "undefined") return
}
else if (pt2CAD) {
getPoint.drawReserve()
}
return true
}
const is = await run()
if (typeof is === "undefined") {
break;
}
}
}
https://gitee.com/mxcadx/mxdraw-article/tree/master/使用mxcad绘制矩形/demo.zip
*请认真填写需求信息,我们会在24小时内与您取得联系。