Plotter code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
7.1 KiB

package plotter
import (
"codeisalie/gono/cfg"
"encoding/xml"
"fmt"
"io/ioutil"
"math"
"os"
"regexp"
"strconv"
"strings"
)
// XMLSvg ...
type XMLSvg struct {
XMLName xml.Name `xml:"svg"`
Width string `xml:"width,attr"`
Height string `xml:"height,attr"`
ViewBox string `xml:"viewBox,attr"`
Paths []XMLPath `xml:"path"`
Circles []XMLCircle `xml:"circle"`
Texts []XMLFlowRoot `xml:"flowRoot"`
}
// XMLPath ...
type XMLPath struct {
XMLName xml.Name `xml:"path"`
Style string `xml:"style,attr"`
Data string `xml:"d,attr"`
}
// XMLCircle ...
type XMLCircle struct {
XMLName xml.Name `xml:"circle"`
CenterX float64 `xml:"cx,attr"`
CenterY float64 `xml:"cy,attr"`
Radius float64 `xml:"r,attr"`
}
// XMLFlowRoot ...
type XMLFlowRoot struct {
XMLName xml.Name `xml:"flowRoot"`
Rec XMLFlowRec `xml:"flowRegion>rect"`
Text string `xml:"flowPara"`
Transform string `xml:"transform,attr"`
}
type XMLFlowRec struct {
XMLName xml.Name `xml:"rect"`
X float64 `xml:"x,attr"`
Y float64 `xml:"y,attr"`
Width float64 `xml:"width,attr"`
Height float64 `xml:"height,attr"`
}
// Svg ...
type Svg struct {
OriginX float64
OriginY float64
Width float64
Height float64
}
// Point ...
type Point struct {
X float64
Y float64
}
// OnePath ...
type OnePath struct {
// StartAt Point
Center Point
Type int
List []*Point
}
// SvgParser struct
type SvgParser struct {
Root XMLSvg
Svg Svg
Font FontParser
cfg cfg.Config
Paths []OnePath
}
const (
ModeAbsolute int = 100 + iota
ModeRelative
TypePath int = 200 + iota
TypeCircle
TypeText
)
func rads(deg float64) float64 {
return deg * math.Pi / 180.0
}
// Parse on SVG file
func (s *SvgParser) Parse(svgPath string, cfg cfg.Config) {
// Open our xmlFile
xmlFile, err := os.Open(svgPath)
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened " + svgPath)
// defer the closing of our xmlFile so that we can parse it later on
defer xmlFile.Close()
// read our opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(xmlFile)
// we unmarshal our byteArray which contains our
// xmlFiles content into 'users' which we defined above
xml.Unmarshal(byteValue, &s.Root)
s.Font = FontParser{}
s.Font.Parse("font-default.xml")
s.cfg = cfg
s.Svg = Svg{}
reViewboxSrc := regexp.MustCompile("(.*) (.*) (.*) (.*)")
reViewboxRes := reViewboxSrc.FindAllStringSubmatch(s.Root.ViewBox, -1)
s.Svg.OriginX, _ = strconv.ParseFloat(reViewboxRes[0][1], 64)
s.Svg.OriginY, _ = strconv.ParseFloat(reViewboxRes[0][2], 64)
s.Svg.Width, _ = strconv.ParseFloat(reViewboxRes[0][3], 64)
s.Svg.Height, _ = strconv.ParseFloat(reViewboxRes[0][4], 64)
// fmt.Println(s.Root.Texts)
s.ProcessPaths()
s.ProcessCircles()
s.ProcessTexts()
for _, path := range s.Paths {
for _, point := range path.List {
point.X = point.X*s.cfg.DrawingArea.Width/s.Svg.Width + s.cfg.DrawingArea.TopLeft.X
point.Y = -point.Y*s.cfg.DrawingArea.Height/s.Svg.Height + s.cfg.DrawingArea.BottomRight.Y
path.Center.X += point.X
path.Center.Y += point.Y
}
path.Center.X /= float64(len(path.List))
path.Center.Y /= float64(len(path.List))
}
}
func (s *SvgParser) ProcessTexts() {
reTranslate := regexp.MustCompile(`translate\((.*),(.*)\)`)
for _, text := range s.Root.Texts {
msg := text.Text
zoomH := text.Rec.Height / s.Font.Root.Height
zoomW := zoomH * s.Font.Root.Width / s.Font.Root.Width
origin := Point{
X: text.Rec.X,
Y: s.Svg.Height - (text.Rec.Y - s.Font.Root.Height),
}
posX := 0.0
posY := 0.0
values := reTranslate.FindAllStringSubmatch(text.Transform, -1)
if values != nil {
tPoint := Point{}
tPoint.X, _ = strconv.ParseFloat(values[0][1], 64)
tPoint.Y, _ = strconv.ParseFloat(values[0][2], 64)
origin.X += tPoint.X
origin.Y += tPoint.Y
}
// fmt.Println("ORIGIN", origin)
for _, char := range msg {
seq, ok := s.Font.ByLetter[char]
if !ok {
fmt.Println("Unknown char", char)
seq = s.Font.ByLetter[' ']
}
for _, path := range seq {
fmt.Println(path)
onePath := OnePath{
Type: TypeText,
}
for _, pt := range path.List {
point := Point{
X: pt.X*zoomW + posX + origin.X,
Y: (s.Font.Root.Height-pt.Y)*zoomH + posY + origin.Y,
}
// fmt.Println("POINT", point)
onePath.List = append(onePath.List, &point)
}
s.Paths = append(s.Paths, onePath)
}
posX += (s.Font.Root.Width + s.Font.Root.Space) * zoomW
}
}
}
func (s *SvgParser) ProcessCircles() {
for _, circle := range s.Root.Circles {
var angle float64
onePath := OnePath{}
onePath.Type = TypeCircle
segments := s.cfg.CircleResolution
for angle = 360 / segments; angle <= 360; angle += 360 / segments {
point := Point{}
point.X = circle.CenterX + circle.Radius*math.Cos(rads(angle))
point.Y = s.Svg.Height - circle.CenterY + circle.Radius*math.Sin(rads(angle))
// s.Scale(&point)
onePath.List = append(onePath.List, &point)
}
pointEnd := Point{
X: onePath.List[0].X,
Y: onePath.List[0].Y,
}
onePath.List = append(onePath.List, &pointEnd)
s.Paths = append(s.Paths, onePath)
}
}
func (s *SvgParser) ProcessPaths() {
rePosSrc := regexp.MustCompile("(.*),(.*)")
for _, path := range s.Root.Paths {
var mode int
var size int
onePath := OnePath{}
onePath.Type = TypePath
segs := strings.Split(path.Data, " ")
for _, seg := range segs {
switch {
case seg == "M":
// fmt.Println("Absolute")
mode = ModeAbsolute
case seg == "m":
// fmt.Println("Relative")
mode = ModeRelative
case seg == "z":
// fmt.Println("Zero")
point := Point{
X: onePath.List[0].X,
Y: onePath.List[0].Y,
}
onePath.List = append(onePath.List, &point)
default:
point := Point{}
seqRes := rePosSrc.FindAllStringSubmatch(seg, -1)
if seqRes == nil {
fmt.Printf("Unknown seg: %v\n", seg)
continue
}
point.X, _ = strconv.ParseFloat(seqRes[0][1], 64)
point.Y, _ = strconv.ParseFloat(seqRes[0][2], 64)
size = len(onePath.List)
if mode == ModeRelative && size > 0 {
prevPoint := onePath.List[size-1]
// fmt.Println(prevPoint, point)
point.X = point.X + prevPoint.X
point.Y = prevPoint.Y - point.Y
} else {
point.Y = s.Svg.Height - point.Y
}
onePath.List = append(onePath.List, &point)
}
}
s.Paths = append(s.Paths, onePath)
}
}
func (s *SvgParser) ScalePath(path *OnePath) {
// var point *Point
// for idx := range path.List {
// pX := (*path).List[idx]
// pointCell := path.List[idx]
// fmt.Printf(">%v %p\n", idx, point)
// path.List[idx].X = path.List[idx].X*s.cfg.DrawingArea.Width/s.Svg.Width + s.cfg.DrawingArea.TopLeft.X
// path.List[idx].Y = -path.List[idx].Y*s.cfg.DrawingArea.Height/s.Svg.Height + s.cfg.DrawingArea.BottomRight.Y
// }
}
func (s *SvgParser) Scale(point *Point) {
(*point).X = point.X*s.cfg.DrawingArea.Width/s.Svg.Width + s.cfg.DrawingArea.TopLeft.X
(*point).Y = -point.Y*s.cfg.DrawingArea.Height/s.Svg.Height + s.cfg.DrawingArea.BottomRight.Y
}
func (path *OnePath) process(cfg cfg.Config) {
}