~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/gopkg.in/juju/jujusvg.v1/canvas.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
 
15
15
const (
16
16
        iconSize           = 96
17
 
        serviceBlockSize   = 189
18
 
        healthCircleRadius = 10
19
 
        relationLineWidth  = 2
 
17
        serviceBlockSize   = 180
 
18
        healthCircleRadius = 8
 
19
        relationLineWidth  = 1
20
20
        maxInt             = int(^uint(0) >> 1)
21
21
        minInt             = -(maxInt - 1)
22
22
        maxHeight          = 450
23
23
        maxWidth           = 1000
24
24
 
25
25
        fontColor     = "#505050"
26
 
        relationColor = "#38B44A"
 
26
        relationColor = "#a7a7a7"
27
27
)
28
28
 
29
29
// Canvas holds the parsed form of a bundle or environment.
46
46
 
47
47
// serviceRelation represents a relation created between two services.
48
48
type serviceRelation struct {
 
49
        name     string
49
50
        serviceA *service
50
51
        serviceB *service
51
52
}
70
71
 
71
72
// usage creates any necessary tags for actually using the service in the SVG.
72
73
func (s *service) usage(canvas *svg.SVG, iconIds map[string]string) {
 
74
        canvas.Group(fmt.Sprintf(`transform="translate(%d,%d)"`, s.point.X, s.point.Y))
 
75
        defer canvas.Gend()
 
76
        canvas.Title(s.name)
 
77
        canvas.Circle(
 
78
                serviceBlockSize/2,
 
79
                serviceBlockSize/2,
 
80
                serviceBlockSize/2,
 
81
                `class="service-block" fill="#f5f5f5" stroke="#888" stroke-width="1"`)
 
82
        canvas.Circle(
 
83
                serviceBlockSize/2-iconSize/2+5, // for these two, add an offset to help
 
84
                serviceBlockSize/2-iconSize/2+7, // hide the embossed border.
 
85
                serviceBlockSize/4,
 
86
                `id="service-icon-mask-`+s.name+`" fill="none"`)
 
87
        canvas.ClipPath(`id="clip-` + s.name + `"`)
73
88
        canvas.Use(
74
 
                s.point.X,
75
 
                s.point.Y,
76
 
                "#serviceBlock",
77
 
                fmt.Sprintf(`id=%q`, s.name))
 
89
                0,
 
90
                0,
 
91
                `#service-icon-mask-`+s.name)
 
92
        canvas.ClipEnd()
78
93
        if len(s.iconSrc) > 0 {
79
94
                canvas.Use(
80
 
                        s.point.X+serviceBlockSize/2-iconSize/2,
81
 
                        s.point.Y+serviceBlockSize/2-iconSize/2,
 
95
                        0,
 
96
                        0,
82
97
                        "#"+iconIds[s.charmPath],
83
 
                        fmt.Sprintf(`width="%d" height="%d"`, iconSize, iconSize),
 
98
                        fmt.Sprintf(`transform="translate(%d,%d)" width="%d" height="%d" clip-path="url(#clip-%s)"`, serviceBlockSize/2-iconSize/2, serviceBlockSize/2-iconSize/2, iconSize, iconSize, s.name),
84
99
                )
85
100
        } else {
86
101
                canvas.Image(
87
 
                        s.point.X+serviceBlockSize/2-iconSize/2,
88
 
                        s.point.Y+serviceBlockSize/2-iconSize/2,
 
102
                        serviceBlockSize/2-iconSize/2,
 
103
                        serviceBlockSize/2-iconSize/2,
89
104
                        iconSize,
90
105
                        iconSize,
91
106
                        s.iconUrl,
 
107
                        `clip-path="url(#clip-`+s.name+`)"`,
92
108
                )
93
109
        }
94
 
        canvas.Textlines(
95
 
                s.point.X+serviceBlockSize/2,
96
 
                s.point.Y+serviceBlockSize/6,
97
 
                []string{s.name},
98
 
                serviceBlockSize/10,
 
110
        name := s.name
 
111
        if len(name) > 20 {
 
112
                name = fmt.Sprintf("%s...", name[:17])
 
113
        }
 
114
        canvas.Rect(
99
115
                0,
100
 
                "#505050",
101
 
                "middle")
 
116
                serviceBlockSize-45,
 
117
                serviceBlockSize,
 
118
                32,
 
119
                `rx="2" ry="2" fill="rgba(220, 220, 220, 0.8)"`)
 
120
        canvas.Text(
 
121
                serviceBlockSize/2,
 
122
                serviceBlockSize-23,
 
123
                name,
 
124
                `text-anchor="middle" style="font-weight:200"`)
102
125
}
103
126
 
104
127
// definition creates any necessary defs that can be used later in the SVG.
107
130
 
108
131
// usage creates any necessary tags for actually using the relation in the SVG.
109
132
func (r *serviceRelation) usage(canvas *svg.SVG) {
110
 
        l := r.shortestRelation()
 
133
        canvas.Group()
 
134
        defer canvas.Gend()
 
135
        canvas.Title(r.name)
 
136
        l := line{
 
137
                p0: r.serviceA.point.Add(point(serviceBlockSize/2, serviceBlockSize/2)),
 
138
                p1: r.serviceB.point.Add(point(serviceBlockSize/2, serviceBlockSize/2)),
 
139
        }
111
140
        canvas.Line(
112
141
                l.p0.X,
113
142
                l.p0.Y,
119
148
        )
120
149
        mid := l.p0.Add(l.p1).Div(2).Sub(point(healthCircleRadius, healthCircleRadius))
121
150
        canvas.Use(mid.X, mid.Y, "#healthCircle")
122
 
}
123
 
 
124
 
// shortestRelation finds the shortest line between two services, assuming
125
 
// that each service can be connected on one of four cardinal points only.
126
 
func (r *serviceRelation) shortestRelation() line {
127
 
        aConnectors, bConnectors := r.serviceA.cardinalPoints(), r.serviceB.cardinalPoints()
128
 
        shortestDistance := float64(maxInt)
129
 
        shortestPair := line{
130
 
                p0: r.serviceA.point,
131
 
                p1: r.serviceB.point,
132
 
        }
133
 
        for _, pointA := range aConnectors {
134
 
                for _, pointB := range bConnectors {
135
 
                        ab := line{p0: pointA, p1: pointB}
136
 
                        distance := ab.length()
137
 
                        if distance < shortestDistance {
138
 
                                shortestDistance = distance
139
 
                                shortestPair = ab
140
 
                        }
141
 
                }
142
 
        }
143
 
        return shortestPair
144
 
}
145
 
 
146
 
// cardinalPoints generates the points for each of the four cardinal points
147
 
// of each service.
148
 
func (s *service) cardinalPoints() []image.Point {
149
 
        return []image.Point{
150
 
                point(s.point.X+serviceBlockSize/2, s.point.Y),
151
 
                point(s.point.X, s.point.Y+serviceBlockSize/2),
152
 
                point(s.point.X+serviceBlockSize/2, s.point.Y+serviceBlockSize),
153
 
                point(s.point.X+serviceBlockSize, s.point.Y+serviceBlockSize/2),
154
 
        }
 
151
 
 
152
        deg := math.Atan2(float64(l.p0.Y-l.p1.Y), float64(l.p0.X-l.p1.X))
 
153
        canvas.Circle(
 
154
                int(float64(l.p0.X)-math.Cos(deg)*(serviceBlockSize/2)),
 
155
                int(float64(l.p0.Y)-math.Sin(deg)*(serviceBlockSize/2)),
 
156
                4,
 
157
                fmt.Sprintf(`fill=%q`, relationColor))
 
158
        canvas.Circle(
 
159
                int(float64(l.p1.X)+math.Cos(deg)*(serviceBlockSize/2)),
 
160
                int(float64(l.p1.Y)+math.Sin(deg)*(serviceBlockSize/2)),
 
161
                4,
 
162
                fmt.Sprintf(`fill=%q`, relationColor))
155
163
}
156
164
 
157
165
// strokeDashArray generates the stroke-dasharray attribute content so that
201
209
        for _, service := range c.services {
202
210
                service.point = service.point.Sub(point(minWidth, minHeight))
203
211
        }
204
 
        return abs(maxWidth-minWidth) + serviceBlockSize,
205
 
                abs(maxHeight-minHeight) + serviceBlockSize
 
212
        return abs(maxWidth-minWidth) + serviceBlockSize + 1,
 
213
                abs(maxHeight-minHeight) + serviceBlockSize + 1
206
214
}
207
215
 
208
216
func (c *Canvas) definition(canvas *svg.SVG) {
209
217
        canvas.Def()
210
218
        defer canvas.DefEnd()
211
219
 
212
 
        // Service block.
213
 
        canvas.Group(`id="serviceBlock"`,
214
 
                `transform="scale(0.8)"`)
215
 
        io.WriteString(canvas.Writer, assets.ServiceModule)
216
 
        canvas.Gend() // Gid
217
 
 
218
220
        // Relation health circle.
219
 
        canvas.Gid("healthCircle")
220
 
        canvas.Circle(
221
 
                healthCircleRadius,
222
 
                healthCircleRadius,
223
 
                healthCircleRadius,
224
 
                fmt.Sprintf("stroke:%s;fill:none;stroke-width:%dpx", relationColor, relationLineWidth),
225
 
        )
226
 
        canvas.Circle(
227
 
                healthCircleRadius,
228
 
                healthCircleRadius,
229
 
                healthCircleRadius/2,
230
 
                fmt.Sprintf("fill:%s", relationColor),
231
 
        )
 
221
        canvas.Group(`id="healthCircle"`,
 
222
                `transform="scale(1.1)"`)
 
223
        io.WriteString(canvas.Writer, assets.RelationIconHealthy)
232
224
        canvas.Gend()
233
225
 
234
226
        // Service and relation specific defs.