├── go.mod ├── use_int32.go ├── use_int64.go ├── README.md ├── .github └── workflows │ └── go.yml ├── use_xy.go ├── use_xyz.go ├── License.txt ├── go.sum ├── clipper_test.go └── clipper.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ctessum/go.clipper 2 | 3 | go 1.16 4 | 5 | require github.com/ctessum/geom v0.2.10 6 | -------------------------------------------------------------------------------- /use_int32.go: -------------------------------------------------------------------------------- 1 | // +build use_int32 2 | 3 | package clipper 4 | 5 | type CInt int32 6 | 7 | const loRange CInt = 46340 8 | const hiRange CInt = 46340 9 | -------------------------------------------------------------------------------- /use_int64.go: -------------------------------------------------------------------------------- 1 | // +build !use_int32 2 | 3 | package clipper 4 | 5 | type CInt int64 6 | 7 | const loRange CInt = 0x3FFFFFFF 8 | const hiRange CInt = 0x3FFFFFFFFFFFFFFF //L; // it won't compile with the L there 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go.clipper 2 | ========== 3 | 4 | BUILD 5 | ----- 6 | 7 | *int32 version* 8 | ``` 9 | $ go build -tags 'use_int32' 10 | ``` 11 | 12 | *int64 version* 13 | ``` 14 | $ go build -tags '!use_int32' 15 | # or combine build tags 16 | $ go build -tags '!use_int32,!use_xyz' 17 | ``` 18 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.15 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /use_xy.go: -------------------------------------------------------------------------------- 1 | // +build !use_xyz 2 | 3 | package clipper 4 | 5 | import "fmt" 6 | 7 | type IntPoint struct { 8 | X CInt 9 | Y CInt 10 | } 11 | 12 | func (p IntPoint) String() string { 13 | return fmt.Sprintf("{%v, %v}", p.X, p.Y) 14 | } 15 | 16 | func NewIntPoint(X, Y CInt) *IntPoint { 17 | ip := new(IntPoint) 18 | ip.X, ip.Y = X, Y 19 | return ip 20 | } 21 | func NewIntPointFromFloat(x, y float64) *IntPoint { 22 | ip := new(IntPoint) 23 | ip.X, ip.Y = CInt(x), CInt(y) 24 | return ip 25 | } 26 | 27 | func (ip *IntPoint) Copy() IntPoint { 28 | return IntPoint{ 29 | X: ip.X, 30 | Y: ip.Y, 31 | } 32 | } 33 | 34 | func (c *ClipperBase) ReverseHorizontal(e *TEdge) { 35 | //swap horizontal edges' top and bottom x's so they follow the natural 36 | //progression of the bounds - ie so their xbots will align with the 37 | //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] 38 | c.Swap(&e.Top.X, &e.Bot.X) 39 | } 40 | 41 | func (c *Clipper) SetZ(pt *IntPoint, e1, e2 *TEdge) { 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /use_xyz.go: -------------------------------------------------------------------------------- 1 | // +build use_xyz 2 | 3 | package clipper 4 | 5 | import "fmt" 6 | 7 | type IntPoint struct { 8 | X CInt 9 | Y CInt 10 | Z CInt 11 | } 12 | 13 | func (p *IntPoint) String() string { 14 | return fmt.Sprintf("{%v, %v}", p.X, p.Y) 15 | } 16 | 17 | func NewIntPoint(X, Y, Z CInt) *IntPoint { 18 | ip := new(IntPoint) 19 | ip.X, ip.Y, ip.Z = X, Y, Z 20 | return ip 21 | } 22 | func NewIntPointFromFloat(x, y, z float64) *IntPoint { 23 | ip := new(IntPoint) 24 | ip.X, ip.Y = CInt(x), CInt(y), CInt(z) 25 | return ip 26 | } 27 | 28 | func (ip *IntPoint) Copy() IntPoint { 29 | return IntPoint{ 30 | X: ip.X, 31 | Y: ip.Y, 32 | Z: ip.Z, 33 | } 34 | } 35 | 36 | func (c *ClipperBase) ReverseHorizontal(e *TEdge) { 37 | //swap horizontal edges' top and bottom x's so they follow the natural 38 | //progression of the bounds - ie so their xbots will align with the 39 | //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] 40 | Swap(&e.Top.X, &e.Bot.X) 41 | Swap(&e.Top.Z, &e.Bot.Z) 42 | } 43 | 44 | func (c *Clipper) SetZ(pt *IntPoint, e1, e2 *TEdge) { 45 | if pt.Z != 0 || ZFillFunction == nil { 46 | return 47 | } else if pt == e1.Bot { 48 | pt.Z = e1.Bot.Z 49 | } else if pt == e1.Top { 50 | pt.Z = e1.Top.Z 51 | } else if pt == e2.Bot { 52 | pt.Z = e2.Bot.Z 53 | } else if pt == e2.Top { 54 | pt.Z = e2.Top.Z 55 | } else { 56 | c.ZFillFunction(e1.Bot, e1.Top, e2.Bot, e2.Top, &pt) 57 | } 58 | } 59 | 60 | //------------------------------------------------------------------------------ 61 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | The Clipper Library (including Delphi, C++ & C# source code, other accompanying 2 | code, examples and documentation), hereafter called "the Software", has been 3 | released under the following license, terms and conditions: 4 | 5 | Boost Software License - Version 1.0 - August 17th, 2003 6 | http://www.boost.org/LICENSE_1_0.txt 7 | 8 | Permission is hereby granted, free of charge, to any person or organization 9 | obtaining a copy of the Software covered by this license to use, reproduce, 10 | display, distribute, execute, and transmit the Software, and to prepare 11 | derivative works of the Software, and to permit third-parties to whom the 12 | Software is furnished to do so, all subject to the following: 13 | 14 | The copyright notices in the Software and this entire statement, including the 15 | above license grant, this restriction and the following disclaimer, must be 16 | included in all copies of the Software, in whole or in part, and all derivative 17 | works of the Software, unless such copies or derivative works are solely in the 18 | form of machine-executable object code generated by a source language processor. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 23 | THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY 24 | DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 2 | github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= 3 | github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= 4 | github.com/ctessum/geom v0.2.10 h1:2jwVCZiwFkn3newoTPhMf/M1+8DEe0jKElUrZs9u4bw= 5 | github.com/ctessum/geom v0.2.10/go.mod h1:qRaD78k6ttfldYw+SkkdobyWPJDmgc3yYz3thx8WHP4= 6 | github.com/ctessum/polyclip-go v1.0.2-0.20200417141046-48e92ea36ddd h1:BVBbmu475OhEvEFXp17QEf0XHJhNHsfvGyNTjsshfOA= 7 | github.com/ctessum/polyclip-go v1.0.2-0.20200417141046-48e92ea36ddd/go.mod h1:e/Lh1JOGyynZwLr0M4tZGIyx07wXw9T+pu6hFut+kFQ= 8 | github.com/go-gl/gl v0.0.0-20180407155706-68e253793080/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= 9 | github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 10 | github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 11 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 12 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= 13 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 14 | github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 h1:EvokxLQsaaQjcWVWSV38221VAK7qc2zhaO17bKys/18= 15 | github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= 16 | github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M= 17 | github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= 18 | github.com/jonas-p/go-shp v0.1.2-0.20190401125246-9fd306ae10a6/go.mod h1:MRIhyxDQ6VVp0oYeD7yPGr5RSTNScUFKCDsI5DR7PtI= 19 | github.com/jung-kurt/gofpdf v1.0.0 h1:EroSdlP9BOoL5ssLYf3uLJXhCQMMM2fFxCJDKA3RhnA= 20 | github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= 21 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 22 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 23 | github.com/llgcode/draw2d v0.0.0-20180817132918-587a55234ca2 h1:3xDkT1Tbsw2yDtKWUrROAlr15+dzp76kwucDvAPPnQo= 24 | github.com/llgcode/draw2d v0.0.0-20180817132918-587a55234ca2/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA= 25 | github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb h1:61ndUreYSlWFeCY44JxDDkngVoI7/1MVhEl98Nm0KOk= 26 | github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY= 27 | github.com/paulmach/orb v0.1.6/go.mod h1:pPwxxs3zoAyosNSbNKn1jiXV2+oovRDObDKfTvRegDI= 28 | github.com/paulmach/osm v0.1.1/go.mod h1:/UEV7XqKKTG3/46W+MtSmIl81yjV7cGoLkpol3S094I= 29 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 30 | github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= 31 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 32 | golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 33 | golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 34 | golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 35 | golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk= 36 | golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 37 | golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 38 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0= 39 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 40 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 41 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 42 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 46 | golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 47 | golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 48 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 49 | golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 50 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 51 | gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= 52 | gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= 53 | gonum.org/v1/gonum v0.0.0-20190509213835-50179cd3f3f7 h1:pTkUVMunXAr9zqT3oQUWs515Lsp5YGZF335aC1HI3N0= 54 | gonum.org/v1/gonum v0.0.0-20190509213835-50179cd3f3f7/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= 55 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= 56 | gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= 57 | gonum.org/v1/plot v0.0.0-20181127114151-f41a315af148 h1:yYvSIczU/Bv0aQo2PoyVuJeUgucaxihBMa+YSBMNN9U= 58 | gonum.org/v1/plot v0.0.0-20181127114151-f41a315af148/go.mod h1:VIQWjXleEHakKVLjfhAAXUy3mq0NuXvobpOBf0ZBZro= 59 | modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= 60 | modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= 61 | modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= 62 | modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= 63 | modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= 64 | rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= 65 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 66 | -------------------------------------------------------------------------------- /clipper_test.go: -------------------------------------------------------------------------------- 1 | package clipper 2 | 3 | import ( 4 | "fmt" 5 | "image/color" 6 | "math" 7 | "math/rand" 8 | "os" 9 | "testing" 10 | "time" 11 | 12 | "github.com/ctessum/geom" 13 | "github.com/ctessum/geom/carto" 14 | ) 15 | 16 | func init() { 17 | rand.Seed(time.Now().UTC().UnixNano()) 18 | } 19 | 20 | func RandomPoly(maxWidth, maxHeight, vertCnt int) Path { 21 | result := Path(make([]*IntPoint, vertCnt)) 22 | for i := 0; i < vertCnt-1; i++ { 23 | result[i] = &IntPoint{CInt(rand.Intn(maxWidth)), CInt(rand.Intn(maxHeight))} 24 | } 25 | result[vertCnt-1] = result[0] 26 | return result 27 | } 28 | 29 | func runOps(t *testing.T, testName string, subj, clip Paths) { 30 | // fmt.Printf("--------------------%v-----------------\n", testName) 31 | //subjArea := AreaCombined(subj) 32 | //clipArea := AreaCombined(clip) 33 | 34 | c := NewClipper(IoNone) 35 | pft := PftEvenOdd 36 | 37 | clipTypes := map[string]ClipType{"intersection": CtIntersection, "union": CtUnion, "xor": CtXor} 38 | areas := make(map[string]float64) 39 | // Load the polygons into Clipper and execute the boolean clip op ... 40 | c.AddPaths(subj, PtSubject, true) 41 | c.AddPaths(clip, PtClip, true) 42 | // fmt.Println("Subj", subj) 43 | // fmt.Println("clip", clip) 44 | 45 | var subjGeom, clipGeom, solutionGeom geom.Polygon 46 | for clipType, ct := range clipTypes { 47 | // fmt.Println("Running " + clipType) 48 | solution, ok := c.Execute1(ct, pft, pft) 49 | if !ok { 50 | t.Fatal("Problem!") 51 | } 52 | // fmt.Println("Finished Running" + clipType) 53 | areas[clipType] = AreaCombined(solution) 54 | // fmt.Println("solution", solution) 55 | 56 | if clipType == "intersection" { 57 | subjGeom, clipGeom, solutionGeom = clipper2geom(subj), clipper2geom(clip), clipper2geom(solution) 58 | } 59 | } 60 | 61 | if different(areas["union"], areas["intersection"]+areas["xor"]) { 62 | t.Logf("writing file %v.png", testName) 63 | f, err := os.Create(fmt.Sprintf("%v.png", testName)) 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | carto.DrawShapes(f, 68 | []color.NRGBA{{0, 0, 0, 255}, {0, 0, 0, 255}, {0, 0, 0, 255}}, 69 | []color.NRGBA{{255, 0, 0, 127}, {0, 255, 0, 127}, 70 | {0, 0, 0, 200}}, 71 | 5, 0, subjGeom, clipGeom, solutionGeom) 72 | f.Close() 73 | t.Logf("%v\t%10.1f%10.1f\tFail", testName, areas["union"], areas["intersection"]+areas["xor"]) 74 | t.FailNow() 75 | } else { 76 | t.Logf("%v\t%10.1f%10.1f\tPass", testName, areas["union"], areas["intersection"]+areas["xor"]) 77 | } 78 | } 79 | 80 | func TestRandom(t *testing.T) { 81 | scale := int(1e0) 82 | 83 | for i := 0; i < 1000; i++ { 84 | // Generate random subject and clip polygons ... 85 | subj, clip := Paths(make([]Path, 0)), Paths(make([]Path, 0)) 86 | subj = append(subj, RandomPoly(640*scale, 480*scale, 4)) 87 | clip = append(clip, RandomPoly(640*scale, 480*scale, 4)) 88 | runOps(t, fmt.Sprintf("rnd%03d", i), subj, clip) 89 | } 90 | } 91 | 92 | func different(a, b float64) bool { 93 | if math.Abs(a-b)/b > 0.01 { 94 | return true 95 | } 96 | return false 97 | } 98 | 99 | func clipper2geom(data []Path) geom.Polygon { 100 | var out geom.Polygon 101 | var temp geom.Polygon = make([]geom.Path, len(data)) 102 | for i, r := range data { 103 | temp[i] = make([]geom.Point, len(r)) 104 | for j, p := range r { 105 | temp[i][j] = *geom.NewPoint(float64(p.X), float64(p.Y)) 106 | } 107 | } 108 | if len(temp) != 0 { 109 | out = temp 110 | } 111 | return out 112 | } 113 | 114 | type testCase struct { 115 | subj, clip Paths 116 | solutions []Paths 117 | } 118 | 119 | var testCases = []testCase{ 120 | testCase{ 121 | subj: Paths{{{343, 243}, {97, 337}, {413, 41}, {343, 243}}}, 122 | clip: Paths{{{319, 178}, {68, 48}, {183, 197}, {319, 178}}}, 123 | solutions: []Paths{ 124 | Paths{{{319, 178}, {258, 187}, {285, 161}}}, // intersection 125 | Paths{{{343, 243}, {97, 337}, {258, 187}, {183, 197}, {68, 48}, {285, 161}, {413, 41}}}, // union 126 | Paths{{{343, 243}, {97, 337}, {258, 187}, {319, 178}, {285, 161}, {413, 41}}, // xor 127 | {{285, 161}, {258, 187}, {183, 197}, {68, 48}}}, 128 | }, 129 | }, 130 | testCase{ 131 | subj: Paths{{{460, 233}, {98, 400}, {147, 32}, {460, 233}}}, 132 | clip: Paths{{{104, 33}, {494, 436}, {613, 347}, {104, 33}}}, 133 | solutions: []Paths{ 134 | Paths{{{442, 241}, {348, 285}, {142, 72}, {144, 57}}}, // intersection 135 | Paths{{{460, 233}, {442, 241}, {613, 347}, {494, 436}, {348, 285}, {98, 400}, {142, 72}, {104, 33}, {144, 57}, {147, 32}}}, // union 136 | Paths{{{460, 233}, {442, 241}, {613, 347}, {494, 436}, {348, 285}, {442, 241}, {144, 57}, {147, 32}}, // xor 137 | {{144, 57}, {142, 72}, {348, 285}, {98, 400}, {142, 72}, {104, 33}}}, 138 | }, 139 | }, 140 | testCase{ 141 | subj: Paths{{{615, 17}, {597, 282}, {232, 151}, {615, 17}}}, 142 | clip: Paths{{{392, 414}, {177, 230}, {67, 230}, {392, 414}}}, 143 | solutions: []Paths{ 144 | Paths{}, // intersection 145 | Paths{{{392, 414}, {67, 230}, {177, 230}}, // union 146 | {{597, 282}, {232, 151}, {615, 17}}}, 147 | Paths{{{392, 414}, {67, 230}, {177, 230}}, // xor 148 | {{597, 282}, {232, 151}, {615, 17}}}, 149 | }, 150 | }, 151 | } 152 | 153 | func TestExecute1(t *testing.T) { 154 | for i, v := range testCases { 155 | testName := fmt.Sprintf("fix%03d", i) 156 | // fmt.Printf("--------------------%v-----------------\n", testName) 157 | c := NewClipper(IoNone) 158 | pft := PftEvenOdd 159 | 160 | clipName := []string{"intersection", "union", "xor"} 161 | clipTypes := []ClipType{CtIntersection, CtUnion, CtXor} 162 | // Load the polygons into Clipper and execute the boolean clip op ... 163 | c.AddPaths(v.subj, PtSubject, true) 164 | c.AddPaths(v.clip, PtClip, true) 165 | // fmt.Println("Subj", v.subj) 166 | // fmt.Println("clip", v.clip) 167 | 168 | for j, ct := range clipTypes { 169 | // fmt.Println("Running " + clipName[j]) 170 | solution, ok := c.Execute1(ct, pft, pft) 171 | if !ok { 172 | t.Errorf("error running Execute1 %v: ok=false", clipName[j]) 173 | } 174 | if got, want := solution.String(), v.solutions[j].String(); got != want { 175 | t.Errorf("bad %v %v solution, got=%v, want=%v", testName, clipName[j], got, want) 176 | } 177 | } 178 | } 179 | } 180 | 181 | var testCasesPftNonZero = []testCase{ 182 | { 183 | subj: Paths{ 184 | { 185 | {X: 53000000000, Y: 180000000000}, 186 | {X: 68000000000, Y: 200000000000}, 187 | {X: 44000000000, Y: 199000000000}, 188 | }, 189 | { 190 | {X: 65000000000, Y: 160000000000}, 191 | {X: 58000000000, Y: 189000000000}, 192 | {X: 30000000000, Y: 190000000000}, 193 | }, 194 | { 195 | {X: 61000000000, Y: 189000000000}, 196 | {X: 52000000000, Y: 195000000000}, 197 | {X: 48000000000, Y: 187000000000}, 198 | }, 199 | }, 200 | clip: Paths{}, 201 | solutions: []Paths{ 202 | // union 203 | {{{58426086957, 187234782609}, {59586956522, 188782608696}, {X: 61000000000, Y: 189000000000}, {X: 60166666667, Y: 189555555556}, {X: 68000000000, Y: 200000000000}, {X: 44000000000, Y: 199000000000}, {X: 48577437859, Y: 189336520076}, {X: 30000000000, Y: 190000000000}, {X: 65000000000, Y: 160000000000}}}, 204 | }, 205 | }, 206 | { 207 | subj: Paths{ 208 | { 209 | {X: 10606601714, Y: 115606601718}, 210 | {X: 0, Y: 112071067812}, 211 | {X: 17071067809, Y: 95000000001}, 212 | }, 213 | { 214 | {X: 42022005724, Y: 111666666668}, 215 | {X: 21415404007, Y: 98131132763}, 216 | {X: 28486471819, Y: 91060064951}, 217 | }, 218 | { 219 | {X: 9999999996, Y: 102071067814}, 220 | {X: 17071067808, Y: 95000000002}, 221 | {X: 27071067807, Y: 95000000002}, 222 | }, 223 | 224 | { 225 | {X: 20606601713, Y: 105606601717}, 226 | {X: 7071067809, Y: 95000000000}, 227 | {X: 17071067808, Y: 95000000000}, 228 | }, 229 | { 230 | {X: 58083558727, Y: 111818317094}, 231 | {X: 36870355293, Y: 104747249284}, 232 | {X: 51012490915, Y: 90605113660}, 233 | }, 234 | { 235 | {X: 5606601714, Y: 110606601717}, 236 | {X: 2071067809, Y: 90000000000}, 237 | {X: 12071067808, Y: 90000000000}, 238 | }, 239 | }, 240 | clip: Paths{}, 241 | solutions: []Paths{ 242 | // union 243 | {{{28486471819, 91060064951}, {37236471817, 104381132760}, {51012490915, 90605113660}, {58083558727, 111818317094}, {37647005723, 105006132761}, {42022005724, 111666666668}, {21415404007, 98131132763}, {22761423751, 96785113019}, {18284271244, 98639610308}, {20606601713, 105606601717}, {15097873315, 101289898106}, {10606601714, 115606601718}, {0, 112071067812}, {4999999997, 107071067814}, {2071067809, 90000000000}, {12071067808, 90000000000}, {10502525313, 95000000000}, {17071067808, 95000000000}, {17071067809, 95000000002}, {24546536768, 95000000002}}, {{9825432008, 97158351806}, {7499999995, 104571067817}, {9999999997, 102071067813}, {12677669527, 99393398283}}}, 244 | }, 245 | }, 246 | } 247 | 248 | func TestExecute1_Union_PftNonZero(t *testing.T) { 249 | for i, v := range testCasesPftNonZero { 250 | testName := fmt.Sprintf("fix%03d", i) 251 | c := NewClipper(IoNone) 252 | pft := PftNonZero 253 | 254 | clipName := []string{"union"} 255 | clipTypes := []ClipType{CtUnion} 256 | c.AddPaths(v.subj, PtSubject, true) 257 | c.AddPaths(v.clip, PtClip, true) 258 | 259 | for j, ct := range clipTypes { 260 | solution, ok := c.Execute1(ct, pft, pft) 261 | if !ok { 262 | t.Errorf("error running Execute1 %v: ok=false", clipName[j]) 263 | } 264 | if got, want := solution.String(), v.solutions[j].String(); got != want { 265 | t.Errorf("bad %v %v solution, got=%v, want=%v", testName, clipName[j], got, want) 266 | } 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /clipper.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * * 3 | * Author : Angus Johnson * 4 | * Version : 6.1.5 * 5 | * Date : 28 March 2014 * 6 | * Website : http://www.angusj.com * 7 | * Copyright : Angus Johnson 2010-2014 * 8 | * * 9 | * License: * 10 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 11 | * http://www.boost.org/LICENSE_1_0.txt * 12 | * * 13 | * Attributions: * 14 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 15 | * "A generic solution to polygon clipping" * 16 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 17 | * http://portal.acm.org/citation.cfm?id=129906 * 18 | * * 19 | * Computer graphics and geometric modeling: implementation and algorithms * 20 | * By Max K. Agoston * 21 | * Springer; 1 edition (January 4, 2005) * 22 | * http://books.google.com/books?q=vatti+clipping+agoston * 23 | * * 24 | * See also: * 25 | * "Polygon Offsetting by Computing Winding Numbers" * 26 | * Paper no. DETC2005-85513 pp. 565-575 * 27 | * ASME 2005 International Design Engineering Technical Conferences * 28 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 29 | * September 24-28, 2005 , Long Beach, California, USA * 30 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 31 | * * 32 | *******************************************************************************/ 33 | 34 | /******************************************************************************* 35 | * * 36 | * This is a translation of the Delphi Clipper library and the naming style * 37 | * used has retained a Delphi flavour. * 38 | * * 39 | *******************************************************************************/ 40 | 41 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 42 | //improve performance but coordinate values are limited to the range +/- 46340 43 | 44 | package clipper 45 | 46 | import ( 47 | "fmt" 48 | "math" 49 | "math/big" 50 | "sort" 51 | "strings" 52 | ) 53 | 54 | type Path []*IntPoint 55 | type Paths []Path 56 | 57 | func NewPath() Path { 58 | return Path(make([]*IntPoint, 0)) 59 | } 60 | func NewPaths() Paths { 61 | return Paths(make([]Path, 0)) 62 | } 63 | 64 | func (p Path) String() string { 65 | v := make([]string, len(p)) 66 | for i, pp := range p { 67 | v[i] = pp.String() 68 | } 69 | return fmt.Sprintf("{%v}", strings.Join(v, ", ")) 70 | } 71 | 72 | func (p Paths) String() string { 73 | s := "{" 74 | for i, pp := range p { 75 | s += pp.String() 76 | if i != len(p)-1 { 77 | s += "\n" 78 | } 79 | } 80 | s += "}" 81 | return s 82 | } 83 | 84 | type DoublePoint struct { 85 | X float64 86 | Y float64 87 | } 88 | 89 | func NewDoublePoint(x, y float64) *DoublePoint { 90 | dp := new(DoublePoint) 91 | dp.X = x 92 | dp.Y = y 93 | return dp 94 | } 95 | 96 | func CopyDoublePoint(dp *DoublePoint) *DoublePoint { 97 | dp2 := new(DoublePoint) 98 | dp2.X, dp2.Y = dp.X, dp.Y 99 | return dp2 100 | } 101 | 102 | func (ip *IntPoint) ToDoublePoint() *DoublePoint { 103 | dp := new(DoublePoint) 104 | dp.X = float64(ip.X) 105 | dp.Y = float64(ip.Y) 106 | return dp 107 | } 108 | 109 | //------------------------------------------------------------------------------ 110 | // PolyTree & PolyNode classes 111 | //------------------------------------------------------------------------------ 112 | 113 | type PolyTree struct { 114 | PolyNode 115 | m_AllPolys []*PolyNode 116 | } 117 | 118 | func NewPolyTree() *PolyTree { 119 | pt := new(PolyTree) 120 | pt.m_AllPolys = make([]*PolyNode, 0) 121 | return pt 122 | } 123 | 124 | func (tree *PolyTree) toPolyNode() *PolyNode { 125 | node := new(PolyNode) 126 | node.m_Parent = tree.m_Parent 127 | node.m_polygon = tree.m_polygon 128 | node.m_Index = tree.m_Index 129 | node.m_jointype = tree.m_jointype 130 | node.m_endtype = tree.m_endtype 131 | node.m_Childs = tree.m_Childs 132 | node.IsOpen = tree.IsOpen 133 | return node 134 | } 135 | 136 | func (pt *PolyTree) Clear() { 137 | pt.m_AllPolys = make([]*PolyNode, 0) 138 | pt.m_Childs = make([]*PolyNode, 0) 139 | } 140 | 141 | func (pt *PolyTree) GetFirst() *PolyNode { 142 | if len(pt.m_Childs) > 0 { 143 | return pt.m_Childs[0] 144 | } else { 145 | return nil 146 | } 147 | } 148 | 149 | func (pt *PolyTree) Total() int { 150 | return len(pt.m_AllPolys) 151 | } 152 | 153 | type PolyNode struct { 154 | m_Parent *PolyNode 155 | m_polygon Path 156 | m_Index int 157 | m_jointype JoinType 158 | m_endtype EndType 159 | m_Childs []*PolyNode 160 | IsOpen bool 161 | } 162 | 163 | func NewPolyNode() *PolyNode { 164 | pn := new(PolyNode) 165 | pn.m_polygon = NewPath() 166 | pn.m_Childs = make([]*PolyNode, 0) 167 | return pn 168 | } 169 | 170 | func (pn *PolyNode) IsHoleNode() bool { 171 | result := true 172 | node := pn.m_Parent 173 | for node != nil { 174 | result = !result 175 | node = node.m_Parent 176 | } 177 | return result 178 | } 179 | 180 | func (pn *PolyNode) ChildCount() int { 181 | return len(pn.m_Childs) 182 | } 183 | 184 | func (pn *PolyNode) Contour() Path { 185 | return pn.m_polygon 186 | } 187 | 188 | func (pn *PolyNode) AddChild(Child *PolyNode) { 189 | cnt := pn.ChildCount() 190 | pn.m_Childs = append(pn.m_Childs, Child) 191 | Child.m_Parent = pn 192 | Child.m_Index = cnt 193 | } 194 | 195 | func (pn *PolyNode) GetNext() *PolyNode { 196 | if len(pn.m_Childs) > 0 { 197 | return pn.m_Childs[0] 198 | } else { 199 | return pn.GetNextSiblingUp() 200 | } 201 | } 202 | 203 | func (pn *PolyNode) GetNextSiblingUp() *PolyNode { 204 | if pn.m_Parent == nil { 205 | return nil 206 | } else if pn.m_Index == len(pn.m_Parent.m_Childs)-1 { 207 | return pn.m_Parent.GetNextSiblingUp() 208 | } else { 209 | return pn.m_Parent.m_Childs[pn.m_Index+1] 210 | } 211 | } 212 | 213 | func (pn *PolyNode) Childs() []*PolyNode { 214 | return pn.m_Childs 215 | } 216 | 217 | func (pn *PolyNode) Parent() *PolyNode { 218 | return pn.m_Parent 219 | } 220 | 221 | func (pn *PolyNode) IsHole() bool { 222 | return pn.IsHoleNode() 223 | } 224 | 225 | //------------------------------------------------------------------------------ 226 | 227 | func Int128Mul(lhs, rhs CInt) *big.Int { 228 | a := big.NewInt(int64(lhs)) 229 | b := big.NewInt(int64(rhs)) 230 | c := new(big.Int) 231 | return c.Mul(a, b) 232 | } 233 | 234 | //------------------------------------------------------------------------------ 235 | 236 | // The == operator, which 237 | // will also compare Z values if they exist, is usually used instead of this one. 238 | // This may be a problem when using Z values. 239 | func (a *IntPoint) Equals(b *IntPoint) bool { 240 | return a.X == b.X && a.Y == b.Y 241 | } 242 | 243 | func (a *IntPoint) NotEqual(b *IntPoint) bool { 244 | return a.X != b.X || a.Y != b.Y 245 | } 246 | 247 | type IntRect struct { 248 | left, top, right, bottom CInt 249 | } 250 | 251 | func NewIntRect(l, t, r, b CInt) *IntRect { 252 | this := new(IntRect) 253 | this.left = l 254 | this.top = t 255 | this.right = r 256 | this.bottom = b 257 | return this 258 | } 259 | 260 | func (ir *IntRect) Copy() IntRect { 261 | return IntRect{ 262 | left: ir.left, 263 | top: ir.top, 264 | right: ir.right, 265 | bottom: ir.bottom, 266 | } 267 | } 268 | 269 | type ClipType int 270 | 271 | const ( 272 | CtIntersection ClipType = iota 273 | CtUnion 274 | CtDifference 275 | CtXor 276 | ) 277 | 278 | type PolyType int 279 | 280 | const ( 281 | PtSubject PolyType = iota 282 | PtClip 283 | ) 284 | 285 | //By far the most widely used winding rules for polygon filling are 286 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 287 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 288 | //see http://glprogramming.com/red/chapter11.html 289 | type PolyFillType int 290 | 291 | const ( 292 | PftEvenOdd PolyFillType = iota 293 | PftNonZero 294 | PftPositive 295 | PftNegative 296 | ) 297 | 298 | type JoinType int 299 | 300 | const ( 301 | JtSquare JoinType = iota 302 | JtRound 303 | JtMiter 304 | ) 305 | 306 | type EndType int 307 | 308 | const ( 309 | EtClosedPolygon EndType = iota 310 | EtClosedLine 311 | EtOpenButt 312 | EtOpenSquare 313 | EtOpenRound 314 | ) 315 | 316 | type EdgeSide int 317 | 318 | const ( 319 | EsLeft EdgeSide = iota 320 | EsRight 321 | ) 322 | 323 | type Direction int 324 | 325 | const ( 326 | DRightToLeft Direction = iota 327 | DLeftToRight 328 | ) 329 | 330 | type TEdge struct { 331 | Bot, Curr, Top, Delta IntPoint 332 | Dx float64 333 | PolyTyp PolyType 334 | Side EdgeSide 335 | WindDelta int //1 or -1 depending on winding direction 336 | WindCnt int 337 | WindCnt2 int //winding count of the opposite polytype 338 | OutIdx int 339 | Next, Prev, NextInLML, NextInAEL *TEdge 340 | PrevInAEL, NextInSEL, PrevInSEL *TEdge 341 | } 342 | 343 | func (e *TEdge) String() string { 344 | return fmt.Sprintf("Bot: %v, Curr: %v, Top: %v, Delta: %v, Dx: %v", 345 | e.Bot, e.Curr, e.Top, e.Delta, e.Dx) 346 | } 347 | 348 | //func (e *TEdge) Copy() *TEdge { 349 | // o := new(TEdge) 350 | // o.Bot = e.Bot.Copy() 351 | // o.Curr = e.Curr.Copy() 352 | // o.Top = e.Top.Copy() 353 | // o.Delta = e.Delta.Copy() 354 | // o.Dx = e.Dx 355 | // o.PolyTyp = e.PolyTyp 356 | // o.Side = e.Side 357 | // o.WindDelta, o.WindCnt, o.WindCnt2, o.OutIdx = 358 | // e.WindDelta, e.WindCnt, e.WindCnt2, e.OutIdx 359 | // return o 360 | //} 361 | 362 | func (e *TEdge) printEdges() string { 363 | E := e 364 | s := "" 365 | for { 366 | s += fmt.Sprintf("%v\n", E) 367 | E = E.Next 368 | if E == nil || E == e { 369 | break 370 | } 371 | } 372 | return s 373 | } 374 | 375 | type IntersectNode struct { 376 | Edge1, Edge2 *TEdge 377 | Pt *IntPoint 378 | } 379 | 380 | func (in *IntersectNode) String() string { 381 | return fmt.Sprintf("Edge1: %v, Edge2: %v, Pt: %v", 382 | in.Edge1, in.Edge2, in.Pt) 383 | } 384 | 385 | type IntersectNodeList []*IntersectNode 386 | 387 | func (i IntersectNodeList) Len() int { return len(i) } 388 | func (i IntersectNodeList) Less(a, b int) bool { return i[a].Pt.Y > i[b].Pt.Y } 389 | func (i IntersectNodeList) Swap(a, b int) { i[a], i[b] = i[b], i[a] } 390 | 391 | // class MyIntersectNodeSort : IComparer 392 | // { 393 | // int Compare(IntersectNode node1, IntersectNode node2) 394 | // { 395 | // return (int)(node2.Pt.Y - node1.Pt.Y); 396 | // } 397 | // } 398 | 399 | type LocalMinima struct { 400 | Y CInt 401 | LeftBound, RightBound *TEdge 402 | Next *LocalMinima 403 | } 404 | 405 | func (lm *LocalMinima) String() string { 406 | return fmt.Sprintf("Y: %v, LeftBound: %v, RightBound: %v", 407 | lm.Y, lm.LeftBound, lm.RightBound) 408 | } 409 | 410 | func (lm *LocalMinima) printLML() string { 411 | s := "" 412 | LM := lm 413 | for { 414 | s += fmt.Sprint(LM) 415 | if LM.Next == nil { 416 | break 417 | } 418 | LM = LM.Next 419 | s += "\n" 420 | } 421 | return s 422 | } 423 | 424 | type Scanbeam struct { 425 | Y CInt 426 | Next *Scanbeam 427 | } 428 | 429 | func (s *Scanbeam) String() string { 430 | return fmt.Sprintf("Y: %v", s.Y) 431 | } 432 | 433 | func (s *Scanbeam) printScanbeams() string { 434 | str := "" 435 | s2 := s 436 | for s2 != nil { 437 | str += " " + fmt.Sprint(s2) 438 | s2 = s2.Next 439 | } 440 | return str 441 | } 442 | 443 | type OutRec struct { 444 | Idx int 445 | IsHole, IsOpen bool 446 | FirstLeft *OutRec //see comments in clipper.pas 447 | Pts *OutPt 448 | BottomPt *OutPt 449 | PolyNode *PolyNode 450 | } 451 | 452 | type OutPt struct { 453 | Idx int 454 | Pt *IntPoint 455 | Next, Prev *OutPt 456 | } 457 | 458 | func (o *OutPt) String() string { 459 | return fmt.Sprintf("Idx: %v, Pt: %v", 460 | o.Idx, o.Pt) 461 | } 462 | 463 | type Join struct { 464 | OutPt1, OutPt2 *OutPt 465 | OffPt *IntPoint 466 | } 467 | 468 | func (j *Join) String() string { 469 | return fmt.Sprintf("OutPt1: %v, OutPt2: %v, OffPt: %v", 470 | j.OutPt1, j.OutPt2, j.OffPt) 471 | } 472 | 473 | var horizontal = math.Inf(-1) 474 | 475 | const ( 476 | Skip int = -2 477 | Unassigned int = -1 478 | tolerance float64 = 1.0e-20 479 | ) 480 | 481 | type ClipperBase struct { 482 | m_MinimaList *LocalMinima 483 | m_CurrentLM *LocalMinima 484 | m_edges [][]*TEdge 485 | m_UseFullRange, m_HasOpenPaths bool 486 | PreserveCollinear bool 487 | } 488 | 489 | func NewClipperBase() *ClipperBase { 490 | c := new(ClipperBase) 491 | c.m_edges = make([][]*TEdge, 0) 492 | return c 493 | } 494 | 495 | func near_zero(val float64) bool { 496 | return (val > -tolerance) && (val < tolerance) 497 | } 498 | 499 | func (c *ClipperBase) Swap(val1, val2 *CInt) { 500 | *val1, *val2 = *val2, *val1 501 | } 502 | 503 | //------------------------------------------------------------------------------ 504 | 505 | func (c *ClipperBase) IsHorizontal(e *TEdge) bool { 506 | return e.Delta.Y == 0 507 | } 508 | 509 | //------------------------------------------------------------------------------ 510 | 511 | func (c *ClipperBase) PointIsVertex(pt *IntPoint, pp *OutPt) bool { 512 | pp2 := pp 513 | for { 514 | if *pp2.Pt == *pt { 515 | return true 516 | } 517 | pp2 = pp2.Next 518 | if pp2 == pp { 519 | break 520 | } 521 | } 522 | return false 523 | } 524 | 525 | //------------------------------------------------------------------------------ 526 | 527 | func (c *ClipperBase) PointOnLineSegment(pt, 528 | linePt1, linePt2 *IntPoint, UseFullRange bool) bool { 529 | if UseFullRange { 530 | return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || 531 | ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || 532 | (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && 533 | ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && 534 | Int128Mul((pt.X-linePt1.X), (linePt2.Y-linePt1.Y)).Cmp( 535 | Int128Mul((linePt2.X-linePt1.X), (pt.Y-linePt1.Y))) == 0) 536 | } else { 537 | return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || 538 | ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || 539 | (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && 540 | ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && 541 | ((pt.X-linePt1.X)*(linePt2.Y-linePt1.Y) == 542 | (linePt2.X-linePt1.X)*(pt.Y-linePt1.Y))) 543 | } 544 | } 545 | 546 | //------------------------------------------------------------------------------ 547 | 548 | func (c *ClipperBase) PointOnPolygon(pt *IntPoint, pp *OutPt, UseFullRange bool) bool { 549 | pp2 := pp 550 | for { 551 | if c.PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange) { 552 | return true 553 | } 554 | pp2 = pp2.Next 555 | if pp2 == pp { 556 | break 557 | } 558 | } 559 | return false 560 | } 561 | 562 | //------------------------------------------------------------------------------ 563 | 564 | func (c *ClipperBase) SlopesEqual(e1, e2 *TEdge, UseFullRange bool) bool { 565 | if UseFullRange { 566 | return Int128Mul(e1.Delta.Y, e2.Delta.X).Cmp( 567 | Int128Mul(e1.Delta.X, e2.Delta.Y)) == 0 568 | } else { 569 | return (e1.Delta.Y)*(e2.Delta.X) == 570 | (e1.Delta.X)*(e2.Delta.Y) 571 | } 572 | } 573 | 574 | //------------------------------------------------------------------------------ 575 | 576 | func (c *ClipperBase) SlopesEqual3(pt1, pt2, 577 | pt3 *IntPoint, UseFullRange bool) bool { 578 | if UseFullRange { 579 | return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X).Cmp( 580 | Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y)) == 0 581 | } else { 582 | return (pt1.Y-pt2.Y)*(pt2.X-pt3.X)- 583 | (pt1.X-pt2.X)*(pt2.Y-pt3.Y) == 0 584 | } 585 | } 586 | 587 | //------------------------------------------------------------------------------ 588 | 589 | func (c *ClipperBase) SlopesEqual4(pt1, pt2, 590 | pt3, pt4 *IntPoint, UseFullRange bool) bool { 591 | if UseFullRange { 592 | return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X).Cmp( 593 | Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y)) == 0 594 | } else { 595 | return (pt1.Y-pt2.Y)*(pt3.X-pt4.X)- 596 | (pt1.X-pt2.X)*(pt3.Y-pt4.Y) == 0 597 | } 598 | } 599 | 600 | //------------------------------------------------------------------------------ 601 | 602 | func (c *ClipperBase) Clear() { 603 | c.m_edges = make([][]*TEdge, 0) 604 | c.m_UseFullRange = false 605 | c.m_HasOpenPaths = false 606 | } 607 | 608 | //------------------------------------------------------------------------------ 609 | 610 | func (c *ClipperBase) DisposeLocalMinimaList() { 611 | for c.m_MinimaList != nil { 612 | tmpLm := c.m_MinimaList.Next 613 | c.m_MinimaList = nil 614 | c.m_MinimaList = tmpLm 615 | } 616 | c.m_CurrentLM = nil 617 | } 618 | 619 | //------------------------------------------------------------------------------ 620 | 621 | func (c *ClipperBase) RangeTest(Pt *IntPoint, useFullRange *bool) { 622 | if *useFullRange { 623 | if Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange { 624 | panic(NewClipperException("Coordinate outside allowed range")) 625 | } 626 | } else if Pt.X > loRange || Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange { 627 | *useFullRange = true 628 | c.RangeTest(Pt, useFullRange) 629 | } 630 | } 631 | 632 | //------------------------------------------------------------------------------ 633 | 634 | func (c *ClipperBase) InitEdge(e, eNext, ePrev *TEdge, pt *IntPoint) { 635 | e.Next = eNext 636 | e.Prev = ePrev 637 | e.Curr = pt.Copy() 638 | e.OutIdx = Unassigned 639 | } 640 | 641 | //------------------------------------------------------------------------------ 642 | 643 | func (c *ClipperBase) InitEdge2(e *TEdge, polyType PolyType) { 644 | if e.Curr.Y >= e.Next.Curr.Y { 645 | e.Bot = e.Curr 646 | e.Top = e.Next.Curr 647 | } else { 648 | e.Top = e.Curr 649 | e.Bot = e.Next.Curr 650 | } 651 | c.SetDx(e) 652 | e.PolyTyp = polyType 653 | } 654 | 655 | //------------------------------------------------------------------------------ 656 | 657 | func (c *ClipperBase) FindNextLocMin(E *TEdge) *TEdge { 658 | var E2 *TEdge 659 | for { 660 | for E.Bot != E.Prev.Bot || E.Curr == E.Top { 661 | E = E.Next 662 | } 663 | if E.Dx != horizontal && E.Prev.Dx != horizontal { 664 | break 665 | } 666 | for E.Prev.Dx == horizontal { 667 | E = E.Prev 668 | } 669 | E2 = E 670 | for E.Dx == horizontal { 671 | E = E.Next 672 | } 673 | if E.Top.Y == E.Prev.Bot.Y { 674 | continue //ie just an intermediate horz. 675 | } 676 | if E2.Prev.Bot.X < E.Bot.X { 677 | E = E2 678 | } 679 | break 680 | } 681 | return E 682 | } 683 | 684 | //------------------------------------------------------------------------------ 685 | 686 | func (c *ClipperBase) ProcessBound(E *TEdge, IsClockwise bool) *TEdge { 687 | EStart := E 688 | Result := E 689 | var Horz *TEdge 690 | var StartX CInt 691 | if E.Dx == horizontal { 692 | //first we need to be careful here with open paths because this 693 | //may not be a true local minima (ie may be following a skip edge). 694 | //also, watch for adjacent horz edges to start heading left 695 | //before finishing right ... 696 | if IsClockwise { 697 | if E.Prev.Bot.Y == E.Bot.Y { 698 | StartX = E.Prev.Bot.X 699 | } else { 700 | StartX = E.Prev.Top.X 701 | } 702 | } else { 703 | if E.Next.Bot.Y == E.Bot.Y { 704 | StartX = E.Next.Bot.X 705 | } else { 706 | StartX = E.Next.Top.X 707 | } 708 | } 709 | if E.Bot.X != StartX { 710 | c.ReverseHorizontal(E) 711 | } 712 | } 713 | if Result.OutIdx != Skip { 714 | if IsClockwise { 715 | for Result.Top.Y == Result.Next.Bot.Y && Result.Next.OutIdx != Skip { 716 | Result = Result.Next 717 | } 718 | if Result.Dx == horizontal && Result.Next.OutIdx != Skip { 719 | //nb: at the top of a bound, horizontals are added to the bound 720 | //only when the preceding edge attaches to the horizontal's left vertex 721 | //unless a Skip edge is encountered when that becomes the top divide 722 | Horz = Result 723 | for Horz.Prev.Dx == horizontal { 724 | Horz = Horz.Prev 725 | } 726 | if Horz.Prev.Top.X == Result.Next.Top.X { 727 | if !IsClockwise { 728 | Result = Horz.Prev 729 | } 730 | } else if Horz.Prev.Top.X > Result.Next.Top.X { 731 | Result = Horz.Prev 732 | } 733 | } 734 | for E != Result { 735 | E.NextInLML = E.Next 736 | if E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X { 737 | c.ReverseHorizontal(E) 738 | } 739 | E = E.Next 740 | } 741 | if E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X { 742 | c.ReverseHorizontal(E) 743 | } 744 | Result = Result.Next //move to the edge just beyond current bound 745 | } else { 746 | for Result.Top.Y == Result.Prev.Bot.Y && Result.Prev.OutIdx != Skip { 747 | Result = Result.Prev 748 | } 749 | if Result.Dx == horizontal && Result.Prev.OutIdx != Skip { 750 | Horz = Result 751 | for Horz.Next.Dx == horizontal { 752 | Horz = Horz.Next 753 | } 754 | if Horz.Next.Top.X == Result.Prev.Top.X { 755 | if !IsClockwise { 756 | Result = Horz.Next 757 | } 758 | } else if Horz.Next.Top.X > Result.Prev.Top.X { 759 | Result = Horz.Next 760 | } 761 | } 762 | 763 | for E != Result { 764 | E.NextInLML = E.Prev 765 | if E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X { 766 | c.ReverseHorizontal(E) 767 | } 768 | E = E.Prev 769 | } 770 | if E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X { 771 | c.ReverseHorizontal(E) 772 | } 773 | Result = Result.Prev //move to the edge just beyond current bound 774 | } 775 | } 776 | 777 | if Result.OutIdx == Skip { 778 | //if edges still remain in the current bound beyond the skip edge then 779 | //create another LocMin and call ProcessBound once more 780 | E = Result 781 | if IsClockwise { 782 | for E.Top.Y == E.Next.Bot.Y { 783 | E = E.Next 784 | } 785 | //don't include top horizontals when parsing a bound a second time, 786 | //they will be contained in the opposite bound ... 787 | for E != Result && E.Dx == horizontal { 788 | E = E.Prev 789 | } 790 | } else { 791 | for E.Top.Y == E.Prev.Bot.Y { 792 | E = E.Prev 793 | } 794 | for E != Result && E.Dx == horizontal { 795 | E = E.Next 796 | } 797 | } 798 | if E == Result { 799 | if IsClockwise { 800 | Result = E.Next 801 | } else { 802 | Result = E.Prev 803 | } 804 | } else { 805 | //there are more edges in the bound beyond result starting with E 806 | if IsClockwise { 807 | E = Result.Next 808 | } else { 809 | E = Result.Prev 810 | } 811 | locMin := new(LocalMinima) 812 | locMin.Y = E.Bot.Y 813 | locMin.RightBound = E 814 | locMin.RightBound.WindDelta = 0 815 | Result = c.ProcessBound(locMin.RightBound, IsClockwise) 816 | c.InsertLocalMinima(locMin) 817 | } 818 | } 819 | return Result 820 | } 821 | 822 | //------------------------------------------------------------------------------ 823 | 824 | func (c *ClipperBase) AddPath(pg Path, polyType PolyType, Closed bool) bool { 825 | if !Closed && polyType == PtClip { 826 | panic(NewClipperException("AddPath: Open paths must be subject.")) 827 | } 828 | 829 | highI := len(pg) - 1 830 | if Closed { 831 | for highI > 0 && (pg[highI] == pg[0]) { 832 | highI-- 833 | } 834 | } 835 | for highI > 0 && (pg[highI] == pg[highI-1]) { 836 | highI-- 837 | } 838 | if (Closed && highI < 2) || (!Closed && highI < 1) { 839 | return false 840 | } 841 | 842 | //create a new edge array ... 843 | edges := make([]*TEdge, highI+1) 844 | for i := 0; i <= highI; i++ { 845 | edges[i] = new(TEdge) 846 | } 847 | 848 | IsFlat := true 849 | 850 | //1. Basic (first) edge initialization ... 851 | edges[1].Curr = pg[1].Copy() 852 | c.RangeTest(pg[0], &c.m_UseFullRange) 853 | c.RangeTest(pg[highI], &c.m_UseFullRange) 854 | c.InitEdge(edges[0], edges[1], edges[highI], pg[0]) 855 | c.InitEdge(edges[highI], edges[0], edges[highI-1], pg[highI]) 856 | for i := highI - 1; i >= 1; i-- { 857 | c.RangeTest(pg[i], &c.m_UseFullRange) 858 | c.InitEdge(edges[i], edges[i+1], edges[i-1], pg[i]) 859 | } 860 | eStart := edges[0] 861 | 862 | //2. Remove duplicate vertices, and (when closed) collinear edges ... 863 | E := eStart 864 | eLoopStop := eStart 865 | for { 866 | if E.Curr == E.Next.Curr { 867 | if E == E.Next { 868 | break 869 | } 870 | if E == eStart { 871 | eStart = E.Next 872 | } 873 | E = c.RemoveEdge(E) 874 | eLoopStop = E 875 | continue 876 | } 877 | if E.Prev == E.Next { 878 | break //only two vertices 879 | } else if Closed && 880 | c.SlopesEqual3(&E.Prev.Curr, &E.Curr, &E.Next.Curr, c.m_UseFullRange) && 881 | (!c.PreserveCollinear || 882 | !c.Pt2IsBetweenPt1AndPt3(&E.Prev.Curr, &E.Curr, &E.Next.Curr)) { 883 | //Collinear edges are allowed for open paths but in closed paths 884 | //the default is to merge adjacent collinear edges into a single edge. 885 | //However, if the PreserveCollinear property is enabled, only overlapping 886 | //collinear edges (ie spikes) will be removed from closed paths. 887 | if E == eStart { 888 | eStart = E.Next 889 | } 890 | E = c.RemoveEdge(E) 891 | E = E.Prev 892 | eLoopStop = E 893 | continue 894 | } 895 | E = E.Next 896 | if (E == eLoopStop) || (!Closed && E.Next == eStart) { 897 | break 898 | } 899 | } 900 | 901 | if (!Closed && (E == E.Next)) || (Closed && (E.Prev == E.Next)) { 902 | return false 903 | } 904 | 905 | if !Closed { 906 | c.m_HasOpenPaths = true 907 | eStart.Prev.OutIdx = Skip 908 | } 909 | 910 | //3. Do second stage of edge initialization ... 911 | E = eStart 912 | for { 913 | c.InitEdge2(E, polyType) 914 | E = E.Next 915 | if IsFlat && E.Curr.Y != eStart.Curr.Y { 916 | IsFlat = false 917 | } 918 | if E == eStart { 919 | break 920 | } 921 | } 922 | 923 | //4. Finally, add edge bounds to LocalMinima list ... 924 | 925 | //Totally flat paths must be handled differently when adding them 926 | //to LocalMinima list to avoid endless loops etc ... 927 | if IsFlat { 928 | if Closed { 929 | return false 930 | } 931 | E.Prev.OutIdx = Skip 932 | if E.Prev.Bot.X < E.Prev.Top.X { 933 | c.ReverseHorizontal(E.Prev) 934 | } 935 | locMin := new(LocalMinima) 936 | locMin.Next = nil 937 | locMin.Y = E.Bot.Y 938 | locMin.LeftBound = nil 939 | locMin.RightBound = E 940 | locMin.RightBound.Side = EsRight 941 | locMin.RightBound.WindDelta = 0 942 | for E.Next.OutIdx != Skip { 943 | E.NextInLML = E.Next 944 | if E.Bot.X != E.Prev.Top.X { 945 | c.ReverseHorizontal(E) 946 | } 947 | E = E.Next 948 | } 949 | c.InsertLocalMinima(locMin) 950 | c.m_edges = append(c.m_edges, edges) 951 | return true 952 | } 953 | 954 | c.m_edges = append(c.m_edges, edges) 955 | var clockwise bool 956 | var EMin *TEdge 957 | for { 958 | E = c.FindNextLocMin(E) 959 | if E == EMin { 960 | break 961 | } else if EMin == nil { 962 | EMin = E 963 | } 964 | 965 | //E and E.Prev now share a local minima (left aligned if horizontal). 966 | //Compare their slopes to find which starts which bound ... 967 | locMin := new(LocalMinima) 968 | locMin.Next = nil 969 | locMin.Y = E.Bot.Y 970 | if E.Dx < E.Prev.Dx { 971 | locMin.LeftBound = E.Prev 972 | locMin.RightBound = E 973 | clockwise = false //Q.nextInLML = Q.prev 974 | } else { 975 | locMin.LeftBound = E 976 | locMin.RightBound = E.Prev 977 | clockwise = true //Q.nextInLML = Q.next 978 | } 979 | locMin.LeftBound.Side = EsLeft 980 | locMin.RightBound.Side = EsRight 981 | 982 | if !Closed { 983 | locMin.LeftBound.WindDelta = 0 984 | } else if locMin.LeftBound.Next == locMin.RightBound { 985 | locMin.LeftBound.WindDelta = -1 986 | } else { 987 | locMin.LeftBound.WindDelta = 1 988 | } 989 | locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta 990 | 991 | E = c.ProcessBound(locMin.LeftBound, clockwise) 992 | E2 := c.ProcessBound(locMin.RightBound, !clockwise) 993 | 994 | if locMin.LeftBound.OutIdx == Skip { 995 | locMin.LeftBound = nil 996 | } else if locMin.RightBound.OutIdx == Skip { 997 | locMin.RightBound = nil 998 | } 999 | c.InsertLocalMinima(locMin) 1000 | if !clockwise { 1001 | E = E2 1002 | } 1003 | } 1004 | return true 1005 | } 1006 | 1007 | //------------------------------------------------------------------------------ 1008 | 1009 | func (c *ClipperBase) AddPaths(ppg Paths, polyType PolyType, closed bool) bool { 1010 | result := false 1011 | for i := 0; i < len(ppg); i++ { 1012 | if c.AddPath(ppg[i], polyType, closed) { 1013 | result = true 1014 | } 1015 | } 1016 | return result 1017 | } 1018 | 1019 | //------------------------------------------------------------------------------ 1020 | 1021 | func (c *ClipperBase) Pt2IsBetweenPt1AndPt3(pt1, pt2, pt3 *IntPoint) bool { 1022 | if (pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2) { 1023 | return false 1024 | } else if pt1.X != pt3.X { 1025 | return (pt2.X > pt1.X) == (pt2.X < pt3.X) 1026 | } else { 1027 | return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y) 1028 | } 1029 | } 1030 | 1031 | //------------------------------------------------------------------------------ 1032 | 1033 | func (c *ClipperBase) RemoveEdge(e *TEdge) *TEdge { 1034 | //removes e from float64_linked_list (but without removing from memory) 1035 | e.Prev.Next = e.Next 1036 | e.Next.Prev = e.Prev 1037 | result := e.Next 1038 | e.Prev = nil //flag as removed (see ClipperBase.Clear) 1039 | return result 1040 | } 1041 | 1042 | //------------------------------------------------------------------------------ 1043 | 1044 | func (c *ClipperBase) SetDx(e *TEdge) { 1045 | e.Delta = IntPoint{(e.Top.X - e.Bot.X), (e.Top.Y - e.Bot.Y)} 1046 | if e.Delta.Y == 0 { 1047 | e.Dx = horizontal 1048 | } else { 1049 | e.Dx = float64(e.Delta.X) / float64(e.Delta.Y) 1050 | } 1051 | } 1052 | 1053 | //--------------------------------------------------------------------------- 1054 | 1055 | func (c *ClipperBase) InsertLocalMinima(newLm *LocalMinima) { 1056 | if c.m_MinimaList == nil { 1057 | c.m_MinimaList = newLm 1058 | } else if newLm.Y >= c.m_MinimaList.Y { 1059 | newLm.Next = c.m_MinimaList 1060 | c.m_MinimaList = newLm 1061 | } else { 1062 | tmpLm := c.m_MinimaList 1063 | for tmpLm.Next != nil && (newLm.Y < tmpLm.Next.Y) { 1064 | tmpLm = tmpLm.Next 1065 | } 1066 | newLm.Next = tmpLm.Next 1067 | tmpLm.Next = newLm 1068 | } 1069 | } 1070 | 1071 | //------------------------------------------------------------------------------ 1072 | 1073 | func (c *ClipperBase) PopLocalMinima() { 1074 | if c.m_CurrentLM == nil { 1075 | return 1076 | } 1077 | c.m_CurrentLM = c.m_CurrentLM.Next 1078 | } 1079 | 1080 | //------------------------------------------------------------------------------ 1081 | 1082 | //------------------------------------------------------------------------------ 1083 | 1084 | func (c *ClipperBase) Reset() { 1085 | c.m_CurrentLM = c.m_MinimaList 1086 | if c.m_CurrentLM == nil { 1087 | return //ie nothing to process 1088 | } 1089 | 1090 | //reset all edges ... 1091 | lm := c.m_MinimaList 1092 | for lm != nil { 1093 | e := lm.LeftBound 1094 | if e != nil { 1095 | e.Curr = e.Bot 1096 | e.Side = EsLeft 1097 | e.OutIdx = Unassigned 1098 | } 1099 | e = lm.RightBound 1100 | if e != nil { 1101 | e.Curr = e.Bot 1102 | e.Side = EsRight 1103 | e.OutIdx = Unassigned 1104 | } 1105 | lm = lm.Next 1106 | } 1107 | } 1108 | 1109 | //------------------------------------------------------------------------------ 1110 | 1111 | func GetBounds(paths Paths) *IntRect { 1112 | i := 0 1113 | cnt := len(paths) 1114 | for i < cnt && len(paths[i]) == 0 { 1115 | i++ 1116 | } 1117 | if i == cnt { 1118 | return &IntRect{0, 0, 0, 0} 1119 | } 1120 | result := new(IntRect) 1121 | result.left = paths[i][0].X 1122 | result.right = result.left 1123 | result.top = paths[i][0].Y 1124 | result.bottom = result.top 1125 | for i < cnt { 1126 | for j := 0; j < len(paths[i]); j++ { 1127 | if paths[i][j].X < result.left { 1128 | result.left = paths[i][j].X 1129 | } else if paths[i][j].X > result.right { 1130 | result.right = paths[i][j].X 1131 | } 1132 | if paths[i][j].Y < result.top { 1133 | result.top = paths[i][j].Y 1134 | } else if paths[i][j].Y > result.bottom { 1135 | result.bottom = paths[i][j].Y 1136 | } 1137 | } 1138 | i++ 1139 | } 1140 | return result 1141 | } 1142 | 1143 | //InitOptions that can be passed to the constructor ... 1144 | type InitOptions int 1145 | 1146 | const ( 1147 | IoNone InitOptions = 0 1148 | IoReverseSolution InitOptions = 1 1149 | IoStrictlySimple InitOptions = 2 1150 | IoPreserveCollinear InitOptions = 4 1151 | ) 1152 | 1153 | type TZFillCallback interface { 1154 | ZFill(bot1, top1, bot2, top2, intersectPt *IntPoint) 1155 | } 1156 | 1157 | type Clipper struct { 1158 | ClipperBase 1159 | 1160 | m_PolyOuts []*OutRec 1161 | m_ClipType ClipType 1162 | m_Scanbeam *Scanbeam 1163 | m_ActiveEdges *TEdge 1164 | m_SortedEdges *TEdge 1165 | m_IntersectList []*IntersectNode 1166 | // m_IntersectNodeComparer IComparer_IntersectNode 1167 | m_ExecuteLocked bool 1168 | m_ClipFillType PolyFillType 1169 | m_SubjFillType PolyFillType 1170 | m_Joins []*Join 1171 | m_GhostJoins []*Join 1172 | m_UsingPolyTree bool 1173 | ZFillFunction TZFillCallback 1174 | ReverseSolution, StrictlySimple bool 1175 | } 1176 | 1177 | func NewClipper(initOptions InitOptions) *Clipper { 1178 | c := new(Clipper) 1179 | c.m_edges = make([][]*TEdge, 0) 1180 | c.m_Scanbeam = nil 1181 | c.m_ActiveEdges = nil 1182 | c.m_SortedEdges = nil 1183 | c.m_IntersectList = make([]*IntersectNode, 0) 1184 | // c.m_IntersectNodeComparer = new MyIntersectNodeSort(); 1185 | c.m_ExecuteLocked = false 1186 | c.m_UsingPolyTree = false 1187 | c.m_PolyOuts = make([]*OutRec, 0) 1188 | c.m_Joins = make([]*Join, 0) 1189 | c.m_GhostJoins = make([]*Join, 0) 1190 | c.ReverseSolution = (IoReverseSolution == initOptions) 1191 | c.StrictlySimple = (IoStrictlySimple == initOptions) 1192 | c.PreserveCollinear = (IoPreserveCollinear == initOptions) 1193 | return c 1194 | } 1195 | 1196 | //------------------------------------------------------------------------------ 1197 | 1198 | func (c *Clipper) DisposeScanbeamList() { 1199 | for c.m_Scanbeam != nil { 1200 | sb2 := c.m_Scanbeam.Next 1201 | c.m_Scanbeam = nil 1202 | c.m_Scanbeam = sb2 1203 | } 1204 | } 1205 | 1206 | //------------------------------------------------------------------------------ 1207 | 1208 | func (c *Clipper) Reset() { 1209 | c.m_CurrentLM = c.m_MinimaList 1210 | if c.m_CurrentLM != nil { 1211 | //reset all edges ... 1212 | lm := c.m_MinimaList 1213 | for lm != nil { 1214 | e := lm.LeftBound 1215 | if e != nil { 1216 | e.Curr = e.Bot 1217 | e.Side = EsLeft 1218 | e.OutIdx = Unassigned 1219 | } 1220 | e = lm.RightBound 1221 | if e != nil { 1222 | e.Curr = e.Bot 1223 | e.Side = EsRight 1224 | e.OutIdx = Unassigned 1225 | } 1226 | lm = lm.Next 1227 | } 1228 | } 1229 | c.m_Scanbeam = nil 1230 | c.m_ActiveEdges = nil 1231 | c.m_SortedEdges = nil 1232 | lm := c.m_MinimaList 1233 | for lm != nil { 1234 | c.InsertScanbeam(lm.Y) 1235 | lm = lm.Next 1236 | } 1237 | } 1238 | 1239 | //------------------------------------------------------------------------------ 1240 | 1241 | func (c *Clipper) InsertScanbeam(Y CInt) { 1242 | if c.m_Scanbeam == nil { 1243 | c.m_Scanbeam = new(Scanbeam) 1244 | c.m_Scanbeam.Next = nil 1245 | c.m_Scanbeam.Y = Y 1246 | } else if Y > c.m_Scanbeam.Y { 1247 | newSb := new(Scanbeam) 1248 | newSb.Y = Y 1249 | newSb.Next = c.m_Scanbeam 1250 | c.m_Scanbeam = newSb 1251 | } else { 1252 | sb2 := c.m_Scanbeam 1253 | for sb2.Next != nil && (Y <= sb2.Next.Y) { 1254 | sb2 = sb2.Next 1255 | } 1256 | if Y == sb2.Y { 1257 | return //ie ignores duplicates 1258 | } 1259 | newSb := new(Scanbeam) 1260 | newSb.Y = Y 1261 | newSb.Next = sb2.Next 1262 | sb2.Next = newSb 1263 | } 1264 | // fmt.Printf("slkjfalsjflasjldfkjaslj%v\n", c.m_Scanbeam.printScanbeams()) 1265 | } 1266 | 1267 | //------------------------------------------------------------------------------ 1268 | 1269 | func (c *Clipper) Execute1(clipType ClipType, 1270 | subjFillType, clipFillType PolyFillType) (solution Paths, succeeded bool) { 1271 | if c.m_ExecuteLocked { 1272 | return 1273 | } else { 1274 | defer func() { 1275 | c.DisposeAllPolyPts() 1276 | c.m_ExecuteLocked = false 1277 | }() 1278 | } 1279 | if c.m_HasOpenPaths { 1280 | panic(NewClipperException("Error: PolyTree struct is needed for " + 1281 | "open path clipping.")) 1282 | } 1283 | 1284 | c.m_ExecuteLocked = true 1285 | c.m_SubjFillType = subjFillType 1286 | c.m_ClipFillType = clipFillType 1287 | c.m_ClipType = clipType 1288 | c.m_UsingPolyTree = false 1289 | 1290 | succeeded = c.ExecuteInternal() 1291 | //build the return polygons ... 1292 | if succeeded { 1293 | solution = c.BuildResult() 1294 | } 1295 | return 1296 | } 1297 | 1298 | //------------------------------------------------------------------------------ 1299 | 1300 | func (c *Clipper) Execute2(clipType ClipType, 1301 | subjFillType, clipFillType PolyFillType) (polytree *PolyTree, succeeded bool) { 1302 | if c.m_ExecuteLocked { 1303 | return 1304 | } else { 1305 | defer func() { 1306 | c.DisposeAllPolyPts() 1307 | c.m_ExecuteLocked = false 1308 | }() 1309 | } 1310 | c.m_ExecuteLocked = true 1311 | c.m_SubjFillType = subjFillType 1312 | c.m_ClipFillType = clipFillType 1313 | c.m_ClipType = clipType 1314 | c.m_UsingPolyTree = true 1315 | 1316 | succeeded = c.ExecuteInternal() 1317 | //build the return polygons ... 1318 | if succeeded { 1319 | polytree = NewPolyTree() 1320 | c.BuildResult2(polytree) 1321 | } 1322 | return 1323 | } 1324 | 1325 | //------------------------------------------------------------------------------ 1326 | 1327 | func (c *Clipper) FixHoleLinkage(outRec *OutRec) { 1328 | //skip if an outermost polygon or 1329 | //already already points to the correct FirstLeft ... 1330 | if outRec.FirstLeft == nil || 1331 | (outRec.IsHole != outRec.FirstLeft.IsHole && 1332 | outRec.FirstLeft.Pts != nil) { 1333 | return 1334 | } 1335 | 1336 | orfl := outRec.FirstLeft 1337 | for orfl != nil && ((orfl.IsHole == outRec.IsHole) || orfl.Pts == nil) { 1338 | orfl = orfl.FirstLeft 1339 | } 1340 | outRec.FirstLeft = orfl 1341 | } 1342 | 1343 | //------------------------------------------------------------------------------ 1344 | 1345 | func (c *Clipper) ExecuteInternal() bool { 1346 | defer func() { 1347 | c.m_Joins = make([]*Join, 0) 1348 | c.m_GhostJoins = make([]*Join, 0) 1349 | }() 1350 | 1351 | c.Reset() 1352 | if c.m_CurrentLM == nil { 1353 | return false 1354 | } 1355 | 1356 | botY := c.PopScanbeam() 1357 | for { 1358 | c.InsertLocalMinimaIntoAEL(botY) 1359 | c.m_GhostJoins = make([]*Join, 0) 1360 | c.ProcessHorizontals(false) 1361 | if c.m_Scanbeam == nil { 1362 | break 1363 | } 1364 | topY := c.PopScanbeam() 1365 | if !c.ProcessIntersections(topY) { 1366 | return false 1367 | } 1368 | c.ProcessEdgesAtTopOfScanbeam(topY) 1369 | botY = topY 1370 | if !(c.m_Scanbeam != nil || c.m_CurrentLM != nil) { 1371 | break 1372 | } 1373 | } 1374 | 1375 | //fix orientations ... 1376 | for i := 0; i < len(c.m_PolyOuts); i++ { 1377 | outRec := c.m_PolyOuts[i] 1378 | if outRec.Pts == nil || outRec.IsOpen { 1379 | continue 1380 | } 1381 | if (outRec.IsHole != c.ReverseSolution) == (c.area(outRec) > 0) { 1382 | c.ReversePolyPtLinks(outRec.Pts) 1383 | } 1384 | } 1385 | 1386 | c.JoinCommonEdges() 1387 | 1388 | for i := 0; i < len(c.m_PolyOuts); i++ { 1389 | outRec := c.m_PolyOuts[i] 1390 | if outRec.Pts != nil && !outRec.IsOpen { 1391 | c.FixupOutPolygon(outRec) 1392 | } 1393 | } 1394 | 1395 | if c.StrictlySimple { 1396 | c.DoSimplePolygons() 1397 | } 1398 | 1399 | return true 1400 | } 1401 | 1402 | //------------------------------------------------------------------------------ 1403 | 1404 | func (c *Clipper) PopScanbeam() CInt { 1405 | Y := c.m_Scanbeam.Y 1406 | c.m_Scanbeam = c.m_Scanbeam.Next 1407 | return Y 1408 | } 1409 | 1410 | //------------------------------------------------------------------------------ 1411 | 1412 | func (c *Clipper) DisposeAllPolyPts() { 1413 | for i := 0; i < len(c.m_PolyOuts); i++ { 1414 | c.DisposeOutRec(i) 1415 | } 1416 | c.m_PolyOuts = make([]*OutRec, 0) 1417 | } 1418 | 1419 | //------------------------------------------------------------------------------ 1420 | 1421 | func (c *Clipper) DisposeOutRec(index int) { 1422 | outRec := c.m_PolyOuts[index] 1423 | outRec.Pts = nil 1424 | outRec = nil 1425 | c.m_PolyOuts[index] = nil 1426 | } 1427 | 1428 | //------------------------------------------------------------------------------ 1429 | 1430 | func (c *Clipper) AddJoin(Op1, Op2 *OutPt, OffPt *IntPoint) { 1431 | j := new(Join) 1432 | j.OutPt1 = Op1 1433 | j.OutPt2 = Op2 1434 | j.OffPt = OffPt 1435 | c.m_Joins = append(c.m_Joins, j) 1436 | } 1437 | 1438 | //------------------------------------------------------------------------------ 1439 | 1440 | func (c *Clipper) AddGhostJoin(Op *OutPt, OffPt *IntPoint) { 1441 | j := new(Join) 1442 | j.OutPt1 = Op 1443 | j.OffPt = OffPt 1444 | c.m_GhostJoins = append(c.m_GhostJoins, j) 1445 | } 1446 | 1447 | //------------------------------------------------------------------------------ 1448 | 1449 | func (c *Clipper) InsertLocalMinimaIntoAEL(botY CInt) { 1450 | for c.m_CurrentLM != nil && (c.m_CurrentLM.Y == botY) { 1451 | lb := c.m_CurrentLM.LeftBound 1452 | rb := c.m_CurrentLM.RightBound 1453 | c.PopLocalMinima() 1454 | 1455 | var Op1 *OutPt 1456 | if lb == nil { 1457 | c.InsertEdgeIntoAEL(rb, nil) 1458 | c.SetWindingCount(rb) 1459 | if c.IsContributing(rb) { 1460 | Op1 = c.AddOutPt(rb, &rb.Bot) 1461 | } 1462 | } else if rb == nil { 1463 | c.InsertEdgeIntoAEL(lb, nil) 1464 | c.SetWindingCount(lb) 1465 | if c.IsContributing(lb) { 1466 | Op1 = c.AddOutPt(lb, &lb.Bot) 1467 | } 1468 | c.InsertScanbeam(lb.Top.Y) 1469 | } else { 1470 | c.InsertEdgeIntoAEL(lb, nil) 1471 | c.InsertEdgeIntoAEL(rb, lb) 1472 | c.SetWindingCount(lb) 1473 | rb.WindCnt = lb.WindCnt 1474 | rb.WindCnt2 = lb.WindCnt2 1475 | if c.IsContributing(lb) { 1476 | Op1 = c.AddLocalMinPoly(lb, rb, &lb.Bot) 1477 | } 1478 | c.InsertScanbeam(lb.Top.Y) 1479 | } 1480 | 1481 | if rb != nil { 1482 | if c.IsHorizontal(rb) { 1483 | c.AddEdgeToSEL(rb) 1484 | } else { 1485 | c.InsertScanbeam(rb.Top.Y) 1486 | } 1487 | } 1488 | 1489 | if lb == nil || rb == nil { 1490 | continue 1491 | } 1492 | 1493 | //if output polygons share an Edge with a horizontal rb, they'll need joining later ... 1494 | if Op1 != nil && c.IsHorizontal(rb) && 1495 | len(c.m_GhostJoins) > 0 && rb.WindDelta != 0 { 1496 | for i := 0; i < len(c.m_GhostJoins); i++ { 1497 | //if the horizontal Rb and a 'ghost' horizontal overlap, then convert 1498 | //the 'ghost' join to a real join ready for later ... 1499 | j := c.m_GhostJoins[i] 1500 | if c.HorzSegmentsOverlap(j.OutPt1.Pt.X, j.OffPt.X, rb.Bot.X, rb.Top.X) { 1501 | c.AddJoin(j.OutPt1, Op1, j.OffPt) 1502 | } 1503 | } 1504 | } 1505 | 1506 | if lb.OutIdx >= 0 && lb.PrevInAEL != nil && 1507 | lb.PrevInAEL.Curr.X == lb.Bot.X && 1508 | lb.PrevInAEL.OutIdx >= 0 && 1509 | c.SlopesEqual(lb.PrevInAEL, lb, c.m_UseFullRange) && 1510 | lb.WindDelta != 0 && lb.PrevInAEL.WindDelta != 0 { 1511 | Op2 := c.AddOutPt(lb.PrevInAEL, &lb.Bot) 1512 | c.AddJoin(Op1, Op2, &lb.Top) 1513 | } 1514 | 1515 | if lb.NextInAEL != rb { 1516 | if rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 && 1517 | c.SlopesEqual(rb.PrevInAEL, rb, c.m_UseFullRange) && 1518 | rb.WindDelta != 0 && rb.PrevInAEL.WindDelta != 0 { 1519 | Op2 := c.AddOutPt(rb.PrevInAEL, &rb.Bot) 1520 | c.AddJoin(Op1, Op2, &rb.Top) 1521 | } 1522 | 1523 | e := lb.NextInAEL 1524 | if e != nil { 1525 | for e != rb { 1526 | //nb: For calculating winding counts etc, IntersectEdges() assumes 1527 | //that param1 will be to the right of param2 ABOVE the intersection ... 1528 | c.IntersectEdges(rb, e, &lb.Curr, true) //order important here 1529 | e = e.NextInAEL 1530 | } 1531 | } 1532 | } 1533 | } 1534 | } 1535 | 1536 | //------------------------------------------------------------------------------ 1537 | 1538 | func (c *Clipper) InsertEdgeIntoAEL(edge, startEdge *TEdge) { 1539 | if c.m_ActiveEdges == nil { 1540 | edge.PrevInAEL = nil 1541 | edge.NextInAEL = nil 1542 | c.m_ActiveEdges = edge 1543 | } else if startEdge == nil && c.E2InsertsBeforeE1(c.m_ActiveEdges, edge) { 1544 | edge.PrevInAEL = nil 1545 | edge.NextInAEL = c.m_ActiveEdges 1546 | c.m_ActiveEdges.PrevInAEL = edge 1547 | c.m_ActiveEdges = edge 1548 | } else { 1549 | if startEdge == nil { 1550 | startEdge = c.m_ActiveEdges 1551 | } 1552 | for startEdge.NextInAEL != nil && 1553 | !c.E2InsertsBeforeE1(startEdge.NextInAEL, edge) { 1554 | startEdge = startEdge.NextInAEL 1555 | } 1556 | edge.NextInAEL = startEdge.NextInAEL 1557 | if startEdge.NextInAEL != nil { 1558 | startEdge.NextInAEL.PrevInAEL = edge 1559 | } 1560 | edge.PrevInAEL = startEdge 1561 | startEdge.NextInAEL = edge 1562 | } 1563 | } 1564 | 1565 | //---------------------------------------------------------------------- 1566 | 1567 | func (c *Clipper) E2InsertsBeforeE1(e1, e2 *TEdge) bool { 1568 | if e2.Curr.X == e1.Curr.X { 1569 | if e2.Top.Y > e1.Top.Y { 1570 | return e2.Top.X < TopX(e1, &e2.Top.Y) 1571 | } 1572 | return e1.Top.X > TopX(e2, &e1.Top.Y) 1573 | } 1574 | return e2.Curr.X < e1.Curr.X 1575 | } 1576 | 1577 | //------------------------------------------------------------------------------ 1578 | 1579 | func (c *Clipper) IsEvenOddFillType(edge *TEdge) bool { 1580 | if edge.PolyTyp == PtSubject { 1581 | return c.m_SubjFillType == PftEvenOdd 1582 | } else { 1583 | return c.m_ClipFillType == PftEvenOdd 1584 | } 1585 | } 1586 | 1587 | //------------------------------------------------------------------------------ 1588 | 1589 | func (c *Clipper) IsEvenOddAltFillType(edge *TEdge) bool { 1590 | if edge.PolyTyp == PtSubject { 1591 | return c.m_ClipFillType == PftEvenOdd 1592 | } else { 1593 | return c.m_SubjFillType == PftEvenOdd 1594 | } 1595 | } 1596 | 1597 | //------------------------------------------------------------------------------ 1598 | 1599 | func (c *Clipper) IsContributing(edge *TEdge) bool { 1600 | var pft, pft2 PolyFillType 1601 | if edge.PolyTyp == PtSubject { 1602 | pft = c.m_SubjFillType 1603 | pft2 = c.m_ClipFillType 1604 | } else { 1605 | pft = c.m_ClipFillType 1606 | pft2 = c.m_SubjFillType 1607 | } 1608 | 1609 | switch pft { 1610 | case PftEvenOdd: 1611 | //return false if a subj line has been flagged as inside a subj polygon 1612 | if edge.WindDelta == 0 && edge.WindCnt != 1 { 1613 | return false 1614 | } 1615 | break 1616 | case PftNonZero: 1617 | if intAbs(edge.WindCnt) != 1 { 1618 | return false 1619 | } 1620 | break 1621 | case PftPositive: 1622 | if edge.WindCnt != 1 { 1623 | return false 1624 | } 1625 | break 1626 | default: //PolyFillType.PftNegative 1627 | if edge.WindCnt != -1 { 1628 | return false 1629 | } 1630 | break 1631 | } 1632 | 1633 | switch c.m_ClipType { 1634 | case CtIntersection: 1635 | switch pft2 { 1636 | case PftEvenOdd, PftNonZero: 1637 | return (edge.WindCnt2 != 0) 1638 | case PftPositive: 1639 | return (edge.WindCnt2 > 0) 1640 | default: 1641 | return (edge.WindCnt2 < 0) 1642 | } 1643 | case CtUnion: 1644 | switch pft2 { 1645 | case PftEvenOdd, PftNonZero: 1646 | return (edge.WindCnt2 == 0) 1647 | case PftPositive: 1648 | return (edge.WindCnt2 <= 0) 1649 | default: 1650 | return (edge.WindCnt2 >= 0) 1651 | } 1652 | case CtDifference: 1653 | if edge.PolyTyp == PtSubject { 1654 | switch pft2 { 1655 | case PftEvenOdd, PftNonZero: 1656 | return (edge.WindCnt2 == 0) 1657 | case PftPositive: 1658 | return (edge.WindCnt2 <= 0) 1659 | default: 1660 | return (edge.WindCnt2 >= 0) 1661 | } 1662 | } else { 1663 | switch pft2 { 1664 | case PftEvenOdd, PftNonZero: 1665 | return (edge.WindCnt2 != 0) 1666 | case PftPositive: 1667 | return (edge.WindCnt2 > 0) 1668 | default: 1669 | return (edge.WindCnt2 < 0) 1670 | } 1671 | } 1672 | case CtXor: 1673 | if edge.WindDelta == 0 { //XOr always contributing unless open 1674 | switch pft2 { 1675 | case PftEvenOdd, PftNonZero: 1676 | return (edge.WindCnt2 == 0) 1677 | case PftPositive: 1678 | return (edge.WindCnt2 <= 0) 1679 | default: 1680 | return (edge.WindCnt2 >= 0) 1681 | } 1682 | } else { 1683 | return true 1684 | } 1685 | } 1686 | return true 1687 | } 1688 | 1689 | //------------------------------------------------------------------------------ 1690 | 1691 | func (c *Clipper) SetWindingCount(edge *TEdge) { 1692 | e := edge.PrevInAEL 1693 | //find the edge of the same polytype that immediately preceeds 'edge' in AEL 1694 | for e != nil && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta == 0)) { 1695 | e = e.PrevInAEL 1696 | } 1697 | if e == nil { 1698 | if edge.WindDelta == 0 { 1699 | edge.WindCnt = 1 1700 | } else { 1701 | edge.WindCnt = edge.WindDelta 1702 | } 1703 | edge.WindCnt2 = 0 1704 | e = c.m_ActiveEdges //ie get ready to calc WindCnt2 1705 | } else if edge.WindDelta == 0 && c.m_ClipType != CtUnion { 1706 | edge.WindCnt = 1 1707 | edge.WindCnt2 = e.WindCnt2 1708 | e = e.NextInAEL //ie get ready to calc WindCnt2 1709 | } else if c.IsEvenOddFillType(edge) { 1710 | //EvenOdd filling ... 1711 | if edge.WindDelta == 0 { 1712 | //are we inside a subj polygon ... 1713 | Inside := true 1714 | e2 := e.PrevInAEL 1715 | for e2 != nil { 1716 | if e2.PolyTyp == e.PolyTyp && e2.WindDelta != 0 { 1717 | Inside = !Inside 1718 | } 1719 | e2 = e2.PrevInAEL 1720 | } 1721 | if Inside { 1722 | edge.WindCnt = 0 1723 | } else { 1724 | edge.WindCnt = 1 1725 | } 1726 | } else { 1727 | edge.WindCnt = edge.WindDelta 1728 | } 1729 | edge.WindCnt2 = e.WindCnt2 1730 | e = e.NextInAEL //ie get ready to calc WindCnt2 1731 | } else { 1732 | //nonZero, Positive or Negative filling ... 1733 | if e.WindCnt*e.WindDelta < 0 { 1734 | //prev edge is 'decreasing' WindCount (WC) toward zero 1735 | //so we're outside the previous polygon ... 1736 | if intAbs(e.WindCnt) > 1 { 1737 | //outside prev poly but still inside another. 1738 | //when reversing direction of prev poly use the same WC 1739 | if e.WindDelta*edge.WindDelta < 0 { 1740 | edge.WindCnt = e.WindCnt 1741 | //otherwise continue to 'decrease' WC ... 1742 | } else { 1743 | edge.WindCnt = e.WindCnt + edge.WindDelta 1744 | } 1745 | } else { 1746 | //now outside all polys of same polytype so set own WC ... 1747 | if edge.WindDelta == 0 { 1748 | edge.WindCnt = 1 1749 | } else { 1750 | edge.WindCnt = edge.WindDelta 1751 | } 1752 | } 1753 | } else { 1754 | //prev edge is 'increasing' WindCount (WC) away from zero 1755 | //so we're inside the previous polygon ... 1756 | if edge.WindDelta == 0 { 1757 | if e.WindCnt < 0 { 1758 | edge.WindCnt = e.WindCnt - 1 1759 | } else { 1760 | edge.WindCnt = e.WindCnt + 1 1761 | } 1762 | //if wind direction is reversing prev then use same WC 1763 | } else if e.WindDelta*edge.WindDelta < 0 { 1764 | edge.WindCnt = e.WindCnt 1765 | //otherwise add to WC ... 1766 | } else { 1767 | edge.WindCnt = e.WindCnt + edge.WindDelta 1768 | } 1769 | } 1770 | edge.WindCnt2 = e.WindCnt2 1771 | e = e.NextInAEL //ie get ready to calc WindCnt2 1772 | } 1773 | 1774 | //update WindCnt2 ... 1775 | if c.IsEvenOddAltFillType(edge) { 1776 | //EvenOdd filling ... 1777 | for e != edge { 1778 | if e.WindDelta != 0 { 1779 | if edge.WindCnt2 == 0 { 1780 | edge.WindCnt2 = 1 1781 | } else { 1782 | edge.WindCnt2 = 0 1783 | } 1784 | } 1785 | e = e.NextInAEL 1786 | } 1787 | } else { 1788 | //nonZero, Positive or Negative filling ... 1789 | for e != edge { 1790 | edge.WindCnt2 += e.WindDelta 1791 | e = e.NextInAEL 1792 | } 1793 | } 1794 | } 1795 | 1796 | //------------------------------------------------------------------------------ 1797 | 1798 | func (c *Clipper) AddEdgeToSEL(edge *TEdge) { 1799 | //SEL pointers in PEdge are reused to build a list of horizontal edges. 1800 | //However, we don't need to worry about order with horizontal edge processing. 1801 | if c.m_SortedEdges == nil { 1802 | c.m_SortedEdges = edge 1803 | edge.PrevInSEL = nil 1804 | edge.NextInSEL = nil 1805 | } else { 1806 | edge.NextInSEL = c.m_SortedEdges 1807 | edge.PrevInSEL = nil 1808 | c.m_SortedEdges.PrevInSEL = edge 1809 | c.m_SortedEdges = edge 1810 | } 1811 | } 1812 | 1813 | //------------------------------------------------------------------------------ 1814 | 1815 | func (c *Clipper) CopyAELToSEL() { 1816 | e := c.m_ActiveEdges 1817 | c.m_SortedEdges = e 1818 | for e != nil { 1819 | e.PrevInSEL = e.PrevInAEL 1820 | e.NextInSEL = e.NextInAEL 1821 | e = e.NextInAEL 1822 | } 1823 | } 1824 | 1825 | //------------------------------------------------------------------------------ 1826 | 1827 | func (c *Clipper) SwapPositionsInAEL(edge1, edge2 *TEdge) { 1828 | //check that one or other edge hasn't already been removed from AEL ... 1829 | if edge1.NextInAEL == edge1.PrevInAEL || 1830 | edge2.NextInAEL == edge2.PrevInAEL { 1831 | return 1832 | } 1833 | 1834 | if edge1.NextInAEL == edge2 { 1835 | next := edge2.NextInAEL 1836 | if next != nil { 1837 | next.PrevInAEL = edge1 1838 | } 1839 | prev := edge1.PrevInAEL 1840 | if prev != nil { 1841 | prev.NextInAEL = edge2 1842 | } 1843 | edge2.PrevInAEL = prev 1844 | edge2.NextInAEL = edge1 1845 | edge1.PrevInAEL = edge2 1846 | edge1.NextInAEL = next 1847 | } else if edge2.NextInAEL == edge1 { 1848 | next := edge1.NextInAEL 1849 | if next != nil { 1850 | next.PrevInAEL = edge2 1851 | } 1852 | prev := edge2.PrevInAEL 1853 | if prev != nil { 1854 | prev.NextInAEL = edge1 1855 | } 1856 | edge1.PrevInAEL = prev 1857 | edge1.NextInAEL = edge2 1858 | edge2.PrevInAEL = edge1 1859 | edge2.NextInAEL = next 1860 | } else { 1861 | next := edge1.NextInAEL 1862 | prev := edge1.PrevInAEL 1863 | edge1.NextInAEL = edge2.NextInAEL 1864 | if edge1.NextInAEL != nil { 1865 | edge1.NextInAEL.PrevInAEL = edge1 1866 | } 1867 | edge1.PrevInAEL = edge2.PrevInAEL 1868 | if edge1.PrevInAEL != nil { 1869 | edge1.PrevInAEL.NextInAEL = edge1 1870 | } 1871 | edge2.NextInAEL = next 1872 | if edge2.NextInAEL != nil { 1873 | edge2.NextInAEL.PrevInAEL = edge2 1874 | } 1875 | edge2.PrevInAEL = prev 1876 | if edge2.PrevInAEL != nil { 1877 | edge2.PrevInAEL.NextInAEL = edge2 1878 | } 1879 | } 1880 | 1881 | if edge1.PrevInAEL == nil { 1882 | c.m_ActiveEdges = edge1 1883 | } else if edge2.PrevInAEL == nil { 1884 | c.m_ActiveEdges = edge2 1885 | } 1886 | } 1887 | 1888 | //------------------------------------------------------------------------------ 1889 | 1890 | func (c *Clipper) SwapPositionsInSEL(edge1, edge2 *TEdge) { 1891 | if edge1.NextInSEL == nil && edge1.PrevInSEL == nil { 1892 | return 1893 | } 1894 | if edge2.NextInSEL == nil && edge2.PrevInSEL == nil { 1895 | return 1896 | } 1897 | 1898 | if edge1.NextInSEL == edge2 { 1899 | next := edge2.NextInSEL 1900 | if next != nil { 1901 | next.PrevInSEL = edge1 1902 | } 1903 | prev := edge1.PrevInSEL 1904 | if prev != nil { 1905 | prev.NextInSEL = edge2 1906 | } 1907 | edge2.PrevInSEL = prev 1908 | edge2.NextInSEL = edge1 1909 | edge1.PrevInSEL = edge2 1910 | edge1.NextInSEL = next 1911 | } else if edge2.NextInSEL == edge1 { 1912 | next := edge1.NextInSEL 1913 | if next != nil { 1914 | next.PrevInSEL = edge2 1915 | } 1916 | prev := edge2.PrevInSEL 1917 | if prev != nil { 1918 | prev.NextInSEL = edge1 1919 | } 1920 | edge1.PrevInSEL = prev 1921 | edge1.NextInSEL = edge2 1922 | edge2.PrevInSEL = edge1 1923 | edge2.NextInSEL = next 1924 | } else { 1925 | next := edge1.NextInSEL 1926 | prev := edge1.PrevInSEL 1927 | edge1.NextInSEL = edge2.NextInSEL 1928 | if edge1.NextInSEL != nil { 1929 | edge1.NextInSEL.PrevInSEL = edge1 1930 | } 1931 | edge1.PrevInSEL = edge2.PrevInSEL 1932 | if edge1.PrevInSEL != nil { 1933 | edge1.PrevInSEL.NextInSEL = edge1 1934 | } 1935 | edge2.NextInSEL = next 1936 | if edge2.NextInSEL != nil { 1937 | edge2.NextInSEL.PrevInSEL = edge2 1938 | } 1939 | edge2.PrevInSEL = prev 1940 | if edge2.PrevInSEL != nil { 1941 | edge2.PrevInSEL.NextInSEL = edge2 1942 | } 1943 | } 1944 | 1945 | if edge1.PrevInSEL == nil { 1946 | c.m_SortedEdges = edge1 1947 | } else if edge2.PrevInSEL == nil { 1948 | c.m_SortedEdges = edge2 1949 | } 1950 | } 1951 | 1952 | //------------------------------------------------------------------------------ 1953 | 1954 | func (c *Clipper) AddLocalMaxPoly(e1, e2 *TEdge, pt *IntPoint) { 1955 | c.AddOutPt(e1, pt) 1956 | if e2.WindDelta == 0 { 1957 | c.AddOutPt(e2, pt) 1958 | } 1959 | if e1.OutIdx == e2.OutIdx { 1960 | e1.OutIdx = Unassigned 1961 | e2.OutIdx = Unassigned 1962 | } else if e1.OutIdx < e2.OutIdx { 1963 | c.AppendPolygon(e1, e2) 1964 | } else { 1965 | c.AppendPolygon(e2, e1) 1966 | } 1967 | } 1968 | 1969 | //------------------------------------------------------------------------------ 1970 | 1971 | func (c *Clipper) AddLocalMinPoly(e1, e2 *TEdge, pt *IntPoint) *OutPt { 1972 | var result *OutPt 1973 | var e, prevE *TEdge 1974 | if c.IsHorizontal(e2) || (e1.Dx > e2.Dx) { 1975 | result = c.AddOutPt(e1, pt) 1976 | e2.OutIdx = e1.OutIdx 1977 | e1.Side = EsLeft 1978 | e2.Side = EsRight 1979 | e = e1 1980 | if e.PrevInAEL == e2 { 1981 | prevE = e2.PrevInAEL 1982 | } else { 1983 | prevE = e.PrevInAEL 1984 | } 1985 | } else { 1986 | result = c.AddOutPt(e2, pt) 1987 | e1.OutIdx = e2.OutIdx 1988 | e1.Side = EsRight 1989 | e2.Side = EsLeft 1990 | e = e2 1991 | if e.PrevInAEL == e1 { 1992 | prevE = e1.PrevInAEL 1993 | } else { 1994 | prevE = e.PrevInAEL 1995 | } 1996 | } 1997 | 1998 | if prevE != nil && prevE.OutIdx >= 0 && 1999 | (TopX(prevE, &pt.Y) == TopX(e, &pt.Y)) && 2000 | c.SlopesEqual(e, prevE, c.m_UseFullRange) && 2001 | (e.WindDelta != 0) && (prevE.WindDelta != 0) { 2002 | outPt := c.AddOutPt(prevE, pt) 2003 | c.AddJoin(result, outPt, &e.Top) 2004 | } 2005 | return result 2006 | } 2007 | 2008 | //------------------------------------------------------------------------------ 2009 | 2010 | func (c *Clipper) CreateOutRec() *OutRec { 2011 | result := new(OutRec) 2012 | result.Idx = Unassigned 2013 | result.IsHole = false 2014 | result.IsOpen = false 2015 | result.FirstLeft = nil 2016 | result.Pts = nil 2017 | result.BottomPt = nil 2018 | result.PolyNode = nil 2019 | c.m_PolyOuts = append(c.m_PolyOuts, result) 2020 | result.Idx = len(c.m_PolyOuts) - 1 2021 | return result 2022 | } 2023 | 2024 | //------------------------------------------------------------------------------ 2025 | 2026 | func (c *Clipper) AddOutPt(e *TEdge, pt *IntPoint) *OutPt { 2027 | ToFront := (e.Side == EsLeft) 2028 | if e.OutIdx < 0 { 2029 | outRec := c.CreateOutRec() 2030 | outRec.IsOpen = (e.WindDelta == 0) 2031 | newOp := new(OutPt) 2032 | outRec.Pts = newOp 2033 | newOp.Idx = outRec.Idx 2034 | p := pt.Copy() 2035 | newOp.Pt = &p 2036 | newOp.Next = newOp 2037 | newOp.Prev = newOp 2038 | if !outRec.IsOpen { 2039 | c.SetHoleState(e, outRec) 2040 | } 2041 | e.OutIdx = outRec.Idx //nb: do this after SetZ ! 2042 | return newOp 2043 | } else { 2044 | outRec := c.m_PolyOuts[e.OutIdx] 2045 | //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' 2046 | op := outRec.Pts 2047 | if ToFront && *pt == *op.Pt { 2048 | return op 2049 | } else if !ToFront && *pt == *op.Prev.Pt { 2050 | return op.Prev 2051 | } 2052 | 2053 | newOp := new(OutPt) 2054 | newOp.Idx = outRec.Idx 2055 | p := pt.Copy() 2056 | newOp.Pt = &p 2057 | newOp.Next = op 2058 | newOp.Prev = op.Prev 2059 | newOp.Prev.Next = newOp 2060 | op.Prev = newOp 2061 | if ToFront { 2062 | outRec.Pts = newOp 2063 | } 2064 | return newOp 2065 | } 2066 | } 2067 | 2068 | //------------------------------------------------------------------------------ 2069 | 2070 | func (c *Clipper) HorzSegmentsOverlap(seg1a, seg1b, seg2a, seg2b CInt) bool { 2071 | if seg1a > seg1b { 2072 | c.Swap(&seg1a, &seg1b) 2073 | } 2074 | if seg2a > seg2b { 2075 | c.Swap(&seg2a, &seg2b) 2076 | } 2077 | return (seg1a < seg2b) && (seg2a < seg1b) 2078 | } 2079 | 2080 | //------------------------------------------------------------------------------ 2081 | 2082 | func (c *Clipper) SetHoleState(e *TEdge, outRec *OutRec) { 2083 | isHole := false 2084 | e2 := e.PrevInAEL 2085 | for e2 != nil { 2086 | if e2.OutIdx >= 0 && e2.WindDelta != 0 { 2087 | isHole = !isHole 2088 | if outRec.FirstLeft == nil { 2089 | outRec.FirstLeft = c.m_PolyOuts[e2.OutIdx] 2090 | } 2091 | } 2092 | e2 = e2.PrevInAEL 2093 | } 2094 | if isHole { 2095 | outRec.IsHole = true 2096 | } 2097 | } 2098 | 2099 | //------------------------------------------------------------------------------ 2100 | 2101 | func (c *Clipper) GetDx(pt1, pt2 *IntPoint) float64 { 2102 | if pt1.Y == pt2.Y { 2103 | return horizontal 2104 | } else { 2105 | return float64(pt2.X-pt1.X) / float64(pt2.Y-pt1.Y) 2106 | } 2107 | } 2108 | 2109 | //--------------------------------------------------------------------------- 2110 | 2111 | func (c *Clipper) FirstIsBottomPt(btmPt1, btmPt2 *OutPt) bool { 2112 | p := btmPt1.Prev 2113 | for (*p.Pt == *btmPt1.Pt) && (p != btmPt1) { 2114 | p = p.Prev 2115 | } 2116 | dx1p := math.Abs(c.GetDx(btmPt1.Pt, p.Pt)) 2117 | p = btmPt1.Next 2118 | for (*p.Pt == *btmPt1.Pt) && (p != btmPt1) { 2119 | p = p.Next 2120 | } 2121 | dx1n := math.Abs(c.GetDx(btmPt1.Pt, p.Pt)) 2122 | 2123 | p = btmPt2.Prev 2124 | for (*p.Pt == *btmPt2.Pt) && (p != btmPt2) { 2125 | p = p.Prev 2126 | } 2127 | dx2p := math.Abs(c.GetDx(btmPt2.Pt, p.Pt)) 2128 | p = btmPt2.Next 2129 | for (*p.Pt == *btmPt2.Pt) && (p != btmPt2) { 2130 | p = p.Next 2131 | } 2132 | dx2n := math.Abs(c.GetDx(btmPt2.Pt, p.Pt)) 2133 | return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n) 2134 | } 2135 | 2136 | //------------------------------------------------------------------------------ 2137 | 2138 | func (c *Clipper) GetBottomPt(pp *OutPt) *OutPt { 2139 | var dups *OutPt 2140 | p := pp.Next 2141 | for p != pp { 2142 | if p.Pt.Y > pp.Pt.Y { 2143 | pp = p 2144 | dups = nil 2145 | } else if p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X { 2146 | if p.Pt.X < pp.Pt.X { 2147 | dups = nil 2148 | pp = p 2149 | } else { 2150 | if p.Next != pp && p.Prev != pp { 2151 | dups = p 2152 | } 2153 | } 2154 | } 2155 | p = p.Next 2156 | } 2157 | if dups != nil { 2158 | //there appears to be at least 2 vertices at bottomPt so ... 2159 | for dups != p { 2160 | if !c.FirstIsBottomPt(p, dups) { 2161 | pp = dups 2162 | } 2163 | dups = dups.Next 2164 | for *dups.Pt != *pp.Pt { 2165 | dups = dups.Next 2166 | } 2167 | } 2168 | } 2169 | return pp 2170 | } 2171 | 2172 | //------------------------------------------------------------------------------ 2173 | 2174 | func (c *Clipper) GetLowermostRec(outRec1, outRec2 *OutRec) *OutRec { 2175 | //work out which polygon fragment has the correct hole state ... 2176 | if outRec1.BottomPt == nil { 2177 | outRec1.BottomPt = c.GetBottomPt(outRec1.Pts) 2178 | } 2179 | if outRec2.BottomPt == nil { 2180 | outRec2.BottomPt = c.GetBottomPt(outRec2.Pts) 2181 | } 2182 | bPt1 := outRec1.BottomPt 2183 | bPt2 := outRec2.BottomPt 2184 | if bPt1.Pt.Y > bPt2.Pt.Y { 2185 | return outRec1 2186 | } else if bPt1.Pt.Y < bPt2.Pt.Y { 2187 | return outRec2 2188 | } else if bPt1.Pt.X < bPt2.Pt.X { 2189 | return outRec1 2190 | } else if bPt1.Pt.X > bPt2.Pt.X { 2191 | return outRec2 2192 | } else if bPt1.Next == bPt1 { 2193 | return outRec2 2194 | } else if bPt2.Next == bPt2 { 2195 | return outRec1 2196 | } else if c.FirstIsBottomPt(bPt1, bPt2) { 2197 | return outRec1 2198 | } else { 2199 | return outRec2 2200 | } 2201 | } 2202 | 2203 | //------------------------------------------------------------------------------ 2204 | 2205 | func (c *Clipper) Param1RightOfParam2(outRec1, outRec2 *OutRec) bool { 2206 | for { 2207 | outRec1 = outRec1.FirstLeft 2208 | if outRec1 == outRec2 { 2209 | return true 2210 | } 2211 | if outRec1 == nil { 2212 | break 2213 | } 2214 | } 2215 | return false 2216 | } 2217 | 2218 | //------------------------------------------------------------------------------ 2219 | 2220 | func (c *Clipper) GetOutRec(idx int) *OutRec { 2221 | outrec := c.m_PolyOuts[idx] 2222 | for outrec != c.m_PolyOuts[outrec.Idx] { 2223 | outrec = c.m_PolyOuts[outrec.Idx] 2224 | } 2225 | return outrec 2226 | } 2227 | 2228 | //------------------------------------------------------------------------------ 2229 | 2230 | func (c *Clipper) AppendPolygon(e1, e2 *TEdge) { 2231 | //get the start and ends of both output polygons ... 2232 | outRec1 := c.m_PolyOuts[e1.OutIdx] 2233 | outRec2 := c.m_PolyOuts[e2.OutIdx] 2234 | 2235 | var holeStateRec *OutRec 2236 | if c.Param1RightOfParam2(outRec1, outRec2) { 2237 | holeStateRec = outRec2 2238 | } else if c.Param1RightOfParam2(outRec2, outRec1) { 2239 | holeStateRec = outRec1 2240 | } else { 2241 | holeStateRec = c.GetLowermostRec(outRec1, outRec2) 2242 | } 2243 | 2244 | p1_lft := outRec1.Pts 2245 | p1_rt := p1_lft.Prev 2246 | p2_lft := outRec2.Pts 2247 | p2_rt := p2_lft.Prev 2248 | 2249 | var side EdgeSide 2250 | //join e2 poly onto e1 poly and delete pointers to e2 ... 2251 | if e1.Side == EsLeft { 2252 | if e2.Side == EsLeft { 2253 | //z y x a b c 2254 | c.ReversePolyPtLinks(p2_lft) 2255 | p2_lft.Next = p1_lft 2256 | p1_lft.Prev = p2_lft 2257 | p1_rt.Next = p2_rt 2258 | p2_rt.Prev = p1_rt 2259 | outRec1.Pts = p2_rt 2260 | } else { 2261 | //x y z a b c 2262 | p2_rt.Next = p1_lft 2263 | p1_lft.Prev = p2_rt 2264 | p2_lft.Prev = p1_rt 2265 | p1_rt.Next = p2_lft 2266 | outRec1.Pts = p2_lft 2267 | } 2268 | side = EsLeft 2269 | } else { 2270 | if e2.Side == EsRight { 2271 | //a b c z y x 2272 | c.ReversePolyPtLinks(p2_lft) 2273 | p1_rt.Next = p2_rt 2274 | p2_rt.Prev = p1_rt 2275 | p2_lft.Next = p1_lft 2276 | p1_lft.Prev = p2_lft 2277 | } else { 2278 | //a b c x y z 2279 | p1_rt.Next = p2_lft 2280 | p2_lft.Prev = p1_rt 2281 | p1_lft.Prev = p2_rt 2282 | p2_rt.Next = p1_lft 2283 | } 2284 | side = EsRight 2285 | } 2286 | 2287 | outRec1.BottomPt = nil 2288 | if holeStateRec == outRec2 { 2289 | if outRec2.FirstLeft != outRec1 { 2290 | outRec1.FirstLeft = outRec2.FirstLeft 2291 | } 2292 | outRec1.IsHole = outRec2.IsHole 2293 | } 2294 | outRec2.Pts = nil 2295 | outRec2.BottomPt = nil 2296 | 2297 | outRec2.FirstLeft = outRec1 2298 | 2299 | OKIdx := e1.OutIdx 2300 | ObsoleteIdx := e2.OutIdx 2301 | 2302 | e1.OutIdx = Unassigned //nb: safe because we only get here via AddLocalMaxPoly 2303 | e2.OutIdx = Unassigned 2304 | 2305 | e := c.m_ActiveEdges 2306 | for e != nil { 2307 | if e.OutIdx == ObsoleteIdx { 2308 | e.OutIdx = OKIdx 2309 | e.Side = side 2310 | break 2311 | } 2312 | e = e.NextInAEL 2313 | } 2314 | outRec2.Idx = outRec1.Idx 2315 | } 2316 | 2317 | //------------------------------------------------------------------------------ 2318 | 2319 | func (c *Clipper) ReversePolyPtLinks(pp *OutPt) { 2320 | if pp == nil { 2321 | return 2322 | } 2323 | var pp1, pp2 *OutPt 2324 | pp1 = pp 2325 | for { 2326 | pp2 = pp1.Next 2327 | pp1.Next = pp1.Prev 2328 | pp1.Prev = pp2 2329 | pp1 = pp2 2330 | if pp1 == pp { 2331 | break 2332 | } 2333 | } 2334 | } 2335 | 2336 | //------------------------------------------------------------------------------ 2337 | 2338 | func (c *Clipper) SwapSides(edge1, edge2 *TEdge) { 2339 | side := edge1.Side 2340 | edge1.Side = edge2.Side 2341 | edge2.Side = side 2342 | } 2343 | 2344 | //------------------------------------------------------------------------------ 2345 | 2346 | func (c *Clipper) SwapPolyIndexes(edge1, edge2 *TEdge) { 2347 | outIdx := edge1.OutIdx 2348 | edge1.OutIdx = edge2.OutIdx 2349 | edge2.OutIdx = outIdx 2350 | } 2351 | 2352 | //------------------------------------------------------------------------------ 2353 | 2354 | // default protect=false 2355 | func (c *Clipper) IntersectEdges(e1, e2 *TEdge, pt *IntPoint, protect bool) { 2356 | //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before 2357 | //e2 in AEL except when e1 is being inserted at the intersection point ... 2358 | 2359 | e1stops := !protect && e1.NextInLML == nil && 2360 | e1.Top.X == pt.X && e1.Top.Y == pt.Y 2361 | e2stops := !protect && e2.NextInLML == nil && 2362 | e2.Top.X == pt.X && e2.Top.Y == pt.Y 2363 | e1Contributing := (e1.OutIdx >= 0) 2364 | e2Contributing := (e2.OutIdx >= 0) 2365 | 2366 | c.SetZ(pt, e1, e2) 2367 | 2368 | //#if use_lines 2369 | //if either edge is on an OPEN path ... 2370 | if e1.WindDelta == 0 || e2.WindDelta == 0 { 2371 | //ignore subject-subject open path intersections UNLESS they 2372 | //are both open paths, AND they are both 'contributing maximas' ... 2373 | if e1.WindDelta == 0 && e2.WindDelta == 0 { 2374 | if (e1stops || e2stops) && e1Contributing && e2Contributing { 2375 | c.AddLocalMaxPoly(e1, e2, pt) 2376 | } 2377 | //if intersecting a subj line with a subj poly ... 2378 | } else if e1.PolyTyp == e2.PolyTyp && 2379 | e1.WindDelta != e2.WindDelta && c.m_ClipType == CtUnion { 2380 | if e1.WindDelta == 0 { 2381 | if e2Contributing { 2382 | c.AddOutPt(e1, pt) 2383 | if e1Contributing { 2384 | e1.OutIdx = Unassigned 2385 | } 2386 | } 2387 | } else { 2388 | if e1Contributing { 2389 | c.AddOutPt(e2, pt) 2390 | if e2Contributing { 2391 | e2.OutIdx = Unassigned 2392 | } 2393 | } 2394 | } 2395 | } else if e1.PolyTyp != e2.PolyTyp { 2396 | if (e1.WindDelta == 0) && intAbs(e2.WindCnt) == 1 && 2397 | (c.m_ClipType != CtUnion || e2.WindCnt2 == 0) { 2398 | c.AddOutPt(e1, pt) 2399 | if e1Contributing { 2400 | e1.OutIdx = Unassigned 2401 | } 2402 | } else if (e2.WindDelta == 0) && (intAbs(e1.WindCnt) == 1) && 2403 | (c.m_ClipType != CtUnion || e1.WindCnt2 == 0) { 2404 | c.AddOutPt(e2, pt) 2405 | if e2Contributing { 2406 | e2.OutIdx = Unassigned 2407 | } 2408 | } 2409 | } 2410 | 2411 | if e1stops { 2412 | if e1.OutIdx < 0 { 2413 | c.DeleteFromAEL(e1) 2414 | } else { 2415 | panic(NewClipperException("Error intersecting polylines")) 2416 | } 2417 | } 2418 | if e2stops { 2419 | if e2.OutIdx < 0 { 2420 | c.DeleteFromAEL(e2) 2421 | } else { 2422 | panic(NewClipperException("Error intersecting polylines")) 2423 | } 2424 | } 2425 | return 2426 | } 2427 | //#endif 2428 | 2429 | //update winding counts... 2430 | //assumes that e1 will be to the Right of e2 ABOVE the intersection 2431 | if e1.PolyTyp == e2.PolyTyp { 2432 | if c.IsEvenOddFillType(e1) { 2433 | oldE1WindCnt := e1.WindCnt 2434 | e1.WindCnt = e2.WindCnt 2435 | e2.WindCnt = oldE1WindCnt 2436 | } else { 2437 | if e1.WindCnt+e2.WindDelta == 0 { 2438 | e1.WindCnt = -e1.WindCnt 2439 | } else { 2440 | e1.WindCnt += e2.WindDelta 2441 | } 2442 | if e2.WindCnt-e1.WindDelta == 0 { 2443 | e2.WindCnt = -e2.WindCnt 2444 | } else { 2445 | e2.WindCnt -= e1.WindDelta 2446 | } 2447 | } 2448 | } else { 2449 | if !c.IsEvenOddFillType(e2) { 2450 | e1.WindCnt2 += e2.WindDelta 2451 | } else { 2452 | if e1.WindCnt2 == 0 { 2453 | e1.WindCnt2 = 1 2454 | } else { 2455 | e1.WindCnt2 = 0 2456 | } 2457 | } 2458 | if !c.IsEvenOddFillType(e1) { 2459 | e2.WindCnt2 -= e1.WindDelta 2460 | } else { 2461 | if e2.WindCnt2 == 0 { 2462 | e2.WindCnt2 = 1 2463 | } else { 2464 | e2.WindCnt2 = 0 2465 | } 2466 | } 2467 | } 2468 | 2469 | var e1FillType, e2FillType, e1FillType2, e2FillType2 PolyFillType 2470 | if e1.PolyTyp == PtSubject { 2471 | e1FillType = c.m_SubjFillType 2472 | e1FillType2 = c.m_ClipFillType 2473 | } else { 2474 | e1FillType = c.m_ClipFillType 2475 | e1FillType2 = c.m_SubjFillType 2476 | } 2477 | if e2.PolyTyp == PtSubject { 2478 | e2FillType = c.m_SubjFillType 2479 | e2FillType2 = c.m_ClipFillType 2480 | } else { 2481 | e2FillType = c.m_ClipFillType 2482 | e2FillType2 = c.m_SubjFillType 2483 | } 2484 | 2485 | var e1Wc, e2Wc int 2486 | switch e1FillType { 2487 | case PftPositive: 2488 | e1Wc = e1.WindCnt 2489 | break 2490 | case PftNegative: 2491 | e1Wc = -e1.WindCnt 2492 | break 2493 | default: 2494 | e1Wc = intAbs(e1.WindCnt) 2495 | break 2496 | } 2497 | switch e2FillType { 2498 | case PftPositive: 2499 | e2Wc = e2.WindCnt 2500 | break 2501 | case PftNegative: 2502 | e2Wc = -e2.WindCnt 2503 | break 2504 | default: 2505 | e2Wc = intAbs(e2.WindCnt) 2506 | break 2507 | } 2508 | 2509 | if e1Contributing && e2Contributing { 2510 | if e1stops || e2stops || 2511 | (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || 2512 | (e1.PolyTyp != e2.PolyTyp && c.m_ClipType != CtXor) { 2513 | c.AddLocalMaxPoly(e1, e2, pt) 2514 | } else { 2515 | c.AddOutPt(e1, pt) 2516 | c.AddOutPt(e2, pt) 2517 | c.SwapSides(e1, e2) 2518 | c.SwapPolyIndexes(e1, e2) 2519 | } 2520 | } else if e1Contributing { 2521 | if e2Wc == 0 || e2Wc == 1 { 2522 | c.AddOutPt(e1, pt) 2523 | c.SwapSides(e1, e2) 2524 | c.SwapPolyIndexes(e1, e2) 2525 | } 2526 | 2527 | } else if e2Contributing { 2528 | if e1Wc == 0 || e1Wc == 1 { 2529 | c.AddOutPt(e2, pt) 2530 | c.SwapSides(e1, e2) 2531 | c.SwapPolyIndexes(e1, e2) 2532 | } 2533 | } else if (e1Wc == 0 || e1Wc == 1) && 2534 | (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops { 2535 | //neither edge is currently contributing ... 2536 | var e1Wc2, e2Wc2 int 2537 | switch e1FillType2 { 2538 | case PftPositive: 2539 | e1Wc2 = e1.WindCnt2 2540 | break 2541 | case PftNegative: 2542 | e1Wc2 = -e1.WindCnt2 2543 | break 2544 | default: 2545 | e1Wc2 = intAbs(e1.WindCnt2) 2546 | break 2547 | } 2548 | switch e2FillType2 { 2549 | case PftPositive: 2550 | e2Wc2 = e2.WindCnt2 2551 | break 2552 | case PftNegative: 2553 | e2Wc2 = -e2.WindCnt2 2554 | break 2555 | default: 2556 | e2Wc2 = intAbs(e2.WindCnt2) 2557 | break 2558 | } 2559 | 2560 | if e1.PolyTyp != e2.PolyTyp { 2561 | c.AddLocalMinPoly(e1, e2, pt) 2562 | } else if e1Wc == 1 && e2Wc == 1 { 2563 | switch c.m_ClipType { 2564 | case CtIntersection: 2565 | if e1Wc2 > 0 && e2Wc2 > 0 { 2566 | c.AddLocalMinPoly(e1, e2, pt) 2567 | break 2568 | } 2569 | case CtUnion: 2570 | if e1Wc2 <= 0 && e2Wc2 <= 0 { 2571 | c.AddLocalMinPoly(e1, e2, pt) 2572 | break 2573 | } 2574 | case CtDifference: 2575 | if ((e1.PolyTyp == PtClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || 2576 | ((e1.PolyTyp == PtSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)) { 2577 | c.AddLocalMinPoly(e1, e2, pt) 2578 | break 2579 | } 2580 | case CtXor: 2581 | c.AddLocalMinPoly(e1, e2, pt) 2582 | break 2583 | } 2584 | } else { 2585 | c.SwapSides(e1, e2) 2586 | } 2587 | } 2588 | 2589 | if (e1stops != e2stops) && 2590 | ((e1stops && (e1.OutIdx >= 0)) || (e2stops && (e2.OutIdx >= 0))) { 2591 | c.SwapSides(e1, e2) 2592 | c.SwapPolyIndexes(e1, e2) 2593 | } 2594 | 2595 | //finally, delete any non-contributing maxima edges ... 2596 | if e1stops { 2597 | c.DeleteFromAEL(e1) 2598 | } 2599 | if e2stops { 2600 | c.DeleteFromAEL(e2) 2601 | } 2602 | } 2603 | 2604 | //------------------------------------------------------------------------------ 2605 | 2606 | func (c *Clipper) DeleteFromAEL(e *TEdge) { 2607 | AelPrev := e.PrevInAEL 2608 | AelNext := e.NextInAEL 2609 | if AelPrev == nil && AelNext == nil && (e != c.m_ActiveEdges) { 2610 | return //already deleted 2611 | } 2612 | if AelPrev != nil { 2613 | AelPrev.NextInAEL = AelNext 2614 | } else { 2615 | c.m_ActiveEdges = AelNext 2616 | } 2617 | if AelNext != nil { 2618 | AelNext.PrevInAEL = AelPrev 2619 | } 2620 | e.NextInAEL = nil 2621 | e.PrevInAEL = nil 2622 | } 2623 | 2624 | //------------------------------------------------------------------------------ 2625 | 2626 | func (c *Clipper) DeleteFromSEL(e *TEdge) { 2627 | SelPrev := e.PrevInSEL 2628 | SelNext := e.NextInSEL 2629 | if SelPrev == nil && SelNext == nil && (e != c.m_SortedEdges) { 2630 | return //already deleted 2631 | } 2632 | if SelPrev != nil { 2633 | SelPrev.NextInSEL = SelNext 2634 | } else { 2635 | c.m_SortedEdges = SelNext 2636 | } 2637 | if SelNext != nil { 2638 | SelNext.PrevInSEL = SelPrev 2639 | } 2640 | e.NextInSEL = nil 2641 | e.PrevInSEL = nil 2642 | } 2643 | 2644 | //------------------------------------------------------------------------------ 2645 | 2646 | func (c *Clipper) UpdateEdgeIntoAEL(e *TEdge) *TEdge { 2647 | if e.NextInLML == nil { 2648 | panic(NewClipperException("UpdateEdgeIntoAEL: invalid call")) 2649 | } 2650 | AelPrev := e.PrevInAEL 2651 | AelNext := e.NextInAEL 2652 | e.NextInLML.OutIdx = e.OutIdx 2653 | if AelPrev != nil { 2654 | AelPrev.NextInAEL = e.NextInLML 2655 | } else { 2656 | c.m_ActiveEdges = e.NextInLML 2657 | } 2658 | if AelNext != nil { 2659 | AelNext.PrevInAEL = e.NextInLML 2660 | } 2661 | e.NextInLML.Side = e.Side 2662 | e.NextInLML.WindDelta = e.WindDelta 2663 | e.NextInLML.WindCnt = e.WindCnt 2664 | e.NextInLML.WindCnt2 = e.WindCnt2 2665 | e = e.NextInLML 2666 | e.Curr = e.Bot 2667 | e.PrevInAEL = AelPrev 2668 | e.NextInAEL = AelNext 2669 | if !c.IsHorizontal(e) { 2670 | c.InsertScanbeam(e.Top.Y) 2671 | } 2672 | return e 2673 | } 2674 | 2675 | //------------------------------------------------------------------------------ 2676 | 2677 | func (c *Clipper) ProcessHorizontals(isTopOfScanbeam bool) { 2678 | horzEdge := c.m_SortedEdges 2679 | for horzEdge != nil { 2680 | c.DeleteFromSEL(horzEdge) 2681 | c.ProcessHorizontal(horzEdge, isTopOfScanbeam) 2682 | horzEdge = c.m_SortedEdges 2683 | } 2684 | } 2685 | 2686 | //------------------------------------------------------------------------------ 2687 | 2688 | func (c *Clipper) GetHorzDirection(HorzEdge *TEdge, Dir *Direction, Left, Right *CInt) { 2689 | if HorzEdge.Bot.X < HorzEdge.Top.X { 2690 | *Left = HorzEdge.Bot.X 2691 | *Right = HorzEdge.Top.X 2692 | *Dir = DLeftToRight 2693 | } else { 2694 | *Left = HorzEdge.Top.X 2695 | *Right = HorzEdge.Bot.X 2696 | *Dir = DRightToLeft 2697 | } 2698 | } 2699 | 2700 | //------------------------------------------------------------------------ 2701 | 2702 | func (c *Clipper) ProcessHorizontal(horzEdge *TEdge, isTopOfScanbeam bool) { 2703 | var dir Direction 2704 | var horzLeft, horzRight CInt 2705 | 2706 | c.GetHorzDirection(horzEdge, &dir, &horzLeft, &horzRight) 2707 | 2708 | eLastHorz := horzEdge 2709 | var eMaxPair *TEdge 2710 | for eLastHorz.NextInLML != nil && c.IsHorizontal(eLastHorz.NextInLML) { 2711 | eLastHorz = eLastHorz.NextInLML 2712 | } 2713 | if eLastHorz.NextInLML == nil { 2714 | eMaxPair = c.GetMaximaPair(eLastHorz) 2715 | } 2716 | 2717 | for { 2718 | IsLastHorz := (horzEdge == eLastHorz) 2719 | e := c.GetNextInAEL(horzEdge, dir) 2720 | for e != nil { 2721 | //Break if we've got to the end of an intermediate horizontal edge ... 2722 | //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. 2723 | if e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != nil && 2724 | e.Dx < horzEdge.NextInLML.Dx { 2725 | break 2726 | } 2727 | 2728 | eNext := c.GetNextInAEL(e, dir) //saves eNext for later 2729 | 2730 | if (dir == DLeftToRight && e.Curr.X <= horzRight) || 2731 | (dir == DRightToLeft && e.Curr.X >= horzLeft) { 2732 | //so far we're still in range of the horizontal Edge but make sure 2733 | //we're at the last of consec. horizontals when matching with eMaxPair 2734 | if e == eMaxPair && IsLastHorz { 2735 | if horzEdge.OutIdx >= 0 { 2736 | op1 := c.AddOutPt(horzEdge, &horzEdge.Top) 2737 | eNextHorz := c.m_SortedEdges 2738 | for eNextHorz != nil { 2739 | if eNextHorz.OutIdx >= 0 && 2740 | c.HorzSegmentsOverlap(horzEdge.Bot.X, 2741 | horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X) { 2742 | op2 := c.AddOutPt(eNextHorz, &eNextHorz.Bot) 2743 | c.AddJoin(op2, op1, &eNextHorz.Top) 2744 | } 2745 | eNextHorz = eNextHorz.NextInSEL 2746 | } 2747 | c.AddGhostJoin(op1, &horzEdge.Bot) 2748 | c.AddLocalMaxPoly(horzEdge, eMaxPair, &horzEdge.Top) 2749 | } 2750 | c.DeleteFromAEL(horzEdge) 2751 | c.DeleteFromAEL(eMaxPair) 2752 | return 2753 | } else if dir == DLeftToRight { 2754 | Pt := &IntPoint{e.Curr.X, horzEdge.Curr.Y} 2755 | c.IntersectEdges(horzEdge, e, Pt, true) 2756 | } else { 2757 | Pt := &IntPoint{e.Curr.X, horzEdge.Curr.Y} 2758 | c.IntersectEdges(e, horzEdge, Pt, true) 2759 | } 2760 | c.SwapPositionsInAEL(horzEdge, e) 2761 | } else if (dir == DLeftToRight && e.Curr.X >= horzRight) || 2762 | (dir == DRightToLeft && e.Curr.X <= horzLeft) { 2763 | break 2764 | } 2765 | e = eNext 2766 | } 2767 | 2768 | if horzEdge.NextInLML != nil && c.IsHorizontal(horzEdge.NextInLML) { 2769 | horzEdge = c.UpdateEdgeIntoAEL(horzEdge) 2770 | if horzEdge.OutIdx >= 0 { 2771 | c.AddOutPt(horzEdge, &horzEdge.Bot) 2772 | } 2773 | c.GetHorzDirection(horzEdge, &dir, &horzLeft, &horzRight) 2774 | } else { 2775 | break 2776 | } 2777 | } 2778 | 2779 | if horzEdge.NextInLML != nil { 2780 | if horzEdge.OutIdx >= 0 { 2781 | op1 := c.AddOutPt(horzEdge, &horzEdge.Top) 2782 | if isTopOfScanbeam { 2783 | c.AddGhostJoin(op1, &horzEdge.Bot) 2784 | } 2785 | 2786 | horzEdge = c.UpdateEdgeIntoAEL(horzEdge) 2787 | if horzEdge.WindDelta == 0 { 2788 | return 2789 | } 2790 | //nb: HorzEdge is no longer horizontal here 2791 | ePrev := horzEdge.PrevInAEL 2792 | eNext := horzEdge.NextInAEL 2793 | if ePrev != nil && ePrev.Curr.X == horzEdge.Bot.X && 2794 | ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 && 2795 | (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && 2796 | c.SlopesEqual(horzEdge, ePrev, c.m_UseFullRange)) { 2797 | op2 := c.AddOutPt(ePrev, &horzEdge.Bot) 2798 | c.AddJoin(op1, op2, &horzEdge.Top) 2799 | } else if eNext != nil && eNext.Curr.X == horzEdge.Bot.X && 2800 | eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 && 2801 | eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && 2802 | c.SlopesEqual(horzEdge, eNext, c.m_UseFullRange) { 2803 | op2 := c.AddOutPt(eNext, &horzEdge.Bot) 2804 | c.AddJoin(op1, op2, &horzEdge.Top) 2805 | } 2806 | } else { 2807 | horzEdge = c.UpdateEdgeIntoAEL(horzEdge) 2808 | } 2809 | } else { 2810 | if horzEdge.OutIdx >= 0 { 2811 | c.AddOutPt(horzEdge, &horzEdge.Top) 2812 | } 2813 | c.DeleteFromAEL(horzEdge) 2814 | } 2815 | } 2816 | 2817 | //------------------------------------------------------------------------------ 2818 | 2819 | func (c *Clipper) GetNextInAEL(e *TEdge, direction Direction) *TEdge { 2820 | if direction == DLeftToRight { 2821 | return e.NextInAEL 2822 | } else { 2823 | return e.PrevInAEL 2824 | } 2825 | } 2826 | 2827 | //------------------------------------------------------------------------------ 2828 | 2829 | func (c *Clipper) IsMinima(e *TEdge) bool { 2830 | return e != nil && (e.Prev.NextInLML != e) && (e.Next.NextInLML != e) 2831 | } 2832 | 2833 | //------------------------------------------------------------------------------ 2834 | 2835 | func (c *Clipper) IsMaxima(e *TEdge, Y CInt) bool { 2836 | return (e != nil && e.Top.Y == Y && e.NextInLML == nil) 2837 | } 2838 | 2839 | //------------------------------------------------------------------------------ 2840 | 2841 | func (c *Clipper) IsIntermediate(e *TEdge, Y CInt) bool { 2842 | return (e.Top.Y == Y && e.NextInLML != nil) 2843 | } 2844 | 2845 | //------------------------------------------------------------------------------ 2846 | 2847 | func (c *Clipper) GetMaximaPair(e *TEdge) *TEdge { 2848 | var result *TEdge 2849 | if e.Next.Top == e.Top && e.Next.NextInLML == nil { 2850 | result = e.Next 2851 | } else if e.Prev.Top == e.Top && e.Prev.NextInLML == nil { 2852 | result = e.Prev 2853 | } 2854 | if result != nil && (result.OutIdx == Skip || 2855 | (result.NextInAEL == result.PrevInAEL && !c.IsHorizontal(result))) { 2856 | return nil 2857 | } 2858 | return result 2859 | } 2860 | 2861 | //------------------------------------------------------------------------------ 2862 | 2863 | func (c *Clipper) ProcessIntersections(topY CInt) bool { 2864 | if c.m_ActiveEdges == nil { 2865 | return true 2866 | } 2867 | // defer func() { 2868 | // if r := recover(); r != nil { 2869 | // c.m_SortedEdges = nil 2870 | // c.m_IntersectList = make([]*IntersectNode, 0) 2871 | // panic(NewClipperException("ProcessIntersections error: " + 2872 | // r.(error).Error())) 2873 | // } 2874 | // }() 2875 | c.BuildIntersectList(topY) 2876 | if len(c.m_IntersectList) == 0 { 2877 | return true 2878 | } 2879 | if len(c.m_IntersectList) == 1 || c.FixupIntersectionOrder() { 2880 | c.ProcessIntersectList() 2881 | } else { 2882 | return false 2883 | } 2884 | 2885 | c.m_SortedEdges = nil 2886 | return true 2887 | } 2888 | 2889 | //------------------------------------------------------------------------------ 2890 | 2891 | func (c *Clipper) BuildIntersectList(topY CInt) { 2892 | if c.m_ActiveEdges == nil { 2893 | return 2894 | } 2895 | 2896 | //prepare for sorting ... 2897 | e := c.m_ActiveEdges 2898 | c.m_SortedEdges = e 2899 | for e != nil { 2900 | e.PrevInSEL = e.PrevInAEL 2901 | e.NextInSEL = e.NextInAEL 2902 | e.Curr.X = TopX(e, &topY) 2903 | e = e.NextInAEL 2904 | } 2905 | 2906 | //bubblesort ... 2907 | isModified := true 2908 | for isModified && c.m_SortedEdges != nil { 2909 | isModified = false 2910 | e = c.m_SortedEdges 2911 | for e.NextInSEL != nil { 2912 | eNext := e.NextInSEL 2913 | 2914 | if e.Curr.X > eNext.Curr.X { 2915 | newNode := new(IntersectNode) 2916 | newNode.Edge1 = e 2917 | newNode.Edge2 = eNext 2918 | newNode.Pt = c.IntersectPoint(e, eNext) 2919 | c.m_IntersectList = append(c.m_IntersectList, newNode) 2920 | 2921 | c.SwapPositionsInSEL(e, eNext) 2922 | isModified = true 2923 | } else { 2924 | e = eNext 2925 | } 2926 | } 2927 | if e.PrevInSEL != nil { 2928 | e.PrevInSEL.NextInSEL = nil 2929 | } else { 2930 | break 2931 | } 2932 | } 2933 | // fmt.Println("IntersectList", c.m_IntersectList) 2934 | c.m_SortedEdges = nil 2935 | } 2936 | 2937 | //------------------------------------------------------------------------------ 2938 | 2939 | func (c *Clipper) EdgesAdjacent(inode *IntersectNode) bool { 2940 | return (inode.Edge1.NextInSEL == inode.Edge2) || 2941 | (inode.Edge1.PrevInSEL == inode.Edge2) 2942 | } 2943 | 2944 | //------------------------------------------------------------------------------ 2945 | 2946 | func (c *Clipper) IntersectNodeSort(node1, node2 *IntersectNode) int { 2947 | //the following typecast is safe because the differences in Pt.Y will 2948 | //be limited to the height of the scanbeam. 2949 | return int(node2.Pt.Y - node1.Pt.Y) 2950 | } 2951 | 2952 | //------------------------------------------------------------------------------ 2953 | 2954 | func (c *Clipper) FixupIntersectionOrder() bool { 2955 | //pre-condition: intersections are sorted bottom-most first. 2956 | //Now it's crucial that intersections are made only between adjacent edges, 2957 | //so to ensure this the order of intersections may need adjusting ... 2958 | sort.Sort(IntersectNodeList(c.m_IntersectList)) 2959 | 2960 | c.CopyAELToSEL() 2961 | cnt := len(c.m_IntersectList) 2962 | for i := 0; i < cnt; i++ { 2963 | if !c.EdgesAdjacent(c.m_IntersectList[i]) { 2964 | j := i + 1 2965 | for j < cnt && !c.EdgesAdjacent(c.m_IntersectList[j]) { 2966 | j++ 2967 | } 2968 | if j == cnt { 2969 | return false 2970 | } 2971 | 2972 | tmp := c.m_IntersectList[i] 2973 | c.m_IntersectList[i] = c.m_IntersectList[j] 2974 | c.m_IntersectList[j] = tmp 2975 | 2976 | } 2977 | c.SwapPositionsInSEL(c.m_IntersectList[i].Edge1, c.m_IntersectList[i].Edge2) 2978 | } 2979 | return true 2980 | } 2981 | 2982 | //------------------------------------------------------------------------------ 2983 | 2984 | func (c *Clipper) ProcessIntersectList() { 2985 | for i := 0; i < len(c.m_IntersectList); i++ { 2986 | iNode := c.m_IntersectList[i] 2987 | { 2988 | c.IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt, true) 2989 | c.SwapPositionsInAEL(iNode.Edge1, iNode.Edge2) 2990 | } 2991 | } 2992 | c.m_IntersectList = make([]*IntersectNode, 0) 2993 | } 2994 | 2995 | //------------------------------------------------------------------------------ 2996 | 2997 | func Round(value float64) CInt { 2998 | if value < 0 { 2999 | return CInt(value - 0.5) 3000 | } else { 3001 | return CInt(value + 0.5) 3002 | } 3003 | } 3004 | 3005 | //------------------------------------------------------------------------------ 3006 | 3007 | func TopX(edge *TEdge, currentY *CInt) CInt { 3008 | if *currentY == edge.Top.Y { 3009 | return edge.Top.X 3010 | } 3011 | // fmt.Println("TopX", edge, "currentY", *currentY) 3012 | return edge.Bot.X + Round(edge.Dx*float64(*currentY-edge.Bot.Y)) 3013 | } 3014 | 3015 | //------------------------------------------------------------------------------ 3016 | 3017 | func (c *Clipper) IntersectPoint(edge1, edge2 *TEdge) (ip *IntPoint) { 3018 | ip = new(IntPoint) 3019 | var b1, b2 float64 3020 | //nb: with very large coordinate values, it's possible for SlopesEqual() to 3021 | //return false but for the edge.Dx value be equal due to float64 precision rounding. 3022 | if edge1.Dx == edge2.Dx { 3023 | ip.Y = edge1.Curr.Y 3024 | ip.X = TopX(edge1, &ip.Y) 3025 | return 3026 | } 3027 | 3028 | if edge1.Delta.X == 0 { 3029 | ip.X = edge1.Bot.X 3030 | if c.IsHorizontal(edge2) { 3031 | ip.Y = edge2.Bot.Y 3032 | } else { 3033 | b2 = float64(edge2.Bot.Y) - (float64(edge2.Bot.X) / edge2.Dx) 3034 | ip.Y = Round(float64(ip.X)/edge2.Dx + b2) 3035 | } 3036 | } else if edge2.Delta.X == 0 { 3037 | ip.X = edge2.Bot.X 3038 | if c.IsHorizontal(edge1) { 3039 | ip.Y = edge1.Bot.Y 3040 | } else { 3041 | b1 = float64(edge1.Bot.Y) - (float64(edge1.Bot.X) / edge1.Dx) 3042 | ip.Y = Round(float64(ip.X)/edge1.Dx + b1) 3043 | } 3044 | } else { 3045 | b1 = float64(edge1.Bot.X) - float64(edge1.Bot.Y)*edge1.Dx 3046 | b2 = float64(edge2.Bot.X) - float64(edge2.Bot.Y)*edge2.Dx 3047 | q := (b2 - b1) / (edge1.Dx - edge2.Dx) 3048 | ip.Y = Round(q) 3049 | if math.Abs(edge1.Dx) < math.Abs(edge2.Dx) { 3050 | ip.X = Round(edge1.Dx*q + b1) 3051 | } else { 3052 | ip.X = Round(edge2.Dx*q + b2) 3053 | } 3054 | } 3055 | 3056 | if ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y { 3057 | if edge1.Top.Y > edge2.Top.Y { 3058 | ip.Y = edge1.Top.Y 3059 | } else { 3060 | ip.Y = edge2.Top.Y 3061 | } 3062 | if math.Abs(edge1.Dx) < math.Abs(edge2.Dx) { 3063 | ip.X = TopX(edge1, &ip.Y) 3064 | } else { 3065 | ip.X = TopX(edge2, &ip.Y) 3066 | } 3067 | } 3068 | //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... 3069 | if ip.Y > edge1.Curr.Y { 3070 | ip.Y = edge1.Curr.Y 3071 | //better to use the more vertical edge to derive X ... 3072 | if math.Abs(edge1.Dx) > math.Abs(edge2.Dx) { 3073 | ip.X = TopX(edge2, &ip.Y) 3074 | } else { 3075 | ip.X = TopX(edge1, &ip.Y) 3076 | } 3077 | } 3078 | return 3079 | } 3080 | 3081 | //------------------------------------------------------------------------------ 3082 | 3083 | func (c *Clipper) ProcessEdgesAtTopOfScanbeam(topY CInt) { 3084 | e := c.m_ActiveEdges 3085 | for e != nil { 3086 | //1. process maxima, treating them as if they're 'bent' horizontal edges, 3087 | // but exclude maxima with horizontal edges. nb: e can't be a horizontal. 3088 | IsMaximaEdge := c.IsMaxima(e, topY) 3089 | 3090 | if IsMaximaEdge { 3091 | eMaxPair := c.GetMaximaPair(e) 3092 | IsMaximaEdge = (eMaxPair == nil || !c.IsHorizontal(eMaxPair)) 3093 | } 3094 | 3095 | if IsMaximaEdge { 3096 | ePrev := e.PrevInAEL 3097 | c.DoMaxima(e) 3098 | if ePrev == nil { 3099 | e = c.m_ActiveEdges 3100 | } else { 3101 | e = ePrev.NextInAEL 3102 | } 3103 | } else { 3104 | //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... 3105 | if c.IsIntermediate(e, topY) && c.IsHorizontal(e.NextInLML) { 3106 | e = c.UpdateEdgeIntoAEL(e) 3107 | if e.OutIdx >= 0 { 3108 | c.AddOutPt(e, &e.Bot) 3109 | } 3110 | c.AddEdgeToSEL(e) 3111 | } else { 3112 | e.Curr.X = TopX(e, &topY) 3113 | e.Curr.Y = topY 3114 | } 3115 | 3116 | if c.StrictlySimple { 3117 | ePrev := e.PrevInAEL 3118 | if (e.OutIdx >= 0) && (e.WindDelta != 0) && ePrev != nil && 3119 | (ePrev.OutIdx >= 0) && (ePrev.Curr.X == e.Curr.X) && 3120 | (ePrev.WindDelta != 0) { 3121 | ip := e.Curr.Copy() 3122 | c.SetZ(&ip, ePrev, e) 3123 | op := c.AddOutPt(ePrev, &ip) 3124 | op2 := c.AddOutPt(e, &ip) 3125 | c.AddJoin(op, op2, &ip) //StrictlySimple (type-3) join 3126 | } 3127 | } 3128 | 3129 | e = e.NextInAEL 3130 | } 3131 | } 3132 | 3133 | //3. Process horizontals at the Top of the scanbeam ... 3134 | c.ProcessHorizontals(true) 3135 | 3136 | //4. Promote intermediate vertices ... 3137 | e = c.m_ActiveEdges 3138 | for e != nil { 3139 | if c.IsIntermediate(e, topY) { 3140 | var op *OutPt 3141 | if e.OutIdx >= 0 { 3142 | op = c.AddOutPt(e, &e.Top) 3143 | } 3144 | e = c.UpdateEdgeIntoAEL(e) 3145 | 3146 | //if output polygons share an edge, they'll need joining later ... 3147 | ePrev := e.PrevInAEL 3148 | eNext := e.NextInAEL 3149 | if ePrev != nil && ePrev.Curr.X == e.Bot.X && 3150 | ePrev.Curr.Y == e.Bot.Y && op != nil && 3151 | ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && 3152 | c.SlopesEqual(e, ePrev, c.m_UseFullRange) && 3153 | (e.WindDelta != 0) && (ePrev.WindDelta != 0) { 3154 | op2 := c.AddOutPt(ePrev, &e.Bot) 3155 | c.AddJoin(op, op2, &e.Top) 3156 | } else if eNext != nil && eNext.Curr.X == e.Bot.X && 3157 | eNext.Curr.Y == e.Bot.Y && op != nil && 3158 | eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && 3159 | c.SlopesEqual(e, eNext, c.m_UseFullRange) && 3160 | (e.WindDelta != 0) && (eNext.WindDelta != 0) { 3161 | op2 := c.AddOutPt(eNext, &e.Bot) 3162 | c.AddJoin(op, op2, &e.Top) 3163 | } 3164 | } 3165 | e = e.NextInAEL 3166 | } 3167 | } 3168 | 3169 | //------------------------------------------------------------------------------ 3170 | 3171 | func (c *Clipper) DoMaxima(e *TEdge) { 3172 | eMaxPair := c.GetMaximaPair(e) 3173 | if eMaxPair == nil { 3174 | if e.OutIdx >= 0 { 3175 | c.AddOutPt(e, &e.Top) 3176 | } 3177 | c.DeleteFromAEL(e) 3178 | return 3179 | } 3180 | 3181 | eNext := e.NextInAEL 3182 | for eNext != nil && eNext != eMaxPair { 3183 | c.IntersectEdges(e, eNext, &e.Top, true) 3184 | c.SwapPositionsInAEL(e, eNext) 3185 | eNext = e.NextInAEL 3186 | } 3187 | 3188 | if e.OutIdx == Unassigned && eMaxPair.OutIdx == Unassigned { 3189 | c.DeleteFromAEL(e) 3190 | c.DeleteFromAEL(eMaxPair) 3191 | } else if e.OutIdx >= 0 && eMaxPair.OutIdx >= 0 { 3192 | if e.OutIdx >= 0 { 3193 | c.AddLocalMaxPoly(e, eMaxPair, &e.Top) 3194 | } 3195 | c.DeleteFromAEL(e) 3196 | c.DeleteFromAEL(eMaxPair) 3197 | //#if use_lines 3198 | } else if e.WindDelta == 0 { 3199 | if e.OutIdx >= 0 { 3200 | c.AddOutPt(e, &e.Top) 3201 | e.OutIdx = Unassigned 3202 | } 3203 | c.DeleteFromAEL(e) 3204 | 3205 | if eMaxPair.OutIdx >= 0 { 3206 | c.AddOutPt(eMaxPair, &e.Top) 3207 | eMaxPair.OutIdx = Unassigned 3208 | } 3209 | c.DeleteFromAEL(eMaxPair) 3210 | //#endif 3211 | } else { 3212 | panic(NewClipperException("DoMaxima error")) 3213 | } 3214 | } 3215 | 3216 | //------------------------------------------------------------------------------ 3217 | 3218 | func reversePath(s Path) { 3219 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 3220 | s[i], s[j] = s[j], s[i] 3221 | } 3222 | } 3223 | 3224 | func (c *Clipper) ReversePaths(polys Paths) { 3225 | for _, poly := range polys { 3226 | reversePath(poly) 3227 | } 3228 | } 3229 | 3230 | //------------------------------------------------------------------------------ 3231 | 3232 | func Orientation(poly Path) bool { 3233 | return Area(poly) >= 0 3234 | } 3235 | 3236 | //------------------------------------------------------------------------------ 3237 | 3238 | func (c *Clipper) PointCount(pts *OutPt) int { 3239 | if pts == nil { 3240 | return 0 3241 | } 3242 | result := 0 3243 | p := pts 3244 | for { 3245 | result++ 3246 | p = p.Next 3247 | if p == pts { 3248 | break 3249 | } 3250 | } 3251 | return result 3252 | } 3253 | 3254 | //------------------------------------------------------------------------------ 3255 | 3256 | func (c *Clipper) BuildResult() Paths { 3257 | polyg := Paths(make([]Path, 0, len(c.m_PolyOuts))) 3258 | for i := 0; i < len(c.m_PolyOuts); i++ { 3259 | outRec := c.m_PolyOuts[i] 3260 | if outRec.Pts == nil { 3261 | continue 3262 | } 3263 | p := outRec.Pts.Prev 3264 | cnt := c.PointCount(p) 3265 | if cnt < 2 { 3266 | continue 3267 | } 3268 | pg := Path(make([]*IntPoint, 0, cnt)) 3269 | for j := 0; j < cnt; j++ { 3270 | pg = append(pg, p.Pt) 3271 | p = p.Prev 3272 | } 3273 | polyg = append(polyg, pg) 3274 | } 3275 | return polyg 3276 | } 3277 | 3278 | //------------------------------------------------------------------------------ 3279 | 3280 | func (c *Clipper) BuildResult2(polytree *PolyTree) { 3281 | polytree.Clear() 3282 | 3283 | //add each output polygon/contour to polytree ... 3284 | polytree.m_AllPolys = make([]*PolyNode, len(c.m_PolyOuts)) 3285 | for i := 0; i < len(c.m_PolyOuts); i++ { 3286 | outRec := c.m_PolyOuts[i] 3287 | cnt := c.PointCount(outRec.Pts) 3288 | if (outRec.IsOpen && cnt < 2) || 3289 | (!outRec.IsOpen && cnt < 3) { 3290 | continue 3291 | } 3292 | c.FixHoleLinkage(outRec) 3293 | pn := new(PolyNode) 3294 | polytree.m_AllPolys[i] = pn 3295 | outRec.PolyNode = pn 3296 | pn.m_polygon = make([]*IntPoint, cnt) 3297 | op := outRec.Pts.Prev 3298 | for j := 0; j < cnt; j++ { 3299 | pn.m_polygon[j] = op.Pt 3300 | op = op.Prev 3301 | } 3302 | } 3303 | 3304 | //fixup PolyNode links etc ... 3305 | polytree.m_Childs = make([]*PolyNode, 0, len(c.m_PolyOuts)) 3306 | for i := 0; i < len(c.m_PolyOuts); i++ { 3307 | outRec := c.m_PolyOuts[i] 3308 | if outRec.PolyNode == nil { 3309 | continue 3310 | } else if outRec.IsOpen { 3311 | outRec.PolyNode.IsOpen = true 3312 | polytree.AddChild(outRec.PolyNode) 3313 | } else if outRec.FirstLeft != nil && 3314 | outRec.FirstLeft.PolyNode != nil { 3315 | outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode) 3316 | } else { 3317 | polytree.AddChild(outRec.PolyNode) 3318 | } 3319 | } 3320 | } 3321 | 3322 | //------------------------------------------------------------------------------ 3323 | 3324 | func (c *Clipper) FixupOutPolygon(outRec *OutRec) { 3325 | //FixupOutPolygon() - removes duplicate points and simplifies consecutive 3326 | //parallel edges by removing the middle vertex. 3327 | var lastOK *OutPt 3328 | outRec.BottomPt = nil 3329 | pp := outRec.Pts 3330 | for { 3331 | if pp.Prev == pp || pp.Prev == pp.Next { 3332 | outRec.Pts = nil 3333 | return 3334 | } 3335 | //test for duplicate points and collinear edges ... 3336 | if (*pp.Pt == *pp.Next.Pt) || (*pp.Pt == *pp.Prev.Pt) || 3337 | (c.SlopesEqual3(pp.Prev.Pt, pp.Pt, pp.Next.Pt, c.m_UseFullRange) && 3338 | (!c.PreserveCollinear || !c.Pt2IsBetweenPt1AndPt3(pp.Prev.Pt, pp.Pt, pp.Next.Pt))) { 3339 | lastOK = nil 3340 | pp.Prev.Next = pp.Next 3341 | pp.Next.Prev = pp.Prev 3342 | pp = pp.Prev 3343 | } else if pp == lastOK { 3344 | break 3345 | } else { 3346 | if lastOK == nil { 3347 | lastOK = pp 3348 | } 3349 | pp = pp.Next 3350 | } 3351 | } 3352 | outRec.Pts = pp 3353 | } 3354 | 3355 | //------------------------------------------------------------------------------ 3356 | 3357 | func (c *Clipper) DupOutPt(outPt *OutPt, InsertAfter bool) *OutPt { 3358 | result := new(OutPt) 3359 | result.Pt = outPt.Pt 3360 | result.Idx = outPt.Idx 3361 | if InsertAfter { 3362 | result.Next = outPt.Next 3363 | result.Prev = outPt 3364 | outPt.Next.Prev = result 3365 | outPt.Next = result 3366 | } else { 3367 | result.Prev = outPt.Prev 3368 | result.Next = outPt 3369 | outPt.Prev.Next = result 3370 | outPt.Prev = result 3371 | } 3372 | return result 3373 | } 3374 | 3375 | //------------------------------------------------------------------------------ 3376 | 3377 | func (c *Clipper) GetOverlap(a1, a2, b1, b2 CInt, Left, Right *CInt) bool { 3378 | if a1 < a2 { 3379 | if b1 < b2 { 3380 | *Left = max(a1, b1) 3381 | *Right = min(a2, b2) 3382 | } else { 3383 | *Left = max(a1, b2) 3384 | *Right = min(a2, b1) 3385 | } 3386 | } else { 3387 | if b1 < b2 { 3388 | *Left = max(a2, b1) 3389 | *Right = min(a1, b2) 3390 | } else { 3391 | *Left = max(a2, b2) 3392 | *Right = min(a1, b1) 3393 | } 3394 | } 3395 | return *Left < *Right 3396 | } 3397 | 3398 | //------------------------------------------------------------------------------ 3399 | 3400 | func (c *Clipper) JoinHorz(op1, op1b, op2, op2b *OutPt, 3401 | Pt *IntPoint, DiscardLeft bool) bool { 3402 | var Dir1 Direction 3403 | if op1.Pt.X > op1b.Pt.X { 3404 | Dir1 = DRightToLeft 3405 | } else { 3406 | Dir1 = DLeftToRight 3407 | } 3408 | var Dir2 Direction 3409 | if op2.Pt.X > op2b.Pt.X { 3410 | Dir2 = DRightToLeft 3411 | } else { 3412 | Dir2 = DLeftToRight 3413 | } 3414 | if Dir1 == Dir2 { 3415 | return false 3416 | } 3417 | 3418 | //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we 3419 | //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) 3420 | //So, to facilitate this while inserting Op1b and Op2b ... 3421 | //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, 3422 | //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) 3423 | if Dir1 == DLeftToRight { 3424 | for op1.Next.Pt.X <= Pt.X && 3425 | op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == Pt.Y { 3426 | op1 = op1.Next 3427 | } 3428 | if DiscardLeft && (op1.Pt.X != Pt.X) { 3429 | op1 = op1.Next 3430 | } 3431 | op1b = c.DupOutPt(op1, !DiscardLeft) 3432 | if *op1b.Pt != *Pt { 3433 | op1 = op1b 3434 | op1.Pt = Pt 3435 | op1b = c.DupOutPt(op1, !DiscardLeft) 3436 | } 3437 | } else { 3438 | for op1.Next.Pt.X >= Pt.X && 3439 | op1.Next.Pt.X <= op1.Pt.X && op1.Next.Pt.Y == Pt.Y { 3440 | op1 = op1.Next 3441 | } 3442 | if !DiscardLeft && (op1.Pt.X != Pt.X) { 3443 | op1 = op1.Next 3444 | } 3445 | op1b = c.DupOutPt(op1, DiscardLeft) 3446 | if *op1b.Pt != *Pt { 3447 | op1 = op1b 3448 | op1.Pt = Pt 3449 | op1b = c.DupOutPt(op1, DiscardLeft) 3450 | } 3451 | } 3452 | 3453 | if Dir2 == DLeftToRight { 3454 | for op2.Next.Pt.X <= Pt.X && 3455 | op2.Next.Pt.X >= op2.Pt.X && op2.Next.Pt.Y == Pt.Y { 3456 | op2 = op2.Next 3457 | } 3458 | if DiscardLeft && (op2.Pt.X != Pt.X) { 3459 | op2 = op2.Next 3460 | } 3461 | op2b = c.DupOutPt(op2, !DiscardLeft) 3462 | if *op2b.Pt != *Pt { 3463 | op2 = op2b 3464 | op2.Pt = Pt 3465 | op2b = c.DupOutPt(op2, !DiscardLeft) 3466 | } 3467 | } else { 3468 | for op2.Next.Pt.X >= Pt.X && 3469 | op2.Next.Pt.X <= op2.Pt.X && op2.Next.Pt.Y == Pt.Y { 3470 | op2 = op2.Next 3471 | } 3472 | if !DiscardLeft && (op2.Pt.X != Pt.X) { 3473 | op2 = op2.Next 3474 | } 3475 | op2b = c.DupOutPt(op2, DiscardLeft) 3476 | if *op2b.Pt != *Pt { 3477 | op2 = op2b 3478 | op2.Pt = Pt 3479 | op2b = c.DupOutPt(op2, DiscardLeft) 3480 | } 3481 | } 3482 | 3483 | if (Dir1 == DLeftToRight) == DiscardLeft { 3484 | op1.Prev = op2 3485 | op2.Next = op1 3486 | op1b.Next = op2b 3487 | op2b.Prev = op1b 3488 | } else { 3489 | op1.Next = op2 3490 | op2.Prev = op1 3491 | op1b.Prev = op2b 3492 | op2b.Next = op1b 3493 | } 3494 | return true 3495 | } 3496 | 3497 | //------------------------------------------------------------------------------ 3498 | 3499 | func (c *Clipper) JoinPoints(j *Join, outRec1, outRec2 *OutRec) bool { 3500 | op1 := j.OutPt1 3501 | op2 := j.OutPt2 3502 | var op1b, op2b *OutPt 3503 | 3504 | //There are 3 kinds of joins for output polygons ... 3505 | //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere 3506 | //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). 3507 | //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same 3508 | //location at the Bottom of the overlapping segment (& Join.OffPt is above). 3509 | //3. StrictlySimple joins where edges touch but are not collinear and where 3510 | //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. 3511 | isHorizontal := (j.OutPt1.Pt.Y == j.OffPt.Y) 3512 | 3513 | if isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt) { 3514 | //Strictly Simple join ... 3515 | if outRec1 != outRec2 { 3516 | return false 3517 | } 3518 | op1b = j.OutPt1.Next 3519 | for op1b != op1 && (*op1b.Pt == *j.OffPt) { 3520 | op1b = op1b.Next 3521 | } 3522 | reverse1 := (op1b.Pt.Y > j.OffPt.Y) 3523 | op2b = j.OutPt2.Next 3524 | for op2b != op2 && (*op2b.Pt == *j.OffPt) { 3525 | op2b = op2b.Next 3526 | } 3527 | reverse2 := (op2b.Pt.Y > j.OffPt.Y) 3528 | if reverse1 == reverse2 { 3529 | return false 3530 | } 3531 | if reverse1 { 3532 | op1b = c.DupOutPt(op1, false) 3533 | op2b = c.DupOutPt(op2, true) 3534 | op1.Prev = op2 3535 | op2.Next = op1 3536 | op1b.Next = op2b 3537 | op2b.Prev = op1b 3538 | j.OutPt1 = op1 3539 | j.OutPt2 = op1b 3540 | return true 3541 | } else { 3542 | op1b = c.DupOutPt(op1, true) 3543 | op2b = c.DupOutPt(op2, false) 3544 | op1.Next = op2 3545 | op2.Prev = op1 3546 | op1b.Prev = op2b 3547 | op2b.Next = op1b 3548 | j.OutPt1 = op1 3549 | j.OutPt2 = op1b 3550 | return true 3551 | } 3552 | } else if isHorizontal { 3553 | //treat horizontal joins differently to non-horizontal joins since with 3554 | //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt 3555 | //may be anywhere along the horizontal edge. 3556 | op1b = op1 3557 | for op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2 { 3558 | op1 = op1.Prev 3559 | } 3560 | for op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2 { 3561 | op1b = op1b.Next 3562 | } 3563 | if op1b.Next == op1 || op1b.Next == op2 { 3564 | return false //a flat 'polygon' 3565 | } 3566 | 3567 | op2b = op2 3568 | for op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b { 3569 | op2 = op2.Prev 3570 | } 3571 | for op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1 { 3572 | op2b = op2b.Next 3573 | } 3574 | if op2b.Next == op2 || op2b.Next == op1 { 3575 | return false //a flat 'polygon' 3576 | } 3577 | 3578 | var Left, Right CInt 3579 | //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges 3580 | if !c.GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, &Left, &Right) { 3581 | return false 3582 | } 3583 | 3584 | //DiscardLeftSide: when overlapping edges are joined, a spike will created 3585 | //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up 3586 | //on the discard Side as either may still be needed for other joins ... 3587 | var Pt *IntPoint 3588 | var DiscardLeftSide bool 3589 | if op1.Pt.X >= Left && op1.Pt.X <= Right { 3590 | Pt = op1.Pt 3591 | DiscardLeftSide = (op1.Pt.X > op1b.Pt.X) 3592 | } else if op2.Pt.X >= Left && op2.Pt.X <= Right { 3593 | Pt = op2.Pt 3594 | DiscardLeftSide = (op2.Pt.X > op2b.Pt.X) 3595 | } else if op1b.Pt.X >= Left && op1b.Pt.X <= Right { 3596 | Pt = op1b.Pt 3597 | DiscardLeftSide = op1b.Pt.X > op1.Pt.X 3598 | } else { 3599 | Pt = op2b.Pt 3600 | DiscardLeftSide = (op2b.Pt.X > op2.Pt.X) 3601 | } 3602 | j.OutPt1 = op1 3603 | j.OutPt2 = op2 3604 | return c.JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide) 3605 | } else { 3606 | //nb: For non-horizontal joins ... 3607 | // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y 3608 | // 2. Jr.OutPt1.Pt > Jr.OffPt.Y 3609 | 3610 | //make sure the polygons are correctly oriented ... 3611 | op1b = op1.Next 3612 | for (*op1b.Pt == *op1.Pt) && (op1b != op1) { 3613 | op1b = op1b.Next 3614 | } 3615 | Reverse1 := ((op1b.Pt.Y > op1.Pt.Y) || 3616 | !c.SlopesEqual3(op1.Pt, op1b.Pt, j.OffPt, c.m_UseFullRange)) 3617 | if Reverse1 { 3618 | op1b = op1.Prev 3619 | for (*op1b.Pt == *op1.Pt) && (op1b != op1) { 3620 | op1b = op1b.Prev 3621 | } 3622 | if (op1b.Pt.Y > op1.Pt.Y) || 3623 | !c.SlopesEqual3(op1.Pt, op1b.Pt, j.OffPt, c.m_UseFullRange) { 3624 | return false 3625 | } 3626 | } 3627 | op2b = op2.Next 3628 | for (*op2b.Pt == *op2.Pt) && (op2b != op2) { 3629 | op2b = op2b.Next 3630 | } 3631 | Reverse2 := ((op2b.Pt.Y > op2.Pt.Y) || 3632 | !c.SlopesEqual3(op2.Pt, op2b.Pt, j.OffPt, c.m_UseFullRange)) 3633 | if Reverse2 { 3634 | op2b = op2.Prev 3635 | for (*op2b.Pt == *op2.Pt) && (op2b != op2) { 3636 | op2b = op2b.Prev 3637 | } 3638 | if (op2b.Pt.Y > op2.Pt.Y) || 3639 | !c.SlopesEqual3(op2.Pt, op2b.Pt, j.OffPt, c.m_UseFullRange) { 3640 | return false 3641 | } 3642 | } 3643 | 3644 | if (op1b == op1) || (op2b == op2) || (op1b == op2b) || 3645 | ((outRec1 == outRec2) && (Reverse1 == Reverse2)) { 3646 | return false 3647 | } 3648 | 3649 | if Reverse1 { 3650 | op1b = c.DupOutPt(op1, false) 3651 | op2b = c.DupOutPt(op2, true) 3652 | op1.Prev = op2 3653 | op2.Next = op1 3654 | op1b.Next = op2b 3655 | op2b.Prev = op1b 3656 | j.OutPt1 = op1 3657 | j.OutPt2 = op1b 3658 | return true 3659 | } else { 3660 | op1b = c.DupOutPt(op1, true) 3661 | op2b = c.DupOutPt(op2, false) 3662 | op1.Next = op2 3663 | op2.Prev = op1 3664 | op1b.Prev = op2b 3665 | op2b.Next = op1b 3666 | j.OutPt1 = op1 3667 | j.OutPt2 = op1b 3668 | return true 3669 | } 3670 | } 3671 | } 3672 | 3673 | //---------------------------------------------------------------------- 3674 | 3675 | //returns 0 if false, +1 if true, -1 if pt ON polygon boundary 3676 | //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos 3677 | //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf 3678 | func PointInPolygon(pt *IntPoint, path Path) int { 3679 | result := 0 3680 | cnt := len(path) 3681 | if cnt < 3 { 3682 | return 0 3683 | } 3684 | ip := path[0] 3685 | for i := 1; i <= cnt; i++ { 3686 | var ipNext *IntPoint 3687 | if i == cnt { 3688 | ipNext = path[0] 3689 | } else { 3690 | ipNext = path[i] 3691 | } 3692 | if ipNext.Y == pt.Y { 3693 | if (ipNext.X == pt.X) || (ip.Y == pt.Y && 3694 | ((ipNext.X > pt.X) == (ip.X < pt.X))) { 3695 | return -1 3696 | } 3697 | } 3698 | if (ip.Y < pt.Y) != (ipNext.Y < pt.Y) { 3699 | if ip.X >= pt.X { 3700 | if ipNext.X > pt.X { 3701 | result = 1 - result 3702 | } else { 3703 | d := float64(ip.X-pt.X)*float64(ipNext.Y-pt.Y) - 3704 | float64(ipNext.X-pt.X)*float64(ip.Y-pt.Y) 3705 | if d == 0 { 3706 | return -1 3707 | } else if (d > 0) == (ipNext.Y > ip.Y) { 3708 | result = 1 - result 3709 | } 3710 | } 3711 | } else { 3712 | if ipNext.X > pt.X { 3713 | d := float64(ip.X-pt.X)*float64(ipNext.Y-pt.Y) - 3714 | float64(ipNext.X-pt.X)*float64(ip.Y-pt.Y) 3715 | if d == 0 { 3716 | return -1 3717 | } else if (d > 0) == (ipNext.Y > ip.Y) { 3718 | result = 1 - result 3719 | } 3720 | } 3721 | } 3722 | } 3723 | ip = ipNext 3724 | } 3725 | return result 3726 | } 3727 | 3728 | //------------------------------------------------------------------------------ 3729 | 3730 | func (c *Clipper) pointInPolygon(pt *IntPoint, op *OutPt) int { 3731 | //returns 0 if false, +1 if true, -1 if pt ON polygon boundary 3732 | //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos 3733 | //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf 3734 | result := 0 3735 | startOp := op 3736 | ptx := pt.X 3737 | pty := pt.Y 3738 | poly0x := op.Pt.X 3739 | poly0y := op.Pt.Y 3740 | for { 3741 | op = op.Next 3742 | poly1x := op.Pt.X 3743 | poly1y := op.Pt.Y 3744 | 3745 | if poly1y == pty { 3746 | if (poly1x == ptx) || (poly0y == pty && 3747 | ((poly1x > ptx) == (poly0x < ptx))) { 3748 | return -1 3749 | } 3750 | } 3751 | if (poly0y < pty) != (poly1y < pty) { 3752 | if poly0x >= ptx { 3753 | if poly1x > ptx { 3754 | result = 1 - result 3755 | } else { 3756 | d := float64(poly0x-ptx)*float64(poly1y-pty) - 3757 | float64(poly1x-ptx)*float64(poly0y-pty) 3758 | if d == 0 { 3759 | return -1 3760 | } 3761 | if (d > 0) == (poly1y > poly0y) { 3762 | result = 1 - result 3763 | } 3764 | } 3765 | } else { 3766 | if poly1x > ptx { 3767 | d := float64(poly0x-ptx)*float64(poly1y-pty) - 3768 | float64(poly1x-ptx)*float64(poly0y-pty) 3769 | if d == 0 { 3770 | return -1 3771 | } 3772 | if (d > 0) == (poly1y > poly0y) { 3773 | result = 1 - result 3774 | } 3775 | } 3776 | } 3777 | } 3778 | poly0x = poly1x 3779 | poly0y = poly1y 3780 | if startOp == op { 3781 | break 3782 | } 3783 | } 3784 | return result 3785 | } 3786 | 3787 | //------------------------------------------------------------------------------ 3788 | 3789 | func (c *Clipper) Poly2ContainsPoly1(outPt1, outPt2 *OutPt) bool { 3790 | op := outPt1 3791 | for { 3792 | res := c.pointInPolygon(op.Pt, outPt2) 3793 | if res >= 0 { 3794 | return res != 0 3795 | } 3796 | op = op.Next 3797 | if op == outPt1 { 3798 | break 3799 | } 3800 | } 3801 | return true 3802 | } 3803 | 3804 | //---------------------------------------------------------------------- 3805 | 3806 | func (c *Clipper) FixupFirstLefts1(OldOutRec, NewOutRec *OutRec) { 3807 | for i := 0; i < len(c.m_PolyOuts); i++ { 3808 | outRec := c.m_PolyOuts[i] 3809 | if outRec.Pts != nil && outRec.FirstLeft == OldOutRec { 3810 | if c.Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts) { 3811 | outRec.FirstLeft = NewOutRec 3812 | } 3813 | } 3814 | } 3815 | } 3816 | 3817 | //---------------------------------------------------------------------- 3818 | 3819 | func (c *Clipper) FixupFirstLefts2(OldOutRec, NewOutRec *OutRec) { 3820 | for _, outRec := range c.m_PolyOuts { 3821 | if outRec.FirstLeft == OldOutRec { 3822 | outRec.FirstLeft = NewOutRec 3823 | } 3824 | } 3825 | } 3826 | 3827 | //---------------------------------------------------------------------- 3828 | 3829 | func (c *Clipper) ParseFirstLeft(FirstLeft *OutRec) *OutRec { 3830 | for FirstLeft != nil && FirstLeft.Pts == nil { 3831 | FirstLeft = FirstLeft.FirstLeft 3832 | } 3833 | return FirstLeft 3834 | } 3835 | 3836 | //------------------------------------------------------------------------------ 3837 | 3838 | func (c *Clipper) JoinCommonEdges() { 3839 | for i := 0; i < len(c.m_Joins); i++ { 3840 | join := c.m_Joins[i] 3841 | 3842 | outRec1 := c.GetOutRec(join.OutPt1.Idx) 3843 | outRec2 := c.GetOutRec(join.OutPt2.Idx) 3844 | 3845 | if outRec1.Pts == nil || outRec2.Pts == nil { 3846 | continue 3847 | } 3848 | 3849 | //get the polygon fragment with the correct hole state (FirstLeft) 3850 | //before calling JoinPoints() ... 3851 | var holeStateRec *OutRec 3852 | if outRec1 == outRec2 { 3853 | holeStateRec = outRec1 3854 | } else if c.Param1RightOfParam2(outRec1, outRec2) { 3855 | holeStateRec = outRec2 3856 | } else if c.Param1RightOfParam2(outRec2, outRec1) { 3857 | holeStateRec = outRec1 3858 | } else { 3859 | holeStateRec = c.GetLowermostRec(outRec1, outRec2) 3860 | } 3861 | 3862 | if !c.JoinPoints(join, outRec1, outRec2) { 3863 | continue 3864 | } 3865 | 3866 | if outRec1 == outRec2 { 3867 | //instead of joining two polygons, we've just created a new one by 3868 | //splitting one polygon into two. 3869 | outRec1.Pts = join.OutPt1 3870 | outRec1.BottomPt = nil 3871 | outRec2 = c.CreateOutRec() 3872 | outRec2.Pts = join.OutPt2 3873 | 3874 | //update all OutRec2.Pts Idx's ... 3875 | c.UpdateOutPtIdxs(outRec2) 3876 | 3877 | //We now need to check every OutRec.FirstLeft pointer. If it points 3878 | //to OutRec1 it may need to point to OutRec2 instead ... 3879 | if c.m_UsingPolyTree { 3880 | for j := 0; j < len(c.m_PolyOuts)-1; j++ { 3881 | oRec := c.m_PolyOuts[j] 3882 | if oRec.Pts == nil || c.ParseFirstLeft(oRec.FirstLeft) != outRec1 || 3883 | oRec.IsHole == outRec1.IsHole { 3884 | continue 3885 | } 3886 | if c.Poly2ContainsPoly1(oRec.Pts, join.OutPt2) { 3887 | oRec.FirstLeft = outRec2 3888 | } 3889 | } 3890 | } 3891 | 3892 | if c.Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts) { 3893 | //outRec2 is contained by outRec1 ... 3894 | outRec2.IsHole = !outRec1.IsHole 3895 | outRec2.FirstLeft = outRec1 3896 | 3897 | //fixup FirstLeft pointers that may need reassigning to OutRec1 3898 | if c.m_UsingPolyTree { 3899 | c.FixupFirstLefts2(outRec2, outRec1) 3900 | } 3901 | 3902 | if (outRec2.IsHole != c.ReverseSolution) == (c.area(outRec2) > 0) { 3903 | c.ReversePolyPtLinks(outRec2.Pts) 3904 | } 3905 | 3906 | } else if c.Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts) { 3907 | //outRec1 is contained by outRec2 ... 3908 | outRec2.IsHole = outRec1.IsHole 3909 | outRec1.IsHole = !outRec2.IsHole 3910 | outRec2.FirstLeft = outRec1.FirstLeft 3911 | outRec1.FirstLeft = outRec2 3912 | 3913 | //fixup FirstLeft pointers that may need reassigning to OutRec1 3914 | if c.m_UsingPolyTree { 3915 | c.FixupFirstLefts2(outRec1, outRec2) 3916 | } 3917 | 3918 | if (outRec1.IsHole != c.ReverseSolution) == (c.area(outRec1) > 0) { 3919 | c.ReversePolyPtLinks(outRec1.Pts) 3920 | } 3921 | } else { 3922 | //the 2 polygons are completely separate ... 3923 | outRec2.IsHole = outRec1.IsHole 3924 | outRec2.FirstLeft = outRec1.FirstLeft 3925 | 3926 | //fixup FirstLeft pointers that may need reassigning to OutRec2 3927 | if c.m_UsingPolyTree { 3928 | c.FixupFirstLefts1(outRec1, outRec2) 3929 | } 3930 | } 3931 | 3932 | } else { 3933 | //joined 2 polygons together ... 3934 | 3935 | outRec2.Pts = nil 3936 | outRec2.BottomPt = nil 3937 | outRec2.Idx = outRec1.Idx 3938 | 3939 | outRec1.IsHole = holeStateRec.IsHole 3940 | if holeStateRec == outRec2 { 3941 | outRec1.FirstLeft = outRec2.FirstLeft 3942 | } 3943 | outRec2.FirstLeft = outRec1 3944 | 3945 | //fixup FirstLeft pointers that may need reassigning to OutRec1 3946 | if c.m_UsingPolyTree { 3947 | c.FixupFirstLefts2(outRec2, outRec1) 3948 | } 3949 | } 3950 | } 3951 | } 3952 | 3953 | //------------------------------------------------------------------------------ 3954 | 3955 | func (c *Clipper) UpdateOutPtIdxs(outrec *OutRec) { 3956 | op := outrec.Pts 3957 | for { 3958 | op.Idx = outrec.Idx 3959 | op = op.Prev 3960 | if op == outrec.Pts { 3961 | break 3962 | } 3963 | } 3964 | } 3965 | 3966 | //------------------------------------------------------------------------------ 3967 | 3968 | func (c *Clipper) DoSimplePolygons() { 3969 | for i := 0; i < len(c.m_PolyOuts); i++ { 3970 | outrec := c.m_PolyOuts[i] 3971 | op := outrec.Pts 3972 | if op == nil { 3973 | continue 3974 | } 3975 | for { //for each Pt in Polygon until duplicate found do ... 3976 | op2 := op.Next 3977 | for op2 != outrec.Pts { 3978 | if op.Pt.Equals(op2.Pt) && op2.Next != op && op2.Prev != op { 3979 | //split the polygon into two ... 3980 | op3 := op.Prev 3981 | op4 := op2.Prev 3982 | op.Prev = op4 3983 | op4.Next = op 3984 | op2.Prev = op3 3985 | op3.Next = op2 3986 | 3987 | outrec.Pts = op 3988 | outrec2 := c.CreateOutRec() 3989 | outrec2.Pts = op2 3990 | c.UpdateOutPtIdxs(outrec2) 3991 | if c.Poly2ContainsPoly1(outrec2.Pts, outrec.Pts) { 3992 | //OutRec2 is contained by OutRec1 ... 3993 | outrec2.IsHole = !outrec.IsHole 3994 | outrec2.FirstLeft = outrec 3995 | } else { 3996 | if c.Poly2ContainsPoly1(outrec.Pts, outrec2.Pts) { 3997 | //OutRec1 is contained by OutRec2 ... 3998 | outrec2.IsHole = outrec.IsHole 3999 | outrec.IsHole = !outrec2.IsHole 4000 | outrec2.FirstLeft = outrec.FirstLeft 4001 | outrec.FirstLeft = outrec2 4002 | } else { 4003 | //the 2 polygons are separate ... 4004 | outrec2.IsHole = outrec.IsHole 4005 | outrec2.FirstLeft = outrec.FirstLeft 4006 | } 4007 | } 4008 | op2 = op //ie get ready for the next iteration 4009 | } 4010 | op2 = op2.Next 4011 | } 4012 | op = op.Next 4013 | if op == outrec.Pts { 4014 | break 4015 | } 4016 | } 4017 | } 4018 | } 4019 | 4020 | //------------------------------------------------------------------------------ 4021 | 4022 | func Area(poly Path) float64 { 4023 | cnt := len(poly) 4024 | if cnt < 3 { 4025 | return 0 4026 | } 4027 | a := float64(0) 4028 | j := cnt - 1 4029 | for i := 0; i < cnt; i++ { 4030 | a += (float64(poly[j].X) + float64(poly[i].X)) * 4031 | (float64(poly[j].Y - poly[i].Y)) 4032 | j = i 4033 | } 4034 | return -a * 0.5 4035 | } 4036 | 4037 | func AreaCombined(polygons Paths) float64 { 4038 | a := 0. 4039 | for _, polygon := range polygons { 4040 | a += Area(polygon) 4041 | } 4042 | return a 4043 | } 4044 | 4045 | //------------------------------------------------------------------------------ 4046 | 4047 | func (c *Clipper) area(outRec *OutRec) float64 { 4048 | op := outRec.Pts 4049 | if op == nil { 4050 | return 0 4051 | } 4052 | a := float64(0) 4053 | for { 4054 | a = a + float64(op.Prev.Pt.X+op.Pt.X)*float64(op.Prev.Pt.Y-op.Pt.Y) 4055 | op = op.Next 4056 | if op == outRec.Pts { 4057 | break 4058 | } 4059 | } 4060 | return a * 0.5 4061 | } 4062 | 4063 | //------------------------------------------------------------------------------ 4064 | // SimplifyPolygon functions ... 4065 | // Convert self-intersecting polygons into simple polygons 4066 | //------------------------------------------------------------------------------ 4067 | // default fillType = PftEvenOdd 4068 | func (c *Clipper) SimplifyPolygon(poly Path, 4069 | fillType PolyFillType) Paths { 4070 | var result Paths 4071 | c2 := NewClipper(IoNone) 4072 | c2.StrictlySimple = true 4073 | c2.AddPath(poly, PtSubject, true) 4074 | result, _ = c2.Execute1(CtUnion, fillType, fillType) 4075 | return result 4076 | } 4077 | 4078 | //------------------------------------------------------------------------------ 4079 | 4080 | // default fillType = PftEvenOdd 4081 | func (c *Clipper) SimplifyPolygons(polys Paths, 4082 | fillType PolyFillType) Paths { 4083 | var result Paths 4084 | c2 := NewClipper(IoNone) 4085 | c2.StrictlySimple = true 4086 | c2.AddPaths(polys, PtSubject, true) 4087 | result, _ = c2.Execute1(CtUnion, fillType, fillType) 4088 | return result 4089 | } 4090 | 4091 | //------------------------------------------------------------------------------ 4092 | 4093 | func (c *Clipper) DistanceSqrd(pt1, pt2 *IntPoint) float64 { 4094 | dx := float64(pt1.X - pt2.X) 4095 | dy := float64(pt1.Y - pt2.Y) 4096 | return (dx*dx + dy*dy) 4097 | } 4098 | 4099 | //------------------------------------------------------------------------------ 4100 | 4101 | func (c *Clipper) DistanceFromLineSqrd(pt, ln1, ln2 *IntPoint) float64 { 4102 | //The equation of a line in general form (Ax + By + C = 0) 4103 | //given 2 points (x¹,y¹) & (x²,y²) is ... 4104 | //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 4105 | //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ 4106 | //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) 4107 | //see http://en.wikipedia.org/wiki/Perpendicular_distance 4108 | A := float64(ln1.Y - ln2.Y) 4109 | B := float64(ln2.X - ln1.X) 4110 | C := A*float64(ln1.X) + B*float64(ln1.Y) 4111 | C = A*float64(pt.X) + B*float64(pt.Y) - C 4112 | return (C * C) / (A*A + B*B) 4113 | } 4114 | 4115 | //--------------------------------------------------------------------------- 4116 | 4117 | func (c *Clipper) SlopesNearCollinear(pt1, 4118 | pt2, pt3 *IntPoint, distSqrd float64) bool { 4119 | return c.DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd 4120 | } 4121 | 4122 | //------------------------------------------------------------------------------ 4123 | 4124 | func (c *Clipper) PointsAreClose(pt1, pt2 *IntPoint, distSqrd float64) bool { 4125 | dx := float64(pt1.X - pt2.X) 4126 | dy := float64(pt1.Y - pt2.Y) 4127 | return ((dx*dx)+(dy*dy) <= distSqrd) 4128 | } 4129 | 4130 | //------------------------------------------------------------------------------ 4131 | 4132 | func (c *Clipper) ExcludeOp(op *OutPt) *OutPt { 4133 | result := op.Prev 4134 | result.Next = op.Next 4135 | op.Next.Prev = result 4136 | result.Idx = 0 4137 | return result 4138 | } 4139 | 4140 | //------------------------------------------------------------------------------ 4141 | 4142 | //distance = proximity in units/pixels below which vertices will be stripped. 4143 | //Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have 4144 | //both x & y coords within 1 unit, then the second vertex will be stripped. 4145 | // default distance=1.415 4146 | func (c *Clipper) CleanPolygon(path Path, distance float64) Path { 4147 | 4148 | cnt := len(path) 4149 | 4150 | if cnt == 0 { 4151 | return NewPath() 4152 | } 4153 | 4154 | outPts := make([]*OutPt, cnt) 4155 | for i := 0; i < cnt; i++ { 4156 | outPts[i] = new(OutPt) 4157 | } 4158 | 4159 | for i := 0; i < cnt; i++ { 4160 | outPts[i].Pt = path[i] 4161 | outPts[i].Next = outPts[(i+1)%cnt] 4162 | outPts[i].Next.Prev = outPts[i] 4163 | outPts[i].Idx = 0 4164 | } 4165 | 4166 | distSqrd := distance * distance 4167 | op := outPts[0] 4168 | for op.Idx == 0 && op.Next != op.Prev { 4169 | if c.PointsAreClose(op.Pt, op.Prev.Pt, distSqrd) { 4170 | op = c.ExcludeOp(op) 4171 | cnt-- 4172 | } else if c.PointsAreClose(op.Prev.Pt, op.Next.Pt, distSqrd) { 4173 | c.ExcludeOp(op.Next) 4174 | op = c.ExcludeOp(op) 4175 | cnt -= 2 4176 | } else if c.SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, distSqrd) { 4177 | op = c.ExcludeOp(op) 4178 | cnt-- 4179 | } else { 4180 | op.Idx = 1 4181 | op = op.Next 4182 | } 4183 | } 4184 | 4185 | if cnt < 3 { 4186 | cnt = 0 4187 | } 4188 | result := Path(make([]*IntPoint, cnt)) 4189 | for i := 0; i < cnt; i++ { 4190 | result[i] = op.Pt 4191 | op = op.Next 4192 | } 4193 | outPts = nil 4194 | return result 4195 | } 4196 | 4197 | //------------------------------------------------------------------------------ 4198 | // default distance = 1.415 4199 | func (c *Clipper) CleanPolygons(polys Paths, 4200 | distance float64) Paths { 4201 | result := Paths(make([]Path, len(polys))) 4202 | for i := 0; i < len(polys); i++ { 4203 | result[i] = c.CleanPolygon(polys[i], distance) 4204 | } 4205 | return result 4206 | } 4207 | 4208 | //------------------------------------------------------------------------------ 4209 | 4210 | func (c *Clipper) Minkowski(pattern, path Path, IsSum, IsClosed bool) Paths { 4211 | var delta int 4212 | if IsClosed { 4213 | delta = 1 4214 | } else { 4215 | delta = 0 4216 | } 4217 | polyCnt := len(pattern) 4218 | pathCnt := len(path) 4219 | result := Paths(make([]Path, pathCnt)) 4220 | if IsSum { 4221 | for i := 0; i < pathCnt; i++ { 4222 | result[i] = make([]*IntPoint, polyCnt) 4223 | for j, ip := range pattern { 4224 | result[i][j] = &IntPoint{path[i].X + ip.X, path[i].Y + ip.Y} 4225 | } 4226 | } 4227 | } else { 4228 | for i := 0; i < pathCnt; i++ { 4229 | result[i] = make([]*IntPoint, polyCnt) 4230 | for j, ip := range pattern { 4231 | result[i][j] = &IntPoint{path[i].X - ip.X, path[i].Y - ip.Y} 4232 | } 4233 | } 4234 | } 4235 | 4236 | quads := Paths(make([]Path, 0, (pathCnt+delta)*(polyCnt+1))) 4237 | for i := 0; i < pathCnt-1+delta; i++ { 4238 | for j := 0; j < polyCnt; j++ { 4239 | quad := Path(make([]*IntPoint, 4)) 4240 | quad[0] = result[i%pathCnt][j%polyCnt] 4241 | quad[1] = result[(i+1)%pathCnt][j%polyCnt] 4242 | quad[2] = result[(i+1)%pathCnt][(j+1)%polyCnt] 4243 | quad[3] = result[i%pathCnt][(j+1)%polyCnt] 4244 | if !Orientation(quad) { 4245 | reversePath(quad) 4246 | } 4247 | quads = append(quads, quad) 4248 | } 4249 | } 4250 | return quads 4251 | } 4252 | 4253 | //------------------------------------------------------------------------------ 4254 | 4255 | func (c *Clipper) MinkowskiSum(pattern, path Path, pathIsClosed bool) Paths { 4256 | paths := c.Minkowski(pattern, path, true, pathIsClosed) 4257 | c2 := NewClipper(IoNone) 4258 | c2.AddPaths(paths, PtSubject, true) 4259 | paths, _ = c2.Execute1(CtUnion, PftNonZero, PftNonZero) 4260 | return paths 4261 | } 4262 | 4263 | //------------------------------------------------------------------------------ 4264 | 4265 | func (c *Clipper) TranslatePath(path Path, delta *IntPoint) Path { 4266 | outPath := Path(make([]*IntPoint, len(path))) 4267 | for i := 0; i < len(path); i++ { 4268 | outPath[i] = &IntPoint{path[i].X + delta.X, path[i].Y + delta.Y} 4269 | } 4270 | return outPath 4271 | } 4272 | 4273 | //------------------------------------------------------------------------------ 4274 | 4275 | func (c *Clipper) MinkowskiSumAll(pattern Path, paths Paths, pathIsClosed bool) Paths { 4276 | var solution Paths 4277 | c2 := NewClipper(IoNone) 4278 | for i := 0; i < len(paths); i++ { 4279 | tmp := c.Minkowski(pattern, paths[i], true, pathIsClosed) 4280 | c.AddPaths(tmp, PtSubject, true) 4281 | if pathIsClosed { 4282 | path := c.TranslatePath(paths[i], pattern[0]) 4283 | c.AddPath(path, PtClip, true) 4284 | } 4285 | } 4286 | solution, _ = c2.Execute1(CtUnion, PftNonZero, PftNonZero) 4287 | return solution 4288 | } 4289 | 4290 | //------------------------------------------------------------------------------ 4291 | 4292 | func (c *Clipper) MinkowskiDiff(poly1, poly2 Path) Paths { 4293 | paths := c.Minkowski(poly1, poly2, false, true) 4294 | c2 := NewClipper(IoNone) 4295 | c2.AddPaths(paths, PtSubject, true) 4296 | paths, _ = c2.Execute1(CtUnion, PftNonZero, PftNonZero) 4297 | return paths 4298 | } 4299 | 4300 | //------------------------------------------------------------------------------ 4301 | 4302 | type NodeType int 4303 | 4304 | const ( 4305 | ntAny NodeType = iota 4306 | ntOpen 4307 | ntClosed 4308 | ) 4309 | 4310 | func (c *Clipper) PolyTreeToPaths(polytree *PolyTree) Paths { 4311 | 4312 | result := Paths(make([]Path, 0, polytree.Total())) 4313 | c.AddPolyNodeToPaths(polytree.toPolyNode(), ntAny, result) 4314 | return result 4315 | } 4316 | 4317 | //------------------------------------------------------------------------------ 4318 | 4319 | func (c *Clipper) AddPolyNodeToPaths(polynode *PolyNode, nt NodeType, paths Paths) { 4320 | match := true 4321 | switch nt { 4322 | case ntOpen: 4323 | return 4324 | case ntClosed: 4325 | match = !polynode.IsOpen 4326 | break 4327 | default: 4328 | break 4329 | } 4330 | 4331 | if len(polynode.m_polygon) > 0 && match { 4332 | paths = append(paths, polynode.m_polygon) 4333 | } 4334 | for _, pn := range polynode.m_Childs { 4335 | c.AddPolyNodeToPaths(pn, nt, paths) 4336 | } 4337 | } 4338 | 4339 | //------------------------------------------------------------------------------ 4340 | 4341 | func (c *Clipper) OpenPathsFromPolyTree(polytree *PolyTree) Paths { 4342 | result := Paths(make([]Path, 0, polytree.ChildCount())) 4343 | for i := 0; i < polytree.ChildCount(); i++ { 4344 | if polytree.m_Childs[i].IsOpen { 4345 | result = append(result, polytree.m_Childs[i].m_polygon) 4346 | } 4347 | } 4348 | return result 4349 | } 4350 | 4351 | //------------------------------------------------------------------------------ 4352 | 4353 | func (c *Clipper) ClosedPathsFromPolyTree(polytree *PolyTree) Paths { 4354 | result := Paths(make([]Path, 0, polytree.Total())) 4355 | c.AddPolyNodeToPaths(polytree.toPolyNode(), ntClosed, result) 4356 | return result 4357 | } 4358 | 4359 | //------------------------------------------------------------------------------ 4360 | 4361 | const two_pi = math.Pi * 2 4362 | const def_arc_tolerance = 0.25 4363 | 4364 | type ClipperOffset struct { 4365 | m_destPolys Paths 4366 | m_srcPoly Path 4367 | m_destPoly Path 4368 | m_normals []*DoublePoint 4369 | m_delta, m_sinA, m_sin, m_cos float64 4370 | m_miterLim, m_StepsPerRad float64 4371 | 4372 | m_lowest *IntPoint 4373 | m_polyNodes *PolyNode 4374 | 4375 | ArcTolerance float64 4376 | MiterLimit float64 4377 | } 4378 | 4379 | func NewClipperOffset() *ClipperOffset { 4380 | co := new(ClipperOffset) 4381 | co.m_normals = make([]*DoublePoint, 0) 4382 | co.m_polyNodes = NewPolyNode() 4383 | co.MiterLimit = 2.0 4384 | co.ArcTolerance = def_arc_tolerance 4385 | co.m_lowest = NewIntPoint(-1, 0) 4386 | return co 4387 | } 4388 | 4389 | func (co *ClipperOffset) Clear() { 4390 | co.m_polyNodes.m_Childs = make([]*PolyNode, 0) 4391 | co.m_lowest.X = -1 4392 | } 4393 | 4394 | //------------------------------------------------------------------------------ 4395 | 4396 | func (co *ClipperOffset) Round(value float64) CInt { 4397 | if value < 0 { 4398 | return CInt(value - 0.5) 4399 | } else { 4400 | return CInt(value + 0.5) 4401 | } 4402 | } 4403 | 4404 | //------------------------------------------------------------------------------ 4405 | 4406 | func (co *ClipperOffset) AddPath(path Path, joinType JoinType, endType EndType) { 4407 | highI := len(path) - 1 4408 | if highI < 0 { 4409 | return 4410 | } 4411 | newNode := NewPolyNode() 4412 | newNode.m_jointype = joinType 4413 | newNode.m_endtype = endType 4414 | 4415 | //strip duplicate points from path and also get index to the lowest point ... 4416 | if endType == EtClosedLine || endType == EtClosedPolygon { 4417 | for highI > 0 && path[0] == path[highI] { 4418 | highI-- 4419 | } 4420 | } 4421 | newNode.m_polygon = Path(make([]*IntPoint, 0, highI+1)) 4422 | newNode.m_polygon = append(newNode.m_polygon, path[0]) 4423 | j := 0 4424 | k := 0 4425 | for i := 1; i <= highI; i++ { 4426 | if newNode.m_polygon[j] != path[i] { 4427 | j++ 4428 | newNode.m_polygon = append(newNode.m_polygon, path[i]) 4429 | if path[i].Y > newNode.m_polygon[k].Y || 4430 | (path[i].Y == newNode.m_polygon[k].Y && 4431 | path[i].X < newNode.m_polygon[k].X) { 4432 | k = j 4433 | } 4434 | } 4435 | } 4436 | if endType == EtClosedPolygon && j < 2 { 4437 | return 4438 | } 4439 | 4440 | co.m_polyNodes.AddChild(newNode) 4441 | 4442 | //if this path's lowest pt is lower than all the others then update m_lowest 4443 | if endType != EtClosedPolygon { 4444 | return 4445 | } 4446 | if co.m_lowest.X < 0 { 4447 | co.m_lowest = &IntPoint{CInt(co.m_polyNodes.ChildCount() - 1), CInt(k)} 4448 | } else { 4449 | ip := co.m_polyNodes.m_Childs[int(co.m_lowest.X)].m_polygon[int(co.m_lowest.Y)] 4450 | if newNode.m_polygon[k].Y > ip.Y || 4451 | (newNode.m_polygon[k].Y == ip.Y && 4452 | newNode.m_polygon[k].X < ip.X) { 4453 | co.m_lowest = &IntPoint{CInt(co.m_polyNodes.ChildCount() - 1), CInt(k)} 4454 | } 4455 | } 4456 | } 4457 | 4458 | //------------------------------------------------------------------------------ 4459 | 4460 | func (co *ClipperOffset) AddPaths(paths Paths, joinType JoinType, endType EndType) { 4461 | for _, p := range paths { 4462 | co.AddPath(p, joinType, endType) 4463 | } 4464 | } 4465 | 4466 | //------------------------------------------------------------------------------ 4467 | 4468 | func (co *ClipperOffset) FixOrientations() { 4469 | //fixup orientations of all closed paths if the orientation of the 4470 | //closed path with the lowermost vertex is wrong ... 4471 | if co.m_lowest.X >= 0 && 4472 | !Orientation(co.m_polyNodes.m_Childs[int(co.m_lowest.X)].m_polygon) { 4473 | for i := 0; i < co.m_polyNodes.ChildCount(); i++ { 4474 | node := co.m_polyNodes.m_Childs[i] 4475 | if node.m_endtype == EtClosedPolygon || 4476 | (node.m_endtype == EtClosedLine && 4477 | Orientation(node.m_polygon)) { 4478 | reversePath(node.m_polygon) 4479 | } 4480 | } 4481 | } else { 4482 | for i := 0; i < co.m_polyNodes.ChildCount(); i++ { 4483 | node := co.m_polyNodes.m_Childs[i] 4484 | if node.m_endtype == EtClosedLine && 4485 | !Orientation(node.m_polygon) { 4486 | reversePath(node.m_polygon) 4487 | } 4488 | } 4489 | } 4490 | } 4491 | 4492 | //------------------------------------------------------------------------------ 4493 | 4494 | func (co *ClipperOffset) GetUnitNormal(pt1, pt2 *IntPoint) *DoublePoint { 4495 | dx := float64(pt2.X - pt1.X) 4496 | dy := float64(pt2.Y - pt1.Y) 4497 | if (dx == 0) && (dy == 0) { 4498 | return new(DoublePoint) 4499 | } 4500 | 4501 | f := 1 * 1.0 / math.Sqrt(dx*dx+dy*dy) 4502 | dx *= f 4503 | dy *= f 4504 | return &DoublePoint{dy, -dx} 4505 | } 4506 | 4507 | //------------------------------------------------------------------------------ 4508 | 4509 | func (co *ClipperOffset) DoOffset(delta float64) { 4510 | co.m_destPolys = Paths(make([]Path, 0, co.m_polyNodes.ChildCount()*2)) 4511 | co.m_delta = delta 4512 | 4513 | //if Zero offset, just copy any CLOSED polygons to m_p and return ... 4514 | if near_zero(delta) { 4515 | for i := 0; i < co.m_polyNodes.ChildCount(); i++ { 4516 | node := co.m_polyNodes.m_Childs[i] 4517 | if node.m_endtype == EtClosedPolygon { 4518 | co.m_destPolys = append(co.m_destPolys, node.m_polygon) 4519 | } 4520 | } 4521 | return 4522 | } 4523 | 4524 | //see offset_triginometry3.svg in the documentation folder ... 4525 | if co.MiterLimit > 2 { 4526 | co.m_miterLim = 2 / (co.MiterLimit * co.MiterLimit) 4527 | } else { 4528 | co.m_miterLim = 0.5 4529 | } 4530 | 4531 | var y float64 4532 | if co.ArcTolerance <= 0.0 { 4533 | y = def_arc_tolerance 4534 | } else if co.ArcTolerance > math.Abs(delta)*def_arc_tolerance { 4535 | y = math.Abs(delta) * def_arc_tolerance 4536 | } else { 4537 | y = co.ArcTolerance 4538 | } 4539 | //see offset_triginometry2.svg in the documentation folder ... 4540 | steps := math.Pi / math.Acos(1-y/math.Abs(delta)) 4541 | co.m_sin = math.Sin(two_pi / steps) 4542 | co.m_cos = math.Cos(two_pi / steps) 4543 | co.m_StepsPerRad = steps / two_pi 4544 | if delta < 0.0 { 4545 | co.m_sin = -co.m_sin 4546 | } 4547 | 4548 | for i := 0; i < co.m_polyNodes.ChildCount(); i++ { 4549 | node := co.m_polyNodes.m_Childs[i] 4550 | co.m_srcPoly = node.m_polygon 4551 | 4552 | length := len(co.m_srcPoly) 4553 | 4554 | if length == 0 || (delta <= 0 && (length < 3 || 4555 | node.m_endtype != EtClosedPolygon)) { 4556 | continue 4557 | } 4558 | 4559 | co.m_destPoly = Path(make([]*IntPoint, 0, int(steps)+4)) 4560 | 4561 | if length == 1 { 4562 | if node.m_jointype == JtRound { 4563 | X := 1.0 4564 | Y := 0.0 4565 | for j := 1; j <= int(steps); j++ { 4566 | co.m_destPoly = append(co.m_destPoly, &IntPoint{ 4567 | co.Round(float64(co.m_srcPoly[0].X) + X*delta), 4568 | co.Round(float64(co.m_srcPoly[0].Y) + Y*delta)}) 4569 | X2 := X 4570 | X = X*co.m_cos - co.m_sin*Y 4571 | Y = X2*co.m_sin + Y*co.m_cos 4572 | } 4573 | } else { 4574 | X := -1.0 4575 | Y := -1.0 4576 | for j := 0; j < 4; j++ { 4577 | co.m_destPoly = append(co.m_destPoly, &IntPoint{ 4578 | co.Round(float64(co.m_srcPoly[0].X) + X*delta), 4579 | co.Round(float64(co.m_srcPoly[0].Y) + Y*delta)}) 4580 | if X < 0 { 4581 | X = 1 4582 | } else if Y < 0 { 4583 | Y = 1 4584 | } else { 4585 | X = -1 4586 | } 4587 | } 4588 | } 4589 | co.m_destPolys = append(co.m_destPolys, co.m_destPoly) 4590 | continue 4591 | } 4592 | 4593 | //build m_normals ... 4594 | co.m_normals = make([]*DoublePoint, 0, length) 4595 | for j := 0; j < length-1; j++ { 4596 | co.m_normals = append(co.m_normals, 4597 | co.GetUnitNormal(co.m_srcPoly[j], co.m_srcPoly[j+1])) 4598 | } 4599 | if node.m_endtype == EtClosedLine || 4600 | node.m_endtype == EtClosedPolygon { 4601 | co.m_normals = append(co.m_normals, 4602 | co.GetUnitNormal(co.m_srcPoly[length-1], co.m_srcPoly[0])) 4603 | } else { 4604 | co.m_normals = append(co.m_normals, 4605 | CopyDoublePoint(co.m_normals[length-2])) 4606 | } 4607 | 4608 | if node.m_endtype == EtClosedPolygon { 4609 | k := length - 1 4610 | for j := 0; j < length; j++ { 4611 | co.OffsetPoint(j, &k, node.m_jointype) 4612 | } 4613 | co.m_destPolys = append(co.m_destPolys, co.m_destPoly) 4614 | } else if node.m_endtype == EtClosedLine { 4615 | k := length - 1 4616 | for j := 0; j < length; j++ { 4617 | co.OffsetPoint(j, &k, node.m_jointype) 4618 | } 4619 | co.m_destPolys = append(co.m_destPolys, co.m_destPoly) 4620 | co.m_destPoly = Path(make([]*IntPoint, 0)) 4621 | //re-build m_normals ... 4622 | n := co.m_normals[length-1] 4623 | for j := length - 1; j > 0; j-- { 4624 | co.m_normals[j] = &DoublePoint{-co.m_normals[j-1].X, -co.m_normals[j-1].Y} 4625 | } 4626 | co.m_normals[0] = &DoublePoint{-n.X, -n.Y} 4627 | k = 0 4628 | for j := length - 1; j >= 0; j-- { 4629 | co.OffsetPoint(j, &k, node.m_jointype) 4630 | } 4631 | co.m_destPolys = append(co.m_destPolys, co.m_destPoly) 4632 | } else { 4633 | k := 0 4634 | for j := 1; j < length-1; j++ { 4635 | co.OffsetPoint(j, &k, node.m_jointype) 4636 | } 4637 | 4638 | var pt1 *IntPoint 4639 | if node.m_endtype == EtOpenButt { 4640 | j := length - 1 4641 | pt1 = &IntPoint{CInt(co.Round(float64(co.m_srcPoly[j].X) + 4642 | co.m_normals[j].X*delta)), 4643 | CInt(co.Round(float64(co.m_srcPoly[j].Y) + 4644 | co.m_normals[j].Y*delta))} 4645 | co.m_destPoly = append(co.m_destPoly, pt1) 4646 | pt1 = &IntPoint{CInt(co.Round(float64(co.m_srcPoly[j].X) - 4647 | co.m_normals[j].X*delta)), 4648 | CInt(co.Round(float64(co.m_srcPoly[j].Y) - 4649 | co.m_normals[j].Y*delta))} 4650 | co.m_destPoly = append(co.m_destPoly, pt1) 4651 | } else { 4652 | j := length - 1 4653 | k := length - 2 4654 | co.m_sinA = 0 4655 | co.m_normals[j] = &DoublePoint{-co.m_normals[j].X, 4656 | -co.m_normals[j].Y} 4657 | if node.m_endtype == EtOpenSquare { 4658 | co.DoSquare(j, k) 4659 | } else { 4660 | co.DoRound(j, k) 4661 | } 4662 | } 4663 | 4664 | //re-build m_normals ... 4665 | for j := length - 1; j > 0; j-- { 4666 | co.m_normals[j] = &DoublePoint{-co.m_normals[j-1].X, -co.m_normals[j-1].Y} 4667 | } 4668 | 4669 | co.m_normals[0] = &DoublePoint{-co.m_normals[1].X, -co.m_normals[1].Y} 4670 | 4671 | k = length - 1 4672 | for j := k - 1; j > 0; j-- { 4673 | co.OffsetPoint(j, &k, node.m_jointype) 4674 | } 4675 | 4676 | if node.m_endtype == EtOpenButt { 4677 | pt1 = &IntPoint{CInt(co.Round(float64(co.m_srcPoly[0].X) - 4678 | co.m_normals[0].X*delta)), 4679 | CInt(co.Round(float64(co.m_srcPoly[0].Y) - 4680 | co.m_normals[0].Y*delta))} 4681 | co.m_destPoly = append(co.m_destPoly, pt1) 4682 | pt1 = &IntPoint{CInt(co.Round(float64(co.m_srcPoly[0].X) + 4683 | co.m_normals[0].X*delta)), 4684 | CInt(co.Round(float64(co.m_srcPoly[0].Y) + 4685 | co.m_normals[0].Y*delta))} 4686 | co.m_destPoly = append(co.m_destPoly, pt1) 4687 | } else { 4688 | k = 1 4689 | co.m_sinA = 0 4690 | if node.m_endtype == EtOpenSquare { 4691 | co.DoSquare(0, 1) 4692 | } else { 4693 | co.DoRound(0, 1) 4694 | } 4695 | } 4696 | co.m_destPolys = append(co.m_destPolys, co.m_destPoly) 4697 | } 4698 | } 4699 | } 4700 | 4701 | //------------------------------------------------------------------------------ 4702 | 4703 | func (co *ClipperOffset) Execute(delta float64) (solution Paths) { 4704 | solution = make([]Path, 0) 4705 | co.FixOrientations() 4706 | co.DoOffset(delta) 4707 | //now clean up 'corners' ... 4708 | clpr := NewClipper(IoNone) 4709 | clpr.AddPaths(co.m_destPolys, PtSubject, true) 4710 | if delta > 0 { 4711 | solution, _ = clpr.Execute1(CtUnion, 4712 | PftPositive, PftPositive) 4713 | } else { 4714 | r := GetBounds(co.m_destPolys) 4715 | outer := Path(make([]*IntPoint, 4)) 4716 | 4717 | outer[0] = &IntPoint{r.left - 10, r.bottom + 10} 4718 | outer[1] = &IntPoint{r.right + 10, r.bottom + 10} 4719 | outer[2] = &IntPoint{r.right + 10, r.top - 10} 4720 | outer[3] = &IntPoint{r.left - 10, r.top - 10} 4721 | 4722 | clpr.AddPath(outer, PtSubject, true) 4723 | clpr.ReverseSolution = true 4724 | solution, _ = clpr.Execute1(CtUnion, PftNegative, PftNegative) 4725 | if len(solution) > 0 { 4726 | solution = solution[1:] 4727 | } 4728 | } 4729 | return 4730 | } 4731 | 4732 | //------------------------------------------------------------------------------ 4733 | 4734 | func (co *ClipperOffset) Execute2(delta float64) (solution *PolyTree) { 4735 | solution = NewPolyTree() 4736 | co.FixOrientations() 4737 | co.DoOffset(delta) 4738 | 4739 | //now clean up 'corners' ... 4740 | clpr := NewClipper(IoNone) 4741 | clpr.AddPaths(co.m_destPolys, PtSubject, true) 4742 | if delta > 0 { 4743 | solution, _ = clpr.Execute2(CtUnion, 4744 | PftPositive, PftPositive) 4745 | } else { 4746 | r := GetBounds(co.m_destPolys) 4747 | outer := Path(make([]*IntPoint, 4)) 4748 | 4749 | outer[0] = &IntPoint{r.left - 10, r.bottom + 10} 4750 | outer[1] = &IntPoint{r.right + 10, r.bottom + 10} 4751 | outer[2] = &IntPoint{r.right + 10, r.top - 10} 4752 | outer[3] = &IntPoint{r.left - 10, r.top - 10} 4753 | 4754 | clpr.AddPath(outer, PtSubject, true) 4755 | clpr.ReverseSolution = true 4756 | solution, _ = clpr.Execute2(CtUnion, PftNegative, PftNegative) 4757 | //remove the outer PolyNode rectangle ... 4758 | if solution.ChildCount() == 1 && solution.m_Childs[0].ChildCount() > 0 { 4759 | outerNode := solution.m_Childs[0] 4760 | solution.m_Childs[0] = outerNode.m_Childs[0] 4761 | for i := 1; i < outerNode.ChildCount(); i++ { 4762 | solution.AddChild(outerNode.m_Childs[i]) 4763 | } 4764 | } else { 4765 | solution.Clear() 4766 | } 4767 | } 4768 | return 4769 | } 4770 | 4771 | //------------------------------------------------------------------------------ 4772 | 4773 | func (co *ClipperOffset) OffsetPoint(j int, k *int, jointype JoinType) { 4774 | //cross product ... 4775 | co.m_sinA = (co.m_normals[*k].X*co.m_normals[j].Y - co.m_normals[j].X*co.m_normals[*k].Y) 4776 | 4777 | if math.Abs(co.m_sinA*co.m_delta) < 1.0 { 4778 | //dot product ... 4779 | cosA := (co.m_normals[*k].X*co.m_normals[j].X + 4780 | co.m_normals[j].Y*co.m_normals[*k].Y) 4781 | if cosA > 0 { // angle ==> 0 degrees { 4782 | co.m_destPoly = append(co.m_destPoly, &IntPoint{co.Round( 4783 | float64(co.m_srcPoly[j].X) + co.m_normals[*k].X*co.m_delta), 4784 | co.Round(float64(co.m_srcPoly[j].Y) + 4785 | co.m_normals[*k].Y*co.m_delta)}) 4786 | return 4787 | } 4788 | //else angle ==> 180 degrees 4789 | } else if co.m_sinA > 1.0 { 4790 | co.m_sinA = 1.0 4791 | } else if co.m_sinA < -1.0 { 4792 | co.m_sinA = -1.0 4793 | } 4794 | 4795 | if co.m_sinA*co.m_delta < 0 { 4796 | co.m_destPoly = append(co.m_destPoly, &IntPoint{co.Round( 4797 | float64(co.m_srcPoly[j].X) + co.m_normals[*k].X*co.m_delta), 4798 | co.Round(float64(co.m_srcPoly[j].Y) + co.m_normals[*k].Y*co.m_delta)}) 4799 | co.m_destPoly = append(co.m_destPoly, co.m_srcPoly[j]) 4800 | co.m_destPoly = append(co.m_destPoly, &IntPoint{co.Round( 4801 | float64(co.m_srcPoly[j].X) + co.m_normals[j].X*co.m_delta), 4802 | co.Round(float64(co.m_srcPoly[j].Y) + co.m_normals[j].Y*co.m_delta)}) 4803 | } else { 4804 | switch jointype { 4805 | case JtMiter: 4806 | { 4807 | r := 1 + (co.m_normals[j].X*co.m_normals[*k].X + 4808 | co.m_normals[j].Y*co.m_normals[*k].Y) 4809 | if r >= co.m_miterLim { 4810 | co.DoMiter(j, *k, r) 4811 | } else { 4812 | co.DoSquare(j, *k) 4813 | } 4814 | break 4815 | } 4816 | case JtSquare: 4817 | co.DoSquare(j, *k) 4818 | break 4819 | case JtRound: 4820 | co.DoRound(j, *k) 4821 | break 4822 | } 4823 | } 4824 | *k = j 4825 | } 4826 | 4827 | //------------------------------------------------------------------------------ 4828 | 4829 | func (co *ClipperOffset) DoSquare(j, k int) { 4830 | dx := math.Tan(math.Atan2(co.m_sinA, 4831 | co.m_normals[k].X*co.m_normals[j].X+ 4832 | co.m_normals[k].Y*co.m_normals[j].Y) / 4) 4833 | co.m_destPoly = append(co.m_destPoly, &IntPoint{ 4834 | co.Round(float64(co.m_srcPoly[j].X) + 4835 | co.m_delta*(co.m_normals[k].X-co.m_normals[k].Y*dx)), 4836 | co.Round(float64(co.m_srcPoly[j].Y) + 4837 | co.m_delta*(co.m_normals[k].Y+co.m_normals[k].X*dx))}) 4838 | co.m_destPoly = append(co.m_destPoly, &IntPoint{ 4839 | co.Round(float64(co.m_srcPoly[j].X) + 4840 | co.m_delta*(co.m_normals[j].X+co.m_normals[j].Y*dx)), 4841 | co.Round(float64(co.m_srcPoly[j].Y) + 4842 | co.m_delta*(co.m_normals[j].Y-co.m_normals[j].X*dx))}) 4843 | } 4844 | 4845 | //------------------------------------------------------------------------------ 4846 | 4847 | func (co *ClipperOffset) DoMiter(j, k int, r float64) { 4848 | q := co.m_delta / r 4849 | co.m_destPoly = append(co.m_destPoly, &IntPoint{co.Round( 4850 | float64(co.m_srcPoly[j].X) + (co.m_normals[k].X+co.m_normals[j].X)*q), 4851 | co.Round(float64(co.m_srcPoly[j].Y) + 4852 | (co.m_normals[k].Y+co.m_normals[j].Y)*q)}) 4853 | } 4854 | 4855 | //------------------------------------------------------------------------------ 4856 | 4857 | func (co *ClipperOffset) DoRound(j, k int) { 4858 | a := math.Atan2(co.m_sinA, 4859 | co.m_normals[k].X*co.m_normals[j].X+co.m_normals[k].Y*co.m_normals[j].Y) 4860 | steps := int(co.Round(co.m_StepsPerRad * math.Abs(a))) 4861 | 4862 | X := co.m_normals[k].X 4863 | Y := co.m_normals[k].Y 4864 | var X2 float64 4865 | for i := 0; i < steps; i++ { 4866 | co.m_destPoly = append(co.m_destPoly, &IntPoint{ 4867 | co.Round(float64(co.m_srcPoly[j].X) + X*co.m_delta), 4868 | co.Round(float64(co.m_srcPoly[j].Y) + Y*co.m_delta)}) 4869 | X2 = X 4870 | X = X*co.m_cos - co.m_sin*Y 4871 | Y = X2*co.m_sin + Y*co.m_cos 4872 | } 4873 | co.m_destPoly = append(co.m_destPoly, &IntPoint{ 4874 | co.Round(float64(co.m_srcPoly[j].X) + co.m_normals[j].X*co.m_delta), 4875 | co.Round(float64(co.m_srcPoly[j].Y) + co.m_normals[j].Y*co.m_delta)}) 4876 | } 4877 | 4878 | //------------------------------------------------------------------------------ 4879 | 4880 | type ClipperException struct { 4881 | s string 4882 | } 4883 | 4884 | func NewClipperException(s string) *ClipperException { 4885 | e := new(ClipperException) 4886 | e.s = s 4887 | return e 4888 | } 4889 | 4890 | func (e ClipperException) Error() string { 4891 | return e.s 4892 | } 4893 | 4894 | func intAbs(i int) int { 4895 | if i > 0 { 4896 | return i 4897 | } else { 4898 | return i * -1 4899 | } 4900 | } 4901 | func min(a, b CInt) CInt { 4902 | if a < b { 4903 | return a 4904 | } else { 4905 | return b 4906 | } 4907 | } 4908 | func max(a, b CInt) CInt { 4909 | if a > b { 4910 | return a 4911 | } else { 4912 | return b 4913 | } 4914 | } 4915 | --------------------------------------------------------------------------------