├── .gitignore ├── go.mod ├── testdata ├── arc.dxf ├── point.dxf ├── vertex1.dxf ├── my_arc.dxf ├── mypoint.dxf ├── mypoint_with_extent.dxf ├── mypoint_with_units.dxf └── torus.dxf ├── example └── torus │ ├── torus.png │ └── torus.go ├── table ├── symboltable.go ├── linewidth.go ├── table_type.go ├── tables.go ├── ucs.go ├── view.go ├── appid.go ├── dimstyle.go ├── block_record.go ├── table.go ├── style.go ├── linetype.go ├── viewport.go └── layer.go ├── format ├── formatter.go └── ascii.go ├── entity ├── arc.go ├── point.go ├── vertex.go ├── entity_type.go ├── 3dface.go ├── entities.go ├── circle.go ├── line.go ├── lwpolyline.go ├── spline.go ├── polyline.go ├── entity.go └── text.go ├── block ├── blocks.go └── block.go ├── object ├── objects.go ├── acdbplaceholder.go ├── dictionary.go ├── group.go └── acdbdictionarywdflt.go ├── geometry └── axis.go ├── extruder.go ├── LICENSE ├── class └── classes.go ├── drawing ├── section.go └── drawing.go ├── README.md ├── handle └── handle.go ├── header └── header.go ├── dxf.go ├── insunit └── insunit.go ├── dxf_test.go ├── color └── colornumber.go └── parser.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yofu/dxf 2 | 3 | go 1.22 4 | -------------------------------------------------------------------------------- /testdata/arc.dxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yofu/dxf/HEAD/testdata/arc.dxf -------------------------------------------------------------------------------- /testdata/point.dxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yofu/dxf/HEAD/testdata/point.dxf -------------------------------------------------------------------------------- /example/torus/torus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yofu/dxf/HEAD/example/torus/torus.png -------------------------------------------------------------------------------- /testdata/vertex1.dxf: -------------------------------------------------------------------------------- 1 | 0 2 | SECTION 3 | 2 4 | ENTITIES 5 | 0 6 | VERTEX 7 | 8 8 | 0 9 | 10 10 | 1.1 11 | 20 12 | 1.2 13 | 30 14 | 1.3 15 | 0 16 | ENDSEC 17 | EOF -------------------------------------------------------------------------------- /table/symboltable.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // SymbolTable is interface for AcDbSymbolTableRecord. 9 | type SymbolTable interface { 10 | IsSymbolTable() bool 11 | Format(format.Formatter) 12 | Handle() int 13 | SetHandle(*int) 14 | SetOwner(handle.Handler) 15 | Name() string 16 | } 17 | -------------------------------------------------------------------------------- /table/linewidth.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | // LineWeight enum value (code 370) 4 | var LineWidth = map[int]float64{ 5 | 5: 0.05, 6 | 9: 0.09, 7 | 13: 0.13, 8 | 15: 0.15, 9 | 18: 0.18, 10 | 20: 0.20, 11 | 25: 0.25, 12 | 30: 0.30, 13 | 35: 0.35, 14 | 40: 0.40, 15 | 50: 0.50, 16 | 53: 0.53, 17 | 60: 0.60, 18 | 70: 0.70, 19 | 80: 0.80, 20 | 90: 0.90, 21 | 100: 1.00, 22 | 106: 1.06, 23 | 120: 1.20, 24 | 140: 1.40, 25 | 158: 1.58, 26 | 200: 2.00, 27 | 211: 2.11, 28 | } 29 | -------------------------------------------------------------------------------- /format/formatter.go: -------------------------------------------------------------------------------- 1 | // DXF Output Formatter 2 | package format 3 | 4 | import ( 5 | "io" 6 | ) 7 | 8 | // Formatter controls output format. 9 | type Formatter interface { 10 | Reset() 11 | WriteTo(w io.Writer) (int64, error) 12 | SetPrecision(p int) 13 | Output() string 14 | String(num int, val string) string 15 | Hex(num int, h int) string 16 | Int(num int, val int) string 17 | Float(num int, val float64) string 18 | WriteString(num int, val string) 19 | WriteHex(num int, h int) 20 | WriteInt(num int, val int) 21 | WriteFloat(num int, val float64) 22 | } 23 | -------------------------------------------------------------------------------- /entity/arc.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | type Arc struct { 8 | *Circle 9 | Angle []float64 // 50, 51 (Degree) 10 | } 11 | 12 | // IsEntity is for Entity interface. 13 | func (a *Arc) IsEntity() bool { 14 | return true 15 | } 16 | 17 | // NewArc creates a new Arc. 18 | func NewArc(c *Circle) *Arc { 19 | if c == nil { 20 | c = NewCircle() 21 | } 22 | a := &Arc{ 23 | Circle: c, 24 | Angle: []float64{0.0, 180.0}, 25 | } 26 | a.SetEntityType(ARC) 27 | return a 28 | } 29 | 30 | // Format writes data to formatter. 31 | func (a *Arc) Format(f format.Formatter) { 32 | a.Circle.Format(f) 33 | f.WriteString(100, "AcDbArc") 34 | for i := 0; i < 2; i++ { 35 | f.WriteFloat(50+i, a.Angle[i]) 36 | } 37 | } 38 | 39 | // String write out the String representation 40 | func (a *Arc) String() string { 41 | f := format.NewASCII() 42 | a.Format(f) 43 | return f.Output() 44 | } 45 | -------------------------------------------------------------------------------- /example/torus/torus.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math" 6 | 7 | "github.com/yofu/dxf" 8 | "github.com/yofu/dxf/color" 9 | "github.com/yofu/dxf/table" 10 | ) 11 | 12 | func main() { 13 | d := dxf.NewDrawing() 14 | d.Header().LtScale = 100.0 15 | d.AddLayer("Toroidal", dxf.DefaultColor, dxf.DefaultLineType, true) 16 | d.AddLayer("Poloidal", color.Red, table.LT_HIDDEN, true) 17 | z := 0.0 18 | r1 := 200.0 19 | r2 := 500.0 20 | ndiv := 16 21 | dtheta := 2.0 * math.Pi / float64(ndiv) 22 | theta := 0.0 23 | for i := 0; i < ndiv; i++ { 24 | d.ChangeLayer("Toroidal") 25 | d.Circle(0.0, 0.0, z+r1*math.Cos(theta), r2-r1*math.Sin(theta)) 26 | d.ChangeLayer("Poloidal") 27 | c, _ := d.Circle(r2*math.Cos(theta), r2*math.Sin(theta), 0.0, r1) 28 | dxf.SetExtrusion(c, []float64{-1.0 * math.Sin(theta), math.Cos(theta), 0.0}) 29 | theta += dtheta 30 | } 31 | err := d.SaveAs("torus.dxf") 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /block/blocks.go: -------------------------------------------------------------------------------- 1 | // BLOCK section 2 | package block 3 | 4 | import ( 5 | "github.com/yofu/dxf/format" 6 | ) 7 | 8 | // Blocks represents BLOCKS section. 9 | type Blocks []*Block 10 | 11 | // New creates a new Blocks. 12 | func New() Blocks { 13 | b := make([]*Block, 3) 14 | b[0] = NewBlock("*Model_Space", "") 15 | b[1] = NewBlock("*Paper_Space", "") 16 | b[2] = NewBlock("*Paper_Space0", "") 17 | return b 18 | } 19 | 20 | // Format writes BLOCKS data to formatter. 21 | func (bs Blocks) Format(f format.Formatter) { 22 | f.WriteString(0, "SECTION") 23 | f.WriteString(2, "BLOCKS") 24 | for _, b := range bs { 25 | b.Format(f) 26 | } 27 | f.WriteString(0, "ENDSEC") 28 | } 29 | 30 | // Add adds a new block to BLOCKS section. 31 | func (bs Blocks) Add(b *Block) Blocks { 32 | bs = append(bs, b) 33 | return bs 34 | } 35 | 36 | // SetHandle sets handles to each block. 37 | func (bs Blocks) SetHandle(v *int) { 38 | for _, b := range bs { 39 | b.SetHandle(v) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /object/objects.go: -------------------------------------------------------------------------------- 1 | // OBJECTS section 2 | package object 3 | 4 | import ( 5 | "github.com/yofu/dxf/format" 6 | ) 7 | 8 | // Object is interface for OBJECT. 9 | type Object interface { 10 | IsObject() bool 11 | Format(f format.Formatter) 12 | Handle() int 13 | SetHandle(*int) 14 | } 15 | 16 | // Objects represents OBJECTS section. 17 | type Objects []Object 18 | 19 | // New create a new Objects. 20 | func New() Objects { 21 | o := make([]Object, 0) 22 | return o 23 | } 24 | 25 | // Format writes OBJECTS data to formatter. 26 | func (os Objects) Format(f format.Formatter) { 27 | f.WriteString(0, "SECTION") 28 | f.WriteString(2, "OBJECTS") 29 | for _, o := range os { 30 | o.Format(f) 31 | } 32 | f.WriteString(0, "ENDSEC") 33 | } 34 | 35 | // Add adds a new object to OBJECTS section. 36 | func (os Objects) Add(o Object) Objects { 37 | os = append(os, o) 38 | return os 39 | } 40 | 41 | // SetHandle sets handles to each object. 42 | func (os Objects) SetHandle(v *int) { 43 | for _, o := range os { 44 | o.SetHandle(v) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /geometry/axis.go: -------------------------------------------------------------------------------- 1 | // Geometrical Functions 2 | package geometry 3 | 4 | import ( 5 | "errors" 6 | "math" 7 | ) 8 | 9 | // ArbitraryAxis generates according X, Y axis from given Z axis using Arbitrary Axis Algorithm. 10 | // Z axis is assumed to be unit-length vector. 11 | // http://www.autodesk.com/techpubs/autocad/acad2000/dxf/arbitrary_axis_algorithm_dxf_ab.htm 12 | func ArbitraryAxis(d []float64) ([]float64, []float64, error) { 13 | if len(d) < 3 { 14 | return nil, nil, errors.New("not enough length") 15 | } 16 | thres := 1.0 / 64.0 17 | ax := make([]float64, 3) 18 | ay := make([]float64, 3) 19 | if math.Abs(d[0]) < thres && math.Abs(d[1]) < thres { 20 | sum := math.Sqrt(d[1]*d[1] + d[2]*d[2]) 21 | ax[0] = d[2] / sum 22 | ax[1] = 0.0 23 | ax[2] = -d[1] / sum 24 | } else { 25 | sum := math.Sqrt(d[0]*d[0] + d[1]*d[1]) 26 | ax[0] = -d[1] / sum 27 | ax[1] = d[0] / sum 28 | ax[2] = 0.0 29 | } 30 | ay[0] = d[1]*ax[2] - d[2]*ax[1] 31 | ay[1] = d[2]*ax[0] - d[0]*ax[2] 32 | ay[2] = d[0]*ax[1] - d[1]*ax[0] 33 | return ax, ay, nil 34 | } 35 | -------------------------------------------------------------------------------- /extruder.go: -------------------------------------------------------------------------------- 1 | package dxf 2 | 3 | import ( 4 | "github.com/yofu/dxf/geometry" 5 | ) 6 | 7 | // Extruder represents an entity with code 210, 220, 230 like Circle. 8 | // 9 | // 210: Extrusion direction (optional; default = 0, 0, 1) 10 | // X value 11 | // 220, 230: Y and Z values of extrusion direction (optional) 12 | type Extruder interface { // 210 220 230 13 | CurrentDirection() []float64 14 | SetDirection([]float64) 15 | CurrentCoord() []float64 16 | SetCoord([]float64) 17 | } 18 | 19 | // SetExtrusion sets new coord acoording to given direction. 20 | func SetExtrusion(e Extruder, d []float64) { 21 | dx, dy, err := geometry.ArbitraryAxis(d) 22 | if err != nil { 23 | return 24 | } 25 | b := e.CurrentDirection() 26 | e.SetDirection(d) 27 | n := e.CurrentCoord() 28 | bx, by, _ := geometry.ArbitraryAxis(b) 29 | before := [][]float64{bx, by, b} 30 | after := [][]float64{dx, dy, d} 31 | newcoord := make([]float64, 3) 32 | for i := 0; i < 3; i++ { 33 | for j := 0; j < 3; j++ { 34 | for k := 0; k < 3; k++ { 35 | newcoord[i] += n[j] * before[j][k] * after[i][k] 36 | } 37 | } 38 | } 39 | e.SetCoord(newcoord) 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2015] [Yoshihiro FUKUSHIMA] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /class/classes.go: -------------------------------------------------------------------------------- 1 | // CLASSES section 2 | package class 3 | 4 | import ( 5 | "github.com/yofu/dxf/format" 6 | ) 7 | 8 | // Class represents each CLASS. 9 | type Class struct { 10 | } 11 | 12 | // Format writes data to formatter. 13 | func (c *Class) Format(f format.Formatter) { 14 | f.WriteString(0, "CLASS") 15 | } 16 | 17 | // String outputs data using default formatter. 18 | func (c *Class) String() string { 19 | f := format.NewASCII() 20 | return c.FormatString(f) 21 | } 22 | 23 | // FormatString outputs data using given formatter. 24 | func (c *Class) FormatString(f format.Formatter) string { 25 | c.Format(f) 26 | return f.Output() 27 | } 28 | 29 | // Classes represents CLASSES section. 30 | type Classes []*Class 31 | 32 | // New creates a new Classes. 33 | func New() Classes { 34 | c := make([]*Class, 0) 35 | return c 36 | } 37 | 38 | // Format writes CLASSES data to formatter. 39 | func (cs Classes) Format(f format.Formatter) { 40 | f.WriteString(0, "SECTION") 41 | f.WriteString(2, "CLASSES") 42 | for _, c := range cs { 43 | c.Format(f) 44 | } 45 | f.WriteString(0, "ENDSEC") 46 | } 47 | 48 | // SetHandle sets handles to each class. 49 | func (cs Classes) SetHandle(v *int) { 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /drawing/section.go: -------------------------------------------------------------------------------- 1 | package drawing 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // Section is interface for DXF sections. 8 | type Section interface { 9 | Format(format.Formatter) 10 | SetHandle(*int) 11 | } 12 | 13 | // SectionType represents Section names (code 2) 14 | type SectionType int 15 | 16 | // Section name: code 2 17 | const ( 18 | HEADER SectionType = iota 19 | CLASSES 20 | TABLES 21 | BLOCKS 22 | ENTITIES 23 | OBJECTS 24 | ) 25 | 26 | // SectionTypeString converts SectionType to string. 27 | // If SectionType is out of range, it returns empty string. 28 | func SectionTypeString(s SectionType) string { 29 | switch s { 30 | case HEADER: 31 | return "HEADER" 32 | case CLASSES: 33 | return "CLASSES" 34 | case TABLES: 35 | return "TABLES" 36 | case BLOCKS: 37 | return "BLOCKS" 38 | case ENTITIES: 39 | return "ENTITIES" 40 | case OBJECTS: 41 | return "OBJECTS" 42 | default: 43 | return "" 44 | } 45 | } 46 | 47 | // SectionTypeValue converts string to SectionType. 48 | // If string is unknown SectionType, it returns -1. 49 | func SectionTypeValue(s string) SectionType { 50 | switch s { 51 | case "HEADER": 52 | return HEADER 53 | case "CLASSES": 54 | return CLASSES 55 | case "TABLES": 56 | return TABLES 57 | case "BLOCKS": 58 | return BLOCKS 59 | case "ENTITIES": 60 | return ENTITIES 61 | case "OBJECTS": 62 | return OBJECTS 63 | default: 64 | return -1 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /table/table_type.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | // TableType represents Table names (code 2) 4 | type TableType int 5 | 6 | // Table name: code 2 7 | const ( 8 | VPORT TableType = iota 9 | LTYPE 10 | LAYER 11 | STYLE 12 | VIEW 13 | UCS 14 | APPID 15 | DIMSTYLE 16 | BLOCK_RECORD 17 | ) 18 | 19 | // TableTypeString converts TableType to string. 20 | // If TableType is out of range, it returns empty string. 21 | func TableTypeString(t TableType) string { 22 | switch t { 23 | case VPORT: 24 | return "VPORT" 25 | case LTYPE: 26 | return "LTYPE" 27 | case LAYER: 28 | return "LAYER" 29 | case STYLE: 30 | return "STYLE" 31 | case VIEW: 32 | return "VIEW" 33 | case UCS: 34 | return "UCS" 35 | case APPID: 36 | return "APPID" 37 | case DIMSTYLE: 38 | return "DIMSTYLE" 39 | case BLOCK_RECORD: 40 | return "BLOCK_RECORD" 41 | default: 42 | return "" 43 | } 44 | } 45 | 46 | // TableTypeValue converts string to TableType. 47 | // If string is unknown TableType, it returns -1. 48 | func TableTypeValue(t string) TableType { 49 | switch t { 50 | case "VPORT": 51 | return VPORT 52 | case "LTYPE": 53 | return LTYPE 54 | case "LAYER": 55 | return LAYER 56 | case "STYLE": 57 | return STYLE 58 | case "VIEW": 59 | return VIEW 60 | case "UCS": 61 | return UCS 62 | case "APPID": 63 | return APPID 64 | case "DIMSTYLE": 65 | return DIMSTYLE 66 | case "BLOCK_RECORD": 67 | return BLOCK_RECORD 68 | default: 69 | return -1 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /entity/point.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // Point represents POINT Entity. 8 | type Point struct { 9 | *entity 10 | Coord []float64 // 10, 20, 30 11 | } 12 | 13 | // IsEntity is for Entity interface. 14 | func (p *Point) IsEntity() bool { 15 | return true 16 | } 17 | 18 | // NewPoint creates a new Point. 19 | func NewPoint(coord ...float64) *Point { 20 | p := &Point{ 21 | entity: NewEntity(POINT), 22 | Coord: []float64{0.0, 0.0, 0.0}, 23 | } 24 | ln := len(coord) 25 | if ln == 0 { 26 | return p 27 | } 28 | if ln < 3 { 29 | for i := ln; i < 3; i++ { 30 | coord = append(coord, 0.0) 31 | } 32 | p.Coord = coord 33 | return p 34 | } 35 | p.Coord = coord[:3] 36 | return p 37 | } 38 | 39 | // Format writes data to formatter. 40 | func (p *Point) Format(f format.Formatter) { 41 | p.entity.Format(f) 42 | f.WriteString(100, "AcDbPoint") 43 | for i := 0; i < 3; i++ { 44 | f.WriteFloat((i+1)*10, p.Coord[i]) 45 | } 46 | } 47 | 48 | // String outputs data using default formatter. 49 | func (p *Point) String() string { 50 | f := format.NewASCII() 51 | return p.FormatString(f) 52 | } 53 | 54 | // FormatString outputs data using given formatter. 55 | func (p *Point) FormatString(f format.Formatter) string { 56 | p.Format(f) 57 | return f.Output() 58 | } 59 | 60 | func (p *Point) BBox() ([]float64, []float64) { 61 | return []float64{p.Coord[0], p.Coord[1], p.Coord[2]}, []float64{p.Coord[0], p.Coord[1], p.Coord[2]} 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #dxf 2 | 3 | DXF(Drawing Exchange Format by Autodesk, Inc) library for golang. 4 | 5 | + version: ACAD2000(AC1015) 6 | + format : ASCII 7 | 8 | is only supported. 9 | 10 | ## Reference 11 | 12 | [http://www.autodesk.com/techpubs/autocad/acad2000/dxf/index.htm](http://www.autodesk.com/techpubs/autocad/acad2000/dxf/index.htm) 13 | 14 | ## Example 15 | ```go 16 | package main 17 | 18 | import ( 19 | "github.com/yofu/dxf" 20 | "github.com/yofu/dxf/color" 21 | "github.com/yofu/dxf/table" 22 | "log" 23 | "math" 24 | ) 25 | 26 | func main() { 27 | d := dxf.NewDrawing() 28 | d.Header().LtScale = 100.0 29 | d.AddLayer("Toroidal", dxf.DefaultColor, dxf.DefaultLineType, true) 30 | d.AddLayer("Poloidal", color.Red, table.LT_HIDDEN, true) 31 | z := 0.0 32 | r1 := 200.0 33 | r2 := 500.0 34 | ndiv := 16 35 | dtheta := 2.0 * math.Pi / float64(ndiv) 36 | theta := 0.0 37 | for i := 0; i < ndiv; i++ { 38 | d.ChangeLayer("Toroidal") 39 | d.Circle(0.0, 0.0, z+r1*math.Cos(theta), r2-r1*math.Sin(theta)) 40 | d.ChangeLayer("Poloidal") 41 | c, _ := d.Circle(r2*math.Cos(theta), r2*math.Sin(theta), 0.0, r1) 42 | dxf.SetExtrusion(c, []float64{-1.0 * math.Sin(theta), math.Cos(theta), 0.0}) 43 | theta += dtheta 44 | } 45 | err := d.SaveAs("torus.dxf") 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | } 50 | ``` 51 | 52 | ![Torus](example/torus/torus.png) 53 | 54 | ## Installation 55 | 56 | ``` 57 | $ go get github.com/yofu/dxf 58 | ``` 59 | 60 | # License 61 | 62 | MIT 63 | 64 | # Author 65 | 66 | Yoshihiro FUKUSHIMA 67 | -------------------------------------------------------------------------------- /object/acdbplaceholder.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // AcDbPlaceHolder represents ACDBPLACEHOLDER Object. 9 | type AcDbPlaceHolder struct { 10 | handle int 11 | owner handle.Handler 12 | } 13 | 14 | // IsObject is for Object interface. 15 | func (p *AcDbPlaceHolder) IsObject() bool { 16 | return true 17 | } 18 | 19 | // NewAcDbPlaceHolder creates a new AcDbPlaceHolder. 20 | func NewAcDbPlaceHolder() *AcDbPlaceHolder { 21 | p := &AcDbPlaceHolder{ 22 | handle: 0, 23 | owner: nil, 24 | } 25 | return p 26 | } 27 | 28 | // Format writes data to formatter. 29 | func (p *AcDbPlaceHolder) Format(f format.Formatter) { 30 | f.WriteString(0, "ACDBPLACEHOLDER") 31 | f.WriteHex(5, p.handle) 32 | if p.owner != nil { 33 | f.WriteString(102, "{ACAD_REACTORS") 34 | f.WriteHex(330, p.owner.Handle()) 35 | f.WriteString(102, "}") 36 | f.WriteHex(330, p.owner.Handle()) 37 | } 38 | } 39 | 40 | // String outputs data using default formatter. 41 | func (p *AcDbPlaceHolder) String() string { 42 | f := format.NewASCII() 43 | return p.FormatString(f) 44 | } 45 | 46 | // FormatString outputs data using given formatter. 47 | func (p *AcDbPlaceHolder) FormatString(f format.Formatter) string { 48 | p.Format(f) 49 | return f.Output() 50 | } 51 | 52 | // Handle returns a handle value. 53 | func (p *AcDbPlaceHolder) Handle() int { 54 | return p.handle 55 | } 56 | 57 | // SetHandle sets a handle. 58 | func (p *AcDbPlaceHolder) SetHandle(v *int) { 59 | p.handle = *v 60 | (*v)++ 61 | } 62 | -------------------------------------------------------------------------------- /handle/handle.go: -------------------------------------------------------------------------------- 1 | // Handler interface 2 | package handle 3 | 4 | // Handler is interface for handle (code 5, 105, etc). 5 | // 5: Entity handle; text string of up to 16 hexadecimal digits (fixed) 6 | // 105: Object handle for DIMVAR symbol table entry 7 | // 320-329: Arbitrary object handles; handle values that are taken "as is." They are not translated during INSERT and XREF operations 8 | // 330-339: Soft-pointer handle; arbitrary soft pointers to other objects within same DXF file or drawing. Translated during INSERT and XREF operations 9 | // 340-349: Hard-pointer handle; arbitrary hard pointers to other objects within same DXF file or drawing. Translated during INSERT and XREF operations 10 | // 350-359: Soft-owner handle; arbitrary soft ownership links to other objects within same DXF file or drawing. Translated during INSERT and XREF operations 11 | // 360-369: Hard-owner handle; arbitrary hard ownership links to other objects within same DXF file or drawing. Translated during INSERT and XREF operations 12 | // 390-399: String representing handle value of the PlotStyleName object, basically a hard pointer, but has a different range to make backward compatibility easier to deal with. 13 | // Stored and moved around as an Object ID (a handle in DXF files) and a special type in AutoLISP. Custom non-entity objects may use the full range, 14 | // but entity classes only use 391-399 DXF group codes in their representation, for the same reason as the Lineweight range above. 15 | type Handler interface { 16 | Handle() int 17 | SetHandle(*int) 18 | } 19 | -------------------------------------------------------------------------------- /entity/vertex.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // Vertex represents VERTEX Entity. 8 | type Vertex struct { 9 | *entity 10 | Flag int 11 | Coord []float64 12 | Buldge float64 13 | } 14 | 15 | // IsEntity is for Entity interface. 16 | func (v *Vertex) IsEntity() bool { 17 | return true 18 | } 19 | 20 | // NewVertex creates a new Vertex. 21 | func NewVertex(x, y, z float64) *Vertex { 22 | v := &Vertex{ 23 | entity: NewEntity(VERTEX), 24 | Flag: 32, 25 | Coord: []float64{x, y, z}, 26 | } 27 | return v 28 | } 29 | 30 | func NewDefaultVertex() *Vertex { 31 | return NewVertex(0, 0, 0) 32 | } 33 | 34 | // Format writes data to formatter. 35 | func (v *Vertex) Format(f format.Formatter) { 36 | v.entity.Format(f) 37 | f.WriteString(100, "AcDbVertex") 38 | f.WriteString(100, "AcDb3dPolylineVertex") 39 | for i := 0; i < 3; i++ { 40 | f.WriteFloat((i+1)*10, v.Coord[i]) 41 | } 42 | f.WriteInt(70, v.Flag) 43 | 44 | if v.Buldge != 0 { 45 | f.WriteFloat(42, v.Buldge) 46 | } 47 | } 48 | 49 | // String outputs data using default formatter. 50 | func (v *Vertex) String() string { 51 | f := format.NewASCII() 52 | return v.FormatString(f) 53 | } 54 | 55 | // FormatString outputs data using given formatter. 56 | func (v *Vertex) FormatString(f format.Formatter) string { 57 | v.Format(f) 58 | return f.Output() 59 | } 60 | 61 | func (v *Vertex) BBox() ([]float64, []float64) { 62 | mins := []float64{v.Coord[0], v.Coord[1], v.Coord[2]} 63 | maxs := []float64{v.Coord[0], v.Coord[1], v.Coord[2]} 64 | return mins, maxs 65 | } 66 | -------------------------------------------------------------------------------- /entity/entity_type.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // EntityType represents Entity names (code 2) 4 | type EntityType int 5 | 6 | // Entity name: code 2 7 | const ( 8 | LINE EntityType = iota 9 | THREEDFACE 10 | LWPOLYLINE 11 | CIRCLE 12 | POLYLINE 13 | VERTEX 14 | POINT 15 | ARC 16 | TEXT 17 | SPLINE 18 | ) 19 | 20 | // EntityTypeString converts EntityType to string. 21 | // If EntityType is out of range, it returns empty string. 22 | func EntityTypeString(t EntityType) string { 23 | switch t { 24 | case LINE: 25 | return "LINE" 26 | case THREEDFACE: 27 | return "3DFACE" 28 | case LWPOLYLINE: 29 | return "LWPOLYLINE" 30 | case CIRCLE: 31 | return "CIRCLE" 32 | case POLYLINE: 33 | return "POLYLINE" 34 | case VERTEX: 35 | return "VERTEX" 36 | case POINT: 37 | return "POINT" 38 | case ARC: 39 | return "ARC" 40 | case TEXT: 41 | return "TEXT" 42 | case SPLINE: 43 | return "SPLINE" 44 | default: 45 | return "" 46 | } 47 | } 48 | 49 | // EntityTypeValue converts string to EntityType. 50 | // If string is unknown EntityType, it returns -1. 51 | func EntityTypeValue(t string) EntityType { 52 | switch t { 53 | case "LINE": 54 | return LINE 55 | case "3DFACE": 56 | return THREEDFACE 57 | case "LWPOLYLINE": 58 | return LWPOLYLINE 59 | case "CIRCLE": 60 | return CIRCLE 61 | case "POLYLINE": 62 | return POLYLINE 63 | case "VERTEX": 64 | return VERTEX 65 | case "POINT": 66 | return POINT 67 | case "ARC": 68 | return ARC 69 | case "TEXT": 70 | return TEXT 71 | case "SPLINE": 72 | return SPLINE 73 | default: 74 | return -1 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /table/tables.go: -------------------------------------------------------------------------------- 1 | // TABLES section 2 | package table 3 | 4 | import ( 5 | "github.com/yofu/dxf/format" 6 | ) 7 | 8 | // Tables represents TABLES section. 9 | type Tables []*Table 10 | 11 | // New creates a new Tables. 12 | func New() Tables { 13 | t := make([]*Table, 9) 14 | t[0] = NewTable("VPORT") 15 | t[1] = NewTable("LTYPE") 16 | t[1].Add(LT_BYLAYER) 17 | t[1].Add(LT_BYBLOCK) 18 | t[1].Add(LT_CONTINUOUS) 19 | t[1].Add(LT_HIDDEN) 20 | t[1].Add(LT_DASHDOT) 21 | t[2] = NewTable("LAYER") 22 | t[2].Add(LY_0) 23 | t[3] = NewTable("STYLE") 24 | t[3].Add(ST_STANDARD) 25 | t[4] = NewTable("VIEW") 26 | t[5] = NewTable("UCS") 27 | t[6] = NewTable("APPID") 28 | t[6].Add(NewAppID("ACAD")) 29 | t[7] = NewTable("DIMSTYLE") 30 | t[8] = NewTable("BLOCK_RECORD") 31 | t[8].Add(NewBlockRecord("*Model_Space")) 32 | t[8].Add(NewBlockRecord("*Paper_Space")) 33 | t[8].Add(NewBlockRecord("*Paper_Space0")) 34 | return t 35 | } 36 | 37 | // Format writes TABLES data to formatter. 38 | func (ts Tables) Format(f format.Formatter) { 39 | f.WriteString(0, "SECTION") 40 | f.WriteString(2, "TABLES") 41 | for _, t := range ts { 42 | t.Format(f) 43 | } 44 | f.WriteString(0, "ENDSEC") 45 | } 46 | 47 | // Add adds a new table to TABLES section. 48 | func (ts Tables) Add(t *Table) Tables { 49 | ts = append(ts, t) 50 | return ts 51 | } 52 | 53 | // SetHandle sets handles to each table. 54 | func (ts Tables) SetHandle(h *int) { 55 | for _, t := range ts { 56 | t.SetHandle(h) 57 | } 58 | } 59 | 60 | // AddLayer adds a new layer to LAYER table. 61 | func (ts Tables) AddLayer(l *Layer) { 62 | ts[2].Add(l) 63 | } 64 | -------------------------------------------------------------------------------- /table/ucs.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // UCS represents UCS SymbolTable. 9 | type Ucs struct { 10 | handle int 11 | owner handle.Handler 12 | name string // 2 13 | } 14 | 15 | // NewUCS creates a new Ucs. 16 | func NewUCS(name string) *Ucs { 17 | u := new(Ucs) 18 | u.name = name 19 | return u 20 | } 21 | 22 | // IsSymbolTable is for SymbolTable interface. 23 | func (u *Ucs) IsSymbolTable() bool { 24 | return true 25 | } 26 | 27 | // Format writes data to formatter. 28 | func (u *Ucs) Format(f format.Formatter) { 29 | f.WriteString(0, "UCS") 30 | f.WriteHex(5, u.handle) 31 | if u.owner != nil { 32 | f.WriteHex(330, u.owner.Handle()) 33 | } 34 | f.WriteString(100, "AcDbSymbolTableRecord") 35 | f.WriteString(100, "AcDbUCSTableRecord") 36 | f.WriteString(2, u.name) 37 | } 38 | 39 | // String outputs data using default formatter. 40 | func (u *Ucs) String() string { 41 | f := format.NewASCII() 42 | return u.FormatString(f) 43 | } 44 | 45 | // FormatString outputs data using given formatter. 46 | func (u *Ucs) FormatString(f format.Formatter) string { 47 | u.Format(f) 48 | return f.Output() 49 | } 50 | 51 | // Handle returns a handle value. 52 | func (u *Ucs) Handle() int { 53 | return u.handle 54 | } 55 | 56 | // SetHandle sets a handle. 57 | func (u *Ucs) SetHandle(h *int) { 58 | u.handle = *h 59 | (*h)++ 60 | } 61 | 62 | // SetOwner sets an owner. 63 | func (u *Ucs) SetOwner(h handle.Handler) { 64 | u.owner = h 65 | } 66 | 67 | // Name returns a name of UCS (code 2). 68 | func (u *Ucs) Name() string { 69 | return u.name 70 | } 71 | -------------------------------------------------------------------------------- /table/view.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // View represents VIEW SymbolTable. 9 | type View struct { 10 | handle int 11 | owner handle.Handler 12 | name string // 2 13 | } 14 | 15 | // NewView creates a new View. 16 | func NewView(name string) *View { 17 | v := new(View) 18 | v.name = name 19 | return v 20 | } 21 | 22 | // IsSymbolTable is for SymbolTable interface. 23 | func (v *View) IsSymbolTable() bool { 24 | return true 25 | } 26 | 27 | // Format writes data to formatter. 28 | func (v *View) Format(f format.Formatter) { 29 | f.WriteString(0, "VIEW") 30 | f.WriteHex(5, v.handle) 31 | if v.owner != nil { 32 | f.WriteHex(330, v.owner.Handle()) 33 | } 34 | f.WriteString(100, "AcDbSymbolTableRecord") 35 | f.WriteString(100, "AcDbViewTableRecord") 36 | f.WriteString(2, v.name) 37 | } 38 | 39 | // String outputs data using default formatter. 40 | func (v *View) String() string { 41 | f := format.NewASCII() 42 | return v.FormatString(f) 43 | } 44 | 45 | // FormatString outputs data using given formatter. 46 | func (v *View) FormatString(f format.Formatter) string { 47 | v.Format(f) 48 | return f.Output() 49 | } 50 | 51 | // Handle returns a handle value. 52 | func (v *View) Handle() int { 53 | return v.handle 54 | } 55 | 56 | // SetHandle sets a handle. 57 | func (v *View) SetHandle(h *int) { 58 | v.handle = *h 59 | (*h)++ 60 | } 61 | 62 | // SetOwner sets an owner. 63 | func (v *View) SetOwner(h handle.Handler) { 64 | v.owner = h 65 | } 66 | 67 | // Name returns a name of VIEW (code 2). 68 | func (v *View) Name() string { 69 | return v.name 70 | } 71 | -------------------------------------------------------------------------------- /header/header.go: -------------------------------------------------------------------------------- 1 | // HEADER section 2 | package header 3 | 4 | import ( 5 | "github.com/yofu/dxf/format" 6 | "github.com/yofu/dxf/insunit" 7 | ) 8 | 9 | // Header contains information written in HEADER section. 10 | type Header struct { 11 | Version string 12 | InsBase []float64 13 | InsUnit insunit.Unit 14 | InsLUnit insunit.Type 15 | ExtMin []float64 16 | ExtMax []float64 17 | LtScale float64 18 | handseed int 19 | } 20 | 21 | // New creates a new Header. 22 | func New() *Header { 23 | h := new(Header) 24 | h.Version = "AC1015" 25 | h.InsBase = make([]float64, 3) 26 | h.ExtMin = make([]float64, 3) 27 | h.ExtMax = make([]float64, 3) 28 | h.LtScale = 1.0 29 | return h 30 | } 31 | 32 | // Format writes HEADER information to formatter. 33 | func (h *Header) Format(f format.Formatter) { 34 | f.WriteString(0, "SECTION") 35 | f.WriteString(2, "HEADER") 36 | f.WriteString(9, "$ACADVER") 37 | f.WriteString(1, h.Version) 38 | f.WriteString(9, "$INSBASE") 39 | for i := 0; i < 3; i++ { 40 | f.WriteFloat((i+1)*10, h.InsBase[i]) 41 | } 42 | f.WriteString(9, "$INSUNITS") 43 | h.InsUnit.Format(f) 44 | f.WriteString(9, "$LUNITS") 45 | h.InsLUnit.Format(f) 46 | 47 | f.WriteString(9, "$EXTMIN") 48 | for i := 0; i < 3; i++ { 49 | f.WriteFloat((i+1)*10, h.ExtMin[i]) 50 | } 51 | f.WriteString(9, "$EXTMAX") 52 | for i := 0; i < 3; i++ { 53 | f.WriteFloat((i+1)*10, h.ExtMax[i]) 54 | } 55 | f.WriteString(9, "$LTSCALE") 56 | f.WriteFloat(40, h.LtScale) 57 | f.WriteString(9, "$HANDSEED") 58 | f.WriteHex(5, h.handseed) 59 | f.WriteString(0, "ENDSEC") 60 | } 61 | 62 | // SetHandle sets $HANDSEED. 63 | func (h *Header) SetHandle(v *int) { 64 | h.handseed = *v 65 | } 66 | -------------------------------------------------------------------------------- /table/appid.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // AppID represents APPID SymbolTable. 9 | type AppID struct { 10 | handle int 11 | owner handle.Handler 12 | name string 13 | } 14 | 15 | // NewAppID create a new AppID. 16 | func NewAppID(name string) *AppID { 17 | a := new(AppID) 18 | a.name = name 19 | return a 20 | } 21 | 22 | // IsSymbolTable is for SymbolTable interface. 23 | func (a *AppID) IsSymbolTable() bool { 24 | return true 25 | } 26 | 27 | // Format writes data to formatter. 28 | func (a *AppID) Format(f format.Formatter) { 29 | f.WriteString(0, "APPID") 30 | f.WriteHex(5, a.handle) 31 | if a.owner != nil { 32 | f.WriteHex(330, a.owner.Handle()) 33 | } 34 | f.WriteString(100, "AcDbSymbolTableRecord") 35 | f.WriteString(100, "AcDbRegAppTableRecord") 36 | f.WriteString(2, a.name) 37 | f.WriteInt(70, 0) 38 | } 39 | 40 | // String outputs data using default formatter. 41 | func (a *AppID) String() string { 42 | f := format.NewASCII() 43 | return a.FormatString(f) 44 | } 45 | 46 | // FormatString outputs data using given formatter. 47 | func (a *AppID) FormatString(f format.Formatter) string { 48 | a.Format(f) 49 | return f.Output() 50 | } 51 | 52 | // Handle returns a handle value. 53 | func (a *AppID) Handle() int { 54 | return a.handle 55 | } 56 | 57 | // SetHandle sets a handle. 58 | func (a *AppID) SetHandle(v *int) { 59 | a.handle = *v 60 | (*v)++ 61 | } 62 | 63 | // SetOwner sets an owner. 64 | func (a *AppID) SetOwner(h handle.Handler) { 65 | a.owner = h 66 | } 67 | 68 | // Name returns a name of APPID (code 2). 69 | func (a *AppID) Name() string { 70 | return a.name 71 | } 72 | -------------------------------------------------------------------------------- /table/dimstyle.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // DimStyle represents DIMSTYLE SymbolTable. 9 | type DimStyle struct { 10 | handle int 11 | owner handle.Handler 12 | name string 13 | } 14 | 15 | // NewDimStyle creates a new DimStyle. 16 | func NewDimStyle(name string) *DimStyle { 17 | d := new(DimStyle) 18 | d.name = name 19 | return d 20 | } 21 | 22 | // IsSymbolTable is for SymbolTable interface. 23 | func (d *DimStyle) IsSymbolTable() bool { 24 | return true 25 | } 26 | 27 | // Format writes data to formatter. 28 | func (d *DimStyle) Format(f format.Formatter) { 29 | f.WriteString(0, "DIMSTYLE") 30 | f.WriteHex(105, d.handle) 31 | if d.owner != nil { 32 | f.WriteHex(330, d.owner.Handle()) 33 | } 34 | f.WriteString(100, "AcDbSymbolTableRecord") 35 | f.WriteString(100, "AcDbDimStyleTableRecord") 36 | f.WriteString(2, d.name) 37 | f.WriteInt(70, 0) 38 | } 39 | 40 | // String outputs data using default formatter. 41 | func (d *DimStyle) String() string { 42 | f := format.NewASCII() 43 | return d.FormatString(f) 44 | } 45 | 46 | // FormatString outputs data using given formatter. 47 | func (d *DimStyle) FormatString(f format.Formatter) string { 48 | d.Format(f) 49 | return f.Output() 50 | } 51 | 52 | // Handle returns a handle value. 53 | func (d *DimStyle) Handle() int { 54 | return d.handle 55 | } 56 | 57 | // SetHandle sets a handle. 58 | func (d *DimStyle) SetHandle(v *int) { 59 | d.handle = *v 60 | (*v)++ 61 | } 62 | 63 | // SetOwner sets an owner. 64 | func (d *DimStyle) SetOwner(h handle.Handler) { 65 | d.owner = h 66 | } 67 | 68 | // Name returns a name of DIMSTYLE (code 2). 69 | func (d *DimStyle) Name() string { 70 | return d.name 71 | } 72 | -------------------------------------------------------------------------------- /entity/3dface.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // ThreeDFace represents 3DFACE Entity. 8 | type ThreeDFace struct { 9 | *entity 10 | Points [][]float64 11 | Flag int // 70 12 | } 13 | 14 | // IsEntity is for Entity interface. 15 | func (f *ThreeDFace) IsEntity() bool { 16 | return true 17 | } 18 | 19 | // New3DFace creates a new ThreeDFace. 20 | func New3DFace() *ThreeDFace { 21 | f := &ThreeDFace{ 22 | entity: NewEntity(THREEDFACE), 23 | Points: [][]float64{[]float64{0.0, 0.0, 0.0}, 24 | []float64{0.0, 0.0, 0.0}, 25 | []float64{0.0, 0.0, 0.0}, 26 | []float64{0.0, 0.0, 0.0}, 27 | }, 28 | Flag: 0, 29 | } 30 | return f 31 | } 32 | 33 | // Format writes data to formatter. 34 | func (f *ThreeDFace) Format(fm format.Formatter) { 35 | f.entity.Format(fm) 36 | fm.WriteString(100, "AcDbFace") 37 | for i := 0; i < 4; i++ { 38 | for j := 0; j < 3; j++ { 39 | fm.WriteFloat((j+1)*10+i, f.Points[i][j]) 40 | } 41 | } 42 | if f.Flag != 0 { 43 | fm.WriteInt(70, f.Flag) 44 | } 45 | } 46 | 47 | // String outputs data using default formatter. 48 | func (f *ThreeDFace) String() string { 49 | fm := format.NewASCII() 50 | return f.FormatString(fm) 51 | } 52 | 53 | // FormatString outputs data using given formatter. 54 | func (f *ThreeDFace) FormatString(fm format.Formatter) string { 55 | f.Format(fm) 56 | return fm.Output() 57 | } 58 | 59 | func (f *ThreeDFace) BBox() ([]float64, []float64) { 60 | mins := make([]float64, 3) 61 | maxs := make([]float64, 3) 62 | for _, p := range f.Points { 63 | for i := 0; i < 3; i++ { 64 | if p[i] < mins[i] { 65 | mins[i] = p[i] 66 | } 67 | if p[i] > maxs[i] { 68 | maxs[i] = p[i] 69 | } 70 | } 71 | } 72 | return mins, maxs 73 | } 74 | -------------------------------------------------------------------------------- /table/block_record.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // BlockRecord represents BLOCK_RECORD SymbolTable. 9 | type BlockRecord struct { 10 | handle int 11 | owner handle.Handler 12 | name string 13 | } 14 | 15 | // NewBlockRecord creates a new BlockRecord. 16 | func NewBlockRecord(name string) *BlockRecord { 17 | b := new(BlockRecord) 18 | b.name = name 19 | return b 20 | } 21 | 22 | // IsSymbolTable is for SymbolTable interface. 23 | func (b *BlockRecord) IsSymbolTable() bool { 24 | return true 25 | } 26 | 27 | // Format writes data to formatter. 28 | func (b *BlockRecord) Format(f format.Formatter) { 29 | f.WriteString(0, "BLOCK_RECORD") 30 | f.WriteHex(5, b.handle) 31 | if b.owner != nil { 32 | f.WriteHex(330, b.owner.Handle()) 33 | } 34 | f.WriteString(100, "AcDbSymbolTableRecord") 35 | f.WriteString(100, "AcDbBlockTableRecord") 36 | f.WriteString(2, b.name) 37 | f.WriteInt(70, 0) 38 | f.WriteInt(280, 1) 39 | f.WriteInt(281, 0) 40 | } 41 | 42 | // String outputs data using default formatter. 43 | func (b *BlockRecord) String() string { 44 | f := format.NewASCII() 45 | return b.FormatString(f) 46 | } 47 | 48 | // FormatString outputs data using given formatter. 49 | func (b *BlockRecord) FormatString(f format.Formatter) string { 50 | b.Format(f) 51 | return f.Output() 52 | } 53 | 54 | // Handle returns a handle value. 55 | func (b *BlockRecord) Handle() int { 56 | return b.handle 57 | } 58 | 59 | // SetHandle sets a handle. 60 | func (b *BlockRecord) SetHandle(v *int) { 61 | b.handle = *v 62 | (*v)++ 63 | } 64 | 65 | // SetOwner sets an owner. 66 | func (b *BlockRecord) SetOwner(h handle.Handler) { 67 | b.owner = h 68 | } 69 | 70 | // Name returns a name of BLOCK_RECORD (code 2). 71 | func (b *BlockRecord) Name() string { 72 | return b.name 73 | } 74 | -------------------------------------------------------------------------------- /entity/entities.go: -------------------------------------------------------------------------------- 1 | // ENTITIES section 2 | package entity 3 | 4 | import ( 5 | "math" 6 | 7 | "github.com/yofu/dxf/format" 8 | ) 9 | 10 | // Entities represents ENTITIES section. 11 | type Entities []Entity 12 | 13 | // New creates a new Entities. 14 | func New() Entities { 15 | e := make([]Entity, 0) 16 | return e 17 | } 18 | 19 | // Format writes ENTITIES data to formatter. 20 | func (es Entities) Format(f format.Formatter) { 21 | f.WriteString(0, "SECTION") 22 | f.WriteString(2, "ENTITIES") 23 | for _, e := range es { 24 | e.Format(f) 25 | } 26 | f.WriteString(0, "ENDSEC") 27 | } 28 | 29 | // Add adds a new entity to ENTITIES section. 30 | func (es Entities) Add(e Entity) Entities { 31 | es = append(es, e) 32 | return es 33 | } 34 | 35 | // SetHandle sets handles to each entity. 36 | func (es Entities) SetHandle(v *int) { 37 | for _, e := range es { 38 | e.SetHandle(v) 39 | } 40 | } 41 | 42 | // bulge computes the center and the ray of arc between 2 vertices. 43 | func bulge(bulge float64, start []float64, end []float64) ([]float64, float64) { 44 | if bulge == 0 { 45 | return nil, 0 46 | } 47 | 48 | verticesDistance := math.Sqrt(math.Pow(end[0]-start[0], 2) + math.Pow(end[1]-start[1], 2)) 49 | 50 | teta := math.Atan(math.Abs(bulge)) * 4 51 | 52 | radius := verticesDistance / (2 * math.Sin(teta/2)) 53 | 54 | h := math.Sqrt(radius*radius - verticesDistance*verticesDistance/4) 55 | 56 | solutions := [][]float64{ 57 | { 58 | 0.5*(end[0]-start[0]) + (h/verticesDistance)*(end[1]-start[1]) + start[0], 59 | 0.5*(end[1]-start[1]) - (h/verticesDistance)*(end[0]-start[0]) + start[1], 60 | }, 61 | { 62 | 0.5*(end[0]-start[0]) - (h/verticesDistance)*(end[1]-start[1]) + start[0], 63 | 0.5*(end[1]-start[1]) + (h/verticesDistance)*(end[0]-start[0]) + start[1], 64 | }, 65 | } 66 | 67 | firstSolutionIsLeft := (end[0]-start[0])*(solutions[0][1]-start[1])-(end[1]-start[1])*(solutions[0][0]-start[0]) > 0 68 | 69 | if firstSolutionIsLeft && bulge > 0 { 70 | return solutions[1], radius 71 | } 72 | 73 | return solutions[0], radius 74 | } 75 | -------------------------------------------------------------------------------- /object/dictionary.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "github.com/yofu/dxf/format" 8 | "github.com/yofu/dxf/handle" 9 | ) 10 | 11 | // Dictionary represents DICTIONARY Object. 12 | type Dictionary struct { 13 | handle int 14 | item map[string]handle.Handler 15 | } 16 | 17 | // IsObject is for Object interface. 18 | func (d *Dictionary) IsObject() bool { 19 | return true 20 | } 21 | 22 | // NewDictionary creates a new Dictionary. 23 | func NewDictionary() *Dictionary { 24 | ds := make(map[string]handle.Handler) 25 | d := &Dictionary{ 26 | handle: 0, 27 | item: ds, 28 | } 29 | return d 30 | } 31 | 32 | // Format writes data to formatter. 33 | func (d *Dictionary) Format(f format.Formatter) { 34 | f.WriteString(0, "DICTIONARY") 35 | f.WriteHex(5, d.handle) 36 | f.WriteString(100, "AcDbDictionary") 37 | f.WriteInt(281, 1) 38 | keys := make([]string, len(d.item)) 39 | { 40 | idx := 0 41 | for k := range d.item { 42 | keys[idx] = k 43 | idx++ 44 | } 45 | } 46 | sort.Strings(keys) 47 | 48 | for _, k := range keys { 49 | f.WriteString(3, k) 50 | f.WriteHex(350, d.item[k].Handle()) 51 | } 52 | } 53 | 54 | // String outputs data using default formatter. 55 | func (d *Dictionary) String() string { 56 | f := format.NewASCII() 57 | return d.FormatString(f) 58 | } 59 | 60 | // FormatString outputs data using given formatter. 61 | func (d *Dictionary) FormatString(f format.Formatter) string { 62 | d.Format(f) 63 | return f.Output() 64 | } 65 | 66 | // Handle returns a handle value. 67 | func (d *Dictionary) Handle() int { 68 | return d.handle 69 | } 70 | 71 | // SetHandle sets a handle. 72 | func (d *Dictionary) SetHandle(v *int) { 73 | d.handle = *v 74 | (*v)++ 75 | for _, val := range d.item { 76 | val.SetHandle(v) 77 | } 78 | } 79 | 80 | // AddItem adds new a new item to Dictionary. 81 | func (d *Dictionary) AddItem(key string, value handle.Handler) error { 82 | if _, exist := d.item[key]; exist { 83 | return fmt.Errorf("key %s already exists", key) 84 | } 85 | d.item[key] = value 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /entity/circle.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // Circle represents CIRCLE Entity. 8 | type Circle struct { 9 | *entity 10 | Center []float64 // 10, 20, 30 11 | Radius float64 // 40 12 | Direction []float64 // 210, 220, 230 13 | } 14 | 15 | // IsEntity is for Entity interface. 16 | func (c *Circle) IsEntity() bool { 17 | return true 18 | } 19 | 20 | // NewCircle creates a new Circle. 21 | func NewCircle() *Circle { 22 | c := &Circle{ 23 | entity: NewEntity(CIRCLE), 24 | Center: []float64{0.0, 0.0, 0.0}, 25 | Radius: 0.0, 26 | Direction: []float64{0.0, 0.0, 1.0}, 27 | } 28 | return c 29 | } 30 | 31 | // Format writes data to formatter. 32 | func (c *Circle) Format(f format.Formatter) { 33 | c.entity.Format(f) 34 | f.WriteString(100, "AcDbCircle") 35 | for i := 0; i < 3; i++ { 36 | f.WriteFloat((i+1)*10, c.Center[i]) 37 | } 38 | f.WriteFloat(40, c.Radius) 39 | for i := 0; i < 3; i++ { 40 | f.WriteFloat(200+(i+1)*10, c.Direction[i]) 41 | } 42 | } 43 | 44 | // String outputs data using default formatter. 45 | func (c *Circle) String() string { 46 | f := format.NewASCII() 47 | return c.FormatString(f) 48 | } 49 | 50 | // FormatString outputs data using given formatter. 51 | func (c *Circle) FormatString(f format.Formatter) string { 52 | c.Format(f) 53 | return f.Output() 54 | } 55 | 56 | // CurrentDirection returns extrusion direction. 57 | func (c *Circle) CurrentDirection() []float64 { 58 | return c.Direction 59 | } 60 | 61 | // SetDirection sets new extrusion direction. 62 | func (c *Circle) SetDirection(d []float64) { 63 | c.Direction = d 64 | } 65 | 66 | // CurrentCoord returns center point coord. 67 | func (c *Circle) CurrentCoord() []float64 { 68 | return c.Center 69 | } 70 | 71 | // SetCoord sets new center point coord. 72 | func (c *Circle) SetCoord(co []float64) { 73 | c.Center = co 74 | } 75 | 76 | func (c *Circle) BBox() ([]float64, []float64) { 77 | // TODO: extrusion 78 | return []float64{c.Center[0] - c.Radius, c.Center[1] - c.Radius, c.Center[2]}, []float64{c.Center[0] + c.Radius, c.Center[1] + c.Radius, c.Center[2]} 79 | } 80 | -------------------------------------------------------------------------------- /block/block.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/table" 6 | ) 7 | 8 | // Block represents each BLOCK. 9 | type Block struct { 10 | Name string 11 | Description string 12 | handle int 13 | endhandle int 14 | layer *table.Layer 15 | Flag int 16 | Coord []float64 17 | } 18 | 19 | // NewBlock create a new Block. 20 | func NewBlock(name, desc string) *Block { 21 | b := &Block{ 22 | Name: name, 23 | Description: desc, 24 | handle: 0, 25 | layer: table.LY_0, 26 | Flag: 0, 27 | Coord: []float64{0.0, 0.0, 0.0}, 28 | } 29 | return b 30 | } 31 | 32 | // Format writes data to formatter. 33 | func (b *Block) Format(f format.Formatter) { 34 | f.WriteString(0, "BLOCK") 35 | f.WriteHex(5, b.handle) 36 | f.WriteString(100, "AcDbEntity") 37 | f.WriteString(8, b.layer.Name()) 38 | f.WriteString(100, "AcDbBlockBegin") 39 | f.WriteString(2, b.Name) 40 | f.WriteInt(70, b.Flag) 41 | for i := 0; i < 3; i++ { 42 | f.WriteFloat((i+1)*10, b.Coord[i]) 43 | } 44 | f.WriteString(3, b.Name) 45 | f.WriteString(1, b.Description) 46 | f.WriteString(0, "ENDBLK") 47 | f.WriteHex(5, b.endhandle) 48 | f.WriteString(100, "AcDbEntity") 49 | f.WriteString(8, b.layer.Name()) 50 | f.WriteString(100, "AcDbBlockEnd") 51 | } 52 | 53 | // String outputs data using default formatter. 54 | func (b *Block) String() string { 55 | f := format.NewASCII() 56 | return b.FormatString(f) 57 | } 58 | 59 | // FormatString outputs data using given formatter. 60 | func (b *Block) FormatString(f format.Formatter) string { 61 | b.Format(f) 62 | return f.Output() 63 | } 64 | 65 | // Handle returns a handle value of BLOCK. 66 | func (b *Block) Handle() int { 67 | return b.handle 68 | } 69 | 70 | // SetHandle sets handles to BLOCK and ENDBLK. 71 | func (b *Block) SetHandle(v *int) { 72 | b.handle = *v 73 | (*v)++ 74 | b.endhandle = *v 75 | (*v)++ 76 | } 77 | 78 | // Layer returns BLOCK's Layer. 79 | func (b *Block) Layer() *table.Layer { 80 | return b.layer 81 | } 82 | 83 | // SetLayer sets Layer to BLOCK. 84 | func (b *Block) SetLayer(layer *table.Layer) { 85 | b.layer = layer 86 | } 87 | -------------------------------------------------------------------------------- /object/group.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "github.com/yofu/dxf/entity" 5 | "github.com/yofu/dxf/format" 6 | "github.com/yofu/dxf/handle" 7 | ) 8 | 9 | // Group represents GROUP Object. 10 | type Group struct { 11 | Name string 12 | Description string 13 | handle int 14 | owner handle.Handler 15 | entities []entity.Entity 16 | selectable bool 17 | } 18 | 19 | // IsObject is for Object interface. 20 | func (g *Group) IsObject() bool { 21 | return true 22 | } 23 | 24 | // NewGroup creates a new Group. 25 | func NewGroup(name, desc string, es ...entity.Entity) *Group { 26 | g := &Group{ 27 | Name: name, 28 | Description: desc, 29 | handle: 0, 30 | owner: nil, 31 | entities: es, 32 | selectable: true, 33 | } 34 | return g 35 | } 36 | 37 | // SetOwner sets an owner(Dictionary). 38 | func (g *Group) SetOwner(d *Dictionary) { 39 | g.owner = d 40 | d.AddItem(g.Name, g) 41 | } 42 | 43 | // Format writes data to formatter. 44 | func (g *Group) Format(f format.Formatter) { 45 | f.WriteString(0, "GROUP") 46 | f.WriteHex(5, g.handle) 47 | f.WriteString(102, "{ACAD_REACTORS") 48 | f.WriteHex(330, g.owner.Handle()) 49 | f.WriteString(102, "}") 50 | f.WriteHex(330, g.owner.Handle()) 51 | f.WriteString(100, "AcDbGroup") 52 | f.WriteString(300, g.Description) 53 | f.WriteInt(70, 0) 54 | if g.selectable { 55 | f.WriteInt(71, 1) 56 | } else { 57 | f.WriteInt(71, 0) 58 | } 59 | for _, e := range g.entities { 60 | f.WriteHex(340, e.Handle()) 61 | } 62 | } 63 | 64 | // String outputs data using default formatter. 65 | func (g *Group) String() string { 66 | f := format.NewASCII() 67 | return g.FormatString(f) 68 | } 69 | 70 | // FormatString outputs data using given formatter. 71 | func (g *Group) FormatString(f format.Formatter) string { 72 | g.Format(f) 73 | return f.Output() 74 | } 75 | 76 | // Handle returns a handle value. 77 | func (g *Group) Handle() int { 78 | return g.handle 79 | } 80 | 81 | // SetHandle sets a handle. 82 | func (g *Group) SetHandle(v *int) { 83 | g.handle = *v 84 | (*v)++ 85 | } 86 | 87 | // AddEntity adds entities to Group. 88 | func (g *Group) AddEntity(es ...entity.Entity) { 89 | for _, e := range es { 90 | e.SetBlockRecord(g) 91 | } 92 | g.entities = append(g.entities, es...) 93 | } 94 | -------------------------------------------------------------------------------- /table/table.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/yofu/dxf/format" 8 | ) 9 | 10 | // Table represents each TABLE. 11 | type Table struct { 12 | name string // 2 13 | handle int // 5 14 | size int // 70 15 | tables []SymbolTable 16 | } 17 | 18 | // NewTable creates a new Table. 19 | func NewTable(name string) *Table { 20 | t := new(Table) 21 | t.name = name 22 | return t 23 | } 24 | 25 | // Format writes data to formatter. 26 | func (t *Table) Format(f format.Formatter) { 27 | f.WriteString(0, "TABLE") 28 | f.WriteString(2, t.name) 29 | f.WriteHex(5, t.handle) 30 | f.WriteString(100, "AcDbSymbolTable") 31 | f.WriteInt(70, t.size) 32 | if t.name == "DIMSTYLE" { 33 | f.WriteString(100, "AcDbDimStyleTable") 34 | f.WriteInt(71, t.size) 35 | for i := 0; i < t.size; i++ { 36 | f.WriteHex(340, t.tables[i].Handle()) 37 | } 38 | } 39 | for i := 0; i < t.size; i++ { 40 | t.tables[i].Format(f) 41 | } 42 | f.WriteString(0, "ENDTAB") 43 | } 44 | 45 | // String outputs data using default formatter. 46 | func (t *Table) String() string { 47 | f := format.NewASCII() 48 | return t.FormatString(f) 49 | } 50 | 51 | // FormatString outputs data using given formatter. 52 | func (t *Table) FormatString(f format.Formatter) string { 53 | t.Format(f) 54 | return f.Output() 55 | } 56 | 57 | // Handle returns a handle value of TABLE. 58 | func (t *Table) Handle() int { 59 | return t.handle 60 | } 61 | 62 | // SetHandle sets handles to TABLE itself and each SymbolTable. 63 | func (t *Table) SetHandle(v *int) { 64 | t.handle = *v 65 | (*v)++ 66 | for i := 0; i < t.size; i++ { 67 | t.tables[i].SetHandle(v) 68 | } 69 | } 70 | 71 | // Add adds a new SymbolTable to TABLE. 72 | func (t *Table) Add(st SymbolTable) { 73 | t.tables = append(t.tables, st) 74 | st.SetOwner(t) 75 | t.size++ 76 | } 77 | 78 | // Clear deletes existing SymbolTables. 79 | func (t *Table) Clear() { 80 | t.tables = make([]SymbolTable, 0) 81 | t.size = 0 82 | } 83 | 84 | // Contains reports if TABLE has the named SymbolTable. 85 | func (t *Table) Contains(name string) (SymbolTable, error) { 86 | for _, st := range t.tables { 87 | if strings.EqualFold(st.Name(), name) { 88 | return st, nil 89 | } 90 | } 91 | return nil, fmt.Errorf("%s doesn't exist", name) 92 | } 93 | -------------------------------------------------------------------------------- /format/ascii.go: -------------------------------------------------------------------------------- 1 | package format 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | // ASCII is Formatter for ASCII format. 10 | type ASCII struct { 11 | buffer bytes.Buffer 12 | float string 13 | } 14 | 15 | // NewASCII creates a new ASCII formatter. 16 | func NewASCII() *ASCII { 17 | var b bytes.Buffer 18 | return &ASCII{ 19 | buffer: b, 20 | float: "%.6f", 21 | } 22 | } 23 | 24 | // Reset resets the buffer. 25 | func (f *ASCII) Reset() { 26 | f.buffer.Reset() 27 | } 28 | 29 | // WriteTo writes data stored in the buffer to w. 30 | func (f *ASCII) WriteTo(w io.Writer) (int64, error) { 31 | return f.buffer.WriteTo(w) 32 | } 33 | 34 | // SetPrecision sets precision part for outputting floating point values. 35 | func (f *ASCII) SetPrecision(p int) { 36 | f.float = fmt.Sprintf("%%.%df", p) 37 | } 38 | 39 | // Output outputs data stored in the buffer in DXF format. 40 | func (f *ASCII) Output() string { 41 | rtn := f.buffer.String() 42 | f.buffer.Reset() 43 | return rtn 44 | } 45 | 46 | // String outputs given code & string in DXF format. 47 | func (f *ASCII) String(num int, val string) string { 48 | return fmt.Sprintf("%d\n%s\n", num, val) 49 | } 50 | 51 | // Hex outputs given code & hex in DXF format. 52 | // It is used for outputting handles. 53 | func (f *ASCII) Hex(num int, h int) string { 54 | return fmt.Sprintf("%d\n%X\n", num, h) 55 | } 56 | 57 | // Int outputs given code & int in DXF format. 58 | func (f *ASCII) Int(num int, val int) string { 59 | return fmt.Sprintf("%d\n%d\n", num, val) 60 | } 61 | 62 | // Float outputs given code & floating point in DXF format. 63 | func (f *ASCII) Float(num int, val float64) string { 64 | return fmt.Sprintf(fmt.Sprintf("%d\n%s\n", num, f.float), val) 65 | } 66 | 67 | // WriteString appends string data to the buffer. 68 | func (f *ASCII) WriteString(num int, val string) { 69 | f.buffer.WriteString(f.String(num, val)) 70 | } 71 | 72 | // WriteHex appends hex data to the buffer. 73 | func (f *ASCII) WriteHex(num int, h int) { 74 | f.buffer.WriteString(f.Hex(num, h)) 75 | } 76 | 77 | // WriteInt appends int data to the buffer. 78 | func (f *ASCII) WriteInt(num int, val int) { 79 | f.buffer.WriteString(f.Int(num, val)) 80 | } 81 | 82 | // WriteFloat appends floating point data to the buffer. 83 | func (f *ASCII) WriteFloat(num int, val float64) { 84 | f.buffer.WriteString(f.Float(num, val)) 85 | } 86 | -------------------------------------------------------------------------------- /entity/line.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/yofu/dxf/format" 7 | ) 8 | 9 | // Line represents LINE Entity. 10 | type Line struct { 11 | *entity 12 | Start []float64 // 10, 20, 30 13 | End []float64 // 11, 21, 31 14 | } 15 | 16 | // IsEntity is for Entity interface. 17 | func (l *Line) IsEntity() bool { 18 | return true 19 | } 20 | 21 | // NewLine creates a new Line. 22 | func NewLine() *Line { 23 | l := &Line{ 24 | entity: NewEntity(LINE), 25 | Start: []float64{0.0, 0.0, 0.0}, 26 | End: []float64{0.0, 0.0, 0.0}, 27 | } 28 | return l 29 | } 30 | 31 | // Format writes data to formatter. 32 | func (l *Line) Format(f format.Formatter) { 33 | l.entity.Format(f) 34 | f.WriteString(100, "AcDbLine") 35 | for i := 0; i < 3; i++ { 36 | f.WriteFloat((i+1)*10, l.Start[i]) 37 | } 38 | for i := 0; i < 3; i++ { 39 | f.WriteFloat((i+1)*10+1, l.End[i]) 40 | } 41 | } 42 | 43 | // String outputs data using default formatter. 44 | func (l *Line) String() string { 45 | f := format.NewASCII() 46 | return l.FormatString(f) 47 | } 48 | 49 | // FormatString outputs data using given formatter. 50 | func (l *Line) FormatString(f format.Formatter) string { 51 | l.Format(f) 52 | return f.Output() 53 | } 54 | 55 | func (l *Line) BBox() ([]float64, []float64) { 56 | mins := make([]float64, 3) 57 | maxs := make([]float64, 3) 58 | for i := 0; i < 3; i++ { 59 | if l.Start[i] <= l.End[i] { 60 | mins[i] = l.Start[i] 61 | maxs[i] = l.End[i] 62 | } else { 63 | mins[i] = l.End[i] 64 | maxs[i] = l.Start[i] 65 | } 66 | } 67 | return mins, maxs 68 | } 69 | 70 | func (l *Line) Length() float64 { 71 | sum := 0.0 72 | for i := 0; i < 3; i++ { 73 | sum += math.Pow(l.End[i]-l.Start[i], 2.0) 74 | } 75 | return math.Sqrt(sum) 76 | } 77 | 78 | func (l *Line) Direction(normalize bool) []float64 { 79 | vec := make([]float64, 3) 80 | var length float64 81 | if normalize { 82 | length = l.Length() 83 | if length == 0.0 { 84 | length = 1.0 85 | } 86 | } 87 | for i := 0; i < 3; i++ { 88 | vec[i] = l.End[i] - l.Start[i] 89 | if normalize { 90 | vec[i] /= length 91 | } 92 | } 93 | return vec 94 | } 95 | 96 | func (l *Line) Move(x, y, z float64) { 97 | for i, val := range []float64{x, y, z} { 98 | l.Start[i] += val 99 | l.End[i] += val 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /entity/lwpolyline.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // LwPolyline represents LWPOLYLINE Entity. 8 | type LwPolyline struct { 9 | *entity 10 | Num int // 90 11 | Closed bool 12 | Vertices [][]float64 13 | Bulges []float64 // 42 14 | } 15 | 16 | // IsEntity is for Entity interface. 17 | func (l *LwPolyline) IsEntity() bool { 18 | return true 19 | } 20 | 21 | // NewLwPolyline creates a new LwPolyline. 22 | func NewLwPolyline(size int) *LwPolyline { 23 | vs := make([][]float64, size) 24 | for i := 0; i < size; i++ { 25 | vs[i] = make([]float64, 2) 26 | } 27 | l := &LwPolyline{ 28 | entity: NewEntity(LWPOLYLINE), 29 | Num: size, 30 | Closed: false, 31 | Vertices: vs, 32 | Bulges: make([]float64, size), 33 | } 34 | return l 35 | } 36 | 37 | // Format writes data to formatter. 38 | func (l *LwPolyline) Format(f format.Formatter) { 39 | l.entity.Format(f) 40 | f.WriteString(100, "AcDbPolyline") 41 | f.WriteInt(90, l.Num) 42 | if l.Closed { 43 | f.WriteInt(70, 1) 44 | } else { 45 | f.WriteInt(70, 0) 46 | } 47 | for i := 0; i < l.Num; i++ { 48 | for j := 0; j < 2; j++ { 49 | f.WriteFloat((j+1)*10, l.Vertices[i][j]) 50 | } 51 | 52 | if l.Bulges[i] != 0 { 53 | f.WriteFloat(42, l.Bulges[i]) 54 | } 55 | } 56 | } 57 | 58 | // String outputs data using default formatter. 59 | func (l *LwPolyline) String() string { 60 | f := format.NewASCII() 61 | return l.FormatString(f) 62 | } 63 | 64 | // FormatString outputs data using given formatter. 65 | func (l *LwPolyline) FormatString(f format.Formatter) string { 66 | l.Format(f) 67 | return f.Output() 68 | } 69 | 70 | // Close closes LwPolyline. 71 | func (l *LwPolyline) Close() { 72 | l.Closed = true 73 | } 74 | 75 | func (l *LwPolyline) BBox() ([]float64, []float64) { 76 | mins := make([]float64, 3) 77 | maxs := make([]float64, 3) 78 | for _, p := range l.Vertices { 79 | for i := 0; i < len(p); i++ { 80 | if p[i] < mins[i] { 81 | mins[i] = p[i] 82 | } 83 | if p[i] > maxs[i] { 84 | maxs[i] = p[i] 85 | } 86 | } 87 | } 88 | return mins, maxs 89 | } 90 | 91 | // Bulge computes the center and the ray of the arc to reach the vertex. 92 | func (l LwPolyline) Bulge(index int) ([]float64, float64) { 93 | if index > len(l.Vertices)-1 || index < 1 { 94 | return nil, 0 95 | } 96 | 97 | return bulge(l.Bulges[index], l.Vertices[index-1], l.Vertices[index]) 98 | } 99 | -------------------------------------------------------------------------------- /table/style.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // Default Styles. 9 | var ( 10 | ST_STANDARD = NewStyle("Standard") 11 | ) 12 | 13 | // Style represents STYLE SymbolTable. 14 | type Style struct { 15 | handle int 16 | owner handle.Handler 17 | name string // 2 18 | FontName string // 3 19 | BigFontName string // 4 20 | FixedTextHeight float64 // 40 21 | WidthFactor float64 // 41 22 | LastHeightUsed float64 // 42 23 | ObliqueAngle float64 // 50 24 | } 25 | 26 | // NewStyle create a new Style. 27 | func NewStyle(name string) *Style { 28 | return &Style{ 29 | name: name, 30 | FontName: "arial.ttf", 31 | FixedTextHeight: 0.0, 32 | WidthFactor: 1.0, 33 | LastHeightUsed: 100.0, 34 | ObliqueAngle: 0.0, 35 | } 36 | } 37 | 38 | // IsSymbolTable is for SymbolTable interface. 39 | func (st *Style) IsSymbolTable() bool { 40 | return true 41 | } 42 | 43 | // Format writes data to formatter. 44 | func (st *Style) Format(f format.Formatter) { 45 | f.WriteString(0, "STYLE") 46 | f.WriteHex(5, st.handle) 47 | if st.owner != nil { 48 | f.WriteHex(330, st.owner.Handle()) 49 | } 50 | f.WriteString(100, "AcDbSymbolTableRecord") 51 | f.WriteString(100, "AcDbTextStyleTableRecord") 52 | f.WriteString(2, st.name) 53 | f.WriteInt(70, 0) 54 | f.WriteFloat(40, st.FixedTextHeight) 55 | f.WriteFloat(41, st.WidthFactor) 56 | f.WriteFloat(50, st.ObliqueAngle) 57 | f.WriteInt(71, 0) 58 | f.WriteFloat(42, st.LastHeightUsed) 59 | f.WriteString(3, st.FontName) 60 | f.WriteString(4, st.BigFontName) 61 | } 62 | 63 | // String outputs data using default formatter. 64 | func (st *Style) String() string { 65 | f := format.NewASCII() 66 | return st.FormatString(f) 67 | } 68 | 69 | // FormatString outputs data using given formatter. 70 | func (st *Style) FormatString(f format.Formatter) string { 71 | st.Format(f) 72 | return f.Output() 73 | } 74 | 75 | // Handle returns a handle value. 76 | func (st *Style) Handle() int { 77 | return st.handle 78 | } 79 | 80 | // SetHandle sets a handle. 81 | func (st *Style) SetHandle(v *int) { 82 | st.handle = *v 83 | (*v)++ 84 | } 85 | 86 | // SetOwner sets an owner. 87 | func (st *Style) SetOwner(h handle.Handler) { 88 | st.owner = h 89 | } 90 | 91 | // Name returns a name of STYLE (code 2). 92 | func (st *Style) Name() string { 93 | return st.name 94 | } 95 | -------------------------------------------------------------------------------- /object/acdbdictionarywdflt.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/yofu/dxf/format" 7 | "github.com/yofu/dxf/handle" 8 | ) 9 | 10 | // AcDbDictionaryWDFLT represents ACDBDICTIONARYWDFLT Object. 11 | type AcDbDictionaryWDFLT struct { 12 | handle int 13 | item map[string]handle.Handler 14 | owner handle.Handler 15 | defaulthandle handle.Handler 16 | } 17 | 18 | // IsObject is for Object interface. 19 | func (d *AcDbDictionaryWDFLT) IsObject() bool { 20 | return true 21 | } 22 | 23 | // NewAcDbDictionaryWDFLT creates a new AcDbDictionaryWDFLT & AcDbPlaceHolder. 24 | func NewAcDbDictionaryWDFLT(owner handle.Handler) (*AcDbDictionaryWDFLT, *AcDbPlaceHolder) { 25 | ds := make(map[string]handle.Handler) 26 | p := NewAcDbPlaceHolder() 27 | ds["Normal"] = p 28 | d := &AcDbDictionaryWDFLT{ 29 | handle: 0, 30 | item: ds, 31 | owner: owner, 32 | defaulthandle: p, 33 | } 34 | p.owner = d 35 | return d, p 36 | } 37 | 38 | // Format writes data to formatter. 39 | func (d *AcDbDictionaryWDFLT) Format(f format.Formatter) { 40 | f.WriteString(0, "ACDBDICTIONARYWDFLT") 41 | f.WriteHex(5, d.handle) 42 | if d.owner != nil { 43 | f.WriteString(102, "{ACAD_REACTORS") 44 | f.WriteHex(330, d.owner.Handle()) 45 | f.WriteString(102, "}") 46 | f.WriteHex(330, d.owner.Handle()) 47 | } 48 | f.WriteString(100, "AcDbDictionary") 49 | f.WriteInt(281, 1) 50 | for k, v := range d.item { 51 | f.WriteString(3, k) 52 | f.WriteHex(350, v.Handle()) 53 | } 54 | f.WriteString(100, "AcDbDictionaryWithDefault") 55 | f.WriteHex(340, d.defaulthandle.Handle()) 56 | } 57 | 58 | // String outputs data using default formatter. 59 | func (d *AcDbDictionaryWDFLT) String() string { 60 | f := format.NewASCII() 61 | return d.FormatString(f) 62 | } 63 | 64 | // FormatString outputs data using given formatter. 65 | func (d *AcDbDictionaryWDFLT) FormatString(f format.Formatter) string { 66 | d.Format(f) 67 | return f.Output() 68 | } 69 | 70 | // Handle returns a handle value. 71 | func (d *AcDbDictionaryWDFLT) Handle() int { 72 | return d.handle 73 | } 74 | 75 | // SetHandle sets a handle. 76 | func (d *AcDbDictionaryWDFLT) SetHandle(v *int) { 77 | d.handle = *v 78 | (*v)++ 79 | } 80 | 81 | // AddItem adds new a new item to AcDbDictionaryWDFLT. 82 | func (d *AcDbDictionaryWDFLT) AddItem(key string, value handle.Handler) error { 83 | if _, exist := d.item[key]; exist { 84 | return fmt.Errorf("key %s already exists", key) 85 | } 86 | d.item[key] = value 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /entity/spline.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // Spline represents LINE Entity. 8 | type Spline struct { 9 | *entity 10 | Normal []float64 // 210, 220, 230 11 | Flag int // 70 12 | Degree int // 71 13 | Knots []float64 // 72, 40 14 | Controls [][]float64 // 73, 10, 20, 30 15 | Fits [][]float64 // 74, 11, 21, 31 16 | Tolerance []float64 // 42, 43, 44 17 | } 18 | 19 | // IsEntity is for Entity interface. 20 | func (l *Spline) IsEntity() bool { 21 | return true 22 | } 23 | 24 | // NewSpline creates a new Spline. 25 | func NewSpline() *Spline { 26 | l := &Spline{ 27 | entity: NewEntity(SPLINE), 28 | Normal: []float64{0.0, 0.0, 1.0}, 29 | Flag: 1064, 30 | Degree: 3, 31 | Knots: nil, 32 | Controls: nil, 33 | Fits: nil, 34 | Tolerance: []float64{0.000000001, 0.0000000001, 0.0000000001}, 35 | } 36 | return l 37 | } 38 | 39 | // Format writes data to formatter. 40 | func (s *Spline) Format(f format.Formatter) { 41 | s.entity.Format(f) 42 | f.WriteString(100, "AcDbSpline") 43 | for i := 0; i < 3; i++ { 44 | f.WriteFloat(210+i*10, s.Normal[i]) 45 | } 46 | f.WriteInt(70, s.Flag) 47 | f.WriteInt(71, s.Degree) 48 | f.WriteInt(72, len(s.Knots)) 49 | f.WriteInt(73, len(s.Controls)) 50 | f.WriteInt(74, len(s.Fits)) 51 | for i := 0; i < 3; i++ { 52 | f.WriteFloat(42+i, s.Tolerance[i]) 53 | } 54 | for _, k := range s.Knots { 55 | f.WriteFloat(40, k) 56 | } 57 | for _, c := range s.Controls { 58 | for i := 0; i < 3; i++ { 59 | f.WriteFloat((i+1)*10, c[i]) 60 | } 61 | } 62 | for _, ft := range s.Fits { 63 | for i := 0; i < 3; i++ { 64 | f.WriteFloat((i+1)*10+1, ft[i]) 65 | } 66 | } 67 | } 68 | 69 | // String outputs data using default formatter. 70 | func (s *Spline) String() string { 71 | f := format.NewASCII() 72 | return s.FormatString(f) 73 | } 74 | 75 | // FormatString outputs data using given formatter. 76 | func (s *Spline) FormatString(f format.Formatter) string { 77 | s.Format(f) 78 | return f.Output() 79 | } 80 | 81 | // BBox calculates the bounding box of the Spline. 82 | func (s *Spline) BBox() ([]float64, []float64) { 83 | mins := make([]float64, 3) 84 | maxs := make([]float64, 3) 85 | 86 | // Initialize mins and maxs with the first control point 87 | for i := 0; i < 3; i++ { 88 | mins[i] = s.Controls[0][i] 89 | maxs[i] = s.Controls[0][i] 90 | } 91 | 92 | // Iterate through the control points to find the minimum and maximum values 93 | for _, p := range s.Controls { 94 | for i := 0; i < 3; i++ { 95 | if p[i] < mins[i] { 96 | mins[i] = p[i] 97 | } 98 | if p[i] > maxs[i] { 99 | maxs[i] = p[i] 100 | } 101 | } 102 | } 103 | 104 | return mins, maxs 105 | } 106 | -------------------------------------------------------------------------------- /entity/polyline.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | ) 6 | 7 | // Polyline represents POLYLINE Entity. 8 | type Polyline struct { 9 | *entity 10 | Flag int 11 | size int 12 | Vertices []*Vertex 13 | endhandle int 14 | } 15 | 16 | // IsEntity is for Entity interface. 17 | func (p *Polyline) IsEntity() bool { 18 | return true 19 | } 20 | 21 | // NewPolyline creates a new Polyline. 22 | func NewPolyline() *Polyline { 23 | vs := make([]*Vertex, 0) 24 | p := &Polyline{ 25 | entity: NewEntity(POLYLINE), 26 | Flag: 8, 27 | size: 0, 28 | Vertices: vs, 29 | endhandle: 0, 30 | } 31 | return p 32 | } 33 | 34 | // Format writes data to formatter. 35 | func (p *Polyline) Format(f format.Formatter) { 36 | p.entity.Format(f) 37 | f.WriteString(100, "AcDb3dPolyline") 38 | f.WriteInt(66, 1) 39 | f.WriteString(10, "0.0") 40 | f.WriteString(20, "0.0") 41 | f.WriteString(30, "0.0") 42 | f.WriteInt(70, p.Flag) 43 | for _, v := range p.Vertices { 44 | v.Format(f) 45 | } 46 | f.WriteString(0, "SEQEND") 47 | f.WriteHex(5, p.endhandle) 48 | f.WriteString(100, "AcDbEntity") 49 | f.WriteString(8, p.Layer().Name()) 50 | } 51 | 52 | // String outputs data using default formatter. 53 | func (p *Polyline) String() string { 54 | f := format.NewASCII() 55 | return p.FormatString(f) 56 | } 57 | 58 | // FormatString outputs data using given formatter. 59 | func (p *Polyline) FormatString(f format.Formatter) string { 60 | p.Format(f) 61 | return f.Output() 62 | } 63 | 64 | // Close closes Polyline. 65 | func (p *Polyline) Close() { 66 | p.Flag |= 1 67 | } 68 | 69 | // AddVertex adds a new vertex to Polyline. 70 | func (p *Polyline) AddVertex(x, y, z float64) *Vertex { 71 | v := NewVertex(x, y, z) 72 | p.Vertices = append(p.Vertices, v) 73 | p.size++ 74 | v.SetLayer(p.Layer()) 75 | v.SetOwner(p) 76 | return v 77 | } 78 | 79 | // SetHandle sets handles to itself and its vertices. 80 | func (p *Polyline) SetHandle(h *int) { 81 | p.entity.SetHandle(h) 82 | for _, v := range p.Vertices { 83 | v.SetHandle(h) 84 | } 85 | p.endhandle = *h 86 | (*h)++ 87 | } 88 | 89 | func (p *Polyline) BBox() ([]float64, []float64) { 90 | mins := make([]float64, 3) 91 | maxs := make([]float64, 3) 92 | for _, v := range p.Vertices { 93 | for i := 0; i < 3; i++ { 94 | if v.Coord[i] < mins[i] { 95 | mins[i] = v.Coord[i] 96 | } 97 | if v.Coord[i] > maxs[i] { 98 | maxs[i] = v.Coord[i] 99 | } 100 | } 101 | } 102 | return mins, maxs 103 | } 104 | 105 | // Bulge computes the center and the ray of the arc to reach the vertex. 106 | func (p Polyline) Bulge(index int) ([]float64, float64) { 107 | if index > len(p.Vertices)-2 { 108 | return nil, 0 109 | } 110 | 111 | return bulge(p.Vertices[index].Buldge, p.Vertices[index].Coord, p.Vertices[index+1].Coord) 112 | } 113 | -------------------------------------------------------------------------------- /entity/entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | "github.com/yofu/dxf/table" 7 | ) 8 | 9 | // Entity is interface for DXF Entities. 10 | type Entity interface { 11 | IsEntity() bool 12 | Format(format.Formatter) 13 | Handle() int 14 | SetHandle(*int) 15 | SetBlockRecord(handle.Handler) 16 | Layer() *table.Layer 17 | SetLayer(*table.Layer) 18 | SetLtscale(float64) 19 | BBox() ([]float64, []float64) 20 | } 21 | 22 | // entity is common part of Entities. 23 | // It is embedded in each entities to implement Entity interface. 24 | type entity struct { 25 | Type EntityType // 0 26 | handle int // 5 27 | blockRecord handle.Handler // 102 330 28 | owner handle.Handler // 330 29 | layer *table.Layer // 8 30 | ltscale float64 // 48 31 | } 32 | 33 | // NewEntity creates a new entity. 34 | func NewEntity(t EntityType) *entity { 35 | e := &entity{ 36 | Type: t, 37 | handle: 0, 38 | blockRecord: nil, 39 | owner: nil, 40 | layer: table.LY_0, 41 | ltscale: 1.0, 42 | } 43 | return e 44 | } 45 | 46 | // Format writes data to formatter. 47 | func (e *entity) Format(f format.Formatter) { 48 | f.WriteString(0, EntityTypeString(e.Type)) 49 | f.WriteHex(5, e.handle) 50 | if e.blockRecord != nil { 51 | f.WriteString(102, "{ACAD_REACTORS") 52 | f.WriteHex(330, e.blockRecord.Handle()) 53 | f.WriteString(102, "}") 54 | } 55 | if e.owner != nil { 56 | f.WriteHex(330, e.owner.Handle()) 57 | } 58 | f.WriteString(100, "AcDbEntity") 59 | f.WriteString(8, e.layer.Name()) 60 | if e.ltscale != 1.0 { 61 | f.WriteFloat(48, e.ltscale) 62 | } 63 | } 64 | 65 | // String outputs data using default formatter. 66 | func (e *entity) String() string { 67 | f := format.NewASCII() 68 | return e.FormatString(f) 69 | } 70 | 71 | // FormatString outputs data using given formatter. 72 | func (e *entity) FormatString(f format.Formatter) string { 73 | e.Format(f) 74 | return f.Output() 75 | } 76 | 77 | // Handle returns a handle value of TABLE. 78 | func (e *entity) Handle() int { 79 | return e.handle 80 | } 81 | 82 | // SetHandle sets handles to TABLE itself and each SymbolTable. 83 | func (e *entity) SetHandle(v *int) { 84 | e.handle = *v 85 | (*v)++ 86 | } 87 | 88 | // SetBlockRecord sets BLOCK_RECORD to entity (code 330). 89 | func (e *entity) SetBlockRecord(h handle.Handler) { 90 | e.blockRecord = h 91 | } 92 | 93 | // SetOwner sets an owner. 94 | func (e *entity) SetOwner(h handle.Handler) { 95 | e.owner = h 96 | } 97 | 98 | // Layer returns entity's Layer. 99 | func (e *entity) Layer() *table.Layer { 100 | return e.layer 101 | } 102 | 103 | // SetLayer sets Layer to entity. 104 | func (e *entity) SetLayer(l *table.Layer) { 105 | e.layer = l 106 | } 107 | 108 | // SetLtscale sets Layer to entity. 109 | func (e *entity) SetLtscale(v float64) { 110 | e.ltscale = v 111 | } 112 | 113 | // SetEntityType sets entity type. 114 | func (e *entity) SetEntityType(t EntityType) { 115 | e.Type = t 116 | } 117 | -------------------------------------------------------------------------------- /table/linetype.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/yofu/dxf/format" 7 | "github.com/yofu/dxf/handle" 8 | ) 9 | 10 | // Default LineTypes. 11 | var ( 12 | LT_CONTINUOUS = NewLineType("Continuous", "Solid Line") 13 | LT_BYLAYER = NewLineType("ByLayer", "") 14 | LT_BYBLOCK = NewLineType("ByBlock", "") 15 | LT_HIDDEN = NewLineType("HIDDEN", "Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ _", 0.25, -0.125) 16 | LT_DASHDOT = NewLineType("DASHDOT", "Dash dot __ . __ . __ . __ . __ . __ . __ . __", 0.5, -0.25, 0.0, -0.25) 17 | ) 18 | 19 | // LineType represents LTYPE SymbolTable. 20 | type LineType struct { 21 | handle int 22 | owner handle.Handler 23 | name string // 2 24 | Description string // 3 25 | lengths []float64 26 | } 27 | 28 | // NewLineType creates a new LineType. 29 | func NewLineType(name, desc string, ls ...float64) *LineType { 30 | lt := new(LineType) 31 | lt.name = name 32 | lt.Description = desc 33 | if len(ls) > 0 { 34 | lt.lengths = ls 35 | } else { 36 | lt.lengths = make([]float64, 0) 37 | } 38 | return lt 39 | } 40 | 41 | // IsSymbolTable is for SymbolTable interface. 42 | func (lt *LineType) IsSymbolTable() bool { 43 | return true 44 | } 45 | 46 | // Format writes data to formatter. 47 | func (lt *LineType) Format(f format.Formatter) { 48 | f.WriteString(0, "LTYPE") 49 | f.WriteHex(5, lt.handle) 50 | if lt.owner != nil { 51 | f.WriteHex(330, lt.owner.Handle()) 52 | } 53 | f.WriteString(100, "AcDbSymbolTableRecord") 54 | f.WriteString(100, "AcDbLinetypeTableRecord") 55 | f.WriteString(2, lt.name) 56 | f.WriteInt(70, 0) 57 | f.WriteString(3, lt.Description) 58 | f.WriteInt(72, 65) 59 | f.WriteInt(73, len(lt.lengths)) 60 | f.WriteFloat(40, lt.TotalLength()) 61 | for _, l := range lt.lengths { 62 | f.WriteFloat(49, l) 63 | f.WriteInt(74, 0) 64 | } 65 | } 66 | 67 | // String outputs data using default formatter. 68 | func (lt *LineType) String() string { 69 | f := format.NewASCII() 70 | return lt.FormatString(f) 71 | } 72 | 73 | // FormatString outputs data using given formatter. 74 | func (lt *LineType) FormatString(f format.Formatter) string { 75 | lt.Format(f) 76 | return f.Output() 77 | } 78 | 79 | // Handle returns a handle value. 80 | func (lt *LineType) Handle() int { 81 | return lt.handle 82 | } 83 | 84 | // SetHandle sets a handle. 85 | func (lt *LineType) SetHandle(v *int) { 86 | lt.handle = *v 87 | (*v)++ 88 | } 89 | 90 | // SetOwner sets an owner. 91 | func (lt *LineType) SetOwner(h handle.Handler) { 92 | lt.owner = h 93 | } 94 | 95 | // Name returns a name of LAYER (code 2). 96 | func (lt *LineType) Name() string { 97 | return lt.name 98 | } 99 | 100 | // TotalLength returns total pattern length (code 40). 101 | func (lt *LineType) TotalLength() float64 { 102 | sum := 0.0 103 | for _, l := range lt.lengths { 104 | sum += math.Abs(l) 105 | } 106 | return sum 107 | } 108 | 109 | // SetLength sets pattern length (code 49). 110 | // 111 | // positive value: Dash 112 | // 0.0: Dot 113 | // negative value: Space 114 | func (lt *LineType) SetLength(ls []float64) { 115 | lt.lengths = ls 116 | } 117 | -------------------------------------------------------------------------------- /table/viewport.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/handle" 6 | ) 7 | 8 | // Viewport represents VPORT SymbolTable. 9 | type Viewport struct { 10 | handle int 11 | owner handle.Handler 12 | name string // 2 13 | LowerLeft []float64 14 | UpperRight []float64 15 | ViewCenter []float64 16 | SnapBase []float64 17 | SnapSpacing []float64 18 | GridSpacing []float64 19 | ViewDirection []float64 20 | ViewTarget []float64 21 | Height float64 22 | AspectRatio float64 23 | LensLength float64 24 | FrontClip float64 25 | BackClip float64 26 | SnapAngle float64 27 | TwistAngle float64 28 | } 29 | 30 | // NewViewport creates a new Viewport. 31 | func NewViewport(name string) *Viewport { 32 | v := &Viewport{ 33 | name: name, 34 | LowerLeft: []float64{0.0, 0.0}, 35 | UpperRight: []float64{0.0, 0.0}, 36 | ViewCenter: []float64{0.0, 0.0}, 37 | SnapBase: []float64{0.0, 0.0}, 38 | SnapSpacing: []float64{0.0, 0.0}, 39 | GridSpacing: []float64{0.0, 0.0}, 40 | ViewDirection: []float64{0.0, 0.0, 0.0}, 41 | ViewTarget: []float64{0.0, 0.0, 0.0}, 42 | Height: 400.0, 43 | AspectRatio: 1.0, 44 | LensLength: 50.0, 45 | FrontClip: 0.0, 46 | BackClip: 0.0, 47 | SnapAngle: 0.0, 48 | TwistAngle: 0.0, 49 | } 50 | return v 51 | } 52 | 53 | // IsSymbolTable is for SymbolTable interface. 54 | func (v *Viewport) IsSymbolTable() bool { 55 | return true 56 | } 57 | 58 | // Format writes data to formatter. 59 | func (v *Viewport) Format(f format.Formatter) { 60 | f.WriteString(0, "VPORT") 61 | f.WriteHex(5, v.handle) 62 | if v.owner != nil { 63 | f.WriteHex(330, v.owner.Handle()) 64 | } 65 | f.WriteString(100, "AcDbSymbolTableRecord") 66 | f.WriteString(100, "AcDbViewportTableRecord") 67 | f.WriteString(2, v.name) 68 | f.WriteInt(70, 0) 69 | for i := 0; i < 2; i++ { 70 | f.WriteFloat((i+1)*10, v.LowerLeft[i]) 71 | } 72 | for i := 0; i < 2; i++ { 73 | f.WriteFloat((i+1)*10+1, v.UpperRight[i]) 74 | } 75 | for i := 0; i < 2; i++ { 76 | f.WriteFloat((i+1)*10+2, v.ViewCenter[i]) 77 | } 78 | for i := 0; i < 2; i++ { 79 | f.WriteFloat((i+1)*10+3, v.SnapBase[i]) 80 | } 81 | for i := 0; i < 2; i++ { 82 | f.WriteFloat((i+1)*10+4, v.SnapSpacing[i]) 83 | } 84 | for i := 0; i < 2; i++ { 85 | f.WriteFloat((i+1)*10+5, v.GridSpacing[i]) 86 | } 87 | for i := 0; i < 3; i++ { 88 | f.WriteFloat((i+1)*10+6, v.ViewDirection[i]) 89 | } 90 | for i := 0; i < 3; i++ { 91 | f.WriteFloat((i+1)*10+7, v.ViewTarget[i]) 92 | } 93 | f.WriteFloat(40, v.Height) 94 | f.WriteFloat(41, v.AspectRatio) 95 | f.WriteFloat(42, v.LensLength) 96 | f.WriteFloat(43, v.FrontClip) 97 | f.WriteFloat(44, v.BackClip) 98 | f.WriteFloat(50, v.SnapAngle) 99 | f.WriteFloat(51, v.TwistAngle) 100 | } 101 | 102 | // String outputs data using default formatter. 103 | func (v *Viewport) String() string { 104 | f := format.NewASCII() 105 | return v.FormatString(f) 106 | } 107 | 108 | // FormatString outputs data using given formatter. 109 | func (v *Viewport) FormatString(f format.Formatter) string { 110 | v.Format(f) 111 | return f.Output() 112 | } 113 | 114 | // Handle returns a handle value. 115 | func (v *Viewport) Handle() int { 116 | return v.handle 117 | } 118 | 119 | // SetHandle sets a handle. 120 | func (v *Viewport) SetHandle(h *int) { 121 | v.handle = *h 122 | (*h)++ 123 | } 124 | 125 | // SetOwner sets an owner. 126 | func (v *Viewport) SetOwner(h handle.Handler) { 127 | v.owner = h 128 | } 129 | 130 | // Name returns a name of VPORT (code 2). 131 | func (v *Viewport) Name() string { 132 | return v.name 133 | } 134 | 135 | // SetName sets a name to VPORT (code 2). 136 | func (v *Viewport) SetName(name string) { 137 | v.name = name 138 | } 139 | -------------------------------------------------------------------------------- /dxf.go: -------------------------------------------------------------------------------- 1 | // Package dxf is a DXF(Drawing Exchange Format) library for golang. 2 | // ACAD2000(AC1015), ASCII format is only supported. 3 | // http://www.autodesk.com/techpubs/autocad/acad2000/dxf/index.htm 4 | package dxf 5 | 6 | import ( 7 | "bufio" 8 | "fmt" 9 | "io" 10 | "os" 11 | "strings" 12 | 13 | "github.com/yofu/dxf/color" 14 | "github.com/yofu/dxf/drawing" 15 | "github.com/yofu/dxf/table" 16 | ) 17 | 18 | // Default values. 19 | var ( 20 | DefaultColor = color.White 21 | DefaultLineType = table.LT_CONTINUOUS 22 | ) 23 | 24 | // NewDrawing creates a drawing. 25 | func NewDrawing() *drawing.Drawing { 26 | return drawing.New() 27 | } 28 | 29 | // Create drawing from file 30 | func FromFile(fn string) (*drawing.Drawing, error) { 31 | var err error 32 | f, err := os.Open(fn) 33 | if err != nil { 34 | return nil, err 35 | } 36 | defer f.Close() 37 | return FromReader(f) 38 | } 39 | 40 | // Create drawing from string 41 | func FromStringData(d string) (*drawing.Drawing, error) { 42 | sr := strings.NewReader(d) 43 | return FromReader(sr) 44 | } 45 | 46 | // Main logic to create a drawing 47 | func FromReader(r io.Reader) (*drawing.Drawing, error) { 48 | var err error 49 | scanner := bufio.NewScanner(r) 50 | d := NewDrawing() 51 | var code, value string 52 | parsers := []func(*drawing.Drawing, int, [][2]string) error{ 53 | ParseHeader, 54 | ParseClasses, 55 | ParseTables, 56 | ParseBlocks, 57 | ParseEntities, 58 | ParseObjects, 59 | } 60 | data := make([][2]string, 0) 61 | setparser := false 62 | var parser func(*drawing.Drawing, int, [][2]string) error 63 | line := 0 64 | startline := 0 65 | for scanner.Scan() { 66 | line++ 67 | if line%2 == 1 { 68 | code = strings.TrimSpace(scanner.Text()) 69 | if err != nil { 70 | return d, err 71 | } 72 | } else { 73 | value = scanner.Text() 74 | if setparser { 75 | if code != "2" { 76 | return d, fmt.Errorf("line %d: invalid group code: %s", line, code) 77 | } 78 | ind := drawing.SectionTypeValue(strings.ToUpper(value)) 79 | if ind < 0 { 80 | return d, fmt.Errorf("line %d: unknown section name: %s", line, value) 81 | } 82 | parser = parsers[ind] 83 | startline = line + 1 84 | setparser = false 85 | } else { 86 | if code == "0" { 87 | switch strings.ToUpper(value) { 88 | case "EOF": 89 | return d, nil 90 | case "SECTION": 91 | setparser = true 92 | case "ENDSEC": 93 | err := parser(d, startline, data) 94 | if err != nil { 95 | return d, err 96 | } 97 | data = make([][2]string, 0) 98 | startline = line + 1 99 | default: 100 | data = append(data, [2]string{code, scanner.Text()}) 101 | } 102 | } else { 103 | data = append(data, [2]string{code, scanner.Text()}) 104 | } 105 | } 106 | } 107 | } 108 | if err := scanner.Err(); err != nil { 109 | return d, err 110 | } 111 | if len(data) > 0 { 112 | err := parser(d, startline, data) 113 | if err != nil { 114 | return d, err 115 | } 116 | } 117 | return d, nil 118 | } 119 | 120 | // Open is deprecated, please use FromFile 121 | func Open(filename string) (*drawing.Drawing, error) { 122 | return FromFile(filename) 123 | } 124 | 125 | // ColorIndex converts RGB value to corresponding color number. 126 | func ColorIndex(cl []int) color.ColorNumber { 127 | minind := 0 128 | minval := 1000000 129 | for i, c := range color.ColorRGB { 130 | tmpval := 0 131 | for j := 0; j < 3; j++ { 132 | tmpval += (cl[j] - int(c[j])) * (cl[j] - int(c[j])) 133 | } 134 | if tmpval < minval { 135 | minind = i 136 | minval = tmpval 137 | if minval == 0 { 138 | break 139 | } 140 | } 141 | } 142 | return color.ColorNumber(minind) 143 | } 144 | 145 | // IndexColor converts color number to RGB value. 146 | func IndexColor(index uint8) []uint8 { 147 | return color.ColorRGB[index] 148 | } 149 | -------------------------------------------------------------------------------- /table/layer.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "github.com/yofu/dxf/color" 5 | "github.com/yofu/dxf/format" 6 | "github.com/yofu/dxf/handle" 7 | ) 8 | 9 | // Default layers. 10 | var ( 11 | LY_0 = NewLayer("0", color.White, LT_CONTINUOUS) 12 | ) 13 | 14 | // Layer represents LAYER SymbolTable. 15 | type Layer struct { 16 | handle int 17 | owner handle.Handler 18 | name string 19 | flag int 20 | Color color.ColorNumber 21 | LineType *LineType 22 | lineWidth int 23 | PlotStyle handle.Handler 24 | } 25 | 26 | // NewLayer creates a new Layer. 27 | func NewLayer(name string, color color.ColorNumber, lt *LineType) *Layer { 28 | l := new(Layer) 29 | l.name = name 30 | l.Color = color 31 | l.LineType = lt 32 | l.lineWidth = -3 33 | return l 34 | } 35 | 36 | // IsSymbolTable is for SymbolTable interface. 37 | func (l *Layer) IsSymbolTable() bool { 38 | return true 39 | } 40 | 41 | // Format writes data to formatter. 42 | func (l *Layer) Format(f format.Formatter) { 43 | f.WriteString(0, "LAYER") 44 | f.WriteHex(5, l.handle) 45 | if l.owner != nil { 46 | f.WriteHex(330, l.owner.Handle()) 47 | } 48 | f.WriteString(100, "AcDbSymbolTableRecord") 49 | f.WriteString(100, "AcDbLayerTableRecord") 50 | f.WriteString(2, l.name) 51 | f.WriteInt(70, l.flag) 52 | f.WriteInt(62, int(l.Color)) 53 | f.WriteString(6, l.LineType.Name()) 54 | f.WriteInt(370, l.lineWidth) 55 | f.WriteHex(390, l.PlotStyle.Handle()) 56 | } 57 | 58 | // String outputs data using default formatter. 59 | func (l *Layer) String() string { 60 | f := format.NewASCII() 61 | return l.FormatString(f) 62 | } 63 | 64 | // FormatString outputs data using given formatter. 65 | func (l *Layer) FormatString(f format.Formatter) string { 66 | l.Format(f) 67 | return f.Output() 68 | } 69 | 70 | // Handle returns a handle value. 71 | func (l *Layer) Handle() int { 72 | return l.handle 73 | } 74 | 75 | // SetHandle sets a handle. 76 | func (l *Layer) SetHandle(v *int) { 77 | l.handle = *v 78 | (*v)++ 79 | } 80 | 81 | // SetOwner sets an owner. 82 | func (l *Layer) SetOwner(h handle.Handler) { 83 | l.owner = h 84 | } 85 | 86 | // Name returns a name of LAYER (code 2). 87 | func (l *Layer) Name() string { 88 | return l.name 89 | } 90 | 91 | // SetLineWidth sets line width. 92 | // As DXF has limitation in line width, 93 | // it returns the actual value set to Layer. 94 | func (l *Layer) SetLineWidth(w int) int { 95 | if _, ok := LineWidth[w]; ok { 96 | l.lineWidth = w 97 | return w 98 | } 99 | if w > 211 { 100 | l.lineWidth = 211 101 | return 211 102 | } 103 | if w < 0 { 104 | l.lineWidth = -3 105 | return -3 106 | } 107 | minkey := -3 108 | minval := 211 109 | for k := range LineWidth { 110 | tmp := k - w 111 | if tmp > 0 && tmp < minval { 112 | minkey = k 113 | minval = tmp 114 | } 115 | } 116 | l.lineWidth = minkey 117 | return minkey 118 | } 119 | 120 | // SetPlotStyle sets plot style by a handle. 121 | func (l *Layer) SetPlotStyle(ps handle.Handler) { 122 | l.PlotStyle = ps 123 | } 124 | 125 | // SetFlag sets standard flags. 126 | // 127 | // 1 = Layer is frozen; otherwise layer is thawed. 128 | // 2 = Layer is frozen by default in new viewports. 129 | // 4 = Layer is locked. 130 | // 16 = If set, table entry is externally dependent on an xref. 131 | // 32 = If this bit and bit 16 are both set, the externally dependent xref has been successfully resolved. 132 | // 64 = If set, the table entry was referenced by at least one entity in the drawing the last time the drawing was edited. (This flag is for the benefit of AutoCAD commands. It can be ignored by most programs that read DXF files and need not be set by programs that write DXF files.) 133 | func (l *Layer) SetFlag(val int) { 134 | l.flag = val 135 | } 136 | 137 | // Freeze freezes Layer. 138 | func (l *Layer) Freeze() { 139 | l.flag |= 1 140 | } 141 | 142 | // UnFreeze unfreezes Layer. 143 | func (l *Layer) UnFreeze() { 144 | l.flag &= ^1 145 | } 146 | 147 | // Lock locks Layer. 148 | func (l *Layer) Lock() { 149 | l.flag |= 4 150 | } 151 | 152 | // UnLock unlocks Layer. 153 | func (l *Layer) UnLock() { 154 | l.flag &= ^4 155 | } 156 | -------------------------------------------------------------------------------- /insunit/insunit.go: -------------------------------------------------------------------------------- 1 | package insunit 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/yofu/dxf/format" 7 | ) 8 | 9 | // Unit is the drawing unit for AutoCAD DesignCenter Blocks 10 | type Unit uint8 11 | 12 | const ( 13 | Unitless Unit = 0 14 | Inches = 1 15 | Feet = 2 16 | Miles = 3 17 | Millimeters = 4 18 | Centimeters = 5 19 | Meters = 6 20 | Kilometers = 7 21 | Microinches = 8 22 | Mils = 9 23 | Yards = 10 24 | Angstroms = 11 25 | Nanometers = 12 26 | Microns = 13 27 | Decimeters = 14 28 | Decameters = 15 29 | Hectometers = 16 30 | Gigameters = 17 31 | Astronomical = 18 32 | LightYears = 19 33 | Parsecs = 20 34 | ) 35 | 36 | func (u Unit) Format(f format.Formatter) { 37 | f.WriteInt(70, int(u)) 38 | } 39 | 40 | func (u Unit) String() string { 41 | switch u { 42 | case Unitless: 43 | return "none" 44 | case Inches: 45 | return "inches" 46 | case Feet: 47 | return "feet" 48 | case Miles: 49 | return "miles" 50 | case Millimeters: 51 | return "millimeters" 52 | case Centimeters: 53 | return "centimeters" 54 | case Meters: 55 | return "meters" 56 | case Kilometers: 57 | return "kilometers" 58 | case Microinches: 59 | return "microinches" 60 | case Mils: 61 | return "mils" 62 | case Yards: 63 | return "yards" 64 | case Angstroms: 65 | return "angstroms" 66 | case Nanometers: 67 | return "nanometers" 68 | case Microns: 69 | return "microns" 70 | case Decimeters: 71 | return "decimeters" 72 | case Decameters: 73 | return "decameters" 74 | case Hectometers: 75 | return "hectometers" 76 | case Gigameters: 77 | return "gigameters" 78 | case Astronomical: 79 | return "astronomical" 80 | case LightYears: 81 | return "light years" 82 | case Parsecs: 83 | return "parsecs" 84 | default: 85 | return "unknown" 86 | } 87 | } 88 | 89 | var str2Unit = map[string]Unit{ 90 | "none": Unitless, 91 | "unitless": Unitless, 92 | "inches": Inches, 93 | "feet": Feet, 94 | "miles": Miles, 95 | "millimeters": Millimeters, 96 | "centimeters": Centimeters, 97 | "meters": Meters, 98 | "kilometers": Kilometers, 99 | "microinches": Microinches, 100 | "mils": Mils, 101 | "yards": Yards, 102 | "angstroms": Angstroms, 103 | "nanometers": Nanometers, 104 | "microns": Microns, 105 | "decimeters": Decimeters, 106 | "decameters": Decameters, 107 | "hectometers": Hectometers, 108 | "gigameters": Gigameters, 109 | "astronomical": Astronomical, 110 | "light years": LightYears, 111 | "lightyears": LightYears, 112 | "light-years": LightYears, 113 | "parsecs": Parsecs, 114 | } 115 | 116 | func UnitFromString(str string) (Unit, bool) { 117 | u, ok := str2Unit[strings.TrimSpace(strings.ToLower(str))] 118 | return u, ok 119 | } 120 | 121 | type Type int8 122 | 123 | const ( 124 | Scientific Type = iota - 1 // We want decimal to be default of zero. 125 | Decimal // This will be zero. 126 | Engineering 127 | Architectural 128 | Fractional 129 | WindowsDesktop 130 | ) 131 | 132 | func (t Type) Format(f format.Formatter) { 133 | // When defining the constants we subtracted two from the values 134 | // to insure that Decimal was the zero value 135 | f.WriteInt(70, int(t+2)) 136 | } 137 | 138 | func (t Type) String() string { 139 | switch t { 140 | case Scientific: 141 | return "scientific" 142 | case Decimal: 143 | return "decimal" 144 | case Engineering: 145 | return "engineering:" 146 | case Architectural: 147 | return "architectural:" 148 | case Fractional: 149 | return "fractional:" 150 | case WindowsDesktop: 151 | return "windows desktop" 152 | default: 153 | return "unknown" 154 | } 155 | } 156 | 157 | var str2Type = map[string]Type{ 158 | "scientific": Scientific, 159 | "decimal": Decimal, 160 | "engineering": Engineering, 161 | "architectural": Architectural, 162 | "fractional": Fractional, 163 | "windows desktop": WindowsDesktop, 164 | "windowsdesktop": WindowsDesktop, 165 | } 166 | 167 | func TypeFromString(str string) (Type, bool) { 168 | t, ok := str2Type[strings.TrimSpace(strings.ToLower(str))] 169 | return t, ok 170 | } 171 | -------------------------------------------------------------------------------- /entity/text.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/yofu/dxf/format" 5 | "github.com/yofu/dxf/table" 6 | ) 7 | 8 | // Text Anchor 9 | const ( 10 | LEFT_BASE = iota 11 | CENTER_BASE 12 | RIGHT_BASE 13 | LEFT_BOTTOM 14 | CENTER_BOTTOM 15 | RIGHT_BOTTOM 16 | LEFT_CENTER 17 | CENTER_CENTER 18 | RIGHT_CENTER 19 | LEFT_TOP 20 | CENTER_TOP 21 | RIGHT_TOP 22 | ) 23 | 24 | // Text represents TEXT Entity. 25 | type Text struct { 26 | *entity 27 | Coord1 []float64 // 10, 20, 30 28 | Coord2 []float64 // 11, 21, 31 29 | Height float64 // 40 30 | Rotation float64 // 50 31 | WidthFactor float64 // 41 32 | ObliqueAngle float64 // 51 33 | Value string // 1 34 | Style *table.Style // 7 35 | GenFlag int // 71 36 | HorizontalFlag int // 72 37 | VerticalFlag int // 73 38 | } 39 | 40 | // IsEntity is for Entity interface. 41 | func (t *Text) IsEntity() bool { 42 | return true 43 | } 44 | 45 | // NewText creates a new Text. 46 | func NewText() *Text { 47 | t := &Text{ 48 | entity: NewEntity(TEXT), 49 | Coord1: []float64{0.0, 0.0, 0.0}, 50 | Coord2: []float64{0.0, 0.0, 0.0}, 51 | Height: 1.0, 52 | Value: "", 53 | Style: table.ST_STANDARD, 54 | GenFlag: 0, 55 | HorizontalFlag: 0, 56 | VerticalFlag: 0, 57 | } 58 | return t 59 | } 60 | 61 | // Format writes data to formatter. 62 | func (t *Text) Format(f format.Formatter) { 63 | t.entity.Format(f) 64 | f.WriteString(100, "AcDbText") 65 | for i := 0; i < 3; i++ { 66 | f.WriteFloat((i+1)*10, t.Coord1[i]) 67 | } 68 | f.WriteFloat(40, t.Height) 69 | f.WriteFloat(50, t.Rotation) 70 | f.WriteFloat(41, t.WidthFactor) 71 | f.WriteFloat(51, t.ObliqueAngle) 72 | f.WriteString(1, t.Value) 73 | f.WriteString(7, t.Style.Name()) 74 | if t.GenFlag != 0 { 75 | f.WriteInt(71, t.GenFlag) 76 | } 77 | if t.HorizontalFlag != 0 { 78 | f.WriteInt(72, t.HorizontalFlag) 79 | if t.VerticalFlag != 0 { 80 | for i := 0; i < 3; i++ { 81 | f.WriteFloat((i+1)*10+1, t.Coord1[i]) 82 | } 83 | } 84 | } 85 | f.WriteString(100, "AcDbText") 86 | if t.VerticalFlag != 0 { 87 | f.WriteInt(73, t.VerticalFlag) 88 | } 89 | } 90 | 91 | // String outputs data using default formatter. 92 | func (t *Text) String() string { 93 | f := format.NewASCII() 94 | return t.FormatString(f) 95 | } 96 | 97 | // FormatString outputs data using given formatter. 98 | func (t *Text) FormatString(f format.Formatter) string { 99 | t.Format(f) 100 | return f.Output() 101 | } 102 | 103 | func (t *Text) togglegenflag(val int) { 104 | if t.GenFlag&val != 0 { 105 | t.GenFlag &= ^val 106 | } else { 107 | t.GenFlag |= val 108 | } 109 | } 110 | 111 | // FlipHorizontal flips Text horizontally. 112 | func (t *Text) FlipHorizontal() { 113 | t.togglegenflag(2) 114 | } 115 | 116 | // FlipVertical flips Text vertically. 117 | func (t *Text) FlipVertical() { 118 | t.togglegenflag(4) 119 | } 120 | 121 | // Anchor sets anchor point flags. 122 | func (t *Text) Anchor(pos int) { 123 | switch pos { 124 | case LEFT_BASE: 125 | t.HorizontalFlag = 0 126 | t.VerticalFlag = 0 127 | case CENTER_BASE: 128 | t.HorizontalFlag = 1 129 | t.VerticalFlag = 0 130 | case RIGHT_BASE: 131 | t.HorizontalFlag = 2 132 | t.VerticalFlag = 0 133 | case LEFT_BOTTOM: 134 | t.HorizontalFlag = 0 135 | t.VerticalFlag = 1 136 | case CENTER_BOTTOM: 137 | t.HorizontalFlag = 1 138 | t.VerticalFlag = 1 139 | case RIGHT_BOTTOM: 140 | t.HorizontalFlag = 2 141 | t.VerticalFlag = 1 142 | case LEFT_CENTER: 143 | t.HorizontalFlag = 0 144 | t.VerticalFlag = 2 145 | case CENTER_CENTER: 146 | t.HorizontalFlag = 1 147 | t.VerticalFlag = 2 148 | case RIGHT_CENTER: 149 | t.HorizontalFlag = 2 150 | t.VerticalFlag = 2 151 | case LEFT_TOP: 152 | t.HorizontalFlag = 0 153 | t.VerticalFlag = 3 154 | case CENTER_TOP: 155 | t.HorizontalFlag = 1 156 | t.VerticalFlag = 3 157 | case RIGHT_TOP: 158 | t.HorizontalFlag = 2 159 | t.VerticalFlag = 3 160 | } 161 | } 162 | 163 | func (t *Text) BBox() ([]float64, []float64) { 164 | // TODO: text length, anchor point 165 | mins := []float64{t.Coord1[0], t.Coord1[1], t.Coord1[2]} 166 | maxs := []float64{t.Coord1[0], t.Coord1[1] + t.Height, t.Coord1[2]} 167 | return mins, maxs 168 | } 169 | -------------------------------------------------------------------------------- /testdata/my_arc.dxf: -------------------------------------------------------------------------------- 1 | 0 2 | SECTION 3 | 2 4 | HEADER 5 | 9 6 | $ACADVER 7 | 1 8 | AC1015 9 | 9 10 | $INSBASE 11 | 10 12 | 0.0000000000000000 13 | 20 14 | 0.0000000000000000 15 | 30 16 | 0.0000000000000000 17 | 9 18 | $INSUNITS 19 | 70 20 | 0 21 | 9 22 | $LUNITS 23 | 70 24 | 2 25 | 9 26 | $EXTMIN 27 | 10 28 | 0.0000000000000000 29 | 20 30 | 0.0000000000000000 31 | 30 32 | 0.0000000000000000 33 | 9 34 | $EXTMAX 35 | 10 36 | 0.0000000000000000 37 | 20 38 | 0.0000000000000000 39 | 30 40 | 0.0000000000000000 41 | 9 42 | $LTSCALE 43 | 40 44 | 1.0000000000000000 45 | 9 46 | $HANDSEED 47 | 5 48 | 22 49 | 0 50 | ENDSEC 51 | 0 52 | SECTION 53 | 2 54 | CLASSES 55 | 0 56 | ENDSEC 57 | 0 58 | SECTION 59 | 2 60 | TABLES 61 | 0 62 | TABLE 63 | 2 64 | VPORT 65 | 5 66 | 1 67 | 100 68 | AcDbSymbolTable 69 | 70 70 | 0 71 | 0 72 | ENDTAB 73 | 0 74 | TABLE 75 | 2 76 | LTYPE 77 | 5 78 | 2 79 | 100 80 | AcDbSymbolTable 81 | 70 82 | 5 83 | 0 84 | LTYPE 85 | 5 86 | 3 87 | 330 88 | 2 89 | 100 90 | AcDbSymbolTableRecord 91 | 100 92 | AcDbLinetypeTableRecord 93 | 2 94 | ByLayer 95 | 70 96 | 0 97 | 3 98 | 99 | 72 100 | 65 101 | 73 102 | 0 103 | 40 104 | 0.0000000000000000 105 | 0 106 | LTYPE 107 | 5 108 | 4 109 | 330 110 | 2 111 | 100 112 | AcDbSymbolTableRecord 113 | 100 114 | AcDbLinetypeTableRecord 115 | 2 116 | ByBlock 117 | 70 118 | 0 119 | 3 120 | 121 | 72 122 | 65 123 | 73 124 | 0 125 | 40 126 | 0.0000000000000000 127 | 0 128 | LTYPE 129 | 5 130 | 5 131 | 330 132 | 2 133 | 100 134 | AcDbSymbolTableRecord 135 | 100 136 | AcDbLinetypeTableRecord 137 | 2 138 | Continuous 139 | 70 140 | 0 141 | 3 142 | Solid Line 143 | 72 144 | 65 145 | 73 146 | 0 147 | 40 148 | 0.0000000000000000 149 | 0 150 | LTYPE 151 | 5 152 | 6 153 | 330 154 | 2 155 | 100 156 | AcDbSymbolTableRecord 157 | 100 158 | AcDbLinetypeTableRecord 159 | 2 160 | HIDDEN 161 | 70 162 | 0 163 | 3 164 | Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ _ 165 | 72 166 | 65 167 | 73 168 | 2 169 | 40 170 | 0.3750000000000000 171 | 49 172 | 0.2500000000000000 173 | 74 174 | 0 175 | 49 176 | -0.1250000000000000 177 | 74 178 | 0 179 | 0 180 | LTYPE 181 | 5 182 | 7 183 | 330 184 | 2 185 | 100 186 | AcDbSymbolTableRecord 187 | 100 188 | AcDbLinetypeTableRecord 189 | 2 190 | DASHDOT 191 | 70 192 | 0 193 | 3 194 | Dash dot __ . __ . __ . __ . __ . __ . __ . __ 195 | 72 196 | 65 197 | 73 198 | 4 199 | 40 200 | 1.0000000000000000 201 | 49 202 | 0.5000000000000000 203 | 74 204 | 0 205 | 49 206 | -0.2500000000000000 207 | 74 208 | 0 209 | 49 210 | 0.0000000000000000 211 | 74 212 | 0 213 | 49 214 | -0.2500000000000000 215 | 74 216 | 0 217 | 0 218 | ENDTAB 219 | 0 220 | TABLE 221 | 2 222 | LAYER 223 | 5 224 | 8 225 | 100 226 | AcDbSymbolTable 227 | 70 228 | 1 229 | 0 230 | LAYER 231 | 5 232 | 9 233 | 330 234 | 8 235 | 100 236 | AcDbSymbolTableRecord 237 | 100 238 | AcDbLayerTableRecord 239 | 2 240 | 0 241 | 70 242 | 0 243 | 62 244 | 7 245 | 6 246 | Continuous 247 | 370 248 | -3 249 | 390 250 | 20 251 | 0 252 | ENDTAB 253 | 0 254 | TABLE 255 | 2 256 | STYLE 257 | 5 258 | A 259 | 100 260 | AcDbSymbolTable 261 | 70 262 | 1 263 | 0 264 | STYLE 265 | 5 266 | B 267 | 330 268 | A 269 | 100 270 | AcDbSymbolTableRecord 271 | 100 272 | AcDbTextStyleTableRecord 273 | 2 274 | Standard 275 | 70 276 | 0 277 | 40 278 | 0.0000000000000000 279 | 41 280 | 1.0000000000000000 281 | 50 282 | 0.0000000000000000 283 | 71 284 | 0 285 | 42 286 | 100.0000000000000000 287 | 3 288 | arial.ttf 289 | 4 290 | 291 | 0 292 | ENDTAB 293 | 0 294 | TABLE 295 | 2 296 | VIEW 297 | 5 298 | C 299 | 100 300 | AcDbSymbolTable 301 | 70 302 | 0 303 | 0 304 | ENDTAB 305 | 0 306 | TABLE 307 | 2 308 | UCS 309 | 5 310 | D 311 | 100 312 | AcDbSymbolTable 313 | 70 314 | 0 315 | 0 316 | ENDTAB 317 | 0 318 | TABLE 319 | 2 320 | APPID 321 | 5 322 | E 323 | 100 324 | AcDbSymbolTable 325 | 70 326 | 1 327 | 0 328 | APPID 329 | 5 330 | F 331 | 330 332 | E 333 | 100 334 | AcDbSymbolTableRecord 335 | 100 336 | AcDbRegAppTableRecord 337 | 2 338 | ACAD 339 | 70 340 | 0 341 | 0 342 | ENDTAB 343 | 0 344 | TABLE 345 | 2 346 | DIMSTYLE 347 | 5 348 | 10 349 | 100 350 | AcDbSymbolTable 351 | 70 352 | 0 353 | 100 354 | AcDbDimStyleTable 355 | 71 356 | 0 357 | 0 358 | ENDTAB 359 | 0 360 | TABLE 361 | 2 362 | BLOCK_RECORD 363 | 5 364 | 11 365 | 100 366 | AcDbSymbolTable 367 | 70 368 | 3 369 | 0 370 | BLOCK_RECORD 371 | 5 372 | 12 373 | 330 374 | 11 375 | 100 376 | AcDbSymbolTableRecord 377 | 100 378 | AcDbBlockTableRecord 379 | 2 380 | *Model_Space 381 | 70 382 | 0 383 | 280 384 | 1 385 | 281 386 | 0 387 | 0 388 | BLOCK_RECORD 389 | 5 390 | 13 391 | 330 392 | 11 393 | 100 394 | AcDbSymbolTableRecord 395 | 100 396 | AcDbBlockTableRecord 397 | 2 398 | *Paper_Space 399 | 70 400 | 0 401 | 280 402 | 1 403 | 281 404 | 0 405 | 0 406 | BLOCK_RECORD 407 | 5 408 | 14 409 | 330 410 | 11 411 | 100 412 | AcDbSymbolTableRecord 413 | 100 414 | AcDbBlockTableRecord 415 | 2 416 | *Paper_Space0 417 | 70 418 | 0 419 | 280 420 | 1 421 | 281 422 | 0 423 | 0 424 | ENDTAB 425 | 0 426 | ENDSEC 427 | 0 428 | SECTION 429 | 2 430 | BLOCKS 431 | 0 432 | BLOCK 433 | 5 434 | 15 435 | 100 436 | AcDbEntity 437 | 8 438 | 0 439 | 100 440 | AcDbBlockBegin 441 | 2 442 | *Model_Space 443 | 70 444 | 0 445 | 10 446 | 0.0000000000000000 447 | 20 448 | 0.0000000000000000 449 | 30 450 | 0.0000000000000000 451 | 3 452 | *Model_Space 453 | 1 454 | 455 | 0 456 | ENDBLK 457 | 5 458 | 16 459 | 100 460 | AcDbEntity 461 | 8 462 | 0 463 | 100 464 | AcDbBlockEnd 465 | 0 466 | BLOCK 467 | 5 468 | 17 469 | 100 470 | AcDbEntity 471 | 8 472 | 0 473 | 100 474 | AcDbBlockBegin 475 | 2 476 | *Paper_Space 477 | 70 478 | 0 479 | 10 480 | 0.0000000000000000 481 | 20 482 | 0.0000000000000000 483 | 30 484 | 0.0000000000000000 485 | 3 486 | *Paper_Space 487 | 1 488 | 489 | 0 490 | ENDBLK 491 | 5 492 | 18 493 | 100 494 | AcDbEntity 495 | 8 496 | 0 497 | 100 498 | AcDbBlockEnd 499 | 0 500 | BLOCK 501 | 5 502 | 19 503 | 100 504 | AcDbEntity 505 | 8 506 | 0 507 | 100 508 | AcDbBlockBegin 509 | 2 510 | *Paper_Space0 511 | 70 512 | 0 513 | 10 514 | 0.0000000000000000 515 | 20 516 | 0.0000000000000000 517 | 30 518 | 0.0000000000000000 519 | 3 520 | *Paper_Space0 521 | 1 522 | 523 | 0 524 | ENDBLK 525 | 5 526 | 1A 527 | 100 528 | AcDbEntity 529 | 8 530 | 0 531 | 100 532 | AcDbBlockEnd 533 | 0 534 | ENDSEC 535 | 0 536 | SECTION 537 | 2 538 | ENTITIES 539 | 0 540 | ARC 541 | 5 542 | 1B 543 | 100 544 | AcDbEntity 545 | 8 546 | 0 547 | 100 548 | AcDbCircle 549 | 10 550 | 0.0000000000000000 551 | 20 552 | 0.0000000000000000 553 | 30 554 | 0.0000000000000000 555 | 40 556 | 100.0000000000000000 557 | 210 558 | 0.0000000000000000 559 | 220 560 | 0.0000000000000000 561 | 230 562 | 1.0000000000000000 563 | 100 564 | AcDbArc 565 | 50 566 | 0.0000000000000000 567 | 51 568 | 60.0000000000000000 569 | 0 570 | ENDSEC 571 | 0 572 | SECTION 573 | 2 574 | OBJECTS 575 | 0 576 | DICTIONARY 577 | 5 578 | 1C 579 | 100 580 | AcDbDictionary 581 | 281 582 | 1 583 | 3 584 | ACAD_GROUP 585 | 350 586 | 21 587 | 3 588 | ACAD_PLOTSTYLENAME 589 | 350 590 | 1F 591 | 0 592 | ACDBDICTIONARYWDFLT 593 | 5 594 | 1F 595 | 102 596 | {ACAD_REACTORS 597 | 330 598 | 1C 599 | 102 600 | } 601 | 330 602 | 1C 603 | 100 604 | AcDbDictionary 605 | 281 606 | 1 607 | 3 608 | Normal 609 | 350 610 | 20 611 | 100 612 | AcDbDictionaryWithDefault 613 | 340 614 | 20 615 | 0 616 | ACDBPLACEHOLDER 617 | 5 618 | 20 619 | 102 620 | {ACAD_REACTORS 621 | 330 622 | 1F 623 | 102 624 | } 625 | 330 626 | 1F 627 | 0 628 | DICTIONARY 629 | 5 630 | 21 631 | 100 632 | AcDbDictionary 633 | 281 634 | 1 635 | 0 636 | ENDSEC 637 | 0 638 | EOF 639 | -------------------------------------------------------------------------------- /testdata/mypoint.dxf: -------------------------------------------------------------------------------- 1 | 0 2 | SECTION 3 | 2 4 | HEADER 5 | 9 6 | $ACADVER 7 | 1 8 | AC1015 9 | 9 10 | $INSBASE 11 | 10 12 | 0.0000000000000000 13 | 20 14 | 0.0000000000000000 15 | 30 16 | 0.0000000000000000 17 | 9 18 | $INSUNITS 19 | 70 20 | 0 21 | 9 22 | $LUNITS 23 | 70 24 | 2 25 | 9 26 | $EXTMIN 27 | 10 28 | 0.0000000000000000 29 | 20 30 | 0.0000000000000000 31 | 30 32 | 0.0000000000000000 33 | 9 34 | $EXTMAX 35 | 10 36 | 0.0000000000000000 37 | 20 38 | 0.0000000000000000 39 | 30 40 | 0.0000000000000000 41 | 9 42 | $LTSCALE 43 | 40 44 | 1.0000000000000000 45 | 9 46 | $HANDSEED 47 | 5 48 | 24 49 | 0 50 | ENDSEC 51 | 0 52 | SECTION 53 | 2 54 | CLASSES 55 | 0 56 | ENDSEC 57 | 0 58 | SECTION 59 | 2 60 | TABLES 61 | 0 62 | TABLE 63 | 2 64 | VPORT 65 | 5 66 | 1 67 | 100 68 | AcDbSymbolTable 69 | 70 70 | 0 71 | 0 72 | ENDTAB 73 | 0 74 | TABLE 75 | 2 76 | LTYPE 77 | 5 78 | 2 79 | 100 80 | AcDbSymbolTable 81 | 70 82 | 5 83 | 0 84 | LTYPE 85 | 5 86 | 3 87 | 330 88 | 2 89 | 100 90 | AcDbSymbolTableRecord 91 | 100 92 | AcDbLinetypeTableRecord 93 | 2 94 | ByLayer 95 | 70 96 | 0 97 | 3 98 | 99 | 72 100 | 65 101 | 73 102 | 0 103 | 40 104 | 0.0000000000000000 105 | 0 106 | LTYPE 107 | 5 108 | 4 109 | 330 110 | 2 111 | 100 112 | AcDbSymbolTableRecord 113 | 100 114 | AcDbLinetypeTableRecord 115 | 2 116 | ByBlock 117 | 70 118 | 0 119 | 3 120 | 121 | 72 122 | 65 123 | 73 124 | 0 125 | 40 126 | 0.0000000000000000 127 | 0 128 | LTYPE 129 | 5 130 | 5 131 | 330 132 | 2 133 | 100 134 | AcDbSymbolTableRecord 135 | 100 136 | AcDbLinetypeTableRecord 137 | 2 138 | Continuous 139 | 70 140 | 0 141 | 3 142 | Solid Line 143 | 72 144 | 65 145 | 73 146 | 0 147 | 40 148 | 0.0000000000000000 149 | 0 150 | LTYPE 151 | 5 152 | 6 153 | 330 154 | 2 155 | 100 156 | AcDbSymbolTableRecord 157 | 100 158 | AcDbLinetypeTableRecord 159 | 2 160 | HIDDEN 161 | 70 162 | 0 163 | 3 164 | Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ _ 165 | 72 166 | 65 167 | 73 168 | 2 169 | 40 170 | 0.3750000000000000 171 | 49 172 | 0.2500000000000000 173 | 74 174 | 0 175 | 49 176 | -0.1250000000000000 177 | 74 178 | 0 179 | 0 180 | LTYPE 181 | 5 182 | 7 183 | 330 184 | 2 185 | 100 186 | AcDbSymbolTableRecord 187 | 100 188 | AcDbLinetypeTableRecord 189 | 2 190 | DASHDOT 191 | 70 192 | 0 193 | 3 194 | Dash dot __ . __ . __ . __ . __ . __ . __ . __ 195 | 72 196 | 65 197 | 73 198 | 4 199 | 40 200 | 1.0000000000000000 201 | 49 202 | 0.5000000000000000 203 | 74 204 | 0 205 | 49 206 | -0.2500000000000000 207 | 74 208 | 0 209 | 49 210 | 0.0000000000000000 211 | 74 212 | 0 213 | 49 214 | -0.2500000000000000 215 | 74 216 | 0 217 | 0 218 | ENDTAB 219 | 0 220 | TABLE 221 | 2 222 | LAYER 223 | 5 224 | 8 225 | 100 226 | AcDbSymbolTable 227 | 70 228 | 1 229 | 0 230 | LAYER 231 | 5 232 | 9 233 | 330 234 | 8 235 | 100 236 | AcDbSymbolTableRecord 237 | 100 238 | AcDbLayerTableRecord 239 | 2 240 | 0 241 | 70 242 | 0 243 | 62 244 | 7 245 | 6 246 | Continuous 247 | 370 248 | -3 249 | 390 250 | 22 251 | 0 252 | ENDTAB 253 | 0 254 | TABLE 255 | 2 256 | STYLE 257 | 5 258 | A 259 | 100 260 | AcDbSymbolTable 261 | 70 262 | 1 263 | 0 264 | STYLE 265 | 5 266 | B 267 | 330 268 | A 269 | 100 270 | AcDbSymbolTableRecord 271 | 100 272 | AcDbTextStyleTableRecord 273 | 2 274 | Standard 275 | 70 276 | 0 277 | 40 278 | 0.0000000000000000 279 | 41 280 | 1.0000000000000000 281 | 50 282 | 0.0000000000000000 283 | 71 284 | 0 285 | 42 286 | 100.0000000000000000 287 | 3 288 | arial.ttf 289 | 4 290 | 291 | 0 292 | ENDTAB 293 | 0 294 | TABLE 295 | 2 296 | VIEW 297 | 5 298 | C 299 | 100 300 | AcDbSymbolTable 301 | 70 302 | 0 303 | 0 304 | ENDTAB 305 | 0 306 | TABLE 307 | 2 308 | UCS 309 | 5 310 | D 311 | 100 312 | AcDbSymbolTable 313 | 70 314 | 0 315 | 0 316 | ENDTAB 317 | 0 318 | TABLE 319 | 2 320 | APPID 321 | 5 322 | E 323 | 100 324 | AcDbSymbolTable 325 | 70 326 | 1 327 | 0 328 | APPID 329 | 5 330 | F 331 | 330 332 | E 333 | 100 334 | AcDbSymbolTableRecord 335 | 100 336 | AcDbRegAppTableRecord 337 | 2 338 | ACAD 339 | 70 340 | 0 341 | 0 342 | ENDTAB 343 | 0 344 | TABLE 345 | 2 346 | DIMSTYLE 347 | 5 348 | 10 349 | 100 350 | AcDbSymbolTable 351 | 70 352 | 0 353 | 100 354 | AcDbDimStyleTable 355 | 71 356 | 0 357 | 0 358 | ENDTAB 359 | 0 360 | TABLE 361 | 2 362 | BLOCK_RECORD 363 | 5 364 | 11 365 | 100 366 | AcDbSymbolTable 367 | 70 368 | 3 369 | 0 370 | BLOCK_RECORD 371 | 5 372 | 12 373 | 330 374 | 11 375 | 100 376 | AcDbSymbolTableRecord 377 | 100 378 | AcDbBlockTableRecord 379 | 2 380 | *Model_Space 381 | 70 382 | 0 383 | 280 384 | 1 385 | 281 386 | 0 387 | 0 388 | BLOCK_RECORD 389 | 5 390 | 13 391 | 330 392 | 11 393 | 100 394 | AcDbSymbolTableRecord 395 | 100 396 | AcDbBlockTableRecord 397 | 2 398 | *Paper_Space 399 | 70 400 | 0 401 | 280 402 | 1 403 | 281 404 | 0 405 | 0 406 | BLOCK_RECORD 407 | 5 408 | 14 409 | 330 410 | 11 411 | 100 412 | AcDbSymbolTableRecord 413 | 100 414 | AcDbBlockTableRecord 415 | 2 416 | *Paper_Space0 417 | 70 418 | 0 419 | 280 420 | 1 421 | 281 422 | 0 423 | 0 424 | ENDTAB 425 | 0 426 | ENDSEC 427 | 0 428 | SECTION 429 | 2 430 | BLOCKS 431 | 0 432 | BLOCK 433 | 5 434 | 15 435 | 100 436 | AcDbEntity 437 | 8 438 | 0 439 | 100 440 | AcDbBlockBegin 441 | 2 442 | *Model_Space 443 | 70 444 | 0 445 | 10 446 | 0.0000000000000000 447 | 20 448 | 0.0000000000000000 449 | 30 450 | 0.0000000000000000 451 | 3 452 | *Model_Space 453 | 1 454 | 455 | 0 456 | ENDBLK 457 | 5 458 | 16 459 | 100 460 | AcDbEntity 461 | 8 462 | 0 463 | 100 464 | AcDbBlockEnd 465 | 0 466 | BLOCK 467 | 5 468 | 17 469 | 100 470 | AcDbEntity 471 | 8 472 | 0 473 | 100 474 | AcDbBlockBegin 475 | 2 476 | *Paper_Space 477 | 70 478 | 0 479 | 10 480 | 0.0000000000000000 481 | 20 482 | 0.0000000000000000 483 | 30 484 | 0.0000000000000000 485 | 3 486 | *Paper_Space 487 | 1 488 | 489 | 0 490 | ENDBLK 491 | 5 492 | 18 493 | 100 494 | AcDbEntity 495 | 8 496 | 0 497 | 100 498 | AcDbBlockEnd 499 | 0 500 | BLOCK 501 | 5 502 | 19 503 | 100 504 | AcDbEntity 505 | 8 506 | 0 507 | 100 508 | AcDbBlockBegin 509 | 2 510 | *Paper_Space0 511 | 70 512 | 0 513 | 10 514 | 0.0000000000000000 515 | 20 516 | 0.0000000000000000 517 | 30 518 | 0.0000000000000000 519 | 3 520 | *Paper_Space0 521 | 1 522 | 523 | 0 524 | ENDBLK 525 | 5 526 | 1A 527 | 100 528 | AcDbEntity 529 | 8 530 | 0 531 | 100 532 | AcDbBlockEnd 533 | 0 534 | ENDSEC 535 | 0 536 | SECTION 537 | 2 538 | ENTITIES 539 | 0 540 | POINT 541 | 5 542 | 1B 543 | 100 544 | AcDbEntity 545 | 8 546 | 0 547 | 100 548 | AcDbPoint 549 | 10 550 | 0.0000000000000000 551 | 20 552 | 0.0000000000000000 553 | 30 554 | 0.0000000000000000 555 | 0 556 | POINT 557 | 5 558 | 1C 559 | 100 560 | AcDbEntity 561 | 8 562 | 0 563 | 100 564 | AcDbPoint 565 | 10 566 | 100.0000000000000000 567 | 20 568 | 100.0000000000000000 569 | 30 570 | 0.0000000000000000 571 | 0 572 | POINT 573 | 5 574 | 1D 575 | 100 576 | AcDbEntity 577 | 8 578 | 0 579 | 100 580 | AcDbPoint 581 | 10 582 | 100.0000000000000000 583 | 20 584 | 200.0000000000000000 585 | 30 586 | 0.0000000000000000 587 | 0 588 | ENDSEC 589 | 0 590 | SECTION 591 | 2 592 | OBJECTS 593 | 0 594 | DICTIONARY 595 | 5 596 | 1E 597 | 100 598 | AcDbDictionary 599 | 281 600 | 1 601 | 3 602 | ACAD_GROUP 603 | 350 604 | 23 605 | 3 606 | ACAD_PLOTSTYLENAME 607 | 350 608 | 21 609 | 0 610 | ACDBDICTIONARYWDFLT 611 | 5 612 | 21 613 | 102 614 | {ACAD_REACTORS 615 | 330 616 | 1E 617 | 102 618 | } 619 | 330 620 | 1E 621 | 100 622 | AcDbDictionary 623 | 281 624 | 1 625 | 3 626 | Normal 627 | 350 628 | 22 629 | 100 630 | AcDbDictionaryWithDefault 631 | 340 632 | 22 633 | 0 634 | ACDBPLACEHOLDER 635 | 5 636 | 22 637 | 102 638 | {ACAD_REACTORS 639 | 330 640 | 21 641 | 102 642 | } 643 | 330 644 | 21 645 | 0 646 | DICTIONARY 647 | 5 648 | 23 649 | 100 650 | AcDbDictionary 651 | 281 652 | 1 653 | 0 654 | ENDSEC 655 | 0 656 | EOF 657 | -------------------------------------------------------------------------------- /testdata/mypoint_with_extent.dxf: -------------------------------------------------------------------------------- 1 | 0 2 | SECTION 3 | 2 4 | HEADER 5 | 9 6 | $ACADVER 7 | 1 8 | AC1015 9 | 9 10 | $INSBASE 11 | 10 12 | 0.0000000000000000 13 | 20 14 | 0.0000000000000000 15 | 30 16 | 0.0000000000000000 17 | 9 18 | $INSUNITS 19 | 70 20 | 0 21 | 9 22 | $LUNITS 23 | 70 24 | 2 25 | 9 26 | $EXTMIN 27 | 10 28 | 0.0000000000000000 29 | 20 30 | 0.0000000000000000 31 | 30 32 | 0.0000000000000000 33 | 9 34 | $EXTMAX 35 | 10 36 | 100.0000000000000000 37 | 20 38 | 200.0000000000000000 39 | 30 40 | 0.0000000000000000 41 | 9 42 | $LTSCALE 43 | 40 44 | 1.0000000000000000 45 | 9 46 | $HANDSEED 47 | 5 48 | 24 49 | 0 50 | ENDSEC 51 | 0 52 | SECTION 53 | 2 54 | CLASSES 55 | 0 56 | ENDSEC 57 | 0 58 | SECTION 59 | 2 60 | TABLES 61 | 0 62 | TABLE 63 | 2 64 | VPORT 65 | 5 66 | 1 67 | 100 68 | AcDbSymbolTable 69 | 70 70 | 0 71 | 0 72 | ENDTAB 73 | 0 74 | TABLE 75 | 2 76 | LTYPE 77 | 5 78 | 2 79 | 100 80 | AcDbSymbolTable 81 | 70 82 | 5 83 | 0 84 | LTYPE 85 | 5 86 | 3 87 | 330 88 | 2 89 | 100 90 | AcDbSymbolTableRecord 91 | 100 92 | AcDbLinetypeTableRecord 93 | 2 94 | ByLayer 95 | 70 96 | 0 97 | 3 98 | 99 | 72 100 | 65 101 | 73 102 | 0 103 | 40 104 | 0.0000000000000000 105 | 0 106 | LTYPE 107 | 5 108 | 4 109 | 330 110 | 2 111 | 100 112 | AcDbSymbolTableRecord 113 | 100 114 | AcDbLinetypeTableRecord 115 | 2 116 | ByBlock 117 | 70 118 | 0 119 | 3 120 | 121 | 72 122 | 65 123 | 73 124 | 0 125 | 40 126 | 0.0000000000000000 127 | 0 128 | LTYPE 129 | 5 130 | 5 131 | 330 132 | 2 133 | 100 134 | AcDbSymbolTableRecord 135 | 100 136 | AcDbLinetypeTableRecord 137 | 2 138 | Continuous 139 | 70 140 | 0 141 | 3 142 | Solid Line 143 | 72 144 | 65 145 | 73 146 | 0 147 | 40 148 | 0.0000000000000000 149 | 0 150 | LTYPE 151 | 5 152 | 6 153 | 330 154 | 2 155 | 100 156 | AcDbSymbolTableRecord 157 | 100 158 | AcDbLinetypeTableRecord 159 | 2 160 | HIDDEN 161 | 70 162 | 0 163 | 3 164 | Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ _ 165 | 72 166 | 65 167 | 73 168 | 2 169 | 40 170 | 0.3750000000000000 171 | 49 172 | 0.2500000000000000 173 | 74 174 | 0 175 | 49 176 | -0.1250000000000000 177 | 74 178 | 0 179 | 0 180 | LTYPE 181 | 5 182 | 7 183 | 330 184 | 2 185 | 100 186 | AcDbSymbolTableRecord 187 | 100 188 | AcDbLinetypeTableRecord 189 | 2 190 | DASHDOT 191 | 70 192 | 0 193 | 3 194 | Dash dot __ . __ . __ . __ . __ . __ . __ . __ 195 | 72 196 | 65 197 | 73 198 | 4 199 | 40 200 | 1.0000000000000000 201 | 49 202 | 0.5000000000000000 203 | 74 204 | 0 205 | 49 206 | -0.2500000000000000 207 | 74 208 | 0 209 | 49 210 | 0.0000000000000000 211 | 74 212 | 0 213 | 49 214 | -0.2500000000000000 215 | 74 216 | 0 217 | 0 218 | ENDTAB 219 | 0 220 | TABLE 221 | 2 222 | LAYER 223 | 5 224 | 8 225 | 100 226 | AcDbSymbolTable 227 | 70 228 | 1 229 | 0 230 | LAYER 231 | 5 232 | 9 233 | 330 234 | 8 235 | 100 236 | AcDbSymbolTableRecord 237 | 100 238 | AcDbLayerTableRecord 239 | 2 240 | 0 241 | 70 242 | 0 243 | 62 244 | 7 245 | 6 246 | Continuous 247 | 370 248 | -3 249 | 390 250 | 22 251 | 0 252 | ENDTAB 253 | 0 254 | TABLE 255 | 2 256 | STYLE 257 | 5 258 | A 259 | 100 260 | AcDbSymbolTable 261 | 70 262 | 1 263 | 0 264 | STYLE 265 | 5 266 | B 267 | 330 268 | A 269 | 100 270 | AcDbSymbolTableRecord 271 | 100 272 | AcDbTextStyleTableRecord 273 | 2 274 | Standard 275 | 70 276 | 0 277 | 40 278 | 0.0000000000000000 279 | 41 280 | 1.0000000000000000 281 | 50 282 | 0.0000000000000000 283 | 71 284 | 0 285 | 42 286 | 100.0000000000000000 287 | 3 288 | arial.ttf 289 | 4 290 | 291 | 0 292 | ENDTAB 293 | 0 294 | TABLE 295 | 2 296 | VIEW 297 | 5 298 | C 299 | 100 300 | AcDbSymbolTable 301 | 70 302 | 0 303 | 0 304 | ENDTAB 305 | 0 306 | TABLE 307 | 2 308 | UCS 309 | 5 310 | D 311 | 100 312 | AcDbSymbolTable 313 | 70 314 | 0 315 | 0 316 | ENDTAB 317 | 0 318 | TABLE 319 | 2 320 | APPID 321 | 5 322 | E 323 | 100 324 | AcDbSymbolTable 325 | 70 326 | 1 327 | 0 328 | APPID 329 | 5 330 | F 331 | 330 332 | E 333 | 100 334 | AcDbSymbolTableRecord 335 | 100 336 | AcDbRegAppTableRecord 337 | 2 338 | ACAD 339 | 70 340 | 0 341 | 0 342 | ENDTAB 343 | 0 344 | TABLE 345 | 2 346 | DIMSTYLE 347 | 5 348 | 10 349 | 100 350 | AcDbSymbolTable 351 | 70 352 | 0 353 | 100 354 | AcDbDimStyleTable 355 | 71 356 | 0 357 | 0 358 | ENDTAB 359 | 0 360 | TABLE 361 | 2 362 | BLOCK_RECORD 363 | 5 364 | 11 365 | 100 366 | AcDbSymbolTable 367 | 70 368 | 3 369 | 0 370 | BLOCK_RECORD 371 | 5 372 | 12 373 | 330 374 | 11 375 | 100 376 | AcDbSymbolTableRecord 377 | 100 378 | AcDbBlockTableRecord 379 | 2 380 | *Model_Space 381 | 70 382 | 0 383 | 280 384 | 1 385 | 281 386 | 0 387 | 0 388 | BLOCK_RECORD 389 | 5 390 | 13 391 | 330 392 | 11 393 | 100 394 | AcDbSymbolTableRecord 395 | 100 396 | AcDbBlockTableRecord 397 | 2 398 | *Paper_Space 399 | 70 400 | 0 401 | 280 402 | 1 403 | 281 404 | 0 405 | 0 406 | BLOCK_RECORD 407 | 5 408 | 14 409 | 330 410 | 11 411 | 100 412 | AcDbSymbolTableRecord 413 | 100 414 | AcDbBlockTableRecord 415 | 2 416 | *Paper_Space0 417 | 70 418 | 0 419 | 280 420 | 1 421 | 281 422 | 0 423 | 0 424 | ENDTAB 425 | 0 426 | ENDSEC 427 | 0 428 | SECTION 429 | 2 430 | BLOCKS 431 | 0 432 | BLOCK 433 | 5 434 | 15 435 | 100 436 | AcDbEntity 437 | 8 438 | 0 439 | 100 440 | AcDbBlockBegin 441 | 2 442 | *Model_Space 443 | 70 444 | 0 445 | 10 446 | 0.0000000000000000 447 | 20 448 | 0.0000000000000000 449 | 30 450 | 0.0000000000000000 451 | 3 452 | *Model_Space 453 | 1 454 | 455 | 0 456 | ENDBLK 457 | 5 458 | 16 459 | 100 460 | AcDbEntity 461 | 8 462 | 0 463 | 100 464 | AcDbBlockEnd 465 | 0 466 | BLOCK 467 | 5 468 | 17 469 | 100 470 | AcDbEntity 471 | 8 472 | 0 473 | 100 474 | AcDbBlockBegin 475 | 2 476 | *Paper_Space 477 | 70 478 | 0 479 | 10 480 | 0.0000000000000000 481 | 20 482 | 0.0000000000000000 483 | 30 484 | 0.0000000000000000 485 | 3 486 | *Paper_Space 487 | 1 488 | 489 | 0 490 | ENDBLK 491 | 5 492 | 18 493 | 100 494 | AcDbEntity 495 | 8 496 | 0 497 | 100 498 | AcDbBlockEnd 499 | 0 500 | BLOCK 501 | 5 502 | 19 503 | 100 504 | AcDbEntity 505 | 8 506 | 0 507 | 100 508 | AcDbBlockBegin 509 | 2 510 | *Paper_Space0 511 | 70 512 | 0 513 | 10 514 | 0.0000000000000000 515 | 20 516 | 0.0000000000000000 517 | 30 518 | 0.0000000000000000 519 | 3 520 | *Paper_Space0 521 | 1 522 | 523 | 0 524 | ENDBLK 525 | 5 526 | 1A 527 | 100 528 | AcDbEntity 529 | 8 530 | 0 531 | 100 532 | AcDbBlockEnd 533 | 0 534 | ENDSEC 535 | 0 536 | SECTION 537 | 2 538 | ENTITIES 539 | 0 540 | POINT 541 | 5 542 | 1B 543 | 100 544 | AcDbEntity 545 | 8 546 | 0 547 | 100 548 | AcDbPoint 549 | 10 550 | 0.0000000000000000 551 | 20 552 | 0.0000000000000000 553 | 30 554 | 0.0000000000000000 555 | 0 556 | POINT 557 | 5 558 | 1C 559 | 100 560 | AcDbEntity 561 | 8 562 | 0 563 | 100 564 | AcDbPoint 565 | 10 566 | 100.0000000000000000 567 | 20 568 | 100.0000000000000000 569 | 30 570 | 0.0000000000000000 571 | 0 572 | POINT 573 | 5 574 | 1D 575 | 100 576 | AcDbEntity 577 | 8 578 | 0 579 | 100 580 | AcDbPoint 581 | 10 582 | 100.0000000000000000 583 | 20 584 | 200.0000000000000000 585 | 30 586 | 0.0000000000000000 587 | 0 588 | ENDSEC 589 | 0 590 | SECTION 591 | 2 592 | OBJECTS 593 | 0 594 | DICTIONARY 595 | 5 596 | 1E 597 | 100 598 | AcDbDictionary 599 | 281 600 | 1 601 | 3 602 | ACAD_GROUP 603 | 350 604 | 23 605 | 3 606 | ACAD_PLOTSTYLENAME 607 | 350 608 | 21 609 | 0 610 | ACDBDICTIONARYWDFLT 611 | 5 612 | 21 613 | 102 614 | {ACAD_REACTORS 615 | 330 616 | 1E 617 | 102 618 | } 619 | 330 620 | 1E 621 | 100 622 | AcDbDictionary 623 | 281 624 | 1 625 | 3 626 | Normal 627 | 350 628 | 22 629 | 100 630 | AcDbDictionaryWithDefault 631 | 340 632 | 22 633 | 0 634 | ACDBPLACEHOLDER 635 | 5 636 | 22 637 | 102 638 | {ACAD_REACTORS 639 | 330 640 | 21 641 | 102 642 | } 643 | 330 644 | 21 645 | 0 646 | DICTIONARY 647 | 5 648 | 23 649 | 100 650 | AcDbDictionary 651 | 281 652 | 1 653 | 0 654 | ENDSEC 655 | 0 656 | EOF 657 | -------------------------------------------------------------------------------- /testdata/mypoint_with_units.dxf: -------------------------------------------------------------------------------- 1 | 0 2 | SECTION 3 | 2 4 | HEADER 5 | 9 6 | $ACADVER 7 | 1 8 | AC1015 9 | 9 10 | $INSBASE 11 | 10 12 | 0.0000000000000000 13 | 20 14 | 0.0000000000000000 15 | 30 16 | 0.0000000000000000 17 | 9 18 | $INSUNITS 19 | 70 20 | 1 21 | 9 22 | $LUNITS 23 | 70 24 | 4 25 | 9 26 | $EXTMIN 27 | 10 28 | 0.0000000000000000 29 | 20 30 | 0.0000000000000000 31 | 30 32 | 0.0000000000000000 33 | 9 34 | $EXTMAX 35 | 10 36 | 100.0000000000000000 37 | 20 38 | 200.0000000000000000 39 | 30 40 | 0.0000000000000000 41 | 9 42 | $LTSCALE 43 | 40 44 | 1.0000000000000000 45 | 9 46 | $HANDSEED 47 | 5 48 | 24 49 | 0 50 | ENDSEC 51 | 0 52 | SECTION 53 | 2 54 | CLASSES 55 | 0 56 | ENDSEC 57 | 0 58 | SECTION 59 | 2 60 | TABLES 61 | 0 62 | TABLE 63 | 2 64 | VPORT 65 | 5 66 | 1 67 | 100 68 | AcDbSymbolTable 69 | 70 70 | 0 71 | 0 72 | ENDTAB 73 | 0 74 | TABLE 75 | 2 76 | LTYPE 77 | 5 78 | 2 79 | 100 80 | AcDbSymbolTable 81 | 70 82 | 5 83 | 0 84 | LTYPE 85 | 5 86 | 3 87 | 330 88 | 2 89 | 100 90 | AcDbSymbolTableRecord 91 | 100 92 | AcDbLinetypeTableRecord 93 | 2 94 | ByLayer 95 | 70 96 | 0 97 | 3 98 | 99 | 72 100 | 65 101 | 73 102 | 0 103 | 40 104 | 0.0000000000000000 105 | 0 106 | LTYPE 107 | 5 108 | 4 109 | 330 110 | 2 111 | 100 112 | AcDbSymbolTableRecord 113 | 100 114 | AcDbLinetypeTableRecord 115 | 2 116 | ByBlock 117 | 70 118 | 0 119 | 3 120 | 121 | 72 122 | 65 123 | 73 124 | 0 125 | 40 126 | 0.0000000000000000 127 | 0 128 | LTYPE 129 | 5 130 | 5 131 | 330 132 | 2 133 | 100 134 | AcDbSymbolTableRecord 135 | 100 136 | AcDbLinetypeTableRecord 137 | 2 138 | Continuous 139 | 70 140 | 0 141 | 3 142 | Solid Line 143 | 72 144 | 65 145 | 73 146 | 0 147 | 40 148 | 0.0000000000000000 149 | 0 150 | LTYPE 151 | 5 152 | 6 153 | 330 154 | 2 155 | 100 156 | AcDbSymbolTableRecord 157 | 100 158 | AcDbLinetypeTableRecord 159 | 2 160 | HIDDEN 161 | 70 162 | 0 163 | 3 164 | Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ _ 165 | 72 166 | 65 167 | 73 168 | 2 169 | 40 170 | 0.3750000000000000 171 | 49 172 | 0.2500000000000000 173 | 74 174 | 0 175 | 49 176 | -0.1250000000000000 177 | 74 178 | 0 179 | 0 180 | LTYPE 181 | 5 182 | 7 183 | 330 184 | 2 185 | 100 186 | AcDbSymbolTableRecord 187 | 100 188 | AcDbLinetypeTableRecord 189 | 2 190 | DASHDOT 191 | 70 192 | 0 193 | 3 194 | Dash dot __ . __ . __ . __ . __ . __ . __ . __ 195 | 72 196 | 65 197 | 73 198 | 4 199 | 40 200 | 1.0000000000000000 201 | 49 202 | 0.5000000000000000 203 | 74 204 | 0 205 | 49 206 | -0.2500000000000000 207 | 74 208 | 0 209 | 49 210 | 0.0000000000000000 211 | 74 212 | 0 213 | 49 214 | -0.2500000000000000 215 | 74 216 | 0 217 | 0 218 | ENDTAB 219 | 0 220 | TABLE 221 | 2 222 | LAYER 223 | 5 224 | 8 225 | 100 226 | AcDbSymbolTable 227 | 70 228 | 1 229 | 0 230 | LAYER 231 | 5 232 | 9 233 | 330 234 | 8 235 | 100 236 | AcDbSymbolTableRecord 237 | 100 238 | AcDbLayerTableRecord 239 | 2 240 | 0 241 | 70 242 | 0 243 | 62 244 | 7 245 | 6 246 | Continuous 247 | 370 248 | -3 249 | 390 250 | 22 251 | 0 252 | ENDTAB 253 | 0 254 | TABLE 255 | 2 256 | STYLE 257 | 5 258 | A 259 | 100 260 | AcDbSymbolTable 261 | 70 262 | 1 263 | 0 264 | STYLE 265 | 5 266 | B 267 | 330 268 | A 269 | 100 270 | AcDbSymbolTableRecord 271 | 100 272 | AcDbTextStyleTableRecord 273 | 2 274 | Standard 275 | 70 276 | 0 277 | 40 278 | 0.0000000000000000 279 | 41 280 | 1.0000000000000000 281 | 50 282 | 0.0000000000000000 283 | 71 284 | 0 285 | 42 286 | 100.0000000000000000 287 | 3 288 | arial.ttf 289 | 4 290 | 291 | 0 292 | ENDTAB 293 | 0 294 | TABLE 295 | 2 296 | VIEW 297 | 5 298 | C 299 | 100 300 | AcDbSymbolTable 301 | 70 302 | 0 303 | 0 304 | ENDTAB 305 | 0 306 | TABLE 307 | 2 308 | UCS 309 | 5 310 | D 311 | 100 312 | AcDbSymbolTable 313 | 70 314 | 0 315 | 0 316 | ENDTAB 317 | 0 318 | TABLE 319 | 2 320 | APPID 321 | 5 322 | E 323 | 100 324 | AcDbSymbolTable 325 | 70 326 | 1 327 | 0 328 | APPID 329 | 5 330 | F 331 | 330 332 | E 333 | 100 334 | AcDbSymbolTableRecord 335 | 100 336 | AcDbRegAppTableRecord 337 | 2 338 | ACAD 339 | 70 340 | 0 341 | 0 342 | ENDTAB 343 | 0 344 | TABLE 345 | 2 346 | DIMSTYLE 347 | 5 348 | 10 349 | 100 350 | AcDbSymbolTable 351 | 70 352 | 0 353 | 100 354 | AcDbDimStyleTable 355 | 71 356 | 0 357 | 0 358 | ENDTAB 359 | 0 360 | TABLE 361 | 2 362 | BLOCK_RECORD 363 | 5 364 | 11 365 | 100 366 | AcDbSymbolTable 367 | 70 368 | 3 369 | 0 370 | BLOCK_RECORD 371 | 5 372 | 12 373 | 330 374 | 11 375 | 100 376 | AcDbSymbolTableRecord 377 | 100 378 | AcDbBlockTableRecord 379 | 2 380 | *Model_Space 381 | 70 382 | 0 383 | 280 384 | 1 385 | 281 386 | 0 387 | 0 388 | BLOCK_RECORD 389 | 5 390 | 13 391 | 330 392 | 11 393 | 100 394 | AcDbSymbolTableRecord 395 | 100 396 | AcDbBlockTableRecord 397 | 2 398 | *Paper_Space 399 | 70 400 | 0 401 | 280 402 | 1 403 | 281 404 | 0 405 | 0 406 | BLOCK_RECORD 407 | 5 408 | 14 409 | 330 410 | 11 411 | 100 412 | AcDbSymbolTableRecord 413 | 100 414 | AcDbBlockTableRecord 415 | 2 416 | *Paper_Space0 417 | 70 418 | 0 419 | 280 420 | 1 421 | 281 422 | 0 423 | 0 424 | ENDTAB 425 | 0 426 | ENDSEC 427 | 0 428 | SECTION 429 | 2 430 | BLOCKS 431 | 0 432 | BLOCK 433 | 5 434 | 15 435 | 100 436 | AcDbEntity 437 | 8 438 | 0 439 | 100 440 | AcDbBlockBegin 441 | 2 442 | *Model_Space 443 | 70 444 | 0 445 | 10 446 | 0.0000000000000000 447 | 20 448 | 0.0000000000000000 449 | 30 450 | 0.0000000000000000 451 | 3 452 | *Model_Space 453 | 1 454 | 455 | 0 456 | ENDBLK 457 | 5 458 | 16 459 | 100 460 | AcDbEntity 461 | 8 462 | 0 463 | 100 464 | AcDbBlockEnd 465 | 0 466 | BLOCK 467 | 5 468 | 17 469 | 100 470 | AcDbEntity 471 | 8 472 | 0 473 | 100 474 | AcDbBlockBegin 475 | 2 476 | *Paper_Space 477 | 70 478 | 0 479 | 10 480 | 0.0000000000000000 481 | 20 482 | 0.0000000000000000 483 | 30 484 | 0.0000000000000000 485 | 3 486 | *Paper_Space 487 | 1 488 | 489 | 0 490 | ENDBLK 491 | 5 492 | 18 493 | 100 494 | AcDbEntity 495 | 8 496 | 0 497 | 100 498 | AcDbBlockEnd 499 | 0 500 | BLOCK 501 | 5 502 | 19 503 | 100 504 | AcDbEntity 505 | 8 506 | 0 507 | 100 508 | AcDbBlockBegin 509 | 2 510 | *Paper_Space0 511 | 70 512 | 0 513 | 10 514 | 0.0000000000000000 515 | 20 516 | 0.0000000000000000 517 | 30 518 | 0.0000000000000000 519 | 3 520 | *Paper_Space0 521 | 1 522 | 523 | 0 524 | ENDBLK 525 | 5 526 | 1A 527 | 100 528 | AcDbEntity 529 | 8 530 | 0 531 | 100 532 | AcDbBlockEnd 533 | 0 534 | ENDSEC 535 | 0 536 | SECTION 537 | 2 538 | ENTITIES 539 | 0 540 | POINT 541 | 5 542 | 1B 543 | 100 544 | AcDbEntity 545 | 8 546 | 0 547 | 100 548 | AcDbPoint 549 | 10 550 | 0.0000000000000000 551 | 20 552 | 0.0000000000000000 553 | 30 554 | 0.0000000000000000 555 | 0 556 | POINT 557 | 5 558 | 1C 559 | 100 560 | AcDbEntity 561 | 8 562 | 0 563 | 100 564 | AcDbPoint 565 | 10 566 | 100.0000000000000000 567 | 20 568 | 100.0000000000000000 569 | 30 570 | 0.0000000000000000 571 | 0 572 | POINT 573 | 5 574 | 1D 575 | 100 576 | AcDbEntity 577 | 8 578 | 0 579 | 100 580 | AcDbPoint 581 | 10 582 | 100.0000000000000000 583 | 20 584 | 200.0000000000000000 585 | 30 586 | 0.0000000000000000 587 | 0 588 | ENDSEC 589 | 0 590 | SECTION 591 | 2 592 | OBJECTS 593 | 0 594 | DICTIONARY 595 | 5 596 | 1E 597 | 100 598 | AcDbDictionary 599 | 281 600 | 1 601 | 3 602 | ACAD_GROUP 603 | 350 604 | 23 605 | 3 606 | ACAD_PLOTSTYLENAME 607 | 350 608 | 21 609 | 0 610 | ACDBDICTIONARYWDFLT 611 | 5 612 | 21 613 | 102 614 | {ACAD_REACTORS 615 | 330 616 | 1E 617 | 102 618 | } 619 | 330 620 | 1E 621 | 100 622 | AcDbDictionary 623 | 281 624 | 1 625 | 3 626 | Normal 627 | 350 628 | 22 629 | 100 630 | AcDbDictionaryWithDefault 631 | 340 632 | 22 633 | 0 634 | ACDBPLACEHOLDER 635 | 5 636 | 22 637 | 102 638 | {ACAD_REACTORS 639 | 330 640 | 21 641 | 102 642 | } 643 | 330 644 | 21 645 | 0 646 | DICTIONARY 647 | 5 648 | 23 649 | 100 650 | AcDbDictionary 651 | 281 652 | 1 653 | 0 654 | ENDSEC 655 | 0 656 | EOF 657 | -------------------------------------------------------------------------------- /dxf_test.go: -------------------------------------------------------------------------------- 1 | package dxf 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha1" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "math" 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | 14 | "github.com/yofu/dxf/color" 15 | "github.com/yofu/dxf/insunit" 16 | "github.com/yofu/dxf/table" 17 | 18 | "github.com/yofu/dxf/drawing" 19 | "github.com/yofu/dxf/entity" 20 | ) 21 | 22 | // TOLERANCE is the epsilon value used in comparing floats. 23 | const TOLERANCE = 0.000001 24 | 25 | // cmpF64 compares two floats to see if they are within the given tolerance. 26 | func cmpF64(f1, f2 float64) bool { 27 | if math.IsInf(f1, 1) { 28 | return math.IsInf(f2, 1) 29 | } 30 | if math.IsInf(f2, 1) { 31 | return math.IsInf(f1, 1) 32 | } 33 | if math.IsInf(f1, -1) { 34 | return math.IsInf(f2, -1) 35 | } 36 | if math.IsInf(f2, -1) { 37 | return math.IsInf(f1, -1) 38 | } 39 | return math.Abs(f1-f2) < TOLERANCE 40 | } 41 | 42 | func checkEntities(t *testing.T, expected, got entity.Entities) bool { 43 | if len(expected) != len(got) { 44 | t.Errorf("number of entities, expected %v got %v", len(expected), len(got)) 45 | return false 46 | } 47 | for i, ee := range expected { 48 | switch et := ee.(type) { 49 | case *entity.Point: 50 | p, ok := got[i].(*entity.Point) 51 | if !ok { 52 | t.Errorf("type for %v, expected %T got %T", i, et, got[i]) 53 | return false 54 | } 55 | if len(et.Coord) != len(p.Coord) { 56 | t.Errorf("point %v : coord len, expected %v got %v", i, len(et.Coord), len(p.Coord)) 57 | return false 58 | } 59 | for j, crd := range et.Coord { 60 | if !cmpF64(crd, p.Coord[j]) { 61 | t.Errorf("point %v : coord %v, expected %v got %v", i, j, crd, p.Coord[j]) 62 | t.Logf("ePoint %v, Point %v", et.Coord, p.Coord) 63 | return false 64 | } 65 | } 66 | } 67 | } 68 | return true 69 | } 70 | 71 | func hashBytes(b []byte) string { 72 | return fmt.Sprintf("%x", sha1.Sum(b)) 73 | } 74 | func hashFile(filename string) (string, error) { 75 | tfile := filepath.Join("testdata", filename) 76 | f, err := os.Open(tfile) 77 | if err != nil { 78 | return "", err 79 | } 80 | text, err := ioutil.ReadAll(f) 81 | f.Close() 82 | if err != nil { 83 | return "", err 84 | } 85 | return hashBytes(text), nil 86 | } 87 | 88 | func TestFromStringData(t *testing.T) { 89 | type tcase struct { 90 | filename string 91 | ExpectedEntities entity.Entities 92 | fn func(*testing.T, *drawing.Drawing, tcase) 93 | } 94 | fn := func(tc tcase) (string, func(*testing.T)) { 95 | return tc.filename, func(t *testing.T) { 96 | tfile := filepath.Join("testdata", tc.filename) 97 | 98 | data, err := ioutil.ReadFile(tfile) 99 | if err != nil { 100 | t.Errorf("file, could not open file %v : %v", tfile, err) 101 | } 102 | d, err := FromStringData(string(data)) 103 | if err != nil { 104 | t.Errorf("error, expected nil, got %v", err) 105 | return 106 | } 107 | 108 | if tc.ExpectedEntities == nil { 109 | // Let's go ahead and log out the entities in the file. 110 | // This is useful for build out test cases. 111 | t.Logf("Number of entities: %d", d.Entities()) 112 | for i, e := range d.Entities() { 113 | t.Logf("\t%3d:[%[2]T]%[2]v", i, e) 114 | } 115 | } else if !checkEntities(t, tc.ExpectedEntities, d.Entities()) { 116 | return 117 | } 118 | 119 | if tc.fn != nil { 120 | tc.fn(t, d, tc) 121 | } 122 | } 123 | } 124 | 125 | tests := []tcase{ 126 | { 127 | filename: "point.dxf", 128 | ExpectedEntities: entity.Entities{ 129 | entity.NewPoint(), 130 | entity.NewPoint(100.0, 100.0, 0.0), 131 | entity.NewPoint(200.0, 100.0, 0.0), 132 | }, 133 | }, 134 | { 135 | filename: "arc.dxf", 136 | ExpectedEntities: entity.Entities{ 137 | func() entity.Entity { 138 | cr := entity.NewCircle() 139 | cr.Direction = []float64{0.0, 0.0, 1.0} 140 | cr.Radius = 100 141 | arc := entity.NewArc(cr) 142 | arc.Angle = []float64{0.0, 60.0} 143 | return arc 144 | }(), 145 | }, 146 | }, 147 | } 148 | for _, tc := range tests { 149 | t.Run(fn(tc)) 150 | } 151 | } 152 | 153 | func TestFromFile(t *testing.T) { 154 | type tcase struct { 155 | filename string 156 | ExpectedEntities entity.Entities 157 | fn func(*testing.T, *drawing.Drawing, tcase) 158 | } 159 | fn := func(tc tcase) (string, func(*testing.T)) { 160 | return tc.filename, func(t *testing.T) { 161 | tfile := filepath.Join("testdata", tc.filename) 162 | d, err := FromFile(tfile) 163 | if err != nil { 164 | t.Errorf("error, expected nil, got %v", err) 165 | return 166 | } 167 | 168 | if len(tc.ExpectedEntities) != 0 && !checkEntities(t, tc.ExpectedEntities, d.Entities()) { 169 | return 170 | } 171 | 172 | if tc.fn != nil { 173 | tc.fn(t, d, tc) 174 | } 175 | } 176 | } 177 | 178 | tests := []tcase{ 179 | { 180 | filename: "vertex1.dxf", 181 | ExpectedEntities: entity.Entities{ 182 | entity.NewVertex(1.1, 1.2, 1.3), 183 | }, 184 | }, 185 | { 186 | filename: "point.dxf", 187 | ExpectedEntities: entity.Entities{ 188 | entity.NewPoint(), 189 | entity.NewPoint(100.0, 100.0, 0.0), 190 | entity.NewPoint(200.0, 100.0, 0.0), 191 | }, 192 | }, 193 | { 194 | filename: "mypoint.dxf", 195 | ExpectedEntities: entity.Entities{ 196 | entity.NewPoint(), 197 | entity.NewPoint(100.0, 100.0, 0.0), 198 | entity.NewPoint(100.0, 200.0, 0.0), 199 | }, 200 | }, 201 | { 202 | filename: "mypoint_with_extent.dxf", 203 | ExpectedEntities: entity.Entities{ 204 | entity.NewPoint(), 205 | entity.NewPoint(100.0, 100.0, 0.0), 206 | entity.NewPoint(100.0, 200.0, 0.0), 207 | }, 208 | }, 209 | { 210 | filename: "arc.dxf", 211 | ExpectedEntities: entity.Entities{ 212 | func() entity.Entity { 213 | cr := entity.NewCircle() 214 | cr.Direction = []float64{0.0, 0.0, 1.0} 215 | cr.Radius = 100 216 | arc := entity.NewArc(cr) 217 | arc.Angle = []float64{0.0, 60.0} 218 | return arc 219 | }(), 220 | }, 221 | }, 222 | } 223 | for _, tc := range tests { 224 | t.Run(fn(tc)) 225 | } 226 | } 227 | 228 | func TestNewDrawing(t *testing.T) { 229 | type tcase struct { 230 | filename string 231 | draw func(d *drawing.Drawing) 232 | } 233 | fn := func(tc tcase) (string, func(*testing.T)) { 234 | return tc.filename, func(t *testing.T) { 235 | // calculate the sha256 236 | fileHash, err := hashFile(tc.filename) 237 | if err != nil { 238 | t.Errorf("hash of file(%v) error, expected nil got %v", tc.filename, err) 239 | return 240 | } 241 | d := drawing.New() 242 | tc.draw(d) 243 | var buff bytes.Buffer 244 | _, err = io.Copy(&buff, d) 245 | if err != nil { 246 | t.Errorf("copy of buffer, expected nil got %v", err) 247 | } 248 | buffHash := hashBytes(buff.Bytes()) 249 | if fileHash != buffHash { 250 | t.Errorf("hash, expected %v got %v", fileHash, buffHash) 251 | outputfn := filepath.Join("testdata", buffHash+"_"+tc.filename) 252 | of, err := os.Create(outputfn) 253 | if err != nil { 254 | t.Logf("could not create debug file: %v", outputfn) 255 | return 256 | } 257 | io.Copy(of, &buff) 258 | of.Close() 259 | t.Logf("wrote out file to: %v", outputfn) 260 | } 261 | } 262 | } 263 | tests := []tcase{ 264 | { 265 | filename: "mypoint.dxf", 266 | draw: func(d *drawing.Drawing) { 267 | d.Point(0.0, 0.0, 0.0) 268 | d.Point(100.0, 100.0, 0.0) 269 | d.Point(100.0, 200.0, 0.0) 270 | }, 271 | }, 272 | { 273 | filename: "mypoint_with_extent.dxf", 274 | draw: func(d *drawing.Drawing) { 275 | d.Point(0.0, 0.0, 0.0) 276 | d.Point(100.0, 100.0, 0.0) 277 | d.Point(100.0, 200.0, 0.0) 278 | d.SetExt() 279 | }, 280 | }, 281 | { 282 | filename: "mypoint_with_units.dxf", 283 | draw: func(d *drawing.Drawing) { 284 | d.Point(0.0, 0.0, 0.0) 285 | d.Point(100.0, 100.0, 0.0) 286 | d.Point(100.0, 200.0, 0.0) 287 | d.Header().LtScale = 1 288 | d.Header().InsUnit = insunit.Inches 289 | d.Header().InsLUnit = insunit.Architectural 290 | d.SetExt() 291 | }, 292 | }, 293 | { 294 | filename: "torus.dxf", 295 | draw: func(d *drawing.Drawing) { 296 | d.Header().LtScale = 100.0 297 | d.AddLayer("Toroidal", DefaultColor, DefaultLineType, true) 298 | d.AddLayer("Poloidal", color.Red, table.LT_HIDDEN, true) 299 | z := 0.0 300 | r1 := 200.0 301 | r2 := 500.0 302 | ndiv := 16 303 | dtheta := 2.0 * math.Pi / float64(ndiv) 304 | theta := 0.0 305 | for i := 0; i < ndiv; i++ { 306 | d.ChangeLayer("Toroidal") 307 | d.Circle(0.0, 0.0, z+r1*math.Cos(theta), r2-r1*math.Sin(theta)) 308 | d.ChangeLayer("Poloidal") 309 | c, _ := d.Circle(r2*math.Cos(theta), r2*math.Sin(theta), 0.0, r1) 310 | SetExtrusion(c, []float64{-1.0 * math.Sin(theta), math.Cos(theta), 0.0}) 311 | theta += dtheta 312 | } 313 | }, 314 | }, 315 | { 316 | filename: "my_arc.dxf", 317 | draw: func(d *drawing.Drawing) { 318 | // x , y, z, radius, start, end 319 | d.Arc(0.0, 0.0, 0.0, 100.0, 0.0, 60.0) 320 | }, 321 | }, 322 | } 323 | for _, tc := range tests { 324 | t.Run(fn(tc)) 325 | } 326 | 327 | } 328 | -------------------------------------------------------------------------------- /color/colornumber.go: -------------------------------------------------------------------------------- 1 | // Color Number 2 | package color 3 | 4 | // AutoCAD color index number (code 62) 5 | type ColorNumber uint8 6 | 7 | // color number: 1 - 9 8 | const ( 9 | Red ColorNumber = iota + 1 10 | Yellow 11 | Green 12 | Cyan 13 | Blue 14 | Magenta 15 | White 16 | Grey128 17 | Grey192 18 | ) 19 | 20 | // color number: 250 - 255 21 | const ( 22 | Grey51 = iota + 250 23 | Grey91 24 | Grey132 25 | Grey173 26 | Grey214 27 | Grey255 28 | ) 29 | 30 | // color number to RGB equivalents 31 | var ( 32 | ColorRGB = [][]uint8{ 33 | []uint8{0, 0, 0}, // 0 34 | []uint8{255, 0, 0}, // 1 35 | []uint8{255, 255, 0}, // 2 36 | []uint8{0, 255, 0}, // 3 37 | []uint8{0, 255, 255}, // 4 38 | []uint8{0, 0, 255}, // 5 39 | []uint8{255, 0, 255}, // 6 40 | []uint8{255, 255, 255}, // 7 41 | []uint8{128, 128, 128}, // 8 42 | []uint8{192, 192, 192}, // 9 43 | []uint8{255, 0, 0}, // 10 44 | []uint8{255, 127, 127}, // 11 45 | []uint8{204, 0, 0}, // 12 46 | []uint8{204, 102, 102}, // 13 47 | []uint8{153, 0, 0}, // 14 48 | []uint8{153, 76, 76}, // 15 49 | []uint8{127, 0, 0}, // 16 50 | []uint8{127, 63, 63}, // 17 51 | []uint8{76, 0, 0}, // 18 52 | []uint8{76, 38, 38}, // 19 53 | []uint8{255, 63, 0}, // 20 54 | []uint8{255, 159, 127}, // 21 55 | []uint8{204, 51, 0}, // 22 56 | []uint8{204, 127, 102}, // 23 57 | []uint8{153, 38, 0}, // 24 58 | []uint8{153, 95, 76}, // 25 59 | []uint8{127, 31, 0}, // 26 60 | []uint8{127, 79, 63}, // 27 61 | []uint8{76, 19, 0}, // 28 62 | []uint8{76, 47, 38}, // 29 63 | []uint8{255, 127, 0}, // 30 64 | []uint8{255, 191, 127}, // 31 65 | []uint8{204, 102, 0}, // 32 66 | []uint8{204, 153, 102}, // 33 67 | []uint8{153, 76, 0}, // 34 68 | []uint8{153, 114, 76}, // 35 69 | []uint8{127, 63, 0}, // 36 70 | []uint8{127, 95, 63}, // 37 71 | []uint8{76, 38, 0}, // 38 72 | []uint8{76, 57, 38}, // 39 73 | []uint8{255, 191, 0}, // 40 74 | []uint8{255, 223, 127}, // 41 75 | []uint8{204, 153, 0}, // 42 76 | []uint8{204, 178, 102}, // 43 77 | []uint8{153, 114, 0}, // 44 78 | []uint8{153, 133, 76}, // 45 79 | []uint8{127, 95, 0}, // 46 80 | []uint8{127, 111, 63}, // 47 81 | []uint8{76, 57, 0}, // 48 82 | []uint8{76, 66, 38}, // 49 83 | []uint8{255, 255, 0}, // 50 84 | []uint8{255, 255, 127}, // 51 85 | []uint8{204, 204, 0}, // 52 86 | []uint8{204, 204, 102}, // 53 87 | []uint8{153, 153, 0}, // 54 88 | []uint8{153, 153, 76}, // 55 89 | []uint8{127, 127, 0}, // 56 90 | []uint8{127, 127, 63}, // 57 91 | []uint8{76, 76, 0}, // 58 92 | []uint8{76, 76, 38}, // 59 93 | []uint8{191, 255, 0}, // 60 94 | []uint8{223, 255, 127}, // 61 95 | []uint8{153, 204, 0}, // 62 96 | []uint8{178, 204, 102}, // 63 97 | []uint8{114, 153, 0}, // 64 98 | []uint8{133, 153, 76}, // 65 99 | []uint8{95, 127, 0}, // 66 100 | []uint8{111, 127, 63}, // 67 101 | []uint8{57, 76, 0}, // 68 102 | []uint8{66, 76, 38}, // 69 103 | []uint8{127, 255, 0}, // 70 104 | []uint8{191, 255, 127}, // 71 105 | []uint8{102, 204, 0}, // 72 106 | []uint8{153, 204, 102}, // 73 107 | []uint8{76, 153, 0}, // 74 108 | []uint8{114, 153, 76}, // 75 109 | []uint8{63, 127, 0}, // 76 110 | []uint8{95, 127, 63}, // 77 111 | []uint8{38, 76, 0}, // 78 112 | []uint8{57, 76, 38}, // 79 113 | []uint8{63, 255, 0}, // 80 114 | []uint8{159, 255, 127}, // 81 115 | []uint8{51, 204, 0}, // 82 116 | []uint8{127, 204, 102}, // 83 117 | []uint8{38, 153, 0}, // 84 118 | []uint8{95, 153, 76}, // 85 119 | []uint8{31, 127, 0}, // 86 120 | []uint8{79, 127, 63}, // 87 121 | []uint8{19, 76, 0}, // 88 122 | []uint8{47, 76, 38}, // 89 123 | []uint8{0, 255, 0}, // 90 124 | []uint8{127, 255, 127}, // 91 125 | []uint8{0, 204, 0}, // 92 126 | []uint8{102, 204, 102}, // 93 127 | []uint8{0, 153, 0}, // 94 128 | []uint8{76, 153, 76}, // 95 129 | []uint8{0, 127, 0}, // 96 130 | []uint8{63, 127, 63}, // 97 131 | []uint8{0, 76, 0}, // 98 132 | []uint8{38, 76, 38}, // 99 133 | []uint8{0, 255, 63}, // 100 134 | []uint8{127, 255, 159}, // 101 135 | []uint8{0, 204, 51}, // 102 136 | []uint8{102, 204, 127}, // 103 137 | []uint8{0, 153, 38}, // 104 138 | []uint8{76, 153, 95}, // 105 139 | []uint8{0, 127, 31}, // 106 140 | []uint8{63, 127, 79}, // 107 141 | []uint8{0, 76, 19}, // 108 142 | []uint8{38, 76, 47}, // 109 143 | []uint8{0, 255, 127}, // 110 144 | []uint8{127, 255, 191}, // 111 145 | []uint8{0, 204, 102}, // 112 146 | []uint8{102, 204, 153}, // 113 147 | []uint8{0, 153, 76}, // 114 148 | []uint8{76, 153, 114}, // 115 149 | []uint8{0, 127, 63}, // 116 150 | []uint8{63, 127, 95}, // 117 151 | []uint8{0, 76, 38}, // 118 152 | []uint8{38, 76, 57}, // 119 153 | []uint8{0, 255, 191}, // 120 154 | []uint8{127, 255, 223}, // 121 155 | []uint8{0, 204, 153}, // 122 156 | []uint8{102, 204, 178}, // 123 157 | []uint8{0, 153, 114}, // 124 158 | []uint8{76, 153, 133}, // 125 159 | []uint8{0, 127, 95}, // 126 160 | []uint8{63, 127, 111}, // 127 161 | []uint8{0, 76, 57}, // 128 162 | []uint8{38, 76, 66}, // 129 163 | []uint8{0, 255, 255}, // 130 164 | []uint8{127, 255, 255}, // 131 165 | []uint8{0, 204, 204}, // 132 166 | []uint8{102, 204, 204}, // 133 167 | []uint8{0, 153, 153}, // 134 168 | []uint8{76, 153, 153}, // 135 169 | []uint8{0, 127, 127}, // 136 170 | []uint8{63, 127, 127}, // 137 171 | []uint8{0, 76, 76}, // 138 172 | []uint8{38, 76, 76}, // 139 173 | []uint8{0, 191, 255}, // 140 174 | []uint8{127, 223, 255}, // 141 175 | []uint8{0, 153, 204}, // 142 176 | []uint8{102, 178, 204}, // 143 177 | []uint8{0, 114, 153}, // 144 178 | []uint8{76, 133, 153}, // 145 179 | []uint8{0, 95, 127}, // 146 180 | []uint8{63, 111, 127}, // 147 181 | []uint8{0, 57, 76}, // 148 182 | []uint8{38, 66, 76}, // 149 183 | []uint8{0, 127, 255}, // 150 184 | []uint8{127, 191, 255}, // 151 185 | []uint8{0, 102, 204}, // 152 186 | []uint8{102, 153, 204}, // 153 187 | []uint8{0, 76, 153}, // 154 188 | []uint8{76, 114, 153}, // 155 189 | []uint8{0, 63, 127}, // 156 190 | []uint8{63, 95, 127}, // 157 191 | []uint8{0, 38, 76}, // 158 192 | []uint8{38, 57, 76}, // 159 193 | []uint8{0, 63, 255}, // 160 194 | []uint8{127, 159, 255}, // 161 195 | []uint8{0, 51, 204}, // 162 196 | []uint8{102, 127, 204}, // 163 197 | []uint8{0, 38, 153}, // 164 198 | []uint8{76, 95, 153}, // 165 199 | []uint8{0, 31, 127}, // 166 200 | []uint8{63, 79, 127}, // 167 201 | []uint8{0, 19, 76}, // 168 202 | []uint8{38, 47, 76}, // 169 203 | []uint8{0, 0, 255}, // 170 204 | []uint8{127, 127, 255}, // 171 205 | []uint8{0, 0, 204}, // 172 206 | []uint8{102, 102, 204}, // 173 207 | []uint8{0, 0, 153}, // 174 208 | []uint8{76, 76, 153}, // 175 209 | []uint8{0, 0, 127}, // 176 210 | []uint8{63, 63, 127}, // 177 211 | []uint8{0, 0, 76}, // 178 212 | []uint8{38, 38, 76}, // 179 213 | []uint8{63, 0, 255}, // 180 214 | []uint8{159, 127, 255}, // 181 215 | []uint8{51, 0, 204}, // 182 216 | []uint8{127, 102, 204}, // 183 217 | []uint8{38, 0, 153}, // 184 218 | []uint8{95, 76, 153}, // 185 219 | []uint8{31, 0, 127}, // 186 220 | []uint8{79, 63, 127}, // 187 221 | []uint8{19, 0, 76}, // 188 222 | []uint8{47, 38, 76}, // 189 223 | []uint8{127, 0, 255}, // 190 224 | []uint8{191, 127, 255}, // 191 225 | []uint8{102, 0, 204}, // 192 226 | []uint8{153, 102, 204}, // 193 227 | []uint8{76, 0, 153}, // 194 228 | []uint8{114, 76, 153}, // 195 229 | []uint8{63, 0, 127}, // 196 230 | []uint8{95, 63, 127}, // 197 231 | []uint8{38, 0, 76}, // 198 232 | []uint8{57, 38, 76}, // 199 233 | []uint8{191, 0, 255}, // 200 234 | []uint8{223, 127, 255}, // 201 235 | []uint8{153, 0, 204}, // 202 236 | []uint8{178, 102, 204}, // 203 237 | []uint8{114, 0, 153}, // 204 238 | []uint8{133, 76, 153}, // 205 239 | []uint8{95, 0, 127}, // 206 240 | []uint8{111, 63, 127}, // 207 241 | []uint8{57, 0, 76}, // 208 242 | []uint8{66, 38, 76}, // 209 243 | []uint8{255, 0, 255}, // 210 244 | []uint8{255, 127, 255}, // 211 245 | []uint8{204, 0, 204}, // 212 246 | []uint8{204, 102, 204}, // 213 247 | []uint8{153, 0, 153}, // 214 248 | []uint8{153, 76, 153}, // 215 249 | []uint8{127, 0, 127}, // 216 250 | []uint8{127, 63, 127}, // 217 251 | []uint8{76, 0, 76}, // 218 252 | []uint8{76, 38, 76}, // 219 253 | []uint8{255, 0, 191}, // 220 254 | []uint8{255, 127, 223}, // 221 255 | []uint8{204, 0, 153}, // 222 256 | []uint8{204, 102, 178}, // 223 257 | []uint8{153, 0, 114}, // 224 258 | []uint8{153, 76, 133}, // 225 259 | []uint8{127, 0, 95}, // 226 260 | []uint8{127, 63, 111}, // 227 261 | []uint8{76, 0, 57}, // 228 262 | []uint8{76, 38, 66}, // 229 263 | []uint8{255, 0, 127}, // 230 264 | []uint8{255, 127, 191}, // 231 265 | []uint8{204, 0, 102}, // 232 266 | []uint8{204, 102, 153}, // 233 267 | []uint8{153, 0, 76}, // 234 268 | []uint8{153, 76, 114}, // 235 269 | []uint8{127, 0, 63}, // 236 270 | []uint8{127, 63, 95}, // 237 271 | []uint8{76, 0, 38}, // 238 272 | []uint8{76, 38, 57}, // 239 273 | []uint8{255, 0, 63}, // 240 274 | []uint8{255, 127, 159}, // 241 275 | []uint8{204, 0, 51}, // 242 276 | []uint8{204, 102, 127}, // 243 277 | []uint8{153, 0, 38}, // 244 278 | []uint8{153, 76, 95}, // 245 279 | []uint8{127, 0, 31}, // 246 280 | []uint8{127, 63, 79}, // 247 281 | []uint8{76, 0, 19}, // 248 282 | []uint8{76, 38, 47}, // 249 283 | []uint8{51, 51, 51}, // 250 284 | []uint8{91, 91, 91}, // 251 285 | []uint8{132, 132, 132}, // 252 286 | []uint8{173, 173, 173}, // 253 287 | []uint8{214, 214, 214}, // 254 288 | []uint8{255, 255, 255}, // 255 289 | } 290 | ) 291 | -------------------------------------------------------------------------------- /drawing/drawing.go: -------------------------------------------------------------------------------- 1 | // Package drawing defines Drawing struct for DXF. 2 | package drawing 3 | 4 | import ( 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "os" 10 | 11 | "github.com/yofu/dxf/block" 12 | "github.com/yofu/dxf/class" 13 | "github.com/yofu/dxf/color" 14 | "github.com/yofu/dxf/entity" 15 | "github.com/yofu/dxf/format" 16 | "github.com/yofu/dxf/handle" 17 | "github.com/yofu/dxf/header" 18 | "github.com/yofu/dxf/object" 19 | "github.com/yofu/dxf/table" 20 | ) 21 | 22 | // Drawing contains DXF drawing data. 23 | type Drawing struct { 24 | FileName string 25 | Layers map[string]*table.Layer 26 | Groups map[string]*object.Group 27 | Styles map[string]*table.Style 28 | CurrentLayer *table.Layer 29 | CurrentStyle *table.Style 30 | formatter format.Formatter 31 | Sections []Section 32 | dictionary *object.Dictionary 33 | groupdict *object.Dictionary 34 | PlotStyle handle.Handler 35 | // savebuff is used internally for the io.Reader options. 36 | savebuff *bytes.Buffer 37 | } 38 | 39 | // New creates a new Drawing. 40 | func New() *Drawing { 41 | d := new(Drawing) 42 | d.Layers = make(map[string]*table.Layer) 43 | d.Layers["0"] = table.LY_0 44 | d.Groups = make(map[string]*object.Group) 45 | d.CurrentLayer = d.Layers["0"] 46 | d.Styles = make(map[string]*table.Style) 47 | d.Styles["STANDARD"] = table.ST_STANDARD 48 | d.CurrentStyle = d.Styles["STANDARD"] 49 | d.formatter = format.NewASCII() 50 | d.formatter.SetPrecision(16) 51 | d.Sections = []Section{ 52 | header.New(), 53 | class.New(), 54 | table.New(), 55 | block.New(), 56 | entity.New(), 57 | object.New(), 58 | } 59 | d.dictionary = object.NewDictionary() 60 | d.addObject(d.dictionary) 61 | wd, ph := object.NewAcDbDictionaryWDFLT(d.dictionary) 62 | d.dictionary.AddItem("ACAD_PLOTSTYLENAME", wd) 63 | d.addObject(wd) 64 | d.addObject(ph) 65 | d.groupdict = object.NewDictionary() 66 | d.addObject(d.groupdict) 67 | d.dictionary.AddItem("ACAD_GROUP", d.groupdict) 68 | d.PlotStyle = ph 69 | d.Layers["0"].SetPlotStyle(d.PlotStyle) 70 | return d 71 | } 72 | 73 | func (d *Drawing) saveFile(filename string) error { 74 | w, err := os.Create(filename) 75 | if err != nil { 76 | return err 77 | } 78 | defer w.Close() 79 | _, err = d.WriteTo(w) 80 | return err 81 | } 82 | 83 | // Save saves the drawing file. 84 | // If it is the first time, use SaveAs(filename). 85 | func (d *Drawing) Save() error { 86 | if d.FileName == "" { 87 | return errors.New("filename is blank, use SaveAs(filename)") 88 | } 89 | return d.saveFile(d.FileName) 90 | } 91 | 92 | // SaveAs saves the drawing file as given filename. 93 | func (d *Drawing) SaveAs(filename string) error { 94 | d.FileName = filename 95 | return d.saveFile(filename) 96 | } 97 | 98 | // setHandle sets all the handles contained in Drawing. 99 | func (d *Drawing) setHandle() { 100 | h := 1 101 | for _, s := range d.Sections[1:] { 102 | s.SetHandle(&h) 103 | } 104 | d.Sections[0].SetHandle(&h) 105 | } 106 | 107 | func (d *Drawing) Header() *header.Header { 108 | return d.Sections[0].(*header.Header) 109 | } 110 | 111 | // Layer returns the named layer if exists. 112 | // If setcurrent is true, set current layer to it. 113 | func (d *Drawing) Layer(name string, setcurrent bool) (*table.Layer, error) { 114 | if l, exist := d.Layers[name]; exist { 115 | if setcurrent { 116 | d.CurrentLayer = l 117 | } 118 | return l, nil 119 | } 120 | return nil, fmt.Errorf("layer %s doesn't exist", name) 121 | } 122 | 123 | // AddLayer adds a new layer with given name, color and line type. 124 | // If setcurrent is true, set current layer to it. 125 | func (d *Drawing) AddLayer(name string, cl color.ColorNumber, lt *table.LineType, setcurrent bool) (*table.Layer, error) { 126 | if l, exist := d.Layers[name]; exist { 127 | if setcurrent { 128 | d.CurrentLayer = l 129 | } 130 | return l, fmt.Errorf("layer %s already exists", name) 131 | } 132 | l := table.NewLayer(name, cl, lt) 133 | l.SetPlotStyle(d.PlotStyle) 134 | d.Layers[name] = l 135 | d.Sections[2].(table.Tables).AddLayer(l) 136 | if setcurrent { 137 | d.CurrentLayer = l 138 | } 139 | return l, nil 140 | } 141 | 142 | // ChangeLayer changes current layer to the named layer. 143 | func (d *Drawing) ChangeLayer(name string) error { 144 | if l, exist := d.Layers[name]; exist { 145 | d.CurrentLayer = l 146 | return nil 147 | } 148 | return fmt.Errorf("layer %s doesn't exist", name) 149 | } 150 | 151 | // Style returns the named text style if exists. 152 | // If setcurrent is true, set current style to it. 153 | func (d *Drawing) Style(name string, setcurrent bool) (*table.Style, error) { 154 | if s, exist := d.Styles[name]; exist { 155 | if setcurrent { 156 | d.CurrentStyle = s 157 | } 158 | return s, nil 159 | } 160 | return nil, fmt.Errorf("style %s doesn't exist", name) 161 | } 162 | 163 | // AddStyle adds a new text style. 164 | // If setcurrent is true, set current style to it. 165 | func (d *Drawing) AddStyle(name string, fontname, bigfontname string, setcurrent bool) (*table.Style, error) { 166 | if s, exist := d.Styles[name]; exist { 167 | if setcurrent { 168 | d.CurrentStyle = s 169 | } 170 | return s, fmt.Errorf("style %s already exists", name) 171 | } 172 | s := table.NewStyle(name) 173 | s.FontName = fontname 174 | s.BigFontName = bigfontname 175 | d.Styles[name] = s 176 | d.Sections[TABLES].(table.Tables)[table.STYLE].Add(s) 177 | if setcurrent { 178 | d.CurrentStyle = s 179 | } 180 | return s, nil 181 | } 182 | 183 | // LineType returns the named line type if exists. 184 | func (d *Drawing) LineType(name string) (*table.LineType, error) { 185 | lt, err := d.Sections[TABLES].(table.Tables)[table.LTYPE].Contains(name) 186 | if err != nil { 187 | return nil, fmt.Errorf("linetype %s", err.Error()) 188 | } 189 | return lt.(*table.LineType), nil 190 | } 191 | 192 | // AddLineType adds a new linetype. 193 | func (d *Drawing) AddLineType(name string, desc string, ls ...float64) (*table.LineType, error) { 194 | lt, _ := d.Sections[TABLES].(table.Tables)[table.LTYPE].Contains(name) 195 | if lt != nil { 196 | return lt.(*table.LineType), fmt.Errorf("linetype %s already exists", name) 197 | } 198 | newlt := table.NewLineType(name, desc, ls...) 199 | d.Sections[TABLES].(table.Tables)[table.LTYPE].Add(newlt) 200 | return newlt, nil 201 | } 202 | 203 | // Entities returns slice of all entities contained in Drawing. 204 | func (d *Drawing) Entities() entity.Entities { 205 | return d.Sections[ENTITIES].(entity.Entities) 206 | } 207 | 208 | // AddEntity adds a new entity. 209 | func (d *Drawing) AddEntity(e entity.Entity) { 210 | d.Sections[4] = d.Sections[4].(entity.Entities).Add(e) 211 | } 212 | 213 | // Point creates a new POINT at (x, y, z). 214 | func (d *Drawing) Point(x, y, z float64) (*entity.Point, error) { 215 | p := entity.NewPoint() 216 | p.Coord = []float64{x, y, z} 217 | p.SetLayer(d.CurrentLayer) 218 | d.AddEntity(p) 219 | return p, nil 220 | } 221 | 222 | // Line creates a new LINE from (x1, y1, z1) to (x2, y2, z2). 223 | func (d *Drawing) Line(x1, y1, z1, x2, y2, z2 float64) (*entity.Line, error) { 224 | l := entity.NewLine() 225 | l.Start = []float64{x1, y1, z1} 226 | l.End = []float64{x2, y2, z2} 227 | l.SetLayer(d.CurrentLayer) 228 | d.AddEntity(l) 229 | return l, nil 230 | } 231 | 232 | // Circle creates a new CIRCLE at (x, y, z) with radius r. 233 | func (d *Drawing) Circle(x, y, z, r float64) (*entity.Circle, error) { 234 | c := entity.NewCircle() 235 | c.Center = []float64{x, y, z} 236 | c.Radius = r 237 | c.SetLayer(d.CurrentLayer) 238 | d.AddEntity(c) 239 | return c, nil 240 | } 241 | 242 | // Arc creates a new ARC at (x, y, z) with radius r from start to end. 243 | func (d *Drawing) Arc(x, y, z, r, start, end float64) (*entity.Arc, error) { 244 | c := entity.NewCircle() 245 | c.Center = []float64{x, y, z} 246 | c.Radius = r 247 | c.SetLayer(d.CurrentLayer) 248 | a := entity.NewArc(c) 249 | a.Angle[0] = start 250 | a.Angle[1] = end 251 | d.AddEntity(a) 252 | return a, nil 253 | } 254 | 255 | // Polyline creates a new POLYLINE with given vertices. 256 | func (d *Drawing) Polyline(closed bool, vertices ...[]float64) (*entity.Polyline, error) { 257 | p := entity.NewPolyline() 258 | p.SetLayer(d.CurrentLayer) 259 | for _, v := range vertices { 260 | p.AddVertex(v[0], v[1], v[2]) 261 | } 262 | if closed { 263 | p.Close() 264 | } 265 | d.AddEntity(p) 266 | return p, nil 267 | } 268 | 269 | // LwPolyline creates a new LWPOLYLINE with given vertices. 270 | func (d *Drawing) LwPolyline(closed bool, vertices ...[]float64) (*entity.LwPolyline, error) { 271 | size := len(vertices) 272 | l := entity.NewLwPolyline(size) 273 | for i := 0; i < size; i++ { 274 | l.Vertices[i] = vertices[i] 275 | } 276 | if closed { 277 | l.Close() 278 | } 279 | l.SetLayer(d.CurrentLayer) 280 | d.AddEntity(l) 281 | return l, nil 282 | } 283 | 284 | // ThreeDFace creates a new 3DFACE with given points. 285 | func (d *Drawing) ThreeDFace(points [][]float64) (*entity.ThreeDFace, error) { 286 | f := entity.New3DFace() 287 | if len(points) < 3 { 288 | return nil, errors.New("3DFace needs 3 or more points") 289 | } 290 | for i := 0; i < 3; i++ { 291 | f.Points[i] = points[i] 292 | } 293 | if len(points) >= 4 { 294 | f.Points[3] = points[3] 295 | } else { 296 | f.Points[3] = points[2] 297 | } 298 | f.SetLayer(d.CurrentLayer) 299 | d.AddEntity(f) 300 | return f, nil 301 | } 302 | 303 | // Text creates a new TEXT str at (x, y, z) with given height. 304 | func (d *Drawing) Text(str string, x, y, z, height float64) (*entity.Text, error) { 305 | t := entity.NewText() 306 | t.Coord1 = []float64{x, y, z} 307 | t.Height = height 308 | t.Value = str 309 | t.SetLayer(d.CurrentLayer) 310 | t.Style = d.CurrentStyle 311 | t.WidthFactor = t.Style.WidthFactor 312 | t.ObliqueAngle = t.Style.ObliqueAngle 313 | t.Style.LastHeightUsed = height 314 | d.AddEntity(t) 315 | return t, nil 316 | } 317 | 318 | func (d *Drawing) addObject(o object.Object) { 319 | d.Sections[5] = d.Sections[5].(object.Objects).Add(o) 320 | } 321 | 322 | // Group adds given entities to the named group. 323 | // If the named group doesn't exist, create it. 324 | func (d *Drawing) Group(name, desc string, es ...entity.Entity) (*object.Group, error) { 325 | if g, exist := d.Groups[name]; exist { 326 | g.AddEntity(es...) 327 | return g, fmt.Errorf("group %s already exists", name) 328 | } 329 | g := object.NewGroup(name, desc, es...) 330 | d.Groups[name] = g 331 | g.SetOwner(d.groupdict) 332 | d.addObject(g) 333 | return g, nil 334 | } 335 | 336 | // AddToGroup adds given entities to the named group. 337 | // If the named group doesn't exist, returns error. 338 | func (d *Drawing) AddToGroup(name string, es ...entity.Entity) error { 339 | if g, exist := d.Groups[name]; exist { 340 | g.AddEntity(es...) 341 | } 342 | return fmt.Errorf("group %s doesn't exist", name) 343 | } 344 | 345 | // WriteTo write the dxf file data to the given writer until 346 | // there is no more data to write or if an error occurs. The return 347 | // value n is the number of bytes writer. 348 | // This method full fills the io.WriterTo interface. 349 | func (d *Drawing) WriteTo(w io.Writer) (n int64, err error) { 350 | if d == nil { 351 | return 0, nil 352 | } 353 | d.setHandle() 354 | d.formatter.Reset() 355 | for _, s := range d.Sections { 356 | s.Format(d.formatter) 357 | } 358 | d.formatter.WriteString(0, "EOF") 359 | return d.formatter.WriteTo(w) 360 | } 361 | 362 | var _ io.WriterTo = &Drawing{} 363 | 364 | // Read implements the standard Read interface: it reads data from a buffer 365 | // containing the drawing, the buffer in initialized on the first read or 366 | // a read call after a close. If the drawing is nil, read returns 0 for n, 367 | // nil for the error. 368 | func (d *Drawing) Read(p []byte) (n int, err error) { 369 | if d == nil { 370 | return 0, nil 371 | } 372 | if d.savebuff == nil { 373 | // We need to initilize our buffer, and write the contents of the 374 | // drawing into it. 375 | d.savebuff = new(bytes.Buffer) 376 | _, err := d.WriteTo(d.savebuff) 377 | if err != nil { 378 | return 0, err 379 | } 380 | } 381 | return d.savebuff.Read(p) 382 | } 383 | 384 | // Close implements the standard Close interface: it close the buffer used by 385 | // the read command if one was initilized, and frees the memory held by it. Close 386 | // will always return nil for the error. 387 | func (d *Drawing) Close() error { 388 | if d != nil && d.savebuff != nil { 389 | d.savebuff = nil 390 | } 391 | return nil 392 | } 393 | 394 | var _ io.ReadCloser = &Drawing{} 395 | 396 | // SetExt sets the extents of the drawing based on the entities 397 | // in the drawing. If the drawing is nil, this function will panic. 398 | func (d *Drawing) SetExt() { 399 | mins := []float64{1e16, 1e16, 1e16} 400 | maxs := []float64{-1e16, -1e16, -1e16} 401 | for _, en := range d.Entities() { 402 | tmpmins, tmpmaxs := en.BBox() 403 | for i := 0; i < 3; i++ { 404 | if tmpmins[i] < mins[i] { 405 | mins[i] = tmpmins[i] 406 | } 407 | if tmpmaxs[i] > maxs[i] { 408 | maxs[i] = tmpmaxs[i] 409 | } 410 | } 411 | } 412 | h := d.Header() 413 | for i := 0; i < 3; i++ { 414 | h.ExtMin[i] = mins[i] 415 | h.ExtMax[i] = maxs[i] 416 | } 417 | } 418 | 419 | func (d *Drawing) Spline(p [][]float64) (*entity.Spline, error) { 420 | l := entity.NewSpline() 421 | l.SetLayer(d.CurrentLayer) 422 | l.Controls = p 423 | l.Knots = []float64{0, 0, 0, 0, 1, 1, 1, 1} 424 | d.AddEntity(l) 425 | return l, nil 426 | } 427 | -------------------------------------------------------------------------------- /testdata/torus.dxf: -------------------------------------------------------------------------------- 1 | 0 2 | SECTION 3 | 2 4 | HEADER 5 | 9 6 | $ACADVER 7 | 1 8 | AC1015 9 | 9 10 | $INSBASE 11 | 10 12 | 0.0000000000000000 13 | 20 14 | 0.0000000000000000 15 | 30 16 | 0.0000000000000000 17 | 9 18 | $INSUNITS 19 | 70 20 | 0 21 | 9 22 | $LUNITS 23 | 70 24 | 2 25 | 9 26 | $EXTMIN 27 | 10 28 | 0.0000000000000000 29 | 20 30 | 0.0000000000000000 31 | 30 32 | 0.0000000000000000 33 | 9 34 | $EXTMAX 35 | 10 36 | 0.0000000000000000 37 | 20 38 | 0.0000000000000000 39 | 30 40 | 0.0000000000000000 41 | 9 42 | $LTSCALE 43 | 40 44 | 100.0000000000000000 45 | 9 46 | $HANDSEED 47 | 5 48 | 43 49 | 0 50 | ENDSEC 51 | 0 52 | SECTION 53 | 2 54 | CLASSES 55 | 0 56 | ENDSEC 57 | 0 58 | SECTION 59 | 2 60 | TABLES 61 | 0 62 | TABLE 63 | 2 64 | VPORT 65 | 5 66 | 1 67 | 100 68 | AcDbSymbolTable 69 | 70 70 | 0 71 | 0 72 | ENDTAB 73 | 0 74 | TABLE 75 | 2 76 | LTYPE 77 | 5 78 | 2 79 | 100 80 | AcDbSymbolTable 81 | 70 82 | 5 83 | 0 84 | LTYPE 85 | 5 86 | 3 87 | 330 88 | 2 89 | 100 90 | AcDbSymbolTableRecord 91 | 100 92 | AcDbLinetypeTableRecord 93 | 2 94 | ByLayer 95 | 70 96 | 0 97 | 3 98 | 99 | 72 100 | 65 101 | 73 102 | 0 103 | 40 104 | 0.0000000000000000 105 | 0 106 | LTYPE 107 | 5 108 | 4 109 | 330 110 | 2 111 | 100 112 | AcDbSymbolTableRecord 113 | 100 114 | AcDbLinetypeTableRecord 115 | 2 116 | ByBlock 117 | 70 118 | 0 119 | 3 120 | 121 | 72 122 | 65 123 | 73 124 | 0 125 | 40 126 | 0.0000000000000000 127 | 0 128 | LTYPE 129 | 5 130 | 5 131 | 330 132 | 2 133 | 100 134 | AcDbSymbolTableRecord 135 | 100 136 | AcDbLinetypeTableRecord 137 | 2 138 | Continuous 139 | 70 140 | 0 141 | 3 142 | Solid Line 143 | 72 144 | 65 145 | 73 146 | 0 147 | 40 148 | 0.0000000000000000 149 | 0 150 | LTYPE 151 | 5 152 | 6 153 | 330 154 | 2 155 | 100 156 | AcDbSymbolTableRecord 157 | 100 158 | AcDbLinetypeTableRecord 159 | 2 160 | HIDDEN 161 | 70 162 | 0 163 | 3 164 | Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ _ 165 | 72 166 | 65 167 | 73 168 | 2 169 | 40 170 | 0.3750000000000000 171 | 49 172 | 0.2500000000000000 173 | 74 174 | 0 175 | 49 176 | -0.1250000000000000 177 | 74 178 | 0 179 | 0 180 | LTYPE 181 | 5 182 | 7 183 | 330 184 | 2 185 | 100 186 | AcDbSymbolTableRecord 187 | 100 188 | AcDbLinetypeTableRecord 189 | 2 190 | DASHDOT 191 | 70 192 | 0 193 | 3 194 | Dash dot __ . __ . __ . __ . __ . __ . __ . __ 195 | 72 196 | 65 197 | 73 198 | 4 199 | 40 200 | 1.0000000000000000 201 | 49 202 | 0.5000000000000000 203 | 74 204 | 0 205 | 49 206 | -0.2500000000000000 207 | 74 208 | 0 209 | 49 210 | 0.0000000000000000 211 | 74 212 | 0 213 | 49 214 | -0.2500000000000000 215 | 74 216 | 0 217 | 0 218 | ENDTAB 219 | 0 220 | TABLE 221 | 2 222 | LAYER 223 | 5 224 | 8 225 | 100 226 | AcDbSymbolTable 227 | 70 228 | 3 229 | 0 230 | LAYER 231 | 5 232 | 9 233 | 330 234 | 8 235 | 100 236 | AcDbSymbolTableRecord 237 | 100 238 | AcDbLayerTableRecord 239 | 2 240 | 0 241 | 70 242 | 0 243 | 62 244 | 7 245 | 6 246 | Continuous 247 | 370 248 | -3 249 | 390 250 | 41 251 | 0 252 | LAYER 253 | 5 254 | A 255 | 330 256 | 8 257 | 100 258 | AcDbSymbolTableRecord 259 | 100 260 | AcDbLayerTableRecord 261 | 2 262 | Toroidal 263 | 70 264 | 0 265 | 62 266 | 7 267 | 6 268 | Continuous 269 | 370 270 | -3 271 | 390 272 | 41 273 | 0 274 | LAYER 275 | 5 276 | B 277 | 330 278 | 8 279 | 100 280 | AcDbSymbolTableRecord 281 | 100 282 | AcDbLayerTableRecord 283 | 2 284 | Poloidal 285 | 70 286 | 0 287 | 62 288 | 1 289 | 6 290 | HIDDEN 291 | 370 292 | -3 293 | 390 294 | 41 295 | 0 296 | ENDTAB 297 | 0 298 | TABLE 299 | 2 300 | STYLE 301 | 5 302 | C 303 | 100 304 | AcDbSymbolTable 305 | 70 306 | 1 307 | 0 308 | STYLE 309 | 5 310 | D 311 | 330 312 | C 313 | 100 314 | AcDbSymbolTableRecord 315 | 100 316 | AcDbTextStyleTableRecord 317 | 2 318 | Standard 319 | 70 320 | 0 321 | 40 322 | 0.0000000000000000 323 | 41 324 | 1.0000000000000000 325 | 50 326 | 0.0000000000000000 327 | 71 328 | 0 329 | 42 330 | 100.0000000000000000 331 | 3 332 | arial.ttf 333 | 4 334 | 335 | 0 336 | ENDTAB 337 | 0 338 | TABLE 339 | 2 340 | VIEW 341 | 5 342 | E 343 | 100 344 | AcDbSymbolTable 345 | 70 346 | 0 347 | 0 348 | ENDTAB 349 | 0 350 | TABLE 351 | 2 352 | UCS 353 | 5 354 | F 355 | 100 356 | AcDbSymbolTable 357 | 70 358 | 0 359 | 0 360 | ENDTAB 361 | 0 362 | TABLE 363 | 2 364 | APPID 365 | 5 366 | 10 367 | 100 368 | AcDbSymbolTable 369 | 70 370 | 1 371 | 0 372 | APPID 373 | 5 374 | 11 375 | 330 376 | 10 377 | 100 378 | AcDbSymbolTableRecord 379 | 100 380 | AcDbRegAppTableRecord 381 | 2 382 | ACAD 383 | 70 384 | 0 385 | 0 386 | ENDTAB 387 | 0 388 | TABLE 389 | 2 390 | DIMSTYLE 391 | 5 392 | 12 393 | 100 394 | AcDbSymbolTable 395 | 70 396 | 0 397 | 100 398 | AcDbDimStyleTable 399 | 71 400 | 0 401 | 0 402 | ENDTAB 403 | 0 404 | TABLE 405 | 2 406 | BLOCK_RECORD 407 | 5 408 | 13 409 | 100 410 | AcDbSymbolTable 411 | 70 412 | 3 413 | 0 414 | BLOCK_RECORD 415 | 5 416 | 14 417 | 330 418 | 13 419 | 100 420 | AcDbSymbolTableRecord 421 | 100 422 | AcDbBlockTableRecord 423 | 2 424 | *Model_Space 425 | 70 426 | 0 427 | 280 428 | 1 429 | 281 430 | 0 431 | 0 432 | BLOCK_RECORD 433 | 5 434 | 15 435 | 330 436 | 13 437 | 100 438 | AcDbSymbolTableRecord 439 | 100 440 | AcDbBlockTableRecord 441 | 2 442 | *Paper_Space 443 | 70 444 | 0 445 | 280 446 | 1 447 | 281 448 | 0 449 | 0 450 | BLOCK_RECORD 451 | 5 452 | 16 453 | 330 454 | 13 455 | 100 456 | AcDbSymbolTableRecord 457 | 100 458 | AcDbBlockTableRecord 459 | 2 460 | *Paper_Space0 461 | 70 462 | 0 463 | 280 464 | 1 465 | 281 466 | 0 467 | 0 468 | ENDTAB 469 | 0 470 | ENDSEC 471 | 0 472 | SECTION 473 | 2 474 | BLOCKS 475 | 0 476 | BLOCK 477 | 5 478 | 17 479 | 100 480 | AcDbEntity 481 | 8 482 | 0 483 | 100 484 | AcDbBlockBegin 485 | 2 486 | *Model_Space 487 | 70 488 | 0 489 | 10 490 | 0.0000000000000000 491 | 20 492 | 0.0000000000000000 493 | 30 494 | 0.0000000000000000 495 | 3 496 | *Model_Space 497 | 1 498 | 499 | 0 500 | ENDBLK 501 | 5 502 | 18 503 | 100 504 | AcDbEntity 505 | 8 506 | 0 507 | 100 508 | AcDbBlockEnd 509 | 0 510 | BLOCK 511 | 5 512 | 19 513 | 100 514 | AcDbEntity 515 | 8 516 | 0 517 | 100 518 | AcDbBlockBegin 519 | 2 520 | *Paper_Space 521 | 70 522 | 0 523 | 10 524 | 0.0000000000000000 525 | 20 526 | 0.0000000000000000 527 | 30 528 | 0.0000000000000000 529 | 3 530 | *Paper_Space 531 | 1 532 | 533 | 0 534 | ENDBLK 535 | 5 536 | 1A 537 | 100 538 | AcDbEntity 539 | 8 540 | 0 541 | 100 542 | AcDbBlockEnd 543 | 0 544 | BLOCK 545 | 5 546 | 1B 547 | 100 548 | AcDbEntity 549 | 8 550 | 0 551 | 100 552 | AcDbBlockBegin 553 | 2 554 | *Paper_Space0 555 | 70 556 | 0 557 | 10 558 | 0.0000000000000000 559 | 20 560 | 0.0000000000000000 561 | 30 562 | 0.0000000000000000 563 | 3 564 | *Paper_Space0 565 | 1 566 | 567 | 0 568 | ENDBLK 569 | 5 570 | 1C 571 | 100 572 | AcDbEntity 573 | 8 574 | 0 575 | 100 576 | AcDbBlockEnd 577 | 0 578 | ENDSEC 579 | 0 580 | SECTION 581 | 2 582 | ENTITIES 583 | 0 584 | CIRCLE 585 | 5 586 | 1D 587 | 100 588 | AcDbEntity 589 | 8 590 | Toroidal 591 | 100 592 | AcDbCircle 593 | 10 594 | 0.0000000000000000 595 | 20 596 | 0.0000000000000000 597 | 30 598 | 200.0000000000000000 599 | 40 600 | 500.0000000000000000 601 | 210 602 | 0.0000000000000000 603 | 220 604 | 0.0000000000000000 605 | 230 606 | 1.0000000000000000 607 | 0 608 | CIRCLE 609 | 5 610 | 1E 611 | 100 612 | AcDbEntity 613 | 8 614 | Poloidal 615 | 100 616 | AcDbCircle 617 | 10 618 | -500.0000000000000000 619 | 20 620 | 0.0000000000000000 621 | 30 622 | 0.0000000000000000 623 | 40 624 | 200.0000000000000000 625 | 210 626 | -0.0000000000000000 627 | 220 628 | 1.0000000000000000 629 | 230 630 | 0.0000000000000000 631 | 0 632 | CIRCLE 633 | 5 634 | 1F 635 | 100 636 | AcDbEntity 637 | 8 638 | Toroidal 639 | 100 640 | AcDbCircle 641 | 10 642 | 0.0000000000000000 643 | 20 644 | 0.0000000000000000 645 | 30 646 | 184.7759065022573566 647 | 40 648 | 423.4633135269820627 649 | 210 650 | 0.0000000000000000 651 | 220 652 | 0.0000000000000000 653 | 230 654 | 1.0000000000000000 655 | 0 656 | CIRCLE 657 | 5 658 | 20 659 | 100 660 | AcDbEntity 661 | 8 662 | Poloidal 663 | 100 664 | AcDbCircle 665 | 10 666 | -499.9999999999999432 667 | 20 668 | 0.0000000000000000 669 | 30 670 | 0.0000000000000000 671 | 40 672 | 200.0000000000000000 673 | 210 674 | -0.3826834323650898 675 | 220 676 | 0.9238795325112867 677 | 230 678 | 0.0000000000000000 679 | 0 680 | CIRCLE 681 | 5 682 | 21 683 | 100 684 | AcDbEntity 685 | 8 686 | Toroidal 687 | 100 688 | AcDbCircle 689 | 10 690 | 0.0000000000000000 691 | 20 692 | 0.0000000000000000 693 | 30 694 | 141.4213562373095101 695 | 40 696 | 358.5786437626904899 697 | 210 698 | 0.0000000000000000 699 | 220 700 | 0.0000000000000000 701 | 230 702 | 1.0000000000000000 703 | 0 704 | CIRCLE 705 | 5 706 | 22 707 | 100 708 | AcDbEntity 709 | 8 710 | Poloidal 711 | 100 712 | AcDbCircle 713 | 10 714 | -500.0000000000000000 715 | 20 716 | 0.0000000000000000 717 | 30 718 | 0.0000000000000000 719 | 40 720 | 200.0000000000000000 721 | 210 722 | -0.7071067811865475 723 | 220 724 | 0.7071067811865476 725 | 230 726 | 0.0000000000000000 727 | 0 728 | CIRCLE 729 | 5 730 | 23 731 | 100 732 | AcDbEntity 733 | 8 734 | Toroidal 735 | 100 736 | AcDbCircle 737 | 10 738 | 0.0000000000000000 739 | 20 740 | 0.0000000000000000 741 | 30 742 | 76.5366864730179657 743 | 40 744 | 315.2240934977426150 745 | 210 746 | 0.0000000000000000 747 | 220 748 | 0.0000000000000000 749 | 230 750 | 1.0000000000000000 751 | 0 752 | CIRCLE 753 | 5 754 | 24 755 | 100 756 | AcDbEntity 757 | 8 758 | Poloidal 759 | 100 760 | AcDbCircle 761 | 10 762 | -500.0000000000000000 763 | 20 764 | 0.0000000000000000 765 | 30 766 | -0.0000000000000284 767 | 40 768 | 200.0000000000000000 769 | 210 770 | -0.9238795325112867 771 | 220 772 | 0.3826834323650898 773 | 230 774 | 0.0000000000000000 775 | 0 776 | CIRCLE 777 | 5 778 | 25 779 | 100 780 | AcDbEntity 781 | 8 782 | Toroidal 783 | 100 784 | AcDbCircle 785 | 10 786 | 0.0000000000000000 787 | 20 788 | 0.0000000000000000 789 | 30 790 | 0.0000000000000122 791 | 40 792 | 300.0000000000000000 793 | 210 794 | 0.0000000000000000 795 | 220 796 | 0.0000000000000000 797 | 230 798 | 1.0000000000000000 799 | 0 800 | CIRCLE 801 | 5 802 | 26 803 | 100 804 | AcDbEntity 805 | 8 806 | Poloidal 807 | 100 808 | AcDbCircle 809 | 10 810 | -500.0000000000000000 811 | 20 812 | 0.0000000000000000 813 | 30 814 | 0.0000000000000000 815 | 40 816 | 200.0000000000000000 817 | 210 818 | -1.0000000000000000 819 | 220 820 | 0.0000000000000001 821 | 230 822 | 0.0000000000000000 823 | 0 824 | CIRCLE 825 | 5 826 | 27 827 | 100 828 | AcDbEntity 829 | 8 830 | Toroidal 831 | 100 832 | AcDbCircle 833 | 10 834 | 0.0000000000000000 835 | 20 836 | 0.0000000000000000 837 | 30 838 | -76.5366864730179515 839 | 40 840 | 315.2240934977426150 841 | 210 842 | 0.0000000000000000 843 | 220 844 | 0.0000000000000000 845 | 230 846 | 1.0000000000000000 847 | 0 848 | CIRCLE 849 | 5 850 | 28 851 | 100 852 | AcDbEntity 853 | 8 854 | Poloidal 855 | 100 856 | AcDbCircle 857 | 10 858 | -499.9999999999999432 859 | 20 860 | 0.0000000000000000 861 | 30 862 | 0.0000000000000000 863 | 40 864 | 200.0000000000000000 865 | 210 866 | -0.9238795325112867 867 | 220 868 | -0.3826834323650897 869 | 230 870 | 0.0000000000000000 871 | 0 872 | CIRCLE 873 | 5 874 | 29 875 | 100 876 | AcDbEntity 877 | 8 878 | Toroidal 879 | 100 880 | AcDbCircle 881 | 10 882 | 0.0000000000000000 883 | 20 884 | 0.0000000000000000 885 | 30 886 | -141.4213562373094817 887 | 40 888 | 358.5786437626904899 889 | 210 890 | 0.0000000000000000 891 | 220 892 | 0.0000000000000000 893 | 230 894 | 1.0000000000000000 895 | 0 896 | CIRCLE 897 | 5 898 | 2A 899 | 100 900 | AcDbEntity 901 | 8 902 | Poloidal 903 | 100 904 | AcDbCircle 905 | 10 906 | -500.0000000000000000 907 | 20 908 | 0.0000000000000000 909 | 30 910 | 0.0000000000000000 911 | 40 912 | 200.0000000000000000 913 | 210 914 | -0.7071067811865476 915 | 220 916 | -0.7071067811865475 917 | 230 918 | 0.0000000000000000 919 | 0 920 | CIRCLE 921 | 5 922 | 2B 923 | 100 924 | AcDbEntity 925 | 8 926 | Toroidal 927 | 100 928 | AcDbCircle 929 | 10 930 | 0.0000000000000000 931 | 20 932 | 0.0000000000000000 933 | 30 934 | -184.7759065022573566 935 | 40 936 | 423.4633135269820059 937 | 210 938 | 0.0000000000000000 939 | 220 940 | 0.0000000000000000 941 | 230 942 | 1.0000000000000000 943 | 0 944 | CIRCLE 945 | 5 946 | 2C 947 | 100 948 | AcDbEntity 949 | 8 950 | Poloidal 951 | 100 952 | AcDbCircle 953 | 10 954 | -500.0000000000000000 955 | 20 956 | 0.0000000000000000 957 | 30 958 | -0.0000000000000284 959 | 40 960 | 200.0000000000000000 961 | 210 962 | -0.3826834323650898 963 | 220 964 | -0.9238795325112867 965 | 230 966 | 0.0000000000000000 967 | 0 968 | CIRCLE 969 | 5 970 | 2D 971 | 100 972 | AcDbEntity 973 | 8 974 | Toroidal 975 | 100 976 | AcDbCircle 977 | 10 978 | 0.0000000000000000 979 | 20 980 | 0.0000000000000000 981 | 30 982 | -200.0000000000000000 983 | 40 984 | 500.0000000000000000 985 | 210 986 | 0.0000000000000000 987 | 220 988 | 0.0000000000000000 989 | 230 990 | 1.0000000000000000 991 | 0 992 | CIRCLE 993 | 5 994 | 2E 995 | 100 996 | AcDbEntity 997 | 8 998 | Poloidal 999 | 100 1000 | AcDbCircle 1001 | 10 1002 | -500.0000000000000000 1003 | 20 1004 | 0.0000000000000000 1005 | 30 1006 | 0.0000000000000000 1007 | 40 1008 | 200.0000000000000000 1009 | 210 1010 | -0.0000000000000001 1011 | 220 1012 | -1.0000000000000000 1013 | 230 1014 | 0.0000000000000000 1015 | 0 1016 | CIRCLE 1017 | 5 1018 | 2F 1019 | 100 1020 | AcDbEntity 1021 | 8 1022 | Toroidal 1023 | 100 1024 | AcDbCircle 1025 | 10 1026 | 0.0000000000000000 1027 | 20 1028 | 0.0000000000000000 1029 | 30 1030 | -184.7759065022573566 1031 | 40 1032 | 576.5366864730178804 1033 | 210 1034 | 0.0000000000000000 1035 | 220 1036 | 0.0000000000000000 1037 | 230 1038 | 1.0000000000000000 1039 | 0 1040 | CIRCLE 1041 | 5 1042 | 30 1043 | 100 1044 | AcDbEntity 1045 | 8 1046 | Poloidal 1047 | 100 1048 | AcDbCircle 1049 | 10 1050 | -500.0000000000000000 1051 | 20 1052 | 0.0000000000000000 1053 | 30 1054 | 0.0000000000000000 1055 | 40 1056 | 200.0000000000000000 1057 | 210 1058 | 0.3826834323650897 1059 | 220 1060 | -0.9238795325112868 1061 | 230 1062 | 0.0000000000000000 1063 | 0 1064 | CIRCLE 1065 | 5 1066 | 31 1067 | 100 1068 | AcDbEntity 1069 | 8 1070 | Toroidal 1071 | 100 1072 | AcDbCircle 1073 | 10 1074 | 0.0000000000000000 1075 | 20 1076 | 0.0000000000000000 1077 | 30 1078 | -141.4213562373095385 1079 | 40 1080 | 641.4213562373095101 1081 | 210 1082 | 0.0000000000000000 1083 | 220 1084 | 0.0000000000000000 1085 | 230 1086 | 1.0000000000000000 1087 | 0 1088 | CIRCLE 1089 | 5 1090 | 32 1091 | 100 1092 | AcDbEntity 1093 | 8 1094 | Poloidal 1095 | 100 1096 | AcDbCircle 1097 | 10 1098 | -500.0000000000000568 1099 | 20 1100 | 0.0000000000000000 1101 | 30 1102 | 0.0000000000000000 1103 | 40 1104 | 200.0000000000000000 1105 | 210 1106 | 0.7071067811865475 1107 | 220 1108 | -0.7071067811865477 1109 | 230 1110 | 0.0000000000000000 1111 | 0 1112 | CIRCLE 1113 | 5 1114 | 33 1115 | 100 1116 | AcDbEntity 1117 | 8 1118 | Toroidal 1119 | 100 1120 | AcDbCircle 1121 | 10 1122 | 0.0000000000000000 1123 | 20 1124 | 0.0000000000000000 1125 | 30 1126 | -76.5366864730180652 1127 | 40 1128 | 684.7759065022572713 1129 | 210 1130 | 0.0000000000000000 1131 | 220 1132 | 0.0000000000000000 1133 | 230 1134 | 1.0000000000000000 1135 | 0 1136 | CIRCLE 1137 | 5 1138 | 34 1139 | 100 1140 | AcDbEntity 1141 | 8 1142 | Poloidal 1143 | 100 1144 | AcDbCircle 1145 | 10 1146 | -500.0000000000000000 1147 | 20 1148 | 0.0000000000000000 1149 | 30 1150 | 0.0000000000000000 1151 | 40 1152 | 200.0000000000000000 1153 | 210 1154 | 0.9238795325112865 1155 | 220 1156 | -0.3826834323650903 1157 | 230 1158 | 0.0000000000000000 1159 | 0 1160 | CIRCLE 1161 | 5 1162 | 35 1163 | 100 1164 | AcDbEntity 1165 | 8 1166 | Toroidal 1167 | 100 1168 | AcDbCircle 1169 | 10 1170 | 0.0000000000000000 1171 | 20 1172 | 0.0000000000000000 1173 | 30 1174 | -0.0000000000000367 1175 | 40 1176 | 700.0000000000000000 1177 | 210 1178 | 0.0000000000000000 1179 | 220 1180 | 0.0000000000000000 1181 | 230 1182 | 1.0000000000000000 1183 | 0 1184 | CIRCLE 1185 | 5 1186 | 36 1187 | 100 1188 | AcDbEntity 1189 | 8 1190 | Poloidal 1191 | 100 1192 | AcDbCircle 1193 | 10 1194 | -500.0000000000000000 1195 | 20 1196 | 0.0000000000000000 1197 | 30 1198 | 0.0000000000000000 1199 | 40 1200 | 200.0000000000000000 1201 | 210 1202 | 1.0000000000000000 1203 | 220 1204 | -0.0000000000000002 1205 | 230 1206 | 0.0000000000000000 1207 | 0 1208 | CIRCLE 1209 | 5 1210 | 37 1211 | 100 1212 | AcDbEntity 1213 | 8 1214 | Toroidal 1215 | 100 1216 | AcDbCircle 1217 | 10 1218 | 0.0000000000000000 1219 | 20 1220 | 0.0000000000000000 1221 | 30 1222 | 76.5366864730179941 1223 | 40 1224 | 684.7759065022573850 1225 | 210 1226 | 0.0000000000000000 1227 | 220 1228 | 0.0000000000000000 1229 | 230 1230 | 1.0000000000000000 1231 | 0 1232 | CIRCLE 1233 | 5 1234 | 38 1235 | 100 1236 | AcDbEntity 1237 | 8 1238 | Poloidal 1239 | 100 1240 | AcDbCircle 1241 | 10 1242 | -500.0000000000000000 1243 | 20 1244 | 0.0000000000000000 1245 | 30 1246 | 0.0000000000000284 1247 | 40 1248 | 200.0000000000000000 1249 | 210 1250 | 0.9238795325112866 1251 | 220 1252 | 0.3826834323650900 1253 | 230 1254 | 0.0000000000000000 1255 | 0 1256 | CIRCLE 1257 | 5 1258 | 39 1259 | 100 1260 | AcDbEntity 1261 | 8 1262 | Toroidal 1263 | 100 1264 | AcDbCircle 1265 | 10 1266 | 0.0000000000000000 1267 | 20 1268 | 0.0000000000000000 1269 | 30 1270 | 141.4213562373095954 1271 | 40 1272 | 641.4213562373093964 1273 | 210 1274 | 0.0000000000000000 1275 | 220 1276 | 0.0000000000000000 1277 | 230 1278 | 1.0000000000000000 1279 | 0 1280 | CIRCLE 1281 | 5 1282 | 3A 1283 | 100 1284 | AcDbEntity 1285 | 8 1286 | Poloidal 1287 | 100 1288 | AcDbCircle 1289 | 10 1290 | -500.0000000000000000 1291 | 20 1292 | 0.0000000000000000 1293 | 30 1294 | 0.0000000000000284 1295 | 40 1296 | 200.0000000000000000 1297 | 210 1298 | 0.7071067811865470 1299 | 220 1300 | 0.7071067811865480 1301 | 230 1302 | 0.0000000000000000 1303 | 0 1304 | CIRCLE 1305 | 5 1306 | 3B 1307 | 100 1308 | AcDbEntity 1309 | 8 1310 | Toroidal 1311 | 100 1312 | AcDbCircle 1313 | 10 1314 | 0.0000000000000000 1315 | 20 1316 | 0.0000000000000000 1317 | 30 1318 | 184.7759065022574418 1319 | 40 1320 | 576.5366864730177667 1321 | 210 1322 | 0.0000000000000000 1323 | 220 1324 | 0.0000000000000000 1325 | 230 1326 | 1.0000000000000000 1327 | 0 1328 | CIRCLE 1329 | 5 1330 | 3C 1331 | 100 1332 | AcDbEntity 1333 | 8 1334 | Poloidal 1335 | 100 1336 | AcDbCircle 1337 | 10 1338 | -500.0000000000000000 1339 | 20 1340 | 0.0000000000000000 1341 | 30 1342 | -0.0000000000000284 1343 | 40 1344 | 200.0000000000000000 1345 | 210 1346 | 0.3826834323650887 1347 | 220 1348 | 0.9238795325112872 1349 | 230 1350 | 0.0000000000000000 1351 | 0 1352 | ENDSEC 1353 | 0 1354 | SECTION 1355 | 2 1356 | OBJECTS 1357 | 0 1358 | DICTIONARY 1359 | 5 1360 | 3D 1361 | 100 1362 | AcDbDictionary 1363 | 281 1364 | 1 1365 | 3 1366 | ACAD_GROUP 1367 | 350 1368 | 42 1369 | 3 1370 | ACAD_PLOTSTYLENAME 1371 | 350 1372 | 40 1373 | 0 1374 | ACDBDICTIONARYWDFLT 1375 | 5 1376 | 40 1377 | 102 1378 | {ACAD_REACTORS 1379 | 330 1380 | 3D 1381 | 102 1382 | } 1383 | 330 1384 | 3D 1385 | 100 1386 | AcDbDictionary 1387 | 281 1388 | 1 1389 | 3 1390 | Normal 1391 | 350 1392 | 41 1393 | 100 1394 | AcDbDictionaryWithDefault 1395 | 340 1396 | 41 1397 | 0 1398 | ACDBPLACEHOLDER 1399 | 5 1400 | 41 1401 | 102 1402 | {ACAD_REACTORS 1403 | 330 1404 | 40 1405 | 102 1406 | } 1407 | 330 1408 | 40 1409 | 0 1410 | DICTIONARY 1411 | 5 1412 | 42 1413 | 100 1414 | AcDbDictionary 1415 | 281 1416 | 1 1417 | 0 1418 | ENDSEC 1419 | 0 1420 | EOF 1421 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package dxf 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/yofu/dxf/block" 9 | "github.com/yofu/dxf/color" 10 | "github.com/yofu/dxf/drawing" 11 | "github.com/yofu/dxf/entity" 12 | "github.com/yofu/dxf/header" 13 | "github.com/yofu/dxf/insunit" 14 | "github.com/yofu/dxf/table" 15 | ) 16 | 17 | const entityTypeCode = "0" 18 | 19 | // setFloat sets a floating point number to a variable using given function. 20 | func setFloat(data [2]string, f func(float64)) error { 21 | val, err := strconv.ParseFloat(strings.TrimSpace(data[1]), 64) 22 | if err != nil { 23 | return fmt.Errorf("code %s: %w", data[0], err) 24 | } 25 | f(val) 26 | return nil 27 | } 28 | 29 | // setInt sets an integer value to a variable using given function. 30 | func setInt(data [2]string, f func(int)) error { 31 | val, err := strconv.ParseInt(strings.TrimSpace(data[1]), 10, 64) 32 | if err != nil { 33 | return fmt.Errorf("code %s: %w", data[0], err) 34 | } 35 | f(int(val)) 36 | return nil 37 | } 38 | 39 | // HEADER 40 | 41 | // ParseHeader parses HEADER section. 42 | func ParseHeader(d *drawing.Drawing, line int, data [][2]string) error { 43 | h := d.Sections[drawing.HEADER].(*header.Header) 44 | var name string 45 | var err error 46 | for _, dt := range data { 47 | switch dt[0] { 48 | case "9": 49 | name = dt[1] 50 | case "1": 51 | switch name { 52 | case "$ACADVER": 53 | h.Version = dt[1] 54 | } 55 | case "10": 56 | switch name { 57 | case "$INSBASE": 58 | err = setFloat(dt, func(val float64) { h.InsBase[0] = val }) 59 | case "$EXTMIN": 60 | err = setFloat(dt, func(val float64) { h.ExtMin[0] = val }) 61 | case "$EXTMAX": 62 | err = setFloat(dt, func(val float64) { h.ExtMax[0] = val }) 63 | } 64 | case "20": 65 | switch name { 66 | case "$INSBASE": 67 | err = setFloat(dt, func(val float64) { h.InsBase[1] = val }) 68 | case "$EXTMIN": 69 | err = setFloat(dt, func(val float64) { h.ExtMin[1] = val }) 70 | case "$EXTMAX": 71 | err = setFloat(dt, func(val float64) { h.ExtMax[1] = val }) 72 | } 73 | case "30": 74 | switch name { 75 | case "$INSBASE": 76 | err = setFloat(dt, func(val float64) { h.InsBase[2] = val }) 77 | case "$EXTMIN": 78 | err = setFloat(dt, func(val float64) { h.ExtMin[2] = val }) 79 | case "$EXTMAX": 80 | err = setFloat(dt, func(val float64) { h.ExtMax[2] = val }) 81 | } 82 | case "40": 83 | switch name { 84 | case "$LTSCALE": 85 | err = setFloat(dt, func(val float64) { h.LtScale = val }) 86 | } 87 | case "70": 88 | switch name { 89 | case "$INSUNITS": 90 | err = setInt(dt, func(val int) { h.InsUnit = insunit.Unit(val) }) 91 | case "$LUNITS": 92 | // Need to adjust the value back to the constants 93 | err = setInt(dt, func(val int) { h.InsLUnit = insunit.Type(val - 2) }) 94 | } 95 | } 96 | if err != nil { 97 | return err 98 | } 99 | } 100 | return nil 101 | } 102 | 103 | // CLASSES 104 | 105 | // ParseClasses parses CLASSES section. 106 | func ParseClasses(d *drawing.Drawing, line int, data [][2]string) error { 107 | return nil 108 | } 109 | 110 | // TABLES 111 | 112 | // ParseTables parses TABLES section. 113 | func ParseTables(d *drawing.Drawing, line int, data [][2]string) error { 114 | parsers := []func(*drawing.Drawing, [][2]string) (table.SymbolTable, error){ 115 | ParseViewport, 116 | ParseLtype, 117 | ParseLayer, 118 | ParseStyle, 119 | ParseView, 120 | ParseUCS, 121 | ParseAppID, 122 | ParseDimStyle, 123 | ParseBlockRecord, 124 | } 125 | tmpdata := make([][2]string, 0) 126 | setparser := false 127 | var parser func(*drawing.Drawing, [][2]string) (table.SymbolTable, error) 128 | var ind int 129 | for i, dt := range data { 130 | if setparser { 131 | if dt[0] != "2" { 132 | return fmt.Errorf("line %d: invalid group code: %s", line+2*i, dt[0]) 133 | } 134 | ind = int(table.TableTypeValue(strings.ToUpper(dt[1]))) 135 | if ind < 0 { 136 | return fmt.Errorf("line %d: unknown table type: %s", line+2*i+1, dt[1]) 137 | } 138 | parser = parsers[ind] 139 | setparser = false 140 | } else { 141 | if dt[0] == entityTypeCode { 142 | switch strings.ToUpper(dt[1]) { 143 | case "TABLE": 144 | setparser = true 145 | case "ENDTAB": 146 | if len(tmpdata) > 0 { 147 | err := ParseTable(d, tmpdata, ind, parser) 148 | if err != nil { 149 | return fmt.Errorf("line %d: %w", line+2*i-len(tmpdata)*2, err) 150 | } 151 | tmpdata = make([][2]string, 0) 152 | } 153 | default: 154 | tmpdata = append(tmpdata, dt) 155 | } 156 | } else { 157 | tmpdata = append(tmpdata, dt) 158 | } 159 | } 160 | } 161 | if len(tmpdata) > 0 { 162 | err := ParseTable(d, tmpdata, ind, parser) 163 | if err != nil { 164 | return fmt.Errorf("line %d: %w", line+2*len(data)-len(tmpdata)*2, err) 165 | } 166 | } 167 | return nil 168 | } 169 | 170 | // ParseTable parses each TABLE, which starts with "0\nTABLE\n" and ends with "0\nENDTAB\n". 171 | func ParseTable(d *drawing.Drawing, data [][2]string, index int, parser func(*drawing.Drawing, [][2]string) (table.SymbolTable, error)) error { 172 | t := d.Sections[drawing.TABLES].(table.Tables)[index] 173 | t.Clear() 174 | tmpdata := make([][2]string, 0) 175 | add := false // skip before first 0-code 176 | for _, dt := range data { 177 | switch dt[0] { 178 | case "0": 179 | if len(tmpdata) > 0 { 180 | st, err := parser(d, tmpdata) 181 | if err != nil { 182 | return err 183 | } 184 | t.Add(st) 185 | if layer, ok := st.(*table.Layer); ok { 186 | d.Layers[layer.Name()] = layer 187 | } 188 | tmpdata = make([][2]string, 0) 189 | } 190 | add = true 191 | default: 192 | if add { 193 | tmpdata = append(tmpdata, dt) 194 | } 195 | } 196 | } 197 | if len(tmpdata) > 0 { 198 | st, err := parser(d, tmpdata) 199 | if err != nil { 200 | return err 201 | } 202 | t.Add(st) 203 | if layer, ok := st.(*table.Layer); ok { 204 | d.Layers[layer.Name()] = layer 205 | } 206 | } 207 | return nil 208 | } 209 | 210 | // ParseViewport parses VPORT tables. 211 | func ParseViewport(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 212 | v := table.NewViewport("") 213 | var err error 214 | for _, dt := range data { 215 | switch dt[0] { 216 | case "2": 217 | v.SetName(dt[1]) 218 | case "10": 219 | err = setFloat(dt, func(val float64) { v.LowerLeft[0] = val }) 220 | case "20": 221 | err = setFloat(dt, func(val float64) { v.LowerLeft[1] = val }) 222 | case "11": 223 | err = setFloat(dt, func(val float64) { v.UpperRight[0] = val }) 224 | case "21": 225 | err = setFloat(dt, func(val float64) { v.UpperRight[1] = val }) 226 | case "12": 227 | err = setFloat(dt, func(val float64) { v.ViewCenter[0] = val }) 228 | case "22": 229 | err = setFloat(dt, func(val float64) { v.ViewCenter[1] = val }) 230 | case "13": 231 | err = setFloat(dt, func(val float64) { v.SnapBase[0] = val }) 232 | case "23": 233 | err = setFloat(dt, func(val float64) { v.SnapBase[1] = val }) 234 | case "14": 235 | err = setFloat(dt, func(val float64) { 236 | v.SnapSpacing[0] = val 237 | v.SnapSpacing[1] = val 238 | }) 239 | case "24": 240 | err = setFloat(dt, func(val float64) { v.SnapSpacing[1] = val }) 241 | case "15": 242 | err = setFloat(dt, func(val float64) { 243 | v.GridSpacing[0] = val 244 | v.GridSpacing[1] = val 245 | }) 246 | case "25": 247 | err = setFloat(dt, func(val float64) { v.GridSpacing[1] = val }) 248 | case "16": 249 | err = setFloat(dt, func(val float64) { v.ViewDirection[0] = val }) 250 | case "26": 251 | err = setFloat(dt, func(val float64) { v.ViewDirection[1] = val }) 252 | case "36": 253 | err = setFloat(dt, func(val float64) { v.ViewDirection[2] = val }) 254 | case "17": 255 | err = setFloat(dt, func(val float64) { v.ViewTarget[0] = val }) 256 | case "27": 257 | err = setFloat(dt, func(val float64) { v.ViewTarget[1] = val }) 258 | case "37": 259 | err = setFloat(dt, func(val float64) { v.ViewTarget[2] = val }) 260 | case "40": 261 | err = setFloat(dt, func(val float64) { v.Height = val }) 262 | case "41": 263 | err = setFloat(dt, func(val float64) { v.AspectRatio = val }) 264 | case "42": 265 | err = setFloat(dt, func(val float64) { v.LensLength = val }) 266 | case "43": 267 | err = setFloat(dt, func(val float64) { v.FrontClip = val }) 268 | case "44": 269 | err = setFloat(dt, func(val float64) { v.BackClip = val }) 270 | case "50": 271 | err = setFloat(dt, func(val float64) { v.SnapAngle = val }) 272 | case "51": 273 | err = setFloat(dt, func(val float64) { v.TwistAngle = val }) 274 | } 275 | if err != nil { 276 | return v, err 277 | } 278 | } 279 | return v, nil 280 | } 281 | 282 | // ParseLtype parses LTYPE tables. 283 | func ParseLtype(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 284 | var name, desc string 285 | var lengths []float64 286 | ind := 0 287 | for _, dt := range data { 288 | switch dt[0] { 289 | case "2": 290 | name = dt[1] 291 | case "3": 292 | desc = dt[1] 293 | case "73": 294 | l, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64) 295 | if err != nil { 296 | return nil, err 297 | } 298 | lengths = make([]float64, int(l)) 299 | case "49": 300 | if ind >= len(lengths) { 301 | return nil, fmt.Errorf("ltype too long") 302 | } 303 | val, err := strconv.ParseFloat(dt[1], 64) 304 | if err != nil { 305 | return nil, err 306 | } 307 | lengths[ind] = val 308 | ind++ 309 | } 310 | } 311 | return table.NewLineType(name, desc, lengths...), nil 312 | } 313 | 314 | // ParseLayer parses LAYER tables. 315 | func ParseLayer(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 316 | var name string 317 | var flag int 318 | var col color.ColorNumber 319 | var lt *table.LineType 320 | var lw int 321 | for _, dt := range data { 322 | switch dt[0] { 323 | case "2": 324 | name = dt[1] 325 | case "70": 326 | val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64) 327 | if err != nil { 328 | return nil, err 329 | } 330 | flag = int(val) 331 | case "62": 332 | val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64) 333 | if err != nil { 334 | return nil, err 335 | } 336 | col = color.ColorNumber(val) 337 | case "6": 338 | l, err := d.LineType(dt[1]) 339 | if err != nil { 340 | return nil, err 341 | } 342 | lt = l 343 | case "370": 344 | val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64) 345 | if err != nil { 346 | return nil, err 347 | } 348 | lw = int(val) 349 | case "390": 350 | // plotstyle 351 | } 352 | } 353 | l := table.NewLayer(name, col, lt) 354 | l.SetFlag(flag) 355 | l.SetLineWidth(lw) 356 | l.SetPlotStyle(d.PlotStyle) 357 | return l, nil 358 | } 359 | 360 | // ParseStyle parses STYLE tables. 361 | func ParseStyle(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 362 | var name, font, bigfont string 363 | for _, dt := range data { 364 | switch dt[0] { 365 | case "2": 366 | name = dt[1] 367 | case "3": 368 | font = dt[1] 369 | case "4": 370 | bigfont = dt[1] 371 | } 372 | } 373 | s := table.NewStyle(name) 374 | s.FontName = font 375 | s.BigFontName = bigfont 376 | return s, nil 377 | } 378 | 379 | // ParseView parses VIEW tables. 380 | func ParseView(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 381 | var name string 382 | for _, dt := range data { 383 | switch dt[0] { 384 | case "2": 385 | name = dt[1] 386 | } 387 | } 388 | v := table.NewView(name) 389 | return v, nil 390 | } 391 | 392 | // ParseUCS parses UCS tables. 393 | func ParseUCS(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 394 | var name string 395 | for _, dt := range data { 396 | switch dt[0] { 397 | case "2": 398 | name = dt[1] 399 | } 400 | } 401 | u := table.NewUCS(name) 402 | return u, nil 403 | } 404 | 405 | // ParseAppID parses APPID tables. 406 | func ParseAppID(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 407 | var name string 408 | for _, dt := range data { 409 | switch dt[0] { 410 | case "2": 411 | name = dt[1] 412 | } 413 | } 414 | a := table.NewAppID(name) 415 | return a, nil 416 | } 417 | 418 | // ParseDimStyle parses DIMSTYLE tables. 419 | func ParseDimStyle(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 420 | var name string 421 | for _, dt := range data { 422 | switch dt[0] { 423 | case "2": 424 | name = dt[1] 425 | } 426 | } 427 | ds := table.NewDimStyle(name) 428 | return ds, nil 429 | } 430 | 431 | // ParseBlockRecord parses BLOCK_RECORD tables. 432 | func ParseBlockRecord(d *drawing.Drawing, data [][2]string) (table.SymbolTable, error) { 433 | var name string 434 | for _, dt := range data { 435 | switch dt[0] { 436 | case "2": 437 | name = dt[1] 438 | } 439 | } 440 | b := table.NewBlockRecord(name) 441 | return b, nil 442 | } 443 | 444 | // BLOCKS 445 | 446 | // ParseBlocks parses BLOCKS section. 447 | func ParseBlocks(d *drawing.Drawing, line int, data [][2]string) error { 448 | tmpdata := make([][2]string, 0) 449 | add := true // skip ENDBLK 450 | for i, dt := range data { 451 | if dt[0] == entityTypeCode { 452 | switch strings.ToUpper(dt[1]) { 453 | case "BLOCK": 454 | add = true 455 | case "ENDBLK": 456 | if len(tmpdata) > 0 { 457 | err := ParseBlock(d, tmpdata) 458 | if err != nil { 459 | return fmt.Errorf("line %d: %w", line+2*i-len(tmpdata)*2, err) 460 | } 461 | tmpdata = make([][2]string, 0) 462 | } 463 | add = false 464 | default: 465 | if add { 466 | tmpdata = append(tmpdata, dt) 467 | } 468 | } 469 | } else { 470 | if add { 471 | tmpdata = append(tmpdata, dt) 472 | } 473 | } 474 | } 475 | if len(tmpdata) > 0 { 476 | err := ParseBlock(d, tmpdata) 477 | if err != nil { 478 | return fmt.Errorf("line %d: %w", line+2*len(data)-len(tmpdata)*2, err) 479 | } 480 | } 481 | return nil 482 | } 483 | 484 | // ParseBlock parses each BLOCK, which starts with "0\nBLOCK\n" and ends with "0\nENDBLK\n". 485 | func ParseBlock(d *drawing.Drawing, data [][2]string) error { 486 | b := block.NewBlock("", "") 487 | var err error 488 | for _, dt := range data { 489 | switch dt[0] { 490 | case "2": 491 | b.Name = dt[1] 492 | case "1": // 4? 493 | b.Description = dt[1] 494 | case "8": 495 | layer, err := d.Layer(dt[1], false) 496 | if err == nil { 497 | b.SetLayer(layer) 498 | } 499 | case "10": 500 | err = setFloat(dt, func(val float64) { b.Coord[0] = val }) 501 | case "20": 502 | err = setFloat(dt, func(val float64) { b.Coord[1] = val }) 503 | case "30": 504 | err = setFloat(dt, func(val float64) { b.Coord[2] = val }) 505 | case "70": 506 | val, err := strconv.ParseInt(strings.TrimSpace(dt[1]), 10, 64) 507 | if err != nil { 508 | return err 509 | } 510 | b.Flag = int(val) 511 | } 512 | if err != nil { 513 | return err 514 | } 515 | } 516 | return nil 517 | } 518 | 519 | // ENTITIES 520 | 521 | // ParseEntities parses ENTITIES section. 522 | func ParseEntities(d *drawing.Drawing, line int, data [][2]string) error { 523 | tmpdata := make([][2]string, 0) 524 | for i, dt := range data { 525 | if dt[0] == entityTypeCode { 526 | if len(tmpdata) > 0 { 527 | e, err := ParseEntity(d, tmpdata) 528 | if err != nil { 529 | return fmt.Errorf("line %d: %w", line+2*i-len(tmpdata)*2, err) 530 | } 531 | d.AddEntity(e) 532 | tmpdata = make([][2]string, 0) 533 | } 534 | } 535 | tmpdata = append(tmpdata, dt) 536 | } 537 | if len(tmpdata) > 0 { 538 | e, err := ParseEntity(d, tmpdata) 539 | if err != nil { 540 | return fmt.Errorf("line %d: %w", line+2*len(data)-len(tmpdata)*2, err) 541 | } 542 | d.AddEntity(e) 543 | } 544 | return nil 545 | } 546 | 547 | // ParseEntity parses each entity. 548 | func ParseEntity(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 549 | if len(data) < 1 { 550 | return nil, fmt.Errorf("no data") 551 | } 552 | if data[0][0] != entityTypeCode { 553 | return nil, fmt.Errorf("invalid group code: %q", data[0][0]) 554 | } 555 | f, err := ParseEntityFunc(data[0][1]) 556 | if err != nil { 557 | return nil, err 558 | } 559 | return f(d, data) 560 | } 561 | 562 | // ParseEntityFunc returns a function for parsing acoording to entity type string. 563 | func ParseEntityFunc(t string) (func(*drawing.Drawing, [][2]string) (entity.Entity, error), error) { 564 | switch t { 565 | case "LINE": 566 | return ParseLine, nil 567 | case "3DFACE": 568 | return Parse3DFace, nil 569 | case "LWPOLYLINE": 570 | return ParseLwPolyline, nil 571 | case "CIRCLE": 572 | return ParseCircle, nil 573 | case "ARC": 574 | return ParseArc, nil 575 | // case "POLYLINE": 576 | // return ParsePolyline, nil 577 | case "VERTEX": 578 | return ParseVertex, nil 579 | case "POINT": 580 | return ParsePoint, nil 581 | case "TEXT": 582 | return ParseText, nil 583 | case "SPLINE": 584 | return ParseSpline, nil 585 | default: 586 | return nil, fmt.Errorf("unknown entity type: %s", t) 587 | } 588 | } 589 | 590 | // ParseLine parses LINE entities. 591 | func ParseLine(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 592 | l := entity.NewLine() 593 | var err error 594 | for _, dt := range data { 595 | switch dt[0] { 596 | default: 597 | continue 598 | case "8": 599 | layer, err := d.Layer(dt[1], false) 600 | if err == nil { 601 | l.SetLayer(layer) 602 | } 603 | case "48": 604 | err = setFloat(dt, func(val float64) { l.SetLtscale(val) }) 605 | case "10": 606 | err = setFloat(dt, func(val float64) { l.Start[0] = val }) 607 | case "20": 608 | err = setFloat(dt, func(val float64) { l.Start[1] = val }) 609 | case "30": 610 | err = setFloat(dt, func(val float64) { l.Start[2] = val }) 611 | case "11": 612 | err = setFloat(dt, func(val float64) { l.End[0] = val }) 613 | case "21": 614 | err = setFloat(dt, func(val float64) { l.End[1] = val }) 615 | case "31": 616 | err = setFloat(dt, func(val float64) { l.End[2] = val }) 617 | } 618 | if err != nil { 619 | return l, err 620 | } 621 | } 622 | return l, nil 623 | } 624 | 625 | // Parse3DFace parses 3DFACE entities. 626 | func Parse3DFace(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 627 | t := entity.New3DFace() 628 | fourth := 0 629 | var err error 630 | for _, dt := range data { 631 | switch dt[0] { 632 | default: 633 | continue 634 | case "8": 635 | layer, err := d.Layer(dt[1], false) 636 | if err == nil { 637 | t.SetLayer(layer) 638 | } 639 | case "48": 640 | err = setFloat(dt, func(val float64) { t.SetLtscale(val) }) 641 | case "10": 642 | err = setFloat(dt, func(val float64) { t.Points[0][0] = val }) 643 | case "20": 644 | err = setFloat(dt, func(val float64) { t.Points[0][1] = val }) 645 | case "30": 646 | err = setFloat(dt, func(val float64) { t.Points[0][2] = val }) 647 | case "11": 648 | err = setFloat(dt, func(val float64) { t.Points[1][0] = val }) 649 | case "21": 650 | err = setFloat(dt, func(val float64) { t.Points[1][1] = val }) 651 | case "31": 652 | err = setFloat(dt, func(val float64) { t.Points[1][2] = val }) 653 | case "12": 654 | err = setFloat(dt, func(val float64) { t.Points[2][0] = val }) 655 | case "22": 656 | err = setFloat(dt, func(val float64) { t.Points[2][1] = val }) 657 | case "32": 658 | err = setFloat(dt, func(val float64) { t.Points[2][2] = val }) 659 | case "13": 660 | err = setFloat(dt, func(val float64) { 661 | t.Points[3][0] = val 662 | fourth |= 1 663 | }) 664 | case "23": 665 | err = setFloat(dt, func(val float64) { 666 | t.Points[3][1] = val 667 | fourth |= 2 668 | }) 669 | case "33": 670 | err = setFloat(dt, func(val float64) { 671 | t.Points[3][2] = val 672 | fourth |= 4 673 | }) 674 | case "70": 675 | err = setInt(dt, func(val int) { t.Flag = val }) 676 | } 677 | if err != nil { 678 | return t, err 679 | } 680 | } 681 | if fourth != 7 { 682 | for i := 0; i < 3; i++ { 683 | t.Points[3][i] = t.Points[2][i] 684 | } 685 | } 686 | return t, nil 687 | } 688 | 689 | // ParseLwPolyline parses LWPOLYLINE entities. 690 | func ParseLwPolyline(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 691 | lw := entity.NewLwPolyline(0) 692 | ind := 0 693 | read := 0 694 | var err error 695 | for _, dt := range data { 696 | switch dt[0] { 697 | default: 698 | continue 699 | case "8": 700 | layer, err := d.Layer(dt[1], false) 701 | if err == nil { 702 | lw.SetLayer(layer) 703 | } 704 | case "48": 705 | err = setFloat(dt, func(val float64) { lw.SetLtscale(val) }) 706 | case "90": 707 | err = setInt(dt, func(val int) { 708 | lw.Num = val 709 | lw.Vertices = make([][]float64, val) 710 | for i := 0; i < val; i++ { 711 | lw.Vertices[i] = make([]float64, 2) 712 | } 713 | lw.Bulges = make([]float64, val) 714 | }) 715 | case "10": 716 | if lw.Num > ind { 717 | err = setFloat(dt, func(val float64) { 718 | lw.Vertices[ind][0] = val 719 | read |= 1 720 | }) 721 | } else { 722 | err = fmt.Errorf("LWPOLYLINE extra vertices") 723 | } 724 | case "20": 725 | if lw.Num > ind { 726 | err = setFloat(dt, func(val float64) { 727 | lw.Vertices[ind][1] = val 728 | read |= 2 729 | }) 730 | } else { 731 | err = fmt.Errorf("LWPOLYLINE extra vertices") 732 | } 733 | case "42": 734 | if lw.Num > ind { 735 | err = setFloat(dt, func(val float64) { 736 | lw.Bulges[ind] = val 737 | }) 738 | } else { 739 | err = fmt.Errorf("LWPOLYLINE extra vertice buldge") 740 | } 741 | case "70": 742 | err = setInt(dt, func(val int) { 743 | if val == 1 { 744 | lw.Close() 745 | } 746 | }) 747 | } 748 | if err != nil { 749 | return lw, err 750 | } 751 | if read == 3 { 752 | read = 0 753 | ind++ 754 | } 755 | } 756 | if ind != lw.Num { 757 | return lw, fmt.Errorf("LWPOLYLINE not enough vertices") 758 | } 759 | return lw, nil 760 | } 761 | 762 | // ParseCircle parses CIRCLE entities. 763 | func ParseCircle(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 764 | c := entity.NewCircle() 765 | var err error 766 | for _, dt := range data { 767 | switch dt[0] { 768 | default: 769 | continue 770 | case "8": 771 | layer, err := d.Layer(dt[1], false) 772 | if err == nil { 773 | c.SetLayer(layer) 774 | } 775 | case "48": 776 | err = setFloat(dt, func(val float64) { c.SetLtscale(val) }) 777 | case "10": 778 | err = setFloat(dt, func(val float64) { c.Center[0] = val }) 779 | case "20": 780 | err = setFloat(dt, func(val float64) { c.Center[1] = val }) 781 | case "30": 782 | err = setFloat(dt, func(val float64) { c.Center[2] = val }) 783 | case "40": 784 | err = setFloat(dt, func(val float64) { c.Radius = val }) 785 | case "210": 786 | err = setFloat(dt, func(val float64) { c.Direction[0] = val }) 787 | case "220": 788 | err = setFloat(dt, func(val float64) { c.Direction[1] = val }) 789 | case "230": 790 | err = setFloat(dt, func(val float64) { c.Direction[2] = val }) 791 | } 792 | if err != nil { 793 | return c, err 794 | } 795 | } 796 | return c, nil 797 | } 798 | 799 | // ParseArc parses ARC entities. 800 | func ParseArc(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 801 | c := entity.NewCircle() 802 | angle := make([]float64, 2) 803 | var err error 804 | for _, dt := range data { 805 | switch dt[0] { 806 | default: 807 | continue 808 | case "8": 809 | layer, err := d.Layer(dt[1], false) 810 | if err == nil { 811 | c.SetLayer(layer) 812 | } 813 | case "48": 814 | err = setFloat(dt, func(val float64) { c.SetLtscale(val) }) 815 | case "10": 816 | err = setFloat(dt, func(val float64) { c.Center[0] = val }) 817 | case "20": 818 | err = setFloat(dt, func(val float64) { c.Center[1] = val }) 819 | case "30": 820 | err = setFloat(dt, func(val float64) { c.Center[2] = val }) 821 | case "40": 822 | err = setFloat(dt, func(val float64) { c.Radius = val }) 823 | case "50": 824 | err = setFloat(dt, func(val float64) { angle[0] = val }) 825 | case "51": 826 | err = setFloat(dt, func(val float64) { angle[1] = val }) 827 | case "210": 828 | err = setFloat(dt, func(val float64) { c.Direction[0] = val }) 829 | case "220": 830 | err = setFloat(dt, func(val float64) { c.Direction[1] = val }) 831 | case "230": 832 | err = setFloat(dt, func(val float64) { c.Direction[2] = val }) 833 | } 834 | if err != nil { 835 | a := entity.NewArc(c) 836 | for i := 0; i < 2; i++ { 837 | a.Angle[i] = angle[i] 838 | } 839 | return a, err 840 | } 841 | } 842 | a := entity.NewArc(c) 843 | for i := 0; i < 2; i++ { 844 | a.Angle[i] = angle[i] 845 | } 846 | return a, nil 847 | } 848 | 849 | func ParseVertex(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 850 | v := entity.NewDefaultVertex() 851 | var err error 852 | for _, dt := range data { 853 | switch dt[0] { 854 | default: 855 | continue 856 | case "8": 857 | layer, err := d.Layer(dt[1], false) 858 | if err == nil { 859 | v.SetLayer(layer) 860 | } 861 | case "48": 862 | err = setFloat(dt, func(val float64) { v.SetLtscale(val) }) 863 | case "10": 864 | err = setFloat(dt, func(val float64) { v.Coord[0] = val }) 865 | case "20": 866 | err = setFloat(dt, func(val float64) { v.Coord[1] = val }) 867 | case "30": 868 | err = setFloat(dt, func(val float64) { v.Coord[2] = val }) 869 | case "42": 870 | err = setFloat(dt, func(val float64) { v.Buldge = val }) 871 | } 872 | if err != nil { 873 | return v, err 874 | } 875 | } 876 | return v, nil 877 | } 878 | 879 | // ParsePoint parses POINT entities. 880 | func ParsePoint(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 881 | p := entity.NewPoint() 882 | var err error 883 | for _, dt := range data { 884 | switch dt[0] { 885 | default: 886 | continue 887 | case "8": 888 | layer, err := d.Layer(dt[1], false) 889 | if err == nil { 890 | p.SetLayer(layer) 891 | } 892 | case "48": 893 | err = setFloat(dt, func(val float64) { p.SetLtscale(val) }) 894 | case "10": 895 | err = setFloat(dt, func(val float64) { p.Coord[0] = val }) 896 | case "20": 897 | err = setFloat(dt, func(val float64) { p.Coord[1] = val }) 898 | case "30": 899 | err = setFloat(dt, func(val float64) { p.Coord[2] = val }) 900 | } 901 | if err != nil { 902 | return p, err 903 | } 904 | } 905 | return p, nil 906 | } 907 | 908 | // ParseText parses TEXT entities. 909 | func ParseText(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 910 | t := entity.NewText() 911 | var err error 912 | for _, dt := range data { 913 | switch dt[0] { 914 | default: 915 | continue 916 | case "8": 917 | layer, err := d.Layer(dt[1], false) 918 | if err == nil { 919 | t.SetLayer(layer) 920 | } 921 | case "48": 922 | err = setFloat(dt, func(val float64) { t.SetLtscale(val) }) 923 | case "10": 924 | err = setFloat(dt, func(val float64) { t.Coord1[0] = val }) 925 | case "20": 926 | err = setFloat(dt, func(val float64) { t.Coord1[1] = val }) 927 | case "30": 928 | err = setFloat(dt, func(val float64) { t.Coord1[2] = val }) 929 | case "11": 930 | err = setFloat(dt, func(val float64) { t.Coord2[0] = val }) 931 | case "21": 932 | err = setFloat(dt, func(val float64) { t.Coord2[1] = val }) 933 | case "31": 934 | err = setFloat(dt, func(val float64) { t.Coord2[2] = val }) 935 | case "40": 936 | err = setFloat(dt, func(val float64) { t.Height = val }) 937 | case "50": 938 | err = setFloat(dt, func(val float64) { t.Rotation = val }) 939 | case "1": 940 | t.Value = dt[1] 941 | case "7": 942 | if s, ok := d.Styles[dt[1]]; ok { 943 | t.Style = s 944 | } 945 | case "71": 946 | err = setInt(dt, func(val int) { t.GenFlag = val }) 947 | case "72": 948 | err = setInt(dt, func(val int) { t.HorizontalFlag = val }) 949 | case "73": 950 | err = setInt(dt, func(val int) { t.VerticalFlag = val }) 951 | } 952 | if err != nil { 953 | return t, err 954 | } 955 | } 956 | return t, nil 957 | } 958 | 959 | // OBJECTS 960 | 961 | // ParseObjects parses OBJECTS section. 962 | func ParseObjects(d *drawing.Drawing, line int, data [][2]string) error { 963 | return nil 964 | } 965 | 966 | // ParseSpline parses SPLINE entities. 967 | func ParseSpline(d *drawing.Drawing, data [][2]string) (entity.Entity, error) { 968 | s := entity.NewSpline() 969 | var err error 970 | var controlPoint []float64 971 | for _, dt := range data { 972 | switch dt[0] { 973 | default: 974 | continue 975 | case "8": 976 | layer, err := d.Layer(dt[1], false) 977 | if err == nil { 978 | s.SetLayer(layer) 979 | } 980 | case "210": 981 | err = setFloat(dt, func(val float64) { s.Normal[0] = val }) 982 | case "220": 983 | err = setFloat(dt, func(val float64) { s.Normal[1] = val }) 984 | case "230": 985 | err = setFloat(dt, func(val float64) { s.Normal[2] = val }) 986 | case "70": 987 | err = setInt(dt, func(val int) { s.Flag = val }) 988 | case "71": 989 | err = setInt(dt, func(val int) { s.Degree = val }) 990 | case "72": 991 | // Node values 992 | case "73": 993 | // Control point coordinates 994 | case "74": 995 | // Fitting point coordinates 996 | case "42": 997 | err = setFloat(dt, func(val float64) { s.Tolerance[0] = val }) 998 | case "43": 999 | err = setFloat(dt, func(val float64) { s.Tolerance[1] = val }) 1000 | case "44": 1001 | err = setFloat(dt, func(val float64) { s.Tolerance[2] = val }) 1002 | case "40": 1003 | err = setFloat(dt, func(val float64) { s.Knots = append(s.Knots, val) }) 1004 | case "10", "20", "30": 1005 | err = setFloat(dt, func(val float64) { 1006 | controlPoint = append(controlPoint, val) 1007 | if len(controlPoint) == 3 { 1008 | s.Controls = append(s.Controls, controlPoint) 1009 | controlPoint = nil 1010 | } 1011 | }) 1012 | case "11", "21", "31": 1013 | err = setFloat(dt, func(val float64) { 1014 | controlPoint = append(controlPoint, val) 1015 | if len(controlPoint) == 3 { 1016 | s.Fits = append(s.Fits, controlPoint) 1017 | controlPoint = nil 1018 | } 1019 | }) 1020 | } 1021 | if err != nil { 1022 | return s, err 1023 | } 1024 | } 1025 | return s, nil 1026 | } 1027 | --------------------------------------------------------------------------------