Need a push for 3D

Everything related to 3D programming
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Need a push for 3D

Post by jacdelad »

Hi #PB_All,
I want to create a program to show a PCB, which I can create from Gerber data (and additional data about what is placed on it). So basically I want to create the board "live" from rectangles/cuboids (no need for textures) without predefined models (and it shall be rotatable/zoomable after creating). I never did anything in 3D before, so I'm a bit lost.
I don't need code (at least I hope so), but can someone give me a push, which librar[y|ies] to use to create this model and show it? I'm a bit overwhelmed from the help regarding all the ogre features.

Edit: I just remembered there are 3D examples, I'll look through them. This will maybe answer my question.
Edit2: Ok, I'm completely lost. What do I need, StaticGeometry? I just want to build my PCB and then be able to move the camera around it.
Edit3: Ok, that's much more complicated than I thought. Guess I will not do this...
Edit4: Now I have this:

Code: Select all

InitEngine3D() : InitSprite()
OpenWindow(0, 0, 0, 1024, 768, "Board Viewer", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0, 0, 1024, 768)
CreateCamera(0, 0, 0, 100, 100)
CreateCube(0, 1)
TransformMesh(0,0,0,0,2,2,0.01,0,0,0)
;MeshVertexColor(
CreateEntity(0, MeshID(0), #PB_Material_None, 0, 0, -8)
Repeat
  RotateEntity(0, 0.5, 0.4, 0.6, #PB_Relative)
  RenderWorld()
  FlipBuffers()
Until WindowEvent() = #PB_Event_CloseWindow
But how do I get the board green? Is this even the right way? How do I add my components, just more and more cubes?
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Saboteur
Enthusiast
Enthusiast
Posts: 272
Joined: Fri Apr 25, 2003 7:09 pm
Location: (Madrid) Spain
Contact:

Re: Need a push for 3D

Post by Saboteur »

The easy way to colorize the board is:
· create texture
· paint texture green
· create material with texture
· assign material to entity

Code: Select all

CreateTexture(0,128,128)
StartDrawing(TextureOutput(0))
Box(0,0,128,128,RGB(0,255,0))
StopDrawing()
CreateMaterial(0,TextureID(0))

CreateEntity(0, MeshID(0), MaterialID(0), 0, 0, -8)
Anyway, there is a bug in linux when drawing colors to a texture, it are inverted BGR instead RGB.
You can add objects in code, but have to manage IDs very well because the numbering could grow out of control.
[:: PB Registered ::]

Win10 Intel core i5-3330 8GB RAM Nvidia GTX 1050Ti
marc_256
Addict
Addict
Posts: 835
Joined: Thu May 06, 2010 10:16 am
Location: Belgium
Contact:

Re: Need a push for 3D

Post by marc_256 »

Hi jacdelad,

As you can see in the picture below, this is my work with PB and OGRE.
This is a project I stopped, and didn't continued for a while now.

As you are new to 3D drawing,
and specially OGRE 3D this is not so simple ...
You want to create objects in the 3D world, scale, rotate and translate your PCB.
Then you want to do the same with your components,
And also convert Gerber data to drawing data.
PB's OGRE is a (old) 3D render engine,
So, you need a kind of game engine.

Image

- Do you want to draw the PCB copper wires ?
- How detailed do you want your PCB results ?
- Do you want to create the holes in the PCB with different diameters ?

I created all in PB 3D engine OGRE, and you can also see the shadows on the PCB ...
If you search the PB forum, I also explained somewhere how I build my 3D worlds.

I will look for the source of my PB PCB program, but it is soooooo long ago ...

Greetings,
Marc
- every professional was once an amateur - greetings from Pajottenland - Belgium -
PS: sorry for my english I speak flemish ...
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

Thanks you both. I initially thought there's some "create simple objects and done" mode when there's no need for shadows, transformation of object and such. Looks like this will be a huge project.

@marc_256: Now this is what I want to come to. The screenshot looks awesome. I'll search for your thread on that. since the Gerber data includes the layers and the the vias, I want to include them too. I plan to show the parts only as rectangles and tubes and such, because my focus is on the solder joints. I get the data where my machines inspect a solder joint.
Guess I'll start with rendering a blank board from Gerber data.

I'll keep you updated, but this will surely take some weeks before I have results.

Edit: You're right, the vias are not in the Gerber files. But one step after another...
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
SMaag
Enthusiast
Enthusiast
Posts: 301
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Need a push for 3D

Post by SMaag »

I want to create a program to show a PCB, which I can create from Gerber data (and additional data about what is placed on it). So basically I want to create the board "live" from rectangles/cuboids (no need for textures) without predefined models (and it shall be rotatable/zoomable after creating). I never did anything in 3D before, so I'm a bit lost.
I think you know what a Gerber Plot File is! So i guess you want to create the copper layers from a Gerber Plot and then place 3D objectes on it!
This is like reverse enginieering because gerber is generally a file for milling machines to create the borard.

I guess parsing the Gerber File and drawing lines and wholes will be possible. But this is not the greatest Problem.

Placing the 3D Parts on it like a CAD do, is the bigger Problem

I work on a similar project which should do the same for electrical Switcht-Boards (a simple electrical CAD)
For this i started the PureBasic Framwork Project which should provide the basic functions.

What wie urgently need is:

1. a 3D Structure Format to handle all the objects
a Brainstorming Code is in PbFw:Module_Obj3D

2. a support for 3D surface modells like STL (STL contains only triangle definitions)
a first working version is in PbFw_Moulse_sTL

2. a support for Standard 3D-Objectes like STEP or IGES
I realized that it's much easier to start with the older IGES Format.
PbfW_Module_STEP3D, PbFw_Module_IGES

3. Some libraries to place/move/rotate/zoom Objects,
We need this for handling the objects outside the 3D Engine.

4. A library for the basic structures of 3D Objects.
2D and 3D Splines, Spline based surfaces ... because the PB functions CreateCube ... do not support all the shapes we need
for 3D CAD Modells.

Tthe 4D-Vector handling libraries with SSE support are nearly finished. With this it's possible to handle the Vector and Matrix Functions
for calculations in 3D.

Take a look at my PureBasicFramework
https://github.com/Maagic7/PureBasicFrameWork/tree/main

marc_256 works on similar 3D too. As I remeber he use at the moment his own 3D-drawing which directly draw on the Screen with PB-2D drawing.
But this is done by the CPU not the GPU. To use GPU drawing we have to use the 3D-Engine and convert all objects to Meshes.

But back to your Gerber problem. Maybe the free Board Desing Software KiCad support this.
Look at a form entry here: https://www.mikrocontroller.net/topic/548821
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

Wow, that's hell of a support from you guys! I'll look into that too.

I basically just need the Gerber data of the top and bottom side, because I only need to inspect the solder joints. Until now we used SI and I already wrote a program to do m own stuff with the programs it creates. Now we will transition to vVision, and it works fundamentally different. Since it creates the programs from Gerber data I would like to include this.

Now, I have a lot of info and hope I can work with it within the next weeks! I'll definitely post the results, maybe someone is interested .
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
marc_256
Addict
Addict
Posts: 835
Joined: Thu May 06, 2010 10:16 am
Location: Belgium
Contact:

Re: Need a push for 3D

Post by marc_256 »

@jacdelad

You need this PDF gerber file updated Revision 2023.03

https://www.ucamco.com/files/downloads/ ... c43d84b05f

Marc
- every professional was once an amateur - greetings from Pajottenland - Belgium -
PS: sorry for my english I speak flemish ...
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

Now, this is the one I needed! I am currently working through the document and boy, Gerber is much more complex than I thought. So firstly, I'll put the 3D rendering aside and focus on rendering the surface of the PCB.
For now, my code can create the apertures and aperture macros and reads most of the settings from the file (still working on it). If anyone's interested and if I will finish it (that's my weakness), I can and will provide an easy to use Gerber library for vector drawing.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

Small update: I created a Gerber renderer which I will post when it's complete. Almost everything is working now.
When I'm done with it I'll be back with more stupid 3D questions.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
SMaag
Enthusiast
Enthusiast
Posts: 301
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Need a push for 3D

Post by SMaag »

I read the Gerber Specification too.
Handling Gerber Files is almost same problem as SVG Vector Grafic files.
And all this is similar to 3D Files like IGES or Step (but 3D is more complex)
Maybe we can use some of your code to solve the missing SVG support in PureBasic.
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

I am currently not completely done, but most Gerber files alread work. I'll publish all the code, when it's done, and everyone is free to contribute or use it. I don't want to publish it right now, because it's not in a state where it works with almost all files. Form my experience, people here would try it and try to fix the wrong and missing parts, which is good teamwork, but counterproductive in my eyes, because I know what is still missing. When it's going as planned, you can get the code until next Friday.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

Ok, so I decided to give anyone who's interested a sneak peek into how far I am:
The code shall be taken "as is", no support in any way, I don't need bug reports or ideas right now, not all commands are supported yet, it always shows the whole board (no zooming or moving), drop the gerber file on the canvas, some primitives are not generated yet, the gerber data is not verified yet, I'm programming on Windows (no idea whether the other OSes work), not threadsafe, didn't test with scaling...

Code: Select all

DeclareModule Gerber
  Enumeration Fill
    #Gerber_FillMode_Outline     ;Draw outlines
    #Gerber_FillMode_Fill        ;Fill polygons
  EndEnumeration
  
  Structure Pos
    X.d
    Y.d
  EndStructure
  Structure Vertex
    X.f
    Y.f
  EndStructure
  Structure Primitive
    Type.a
    Exposure.a
    Diameter.f
    CenterX.f
    CenterY.f
    Rotation.f
    Width.f
    Height.f
    StartX.f
    StartY.f
    EndX.f
    EndY.f
    OuterDiameter.f
    InnerDiameter.f
    Gap.f
    VertexCount.w
    RingThickness.f
    CrosshairThickness.f
    CrosshairLength.f
    List Vertices.Vertex()
  EndStructure
  Structure AM
    List Primitives.Primitive()
  EndStructure
  Structure Aperture
    Type.a
    ApertureMacro.s
    Diameter.d
    Vertex.a
    Rotation.d
    X.d
    Y.d
    InnerX.d
    InnerY.d
  EndStructure
  Structure Gerber
    OmittedZeros.a
    CoordinateMode.a
    SequenceNumber.a
    PreparatoryFunctionCode.a
    X.f
    Y.f
    Z.f
    DraftCode.a
    MiscCode.a
    Unit.a
    ExposureMode.a
    LeadPolarity.a
    MirrorA.a
    MirrorB.a
    ScaleFactorA.f
    ScaleFactorB.f
    Digits.a
    Map ApertureMacro.AM()
    Map Apertures.Aperture()
    List Names.s()
    List RawData.s()
    List Path.s()
    Map Attributes.s()
    Min.Pos
    Max.Pos
    Scaling.d
    BackgroundColor.l
    ForegroundColor.l
    OutlineColor.l
    FiducialColor.l
    FillMode.a
    BoardSize.Pos;in mm
    LastDuration.l
  EndStructure
  
  Declare PlotGerberToCanvas(Gadget.i,*Gerber.Gerber)
  Declare CreateGerberDataFromString(Gerber$)
  Declare CreateGerberDataFromFile(File$)
EndDeclareModule

Module Gerber
  EnableExplicit
  Enumeration RegEx
    #Gerber_RegEx_Input
    #Gerber_RegEx_Header
    #Gerber_RegEx_ExposureMode
    #Gerber_RegEx_Macro
    #Gerber_RegEx_Apertures
    #Gerber_RegEx_Names
    #Gerber_RegEx_Scaling
    #Gerber_RegEx_LeadPolarity
    #Gerber_RegEx_Mirror
    #Gerber_RegEx_ScaleFactor
    #Gerber_RegEx_Attributes
    #Gerber_RegEx_Omitter
    #Gerber_Command_Preprocess
    #Gerber_Command_PreprocessX
    #Gerber_Command_PreprocessY
    #Gerber_Command_SelectAperture
    #Gerber_Command_D01
    #Gerber_Command_D02
    #Gerber_Command_D03
    #Gerber_Command_G01
    #Gerber_Command_G02
    #Gerber_Command_G03
    #Gerber_Command_Generic
    #Gerber_Command_JustMove
  EndEnumeration
  Enumeration OmittedZeros
    #Gerber_OZ_No
    #Gerber_OZ_Trailing
    #Gerber_OZ_Leading
  EndEnumeration
  Enumeration CoordinateMode
    #Gerber_Coord_Absolute
    #Gerber_Coord_Incremental
  EndEnumeration
  Enumeration Unit
    #Gerber_Unit_Inch
    #Gerber_Unit_MM
  EndEnumeration
  Enumeration ExposureMode
    #Gerber_EM_Positiv
    #Gerber_EM_Negativ
  EndEnumeration
  ;{ MacroTypes
  #Gerber_MT_Comment       =  0
  #Gerber_MT_Circle        =  1
  #Gerber_MT_LineVector    =  2;=#Gerber_MT_VectorLine
  #Gerber_MT_VectorLine    = 20
  #Gerber_MT_CenterLine    = 21
  #Gerber_MT_LowerLeftLine = 22
  #Gerber_MT_Outline       =  4
  #Gerber_MT_Polygon       =  5
  #Gerber_MT_Moire         =  6
  #Gerber_MT_Thermal       =  7
  ;}
  Enumeration Exposure
    #Gerber_Exp_Off
    #Gerber_Exp_On
  EndEnumeration
  Enumeration ApertureType
    #Gerber_AT_Circle
    #Gerber_AT_Rectangle
    #Gerber_AT_Obround
    #Gerber_AT_Polygon
    #Gerber_AT_ApertureMacro
  EndEnumeration
  Enumeration LeadPolarity
    #Gerber_LeadPolarity_Dark
    #Gerber_LeadPolarity_Clear
  EndEnumeration
  Enumeration GMode
    #Gerber_GMode_Undefined
    #Gerber_GMode_G01
    #Gerber_GMode_G02
    #Gerber_GMode_G03
  EndEnumeration
  ;{ Create RegEx
  CreateRegularExpression(#Gerber_RegEx_Header,"^FS([L|T|D])([A|I])(\d?)(\d?)X(\d{2})Y(\d{2})(Z(\d{2}))?(D(\d+))?(M(\d+))?\*$")
  CreateRegularExpression(#Gerber_RegEx_Names,"^LN([^\*]+)\*$")
  CreateRegularExpression(#Gerber_RegEx_Scaling,"^MO(MM|IN)\*$")
  CreateRegularExpression(#Gerber_RegEx_ExposureMode,"^IP(POS|NEG)\*$")
  CreateRegularExpression(#Gerber_RegEx_Macro,"^AM([^\*]+)\*([^\%]+)$")
  CreateRegularExpression(#Gerber_RegEx_Apertures,"^ADD(\d+)([^\,\%]+)\,?(.*)\*$")
  CreateRegularExpression(#Gerber_RegEx_LeadPolarity,"^LP([CD])\*$")
  CreateRegularExpression(#Gerber_RegEx_Mirror,"^MI([AB][01])([AB][01])?\*$")
  CreateRegularExpression(#Gerber_RegEx_ScaleFactor,"^SF([AB][\d\.]+)([AB][\d\.]+)?\*$")
  CreateRegularExpression(#Gerber_RegEx_Attributes,"^TF\.(\w+)\,(.+)\*$")
  CreateRegularExpression(#Gerber_RegEx_Attributes,"^TF\.(\w+)\,(.+)\*$")
  CreateRegularExpression(#Gerber_RegEx_Omitter,"[XYIJ][-\d]+")
  CreateRegularExpression(#Gerber_Command_Preprocess,"((X)([\d\.\-]+))((Y)([\d\.\-]+))|((X)([\d\.\-]+))|((Y)([\d\.\-]+))")
  CreateRegularExpression(#Gerber_Command_PreprocessX,"X([\d\.\-]+)")
  CreateRegularExpression(#Gerber_Command_PreprocessY,"Y([\d\.\-]+)")
  CreateRegularExpression(#Gerber_Command_SelectAperture,"^((G54D|D)(\d+))$")
  CreateRegularExpression(#Gerber_Command_Generic,"^([XYIJD])([\d-]+)(([XYIJD])([\d-]+))?(([XYIJD])([\d-]+))?(([XYIJD])([\d-]+))?(([XYIJD])([\d-]+))?(([XYIJD])([\d-]+))?$")
  CreateRegularExpression(#Gerber_Command_D01,"^([XY])([-\d]+)(([XY])([-\d]+))?D01$")
  CreateRegularExpression(#Gerber_Command_D02,"^([XY])([-\d]+)(([XY])([-\d]+))?D02$")
  CreateRegularExpression(#Gerber_Command_D03,"^([XY])([-\d]+)(([XY])([-\d]+))?D03$")
  CreateRegularExpression(#Gerber_Command_G01,"^G01([XY])([-\d]+)(([XY])([-\d]+))?D(\d+)$")
  CreateRegularExpression(#Gerber_Command_G02,"^G02([XYIJ][\d-]+)([XYIJ][\d-]+)?([XYIJ][\d-]+)?([XYIJ][\d-]+)?D01$")
  CreateRegularExpression(#Gerber_Command_G03,"^G03([XYIJ][\d-]+)([XYIJ][\d-]+)?([XYIJ][\d-]+)?([XYIJ][\d-]+)?D01$")
  CreateRegularExpression(#Gerber_Command_JustMove,"^([XY])([-\d]+)(([XY])([-\d]+))?$")
  ;}
  
  Macro Movement(UsedCommand)
    OldPosition\X=Position\X
    OldPosition\Y=Position\Y
    If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
      NewPosition\X=Position\X
      NewPosition\Y=Position\Y
    Else
      NewPosition\X=0
      NewPosition\Y=0
    EndIf
    Select CountRegularExpressionGroups(UsedCommand)
      Case 2,3;X/Y
        Select RegularExpressionGroup(UsedCommand,1)
          Case "X"
            NewPosition\X=Val(RegularExpressionGroup(UsedCommand,2))
          Case "Y"
            NewPosition\Y=Val(RegularExpressionGroup(UsedCommand,2))
        EndSelect
      Case 5,6;X und Y
        Select RegularExpressionGroup(UsedCommand,1)
          Case "X"
            NewPosition\X=Val(RegularExpressionGroup(UsedCommand,2))
          Case "Y"
            NewPosition\Y=Val(RegularExpressionGroup(UsedCommand,2))
        EndSelect
        Select RegularExpressionGroup(UsedCommand,4)
          Case "X"
            NewPosition\X=Val(RegularExpressionGroup(UsedCommand,5))
          Case "Y"
            NewPosition\Y=Val(RegularExpressionGroup(UsedCommand,5))
        EndSelect
    EndSelect
    If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
      Position\X=NewPosition\X
      Position\Y=NewPosition\Y
    Else
      Position\X+NewPosition\X
      Position\Y+NewPosition\Y
    EndIf
    If SizeMode
      If Position\X>*Gerber\Max\X:*Gerber\Max\X=Position\X:EndIf
      If Position\X<*Gerber\Min\X:*Gerber\Min\X=Position\X:EndIf
      If Position\Y>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y:EndIf
      If Position\Y<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y:EndIf
    EndIf
  EndMacro
  Macro DrawGerber(MyPosition)
    If G75
      VectorSourceColor($FF000000|*Gerber\OutlineColor)
    Else
      VectorSourceColor($FF000000|*Gerber\ForegroundColor)
    EndIf
    Select *Gerber\FillMode
      Case #Gerber_FillMode_Outline
        StrokePath(1,#PB_Path_Default)
      Case #Gerber_FillMode_Fill
        If G75
          StrokePath(1,#PB_Path_Default)
        Else
          If GMode=1
            If *Gerber\Apertures(Aperture)\Type=#Gerber_AT_Circle
              StrokePath(*Gerber\Apertures(Aperture)\Diameter,#PB_Path_Default)
            Else
              StrokePath(*Gerber\Apertures(Aperture)\X,#PB_Path_Default)
            EndIf
          Else
            FillPath(#PB_Path_Default|#PB_Path_Winding)
          EndIf
        EndIf
    EndSelect
    MovePathCursor(MyPosition\X,MyPosition\Y)
  EndMacro
  
  Procedure DrawPrimitives(*AMacro.AM,*Position.Pos,SizeMode.a,*Gerber.Gerber)
    Protected Orig.Pos,Rot.d,Count.w,Len.d,NX.d,NY.d,G75.a=0,GMode.a=0,Aperture.s=""
    ForEach *AMacro\Primitives()
      MovePathCursor(*Position\X,*Position\Y,#PB_Path_Default)
      Select *AMacro\Primitives()\Type
        Case #Gerber_MT_Comment;0  
          Debug "Primitive: Comment"
        Case #Gerber_MT_Circle ;1
          If SizeMode
            Len=Pow(Pow(*AMacro\Primitives()\CenterX,2)+Pow(*AMacro\Primitives()\CenterY,2),0.5)
            Rot=*AMacro\Primitives()\Diameter/2
            NX=*Position\X+Len*Cos(Radian(*AMacro\Primitives()\Rotation))
            NY=*Position\Y+Len*Sin(Radian(*AMacro\Primitives()\Rotation))
            AddPathCircle(NX,NY,Rot,0,360,#PB_Path_Default)
            If NX-Rot<*Gerber\Min\X:*Gerber\Min\X=NX-Rot:EndIf
            If NX+Rot>*Gerber\Max\X:*Gerber\Max\X=NX+Rot:EndIf
            If NY-Rot<*Gerber\Min\Y:*Gerber\Min\Y=NY-Rot:EndIf
            If NY+Rot>*Gerber\Max\Y:*Gerber\Max\Y=NY+Rot:EndIf
          Else
            Len=Pow(Pow(*AMacro\Primitives()\CenterX,2)+Pow(*AMacro\Primitives()\CenterY,2),0.5)
            AddPathCircle(*Position\X+Len*Cos(Radian(*AMacro\Primitives()\Rotation)),*Position\Y+Len*Sin(Radian(*AMacro\Primitives()\Rotation)),*AMacro\Primitives()\Diameter/2,0,360,#PB_Path_Default)
          EndIf
        Case #Gerber_MT_LineVector,#Gerber_MT_VectorLine;2,20
          Select #Gerber_FillMode_Fill
            Case #Gerber_FillMode_Outline
              StrokePath(1,#PB_Path_Default)
            Case #Gerber_FillMode_Fill
              FillPath(#PB_Path_Winding)
          EndSelect
          If SizeMode
            Len=Pow(Pow(*AMacro\Primitives()\StartX,2)+Pow(*AMacro\Primitives()\StartY,2),0.5)
            Rot=ATan2(*AMacro\Primitives()\StartX,*AMacro\Primitives()\StartY)+Radian(*AMacro\Primitives()\Rotation)
            NX=*Position\X+Len*Cos(Rot)
            NY=*Position\Y+Len*Sin(Rot)
            MovePathCursor(NX,NY,#PB_Path_Default)
            If NX<*Gerber\Min\X:*Gerber\Min\X=NX:EndIf
            If NX>*Gerber\Max\X:*Gerber\Max\X=NX:EndIf
            If NY<*Gerber\Min\Y:*Gerber\Min\Y=NY:EndIf
            If NY>*Gerber\Max\Y:*Gerber\Max\Y=NY:EndIf
            Len=Pow(Pow(*AMacro\Primitives()\EndX,2)+Pow(*AMacro\Primitives()\EndY,2),0.5)
            Rot=ATan2(*AMacro\Primitives()\EndX,*AMacro\Primitives()\EndY)+Radian(*AMacro\Primitives()\Rotation)
            NX=*Position\X+Len*Cos(Rot)
            NY=*Position\Y+Len*Sin(Rot)
            AddPathLine(NX,NY,#PB_Path_Default)
            If NX<*Gerber\Min\X:*Gerber\Min\X=NX:EndIf
            If NX>*Gerber\Max\X:*Gerber\Max\X=NX:EndIf
            If NY<*Gerber\Min\Y:*Gerber\Min\Y=NY:EndIf
            If NY>*Gerber\Max\Y:*Gerber\Max\Y=NY:EndIf
          Else
            Len=Pow(Pow(*AMacro\Primitives()\StartX,2)+Pow(*AMacro\Primitives()\StartY,2),0.5)
            Rot=ATan2(*AMacro\Primitives()\StartX,*AMacro\Primitives()\StartY)+Radian(*AMacro\Primitives()\Rotation)
            MovePathCursor(*Position\X+Len*Cos(Rot),*Position\Y+Len*Sin(Rot),#PB_Path_Default)
            Len=Pow(Pow(*AMacro\Primitives()\EndX,2)+Pow(*AMacro\Primitives()\EndY,2),0.5)
            Rot=ATan2(*AMacro\Primitives()\EndX,*AMacro\Primitives()\EndY)+Radian(*AMacro\Primitives()\Rotation)
            AddPathLine(*Position\X+Len*Cos(Rot),*Position\Y+Len*Sin(Rot),#PB_Path_Default)
          EndIf
          StrokePath(*AMacro\Primitives()\Width)
        Case #Gerber_MT_Outline                         ;4
          Len=Pow(Pow(*AMacro\Primitives()\StartX,2)+Pow(*AMacro\Primitives()\StartY,2),0.5)
          Rot=ATan2(*AMacro\Primitives()\StartX,*AMacro\Primitives()\StartY)+Radian(*AMacro\Primitives()\Rotation)
          MovePathCursor(*Position\X+Len*Cos(Rot),*Position\Y+Len*Sin(Rot),#PB_Path_Default)
          ResetList(*AMacro\Primitives()\Vertices())
          For Count=1 To *AMacro\Primitives()\VertexCount
            NextElement(*AMacro\Primitives()\Vertices())
            Len=Pow(Pow(*AMacro\Primitives()\Vertices()\X,2)+Pow(*AMacro\Primitives()\Vertices()\Y,2),0.5)
            Rot=ATan2(*AMacro\Primitives()\Vertices()\X,*AMacro\Primitives()\Vertices()\Y)+Radian(*AMacro\Primitives()\Rotation)
            AddPathLine(*Position\X+Len*Cos(Rot),*Position\Y+Len*Sin(Rot),#PB_Path_Default)
          Next
        Case #Gerber_MT_Polygon;5
          Debug "Primitive ignored: Polygon"
        Case #Gerber_MT_Moire  ;6
          Debug "Primitive ignored: Moire"
        Case #Gerber_MT_Thermal;7
          Debug "Primitive ignored: Thermal"
        Case #Gerber_MT_CenterLine;21
          Debug "Primitive ignored: CenterLine"
        Case #Gerber_MT_LowerLeftLine;22
          Debug "Primitive ignored: LowerLeftLine"
        Default
          Debug "Unbekanntes Primitive: "+Str(*AMacro\Primitives()\Type)
      EndSelect
      If *Gerber\FillMode=#Gerber_FillMode_Fill
        DrawGerber(*Position)
      EndIf
    Next
  EndProcedure
  
  Procedure DrawAperture(*Aperture.Aperture,*Position.Pos,SizeMode.a,*Gerber.Gerber,*AMacro.AM=0)
    Protected Counter.a,Part.d,Rad.d
    MovePathCursor(*Position\X,*Position\Y,#PB_Path_Default)
    Select *Aperture\Type
      Case #Gerber_AT_Circle
        Rad=*Aperture\Diameter/2
        AddPathCircle(*Position\X,*Position\Y,Rad,0,360,#PB_Path_Default)
        If *Aperture\InnerX>0
          AddPathCircle(*Position\X,*Position\Y,Rad,0,360,#PB_Path_Default)
        EndIf
        If SizeMode
          If *Position\X-Rad<*Gerber\Min\X:*Gerber\Min\X=*Position\X-Rad:EndIf
          If *Position\X+Rad>*Gerber\Max\X:*Gerber\Max\X=*Position\X+Rad:EndIf
          If *Position\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=*Position\Y-Rad:EndIf
          If *Position\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=*Position\Y+Rad:EndIf
        EndIf
      Case #Gerber_AT_Rectangle
        AddPathBox(-*Aperture\X/2,-*Aperture\Y/2,*Aperture\X,*Aperture\Y,#PB_Path_Relative)
      Case #Gerber_AT_Obround
        If *Aperture\X>*Aperture\Y
          AddPathCircle(-(*Aperture\X-*Aperture\Y)/2,0,*Aperture\Y/2,90,270,#PB_Path_Relative)
          AddPathLine(*Aperture\X-*Aperture\Y,0,#PB_Path_Relative)
          AddPathCircle(0,*Aperture\Y/2,*Aperture\Y/2,270,90,#PB_Path_Relative)
          AddPathLine(*Aperture\Y-*Aperture\X,0,#PB_Path_Relative)
        Else
          AddPathCircle(0,-(*Aperture\Y-*Aperture\X)/2,*Aperture\X/2,180,0,#PB_Path_Relative)
          AddPathLine(0,*Aperture\Y-*Aperture\X,#PB_Path_Relative)
          AddPathCircle(-*Aperture\X/2,0,*Aperture\X/2,0,180,#PB_Path_Relative)
          AddPathLine(0,*Aperture\X-*Aperture\Y,#PB_Path_Relative)
        EndIf
        MovePathCursor(*Position\X,*Position\Y,#PB_Path_Default)
        If *Aperture\InnerX
          MovePathCursor(*Position\X,*Position\Y,#PB_Path_Default)
          AddPathCircle(*Position\X,*Position\Y,*Aperture\InnerX,0,360,#PB_Path_Default)
        EndIf
      Case #Gerber_AT_Polygon
        MovePathCursor(*Aperture\Diameter/2*Cos(Radian(-1**Aperture\Rotation)),*Aperture\Diameter/2*Sin(Radian(-1**Aperture\Rotation)),#PB_Path_Relative)
        Part=360/*Aperture\Vertex
        For Counter=0 To *Aperture\Vertex
          *Position\X+*Aperture\Diameter/2*Cos(Radian(-1**Aperture\Rotation+Part*Counter))
          *Position\Y+*Aperture\Diameter/2*Sin(Radian(-1**Aperture\Rotation+Part*Counter))
          AddPathLine(*Position\X,*Position\Y)
        Next
        If *Aperture\InnerX>0
          AddPathCircle(*Position\X,*Position\Y,*Aperture\InnerX,0,360,#PB_Path_Default)
        EndIf
      Case #Gerber_AT_ApertureMacro
        DrawPrimitives(*AMacro,*Position,SizeMode,*Gerber)
      Default
        Debug "Aperture-Fehler (Type): "+Str(*Aperture\Type)
    EndSelect
    MovePathCursor(*Position\X,*Position\Y,#PB_Path_Default)
  EndProcedure
  
  Procedure PlotGerber(*Gerber.Gerber,SizeMode.a=#False)
    Protected PlotMode.a,Counter.a,Temp$,Position.Pos,Aperture.s,NewPosition.Pos,Center.Pos,G75.a,GMode.a,Rad.d,OldPosition.Pos,G36.a
    MovePathCursor(0,0)
    Position\X=0:Position\Y=0
    NewPosition\X=0:NewPosition\Y=0
    
    ForEach *Gerber\Path()
      If MatchRegularExpression(#Gerber_Command_SelectAperture,*Gerber\Path())
        ;{ Apertur einstellen
        If ExamineRegularExpression(#Gerber_Command_SelectAperture,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_SelectAperture)
          Aperture=RegularExpressionGroup(#Gerber_Command_SelectAperture,3)
          GMode=#Gerber_GMode_Undefined
        Else
          PrintN("G54-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_D01,*Gerber\Path())
        ;{ Kommando D01, LINE
        If ExamineRegularExpression(#Gerber_Command_D01,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_D01)
          Movement(#Gerber_Command_D01)
          AddPathLine(Position\X,Position\Y,#PB_Path_Default)
          DrawGerber(Position)
          If SizeMode
            If Position\X>*Gerber\Max\X:*Gerber\Max\X=Position\X:EndIf
            If Position\X<*Gerber\Min\X:*Gerber\Min\X=Position\X:EndIf
            If Position\Y>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y:EndIf
            If Position\Y<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y:EndIf
          EndIf
        Else
          PrintN("D01-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_D02,*Gerber\Path())
        ;{ Kommando D02, MOVE
        If ExamineRegularExpression(#Gerber_Command_D02,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_D02)
          Movement(#Gerber_Command_D02)
          MovePathCursor(Position\X,Position\Y,#PB_Path_Default)
          If SizeMode
            If Position\X>*Gerber\Max\X:*Gerber\Max\X=Position\X:EndIf
            If Position\X<*Gerber\Min\X:*Gerber\Min\X=Position\X:EndIf
            If Position\Y>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y:EndIf
            If Position\Y<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y:EndIf
          EndIf
        Else
          PrintN("D02-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_D03,*Gerber\Path())
        ;{ Kommando D03, FLASH
        If ExamineRegularExpression(#Gerber_Command_D03,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_D03)
          Movement(#Gerber_Command_D03)
          MovePathCursor(Position\X,Position\Y,#PB_Path_Default)
          ;AddPathLine(Position\X,Position\Y,#PB_Path_Default)
          If *Gerber\Apertures(Aperture)\Type=#Gerber_AT_ApertureMacro
            DrawAperture(*Gerber\Apertures(Aperture),Position,SizeMode,*Gerber,*Gerber\ApertureMacro(*Gerber\Apertures(Aperture)\ApertureMacro))
          Else
            DrawAperture(*Gerber\Apertures(Aperture),Position,SizeMode,*Gerber)
          EndIf
        Else
          PrintN("D03-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_G01,*Gerber\Path())
        ;{ G01: Linear-Plot-Modus
        If ExamineRegularExpression(#Gerber_Command_G01,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_G01)
          If *Gerber\FillMode=#Gerber_FillMode_Fill
            DrawGerber(Position)
          EndIf            
          Movement(#Gerber_Command_G01)
          Select RegularExpressionGroup(#Gerber_Command_G01,CountRegularExpressionGroups(#Gerber_Command_G01))
            Case "01"
              If *Gerber\FillMode=#Gerber_FillMode_Outline
                Select *Gerber\Apertures(Aperture)\Type
                  Case #Gerber_AT_Circle
                    Rad=*Gerber\Apertures(Aperture)\Diameter/2
                    AddPathCircle(OldPosition\X,OldPosition\Y,Rad,0,360,#PB_Path_Default)
                    AddPathCircle(Position\X,Position\Y,Rad,0,360,#PB_Path_Default)
                    If SizeMode
                      If OldPosition\X-Rad<*Gerber\Min\X:*Gerber\Min\X=OldPosition\X-Rad:EndIf
                      If OldPosition\X+Rad>*Gerber\Max\X:*Gerber\Max\X=OldPosition\X+Rad:EndIf
                      If OldPosition\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=OldPosition\Y-Rad:EndIf
                      If OldPosition\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=OldPosition\Y+Rad:EndIf
                      If Position\X-Rad<*Gerber\Min\X:*Gerber\Min\X=Position\X-Rad:EndIf
                      If Position\X+Rad>*Gerber\Max\X:*Gerber\Max\X=Position\X+Rad:EndIf
                      If Position\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y-Rad:EndIf
                      If Position\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y+Rad:EndIf
                    EndIf
                  Case #Gerber_AT_Rectangle
                    AddPathBox(OldPosition\X-*Gerber\Apertures(Aperture)\X/2,OldPosition\Y-*Gerber\Apertures(Aperture)\Y/2,*Gerber\Apertures(Aperture)\X,*Gerber\Apertures(Aperture)\Y,#PB_Path_Default)
                    AddPathBox(Position\X-*Gerber\Apertures(Aperture)\X/2,Position\Y-*Gerber\Apertures(Aperture)\Y/2,*Gerber\Apertures(Aperture)\X,*Gerber\Apertures(Aperture)\Y,#PB_Path_Default)
                    If SizeMode
                      If OldPosition\X-*Gerber\Apertures(Aperture)\X/2<*Gerber\Min\X:*Gerber\Min\X=OldPosition\X-*Gerber\Apertures(Aperture)\X/2:EndIf
                      If OldPosition\X+*Gerber\Apertures(Aperture)\X/2>*Gerber\Max\X:*Gerber\Max\X=OldPosition\X+*Gerber\Apertures(Aperture)\X/2:EndIf
                      If OldPosition\Y-*Gerber\Apertures(Aperture)\Y/2<*Gerber\Min\Y:*Gerber\Min\Y=OldPosition\Y-*Gerber\Apertures(Aperture)\Y/2:EndIf
                      If OldPosition\Y+*Gerber\Apertures(Aperture)\Y/2>*Gerber\Max\Y:*Gerber\Max\Y=OldPosition\Y+*Gerber\Apertures(Aperture)\Y/2:EndIf
                      If Position\X-*Gerber\Apertures(Aperture)\X/2<*Gerber\Min\X:*Gerber\Min\X=Position\X-*Gerber\Apertures(Aperture)\X/2:EndIf
                      If Position\X+*Gerber\Apertures(Aperture)\X/2>*Gerber\Max\X:*Gerber\Max\X=Position\X+*Gerber\Apertures(Aperture)\X/2:EndIf
                      If Position\Y-*Gerber\Apertures(Aperture)\Y/2<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y-*Gerber\Apertures(Aperture)\Y/2:EndIf
                      If Position\Y+*Gerber\Apertures(Aperture)\Y/2>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y+*Gerber\Apertures(Aperture)\Y/2:EndIf
                    EndIf
                EndSelect
                MovePathCursor(OldPosition\X,OldPosition\Y,#PB_Path_Default)
                AddPathLine(Position\X,Position\Y,#PB_Path_Default)
              Else
                AddPathLine(Position\X,Position\Y,#PB_Path_Default)
                If Not G36
                  Select *Gerber\Apertures(Aperture)\Type
                    Case #Gerber_AT_Circle,#Gerber_AT_Polygon
                      StrokePath(*Gerber\Apertures(Aperture)\Diameter,#PB_Path_Default|#PB_Path_RoundEnd)
                    Case #Gerber_AT_Rectangle
                      StrokePath(*Gerber\Apertures(Aperture)\X,#PB_Path_Default|#PB_Path_SquareEnd)
                  EndSelect
                EndIf
              EndIf
            Case "02"
              MovePathCursor(Position\X,Position\Y,#PB_Path_Default)
            Default
              Debug "G01-Error: "+*Gerber\Path()
          EndSelect
          GMode=#Gerber_GMode_G01
        Else
          PrintN("G01-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_G02,*Gerber\Path())
        ;{ G02: Kreismodus (im Uhrzeigersinn)
        If ExamineRegularExpression(#Gerber_Command_G02,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_G02)
          DrawGerber(Position)
          NewPosition\X=Position\X
          NewPosition\Y=Position\Y
          Center\X=Position\X
          Center\Y=Position\Y
          For Counter=1 To CountRegularExpressionGroups(#Gerber_Command_G02)
            Temp$=RegularExpressionGroup(#Gerber_Command_G02,Counter)
            Select Left(Temp$,1)
              Case "X"
                If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
                  NewPosition\X=ValD(Right(Temp$,Len(Temp$)-1))
                Else
                  NewPosition\X=Position\X+ValD(Right(Temp$,Len(Temp$)-1))
                EndIf
              Case "Y"
                If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
                  NewPosition\Y=ValD(Right(Temp$,Len(Temp$)-1))
                Else
                  NewPosition\Y=Position\Y+ValD(Right(Temp$,Len(Temp$)-1))
                EndIf
              Case "I"
                Center\X=Position\X+ValD(Right(Temp$,Len(Temp$)-1))
              Case "J"
                Center\Y=Position\Y+ValD(Right(Temp$,Len(Temp$)-1))
            EndSelect
          Next
          Rad=Pow(Pow(Center\X-Position\X,2)+Pow(Center\Y-Position\Y,2),0.5)
          AddPathCircle(Center\X,Center\Y,Rad,Degree(ATan2(NewPosition\X-Center\X,NewPosition\Y-Center\Y)),Degree(ATan2(Position\X-Center\X,Position\Y-Center\Y)),#PB_Path_Default)
          If SizeMode
            If Center\X-Rad<*Gerber\Min\X:*Gerber\Min\X=Center\X-Rad:EndIf
            If Center\X+Rad>*Gerber\Max\X:*Gerber\Max\X=Center\X+Rad:EndIf
            If Center\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=Center\Y-Rad:EndIf
            If Center\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=Center\Y+Rad:EndIf
          EndIf
          Position\X=NewPosition\X
          Position\Y=NewPosition\Y
          GMode=#Gerber_GMode_G02
          If *Gerber\FillMode=#Gerber_FillMode_Fill
            StrokePath(*Gerber\Apertures(Aperture)\Diameter,#PB_Path_RoundEnd)
          Else
            StrokePath(1,#PB_Path_RoundEnd)
          EndIf
        Else
          PrintN("G02-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_G03,*Gerber\Path())
        ;{ G03: Kreismodus (gegen den Uhrzeigersinn)
        If ExamineRegularExpression(#Gerber_Command_G03,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_G03)
          DrawGerber(Position)
          NewPosition\X=Position\X
          NewPosition\Y=Position\Y
          Center\X=Position\X
          Center\Y=Position\Y
          For Counter=1 To CountRegularExpressionGroups(#Gerber_Command_G03)
            Temp$=RegularExpressionGroup(#Gerber_Command_G03,Counter)
            Select Left(Temp$,1)
              Case "X"
                If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
                  NewPosition\X=ValD(Right(Temp$,Len(Temp$)-1))
                Else
                  NewPosition\X=Position\X+ValD(Right(Temp$,Len(Temp$)-1))
                EndIf
              Case "Y"
                If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
                  NewPosition\Y=ValD(Right(Temp$,Len(Temp$)-1))
                Else
                  NewPosition\Y=Position\Y+ValD(Right(Temp$,Len(Temp$)-1))
                EndIf
              Case "I"
                Center\X=Position\X+ValD(Right(Temp$,Len(Temp$)-1))
              Case "J"
                Center\Y=Position\Y+ValD(Right(Temp$,Len(Temp$)-1))
            EndSelect
          Next
          Rad=Pow(Pow(Center\X-Position\X,2)+Pow(Center\Y-Position\Y,2),0.5)
          AddPathCircle(Center\X,Center\Y,Rad,Degree(ATan2(Position\X-Center\X,Position\Y-Center\Y)),Degree(ATan2(NewPosition\X-Center\X,NewPosition\Y-Center\Y)),#PB_Path_Default)
          If SizeMode
            If Center\X-Rad<*Gerber\Min\X:*Gerber\Min\X=Center\X-Rad:EndIf
            If Center\X+Rad>*Gerber\Max\X:*Gerber\Max\X=Center\X+Rad:EndIf
            If Center\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=Center\Y-Rad:EndIf
            If Center\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=Center\Y+Rad:EndIf
          EndIf
          Position\X=NewPosition\X
          Position\Y=NewPosition\Y
          GMode=#Gerber_GMode_G03
          If *Gerber\FillMode=#Gerber_FillMode_Fill
            StrokePath(*Gerber\Apertures(Aperture)\Diameter,#PB_Path_RoundEnd)
          Else
            StrokePath(1,#PB_Path_RoundEnd)
          EndIf
        Else
          PrintN("G03-Fehler: "+*Gerber\Path())
        EndIf
        ;}
      ElseIf *Gerber\Path()="G36"
        ;{ Begin Contour
        DrawGerber(Position)
        G36=#True
        ;}
      ElseIf *Gerber\Path()="G37"
        ;{ End Contour
        DrawGerber(Position)
        G36=#False
        ;}
      ElseIf MatchRegularExpression(#Gerber_Command_JustMove,*Gerber\Path())
        ;{ Nur bewegen (ggf. mit aktuellem GMode zeichnen)
        If ExamineRegularExpression(#Gerber_Command_JustMove,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_JustMove)
          Movement(#Gerber_Command_JustMove)
          If G36
            AddPathLine(Position\X,Position\Y,#PB_Path_Default)
          Else
            Select GMode
              Case #Gerber_GMode_G01
                Select *Gerber\FillMode
                  Case #Gerber_FillMode_Outline
                    Select *Gerber\Apertures(Aperture)\Type
                      Case #Gerber_AT_Circle
                        Rad=*Gerber\Apertures(Aperture)\Diameter/2
                        AddPathCircle(OldPosition\X,OldPosition\Y,Rad,0,360,#PB_Path_Default)
                        AddPathCircle(Position\X,Position\Y,Rad,0,360,#PB_Path_Default)
                        If SizeMode
                          If OldPosition\X-Rad<*Gerber\Min\X:*Gerber\Min\X=OldPosition\X-Rad:EndIf
                          If OldPosition\X+Rad>*Gerber\Max\X:*Gerber\Max\X=OldPosition\X+Rad:EndIf
                          If OldPosition\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=OldPosition\Y-Rad:EndIf
                          If OldPosition\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=OldPosition\Y+Rad:EndIf
                          If Position\X-Rad<*Gerber\Min\X:*Gerber\Min\X=Position\X-Rad:EndIf
                          If Position\X+Rad>*Gerber\Max\X:*Gerber\Max\X=Position\X+Rad:EndIf
                          If Position\Y-Rad<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y-Rad:EndIf
                          If Position\Y+Rad>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y+Rad:EndIf
                        EndIf
                      Case #Gerber_AT_Rectangle
                        AddPathBox(OldPosition\X-*Gerber\Apertures(Aperture)\X/2,OldPosition\Y-*Gerber\Apertures(Aperture)\Y/2,*Gerber\Apertures(Aperture)\X,*Gerber\Apertures(Aperture)\Y,#PB_Path_Default)
                        AddPathBox(Position\X-*Gerber\Apertures(Aperture)\X/2,Position\Y-*Gerber\Apertures(Aperture)\Y/2,*Gerber\Apertures(Aperture)\X,*Gerber\Apertures(Aperture)\Y,#PB_Path_Default)
                        If SizeMode
                          If OldPosition\X-*Gerber\Apertures(Aperture)\X/2<*Gerber\Min\X:*Gerber\Min\X=OldPosition\X-*Gerber\Apertures(Aperture)\X/2:EndIf
                          If OldPosition\X+*Gerber\Apertures(Aperture)\X/2>*Gerber\Max\X:*Gerber\Max\X=OldPosition\X+*Gerber\Apertures(Aperture)\X/2:EndIf
                          If OldPosition\Y-*Gerber\Apertures(Aperture)\Y/2<*Gerber\Min\Y:*Gerber\Min\Y=OldPosition\Y-*Gerber\Apertures(Aperture)\Y/2:EndIf
                          If OldPosition\Y+*Gerber\Apertures(Aperture)\Y/2>*Gerber\Max\Y:*Gerber\Max\Y=OldPosition\Y+*Gerber\Apertures(Aperture)\Y/2:EndIf
                          If Position\X-*Gerber\Apertures(Aperture)\X/2<*Gerber\Min\X:*Gerber\Min\X=Position\X-*Gerber\Apertures(Aperture)\X/2:EndIf
                          If Position\X+*Gerber\Apertures(Aperture)\X/2>*Gerber\Max\X:*Gerber\Max\X=Position\X+*Gerber\Apertures(Aperture)\X/2:EndIf
                          If Position\Y-*Gerber\Apertures(Aperture)\Y/2<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y-*Gerber\Apertures(Aperture)\Y/2:EndIf
                          If Position\Y+*Gerber\Apertures(Aperture)\Y/2>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y+*Gerber\Apertures(Aperture)\Y/2:EndIf
                        EndIf
                    EndSelect
                    MovePathCursor(OldPosition\X,OldPosition\Y,#PB_Path_Default)
                    AddPathLine(Position\X,Position\Y,#PB_Path_Default)
                  Case #Gerber_FillMode_Fill
                    DrawGerber(OldPosition)
                    AddPathLine(Position\X,Position\Y,#PB_Path_Default)
                    Select *Gerber\Apertures(Aperture)\Type
                      Case #Gerber_AT_Circle
                        StrokePath(*Gerber\Apertures(Aperture)\Diameter,#PB_Path_Default|#PB_Path_RoundEnd)
                      Case #Gerber_AT_Rectangle
                        StrokePath(*Gerber\Apertures(Aperture)\X,#PB_Path_Default|#PB_Path_SquareEnd)
                    EndSelect
                EndSelect
              Case #Gerber_GMode_G02
                Debug "JustMove G02 ignoriert"
              Case #Gerber_GMode_G03
                Debug "JustMove G03 ignoriert"
              Default
                MovePathCursor(Position\X,Position\Y,#PB_Path_Default)
            EndSelect
          EndIf
        EndIf
        ;}
      ElseIf *Gerber\Path()="G75" Or *Gerber\Path()="G74"
        ;{ Umschaltung auf Umrissmodus (74 ist Single-Quadrant-Mode; eventuell anpassen, aber eigentlich obsolet)
        VectorSourceColor($FF000000|*Gerber\ForegroundColor)
        Select *Gerber\FillMode
          Case #Gerber_FillMode_Outline
            StrokePath(1,#PB_Path_Default)
          Case #Gerber_FillMode_Fill
            If G75
              StrokePath(1,#PB_Path_Default)
            Else
              FillPath(#PB_Path_Winding)
            EndIf
        EndSelect
        G75=#True
        GMode=#Gerber_GMode_Undefined
        ;}
      Else
        Debug "Nicht verarbeitetes Kommando: "+*Gerber\Path()
      EndIf
    Next
    
    DrawGerber(Position)
  EndProcedure
  
  Procedure PlotGerberToCanvas(Gadget.i,*Gerber.Gerber)
    Protected Tick.q=ElapsedMilliseconds(),Size.Pos,NewPosition.Pos,Image.i
    Image=CreateImage(#PB_Any,GadgetWidth(Gadget),GadgetHeight(Gadget),24,*Gerber\BackgroundColor)
    StartVectorDrawing(ImageVectorOutput(Image))
    Size\X=ImageWidth(Image)/(*Gerber\Max\X-*Gerber\Min\X)
    Size\Y=ImageHeight(Image)/(*Gerber\Max\Y-*Gerber\Min\Y)
    If Size\X>Size\Y:Size\X=Size\Y:EndIf
    ScaleCoordinates(Size\X*0.98,Size\X*0.98,#PB_Coordinate_User)
    TranslateCoordinates(-*Gerber\Min\X,*Gerber\Min\Y,#PB_Coordinate_User)
    TranslateCoordinates(0.01*(*Gerber\Max\X-*Gerber\Min\X),0.01*(*Gerber\Max\Y-*Gerber\Min\Y),#PB_Coordinate_User)
    FlipCoordinatesY((*Gerber\Max\Y-*Gerber\Min\Y)/2,#PB_Coordinate_User)
    PlotGerber(*Gerber,#False)
    StopDrawing()
    StartDrawing(CanvasOutput(Gadget))
    Box(0,0,GadgetWidth(Gadget),GadgetHeight(Gadget),*Gerber\BackgroundColor)
    DrawImage(ImageID(Image),0,0)
    StopDrawing()
    FreeImage(Image)
    *Gerber\LastDuration=ElapsedMilliseconds()-Tick
  EndProcedure
  
  Procedure CreateGerberDataFromString(Gerber$)
    Protected Tick.q=ElapsedMilliseconds(),MacroName$,Temp$,PCount.l,PCounter.l,Counter.l,TCount.l,TCounter.l,ACount.l,ATemp$,ACounter.l,*Gerber.Gerber,Position.Pos,Image.i,Dim Out$(0),sum.a,count
    Gerber$=ReplaceString(ReplaceString(ReplaceString(ReplaceString(Gerber$,#CR$,""),#LF$,""),#TAB$,"")," ","")
    PCount=CountString(Gerber$,"%")+1
    *Gerber=AllocateStructure(Gerber)
    *Gerber\Min\X=Pow(10,9):*Gerber\Min\Y=Pow(10,9)
    *Gerber\Max\X=-Pow(10,9):*Gerber\Max\Y=-Pow(10,9)
    
    For PCounter=1 To PCount
      Temp$=Trim(StringField(Gerber$,PCounter,"%"))
      If Temp$<>""
        AddElement(*Gerber\RawData())
        *Gerber\RawData()=Temp$
      EndIf
    Next
    
    ForEach *Gerber\RawData()
      If MatchRegularExpression(#Gerber_RegEx_Header,*Gerber\RawData())
        ;{ Header einlesen
        If ExamineRegularExpression(#Gerber_RegEx_Header,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_Header)
          Select RegularExpressionGroup(#Gerber_RegEx_Header,1);Ommited Zeros
            Case "L"
              *Gerber\OmittedZeros=#Gerber_OZ_Leading
            Case "T"
              *Gerber\OmittedZeros=#Gerber_OZ_Trailing
            Case "D"
              *Gerber\OmittedZeros=#Gerber_OZ_No
          EndSelect
          Select RegularExpressionGroup(#Gerber_RegEx_Header,2);Coordinate Mode
            Case "A"
              *Gerber\CoordinateMode=#Gerber_Coord_Absolute
            Case "I"
              *Gerber\CoordinateMode=#Gerber_Coord_Incremental
          EndSelect
          *Gerber\SequenceNumber=Val(RegularExpressionGroup(#Gerber_RegEx_Header,3))
          *Gerber\PreparatoryFunctionCode=Val(RegularExpressionGroup(#Gerber_RegEx_Header,4))
          *Gerber\X=Val(Left(RegularExpressionGroup(#Gerber_RegEx_Header,5),1))
          *Gerber\Digits=Val(Right(RegularExpressionGroup(#Gerber_RegEx_Header,5),1))
          *Gerber\Y=Val(Left(RegularExpressionGroup(#Gerber_RegEx_Header,6),1))
          *Gerber\Z=Val(Left(RegularExpressionGroup(#Gerber_RegEx_Header,7),1))
          *Gerber\DraftCode=Val(RegularExpressionGroup(#Gerber_RegEx_Header,8))
          *Gerber\MiscCode=Val(RegularExpressionGroup(#Gerber_RegEx_Header,9))
          *Gerber\Scaling=Pow(10,*Gerber\Digits)
        Else
          PrintN("Header fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_Names,*Gerber\RawData())
        ;{ Names einlesen
        If ExamineRegularExpression(#Gerber_RegEx_Names,*Gerber\RawData()) And  NextRegularExpressionMatch(#Gerber_RegEx_Names)
          AddElement(*Gerber\Names())
          *Gerber\Names()=RegularExpressionGroup(#Gerber_RegEx_Names,1)
        Else
          PrintN("Namenszeile fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_Scaling,*Gerber\RawData())
        ;{ Scaling einlesen (mm oder Zoll)
        If ExamineRegularExpression(#Gerber_RegEx_Scaling,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_Scaling)
          Select RegularExpressionGroup(#Gerber_RegEx_Scaling,1)
            Case "IN"
              *Gerber\Unit=#Gerber_Unit_Inch
            Case "MM"
              *Gerber\Unit=#Gerber_Unit_MM
          EndSelect
        Else
          PrintN("Header Scalingzeile fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_ExposureMode,*Gerber\RawData())
        ;{ Exposuremode einlesen (positiv oder negativ)
        If ExamineRegularExpression(#Gerber_RegEx_ExposureMode,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_ExposureMode)
          Select RegularExpressionGroup(#Gerber_RegEx_ExposureMode,1)
            Case "POS"
              *Gerber\ExposureMode=#Gerber_EM_Positiv
            Case "NEG"
              *Gerber\ExposureMode=#Gerber_EM_Negativ
          EndSelect
        Else
          PrintN("Exposuremode Zeile fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_Macro,*Gerber\RawData())
        ;{ Apertur-Makros erstellen
        If ExamineRegularExpression(#Gerber_RegEx_Macro,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_Macro)
          MacroName$=RegularExpressionGroup(#Gerber_RegEx_Macro,1)
          ATemp$=Trim(RegularExpressionGroup(#Gerber_RegEx_Macro,2),"*")
          ACount=CountString(ATemp$,"*")+1
          For ACounter=1 To ACount
            Temp$=StringField(ATemp$,ACounter,"*")
            TCount=CountString(Temp$,",")+1
            If Val(StringField(Temp$,1,","))<>0;Kommentare aussortieren
              AddElement(*Gerber\ApertureMacro(MacroName$)\Primitives())
              *Gerber\ApertureMacro(MacroName$)\Primitives()\Type=Val(StringField(Temp$,1,","))
              Select *Gerber\ApertureMacro(MacroName$)\Primitives()\Type
                Case #Gerber_MT_Circle
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Exposure=Val(StringField(Temp$,2,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Diameter=ValD(StringField(Temp$,3,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterX=ValD(StringField(Temp$,4,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterY=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  If TCount>5
                    *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,6,","))**Gerber\Scaling
                  EndIf
                Case #Gerber_MT_Outline
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Exposure=Val(StringField(Temp$,2,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\StartX=ValD(StringField(Temp$,4,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\StartY=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\VertexCount=Val(StringField(Temp$,3,","))
                  For TCounter=1 To *Gerber\ApertureMacro(MacroName$)\Primitives()\VertexCount
                    AddElement(*Gerber\ApertureMacro(MacroName$)\Primitives()\Vertices())
                    *Gerber\ApertureMacro(MacroName$)\Primitives()\Vertices()\X=ValD(StringField(Temp$,4+2*TCounter,","))**Gerber\Scaling
                    *Gerber\ApertureMacro(MacroName$)\Primitives()\Vertices()\Y=ValD(StringField(Temp$,5+2*TCounter,","))**Gerber\Scaling
                  Next
                  If TCount>5+2**Gerber\ApertureMacro(MacroName$)\Primitives()\VertexCount
                    *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,6+2*ListSize(*Gerber\ApertureMacro(MacroName$)\Primitives()\Vertices()),","))**Gerber\Scaling
                  EndIf
                Case #Gerber_MT_Polygon
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Exposure=Val(StringField(Temp$,2,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\VertexCount=Val(StringField(Temp$,3,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterX=ValD(StringField(Temp$,4,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterY=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Diameter=ValD(StringField(Temp$,6,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,7,","))**Gerber\Scaling
                Case #Gerber_MT_Thermal
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterX=ValD(StringField(Temp$,2,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterY=ValD(StringField(Temp$,3,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\OuterDiameter=ValD(StringField(Temp$,4,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\InnerDiameter=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Gap=ValD(StringField(Temp$,6,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,7,","))**Gerber\Scaling
                Case #Gerber_MT_VectorLine,#Gerber_MT_LineVector
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Exposure=Val(StringField(Temp$,2,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Width=ValD(StringField(Temp$,3,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\StartX=ValD(StringField(Temp$,4,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\StartY=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\EndX=ValD(StringField(Temp$,6,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\EndY=ValD(StringField(Temp$,7,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,8,","))**Gerber\Scaling
                Case #Gerber_MT_CenterLine,#Gerber_MT_LowerLeftLine
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Exposure=Val(StringField(Temp$,2,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Width=ValD(StringField(Temp$,3,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Height=ValD(StringField(Temp$,4,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterX=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterY=ValD(StringField(Temp$,6,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,7,","))**Gerber\Scaling
                Case #Gerber_MT_Moire
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterX=ValD(StringField(Temp$,2,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CenterY=ValD(StringField(Temp$,3,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Diameter=Val(StringField(Temp$,4,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\RingThickness=ValD(StringField(Temp$,5,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Gap=ValD(StringField(Temp$,6,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\VertexCount=Val(StringField(Temp$,7,","))
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CrosshairThickness=ValD(StringField(Temp$,8,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\CrosshairLength=ValD(StringField(Temp$,9,","))**Gerber\Scaling
                  *Gerber\ApertureMacro(MacroName$)\Primitives()\Rotation=ValD(StringField(Temp$,10,","))**Gerber\Scaling
              EndSelect
            EndIf
          Next
        Else
          PrintN("Apertur-Makro fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_Apertures,*Gerber\RawData())
        ;{ Aperturen erstellen
        If ExamineRegularExpression(#Gerber_RegEx_Apertures,*Gerber\RawData())
          While NextRegularExpressionMatch(#Gerber_RegEx_Apertures)
            ATemp$=RegularExpressionGroup(#Gerber_RegEx_Apertures,1)
            Select RegularExpressionGroup(#Gerber_RegEx_Apertures,2)
              Case "C"
                *Gerber\Apertures(ATemp$)\Type=#Gerber_AT_Circle
                Temp$=RegularExpressionGroup(#Gerber_RegEx_Apertures,3)
                *Gerber\Apertures(ATemp$)\Diameter=ValD(StringField(Temp$,1,"X"))**Gerber\Scaling
                Select CountString(Temp$,"X")
                  Case 1
                    *Gerber\Apertures(ATemp$)\InnerX=ValD(StringField(Temp$,2,"X"))**Gerber\Scaling
                  Case 2
                    *Gerber\Apertures(ATemp$)\InnerX=ValD(StringField(Temp$,2,"X"))**Gerber\Scaling
                    *Gerber\Apertures(ATemp$)\InnerY=ValD(StringField(Temp$,3,"X"))**Gerber\Scaling
                EndSelect
              Case "R"
                *Gerber\Apertures(ATemp$)\Type=#Gerber_AT_Rectangle
                Temp$=RegularExpressionGroup(#Gerber_RegEx_Apertures,3)
                *Gerber\Apertures(ATemp$)\X=ValD(StringField(Temp$,1,"X"))**Gerber\Scaling
                *Gerber\Apertures(ATemp$)\Y=ValD(StringField(Temp$,2,"X"))**Gerber\Scaling
                Select CountString(Temp$,"X")
                  Case 2
                    *Gerber\Apertures(ATemp$)\Diameter=ValD(StringField(Temp$,3,"X"))**Gerber\Scaling
                  Case 3
                    *Gerber\Apertures(ATemp$)\InnerX=ValD(StringField(Temp$,3,"X"))**Gerber\Scaling
                    *Gerber\Apertures(ATemp$)\InnerY=ValD(StringField(Temp$,4,"X"))**Gerber\Scaling
                EndSelect
              Case "O"
                *Gerber\Apertures(ATemp$)\Type=#Gerber_AT_Obround
                Temp$=RegularExpressionGroup(#Gerber_RegEx_Apertures,3)
                *Gerber\Apertures(ATemp$)\X=ValD(StringField(Temp$,1,"X"))**Gerber\Scaling
                *Gerber\Apertures(ATemp$)\Y=ValD(StringField(Temp$,2,"X"))**Gerber\Scaling
                If CountString(Temp$,"X")=2
                  *Gerber\Apertures(Temp$)\InnerX=ValD(StringField(Temp$,3,"X"))**Gerber\Scaling
                EndIf
              Case "P"
                *Gerber\Apertures(ATemp$)\Type=#Gerber_AT_Polygon
                Temp$=RegularExpressionGroup(#Gerber_RegEx_Apertures,3)
                *Gerber\Apertures(ATemp$)\X=ValD(StringField(Temp$,1,"X"))**Gerber\Scaling
                *Gerber\Apertures(ATemp$)\Vertex=Val(StringField(Temp$,2,"X"))
                Select CountString(Temp$,"X")
                  Case 2
                    *Gerber\Apertures(ATemp$)\Rotation=ValD(StringField(Temp$,3,"X"))**Gerber\Scaling
                  Case 3
                    *Gerber\Apertures(ATemp$)\Rotation=ValD(StringField(Temp$,3,"X"))**Gerber\Scaling
                    *Gerber\Apertures(ATemp$)\InnerX=ValD(StringField(Temp$,4,"X"))**Gerber\Scaling
                EndSelect
              Default
                *Gerber\Apertures(ATemp$)\Type=#Gerber_AT_ApertureMacro
                *Gerber\Apertures(ATemp$)\ApertureMacro=RegularExpressionGroup(#Gerber_RegEx_Apertures,2)
                If Not FindMapElement(*Gerber\ApertureMacro(),*Gerber\Apertures(ATemp$)\ApertureMacro)
                  Debug "Fehlendes Apertur-Makro: "+*Gerber\Apertures(ATemp$)\ApertureMacro
                EndIf
            EndSelect
          Wend
        Else
          PrintN("Apertur fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_LeadPolarity,*Gerber\RawData())
        ;{ Lead Polarity (Dark/Clear)
        If ExamineRegularExpression(#Gerber_RegEx_LeadPolarity,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_LeadPolarity)
          Select RegularExpressionGroup(#Gerber_RegEx_LeadPolarity,1)
            Case "D"
              *Gerber\LeadPolarity=#Gerber_LeadPolarity_Dark
            Case "C"
              *Gerber\LeadPolarity=#Gerber_LeadPolarity_Clear
          EndSelect
        Else
          PrintN("Header LeadPolarity fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_Mirror,*Gerber\RawData())
        ;{ Mirror Image
        If ExamineRegularExpression(#Gerber_RegEx_Mirror,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_Mirror)
          For ACount=1 To CountRegularExpressionGroups(#Gerber_RegEx_Mirror)
            Temp$=RegularExpressionGroup(#Gerber_RegEx_Mirror,ACount)
            Select Left(Temp$,1)
              Case "A"
                *Gerber\MirrorA=Val(Right(Temp$,1))
              Case "B"
                *Gerber\MirrorB=Val(Right(Temp$,1))
            EndSelect
          Next
        Else
          PrintN("Mirroring fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_ScaleFactor,*Gerber\RawData())
        ;{ ScaleFactor
        If ExamineRegularExpression(#Gerber_RegEx_ScaleFactor,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_ScaleFactor)
          For ACount=1 To CountRegularExpressionGroups(#Gerber_RegEx_ScaleFactor)
            Temp$=RegularExpressionGroup(#Gerber_RegEx_ScaleFactor,ACount)
            Select Left(Temp$,1)
              Case "A"
                *Gerber\ScaleFactorA=Val(Right(Temp$,Len(Temp$)-1))
              Case "B"
                *Gerber\ScaleFactorB=Val(Right(Temp$,Len(Temp$)-1))
            EndSelect
          Next
        Else
          PrintN("ScaleFactor fehlerhaft!")
        EndIf
        ;}
      ElseIf MatchRegularExpression(#Gerber_RegEx_Attributes,*Gerber\RawData())
        ;{ Attribute einlesen
        If ExamineRegularExpression(#Gerber_RegEx_Attributes,*Gerber\RawData()) And NextRegularExpressionMatch(#Gerber_RegEx_Attributes)
          *Gerber\Attributes(RegularExpressionGroup(#Gerber_RegEx_Attributes,1))=RegularExpressionGroup(#Gerber_RegEx_Attributes,2)
        Else
          PrintN("Attribut fehlerhaft!")
        EndIf
        ;}
      Else
        ;{ Renderpfad generieren
        Select Left(*Gerber\RawData(),1)
          Case "X","Y","G","D"
            Temp$=Trim(*Gerber\RawData(),"*")
            ACount=CountString(Temp$,"*")
            If StringField(Temp$,ACount+1,"*")="M02"
              For ACounter=1 To ACount
                AddElement(*Gerber\Path())
                If *Gerber\OmittedZeros=#Gerber_OZ_Trailing
                  ATemp$=StringField(Temp$,ACounter,"*")
                  sum=ExtractRegularExpression(#Gerber_RegEx_Omitter,ATemp$,Out$())
                  If sum>0
                    For count=0 To ArraySize(Out$())
                      If FindString(Out$(count),"-")
                        ATemp$=ReplaceString(ATemp$,Out$(count),LSet(Out$(count),2+*Gerber\X+*Gerber\Digits,"0"))
                      Else
                        ATemp$=ReplaceString(ATemp$,Out$(count),LSet(Out$(count),1+*Gerber\X+*Gerber\Digits,"0"))
                      EndIf
                    Next
                  EndIf
                  *Gerber\Path()=ATemp$
                Else
                  *Gerber\Path()=StringField(Temp$,ACounter,"*")
                EndIf
                If ExamineRegularExpression(#Gerber_Command_PreprocessX,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_PreprocessX)
                  If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
                    Position\X=ValD(RegularExpressionGroup(#Gerber_Command_PreprocessX,1))
                  Else
                    Position\X+ValD(RegularExpressionGroup(#Gerber_Command_PreprocessX,1))
                  EndIf
                  If Position\X>*Gerber\Max\X:*Gerber\Max\X=Position\X:EndIf
                  If Position\X<*Gerber\Min\X:*Gerber\Min\X=Position\X:EndIf
                EndIf
                If ExamineRegularExpression(#Gerber_Command_PreprocessY,*Gerber\Path()) And NextRegularExpressionMatch(#Gerber_Command_PreprocessY)
                  If *Gerber\CoordinateMode=#Gerber_Coord_Absolute
                    Position\Y=ValD(RegularExpressionGroup(#Gerber_Command_PreprocessY,1))
                  Else
                    Position\Y+ValD(RegularExpressionGroup(#Gerber_Command_PreprocessY,1))
                  EndIf
                  If Position\Y>*Gerber\Max\Y:*Gerber\Max\Y=Position\Y:EndIf
                  If Position\Y<*Gerber\Min\Y:*Gerber\Min\Y=Position\Y:EndIf
                EndIf
              Next
            Else
              PrintN("Plottersequenz ohne Ende gefunden!")
            EndIf
          Default
            ConsoleColor(4,0)
            PrintN("Unbekannt: "+Left(*Gerber\RawData(),25)+"...")
            ConsoleColor(7,0)
        EndSelect
        ;}
      EndIf
    Next
    
    Image=CreateImage(#PB_Any,1,1)
    StartVectorDrawing(ImageVectorOutput(Image))
    PlotGerber(*Gerber,#True)
    StopDrawing()
    FreeImage(Image)
    
    *Gerber\BoardSize\X=(*Gerber\Max\X-*Gerber\Min\X)/*Gerber\Scaling
    *Gerber\BoardSize\Y=(*Gerber\Max\Y-*Gerber\Min\Y)/*Gerber\Scaling
    If *Gerber\Unit=#Gerber_Unit_Inch
      *Gerber\BoardSize\X=*Gerber\BoardSize\X*25.4
      *Gerber\BoardSize\Y=*Gerber\BoardSize\Y*25.4
    EndIf
    
    *Gerber\LastDuration=ElapsedMilliseconds()-Tick
    ProcedureReturn *Gerber
  EndProcedure
  
  Procedure CreateGerberDataFromFile(File$)
    Protected File,Input$
    File=ReadFile(#PB_Any,File$,#PB_Ascii|#PB_File_SharedRead)
    If File
      Input$=ReadString(file,#PB_File_IgnoreEOL)
      CloseFile(File)
    EndIf
    If Input$<>""
      ProcedureReturn CreateGerberDataFromString(Input$)
    EndIf
  EndProcedure
  
EndModule
Last edited by jacdelad on Wed Aug 23, 2023 5:32 pm, edited 7 times in total.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

And the demo (the post was too long):

Code: Select all

EnableExplicit
XIncludeFile "Gerber.pbi"
UseModule Gerber

Global *Gerber.Gerber
#W_Main=0
#G_Canvas=0
Runtime #W_Main
Runtime #G_Canvas

Procedure SizeWindow()
  StartDrawing(CanvasOutput(0))
  CompilerIf #PB_Compiler_OS=#PB_OS_Windows
    Box(0,0,GadgetWidth(0),GadgetHeight(0),GetSysColor_(#COLOR_BTNFACE))
  CompilerElse
    Box(0,0,GadgetWidth(0),GadgetHeight(0),#White)
  CompilerEndIf
  StopDrawing()
  If *Gerber
    PlotGerberToCanvas(0,*Gerber)
  EndIf
EndProcedure

Define XML$="<window id='#W_Main' name='gerbertest' text='Gerber-Test' minwidth='600' minheight='400' flags='#PB_Window_SizeGadget|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_ScreenCentered'>"+
            "<canvas id='#G_Canvas'/>"+
            "</window>"
ParseXML(0,XML$)
CreateDialog(0)
OpenXMLDialog(0,0,"gerbertest")
StartDrawing(CanvasOutput(0))
CompilerIf #PB_Compiler_OS=#PB_OS_Windows
  Box(0,0,GadgetWidth(0),GadgetHeight(0),GetSysColor_(#COLOR_BTNFACE))
CompilerElse
  Box(0,0,GadgetWidth(0),GadgetHeight(0),#White)
CompilerEndIf
StopDrawing()
BindGadgetEvent(#G_Canvas,@SizeWindow(),#PB_EventType_Resize)
EnableGadgetDrop(#G_Canvas,#PB_Drop_Files,#PB_Drag_Copy|#PB_Drag_Link|#PB_Drag_Move)
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
      
    Case #PB_Event_GadgetDrop
      If *Gerber
        FreeStructure(*Gerber)
      EndIf
      *Gerber=CreateGerberDataFromFile(EventDropFiles())
      CompilerIf #PB_OS_Windows
        *Gerber\BackgroundColor=GetSysColor_(#COLOR_BTNFACE)
      CompilerElse
        *Gerber\BackgroundColor=#White
      CompilerEndIf
      *Gerber\ForegroundColor=#Black
      *Gerber\FillMode=#Gerber_FillMode_Fill
      PlotGerberToCanvas(#G_Canvas,*Gerber)
      
  EndSelect
ForEver
Save the module into Gerber.pbi into the same directory as the demo or put it into one file and remove the XIncludeFile.

Reference Gerber Viewer online: https://gerber-viewer.ucamco.com/
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Need a push for 3D

Post by jacdelad »

Little update:
The Gerber library is almost complete. I have ~107400 files to test, which I will do within the next 2 weeks (my kind of vacation). Two standard aperture macros are still missing (both rarely used, one deprecated in 2021). Right now I'm working on a GerberGadget which allows zoom/moving by mouse and keyboard (based on a canvas). Moving/zooming itself isn't hard, but it's hard to keep track of the current position, the position of the mouse cursor relative to the zoomed board, etc.
However, I will post the result when it's done. Then I will start to do the modeler to bring the board onto a 3D object, my initial plan. But this will another thread full of questions...
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Need a push for 3D

Post by Caronte3D »

An interesting project, I definitely will test it when you release it :wink:
Good luck!
Post Reply