func New(w, h vg.Length) *Canvas { buf := new(bytes.Buffer) c := &Canvas{ svg: svgo.New(buf), w: w, h: h, buf: buf, ht: w.Points(), stk: []context{context{}}, } // This is like svg.Start, except it uses floats // and specifies the units. fmt.Fprintf(buf, `<?xml version="1.0"?> <!-- Generated by SVGo and Plotinum VG --> <svg width="%.*gin" height="%.*gin" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`+"\n", pr, w/vg.Inch, pr, h/vg.Inch, ) // Swap the origin to the bottom left. // This must be matched with a </g> when saving, // before the closing </svg>. c.svg.Gtransform(fmt.Sprintf("scale(1, -1) translate(0, -%.*g)", pr, h.Dots(c))) vg.Initialize(c) return c }
func drawSvgChart(w, h int, data []StockData, c *http.Conn) { // SVG-Ausgabe mit der HTTP-Verbindung als Stream initialisieren s := svg.New(c) // SVG-Objekt starten s.Start(w, h) // die horizontale Schrittweite pro Eintrag berechnen step := float(w) / float((len(data) + 1)) // das Minimum und das Maximum ermitteln min, max := minMax(data) diff := max - min // Jetzt über die Einträge iterieren ... var lastPoint Point for i, d := range data { // ... die Koordinaten berechnen... x := w - int(float(i)*step) y := int(float(h-1) - ((d.close-min)/diff)*float(h-1)) // ... und die Linie malen, falls es schon zwei Punkte gibt if i > 0 { s.Line(lastPoint.x, lastPoint.y, x, y, "width:1;stroke:blue") } // den berechneten Punkt als Startpunkt für die nächste Linie merken lastPoint = Point{x, y} } // SVG-Ausgabe beenden - fertig. s.End() }
func TestCalcCanvasSize_30x27(t *testing.T) { f, err := os.Create("test_page.svg") if err != nil { t.Errorf("failed to create test.svg : %s\n", err) } defer f.Close() canvas := svg.New(f) defer canvas.End() br := make([]rune, 100, 0x28FF) for i := 0; i < 100; i++ { switch { case i%5 == 0: br[i] = 0x2800 case i%37 == 0: br[i] = 0xa default: br[i] = brl.Rune(1, 2, 3, 4, 5, 6) } } bs := string(br) fmt.Println(bs) calcLines(bs, 30) DrawPage30(canvas, bs) }
// Marshal renders the SVG to the given io.Writer. func (c *Canvas) Marshal(w io.Writer) { // Initialize maps for service icons, which are used both in definition // and use methods for services. c.iconsRendered = make(map[string]bool) c.iconIds = make(map[string]string) // TODO check write errors and return an error from // Marshal if the write fails. The svg package does not // itself check or return write errors; a possible work-around // is to wrap the writer in a custom writer that panics // on error, and catch the panic here. width, height := c.layout() canvas := svg.New(w) canvas.Start( width, height, fmt.Sprintf(`style="font-family:Ubuntu, sans-serif;" viewBox="0 0 %d %d"`, width, height), ) defer canvas.End() c.definition(canvas) c.relationsGroup(canvas) c.servicesGroup(canvas) }
// API: POST /braille func brailleHandler(w http.ResponseWriter, r *http.Request) { src := r.FormValue("input") lang := r.FormValue("lang") if lang == "" { // TODO: lang이 없거나 auto이면 언어 판단해야함 lang = "ko" } format := r.FormValue("format") if format == "" { format = "svg" } w.Header().Set("Content-Type", "text/html; charset=utf-8") var bStr string if lang == "ko" { bStr, _ = brl_ko.Encode(src) } else if lang == "en" { bStr, _ = brl_en.Encode(src) } if format == "svg" { canvas := svg.New(w) defer canvas.End() brl_svg.DrawPage30(canvas, bStr) } else { fmt.Fprint(w, bStr) } }
func showMaze(p string, e Environment, hist []Line, pts []Point) error { // Determine image size var h, w float64 for _, line := range e.Lines { if line.A.X > w { w = line.A.X } if line.A.Y > h { h = line.A.Y } if line.B.X > w { w = line.B.X } if line.B.Y > h { h = line.B.Y } } // Create the image f, err := os.Create(p) if err != nil { return err } defer f.Close() img := svg.New(f) img.Start(int(w), int(h)) defer img.End() // Add the maze if len(hist) > 0 { img.Circle(int(hist[0].A.X), int(hist[0].A.Y), 4, `fill="green"`) // start } img.Circle(int(e.End.X), int(e.End.Y), 4, `fill="red"`) for _, line := range e.Lines { img.Path(fmt.Sprintf("M %f %f L %f %f", line.A.X, line.A.Y, line.B.X, line.B.Y), `stroke-width="1" stroke="black" fill="none"`) } for _, line := range hist { img.Path(fmt.Sprintf("M %f %f L %f %f", line.A.X, line.A.Y, line.B.X, line.B.Y), `stroke-width="1" stroke="blue" fill="none"`) } for _, point := range pts { img.Circle(int(point.X), int(point.Y), 1, `fill="green"`) } return nil }
func TestDraw(t *testing.T) { f, err := os.Create("test_label.svg") if err != nil { t.Errorf("failed to create test.svg : %s\n", err) } defer f.Close() canvas := svg.New(f) defer canvas.End() s := "Braille Printer" bs, _ := brl.Encode(s) DrawLabel(canvas, bs) }
// RandomGradientColorSVG builds a square image with with x colors selected at random for each quadrant. // the background color stays the same the other colors get mixed in a gradient color from the first one to the last one. func RandomGradientColorSVG(w http.ResponseWriter, colors, gColors []color.RGBA, gv colors.GradientVector, width, height, xsquares int, prob float64) { var gradientColors []svg.Offcolor gradientColors = make([]svg.Offcolor, len(gColors)) percentage := uint8(0) step := uint8(100 / len(gColors)) for i, c := range gColors { gradientColors[i] = svg.Offcolor{ Offset: percentage, Color: draw.RGBToHex(c.R, c.G, c.B), Opacity: 1, } percentage += step } canvas := svg.New(w) canvas.Start(width, height) canvas.Def() canvas.LinearGradient("gradientColors", gv.X1, gv.Y1, gv.X2, gv.Y2, gradientColors) canvas.DefEnd() canvas.Rect(0, 0, width, height, "fill:url(#gradientColors)") squares := xsquares quadrantSize := width / squares colorMap := make(map[int]color.RGBA) colorIndex := make(map[int]int) for yQ := 0; yQ < squares; yQ++ { y := yQ * quadrantSize colorMap = make(map[int]color.RGBA) colorIndex = make(map[int]int) for xQ := 0; xQ <= squares+1; xQ++ { x := xQ * quadrantSize if _, ok := colorMap[xQ]; !ok { colorIndex[xQ] = draw.RandomIndexFromArrayWithFreq(colors, prob) colorMap[xQ] = colors[colorIndex[xQ]] } if colorIndex[xQ] == 0 { fill := draw.FillFromRGBA(colorMap[xQ]) canvas.Rect(x, y, quadrantSize, quadrantSize, fill) } } } canvas.End() }
// API: POST /printq/add // input: text to translation // lang: auto|ko|en // key: examplekey (TODO: OAuth implementation) func printqAddHandler(w http.ResponseWriter, r *http.Request) { if strings.ToUpper(r.Method) != "POST" { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } /* var authKey string if strings.Contains(r.Referer(), "http://localhost") || strings.Contains(r.Referer(), "http://braille-printer.appspot.com") { authKey = EXAMPLE_AUTHKEY } else { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } */ authKey := r.FormValue("key") if authKey == "" { authKey = EXAMPLE_AUTHKEY } input := r.FormValue("input") lang := r.FormValue("lang") if lang == "" { // TODO: lang이 없거나 auto이면 언어 판단해야함 lang = "ko" } label := "label" if strings.Contains(input, "\n") { label = "paper" } var bStr string if lang == "ko" { bStr, _ = brl_ko.Encode(input) } else if lang == "en" { bStr, _ = brl_en.Encode(input) } buf := bytes.NewBuffer(make([]byte, 24288)) canvas := svg.New(buf) defer canvas.End() brl_svg.DrawPage30(canvas, bStr) printq := PrintQ{ Type: label, Key: authKey, Origin: input, ResultText: bStr, ResultSVG: buf.Bytes(), Status: 0, CTime: time.Now(), } c := appengine.NewContext(r) _, err := datastore.Put(c, datastore.NewIncompleteKey(c, "PrintQ", nil), &printq) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }
// Graph takes an OpenTSDB request data structure and queries OpenTSDB. Use the // json parameter to pass JSON. Use the b64 parameter to pass base64-encoded // JSON. func Graph(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) { j := []byte(r.FormValue("json")) if bs := r.FormValue("b64"); bs != "" { b, err := base64.StdEncoding.DecodeString(bs) if err != nil { return nil, err } j = b } if len(j) == 0 { return nil, fmt.Errorf("either json or b64 required") } oreq, err := opentsdb.RequestFromJSON(j) if err != nil { return nil, err } if ads_v := r.FormValue("autods"); ads_v != "" { ads_i, err := strconv.Atoi(ads_v) if err != nil { return nil, err } if err := oreq.AutoDownsample(ads_i); err != nil { return nil, err } } ar := make(map[int]bool) for _, v := range r.Form["autorate"] { if i, err := strconv.Atoi(v); err == nil { ar[i] = true } } queries := make([]string, len(oreq.Queries)) var start, end string var startT, endT time.Time if s, ok := oreq.Start.(string); ok && strings.Contains(s, "-ago") { startT, err = opentsdb.ParseTime(s) if err != nil { return nil, err } start = strings.TrimSuffix(s, "-ago") } if s, ok := oreq.End.(string); ok && strings.Contains(s, "-ago") { endT, err = opentsdb.ParseTime(s) if err != nil { return nil, err } end = strings.TrimSuffix(s, "-ago") } if start == "" && end == "" { s, sok := oreq.Start.(int64) e, eok := oreq.End.(int64) if sok && eok { start = fmt.Sprintf("%vs", e-s) startT = time.Unix(s, 0) endT = time.Unix(e, 0) if err != nil { return nil, err } } } if endT.Equal(time.Time{}) { endT = time.Now().UTC() } m_units := make(map[string]string) for i, q := range oreq.Queries { if ar[i] { meta, err := schedule.MetadataMetrics(q.Metric) if err != nil { return nil, err } if meta == nil { return nil, fmt.Errorf("no metadata for %s: cannot use auto rate", q) } if meta.Unit != "" { m_units[q.Metric] = meta.Unit } if meta.Rate != "" { switch meta.Rate { case metadata.Gauge: // ignore case metadata.Rate: q.Rate = true case metadata.Counter: q.Rate = true q.RateOptions = opentsdb.RateOptions{ Counter: true, ResetValue: 1, } default: return nil, fmt.Errorf("unknown metadata rate: %s", meta.Rate) } } } queries[i] = fmt.Sprintf(`q("%v", "%v", "%v")`, q, start, end) if !schedule.SystemConf.GetTSDBContext().Version().FilterSupport() { if err := schedule.Search.Expand(q); err != nil { return nil, err } } } var tr opentsdb.ResponseSet b, _ := json.MarshalIndent(oreq, "", " ") t.StepCustomTiming("tsdb", "query", string(b), func() { h := schedule.SystemConf.GetTSDBHost() if h == "" { err = fmt.Errorf("tsdbHost not set") return } tr, err = oreq.Query(h) }) if err != nil { return nil, err } cs, err := makeChart(tr, m_units) if err != nil { return nil, err } if _, present := r.Form["png"]; present { c := chart.ScatterChart{ Title: fmt.Sprintf("%v - %v", oreq.Start, queries), } c.XRange.Time = true if min, err := strconv.ParseFloat(r.FormValue("min"), 64); err == nil { c.YRange.MinMode.Fixed = true c.YRange.MinMode.Value = min } if max, err := strconv.ParseFloat(r.FormValue("max"), 64); err == nil { c.YRange.MaxMode.Fixed = true c.YRange.MaxMode.Value = max } for ri, r := range cs { pts := make([]chart.EPoint, len(r.Data)) for idx, v := range r.Data { pts[idx].X = v[0] pts[idx].Y = v[1] } slice.Sort(pts, func(i, j int) bool { return pts[i].X < pts[j].X }) c.AddData(r.Name, pts, chart.PlotStyleLinesPoints, sched.Autostyle(ri)) } w.Header().Set("Content-Type", "image/svg+xml") white := color.RGBA{0xff, 0xff, 0xff, 0xff} const width = 800 const height = 600 s := svg.New(w) s.Start(width, height) s.Rect(0, 0, width, height, "fill: #ffffff") sgr := svgg.AddTo(s, 0, 0, width, height, "", 12, white) c.Plot(sgr) s.End() return nil, nil } var a []annotate.Annotation warnings := []string{} if schedule.SystemConf.AnnotateEnabled() { a, err = annotateBackend.GetAnnotations(&startT, &endT) if err != nil { warnings = append(warnings, fmt.Sprintf("unable to get annotations: %v", err)) } } return struct { Queries []string Series []*chartSeries Annotations []annotate.Annotation Warnings []string }{ queries, cs, a, warnings, }, nil }
// RandomGradientColor builds a isogrid image with with x colors selected at random for each quadrant. // the background color stays the same the other colors get mixed in a gradient color from the first one to the last one. func RandomGradientColor(w http.ResponseWriter, colors, gColors []color.RGBA, gv colors.GradientVector, width, height, lines int, prob float64) { var gradientColors []svg.Offcolor gradientColors = make([]svg.Offcolor, len(gColors)) percentage := uint8(0) step := uint8(100 / len(gColors)) for i, c := range gColors { gradientColors[i] = svg.Offcolor{ Offset: percentage, Color: draw.RGBToHex(c.R, c.G, c.B), Opacity: 1, } percentage += step } canvas := svg.New(w) canvas.Start(width, height) canvas.Def() canvas.LinearGradient("gradientColors", gv.X1, gv.Y1, gv.X2, gv.Y2, gradientColors) canvas.DefEnd() canvas.Rect(0, 0, width, height, "fill:url(#gradientColors)") fringeSize := width / lines distance := distanceTo3rdPoint(fringeSize) fringeSize = distance lines = width / fringeSize colorMap := make(map[int]color.RGBA) colorIndex := make(map[int]int) for xL := 0; xL <= lines; xL++ { colorMap = make(map[int]color.RGBA) colorIndex = make(map[int]int) for yL := -1; yL <= lines; yL++ { var x1, x2, y1, y2, y3 int if (xL % 2) == 0 { x1, y1, x2, y2, _, y3 = right1stTriangle(xL, yL, fringeSize, distance) } else { x1, y1, x2, y2, _, y3 = left1stTriangle(xL, yL, fringeSize, distance) } xs := []int{x2, x1, x2} ys := []int{y1, y2, y3} colorIndex[yL] = draw.RandomIndexFromArrayWithFreq(colors, prob) colorMap[yL] = colors[colorIndex[yL]] if colorIndex[yL] == 0 { canvas.Polygon(xs, ys, draw.FillFromRGBA(colorMap[yL])) } var x11, x12, y11, y12, y13 int if (xL % 2) == 0 { x11, y11, x12, y12, _, y13 = left2ndTriangle(xL, yL, fringeSize, distance) // we make sure that the previous triangle and this one touch each other in this point. y12 = y3 } else { x11, y11, x12, y12, _, y13 = right2ndTriangle(xL, yL, fringeSize, distance) // we make sure that the previous triangle and this one touch each other in this point. y12 = y1 + fringeSize } xs1 := []int{x12, x11, x12} ys1 := []int{y11, y12, y13} colorIndex[yL] = draw.RandomIndexFromArrayWithFreq(colors, prob) colorMap[yL] = colors[colorIndex[yL]] if colorIndex[yL] == 0 { canvas.Polygon(xs1, ys1, draw.FillFromRGBA(colorMap[yL])) } } } canvas.End() }
func visualizeBest(v *Web) error { // Create the file f, err := os.Create(v.makePath("network")) if err != nil { return err } defer f.Close() best := v.best[len(v.best)-1] net0, err := v.ctx.Decoder().Decode(best) if err != nil { return err } var net *network.Classic p, ok := net0.(decoder.Phenome) if !ok { return errors.New("Web visualizer only knows the decoder package's phenome") } net, ok = p.Network.(*network.Classic) if !ok { return errors.New("Web visualizer only knows the Clasic network") } // Create the image img := svg.New(f) w, h := 1024.0, 1280.0 img.Start(int(w)+30, int(h)+30) defer img.End() // Write out the title img.Text(10, 10, fmt.Sprintf("Best Genome is %d", best.ID), `font-size="12"`) // Define connection heads img.Def() img.Marker("triangle_black", 0, 10, 8, 6, `viewBox="0 0 20 20" markerUnits="strokeWidth" orient="auto"`) img.Path("M 0 0 L 20 10 L 0 20 z", `fill="black" fill-opacity="0.8"`) img.MarkerEnd() img.Marker("triangle_red", 0, 10, 8, 6, `viewBox="0 0 20 20" markerUnits="strokeWidth" orient="auto"`) img.Path("M 0 0 L 20 10 L 0 20 z", `fill="red" fill-opacity="0.8"`) img.MarkerEnd() img.DefEnd() // Draw neurons for i, neuron := range net.Neurons { var node_color, font_color string switch neuron.NeuronType { case neat.Bias: node_color = "black" font_color = "white" case neat.Input: node_color = "paleturquoise" font_color = "black" case neat.Hidden: node_color = "palegreen" font_color = "black" case neat.Output: node_color = "thistle" font_color = "black" } cx := int(neuron.X*w) + 15 cy := int((1.0-neuron.Y)*h) + 15 img.Circle(cx, cy, 10, fmt.Sprintf(`fill="%s" stroke="black" stroke-width="1"`, node_color)) img.Text(cx-3, cy+3, fmt.Sprintf(`%d`, i+1), fmt.Sprintf(`font-size="5pt" fill="%s"`, font_color)) } // Draw synapses for _, synapse := range net.Synapses { src := net.Neurons[synapse.Source] tgt := net.Neurons[synapse.Target] fromX := int(src.X*w) + 15 fromY := int((1.0-src.Y)*h) + 15 toX := int(tgt.X*w) + 15 toY := int((1.0-tgt.Y)*h) + 15 var line_color, triangle_color string if synapse.Weight >= 0 { line_color = "black" triangle_color = "#triangle_black" } else { line_color = "red" triangle_color = "#triangle_red" } var opacity, strokewidth string switch { case synapse.Weight < 1.0 && synapse.Weight >= 0.5, synapse.Weight > -1.0 && synapse.Weight <= -0.5: opacity = "0.8" strokewidth = "0.3" case synapse.Weight >= 1.0, synapse.Weight <= -1.0: opacity = "1.0" strokewidth = "0.3" default: opacity = "0.5" strokewidth = "1.0" } img.Path(fmt.Sprintf("M %v %v L %v %v", fromX, fromY, toX, toY), fmt.Sprintf(`fill="none" stroke="%s" stroke-width="%s" stroke-opacity="%s" marker-end="%s"`, line_color, strokewidth, opacity, triangle_color)) } f.WriteString(fmt.Sprintf("<P>%s</P>", strings.Replace(best.String(), "\n", "<BR/>", -1))) f.WriteString(fmt.Sprintf("<P>%s</P>", strings.Replace((*net).String(), "\n", "<BR/>", -1))) return nil }
func visualizeSpecies(v *Web) error { // Create the file f, err := os.Create(v.makePath("species")) if err != nil { return err } defer f.Close() // Identify the max population size popSize := 0 for _, h := range v.species { cnt := 0 for _, s := range h { cnt += s } if cnt > popSize { popSize = cnt } } if popSize == 0 { return nil } // Create the image img := svg.New(f) img.Start(popSize*2+400, 460) defer img.End() //img.Text(10, 10, fmt.Sprintf("ID=%d Time/Date=%v", ?, ?), `style="font-size:12"`) img.Text(10, 25, fmt.Sprintf("PopSize=%d NumGenerations=%d", popSize, len(v.species)), `style="font-size:10"`) img.Path("M 40 340 L 540 340", `id="generation" stroke-width="1" stroke="black" fill="none"`) img.Textpath("Generation", "#generation", `fill="blue" font-size="12" font-family="Verdana" dy="30" startOffset="25%"`) img.Path("M 140 345 L 140 340", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 240 345 L 240 340", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 340 345 L 340 340", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 440 345 L 440 340", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 540 345 L 540 340", `stroke-width="1" stroke="black" fill="none"`) img.Text(132, 355, fmt.Sprintf("%d", len(v.species)/5*1), `fill="black" font-size="11" font-family="Verdana"`) img.Text(232, 355, fmt.Sprintf("%d", len(v.species)/5*2), `fill="black" font-size="11" font-family="Verdana"`) img.Text(332, 355, fmt.Sprintf("%d", len(v.species)/5*3), `fill="black" font-size="11" font-family="Verdana"`) img.Text(432, 355, fmt.Sprintf("%d", len(v.species)/5*4), `fill="black" font-size="11" font-family="Verdana"`) img.Text(532, 355, fmt.Sprintf("%d", len(v.species)/5*5), `fill="black" font-size="11" font-family="Verdana"`) img.Text(5, 75, "# of Individuals", `style="writing-mode: tb; glyph-orientation-vertical:0; fill: blue; font-size: 10; font-family: Verdana;"`) img.Text(15, 285, fmt.Sprintf("%d", int(float64(popSize)*0.2)), `fill="black" font-size="11" font-family="Verdana"`) img.Text(15, 225, fmt.Sprintf("%d", int(float64(popSize)*0.4)), `fill="black" font-size="11" font-family="Verdana"`) img.Text(15, 165, fmt.Sprintf("%d", int(float64(popSize)*0.6)), `fill="black" font-size="11" font-family="Verdana"`) img.Text(15, 105, fmt.Sprintf("%d", int(float64(popSize)*0.8)), `fill="black" font-size="11" font-family="Verdana"`) img.Text(15, 45, fmt.Sprintf("%d", int(float64(popSize)*1.0)), `fill="black" font-size="11" font-family="Verdana"`) popIncrement := 300 / popSize for i, generation := range v.species { xplot := 500/len(v.species)*i + 42 for j, speciesCount := range generation { var speciesColor string switch j % 3 { case 0: speciesColor = "CornflowerBlue" case 1: speciesColor = "Yellow" case 2: speciesColor = "Plum" default: speciesColor = "Chartreuse" } yplotFrom := 340.0 for k := 0; k < j; k++ { yplotFrom -= float64(generation[k] * popIncrement) } yplotTo := yplotFrom - float64(speciesCount*popIncrement) + 0.5 img.Path(fmt.Sprintf("M %v %v L %v %v", xplot, yplotFrom, xplot, yplotTo), fmt.Sprintf(`stroke-width="3" stroke="%s" fill="none"`, speciesColor)) } } return nil }
func visualizeComplexity(v *Web) error { // Create the file f, err := os.Create(v.makePath("complexity")) if err != nil { return err } defer f.Close() // Create the image img := svg.New(f) img.Start(575, 375) defer img.End() // Draw and label horizontal axis img.Path("M 40 340 L 540 340", `id="generation" stroke-width="1" stroke="black" fill="none"`) img.Textpath("Generation", "#generation", `fill="blue" font-size="15" font-family="Verdana" dy="30" startOffset="40%"`) img.Path("M 140 345 L 140 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 240 345 L 240 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 340 345 L 340 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 440 345 L 440 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 540 345 L 540 335", `stroke-width="1" stroke="black" fill="none"`) generations := len(v.complexity) img.Text(132, 355, fmt.Sprintf("%d", generations/5*1), `fill="black" font-size="11" font-family="Verdana"`) img.Text(232, 355, fmt.Sprintf("%d", generations/5*2), `fill="black" font-size="11" font-family="Verdana"`) img.Text(332, 355, fmt.Sprintf("%d", generations/5*3), `fill="black" font-size="11" font-family="Verdana"`) img.Text(432, 355, fmt.Sprintf("%d", generations/5*4), `fill="black" font-size="11" font-family="Verdana"`) img.Text(532, 355, fmt.Sprintf("%d", generations/5*5), `fill="black" font-size="11" font-family="Verdana"`) // Draw and label veritical axis img.Path("M 40 340 L 40 40", `id="complexity" stroke-width="1" strok="black" fill="none"`) img.Textpath("Number of Genes", "#complexity", `fill="blue" font-size="15" font-family="Verdana" dy="-25" startOffset="40%"`) var complexity_max, complexity_min float64 complexity_min = 9e10 for _, generation := range v.complexity { if generation[0] < complexity_min { complexity_min = generation[0] } if generation[2] > complexity_max { complexity_max = generation[2] } } complexity_range := complexity_max - complexity_min img.Path("M 40 40 L 540 40", `id="complexity1" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%v", complexity_range+complexity_min), "#complexity1", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="90%"`) img.Path("M 40 100 L 540 100", `id="complexity2" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%v", int(complexity_range*0.8+complexity_min)), "#complexity2", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="70%"`) img.Path("M 40 160 L 540 160", `id="complexity3" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%v", int(complexity_range*0.6+complexity_min)), "#complexity3", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="50%"`) img.Path("M 40 220 L 540 220", `id="complexity4" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%v", int(complexity_range*0.4+complexity_min)), "#complexity4", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="30%"`) img.Path("M 40 280 L 540 280", `id="complexity5" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%v", int(complexity_range*0.2+complexity_min)), "#complexity5", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="10%"`) img.Textpath(fmt.Sprintf("%v", int(complexity_min)), "#generation", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="0%"`) for i, generation := range v.complexity { complexityMax := generation[2] complexityMin := generation[0] complexityAvg := generation[1] complexityChamp := generation[3] xplot := 500/generations*i + 40 yplotMax := 340 - 300/complexity_range*(complexityMax-complexity_min) yplotMin := 340 - 300/complexity_range*(complexityMin-complexity_min) yplotAvg := 340 - 300/complexity_range*(complexityAvg-complexity_min) yplotChamp := 340 - 300/complexity_range*(complexityChamp-complexity_min) img.Path(fmt.Sprintf("M %v %v L %v %v L %v %v z", xplot-2, yplotChamp-2, xplot, yplotChamp-6, xplot+2, yplotChamp-2), fmt.Sprintf(`id="%d" fill="red"`, i)) img.Circle(xplot, int(yplotAvg), 1, fmt.Sprintf(`id="%d" fill="black"`, i)) img.Path(fmt.Sprintf("M %v %v L %v %v", xplot, yplotMin, xplot, yplotMax), `stroke-width="0.5" stroke="blue" fill="none"`) } return nil }
func visualizeFitness(v *Web) error { // Create the file f, err := os.Create(v.makePath("fitness")) if err != nil { return err } defer f.Close() // Create the image img := svg.New(f) img.Start(575, 375) defer img.End() // Draw and label horizontal axis img.Path("M 40 340 L 540 340", `id="generation" stroke-width="1" stroke="black" fill="none"`) img.Textpath("Generation", "#generation", `fill="blue" font-size="15" font-family="Verdana" dy="30" startOffset="40%"`) img.Path("M 140 345 L 140 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 240 345 L 240 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 340 345 L 340 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 440 345 L 440 335", `stroke-width="1" stroke="black" fill="none"`) img.Path("M 540 345 L 540 335", `stroke-width="1" stroke="black" fill="none"`) generations := len(v.fitness) img.Text(132, 355, fmt.Sprintf("%d", generations/5*1), `fill="black" font-size="11" font-family="Verdana"`) img.Text(232, 355, fmt.Sprintf("%d", generations/5*2), `fill="black" font-size="11" font-family="Verdana"`) img.Text(332, 355, fmt.Sprintf("%d", generations/5*3), `fill="black" font-size="11" font-family="Verdana"`) img.Text(432, 355, fmt.Sprintf("%d", generations/5*4), `fill="black" font-size="11" font-family="Verdana"`) img.Text(532, 355, fmt.Sprintf("%d", generations/5*5), `fill="black" font-size="11" font-family="Verdana"`) // Draw and label veritical axis img.Path("M 40 340 L 40 40", `id="fitness" stroke-width="1" strok="black" fill="none"`) img.Textpath("Fitness", "#fitness", `fill="blue" font-size="15" font-family="Verdana" dy="-25" startOffset="40%"`) var fitness_max, fitness_min float64 fitness_min = 9e10 for _, generation := range v.fitness { if generation[0] < fitness_min { fitness_min = generation[0] } if generation[2] > fitness_max { fitness_max = generation[2] } } fitness_range := fitness_max - fitness_min img.Path("M 40 40 L 540 40", `id="fitness1" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%2f", fitness_range+fitness_min), "#fitness1", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="90%"`) img.Path("M 40 100 L 540 100", `id="fitness2" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%2f", (fitness_range*0.8+fitness_min)), "#fitness2", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="70%"`) img.Path("M 40 160 L 540 160", `id="fitness3" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%2f", (fitness_range*0.6+fitness_min)), "#fitness3", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="50%"`) img.Path("M 40 220 L 540 220", `id="fitness4" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%2f", (fitness_range*0.4+fitness_min)), "#fitness4", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="30%"`) img.Path("M 40 280 L 540 280", `id="fitness5" stroke-width="0.5" stroke="green" fill="none"`) img.Textpath(fmt.Sprintf("%2f", (fitness_range*0.2+fitness_min)), "#fitness5", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="10%"`) img.Textpath(fmt.Sprintf("%2f", (fitness_min)), "#generation", `fill="green" fill-opacity="1.0" font-size="9" font-family="Verdana" dy="8" startOffset="0%"`) for i, generation := range v.fitness { fitnessMax := generation[2] fitnessMin := generation[0] fitnessAvg := generation[1] xplot := 500/generations*i + 40 yplotMax := 340 - 300/fitness_range*(fitnessMax-fitness_min) yplotMin := 340 - 300/fitness_range*(fitnessMin-fitness_min) yplotAvg := 340 - 300/fitness_range*(fitnessAvg-fitness_min) img.Circle(xplot, int(yplotAvg), 1, fmt.Sprintf(`id="%d" fill="black"`, i)) img.Path(fmt.Sprintf("M %v %v L %v %v", xplot, yplotMin, xplot, yplotMax), `stroke-width="0.5" stroke="blue" fill="none"`) } return nil }