aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi Murayama <opiopan@gmail.com>2019-12-28 23:45:33 +0900
committerHiroshi Murayama <opiopan@gmail.com>2019-12-28 23:45:33 +0900
commit244fcaa5346f4fad819cc2b72857cfb2c472944a (patch)
treed849592e18a1afe61fb98f3207dabdaea1f5336e
parentd7a069324222bb8f69adc9b1c815fc9f3f6a29d6 (diff)
downloadpcb-tools-extension-244fcaa5346f4fad819cc2b72857cfb2c472944a.tar.gz
pcb-tools-extension-244fcaa5346f4fad819cc2b72857cfb2c472944a.tar.bz2
pcb-tools-extension-244fcaa5346f4fad819cc2b72857cfb2c472944a.zip
add a function that generate filled gerberdata with representing internal shape by fliping polarity
-rw-r--r--README.md141
-rw-r--r--examples/inputs/fill.dxf2602
-rwxr-xr-xexamples/panelize.py7
-rw-r--r--gerberex/dxf.py282
-rw-r--r--gerberex/dxf_path.py125
-rw-r--r--gerberex/utility.py9
-rw-r--r--setup.py2
-rw-r--r--tests/data/ref_dxf_metric.dxf304
-rw-r--r--tests/data/ref_dxf_mousebites.dxf344
-rw-r--r--tests/expects/dxf_offset.gtl82
-rw-r--r--tests/expects/dxf_rotate.gtl82
-rw-r--r--tests/expects/dxf_save_fill.gtl71
-rw-r--r--tests/expects/dxf_save_fill_simple.gtl78
-rw-r--r--tests/expects/dxf_save_line.gtl84
-rw-r--r--tests/expects/dxf_save_line.txt84
-rw-r--r--tests/expects/dxf_save_mousebites.gtl592
-rw-r--r--tests/expects/dxf_save_mousebites.txt592
-rw-r--r--tests/expects/dxf_to_inch.gtl82
-rw-r--r--tests/test_dxf.py30
19 files changed, 2428 insertions, 3165 deletions
diff --git a/README.md b/README.md
index 2831672..252de53 100644
--- a/README.md
+++ b/README.md
@@ -68,96 +68,121 @@ ctx.dump('panelized-board.txt')
```
## DXF file translation
+pcb-tools-extension hsa a function to load a DXF file and handle that as same as RX-274x gerber file or Excellon NC file.<br>
+In this version, Only line, circle, arc, and polyline objects are recognized and are translated to gerber file or NC file.
-### PCB Outline
-You can also load a dxf file and handle that as same as RX-274x gerber file or Excellon NC file.<br>
-This function is useful to generate outline data of pnanelized PCB boad.
+### Two way to tranlate DXF file
+Both composition objects, ```GerberComposition``` for RX-274x and ```DrillionComposition``` for Excellon, can accept an object created as result of DXF file loaded. When composition object dump text stream, DXF data tranclate to appropriate format data.<br>
+The object which represent DXF file, can also output translated data directly by ```save``` method. In this case output format is specified by ```filetype``` argument. If ```filetype``` argument is ommited, DXF data is translated to RX-274x gerber data.
```python
import gerberex
+dxf = gerberex.read('sample.dxf')
-dxf = gerberex.read('outline.dxf')
-ctx1 = gerberex.GerberComposition()
-ctx1.merge(dxf)
-ctx2 = gerberex.DrillComposition()
-ctx2.merge(dxf)
+# translate to RX-274x using composition object
+ctx = gerberex.GerberComposition()
+ctx.merge(dxf)
+ctx.dump('sample.gml')
+
+# translate to Excellon using composition object
+ctx = gerberex.DrillComposition()
+ctx.merge(dxf)
+ctx.dump('sample.txt')
+
+# translate to RX-274x directly
+dxf.save('sample2.gml')
+
+# translate to Excellon directly
+dxf.save('sample2.txt', filetype=dxf.FT_EXCELLON)
```
-Circle object, Arc object, Line object and Polyline object are supported. Other kind of objects in DXF file are ignored when translating to gerber data.
-You can specify line width (default 0). PCB tools extension will translate DXF primitive shape to RX-274x line or arc sequense using circle aperture with diamater as same as specified line width. <br>
+### Generating Rectangle
+If you want to arrange simple rectangle for PCB outline, ```gerberex.rectangle()``` is better solution. This generate a object representing a rectangle compatible with DXF file object.<br>
```python
import gerberex
-dxf = gerberex.read('outline.dxf')
-dxf.to_inch()
-dxf.width = 0.004
-dxf.write('outline.gml')
+outline = gerberex.rectangle(width=100, height=100, units='metric')
+outline.write('outline.gml')
```
-If ```FT_EXCELLON``` is specified for ```filetype``` argument of ```write()```, Excellon NC data is generated. In this case, Excellon file consists of routing commands using a specified width drill.
+### Drawing Mode
+PCB tools extension provide three type of translation method that affects geometric finish. These method are specified a value for ```draw_mode``` attribute, ```DM_LINE```, ```DM_MOUSE_BITES```, or ```DM_FILL```.<br>
+```DM_LINE``` and ```DM_MOUSE_BITES``` are used to translate to both of RX-274x and Excellon, however ```DM_FILL``` is used to translate to only RX-274x.
+![Drawing Mode](https://raw.githubusercontent.com/wiki/opiopan/pcb-tools-extension/images/draw_mode.jpg)
-```python
-import gerberex
+- **draw_mode = DM_LINE**<br>
+ All edge expressed as DXF line object, circle object, arc object and plyline objects are translated to line and arc applied a circular aperture in case of RX-274x. That circular aperture r radius is specified by ```width``` attribute. Default value of width is 0.<br>
+ In case of Excellon, DXF objects are translated to routing path command sequence.
+ This function is useful to generate outline data of pnanelized PCB boad.
-dxf = gerberex.read('outline.dxf')
-dxf.to_metric()
-dxf.width = 0.3
-dxf.write('outline.txt', filetype=dxf.FT_EXCELLON)
-```
+ ```python
+ import gerberex
+ dxf = gerberex.read('outline.dxf')
+ dxf.to_inch()
+ dxf.width = 0.004
+ dxf.write('outline.gml')
+ ```
-You can also translate DXF closed shape such as circle to RX-274x polygon fill sequence.<br>
-In order to fill closed shape, ```DM_FILL``` has to be set to ```draw_mode``` property. In this mode, All object except closed shapes listed below are ignored.
+- **draw_mode = DM_MOUSE_BITES**<br>
+ <img alt="mouse bites" src="https://raw.githubusercontent.com/wiki/opiopan/pcb-tools-extension/images/mousebites.png" width=200 align="right">
+ If DM_MOUSE_BITES is specified for draw_mode, filled circles are arranged at equal intervals along a paths consisted of DXF line, arc, circle, and plyline objects.
+ DXF file object in this state can be merged to excellon file also. That means you can arrange mouse bites easily.
-- circle
-- closed polyline
-- closed path which consists of lines and arcs
+ ```python
+ import gerberex
-NOTE: ```DM_FILL``` can be used only to generate RX-274x data, it cannot be used to generate Excellon data.
+ ctx = gerberex.DrillComposition()
+ drill = gerberex.read('drill.txt')
+ ctx.merge(drill)
-```python
-import gerberex
+ dxf = gerberex.read('mousebites.dxf')
+ dxf.draw_mode = dxf.DM_MOUSE_BITES
+ dxf.to_metric()
+ dxf.width = 0.5
+ dxf.pitch = 1
+ ctx.merge(dxf)
-dxf = gerberex.read('outline.dxf')
-dxf.draw_mode = dxf.DM_FILL
-dxf.write('outline.gml')
-```
+ ctx.dump('merged_drill.txt')
+ ```
-If you want to arrange simple rectangle for PCB outline, ```gerberex.rectangle()``` is better solution. This generate a object representing a rectangle compatible with DXF file object.<br>
+- **draw_mode = DM_FILL**<br>
+ You can translate DXF closed shape such as circle to RX-274x polygon fill sequence.<br>
+ In order to fill closed shape, ```DM_FILL``` has to be set to ```draw_mode``` property. In this mode, All object except closed shapes listed below are ignored.
-```python
-import gerberex
+ - circle
+ - closed polyline
+ - closed path which consists of lines and arcs
-outline = gerberex.rectangle(width=100, height=100, units='metric')
-outline.write('outline.gml')
-```
+ If a closed shape is completly included in other closed shape, The inner shape will be draw with reversed polality of container shape as above example image.<br>
-### Mouse bites
+ I assume there are two typical usecase for this mode.<br>
+ One is to arrange logo design on silk layer. This is superior to other method generating raster image data since image data express as vector data.<br>
+ The other one is generating gerber data represented cropped area of panelized PCB.
+ By merging rectangle and PCB outline data, generate a file represented cropped area as below, and this kind of data is useful to make PCB image look good a little bit.<br>
+ [This script](https://github.com/opiopan/pcb-tools-extension/blob/master/examples/genimage.py) which generate example image shown below, also uses this technic.
-<img alt="mouse bites" src="https://raw.githubusercontent.com/wiki/opiopan/pcb-tools-extension/images/mousebites.png" width=200 align="right">
+ ```python
+ import gerberex
+ ctx = gerberex.GerberComposition()
-If ```DM_MOUSE_BITES``` is specified for ```draw_mode```, filled circles are arranged at equal intervals along a paths consisted of DXF line, arc, circle, and plyline objects. <br>
-DXF file object in this state can be merged to excellon file also. That means you can arrange mouse bites easily.
+ rectangle = gerberex.rectangle(width=100, height=100, units='metric')
+ rectangle.draw_mode = rectangle.DM_FILL
+ ctx.merge(rectangle)
+
+ outline = gerberex.read('outline.dxf')
+ outline.draw_mode = outline.DM_FILL
+ outline.negate_polarity()
+ ctx.merge(outline)
-```python
-import gerberex
+ ctx.dump('cropped_area.gml')
+ ```
-ctx = gerberex.DrillComposition()
-drill = gerberex.read('drill.txt')
-ctx.merge(drill)
-
-dxf = gerberex.read('mousebites.dxf')
-dxf.draw_mode = dxf.DM_MOUSE_BITES
-dxf.to_metric()
-dxf.width = 0.5
-dxf.pitch = 1
-ctx.merge(dxf)
+ NOTE: ```DM_FILL``` can be used only to generate RX-274x data, it cannot be used to generate Excellon data.
-ctx.dump('merged_drill.txt')
-```
## Panelizing Example
This example board image is generated by following scripts from [these source data](https://github.com/opiopan/pcb-tools-extension/tree/master/examples/inputs).
diff --git a/examples/inputs/fill.dxf b/examples/inputs/fill.dxf
deleted file mode 100644
index fdea04b..0000000
--- a/examples/inputs/fill.dxf
+++ /dev/null
@@ -1,2602 +0,0 @@
-0
-SECTION
-2
-HEADER
-9
-$INSUNITS
-70
-4
-9
-$ACADVER
-1
-AC1014
-9
-$HANDSEED
-5
-FFFF
-0
-ENDSEC
-0
-SECTION
-2
-TABLES
-0
-TABLE
-2
-VPORT
-5
-8
-100
-AcDbSymbolTable
-0
-ENDTAB
-0
-TABLE
-2
-LTYPE
-5
-5
-100
-AcDbSymbolTable
-0
-LTYPE
-5
-14
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
-2
-BYBLOCK
-70
-0
-0
-LTYPE
-5
-15
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
-2
-BYLAYER
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-LAYER
-5
-2
-100
-AcDbSymbolTable
-70
-2
-0
-LAYER
-5
-50
-100
-AcDbSymbolTableRecord
-100
-AcDbLayerTableRecord
-2
-0
-70
-0
-6
-CONTINUOUS
-0
-ENDTAB
-0
-TABLE
-2
-STYLE
-5
-3
-100
-AcDbSymbolTable
-70
-1
-0
-STYLE
-5
-11
-100
-AcDbSymbolTableRecord
-100
-AcDbTextStyleTableRecord
-2
-STANDARD
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-VIEW
-5
-6
-100
-AcDbSymbolTable
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-UCS
-5
-7
-100
-AcDbSymbolTable
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-APPID
-5
-9
-100
-AcDbSymbolTable
-70
-2
-0
-APPID
-5
-12
-100
-AcDbSymbolTableRecord
-100
-AcDbRegAppTableRecord
-2
-ACAD
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-DIMSTYLE
-5
-A
-100
-AcDbSymbolTable
-70
-1
-0
-ENDTAB
-0
-TABLE
-2
-BLOCK_RECORD
-5
-1
-100
-AcDbSymbolTable
-70
-1
-0
-BLOCK_RECORD
-5
-1F
-100
-AcDbSymbolTableRecord
-100
-AcDbBlockTableRecord
-2
-*MODEL_SPACE
-0
-BLOCK_RECORD
-5
-1B
-100
-AcDbSymbolTableRecord
-100
-AcDbBlockTableRecord
-2
-*PAPER_SPACE
-0
-ENDTAB
-0
-ENDSEC
-0
-SECTION
-2
-BLOCKS
-0
-BLOCK
-5
-20
-100
-AcDbEntity
-100
-AcDbBlockBegin
-2
-*MODEL_SPACE
-0
-ENDBLK
-5
-21
-100
-AcDbEntity
-100
-AcDbBlockEnd
-0
-BLOCK
-5
-1C
-100
-AcDbEntity
-100
-AcDbBlockBegin
-2
-*PAPER_SPACE
-0
-ENDBLK
-5
-1D
-100
-AcDbEntity
-100
-AcDbBlockEnd
-0
-ENDSEC
-0
-SECTION
-2
-ENTITIES
-0
-LWPOLYLINE
-5
-100
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-6
-70
-1
-43
-0.0
-10
-40
-20
-100
-10
-39.999999999999993
-20
-88.499999999999972
-42
--0.41421356237308982
-10
-38.5
-20
-87
-10
-36.899999999999999
-20
-87
-42
--0.41421356237308982
-10
-35.400000000000006
-20
-88.499999999999972
-10
-35.399999999999999
-20
-100
-0
-LWPOLYLINE
-5
-101
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-4
-70
-1
-43
-0.0
-10
-21.25
-20
-63.335000000000001
-42
--0.99999999999999989
-10
-22.450000000000003
-20
-63.335000000000001
-10
-22.450000000000003
-20
-62.034999999999997
-42
--0.99999999999999989
-10
-21.25
-20
-62.034999999999997
-0
-ARC
-5
-102
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-21.850000000000001
-20
-63.335000000000001
-30
-0
-40
-0.60000000000000053
-100
-AcDbArc
-50
-0
-51
-180
-0
-CIRCLE
-5
-103
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-29.950000000000003
-20
-62.685000000000002
-30
-0
-40
-0.59999999999999998
-0
-LWPOLYLINE
-5
-104
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-19
-70
-1
-43
-0.0
-10
-22.200000000000003
-20
-60
-42
--0.4142135623730907
-10
-23.700000000000003
-20
-58.5
-10
-23.700000000000003
-20
-55.520659651813133
-42
--0.4142135623730907
-10
-22.200000000000003
-20
-54.020659651813133
-10
-13.5
-20
-54.020659651813133
-42
--0.4142135623730907
-10
-12.000000000000002
-20
-55.520659651813133
-10
-12.000000000000002
-20
-57
-10
-12.000000000000002
-20
-58.5
-42
--0.4142135623730907
-10
-13.5
-20
-60
-10
-13.900000000000002
-20
-60
-10
-15.4
-20
-60
-10
-15.4
-20
-61.5
-10
-15.4
-20
-71.5
-42
--0.41421356237308898
-10
-16.899999999999999
-20
-73
-10
-18.5
-20
-73
-42
--0.41421356237308898
-10
-20
-20
-71.5
-10
-20
-20
-61.5
-10
-20
-20
-60
-10
-21.5
-20
-60
-0
-LWPOLYLINE
-5
-105
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-8
-70
-1
-43
-0.0
-10
-22.5
-20
-21.300000000000004
-42
--0.41421356237308982
-10
-21.5
-20
-20.300000000000004
-10
-2.9999999999999982
-20
-20.300000000000004
-42
-0.41421356237309481
-10
-0
-20
-17.300000000000001
-10
-0
-20
-25.500000000000004
-42
-0.41421356237309548
-10
-3.0000000000000004
-20
-22.5
-10
-21.5
-20
-22.5
-42
--0.41421356237308982
-10
-22.5
-20
-21.5
-0
-LWPOLYLINE
-5
-106
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-18
-70
-1
-43
-0.0
-10
-43.699999999999989
-20
-55.520659651813126
-42
--0.4142135623730881
-10
-42.199999999999989
-20
-54.020659651813119
-10
-33.199999999999996
-20
-54.020659651813119
-42
--0.4142135623730907
-10
-31.699999999999999
-20
-55.520659651813126
-10
-31.699999999999999
-20
-58.5
-42
--0.4142135623730907
-10
-33.199999999999996
-20
-60
-10
-33.899999999999999
-20
-60
-10
-35.399999999999999
-20
-60
-10
-35.399999999999999
-20
-61.5
-10
-35.399999999999999
-20
-71.5
-42
--0.41421356237308898
-10
-36.899999999999999
-20
-73
-10
-38.5
-20
-73
-42
--0.41421356237308898
-10
-40
-20
-71.5
-10
-40
-20
-61.5
-10
-40
-20
-60
-10
-41.5
-20
-60
-10
-42.199999999999989
-20
-60
-42
--0.4142135623730881
-10
-43.699999999999989
-20
-58.5
-0
-LWPOLYLINE
-5
-107
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-6
-70
-1
-43
-0.0
-10
-60.220000000000013
-20
-88.499999999999972
-42
--0.41421356237308982
-10
-58.720000000000013
-20
-87
-10
-56.900000000000006
-20
-87
-42
--0.41421356237308982
-10
-55.400000000000006
-20
-88.499999999999972
-10
-55.399999999999999
-20
-100
-10
-60.220000000000027
-20
-100
-0
-LWPOLYLINE
-5
-108
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-9
-70
-1
-43
-0.0
-10
-0
-20
-44
-10
-12.000000000000002
-20
-44
-10
-12.000000000000002
-20
-47.094063415349972
-10
-21
-20
-47.094063415349972
-42
--0.4142135623730907
-10
-22.5
-20
-45.594063415349964
-10
-22.5
-20
-44.300000000000004
-42
--0.4142135623730907
-10
-21
-20
-42.800000000000004
-10
-2.9999999999999982
-20
-42.800000000000004
-42
-0.41421356237309481
-10
-0
-20
-39.800000000000004
-0
-LWPOLYLINE
-5
-109
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-54
-70
-1
-43
-0.0
-10
-42.5
-20
-21.5
-10
-42.5
-20
-21.300000000000004
-42
-0.4142135623731002
-10
-43.5
-20
-20.300000000000008
-10
-62
-20
-20.300000000000004
-42
--0.41421356237309509
-10
-65
-20
-17.300000000000001
-10
-65
-20
-16.500250410747213
-42
-0.4142135623730907
-10
-66.5
-20
-15.000250410747213
-10
-66.868642843789601
-20
-15.000250410747213
-42
--0.3348378315883157
-10
-68.310895504208148
-20
-13.912448498461867
-10
-70.46220136370448
-20
-6.3851777718192153
-42
-0.38411426985847918
-10
-72.732501266263071
-20
-4.9651597160810006
-10
-80.234065260223005
-20
-6.2878878457272922
-42
-0.41421356237308843
-10
-81.450804623240941
-20
-8.0255717417460044
-10
-81.446322967315581
-20
-8.0509884755172365
-42
-0.41421356237309825
-10
-79.708639071296886
-20
-9.2677278385351443
-10
-72.519542474307769
-20
-8.0000961415665639
-10
-65.035306016863061
-20
-50.445310296392726
-10
-92.21599999999998
-20
-55.238000000000014
-10
-99.700236457444689
-20
-12.792785845173839
-10
-92.511139860455586
-20
-11.525154148205248
-42
-0.41421356237310253
-10
-91.294400497437664
-20
-9.7874702521865409
-10
-91.298882153363024
-20
-9.7620535184153105
-42
-0.41421356237309104
-10
-93.036566049381747
-20
-8.5453141553973957
-10
-100.00000000000001
-20
-9.7731554392727293
-10
-100.00000000000001
-20
-59.359999999999999
-10
-82.220000000000013
-20
-59.359999999999999
-10
-82.220000000000013
-20
-71.5
-42
-0.41421356237309331
-10
-80.720000000000013
-20
-73
-10
-79.500000000000028
-20
-73
-42
-0.4142135623730881
-10
-78.000000000000028
-20
-71.5
-10
-78.000000000000014
-20
-59.359999999999999
-10
-60.220000000000013
-20
-59.359999999999999
-10
-60.22000000000002
-20
-71.5
-42
-0.4142135623730881
-10
-58.720000000000013
-20
-73
-10
-56.900000000000006
-20
-73
-42
-0.4142135623730881
-10
-55.399999999999999
-20
-71.5
-10
-55.399999999999999
-20
-60
-10
-53.200000000000003
-20
-60
-42
-0.4142135623730881
-10
-51.700000000000003
-20
-58.5
-10
-51.700000000000003
-20
-55.520659651813119
-42
-0.4142135623730881
-10
-53.200000000000003
-20
-54.020659651813112
-10
-58.220000000000013
-20
-54.020659651813119
-42
--0.41421356237309109
-10
-60.220000000000013
-20
-52.020659651813112
-10
-60.220000000000013
-20
-49.094063415349972
-42
--0.41421356237309109
-10
-58.220000000000013
-20
-47.094063415349964
-10
-44
-20
-47.094063415349972
-42
-0.4142135623730881
-10
-42.5
-20
-45.594063415349964
-10
-42.5
-20
-44.300000000000004
-42
-0.4142135623730881
-10
-44
-20
-42.800000000000004
-10
-62
-20
-42.800000000000004
-42
--0.41421356237309509
-10
-65
-20
-39.800000000000004
-10
-65
-20
-25.5
-42
--0.41421356237309509
-10
-62
-20
-22.5
-10
-43.5
-20
-22.499999999999996
-42
-0.4142135623731002
-0
-ARC
-5
-110
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-3.0000000000000004
-20
-3.0000000000000004
-30
-0
-40
-3.0000000000000004
-100
-AcDbArc
-50
-180
-51
-270
-0
-LINE
-5
-111
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-0
-20
-57
-30
-0
-11
-1.9000000000000004
-21
-57
-31
-0
-0
-LINE
-5
-112
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-3.4000000000000004
-20
-58.5
-30
-0
-11
-3.4000000000000004
-21
-58.5
-31
-0
-0
-ARC
-5
-113
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-1.8999999999999884
-20
-58.499999999999986
-30
-0
-40
-1.500000000000012
-100
-AcDbArc
-50
-0
-51
-89.999999999999545
-0
-LWPOLYLINE
-5
-114
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-4
-70
-1
-43
-0.0
-10
-2.4500000000000006
-20
-62.034999999999997
-42
--0.99999999999999989
-10
-1.25
-20
-62.034999999999997
-10
-1.2500000000000002
-20
-63.335000000000001
-42
--0.99999999999999989
-10
-2.4500000000000002
-20
-63.335000000000001
-0
-CIRCLE
-5
-115
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-9.9499999999999993
-20
-62.685000000000002
-30
-0
-40
-0.59999999999999998
-0
-LWPOLYLINE
-5
-116
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-4
-70
-1
-43
-0.0
-10
-12.950000000000001
-20
-63.335000000000001
-42
--0.99999999999999989
-10
-14.150000000000002
-20
-63.335000000000001
-10
-14.150000000000006
-20
-62.034999999999997
-42
--0.99999999999999989
-10
-12.949999999999999
-20
-62.034999999999997
-0
-ARC
-5
-117
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-13.550000000000002
-20
-63.335000000000001
-30
-0
-40
-0.60000000000000053
-100
-AcDbArc
-50
-0
-51
-180
-0
-ARC
-5
-118
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-1.8500000000000003
-20
-63.335000000000001
-30
-0
-40
-0.59999999999999998
-100
-AcDbArc
-50
-0
-51
-180
-0
-ARC
-5
-119
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-1.8500000000000003
-20
-62.034999999999997
-30
-0
-40
-0.60000000000000031
-100
-AcDbArc
-50
--180
-51
-0
-0
-ARC
-5
-120
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-13.550000000000002
-20
-62.034999999999997
-30
-0
-40
-0.60000000000000275
-100
-AcDbArc
-50
--180
-51
-0
-0
-CIRCLE
-5
-121
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-5.4500000000000002
-20
-62.685000000000002
-30
-0
-40
-0.59999999999999998
-0
-LINE
-5
-122
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-0
-20
-60
-30
-0
-11
-1.9000000000000004
-21
-60
-31
-0
-0
-LINE
-5
-123
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-14.150000000000002
-20
-63.335000000000001
-30
-0
-11
-14.150000000000002
-21
-62.034999999999997
-31
-0
-0
-LINE
-5
-124
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-1.25
-20
-62.034999999999997
-30
-0
-11
-1.25
-21
-63.335000000000001
-31
-0
-0
-LINE
-5
-125
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-12.949999999999999
-20
-62.034999999999997
-30
-0
-11
-12.950000000000001
-21
-63.335000000000001
-31
-0
-0
-LINE
-5
-126
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-2.4500000000000002
-20
-63.335000000000001
-30
-0
-11
-2.4500000000000002
-21
-62.034999999999997
-31
-0
-0
-LWPOLYLINE
-5
-127
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-6
-70
-1
-43
-0.0
-10
-18.5
-20
-87
-10
-16.899999999999999
-20
-87
-42
--0.41421356237308982
-10
-15.400000000000006
-20
-88.499999999999972
-10
-15.4
-20
-100
-10
-20
-20
-100
-10
-19.999999999999996
-20
-88.499999999999972
-42
--0.41421356237308982
-0
-LWPOLYLINE
-5
-128
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-4
-70
-1
-43
-0.0
-10
-54.150000000000006
-20
-62.034999999999997
-42
--0.99999999999999989
-10
-52.950000000000003
-20
-62.034999999999997
-10
-52.95000000000001
-20
-63.335000000000001
-42
--0.99999999999999989
-10
-54.149999999999999
-20
-63.335000000000001
-0
-CIRCLE
-5
-129
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-49.950000000000003
-20
-62.685000000000002
-30
-0
-40
-0.59999999999999998
-0
-ARC
-5
-130
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-53.550000000000004
-20
-62.034999999999997
-30
-0
-40
-0.60000000000000497
-100
-AcDbArc
-50
--180
-51
-0
-0
-LINE
-5
-131
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-72.874812992585703
-20
-48.312219961316394
-30
-0
-11
-72.649070361618698
-21
-49.592470040232264
-31
-0
-0
-ARC
-5
-132
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-53.550000000000004
-20
-63.335000000000001
-30
-0
-40
-0.59999999999999609
-100
-AcDbArc
-50
-0
-51
-180
-0
-LINE
-5
-133
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-85.353090375476171
-20
-51.832531532135668
-30
-0
-11
-85.578833006443176
-21
-50.552281453219805
-31
-0
-0
-CIRCLE
-5
-134
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-45.450000000000003
-20
-62.685000000000002
-30
-0
-40
-0.59999999999999998
-0
-LINE
-5
-135
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-73.830839665233341
-20
-49.800847853432586
-30
-0
-11
-74.056582296200347
-21
-48.520597774516723
-31
-0
-0
-LINE
-5
-136
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-84.397063702828561
-20
-50.343903640019477
-30
-0
-11
-84.171321071861513
-21
-51.624153718935347
-31
-0
-0
-LINE
-5
-137
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-52.950000000000003
-20
-62.034999999999997
-30
-0
-11
-52.950000000000003
-21
-63.335000000000001
-31
-0
-0
-LWPOLYLINE
-5
-138
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-6
-70
-1
-43
-0.0
-10
-78.000000000000028
-20
-88.499999999999972
-10
-78.000000000000028
-20
-100
-10
-82.220000000000027
-20
-100
-10
-82.220000000000027
-20
-88.5
-42
--0.4142135623730881
-10
-80.720000000000027
-20
-87
-10
-79.500000000000028
-20
-87
-42
--0.41421356237308982
-0
-ARC
-5
-139
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-84.76220572366887
-20
-51.728342625535511
-30
-0
-40
-0.59999999999996168
-100
-AcDbArc
-50
-10.000000000000307
-51
-190.00000000000006
-0
-ARC
-5
-140
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-73.239955013426027
-20
-49.696658946832429
-30
-0
-40
-0.59999999999999665
-100
-AcDbArc
-50
-10.000000000000087
-51
-190.00000000000051
-0
-CIRCLE
-5
-141
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-81.329769128308413
-20
-50.463084146476625
-30
-0
-40
-0.59999999999999987
-0
-CIRCLE
-5
-142
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-76.898134239753475
-20
-49.681667346975445
-30
-0
-40
-0.59999999999999987
-0
-ARC
-5
-143
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-84.987948354635876
-20
-50.448092546619641
-30
-0
-40
-0.5999999999999982
-100
-AcDbArc
-50
--169.99999999999923
-51
-10.000000000000965
-0
-ARC
-5
-144
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-73.465697644393032
-20
-48.416408867916559
-30
-0
-40
-0.5999999999999982
-100
-AcDbArc
-50
--169.99999999999946
-51
-10.000000000000746
-0
-LINE
-5
-145
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-54.149999999999999
-20
-63.335000000000001
-30
-0
-11
-54.149999999999999
-21
-62.034999999999997
-31
-0
-0
-ARC
-5
-146
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-1.8999999999999884
-20
-58.500000000000014
-30
-0
-40
-1.5000000000000124
-100
-AcDbArc
-50
-270.00000000000045
-51
-360
-0
-CIRCLE
-5
-147
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-3.5000000000000004
-20
-39.300000000000004
-30
-0
-40
-1.375
-0
-CIRCLE
-5
-148
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-3.5000000000000004
-20
-16.800000000000001
-30
-0
-40
-1.375
-0
-LWPOLYLINE
-5
-149
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-4
-70
-1
-43
-0.0
-10
-32.95000000000001
-20
-63.335000000000001
-42
--0.99999999999999989
-10
-34.149999999999999
-20
-63.335000000000001
-10
-34.150000000000006
-20
-62.034999999999997
-42
--0.99999999999999989
-10
-32.950000000000003
-20
-62.034999999999997
-0
-LWPOLYLINE
-5
-150
-100
-AcDbEntity
-8
-0
-100
-AcDbPolyline
-90
-4
-70
-1
-43
-0.0
-10
-41.250000000000007
-20
-63.335000000000001
-42
--0.99999999999999989
-10
-42.450000000000003
-20
-63.335000000000001
-10
-42.45000000000001
-20
-62.034999999999997
-42
--0.99999999999999989
-10
-41.25
-20
-62.034999999999997
-0
-ARC
-5
-151
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-41.850000000000009
-20
-63.335000000000001
-30
-0
-40
-0.59999999999999609
-100
-AcDbArc
-50
-0
-51
-180
-0
-ARC
-5
-152
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-41.850000000000009
-20
-62.034999999999997
-30
-0
-40
-0.60000000000000497
-100
-AcDbArc
-50
--180
-51
-0
-0
-ARC
-5
-153
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-33.550000000000004
-20
-63.335000000000001
-30
-0
-40
-0.59999999999999609
-100
-AcDbArc
-50
-0
-51
-180
-0
-ARC
-5
-154
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-21.850000000000001
-20
-62.034999999999997
-30
-0
-40
-0.60000000000000053
-100
-AcDbArc
-50
--180
-51
-0
-0
-CIRCLE
-5
-155
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-25.449999999999999
-20
-62.685000000000002
-30
-0
-40
-0.59999999999999998
-0
-ARC
-5
-156
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-33.550000000000004
-20
-62.034999999999997
-30
-0
-40
-0.60000000000000497
-100
-AcDbArc
-50
--180
-51
-0
-0
-LINE
-5
-157
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-22.450000000000003
-20
-63.335000000000001
-30
-0
-11
-22.450000000000003
-21
-62.034999999999997
-31
-0
-0
-LINE
-5
-158
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-34.149999999999999
-20
-63.335000000000001
-30
-0
-11
-34.149999999999999
-21
-62.034999999999997
-31
-0
-0
-LINE
-5
-159
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-32.950000000000003
-20
-62.034999999999997
-30
-0
-11
-32.950000000000003
-21
-63.335000000000001
-31
-0
-0
-LINE
-5
-160
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-21.25
-20
-62.034999999999997
-30
-0
-11
-21.25
-21
-63.335000000000001
-31
-0
-0
-LINE
-5
-161
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-42.450000000000003
-20
-63.335000000000001
-30
-0
-11
-42.450000000000003
-21
-62.034999999999997
-31
-0
-0
-LINE
-5
-162
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-41.25
-20
-62.034999999999997
-30
-0
-11
-41.25
-21
-63.335000000000001
-31
-0
-0
-ARC
-5
-163
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-66.500000000000014
-20
-3.5002504107472014
-30
-0
-40
-1.500000000000012
-100
-AcDbArc
-50
-90.00000000000054
-51
-180
-0
-ARC
-5
-164
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-62
-20
-3.0000000000000004
-30
-0
-40
-3.0000000000000004
-100
-AcDbArc
-50
--90
-51
-0
-0
-CIRCLE
-5
-165
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-61.5
-20
-39.300000000000004
-30
-0
-40
-1.375
-0
-LINE
-5
-166
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-66.5
-20
-5.0002504107472134
-30
-0
-11
-66.5
-21
-5.0002504107472134
-31
-0
-0
-CIRCLE
-5
-167
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-61.5
-20
-16.800000000000001
-30
-0
-40
-1.375
-0
-LINE
-5
-168
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-68
-20
-3.5002504107472134
-30
-0
-11
-68
-21
-4.3709567898628133e-16
-31
-0
-0
-LINE
-5
-169
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-65
-20
-3.5002504107472134
-30
-0
-11
-65
-21
-3.0000000000000004
-31
-0
-0
-LINE
-5
-170
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-68
-20
-4.3709567898628133e-16
-30
-0
-11
-62
-21
-0
-31
-0
-0
-LINE
-5
-171
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-3.0000000000000004
-20
-0
-30
-0
-11
-0
-21
-0
-31
-0
-0
-LINE
-5
-172
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-0
-20
-0
-30
-0
-11
-0
-21
-3.0000000000000009
-31
-0
-0
-LINE
-5
-173
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-0
-20
-60
-30
-0
-11
-0
-21
-57
-31
-0
-0
-ARC
-5
-174
-100
-AcDbEntity
-8
-0
-100
-AcDbCircle
-10
-66.5
-20
-3.5002504107472014
-30
-0
-40
-1.5000000000000124
-100
-AcDbArc
-50
-0
-51
-90
-0
-ENDSEC
-0
-SECTION
-2
-OBJECTS
-0
-DICTIONARY
-5
-C
-100
-AcDbDictionary
-3
-ACAD_GROUP
-350
-D
-3
-ACAD_MLINESTYLE
-350
-17
-0
-DICTIONARY
-5
-D
-100
-AcDbDictionary
-0
-DICTIONARY
-5
-1A
-330
-C
-100
-AcDbDictionary
-0
-DICTIONARY
-5
-17
-100
-AcDbDictionary
-0
-ENDSEC
-0
-EOF
diff --git a/examples/panelize.py b/examples/panelize.py
index 34cc446..20a2cac 100755
--- a/examples/panelize.py
+++ b/examples/panelize.py
@@ -18,7 +18,6 @@ boards=[
]
outline = 'inputs/outline.dxf'
mousebites = 'inputs/mousebites.dxf'
-fill = 'inputs/fill.dxf'
outputs = 'outputs/panelized'
os.chdir(os.path.dirname(__file__))
@@ -62,9 +61,11 @@ file = gerberex.read(outline)
file.write(outputs + '.GML')
putstr('.')
ctx = GerberComposition()
-file = gerberex.read(fill)
-file.to_metric()
+base = gerberex.rectangle(width=100, height=100, left=0, bottom=0, units='metric')
+base.draw_mode = DxfFile.DM_FILL
+ctx.merge(base)
file.draw_mode = DxfFile.DM_FILL
+file.negate_polarity()
ctx.merge(file)
ctx.dump(outputs + '-fill.GML')
diff --git a/gerberex/dxf.py b/gerberex/dxf.py
index 00b7695..95a3114 100644
--- a/gerberex/dxf.py
+++ b/gerberex/dxf.py
@@ -12,12 +12,88 @@ from gerber.gerber_statements import ADParamStmt
from gerber.excellon_statements import ExcellonTool
from gerber.excellon_statements import CoordinateStmt
from gerberex.utility import is_equal_point, is_equal_value
-from gerberex.dxf_path import generate_paths
+from gerberex.dxf_path import generate_paths, judge_containment
from gerberex.excellon import write_excellon_header
from gerberex.rs274x import write_gerber_header
ACCEPTABLE_ERROR = 0.001
+def _normalize_angle(start_angle, end_angle):
+ angle = end_angle - start_angle
+ if angle > 0:
+ start = start_angle % 360
+ else:
+ angle = -angle
+ start = end_angle % 360
+ angle = min(angle, 360)
+ start = start - 360 if start > 180 else start
+
+ regions = []
+ while angle > 0:
+ end = start + angle
+ if end <= 180:
+ regions.append((start * pi / 180, end * pi / 180))
+ angle = 0
+ else:
+ regions.append((start * pi / 180, pi))
+ angle = end - 180
+ start = -180
+ return regions
+
+def _intersections_of_line_and_circle(start, end, center, radius, error_range):
+ x1 = start[0] - center[0]
+ y1 = start[1] - center[1]
+ x2 = end[0] - center[0]
+ y2 = end[1] - center[1]
+
+ dx = x2 - x1
+ dy = y2 - y1
+ dr = sqrt(dx * dx + dy * dy)
+ D = x1 * y2 - x2 * y1
+
+ D2 = D * D
+ dr2 = dr * dr
+ r2 = radius * radius
+ delta = r2 * dr2 - D2
+ e4 = error_range * error_range * error_range * error_range * 10
+ if delta > - e4 and delta < e4:
+ delta = 0
+ if delta < 0:
+ return None
+
+ sqrt_D = sqrt(delta)
+ E_x = -dx * sqrt_D if dy < 0 else dx * sqrt_D
+ E_y = abs(dy) * sqrt_D
+
+ p1_x = (D * dy + E_x) / dr2
+ p2_x = (D * dy - E_x) / dr2
+ p1_y = (-D * dx + E_y) / dr2
+ p2_y = (-D * dx - E_y) / dr2
+
+ p1_angle = atan2(p1_y, p1_x)
+ p2_angle = atan2(p2_y, p2_x)
+ if dx == 0:
+ p1_t = (p1_y - y1) / dy
+ p2_t = (p2_y - y1) / dy
+ else:
+ p1_t = (p1_x - x1) / dx
+ p2_t = (p2_x - x1) / dx
+
+ if delta == 0:
+ return (
+ (p1_x + center[0], p1_y + center[1]),
+ None,
+ p1_angle, None,
+ p1_t, None
+ )
+ else:
+ return (
+ (p1_x + center[0], p1_y + center[1]),
+ (p2_x + center[0], p2_y + center[1]),
+ p1_angle, p2_angle,
+ p1_t, p2_t
+ )
+
class DxfStatement(object):
def __init__(self, entity):
self.entity = entity
@@ -51,6 +127,13 @@ class DxfLineStatement(DxfStatement):
end = (entity.end[0], entity.end[1])
return cls(entity, start, end)
+ @property
+ def bounding_box(self):
+ return (min(self.start[0], self.end[0]),
+ min(self.start[1], self.end[1]),
+ max(self.start[0], self.end[0]),
+ max(self.start[1], self.end[1]))
+
def __init__(self, entity, start, end):
super(DxfLineStatement, self).__init__(entity)
self.start = start
@@ -110,6 +193,53 @@ class DxfLineStatement(DxfStatement):
def rotate(self, angle, center=(0, 0)):
self.start = rotate_point(self.start, angle, center)
self.end = rotate_point(self.end, angle, center)
+
+ def intersections_with_halfline(self, point_from, point_to, error_range):
+ denominator = (self.end[0] - self.start[0]) * (point_to[1] - point_from[1]) - \
+ (self.end[1] - self.start[1]) * (point_to[0] - point_from[0])
+ de = error_range * error_range
+ if denominator > -de and denominator < de:
+ return []
+ from_dx = point_from[0] - self.start[0]
+ from_dy = point_from[1] - self.start[1]
+ r = ((point_to[1] - point_from[1]) * from_dx -
+ (point_to[0] - point_from[0]) * from_dy) / denominator
+ s = ((self.end[1] - self.start[1]) * from_dx -
+ (self.end[0] - self.start[0]) * from_dy) / denominator
+ dx = (self.end[0] - self.start[0])
+ dy = (self.end[1] - self.start[1])
+ le = error_range / sqrt(dx * dx + dy * dy)
+ if s < 0 or r < -le or r > 1 + le:
+ return []
+
+ pt = (self.start[0] + (self.end[0] - self.start[0]) * r,
+ self.start[1] + (self.end[1] - self.start[1]) * r)
+ if is_equal_point(pt, self.start, error_range):
+ return []
+ else:
+ return [pt]
+
+ def intersections_with_arc(self, center, radius, angle_regions, error_range):
+ intersection = \
+ _intersections_of_line_and_circle(self.start, self.end, center, radius, error_range)
+ if intersection is None:
+ return []
+ else:
+ p1, p2, p1_angle, p2_angle, p1_t, p2_t = intersection
+
+ pts = []
+ if p1_t >= 0 and p1_t <= 1:
+ for region in angle_regions:
+ if p1_angle >= region[0] and p1_angle <= region[1]:
+ pts.append(p1)
+ break
+ if p2 is not None and p2_t >= 0 and p2_t <= 1:
+ for region in angle_regions:
+ if p2_angle >= region[0] and p2_angle <= region[1]:
+ pts.append(p2)
+ break
+
+ return pts
class DxfArcStatement(DxfStatement):
def __init__(self, entity):
@@ -139,6 +269,12 @@ class DxfArcStatement(DxfStatement):
self.is_closed = angle >= 360 or angle <= -360
else:
raise Exception('invalid DXF type was specified')
+ self.angle_regions = _normalize_angle(self.start_angle, self.end_angle)
+
+ @property
+ def bounding_box(self):
+ return (self.center[0] - self.radius, self.center[1] - self.radius,
+ self.center[0] + self.radius, self.center[1] + self.radius)
def to_inch(self):
self.radius = inch(self.radius)
@@ -204,6 +340,82 @@ class DxfArcStatement(DxfStatement):
self.center = rotate_point(self.center, angle, center)
self.start = rotate_point(self.start, angle, center)
self.end = rotate_point(self.end, angle, center)
+ self.angle_regions = _normalize_angle(self.start_angle, self.end_angle)
+
+ def intersections_with_halfline(self, point_from, point_to, error_range):
+ intersection = \
+ _intersections_of_line_and_circle(
+ point_from, point_to, self.center, self.radius, error_range)
+ if intersection is None:
+ return []
+ else:
+ p1, p2, p1_angle, p2_angle, p1_t, p2_t = intersection
+
+ if is_equal_point(p1, self.start, error_range):
+ p1 = None
+ elif p2 is not None and is_equal_point(p2, self.start, error_range):
+ p2 = None
+
+ aerror = error_range * self.radius
+ pts = []
+ if p1 is not None and p1_t >= 0 and not is_equal_point(p1, self.start, error_range):
+ for region in self.angle_regions:
+ if p1_angle >= region[0] - aerror and p1_angle <= region[1] + aerror:
+ pts.append(p1)
+ break
+ if p2 is not None and p2_t >= 0 and not is_equal_point(p2, self.start, error_range):
+ for region in self.angle_regions:
+ if p2_angle >= region[0] - aerror and p2_angle <= region[1] + aerror:
+ pts.append(p2)
+ break
+
+ return pts
+
+ def intersections_with_arc(self, center, radius, angle_regions, error_range):
+ x1 = center[0] - self.center[0]
+ y1 = center[1] - self.center[1]
+ r1 = self.radius
+ r2 = radius
+ cd_sq = x1 * x1 + y1 * y1
+ cd = sqrt(cd_sq)
+ rd = abs(r1 - r2)
+
+ if (cd >= 0 and cd <= rd) or cd >= r1 + r2:
+ return []
+
+ A = (cd_sq + r1 * r1 - r2 * r2) / 2
+ scale = sqrt(cd_sq * r1 * r1 - A * A) / cd_sq
+ xl = A * x1 / cd_sq
+ xr = y1 * scale
+ yl = A * y1 / cd_sq
+ yr = x1 * scale
+
+ pt1_x = xl + xr
+ pt1_y = yl - yr
+ pt2_x = xl - xr
+ pt2_y = yl + yr
+ pt1_angle1 = atan2(pt1_y, pt1_x)
+ pt1_angle2 = atan2(pt1_y - y1, pt1_x - x1)
+ pt2_angle1 = atan2(pt2_y, pt2_x)
+ pt2_angle2 = atan2(pt2_y - y1, pt2_x - x1)
+
+ aerror = error_range * self.radius
+ pts=[]
+ for region in self.angle_regions:
+ if pt1_angle1 >= region[0] and pt1_angle1 <= region[1]:
+ for region in angle_regions:
+ if pt1_angle2 >= region[0] - aerror and pt1_angle2 <= region[1] + aerror:
+ pts.append((pt1_x + self.center[0], pt1_y + self.center[1]))
+ break
+ break
+ for region in self.angle_regions:
+ if pt2_angle1 >= region[0] and pt2_angle1 <= region[1]:
+ for region in angle_regions:
+ if pt2_angle2 >= region[0] - aerror and pt2_angle2 <= region[1] + aerror:
+ pts.append((pt2_x + self.center[0], pt2_y + self.center[1]))
+ break
+ break
+ return pts
class DxfPolylineStatement(DxfStatement):
def __init__(self, entity):
@@ -293,31 +505,69 @@ class DxfPolylineStatement(DxfStatement):
self.entity.points[idx] = rotate_point(self.entity.points[idx], angle, center)
class DxfStatements(object):
- def __init__(self, statements, units, dcode=10, draw_mode=None):
- if draw_mode == None:
+ def __init__(self, statements, units, dcode=10, draw_mode=None, fill_mode=None):
+ if draw_mode is None:
draw_mode = DxfFile.DM_LINE
+ if fill_mode is None:
+ fill_mode = DxfFile.FM_TURN_OVER
self._units = units
self.dcode = dcode
self.draw_mode = draw_mode
+ self.fill_mode = fill_mode
self.pitch = inch(1) if self._units == 'inch' else 1
self.width = 0
self.error_range = inch(ACCEPTABLE_ERROR) if self._units == 'inch' else ACCEPTABLE_ERROR
- self.statements = statements
+ self.statements = list(filter(
+ lambda i: not (isinstance(i, DxfLineStatement) and \
+ is_equal_point(i.start, i.end, self.error_range)),
+ statements
+ ))
self.close_paths, self.open_paths = generate_paths(self.statements, self.error_range)
+ self.sorted_close_paths = []
+ self.polarity = True # True means dark, False means clear
@property
def units(self):
return _units
+ def _polarity_command(self, polarity=None):
+ if polarity is None:
+ polarity = self.polarity
+ return '%LPD*%' if polarity else '%LPC*%'
+
+ def _prepare_sorted_close_paths(self):
+ if self.sorted_close_paths:
+ return
+ for i in range(0, len(self.close_paths)):
+ for j in range(i + 1, len(self.close_paths)):
+ containee, container = judge_containment(
+ self.close_paths[i], self.close_paths[j], self.error_range)
+ if containee is not None:
+ containee.containers.append(container)
+ self.sorted_close_paths = sorted(self.close_paths, key=lambda path: len(path.containers))
+
def to_gerber(self, settings=FileSettings()):
def gerbers():
yield 'G75*'
- yield '%LPD*%'
+ yield self._polarity_command()
yield 'D{0}*'.format(self.dcode)
if self.draw_mode == DxfFile.DM_FILL:
yield 'G36*'
- for path in self.close_paths:
- yield path.to_gerber(settings)
+ if self.fill_mode == DxfFile.FM_TURN_OVER:
+ self._prepare_sorted_close_paths()
+ polarity = self.polarity
+ level = 0
+ for path in self.sorted_close_paths:
+ if len(path.containers) > level:
+ level = len(path.containers)
+ polarity = not polarity
+ yield 'G37*'
+ yield self._polarity_command(polarity)
+ yield 'G36*'
+ yield path.to_gerber(settings)
+ else:
+ for path in self.close_paths:
+ yield path.to_gerber(settings)
yield 'G37*'
else:
pitch = self.pitch if self.draw_mode == DxfFile.DM_MOUSE_BITES else 0
@@ -378,6 +628,9 @@ class DxfFile(CamFile):
DM_FILL = 1
DM_MOUSE_BITES = 2
+ FM_SIMPLE = 0
+ FM_TURN_OVER = 1
+
FT_RX274X = 0
FT_EXCELLON = 1
@@ -430,6 +683,7 @@ class DxfFile(CamFile):
super(DxfFile, self).__init__(settings=settings, filename=filename)
self._draw_mode = draw_mode
+ self._fill_mode = self.FM_TURN_OVER
self.aperture = ADParamStmt.circle(dcode=10, diameter=0.0)
if settings.units == 'inch':
@@ -437,7 +691,7 @@ class DxfFile(CamFile):
else:
self.aperture.to_metric()
self.statements = DxfStatements(
- statements, self.units, dcode=self.aperture.d, draw_mode=self.draw_mode)
+ statements, self.units, dcode=self.aperture.d, draw_mode=self.draw_mode, fill_mode=self.filename)
@property
def dcode(self):
@@ -467,6 +721,15 @@ class DxfFile(CamFile):
self.statements.draw_mode = value
@property
+ def fill_mode(self):
+ return self._fill_mode
+
+ @fill_mode.setter
+ def fill_mode(self, value):
+ self._fill_mode = value
+ self.statements.fill_mode = value
+
+ @property
def pitch(self):
return self.statements.pitch
@@ -512,6 +775,9 @@ class DxfFile(CamFile):
def rotate(self, angle, center=(0, 0)):
self.statements.rotate(angle, center)
+ def negate_polarity(self):
+ self.statements.polarity = not self.statements.polarity
+
def loads(data, filename=None):
if sys.version_info.major == 2:
data = unicode(data)
diff --git a/gerberex/dxf_path.py b/gerberex/dxf_path.py
index 1307411..bb620ff 100644
--- a/gerberex/dxf_path.py
+++ b/gerberex/dxf_path.py
@@ -5,13 +5,17 @@
from gerber.utils import inch, metric, write_gerber_value
from gerber.cam import FileSettings
-from gerberex.utility import is_equal_point, is_equal_value
+from gerberex.utility import is_equal_point, is_equal_value, normalize_vec2d, dot_vec2d
from gerberex.excellon import CoordinateStmtEx
class DxfPath(object):
def __init__(self, statements, error_range=0):
self.statements = statements
self.error_range = error_range
+ self.bounding_box = statements[0].bounding_box
+ self.containers = []
+ for statement in statements[1:]:
+ self._merge_bounding_box(statement.bounding_box)
@property
def start(self):
@@ -116,12 +120,15 @@ class DxfPath(object):
if j > 0:
del mergee[-j]
del self.statements[0:j]
+ for statement in mergee:
+ self._merge_bounding_box(statement.bounding_box)
self.statements.extend(mergee)
return True
else:
if self.statements[-1].is_equal_to(element, error_range) or \
self.statements[0].is_equal_to(element, error_range):
return False
+ self._merge_bounding_box(element.bounding_box)
self.statements.appen(element)
return True
@@ -153,6 +160,21 @@ class DxfPath(object):
self.statements.insert(0, element)
return True
+ def _merge_bounding_box(self, box):
+ self.bounding_box = (min(self.bounding_box[0], box[0]),
+ min(self.bounding_box[1], box[1]),
+ max(self.bounding_box[2], box[2]),
+ max(self.bounding_box[3], box[3]))
+
+ def may_be_in_collision(self, path):
+ if self.bounding_box[0] >= path.bounding_box[2] or \
+ self.bounding_box[1] >= path.bounding_box[3] or \
+ self.bounding_box[2] <= path.bounding_box[0] or \
+ self.bounding_box[3] <= path.bounding_box[1]:
+ return False
+ else:
+ return True
+
def to_gerber(self, settings=FileSettings(), pitch=0, width=0):
from gerberex.dxf import DxfArcStatement
if pitch == 0:
@@ -244,7 +266,61 @@ class DxfPath(object):
out += ploter(dot[0], dot[1])
return out
+ def intersections_with_halfline(self, point_from, point_to, error_range=0):
+ def calculator(statement):
+ return statement.intersections_with_halfline(point_from, point_to, error_range)
+ def validator(pt, statement, idx):
+ if is_equal_point(pt, statement.end, error_range) and \
+ not self._judge_cross(point_from, point_to, idx, error_range):
+ return False
+ return True
+ return self._collect_intersections(calculator, validator, error_range)
+
+ def intersections_with_arc(self, center, radius, angle_regions, error_range=0):
+ def calculator(statement):
+ return statement.intersections_with_arc(center, radius, angle_regions, error_range)
+ return self._collect_intersections(calculator, None, error_range)
+ def _collect_intersections(self, calculator, validator, error_range):
+ allpts = []
+ last = allpts
+ for i in range(0, len(self.statements)):
+ statement = self.statements[i]
+ cur = calculator(statement)
+ if cur:
+ for pt in cur:
+ for dest in allpts:
+ if is_equal_point(pt, dest, error_range):
+ break
+ else:
+ if validator is not None and not validator(pt, statement, i):
+ continue
+ allpts.append(pt)
+ last = cur
+ return allpts
+
+ def _judge_cross(self, from_pt, to_pt, index, error_range):
+ standard = normalize_vec2d((to_pt[0] - from_pt[0], to_pt[1] - from_pt[1]))
+ normal = (standard[1], standard[0])
+ def statements():
+ for i in range(index, len(self.statements)):
+ yield self.statements[i]
+ for i in range(0, index):
+ yield self.statements[i]
+ dot_standard = None
+ for statement in statements():
+ tstart = statement.start
+ tend = statement.end
+ target = normalize_vec2d((tend[0] - tstart[0], tend[1] - tstart[1]))
+ dot= dot_vec2d(normal, target)
+ if dot_standard is None:
+ dot_standard = dot
+ continue
+ if is_equal_point(standard, target, error_range):
+ continue
+ return (dot_standard > 0 and dot > 0) or (dot_standard < 0 and dot < 0)
+ raise Exception('inconsistensy is detected while cross judgement between paths')
+
def generate_paths(statements, error_range=0):
from gerberex.dxf import DxfPolylineStatement
@@ -287,3 +363,50 @@ def generate_paths(statements, error_range=0):
closed_path = list(filter(lambda p: p.is_closed, paths))
open_path = list(filter(lambda p: not p.is_closed, paths))
return (closed_path, open_path)
+
+def judge_containment(path1, path2, error_range=0):
+ from gerberex.dxf import DxfArcStatement, DxfLineStatement
+
+ nocontainment = (None, None)
+ if not path1.may_be_in_collision(path2):
+ return nocontainment
+
+ def is_in_line_segment(point_from, point_to, point):
+ dx = point_to[0] - point_from[0]
+ ratio = (point[0] - point_from[0]) / dx if dx != 0 else \
+ (point[1] - point_from[1]) / (point_to[1] - point_from[1])
+ return ratio >= 0 and ratio <= 1
+
+ def contain_in_path(statement, path):
+ if isinstance(statement, DxfLineStatement):
+ segment = (statement.start, statement.end)
+ elif isinstance(statement, DxfArcStatement):
+ if statement.start == statement.end:
+ segment = (statement.start, statement.center)
+ else:
+ segment = (statement.start, statement.end)
+ else:
+ raise Exception('invalid dxf statement type')
+ pts = path.intersections_with_halfline(segment[0], segment[1], error_range)
+ if len(pts) % 2 == 0:
+ return False
+ for pt in pts:
+ if is_in_line_segment(segment[0], segment[1], pt):
+ return False
+ if isinstance(statement, DxfArcStatement):
+ pts = path.intersections_with_arc(
+ statement.center, statement.radius, statement.angle_regions, error_range)
+ if len(pts) > 0:
+ return False
+ return True
+
+ if contain_in_path(path1.statements[0], path2):
+ containment = [path1, path2]
+ elif contain_in_path(path2.statements[0], path1):
+ containment = [path2, path1]
+ else:
+ return nocontainment
+ for i in range(1, len(containment[0].statements)):
+ if not contain_in_path(containment[0].statements[i], containment[1]):
+ return nocontainment
+ return containment
diff --git a/gerberex/utility.py b/gerberex/utility.py
index 4c89fa6..37de5e8 100644
--- a/gerberex/utility.py
+++ b/gerberex/utility.py
@@ -3,7 +3,7 @@
# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
-from math import cos, sin, pi
+from math import cos, sin, pi, sqrt
def rotate(x, y, angle, center):
x0 = x - center[0]
@@ -18,3 +18,10 @@ def is_equal_value(a, b, error_range=0):
def is_equal_point(a, b, error_range=0):
return is_equal_value(a[0], b[0], error_range) and \
is_equal_value(a[1], b[1], error_range)
+
+def normalize_vec2d(vec):
+ length = sqrt(vec[0] * vec[0] + vec[1] * vec[1])
+ return (vec[0] / length, vec[1] / length)
+
+def dot_vec2d(vec1, vec2):
+ return vec1[0] * vec2[0] + vec1[1] * vec2[1] \ No newline at end of file
diff --git a/setup.py b/setup.py
index 6f144cb..bfe25c1 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ def read(fname):
METADATA = {
'name': 'pcb-tools-extension',
- 'version': "0.9.1",
+ 'version': "0.9.2",
'author': 'Hiroshi Murayama <opiopan@gmail.com>',
'author_email': "opiopan@gmail.com",
'description': ("Extension for pcb-tools package to panelize gerber files"),
diff --git a/tests/data/ref_dxf_metric.dxf b/tests/data/ref_dxf_metric.dxf
index 89af1e7..8861d4b 100644
--- a/tests/data/ref_dxf_metric.dxf
+++ b/tests/data/ref_dxf_metric.dxf
@@ -271,49 +271,105 @@ AcDbPolyline
43
0.0
10
-9
+40
20
0
10
-1
+9.9999999999999982
20
-0
+1.1102230246251565e-15
42
--0.41421356237309515
+-0.41421356237309565
10
-0
+1.1102230246251565e-15
+20
+10
+10
+1.1102230246251565e-15
20
-0.99999999999999978
+40
+42
+-0.41421356237309553
+10
10
-6.9388939039072284e-16
20
-9
+50
+10
+40
+20
+50
+42
+-0.41421356237309603
+10
+50
+20
+40
+10
+50
+20
+9.9999999999999982
42
-0.41421356237309548
+0
+LWPOLYLINE
+5
+101
+100
+AcDbEntity
+8
+0
+100
+AcDbPolyline
+90
+8
+70
+1
+43
+0.0
10
-0.99999999999999978
+74.270404858697972
20
+75.039392445007664
10
+74.270404858697972
+20
+45.039392445007664
+42
+-0.41421356237309553
10
-9
+64.270404858697972
20
+35.039392445007664
10
+34.270404858697965
+20
+35.039392445007664
42
--0.41421356237309509
+-0.41421356237309553
10
+24.270404858697965
+20
+45.039392445007664
10
+24.270404858697958
20
-9
+75.039392445007664
+42
+-0.41421356237309553
10
+34.270404858697965
+20
+85.039392445007678
10
+64.270404858697972
20
-1
+85.039392445007678
42
--0.41421356237309548
+-0.41421356237309603
0
CIRCLE
5
-101
+102
100
AcDbEntity
8
@@ -321,17 +377,121 @@ AcDbEntity
100
AcDbCircle
10
-0.61705708382705282
+80.154604804025013
20
+12.631646043929035
+30
+0
+40
+12.546259950593821
+210
+0
+220
+0
+230
+1
+0
+CIRCLE
5
+103
+100
+AcDbEntity
+8
+0
+100
+AcDbCircle
+10
+50
+20
+65
30
0
40
-0.29999999999999999
+10.277260744660863
+210
+0
+220
+0
+230
+1
+0
+ARC
+5
+104
+100
+AcDbEntity
+8
+0
+100
+AcDbCircle
+10
+50
+20
+65
+30
+0
+40
+6.4592498736207826
+210
+0
+220
+-0
+230
+1
+100
+AcDbArc
+50
+90
+51
+345.73898023603914
+0
+LINE
+5
+105
+100
+AcDbEntity
+8
+0
+100
+AcDbLine
+10
+50
+20
+65
+30
+0
+11
+56.26019871530297
+21
+63.408830312383643
+31
+0
+0
+LINE
+5
+106
+100
+AcDbEntity
+8
+0
+100
+AcDbLine
+10
+50
+20
+65
+30
+0
+11
+50
+21
+71.459249873620777
+31
+0
0
LWPOLYLINE
5
-102
+107
100
AcDbEntity
8
@@ -339,25 +499,115 @@ AcDbEntity
100
AcDbPolyline
90
-3
+4
70
1
43
0.0
10
-0.91705708382705309
+38.203772564818578
20
-7.5106817728417301
-42
--0.67748879940688445
+42.206179116779516
+10
+13.451174232106741
+20
+42.206179116779516
+10
+13.451174232106741
+20
+13.596032991956758
+10
+38.203772564818578
+20
+13.596032991956758
+0
+LINE
+5
+108
+100
+AcDbEntity
+8
+0
+100
+AcDbLine
+10
+80
+20
+85
+30
+0
+11
+80
+21
+35
+31
+0
+0
+LINE
+5
+109
+100
+AcDbEntity
+8
+0
+100
+AcDbLine
10
-0.39955725374872897
+90
20
-7.3040569342673214
+85
+30
+0
+11
+90
+21
+35
+31
+0
+0
+LINE
+5
+110
+100
+AcDbEntity
+8
+0
+100
+AcDbLine
10
-0.61705708382705282
20
-7.5106817728417301
+20
+32.938987371135568
+30
+0
+11
+32.582672402541192
+21
+32.938987371135568
+31
+0
+0
+LINE
+5
+111
+100
+AcDbEntity
+8
+0
+100
+AcDbLine
+10
+32.582672402541192
+20
+32.938987371135568
+30
+0
+11
+32.582672402541192
+21
+17.844276543429842
+31
+0
0
ENDSEC
0
diff --git a/tests/data/ref_dxf_mousebites.dxf b/tests/data/ref_dxf_mousebites.dxf
deleted file mode 100644
index fcc56a5..0000000
--- a/tests/data/ref_dxf_mousebites.dxf
+++ /dev/null
@@ -1,344 +0,0 @@
-0
-SECTION
-2
-HEADER
-9
-$INSUNITS
-70
-4
-9
-$ACADVER
-1
-AC1014
-9
-$HANDSEED
-5
-FFFF
-0
-ENDSEC
-0
-SECTION
-2
-TABLES
-0
-TABLE
-2
-VPORT
-5
-8
-100
-AcDbSymbolTable
-0
-ENDTAB
-0
-TABLE
-2
-LTYPE
-5
-5
-100
-AcDbSymbolTable
-0
-LTYPE
-5
-14
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
-2
-BYBLOCK
-70
-0
-0
-LTYPE
-5
-15
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
-2
-BYLAYER
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-LAYER
-5
-2
-100
-AcDbSymbolTable
-70
-2
-0
-LAYER
-5
-50
-100
-AcDbSymbolTableRecord
-100
-AcDbLayerTableRecord
-2
-0
-70
-0
-6
-CONTINUOUS
-0
-ENDTAB
-0
-TABLE
-2
-STYLE
-5
-3
-100
-AcDbSymbolTable
-70
-1
-0
-STYLE
-5
-11
-100
-AcDbSymbolTableRecord
-100
-AcDbTextStyleTableRecord
-2
-STANDARD
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-VIEW
-5
-6
-100
-AcDbSymbolTable
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-UCS
-5
-7
-100
-AcDbSymbolTable
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-APPID
-5
-9
-100
-AcDbSymbolTable
-70
-2
-0
-APPID
-5
-12
-100
-AcDbSymbolTableRecord
-100
-AcDbRegAppTableRecord
-2
-ACAD
-70
-0
-0
-ENDTAB
-0
-TABLE
-2
-DIMSTYLE
-5
-A
-100
-AcDbSymbolTable
-70
-1
-0
-ENDTAB
-0
-TABLE
-2
-BLOCK_RECORD
-5
-1
-100
-AcDbSymbolTable
-70
-1
-0
-BLOCK_RECORD
-5
-1F
-100
-AcDbSymbolTableRecord
-100
-AcDbBlockTableRecord
-2
-*MODEL_SPACE
-0
-BLOCK_RECORD
-5
-1B
-100
-AcDbSymbolTableRecord
-100
-AcDbBlockTableRecord
-2
-*PAPER_SPACE
-0
-ENDTAB
-0
-ENDSEC
-0
-SECTION
-2
-BLOCKS
-0
-BLOCK
-5
-20
-100
-AcDbEntity
-100
-AcDbBlockBegin
-2
-*MODEL_SPACE
-0
-ENDBLK
-5
-21
-100
-AcDbEntity
-100
-AcDbBlockEnd
-0
-BLOCK
-5
-1C
-100
-AcDbEntity
-100
-AcDbBlockBegin
-2
-*PAPER_SPACE
-0
-ENDBLK
-5
-1D
-100
-AcDbEntity
-100
-AcDbBlockEnd
-0
-ENDSEC
-0
-SECTION
-2
-ENTITIES
-0
-LINE
-5
-100
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-0.99999999999999933
-20
-9.0000000000000018
-30
-0
-11
-0.99999999999999967
-21
-0.99999999999999967
-31
-0
-0
-LINE
-5
-101
-100
-AcDbEntity
-8
-0
-100
-AcDbLine
-10
-5
-20
-9.0000000000000018
-30
-0
-11
-5
-21
-0.99999999999999967
-31
-0
-0
-ENDSEC
-0
-SECTION
-2
-OBJECTS
-0
-DICTIONARY
-5
-C
-100
-AcDbDictionary
-3
-ACAD_GROUP
-350
-D
-3
-ACAD_MLINESTYLE
-350
-17
-0
-DICTIONARY
-5
-D
-100
-AcDbDictionary
-0
-DICTIONARY
-5
-1A
-330
-C
-100
-AcDbDictionary
-0
-DICTIONARY
-5
-17
-100
-AcDbDictionary
-0
-ENDSEC
-0
-EOF
diff --git a/tests/expects/dxf_offset.gtl b/tests/expects/dxf_offset.gtl
index ff0cdda..df1b015 100644
--- a/tests/expects/dxf_offset.gtl
+++ b/tests/expects/dxf_offset.gtl
@@ -6,36 +6,88 @@ G75*
%LPD*%
D10*
G01*
-X200000Y50000D02*
+X910000Y900000D02*
G75*
G01*
-X120000Y50000D01*
+X910000Y400000D01*
+G01*
+X1010000Y900000D02*
+G75*
+G01*
+X1010000Y400000D01*
+G01*
+X310000Y379390D02*
+G75*
+G01*
+X435827Y379390D01*
+G01*
+X435827Y228443D01*
+G01*
+X510000Y50000D02*
+G75*
+G01*
+X210000Y50000D01*
G02*
-X110000Y60000I0J10000D01*
+X110000Y150000I0J100000D01*
G01*
-X110000Y140000D01*
+X110000Y450000D01*
G02*
-X120000Y150000I10000J0D01*
+X210000Y550000I100000J0D01*
G01*
-X200000Y150000D01*
+X510000Y550000D01*
G02*
-X210000Y140000I0J-10000D01*
+X610000Y450000I0J-100000D01*
G01*
-X210000Y60000D01*
+X610000Y150000D01*
G02*
-X200000Y50000I-10000J0D01*
+X510000Y50000I-100000J0D01*
G01*
-X119171Y125107D02*
+X852704Y800394D02*
G75*
+G01*
+X852704Y500394D01*
G02*
-X113996Y123041I-3000J0D01*
+X752704Y400394I-100000J0D01*
G01*
-X116171Y125107D01*
+X452704Y400394D01*
+G02*
+X352704Y500394I0J100000D01*
G01*
-X119171Y125107D01*
+X352704Y800394D01*
+G02*
+X452704Y900394I100000J0D01*
+G01*
+X752704Y900394D01*
+G02*
+X852704Y800394I0J-100000D01*
G01*
-X119171Y100000D02*
+X492038Y472062D02*
+G75*
+G01*
+X244512Y472062D01*
+G01*
+X244512Y185960D01*
+G01*
+X492038Y185960D01*
+G01*
+X492038Y472062D01*
+G01*
+X1037009Y176316D02*
+G75*
+G03*
+X1037009Y176316I-125463J0D01*
+G01*
+X712773Y700000D02*
G75*
G03*
-X119171Y100000I-3000J0D01*
+X712773Y700000I-102773J0D01*
+G01*
+X610000Y700000D02*
+G75*
+G01*
+X610000Y764592D01*
+G03*
+X672602Y684088I0J-64592D01*
+G01*
+X610000Y700000D01*
M02*
diff --git a/tests/expects/dxf_rotate.gtl b/tests/expects/dxf_rotate.gtl
index c6449ca..5c04b64 100644
--- a/tests/expects/dxf_rotate.gtl
+++ b/tests/expects/dxf_rotate.gtl
@@ -6,36 +6,88 @@ G75*
%LPD*%
D10*
G01*
-X124805Y2611D02*
+X501270Y1044184D02*
G75*
G01*
-X49630Y-24751D01*
+X672280Y574337D01*
+G01*
+X595239Y1078386D02*
+G75*
+G01*
+X766249Y608539D01*
+G01*
+X115513Y349758D02*
+G75*
+G01*
+X233752Y392793D01*
+G01*
+X285379Y250949D01*
+G01*
+X416110Y108637D02*
+G75*
+G01*
+X134202Y6031D01*
G02*
-X36813Y-18774I-3420J9397D01*
+X6031Y65798I-34202J93969D01*
G01*
-X9451Y56401D01*
+X-96575Y347706D01*
G02*
-X15428Y69218I9397J3420D01*
+X-36808Y475877I93969J34202D01*
G01*
-X90603Y96580D01*
+X245100Y578483D01*
G02*
-X103420Y90603I3420J-9397D01*
+X373271Y518716I34202J-93969D01*
G01*
-X130782Y15428D01*
+X475877Y236808D01*
G02*
-X124805Y2611I-9397J-3420D01*
+X416110Y108637I-93969J-34202D01*
G01*
-X23162Y45543D02*
+X481496Y930988D02*
G75*
+G01*
+X584102Y649080D01*
G02*
-X19006Y41831I-2819J-1026D01*
+X524335Y520909I-93969J-34202D01*
G01*
-X20343Y44517D01*
+X242427Y418303D01*
+G02*
+X114256Y478070I-34202J93969D01*
G01*
-X23162Y45543D01*
+X11650Y759978D01*
+G02*
+X71417Y888149I93969J34202D01*
+G01*
+X353325Y990755D01*
+G02*
+X481496Y930988I34202J-93969D01*
G01*
-X31749Y21950D02*
+X254877Y499102D02*
+G75*
+G01*
+X22279Y414443D01*
+G01*
+X120131Y145595D01*
+G01*
+X352730Y230254D01*
+G01*
+X254877Y499102D01*
+G01*
+X868133Y407583D02*
+G75*
+G03*
+X868133Y407583I-117896J-42911D01*
+G01*
+X384341Y788789D02*
G75*
G03*
-X31749Y21950I-2819J-1026D01*
+X384341Y788789I-96575J-35150D01*
+G01*
+X287766Y753639D02*
+G75*
+G01*
+X265674Y814336D01*
+G03*
+X352035Y760098I22092J-60697D01*
+G01*
+X287766Y753639D01*
M02*
diff --git a/tests/expects/dxf_save_fill.gtl b/tests/expects/dxf_save_fill.gtl
index f18c9f7..4b82fb2 100644
--- a/tests/expects/dxf_save_fill.gtl
+++ b/tests/expects/dxf_save_fill.gtl
@@ -7,37 +7,78 @@ G75*
D10*
G36*
G01*
-X90000Y0D02*
+X400000Y0D02*
G75*
G01*
-X10000Y0D01*
+X100000Y0D01*
G02*
-X0Y10000I0J10000D01*
+X0Y100000I0J100000D01*
G01*
-X0Y90000D01*
+X0Y400000D01*
G02*
-X10000Y100000I10000J0D01*
+X100000Y500000I100000J0D01*
G01*
-X90000Y100000D01*
+X400000Y500000D01*
G02*
-X100000Y90000I0J-10000D01*
+X500000Y400000I0J-100000D01*
G01*
-X100000Y10000D01*
+X500000Y100000D01*
G02*
-X90000Y0I-10000J0D01*
+X400000Y0I-100000J0D01*
G01*
-X9171Y75107D02*
+X742704Y750394D02*
G75*
+G01*
+X742704Y450394D01*
+G02*
+X642704Y350394I-100000J0D01*
+G01*
+X342704Y350394D01*
+G02*
+X242704Y450394I0J100000D01*
+G01*
+X242704Y750394D01*
+G02*
+X342704Y850394I100000J0D01*
+G01*
+X642704Y850394D01*
G02*
-X3996Y73041I-3000J0D01*
+X742704Y750394I0J-100000D01*
+G01*
+X927009Y126316D02*
+G75*
+G03*
+X927009Y126316I-125463J0D01*
+G37*
+%LPC*%
+G36*
G01*
-X6171Y75107D01*
+X382038Y422062D02*
+G75*
+G01*
+X134512Y422062D01*
+G01*
+X134512Y135960D01*
G01*
-X9171Y75107D01*
+X382038Y135960D01*
+G01*
+X382038Y422062D01*
+G01*
+X602773Y650000D02*
+G75*
+G03*
+X602773Y650000I-102773J0D01*
+G37*
+%LPD*%
+G36*
G01*
-X9171Y50000D02*
+X500000Y650000D02*
G75*
+G01*
+X500000Y714592D01*
G03*
-X9171Y50000I-3000J0D01*
+X562602Y634088I0J-64592D01*
+G01*
+X500000Y650000D01*
G37*
M02*
diff --git a/tests/expects/dxf_save_fill_simple.gtl b/tests/expects/dxf_save_fill_simple.gtl
new file mode 100644
index 0000000..5ff4d55
--- /dev/null
+++ b/tests/expects/dxf_save_fill_simple.gtl
@@ -0,0 +1,78 @@
+%MOMM*%
+%FSLAX34Y34*%
+%IPPOS*%
+%ADD10C,0*%
+G75*
+%LPD*%
+D10*
+G36*
+G01*
+X400000Y0D02*
+G75*
+G01*
+X100000Y0D01*
+G02*
+X0Y100000I0J100000D01*
+G01*
+X0Y400000D01*
+G02*
+X100000Y500000I100000J0D01*
+G01*
+X400000Y500000D01*
+G02*
+X500000Y400000I0J-100000D01*
+G01*
+X500000Y100000D01*
+G02*
+X400000Y0I-100000J0D01*
+G01*
+X742704Y750394D02*
+G75*
+G01*
+X742704Y450394D01*
+G02*
+X642704Y350394I-100000J0D01*
+G01*
+X342704Y350394D01*
+G02*
+X242704Y450394I0J100000D01*
+G01*
+X242704Y750394D01*
+G02*
+X342704Y850394I100000J0D01*
+G01*
+X642704Y850394D01*
+G02*
+X742704Y750394I0J-100000D01*
+G01*
+X382038Y422062D02*
+G75*
+G01*
+X134512Y422062D01*
+G01*
+X134512Y135960D01*
+G01*
+X382038Y135960D01*
+G01*
+X382038Y422062D01*
+G01*
+X927009Y126316D02*
+G75*
+G03*
+X927009Y126316I-125463J0D01*
+G01*
+X602773Y650000D02*
+G75*
+G03*
+X602773Y650000I-102773J0D01*
+G01*
+X500000Y650000D02*
+G75*
+G01*
+X500000Y714592D01*
+G03*
+X562602Y634088I0J-64592D01*
+G01*
+X500000Y650000D01*
+G37*
+M02*
diff --git a/tests/expects/dxf_save_line.gtl b/tests/expects/dxf_save_line.gtl
index d1966ad..b3ee8f1 100644
--- a/tests/expects/dxf_save_line.gtl
+++ b/tests/expects/dxf_save_line.gtl
@@ -1,41 +1,93 @@
%MOMM*%
%FSLAX34Y34*%
%IPPOS*%
-%ADD10C,0*%
+%ADD10C,0.2*%
G75*
%LPD*%
D10*
G01*
-X90000Y0D02*
+X800000Y850000D02*
G75*
G01*
-X10000Y0D01*
+X800000Y350000D01*
+G01*
+X900000Y850000D02*
+G75*
+G01*
+X900000Y350000D01*
+G01*
+X200000Y329390D02*
+G75*
+G01*
+X325827Y329390D01*
+G01*
+X325827Y178443D01*
+G01*
+X400000Y0D02*
+G75*
+G01*
+X100000Y0D01*
G02*
-X0Y10000I0J10000D01*
+X0Y100000I0J100000D01*
G01*
-X0Y90000D01*
+X0Y400000D01*
G02*
-X10000Y100000I10000J0D01*
+X100000Y500000I100000J0D01*
G01*
-X90000Y100000D01*
+X400000Y500000D01*
G02*
-X100000Y90000I0J-10000D01*
+X500000Y400000I0J-100000D01*
G01*
-X100000Y10000D01*
+X500000Y100000D01*
G02*
-X90000Y0I-10000J0D01*
+X400000Y0I-100000J0D01*
G01*
-X9171Y75107D02*
+X742704Y750394D02*
G75*
+G01*
+X742704Y450394D01*
G02*
-X3996Y73041I-3000J0D01*
+X642704Y350394I-100000J0D01*
G01*
-X6171Y75107D01*
+X342704Y350394D01*
+G02*
+X242704Y450394I0J100000D01*
G01*
-X9171Y75107D01*
+X242704Y750394D01*
+G02*
+X342704Y850394I100000J0D01*
+G01*
+X642704Y850394D01*
+G02*
+X742704Y750394I0J-100000D01*
G01*
-X9171Y50000D02*
+X382038Y422062D02*
+G75*
+G01*
+X134512Y422062D01*
+G01*
+X134512Y135960D01*
+G01*
+X382038Y135960D01*
+G01*
+X382038Y422062D01*
+G01*
+X927009Y126316D02*
+G75*
+G03*
+X927009Y126316I-125463J0D01*
+G01*
+X602773Y650000D02*
G75*
G03*
-X9171Y50000I-3000J0D01*
+X602773Y650000I-102773J0D01*
+G01*
+X500000Y650000D02*
+G75*
+G01*
+X500000Y714592D01*
+G03*
+X562602Y634088I0J-64592D01*
+G01*
+X500000Y650000D01*
M02*
diff --git a/tests/expects/dxf_save_line.txt b/tests/expects/dxf_save_line.txt
new file mode 100644
index 0000000..c3d73d4
--- /dev/null
+++ b/tests/expects/dxf_save_line.txt
@@ -0,0 +1,84 @@
+M48
+FMAT,2
+ICI,OFF
+METRIC,TZ,000.000
+T01C0.200
+%
+G90
+M71
+T01
+G00X80000Y85000
+M15
+G01X80000Y35000
+M16
+G05
+
+G00X90000Y85000
+M15
+G01X90000Y35000
+M16
+G05
+
+G00X20000Y32939
+M15
+G01X32583Y32939
+G01X32583Y17844
+M16
+G05
+
+G00X40000Y0
+M15
+G01X10000Y0
+G02X0Y10000I0J10000
+G01X0Y40000
+G02X10000Y50000I10000J0
+G01X40000Y50000
+G02X50000Y40000I0J-10000
+G01X50000Y10000
+G02X40000Y0I-10000J0
+M16
+G05
+
+G00X74270Y75039
+M15
+G01X74270Y45039
+G02X64270Y35039I-10000J0
+G01X34270Y35039
+G02X24270Y45039I0J10000
+G01X24270Y75039
+G02X34270Y85039I10000J0
+G01X64270Y85039
+G02X74270Y75039I0J-10000
+M16
+G05
+
+G00X38204Y42206
+M15
+G01X13451Y42206
+G01X13451Y13596
+G01X38204Y13596
+G01X38204Y42206
+M16
+G05
+
+G00X92701Y12632
+M15
+G03X92701Y12632I-12546J0
+M16
+G05
+
+G00X60277Y65000
+M15
+G03X60277Y65000I-10277J0
+M16
+G05
+
+G00X50000Y65000
+M15
+G01X50000Y71459
+G03X56260Y63409I0J-6459
+G01X50000Y65000
+M16
+G05
+
+M30
diff --git a/tests/expects/dxf_save_mousebites.gtl b/tests/expects/dxf_save_mousebites.gtl
index 282234d..64cc6d8 100644
--- a/tests/expects/dxf_save_mousebites.gtl
+++ b/tests/expects/dxf_save_mousebites.gtl
@@ -5,24 +5,578 @@
G75*
%LPD*%
D10*
-X10000Y90000D03*
-X10000Y80000D03*
-X10000Y70000D03*
-X10000Y60000D03*
-X10000Y50000D03*
-X10000Y40000D03*
-X10000Y30000D03*
-X10000Y20000D03*
-X10000Y10000D03*
-
-X50000Y90000D03*
-X50000Y80000D03*
-X50000Y70000D03*
-X50000Y60000D03*
-X50000Y50000D03*
-X50000Y40000D03*
-X50000Y30000D03*
-X50000Y20000D03*
-X50000Y10000D03*
+X800000Y850000D03*
+X800000Y836000D03*
+X800000Y822000D03*
+X800000Y808000D03*
+X800000Y794000D03*
+X800000Y780000D03*
+X800000Y766000D03*
+X800000Y752000D03*
+X800000Y738000D03*
+X800000Y724000D03*
+X800000Y710000D03*
+X800000Y696000D03*
+X800000Y682000D03*
+X800000Y668000D03*
+X800000Y654000D03*
+X800000Y640000D03*
+X800000Y626000D03*
+X800000Y612000D03*
+X800000Y598000D03*
+X800000Y584000D03*
+X800000Y570000D03*
+X800000Y556000D03*
+X800000Y542000D03*
+X800000Y528000D03*
+X800000Y514000D03*
+X800000Y500000D03*
+X800000Y486000D03*
+X800000Y472000D03*
+X800000Y458000D03*
+X800000Y444000D03*
+X800000Y430000D03*
+X800000Y416000D03*
+X800000Y402000D03*
+X800000Y388000D03*
+X800000Y374000D03*
+X800000Y360000D03*
+
+X900000Y850000D03*
+X900000Y836000D03*
+X900000Y822000D03*
+X900000Y808000D03*
+X900000Y794000D03*
+X900000Y780000D03*
+X900000Y766000D03*
+X900000Y752000D03*
+X900000Y738000D03*
+X900000Y724000D03*
+X900000Y710000D03*
+X900000Y696000D03*
+X900000Y682000D03*
+X900000Y668000D03*
+X900000Y654000D03*
+X900000Y640000D03*
+X900000Y626000D03*
+X900000Y612000D03*
+X900000Y598000D03*
+X900000Y584000D03*
+X900000Y570000D03*
+X900000Y556000D03*
+X900000Y542000D03*
+X900000Y528000D03*
+X900000Y514000D03*
+X900000Y500000D03*
+X900000Y486000D03*
+X900000Y472000D03*
+X900000Y458000D03*
+X900000Y444000D03*
+X900000Y430000D03*
+X900000Y416000D03*
+X900000Y402000D03*
+X900000Y388000D03*
+X900000Y374000D03*
+X900000Y360000D03*
+
+X200000Y329390D03*
+X214000Y329390D03*
+X228000Y329390D03*
+X242000Y329390D03*
+X256000Y329390D03*
+X270000Y329390D03*
+X284000Y329390D03*
+X298000Y329390D03*
+X312000Y329390D03*
+X325827Y329217D03*
+X325827Y315217D03*
+X325827Y301217D03*
+X325827Y287217D03*
+X325827Y273217D03*
+X325827Y259217D03*
+X325827Y245217D03*
+X325827Y231217D03*
+X325827Y217217D03*
+X325827Y203217D03*
+X325827Y189217D03*
+
+X400000Y0D03*
+X386000Y0D03*
+X372000Y0D03*
+X358000Y0D03*
+X344000Y0D03*
+X330000Y0D03*
+X316000Y0D03*
+X302000Y0D03*
+X288000Y0D03*
+X274000Y0D03*
+X260000Y0D03*
+X246000Y0D03*
+X232000Y0D03*
+X218000Y0D03*
+X204000Y0D03*
+X190000Y0D03*
+X176000Y0D03*
+X162000Y0D03*
+X148000Y0D03*
+X134000Y0D03*
+X120000Y0D03*
+X106000Y0D03*
+X92009Y320D03*
+X78177Y2410D03*
+X64773Y6410D03*
+X52057Y12242D03*
+X40280Y19790D03*
+X29672Y28909D03*
+X20440Y39418D03*
+X12764Y51113D03*
+X6796Y63764D03*
+X2652Y77125D03*
+X412Y90933D03*
+X0Y104920D03*
+X0Y118920D03*
+X0Y132920D03*
+X0Y146920D03*
+X0Y160920D03*
+X0Y174920D03*
+X0Y188920D03*
+X0Y202920D03*
+X0Y216920D03*
+X0Y230920D03*
+X0Y244920D03*
+X0Y258920D03*
+X0Y272920D03*
+X0Y286920D03*
+X0Y300920D03*
+X0Y314920D03*
+X0Y328920D03*
+X0Y342920D03*
+X0Y356920D03*
+X0Y370920D03*
+X0Y384920D03*
+X0Y398920D03*
+X834Y412884D03*
+X3602Y426596D03*
+X8256Y439788D03*
+X14706Y452201D03*
+X22825Y463592D03*
+X32454Y473739D03*
+X43404Y482444D03*
+X55463Y489534D03*
+X68392Y494873D03*
+X81940Y498356D03*
+X95842Y499914D03*
+X109841Y500000D03*
+X123841Y500000D03*
+X137841Y500000D03*
+X151841Y500000D03*
+X165841Y500000D03*
+X179841Y500000D03*
+X193841Y500000D03*
+X207841Y500000D03*
+X221841Y500000D03*
+X235841Y500000D03*
+X249841Y500000D03*
+X263841Y500000D03*
+X277841Y500000D03*
+X291841Y500000D03*
+X305841Y500000D03*
+X319841Y500000D03*
+X333841Y500000D03*
+X347841Y500000D03*
+X361841Y500000D03*
+X375841Y500000D03*
+X389841Y500000D03*
+X403840Y499926D03*
+X417746Y498413D03*
+X431305Y494974D03*
+X444252Y489676D03*
+X456333Y482623D03*
+X467311Y473954D03*
+X476972Y463838D03*
+X485127Y452472D03*
+X491617Y440080D03*
+X496313Y426903D03*
+X499125Y413200D03*
+X500000Y399239D03*
+X500000Y385239D03*
+X500000Y371239D03*
+X500000Y357239D03*
+X500000Y343239D03*
+X500000Y329239D03*
+X500000Y315239D03*
+X500000Y301239D03*
+X500000Y287239D03*
+X500000Y273239D03*
+X500000Y259239D03*
+X500000Y245239D03*
+X500000Y231239D03*
+X500000Y217239D03*
+X500000Y203239D03*
+X500000Y189239D03*
+X500000Y175239D03*
+X500000Y161239D03*
+X500000Y147239D03*
+X500000Y133239D03*
+X500000Y119239D03*
+X500000Y105239D03*
+X499616Y91250D03*
+X497421Y77435D03*
+X493319Y64061D03*
+X487391Y51391D03*
+X479753Y39672D03*
+X470554Y29133D03*
+X459975Y19981D03*
+X448222Y12395D03*
+X435525Y6523D03*
+X422134Y2480D03*
+X408309Y346D03*
+
+X742704Y750394D03*
+X742704Y736394D03*
+X742704Y722394D03*
+X742704Y708394D03*
+X742704Y694394D03*
+X742704Y680394D03*
+X742704Y666394D03*
+X742704Y652394D03*
+X742704Y638394D03*
+X742704Y624394D03*
+X742704Y610394D03*
+X742704Y596394D03*
+X742704Y582394D03*
+X742704Y568394D03*
+X742704Y554394D03*
+X742704Y540394D03*
+X742704Y526394D03*
+X742704Y512394D03*
+X742704Y498394D03*
+X742704Y484394D03*
+X742704Y470394D03*
+X742704Y456394D03*
+X742384Y442402D03*
+X740294Y428571D03*
+X736294Y415167D03*
+X730462Y402451D03*
+X722914Y390674D03*
+X713795Y380066D03*
+X703286Y370834D03*
+X691591Y363158D03*
+X678940Y357190D03*
+X665579Y353045D03*
+X651771Y350806D03*
+X637784Y350394D03*
+X623784Y350394D03*
+X609784Y350394D03*
+X595784Y350394D03*
+X581784Y350394D03*
+X567784Y350394D03*
+X553784Y350394D03*
+X539784Y350394D03*
+X525784Y350394D03*
+X511784Y350394D03*
+X497784Y350394D03*
+X483784Y350394D03*
+X469784Y350394D03*
+X455784Y350394D03*
+X441784Y350394D03*
+X427784Y350394D03*
+X413784Y350394D03*
+X399784Y350394D03*
+X385784Y350394D03*
+X371784Y350394D03*
+X357784Y350394D03*
+X343784Y350394D03*
+X329820Y351227D03*
+X316108Y353996D03*
+X302916Y358650D03*
+X290503Y365100D03*
+X279112Y373219D03*
+X268965Y382848D03*
+X260261Y393798D03*
+X253170Y405856D03*
+X247831Y418786D03*
+X244348Y432334D03*
+X242791Y446236D03*
+X242704Y460235D03*
+X242704Y474235D03*
+X242704Y488235D03*
+X242704Y502235D03*
+X242704Y516235D03*
+X242704Y530235D03*
+X242704Y544235D03*
+X242704Y558235D03*
+X242704Y572235D03*
+X242704Y586235D03*
+X242704Y600235D03*
+X242704Y614235D03*
+X242704Y628235D03*
+X242704Y642235D03*
+X242704Y656235D03*
+X242704Y670235D03*
+X242704Y684235D03*
+X242704Y698235D03*
+X242704Y712235D03*
+X242704Y726235D03*
+X242704Y740235D03*
+X242778Y754234D03*
+X244291Y768140D03*
+X247731Y781699D03*
+X253028Y794646D03*
+X260081Y806727D03*
+X268750Y817705D03*
+X278866Y827366D03*
+X290232Y835521D03*
+X302624Y842011D03*
+X315801Y846707D03*
+X329504Y849519D03*
+X343465Y850394D03*
+X357465Y850394D03*
+X371465Y850394D03*
+X385465Y850394D03*
+X399465Y850394D03*
+X413465Y850394D03*
+X427465Y850394D03*
+X441465Y850394D03*
+X455465Y850394D03*
+X469465Y850394D03*
+X483465Y850394D03*
+X497465Y850394D03*
+X511465Y850394D03*
+X525465Y850394D03*
+X539465Y850394D03*
+X553465Y850394D03*
+X567465Y850394D03*
+X581465Y850394D03*
+X595465Y850394D03*
+X609465Y850394D03*
+X623465Y850394D03*
+X637465Y850394D03*
+X651454Y850010D03*
+X665269Y847815D03*
+X678643Y843713D03*
+X691313Y837785D03*
+X703032Y830147D03*
+X713571Y820948D03*
+X722723Y810369D03*
+X730309Y798616D03*
+X736181Y785919D03*
+X740224Y772528D03*
+X742358Y758703D03*
+
+X382038Y422062D03*
+X368038Y422062D03*
+X354038Y422062D03*
+X340038Y422062D03*
+X326038Y422062D03*
+X312038Y422062D03*
+X298038Y422062D03*
+X284038Y422062D03*
+X270038Y422062D03*
+X256038Y422062D03*
+X242038Y422062D03*
+X228038Y422062D03*
+X214038Y422062D03*
+X200038Y422062D03*
+X186038Y422062D03*
+X172038Y422062D03*
+X158038Y422062D03*
+X144038Y422062D03*
+X134512Y417588D03*
+X134512Y403588D03*
+X134512Y389588D03*
+X134512Y375588D03*
+X134512Y361588D03*
+X134512Y347588D03*
+X134512Y333588D03*
+X134512Y319588D03*
+X134512Y305588D03*
+X134512Y291588D03*
+X134512Y277588D03*
+X134512Y263588D03*
+X134512Y249588D03*
+X134512Y235588D03*
+X134512Y221588D03*
+X134512Y207588D03*
+X134512Y193588D03*
+X134512Y179588D03*
+X134512Y165588D03*
+X134512Y151588D03*
+X134512Y137588D03*
+X146884Y135960D03*
+X160884Y135960D03*
+X174884Y135960D03*
+X188884Y135960D03*
+X202884Y135960D03*
+X216884Y135960D03*
+X230884Y135960D03*
+X244884Y135960D03*
+X258884Y135960D03*
+X272884Y135960D03*
+X286884Y135960D03*
+X300884Y135960D03*
+X314884Y135960D03*
+X328884Y135960D03*
+X342884Y135960D03*
+X356884Y135960D03*
+X370884Y135960D03*
+X382038Y138807D03*
+X382038Y152807D03*
+X382038Y166807D03*
+X382038Y180807D03*
+X382038Y194807D03*
+X382038Y208807D03*
+X382038Y222807D03*
+X382038Y236807D03*
+X382038Y250807D03*
+X382038Y264807D03*
+X382038Y278807D03*
+X382038Y292807D03*
+X382038Y306807D03*
+X382038Y320807D03*
+X382038Y334807D03*
+X382038Y348807D03*
+X382038Y362807D03*
+X382038Y376807D03*
+X382038Y390807D03*
+X382038Y404807D03*
+X382038Y418807D03*
+
+X927009Y126316D03*
+X926228Y140287D03*
+X923897Y154085D03*
+X920044Y167536D03*
+X914717Y180475D03*
+X907982Y192741D03*
+X899924Y204180D03*
+X890641Y214651D03*
+X880251Y224022D03*
+X868881Y232179D03*
+X856674Y239019D03*
+X843781Y244457D03*
+X830363Y248425D03*
+X816586Y250874D03*
+X802622Y251774D03*
+X788645Y251114D03*
+X774828Y248901D03*
+X761344Y245164D03*
+X748360Y239948D03*
+X736037Y233319D03*
+X724529Y225358D03*
+X713979Y216166D03*
+X704519Y205856D03*
+X696265Y194557D03*
+X689321Y182409D03*
+X683773Y169563D03*
+X679689Y156179D03*
+X677122Y142424D03*
+X676102Y128469D03*
+X676642Y114486D03*
+X678737Y100651D03*
+X682358Y87135D03*
+X687463Y74107D03*
+X693986Y61728D03*
+X701847Y50152D03*
+X710949Y39524D03*
+X721177Y29975D03*
+X732405Y21625D03*
+X744493Y14576D03*
+X757291Y8918D03*
+X770639Y4720D03*
+X784372Y2035D03*
+X798318Y895D03*
+X812304Y1316D03*
+X826157Y3291D03*
+X839703Y6797D03*
+X852775Y11789D03*
+X865210Y18206D03*
+X876852Y25968D03*
+X887558Y34978D03*
+X897195Y45124D03*
+X905641Y56280D03*
+X912793Y68307D03*
+X918560Y81056D03*
+X922873Y94367D03*
+X925676Y108077D03*
+X926935Y122013D03*
+
+X602773Y650000D03*
+X601821Y663957D03*
+X598982Y677655D03*
+X594309Y690841D03*
+X587889Y703270D03*
+X579841Y714712D03*
+X570313Y724955D03*
+X559483Y733809D03*
+X547550Y741111D03*
+X534737Y746724D03*
+X521280Y750545D03*
+X507428Y752504D03*
+X493439Y752563D03*
+X479572Y750722D03*
+X466083Y747015D03*
+X453222Y741510D03*
+X441228Y734310D03*
+X430323Y725547D03*
+X420709Y715385D03*
+X412565Y704012D03*
+X406040Y691637D03*
+X401256Y678491D03*
+X398301Y664818D03*
+X397231Y650870D03*
+X398065Y636905D03*
+X400788Y623184D03*
+X405348Y609959D03*
+X411663Y597476D03*
+X419614Y585966D03*
+X429055Y575643D03*
+X439810Y566697D03*
+X451680Y559295D03*
+X464446Y553573D03*
+X477870Y549638D03*
+X491705Y547563D03*
+X505693Y547385D03*
+X519575Y549109D03*
+X533095Y552702D03*
+X546002Y558098D03*
+X558056Y565196D03*
+X569035Y573866D03*
+X578734Y583946D03*
+X586975Y595251D03*
+X593605Y607569D03*
+X598500Y620674D03*
+X601570Y634322D03*
+X602758Y648261D03*
+
+X500000Y650000D03*
+X500000Y664000D03*
+X500000Y678000D03*
+X500000Y692000D03*
+X500000Y706000D03*
+X494599Y714366D03*
+X480883Y711699D03*
+X468062Y706144D03*
+X456736Y697962D03*
+X447434Y687536D03*
+X440591Y675353D03*
+X436529Y661984D03*
+X435437Y648054D03*
+X437366Y634216D03*
+X442226Y621115D03*
+X449789Y609367D03*
+X459702Y599520D03*
+X471501Y592035D03*
+X484633Y587262D03*
+X498484Y585425D03*
+X512407Y586610D03*
+X525748Y590761D03*
+X537885Y597685D03*
+X548249Y607056D03*
+X556356Y618437D03*
+X561825Y631294D03*
+X551844Y636823D03*
+X538276Y640271D03*
+X524707Y643720D03*
+X511139Y647169D03*
M02*
diff --git a/tests/expects/dxf_save_mousebites.txt b/tests/expects/dxf_save_mousebites.txt
index e82825a..4a64514 100644
--- a/tests/expects/dxf_save_mousebites.txt
+++ b/tests/expects/dxf_save_mousebites.txt
@@ -7,24 +7,578 @@ T01C0.500
G90
M71
T01
-X1000Y9000
-X1000Y8000
-X1000Y7000
-X1000Y6000
-X1000Y5000
-X1000Y4000
-X1000Y3000
-X1000Y2000
-X1000Y1000
-
-X5000Y9000
-X5000Y8000
-X5000Y7000
-X5000Y6000
-X5000Y5000
-X5000Y4000
-X5000Y3000
-X5000Y2000
-X5000Y1000
+X80000Y85000
+X80000Y83600
+X80000Y82200
+X80000Y80800
+X80000Y79400
+X80000Y78000
+X80000Y76600
+X80000Y75200
+X80000Y73800
+X80000Y72400
+X80000Y71000
+X80000Y69600
+X80000Y68200
+X80000Y66800
+X80000Y65400
+X80000Y64000
+X80000Y62600
+X80000Y61200
+X80000Y59800
+X80000Y58400
+X80000Y57000
+X80000Y55600
+X80000Y54200
+X80000Y52800
+X80000Y51400
+X80000Y50000
+X80000Y48600
+X80000Y47200
+X80000Y45800
+X80000Y44400
+X80000Y43000
+X80000Y41600
+X80000Y40200
+X80000Y38800
+X80000Y37400
+X80000Y36000
+
+X90000Y85000
+X90000Y83600
+X90000Y82200
+X90000Y80800
+X90000Y79400
+X90000Y78000
+X90000Y76600
+X90000Y75200
+X90000Y73800
+X90000Y72400
+X90000Y71000
+X90000Y69600
+X90000Y68200
+X90000Y66800
+X90000Y65400
+X90000Y64000
+X90000Y62600
+X90000Y61200
+X90000Y59800
+X90000Y58400
+X90000Y57000
+X90000Y55600
+X90000Y54200
+X90000Y52800
+X90000Y51400
+X90000Y50000
+X90000Y48600
+X90000Y47200
+X90000Y45800
+X90000Y44400
+X90000Y43000
+X90000Y41600
+X90000Y40200
+X90000Y38800
+X90000Y37400
+X90000Y36000
+
+X20000Y32939
+X21400Y32939
+X22800Y32939
+X24200Y32939
+X25600Y32939
+X27000Y32939
+X28400Y32939
+X29800Y32939
+X31200Y32939
+X32583Y32922
+X32583Y31522
+X32583Y30122
+X32583Y28722
+X32583Y27322
+X32583Y25922
+X32583Y24522
+X32583Y23122
+X32583Y21722
+X32583Y20322
+X32583Y18922
+
+X40000Y0
+X38600Y0
+X37200Y0
+X35800Y0
+X34400Y0
+X33000Y0
+X31600Y0
+X30200Y0
+X28800Y0
+X27400Y0
+X26000Y0
+X24600Y0
+X23200Y0
+X21800Y0
+X20400Y0
+X19000Y0
+X17600Y0
+X16200Y0
+X14800Y0
+X13400Y0
+X12000Y0
+X10600Y0
+X9201Y32
+X7818Y241
+X6477Y641
+X5206Y1224
+X4028Y1979
+X2967Y2891
+X2044Y3942
+X1276Y5111
+X680Y6376
+X265Y7712
+X41Y9093
+X0Y10492
+X0Y11892
+X0Y13292
+X0Y14692
+X0Y16092
+X0Y17492
+X0Y18892
+X0Y20292
+X0Y21692
+X0Y23092
+X0Y24492
+X0Y25892
+X0Y27292
+X0Y28692
+X0Y30092
+X0Y31492
+X0Y32892
+X0Y34292
+X0Y35692
+X0Y37092
+X0Y38492
+X0Y39892
+X83Y41288
+X360Y42660
+X826Y43979
+X1471Y45220
+X2282Y46359
+X3245Y47374
+X4340Y48244
+X5546Y48953
+X6839Y49487
+X8194Y49836
+X9584Y49991
+X10984Y50000
+X12384Y50000
+X13784Y50000
+X15184Y50000
+X16584Y50000
+X17984Y50000
+X19384Y50000
+X20784Y50000
+X22184Y50000
+X23584Y50000
+X24984Y50000
+X26384Y50000
+X27784Y50000
+X29184Y50000
+X30584Y50000
+X31984Y50000
+X33384Y50000
+X34784Y50000
+X36184Y50000
+X37584Y50000
+X38984Y50000
+X40384Y49993
+X41775Y49841
+X43131Y49497
+X44425Y48968
+X45633Y48262
+X46731Y47395
+X47697Y46384
+X48513Y45247
+X49162Y44008
+X49631Y42690
+X49912Y41320
+X50000Y39924
+X50000Y38524
+X50000Y37124
+X50000Y35724
+X50000Y34324
+X50000Y32924
+X50000Y31524
+X50000Y30124
+X50000Y28724
+X50000Y27324
+X50000Y25924
+X50000Y24524
+X50000Y23124
+X50000Y21724
+X50000Y20324
+X50000Y18924
+X50000Y17524
+X50000Y16124
+X50000Y14724
+X50000Y13324
+X50000Y11924
+X50000Y10524
+X49962Y9125
+X49742Y7743
+X49332Y6406
+X48739Y5139
+X47975Y3967
+X47055Y2913
+X45997Y1998
+X44822Y1239
+X43553Y652
+X42213Y248
+X40831Y35
+
+X74270Y75039
+X74270Y73639
+X74270Y72239
+X74270Y70839
+X74270Y69439
+X74270Y68039
+X74270Y66639
+X74270Y65239
+X74270Y63839
+X74270Y62439
+X74270Y61039
+X74270Y59639
+X74270Y58239
+X74270Y56839
+X74270Y55439
+X74270Y54039
+X74270Y52639
+X74270Y51239
+X74270Y49839
+X74270Y48439
+X74270Y47039
+X74270Y45639
+X74238Y44240
+X74029Y42857
+X73629Y41517
+X73046Y40245
+X72291Y39067
+X71380Y38007
+X70329Y37083
+X69159Y36316
+X67894Y35719
+X66558Y35305
+X65177Y35081
+X63778Y35039
+X62378Y35039
+X60978Y35039
+X59578Y35039
+X58178Y35039
+X56778Y35039
+X55378Y35039
+X53978Y35039
+X52578Y35039
+X51178Y35039
+X49778Y35039
+X48378Y35039
+X46978Y35039
+X45578Y35039
+X44178Y35039
+X42778Y35039
+X41378Y35039
+X39978Y35039
+X38578Y35039
+X37178Y35039
+X35778Y35039
+X34378Y35039
+X32982Y35123
+X31611Y35400
+X30292Y35865
+X29050Y36510
+X27911Y37322
+X26896Y38285
+X26026Y39380
+X25317Y40586
+X24783Y41879
+X24435Y43233
+X24279Y44624
+X24270Y46023
+X24270Y47423
+X24270Y48823
+X24270Y50223
+X24270Y51623
+X24270Y53023
+X24270Y54423
+X24270Y55823
+X24270Y57223
+X24270Y58623
+X24270Y60023
+X24270Y61423
+X24270Y62823
+X24270Y64223
+X24270Y65623
+X24270Y67023
+X24270Y68423
+X24270Y69823
+X24270Y71223
+X24270Y72623
+X24270Y74023
+X24278Y75423
+X24429Y76814
+X24773Y78170
+X25303Y79465
+X26008Y80673
+X26875Y81771
+X27887Y82737
+X29023Y83552
+X30262Y84201
+X31580Y84671
+X32950Y84952
+X34347Y85039
+X35747Y85039
+X37147Y85039
+X38547Y85039
+X39947Y85039
+X41347Y85039
+X42747Y85039
+X44147Y85039
+X45547Y85039
+X46947Y85039
+X48347Y85039
+X49747Y85039
+X51147Y85039
+X52547Y85039
+X53947Y85039
+X55347Y85039
+X56747Y85039
+X58147Y85039
+X59547Y85039
+X60947Y85039
+X62347Y85039
+X63747Y85039
+X65145Y85001
+X66527Y84781
+X67864Y84371
+X69131Y83778
+X70303Y83015
+X71357Y82095
+X72272Y81037
+X73031Y79862
+X73618Y78592
+X74022Y77253
+X74236Y75870
+
+X38204Y42206
+X36804Y42206
+X35404Y42206
+X34004Y42206
+X32604Y42206
+X31204Y42206
+X29804Y42206
+X28404Y42206
+X27004Y42206
+X25604Y42206
+X24204Y42206
+X22804Y42206
+X21404Y42206
+X20004Y42206
+X18604Y42206
+X17204Y42206
+X15804Y42206
+X14404Y42206
+X13451Y41759
+X13451Y40359
+X13451Y38959
+X13451Y37559
+X13451Y36159
+X13451Y34759
+X13451Y33359
+X13451Y31959
+X13451Y30559
+X13451Y29159
+X13451Y27759
+X13451Y26359
+X13451Y24959
+X13451Y23559
+X13451Y22159
+X13451Y20759
+X13451Y19359
+X13451Y17959
+X13451Y16559
+X13451Y15159
+X13451Y13759
+X14688Y13596
+X16088Y13596
+X17488Y13596
+X18888Y13596
+X20288Y13596
+X21688Y13596
+X23088Y13596
+X24488Y13596
+X25888Y13596
+X27288Y13596
+X28688Y13596
+X30088Y13596
+X31488Y13596
+X32888Y13596
+X34288Y13596
+X35688Y13596
+X37088Y13596
+X38204Y13881
+X38204Y15281
+X38204Y16681
+X38204Y18081
+X38204Y19481
+X38204Y20881
+X38204Y22281
+X38204Y23681
+X38204Y25081
+X38204Y26481
+X38204Y27881
+X38204Y29281
+X38204Y30681
+X38204Y32081
+X38204Y33481
+X38204Y34881
+X38204Y36281
+X38204Y37681
+X38204Y39081
+X38204Y40481
+X38204Y41881
+
+X92701Y12632
+X92623Y14029
+X92390Y15408
+X92004Y16754
+X91472Y18048
+X90798Y19274
+X89992Y20418
+X89064Y21465
+X88025Y22402
+X86888Y23218
+X85667Y23902
+X84378Y24446
+X83036Y24842
+X81659Y25087
+X80262Y25177
+X78865Y25111
+X77483Y24890
+X76134Y24516
+X74836Y23995
+X73604Y23332
+X72453Y22536
+X71398Y21617
+X70452Y20586
+X69626Y19456
+X68932Y18241
+X68377Y16956
+X67969Y15618
+X67712Y14242
+X67610Y12847
+X67664Y11449
+X67874Y10065
+X68236Y8714
+X68746Y7411
+X69399Y6173
+X70185Y5015
+X71095Y3952
+X72118Y2997
+X73241Y2162
+X74449Y1458
+X75729Y892
+X77064Y472
+X78437Y203
+X79832Y90
+X81230Y132
+X82616Y329
+X83970Y680
+X85278Y1179
+X86521Y1821
+X87685Y2597
+X88756Y3498
+X89719Y4512
+X90564Y5628
+X91279Y6831
+X91856Y8106
+X92287Y9437
+X92568Y10808
+X92693Y12201
+
+X60277Y65000
+X60182Y66396
+X59898Y67765
+X59431Y69084
+X58789Y70327
+X57984Y71471
+X57031Y72495
+X55948Y73381
+X54755Y74111
+X53474Y74672
+X52128Y75055
+X50743Y75250
+X49344Y75256
+X47957Y75072
+X46608Y74701
+X45322Y74151
+X44123Y73431
+X43032Y72555
+X42071Y71539
+X41256Y70401
+X40604Y69164
+X40126Y67849
+X39830Y66482
+X39723Y65087
+X39807Y63691
+X40079Y62318
+X40535Y60996
+X41166Y59748
+X41961Y58597
+X42905Y57564
+X43981Y56670
+X45168Y55929
+X46445Y55357
+X47787Y54964
+X49170Y54756
+X50569Y54739
+X51958Y54911
+X53310Y55270
+X54600Y55810
+X55806Y56520
+X56903Y57387
+X57873Y58395
+X58698Y59525
+X59360Y60757
+X59850Y62067
+X60157Y63432
+X60276Y64826
+
+X50000Y65000
+X50000Y66400
+X50000Y67800
+X50000Y69200
+X50000Y70600
+X49460Y71437
+X48088Y71170
+X46806Y70614
+X45674Y69796
+X44743Y68754
+X44059Y67535
+X43653Y66198
+X43544Y64805
+X43737Y63422
+X44223Y62112
+X44979Y60937
+X45970Y59952
+X47150Y59203
+X48463Y58726
+X49848Y58543
+X51241Y58661
+X52575Y59076
+X53789Y59768
+X54825Y60706
+X55636Y61844
+X56182Y63129
+X55184Y63682
+X53828Y64027
+X52471Y64372
+X51114Y64717
M30
diff --git a/tests/expects/dxf_to_inch.gtl b/tests/expects/dxf_to_inch.gtl
index aa862bf..f838214 100644
--- a/tests/expects/dxf_to_inch.gtl
+++ b/tests/expects/dxf_to_inch.gtl
@@ -6,36 +6,88 @@ G75*
%LPD*%
D10*
G01*
-X35433Y0D02*
+X314961Y334646D02*
G75*
G01*
-X3937Y0D01*
+X314961Y137795D01*
+G01*
+X354331Y334646D02*
+G75*
+G01*
+X354331Y137795D01*
+G01*
+X78740Y129681D02*
+G75*
+G01*
+X128278Y129681D01*
+G01*
+X128278Y70253D01*
+G01*
+X157480Y0D02*
+G75*
+G01*
+X39370Y0D01*
G02*
-X0Y3937I0J3937D01*
+X0Y39370I0J39370D01*
G01*
-X0Y35433D01*
+X0Y157480D01*
G02*
-X3937Y39370I3937J0D01*
+X39370Y196850I39370J0D01*
G01*
-X35433Y39370D01*
+X157480Y196850D01*
G02*
-X39370Y35433I0J-3937D01*
+X196850Y157480I0J-39370D01*
G01*
-X39370Y3937D01*
+X196850Y39370D01*
G02*
-X35433Y0I-3937J0D01*
+X157480Y0I-39370J0D01*
G01*
-X3610Y29570D02*
+X292403Y295431D02*
G75*
+G01*
+X292403Y177320D01*
G02*
-X1573Y28756I-1181J0D01*
+X253033Y137950I-39370J0D01*
G01*
-X2429Y29570D01*
+X134923Y137950D01*
+G02*
+X95553Y177320I0J39370D01*
G01*
-X3610Y29570D01*
+X95553Y295431D01*
+G02*
+X134923Y334801I39370J0D01*
+G01*
+X253033Y334801D01*
+G02*
+X292403Y295431I0J-39370D01*
G01*
-X3610Y19685D02*
+X150409Y166166D02*
+G75*
+G01*
+X52957Y166166D01*
+G01*
+X52957Y53528D01*
+G01*
+X150409Y53528D01*
+G01*
+X150409Y166166D01*
+G01*
+X364964Y49731D02*
+G75*
+G03*
+X364964Y49731I-49395J0D01*
+G01*
+X237312Y255906D02*
G75*
G03*
-X3610Y19685I-1181J0D01*
+X237312Y255906I-40462J0D01*
+G01*
+X196850Y255906D02*
+G75*
+G01*
+X196850Y281336D01*
+G03*
+X221497Y249641I0J-25430D01*
+G01*
+X196850Y255906D01*
M02*
diff --git a/tests/test_dxf.py b/tests/test_dxf.py
index 31589da..3d4d28e 100644
--- a/tests/test_dxf.py
+++ b/tests/test_dxf.py
@@ -19,7 +19,6 @@ class TestExcellon(unittest.TestCase):
cls.OUTPREFIX = 'dxf_'
cls.METRIC_FILE = os.path.join(cls.INDIR, 'ref_dxf_metric.dxf')
cls.INCH_FILE = os.path.join(cls.INDIR, 'ref_dxf_inch.dxf')
- cls.MOUSEBITES_FILE = os.path.join(cls.INDIR, 'ref_dxf_mousebites.dxf')
try:
os.mkdir(cls.OUTDIR)
except FileExistsError:
@@ -36,6 +35,7 @@ class TestExcellon(unittest.TestCase):
outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save_line.gtl')
dxf = gerberex.read(self.METRIC_FILE)
dxf.draw_mode = dxf.DM_LINE
+ dxf.width = 0.2
dxf.write(outfile)
self._checkResult(outfile)
@@ -46,23 +46,41 @@ class TestExcellon(unittest.TestCase):
dxf.write(outfile)
self._checkResult(outfile)
+ def test_save_fill_simple(self):
+ outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save_fill_simple.gtl')
+ dxf = gerberex.read(self.METRIC_FILE)
+ dxf.draw_mode = dxf.DM_FILL
+ dxf.fill_mode = dxf.FM_SIMPLE
+ dxf.write(outfile)
+ self._checkResult(outfile)
+
def test_save_mousebites(self):
outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save_mousebites.gtl')
- dxf = gerberex.read(self.MOUSEBITES_FILE)
+ dxf = gerberex.read(self.METRIC_FILE)
dxf.draw_mode = dxf.DM_MOUSE_BITES
dxf.width = 0.5
- dxf.pitch = 1
+ dxf.pitch = 1.4
dxf.write(outfile)
self._checkResult(outfile)
def test_save_excellon(self):
outfile = os.path.join(
+ self.OUTDIR, self.OUTPREFIX + 'save_line.txt')
+ dxf = gerberex.read(self.METRIC_FILE)
+ dxf.draw_mode = dxf.DM_LINE
+ dxf.format = (3,3)
+ dxf.width = 0.2
+ dxf.write(outfile, filetype=dxf.FT_EXCELLON)
+ self._checkResult(outfile)
+
+ def test_save_excellon_mousebites(self):
+ outfile = os.path.join(
self.OUTDIR, self.OUTPREFIX + 'save_mousebites.txt')
- dxf = gerberex.read(self.MOUSEBITES_FILE)
+ dxf = gerberex.read(self.METRIC_FILE)
dxf.draw_mode = dxf.DM_MOUSE_BITES
- dxf.format = (3,3)
+ dxf.format = (3, 3)
dxf.width = 0.5
- dxf.pitch = 1
+ dxf.pitch = 1.4
dxf.write(outfile, filetype=dxf.FT_EXCELLON)
self._checkResult(outfile)