Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲精品国产成人,午夜小视频免费,精品久久久影院

          整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          版圖中Metal專(zhuān)題-線寬選擇

          文為作者版圖系列專(zhuān)題之:版圖中Metal專(zhuān)題——線寬選擇,論壇里還有很多作者整理的版圖相關(guān)專(zhuān)輯,可以登錄論壇下載:http://bbs.eetop.cn/thread-595732-1-1.html

          金屬線是為了傳輸電流,因此主要需要從解決和減小它的(寄生)電阻、 (寄生)電容方面多做考慮。 (寄生)電感一般忽略,高頻電路除外。這主要從兩個(gè)方面分析解決:

          1. 電路方面

          A、如果所用金屬線,主要是流過(guò)電流 (如電流鏡 MOS 管的漏極連線、功率 MOS管的漏極等) 。在這種情況下金屬連線的寄生電阻越小越好,此時(shí)需要金屬導(dǎo)線盡可能的寬,以減少寄生電阻,降低導(dǎo)線壓降IR。

          B、如果所用金屬線,是用于高頻信號(hào),如 clock 等,金屬連線不能太寬,否則寄生電容過(guò)大,影響頻率。此時(shí)信號(hào)還應(yīng)加shield 信號(hào)線。

          C、其他低頻控制信號(hào),如 enable 、able 等信號(hào),這些信號(hào)通常接 MOS 管的柵極,流過(guò)的電流很小,這些金屬連線寬窄(寄生電容、寄生電阻)不是很重要,不需要過(guò)多考慮。

          2. 版圖方面 (在考慮金屬線周?chē)h(huán)境的前提下)

          A、對(duì)于走大電流的信號(hào)線,從電路方面越寬越好,但從版圖方面很寬的金屬線由于受到工藝、物理?xiàng)l件等的制約會(huì)受到限制。過(guò)寬的金屬線,由于高溫、應(yīng)力等影響,會(huì)翹起變形甚至折斷。所以很寬的金屬線需要打 slot,slot 的尺寸因各個(gè)工藝廠的工藝不同而有區(qū)別。另外,由于趨膚效應(yīng),電流走金屬表面和邊緣,金屬線太寬也不好,這樣金屬的線上電流分布不均勻。電流很大時(shí)應(yīng)采取兩種方式排布金屬線:同層金屬線并聯(lián)(類(lèi)似很寬金屬線打了 slot) ;不同金屬線并聯(lián),過(guò)孔要盡可能多打,節(jié)省面積。

          B、不同層金屬導(dǎo)線的連接,要盡可能打更多的通孔 via,以減少寄生電阻。過(guò)孔尺寸和個(gè)數(shù)最少的情況因電路和工藝而定。

          C、越靠近 AA(有源區(qū))的金屬例如 M0,盡量不要從上面經(jīng)過(guò) MOS管、敏感電阻等器件。因?yàn)樵诮饘倬€的工藝后期處理中(高溫濺射、刻蝕、退火等)會(huì)影響這些器件的性能。盡可能換用更高層的金屬線。另外:有些電路中專(zhuān)門(mén)需要用金屬線做電阻的,寬度和長(zhǎng)度需要單獨(dú)考慮。

          Metal 太大太寬,會(huì)導(dǎo)致電流密度不好,會(huì)導(dǎo)致電遷移、趨膚效應(yīng)等現(xiàn)象,會(huì)降低可靠性和影響良率。

          1. 我們?yōu)槭裁匆吆軐挼慕饘費(fèi)etal 呢?

          原因無(wú)非是電路要求電流的承載能力要達(dá)到很大。常見(jiàn)的地方如:電源線、功率開(kāi)關(guān)管上連線、芯片的地線等,這種線會(huì)走的很寬。如果我們直接用很寬的Metal線,后果是,隨著溫度的升高,大塊的金屬中間會(huì)拱起來(lái)(熱脹冷縮),這樣會(huì)破壞絕緣層,損壞芯片。久而久之,即使運(yùn)氣好,芯片沒(méi)有被損壞,運(yùn)氣不好的,這根金屬很大可能會(huì)斷掉,斷掉的后果大家都懂的——直接斷路。

          2. 如何解決寬金屬的問(wèn)題呢?想必這是大家最為關(guān)心的事情。

          大家都知道要打 slot(槽,有 slot rule,品字形,順著電流開(kāi)),slot打了之后,即便是金屬斷掉了,也不會(huì)全斷。一種方法,是把金屬重疊著走,這個(gè)當(dāng)然需要足夠的金屬層,就是采取不同金屬層的并聯(lián)。另一種方法,如果是只用一層金屬的話,可以直接將寬金屬線拆分成多條細(xì)金屬并排(最細(xì)的金屬線要滿足 designrule),類(lèi)似于金屬并聯(lián),其實(shí)也可以理解成Metal Bus(總線) 。

          3. 注意金屬密度問(wèn)題。

          還有一點(diǎn),有人提到很寬的金屬會(huì)造成金屬密度過(guò)大,影響金屬覆蓋率。 金屬的覆蓋比例 Metal ratio:30%-55%之間為最佳(根據(jù)所用工藝而言),比例偏離的話,鋁腐蝕就不好,不干凈或過(guò)腐蝕。不知道大家在交 GDS 的時(shí)候有沒(méi)有修改這個(gè)DRC 錯(cuò)誤,這個(gè)會(huì)直接影響產(chǎn)品良率,我們都是修改OK 之后交的,所以應(yīng)該注意這個(gè)問(wèn)題。

          金屬寬度首先要滿足電流條件,一般規(guī)則上都有明確說(shuō)明,比如靜態(tài)電流經(jīng)驗(yàn)值大概1.5mA/um(有的是 1mA/um,根據(jù)具體工藝而言) ,但高溫、大電流、臺(tái)階等情況下會(huì)有所下降,大概 1mA/um(有的是0.6-0.8mA/um,根據(jù)具體工藝而言)。這里指的是通常情況。靜態(tài)電流密度的大小主要受電遷移、趨膚效應(yīng)、金屬材質(zhì)等問(wèn)題的影響。而動(dòng)態(tài)電流大小對(duì)應(yīng)的寬度一般規(guī)則上也會(huì)寫(xiě)明,通常會(huì)以能量、峰峰值、均值等來(lái)衡量,動(dòng)態(tài)電流密度的大小也主要受電遷移、趨膚效應(yīng)、金屬材質(zhì)等問(wèn)題的影響。

          對(duì)應(yīng)不同的情況需要滿足不同的約束。在滿足電流的約束條件后,就需要考慮信號(hào)頻率的因素了,頻率越高的信號(hào)走線適當(dāng)要細(xì),因?yàn)榧纳娙萦绊戄^大。如果需要流過(guò)很大電流,則需要很寬的金屬,一般工藝規(guī)則都會(huì)規(guī)定最小與最大的金屬寬度,最大的金屬寬度是要防止電流不均勻?qū)е码娺w移、趨膚效應(yīng)以及發(fā)熱不均勻的問(wèn)題,當(dāng)然還有熱脹冷縮的問(wèn)題。

          此外,過(guò)寬的金屬會(huì)使得中間部分略有下沉導(dǎo)致平坦化的問(wèn)題,所以需要在過(guò)寬金屬上打slot。注意,工藝規(guī)定的最大線寬不會(huì)直接寫(xiě)出來(lái),而是通過(guò)slot的規(guī)則隱含其中。比如規(guī)定很寬的金屬在里面超過(guò)多少間距要打 slot,這里規(guī)定的間距其實(shí)就是最大的金屬線寬, 另外有些工藝規(guī)則會(huì)規(guī)定不同層的金屬有不同的最大寬度。

          關(guān)注EETOP后臺(tái)輸入“百寶箱”,閱讀推薦文章

          版圖:

          趣圖:

          趣圖02:

          職業(yè)發(fā)展01 :

          • 何為技術(shù)型復(fù)合人才

          • 開(kāi)發(fā)工程師人生之路

          • 數(shù)字IC工程師的技能樹(shù)

          • 好的模擬IC工程師應(yīng)該具有的素養(yǎng)

          • 3年以上工作經(jīng)驗(yàn)的工程師中長(zhǎng)期職業(yè)規(guī)劃

          • 一位老工程師的心里話

          • 一名工作11年老IC工程師的未來(lái)之路的探討

          • 給去小微初創(chuàng)公司的同學(xué)一點(diǎn)建議

          • 電子工程師路線圖全剖析

          職業(yè)發(fā)展02 :

          • 我的處理器之路

          • 模擬IC設(shè)計(jì)-我的成長(zhǎng)經(jīng)歷

          • 怎樣成為強(qiáng)壯、健康的工程師

          • 與年輕電子工程師談?wù)勛铌P(guān)心的前途問(wèn)題

          • 微電子(集成電路)前途怎么樣?

          • 如何學(xué)習(xí)模擬集成電路?

          mn02 : 好的模擬IC工程師應(yīng)該具有的素養(yǎng)

          mn03 : 模擬IC設(shè)計(jì)領(lǐng)域的經(jīng)典之作

          mn04 : 極點(diǎn)零點(diǎn)之我見(jiàn)

          mn05 : 六本經(jīng)典模擬IC書(shū)籍精彩評(píng)論及總結(jié)

          mn06 : 模擬設(shè)計(jì)的100條圣經(jīng)

          mn07 : 模擬電路學(xué)習(xí)入門(mén)的建議

          mn08 : 模擬IC流片經(jīng)驗(yàn)分享

          mn09 : 模擬IC年薪幾十萬(wàn)師兄的模電學(xué)習(xí)經(jīng)歷

          mn10 : 想成為一名模擬ic設(shè)計(jì)師在本科期間應(yīng)該做哪些準(zhǔn)備?

          mn11 : 模擬電路設(shè)計(jì)的九重進(jìn)階

          mn12 : AnalogIC難在哪里,結(jié)構(gòu)?參數(shù)?版圖?系統(tǒng)?

          icsj01 : IC設(shè)計(jì)完整流程及工具簡(jiǎn)述

          IC芯片設(shè)計(jì)及生產(chǎn)流程

          射頻半導(dǎo)體工藝介紹

          IC 芯片的成本從哪里來(lái)?

          icsj02 : 說(shuō)說(shuō)芯片設(shè)計(jì)這點(diǎn)事

          icsj03 : 關(guān)于IC設(shè)計(jì)的想法

          icsj04 : 數(shù)字IC設(shè)計(jì)的完整流程(非常詳細(xì)!)

          icsj05 : 數(shù)字IC Design技術(shù)全局觀(110頁(yè)P(yáng)PT!)

          icsj06 : ASIC設(shè)計(jì)中各個(gè)階段需要注意的問(wèn)題

          icsj07 : 集成電路反向分析的爭(zhēng)議性

          人生

          • 無(wú)電路不人生-微電子集成電路大牛Willy Sansen自傳

          • 無(wú)數(shù)學(xué)不人生--原來(lái)數(shù)學(xué)講的是滿滿的人生啊!

          • 無(wú)通信不人生--原來(lái)人生就是一本通信原理!

          • 無(wú)折騰不人生--一個(gè)技術(shù)牛人的電子人生

          • 開(kāi)發(fā)工程師人生之路

          • 感悟:人生如電路!


          Finfet

          • 五分鐘看懂FinFET

          • 華人胡正明獲美國(guó)最高技術(shù)獎(jiǎng):發(fā)明FinFET

          • FinFET發(fā)明人胡正明教授的兩篇原版PPT

          • 集成電路史上最著名的10個(gè)人

          封裝

          • 2014年度中國(guó)IC封裝測(cè)試產(chǎn)業(yè)調(diào)研報(bào)告

          • 封裝,IC 芯片的最終防護(hù)與統(tǒng)整

          • 非常全面的集成電路封裝示意圖

          • 非常詳細(xì)的封裝流程介紹

          • 穩(wěn)壓器封裝概述

          最偉大

          • 世界上最偉大的十個(gè)公式

          • 統(tǒng)治世界的十大算法

          • 微波射頻領(lǐng)域傳奇人物

          • 集成電路史上最著名的10個(gè)人

          • 電氣之王,還原真實(shí)的尼古拉·特斯拉

          • 電學(xué)實(shí)驗(yàn)史話--幾個(gè)著名的電學(xué)實(shí)驗(yàn)

          • 六位偉大的“數(shù)學(xué)學(xué)渣”科學(xué)家

          • 盤(pán)點(diǎn)計(jì)算機(jī)算法世界最偉大的十位大師

          • 雅馬哈:世界上最奇葩的公司

          • CDMA之母:海蒂?拉瑪--史上最美的女發(fā)明家

          點(diǎn)擊閱讀原文下載更多版圖相關(guān)文檔資料

          里更正一下上一節(jié)的一個(gè)bug,之前DRC的時(shí)候出現(xiàn)了warnning,那個(gè)是因?yàn)槲彝浗o元器件添加PCB元器件了。

          直接添加元器件的PCB就好了,下面就是添加的過(guò)程。

          全部添加完后,就不會(huì)報(bào)warring了,這個(gè)BOOT的警告是因?yàn)锽oot0需要鏈接到輸入端,但我們連了排針,我們使用跳線帽來(lái)連接電源,因此這個(gè)警告不需要理。

          我們重新DRC一波,直接導(dǎo)入到PCB,現(xiàn)在所有的元器件都導(dǎo)入到PCB里面了。

          可以看到有些引腳是綠色的,這是因?yàn)橐?guī)則設(shè)置的引腳間隔太大了。

          我們?nèi)ピO(shè)置一下規(guī)則。

          具體規(guī)則設(shè)置可以看以下博客 :

          https://blog.csdn.net/Mark_md/article/details/116480633#:~:text=PCB設(shè)計(jì)規(guī)則管理器%201%20打開(kāi)AD,%20設(shè)計(jì)%20-%20規(guī)則%20,打開(kāi)%20PCB設(shè)計(jì)規(guī)則管理器,貼片規(guī)則%20阻焊規(guī)則%20鋪銅規(guī)則%20測(cè)試點(diǎn)規(guī)則%20生產(chǎn)部分規(guī)則%20高速部分的規(guī)則%20放置器件的規(guī)則%20信號(hào)完整性分析的規(guī)則

          具體的PCB繪制過(guò)程可以看以下博客:

          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

          在畫(huà)PCB圖時(shí),在“窗口”下點(diǎn)擊“垂直分割”,將原理圖和PCB圖分別放到AD界面左側(cè)和右側(cè)。然后點(diǎn)擊一下左側(cè)的原理圖窗口,在“工具”菜單下打開(kāi)“交叉選擇模式”,這樣選中左側(cè)原理圖中的一部分元器件右側(cè)的PCB窗口也選中了對(duì)應(yīng)的元器件,這樣就比較方便一部分一部分地進(jìn)行PCB布局。

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

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

          AD畫(huà)PCB時(shí)如何修改編輯區(qū)(黑色部分)

          https://blog.csdn.net/qq_22600163/article/details/80741443

          設(shè)置線寬規(guī)則(快速設(shè)置線寬):

          https://blog.csdn.net/lwb450921/article/details/123108292

          分類(lèi)線寬設(shè)置:

          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

          最后就畫(huà)好了,我這里還沒(méi)有添加引腳說(shuō)明的絲印。

          3D圖

          更多干貨請(qǐng)關(guān)注特辣番茄炒雞蛋公眾號(hào)



          小貼士:

          AD的單位改變(mm<->mil)

          https://jingyan.baidu.com/article/b7001fe17b75924f7282dd9f.html

          三種測(cè)量方式:

          https://blog.csdn.net/cgy8919/article/details/96473452

          板子變灰色了,怎么搞?原來(lái)是進(jìn)入了過(guò)濾器模式

          https://blog.csdn.net/weixin_46180926/article/details/104133222

          在mxcad中繪制矩形,本質(zhì)上還是繪制多段線,那如何用mxcad中的多段線去繪制一個(gè)支持倒角和圓角的矩形呢,在autocad中繪制一個(gè)矩形會(huì)通過(guò)一些命令或者輸入關(guān)鍵字來(lái)確定是否需要倒角圓角或者通過(guò)面積, 寬高去繪制。下面我們將模仿autocad的繪制矩形的交互繪制, 完整的實(shí)現(xiàn)一個(gè)動(dòng)態(tài)交互式的繪制一個(gè)矩形出來(lái)。

          在線CAD功能測(cè)試:在線CAD夢(mèng)想畫(huà)圖,效果如下:

          命令交互初始化工作

          對(duì)于命令交互, 我們用盡量簡(jiǎn)潔的方式實(shí)現(xiàn), 代碼如下:

          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中進(jìn)行處理
          
          MxFun.setCommandLineInputData((e.target as HTMLInputElement).value, e.keyCode);
          
          // 回車(chē)清空輸入
          
          if(e.keyCode === 13) (e.target as HTMLInputElement).value = ""
          
          })
          
          // 接收提示信息和命令信息
          
          MxFun.listenForCommandLineInput(({ msCmdTip, msCmdDisplay, msCmdText }) => {
          
          tip.innerText = msCmdTip + msCmdText
          
          dis.innerText = msCmdDisplay
          
          }
          
          );

          繪制矩形

          首先矩形一般由兩個(gè)對(duì)角點(diǎn)來(lái)繪制出完整的矩形, 所以,我們第一步自然是獲取對(duì)角點(diǎn)。

          通過(guò)mxcad提供的獲取用戶輸入的一些類(lèi):MxCADUiPrPoint(Class: MxCADUiPrPoint | mxcadGitHubGitHub)獲取點(diǎn)、MxCADUiPrDist(Class: MxCADUiPrDist | mxcadGitHubGitHub)獲取距離、MxCADUiPrInt(Class: MxCADUiPrInt | mxcad)獲取數(shù)字、MxCADUiPrKeyWord(Class: MxCADUiPrKeyWord | mxcadGitHubGitHub)獲取關(guān)鍵詞 來(lái)交互式的繪制矩形

          我們可以用MxCADUiPrPoint獲取到用戶點(diǎn)擊的對(duì)角點(diǎn), 以及其他的幾個(gè)類(lèi)獲取到用戶的不同輸入, 比如距離、數(shù)字、關(guān)鍵詞等等。

          根據(jù)這些用戶輸入, 我們來(lái)一個(gè)動(dòng)態(tài)可交互的確認(rèn)一個(gè)矩形如何繪制

          繪制矩形主要分為以下幾個(gè)步驟:

          1.先獲取第一個(gè)對(duì)角點(diǎn)

          2.然后看看用戶是否輸入了關(guān)鍵詞, 根據(jù)關(guān)鍵詞獲取對(duì)應(yīng)的參數(shù), 比如獲取倒角距離,圓角半徑等等 然后重新回到第一步重新獲取角點(diǎn)

          3.在有了第一個(gè)角度后,進(jìn)行動(dòng)態(tài)繪制矩形

          4.獲取第二個(gè)對(duì)角點(diǎn), 生成矩形并繪制

          其中一些關(guān)鍵詞可能導(dǎo)致不同的繪制方式, 每個(gè)關(guān)鍵詞對(duì)應(yīng)不同處理。

          首先獲取對(duì)角點(diǎn)的代碼比較簡(jiǎn)單,代碼如下:

          import { MxCADUiPrPoint } from "mxcad"
          const getPoint = new MxCADUiPrPoint();
          const pt1 = await getPoint.go()
          console.log("對(duì)角點(diǎn)", pt1)

          然后關(guān)鍵詞就算有一些簡(jiǎn)單必要的格式: 首先如果不需要給用戶任何提示 可以直接寫(xiě)關(guān)鍵詞例如:A B用空格分隔每個(gè)關(guān)鍵詞 如果需要對(duì)應(yīng)的說(shuō)明提示則需要加[]然后里面的內(nèi)容格式則是提示(關(guān)鍵詞),最后用/分割每個(gè)關(guān)鍵詞 例如:[倒角(C)/圓角(F)/寬度(W)]

          getPoint.setKeyWords("[倒角(C)/圓角(F)/寬度(W)]")
          // 這里是點(diǎn)擊, 但是它也可能沒(méi)有點(diǎn)擊,而是輸入了關(guān)鍵詞,這時(shí)返回的是null
          await getPoint.go()
          // 這里可以直接判斷是否輸入了某個(gè)關(guān)鍵詞
          if(getPoint.isKeyWordPicked("C"))

          然后我們對(duì)角點(diǎn),倒角距離,圓半徑這些參數(shù)來(lái)確定矩形的坐標(biāo)點(diǎn)了。 首先最普通的矩形坐標(biāo)點(diǎn),我們通過(guò)兩個(gè)對(duì)角點(diǎn)生成:

          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];
          };

          有了四個(gè)點(diǎn),這個(gè)時(shí)候我們要考慮如果要對(duì)矩形進(jìn)行倒角,我們就需要8個(gè)坐標(biāo)點(diǎn)構(gòu)成 也就是根據(jù)xy軸倒角的距離去做偏移,把一個(gè)坐標(biāo)生成兩個(gè)偏移后坐標(biāo), 代碼如下:

          // 計(jì)算第二個(gè)對(duì)角點(diǎn)相對(duì)于第一個(gè)對(duì)角點(diǎn)的象限位置, 分別返回四象限
          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]
          }
          // 根據(jù)矩形的坐標(biāo)點(diǎn)和兩個(gè)倒角距離生成8個(gè)坐標(biāo)點(diǎn)的多邊形
          function calculateRoundedRectangleVertices(points: McGePoint3d[], chamferDistance1: number, chamferDistance2: number) {
          // 首先如果倒角距離為0, 則直接返回矩形坐標(biāo)點(diǎn)
          if (chamferDistance1 === 0 && chamferDistance2 === 0) return points
          const [pt1, pt2, pt3, pt4] = points
          // 然后計(jì)算矩形寬高, 與倒角距離進(jìn)行比較,如果不能對(duì)矩形倒角就返回矩形坐標(biāo)點(diǎn)
          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
          // 為了確保矩形偏移生成的倒角點(diǎn)是正確的, 需要根據(jù)不都的象限做一些偏移取反處理
          const [_, isPt3InQuadrant2, isPt3InQuadrant3, isPt3InQuadrant4] = getQuadrant(pt1, pt3)
          const chamferDistanceX = isPt3InQuadrant2 || isPt3InQuadrant3 ? -chamferDistance1 : chamferDistance1;
          const chamferDistanceY = isPt3InQuadrant3 || isPt3InQuadrant4 ? -chamferDistance2 : chamferDistance2;
          // 計(jì)算出正確的xy倒角偏移距離,就開(kāi)始對(duì)矩形的四個(gè)點(diǎn)在x或者y上進(jìn)行偏移
          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;
          }

          然后我們就要考慮圓角了, 在上面我們已知矩形倒角后的坐標(biāo)集合, 那么我們把相當(dāng)于要把矩形倒角點(diǎn)的四個(gè)角從原來(lái)的直線變成圓弧。 在cad中多段線去繪制圓弧我們只需要計(jì)算它的凸度就可以形成圓弧了,現(xiàn)在已經(jīng)知道矩形的倒角連成的直線,那么也就知道了圓弧的開(kāi)始點(diǎn)和結(jié)束點(diǎn)。 我們根據(jù)mxcad中提供的一些運(yùn)算方法計(jì)算出對(duì)應(yīng)的凸度:

          import { MxCADUtility } from "mxcad"
          // 根據(jù)第一個(gè)點(diǎn)和下一個(gè)點(diǎn)和表示弧切線的一個(gè)向量得到弧中點(diǎn)位置, 最后通過(guò)MxCADUtility.calcBulge計(jì)算出凸度
          function CMxDrawPolylineDragArcDraw_CalcArcBulge(firstPoint: McGePoint3d, nextPoint: McGePoint3d, vecArcTangent: McGeVector3d): number {
          // 如果是同一個(gè)點(diǎn),那凸度是0
          if (firstPoint.isEqualTo(nextPoint))
          return 0.0;
          // 先得到兩點(diǎn)之間的中點(diǎn)
          let midPt = firstPoint.c().addvec(nextPoint.c().sub(firstPoint).mult(0.5));
          // 從 firstPoint 指向 nextPoint 的矢量。然后,它繞 Z 軸旋轉(zhuǎn)了90度
          let vecMid = nextPoint.c().sub(firstPoint);
          vecMid.rotateBy(Math.PI / 2.0, McGeVector3d.kZAxis);
          // 然后中點(diǎn)和其延vecMid向量移動(dòng)的一個(gè)新點(diǎn)構(gòu)成的一條直線
          let tmpMidLine = new McDbLine(midPt, midPt.c().addvec(vecMid));
          // 然后講vecArcTangent弧切線向量繞 Z 軸旋轉(zhuǎn)了90度得到一個(gè)新向量
          let vecVertical: McGeVector3d = vecArcTangent.c();
          vecVertical.rotateBy(Math.PI / 2.0, McGeVector3d.kZAxis);
          // 第一個(gè)點(diǎn)和其vecArcTangent向z軸旋轉(zhuǎn)了90度的新向量構(gòu)成一條直線
          let tmpVerticalLine = new McDbLine(firstPoint, firstPoint.c().addvec(vecVertical));
          // 然后得tmpMidLine和tmpVerticalLine的交點(diǎn)
          let aryPoint: McGePoint3dArray = tmpMidLine.IntersectWith(tmpVerticalLine, McDb.Intersect.kExtendBoth);
          if (aryPoint.isEmpty())
          return 0.0;
          // 根據(jù)交點(diǎn),就可以知道這個(gè)弧的圓心了
          let arcCenPoint = aryPoint.at(0);
          // 計(jì)算出半徑
          let dR = arcCenPoint.distanceTo(firstPoint);
          // 然會(huì)對(duì)vecMid向量進(jìn)行歸一化和縮放, 乘以半徑 dR
          vecMid.normalize();
          vecMid.mult(dR);
          // 最終計(jì)算出兩個(gè)弧不同方向上的中點(diǎn)坐標(biāo), 根據(jù)兩個(gè)中點(diǎn)與給定方向 vecArcTangent 的夾角,選擇最接近的中點(diǎn)。
          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中提供計(jì)算凸度的方法計(jì)算出凸度
          return MxCADUtility.calcBulge(firstPoint, arcMidPt, nextPoint).val;
          }

          那么有了凸度,我們就可以為多義線新增具有凸度的點(diǎn), 兩點(diǎn)相連就形成了一個(gè)圓弧,具體代碼如下:

          import { McDbPolyline } from "mxcad"
          const pl = new McDbPolyline()
          // bulge就算凸度值 通過(guò)給多段線添加兩個(gè)點(diǎn)就形成了一個(gè)圓弧
          pl.addVertexAt(startPoint, bulge)
          pl.addVertexAt(endPoint)

          通過(guò)上述關(guān)鍵代碼的講解, 結(jié)合如下完整繪制矩形的交互式代碼閱讀可以更好的理解mxcad中繪制矩形的具體實(shí)現(xiàn)方式 下面結(jié)合上述步驟描述實(shí)現(xiàn)了一個(gè)包含倒角/圓角/面積/尺寸四種不同的繪制方式,形成了根據(jù)用戶的輸入以不同方式繪制矩形的功能, 代碼如下:

          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) {
          // 獲取兩點(diǎn)間距離
          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()
          // 提示用戶點(diǎn)擊第一個(gè)點(diǎn)
          getPoint.setMessage("\n指定第一個(gè)角點(diǎn)");
          getPoint.setKeyWords("[倒角(C)/圓角(F)/寬度(W)]")
          const pt1CAD = await getPoint.go()
          // 實(shí)例化一個(gè)多段線
          let pl = new McDbPolyline();
          // 檢查命令輸入框是否輸入了對(duì)于的關(guān)鍵詞
          try {
          if (getPoint.isKeyWordPicked("C")) {
          // 獲取倒角的距離
          chamfer1Length = await getLength("\n指定第一個(gè)倒角距離:")
          chamfer2Length = await getLength("\n指定第二個(gè)倒角距離")
          // 下次繪制將變成倒角繪制模式
          type = "chamfer"
          // 退出本次循環(huán) 進(jìn)入新一輪的繪制交互
          continue;
          }
          if (getPoint.isKeyWordPicked("F")) {
          filletRadius = await getLength("\n指定矩形的圓角半徑")
          type = "angleRounded"
          continue;
          }
          if (getPoint.isKeyWordPicked("W")) {
          width = await getLength("\n指定矩形的線寬")
          continue;
          }
          } catch (e) {
          break;
          }
          // 有了這些必要的信息, 我們就可以根據(jù)一些算法,就可以得到矩形的所有坐標(biāo)點(diǎn)了
          const getRect = (pt1: McGePoint3d, pt3: McGePoint3d) => {
          // 重新實(shí)例化
          pl = new McDbPolyline()
          // 正常的矩形坐標(biāo)
          const rectPoint = getRectPoints(pt1, pt3)
          let points = rectPoint
          if (type === "chamfer") {
          // 倒角的矩形
          points = calculateRoundedRectangleVertices(rectPoint, chamfer1Length, chamfer2Length)
          }
          if (type === "angleRounded" && filletRadius !== 0) {
          // 圓角后的矩形, 根據(jù)倒角的矩形坐標(biāo)去計(jì)算
          points = calculateRoundedRectangleVertices(getRectPoints(pt1, pt3), filletRadius, filletRadius)
          // 四個(gè)象限
          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指定另一個(gè)角點(diǎ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("輸入當(dāng)前單位計(jì)算的矩形面積<" + area + ">")
          const _area = await getInt.go()
          if (!_area) return
          area = _area
          const getKey = new MxCADUiPrKeyWord()
          getKey.setMessage("計(jì)算矩形標(biāo)注時(shí)的依據(jù)")
          getKey.setKeyWords("[長(zhǎng)度(L)/寬度(W)]")
          const key = await getKey.go()
          if (key === null) return
          if (key === "w") {
          rectWidth = await getLength("輸入矩形寬度")
          rectLength = area / rectWidth
          }
          else {
          rectLength = await getLength("輸入矩形長(zhǎng)度")
          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无码不卡一区二区三区| 亚洲av日韩综合一区久热| 中文字幕亚洲一区二区三区| 在线免费视频一区| 亚洲一本一道一区二区三区| 少妇人妻偷人精品一区二区| 欧洲精品一区二区三区在线观看 | 午夜视频一区二区| 日本一区二区在线播放| 国产品无码一区二区三区在线蜜桃| 精品久久久久久无码中文字幕一区| 久久一区二区精品综合| 国产在线观看一区二区三区精品| 北岛玲在线一区二区| 日韩在线一区二区三区视频| 国产精品第一区揄拍| 国产精品久久一区二区三区| 精品一区二区三区在线播放视频| 国产av一区二区三区日韩| 亚洲一区二区三区自拍公司| 亚洲性日韩精品一区二区三区| 色一情一乱一伦一区二区三区日本 | 日韩精品一区二区三区影院| 国产精品视频一区国模私拍| 国产一区二区三区小说| 一区二区三区无码被窝影院| 亚洲日本va一区二区三区| 成人在线观看一区| 国产一区二区视频免费| av无码精品一区二区三区四区 | 国产福利日本一区二区三区| 蜜桃AV抽搐高潮一区二区| 精品一区二区三区AV天堂| 成人国产精品一区二区网站| 国产午夜精品一区二区三区不卡 | 国产精品久久亚洲一区二区 | 精品国产AV一区二区三区|