1
0
Fork 0
mirror of https://github.com/Luzifer/staticmap.git synced 2024-12-20 12:51:18 +00:00

Vendor dependencies

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2017-06-27 22:50:36 +02:00
parent 4414239854
commit 759b968510
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
392 changed files with 161865 additions and 0 deletions

126
Godeps/Godeps.json generated Normal file
View file

@ -0,0 +1,126 @@
{
"ImportPath": "github.com/Luzifer/staticmap",
"GoVersion": "go1.8",
"GodepVersion": "v79",
"Deps": [
{
"ImportPath": "github.com/Luzifer/go_helpers/accessLogger",
"Comment": "v2.2.0",
"Rev": "e31c3a2659d3f4901f696692cfe98bd0eb5168f9"
},
{
"ImportPath": "github.com/Luzifer/go_helpers/http",
"Comment": "v2.2.0",
"Rev": "e31c3a2659d3f4901f696692cfe98bd0eb5168f9"
},
{
"ImportPath": "github.com/Luzifer/rconfig",
"Comment": "v1.2.0",
"Rev": "7aef1d393c1e2d0758901853b59981c7adc67c7e"
},
{
"ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.10.0-38-g3ec0642",
"Rev": "3ec0642a7fb6488f65b06f9040adc67e3990296a"
},
{
"ImportPath": "github.com/Wessie/appdirs",
"Rev": "6573e894f8e294cbae0c4e45c25ff9f2e2918a4e"
},
{
"ImportPath": "github.com/flopp/go-coordsparser",
"Rev": "845bca739e263e1cd38de25024a47b4d6acbfc1f"
},
{
"ImportPath": "github.com/flopp/go-staticmaps",
"Rev": "e8779c98399f6efad291d6504990daceeb9940a9"
},
{
"ImportPath": "github.com/fogleman/gg",
"Comment": "v1.0.0-10-gee8994f",
"Rev": "ee8994ff90057955c428a5a949da5d064bf3ce6b"
},
{
"ImportPath": "github.com/golang/freetype/raster",
"Comment": "release-131-g38b4c39",
"Rev": "38b4c392adc5eed94207994c4848fff99f4ac234"
},
{
"ImportPath": "github.com/golang/freetype/truetype",
"Comment": "release-131-g38b4c39",
"Rev": "38b4c392adc5eed94207994c4848fff99f4ac234"
},
{
"ImportPath": "github.com/golang/geo/r1",
"Rev": "f65fe014169924880aa2c95d7707c2da435534b9"
},
{
"ImportPath": "github.com/golang/geo/r2",
"Rev": "f65fe014169924880aa2c95d7707c2da435534b9"
},
{
"ImportPath": "github.com/golang/geo/r3",
"Rev": "f65fe014169924880aa2c95d7707c2da435534b9"
},
{
"ImportPath": "github.com/golang/geo/s1",
"Rev": "f65fe014169924880aa2c95d7707c2da435534b9"
},
{
"ImportPath": "github.com/golang/geo/s2",
"Rev": "f65fe014169924880aa2c95d7707c2da435534b9"
},
{
"ImportPath": "github.com/gorilla/context",
"Rev": "1c83b3eabd45b6d76072b66b746c20815fb2872d"
},
{
"ImportPath": "github.com/gorilla/mux",
"Rev": "49c024275504f0341e5a9971eb7ba7fa3dc7af40"
},
{
"ImportPath": "github.com/lucasb-eyer/go-colorful",
"Rev": "c900de9dbbc73129068f5af6a823068fc5f2308c"
},
{
"ImportPath": "github.com/spf13/pflag",
"Rev": "c7e63cf4530bcd3ba943729cee0efeff2ebea63f"
},
{
"ImportPath": "github.com/tkrajina/gpxgo/gpx",
"Rev": "7848cf26f5a58b4a4e23b89a4b67cfc3d52dd042"
},
{
"ImportPath": "golang.org/x/image/draw",
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
},
{
"ImportPath": "golang.org/x/image/font",
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
},
{
"ImportPath": "golang.org/x/image/font/basicfont",
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
},
{
"ImportPath": "golang.org/x/image/math/f64",
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
},
{
"ImportPath": "golang.org/x/image/math/fixed",
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9"
},
{
"ImportPath": "gopkg.in/validator.v2",
"Rev": "07ffaad256c8e957050ad83d6472eb97d785013d"
},
{
"ImportPath": "gopkg.in/yaml.v2",
"Rev": "31c299268d302dd0aa9a0dcf765a3d58971ac83f"
}
]
}

5
Godeps/Readme generated Normal file
View file

@ -0,0 +1,5 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View file

@ -0,0 +1,37 @@
package accessLogger
import (
"fmt"
"net/http"
"strconv"
)
type AccessLogResponseWriter struct {
StatusCode int
Size int
http.ResponseWriter
}
func New(res http.ResponseWriter) *AccessLogResponseWriter {
return &AccessLogResponseWriter{
StatusCode: 200,
Size: 0,
ResponseWriter: res,
}
}
func (a *AccessLogResponseWriter) Write(out []byte) (int, error) {
s, err := a.ResponseWriter.Write(out)
a.Size += s
return s, err
}
func (a *AccessLogResponseWriter) WriteHeader(code int) {
a.StatusCode = code
a.ResponseWriter.WriteHeader(code)
}
func (a *AccessLogResponseWriter) HTTPResponseType() string {
return fmt.Sprintf("%sxx", strconv.FormatInt(int64(a.StatusCode), 10)[0])
}

View file

@ -0,0 +1,35 @@
package http
import (
"log"
"net/http"
"time"
"github.com/Luzifer/go_helpers/accessLogger"
)
type HTTPLogHandler struct {
Handler http.Handler
}
func NewHTTPLogHandler(h http.Handler) http.Handler {
return HTTPLogHandler{Handler: h}
}
func (l HTTPLogHandler) ServeHTTP(res http.ResponseWriter, r *http.Request) {
start := time.Now()
ares := accessLogger.New(res)
l.Handler.ServeHTTP(ares, r)
log.Printf("%s - \"%s %s\" %d %d \"%s\" \"%s\" %s",
r.RemoteAddr,
r.Method,
r.URL.Path,
ares.StatusCode,
ares.Size,
r.Header.Get("Referer"),
r.Header.Get("User-Agent"),
time.Since(start),
)
}

8
vendor/github.com/Luzifer/rconfig/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,8 @@
language: go
go:
- 1.6
- 1.7
- tip
script: go test -v -race -cover ./...

9
vendor/github.com/Luzifer/rconfig/History.md generated vendored Normal file
View file

@ -0,0 +1,9 @@
# 1.2.0 / 2017-06-19
* Add ParseAndValidate method
# 1.1.0 / 2016-06-28
* Support time.Duration config parameters
* Added goreportcard badge
* Added testcase for using bool with ENV and default

13
vendor/github.com/Luzifer/rconfig/LICENSE generated vendored Normal file
View file

@ -0,0 +1,13 @@
Copyright 2015 Knut Ahlers <knut@ahlers.me>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

87
vendor/github.com/Luzifer/rconfig/README.md generated vendored Normal file
View file

@ -0,0 +1,87 @@
[![Build Status](https://travis-ci.org/Luzifer/rconfig.svg?branch=master)](https://travis-ci.org/Luzifer/rconfig)
[![License: Apache v2.0](https://badge.luzifer.io/v1/badge?color=5d79b5&title=license&text=Apache+v2.0)](http://www.apache.org/licenses/LICENSE-2.0)
[![Documentation](https://badge.luzifer.io/v1/badge?title=godoc&text=reference)](https://godoc.org/github.com/Luzifer/rconfig)
[![Go Report](http://goreportcard.com/badge/Luzifer/rconfig)](http://goreportcard.com/report/Luzifer/rconfig)
## Description
> Package rconfig implements a CLI configuration reader with struct-embedded defaults, environment variables and posix compatible flag parsing using the [pflag](https://github.com/spf13/pflag) library.
## Installation
Install by running:
```
go get -u github.com/Luzifer/rconfig
```
OR fetch a specific version:
```
go get -u gopkg.in/luzifer/rconfig.v1
```
Run tests by running:
```
go test -v -race -cover github.com/Luzifer/rconfig
```
## Usage
A very simple usecase is to just configure a struct inside the vars section of your `main.go` and to parse the commandline flags from the `main()` function:
```go
package main
import (
"fmt"
"github.com/Luzifer/rconfig"
)
var (
cfg = struct {
Username string `default:"unknown" flag:"user" description:"Your name"`
Details struct {
Age int `default:"25" flag:"age" env:"age" description:"Your age"`
}
}{}
)
func main() {
rconfig.Parse(&cfg)
fmt.Printf("Hello %s, happy birthday for your %dth birthday.",
cfg.Username,
cfg.Details.Age)
}
```
### Provide variable defaults by using a file
Given you have a file `~/.myapp.yml` containing some secrets or usernames (for the example below username is assumed to be "luzifer") as a default configuration for your application you can use this source code to load the defaults from that file using the `vardefault` tag in your configuration struct.
The order of the directives (lower number = higher precedence):
1. Flags provided in command line
1. Environment variables
1. Variable defaults (`vardefault` tag in the struct)
1. `default` tag in the struct
```go
var cfg = struct {
Username string `vardefault:"username" flag:"username" description:"Your username"`
}
func main() {
rconfig.SetVariableDefaults(rconfig.VarDefaultsFromYAMLFile("~/.myapp.yml"))
rconfig.Parse(&cfg)
fmt.Printf("Username = %s", cfg.Username)
// Output: Username = luzifer
}
```
## More info
You can see the full reference documentation of the rconfig package [at godoc.org](https://godoc.org/github.com/Luzifer/rconfig), or through go's standard documentation system by running `godoc -http=:6060` and browsing to [http://localhost:6060/pkg/github.com/Luzifer/rconfig](http://localhost:6060/pkg/github.com/Luzifer/rconfig) after installation.

356
vendor/github.com/Luzifer/rconfig/config.go generated vendored Normal file
View file

@ -0,0 +1,356 @@
// Package rconfig implements a CLI configuration reader with struct-embedded
// defaults, environment variables and posix compatible flag parsing using
// the pflag library.
package rconfig
import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
"strings"
"time"
"github.com/spf13/pflag"
validator "gopkg.in/validator.v2"
)
var (
fs *pflag.FlagSet
variableDefaults map[string]string
)
func init() {
variableDefaults = make(map[string]string)
}
// Parse takes the pointer to a struct filled with variables which should be read
// from ENV, default or flag. The precedence in this is flag > ENV > default. So
// if a flag is specified on the CLI it will overwrite the ENV and otherwise ENV
// overwrites the default specified.
//
// For your configuration struct you can use the following struct-tags to control
// the behavior of rconfig:
//
// default: Set a default value
// vardefault: Read the default value from the variable defaults
// env: Read the value from this environment variable
// flag: Flag to read in format "long,short" (for example "listen,l")
// description: A help text for Usage output to guide your users
//
// The format you need to specify those values you can see in the example to this
// function.
//
func Parse(config interface{}) error {
return parse(config, nil)
}
// ParseAndValidate works exactly like Parse but implements an additional run of
// the go-validator package on the configuration struct. Therefore additonal struct
// tags are supported like described in the readme file of the go-validator package:
//
// https://github.com/go-validator/validator/tree/v2#usage
func ParseAndValidate(config interface{}) error {
return parseAndValidate(config, nil)
}
// Args returns the non-flag command-line arguments.
func Args() []string {
return fs.Args()
}
// Usage prints a basic usage with the corresponding defaults for the flags to
// os.Stdout. The defaults are derived from the `default` struct-tag and the ENV.
func Usage() {
if fs != nil && fs.Parsed() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fs.PrintDefaults()
}
}
// SetVariableDefaults presets the parser with a map of default values to be used
// when specifying the vardefault tag
func SetVariableDefaults(defaults map[string]string) {
variableDefaults = defaults
}
func parseAndValidate(in interface{}, args []string) error {
if err := parse(in, args); err != nil {
return err
}
return validator.Validate(in)
}
func parse(in interface{}, args []string) error {
if args == nil {
args = os.Args
}
fs = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
if err := execTags(in, fs); err != nil {
return err
}
return fs.Parse(args)
}
func execTags(in interface{}, fs *pflag.FlagSet) error {
if reflect.TypeOf(in).Kind() != reflect.Ptr {
return errors.New("Calling parser with non-pointer")
}
if reflect.ValueOf(in).Elem().Kind() != reflect.Struct {
return errors.New("Calling parser with pointer to non-struct")
}
st := reflect.ValueOf(in).Elem()
for i := 0; i < st.NumField(); i++ {
valField := st.Field(i)
typeField := st.Type().Field(i)
if typeField.Tag.Get("default") == "" && typeField.Tag.Get("env") == "" && typeField.Tag.Get("flag") == "" && typeField.Type.Kind() != reflect.Struct {
// None of our supported tags is present and it's not a sub-struct
continue
}
value := varDefault(typeField.Tag.Get("vardefault"), typeField.Tag.Get("default"))
value = envDefault(typeField.Tag.Get("env"), value)
parts := strings.Split(typeField.Tag.Get("flag"), ",")
switch typeField.Type {
case reflect.TypeOf(time.Duration(0)):
v, err := time.ParseDuration(value)
if err != nil {
if value == "" {
v = time.Duration(0)
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
if len(parts) == 1 {
fs.DurationVar(valField.Addr().Interface().(*time.Duration), parts[0], v, typeField.Tag.Get("description"))
} else {
fs.DurationVarP(valField.Addr().Interface().(*time.Duration), parts[0], parts[1], v, typeField.Tag.Get("description"))
}
} else {
valField.Set(reflect.ValueOf(v))
}
continue
}
switch typeField.Type.Kind() {
case reflect.String:
if typeField.Tag.Get("flag") != "" {
if len(parts) == 1 {
fs.StringVar(valField.Addr().Interface().(*string), parts[0], value, typeField.Tag.Get("description"))
} else {
fs.StringVarP(valField.Addr().Interface().(*string), parts[0], parts[1], value, typeField.Tag.Get("description"))
}
} else {
valField.SetString(value)
}
case reflect.Bool:
v := value == "true"
if typeField.Tag.Get("flag") != "" {
if len(parts) == 1 {
fs.BoolVar(valField.Addr().Interface().(*bool), parts[0], v, typeField.Tag.Get("description"))
} else {
fs.BoolVarP(valField.Addr().Interface().(*bool), parts[0], parts[1], v, typeField.Tag.Get("description"))
}
} else {
valField.SetBool(v)
}
case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64:
vt, err := strconv.ParseInt(value, 10, 64)
if err != nil {
if value == "" {
vt = 0
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
registerFlagInt(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
} else {
valField.SetInt(vt)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
vt, err := strconv.ParseUint(value, 10, 64)
if err != nil {
if value == "" {
vt = 0
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
registerFlagUint(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
} else {
valField.SetUint(vt)
}
case reflect.Float32, reflect.Float64:
vt, err := strconv.ParseFloat(value, 64)
if err != nil {
if value == "" {
vt = 0.0
} else {
return err
}
}
if typeField.Tag.Get("flag") != "" {
registerFlagFloat(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
} else {
valField.SetFloat(vt)
}
case reflect.Struct:
if err := execTags(valField.Addr().Interface(), fs); err != nil {
return err
}
case reflect.Slice:
switch typeField.Type.Elem().Kind() {
case reflect.Int:
def := []int{}
for _, v := range strings.Split(value, ",") {
it, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
if err != nil {
return err
}
def = append(def, int(it))
}
if len(parts) == 1 {
fs.IntSliceVar(valField.Addr().Interface().(*[]int), parts[0], def, typeField.Tag.Get("description"))
} else {
fs.IntSliceVarP(valField.Addr().Interface().(*[]int), parts[0], parts[1], def, typeField.Tag.Get("description"))
}
case reflect.String:
del := typeField.Tag.Get("delimiter")
if len(del) == 0 {
del = ","
}
def := strings.Split(value, del)
if len(parts) == 1 {
fs.StringSliceVar(valField.Addr().Interface().(*[]string), parts[0], def, typeField.Tag.Get("description"))
} else {
fs.StringSliceVarP(valField.Addr().Interface().(*[]string), parts[0], parts[1], def, typeField.Tag.Get("description"))
}
}
}
}
return nil
}
func registerFlagFloat(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt float64, desc string) {
switch t {
case reflect.Float32:
if len(parts) == 1 {
fs.Float32Var(field.(*float32), parts[0], float32(vt), desc)
} else {
fs.Float32VarP(field.(*float32), parts[0], parts[1], float32(vt), desc)
}
case reflect.Float64:
if len(parts) == 1 {
fs.Float64Var(field.(*float64), parts[0], float64(vt), desc)
} else {
fs.Float64VarP(field.(*float64), parts[0], parts[1], float64(vt), desc)
}
}
}
func registerFlagInt(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt int64, desc string) {
switch t {
case reflect.Int:
if len(parts) == 1 {
fs.IntVar(field.(*int), parts[0], int(vt), desc)
} else {
fs.IntVarP(field.(*int), parts[0], parts[1], int(vt), desc)
}
case reflect.Int8:
if len(parts) == 1 {
fs.Int8Var(field.(*int8), parts[0], int8(vt), desc)
} else {
fs.Int8VarP(field.(*int8), parts[0], parts[1], int8(vt), desc)
}
case reflect.Int32:
if len(parts) == 1 {
fs.Int32Var(field.(*int32), parts[0], int32(vt), desc)
} else {
fs.Int32VarP(field.(*int32), parts[0], parts[1], int32(vt), desc)
}
case reflect.Int64:
if len(parts) == 1 {
fs.Int64Var(field.(*int64), parts[0], int64(vt), desc)
} else {
fs.Int64VarP(field.(*int64), parts[0], parts[1], int64(vt), desc)
}
}
}
func registerFlagUint(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt uint64, desc string) {
switch t {
case reflect.Uint:
if len(parts) == 1 {
fs.UintVar(field.(*uint), parts[0], uint(vt), desc)
} else {
fs.UintVarP(field.(*uint), parts[0], parts[1], uint(vt), desc)
}
case reflect.Uint8:
if len(parts) == 1 {
fs.Uint8Var(field.(*uint8), parts[0], uint8(vt), desc)
} else {
fs.Uint8VarP(field.(*uint8), parts[0], parts[1], uint8(vt), desc)
}
case reflect.Uint16:
if len(parts) == 1 {
fs.Uint16Var(field.(*uint16), parts[0], uint16(vt), desc)
} else {
fs.Uint16VarP(field.(*uint16), parts[0], parts[1], uint16(vt), desc)
}
case reflect.Uint32:
if len(parts) == 1 {
fs.Uint32Var(field.(*uint32), parts[0], uint32(vt), desc)
} else {
fs.Uint32VarP(field.(*uint32), parts[0], parts[1], uint32(vt), desc)
}
case reflect.Uint64:
if len(parts) == 1 {
fs.Uint64Var(field.(*uint64), parts[0], uint64(vt), desc)
} else {
fs.Uint64VarP(field.(*uint64), parts[0], parts[1], uint64(vt), desc)
}
}
}
func envDefault(env, def string) string {
value := def
if env != "" {
if e := os.Getenv(env); e != "" {
value = e
}
}
return value
}
func varDefault(name, def string) string {
value := def
if name != "" {
if v, ok := variableDefaults[name]; ok {
value = v
}
}
return value
}

View file

@ -0,0 +1,27 @@
package rconfig
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
// VarDefaultsFromYAMLFile reads contents of a file and calls VarDefaultsFromYAML
func VarDefaultsFromYAMLFile(filename string) map[string]string {
data, err := ioutil.ReadFile(filename)
if err != nil {
return make(map[string]string)
}
return VarDefaultsFromYAML(data)
}
// VarDefaultsFromYAML creates a vardefaults map from YAML raw data
func VarDefaultsFromYAML(in []byte) map[string]string {
out := make(map[string]string)
err := yaml.Unmarshal(in, &out)
if err != nil {
return make(map[string]string)
}
return out
}

1
vendor/github.com/Sirupsen/logrus/.gitignore generated vendored Normal file
View file

@ -0,0 +1 @@
logrus

10
vendor/github.com/Sirupsen/logrus/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,10 @@
language: go
go:
- 1.3
- 1.4
- 1.5
- 1.6
- tip
install:
- go get -t ./...
script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...

66
vendor/github.com/Sirupsen/logrus/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,66 @@
# 0.10.0
* feature: Add a test hook (#180)
* feature: `ParseLevel` is now case-insensitive (#326)
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
* performance: avoid re-allocations on `WithFields` (#335)
# 0.9.0
* logrus/text_formatter: don't emit empty msg
* logrus/hooks/airbrake: move out of main repository
* logrus/hooks/sentry: move out of main repository
* logrus/hooks/papertrail: move out of main repository
* logrus/hooks/bugsnag: move out of main repository
* logrus/core: run tests with `-race`
* logrus/core: detect TTY based on `stderr`
* logrus/core: support `WithError` on logger
* logrus/core: Solaris support
# 0.8.7
* logrus/core: fix possible race (#216)
* logrus/doc: small typo fixes and doc improvements
# 0.8.6
* hooks/raven: allow passing an initialized client
# 0.8.5
* logrus/core: revert #208
# 0.8.4
* formatter/text: fix data race (#218)
# 0.8.3
* logrus/core: fix entry log level (#208)
* logrus/core: improve performance of text formatter by 40%
* logrus/core: expose `LevelHooks` type
* logrus/core: add support for DragonflyBSD and NetBSD
* formatter/text: print structs more verbosely
# 0.8.2
* logrus: fix more Fatal family functions
# 0.8.1
* logrus: fix not exiting on `Fatalf` and `Fatalln`
# 0.8.0
* logrus: defaults to stderr instead of stdout
* hooks/sentry: add special field for `*http.Request`
* formatter/text: ignore Windows for colors
# 0.7.3
* formatter/\*: allow configuration of timestamp layout
# 0.7.2
* formatter/text: Add configuration option for time format (#158)

21
vendor/github.com/Sirupsen/logrus/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Simon Eskildsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

421
vendor/github.com/Sirupsen/logrus/README.md generated vendored Normal file
View file

@ -0,0 +1,421 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
many large deployments. The core API is unlikely to change much but please
version control your Logrus to make sure you aren't fetching latest `master` on
every build.**
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
![Colored](http://i.imgur.com/PY7qMwd.png)
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
or Splunk:
```json
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
```
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
exit status 1
```
#### Example
The simplest way to use Logrus is simply the package-level exported logger:
```go
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
```
Note that it's completely api-compatible with the stdlib logger, so you can
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you
want:
```go
package main
import (
"os"
log "github.com/Sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Output to stderr instead of stdout, could also be a file.
log.SetOutput(os.Stderr)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
```
For more advanced usage such as logging to multiple locations from the same
application, you can also create an instance of the `logrus` Logger:
```go
package main
import (
"github.com/Sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log.Out = os.Stderr
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
```
#### Fields
Logrus encourages careful, structured logging though logging fields instead of
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
to send event %s to topic %s with key %d")`, you should log the much more
discoverable:
```go
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
```
We've found this API forces you to think about logging in a way that produces
much more useful logging messages. We've been in countless situations where just
a single added field to a log statement that was already there would've saved us
hours. The `WithFields` call is optional.
In general, with Logrus using any of the `printf`-family functions should be
seen as a hint you should add a field, however, you can still use the
`printf`-family functions with Logrus.
#### Hooks
You can add hooks for logging levels. For example to send errors to an exception
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
multiple places simultaneously, e.g. syslog.
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
`init`:
```go
import (
log "github.com/Sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
log.Error("Unable to connect to local syslog daemon")
} else {
log.AddHook(hook)
}
}
```
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
| Hook | Description |
| ----- | ----------- |
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
#### Level logging
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
```go
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
log.Fatal("Bye.")
// Calls panic() after logging
log.Panic("I'm bailing.")
```
You can set the logging level on a `Logger`, then it will only log entries with
that severity or anything above it:
```go
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log.SetLevel(log.InfoLevel)
```
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
environment if your application has that.
#### Entries
Besides the fields added with `WithField` or `WithFields` some fields are
automatically added to all logging events:
1. `time`. The timestamp when the entry was created.
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
the `AddFields` call. E.g. `Failed to send event.`
3. `level`. The logging level. E.g. `info`.
#### Environments
Logrus has no notion of environment.
If you wish for hooks and formatters to only be used in specific environments,
you should handle that yourself. For example, if your application has a global
variable `Environment`, which is a string representation of the environment you
could do:
```go
import (
log "github.com/Sirupsen/logrus"
)
init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log.SetFormatter(&log.JSONFormatter{})
} else {
// The TextFormatter is default, you don't actually have to do this.
log.SetFormatter(&log.TextFormatter{})
}
}
```
This configuration is how `logrus` was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.
#### Formatters
The built-in logging formatters are:
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
without colors.
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
field to `true`. To force no colored output even if there is a TTY set the
`DisableColors` field to `true`
* `logrus.JSONFormatter`. Logs fields as JSON.
Third party logging formatters:
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
`Fields` type (`map[string]interface{}`) with all your fields as well as the
default ones (see Entries section above):
```go
type MyJSONFormatter struct {
}
log.SetFormatter(new(MyJSONFormatter))
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized, err := json.Marshal(entry.Data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
```
#### Logger as an `io.Writer`
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}
```
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
#### Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like `logrotate(8)`) that can compress and delete old log
entries. It should not be a feature of the application-level logger.
#### Tools
| Tool | Description |
| ---- | ----------- |
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
#### Testing
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
```go
logger, hook := NewNullLogger()
logger.Error("Hello error")
assert.Equal(1, len(hook.Entries))
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
hook.Reset()
assert.Nil(hook.LastEntry())
```
#### Fatal handlers
Logrus can register one or more functions that will be called when any `fatal`
level message is logged. The registered handlers will be executed before
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
```
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...
```
#### Thread safty
By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
Situation when locking is not needed includes:
* You have no hooks registered, or hooks calling is already thread-safe.
* Writing to logger.Out is already thread-safe, for example:
1) logger.Out is protected by locks.
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)

64
vendor/github.com/Sirupsen/logrus/alt_exit.go generated vendored Normal file
View file

@ -0,0 +1,64 @@
package logrus
// The following code was sourced and modified from the
// https://bitbucket.org/tebeka/atexit package governed by the following license:
//
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import (
"fmt"
"os"
)
var handlers = []func(){}
func runHandler(handler func()) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
}
}()
handler()
}
func runHandlers() {
for _, handler := range handlers {
runHandler(handler)
}
}
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
func Exit(code int) {
runHandlers()
os.Exit(code)
}
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
// all handlers. The handlers will also be invoked when any Fatal log entry is
// made.
//
// This method is useful when a caller wishes to use logrus to log a fatal
// message but also needs to gracefully shutdown. An example usecase could be
// closing database connections, or sending a alert that the application is
// closing.
func RegisterExitHandler(handler func()) {
handlers = append(handlers, handler)
}

26
vendor/github.com/Sirupsen/logrus/doc.go generated vendored Normal file
View file

@ -0,0 +1,26 @@
/*
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"number": 1,
"size": 10,
}).Info("A walrus appears")
}
Output:
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
For a full guide visit https://github.com/Sirupsen/logrus
*/
package logrus

275
vendor/github.com/Sirupsen/logrus/entry.go generated vendored Normal file
View file

@ -0,0 +1,275 @@
package logrus
import (
"bytes"
"fmt"
"os"
"sync"
"time"
)
var bufferPool *sync.Pool
func init() {
bufferPool = &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
}
// Defines the key when adding errors using WithError.
var ErrorKey = "error"
// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
// passed around as much as you wish to avoid field duplication.
type Entry struct {
Logger *Logger
// Contains all the fields set by the user.
Data Fields
// Time at which the log entry was created
Time time.Time
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
Level Level
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
Message string
// When formatter is called in entry.log(), an Buffer may be set to entry
Buffer *bytes.Buffer
}
func NewEntry(logger *Logger) *Entry {
return &Entry{
Logger: logger,
// Default is three fields, give a little extra room
Data: make(Fields, 5),
}
}
// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil {
return "", err
}
str := string(serialized)
return str, nil
}
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
return entry.WithField(ErrorKey, err)
}
// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
return entry.WithFields(Fields{key: value})
}
// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
data := make(Fields, len(entry.Data)+len(fields))
for k, v := range entry.Data {
data[k] = v
}
for k, v := range fields {
data[k] = v
}
return &Entry{Logger: entry.Logger, Data: data}
}
// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
var buffer *bytes.Buffer
entry.Time = time.Now()
entry.Level = level
entry.Message = msg
if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
entry.Logger.mu.Lock()
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
entry.Logger.mu.Unlock()
}
buffer = bufferPool.Get().(*bytes.Buffer)
buffer.Reset()
defer bufferPool.Put(buffer)
entry.Buffer = buffer
serialized, err := entry.Logger.Formatter.Format(&entry)
entry.Buffer = nil
if err != nil {
entry.Logger.mu.Lock()
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
entry.Logger.mu.Unlock()
} else {
entry.Logger.mu.Lock()
_, err = entry.Logger.Out.Write(serialized)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
}
entry.Logger.mu.Unlock()
}
// To avoid Entry#log() returning a value that only would make sense for
// panic() to use in Entry#Panic(), we avoid the allocation by checking
// directly here.
if level <= PanicLevel {
panic(&entry)
}
}
func (entry *Entry) Debug(args ...interface{}) {
if entry.Logger.Level >= DebugLevel {
entry.log(DebugLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Print(args ...interface{}) {
entry.Info(args...)
}
func (entry *Entry) Info(args ...interface{}) {
if entry.Logger.Level >= InfoLevel {
entry.log(InfoLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Warn(args ...interface{}) {
if entry.Logger.Level >= WarnLevel {
entry.log(WarnLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Warning(args ...interface{}) {
entry.Warn(args...)
}
func (entry *Entry) Error(args ...interface{}) {
if entry.Logger.Level >= ErrorLevel {
entry.log(ErrorLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Fatal(args ...interface{}) {
if entry.Logger.Level >= FatalLevel {
entry.log(FatalLevel, fmt.Sprint(args...))
}
Exit(1)
}
func (entry *Entry) Panic(args ...interface{}) {
if entry.Logger.Level >= PanicLevel {
entry.log(PanicLevel, fmt.Sprint(args...))
}
panic(fmt.Sprint(args...))
}
// Entry Printf family functions
func (entry *Entry) Debugf(format string, args ...interface{}) {
if entry.Logger.Level >= DebugLevel {
entry.Debug(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Infof(format string, args ...interface{}) {
if entry.Logger.Level >= InfoLevel {
entry.Info(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Printf(format string, args ...interface{}) {
entry.Infof(format, args...)
}
func (entry *Entry) Warnf(format string, args ...interface{}) {
if entry.Logger.Level >= WarnLevel {
entry.Warn(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Warningf(format string, args ...interface{}) {
entry.Warnf(format, args...)
}
func (entry *Entry) Errorf(format string, args ...interface{}) {
if entry.Logger.Level >= ErrorLevel {
entry.Error(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Fatalf(format string, args ...interface{}) {
if entry.Logger.Level >= FatalLevel {
entry.Fatal(fmt.Sprintf(format, args...))
}
Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
if entry.Logger.Level >= PanicLevel {
entry.Panic(fmt.Sprintf(format, args...))
}
}
// Entry Println family functions
func (entry *Entry) Debugln(args ...interface{}) {
if entry.Logger.Level >= DebugLevel {
entry.Debug(entry.sprintlnn(args...))
}
}
func (entry *Entry) Infoln(args ...interface{}) {
if entry.Logger.Level >= InfoLevel {
entry.Info(entry.sprintlnn(args...))
}
}
func (entry *Entry) Println(args ...interface{}) {
entry.Infoln(args...)
}
func (entry *Entry) Warnln(args ...interface{}) {
if entry.Logger.Level >= WarnLevel {
entry.Warn(entry.sprintlnn(args...))
}
}
func (entry *Entry) Warningln(args ...interface{}) {
entry.Warnln(args...)
}
func (entry *Entry) Errorln(args ...interface{}) {
if entry.Logger.Level >= ErrorLevel {
entry.Error(entry.sprintlnn(args...))
}
}
func (entry *Entry) Fatalln(args ...interface{}) {
if entry.Logger.Level >= FatalLevel {
entry.Fatal(entry.sprintlnn(args...))
}
Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
if entry.Logger.Level >= PanicLevel {
entry.Panic(entry.sprintlnn(args...))
}
}
// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
msg := fmt.Sprintln(args...)
return msg[:len(msg)-1]
}

193
vendor/github.com/Sirupsen/logrus/exported.go generated vendored Normal file
View file

@ -0,0 +1,193 @@
package logrus
import (
"io"
)
var (
// std is the name of the standard logger in stdlib `log`
std = New()
)
func StandardLogger() *Logger {
return std
}
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
std.mu.Lock()
defer std.mu.Unlock()
std.Out = out
}
// SetFormatter sets the standard logger formatter.
func SetFormatter(formatter Formatter) {
std.mu.Lock()
defer std.mu.Unlock()
std.Formatter = formatter
}
// SetLevel sets the standard logger level.
func SetLevel(level Level) {
std.mu.Lock()
defer std.mu.Unlock()
std.Level = level
}
// GetLevel returns the standard logger level.
func GetLevel() Level {
std.mu.Lock()
defer std.mu.Unlock()
return std.Level
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook Hook) {
std.mu.Lock()
defer std.mu.Unlock()
std.Hooks.Add(hook)
}
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
func WithError(err error) *Entry {
return std.WithField(ErrorKey, err)
}
// WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithField(key string, value interface{}) *Entry {
return std.WithField(key, value)
}
// WithFields creates an entry from the standard logger and adds multiple
// fields to it. This is simply a helper for `WithField`, invoking it
// once for each field.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithFields(fields Fields) *Entry {
return std.WithFields(fields)
}
// Debug logs a message at level Debug on the standard logger.
func Debug(args ...interface{}) {
std.Debug(args...)
}
// Print logs a message at level Info on the standard logger.
func Print(args ...interface{}) {
std.Print(args...)
}
// Info logs a message at level Info on the standard logger.
func Info(args ...interface{}) {
std.Info(args...)
}
// Warn logs a message at level Warn on the standard logger.
func Warn(args ...interface{}) {
std.Warn(args...)
}
// Warning logs a message at level Warn on the standard logger.
func Warning(args ...interface{}) {
std.Warning(args...)
}
// Error logs a message at level Error on the standard logger.
func Error(args ...interface{}) {
std.Error(args...)
}
// Panic logs a message at level Panic on the standard logger.
func Panic(args ...interface{}) {
std.Panic(args...)
}
// Fatal logs a message at level Fatal on the standard logger.
func Fatal(args ...interface{}) {
std.Fatal(args...)
}
// Debugf logs a message at level Debug on the standard logger.
func Debugf(format string, args ...interface{}) {
std.Debugf(format, args...)
}
// Printf logs a message at level Info on the standard logger.
func Printf(format string, args ...interface{}) {
std.Printf(format, args...)
}
// Infof logs a message at level Info on the standard logger.
func Infof(format string, args ...interface{}) {
std.Infof(format, args...)
}
// Warnf logs a message at level Warn on the standard logger.
func Warnf(format string, args ...interface{}) {
std.Warnf(format, args...)
}
// Warningf logs a message at level Warn on the standard logger.
func Warningf(format string, args ...interface{}) {
std.Warningf(format, args...)
}
// Errorf logs a message at level Error on the standard logger.
func Errorf(format string, args ...interface{}) {
std.Errorf(format, args...)
}
// Panicf logs a message at level Panic on the standard logger.
func Panicf(format string, args ...interface{}) {
std.Panicf(format, args...)
}
// Fatalf logs a message at level Fatal on the standard logger.
func Fatalf(format string, args ...interface{}) {
std.Fatalf(format, args...)
}
// Debugln logs a message at level Debug on the standard logger.
func Debugln(args ...interface{}) {
std.Debugln(args...)
}
// Println logs a message at level Info on the standard logger.
func Println(args ...interface{}) {
std.Println(args...)
}
// Infoln logs a message at level Info on the standard logger.
func Infoln(args ...interface{}) {
std.Infoln(args...)
}
// Warnln logs a message at level Warn on the standard logger.
func Warnln(args ...interface{}) {
std.Warnln(args...)
}
// Warningln logs a message at level Warn on the standard logger.
func Warningln(args ...interface{}) {
std.Warningln(args...)
}
// Errorln logs a message at level Error on the standard logger.
func Errorln(args ...interface{}) {
std.Errorln(args...)
}
// Panicln logs a message at level Panic on the standard logger.
func Panicln(args ...interface{}) {
std.Panicln(args...)
}
// Fatalln logs a message at level Fatal on the standard logger.
func Fatalln(args ...interface{}) {
std.Fatalln(args...)
}

45
vendor/github.com/Sirupsen/logrus/formatter.go generated vendored Normal file
View file

@ -0,0 +1,45 @@
package logrus
import "time"
const DefaultTimestampFormat = time.RFC3339
// The Formatter interface is used to implement a custom Formatter. It takes an
// `Entry`. It exposes all the fields, including the default ones:
//
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
// * `entry.Data["time"]`. The timestamp.
// * `entry.Data["level"]. The level the entry was logged at.
//
// Any additional fields added with `WithField` or `WithFields` are also in
// `entry.Data`. Format is expected to return an array of bytes which are then
// logged to `logger.Out`.
type Formatter interface {
Format(*Entry) ([]byte, error)
}
// This is to not silently overwrite `time`, `msg` and `level` fields when
// dumping it. If this code wasn't there doing:
//
// logrus.WithField("level", 1).Info("hello")
//
// Would just silently drop the user provided level. Instead with this code
// it'll logged as:
//
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
//
// It's not exported because it's still using Data in an opinionated way. It's to
// avoid code duplication between the two default formatters.
func prefixFieldClashes(data Fields) {
if t, ok := data["time"]; ok {
data["fields.time"] = t
}
if m, ok := data["msg"]; ok {
data["fields.msg"] = m
}
if l, ok := data["level"]; ok {
data["fields.level"] = l
}
}

34
vendor/github.com/Sirupsen/logrus/hooks.go generated vendored Normal file
View file

@ -0,0 +1,34 @@
package logrus
// A hook to be fired when logging on the logging levels returned from
// `Levels()` on your implementation of the interface. Note that this is not
// fired in a goroutine or a channel with workers, you should handle such
// functionality yourself if your call is non-blocking and you don't wish for
// the logging calls for levels returned from `Levels()` to block.
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
// Internal type for storing the hooks on a logger instance.
type LevelHooks map[Level][]Hook
// Add a hook to an instance of logger. This is called with
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
func (hooks LevelHooks) Add(hook Hook) {
for _, level := range hook.Levels() {
hooks[level] = append(hooks[level], hook)
}
}
// Fire all the hooks for the passed level. Used by `entry.log` to fire
// appropriate hooks for a log entry.
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
for _, hook := range hooks[level] {
if err := hook.Fire(entry); err != nil {
return err
}
}
return nil
}

41
vendor/github.com/Sirupsen/logrus/json_formatter.go generated vendored Normal file
View file

@ -0,0 +1,41 @@
package logrus
import (
"encoding/json"
"fmt"
)
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
TimestampFormat string
}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/Sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
}
}
prefixFieldClashes(data)
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
data["time"] = entry.Time.Format(timestampFormat)
data["msg"] = entry.Message
data["level"] = entry.Level.String()
serialized, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}

308
vendor/github.com/Sirupsen/logrus/logger.go generated vendored Normal file
View file

@ -0,0 +1,308 @@
package logrus
import (
"io"
"os"
"sync"
)
type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
// file, or leave it default which is `os.Stderr`. You can also set this to
// something more adventorous, such as logging to Kafka.
Out io.Writer
// Hooks for the logger instance. These allow firing events based on logging
// levels and log entries. For example, to send errors to an error tracking
// service, log to StatsD or dump the core on fatal errors.
Hooks LevelHooks
// All log entries pass through the formatter before logged to Out. The
// included formatters are `TextFormatter` and `JSONFormatter` for which
// TextFormatter is the default. In development (when a TTY is attached) it
// logs with colors, but to a file it wouldn't. You can easily implement your
// own that implements the `Formatter` interface, see the `README` or included
// formatters for examples.
Formatter Formatter
// The logging level the logger should log at. This is typically (and defaults
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
// logged. `logrus.Debug` is useful in
Level Level
// Used to sync writing to the log. Locking is enabled by Default
mu MutexWrap
// Reusable empty entry
entryPool sync.Pool
}
type MutexWrap struct {
lock sync.Mutex
disabled bool
}
func (mw *MutexWrap) Lock() {
if !mw.disabled {
mw.lock.Lock()
}
}
func (mw *MutexWrap) Unlock() {
if !mw.disabled {
mw.lock.Unlock()
}
}
func (mw *MutexWrap) Disable() {
mw.disabled = true
}
// Creates a new logger. Configuration should be set by changing `Formatter`,
// `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own:
//
// var log = &Logger{
// Out: os.Stderr,
// Formatter: new(JSONFormatter),
// Hooks: make(LevelHooks),
// Level: logrus.DebugLevel,
// }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
return &Logger{
Out: os.Stderr,
Formatter: new(TextFormatter),
Hooks: make(LevelHooks),
Level: InfoLevel,
}
}
func (logger *Logger) newEntry() *Entry {
entry, ok := logger.entryPool.Get().(*Entry)
if ok {
return entry
}
return NewEntry(logger)
}
func (logger *Logger) releaseEntry(entry *Entry) {
logger.entryPool.Put(entry)
}
// Adds a field to the log entry, note that it doesn't log until you call
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
// If you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithField(key, value)
}
// Adds a struct of fields to the log entry. All it does is call `WithField` for
// each `Field`.
func (logger *Logger) WithFields(fields Fields) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithFields(fields)
}
// Add an error as single field to the log entry. All it does is call
// `WithError` for the given `error`.
func (logger *Logger) WithError(err error) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithError(err)
}
func (logger *Logger) Debugf(format string, args ...interface{}) {
if logger.Level >= DebugLevel {
entry := logger.newEntry()
entry.Debugf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Infof(format string, args ...interface{}) {
if logger.Level >= InfoLevel {
entry := logger.newEntry()
entry.Infof(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Printf(format string, args ...interface{}) {
entry := logger.newEntry()
entry.Printf(format, args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
if logger.Level >= WarnLevel {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
if logger.Level >= WarnLevel {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
if logger.Level >= ErrorLevel {
entry := logger.newEntry()
entry.Errorf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
if logger.Level >= FatalLevel {
entry := logger.newEntry()
entry.Fatalf(format, args...)
logger.releaseEntry(entry)
}
Exit(1)
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
if logger.Level >= PanicLevel {
entry := logger.newEntry()
entry.Panicf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Debug(args ...interface{}) {
if logger.Level >= DebugLevel {
entry := logger.newEntry()
entry.Debug(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Info(args ...interface{}) {
if logger.Level >= InfoLevel {
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Print(args ...interface{}) {
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warn(args ...interface{}) {
if logger.Level >= WarnLevel {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Warning(args ...interface{}) {
if logger.Level >= WarnLevel {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Error(args ...interface{}) {
if logger.Level >= ErrorLevel {
entry := logger.newEntry()
entry.Error(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Fatal(args ...interface{}) {
if logger.Level >= FatalLevel {
entry := logger.newEntry()
entry.Fatal(args...)
logger.releaseEntry(entry)
}
Exit(1)
}
func (logger *Logger) Panic(args ...interface{}) {
if logger.Level >= PanicLevel {
entry := logger.newEntry()
entry.Panic(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Debugln(args ...interface{}) {
if logger.Level >= DebugLevel {
entry := logger.newEntry()
entry.Debugln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Infoln(args ...interface{}) {
if logger.Level >= InfoLevel {
entry := logger.newEntry()
entry.Infoln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Println(args ...interface{}) {
entry := logger.newEntry()
entry.Println(args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warnln(args ...interface{}) {
if logger.Level >= WarnLevel {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Warningln(args ...interface{}) {
if logger.Level >= WarnLevel {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Errorln(args ...interface{}) {
if logger.Level >= ErrorLevel {
entry := logger.newEntry()
entry.Errorln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Fatalln(args ...interface{}) {
if logger.Level >= FatalLevel {
entry := logger.newEntry()
entry.Fatalln(args...)
logger.releaseEntry(entry)
}
Exit(1)
}
func (logger *Logger) Panicln(args ...interface{}) {
if logger.Level >= PanicLevel {
entry := logger.newEntry()
entry.Panicln(args...)
logger.releaseEntry(entry)
}
}
//When file is opened with appending mode, it's safe to
//write concurrently to a file (within 4k message on Linux).
//In these cases user can choose to disable the lock.
func (logger *Logger) SetNoLock() {
logger.mu.Disable()
}

143
vendor/github.com/Sirupsen/logrus/logrus.go generated vendored Normal file
View file

@ -0,0 +1,143 @@
package logrus
import (
"fmt"
"log"
"strings"
)
// Fields type, used to pass to `WithFields`.
type Fields map[string]interface{}
// Level type
type Level uint8
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
func (level Level) String() string {
switch level {
case DebugLevel:
return "debug"
case InfoLevel:
return "info"
case WarnLevel:
return "warning"
case ErrorLevel:
return "error"
case FatalLevel:
return "fatal"
case PanicLevel:
return "panic"
}
return "unknown"
}
// ParseLevel takes a string level and returns the Logrus log level constant.
func ParseLevel(lvl string) (Level, error) {
switch strings.ToLower(lvl) {
case "panic":
return PanicLevel, nil
case "fatal":
return FatalLevel, nil
case "error":
return ErrorLevel, nil
case "warn", "warning":
return WarnLevel, nil
case "info":
return InfoLevel, nil
case "debug":
return DebugLevel, nil
}
var l Level
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
}
// A constant exposing all logging levels
var AllLevels = []Level{
PanicLevel,
FatalLevel,
ErrorLevel,
WarnLevel,
InfoLevel,
DebugLevel,
}
// These are the different logging levels. You can set the logging level to log
// on your instance of logger, obtained with `logrus.New()`.
const (
// PanicLevel level, highest level of severity. Logs and then calls panic with the
// message passed to Debug, Info, ...
PanicLevel Level = iota
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
// logging level is set to Panic.
FatalLevel
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
// Commonly used for hooks to send errors to an error tracking service.
ErrorLevel
// WarnLevel level. Non-critical entries that deserve eyes.
WarnLevel
// InfoLevel level. General operational entries about what's going on inside the
// application.
InfoLevel
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
DebugLevel
)
// Won't compile if StdLogger can't be realized by a log.Logger
var (
_ StdLogger = &log.Logger{}
_ StdLogger = &Entry{}
_ StdLogger = &Logger{}
)
// StdLogger is what your logrus-enabled library should take, that way
// it'll accept a stdlib logger and a logrus logger. There's no standard
// interface, this is the closest we get, unfortunately.
type StdLogger interface {
Print(...interface{})
Printf(string, ...interface{})
Println(...interface{})
Fatal(...interface{})
Fatalf(string, ...interface{})
Fatalln(...interface{})
Panic(...interface{})
Panicf(string, ...interface{})
Panicln(...interface{})
}
// The FieldLogger interface generalizes the Entry and Logger types
type FieldLogger interface {
WithField(key string, value interface{}) *Entry
WithFields(fields Fields) *Entry
WithError(err error) *Entry
Debugf(format string, args ...interface{})
Infof(format string, args ...interface{})
Printf(format string, args ...interface{})
Warnf(format string, args ...interface{})
Warningf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
Panicf(format string, args ...interface{})
Debug(args ...interface{})
Info(args ...interface{})
Print(args ...interface{})
Warn(args ...interface{})
Warning(args ...interface{})
Error(args ...interface{})
Fatal(args ...interface{})
Panic(args ...interface{})
Debugln(args ...interface{})
Infoln(args ...interface{})
Println(args ...interface{})
Warnln(args ...interface{})
Warningln(args ...interface{})
Errorln(args ...interface{})
Fatalln(args ...interface{})
Panicln(args ...interface{})
}

View file

@ -0,0 +1,8 @@
// +build appengine
package logrus
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
return true
}

10
vendor/github.com/Sirupsen/logrus/terminal_bsd.go generated vendored Normal file
View file

@ -0,0 +1,10 @@
// +build darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package logrus
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
type Termios syscall.Termios

14
vendor/github.com/Sirupsen/logrus/terminal_linux.go generated vendored Normal file
View file

@ -0,0 +1,14 @@
// Based on ssh/terminal:
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !appengine
package logrus
import "syscall"
const ioctlReadTermios = syscall.TCGETS
type Termios syscall.Termios

View file

@ -0,0 +1,22 @@
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package logrus
import (
"syscall"
"unsafe"
)
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
fd := syscall.Stderr
var termios Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

15
vendor/github.com/Sirupsen/logrus/terminal_solaris.go generated vendored Normal file
View file

@ -0,0 +1,15 @@
// +build solaris,!appengine
package logrus
import (
"os"
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal() bool {
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
return err == nil
}

27
vendor/github.com/Sirupsen/logrus/terminal_windows.go generated vendored Normal file
View file

@ -0,0 +1,27 @@
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows,!appengine
package logrus
import (
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
)
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
fd := syscall.Stderr
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}

165
vendor/github.com/Sirupsen/logrus/text_formatter.go generated vendored Normal file
View file

@ -0,0 +1,165 @@
package logrus
import (
"bytes"
"fmt"
"runtime"
"sort"
"strings"
"time"
)
const (
nocolor = 0
red = 31
green = 32
yellow = 33
blue = 34
gray = 37
)
var (
baseTimestamp time.Time
isTerminal bool
)
func init() {
baseTimestamp = time.Now()
isTerminal = IsTerminal()
}
func miniTS() int {
return int(time.Since(baseTimestamp) / time.Second)
}
type TextFormatter struct {
// Set to true to bypass checking for a TTY before outputting colors.
ForceColors bool
// Force disabling colors.
DisableColors bool
// Disable timestamp logging. useful when output is redirected to logging
// system that already adds timestamps.
DisableTimestamp bool
// Enable logging the full timestamp when a TTY is attached instead of just
// the time passed since beginning of execution.
FullTimestamp bool
// TimestampFormat to use for display when a full timestamp is printed
TimestampFormat string
// The fields are sorted by default for a consistent output. For applications
// that log extremely frequently and don't use the JSON formatter this may not
// be desired.
DisableSorting bool
}
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
var b *bytes.Buffer
var keys []string = make([]string, 0, len(entry.Data))
for k := range entry.Data {
keys = append(keys, k)
}
if !f.DisableSorting {
sort.Strings(keys)
}
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
prefixFieldClashes(entry.Data)
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
if isColored {
f.printColored(b, entry, keys, timestampFormat)
} else {
if !f.DisableTimestamp {
f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
}
f.appendKeyValue(b, "level", entry.Level.String())
if entry.Message != "" {
f.appendKeyValue(b, "msg", entry.Message)
}
for _, key := range keys {
f.appendKeyValue(b, key, entry.Data[key])
}
}
b.WriteByte('\n')
return b.Bytes(), nil
}
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
var levelColor int
switch entry.Level {
case DebugLevel:
levelColor = gray
case WarnLevel:
levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel:
levelColor = red
default:
levelColor = blue
}
levelText := strings.ToUpper(entry.Level.String())[0:4]
if !f.FullTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
} else {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
}
for _, k := range keys {
v := entry.Data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
}
}
func needsQuoting(text string) bool {
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
ch == '-' || ch == '.') {
return true
}
}
return false
}
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
b.WriteString(key)
b.WriteByte('=')
switch value := value.(type) {
case string:
if !needsQuoting(value) {
b.WriteString(value)
} else {
fmt.Fprintf(b, "%q", value)
}
case error:
errmsg := value.Error()
if !needsQuoting(errmsg) {
b.WriteString(errmsg)
} else {
fmt.Fprintf(b, "%q", value)
}
default:
fmt.Fprint(b, value)
}
b.WriteByte(' ')
}

53
vendor/github.com/Sirupsen/logrus/writer.go generated vendored Normal file
View file

@ -0,0 +1,53 @@
package logrus
import (
"bufio"
"io"
"runtime"
)
func (logger *Logger) Writer() *io.PipeWriter {
return logger.WriterLevel(InfoLevel)
}
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
reader, writer := io.Pipe()
var printFunc func(args ...interface{})
switch level {
case DebugLevel:
printFunc = logger.Debug
case InfoLevel:
printFunc = logger.Info
case WarnLevel:
printFunc = logger.Warn
case ErrorLevel:
printFunc = logger.Error
case FatalLevel:
printFunc = logger.Fatal
case PanicLevel:
printFunc = logger.Panic
default:
printFunc = logger.Print
}
go logger.writerScanner(reader, printFunc)
runtime.SetFinalizer(writer, writerFinalizer)
return writer
}
func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
printFunc(scanner.Text())
}
if err := scanner.Err(); err != nil {
logger.Errorf("Error while reading from Writer: %s", err)
}
reader.Close()
}
func writerFinalizer(writer *io.PipeWriter) {
writer.Close()
}

0
vendor/github.com/Wessie/appdirs/.gitignore generated vendored Normal file
View file

18
vendor/github.com/Wessie/appdirs/LICENSE generated vendored Normal file
View file

@ -0,0 +1,18 @@
Copyright (c) 2013 Wesley Bitter
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
vendor/github.com/Wessie/appdirs/README.md generated vendored Normal file
View file

@ -0,0 +1,3 @@
This is a port of the excellent python module named the same, which can be found here [appdirs](https://github.com/ActiveState/appdirs).
The README and documentation in the original should be a good starting point. Documentation is currently lacking on the port itself, but the API is very similar to the python version.

80
vendor/github.com/Wessie/appdirs/appdirs.go generated vendored Normal file
View file

@ -0,0 +1,80 @@
// A port of the excellent python module `appdirs`.
// See https://github.com/ActiveState/appdirs for the python version.
package appdirs
import (
"os/user"
"strings"
)
// App is a helper type to create easy access across your program to the appdirs
// functions.
//
// The *App type has 6 methods that map to the 6 functions exported by `appdirs`.
// All methods take no arguments, and supply the function it wraps with arguments
// pre-set in the struct on creation.
type App struct {
Name string
Author string
Version string
Roaming bool
Opinion bool
}
// New returns a new App helper that has various methods for receiving
// relevant directories for your application.
//
// The following defaults are used for the two fields not settable by New:
// Roaming: false, Opinion: true
//
// If you want to set these, create your own App struct by the usual means.
func New(name, author, version string) *App {
return &App{
Name: name,
Author: author,
Version: version,
Roaming: false,
Opinion: true,
}
}
// UserData returns the full path to the user-specific data directory
func (app *App) UserData() string {
return UserDataDir(app.Name, app.Author, app.Version, app.Roaming)
}
// SiteData returns the full path to the user-shared data directory
func (app *App) SiteData() string {
return SiteDataDir(app.Name, app.Author, app.Version)
}
// SiteConfig returns the full path to the user-shared configuration directory
func (app *App) SiteConfig() string {
return SiteConfigDir(app.Name, app.Author, app.Version)
}
// UserConfig returns the full path to the user-specific configuration directory
func (app *App) UserConfig() string {
return UserConfigDir(app.Name, app.Author, app.Version, app.Roaming)
}
// UserCache returns the full path to the user-specific cache directory
func (app *App) UserCache() string {
return UserCacheDir(app.Name, app.Author, app.Version, app.Opinion)
}
// UserLog returns the full path to the user-specific log directory
func (app *App) UserLog() string {
return UserLogDir(app.Name, app.Author, app.Version, app.Opinion)
}
// ExpandUser is a helper function that expands the first '~' it finds in the
// passed path with the home directory of the current user.
//
// Note: This only works on environments similar to bash.
func ExpandUser(path string) string {
if u, err := user.Current(); err == nil {
return strings.Replace(path, "~", u.HomeDir, -1)
}
return path
}

63
vendor/github.com/Wessie/appdirs/appdirs_darwin.go generated vendored Normal file
View file

@ -0,0 +1,63 @@
package appdirs
import (
"path/filepath"
)
func userDataDir(name, author, version string, roaming bool) (path string) {
path = ExpandUser("~/Library/Application Support")
if name != "" {
path = filepath.Join(path, name)
}
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}
func siteDataDir(name, author, version string) (path string) {
path = ExpandUser("/Library/Application Support")
if name != "" {
path = filepath.Join(path, name)
}
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}
func userConfigDir(name, author, version string, roaming bool) (path string) {
return UserDataDir(name, author, version, roaming)
}
func siteConfigDir(name, author, version string) (path string) {
return SiteDataDir(name, author, version)
}
func userCacheDir(name, author, version string, opinion bool) (path string) {
path = ExpandUser("~/Library/Caches")
if name != "" {
path = filepath.Join(path, name)
}
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}
func userLogDir(name, author, version string, opinion bool) (path string) {
path = ExpandUser("~/Library/Logs")
path = filepath.Join(path, name)
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}

102
vendor/github.com/Wessie/appdirs/appdirs_unix.go generated vendored Normal file
View file

@ -0,0 +1,102 @@
// +build linux freebsd netbsd openbsd
package appdirs
import (
"os"
"path/filepath"
)
func userDataDir(name, author, version string, roaming bool) (path string) {
if path = os.Getenv("XDG_DATA_HOME"); path == "" {
path = ExpandUser("~/.local/share")
}
if name != "" {
path = filepath.Join(path, name, version)
}
return path
}
func SiteDataDirs(name, author, version string) (paths []string) {
var path string
if path = os.Getenv("XDG_DATA_DIRS"); path == "" {
paths = []string{"/usr/local/share", "/usr/share"}
} else {
paths = filepath.SplitList(path)
}
for i, path := range paths {
path = ExpandUser(path)
if name != "" {
path = filepath.Join(path, name, version)
}
paths[i] = path
}
return paths
}
func siteDataDir(name, author, version string) (path string) {
return SiteDataDirs(name, author, version)[0]
}
func userConfigDir(name, author, version string, roaming bool) (path string) {
if path = os.Getenv("XDG_CONFIG_HOME"); path == "" {
path = ExpandUser("~/.config")
}
if name != "" {
path = filepath.Join(path, name, version)
}
return path
}
func SiteConfigDirs(name, author, version string) (paths []string) {
var path string
if path = os.Getenv("XDG_CONFIG_DIRS"); path == "" {
paths = []string{"/etc/xdg"}
} else {
paths = filepath.SplitList(path)
}
for i, path := range paths {
path = ExpandUser(path)
if name != "" {
path = filepath.Join(path, name, version)
}
paths[i] = path
}
return paths
}
func siteConfigDir(name, author, version string) (path string) {
return SiteConfigDirs(name, author, version)[0]
}
func userCacheDir(name, author, version string, opinion bool) (path string) {
if path = os.Getenv("XDG_CACHE_HOME"); path == "" {
path = ExpandUser("~/.cache")
}
if name != "" {
path = filepath.Join(path, name, version)
}
return path
}
func userLogDir(name, author, version string, opinion bool) (path string) {
path = UserCacheDir(name, author, version, opinion)
return filepath.Join(path, "log")
}

171
vendor/github.com/Wessie/appdirs/appdirs_windows.go generated vendored Normal file
View file

@ -0,0 +1,171 @@
package appdirs
import (
"path/filepath"
"syscall"
"unsafe"
)
var (
shell32, _ = syscall.LoadLibrary("shell32.dll")
getKnownFolderPath, _ = syscall.GetProcAddress(shell32, "SHGetKnownFolderPath")
ole32, _ = syscall.LoadLibrary("Ole32.dll")
coTaskMemFree, _ = syscall.GetProcAddress(ole32, "CoTaskMemFree")
)
// These are KNOWNFOLDERID constants that are passed to GetKnownFolderPath
var (
rfidLocalAppData = syscall.GUID{
0xf1b32785,
0x6fba,
0x4fcf,
[8]byte{0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91},
}
rfidRoamingAppData = syscall.GUID{
0x3eb685db,
0x65f9,
0x4cf6,
[8]byte{0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d},
}
rfidProgramData = syscall.GUID{
0x62ab5d82,
0xfdc1,
0x4dc3,
[8]byte{0xa9, 0xdd, 0x07, 0x0d, 0x1d, 0x49, 0x5d, 0x97},
}
)
func userDataDir(name, author, version string, roaming bool) (path string) {
if author == "" {
author = name
}
var rfid syscall.GUID
if roaming {
rfid = rfidRoamingAppData
} else {
rfid = rfidLocalAppData
}
path, err := getFolderPath(rfid)
if err != nil {
return ""
}
if path, err = filepath.Abs(path); err != nil {
return ""
}
if name != "" {
path = filepath.Join(path, author, name)
}
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}
func siteDataDir(name, author, version string) (path string) {
path, err := getFolderPath(rfidProgramData)
if err != nil {
return ""
}
if path, err = filepath.Abs(path); err != nil {
return ""
}
if author == "" {
author = name
}
if name != "" {
path = filepath.Join(path, author, name)
}
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}
func userConfigDir(name, author, version string, roaming bool) string {
return UserDataDir(name, author, version, roaming)
}
func siteConfigDir(name, author, version string) (path string) {
return SiteDataDir(name, author, version)
}
func userCacheDir(name, author, version string, opinion bool) (path string) {
if author == "" {
author = name
}
path, err := getFolderPath(rfidLocalAppData)
if err != nil {
return ""
}
if path, err = filepath.Abs(path); err != nil {
return ""
}
if name != "" {
path = filepath.Join(path, author, name)
if opinion {
path = filepath.Join(path, "Cache")
}
}
if name != "" && version != "" {
path = filepath.Join(path, version)
}
return path
}
func userLogDir(name, author, version string, opinion bool) (path string) {
path = UserDataDir(name, author, version, false)
if opinion {
path = filepath.Join(path, "Logs")
}
return path
}
func getFolderPath(rfid syscall.GUID) (string, error) {
var res uintptr
ret, _, callErr := syscall.Syscall6(
uintptr(getKnownFolderPath),
4,
uintptr(unsafe.Pointer(&rfid)),
0,
0,
uintptr(unsafe.Pointer(&res)),
0,
0,
)
if callErr != 0 && ret != 0 {
return "", callErr
}
defer syscall.Syscall(uintptr(coTaskMemFree), 1, res, 0, 0)
return ucs2PtrToString(res), nil
}
func ucs2PtrToString(p uintptr) string {
ptr := (*[4096]uint16)(unsafe.Pointer(p))
return syscall.UTF16ToString((*ptr)[:])
}

108
vendor/github.com/Wessie/appdirs/doc.go generated vendored Normal file
View file

@ -0,0 +1,108 @@
// appdirs project doc.go
/*
This is a port of a python module used for finding what directory you 'should'
be using for saving your application data such as configuration, cache files or
other files.
The location of these directories is often hard to get right. The original python
module set out to change this into a simple API that returns you the exact
directory you need. This is a port of it to Go.
Depending on platform, this package exports you at the least 6 functions that
return various system directories. And one helper struct type that combines the
functions into methods for less arguments in your code.
Each function defined accepts a number of arguments, each argument is optional
and can be left to the types default value if omitted. Often the function will
ignore arguments if the name given is empty.
Passing in all default values into any of the functions will return you the base
directory without any of the arguments appended to it.
*/
package appdirs
// UserDataDir returns the full path to the user-specific data directory.
//
// This function uses XDG_DATA_HOME as defined by the XDG spec on *nix like systems.
//
// Examples of return values:
// Mac OS X: ~/Library/Application Support/<AppName>
// Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
// Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
// Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
// Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
// Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
func UserDataDir(name, author, version string, roaming bool) string {
return userDataDir(name, author, version, roaming)
}
// SiteDataDir returns the full path to the user-shared data directory.
//
// This function uses XDG_DATA_DIRS[0] as by the XDG spec on *nix like systems.
//
// Examples of return values:
// Mac OS X: /Library/Application Support/<AppName>
// Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
// Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
// Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
// Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
//
// WARNING: Do not use this on Windows Vista, See the note above.
func SiteDataDir(name, author, version string) string {
return siteDataDir(name, author, version)
}
// UserConfigDir returns the full path to the user-specific configuration directory
//
// This function uses XDG_CONFIG_HOME as by the XDG spec on *nix like systems.
//
// Examples of return values:
// Mac OS X: same as UserDataDir
// Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
// Win *: same as UserDataDir
func UserConfigDir(name, author, version string, roaming bool) string {
return userConfigDir(name, author, version, roaming)
}
// SiteConfigDir returns the full path to the user-shared data directory.
//
// This function uses XDG_CONFIG_DIRS[0] as by the XDG spec on *nix like systems.
//
// Examples of return values:
// Mac OS X: same as SiteDataDir
// Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in $XDG_CONFIG_DIRS
// Win *: same as SiteDataDir
// Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
//
// WARNING: Do not use this on Windows Vista, see the note above.
func SiteConfigDir(name, author, version string) string {
return siteConfigDir(name, author, version)
}
// UserCacheDir returns the full path to the user-specific cache directory.
//
// The opinion argument will append 'Cache' to the base directory if set to true.
//
// Examples of return values:
// Mac OS X: ~/Library/Caches/<AppName>
// Unix: ~/.cache/<AppName> (XDG default)
// Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
// Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
func UserCacheDir(name, author, version string, opinion bool) string {
return userCacheDir(name, author, version, opinion)
}
// UserLogDir returns the full path to the user-specific log directory.
//
// The opinion argument will append either 'Logs' (windows) or 'log' (unix) to
// the base directory when set to true.
//
// Examples of return values:
// Mac OS X: ~/Library/Logs/<AppName>
// Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
// Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
// Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
func UserLogDir(name, author, version string, opinion bool) string {
return userLogDir(name, author, version, opinion)
}

3
vendor/github.com/flopp/go-coordsparser/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,3 @@
language: go
go:
- 1.5

21
vendor/github.com/flopp/go-coordsparser/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Florian Pigorsch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

57
vendor/github.com/flopp/go-coordsparser/README.md generated vendored Normal file
View file

@ -0,0 +1,57 @@
[![GoDoc](https://godoc.org/github.com/flopp/go-coordsparser?status.svg)](https://godoc.org/github.com/flopp/go-coordsparser)
[![Build Status](https://travis-ci.org/flopp/go-coordsparser.svg)](https://travis-ci.org/flopp/go-coordsparser)
[![Go Report Card](https://goreportcard.com/badge/flopp/go-coordsparser)](https://goreportcard.com/report/flopp/go-coordsparser)
[![License MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/flopp/go-coordsparser)
# go-coordsparser
A library for parsing (geographic) coordinates in go (golang)
# What?
go-coordsparser allows you to parse lat/lng coordinates from strings in various popular formats. Currently supported formats are:
- **D** (decimal degrees), e.g. `40.76, -73.984`
- **HD** (hemisphere prefix, decimal degrees), e.g. `N 40.76 W 73.984`
- **HDM** (hemisphere prefix, integral degrees, decimal minutes), e.g. `N 40 45.600 W 73 59.040`
- **HDMS** (hemisphere prefix, integral degrees, integral minutes, decimal seconds), e.g. `N 40 45 36.0 W 73 59 02.4`
# How?
### Installing
Installing the library is as easy as
```bash
$ go get github.com/flopp/go-coordsparser
```
The package can then be used through an
```go
import "github.com/flopp/go-coordsparser"
```
### Using
go-coordsparser provides several functions for parsing coordinate strings: a general parsing function `coordsparser.Parse`, which accepts all supported formats, as well as specialized functions `coordsparser.ParseD`, `coordsparser.ParseHD`, `coordsparser.ParseHDM`, `coordsparser.ParseHDMS` for the corresponding coordinate formats.
Each function takes a single string as a parameter and returns an idiomatic `lat, lng, error` triple, where `lat` and `lng` are decimal degrees (`float64`) with -90 ≤ `lat` ≤ 90 and -180 ≤ `lng` ≤ 180.
```go
// parse any format
s1 := "..."
lat1, lng1, err := coordsparser.Parse(s1)
if err != nil {
fmt.Errorf("Cannot parse coordinates string:", s1)
}
// parse specific format, e.g. HDM
s2 := "..."
lat2, lng2, err = coordsparser.ParseHDM(s2)
if err != nil {
fmt.Errorf("Cannot parse coordinates string:", s2)
}
```
# License
Copyright 2016 Florian Pigorsch. All rights reserved.
Use of this source code is governed by a MIT-style license that can be found in the LICENSE file.

172
vendor/github.com/flopp/go-coordsparser/coordsparser.go generated vendored Normal file
View file

@ -0,0 +1,172 @@
// Copyright 2016 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package coordsparser is a library for parsing (geographic) coordinates in various string formats
package coordsparser
import (
"fmt"
"regexp"
"strconv"
)
// Parse parses a coordinate string and returns a lat/lng pair or an error
func Parse(s string) (float64, float64, error) {
lat, lng, err := ParseD(s)
if err == nil {
return lat, lng, nil
}
lat, lng, err = ParseHD(s)
if err == nil {
return lat, lng, nil
}
lat, lng, err = ParseHDM(s)
if err == nil {
return lat, lng, nil
}
lat, lng, err = ParseHDMS(s)
if err == nil {
return lat, lng, nil
}
return 0, 0, fmt.Errorf("Cannot parse coordinates: %s", s)
}
// ParseD parses a coordinate string of the form "D.DDDD D.DDDD" and returns a lat/lng pair or an error
func ParseD(s string) (float64, float64, error) {
re := regexp.MustCompile(`^\s*([+-]?[\d\.]+)\s*(,|;|:|\s)\s*([+-]?[\d\.]+)\s*$`)
matches := re.FindStringSubmatch(s)
if matches == nil {
return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
}
lat, err := strconv.ParseFloat(matches[1], 64)
if err != nil || lat < -90 || lat > 90 {
return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
}
lng, err := strconv.ParseFloat(matches[3], 64)
if err != nil || lng < -180 || lng > 180 {
return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
}
return lat, lng, nil
}
// ParseHD parses a coordinate string of the form "H D.DDDD H D.DDDD" and returns a lat/lng pair or an error
func ParseHD(s string) (float64, float64, error) {
re := regexp.MustCompile(`^\s*([NnSs])\s*([\d\.]+)\s+([EeWw])\s*([\d\.]+)\s*$`)
matches := re.FindStringSubmatch(s)
if matches == nil {
return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
}
lat, err := strconv.ParseFloat(matches[2], 64)
if err != nil || lat > 90 {
return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
}
if matches[1] == "S" || matches[1] == "s" {
lat = -lat
}
lng, err := strconv.ParseFloat(matches[4], 64)
if err != nil || lng > 180 {
return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
}
if matches[3] == "W" || matches[3] == "w" {
lng = -lng
}
return lat, lng, nil
}
// ParseHDM parses a coordinate string of the form "H D M.MMM H D M.MMM" and returns a lat/lng pair or an error
func ParseHDM(s string) (float64, float64, error) {
re := regexp.MustCompile(`^\s*([NnSs])\s*([\d]+)\s+([\d.]+)\s+([EeWw])\s*([\d]+)\s+([\d.]+)\s*$`)
matches := re.FindStringSubmatch(s)
if matches == nil {
return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
}
latDeg, err := strconv.ParseFloat(matches[2], 64)
if err != nil || latDeg > 90 {
return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
}
latMin, err := strconv.ParseFloat(matches[3], 64)
if err != nil || latMin >= 60 {
return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
}
lat := latDeg + latMin/60.0
if matches[1] == "S" || matches[1] == "s" {
lat = -lat
}
lngDeg, err := strconv.ParseFloat(matches[5], 64)
if err != nil || lngDeg > 180 {
return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
}
lngMin, err := strconv.ParseFloat(matches[6], 64)
if err != nil || lngMin >= 60 {
return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
}
lng := lngDeg + lngMin/60.0
if matches[4] == "W" || matches[4] == "w" {
lng = -lng
}
return lat, lng, nil
}
// ParseHDMS parses a coordinate string of the form "H D M S.SSS H D M S.SSS" and returns a lat/lng pair or an error
func ParseHDMS(s string) (float64, float64, error) {
re := regexp.MustCompile(`^\s*([NnSs])\s*([\d]+)\s+([\d]+)\s+([\d.]+)\s+([EeWw])\s*([\d]+)\s+([\d]+)\s+([\d.]+)\s*$`)
matches := re.FindStringSubmatch(s)
if matches == nil {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
latDeg, err := strconv.ParseFloat(matches[2], 64)
if err != nil || latDeg > 90 {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
latMin, err := strconv.ParseFloat(matches[3], 64)
if err != nil || latMin >= 60 {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
latSec, err := strconv.ParseFloat(matches[4], 64)
if err != nil || latSec >= 60 {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
lat := latDeg + latMin/60.0 + latSec/3600.0
if matches[1] == "S" || matches[1] == "s" {
lat = -lat
}
lngDeg, err := strconv.ParseFloat(matches[6], 64)
if err != nil || lngDeg > 180 {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
lngMin, err := strconv.ParseFloat(matches[7], 64)
if err != nil || lngMin >= 60 {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
lngSec, err := strconv.ParseFloat(matches[8], 64)
if err != nil || lngSec >= 60 {
return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
}
lng := lngDeg + lngMin/60.0 + lngSec/3600.0
if matches[5] == "W" || matches[5] == "w" {
lng = -lng
}
return lat, lng, nil
}

1
vendor/github.com/flopp/go-staticmaps/.gitignore generated vendored Normal file
View file

@ -0,0 +1 @@
*.png

21
vendor/github.com/flopp/go-staticmaps/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Florian Pigorsch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

225
vendor/github.com/flopp/go-staticmaps/README.md generated vendored Normal file
View file

@ -0,0 +1,225 @@
[![GoDoc](https://godoc.org/github.com/flopp/go-staticmaps?status.svg)](https://godoc.org/github.com/flopp/go-staticmaps)
[![Go Report Card](https://goreportcard.com/badge/github.com/flopp/go-staticmaps)](https://goreportcard.com/report/flopp/go-staticmaps)
[![License MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/flopp/go-staticmaps/)
# go-staticmaps
A go (golang) library and command line tool to render static map images using OpenStreetMap tiles.
## What?
go-staticmaps is a golang library that allows you to create nice static map images from OpenStreetMap tiles, along with markers of different size and color, as well as paths and colored areas.
go-staticmaps comes with a command line tool called `create-static-map` for use in shell scripts, etc.
![Static map of the Berlin Marathon](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/berlin-marathon.png)
## How?
### Installation
Installing go-staticmaps is as easy as
```bash
go get -u github.com/flopp/go-staticmaps
```
For the command line tool, use
```bash
go get -u github.com/flopp/go-staticmaps/create-static-map
```
Of course, your local Go installation must be setup up properly.
### Library Usage
Create a 400x300 pixel map with a red marker:
```go
import (
"image/color"
"github.com/flopp/go-staticmaps"
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
)
func main() {
ctx := sm.NewContext()
ctx.SetSize(400, 300)
ctx.AddMarker(sm.NewMarker(s2.LatLngFromDegrees(52.514536, 13.350151), color.RGBA{0xff, 0, 0, 0xff}, 16.0))
img, err := ctx.Render()
if err != nil {
panic(err)
}
if err := gg.SavePNG("my-map.png", img); err != nil {
panic(err)
}
}
```
See [GoDoc](https://godoc.org/github.com/flopp/go-staticmaps) for a complete documentation and the source code of the [command line tool](https://github.com/flopp/go-staticmaps/blob/master/create-static-map/create-static-map.go) for an example how to use the package.
### Command Line Usage
Usage:
create-static-map [OPTIONS]
Creates a static map
Application Options:
--width=PIXELS Width of the generated static map image (default: 512)
--height=PIXELS Height of the generated static map image (default: 512)
-o, --output=FILENAME Output file name (default: map.png)
-t, --type=MAPTYPE Select the map type; list possible map types with '--type list'
-c, --center=LATLNG Center coordinates (lat,lng) of the static map
-z, --zoom=ZOOMLEVEL Zoom factor
-b, --bbox=NW_LATLNG|SE_LATLNG
Set the bounding box (NW_LATLNG = north-western point of the
bounding box, SW_LATLNG = southe-western point of the bounding
box)
-m, --marker=MARKER Add a marker to the static map
-p, --path=PATH Add a path to the static map
-a, --area=AREA Add an area to the static map
Help Options:
-h, --help Show this help message
### General
The command line interface tries to resemble [Google's Static Maps API](https://developers.google.com/maps/documentation/static-maps/intro).
If neither `--bbox`, `--center`, nor `--zoom` are given, the map extent is determined from the specified markers, paths and areas.
### Markers
The `--marker` option defines one or more map markers of the same style. Use multiple `--marker` options to add markers of different styles.
--marker MARKER_STYLES|LATLNG|LATLNG|...
`LATLNG` is a comma separated pair of latitude and longitude, e.g. `52.5153,13.3564`.
`MARKER_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `size:SIZE` - where `SIZE` is one of `mid`, `small`, `tiny`, or some number > 0 (default: `mid`)
- `label:LABEL` - where `LABEL` is an alpha numeric character, i.e. `A`-`Z`, `a`-`z`, `0`-`9`; (default: no label)
- `labelcolor:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `black` or `white`, depending on the marker color)
### Paths
The `--path` option defines a path on the map. Use multiple `--path` options to add multiple paths to the map.
--path PATH_STYLES|LATLNG|LATLNG|...
or
--path PATH_STYLES|gpx:my_gpx_file.gpx
`PATH_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `weight:WEIGHT` - where `WEIGHT` is the line width in pixels (defaut: `5`)
### Areas
The `--area` option defines a closed area on the map. Use multiple `--area` options to add multiple areas to the map.
--area AREA_STYLES|LATLNG|LATLNG|...
`AREA_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `weight:WEIGHT` - where `WEIGHT` is the line width in pixels (defaut: `5`)
- `fill:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: none)
## Examples
### Basic Maps
Centered at "N 52.514536 E 13.350151" with zoom level 10:
```bash
$ create-static-map --width 600 --height 400 -o map1.png -c "52.514536,13.350151" -z 10
```
![Example 1](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/map1.png)
A map with a marker at "N 52.514536 E 13.350151" with zoom level 14 (no need to specify the map's center - it is automatically computed from the marker(s)):
```bash
$ create-static-map --width 600 --height 400 -o map2.png -z 14 -m "52.514536,13.350151"
```
![Example 2](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/map2.png)
A map with two markers (red and green). If there are more than two markers in the map, a *good* zoom level can be determined automatically:
```bash
$ create-static-map --width 600 --height 400 -o map3.png -m "color:red|52.514536,13.350151" -m "color:green|52.516285,13.377746"
```
![Example 3](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/map3.png)
### Create a map of the Berlin Marathon
create-static-map --width 800 --height 600 \
--marker "color:green|52.5153,13.3564" \
--marker "color:red|52.5160,13.3711" \
--output "berlin-marathon.png" \
--path "color:blue|weight:2|gpx:berlin-marathon.gpx"
![Static map of the Berlin Marathon](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/berlin-marathon.png)
### Create a map of the US capitals
create-static-map --width 800 --height 400 \
--output "us-capitals.png" \
--marker "color:blue|size:tiny|32.3754,-86.2996|58.3637,-134.5721|33.4483,-112.0738|34.7244,-92.2789|\
38.5737,-121.4871|39.7551,-104.9881|41.7665,-72.6732|39.1615,-75.5136|30.4382,-84.2806|33.7545,-84.3897|\
21.2920,-157.8219|43.6021,-116.2125|39.8018,-89.6533|39.7670,-86.1563|41.5888,-93.6203|39.0474,-95.6815|\
38.1894,-84.8715|30.4493,-91.1882|44.3294,-69.7323|38.9693,-76.5197|42.3589,-71.0568|42.7336,-84.5466|\
44.9446,-93.1027|32.3122,-90.1780|38.5698,-92.1941|46.5911,-112.0205|40.8136,-96.7026|39.1501,-119.7519|\
43.2314,-71.5597|40.2202,-74.7642|35.6816,-105.9381|42.6517,-73.7551|35.7797,-78.6434|46.8084,-100.7694|\
39.9622,-83.0007|35.4931,-97.4591|44.9370,-123.0272|40.2740,-76.8849|41.8270,-71.4087|34.0007,-81.0353|\
44.3776,-100.3177|36.1589,-86.7821|30.2687,-97.7452|40.7716,-111.8882|44.2627,-72.5716|37.5408,-77.4339|\
47.0449,-122.9016|38.3533,-81.6354|43.0632,-89.4007|41.1389,-104.8165"
![Static map of the US capitals](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/us-capitals.png)
### Create a map of Australia
...where the Northern Territory is highlighted and the capital Canberra is marked.
create-static-map --width 800 --height 600 \
--center="-26.284973,134.303764" \
--output "australia.png" \
--marker "color:blue|-35.305200,149.121574" \
--area "color:0x00FF00|fill:0x00FF007F|weight:2|-25.994024,129.013847|-25.994024,137.989677|-16.537670,138.011649|\
-14.834820,135.385917|-12.293236,137.033866|-11.174554,130.398124|-12.925791,130.167411|-14.866678,129.002860"
![Static map of Australia](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/australia.png)
## Acknowledgements
Besides the go standard library, go-staticmaps uses
- [OpenStreetMap](http://openstreetmap.org/), [Thunderforest](http://www.thunderforest.com/), [OpenTopoMap](http://www.opentopomap.org/), [Stamen](http://maps.stamen.com/) and [Carto](http://carto.com) as map tile providers
- [Go Graphics](https://github.com/fogleman/gg) for 2D drawing
- [S2 geometry library](https://github.com/golang/geo) for spherical geometry calculations
- [appdirs](https://github.com/Wessie/appdirs) for platform specific system directories
- [gpxgo](github.com/tkrajina/gpxgo) for loading GPX files
- [go-coordsparser](https://github.com/flopp/go-coordsparser) for parsing geo coordinates
## Contributors
- [Kooper](https://github.com/Kooper): fixed *library usage examples*
- [felix](https://github.com/felix): added *more tile servers*
- [wiless](https://github.com/wiless): suggested to add user definable *marker label colors*
- [noki](https://github.com/Noki): suggested to add a user definable *bounding box*
- [digitocero](https://github.com/digitocero): reported and fixed *type mismatch error*
- [bcicen](https://github.com/bcicen): reported and fixed *syntax error in examples*
- [pshevtsov](https://github.com/pshevtsov): fixed *drawing of empty attribution strings*
- [Luzifer](https://github.com/Luzifer): added *overwritable user agent strings* to comply with the OSM tile usage policy
## License
Copyright 2016, 2017 Florian Pigorsch & Contributors. All rights reserved.
Use of this source code is governed by a MIT-style license that can be found in the LICENSE file.

94
vendor/github.com/flopp/go-staticmaps/area.go generated vendored Normal file
View file

@ -0,0 +1,94 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"image/color"
"strconv"
"strings"
"github.com/flopp/go-coordsparser"
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
)
// Area represents a area or area on the map
type Area struct {
MapObject
Positions []s2.LatLng
Color color.Color
Fill color.Color
Weight float64
}
// ParseAreaString parses a string and returns an area
func ParseAreaString(s string) (*Area, error) {
area := new(Area)
area.Color = color.RGBA{0xff, 0, 0, 0xff}
area.Fill = color.Transparent
area.Weight = 5.0
for _, ss := range strings.Split(s, "|") {
if ok, suffix := hasPrefix(ss, "color:"); ok {
var err error
area.Color, err = ParseColorString(suffix)
if err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "fill:"); ok {
var err error
area.Fill, err = ParseColorString(suffix)
if err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "weight:"); ok {
var err error
area.Weight, err = strconv.ParseFloat(suffix, 64)
if err != nil {
return nil, err
}
} else {
lat, lng, err := coordsparser.Parse(ss)
if err != nil {
return nil, err
}
area.Positions = append(area.Positions, s2.LatLngFromDegrees(lat, lng))
}
}
return area, nil
}
func (p *Area) extraMarginPixels() float64 {
return 0.5 * p.Weight
}
func (p *Area) bounds() s2.Rect {
r := s2.EmptyRect()
for _, ll := range p.Positions {
r = r.AddPoint(ll)
}
return r
}
func (p *Area) draw(gc *gg.Context, trans *transformer) {
if len(p.Positions) <= 1 {
return
}
gc.ClearPath()
gc.SetLineWidth(p.Weight)
gc.SetLineCap(gg.LineCapRound)
gc.SetLineJoin(gg.LineJoinRound)
for _, ll := range p.Positions {
gc.LineTo(trans.ll2p(ll))
}
gc.ClosePath()
gc.SetColor(p.Fill)
gc.FillPreserve()
gc.SetColor(p.Color)
gc.Stroke()
}

53
vendor/github.com/flopp/go-staticmaps/bbox.go generated vendored Normal file
View file

@ -0,0 +1,53 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"fmt"
"math"
"github.com/golang/geo/s1"
"github.com/golang/geo/s2"
)
// CreateBBox creates a bounding box from a north-western point
// (lat/lng in degrees) and a south-eastern point (lat/lng in degrees).
// Note that you can create a bounding box wrapping over the antimeridian at
// lng=+-/180° by nwlng > selng.
func CreateBBox(nwlat float64, nwlng float64, selat float64, selng float64) (*s2.Rect, error) {
if nwlat < -90 || nwlat > 90 {
return nil, fmt.Errorf("Out of range nwlat (%f) must be in [-90, 90]", nwlat)
}
if nwlng < -180 || nwlng > 180 {
return nil, fmt.Errorf("Out of range nwlng (%f) must be in [-180, 180]", nwlng)
}
if selat < -90 || selat > 90 {
return nil, fmt.Errorf("Out of range selat (%f) must be in [-90, 90]", selat)
}
if selng < -180 || selng > 180 {
return nil, fmt.Errorf("Out of range selng (%f) must be in [-180, 180]", selng)
}
if nwlat == selat {
return nil, fmt.Errorf("nwlat and selat must not be equal")
}
if nwlng == selng {
return nil, fmt.Errorf("nwlng and selng must not be equal")
}
bbox := new(s2.Rect)
if selat < nwlat {
bbox.Lat.Lo = selat * math.Pi / 180.0
bbox.Lat.Hi = nwlat * math.Pi / 180.0
} else {
bbox.Lat.Lo = nwlat * math.Pi / 180.0
bbox.Lat.Hi = selat * math.Pi / 180.0
}
bbox.Lng = s1.IntervalFromEndpoints(nwlng*math.Pi/180.0, selng*math.Pi/180.0)
return bbox, nil
}

65
vendor/github.com/flopp/go-staticmaps/color.go generated vendored Normal file
View file

@ -0,0 +1,65 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"fmt"
"image/color"
"regexp"
"strings"
)
// ParseColorString parses hex color strings (i.e. `0xRRGGBB`, `#RRGGBB`, `0xRRGGBBAA`, `#RRGGBBAA`), and names colors (e.g. 'black', 'blue', ...)
func ParseColorString(s string) (color.Color, error) {
s = strings.ToLower(strings.TrimSpace(s))
re := regexp.MustCompile(`^(0x|#)([A-Fa-f0-9]{6})$`)
matches := re.FindStringSubmatch(s)
if matches != nil {
var r, g, b int
fmt.Sscanf(matches[2], "%2x%2x%2x", &r, &g, &b)
return color.RGBA{uint8(r), uint8(g), uint8(b), 0xff}, nil
}
re = regexp.MustCompile(`^(0x|#)([A-Fa-f0-9]{8})$`)
matches = re.FindStringSubmatch(s)
if matches != nil {
var r, g, b, a int
fmt.Sscanf(matches[2], "%2x%2x%2x%2x", &r, &g, &b, &a)
rr := float64(r) * float64(a) / 256.0
gg := float64(g) * float64(a) / 256.0
bb := float64(b) * float64(a) / 256.0
return color.RGBA{uint8(rr), uint8(gg), uint8(bb), uint8(a)}, nil
}
switch s {
case "black":
return color.RGBA{0x00, 0x00, 0x00, 0xff}, nil
case "blue":
return color.RGBA{0x00, 0x00, 0xff, 0xff}, nil
case "brown":
return color.RGBA{0x96, 0x4b, 0x00, 0xff}, nil
case "green":
return color.RGBA{0x00, 0xff, 0x00, 0xff}, nil
case "orange":
return color.RGBA{0xff, 0x7f, 0x00, 0xff}, nil
case "purple":
return color.RGBA{0x7f, 0x00, 0x7f, 0xff}, nil
case "red":
return color.RGBA{0xff, 0x00, 0x00, 0xff}, nil
case "yellow":
return color.RGBA{0xff, 0xff, 0x00, 0xff}, nil
case "white":
return color.RGBA{0xff, 0xff, 0xff, 0xff}, nil
}
return color.Transparent, fmt.Errorf("Cannot parse color string: %s", s)
}
// Luminance computes the luminance (~ brightness) of the given color. Range: 0.0 for black to 1.0 for white.
func Luminance(col color.Color) float64 {
r, g, b, _ := col.RGBA()
return (float64(r)*0.299 + float64(g)*0.587 + float64(b)*0.114) / float64(0xffff)
}

321
vendor/github.com/flopp/go-staticmaps/context.go generated vendored Normal file
View file

@ -0,0 +1,321 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package sm (~ static maps) renders static map images from OSM tiles with markers, paths, and filled areas.
package sm
import (
"errors"
"image"
"image/draw"
"log"
"math"
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
)
// Context holds all information about the map image that is to be rendered
type Context struct {
width int
height int
hasZoom bool
zoom int
hasCenter bool
center s2.LatLng
hasBoundingBox bool
boundingBox s2.Rect
markers []*Marker
paths []*Path
areas []*Area
tileProvider *TileProvider
}
// NewContext creates a new instance of Context
func NewContext() *Context {
t := new(Context)
t.width = 512
t.height = 512
t.hasZoom = false
t.hasCenter = false
t.hasBoundingBox = false
t.tileProvider = NewTileProviderOpenStreetMaps()
return t
}
// SetTileProvider sets the TileProvider to be used
func (m *Context) SetTileProvider(t *TileProvider) {
m.tileProvider = t
}
// SetSize sets the size of the generated image
func (m *Context) SetSize(width, height int) {
m.width = width
m.height = height
}
// SetZoom sets the zoom level
func (m *Context) SetZoom(zoom int) {
m.zoom = zoom
m.hasZoom = true
}
// SetCenter sets the center coordinates
func (m *Context) SetCenter(center s2.LatLng) {
m.center = center
m.hasCenter = true
}
// SetBoundingBox sets the bounding box
func (m *Context) SetBoundingBox(bbox s2.Rect) {
m.boundingBox = bbox
m.hasBoundingBox = true
}
// AddMarker adds a marker to the Context
func (m *Context) AddMarker(marker *Marker) {
m.markers = append(m.markers, marker)
}
// ClearMarkers removes all markers from the Context
func (m *Context) ClearMarkers() {
m.markers = nil
}
// AddPath adds a path to the Context
func (m *Context) AddPath(path *Path) {
m.paths = append(m.paths, path)
}
// ClearPaths removes all paths from the Context
func (m *Context) ClearPaths() {
m.paths = nil
}
// AddArea adds an area to the Context
func (m *Context) AddArea(area *Area) {
m.areas = append(m.areas, area)
}
// ClearAreas removes all areas from the Context
func (m *Context) ClearAreas() {
m.areas = nil
}
func (m *Context) determineBounds() s2.Rect {
r := s2.EmptyRect()
for _, marker := range m.markers {
r = r.Union(marker.bounds())
}
for _, path := range m.paths {
r = r.Union(path.bounds())
}
for _, area := range m.areas {
r = r.Union(area.bounds())
}
return r
}
func (m *Context) determineExtraMarginPixels() float64 {
p := 0.0
for _, marker := range m.markers {
if pp := marker.extraMarginPixels(); pp > p {
p = pp
}
}
for _, path := range m.paths {
if pp := path.extraMarginPixels(); pp > p {
p = pp
}
}
for _, area := range m.areas {
if pp := area.extraMarginPixels(); pp > p {
p = pp
}
}
return p
}
func (m *Context) determineZoom(bounds s2.Rect, center s2.LatLng) int {
b := bounds.AddPoint(center)
if b.IsEmpty() || b.IsPoint() {
return 15
}
tileSize := m.tileProvider.TileSize
margin := 4.0 + m.determineExtraMarginPixels()
w := (float64(m.width) - 2.0*margin) / float64(tileSize)
h := (float64(m.height) - 2.0*margin) / float64(tileSize)
minX := (b.Lo().Lng.Degrees() + 180.0) / 360.0
maxX := (b.Hi().Lng.Degrees() + 180.0) / 360.0
minY := (1.0 - math.Log(math.Tan(b.Lo().Lat.Radians())+(1.0/math.Cos(b.Lo().Lat.Radians())))/math.Pi) / 2.0
maxY := (1.0 - math.Log(math.Tan(b.Hi().Lat.Radians())+(1.0/math.Cos(b.Hi().Lat.Radians())))/math.Pi) / 2.0
dx := maxX - minX
for dx < 0 {
dx = dx + 1
}
for dx > 1 {
dx = dx - 1
}
dy := math.Abs(maxY - minY)
zoom := 1
for zoom < 30 {
tiles := float64(uint(1) << uint(zoom))
if dx*tiles > w || dy*tiles > h {
return zoom - 1
}
zoom = zoom + 1
}
return 15
}
func (m *Context) determineZoomCenter() (int, s2.LatLng, error) {
bounds := m.determineBounds()
if m.hasBoundingBox && !m.boundingBox.IsEmpty() {
center := m.boundingBox.Center()
return m.determineZoom(m.boundingBox, center), center, nil
} else if m.hasCenter {
if m.hasZoom {
return m.zoom, m.center, nil
}
return m.determineZoom(bounds, m.center), m.center, nil
} else if !bounds.IsEmpty() {
center := bounds.Center()
if m.hasZoom {
return m.zoom, center, nil
}
return m.determineZoom(bounds, center), center, nil
}
return 0, s2.LatLngFromDegrees(0, 0), errors.New("Cannot determine map extent: no center coordinates given, no bounding box given, no content (markers, paths, areas) given")
}
type transformer struct {
zoom int
tileSize int
pWidth, pHeight int
pCenterX, pCenterY int
tCountX, tCountY int
tCenterX, tCenterY float64
tOriginX, tOriginY int
}
func newTransformer(width int, height int, zoom int, llCenter s2.LatLng, tileSize int) *transformer {
t := new(transformer)
t.zoom = zoom
t.tileSize = tileSize
t.tCenterX, t.tCenterY = t.ll2t(llCenter)
ww := float64(width) / float64(tileSize)
hh := float64(height) / float64(tileSize)
t.tOriginX = int(math.Floor(t.tCenterX - 0.5*ww))
t.tOriginY = int(math.Floor(t.tCenterY - 0.5*hh))
t.tCountX = 1 + int(math.Floor(t.tCenterX+0.5*ww)) - t.tOriginX
t.tCountY = 1 + int(math.Floor(t.tCenterY+0.5*hh)) - t.tOriginY
t.pWidth = t.tCountX * tileSize
t.pHeight = t.tCountY * tileSize
t.pCenterX = int((t.tCenterX - float64(t.tOriginX)) * float64(tileSize))
t.pCenterY = int((t.tCenterY - float64(t.tOriginY)) * float64(tileSize))
return t
}
func (t *transformer) ll2t(ll s2.LatLng) (float64, float64) {
tiles := math.Exp2(float64(t.zoom))
x := tiles * (ll.Lng.Degrees() + 180.0) / 360.0
y := tiles * (1 - math.Log(math.Tan(ll.Lat.Radians())+(1.0/math.Cos(ll.Lat.Radians())))/math.Pi) / 2.0
return x, y
}
func (t *transformer) ll2p(ll s2.LatLng) (float64, float64) {
x, y := t.ll2t(ll)
if x < float64(t.tOriginX) {
x = x + math.Exp2(float64(t.zoom))
}
x = float64(t.pCenterX) + (x-t.tCenterX)*float64(t.tileSize)
y = float64(t.pCenterY) + (y-t.tCenterY)*float64(t.tileSize)
return x, y
}
// Render actually renders the map image including all map objects (markers, paths, areas)
func (m *Context) Render() (image.Image, error) {
zoom, center, err := m.determineZoomCenter()
if err != nil {
return nil, err
}
tileSize := m.tileProvider.TileSize
trans := newTransformer(m.width, m.height, zoom, center, tileSize)
img := image.NewRGBA(image.Rect(0, 0, trans.pWidth, trans.pHeight))
gc := gg.NewContextForRGBA(img)
// fetch and draw tiles to img
t := NewTileFetcher(m.tileProvider)
tiles := (1 << uint(zoom))
for xx := 0; xx < trans.tCountX; xx++ {
x := trans.tOriginX + xx
if x < 0 {
x = x + tiles
} else if x >= tiles {
x = x - tiles
}
for yy := 0; yy < trans.tCountY; yy++ {
y := trans.tOriginY + yy
if y < 0 || y >= tiles {
log.Printf("Skipping out of bounds tile %d/%d", x, y)
} else {
if tileImg, err := t.Fetch(zoom, x, y); err == nil {
gc.DrawImage(tileImg, xx*tileSize, yy*tileSize)
} else {
log.Printf("Error downloading tile file: %s", err)
}
}
}
}
// draw map objects
for _, area := range m.areas {
area.draw(gc, trans)
}
for _, path := range m.paths {
path.draw(gc, trans)
}
for _, marker := range m.markers {
marker.draw(gc, trans)
}
// crop image
croppedImg := image.NewRGBA(image.Rect(0, 0, int(m.width), int(m.height)))
draw.Draw(croppedImg, image.Rect(0, 0, int(m.width), int(m.height)),
img, image.Point{trans.pCenterX - int(m.width)/2, trans.pCenterY - int(m.height)/2},
draw.Src)
// draw attribution
if m.tileProvider.Attribution == "" {
return croppedImg, nil
}
_, textHeight := gc.MeasureString(m.tileProvider.Attribution)
boxHeight := textHeight + 4.0
gc = gg.NewContextForRGBA(croppedImg)
gc.SetRGBA(0.0, 0.0, 0.0, 0.5)
gc.DrawRectangle(0.0, float64(m.height)-boxHeight, float64(m.width), boxHeight)
gc.Fill()
gc.SetRGBA(1.0, 1.0, 1.0, 0.75)
gc.DrawString(m.tileProvider.Attribution, 4.0, float64(m.height)-4.0)
return croppedImg, nil
}

18
vendor/github.com/flopp/go-staticmaps/map_object.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
)
// MapObject is the interface for all objects on the map
type MapObject interface {
bounds() s2.Rect
extraMarginPixels() float64
draw(dc *gg.Context, trans *transformer)
}

142
vendor/github.com/flopp/go-staticmaps/marker.go generated vendored Normal file
View file

@ -0,0 +1,142 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"fmt"
"image/color"
"math"
"strconv"
"strings"
"github.com/flopp/go-coordsparser"
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
)
// Marker represents a marker on the map
type Marker struct {
MapObject
Position s2.LatLng
Color color.Color
Size float64
Label string
LabelColor color.Color
}
// NewMarker creates a new Marker
func NewMarker(pos s2.LatLng, col color.Color, size float64) *Marker {
m := new(Marker)
m.Position = pos
m.Color = col
m.Size = size
m.Label = ""
if Luminance(m.Color) >= 0.5 {
m.LabelColor = color.RGBA{0x00, 0x00, 0x00, 0xff}
} else {
m.LabelColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
}
return m
}
func parseSizeString(s string) (float64, error) {
switch {
case s == "mid":
return 16.0, nil
case s == "small":
return 12.0, nil
case s == "tiny":
return 8.0, nil
}
if ss, err := strconv.ParseFloat(s, 64); err != nil && ss > 0 {
return ss, nil
}
return 0.0, fmt.Errorf("Cannot parse size string: %s", s)
}
// ParseMarkerString parses a string and returns an array of markers
func ParseMarkerString(s string) ([]*Marker, error) {
markers := make([]*Marker, 0, 0)
var markerColor color.Color = color.RGBA{0xff, 0, 0, 0xff}
size := 16.0
label := ""
var labelColor color.Color
for _, ss := range strings.Split(s, "|") {
if ok, suffix := hasPrefix(ss, "color:"); ok {
var err error
markerColor, err = ParseColorString(suffix)
if err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "label:"); ok {
label = suffix
} else if ok, suffix := hasPrefix(ss, "size:"); ok {
var err error
size, err = parseSizeString(suffix)
if err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "labelcolor:"); ok {
var err error
labelColor, err = ParseColorString(suffix)
if err != nil {
return nil, err
}
} else {
lat, lng, err := coordsparser.Parse(ss)
if err != nil {
return nil, err
}
m := NewMarker(s2.LatLngFromDegrees(lat, lng), markerColor, size)
m.Label = label
if labelColor != nil {
m.SetLabelColor(labelColor)
}
markers = append(markers, m)
}
}
return markers, nil
}
// SetLabelColor sets the color of the marker's text label
func (m *Marker) SetLabelColor(col color.Color) {
m.LabelColor = col
}
func (m *Marker) extraMarginPixels() float64 {
return 1.0 + 1.5*m.Size
}
func (m *Marker) bounds() s2.Rect {
r := s2.EmptyRect()
r = r.AddPoint(m.Position)
return r
}
func (m *Marker) draw(gc *gg.Context, trans *transformer) {
gc.ClearPath()
gc.SetLineJoin(gg.LineJoinRound)
gc.SetLineWidth(1.0)
radius := 0.5 * m.Size
x, y := trans.ll2p(m.Position)
gc.DrawArc(x, y-m.Size, radius, (90.0+60.0)*math.Pi/180.0, (360.0+90.0-60.0)*math.Pi/180.0)
gc.LineTo(x, y)
gc.ClosePath()
gc.SetColor(m.Color)
gc.FillPreserve()
gc.SetRGB(0, 0, 0)
gc.Stroke()
if m.Label != "" {
gc.SetColor(m.LabelColor)
gc.DrawStringAnchored(m.Label, x, y-m.Size, 0.5, 0.5)
}
}

103
vendor/github.com/flopp/go-staticmaps/path.go generated vendored Normal file
View file

@ -0,0 +1,103 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"image/color"
"strconv"
"strings"
"github.com/flopp/go-coordsparser"
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
"github.com/tkrajina/gpxgo/gpx"
)
// Path represents a path or area on the map
type Path struct {
MapObject
Positions []s2.LatLng
Color color.Color
Weight float64
}
// ParsePathString parses a string and returns a path
func ParsePathString(s string) ([]*Path, error) {
paths := make([]*Path, 0, 0)
currentPath := new(Path)
currentPath.Color = color.RGBA{0xff, 0, 0, 0xff}
currentPath.Weight = 5.0
for _, ss := range strings.Split(s, "|") {
if ok, suffix := hasPrefix(ss, "color:"); ok {
var err error
if currentPath.Color, err = ParseColorString(suffix); err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "weight:"); ok {
var err error
if currentPath.Weight, err = strconv.ParseFloat(suffix, 64); err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "gpx:"); ok {
gpxData, err := gpx.ParseFile(suffix)
if err != nil {
return nil, err
}
for _, trk := range gpxData.Tracks {
for _, seg := range trk.Segments {
p := new(Path)
p.Color = currentPath.Color
p.Weight = currentPath.Weight
for _, pt := range seg.Points {
p.Positions = append(p.Positions, s2.LatLngFromDegrees(pt.GetLatitude(), pt.GetLongitude()))
}
if len(p.Positions) > 0 {
paths = append(paths, p)
}
}
}
} else {
lat, lng, err := coordsparser.Parse(ss)
if err != nil {
return nil, err
}
currentPath.Positions = append(currentPath.Positions, s2.LatLngFromDegrees(lat, lng))
}
}
if len(currentPath.Positions) > 0 {
paths = append(paths, currentPath)
}
return paths, nil
}
func (p *Path) extraMarginPixels() float64 {
return 0.5 * p.Weight
}
func (p *Path) bounds() s2.Rect {
r := s2.EmptyRect()
for _, ll := range p.Positions {
r = r.AddPoint(ll)
}
return r
}
func (p *Path) draw(gc *gg.Context, trans *transformer) {
if len(p.Positions) <= 1 {
return
}
gc.ClearPath()
gc.SetLineWidth(p.Weight)
gc.SetLineCap(gg.LineCapRound)
gc.SetLineJoin(gg.LineJoinRound)
for _, ll := range p.Positions {
gc.LineTo(trans.ll2p(ll))
}
gc.SetColor(p.Color)
gc.Stroke()
}

162
vendor/github.com/flopp/go-staticmaps/tile_fetcher.go generated vendored Normal file
View file

@ -0,0 +1,162 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"bytes"
"fmt"
"image"
_ "image/jpeg" // to be able to decode jpegs
_ "image/png" // to be able to decode pngs
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"github.com/Wessie/appdirs"
)
var TileFetcherUserAgent = "Mozilla/5.0+(compatible; go-staticmaps/0.1; https://github.com/flopp/go-staticmaps)"
// TileFetcher downloads map tile images from a TileProvider
type TileFetcher struct {
tileProvider *TileProvider
cacheDir string
useCaching bool
}
// NewTileFetcher creates a new Tilefetcher struct
func NewTileFetcher(tileProvider *TileProvider) *TileFetcher {
t := new(TileFetcher)
t.tileProvider = tileProvider
app := appdirs.New("go-staticmaps", "flopp.net", "0.1")
t.cacheDir = fmt.Sprintf("%s/%s", app.UserCache(), tileProvider.Name)
t.useCaching = true
return t
}
func (t *TileFetcher) url(zoom, x, y int) string {
shard := ""
ss := len(t.tileProvider.Shards)
if len(t.tileProvider.Shards) > 0 {
shard = t.tileProvider.Shards[(x+y)%ss]
}
return t.tileProvider.getURL(shard, zoom, x, y)
}
func (t *TileFetcher) cacheFileName(zoom int, x, y int) string {
return fmt.Sprintf("%s/%d/%d/%d", t.cacheDir, zoom, x, y)
}
// ToggleCaching enables/disables caching
func (t *TileFetcher) ToggleCaching(enabled bool) {
t.useCaching = enabled
}
// Fetch download (or retrieves from the cache) a tile image for the specified zoom level and tile coordinates
func (t *TileFetcher) Fetch(zoom, x, y int) (image.Image, error) {
if t.useCaching {
fileName := t.cacheFileName(zoom, x, y)
cachedImg, err := t.loadCache(fileName)
if err == nil {
return cachedImg, nil
}
}
url := t.url(zoom, x, y)
data, err := t.download(url)
if err != nil {
return nil, err
}
img, _, err := image.Decode(bytes.NewBuffer(data))
if err != nil {
return nil, err
}
if t.useCaching {
fileName := t.cacheFileName(zoom, x, y)
if err := t.storeCache(fileName, data); err != nil {
log.Printf("Failed to store map tile as '%s': %s", fileName, err)
}
}
return img, nil
}
func (t *TileFetcher) download(url string) ([]byte, error) {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", TileFetcherUserAgent)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("GET %s: %s", url, resp.Status)
}
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return contents, nil
}
func (t *TileFetcher) loadCache(fileName string) (image.Image, error) {
file, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return img, nil
}
func (t *TileFetcher) createCacheDir(path string) error {
src, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return os.MkdirAll(path, 0777)
}
return err
}
if src.IsDir() {
return nil
}
return fmt.Errorf("File exists but is not a directory: %s", path)
}
func (t *TileFetcher) storeCache(fileName string, data []byte) error {
dir, _ := filepath.Split(fileName)
if err := t.createCacheDir(dir); err != nil {
return err
}
file, err := os.Create(fileName)
if err != nil {
return err
}
defer file.Close()
if _, err = io.Copy(file, bytes.NewBuffer(data)); err != nil {
return err
}
return nil
}

158
vendor/github.com/flopp/go-staticmaps/tile_provider.go generated vendored Normal file
View file

@ -0,0 +1,158 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import "fmt"
// TileProvider encapsulates all infos about a map tile provider service (name, url scheme, attribution, etc.)
type TileProvider struct {
Name string
Attribution string
TileSize int
URLPattern string // "%[1]s" => shard, "%[2]d" => zoom, "%[3]d" => x, "%[4]d" => y
Shards []string
}
func (t *TileProvider) getURL(shard string, zoom, x, y int) string {
return fmt.Sprintf(t.URLPattern, shard, zoom, x, y)
}
// NewTileProviderOpenStreetMaps creates a TileProvider struct for OSM's tile service
func NewTileProviderOpenStreetMaps() *TileProvider {
t := new(TileProvider)
t.Name = "osm"
t.Attribution = "Maps and Data (c) openstreetmap.org and contributors, ODbL"
t.TileSize = 256
t.URLPattern = "http://%[1]s.tile.openstreetmap.org/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b", "c"}
return t
}
func newTileProviderThunderforest(name string) *TileProvider {
t := new(TileProvider)
t.Name = fmt.Sprintf("thunderforest-%s", name)
t.Attribution = "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL"
t.TileSize = 256
t.URLPattern = "https://%[1]s.tile.thunderforest.com/" + name + "/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b", "c"}
return t
}
// NewTileProviderThunderforestLandscape creates a TileProvider struct for thundeforests's 'landscape' tile service
func NewTileProviderThunderforestLandscape() *TileProvider {
return newTileProviderThunderforest("landscape")
}
// NewTileProviderThunderforestOutdoors creates a TileProvider struct for thundeforests's 'outdoors' tile service
func NewTileProviderThunderforestOutdoors() *TileProvider {
return newTileProviderThunderforest("outdoors")
}
// NewTileProviderThunderforestTransport creates a TileProvider struct for thundeforests's 'transport' tile service
func NewTileProviderThunderforestTransport() *TileProvider {
return newTileProviderThunderforest("transport")
}
// NewTileProviderStamenToner creates a TileProvider struct for stamens' 'toner' tile service
func NewTileProviderStamenToner() *TileProvider {
t := new(TileProvider)
t.Name = "stamen-toner"
t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
t.TileSize = 256
t.URLPattern = "http://%[1]s.tile.stamen.com/toner/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b", "c", "d"}
return t
}
// NewTileProviderStamenTerrain creates a TileProvider struct for stamens' 'terrain' tile service
func NewTileProviderStamenTerrain() *TileProvider {
t := new(TileProvider)
t.Name = "stamen-terrain"
t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
t.TileSize = 256
t.URLPattern = "http://%[1]s.tile.stamen.com/terrain/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b", "c", "d"}
return t
}
// NewTileProviderOpenTopoMap creates a TileProvider struct for opentopomap's tile service
func NewTileProviderOpenTopoMap() *TileProvider {
t := new(TileProvider)
t.Name = "opentopomap"
t.Attribution = "Maps (c) OpenTopoMap [CC-BY-SA]; Data (c) OSM and contributors [ODbL]; Data (c) SRTM"
t.TileSize = 256
t.URLPattern = "http://%[1]s.tile.opentopomap.org/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b", "c"}
return t
}
// NewTileProviderWikimedia creates a TileProvider struct for Wikimedia's tile service
func NewTileProviderWikimedia() *TileProvider {
t := new(TileProvider)
t.Name = "wikimedia"
t.Attribution = "Map (c) Wikimedia; Data (c) OSM and contributors, ODbL."
t.TileSize = 256
t.URLPattern = "https://maps.wikimedia.org/osm-intl/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{}
return t
}
// NewTileProviderOpenCycleMap creates a TileProvider struct for OpenCycleMap's tile service
func NewTileProviderOpenCycleMap() *TileProvider {
t := new(TileProvider)
t.Name = "cycle"
t.Attribution = "Maps and Data (c) openstreetmaps.org and contributors, ODbL"
t.TileSize = 256
t.URLPattern = "http://%[1]s.tile.opencyclemap.org/cycle/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b"}
return t
}
func newTileProviderCarto(name string) *TileProvider {
t := new(TileProvider)
t.Name = fmt.Sprintf("carto-%s", name)
t.Attribution = "Map (c) Carto [CC BY 3.0] Data (c) OSM and contributors, ODbL."
t.TileSize = 256
t.URLPattern = "https://cartodb-basemaps-%[1]s.global.ssl.fastly.net/" + name + "_all/%[2]d/%[3]d/%[4]d.png"
t.Shards = []string{"a", "b", "c", "d"}
return t
}
// NewTileProviderCartoLight creates a TileProvider struct for Carto's tile service (light variant)
func NewTileProviderCartoLight() *TileProvider {
return newTileProviderCarto("light")
}
// NewTileProviderCartoDark creates a TileProvider struct for Carto's tile service (dark variant)
func NewTileProviderCartoDark() *TileProvider {
return newTileProviderCarto("dark")
}
// GetTileProviders returns a map of all available TileProviders
func GetTileProviders() map[string]*TileProvider {
m := make(map[string]*TileProvider)
list := []*TileProvider{
NewTileProviderOpenStreetMaps(),
NewTileProviderOpenCycleMap(),
NewTileProviderThunderforestLandscape(),
NewTileProviderThunderforestOutdoors(),
NewTileProviderThunderforestTransport(),
NewTileProviderStamenToner(),
NewTileProviderStamenTerrain(),
NewTileProviderOpenTopoMap(),
NewTileProviderOpenStreetMaps(),
NewTileProviderOpenCycleMap(),
NewTileProviderCartoLight(),
NewTileProviderCartoDark(),
}
for _, tp := range list {
m[tp.Name] = tp
}
return m
}

18
vendor/github.com/flopp/go-staticmaps/util.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sm
import (
"strings"
)
// hasPrefix checks if 's' has prefix 'prefix'; returns 'true' and the remainder on success, and 'false', 's' otherwise.
func hasPrefix(s string, prefix string) (bool, string) {
if strings.HasPrefix(s, prefix) {
return true, strings.TrimPrefix(s, prefix)
}
return false, s
}

2
vendor/github.com/fogleman/gg/.gitignore generated vendored Normal file
View file

@ -0,0 +1,2 @@
*.png

19
vendor/github.com/fogleman/gg/LICENSE.md generated vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (C) 2016 Michael Fogleman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

216
vendor/github.com/fogleman/gg/README.md generated vendored Normal file
View file

@ -0,0 +1,216 @@
# Go Graphics
`gg` is a library for rendering 2D graphics in pure Go.
![Stars](http://i.imgur.com/CylQIJt.png)
## Installation
go get -u github.com/fogleman/gg
Alternatively, you may use gopkg.in to grab a specific major-version:
go get -u gopkg.in/fogleman/gg.v1
## Documentation
https://godoc.org/github.com/fogleman/gg
## Hello, Circle!
Look how easy!
```go
package main
import "github.com/fogleman/gg"
func main() {
dc := gg.NewContext(1000, 1000)
dc.DrawCircle(500, 500, 400)
dc.SetRGB(0, 0, 0)
dc.Fill()
dc.SavePNG("out.png")
}
```
## Examples
There are [lots of examples](https://github.com/fogleman/gg/tree/master/examples) included. They're mostly for testing the code, but they're good for learning, too.
![Examples](http://i.imgur.com/tMFoyzu.png)
## Creating Contexts
There are a few ways of creating a context.
```go
NewContext(width, height int) *Context
NewContextForImage(im image.Image) *Context
NewContextForRGBA(im *image.RGBA) *Context
```
## Drawing Functions
Ever used a graphics library that didn't have functions for drawing rectangles
or circles? What a pain!
```go
DrawPoint(x, y, r float64)
DrawLine(x1, y1, x2, y2 float64)
DrawRectangle(x, y, w, h float64)
DrawRoundedRectangle(x, y, w, h, r float64)
DrawCircle(x, y, r float64)
DrawArc(x, y, r, angle1, angle2 float64)
DrawEllipse(x, y, rx, ry float64)
DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64)
DrawRegularPolygon(n int, x, y, r, rotation float64)
DrawImage(im image.Image, x, y int)
DrawImageAnchored(im image.Image, x, y int, ax, ay float64)
SetPixel(x, y int)
MoveTo(x, y float64)
LineTo(x, y float64)
QuadraticTo(x1, y1, x2, y2 float64)
CubicTo(x1, y1, x2, y2, x3, y3 float64)
ClosePath()
ClearPath()
NewSubPath()
Clear()
Stroke()
Fill()
StrokePreserve()
FillPreserve()
```
It is often desired to center an image at a point. Use `DrawImageAnchored` with `ax` and `ay` set to 0.5 to do this. Use 0 to left or top align. Use 1 to right or bottom align. `DrawStringAnchored` does the same for text, so you don't need to call `MeasureString` yourself.
## Text Functions
It will even do word wrap for you!
```go
DrawString(s string, x, y float64)
DrawStringAnchored(s string, x, y, ax, ay float64)
DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align)
MeasureString(s string) (w, h float64)
WordWrap(s string, w float64) []string
SetFontFace(fontFace font.Face)
LoadFontFace(path string, points float64) error
```
## Color Functions
Colors can be set in several different ways for your convenience.
```go
SetRGB(r, g, b float64)
SetRGBA(r, g, b, a float64)
SetRGB255(r, g, b int)
SetRGBA255(r, g, b, a int)
SetColor(c color.Color)
SetHexColor(x string)
```
## Stroke & Fill Options
```go
SetLineWidth(lineWidth float64)
SetLineCap(lineCap LineCap)
SetLineJoin(lineJoin LineJoin)
SetDash(dashes ...float64)
SetFillRule(fillRule FillRule)
```
## Gradients & Patterns
`gg` supports linear and radial gradients and surface patterns. You can also implement your own patterns.
```go
SetFillStyle(pattern Pattern)
SetStrokeStyle(pattern Pattern)
NewSolidPattern(color color.Color)
NewLinearGradient(x0, y0, x1, y1 float64)
NewRadialGradient(x0, y0, r0, x1, y1, r1 float64)
NewSurfacePattern(im image.Image, op RepeatOp)
```
## Transformation Functions
```go
Identity()
Translate(x, y float64)
Scale(x, y float64)
Rotate(angle float64)
Shear(x, y float64)
ScaleAbout(sx, sy, x, y float64)
RotateAbout(angle, x, y float64)
ShearAbout(sx, sy, x, y float64)
TransformPoint(x, y float64) (tx, ty float64)
InvertY()
```
It is often desired to rotate or scale about a point that is not the origin. The functions `RotateAbout`, `ScaleAbout`, `ShearAbout` are provided as a convenience.
`InvertY` is provided in case Y should increase from bottom to top vs. the default top to bottom.
## Stack Functions
Save and restore the state of the context. These can be nested.
```go
Push()
Pop()
```
## Clipping Functions
Use clipping regions to restrict drawing operations to an area that you
defined using paths.
```go
Clip()
ClipPreserve()
ResetClip()
```
## Helper Functions
Sometimes you just don't want to write these yourself.
```go
Radians(degrees float64) float64
Degrees(radians float64) float64
LoadImage(path string) (image.Image, error)
LoadPNG(path string) (image.Image, error)
SavePNG(path string, im image.Image) error
```
![Separator](http://i.imgur.com/fsUvnPB.png)
## Another Example
See the output of this example below.
```go
package main
import "github.com/fogleman/gg"
func main() {
const S = 1024
dc := gg.NewContext(S, S)
dc.SetRGBA(0, 0, 0, 0.1)
for i := 0; i < 360; i += 15 {
dc.Push()
dc.RotateAbout(gg.Radians(float64(i)), S/2, S/2)
dc.DrawEllipse(S/2, S/2, S*7/16, S/8)
dc.Fill()
dc.Pop()
}
dc.SavePNG("out.png")
}
```
![Ellipses](http://i.imgur.com/J9CBZef.png)

59
vendor/github.com/fogleman/gg/bezier.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
package gg
import "math"
func quadratic(x0, y0, x1, y1, x2, y2, t float64) (x, y float64) {
u := 1 - t
a := u * u
b := 2 * u * t
c := t * t
x = a*x0 + b*x1 + c*x2
y = a*y0 + b*y1 + c*y2
return
}
func QuadraticBezier(x0, y0, x1, y1, x2, y2 float64) []Point {
l := (math.Hypot(x1-x0, y1-y0) +
math.Hypot(x2-x1, y2-y1))
n := int(l + 0.5)
if n < 4 {
n = 4
}
d := float64(n) - 1
result := make([]Point, n)
for i := 0; i < n; i++ {
t := float64(i) / d
x, y := quadratic(x0, y0, x1, y1, x2, y2, t)
result[i] = Point{x, y}
}
return result
}
func cubic(x0, y0, x1, y1, x2, y2, x3, y3, t float64) (x, y float64) {
u := 1 - t
a := u * u * u
b := 3 * u * u * t
c := 3 * u * t * t
d := t * t * t
x = a*x0 + b*x1 + c*x2 + d*x3
y = a*y0 + b*y1 + c*y2 + d*y3
return
}
func CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) []Point {
l := (math.Hypot(x1-x0, y1-y0) +
math.Hypot(x2-x1, y2-y1) +
math.Hypot(x3-x2, y3-y2))
n := int(l + 0.5)
if n < 4 {
n = 4
}
d := float64(n) - 1
result := make([]Point, n)
for i := 0; i < n; i++ {
t := float64(i) / d
x, y := cubic(x0, y0, x1, y1, x2, y2, x3, y3, t)
result[i] = Point{x, y}
}
return result
}

795
vendor/github.com/fogleman/gg/context.go generated vendored Normal file
View file

@ -0,0 +1,795 @@
// Package gg provides a simple API for rendering 2D graphics in pure Go.
package gg
import (
"image"
"image/color"
"image/png"
"io"
"math"
"github.com/golang/freetype/raster"
"golang.org/x/image/draw"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/f64"
)
type LineCap int
const (
LineCapRound LineCap = iota
LineCapButt
LineCapSquare
)
type LineJoin int
const (
LineJoinRound LineJoin = iota
LineJoinBevel
)
type FillRule int
const (
FillRuleWinding FillRule = iota
FillRuleEvenOdd
)
type Align int
const (
AlignLeft Align = iota
AlignCenter
AlignRight
)
var (
defaultFillStyle = NewSolidPattern(color.White)
defaultStrokeStyle = NewSolidPattern(color.Black)
)
type Context struct {
width int
height int
im *image.RGBA
mask *image.Alpha
color color.Color
fillPattern Pattern
strokePattern Pattern
strokePath raster.Path
fillPath raster.Path
start Point
current Point
hasCurrent bool
dashes []float64
lineWidth float64
lineCap LineCap
lineJoin LineJoin
fillRule FillRule
fontFace font.Face
fontHeight float64
matrix Matrix
stack []*Context
}
// NewContext creates a new image.RGBA with the specified width and height
// and prepares a context for rendering onto that image.
func NewContext(width, height int) *Context {
return NewContextForRGBA(image.NewRGBA(image.Rect(0, 0, width, height)))
}
// NewContextForImage copies the specified image into a new image.RGBA
// and prepares a context for rendering onto that image.
func NewContextForImage(im image.Image) *Context {
return NewContextForRGBA(imageToRGBA(im))
}
// NewContextForRGBA prepares a context for rendering onto the specified image.
// No copy is made.
func NewContextForRGBA(im *image.RGBA) *Context {
return &Context{
width: im.Bounds().Size().X,
height: im.Bounds().Size().Y,
im: im,
color: color.Transparent,
fillPattern: defaultFillStyle,
strokePattern: defaultStrokeStyle,
lineWidth: 1,
fillRule: FillRuleWinding,
fontFace: basicfont.Face7x13,
fontHeight: 13,
matrix: Identity(),
}
}
// Image returns the image that has been drawn by this context.
func (dc *Context) Image() image.Image {
return dc.im
}
// Width returns the width of the image in pixels.
func (dc *Context) Width() int {
return dc.width
}
// Height returns the height of the image in pixels.
func (dc *Context) Height() int {
return dc.height
}
// SavePNG encodes the image as a PNG and writes it to disk.
func (dc *Context) SavePNG(path string) error {
return SavePNG(path, dc.im)
}
// EncodePNG encodes the image as a PNG and writes it to the provided io.Writer.
func (dc *Context) EncodePNG(w io.Writer) error {
return png.Encode(w, dc.im)
}
// SetDash sets the current dash pattern to use. Call with zero arguments to
// disable dashes. The values specify the lengths of each dash, with
// alternating on and off lengths.
func (dc *Context) SetDash(dashes ...float64) {
dc.dashes = dashes
}
func (dc *Context) SetLineWidth(lineWidth float64) {
dc.lineWidth = lineWidth
}
func (dc *Context) SetLineCap(lineCap LineCap) {
dc.lineCap = lineCap
}
func (dc *Context) SetLineCapRound() {
dc.lineCap = LineCapRound
}
func (dc *Context) SetLineCapButt() {
dc.lineCap = LineCapButt
}
func (dc *Context) SetLineCapSquare() {
dc.lineCap = LineCapSquare
}
func (dc *Context) SetLineJoin(lineJoin LineJoin) {
dc.lineJoin = lineJoin
}
func (dc *Context) SetLineJoinRound() {
dc.lineJoin = LineJoinRound
}
func (dc *Context) SetLineJoinBevel() {
dc.lineJoin = LineJoinBevel
}
func (dc *Context) SetFillRule(fillRule FillRule) {
dc.fillRule = fillRule
}
func (dc *Context) SetFillRuleWinding() {
dc.fillRule = FillRuleWinding
}
func (dc *Context) SetFillRuleEvenOdd() {
dc.fillRule = FillRuleEvenOdd
}
// Color Setters
func (dc *Context) setFillAndStrokeColor(c color.Color) {
dc.color = c
dc.fillPattern = NewSolidPattern(c)
dc.strokePattern = NewSolidPattern(c)
}
// SetFillStyle sets current fill style
func (dc *Context) SetFillStyle(pattern Pattern) {
// if pattern is SolidPattern, also change dc.color(for dc.Clear, dc.drawString)
if fillStyle, ok := pattern.(*solidPattern); ok {
dc.color = fillStyle.color
}
dc.fillPattern = pattern
}
// SetStrokeStyle sets current stroke style
func (dc *Context) SetStrokeStyle(pattern Pattern) {
dc.strokePattern = pattern
}
// SetColor sets the current color(for both fill and stroke).
func (dc *Context) SetColor(c color.Color) {
dc.setFillAndStrokeColor(c)
}
// SetHexColor sets the current color using a hex string. The leading pound
// sign (#) is optional. Both 3- and 6-digit variations are supported. 8 digits
// may be provided to set the alpha value as well.
func (dc *Context) SetHexColor(x string) {
r, g, b, a := parseHexColor(x)
dc.SetRGBA255(r, g, b, a)
}
// SetRGBA255 sets the current color. r, g, b, a values should be between 0 and
// 255, inclusive.
func (dc *Context) SetRGBA255(r, g, b, a int) {
dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
dc.setFillAndStrokeColor(dc.color)
}
// SetRGB255 sets the current color. r, g, b values should be between 0 and 255,
// inclusive. Alpha will be set to 255 (fully opaque).
func (dc *Context) SetRGB255(r, g, b int) {
dc.SetRGBA255(r, g, b, 255)
}
// SetRGBA sets the current color. r, g, b, a values should be between 0 and 1,
// inclusive.
func (dc *Context) SetRGBA(r, g, b, a float64) {
dc.color = color.NRGBA{
uint8(r * 255),
uint8(g * 255),
uint8(b * 255),
uint8(a * 255),
}
dc.setFillAndStrokeColor(dc.color)
}
// SetRGB sets the current color. r, g, b values should be between 0 and 1,
// inclusive. Alpha will be set to 1 (fully opaque).
func (dc *Context) SetRGB(r, g, b float64) {
dc.SetRGBA(r, g, b, 1)
}
// Path Manipulation
// MoveTo starts a new subpath within the current path starting at the
// specified point.
func (dc *Context) MoveTo(x, y float64) {
if dc.hasCurrent {
dc.fillPath.Add1(dc.start.Fixed())
}
x, y = dc.TransformPoint(x, y)
p := Point{x, y}
dc.strokePath.Start(p.Fixed())
dc.fillPath.Start(p.Fixed())
dc.start = p
dc.current = p
dc.hasCurrent = true
}
// LineTo adds a line segment to the current path starting at the current
// point. If there is no current point, it is equivalent to MoveTo(x, y)
func (dc *Context) LineTo(x, y float64) {
if !dc.hasCurrent {
dc.MoveTo(x, y)
} else {
x, y = dc.TransformPoint(x, y)
p := Point{x, y}
dc.strokePath.Add1(p.Fixed())
dc.fillPath.Add1(p.Fixed())
dc.current = p
}
}
// QuadraticTo adds a quadratic bezier curve to the current path starting at
// the current point. If there is no current point, it first performs
// MoveTo(x1, y1)
func (dc *Context) QuadraticTo(x1, y1, x2, y2 float64) {
if !dc.hasCurrent {
dc.MoveTo(x1, y1)
}
x1, y1 = dc.TransformPoint(x1, y1)
x2, y2 = dc.TransformPoint(x2, y2)
p1 := Point{x1, y1}
p2 := Point{x2, y2}
dc.strokePath.Add2(p1.Fixed(), p2.Fixed())
dc.fillPath.Add2(p1.Fixed(), p2.Fixed())
dc.current = p2
}
// CubicTo adds a cubic bezier curve to the current path starting at the
// current point. If there is no current point, it first performs
// MoveTo(x1, y1). Because freetype/raster does not support cubic beziers,
// this is emulated with many small line segments.
func (dc *Context) CubicTo(x1, y1, x2, y2, x3, y3 float64) {
if !dc.hasCurrent {
dc.MoveTo(x1, y1)
}
x0, y0 := dc.current.X, dc.current.Y
x1, y1 = dc.TransformPoint(x1, y1)
x2, y2 = dc.TransformPoint(x2, y2)
x3, y3 = dc.TransformPoint(x3, y3)
points := CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3)
previous := dc.current.Fixed()
for _, p := range points[1:] {
f := p.Fixed()
if f == previous {
// TODO: this fixes some rendering issues but not all
continue
}
previous = f
dc.strokePath.Add1(f)
dc.fillPath.Add1(f)
dc.current = p
}
}
// ClosePath adds a line segment from the current point to the beginning
// of the current subpath. If there is no current point, this is a no-op.
func (dc *Context) ClosePath() {
if dc.hasCurrent {
dc.strokePath.Add1(dc.start.Fixed())
dc.fillPath.Add1(dc.start.Fixed())
dc.current = dc.start
}
}
// ClearPath clears the current path. There is no current point after this
// operation.
func (dc *Context) ClearPath() {
dc.strokePath.Clear()
dc.fillPath.Clear()
dc.hasCurrent = false
}
// NewSubPath starts a new subpath within the current path. There is no current
// point after this operation.
func (dc *Context) NewSubPath() {
if dc.hasCurrent {
dc.fillPath.Add1(dc.start.Fixed())
}
dc.hasCurrent = false
}
// Path Drawing
func (dc *Context) capper() raster.Capper {
switch dc.lineCap {
case LineCapButt:
return raster.ButtCapper
case LineCapRound:
return raster.RoundCapper
case LineCapSquare:
return raster.SquareCapper
}
return nil
}
func (dc *Context) joiner() raster.Joiner {
switch dc.lineJoin {
case LineJoinBevel:
return raster.BevelJoiner
case LineJoinRound:
return raster.RoundJoiner
}
return nil
}
func (dc *Context) stroke(painter raster.Painter) {
path := dc.strokePath
if len(dc.dashes) > 0 {
path = dashed(path, dc.dashes)
} else {
// TODO: this is a temporary workaround to remove tiny segments
// that result in rendering issues
path = rasterPath(flattenPath(path))
}
r := raster.NewRasterizer(dc.width, dc.height)
r.UseNonZeroWinding = true
r.AddStroke(path, fix(dc.lineWidth), dc.capper(), dc.joiner())
r.Rasterize(painter)
}
func (dc *Context) fill(painter raster.Painter) {
path := dc.fillPath
if dc.hasCurrent {
path = make(raster.Path, len(dc.fillPath))
copy(path, dc.fillPath)
path.Add1(dc.start.Fixed())
}
r := raster.NewRasterizer(dc.width, dc.height)
r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
r.AddPath(path)
r.Rasterize(painter)
}
// StrokePreserve strokes the current path with the current color, line width,
// line cap, line join and dash settings. The path is preserved after this
// operation.
func (dc *Context) StrokePreserve() {
painter := newPatternPainter(dc.im, dc.mask, dc.strokePattern)
dc.stroke(painter)
}
// Stroke strokes the current path with the current color, line width,
// line cap, line join and dash settings. The path is cleared after this
// operation.
func (dc *Context) Stroke() {
dc.StrokePreserve()
dc.ClearPath()
}
// FillPreserve fills the current path with the current color. Open subpaths
// are implicity closed. The path is preserved after this operation.
func (dc *Context) FillPreserve() {
painter := newPatternPainter(dc.im, dc.mask, dc.fillPattern)
dc.fill(painter)
}
// Fill fills the current path with the current color. Open subpaths
// are implicity closed. The path is cleared after this operation.
func (dc *Context) Fill() {
dc.FillPreserve()
dc.ClearPath()
}
// ClipPreserve updates the clipping region by intersecting the current
// clipping region with the current path as it would be filled by dc.Fill().
// The path is preserved after this operation.
func (dc *Context) ClipPreserve() {
clip := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
painter := raster.NewAlphaOverPainter(clip)
dc.fill(painter)
if dc.mask == nil {
dc.mask = clip
} else {
mask := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
draw.DrawMask(mask, mask.Bounds(), clip, image.ZP, dc.mask, image.ZP, draw.Over)
dc.mask = mask
}
}
// Clip updates the clipping region by intersecting the current
// clipping region with the current path as it would be filled by dc.Fill().
// The path is cleared after this operation.
func (dc *Context) Clip() {
dc.ClipPreserve()
dc.ClearPath()
}
// ResetClip clears the clipping region.
func (dc *Context) ResetClip() {
dc.mask = nil
}
// Convenient Drawing Functions
// Clear fills the entire image with the current color.
func (dc *Context) Clear() {
src := image.NewUniform(dc.color)
draw.Draw(dc.im, dc.im.Bounds(), src, image.ZP, draw.Src)
}
// SetPixel sets the color of the specified pixel using the current color.
func (dc *Context) SetPixel(x, y int) {
dc.im.Set(x, y, dc.color)
}
// DrawPoint is like DrawCircle but ensures that a circle of the specified
// size is drawn regardless of the current transformation matrix. The position
// is still transformed, but not the shape of the point.
func (dc *Context) DrawPoint(x, y, r float64) {
dc.Push()
tx, ty := dc.TransformPoint(x, y)
dc.Identity()
dc.DrawCircle(tx, ty, r)
dc.Pop()
}
func (dc *Context) DrawLine(x1, y1, x2, y2 float64) {
dc.MoveTo(x1, y1)
dc.LineTo(x2, y2)
}
func (dc *Context) DrawRectangle(x, y, w, h float64) {
dc.NewSubPath()
dc.MoveTo(x, y)
dc.LineTo(x+w, y)
dc.LineTo(x+w, y+h)
dc.LineTo(x, y+h)
dc.ClosePath()
}
func (dc *Context) DrawRoundedRectangle(x, y, w, h, r float64) {
x0, x1, x2, x3 := x, x+r, x+w-r, x+w
y0, y1, y2, y3 := y, y+r, y+h-r, y+h
dc.NewSubPath()
dc.MoveTo(x1, y0)
dc.LineTo(x2, y0)
dc.DrawArc(x2, y1, r, Radians(270), Radians(360))
dc.LineTo(x3, y2)
dc.DrawArc(x2, y2, r, Radians(0), Radians(90))
dc.LineTo(x1, y3)
dc.DrawArc(x1, y2, r, Radians(90), Radians(180))
dc.LineTo(x0, y1)
dc.DrawArc(x1, y1, r, Radians(180), Radians(270))
dc.ClosePath()
}
func (dc *Context) DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) {
const n = 16
for i := 0; i < n; i++ {
p1 := float64(i+0) / n
p2 := float64(i+1) / n
a1 := angle1 + (angle2-angle1)*p1
a2 := angle1 + (angle2-angle1)*p2
x0 := x + rx*math.Cos(a1)
y0 := y + ry*math.Sin(a1)
x1 := x + rx*math.Cos(a1+(a2-a1)/2)
y1 := y + ry*math.Sin(a1+(a2-a1)/2)
x2 := x + rx*math.Cos(a2)
y2 := y + ry*math.Sin(a2)
cx := 2*x1 - x0/2 - x2/2
cy := 2*y1 - y0/2 - y2/2
if i == 0 && !dc.hasCurrent {
dc.MoveTo(x0, y0)
}
dc.QuadraticTo(cx, cy, x2, y2)
}
}
func (dc *Context) DrawEllipse(x, y, rx, ry float64) {
dc.NewSubPath()
dc.DrawEllipticalArc(x, y, rx, ry, 0, 2*math.Pi)
dc.ClosePath()
}
func (dc *Context) DrawArc(x, y, r, angle1, angle2 float64) {
dc.DrawEllipticalArc(x, y, r, r, angle1, angle2)
}
func (dc *Context) DrawCircle(x, y, r float64) {
dc.NewSubPath()
dc.DrawEllipticalArc(x, y, r, r, 0, 2*math.Pi)
dc.ClosePath()
}
func (dc *Context) DrawRegularPolygon(n int, x, y, r, rotation float64) {
angle := 2 * math.Pi / float64(n)
rotation -= math.Pi / 2
if n%2 == 0 {
rotation += angle / 2
}
dc.NewSubPath()
for i := 0; i < n; i++ {
a := rotation + angle*float64(i)
dc.LineTo(x+r*math.Cos(a), y+r*math.Sin(a))
}
dc.ClosePath()
}
// DrawImage draws the specified image at the specified point.
func (dc *Context) DrawImage(im image.Image, x, y int) {
dc.DrawImageAnchored(im, x, y, 0, 0)
}
// DrawImageAnchored draws the specified image at the specified anchor point.
// The anchor point is x - w * ax, y - h * ay, where w, h is the size of the
// image. Use ax=0.5, ay=0.5 to center the image at the specified point.
func (dc *Context) DrawImageAnchored(im image.Image, x, y int, ax, ay float64) {
s := im.Bounds().Size()
x -= int(ax * float64(s.X))
y -= int(ay * float64(s.Y))
transformer := draw.BiLinear
fx, fy := float64(x), float64(y)
m := dc.matrix.Translate(fx, fy)
s2d := f64.Aff3{m.XX, m.XY, m.X0, m.YX, m.YY, m.Y0}
if dc.mask == nil {
transformer.Transform(dc.im, s2d, im, im.Bounds(), draw.Over, nil)
} else {
transformer.Transform(dc.im, s2d, im, im.Bounds(), draw.Over, &draw.Options{
DstMask: dc.mask,
DstMaskP: image.ZP,
})
}
}
// Text Functions
func (dc *Context) SetFontFace(fontFace font.Face) {
dc.fontFace = fontFace
dc.fontHeight = float64(fontFace.Metrics().Height) / 64
}
func (dc *Context) LoadFontFace(path string, points float64) error {
face, err := LoadFontFace(path, points)
if err == nil {
dc.fontFace = face
dc.fontHeight = points * 72 / 96
}
return err
}
func (dc *Context) drawString(im *image.RGBA, s string, x, y float64) {
d := &font.Drawer{
Dst: im,
Src: image.NewUniform(dc.color),
Face: dc.fontFace,
Dot: fixp(x, y),
}
// based on Drawer.DrawString() in golang.org/x/image/font/font.go
prevC := rune(-1)
for _, c := range s {
if prevC >= 0 {
d.Dot.X += d.Face.Kern(prevC, c)
}
dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c)
if !ok {
// TODO: is falling back on the U+FFFD glyph the responsibility of
// the Drawer or the Face?
// TODO: set prevC = '\ufffd'?
continue
}
sr := dr.Sub(dr.Min)
transformer := draw.BiLinear
fx, fy := float64(dr.Min.X), float64(dr.Min.Y)
m := dc.matrix.Translate(fx, fy)
s2d := f64.Aff3{m.XX, m.XY, m.X0, m.YX, m.YY, m.Y0}
transformer.Transform(d.Dst, s2d, d.Src, sr, draw.Over, &draw.Options{
SrcMask: mask,
SrcMaskP: maskp,
})
d.Dot.X += advance
prevC = c
}
}
// DrawString draws the specified text at the specified point.
func (dc *Context) DrawString(s string, x, y float64) {
dc.DrawStringAnchored(s, x, y, 0, 0)
}
// DrawStringAnchored draws the specified text at the specified anchor point.
// The anchor point is x - w * ax, y - h * ay, where w, h is the size of the
// text. Use ax=0.5, ay=0.5 to center the text at the specified point.
func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) {
w, h := dc.MeasureString(s)
x -= ax * w
y += ay * h
if dc.mask == nil {
dc.drawString(dc.im, s, x, y)
} else {
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
dc.drawString(im, s, x, y)
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
}
}
// DrawStringWrapped word-wraps the specified string to the given max width
// and then draws it at the specified anchor point using the given line
// spacing and text alignment.
func (dc *Context) DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align) {
lines := dc.WordWrap(s, width)
h := float64(len(lines)) * dc.fontHeight * lineSpacing
h -= (lineSpacing - 1) * dc.fontHeight
x -= ax * width
y -= ay * h
switch align {
case AlignLeft:
ax = 0
case AlignCenter:
ax = 0.5
x += width / 2
case AlignRight:
ax = 1
x += width
}
ay = 1
for _, line := range lines {
dc.DrawStringAnchored(line, x, y, ax, ay)
y += dc.fontHeight * lineSpacing
}
}
// MeasureString returns the rendered width and height of the specified text
// given the current font face.
func (dc *Context) MeasureString(s string) (w, h float64) {
d := &font.Drawer{
Face: dc.fontFace,
}
a := d.MeasureString(s)
return float64(a >> 6), dc.fontHeight
}
// WordWrap wraps the specified string to the given max width and current
// font face.
func (dc *Context) WordWrap(s string, w float64) []string {
return wordWrap(dc, s, w)
}
// Transformation Matrix Operations
// Identity resets the current transformation matrix to the identity matrix.
// This results in no translating, scaling, rotating, or shearing.
func (dc *Context) Identity() {
dc.matrix = Identity()
}
// Translate updates the current matrix with a translation.
func (dc *Context) Translate(x, y float64) {
dc.matrix = dc.matrix.Translate(x, y)
}
// Scale updates the current matrix with a scaling factor.
// Scaling occurs about the origin.
func (dc *Context) Scale(x, y float64) {
dc.matrix = dc.matrix.Scale(x, y)
}
// ScaleAbout updates the current matrix with a scaling factor.
// Scaling occurs about the specified point.
func (dc *Context) ScaleAbout(sx, sy, x, y float64) {
dc.Translate(x, y)
dc.Scale(sx, sy)
dc.Translate(-x, -y)
}
// Rotate updates the current matrix with a clockwise rotation.
// Rotation occurs about the origin. Angle is specified in radians.
func (dc *Context) Rotate(angle float64) {
dc.matrix = dc.matrix.Rotate(angle)
}
// RotateAbout updates the current matrix with a clockwise rotation.
// Rotation occurs about the specified point. Angle is specified in radians.
func (dc *Context) RotateAbout(angle, x, y float64) {
dc.Translate(x, y)
dc.Rotate(angle)
dc.Translate(-x, -y)
}
// Shear updates the current matrix with a shearing angle.
// Shearing occurs about the origin.
func (dc *Context) Shear(x, y float64) {
dc.matrix = dc.matrix.Shear(x, y)
}
// ShearAbout updates the current matrix with a shearing angle.
// Shearing occurs about the specified point.
func (dc *Context) ShearAbout(sx, sy, x, y float64) {
dc.Translate(x, y)
dc.Shear(sx, sy)
dc.Translate(-x, -y)
}
// TransformPoint multiplies the specified point by the current matrix,
// returning a transformed position.
func (dc *Context) TransformPoint(x, y float64) (tx, ty float64) {
return dc.matrix.TransformPoint(x, y)
}
// InvertY flips the Y axis so that Y grows from bottom to top and Y=0 is at
// the bottom of the image.
func (dc *Context) InvertY() {
dc.Translate(0, float64(dc.height))
dc.Scale(1, -1)
}
// Stack
// Push saves the current state of the context for later retrieval. These
// can be nested.
func (dc *Context) Push() {
x := *dc
dc.stack = append(dc.stack, &x)
}
// Pop restores the last saved context state from the stack.
func (dc *Context) Pop() {
before := *dc
s := dc.stack
x, s := s[len(s)-1], s[:len(s)-1]
*dc = *x
dc.mask = before.mask
dc.strokePath = before.strokePath
dc.fillPath = before.fillPath
dc.start = before.start
dc.current = before.current
dc.hasCurrent = before.hasCurrent
}

202
vendor/github.com/fogleman/gg/gradient.go generated vendored Normal file
View file

@ -0,0 +1,202 @@
package gg
import (
"image/color"
"math"
"sort"
)
type stop struct {
pos float64
color color.Color
}
type stops []stop
// Len satisfies the Sort interface.
func (s stops) Len() int {
return len(s)
}
// Less satisfies the Sort interface.
func (s stops) Less(i, j int) bool {
return s[i].pos < s[j].pos
}
// Swap satisfies the Sort interface.
func (s stops) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type Gradient interface {
Pattern
AddColorStop(offset float64, color color.Color)
}
// Linear Gradient
type linearGradient struct {
x0, y0, x1, y1 float64
stops stops
}
func (g *linearGradient) ColorAt(x, y int) color.Color {
if len(g.stops) == 0 {
return color.Transparent
}
fx, fy := float64(x), float64(y)
x0, y0, x1, y1 := g.x0, g.y0, g.x1, g.y1
dx, dy := x1-x0, y1-y0
// Horizontal
if dy == 0 && dx != 0 {
return getColor((fx-x0)/dx, g.stops)
}
// Vertical
if dx == 0 && dy != 0 {
return getColor((fy-y0)/dy, g.stops)
}
// Dot product
s0 := dx*(fx-x0) + dy*(fy-y0)
if s0 < 0 {
return g.stops[0].color
}
// Calculate distance to (x0,y0) alone (x0,y0)->(x1,y1)
mag := math.Hypot(dx, dy)
u := ((fx-x0)*-dy + (fy-y0)*dx) / (mag * mag)
x2, y2 := x0+u*-dy, y0+u*dx
d := math.Hypot(fx-x2, fy-y2) / mag
return getColor(d, g.stops)
}
func (g *linearGradient) AddColorStop(offset float64, color color.Color) {
g.stops = append(g.stops, stop{pos: offset, color: color})
sort.Sort(g.stops)
}
func NewLinearGradient(x0, y0, x1, y1 float64) Gradient {
g := &linearGradient{
x0: x0, y0: y0,
x1: x1, y1: y1,
}
return g
}
// Radial Gradient
type circle struct {
x, y, r float64
}
type radialGradient struct {
c0, c1, cd circle
a, inva float64
mindr float64
stops stops
}
func dot3(x0, y0, z0, x1, y1, z1 float64) float64 {
return x0*x1 + y0*y1 + z0*z1
}
func (g *radialGradient) ColorAt(x, y int) color.Color {
if len(g.stops) == 0 {
return color.Transparent
}
// copy from pixman's pixman-radial-gradient.c
dx, dy := float64(x)+0.5-g.c0.x, float64(y)+0.5-g.c0.y
b := dot3(dx, dy, g.c0.r, g.cd.x, g.cd.y, g.cd.r)
c := dot3(dx, dy, -g.c0.r, dx, dy, g.c0.r)
if g.a == 0 {
if b == 0 {
return color.Transparent
}
t := 0.5 * c / b
if t*g.cd.r >= g.mindr {
return getColor(t, g.stops)
}
return color.Transparent
}
discr := dot3(b, g.a, 0, b, -c, 0)
if discr >= 0 {
sqrtdiscr := math.Sqrt(discr)
t0 := (b + sqrtdiscr) * g.inva
t1 := (b - sqrtdiscr) * g.inva
if t0*g.cd.r >= g.mindr {
return getColor(t0, g.stops)
} else if t1*g.cd.r >= g.mindr {
return getColor(t1, g.stops)
}
}
return color.Transparent
}
func (g *radialGradient) AddColorStop(offset float64, color color.Color) {
g.stops = append(g.stops, stop{pos: offset, color: color})
sort.Sort(g.stops)
}
func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) Gradient {
c0 := circle{x0, y0, r0}
c1 := circle{x1, y1, r1}
cd := circle{x1 - x0, y1 - y0, r1 - r0}
a := dot3(cd.x, cd.y, -cd.r, cd.x, cd.y, cd.r)
var inva float64
if a != 0 {
inva = 1.0 / a
}
mindr := -c0.r
g := &radialGradient{
c0: c0,
c1: c1,
cd: cd,
a: a,
inva: inva,
mindr: mindr,
}
return g
}
func getColor(pos float64, stops stops) color.Color {
if pos <= 0.0 || len(stops) == 1 {
return stops[0].color
}
last := stops[len(stops)-1]
if pos >= last.pos {
return last.color
}
for i, stop := range stops[1:] {
if pos < stop.pos {
pos = (pos - stops[i].pos) / (stop.pos - stops[i].pos)
return colorLerp(stops[i].color, stop.color, pos)
}
}
return last.color
}
func colorLerp(c0, c1 color.Color, t float64) color.Color {
r0, g0, b0, a0 := c0.RGBA()
r1, g1, b1, a1 := c1.RGBA()
return color.NRGBA{
lerp(r0, r1, t),
lerp(g0, g1, t),
lerp(b0, b1, t),
lerp(a0, a1, t),
}
}
func lerp(a, b uint32, t float64) uint8 {
return uint8(int32(float64(a)*(1.0-t)+float64(b)*t) >> 8)
}

88
vendor/github.com/fogleman/gg/matrix.go generated vendored Normal file
View file

@ -0,0 +1,88 @@
package gg
import "math"
type Matrix struct {
XX, YX, XY, YY, X0, Y0 float64
}
func Identity() Matrix {
return Matrix{
1, 0,
0, 1,
0, 0,
}
}
func Translate(x, y float64) Matrix {
return Matrix{
1, 0,
0, 1,
x, y,
}
}
func Scale(x, y float64) Matrix {
return Matrix{
x, 0,
0, y,
0, 0,
}
}
func Rotate(angle float64) Matrix {
c := math.Cos(angle)
s := math.Sin(angle)
return Matrix{
c, s,
-s, c,
0, 0,
}
}
func Shear(x, y float64) Matrix {
return Matrix{
1, y,
x, 1,
0, 0,
}
}
func (a Matrix) Multiply(b Matrix) Matrix {
return Matrix{
a.XX*b.XX + a.YX*b.XY,
a.XX*b.YX + a.YX*b.YY,
a.XY*b.XX + a.YY*b.XY,
a.XY*b.YX + a.YY*b.YY,
a.X0*b.XX + a.Y0*b.XY + b.X0,
a.X0*b.YX + a.Y0*b.YY + b.Y0,
}
}
func (a Matrix) TransformVector(x, y float64) (tx, ty float64) {
tx = a.XX*x + a.XY*y
ty = a.YX*x + a.YY*y
return
}
func (a Matrix) TransformPoint(x, y float64) (tx, ty float64) {
tx = a.XX*x + a.XY*y + a.X0
ty = a.YX*x + a.YY*y + a.Y0
return
}
func (a Matrix) Translate(x, y float64) Matrix {
return Translate(x, y).Multiply(a)
}
func (a Matrix) Scale(x, y float64) Matrix {
return Scale(x, y).Multiply(a)
}
func (a Matrix) Rotate(angle float64) Matrix {
return Rotate(angle).Multiply(a)
}
func (a Matrix) Shear(x, y float64) Matrix {
return Shear(x, y).Multiply(a)
}

140
vendor/github.com/fogleman/gg/path.go generated vendored Normal file
View file

@ -0,0 +1,140 @@
package gg
import (
"github.com/golang/freetype/raster"
"golang.org/x/image/math/fixed"
)
func flattenPath(p raster.Path) [][]Point {
var result [][]Point
var path []Point
var cx, cy float64
for i := 0; i < len(p); {
switch p[i] {
case 0:
if len(path) > 0 {
result = append(result, path)
path = nil
}
x := unfix(p[i+1])
y := unfix(p[i+2])
path = append(path, Point{x, y})
cx, cy = x, y
i += 4
case 1:
x := unfix(p[i+1])
y := unfix(p[i+2])
path = append(path, Point{x, y})
cx, cy = x, y
i += 4
case 2:
x1 := unfix(p[i+1])
y1 := unfix(p[i+2])
x2 := unfix(p[i+3])
y2 := unfix(p[i+4])
points := QuadraticBezier(cx, cy, x1, y1, x2, y2)
path = append(path, points...)
cx, cy = x2, y2
i += 6
case 3:
x1 := unfix(p[i+1])
y1 := unfix(p[i+2])
x2 := unfix(p[i+3])
y2 := unfix(p[i+4])
x3 := unfix(p[i+5])
y3 := unfix(p[i+6])
points := CubicBezier(cx, cy, x1, y1, x2, y2, x3, y3)
path = append(path, points...)
cx, cy = x3, y3
i += 8
default:
panic("bad path")
}
}
if len(path) > 0 {
result = append(result, path)
}
return result
}
func dashPath(paths [][]Point, dashes []float64) [][]Point {
var result [][]Point
if len(dashes) == 0 {
return paths
}
if len(dashes) == 1 {
dashes = append(dashes, dashes[0])
}
for _, path := range paths {
if len(path) < 2 {
continue
}
previous := path[0]
pathIndex := 1
dashIndex := 0
segmentLength := 0.0
var segment []Point
segment = append(segment, previous)
for pathIndex < len(path) {
dashLength := dashes[dashIndex]
point := path[pathIndex]
d := previous.Distance(point)
maxd := dashLength - segmentLength
if d > maxd {
t := maxd / d
p := previous.Interpolate(point, t)
segment = append(segment, p)
if dashIndex%2 == 0 && len(segment) > 1 {
result = append(result, segment)
}
segment = nil
segment = append(segment, p)
segmentLength = 0
previous = p
dashIndex = (dashIndex + 1) % len(dashes)
} else {
segment = append(segment, point)
previous = point
segmentLength += d
pathIndex++
}
}
if dashIndex%2 == 0 && len(segment) > 1 {
result = append(result, segment)
}
}
return result
}
func rasterPath(paths [][]Point) raster.Path {
var result raster.Path
for _, path := range paths {
var previous fixed.Point26_6
for i, point := range path {
f := point.Fixed()
if i == 0 {
result.Start(f)
} else {
dx := f.X - previous.X
dy := f.Y - previous.Y
if dx < 0 {
dx = -dx
}
if dy < 0 {
dy = -dy
}
if dx+dy > 8 {
// TODO: this is a hack for cases where two points are
// too close - causes rendering issues with joins / caps
result.Add1(f)
}
}
previous = f
}
}
return result
}
func dashed(path raster.Path, dashes []float64) raster.Path {
return rasterPath(dashPath(flattenPath(path), dashes))
}

123
vendor/github.com/fogleman/gg/pattern.go generated vendored Normal file
View file

@ -0,0 +1,123 @@
package gg
import (
"image"
"image/color"
"github.com/golang/freetype/raster"
)
type RepeatOp int
const (
RepeatBoth RepeatOp = iota
RepeatX
RepeatY
RepeatNone
)
type Pattern interface {
ColorAt(x, y int) color.Color
}
// Solid Pattern
type solidPattern struct {
color color.Color
}
func (p *solidPattern) ColorAt(x, y int) color.Color {
return p.color
}
func NewSolidPattern(color color.Color) Pattern {
return &solidPattern{color: color}
}
// Surface Pattern
type surfacePattern struct {
im image.Image
op RepeatOp
}
func (p *surfacePattern) ColorAt(x, y int) color.Color {
b := p.im.Bounds()
switch p.op {
case RepeatX:
if y >= b.Dy() {
return color.Transparent
}
case RepeatY:
if x >= b.Dx() {
return color.Transparent
}
case RepeatNone:
if x >= b.Dx() || y >= b.Dy() {
return color.Transparent
}
}
x = x%b.Dx() + b.Min.X
y = y%b.Dy() + b.Min.Y
return p.im.At(x, y)
}
func NewSurfacePattern(im image.Image, op RepeatOp) Pattern {
return &surfacePattern{im: im, op: op}
}
type patternPainter struct {
im *image.RGBA
mask *image.Alpha
p Pattern
}
// Paint satisfies the Painter interface.
func (r *patternPainter) Paint(ss []raster.Span, done bool) {
b := r.im.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
const m = 1<<16 - 1
y := s.Y - r.im.Rect.Min.Y
x0 := s.X0 - r.im.Rect.Min.X
// RGBAPainter.Paint() in $GOPATH/src/github.com/golang/freetype/raster/paint.go
i0 := (s.Y-r.im.Rect.Min.Y)*r.im.Stride + (s.X0-r.im.Rect.Min.X)*4
i1 := i0 + (s.X1-s.X0)*4
for i, x := i0, x0; i < i1; i, x = i+4, x+1 {
ma := s.Alpha
if r.mask != nil {
ma = ma * uint32(r.mask.AlphaAt(x, y).A) / 255
if ma == 0 {
continue
}
}
c := r.p.ColorAt(x, y)
cr, cg, cb, ca := c.RGBA()
dr := uint32(r.im.Pix[i+0])
dg := uint32(r.im.Pix[i+1])
db := uint32(r.im.Pix[i+2])
da := uint32(r.im.Pix[i+3])
a := (m - (ca * ma / m)) * 0x101
r.im.Pix[i+0] = uint8((dr*a + cr*ma) / m >> 8)
r.im.Pix[i+1] = uint8((dg*a + cg*ma) / m >> 8)
r.im.Pix[i+2] = uint8((db*a + cb*ma) / m >> 8)
r.im.Pix[i+3] = uint8((da*a + ca*ma) / m >> 8)
}
}
}
func newPatternPainter(im *image.RGBA, mask *image.Alpha, p Pattern) *patternPainter {
return &patternPainter{im, mask, p}
}

25
vendor/github.com/fogleman/gg/point.go generated vendored Normal file
View file

@ -0,0 +1,25 @@
package gg
import (
"math"
"golang.org/x/image/math/fixed"
)
type Point struct {
X, Y float64
}
func (a Point) Fixed() fixed.Point26_6 {
return fixp(a.X, a.Y)
}
func (a Point) Distance(b Point) float64 {
return math.Hypot(a.X-b.X, a.Y-b.Y)
}
func (a Point) Interpolate(b Point, t float64) Point {
x := a.X + (b.X-a.X)*t
y := a.Y + (b.Y-a.Y)*t
return Point{x, y}
}

117
vendor/github.com/fogleman/gg/util.go generated vendored Normal file
View file

@ -0,0 +1,117 @@
package gg
import (
"fmt"
"image"
"image/draw"
_ "image/jpeg"
"image/png"
"io/ioutil"
"math"
"os"
"strings"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
func Radians(degrees float64) float64 {
return degrees * math.Pi / 180
}
func Degrees(radians float64) float64 {
return radians * 180 / math.Pi
}
func LoadImage(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
im, _, err := image.Decode(file)
return im, err
}
func LoadPNG(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return png.Decode(file)
}
func SavePNG(path string, im image.Image) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
return png.Encode(file, im)
}
func imageToRGBA(src image.Image) *image.RGBA {
dst := image.NewRGBA(src.Bounds())
draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
return dst
}
func parseHexColor(x string) (r, g, b, a int) {
x = strings.TrimPrefix(x, "#")
a = 255
if len(x) == 3 {
format := "%1x%1x%1x"
fmt.Sscanf(x, format, &r, &g, &b)
r |= r << 4
g |= g << 4
b |= b << 4
}
if len(x) == 6 {
format := "%02x%02x%02x"
fmt.Sscanf(x, format, &r, &g, &b)
}
if len(x) == 8 {
format := "%02x%02x%02x%02x"
fmt.Sscanf(x, format, &r, &g, &b, &a)
}
return
}
func fixp(x, y float64) fixed.Point26_6 {
return fixed.Point26_6{fix(x), fix(y)}
}
func fix(x float64) fixed.Int26_6 {
return fixed.Int26_6(x * 64)
}
func unfix(x fixed.Int26_6) float64 {
const shift, mask = 6, 1<<6 - 1
if x >= 0 {
return float64(x>>shift) + float64(x&mask)/64
}
x = -x
if x >= 0 {
return -(float64(x>>shift) + float64(x&mask)/64)
}
return 0
}
func LoadFontFace(path string, points float64) (font.Face, error) {
fontBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
f, err := truetype.Parse(fontBytes)
if err != nil {
return nil, err
}
face := truetype.NewFace(f, &truetype.Options{
Size: points,
// Hinting: font.HintingFull,
})
return face, nil
}

58
vendor/github.com/fogleman/gg/wrap.go generated vendored Normal file
View file

@ -0,0 +1,58 @@
package gg
import (
"strings"
"unicode"
)
type measureStringer interface {
MeasureString(s string) (w, h float64)
}
func splitOnSpace(x string) []string {
var result []string
pi := 0
ps := false
for i, c := range x {
s := unicode.IsSpace(c)
if s != ps && i > 0 {
result = append(result, x[pi:i])
pi = i
}
ps = s
}
result = append(result, x[pi:])
return result
}
func wordWrap(m measureStringer, s string, width float64) []string {
var result []string
for _, line := range strings.Split(s, "\n") {
fields := splitOnSpace(line)
if len(fields)%2 == 1 {
fields = append(fields, "")
}
x := ""
for i := 0; i < len(fields); i += 2 {
w, _ := m.MeasureString(x + fields[i])
if w > width {
if x == "" {
result = append(result, fields[i])
x = ""
continue
} else {
result = append(result, x)
x = ""
}
}
x += fields[i] + fields[i+1]
}
if x != "" {
result = append(result, x)
}
}
for i, line := range result {
result[i] = strings.TrimSpace(line)
}
return result
}

20
vendor/github.com/golang/freetype/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,20 @@
# This is the official list of Freetype-Go authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
#
# Freetype-Go is derived from Freetype, which is written in C. The latter
# is copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# Please keep the list sorted.
Google Inc.
Jeff R. Allen <jra@nella.org>
Maksim Kochkin <maxxarts@gmail.com>
Michael Fogleman <fogleman@gmail.com>
Rémy Oudompheng <oudomphe@phare.normalesup.org>
Roger Peppe <rogpeppe@gmail.com>
Steven Edwards <steven@stephenwithav.com>

38
vendor/github.com/golang/freetype/CONTRIBUTORS generated vendored Normal file
View file

@ -0,0 +1,38 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the Freetype-Go repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# The submission process automatically checks to make sure
# that people submitting code are listed in this file (by email address).
#
# Names should be added to this file only after verifying that
# the individual or the individual's organization has agreed to
# the appropriate Contributor License Agreement, found here:
#
# http://code.google.com/legal/individual-cla-v1.0.html
# http://code.google.com/legal/corporate-cla-v1.0.html
#
# The agreement for individuals can be filled out on the web.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# individual or corporate CLA was used.
# Names should be added to this file like so:
# Name <email address>
# Please keep the list sorted.
Andrew Gerrand <adg@golang.org>
Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com>
Maksim Kochkin <maxxarts@gmail.com>
Michael Fogleman <fogleman@gmail.com>
Nigel Tao <nigeltao@golang.org>
Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
Rob Pike <r@golang.org>
Roger Peppe <rogpeppe@gmail.com>
Russ Cox <rsc@golang.org>
Steven Edwards <steven@stephenwithav.com>

12
vendor/github.com/golang/freetype/LICENSE generated vendored Normal file
View file

@ -0,0 +1,12 @@
Use of the Freetype-Go software is subject to your choice of exactly one of
the following two licenses:
* The FreeType License, which is similar to the original BSD license with
an advertising clause, or
* The GNU General Public License (GPL), version 2 or later.
The text of these licenses are available in the licenses/ftl.txt and the
licenses/gpl.txt files respectively. They are also available at
http://freetype.sourceforge.net/license.html
The Luxi fonts in the testdata directory are licensed separately. See the
testdata/COPYING file for details.

245
vendor/github.com/golang/freetype/raster/geom.go generated vendored Normal file
View file

@ -0,0 +1,245 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package raster
import (
"fmt"
"math"
"golang.org/x/image/math/fixed"
)
// maxAbs returns the maximum of abs(a) and abs(b).
func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
if a < 0 {
a = -a
}
if b < 0 {
b = -b
}
if a < b {
return b
}
return a
}
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
func pNeg(p fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{-p.X, -p.Y}
}
// pDot returns the dot product p·q.
func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
px, py := int64(p.X), int64(p.Y)
qx, qy := int64(q.X), int64(q.Y)
return fixed.Int52_12(px*qx + py*qy)
}
// pLen returns the length of the vector p.
func pLen(p fixed.Point26_6) fixed.Int26_6 {
// TODO(nigeltao): use fixed point math.
x := float64(p.X)
y := float64(p.Y)
return fixed.Int26_6(math.Sqrt(x*x + y*y))
}
// pNorm returns the vector p normalized to the given length, or zero if p is
// degenerate.
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
d := pLen(p)
if d == 0 {
return fixed.Point26_6{}
}
s, t := int64(length), int64(d)
x := int64(p.X) * s / t
y := int64(p.Y) * s / t
return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
}
// pRot45CW returns the vector p rotated clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (+px - py) * 181 / 256
qy := (+px + py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{-p.Y, p.X}
}
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (-px - py) * 181 / 256
qy := (+px - py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (+px + py) * 181 / 256
qy := (-px + py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{p.Y, -p.X}
}
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (-px + py) * 181 / 256
qy := (-px - py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// An Adder accumulates points on a curve.
type Adder interface {
// Start starts a new curve at the given point.
Start(a fixed.Point26_6)
// Add1 adds a linear segment to the current curve.
Add1(b fixed.Point26_6)
// Add2 adds a quadratic segment to the current curve.
Add2(b, c fixed.Point26_6)
// Add3 adds a cubic segment to the current curve.
Add3(b, c, d fixed.Point26_6)
}
// A Path is a sequence of curves, and a curve is a start point followed by a
// sequence of linear, quadratic or cubic segments.
type Path []fixed.Int26_6
// String returns a human-readable representation of a Path.
func (p Path) String() string {
s := ""
for i := 0; i < len(p); {
if i != 0 {
s += " "
}
switch p[i] {
case 0:
s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
i += 4
case 1:
s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
i += 4
case 2:
s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
i += 6
case 3:
s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
i += 8
default:
panic("freetype/raster: bad path")
}
}
return s
}
// Clear cancels any previous calls to p.Start or p.AddXxx.
func (p *Path) Clear() {
*p = (*p)[:0]
}
// Start starts a new curve at the given point.
func (p *Path) Start(a fixed.Point26_6) {
*p = append(*p, 0, a.X, a.Y, 0)
}
// Add1 adds a linear segment to the current curve.
func (p *Path) Add1(b fixed.Point26_6) {
*p = append(*p, 1, b.X, b.Y, 1)
}
// Add2 adds a quadratic segment to the current curve.
func (p *Path) Add2(b, c fixed.Point26_6) {
*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
}
// Add3 adds a cubic segment to the current curve.
func (p *Path) Add3(b, c, d fixed.Point26_6) {
*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
}
// AddPath adds the Path q to p.
func (p *Path) AddPath(q Path) {
*p = append(*p, q...)
}
// AddStroke adds a stroked Path.
func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
Stroke(p, q, width, cr, jr)
}
// firstPoint returns the first point in a non-empty Path.
func (p Path) firstPoint() fixed.Point26_6 {
return fixed.Point26_6{p[1], p[2]}
}
// lastPoint returns the last point in a non-empty Path.
func (p Path) lastPoint() fixed.Point26_6 {
return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
}
// addPathReversed adds q reversed to p.
// For example, if q consists of a linear segment from A to B followed by a
// quadratic segment from B to C to D, then the values of q looks like:
// index: 01234567890123
// value: 0AA01BB12CCDD2
// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
func addPathReversed(p Adder, q Path) {
if len(q) == 0 {
return
}
i := len(q) - 1
for {
switch q[i] {
case 0:
return
case 1:
i -= 4
p.Add1(
fixed.Point26_6{q[i-2], q[i-1]},
)
case 2:
i -= 6
p.Add2(
fixed.Point26_6{q[i+2], q[i+3]},
fixed.Point26_6{q[i-2], q[i-1]},
)
case 3:
i -= 8
p.Add3(
fixed.Point26_6{q[i+4], q[i+5]},
fixed.Point26_6{q[i+2], q[i+3]},
fixed.Point26_6{q[i-2], q[i-1]},
)
default:
panic("freetype/raster: bad path")
}
}
}

287
vendor/github.com/golang/freetype/raster/paint.go generated vendored Normal file
View file

@ -0,0 +1,287 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package raster
import (
"image"
"image/color"
"image/draw"
"math"
)
// A Span is a horizontal segment of pixels with constant alpha. X0 is an
// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
// Span has Alpha == 0xffff.
type Span struct {
Y, X0, X1 int
Alpha uint32
}
// A Painter knows how to paint a batch of Spans. Rasterization may involve
// Painting multiple batches, and done will be true for the final batch. The
// Spans' Y values are monotonically increasing during a rasterization. Paint
// may use all of ss as scratch space during the call.
type Painter interface {
Paint(ss []Span, done bool)
}
// The PainterFunc type adapts an ordinary function to the Painter interface.
type PainterFunc func(ss []Span, done bool)
// Paint just delegates the call to f.
func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
// the Over Porter-Duff composition operator.
type AlphaOverPainter struct {
Image *image.Alpha
}
// Paint satisfies the Painter interface.
func (r AlphaOverPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
p := r.Image.Pix[base+s.X0 : base+s.X1]
a := int(s.Alpha >> 8)
for i, c := range p {
v := int(c)
p[i] = uint8((v*255 + (255-v)*a) / 255)
}
}
}
// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
return AlphaOverPainter{m}
}
// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
// the Src Porter-Duff composition operator.
type AlphaSrcPainter struct {
Image *image.Alpha
}
// Paint satisfies the Painter interface.
func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
p := r.Image.Pix[base+s.X0 : base+s.X1]
color := uint8(s.Alpha >> 8)
for i := range p {
p[i] = color
}
}
}
// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
return AlphaSrcPainter{m}
}
// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
type RGBAPainter struct {
// Image is the image to compose onto.
Image *image.RGBA
// Op is the Porter-Duff composition operator.
Op draw.Op
// cr, cg, cb and ca are the 16-bit color to paint the spans.
cr, cg, cb, ca uint32
}
// Paint satisfies the Painter interface.
func (r *RGBAPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
ma := s.Alpha
const m = 1<<16 - 1
i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
i1 := i0 + (s.X1-s.X0)*4
if r.Op == draw.Over {
for i := i0; i < i1; i += 4 {
dr := uint32(r.Image.Pix[i+0])
dg := uint32(r.Image.Pix[i+1])
db := uint32(r.Image.Pix[i+2])
da := uint32(r.Image.Pix[i+3])
a := (m - (r.ca * ma / m)) * 0x101
r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
}
} else {
for i := i0; i < i1; i += 4 {
r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
}
}
}
}
// SetColor sets the color to paint the spans.
func (r *RGBAPainter) SetColor(c color.Color) {
r.cr, r.cg, r.cb, r.ca = c.RGBA()
}
// NewRGBAPainter creates a new RGBAPainter for the given image.
func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
return &RGBAPainter{Image: m}
}
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
// be either fully opaque or fully transparent.
type MonochromePainter struct {
Painter Painter
y, x0, x1 int
}
// Paint delegates to the wrapped Painter after quantizing each Span's alpha
// value and merging adjacent fully opaque Spans.
func (m *MonochromePainter) Paint(ss []Span, done bool) {
// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
j := 0
for _, s := range ss {
if s.Alpha >= 0x8000 {
if m.y == s.Y && m.x1 == s.X0 {
m.x1 = s.X1
} else {
ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1}
j++
m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
}
}
}
if done {
// Flush the accumulated Span.
finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1}
if j < len(ss) {
ss[j] = finalSpan
j++
m.Painter.Paint(ss[:j], true)
} else if j == len(ss) {
m.Painter.Paint(ss, false)
if cap(ss) > 0 {
ss = ss[:1]
} else {
ss = make([]Span, 1)
}
ss[0] = finalSpan
m.Painter.Paint(ss, true)
} else {
panic("unreachable")
}
// Reset the accumulator, so that this Painter can be re-used.
m.y, m.x0, m.x1 = 0, 0, 0
} else {
m.Painter.Paint(ss[:j], false)
}
}
// NewMonochromePainter creates a new MonochromePainter that wraps the given
// Painter.
func NewMonochromePainter(p Painter) *MonochromePainter {
return &MonochromePainter{Painter: p}
}
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
// on each Span's alpha value.
type GammaCorrectionPainter struct {
// Painter is the wrapped Painter.
Painter Painter
// a is the precomputed alpha values for linear interpolation, with fully
// opaque == 0xffff.
a [256]uint16
// gammaIsOne is whether gamma correction is a no-op.
gammaIsOne bool
}
// Paint delegates to the wrapped Painter after performing gamma-correction on
// each Span.
func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
if !g.gammaIsOne {
const n = 0x101
for i, s := range ss {
if s.Alpha == 0 || s.Alpha == 0xffff {
continue
}
p, q := s.Alpha/n, s.Alpha%n
// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
ss[i].Alpha = (a + n/2) / n
}
}
g.Painter.Paint(ss, done)
}
// SetGamma sets the gamma value.
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
g.gammaIsOne = gamma == 1
if g.gammaIsOne {
return
}
for i := 0; i < 256; i++ {
a := float64(i) / 0xff
a = math.Pow(a, gamma)
g.a[i] = uint16(0xffff * a)
}
}
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
// the given Painter.
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
g := &GammaCorrectionPainter{Painter: p}
g.SetGamma(gamma)
return g
}

601
vendor/github.com/golang/freetype/raster/raster.go generated vendored Normal file
View file

@ -0,0 +1,601 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
// Package raster provides an anti-aliasing 2-D rasterizer.
//
// It is part of the larger Freetype suite of font-related packages, but the
// raster package is not specific to font rasterization, and can be used
// standalone without any other Freetype package.
//
// Rasterization is done by the same area/coverage accumulation algorithm as
// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
// description of the area/coverage algorithm is at
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
package raster
import (
"strconv"
"golang.org/x/image/math/fixed"
)
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
// area/coverage for the pixel at (xi, yi).
type cell struct {
xi int
area, cover int
next int
}
type Rasterizer struct {
// If false, the default behavior is to use the even-odd winding fill
// rule during Rasterize.
UseNonZeroWinding bool
// An offset (in pixels) to the painted spans.
Dx, Dy int
// The width of the Rasterizer. The height is implicit in len(cellIndex).
width int
// splitScaleN is the scaling factor used to determine how many times
// to decompose a quadratic or cubic segment into a linear approximation.
splitScale2, splitScale3 int
// The current pen position.
a fixed.Point26_6
// The current cell and its area/coverage being accumulated.
xi, yi int
area, cover int
// Saved cells.
cell []cell
// Linked list of cells, one per row.
cellIndex []int
// Buffers.
cellBuf [256]cell
cellIndexBuf [64]int
spanBuf [64]Span
}
// findCell returns the index in r.cell for the cell corresponding to
// (r.xi, r.yi). The cell is created if necessary.
func (r *Rasterizer) findCell() int {
if r.yi < 0 || r.yi >= len(r.cellIndex) {
return -1
}
xi := r.xi
if xi < 0 {
xi = -1
} else if xi > r.width {
xi = r.width
}
i, prev := r.cellIndex[r.yi], -1
for i != -1 && r.cell[i].xi <= xi {
if r.cell[i].xi == xi {
return i
}
i, prev = r.cell[i].next, i
}
c := len(r.cell)
if c == cap(r.cell) {
buf := make([]cell, c, 4*c)
copy(buf, r.cell)
r.cell = buf[0 : c+1]
} else {
r.cell = r.cell[0 : c+1]
}
r.cell[c] = cell{xi, 0, 0, i}
if prev == -1 {
r.cellIndex[r.yi] = c
} else {
r.cell[prev].next = c
}
return c
}
// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
func (r *Rasterizer) saveCell() {
if r.area != 0 || r.cover != 0 {
i := r.findCell()
if i != -1 {
r.cell[i].area += r.area
r.cell[i].cover += r.cover
}
r.area = 0
r.cover = 0
}
}
// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
func (r *Rasterizer) setCell(xi, yi int) {
if r.xi != xi || r.yi != yi {
r.saveCell()
r.xi, r.yi = xi, yi
}
}
// scan accumulates area/coverage for the yi'th scanline, going from
// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
// and from y0f to y1f fractional vertical units within that scanline.
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
x0i := int(x0) / 64
x0f := x0 - fixed.Int26_6(64*x0i)
x1i := int(x1) / 64
x1f := x1 - fixed.Int26_6(64*x1i)
// A perfectly horizontal scan.
if y0f == y1f {
r.setCell(x1i, yi)
return
}
dx, dy := x1-x0, y1f-y0f
// A single cell scan.
if x0i == x1i {
r.area += int((x0f + x1f) * dy)
r.cover += int(dy)
return
}
// There are at least two cells. Apart from the first and last cells,
// all intermediate cells go through the full width of the cell,
// or 64 units in 26.6 fixed point format.
var (
p, q, edge0, edge1 fixed.Int26_6
xiDelta int
)
if dx > 0 {
p, q = (64-x0f)*dy, dx
edge0, edge1, xiDelta = 0, 64, 1
} else {
p, q = x0f*dy, -dx
edge0, edge1, xiDelta = 64, 0, -1
}
yDelta, yRem := p/q, p%q
if yRem < 0 {
yDelta -= 1
yRem += q
}
// Do the first cell.
xi, y := x0i, y0f
r.area += int((x0f + edge1) * yDelta)
r.cover += int(yDelta)
xi, y = xi+xiDelta, y+yDelta
r.setCell(xi, yi)
if xi != x1i {
// Do all the intermediate cells.
p = 64 * (y1f - y + yDelta)
fullDelta, fullRem := p/q, p%q
if fullRem < 0 {
fullDelta -= 1
fullRem += q
}
yRem -= q
for xi != x1i {
yDelta = fullDelta
yRem += fullRem
if yRem >= 0 {
yDelta += 1
yRem -= q
}
r.area += int(64 * yDelta)
r.cover += int(yDelta)
xi, y = xi+xiDelta, y+yDelta
r.setCell(xi, yi)
}
}
// Do the last cell.
yDelta = y1f - y
r.area += int((edge0 + x1f) * yDelta)
r.cover += int(yDelta)
}
// Start starts a new curve at the given point.
func (r *Rasterizer) Start(a fixed.Point26_6) {
r.setCell(int(a.X/64), int(a.Y/64))
r.a = a
}
// Add1 adds a linear segment to the current curve.
func (r *Rasterizer) Add1(b fixed.Point26_6) {
x0, y0 := r.a.X, r.a.Y
x1, y1 := b.X, b.Y
dx, dy := x1-x0, y1-y0
// Break the 26.6 fixed point Y co-ordinates into integral and fractional
// parts.
y0i := int(y0) / 64
y0f := y0 - fixed.Int26_6(64*y0i)
y1i := int(y1) / 64
y1f := y1 - fixed.Int26_6(64*y1i)
if y0i == y1i {
// There is only one scanline.
r.scan(y0i, x0, y0f, x1, y1f)
} else if dx == 0 {
// This is a vertical line segment. We avoid calling r.scan and instead
// manipulate r.area and r.cover directly.
var (
edge0, edge1 fixed.Int26_6
yiDelta int
)
if dy > 0 {
edge0, edge1, yiDelta = 0, 64, 1
} else {
edge0, edge1, yiDelta = 64, 0, -1
}
x0i, yi := int(x0)/64, y0i
x0fTimes2 := (int(x0) - (64 * x0i)) * 2
// Do the first pixel.
dcover := int(edge1 - y0f)
darea := int(x0fTimes2 * dcover)
r.area += darea
r.cover += dcover
yi += yiDelta
r.setCell(x0i, yi)
// Do all the intermediate pixels.
dcover = int(edge1 - edge0)
darea = int(x0fTimes2 * dcover)
for yi != y1i {
r.area += darea
r.cover += dcover
yi += yiDelta
r.setCell(x0i, yi)
}
// Do the last pixel.
dcover = int(y1f - edge0)
darea = int(x0fTimes2 * dcover)
r.area += darea
r.cover += dcover
} else {
// There are at least two scanlines. Apart from the first and last
// scanlines, all intermediate scanlines go through the full height of
// the row, or 64 units in 26.6 fixed point format.
var (
p, q, edge0, edge1 fixed.Int26_6
yiDelta int
)
if dy > 0 {
p, q = (64-y0f)*dx, dy
edge0, edge1, yiDelta = 0, 64, 1
} else {
p, q = y0f*dx, -dy
edge0, edge1, yiDelta = 64, 0, -1
}
xDelta, xRem := p/q, p%q
if xRem < 0 {
xDelta -= 1
xRem += q
}
// Do the first scanline.
x, yi := x0, y0i
r.scan(yi, x, y0f, x+xDelta, edge1)
x, yi = x+xDelta, yi+yiDelta
r.setCell(int(x)/64, yi)
if yi != y1i {
// Do all the intermediate scanlines.
p = 64 * dx
fullDelta, fullRem := p/q, p%q
if fullRem < 0 {
fullDelta -= 1
fullRem += q
}
xRem -= q
for yi != y1i {
xDelta = fullDelta
xRem += fullRem
if xRem >= 0 {
xDelta += 1
xRem -= q
}
r.scan(yi, x, edge0, x+xDelta, edge1)
x, yi = x+xDelta, yi+yiDelta
r.setCell(int(x)/64, yi)
}
}
// Do the last scanline.
r.scan(yi, x, edge0, x1, y1f)
}
// The next lineTo starts from b.
r.a = b
}
// Add2 adds a quadratic segment to the current curve.
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
// Calculate nSplit (the number of recursive decompositions) based on how
// 'curvy' it is. Specifically, how much the middle point b deviates from
// (a+c)/2.
dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
nsplit := 0
for dev > 0 {
dev /= 4
nsplit++
}
// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
// is 16.
const maxNsplit = 16
if nsplit > maxNsplit {
panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
}
// Recursively decompose the curve nSplit levels deep.
var (
pStack [2*maxNsplit + 3]fixed.Point26_6
sStack [maxNsplit + 1]int
i int
)
sStack[0] = nsplit
pStack[0] = c
pStack[1] = b
pStack[2] = r.a
for i >= 0 {
s := sStack[i]
p := pStack[2*i:]
if s > 0 {
// Split the quadratic curve p[:3] into an equivalent set of two
// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
// and p[0] is unchanged.
mx := p[1].X
p[4].X = p[2].X
p[3].X = (p[4].X + mx) / 2
p[1].X = (p[0].X + mx) / 2
p[2].X = (p[1].X + p[3].X) / 2
my := p[1].Y
p[4].Y = p[2].Y
p[3].Y = (p[4].Y + my) / 2
p[1].Y = (p[0].Y + my) / 2
p[2].Y = (p[1].Y + p[3].Y) / 2
// The two shorter curves have one less split to do.
sStack[i] = s - 1
sStack[i+1] = s - 1
i++
} else {
// Replace the level-0 quadratic with a two-linear-piece
// approximation.
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
r.Add1(fixed.Point26_6{midx, midy})
r.Add1(p[0])
i--
}
}
}
// Add3 adds a cubic segment to the current curve.
func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
// Calculate nSplit (the number of recursive decompositions) based on how
// 'curvy' it is.
dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
nsplit := 0
for dev2 > 0 || dev3 > 0 {
dev2 /= 8
dev3 /= 4
nsplit++
}
// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
// maxNsplit is 16.
const maxNsplit = 16
if nsplit > maxNsplit {
panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
}
// Recursively decompose the curve nSplit levels deep.
var (
pStack [3*maxNsplit + 4]fixed.Point26_6
sStack [maxNsplit + 1]int
i int
)
sStack[0] = nsplit
pStack[0] = d
pStack[1] = c
pStack[2] = b
pStack[3] = r.a
for i >= 0 {
s := sStack[i]
p := pStack[3*i:]
if s > 0 {
// Split the cubic curve p[:4] into an equivalent set of two
// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
// and p[0] is unchanged.
m01x := (p[0].X + p[1].X) / 2
m12x := (p[1].X + p[2].X) / 2
m23x := (p[2].X + p[3].X) / 2
p[6].X = p[3].X
p[5].X = m23x
p[1].X = m01x
p[2].X = (m01x + m12x) / 2
p[4].X = (m12x + m23x) / 2
p[3].X = (p[2].X + p[4].X) / 2
m01y := (p[0].Y + p[1].Y) / 2
m12y := (p[1].Y + p[2].Y) / 2
m23y := (p[2].Y + p[3].Y) / 2
p[6].Y = p[3].Y
p[5].Y = m23y
p[1].Y = m01y
p[2].Y = (m01y + m12y) / 2
p[4].Y = (m12y + m23y) / 2
p[3].Y = (p[2].Y + p[4].Y) / 2
// The two shorter curves have one less split to do.
sStack[i] = s - 1
sStack[i+1] = s - 1
i++
} else {
// Replace the level-0 cubic with a two-linear-piece approximation.
midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
r.Add1(fixed.Point26_6{midx, midy})
r.Add1(p[0])
i--
}
}
}
// AddPath adds the given Path.
func (r *Rasterizer) AddPath(p Path) {
for i := 0; i < len(p); {
switch p[i] {
case 0:
r.Start(
fixed.Point26_6{p[i+1], p[i+2]},
)
i += 4
case 1:
r.Add1(
fixed.Point26_6{p[i+1], p[i+2]},
)
i += 4
case 2:
r.Add2(
fixed.Point26_6{p[i+1], p[i+2]},
fixed.Point26_6{p[i+3], p[i+4]},
)
i += 6
case 3:
r.Add3(
fixed.Point26_6{p[i+1], p[i+2]},
fixed.Point26_6{p[i+3], p[i+4]},
fixed.Point26_6{p[i+5], p[i+6]},
)
i += 8
default:
panic("freetype/raster: bad path")
}
}
}
// AddStroke adds a stroked Path.
func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
Stroke(r, q, width, cr, jr)
}
// areaToAlpha converts an area value to a uint32 alpha value. A completely
// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
// conversion of area values greater than this depends on the winding rule:
// even-odd or non-zero.
func (r *Rasterizer) areaToAlpha(area int) uint32 {
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
// without the +1. Round-to-nearest gives a more symmetric result than
// round-down. The C implementation also returns 8-bit alpha, not 16-bit
// alpha.
a := (area + 1) >> 1
if a < 0 {
a = -a
}
alpha := uint32(a)
if r.UseNonZeroWinding {
if alpha > 0x0fff {
alpha = 0x0fff
}
} else {
alpha &= 0x1fff
if alpha > 0x1000 {
alpha = 0x2000 - alpha
} else if alpha == 0x1000 {
alpha = 0x0fff
}
}
// alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
// 16-bit alpha.
return alpha<<4 | alpha>>8
}
// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
// Span, which has Y, X0, X1 and A all equal to zero.
func (r *Rasterizer) Rasterize(p Painter) {
r.saveCell()
s := 0
for yi := 0; yi < len(r.cellIndex); yi++ {
xi, cover := 0, 0
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
if cover != 0 && r.cell[c].xi > xi {
alpha := r.areaToAlpha(cover * 64 * 2)
if alpha != 0 {
xi0, xi1 := xi, r.cell[c].xi
if xi0 < 0 {
xi0 = 0
}
if xi1 >= r.width {
xi1 = r.width
}
if xi0 < xi1 {
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
s++
}
}
}
cover += r.cell[c].cover
alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
xi = r.cell[c].xi + 1
if alpha != 0 {
xi0, xi1 := r.cell[c].xi, xi
if xi0 < 0 {
xi0 = 0
}
if xi1 >= r.width {
xi1 = r.width
}
if xi0 < xi1 {
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
s++
}
}
if s > len(r.spanBuf)-2 {
p.Paint(r.spanBuf[:s], false)
s = 0
}
}
}
p.Paint(r.spanBuf[:s], true)
}
// Clear cancels any previous calls to r.Start or r.AddXxx.
func (r *Rasterizer) Clear() {
r.a = fixed.Point26_6{}
r.xi = 0
r.yi = 0
r.area = 0
r.cover = 0
r.cell = r.cell[:0]
for i := 0; i < len(r.cellIndex); i++ {
r.cellIndex[i] = -1
}
}
// SetBounds sets the maximum width and height of the rasterized image and
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
func (r *Rasterizer) SetBounds(width, height int) {
if width < 0 {
width = 0
}
if height < 0 {
height = 0
}
// Use the same ssN heuristic as the C Freetype (version 2.4.0)
// implementation.
ss2, ss3 := 32, 16
if width > 24 || height > 24 {
ss2, ss3 = 2*ss2, 2*ss3
if width > 120 || height > 120 {
ss2, ss3 = 2*ss2, 2*ss3
}
}
r.width = width
r.splitScale2 = ss2
r.splitScale3 = ss3
r.cell = r.cellBuf[:0]
if height > len(r.cellIndexBuf) {
r.cellIndex = make([]int, height)
} else {
r.cellIndex = r.cellIndexBuf[:height]
}
r.Clear()
}
// NewRasterizer creates a new Rasterizer with the given bounds.
func NewRasterizer(width, height int) *Rasterizer {
r := new(Rasterizer)
r.SetBounds(width, height)
return r
}

483
vendor/github.com/golang/freetype/raster/stroke.go generated vendored Normal file
View file

@ -0,0 +1,483 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package raster
import (
"golang.org/x/image/math/fixed"
)
// Two points are considered practically equal if the square of the distance
// between them is less than one quarter (i.e. 1024 / 4096).
const epsilon = fixed.Int52_12(1024)
// A Capper signifies how to begin or end a stroked path.
type Capper interface {
// Cap adds a cap to p given a pivot point and the normal vector of a
// terminal segment. The normal's length is half of the stroke width.
Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6)
}
// The CapperFunc type adapts an ordinary function to be a Capper.
type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
f(p, halfWidth, pivot, n1)
}
// A Joiner signifies how to join interior nodes of a stroked path.
type Joiner interface {
// Join adds a join to the two sides of a stroked path given a pivot
// point and the normal vectors of the trailing and leading segments.
// Both normals have length equal to half of the stroke width.
Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
}
// The JoinerFunc type adapts an ordinary function to be a Joiner.
type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
f(lhs, rhs, halfWidth, pivot, n0, n1)
}
// RoundCapper adds round caps to a stroked path.
var RoundCapper Capper = CapperFunc(roundCapper)
func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
// The cubic Bézier approximation to a circle involves the magic number
// (√2 - 1) * 4/3, which is approximately 35/64.
const k = 35
e := pRot90CCW(n1)
side := pivot.Add(e)
start, end := pivot.Sub(n1), pivot.Add(n1)
d, e := n1.Mul(k), e.Mul(k)
p.Add3(start.Add(e), side.Sub(d), side)
p.Add3(side.Add(d), end.Add(e), end)
}
// ButtCapper adds butt caps to a stroked path.
var ButtCapper Capper = CapperFunc(buttCapper)
func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
p.Add1(pivot.Add(n1))
}
// SquareCapper adds square caps to a stroked path.
var SquareCapper Capper = CapperFunc(squareCapper)
func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
e := pRot90CCW(n1)
side := pivot.Add(e)
p.Add1(side.Sub(n1))
p.Add1(side.Add(n1))
p.Add1(pivot.Add(n1))
}
// RoundJoiner adds round joins to a stroked path.
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
dot := pDot(pRot90CW(n0), n1)
if dot >= 0 {
addArc(lhs, pivot, n0, n1)
rhs.Add1(pivot.Sub(n1))
} else {
lhs.Add1(pivot.Add(n1))
addArc(rhs, pivot, pNeg(n0), pNeg(n1))
}
}
// BevelJoiner adds bevel joins to a stroked path.
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
lhs.Add1(pivot.Add(n1))
rhs.Add1(pivot.Sub(n1))
}
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
// two vectors n0 and n1 must be of equal length.
func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) {
// r2 is the square of the length of n0.
r2 := pDot(n0, n0)
if r2 < epsilon {
// The arc radius is so small that we collapse to a straight line.
p.Add1(pivot.Add(n1))
return
}
// We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
// a final quadratic segment from s to n1. Each 45-degree segment has
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
// rotated and translated. tan(π/8) is approximately 27/64.
const tpo8 = 27
var s fixed.Point26_6
// We determine which octant the angle between n0 and n1 is in via three
// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
// degrees.
m0 := pRot45CW(n0)
m1 := pRot90CW(n0)
m2 := pRot90CW(m0)
if pDot(m1, n1) >= 0 {
if pDot(n0, n1) >= 0 {
if pDot(m2, n1) <= 0 {
// n1 is between 0 and 45 degrees clockwise of n0.
s = n0
} else {
// n1 is between 45 and 90 degrees clockwise of n0.
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
s = m0
}
} else {
pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
p.Add2(pm1.Add(n0t), pm1)
if pDot(m0, n1) >= 0 {
// n1 is between 90 and 135 degrees clockwise of n0.
s = m1
} else {
// n1 is between 135 and 180 degrees clockwise of n0.
p.Add2(pm1.Sub(n0t), pivot.Add(m2))
s = m2
}
}
} else {
if pDot(n0, n1) >= 0 {
if pDot(m0, n1) >= 0 {
// n1 is between 0 and 45 degrees counter-clockwise of n0.
s = n0
} else {
// n1 is between 45 and 90 degrees counter-clockwise of n0.
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
s = pNeg(m2)
}
} else {
pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
p.Add2(pm1.Add(n0t), pm1)
if pDot(m2, n1) <= 0 {
// n1 is between 90 and 135 degrees counter-clockwise of n0.
s = pNeg(m1)
} else {
// n1 is between 135 and 180 degrees counter-clockwise of n0.
p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
s = pNeg(m0)
}
}
}
// The final quadratic segment has two endpoints s and n1 and the middle
// control point is a multiple of s.Add(n1), i.e. it is on the angle
// bisector of those two points. The multiple ranges between 128/256 and
// 150/256 as the angle between s and n1 ranges between 0 and 45 degrees.
//
// When the angle is 0 degrees (i.e. s and n1 are coincident) then
// s.Add(n1) is twice s and so the middle control point of the degenerate
// quadratic segment should be half s.Add(n1), and half = 128/256.
//
// When the angle is 45 degrees then 150/256 is the ratio of the lengths of
// the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
//
// d is the normalized dot product between s and n1. Since the angle ranges
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
d := 256 * pDot(s, n1) / r2
multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2
p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
}
// midpoint returns the midpoint of two Points.
func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
}
// angleGreaterThan45 returns whether the angle between two vectors is more
// than 45 degrees.
func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
v := pRot45CCW(v0)
return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
}
// interpolate returns the point (1-t)*a + t*b.
func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
s := 1<<12 - t
x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)}
}
// curviest2 returns the value of t for which the quadratic parametric curve
// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
//
// The curvature of the parametric curve f(t) = (x(t), y(t)) is
// |xy″-yx″| / (x²+y²)^(3/2).
//
// Let d = b-a and e = c-2*b+a, so that f(t) = 2*d+2*e*t and f″(t) = 2*e.
// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
//
// Thus, curvature is extreme where the denominator is extreme, i.e. where
// (x²+y²) is extreme. The first order condition is that
// 2*x*x″+2*y*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 {
dx := int64(b.X - a.X)
dy := int64(b.Y - a.Y)
ex := int64(c.X - 2*b.X + a.X)
ey := int64(c.Y - 2*b.Y + a.Y)
if ex == 0 && ey == 0 {
return 2048
}
return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
}
// A stroker holds state for stroking a path.
type stroker struct {
// p is the destination that records the stroked path.
p Adder
// u is the half-width of the stroke.
u fixed.Int26_6
// cr and jr specify how to end and connect path segments.
cr Capper
jr Joiner
// r is the reverse path. Stroking a path involves constructing two
// parallel paths 2*u apart. The first path is added immediately to p,
// the second path is accumulated in r and eventually added in reverse.
r Path
// a is the most recent segment point. anorm is the segment normal of
// length u at that point.
a, anorm fixed.Point26_6
}
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) {
// We repeatedly divide the segment at its middle until it is straight
// enough to approximate the stroke by just translating the control points.
// ds and ps are stacks of depths and points. t is the top of the stack.
const maxDepth = 5
var (
ds [maxDepth + 1]int
ps [2*maxDepth + 3]fixed.Point26_6
t int
)
// Initially the ps stack has one quadratic segment of depth zero.
ds[0] = 0
ps[2] = k.a
ps[1] = b
ps[0] = c
anorm := k.anorm
var cnorm fixed.Point26_6
for {
depth := ds[t]
a := ps[2*t+2]
b := ps[2*t+1]
c := ps[2*t+0]
ab := b.Sub(a)
bc := c.Sub(b)
abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
if abIsSmall && bcIsSmall {
// Approximate the segment by a circular arc.
cnorm = pRot90CCW(pNorm(bc, k.u))
mac := midpoint(a, c)
addArc(k.p, mac, anorm, cnorm)
addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm))
} else if depth < maxDepth && angleGreaterThan45(ab, bc) {
// Divide the segment in two and push both halves on the stack.
mab := midpoint(a, b)
mbc := midpoint(b, c)
t++
ds[t+0] = depth + 1
ds[t-1] = depth + 1
ps[2*t+2] = a
ps[2*t+1] = mab
ps[2*t+0] = midpoint(mab, mbc)
ps[2*t-1] = mbc
continue
} else {
// Translate the control points.
bnorm := pRot90CCW(pNorm(c.Sub(a), k.u))
cnorm = pRot90CCW(pNorm(bc, k.u))
k.p.Add2(b.Add(bnorm), c.Add(cnorm))
k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
}
if t == 0 {
k.a, k.anorm = c, cnorm
return
}
t--
anorm = cnorm
}
panic("unreachable")
}
// Add1 adds a linear segment to the stroker.
func (k *stroker) Add1(b fixed.Point26_6) {
bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
if len(k.r) == 0 {
k.p.Start(k.a.Add(bnorm))
k.r.Start(k.a.Sub(bnorm))
} else {
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
}
k.p.Add1(b.Add(bnorm))
k.r.Add1(b.Sub(bnorm))
k.a, k.anorm = b, bnorm
}
// Add2 adds a quadratic segment to the stroker.
func (k *stroker) Add2(b, c fixed.Point26_6) {
ab := b.Sub(k.a)
bc := c.Sub(b)
abnorm := pRot90CCW(pNorm(ab, k.u))
if len(k.r) == 0 {
k.p.Start(k.a.Add(abnorm))
k.r.Start(k.a.Sub(abnorm))
} else {
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
}
// Approximate nearly-degenerate quadratics by linear segments.
abIsSmall := pDot(ab, ab) < epsilon
bcIsSmall := pDot(bc, bc) < epsilon
if abIsSmall || bcIsSmall {
acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u))
k.p.Add1(c.Add(acnorm))
k.r.Add1(c.Sub(acnorm))
k.a, k.anorm = c, acnorm
return
}
// The quadratic segment (k.a, b, c) has a point of maximum curvature.
// If this occurs at an end point, we process the segment as a whole.
t := curviest2(k.a, b, c)
if t <= 0 || 4096 <= t {
k.addNonCurvy2(b, c)
return
}
// Otherwise, we perform a de Casteljau decomposition at the point of
// maximum curvature and process the two straighter parts.
mab := interpolate(k.a, b, t)
mbc := interpolate(b, c, t)
mabc := interpolate(mab, mbc, t)
// If the vectors ab and bc are close to being in opposite directions,
// then the decomposition can become unstable, so we approximate the
// quadratic segment by two linear segments joined by an arc.
bcnorm := pRot90CCW(pNorm(bc, k.u))
if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 {
pArc := pDot(abnorm, bc) < 0
k.p.Add1(mabc.Add(abnorm))
if pArc {
z := pRot90CW(abnorm)
addArc(k.p, mabc, abnorm, z)
addArc(k.p, mabc, z, bcnorm)
}
k.p.Add1(mabc.Add(bcnorm))
k.p.Add1(c.Add(bcnorm))
k.r.Add1(mabc.Sub(abnorm))
if !pArc {
z := pRot90CW(abnorm)
addArc(&k.r, mabc, pNeg(abnorm), z)
addArc(&k.r, mabc, z, pNeg(bcnorm))
}
k.r.Add1(mabc.Sub(bcnorm))
k.r.Add1(c.Sub(bcnorm))
k.a, k.anorm = c, bcnorm
return
}
// Process the decomposed parts.
k.addNonCurvy2(mab, mabc)
k.addNonCurvy2(mbc, c)
}
// Add3 adds a cubic segment to the stroker.
func (k *stroker) Add3(b, c, d fixed.Point26_6) {
panic("freetype/raster: stroke unimplemented for cubic segments")
}
// stroke adds the stroked Path q to p, where q consists of exactly one curve.
func (k *stroker) stroke(q Path) {
// Stroking is implemented by deriving two paths each k.u apart from q.
// The left-hand-side path is added immediately to k.p; the right-hand-side
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
// we add the RHS in reverse order.
k.r = make(Path, 0, len(q))
k.a = fixed.Point26_6{q[1], q[2]}
for i := 4; i < len(q); {
switch q[i] {
case 1:
k.Add1(
fixed.Point26_6{q[i+1], q[i+2]},
)
i += 4
case 2:
k.Add2(
fixed.Point26_6{q[i+1], q[i+2]},
fixed.Point26_6{q[i+3], q[i+4]},
)
i += 6
case 3:
k.Add3(
fixed.Point26_6{q[i+1], q[i+2]},
fixed.Point26_6{q[i+3], q[i+4]},
fixed.Point26_6{q[i+5], q[i+6]},
)
i += 8
default:
panic("freetype/raster: bad path")
}
}
if len(k.r) == 0 {
return
}
// TODO(nigeltao): if q is a closed curve then we should join the first and
// last segments instead of capping them.
k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
addPathReversed(k.p, k.r)
pivot := q.firstPoint()
k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]}))
}
// Stroke adds q stroked with the given width to p. The result is typically
// self-intersecting and should be rasterized with UseNonZeroWinding.
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
if len(q) == 0 {
return
}
if cr == nil {
cr = RoundCapper
}
if jr == nil {
jr = RoundJoiner
}
if q[0] != 0 {
panic("freetype/raster: bad path")
}
s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
i := 0
for j := 4; j < len(q); {
switch q[j] {
case 0:
s.stroke(q[i:j])
i, j = j, j+4
case 1:
j += 4
case 2:
j += 6
case 3:
j += 8
default:
panic("freetype/raster: bad path")
}
}
s.stroke(q[i:])
}

507
vendor/github.com/golang/freetype/truetype/face.go generated vendored Normal file
View file

@ -0,0 +1,507 @@
// Copyright 2015 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
import (
"image"
"math"
"github.com/golang/freetype/raster"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
func powerOf2(i int) bool {
return i != 0 && (i&(i-1)) == 0
}
// Options are optional arguments to NewFace.
type Options struct {
// Size is the font size in points, as in "a 10 point font size".
//
// A zero value means to use a 12 point font size.
Size float64
// DPI is the dots-per-inch resolution.
//
// A zero value means to use 72 DPI.
DPI float64
// Hinting is how to quantize the glyph nodes.
//
// A zero value means to use no hinting.
Hinting font.Hinting
// GlyphCacheEntries is the number of entries in the glyph mask image
// cache.
//
// If non-zero, it must be a power of 2.
//
// A zero value means to use 512 entries.
GlyphCacheEntries int
// SubPixelsX is the number of sub-pixel locations a glyph's dot is
// quantized to, in the horizontal direction. For example, a value of 8
// means that the dot is quantized to 1/8th of a pixel. This quantization
// only affects the glyph mask image, not its bounding box or advance
// width. A higher value gives a more faithful glyph image, but reduces the
// effectiveness of the glyph cache.
//
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
//
// A zero value means to use 4 sub-pixel locations.
SubPixelsX int
// SubPixelsY is the number of sub-pixel locations a glyph's dot is
// quantized to, in the vertical direction. For example, a value of 8
// means that the dot is quantized to 1/8th of a pixel. This quantization
// only affects the glyph mask image, not its bounding box or advance
// width. A higher value gives a more faithful glyph image, but reduces the
// effectiveness of the glyph cache.
//
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
//
// A zero value means to use 1 sub-pixel location.
SubPixelsY int
}
func (o *Options) size() float64 {
if o != nil && o.Size > 0 {
return o.Size
}
return 12
}
func (o *Options) dpi() float64 {
if o != nil && o.DPI > 0 {
return o.DPI
}
return 72
}
func (o *Options) hinting() font.Hinting {
if o != nil {
switch o.Hinting {
case font.HintingVertical, font.HintingFull:
// TODO: support vertical hinting.
return font.HintingFull
}
}
return font.HintingNone
}
func (o *Options) glyphCacheEntries() int {
if o != nil && powerOf2(o.GlyphCacheEntries) {
return o.GlyphCacheEntries
}
// 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
// locations in the X and Y direction.
return 512
}
func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) {
if o != nil {
switch o.SubPixelsX {
case 1, 2, 4, 8, 16, 32, 64:
return subPixels(o.SubPixelsX)
}
}
// This default value of 4 isn't based on anything scientific, merely as
// small a number as possible that looks almost as good as no quantization,
// or returning subPixels(64).
return subPixels(4)
}
func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) {
if o != nil {
switch o.SubPixelsX {
case 1, 2, 4, 8, 16, 32, 64:
return subPixels(o.SubPixelsX)
}
}
// This default value of 1 isn't based on anything scientific, merely that
// vertical sub-pixel glyph rendering is pretty rare. Baseline locations
// can usually afford to snap to the pixel grid, so the vertical direction
// doesn't have the deal with the horizontal's fractional advance widths.
return subPixels(1)
}
// subPixels returns q and the bias and mask that leads to q quantized
// sub-pixel locations per full pixel.
//
// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
// because we want to round fractions of fixed.Int26_6 as:
// - 0 to 7 rounds to 0.
// - 8 to 23 rounds to 16.
// - 24 to 39 rounds to 32.
// - 40 to 55 rounds to 48.
// - 56 to 63 rounds to 64.
// which means to add 8 and then bitwise-and with -16, in two's complement
// representation.
//
// When q == 1, we want bias == 32 and mask == -64.
// When q == 2, we want bias == 16 and mask == -32.
// When q == 4, we want bias == 8 and mask == -16.
// ...
// When q == 64, we want bias == 0 and mask == -1. (The no-op case).
// The pattern is clear.
func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
}
// glyphCacheEntry caches the arguments and return values of rasterize.
type glyphCacheEntry struct {
key glyphCacheKey
val glyphCacheVal
}
type glyphCacheKey struct {
index Index
fx, fy uint8
}
type glyphCacheVal struct {
advanceWidth fixed.Int26_6
offset image.Point
gw int
gh int
}
type indexCacheEntry struct {
rune rune
index Index
}
// NewFace returns a new font.Face for the given Font.
func NewFace(f *Font, opts *Options) font.Face {
a := &face{
f: f,
hinting: opts.hinting(),
scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
}
a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()
// Fill the cache with invalid entries. Valid glyph cache entries have fx
// and fy in the range [0, 64). Valid index cache entries have rune >= 0.
for i := range a.glyphCache {
a.glyphCache[i].key.fy = 0xff
}
for i := range a.indexCache {
a.indexCache[i].rune = -1
}
// Set the rasterizer's bounds to be big enough to handle the largest glyph.
b := f.Bounds(a.scale)
xmin := +int(b.Min.X) >> 6
ymin := -int(b.Max.Y) >> 6
xmax := +int(b.Max.X+63) >> 6
ymax := -int(b.Min.Y-63) >> 6
a.maxw = xmax - xmin
a.maxh = ymax - ymin
a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache)))
a.r.SetBounds(a.maxw, a.maxh)
a.p = facePainter{a}
return a
}
type face struct {
f *Font
hinting font.Hinting
scale fixed.Int26_6
subPixelX uint32
subPixelBiasX fixed.Int26_6
subPixelMaskX fixed.Int26_6
subPixelY uint32
subPixelBiasY fixed.Int26_6
subPixelMaskY fixed.Int26_6
masks *image.Alpha
glyphCache []glyphCacheEntry
r raster.Rasterizer
p raster.Painter
paintOffset int
maxw int
maxh int
glyphBuf GlyphBuf
indexCache [indexCacheLen]indexCacheEntry
// TODO: clip rectangle?
}
const indexCacheLen = 256
func (a *face) index(r rune) Index {
const mask = indexCacheLen - 1
c := &a.indexCache[r&mask]
if c.rune == r {
return c.index
}
i := a.f.Index(r)
c.rune = r
c.index = i
return i
}
// Close satisfies the font.Face interface.
func (a *face) Close() error { return nil }
// Metrics satisfies the font.Face interface.
func (a *face) Metrics() font.Metrics {
scale := float64(a.scale)
fupe := float64(a.f.FUnitsPerEm())
return font.Metrics{
Height: a.scale,
Ascent: fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)),
Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)),
}
}
// Kern satisfies the font.Face interface.
func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
i0 := a.index(r0)
i1 := a.index(r1)
kern := a.f.Kern(a.scale, i0, i1)
if a.hinting != font.HintingNone {
kern = (kern + 32) &^ 63
}
return kern
}
// Glyph satisfies the font.Face interface.
func (a *face) Glyph(dot fixed.Point26_6, r rune) (
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
// Quantize to the sub-pixel granularity.
dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY
// Split the coordinates into their integer and fractional parts.
ix, fx := int(dotX>>6), dotX&0x3f
iy, fy := int(dotY>>6), dotY&0x3f
index := a.index(r)
cIndex := uint32(index)
cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX)
cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY)
cIndex &= uint32(len(a.glyphCache) - 1)
a.paintOffset = a.maxh * int(cIndex)
k := glyphCacheKey{
index: index,
fx: uint8(fx),
fy: uint8(fy),
}
var v glyphCacheVal
if a.glyphCache[cIndex].key != k {
var ok bool
v, ok = a.rasterize(index, fx, fy)
if !ok {
return image.Rectangle{}, nil, image.Point{}, 0, false
}
a.glyphCache[cIndex] = glyphCacheEntry{k, v}
} else {
v = a.glyphCache[cIndex].val
}
dr.Min = image.Point{
X: ix + v.offset.X,
Y: iy + v.offset.Y,
}
dr.Max = image.Point{
X: dr.Min.X + v.gw,
Y: dr.Min.Y + v.gh,
}
return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true
}
func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
return fixed.Rectangle26_6{}, 0, false
}
xmin := +a.glyphBuf.Bounds.Min.X
ymin := -a.glyphBuf.Bounds.Max.Y
xmax := +a.glyphBuf.Bounds.Max.X
ymax := -a.glyphBuf.Bounds.Min.Y
if xmin > xmax || ymin > ymax {
return fixed.Rectangle26_6{}, 0, false
}
return fixed.Rectangle26_6{
Min: fixed.Point26_6{
X: xmin,
Y: ymin,
},
Max: fixed.Point26_6{
X: xmax,
Y: ymax,
},
}, a.glyphBuf.AdvanceWidth, true
}
func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
return 0, false
}
return a.glyphBuf.AdvanceWidth, true
}
// rasterize returns the advance width, integer-pixel offset to render at, and
// the width and height of the given glyph at the given sub-pixel offsets.
//
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) {
if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
return glyphCacheVal{}, false
}
// Calculate the integer-pixel bounds for the glyph.
xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
if xmin > xmax || ymin > ymax {
return glyphCacheVal{}, false
}
// A TrueType's glyph's nodes can have negative co-ordinates, but the
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
// the pixel offsets, based on the font's FUnit metrics, that let a
// negative co-ordinate in TrueType space be non-negative in rasterizer
// space. xmin and ymin are typically <= 0.
fx -= fixed.Int26_6(xmin << 6)
fy -= fixed.Int26_6(ymin << 6)
// Rasterize the glyph's vectors.
a.r.Clear()
pixOffset := a.paintOffset * a.maxw
clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
e0 := 0
for _, e1 := range a.glyphBuf.Ends {
a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy)
e0 = e1
}
a.r.Rasterize(a.p)
return glyphCacheVal{
a.glyphBuf.AdvanceWidth,
image.Point{xmin, ymin},
xmax - xmin,
ymax - ymin,
}, true
}
func clear(pix []byte) {
for i := range pix {
pix[i] = 0
}
}
// drawContour draws the given closed contour with the given offset.
func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
if len(ps) == 0 {
return
}
// The low bit of each point's Flags value is whether the point is on the
// curve. Truetype fonts only have quadratic Bézier curves, not cubics.
// Thus, two consecutive off-curve points imply an on-curve point in the
// middle of those two.
//
// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
// ps[0] is a truetype.Point measured in FUnits and positive Y going
// upwards. start is the same thing measured in fixed point units and
// positive Y going downwards, and offset by (dx, dy).
start := fixed.Point26_6{
X: dx + ps[0].X,
Y: dy - ps[0].Y,
}
var others []Point
if ps[0].Flags&0x01 != 0 {
others = ps[1:]
} else {
last := fixed.Point26_6{
X: dx + ps[len(ps)-1].X,
Y: dy - ps[len(ps)-1].Y,
}
if ps[len(ps)-1].Flags&0x01 != 0 {
start = last
others = ps[:len(ps)-1]
} else {
start = fixed.Point26_6{
X: (start.X + last.X) / 2,
Y: (start.Y + last.Y) / 2,
}
others = ps
}
}
a.r.Start(start)
q0, on0 := start, true
for _, p := range others {
q := fixed.Point26_6{
X: dx + p.X,
Y: dy - p.Y,
}
on := p.Flags&0x01 != 0
if on {
if on0 {
a.r.Add1(q)
} else {
a.r.Add2(q0, q)
}
} else {
if on0 {
// No-op.
} else {
mid := fixed.Point26_6{
X: (q0.X + q.X) / 2,
Y: (q0.Y + q.Y) / 2,
}
a.r.Add2(q0, mid)
}
}
q0, on0 = q, on
}
// Close the curve.
if on0 {
a.r.Add1(start)
} else {
a.r.Add2(q0, start)
}
}
// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
// (face.paintOffset) to the painted spans.
type facePainter struct {
a *face
}
func (p facePainter) Paint(ss []raster.Span, done bool) {
m := p.a.masks
b := m.Bounds()
b.Min.Y = p.a.paintOffset
b.Max.Y = p.a.paintOffset + p.a.maxh
for _, s := range ss {
s.Y += p.a.paintOffset
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X
p := m.Pix[base+s.X0 : base+s.X1]
color := uint8(s.Alpha >> 8)
for i := range p {
p[i] = color
}
}
}

517
vendor/github.com/golang/freetype/truetype/glyph.go generated vendored Normal file
View file

@ -0,0 +1,517 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
import (
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
// TODO: implement VerticalHinting.
// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
// control point.
type Point struct {
X, Y fixed.Int26_6
// The Flags' LSB means whether or not this Point is 'on' the contour.
// Other bits are reserved for internal use.
Flags uint32
}
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
// series of glyphs from a Font.
type GlyphBuf struct {
// AdvanceWidth is the glyph's advance width.
AdvanceWidth fixed.Int26_6
// Bounds is the glyph's bounding box.
Bounds fixed.Rectangle26_6
// Points contains all Points from all contours of the glyph. If hinting
// was used to load a glyph then Unhinted contains those Points before they
// were hinted, and InFontUnits contains those Points before they were
// hinted and scaled.
Points, Unhinted, InFontUnits []Point
// Ends is the point indexes of the end point of each contour. The length
// of Ends is the number of contours in the glyph. The i'th contour
// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
// interpreted to mean zero.
Ends []int
font *Font
scale fixed.Int26_6
hinting font.Hinting
hinter hinter
// phantomPoints are the co-ordinates of the synthetic phantom points
// used for hinting and bounding box calculations.
phantomPoints [4]Point
// pp1x is the X co-ordinate of the first phantom point. The '1' is
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
// TODO: eliminate this and consistently use phantomPoints[0].X.
pp1x fixed.Int26_6
// metricsSet is whether the glyph's metrics have been set yet. For a
// compound glyph, a sub-glyph may override the outer glyph's metrics.
metricsSet bool
// tmp is a scratch buffer.
tmp []Point
}
// Flags for decoding a glyph's contours. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
flagOnCurve = 1 << iota
flagXShortVector
flagYShortVector
flagRepeat
flagPositiveXShortVector
flagPositiveYShortVector
// The remaining flags are for internal use.
flagTouchedX
flagTouchedY
)
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
// dependent on the value of the flag{X,Y}ShortVector bits.
const (
flagThisXIsSame = flagPositiveXShortVector
flagThisYIsSame = flagPositiveYShortVector
)
// Load loads a glyph's contours from a Font, overwriting any previously loaded
// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
// 1 em, i is the glyph index, and h is the hinting policy.
func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
g.Points = g.Points[:0]
g.Unhinted = g.Unhinted[:0]
g.InFontUnits = g.InFontUnits[:0]
g.Ends = g.Ends[:0]
g.font = f
g.hinting = h
g.scale = scale
g.pp1x = 0
g.phantomPoints = [4]Point{}
g.metricsSet = false
if h != font.HintingNone {
if err := g.hinter.init(f, scale); err != nil {
return err
}
}
if err := g.load(0, i, true); err != nil {
return err
}
// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
// and should be cleaned up once we have all the testScaling tests passing,
// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
pp1x := g.pp1x
if h != font.HintingNone {
pp1x = g.phantomPoints[0].X
}
if pp1x != 0 {
for i := range g.Points {
g.Points[i].X -= pp1x
}
}
advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
if h != font.HintingNone {
if len(f.hdmx) >= 8 {
if n := u32(f.hdmx, 4); n > 3+uint32(i) {
for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
if fixed.Int26_6(hdmx[0]) == scale>>6 {
advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
break
}
}
}
}
advanceWidth = (advanceWidth + 32) &^ 63
}
g.AdvanceWidth = advanceWidth
// Set g.Bounds to the 'control box', which is the bounding box of the
// Bézier curves' control points. This is easier to calculate, no smaller
// than and often equal to the tightest possible bounding box of the curves
// themselves. This approach is what C Freetype does. We can't just scale
// the nominal bounding box in the glyf data as the hinting process and
// phantom point adjustment may move points outside of that box.
if len(g.Points) == 0 {
g.Bounds = fixed.Rectangle26_6{}
} else {
p := g.Points[0]
g.Bounds.Min.X = p.X
g.Bounds.Max.X = p.X
g.Bounds.Min.Y = p.Y
g.Bounds.Max.Y = p.Y
for _, p := range g.Points[1:] {
if g.Bounds.Min.X > p.X {
g.Bounds.Min.X = p.X
} else if g.Bounds.Max.X < p.X {
g.Bounds.Max.X = p.X
}
if g.Bounds.Min.Y > p.Y {
g.Bounds.Min.Y = p.Y
} else if g.Bounds.Max.Y < p.Y {
g.Bounds.Max.Y = p.Y
}
}
// Snap the box to the grid, if hinting is on.
if h != font.HintingNone {
g.Bounds.Min.X &^= 63
g.Bounds.Min.Y &^= 63
g.Bounds.Max.X += 63
g.Bounds.Max.X &^= 63
g.Bounds.Max.Y += 63
g.Bounds.Max.Y &^= 63
}
}
return nil
}
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
// The recursion limit here is arbitrary, but defends against malformed glyphs.
if recursion >= 32 {
return UnsupportedError("excessive compound glyph recursion")
}
// Find the relevant slice of g.font.glyf.
var g0, g1 uint32
if g.font.locaOffsetFormat == locaOffsetFormatShort {
g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
} else {
g0 = u32(g.font.loca, 4*int(i))
g1 = u32(g.font.loca, 4*int(i)+4)
}
// Decode the contour count and nominal bounding box, from the first
// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
// and 6, are unused.
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
if g0+10 <= g1 {
glyf = g.font.glyf[g0:g1]
ne = int(int16(u16(glyf, 0)))
boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
}
// Create the phantom points.
uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
uvm := g.font.unscaledVMetric(i, boundsYMax)
g.phantomPoints = [4]Point{
{X: boundsXMin - uhm.LeftSideBearing},
{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
}
if len(glyf) == 0 {
g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
g.Points = g.Points[:len(g.Points)-4]
return nil
}
// Load and hint the contours.
if ne < 0 {
if ne != -1 {
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
// "the values -2, -3, and so forth, are reserved for future use."
return UnsupportedError("negative number of contours")
}
pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
return err
}
} else {
np0, ne0 := len(g.Points), len(g.Ends)
program := g.loadSimple(glyf, ne)
g.addPhantomsAndScale(np0, np0, true, true)
pp1x = g.Points[len(g.Points)-4].X
if g.hinting != font.HintingNone {
if len(program) != 0 {
err := g.hinter.run(
program,
g.Points[np0:],
g.Unhinted[np0:],
g.InFontUnits[np0:],
g.Ends[ne0:],
)
if err != nil {
return err
}
}
// Drop the four phantom points.
g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
}
if useMyMetrics {
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
}
g.Points = g.Points[:len(g.Points)-4]
if np0 != 0 {
// The hinting program expects the []Ends values to be indexed
// relative to the inner glyph, not the outer glyph, so we delay
// adding np0 until after the hinting program (if any) has run.
for i := ne0; i < len(g.Ends); i++ {
g.Ends[i] += np0
}
}
}
if useMyMetrics && !g.metricsSet {
g.metricsSet = true
g.pp1x = pp1x
}
return nil
}
// loadOffset is the initial offset for loadSimple and loadCompound. The first
// 10 bytes are the number of contours and the bounding box.
const loadOffset = 10
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
offset := loadOffset
for i := 0; i < ne; i++ {
g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
offset += 2
}
// Note the TrueType hinting instructions.
instrLen := int(u16(glyf, offset))
offset += 2
program = glyf[offset : offset+instrLen]
offset += instrLen
np0 := len(g.Points)
np1 := np0 + int(g.Ends[len(g.Ends)-1])
// Decode the flags.
for i := np0; i < np1; {
c := uint32(glyf[offset])
offset++
g.Points = append(g.Points, Point{Flags: c})
i++
if c&flagRepeat != 0 {
count := glyf[offset]
offset++
for ; count > 0; count-- {
g.Points = append(g.Points, Point{Flags: c})
i++
}
}
}
// Decode the co-ordinates.
var x int16
for i := np0; i < np1; i++ {
f := g.Points[i].Flags
if f&flagXShortVector != 0 {
dx := int16(glyf[offset])
offset++
if f&flagPositiveXShortVector == 0 {
x -= dx
} else {
x += dx
}
} else if f&flagThisXIsSame == 0 {
x += int16(u16(glyf, offset))
offset += 2
}
g.Points[i].X = fixed.Int26_6(x)
}
var y int16
for i := np0; i < np1; i++ {
f := g.Points[i].Flags
if f&flagYShortVector != 0 {
dy := int16(glyf[offset])
offset++
if f&flagPositiveYShortVector == 0 {
y -= dy
} else {
y += dy
}
} else if f&flagThisYIsSame == 0 {
y += int16(u16(glyf, offset))
offset += 2
}
g.Points[i].Y = fixed.Int26_6(y)
}
return program
}
func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
glyf []byte, useMyMetrics bool) error {
// Flags for decoding a compound glyph. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
flagArg1And2AreWords = 1 << iota
flagArgsAreXYValues
flagRoundXYToGrid
flagWeHaveAScale
flagUnused
flagMoreComponents
flagWeHaveAnXAndYScale
flagWeHaveATwoByTwo
flagWeHaveInstructions
flagUseMyMetrics
flagOverlapCompound
)
np0, ne0 := len(g.Points), len(g.Ends)
offset := loadOffset
for {
flags := u16(glyf, offset)
component := Index(u16(glyf, offset+2))
dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
if flags&flagArg1And2AreWords != 0 {
dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
offset += 8
} else {
dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
offset += 6
}
if flags&flagArgsAreXYValues == 0 {
return UnsupportedError("compound glyph transform vector")
}
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
hasTransform = true
switch {
case flags&flagWeHaveAScale != 0:
transform[0] = int16(u16(glyf, offset+0))
transform[3] = transform[0]
offset += 2
case flags&flagWeHaveAnXAndYScale != 0:
transform[0] = int16(u16(glyf, offset+0))
transform[3] = int16(u16(glyf, offset+2))
offset += 4
case flags&flagWeHaveATwoByTwo != 0:
transform[0] = int16(u16(glyf, offset+0))
transform[1] = int16(u16(glyf, offset+2))
transform[2] = int16(u16(glyf, offset+4))
transform[3] = int16(u16(glyf, offset+6))
offset += 8
}
}
savedPP := g.phantomPoints
np0 := len(g.Points)
componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
if err := g.load(recursion+1, component, componentUMM); err != nil {
return err
}
if flags&flagUseMyMetrics == 0 {
g.phantomPoints = savedPP
}
if hasTransform {
for j := np0; j < len(g.Points); j++ {
p := &g.Points[j]
newX := 0 +
fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
newY := 0 +
fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
p.X, p.Y = newX, newY
}
}
dx = g.font.scale(g.scale * dx)
dy = g.font.scale(g.scale * dy)
if flags&flagRoundXYToGrid != 0 {
dx = (dx + 32) &^ 63
dy = (dy + 32) &^ 63
}
for j := np0; j < len(g.Points); j++ {
p := &g.Points[j]
p.X += dx
p.Y += dy
}
// TODO: also adjust g.InFontUnits and g.Unhinted?
if flags&flagMoreComponents == 0 {
break
}
}
instrLen := 0
if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
instrLen = int(u16(glyf, offset))
offset += 2
}
g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
points, ends := g.Points[np0:], g.Ends[ne0:]
g.Points = g.Points[:len(g.Points)-4]
for j := range points {
points[j].Flags &^= flagTouchedX | flagTouchedY
}
if instrLen == 0 {
if !g.metricsSet {
copy(g.phantomPoints[:], points[len(points)-4:])
}
return nil
}
// Hint the compound glyph.
program := glyf[offset : offset+instrLen]
// Temporarily adjust the ends to be relative to this compound glyph.
if np0 != 0 {
for i := range ends {
ends[i] -= np0
}
}
// Hinting instructions of a composite glyph completely refer to the
// (already) hinted subglyphs.
g.tmp = append(g.tmp[:0], points...)
if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
return err
}
if np0 != 0 {
for i := range ends {
ends[i] += np0
}
}
if !g.metricsSet {
copy(g.phantomPoints[:], points[len(points)-4:])
}
return nil
}
func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
// Add the four phantom points.
g.Points = append(g.Points, g.phantomPoints[:]...)
// Scale the points.
if simple && g.hinting != font.HintingNone {
g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
}
for i := np1; i < len(g.Points); i++ {
p := &g.Points[i]
p.X = g.font.scale(g.scale * p.X)
p.Y = g.font.scale(g.scale * p.Y)
}
if g.hinting == font.HintingNone {
return
}
// Round the 1st phantom point to the grid, shifting all other points equally.
// Note that "all other points" starts from np0, not np1.
// TODO: delete this adjustment and the np0/np1 distinction, when
// we update the compatibility tests to C Freetype 2.5.3.
// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
if adjust {
pp1x := g.Points[len(g.Points)-4].X
if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
for i := np0; i < len(g.Points); i++ {
g.Points[i].X += dx
}
}
}
if simple {
g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
}
// Round the 2nd and 4th phantom point to the grid.
p := &g.Points[len(g.Points)-3]
p.X = (p.X + 32) &^ 63
p = &g.Points[len(g.Points)-1]
p.Y = (p.Y + 32) &^ 63
}

1770
vendor/github.com/golang/freetype/truetype/hint.go generated vendored Normal file

File diff suppressed because it is too large Load diff

289
vendor/github.com/golang/freetype/truetype/opcodes.go generated vendored Normal file
View file

@ -0,0 +1,289 @@
// Copyright 2012 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
// The Truetype opcodes are summarized at
// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
const (
opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
opSVTCA1 = 0x01 // .
opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
opSPVTCA1 = 0x03 // .
opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
opSFVTCA1 = 0x05 // .
opSPVTL0 = 0x06 // Set Projection Vector To Line
opSPVTL1 = 0x07 // .
opSFVTL0 = 0x08 // Set Freedom Vector To Line
opSFVTL1 = 0x09 // .
opSPVFS = 0x0a // Set Projection Vector From Stack
opSFVFS = 0x0b // Set Freedom Vector From Stack
opGPV = 0x0c // Get Projection Vector
opGFV = 0x0d // Get Freedom Vector
opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
opISECT = 0x0f // moves point p to the InterSECTion of two lines
opSRP0 = 0x10 // Set Reference Point 0
opSRP1 = 0x11 // Set Reference Point 1
opSRP2 = 0x12 // Set Reference Point 2
opSZP0 = 0x13 // Set Zone Pointer 0
opSZP1 = 0x14 // Set Zone Pointer 1
opSZP2 = 0x15 // Set Zone Pointer 2
opSZPS = 0x16 // Set Zone PointerS
opSLOOP = 0x17 // Set LOOP variable
opRTG = 0x18 // Round To Grid
opRTHG = 0x19 // Round To Half Grid
opSMD = 0x1a // Set Minimum Distance
opELSE = 0x1b // ELSE clause
opJMPR = 0x1c // JuMP Relative
opSCVTCI = 0x1d // Set Control Value Table Cut-In
opSSWCI = 0x1e // Set Single Width Cut-In
opSSW = 0x1f // Set Single Width
opDUP = 0x20 // DUPlicate top stack element
opPOP = 0x21 // POP top stack element
opCLEAR = 0x22 // CLEAR the stack
opSWAP = 0x23 // SWAP the top two elements on the stack
opDEPTH = 0x24 // DEPTH of the stack
opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
opALIGNPTS = 0x27 // ALIGN PoinTS
op_0x28 = 0x28 // deprecated
opUTP = 0x29 // UnTouch Point
opLOOPCALL = 0x2a // LOOP and CALL function
opCALL = 0x2b // CALL function
opFDEF = 0x2c // Function DEFinition
opENDF = 0x2d // END Function definition
opMDAP0 = 0x2e // Move Direct Absolute Point
opMDAP1 = 0x2f // .
opIUP0 = 0x30 // Interpolate Untouched Points through the outline
opIUP1 = 0x31 // .
opSHP0 = 0x32 // SHift Point using reference point
opSHP1 = 0x33 // .
opSHC0 = 0x34 // SHift Contour using reference point
opSHC1 = 0x35 // .
opSHZ0 = 0x36 // SHift Zone using reference point
opSHZ1 = 0x37 // .
opSHPIX = 0x38 // SHift point by a PIXel amount
opIP = 0x39 // Interpolate Point
opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
opMSIRP1 = 0x3b // .
opALIGNRP = 0x3c // ALIGN to Reference Point
opRTDG = 0x3d // Round To Double Grid
opMIAP0 = 0x3e // Move Indirect Absolute Point
opMIAP1 = 0x3f // .
opNPUSHB = 0x40 // PUSH N Bytes
opNPUSHW = 0x41 // PUSH N Words
opWS = 0x42 // Write Store
opRS = 0x43 // Read Store
opWCVTP = 0x44 // Write Control Value Table in Pixel units
opRCVT = 0x45 // Read Control Value Table entry
opGC0 = 0x46 // Get Coordinate projected onto the projection vector
opGC1 = 0x47 // .
opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
opMD0 = 0x49 // Measure Distance
opMD1 = 0x4a // .
opMPPEM = 0x4b // Measure Pixels Per EM
opMPS = 0x4c // Measure Point Size
opFLIPON = 0x4d // set the auto FLIP Boolean to ON
opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
opDEBUG = 0x4f // DEBUG call
opLT = 0x50 // Less Than
opLTEQ = 0x51 // Less Than or EQual
opGT = 0x52 // Greater Than
opGTEQ = 0x53 // Greater Than or EQual
opEQ = 0x54 // EQual
opNEQ = 0x55 // Not EQual
opODD = 0x56 // ODD
opEVEN = 0x57 // EVEN
opIF = 0x58 // IF test
opEIF = 0x59 // End IF
opAND = 0x5a // logical AND
opOR = 0x5b // logical OR
opNOT = 0x5c // logical NOT
opDELTAP1 = 0x5d // DELTA exception P1
opSDB = 0x5e // Set Delta Base in the graphics state
opSDS = 0x5f // Set Delta Shift in the graphics state
opADD = 0x60 // ADD
opSUB = 0x61 // SUBtract
opDIV = 0x62 // DIVide
opMUL = 0x63 // MULtiply
opABS = 0x64 // ABSolute value
opNEG = 0x65 // NEGate
opFLOOR = 0x66 // FLOOR
opCEILING = 0x67 // CEILING
opROUND00 = 0x68 // ROUND value
opROUND01 = 0x69 // .
opROUND10 = 0x6a // .
opROUND11 = 0x6b // .
opNROUND00 = 0x6c // No ROUNDing of value
opNROUND01 = 0x6d // .
opNROUND10 = 0x6e // .
opNROUND11 = 0x6f // .
opWCVTF = 0x70 // Write Control Value Table in Funits
opDELTAP2 = 0x71 // DELTA exception P2
opDELTAP3 = 0x72 // DELTA exception P3
opDELTAC1 = 0x73 // DELTA exception C1
opDELTAC2 = 0x74 // DELTA exception C2
opDELTAC3 = 0x75 // DELTA exception C3
opSROUND = 0x76 // Super ROUND
opS45ROUND = 0x77 // Super ROUND 45 degrees
opJROT = 0x78 // Jump Relative On True
opJROF = 0x79 // Jump Relative On False
opROFF = 0x7a // Round OFF
op_0x7b = 0x7b // deprecated
opRUTG = 0x7c // Round Up To Grid
opRDTG = 0x7d // Round Down To Grid
opSANGW = 0x7e // Set ANGle Weight
opAA = 0x7f // Adjust Angle
opFLIPPT = 0x80 // FLIP PoinT
opFLIPRGON = 0x81 // FLIP RanGe ON
opFLIPRGOFF = 0x82 // FLIP RanGe OFF
op_0x83 = 0x83 // deprecated
op_0x84 = 0x84 // deprecated
opSCANCTRL = 0x85 // SCAN conversion ConTRoL
opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
opSDPVTL1 = 0x87 // .
opGETINFO = 0x88 // GET INFOrmation
opIDEF = 0x89 // Instruction DEFinition
opROLL = 0x8a // ROLL the top three stack elements
opMAX = 0x8b // MAXimum of top two stack elements
opMIN = 0x8c // MINimum of top two stack elements
opSCANTYPE = 0x8d // SCANTYPE
opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
op_0x8f = 0x8f
op_0x90 = 0x90
op_0x91 = 0x91
op_0x92 = 0x92
op_0x93 = 0x93
op_0x94 = 0x94
op_0x95 = 0x95
op_0x96 = 0x96
op_0x97 = 0x97
op_0x98 = 0x98
op_0x99 = 0x99
op_0x9a = 0x9a
op_0x9b = 0x9b
op_0x9c = 0x9c
op_0x9d = 0x9d
op_0x9e = 0x9e
op_0x9f = 0x9f
op_0xa0 = 0xa0
op_0xa1 = 0xa1
op_0xa2 = 0xa2
op_0xa3 = 0xa3
op_0xa4 = 0xa4
op_0xa5 = 0xa5
op_0xa6 = 0xa6
op_0xa7 = 0xa7
op_0xa8 = 0xa8
op_0xa9 = 0xa9
op_0xaa = 0xaa
op_0xab = 0xab
op_0xac = 0xac
op_0xad = 0xad
op_0xae = 0xae
op_0xaf = 0xaf
opPUSHB000 = 0xb0 // PUSH Bytes
opPUSHB001 = 0xb1 // .
opPUSHB010 = 0xb2 // .
opPUSHB011 = 0xb3 // .
opPUSHB100 = 0xb4 // .
opPUSHB101 = 0xb5 // .
opPUSHB110 = 0xb6 // .
opPUSHB111 = 0xb7 // .
opPUSHW000 = 0xb8 // PUSH Words
opPUSHW001 = 0xb9 // .
opPUSHW010 = 0xba // .
opPUSHW011 = 0xbb // .
opPUSHW100 = 0xbc // .
opPUSHW101 = 0xbd // .
opPUSHW110 = 0xbe // .
opPUSHW111 = 0xbf // .
opMDRP00000 = 0xc0 // Move Direct Relative Point
opMDRP00001 = 0xc1 // .
opMDRP00010 = 0xc2 // .
opMDRP00011 = 0xc3 // .
opMDRP00100 = 0xc4 // .
opMDRP00101 = 0xc5 // .
opMDRP00110 = 0xc6 // .
opMDRP00111 = 0xc7 // .
opMDRP01000 = 0xc8 // .
opMDRP01001 = 0xc9 // .
opMDRP01010 = 0xca // .
opMDRP01011 = 0xcb // .
opMDRP01100 = 0xcc // .
opMDRP01101 = 0xcd // .
opMDRP01110 = 0xce // .
opMDRP01111 = 0xcf // .
opMDRP10000 = 0xd0 // .
opMDRP10001 = 0xd1 // .
opMDRP10010 = 0xd2 // .
opMDRP10011 = 0xd3 // .
opMDRP10100 = 0xd4 // .
opMDRP10101 = 0xd5 // .
opMDRP10110 = 0xd6 // .
opMDRP10111 = 0xd7 // .
opMDRP11000 = 0xd8 // .
opMDRP11001 = 0xd9 // .
opMDRP11010 = 0xda // .
opMDRP11011 = 0xdb // .
opMDRP11100 = 0xdc // .
opMDRP11101 = 0xdd // .
opMDRP11110 = 0xde // .
opMDRP11111 = 0xdf // .
opMIRP00000 = 0xe0 // Move Indirect Relative Point
opMIRP00001 = 0xe1 // .
opMIRP00010 = 0xe2 // .
opMIRP00011 = 0xe3 // .
opMIRP00100 = 0xe4 // .
opMIRP00101 = 0xe5 // .
opMIRP00110 = 0xe6 // .
opMIRP00111 = 0xe7 // .
opMIRP01000 = 0xe8 // .
opMIRP01001 = 0xe9 // .
opMIRP01010 = 0xea // .
opMIRP01011 = 0xeb // .
opMIRP01100 = 0xec // .
opMIRP01101 = 0xed // .
opMIRP01110 = 0xee // .
opMIRP01111 = 0xef // .
opMIRP10000 = 0xf0 // .
opMIRP10001 = 0xf1 // .
opMIRP10010 = 0xf2 // .
opMIRP10011 = 0xf3 // .
opMIRP10100 = 0xf4 // .
opMIRP10101 = 0xf5 // .
opMIRP10110 = 0xf6 // .
opMIRP10111 = 0xf7 // .
opMIRP11000 = 0xf8 // .
opMIRP11001 = 0xf9 // .
opMIRP11010 = 0xfa // .
opMIRP11011 = 0xfb // .
opMIRP11100 = 0xfc // .
opMIRP11101 = 0xfd // .
opMIRP11110 = 0xfe // .
opMIRP11111 = 0xff // .
)
// popCount is the number of stack elements that each opcode pops.
var popCount = [256]uint8{
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
}

643
vendor/github.com/golang/freetype/truetype/truetype.go generated vendored Normal file
View file

@ -0,0 +1,643 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
// Package truetype provides a parser for the TTF and TTC file formats.
// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
// and http://www.microsoft.com/typography/otspec/
//
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
// metrics and control points. All these methods take a scale parameter, which
// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
// fixed.Int26_6(10 << 6).
//
// To measure a TrueType font in ideal FUnit space, use scale equal to
// font.FUnitsPerEm().
package truetype
import (
"fmt"
"golang.org/x/image/math/fixed"
)
// An Index is a Font's index of a rune.
type Index uint16
// A NameID identifies a name table entry.
//
// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
type NameID uint16
const (
NameIDCopyright NameID = 0
NameIDFontFamily = 1
NameIDFontSubfamily = 2
NameIDUniqueSubfamilyID = 3
NameIDFontFullName = 4
NameIDNameTableVersion = 5
NameIDPostscriptName = 6
NameIDTrademarkNotice = 7
NameIDManufacturerName = 8
NameIDDesignerName = 9
NameIDFontDescription = 10
NameIDFontVendorURL = 11
NameIDFontDesignerURL = 12
NameIDFontLicense = 13
NameIDFontLicenseURL = 14
NameIDPreferredFamily = 16
NameIDPreferredSubfamily = 17
NameIDCompatibleName = 18
NameIDSampleText = 19
)
const (
// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
// least-significant 16-bit Platform Specific ID. The magic numbers are
// specified at https://www.microsoft.com/typography/otspec/name.htm
unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
)
// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
AdvanceWidth, LeftSideBearing fixed.Int26_6
}
// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
AdvanceHeight, TopSideBearing fixed.Int26_6
}
// A FormatError reports that the input is not a valid TrueType font.
type FormatError string
func (e FormatError) Error() string {
return "freetype: invalid TrueType format: " + string(e)
}
// An UnsupportedError reports that the input uses a valid but unimplemented
// TrueType feature.
type UnsupportedError string
func (e UnsupportedError) Error() string {
return "freetype: unsupported TrueType feature: " + string(e)
}
// u32 returns the big-endian uint32 at b[i:].
func u32(b []byte, i int) uint32 {
return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
}
// u16 returns the big-endian uint16 at b[i:].
func u16(b []byte, i int) uint16 {
return uint16(b[i])<<8 | uint16(b[i+1])
}
// readTable returns a slice of the TTF data given by a table's directory entry.
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
offset := int(u32(offsetLength, 0))
if offset < 0 {
return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
}
length := int(u32(offsetLength, 4))
if length < 0 {
return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
}
end := offset + length
if end < 0 || end > len(ttf) {
return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
}
return ttf[offset:end], nil
}
// parseSubtables returns the offset and platformID of the best subtable in
// table, where best favors a Unicode cmap encoding, and failing that, a
// Microsoft cmap encoding. offset is the offset of the first subtable in
// table, and size is the size of each subtable.
//
// If pred is non-nil, then only subtables that satisfy that predicate will be
// considered.
func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
bestOffset int, bestPID uint32, retErr error) {
if len(table) < 4 {
return 0, 0, FormatError(name + " too short")
}
nSubtables := int(u16(table, 2))
if len(table) < size*nSubtables+offset {
return 0, 0, FormatError(name + " too short")
}
ok := false
for i := 0; i < nSubtables; i, offset = i+1, offset+size {
if pred != nil && !pred(table[offset:]) {
continue
}
// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
// All values are big-endian.
pidPsid := u32(table, offset)
// We prefer the Unicode cmap encoding. Failing to find that, we fall
// back onto the Microsoft cmap encoding.
if pidPsid == unicodeEncoding {
bestOffset, bestPID, ok = offset, pidPsid>>16, true
break
} else if pidPsid == microsoftSymbolEncoding ||
pidPsid == microsoftUCS2Encoding ||
pidPsid == microsoftUCS4Encoding {
bestOffset, bestPID, ok = offset, pidPsid>>16, true
// We don't break out of the for loop, so that Unicode can override Microsoft.
}
}
if !ok {
return 0, 0, UnsupportedError(name + " encoding")
}
return bestOffset, bestPID, nil
}
const (
locaOffsetFormatUnknown int = iota
locaOffsetFormatShort
locaOffsetFormatLong
)
// A cm holds a parsed cmap entry.
type cm struct {
start, end, delta, offset uint32
}
// A Font represents a Truetype font.
type Font struct {
// Tables sliced from the TTF data. The different tables are documented
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
cmapIndexes []byte
// Cached values derived from the raw ttf data.
cm []cm
locaOffsetFormat int
nGlyph, nHMetric, nKern int
fUnitsPerEm int32
ascent int32 // In FUnits.
descent int32 // In FUnits; typically negative.
bounds fixed.Rectangle26_6 // In FUnits.
// Values from the maxp section.
maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
}
func (f *Font) parseCmap() error {
const (
cmapFormat4 = 4
cmapFormat12 = 12
languageIndependent = 0
)
offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
if err != nil {
return err
}
offset = int(u32(f.cmap, offset+4))
if offset <= 0 || offset > len(f.cmap) {
return FormatError("bad cmap offset")
}
cmapFormat := u16(f.cmap, offset)
switch cmapFormat {
case cmapFormat4:
language := u16(f.cmap, offset+4)
if language != languageIndependent {
return UnsupportedError(fmt.Sprintf("language: %d", language))
}
segCountX2 := int(u16(f.cmap, offset+6))
if segCountX2%2 == 1 {
return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
}
segCount := segCountX2 / 2
offset += 14
f.cm = make([]cm, segCount)
for i := 0; i < segCount; i++ {
f.cm[i].end = uint32(u16(f.cmap, offset))
offset += 2
}
offset += 2
for i := 0; i < segCount; i++ {
f.cm[i].start = uint32(u16(f.cmap, offset))
offset += 2
}
for i := 0; i < segCount; i++ {
f.cm[i].delta = uint32(u16(f.cmap, offset))
offset += 2
}
for i := 0; i < segCount; i++ {
f.cm[i].offset = uint32(u16(f.cmap, offset))
offset += 2
}
f.cmapIndexes = f.cmap[offset:]
return nil
case cmapFormat12:
if u16(f.cmap, offset+2) != 0 {
return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
}
length := u32(f.cmap, offset+4)
language := u32(f.cmap, offset+8)
if language != languageIndependent {
return UnsupportedError(fmt.Sprintf("language: %d", language))
}
nGroups := u32(f.cmap, offset+12)
if length != 12*nGroups+16 {
return FormatError("inconsistent cmap length")
}
offset += 16
f.cm = make([]cm, nGroups)
for i := uint32(0); i < nGroups; i++ {
f.cm[i].start = u32(f.cmap, offset+0)
f.cm[i].end = u32(f.cmap, offset+4)
f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
offset += 12
}
return nil
}
return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
}
func (f *Font) parseHead() error {
if len(f.head) != 54 {
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
}
f.fUnitsPerEm = int32(u16(f.head, 18))
f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
switch i := u16(f.head, 50); i {
case 0:
f.locaOffsetFormat = locaOffsetFormatShort
case 1:
f.locaOffsetFormat = locaOffsetFormatLong
default:
return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
}
return nil
}
func (f *Font) parseHhea() error {
if len(f.hhea) != 36 {
return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
}
f.ascent = int32(int16(u16(f.hhea, 4)))
f.descent = int32(int16(u16(f.hhea, 6)))
f.nHMetric = int(u16(f.hhea, 34))
if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
}
return nil
}
func (f *Font) parseKern() error {
// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
// and Windows should use the old format."
// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
// just like the C Freetype implementation.
if len(f.kern) == 0 {
if f.nKern != 0 {
return FormatError("bad kern table length")
}
return nil
}
if len(f.kern) < 18 {
return FormatError("kern data too short")
}
version, offset := u16(f.kern, 0), 2
if version != 0 {
return UnsupportedError(fmt.Sprintf("kern version: %d", version))
}
n, offset := u16(f.kern, offset), offset+2
if n != 1 {
return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
}
offset += 2
length, offset := int(u16(f.kern, offset)), offset+2
coverage, offset := u16(f.kern, offset), offset+2
if coverage != 0x0001 {
// We only support horizontal kerning.
return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
}
f.nKern, offset = int(u16(f.kern, offset)), offset+2
if 6*f.nKern != length-14 {
return FormatError("bad kern table length")
}
return nil
}
func (f *Font) parseMaxp() error {
if len(f.maxp) != 32 {
return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
}
f.nGlyph = int(u16(f.maxp, 4))
f.maxTwilightPoints = u16(f.maxp, 16)
f.maxStorage = u16(f.maxp, 18)
f.maxFunctionDefs = u16(f.maxp, 20)
f.maxStackElements = u16(f.maxp, 24)
return nil
}
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
if x >= 0 {
x += fixed.Int26_6(f.fUnitsPerEm) / 2
} else {
x -= fixed.Int26_6(f.fUnitsPerEm) / 2
}
return x / fixed.Int26_6(f.fUnitsPerEm)
}
// Bounds returns the union of a Font's glyphs' bounds.
func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
b := f.bounds
b.Min.X = f.scale(scale * b.Min.X)
b.Min.Y = f.scale(scale * b.Min.Y)
b.Max.X = f.scale(scale * b.Max.X)
b.Max.Y = f.scale(scale * b.Max.Y)
return b
}
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
func (f *Font) FUnitsPerEm() int32 {
return f.fUnitsPerEm
}
// Index returns a Font's index for the given rune.
func (f *Font) Index(x rune) Index {
c := uint32(x)
for i, j := 0, len(f.cm); i < j; {
h := i + (j-i)/2
cm := &f.cm[h]
if c < cm.start {
j = h
} else if cm.end < c {
i = h + 1
} else if cm.offset == 0 {
return Index(c + cm.delta)
} else {
offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
return Index(u16(f.cmapIndexes, offset))
}
}
return 0
}
// Name returns the Font's name value for the given NameID. It returns "" if
// there was an error, or if that name was not found.
func (f *Font) Name(id NameID) string {
x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
return NameID(u16(b, 6)) == id
})
if err != nil {
return ""
}
offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
// Return the ASCII value of the encoded string.
// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
src := f.name[offset : offset+length]
var dst []byte
if platformID != 1 { // UTF-16.
if len(src)&1 != 0 {
return ""
}
dst = make([]byte, len(src)/2)
for i := range dst {
dst[i] = printable(u16(src, 2*i))
}
} else { // ASCII.
dst = make([]byte, len(src))
for i, c := range src {
dst[i] = printable(uint16(c))
}
}
return string(dst)
}
func printable(r uint16) byte {
if 0x20 <= r && r < 0x7f {
return byte(r)
}
return '?'
}
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return HMetric{}
}
if j >= f.nHMetric {
p := 4 * (f.nHMetric - 1)
return HMetric{
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
}
}
return HMetric{
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
}
}
// HMetric returns the horizontal metrics for the glyph with the given index.
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
h := f.unscaledHMetric(i)
h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
return h
}
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
// the given index. yMax is the top of the glyph's bounding box.
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return VMetric{}
}
if 4*j+4 <= len(f.vmtx) {
return VMetric{
AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
}
}
// The OS/2 table has grown over time.
// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
// says that it was originally 68 bytes. Optional fields, including
// the ascender and descender, are described at
// http://www.microsoft.com/typography/otspec/os2.htm
if len(f.os2) >= 72 {
sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
return VMetric{
AdvanceHeight: sTypoAscender - sTypoDescender,
TopSideBearing: sTypoAscender - yMax,
}
}
return VMetric{
AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
TopSideBearing: 0,
}
}
// VMetric returns the vertical metrics for the glyph with the given index.
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
// TODO: should 0 be bounds.YMax?
v := f.unscaledVMetric(i, 0)
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
v.TopSideBearing = f.scale(scale * v.TopSideBearing)
return v
}
// Kern returns the horizontal adjustment for the given glyph pair. A positive
// kern means to move the glyphs further apart.
func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
if f.nKern == 0 {
return 0
}
g := uint32(i0)<<16 | uint32(i1)
lo, hi := 0, f.nKern
for lo < hi {
i := (lo + hi) / 2
ig := u32(f.kern, 18+6*i)
if ig < g {
lo = i + 1
} else if ig > g {
hi = i
} else {
return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
}
}
return 0
}
// Parse returns a new Font for the given TTF or TTC data.
//
// For TrueType Collections, the first font in the collection is parsed.
func Parse(ttf []byte) (font *Font, err error) {
return parse(ttf, 0)
}
func parse(ttf []byte, offset int) (font *Font, err error) {
if len(ttf)-offset < 12 {
err = FormatError("TTF data is too short")
return
}
originalOffset := offset
magic, offset := u32(ttf, offset), offset+4
switch magic {
case 0x00010000:
// No-op.
case 0x74746366: // "ttcf" as a big-endian uint32.
if originalOffset != 0 {
err = FormatError("recursive TTC")
return
}
ttcVersion, offset := u32(ttf, offset), offset+4
if ttcVersion != 0x00010000 {
// TODO: support TTC version 2.0, once I have such a .ttc file to test with.
err = FormatError("bad TTC version")
return
}
numFonts, offset := int(u32(ttf, offset)), offset+4
if numFonts <= 0 {
err = FormatError("bad number of TTC fonts")
return
}
if len(ttf[offset:])/4 < numFonts {
err = FormatError("TTC offset table is too short")
return
}
// TODO: provide an API to select which font in a TrueType collection to return,
// not just the first one. This may require an API to parse a TTC's name tables,
// so users of this package can select the font in a TTC by name.
offset = int(u32(ttf, offset))
if offset <= 0 || offset > len(ttf) {
err = FormatError("bad TTC offset")
return
}
return parse(ttf, offset)
default:
err = FormatError("bad TTF version")
return
}
n, offset := int(u16(ttf, offset)), offset+2
if len(ttf) < 16*n+12 {
err = FormatError("TTF data is too short")
return
}
f := new(Font)
// Assign the table slices.
for i := 0; i < n; i++ {
x := 16*i + 12
switch string(ttf[x : x+4]) {
case "cmap":
f.cmap, err = readTable(ttf, ttf[x+8:x+16])
case "cvt ":
f.cvt, err = readTable(ttf, ttf[x+8:x+16])
case "fpgm":
f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
case "glyf":
f.glyf, err = readTable(ttf, ttf[x+8:x+16])
case "hdmx":
f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
case "head":
f.head, err = readTable(ttf, ttf[x+8:x+16])
case "hhea":
f.hhea, err = readTable(ttf, ttf[x+8:x+16])
case "hmtx":
f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
case "kern":
f.kern, err = readTable(ttf, ttf[x+8:x+16])
case "loca":
f.loca, err = readTable(ttf, ttf[x+8:x+16])
case "maxp":
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
case "name":
f.name, err = readTable(ttf, ttf[x+8:x+16])
case "OS/2":
f.os2, err = readTable(ttf, ttf[x+8:x+16])
case "prep":
f.prep, err = readTable(ttf, ttf[x+8:x+16])
case "vmtx":
f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
}
if err != nil {
return
}
}
// Parse and sanity-check the TTF data.
if err = f.parseHead(); err != nil {
return
}
if err = f.parseMaxp(); err != nil {
return
}
if err = f.parseCmap(); err != nil {
return
}
if err = f.parseKern(); err != nil {
return
}
if err = f.parseHhea(); err != nil {
return
}
font = f
return
}

202
vendor/github.com/golang/geo/LICENSE generated vendored Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

22
vendor/github.com/golang/geo/r1/doc.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package r1 implements types and functions for working with geometry in ¹.
See ../s2 for a more detailed overview.
*/
package r1

161
vendor/github.com/golang/geo/r1/interval.go generated vendored Normal file
View file

@ -0,0 +1,161 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package r1
import (
"fmt"
"math"
)
// Interval represents a closed interval on .
// Zero-length intervals (where Lo == Hi) represent single points.
// If Lo > Hi then the interval is empty.
type Interval struct {
Lo, Hi float64
}
// EmptyInterval returns an empty interval.
func EmptyInterval() Interval { return Interval{1, 0} }
// IntervalFromPoint returns an interval representing a single point.
func IntervalFromPoint(p float64) Interval { return Interval{p, p} }
// IsEmpty reports whether the interval is empty.
func (i Interval) IsEmpty() bool { return i.Lo > i.Hi }
// Equal returns true iff the interval contains the same points as oi.
func (i Interval) Equal(oi Interval) bool {
return i == oi || i.IsEmpty() && oi.IsEmpty()
}
// Center returns the midpoint of the interval.
// It is undefined for empty intervals.
func (i Interval) Center() float64 { return 0.5 * (i.Lo + i.Hi) }
// Length returns the length of the interval.
// The length of an empty interval is negative.
func (i Interval) Length() float64 { return i.Hi - i.Lo }
// Contains returns true iff the interval contains p.
func (i Interval) Contains(p float64) bool { return i.Lo <= p && p <= i.Hi }
// ContainsInterval returns true iff the interval contains oi.
func (i Interval) ContainsInterval(oi Interval) bool {
if oi.IsEmpty() {
return true
}
return i.Lo <= oi.Lo && oi.Hi <= i.Hi
}
// InteriorContains returns true iff the the interval strictly contains p.
func (i Interval) InteriorContains(p float64) bool {
return i.Lo < p && p < i.Hi
}
// InteriorContainsInterval returns true iff the interval strictly contains oi.
func (i Interval) InteriorContainsInterval(oi Interval) bool {
if oi.IsEmpty() {
return true
}
return i.Lo < oi.Lo && oi.Hi < i.Hi
}
// Intersects returns true iff the interval contains any points in common with oi.
func (i Interval) Intersects(oi Interval) bool {
if i.Lo <= oi.Lo {
return oi.Lo <= i.Hi && oi.Lo <= oi.Hi // oi.Lo ∈ i and oi is not empty
}
return i.Lo <= oi.Hi && i.Lo <= i.Hi // i.Lo ∈ oi and i is not empty
}
// InteriorIntersects returns true iff the interior of the interval contains any points in common with oi, including the latter's boundary.
func (i Interval) InteriorIntersects(oi Interval) bool {
return oi.Lo < i.Hi && i.Lo < oi.Hi && i.Lo < i.Hi && oi.Lo <= oi.Hi
}
// Intersection returns the interval containing all points common to i and j.
func (i Interval) Intersection(j Interval) Interval {
// Empty intervals do not need to be special-cased.
return Interval{
Lo: math.Max(i.Lo, j.Lo),
Hi: math.Min(i.Hi, j.Hi),
}
}
// AddPoint returns the interval expanded so that it contains the given point.
func (i Interval) AddPoint(p float64) Interval {
if i.IsEmpty() {
return Interval{p, p}
}
if p < i.Lo {
return Interval{p, i.Hi}
}
if p > i.Hi {
return Interval{i.Lo, p}
}
return i
}
// ClampPoint returns the closest point in the interval to the given point "p".
// The interval must be non-empty.
func (i Interval) ClampPoint(p float64) float64 {
return math.Max(i.Lo, math.Min(i.Hi, p))
}
// Expanded returns an interval that has been expanded on each side by margin.
// If margin is negative, then the function shrinks the interval on
// each side by margin instead. The resulting interval may be empty. Any
// expansion of an empty interval remains empty.
func (i Interval) Expanded(margin float64) Interval {
if i.IsEmpty() {
return i
}
return Interval{i.Lo - margin, i.Hi + margin}
}
// Union returns the smallest interval that contains this interval and the given interval.
func (i Interval) Union(other Interval) Interval {
if i.IsEmpty() {
return other
}
if other.IsEmpty() {
return i
}
return Interval{math.Min(i.Lo, other.Lo), math.Max(i.Hi, other.Hi)}
}
func (i Interval) String() string { return fmt.Sprintf("[%.7f, %.7f]", i.Lo, i.Hi) }
// epsilon is a small number that represents a reasonable level of noise between two
// values that can be considered to be equal.
const epsilon = 1e-14
// ApproxEqual reports whether the interval can be transformed into the
// given interval by moving each endpoint a small distance.
// The empty interval is considered to be positioned arbitrarily on the
// real line, so any interval with a small enough length will match
// the empty interval.
func (i Interval) ApproxEqual(other Interval) bool {
if i.IsEmpty() {
return other.Length() <= 2*epsilon
}
if other.IsEmpty() {
return i.Length() <= 2*epsilon
}
return math.Abs(other.Lo-i.Lo) <= epsilon &&
math.Abs(other.Hi-i.Hi) <= epsilon
}

22
vendor/github.com/golang/geo/r2/doc.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package r2 implements types and functions for working with geometry in ².
See package s2 for a more detailed overview.
*/
package r2

257
vendor/github.com/golang/geo/r2/rect.go generated vendored Normal file
View file

@ -0,0 +1,257 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package r2
import (
"fmt"
"math"
"github.com/golang/geo/r1"
)
// Point represents a point in ℝ².
type Point struct {
X, Y float64
}
// Add returns the sum of p and op.
func (p Point) Add(op Point) Point { return Point{p.X + op.X, p.Y + op.Y} }
// Sub returns the difference of p and op.
func (p Point) Sub(op Point) Point { return Point{p.X - op.X, p.Y - op.Y} }
// Mul returns the scalar product of p and m.
func (p Point) Mul(m float64) Point { return Point{m * p.X, m * p.Y} }
// Ortho returns a counterclockwise orthogonal point with the same norm.
func (p Point) Ortho() Point { return Point{-p.Y, p.X} }
// Dot returns the dot product between p and op.
func (p Point) Dot(op Point) float64 { return p.X*op.X + p.Y*op.Y }
// Cross returns the cross product of p and op.
func (p Point) Cross(op Point) float64 { return p.X*op.Y - p.Y*op.X }
// Norm returns the vector's norm.
func (p Point) Norm() float64 { return math.Hypot(p.X, p.Y) }
// Normalize returns a unit point in the same direction as p.
func (p Point) Normalize() Point {
if p.X == 0 && p.Y == 0 {
return p
}
return p.Mul(1 / p.Norm())
}
func (p Point) String() string { return fmt.Sprintf("(%.12f, %.12f)", p.X, p.Y) }
// Rect represents a closed axis-aligned rectangle in the (x,y) plane.
type Rect struct {
X, Y r1.Interval
}
// RectFromPoints constructs a rect that contains the given points.
func RectFromPoints(pts ...Point) Rect {
// Because the default value on interval is 0,0, we need to manually
// define the interval from the first point passed in as our starting
// interval, otherwise we end up with the case of passing in
// Point{0.2, 0.3} and getting the starting Rect of {0, 0.2}, {0, 0.3}
// instead of the Rect {0.2, 0.2}, {0.3, 0.3} which is not correct.
if len(pts) == 0 {
return Rect{}
}
r := Rect{
X: r1.Interval{Lo: pts[0].X, Hi: pts[0].X},
Y: r1.Interval{Lo: pts[0].Y, Hi: pts[0].Y},
}
for _, p := range pts[1:] {
r = r.AddPoint(p)
}
return r
}
// RectFromCenterSize constructs a rectangle with the given center and size.
// Both dimensions of size must be non-negative.
func RectFromCenterSize(center, size Point) Rect {
return Rect{
r1.Interval{Lo: center.X - size.X/2, Hi: center.X + size.X/2},
r1.Interval{Lo: center.Y - size.Y/2, Hi: center.Y + size.Y/2},
}
}
// EmptyRect constructs the canonical empty rectangle. Use IsEmpty() to test
// for empty rectangles, since they have more than one representation. A Rect{}
// is not the same as the EmptyRect.
func EmptyRect() Rect {
return Rect{r1.EmptyInterval(), r1.EmptyInterval()}
}
// IsValid reports whether the rectangle is valid.
// This requires the width to be empty iff the height is empty.
func (r Rect) IsValid() bool {
return r.X.IsEmpty() == r.Y.IsEmpty()
}
// IsEmpty reports whether the rectangle is empty.
func (r Rect) IsEmpty() bool {
return r.X.IsEmpty()
}
// Vertices returns all four vertices of the rectangle. Vertices are returned in
// CCW direction starting with the lower left corner.
func (r Rect) Vertices() [4]Point {
return [4]Point{
{r.X.Lo, r.Y.Lo},
{r.X.Hi, r.Y.Lo},
{r.X.Hi, r.Y.Hi},
{r.X.Lo, r.Y.Hi},
}
}
// VertexIJ returns the vertex in direction i along the X-axis (0=left, 1=right) and
// direction j along the Y-axis (0=down, 1=up).
func (r Rect) VertexIJ(i, j int) Point {
x := r.X.Lo
if i == 1 {
x = r.X.Hi
}
y := r.Y.Lo
if j == 1 {
y = r.Y.Hi
}
return Point{x, y}
}
// Lo returns the low corner of the rect.
func (r Rect) Lo() Point {
return Point{r.X.Lo, r.Y.Lo}
}
// Hi returns the high corner of the rect.
func (r Rect) Hi() Point {
return Point{r.X.Hi, r.Y.Hi}
}
// Center returns the center of the rectangle in (x,y)-space
func (r Rect) Center() Point {
return Point{r.X.Center(), r.Y.Center()}
}
// Size returns the width and height of this rectangle in (x,y)-space. Empty
// rectangles have a negative width and height.
func (r Rect) Size() Point {
return Point{r.X.Length(), r.Y.Length()}
}
// ContainsPoint reports whether the rectangle contains the given point.
// Rectangles are closed regions, i.e. they contain their boundary.
func (r Rect) ContainsPoint(p Point) bool {
return r.X.Contains(p.X) && r.Y.Contains(p.Y)
}
// InteriorContainsPoint returns true iff the given point is contained in the interior
// of the region (i.e. the region excluding its boundary).
func (r Rect) InteriorContainsPoint(p Point) bool {
return r.X.InteriorContains(p.X) && r.Y.InteriorContains(p.Y)
}
// Contains reports whether the rectangle contains the given rectangle.
func (r Rect) Contains(other Rect) bool {
return r.X.ContainsInterval(other.X) && r.Y.ContainsInterval(other.Y)
}
// InteriorContains reports whether the interior of this rectangle contains all of the
// points of the given other rectangle (including its boundary).
func (r Rect) InteriorContains(other Rect) bool {
return r.X.InteriorContainsInterval(other.X) && r.Y.InteriorContainsInterval(other.Y)
}
// Intersects reports whether this rectangle and the other rectangle have any points in common.
func (r Rect) Intersects(other Rect) bool {
return r.X.Intersects(other.X) && r.Y.Intersects(other.Y)
}
// InteriorIntersects reports whether the interior of this rectangle intersects
// any point (including the boundary) of the given other rectangle.
func (r Rect) InteriorIntersects(other Rect) bool {
return r.X.InteriorIntersects(other.X) && r.Y.InteriorIntersects(other.Y)
}
// AddPoint expands the rectangle to include the given point. The rectangle is
// expanded by the minimum amount possible.
func (r Rect) AddPoint(p Point) Rect {
return Rect{r.X.AddPoint(p.X), r.Y.AddPoint(p.Y)}
}
// AddRect expands the rectangle to include the given rectangle. This is the
// same as replacing the rectangle by the union of the two rectangles, but
// is more efficient.
func (r Rect) AddRect(other Rect) Rect {
return Rect{r.X.Union(other.X), r.Y.Union(other.Y)}
}
// ClampPoint returns the closest point in the rectangle to the given point.
// The rectangle must be non-empty.
func (r Rect) ClampPoint(p Point) Point {
return Point{r.X.ClampPoint(p.X), r.Y.ClampPoint(p.Y)}
}
// Expanded returns a rectangle that has been expanded in the x-direction
// by margin.X, and in y-direction by margin.Y. If either margin is empty,
// then shrink the interval on the corresponding sides instead. The resulting
// rectangle may be empty. Any expansion of an empty rectangle remains empty.
func (r Rect) Expanded(margin Point) Rect {
xx := r.X.Expanded(margin.X)
yy := r.Y.Expanded(margin.Y)
if xx.IsEmpty() || yy.IsEmpty() {
return EmptyRect()
}
return Rect{xx, yy}
}
// ExpandedByMargin returns a Rect that has been expanded by the amount on all sides.
func (r Rect) ExpandedByMargin(margin float64) Rect {
return r.Expanded(Point{margin, margin})
}
// Union returns the smallest rectangle containing the union of this rectangle and
// the given rectangle.
func (r Rect) Union(other Rect) Rect {
return Rect{r.X.Union(other.X), r.Y.Union(other.Y)}
}
// Intersection returns the smallest rectangle containing the intersection of this
// rectangle and the given rectangle.
func (r Rect) Intersection(other Rect) Rect {
xx := r.X.Intersection(other.X)
yy := r.Y.Intersection(other.Y)
if xx.IsEmpty() || yy.IsEmpty() {
return EmptyRect()
}
return Rect{xx, yy}
}
// ApproxEquals returns true if the x- and y-intervals of the two rectangles are
// the same up to the given tolerance.
func (r Rect) ApproxEquals(r2 Rect) bool {
return r.X.ApproxEqual(r2.X) && r.Y.ApproxEqual(r2.Y)
}
func (r Rect) String() string { return fmt.Sprintf("[Lo%s, Hi%s]", r.Lo(), r.Hi()) }

22
vendor/github.com/golang/geo/r3/doc.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package r3 implements types and functions for working with geometry in ³.
See ../s2 for a more detailed overview.
*/
package r3

200
vendor/github.com/golang/geo/r3/precisevector.go generated vendored Normal file
View file

@ -0,0 +1,200 @@
/*
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package r3
import (
"fmt"
"math/big"
)
const (
// prec is the number of bits of precision to use for the Float values.
// To keep things simple, we use the maximum allowable precision on big
// values. This allows us to handle all values we expect in the s2 library.
prec = big.MaxPrec
)
// define some commonly referenced values.
var (
precise0 = precInt(0)
precise1 = precInt(1)
)
// precStr wraps the conversion from a string into a big.Float. For results that
// actually can be represented exactly, this should only be used on values that
// are integer multiples of integer powers of 2.
func precStr(s string) *big.Float {
// Explicitly ignoring the bool return for this usage.
f, _ := new(big.Float).SetPrec(prec).SetString(s)
return f
}
func precInt(i int64) *big.Float {
return new(big.Float).SetPrec(prec).SetInt64(i)
}
func precFloat(f float64) *big.Float {
return new(big.Float).SetPrec(prec).SetFloat64(f)
}
func precAdd(a, b *big.Float) *big.Float {
return new(big.Float).SetPrec(prec).Add(a, b)
}
func precSub(a, b *big.Float) *big.Float {
return new(big.Float).SetPrec(prec).Sub(a, b)
}
func precMul(a, b *big.Float) *big.Float {
return new(big.Float).SetPrec(prec).Mul(a, b)
}
// PreciseVector represents a point in ℝ³ using high-precision values.
// Note that this is NOT a complete implementation because there are some
// operations that Vector supports that are not feasible with arbitrary precision
// math. (e.g., methods that need divison like Normalize, or methods needing a
// square root operation such as Norm)
type PreciseVector struct {
X, Y, Z *big.Float
}
// PreciseVectorFromVector creates a high precision vector from the given Vector.
func PreciseVectorFromVector(v Vector) PreciseVector {
return NewPreciseVector(v.X, v.Y, v.Z)
}
// NewPreciseVector creates a high precision vector from the given floating point values.
func NewPreciseVector(x, y, z float64) PreciseVector {
return PreciseVector{
X: precFloat(x),
Y: precFloat(y),
Z: precFloat(z),
}
}
// Vector returns this precise vector converted to a Vector.
func (v PreciseVector) Vector() Vector {
// The accuracy flag is ignored on these conversions back to float64.
x, _ := v.X.Float64()
y, _ := v.Y.Float64()
z, _ := v.Z.Float64()
return Vector{x, y, z}
}
// Equals reports whether v and ov are equal.
func (v PreciseVector) Equals(ov PreciseVector) bool {
return v.X.Cmp(ov.X) == 0 && v.Y.Cmp(ov.Y) == 0 && v.Z.Cmp(ov.Z) == 0
}
func (v PreciseVector) String() string {
return fmt.Sprintf("(%v, %v, %v)", v.X, v.Y, v.Z)
}
// Norm2 returns the square of the norm.
func (v PreciseVector) Norm2() *big.Float { return v.Dot(v) }
// IsUnit reports whether this vector is of unit length.
func (v PreciseVector) IsUnit() bool {
return v.Norm2().Cmp(precise1) == 0
}
// Abs returns the vector with nonnegative components.
func (v PreciseVector) Abs() PreciseVector {
return PreciseVector{
X: new(big.Float).Abs(v.X),
Y: new(big.Float).Abs(v.Y),
Z: new(big.Float).Abs(v.Z),
}
}
// Add returns the standard vector sum of v and ov.
func (v PreciseVector) Add(ov PreciseVector) PreciseVector {
return PreciseVector{
X: precAdd(v.X, ov.X),
Y: precAdd(v.Y, ov.Y),
Z: precAdd(v.Z, ov.Z),
}
}
// Sub returns the standard vector difference of v and ov.
func (v PreciseVector) Sub(ov PreciseVector) PreciseVector {
return PreciseVector{
X: precSub(v.X, ov.X),
Y: precSub(v.Y, ov.Y),
Z: precSub(v.Z, ov.Z),
}
}
// Mul returns the standard scalar product of v and f.
func (v PreciseVector) Mul(f *big.Float) PreciseVector {
return PreciseVector{
X: precMul(v.X, f),
Y: precMul(v.Y, f),
Z: precMul(v.Z, f),
}
}
// MulByFloat64 returns the standard scalar product of v and f.
func (v PreciseVector) MulByFloat64(f float64) PreciseVector {
return v.Mul(precFloat(f))
}
// Dot returns the standard dot product of v and ov.
func (v PreciseVector) Dot(ov PreciseVector) *big.Float {
return precAdd(precMul(v.X, ov.X), precAdd(precMul(v.Y, ov.Y), precMul(v.Z, ov.Z)))
}
// Cross returns the standard cross product of v and ov.
func (v PreciseVector) Cross(ov PreciseVector) PreciseVector {
return PreciseVector{
X: precSub(precMul(v.Y, ov.Z), precMul(v.Z, ov.Y)),
Y: precSub(precMul(v.Z, ov.X), precMul(v.X, ov.Z)),
Z: precSub(precMul(v.X, ov.Y), precMul(v.Y, ov.X)),
}
}
// LargestComponent returns the axis that represents the largest component in this vector.
func (v PreciseVector) LargestComponent() Axis {
t := v.Abs()
if t.X.Cmp(t.Y) > 0 {
if t.X.Cmp(t.Z) > 0 {
return XAxis
}
return ZAxis
}
if t.Y.Cmp(t.Z) > 0 {
return YAxis
}
return ZAxis
}
// SmallestComponent returns the axis that represents the smallest component in this vector.
func (v PreciseVector) SmallestComponent() Axis {
t := v.Abs()
if t.X.Cmp(t.Y) < 0 {
if t.X.Cmp(t.Z) < 0 {
return XAxis
}
return ZAxis
}
if t.Y.Cmp(t.Z) < 0 {
return YAxis
}
return ZAxis
}

184
vendor/github.com/golang/geo/r3/vector.go generated vendored Normal file
View file

@ -0,0 +1,184 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package r3
import (
"fmt"
"math"
"github.com/golang/geo/s1"
)
// Vector represents a point in ℝ³.
type Vector struct {
X, Y, Z float64
}
// ApproxEqual reports whether v and ov are equal within a small epsilon.
func (v Vector) ApproxEqual(ov Vector) bool {
const epsilon = 1e-16
return math.Abs(v.X-ov.X) < epsilon && math.Abs(v.Y-ov.Y) < epsilon && math.Abs(v.Z-ov.Z) < epsilon
}
func (v Vector) String() string { return fmt.Sprintf("(%0.24f, %0.24f, %0.24f)", v.X, v.Y, v.Z) }
// Norm returns the vector's norm.
func (v Vector) Norm() float64 { return math.Sqrt(v.Dot(v)) }
// Norm2 returns the square of the norm.
func (v Vector) Norm2() float64 { return v.Dot(v) }
// Normalize returns a unit vector in the same direction as v.
func (v Vector) Normalize() Vector {
if v == (Vector{0, 0, 0}) {
return v
}
return v.Mul(1 / v.Norm())
}
// IsUnit returns whether this vector is of approximately unit length.
func (v Vector) IsUnit() bool {
const epsilon = 5e-14
return math.Abs(v.Norm2()-1) <= epsilon
}
// Abs returns the vector with nonnegative components.
func (v Vector) Abs() Vector { return Vector{math.Abs(v.X), math.Abs(v.Y), math.Abs(v.Z)} }
// Add returns the standard vector sum of v and ov.
func (v Vector) Add(ov Vector) Vector { return Vector{v.X + ov.X, v.Y + ov.Y, v.Z + ov.Z} }
// Sub returns the standard vector difference of v and ov.
func (v Vector) Sub(ov Vector) Vector { return Vector{v.X - ov.X, v.Y - ov.Y, v.Z - ov.Z} }
// Mul returns the standard scalar product of v and m.
func (v Vector) Mul(m float64) Vector { return Vector{m * v.X, m * v.Y, m * v.Z} }
// Dot returns the standard dot product of v and ov.
func (v Vector) Dot(ov Vector) float64 { return v.X*ov.X + v.Y*ov.Y + v.Z*ov.Z }
// Cross returns the standard cross product of v and ov.
func (v Vector) Cross(ov Vector) Vector {
return Vector{
v.Y*ov.Z - v.Z*ov.Y,
v.Z*ov.X - v.X*ov.Z,
v.X*ov.Y - v.Y*ov.X,
}
}
// Distance returns the Euclidean distance between v and ov.
func (v Vector) Distance(ov Vector) float64 { return v.Sub(ov).Norm() }
// Angle returns the angle between v and ov.
func (v Vector) Angle(ov Vector) s1.Angle {
return s1.Angle(math.Atan2(v.Cross(ov).Norm(), v.Dot(ov))) * s1.Radian
}
// Axis enumerates the 3 axes of ℝ³.
type Axis int
// The three axes of ℝ³.
const (
XAxis Axis = iota
YAxis
ZAxis
)
// Ortho returns a unit vector that is orthogonal to v.
// Ortho(-v) = -Ortho(v) for all v.
func (v Vector) Ortho() Vector {
ov := Vector{0.012, 0.0053, 0.00457}
switch v.LargestComponent() {
case XAxis:
ov.Z = 1
case YAxis:
ov.X = 1
default:
ov.Y = 1
}
return v.Cross(ov).Normalize()
}
// LargestComponent returns the axis that represents the largest component in this vector.
func (v Vector) LargestComponent() Axis {
t := v.Abs()
if t.X > t.Y {
if t.X > t.Z {
return XAxis
}
return ZAxis
}
if t.Y > t.Z {
return YAxis
}
return ZAxis
}
// SmallestComponent returns the axis that represents the smallest component in this vector.
func (v Vector) SmallestComponent() Axis {
t := v.Abs()
if t.X < t.Y {
if t.X < t.Z {
return XAxis
}
return ZAxis
}
if t.Y < t.Z {
return YAxis
}
return ZAxis
}
// Cmp compares v and ov lexicographically and returns:
//
// -1 if v < ov
// 0 if v == ov
// +1 if v > ov
//
// This method is based on C++'s std::lexicographical_compare. Two entities
// are compared element by element with the given operator. The first mismatch
// defines which is less (or greater) than the other. If both have equivalent
// values they are lexicographically equal.
func (v Vector) Cmp(ov Vector) int {
if v.X < ov.X {
return -1
}
if v.X > ov.X {
return 1
}
// First elements were the same, try the next.
if v.Y < ov.Y {
return -1
}
if v.Y > ov.Y {
return 1
}
// Second elements were the same return the final compare.
if v.Z < ov.Z {
return -1
}
if v.Z > ov.Z {
return 1
}
// Both are equal
return 0
}

119
vendor/github.com/golang/geo/s1/angle.go generated vendored Normal file
View file

@ -0,0 +1,119 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s1
import (
"math"
"strconv"
)
// Angle represents a 1D angle. The internal representation is a double precision
// value in radians, so conversion to and from radians is exact.
// Conversions between E5, E6, E7, and Degrees are not always
// exact. For example, Degrees(3.1) is different from E6(3100000) or E7(310000000).
//
// The following conversions between degrees and radians are exact:
//
// Degree*180 == Radian*math.Pi
// Degree*(180/n) == Radian*(math.Pi/n) for n == 0..8
//
// These identities hold when the arguments are scaled up or down by any power
// of 2. Some similar identities are also true, for example,
//
// Degree*60 == Radian*(math.Pi/3)
//
// But be aware that this type of identity does not hold in general. For example,
//
// Degree*3 != Radian*(math.Pi/60)
//
// Similarly, the conversion to radians means that (Angle(x)*Degree).Degrees()
// does not always equal x. For example,
//
// (Angle(45*n)*Degree).Degrees() == 45*n for n == 0..8
//
// but
//
// (60*Degree).Degrees() != 60
//
// When testing for equality, you should allow for numerical errors (floatApproxEq)
// or convert to discrete E5/E6/E7 values first.
type Angle float64
// Angle units.
const (
Radian Angle = 1
Degree = (math.Pi / 180) * Radian
E5 = 1e-5 * Degree
E6 = 1e-6 * Degree
E7 = 1e-7 * Degree
)
// Radians returns the angle in radians.
func (a Angle) Radians() float64 { return float64(a) }
// Degrees returns the angle in degrees.
func (a Angle) Degrees() float64 { return float64(a / Degree) }
// round returns the value rounded to nearest as an int32.
// This does not match C++ exactly for the case of x.5.
func round(val float64) int32 {
if val < 0 {
return int32(val - 0.5)
}
return int32(val + 0.5)
}
// InfAngle returns an angle larger than any finite angle.
func InfAngle() Angle {
return Angle(math.Inf(1))
}
// isInf reports whether this Angle is infinite.
func (a Angle) isInf() bool {
return math.IsInf(float64(a), 0)
}
// E5 returns the angle in hundred thousandths of degrees.
func (a Angle) E5() int32 { return round(a.Degrees() * 1e5) }
// E6 returns the angle in millionths of degrees.
func (a Angle) E6() int32 { return round(a.Degrees() * 1e6) }
// E7 returns the angle in ten millionths of degrees.
func (a Angle) E7() int32 { return round(a.Degrees() * 1e7) }
// Abs returns the absolute value of the angle.
func (a Angle) Abs() Angle { return Angle(math.Abs(float64(a))) }
// Normalized returns an equivalent angle in [0, 2π).
func (a Angle) Normalized() Angle {
rad := math.Mod(float64(a), 2*math.Pi)
if rad < 0 {
rad += 2 * math.Pi
}
return Angle(rad)
}
func (a Angle) String() string {
return strconv.FormatFloat(a.Degrees(), 'f', 7, 64) // like "%.7f"
}
// BUG(dsymonds): The major differences from the C++ version are:
// - no unsigned E5/E6/E7 methods
// - no S2Point or S2LatLng constructors
// - no comparison or arithmetic operators

208
vendor/github.com/golang/geo/s1/chordangle.go generated vendored Normal file
View file

@ -0,0 +1,208 @@
/*
Copyright 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s1
import (
"math"
)
// ChordAngle represents the angle subtended by a chord (i.e., the straight
// line segment connecting two points on the sphere). Its representation
// makes it very efficient for computing and comparing distances, but unlike
// Angle it is only capable of representing angles between 0 and π radians.
// Generally, ChordAngle should only be used in loops where many angles need
// to be calculated and compared. Otherwise it is simpler to use Angle.
//
// ChordAngle loses some accuracy as the angle approaches π radians.
// Specifically, the representation of (π - x) radians has an error of about
// (1e-15 / x), with a maximum error of about 2e-8 radians (about 13cm on the
// Earth's surface). For comparison, for angles up to π/2 radians (10000km)
// the worst-case representation error is about 2e-16 radians (1 nanonmeter),
// which is about the same as Angle.
//
// ChordAngles are represented by the squared chord length, which can
// range from 0 to 4. Positive infinity represents an infinite squared length.
type ChordAngle float64
const (
// NegativeChordAngle represents a chord angle smaller than the zero angle.
// The only valid operations on a NegativeChordAngle are comparisons and
// Angle conversions.
NegativeChordAngle = ChordAngle(-1)
// RightChordAngle represents a chord angle of 90 degrees (a "right angle").
RightChordAngle = ChordAngle(2)
// StraightChordAngle represents a chord angle of 180 degrees (a "straight angle").
// This is the maximum finite chord angle.
StraightChordAngle = ChordAngle(4)
)
// ChordAngleFromAngle returns a ChordAngle from the given Angle.
func ChordAngleFromAngle(a Angle) ChordAngle {
if a < 0 {
return NegativeChordAngle
}
if a.isInf() {
return InfChordAngle()
}
l := 2 * math.Sin(0.5*math.Min(math.Pi, a.Radians()))
return ChordAngle(l * l)
}
// ChordAngleFromSquaredLength returns a ChordAngle from the squared chord length.
// Note that the argument is automatically clamped to a maximum of 4.0 to
// handle possible roundoff errors. The argument must be non-negative.
func ChordAngleFromSquaredLength(length2 float64) ChordAngle {
if length2 > 4 {
return StraightChordAngle
}
return ChordAngle(length2)
}
// Expanded returns a new ChordAngle that has been adjusted by the given error
// bound (which can be positive or negative). Error should be the value
// returned by either MaxPointError or MaxAngleError. For example:
// a := ChordAngleFromPoints(x, y)
// a1 := a.Expanded(a.MaxPointError())
func (c ChordAngle) Expanded(e float64) ChordAngle {
// If the angle is special, don't change it. Otherwise clamp it to the valid range.
if c.isSpecial() {
return c
}
return ChordAngle(math.Max(0.0, math.Min(4.0, float64(c)+e)))
}
// Angle converts this ChordAngle to an Angle.
func (c ChordAngle) Angle() Angle {
if c < 0 {
return -1 * Radian
}
if c.isInf() {
return InfAngle()
}
return Angle(2 * math.Asin(0.5*math.Sqrt(float64(c))))
}
// InfChordAngle returns a chord angle larger than any finite chord angle.
// The only valid operations on an InfChordAngle are comparisons and Angle conversions.
func InfChordAngle() ChordAngle {
return ChordAngle(math.Inf(1))
}
// isInf reports whether this ChordAngle is infinite.
func (c ChordAngle) isInf() bool {
return math.IsInf(float64(c), 1)
}
// isSpecial reports whether this ChordAngle is one of the special cases.
func (c ChordAngle) isSpecial() bool {
return c < 0 || c.isInf()
}
// isValid reports whether this ChordAngle is valid or not.
func (c ChordAngle) isValid() bool {
return (c >= 0 && c <= 4) || c.isSpecial()
}
// MaxPointError returns the maximum error size for a ChordAngle constructed
// from 2 Points x and y, assuming that x and y are normalized to within the
// bounds guaranteed by s2.Point.Normalize. The error is defined with respect to
// the true distance after the points are projected to lie exactly on the sphere.
func (c ChordAngle) MaxPointError() float64 {
// There is a relative error of (2.5*dblEpsilon) when computing the squared
// distance, plus an absolute error of (16 * dblEpsilon**2) because the
// lengths of the input points may differ from 1 by up to (2*dblEpsilon) each.
return 2.5*dblEpsilon*float64(c) + 16*dblEpsilon*dblEpsilon
}
// MaxAngleError returns the maximum error for a ChordAngle constructed
// as an Angle distance.
func (c ChordAngle) MaxAngleError() float64 {
return dblEpsilon * float64(c)
}
// Add adds the other ChordAngle to this one and returns the resulting value.
// This method assumes the ChordAngles are not special.
func (c ChordAngle) Add(other ChordAngle) ChordAngle {
// Note that this method (and Sub) is much more efficient than converting
// the ChordAngle to an Angle and adding those and converting back. It
// requires only one square root plus a few additions and multiplications.
// Optimization for the common case where b is an error tolerance
// parameter that happens to be set to zero.
if other == 0 {
return c
}
// Clamp the angle sum to at most 180 degrees.
if c+other >= 4 {
return StraightChordAngle
}
// Let a and b be the (non-squared) chord lengths, and let c = a+b.
// Let A, B, and C be the corresponding half-angles (a = 2*sin(A), etc).
// Then the formula below can be derived from c = 2 * sin(A+B) and the
// relationships sin(A+B) = sin(A)*cos(B) + sin(B)*cos(A)
// cos(X) = sqrt(1 - sin^2(X))
x := float64(c * (1 - 0.25*other))
y := float64(other * (1 - 0.25*c))
return ChordAngle(math.Min(4.0, x+y+2*math.Sqrt(x*y)))
}
// Sub subtracts the other ChordAngle from this one and returns the resulting
// value. This method assumes the ChordAngles are not special.
func (c ChordAngle) Sub(other ChordAngle) ChordAngle {
if other == 0 {
return c
}
if c <= other {
return 0
}
x := float64(c * (1 - 0.25*other))
y := float64(other * (1 - 0.25*c))
return ChordAngle(math.Max(0.0, x+y-2*math.Sqrt(x*y)))
}
// Sin returns the sine of this chord angle. This method is more efficient
// than converting to Angle and performing the computation.
func (c ChordAngle) Sin() float64 {
return math.Sqrt(c.Sin2())
}
// Sin2 returns the square of the sine of this chord angle.
// It is more efficient than Sin.
func (c ChordAngle) Sin2() float64 {
// Let a be the (non-squared) chord length, and let A be the corresponding
// half-angle (a = 2*sin(A)). The formula below can be derived from:
// sin(2*A) = 2 * sin(A) * cos(A)
// cos^2(A) = 1 - sin^2(A)
// This is much faster than converting to an angle and computing its sine.
return float64(c * (1 - 0.25*c))
}
// Cos returns the cosine of this chord angle. This method is more efficient
// than converting to Angle and performing the computation.
func (c ChordAngle) Cos() float64 {
// cos(2*A) = cos^2(A) - sin^2(A) = 1 - 2*sin^2(A)
return float64(1 - 0.5*c)
}
// Tan returns the tangent of this chord angle.
func (c ChordAngle) Tan() float64 {
return c.Sin() / c.Cos()
}

22
vendor/github.com/golang/geo/s1/doc.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package s1 implements types and functions for working with geometry in S¹ (circular geometry).
See ../s2 for a more detailed overview.
*/
package s1

350
vendor/github.com/golang/geo/s1/interval.go generated vendored Normal file
View file

@ -0,0 +1,350 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s1
import (
"math"
"strconv"
)
// Interval represents a closed interval on a unit circle.
// Zero-length intervals (where Lo == Hi) represent single points.
// If Lo > Hi then the interval is "inverted".
// The point at (-1, 0) on the unit circle has two valid representations,
// [π,π] and [-π,-π]. We normalize the latter to the former in IntervalFromEndpoints.
// There are two special intervals that take advantage of that:
// - the full interval, [-π,π], and
// - the empty interval, [π,-π].
// Treat the exported fields as read-only.
type Interval struct {
Lo, Hi float64
}
// IntervalFromEndpoints constructs a new interval from endpoints.
// Both arguments must be in the range [-π,π]. This function allows inverted intervals
// to be created.
func IntervalFromEndpoints(lo, hi float64) Interval {
i := Interval{lo, hi}
if lo == -math.Pi && hi != math.Pi {
i.Lo = math.Pi
}
if hi == -math.Pi && lo != math.Pi {
i.Hi = math.Pi
}
return i
}
// IntervalFromPointPair returns the minimal interval containing the two given points.
// Both arguments must be in [-π,π].
func IntervalFromPointPair(a, b float64) Interval {
if a == -math.Pi {
a = math.Pi
}
if b == -math.Pi {
b = math.Pi
}
if positiveDistance(a, b) <= math.Pi {
return Interval{a, b}
}
return Interval{b, a}
}
// EmptyInterval returns an empty interval.
func EmptyInterval() Interval { return Interval{math.Pi, -math.Pi} }
// FullInterval returns a full interval.
func FullInterval() Interval { return Interval{-math.Pi, math.Pi} }
// IsValid reports whether the interval is valid.
func (i Interval) IsValid() bool {
return (math.Abs(i.Lo) <= math.Pi && math.Abs(i.Hi) <= math.Pi &&
!(i.Lo == -math.Pi && i.Hi != math.Pi) &&
!(i.Hi == -math.Pi && i.Lo != math.Pi))
}
// IsFull reports whether the interval is full.
func (i Interval) IsFull() bool { return i.Lo == -math.Pi && i.Hi == math.Pi }
// IsEmpty reports whether the interval is empty.
func (i Interval) IsEmpty() bool { return i.Lo == math.Pi && i.Hi == -math.Pi }
// IsInverted reports whether the interval is inverted; that is, whether Lo > Hi.
func (i Interval) IsInverted() bool { return i.Lo > i.Hi }
// Invert returns the interval with endpoints swapped.
func (i Interval) Invert() Interval {
return Interval{i.Hi, i.Lo}
}
// Center returns the midpoint of the interval.
// It is undefined for full and empty intervals.
func (i Interval) Center() float64 {
c := 0.5 * (i.Lo + i.Hi)
if !i.IsInverted() {
return c
}
if c <= 0 {
return c + math.Pi
}
return c - math.Pi
}
// Length returns the length of the interval.
// The length of an empty interval is negative.
func (i Interval) Length() float64 {
l := i.Hi - i.Lo
if l >= 0 {
return l
}
l += 2 * math.Pi
if l > 0 {
return l
}
return -1
}
// Assumes p ∈ (-π,π].
func (i Interval) fastContains(p float64) bool {
if i.IsInverted() {
return (p >= i.Lo || p <= i.Hi) && !i.IsEmpty()
}
return p >= i.Lo && p <= i.Hi
}
// Contains returns true iff the interval contains p.
// Assumes p ∈ [-π,π].
func (i Interval) Contains(p float64) bool {
if p == -math.Pi {
p = math.Pi
}
return i.fastContains(p)
}
// ContainsInterval returns true iff the interval contains oi.
func (i Interval) ContainsInterval(oi Interval) bool {
if i.IsInverted() {
if oi.IsInverted() {
return oi.Lo >= i.Lo && oi.Hi <= i.Hi
}
return (oi.Lo >= i.Lo || oi.Hi <= i.Hi) && !i.IsEmpty()
}
if oi.IsInverted() {
return i.IsFull() || oi.IsEmpty()
}
return oi.Lo >= i.Lo && oi.Hi <= i.Hi
}
// InteriorContains returns true iff the interior of the interval contains p.
// Assumes p ∈ [-π,π].
func (i Interval) InteriorContains(p float64) bool {
if p == -math.Pi {
p = math.Pi
}
if i.IsInverted() {
return p > i.Lo || p < i.Hi
}
return (p > i.Lo && p < i.Hi) || i.IsFull()
}
// InteriorContainsInterval returns true iff the interior of the interval contains oi.
func (i Interval) InteriorContainsInterval(oi Interval) bool {
if i.IsInverted() {
if oi.IsInverted() {
return (oi.Lo > i.Lo && oi.Hi < i.Hi) || oi.IsEmpty()
}
return oi.Lo > i.Lo || oi.Hi < i.Hi
}
if oi.IsInverted() {
return i.IsFull() || oi.IsEmpty()
}
return (oi.Lo > i.Lo && oi.Hi < i.Hi) || i.IsFull()
}
// Intersects returns true iff the interval contains any points in common with oi.
func (i Interval) Intersects(oi Interval) bool {
if i.IsEmpty() || oi.IsEmpty() {
return false
}
if i.IsInverted() {
return oi.IsInverted() || oi.Lo <= i.Hi || oi.Hi >= i.Lo
}
if oi.IsInverted() {
return oi.Lo <= i.Hi || oi.Hi >= i.Lo
}
return oi.Lo <= i.Hi && oi.Hi >= i.Lo
}
// InteriorIntersects returns true iff the interior of the interval contains any points in common with oi, including the latter's boundary.
func (i Interval) InteriorIntersects(oi Interval) bool {
if i.IsEmpty() || oi.IsEmpty() || i.Lo == i.Hi {
return false
}
if i.IsInverted() {
return oi.IsInverted() || oi.Lo < i.Hi || oi.Hi > i.Lo
}
if oi.IsInverted() {
return oi.Lo < i.Hi || oi.Hi > i.Lo
}
return (oi.Lo < i.Hi && oi.Hi > i.Lo) || i.IsFull()
}
// Compute distance from a to b in [0,2π], in a numerically stable way.
func positiveDistance(a, b float64) float64 {
d := b - a
if d >= 0 {
return d
}
return (b + math.Pi) - (a - math.Pi)
}
// Union returns the smallest interval that contains both the interval and oi.
func (i Interval) Union(oi Interval) Interval {
if oi.IsEmpty() {
return i
}
if i.fastContains(oi.Lo) {
if i.fastContains(oi.Hi) {
// Either oi ⊂ i, or i oi is the full interval.
if i.ContainsInterval(oi) {
return i
}
return FullInterval()
}
return Interval{i.Lo, oi.Hi}
}
if i.fastContains(oi.Hi) {
return Interval{oi.Lo, i.Hi}
}
// Neither endpoint of oi is in i. Either i ⊂ oi, or i and oi are disjoint.
if i.IsEmpty() || oi.fastContains(i.Lo) {
return oi
}
// This is the only hard case where we need to find the closest pair of endpoints.
if positiveDistance(oi.Hi, i.Lo) < positiveDistance(i.Hi, oi.Lo) {
return Interval{oi.Lo, i.Hi}
}
return Interval{i.Lo, oi.Hi}
}
// Intersection returns the smallest interval that contains the intersection of the interval and oi.
func (i Interval) Intersection(oi Interval) Interval {
if oi.IsEmpty() {
return EmptyInterval()
}
if i.fastContains(oi.Lo) {
if i.fastContains(oi.Hi) {
// Either oi ⊂ i, or i and oi intersect twice. Neither are empty.
// In the first case we want to return i (which is shorter than oi).
// In the second case one of them is inverted, and the smallest interval
// that covers the two disjoint pieces is the shorter of i and oi.
// We thus want to pick the shorter of i and oi in both cases.
if oi.Length() < i.Length() {
return oi
}
return i
}
return Interval{oi.Lo, i.Hi}
}
if i.fastContains(oi.Hi) {
return Interval{i.Lo, oi.Hi}
}
// Neither endpoint of oi is in i. Either i ⊂ oi, or i and oi are disjoint.
if oi.fastContains(i.Lo) {
return i
}
return EmptyInterval()
}
// AddPoint returns the interval expanded by the minimum amount necessary such
// that it contains the given point "p" (an angle in the range [-Pi, Pi]).
func (i Interval) AddPoint(p float64) Interval {
if math.Abs(p) > math.Pi {
return i
}
if p == -math.Pi {
p = math.Pi
}
if i.fastContains(p) {
return i
}
if i.IsEmpty() {
return Interval{p, p}
}
if positiveDistance(p, i.Lo) < positiveDistance(i.Hi, p) {
return Interval{p, i.Hi}
}
return Interval{i.Lo, p}
}
// Define the maximum rounding error for arithmetic operations. Depending on the
// platform the mantissa precision may be different than others, so we choose to
// use specific values to be consistent across all.
// The values come from the C++ implementation.
var (
// epsilon is a small number that represents a reasonable level of noise between two
// values that can be considered to be equal.
epsilon = 1e-15
// dblEpsilon is a smaller number for values that require more precision.
dblEpsilon = 2.220446049e-16
)
// Expanded returns an interval that has been expanded on each side by margin.
// If margin is negative, then the function shrinks the interval on
// each side by margin instead. The resulting interval may be empty or
// full. Any expansion (positive or negative) of a full interval remains
// full, and any expansion of an empty interval remains empty.
func (i Interval) Expanded(margin float64) Interval {
if margin >= 0 {
if i.IsEmpty() {
return i
}
// Check whether this interval will be full after expansion, allowing
// for a rounding error when computing each endpoint.
if i.Length()+2*margin+2*dblEpsilon >= 2*math.Pi {
return FullInterval()
}
} else {
if i.IsFull() {
return i
}
// Check whether this interval will be empty after expansion, allowing
// for a rounding error when computing each endpoint.
if i.Length()+2*margin-2*dblEpsilon <= 0 {
return EmptyInterval()
}
}
result := IntervalFromEndpoints(
math.Remainder(i.Lo-margin, 2*math.Pi),
math.Remainder(i.Hi+margin, 2*math.Pi),
)
if result.Lo <= -math.Pi {
result.Lo = math.Pi
}
return result
}
func (i Interval) String() string {
// like "[%.7f, %.7f]"
return "[" + strconv.FormatFloat(i.Lo, 'f', 7, 64) + ", " + strconv.FormatFloat(i.Hi, 'f', 7, 64) + "]"
}
// BUG(dsymonds): The major differences from the C++ version are:
// - no validity checking on construction, etc. (not a bug?)
// - a few operations

468
vendor/github.com/golang/geo/s2/cap.go generated vendored Normal file
View file

@ -0,0 +1,468 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s2
import (
"fmt"
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/s1"
)
var (
// centerPoint is the default center for Caps
centerPoint = PointFromCoords(1.0, 0, 0)
)
// Cap represents a disc-shaped region defined by a center and radius.
// Technically this shape is called a "spherical cap" (rather than disc)
// because it is not planar; the cap represents a portion of the sphere that
// has been cut off by a plane. The boundary of the cap is the circle defined
// by the intersection of the sphere and the plane. For containment purposes,
// the cap is a closed set, i.e. it contains its boundary.
//
// For the most part, you can use a spherical cap wherever you would use a
// disc in planar geometry. The radius of the cap is measured along the
// surface of the sphere (rather than the straight-line distance through the
// interior). Thus a cap of radius π/2 is a hemisphere, and a cap of radius
// π covers the entire sphere.
//
// The center is a point on the surface of the unit sphere. (Hence the need for
// it to be of unit length.)
//
// A cap can also be defined by its center point and height. The height is the
// distance from the center point to the cutoff plane. There is also support for
// "empty" and "full" caps, which contain no points and all points respectively.
//
// Here are some useful relationships between the cap height (h), the cap
// radius (r), the maximum chord length from the cap's center (d), and the
// radius of cap's base (a).
//
// h = 1 - cos(r)
// = 2 * sin^2(r/2)
// d^2 = 2 * h
// = a^2 + h^2
//
// The zero value of Cap is an invalid cap. Use EmptyCap to get a valid empty cap.
type Cap struct {
center Point
radius s1.ChordAngle
}
// CapFromPoint constructs a cap containing a single point.
func CapFromPoint(p Point) Cap {
return CapFromCenterChordAngle(p, 0)
}
// CapFromCenterAngle constructs a cap with the given center and angle.
func CapFromCenterAngle(center Point, angle s1.Angle) Cap {
return CapFromCenterChordAngle(center, s1.ChordAngleFromAngle(angle))
}
// CapFromCenterChordAngle constructs a cap where the angle is expressed as an
// s1.ChordAngle. This constructor is more efficient than using an s1.Angle.
func CapFromCenterChordAngle(center Point, radius s1.ChordAngle) Cap {
return Cap{
center: center,
radius: radius,
}
}
// CapFromCenterHeight constructs a cap with the given center and height. A
// negative height yields an empty cap; a height of 2 or more yields a full cap.
// The center should be unit length.
func CapFromCenterHeight(center Point, height float64) Cap {
return CapFromCenterChordAngle(center, s1.ChordAngleFromSquaredLength(2*height))
}
// CapFromCenterArea constructs a cap with the given center and surface area.
// Note that the area can also be interpreted as the solid angle subtended by the
// cap (because the sphere has unit radius). A negative area yields an empty cap;
// an area of 4*π or more yields a full cap.
func CapFromCenterArea(center Point, area float64) Cap {
return CapFromCenterChordAngle(center, s1.ChordAngleFromSquaredLength(area/math.Pi))
}
// EmptyCap returns a cap that contains no points.
func EmptyCap() Cap {
return CapFromCenterChordAngle(centerPoint, s1.NegativeChordAngle)
}
// FullCap returns a cap that contains all points.
func FullCap() Cap {
return CapFromCenterChordAngle(centerPoint, s1.StraightChordAngle)
}
// IsValid reports whether the Cap is considered valid.
func (c Cap) IsValid() bool {
return c.center.Vector.IsUnit() && c.radius <= s1.StraightChordAngle
}
// IsEmpty reports whether the cap is empty, i.e. it contains no points.
func (c Cap) IsEmpty() bool {
return c.radius < 0
}
// IsFull reports whether the cap is full, i.e. it contains all points.
func (c Cap) IsFull() bool {
return c.radius == s1.StraightChordAngle
}
// Center returns the cap's center point.
func (c Cap) Center() Point {
return c.center
}
// Height returns the height of the cap. This is the distance from the center
// point to the cutoff plane.
func (c Cap) Height() float64 {
return float64(0.5 * c.radius)
}
// Radius returns the cap radius as an s1.Angle. (Note that the cap angle
// is stored internally as a ChordAngle, so this method requires a trigonometric
// operation and may yield a slightly different result than the value passed
// to CapFromCenterAngle).
func (c Cap) Radius() s1.Angle {
return c.radius.Angle()
}
// Area returns the surface area of the Cap on the unit sphere.
func (c Cap) Area() float64 {
return 2.0 * math.Pi * math.Max(0, c.Height())
}
// Contains reports whether this cap contains the other.
func (c Cap) Contains(other Cap) bool {
// In a set containment sense, every cap contains the empty cap.
if c.IsFull() || other.IsEmpty() {
return true
}
return c.radius >= ChordAngleBetweenPoints(c.center, other.center).Add(other.radius)
}
// Intersects reports whether this cap intersects the other cap.
// i.e. whether they have any points in common.
func (c Cap) Intersects(other Cap) bool {
if c.IsEmpty() || other.IsEmpty() {
return false
}
return c.radius.Add(other.radius) >= ChordAngleBetweenPoints(c.center, other.center)
}
// InteriorIntersects reports whether this caps interior intersects the other cap.
func (c Cap) InteriorIntersects(other Cap) bool {
// Make sure this cap has an interior and the other cap is non-empty.
if c.radius <= 0 || other.IsEmpty() {
return false
}
return c.radius.Add(other.radius) > ChordAngleBetweenPoints(c.center, other.center)
}
// ContainsPoint reports whether this cap contains the point.
func (c Cap) ContainsPoint(p Point) bool {
return ChordAngleBetweenPoints(c.center, p) <= c.radius
}
// InteriorContainsPoint reports whether the point is within the interior of this cap.
func (c Cap) InteriorContainsPoint(p Point) bool {
return c.IsFull() || ChordAngleBetweenPoints(c.center, p) < c.radius
}
// Complement returns the complement of the interior of the cap. A cap and its
// complement have the same boundary but do not share any interior points.
// The complement operator is not a bijection because the complement of a
// singleton cap (containing a single point) is the same as the complement
// of an empty cap.
func (c Cap) Complement() Cap {
if c.IsFull() {
return EmptyCap()
}
if c.IsEmpty() {
return FullCap()
}
return CapFromCenterChordAngle(Point{c.center.Mul(-1)}, s1.StraightChordAngle.Sub(c.radius))
}
// CapBound returns a bounding spherical cap. This is not guaranteed to be exact.
func (c Cap) CapBound() Cap {
return c
}
// RectBound returns a bounding latitude-longitude rectangle.
// The bounds are not guaranteed to be tight.
func (c Cap) RectBound() Rect {
if c.IsEmpty() {
return EmptyRect()
}
capAngle := c.Radius().Radians()
allLongitudes := false
lat := r1.Interval{
Lo: latitude(c.center).Radians() - capAngle,
Hi: latitude(c.center).Radians() + capAngle,
}
lng := s1.FullInterval()
// Check whether cap includes the south pole.
if lat.Lo <= -math.Pi/2 {
lat.Lo = -math.Pi / 2
allLongitudes = true
}
// Check whether cap includes the north pole.
if lat.Hi >= math.Pi/2 {
lat.Hi = math.Pi / 2
allLongitudes = true
}
if !allLongitudes {
// Compute the range of longitudes covered by the cap. We use the law
// of sines for spherical triangles. Consider the triangle ABC where
// A is the north pole, B is the center of the cap, and C is the point
// of tangency between the cap boundary and a line of longitude. Then
// C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
// we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
// Here "a" is the cap angle, and "c" is the colatitude (90 degrees
// minus the latitude). This formula also works for negative latitudes.
//
// The formula for sin(a) follows from the relationship h = 1 - cos(a).
sinA := c.radius.Sin()
sinC := math.Cos(latitude(c.center).Radians())
if sinA <= sinC {
angleA := math.Asin(sinA / sinC)
lng.Lo = math.Remainder(longitude(c.center).Radians()-angleA, math.Pi*2)
lng.Hi = math.Remainder(longitude(c.center).Radians()+angleA, math.Pi*2)
}
}
return Rect{lat, lng}
}
// Equal reports whether this cap is equal to the other cap.
func (c Cap) Equal(other Cap) bool {
return (c.radius == other.radius && c.center == other.center) ||
(c.IsEmpty() && other.IsEmpty()) ||
(c.IsFull() && other.IsFull())
}
// ApproxEqual reports whether this cap is equal to the other cap within the given tolerance.
func (c Cap) ApproxEqual(other Cap) bool {
const epsilon = 1e-14
r2 := float64(c.radius)
otherR2 := float64(other.radius)
return c.center.ApproxEqual(other.center) &&
math.Abs(r2-otherR2) <= epsilon ||
c.IsEmpty() && otherR2 <= epsilon ||
other.IsEmpty() && r2 <= epsilon ||
c.IsFull() && otherR2 >= 2-epsilon ||
other.IsFull() && r2 >= 2-epsilon
}
// AddPoint increases the cap if necessary to include the given point. If this cap is empty,
// then the center is set to the point with a zero height. p must be unit-length.
func (c Cap) AddPoint(p Point) Cap {
if c.IsEmpty() {
c.center = p
c.radius = 0
return c
}
// After calling cap.AddPoint(p), cap.Contains(p) must be true. However
// we don't need to do anything special to achieve this because Contains()
// does exactly the same distance calculation that we do here.
if newRad := ChordAngleBetweenPoints(c.center, p); newRad > c.radius {
c.radius = newRad
}
return c
}
// AddCap increases the cap height if necessary to include the other cap. If this cap is empty,
// it is set to the other cap.
func (c Cap) AddCap(other Cap) Cap {
if c.IsEmpty() {
return other
}
if other.IsEmpty() {
return c
}
// We round up the distance to ensure that the cap is actually contained.
// TODO(roberts): Do some error analysis in order to guarantee this.
dist := ChordAngleBetweenPoints(c.center, other.center).Add(other.radius)
if newRad := dist.Expanded(dblEpsilon * float64(dist)); newRad > c.radius {
c.radius = newRad
}
return c
}
// Expanded returns a new cap expanded by the given angle. If the cap is empty,
// it returns an empty cap.
func (c Cap) Expanded(distance s1.Angle) Cap {
if c.IsEmpty() {
return EmptyCap()
}
return CapFromCenterChordAngle(c.center, c.radius.Add(s1.ChordAngleFromAngle(distance)))
}
func (c Cap) String() string {
return fmt.Sprintf("[Center=%v, Radius=%f]", c.center.Vector, c.Radius().Degrees())
}
// radiusToHeight converts an s1.Angle into the height of the cap.
func radiusToHeight(r s1.Angle) float64 {
if r.Radians() < 0 {
return float64(s1.NegativeChordAngle)
}
if r.Radians() >= math.Pi {
return float64(s1.RightChordAngle)
}
return float64(0.5 * s1.ChordAngleFromAngle(r))
}
// ContainsCell reports whether the cap contains the given cell.
func (c Cap) ContainsCell(cell Cell) bool {
// If the cap does not contain all cell vertices, return false.
var vertices [4]Point
for k := 0; k < 4; k++ {
vertices[k] = cell.Vertex(k)
if !c.ContainsPoint(vertices[k]) {
return false
}
}
// Otherwise, return true if the complement of the cap does not intersect the cell.
return !c.Complement().intersects(cell, vertices)
}
// IntersectsCell reports whether the cap intersects the cell.
func (c Cap) IntersectsCell(cell Cell) bool {
// If the cap contains any cell vertex, return true.
var vertices [4]Point
for k := 0; k < 4; k++ {
vertices[k] = cell.Vertex(k)
if c.ContainsPoint(vertices[k]) {
return true
}
}
return c.intersects(cell, vertices)
}
// intersects reports whether the cap intersects any point of the cell excluding
// its vertices (which are assumed to already have been checked).
func (c Cap) intersects(cell Cell, vertices [4]Point) bool {
// If the cap is a hemisphere or larger, the cell and the complement of the cap
// are both convex. Therefore since no vertex of the cell is contained, no other
// interior point of the cell is contained either.
if c.radius >= s1.RightChordAngle {
return false
}
// We need to check for empty caps due to the center check just below.
if c.IsEmpty() {
return false
}
// Optimization: return true if the cell contains the cap center. This allows half
// of the edge checks below to be skipped.
if cell.ContainsPoint(c.center) {
return true
}
// At this point we know that the cell does not contain the cap center, and the cap
// does not contain any cell vertex. The only way that they can intersect is if the
// cap intersects the interior of some edge.
sin2Angle := c.radius.Sin2()
for k := 0; k < 4; k++ {
edge := cell.Edge(k).Vector
dot := c.center.Vector.Dot(edge)
if dot > 0 {
// The center is in the interior half-space defined by the edge. We do not need
// to consider these edges, since if the cap intersects this edge then it also
// intersects the edge on the opposite side of the cell, because the center is
// not contained with the cell.
continue
}
// The Norm2() factor is necessary because "edge" is not normalized.
if dot*dot > sin2Angle*edge.Norm2() {
return false
}
// Otherwise, the great circle containing this edge intersects the interior of the cap. We just
// need to check whether the point of closest approach occurs between the two edge endpoints.
dir := edge.Cross(c.center.Vector)
if dir.Dot(vertices[k].Vector) < 0 && dir.Dot(vertices[(k+1)&3].Vector) > 0 {
return true
}
}
return false
}
// Centroid returns the true centroid of the cap multiplied by its surface area
// The result lies on the ray from the origin through the cap's center, but it
// is not unit length. Note that if you just want the "surface centroid", i.e.
// the normalized result, then it is simpler to call Center.
//
// The reason for multiplying the result by the cap area is to make it
// easier to compute the centroid of more complicated shapes. The centroid
// of a union of disjoint regions can be computed simply by adding their
// Centroid() results. Caveat: for caps that contain a single point
// (i.e., zero radius), this method always returns the origin (0, 0, 0).
// This is because shapes with no area don't affect the centroid of a
// union whose total area is positive.
func (c Cap) Centroid() Point {
// From symmetry, the centroid of the cap must be somewhere on the line
// from the origin to the center of the cap on the surface of the sphere.
// When a sphere is divided into slices of constant thickness by a set of
// parallel planes, all slices have the same surface area. This implies
// that the radial component of the centroid is simply the midpoint of the
// range of radial distances spanned by the cap. That is easily computed
// from the cap height.
if c.IsEmpty() {
return Point{}
}
r := 1 - 0.5*c.Height()
return Point{c.center.Mul(r * c.Area())}
}
// Union returns the smallest cap which encloses this cap and other.
func (c Cap) Union(other Cap) Cap {
// If the other cap is larger, swap c and other for the rest of the computations.
if c.radius < other.radius {
c, other = other, c
}
if c.IsFull() || other.IsEmpty() {
return c
}
// TODO: This calculation would be more efficient using s1.ChordAngles.
cRadius := c.Radius()
otherRadius := other.Radius()
distance := c.center.Distance(other.center)
if cRadius >= distance+otherRadius {
return c
}
resRadius := 0.5 * (distance + cRadius + otherRadius)
resCenter := InterpolateAtDistance(0.5*(distance-cRadius+otherRadius), c.center, other.center)
return CapFromCenterAngle(resCenter, resRadius)
}

385
vendor/github.com/golang/geo/s2/cell.go generated vendored Normal file
View file

@ -0,0 +1,385 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s2
import (
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
"github.com/golang/geo/s1"
)
// Cell is an S2 region object that represents a cell. Unlike CellIDs,
// it supports efficient containment and intersection tests. However, it is
// also a more expensive representation.
type Cell struct {
face int8
level int8
orientation int8
id CellID
uv r2.Rect
}
// CellFromCellID constructs a Cell corresponding to the given CellID.
func CellFromCellID(id CellID) Cell {
c := Cell{}
c.id = id
f, i, j, o := c.id.faceIJOrientation()
c.face = int8(f)
c.level = int8(c.id.Level())
c.orientation = int8(o)
c.uv = ijLevelToBoundUV(i, j, int(c.level))
return c
}
// CellFromPoint constructs a cell for the given Point.
func CellFromPoint(p Point) Cell {
return CellFromCellID(cellIDFromPoint(p))
}
// CellFromLatLng constructs a cell for the given LatLng.
func CellFromLatLng(ll LatLng) Cell {
return CellFromCellID(CellIDFromLatLng(ll))
}
// Face returns the face this cell is on.
func (c Cell) Face() int {
return int(c.face)
}
// Level returns the level of this cell.
func (c Cell) Level() int {
return int(c.level)
}
// ID returns the CellID this cell represents.
func (c Cell) ID() CellID {
return c.id
}
// IsLeaf returns whether this Cell is a leaf or not.
func (c Cell) IsLeaf() bool {
return c.level == maxLevel
}
// SizeIJ returns the CellID value for the cells level.
func (c Cell) SizeIJ() int {
return sizeIJ(int(c.level))
}
// Vertex returns the k-th vertex of the cell (k = 0,1,2,3) in CCW order
// (lower left, lower right, upper right, upper left in the UV plane).
func (c Cell) Vertex(k int) Point {
return Point{faceUVToXYZ(int(c.face), c.uv.Vertices()[k].X, c.uv.Vertices()[k].Y).Normalize()}
}
// Edge returns the inward-facing normal of the great circle passing through
// the CCW ordered edge from vertex k to vertex k+1 (mod 4) (for k = 0,1,2,3).
func (c Cell) Edge(k int) Point {
switch k {
case 0:
return Point{vNorm(int(c.face), c.uv.Y.Lo).Normalize()} // Bottom
case 1:
return Point{uNorm(int(c.face), c.uv.X.Hi).Normalize()} // Right
case 2:
return Point{vNorm(int(c.face), c.uv.Y.Hi).Mul(-1.0).Normalize()} // Top
default:
return Point{uNorm(int(c.face), c.uv.X.Lo).Mul(-1.0).Normalize()} // Left
}
}
// BoundUV returns the bounds of this cell in (u,v)-space.
func (c Cell) BoundUV() r2.Rect {
return c.uv
}
// Center returns the direction vector corresponding to the center in
// (s,t)-space of the given cell. This is the point at which the cell is
// divided into four subcells; it is not necessarily the centroid of the
// cell in (u,v)-space or (x,y,z)-space
func (c Cell) Center() Point {
return Point{c.id.rawPoint().Normalize()}
}
// Children returns the four direct children of this cell in traversal order
// and returns true. If this is a leaf cell, or the children could not be created,
// false is returned.
// The C++ method is called Subdivide.
func (c Cell) Children() ([4]Cell, bool) {
var children [4]Cell
if c.id.IsLeaf() {
return children, false
}
// Compute the cell midpoint in uv-space.
uvMid := c.id.centerUV()
// Create four children with the appropriate bounds.
cid := c.id.ChildBegin()
for pos := 0; pos < 4; pos++ {
children[pos] = Cell{
face: c.face,
level: c.level + 1,
orientation: c.orientation ^ int8(posToOrientation[pos]),
id: cid,
}
// We want to split the cell in half in u and v. To decide which
// side to set equal to the midpoint value, we look at cell's (i,j)
// position within its parent. The index for i is in bit 1 of ij.
ij := posToIJ[c.orientation][pos]
i := ij >> 1
j := ij & 1
if i == 1 {
children[pos].uv.X.Hi = c.uv.X.Hi
children[pos].uv.X.Lo = uvMid.X
} else {
children[pos].uv.X.Lo = c.uv.X.Lo
children[pos].uv.X.Hi = uvMid.X
}
if j == 1 {
children[pos].uv.Y.Hi = c.uv.Y.Hi
children[pos].uv.Y.Lo = uvMid.Y
} else {
children[pos].uv.Y.Lo = c.uv.Y.Lo
children[pos].uv.Y.Hi = uvMid.Y
}
cid = cid.Next()
}
return children, true
}
// ExactArea returns the area of this cell as accurately as possible.
func (c Cell) ExactArea() float64 {
v0, v1, v2, v3 := c.Vertex(0), c.Vertex(1), c.Vertex(2), c.Vertex(3)
return PointArea(v0, v1, v2) + PointArea(v0, v2, v3)
}
// ApproxArea returns the approximate area of this cell. This method is accurate
// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
// at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
// surface). It is moderately cheap to compute.
func (c Cell) ApproxArea() float64 {
// All cells at the first two levels have the same area.
if c.level < 2 {
return c.AverageArea()
}
// First, compute the approximate area of the cell when projected
// perpendicular to its normal. The cross product of its diagonals gives
// the normal, and the length of the normal is twice the projected area.
flatArea := 0.5 * (c.Vertex(2).Sub(c.Vertex(0).Vector).
Cross(c.Vertex(3).Sub(c.Vertex(1).Vector)).Norm())
// Now, compensate for the curvature of the cell surface by pretending
// that the cell is shaped like a spherical cap. The ratio of the
// area of a spherical cap to the area of its projected disc turns out
// to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disc.
// For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
// Here we set Pi*r*r == flatArea to find the equivalent disc.
return flatArea * 2 / (1 + math.Sqrt(1-math.Min(1/math.Pi*flatArea, 1)))
}
// AverageArea returns the average area of cells at the level of this cell.
// This is accurate to within a factor of 1.7.
func (c Cell) AverageArea() float64 {
return AvgAreaMetric.Value(int(c.level))
}
// IntersectsCell reports whether the intersection of this cell and the other cell is not nil.
func (c Cell) IntersectsCell(oc Cell) bool {
return c.id.Intersects(oc.id)
}
// ContainsCell reports whether this cell contains the other cell.
func (c Cell) ContainsCell(oc Cell) bool {
return c.id.Contains(oc.id)
}
// latitude returns the latitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
func (c Cell) latitude(i, j int) float64 {
var u, v float64
switch {
case i == 0 && j == 0:
u = c.uv.X.Lo
v = c.uv.Y.Lo
case i == 0 && j == 1:
u = c.uv.X.Lo
v = c.uv.Y.Hi
case i == 1 && j == 0:
u = c.uv.X.Hi
v = c.uv.Y.Lo
case i == 1 && j == 1:
u = c.uv.X.Hi
v = c.uv.Y.Hi
default:
panic("i and/or j is out of bound")
}
return latitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}
// longitude returns the longitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
func (c Cell) longitude(i, j int) float64 {
var u, v float64
switch {
case i == 0 && j == 0:
u = c.uv.X.Lo
v = c.uv.Y.Lo
case i == 0 && j == 1:
u = c.uv.X.Lo
v = c.uv.Y.Hi
case i == 1 && j == 0:
u = c.uv.X.Hi
v = c.uv.Y.Lo
case i == 1 && j == 1:
u = c.uv.X.Hi
v = c.uv.Y.Hi
default:
panic("i and/or j is out of bound")
}
return longitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}
var (
poleMinLat = math.Asin(math.Sqrt(1.0/3)) - 0.5*dblEpsilon
)
// RectBound returns the bounding rectangle of this cell.
func (c Cell) RectBound() Rect {
if c.level > 0 {
// Except for cells at level 0, the latitude and longitude extremes are
// attained at the vertices. Furthermore, the latitude range is
// determined by one pair of diagonally opposite vertices and the
// longitude range is determined by the other pair.
//
// We first determine which corner (i,j) of the cell has the largest
// absolute latitude. To maximize latitude, we want to find the point in
// the cell that has the largest absolute z-coordinate and the smallest
// absolute x- and y-coordinates. To do this we look at each coordinate
// (u and v), and determine whether we want to minimize or maximize that
// coordinate based on the axis direction and the cell's (u,v) quadrant.
u := c.uv.X.Lo + c.uv.X.Hi
v := c.uv.Y.Lo + c.uv.Y.Hi
var i, j int
if uAxis(int(c.face)).Z == 0 {
if u < 0 {
i = 1
}
} else if u > 0 {
i = 1
}
if vAxis(int(c.face)).Z == 0 {
if v < 0 {
j = 1
}
} else if v > 0 {
j = 1
}
lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))
// We grow the bounds slightly to make sure that the bounding rectangle
// contains LatLngFromPoint(P) for any point P inside the loop L defined by the
// four *normalized* vertices. Note that normalization of a vector can
// change its direction by up to 0.5 * dblEpsilon radians, and it is not
// enough just to add Normalize calls to the code above because the
// latitude/longitude ranges are not necessarily determined by diagonally
// opposite vertex pairs after normalization.
//
// We would like to bound the amount by which the latitude/longitude of a
// contained point P can exceed the bounds computed above. In the case of
// longitude, the normalization error can change the direction of rounding
// leading to a maximum difference in longitude of 2 * dblEpsilon. In
// the case of latitude, the normalization error can shift the latitude by
// up to 0.5 * dblEpsilon and the other sources of error can cause the
// two latitudes to differ by up to another 1.5 * dblEpsilon, which also
// leads to a maximum difference of 2 * dblEpsilon.
return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
}
// The 4 cells around the equator extend to +/-45 degrees latitude at the
// midpoints of their top and bottom edges. The two cells covering the
// poles extend down to +/-35.26 degrees at their vertices. The maximum
// error in this calculation is 0.5 * dblEpsilon.
var bound Rect
switch c.face {
case 0:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
case 1:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
case 2:
bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
case 3:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
case 4:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
default:
bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
}
// Finally, we expand the bound to account for the error when a point P is
// converted to an LatLng to test for containment. (The bound should be
// large enough so that it contains the computed LatLng of any contained
// point, not just the infinite-precision version.) We don't need to expand
// longitude because longitude is calculated via a single call to math.Atan2,
// which is guaranteed to be semi-monotonic.
return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
}
// CapBound returns the bounding cap of this cell.
func (c Cell) CapBound() Cap {
// We use the cell center in (u,v)-space as the cap axis. This vector is very close
// to GetCenter() and faster to compute. Neither one of these vectors yields the
// bounding cap with minimal surface area, but they are both pretty close.
cap := CapFromPoint(Point{faceUVToXYZ(int(c.face), c.uv.Center().X, c.uv.Center().Y).Normalize()})
for k := 0; k < 4; k++ {
cap = cap.AddPoint(c.Vertex(k))
}
return cap
}
// ContainsPoint reports whether this cell contains the given point. Note that
// unlike Loop/Polygon, a Cell is considered to be a closed set. This means
// that a point on a Cell's edge or vertex belong to the Cell and the relevant
// adjacent Cells too.
//
// If you want every point to be contained by exactly one Cell,
// you will need to convert the Cell to a Loop.
func (c Cell) ContainsPoint(p Point) bool {
var uv r2.Point
var ok bool
if uv.X, uv.Y, ok = faceXYZToUV(int(c.face), p); !ok {
return false
}
// Expand the (u,v) bound to ensure that
//
// CellFromPoint(p).ContainsPoint(p)
//
// is always true. To do this, we need to account for the error when
// converting from (u,v) coordinates to (s,t) coordinates. In the
// normal case the total error is at most dblEpsilon.
return c.uv.ExpandedByMargin(dblEpsilon).ContainsPoint(uv)
}
// BUG(roberts): Differences from C++:
// Subdivide
// BoundUV
// Distance/DistanceToEdge
// VertexChordDistance

889
vendor/github.com/golang/geo/s2/cellid.go generated vendored Normal file
View file

@ -0,0 +1,889 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s2
import (
"bytes"
"fmt"
"math"
"strconv"
"strings"
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// CellID uniquely identifies a cell in the S2 cell decomposition.
// The most significant 3 bits encode the face number (0-5). The
// remaining 61 bits encode the position of the center of this cell
// along the Hilbert curve on that face. The zero value and the value
// (1<<64)-1 are invalid cell IDs. The first compares less than any
// valid cell ID, the second as greater than any valid cell ID.
//
// Sequentially increasing cell IDs follow a continuous space-filling curve
// over the entire sphere. They have the following properties:
//
// - The ID of a cell at level k consists of a 3-bit face number followed
// by k bit pairs that recursively select one of the four children of
// each cell. The next bit is always 1, and all other bits are 0.
// Therefore, the level of a cell is determined by the position of its
// lowest-numbered bit that is turned on (for a cell at level k, this
// position is 2 * (maxLevel - k)).
//
// - The ID of a parent cell is at the midpoint of the range of IDs spanned
// by its children (or by its descendants at any level).
//
// Leaf cells are often used to represent points on the unit sphere, and
// this type provides methods for converting directly between these two
// representations. For cells that represent 2D regions rather than
// discrete point, it is better to use Cells.
type CellID uint64
// TODO(dsymonds): Some of these constants should probably be exported.
const (
faceBits = 3
numFaces = 6
maxLevel = 30
// The extra position bit (61 rather than 60) lets us encode each cell as its
// Hilbert curve position at the cell center (which is halfway along the
// portion of the Hilbert curve that fills that cell).
posBits = 2*maxLevel + 1
maxSize = 1 << maxLevel
wrapOffset = uint64(numFaces) << posBits
)
// CellIDFromFacePosLevel returns a cell given its face in the range
// [0,5], the 61-bit Hilbert curve position pos within that face, and
// the level in the range [0,maxLevel]. The position in the cell ID
// will be truncated to correspond to the Hilbert curve position at
// the center of the returned cell.
func CellIDFromFacePosLevel(face int, pos uint64, level int) CellID {
return CellID(uint64(face)<<posBits + pos | 1).Parent(level)
}
// CellIDFromFace returns the cell corresponding to a given S2 cube face.
func CellIDFromFace(face int) CellID {
return CellID((uint64(face) << posBits) + lsbForLevel(0))
}
// CellIDFromLatLng returns the leaf cell containing ll.
func CellIDFromLatLng(ll LatLng) CellID {
return cellIDFromPoint(PointFromLatLng(ll))
}
// CellIDFromToken returns a cell given a hex-encoded string of its uint64 ID.
func CellIDFromToken(s string) CellID {
if len(s) > 16 {
return CellID(0)
}
n, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return CellID(0)
}
// Equivalent to right-padding string with zeros to 16 characters.
if len(s) < 16 {
n = n << (4 * uint(16-len(s)))
}
return CellID(n)
}
// ToToken returns a hex-encoded string of the uint64 cell id, with leading
// zeros included but trailing zeros stripped.
func (ci CellID) ToToken() string {
s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
if len(s) == 0 {
return "X"
}
return s
}
// IsValid reports whether ci represents a valid cell.
func (ci CellID) IsValid() bool {
return ci.Face() < numFaces && (ci.lsb()&0x1555555555555555 != 0)
}
// Face returns the cube face for this cell ID, in the range [0,5].
func (ci CellID) Face() int { return int(uint64(ci) >> posBits) }
// Pos returns the position along the Hilbert curve of this cell ID, in the range [0,2^posBits-1].
func (ci CellID) Pos() uint64 { return uint64(ci) & (^uint64(0) >> faceBits) }
// Level returns the subdivision level of this cell ID, in the range [0, maxLevel].
func (ci CellID) Level() int {
return maxLevel - findLSBSetNonZero64(uint64(ci))>>1
}
// IsLeaf returns whether this cell ID is at the deepest level;
// that is, the level at which the cells are smallest.
func (ci CellID) IsLeaf() bool { return uint64(ci)&1 != 0 }
// ChildPosition returns the child position (0..3) of this cell's
// ancestor at the given level, relative to its parent. The argument
// should be in the range 1..kMaxLevel. For example,
// ChildPosition(1) returns the position of this cell's level-1
// ancestor within its top-level face cell.
func (ci CellID) ChildPosition(level int) int {
return int(uint64(ci)>>uint64(2*(maxLevel-level)+1)) & 3
}
// lsbForLevel returns the lowest-numbered bit that is on for cells at the given level.
func lsbForLevel(level int) uint64 { return 1 << uint64(2*(maxLevel-level)) }
// Parent returns the cell at the given level, which must be no greater than the current level.
func (ci CellID) Parent(level int) CellID {
lsb := lsbForLevel(level)
return CellID((uint64(ci) & -lsb) | lsb)
}
// immediateParent is cheaper than Parent, but assumes !ci.isFace().
func (ci CellID) immediateParent() CellID {
nlsb := CellID(ci.lsb() << 2)
return (ci & -nlsb) | nlsb
}
// isFace returns whether this is a top-level (face) cell.
func (ci CellID) isFace() bool { return uint64(ci)&(lsbForLevel(0)-1) == 0 }
// lsb returns the least significant bit that is set.
func (ci CellID) lsb() uint64 { return uint64(ci) & -uint64(ci) }
// Children returns the four immediate children of this cell.
// If ci is a leaf cell, it returns four identical cells that are not the children.
func (ci CellID) Children() [4]CellID {
var ch [4]CellID
lsb := CellID(ci.lsb())
ch[0] = ci - lsb + lsb>>2
lsb >>= 1
ch[1] = ch[0] + lsb
ch[2] = ch[1] + lsb
ch[3] = ch[2] + lsb
return ch
}
func sizeIJ(level int) int {
return 1 << uint(maxLevel-level)
}
// EdgeNeighbors returns the four cells that are adjacent across the cell's four edges.
// Edges 0, 1, 2, 3 are in the down, right, up, left directions in the face space.
// All neighbors are guaranteed to be distinct.
func (ci CellID) EdgeNeighbors() [4]CellID {
level := ci.Level()
size := sizeIJ(level)
f, i, j, _ := ci.faceIJOrientation()
return [4]CellID{
cellIDFromFaceIJWrap(f, i, j-size).Parent(level),
cellIDFromFaceIJWrap(f, i+size, j).Parent(level),
cellIDFromFaceIJWrap(f, i, j+size).Parent(level),
cellIDFromFaceIJWrap(f, i-size, j).Parent(level),
}
}
// VertexNeighbors returns the neighboring cellIDs with vertex closest to this cell at the given level.
// (Normally there are four neighbors, but the closest vertex may only have three neighbors if it is one of
// the 8 cube vertices.)
func (ci CellID) VertexNeighbors(level int) []CellID {
halfSize := sizeIJ(level + 1)
size := halfSize << 1
f, i, j, _ := ci.faceIJOrientation()
var isame, jsame bool
var ioffset, joffset int
if i&halfSize != 0 {
ioffset = size
isame = (i + size) < maxSize
} else {
ioffset = -size
isame = (i - size) >= 0
}
if j&halfSize != 0 {
joffset = size
jsame = (j + size) < maxSize
} else {
joffset = -size
jsame = (j - size) >= 0
}
results := []CellID{
ci.Parent(level),
cellIDFromFaceIJSame(f, i+ioffset, j, isame).Parent(level),
cellIDFromFaceIJSame(f, i, j+joffset, jsame).Parent(level),
}
if isame || jsame {
results = append(results, cellIDFromFaceIJSame(f, i+ioffset, j+joffset, isame && jsame).Parent(level))
}
return results
}
// AllNeighbors returns all neighbors of this cell at the given level. Two
// cells X and Y are neighbors if their boundaries intersect but their
// interiors do not. In particular, two cells that intersect at a single
// point are neighbors. Note that for cells adjacent to a face vertex, the
// same neighbor may be returned more than once. There could be up to eight
// neighbors including the diagonal ones that share the vertex.
//
// This requires level >= ci.Level().
func (ci CellID) AllNeighbors(level int) []CellID {
var neighbors []CellID
face, i, j, _ := ci.faceIJOrientation()
// Find the coordinates of the lower left-hand leaf cell. We need to
// normalize (i,j) to a known position within the cell because level
// may be larger than this cell's level.
size := sizeIJ(ci.Level())
i &= -size
j &= -size
nbrSize := sizeIJ(level)
// We compute the top-bottom, left-right, and diagonal neighbors in one
// pass. The loop test is at the end of the loop to avoid 32-bit overflow.
for k := -nbrSize; ; k += nbrSize {
var sameFace bool
if k < 0 {
sameFace = (j+k >= 0)
} else if k >= size {
sameFace = (j+k < maxSize)
} else {
sameFace = true
// Top and bottom neighbors.
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i+k, j-nbrSize,
j-size >= 0).Parent(level))
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i+k, j+size,
j+size < maxSize).Parent(level))
}
// Left, right, and diagonal neighbors.
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i-nbrSize, j+k,
sameFace && i-size >= 0).Parent(level))
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i+size, j+k,
sameFace && i+size < maxSize).Parent(level))
if k >= size {
break
}
}
return neighbors
}
// RangeMin returns the minimum CellID that is contained within this cell.
func (ci CellID) RangeMin() CellID { return CellID(uint64(ci) - (ci.lsb() - 1)) }
// RangeMax returns the maximum CellID that is contained within this cell.
func (ci CellID) RangeMax() CellID { return CellID(uint64(ci) + (ci.lsb() - 1)) }
// Contains returns true iff the CellID contains oci.
func (ci CellID) Contains(oci CellID) bool {
return uint64(ci.RangeMin()) <= uint64(oci) && uint64(oci) <= uint64(ci.RangeMax())
}
// Intersects returns true iff the CellID intersects oci.
func (ci CellID) Intersects(oci CellID) bool {
return uint64(oci.RangeMin()) <= uint64(ci.RangeMax()) && uint64(oci.RangeMax()) >= uint64(ci.RangeMin())
}
// String returns the string representation of the cell ID in the form "1/3210".
func (ci CellID) String() string {
if !ci.IsValid() {
return "Invalid: " + strconv.FormatInt(int64(ci), 16)
}
var b bytes.Buffer
b.WriteByte("012345"[ci.Face()]) // values > 5 will have been picked off by !IsValid above
b.WriteByte('/')
for level := 1; level <= ci.Level(); level++ {
b.WriteByte("0123"[ci.ChildPosition(level)])
}
return b.String()
}
// Point returns the center of the s2 cell on the sphere as a Point.
// The maximum directional error in Point (compared to the exact
// mathematical result) is 1.5 * dblEpsilon radians, and the maximum length
// error is 2 * dblEpsilon (the same as Normalize).
func (ci CellID) Point() Point { return Point{ci.rawPoint().Normalize()} }
// LatLng returns the center of the s2 cell on the sphere as a LatLng.
func (ci CellID) LatLng() LatLng { return LatLngFromPoint(Point{ci.rawPoint()}) }
// ChildBegin returns the first child in a traversal of the children of this cell, in Hilbert curve order.
//
// for ci := c.ChildBegin(); ci != c.ChildEnd(); ci = ci.Next() {
// ...
// }
func (ci CellID) ChildBegin() CellID {
ol := ci.lsb()
return CellID(uint64(ci) - ol + ol>>2)
}
// ChildBeginAtLevel returns the first cell in a traversal of children a given level deeper than this cell, in
// Hilbert curve order. The given level must be no smaller than the cell's level.
// See ChildBegin for example use.
func (ci CellID) ChildBeginAtLevel(level int) CellID {
return CellID(uint64(ci) - ci.lsb() + lsbForLevel(level))
}
// ChildEnd returns the first cell after a traversal of the children of this cell in Hilbert curve order.
// The returned cell may be invalid.
func (ci CellID) ChildEnd() CellID {
ol := ci.lsb()
return CellID(uint64(ci) + ol + ol>>2)
}
// ChildEndAtLevel returns the first cell after the last child in a traversal of children a given level deeper
// than this cell, in Hilbert curve order.
// The given level must be no smaller than the cell's level.
// The returned cell may be invalid.
func (ci CellID) ChildEndAtLevel(level int) CellID {
return CellID(uint64(ci) + ci.lsb() + lsbForLevel(level))
}
// Next returns the next cell along the Hilbert curve.
// This is expected to be used with ChildBegin and ChildEnd,
// or ChildBeginAtLevel and ChildEndAtLevel.
func (ci CellID) Next() CellID {
return CellID(uint64(ci) + ci.lsb()<<1)
}
// Prev returns the previous cell along the Hilbert curve.
func (ci CellID) Prev() CellID {
return CellID(uint64(ci) - ci.lsb()<<1)
}
// NextWrap returns the next cell along the Hilbert curve, wrapping from last to
// first as necessary. This should not be used with ChildBegin and ChildEnd.
func (ci CellID) NextWrap() CellID {
n := ci.Next()
if uint64(n) < wrapOffset {
return n
}
return CellID(uint64(n) - wrapOffset)
}
// PrevWrap returns the previous cell along the Hilbert curve, wrapping around from
// first to last as necessary. This should not be used with ChildBegin and ChildEnd.
func (ci CellID) PrevWrap() CellID {
p := ci.Prev()
if uint64(p) < wrapOffset {
return p
}
return CellID(uint64(p) + wrapOffset)
}
// AdvanceWrap advances or retreats the indicated number of steps along the
// Hilbert curve at the current level and returns the new position. The
// position wraps between the first and last faces as necessary.
func (ci CellID) AdvanceWrap(steps int64) CellID {
if steps == 0 {
return ci
}
// We clamp the number of steps if necessary to ensure that we do not
// advance past the End() or before the Begin() of this level.
shift := uint(2*(maxLevel-ci.Level()) + 1)
if steps < 0 {
if min := -int64(uint64(ci) >> shift); steps < min {
wrap := int64(wrapOffset >> shift)
steps %= wrap
if steps < min {
steps += wrap
}
}
} else {
// Unlike Advance(), we don't want to return End(level).
if max := int64((wrapOffset - uint64(ci)) >> shift); steps > max {
wrap := int64(wrapOffset >> shift)
steps %= wrap
if steps > max {
steps -= wrap
}
}
}
// If steps is negative, then shifting it left has undefined behavior.
// Cast to uint64 for a 2's complement answer.
return CellID(uint64(ci) + (uint64(steps) << shift))
}
// TODO: the methods below are not exported yet. Settle on the entire API design
// before doing this. Do we want to mirror the C++ one as closely as possible?
// distanceFromBegin returns the number of steps that this cell is from the first
// node in the S2 heirarchy at our level. (i.e., FromFace(0).ChildBeginAtLevel(ci.Level())).
// The return value is always non-negative.
func (ci CellID) distanceFromBegin() int64 {
return int64(ci >> uint64(2*(maxLevel-ci.Level())+1))
}
// rawPoint returns an unnormalized r3 vector from the origin through the center
// of the s2 cell on the sphere.
func (ci CellID) rawPoint() r3.Vector {
face, si, ti := ci.faceSiTi()
return faceUVToXYZ(face, stToUV((0.5/maxSize)*float64(si)), stToUV((0.5/maxSize)*float64(ti)))
}
// faceSiTi returns the Face/Si/Ti coordinates of the center of the cell.
func (ci CellID) faceSiTi() (face, si, ti int) {
face, i, j, _ := ci.faceIJOrientation()
delta := 0
if ci.IsLeaf() {
delta = 1
} else {
if (i^(int(ci)>>2))&1 != 0 {
delta = 2
}
}
return face, 2*i + delta, 2*j + delta
}
// faceIJOrientation uses the global lookupIJ table to unfiddle the bits of ci.
func (ci CellID) faceIJOrientation() (f, i, j, orientation int) {
f = ci.Face()
orientation = f & swapMask
nbits := maxLevel - 7*lookupBits // first iteration
for k := 7; k >= 0; k-- {
orientation += (int(uint64(ci)>>uint64(k*2*lookupBits+1)) & ((1 << uint((2 * nbits))) - 1)) << 2
orientation = lookupIJ[orientation]
i += (orientation >> (lookupBits + 2)) << uint(k*lookupBits)
j += ((orientation >> 2) & ((1 << lookupBits) - 1)) << uint(k*lookupBits)
orientation &= (swapMask | invertMask)
nbits = lookupBits // following iterations
}
if ci.lsb()&0x1111111111111110 != 0 {
orientation ^= swapMask
}
return
}
// cellIDFromFaceIJ returns a leaf cell given its cube face (range 0..5) and IJ coordinates.
func cellIDFromFaceIJ(f, i, j int) CellID {
// Note that this value gets shifted one bit to the left at the end
// of the function.
n := uint64(f) << (posBits - 1)
// Alternating faces have opposite Hilbert curve orientations; this
// is necessary in order for all faces to have a right-handed
// coordinate system.
bits := f & swapMask
// Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
// curve position. The lookup table transforms a 10-bit key of the form
// "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
// letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
// Hilbert curve orientation respectively.
for k := 7; k >= 0; k-- {
mask := (1 << lookupBits) - 1
bits += int((i>>uint(k*lookupBits))&mask) << (lookupBits + 2)
bits += int((j>>uint(k*lookupBits))&mask) << 2
bits = lookupPos[bits]
n |= uint64(bits>>2) << (uint(k) * 2 * lookupBits)
bits &= (swapMask | invertMask)
}
return CellID(n*2 + 1)
}
func cellIDFromFaceIJWrap(f, i, j int) CellID {
// Convert i and j to the coordinates of a leaf cell just beyond the
// boundary of this face. This prevents 32-bit overflow in the case
// of finding the neighbors of a face cell.
i = clamp(i, -1, maxSize)
j = clamp(j, -1, maxSize)
// We want to wrap these coordinates onto the appropriate adjacent face.
// The easiest way to do this is to convert the (i,j) coordinates to (x,y,z)
// (which yields a point outside the normal face boundary), and then call
// xyzToFaceUV to project back onto the correct face.
//
// The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using
// the linear projection (u=2*s-1 and v=2*t-1). (The code further below
// converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1).
// Any projection would work here, so we use the simplest.) We also clamp
// the (u,v) coordinates so that the point is barely outside the
// [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step
// (which divides by the new z coordinate) might change the other
// coordinates enough so that we end up in the wrong leaf cell.
const scale = 1.0 / maxSize
limit := math.Nextafter(1, 2)
u := math.Max(-limit, math.Min(limit, scale*float64((i<<1)+1-maxSize)))
v := math.Max(-limit, math.Min(limit, scale*float64((j<<1)+1-maxSize)))
// Find the leaf cell coordinates on the adjacent face, and convert
// them to a cell id at the appropriate level.
f, u, v = xyzToFaceUV(faceUVToXYZ(f, u, v))
return cellIDFromFaceIJ(f, stToIJ(0.5*(u+1)), stToIJ(0.5*(v+1)))
}
func cellIDFromFaceIJSame(f, i, j int, sameFace bool) CellID {
if sameFace {
return cellIDFromFaceIJ(f, i, j)
}
return cellIDFromFaceIJWrap(f, i, j)
}
// clamp returns number closest to x within the range min..max.
func clamp(x, min, max int) int {
if x < min {
return min
}
if x > max {
return max
}
return x
}
// ijToSTMin converts the i- or j-index of a leaf cell to the minimum corresponding
// s- or t-value contained by that cell. The argument must be in the range
// [0..2**30], i.e. up to one position beyond the normal range of valid leaf
// cell indices.
func ijToSTMin(i int) float64 {
return float64(i) / float64(maxSize)
}
// stToIJ converts value in ST coordinates to a value in IJ coordinates.
func stToIJ(s float64) int {
return clamp(int(math.Floor(maxSize*s)), 0, maxSize-1)
}
// cellIDFromPoint returns a leaf cell containing point p. Usually there is
// exactly one such cell, but for points along the edge of a cell, any
// adjacent cell may be (deterministically) chosen. This is because
// s2.CellIDs are considered to be closed sets. The returned cell will
// always contain the given point, i.e.
//
// CellFromPoint(p).ContainsPoint(p)
//
// is always true.
func cellIDFromPoint(p Point) CellID {
f, u, v := xyzToFaceUV(r3.Vector{p.X, p.Y, p.Z})
i := stToIJ(uvToST(u))
j := stToIJ(uvToST(v))
return cellIDFromFaceIJ(f, i, j)
}
// ijLevelToBoundUV returns the bounds in (u,v)-space for the cell at the given
// level containing the leaf cell with the given (i,j)-coordinates.
func ijLevelToBoundUV(i, j, level int) r2.Rect {
cellSize := sizeIJ(level)
xLo := i & -cellSize
yLo := j & -cellSize
return r2.Rect{
X: r1.Interval{
Lo: stToUV(ijToSTMin(xLo)),
Hi: stToUV(ijToSTMin(xLo + cellSize)),
},
Y: r1.Interval{
Lo: stToUV(ijToSTMin(yLo)),
Hi: stToUV(ijToSTMin(yLo + cellSize)),
},
}
}
// Constants related to the bit mangling in the Cell ID.
const (
lookupBits = 4
swapMask = 0x01
invertMask = 0x02
)
var (
ijToPos = [4][4]int{
{0, 1, 3, 2}, // canonical order
{0, 3, 1, 2}, // axes swapped
{2, 3, 1, 0}, // bits inverted
{2, 1, 3, 0}, // swapped & inverted
}
posToIJ = [4][4]int{
{0, 1, 3, 2}, // canonical order: (0,0), (0,1), (1,1), (1,0)
{0, 2, 3, 1}, // axes swapped: (0,0), (1,0), (1,1), (0,1)
{3, 2, 0, 1}, // bits inverted: (1,1), (1,0), (0,0), (0,1)
{3, 1, 0, 2}, // swapped & inverted: (1,1), (0,1), (0,0), (1,0)
}
posToOrientation = [4]int{swapMask, 0, 0, invertMask | swapMask}
lookupIJ [1 << (2*lookupBits + 2)]int
lookupPos [1 << (2*lookupBits + 2)]int
)
func init() {
initLookupCell(0, 0, 0, 0, 0, 0)
initLookupCell(0, 0, 0, swapMask, 0, swapMask)
initLookupCell(0, 0, 0, invertMask, 0, invertMask)
initLookupCell(0, 0, 0, swapMask|invertMask, 0, swapMask|invertMask)
}
// initLookupCell initializes the lookupIJ table at init time.
func initLookupCell(level, i, j, origOrientation, pos, orientation int) {
if level == lookupBits {
ij := (i << lookupBits) + j
lookupPos[(ij<<2)+origOrientation] = (pos << 2) + orientation
lookupIJ[(pos<<2)+origOrientation] = (ij << 2) + orientation
return
}
level++
i <<= 1
j <<= 1
pos <<= 2
r := posToIJ[orientation]
initLookupCell(level, i+(r[0]>>1), j+(r[0]&1), origOrientation, pos, orientation^posToOrientation[0])
initLookupCell(level, i+(r[1]>>1), j+(r[1]&1), origOrientation, pos+1, orientation^posToOrientation[1])
initLookupCell(level, i+(r[2]>>1), j+(r[2]&1), origOrientation, pos+2, orientation^posToOrientation[2])
initLookupCell(level, i+(r[3]>>1), j+(r[3]&1), origOrientation, pos+3, orientation^posToOrientation[3])
}
// CommonAncestorLevel returns the level of the common ancestor of the two S2 CellIDs.
func (ci CellID) CommonAncestorLevel(other CellID) (level int, ok bool) {
bits := uint64(ci ^ other)
if bits < ci.lsb() {
bits = ci.lsb()
}
if bits < other.lsb() {
bits = other.lsb()
}
msbPos := findMSBSetNonZero64(bits)
if msbPos > 60 {
return 0, false
}
return (60 - msbPos) >> 1, true
}
// findMSBSetNonZero64 returns the index (between 0 and 63) of the most
// significant set bit. Passing zero to this function has undefined behavior.
func findMSBSetNonZero64(bits uint64) int {
val := []uint64{0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000}
shift := []uint64{1, 2, 4, 8, 16, 32}
var msbPos uint64
for i := 5; i >= 0; i-- {
if bits&val[i] != 0 {
bits >>= shift[i]
msbPos |= shift[i]
}
}
return int(msbPos)
}
const deBruijn64 = 0x03f79d71b4ca8b09
const digitMask = uint64(1<<64 - 1)
var deBruijn64Lookup = []byte{
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}
// findLSBSetNonZero64 returns the index (between 0 and 63) of the least
// significant set bit. Passing zero to this function has undefined behavior.
//
// This code comes from trailingZeroBits in https://golang.org/src/math/big/nat.go
// which references (Knuth, volume 4, section 7.3.1).
func findLSBSetNonZero64(bits uint64) int {
return int(deBruijn64Lookup[((bits&-bits)*(deBruijn64&digitMask))>>58])
}
// Advance advances or retreats the indicated number of steps along the
// Hilbert curve at the current level, and returns the new position. The
// position is never advanced past End() or before Begin().
func (ci CellID) Advance(steps int64) CellID {
if steps == 0 {
return ci
}
// We clamp the number of steps if necessary to ensure that we do not
// advance past the End() or before the Begin() of this level. Note that
// minSteps and maxSteps always fit in a signed 64-bit integer.
stepShift := uint(2*(maxLevel-ci.Level()) + 1)
if steps < 0 {
minSteps := -int64(uint64(ci) >> stepShift)
if steps < minSteps {
steps = minSteps
}
} else {
maxSteps := int64((wrapOffset + ci.lsb() - uint64(ci)) >> stepShift)
if steps > maxSteps {
steps = maxSteps
}
}
return ci + CellID(steps)<<stepShift
}
// centerST return the center of the CellID in (s,t)-space.
func (ci CellID) centerST() r2.Point {
_, si, ti := ci.faceSiTi()
return r2.Point{siTiToST(uint64(si)), siTiToST(uint64(ti))}
}
// sizeST returns the edge length of this CellID in (s,t)-space at the given level.
func (ci CellID) sizeST(level int) float64 {
return ijToSTMin(sizeIJ(level))
}
// boundST returns the bound of this CellID in (s,t)-space.
func (ci CellID) boundST() r2.Rect {
s := ci.sizeST(ci.Level())
return r2.RectFromCenterSize(ci.centerST(), r2.Point{s, s})
}
// centerUV returns the center of this CellID in (u,v)-space. Note that
// the center of the cell is defined as the point at which it is recursively
// subdivided into four children; in general, it is not at the midpoint of
// the (u,v) rectangle covered by the cell.
func (ci CellID) centerUV() r2.Point {
_, si, ti := ci.faceSiTi()
return r2.Point{stToUV(siTiToST(uint64(si))), stToUV(siTiToST(uint64(ti)))}
}
// boundUV returns the bound of this CellID in (u,v)-space.
func (ci CellID) boundUV() r2.Rect {
_, i, j, _ := ci.faceIJOrientation()
return ijLevelToBoundUV(i, j, ci.Level())
}
// expandEndpoint returns a new u-coordinate u' such that the distance from the
// line u=u' to the given edge (u,v0)-(u,v1) is exactly the given distance
// (which is specified as the sine of the angle corresponding to the distance).
func expandEndpoint(u, maxV, sinDist float64) float64 {
// This is based on solving a spherical right triangle, similar to the
// calculation in Cap.RectBound.
// Given an edge of the form (u,v0)-(u,v1), let maxV = max(abs(v0), abs(v1)).
sinUShift := sinDist * math.Sqrt((1+u*u+maxV*maxV)/(1+u*u))
cosUShift := math.Sqrt(1 - sinUShift*sinUShift)
// The following is an expansion of tan(atan(u) + asin(sinUShift)).
return (cosUShift*u + sinUShift) / (cosUShift - sinUShift*u)
}
// expandedByDistanceUV returns a rectangle expanded in (u,v)-space so that it
// contains all points within the given distance of the boundary, and return the
// smallest such rectangle. If the distance is negative, then instead shrink this
// rectangle so that it excludes all points within the given absolute distance
// of the boundary.
//
// Distances are measured *on the sphere*, not in (u,v)-space. For example,
// you can use this method to expand the (u,v)-bound of an CellID so that
// it contains all points within 5km of the original cell. You can then
// test whether a point lies within the expanded bounds like this:
//
// if u, v, ok := faceXYZtoUV(face, point); ok && bound.ContainsPoint(r2.Point{u,v}) { ... }
//
// Limitations:
//
// - Because the rectangle is drawn on one of the six cube-face planes
// (i.e., {x,y,z} = +/-1), it can cover at most one hemisphere. This
// limits the maximum amount that a rectangle can be expanded. For
// example, CellID bounds can be expanded safely by at most 45 degrees
// (about 5000 km on the Earth's surface).
//
// - The implementation is not exact for negative distances. The resulting
// rectangle will exclude all points within the given distance of the
// boundary but may be slightly smaller than necessary.
func expandedByDistanceUV(uv r2.Rect, distance s1.Angle) r2.Rect {
// Expand each of the four sides of the rectangle just enough to include all
// points within the given distance of that side. (The rectangle may be
// expanded by a different amount in (u,v)-space on each side.)
maxU := math.Max(math.Abs(uv.X.Lo), math.Abs(uv.X.Hi))
maxV := math.Max(math.Abs(uv.Y.Lo), math.Abs(uv.Y.Hi))
sinDist := math.Sin(float64(distance))
return r2.Rect{
X: r1.Interval{expandEndpoint(uv.X.Lo, maxV, -sinDist),
expandEndpoint(uv.X.Hi, maxV, sinDist)},
Y: r1.Interval{expandEndpoint(uv.Y.Lo, maxU, -sinDist),
expandEndpoint(uv.Y.Hi, maxU, sinDist)}}
}
// MaxTile returns the largest cell with the same RangeMin such that
// RangeMax < limit.RangeMin. It returns limit if no such cell exists.
// This method can be used to generate a small set of CellIDs that covers
// a given range (a tiling). This example shows how to generate a tiling
// for a semi-open range of leaf cells [start, limit):
//
// for id := start.MaxTile(limit); id != limit; id = id.Next().MaxTile(limit)) { ... }
//
// Note that in general the cells in the tiling will be of different sizes;
// they gradually get larger (near the middle of the range) and then
// gradually get smaller as limit is approached.
func (ci CellID) MaxTile(limit CellID) CellID {
start := ci.RangeMin()
if start >= limit.RangeMin() {
return limit
}
if ci.RangeMax() >= limit {
// The cell is too large, shrink it. Note that when generating coverings
// of CellID ranges, this loop usually executes only once. Also because
// ci.RangeMin() < limit.RangeMin(), we will always exit the loop by the
// time we reach a leaf cell.
for {
ci = ci.Children()[0]
if ci.RangeMax() < limit {
break
}
}
return ci
}
// The cell may be too small. Grow it if necessary. Note that generally
// this loop only iterates once.
for !ci.isFace() {
parent := ci.immediateParent()
if parent.RangeMin() != start || parent.RangeMax() >= limit {
break
}
ci = parent
}
return ci
}
// centerFaceSiTi returns the (face, si, ti) coordinates of the center of the cell.
// Note that although (si,ti) coordinates span the range [0,2**31] in general,
// the cell center coordinates are always in the range [1,2**31-1] and
// therefore can be represented using a signed 32-bit integer.
func (ci CellID) centerFaceSiTi() (face, si, ti int) {
// First we compute the discrete (i,j) coordinates of a leaf cell contained
// within the given cell. Given that cells are represented by the Hilbert
// curve position corresponding at their center, it turns out that the cell
// returned by faceIJOrientation is always one of two leaf cells closest
// to the center of the cell (unless the given cell is a leaf cell itself,
// in which case there is only one possibility).
//
// Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin,
// jmin) be the coordinates of its lower left-hand corner, the leaf cell
// returned by faceIJOrientation is either (imin + s/2, jmin + s/2)
// (imin + s/2 - 1, jmin + s/2 - 1). The first case is the one we want.
// We can distinguish these two cases by looking at the low bit of i or
// j. In the second case the low bit is one, unless s == 2 (i.e. the
// level just above leaf cells) in which case the low bit is zero.
//
// In the code below, the expression ((i ^ (int(id) >> 2)) & 1) is true
// if we are in the second case described above.
face, i, j, _ := ci.faceIJOrientation()
delta := 0
if ci.IsLeaf() {
delta = 1
} else if (int64(i)^(int64(ci)>>2))&1 == 1 {
delta = 2
}
// Note that (2 * {i,j} + delta) will never overflow a 32-bit integer.
return face, 2*i + delta, 2*j + delta
}

241
vendor/github.com/golang/geo/s2/cellunion.go generated vendored Normal file
View file

@ -0,0 +1,241 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s2
import (
"sort"
)
// A CellUnion is a collection of CellIDs.
//
// It is normalized if it is sorted, and does not contain redundancy.
// Specifically, it may not contain the same CellID twice, nor a CellID that
// is contained by another, nor the four sibling CellIDs that are children of
// a single higher level CellID.
type CellUnion []CellID
// CellUnionFromRange creates a CellUnion that covers the half-open range
// of leaf cells [begin, end). If begin == end the resulting union is empty.
// This requires that begin and end are both leaves, and begin <= end.
// To create a closed-ended range, pass in end.Next().
func CellUnionFromRange(begin, end CellID) CellUnion {
// We repeatedly add the largest cell we can.
var cu CellUnion
for id := begin.MaxTile(end); id != end; id = id.Next().MaxTile(end) {
cu = append(cu, id)
}
return cu
}
// Normalize normalizes the CellUnion.
func (cu *CellUnion) Normalize() {
sort.Sort(byID(*cu))
output := make([]CellID, 0, len(*cu)) // the list of accepted cells
// Loop invariant: output is a sorted list of cells with no redundancy.
for _, ci := range *cu {
// The first two passes here either ignore this new candidate,
// or remove previously accepted cells that are covered by this candidate.
// Ignore this cell if it is contained by the previous one.
// We only need to check the last accepted cell. The ordering of the
// cells implies containment (but not the converse), and output has no redundancy,
// so if this candidate is not contained by the last accepted cell
// then it cannot be contained by any previously accepted cell.
if len(output) > 0 && output[len(output)-1].Contains(ci) {
continue
}
// Discard any previously accepted cells contained by this one.
// This could be any contiguous trailing subsequence, but it can't be
// a discontiguous subsequence because of the containment property of
// sorted S2 cells mentioned above.
j := len(output) - 1 // last index to keep
for j >= 0 {
if !ci.Contains(output[j]) {
break
}
j--
}
output = output[:j+1]
// See if the last three cells plus this one can be collapsed.
// We loop because collapsing three accepted cells and adding a higher level cell
// could cascade into previously accepted cells.
for len(output) >= 3 {
fin := output[len(output)-3:]
// fast XOR test; a necessary but not sufficient condition
if fin[0]^fin[1]^fin[2]^ci != 0 {
break
}
// more expensive test; exact.
// Compute the two bit mask for the encoded child position,
// then see if they all agree.
mask := CellID(ci.lsb() << 1)
mask = ^(mask + mask<<1)
should := ci & mask
if (fin[0]&mask != should) || (fin[1]&mask != should) || (fin[2]&mask != should) || ci.isFace() {
break
}
output = output[:len(output)-3]
ci = ci.immediateParent() // checked !ci.isFace above
}
output = append(output, ci)
}
*cu = output
}
// IntersectsCellID reports whether this cell union intersects the given cell ID.
//
// This method assumes that the CellUnion has been normalized.
func (cu *CellUnion) IntersectsCellID(id CellID) bool {
// Find index of array item that occurs directly after our probe cell:
i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
if i != len(*cu) && (*cu)[i].RangeMin() <= id.RangeMax() {
return true
}
return i != 0 && (*cu)[i-1].RangeMax() >= id.RangeMin()
}
// ContainsCellID reports whether the cell union contains the given cell ID.
// Containment is defined with respect to regions, e.g. a cell contains its 4 children.
//
// This method assumes that the CellUnion has been normalized.
func (cu *CellUnion) ContainsCellID(id CellID) bool {
// Find index of array item that occurs directly after our probe cell:
i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
if i != len(*cu) && (*cu)[i].RangeMin() <= id {
return true
}
return i != 0 && (*cu)[i-1].RangeMax() >= id
}
type byID []CellID
func (cu byID) Len() int { return len(cu) }
func (cu byID) Less(i, j int) bool { return cu[i] < cu[j] }
func (cu byID) Swap(i, j int) { cu[i], cu[j] = cu[j], cu[i] }
// Denormalize replaces this CellUnion with an expanded version of the
// CellUnion where any cell whose level is less than minLevel or where
// (level - minLevel) is not a multiple of levelMod is replaced by its
// children, until either both of these conditions are satisfied or the
// maximum level is reached.
func (cu *CellUnion) Denormalize(minLevel, levelMod int) {
var denorm CellUnion
for _, id := range *cu {
level := id.Level()
newLevel := level
if newLevel < minLevel {
newLevel = minLevel
}
if levelMod > 1 {
newLevel += (maxLevel - (newLevel - minLevel)) % levelMod
if newLevel > maxLevel {
newLevel = maxLevel
}
}
if newLevel == level {
denorm = append(denorm, id)
} else {
end := id.ChildEndAtLevel(newLevel)
for ci := id.ChildBeginAtLevel(newLevel); ci != end; ci = ci.Next() {
denorm = append(denorm, ci)
}
}
}
*cu = denorm
}
// RectBound returns a Rect that bounds this entity.
func (cu *CellUnion) RectBound() Rect {
bound := EmptyRect()
for _, c := range *cu {
bound = bound.Union(CellFromCellID(c).RectBound())
}
return bound
}
// CapBound returns a Cap that bounds this entity.
func (cu *CellUnion) CapBound() Cap {
if len(*cu) == 0 {
return EmptyCap()
}
// Compute the approximate centroid of the region. This won't produce the
// bounding cap of minimal area, but it should be close enough.
var centroid Point
for _, ci := range *cu {
area := AvgAreaMetric.Value(ci.Level())
centroid = Point{centroid.Add(ci.Point().Mul(area))}
}
if zero := (Point{}); centroid == zero {
centroid = PointFromCoords(1, 0, 0)
} else {
centroid = Point{centroid.Normalize()}
}
// Use the centroid as the cap axis, and expand the cap angle so that it
// contains the bounding caps of all the individual cells. Note that it is
// *not* sufficient to just bound all the cell vertices because the bounding
// cap may be concave (i.e. cover more than one hemisphere).
c := CapFromPoint(centroid)
for _, ci := range *cu {
c = c.AddCap(CellFromCellID(ci).CapBound())
}
return c
}
// ContainsCell reports whether this cell union contains the given cell.
func (cu *CellUnion) ContainsCell(c Cell) bool {
return cu.ContainsCellID(c.id)
}
// IntersectsCell reports whether this cell union intersects the given cell.
func (cu *CellUnion) IntersectsCell(c Cell) bool {
return cu.IntersectsCellID(c.id)
}
// ContainsPoint reports whether this cell union contains the given point.
func (cu *CellUnion) ContainsPoint(p Point) bool {
return cu.ContainsCell(CellFromPoint(p))
}
// LeafCellsCovered reports the number of leaf cells covered by this cell union.
// This will be no more than 6*2^60 for the whole sphere.
func (cu *CellUnion) LeafCellsCovered() int64 {
var numLeaves int64
for _, c := range *cu {
numLeaves += 1 << uint64((maxLevel-int64(c.Level()))<<1)
}
return numLeaves
}
// BUG: Differences from C++:
// Contains(CellUnion)/Intersects(CellUnion)
// Union(CellUnion)/Intersection(CellUnion)/Difference(CellUnion)
// Expand
// ContainsPoint
// AverageArea/ApproxArea/ExactArea

31
vendor/github.com/golang/geo/s2/doc.go generated vendored Normal file
View file

@ -0,0 +1,31 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package s2 implements types and functions for working with geometry in S² (spherical geometry).
Its related packages, parallel to this one, are s1 (operates on S¹), r1 (operates on ¹)
and r3 (operates on ³).
This package provides types and functions for the S2 cell hierarchy and coordinate systems.
The S2 cell hierarchy is a hierarchical decomposition of the surface of a unit sphere (S²)
into ``cells''; it is highly efficient, scales from continental size to under 1 cm²
and preserves spatial locality (nearby cells have close IDs).
A presentation that gives an overview of S2 is
https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view.
*/
package s2

1405
vendor/github.com/golang/geo/s2/edgeutil.go generated vendored Normal file

File diff suppressed because it is too large Load diff

97
vendor/github.com/golang/geo/s2/latlng.go generated vendored Normal file
View file

@ -0,0 +1,97 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package s2
import (
"fmt"
"math"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
const (
northPoleLat = s1.Angle(math.Pi/2) * s1.Radian
southPoleLat = -northPoleLat
)
// LatLng represents a point on the unit sphere as a pair of angles.
type LatLng struct {
Lat, Lng s1.Angle
}
// LatLngFromDegrees returns a LatLng for the coordinates given in degrees.
func LatLngFromDegrees(lat, lng float64) LatLng {
return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree}
}
// IsValid returns true iff the LatLng is normalized, with Lat ∈ [-π/2,π/2] and Lng ∈ [-π,π].
func (ll LatLng) IsValid() bool {
return math.Abs(ll.Lat.Radians()) <= math.Pi/2 && math.Abs(ll.Lng.Radians()) <= math.Pi
}
// Normalized returns the normalized version of the LatLng,
// with Lat clamped to [-π/2,π/2] and Lng wrapped in [-π,π].
func (ll LatLng) Normalized() LatLng {
lat := ll.Lat
if lat > northPoleLat {
lat = northPoleLat
} else if lat < southPoleLat {
lat = southPoleLat
}
lng := s1.Angle(math.Remainder(ll.Lng.Radians(), 2*math.Pi)) * s1.Radian
return LatLng{lat, lng}
}
func (ll LatLng) String() string { return fmt.Sprintf("[%v, %v]", ll.Lat, ll.Lng) }
// Distance returns the angle between two LatLngs.
func (ll LatLng) Distance(ll2 LatLng) s1.Angle {
// Haversine formula, as used in C++ S2LatLng::GetDistance.
lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians()
lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians()
dlat := math.Sin(0.5 * (lat2 - lat1))
dlng := math.Sin(0.5 * (lng2 - lng1))
x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2)
return s1.Angle(2*math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x)))) * s1.Radian
}
// NOTE(mikeperrow): The C++ implementation publicly exposes latitude/longitude
// functions. Let's see if that's really necessary before exposing the same functionality.
func latitude(p Point) s1.Angle {
return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y))) * s1.Radian
}
func longitude(p Point) s1.Angle {
return s1.Angle(math.Atan2(p.Y, p.X)) * s1.Radian
}
// PointFromLatLng returns an Point for the given LatLng.
// The maximum error in the result is 1.5 * dblEpsilon. (This does not
// include the error of converting degrees, E5, E6, or E7 into radians.)
func PointFromLatLng(ll LatLng) Point {
phi := ll.Lat.Radians()
theta := ll.Lng.Radians()
cosphi := math.Cos(phi)
return Point{r3.Vector{math.Cos(theta) * cosphi, math.Sin(theta) * cosphi, math.Sin(phi)}}
}
// LatLngFromPoint returns an LatLng for a given Point.
func LatLngFromPoint(p Point) LatLng {
return LatLng{latitude(p), longitude(p)}
}

Some files were not shown because too many files have changed in this diff Show more