├── 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(""
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 | [](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 | 
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 | 
79 |
80 | #### concentric-circles
81 | 
82 |
83 | #### diamonds
84 | 
85 |
86 | #### hexagons
87 | 
88 |
89 | #### mosaic-squares
90 | 
91 |
92 | #### nested-squares
93 | 
94 |
95 | #### octagons
96 | 
97 |
98 | #### overlapping-circles
99 | 
100 |
101 | #### overlapping-rings
102 | 
103 |
104 | #### plaid
105 | 
106 |
107 | #### plus-signs
108 | 
109 |
110 | #### sine-waves
111 | 
112 |
113 | #### squares
114 | 
115 |
116 | #### tessellation
117 | 
118 |
119 | #### triangles
120 | 
121 |
122 | #### xes
123 | 
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 |
--------------------------------------------------------------------------------