├── examples ├── pattern.png ├── patterns │ ├── xes.png │ ├── plaid.png │ ├── chevrons.png │ ├── diamonds.png │ ├── hexagons.png │ ├── octagons.png │ ├── pattern.png │ ├── squares.png │ ├── plus-signs.png │ ├── sine-waves.png │ ├── triangles.png │ ├── tessellation.png │ ├── mosaic-squares.png │ ├── nested-squares.png │ ├── concentric-circles.png │ ├── overlapping-rings.png │ └── overlapping-circles.png ├── uri_image.go ├── base64.go ├── default.go ├── phrase.go ├── pattern.go ├── background_color.go └── base_color.go ├── geopattern.go ├── LICENSE ├── CHANGELOG.md ├── utils └── utils.go ├── svg └── svg.go ├── README.md ├── shapes └── shapes.go └── pattern └── pattern.go /examples/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/pattern.png -------------------------------------------------------------------------------- /examples/patterns/xes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/xes.png -------------------------------------------------------------------------------- /examples/patterns/plaid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/plaid.png -------------------------------------------------------------------------------- /examples/patterns/chevrons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/chevrons.png -------------------------------------------------------------------------------- /examples/patterns/diamonds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/diamonds.png -------------------------------------------------------------------------------- /examples/patterns/hexagons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/hexagons.png -------------------------------------------------------------------------------- /examples/patterns/octagons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/octagons.png -------------------------------------------------------------------------------- /examples/patterns/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/pattern.png -------------------------------------------------------------------------------- /examples/patterns/squares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/squares.png -------------------------------------------------------------------------------- /examples/patterns/plus-signs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/plus-signs.png -------------------------------------------------------------------------------- /examples/patterns/sine-waves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/sine-waves.png -------------------------------------------------------------------------------- /examples/patterns/triangles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/triangles.png -------------------------------------------------------------------------------- /examples/patterns/tessellation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/tessellation.png -------------------------------------------------------------------------------- /examples/patterns/mosaic-squares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/mosaic-squares.png -------------------------------------------------------------------------------- /examples/patterns/nested-squares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/nested-squares.png -------------------------------------------------------------------------------- /examples/patterns/concentric-circles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/concentric-circles.png -------------------------------------------------------------------------------- /examples/patterns/overlapping-rings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/overlapping-rings.png -------------------------------------------------------------------------------- /examples/patterns/overlapping-circles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravj/geopattern/HEAD/examples/patterns/overlapping-circles.png -------------------------------------------------------------------------------- /examples/uri_image.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's uri image string 9 | func main() { 10 | args := map[string]string{} 11 | gp := geopattern.URIimage(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /examples/base64.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's Base64 encoded string 9 | func main() { 10 | args := map[string]string{} 11 | gp := geopattern.Base64String(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /examples/default.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's SVG string without any argument 9 | func main() { 10 | args := map[string]string{} 11 | gp := geopattern.Generate(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /examples/phrase.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's SVG string for a phrase argument 9 | func main() { 10 | args := map[string]string{"phrase": "O"} 11 | gp := geopattern.Generate(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /examples/pattern.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's SVG string for a specific pattern 9 | func main() { 10 | args := map[string]string{"generator": "squares"} 11 | gp := geopattern.Generate(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /examples/background_color.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's SVG string with a specific background color 9 | func main() { 10 | args := map[string]string{"color": "#f9b"} 11 | gp := geopattern.Generate(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /examples/base_color.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pravj/geopattern" 6 | ) 7 | 8 | // Prints pattern's SVG string with a specific base background color 9 | func main() { 10 | args := map[string]string{"baseColor": "#e2b"} 11 | gp := geopattern.Generate(args) 12 | fmt.Println(gp) 13 | } 14 | -------------------------------------------------------------------------------- /geopattern.go: -------------------------------------------------------------------------------- 1 | // Package geopattern creates beautiful generative image patterns from a string. 2 | package geopattern 3 | 4 | import ( 5 | "encoding/base64" 6 | "fmt" 7 | "github.com/pravj/geopattern/pattern" 8 | ) 9 | 10 | // Generate returns pattern's SVG string 11 | func Generate(args map[string]string) string { 12 | p := pattern.New(args) 13 | 14 | return p.SvgStr() 15 | } 16 | 17 | // Base64String returns pattern's Base64 encoded string 18 | func Base64String(args map[string]string) string { 19 | svgStr := Generate(args) 20 | base64Str := base64.StdEncoding.EncodeToString([]byte(svgStr)) 21 | 22 | return base64Str 23 | } 24 | 25 | // URIimage returns pattern's uri image string 26 | func URIimage(args map[string]string) string { 27 | base64Str := Base64String(args) 28 | 29 | return fmt.Sprintf("url(data:image/svg+xml;base64,%s);", base64Str) 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 Jason Long (Original) 4 | Copyright (c) 2014-2015 Pravendra Singh (Golang Port) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [v1.4.1] - 2015-03-10 2 | 3 | ### Change 4 | * Rename project to **geopattern**, after [this discussion](https://github.com/avelino/awesome-go/issues/305) 5 | 6 | 7 | ## [v1.4.0] - 2015-03-09 8 | 9 | ### Add 10 | * CHANGELOG for the project 11 | 12 | ### Fix 13 | * Issue [#4](https://github.com/pravj/geoPattern/issues/4) : Struct fields should be in camelcase 14 | 15 | ### Change 16 | * Rename project to **geoPattern**; following the new golang blog, [Package Names](http://blog.golang.org/package-names) 17 | * Package functions API : Change function names to CamelCase 18 | * Pattern names : Change available image pattern names 19 | * (Now supports **-** instead of **_**; like **concentric-circles** instead of **concentric_circles**) 20 | * Function arguments : Rename function argument **base_color** to **baseColor** 21 | * Example gists according to the API changes 22 | 23 | 24 | ## [v1.3.0] - 2015-03-06 25 | 26 | ### Fix 27 | * Issue [#6](https://github.com/pravj/geoPattern/issues/6) : Red color not working (contributor : [Thesandlord](https://github.com/Thesandlord)) 28 | 29 | 30 | ## [v1.2.1] - 2014-11-03 31 | 32 | ### Add 33 | * Blog entry as a development story of geoPatten 34 | 35 | 36 | ## [v1.2.0] - 2014-10-28 37 | 38 | ### Fix 39 | * Issue [#1](https://github.com/pravj/geo_pattern/pull/1) : Typo in README (contributor : [follower](https://github.com/follower)) 40 | 41 | 42 | ## [v1.0.1] - 2014-10-28 43 | 44 | ### Add 45 | * Example image pattern files 46 | 47 | 48 | ## [v1.0.0] - 2014-10-26 49 | * Initial Release 50 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | // Package utils provide utility functions that helps 2 | // development across the project. 3 | package utils 4 | 5 | import ( 6 | "crypto/sha1" 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | // constants representing stroke, fill's opacity and color, 12 | // base color, minimum and maximum opacity value 13 | const ( 14 | BaseColor string = "#9e2c7b" 15 | StrokeColor string = "#000" 16 | StrokeOpacity float64 = 0.02 17 | FillColorDark string = "#222" 18 | FillColorLight string = "#ddd" 19 | OpacityMin float64 = 0.02 20 | OpacityMax float64 = 0.15 21 | ) 22 | 23 | // Hash returns SHA-1 encryption of a string 24 | func Hash(s string) string { 25 | h := sha1.New() 26 | h.Write([]byte(s)) 27 | 28 | hash := h.Sum(nil) 29 | 30 | return fmt.Sprintf("%x", hash) 31 | } 32 | 33 | // Map returns respective value of a number from a range to different range 34 | func Map(value, aMin, aMax, bMin, bMax float64) float64 { 35 | aRange := aMax - aMin 36 | bRange := bMax - bMin 37 | 38 | return (bMax - (aMax-value)*(bRange/aRange)) 39 | } 40 | 41 | // HexVal returns decimal representation of a substring of a hexa decimal string 42 | func HexVal(str string, index, length int) float64 { 43 | hexStr := str[index : index+length] 44 | 45 | hexVal, err := strconv.ParseInt(hexStr, 16, 0) 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | return float64(hexVal) 51 | } 52 | 53 | // Merge merges two 'map' objects and returns the resultant object 54 | func Merge(mapA map[string]interface{}, mapB map[string]interface{}) map[string]interface{} { 55 | for k, v := range mapA { 56 | mapB[k] = v 57 | } 58 | 59 | return mapB 60 | } 61 | 62 | // Opacity returns opacity value in a particular range 63 | func Opacity(value float64) float64 { 64 | return Map(value, 0, 15, OpacityMin, OpacityMax) 65 | } 66 | 67 | // FillColor returns string to be used for fill color 68 | func FillColor(value float64) string { 69 | if int(value)%2 == 0 { 70 | return FillColorLight 71 | } 72 | return FillColorDark 73 | } 74 | -------------------------------------------------------------------------------- /svg/svg.go: -------------------------------------------------------------------------------- 1 | // Package svg provide methods to work effortlessly with SVG. 2 | package svg 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | // SVG struct, SVG contains elementry attributes like 10 | // svg string, width, height 11 | type SVG struct { 12 | svgString string 13 | width, height int 14 | } 15 | 16 | // SetWidth sets SVG object's width 17 | func (s *SVG) SetWidth(w int) { 18 | s.width = w 19 | } 20 | 21 | // SetHeight sets SVG object's height 22 | func (s *SVG) SetHeight(h int) { 23 | s.height = h 24 | } 25 | 26 | // header returns string representing SVG object's header(staring) part 27 | func (s *SVG) header() string { 28 | return fmt.Sprintf("", s.width, s.height) 29 | } 30 | 31 | // footer returns string representing SVG object's footer(ending) part 32 | func (s *SVG) footer() string { 33 | return "" 34 | } 35 | 36 | // Str returns string representing whole SVG object 37 | func (s *SVG) Str() string { 38 | return s.header() + s.svgString + s.footer() 39 | } 40 | 41 | // Rect adds a rectangle element to SVG object 42 | func (s *SVG) Rect(x, y, w, h interface{}, args map[string]interface{}) { 43 | rectStr := fmt.Sprintf("", x, y, w, h, s.WriteArgs(args)) 44 | s.svgString += rectStr 45 | } 46 | 47 | // Circle adds a circle element to SVG object 48 | func (s *SVG) Circle(cx, cy, r interface{}, args map[string]interface{}) { 49 | circleStr := fmt.Sprintf("", cx, cy, r, s.WriteArgs(args)) 50 | s.svgString += circleStr 51 | } 52 | 53 | // Path adds a path element to SVG object 54 | func (s *SVG) Path(str string, args map[string]interface{}) { 55 | pathStr := fmt.Sprintf("", str, s.WriteArgs(args)) 56 | s.svgString += pathStr 57 | } 58 | 59 | // Polyline adds a polyline element to SVG object 60 | func (s *SVG) Polyline(str string, args map[string]interface{}) { 61 | polylineStr := fmt.Sprintf("", str, s.WriteArgs(args)) 62 | s.svgString += polylineStr 63 | } 64 | 65 | // Group adds a group element to SVG object. 66 | // 67 | // It groups optionally provided elements together. 68 | func (s *SVG) Group(elements [2]string, args map[string]interface{}) { 69 | s.svgString += fmt.Sprintf("", s.WriteArgs(args)) 70 | s.svgString += elements[0] + elements[1] 71 | s.svgString += "" 72 | } 73 | 74 | // WriteArgs adds additional attributes to a SVG elements. 75 | // 76 | // It parses provides 'map' arguments to add attributes to SVG element. 77 | func (s *SVG) WriteArgs(args map[string]interface{}) string { 78 | str := "" 79 | 80 | for k, v := range args { 81 | objType := fmt.Sprintf("%s", reflect.TypeOf(v)) 82 | 83 | switch objType { 84 | case "string": 85 | str += fmt.Sprintf("%s='%s' ", k, v) 86 | case "int": 87 | str += fmt.Sprintf("%s='%v' ", k, v) 88 | case "float64": 89 | str += fmt.Sprintf("%s='%v' ", k, v) 90 | default: 91 | { 92 | str += fmt.Sprintf("%s='", k) 93 | for K, V := range v.(map[string]string) { 94 | str += fmt.Sprintf("%s:%s;", K, V) 95 | } 96 | str += "' " 97 | } 98 | } 99 | } 100 | 101 | return str 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | geopattern 2 | =========== 3 | > Create beautiful generative image patterns from a string in golang. 4 | > > Go port of [Jason Long](https://github.com/jasonlong)'s awesome [GeoPattern](https://github.com/jasonlong/geo_pattern) library. 5 | 6 | [![GoDoc](https://godoc.org/github.com/pravj/geopattern?status.svg)](http://godoc.org/github.com/pravj/geopattern) 7 | 8 | > Read geopattern's development story [**geo_pattern: going on the Go path**](http://pravj.github.io/blog/going-on-the-go-path/) 9 | 10 | ![Nested Squares Pattern](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/pattern.png) 11 | 12 | Generate beautiful tiling SVG patterns from a string. The string is converted into a SHA and a color and pattern are determined based on the values in the hash. The color is determined by shifting the hue from a default (or passed in) base color. One of 16 patterns is used (or you can specify one) and the sizing of the pattern elements is also determined by the hash values. 13 | 14 | You can use the generated pattern as the background-image for a container. Using the base64 representation of the pattern still results in SVG rendering, so it looks great on retina displays. 15 | 16 | See the GitHub Guides [site](https://guides.github.com) as an example of this library in action. GitHub Guides use [Original](https://github.com/jasonlong/geo_pattern) Ruby implementation. 17 | 18 | ## Installation 19 | 20 | `go get github.com/pravj/geopattern` 21 | 22 | ## Usage 23 | 24 | [Example](https://github.com/pravj/geopattern/tree/master/examples) directory contains sample go programs that explains use of `geopattern` 25 | 26 | ## API 27 | 28 | ### Arguments for functions returning pattern's string 29 | 30 | #### `phrase` : custom pattern phrase 31 | 32 | ``` 33 | args := map[string]string{"phrase": "My Custom Phrase"} 34 | ``` 35 | 36 | #### `generator` : custom pattern type 37 | 38 | ``` 39 | args := map[string]string{"generator": "plaid"} 40 | ``` 41 | 42 | #### `color` : custom background color 43 | 44 | ``` 45 | args := map[string]string{"color": "#3b5998"} 46 | ``` 47 | 48 | #### `baseColor` : custom base color that decides background color 49 | 50 | ``` 51 | args := map[string]string{"baseColor": "#ffcc00"} 52 | ``` 53 | --- 54 | 55 | ### Functions provided by package for pattern's string representation 56 | 57 | #### Get the SVG string : 58 | 59 | ``` 60 | Generate(args) 61 | ``` 62 | 63 | #### Get the Base64 encoded string : 64 | 65 | ``` 66 | Base64String(args) 67 | ``` 68 | 69 | #### Get uri image string : 70 | 71 | ``` 72 | URIimage(args) 73 | ``` 74 | 75 | ## Available Pattern 76 | 77 | #### chevrons 78 | ![Chevrons](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/chevrons.png) 79 | 80 | #### concentric-circles 81 | ![Concentric-Circles](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/concentric-circles.png) 82 | 83 | #### diamonds 84 | ![Diamonds](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/diamonds.png) 85 | 86 | #### hexagons 87 | ![Hexagons](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/hexagons.png) 88 | 89 | #### mosaic-squares 90 | ![Mosaic-Squares](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/mosaic-squares.png) 91 | 92 | #### nested-squares 93 | ![Nested-Squares](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/nested-squares.png) 94 | 95 | #### octagons 96 | ![Octagons](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/octagons.png) 97 | 98 | #### overlapping-circles 99 | ![Overlapping-Circles](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/overlapping-circles.png) 100 | 101 | #### overlapping-rings 102 | ![Overlapping-rings](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/overlapping-rings.png) 103 | 104 | #### plaid 105 | ![Plaid](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/plaid.png) 106 | 107 | #### plus-signs 108 | ![Plus-Signs](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/plus-signs.png) 109 | 110 | #### sine-waves 111 | ![Sine-Waves](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/sine-waves.png) 112 | 113 | #### squares 114 | ![Squares](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/squares.png) 115 | 116 | #### tessellation 117 | ![Tessellation](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/tessellation.png) 118 | 119 | #### triangles 120 | ![Triangles](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/triangles.png) 121 | 122 | #### xes 123 | ![Xes](https://raw.githubusercontent.com/pravj/geopattern/master/examples/patterns/xes.png) 124 | 125 | ## Dependencies 126 | [go-colorful](https://github.com/lucasb-eyer/go-colorful) : for color space conversion 127 | 128 | --- 129 | 130 | Made with *Muzi* and *Appy* by [Pravendra Singh](https://pravj.github.io) 131 | -------------------------------------------------------------------------------- /shapes/shapes.go: -------------------------------------------------------------------------------- 1 | // Package shapes implements some geometric shapes to be used in project 2 | package shapes 3 | 4 | import ( 5 | "fmt" 6 | "github.com/pravj/geopattern/svg" 7 | "github.com/pravj/geopattern/utils" 8 | "math" 9 | ) 10 | 11 | // BuildOctagon returns string representing an octagon shape 12 | func BuildOctagon(squareSize float64) string { 13 | s := squareSize 14 | c := 0.33 * s 15 | 16 | return fmt.Sprintf("%v,0,%v,0,%v,%v,%v,%v,%v,%v,%v,%v,0,%v,0,%v,%v,0", c, s-c, s, c, s, s-c, s-c, s, c, s, s-c, c, c) 17 | } 18 | 19 | // BuildTriangle returns string representing a triangle shape 20 | func BuildTriangle(sideLength, height float64) string { 21 | halfWidth := sideLength / 2 22 | 23 | return fmt.Sprintf("%v,0,%v,%v,0,%v,%v,0", halfWidth, sideLength, height, height, halfWidth) 24 | } 25 | 26 | // BuildDiamond returns string representing a diamond shape 27 | func BuildDiamond(width, height float64) string { 28 | return fmt.Sprintf("%v,0,%v,%v,%v,%v,0,%v", width/2, width, height/2, width/2, height, height/2) 29 | } 30 | 31 | // BuildRightTriangle returns string representing a right angle triangle shape 32 | func BuildRightTriangle(sideLength float64) string { 33 | return fmt.Sprintf("0,0,%v,%v,0,%v,0,0", sideLength, sideLength, sideLength) 34 | } 35 | 36 | // BuildRotatedTriangle returns string representing a rotated triangle shape 37 | func BuildRotatedTriangle(sideLength, width float64) string { 38 | halfHeight := sideLength / 2 39 | 40 | return fmt.Sprintf("0,0,%v,%v,0,%v,0,0", width, halfHeight, sideLength) 41 | } 42 | 43 | // BuildHexagon returns string representing a hexagon shape 44 | func BuildHexagon(sideLength float64) string { 45 | c := sideLength 46 | a := c / 2 47 | b := math.Sin(60*math.Pi/180) * c 48 | 49 | return fmt.Sprintf("0,%v,%v,0,%v,0,%v,%v,%v,%v,%v,%v,0,%v", b, a, a+c, 2*c, b, a+c, 2*b, a, 2*b, b) 50 | } 51 | 52 | // BuildChevron returns string representing a chevron shape 53 | func BuildChevron(width, height float64) [2]string { 54 | e := height * 0.66 55 | var elements [2]string 56 | 57 | elements[0] = fmt.Sprintf("", width/2, height-e, width/2, height, e) 58 | elements[1] = fmt.Sprintf("", width/2, height-e, width, width, e, width/2, height, width/2, height-e) 59 | 60 | return elements 61 | } 62 | 63 | // BuildPlus returns string representing an plus shape 64 | func BuildPlus(squareSize float64) [2]string { 65 | var elements [2]string 66 | 67 | elements[0] = fmt.Sprintf("", squareSize, squareSize, squareSize*3) 68 | elements[1] = fmt.Sprintf("", squareSize, squareSize*3, squareSize) 69 | 70 | return elements 71 | } 72 | 73 | // DrawInnerMosaicTile returns string representing an inner mosaic tile shape 74 | func DrawInnerMosaicTile(s *svg.SVG, x, y, triangleSize float64, values [2]float64) { 75 | triangle := BuildRightTriangle(triangleSize) 76 | opacity := utils.Opacity(values[0]) 77 | fill := utils.FillColor(values[0]) 78 | 79 | styles := make(map[string]interface{}) 80 | styles["fill"] = fill 81 | styles["fill-opacity"] = opacity 82 | styles["stroke"] = utils.StrokeColor 83 | styles["stroke-opacity"] = utils.StrokeOpacity 84 | 85 | style := make(map[string]interface{}) 86 | 87 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, 1)", x+triangleSize, y) 88 | s.Polyline(triangle, utils.Merge(styles, style)) 89 | 90 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(1, -1)", x+triangleSize, y+triangleSize*2) 91 | s.Polyline(triangle, utils.Merge(styles, style)) 92 | 93 | opacity = utils.Opacity(values[1]) 94 | fill = utils.FillColor(values[1]) 95 | 96 | styles["fill"] = fill 97 | styles["fill-opacity"] = opacity 98 | 99 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, -1)", x+triangleSize, y+triangleSize*2) 100 | s.Polyline(triangle, utils.Merge(styles, style)) 101 | 102 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(1, 1)", x+triangleSize, y) 103 | s.Polyline(triangle, utils.Merge(styles, style)) 104 | } 105 | 106 | // DrawOuterMosaicTile returns string representing an outer mosaic tile shape 107 | func DrawOuterMosaicTile(s *svg.SVG, x, y, triangleSize, value float64) { 108 | opacity := utils.Opacity(value) 109 | fill := utils.FillColor(value) 110 | triangle := BuildRightTriangle(triangleSize) 111 | 112 | styles := make(map[string]interface{}) 113 | styles["fill"] = fill 114 | styles["fill-opacity"] = opacity 115 | styles["stroke"] = utils.StrokeColor 116 | styles["stroke-opacity"] = utils.StrokeOpacity 117 | 118 | style := make(map[string]interface{}) 119 | 120 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(1, -1)", x, y+triangleSize) 121 | s.Polyline(triangle, utils.Merge(styles, style)) 122 | 123 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, -1)", x+triangleSize*2, y+triangleSize) 124 | s.Polyline(triangle, utils.Merge(styles, style)) 125 | 126 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(1, 1)", x, y+triangleSize) 127 | s.Polyline(triangle, utils.Merge(styles, style)) 128 | 129 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, 1)", x+triangleSize*2, y+triangleSize) 130 | s.Polyline(triangle, utils.Merge(styles, style)) 131 | } 132 | -------------------------------------------------------------------------------- /pattern/pattern.go: -------------------------------------------------------------------------------- 1 | // Package pattern implements methods to generate a new pattern 2 | // background for the pattern, type of pattern and particular SVG structure 3 | // for that pattern 4 | package pattern 5 | 6 | import ( 7 | "fmt" 8 | "github.com/lucasb-eyer/go-colorful" 9 | "github.com/pravj/geopattern/shapes" 10 | "github.com/pravj/geopattern/svg" 11 | "github.com/pravj/geopattern/utils" 12 | "math" 13 | "time" 14 | ) 15 | 16 | // All available geo patterns 17 | var PATTERNS = [16]string{ 18 | "chevrons", 19 | "concentric-circles", 20 | "diamonds", 21 | "hexagons", 22 | "mosaic-squares", 23 | "nested-squares", 24 | "octagons", 25 | "overlapping-circles", 26 | "overlapping-rings", 27 | "plaid", 28 | "plus-signs", 29 | "sine-waves", 30 | "squares", 31 | "tessellation", 32 | "triangles", 33 | "xes", 34 | } 35 | 36 | // Pattern struct that contains attributes like base color, background color 37 | // pattern type, phrase for the pattern and SHA-1 hash of phrase 38 | type Pattern struct { 39 | BaseColor string 40 | Color string 41 | Generator string 42 | Hash string 43 | Svg *svg.SVG 44 | } 45 | 46 | // New parses the arguments and returns an instance of Pattern struct that has linked 47 | // methods to work further things 48 | func New(args map[string]string) *Pattern { 49 | var phrase, generator, color, baseColor string 50 | 51 | phrase = fmt.Sprintf("%s", time.Now().Local()) 52 | 53 | if args["phrase"] != "" { 54 | phrase = args["phrase"] 55 | } 56 | 57 | if args["generator"] != "" { 58 | generator = args["generator"] 59 | } 60 | 61 | if args["color"] != "" { 62 | color = args["color"] 63 | } 64 | 65 | if args["baseColor"] != "" { 66 | baseColor = args["baseColor"] 67 | } 68 | 69 | return &Pattern{BaseColor: baseColor, Color: color, Generator: generator, Hash: utils.Hash(phrase), Svg: new(svg.SVG)} 70 | } 71 | 72 | // SvgStr returns string representing pattern's SVG string 73 | func (p *Pattern) SvgStr() string { 74 | p.generateBackground() 75 | p.genaratePattern() 76 | 77 | return p.Svg.Str() 78 | } 79 | 80 | // generateBackground decides on background color for the pattern. 81 | // 82 | // It uses 'color' or 'baseColor' arguments for this task. 83 | func (p *Pattern) generateBackground() { 84 | var rgb, color colorful.Color 85 | 86 | if p.Color != "" { 87 | rgb, _ = colorful.Hex(p.Color) 88 | } else { 89 | hueOffset := utils.Map(utils.HexVal(p.Hash, 14, 3), 0, 4095, 0, 359) 90 | satOffset := utils.Map(utils.HexVal(p.Hash, 17, 1), 0, 15, -1, 1) 91 | 92 | if p.BaseColor == "" { 93 | color, _ = colorful.Hex(utils.BaseColor) 94 | } else { 95 | color, _ = colorful.Hex(p.BaseColor) 96 | } 97 | 98 | h, c, l := color.Hcl() 99 | 100 | h -= hueOffset 101 | if satOffset >= 0 { 102 | c += float64(satOffset) 103 | } else { 104 | c -= float64(satOffset) 105 | } 106 | 107 | rgb = colorful.Color{h, c, l} 108 | } 109 | 110 | r, g, b := int(rgb.R*105), int(rgb.G*105), int(rgb.B*150) 111 | 112 | args := make(map[string]interface{}) 113 | args["fill"] = fmt.Sprintf("rgb(%v, %v, %v)", r, g, b) 114 | 115 | p.Svg.Rect(0, 0, "100%", "100%", args) 116 | } 117 | 118 | // isPattern decides whether a pattern is a valid one or not 119 | func isPattern(generator string) bool { 120 | for _, ptn := range PATTERNS { 121 | if ptn == generator { 122 | return true 123 | } 124 | } 125 | return false 126 | } 127 | 128 | // genaratePattern decides on type of pattern and build respective SVG object 129 | func (p *Pattern) genaratePattern() { 130 | if p.Generator == "" { 131 | p.Generator = PATTERNS[int(utils.HexVal(p.Hash, 20, 1))] 132 | } else { 133 | if !isPattern(p.Generator) { 134 | panic("Error: the requested generator is invalid.") 135 | } 136 | } 137 | 138 | switch p.Generator { 139 | case "chevrons": 140 | p.geoChevrons() 141 | case "concentric-circles": 142 | p.geoConcentricCircles() 143 | case "diamonds": 144 | p.geoDiamonds() 145 | case "hexagons": 146 | p.geoHexagons() 147 | case "mosaic-squares": 148 | p.geoMosaicSquares() 149 | case "nested-squares": 150 | p.geoNestedSquares() 151 | case "octagons": 152 | p.geoOctagons() 153 | case "overlapping-circles": 154 | p.geoOverlappingCircles() 155 | case "overlapping-rings": 156 | p.geoOverlappingRings() 157 | case "plaid": 158 | p.geoPlaid() 159 | case "plus-signs": 160 | p.geoPlusSigns() 161 | case "sine-waves": 162 | p.geoSineWaves() 163 | case "squares": 164 | p.geoSquares() 165 | case "tessellation": 166 | p.geoTessellation() 167 | case "triangles": 168 | p.geoTriangles() 169 | case "xes": 170 | p.geoXes() 171 | } 172 | } 173 | 174 | // geoChevrons build the chevrons SVG pattern 175 | func (p *Pattern) geoChevrons() { 176 | chevronWidth := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 30, 80) 177 | chevronHeight := chevronWidth 178 | chevron := shapes.BuildChevron(chevronWidth, chevronHeight) 179 | 180 | p.Svg.SetHeight(int(chevronHeight * 6 * 0.66)) 181 | p.Svg.SetWidth(int(chevronWidth * 6)) 182 | 183 | i := 0 184 | for y := 0; y <= 5; y++ { 185 | for x := 0; x <= 5; x++ { 186 | 187 | val := utils.HexVal(p.Hash, i, 1) 188 | opacity := utils.Opacity(val) 189 | fill := utils.FillColor(val) 190 | 191 | styles := make(map[string]interface{}) 192 | styles["fill"] = fill 193 | styles["fill-opacity"] = opacity 194 | styles["stroke"] = utils.StrokeColor 195 | styles["stroke-opacity"] = utils.StrokeOpacity 196 | styles["stroke-width"] = 1 197 | 198 | style := make(map[string]interface{}) 199 | 200 | style["transform"] = fmt.Sprintf("translate(%v, %v)", float64(x)*chevronWidth, float64(y)*chevronHeight*0.66-chevronHeight/2) 201 | p.Svg.Group(chevron, utils.Merge(styles, style)) 202 | 203 | if y == 0 { 204 | style["transform"] = fmt.Sprintf("translate(%v, %v)", float64(x)*chevronWidth, 6*chevronHeight*0.66-chevronHeight/2) 205 | p.Svg.Group(chevron, utils.Merge(styles, style)) 206 | } 207 | 208 | i = i + 1 209 | } 210 | } 211 | } 212 | 213 | // geoConcentricCircles build the concentric_circles SVG pattern 214 | func (p *Pattern) geoConcentricCircles() { 215 | scale := utils.HexVal(p.Hash, 0, 1) 216 | ringSize := utils.Map(scale, 0, 15, 10, 60) 217 | strokeWidth := ringSize / 5 218 | 219 | p.Svg.SetHeight(int((ringSize + strokeWidth) * 6)) 220 | p.Svg.SetWidth(int((ringSize + strokeWidth) * 6)) 221 | 222 | i := 0 223 | for y := 0; y <= 5; y++ { 224 | for x := 0; x <= 5; x++ { 225 | 226 | val := utils.HexVal(p.Hash, i, 1) 227 | opacity := utils.Opacity(val) 228 | fill := utils.FillColor(val) 229 | 230 | cx := float64(x)*ringSize + float64(x)*strokeWidth + (ringSize+strokeWidth)/2 231 | cy := float64(y)*ringSize + float64(y)*strokeWidth + (ringSize+strokeWidth)/2 232 | 233 | styles := make(map[string]interface{}) 234 | styles["fill"] = "none" 235 | styles["stroke"] = fill 236 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity), "stroke-width": fmt.Sprintf("%vpx", strokeWidth)} 237 | 238 | p.Svg.Circle(cx, cy, ringSize/2, styles) 239 | 240 | val = utils.HexVal(p.Hash, 39-i, 1) 241 | opacity = utils.Opacity(val) 242 | fill = utils.FillColor(val) 243 | 244 | styles = make(map[string]interface{}) 245 | styles["fill"] = fill 246 | styles["fill-opacity"] = opacity 247 | 248 | p.Svg.Circle(cx, cy, ringSize/4, styles) 249 | 250 | i = i + 1 251 | } 252 | } 253 | } 254 | 255 | // geoDiamonds build the diamonds SVG pattern 256 | func (p *Pattern) geoDiamonds() { 257 | diamondWidth := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 10, 50) 258 | diamondHeight := utils.Map(utils.HexVal(p.Hash, 1, 1), 0, 15, 10, 50) 259 | diamond := shapes.BuildDiamond(diamondWidth, diamondHeight) 260 | 261 | p.Svg.SetHeight(int(diamondHeight * 3)) 262 | p.Svg.SetWidth(int(diamondWidth * 6)) 263 | 264 | i := 0 265 | for y := 0; y <= 5; y++ { 266 | for x := 0; x <= 5; x++ { 267 | 268 | val := utils.HexVal(p.Hash, i, 1) 269 | opacity := utils.Opacity(val) 270 | fill := utils.FillColor(val) 271 | 272 | styles := make(map[string]interface{}) 273 | styles["fill"] = fill 274 | styles["fill-opacity"] = opacity 275 | styles["stroke"] = utils.StrokeColor 276 | styles["stroke-opacity"] = utils.StrokeOpacity 277 | 278 | var dx float64 279 | if y%2 != 0 { 280 | dx = diamondWidth / 2 281 | } 282 | 283 | style := make(map[string]interface{}) 284 | 285 | style["transform"] = fmt.Sprintf("translate(%v, %v)", dx + float64(x)*diamondWidth - diamondWidth/2, diamondHeight/2*float64(y) - diamondHeight/2) 286 | p.Svg.Polyline(diamond, utils.Merge(styles, style)) 287 | 288 | if x == 0 { 289 | style["transform"] = fmt.Sprintf("translate(%v, %v)", dx + 6*diamondWidth - diamondWidth/2, diamondHeight/2*float64(y) - diamondHeight/2) 290 | p.Svg.Polyline(diamond, utils.Merge(styles, style)) 291 | } 292 | 293 | if y == 0 { 294 | style["transform"] = fmt.Sprintf("translate(%v, %v)", dx + float64(x)*diamondWidth - diamondWidth/2, diamondHeight/2*6 - diamondHeight/2) 295 | p.Svg.Polyline(diamond, utils.Merge(styles, style)) 296 | } 297 | 298 | if x == 0 && y == 0 { 299 | style["transform"] = fmt.Sprintf("translate(%v, %v)", dx + 6*diamondWidth - diamondWidth/2, diamondHeight/2*6 - diamondHeight/2) 300 | p.Svg.Polyline(diamond, utils.Merge(styles, style)) 301 | } 302 | 303 | i = i + 1 304 | } 305 | } 306 | } 307 | 308 | // geoHexagons build the hexagons SVG pattern 309 | func (p *Pattern) geoHexagons() { 310 | scale := utils.HexVal(p.Hash, 0, 1) 311 | sideLength := utils.Map(scale, 0, 15, 8, 60) 312 | hexHeight := sideLength * math.Sqrt(3) 313 | hexWidth := sideLength * 2 314 | hex := shapes.BuildHexagon(sideLength) 315 | 316 | p.Svg.SetHeight(int(hexHeight * 6)) 317 | p.Svg.SetWidth(int((hexWidth * 3) + (sideLength * 3))) 318 | 319 | i := 0 320 | for y := 0; y <= 5; y++ { 321 | for x := 0; x <= 5; x++ { 322 | 323 | val := utils.HexVal(p.Hash, i, 1) 324 | var dy float64 325 | if x%2 == 0 { 326 | dy = float64(y) * hexHeight 327 | } else { 328 | dy = float64(y)*hexHeight + hexHeight/2 329 | } 330 | opacity := utils.Opacity(val) 331 | fill := utils.FillColor(val) 332 | 333 | styles := make(map[string]interface{}) 334 | styles["fill"] = fill 335 | styles["fill-opacity"] = opacity 336 | styles["stroke"] = utils.StrokeColor 337 | styles["stroke-opacity"] = utils.StrokeOpacity 338 | 339 | style := make(map[string]interface{}) 340 | 341 | style["transform"] = fmt.Sprintf("translate(%v, %v)", float64(x)*sideLength*1.5-hexWidth/2, dy-hexHeight/2) 342 | p.Svg.Polyline(hex, utils.Merge(styles, style)) 343 | 344 | if x == 0 { 345 | style["transform"] = fmt.Sprintf("translate(%v, %v)", 6*sideLength*1.5-hexWidth/2, dy-hexHeight/2) 346 | p.Svg.Polyline(hex, utils.Merge(styles, style)) 347 | } 348 | 349 | if y == 0 { 350 | if x%2 == 0 { 351 | dy = 6 * hexHeight 352 | } else { 353 | dy = 6*hexHeight + hexHeight/2 354 | } 355 | style["transform"] = fmt.Sprintf("translate(%v, %v)", float64(x)*sideLength*1.5-hexWidth/2, dy-hexHeight/2) 356 | p.Svg.Polyline(hex, utils.Merge(styles, style)) 357 | } 358 | 359 | if x == 0 && y == 0 { 360 | style["transform"] = fmt.Sprintf("translate(%v, %v)", 6*sideLength*1.5-hexWidth/2, 5*hexHeight+hexHeight/2) 361 | p.Svg.Polyline(hex, utils.Merge(styles, style)) 362 | } 363 | 364 | i = i + 1 365 | } 366 | } 367 | } 368 | 369 | // geoMosaicSquares build the mosaic_squares SVG pattern 370 | func (p *Pattern) geoMosaicSquares() { 371 | triangleSize := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 15, 50) 372 | 373 | p.Svg.SetHeight(int(triangleSize * 8)) 374 | p.Svg.SetWidth(int(triangleSize * 8)) 375 | 376 | i := 0 377 | for y := 0; y <= 3; y++ { 378 | for x := 0; x <= 3; x++ { 379 | 380 | values := [2]float64{utils.HexVal(p.Hash, i, 1), utils.HexVal(p.Hash, i+1, 1)} 381 | 382 | if x%2 == 0 { 383 | if y%2 == 0 { 384 | shapes.DrawOuterMosaicTile(p.Svg, float64(x)*triangleSize*2, float64(y)*triangleSize*2, triangleSize, utils.HexVal(p.Hash, i, 1)) 385 | } else { 386 | shapes.DrawInnerMosaicTile(p.Svg, float64(x)*triangleSize*2, float64(y)*triangleSize*2, triangleSize, values) 387 | } 388 | } else { 389 | if y%2 == 0 { 390 | shapes.DrawInnerMosaicTile(p.Svg, float64(x)*triangleSize*2, float64(y)*triangleSize*2, triangleSize, values) 391 | } else { 392 | shapes.DrawOuterMosaicTile(p.Svg, float64(x)*triangleSize*2, float64(y)*triangleSize*2, triangleSize, utils.HexVal(p.Hash, i, 1)) 393 | } 394 | } 395 | 396 | i = i + 1 397 | } 398 | } 399 | 400 | } 401 | 402 | // geoNestedSquares build the nested_squares SVG pattern 403 | func (p *Pattern) geoNestedSquares() { 404 | blockSize := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 4, 12) 405 | squareSize := blockSize * 7 406 | 407 | p.Svg.SetHeight(int((squareSize+blockSize)*6 + blockSize*6)) 408 | p.Svg.SetWidth(int((squareSize+blockSize)*6 + blockSize*6)) 409 | 410 | i := 0 411 | for y := 0; y <= 5; y++ { 412 | for x := 0; x <= 5; x++ { 413 | 414 | val := utils.HexVal(p.Hash, i, 1) 415 | opacity := utils.Opacity(val) 416 | fill := utils.FillColor(val) 417 | 418 | styles := make(map[string]interface{}) 419 | styles["fill"] = "none" 420 | styles["stroke"] = fill 421 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity), "stroke-width": fmt.Sprintf("%vpx", blockSize)} 422 | 423 | p.Svg.Rect(float64(x)*squareSize+float64(x)*blockSize*2+blockSize/2, float64(y)*squareSize+float64(y)*blockSize*2+blockSize/2, squareSize, squareSize, styles) 424 | 425 | val = utils.HexVal(p.Hash, 39-i, 1) 426 | opacity = utils.Opacity(val) 427 | fill = utils.FillColor(val) 428 | 429 | styles = make(map[string]interface{}) 430 | styles["fill"] = "none" 431 | styles["stroke"] = fill 432 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity), "stroke-width": fmt.Sprintf("%vpx", blockSize)} 433 | 434 | p.Svg.Rect(float64(x)*squareSize+float64(x)*blockSize*2+blockSize/2+blockSize*2, float64(y)*squareSize+float64(y)*blockSize*2+blockSize/2+blockSize*2, blockSize*3, blockSize*3, styles) 435 | 436 | i = i + 1 437 | } 438 | } 439 | } 440 | 441 | // geoOctagons build the octagons SVG pattern 442 | func (p *Pattern) geoOctagons() { 443 | squareSize := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 10, 60) 444 | tile := shapes.BuildOctagon(squareSize) 445 | 446 | p.Svg.SetHeight(int(squareSize * 6)) 447 | p.Svg.SetWidth(int(squareSize * 6)) 448 | 449 | i := 0 450 | for y := 0; y <= 5; y++ { 451 | for x := 0; x <= 5; x++ { 452 | val := utils.HexVal(p.Hash, i, 1) 453 | opacity := utils.Opacity(val) 454 | fill := utils.FillColor(val) 455 | 456 | styles := make(map[string]interface{}) 457 | styles["fill"] = fill 458 | styles["fill-opacity"] = opacity 459 | styles["stroke"] = utils.StrokeColor 460 | styles["stroke-opacity"] = utils.StrokeOpacity 461 | styles["transform"] = fmt.Sprintf("translate(%v, %v)", float64(x)*squareSize, float64(y)*squareSize) 462 | 463 | p.Svg.Polyline(tile, styles) 464 | 465 | i = i + 1 466 | } 467 | } 468 | } 469 | 470 | // geoOverlappingCircles build the overlapping_circles SVG pattern 471 | func (p *Pattern) geoOverlappingCircles() { 472 | scale := utils.HexVal(p.Hash, 0, 1) 473 | diameter := utils.Map(scale, 0, 15, 25, 200) 474 | radius := diameter / 2 475 | 476 | p.Svg.SetHeight(int(radius * 6)) 477 | p.Svg.SetWidth(int(radius * 6)) 478 | 479 | i := 0 480 | for y := 0; y <= 5; y++ { 481 | for x := 0; x <= 5; x++ { 482 | 483 | val := utils.HexVal(p.Hash, i, 1) 484 | opacity := utils.Opacity(val) 485 | fill := utils.FillColor(val) 486 | 487 | styles := make(map[string]interface{}) 488 | styles["fill"] = fill 489 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity)} 490 | 491 | p.Svg.Circle(float64(x)*radius, float64(y)*radius, radius, styles) 492 | 493 | if x == 0 { 494 | p.Svg.Circle(6*radius, float64(y)*radius, radius, styles) 495 | } 496 | 497 | if y == 0 { 498 | p.Svg.Circle(float64(x)*radius, 6*radius, radius, styles) 499 | } 500 | 501 | if x == 0 && y == 0 { 502 | p.Svg.Circle(6*radius, 6*radius, radius, styles) 503 | } 504 | 505 | i = i + 1 506 | } 507 | } 508 | } 509 | 510 | // geoOverlappingRings build the overlapping_rings SVG pattern 511 | func (p *Pattern) geoOverlappingRings() { 512 | scale := utils.HexVal(p.Hash, 0, 1) 513 | ringSize := utils.Map(scale, 0, 15, 10, 60) 514 | strokeWidth := ringSize / 4 515 | 516 | p.Svg.SetHeight(int(ringSize * 6)) 517 | p.Svg.SetWidth(int(ringSize * 6)) 518 | 519 | i := 0 520 | for y := 0; y <= 5; y++ { 521 | for x := 0; x <= 5; x++ { 522 | 523 | val := utils.HexVal(p.Hash, i, 1) 524 | opacity := utils.Opacity(val) 525 | fill := utils.FillColor(val) 526 | 527 | styles := make(map[string]interface{}) 528 | styles["fill"] = "none" 529 | styles["stroke"] = fill 530 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity), "stroke-width": fmt.Sprintf("%vpx", strokeWidth)} 531 | 532 | p.Svg.Circle(float64(x)*ringSize, float64(y)*ringSize, ringSize-(strokeWidth/2), styles) 533 | 534 | if x == 0 { 535 | p.Svg.Circle(6*ringSize, float64(y)*ringSize, ringSize-(strokeWidth/2), styles) 536 | } 537 | 538 | if y == 0 { 539 | p.Svg.Circle(float64(x)*ringSize, 6*ringSize, ringSize-(strokeWidth/2), styles) 540 | } 541 | 542 | if x == 0 && y == 0 { 543 | p.Svg.Circle(6*ringSize, 6*ringSize, ringSize-(strokeWidth/2), styles) 544 | } 545 | 546 | i = i + 1 547 | } 548 | } 549 | } 550 | 551 | // geoPlaid build the plaid SVG pattern 552 | func (p *Pattern) geoPlaid() { 553 | height := 0 554 | width := 0 555 | 556 | i := 1 557 | j := 0 558 | for i <= 18 { 559 | 560 | space := utils.HexVal(p.Hash, j, 1) 561 | height = height + int(space) + 5 562 | 563 | val := utils.HexVal(p.Hash, j+1, 1) 564 | opacity := utils.Opacity(val) 565 | fill := utils.FillColor(val) 566 | stripeHeight := val + 5 567 | 568 | styles := make(map[string]interface{}) 569 | styles["opacity"] = opacity 570 | styles["fill"] = fill 571 | 572 | p.Svg.Rect(0, height, "100%", stripeHeight, styles) 573 | 574 | height = height + int(stripeHeight) 575 | j = j + 2 576 | 577 | i = i + 1 578 | } 579 | 580 | i = 1 581 | j = 0 582 | for i <= 18 { 583 | 584 | space := utils.HexVal(p.Hash, j, 1) 585 | width = width + int(space) + 5 586 | 587 | val := utils.HexVal(p.Hash, j+1, 1) 588 | opacity := utils.Opacity(val) 589 | fill := utils.FillColor(val) 590 | stripeWidth := val + 5 591 | 592 | styles := make(map[string]interface{}) 593 | styles["opacity"] = opacity 594 | styles["fill"] = fill 595 | 596 | p.Svg.Rect(width, 0, stripeWidth, "100%", styles) 597 | 598 | width = width + int(stripeWidth) 599 | j = j + 2 600 | 601 | i = i + 1 602 | } 603 | 604 | p.Svg.SetHeight(int(height)) 605 | p.Svg.SetWidth(int(width)) 606 | } 607 | 608 | // geoPlusSigns build the plus_signs SVG pattern 609 | func (p *Pattern) geoPlusSigns() { 610 | squareSize := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 10, 25) 611 | plusSize := squareSize * 3 612 | plusShape := shapes.BuildPlus(squareSize) 613 | 614 | p.Svg.SetHeight(int(squareSize * 12)) 615 | p.Svg.SetWidth(int(squareSize * 12)) 616 | 617 | i := 0 618 | for y := 0; y <= 5; y++ { 619 | for x := 0; x <= 5; x++ { 620 | 621 | val := utils.HexVal(p.Hash, i, 1) 622 | opacity := utils.Opacity(val) 623 | fill := utils.FillColor(val) 624 | 625 | var dx float64 626 | if y%2 != 0 { 627 | dx = 1 628 | } 629 | 630 | styles := make(map[string]interface{}) 631 | styles["fill"] = fill 632 | styles["stroke"] = utils.StrokeColor 633 | styles["stroke-opacity"] = utils.StrokeOpacity 634 | styles["style"] = map[string]string{"fill-opacity": fmt.Sprintf("%v", opacity)} 635 | 636 | style := make(map[string]interface{}) 637 | 638 | style["transform"] = fmt.Sprintf("translate(%v,%v)", float64(x)*(plusSize-squareSize)+dx*squareSize-squareSize, float64(y)*(plusSize-squareSize)-plusSize/2) 639 | p.Svg.Group(plusShape, utils.Merge(styles, style)) 640 | 641 | if x == 0 { 642 | style["transform"] = fmt.Sprintf("translate(%v,%v)", 4*plusSize-float64(x)*squareSize+dx*squareSize-squareSize, float64(y)*(plusSize-squareSize)-plusSize/2) 643 | p.Svg.Group(plusShape, utils.Merge(styles, style)) 644 | } 645 | 646 | if y == 0 { 647 | style["transform"] = fmt.Sprintf("translate(%v,%v)", float64(x)*(plusSize-squareSize)+dx*squareSize-squareSize, 4*(plusSize)-float64(y)*squareSize-plusSize/2) 648 | p.Svg.Group(plusShape, utils.Merge(styles, style)) 649 | } 650 | 651 | if x == 0 && y == 0 { 652 | style["transform"] = fmt.Sprintf("translate(%v,%v)", 4*plusSize-float64(x)*squareSize+dx*squareSize-squareSize, 4*plusSize-float64(y)*squareSize-plusSize/2) 653 | p.Svg.Group(plusShape, utils.Merge(styles, style)) 654 | } 655 | 656 | i = i + 1 657 | } 658 | } 659 | } 660 | 661 | // geoSineWaves build the sine_waves SVG pattern 662 | func (p *Pattern) geoSineWaves() { 663 | period := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 100, 400) 664 | amplitude := utils.Map(utils.HexVal(p.Hash, 1, 1), 0, 15, 30, 100) 665 | waveWidth := utils.Map(utils.HexVal(p.Hash, 2, 1), 0, 15, 3, 30) 666 | 667 | p.Svg.SetHeight(int(waveWidth * 36)) 668 | p.Svg.SetWidth(int(period)) 669 | 670 | for i := 0; i <= 35; i++ { 671 | val := utils.HexVal(p.Hash, i, 1) 672 | opacity := utils.Opacity(val) 673 | fill := utils.FillColor(val) 674 | xOffset := (period / 4) * 0.7 675 | 676 | styles := make(map[string]interface{}) 677 | styles["fill"] = "none" 678 | styles["stroke"] = fill 679 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity), "stroke-width": fmt.Sprintf("%vpx", waveWidth)} 680 | 681 | str := fmt.Sprintf("M0 %v C %v 0, %v 0, %v %v S %v %v, %v %v S %v 0, %v, %v", amplitude, xOffset, period/2-xOffset, period/2, amplitude, period-xOffset, amplitude*2, period, amplitude, period*1.5-xOffset, period*1.5, amplitude) 682 | 683 | style := make(map[string]interface{}) 684 | 685 | style["transform"] = fmt.Sprintf("translate(-%v, %v)", period/4, (waveWidth*float64(i))-(amplitude*1.5)) 686 | p.Svg.Path(str, utils.Merge(styles, style)) 687 | 688 | style["transform"] = fmt.Sprintf("translate(-%v, %v)", period/4, (waveWidth*float64(i))-(amplitude*1.5)+waveWidth*36) 689 | p.Svg.Path(str, utils.Merge(styles, style)) 690 | } 691 | } 692 | 693 | // geoSquares build the squares SVG pattern 694 | func (p *Pattern) geoSquares() { 695 | squareSize := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 10, 60) 696 | 697 | p.Svg.SetHeight(int(squareSize * 6)) 698 | p.Svg.SetWidth(int(squareSize * 6)) 699 | 700 | i := 0 701 | for y := 0; y <= 5; y++ { 702 | for x := 0; x <= 5; x++ { 703 | 704 | val := utils.HexVal(p.Hash, i, 1) 705 | opacity := utils.Opacity(val) 706 | fill := utils.FillColor(val) 707 | 708 | styles := make(map[string]interface{}) 709 | styles["fill"] = fill 710 | styles["fill-opacity"] = opacity 711 | styles["stroke"] = utils.StrokeColor 712 | styles["stroke-opacity"] = utils.StrokeOpacity 713 | 714 | p.Svg.Rect(float64(x)*squareSize, float64(y)*squareSize, squareSize, squareSize, styles) 715 | 716 | i = i + 1 717 | } 718 | } 719 | } 720 | 721 | // geoTessellation build the tessellation SVG pattern 722 | func (p *Pattern) geoTessellation() { 723 | sideLength := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 5, 40) 724 | hexHeight := sideLength * math.Sqrt(3) 725 | hexWidth := sideLength * 2 726 | triangleHeight := sideLength / 2 * math.Sqrt(3) 727 | triangle := shapes.BuildRotatedTriangle(sideLength, triangleHeight) 728 | tileWidth := sideLength*3 + triangleHeight*2 729 | tileHeight := (hexHeight * 2) + (sideLength * 2) 730 | 731 | p.Svg.SetHeight(int(tileHeight)) 732 | p.Svg.SetWidth(int(tileWidth)) 733 | 734 | for i := 0; i <= 19; i++ { 735 | val := utils.HexVal(p.Hash, i, 1) 736 | opacity := utils.Opacity(val) 737 | fill := utils.FillColor(val) 738 | 739 | styles := make(map[string]interface{}) 740 | styles["fill"] = fill 741 | styles["fill-opacity"] = opacity 742 | styles["stroke"] = utils.StrokeColor 743 | styles["stroke-opacity"] = utils.StrokeOpacity 744 | styles["stroke-width"] = 1 745 | 746 | style := make(map[string]interface{}) 747 | 748 | switch i { 749 | case 0: 750 | p.Svg.Rect(-sideLength/2, -sideLength/2, sideLength, sideLength, styles) 751 | p.Svg.Rect(tileWidth-sideLength/2, -sideLength/2, sideLength, sideLength, styles) 752 | p.Svg.Rect(-sideLength/2, tileHeight-sideLength/2, sideLength, sideLength, styles) 753 | p.Svg.Rect(tileWidth-sideLength/2, tileHeight-sideLength/2, sideLength, sideLength, styles) 754 | case 1: 755 | p.Svg.Rect(hexWidth/2+triangleHeight, hexHeight/2, sideLength, sideLength, styles) 756 | case 2: 757 | p.Svg.Rect(-sideLength/2, tileHeight/2-sideLength/2, sideLength, sideLength, styles) 758 | p.Svg.Rect(tileWidth-sideLength/2, tileHeight/2-sideLength/2, sideLength, sideLength, styles) 759 | case 3: 760 | p.Svg.Rect(hexWidth/2+triangleHeight, hexHeight*1.5+sideLength, sideLength, sideLength, styles) 761 | case 4: 762 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(0, %v, %v)", sideLength/2, -sideLength/2, sideLength/2, triangleHeight/2) 763 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 764 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(0, %v, %v) scale(1, -1)", sideLength/2, tileHeight-(-sideLength/2), sideLength/2, triangleHeight/2) 765 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 766 | case 5: 767 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(0, %v, %v) scale(-1, 1)", tileWidth-sideLength/2, -sideLength/2, sideLength/2, triangleHeight/2) 768 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 769 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(0, %v, %v) scale(-1, -1)", tileWidth-sideLength/2, tileHeight+sideLength/2, sideLength/2, triangleHeight/2) 770 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 771 | case 6: 772 | style["transform"] = fmt.Sprintf("translate(%v, %v)", tileWidth/2+sideLength/2, hexHeight/2) 773 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 774 | case 7: 775 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, 1)", tileWidth-tileWidth/2-sideLength/2, hexHeight/2) 776 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 777 | case 8: 778 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(1, -1)", tileWidth/2+sideLength/2, tileHeight-hexHeight/2) 779 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 780 | case 9: 781 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, -1)", tileWidth-tileWidth/2-sideLength/2, tileHeight-hexHeight/2) 782 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 783 | case 10: 784 | style["transform"] = fmt.Sprintf("translate(%v, %v)", sideLength/2, tileHeight/2-sideLength/2) 785 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 786 | case 11: 787 | style["transform"] = fmt.Sprintf("translate(%v, %v) scale(-1, 1)", tileWidth-sideLength/2, tileHeight/2-sideLength/2) 788 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 789 | case 12: 790 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(-30, 0, 0)", sideLength/2, sideLength/2) 791 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 792 | case 13: 793 | style["transform"] = fmt.Sprintf("scale(-1, 1) translate(%v, %v) rotate(-30, 0, 0)", -tileWidth+sideLength/2, sideLength/2) 794 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 795 | case 14: 796 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(30, 0, %v)", sideLength/2, tileHeight/2-sideLength/2-sideLength, sideLength) 797 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 798 | case 15: 799 | style["transform"] = fmt.Sprintf("scale(-1, 1) translate(%v, %v) rotate(30, 0, %v)", -tileWidth+sideLength/2, tileHeight/2-sideLength/2-sideLength, sideLength) 800 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 801 | case 16: 802 | style["transform"] = fmt.Sprintf("scale(1, -1) translate(%v, %v) rotate(30, 0, %v)", sideLength/2, -tileHeight+tileHeight/2-sideLength/2-sideLength, sideLength) 803 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 804 | case 17: 805 | style["transform"] = fmt.Sprintf("scale(-1, -1) translate(%v, %v) rotate(30, 0, %v)", -tileWidth+sideLength/2, -tileHeight+tileHeight/2-sideLength/2-sideLength, sideLength) 806 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 807 | case 18: 808 | style["transform"] = fmt.Sprintf("scale(1, -1) translate(%v, %v) rotate(-30, 0, 0)", sideLength/2, -tileHeight+sideLength/2) 809 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 810 | case 19: 811 | style["transform"] = fmt.Sprintf("scale(-1, -1) translate(%v, %v) rotate(-30, 0, 0)", -tileWidth+sideLength/2, -tileHeight+sideLength/2) 812 | p.Svg.Rect(0, 0, sideLength, sideLength, utils.Merge(styles, style)) 813 | } 814 | } 815 | } 816 | 817 | // geoTriangles build the triangles SVG pattern 818 | func (p *Pattern) geoTriangles() { 819 | scale := utils.HexVal(p.Hash, 0, 1) 820 | sideLength := utils.Map(scale, 0, 15, 15, 80) 821 | triangleHeight := sideLength / 2 * math.Sqrt(3) 822 | triangle := shapes.BuildTriangle(sideLength, triangleHeight) 823 | 824 | p.Svg.SetHeight(int(triangleHeight * 6)) 825 | p.Svg.SetWidth(int(sideLength * 3)) 826 | 827 | i := 0 828 | for y := 0; y <= 5; y++ { 829 | for x := 0; x <= 5; x++ { 830 | 831 | val := utils.HexVal(p.Hash, i, 1) 832 | opacity := utils.Opacity(val) 833 | fill := utils.FillColor(val) 834 | 835 | styles := make(map[string]interface{}) 836 | styles["fill"] = fill 837 | styles["fill-opacity"] = opacity 838 | styles["stroke"] = utils.StrokeColor 839 | styles["stroke-opacity"] = utils.StrokeOpacity 840 | 841 | var rotation int 842 | if y%2 == 0 { 843 | if x%2 == 0 { 844 | rotation = 180 845 | } 846 | } else { 847 | if x%2 != 0 { 848 | rotation = 180 849 | } 850 | } 851 | 852 | style := make(map[string]interface{}) 853 | 854 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(%v, %v, %v)", float64(x)*sideLength*0.5-sideLength/2, triangleHeight*float64(y), rotation, sideLength/2, triangleHeight/2) 855 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 856 | 857 | if x == 0 { 858 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(%v, %v, %v)", 6*sideLength*0.5-sideLength/2, triangleHeight*float64(y), rotation, sideLength/2, triangleHeight/2) 859 | p.Svg.Polyline(triangle, utils.Merge(styles, style)) 860 | } 861 | 862 | i = i + 1 863 | } 864 | } 865 | } 866 | 867 | // geoXes build the xes SVG pattern 868 | func (p *Pattern) geoXes() { 869 | squareSize := utils.Map(utils.HexVal(p.Hash, 0, 1), 0, 15, 10, 25) 870 | xShape := shapes.BuildPlus(squareSize) 871 | xSize := squareSize * 3 * 0.943 872 | 873 | p.Svg.SetHeight(int(xSize * 3)) 874 | p.Svg.SetWidth(int(xSize * 3)) 875 | 876 | i := 0 877 | for y := 0; y <= 5; y++ { 878 | for x := 0; x <= 5; x++ { 879 | 880 | val := utils.HexVal(p.Hash, i, 1) 881 | opacity := utils.Opacity(val) 882 | fill := utils.FillColor(val) 883 | 884 | var dy float64 885 | if x%2 == 0 { 886 | dy = float64(y)*xSize - xSize*0.5 887 | } else { 888 | dy = float64(y)*xSize - xSize*0.5 + xSize/4 889 | } 890 | 891 | styles := make(map[string]interface{}) 892 | styles["fill"] = fill 893 | styles["style"] = map[string]string{"opacity": fmt.Sprintf("%v", opacity)} 894 | 895 | style := make(map[string]interface{}) 896 | 897 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(45, %v, %v)", float64(x)*xSize/2-xSize/2, dy-float64(y)*xSize/2, xSize/2, xSize/2) 898 | p.Svg.Group(xShape, utils.Merge(styles, style)) 899 | 900 | if x == 0 { 901 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(45, %v, %v)", 6*xSize/2-xSize/2, dy-float64(y)*xSize/2, xSize/2, xSize/2) 902 | p.Svg.Group(xShape, utils.Merge(styles, style)) 903 | } 904 | 905 | if y == 0 { 906 | if x%2 == 0 { 907 | dy = 6*xSize - xSize/2 908 | } else { 909 | dy = 6*xSize - xSize/2 + xSize/4 910 | } 911 | 912 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(45, %v, %v)", float64(x)*xSize/2-xSize/2, dy-float64(y)*xSize/2, xSize/2, xSize/2) 913 | p.Svg.Group(xShape, utils.Merge(styles, style)) 914 | } 915 | 916 | if y == 5 { 917 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(45, %v, %v)", float64(x)*xSize/2-xSize/2, dy-11*xSize/2, xSize/2, xSize/2) 918 | p.Svg.Group(xShape, utils.Merge(styles, style)) 919 | } 920 | 921 | if x == 0 && y == 0 { 922 | style["transform"] = fmt.Sprintf("translate(%v, %v) rotate(45, %v, %v)", 6*xSize/2-xSize/2, dy-6*xSize/2, xSize/2, xSize/2) 923 | p.Svg.Group(xShape, utils.Merge(styles, style)) 924 | } 925 | 926 | i = i + 1 927 | } 928 | } 929 | } 930 | --------------------------------------------------------------------------------