Browse Source

first commit

master
P.BARRY 6 years ago
commit
1270f0cbc9
12 changed files with 1050 additions and 0 deletions
  1. 55
      cfg/config.go
  2. 62
      cmd/gamepad/gamepad.go
  3. 45
      cmd/plotter/plotter.go
  4. 22
      cmd/setup/setup.go
  5. 97
      drawing.svg
  6. 96
      font-default.xml
  7. 7
      go.mod
  8. 6
      go.sum
  9. 38
      main.go
  10. 250
      pkg/comm/main.go
  11. 78
      pkg/plotter/font.go
  12. 294
      pkg/plotter/svg.go

55
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
}

62
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.")
}

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

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

97
drawing.svg

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="200cm"
height="120cm"
viewBox="0 0 7086.614 4251.9685"
id="svg2"
version="1.1"
inkscape:version="0.91 r"
sodipodi:docname="drawing.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.175"
inkscape:cx="3198.9842"
inkscape:cy="2091.4294"
inkscape:document-units="px"
inkscape:current-layer="svg2"
showgrid="false"
units="cm"
inkscape:window-width="1920"
inkscape:window-height="1023"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 199.99976,4080.5397 6554.2855,337.68255"
id="path3370"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4319.9998,2566.254 1291.4286,-11.4286 11.4285,-1080 1051.4286,62.8572"
id="path3372"
inkscape:connector-curvature="0" />
<circle
style="fill:none;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path3368-3"
cx="3805.7141"
cy="1806.2543"
r="1005.7143" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 565.71405,1131.9685 1262.8569,343.39708 1759.9998,1183.3971 542.8569,3086.2542"
id="path3384"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2845.7141,320.53993 -17.1429,331.42857 1880,-177.14285 z"
id="path3386"
inkscape:connector-curvature="0" />
<flowRoot
xml:space="preserve"
id="flowRoot3483"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:112.99999952%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="translate(-2.4414063e-4,0)"><flowRegion
id="flowRegion3485"><rect
id="rect3487"
width="1011.4288"
height="148.57152"
x="217.14285"
y="360.53995"
style="line-height:112.99999952%" /></flowRegion><flowPara
id="flowPara3489"
style="font-size:125px;line-height:112.99999952%">HALLO WORLD</flowPara></flowRoot> <circle
style="fill:none;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="path3368-3-6"
cx="2588.5713"
cy="2686.2542"
r="1005.7143" />
</svg>

96
font-default.xml

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root name="default" width="10" height="20" space="2">
<letter id=" ">
<path>0,0</path>
</letter>
<letter id="A">
<path>0,20 5,0 10,20</path>
<path>3,10 7,10</path>
</letter>
<letter id="B">
<path>0,0 0,20 10,20 10,10 0,10</path>
<path>0,0 6,0 6,10</path>
</letter>
<letter id="C">
<path>10,0 0,0 0,20 10,20</path>
</letter>
<letter id="D">
<path>0,0 7,0 10,3 10,17 8,20 0,20 0,0</path>
</letter>
<letter id="E">
<path>10,0 0,0 0,10 10,10 0,10 0,20 10,20</path>
</letter>
<letter id="F">
<path>10,0 0,0 0,10 10,10 0,10 0,20</path>
</letter>
<letter id="G">
<path>10,0 0,0 0,20 10,20 10,10 5,10</path>
</letter>
<letter id="H">
<path>0,0 0,20 0,10 10,10 10,0 10,20</path>
</letter>
<letter id="I">
<path>2,0 8,0 5,0 5,20 2,20 8,20</path>
</letter>
<letter id="J">
<path>0,10 0,20 10,20 10,0</path>
</letter>
<letter id="K">
<path>0,0 0,20</path>
<path>10,0 0,10 10,20</path>
</letter>
<letter id="L">
<path>0,0 0,20 10,20</path>
</letter>
<letter id="M">
<path>0,20 0,0 5,10 10,0 10,20</path>
</letter>
<letter id="N">
<path>0,20 0,0 10,20 10,0</path>
</letter>
<letter id="O">
<path>0,2 2,0 8,0 10,2 10,18 8,20 2,20 0,18 0,2</path>
</letter>
<letter id="P">
<path>0,20 0,0 8,0 10,2 10,8 8,10 0,10</path>
</letter>
<letter id="Q">
<path>0,2 2,0 8,0 10,2 10,18 8,20 2,20 0,18 0,2</path>
<path>7,17 10,20</path>
</letter>
<letter id="R">
<path>0,20 0,0 8,0 10,2 10,8 8,10 0,10</path>
<path>8,10 10,12 10,20</path>
</letter>
<letter id="S">
<path>10,0 2,0 0,2 0,8 2,10 8,10 10,12 10,18 8,20 0,20</path>
</letter>
<letter id="T">
<path>0,0 10,0</path>
<path>5,0 5,20</path>
</letter>
<letter id="U">
<path>0,2 0,18 2,20 8,20 10,18 10,2</path>
</letter>
<letter id="V">
<path>0,0 5,20 10,0</path>
</letter>
<letter id="W">
<path>0,0 3,20 5,10 7,20 10,0</path>
</letter>
<letter id="X">
<path>0,0 10,20</path>
<path>10,0 0,20</path>
</letter>
<letter id="Y">
<path>0,0 5,10 10,0</path>
<path>5,10 5,20</path>
</letter>
<letter id="Z">
<path>0,0 10,0 0,20 10,20</path>
</letter>
<letter id="b">
<path>0,0 0,7 10,12 10,18 8,20 0,20 0,0</path>
</letter>
</root>

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

6
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=

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

250
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)
// // }
// }

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

294
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) {
}
Loading…
Cancel
Save