Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

340 Zeilen
8.2KB

  1. package template
  2. import (
  3. "errors"
  4. "fmt"
  5. "gopkg.in/Knetic/govaluate.v3"
  6. "math"
  7. "strconv"
  8. "git.wtrh.nl/wouter/gopatterns/pkg/pattern"
  9. "git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
  10. )
  11. const maxRecursionDepth = 100
  12. var (
  13. // ErrPointNotFound is returned when a required point is not defined.
  14. ErrPointNotFound = errors.New("required point not found")
  15. // ErrRelativePointRecursion is returned when a points are relative to itself.
  16. ErrRelativePointRecursion = errors.New("point cannot be relative to itself")
  17. )
  18. // Points contains a map with points.
  19. type Points map[point.ID]Point
  20. // Point contains the template information for a point.
  21. type Point struct {
  22. Position Position `yaml:"position"`
  23. RelativeTo *point.ID `yaml:"relativeTo,omitempty"`
  24. Description string `yaml:"description"`
  25. Between *BetweenPoint `yaml:"between"`
  26. Extend *ExtendPoint `yaml:"extend"`
  27. Hide bool `yaml:"hide"`
  28. Polar *PolarPoint `yaml:"polar"`
  29. }
  30. var ErrInvalidPointID = errors.New("type cannot be converted to a PointID")
  31. func (p Points) Functions(pat *pattern.Pattern) map[string]govaluate.ExpressionFunction {
  32. return map[string]govaluate.ExpressionFunction{
  33. "DistanceBetween": func(args ...interface{}) (interface{}, error) {
  34. id0, err := toPointID(args[0])
  35. if err != nil {
  36. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  37. }
  38. id1, err := toPointID(args[1])
  39. if err != nil {
  40. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  41. }
  42. p0, err := p.getOrCreate(id0, pat, 0)
  43. if err != nil {
  44. return nil, fmt.Errorf("get or create point %q: %w", id0, err)
  45. }
  46. p1, err := p.getOrCreate(id1, pat, 0)
  47. if err != nil {
  48. return nil, fmt.Errorf("get or create point %q: %w", id1, err)
  49. }
  50. return p0.Position().Distance(p1.Position()), nil
  51. },
  52. "AngleBetween": func(args ...interface{}) (interface{}, error) {
  53. id0, err := toPointID(args[0])
  54. if err != nil {
  55. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  56. }
  57. id1, err := toPointID(args[1])
  58. if err != nil {
  59. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  60. }
  61. p0, err := p.getOrCreate(id0, pat, 0)
  62. if err != nil {
  63. return nil, fmt.Errorf("get or create point %q: %w", id0, err)
  64. }
  65. p1, err := p.getOrCreate(id1, pat, 0)
  66. if err != nil {
  67. return nil, fmt.Errorf("get or create point %q: %w", id1, err)
  68. }
  69. return p0.Vector().AngleBetween(p1.Vector()), nil
  70. },
  71. }
  72. }
  73. func toPointID(arg interface{}) (point.ID, error) {
  74. v1, ok := arg.(string)
  75. if !ok {
  76. f, ok := arg.(float64)
  77. if !ok {
  78. return "", fmt.Errorf("parsing %v as PointID: %w", arg, ErrInvalidPointID)
  79. }
  80. v1 = strconv.FormatFloat(f, 'f', -1, 64)
  81. }
  82. id1 := point.ID(v1)
  83. return id1, nil
  84. }
  85. // BetweenPoint contains the template information for a point in between two other points.
  86. type BetweenPoint struct {
  87. From point.ID `yaml:"from"`
  88. To point.ID `yaml:"to"`
  89. Offset *Value `yaml:"offset"`
  90. }
  91. func (p Points) evaluationFunctions() map[string]govaluate.ExpressionFunction {
  92. return map[string]govaluate.ExpressionFunction{
  93. "acos": func(args ...interface{}) (interface{}, error) {
  94. return math.Acos(args[0].(float64)), nil
  95. },
  96. "atan2": func(args ...interface{}) (interface{}, error) {
  97. return math.Atan2(args[0].(float64), args[1].(float64)), nil
  98. },
  99. }
  100. }
  101. // AddToPattern will add all points to the provided [pattern.Pattern].
  102. func (p Points) AddToPattern(pat *pattern.Pattern) error {
  103. for id := range p {
  104. if pat.GetPoint(id) == nil {
  105. err := p.addSingleToPattern(id, pat, 0)
  106. if err != nil {
  107. return err
  108. }
  109. }
  110. }
  111. return nil
  112. }
  113. func (p Points) addSingleToPattern(id point.ID, pat *pattern.Pattern, depth int) (err error) {
  114. templatePoint, ok := p[id]
  115. if !ok {
  116. return ErrPointNotFound
  117. }
  118. var newPoint point.Point
  119. switch {
  120. case templatePoint.RelativeTo != nil && templatePoint.Polar != nil:
  121. newPoint, err = p.createPolar(id, pat, depth)
  122. if err != nil {
  123. return err
  124. }
  125. case templatePoint.RelativeTo != nil:
  126. newPoint, err = p.createRelative(id, pat, depth)
  127. if err != nil {
  128. return err
  129. }
  130. case templatePoint.Between != nil:
  131. newPoint, err = p.createBetween(id, pat, depth)
  132. if err != nil {
  133. return err
  134. }
  135. case templatePoint.Extend != nil:
  136. newPoint, err = p.createExtend(id, pat, depth)
  137. if err != nil {
  138. return err
  139. }
  140. default:
  141. x, y, r, err := templatePoint.Position.evaluate(pat.Parameters(), p.Functions(pat))
  142. if err != nil {
  143. return err
  144. }
  145. newPoint = point.NewAbsolutePoint(x, y, r, id)
  146. }
  147. if templatePoint.Hide {
  148. newPoint.SetHide()
  149. }
  150. pat.AddPoint(newPoint)
  151. return nil
  152. }
  153. func (p Points) createRelative(
  154. id point.ID,
  155. pat *pattern.Pattern,
  156. depth int,
  157. ) (*point.RelativePoint, error) {
  158. templatePoint, ok := p[id]
  159. if !ok {
  160. return nil, ErrPointNotFound
  161. }
  162. relativePointID := *templatePoint.RelativeTo
  163. if relativePointID == id || depth > maxRecursionDepth {
  164. return nil, ErrRelativePointRecursion
  165. }
  166. relativePoint, err := p.getOrCreate(relativePointID, pat, depth)
  167. if err != nil {
  168. return nil, err
  169. }
  170. x, y, r, err := templatePoint.Position.evaluate(pat.Parameters(), p.Functions(pat))
  171. if err != nil {
  172. return nil, err
  173. }
  174. return point.NewRelativePoint(relativePoint).
  175. WithXOffset(x).WithYOffset(y).WithRotationOffset(r).
  176. MarkWith(id), nil
  177. }
  178. //nolint:ireturn
  179. func (p Points) createBetween(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  180. newPoint, ok := p[id]
  181. if !ok {
  182. return nil, ErrPointNotFound
  183. }
  184. if newPoint.Between.To == id || newPoint.Between.From == id || depth > maxRecursionDepth {
  185. return nil, ErrRelativePointRecursion
  186. }
  187. fromPoint, err := p.getOrCreate(newPoint.Between.From, pat, depth)
  188. if err != nil {
  189. return nil, err
  190. }
  191. toPoint, err := p.getOrCreate(newPoint.Between.To, pat, depth)
  192. if err != nil {
  193. return nil, err
  194. }
  195. params := pat.Parameters()
  196. offset, err := newPoint.Between.Offset.Evaluate(params, p.Functions(pat))
  197. if err != nil {
  198. return nil, err
  199. }
  200. return point.NewBetweenPoint(fromPoint, toPoint, offset, id), nil
  201. }
  202. //nolint:ireturn
  203. func (p Points) getOrCreate(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  204. if pat.GetPoint(id) == nil {
  205. err := p.addSingleToPattern(id, pat, depth+1)
  206. if err != nil {
  207. return nil, err
  208. }
  209. }
  210. createdPoint := pat.GetPoint(id)
  211. if createdPoint == nil {
  212. panic("getPoint cannot be nil")
  213. }
  214. return createdPoint, nil
  215. }
  216. func (p Points) createExtend(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  217. newPoint, ok := p[id]
  218. if !ok {
  219. return nil, ErrPointNotFound
  220. }
  221. if newPoint.Extend.To == id || newPoint.Extend.From == id || depth > maxRecursionDepth {
  222. return nil, ErrRelativePointRecursion
  223. }
  224. fromPoint, err := p.getOrCreate(newPoint.Extend.From, pat, depth)
  225. if err != nil {
  226. return nil, err
  227. }
  228. toPoint, err := p.getOrCreate(newPoint.Extend.To, pat, depth)
  229. if err != nil {
  230. return nil, err
  231. }
  232. params := pat.Parameters()
  233. offset, err := newPoint.Extend.Offset.Evaluate(params, p.Functions(pat))
  234. if err != nil {
  235. return nil, err
  236. }
  237. return point.NewExtendPoint(fromPoint, toPoint, offset, id), nil
  238. }
  239. func (p Points) createPolar(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  240. templatePoint, ok := p[id]
  241. if !ok {
  242. return nil, ErrPointNotFound
  243. }
  244. relativePointID := *templatePoint.RelativeTo
  245. if relativePointID == id || depth > maxRecursionDepth {
  246. return nil, ErrRelativePointRecursion
  247. }
  248. relativePoint, err := p.getOrCreate(relativePointID, pat, depth)
  249. if err != nil {
  250. return nil, err
  251. }
  252. x, y, err := templatePoint.Polar.evaluate(pat.Parameters(), p.Functions(pat))
  253. if err != nil {
  254. return nil, err
  255. }
  256. return point.NewRelativePoint(relativePoint).
  257. WithXOffset(x).WithYOffset(y).MarkWith(id), nil
  258. }
  259. type ExtendPoint struct {
  260. From point.ID `yaml:"from"`
  261. To point.ID `yaml:"to"`
  262. Offset *Value `yaml:"offset"`
  263. }
  264. type PolarPoint struct {
  265. Length *Value `yaml:"length"`
  266. Rotation *Value `yaml:"rotation"`
  267. }
  268. func (p PolarPoint) evaluate(params govaluate.MapParameters, funcs map[string]govaluate.ExpressionFunction) (x, y float64, err error) {
  269. rotation, err := p.Rotation.Evaluate(params, funcs)
  270. if err != nil {
  271. return 0, 0, err
  272. }
  273. length, err := p.Length.Evaluate(params, funcs)
  274. if err != nil {
  275. return 0, 0, err
  276. }
  277. return math.Sin(rotation) * -length, math.Cos(rotation) * -length, nil
  278. }