PCB javascript解析Gerber274X格式实现方法

Stella981
• 阅读 891

解析钻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格式绘出图形, 数据结构构建与解析方式不是很完美.后续需改进。

PCB javascript解析Gerber274X格式实现方法

二.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解析展示,无交互功能,虽然是在前端,但最佳作法解析动作放在后端,后端解析后的数据或图像传送到前端

 PCB javascript解析Gerber274X格式实现方法

部份Gerber274X解析说明引用了:

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

点赞
收藏
评论区
推荐文章
good123 good123
3年前
PDF转WORD为什么是历史难题
PDF转Word是一个非常非常普遍的需求,可谓人人忌危,为什么如此普遍的需求,却如此难行呢,还得看为什么会有这样的一个需求:PDF文档遵循iOS32000的规范是由Adobe公司推出的文档格式,之所以应用如此广泛,是因为PDF精确定位了每个字符的坐标、根据坐标绘制的各种形状,使用PDF格式传输和打印文档可以保证格式的一致性,然后很多PDF文件是可用于阅
Stella981 Stella981
3年前
Google开源GSON的使用
\谷歌开原项目GitHub地址:https://github.com/google/gson1.新建一个maven项目,引入gson的dependency坐标依赖以及commonsio的坐标:<!处理json格式数据<dependency<groupIdcom.g
Easter79 Easter79
3年前
Three.js 地理坐标和三维空间坐标的转换
奇技指南本文作者高峰,360奇舞团前端工程师,W3C性能工作组/WOT工作组成员本文转载自奇舞周刊引言在实现3D地球时,球面是通过地理贴图渲染的。所以我们所说的地理坐标和三维空间坐标的转换,是指将地理贴图上的坐标,转换为球面坐标(https://en.wikipedia.org/wiki/Spherical\_coor
Wesley13 Wesley13
3年前
3D数学
4D向量和4x4矩阵不过是对3D运算的一种方便的记忆而已。4D齐次空间4D向量有4个分量,前3个是标准的x,y和z分量,第4个是w,有时称作齐次坐标。为了理解标准3D坐标是怎样扩展到4D坐标的,让我们先看一下2D中的齐次坐标,它的形式为(x,y,w)。想象在3D中w1处的标准2D平面,实际的2D点(x,y)用齐次坐标表示为(x,
Wesley13 Wesley13
3年前
NGUI 屏幕适配(2D UI)
博客内容基于NGUI.3.11.2,讲述NGUI2DUI。设备无关坐标以OpenGL为例,经过各种图形阶段(stage)后,几何形状最终被转换成像素显示在屏幕上。设备屏幕坐标以像素为单位,是设备相关的。不同设备分辨率不一样,即使同一设备,不同窗口的大小也不一样。在不关注具体设备的情况下绘制几何形状,则需要设备无关坐标系统。
Stella981 Stella981
3年前
JavaScript获取Input输入框的屏幕绝对位置
需求:使用JavaScript,在input输入框被点击时获取input坐标的绝对位置,原理:根据js中的元素offsetLeft、offsetTop获取相对于父元素的X、Y坐标相对位置,然后遍历到最顶层的body元素,逐步叠加距离,最终获取的位置即为input的绝对位置。代码如下://获取x坐标functiongetXPo
Stella981 Stella981
3年前
Android获得控件在屏幕中的绝对坐标
intlocationnewint2;view.getLocationInWindow(location);//获取在当前窗口内的绝对坐标view.getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标location0x坐标,location1
Wesley13 Wesley13
3年前
80、54、84坐标系七参数转换算法
一、为什么要进行坐标转换  我们所在地球是一个不规则的椭球,地表凹凸不平,地底密度不均,因此很难用一个简单模型来概括。国际上根据建模坐标系的原点不同分为参心坐标系和地心坐标系,其中参心坐标系是指参考椭球的几何中心坐标基准,地心,通常分为:参心空间直角坐标系(以x,y,z为其坐标元素)和参心大地坐标系(以B,L,H为其坐标元素)
Wesley13 Wesley13
3年前
Unity中鼠标拖动物体移动的算法
鼠标拖动物移动在游戏设计上用的非常多,像飞机射击游戏,我来分享下我的思路:  我的思路: 因为鼠标的屏幕坐标是二维的,物体是世界坐标,三维的。首先将鼠标的屏幕坐标转换为三维的世界坐标,这样为了计算鼠标与物体位置之间的距离,再将移动后的鼠标的屏幕坐标转换为世界坐标,加上之前的距离量,将和值赋给物体坐标,就可以实现了。C算法实现:IEnumera
暗箭伤人 暗箭伤人
1年前
【www.ithunter.club】 20230922下午
不容易的2023年,我们一起努力【www.ithunter.club】(2023092208:00:00.8872062023092216:00:00.887206)1.人事招聘专员数名(可选远程或入职)2.招聘向坐标东京Yahoo、Shift、L