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
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) {
|
|
|
|
}
|