25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

248 satır
6.0KB

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