No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

290 líneas
6.9KB

  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. }
  29. var ErrInvalidPointID = errors.New("type cannot be converted to a PointID")
  30. func (p Points) Functions(pat *pattern.Pattern) map[string]govaluate.ExpressionFunction {
  31. return map[string]govaluate.ExpressionFunction{
  32. "DistanceBetween": func(args ...interface{}) (interface{}, error) {
  33. id0, err := toPointID(args[0])
  34. if err != nil {
  35. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  36. }
  37. id1, err := toPointID(args[1])
  38. if err != nil {
  39. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  40. }
  41. p0, err := p.getOrCreate(id0, pat, 0)
  42. if err != nil {
  43. return nil, fmt.Errorf("get or create point %q: %w", id0, err)
  44. }
  45. p1, err := p.getOrCreate(id1, pat, 0)
  46. if err != nil {
  47. return nil, fmt.Errorf("get or create point %q: %w", id1, err)
  48. }
  49. return p0.Position().Distance(p1.Position()), nil
  50. },
  51. "AngleBetween": func(args ...interface{}) (interface{}, error) {
  52. id0, err := toPointID(args[0])
  53. if err != nil {
  54. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  55. }
  56. id1, err := toPointID(args[1])
  57. if err != nil {
  58. return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
  59. }
  60. p0, err := p.getOrCreate(id0, pat, 0)
  61. if err != nil {
  62. return nil, fmt.Errorf("get or create point %q: %w", id0, err)
  63. }
  64. p1, err := p.getOrCreate(id1, pat, 0)
  65. if err != nil {
  66. return nil, fmt.Errorf("get or create point %q: %w", id1, err)
  67. }
  68. return p0.Vector().AngleBetween(p1.Vector()), nil
  69. },
  70. }
  71. }
  72. func toPointID(arg interface{}) (point.ID, error) {
  73. v1, ok := arg.(string)
  74. if !ok {
  75. f, ok := arg.(float64)
  76. if !ok {
  77. return "", fmt.Errorf("parsing %v as PointID: %w", arg, ErrInvalidPointID)
  78. }
  79. v1 = strconv.FormatFloat(f, 'f', -1, 64)
  80. }
  81. id1 := point.ID(v1)
  82. return id1, nil
  83. }
  84. // BetweenPoint contains the template information for a point in between two other points.
  85. type BetweenPoint struct {
  86. From point.ID `yaml:"from"`
  87. To point.ID `yaml:"to"`
  88. Offset *Value `yaml:"offset"`
  89. }
  90. func (p Points) evaluationFunctions() map[string]govaluate.ExpressionFunction {
  91. return map[string]govaluate.ExpressionFunction{
  92. "acos": func(args ...interface{}) (interface{}, error) {
  93. return math.Acos(args[0].(float64)), nil
  94. },
  95. "atan2": func(args ...interface{}) (interface{}, error) {
  96. return math.Atan2(args[0].(float64), args[1].(float64)), nil
  97. },
  98. }
  99. }
  100. // AddToPattern will add all points to the provided [pattern.Pattern].
  101. func (p Points) AddToPattern(pat *pattern.Pattern) error {
  102. for id := range p {
  103. if pat.GetPoint(id) == nil {
  104. err := p.addSingleToPattern(id, pat, 0)
  105. if err != nil {
  106. return err
  107. }
  108. }
  109. }
  110. return nil
  111. }
  112. func (p Points) addSingleToPattern(id point.ID, pat *pattern.Pattern, depth int) (err error) {
  113. templatePoint, ok := p[id]
  114. if !ok {
  115. return ErrPointNotFound
  116. }
  117. var newPoint point.Point
  118. switch {
  119. case templatePoint.RelativeTo != nil:
  120. newPoint, err = p.createRelative(id, pat, depth)
  121. if err != nil {
  122. return err
  123. }
  124. case templatePoint.Between != nil:
  125. newPoint, err = p.createBetween(id, pat, depth)
  126. if err != nil {
  127. return err
  128. }
  129. case templatePoint.Extend != nil:
  130. newPoint, err = p.createExtend(id, pat, depth)
  131. if err != nil {
  132. return err
  133. }
  134. default:
  135. x, y, r, err := templatePoint.Position.evaluate(pat.Parameters(), p.Functions(pat))
  136. if err != nil {
  137. return err
  138. }
  139. newPoint = point.NewAbsolutePoint(x, y, r, id)
  140. }
  141. if templatePoint.Hide {
  142. newPoint.SetHide()
  143. }
  144. pat.AddPoint(newPoint)
  145. return nil
  146. }
  147. func (p Points) createRelative(
  148. id point.ID,
  149. pat *pattern.Pattern,
  150. depth int,
  151. ) (*point.RelativePoint, error) {
  152. templatePoint, ok := p[id]
  153. if !ok {
  154. return nil, ErrPointNotFound
  155. }
  156. relativePointID := *templatePoint.RelativeTo
  157. if relativePointID == id || depth > maxRecursionDepth {
  158. return nil, ErrRelativePointRecursion
  159. }
  160. relativePoint, err := p.getOrCreate(relativePointID, pat, depth)
  161. if err != nil {
  162. return nil, err
  163. }
  164. x, y, r, err := templatePoint.Position.evaluate(pat.Parameters(), p.Functions(pat))
  165. if err != nil {
  166. return nil, err
  167. }
  168. return point.NewRelativePoint(relativePoint).
  169. WithXOffset(x).WithYOffset(y).WithRotationOffset(r).
  170. MarkWith(id), nil
  171. }
  172. //nolint:ireturn
  173. func (p Points) createBetween(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  174. newPoint, ok := p[id]
  175. if !ok {
  176. return nil, ErrPointNotFound
  177. }
  178. if newPoint.Between.To == id || newPoint.Between.From == id || depth > maxRecursionDepth {
  179. return nil, ErrRelativePointRecursion
  180. }
  181. fromPoint, err := p.getOrCreate(newPoint.Between.From, pat, depth)
  182. if err != nil {
  183. return nil, err
  184. }
  185. toPoint, err := p.getOrCreate(newPoint.Between.To, pat, depth)
  186. if err != nil {
  187. return nil, err
  188. }
  189. params := pat.Parameters()
  190. offset, err := newPoint.Between.Offset.Evaluate(params, p.Functions(pat))
  191. if err != nil {
  192. return nil, err
  193. }
  194. return point.NewBetweenPoint(fromPoint, toPoint, offset, id), nil
  195. }
  196. //nolint:ireturn
  197. func (p Points) getOrCreate(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  198. if pat.GetPoint(id) == nil {
  199. err := p.addSingleToPattern(id, pat, depth+1)
  200. if err != nil {
  201. return nil, err
  202. }
  203. }
  204. createdPoint := pat.GetPoint(id)
  205. if createdPoint == nil {
  206. panic("getPoint cannot be nil")
  207. }
  208. return createdPoint, nil
  209. }
  210. func (p Points) createExtend(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
  211. newPoint, ok := p[id]
  212. if !ok {
  213. return nil, ErrPointNotFound
  214. }
  215. if newPoint.Extend.To == id || newPoint.Extend.From == id || depth > maxRecursionDepth {
  216. return nil, ErrRelativePointRecursion
  217. }
  218. fromPoint, err := p.getOrCreate(newPoint.Extend.From, pat, depth)
  219. if err != nil {
  220. return nil, err
  221. }
  222. toPoint, err := p.getOrCreate(newPoint.Extend.To, pat, depth)
  223. if err != nil {
  224. return nil, err
  225. }
  226. params := pat.Parameters()
  227. offset, err := newPoint.Extend.Offset.Evaluate(params, p.Functions(pat))
  228. if err != nil {
  229. return nil, err
  230. }
  231. return point.NewExtendPoint(fromPoint, toPoint, offset, id), nil
  232. }
  233. type ExtendPoint struct {
  234. From point.ID `yaml:"from"`
  235. To point.ID `yaml:"to"`
  236. Offset *Value `yaml:"offset"`
  237. }