commit 1270f0cbc997f59504a950a2a7fa2a768c78b8cc Author: P.BARRY Date: Mon Mar 4 22:23:27 2019 +0100 first commit diff --git a/cfg/config.go b/cfg/config.go new file mode 100644 index 0000000..41111a1 --- /dev/null +++ b/cfg/config.go @@ -0,0 +1,55 @@ +package cfg + +import "math" + +// CFG returns the configuration +func CFG() Config { + cfg := Config{ + CircleResolution: 30, + MachineWidth: 1225, + MachineHeight: 800, + MachineMmPerRev: 37.7, + MachineStepsPerRev: 200, + MachineMotorAccel: 400, + MachineMotorSpeed: 800, + MachineStepMultiplier: 1, + DrawingArea: Area{ + TopLeft: Point{X: 400, Y: 250}, + BottomRight: Point{X: 800, Y: 500}, + Home: Point{X: 612.44, Y: 217.61}, + }, + } + + cfg.DrawingArea.Width = math.Abs(cfg.DrawingArea.BottomRight.X - cfg.DrawingArea.TopLeft.X) + cfg.DrawingArea.Height = math.Abs(cfg.DrawingArea.BottomRight.Y - cfg.DrawingArea.TopLeft.Y) + + return cfg +} + +// Point ... +type Point struct { + X float64 + Y float64 +} + +// Area ... +type Area struct { + Home Point + TopLeft Point + BottomRight Point + Width float64 + Height float64 +} + +// Config ... +type Config struct { + DrawingArea Area + MachineWidth float64 + MachineHeight float64 + MachineMmPerRev float64 + MachineStepsPerRev float64 + MachineMotorSpeed float64 + MachineMotorAccel float64 + MachineStepMultiplier float64 + CircleResolution float64 +} diff --git a/cmd/gamepad/gamepad.go b/cmd/gamepad/gamepad.go new file mode 100644 index 0000000..955d325 --- /dev/null +++ b/cmd/gamepad/gamepad.go @@ -0,0 +1,62 @@ +package gamepad + +import ( + "codeisalie/gono/pkg/comm" + "log" + "time" + + "github.com/splace/joysticks" +) + +// Run initializes the plotter +func Run(c *comm.Comm) { + motorsState := false + + device := joysticks.Connect(1) + + if device == nil { + panic("no HIDs") + } + + // using Connect allows a device to be interrogated + log.Printf("HID#1:- Buttons:%d, Hats:%d\n", len(device.Buttons), len(device.HatAxes)/2) + + // get/assign channels for specific events + b1press := device.OnClose(1) + b7press := device.OnClose(7) + b8press := device.OnClose(8) + b10press := device.OnClose(10) + h1move := device.OnMove(3) + + // start feeding OS events onto the event channels. + go device.ParcelOutEvents() + + // handle event channels + go func() { + for { + select { + case <-b1press: + log.Println("button #1 pressed") + case <-b7press: + log.Println("button #7 pressed") + case <-b8press: + log.Println("button #8 pressed") + case <-b10press: + log.Println("button #10 pressed", motorsState) + if motorsState == false { + // c.Send(comm.EnableMotors) + motorsState = true + } else { + // c.Send(comm.DisableMotors) + motorsState = false + } + case h := <-h1move: + hpos := h.(joysticks.CoordsEvent) + log.Println(".hat #1 moved too:", hpos) + } + } + }() + + time.Sleep(time.Second * 60) + log.Println("Shutting down due to timeout.") +} diff --git a/cmd/plotter/plotter.go b/cmd/plotter/plotter.go new file mode 100644 index 0000000..d81db6b --- /dev/null +++ b/cmd/plotter/plotter.go @@ -0,0 +1,45 @@ +package plotter + +import ( + "codeisalie/gono/pkg/comm" + "codeisalie/gono/pkg/plotter" + "fmt" + "time" +) + +// Run ... +func Run(c *comm.Comm) { + + svg := plotter.SvgParser{} + svg.Parse("drawing.svg", c.Cfg) + + // for _, path := range svg.Paths { + // fmt.Printf("%+v\n", path) + // for _, point := range path.List { + // fmt.Printf("-- %+v\n", point) + // } + // } + + // svg.Paths + fmt.Println(svg.Root.ViewBox) + c.Send(comm.EnableMotors) + c.Send(comm.GetReportPosition) + for _, path := range svg.Paths { + fmt.Printf("%+v\n", path) + // if len(path.List) <= 1 { + // continue + // } + c.Send(comm.SetPenUp) + for idx, point := range path.List { + c.Send(comm.DrawLine, point.X, point.Y, 1) + if idx == 0 && len(path.List) > 1 { + c.Send(comm.SetPenDown) + } + } + c.Send(comm.SetPenUp) + } + c.Send(comm.DrawLine, c.Cfg.DrawingArea.Home.X, c.Cfg.DrawingArea.Home.Y, 1) + c.Send(comm.DisableMotors) + + time.Sleep(2 * time.Second) +} diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go new file mode 100644 index 0000000..01b3166 --- /dev/null +++ b/cmd/setup/setup.go @@ -0,0 +1,22 @@ +package setup + +import ( + "codeisalie/gono/pkg/comm" + "time" +) + +// List ... +type List []comm.SetCMD + +// Run initializes the plotter +func Run(c *comm.Comm) { + + c.Send(comm.SetMachineSize, c.Cfg.MachineWidth, c.Cfg.MachineHeight) + c.Send(comm.SetMachineMmPerRev, c.Cfg.MachineMmPerRev) + c.Send(comm.SetMachineStepsPerRev, c.Cfg.MachineStepsPerRev) + c.Send(comm.SetMachineMotorSpeed, c.Cfg.MachineMotorSpeed) + c.Send(comm.SetMachineMotorAccel, c.Cfg.MachineMotorAccel) + c.Send(comm.SetMachineStepMultiplier, c.Cfg.MachineStepMultiplier) + + time.Sleep(5 * time.Second) +} diff --git a/drawing.svg b/drawing.svg new file mode 100644 index 0000000..c65c7e8 --- /dev/null +++ b/drawing.svg @@ -0,0 +1,97 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + HALLO WORLD + diff --git a/font-default.xml b/font-default.xml new file mode 100644 index 0000000..4c7392d --- /dev/null +++ b/font-default.xml @@ -0,0 +1,96 @@ + + + + + 0,0 + + + 0,20 5,0 10,20 + 3,10 7,10 + + + 0,0 0,20 10,20 10,10 0,10 + 0,0 6,0 6,10 + + + 10,0 0,0 0,20 10,20 + + + 0,0 7,0 10,3 10,17 8,20 0,20 0,0 + + + 10,0 0,0 0,10 10,10 0,10 0,20 10,20 + + + 10,0 0,0 0,10 10,10 0,10 0,20 + + + 10,0 0,0 0,20 10,20 10,10 5,10 + + + 0,0 0,20 0,10 10,10 10,0 10,20 + + + 2,0 8,0 5,0 5,20 2,20 8,20 + + + 0,10 0,20 10,20 10,0 + + + 0,0 0,20 + 10,0 0,10 10,20 + + + 0,0 0,20 10,20 + + + 0,20 0,0 5,10 10,0 10,20 + + + 0,20 0,0 10,20 10,0 + + + 0,2 2,0 8,0 10,2 10,18 8,20 2,20 0,18 0,2 + + + 0,20 0,0 8,0 10,2 10,8 8,10 0,10 + + + 0,2 2,0 8,0 10,2 10,18 8,20 2,20 0,18 0,2 + 7,17 10,20 + + + 0,20 0,0 8,0 10,2 10,8 8,10 0,10 + 8,10 10,12 10,20 + + + 10,0 2,0 0,2 0,8 2,10 8,10 10,12 10,18 8,20 0,20 + + + 0,0 10,0 + 5,0 5,20 + + + 0,2 0,18 2,20 8,20 10,18 10,2 + + + 0,0 5,20 10,0 + + + 0,0 3,20 5,10 7,20 10,0 + + + 0,0 10,20 + 10,0 0,20 + + + 0,0 5,10 10,0 + 5,10 5,20 + + + 0,0 10,0 0,20 10,20 + + + 0,0 0,7 10,12 10,18 8,20 0,20 0,0 + + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3d26064 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module codeisalie/gono + +require ( + github.com/splace/joysticks v0.0.0-20180907185707-7416f8bb58bd + github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 + golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c70b28d --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/splace/joysticks v0.0.0-20180907185707-7416f8bb58bd h1:WO95lkUpx1EbYOnppBa8i/fEY6t2itFxK5LCCRcgzKQ= +github.com/splace/joysticks v0.0.0-20180907185707-7416f8bb58bd/go.mod h1:PQPCtmjcD4hfFT4yHSY7zomN8fvov8fFvQWYSth8L34= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/main.go b/main.go new file mode 100644 index 0000000..f7ca450 --- /dev/null +++ b/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "codeisalie/gono/cfg" + "codeisalie/gono/cmd/gamepad" + "codeisalie/gono/cmd/plotter" + "codeisalie/gono/cmd/setup" + "codeisalie/gono/pkg/comm" + "flag" + "log" +) + +func main() { + var setupMode, gameMode bool + flag.BoolVar(&setupMode, "setup", false, "Setup plotter machine") + flag.BoolVar(&gameMode, "game", false, "Gamepad mode") + flag.Parse() + + CFG := cfg.CFG() + + if setupMode { + log.Println("[>] Running mode: gamepad") + } else if gameMode { + log.Println("[>] Running mode: setup") + } else { + log.Println("[>] Running mode: plotter") + } + + c := comm.Init("", CFG) + + if gameMode { + gamepad.Run(c) + } else if setupMode { + setup.Run(c) + } else { + plotter.Run(c) + } +} diff --git a/pkg/comm/main.go b/pkg/comm/main.go new file mode 100644 index 0000000..9e8808f --- /dev/null +++ b/pkg/comm/main.go @@ -0,0 +1,250 @@ +package comm + +import ( + "codeisalie/gono/cfg" + "fmt" + "log" + "math" + "regexp" + "sync" + "time" + + "github.com/tarm/serial" +) + +const ( + // SetPenDown ... + SetPenDown string = "C13" + // SetPenUp ... - args = 0 + SetPenUp string = "C14" + // SetMachineSize saves machine width and height - args = 2 + SetMachineSize string = "C24" + // SetMachineMmPerRev ... - args = 1 + SetMachineMmPerRev string = "C29" + // SetMachineStepsPerRev ... - args = 1 + SetMachineStepsPerRev string = "C30" + // SetMachineMotorSpeed ... - args = 1 + SetMachineMotorSpeed string = "C31" + // SetMachineMotorAccel ... - args = 1 + SetMachineMotorAccel string = "C32" + // SetMachineStepMultiplier ... - args = 1 + SetMachineStepMultiplier string = "C37" + // GetMachineDetails ... - args = 0 + GetMachineDetails string = "C26" + // DisableMotors ... + DisableMotors string = "CA0" + // EnableMotors ... + EnableMotors string = "CA1" + // DrawLine ... - args = 0 + DrawLine string = "C17" + // DrawCircle ... - args = 0 + DrawCircle string = "CB0" + // GetReportPosition ... + GetReportPosition string = "CC0" +) + +// SetCMD saves one EEPROM command +type SetCMD struct { + Cmd string + Args []float64 +} + +// Comm context +type Comm struct { + isReady bool + mutex sync.Mutex + sync chan int + list chan string + com string + Cfg cfg.Config + serialCfg *serial.Config + handler *serial.Port +} + +// Init returns an initialized serial handler +// Def: /dev/ttyACM0 +func Init(com string, cfg cfg.Config) *Comm { + + c := Comm{} + c.list = make(chan string) + c.sync = make(chan int) + c.Cfg = cfg + + if com == "" { + com = "/dev/ttyACM0" + } + + c.com = com + + c.serialCfg = &serial.Config{Name: c.com, Baud: 57600} + handler, err := serial.OpenPort(c.serialCfg) + if err != nil { + log.Println(err) + return &c + } + + log.Printf("[-] comm | %v opened\n", c.com) + + c.handler = handler + + go c.readSerial() + log.Printf("[-] comm | Waiting plotter stating\n") + <-c.sync + log.Printf("[-] comm | Plotter ready\n") + go c.sendMsg() + + // q := make(chan os.Signal, 1) + // signal.Notify(q, os.Interrupt) + // go func() { + // for sig := range q { + // fmt.Println(sig) + // close(c.list) + // close(c.sync) + // } + // }() + + return &c +} + +// Queue one new message to send to serial interface +func (c *Comm) Queue(msg string) { + c.list <- msg +} + +// Send ... +func (c *Comm) Send(cmd string, args ...float64) { + msg := cmd + switch cmd { + case DrawLine: + r1, r2 := c.convertCartToPolar(args[0], args[1]) + args[0] = math.Floor(r1*100) / 100 + args[1] = math.Floor(r2*100) / 100 + } + for _, arg := range args { + msg += fmt.Sprintf(",%v", arg) + } + msg += ",END;" + // fmt.Println(msg) + c.Queue(msg) +} + +// SendPPP one message to machine +func (c *Comm) SendPPP(cmd SetCMD) { + msg := cmd.Cmd + switch cmd.Cmd { + case DrawLine: + r1, r2 := c.convertCartToPolar(cmd.Args[0], cmd.Args[1]) + cmd.Args[0] = r1 + cmd.Args[1] = r2 + } + for _, arg := range cmd.Args { + msg += fmt.Sprintf(",%v", arg) + } + msg += ",END;" + // fmt.Println(msg) + c.Queue(msg) +} + +func (c *Comm) convertCartToPolar(x, y float64) (float64, float64) { + r1 := math.Sqrt(math.Pow(x, 2) + math.Pow(y, 2)) + r2 := math.Sqrt(math.Pow(c.Cfg.MachineWidth-x, 2) + math.Pow(y, 2)) + return r1, r2 +} + +// Send a message when it's serial com is ready +func (c *Comm) sendMsg() { + log.Printf("[-] comm | routine sendMsg started\n") + for { + <-c.sync + c.mutex.Lock() + c.isReady = true + c.mutex.Unlock() + time.Sleep(100 * time.Millisecond) + msg := <-c.list + log.Printf("[-] comm | Sending: %v\n", msg) + _, err := c.handler.Write([]byte(msg)) + if err != nil { + log.Fatal(err) + } else { + // log.Println("written", n) + } + } +} + +func (c *Comm) GetIsReady() { + isReady := false + c.mutex.Lock() + isReady = c.isReady + c.mutex.Unlock() + + return isReady +} + +func (c *Comm) processSerialMsg(msg string) { + reReady := regexp.MustCompile("READY") + if reReady.MatchString(msg) { + c.sync <- 0 + } +} + +func (c *Comm) readSerial() { + log.Printf("[-] comm | routine readSerial started\n") + + var char byte + var msg string + cr := byte('\r') + eol := byte('\n') + baf := make([]byte, 1) + buf := make([]byte, 128) + ptr := 0 + for { + n, err := c.handler.Read(baf) + if err != nil { + log.Fatal(err) + } + + if n != 1 { + continue + } + + char = baf[0] + + if char == cr || char == eol { + if ptr > 0 { + msg = string(buf[:ptr]) + fmt.Println(msg) + c.processSerialMsg(msg) + } + ptr = 0 + } else { + buf[ptr] = char + ptr++ + } + } +} + +// codeReady := []byte("READY") +// reReady := regexp.MustCompile("READY") +// reSync := regexp.MustCompile("SYNC,(.*),(.*),END") + +// n, err := s.Read(buf) +// if err != nil { +// log.Fatal(err) +// } +// msg := string(buf[:n]) +// fmt.Printf("%v", msg) + +// if reReady.Match(buf) { +// // fmt.Print("READY FROM RE") +// sync <- 0 +// } else { +// // match := reSync.FindAllSubmatch(buf, -1) +// // if len(match) > 0 { +// // r1 := string(match[0][1]) +// // r2 := string(match[0][2]) +// // r1i, _ := strconv.Atoi(r1) +// // r2i, _ := strconv.Atoi(r2) +// // x, y := getXY(float64(r1i), float64(r2i)) +// // fmt.Printf("R1: %v, R2:%v - X:%v - Y:%v\n", r1i, r2i, x, y) +// // } +// } diff --git a/pkg/plotter/font.go b/pkg/plotter/font.go new file mode 100644 index 0000000..0abfcb5 --- /dev/null +++ b/pkg/plotter/font.go @@ -0,0 +1,78 @@ +package plotter + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" +) + +type OneChar struct { + Letter rune + Root XMLFont + Path []*Point +} + +// XMLChar ... +type XMLChar struct { + XMLName xml.Name `xml:"letter"` + ID string `xml:"id,attr"` + Paths []string `xml:"path"` + // XMLName xml.Name `xml:"bite"` + // ID rune `xml:"id,attr"` + // Paths []XMLPath `xml:"path"` +} + +// XMLFont ... +type XMLFont struct { + XMLName xml.Name `xml:"root"` + Name string `xml:"name,attr"` + Width float64 `xml:"width,attr"` + Height float64 `xml:"height,attr"` + Space float64 `xml:"space,attr"` + Letters []XMLChar `xml:"letter"` +} + +type FontParser struct { + Root XMLFont + ByLetter map[rune][]*OnePath +} + +// Parse one font file +func (f *FontParser) Parse(fontPath string) { + // Open our xmlFile + xmlFile, err := os.Open(fontPath) + // if we os.Open returns an error then handle it + if err != nil { + fmt.Println(err) + } + + fmt.Println("Successfully Opened " + fontPath) + // 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) + + xml.Unmarshal(byteValue, &f.Root) + + f.ByLetter = make(map[rune][]*OnePath) + + for _, letter := range f.Root.Letters { + for _, path := range letter.Paths { + onePath := OnePath{} + onePath.Type = TypeText + for _, pt := range strings.Split(path, " ") { + ptSt := strings.Split(pt, ",") + point := Point{} + point.X, _ = strconv.ParseFloat(ptSt[0], 64) + point.Y, _ = strconv.ParseFloat(ptSt[1], 64) + onePath.List = append(onePath.List, &point) + } + letterID := []rune(letter.ID)[0] + f.ByLetter[letterID] = append(f.ByLetter[letterID], &onePath) + } + } +} diff --git a/pkg/plotter/svg.go b/pkg/plotter/svg.go new file mode 100644 index 0000000..a1d94a6 --- /dev/null +++ b/pkg/plotter/svg.go @@ -0,0 +1,294 @@ +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) { + +}