整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          版圖中Metal專題-線寬選擇

          文為作者版圖系列專題之:版圖中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布局。

          原理圖選中元器件之后,PCB中集合在指定區域

          https://blog.csdn.net/ly5120146160/article/details/105604737

          AD畫PCB時如何修改編輯區(黑色部分)

          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


          主站蜘蛛池模板: 欧美亚洲精品一区二区| 波多野结衣一区二区免费视频 | 亚洲一区在线视频| 中文字幕日韩精品一区二区三区| 精品女同一区二区三区在线| 亚洲一区二区三区四区视频 | 国产福利一区二区| 亚洲国产高清在线精品一区| 一区二区三区四区视频在线| 国产精品美女一区二区| 麻豆精品人妻一区二区三区蜜桃| 精品女同一区二区三区免费站| 亚洲AV无码一区东京热久久| 亚洲狠狠久久综合一区77777| 国产精品免费一区二区三区四区| 中文字幕一区在线| 亚洲日本乱码一区二区在线二产线| 一区视频在线播放| 狠狠爱无码一区二区三区| 无码人妻精品一区二区三| 日韩一区二区三区不卡视频| 精品无码一区二区三区爱欲| 国产亚洲福利精品一区二区| 亚洲一区二区三区无码国产| 精品无码日韩一区二区三区不卡| 国产精品福利区一区二区三区四区| 国产精品亚洲一区二区在线观看| 日本免费电影一区二区| 一夲道无码人妻精品一区二区| 视频精品一区二区三区| 日韩A无码AV一区二区三区 | 日韩毛片基地一区二区三区| 午夜影视日本亚洲欧洲精品一区| 国产AV国片精品一区二区| 国产情侣一区二区三区| 香蕉视频一区二区| 国产激情一区二区三区小说| 精品国产一区二区三区免费看| 日韩精品成人一区二区三区| 日本国产一区二区三区在线观看 | 嫩B人妻精品一区二区三区|