rasterizer.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package iconvg
  5. import (
  6. "image"
  7. "image/color"
  8. "image/draw"
  9. "math"
  10. "golang.org/x/exp/shiny/iconvg/internal/gradient"
  11. "golang.org/x/image/math/f64"
  12. "golang.org/x/image/vector"
  13. )
  14. const (
  15. smoothTypeNone = iota
  16. smoothTypeQuad
  17. smoothTypeCube
  18. )
  19. // Rasterizer is a Destination that draws an IconVG graphic onto a raster
  20. // image.
  21. //
  22. // The zero value is usable, in that it has no raster image to draw onto, so
  23. // that calling Decode with this Destination is a no-op (other than checking
  24. // the encoded form for errors in the byte code). Call SetDstImage to change
  25. // the raster image, before calling Decode or between calls to Decode.
  26. type Rasterizer struct {
  27. z vector.Rasterizer
  28. dst draw.Image
  29. r image.Rectangle
  30. drawOp draw.Op
  31. // scale and bias transforms the metadata.ViewBox rectangle to the (0, 0) -
  32. // (r.Dx(), r.Dy()) rectangle.
  33. scaleX float32
  34. biasX float32
  35. scaleY float32
  36. biasY float32
  37. metadata Metadata
  38. lod0 float32
  39. lod1 float32
  40. cSel uint8
  41. nSel uint8
  42. disabled bool
  43. firstStartPath bool
  44. prevSmoothType uint8
  45. prevSmoothPointX float32
  46. prevSmoothPointY float32
  47. fill image.Image
  48. flatColor color.RGBA
  49. flatImage image.Uniform
  50. gradient gradient.Gradient
  51. cReg [64]color.RGBA
  52. nReg [64]float32
  53. stops [64]gradient.Stop
  54. }
  55. // SetDstImage sets the Rasterizer to draw onto a destination image, given by
  56. // dst and r, with the given compositing operator.
  57. //
  58. // The IconVG graphic (which does not have a fixed size in pixels) will be
  59. // scaled in the X and Y dimensions to fit the rectangle r. The scaling factors
  60. // may differ in the two dimensions.
  61. func (z *Rasterizer) SetDstImage(dst draw.Image, r image.Rectangle, drawOp draw.Op) {
  62. z.dst = dst
  63. if r.Empty() {
  64. r = image.Rectangle{}
  65. }
  66. z.r = r
  67. z.drawOp = drawOp
  68. z.recalcTransform()
  69. }
  70. // Reset resets the Rasterizer for the given Metadata.
  71. func (z *Rasterizer) Reset(m Metadata) {
  72. z.metadata = m
  73. z.lod0 = 0
  74. z.lod1 = positiveInfinity
  75. z.cSel = 0
  76. z.nSel = 0
  77. z.firstStartPath = true
  78. z.prevSmoothType = smoothTypeNone
  79. z.prevSmoothPointX = 0
  80. z.prevSmoothPointY = 0
  81. z.cReg = m.Palette
  82. z.nReg = [64]float32{}
  83. z.recalcTransform()
  84. }
  85. func (z *Rasterizer) recalcTransform() {
  86. z.scaleX = float32(z.r.Dx()) / (z.metadata.ViewBox.Max[0] - z.metadata.ViewBox.Min[0])
  87. z.biasX = -z.metadata.ViewBox.Min[0]
  88. z.scaleY = float32(z.r.Dy()) / (z.metadata.ViewBox.Max[1] - z.metadata.ViewBox.Min[1])
  89. z.biasY = -z.metadata.ViewBox.Min[1]
  90. }
  91. func (z *Rasterizer) SetCSel(cSel uint8) { z.cSel = cSel & 0x3f }
  92. func (z *Rasterizer) SetNSel(nSel uint8) { z.nSel = nSel & 0x3f }
  93. func (z *Rasterizer) SetCReg(adj uint8, incr bool, c Color) {
  94. z.cReg[(z.cSel-adj)&0x3f] = c.Resolve(&z.metadata.Palette, &z.cReg)
  95. if incr {
  96. z.cSel++
  97. }
  98. }
  99. func (z *Rasterizer) SetNReg(adj uint8, incr bool, f float32) {
  100. z.nReg[(z.nSel-adj)&0x3f] = f
  101. if incr {
  102. z.nSel++
  103. }
  104. }
  105. func (z *Rasterizer) SetLOD(lod0, lod1 float32) {
  106. z.lod0, z.lod1 = lod0, lod1
  107. }
  108. func (z *Rasterizer) unabsX(x float32) float32 { return x/z.scaleX - z.biasX }
  109. func (z *Rasterizer) unabsY(y float32) float32 { return y/z.scaleY - z.biasY }
  110. func (z *Rasterizer) absX(x float32) float32 { return z.scaleX * (x + z.biasX) }
  111. func (z *Rasterizer) absY(y float32) float32 { return z.scaleY * (y + z.biasY) }
  112. func (z *Rasterizer) relX(x float32) float32 { return z.scaleX * x }
  113. func (z *Rasterizer) relY(y float32) float32 { return z.scaleY * y }
  114. func (z *Rasterizer) absVec2(x, y float32) (zx, zy float32) {
  115. return z.absX(x), z.absY(y)
  116. }
  117. func (z *Rasterizer) relVec2(x, y float32) (zx, zy float32) {
  118. px, py := z.z.Pen()
  119. return px + z.relX(x), py + z.relY(y)
  120. }
  121. // implicitSmoothPoint returns the implicit control point for smooth-quadratic
  122. // and smooth-cubic Bézier curves.
  123. //
  124. // https://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands says, "The first
  125. // control point is assumed to be the reflection of the second control point on
  126. // the previous command relative to the current point. (If there is no previous
  127. // command or if the previous command was not [a quadratic or cubic command],
  128. // assume the first control point is coincident with the current point.)"
  129. func (z *Rasterizer) implicitSmoothPoint(thisSmoothType uint8) (zx, zy float32) {
  130. px, py := z.z.Pen()
  131. if z.prevSmoothType != thisSmoothType {
  132. return px, py
  133. }
  134. return 2*px - z.prevSmoothPointX, 2*py - z.prevSmoothPointY
  135. }
  136. func (z *Rasterizer) initGradient(rgba color.RGBA) (ok bool) {
  137. nStops := int(rgba.R & 0x3f)
  138. cBase := int(rgba.G & 0x3f)
  139. nBase := int(rgba.B & 0x3f)
  140. prevN := negativeInfinity
  141. for i := 0; i < nStops; i++ {
  142. c := z.cReg[(cBase+i)&0x3f]
  143. if !validAlphaPremulColor(c) {
  144. return false
  145. }
  146. n := z.nReg[(nBase+i)&0x3f]
  147. if !(0 <= n && n <= 1) || !(n > prevN) {
  148. return false
  149. }
  150. prevN = n
  151. z.stops[i] = gradient.Stop{
  152. Offset: float64(n),
  153. RGBA64: color.RGBA64{
  154. R: uint16(c.R) * 0x101,
  155. G: uint16(c.G) * 0x101,
  156. B: uint16(c.B) * 0x101,
  157. A: uint16(c.A) * 0x101,
  158. },
  159. }
  160. }
  161. // The affine transformation matrix in the IconVG graphic, stored in 6
  162. // contiguous NREG registers, goes from graphic coordinate space (i.e. the
  163. // metadata viewBox) to the gradient coordinate space. We need it to start
  164. // in pixel space, not graphic coordinate space.
  165. invZSX := 1 / float64(z.scaleX)
  166. invZSY := 1 / float64(z.scaleY)
  167. zBX := float64(z.biasX)
  168. zBY := float64(z.biasY)
  169. a := float64(z.nReg[(nBase-6)&0x3f])
  170. b := float64(z.nReg[(nBase-5)&0x3f])
  171. c := float64(z.nReg[(nBase-4)&0x3f])
  172. d := float64(z.nReg[(nBase-3)&0x3f])
  173. e := float64(z.nReg[(nBase-2)&0x3f])
  174. f := float64(z.nReg[(nBase-1)&0x3f])
  175. pix2Grad := f64.Aff3{
  176. a * invZSX,
  177. b * invZSY,
  178. c - a*zBX - b*zBY,
  179. d * invZSX,
  180. e * invZSY,
  181. f - d*zBX - e*zBY,
  182. }
  183. shape := gradient.ShapeLinear
  184. if (rgba.B>>6)&0x01 != 0 {
  185. shape = gradient.ShapeRadial
  186. }
  187. z.gradient.Init(
  188. shape,
  189. gradient.Spread(rgba.G>>6),
  190. pix2Grad,
  191. z.stops[:nStops],
  192. )
  193. return true
  194. }
  195. func (z *Rasterizer) StartPath(adj uint8, x, y float32) {
  196. z.flatColor = z.cReg[(z.cSel-adj)&0x3f]
  197. if validAlphaPremulColor(z.flatColor) {
  198. z.flatImage.C = &z.flatColor
  199. z.fill = &z.flatImage
  200. z.disabled = z.flatColor.A == 0
  201. } else if z.flatColor.A == 0x00 && z.flatColor.B&0x80 != 0 {
  202. z.fill = &z.gradient
  203. z.disabled = !z.initGradient(z.flatColor)
  204. } else {
  205. z.fill = nil
  206. z.disabled = true
  207. }
  208. width, height := z.r.Dx(), z.r.Dy()
  209. h := float32(height)
  210. z.disabled = z.disabled || !(z.lod0 <= h && h < z.lod1)
  211. if z.disabled {
  212. return
  213. }
  214. z.z.Reset(width, height)
  215. if z.firstStartPath {
  216. z.firstStartPath = false
  217. z.z.DrawOp = z.drawOp
  218. }
  219. z.prevSmoothType = smoothTypeNone
  220. z.z.MoveTo(z.absVec2(x, y))
  221. }
  222. func (z *Rasterizer) ClosePathEndPath() {
  223. if z.disabled {
  224. return
  225. }
  226. z.z.ClosePath()
  227. if z.dst == nil {
  228. return
  229. }
  230. z.z.Draw(z.dst, z.r, z.fill, image.Point{})
  231. }
  232. func (z *Rasterizer) ClosePathAbsMoveTo(x, y float32) {
  233. if z.disabled {
  234. return
  235. }
  236. z.prevSmoothType = smoothTypeNone
  237. z.z.ClosePath()
  238. z.z.MoveTo(z.absVec2(x, y))
  239. }
  240. func (z *Rasterizer) ClosePathRelMoveTo(x, y float32) {
  241. if z.disabled {
  242. return
  243. }
  244. z.prevSmoothType = smoothTypeNone
  245. z.z.ClosePath()
  246. z.z.MoveTo(z.relVec2(x, y))
  247. }
  248. func (z *Rasterizer) AbsHLineTo(x float32) {
  249. if z.disabled {
  250. return
  251. }
  252. _, py := z.z.Pen()
  253. z.prevSmoothType = smoothTypeNone
  254. z.z.LineTo(z.absX(x), py)
  255. }
  256. func (z *Rasterizer) RelHLineTo(x float32) {
  257. if z.disabled {
  258. return
  259. }
  260. px, py := z.z.Pen()
  261. z.prevSmoothType = smoothTypeNone
  262. z.z.LineTo(px+z.relX(x), py)
  263. }
  264. func (z *Rasterizer) AbsVLineTo(y float32) {
  265. if z.disabled {
  266. return
  267. }
  268. px, _ := z.z.Pen()
  269. z.prevSmoothType = smoothTypeNone
  270. z.z.LineTo(px, z.absY(y))
  271. }
  272. func (z *Rasterizer) RelVLineTo(y float32) {
  273. if z.disabled {
  274. return
  275. }
  276. px, py := z.z.Pen()
  277. z.prevSmoothType = smoothTypeNone
  278. z.z.LineTo(px, py+z.relY(y))
  279. }
  280. func (z *Rasterizer) AbsLineTo(x, y float32) {
  281. if z.disabled {
  282. return
  283. }
  284. z.prevSmoothType = smoothTypeNone
  285. z.z.LineTo(z.absVec2(x, y))
  286. }
  287. func (z *Rasterizer) RelLineTo(x, y float32) {
  288. if z.disabled {
  289. return
  290. }
  291. z.prevSmoothType = smoothTypeNone
  292. z.z.LineTo(z.relVec2(x, y))
  293. }
  294. func (z *Rasterizer) AbsSmoothQuadTo(x, y float32) {
  295. if z.disabled {
  296. return
  297. }
  298. x1, y1 := z.implicitSmoothPoint(smoothTypeQuad)
  299. x, y = z.absVec2(x, y)
  300. z.prevSmoothType = smoothTypeQuad
  301. z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
  302. z.z.QuadTo(x1, y1, x, y)
  303. }
  304. func (z *Rasterizer) RelSmoothQuadTo(x, y float32) {
  305. if z.disabled {
  306. return
  307. }
  308. x1, y1 := z.implicitSmoothPoint(smoothTypeQuad)
  309. x, y = z.relVec2(x, y)
  310. z.prevSmoothType = smoothTypeQuad
  311. z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
  312. z.z.QuadTo(x1, y1, x, y)
  313. }
  314. func (z *Rasterizer) AbsQuadTo(x1, y1, x, y float32) {
  315. if z.disabled {
  316. return
  317. }
  318. x1, y1 = z.absVec2(x1, y1)
  319. x, y = z.absVec2(x, y)
  320. z.prevSmoothType = smoothTypeQuad
  321. z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
  322. z.z.QuadTo(x1, y1, x, y)
  323. }
  324. func (z *Rasterizer) RelQuadTo(x1, y1, x, y float32) {
  325. if z.disabled {
  326. return
  327. }
  328. x1, y1 = z.relVec2(x1, y1)
  329. x, y = z.relVec2(x, y)
  330. z.prevSmoothType = smoothTypeQuad
  331. z.prevSmoothPointX, z.prevSmoothPointY = x1, y1
  332. z.z.QuadTo(x1, y1, x, y)
  333. }
  334. func (z *Rasterizer) AbsSmoothCubeTo(x2, y2, x, y float32) {
  335. if z.disabled {
  336. return
  337. }
  338. x1, y1 := z.implicitSmoothPoint(smoothTypeCube)
  339. x2, y2 = z.absVec2(x2, y2)
  340. x, y = z.absVec2(x, y)
  341. z.prevSmoothType = smoothTypeCube
  342. z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
  343. z.z.CubeTo(x1, y1, x2, y2, x, y)
  344. }
  345. func (z *Rasterizer) RelSmoothCubeTo(x2, y2, x, y float32) {
  346. if z.disabled {
  347. return
  348. }
  349. x1, y1 := z.implicitSmoothPoint(smoothTypeCube)
  350. x2, y2 = z.relVec2(x2, y2)
  351. x, y = z.relVec2(x, y)
  352. z.prevSmoothType = smoothTypeCube
  353. z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
  354. z.z.CubeTo(x1, y1, x2, y2, x, y)
  355. }
  356. func (z *Rasterizer) AbsCubeTo(x1, y1, x2, y2, x, y float32) {
  357. if z.disabled {
  358. return
  359. }
  360. x1, y1 = z.absVec2(x1, y1)
  361. x2, y2 = z.absVec2(x2, y2)
  362. x, y = z.absVec2(x, y)
  363. z.prevSmoothType = smoothTypeCube
  364. z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
  365. z.z.CubeTo(x1, y1, x2, y2, x, y)
  366. }
  367. func (z *Rasterizer) RelCubeTo(x1, y1, x2, y2, x, y float32) {
  368. if z.disabled {
  369. return
  370. }
  371. x1, y1 = z.relVec2(x1, y1)
  372. x2, y2 = z.relVec2(x2, y2)
  373. x, y = z.relVec2(x, y)
  374. z.prevSmoothType = smoothTypeCube
  375. z.prevSmoothPointX, z.prevSmoothPointY = x2, y2
  376. z.z.CubeTo(x1, y1, x2, y2, x, y)
  377. }
  378. func (z *Rasterizer) AbsArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
  379. if z.disabled {
  380. return
  381. }
  382. z.prevSmoothType = smoothTypeNone
  383. // We follow the "Conversion from endpoint to center parameterization"
  384. // algorithm as per
  385. // https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
  386. // There seems to be a bug in the spec's "implementation notes".
  387. //
  388. // Actual implementations, such as
  389. // - https://git.gnome.org/browse/librsvg/tree/rsvg-path.c
  390. // - http://svn.apache.org/repos/asf/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/ext/awt/geom/ExtendedGeneralPath.java
  391. // - https://java.net/projects/svgsalamander/sources/svn/content/trunk/svg-core/src/main/java/com/kitfox/svg/pathcmd/Arc.java
  392. // - https://github.com/millermedeiros/SVGParser/blob/master/com/millermedeiros/geom/SVGArc.as
  393. // do something slightly different (marked with a †).
  394. // (†) The Abs isn't part of the spec. Neither is checking that Rx and Ry
  395. // are non-zero (and non-NaN).
  396. Rx := math.Abs(float64(rx))
  397. Ry := math.Abs(float64(ry))
  398. if !(Rx > 0 && Ry > 0) {
  399. z.z.LineTo(x, y)
  400. return
  401. }
  402. // We work in IconVG coordinates (e.g. from -32 to +32 by default), rather
  403. // than destination image coordinates (e.g. the width of the dst image),
  404. // since the rx and ry radii also need to be scaled, but their scaling
  405. // factors can be different, and aren't trivial to calculate due to
  406. // xAxisRotation.
  407. //
  408. // We convert back to destination image coordinates via absX and absY calls
  409. // later, during arcSegmentTo.
  410. penX, penY := z.z.Pen()
  411. x1 := float64(z.unabsX(penX))
  412. y1 := float64(z.unabsY(penY))
  413. x2 := float64(x)
  414. y2 := float64(y)
  415. phi := 2 * math.Pi * float64(xAxisRotation)
  416. // Step 1: Compute (x1′, y1′)
  417. halfDx := (x1 - x2) / 2
  418. halfDy := (y1 - y2) / 2
  419. cosPhi := math.Cos(phi)
  420. sinPhi := math.Sin(phi)
  421. x1Prime := +cosPhi*halfDx + sinPhi*halfDy
  422. y1Prime := -sinPhi*halfDx + cosPhi*halfDy
  423. // Step 2: Compute (cx′, cy′)
  424. rxSq := Rx * Rx
  425. rySq := Ry * Ry
  426. x1PrimeSq := x1Prime * x1Prime
  427. y1PrimeSq := y1Prime * y1Prime
  428. // (†) Check that the radii are large enough.
  429. radiiCheck := x1PrimeSq/rxSq + y1PrimeSq/rySq
  430. if radiiCheck > 1 {
  431. c := math.Sqrt(radiiCheck)
  432. Rx *= c
  433. Ry *= c
  434. rxSq = Rx * Rx
  435. rySq = Ry * Ry
  436. }
  437. denom := rxSq*y1PrimeSq + rySq*x1PrimeSq
  438. step2 := 0.0
  439. if a := rxSq*rySq/denom - 1; a > 0 {
  440. step2 = math.Sqrt(a)
  441. }
  442. if largeArc == sweep {
  443. step2 = -step2
  444. }
  445. cxPrime := +step2 * Rx * y1Prime / Ry
  446. cyPrime := -step2 * Ry * x1Prime / Rx
  447. // Step 3: Compute (cx, cy) from (cx′, cy′)
  448. cx := +cosPhi*cxPrime - sinPhi*cyPrime + (x1+x2)/2
  449. cy := +sinPhi*cxPrime + cosPhi*cyPrime + (y1+y2)/2
  450. // Step 4: Compute θ1 and Δθ
  451. ax := (+x1Prime - cxPrime) / Rx
  452. ay := (+y1Prime - cyPrime) / Ry
  453. bx := (-x1Prime - cxPrime) / Rx
  454. by := (-y1Prime - cyPrime) / Ry
  455. theta1 := angle(1, 0, ax, ay)
  456. deltaTheta := angle(ax, ay, bx, by)
  457. if sweep {
  458. if deltaTheta < 0 {
  459. deltaTheta += 2 * math.Pi
  460. }
  461. } else {
  462. if deltaTheta > 0 {
  463. deltaTheta -= 2 * math.Pi
  464. }
  465. }
  466. // This ends the
  467. // https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
  468. // algorithm. What follows below is specific to this implementation.
  469. // We approximate an arc by one or more cubic Bézier curves.
  470. n := int(math.Ceil(math.Abs(deltaTheta) / (math.Pi/2 + 0.001)))
  471. for i := 0; i < n; i++ {
  472. z.arcSegmentTo(cx, cy,
  473. theta1+deltaTheta*float64(i+0)/float64(n),
  474. theta1+deltaTheta*float64(i+1)/float64(n),
  475. Rx, Ry, cosPhi, sinPhi,
  476. )
  477. }
  478. }
  479. // arcSegmentTo approximates an arc by a cubic Bézier curve. The mathematical
  480. // formulae for the control points are the same as that used by librsvg.
  481. func (z *Rasterizer) arcSegmentTo(cx, cy, theta1, theta2, rx, ry, cosPhi, sinPhi float64) {
  482. halfDeltaTheta := (theta2 - theta1) * 0.5
  483. q := math.Sin(halfDeltaTheta * 0.5)
  484. t := (8 * q * q) / (3 * math.Sin(halfDeltaTheta))
  485. cos1 := math.Cos(theta1)
  486. sin1 := math.Sin(theta1)
  487. cos2 := math.Cos(theta2)
  488. sin2 := math.Sin(theta2)
  489. x1 := rx * (+cos1 - t*sin1)
  490. y1 := ry * (+sin1 + t*cos1)
  491. x2 := rx * (+cos2 + t*sin2)
  492. y2 := ry * (+sin2 - t*cos2)
  493. x3 := rx * (+cos2)
  494. y3 := ry * (+sin2)
  495. z.z.CubeTo(
  496. z.absX(float32(cx+cosPhi*x1-sinPhi*y1)),
  497. z.absY(float32(cy+sinPhi*x1+cosPhi*y1)),
  498. z.absX(float32(cx+cosPhi*x2-sinPhi*y2)),
  499. z.absY(float32(cy+sinPhi*x2+cosPhi*y2)),
  500. z.absX(float32(cx+cosPhi*x3-sinPhi*y3)),
  501. z.absY(float32(cy+sinPhi*x3+cosPhi*y3)),
  502. )
  503. }
  504. func (z *Rasterizer) RelArcTo(rx, ry, xAxisRotation float32, largeArc, sweep bool, x, y float32) {
  505. ax, ay := z.relVec2(x, y)
  506. z.AbsArcTo(rx, ry, xAxisRotation, largeArc, sweep, z.unabsX(ax), z.unabsY(ay))
  507. }
  508. // angle returns the angle between the u and v vectors.
  509. func angle(ux, uy, vx, vy float64) float64 {
  510. uNorm := math.Sqrt(ux*ux + uy*uy)
  511. vNorm := math.Sqrt(vx*vx + vy*vy)
  512. norm := uNorm * vNorm
  513. cos := (ux*vx + uy*vy) / norm
  514. ret := 0.0
  515. if cos <= -1 {
  516. ret = math.Pi
  517. } else if cos >= +1 {
  518. ret = 0
  519. } else {
  520. ret = math.Acos(cos)
  521. }
  522. if ux*vy < uy*vx {
  523. return -ret
  524. }
  525. return +ret
  526. }