PCB javascript解析Gerber274X格式實現方法


解析鑽Gerber274X格式前首先得了解此格式,這樣才能更好的解析呀。

一個Gerber274X里面包含的基本信息如下:

1.單位:公式mm,英制inch

2.省零方式:前省零,后省零

3.坐標方式:絕對坐標,相對坐標

4.坐標位數:3:5,2:6

5.Gerber D碼形狀

6.Gerber坐標信息

更多Gerber 274X格式詳見(1M帶寬,需等20秒)

http://pcbren.cn/ShareFiles/Gerber274x.pdf

一.數據結構信息

  1、數據塊(Data Blocks)

RS-274X 文件是由數據塊組成的,每個數據塊都是以(*) 結尾,每個數據塊都包括了一個或多個參數,如示例

X0Y0D02*
X10000Y0DO1*

為了增強可讀性,建議相關的數據塊在一行(因為一個數據塊可以在多行),

 2、數據類型

數據類型主要包括以下幾種類型,坐標數據(Coordinate Data),功能指令(Function Codes) ,參數(Parameters)

 3、坐標數據

坐標數據主要是定義在平面的中點數據,在RS274D的術語中稱為地址。

坐標數據可能是:

1)X和Y坐標定義的點,

2)相對於X,Y方向的便移量數據,稱為I,J數據

FS(Format Specification) 格式定義指示了數字如何被解釋的。

坐標系采用右手坐標系。

坐標是模態(modal) 的,如果一個X被忽略,則X將保留上一次的X坐標值,如果在當前層的第一個X被忽略,因為沒有上一次的X的坐標值,那么X坐標將被視為零。類似地,Y坐標也是這樣處理的。

偏移量不是模態上的,如果I或J被忽略,則缺省值為零。

注意:

GERBER的讀者有時候會錯誤地處理缺省值零。為了清晰和魯棒性,推薦總是顯式地指定第一個坐標(即便就是零,也顯式指定),這樣就不用考慮缺省的零。

示例:

1:  X100Y200* 點 (+100, +200)
2:  Y-300* 點 (+100, -300)
3:  I200J100* 平移 (+200, +100)
4:  X300Y200I150J50* 點(+300, +200) 且 平移(+150, +50)
5:  X+100I-50* 點 (+100, +200) 且 平移 (-50, 0)

 

4、功能指令(Function Codes):

功能指令描述的是如何解析相關聯的坐標數據。如,畫一條線或畫一個圓。(通常,但不是所有,這些代碼是延續已經過時的RS-274D的格式,它們被稱為字(words)或代碼(codes))。

G74*

每個指令都會影響到其后的數據塊,直到遇到另外一個相同類型的代碼或生成新層時結束。我們稱這種持續性的活動為模態(modal)。例如G02指示的是順時針圓弧插補(CCI, clockwise circular interpolation)。在遇到另外一個插補指令或生成新層之前,該指令后的所有坐標數據都被解釋為順時針圓弧插補。指令的細節描述后序再討論。

5、參數 (Parameters)

參數定義了整個圖像或單層的各種特征。它們被用於解釋其他的數據類型,(通常,這些參數被稱為Mass 參數)。控制整個圖像的參數通常會放在文件的開始處。產生新層的參數被放置在文件恰當的位置。參數由兩個字符加一個或多個緊隨其后的可選修改符組成。參數的限定符號為“%”.每個包含在數據塊內的參數必須以“*”結束。並且參數限定符必須立即跟在塊結束符后面,不允許插入空格。

例如:

%FSLAX23Y23*%

參數必須是在成對的參數限定符內,限定符內可以放一個或多個參數,兩個限定符之間最大的字符數為4096個。

例如:

%SFA1.0B1.0*ASAXBY*%

為了提高可讀性,兩個參數間允許換行,如:

%SFA1.0B1.0*

ASAXBY*%

當然,為了簡化和可讀性,推薦每行是只設置一個參數。與參數聯合的所有數值都使用顯式的小數點,如果不使用小數點,數值應當認為是整數。

參數的語法為:

%參數指令<必選修飾符>[可選修飾符]% 

語法 說明
參數指令 (parameter code) 兩個字符的指令,如AD,AM,FS等
必選修飾符(required modifiers) 必須是完整的定義
可選修飾符(optional modifiers) 依賴必選修飾符的定義

 

    此數據結構僅用於測試用JS解析Gerber格式繪出圖形, 數據結構構建與解析方式不是很完美.后續需改進。

 

 二.JS代碼實現:

function loadGerber274X(text) {
    text = text.replace(/\s+/g, ''); 
    var sections = text.split('%');
    var g = {offA: 0, offB: 0, shapes: [], cmds: [], scale: 1}, shape = 0, macros = {}, mode = 1, inverted = false, prevX = 0, prevY = 0;
    function numVal(x) {
        if(x[0] == '+')
            return numVal(x.slice(1));
        if(x[0] == '-')
            return -numVal(x.slice(1));
        if(x == '0')
            return 0;
        if(g.omitLead)
            while(x.length < g.num)
                x = '0'+x;
        else
            while(x.length < g.num)
                x += '0';
        return parseFloat(x.slice(0, g.int)+'.'+x.slice(g.int), 10);
    }
    
    for(var i = 0; i < sections.length; i++) {
        if(!sections[i].length)
            continue;
        sections[i][sections[i].length-1] == '*' && (sections[i] = sections[i].slice(0, -1));
        sections[i] = sections[i].split('*');
        for(var j = 0; j < sections[i].length; j++) {
            var d = sections[i][j];
            if(i%2) { // Parameters.
                if(d[0] == 'F' && d[1] == 'S') {// Format Specification.
                    var r = /^([LT]?)([AI])X(\d)(\d)Y(\d)(\d)$/.exec(d.slice(2)); // assert(r);
                    g.omitLead = !r[1] || r[1] == 'L';
                    g.abs = r[2] == 'A';
                    if(!g.abs) throw new Error('Need absolute values');
                    g.int = +r[3], g.dec = +r[4], g.num = g.int+g.dec;
                } else if(d[0] == 'O' && d[1] == 'F') {// Offset.
                    var r = /^(?:A([-+\d.]+)|)(?:B([-+\d.]+)|)$/.exec(d.slice(2)); // assert(r);
                    g.offA = parseInt(r[1], 10), g.offB = parseInt(r[2], 10);
                } else if(d == 'IPNEG') // Image Polarity.
                    throw new Error('Negative image polarity');
                else if(d[0] == 'L' && d[1] == 'P') { // Layer Polarity.
                    if(inverted && d[2] == 'D') // Switch to dark.
                        g.cmds.push([16<<2, inverted = false]);
                    else if(!inverted && d[2] == 'C') // Switch to clear.
                        g.cmds.push([16<<2, inverted = true]);
                } else if(d[0] == 'A' && d[1] == 'M') { // Aperture Macro.
                    var macro = [];
                    for(j++; j < sections[i].length; j++)
                        macro.push(sections[i][j]/*.split(',')*/);
                    macros[d.slice(2)] = macro;
                } else if(d[0] == 'A' && d[1] == 'D' && d[2] == 'D') { // Aperture Definition.
                    var r = /^(\d+)([^,]+)(?:,(.+)|)$/.exec(d.slice(3)); // assert(r);
                    var j = r[1]-10, args = [];
                    if(r[3])
                        args = r[3].split('X');
                    if(macros[r[2]]) {
                        function applyArgs(m) {
                            m = m.replace(/\$(\d+)/g, function(s, n) {
                                return +args[n-1] || 0;
                            }).toLowerCase(), repl = true;
                            while(repl == true)
                                repl = false, m = m.replace(/([\d.]+)x([\d.]+)/g, function(s, a, b) {return repl = true, a*b});
                            repl = true;
                            while(repl == true)
                                repl = false, m = m.replace(/([\d.]+)\/([\d.]+)/g, function(s, a, b) {return repl = true, a/b});
                            repl = true;
                            while(repl == true)
                                repl = false, m = m.replace(/([\d.]+)\+([\d.]+)/g, function(s, a, b) {return repl = true, a+b});
                            repl = true;
                            while(repl == true)
                                repl = false, m = m.replace(/([\d.]+)-([\d.]+)/g, function(s, a, b) {return repl = true, a-b});
                            return m;
                        }
                        var m1 = macros[r[2]], m2 = [];
                        for(var k = 0; k < m1.length; k++) {
                            var eq = /^\$(\d+)=(.+)$/.exec(m1[k]);
                            if(eq)
                                args[eq[1]-1] = +applyArgs(eq[2]);
                            else
                                m2.push(applyArgs(m1[k]).split(',').map(function(x) {return +x}));
                        }
                        g.shapes[j] = ['M', m2];
                        
                    } else
                        g.shapes[j] = [r[2]].concat(args.map(function(x) {return +x}));
                    if(j < shape)
                        shape = j;
                } else if(d == 'MOIN') // Specify Inches.
                    g.scale = 25.4;
                else if(d == 'MOMM') // Specify MMs.
                    g.scale = 1;
                else
                    console.log(d);
            } else { // Function codes.
                if(d[0] == 'G' && d[1] == '0' && d[2] == '4' || d[0] == 'M')
                    continue;
                if(d[0] == 'G' && d[1] == '5' && d[2] == '4')
                    d = d.slice(3);
                if(d == 'G70') { // Specify Inches.
                    g.scale = 25.4;
                    continue;
                }
                if(d == 'G74') { // Set Single quadrant mode.
                    mode &= ~4;
                    continue;
                }
                if(d == 'G75') { // Set Multi quadrant mode.
                    mode |= 4;
                    continue;
                }
                if(d == 'G36') { // Start Outline fill.
                    if(!(mode & 8))
                        g.cmds.push([8<<2, true]);
                    mode |= 8;
                    continue;
                }
                if(d == 'G37') { // End Outline fill.
                    if(mode & 8)
                        g.cmds.push([8<<2, false]);
                    mode &= ~8;
                    continue;
                }
                var cmode = 0;
                if(d[0] == 'G' && d.length > 4) {
                    var r = /^\d*/.exec(d = d.slice(1)); // assert(r);
                    mode = (mode & 12) | (cmode = parseInt(r[0], 10));
                    d = d.slice(r[0].length);
                }
                function getNum(offset) {
                    var r = /^[-+\d]*/.exec(d = d.slice(offset)); // assert(r);
                    d = d.slice(r[0].length);
                    return numVal(r[0]);
                }
                var x = prevX, y = prevY, oi = 0, oj = 0, hasX = false, hasY = false;
                if(d[0] == 'X')
                    x = getNum(1), hasX = true;
                if(d[0] == 'Y')
                    y = getNum(1), hasY = true;
                if(d[0] == 'I')
                    oi = getNum(1), (!(mode&2) && (x += oi, hasX = true));
                if(d[0] == 'J')
                    oj = getNum(1), (!(mode&2) && (y += oj, hasY = true));
                if(d[0] == 'D')  {// Draw.
                    if(d[1] == '0')
                        g.cmds.push([(mode<<2) | d[2], shape, x, y, oi, oj]);
                    else
                        shape = d.slice(1)-10;
                } else if(hasX && (x != prevX) || hasY && (y != prevY))
                    g.cmds.push([(mode<<2) | 1, shape, x, y, oi, oj]);
                else
                    console.log(d);
                prevX = x, prevY = y;
            }
        }
    }
    return g;
};

 

三.Gerber274X解析繪圖Web效果圖

      JS解析展示,無交互功能,雖然是在前端,但最佳作法解析動作放在后端,后端解析后的數據或圖像傳送到前端

 

 

部份Gerber274X解析說明引用了:

https://www.cnblogs.com/begincsdn/archive/2012/07/07/2580279.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM