選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

350 行
8.5KB

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