packages feed

diagrams-lib 0.7.1.1 → 1.5.1

raw patch · 182 files changed

Files

+ CHANGELOG.md view
@@ -0,0 +1,1837 @@+## [v1.5.1](https://github.com/diagrams/diagrams-lib/tree/v1.5.1) (2025-12-02)++- New `Semigroup` instance for located lines (i.e. `Located (Trail'+  Line v n)`), which adds an extra segment from the end of the first+  to the start of the second.++## [v1.5.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.5.0.1) (2025-08-25)++- Fix bug in `bezierFindRoot` that caused diagrams to hang when+  computing the trace of certain degenerate Bezier curves.  See+  https://github.com/diagrams/diagrams-contrib/issues/91 .++## [v1.5-r2](https://github.com/diagrams/diagrams-lib/tree/v1.5-r2) (2025-06-12)++- Allow `optparse-applicative-0.19`++## [v1.5-r1](https://github.com/diagrams/diagrams-lib/tree/v1.5-r1) (2025-05-17)++- Allow `monoid-extras-0.7`++## [v1.5](https://github.com/diagrams/diagrams-lib/tree/v1.5) (2025-02-13)++- Allow `base-4.21` and test on GHC 9.12+- Remove looped compilation mode options ([#372](https://github.com/diagrams/diagrams-lib/pull/372))+    - This allows us to remove the `fsnotify` dependency, which in+      turn allows compiling diagrams to Wasm.+    - To recover similar functionality (automatically recompiling+      every time a source file changes), try an external tool like+      `ghcid` or `ghciwatch`, or, more generically, `entr`.++## [v1.4.7](https://github.com/diagrams/diagrams-lib/tree/v1.4.7) (2024-10-26)++- Switch to `data-default-0.8` instead of `data-default-class`++## [v1.4.6.2-r3](https://github.com/diagrams/diagrams-lib/tree/v1.4.6.2-r3) (2024-08-23)++- Allow `hashable-1.5`++## [v1.4.6.2-r2](https://github.com/diagrams/diagrams-lib/tree/v1.4.6.2-r2) (2024-07-01)++- Allow `tasty-quickcheck-0.11`++## [v1.4.6.2-r1](https://github.com/diagrams/diagrams-lib/tree/v1.4.6.2-r1) (2024-05-01)++- Allow `base-4.21` (GHC 9.10), `containers-0.7`, `lens-5.3`, and `filepath-1.5`++## [v1.4.6.2](https://github.com/diagrams/diagrams-lib/tree/v1.4.6.2) (2024-05-01)++- Support for `linear-1.23`.  This version of `linear` introduces a+  breaking change, so add an internal compatibility module that allows+  diagrams to work with any version of `linear` both before and after+  the change.++## [v1.4.6.1](https://github.com/diagrams/diagrams-lib/tree/v1.4.6.1) (2024-02-26)++- Fix compilation on BSD ([#367](https://github.com/diagrams/diagrams-lib/pull/367), thanks to Finn Espen Gundersen)++## [v1.4.6-r4](https://github.com/diagrams/diagrams-lib/tree/v1.4.6-r4) (2024-01-11)++- Allow `base-4.19` and test with GHC 9.8+- Allow `text-2.1`++## [v1.4.6-r3](https://github.com/diagrams/diagrams-lib/tree/v1.4.6-r3) (2023-09-14)++- Allow `tasty-1.5`++## [v1.4.6-r2](https://github.com/diagrams/diagrams-lib/tree/v1.4.6-r2) (2023-08-16)++- Allow `bytestring-0.12` and `deepseq-1.5`++## [v1.4.6-r1](https://github.com/diagrams/diagrams-lib/tree/v1.4.6-r1) (2023-05-25)++- Allow `optparse-applicative-0.18`++## [v1.4.6](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.3) (2023-05-15)++- Allow `base-4.18`; test with GHC 9.6 (thanks to @sergv)+- Fix some warnings (thanks to @sergv)++## [v1.4.5.3](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.3) (2022-09-19)++- Update to `fsnotify-0.4`++- Revisions:+  - r1: require `lens-5.1`+  - r2: allow `mtl-2.3`+  - r3 (3 Dec 2022): allow `linear-1.22`++## [v1.4.5.2](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.2) (2022-09-14)++- Fix build with `transformers-0.6`+- Fix some deprecation warnings++## [v1.4.5.1-r3](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.1-r3) (2022-08-27)++- Allow `base-4.17` and `lens-5.2`; test with GHC 9.4++## [v1.4.5.1-r2](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.1-r2) (2022-02-01)++- Allow `optparse-applicative-0.17`++## [v1.4.5.1-r1](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.1-r1) (2022-01-08)++- Allow `text-2.0`++## [v1.4.5.1](https://github.com/diagrams/diagrams-lib/tree/v1.4.5.1) (2021-12-17)++- Bug fix: make things compile again under versions of `lens` before 5.1.++## [v1.4.5](https://github.com/diagrams/diagrams-lib/tree/v1.4.5) (2021-12-16)++- Allow `base-4.16` (tested with GHC 9.2.1)+- Allow `semigroups-0.20`, `lens-5.1`, `hashable-1.4`,+  `transformers-0.6`+- Add `Eq` instance for `SizeSpec`++## [v1.4.4-r1](https://github.com/diagrams/diagrams-lib/tree/v1.4.4-r1) (2021-06-05)++- Bumps to dependency upper bounds:+    - Allow `tasty-1.4`+    - Allow `bytestring-0.11`++## [v1.4.4](https://github.com/diagrams/diagrams-lib/tree/v1.4.4) (2021-05-24)++- Bumps to upper bounds, to allow building with:+    - `base-4.15` (tested with GHC 9.0.1)+    - `optparse-applicative` (tested with GHC 8.8.4 & 8.10.2)++- Updated use of Kinds thoughout the package++- Drop support for GHC 8.2 or earlier++## [v1.4.3](https://github.com/diagrams/diagrams-lib/tree/v1.4.3) (2019-11-06)++- Bumps to upper bounds, to allow building with:+    - `base-4.13` (tested with GHC 8.8.1)+    - `intervals-0.9`+    - `semigroups-0.19`+    - `hashable-1.3`+- Many bug fixes, including+    - [#313](https://github.com/diagrams/diagrams-lib/issues/313) (`combineBoundaries`)+    - [#322](https://github.com/diagrams/diagrams-lib/issues/322), [#329](https://github.com/diagrams/diagrams-lib/issues/329) (`section`)+    - [#325](https://github.com/diagrams/diagrams-lib/pull/325)+      (Bezier/Bezier intersection)+    - [#339](https://github.com/diagrams/diagrams-lib/pull/339) (`perspectiveZ1`)+- Added derived `Eq` and `Ord` instances for `FixedSegment`++## [v1.4.2.3](https://github.com/diagrams/diagrams-lib/tree/v1.4.2.3) (2018-06-11)++- Bug fix for `extrudeEnvelope` and friends ([#316](https://github.com/diagrams/diagrams-lib/issues/316))++## [v1.4.2.2](https://github.com/diagrams/diagrams-lib/tree/v1.4.2.2) (2018-05-08)++- Fixes for GHC < 8.0++## [v1.4.2.1](https://github.com/diagrams/diagrams-lib/tree/v1.4.2.1) (2018-04-13)++- Allow `base-4.11` (GHC 8.4)+- Allow `tasty-quickcheck-0.10`+- Bug fix for `Diagrams.TwoD.Offset.capArc` ([#310](https://github.com/diagrams/diagrams-lib/pull/310))++## [v1.4.2-r1](https://github.com/diagrams/diagrams-lib/tree/v1.4.2-r1) (2017-12-20)++Hackage revision to allow `tasty-1.0` in the test suite.++## [v1.4.2](https://github.com/diagrams/diagrams-lib/tree/v1.4.2) (2017-12-20)++- New functions:+    - `boxGrid`, for computing a grid of regularly spaced points.+    - `scalingRotationTo` and `scaleRotateTo`, for affine conformal 2D+      transformations.+- Documentation fixes:+    - `dirBetween`+    - `PolyOrientation`+- Upper bound updates: allow `tasty-0.12`, `tasty-quickcheck-0.9`,+  `tasty-hunit-0.10`, `optparse-applicative-0.14`+- Test with GHC 8.2.2++## [v1.4.1.2](https://github.com/diagrams/diagrams-lib/tree/v1.4.1.2) (2017-06-10)++- Fix test suite compilation failure [#299](https://github.com/diagrams/diagrams-lib/issues/299).++## [v1.4.1.1](https://github.com/diagrams/diagrams-lib/tree/v1.4.1.1) (2017-06-06)++- Fix `Diagrams.Points.centroid` to make it total.+- Fix bug in `Diagrams.Transform.Matrix.fromMatWithInv` (and hence+  also related functions which called it, such as `fromMat22` and+  `fromMat33`).++## [v1.4.1](https://github.com/diagrams/diagrams-lib/tree/v1.4.1) (2017-05-28)++- New functions `embeddedImage` and `loadImageEmbBS` for loading+  images.+- Fix [#289](https://github.com/diagrams/diagrams-lib/issues/289)+  which could have caused strange behavior in looped compilation mode+  on 32-bit platforms.+- Allow `intervals-0.8` and `directory-1.3`.+- Minor fixes to compile with GHC 8.2.++## [v1.4.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.4.0.1) (2016-11-07)++- Fix test suite compilation problem ([#286](https://github.com/diagrams/diagrams-lib/issues/286))++## [v1.4](https://github.com/diagrams/diagrams-lib/tree/v1.4) (2016-10-26)++* **New features**++    - New `mkText'` function, which allows making a text primitive+      without recommending a fill colour or font size so users can+      recommend their own (*e.g.* using the `recommendFontSize`+      function).++    - New functions `reflectXY` and `reflectionXY`++    - New `composeAligned` combinator for doing composition under an+      alignment while leaving the local origin unaffected.++    - Add `_LocLoop` and `_LocLine` prisms++    - New `bspline` function for creating uniform cubic B-splines++    - New 3D features:+        - New `Skinned` class+        - Improved handling of 3D primitives+        - CSG++    - New standard attributes for separate fill and stroke opacity+      (see+      [#248](https://github.com/diagrams/diagrams-lib/issues/248)).++    - New `HasQuery` class for things which can be spatially queried+      for values from some monoid.++    - New function `inquire` for testing whether a given point is+      inside a diagram.++    - New font weights: `bolder`, `lighter`, `thinWeight`,+      `ultraLight`, `light`, `mediumWeight`, `heavy`, `semiBold`,+      `ultraBold`.  Note that currently only the SVG backend deals+      with the new weights.++    - Export `GetSegmentCodomain` and update documentation++    - Improved performance of 2D rotations++* **New instances**++    - `Alignable` instance for `Located`++    - `ToPath` instances for lines and loops++    - `Serialize` instances for `Trail`, `Path`, `Located`, `SegTree`,+      `Segment`++    - `Generic` instances for `Path`, `Located`++    - `Action` instance for `Angle`: angles act by rotation.++* **API changes**++    - `snugBL`, `snugBR`, `snugTR` and `snugBR` are deprecated.+      These functions were unintuitive, ad-hoc, and not particularly useful,+      especially since e.g. `snugL` and `snugB` do not commute. You+      can use something like `snugB . snugL` directly, or use `snug`+      with a direction vector.  See+      [#250](https://github.com/diagrams/diagrams-lib/issues/250) for+      more details.++* **Dependency/version changes**++    - upgrade `fsnotify` and drop dependency on deprecated+      `system-filepath`+    - Allow `lens-4.15`+    - Many other bumped upper bounds, see release notes for minor releases+      below++* **Bug fixes**++    - fix `orientPoints` function, which was previously generating NaN+      values with lists of only one or two+      points. ([#210](https://github.com/diagrams/diagrams-lib/issues/210))++    - Broken offset joins with non-vertices in loops ([#263](https://github.com/diagrams/diagrams-lib/issues/263))++    - Properly transform arrow shaft styles ([#274](https://github.com/diagrams/diagrams-lib/issues/274))++    - Fix sign error in `reflectionAbout`++## [v1.3.1.4](https://github.com/diagrams/diagrams-lib/tree/v1.3.1.4) (2016-08-16) (16 August 2016)++- allow `optparse-applicative-0.13`++## [v1.3.1.3](https://github.com/diagrams/diagrams-lib/tree/v1.3.1.3) (2016-06-05)++- allow `base-4.9`+- allow `data-default-class-0.1`+- test with GHC 8.0.1++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.1.2...v1.3.1.3)++## [v1.3.1.2](https://github.com/diagrams/diagrams-lib/tree/v1.3.1.2) (2016-05-01)++- allow `lens-4.14`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.1.1...v1.3.1.2)++## [v1.3.1.2](https://github.com/diagrams/diagrams-lib/tree/v1.3.1.2) (2016-02-19)++  - allow `unordered-containers-0.2.*`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.1.0...v1.3.1.1)++## [v1.3.1.0](https://github.com/diagrams/diagrams-lib/tree/v1.3.1.0) (2016-02-14)++- improve path offset calculations++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.9...v1.3.1.0)++## [v1.3.0.9](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.9) (2016-01-14)++- allow `unordered-containers-0.2.6`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.8...v1.3.0.9)++## [v1.3.0.8](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.8) (2015-11-10)++- allow `semigroups-0.18`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.7...v1.3.0.8)++## [v1.3.0.7](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.7) (2015-10-08) ##++- Add `LambdaCase` extension to `.cabal` file, so `cabal` correctly+  reports that `diagrams-lib` does not build on `GHC-7.4`.++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.6...v1.3.0.7)++## [v1.3.0.6](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.6) (2015-09-29) ##++- Allow `optparse-applicative-0.12`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.5...v1.3.0.6)++## [v1.3.0.5](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.5) (2015-09-18) ##++- Fix compilation problem with `lens-4.13`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.4...v1.3.0.5)++## [v1.3.0.4](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.4) (2015-09-17) ##++**Dependency/version changes**++- Allow `lens-1.13`+- Allow `semigroups-0.17`+- Require `linear-1.20`++## [v1.3.0.3](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.3) (2015-08-10)++**Dependency/version changes**++- Drop dependency on deprecated `system-filepath` package+- Require `fsnotify-0.2.1`++## [v1.3.0.2](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.2) (2015-07-19)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3.0.1...v1.3.0.2)++## [v1.3.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.3.0.1) (2015-05-26)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.3...v1.3.0.1)++## [v1.3](https://github.com/diagrams/diagrams-lib/tree/v1.3) (2015-04-19)++**New features**++- Native image type that backends can specify.++- Affine maps between spaces for path-like objects. A new+  `Diagrams.ThreeD.Projections` has some helper functions for+  orthographic and perspective projections.++- Intersections for path-like objects using Bézier clipping.++- Helper functions in `Diagrams.Matrix` for converting between+  transforms and matrices.++- New `Diagrams` module that only exports functions defined in+  diagrams.++- New `Direction` type. `Direction` is a vector that's forgot it's+  magnitude. Some functions have changed their type from `R2` to+  `Direction V2 n` to make it clear that magnitude has no effect.++- Use the [`fsnotify`](https://hackage.haskell.org/package/fsnotify)+  package for command line looping. Command line looping now works+  on Windows.++- `groupOpacity` function added for lowering the opacity of a+  diagram as a whole.++- New `ToPath` class for converting path-like things to a `Path`.++**New instances**++- `Each` instances for `BoundingBox`, `Offset`, `Segment`,+  `FixedSegment` and `Path`.++- `Reversing` instances for `Offset`, `Segment`, `FixedSegment`,+  `Trail` and `Path`.++- `AsEmpty` instances for `BoundingBox`, `Trail` and `Path`.++- `Cons` and `Snoc` instances for `Path` and `Line`.++- New `Show` instances for `Angle`, `Segment`, `SomeColor`, `Trail'`+  and `at`.++- `Tangent` instance for `(FixedSegment v n)`.++- `Ord` instances for `LineMiterLimit`, `LineJoin` and `LineCap`.++**New helper functions**++- `_Line` and `_Loop` prisms.++- Style lenses: `_fontSize`, `_lineWidth`, `_fillTexture`,+  `_lineTexture`, `_opacity`, `_font`, `_lineCap`, `_lineJoin`+  `_dashing`.++- `_SomeColor` iso and `_AC` prism onto an `AlphaColour`.++- `atPoints` function to zip points with diagrams.++**API changes**++- `Diagram` type synonym now only takes a backend token: `Diagram B`++- Types that previously had a `v` variable now have `v` and `n`.++- `Control.Lens` and `Data.Default.Class` are now exported from from+  `Diagrams.Prelude`++- `Measure` has a new internal representation. `Local`, `Global`,+  `Normalized`, and `Output` have been renamed to `local`, `global`,+  `normalized` and `output` respectivly.++- `SizeSpec2D` has moved to `SizeSpec v n` in `Diagrams.SizeSpec`.+  `Dims, Height, Width and `Absolute` have moved to `dims2D`,+  `mkHeight`, `mkWidth` and `absolute` respectively.++- `Color` instances for `Colour` and `AlphaColour` are limited to+  `Double` for better type inference.++- `under` has been renamed to `underT`. New `transformed`,+  `translated`, `movedTo`, `movedFrom` and `rotated` isomorphisms to+  use with lens's `under` function.++- `stroke` is now polymorphic. Use `strokePath` or `strokeP` to get+  old `stroke` behaviour.++- `angleBetween` now works for any vector space, which means the+  angle is always positive. The old behaviour can be retrieved from+  `signedAngleBetween`++- `arc` now takes a starting `Direction` and a sweep `Angle`.+  `arcCW` and `arcCCW` take a start and finish `Direction`.++**Dependency/version changes**++- use `linear` instead of `vector-space`++**Closed issues:**++- Perspective deformation of square vertices yields extra point [\#244](https://github.com/diagrams/diagrams-lib/issues/244)++- Local fontsize renders inconsistentl on diffrent backends [\#243](https://github.com/diagrams/diagrams-lib/issues/243)++- Factor out Diagrams.Solve into a package? [\#235](https://github.com/diagrams/diagrams-lib/issues/235)++**Merged pull requests:**++- add pathPoints and pathVertices' functions [\#245](https://github.com/diagrams/diagrams-lib/pull/245) ([byorgey](https://github.com/byorgey))++- New loop [\#242](https://github.com/diagrams/diagrams-lib/pull/242) ([cchalmers](https://github.com/cchalmers))++- Pre 1.3 [\#241](https://github.com/diagrams/diagrams-lib/pull/241) ([cchalmers](https://github.com/cchalmers))++- Update CubicSpline.hs [\#240](https://github.com/diagrams/diagrams-lib/pull/240) ([fryguybob](https://github.com/fryguybob))++- updated changes for GHC-7.10 [\#239](https://github.com/diagrams/diagrams-lib/pull/239) ([bergey](https://github.com/bergey))++- split out new package diagrams-solve [\#237](https://github.com/diagrams/diagrams-lib/pull/237) ([byorgey](https://github.com/byorgey))++- Lens style [\#236](https://github.com/diagrams/diagrams-lib/pull/236) ([cchalmers](https://github.com/cchalmers))++- Half-dart arrowheads [\#234](https://github.com/diagrams/diagrams-lib/pull/234) ([byorgey](https://github.com/byorgey))++- TwoD.Points: Needs TypeFamilies [\#233](https://github.com/diagrams/diagrams-lib/pull/233) ([bgamari](https://github.com/bgamari))++- Projections [\#229](https://github.com/diagrams/diagrams-lib/pull/229) ([cchalmers](https://github.com/cchalmers))++## [1.2.0.9]() (2 April 2015)++ - allow `lens-4.9`++ - allow `vector-space-0.10`++## [v1.2.0.8](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.8) (2015-01-13)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.7...v1.2.0.8)++**Merged pull requests:**++- typo in haddocks [\#232](https://github.com/diagrams/diagrams-lib/pull/232) ([ggreif](https://github.com/ggreif))++- Update diagrams-lib.cabal : bumping JuicyPixels dependency [\#230](https://github.com/diagrams/diagrams-lib/pull/230) ([Twinside](https://github.com/Twinside))++## [v1.2.0.7](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.7) (2014-12-07)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.6...v1.2.0.7)++## [v1.2.0.6](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.6) (2014-12-04)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.5...v1.2.0.6)++**Merged pull requests:**++- Minor changes in documentation about Polygons. [\#228](https://github.com/diagrams/diagrams-lib/pull/228) ([alexDarcy](https://github.com/alexDarcy))++## [v1.2.0.5](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.5) (2014-11-17)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.4...v1.2.0.5)++**Implemented enhancements:**++- Path/trail intersection [\#209](https://github.com/diagrams/diagrams-lib/issues/209)++- Turn R2 into D2 \(Generalize R2 to any numeric type\) [\#50](https://github.com/diagrams/diagrams-lib/issues/50)++**Fixed bugs:**++- `Sectionable` instance for `SegTree` is not a linear reparameterization. [\#217](https://github.com/diagrams/diagrams-lib/issues/217)++**Closed issues:**++- bezierFromSweep very slow? [\#227](https://github.com/diagrams/diagrams-lib/issues/227)++- All lines render at same width [\#222](https://github.com/diagrams/diagrams-lib/issues/222)++- numerically stable cubic rootfinder [\#204](https://github.com/diagrams/diagrams-lib/issues/204)++**Merged pull requests:**++- Intersections [\#226](https://github.com/diagrams/diagrams-lib/pull/226) ([cchalmers](https://github.com/cchalmers))++- Linear update [\#225](https://github.com/diagrams/diagrams-lib/pull/225) ([cchalmers](https://github.com/cchalmers))++- Add arcCCW and friends.  Fix offset joins. [\#221](https://github.com/diagrams/diagrams-lib/pull/221) ([fryguybob](https://github.com/fryguybob))++- Reparameterize section [\#220](https://github.com/diagrams/diagrams-lib/pull/220) ([fryguybob](https://github.com/fryguybob))++- Diagram b v n to QDiagram b v n Any [\#219](https://github.com/diagrams/diagrams-lib/pull/219) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- New stuff [\#218](https://github.com/diagrams/diagrams-lib/pull/218) ([cchalmers](https://github.com/cchalmers))++- Linear [\#215](https://github.com/diagrams/diagrams-lib/pull/215) ([cchalmers](https://github.com/cchalmers))++## [v1.2.0.4](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.4) (2014-10-08)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.3...v1.2.0.4)++**Merged pull requests:**++- add semi-portable looping using fsnotify [\#213](https://github.com/diagrams/diagrams-lib/pull/213) ([bergey](https://github.com/bergey))++## [v1.2.0.3](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.3) (2014-09-07)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.2...v1.2.0.3)++**Closed issues:**++- arrowFromLocatedTrail function [\#205](https://github.com/diagrams/diagrams-lib/issues/205)++**Merged pull requests:**++- Added `arrowFromLocatedTrail` [\#206](https://github.com/diagrams/diagrams-lib/pull/206) ([pnutus](https://github.com/pnutus))++## [v1.2.0.2](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.2) (2014-08-22)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2.0.1...v1.2.0.2)++**Implemented enhancements:**++- Generalize Angle class? [\#38](https://github.com/diagrams/diagrams-lib/issues/38)++**Fixed bugs:**++- --selection and --src both use -s abbreviation [\#172](https://github.com/diagrams/diagrams-lib/issues/172)++**Closed issues:**++- Spike arrowhead and tail render with oversized joint [\#203](https://github.com/diagrams/diagrams-lib/issues/203)++**Merged pull requests:**++- Enable compilation with GHC HEAD \(v7.9\) [\#211](https://github.com/diagrams/diagrams-lib/pull/211) ([ggreif](https://github.com/ggreif))++- cli-options [\#200](https://github.com/diagrams/diagrams-lib/pull/200) ([bergey](https://github.com/bergey))++- Add Native images to Diagrams.TwoD.Image [\#199](https://github.com/diagrams/diagrams-lib/pull/199) ([taruti](https://github.com/taruti))++- add atPoints, deprecate decorateFoo [\#198](https://github.com/diagrams/diagrams-lib/pull/198) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Add bg' [\#197](https://github.com/diagrams/diagrams-lib/pull/197) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- add convenience synonyms \[hv\]sep for \[hv\]cat' \(with & sep .~ x\) [\#196](https://github.com/diagrams/diagrams-lib/pull/196) ([byorgey](https://github.com/byorgey))++- Vertices [\#192](https://github.com/diagrams/diagrams-lib/pull/192) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Direction type [\#186](https://github.com/diagrams/diagrams-lib/pull/186) ([bergey](https://github.com/bergey))++## [v1.2.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.2.0.1) (2014-06-04)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.2...v1.2.0.1)++**Closed issues:**++- Local headLength / headGaps act like Output [\#193](https://github.com/diagrams/diagrams-lib/issues/193)++**Merged pull requests:**++- transform Local Measure in arrow styles [\#194](https://github.com/diagrams/diagrams-lib/pull/194) ([bergey](https://github.com/bergey))++## [v1.2](https://github.com/diagrams/diagrams-lib/tree/v1.2) (2014-06-02)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.1.0.4...v1.2)++**New features**++- Several attributes (such as line width, dashing size, arrowhead+  size, and font size) that formerly had a value of type `Double`+  now have the more general type `Measure R2`. This allows the+  attributes to be specified relative to one of four measurement+  frames: `Local`, `Global`, `Normalized`, and `Output`.++- New DSL for specifying measurements.++- New synonyms for specifying measurements, *e.g.* `thin`,+  `thick`, and `medium`, `large`.++- Support for radial and linear gradients for fills and strokes.++- New `DImage` type that supports both embedded and external images in+  Backends that support them.++- New `lengths` Traversal for setting `headLength` and `tailLength`+  simultaneously.++- `Frustrum` and `Box` shapes added to `Diagrams.ThreeD.Shapes`.++- New function `quartForm` to find roots of quartic polynomials.++- New Lenses for polar coordinates.++- New trig functions, `sinA`, `atanA`, etc. which take `Angle` as+  input or output.++**New instances**++- `Transformable` instances for `LineWidth`, `Dashing`,+  `LineTexture`, and  `FillTexture`.++**API changes**++- `FillColor` and `LineColor` attributes have been replaced with+  the more general `FillTexture` and `LineTexture`. Use the `solid`+  function to convert a color to a texture.++- The size of arrow heads and tails is now specified in terms of+  length instead of the radius of their circumcircle.++- Gaps at the ends of arrows are now specified using `Measure R2`.++- The `gap` traversal has been replaced by `gaps` for consistency+  in naming, though `gap` is still provided for backwards compatibility.++- `fontSize` now takes a value of type `Measure R2`.++- Get rid of (bitrotted) `Show` backend.++- Functions in `TwoD.Adjust` now return the adjustment+  transformation itself in addition to the resized `Diagram` and+  `Options` record; this can be used, *e.g.* to convert screen+  coordinates back into diagram coordinates.++- Export `pathLocSegments`.++- The `avgScale` function has been moved to `Diagrams.Core`.++- The `Angle` definition and related functions (*e.g.*+  `angleBetween`) have moved to a separate module, `Diagrams.Angle`.++- A separate `Diagrams.TwoD.Attributes` module now contains most+  of the attributes that require 2D transformation instances.++- The `splitColorFills` function has been replaced by `splitTextureFills`.++**Dependency/version changes**++- Allow `semigroups-0.15`++- Allow `opt-parse-applicative-0.9.0++- Allow `lens-4.2`++**Implemented enhancements:**++- 'image' should be in IO [\#29](https://github.com/diagrams/diagrams-lib/issues/29)++- Add gradient support [\#9](https://github.com/diagrams/diagrams-lib/issues/9)++**Closed issues:**++- fails to build against HP [\#190](https://github.com/diagrams/diagrams-lib/issues/190)++- text does not scale [\#179](https://github.com/diagrams/diagrams-lib/issues/179)++- Please add support for latest version of intervals library [\#170](https://github.com/diagrams/diagrams-lib/issues/170)++- presence of arrowHead can tilt connection sideways [\#162](https://github.com/diagrams/diagrams-lib/issues/162)++**Merged pull requests:**++- Quartic formula, no obvious bugs [\#187](https://github.com/diagrams/diagrams-lib/pull/187) ([Mathnerd314](https://github.com/Mathnerd314))++- Arrow length [\#185](https://github.com/diagrams/diagrams-lib/pull/185) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- fix handling of text scaling [\#182](https://github.com/diagrams/diagrams-lib/pull/182) ([byorgey](https://github.com/byorgey))++- Texture [\#181](https://github.com/diagrams/diagrams-lib/pull/181) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- arrow envelopes [\#180](https://github.com/diagrams/diagrams-lib/pull/180) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- make headSize and tailSize back into ArrowOpts fields [\#177](https://github.com/diagrams/diagrams-lib/pull/177) ([byorgey](https://github.com/byorgey))++- updated the upper bounds of the .cabal constraints for 'semigroups' package [\#176](https://github.com/diagrams/diagrams-lib/pull/176) ([zgredzik](https://github.com/zgredzik))++- Image2 [\#174](https://github.com/diagrams/diagrams-lib/pull/174) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Image [\#173](https://github.com/diagrams/diagrams-lib/pull/173) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Preliminary implementation of Measure [\#159](https://github.com/diagrams/diagrams-lib/pull/159) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Gradient [\#136](https://github.com/diagrams/diagrams-lib/pull/136) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++## [v1.1.0.7]() (2014-05-15)++- Allow `semigroups-0.14`++## [v1.1.0.6]() (2014-04-10)++- Allow `semigroups-0.13`++## [v1.1.0.4](https://github.com/diagrams/diagrams-lib/tree/v1.1.0.4) (2014-04-04)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.1.0.2...v1.1.0.4)++**Merged pull requests:**++- Updates to work with `Backend` redesign [\#171](https://github.com/diagrams/diagrams-lib/pull/171) ([byorgey](https://github.com/byorgey))++- Rework of units [\#169](https://github.com/diagrams/diagrams-lib/pull/169) ([byorgey](https://github.com/byorgey))++- Make Diagrams.Transform.under more polymorphic [\#168](https://github.com/diagrams/diagrams-lib/pull/168) ([FlorentBecker](https://github.com/FlorentBecker))++## [v1.1.0.3]() (2014-03-19)++- Allow `lens-4.1`++## [v1.1.0.2](https://github.com/diagrams/diagrams-lib/tree/v1.1.0.2) (2014-03-19)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.1.0.1...v1.1.0.2)++**Merged pull requests:**++- move avgScale to core [\#167](https://github.com/diagrams/diagrams-lib/pull/167) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- 3d color & lighting, more solids, Angle utilities [\#166](https://github.com/diagrams/diagrams-lib/pull/166) ([bergey](https://github.com/bergey))++- Added bothSize function, lineHead and lineTail [\#165](https://github.com/diagrams/diagrams-lib/pull/165) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++## [v1.1.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.1.0.1) (2014-03-09)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.1...v1.1.0.1)++- Depend on `hashable` package, and add `Hashable` instance for+  `SizeSpec2D`.++  Technically, the PVP specifies that adding a new instance+  requires a major version bump.  However, I highly doubt anyone+  was making their own orphan `Hashable` instances before.  Feel+  free to yell at Brent if this breaks your build.++## [v1.1](https://github.com/diagrams/diagrams-lib/tree/v1.1) (2014-03-09)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.0.1...v1.1)++**New features**++- Support for `Deformation`s, arbitrary (non-affine)+  transformations on objects such as points, paths, and located+  trails (though not on diagrams).++- New functions `clipTo`, which clips a diagram's envelope and+  trace along with its visual representation, and `clipped`, which+  clips the diagram's visual representation but replaces its+  envelope and trace with those of the clipping path.++- New `arrowV` function, for creating an arrow with the direction+  and magnitude of a given vector.++- `gap` traversal, for setting the head and tail gaps of an arrow+  simultaneously.++- Generalized types for `centerXY` and `snugXY`, based on new+  `basis` function from `diagrams-core++- New 3D `Transform`s, alignment, and 3D-specific `Prelude`.++- New `frame` function similar to `pad`, but increases the envelope+  of a diagram by an amount specified in local units in every direction+  irrespective of the local origin.++- New `splitFills` function for pushing fill attributes down to+  subtrees containing only loops (mostly of relevance only to+  backend implementors).++**New instances**++- `Typeable` instances for all data types that are used as diagram+  primitives.++- `Sectionable` instance for `FixedSegment`.++**API changes**++- `Angle` is now a type, rather than a class.  It uses a single+  internal representation for angles, and lenses `turn`, `rad,`+  and `deg` are supplied for constructing (using `@@`) and viewing+  (using `^.`) `Angle`s in various units.  In addition, the `Num`+  instance for `Angle` has been removed, eliminating a class of+  errors where a bare number is interpreted in units other than+  what you expect.++- Removed `Num` instance for angles.++**Dependency/version changes**++- Require `lens >= 4.0`.++- Allow `array-0.5`.++- Allow `hashable-1.1`.++- Remove `NumInstances` dependency.++**Bug fixes**++- Exclude joins in offsets on close segments (#160).++- Exclude extra segment when joining loops in offset (#155).++**Performance improvements**++- `colorToSRGBA` function now avoids expensive matrix operations,+  offering dramatic speedups in rendering diagrams with many color+  attributes.++**Implemented enhancements:**++- Better color model in 3D [\#121](https://github.com/diagrams/diagrams-lib/issues/121)++- Projective/perspective transforms for points and paths [\#108](https://github.com/diagrams/diagrams-lib/issues/108)++- clipTo function [\#35](https://github.com/diagrams/diagrams-lib/issues/35)++**Fixed bugs:**++- Offset Bug [\#155](https://github.com/diagrams/diagrams-lib/issues/155)++**Closed issues:**++- Expand of an expand [\#160](https://github.com/diagrams/diagrams-lib/issues/160)++**Merged pull requests:**++- fix doc for === and ||| [\#164](https://github.com/diagrams/diagrams-lib/pull/164) ([denys-duchier](https://github.com/denys-duchier))++- Split fills [\#161](https://github.com/diagrams/diagrams-lib/pull/161) ([byorgey](https://github.com/byorgey))++- Exclude extra segment when joining loops in offset. Fixes \#155. [\#158](https://github.com/diagrams/diagrams-lib/pull/158) ([fryguybob](https://github.com/fryguybob))++- added basis, generalized `centerXY` and `snugXY` [\#157](https://github.com/diagrams/diagrams-lib/pull/157) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- derive Typeable for all data types used in Prims [\#156](https://github.com/diagrams/diagrams-lib/pull/156) ([byorgey](https://github.com/byorgey))++- Lens4 [\#154](https://github.com/diagrams/diagrams-lib/pull/154) ([bergey](https://github.com/bergey))++- removed NumInstances dependency [\#153](https://github.com/diagrams/diagrams-lib/pull/153) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Remove Num instance for Angle [\#150](https://github.com/diagrams/diagrams-lib/pull/150) ([bergey](https://github.com/bergey))++- Change internal color representation to SRGBA [\#149](https://github.com/diagrams/diagrams-lib/pull/149) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Projections - non-affine transformations [\#148](https://github.com/diagrams/diagrams-lib/pull/148) ([bergey](https://github.com/bergey))++## [v1.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.0.1) (2014-01-26)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.0.0.1...v1.0.1)++**Implemented enhancements:**++- Module for generating nice colors/color schemes [\#77](https://github.com/diagrams/diagrams-lib/issues/77)++**Closed issues:**++- "Maybe.fromJust: Nothing" error when connecting translated small diagram [\#147](https://github.com/diagrams/diagrams-lib/issues/147)++**Merged pull requests:**++- Add Hashable instance for SizeSpec2D [\#146](https://github.com/diagrams/diagrams-lib/pull/146) ([byorgey](https://github.com/byorgey))++- return list of traces [\#145](https://github.com/diagrams/diagrams-lib/pull/145) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- added clipTo [\#144](https://github.com/diagrams/diagrams-lib/pull/144) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- Unified angle type [\#140](https://github.com/diagrams/diagrams-lib/pull/140) ([bergey](https://github.com/bergey))++## [v1.0.0.1](https://github.com/diagrams/diagrams-lib/tree/v1.0.0.1) (2013-11-28)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v1.0...v1.0.0.1)++## [v1.0](https://github.com/diagrams/diagrams-lib/tree/v1.0) (2013-11-25)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.7.1.1...v1.0)++**New features**++- New modules `Diagrams.TwoD.Arrow` and `Diagrams.TwoD.Arrowheads`+  for creating arrows.++- New module `Diagrams.Backend.CmdLine`, providing a flexible+  framework for creating command-line-driven diagram rendering executables.++- New functions in `Diagrams.Offset`: `offsetTrail` and+  `offsetPath` for one-sided offsets of trails and paths;+  `expandTrail` and `expandPath` for "stroking" trails and paths,+  computing a path whose fill corresponds to the stroke of the+  given trail or path.++- New module `Diagrams.Tangent` for computing tangent and normal+  vectors of segments, trails, and paths.++- New functions in `Diagrams.Align` to allow diagrams to be aligned by `Trace`+  called `snug`, `snugBy` and `snugCenter`+  and the ability to define other boundary functions for alignment. Functions+  `snugL`, `snugR`, etc. are included in `TwoD.Align`.++- Lenses from `Control.Lens` are now used consistently for record fields+  throughout the library.++- New function `angleRatio` for calculating the ratio between two angles.++- Restricted identity functions `asTurn`, `asRad`, and `asDeg` for+  resolving type ambiguity++- New miter limit attribute.++- New function `annularWedge` in `TwoD.Arc`++- New `avgScale` utility in `TwoD.Transform`, for backends which+  cannot fully implement freezing of line width++- New function `heptagon`, a vast improvement over the linguistic+  frankenstein `septagon`.++- New function `lookupName` (re-exported from `diagrams-core`) for+  simple lookups of named subdiagrams++- New function `angleBetween` to calculate the angle between two+  vectors.++- New function `arcBetween` to draw an arc between two given+  points.++- A bunch of new modules containing types, primitives and+  utilities for constructing 3D diagrams: `Diagrams.ThreeD.Align`,+  `.Camera`, `.Light`, `.Shapes`, `.Transform`, `.Types`, and+  `.Vector`.  This is still a "feature preview" (in particular,+  appropriate 3D backends are still under construction).++**New instances**++- `AdditiveGroup` and `VectorSpace` instances for `Turn`, `Rad`, `Deg`++- `Alignable` instance for `(->) e`++- `Default` instances for `FillRule`, `FillRuleA`, `LineJoin`,+    `LineCap`, `FillColor`++- `Show` instances for `FillRule`, `FillRuleA`++**API changes**++- `e` no longer exported from `Diagrams.Prelude`.++- `Diagrams.BoundingBox` is no longer exported from `Diagrams.Prelude`.++- Re-export `Diagrams.Core.pointDiagram` from `Diagrams.Prelude`.++- Added `fromAlphaColour` method to `Color` class.++- `&` renamed to `^&`++- Stop re-exporting `tan`, `over`, and `both` from `Data.Colour`.++- New coordinate lenses `_x`, `_y`, and `_z` for `R2`, `P2`, `R3`, `P3`++- Export `fullTurn` from `Diagrams.Prelude`.++- `Codomain (Located a)` is now `Point (Codomain a)` instead of+  `Located (Codomain a)`.++- Export `domainBounds` from `Diagrams.Parametric`.++- Adjusting functionality moved from `Diagrams.Parametric` to its+  own module, `Diagrams.Parametric.Adjust`.++- Rename `strokeT` (and primed variant) to `strokeTrail`; rename+  `strokeLocT` to `strokeLocTrail`.++- `ScaleInv` is now in its own module, `Diagrams.TwoD.Transform.ScaleInv`.++- Re-export `Image` type (but not constructor) from `Diagrams.TwoD`++- Removed `Floating` and `RealFloat` instances for `Turn` and `Deg`++- `offsetSegment` now returns a `Located` instead of a tuple.++- Removed `Num` and `Fractional` instances for `R2`.++**Dependency/version changes**++- Remove `newtype` dependency++- New dependencies on `lens`, `tagged`, `optparse-applicative`,+  `filepath`, `safe`, `vector-space-points`, `MemoTrie`++- Depend on `intervals >= 0.3 && < 0.5`.++**Bug fixes**++- Depend on `intervals 0.3`, which allows diagrams to build on+  Windows, by evading a GHCi linker bug which affects the FFI use in+  previous versions of intervals ([diagrams-contrib#14](https://github.com/diagrams/diagrams-contrib/issues/14))++- Use point envelope at the origin for text objects instead of an+  empty envelope+  ([#115](https://github.com/diagrams/diagrams-lib/issues/115),+  [#116](https://github.com/diagrams/diagrams-lib/issues/116)).++- Adjusting the end of a trail now works correctly ([#95](https://github.com/diagrams/diagrams-lib/issues/95)).++- Only look for miter join on corners in `Diagrams.TwoD.Offset` ([#118](https://github.com/diagrams/diagrams-lib/issues/118)).++- `wedge` from `Diagrams.TwoD.Arc` is now a Loop ([#99](https://github.com/diagrams/diagrams-lib/issues/99))++- Arrows do not behave correctly under scaling [\#112](https://github.com/diagrams/diagrams-lib/issues/112)++**Performance improvements**++- `R2` is now strict and `UNPACK`ed++- Add strictness to `Offset`, `Segment`, `OffsetEnvelope`, and `SizeSpec2D`.++- Make `getEnvelope` calculation for `Segment` more efficient by+  floating divisions out of the inner calculation.++- Use a specialized `HasTrie` instance for `R2`.+++**Closed issues:**++- diagrams-lib-0.7.1.1 fails to build with ghc-7.7 \(-HEAD\) [\#128](https://github.com/diagrams/diagrams-lib/issues/128)++- Arrow misses target [\#126](https://github.com/diagrams/diagrams-lib/issues/126)++**Merged pull requests:**++- Lenses for setting arrow head, tail, and shaft colors [\#138](https://github.com/diagrams/diagrams-lib/pull/138) ([byorgey](https://github.com/byorgey))++- Delayed subtrees [\#137](https://github.com/diagrams/diagrams-lib/pull/137) ([byorgey](https://github.com/byorgey))++- Add helpers for common optparse-applicative backend command line. [\#135](https://github.com/diagrams/diagrams-lib/pull/135) ([fryguybob](https://github.com/fryguybob))++- add located lens for access into Located things [\#134](https://github.com/diagrams/diagrams-lib/pull/134) ([byorgey](https://github.com/byorgey))++- add b-\>a instance for Alignable [\#133](https://github.com/diagrams/diagrams-lib/pull/133) ([cscheid](https://github.com/cscheid))++- Strictness optimizations [\#132](https://github.com/diagrams/diagrams-lib/pull/132) ([JohnLato](https://github.com/JohnLato))++- Lens [\#131](https://github.com/diagrams/diagrams-lib/pull/131) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- 3D scaling, alignment, coordinate lenses [\#129](https://github.com/diagrams/diagrams-lib/pull/129) ([bergey](https://github.com/bergey))++- correctly \(?\) compute shaftScale by solving a quadratic [\#127](https://github.com/diagrams/diagrams-lib/pull/127) ([byorgey](https://github.com/byorgey))++- calculate arrow shaftScale using projection of ends onto shaft offset [\#125](https://github.com/diagrams/diagrams-lib/pull/125) ([bergey](https://github.com/bergey))++- Convert from newtype to lens [\#124](https://github.com/diagrams/diagrams-lib/pull/124) ([byorgey](https://github.com/byorgey))++- Version bump on lens [\#123](https://github.com/diagrams/diagrams-lib/pull/123) ([haasn](https://github.com/haasn))++## [v0.7.1.1](https://github.com/diagrams/diagrams-lib/tree/v0.7.1.1) (2013-09-27)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.7.1...v0.7.1.1)++**Implemented enhancements:**++- Use a point envelope for built-in text objects [\#116](https://github.com/diagrams/diagrams-lib/issues/116)++- implement rotations in 3D [\#86](https://github.com/diagrams/diagrams-lib/issues/86)++- Control over boundary conditions on cubic splines. [\#32](https://github.com/diagrams/diagrams-lib/issues/32)++**Fixed bugs:**++- diagrams with empty envelopes are not properly separated by struts [\#115](https://github.com/diagrams/diagrams-lib/issues/115)++**Closed issues:**++- calculate ratio between two angles [\#109](https://github.com/diagrams/diagrams-lib/issues/109)++- Add primitives for arrows [\#73](https://github.com/diagrams/diagrams-lib/issues/73)++**Merged pull requests:**++- Three d render [\#114](https://github.com/diagrams/diagrams-lib/pull/114) ([bergey](https://github.com/bergey))++- API for computing tangent and normal vectors to segments and trails [\#113](https://github.com/diagrams/diagrams-lib/pull/113) ([byorgey](https://github.com/byorgey))++- change default styles for arrows [\#111](https://github.com/diagrams/diagrams-lib/pull/111) ([byorgey](https://github.com/byorgey))++- 3D utility functions [\#107](https://github.com/diagrams/diagrams-lib/pull/107) ([bergey](https://github.com/bergey))++## [v0.7.1](https://github.com/diagrams/diagrams-lib/tree/v0.7.1) (2013-09-11)++**New features**++- New standard miter limit attribute++- New functions `lineColorA`, `lineWidthA`, `lineMiterLimitA`,+  `fontSizeA` for directly applying attribute values++- `setDefault2DAttributes` now sets default line cap (butt), line+  join (miter), and miter limit (10) attributes++**New instances**++- `Data.Default` instances for++- `LineCap`++- `LineJoin`++- `LineMiterLimit`++- `LineWidth`++- `LineColor`++- `FontSize`++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.7...v0.7.1)++**Implemented enhancements:**++- Path expansion. [\#55](https://github.com/diagrams/diagrams-lib/issues/55)++- way to move diagrams closer together [\#18](https://github.com/diagrams/diagrams-lib/issues/18)++**Fixed bugs:**++- wedge should be closed [\#99](https://github.com/diagrams/diagrams-lib/issues/99)++- Adjusting trail from the end has no effect [\#95](https://github.com/diagrams/diagrams-lib/issues/95)++**Merged pull requests:**++- Add offsetTrail and expandTrail. [\#103](https://github.com/diagrams/diagrams-lib/pull/103) ([fryguybob](https://github.com/fryguybob))++- change Codomain of Located to Point \(Codomain a\) [\#102](https://github.com/diagrams/diagrams-lib/pull/102) ([byorgey](https://github.com/byorgey))++- Default and Show instances for FillRule and FillRuleA [\#100](https://github.com/diagrams/diagrams-lib/pull/100) ([jbracker](https://github.com/jbracker))++- Changes required due to the introduction of Roles in GHC [\#98](https://github.com/diagrams/diagrams-lib/pull/98) ([co-dan](https://github.com/co-dan))++- removed default implementation of reverseDomain [\#97](https://github.com/diagrams/diagrams-lib/pull/97) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++- fixed issue \#95 [\#96](https://github.com/diagrams/diagrams-lib/pull/96) ([jeffreyrosenbluth](https://github.com/jeffreyrosenbluth))++## [v0.7](https://github.com/diagrams/diagrams-lib/tree/v0.7) (2013-08-09)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.6.0.3...v0.7)++**New features**++- New module `Diagrams.TwoD.Curvature`, for computing the+  curvature of 2D segments at any given point.++- New module `Diagrams.Offset`, containing an `offsetSegment`+  function that builds a trail a fixed distance from the original+  segment.  This is a precursor to planned functions `offsetTrail`+  and `offsetPath`.++- New function `Diagrams.TwoD.Transform.onBasis`, for extracting a+  matrix representation of a 2D transformation++- New functions `extrudeEnvelope` and `intrudeEnvelope`, for+  extending or shrinking an envelope only in a certain direction.++- Generalize the `Color` class to absolute colors.+  This addresses concerns raised in issue #66 by letting the backend+  choose which color space to render `Color` instances to. Functions are+  provided for backwards compatibility with the old semantics.++- New function `scaleInvPrim` for creating a diagram from a single+  scale-invariant primitive.++- New module `Diagrams.Parametric`, containing a collection of+  classes abstracting over "parametric" things: `Parametric`,+  `DomainBounds`, `EndValues`, `Sectionable`, and `HasArcLength`,+  with instances for segments, trails, and related things.++- A big refactoring of segments and trails:++  - Segments can now be either "closed" or "open".++  - There are now two types of trails: "lines" (which travel+    from point A to point B) or "loops" (closed curves which end+      where they started). `Trail` is now a wrapper type which can+      contain both loops and lines.++  - There is a new `Located` wrapper type for adding locations to+    translation-invariant things.  `Path`s now consist of a+    collection of `Located Trail`s.++  - The `PathLike` class is now renamed to `TrailLike`; the+    `trailLike` function takes a `Located Trail` as input.++- New convenience functions `boundaryFrom` and `boundaryFromMay`,+  for computing boundaries of subdiagrams.++- Re-export from `diagrams-lib` a lot of things defined in+  `diagrams-core`, to make them easier for users to find.  Several+  new modules have been created as a result: `Diagrams.Query`,+  `Diagrams.Envelope`, `Diagrams.Trace`, and `Diagrams.Names`.++- Export the `centroid` function from `Diagrams.Prelude`.++- `triangle` is now a synonym for `eqTriangle`.++**New instances**++- `IsPrim` instances for `Path`, `Ellipsoid`, `Image`, `Text`, and+  `ScaleInv`++- `Eq`, `Ord`, and `Show` instances for `SizeSpec2D`++**API changes**++- `CircleFrac` has been renamed `Turn` (though `CircleFrac` is+  retained as a deprecated synonym).++- `Diagrams.Coordinates` is no longer exported from+  `Diagrams.Prelude`.  This is for compatibility with `lens`, as `(&)`+  is a rather important lens operator and clashes with+  `Diagrams.Coordinates`.  Users who want the `Coordinates` stuff can import+  `Diagrams.Coordinates` explicitly.++**Dependency/version changes**++- allow `base-4.7`++- upgrade to `monoid-extras-0.3`++- depend on `data-default-class` instead of `data-default`++- Tested with GHC 7.7.++**Bug fixes**++- Added a special case that was a not handled properly by the+  quadratic solver, resulting in bogus envelopes in certain cases+  (#88).++- Import only `Data.NumInstances.Tuple` instead of+  `Data.NumInstances`. Previously, `Diagrams.Prelude` exported+  `Eq`, `Show`, and `Num` instances for functions and tuples; now+  it only exports tuple instances. Users wishing to use+  `Diagrams.CubicSpline` with a vector space built over functions (!?)+  can import `Data.NumInstances.Function` themselves. (#48)++- Do scaling on a `Path` *before* constructing a `TrailLike` in+      `rect` (#43)++**Implemented enhancements:**++- Split PathLike, Trail, and Path into separate modules [\#25](https://github.com/diagrams/diagrams-lib/issues/25)++- Add support for path-oriented diagrams. [\#13](https://github.com/diagrams/diagrams-lib/issues/13)++**Fixed bugs:**++- Wrong envelope for cubic segment [\#88](https://github.com/diagrams/diagrams-lib/issues/88)++- Fix documentation of beside, \(===\), and \(|||\) re: monoidal semantics [\#83](https://github.com/diagrams/diagrams-lib/issues/83)++- reverseTrail should not generate extra segments for closed trails [\#24](https://github.com/diagrams/diagrams-lib/issues/24)++**Closed issues:**++- Test issue [\#91](https://github.com/diagrams/diagrams-lib/issues/91)++**Merged pull requests:**++- Add parametric generalizations for segments, trails, Located, etc. [\#92](https://github.com/diagrams/diagrams-lib/pull/92) ([byorgey](https://github.com/byorgey))++- Adding type signature so it compiles with GHC7.7 [\#90](https://github.com/diagrams/diagrams-lib/pull/90) ([co-dan](https://github.com/co-dan))++- bug fix: add special case for b==0 to quadform solver [\#89](https://github.com/diagrams/diagrams-lib/pull/89) ([byorgey](https://github.com/byorgey))++- Big refactoring of segments and trails. [\#87](https://github.com/diagrams/diagrams-lib/pull/87) ([byorgey](https://github.com/byorgey))++- Stop exporting Diagrams.Coordinates from Diagrams.Prelude [\#82](https://github.com/diagrams/diagrams-lib/pull/82) ([byorgey](https://github.com/byorgey))++- Generalization of R2 to D2 a [\#65](https://github.com/diagrams/diagrams-lib/pull/65) ([jbracker](https://github.com/jbracker))++## [v0.6.0.3](https://github.com/diagrams/diagrams-lib/tree/v0.6.0.3) (2013-05-04)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.6.0.2...v0.6.0.3)++**Closed issues:**++- Offset [\#80](https://github.com/diagrams/diagrams-lib/issues/80)++**Merged pull requests:**++- Added offsetSegment function [\#79](https://github.com/diagrams/diagrams-lib/pull/79) ([fryguybob](https://github.com/fryguybob))++- Working on adding curvature. [\#74](https://github.com/diagrams/diagrams-lib/pull/74) ([fryguybob](https://github.com/fryguybob))++- New `scaleInvPrim` function [\#71](https://github.com/diagrams/diagrams-lib/pull/71) ([byorgey](https://github.com/byorgey))++## [v0.6.0.2](https://github.com/diagrams/diagrams-lib/tree/v0.6.0.2) (2013-03-29)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/types-generalized...v0.6.0.2)++**Implemented enhancements:**++- Don't export Eq and Show instances for functions from Diagrams.Prelude [\#48](https://github.com/diagrams/diagrams-lib/issues/48)++**Closed issues:**++- radius is wrong [\#75](https://github.com/diagrams/diagrams-lib/issues/75)++**Merged pull requests:**++- Matrix basis rep [\#78](https://github.com/diagrams/diagrams-lib/pull/78) ([bergey](https://github.com/bergey))++## [types-generalized](https://github.com/diagrams/diagrams-lib/tree/types-generalized) (2013-02-13)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.6.0.1...types-generalized)++## [v0.6.0.1](https://github.com/diagrams/diagrams-lib/tree/v0.6.0.1) (2013-01-07)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/v0.6...v0.6.0.1)++**Fixed bugs:**++- text on zero width rect leads to nans [\#51](https://github.com/diagrams/diagrams-lib/issues/51)++- wrong result when using `beside` with circle and vector \(1 & \(-1\)\) [\#46](https://github.com/diagrams/diagrams-lib/issues/46)++- Handle `rect` with zero arguments. [\#43](https://github.com/diagrams/diagrams-lib/issues/43)++**Closed issues:**++- Generalization of color space used in rendering [\#66](https://github.com/diagrams/diagrams-lib/issues/66)++**Merged pull requests:**++- do scaling on a Path before constructing PathLike in 'rect' \(fixes \#43\) [\#70](https://github.com/diagrams/diagrams-lib/pull/70) ([byorgey](https://github.com/byorgey))++## [v0.6](https://github.com/diagrams/diagrams-lib/tree/v0.6) (2012-12-12)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/0_5_0_1...v0.6)++**New features**++- `boundingRect` function for constructing a bounding rectangle++- `bg` function for "setting the background color" (*i.e.* placing+  atop a colored bounding rectangle)++- New functions `setDefault2DAttributes` and `adjustDiaSize2D`.+  `adjustDia2D` does both --- so the behavior of `adjustDia2D` has+  not changed, but it is now possible to apply just one of the two+  adjustments using the new functions.++- `Diagrams.TwoD.Transform` now exports a `ScaleInv` type for creating+  scale-invariant objects, which are only affected by rotational+  and translational components of transformations.++- The new `Diagrams.Coordinates` module provides nicer syntax for+  constructing and pattern-matching point and vector literals.++- New `fromFixedSeg` function in `Diagrams.Segment`, which+  decomposes a `FixedSegment` into a starting point and a `Segment`.++- New `withTrace` function for setting the `Trace` of a diagram.++- Three new size-related functions:++    - New `sized` function for scaling an object to a particular size.+      One particularly nice use of this is to obviate the need to keep+      fiddling with the line width to get diagrams to "look right";+      just set the line width relative to some arbitrary scale+      (*e.g.* assuming the final diagram will fit into a 1x1 box) and+      then apply `sized` to the final diagram to make it that given+      arbitrary size.  It can also be used for easily making something+      (a diagram, path, trail, ...) the same size as something else,+      with the help of the new `sizeSpec2D` function.++    - New `sizedAs` function, for setting the size of some object to+      be "the same as" some other object.++    - New `sizeSpec2D` function for conveniently calculating the size+      of an object as a `SizeSpec2D` value (for use with the new `sized`+      funtion).++- New `extrudeEnvelope` and `intrudeEnvelope` functions for+  modifying envelopes in a single direction only, as well as new+  functions `extrude{Left,Right,Bottom,Top}` specializing+  `extrudeEnvelope` to 2D.++- `arcCW` draws clockwise arcs; `arc'` draws arcs counterclockwise+  or clockwise as the radius is positive or negative,+  respectively.++- fill color attribute is generalized to support "recommended" and+  "committed" colors; text objects use a recommended fill color of+  black.++**New instances**++- The `Show` instance for `R2` now produces something like `2 & 6`+  instead of `R2 { unR2 = (2,6) }`.  The `Read` instance has also+  been changed to match, so `read . show = id`.++- `Enveloped` instance for `FixedSegment`++- `Traced` instances for `Segment`, `FixedSegment`, `Trail`, and `Path`++- New derived `Eq` instances for `LineCapA`, `LineJoinA`, `Dashing`,+  `DashingA`, `FillRule`, `Font`, `FontSize`, `FontSlant`, `FontSlantA`,+  `FontWeight`, and `FontWeightA`++- `Renderable Ellipsoid NullBackend` instance++**API changes**++- `Data.Colour` (minus `atop` and `AffineSpace`) is now re-exported+  from Diagrams.Prelude for convenience.++- The `beneath` function is now infixl 6.++- The `BoundingBox` module has had a complete overhaul.  There is+  now a special empty bounding box, and bounding boxes are an+  instance of `Monoid`.++- The type of `withEnvelope` has been slightly generalized.++- `Diagrams.TwoD.Adjust.adjustSize` is now deprecated; it has been+  renamed and moved to `Diagrams.TwoD.Size.requiredScaleT`.++- `expandPath` has been renamed to `scalePath`.++**Dependency/version changes**++- Allow `data-default` 0.4 and 0.5++- Allow `base`-4.6++- Allow `containers`-0.5++**Bug fixes**++- `arc` and `arcT` functions now always produce counterclockwise arcs,+  as claimed.++**Implemented enhancements:**++- Enhancements for `arc`s. [\#54](https://github.com/diagrams/diagrams-lib/issues/54)++- align and friends should be in terms of a new 'Alignable' class instead of 'Boundable' [\#31](https://github.com/diagrams/diagrams-lib/issues/31)++- Tools for more accurate boundary calculations in common cases [\#30](https://github.com/diagrams/diagrams-lib/issues/30)++- Reimplement Diagrams.TwoD.Ellipse in terms of Diagrams.TwoD.Arc [\#27](https://github.com/diagrams/diagrams-lib/issues/27)++- Function to convert angles into 2D unit vectors [\#23](https://github.com/diagrams/diagrams-lib/issues/23)++- 2D specialization of 'beside' which takes an angle instead of a vector [\#22](https://github.com/diagrams/diagrams-lib/issues/22)++- Generalize showOrigin function [\#21](https://github.com/diagrams/diagrams-lib/issues/21)++- Add generic 'extent' and 'breadth' ? functions [\#20](https://github.com/diagrams/diagrams-lib/issues/20)++- Add shearing transformations to standard library [\#19](https://github.com/diagrams/diagrams-lib/issues/19)++- Add support for text [\#15](https://github.com/diagrams/diagrams-lib/issues/15)++- Allow the user to choose the fill rule used for paths when stroking [\#14](https://github.com/diagrams/diagrams-lib/issues/14)++- Image primitives [\#10](https://github.com/diagrams/diagrams-lib/issues/10)++- Merge polygon code from Dmitry Olshansky [\#4](https://github.com/diagrams/diagrams-lib/issues/4)++**Fixed bugs:**++- Enhancements for `arc`s. [\#54](https://github.com/diagrams/diagrams-lib/issues/54)++- boundingBox computes incorrect bounding box for transformed diagrams [\#39](https://github.com/diagrams/diagrams-lib/issues/39)++- hcat is really \*terrible\* performance-wise [\#28](https://github.com/diagrams/diagrams-lib/issues/28)++- stroke sets the fill rule attribute to a default value, so it can't be changed later [\#26](https://github.com/diagrams/diagrams-lib/issues/26)++- Text alignment should be with respect to descent and ascent lines rather than text bounding box [\#17](https://github.com/diagrams/diagrams-lib/issues/17)++- incorrect bounds for Bezier segments [\#11](https://github.com/diagrams/diagrams-lib/issues/11)++- More combinators in D.Combinators [\#5](https://github.com/diagrams/diagrams-lib/issues/5)++**Merged pull requests:**++- clean up and fix bugs with ScaleInv wrapper [\#69](https://github.com/diagrams/diagrams-lib/pull/69) ([byorgey](https://github.com/byorgey))++- Patch proposal for generalized Color [\#67](https://github.com/diagrams/diagrams-lib/pull/67) ([haasn](https://github.com/haasn))++- Renamed `expandPath` to `scalePath` to make room [\#61](https://github.com/diagrams/diagrams-lib/pull/61) ([fryguybob](https://github.com/fryguybob))++- Envelope deformation [\#60](https://github.com/diagrams/diagrams-lib/pull/60) ([mgsloan](https://github.com/mgsloan))++- Added `Eq` instances for some data structures. [\#59](https://github.com/diagrams/diagrams-lib/pull/59) ([fryguybob](https://github.com/fryguybob))++- More involved diameter benchmark + better implementation [\#58](https://github.com/diagrams/diagrams-lib/pull/58) ([mgsloan](https://github.com/mgsloan))++- Add NullBackend for Ellipsoid [\#57](https://github.com/diagrams/diagrams-lib/pull/57) ([mgsloan](https://github.com/mgsloan))++- Fixed `arc` and `arcT` so they are always CCW. [\#56](https://github.com/diagrams/diagrams-lib/pull/56) ([fryguybob](https://github.com/fryguybob))++- new sized, sizedAs, and sizeSpec2D functions [\#52](https://github.com/diagrams/diagrams-lib/pull/52) ([byorgey](https://github.com/byorgey))++- Update to track with diagrams-core renaming, and a better Show instance for R2 [\#47](https://github.com/diagrams/diagrams-lib/pull/47) ([byorgey](https://github.com/byorgey))++- Minor tweaks due to monoid-extra and dual-tree changes [\#45](https://github.com/diagrams/diagrams-lib/pull/45) ([byorgey](https://github.com/byorgey))++- split out default attribute setting and size adjustment into separate functions [\#42](https://github.com/diagrams/diagrams-lib/pull/42) ([byorgey](https://github.com/byorgey))++- make beneath infixl 6 [\#41](https://github.com/diagrams/diagrams-lib/pull/41) ([byorgey](https://github.com/byorgey))++- scale invariant [\#3](https://github.com/diagrams/diagrams-lib/pull/3) ([ludflu](https://github.com/ludflu))++- BoundingBox is now Monoidal [\#2](https://github.com/diagrams/diagrams-lib/pull/2) ([mgsloan](https://github.com/mgsloan))++- New module enabling nice syntax for constructing and pattern-matching literal points and vectors [\#1](https://github.com/diagrams/diagrams-lib/pull/1) ([byorgey](https://github.com/byorgey))++## [v0.5.0.1](https://github.com/diagrams/diagrams-lib/tree/0_5_0_1) (2012-07-24)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/0_5...0_5_0_1)++## [v0.5](https://github.com/diagrams/diagrams-lib/tree/0_5) (2012-03-09)++[Full Changelog](https://github.com/diagrams/diagrams-lib/compare/0_4...0_5)++**New features**+- `mkSizeSpec` function for constructing a `SizeSpec2D` from two `Maybe Double`s++- `beneath` as convenient synonym for `flip atop`++- Improvements and extensions to rounded rectangles by Peter Hall:++    + `roundedRect'` allows rounded rectangles with a different radius+      specified for each corner++    + both `roundedRect'` and `roundedRect` now allow negative radii,+      resulting in "inverted" circular corners++- [\#64](http://code.google.com/p/diagrams/issues/detail?id=64): New `Alignable` class for things that can be aligned.++- `explodeTrail` and `explodePath` have been generalized to return any+  `PathLike` type.++- New path functions `pathCentroid` (compute the centroid of a+  path's vertices) and `expandPath` (scale a path about its centroid).++- Generalized `centroid` function now exported from new module+  `Diagrams.Points`.++- Initial (experimental) support for animation:++    + `Animation` and `QAnimation` defined as synonyms for `Active`+      diagrams (see `active` package)++    + Instances for `Active`: `V`, `HasOrigin`, `Transformable`,+      `HasStyle`, `PathLike`, `Juxtaposable`, `Alignable`++    + `animEnvelope` and `animRect` functions for automatic bounding of+      animations++- `addClosingSegment` function for making the implicit closing+  segment of a closed trail explicit++- Improvements to `BoundingBox` module from Michael Sloan: querying+  of `BoundingBox` bounds, corners, extents, and transformation of+  objects to fit within a given box.++- Text alignment options from Michael Sloan++- `view` function for restricting a diagram's envelope to a+  rectangular region.++- `iterateN` function for iterating a finite number of times++- `atAngle` for placing two diagrams next to each other along a+  specified angle.++- `padX` and `padY` functions for padding in the X- and Y-directions+  independently.+++- generalized `showOrigin` function from Ian Ross++- [\#40](http://code.google.com/p/diagrams/issues/detail?id=40): add shears to `Diagrams.TwoD.Transform`++**Performance improvements**++- Use a balanced folding scheme for `cat'`, reducing time in some+  cases from \\(O(n^2)\\) to \\(O(n \\log n)\\)++- More efficient implementation of `beside`++**New instances**++- `Alignable` instances for `QDiagram`, `Path`, `Envelope`, `Active`, `Set`,+  `Map`, `[]`++- `Renderable` instances for `NullBackend` (`Trail`, `Path`, `Segment`,+  `Image`, `Text`)++- Instances for `Active`: `V`, `HasOrigin`, `Transformable`,+  `HasStyle`, `PathLike`, `Juxtaposable`, `Alignable`++**API changes**++- `R2` used to be a synonym for `(Double, Double)` but is now+  abstract. To convert between pairs of `Doubles` and `R2`, use the+  new functions `r2` and `unr2`.  There are two reasons for this+  change:+      1. to allow for future changes to the implementation of `R2`;+      2. `(Double, Double)` was an awkward special case getting in the+       way of useful tuple instances for classes like `HasOrigin`,+       `Enveloped`, and so on.++- `circlePath` has been removed; its functionality has been+  subsumed by `circle`.++- `adjustSegment` now takes an extra tolerance option.++- Ellipses are now represented using Bezier approximations rather+  than a separate special type.++- `BoundingBox` no longer has a `Transformable` instance; the old+  instance was misleading at best.++- Change semantics of `beside` (hence also `(|||)` and `(===)`) so the+  result's origin is the same as that of the first argument.++- `adjustDia2D` now takes a `SizeSpec2D`.++- `beside` and related functions are now implemented in terms of+  `juxtapose`.++- Instead of taking an `R2`, `roundedRect` now takes a pair of+  `Double`s, to be more consistent with `rect`.++**Dependency/version changes**++- Support for GHC 7.4.1:++    + depend on `colour` >= 2.3.2++    + update `base` and `array` upper bounds++- bump `vector-space` upper bound++**Bug fixes**++- Avoid scale by zero error in `showOrigin`.+- Base `adjustDia2D` translation on output size rather than diagram size.++## [v0.4.0.1] () (30 October 2011)+------------------------++- bump `data-default` dependency to allow version 0.3++## [v0.4]() (23 October 2011)+--------------------++**documentation fixes**++**New functions and primitives**+++ `wedge` shape primitive+++ `fromDirection` function for converting angles to 2D unit vectors;++  inverse function `direction` generalized to return any Angle type++ New functions for computing and adjusting segment lengths+++ `scaleUToX` and `scaleUToY` for doing uniform scales+  resulting in a desired width or height.+++ `circlePath`, `reversePath`, `decoratePath`+++**New features**+++ Completely new and improved polygon generation API+++ Cubic splines+++ User-controllable path fill rules++**Bug fixes**+++ fix incorrect corner case in arc generation+++ fix incorrect `reverseTrail` function++## [v0.3]() (18 June 2011)+-----------------++**New features**+++ new customizable `stroke'` function which lets you assign names to+  path vertices+++ `circle` and `square` functions now take a size argument+++ function for adjusting 2D diagrams to requested size abstracted+  from cairo backend+++ generalize `PathLike` class to include an instance for diagrams,+  and collapse things like `polygon`/`polygonPath` into a single+  polymorphic function+++ basic text support+++ basic support for external images+++ very sketchy initial proof-of-concept library of 3D primitives.+  See also diagrams-povray package.++**Bug fixes**+++ Issue 32 (`mempty` not behaving correctly within concatenations)++## [v0.2]() (3 June 2011)+----------------++**New functions and primitives**+++ `scaleToX` and `scaleToY` for scaling to an absolute width/height+++ `reverseTrail`+++ new `Angle` class and ability to use radians, degrees, or circle fractions+  for specifying angles+++ `rotateAbout` and `reflectAbout` transformations based on new conjugation functions+++ `rect` and `roundedRect` primitives+++ `explodeTrail`/`Path` for breaking trails and paths into individual segments++**New features**+++ opacity attribute+++ support for path clipping++* **New modules**+    + `Diagrams.BoundingBox`++**Fixes and updates**++++ `withBounds` now properly uses the new bounds instead of just combining+      them with the old++## [v0.1.1]() (18 May 2011)+------------------++* minor documentation fixes++* link to new website++## [v0.1]() [17 May 2011]+----------------++* initial preview release++\* *This Change Log was automatically generated by (and then edited by hand) [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
− CHANGES.markdown
@@ -1,405 +0,0 @@-0.7.1.1 (27 September 2013)------------------------------* allow semigroups-0.11--0.7.1 (11 September 2013)----------------------------* **New features**--    - New standard miter limit attribute-    - New functions `lineColorA`, `lineWidthA`, `lineMiterLimitA`,-      `fontSizeA` for directly applying attribute values-    - `setDefault2DAttributes` now sets default line cap (butt), line-      join (miter), and miter limit (10) attributes--* **New instances**--    - `Data.Default` instances for-        - `LineCap`-	- `LineJoin`-	- `LineMiterLimit`-	- `LineWidth`-	- `LineColor`-	- `FontSize`--0.7 (9 August 2013)----------------------* **New features**--    - New module `Diagrams.TwoD.Curvature`, for computing the-      curvature of 2D segments at any given point.-    - New module `Diagrams.Offset`, containing an `offsetSegment`-      function that builds a trail a fixed distance from the original-      segment.  This is a precursor to planned functions `offsetTrail`-      and `offsetPath`.-    - New function `Diagrams.TwoD.Transform.onBasis`, for extracting a-      matrix representation of a 2D transformation-    - New functions `extrudeEnvelope` and `intrudeEnvelope`, for-      extending or shrinking an envelope only in a certain direction.-    - Generalize the `Color` class to absolute colors.-      This addresses concerns raised in issue #66 by letting the backend-      choose which color space to render `Color` instances to. Functions are-      provided for backwards compatibility with the old semantics.-    - New function `scaleInvPrim` for creating a diagram from a single-      scale-invariant primitive.-    - New module `Diagrams.Parametric`, containing a collection of-      classes abstracting over "parametric" things: `Parametric`,-      `DomainBounds`, `EndValues`, `Sectionable`, and `HasArcLength`,-      with instances for segments, trails, and related things.-    - A big refactoring of segments and trails:-        - Segments can now be either "closed" or "open".-        - There are now two types of trails: "lines" (which travel-          from point A to point B) or "loops" (closed curves which end-          where they started). `Trail` is now a wrapper type which can-          contain both loops and lines.-        - There is a new `Located` wrapper type for adding locations to-          translation-invariant things.  `Path`s now consist of a-          collection of `Located Trail`s.-        - The `PathLike` class is now renamed to `TrailLike`; the-          `trailLike` function takes a `Located Trail` as input.-    - New convenience functions `boundaryFrom` and `boundaryFromMay`,-      for computing boundaries of subdiagrams.-    - Re-export from `diagrams-lib` a lot of things defined in-      `diagrams-core`, to make them easier for users to find.  Several-      new modules have been created as a result: `Diagrams.Query`,-      `Diagrams.Envelope`, `Diagrams.Trace`, and `Diagrams.Names`.-    - Export the `centroid` function from `Diagrams.Prelude`.-    - `triangle` is now a synonym for `eqTriangle`.--* **New instances**--    - `IsPrim` instances for `Path`, `Ellipsoid`, `Image`, `Text`, and-      `ScaleInv`-    - `Eq`, `Ord`, and `Show` instances for `SizeSpec2D`--* **API changes**--    - `CircleFrac` has been renamed `Turn` (though `CircleFrac` is-      retained as a deprecated synonym).-    - `Diagrams.Coordinates` is no longer exported from-      `Diagrams.Prelude`.  This is for compatibility with `lens`, as `(&)`-      is a rather important lens operator and clashes with-      `Diagrams.Coordinates`.  Users who want the `Coordinates` stuff can import-      `Diagrams.Coordinates` explicitly.--* **Dependency/version changes**--    - allow `base-4.7`-    - upgrade to `monoid-extras-0.3`-    - depend on `data-default-class` instead of `data-default`-    - Tested with GHC 7.7.--* **Bug fixes**--    - Added a special case that was a not handled properly by the-      quadratic solver, resulting in bogus envelopes in certain cases-      (#88).-    - Import only `Data.NumInstances.Tuple` instead of-      `Data.NumInstances`. Previously, `Diagrams.Prelude` exported-      `Eq`, `Show`, and `Num` instances for functions and tuples; now-      it only exports tuple instances. Users wishing to use-      `Diagrams.CubicSpline` with a vector space built over functions (!?)-      can import `Data.NumInstances.Function` themselves. (#48)-    - Do scaling on a `Path` *before* constructing a `TrailLike` in-      `rect` (#43)--0.6.0.3 (4 May 2013)-----------------------* bump upper bound to allow `NumInstances-1.3`--0.6.0.2 (28 March 2013)--------------------------* bump upper bound to allow `NumInstances-1.2`--* Quadratic solver is now more numerically stable, getting rid of some-  incorrect behavior of `juxtapose`-  ([\#46](https://github.com/diagrams/diagrams-lib/issues/46))--0.6.0.1: 7 January 2013--------------------------* allow `semigroups-0.9`--0.6: 11 December 2012------------------------* **New features**--    - `boundingRect` function for constructing a bounding rectangle--    - `bg` function for "setting the background color" (*i.e.* placing-      atop a colored bounding rectangle)--    - New functions `setDefault2DAttributes` and `adjustDiaSize2D`.-      `adjustDia2D` does both --- so the behavior of `adjustDia2D` has-      not changed, but it is now possible to apply just one of the two-      adjustments using the new functions.--    - `Diagrams.TwoD.Transform` now exports a `ScaleInv` type for creating-      scale-invariant objects, which are only affected by rotational-      and translational components of transformations.--    - The new `Diagrams.Coordinates` module provides nicer syntax for-      constructing and pattern-matching point and vector literals.--    - New `fromFixedSeg` function in `Diagrams.Segment`, which-      decomposes a `FixedSegment` into a starting point and a `Segment`.--    - New `withTrace` function for setting the `Trace` of a diagram.--    - Three new size-related functions:--        - New `sized` function for scaling an object to a particular size.-          One particularly nice use of this is to obviate the need to keep-          fiddling with the line width to get diagrams to "look right";-          just set the line width relative to some arbitrary scale-          (*e.g.* assuming the final diagram will fit into a 1x1 box) and-          then apply `sized` to the final diagram to make it that given-          arbitrary size.  It can also be used for easily making something-          (a diagram, path, trail, ...) the same size as something else,-          with the help of the new `sizeSpec2D` function.--        - New `sizedAs` function, for setting the size of some object to-          be "the same as" some other object.--        - New `sizeSpec2D` function for conveniently calculating the size-          of an object as a `SizeSpec2D` value (for use with the new `sized`-          funtion).--    - New `extrudeEnvelope` and `intrudeEnvelope` functions for-      modifying envelopes in a single direction only, as well as new-      functions `extrude{Left,Right,Bottom,Top}` specializing-      `extrudeEnvelope` to 2D.--    - `arcCW` draws clockwise arcs; `arc'` draws arcs counterclockwise-      or clockwise as the radius is positive or negative,-      respectively.--    - fill color attribute is generalized to support "recommended" and-      "committed" colors; text objects use a recommended fill color of-      black.--* **New instances**--    - The `Show` instance for `R2` now produces something like `2 & 6`-      instead of `R2 { unR2 = (2,6) }`.  The `Read` instance has also-      been changed to match, so `read . show = id`.--    - `Enveloped` instance for `FixedSegment`--    - `Traced` instances for `Segment`, `FixedSegment`, `Trail`, and `Path`--    - New derived `Eq` instances for `LineCapA`, `LineJoinA`, `Dashing`,-      `DashingA`, `FillRule`, `Font`, `FontSize`, `FontSlant`, `FontSlantA`,-      `FontWeight`, and `FontWeightA`--    - `Renderable Ellipsoid NullBackend` instance--* **API changes**--    - `Data.Colour` (minus `atop` and `AffineSpace`) is now re-exported-      from Diagrams.Prelude for convenience.--    - The `beneath` function is now infixl 6.--    - The `BoundingBox` module has had a complete overhaul.  There is-      now a special empty bounding box, and bounding boxes are an-      instance of `Monoid`.--    - The type of `withEnvelope` has been slightly generalized.--    - `Diagrams.TwoD.Adjust.adjustSize` is now deprecated; it has been-      renamed and moved to `Diagrams.TwoD.Size.requiredScaleT`.--    - `expandPath` has been renamed to `scalePath`.--* **Dependency/version changes**--    - Allow `data-default` 0.4 and 0.5-    - Allow `base`-4.6-    - Allow `containers`-0.5--* **Bug fixes**--    - `arc` and `arcT` functions now always produce counterclockwise arcs,-      as claimed.--0.5: 9 March 2012--------------------* **New features**-    - `mkSizeSpec` function for constructing a `SizeSpec2D` from two `Maybe Double`s-    - `beneath` as convenient synonym for `flip atop`-    - Improvements and extensions to rounded rectangles by Peter Hall:-        + `roundedRect'` allows rounded rectangles with a different radius-          specified for each corner-        + both `roundedRect'` and `roundedRect` now allow negative radii,-          resulting in "inverted" circular corners-    - [\#64](http://code.google.com/p/diagrams/issues/detail?id=64): New `Alignable` class for things that can be aligned.-    - `explodeTrail` and `explodePath` have been generalized to return any-      `PathLike` type.-    - New path functions `pathCentroid` (compute the centroid of a-      path's vertices) and `expandPath` (scale a path about its centroid).-    - Generalized `centroid` function now exported from new module-      `Diagrams.Points`.-    - Initial (experimental) support for animation:-        + `Animation` and `QAnimation` defined as synonyms for `Active`-          diagrams (see `active` package)-        + Instances for `Active`: `V`, `HasOrigin`, `Transformable`,-          `HasStyle`, `PathLike`, `Juxtaposable`, `Alignable`-        + `animEnvelope` and `animRect` functions for automatic bounding of-          animations-    - `addClosingSegment` function for making the implicit closing-      segment of a closed trail explicit-    - Improvements to `BoundingBox` module from Michael Sloan: querying-      of `BoundingBox` bounds, corners, extents, and transformation of-      objects to fit within a given box.-    - Text alignment options from Michael Sloan-    - `view` function for restricting a diagram's envelope to a-      rectangular region.-    - `iterateN` function for iterating a finite number of times-    - `atAngle` for placing two diagrams next to each other along a-      specified angle.-    - `padX` and `padY` functions for padding in the X- and Y-directions-      independently.-    - generalized `showOrigin` function from Ian Ross-    - [\#40](http://code.google.com/p/diagrams/issues/detail?id=40): add shears to `Diagrams.TwoD.Transform`--* **Performance improvements**-    - Use a balanced folding scheme for `cat'`, reducing time in some-      cases from \\(O(n^2)\\) to \\(O(n \\log n)\\)-    - More efficient implementation of `beside`--* **New instances**-    - `Alignable` instances for `QDiagram`, `Path`, `Envelope`, `Active`, `Set`,-      `Map`, `[]`-    - `Renderable` instances for `NullBackend` (`Trail`, `Path`, `Segment`,-      `Image`, `Text`)-    - Instances for `Active`: `V`, `HasOrigin`, `Transformable`,-      `HasStyle`, `PathLike`, `Juxtaposable`, `Alignable`--* **API changes**-    - `R2` used to be a synonym for `(Double, Double)` but is now-      abstract. To convert between pairs of `Doubles` and `R2`, use the-      new functions `r2` and `unr2`.  There are two reasons for this-      change:-          1. to allow for future changes to the implementation of `R2`;-          2. `(Double, Double)` was an awkward special case getting in the-           way of useful tuple instances for classes like `HasOrigin`,-           `Enveloped`, and so on.-    - `circlePath` has been removed; its functionality has been-      subsumed by `circle`.-    - `adjustSegment` now takes an extra tolerance option.-    - Ellipses are now represented using Bezier approximations rather-      than a separate special type.-    - `BoundingBox` no longer has a `Transformable` instance; the old-      instance was misleading at best.-    - Change semantics of `beside` (hence also `(|||)` and `(===)`) so the-      result's origin is the same as that of the first argument.-    - `adjustDia2D` now takes a `SizeSpec2D`.-    - `beside` and related functions are now implemented in terms of-      `juxtapose`.-    - Instead of taking an `R2`, `roundedRect` now takes a pair of-      `Double`s, to be more consistent with `rect`.--* **Dependency/version changes**-    - Support for GHC 7.4.1:-        + depend on `colour` >= 2.3.2-        + update `base` and `array` upper bounds-    - bump `vector-space` upper bound--* **Bug fixes**-    - Avoid scale by zero error in `showOrigin`.-    - Base `adjustDia2D` translation on output size rather than diagram size.--0.4.0.1: 30 October 2011---------------------------* bump `data-default` dependency to allow version 0.3--0.4: 23 October 2011-----------------------* **documentation fixes**--* **New functions and primitives**--    + `wedge` shape primitive-    + `fromDirection` function for converting angles to 2D unit vectors;-      inverse function `direction` generalized to return any Angle type-    + New functions for computing and adjusting segment lengths-    + `scaleUToX` and `scaleUToY` for doing uniform scales-      resulting in a desired width or height.-    + `circlePath`, `reversePath`, `decoratePath`--* **New features**--    + Completely new and improved polygon generation API-    + Cubic splines-    + User-controllable path fill rules--* **Bug fixes**--    + fix incorrect corner case in arc generation-    + fix incorrect `reverseTrail` function--0.3: 18 June 2011--------------------* **New features**-    + new customizable `stroke'` function which lets you assign names to-      path vertices-    + `circle` and `square` functions now take a size argument-    + function for adjusting 2D diagrams to requested size abstracted-      from cairo backend-    + generalize `PathLike` class to include an instance for diagrams,-      and collapse things like `polygon`/`polygonPath` into a single-      polymorphic function-    + basic text support-    + basic support for external images-    + very sketchy initial proof-of-concept library of 3D primitives.-      See also diagrams-povray package.--* **Bug fixes**-    + Issue 32 (`mempty` not behaving correctly within concatenations)--0.2: 3 June 2011-------------------* **documentation fixes**--* **New functions and primitives**-    + `scaleToX` and `scaleToY` for scaling to an absolute width/height-    + `reverseTrail`-    + new `Angle` class and ability to use radians, degrees, or circle fractions-      for specifying angles-    + `rotateAbout` and `reflectAbout` transformations based on new conjugation functions-    + `rect` and `roundedRect` primitives-    + `explodeTrail`/`Path` for breaking trails and paths into individual segments--* **New features**-    + opacity attribute-    + support for path clipping--* **New modules**-    + `Diagrams.BoundingBox`--* **Fixes and updates**-    + `withBounds` now properly uses the new bounds instead of just combining-      them with the old--0.1.1: 18 May 2011---------------------* minor documentation fixes-* link to new website--0.1: 17 May 2011-------------------* initial preview release
LICENSE view
@@ -1,15 +1,35 @@-Copyright (c) 2011-2013 diagrams-lib team:+Copyright (c) 2011-2016 diagrams-lib team: +  Florent Becker <florent.becker@univ-orleans.fr>+  Jan Bracker <jan.bracker@googlemail.com>   Daniel Bergey <bergey@alum.mit.edu>+  Vincent Berthoux <vincent.berthoux@gmail.com>+  Christopher Chalmers <c.chalmers@me.com>+  Michael Chavinda <mchavinda@colgate.edu>+  Denys Duchier <denys.duchier@univ-orleans.fr>   Daniil Frumin <difrumin@gmail.com>+  Ben Gamari <ben@smart-cactus.org>+  Allan Gardner <allanegardner@gmail.com>+  Pontus Granström <pnutus@gmail.com>+  Gabor Greif <ggreif@gmail.com>   Niklas Haas <nand@lavabit.com>   Peter Hall <peter.hall@memorphic.com>+  Dashiell Halpern   Claude Heiland-Allen <claudiusmaximus@goto10.org>   Deepak Jois <deepak.jois@gmail.com>+  Sidharth Kapur <sidharthkapur1@gmail.com>+  Taru Karttunen <taruti@taruti.net>+  John Lato <jwlato@tsurucapital.com>+  Konrad Madej <kdmadej@gmail.com>+  Chris Mears <chris@cmears.id.au>+  Alexis Praga <praga@cerfacs.fr>+  Jeffrey Rosenbluth <jeffrey.rosenbluth@gmail.com>   Ian Ross <ian@skybluetrades.net>+  Carlos Scheidegger <cscheid@research.att.com>   Michael Sloan <mgsloan@gmail.com>   Jim Snavely <ludflu@gmail.com>   Kanchalai Suveepattananont <ksuvee@seas.upenn.edu>+  Robert Vollmert <rfvollmert@gmail.com>   Scott Walck <walck@lvc.edu>   Ryan Yates <fryguybob@gmail.com>   Brent Yorgey <byorgey@cis.upenn.edu>
README.markdown view
@@ -1,4 +1,4 @@-[![Build Status](https://secure.travis-ci.org/diagrams/diagrams-lib.png)](http://travis-ci.org/diagrams/diagrams-lib)+[![Build Status](https://github.com/diagrams/diagrams-lib/actions/workflows/haskell-ci.yml/badge.svg)](https://github.com/diagrams/diagrams-lib/actions/workflows/haskell-ci.yml)  The standard library for [diagrams](http://projects.haskell.org/diagrams/), a Haskell embedded
Setup.hs view
@@ -1,29 +1,2 @@-import           Data.List                 (isSuffixOf) import           Distribution.Simple-import           Distribution.Simple.Setup (Flag (..), HaddockFlags,-                                            haddockDistPref)-import           Distribution.Simple.Utils (copyFiles)-import           Distribution.Text         (display)-import           Distribution.Verbosity    (normal)-import           System.Directory          (getDirectoryContents)-import           System.FilePath           ((</>))---- Ugly hack, logic copied from Distribution.Simple.Haddock-haddockOutputDir :: Package pkg => HaddockFlags -> pkg -> FilePath-haddockOutputDir flags pkg = destDir-   where-     baseDir = case haddockDistPref flags of-                      NoFlag -> "."-                      Flag x -> x-     destDir = baseDir </> "doc" </> "html" </> display (packageName pkg)--diagramsDir = "diagrams"--main :: IO ()-main = defaultMainWithHooks simpleUserHooks-         { postHaddock = \args flags pkg lbi -> do-             dias <- filter ("svg" `isSuffixOf`) `fmap` getDirectoryContents diagramsDir-             copyFiles normal (haddockOutputDir flags pkg)-               (map (\d -> ("", diagramsDir </> d)) dias)-             postHaddock simpleUserHooks args flags pkg lbi-         }+main = defaultMain
diagrams-lib.cabal view
@@ -1,5 +1,5 @@ Name:                diagrams-lib-Version:             0.7.1.1+Version:             1.5.1 Synopsis:            Embedded domain-specific language for declarative graphics Description:         Diagrams is a flexible, extensible EDSL for creating                      graphics of many types.  Graphics can be created@@ -7,83 +7,176 @@                      multiple backends.  diagrams-lib provides a                      standard library of primitives and operations for                      creating diagrams.  To get started using it, see-                     "Diagrams.Prelude".-Homepage:            http://projects.haskell.org/diagrams+                     the "Diagrams" module, and refer to the tutorials and+                     documentation on the diagrams website,+                     <http://diagrams.github.io>.+Homepage:            http://diagrams.github.io License:             BSD3 License-file:        LICENSE Author:              Brent Yorgey Maintainer:          diagrams-discuss@googlegroups.com Bug-reports:         http://github.com/diagrams/diagrams-lib/issues Category:            Graphics-Build-type:          Custom-Cabal-version:       >=1.10-Extra-source-files:  CHANGES.markdown, README.markdown, diagrams/*.svg-Tested-with:         GHC == 7.4.2, GHC == 7.6.1+Build-type:          Simple+Cabal-version:       1.18+Extra-source-files:  diagrams/*.svg+Extra-doc-files:     CHANGELOG.md, README.markdown, diagrams/*.svg+Tested-with:         GHC ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1 || ==9.12.1 Source-repository head   type:     git   location: http://github.com/diagrams/diagrams-lib.git  Library-  Exposed-modules:     Diagrams.Prelude,+  Exposed-modules:     Diagrams,+                       Diagrams.Prelude,                        Diagrams.Align,+                       Diagrams.Angle,+                       Diagrams.Animation,+                       Diagrams.Animation.Active,+                       Diagrams.Attributes,+                       Diagrams.Attributes.Compile,+                       Diagrams.Backend.CmdLine,+                       Diagrams.BoundingBox,                        Diagrams.Combinators,                        Diagrams.Coordinates,-                       Diagrams.Attributes,-                       Diagrams.Points,+                       Diagrams.CubicSpline,+                       Diagrams.CubicSpline.Boehm,+                       Diagrams.CubicSpline.Internal,+                       Diagrams.Deform+                       Diagrams.Direction,+                       Diagrams.Envelope,+                       Diagrams.LinearMap,                        Diagrams.Located,+                       Diagrams.Names,                        Diagrams.Parametric,+                       Diagrams.Parametric.Adjust,+                       Diagrams.Path,+                       Diagrams.Points,+                       Diagrams.Query,                        Diagrams.Segment,+                       Diagrams.Size,+                       Diagrams.Tangent,+                       Diagrams.ThreeD,+                       Diagrams.ThreeD.Align,+                       Diagrams.ThreeD.Attributes,+                       Diagrams.ThreeD.Camera,+                       Diagrams.ThreeD.Deform,+                       Diagrams.ThreeD.Light,+                       Diagrams.ThreeD.Shapes,+                       Diagrams.ThreeD.Size,+                       Diagrams.ThreeD.Transform,+                       Diagrams.ThreeD.Projection,+                       Diagrams.ThreeD.Types,+                       Diagrams.ThreeD.Vector,+                       Diagrams.Trace,                        Diagrams.Trail,                        Diagrams.TrailLike,-                       Diagrams.Path,-                       Diagrams.CubicSpline,-                       Diagrams.CubicSpline.Internal,-                       Diagrams.Solve,                        Diagrams.Transform,-                       Diagrams.BoundingBox,-                       Diagrams.Names,-                       Diagrams.Envelope,-                       Diagrams.Trace,-                       Diagrams.Query,+                       Diagrams.Transform.ScaleInv,+                       Diagrams.Transform.Matrix,                        Diagrams.TwoD,-                       Diagrams.TwoD.Types,+                       Diagrams.TwoD.Adjust,                        Diagrams.TwoD.Align,-                       Diagrams.TwoD.Combinators,-                       Diagrams.TwoD.Transform,-                       Diagrams.TwoD.Ellipse,                        Diagrams.TwoD.Arc,-                       Diagrams.TwoD.Segment,+                       Diagrams.TwoD.Arrow,+                       Diagrams.TwoD.Arrowheads,+                       Diagrams.TwoD.Attributes,+                       Diagrams.TwoD.Combinators,                        Diagrams.TwoD.Curvature,+                       Diagrams.TwoD.Deform,+                       Diagrams.TwoD.Ellipse,+                       Diagrams.TwoD.Image,+                       Diagrams.TwoD.Model,                        Diagrams.TwoD.Offset,                        Diagrams.TwoD.Path,+                       Diagrams.TwoD.Points,                        Diagrams.TwoD.Polygons,-                       Diagrams.TwoD.Shapes,-                       Diagrams.TwoD.Vector,+                       Diagrams.TwoD.Segment,+                       Diagrams.TwoD.Segment.Bernstein,                        Diagrams.TwoD.Size,-                       Diagrams.TwoD.Model,+                       Diagrams.TwoD.Shapes,                        Diagrams.TwoD.Text,-                       Diagrams.TwoD.Image,-                       Diagrams.TwoD.Adjust,-                       Diagrams.ThreeD.Types,-                       Diagrams.ThreeD.Shapes,-                       Diagrams.Animation,-                       Diagrams.Animation.Active,-                       Diagrams.Util,-                       Diagrams.Backend.Show-  Build-depends:       base >= 4.2 && < 4.8,-                       containers >= 0.3 && < 0.6,-                       array >= 0.3 && < 0.5,-                       semigroups >= 0.3.4 && < 0.12,-                       monoid-extras >= 0.3 && < 0.4,-                       diagrams-core >= 0.7 && < 0.8,-                       active >= 0.1 && < 0.2,-                       vector-space >= 0.7.7 && < 0.9,-                       NumInstances >= 1.2 && < 1.4,+                       Diagrams.TwoD.Transform,+                       Diagrams.TwoD.Types,+                       Diagrams.TwoD.Vector,+                       Diagrams.Util+  other-modules:       Linear.Vector.Compat+  Build-depends:       base >= 4.9 && < 4.22,+                       containers >= 0.3 && < 0.8,+                       array >= 0.3 && < 0.6,+                       semigroups >= 0.3.4 && < 0.21,+                       monoid-extras >= 0.6 && < 0.8,+                       dual-tree >= 0.2 && < 0.3,+                       diagrams-core >= 1.4 && < 1.6,+                       diagrams-solve >= 0.1 && < 0.2,+                       active >= 0.2 && < 0.3,                        colour >= 2.3.2 && < 2.4,-                       data-default-class < 0.1,-                       pretty >= 1.0.1.2 && < 1.2,-                       newtype >= 0.2 && < 0.3,+                       data-default >= 0.8 && < 0.9,                        fingertree >= 0.1 && < 0.2,-                       intervals >= 0.2.2 && < 0.3+                       intervals >= 0.7 && < 0.10,+                       lens >= 5.1 && < 5.4,+                       tagged >= 0.7 && < 0.9,+                       optparse-applicative >= 0.11 && < 0.20,+                       filepath >= 1.4 && < 1.6,+                       JuicyPixels >= 3.3.4 && < 3.4,+                       hashable >= 1.1 && < 1.6,+                       linear >= 1.20.1 && < 1.24,+                       adjunctions >= 4.0 && < 5.0,+                       distributive >=0.2.2 && < 1.0,+                       process >= 1.1 && < 1.7,+                       directory >= 1.2 && < 1.4,+                       unordered-containers >= 0.2 && < 0.3,+                       text >= 0.7.1 && < 2.2,+                       mtl >= 2.0 && < 2.3 || >= 2.3.1 && < 2.4,+                       transformers >= 0.3.0 && < 0.7.0,+                       profunctors >= 5.0 && < 6.0,+                       exceptions >= 0.6 && < 1.0,+                       cereal >=0.4.1.1 && <0.6,+                       bytestring >=0.9 && <0.13,+                       fail >= 4.9.0.0 && <4.10   Hs-source-dirs:      src+  ghc-options: -Wall   default-language:    Haskell2010+  other-extensions: BangPatterns, CPP, DefaultSignatures, DeriveDataTypeable,+    DeriveFunctor, DeriveGeneric, EmptyDataDecls, ExistentialQuantification,+    GADTs, GeneralizedNewtypeDeriving, NoMonomorphismRestriction, Rank2Types,+    RankNTypes, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell,+    TypeOperators, TypeSynonymInstances, UndecidableInstances, ViewPatterns,+    LambdaCase++test-suite tests+  type: exitcode-stdio-1.0+  main-is: Test.hs+  other-modules: Diagrams.Test.Direction+               , Diagrams.Test.Trail+               , Diagrams.Test.Transform+               , Diagrams.Test.Transform.Matrix+               , Diagrams.Test.TwoD.Offset+               , Diagrams.Test.TwoD.Segment+               , Diagrams.Test.TwoD+               , Diagrams.Test.Angle+               , Instances+  hs-source-dirs: test+  build-depends:       base,+                       tasty >= 0.10 && < 1.6,+                       tasty-hunit >= 0.9.2 && < 0.11,+                       tasty-quickcheck >= 0.8 && < 0.12,+                       QuickCheck >= 2.7,+                       deepseq >= 1.3 && < 1.6,+                       diagrams-lib,+                       lens,+                       distributive,+                       numeric-extras,+                       diagrams-solve+  default-language:    Haskell2010++benchmark benchmarks+  type: exitcode-stdio-1.0+  main-is:Speed.hs+  hs-source-dirs:test+  default-language: Haskell2010+  build-depends:+    base < 5,+    criterion,+    diagrams-core,+    diagrams-lib
− diagrams/besideEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="84.860339985334" height="100.0" font-size="1" viewBox="0 0 85 100"><g><g transform="matrix(21.832407982400788,0.0,0.0,21.832407982400788,21.832407982400788,78.16759201759922)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(128,0,128)" fill-opacity="1.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 2.8868978654915356,-2.0803467982373034 c 0.0,-0.8284271247461902 -0.6715728752538097,-1.5 -1.4999999999999996 -1.5c -0.8284271247461902,-5.072653133236334e-17 -1.5,0.6715728752538096 -1.5 1.4999999999999996c -1.0145306266472669e-16,0.8284271247461902 0.6715728752538095,1.5 1.4999999999999993 1.5c 0.8284271247461902,1.5217959399709003e-16 1.5,-0.6715728752538094 1.5000000000000002 -1.4999999999999996Z" /></g></g><g transform="matrix(21.832407982400788,0.0,0.0,21.832407982400788,21.832407982400788,78.16759201759922)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,165,0)" fill-opacity="1.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.0,0.0 c 0.0,-0.5522847498307935 -0.44771525016920644,-1.0 -0.9999999999999998 -1.0c -0.5522847498307935,-3.3817687554908895e-17 -1.0,0.4477152501692064 -1.0 0.9999999999999997c -6.763537510981779e-17,0.5522847498307935 0.4477152501692063,1.0 0.9999999999999996 1.0c 0.5522847498307935,1.0145306266472669e-16 1.0,-0.44771525016920627 1.0000000000000002 -0.9999999999999997Z" /></g></g><g transform="matrix(21.832407982400788,0.0,0.0,21.832407982400788,21.832407982400788,78.16759201759922)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 9.160693596474608e-2,0.0 c 0.0,-5.05931137120553e-2 -4.101382225269076e-2,-9.160693596474608e-2 -9.160693596474606e-2 -9.160693596474608e-2c -5.05931137120553e-2,-3.0979347383183296e-18 -9.160693596474608e-2,4.1013822252690756e-2 -9.160693596474608e-2 9.160693596474605e-2c -6.195869476636659e-18,5.05931137120553e-2 4.1013822252690756e-2,9.160693596474608e-2 9.160693596474603e-2 9.160693596474608e-2c 5.05931137120553e-2,9.293804214954989e-18 9.160693596474608e-2,-4.101382225269075e-2 9.160693596474609e-2 -9.160693596474605e-2Z" /></g></g></g></svg>
− diagrams/closeLineEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500.0" height="68.18181818181819" font-size="1" viewBox="0 0 500 68"><g><g transform="matrix(41.32231404958678,0.0,0.0,41.32231404958678,250.0,34.09090909090909)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 3.5,-0.75 l 2.0,1.0 l -3.0,0.5 l -2.0,-1.0 l 1.0,-0.5 h 2.0 Z" /></g></g><g transform="matrix(41.32231404958678,0.0,0.0,41.32231404958678,250.0,34.09090909090909)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -2.5,-0.75 l 2.0,1.0 l -3.0,0.5 l -2.0,-1.0 l 1.0,-0.5 " /></g></g></g></svg>
− diagrams/cubicOffsetExample.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600.0" height="302.8587568898731" font-size="1" viewBox="0 0 600 303"><g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 33.417476733497544,-1.6473872989404414 c 0.8062160672722946,1.905601613552696 2.6000741970593846,4.243483864433998 6.1181079451566704 6.588839696498854" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 32.86092310128323,-3.9349733247464833 c 2.5438939233202715e-2,0.6274938344190003 0.18344919012734384,1.4057027990550142 0.5565536322143172 2.287586025806042" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 33.000311673428904,-5.029283712603785 c -0.11129079091128233,0.22258158182256466 -0.16025559544153956,0.5795904798926219 -0.13938857214567413 1.0943103878573026" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 32.97862528132407,-5.0044688233462296 c 5.153156049894565e-3,-5.841483753310084e-3 1.692390853525727e-2,-1.5289922118400523e-2 2.1686392104834917e-2 -2.4814889257555817e-2" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 32.70873785722833,-4.826371179429536 c 8.358937623750423e-2,-3.1497446118479856e-2 0.1859951877994061,-8.299958544359087e-2 0.2698874240957419 -0.17809764391669347" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 32.055356993320274,-4.853120624705525 c 0.22391741974384227,0.10443579260779995 0.40910731509659143,0.11879455062523658 0.6533808639080557 2.6749445275989214e-2" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 31.72347357921467,-5.143549245977102 c 9.378804941573246e-2,0.15871823747277802 0.21780468092203953,0.23722193939149805 0.33188341410559885 0.290428621271577" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 31.691808767395802,-5.205805672754545 c 5.879553721364279e-3,2.4935979612594604e-2 2.2991427605793424e-2,4.7578391955314714e-2 3.166481181886982e-2 6.2256426777443974e-2" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 31.711972577462916,-5.042326266928818 c -3.953664939657162e-3,-6.508340746820251e-2 -9.443295519196648e-3,-0.11801225901967066 -2.0163810067113208e-2 -0.163479405825728" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 31.661929314384686,-4.205360512837057 c 4.7781656372351815e-2,-0.35039881339724666 6.298412834573885e-2,-0.6239392027650263 5.004326307822693e-2 -0.8369657540917615" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 28.08645729142907,5.326289096396298 c 2.4270875082384364,-4.854175016476873 3.347932569058263,-7.86302694731943 3.5754720229556165 -9.531649609233355" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,0,255)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 28.980884482428987,5.773502691896256 c 10.0,-20.0 -5.0,-10.0 10.0 0.0" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 22.22681744227194,-4.975977707743982 c 0.0,1.1652184940288222 4.623280507125882e-2,5.8820192426981635 4.707106781186548 10.542893218813452" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 22.22527234029002,-5.190961309967572 c 1.3596945248057525e-3,5.336801009862579e-2 1.5451019819200384e-3,0.12796315263602145 1.5451019819200384e-3 0.21498360222358961" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 22.219061458808028,-5.310773112363486 c 2.667352776137804e-3,3.119320885427821e-2 5.02539135926483e-3,7.328131507884304e-2 6.2108814819927805e-3 0.11981180239591362" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 22.17283082599389,-5.555708087831506 c 1.5097180450167632e-2,4.9065836463044805e-2 3.77384688109178e-2,0.14562383531926093 4.6230632814137085e-2 0.2449349754680199" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 21.98765210484117,-5.902700770832827 c 5.915986229623687e-2,7.248909052964826e-2 0.1384351262519885,0.19507599957394778 0.18517872115271883 0.34699268300132147" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 21.651201254320615,-6.170110768106993 c 0.11662169140991802,5.726092743049299e-2 0.23178207485071373,0.1391584419009324 0.33645085052055707 0.26740999727416637" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 21.098403476790917,-6.266956245808321 c 0.20191976907307557,-2.2211174598038314e-2 0.3705651859412463,7.369780030821671e-3 0.552797777529697 9.684547770132823e-2" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 20.68717773824717,-6.127488517764909 c 0.14370207595416082,-8.692183392465397e-2 0.28088371953770774,-0.12513010595274754 0.41122573854374933 -0.13946772804341212" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 20.48926554076378,-5.971144448967711 c 6.721861761310331e-2,-6.819078770254902e-2 0.13666548184831018,-0.11929744431724854 0.19791219748338984 -0.15634406879719753" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 20.34021917756031,-5.779740302591862 c 4.992158119902007e-2,-8.181592474283846e-2 0.10709836789040328,-0.14884946518015663 0.14904636320346876 -0.19140414637584913" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 20.208899018783505,-5.493502020830685 c 4.542742635734029e-2,-0.12492542248268579 0.10576723145080258,-0.2443598730880031 0.13132015877680647 -0.28623828176117616" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 19.662024018783505,-3.911470770830685 c 0.3068064887395122,-0.8437178440336586 0.4701733778151219,-1.3711017889915853 0.546875 -1.58203125" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 15.332390251272024,5.826808696756062 c 2.472343200154557,-4.944686400309114 3.7115479674728395,-8.03854351748049 4.329633767511479 -9.738279467586747" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,0,255)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 16.22681744227194,6.27402229225602 c 10.0,-20.0 0.0,-10.0 10.0 0.0" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 13.009770219789226,-2.4104743608548755 c -0.2855399505357898,9.691052866669228e-2 -0.6461909532308479,0.16047436085487377 -1.0892105128500125 0.16047436085487377" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 13.893839526646092,-2.982120392131483 c -0.2040335330264581,0.2331811806016664 -0.4832891526977512,0.4356236759256361 -0.8840693068568652 0.5716460312766082" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 14.185013884508258,-4.851123441588392 c 0.26509461237177,0.70691896632472 0.17274121378843077,1.3388138247133698 -0.2911743578621666 1.8690030494569096" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 11.295559706939214,-6.75 c 1.3528278761481514,0.0 2.551247208532007,0.996991307646174 2.8894541775690445 1.8988765584116083" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 9.203810396758112,-5.989978417355905 c 0.7131947941382994,-0.5593684659908231 1.4205071509921139,-0.7600215826440954 2.0917493101811018 -0.7600215826440954" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 7.49586988404493,-3.666654710493214 c 0.5144343791036886,-1.122402281680775 1.1116643005702707,-1.8556560894957015 1.7079405127131826 -2.3233237068626913" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 5.920559706939214,6.75 c 0.0,-5.2028660713222505 0.6810675710972038,-8.46557993374737 1.5753101771057156 -10.416654710493214" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,0,255)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 6.920559706939214,6.75 c 0.0,-20.0 10.0,-10.0 5.0 -10.0" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 2.660826934705069,-8.569475230141833 c 0.48895650949203384,-0.9250528557957396 1.122019777927579,-1.556458070895497 1.9677823889408268 -1.894763115300796" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 1.7534960535105757,-5.761630493146541 c 0.2156599309608425,-1.086926052042646 0.5083600089169349,-2.0530349786323434 0.9073308811944935 -2.807844736995292" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M 0.8835457044574557,1.0837838328712444 c 0.31486663625486255,-2.3929864355369554 0.4763670537345416,-4.86175451761215 0.8699503490531199 -6.845414326017784" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -0.7828015282143159,7.11175903331599 c 0.9591837382060431,-1.6232340185025345 1.3712137747622202,-3.7849609203321535 1.6663472326717716 -6.027975200444747" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,128,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -5.0,9.46423834544263 c 2.0016683287913244,0.0 3.404020713214209,-0.9763323360826055 4.217198471785684 -2.3524793121266407" /></g></g><g transform="matrix(13.155574898915933,0.0,0.0,13.155574898915933,72.35566194403765,151.42937844493656)"><g stroke="rgb(0,0,255)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -5.0,10.46423834544263 c 10.0,0.0 5.0,-18.0 10.0 -20.0" /></g></g></g></svg>
− diagrams/cubicSplineEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600.0" height="150.9864382575877" font-size="1" viewBox="0 0 600 151"><g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 5.30560144347186,0.5148061556407302 c -0.2727272727272727,-0.2727272727272727 0.2727272727272728,-3.3636363636363638 2.0 -3.000000000000001c 1.727272727272727,0.36363636363636376 4.636363636363635,4.181818181818182 3.0 5.000000000000002c -1.6363636363636362,0.8181818181818175 -7.8181818181818175,-1.3636363636363633 -8.999999999999998 -2.999999999999999c -1.1818181818181817,-1.6363636363636362 2.6363636363636367,-2.727272727272727 4.000000000000002 -2.0c 1.3636363636363635,0.7272727272727275 0.27272727272727276,3.272727272727273 -3.552713678800501e-15 2.9999999999999982Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 5.5056014434718605,-2.48519384435927 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 1.5056014434718603,-0.4851938443592698 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 10.50560144347186,2.51480615564073 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 7.5056014434718605,-2.48519384435927 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 5.5056014434718605,0.5148061556407302 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M -6.333419254268965,0.5148061556407302 c 0.2142857142857144,-1.9107142857142858 0.4285714285714288,-3.8214285714285716 2.0 -3.0c 1.5714285714285712,0.8214285714285716 4.5,4.375 2.999999999999999 5.0c -1.5,0.625 -7.428571428571428,-1.6785714285714284 -8.999999999999998 -2.999999999999999c -1.5714285714285712,-1.3214285714285712 1.2142857142857149,-1.6607142857142856 4.0 -2.0" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M -6.133419254268965,-2.48519384435927 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M -10.133419254268965,-0.4851938443592698 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M -1.1334192542689652,2.51480615564073 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M -4.133419254268965,-2.48519384435927 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g><g transform="matrix(25.27996301518507,0.0,0.0,25.27996301518507,300.0,75.49321912879385)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M -6.133419254268965,0.5148061556407302 c 0.0,-0.1104569499661587 -8.954305003384129e-2,-0.2 -0.19999999999999996 -0.2c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996c -1.3527075021963559e-17,0.1104569499661587 8.954305003384128e-2,0.2 0.19999999999999993 0.2c 0.1104569499661587,2.029061253294534e-17 0.2,-8.954305003384126e-2 0.20000000000000004 -0.19999999999999996Z" /></g></g></g></svg>
− diagrams/decagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="95.10565162951534" font-size="1" viewBox="0 0 100 95"><g><g transform="matrix(28.092454034086124,0.0,0.0,28.092454034086124,49.99999999999999,47.55282581475767)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.3090169943749481,0.9510565162951532 l 0.3090169943749472,-0.9510565162951536 l -0.3090169943749477,-0.9510565162951534 l -0.8090169943749475,-0.5877852522924729 l -1.0,4.440892098500626e-16 l -0.8090169943749473,0.5877852522924731 l -0.3090169943749473,0.9510565162951536 l 0.3090169943749475,0.9510565162951535 l 0.8090169943749475,0.587785252292473 l 1.0,-5.551115123125783e-17 l 0.8090169943749477,-0.5877852522924736 Z" /></g></g></g></svg>
− diagrams/diagramA.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400.0" height="200.0" font-size="1" viewBox="0 0 400 200"><g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -10.0,4.132231404958677 c 12.0,0.0 8.0,-10.0 20.0 -8.0" /></g></g></g></svg>
− diagrams/diagramNeg.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400.0" height="200.0" font-size="1" viewBox="0 0 400 200"><g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 4.464000000000002,-3.8037685950413227 l 2.077770135746605,7.445342986425346 " /></g></g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 14.271599430272687,3.6415743913840233 c 0.0,-4.269066838162075 -3.4607624563640047,-7.7298292945260805 -7.729829294526079 -7.7298292945260805c -4.269066838162075,-2.614049519350648e-16 -7.7298292945260805,3.4607624563640043 -7.7298292945260805 7.729829294526078c -5.228099038701296e-16,4.269066838162075 3.460762456364004,7.7298292945260805 7.729829294526077 7.7298292945260805c 4.269066838162075,7.842148558051945e-16 7.7298292945260805,-3.4607624563640034 7.729829294526082 -7.729829294526078Z" /></g></g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -10.0,4.132231404958677 c 12.0,0.0 8.0,-10.0 20.0 -8.0" /></g></g></g></svg>
− diagrams/diagramPos.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400.0" height="200.0" font-size="1" viewBox="0 0 400 200"><g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M -4.463999999999999,3.1082314049586772 l -3.900145794392522,-8.6003214953271 " /></g></g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 1.079194030081819,-5.492090090368422 c 0.0,-5.215412572526979 -4.227927251947359,-9.44333982447434 -9.443339824474338 -9.44333982447434c -5.215412572526979,-3.193519156589014e-16 -9.44333982447434,4.227927251947358 -9.44333982447434 9.443339824474336c -6.387038313178028e-16,5.215412572526979 4.227927251947357,9.44333982447434 9.443339824474336 9.44333982447434c 5.215412572526979,9.580557469767042e-16 9.44333982447434,-4.227927251947357 9.443339824474343 -9.443339824474336Z" /></g></g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -10.0,4.132231404958677 c 12.0,0.0 8.0,-10.0 20.0 -8.0" /></g></g></g></svg>
− diagrams/diagramZero.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400.0" height="200.0" font-size="1" viewBox="0 0 400 200"><g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 0.0,-0.6177685950413228 l 61.17187500000001,54.37500000000001 " /></g></g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 143.0170881527289,53.75723140495869 c 0.0,-45.20186307090284 -36.64335008182604,-81.84521315272889 -81.84521315272887 -81.84521315272889c -45.20186307090284,-2.7678158462639055e-15 -81.84521315272889,36.643350081826036 -81.84521315272889 81.84521315272886c -5.535631692527811e-15,45.20186307090284 36.64335008182603,81.84521315272889 81.84521315272885 81.84521315272889c 45.20186307090284,8.303447538791717e-15 81.84521315272889,-36.64335008182603 81.84521315272893 -81.84521315272886Z" /></g></g><g transform="matrix(18.181818181818183,0.0,0.0,18.181818181818183,200.0,100.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="0.1" font-size="1.0em"><path d="M -10.0,4.132231404958677 c 12.0,0.0 8.0,-10.0 20.0 -8.0" /></g></g></g></svg>
− diagrams/dodecagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="99.99999999999999" font-size="1" viewBox="0 0 100 100"><g><g transform="matrix(24.35901749373842,0.0,0.0,24.35901749373842,50.0,49.99999999999999)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.8660254037844393,0.4999999999999996 l -2.220446049250313e-16,-1.0 l -0.5000000000000001,-0.8660254037844386 l -0.8660254037844387,-0.49999999999999994 l -1.0,1.6081226496766364e-16 l -0.8660254037844386,0.49999999999999994 l -0.5000000000000001,0.8660254037844386 l -3.4450928483976665e-16,1.0 l 0.49999999999999956,0.8660254037844389 l 0.8660254037844385,0.5000000000000002 l 1.0,-3.834758505292834e-17 l 0.8660254037844385,-0.5000000000000003 l 0.5000000000000021,-0.8660254037844389 Z" /></g></g></g></svg>
− diagrams/glueLineEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500.0" height="68.18181818181819" font-size="1" viewBox="0 0 500 68"><g><g transform="matrix(41.32231404958677,0.0,0.0,41.32231404958677,136.36363636363637,0.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 6.0,0.0 l 2.0,1.0 l -3.0,0.5 l -2.0,-1.0 l 3.0,-0.5 Z" /></g></g><g transform="matrix(41.32231404958677,0.0,0.0,41.32231404958677,136.36363636363637,0.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.0,0.0 l 2.0,1.0 l -3.0,0.5 l -2.0,-1.0 l 1.0,-0.5 " /></g></g></g></svg>
− diagrams/hendecagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="98.98214418809323" font-size="1" viewBox="0 0 100 99"><g><g transform="matrix(25.875425140597297,0.0,0.0,25.875425140597297,50.00000000000001,50.5141613269018)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.7566685458330673,0.2525708066345108 l -0.1423148382732844,-0.9898214418809328 l -0.6548607339452844,-0.7557495743542588 l -0.9594929736144971,-0.28173255684143045 l -0.9594929736144977,0.28173255684142884 l -0.6548607339452855,0.7557495743542577 l -0.14231483827328584,0.9898214418809326 l 0.4154150130018858,0.9096319953545187 l 0.841253532831181,0.5406408174555977 l 0.9999999999999999,-2.7755575615628914e-17 l 0.8412535328311814,-0.5406408174555973 l 0.41541501300188677,-0.9096319953545157 Z" /></g></g></g></svg>
− diagrams/hexagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="86.6025403784439" font-size="1" viewBox="0 0 100 87"><g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,50.0,43.30127018922196)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.49999999999999967,0.8660254037844387 l 0.5000000000000002,-0.8660254037844385 l -0.4999999999999996,-0.8660254037844388 l -0.9999999999999999,-4.440892098500626e-16 l -0.5000000000000003,0.8660254037844384 l 0.49999999999999933,0.866025403784439 l 1.0000000000000002,2.220446049250313e-16 Z" /></g></g></g></svg>
− diagrams/hruleEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="300.0" height="48.00000000000001" font-size="1" viewBox="0 0 300 48"><g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,24.000000000000004)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -2.5,0.4000000000000001 h 5.0 " /></g></g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,24.000000000000004)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -2.0,0.2 h 4.0 " /></g></g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,24.000000000000004)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -1.5,0.0 h 3.0 " /></g></g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,24.000000000000004)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -1.0,-0.2 h 2.0 " /></g></g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,24.000000000000004)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -0.5,-0.4 h 1.0 " /></g></g></g></svg>
− diagrams/lineFromOffsetsEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="300.0" height="50.0" font-size="1" viewBox="0 0 300 50"><g><g transform="matrix(50.0,0.0,0.0,50.0,0.0,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.0,0.0 l 2.0,-1.0 l 2.0,1.0 l 2.0,-0.5 " /></g></g></g></svg>
− diagrams/lineFromVerticesEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="300.0" height="120.00000000000001" font-size="1" viewBox="0 0 300 120"><g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,60.00000000000001)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -2.5,1.0 v -1.0 l 1.0,-1.0 l 4.0,1.0 " /></g></g></g></svg>
− diagrams/nonagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="98.48077530122075" font-size="1" viewBox="0 0 100 98"><g><g transform="matrix(31.572395939441876,0.0,0.0,31.572395939441876,50.0,50.771330594287235)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.266044443118978,0.7309511000407727 l 0.1736481776669308,-0.984807753012208 l -0.49999999999999956,-0.8660254037844388 l -0.9396926207859082,-0.3420201433256692 l -0.9396926207859086,0.3420201433256681 l -0.5000000000000006,0.8660254037844384 l 0.17364817766692983,0.9848077530122082 l 0.766044443118978,0.6427876096865394 l 1.0,-2.7755575615628914e-17 l 0.7660444431189786,-0.6427876096865375 Z" /></g></g></g></svg>
− diagrams/octagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="99.99999999999999" font-size="1" viewBox="0 0 100 100"><g><g transform="matrix(37.65577839755409,0.0,0.0,37.65577839755409,50.00000000000001,50.00000000000001)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.2071067811865475,0.4999999999999996 l -2.220446049250313e-16,-1.0 l -0.7071067811865476,-0.7071067811865475 l -1.0,1.6081226496766364e-16 l -0.7071067811865475,0.7071067811865476 l 9.957992501029599e-17,1.0 l 0.7071067811865476,0.7071067811865476 l 1.0,-3.834758505292834e-17 l 0.7071067811865477,-0.7071067811865477 Z" /></g></g></g></svg>
− diagrams/pentagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="95.10565162951538" font-size="1" viewBox="0 0 100 95"><g><g transform="matrix(56.18490806817227,0.0,0.0,56.18490806817227,50.00000000000001,52.573111211913364)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.5000000000000001,0.6881909602355868 l 0.3090169943749472,-0.9510565162951536 l -0.8090169943749475,-0.5877852522924729 l -0.8090169943749473,0.5877852522924731 l 0.3090169943749479,0.9510565162951533 l 0.9999999999999996,1.1102230246251565e-16 Z" /></g></g></g></svg>
− diagrams/rectEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="150.0" height="104.99999999999999" font-size="1" viewBox="0 0 150 105"><g><g transform="matrix(136.36363636363635,0.0,0.0,136.36363636363635,74.99999999999999,52.49999999999999)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.5000000000000001,0.35 l -1.1102230246251565e-16,-0.7 h -1.0 l -1.1102230246251565e-16,0.7 h 1.0 Z" /></g></g></g></svg>
− diagrams/roundedRectEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400.0" height="76.19047619047622" font-size="1" viewBox="0 0 400 76"><g><g transform="matrix(173.1601731601732,0.0,0.0,173.1601731601732,199.99999999999997,38.09523809523811)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.0499999999999998,0.10000000000000003 v -0.1 c -0.11045694996615868,-4.4408920985006264e-17 -0.19999999999999996,-8.954305003384136e-2 -0.19999999999999996 -0.20000000000000007h -0.29999999999999993 c -0.1104569499661587,-6.763537510981779e-18 -0.2,8.954305003384128e-2 -0.2 0.19999999999999996v 0.2 h 0.6 c 5.522847498307935e-2,1.014530626647267e-17 0.1,-4.477152501692063e-2 9.999999999999992e-2 -9.999999999999992e-2Z" /></g></g><g transform="matrix(173.1601731601732,0.0,0.0,173.1601731601732,199.99999999999997,38.09523809523811)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.15000000000000013,0.10000000000000003 v -0.20000000000000004 c -5.522847498307934e-2,-2.2204460492503132e-17 -9.999999999999998e-2,-4.477152501692068e-2 -9.999999999999998e-2 -0.10000000000000003h -0.3 c -2.2204460492503132e-17,5.5228474983079326e-2 -4.477152501692068e-2,9.999999999999998e-2 -0.10000000000000003 9.999999999999996e-2v 0.2 c 5.522847498307934e-2,2.2204460492503132e-17 9.999999999999998e-2,4.477152501692068e-2 9.999999999999996e-2 0.10000000000000003h 0.30000000000000004 c 0.0,-5.522847498307934e-2 4.477152501692068e-2,-9.999999999999998e-2 9.999999999999998e-2 -9.999999999999998e-2Z" /></g></g><g transform="matrix(173.1601731601732,0.0,0.0,173.1601731601732,199.99999999999997,38.09523809523811)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -0.5499999999999998,0.10000000000000003 v -0.20000000000000004 c 0.0,-5.522847498307935e-2 -4.4771525016920645e-2,-0.1 -9.999999999999998e-2 -0.1h -0.3 c -5.522847498307935e-2,-3.3817687554908897e-18 -0.1,4.477152501692064e-2 -0.1 9.999999999999998e-2v 0.2 c -6.763537510981779e-18,5.522847498307935e-2 4.477152501692064e-2,0.1 9.999999999999998e-2 0.10000000000000003h 0.30000000000000004 c 5.522847498307935e-2,1.014530626647267e-17 0.1,-4.477152501692063e-2 9.999999999999998e-2 -9.999999999999998e-2Z" /></g></g></g></svg>
− diagrams/septagonEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="97.49279121818235" font-size="1" viewBox="0 0 100 97"><g><g transform="matrix(40.45835162842079,0.0,0.0,40.45835162842079,50.0,51.28584316362768)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 1.1234898018587336,0.2564292158181373 l -0.22252093395631548,-0.9749279121818234 l -0.9009688679024197,-0.4338837391175572 l -0.9009688679024187,0.43388373911755906 l -0.22252093395631345,0.9749279121818237 l 0.6234898018587343,0.7818314824680291 l 1.0,-2.7755575615628914e-17 l 0.6234898018587329,-0.7818314824680312 Z" /></g></g></g></svg>
− diagrams/squareEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200.0" height="85.71428571428572" font-size="1" viewBox="0 0 200 86"><g><g transform="matrix(25.974025974025974,0.0,0.0,25.974025974025974,100.0,42.85714285714286)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="3.0e-2" font-size="1.0em"><path d="M 3.5000000000000004,1.5 l -3.3306690738754696e-16,-3.0 h -3.0 l -3.3306690738754696e-16,3.0 h 3.000000000000001 Z" /></g></g><g transform="matrix(25.974025974025974,0.0,0.0,25.974025974025974,100.0,42.85714285714286)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="3.0e-2" font-size="1.0em"><path d="M 2.220446049250313e-16,1.0 l -2.220446049250313e-16,-2.0 h -2.0 l -2.220446049250313e-16,2.0 h 2.0 Z" /></g></g><g transform="matrix(25.974025974025974,0.0,0.0,25.974025974025974,100.0,42.85714285714286)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="3.0e-2" font-size="1.0em"><path d="M -2.5,0.5 l -1.1102230246251565e-16,-1.0 h -1.0 l -1.1102230246251565e-16,1.0 h 1.0 Z" /></g></g></g></svg>
+ diagrams/src_Diagrams_Combinators_alignedEx1.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="151.7241" stroke-opacity="1" viewBox="0 0 400 152" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.9854108192823033" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 393.1034,34.4828 c 0.0000,-15.2354 -12.3508,-27.5862 -27.5862 -27.5862c -15.2354,-0.0000 -27.5862,12.3508 -27.5862 27.5862c -0.0000,15.2354 12.3508,27.5862 27.5862 27.5862c 15.2354,0.0000 27.5862,-12.3508 27.5862 -27.5862Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.9854108192823033" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 310.3448,75.8621 c 0.0000,-38.0886 -30.8769,-68.9655 -68.9655 -68.9655c -38.0886,-0.0000 -68.9655,30.8769 -68.9655 68.9655c -0.0000,38.0886 30.8769,68.9655 68.9655 68.9655c 38.0886,0.0000 68.9655,-30.8769 68.9655 -68.9655Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.9854108192823033" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 144.8276,48.2759 c 0.0000,-22.8532 -18.5261,-41.3793 -41.3793 -41.3793c -22.8532,-0.0000 -41.3793,18.5261 -41.3793 41.3793c -0.0000,22.8532 18.5261,41.3793 41.3793 41.3793c 22.8532,0.0000 41.3793,-18.5261 41.3793 -41.3793Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.9854108192823033" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 34.4828,20.6897 c 0.0000,-7.6177 -6.1754,-13.7931 -13.7931 -13.7931c -7.6177,-0.0000 -13.7931,6.1754 -13.7931 13.7931c -0.0000,7.6177 6.1754,13.7931 13.7931 13.7931c 7.6177,0.0000 13.7931,-6.1754 13.7931 -13.7931Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 28.4138,20.6897 c 0.0000,-4.2659 -3.4582,-7.7241 -7.7241 -7.7241c -4.2659,-0.0000 -7.7241,3.4582 -7.7241 7.7241c -0.0000,4.2659 3.4582,7.7241 7.7241 7.7241c 4.2659,0.0000 7.7241,-3.4582 7.7241 -7.7241Z"/></g></svg>
+ diagrams/src_Diagrams_Combinators_alignedEx2.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="400.0000" stroke-opacity="1" viewBox="0 0 400 400" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.5999999999999999" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 256.1849,297.9712 l 56.1849,-172.9194 l -147.0940,-106.8700 l -147.0940,106.8700 l 56.1849,172.9194 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.5999999999999999" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 200.0000,175.6410 l -90.9091,-157.4592 l -90.9091,157.4592 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.5999999999999999" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 200.0000,200.0000 l -0.0000,-181.8182 h -181.8182 l -0.0000,181.8182 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.5999999999999999" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 381.8182,200.0000 c 0.0000,-100.4154 -81.4028,-181.8182 -181.8182 -181.8182c -100.4154,-0.0000 -181.8182,81.4028 -181.8182 181.8182c -0.0000,100.4154 81.4028,181.8182 181.8182 181.8182c 100.4154,0.0000 181.8182,-81.4028 181.8182 -181.8182Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 207.2727,200.0000 c 0.0000,-4.0166 -3.2561,-7.2727 -7.2727 -7.2727c -4.0166,-0.0000 -7.2727,3.2561 -7.2727 7.2727c -0.0000,4.0166 3.2561,7.2727 7.2727 7.2727c 4.0166,0.0000 7.2727,-3.2561 7.2727 -7.2727Z"/></g></svg>
+ diagrams/src_Diagrams_Combinators_appendsEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="182.1560" stroke-opacity="1" viewBox="0 0 200 182" font-size="1" width="200.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 160.6111,143.5732 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 99.9949,143.5732 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 69.6970,91.0780 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 99.9949,38.5828 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 160.6111,38.5828 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 190.9091,91.0780 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7634783583493163" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 130.3030,91.0780 c 0.0000,-16.7359 -13.5671,-30.3030 -30.3030 -30.3030c -16.7359,-0.0000 -30.3030,13.5671 -30.3030 30.3030c -0.0000,16.7359 13.5671,30.3030 30.3030 30.3030c 16.7359,0.0000 30.3030,-13.5671 30.3030 -30.3030Z"/></g></svg>
+ diagrams/src_Diagrams_Combinators_besideEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="200.0000" stroke-opacity="1" viewBox="0 0 170 200" font-size="1" width="169.7207" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.7369573772655632" fill="rgb(128,0,128)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 162.0061,68.6338 c 0.0000,-32.8847 -26.6583,-59.5429 -59.5429 -59.5429c -32.8847,-0.0000 -59.5429,26.6583 -59.5429 59.5429c -0.0000,32.8847 26.6583,59.5429 59.5429 59.5429c 32.8847,0.0000 59.5429,-26.6583 59.5429 -59.5429Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.7369573772655632" fill="rgb(255,165,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 87.1052,151.2138 c 0.0000,-21.9231 -17.7722,-39.6953 -39.6953 -39.6953c -21.9231,-0.0000 -39.6953,17.7722 -39.6953 39.6953c -0.0000,21.9231 17.7722,39.6953 39.6953 39.6953c 21.9231,0.0000 39.6953,-17.7722 39.6953 -39.6953Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 51.0462,151.2138 c 0.0000,-2.0083 -1.6281,-3.6364 -3.6364 -3.6364c -2.0083,-0.0000 -3.6364,1.6281 -3.6364 3.6364c -0.0000,2.0083 1.6281,3.6364 3.6364 3.6364c 2.0083,0.0000 3.6364,-1.6281 3.6364 -3.6364Z"/></g></svg>
+ diagrams/src_Diagrams_Combinators_positionEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="300.0000" stroke-opacity="1" viewBox="0 0 204 300" font-size="1" width="204.2553" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 204.2553,6.3830 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 197.8723,43.4043 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 191.4894,77.8723 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 185.1064,109.7872 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 178.7234,139.1489 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 172.3404,165.9574 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 165.9574,190.2128 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 159.5745,211.9149 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 153.1915,231.0638 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 146.8085,247.6596 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 140.4255,261.7021 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 134.0426,273.1915 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 127.6596,282.1277 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 121.2766,288.5106 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 114.8936,292.3404 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 108.5106,293.6170 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 102.1277,292.3404 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.7447,288.5106 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 89.3617,282.1277 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 82.9787,273.1915 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 76.5957,261.7021 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 70.2128,247.6596 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 63.8298,231.0638 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 57.4468,211.9149 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 51.0638,190.2128 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 44.6809,165.9574 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 38.2979,139.1489 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 31.9149,109.7872 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 25.5319,77.8723 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 19.1489,43.4043 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.9901643964084406" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 12.7660,6.3830 c 0.0000,-3.5252 -2.8578,-6.3830 -6.3830 -6.3830c -3.5252,-0.0000 -6.3830,2.8578 -6.3830 6.3830c -0.0000,3.5252 2.8578,6.3830 6.3830 6.3830c 3.5252,0.0000 6.3830,-2.8578 6.3830 -6.3830Z"/></g></svg>
+ diagrams/src_Diagrams_Combinators_strutEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="120.0000" stroke-opacity="1" viewBox="0 0 300 120" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.758946638440411" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,60.0000 c 0.0000,-30.1246 -24.4208,-54.5455 -54.5455 -54.5455c -30.1246,-0.0000 -54.5455,24.4208 -54.5455 54.5455c -0.0000,30.1246 24.4208,54.5455 54.5455 54.5455c 30.1246,0.0000 54.5455,-24.4208 54.5455 -54.5455Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.758946638440411" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 122.7273,60.0000 c 0.0000,-30.1246 -24.4208,-54.5455 -54.5455 -54.5455c -30.1246,-0.0000 -54.5455,24.4208 -54.5455 54.5455c -0.0000,30.1246 24.4208,54.5455 54.5455 54.5455c 30.1246,0.0000 54.5455,-24.4208 54.5455 -54.5455Z"/></g></svg>
+ diagrams/src_Diagrams_Combinators_withEnvelopeEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="166.6667" stroke-opacity="1" viewBox="0 0 300 167" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.8944271909999159" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 261.1111,138.8889 l -0.0000,-111.1111 h -111.1111 l -0.0000,111.1111 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,255,255)" stroke-width="0.8944271909999159" fill="rgb(0,0,0)" stroke-dashoffset="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-dasharray="5.555555555555557,5.555555555555557"><path d="M 250.0000,83.3333 c 0.0000,-24.5460 -19.8985,-44.4444 -44.4444 -44.4444c -24.5460,-0.0000 -44.4444,19.8985 -44.4444 44.4444c -0.0000,24.5460 19.8985,44.4444 44.4444 44.4444c 24.5460,0.0000 44.4444,-19.8985 44.4444 -44.4444Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.8944271909999159" fill="rgb(0,128,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 161.1111,83.3333 c 0.0000,-30.6825 -24.8731,-55.5556 -55.5556 -55.5556c -30.6825,-0.0000 -55.5556,24.8731 -55.5556 55.5556c -0.0000,30.6825 24.8731,55.5556 55.5556 55.5556c 30.6825,0.0000 55.5556,-24.8731 55.5556 -55.5556Z"/></g></svg>
+ diagrams/src_Diagrams_CubicSpline_Boehm_bsplineEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="184.6154" stroke-opacity="1" viewBox="0 0 300 185" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.9413574486632834" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 135.5769,106.7308 c 0.0000,-0.0000 0.0000,-0.0000 9.6154 -14.4231c 9.6154,-14.4231 28.8462,-43.2692 52.8846 -33.6538c 24.0385,9.6154 52.8846,57.6923 24.0385 67.3077c -28.8462,9.6154 -115.3846,-19.2308 -139.4231 -43.2692c -24.0385,-24.0385 14.4231,-43.2692 33.6538 -52.8846c 19.2308,-9.6154 19.2308,-9.6154 19.2308 -9.6154"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 141.3462,20.1923 c 0.0000,-3.1863 -2.5830,-5.7692 -5.7692 -5.7692c -3.1863,-0.0000 -5.7692,2.5830 -5.7692 5.7692c -0.0000,3.1863 2.5830,5.7692 5.7692 5.7692c 3.1863,0.0000 5.7692,-2.5830 5.7692 -5.7692Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 25.9615,77.8846 c 0.0000,-3.1863 -2.5830,-5.7692 -5.7692 -5.7692c -3.1863,-0.0000 -5.7692,2.5830 -5.7692 5.7692c -0.0000,3.1863 2.5830,5.7692 5.7692 5.7692c 3.1863,0.0000 5.7692,-2.5830 5.7692 -5.7692Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 285.5769,164.4231 c 0.0000,-3.1863 -2.5830,-5.7692 -5.7692 -5.7692c -3.1863,-0.0000 -5.7692,2.5830 -5.7692 5.7692c -0.0000,3.1863 2.5830,5.7692 5.7692 5.7692c 3.1863,0.0000 5.7692,-2.5830 5.7692 -5.7692Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 199.0385,20.1923 c 0.0000,-3.1863 -2.5830,-5.7692 -5.7692 -5.7692c -3.1863,-0.0000 -5.7692,2.5830 -5.7692 5.7692c -0.0000,3.1863 2.5830,5.7692 5.7692 5.7692c 3.1863,0.0000 5.7692,-2.5830 5.7692 -5.7692Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 141.3462,106.7308 c 0.0000,-3.1863 -2.5830,-5.7692 -5.7692 -5.7692c -3.1863,-0.0000 -5.7692,2.5830 -5.7692 5.7692c -0.0000,3.1863 2.5830,5.7692 5.7692 5.7692c 3.1863,0.0000 5.7692,-2.5830 5.7692 -5.7692Z"/></g></svg>
+ diagrams/src_Diagrams_CubicSpline_cubicSplineEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="150.9864" stroke-opacity="1" viewBox="0 0 600 151" font-size="1" width="600.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.2039392872038202" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 434.1254,88.5075 c -6.8945,-6.8945 6.8945,-85.0326 50.5599 -75.8399c 43.6654,9.1927 117.2071,105.7162 75.8399 126.3998c -41.3672,20.6836 -197.6433,-34.4727 -227.5197 -75.8399c -29.8763,-41.3672 66.6472,-68.9454 101.1199 -50.5599c 34.4727,18.3854 6.8945,82.7344 -0.0000 75.8399Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 439.1814,12.6676 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 338.0615,63.2275 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 565.5812,139.0674 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 489.7413,12.6676 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 439.1814,88.5075 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.2039392872038202" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 139.8914,88.5075 c 5.4171,-48.3028 10.8343,-96.6056 50.5599 -75.8399c 39.7257,20.7657 113.7598,110.5998 75.8399 126.3998c -37.9199,15.8000 -187.7940,-42.4342 -227.5197 -75.8399c -39.7257,-33.4057 30.6971,-41.9828 101.1199 -50.5599"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 144.9474,12.6676 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 43.8275,63.2275 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 271.3472,139.0674 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 195.5073,12.6676 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 144.9474,88.5075 c 0.0000,-2.7923 -2.2636,-5.0560 -5.0560 -5.0560c -2.7923,-0.0000 -5.0560,2.2636 -5.0560 5.0560c -0.0000,2.7923 2.2636,5.0560 5.0560 5.0560c 2.7923,0.0000 5.0560,-2.2636 5.0560 -5.0560Z"/></g></svg>
+ diagrams/src_Diagrams_TrailLike_explodeTrailEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="285.3170" stroke-opacity="1" viewBox="0 0 300 285" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="1.17026551836112" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 65.7226,272.3480 l 168.5547,-0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.17026551836112" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,112.0429 l 52.0863,160.3051 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,255,0)" stroke-width="1.17026551836112" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 150.0000,12.9690 l -136.3636,99.0740 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="1.17026551836112" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,112.0429 l -136.3636,-99.0740 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,165,0)" stroke-width="1.17026551836112" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 234.2774,272.3480 l 52.0863,-160.3051 "/></g></svg>
+ diagrams/src_Diagrams_TrailLike_fromOffsetsEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="86.6025" stroke-opacity="1" viewBox="0 0 300 87" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.644741959094125" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,82.6661 h 90.9091 l 45.4545,-78.7296 l 45.4545,78.7296 h 90.9091 "/></g></svg>
+ diagrams/src_Diagrams_TrailLike_fromSegmentsEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="200.0000" stroke-opacity="1" viewBox="0 0 247 200" font-size="1" width="247.4874" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.8899211172508118" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 11.2494,190.9091 l 128.5649,-128.5649 c 128.5649,-128.5649 128.5649,-0.0000 0.0000 128.5649h -128.5649 "/></g></svg>
+ diagrams/src_Diagrams_TrailLike_fromVerticesEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="305.0220" stroke-opacity="1" viewBox="0 0 300 305" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.2100023771913093" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 198.0223,226.8357 l 52.5796,-15.5396 l 35.7617,-48.1977 l -52.8414,-45.5203 l 1.4689,-54.8082 l -34.7877,-48.9053 l -59.6213,36.1886 l -51.6718,-18.3337 l -57.2617,17.9725 l 15.9934,67.8861 l -33.4039,43.4773 l -0.6019,60.0129 l 69.5057,5.7673 l 31.0270,45.2042 l 56.8897,19.1175 Z"/></g></svg>
+ diagrams/src_Diagrams_TrailLike_twiddleEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="259.8076" stroke-opacity="1" viewBox="0 0 300 260" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,247.9982 h 0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,247.9982 l -68.1818,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,247.9982 l 0.0000,-236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,247.9982 l 136.3636,-236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,247.9982 l 204.5455,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,247.9982 l 136.3636,0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,129.9038 l 68.1818,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,129.9038 h 0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,129.9038 l 68.1818,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,129.9038 l 204.5455,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,129.9038 l 272.7273,0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,129.9038 l 204.5455,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,11.8094 l -0.0000,236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,11.8094 l -68.1818,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,11.8094 h 0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,11.8094 l 136.3636,0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,11.8094 l 204.5455,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 81.8182,11.8094 l 136.3636,236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,11.8094 l -136.3636,236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,11.8094 l -204.5455,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,11.8094 l -136.3636,-0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,11.8094 h 0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,11.8094 l 68.1818,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,11.8094 l -0.0000,236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,129.9038 l -204.5455,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,129.9038 l -272.7273,-0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,129.9038 l -204.5455,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,129.9038 l -68.1818,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,129.9038 h 0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,129.9038 l -68.1818,118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,247.9982 l -136.3636,-0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,247.9982 l -204.5455,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,247.9982 l -136.3636,-236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,247.9982 l 0.0000,-236.1887 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,247.9982 l 68.1818,-118.0944 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.1167258309225196" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 218.1818,247.9982 h 0.0000 "/></g></svg>
+ diagrams/src_Diagrams_Trail_closeLineEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="68.1818" stroke-opacity="1" viewBox="0 0 500 68" font-size="1" width="500.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7385489458759964" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 394.6281,3.0992 l 82.6446,41.3223 l -123.9669,20.6612 l -82.6446,-41.3223 l 41.3223,-20.6612 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7385489458759964" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 146.6942,3.0992 l 82.6446,41.3223 l -123.9669,20.6612 l -82.6446,-41.3223 l 41.3223,-20.6612 "/></g></svg>
+ diagrams/src_Diagrams_Trail_glueLineEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="68.1818" stroke-opacity="1" viewBox="0 0 500 68" font-size="1" width="500.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7385489458759964" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 384.2975,0.0000 l 82.6446,41.3223 l -123.9669,20.6612 l -82.6446,-41.3223 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7385489458759964" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 136.3636,0.0000 l 82.6446,41.3223 l -123.9669,20.6612 l -82.6446,-41.3223 l 41.3223,-20.6612 "/></g></svg>
+ diagrams/src_Diagrams_Trail_lineFromOffsetsEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="50.0000" stroke-opacity="1" viewBox="0 0 300 50" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 0.0000,50.0000 l 100.0000,-50.0000 l 100.0000,50.0000 l 100.0000,-25.0000 "/></g></svg>
+ diagrams/src_Diagrams_Trail_lineFromVerticesEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="120.0000" stroke-opacity="1" viewBox="0 0 300 120" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.7589466384404111" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,114.5455 v -54.5455 l 54.5455,-54.5455 l 218.1818,54.5455 "/></g></svg>
+ diagrams/src_Diagrams_Trail_trailOffsetEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="90.0000" stroke-opacity="1" viewBox="0 0 300 90" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="0.6572670690061994" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 177.2727,4.0909 h -109.0909 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.6572670690061994" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 177.2727,4.0909 l 109.0909,54.5455 l -163.6364,27.2727 l -109.0909,-54.5455 l 54.5455,-27.2727 "/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arc_annularWedgeEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="178.5413" stroke-opacity="1" viewBox="0 0 400 179" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="1.0689548060277785" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 364.6026,49.1012 l 17.2156,-17.2156 c -31.6930,-31.6930 -83.0775,-31.6930 -114.7704 -0.0000c -31.6930,31.6930 -31.6930,83.0775 -0.0000 114.7704c 31.6930,31.6930 83.0775,31.6930 114.7704 0.0000l -17.2156,-17.2156 c -22.1851,22.1851 -58.1542,22.1851 -80.3393 -0.0000c -22.1851,-22.1851 -22.1851,-58.1542 0.0000 -80.3393c 22.1851,-22.1851 58.1542,-22.1851 80.3393 -0.0000Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="1.0689548060277785" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 196.7624,65.0577 l 5.9381,-56.4973 c -22.8740,-2.4042 -45.6939,5.0105 -62.7863 20.4005l 38.0123,42.2169 c 5.1277,-4.6170 11.9737,-6.8414 18.8359 -6.1201Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="1.0689548060277785" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 58.7593,89.2708 h 40.5775 c 0.0000,-44.8206 -36.3343,-81.1550 -81.1550 -81.1550l -0.0000,40.5775 c 22.4103,0.0000 40.5775,18.1672 40.5775 40.5775Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arc_arc'Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="300.0000" stroke-opacity="1" viewBox="0 0 300 300" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.2" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 286.3636,286.3636 c 0.0000,-150.6231 -122.1042,-272.7273 -272.7273 -272.7273"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.2" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 195.4545,286.3636 c 0.0000,-100.4154 -81.4028,-181.8182 -181.8182 -181.8182"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.2" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 104.5455,286.3636 c 0.0000,-50.2077 -40.7014,-90.9091 -90.9091 -90.9091"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arc_arcBetweenEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="150.0000" stroke-opacity="1" viewBox="0 0 300 150" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.8485281374238577" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,143.1818 c 71.7377,-76.5246 168.4650,-124.8883 272.7273 -136.3636"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.8485281374238577" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,143.1818 c 82.0507,-61.3532 174.4143,-107.5350 272.7273 -136.3636"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.8485281374238577" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,143.1818 l 272.7273,-136.3636 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.8485281374238577" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,143.1818 c 98.3130,-28.8286 190.6766,-75.0104 272.7273 -136.3636"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.8485281374238577" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,143.1818 c 104.2623,-11.4754 200.9896,-59.8390 272.7273 -136.3636"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arc_wedgeEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="178.5413" stroke-opacity="1" viewBox="0 0 400 179" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="1.0689548060277785" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 324.4330,89.2708 l 57.3852,-57.3852 c -31.6930,-31.6930 -83.0775,-31.6930 -114.7704 -0.0000c -31.6930,31.6930 -31.6930,83.0775 -0.0000 114.7704c 31.6930,31.6930 83.0775,31.6930 114.7704 0.0000Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="1.0689548060277785" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 194.2175,89.2708 l 8.4830,-80.7104 c -22.8740,-2.4042 -45.6939,5.0105 -62.7863 20.4005Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="1.0689548060277785" fill="rgb(0,0,255)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 18.1818,89.2708 h 81.1550 c 0.0000,-44.8206 -36.3343,-81.1550 -81.1550 -81.1550Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrow_example1.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="142.8571" stroke-opacity="1" viewBox="0 0 500 143" font-size="1" width="500.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(169,169,169)" stroke-width="5.345224838248488" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 461.0390,136.3636 l -0.0000,-129.8701 h -129.8701 l -0.0000,129.8701 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(169,169,169)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 398.7013,71.4286 c 0.0000,-1.4345 -1.1629,-2.5974 -2.5974 -2.5974c -1.4345,-0.0000 -2.5974,1.1629 -2.5974 2.5974c -0.0000,1.4345 1.1629,2.5974 2.5974 2.5974c 1.4345,0.0000 2.5974,-1.1629 2.5974 -2.5974Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(169,169,169)" stroke-width="5.345224838248488" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 136.3636,136.3636 l -0.0000,-129.8701 h -129.8701 l -0.0000,129.8701 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(169,169,169)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 74.0260,71.4286 c 0.0000,-1.4345 -1.1629,-2.5974 -2.5974 -2.5974c -1.4345,-0.0000 -2.5974,1.1629 -2.5974 2.5974c -0.0000,1.4345 1.1629,2.5974 2.5974 2.5974c 1.4345,0.0000 2.5974,-1.1629 2.5974 -2.5974Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.0690449676496976" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 89.9820,73.8309 c 61.4068,7.9510 122.8137,15.9021 138.7131 10.9542c 15.8994,-4.9479 -13.7086,-22.7947 2.1908 -27.7426c 15.8994,-4.9479 77.3063,3.0032 138.7131 10.9542"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 89.9820,73.8309 l -7.4663,-10.2070 l -19.3265,-2.5024 l 8.2394,10.3071 l -10.5928,7.8686 l 19.3265,2.5024 l 9.8198,-7.9687 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 89.9820,73.8309 l 0.0686,-0.5301 l -0.5042,-0.0653 l 0.4355,0.5954 l -0.5728,0.4648 l 0.5042,0.0653 l 0.0686,-0.5301 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 396.1039,71.4286 l -30.2984,-14.3723 l 4.0722,10.9765 l -6.7336,9.5773 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 369.8777,68.0328 l -0.2787,-0.0361 l 0.0686,-0.5301 l 0.2101,0.5662 l -0.2787,-0.0361 l -0.0686,0.5301 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrow_example2.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="191.6094" stroke-opacity="1" viewBox="0 0 500 192" font-size="1" width="500.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.238093280317519" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 92.8161,95.8047 l 0.0000,47.2301 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 92.8161,153.8681 l 4.1176,-12.6726 l -4.1176,2.1647 l -4.1176,-2.1647 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 92.8161,143.3603 l -0.0000,-0.3255 l 0.6190,-0.0000 l -0.6190,0.3255 l -0.0000,-0.3255 l -0.6190,0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(169,169,169)" stroke-width="6.190466401587595" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 477.2727,124.8364 l -0.0000,-58.0634 l -41.0571,-41.0571 l -58.0634,0.0000 l -41.0571,41.0571 l 0.0000,58.0634 l 41.0571,41.0571 l 58.0634,-0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 409.9875,95.8047 c 0.0000,-1.5484 -1.2552,-2.8036 -2.8036 -2.8036c -1.5484,-0.0000 -2.8036,1.2552 -2.8036 2.8036c -0.0000,1.5484 1.2552,2.8036 2.8036 2.8036c 1.5484,0.0000 2.8036,-1.2552 2.8036 -2.8036Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(169,169,169)" stroke-width="6.190466401587595" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 162.9048,124.8364 l -0.0000,-58.0634 l -41.0571,-41.0571 l -58.0634,0.0000 l -41.0571,41.0571 l 0.0000,58.0634 l 41.0571,41.0571 l 58.0634,-0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.6196,95.8047 c 0.0000,-1.5484 -1.2552,-2.8036 -2.8036 -2.8036c -1.5484,-0.0000 -2.8036,1.2552 -2.8036 2.8036c -0.0000,1.5484 1.2552,2.8036 2.8036 2.8036c 1.5484,0.0000 2.8036,-1.2552 2.8036 -2.8036Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.238093280317519" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 92.8161,95.8047 h 292.7013 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 407.1839,95.8047 l -25.7377,-8.3627 l 4.3965,8.3627 l -4.3965,8.3627 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 385.8428,95.8047 h -0.3255 v -0.6190 l 0.3255,0.6190 h -0.3255 v 0.6190 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.238093280317519" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 162.9048,124.8364 l 152.5237,-0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 337.0952,124.8364 l -25.7377,-8.3627 l 4.3965,8.3627 l -4.3965,8.3627 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 315.7540,124.8364 l -0.3255,0.0000 l -0.0000,-0.6190 l 0.3255,0.6190 l -0.3255,0.0000 l 0.0000,0.6190 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_blockEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,49.2129 l -0.0000,-34.7894 h -45.4545 l -0.0000,34.7894 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_dart'Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 l 54.8187,-17.8117 l -9.3642,17.8117 l 9.3642,17.8117 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 0.0000 h -0.0000 h 0.0000 h 0.0000 h 0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_dartEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 100.0000,31.8182 l -54.8187,-17.8117 l 9.3642,17.8117 l -9.3642,17.8117 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h -0.0000 h 0.0000 h 0.0000 h 0.0000 h 0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_halfDart'Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 l 54.8187,-17.8117 l -9.3642,17.8117 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 0.0000 h -0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_halfDartEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 100.0000,31.8182 l -54.8187,-17.8117 l 9.3642,17.8117 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h -0.0000 h 0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_quillEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 l -21.1749,-22.2646 h -47.3485 l 23.0688,22.2646 l -23.0688,22.2646 h 47.3485 l 21.1749,-22.2646 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 0.0000 h 0.0000 h 0.0000 h 0.0000 h 0.0000 h 0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_spike'Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 l 54.8685,-22.7273 c -12.5519,12.5519 -12.5519,32.9026 0.0000 45.4545l -54.8685,-22.7273 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_spikeEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 100.0000,31.8182 l -54.8685,-22.7273 c 12.5519,12.5519 12.5519,32.9026 0.0000 45.4545l 54.8685,-22.7273 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_thorn'Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 c 30.3030,0.0000 51.7305,-21.4275 51.7305 -21.4275l -6.2760,21.4275 h -45.4545 c 30.3030,0.0000 51.7305,21.4275 51.7305 21.4275l -6.2760,-21.4275 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 0.0000 h -0.0000 h 0.0000 h 0.0000 h 0.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_thornEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 100.0000,31.8182 c -30.3030,0.0000 -51.7305,-21.4275 -51.7305 -21.4275l 6.2760,21.4275 h 45.4545 c -30.3030,0.0000 -51.7305,21.4275 -51.7305 21.4275l 6.2760,-21.4275 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h -0.0000 h 0.0000 h 0.0000 h 0.0000 h 0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_tri'Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 l 45.4545,-26.2432 v 52.4864 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_tri25Ex.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="120.0000" stroke-opacity="1" viewBox="0 0 120 120" font-size="1" width="120.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 105.0000,105.0000 l -0.0000,-90.0000 h -90.0000 l -0.0000,90.0000 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 15.0000,60.0000 h -0.0000 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 90.0000,60.0000 l -75.0000,-24.3690 v 48.7380 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Arrowheads_triEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="63.6364" stroke-opacity="1" viewBox="0 0 100 64" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 54.5455,54.5455 l -0.0000,-45.4545 h -45.4545 l -0.0000,45.4545 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,31.8182 h 45.4545 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 100.0000,31.8182 l -45.4545,-26.2432 v 52.4864 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"></g></svg>
+ diagrams/src_Diagrams_TwoD_Curvature_diagramA.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="200.0000" stroke-opacity="1" viewBox="0 0 400 200" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.9284730395996748" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 18.1818,175.1315 c 218.1818,-0.0000 145.4545,-181.8182 363.6364 -145.4545"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Curvature_diagramNeg.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="200.0000" stroke-opacity="1" viewBox="0 0 400 200" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.0285189544531599" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 281.1636,30.8406 l 37.7776,135.3699 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.0285189544531599" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 459.4836,166.2104 c 0.0000,-77.6194 -62.9230,-140.5424 -140.5424 -140.5424c -77.6194,-0.0000 -140.5424,62.9230 -140.5424 140.5424c -0.0000,77.6194 62.9230,140.5424 140.5424 140.5424c 77.6194,0.0000 140.5424,-62.9230 140.5424 -140.5424Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.9284730395996748" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 18.1818,175.1315 c 218.1818,-0.0000 145.4545,-181.8182 363.6364 -145.4545"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Curvature_diagramPos.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="200.0000" stroke-opacity="1" viewBox="0 0 400 200" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.0285189544531599" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 118.8364,156.5133 l -70.9117,-156.3695 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.0285189544531599" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 219.6217,0.1438 c 0.0000,-94.8257 -76.8714,-171.6971 -171.6971 -171.6971c -94.8257,-0.0000 -171.6971,76.8714 -171.6971 171.6971c -0.0000,94.8257 76.8714,171.6971 171.6971 171.6971c 94.8257,0.0000 171.6971,-76.8714 171.6971 -171.6971Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.9284730395996748" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 18.1818,175.1315 c 218.1818,-0.0000 145.4545,-181.8182 363.6364 -145.4545"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Curvature_diagramZero.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="200.0000" stroke-opacity="1" viewBox="0 0 400 200" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.0285189544531599" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 189.0182,101.1269 l -602.0741,-540.4758 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="1.0285189544531599" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 396.0221,-439.3488 c 0.0000,-446.8415 -362.2366,-809.0780 -809.0780 -809.0780c -446.8415,-0.0000 -809.0780,362.2366 -809.0780 809.0780c -0.0000,446.8415 362.2366,809.0780 809.0780 809.0780c 446.8415,0.0000 809.0780,-362.2366 809.0780 -809.0780Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="1.9284730395996748" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 18.1818,175.1315 c 218.1818,-0.0000 145.4545,-181.8182 363.6364 -145.4545"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Offset_cubicOffsetExample.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="302.8588" stroke-opacity="1" viewBox="0 0 600 303" font-size="1" width="600.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 511.9818,129.7571 c 10.6062,25.0693 34.2055,55.8255 80.4872 86.6800"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 504.6600,99.6625 c 0.3347,8.2550 2.4134,18.4928 7.3218 30.0945"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 506.4937,85.2663 c -1.4641,2.9282 -2.1083,7.6248 -1.8337 14.3963"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 506.2084,85.5927 c 0.0678,-0.0768 0.2226,-0.2011 0.2853 -0.3265"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 502.6579,87.9357 c 1.0997,-0.4144 2.4469,-1.0919 3.5505 -2.3430"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 494.0623,87.5838 c 2.9458,1.3739 5.3820,1.5628 8.5956 0.3519"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 489.6962,83.7630 c 1.2338,2.0880 2.8653,3.1208 4.3661 3.8208"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 489.2796,82.9440 c 0.0773,0.3280 0.3025,0.6259 0.4166 0.8190"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 489.5449,85.0947 c -0.0520,-0.8562 -0.1242,-1.5525 -0.2653 -2.1507"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 488.8865,96.1054 c 0.6286,-4.6097 0.8286,-8.2083 0.6583 -11.0108"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 441.8492,221.4998 c 31.9297,-63.8595 44.0440,-103.4426 47.0374 -125.3943"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 453.6159,227.3831 c 131.5557,-263.1115 -65.7779,-131.5557 131.5557 -0.0000"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 364.7622,85.9675 c 0.0000,15.3291 0.6082,77.3813 61.9247 138.6978"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 364.7419,83.1393 c 0.0179,0.7021 0.0203,1.6834 0.0203 2.8282"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 364.6602,81.5631 c 0.0351,0.4104 0.0661,0.9641 0.0817 1.5762"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 364.0520,78.3408 c 0.1986,0.6455 0.4965,1.9158 0.6082 3.2223"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 361.6159,73.7760 c 0.7783,0.9536 1.8212,2.5663 2.4361 4.5649"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 357.1897,70.2580 c 1.5342,0.7533 3.0492,1.8307 4.4262 3.5179"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 349.9173,68.9840 c 2.6564,-0.2922 4.8750,0.0970 7.2724 1.2741"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 344.5074,70.8187 c 1.8905,-1.1435 3.6952,-1.6462 5.4099 -1.8348"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 341.9037,72.8755 c 0.8843,-0.8971 1.7979,-1.5694 2.6036 -2.0568"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 339.9429,75.3936 c 0.6567,-1.0763 1.4089,-1.9582 1.9608 -2.5180"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 338.2153,79.1592 c 0.5976,-1.6435 1.3914,-3.2147 1.7276 -3.7656"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 331.0209,99.9717 c 4.0362,-11.0996 6.1854,-18.0376 7.1945 -20.8125"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 274.0621,228.0844 c 32.5251,-65.0502 48.8275,-105.7517 56.9588 -128.1127"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 285.8288,233.9677 c 131.5557,-263.1115 0.0000,-131.5557 131.5557 -0.0000"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 243.5067,119.7182 c -3.7564,1.2749 -8.5010,2.1111 -14.3292 2.1111"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 255.1371,112.1979 c -2.6842,3.0676 -6.3579,5.7309 -11.6304 7.5203"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 258.9677,87.6101 c 3.4875,9.2999 2.2725,17.6129 -3.8306 24.5878"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 220.9552,62.6292 c 17.7972,-0.0000 33.5631,13.1160 38.0124 24.9808"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 193.4371,72.6278 c 9.3825,-7.3588 18.6876,-9.9985 27.5182 -9.9985"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 170.9681,103.1924 c 6.7677,-14.7658 14.6246,-24.4122 22.4689 -30.5647"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 150.2440,240.2295 c 0.0000,-68.4467 8.9598,-111.3696 20.7241 -137.0371"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 163.3996,240.2295 c 0.0000,-263.1115 131.5557,-131.5557 65.7779 -131.5557"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 107.3604,38.6930 c 6.4325,-12.1696 14.7608,-20.4761 25.8873 -24.9267"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4239,75.6318 c 2.8371,-14.2991 6.6878,-27.0089 11.9365 -36.9388"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 83.9792,165.6872 c 4.1423,-31.4811 6.2669,-63.9592 11.4447 -90.0554"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,0,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 62.0575,244.9887 c 12.6186,-21.3546 18.0391,-49.7933 21.9218 -79.3015"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 6.5778,275.9369 c 26.3331,-0.0000 44.7818,-12.8442 55.4797 -30.9482"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.197105416628175" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 6.5778,289.0924 c 131.5557,-0.0000 65.7779,-236.8003 131.5557 -263.1115"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Offset_expandLoopExample.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="300.0000" stroke-opacity="1" viewBox="0 0 300 300" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,128,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 36.3636,286.3636 h 113.6364 c 6.0276,-0.0000 11.8084,-2.3945 16.0706 -6.6567l 113.6364,-113.6364 c 4.2622,-4.2622 6.6567,-10.0430 6.6567 -16.0706v -113.6364 c 0.0000,-12.5519 -10.1753,-22.7273 -22.7273 -22.7273c -6.0276,0.0000 -11.8084,2.3945 -16.0706 6.6567l -227.2727,227.2727 c -8.8756,8.8756 -8.8756,23.2657 -0.0000 32.1412c 4.2622,4.2622 10.0430,6.6567 16.0706 6.6567ZM 36.3636,240.9091 c -12.5519,0.0000 -22.7273,10.1753 -22.7273 22.7273c -0.0000,12.5519 10.1753,22.7273 22.7273 22.7273c 6.0276,-0.0000 11.8084,-2.3945 16.0706 -6.6567l 227.2727,-227.2727 c 8.8756,-8.8756 8.8756,-23.2657 0.0000 -32.1412c -8.8756,-8.8756 -23.2657,-8.8756 -32.1412 -0.0000c -4.2622,4.2622 -6.6567,10.0430 -6.6567 16.0706v 113.6364 c -0.0000,12.5519 10.1753,22.7273 22.7273 22.7273c 12.5519,0.0000 22.7273,-10.1753 22.7273 -22.7273c 0.0000,-12.5519 -10.1753,-22.7273 -22.7273 -22.7273c -6.0276,0.0000 -11.8084,2.3945 -16.0706 6.6567l -113.6364,113.6364 c -8.8756,8.8756 -8.8756,23.2657 0.0000 32.1412c 8.8756,8.8756 23.2657,8.8756 32.1412 -0.0000c 8.8756,-8.8756 8.8756,-23.2657 -0.0000 -32.1412c -4.2622,-4.2622 -10.0430,-6.6567 -16.0706 -6.6567Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,255,255)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 36.3636,263.6364 h 113.6364 l 113.6364,-113.6364 v -113.6364 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Offset_expandTrailExample.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="197.3830" stroke-opacity="1" viewBox="0 0 600 197" font-size="1" width="600.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="12.98696646675048px" stroke="rgb(0,0,0)" stroke-width="1.376545341172593" fill="rgb(0,0,0)" stroke-linecap="butt" font-family="Helvetica" stroke-miterlimit="10.0" font-weight="bold"><text dominant-baseline="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,481.8182,168.9306)" stroke="none" text-anchor="middle">LineCapSquare</text></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,128,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 416.8830,97.5017 h -25.9739 v 51.9479 h 25.9739 h 129.8697 c 14.3450,-0.0000 25.9739,-11.6289 25.9739 -25.9739c 0.0000,-6.0763 -2.1303,-11.9602 -6.0202 -16.6281l -64.9348,-77.9218 l -16.6281,-19.9537 l -39.9075,33.2562 l 16.6281,19.9537 l 64.9348,77.9218 c 9.1834,11.0201 25.5617,12.5091 36.5818 3.3256c 11.0201,-9.1834 12.5091,-25.5617 3.3256 -36.5818c -4.9349,-5.9219 -12.2452,-9.3458 -19.9537 -9.3458Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,255,255)" stroke-width="3.4413633529314827" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 416.8830,123.4756 h 129.8697 l -64.9348,-77.9218 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="12.98696646675048px" stroke="rgb(0,0,0)" stroke-width="1.376545341172593" fill="rgb(0,0,0)" stroke-linecap="butt" font-family="Helvetica" stroke-miterlimit="10.0" font-weight="bold"><text dominant-baseline="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,287.0130,163.6270)" stroke="none" text-anchor="middle">LineCapRound</text></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,128,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 222.0779,92.1980 c -14.3450,-0.0000 -25.9739,11.6289 -25.9739 25.9739c -0.0000,14.3450 11.6289,25.9739 25.9739 25.9739h 129.8697 c 14.3450,-0.0000 25.9739,-11.6289 25.9739 -25.9739c 0.0000,-6.0763 -2.1303,-11.9602 -6.0202 -16.6281l -64.9348,-77.9218 c -9.1834,-11.0201 -25.5617,-12.5091 -36.5818 -3.3256c -11.0201,9.1834 -12.5091,25.5617 -3.3256 36.5818l 64.9348,77.9218 c 9.1834,11.0201 25.5617,12.5091 36.5818 3.3256c 11.0201,-9.1834 12.5091,-25.5617 3.3256 -36.5818c -4.9349,-5.9219 -12.2452,-9.3458 -19.9537 -9.3458Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,255,255)" stroke-width="3.4413633529314827" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 222.0779,118.1720 h 129.8697 l -64.9348,-77.9218 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="12.98696646675048px" stroke="rgb(0,0,0)" stroke-width="1.376545341172593" fill="rgb(0,0,0)" stroke-linecap="butt" font-family="Helvetica" stroke-miterlimit="10.0" font-weight="bold"><text dominant-baseline="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,105.1949,158.9538)" stroke="none" text-anchor="middle">LineCapButt</text></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(0,128,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 27.2727,87.5248 v 51.9479 h 129.8697 c 14.3450,-0.0000 25.9739,-11.6289 25.9739 -25.9739c 0.0000,-6.0763 -2.1303,-11.9602 -6.0202 -16.6281l -64.9348,-77.9218 l -39.9075,33.2562 l 64.9348,77.9218 c 9.1834,11.0201 25.5617,12.5091 36.5818 3.3256c 11.0201,-9.1834 12.5091,-25.5617 3.3256 -36.5818c -4.9349,-5.9219 -12.2452,-9.3458 -19.9537 -9.3458Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(255,255,255)" stroke-width="3.4413633529314827" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 27.2727,113.4987 h 129.8697 l -64.9348,-77.9218 "/></g></svg>
+ diagrams/src_Diagrams_TwoD_Offset_offsetTrailExample.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="185.1008" stroke-opacity="1" viewBox="0 0 600 185" font-size="1" width="600.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="13.702641330909062px" stroke="rgb(0,0,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" font-family="Helvetica" stroke-miterlimit="10.0" font-weight="bold"><text dominant-baseline="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,493.6874,156.1332)" stroke="none" text-anchor="middle">LineJoinBevel</text></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 414.6475,135.5792 h 137.0264 l 21.0533,-44.9497 l -68.5132,-82.2158 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 414.6475,108.1740 h 137.0264 l -68.5132,-82.2158 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="13.702641330909062px" stroke="rgb(0,0,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" font-family="Helvetica" stroke-miterlimit="10.0" font-weight="bold"><text dominant-baseline="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,318.7290,156.1332)" stroke="none" text-anchor="middle">LineJoinRound</text></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 236.5132,135.5792 h 137.0264 c 15.1355,-0.0000 27.4053,-12.2698 27.4053 -27.4053c 0.0000,-6.4111 -2.2477,-12.6193 -6.3520 -17.5444l -68.5132,-82.2158 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 236.5132,108.1740 h 137.0264 l -68.5132,-82.2158 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="13.702641330909062px" stroke="rgb(0,0,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" font-family="Helvetica" stroke-miterlimit="10.0" font-weight="bold"><text dominant-baseline="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,125.0416,156.1332)" stroke="none" text-anchor="middle">LineJoinMiter</text></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 27.2727,135.5792 h 137.0264 h 58.5114 l -37.4581,-44.9497 l -68.5132,-82.2158 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 27.2727,108.1740 h 137.0264 l -68.5132,-82.2158 "/></g></svg>
+ diagrams/src_Diagrams_TwoD_Offset_offsetTrailLeftExample.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="154.6722" stroke-opacity="1" viewBox="0 0 200 155" font-size="1" width="200.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,7.0306 h 151.5145 c 7.0890,0.0000 13.9536,2.4853 19.3994 7.0236c 12.8568,10.7140 14.5939,29.8220 3.8799 42.6788l -75.7573,90.9087 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 9.0909,37.3335 h 151.5145 l -75.7573,90.9087 "/></g></svg>
+ diagrams/src_Diagrams_TwoD_Offset_offsetTrailOuterExample.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="271.2912" stroke-opacity="1" viewBox="0 0 300 271" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,128,0)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 232.4425,239.4792 l 48.7013,-84.3531 c 6.9597,-12.0546 6.9597,-26.9065 -0.0000 -38.9610l -48.7013,-84.3531 c -6.9597,-12.0546 -19.8218,-19.4805 -33.7412 -19.4805h -97.4026 c -13.9194,-0.0000 -26.7815,7.4259 -33.7412 19.4805l -48.7013,84.3531 c -6.9597,12.0546 -6.9597,26.9065 -0.0000 38.9610l 48.7013,84.3531 c 6.9597,12.0546 19.8218,19.4805 33.7412 19.4805l 97.4026,-0.0000 c 13.9194,-0.0000 26.7815,-7.4259 33.7412 -19.4805Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,255)" stroke-width="3.0" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 198.7013,219.9987 l 48.7013,-84.3531 l -48.7013,-84.3531 h -97.4026 l -48.7013,84.3531 l 48.7013,84.3531 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_decagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="95.1057" stroke-opacity="1" viewBox="0 0 100 95" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 86.7735,74.2703 l 8.6810,-26.7175 l -8.6810,-26.7175 l -22.7273,-16.5123 l -28.0925,0.0000 l -22.7273,16.5123 l -8.6810,26.7175 l 8.6810,26.7175 l 22.7273,16.5123 l 28.0925,-0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_dodecagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="100.0000" stroke-opacity="1" viewBox="0 0 100 100" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,62.1795 l -0.0000,-24.3590 l -12.1795,-21.0955 l -21.0955,-12.1795 l -24.3590,0.0000 l -21.0955,12.1795 l -12.1795,21.0955 l -0.0000,24.3590 l 12.1795,21.0955 l 21.0955,12.1795 l 24.3590,-0.0000 l 21.0955,-12.1795 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_hendecagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="98.9821" stroke-opacity="1" viewBox="0 0 100 99" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,57.0495 l -3.6825,-25.6121 l -16.9448,-19.5553 l -24.8273,-7.2899 l -24.8273,7.2899 l -16.9448,19.5553 l -3.6825,25.6121 l 10.7490,23.5371 l 21.7678,13.9893 l 25.8754,-0.0000 l 21.7678,-13.9893 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_heptagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="97.4928" stroke-opacity="1" viewBox="0 0 100 97" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,61.6605 l -9.0028,-39.4440 l -36.4517,-17.5542 l -36.4517,17.5542 l -9.0028,39.4440 l 25.2254,31.6316 l 40.4584,-0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_hexagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="86.6025" stroke-opacity="1" viewBox="0 0 100 87" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 72.7273,82.6661 l 22.7273,-39.3648 l -22.7273,-39.3648 l -45.4545,-0.0000 l -22.7273,39.3648 l 22.7273,39.3648 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_hruleEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="48.0000" stroke-opacity="1" viewBox="0 0 300 48" font-size="1" width="300.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 13.6364,45.8182 h 272.7273 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 40.9091,34.9091 h 218.1818 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 68.1818,24.0000 h 163.6364 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,13.0909 h 109.0909 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 122.7273,2.1818 h 54.5455 "/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_nonagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="98.4808" stroke-opacity="1" viewBox="0 0 100 98" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 89.9721,73.8492 l 5.4825,-31.0927 l -15.7862,-27.3425 l -29.6683,-10.7984 l -29.6683,10.7984 l -15.7862,27.3425 l 5.4825,31.0927 l 24.1859,20.2943 l 31.5724,0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_octagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="100.0000" stroke-opacity="1" viewBox="0 0 100 100" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,68.8279 l -0.0000,-37.6558 l -26.6267,-26.6267 l -37.6558,0.0000 l -26.6267,26.6267 l 0.0000,37.6558 l 26.6267,26.6267 l 37.6558,-0.0000 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_pentagonEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="95.1057" stroke-opacity="1" viewBox="0 0 100 95" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 78.0925,91.2391 l 17.3621,-53.4350 l -45.4545,-33.0247 l -45.4545,33.0247 l 17.3621,53.4350 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_rectEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="105.0000" stroke-opacity="1" viewBox="0 0 150 105" font-size="1" width="150.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5019960159204453" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 143.1818,100.2273 l -0.0000,-95.4545 h -136.3636 l -0.0000,95.4545 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_roundedRectEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="76.1905" stroke-opacity="1" viewBox="0 0 400 76" font-size="1" width="400.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.6982972487551757" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 381.8182,55.4113 v -17.3160 c -19.1267,-0.0000 -34.6320,-15.5053 -34.6320 -34.6320h -51.9481 c -19.1267,-0.0000 -34.6320,15.5053 -34.6320 34.6320v 34.6320 h 103.8961 c 9.5634,0.0000 17.3160,-7.7526 17.3160 -17.3160Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.6982972487551757" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 225.9740,55.4113 v -34.6320 c -9.5634,-0.0000 -17.3160,-7.7526 -17.3160 -17.3160h -51.9481 c -0.0000,9.5634 -7.7526,17.3160 -17.3160 17.3160v 34.6320 c 9.5634,0.0000 17.3160,7.7526 17.3160 17.3160h 51.9481 c 0.0000,-9.5634 7.7526,-17.3160 17.3160 -17.3160Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.6982972487551757" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 104.7619,55.4113 v -34.6320 c 0.0000,-9.5634 -7.7526,-17.3160 -17.3160 -17.3160h -51.9481 c -9.5634,-0.0000 -17.3160,7.7526 -17.3160 17.3160v 34.6320 c -0.0000,9.5634 7.7526,17.3160 17.3160 17.3160h 51.9481 c 9.5634,0.0000 17.3160,-7.7526 17.3160 -17.3160Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_squareEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="85.7143" stroke-opacity="1" viewBox="0 0 200 86" font-size="1" width="200.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5237229365663817" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 190.9091,81.8182 l -0.0000,-77.9221 h -77.9221 l -0.0000,77.9221 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5237229365663817" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 100.0000,68.8312 l -0.0000,-51.9481 h -51.9481 l -0.0000,51.9481 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5237229365663817" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 35.0649,55.8442 l -0.0000,-25.9740 h -25.9740 l -0.0000,25.9740 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_triangleEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="86.6025" stroke-opacity="1" viewBox="0 0 100 87" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,83.9782 l -45.4545,-78.7296 l -45.4545,78.7296 Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_unitSquareEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="100.0000" stroke-opacity="1" viewBox="0 0 100 100" font-size="1" width="100.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 95.4545,95.4545 l -0.0000,-90.9091 h -90.9091 l -0.0000,90.9091 Z"/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" stroke="rgb(0,0,0)" stroke-width="0.0" fill="rgb(255,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 52.0000,50.0000 c 0.0000,-1.1046 -0.8954,-2.0000 -2.0000 -2.0000c -1.1046,-0.0000 -2.0000,0.8954 -2.0000 2.0000c -0.0000,1.1046 0.8954,2.0000 2.0000 2.0000c 1.1046,0.0000 2.0000,-0.8954 2.0000 -2.0000Z"/></g></svg>
+ diagrams/src_Diagrams_TwoD_Shapes_vruleEx.svg view
@@ -0,0 +1,1 @@+<svg xmlns="http://www.w3.org/2000/svg" height="100.0000" stroke-opacity="1" viewBox="0 0 50 100" font-size="1" width="50.0000" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="rgb(0,0,0)" version="1.1"><defs></defs><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 47.7273,4.5455 v 90.9091 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 38.6364,9.0909 v 81.8182 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 29.5455,13.6364 v 72.7273 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 20.4545,18.1818 v 63.6364 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 11.3636,22.7273 v 54.5455 "/></g><g stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0" stroke="rgb(0,0,0)" stroke-width="0.5" fill="rgb(0,0,0)" stroke-linecap="butt" stroke-miterlimit="10.0"><path d="M 2.2727,27.2727 v 45.4545 "/></g></svg>
− diagrams/trailOffsetEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="300.0" height="90.00000000000001" font-size="1" viewBox="0 0 300 90"><g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,45.00000000000001)"><g stroke="rgb(255,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="5.0e-2" font-size="1.0em"><path d="M 0.5,-0.75 h -2.0 " /></g></g><g transform="matrix(54.54545454545455,0.0,0.0,54.54545454545455,150.0,45.00000000000001)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.5,-0.75 l 2.0,1.0 l -3.0,0.5 l -2.0,-1.0 l 1.0,-0.5 " /></g></g></g></svg>
− diagrams/triangleEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="86.60254037844385" font-size="1" viewBox="0 0 100 87"><g><g transform="matrix(90.9090909090909,0.0,0.0,90.9090909090909,50.0,57.73502691896257)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.5,0.28867513459481275 l -0.5000000000000002,-0.8660254037844385 l -0.4999999999999999,0.8660254037844386 l 1.0,-1.1102230246251565e-16 Z" /></g></g></g></svg>
− diagrams/unitSquareEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100.0" height="100.0" font-size="1" viewBox="0 0 100 100"><g><g transform="matrix(90.9090909090909,0.0,0.0,90.9090909090909,49.99999999999999,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.5000000000000001,0.5 l -1.1102230246251565e-16,-1.0 h -1.0 l -1.1102230246251565e-16,1.0 h 1.0 Z" /></g></g><g transform="matrix(90.9090909090909,0.0,0.0,90.9090909090909,49.99999999999999,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.0" font-size="1.0em"><path d="M 2.2000000000000002e-2,0.0 c 0.0,-1.2150264496277458e-2 -9.849735503722543e-3,-2.2000000000000002e-2 -2.2e-2 -2.2000000000000002e-2c -1.2150264496277458e-2,-7.439891262079958e-19 -2.2000000000000002e-2,9.849735503722541e-3 -2.2000000000000002e-2 2.1999999999999995e-2c -1.4879782524159916e-18,1.2150264496277458e-2 9.84973550372254e-3,2.2000000000000002e-2 2.1999999999999992e-2 2.2000000000000002e-2c 1.2150264496277458e-2,2.2319673786239874e-18 2.2000000000000002e-2,-9.84973550372254e-3 2.2000000000000006e-2 -2.1999999999999995e-2Z" /></g></g></g></svg>
− diagrams/vruleEx.svg
@@ -1,4 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"-    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="50.00000000000001" height="100.0" font-size="1" viewBox="0 0 50 100"><g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,25.000000000000004,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.5000000000000001,-0.9999999999999999 v 1.9999999999999998 " /></g></g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,25.000000000000004,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.3000000000000001,-0.8999999999999999 v 1.7999999999999998 " /></g></g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,25.000000000000004,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M 0.10000000000000003,-0.7999999999999999 v 1.5999999999999999 " /></g></g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,25.000000000000004,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -9.999999999999998e-2,-0.7 v 1.4 " /></g></g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,25.000000000000004,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -0.3,-0.6 v 1.2 " /></g></g><g transform="matrix(45.45454545454546,0.0,0.0,45.45454545454546,25.000000000000004,50.0)"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="0.0" stroke-width="1.0e-2" font-size="1.0em"><path d="M -0.5,-0.5 v 1.0 " /></g></g></g></svg>
+ src/Diagrams.hs view
@@ -0,0 +1,153 @@+{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams+-- Copyright   :  (c) 2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- This module only contains exports defined in @diagrams-lib@ or+-- @diagrams-core@. This module can be used if you want to avoid some+-- potential conflicts with other modules, but importing+-- "Diagrams.Prelude" (which includes re-exports from other packages)+-- is often more convenient.+--+-----------------------------------------------------------------------------++module Diagrams+  (+    -- * Core library+    -- | The core definitions of transformations, diagrams,+    --   backends, and so on.+    module Diagrams.Core++    -- * Standard library++    -- | Attributes (color, line style, etc.) and styles.+  , module Diagrams.Attributes++    -- | Alignment of diagrams relative to their envelopes.+  , module Diagrams.Align++    -- | Creating and using bounding boxes.+  , module Diagrams.BoundingBox++    -- | Combining multiple diagrams into one.+  , module Diagrams.Combinators++    -- | Giving concrete locations to translation-invariant things.+  , module Diagrams.Located++    -- | Linear and cubic bezier segments.+  , module Diagrams.Segment++    -- | Trails.+  , module Diagrams.Trail++    -- | Parametrization of segments and trails.+  , module Diagrams.Parametric++    -- | Adjusting the length of parameterized objects.+  , module Diagrams.Parametric.Adjust++    -- | Computing tangent and normal vectors of segments and+    --   trails.+  , module Diagrams.Tangent++    -- | Trail-like things.+  , module Diagrams.TrailLike++    -- | Paths.+  , module Diagrams.Path++    -- | Cubic splines.+  , module Diagrams.CubicSpline++    -- | Some additional transformation-related functions, like+    --   conjugation of transformations.+  , module Diagrams.Transform++    -- | Projective transformations and other deformations+    -- lacking an inverse.+  , module Diagrams.Deform++    -- | Giving names to subdiagrams and later retrieving+    --   subdiagrams by name.+  , module Diagrams.Names++    -- | Envelopes, aka functional bounding regions.+  , module Diagrams.Envelope++    -- | Traces, aka embedded raytracers, for finding points on+    --   the boundary of a diagram.+  , module Diagrams.Trace++    -- | A query is a function that maps points in a vector space+    --   to values in some monoid; they can be used to annotate+    --   the points of a diagram with some values.+  , module Diagrams.Query++    -- | Utilities for working with points.+  , module Diagrams.Points++    -- | Utilities for working with size.+  , module Diagrams.Size++    -- | Angles+  , module Diagrams.Angle++    -- | Convenience infix operators for working with coordinates.+  , module Diagrams.Coordinates++    -- | Directions, distinguished from angles or vectors+  , module Diagrams.Direction++    -- | A wide range of things (shapes, transformations,+    --   combinators) specific to creating two-dimensional+    --   diagrams.+  , module Diagrams.TwoD++    -- | Extra things for three-dimensional diagrams.+  , module Diagrams.ThreeD++    -- | Tools for making animations.+  , module Diagrams.Animation++    -- | Various utility definitions.+  , module Diagrams.Util++  ) where++import           Diagrams.Core++import           Diagrams.Align+import           Diagrams.Angle+import           Diagrams.Animation+import           Diagrams.Attributes+import           Diagrams.BoundingBox       hiding (contains, inside,+                                             intersection, outside, union)+import           Diagrams.Combinators+import           Diagrams.Coordinates+import           Diagrams.CubicSpline+import           Diagrams.Deform+import           Diagrams.Direction         hiding (dir)+import           Diagrams.Envelope+import           Diagrams.Located+import           Diagrams.Names+import           Diagrams.Parametric+import           Diagrams.Parametric.Adjust+import           Diagrams.Path              hiding (pathPoints)+import           Diagrams.Points+import           Diagrams.Query+import           Diagrams.Segment+import           Diagrams.Size+import           Diagrams.Tangent+import           Diagrams.ThreeD+import           Diagrams.Trace+import           Diagrams.Trail             hiding (linePoints, loopPoints,+                                             trailPoints)+import           Diagrams.TrailLike+import           Diagrams.Transform+import           Diagrams.TwoD+import           Diagrams.Util+
src/Diagrams/Align.hs view
@@ -1,6 +1,9 @@-{-# LANGUAGE FlexibleContexts     #-}-{-# LANGUAGE TypeFamilies         #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ConstrainedClassMethods #-}+{-# LANGUAGE ConstraintKinds         #-}+{-# LANGUAGE FlexibleContexts        #-}+{-# LANGUAGE TypeFamilies            #-}+{-# LANGUAGE TypeOperators           #-}+{-# LANGUAGE UndecidableInstances    #-}  ----------------------------------------------------------------------------- -- |@@ -21,69 +24,151 @@        ( -- * Alignable class           Alignable(..)-       , alignByDefault+       , alignBy'Default+       , envelopeBoundary+       , traceBoundary           -- * General alignment functions         , align-       , center+       , snug+       , centerV, center+       , snugBy+       , snugCenterV, snugCenter         ) where  import           Diagrams.Core+import           Diagrams.Util    (applyAll) -import           Data.AffineSpace (alerp)-import           Data.VectorSpace+import           Data.Maybe       (fromMaybe)+import           Data.Ord         (comparing)+import           Data.Traversable+import           Prelude +import qualified Data.Foldable    as F import qualified Data.Map         as M import qualified Data.Set         as S +import           Linear.Affine+import           Linear.Metric+import           Linear.Vector        (negated)+import           Linear.Vector.Compat (lerp)+ -- | Class of things which can be aligned. class Alignable a where    -- | @alignBy v d a@ moves the origin of @a@ along the vector   --   @v@. If @d = 1@, the origin is moved to the edge of the-  --   envelope in the direction of @v@; if @d = -1@, it moves to the-  --   edge of the envelope in the direction of the negation of @v@.+  --   boundary in the direction of @v@; if @d = -1@, it moves to the+  --   edge of the boundary in the direction of the negation of @v@.   --   Other values of @d@ interpolate linearly (so for example, @d =   --   0@ centers the origin along the direction of @v@).-  alignBy :: V a -> Scalar (V a) -> a -> a+  alignBy' :: (InSpace v n a, Fractional n, HasOrigin a)+           => (v n -> a -> Point v n) -> v n -> n -> a -> a+  alignBy' = alignBy'Default +  defaultBoundary :: (V a ~ v, N a ~ n) => v n -> a -> Point v n++  alignBy :: (InSpace v n a, Fractional n, HasOrigin a)+          => v n -> n -> a -> a+  alignBy = alignBy' defaultBoundary+ -- | Default implementation of 'alignBy' for types with 'HasOrigin'---   and 'Enveloped' instances.-alignByDefault :: (HasOrigin a, Enveloped a, Num (Scalar (V a)))-               => V a -> Scalar (V a) -> a -> a-alignByDefault v d a = moveOriginTo (alerp (envelopeP (negateV v) a)-                                    (envelopeP v a)-                                    ((d + 1) / 2))-                             a+--   and 'AdditiveGroup' instances.+alignBy'Default :: (InSpace v n a, Fractional n, HasOrigin a)+                => (v n -> a -> Point v n) -> v n -> n -> a -> a+alignBy'Default boundary v d a = moveOriginTo (lerp ((d + 1) / 2)+                                                    (boundary (negated v) a)+                                                    (boundary v a)+                                              ) a+{-# ANN alignBy'Default ("HLint: ignore Use camelCase" :: String) #-} -instance (InnerSpace v, OrderedField (Scalar v)) => Alignable (Envelope v) where-  alignBy = alignByDefault -instance (Enveloped b, HasOrigin b) => Alignable [b] where-  alignBy = alignByDefault+-- | Some standard functions which can be used as the `boundary` argument to+--  `alignBy'`.+envelopeBoundary :: (V a ~ v, N a ~ n, Enveloped a) => v n -> a -> Point v n+envelopeBoundary = envelopeP -instance (Enveloped b, HasOrigin b, Ord b) => Alignable (S.Set b) where-  alignBy = alignByDefault+traceBoundary :: (V a ~ v, N a ~ n, Num n, Traced a) => v n -> a -> Point v n+traceBoundary v a = fromMaybe origin (maxTraceP origin v a) -instance (Enveloped b, HasOrigin b) => Alignable (M.Map k b) where-  alignBy = alignByDefault+combineBoundaries+  :: (InSpace v n a, Metric v, Ord n, F.Foldable f)+  => (v n -> a -> Point v n) -> v n -> f a -> Point v n+combineBoundaries b v fa+  = b v $ F.maximumBy (comparing (dot v . (.-.origin) . b v)) fa -instance ( HasLinearMap v, InnerSpace v, OrderedField (Scalar v)-         , Monoid' m-         ) => Alignable (QDiagram b v m) where-  alignBy = alignByDefault+instance (Metric v, OrderedField n) => Alignable (Envelope v n) where+  defaultBoundary = envelopeBoundary +instance (Metric v, OrderedField n) => Alignable (Trace v n) where+  defaultBoundary = traceBoundary++instance (V b ~ v, N b ~ n, Metric v, OrderedField n, Alignable b) => Alignable [b] where+  defaultBoundary = combineBoundaries defaultBoundary++instance (V b ~ v, N b ~ n, Metric v, OrderedField n, Alignable b)+    => Alignable (S.Set b) where+  defaultBoundary = combineBoundaries defaultBoundary++instance (V b ~ v, N b ~ n, Metric v, OrderedField n, Alignable b)+    => Alignable (M.Map k b) where+  defaultBoundary = combineBoundaries defaultBoundary++instance (Metric v, OrderedField n, Monoid' m)+    => Alignable (QDiagram b v n m) where+  defaultBoundary = envelopeBoundary++-- | Although the 'alignBy' method for the @(b -> a)@ instance is+--   sensible, there is no good implementation for+--   'defaultBoundary'. Instead, we provide a total method, but one that+--   is not sensible. This should not present a serious problem as long+--   as your use of 'Alignable' happens through 'alignBy'.+instance (InSpace v n a, HasOrigin a, Alignable a) => Alignable (b -> a) where+  alignBy v d f b     = alignBy v d (f b)+  defaultBoundary _ _ = origin+ -- | @align v@ aligns an enveloped object along the edge in the --   direction of @v@.  That is, it moves the local origin in the --   direction of @v@ until it is on the edge of the envelope.  (Note --   that if the local origin is outside the envelope to begin with, --   it may have to move \"backwards\".)-align :: (Alignable a, Num (Scalar (V a))) => V a -> a -> a+align :: (InSpace v n a, Fractional n, Alignable a, HasOrigin a) => v n -> a -> a align v = alignBy v 1 --- | @center v@ centers an enveloped object along the direction of+-- | Version of @alignBy@ specialized to use @traceBoundary@+snugBy :: (InSpace v n a, Fractional n, Alignable a, Traced a, HasOrigin a)+       => v n -> n -> a -> a+snugBy = alignBy' traceBoundary++-- | Like align but uses trace.+snug :: (InSpace v n a, Fractional n, Alignable a, Traced a, HasOrigin a)+      => v n -> a -> a+snug v = snugBy v 1++-- | @centerV v@ centers an enveloped object along the direction of --   @v@.-center :: (Alignable a, Num (Scalar (V a))) => V a -> a -> a-center v = alignBy v 0+centerV :: (InSpace v n a, Fractional n, Alignable a, HasOrigin a) => v n -> a -> a+centerV v = alignBy v 0++-- | @center@ centers an enveloped object along all of its basis vectors.+center :: (InSpace v n a, Fractional n, Traversable v, Alignable a, HasOrigin a) => a -> a+center = applyAll fs+  where+    fs = map centerV basis++-- | Like @centerV@ using trace.+snugCenterV+  :: (InSpace v n a, Fractional n, Alignable a, Traced a, HasOrigin a)+   => v n -> a -> a+snugCenterV v = alignBy' traceBoundary v 0++-- | Like @center@ using trace.+snugCenter :: (InSpace v n a, Traversable v, Fractional n, Alignable a, HasOrigin a, Traced a)+           => a -> a+snugCenter = applyAll fs+  where+    fs = map snugCenterV basis++{-# ANN module ("HLint: ignore Use camelCase" :: String) #-}
+ src/Diagrams/Angle.hs view
@@ -0,0 +1,284 @@+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Angle+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Type for representing angles.+--+-----------------------------------------------------------------------------++module Diagrams.Angle+       ( -- * Angle type+         Angle++         -- ** Using angles+       , (@@), rad, turn, deg++         -- ** Common angles+       , fullTurn, halfTurn, quarterTurn++         -- ** Trigonometric functions+       , sinA, cosA, tanA+       , asinA, acosA, atanA, atan2A, atan2A'++         -- ** Angle utilities+       , angleBetween, angleRatio, normalizeAngle++         -- ** Classes+       , HasTheta(..)+       , HasPhi(..)++         -- * Rotation+       , rotation, rotate+       ) where++import           Control.Applicative+import           Control.Lens            (AReview, Iso', Lens', iso, over,+                                          review, (^.))+import           Data.Fixed+import           Data.Monoid             hiding ((<>))+import           Data.Monoid.Action+import           Data.Semigroup+import           Prelude+import           Text.Read++import           Diagrams.Core           (OrderedField)+import           Diagrams.Core.Transform+import           Diagrams.Core.V+import           Diagrams.Points+import           Linear.V2               (V2 (..))++import           Linear.Metric+import           Linear.Vector++-- | Angles can be expressed in a variety of units.  Internally,+--   they are represented in radians.+newtype Angle n = Radians n+  deriving (Eq, Ord, Enum, Functor)++instance Show n => Show (Angle n) where+  showsPrec d (Radians a) = showParen (d > 5) $+    showsPrec 6 a . showString " @@ rad"++instance Read n => Read (Angle n) where+  readPrec = parens . prec 5 $ do+    x <- readPrec+    Symbol "@@" <- lexP+    Ident "rad" <- lexP+    pure (Radians x)++type instance N (Angle n) = n++instance Applicative Angle where+  pure = Radians+  {-# INLINE pure #-}+  Radians f <*> Radians x = Radians (f x)+  {-# INLINE (<*>) #-}++instance Additive Angle where+  zero = pure 0+  {-# INLINE zero #-}++instance Num n => Semigroup (Angle n) where+  (<>) = (^+^)+  {-# INLINE (<>) #-}++instance Num n => Monoid (Angle n) where+  mappend = (<>)+  mempty  = Radians 0++-- | The radian measure of an 'Angle' @a@ can be accessed as @a '^.'+--   rad@. A new 'Angle' can be defined in radians as @pi \@\@+--   rad@.+rad :: Iso' (Angle n) n+rad = iso (\(Radians r) -> r) Radians+{-# INLINE rad #-}++-- | The measure of an 'Angle' @a@ in full circles can be accessed as+--   @a '^.' turn@.  A new 'Angle' of one-half circle can be defined in as+--   @1/2 \@\@ turn@.+turn :: Floating n => Iso' (Angle n) n+turn = iso (\(Radians r) -> r / (2*pi)) (Radians . (*(2*pi)))+{-# INLINE turn #-}++-- | The degree measure of an 'Angle' @a@ can be accessed as @a+--   '^.' deg@. A new 'Angle' can be defined in degrees as @180 \@\@+--   deg@.+deg :: Floating n => Iso' (Angle n) n+deg = iso (\(Radians r) -> r / (2*pi/360)) (Radians . ( * (2*pi/360)))+{-# INLINE deg #-}++-- | An angle representing one full turn.+fullTurn :: Floating v => Angle v+fullTurn = 1 @@ turn++-- | An angle representing a half turn.+halfTurn :: Floating v => Angle v+halfTurn = 0.5 @@ turn++-- | An angle representing a quarter turn.+quarterTurn :: Floating v => Angle v+quarterTurn = 0.25 @@ turn++-- | Calculate ratio between two angles.+angleRatio :: Floating n => Angle n -> Angle n -> n+angleRatio a b = (a ^. rad) / (b ^. rad)++-- | The sine of the given @Angle@.+sinA :: Floating n => Angle n -> n+sinA (Radians r) = sin r++-- | The cosine of the given @Angle@.+cosA :: Floating n => Angle n -> n+cosA (Radians r) = cos r++-- | The tangent function of the given @Angle@.+tanA :: Floating n => Angle n -> n+tanA (Radians r) = tan r++-- | The @Angle@ with the given sine.+asinA :: Floating n => n -> Angle n+asinA = Radians . asin++-- | The @Angle@ with the given cosine.+acosA :: Floating n => n -> Angle n+acosA = Radians . acos++-- | The @Angle@ with the given tangent.+atanA :: Floating n => n -> Angle n+atanA = Radians . atan++-- | @atan2A y x@ is the angle between the positive x-axis and the vector given+--   by the coordinates (x, y). The 'Angle' returned is in the [-pi,pi] range.+atan2A :: RealFloat n => n -> n -> Angle n+atan2A y x = Radians $ atan2 y x++-- | Similar to 'atan2A' but without the 'RealFloat' constraint. This means it+--   doesn't handle negative zero cases. However, for most geometric purposes,+--   the outcome will be the same.+atan2A' :: OrderedField n => n -> n -> Angle n+atan2A' y x = atan2' y x @@ rad++-- atan2 without negative zero tests+atan2' :: OrderedField n => n -> n -> n+atan2' y x+  | x > 0            =  atan (y/x)+  | x == 0 && y > 0  =  pi/2+  | x <  0 && y > 0  =  pi + atan (y/x)+  | x <= 0 && y < 0  = -atan2' (-y) x+  | y == 0 && x < 0  =  pi    -- must be after the previous test on zero y+  | x==0 && y==0     =  y     -- must be after the other double zero tests+  | otherwise        =  x + y -- x or y is a NaN, return a NaN (via +)++-- | @30 \@\@ deg@ is an 'Angle' of the given measure and units.+--+-- >>> pi @@ rad+-- 3.141592653589793 @@ rad+--+-- >>> 1 @@ turn+-- 6.283185307179586 @@ rad+--+-- >>> 30 @@ deg+-- 0.5235987755982988 @@ rad+--+--   For 'Iso''s, ('@@') reverses the 'Iso'' on its right, and applies+--   the 'Iso'' to the value on the left. 'Angle's are the motivating+--   example where this order improves readability.+--+--   This is the same as a flipped 'review'.+--+-- @+-- ('@@') :: a -> 'Iso''      s a -> s+-- ('@@') :: a -> 'Prism''    s a -> s+-- ('@@') :: a -> 'Review'    s a -> s+-- ('@@') :: a -> 'Equality'' s a -> s+-- @+(@@) :: b -> AReview a b -> a+a @@ i = review i a++infixl 5 @@++-- | Compute the positive angle between the two vectors in their common+--   plane in the [0,pi] range. For a signed angle see+--   'Diagrams.TwoD.Vector.signedAngleBetween'.+--+--   Returns NaN if either of the vectors are zero.+angleBetween  :: (Metric v, Floating n, Ord n) => v n -> v n -> Angle n+angleBetween v1 v2 = acosA (min 1 . max (-1) $ signorm v1 `dot` signorm v2)+-- N.B.: Currently discards the common plane information.++-- | Normalize an angle so that it lies in the [0,tau) range.+normalizeAngle :: (Floating n, Real n) => Angle n -> Angle n+normalizeAngle = over rad (`mod'` (2 * pi))++------------------------------------------------------------+-- Rotation++-- These functions are defined here (instead of in+-- Diagrams.TwoD.Transform) because the Action instance needs to go+-- here.++-- | Create a transformation which performs a rotation about the local+--   origin by the given angle.  See also 'rotate'.+rotation :: Floating n => Angle n -> Transformation V2 n+rotation theta = fromLinear r (linv r)+    where+    c = cosA theta+    s = sinA theta+    r               = rot c s <-> rot c (-s)+    rot co si (V2 x y) = V2 (co * x - si * y)+                            (si * x + co * y)++-- | Rotate about the local origin by the given angle. Positive angles+--   correspond to counterclockwise rotation, negative to+--   clockwise. The angle can be expressed using any of the 'Iso's on+--   'Angle'.  For example, @rotate (1\/4 \@\@ 'turn')@, @rotate+--   (tau\/4 \@\@ rad)@, and @rotate (90 \@\@ deg)@ all+--   represent the same transformation, namely, a counterclockwise+--   rotation by a right angle.  To rotate about some point other than+--   the local origin, see 'rotateAbout'.+--+--   Note that writing @rotate (1\/4)@, with no 'Angle' constructor,+--   will yield an error since GHC cannot figure out which sort of+--   angle you want to use.  In this common situation you can use+--   'rotateBy', which interprets its argument as a number of turns.++rotate :: (InSpace V2 n t, Transformable t, Floating n) => Angle n -> t -> t+rotate = transform . rotation++-- | Angles act on other things by rotation.+instance (V t ~ V2, N t ~ n, Transformable t, Floating n)+  => Action (Angle n) t where+  act = rotate++------------------------------------------------------------+-- Polar Coordinates++-- | The class of types with at least one angle coordinate, called '_theta'.+class HasTheta t where+  _theta :: RealFloat n => Lens' (t n) (Angle n)++-- | The class of types with at least two angle coordinates, the second called+--   '_phi'. '_phi' is the positive angle measured from the z axis.+class HasTheta t => HasPhi t where+  _phi :: RealFloat n => Lens' (t n) (Angle n)++-- Point instances+instance HasTheta v => HasTheta (Point v) where+  _theta = lensP . _theta+  {-# INLINE _theta #-}++instance HasPhi v => HasPhi (Point v) where+  _phi = lensP . _phi+  {-# INLINE _phi #-}
src/Diagrams/Animation.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE ConstraintKinds       #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies          #-}@@ -29,6 +30,9 @@         ) where +import           Data.Active+import           Data.Semigroup+ import           Diagrams.Core  import           Diagrams.Animation.Active ()@@ -38,18 +42,13 @@ import           Diagrams.TwoD.Shapes import           Diagrams.TwoD.Types -import           Data.Active-import           Data.Semigroup--import           Control.Applicative       ((<$>))-import           Data.Foldable             (foldMap)-import           Data.VectorSpace+import           Linear.Metric  -- | A value of type @QAnimation b v m@ is an animation (a --   time-varying diagram with start and end times) that can be---   rendered by backspace @b@, with vector space @v@ and monoidal+--   rendered by backend @b@, with vector space @v@ and monoidal --   annotations of type @m@.-type QAnimation b v m = Active (QDiagram b v m)+type QAnimation b v n m = Active (QDiagram b v n m)  -- | A value of type @Animation b v@ is an animation (a time-varying --   diagram with start and end times) in vector space @v@ that can be@@ -58,7 +57,7 @@ --   Note that @Animation@ is actually a synonym for @QAnimation@ --   where the type of the monoidal annotations has been fixed to --   'Any' (the default).-type Animation b v = QAnimation b v Any+type Animation b v n = QAnimation b v n Any  -- $animComb -- Most combinators for working with animations are to be found in the@@ -90,16 +89,16 @@ -- --   See also 'animRect' for help constructing a background to go --   behind an animation.-animEnvelope :: (Backend b v, OrderedField (Scalar v), InnerSpace v, Monoid' m)-           => QAnimation b v m -> QAnimation b v m+animEnvelope :: (OrderedField n, Metric v, Monoid' m)+           => QAnimation b v n m -> QAnimation b v n m animEnvelope = animEnvelope' 30  -- | Like 'animEnvelope', but with an adjustible sample rate.  The first --   parameter is the number of samples per time unit to use.  Lower --   rates will be faster but less accurate; higher rates are more --   accurate but slower.-animEnvelope' :: (Backend b v, OrderedField (Scalar v), InnerSpace v, Monoid' m)-            => Rational -> QAnimation b v m -> QAnimation b v m+animEnvelope' :: (OrderedField n, Metric v, Monoid' m)+            => Rational -> QAnimation b v n m -> QAnimation b v n m animEnvelope' r a = withEnvelope (simulate r a) <$> a  -- | @animRect@ works similarly to 'animEnvelope' for 2D diagrams, but@@ -109,16 +108,16 @@ -- --   Uses 30 samples per time unit by default; to adjust this number --   see 'animRect''.-animRect :: (TrailLike t, Enveloped t, Transformable t, Monoid t, V t ~ R2)-         => QAnimation b R2 m -> t+animRect :: (InSpace V2 n t, Monoid' m, TrailLike t, Enveloped t, Transformable t, Monoid t)+         => QAnimation b V2 n m -> t animRect = animRect' 30  -- | Like 'animRect', but with an adjustible sample rate.  The first --   parameter is the number of samples per time unit to use.  Lower --   rates will be faster but less accurate; higher rates are more --   accurate but slower.-animRect' :: (TrailLike t, Enveloped t, Transformable t, Monoid t, V t ~ R2)-          => Rational -> QAnimation b R2 m -> t+animRect' :: (InSpace V2 n t, Monoid' m, TrailLike t, Enveloped t, Transformable t, Monoid t)+          => Rational -> QAnimation b V2 n m -> t animRect' r anim     | null results = rect 1 1     | otherwise    = boxFit (foldMap boundingBox results) (rect 1 1)
src/Diagrams/Animation/Active.hs view
@@ -37,16 +37,13 @@  module Diagrams.Animation.Active where -import           Control.Applicative (pure, (<$>))--import           Diagrams.Align import           Diagrams.Core-import           Diagrams.Path import           Diagrams.TrailLike  import           Data.Active  type instance V (Active a) = V a+type instance N (Active a) = N a  -- Yes, these are all orphan instances. Get over it.  We don't want to -- put them in the 'active' package because 'active' is supposed to be@@ -97,5 +94,5 @@       )       a1 -instance Alignable a => Alignable (Active a) where-  alignBy v d a = alignBy v d <$> a+-- instance Alignable a => Alignable (Active a) where+--   alignBy v d a = alignBy v d <$> a
src/Diagrams/Attributes.hs view
@@ -1,11 +1,19 @@-{-# LANGUAGE DeriveDataTypeable-           , ExistentialQuantification-           , GeneralizedNewtypeDeriving-  #-}+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE ExistentialQuantification  #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE ViewPatterns               #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Attributes--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -23,58 +31,220 @@ -----------------------------------------------------------------------------  module Diagrams.Attributes (+    -- ** Standard measures+    ultraThin, veryThin, thin, medium, thick, veryThick, ultraThick, none+  , tiny, verySmall, small, normal, large, veryLarge, huge++    -- ** Line width+  , LineWidth, getLineWidth+  , _LineWidth, _LineWidthM+  , lineWidth, lineWidthM+  , _lineWidth, _lw, _lineWidthU+  , lw, lwN, lwO, lwL, lwG++    -- ** Dashing+  , Dashing(..), getDashing+  , dashing, dashingN, dashingO, dashingL, dashingG+  , _dashing, _dashingU+   -- * Color   -- $color -    Color(..), SomeColor(..)+  , Color(..), SomeColor(..), _SomeColor, someToAlpha -  -- ** Line color-  , LineColor, getLineColor, lineColor, lineColorA, lc, lcA+  -- ** Opacity+  , Opacity, _Opacity+  , getOpacity, opacity, _opacity -  -- ** Fill color-  , FillColor, getFillColor, recommendFillColor, fillColor, fc, fcA+  , FillOpacity, _FillOpacity+  , getFillOpacity, fillOpacity, _fillOpacity -  -- ** Opacity-  , Opacity, getOpacity, opacity+  , StrokeOpacity, _StrokeOpacity+  , getStrokeOpacity, strokeOpacity, _strokeOpacity    -- ** Converting colors-  , toRGBAUsingSpace, colorToSRGBA, colorToRGBA--  -- * Lines-  -- ** Width-  , LineWidth, getLineWidth, lineWidth, lineWidthA, lw+  , colorToSRGBA, colorToRGBA +  -- * Line stuff   -- ** Cap style-  , LineCap(..), LineCapA, getLineCap, lineCap+  , LineCap(..)+  , getLineCap, lineCap, _lineCap    -- ** Join style-  , LineJoin(..), LineJoinA, getLineJoin, lineJoin+  , LineJoin(..)+  , getLineJoin, lineJoin, _lineJoin    -- ** Miter limit-  , LineMiterLimit(..), getLineMiterLimit, lineMiterLimit, lineMiterLimitA+  , LineMiterLimit(..), _LineMiterLimit+  , getLineMiterLimit, lineMiterLimit, lineMiterLimitA, _lineMiterLimit -  -- ** Dashing-  , Dashing(..), DashingA, getDashing, dashing+  -- * Recommend optics +  , _Recommend+  , _Commit+  , _recommend+  , isCommitted+  , committed+   ) where +import           Control.Lens          hiding (none, over)+import           Data.Colour+import           Data.Colour.RGBSpace  (RGB (..))+import           Data.Colour.SRGB      (toSRGB)+import           Data.Default+import           Data.Distributive+import           Data.Monoid.Recommend+import           Data.Semigroup+import           Data.Typeable+ import           Diagrams.Core -import           Data.Default.Class+------------------------------------------------------------------------+-- Standard measures+------------------------------------------------------------------------ -import           Data.Colour-import           Data.Colour.RGBSpace-import           Data.Colour.SRGB (sRGBSpace)+none, ultraThin, veryThin, thin, medium, thick, veryThick, ultraThick,+  tiny, verySmall, small, normal, large, veryLarge, huge+  :: OrderedField n => Measure n+none       = output 0+ultraThin  = normalized 0.0005 `atLeast` output 0.5+veryThin   = normalized 0.001  `atLeast` output 0.5+thin       = normalized 0.002  `atLeast` output 0.5+medium     = normalized 0.004  `atLeast` output 0.5+thick      = normalized 0.0075 `atLeast` output 0.5+veryThick  = normalized 0.01   `atLeast` output 0.5+ultraThick = normalized 0.02   `atLeast` output 0.5 -import           Data.Typeable+tiny      = normalized 0.01+verySmall = normalized 0.015+small     = normalized 0.023+normal    = normalized 0.035+large     = normalized 0.05+veryLarge = normalized 0.07+huge      = normalized 0.10 -import           Data.Monoid.Recommend-import           Data.Semigroup+------------------------------------------------------------------------+-- Line width+------------------------------------------------------------------------ ----------------------------------------------------------------  Color  --------------------------------------------------------------------------------------------------------------+-- | Line widths specified on child nodes always override line widths+--   specified at parent nodes.+newtype LineWidth n = LineWidth (Last n)+  deriving (Typeable, Semigroup) +_LineWidth :: Iso' (LineWidth n) n+_LineWidth = iso getLineWidth (LineWidth . Last)++_LineWidthM :: Iso' (LineWidthM n) (Measure n)+_LineWidthM = mapping _LineWidth++instance Typeable n => AttributeClass (LineWidth n)++type LineWidthM n = Measured n (LineWidth n)++instance OrderedField n => Default (LineWidthM n) where+  def = fmap (LineWidth . Last) medium++getLineWidth :: LineWidth n -> n+getLineWidth (LineWidth (Last w)) = w++-- | Set the line (stroke) width.+lineWidth :: (N a ~ n, HasStyle a, Typeable n) => Measure n -> a -> a+lineWidth = applyMAttr . fmap (LineWidth . Last)++-- | Apply a 'LineWidth' attribute.+lineWidthM :: (N a ~ n, HasStyle a, Typeable n) => LineWidthM n -> a -> a+lineWidthM = applyMAttr++-- | Default for 'lineWidth'.+lw :: (N a ~ n, HasStyle a, Typeable n) => Measure n -> a -> a+lw = lineWidth++-- | A convenient synonym for 'lineWidth (global w)'.+lwG :: (N a ~ n, HasStyle a, Typeable n, Num n) => n -> a -> a+lwG = lw . global++-- | A convenient synonym for 'lineWidth (normalized w)'.+lwN :: (N a ~ n, HasStyle a, Typeable n, Num n) => n -> a -> a+lwN = lw . normalized++-- | A convenient synonym for 'lineWidth (output w)'.+lwO :: (N a ~ n, HasStyle a, Typeable n) => n -> a -> a+lwO = lw . output++-- | A convenient sysnonym for 'lineWidth (local w)'.+lwL :: (N a ~ n, HasStyle a, Typeable n, Num n) => n -> a -> a+lwL = lw . local++-- | Lens onto a measured line width in a style.+_lineWidth, _lw :: (Typeable n, OrderedField n) => Lens' (Style v n) (Measure n)+_lineWidth = atMAttr . anon def (const False) . _LineWidthM+_lw = _lineWidth++-- | Lens onto the unmeasured linewith attribute. This is useful for+--   backends to use on styles once they have been unmeasured. Using on+--   a diagram style could lead to unexpected results.+_lineWidthU :: Typeable n => Lens' (Style v n) (Maybe n)+_lineWidthU = atAttr . mapping _LineWidth++------------------------------------------------------------------------+-- Dashing+------------------------------------------------------------------------++-- | Create lines that are dashing... er, dashed.+data Dashing n = Dashing [n] n+  deriving (Functor, Typeable, Eq)++instance Semigroup (Dashing n) where+  _ <> b = b++instance Typeable n => AttributeClass (Dashing n)++getDashing :: Dashing n -> Dashing n+getDashing = id++-- | Set the line dashing style.+dashing :: (N a ~ n, HasStyle a, Typeable n)+        => [Measure n]  -- ^ A list specifying alternate lengths of on+                        --   and off portions of the stroke.  The empty+                        --   list indicates no dashing.+        -> Measure n    -- ^ An offset into the dash pattern at which the+                        --   stroke should start.+        -> a -> a+dashing ds offs = applyMAttr . distribute $ Dashing ds offs++-- | A convenient synonym for 'dashing (global w)'.+dashingG :: (N a ~ n, HasStyle a, Typeable n, Num n) => [n] -> n -> a -> a+dashingG w v = dashing (map global w) (global v)++-- | A convenient synonym for 'dashing (normalized w)'.+dashingN :: (N a ~ n, HasStyle a, Typeable n, Num n) => [n] -> n -> a -> a+dashingN w v = dashing (map normalized w) (normalized v)++-- | A convenient synonym for 'dashing (output w)'.+dashingO :: (N a ~ n, HasStyle a, Typeable n) => [n] -> n -> a -> a+dashingO w v = dashing (map output w) (output v)++-- | A convenient sysnonym for 'dashing (local w)'.+dashingL :: (N a ~ n, HasStyle a, Typeable n, Num n) => [n] -> n -> a -> a+dashingL w v = dashing (map local w) (local v)++-- | Lens onto a measured dashing attribute in a style.+_dashing :: Typeable n+         => Lens' (Style v n) (Maybe (Measured n (Dashing n)))+_dashing = atMAttr++-- | Lens onto the unmeasured 'Dashing' attribute. This is useful for+--   backends to use on styles once they have been unmeasured. Using on+--   a diagram style could lead to unexpected results.+_dashingU :: Typeable n => Lens' (Style v n) (Maybe (Dashing n))+_dashingU = atAttr++------------------------------------------------------------------------+-- Color+------------------------------------------------------------------------+ -- $color -- Diagrams outsources all things color-related to Russell O\'Connor\'s -- very nice colour package@@ -88,123 +258,68 @@ --   both the 'Data.Colour.Colour' and 'Data.Colour.AlphaColour' types --   from the "Data.Colour" library. class Color c where-  -- | Convert a color to its standard representation, AlphaColour+  -- | Convert a color to its standard representation, AlphaColour.   toAlphaColour :: c -> AlphaColour Double +  -- | Convert from an AlphaColour Double.  Note that this direction+  --   may lose some information. For example, the instance for+  --   'Colour' drops the alpha channel.+  fromAlphaColour :: AlphaColour Double -> c+ -- | An existential wrapper for instances of the 'Color' class. data SomeColor = forall c. Color c => SomeColor c   deriving Typeable --- | The color with which lines (strokes) are drawn.  Note that child---   colors always override parent colors; that is, @'lineColor' c1---   . 'lineColor' c2 $ d@ is equivalent to @'lineColor' c2 $ d@.---   More precisely, the semigroup structure on line color attributes---   is that of 'Last'.-newtype LineColor = LineColor (Last SomeColor)-  deriving (Typeable, Semigroup)-instance AttributeClass LineColor--instance Default LineColor where-    def = LineColor (Last (SomeColor black))--getLineColor :: LineColor -> SomeColor-getLineColor (LineColor (Last c)) = c---- | Set the line (stroke) color.  This function is polymorphic in the---   color type (so it can be used with either 'Colour' or---   'AlphaColour'), but this can sometimes create problems for type---   inference, so the 'lc' and 'lcA' variants are provided with more---   concrete types.-lineColor :: (Color c, HasStyle a) => c -> a -> a-lineColor = applyAttr . LineColor . Last . SomeColor---- | Apply a 'lineColor' attribute.-lineColorA :: HasStyle a => LineColor -> a -> a-lineColorA = applyAttr---- | A synonym for 'lineColor', specialized to @'Colour' Double@---   (i.e. opaque colors).-lc :: HasStyle a => Colour Double -> a -> a-lc = lineColor---- | A synonym for 'lineColor', specialized to @'AlphaColour' Double@---   (i.e. colors with transparency).-lcA :: HasStyle a => AlphaColour Double -> a -> a-lcA = lineColor---- | The color with which shapes are filled. Note that child---   colors always override parent colors; that is, @'fillColor' c1---   . 'fillColor' c2 $ d@ is equivalent to @'lineColor' c2 $ d@.---   More precisely, the semigroup structure on fill color attributes---   is that of 'Last'.-newtype FillColor = FillColor (Recommend (Last SomeColor))-  deriving (Typeable, Semigroup)-instance AttributeClass FillColor---- | Set the fill color.  This function is polymorphic in the color---   type (so it can be used with either 'Colour' or 'AlphaColour'),---   but this can sometimes create problems for type inference, so the---   'fc' and 'fcA' variants are provided with more concrete types.-fillColor :: (Color c, HasStyle a) => c -> a -> a-fillColor = applyAttr . FillColor . Commit . Last . SomeColor---- | Set a \"recommended\" fill color, to be used only if no explicit---   calls to 'fillColor' (or 'fc', or 'fcA') are used.-recommendFillColor :: (Color c, HasStyle a) => c -> a -> a-recommendFillColor = applyAttr . FillColor . Recommend . Last . SomeColor--getFillColor :: FillColor -> SomeColor-getFillColor (FillColor c) = getLast . getRecommend $ c+instance Show SomeColor where+  showsPrec d (colorToSRGBA -> (r,g,b,a)) =+    showParen (d > 10) $ showString "SomeColor " .+      if a == 0+        then showString "transparent"+        else showString "(sRGB " . showsPrec 11 r . showChar ' '+                                 . showsPrec 11 g . showChar ' '+                                 . showsPrec 11 b .+                        (if a /= 1+                           then showString " `withOpacity` " . showsPrec 11 a+                           else id) . showChar ')' --- | A synonym for 'fillColor', specialized to @'Colour' Double@---   (i.e. opaque colors).-fc :: HasStyle a => Colour Double -> a -> a-fc = fillColor+-- | Isomorphism between 'SomeColor' and 'AlphaColour' 'Double'.+_SomeColor :: Iso' SomeColor (AlphaColour Double)+_SomeColor = iso toAlphaColour fromAlphaColour --- | A synonym for 'fillColor', specialized to @'AlphaColour' Double@---   (i.e. colors with transparency).-fcA :: HasStyle a => AlphaColour Double -> a -> a-fcA = fillColor+someToAlpha :: SomeColor -> AlphaColour Double+someToAlpha (SomeColor c) = toAlphaColour c -instance (Floating a, Real a) => Color (Colour a) where-  toAlphaColour = opaque . colourConvert+instance a ~ Double => Color (Colour a) where+  toAlphaColour   = opaque+  fromAlphaColour = (`over` black) -instance (Floating a, Real a) => Color (AlphaColour a) where-  toAlphaColour = alphaColourConvert+instance a ~ Double => Color (AlphaColour a) where+  toAlphaColour   = id+  fromAlphaColour = id  instance Color SomeColor where   toAlphaColour (SomeColor c) = toAlphaColour c--instance Color LineColor where-  toAlphaColour (LineColor (Last c)) = toAlphaColour c--instance Color FillColor where-  toAlphaColour (FillColor c) = toAlphaColour . getLast . getRecommend $ c---- | Convert to an RGB space while preserving the alpha channel.-toRGBAUsingSpace :: Color c => RGBSpace Double -> c-                            -> (Double, Double, Double, Double)-toRGBAUsingSpace s col = (r,g,b,a)-  where c' = toAlphaColour col-        c  = toRGBUsingSpace s (alphaToColour c')-        a  = alphaChannel  c'-        r  = channelRed   c-        g  = channelGreen c-        b  = channelBlue  c+  fromAlphaColour             = SomeColor  -- | Convert to sRGBA. colorToSRGBA, colorToRGBA :: Color c => c -> (Double, Double, Double, Double)-colorToSRGBA = toRGBAUsingSpace sRGBSpace+colorToSRGBA col = (r, g, b, a)+  where+    c' = toAlphaColour col+    c = alphaToColour c'+    a = alphaChannel c'+    RGB r g b = toSRGB c  colorToRGBA = colorToSRGBA {-# DEPRECATED colorToRGBA "Renamed to colorToSRGBA." #-} -alphaToColour :: (Floating a, Ord a, Fractional a) => AlphaColour a -> Colour a+alphaToColour :: (Floating a, Ord a) => AlphaColour a -> Colour a alphaToColour ac | alphaChannel ac == 0 = ac `over` black                  | otherwise = darken (recip (alphaChannel ac)) (ac `over` black) -------------------------------------------------------------+------------------------------------------------------------------------ -- Opacity+------------------------------------------------------------------------  -- | Although the individual colors in a diagram can have --   transparency, the opacity/transparency of a diagram as a whole@@ -218,6 +333,9 @@   deriving (Typeable, Semigroup) instance AttributeClass Opacity +_Opacity :: Iso' Opacity Double+_Opacity = iso getOpacity (Opacity . Product)+ getOpacity :: Opacity -> Double getOpacity (Opacity (Product d)) = d @@ -227,98 +345,139 @@ opacity :: HasStyle a => Double -> a -> a opacity = applyAttr . Opacity . Product ----------------------------------------------------------------  Lines and stuff    --------------------------------------------------------------------------------------------------+-- | Lens onto the opacity in a style.+_opacity :: Lens' (Style v n) Double+_opacity = atAttr . mapping _Opacity . non 1 --- | The width of lines.  By default, the line width is measured with---   respect to the /final/ coordinate system of a rendered diagram,---   as opposed to the local coordinate systems in effect at the time---   the line width was set for various subdiagrams.  This is so that---   it is easy to combine a variety of shapes (some created by---   scaling) and have them all drawn using a consistent line width.---   However, sometimes it is desirable for scaling to affect line---   width; the 'freeze' operation is provided for this purpose.  The---   line width of frozen diagrams is affected by transformations.------   Line widths specified on child nodes always override line widths---   specified at parent nodes.-newtype LineWidth = LineWidth (Last Double)+-- fill opacity --------------------------------------------------------++-- | Like 'Opacity', but set the opacity only for fills (as opposed to strokes).+--   As with 'Opacity', the fill opacity is a value between 1+--   (completely opaque, the default) and 0 (completely transparent),+--   and is multiplicative.+newtype FillOpacity = FillOpacity (Product Double)   deriving (Typeable, Semigroup)-instance AttributeClass LineWidth+instance AttributeClass FillOpacity -instance Default LineWidth where-    def = LineWidth (Last 0.01)+_FillOpacity :: Iso' FillOpacity Double+_FillOpacity = iso getFillOpacity (FillOpacity . Product) -getLineWidth :: LineWidth -> Double-getLineWidth (LineWidth (Last w)) = w+getFillOpacity :: FillOpacity -> Double+getFillOpacity (FillOpacity (Product d)) = d --- | Set the line (stroke) width.-lineWidth :: HasStyle a => Double -> a -> a-lineWidth = applyAttr . LineWidth . Last+-- | Multiply the fill opacity (see 'FillOpacity') by the given value.  For+--   example, @fillOpacity 0.8@ means \"decrease this diagram's fill opacity to+--   80% of its previous value\".+fillOpacity :: HasStyle a => Double -> a -> a+fillOpacity = applyAttr . FillOpacity . Product --- | Apply a 'LineWidth' attribute.-lineWidthA ::  HasStyle a => LineWidth -> a -> a-lineWidthA = applyAttr+-- | Lens onto the fill opacity in a style.+_fillOpacity :: Lens' (Style v n) Double+_fillOpacity = atAttr . mapping _FillOpacity . non 1 --- | A convenient synonym for 'lineWidth'.-lw :: HasStyle a => Double -> a -> a-lw = lineWidth+-- stroke opacity -------------------------------------------------------- +-- | Like 'Opacity', but set the opacity only for strokes (as opposed to fills).+--   As with 'Opacity', the fill opacity is a value between 1+--   (completely opaque, the default) and 0 (completely transparent),+--   and is multiplicative.+newtype StrokeOpacity = StrokeOpacity (Product Double)+  deriving (Typeable, Semigroup)+instance AttributeClass StrokeOpacity++_StrokeOpacity :: Iso' StrokeOpacity Double+_StrokeOpacity = iso getStrokeOpacity (StrokeOpacity . Product)++getStrokeOpacity :: StrokeOpacity -> Double+getStrokeOpacity (StrokeOpacity (Product d)) = d++-- | Multiply the stroke opacity (see 'StrokeOpacity') by the given value.  For+--   example, @strokeOpacity 0.8@ means \"decrease this diagram's+--   stroke opacity to 80% of its previous value\".+strokeOpacity :: HasStyle a => Double -> a -> a+strokeOpacity = applyAttr . StrokeOpacity . Product++-- | Lens onto the stroke opacity in a style.+_strokeOpacity :: Lens' (Style v n) Double+_strokeOpacity = atAttr . mapping _StrokeOpacity . non 1++------------------------------------------------------------------------+-- Line stuff+------------------------------------------------------------------------++-- line cap ------------------------------------------------------------+ -- | What sort of shape should be placed at the endpoints of lines? data LineCap = LineCapButt   -- ^ Lines end precisely at their endpoints.              | LineCapRound  -- ^ Lines are capped with semicircles                              --   centered on endpoints.              | LineCapSquare -- ^ Lines are capped with a squares                              --   centered on endpoints.-  deriving (Eq,Show,Typeable)--newtype LineCapA = LineCapA (Last LineCap)-  deriving (Typeable, Semigroup, Eq)-instance AttributeClass LineCapA+  deriving (Eq, Ord, Show, Typeable)  instance Default LineCap where-    def = LineCapButt+  def = LineCapButt -getLineCap :: LineCapA -> LineCap-getLineCap (LineCapA (Last c)) = c+instance AttributeClass LineCap +-- | Last semigroup structure.+instance Semigroup LineCap where+  _ <> b = b++getLineCap :: LineCap -> LineCap+getLineCap = id+ -- | Set the line end cap attribute. lineCap :: HasStyle a => LineCap -> a -> a-lineCap = applyAttr . LineCapA . Last+lineCap = applyAttr +-- | Lens onto the line cap in a style.+_lineCap :: Lens' (Style v n) LineCap+_lineCap = atAttr . non def +-- line join -----------------------------------------------------------+ -- | How should the join points between line segments be drawn? data LineJoin = LineJoinMiter    -- ^ Use a \"miter\" shape (whatever that is).               | LineJoinRound    -- ^ Use rounded join points.               | LineJoinBevel    -- ^ Use a \"bevel\" shape (whatever                                  --   that is).  Are these...                                  --   carpentry terms?-  deriving (Eq,Show,Typeable)+  deriving (Eq, Ord, Show, Typeable) -newtype LineJoinA = LineJoinA (Last LineJoin)-  deriving (Typeable, Semigroup, Eq)-instance AttributeClass LineJoinA+instance AttributeClass LineJoin +-- | Last semigroup structure.+instance Semigroup LineJoin where+  _ <> b = b+ instance Default LineJoin where-    def = LineJoinMiter+  def = LineJoinMiter -getLineJoin :: LineJoinA -> LineJoin-getLineJoin (LineJoinA (Last j)) = j+getLineJoin :: LineJoin -> LineJoin+getLineJoin = id  -- | Set the segment join style. lineJoin :: HasStyle a => LineJoin -> a -> a-lineJoin = applyAttr . LineJoinA . Last+lineJoin = applyAttr +-- | Lens onto the line join type in a style.+_lineJoin :: Lens' (Style v n) LineJoin+_lineJoin = atAttr . non def +-- miter limit ---------------------------------------------------------+ -- | Miter limit attribute affecting the 'LineJoinMiter' joins. --   For some backends this value may have additional effects. newtype LineMiterLimit = LineMiterLimit (Last Double)-  deriving (Typeable, Semigroup)+  deriving (Typeable, Semigroup, Eq, Ord) instance AttributeClass LineMiterLimit +_LineMiterLimit :: Iso' LineMiterLimit Double+_LineMiterLimit = iso getLineMiterLimit (LineMiterLimit . Last)+ instance Default LineMiterLimit where-    def = LineMiterLimit (Last 10)+  def = LineMiterLimit (Last 10)  getLineMiterLimit :: LineMiterLimit -> Double getLineMiterLimit (LineMiterLimit (Last l)) = l@@ -331,23 +490,39 @@ lineMiterLimitA :: HasStyle a => LineMiterLimit -> a -> a lineMiterLimitA = applyAttr --- | Create lines that are dashing... er, dashed.-data Dashing = Dashing [Double] Double-  deriving (Typeable, Eq)+-- | Lens onto the line miter limit in a style.+_lineMiterLimit :: Lens' (Style v n) Double+_lineMiterLimit = atAttr . non def . _LineMiterLimit -newtype DashingA = DashingA (Last Dashing)-  deriving (Typeable, Semigroup, Eq)-instance AttributeClass DashingA+------------------------------------------------------------------------+-- Recommend optics+------------------------------------------------------------------------ -getDashing :: DashingA -> Dashing-getDashing (DashingA (Last d)) = d+-- | Prism onto a 'Recommend'.+_Recommend :: Prism' (Recommend a) a+_Recommend = prism' Recommend $ \case (Recommend a) -> Just a; _ -> Nothing --- | Set the line dashing style.-dashing :: HasStyle a =>-           [Double]  -- ^ A list specifying alternate lengths of on-                     --   and off portions of the stroke.  The empty-                     --   list indicates no dashing.-        -> Double    -- ^ An offset into the dash pattern at which the-                     --   stroke should start.-        -> a -> a-dashing ds offs = applyAttr (DashingA (Last (Dashing ds offs)))+-- | Prism onto a 'Commit'.+_Commit :: Prism' (Recommend a) a+_Commit = prism' Commit $ \case (Commit a) -> Just a; _ -> Nothing++-- | Lens onto the value inside either a 'Recommend' or 'Commit'. Unlike+--   'committed', this is a valid lens.+_recommend :: Lens (Recommend a) (Recommend b) a b+_recommend f (Recommend a) = Recommend <$> f a+_recommend f (Commit a)    = Commit <$> f a++-- | Lens onto whether something is committed or not.+isCommitted :: Lens' (Recommend a) Bool+isCommitted f r@(Recommend a) = f False <&> \b -> if b then Commit a else r+isCommitted f r@(Commit a)    = f True  <&> \b -> if b then r else Recommend a++-- | 'Commit' a value for any 'Recommend'. This is *not* a valid 'Iso'+--   because the resulting @Recommend b@ is always a 'Commit'. This is+--   useful because it means any 'Recommend' styles set with a lens will+--   not be accidentally overridden. If you want a valid lens onto a+--   recommend value use '_recommend'.+--+--   Other lenses that use this are labeled with a warning.+committed :: Iso (Recommend a) (Recommend b) a b+committed = iso getRecommend Commit
+ src/Diagrams/Attributes/Compile.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE GADTs               #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies        #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Attributes.Compile+-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- XXX+--+-----------------------------------------------------------------------------++module Diagrams.Attributes.Compile (+    SplitAttribute(..), splitAttr+  ) where++import           Data.Typeable++import           Control.Arrow       (second)+import           Control.Lens        ((%~), (&), _Wrapping')+import           Data.Kind           (Type)+import qualified Data.HashMap.Strict as HM+import           Data.Semigroup+import           Data.Tree           (Tree (..))++import           Diagrams.Core+import           Diagrams.Core.Style (Style (..), attributeToStyle)+import           Diagrams.Core.Types (RNode (..), RTree)++------------------------------------------------------------++-- This is a sort of roundabout, overly-general way to define+-- splitFills; it's done this way to facilitate testing.++class (AttributeClass (AttrType code), Typeable (PrimType code)) => SplitAttribute code where+  type AttrType code :: Type+  type PrimType code :: Type++  primOK :: code -> PrimType code -> Bool++-- | Push certain attributes down until they are at the roots of trees+--   containing only "safe" nodes.  In particular this is used to push+--   fill attributes down until they are over only loops; see+--   'splitFills'.+splitAttr :: forall code b v n a. SplitAttribute code => code -> RTree b v n a -> RTree b v n a+splitAttr code = fst . splitAttr' Nothing+  where++  -- splitAttr' is where the most interesting logic happens.+  -- Mutually recursive with splitAttr'Forest. rebuildNode and+  -- applyMfc are helper functions.+  --+  -- Input: attribute to apply to "safe" subtrees.+  --+  -- Output: tree with attributes pushed down appropriately, and+  -- a Bool indicating whether the tree contains only "safe" prims (True) or+  -- contains some unsafe ones (False).+  splitAttr' :: Maybe (AttrType code) -> RTree b v n a -> (RTree b v n a, Bool)++  -- RStyle node: Check for the special attribute, and split it out of+  -- the style, combining it with the incoming attribute.  Recurse and+  -- rebuild. The tricky bit is that we use some knot-tying to+  -- determine the right attribute to pass down to the subtrees based+  -- on this computed Bool: if all subtrees are safe, then we will+  -- apply the attribute at the root of this tree, and pass Nothing to+  -- all the subtrees.  Otherwise, we pass the given attribute along.+  -- This works out because the attribute does not need to be+  -- pattern-matched until actually applying it at some root, so the+  -- recursion can proceed and the Bool values be computed with the+  -- actual value of the attributes nodes filled in lazily.+  splitAttr' mattr (Node (RStyle sty) cs) = (t', ok)+    where+      mattr' = mattr <> getAttr sty+      sty' = sty & _Wrapping' Style %~ HM.delete ty+      ty   = typeOf (undefined :: AttrType code)+      (cs', ok) = splitAttr'Forest mattr' cs+      t' | ok        = rebuildNode Nothing ok (RStyle sty) cs'+         | otherwise = rebuildNode mattr ok (RStyle sty') cs'++  -- RPrim node: check whether it+  --   * is some sort of prim not under consideration: don't apply the attribute; return True+  --   * is unsafe: don't apply the attribute; return False+  --   * is safe  :  do   apply the attribute; return True+  splitAttr' mattr (Node rp@(RPrim (Prim prm)) _) =+      case cast prm :: Maybe (PrimType code) of+        Nothing  -> (Node rp [], True)+        Just p ->+          if primOK code p+            then (rebuildNode mattr True rp [], True)+            else (Node rp [], False)++  -- RFrozenTr, RAnnot, REmpty cases: just recurse and rebuild.  Note+  -- we assume that transformations do not affect the attributes.+  splitAttr' mattr (Node nd cs)    = (t', ok)+    where+      (cs', ok) = splitAttr'Forest mattr cs+      t'        = rebuildNode mattr ok nd cs'++  -- Recursively call splitAttr' on all subtrees, returning the+  -- logical AND of the Bool results returned (the whole forest is+  -- safe iff all subtrees are).+  splitAttr'Forest :: Maybe (AttrType code) -> [RTree b v n a] -> ([RTree b v n a], Bool)+  splitAttr'Forest mattr cs = (cs', ok)+    where+      (cs', ok) = second and . unzip . map (splitAttr' mattr) $ cs++  -- Given a fill attribute, a Bool indicating whether the given+  -- subforest contains only loops, a node, and a subforest, rebuild a+  -- tree, applying the fill attribute as appropriate (only if the+  -- Bool is true and the attribute is not Nothing).+  rebuildNode :: Maybe (AttrType code) -> Bool -> RNode b v n a -> [RTree b v n a] -> RTree b v n a+  rebuildNode mattr ok nd cs+    | ok        = applyMattr mattr (Node nd cs)+    | otherwise = Node nd cs++  -- Prepend a new fill color node if Just; the identity function if+  -- Nothing.+  applyMattr :: Maybe (AttrType code) -> RTree b v n a -> RTree b v n a+  applyMattr Nothing  t = t+  applyMattr (Just a) t = Node (RStyle $ attributeToStyle (Attribute a)) [t]
+ src/Diagrams/Backend/CmdLine.hs view
@@ -0,0 +1,518 @@+{-# LANGUAGE CPP                       #-}+{-# LANGUAGE ConstrainedClassMethods   #-}+{-# LANGUAGE DeriveDataTypeable        #-}+{-# LANGUAGE FlexibleContexts          #-}+{-# LANGUAGE FlexibleInstances         #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE RankNTypes                #-}+{-# LANGUAGE TemplateHaskell           #-}+{-# LANGUAGE TypeFamilies              #-}+{-# LANGUAGE UndecidableInstances      #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Backend.CmdLine+-- Copyright   :  (c) 2013 Diagrams team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Convenient creation of command-line-driven executables for rendering+-- diagrams.  This module provides a general framework and default+-- behaviors for parsing command-line arguments, records for diagram+-- creation options in various forms, and classes and instances for a+-- unified entry point to command-line-driven diagram creation+-- executables.+--+-- For a tutorial on command-line diagram creation see+-- <https://diagrams.github.io/doc/cmdline.html>.+--+-----------------------------------------------------------------------------++module Diagrams.Backend.CmdLine+  (++    -- * Options++    -- ** Standard options+    DiagramOpts(..)+  , diagramOpts+  , width+  , height+  , output++    -- ** Multi-diagram options+  , DiagramMultiOpts(..)+  , diagramMultiOpts+  , selection+  , list++    -- ** Animation options+  , DiagramAnimOpts(..)+  , diagramAnimOpts+  , fpu++    -- * Parsing+  , Parseable(..)+  , readHexColor++    -- * Command-line programs (@Mainable@)+    -- ** Arguments, rendering, and entry point+  , Mainable(..)++    -- ** General currying+  , ToResult(..)++    -- ** helper functions for implementing @mainRender@+  , defaultAnimMainRender+  , defaultMultiMainRender+  ) where++import           Control.Lens              (Lens', makeLenses, (&), (.~), (^.))+import           Diagrams.Animation+import           Diagrams.Attributes+import           Diagrams.Core             hiding (output)++import           Options.Applicative+import           Options.Applicative.Types (readerAsk)++import           Control.Monad             (forM_)++-- MonadFail comes from Prelude in base-4.13 and up+#if !MIN_VERSION_base(4,13,0)+import           Control.Monad.Fail        (MonadFail)+#endif++import           Data.Active               hiding (interval)+import           Data.Char                 (isDigit)+import           Data.Colour+import           Data.Colour.Names+import           Data.Colour.SRGB+import           Data.Data+import           Data.Functor.Identity+import           Data.Kind                 (Type)+import           Data.Monoid+import           Numeric++import           System.Environment        (getProgName)+import           System.FilePath           (addExtension, splitExtension)++import           Text.Printf++-- | Standard options most diagrams are likely to have.+data DiagramOpts = DiagramOpts+  { _width  :: Maybe Int -- ^ Final output width of diagram.+  , _height :: Maybe Int -- ^ Final output height of diagram.+  , _output :: FilePath  -- ^ Output file path, format is typically chosen by extension.+  }+  deriving (Show, Data, Typeable)++makeLenses ''DiagramOpts++-- | Extra options for a program that can offer a choice+--   between multiple diagrams.+data DiagramMultiOpts = DiagramMultiOpts+  { _selection :: Maybe String -- ^ Selected diagram to render.+  , _list      :: Bool         -- ^ Flag to indicate that a list of available diagrams should+                               --   be printed to standard out.+  }+  deriving (Show, Data, Typeable)++makeLenses ''DiagramMultiOpts++-- | Extra options for animations.+data DiagramAnimOpts = DiagramAnimOpts+  { _fpu :: Double -- ^ Number of frames per unit time to generate for the animation.+  }+  deriving (Show, Data, Typeable)++makeLenses ''DiagramAnimOpts++-- | Command line parser for 'DiagramOpts'.+--   Width is option @--width@ or @-w@.+--   Height is option @--height@ or @-h@ (note we change help to be @-?@ due to this).+--   Output is option @--output@ or @-o@.+diagramOpts :: Parser DiagramOpts+diagramOpts = DiagramOpts+  <$> (optional . option auto)+      ( long "width" <> short 'w'+     <> metavar "WIDTH"+     <> help "Desired WIDTH of the output image")+  <*> (optional . option auto)+      ( long "height" <> short 'h'+     <> metavar "HEIGHT"+     <> help "Desired HEIGHT of the output image")+  <*> strOption+      ( long "output" <> short 'o'+     <> value ""+     <> metavar "OUTPUT"+     <> help "OUTPUT file")++-- | Command line parser for 'DiagramMultiOpts'.+--   Selection is option @--selection@ or @-S@.+--   List is @--list@ or @-L@.+diagramMultiOpts :: Parser DiagramMultiOpts+diagramMultiOpts = DiagramMultiOpts+  <$> (optional . strOption)+      ( long "selection" <> short 'S'+     <> metavar "NAME"+     <> help "NAME of the diagram to render")+  <*> switch+      ( long "list" <> short 'L'+     <> help "List all available diagrams")++-- | Command line parser for 'DiagramAnimOpts'+--   Frames per unit is @--fpu@ or @-f@.+diagramAnimOpts :: Parser DiagramAnimOpts+diagramAnimOpts = DiagramAnimOpts+  <$> option auto+      ( long "fpu" <> short 'f'+     <> value 30.0+     <> help "Frames per unit time (for animations)")++-- | A hidden \"helper\" option which always fails.+--   Taken from Options.Applicative.Extra but without the+--   short option 'h'.  We want the 'h' for Height.+helper' :: Parser (a -> a)+helper' = abortOption param $ mconcat+  [ long "help"+  , short '?'+  , help "Show this help text"+  ]+  where+#if MIN_VERSION_optparse_applicative(0,16,0)+    param = ShowHelpText Nothing+#else+    param = ShowHelpText +#endif++-- | Apply a parser to the command line that includes the standard+--   program description and help behavior.  Results in parsed commands+--   or fails with a help message.+defaultOpts :: Parser a -> IO a+defaultOpts optsParser = do+  prog <- getProgName+  let p = info (helper' <*> optsParser)+              ( fullDesc+             <> progDesc "Command-line diagram generation."+             <> header prog)+  execParser p++-- | Parseable instances give a command line parser for a type.  If a custom+--   parser for a common type is wanted a newtype wrapper could be used to make+--   a new 'Parseable' instance.  Notice that we do /not/ want as many+--   instances as 'Read' because we want to limit ourselves to things that make+--   sense to parse from the command line.+class Parseable a where+  parser :: Parser a++-- The following instance would overlap with the product instance for+-- Parseable.  We can't tell if one wants to parse (a,b) as one argument or a+-- as one argument and b as another.  Since this is the command line we almost+-- certainly want the latter.  So we need to have less Read instances.+--+-- instance Read a => Parseable a where+--    parser = argument auto mempty++-- | Parse 'Int' according to its 'Read' instance.+instance Parseable Int where+  parser = argument auto mempty++-- | Parse 'Double' according to its 'Read' instance.+instance Parseable Double where+  parser = argument auto mempty++-- | Parse a string by just accepting the given string.+instance Parseable String where+  parser = argument str mempty++-- | Parse 'DiagramOpts' using the 'diagramOpts' parser.+instance Parseable DiagramOpts where+  parser = diagramOpts++-- | Parse 'DiagramMultiOpts' using the 'diagramMultiOpts' parser.+instance Parseable DiagramMultiOpts where+  parser = diagramMultiOpts++-- | Parse 'DiagramAnimOpts' using the 'diagramAnimOpts' parser.+instance Parseable DiagramAnimOpts where+  parser = diagramAnimOpts++-- | Parse @'Colour' Double@ as either a named color from "Data.Colour.Names"+--   or a hexadecimal color.+instance Parseable (Colour Double) where+  parser = argument (rc <|> rh) mempty+    where+      rh, rc :: ReadM (Colour Double)+      rh = f . colorToSRGBA <$> (readerAsk >>= readHexColor)+      rc = readerAsk >>= readColourName+      f (r,g,b,_) = sRGB r g b -- TODO: this seems unfortunate.  Should the alpha+                               -- value be applied to the r g b values?++-- | Parse @'AlphaColour' Double@ as either a named color from "Data.Colour.Names"+--   or a hexadecimal color.+instance Parseable (AlphaColour Double) where+  parser = argument (rc <|> rh) mempty+    where+      rh = readerAsk >>= readHexColor+      rc = opaque <$> (readerAsk >>= readColourName)++-- Addapted from the Clay.Color module of the clay package++-- | Parses a hexadecimal color.  The string can start with @\"0x\"@ or @\"#\"@+--   or just be a string of hexadecimal values.  If four or three digits are+--   given each digit is repeated to form a full 24 or 32 bit color.  For+--   example, @\"0xfc4\"@ is the same as @\"0xffcc44\"@.  When eight or six+--   digits are given each pair of digits is a color or alpha channel with the+--   order being red, green, blue, alpha.+readHexColor :: (Applicative m, MonadFail m) => String -> m (AlphaColour Double)+readHexColor cs = case cs of+  ('0':'x':hs) -> handle hs+  ('#':hs)     -> handle hs+  hs           -> handle hs+  where+    handle hs | length hs <= 8 && all isHexDigit hs+      = case hs of+        [a,b,c,d,e,f,g,h] -> withOpacity <$> (sRGB <$> hex a b <*> hex c d <*> hex e f) <*> hex g h+        [a,b,c,d,e,f    ] -> opaque      <$> (sRGB <$> hex a b <*> hex c d <*> hex e f)+        [a,b,c,d        ] -> withOpacity <$> (sRGB <$> hex a a <*> hex b b <*> hex c c) <*> hex d d+        [a,b,c          ] -> opaque      <$> (sRGB <$> hex a a <*> hex b b <*> hex c c)+        _                 -> fail $ "could not parse as a colour" ++ cs+    handle _ = fail $ "could not parse as a colour: " ++ cs++    isHexDigit c = isDigit c || c `elem` "abcdef"++    hex a b = (/ 255) <$> case readHex [a,b] of+                [(h,"")] -> return h+                _        -> fail $ "could not parse as a hex value" ++ [a,b]+++-- | This instance is needed to signal the end of a chain of+--   nested tuples, it always just results in the unit value+--   without consuming anything.+instance Parseable () where+  parser = pure ()++-- | Allow 'Parseable' things to be combined.+instance (Parseable a, Parseable b) => Parseable (a,b) where+  parser = (,) <$> parser <*> parser++-- | Triples of Parsebales should also be Parseable.+instance (Parseable a, Parseable b, Parseable c) => Parseable (a, b, c) where+  parser = (,,) <$> parser <*> parser <*> parser++instance (Parseable a, Parseable b, Parseable c, Parseable d) => Parseable (a, b, c, d) where+  parser = (,,,) <$> parser <*> parser <*> parser <*> parser++-- | This class allows us to abstract over functions that take some arguments+--   and produce a final value.  When some @d@ is an instance of+--   'ToResult' we get a type @'Args' d@ that is a type of /all/ the arguments+--   at once, and a type @'ResultOf' d@ that is the type of the final result from+--   some base case instance.+class ToResult d where+  type Args d :: Type+  type ResultOf d :: Type++  toResult :: d -> Args d -> ResultOf d++-- | A diagram can always produce a diagram when given @()@ as an argument.+--   This is our base case.+instance ToResult (QDiagram b v n Any) where+  type Args (QDiagram b v n Any) = ()+  type ResultOf (QDiagram b v n Any) = QDiagram b v n Any++  toResult d _ = d++-- | A list of diagrams can produce pages.+instance ToResult [QDiagram b v n Any] where+  type Args [QDiagram b v n Any] = ()+  type ResultOf [QDiagram b v n Any] = [QDiagram b v n Any]++  toResult ds _ = ds++-- | A list of named diagrams can give the multi-diagram interface.+instance ToResult [(String, QDiagram b v n Any)] where+  type Args [(String,QDiagram b v n Any)] = ()+  type ResultOf [(String,QDiagram b v n Any)] = [(String,QDiagram b v n Any)]++  toResult ds _ = ds++-- | An animation is another suitable base case.+instance ToResult (Animation b v n) where+  type Args (Animation b v n) = ()+  type ResultOf (Animation b v n) = Animation b v n++  toResult a _ = a++-- | Diagrams that require IO to build are a base case.+instance ToResult d => ToResult (IO d) where+  type Args (IO d) = Args d+  type ResultOf (IO d) = IO (ResultOf d)++  toResult d args = flip toResult args <$> d++-- | An instance for a function that, given some 'a', can produce a 'd' that is+--   also an instance of 'ToResult'.  For this to work we need both the+--   argument 'a' and all the arguments that 'd' will need.  Producing the+--   result is simply applying the argument to the producer and passing the+--   remaining arguments to the produced producer.++--   The previous paragraph stands as a witness to the fact that Haskell code+--   is clearer and easier to understand then paragraphs in English written by+--   me.+instance ToResult d => ToResult (a -> d) where+  type Args (a -> d) = (a, Args d)+  type ResultOf (a -> d) = ResultOf d++  toResult f (a,args) = toResult (f a) args+++-- | This class represents the various ways we want to support diagram creation+--   from the command line.  It has the right instances to select between creating+--   single static diagrams, multiple static diagrams, static animations, and+--   functions that produce diagrams as long as the arguments are 'Parseable'.+--+--   Backends are expected to create @Mainable@ instances for the types that are+--   suitable for generating output in the backend's format.  For instance,+--   Postscript can handle single diagrams, pages of diagrams, animations as+--   separate files, and association lists.  This implies instances for+--   @Diagram Postscript R2@, @[Diagram Postscript R2]@, @Animation Postscript R2@,+--   and @[(String,Diagram Postscript R2)]@.  We can consider these as the base+--   cases for the function instance.+--+--   The associated type 'MainOpts' describes the options which need to be parsed+--   from the command-line and passed to @mainRender@.+class Mainable d where+  -- | Associated type that describes the options which need to be parsed+  -- from the command-line and passed to @mainRender@.+  type MainOpts d :: Type++  -- | This method invokes the command-line parser resulting in an options+  -- value or ending the program with an error or help message.+  -- Typically the default instance will work.  If a different help message+  -- or parsing behavior is desired a new implementation is appropriate.+  mainArgs :: Parseable (MainOpts d) => proxy d -> IO (MainOpts d)+  mainArgs _ = defaultOpts parser++  -- | Backend specific work of rendering with the given options and mainable+  -- value is done here.  All backend instances should implement this method.+  mainRender :: MainOpts d -> d -> IO ()++  -- | Main entry point for command-line diagram creation.  This is the method+  -- that users will call from their program @main@.  For instance an expected+  -- user program would take the following form.+  --+  -- @+  -- import Diagrams.Prelude+  -- import Diagrams.Backend.TheBestBackend.CmdLine+  --+  -- d :: Diagram B R2+  -- d = ...+  --+  -- main = mainWith d+  -- @+  --+  -- Most backends should be able to use the default implementation.  A different+  -- implementation should be used to handle more complex interactions with the user.+  mainWith :: Parseable (MainOpts d) => d -> IO ()+  mainWith d = do+    opts <- mainArgs (Identity d)+    mainRender opts d++-- | This instance allows functions resulting in something that is 'Mainable' to+--   be 'Mainable'.  It takes a parse of collected arguments and applies them to+--   the given function producing the 'Mainable' result.+instance (ToResult d, Mainable (ResultOf d))+        => Mainable (a -> d) where+  type MainOpts (a -> d) = (MainOpts (ResultOf (a -> d)), Args (a -> d))++  mainRender (opts, a) f  = mainRender opts (toResult f a)+-- TODO: why can't we get away with: instance (Parseable (Args (a -> d)), Mainable (ResultOf d)) => ...+--       Doesn't `Args (a -> d)` imply `ToResult (a -> d)` which implies `ToResult d` ?++-- | With this instance we can perform IO to produce something+--   'Mainable' before rendering.+instance Mainable d => Mainable (IO d) where+  type MainOpts (IO d) = MainOpts d++  mainRender opts dio = dio >>= mainRender opts++-- | @defaultMultiMainRender@ is an implementation of 'mainRender' where+--   instead of a single diagram it takes a list of diagrams paired with names+--   as input.  The generated executable then takes a @--selection@ option+--   specifying the name of the diagram that should be rendered.  The list of+--   available diagrams may also be printed by passing the option @--list@.+--+--   Typically a backend can write its @[(String,QDiagram b v n Any)]@ instance as+--+--   @+--   instance Mainable [(String,QDiagram b v n Any)] where+--       type MainOpts [(String,QDiagram b v n Any)] = (DiagramOpts, DiagramMultiOpts)+--       mainRender = defaultMultiMainRender+--   @+--+--   We do not provide this instance in general so that backends can choose to+--   opt-in to this form or provide a different instance that makes more sense.+defaultMultiMainRender :: Mainable d => (MainOpts d, DiagramMultiOpts) -> [(String, d)] -> IO ()+defaultMultiMainRender (opts,multi) ds =+  if multi^.list+    then showDiaList (map fst ds)+    else case multi^.selection of+           Nothing  -> putStrLn "No diagram selected." >> showDiaList (map fst ds)+           Just sel -> case lookup sel ds of+                         Nothing -> putStrLn $ "Unknown diagram: " ++ sel+                         Just d  -> mainRender opts d++-- | Display the list of diagrams available for rendering.+showDiaList :: [String] -> IO ()+showDiaList ds = do+  putStrLn "Available diagrams:"+  putStrLn $ "  " ++ unwords ds++-- | @defaultAnimMainRender@ is an implementation of 'mainRender' which renders+--   an animation as numbered frames, named by extending the given output file+--   name by consecutive integers.  For example if the given output file name is+--   @foo\/blah.ext@, the frames will be saved in @foo\/blah001.ext@,+--   @foo\/blah002.ext@, and so on (the number of padding digits used depends on+--   the total number of frames).  It is up to the user to take these images and+--   stitch them together into an actual animation format (using, /e.g./+--   @ffmpeg@).+--+--   Of course, this is a rather crude method of rendering animations;+--   more sophisticated methods will likely be added in the future.+--+--   The @fpu@ option from 'DiagramAnimOpts' can be used to control how many frames will+--   be output for each second (unit time) of animation.+--+--   This function requires a lens into the structure that the particular backend+--   uses for its diagram base case.  If @MainOpts (QDiagram b v n Any) ~ DiagramOpts@+--   then this lens will simply be 'output'.  This lens is required because the+--   implementation works by modifying the output field and running the base @mainRender@.+--   Typically a backend can write its @Animation B V@ instance as+--+--   @+--   instance Mainable (Animation B V) where+--       type MainOpts (Animation B V) = (DiagramOpts, DiagramAnimOpts)+--       mainRender = defaultAnimMainRender output+--   @+--+--   We do not provide this instance in general so that backends can choose to+--   opt-in to this form or provide a different instance that makes more sense.++defaultAnimMainRender ::+    (opts -> QDiagram b v n Any -> IO ())+    -> Lens' opts FilePath -- ^ A lens into the output path.+    -> (opts, DiagramAnimOpts)+    -> Animation b v n+    -> IO ()+defaultAnimMainRender renderF out (opts,animOpts) anim = do+  let frames  = simulate (toRational $ animOpts^.fpu) anim+      nDigits = length . show . length $ frames+  forM_ (zip [1..] frames) $ \(i,d) -> renderF (indexize out nDigits i opts) d++-- | @indexize d n@ adds the integer index @n@ to the end of the+--   output file name, padding with zeros if necessary so that it uses+--   at least @d@ digits.+indexize :: Lens' s FilePath -> Int -> Integer -> s -> s+indexize out nDigits i opts = opts & out .~ output'+  where fmt         = "%0" ++ show nDigits ++ "d"+        output'     = addExtension (base ++ printf fmt i) ext+        (base, ext) = splitExtension (opts^.out)
− src/Diagrams/Backend/Show.hs
@@ -1,69 +0,0 @@-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE TypeSynonymInstances       #-}--------------------------------------------------------------------------------- |--- Module      :  Diagrams.Backend.Show--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)--- License     :  BSD-style (see LICENSE)--- Maintainer  :  diagrams-discuss@googlegroups.com------ A simple Show-based diagrams backend, for testing purposes.----------------------------------------------------------------------------------module Diagrams.Backend.Show where--import           Diagrams.Core.Transform (onBasis)-import           Diagrams.Prelude-import           Diagrams.Trail--import           Data.Basis--import           Text.PrettyPrint        (Doc, empty, hsep, parens, ($+$))-import qualified Text.PrettyPrint        as PP--import           Data.List               (transpose)---- | Token for identifying this backend.-data ShowBackend = ShowBackend--instance HasLinearMap v => Backend ShowBackend v where-  data Render  ShowBackend v = SR Doc-  type Result  ShowBackend v = String-  data Options ShowBackend v = SBOpt--  withStyle _ _ _ r = r -- XXX FIXME--  doRender _ _ (SR r) = PP.render r--instance Monoid (Render ShowBackend v) where-  mempty = SR empty-  (SR d1) `mappend` (SR d2) = SR (d1 $+$ d2)--renderTransf :: forall v. (Num (Scalar v), HasLinearMap v, Show (Scalar v))-             => Transformation v -> Doc-renderTransf t = renderMat mat-  where vmat :: [v]-        (vmat, _) = onBasis t-        mat :: [[Scalar v]]-        mat = map decompV vmat---        mat' :: [[Scalar v]]---        mat'  = map (++[0]) mat ++ [decompV tr ++ [1]]-        decompV = map snd . decompose--renderMat :: Show a => [[a]] -> Doc-renderMat = PP.vcat . map renderRow . transpose-  where renderRow = parens . hsep . map (PP.text . show)--instance (Show v, HasLinearMap v) => Renderable (Segment o v) ShowBackend where-  render _ s = SR $ PP.text (show s)--instance (Show v, OrderedField (Scalar v), InnerSpace v, HasLinearMap v) => Renderable (Trail v) ShowBackend where-  render _ t = SR $ PP.text (show t)--instance (Show v, OrderedField (Scalar v), InnerSpace v, HasLinearMap v) => Renderable (Path v) ShowBackend where-  render _ p = SR $ PP.text (show p)
src/Diagrams/BoundingBox.hs view
@@ -1,17 +1,19 @@-{-# LANGUAGE DeriveDataTypeable-           , DeriveFunctor-           , FlexibleContexts-           , GeneralizedNewtypeDeriving-           , NoMonomorphismRestriction-           , ScopedTypeVariables-           , StandaloneDeriving-           , TypeFamilies-           , UndecidableInstances-  #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE NoMonomorphismRestriction  #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE ViewPatterns               #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.BoundingBox--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -24,309 +26,309 @@ -----------------------------------------------------------------------------  module Diagrams.BoundingBox-       ( -- * Bounding boxes-         BoundingBox()--         -- * Constructing bounding boxes-       , emptyBox, fromCorners, fromPoint, fromPoints-       , boundingBox+  ( -- * Bounding boxes+    BoundingBox -         -- * Queries on bounding boxes-       , isEmptyBox-       , getCorners, getAllCorners-       , boxExtents, boxTransform, boxFit-       , contains, contains'-       , inside, inside', outside, outside'+    -- * Constructing bounding boxes+  , emptyBox, fromCorners, fromPoint, fromPoints+  , boundingBox -         -- * Operations on bounding boxes-       , union, intersection-       ) where+    -- * Queries on bounding boxes+  , isEmptyBox+  , getCorners, getAllCorners+  , boxExtents, boxCenter+  , mCenterPoint, centerPoint+  , boxTransform, boxFit+  , contains, contains'+  , inside, inside', outside, outside' -import Control.Applicative ((<$>))-import Control.Monad (join, liftM2)-import Data.Map (Map, fromList, toList, fromDistinctAscList, toAscList)-import qualified Data.Foldable as F+  , boxGrid -import Data.Maybe (fromMaybe)+    -- * Operations on bounding boxes+  , union, intersection+  ) where -import Data.VectorSpace--- (VectorSpace, Scalar, AdditiveGroup, zeroV, negateV, (^+^), (^-^))-import Data.Basis (HasBasis, Basis, decompose, recompose, basisValue)-import Data.Monoid (Monoid(..))-import Data.Semigroup (Semigroup(..), Option(..))+import           Control.Lens            (AsEmpty (..), Each (..), nearly)+import           Data.Foldable           as F+import           Data.Maybe              (fromMaybe)+import           Data.Semigroup+import           Text.Read -import Data.Data (Data)-import Data.Typeable (Typeable)+import           Diagrams.Align+import           Diagrams.Core+import           Diagrams.Core.Transform+import           Diagrams.Path+import           Diagrams.Query+import           Diagrams.ThreeD.Shapes  (cube)+import           Diagrams.ThreeD.Types+import           Diagrams.TwoD.Path      ()+import           Diagrams.TwoD.Shapes+import           Diagrams.TwoD.Types -import Diagrams.Core.Points (Point(..))-import Diagrams.Core.HasOrigin (HasOrigin(..))-import Diagrams.Core.Envelope (Enveloped(..), appEnvelope)-import Diagrams.Core.V (V)-import Diagrams.Core.Transform-  (Transformation(..), Transformable(..), HasLinearMap, (<->))+import           Control.Applicative+import           Data.Traversable        as T+import           Linear.Affine+import           Linear.Metric+import           Linear.Vector           hiding (lerp)+import           Linear.Vector.Compat    (lerp)  -- Unexported utility newtype -newtype NonEmptyBoundingBox v = NonEmptyBoundingBox (Point v, Point v)-  deriving (Eq, Data, Typeable)+newtype NonEmptyBoundingBox v n = NonEmptyBoundingBox (Point v n, Point v n)+  deriving (Eq, Functor) -fromNonEmpty :: NonEmptyBoundingBox v -> BoundingBox v-fromNonEmpty = BoundingBox . Option . Just+type instance V (NonEmptyBoundingBox v n) = v+type instance N (NonEmptyBoundingBox v n) = n -fromMaybeEmpty :: Maybe (NonEmptyBoundingBox v) -> BoundingBox v+fromNonEmpty :: NonEmptyBoundingBox v n -> BoundingBox v n+fromNonEmpty = BoundingBox . Just++fromMaybeEmpty :: Maybe (NonEmptyBoundingBox v n) -> BoundingBox v n fromMaybeEmpty = maybe emptyBox fromNonEmpty -nonEmptyCorners :: NonEmptyBoundingBox v -> (Point v, Point v)+nonEmptyCorners :: NonEmptyBoundingBox v n -> (Point v n, Point v n) nonEmptyCorners (NonEmptyBoundingBox x) = x -instance (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-    => Semigroup (NonEmptyBoundingBox v) where+instance (Additive v, Ord n) => Semigroup (NonEmptyBoundingBox v n) where   (NonEmptyBoundingBox (ul, uh)) <> (NonEmptyBoundingBox (vl, vh))-    = NonEmptyBoundingBox-    $ mapT toPoint (combineP min ul vl, combineP max uh vh)-+    = NonEmptyBoundingBox (liftU2 min ul vl, liftU2 max uh vh)  -- | A bounding box is an axis-aligned region determined by two points --   indicating its \"lower\" and \"upper\" corners.  It can also represent --   an empty bounding box - the points are wrapped in @Maybe@.-newtype BoundingBox v = BoundingBox (Option (NonEmptyBoundingBox v))-  deriving (Eq, Data, Typeable)+newtype BoundingBox v n = BoundingBox (Maybe (NonEmptyBoundingBox v n))+  deriving (Eq, Functor) -deriving instance-  ( HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v)-  ) => Semigroup (BoundingBox v)-deriving instance-  ( HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v)-  ) => Monoid (BoundingBox v)+deriving instance (Additive v, Ord n) => Semigroup (BoundingBox v n)+deriving instance (Additive v, Ord n) => Monoid (BoundingBox v n) -type instance V (BoundingBox v) = v+instance AsEmpty (BoundingBox v n) where+  _Empty = nearly emptyBox isEmptyBox --- Map a function on a homogenous 2-tuple. (unexported utility)+-- | Only valid if the second point is not smaller than the first.+instance (Additive v', Foldable v', Ord n') =>+    Each (BoundingBox v n) (BoundingBox v' n') (Point v n) (Point v' n') where+  each f (getCorners -> Just (l, u)) = fromCorners <$> f l <*> f u+  each _ _                           = pure emptyBox++type instance V (BoundingBox v n) = v+type instance N (BoundingBox v n) = n++-- Map a function on a homogeneous 2-tuple. (unexported utility) mapT :: (a -> b) -> (a, a) -> (b, b) mapT f (x, y) = (f x, f y) -instance ( VectorSpace v, HasBasis v, Ord (Basis v)-         , AdditiveGroup (Scalar v), Ord (Scalar v)-         )  => HasOrigin (BoundingBox v) where+instance (Additive v, Num n) => HasOrigin (BoundingBox v n) where   moveOriginTo p b     = fromMaybeEmpty-    ( NonEmptyBoundingBox . mapT (moveOriginTo p) <$> getCorners b )+    (NonEmptyBoundingBox . mapT (moveOriginTo p) <$> getCorners b) -instance ( InnerSpace v, HasBasis v, Ord (Basis v)-         , AdditiveGroup (Scalar v), Ord (Scalar v), Floating (Scalar v)-         ) => Enveloped (BoundingBox v) where+instance (Additive v, Foldable v, Ord n)+     => HasQuery (BoundingBox v n) Any where+  getQuery bb = Query $ Any . contains bb++instance (Metric v, Traversable v, OrderedField n)+     => Enveloped (BoundingBox v n) where   getEnvelope = getEnvelope . getAllCorners -instance Show v => Show (BoundingBox v) where-  show-    = maybe "emptyBox" (\(l, u) -> "fromCorners " ++ show l ++ " " ++ show u)-    . getCorners+-- Feels like cheating.+-- Should be possible to generalise this.+instance RealFloat n => Traced (BoundingBox V2 n) where+  getTrace = getTrace+           . ((`boxFit` rect 1 1) . boundingBox :: Envelope V2 n -> Path V2 n)+           . getEnvelope -{- TODO-instance Read v => Read (BoundingBox v) where-  read "emptyBox" = emptyBox--}+instance TypeableFloat n => Traced (BoundingBox V3 n) where+  getTrace bb = foldMap (\tr -> getTrace $ transform tr cube) $+                boxTransform (boundingBox cube) bb +instance (Metric v, Traversable v, OrderedField n) => Alignable (BoundingBox v n) where+  defaultBoundary = envelopeP++instance Show (v n) => Show (BoundingBox v n) where+  showsPrec d b = case getCorners b of+    Just (l, u) -> showParen (d > 10) $+      showString "fromCorners " . showsPrec 11 l . showChar ' ' . showsPrec 11 u+    Nothing     -> showString "emptyBox"++instance Read (v n) => Read (BoundingBox v n) where+  readPrec = parens $+    (do+      Ident "emptyBox" <- lexP+      pure emptyBox+    ) <|>+    (prec 10 $ do+      Ident "fromCorners" <- lexP+      l <- step readPrec+      h <- step readPrec+      pure . fromNonEmpty $ NonEmptyBoundingBox (l, h)+    )+ -- | An empty bounding box.  This is the same thing as @mempty@, but it doesn't---   require the same type constraints that the @Monoid@ -emptyBox :: BoundingBox v-emptyBox = BoundingBox $ Option Nothing+--   require the same type constraints that the @Monoid@ instance does.+emptyBox :: BoundingBox v n+emptyBox = BoundingBox Nothing  -- | Create a bounding box from a point that is component-wise @(<=)@ than the --   other.  If this is not the case, then @mempty@ is returned. fromCorners-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => Point v -> Point v -> BoundingBox v+  :: (Additive v, Foldable v, Ord n)+  => Point v n -> Point v n -> BoundingBox v n fromCorners l h-  | F.and (combineP (<=) l h) = fromNonEmpty $ NonEmptyBoundingBox (l, h)-  | otherwise = mempty+  | F.and (liftI2 (<=) l h) = fromNonEmpty $ NonEmptyBoundingBox (l, h)+  | otherwise               = mempty  -- | Create a degenerate bounding \"box\" containing only a single point.-fromPoint-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => Point v -> BoundingBox v+fromPoint :: Point v n -> BoundingBox v n fromPoint p = fromNonEmpty $ NonEmptyBoundingBox (p, p)  -- | Create the smallest bounding box containing all the given points.-fromPoints-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => [Point v] -> BoundingBox v+fromPoints :: (Additive v, Ord n) => [Point v n] -> BoundingBox v n fromPoints = mconcat . map fromPoint  -- | Create a bounding box for any enveloped object (such as a diagram or path).-boundingBox :: forall a. ( Enveloped a, HasBasis (V a), AdditiveGroup (V a)-                         , Ord (Basis (V a))-                         ) => a -> BoundingBox (V a)+boundingBox :: (InSpace v n a, HasBasis v, Enveloped a)+            => a -> BoundingBox v n boundingBox a = fromMaybeEmpty $ do-    env <- appEnvelope $ getEnvelope a-    let h = recompose $ map (\v -> (v,          env           $ basisValue v)) us-        l = recompose $ map (\v -> (v, negate . env . negateV $ basisValue v)) us-    return $ NonEmptyBoundingBox (P l, P h)-  where-    -- The units. Might not work if 0-components aren't reported.-    --TODO: Depend on Enum Basis?-    us = map fst $ decompose (zeroV :: V a)+  env <- (appEnvelope . getEnvelope) a+  let h = fmap env eye+      l = negated $ fmap (env . negated) eye+  return $ NonEmptyBoundingBox (P l, P h)  -- | Queries whether the BoundingBox is empty.-isEmptyBox :: BoundingBox v -> Bool-isEmptyBox (BoundingBox (Option Nothing)) = True-isEmptyBox _ = False+isEmptyBox :: BoundingBox v n -> Bool+isEmptyBox (BoundingBox Nothing) = True+isEmptyBox _                              = False  -- | Gets the lower and upper corners that define the bounding box.-getCorners :: BoundingBox v -> Maybe (Point v, Point v)-getCorners (BoundingBox p) = nonEmptyCorners <$> getOption p+getCorners :: BoundingBox v n -> Maybe (Point v n, Point v n)+getCorners (BoundingBox p) = nonEmptyCorners <$> p  -- | Computes all of the corners of the bounding box.-getAllCorners :: (HasBasis v, AdditiveGroup (Scalar v), Ord (Basis v))-              => BoundingBox v -> [Point v]-getAllCorners (BoundingBox (Option Nothing)) = []-getAllCorners (BoundingBox (Option (Just (NonEmptyBoundingBox (l, u)))))-  = map (P . recompose)-  -- Enumerate all combinations of selections of lower / higher values. -  . mapM (\(b, (l', u')) -> [(b, l'), (b, u')])-  -- List of [(basis, (lower, upper))]-  . toList-  $ combineP (,) l u+getAllCorners :: (Additive v, Traversable v) => BoundingBox v n -> [Point v n]+getAllCorners (BoundingBox Nothing) = []+getAllCorners (BoundingBox (Just (NonEmptyBoundingBox (l, u))))+  = T.sequence (liftI2 (\a b -> [a,b]) l u)  -- | Get the size of the bounding box - the vector from the (component-wise) --   lesser point to the greater point.-boxExtents :: (AdditiveGroup v) => BoundingBox v -> v-boxExtents = maybe zeroV (\(P l, P h) -> h ^-^ l) . getCorners+boxExtents :: (Additive v, Num n) => BoundingBox v n -> v n+boxExtents = maybe zero (\(l,u) -> u .-. l) . getCorners --- | Create a transformation mapping points from one bounding box to the other.-boxTransform :: (AdditiveGroup v, HasLinearMap v, -                 Fractional (Scalar v), AdditiveGroup (Scalar v), Ord (Basis v))-             => BoundingBox v -> BoundingBox v -> Maybe (Transformation v)+-- | Get the center point in a bounding box.+boxCenter :: (Additive v, Fractional n) => BoundingBox v n -> Maybe (Point v n)+boxCenter = fmap (uncurry (lerp 0.5)) . getCorners++-- | Get the center of a the bounding box of an enveloped object, return+--   'Nothing' for object with empty envelope.+mCenterPoint :: (InSpace v n a, HasBasis v, Enveloped a)+            => a -> Maybe (Point v n)+mCenterPoint = boxCenter . boundingBox++-- | Get the center of a the bounding box of an enveloped object, return+--   the origin for object with empty envelope.+centerPoint :: (InSpace v n a, HasBasis v, Enveloped a)+            => a -> Point v n+centerPoint = fromMaybe origin . mCenterPoint++-- | Create a transformation mapping points from one bounding box to the+--   other. Returns 'Nothing' if either of the boxes are empty.+boxTransform+  :: (Additive v, Fractional n)+  => BoundingBox v n -> BoundingBox v n -> Maybe (Transformation v n) boxTransform u v = do-    ((P ul), _) <- getCorners u-    ((P vl), _) <- getCorners v-    let lin_map = box_scale (v, u) <-> box_scale (u, v)-        box_scale = combineV' (*) . uncurry (combineV' (/)) . mapT boxExtents-        combineV' f x = toVector . combineV f x-    return $ Transformation lin_map lin_map (vl ^-^ box_scale (v, u) ul)+  (P ul, _) <- getCorners u+  (P vl, _) <- getCorners v+  let i  = s (v, u) <-> s (u, v)+      s = liftU2 (*) . uncurry (liftU2 (/)) . mapT boxExtents+  return $ Transformation i i (vl ^-^ s (v, u) ul) --- | Transforms an enveloped thing to fit within a @BoundingBox@.  If it's---   empty, then the result is also @mempty@.-boxFit :: (Enveloped a, Transformable a, Monoid a, Ord (Basis (V a)))-       => BoundingBox (V a) -> a -> a+-- | Transforms an enveloped thing to fit within a @BoundingBox@.  If the+--   bounding box is empty, then the result is also @mempty@.+boxFit+  :: (InSpace v n a, HasBasis v, Enveloped a, Transformable a, Monoid a)+  => BoundingBox v n -> a -> a boxFit b x = maybe mempty (`transform` x) $ boxTransform (boundingBox x) b  -- | Check whether a point is contained in a bounding box (including its edges).-contains-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> Point v -> Bool+contains :: (Additive v, Foldable v, Ord n) => BoundingBox v n -> Point v n -> Bool contains b p = maybe False check $ getCorners b   where-    check (l, h) = F.and (combineP (<=) l p)-                && F.and (combineP (<=) p h)+    check (l, h) = F.and (liftI2 (<=) l p)+                && F.and (liftI2 (<=) p h)  -- | Check whether a point is /strictly/ contained in a bounding box.-contains'-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> Point v -> Bool+contains' :: (Additive v, Foldable v, Ord n) => BoundingBox v n -> Point v n -> Bool contains' b p = maybe False check $ getCorners b   where-    check (l, h) = F.and (combineP (<) l p)-                && F.and (combineP (<) p h)+    check (l, h) = F.and (liftI2 (<) l p)+                && F.and (liftI2 (<) p h)  -- | Test whether the first bounding box is contained inside --   the second.-inside-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> BoundingBox v -> Bool+inside :: (Additive v, Foldable v, Ord n) => BoundingBox v n -> BoundingBox v n -> Bool inside u v = fromMaybe False $ do   (ul, uh) <- getCorners u   (vl, vh) <- getCorners v-  return $ F.and (combineP (>=) ul vl)-        && F.and (combineP (<=) uh vh)+  return $ F.and (liftI2 (>=) ul vl)+        && F.and (liftI2 (<=) uh vh)  -- | Test whether the first bounding box is /strictly/ contained --   inside the second.-inside'-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> BoundingBox v -> Bool+inside' :: (Additive v, Foldable v, Ord n) => BoundingBox v n -> BoundingBox v n -> Bool inside' u v = fromMaybe False $ do   (ul, uh) <- getCorners u   (vl, vh) <- getCorners v-  return $ F.and (combineP (>) ul vl)-        && F.and (combineP (<) uh vh)+  return $ F.and (liftI2 (>) ul vl)+        && F.and (liftI2 (<) uh vh)  -- | Test whether the first bounding box lies outside the second --   (although they may intersect in their boundaries).-outside-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> BoundingBox v -> Bool+outside :: (Additive v, Foldable v, Ord n) => BoundingBox v n -> BoundingBox v n -> Bool outside u v = fromMaybe True $ do   (ul, uh) <- getCorners u   (vl, vh) <- getCorners v-  return $ F.or (combineP (<=) uh vl)-        || F.or (combineP (>=) ul vh)+  return $ F.or (liftI2 (<=) uh vl)+        || F.or (liftI2 (>=) ul vh)  -- | Test whether the first bounding box lies /strictly/ outside the second --   (they do not intersect at all).-outside'-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> BoundingBox v -> Bool+outside' :: (Additive v, Foldable v, Ord n) => BoundingBox v n -> BoundingBox v n -> Bool outside' u v = fromMaybe True $ do   (ul, uh) <- getCorners u   (vl, vh) <- getCorners v-  return $ F.or (combineP (<) uh vl)-        || F.or (combineP (>) ul vh)+  return $ F.or (liftI2 (<) uh vl)+        || F.or (liftI2 (>) ul vh)  -- | Form the largest bounding box contained within this given two --   bounding boxes, or @Nothing@ if the two bounding boxes do not --   overlap at all. intersection-  :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-  => BoundingBox v -> BoundingBox v -> BoundingBox v+  :: (Additive v, Foldable v, Ord n)+  => BoundingBox v n -> BoundingBox v n -> BoundingBox v n intersection u v = maybe mempty (uncurry fromCorners) $ do   (ul, uh) <- getCorners u   (vl, vh) <- getCorners v-  return $ mapT toPoint (combineP max ul vl, combineP min uh vh)+  return (liftI2 max ul vl, liftI2 min uh vh)  -- | Form the smallest bounding box containing the given two bound union.  This --   function is just an alias for @mappend@.-union :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v), Ord (Scalar v))-      => BoundingBox v -> BoundingBox v -> BoundingBox v+union :: (Additive v, Ord n) => BoundingBox v n -> BoundingBox v n -> BoundingBox v n union = mappend --- internals using Map (Basis v) (Scalar v)--- probably paranoia, but decompose might not always---   1. contain basis elements whose component is zero---   2. have basis elements in the same order--fromVector :: (HasBasis v, Ord (Basis v)) => v -> Map (Basis v) (Scalar v)-fromVector = fromList . decompose--toVector :: HasBasis v => Map (Basis v) (Scalar v) -> v-toVector = recompose . toList--toPoint :: HasBasis v => Map (Basis v) (Scalar v) -> Point v-toPoint = P . toVector--combineV :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v))-         => (Scalar v -> Scalar v -> a) -> v -> v -> Map (Basis v) a-combineV f u v = combineDefault zeroV zeroV f (fromVector u) (fromVector v)--combineP :: (HasBasis v, Ord (Basis v), AdditiveGroup (Scalar v))-         => (Scalar v -> Scalar v -> a) -> Point v -> Point v -> Map (Basis v) a-combineP f (P u) (P v) = combineV f u v--combineDefault :: Ord k => a -> b -> (a -> b -> c) -> Map k a -> Map k b -> Map k c-combineDefault a b f = combine g+-- | @boxGrid f box@ returns a grid of regularly spaced points inside+--   the box, such that there are @(1/f)@ points along each dimension.+--   For example, for a 3D box with corners at (0,0,0) and (2,2,2),+--   @boxGrid 0.1@ would yield a grid of approximately 1000 points (it+--   might actually be @11^3@ instead of @10^3@) spaced @0.2@ units+--   apart.+boxGrid+  :: (Traversable v, Additive v, Num n, Enum n)+  => n -> BoundingBox v n -> [Point v n]+boxGrid f = maybe [] (sequenceA . uncurry (liftI2 mkRange)) . getCorners   where-    g Nothing  Nothing  = f a b-    g Nothing  (Just y) = f a y-    g (Just x) Nothing  = f x b-    g (Just x) (Just y) = f x y+    mkRange lo hi = [lo, (1-f)*lo + f*hi .. hi] -combine :: Ord k => (Maybe a -> Maybe b -> c) -> Map k a -> Map k b -> Map k c-combine f am bm = fromDistinctAscList $ merge (toAscList am) (toAscList bm)-  where-    merge [] [] = []-    merge ((x,a):xs) [] = (x, f (Just a) Nothing) : merge xs []-    merge [] ((y,b):ys) = (y, f Nothing (Just b)) : merge [] ys-    merge xs0@((x,a):xs) ys0@((y,b):ys) = case compare x y of-      LT -> (x, f (Just a) Nothing ) : merge xs ys0-      EQ -> (x, f (Just a) (Just b)) : merge xs ys-      GT -> (y, f Nothing  (Just b)) : merge xs0 ys+    -- liftA2 mkRange on the two corner points creates a (Point V2+    -- [n]), where each component is the range of values for that+    -- dimension.  sequenceA then yields a grid of type [Point V2 n].
src/Diagrams/Combinators.hs view
@@ -1,5 +1,8 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types            #-}+{-# LANGUAGE TemplateHaskell       #-} {-# LANGUAGE TypeFamilies          #-} {-# LANGUAGE UndecidableInstances  #-} -----------------------------------------------------------------------------@@ -18,55 +21,76 @@           withEnvelope, withTrace        , phantom, strut-       , pad+       , pad, frame        , extrudeEnvelope, intrudeEnvelope           -- * Binary operations        , atop        , beneath        , beside+       , atDirection           -- * n-ary operations        , appends-       , position, decorateTrail, decorateLocatedTrail, decoratePath-       , cat, cat', CatOpts(..), CatMethod(..)+       , position, atPoints+       , cat, cat'+       , CatOpts(_catMethod, _sep), catMethod, sep+       , CatMethod(..)+       , composeAligned         ) where -import           Data.AdditiveGroup-import           Data.AffineSpace   ((.+^))-import           Data.Default.Class+import           Control.Lens          hiding (beside, ( # ))+import           Data.Default+import           Data.Maybe            (fromJust)+import           Data.Monoid.Deletable (toDeletable)+import           Data.Monoid.MList     (inj)+import           Data.Proxy import           Data.Semigroup-import           Data.VectorSpace+import qualified Data.Tree.DUAL        as D  import           Diagrams.Core-import           Diagrams.Located-import           Diagrams.Path-import           Diagrams.Segment   (Segment (..), straight)-import           Diagrams.Trail     (Trail, trailVertices)-import           Diagrams.TrailLike (fromOffsets)+import           Diagrams.Core.Types   (QDiagram (QD))+import           Diagrams.Direction+import           Diagrams.Names        (named)+import           Diagrams.Segment      (straight) import           Diagrams.Util +import           Linear.Affine+import           Linear.Metric+import           Linear.Vector+ ------------------------------------------------------------ -- Working with envelopes ------------------------------------------------------------  -- | Use the envelope from some object as the envelope for a --   diagram, in place of the diagram's default envelope.-withEnvelope :: (HasLinearMap (V a), Enveloped a, Monoid' m)-           => a -> QDiagram b (V a) m -> QDiagram b (V a) m+--+--   <<diagrams/src_Diagrams_Combinators_withEnvelopeEx.svg#diagram=withEnvelopeEx&width=300>>+--+--   > sqNewEnv =+--   >     circle 1 # fc green+--   >     |||+--   >     (    c # dashingG [0.1,0.1] 0 # lc white+--   >       <> square 2 # withEnvelope (c :: D V2 Double) # fc blue+--   >     )+--   > c = circle 0.8+--   > withEnvelopeEx = sqNewEnv # centerXY # pad 1.5+withEnvelope :: (InSpace v n a, Monoid' m, Enveloped a)+           => a -> QDiagram b v n m -> QDiagram b v n m withEnvelope = setEnvelope . getEnvelope  -- | Use the trace from some object as the trace for a diagram, in --   place of the diagram's default trace.-withTrace :: (HasLinearMap (V a), Traced a, OrderedField (Scalar (V a)), InnerSpace (V a), Monoid' m)-          => a -> QDiagram b (V a) m -> QDiagram b (V a) m+withTrace :: (InSpace v n a, Metric v, OrderedField n, Monoid' m, Traced a)+          => a -> QDiagram b v n m -> QDiagram b v n m withTrace = setTrace . getTrace  -- | @phantom x@ produces a \"phantom\" diagram, which has the same --   envelope and trace as @x@ but produces no output.-phantom :: (Backend b (V a), Enveloped a, Traced a, Monoid' m) => a -> QDiagram b (V a) m-phantom a = mkQD nullPrim (getEnvelope a) (getTrace a) mempty mempty+phantom :: (InSpace v n a, Monoid' m, Enveloped a, Traced a) => a -> QDiagram b v n m+phantom a = QD $ D.leafU ((inj . toDeletable . getEnvelope $ a) <> (inj . toDeletable . getTrace $ a))  -- | @pad s@ \"pads\" a diagram, expanding its envelope by a factor of --   @s@ (factors between 0 and 1 can be used to shrink the envelope).@@ -74,25 +98,32 @@ --   origin, so if the origin is not centered the padding may appear --   \"uneven\".  If this is not desired, the origin can be centered --   (using, e.g., 'centerXY' for 2D diagrams) before applying @pad@.-pad :: ( Backend b v-       , InnerSpace v, OrderedField (Scalar v)-       , Monoid' m )-    => Scalar v -> QDiagram b v m -> QDiagram b v m+pad :: (Metric v, OrderedField n, Monoid' m)+    => n -> QDiagram b v n m -> QDiagram b v n m pad s d = withEnvelope (d # scale s) d +-- | @frame s@ increases the envelope of a diagram by and absolute amount @s@,+--   s is in the local units of the diagram. This function is similar to @pad@,+--   only it takes an absolute quantity and pre-centering should not be+--   necessary.+frame :: (Metric v, OrderedField n, Monoid' m)+        => n -> QDiagram b v n m -> QDiagram b v n m+frame s = over envelope (onEnvelope $ \f x -> f x + s)+ -- | @strut v@ is a diagram which produces no output, but with respect --   to alignment and envelope acts like a 1-dimensional segment --   oriented along the vector @v@, with local origin at its --   center. (Note, however, that it has an empty trace; for 2D struts---   with a nonempty trace see 'strutR2', 'strutX', and 'strutY' from+--   with a nonempty trace see 'strutR2' from --   "Diagrams.TwoD.Combinators".) Useful for manually creating --   separation between two diagrams.-strut :: ( Backend b v, InnerSpace v-         , OrderedField (Scalar v)-         , Monoid' m-         )-      => v -> QDiagram b v m-strut v = mkQD nullPrim env mempty mempty mempty+--+--   <<diagrams/src_Diagrams_Combinators_strutEx.svg#diagram=strutEx&width=300>>+--+--   > strutEx = (circle 1 ||| strut unitX ||| circle 1) # centerXY # pad 1.1+strut :: (Metric v, OrderedField n)+      => v n -> QDiagram b v n m+strut v = QD $ D.leafU (inj . toDeletable $ env)   where env = translate ((-0.5) *^ v) . getEnvelope $ straight v   -- note we can't use 'phantom' here because it tries to construct a   -- trace as well, and segments do not have a trace in general (only@@ -112,10 +143,9 @@ --   the cosine of the difference in angle, and leaving it unchanged --   when this factor is negative. extrudeEnvelope-  :: ( Ord (Scalar v), Num (Scalar v), AdditiveGroup (Scalar v)-     , Floating (Scalar v), HasLinearMap v, InnerSpace v, Monoid' m )-  => v -> QDiagram b v m -> QDiagram b v m-extrudeEnvelope = deformEnvelope 0.5+  :: (Metric v, OrderedField n, Monoid' m)+  => v n -> QDiagram b v n m -> QDiagram b v n m+extrudeEnvelope = deformEnvelope 1  -- | @intrudeEnvelope v d@ asymmetrically \"intrudes\" the envelope of --   a diagram away from the given direction.  All parts of the envelope@@ -125,24 +155,22 @@ --   Note that this could create strange inverted envelopes, where --   @ diameter v d < 0 @. intrudeEnvelope-  :: ( Ord (Scalar v), Num (Scalar v), AdditiveGroup (Scalar v)-     , Floating (Scalar v), HasLinearMap v, InnerSpace v, Monoid' m )-  => v -> QDiagram b v m -> QDiagram b v m-intrudeEnvelope = deformEnvelope (-0.5)+  :: (Metric v, OrderedField n, Monoid' m)+  => v n -> QDiagram b v n m -> QDiagram b v n m+intrudeEnvelope = deformEnvelope (-1)  -- Utility for extrudeEnvelope / intrudeEnvelope deformEnvelope-  :: ( Ord (Scalar v), Num (Scalar v), AdditiveGroup (Scalar v)-     , Floating (Scalar v), HasLinearMap v, InnerSpace v, Monoid' m )-  => (Scalar v) -> v -> QDiagram b v m -> QDiagram b v m-deformEnvelope s v d = setEnvelope (inEnvelope deform $ getEnvelope d) d+  :: (Metric v, OrderedField n, Monoid' m)+  => n -> v n -> QDiagram b v n m -> QDiagram b v n m+deformEnvelope s v = over (envelope . _Wrapping Envelope) deformE   where-    deform = Option . fmap deform' . getOption-    deform' env v'-        | dot > 0 = Max $ getMax (env v') + (dot * s) / magnitude v'+    deformE = fmap deformE'+    deformE' env v'+        | dp > 0    = Max $ getMax (env v') + (dp * s) / quadrance v'         | otherwise = env v'       where-        dot = v' <.> v+        dp = v' `dot` v  ------------------------------------------------------------ -- Combining two objects@@ -151,8 +179,8 @@ -- | @beneath@ is just a convenient synonym for @'flip' 'atop'@; that is, --   @d1 \`beneath\` d2@ is the diagram with @d2@ superimposed on top of --   @d1@.-beneath :: (HasLinearMap v, OrderedField (Scalar v), InnerSpace v, Monoid' m)-     => QDiagram b v m -> QDiagram b v m -> QDiagram b v m+beneath :: (Metric v, OrderedField n, Monoid' m)+     => QDiagram b v n m -> QDiagram b v n m -> QDiagram b v n m beneath = flip atop  infixl 6 `beneath`@@ -167,7 +195,7 @@ --   identity element, in which case the second object is returned --   unchanged). -----   <<diagrams/besideEx.svg#diagram=besideEx&height=200>>+--   <<diagrams/src_Diagrams_Combinators_besideEx.svg#diagram=besideEx&height=200>> -- --   > besideEx = beside (r2 (20,30)) --   >                   (circle 1 # fc orange)@@ -192,9 +220,18 @@ --   To get something like @beside v x1 x2@ whose local origin is --   identified with that of @x2@ instead of @x1@, use @beside --   (negateV v) x2 x1@.-beside :: (Juxtaposable a, Semigroup a) => V a -> a -> a -> a+beside :: (Juxtaposable a, Semigroup a) => Vn a -> a -> a -> a beside v d1 d2 = d1 <> juxtapose v d1 d2 +-- | Place two diagrams (or other juxtaposable objects) adjacent to+--   one another, with the second diagram placed in the direction 'd'+--   from the first.  The local origin of the resulting combined+--   diagram is the same as the local origin of the first.  See the+--   documentation of 'beside' for more information.+atDirection :: (InSpace v n a, Metric v, Floating n, Juxtaposable a, Semigroup a)+            => Direction v n -> a -> a -> a+atDirection = beside . fromDirection+ ------------------------------------------------------------ -- Combining multiple objects ------------------------------------------------------------@@ -203,42 +240,32 @@ --   @x@ in the corresponding direction.  Note that each object in --   @ys@ is positioned beside @x@ /without/ reference to the other --   objects in @ys@, so this is not the same as iterating 'beside'.-appends :: (Juxtaposable a, Monoid' a) => a -> [(V a,a)] -> a+--+--   <<diagrams/src_Diagrams_Combinators_appendsEx.svg#diagram=appendsEx&width=200>>+--+--   > appendsEx = appends c (zip (iterateN 6 (rotateBy (1/6)) unitX) (repeat c))+--   >             # centerXY # pad 1.1+--   >   where c = circle 1+appends :: (Juxtaposable a, Monoid' a) => a -> [(Vn a,a)] -> a appends d1 apps = d1 <> mconcat (map (\(v,d) -> juxtapose v d1 d) apps)  -- | Position things absolutely: combine a list of objects --   (e.g. diagrams or paths) by assigning them absolute positions in --   the vector space of the combined object.-position :: (HasOrigin a, Monoid' a) => [(Point (V a), a)] -> a+--+--   <<diagrams/src_Diagrams_Combinators_positionEx.svg#diagram=positionEx&height=300>>+--+--   > positionEx = position (zip (map mkPoint [-3, -2.8 .. 3]) (repeat spot))+--   >   where spot      = circle 0.2 # fc black+--   >         mkPoint :: Double -> P2 Double+--   >         mkPoint x = p2 (x,x*x)+position :: (InSpace v n a, HasOrigin a, Monoid' a) => [(Point v n, a)] -> a position = mconcat . map (uncurry moveTo) --- | Combine a list of diagrams (or paths) by using them to---   \"decorate\" a trail, placing the local origin of one object at---   each successive vertex of the trail.  The first vertex of the---   trail is placed at the origin.  If the trail and list of objects---   have different lengths, the extra tail of the longer one is---   ignored.-decorateTrail :: (InnerSpace (V a), OrderedField (Scalar (V a)), HasOrigin a, Monoid' a)-              => Trail (V a) -> [a] -> a-decorateTrail = decorateLocatedTrail . (`at` origin)---- | Combine a list of diagrams (or paths) by using them to---   \"decorate\" a concretely located trail, placing the local origin---   of one object at each successive vertex of the trail. If the---   trail and list of objects have different lengths, the extra tail---   of the longer one is ignored.-decorateLocatedTrail :: (InnerSpace (V a), OrderedField (Scalar (V a)), HasOrigin a, Monoid' a)-              => Located (Trail (V a)) -> [a] -> a-decorateLocatedTrail t = position . zip (trailVertices t)---- | Combine a list of diagrams (or paths) by using them to---   \"decorate\" a path, placing the local origin of one object at---   each successive vertex of the path.  If the path and list of objects---   have different lengths, the extra tail of the longer one is---   ignored.-decoratePath :: (InnerSpace (V a), OrderedField (Scalar (V a)), HasOrigin a, Monoid' a)-             => Path (V a) -> [a] -> a-decoratePath p = position . zip (concat $ pathVertices p)+-- | Curried version of @position@, takes a list of points and a list of+--   objects.+atPoints :: (InSpace v n a, HasOrigin a, Monoid' a) => [Point v n] -> [a] -> a+atPoints ps as = position $ zip ps as  -- | Methods for concatenating diagrams. data CatMethod = Cat     -- ^ Normal catenation: simply put diagrams@@ -257,36 +284,38 @@                          --   of separation, diagrams may overlap.  -- | Options for 'cat''.-data CatOpts v = CatOpts { catMethod       :: CatMethod-                             -- ^ Which 'CatMethod' should be used:-                             --   normal catenation (default), or-                             --   distribution?-                         , sep             :: Scalar v-                             -- ^ How much separation should be used-                             --   between successive diagrams-                             --   (default: 0)?  When @catMethod =-                             --   Cat@, this is the distance between-                             --   /envelopes/; when @catMethod =-                             --   Distrib@, this is the distance-                             --   between /origins/.-                         , catOptsvProxy__ :: Proxy v-                             -- ^ This field exists solely to aid type inference;-                             --   please ignore it.+data CatOpts n = CatOpts { _catMethod    :: CatMethod+                         , _sep          :: n+                         , catOptsvProxy :: Proxy n                          }  -- The reason the proxy field is necessary is that without it, -- altering the sep field could theoretically change the type of a--- CatOpts record.  This causes problems when writing an expression--- like @with { sep = 10 }@, because knowing the type of the whole+-- CatOpts record.  This causes problems when using record update, as+-- in @with { _sep = 10 }@, because knowing the type of the whole -- expression does not tell us anything about the type of @with@, and -- therefore the @Num (Scalar v)@ constraint cannot be satisfied.--- Adding the Proxy field constrains the type of @with@ in @with {sep--- = 10}@ to be the same as the type of the whole expression.+-- Adding the Proxy field constrains the type of @with@ in @with {_sep+-- = 10}@ to be the same as the type of the whole expression.  Note+-- this is not a problem when using the 'sep' lens, as its type is+-- more restricted. -instance Num (Scalar v) => Default (CatOpts v) where-  def = CatOpts { catMethod       = Cat-                , sep             = 0-                , catOptsvProxy__ = Proxy+makeLensesWith (lensRules & generateSignatures .~ False) ''CatOpts++-- | Which 'CatMethod' should be used:+--   normal catenation (default), or distribution?+catMethod :: Lens' (CatOpts n) CatMethod++-- | How much separation should be used between successive diagrams+--   (default: 0)?  When @catMethod = Cat@, this is the distance between+--   /envelopes/; when @catMethod = Distrib@, this is the distance+--   between /origins/.+sep :: Lens' (CatOpts n) n++instance Num n => Default (CatOpts n) where+  def = CatOpts { _catMethod    = Cat+                , _sep          = 0+                , catOptsvProxy = Proxy                 }  -- | @cat v@ positions a list of objects so that their local origins@@ -297,10 +326,8 @@ -- --   See also 'cat'', which takes an extra options record allowing --   certain aspects of the operation to be tweaked.-cat :: ( Juxtaposable a, Monoid' a, HasOrigin a-       , InnerSpace (V a), OrderedField (Scalar (V a))-       )-       => V a -> [a] -> a+cat :: (InSpace v n a, Metric v, Floating n, Juxtaposable a, Monoid' a, HasOrigin a)+       => v n -> [a] -> a cat v = cat' v def  -- | Like 'cat', but taking an extra 'CatOpts' arguments allowing the@@ -315,18 +342,58 @@ --     default is 0. -- --   'CatOpts' is an instance of 'Default', so 'with' may be used for---   the second argument, as in @cat' (1,2) with {sep = 2}@.+--   the second argument, as in @cat' (1,2) (with & sep .~ 2)@. -----   Note that @cat' v with {catMethod = Distrib} === mconcat@+--   Note that @cat' v (with & catMethod .~ Distrib) === mconcat@ --   (distributing with a separation of 0 is the same as --   superimposing).-cat' :: ( Juxtaposable a, Monoid' a, HasOrigin a-        , InnerSpace (V a), OrderedField (Scalar (V a))-        )-     => V a -> CatOpts (V a) -> [a] -> a-cat' v (CatOpts { catMethod = Cat, sep = s }) = foldB comb mempty+cat' :: (InSpace v n a, Metric v, Floating n, Juxtaposable a, Monoid' a, HasOrigin a)+     => v n -> CatOpts n -> [a] -> a+cat' v (CatOpts { _catMethod = Cat, _sep = s }) = foldB comb mempty   where comb d1 d2 = d1 <> (juxtapose v d1 d2 # moveOriginBy vs)-        vs = s *^ normalized (negateV v)+        vs = s *^ signorm (negated v) -cat' v (CatOpts { catMethod = Distrib, sep = s }) =-  position . zip (iterate (.+^ (s *^ normalized v)) origin)+cat' v (CatOpts { _catMethod = Distrib, _sep = s }) =+  position . zip (iterate (.+^ (s *^ signorm v)) origin)++-- | Compose a list of diagrams using the given composition function,+--   first aligning them all according to the given alignment, /but/+--   retain the local origin of the first diagram, as it would be if+--   the composition function were applied directly.  That is,+--   @composeAligned algn comp@ is equivalent to @translate v . comp+--   . map algn@ for some appropriate translation vector @v@.+--+--   Unfortunately, this only works for diagrams (and not, say, paths)+--   because there is no most general type for alignment functions,+--   and no generic way to find out what an alignment function does to+--   the origin of things.  (However, it should be possible to make a+--   version of this function that works /specifically/ on paths, if+--   such a thing were deemed useful.)+--+--   <<diagrams/src_Diagrams_Combinators_alignedEx1.svg#diagram=alignedEx1&width=400>>+--+--   > alignedEx1 = (hsep 2 # composeAligned alignT) (map circle [1,3,5,2])+--   >            # showOrigin+--   >            # frame 0.5+--+--   <<diagrams/src_Diagrams_Combinators_alignedEx2.svg#diagram=alignedEx2&width=400>>+--+--   > alignedEx2 = (mconcat # composeAligned alignTL) [circle 1, square 1, triangle 1, pentagon 1]+--   >            # showOrigin+--   >            # frame 0.1+composeAligned+  :: (Monoid' m, Floating n, Ord n, Metric v)+  => (QDiagram b v n m -> QDiagram b v n m)    -- ^ Alignment function+  -> ([QDiagram b v n m] -> QDiagram b v n m)  -- ^ Composition function+  -> ([QDiagram b v n m] -> QDiagram b v n m)+composeAligned _ combine [] = combine []+composeAligned algn comb (d:ds) = (comb $ map algn (d:ds)) # moveOriginTo l+  where+    mss = ( (() .>> d)   -- qualify first to avoid stomping on an existing () name+          # named ()     -- Mark the origin+          # algn         -- Apply the alignment function+          )+          -- then find out what happened to the origin+        ^. subMap . _Wrapped . Control.Lens.at (toName ())+    l   = location . head . fromJust $ mss+          -- the fromJust is Justified since we put the () name in
src/Diagrams/Coordinates.hs view
@@ -1,5 +1,7 @@-{-# LANGUAGE TypeFamilies  #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeOperators        #-}+{-# LANGUAGE UndecidableInstances #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Coordinates@@ -10,28 +12,20 @@ -- Nice syntax for constructing and pattern-matching on literal -- points and vectors. ----- NOTE: to avoid clashing with the '(&)' operator from the @lens@--- package, this module is not re-exported by "Diagrams.Prelude".  To--- make use of the contents of this module, you must explicitly import--- it.--- -----------------------------------------------------------------------------  module Diagrams.Coordinates-    ( (:&)(..), Coordinates(..) )+    ( (:&)(..), Coordinates(..)+    )     where --- | A pair of values, with a convenient infix (left-associative)---   data constructor.-data a :& b = a :& b-  deriving (Eq, Ord, Show)--infixl 7 :&-+import           Data.Kind       (Type)+import           Diagrams.Points+import           Linear          (V2 (..), V3 (..), V4 (..))  -- | Types which are instances of the @Coordinates@ class can be---   constructed using '&' (for example, a three-dimensional vector---   could be constructed by @1 & 6 & 3@), and deconstructed using+--   constructed using '^&' (for example, a three-dimensional vector+--   could be constructed by @1 ^& 6 ^& 3@), and deconstructed using --   'coords'.  A common pattern is to use 'coords' in conjunction --   with the @ViewPatterns@ extension, like so: --@@ -42,33 +36,53 @@ class Coordinates c where    -- | The type of the final coordinate.-  type FinalCoord c    :: *+  type FinalCoord c    :: Type    -- | The type of everything other than the final coordinate.-  type PrevDim c       :: *+  type PrevDim c       :: Type    -- | Decomposition of @c@ into applications of ':&'.-  type Decomposition c :: *+  type Decomposition c :: Type     -- Decomposition c = Decomposition (PrevDim c) :& FinalCoord c  (essentially)    -- | Construct a value of type @c@ by providing something of one   --   less dimension (which is perhaps itself recursively constructed-  --   using @(&)@) and a final coordinate.  For example,+  --   using @(^&)@) and a final coordinate.  For example,   --   -- @-  -- 2 & 3 :: P2-  -- 3 & 5 & 6 :: R3+  -- 2 ^& 3 :: P2+  -- 3 ^& 5 ^& 6 :: V3   -- @   ---  --   Note that @&@ is left-associative.-  (&)    :: PrevDim c -> FinalCoord c -> c+  --   Note that @^&@ is left-associative.+  (^&)    :: PrevDim c -> FinalCoord c -> c +  -- | Prefix synonym for @^&@. pr stands for pair of @PrevDim@, @FinalCoord@+  pr      :: PrevDim c -> FinalCoord c -> c+  pr = (^&)+   -- | Decompose a value of type @c@ into its constituent coordinates,   --   stored in a nested @(:&)@ structure.   coords :: c -> Decomposition c -infixl 7 &+infixl 7 ^& +-- | A pair of values, with a convenient infix (left-associative)+--   data constructor.+data a :& b = a :& b+  deriving (Eq, Ord, Show)++infixl 7 :&++-- Instance for :& (the buck stops here)+instance Coordinates (a :& b) where+  type FinalCoord (a :& b) = b+  type PrevDim (a :& b) = a+  type Decomposition (a :& b) = a :& b+  x ^& y                    = x :& y+  coords (x :& y)           = x :& y++ -- Some standard instances for plain old tuples  instance Coordinates (a,b) where@@ -76,7 +90,7 @@   type PrevDim (a,b)       = a   type Decomposition (a,b) = a :& b -  x & y                    = (x,y)+  x ^& y                   = (x,y)   coords (x,y)             = x :& y  instance Coordinates (a,b,c) where@@ -84,7 +98,7 @@   type PrevDim (a,b,c)       = (a,b)   type Decomposition (a,b,c) = Decomposition (a,b) :& c -  (x,y) & z                  = (x,y,z)+  (x,y) ^& z                  = (x,y,z)   coords (x,y,z)             = coords (x,y) :& z  instance Coordinates (a,b,c,d) where@@ -92,5 +106,40 @@   type PrevDim (a,b,c,d)       = (a,b,c)   type Decomposition (a,b,c,d) = Decomposition (a,b,c) :& d -  (w,x,y) & z                  = (w,x,y,z)+  (w,x,y)  ^& z                = (w,x,y,z)   coords (w,x,y,z)             = coords (w,x,y) :& z++instance Coordinates (v n) => Coordinates (Point v n) where+  type FinalCoord (Point v n)    = FinalCoord (v n)+  type PrevDim (Point v n)       = PrevDim (v n)+  type Decomposition (Point v n) = Decomposition (v n)++  x ^& y       = P (x ^& y)+  coords (P v) = coords v++-- instances for linear++instance Coordinates (V2 n) where+  type FinalCoord (V2 n)    = n+  type PrevDim (V2 n)       = n+  type Decomposition (V2 n) = n :& n++  x ^& y          = V2 x y+  coords (V2 x y) = x :& y++instance Coordinates (V3 n) where+  type FinalCoord (V3 n)    = n+  type PrevDim (V3 n)       = V2 n+  type Decomposition (V3 n) = n :& n :& n++  V2 x y ^& z       = V3 x y z+  coords (V3 x y z) = x :& y :& z++instance Coordinates (V4 n) where+  type FinalCoord (V4 n)    = n+  type PrevDim (V4 n)       = V3 n+  type Decomposition (V4 n) = n :& n :& n :& n++  V3 x y z ^& w       = V4 x y z w+  coords (V4 x y z w) = x :& y :& z :& w+
src/Diagrams/CubicSpline.hs view
@@ -1,6 +1,10 @@+{-# LANGUAGE ConstraintKinds  #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies     #-}+{-# LANGUAGE TypeOperators    #-}+ {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.CubicSpline@@ -8,61 +12,68 @@ -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com ----- A /cubic spline/ is a smooth, connected sequence of cubic curves--- passing through a given sequence of points.  This module provides--- the 'cubicSpline' method, which can be used to create closed or--- open cubic splines from a list of points.  For access to the--- internals of the spline generation algorithm (including in--- particular a solver for cyclic tridiagonal systems of linear--- equations), see "Diagrams.CubicSpline.Internal".+-- A /cubic spline/ is a smooth, connected sequence of cubic curves.+-- This module provides two methods for constructing splines. --+-- The 'cubicSpline' method can be used to create closed or open cubic+-- splines from a list of points. The resulting splines /pass through/+-- all the control points, but depend on the control points in a+-- "global" way (that is, changing one control point may alter the+-- entire curve).  For access to the internals of the spline+-- generation algorithm, see "Diagrams.CubicSpline.Internal".+--+-- 'bspline' creates a cubic B-spline, which starts and ends at the+-- first and last control points, but does not necessarily pass+-- through any of the other control points.  It depends on the control+-- points in a "local" way, that is, changing one control point will+-- only affect a local portion of the curve near that control point.+-- ----------------------------------------------------------------------------- module Diagrams.CubicSpline        (          -- * Constructing paths from cubic splines          cubicSpline+       , BSpline+       , bspline        ) where +import           Control.Lens                  (view)+ import           Diagrams.Core-import           Diagrams.Core.Points+import           Diagrams.CubicSpline.Boehm import           Diagrams.CubicSpline.Internal-import           Diagrams.Located              (at)-import           Diagrams.Located-import           Diagrams.Path+import           Diagrams.Located              (Located, at, mapLoc) import           Diagrams.Segment import           Diagrams.Trail import           Diagrams.TrailLike            (TrailLike (..)) --- for e.g. the Fractional (Double, Double) instance-import           Data.NumInstances.Tuple       ()--import           Control.Newtype-import           Data.Semigroup-import           Data.VectorSpace+import           Linear.Affine+import           Linear.Metric  -- | Construct a spline path-like thing of cubic segments from a list of --   vertices, with the first vertex as the starting point.  The first --   argument specifies whether the path should be closed. -----   <<diagrams/cubicSplineEx.svg#diagram=cubicSplineEx&width=600>>+--   <<diagrams/src_Diagrams_CubicSpline_cubicSplineEx.svg#diagram=cubicSplineEx&width=600>> -- --   > pts = map p2 [(0,0), (2,3), (5,-2), (-4,1), (0,3)]---   > dot = circle 0.2 # fc blue # lw 0---   > mkPath closed = position (zip pts (repeat dot))---   >              <> cubicSpline closed pts # lw 0.05+--   > spot = circle 0.2 # fc blue # lw none+--   > mkPath closed = position (zip pts (repeat spot))+--   >              <> cubicSpline closed pts --   > cubicSplineEx = (mkPath False ||| strutX 2 ||| mkPath True) --   >               # centerXY # pad 1.1 -- --   For more information, see <http://mathworld.wolfram.com/CubicSpline.html>.-cubicSpline :: (TrailLike t, Fractional (V t)) => Bool -> [Point (V t)] -> t-cubicSpline c [] = trailLike . closeIf c $ emptyLine `at` origin-cubicSpline c ps = flattenBeziers . map f . solveCubicSplineCoefficients c . map unpack $ ps+cubicSpline :: (V t ~ v, N t ~ n, TrailLike t, Fractional (v n)) => Bool -> [Point v n] -> t+cubicSpline closed []  = trailLike . closeIf closed $ emptyLine `at` origin+cubicSpline closed [p] = trailLike . closeIf closed $ emptyLine `at` p+cubicSpline closed ps  = flattenBeziers . map f . solveCubicSplineCoefficients closed . map (view lensP) $ ps   where     f [a,b,c,d] = [a, (3*a+b)/3, (3*a+2*b+c)/3, a+b+c+d]     flattenBeziers bs@((b:_):_)-      = trailLike . closeIf c $ lineFromSegments (map bez bs) `at` P b+      = trailLike . closeIf closed $ lineFromSegments (map bez bs) `at` P b     bez [a,b,c,d] = bezier3 (b - a) (c - a) (d - a) -closeIf :: (InnerSpace v, OrderedField (Scalar v))-        => Bool -> Located (Trail' Line v) -> Located (Trail v)+closeIf :: (Metric v, OrderedField n)+        => Bool -> Located (Trail' Line v n) -> Located (Trail v n) closeIf c = mapLoc (if c then wrapLoop . glueLine else wrapLine)
+ src/Diagrams/CubicSpline/Boehm.hs view
@@ -0,0 +1,149 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies     #-}+{-# LANGUAGE TypeOperators    #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.CubicSpline.Boehm+-- Copyright   :  (c) 2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Boehm's algorithm for converting a cubic B-spline into a sequence+-- of cubic Bezier curves.+--+-- See+--+--   * Thomas W. Sederberg, /An Introduction to B-Spline Curves/,+--     <http://web.archive.org/web/20120227050519/http://tom.cs.byu.edu/~455/bs.pdf>+--+--   * Lyle Ramshaw, /Blossoming: A Connect-the-Dots Approach to Splines/,+--     <http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-19.pdf>+--+-----------------------------------------------------------------------------+module Diagrams.CubicSpline.Boehm+       ( BSpline+       , bsplineToBeziers+       , bspline+       ) where++import           Data.List          (sort, tails)+import           Diagrams.Core      (N, Point, V, origin)+import           Diagrams.Located   (at, loc, unLoc)+import           Diagrams.Segment   (FixedSegment (..), fromFixedSeg)+import           Diagrams.TrailLike (TrailLike, fromLocSegments)+import           Diagrams.Util      (iterateN)+import           Linear.Vector      (Additive)+import           Linear.Vector.Compat (lerp)++type BSpline v n = [Point v n]++-- | @affineCombo a b t x y@ computes an affine combination of x and y+--   which lies at parameter t, if x has parameter a and y has parameter b.+--   The usual @lerp@ arises by giving x parameter 0 and y parameter 1.+affineCombo :: (Additive f, Fractional a) => a -> a -> a -> f a -> f a -> f a+affineCombo a b t = lerp ((t-a)/(b-a))++-- | @windows k xs@ yields all the length-@k@ windows from @xs@, e.g.+--   @windows 3 [a,b,c,d,e] == [[a,b,c], [b,c,d], [c,d,e]]@.+windows :: Int -> [a] -> [[a]]+windows k = takeWhile ((==k) . length) . map (take k) . tails++-- | @extend k xs@ extends @xs@ on both ends by prepending @k@ copies+-- of its head and appending @k@ copies of its last element.  For example,+-- @extend 2 [1..5] == [1,1,1,2,3,4,5,5,5]@.+extend :: Int -> [a] -> [a]+extend k xs = replicate k (head xs) ++ xs ++ replicate k (last xs)++-- | A "polar point" is a point along with three knot values.+--   We consider the "blossom" of a cubic spline, a 3-ary symmetric+--   polynomial; a polar point consists of 3 values paired with the+--   output of the blossom at those input values.  Blossoms have nice+--   affine properties so this makes it easy to keep track of how+--   points may be combined to yield other points of interest.+--+--   Invariant: knot values are in nondecreasing order.+data PolarPt v n = PP { unPP :: Point v n, _knots :: [n] }++mkPolarPt :: Ord n => Point v n -> [n] -> PolarPt v n+mkPolarPt pt kts = PP pt (sort kts)++-- | Precondition: the knots of the two polar points overlap, like abc+--   and bcd.  The @Int@ should be 0 or 1, indicating which knot to+--   replicate (0 means to replicate b, yielding bbc, 1 means to+--   replicate c, yielding bcc).+combine+  :: (Additive v, Fractional n, Ord n)+  => Int -> PolarPt v n -> PolarPt v n -> PolarPt v n+combine k (PP pt1 kts1) (PP pt2 kts2)+  = mkPolarPt+      (affineCombo (head kts1) (last kts2) newKt pt1 pt2)+      (newKt : drop 1 kts1)+  where+    newKt = kts2 !! k++-- | Convert a uniform cubic B-spline to a sequence of cubic beziers.+--   (/Uniform/ refers to the fact that the knots are assumed to be+--   evenly spaced, with no duplicates.)  The knots at the end are+--   replicated so the cubic spline begins and ends at the first and+--   last control points, tangent to the line from the end control+--   point to the next.+bsplineToBeziers+  :: (Additive v, Fractional n, Num n, Ord n)+  => BSpline v n+  -> [FixedSegment v n]+bsplineToBeziers controls = beziers+  where+    n                            = length controls+    numKnots                     = n + 2+    knots                        = iterateN numKnots (+1/(fromIntegral numKnots - 1)) 0++    -- The control points are P(a,b,c), P(b,c,d), P(c,d,e), and so on.+    controls' = zipWith mkPolarPt (extend 2 controls) (windows 3 $ extend 2 knots)++    -- The bezier internal control points are affine combinations of+    -- the spline control points.+    bezierControls        = map combineC (windows 2 controls')+    combineC [pabc, pbcd] = (combine 0 pabc pbcd, combine 1 pabc pbcd)+    combineC _ = error "combineC must be called on a list of length 2"++    -- The bezier end points are affine combinations of the bezier+    -- control points.+    bezierEnds                   = map combineE (windows 2 bezierControls)+    combineE [(_,pabb),(pbbc,_)] = combine 0 pabb pbbc+    combineE _ = error "combineE must be called on a list of length 2"++    -- Finally, we actually put together the generated bezier segments.+    beziers                      = zipWith mkBezier (drop 1 bezierControls) (windows 2 bezierEnds)+      where+        mkBezier (paab,pabb) [paaa,pbbb]+          = FCubic (unPP paaa) (unPP paab) (unPP pabb) (unPP pbbb)+        mkBezier _ _ = error "mkBezier must be called on a list of length 2"++    -- Note that the above algorithm works in any dimension but is+    -- very specific to *cubic* splines.  This can of course be+    -- generalized to higher degree splines but keeping track of+    -- everything gets a bit more complicated; to be honest I am not+    -- quite sure how to do it.++-- | Generate a uniform cubic B-spline from the given control points.+--   The spline starts and ends at the first and last control points,+--   and is tangent to the line to the second(-to-last) control point.+--   It does not necessarily pass through any of the other control+--   points.+--+--   <<diagrams/src_Diagrams_CubicSpline_Boehm_bsplineEx.svg#diagram=bsplineEx&width=300>>+--+--   > pts = map p2 [(0,0), (2,3), (5,-2), (-4,1), (0,3)]+--   > spot = circle 0.2 # fc blue # lw none+--   > bsplineEx = mconcat+--   >   [ position (zip pts (repeat spot))+--   >   , bspline pts+--   >   ]+--   >   # frame 0.5++bspline :: (TrailLike t, V t ~ v, N t ~ n) => BSpline v n -> t+bspline = fromLocSegments . fixup . map fromFixedSeg . bsplineToBeziers+  where+    fixup []        = [] `at` origin+    fixup (b1:rest) = (unLoc b1 : map unLoc rest) `at` loc b1
src/Diagrams/CubicSpline/Internal.hs view
@@ -1,7 +1,3 @@-{-# LANGUAGE TypeFamilies-           , FlexibleContexts-  #-}-{-# OPTIONS_GHC -fno-warn-name-shadowing #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.CubicSpline@@ -18,76 +14,19 @@ module Diagrams.CubicSpline.Internal        (          -- * Solving for spline coefficents-         solveTriDiagonal-       , solveCyclicTriDiagonal-       , solveCubicSplineDerivatives+         solveCubicSplineDerivatives        , solveCubicSplineDerivativesClosed        , solveCubicSplineCoefficients        ) where -import Data.List---- | Solves a system of the form 'A*X=D' for 'x' where 'A' is an ---   'n' by 'n' matrix with 'bs' as the main diagonal and ---   'as' the diagonal below and 'cs' the diagonal above.---   See: <http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm>-solveTriDiagonal :: Fractional a => [a] -> [a] -> [a] -> [a] -> [a]-solveTriDiagonal as (b0:bs) (c0:cs) (d0:ds) = h cs' ds'-  where-    cs' = c0 / b0 : f cs' as bs cs-    f _ [_] _ _ = []-    f (c':cs') (a:as) (b:bs) (c:cs) = c / (b - c' * a) : f cs' as bs cs-    f _ _ _ _ = error "solveTriDiagonal.f: impossible!"--    ds' = d0 / b0 : g ds' as bs cs' ds -    g _ [] _ _ _ = []-    g (d':ds') (a:as) (b:bs) (c':cs') (d:ds) = (d - d' * a)/(b - c' * a) : g ds' as bs cs' ds-    g _ _ _ _ _ = error "solveTriDiagonal.g: impossible!"-    -    h _ [d] = [d]-    h (c:cs) (d:ds) = let xs@(x:_) = h cs ds in d - c * x : xs-    h _ _ = error "solveTriDiagonal.h: impossible!"--solveTriDiagonal _ _ _ _ = error "arguments 2,3,4 to solveTriDiagonal must be nonempty"---- Helper that applies the passed function only to the last element of a list-modifyLast :: (a -> a) -> [a] -> [a]-modifyLast _ []     = []-modifyLast f [a]    = [f a]-modifyLast f (a:as) = a : modifyLast f as---- Helper that builds a list of length n of the form: '[s,m,m,...,m,m,e]'-sparseVector :: Int -> a -> a -> a -> [a]-sparseVector n s m e -    | n < 1     = []-    | otherwise = s : h (n - 1)-  where -    h 1 = [e]-    h n = m : h (n - 1)-    --- | Solves a system similar to the tri-diagonal system using a special case---   of the Sherman-Morrison formula <http://en.wikipedia.org/wiki/Sherman-Morrison_formula>.---   This code is based on /Numerical Recpies in C/'s @cyclic@ function in section 2.7.-solveCyclicTriDiagonal :: Fractional a => [a] -> [a] -> [a] -> [a] -> a -> a -> [a]-solveCyclicTriDiagonal as (b0:bs) cs ds alpha beta = zipWith ((+) . (fact *)) zs xs-  where-    l = length ds-    gamma = -b0-    us = sparseVector l gamma 0 alpha-    -    bs' = (b0 - gamma) : modifyLast (subtract (alpha*beta/gamma)) bs-    -    xs@(x:_) = solveTriDiagonal as bs' cs ds-    zs@(z:_) = solveTriDiagonal as bs' cs us-    -    fact = -(x + beta * last xs / gamma) / (1.0 + z + beta * last zs / gamma)+import           Diagrams.Solve.Tridiagonal -solveCyclicTriDiagonal _ _ _ _ _ _ = error "second argument to solveCyclicTriDiagonal must be nonempty"+import           Data.List  -- | Use the tri-diagonal solver with the appropriate parameters for an open cubic spline. solveCubicSplineDerivatives :: Fractional a => [a] -> [a] solveCubicSplineDerivatives (x:xs) = solveTriDiagonal as bs as ds-  where +  where     as = replicate (l - 1) 1     bs = 2 : replicate (l - 2) 4 ++ [2]     l  = length ds@@ -99,7 +38,7 @@ -- | Use the cyclic-tri-diagonal solver with the appropriate parameters for a closed cubic spline. solveCubicSplineDerivativesClosed :: Fractional a => [a] -> [a] solveCubicSplineDerivativesClosed xs = solveCyclicTriDiagonal as bs as ds 1 1-  where +  where     as = replicate (l - 1) 1     bs = replicate l 4     l  = length xs@@ -109,11 +48,11 @@  -- | Use the cyclic-tri-diagonal solver with the appropriate parameters for a closed cubic spline. solveCubicSplineCoefficients :: Fractional a => Bool -> [a] -> [[a]]-solveCubicSplineCoefficients closed xs = +solveCubicSplineCoefficients closed xs =     [ [x,d,3*(x1-x)-2*d-d1,2*(x-x1)+d+d1]     | (x,x1,d,d1) <- zip4 xs' (tail xs') ds' (tail ds')     ]-  where +  where     ds | closed    = solveCubicSplineDerivativesClosed xs        | otherwise = solveCubicSplineDerivatives xs     close as | closed    = as ++ [head as]
+ src/Diagrams/Deform.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE UndecidableInstances  #-}++module Diagrams.Deform+       ( Deformation(..)+       , Deformable(..)+       , asDeformation+       ) where++import           Control.Lens        (mapped, over, _Wrapped)+import           Data.Monoid         hiding ((<>))+import           Data.Semigroup+import           Prelude++import           Diagrams.Core+import           Diagrams.Located+import           Diagrams.Parametric+import           Diagrams.Path+import           Diagrams.Segment+import           Diagrams.Trail++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector++------------------------------------------------------------+-- Deformations++-- | @Deformations@ are a superset of the affine transformations+--   represented by the 'Transformation' type.  In general they are not+--   invertible.  @Deformation@s include projective transformations.+--   @Deformation@ can represent other functions from points to points+--   which are "well-behaved", in that they do not introduce small wiggles.+newtype Deformation v u n = Deformation (Point v n -> Point u n)++instance Semigroup (Deformation v v n) where+  (Deformation p1) <> (Deformation p2) = Deformation (p1 . p2)++instance Monoid (Deformation v v n) where+  mappend = (<>)+  mempty = Deformation id++class Deformable a b where+  -- | @deform' epsilon d a@ transforms @a@ by the deformation @d@.+  -- If the type of @a@ is not closed under projection, approximate+  -- to accuracy @epsilon@.+  deform' :: N a -> Deformation (V a) (V b) (N a) -> a -> b++  -- | @deform d a@ transforms @a@ by the deformation @d@.+  -- If the type of @a@ is not closed under projection, @deform@+  -- should call @deform'@ with some reasonable default value of+  -- @epsilon@.+  deform :: Deformation (V a) (V b) (N a) -> a -> b++-- | @asDeformation@ converts a 'Transformation' to a 'Deformation' by+-- discarding the inverse transform.  This allows reusing+-- @Transformation@s in the construction of @Deformation@s.+asDeformation :: (Additive v, Num n) => Transformation v n -> Deformation v v n+asDeformation t = Deformation (papply t)++------------------------------------------------------------+-- Instances++instance r ~ Point u n => Deformable (Point v n) r where+  deform' = const deform++  deform (Deformation l) = l++-- | Cubic curves are not closed under perspective projections.+-- Therefore @Segment@s are not an instance of Deformable.  However,+-- the deformation of a @Segment@ can be approximated to arbitrary+-- precision by a series of @Segment@s.  @deformSegment@ does this,+-- which allows types built from lists of @Segment@s to themselves be+-- @Deformable@.+deformSegment :: (Metric v, Metric u, OrderedField n)+   => n -> Deformation v u n -> FixedSegment v n -> [FixedSegment u n]+deformSegment epsilon t = go (0::Int)+  where+    go n s+      | n == 100               = [approx t s]+      | goodEnough epsilon t s = [approx t s]+      | otherwise              = concatMap (go (n+1)) [s1, s2]+      where+        (s1, s2) = splitAtParam s 0.5+-- deformSegment epsilon t s+--     | goodEnough epsilon t s = [approx t s]+--     | otherwise              = concatMap (deformSegment epsilon t) [s1, s2]+--   where+--     (s1, s2) = splitAtParam s 0.5++approx :: Deformation v u n -> FixedSegment v n -> FixedSegment u n+approx t (FLinear p0 p1)      = FLinear (deform t p0) (deform t p1)+approx t (FCubic p0 c1 c2 p1) = FCubic (f p0) (f c1) (f c2) (f p1)+  where f = deform t++goodEnough :: (Metric v, Metric u, OrderedField n) => n -> Deformation v u n -> FixedSegment v n -> Bool+goodEnough e t s =+    all (< e) [norm $ deform t (s `atParam` u) .-. approx t s `atParam` u+              | u <- [0.25, 0.5, 0.75]]++instance (Metric v, Metric u, OrderedField n, r ~ Located (Trail u n))+    => Deformable (Located (Trail v n)) r where+  deform' eps p t+    | isLine $ unLoc t  = line `at` p0+    | otherwise = glueTrail line `at` p0+    where+      segs = concatMap (deformSegment eps p) $ fixTrail t+      p0 = case segs of+             (FLinear start _:_)    -> start+             (FCubic start _ _ _:_) -> start+             _                      -> deform p (loc t)+      line = trailFromSegments $ map (unLoc . fromFixedSeg) segs++  deform p t = deform' (0.01 * extent) p t+    where+      -- estimate the "size" of the Trail' as+      -- the maximum distance to any vertex+      extent = maximum . map dist . trailVertices $ t+      dist pt = norm $ pt .-. loc t++instance (Metric v, Metric u, OrderedField n, r ~ Path u n) => Deformable (Path v n) r where+  deform' eps p = over (_Wrapped . mapped) (deform' eps p)+  deform p      = over (_Wrapped . mapped) (deform p)
+ src/Diagrams/Direction.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE DeriveFunctor        #-}+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeOperators        #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Direction+-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Type for representing directions, polymorphic in vector space+--+-----------------------------------------------------------------------------++module Diagrams.Direction+       ( Direction+       , _Dir+       , direction, dir, fromDirection, fromDir+       , angleBetweenDirs+       , dirBetween+       ) where++import           Control.Lens   (Iso', iso)++import           Diagrams.Angle+import           Diagrams.Core++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector++--------------------------------------------------------------------------------+-- Direction++-- | A vector is described by a @Direction@ and a magnitude.  So we+-- can think of a @Direction@ as a vector that has forgotten its+-- magnitude.  @Direction@s can be used with 'fromDirection' and the+-- lenses provided by its instances.+newtype Direction v n = Dir (v n)+  deriving (Read, Show, Eq, Ord, Functor) -- todo: special instances++type instance V (Direction v n) = v+type instance N (Direction v n) = n++instance (V (v n) ~ v, N (v n) ~ n, Transformable (v n)) => Transformable (Direction v n) where+  transform t (Dir v) = Dir (transform t v)++instance HasTheta v => HasTheta (Direction v) where+  _theta = _Dir . _theta++instance HasPhi v => HasPhi (Direction v) where+  _phi = _Dir . _phi++-- | _Dir is provided to allow efficient implementations of functions+--   in particular vector-spaces, but should be used with care as it+--   exposes too much information.+_Dir :: Iso' (Direction v n) (v n)+_Dir = iso (\(Dir v) -> v) Dir++-- | @direction v@ is the direction in which @v@ points.  Returns an+--   unspecified value when given the zero vector as input.+direction :: v n -> Direction v n+direction = Dir++-- | Synonym for 'direction'.+dir :: v n -> Direction v n+dir = Dir++-- | @fromDirection d@ is the unit vector in the direction @d@.+fromDirection :: (Metric v, Floating n) => Direction v n -> v n+fromDirection (Dir v) = signorm v++-- | Synonym for 'fromDirection'.+fromDir :: (Metric v, Floating n) => Direction v n -> v n+fromDir (Dir v) = signorm v++-- | compute the positive angle between the two directions in their common plane+angleBetweenDirs :: (Metric v, Floating n, Ord n)+  => Direction v n -> Direction v n -> Angle n+angleBetweenDirs d1 d2 = angleBetween (fromDirection d1) (fromDirection d2)++-- | @dirBetween p q@ returns the direction from @p@ to @q@.+dirBetween :: (Additive v, Num n) => Point v n -> Point v n -> Direction v n+dirBetween p q = dir $ q .-. p
+ src/Diagrams/LinearMap.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE ImpredicativeTypes    #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE UndecidableInstances  #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.LinearMap+-- Copyright   :  (c) 2014-2015 diagrams team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Linear maps. Unlike 'Transformation's these are not restricted to the+-- same space. In practice these are used for projections in+-- "Diagrams.ThreeD.Projection". Unless you want to work with+-- projections you're probably better off using 'Diagrams.Transform'.+--+-- Currently only path-like things can be projected. In the future we+-- hope to support projecting diagrams.+--+-----------------------------------------------------------------------------++module Diagrams.LinearMap+  ( -- * Linear maps+    LinearMap (..)+  , LinearMappable (..)+    -- ** Applying linear maps+  , linmap++    -- * Affine maps+  , AffineMap (..)+  , AffineMappable (..)++    -- ** Constructing affine maps+  , mkAffineMap+  , toAffineMap+  ) where++import           Control.Lens+import           Data.FingerTree         as FT+import qualified Data.Foldable           as F++import           Diagrams.Core+import           Diagrams.Core.Transform+import           Diagrams.Located+import           Diagrams.Path+import           Diagrams.Segment+import           Diagrams.Trail          hiding (offset)++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector+++-- | Type for holding linear maps. Note that these are not affine transforms so+--   attemping apply a translation with 'LinearMap' will likely produce incorrect+--   results.+newtype LinearMap v u n = LinearMap { lapply :: v n -> u n }++toLinearMap :: Transformation v n -> LinearMap v v n+toLinearMap (Transformation (m :-: _) _ _) = LinearMap m++-- | Class of things that have vectors that can be mapped over.+class LinearMappable a b where+  -- | Apply a linear map to an object. If the map is not linear,+  --   behaviour will likely be wrong.+  vmap :: (Vn a -> Vn b) -> a -> b+  -- this uses a function instead of LinearMap so we can also use this+  -- class to change number types++-- Note: instances need to be of the form+--+-- r ~ A u m => LinearMappable (A v n) r+--+-- so ghc knows there's only one possible result from calling vmap.++-- | Apply a linear map.+linmap :: (InSpace v n a, LinearMappable a b, N b ~ n)+     => LinearMap v (V b) n -> a -> b+linmap = vmap . lapply++instance r ~ Offset c u m => LinearMappable (Offset c v n) r where+  vmap f (OffsetClosed v) = OffsetClosed (f v)+  vmap _ OffsetOpen       = OffsetOpen+  {-# INLINE vmap #-}++instance r ~ Segment c u m => LinearMappable (Segment c v n) r where+  vmap f (Linear offset)      = Linear (vmap f offset)+  vmap f (Cubic v1 v2 offset) = Cubic (f v1) (f v2) (vmap f offset)+  {-# INLINE vmap #-}++instance (Metric v, Metric u, OrderedField n, OrderedField m, r ~ SegTree u m)+    => LinearMappable (SegTree v n) r where+  vmap f = over _Wrapped (fmap' (vmap f))+  {-# INLINE vmap #-}++instance (Metric v, Metric u, OrderedField n, OrderedField m, r ~ Trail' l u m)+    => LinearMappable (Trail' l v n) r where+  vmap f (Line st)        = Line (vmap f st)+  vmap f (Loop st offset) = Loop (vmap f st) (vmap f offset)+  {-# INLINE vmap #-}++instance (Metric v, Metric u, OrderedField n, OrderedField m, r ~ Trail u m)+    => LinearMappable (Trail v n) r where+  vmap f (Trail (Line st))        = Trail $ Line (vmap f st)+  vmap f (Trail (Loop st offset)) = Trail $ Loop (vmap f st) (vmap f offset)+  {-# INLINE vmap #-}++instance LinearMappable (Point v n) (Point u m) where+  vmap f (P v) = P (f v)+  {-# INLINE vmap #-}++instance r ~ FixedSegment u m => LinearMappable (FixedSegment v n) r where+  vmap f (FLinear p0 p1)      = FLinear (vmap f p0) (vmap f p1)+  vmap f (FCubic p0 p1 p2 p3) = FCubic (vmap f p0) (vmap f p1)+                                       (vmap f p2) (vmap f p3)+  {-# INLINE vmap #-}++instance (LinearMappable a b, r ~ Located b) => LinearMappable (Located a) r where+  vmap f (Loc p a) = Loc (vmap f p) (vmap f a)+  {-# INLINE vmap #-}++instance (Metric v, Metric u, OrderedField n, OrderedField m, r ~ Path u m)+    => LinearMappable (Path v n) r where+  vmap f = _Wrapped . mapped %~ vmap f+  {-# INLINE vmap #-}++-- | Affine linear maps. Unlike 'Transformation' these do not have to be+--   invertible so we can map between spaces.+data AffineMap v u n = AffineMap (LinearMap v u n) (u n)++-- | Make an affine map from a linear function and a translation.+mkAffineMap :: (v n -> u n) -> u n -> AffineMap v u n+mkAffineMap f = AffineMap (LinearMap f)++toAffineMap :: Transformation v n -> AffineMap v v n+toAffineMap t = AffineMap (toLinearMap t) (transl t)++class (LinearMappable a b, N a ~ N b) => AffineMappable a b where+  -- | Affine map over an object. Has a default implimentation of only+  --   applying the linear map+  amap :: (Additive (V a), F.Foldable (V a), Additive (V b), Num (N b))+       => AffineMap (V a) (V b) (N b) -> a -> b+  amap (AffineMap f _) = linmap f+  {-# INLINE amap #-}++instance r ~ Offset c u n => AffineMappable (Offset c v n) r+instance r ~ Segment c u n => AffineMappable (Segment c v n) r+instance (Metric v, Metric u, OrderedField n, r ~ SegTree u n) => AffineMappable (SegTree v n) r+instance (Metric v, Metric u, OrderedField n, r ~ Trail' l u n) => AffineMappable (Trail' l v n) r+instance (Metric v, Metric u, OrderedField n, r ~ Trail u n) => AffineMappable (Trail v n) r++instance (Additive v, Num n, r ~ Point u n) => AffineMappable (Point v n) r where+  amap (AffineMap f v) p = linmap f p .+^ v+  {-# INLINE amap #-}++instance r ~ FixedSegment u n => AffineMappable (FixedSegment v n) r where+  amap m (FLinear p0 p1)      = FLinear (amap m p0) (amap m p1)+  amap m (FCubic p0 p1 p2 p3) = FCubic (amap m p0) (amap m p1) (amap m p2) (amap m p3)+  {-# INLINE amap #-}++instance (LinearMappable a b, N a ~ N b, r ~ Located b) => AffineMappable (Located a) r where+  amap m@(AffineMap l _) (Loc p x) = Loc (amap m p) (linmap l x)+  {-# INLINE amap #-}++instance (Metric v, Metric u, OrderedField n, r ~ Path u n)+    => AffineMappable (Path v n) r where+  amap m = _Wrapped . mapped %~ amap m+  {-# INLINE amap #-}
src/Diagrams/Located.hs view
@@ -1,12 +1,15 @@-{-# LANGUAGE FlexibleContexts     #-}-{-# LANGUAGE StandaloneDeriving   #-}-{-# LANGUAGE TypeFamilies         #-}-{-# LANGUAGE UndecidableInstances #-}-+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE UndecidableInstances  #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Located--- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2013-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -19,20 +22,25 @@ -----------------------------------------------------------------------------  module Diagrams.Located-    ( Located-    , at, viewLoc, unLoc, loc, mapLoc+    ( Located (..)+    , at, viewLoc, mapLoc, located, _loc     )     where -import           Data.AffineSpace-import           Data.VectorSpace+import           Control.Lens            (Lens, Lens')+import           Text.Read +import           Linear.Affine+import           Linear.Vector++import           Diagrams.Align import           Diagrams.Core-import           Diagrams.Core.Points import           Diagrams.Core.Transform import           Diagrams.Parametric-  -- for GHC 7.4 type family bug +import           Data.Serialize          (Serialize)+import           GHC.Generics            (Generic)+ -- | \"Located\" things, /i.e./ things with a concrete location: --   intuitively, @Located a ~ (Point, a)@.  Wrapping a translationally --   invariant thing (/e.g./ a 'Segment' or 'Trail') in 'Located' pins@@ -48,27 +56,30 @@ --   'HasOrigin', 'Transformable', 'Enveloped', 'Traced', and --   'TrailLike' instances are particularly useful; see the documented --   instances below for more information.-data Located a = Loc { loc   :: Point (V a)   -- ^ Project out the-                                              --   location of a @Located@-                                              --   value.-                     , unLoc :: a             -- ^ Project the value-                                              --   of type @a@ out of-                                              --   a @Located a@,-                                              --   discarding the-                                              --   location.-                     }+data Located a =+  Loc { loc   :: Point (V a) (N a)  -- ^ Project out the+                                --   location of a @Located@+                                --   value.+      , unLoc :: a              -- ^ Project the value+                                --   of type @a@ out of+                                --   a @Located a@,+                                --   discarding the+                                --   location.+      } deriving (Generic) +instance (Serialize a, Serialize (V a (N a))) => Serialize (Located a)+ infix 5 `at`  -- | Construct a @Located a@ from a value of type @a@ and a location. --   @at@ is intended to be used infix, like @x \`at\` origin@.-at :: a -> Point (V a) -> Located a+at :: a -> Point (V a) (N a) -> Located a at a p = Loc p a  -- | Deconstruct a @Located a@ into a location and a value of type --   @a@.  @viewLoc@ can be especially useful in conjunction with the --   @ViewPatterns@ extension.-viewLoc :: Located a -> (Point (V a), a)+viewLoc :: Located a -> (Point (V a) (N a), a) viewLoc (Loc p a) = (p,a)  -- | 'Located' is not a @Functor@, since changing the type could@@ -81,28 +92,49 @@ --   @Located@ is a little-f (endo)functor on the category of types --   with associated vector space @v@; but that is not covered by the --   standard @Functor@ class.)-mapLoc :: (V a ~ V b) => (a -> b) -> Located a -> Located b+mapLoc :: SameSpace a b => (a -> b) -> Located a -> Located b mapLoc f (Loc p a) = Loc p (f a) -deriving instance (Eq   (V a), Eq a  ) => Eq   (Located a)-deriving instance (Ord  (V a), Ord a ) => Ord  (Located a)-deriving instance (Show (V a), Show a) => Show (Located a)+-- | A lens giving access to the object within a 'Located' wrapper.+located :: SameSpace a b => Lens (Located a) (Located b) a b+located f (Loc p a) = Loc p <$> f a +-- | Lens onto the location of something 'Located'.+_loc :: Lens' (Located a) (Point (V a) (N a))+_loc f (Loc p a) = flip Loc a <$> f p++deriving instance (Eq   (V a (N a)), Eq a  ) => Eq   (Located a)+deriving instance (Ord  (V a (N a)), Ord a ) => Ord  (Located a)++instance (Show (V a (N a)), Show a) => Show (Located a) where+  showsPrec d (Loc p a) = showParen (d > 5) $+    showsPrec 6 a . showString " `at` " . showsPrec 6 p++instance (Read (V a (N a)), Read a) => Read (Located a) where+  readPrec = parens . prec 5 $ do+    a <- readPrec+    Punc "`"   <- lexP+    Ident "at" <- lexP+    Punc "`"   <- lexP+    p <- readPrec+    return (Loc p a)+ type instance V (Located a) = V a+type instance N (Located a) = N a  -- | @Located a@ is an instance of @HasOrigin@ whether @a@ is or not. --   In particular, translating a @Located a@ simply translates the --   associated point (and does /not/ affect the value of type @a@).-instance VectorSpace (V a) => HasOrigin (Located a) where+instance (Num (N a), Additive (V a)) => HasOrigin (Located a) where   moveOriginTo o (Loc p a) = Loc (moveOriginTo o p) a  -- | Applying a transformation @t@ to a @Located a@ results in the --   transformation being applied to the location, and the /linear/ --   /portion/ of @t@ being applied to the value of type @a@ (/i.e./ --   it is not translated).-instance Transformable a => Transformable (Located a) where+instance (Additive (V a), Num (N a), Transformable a) => Transformable (Located a) where   transform t@(Transformation t1 t2 _) (Loc p a)-    = Loc (transform t p) (transform (Transformation t1 t2 zeroV) a)+    = Loc (transform t p) (transform (Transformation t1 t2 zero) a)  -- | The envelope of a @Located a@ is the envelope of the @a@, --   translated to the location.@@ -114,31 +146,38 @@  -- | The trace of a @Located a@ is the trace of the @a@, --   translated to the location.-instance Traced a => Traced (Located a) where+instance (Traced a, Num (N a)) => Traced (Located a) where   getTrace (Loc p a) = moveTo p (getTrace a) +instance Alignable a => Alignable (Located a) where+  defaultBoundary v = defaultBoundary v . unLoc+ instance Qualifiable a => Qualifiable (Located a) where-  n |> (Loc p a) = Loc p (n |> a)+  n .>> Loc p a = Loc p (n .>> a) -type instance Codomain (Located a) = Located (Codomain a)+type instance Codomain (Located a) = Point (Codomain a) -instance (V a ~ V (Codomain a), Parametric a) => Parametric (Located a) where-  (Loc x a) `atParam` p = Loc x (a `atParam` p)+instance (InSpace v n a, Parametric a, Codomain a ~ v)+    => Parametric (Located a) where+  Loc x a `atParam` p = x .+^ (a `atParam` p)  instance DomainBounds a => DomainBounds (Located a) where   domainLower (Loc _ a) = domainLower a   domainUpper (Loc _ a) = domainUpper a -instance (V a ~ V (Codomain a), EndValues a) => EndValues (Located a)+instance (InSpace v n a, EndValues a, Codomain a ~ v) => EndValues (Located a) -instance ( Codomain a ~ V a, Fractional (Scalar (V a)), AdditiveGroup (V a)-         , Sectionable a, Parametric a-         )+instance (InSpace v n a, Fractional n, Parametric a, Sectionable a, Codomain a ~ v)     => Sectionable (Located a) where   splitAtParam (Loc x a) p = (Loc x a1, Loc (x .+^ (a `atParam` p)) a2)     where (a1,a2) = splitAtParam a p -instance (HasArcLength a, Fractional (Scalar (V a)), V a ~ V (Codomain a))+  section (Loc x a) p1 p2 = Loc (x .+^ (a `atParam` p1)) (section a p1 p2)++  reverseDomain (Loc x a) = Loc (x .+^ y) (reverseDomain a)+    where y = a `atParam` domainUpper a++instance (InSpace v n a, Fractional n, HasArcLength a, Codomain a ~ v)     => HasArcLength (Located a) where-  arcLengthBounded eps (Loc _ a)   = arcLengthBounded eps a-  arcLengthToParam eps (Loc _ a) l = arcLengthToParam eps a l+  arcLengthBounded eps (Loc _ a) = arcLengthBounded eps a+  arcLengthToParam eps (Loc _ a) = arcLengthToParam eps a
src/Diagrams/Names.hs view
@@ -1,4 +1,10 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MonoLocalBinds   #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Names@@ -33,28 +39,28 @@       -- * Querying by name      , names+    , lookupName     , withName, withNameAll, withNames      ) where  import           Data.Semigroup-import           Data.VectorSpace -import           Diagrams.Core       (HasLinearMap, OrderedField, Point)+import           Diagrams.Core       (OrderedField, Point) import           Diagrams.Core.Names import           Diagrams.Core.Types +import           Linear.Metric+ -- | Attach an atomic name to a diagram.-named :: ( IsName n-         , HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m)-      => n -> QDiagram b v m -> QDiagram b v m+named :: (IsName nm, Metric v, OrderedField n, Semigroup m)+      => nm -> QDiagram b v n m -> QDiagram b v n m named = nameSub mkSubdiagram  -- | Attach an atomic name to a certain point (which may be computed --   from the given diagram), treated as a subdiagram with no content --   and a point envelope.-namePoint :: ( IsName n-             , HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m)-          => (QDiagram b v m -> Point v) -> n -> QDiagram b v m -> QDiagram b v m+namePoint :: (IsName nm , Metric v, OrderedField n, Semigroup m)+          => (QDiagram b v n m -> Point v n) -> nm -> QDiagram b v n m -> QDiagram b v n m namePoint p = nameSub (subPoint . p) 
src/Diagrams/Parametric.hs view
@@ -15,28 +15,20 @@ ----------------------------------------------------------------------------- module Diagrams.Parametric   (-  -- * Parametric functions     stdTolerance   , Codomain, Parametric(..) -  , DomainBounds(..), EndValues(..), Sectionable(..), HasArcLength(..)--  -- * Adjusting-  , adjust-  , AdjustOpts(..), AdjustMethod(..), AdjustSide(..)+  , DomainBounds(..), domainBounds, EndValues(..), Sectionable(..), HasArcLength(..)    ) where -import           Diagrams.Core-import           Diagrams.Util--import           Data.Default.Class-import           Data.VectorSpace-import qualified Numeric.Interval   as I+import           Data.Kind (Type)+import           Diagrams.Core.V+import qualified Numeric.Interval.Kaucher as I  -- | Codomain of parametric classes.  This is usually either @(V p)@, for relative --   vector results, or @(Point (V p))@, for functions with absolute coordinates.-type family Codomain p :: *+type family Codomain p :: Type -> Type  -- | Type class for parametric functions. class Parametric p where@@ -44,7 +36,7 @@   -- | 'atParam' yields a parameterized view of an object as a   --   continuous function. It is designed to be used infix, like @path   --   ``atParam`` 0.5@.-  atParam :: p -> Scalar (V p) -> Codomain p+  atParam :: p -> N p -> Codomain p (N p)  -- | Type class for parametric functions with a bounded domain.  The --   default bounds are @[0,1]@.@@ -55,16 +47,16 @@ class DomainBounds p where   -- | 'domainLower' defaults to being constantly 0 (for vector spaces with   --   numeric scalars).-  domainLower :: p -> Scalar (V p)+  domainLower :: p -> N p -  default domainLower :: Num (Scalar (V p)) => p -> Scalar (V p)+  default domainLower :: Num (N p) => p -> N p   domainLower = const 0    -- | 'domainUpper' defaults to being constantly 1 (for vector spaces   --   with numeric scalars).-  domainUpper :: p -> Scalar (V p)+  domainUpper :: p -> N p -  default domainUpper :: Num (Scalar (V p)) => p -> Scalar (V p)+  default domainUpper :: Num (N p) => p -> N p   domainUpper = const 1  -- | Type class for querying the values of a parametric object at the@@ -76,7 +68,7 @@   --   --   This is the default implementation, but some representations will   --   have a more efficient and/or precise implementation.-  atStart :: p -> Codomain p+  atStart :: p -> Codomain p (N p)   atStart x = x `atParam` domainLower x    -- | 'atEnd' is the value at the end of the domain. That is,@@ -85,28 +77,31 @@   --   --   This is the default implementation, but some representations will   --   have a more efficient and/or precise implementation.-  atEnd :: p -> Codomain p+  atEnd :: p -> Codomain p (N p)   atEnd x = x `atParam` domainUpper x  -- | Return the lower and upper bounds of a parametric domain together --   as a pair.-domainBounds :: DomainBounds p => p -> (Scalar (V p), Scalar (V p))+domainBounds :: DomainBounds p => p -> (N p, N p) domainBounds x = (domainLower x, domainUpper x)  -- | Type class for parametric objects which can be split into --   subobjects. -----   Minimal definition: Either 'splitAtParam' or 'section'.+--   Minimal definition: Either 'splitAtParam' or 'section',+--   plus 'reverseDomain'. class DomainBounds p => Sectionable p where   -- | 'splitAtParam' splits an object @p@ into two new objects   --   @(l,r)@ at the parameter @t@, where @l@ corresponds to the   --   portion of @p@ for parameter values from @0@ to @t@ and @r@ for   --   to that from @t@ to @1@.  The following property should hold:   ---  -- > prop_splitAtParam f t u =-  -- >   | u < t     = atParam f u == atParam l (u / t)-  -- >   | otherwise = atParam f u == atParam f t ??? atParam l ((u - t) / (domainUpper f - t))-  -- >   where (l,r) = splitAtParam f t+  -- @+  --   prop_splitAtParam f t u =+  --     | u < t     = atParam f u == atParam l (u / t)+  --     | otherwise = atParam f u == atParam f t ??? atParam l ((u - t) / (domainUpper f - t))+  --     where (l,r) = splitAtParam f t+  -- @   --   --   where @(???) = (^+^)@ if the codomain is a vector type, or   --   @const flip@ if the codomain is a point type.  Stated more@@ -119,7 +114,7 @@   --   result paths where the first is the original path extended to   --   the parameter 2, and the second result path travels /backwards/   --   from the end of the first to the end of the original path.-  splitAtParam :: p -> Scalar (V p) -> (p, p)+  splitAtParam :: p -> N p -> (p, p)   splitAtParam x t     = ( section x (domainLower x) t       , section x t (domainUpper x))@@ -135,16 +130,12 @@   --   --   That is, the section should have the same domain as the   --   original, and the reparameterization should be linear.-  section :: p -> Scalar (V p) -> Scalar (V p) -> p-  default section :: Fractional (Scalar (V p)) => p -> Scalar (V p) -> Scalar (V p) -> p+  section :: p -> N p -> N p -> p+  default section :: Fractional (N p) => p -> N p -> N p -> p   section x t1 t2 = snd (splitAtParam (fst (splitAtParam x t2)) (t1/t2)) -  -- | Flip the parameterization on the domain.  This has the-  --   following default definition:-  ---  -- > reverse x = section x (domainUpper x) (domainLower x)+  -- | Flip the parameterization on the domain.   reverseDomain :: p -> p-  reverseDomain x = section x (domainUpper x) (domainLower x)  -- | The standard tolerance used by @std...@ functions (like --   'stdArcLength' and 'stdArcLengthToParam', currently set at@@ -158,18 +149,18 @@   -- | @arcLengthBounded eps x@ approximates the arc length of @x@.   --   The true arc length is guaranteed to lie within the interval   --   returned, which will have a size of at most @eps@.-  arcLengthBounded :: Scalar (V p) -> p -> I.Interval (Scalar (V p))+  arcLengthBounded :: N p -> p -> I.Interval (N p)    -- | @arcLength eps s@ approximates the arc length of @x@ up to the   --   accuracy @eps@ (plus or minus).-  arcLength :: Scalar (V p) -> p -> Scalar (V p)-  default arcLength :: Fractional (Scalar (V p)) => Scalar (V p ) -> p -> Scalar (V p)+  arcLength :: N p -> p -> N p+  default arcLength :: Fractional (N p) => N p -> p -> N p   arcLength eps = I.midpoint . arcLengthBounded eps    -- | Approximate the arc length up to a standard accuracy of   --   'stdTolerance' (@1e-6@).-  stdArcLength :: p -> Scalar (V p)-  default stdArcLength :: Fractional (Scalar (V p)) => p -> Scalar (V p)+  stdArcLength :: p -> N p+  default stdArcLength :: Fractional (N p) => p -> N p   stdArcLength = arcLength stdTolerance    -- | @'arcLengthToParam' eps s l@ converts the absolute arc length@@ -179,65 +170,11 @@   --   --   This should work for /any/ arc length, and may return any   --   parameter value (not just parameters in the domain).-  arcLengthToParam :: Scalar (V p) -> p -> Scalar (V p) -> Scalar (V p)+  arcLengthToParam :: N p -> p -> N p -> N p    -- | A simple interface to convert arc length to a parameter,   --   guaranteed to be accurate within 'stdTolerance', or @1e-6@.-  stdArcLengthToParam :: p -> Scalar (V p) -> Scalar (V p)-  default stdArcLengthToParam :: Fractional (Scalar (V p))-                              => p -> Scalar (V p) -> Scalar (V p)+  stdArcLengthToParam :: p -> N p -> N p+  default stdArcLengthToParam :: Fractional (N p) => p -> N p -> N p   stdArcLengthToParam = arcLengthToParam stdTolerance ------------------------------------------------------  Adjusting length------------------------------------------------------- | What method should be used for adjusting a segment, trail, or---   path?-data AdjustMethod v = ByParam (Scalar v)     -- ^ Extend by the given parameter value-                                             --   (use a negative parameter to shrink)-                    | ByAbsolute (Scalar v)  -- ^ Extend by the given arc length-                                             --   (use a negative length to shrink)-                    | ToAbsolute (Scalar v)  -- ^ Extend or shrink to the given-                                             --   arc length---- | Which side of a segment, trail, or path should be adjusted?-data AdjustSide = Start  -- ^ Adjust only the beginning-                | End    -- ^ Adjust only the end-                | Both   -- ^ Adjust both sides equally-  deriving (Show, Read, Eq, Ord, Bounded, Enum)---- | How should a segment, trail, or path be adjusted?-data AdjustOpts v = AO { adjMethod       :: AdjustMethod v-                       , adjSide         :: AdjustSide-                       , adjEps          :: Scalar v-                       , adjOptsvProxy__ :: Proxy v-                       }--instance Fractional (Scalar v) => Default (AdjustMethod v) where-  def = ByParam 0.2--instance Default AdjustSide where-  def = Both--instance Fractional (Scalar v) => Default (AdjustOpts v) where-  def = AO def def stdTolerance Proxy---- | Adjust the length of a parametric object such as a segment or---   trail.  The second parameter is an option record which controls how---   the adjustment should be performed; see 'AdjustOpts'.-adjust :: (DomainBounds a, Sectionable a, HasArcLength a, Fractional (Scalar (V a)))-       => a -> AdjustOpts (V a) -> a-adjust s opts = section s-  (if adjSide opts == End   then domainLower s else getParam s)-  (if adjSide opts == Start then domainUpper s else domainUpper s - getParam (reverseDomain s))- where-  getParam seg = case adjMethod opts of-    ByParam p -> -p * bothCoef-    ByAbsolute len -> param (-len * bothCoef)-    ToAbsolute len -> param (absDelta len * bothCoef)-   where-    param        = arcLengthToParam eps seg-    absDelta len = arcLength eps s - len-  bothCoef = if adjSide opts == Both then 0.5 else 1-  eps = adjEps opts
+ src/Diagrams/Parametric/Adjust.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeOperators        #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Parametric.Adjust+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Tools for adjusting the length of parametric objects such as+-- segments and trails.+--+-----------------------------------------------------------------------------+module Diagrams.Parametric.Adjust+    ( adjust+    , AdjustOpts(_adjMethod, _adjSide, _adjEps)+    , adjMethod, adjSide, adjEps+    , AdjustMethod(..), AdjustSide(..)++    ) where++import           Control.Lens        (Lens', generateSignatures, lensRules, makeLensesWith, (&),+                                      (.~), (^.))+import           Data.Proxy++import           Data.Default++import           Diagrams.Core.V+import           Diagrams.Parametric++-- | What method should be used for adjusting a segment, trail, or+--   path?+data AdjustMethod n = ByParam n     -- ^ Extend by the given parameter value+                                    --   (use a negative parameter to shrink)+                    | ByAbsolute n  -- ^ Extend by the given arc length+                                    --   (use a negative length to shrink)+                    | ToAbsolute n  -- ^ Extend or shrink to the given+                                    --   arc length++-- | Which side of a segment, trail, or path should be adjusted?+data AdjustSide = Start  -- ^ Adjust only the beginning+                | End    -- ^ Adjust only the end+                | Both   -- ^ Adjust both sides equally+  deriving (Show, Read, Eq, Ord, Bounded, Enum)++-- | How should a segment, trail, or path be adjusted?+data AdjustOpts n = AO { _adjMethod    :: AdjustMethod n+                       , _adjSide      :: AdjustSide+                       , _adjEps       :: n+                       , adjOptsvProxy :: Proxy n+                       }++-- See Diagrams.Combinators for reasoning behind 'Proxy'.++makeLensesWith (lensRules & generateSignatures .~ False) ''AdjustOpts++-- | Which method should be used for adjusting?+adjMethod :: Lens' (AdjustOpts n) (AdjustMethod n)++-- | Which end(s) of the object should be adjusted?+adjSide :: Lens' (AdjustOpts n) AdjustSide++-- | Tolerance to use when doing adjustment.+adjEps :: Lens' (AdjustOpts n) n++instance Fractional n => Default (AdjustMethod n) where+  def = ByParam 0.2++instance Default AdjustSide where+  def = Both++instance Fractional n => Default (AdjustOpts n) where+  def = AO { _adjMethod    = def+           , _adjSide      = def+           , _adjEps       = stdTolerance+           , adjOptsvProxy = Proxy+           }++-- | Adjust the length of a parametric object such as a segment or+--   trail.  The second parameter is an option record which controls how+--   the adjustment should be performed; see 'AdjustOpts'.+adjust :: (N t ~ n, Sectionable t, HasArcLength t, Fractional n)+       => t -> AdjustOpts n -> t+adjust s opts = section s+  (if opts^.adjSide == End   then domainLower s else getParam s)+  (if opts^.adjSide == Start then domainUpper s else domainUpper s - getParam (reverseDomain s))+ where+  getParam seg = case opts^.adjMethod of+    ByParam p -> -p * bothCoef+    ByAbsolute len -> param (-len * bothCoef)+    ToAbsolute len -> param (absDelta len * bothCoef)+   where+    param        = arcLengthToParam eps seg+    absDelta len = arcLength eps s - len+  bothCoef = if opts^.adjSide == Both then 0.5 else 1+  eps = opts^.adjEps
src/Diagrams/Path.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE DeriveGeneric              #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}@@ -6,8 +8,13 @@ {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE StandaloneDeriving         #-} {-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-} {-# LANGUAGE UndecidableInstances       #-} {-# LANGUAGE ViewPatterns               #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Path@@ -28,21 +35,24 @@           -- * Paths -         Path(..)+         Path(..), pathTrails           -- * Constructing paths          -- $construct +       , ToPath (..)        , pathFromTrail        , pathFromTrailAt        , pathFromLocTrail           -- * Eliminating paths +       , pathPoints+       , pathVertices'        , pathVertices        , pathOffsets        , pathCentroid-       , fixPath+       , pathLocSegments, fixPath           -- * Modifying paths @@ -56,9 +66,15 @@         ) where +import           Control.Arrow      ((***))+import           Control.Lens       hiding (at, transform, ( # ))+import qualified Data.Foldable      as F+import           Data.List          (partition)+import           Data.Semigroup+import           Data.Typeable+ import           Diagrams.Align import           Diagrams.Core-import           Diagrams.Core.Points import           Diagrams.Located import           Diagrams.Points import           Diagrams.Segment@@ -66,14 +82,12 @@ import           Diagrams.TrailLike import           Diagrams.Transform -import           Control.Arrow        (first, second, (***))-import           Control.Newtype      hiding (under)-import           Data.AffineSpace-import qualified Data.Foldable        as F-import           Data.List            (mapAccumL, partition)-import           Data.Semigroup-import           Data.VectorSpace+import           Linear.Metric+import           Linear.Vector +import           Data.Serialize     (Serialize)+import           GHC.Generics       (Generic)+ ------------------------------------------------------------ --  Paths  ------------------------------------------------- ------------------------------------------------------------@@ -82,81 +96,136 @@ --   Hence, unlike trails, paths are not translationally invariant, --   and they form a monoid under /superposition/ (placing one path on --   top of another) rather than concatenation.-newtype Path v = Path { pathTrails :: [Located (Trail v)] }-  deriving (Semigroup, Monoid)+newtype Path v n = Path [Located (Trail v n)]+  deriving (Semigroup, Monoid, Generic+  , Typeable+  ) -deriving instance Show v => Show (Path v)-deriving instance Eq   v => Eq   (Path v)-deriving instance Ord  v => Ord  (Path v)+-- instance (OrderedField n, Metric v, Serialize (v n), Serialize (V n (N n))) =>+instance (OrderedField n, Metric v, Serialize (v n), Serialize (V (v n) (N (v n)))) =>+  Serialize (Path v n) -type instance V (Path v) = v+instance Wrapped (Path v n) where+  type Unwrapped (Path v n) = [Located (Trail v n)]+  _Wrapped' = iso (\(Path x) -> x) Path -instance Newtype (Path v) [Located (Trail v)] where-  pack   = Path-  unpack = pathTrails+instance Rewrapped (Path v n) (Path v' n') -instance VectorSpace v => HasOrigin (Path v) where-  moveOriginTo = over Path . map . moveOriginTo+instance Each (Path v n) (Path v' n') (Located (Trail v n)) (Located (Trail v' n')) where+  each = _Wrapped . traverse --- | Paths are trail-like; a trail can be used to construct a---   singleton path.-instance (InnerSpace v, OrderedField (Scalar v)) => TrailLike (Path v) where-  trailLike = Path . (:[])+instance AsEmpty (Path v n) where+  _Empty = _Wrapped' . _Empty --- See Note [Transforming paths]-instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-    => Transformable (Path v) where-  transform = over Path . map . transform+instance Cons (Path v n) (Path v' n') (Located (Trail v n)) (Located (Trail v' n')) where+  _Cons = _Wrapped . _Cons . bimapping id _Unwrapped+  {-# INLINE _Cons #-} -{- ~~~~ Note [Transforming paths]+instance Snoc (Path v n) (Path v' n') (Located (Trail v n)) (Located (Trail v' n')) where+  _Snoc = _Wrapped . _Snoc . bimapping _Unwrapped id+  {-# INLINE _Snoc #-} -Careful!  It's tempting to just define+-- | Extract the located trails making up a 'Path'.+pathTrails :: Path v n -> [Located (Trail v n)]+pathTrails = op Path -> transform = fmap . transform+deriving instance Show (v n) => Show (Path v n)+deriving instance Eq   (v n) => Eq   (Path v n)+deriving instance Ord  (v n) => Ord  (Path v n) -but that doesn't take into account the fact that some-of the v's are inside Points and hence ought to be translated.--}+type instance V (Path v n) = v+type instance N (Path v n) = n -instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v)) => IsPrim (Path v)+instance (Additive v, Num n) => HasOrigin (Path v n) where+  moveOriginTo = over _Wrapped' . map . moveOriginTo -instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (Path v) where-  getEnvelope = F.foldMap trailEnvelope . pathTrails+-- | Paths are trail-like; a trail can be used to construct a+--   singleton path.+instance (Metric v, OrderedField n) => TrailLike (Path v n) where+  trailLike = Path . (:[])++-- See Note [Transforming paths]+instance (HasLinearMap v, Metric v, OrderedField n)+    => Transformable (Path v n) where+  transform = over _Wrapped . map . transform++instance (Metric v, OrderedField n) => Enveloped (Path v n) where+  getEnvelope = F.foldMap trailEnvelope . op Path           -- this type signature is necessary to work around an apparent bug in ghc 6.12.1-    where trailEnvelope :: Located (Trail v) -> Envelope v+    where trailEnvelope :: Located (Trail v n) -> Envelope v n           trailEnvelope (viewLoc -> (p, t)) = moveOriginTo ((-1) *. p) (getEnvelope t) -instance (InnerSpace v, OrderedField (Scalar v)) => Juxtaposable (Path v) where+instance (Metric v, OrderedField n) => Juxtaposable (Path v n) where   juxtapose = juxtaposeDefault -instance (InnerSpace v, OrderedField (Scalar v)) => Alignable (Path v) where-  alignBy = alignByDefault+instance (Metric v, OrderedField n) => Alignable (Path v n) where+  defaultBoundary = envelopeBoundary -instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-    => Renderable (Path v) NullBackend where+instance (HasLinearMap v, Metric v, OrderedField n)+    => Renderable (Path v n) NullBackend where   render _ _ = mempty  ------------------------------------------------------------ --  Constructing paths  ------------------------------------ ------------------------------------------------------------ +-- | Type class for things that can be converted to a 'Path'.+--+--   Note that this class is very different from 'TrailLike'. 'TrailLike' is+--   usually the result of a library function to give you a convenient,+--   polymorphic result ('Path', 'Diagram' etc.).+--+class ToPath t where+  -- | 'toPath' takes something that can be converted to 'Path' and returns+  --    the 'Path'.+  toPath :: (Metric (V t), OrderedField (N t)) => t -> Path (V t) (N t)++instance ToPath (Path v n) where+  toPath = id++instance ToPath (Trail v n) where+  toPath = pathFromTrail++instance ToPath (Trail' l v n) where+  toPath t = Path [Trail t `at` origin]++instance ToPath (Located (Trail v n)) where+  toPath = pathFromLocTrail++instance ToPath (Located (Trail' l v n)) where+  toPath = pathFromLocTrail . mapLoc Trail++instance ToPath (Located (Segment Closed v n)) where+  toPath (viewLoc -> (p,seg))+    = Path [trailFromSegments [seg] `at` p]++instance ToPath (Located [Segment Closed v n]) where+  toPath (viewLoc -> (p,segs))+    = Path [trailFromSegments segs `at` p]++instance ToPath (FixedSegment v n) where+  toPath = toPath . fromFixedSeg++instance ToPath a => ToPath [a] where+  toPath = F.foldMap toPath+ -- $construct -- Since paths are 'TrailLike', any function producing a 'TrailLike' -- can be used to construct a (singleton) path.  The functions in this -- section are provided for convenience.  -- | Convert a trail to a path beginning at the origin.-pathFromTrail :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> Path v+pathFromTrail :: (Metric v, OrderedField n) => Trail v n -> Path v n pathFromTrail = trailLike . (`at` origin)  -- | Convert a trail to a path with a particular starting point.-pathFromTrailAt :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> Point v -> Path v+pathFromTrailAt :: (Metric v, OrderedField n) => Trail v n -> Point v n -> Path v n pathFromTrailAt t p = trailLike (t `at` p)  -- | Convert a located trail to a singleton path.  This is equivalent --   to 'trailLike', but provided with a more specific name and type --   for convenience.-pathFromLocTrail :: (InnerSpace v, OrderedField (Scalar v)) => Located (Trail v) -> Path v+pathFromLocTrail :: (Metric v, OrderedField n) => Located (Trail v n) -> Path v n pathFromLocTrail = trailLike  ------------------------------------------------------------@@ -164,33 +233,65 @@ ------------------------------------------------------------  -- | Extract the vertices of a path, resulting in a separate list of---   vertices for each component trail (see 'trailVertices').-pathVertices :: (InnerSpace v, OrderedField (Scalar v)) => Path v -> [[Point v]]-pathVertices = map trailVertices . pathTrails+--   vertices for each component trail.  Here a /vertex/ is defined as+--   a non-differentiable point on the trail, /i.e./ a sharp corner.+--   (Vertices are thus a subset of the places where segments join; if+--   you want all joins between segments, see 'pathPoints'.)  The+--   tolerance determines how close the tangents of two segments must be+--   at their endpoints to consider the transition point to be+--   differentiable.  See 'trailVertices' for more information.+pathVertices' :: (Metric v, OrderedField n) => n -> Path v n -> [[Point v n]]+pathVertices' toler = map (trailVertices' toler) . op Path +-- | Like 'pathVertices'', with a default tolerance.+pathVertices :: (Metric v, OrderedField n) => Path v n -> [[Point v n]]+pathVertices = map trailVertices . op Path++-- | Extract the points of a path, resulting in a separate list of+--   points for each component trail.  Here a /point/ is any place+--   where two segments join; see also 'pathVertices' and 'trailPoints'.+--+--   This function allows you "observe" the fact that trails are+--   implemented as lists of segments, which may be problematic if we+--   want to think of trails as parametric vector functions. This also+--   means that the behavior of this function may not be stable under+--   future changes to the implementation of trails and paths.  For an+--   unproblematic version which only yields vertices at which there+--   is a sharp corner, excluding points differentiable points, see+--   'pathVertices'.+--+--   This function is not re-exported from "Diagrams.Prelude"; to use+--   it, import "Diagrams.Path".+pathPoints :: (Metric v, OrderedField n) => Path v n -> [[Point v n]]+pathPoints = map trailPoints . op Path+ -- | Compute the total offset of each trail comprising a path (see 'trailOffset').-pathOffsets :: (InnerSpace v, OrderedField (Scalar v)) => Path v -> [v]-pathOffsets = map (trailOffset . unLoc) . pathTrails+pathOffsets :: (Metric v, OrderedField n) => Path v n -> [v n]+pathOffsets = map (trailOffset . unLoc) . op Path  -- | Compute the /centroid/ of a path (/i.e./ the average location of---   its vertices).-pathCentroid :: (InnerSpace v, OrderedField (Scalar v)) => Path v -> Point v+--   its /vertices/; see 'pathVertices').+pathCentroid :: (Metric v, OrderedField n) => Path v n -> Point v n pathCentroid = centroid . concat . pathVertices +-- | Convert a path into a list of lists of located segments.+pathLocSegments :: (Metric v, OrderedField n) => Path v n -> [[Located (Segment Closed v n)]]+pathLocSegments = map trailLocSegments . op Path+ -- | Convert a path into a list of lists of 'FixedSegment's.-fixPath :: (InnerSpace v, OrderedField (Scalar v)) => Path v -> [[FixedSegment v]]-fixPath = map fixTrail . unpack+fixPath :: (Metric v, OrderedField n) => Path v n -> [[FixedSegment v n]]+fixPath = map fixTrail . op Path  -- | \"Explode\" a path by exploding every component trail (see --   'explodeTrail').-explodePath :: (VectorSpace (V t), TrailLike t) => Path (V t) -> [[t]]-explodePath = map explodeTrail . pathTrails+explodePath :: (V t ~ v, N t ~ n, TrailLike t) => Path v n -> [[t]]+explodePath = map explodeTrail . op Path  -- | Partition a path into two paths based on a predicate on trails: --   the first containing all the trails for which the predicate returns --   @True@, and the second containing the remaining trails.-partitionPath :: (Located (Trail v) -> Bool) -> Path v -> (Path v, Path v)-partitionPath p = (pack *** pack) . partition p . unpack+partitionPath :: (Located (Trail v n) -> Bool) -> Path v n -> (Path v n, Path v n)+partitionPath p = (view _Unwrapped' *** view _Unwrapped') . partition p . op Path  ------------------------------------------------------------ --  Modifying paths  ---------------------------------------@@ -198,10 +299,13 @@  -- | Scale a path using its centroid (see 'pathCentroid') as the base --   point for the scale.-scalePath :: (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-          => Scalar v -> Path v -> Path v-scalePath d p = (scale d `under` translation (origin .-. pathCentroid p)) p+scalePath :: (HasLinearMap v, Metric v, OrderedField n) => n -> Path v n -> Path v n+scalePath d p = under (movedFrom (pathCentroid p)) (scale d) p  -- | Reverse all the component trails of a path.-reversePath :: (InnerSpace v, OrderedField (Scalar v)) => Path v -> Path v-reversePath = (over Path . map) reverseLocTrail+reversePath :: (Metric v, OrderedField n) => Path v n -> Path v n+reversePath = _Wrapped . mapped %~ reverseLocTrail++-- | Same as 'reversePath'.+instance (Metric v, OrderedField n) => Reversing (Path v n) where+  reversing = _Wrapped' . mapped %~ reversing
src/Diagrams/Points.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies     #-}- ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Points@@ -9,36 +6,36 @@ -- Maintainer  :  diagrams-discuss@googlegroups.com -- -- Points in space.  For more tools for working with points and--- vectors, see "Data.AffineSpace" and "Diagrams.Coordinates".+-- vectors, see "Linear.Affine". -- -----------------------------------------------------------------------------  module Diagrams.Points        ( -- * Points-         Point, origin, (*.)+         Point (..), origin, (*.)           -- * Point-related utilities        , centroid-+       , pointDiagram+       , _Point, lensP        ) where -import           Diagrams.Coordinates+import           Diagrams.Core        (pointDiagram) import           Diagrams.Core.Points -import           Control.Newtype--import           Control.Arrow        ((&&&))+import           Data.Foldable        as F -import           Data.VectorSpace+import           Linear.Affine+import           Linear.Vector  -- | The centroid of a set of /n/ points is their sum divided by /n/.-centroid :: (VectorSpace v, Fractional (Scalar v)) => [Point v] -> Point v-centroid = pack . uncurry (^/) . (sumV &&& (fromIntegral . length)) . map unpack+--   Returns the origin for an empty list of points.+centroid :: (Additive v, Fractional n) => [Point v n] -> Point v n+centroid [] = origin+centroid ps = meanV ps+{-# INLINE centroid #-} -instance Coordinates v => Coordinates (Point v) where-  type FinalCoord (Point v)    = FinalCoord v-  type PrevDim (Point v)       = PrevDim v-  type Decomposition (Point v) = Decomposition v+meanV :: (Foldable f, Additive v, Fractional a) => f (v a) -> v a+meanV = uncurry (^/) . F.foldl' (\(s,c) e -> (e ^+^ s,c+1)) (zero,0)+{-# INLINE meanV #-} -  x & y        = P (x & y)-  coords (P v) = coords v
src/Diagrams/Prelude.hs view
@@ -1,8 +1,9 @@ {-# OPTIONS_GHC -fno-warn-duplicate-exports #-}+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Prelude--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -12,129 +13,93 @@ -----------------------------------------------------------------------------  module Diagrams.Prelude-       (-         -- * Core library-         -- | The core definitions of transformations, diagrams,-         --   backends, and so on.-         module Diagrams.Core--         -- * Standard library--         -- | Attributes (color, line style, etc.) and styles.-       , module Diagrams.Attributes--         -- | Alignment of diagrams relative to their envelopes.-       , module Diagrams.Align--         -- | Combining multiple diagrams into one.-       , module Diagrams.Combinators--         -- | Giving concrete locations to translation-invariant things.-       , module Diagrams.Located--         -- | Linear and cubic bezier segments.-       , module Diagrams.Segment--         -- | Trails.-       , module Diagrams.Trail--         -- | Parametrization of segments and trails.-       , module Diagrams.Parametric--         -- | Trail-like things.-       , module Diagrams.TrailLike--         -- | Paths.-       , module Diagrams.Path--         -- | Cubic splines.-       , module Diagrams.CubicSpline+  (+    -- * Diagrams library+    -- | Exports from this library for working with diagrams.+    module Diagrams -         -- | Some additional transformation-related functions, like-         --   conjugation of transformations.-       , module Diagrams.Transform+    -- * Convenience re-exports from other packages -         -- | Convenient definitions and utilities for working with-         --   good old-fashioned, axis-aligned bounding boxes.-       , module Diagrams.BoundingBox+    -- | For working with default values. Diagrams also exports 'with',+    --   an alias for 'def'.+  , module Data.Default -         -- | Giving names to subdiagrams and later retrieving-         --   subdiagrams by name.-       , module Diagrams.Names+    -- | For representing and operating on colors.+  , module Data.Colour -         -- | Envelopes, aka functional bounding regions.-       , module Diagrams.Envelope+    -- | A large list of color names.+  , module Data.Colour.Names -         -- | Traces, aka embedded raytracers, for finding points on-         --   the boundary of a diagram.-       , module Diagrams.Trace+    -- | Specify your own colours.+  , module Data.Colour.SRGB -         -- | A query is a function that maps points in a vector space-         --   to values in some monoid; they can be used to annotate-         --   the points of a diagram with some values.-       , module Diagrams.Query+    -- | Semigroups and monoids show up all over the place, so things from+    --   Data.Semigroup and Data.Monoid often come in handy.+  , module Data.Semigroup -         -- | Utilities for working with points.-       , module Diagrams.Points+    -- | For computing with vectors.+  , module Linear.Vector -         -- | A wide range of things (shapes, transformations,-         --   combinators) specific to creating two-dimensional-         --   diagrams.-       , module Diagrams.TwoD+    -- | For computing with points and vectors.+  , module Linear.Affine -         -- | Tools for making animations.-       , module Diagrams.Animation+    -- | For computing with dot products and norm.+  , module Linear.Metric -         -- | Various utility definitions.-       , module Diagrams.Util+    -- | For working with 'Active' (i.e. animated) things.+  , module Data.Active -         -- * Convenience re-exports-         -- | For representing and operating on colors.-       , module Data.Colour-         -- | A large list of color names.-       , module Data.Colour.Names-         -- | Semigroups and monoids show up all over the place, so things from-         --   Data.Semigroup and Data.Monoid often come in handy.-       , module Data.Semigroup-         -- | For computing with vectors.-       , module Data.VectorSpace-         -- | For computing with points and vectors.-       , module Data.AffineSpace+    -- | Most of the lens package. The following functions are not+    --   exported from lens because they either conflict with+    --   diagrams or may conflict with other libraries:+    --+    --   * 'Control.Lens.At.at'+    --   * 'Control.Lens.At.contains'+    --   * 'Control.Lens.Indexed..>'+    --   * 'Control.Lens.Indexed.<.>'+    --   * 'Control.Lens.Indexed.index'+    --   * 'Control.Lens.Indexed.indices'+    --   * 'Control.Lens.Indexed.none'+    --   * 'Control.Lens.Internal.Getter.coerce'+    --   * 'Control.Lens.Internal.Indexed.indexed'+    --   * 'Control.Lens.Lens.inside'+    --   * 'Control.Lens.Level.levels'+    --   * 'Control.Lens.Plated....'+    --   * 'Control.Lens.Plated.children'+    --   * 'Control.Lens.Plated.transform'+    --   * 'Control.Lens.Prism.outside'+    --   * 'Control.Lens.Setter.argument'+    --   * 'Control.Lens.Traversal.beside'+    --   * 'Control.Lens.Traversal.singular'+  , module Control.Lens -         -- | For working with 'Active' (i.e. animated) things.-       , module Data.Active+  , Applicative(..), (*>), (<*), (<$>), (<$), liftA, liftA2, liftA3+  ) where -       , Applicative(..), (*>), (<*), (<$>), (<$), liftA, liftA2, liftA3-       ) where+import           Diagrams -import           Diagrams.Core+import           Control.Applicative -import           Diagrams.Align-import           Diagrams.Animation-import           Diagrams.Attributes-import           Diagrams.BoundingBox-import           Diagrams.Combinators-import           Diagrams.Coordinates-import           Diagrams.CubicSpline-import           Diagrams.Envelope-import           Diagrams.Located-import           Diagrams.Names-import           Diagrams.Parametric-import           Diagrams.Path-import           Diagrams.Points-import           Diagrams.Query-import           Diagrams.Segment-import           Diagrams.Trace-import           Diagrams.Trail-import           Diagrams.TrailLike-import           Diagrams.Transform-import           Diagrams.TwoD-import           Diagrams.Util+#if MIN_VERSION_lens(4,13,0)+import           Control.Lens        hiding (argument, at, backwards, beside,+                                      children, contains, indexed, indices,+                                      inside, levels, none, outside, singular,+                                      transform, ( # ), (...), (.>), (<.>))+#else+import           Control.Lens        hiding (argument, at, backwards, beside,+                                      children, coerce, contains, indexed,+                                      indices, inside, levels, none, outside,+                                      singular, transform, ( # ), (...), (.>),+                                      (<.>))+#endif -import           Control.Applicative import           Data.Active-import           Data.AffineSpace-import           Data.Colour          hiding (AffineSpace (..), atop)-import           Data.Colour.Names+import           Data.Colour         hiding (AffineSpace (..), atop, over)+import           Data.Colour.Names   hiding (tan)+import           Data.Colour.SRGB+import           Data.Default import           Data.Semigroup-import           Data.VectorSpace     hiding (Sum (..))++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector
src/Diagrams/Query.hs view
@@ -1,3 +1,7 @@+{-# LANGUAGE FlexibleContexts       #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses  #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Query@@ -12,8 +16,78 @@ -----------------------------------------------------------------------------  module Diagrams.Query-    ( Query(..), query, sample, value, resetValue, clearValue+  ( -- * Queries+    Query(..)+  , HasQuery (..)+  , sample+  , inquire -    ) where+    -- ** Queries on diagrams+  , query+  , value+  , resetValue+  , clearValue+  ) where +import           Data.Monoid+ import           Diagrams.Core++-- | Types which can answer a 'Query' about points inside the geometric+--   object.+--+--   If @t@ and @m@ are both a 'Semigroup's, 'getQuery' should satisfy+--+-- @+-- 'getQuery' (t1 <> t2) = 'getQuery' t1 <> 'getQuery' t2+-- @+class HasQuery t m | t -> m where+  -- | Extract the query of an object.+  getQuery :: t -> Query (V t) (N t) m++instance HasQuery (Query v n m) m where+  getQuery = id++instance Monoid m => HasQuery (QDiagram b v n m) m where+  getQuery = query++-- | Test if a point is not equal to 'mempty'.+--+-- @+-- 'inquire' :: 'QDiagram' b v n 'Any' -> 'Point' v n -> 'Bool'+-- 'inquire' :: 'Query' v n 'Any'      -> 'Point' v n -> 'Bool'+-- 'inquire' :: 'Diagrams.BoundingBox.BoundingBox' v n  -> 'Point' v n -> 'Bool'+-- @+inquire :: HasQuery t Any => t -> Point (V t) (N t) -> Bool+inquire t = getAny . sample t++-- | Sample a diagram's query function at a given point.+--+-- @+-- 'sample' :: 'QDiagram' b v n m -> 'Point' v n -> m+-- 'sample' :: 'Query' v n m      -> 'Point' v n -> m+-- 'sample' :: 'Diagrams.BoundingBox.BoundingBox' v n  -> 'Point' v n -> 'Any'+-- 'sample' :: 'Diagrams.Path.Path' 'V2' 'Double'   -> 'Point' v n -> 'Diagrams.TwoD.Path.Crossings'+-- @+sample :: HasQuery t m => t -> Point (V t) (N t) -> m+sample = runQuery . getQuery++-- | Set the query value for 'True' points in a diagram (/i.e./ points+--   \"inquire\" the diagram); 'False' points will be set to 'mempty'.+value :: Monoid m => m -> QDiagram b v n Any -> QDiagram b v n m+value m = fmap fromAny+  where fromAny (Any True)  = m+        fromAny (Any False) = mempty++-- | Reset the query values of a diagram to @True@/@False@: any values+--   equal to 'mempty' are set to 'False'; any other values are set to+--   'True'.+resetValue :: (Eq m, Monoid m) => QDiagram b v n m -> QDiagram b v n Any+resetValue = fmap toAny+  where toAny m | m == mempty = Any False+                | otherwise   = Any True++-- | Set all the query values of a diagram to 'False'.+clearValue :: QDiagram b v n m -> QDiagram b v n Any+clearValue = fmap (const (Any False))+
src/Diagrams/Segment.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE BangPatterns               #-} {-# LANGUAGE DeriveFunctor              #-} {-# LANGUAGE EmptyDataDecls             #-} {-# LANGUAGE FlexibleContexts           #-}@@ -6,10 +8,10 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TemplateHaskell            #-} {-# LANGUAGE TypeFamilies               #-} {-# LANGUAGE TypeOperators              #-} {-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE ViewPatterns               #-}  ----------------------------------------------------------------------------- -- |@@ -40,43 +42,50 @@           -- * Segment offsets -       , Offset(..), segOffset+       , Offset(..) , segOffset           -- * Constructing and modifying segments -       , Segment(..), straight, bezier3, bézier3, reverseSegment+       , Segment(..), straight, bezier3, bézier3, reverseSegment, mapSegmentVectors+       , openLinear, openCubic           -- * Fixed (absolutely located) segments        , FixedSegment(..)        , mkFixedSeg, fromFixedSeg+       , fixedSegIso           -- * Segment measures          -- $segmeas         , SegCount(..)-       , ArcLength(..), getArcLengthCached, getArcLengthFun, getArcLengthBounded+       , ArcLength(..)+       , getArcLengthCached, getArcLengthFun, getArcLengthBounded        , TotalOffset(..)-       , OffsetEnvelope(..)+       , OffsetEnvelope(..), oeOffset, oeEnvelope        , SegMeasure         ) where -import           Control.Applicative (liftA2)-import           Data.AffineSpace-import           Data.Default.Class+import           Control.Lens              hiding (at, transform) import           Data.FingerTree import           Data.Monoid.MList import           Data.Semigroup-import           Data.VectorSpace    hiding (Sum (..))-import           Numeric.Interval    (Interval (..))-import qualified Numeric.Interval    as I+import           Numeric.Interval.Kaucher  (Interval (..))+import qualified Numeric.Interval.Kaucher  as I -import           Diagrams.Core+import           Linear.Affine+import           Linear.Metric+import           Linear.Vector             hiding (lerp)+import           Linear.Vector.Compat      (lerp)++import           Control.Applicative+import           Diagrams.Core             hiding (Measured) import           Diagrams.Located import           Diagrams.Parametric-import           Diagrams.Solve-import           Diagrams.Util+import           Diagrams.Solve.Polynomial +import           Data.Serialize            (Serialize)+import qualified Data.Serialize            as Serialize  ------------------------------------------------------------ --  Open/closed type tags  ---------------------------------@@ -100,23 +109,35 @@ --   the context, /i.e./ its endpoint is not fixed.  The offset for a --   /closed/ segment is stored explicitly, /i.e./ its endpoint is at --   a fixed offset from its start.-data Offset c v where-  OffsetOpen   :: Offset Open v-  OffsetClosed :: v -> Offset Closed v+data Offset c v n where+  OffsetOpen   :: Offset Open v n+  OffsetClosed :: v n -> Offset Closed v n -deriving instance Show v => Show (Offset c v)-deriving instance Eq   v => Eq   (Offset c v)-deriving instance Ord  v => Ord  (Offset c v)+deriving instance Show (v n) => Show (Offset c v n)+deriving instance Eq   (v n) => Eq   (Offset c v n)+deriving instance Ord  (v n) => Ord  (Offset c v n) -instance Functor (Offset c) where+instance Functor v => Functor (Offset c v) where   fmap _ OffsetOpen       = OffsetOpen-  fmap f (OffsetClosed v) = OffsetClosed (f v)+  fmap f (OffsetClosed v) = OffsetClosed (fmap f v) -type instance V (Offset c v) = v+instance Each (Offset c v n) (Offset c v' n') (v n) (v' n') where+  each f (OffsetClosed v) = OffsetClosed <$> f v+  each _ OffsetOpen       = pure OffsetOpen+  {-# INLINE each #-} -instance HasLinearMap v => Transformable (Offset c v) where-  transform = fmap . apply+-- | Reverses the direction of closed offsets.+instance (Additive v, Num n) => Reversing (Offset c v n) where+  reversing (OffsetClosed off) = OffsetClosed $ negated off+  reversing a@OffsetOpen       = a +type instance V (Offset c v n) = v+type instance N (Offset c v n) = n++instance Transformable (Offset c v n) where+  transform _ OffsetOpen       = OffsetOpen+  transform t (OffsetClosed v) = OffsetClosed (apply t v)+ ------------------------------------------------------------ --  Constructing segments  --------------------------------- ------------------------------------------------------------@@ -128,34 +149,62 @@ --   \"location\" and are unaffected by translations.  They are, --   however, affected by other transformations such as rotations and --   scales.-data Segment c v-    = Linear (Offset c v)+data Segment c v n+    = Linear !(Offset c v n)       -- ^ A linear segment with given offset. -    | Cubic v v (Offset c v)+    | Cubic !(v n) !(v n) !(Offset c v n)       -- ^ A cubic Bézier segment specified by       --   three offsets from the starting       --   point to the first control point,       --   second control point, and ending       --   point, respectively. -  deriving (Show, Functor, Eq, Ord)+  deriving (Functor, Eq, Ord) +instance Show (v n) => Show (Segment c v n) where+  showsPrec d seg = case seg of+    Linear (OffsetClosed v)       -> showParen (d > 10) $+      showString "straight " . showsPrec 11 v+    Cubic v1 v2 (OffsetClosed v3) -> showParen (d > 10) $+      showString "bézier3  " . showsPrec 11 v1 . showChar ' '+                             . showsPrec 11 v2 . showChar ' '+                             . showsPrec 11 v3+    Linear OffsetOpen             -> showString "openLinear"+    Cubic v1 v2 OffsetOpen        -> showParen (d > 10) $+      showString "openCubic " . showsPrec 11 v1 . showChar ' '+                              . showsPrec 11 v2+++instance Each (Segment c v n) (Segment c v' n') (v n) (v' n') where+  each f (Linear offset)      = Linear <$> each f offset+  each f (Cubic v1 v2 offset) = Cubic  <$> f v1 <*> f v2 <*> each f offset+  {-# INLINE each #-}++-- | Reverse the direction of a segment.+instance (Additive v, Num n) => Reversing (Segment Closed v n) where+  reversing = reverseSegment++-- | Map over the vectors of each segment.+mapSegmentVectors :: (v n -> v' n') -> Segment c v n -> Segment c v' n'+mapSegmentVectors = over each+ -- Note, can't yet have Haddock comments on GADT constructors; see -- http://trac.haskell.org/haddock/ticket/43. For now we don't need -- Segment to be a GADT but we might in the future. (?) -type instance V (Segment c v) = v+type instance V (Segment c v n) = v+type instance N (Segment c v n) = n -instance HasLinearMap v => Transformable (Segment c v) where-  transform = fmap . apply+instance Transformable (Segment c v n) where+  transform = mapSegmentVectors . apply -instance HasLinearMap v => Renderable (Segment c v) NullBackend where+instance Renderable (Segment c v n) NullBackend where   render _ _ = mempty  -- | @'straight' v@ constructs a translationally invariant linear --   segment with direction and length given by the vector @v@.-straight :: v -> Segment Closed v+straight :: v n -> Segment Closed v n straight = Linear . OffsetClosed  -- Note, if we didn't have a Linear constructor we could also create@@ -167,40 +216,50 @@ --   Bézier curve where the offsets from the first endpoint to the --   first and second control point and endpoint are respectively --   given by @c1@, @c2@, and @x@.-bezier3 :: v -> v -> v -> Segment Closed v+bezier3 :: v n -> v n -> v n -> Segment Closed v n bezier3 c1 c2 x = Cubic c1 c2 (OffsetClosed x)  -- | @bézier3@ is the same as @bezier3@, but with more snobbery.-bézier3 :: v -> v -> v -> Segment Closed v+bézier3 :: v n -> v n -> v n -> Segment Closed v n bézier3 = bezier3 -type instance Codomain (Segment Closed v) = v+type instance Codomain (Segment Closed v n) = v  -- | 'atParam' yields a parametrized view of segments as continuous --   functions @[0,1] -> v@, which give the offset from the start of --   the segment for each value of the parameter between @0@ and @1@. --   It is designed to be used infix, like @seg ``atParam`` 0.5@.-instance (VectorSpace v, Num (Scalar v)) => Parametric (Segment Closed v) where+instance (Additive v, Num n) => Parametric (Segment Closed v n) where   atParam (Linear (OffsetClosed x)) t       = t *^ x   atParam (Cubic c1 c2 (OffsetClosed x2)) t =     (3 * t'*t'*t ) *^ c1                                               ^+^ (3 * t'*t *t ) *^ c2                                               ^+^ (    t *t *t ) *^ x2     where t' = 1-t -instance Num (Scalar v) => DomainBounds (Segment Closed v)+instance Num n => DomainBounds (Segment Closed v n) -instance (VectorSpace v, Num (Scalar v)) => EndValues (Segment Closed v) where-  atStart                            = const zeroV+instance (Additive v, Num n) => EndValues (Segment Closed v n) where+  atStart                            = const zero   atEnd (Linear (OffsetClosed v))    = v   atEnd (Cubic _ _ (OffsetClosed v)) = v  -- | Compute the offset from the start of a segment to the --   end.  Note that in the case of a Bézier segment this is /not/ the --   same as the length of the curve itself; for that, see 'arcLength'.-segOffset :: Segment Closed v -> v+segOffset :: Segment Closed v n -> v n segOffset (Linear (OffsetClosed v))    = v segOffset (Cubic _ _ (OffsetClosed v)) = v +-- | An open linear segment. This means the trail makes a straight line+-- from the last segment the beginning to form a loop.+openLinear :: Segment Open v n+openLinear = Linear OffsetOpen++-- | An open cubic segment. This means the trail makes a cubic bézier+-- with control vectors @v1@ and @v2@ to form a loop.+openCubic :: v n -> v n -> Segment Open v n+openCubic v1 v2 = Cubic v1 v2 OffsetOpen+ ------------------------------------------------------------ --  Computing segment envelope  ------------------------------ ------------------------------------------------------------@@ -228,63 +287,68 @@ -}  -- | The envelope for a segment is based at the segment's start.-instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (Segment Closed v) where+instance (Metric v, OrderedField n) => Enveloped (Segment Closed v n) where    getEnvelope (s@(Linear {})) = mkEnvelope $ \v ->-    maximum . map (\t -> ((s `atParam` t) <.> v) / magnitudeSq v) $ [0,1]+    maximum (map (\t -> (s `atParam` t) `dot` v) [0,1]) / quadrance v    getEnvelope (s@(Cubic c1 c2 (OffsetClosed x2))) = mkEnvelope $ \v ->     maximum .-    map (\t -> ((s `atParam` t) <.> v) / magnitudeSq v) $+    map (\t -> ((s `atParam` t) `dot` v) / quadrance v) $     [0,1] ++     filter (liftA2 (&&) (>0) (<1))-      (quadForm (3 * ((3 *^ c1 ^-^ 3 *^ c2 ^+^ x2) <.> v))-                (6 * (((-2) *^ c1 ^+^ c2) <.> v))-                ((3 *^ c1) <.> v))+      (quadForm (3 * ((3 *^ c1 ^-^ 3 *^ c2 ^+^ x2) `dot` v))+                (6 * (((-2) *^ c1 ^+^ c2) `dot` v))+                ((3 *^ c1) `dot` v))  ------------------------------------------------------------ --  Manipulating segments ------------------------------------------------------------ -instance (VectorSpace v, Fractional (Scalar v)) => Sectionable (Segment Closed v) where+instance (Additive v, Fractional n) => Sectionable (Segment Closed v n) where   splitAtParam (Linear (OffsetClosed x1)) t = (left, right)     where left  = straight p           right = straight (x1 ^-^ p)-          p = lerp zeroV x1 t+          p = lerp t zero x1   splitAtParam (Cubic c1 c2 (OffsetClosed x2)) t = (left, right)     where left  = bezier3 a b e           right = bezier3 (c ^-^ e) (d ^-^ e) (x2 ^-^ e)-          p = lerp c1    c2 t-          a = lerp zeroV c1 t-          b = lerp a     p  t-          d = lerp c2    x2 t-          c = lerp p     d  t-          e = lerp b     c  t+          p = lerp t c1 c2+          a = lerp t zero c1+          b = lerp t a p+          d = lerp t c2 x2+          c = lerp t p d+          e = lerp t b c    reverseDomain = reverseSegment  -- | Reverse the direction of a segment.-reverseSegment :: AdditiveGroup v => Segment Closed v -> Segment Closed v-reverseSegment (Linear (OffsetClosed v))       = straight (negateV v)-reverseSegment (Cubic c1 c2 (OffsetClosed x2)) = bezier3 (c2 ^-^ x2) (c1 ^-^ x2) (negateV x2)+reverseSegment :: (Num n, Additive v) => Segment Closed v n -> Segment Closed v n+reverseSegment (Linear (OffsetClosed v))       = straight (negated v)+reverseSegment (Cubic c1 c2 (OffsetClosed x2)) = bezier3 (c2 ^-^ x2) (c1 ^-^ x2) (negated x2) -instance (InnerSpace v, Floating (Scalar v), Ord (Scalar v), AdditiveGroup v)-      => HasArcLength (Segment Closed v) where+-- Imitates I.elem for intervals<0.8 and I.member for intervals>=0.8+member :: Ord a => a -> I.Interval a -> Bool+member x (I.I a b) = x >= a && x <= b+{-# INLINE member #-} -  arcLengthBounded _ (Linear (OffsetClosed x1)) = I.singleton $ magnitude x1+instance (Metric v, OrderedField n)+      => HasArcLength (Segment Closed v n) where++  arcLengthBounded _ (Linear (OffsetClosed x1)) = I.singleton $ norm x1   arcLengthBounded m s@(Cubic c1 c2 (OffsetClosed x2))     | ub - lb < m = I lb ub     | otherwise   = arcLengthBounded (m/2) l + arcLengthBounded (m/2) r    where (l,r) = s `splitAtParam` 0.5-         ub    = sum (map magnitude [c1, c2 ^-^ c1, x2 ^-^ c2])-         lb    = magnitude x2+         ub    = sum (map norm [c1, c2 ^-^ c1, x2 ^-^ c2])+         lb    = norm x2    arcLengthToParam m s _ | arcLength m s == 0 = 0.5   arcLengthToParam m s@(Linear {}) len = len / arcLength m s   arcLengthToParam m s@(Cubic {})  len-    | len `I.elem` (I (-m/2) (m/2)) = 0+    | len `member` I (-m/2) (m/2) = 0     | len < 0              = - arcLengthToParam m (fst (splitAtParam s (-1))) (-len)-    | len `I.elem` slen    = 1+    | len `member` slen    = 1     | len > I.sup slen     = 2 * arcLengthToParam m (fst (splitAtParam s 2)) len     | len < I.sup llen     = (*0.5) $ arcLengthToParam m l len     | otherwise            = (+0.5) . (*0.5)@@ -306,39 +370,30 @@ --   (Segment Closed v)@, as witnessed by 'mkFixedSeg' and --   'fromFixedSeg', but @FixedSegment@ is convenient when one needs --   the absolute locations of the vertices and control points.-data FixedSegment v = FLinear (Point v) (Point v)-                    | FCubic (Point v) (Point v) (Point v) (Point v)-  deriving Show+data FixedSegment v n = FLinear (Point v n) (Point v n)+                      | FCubic (Point v n) (Point v n) (Point v n) (Point v n)+  deriving (Eq, Ord, Show) -type instance V (FixedSegment v) = v+type instance V (FixedSegment v n) = v+type instance N (FixedSegment v n) = n -instance HasLinearMap v => Transformable (FixedSegment v) where-  transform t (FLinear p1 p2)-    = FLinear-      (transform t p1)-      (transform t p2)+instance Each (FixedSegment v n) (FixedSegment v' n') (Point v n) (Point v' n') where+  each f (FLinear p0 p1)      = FLinear <$> f p0 <*> f p1+  each f (FCubic p0 p1 p2 p3) = FCubic  <$> f p0 <*> f p1 <*> f p2 <*> f p3+  {-# INLINE each #-} -  transform t (FCubic p1 c1 c2 p2)-    = FCubic-      (transform t p1)-      (transform t c1)-      (transform t c2)-      (transform t p2)+-- | Reverses the control points.+instance Reversing (FixedSegment v n) where+  reversing (FLinear p0 p1)      = FLinear p1 p0+  reversing (FCubic p0 p1 p2 p3) = FCubic p3 p2 p1 p0 -instance VectorSpace v => HasOrigin (FixedSegment v) where-  moveOriginTo o (FLinear p1 p2)-    = FLinear-      (moveOriginTo o p1)-      (moveOriginTo o p2)+instance (Additive v, Num n) => Transformable (FixedSegment v n) where+  transform t = over each (papply t) -  moveOriginTo o (FCubic p1 c1 c2 p2)-    = FCubic-      (moveOriginTo o p1)-      (moveOriginTo o c1)-      (moveOriginTo o c2)-      (moveOriginTo o p2)+instance (Additive v, Num n) => HasOrigin (FixedSegment v n) where+  moveOriginTo o = over each (moveOriginTo o) -instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (FixedSegment v) where+instance (Metric v, OrderedField n) => Enveloped (FixedSegment v n) where   getEnvelope f = moveTo p (getEnvelope s)     where (p, s) = viewLoc $ fromFixedSeg f @@ -347,32 +402,71 @@     -- Envelope, and implement the Segment instance in terms of it,     -- instead of the other way around +instance (Metric v, OrderedField n)+      => HasArcLength (FixedSegment v n) where+  arcLengthBounded m s = arcLengthBounded m (fromFixedSeg s)+  arcLengthToParam m s = arcLengthToParam m (fromFixedSeg s)+ -- | Create a 'FixedSegment' from a located 'Segment'.-mkFixedSeg :: AdditiveGroup v => Located (Segment Closed v) -> FixedSegment v-mkFixedSeg (viewLoc -> (p, Linear (OffsetClosed v)))-  = FLinear p (p .+^ v)-mkFixedSeg (viewLoc -> (p, Cubic c1 c2 (OffsetClosed x2)))-  = FCubic  p (p .+^ c1) (p .+^ c2) (p .+^ x2)+mkFixedSeg :: (Num n, Additive v) => Located (Segment Closed v n) -> FixedSegment v n+mkFixedSeg ls =+  case viewLoc ls of+    (p, Linear (OffsetClosed v))       -> FLinear p (p .+^ v)+    (p, Cubic c1 c2 (OffsetClosed x2)) -> FCubic  p (p .+^ c1) (p .+^ c2) (p .+^ x2)  -- | Convert a 'FixedSegment' back into a located 'Segment'.-fromFixedSeg :: AdditiveGroup v => FixedSegment v -> Located (Segment Closed v)+fromFixedSeg :: (Num n, Additive v) => FixedSegment v n -> Located (Segment Closed v n) fromFixedSeg (FLinear p1 p2)      = straight (p2 .-. p1) `at` p1 fromFixedSeg (FCubic x1 c1 c2 x2) = bezier3 (c1 .-. x1) (c2 .-. x1) (x2 .-. x1) `at` x1 -type instance Codomain (FixedSegment v) = Point v+-- | Use a 'FixedSegment' to make an 'Iso' between an+-- a fixed segment and a located segment.+fixedSegIso :: (Num n, Additive v) => Iso' (FixedSegment v n) (Located (Segment Closed v n))+fixedSegIso = iso fromFixedSeg mkFixedSeg -instance VectorSpace v => Parametric (FixedSegment v) where-  atParam (FLinear p1 p2) t = alerp p1 p2 t+type instance Codomain (FixedSegment v n) = Point v++instance (Additive v, Num n) => Parametric (FixedSegment v n) where+  atParam (FLinear p1 p2) t = lerp t p1 p2   atParam (FCubic x1 c1 c2 x2) t = p3-    where p11 = alerp x1 c1 t-          p12 = alerp c1 c2 t-          p13 = alerp c2 x2 t+    where p11 = lerp t x1 c1+          p12 = lerp t c1 c2+          p13 = lerp t c2 x2 -          p21 = alerp p11 p12 t-          p22 = alerp p12 p13 t+          p21 = lerp t p11 p12+          p22 = lerp t p12 p13 -          p3  = alerp p21 p22 t+          p3  = lerp t p21 p22 +instance Num n => DomainBounds (FixedSegment v n)++instance (Additive v, Num n) => EndValues (FixedSegment v n) where+  atStart (FLinear p0 _)     = p0+  atStart (FCubic  p0 _ _ _) = p0+  atEnd   (FLinear _ p1)     = p1+  atEnd   (FCubic _ _ _ p1 ) = p1++instance (Additive v, Fractional n) => Sectionable (FixedSegment v n) where+  splitAtParam (FLinear p0 p1) t = (left, right)+    where left  = FLinear p0 p+          right = FLinear p  p1+          p = lerp t p0 p1+  splitAtParam (FCubic p0 c1 c2 p1) t = (left, right)+    where left  = FCubic p0 a b cut+          right = FCubic cut c d p1+          -- first round+          a   = lerp t p0 c1+          p   = lerp t c1 c2+          d   = lerp t c2 p1+          -- second round+          b   = lerp t a p+          c   = lerp t p d+          -- final round+          cut = lerp t b c++  reverseDomain (FLinear p0 p1) = FLinear p1 p0+  reverseDomain (FCubic p0 c1 c2 p1) = FCubic p1 c2 c1 p0+ ------------------------------------------------------------ --  Segment measures  -------------------------------------- ------------------------------------------------------------@@ -382,94 +476,161 @@ -- automatically track various monoidal \"measures\" on segments.  -- | A type to track the count of segments in a 'Trail'.-newtype SegCount = SegCount { getSegCount :: Sum Int }+newtype SegCount = SegCount (Sum Int)   deriving (Semigroup, Monoid) +instance Wrapped SegCount where+  type Unwrapped SegCount = Sum Int+  _Wrapped' = iso (\(SegCount x) -> x) SegCount++instance Rewrapped SegCount SegCount+ -- | A type to represent the total arc length of a chain of --   segments. The first component is a \"standard\" arc length, --   computed to within a tolerance of @10e-6@.  The second component is --   a generic arc length function taking the tolerance as an --   argument.-newtype ArcLength v = ArcLength-  { getArcLength :: (Sum (Interval (Scalar v)), Scalar v -> Sum (Interval (Scalar v))) } +newtype ArcLength n+  = ArcLength (Sum (Interval n), n -> Sum (Interval n))++instance Wrapped (ArcLength n) where+  type Unwrapped (ArcLength n) = (Sum (Interval n), n -> Sum (Interval n))+  _Wrapped' = iso (\(ArcLength x) -> x) ArcLength++instance Rewrapped (ArcLength n) (ArcLength n')+ -- | Project out the cached arc length, stored together with error --   bounds.-getArcLengthCached :: ArcLength v -> Interval (Scalar v)-getArcLengthCached = getSum . fst . getArcLength+getArcLengthCached :: ArcLength n -> Interval n+getArcLengthCached = getSum . fst . op ArcLength  -- | Project out the generic arc length function taking the tolerance as --   an argument.-getArcLengthFun :: ArcLength v -> Scalar v -> Interval (Scalar v)-getArcLengthFun = fmap getSum . snd . getArcLength+getArcLengthFun :: ArcLength n -> n -> Interval n+getArcLengthFun = fmap getSum . snd . op ArcLength  -- | Given a specified tolerance, project out the cached arc length if --   it is accurate enough; otherwise call the generic arc length --   function with the given tolerance.-getArcLengthBounded :: (Num (Scalar v), Ord (Scalar v))-                    => Scalar v -> ArcLength v -> Interval (Scalar v)+getArcLengthBounded :: (Num n, Ord n)+                    => n -> ArcLength n -> Interval n getArcLengthBounded eps al   | I.width cached <= eps = cached   | otherwise             = getArcLengthFun al eps   where     cached = getArcLengthCached al-deriving instance (Num (Scalar v), Ord (Scalar v)) => Semigroup (ArcLength v)-deriving instance (Num (Scalar v), Ord (Scalar v)) => Monoid    (ArcLength v)+deriving instance (Num n, Ord n) => Semigroup (ArcLength n)+deriving instance (Num n, Ord n) => Monoid    (ArcLength n)  -- | A type to represent the total cumulative offset of a chain of --   segments.-newtype TotalOffset v = TotalOffset { getTotalOffset :: v }+newtype TotalOffset v n = TotalOffset (v n) -instance AdditiveGroup v => Semigroup (TotalOffset v) where+instance Wrapped (TotalOffset v n) where+  type Unwrapped (TotalOffset v n) = v n+  _Wrapped' = iso (\(TotalOffset x) -> x) TotalOffset++instance Rewrapped (TotalOffset v n) (TotalOffset v' n')++instance (Num n, Additive v) => Semigroup (TotalOffset v n) where   TotalOffset v1 <> TotalOffset v2 = TotalOffset (v1 ^+^ v2) -instance AdditiveGroup v => Monoid (TotalOffset v) where-  mempty  = TotalOffset zeroV+instance (Num n, Additive v) => Monoid (TotalOffset v n) where+  mempty  = TotalOffset zero   mappend = (<>)  -- | A type to represent the offset and envelope of a chain of --   segments.  They have to be paired into one data structure, since --   combining the envelopes of two consecutive chains needs to take---   the offset of the the offset of the first into account.-data OffsetEnvelope v = OffsetEnvelope-  { oeOffset   :: TotalOffset v-  , oeEnvelope :: Envelope v+--   the offset of the first into account.+data OffsetEnvelope v n = OffsetEnvelope+  { _oeOffset   :: !(TotalOffset v n)+  , _oeEnvelope :: Envelope v n   } -instance (InnerSpace v, OrderedField (Scalar v)) => Semigroup (OffsetEnvelope v) where+makeLenses ''OffsetEnvelope++instance (Metric v, OrderedField n) => Semigroup (OffsetEnvelope v n) where   (OffsetEnvelope o1 e1) <> (OffsetEnvelope o2 e2)-    = OffsetEnvelope-        (o1 <> o2)-        (e1 <> moveOriginBy (negateV . getTotalOffset $ o1) e2)+    = let !negOff = negated . op TotalOffset $ o1+          e2Off = moveOriginBy negOff e2+          !_unused = maybe () (\f -> f `seq` ()) $ appEnvelope e2Off+      in OffsetEnvelope+          (o1 <> o2)+          (e1 <> e2Off)  -- | @SegMeasure@ collects up all the measurements over a chain of --   segments.-type SegMeasure v = SegCount-                ::: ArcLength v-                ::: OffsetEnvelope v-                ::: ()+type SegMeasure v n = SegCount+                  ::: ArcLength n+                  ::: OffsetEnvelope v n+                  ::: ()   -- unfortunately we can't cache Trace, since there is not a generic   -- instance Traced (Segment Closed v), only Traced (Segment Closed R2). -instance (InnerSpace v, OrderedField (Scalar v))-    => Measured (SegMeasure v) (SegMeasure v) where+instance (Metric v, OrderedField n)+    => Measured (SegMeasure v n) (SegMeasure v n) where   measure = id -instance (OrderedField (Scalar v), InnerSpace v)-    => Measured (SegMeasure v) (Segment Closed v) where-  measure s = (SegCount . Sum $ 1)+instance (OrderedField n, Metric v)+    => Measured (SegMeasure v n) (Segment Closed v n) where+  measure s = (SegCount . Sum) 1              -- cache arc length with two orders of magnitude more             -- accuracy than standard, so we have a hope of coming out             -- with an accurate enough total arc length for             -- reasonable-length trails-           *: (ArcLength $ ( Sum $ arcLengthBounded (stdTolerance/100) s-                           , Sum . flip arcLengthBounded s-                           )-              )+            *: ArcLength ( Sum $ arcLengthBounded (stdTolerance/100) s+                         , Sum . flip arcLengthBounded s               ) -           *: (OffsetEnvelope-                (TotalOffset . segOffset $ s)-                (getEnvelope s)-              )-           *: ()+            *: OffsetEnvelope (TotalOffset . segOffset $ s)+                              (getEnvelope s)++            *: ()++------------------------------------------------------------+--  Serialize instances+------------------------------------------------------------++instance (Serialize (v n)) => Serialize (Segment Open v n) where+  {-# INLINE put #-}+  put segment = case segment of+    Linear OffsetOpen    -> Serialize.put True+    Cubic v w OffsetOpen -> do+      Serialize.put False+      Serialize.put v+      Serialize.put w++  {-# INLINE get #-}+  get = do+    isLinear <- Serialize.get+    case isLinear of+      True  -> return (Linear OffsetOpen)+      False -> do+        v <- Serialize.get+        w <- Serialize.get+        return (Cubic v w OffsetOpen)++instance (Serialize (v n)) => Serialize (Segment Closed v n) where+  {-# INLINE put #-}+  put segment = case segment of+    Linear (OffsetClosed z)    -> do+      Serialize.put z+      Serialize.put True+    Cubic v w (OffsetClosed z) -> do+      Serialize.put z+      Serialize.put False+      Serialize.put v+      Serialize.put w++  {-# INLINE get #-}+  get = do+    z <- Serialize.get+    isLinear <- Serialize.get+    case isLinear of+      True  -> return (Linear (OffsetClosed z))+      False -> do+        v <- Serialize.get+        w <- Serialize.get+        return (Cubic v w (OffsetClosed z))
+ src/Diagrams/Size.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE DeriveGeneric              #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE UndecidableInstances       #-}+{-# LANGUAGE ViewPatterns               #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Size+-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Utilities for working with sizes of objects.+--+-----------------------------------------------------------------------------+module Diagrams.Size+  ( -- * Size spec+    SizeSpec++    -- ** Making size spec+  , mkSizeSpec+  , dims+  , absolute++    -- ** Extracting size specs+  , getSpec+  , specToSize++    -- ** Functions on size specs+  , requiredScale+  , requiredScaling+  , sized+  , sizedAs+  , sizeAdjustment+  ) where++import           Control.Applicative+import           Control.Lens         hiding (transform)+import           Control.Monad+import           Data.Foldable        as F+import           Data.Hashable+import           Data.Maybe+import           Data.Semigroup+import           Data.Typeable+import           GHC.Generics         (Generic)+import           Prelude++import           Diagrams.BoundingBox+import           Diagrams.Core++import           Linear.Affine+import           Linear.Vector++------------------------------------------------------------+-- Computing diagram sizes+------------------------------------------------------------++-- | A 'SizeSpec' is a way of specifying a size without needed lengths for all+--   the dimensions.+newtype SizeSpec v n = SizeSpec (v n)+  deriving (+  Eq,+  Typeable,+  Functor,+  Generic,+  Hashable,+  Show)++type instance V (SizeSpec v n) = v+type instance N (SizeSpec v n) = n++-- | Retrieve a size spec as a vector of maybe values. Only positive sizes are+--   returned.+getSpec :: (Functor v, Num n, Ord n) => SizeSpec v n -> v (Maybe n)+getSpec (SizeSpec sp) = mfilter (>0) . Just <$> sp++-- | Make a 'SizeSpec' from a vector of maybe values. Any negative values will+--   be ignored. For 2D 'SizeSpec's see 'mkWidth' and 'mkHeight' from+--   "Diagrams.TwoD.Size".+mkSizeSpec :: (Functor v, Num n) => v (Maybe n) -> SizeSpec v n+mkSizeSpec = dims . fmap (fromMaybe 0)++-- | Make a 'SizeSpec' from a vector. Any negative values will be ignored.+dims :: v n -> SizeSpec v n+dims = SizeSpec++-- | A size spec with no hints to the size.+absolute :: (Additive v, Num n) => SizeSpec v n+absolute = SizeSpec zero++-- | @specToSize n spec@ extracts a size from a 'SizeSpec' @sz@. Any values not+--   specified in the spec are replaced by the smallest of the values that are+--   specified. If there are no specified values (i.e. 'absolute') then @n@ is+--   used.+specToSize :: (Foldable v, Functor v, Num n, Ord n) => n -> SizeSpec v n -> v n+specToSize n (getSpec -> spec) = fmap (fromMaybe smallest) spec+  where+    smallest = fromMaybe n $ minimumOf (folded . _Just) spec++-- | @requiredScale spec sz@ returns the largest scaling factor to make+--   something of size @sz@ fit the requested size @spec@ without changing the+--   aspect ratio. @sz@ should be non-zero (otherwise a scale of 1 is+--   returned). For non-uniform scaling see 'boxFit'.+requiredScale :: (Additive v, Foldable v, Fractional n, Ord n)+              => SizeSpec v n -> v n -> n+requiredScale (getSpec -> spec) sz+  | allOf (folded . _Just) (<= 0) usedSz = 1+  | otherwise                            = fromMaybe 1 mScale+  where+    usedSz = liftI2 (<$) sz spec+    scales = liftI2 (^/) spec sz+    mScale = minimumOf (folded . _Just) scales++-- | Return the 'Transformation' calcuated from 'requiredScale'.+requiredScaling :: (Additive v, Foldable v, Fractional n, Ord n)+  => SizeSpec v n -> v n -> Transformation v n+requiredScaling spec = scaling . requiredScale spec++-- | Uniformly scale any enveloped object so that it fits within the+--   given size. For non-uniform scaling see 'boxFit'.+sized :: (InSpace v n a, HasLinearMap v, Transformable a, Enveloped a)+      => SizeSpec v n -> a -> a+sized spec a = transform (requiredScaling spec (size a)) a++-- | Uniformly scale an enveloped object so that it \"has the same+--   size as\" (fits within the width and height of) some other+--   object.+sizedAs :: (InSpace v n a, SameSpace a b, HasLinearMap v, Transformable a,+            Enveloped a, Enveloped b)+        => b -> a -> a+sizedAs other = sized (dims $ size other)++-- | Get the adjustment to fit a 'BoundingBox' in the given 'SizeSpec'. The+--   vector is the new size and the transformation to position the lower+--   corner at the origin and scale to the size spec.+sizeAdjustment :: (Additive v, Foldable v, OrderedField n)+  => SizeSpec v n -> BoundingBox v n -> (v n, Transformation v n)+sizeAdjustment spec bb = (sz', t)+  where+    v = (0.5 *^ P sz') .-. (s *^ fromMaybe origin (boxCenter bb))++    sz  = boxExtents bb+    sz' = if allOf folded isJust (getSpec spec)+            then specToSize 0 spec+            else s *^ sz++    s = requiredScale spec sz++    t = translation v <> scaling s
− src/Diagrams/Solve.hs
@@ -1,118 +0,0 @@-{-# OPTIONS_GHC -fno-warn-unused-binds #-}--------------------------------------------------------------------------------- |--- Module      :  Diagrams.Solve--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)--- License     :  BSD-style (see LICENSE)--- Maintainer  :  diagrams-discuss@googlegroups.com------ Exact solving of low-degree (n <= 3) polynomials.----------------------------------------------------------------------------------module Diagrams.Solve-       ( quadForm-       , cubForm-       ) where--import           Data.List     (maximumBy)-import           Data.Ord      (comparing)--import           Diagrams.Util (tau)----------------------------------------------------------------- Quadratic formula----------------------------------------------------------------- | The quadratic formula.-quadForm :: (Floating d, Ord d) => d -> d -> d -> [d]-quadForm a b c--    -- There are infinitely many solutions in this case,-    -- so arbitrarily return 0-  | a == 0 && b == 0 && c == 0 = [0]--    -- c /= 0-  | a == 0 && b == 0 = []--    -- linear-  | a == 0    = [-c/b]--    -- no real solutions-  | d < 0     = []--    -- ax^2 + c = 0-  | b == 0    = [sqrt (-c/a), -sqrt (-c/a)]--    -- multiplicity 2 solution-  | d == 0    = [-b/(2*a)]--    -- see http://www.mpi-hd.mpg.de/astrophysik/HEA/internal/Numerical_Recipes/f5-6.pdf-  | otherwise = [q/a, c/q]- where d = b*b - 4*a*c-       q = -1/2*(b + signum b * sqrt d)--quadForm_prop :: Double -> Double -> Double -> Bool-quadForm_prop a b c = all (aboutZero . eval) (quadForm a b c)-  where eval x = a*x*x + b*x + c-        aboutZero x = abs x < tolerance-        tolerance = 1e-10----------------------------------------------------------------- Cubic formula----------------------------------------------------------------- See http://en.wikipedia.org/wiki/Cubic_formula#General_formula_of_roots---- | Solve the cubic equation ax^3 + bx^2 + cx + d = 0, returning a---   list of all real roots.-cubForm :: (Floating d, Ord d) => d -> d -> d -> d -> [d]-cubForm a b c d-  | aboutZero a             = quadForm b c d--    -- three real roots, use trig method to avoid complex numbers-  | delta >  0              = map trig [0,1,2]--    -- one real root of multiplicity 3-  | delta == 0 && disc == 0 = [ -b/(3*a) ]--    -- two real roots, one of multiplicity 2-  | delta == 0 && disc /= 0 = [ (b*c - 9*a*d)/(2*disc)-                              , (9*a*a*d - 4*a*b*c + b*b*b)/(a * disc)-                              ]--    -- one real root (and two complex)-  | otherwise               = [-b/(3*a) - cc/(3*a) + disc/(3*a*cc)]-- where delta  = 18*a*b*c*d - 4*b*b*b*d + b*b*c*c - 4*a*c*c*c - 27*a*a*d*d-       disc   = 3*a*c - b*b-       qq     = sqrt(-27*a*a*delta)-       qq'    | aboutZero disc = maximumBy (comparing (abs . (+xx))) [qq, -qq]-              | otherwise = qq-       cc     = cubert (1/2*(qq' + xx))-       xx     = 2*b*b*b - 9*a*b*c + 27*a*a*d-       p      = disc/(3*a*a)-       q      = xx/(27*a*a*a)-       trig k = 2 * sqrt(-p/3) * cos(1/3*acos(3*q/(2*p)*sqrt(-3/p)) - k*tau/3)-                - b/(3*a)--       cubert x | x < 0     = -((-x)**(1/3))-                | otherwise = x**(1/3)--       aboutZero x = abs x < toler-       toler = 1e-10--cubForm_prop :: Double -> Double -> Double -> Double -> Bool-cubForm_prop a b c d = all (aboutZero . eval) (cubForm a b c d)-  where eval x = a*x*x*x + b*x*x + c*x + d-        aboutZero x = abs x < tolerance-        tolerance = 1e-5-           -- Basically, however large you set the tolerance it seems-           -- that quickcheck can always come up with examples where-           -- the returned solutions evaluate to something near zero-           -- but larger than the tolerance (but it takes it more-           -- tries the larger you set the tolerance). Wonder if this-           -- is an inherent limitation or (more likely) a problem-           -- with numerical stability.  If this turns out to be an-           -- issue in practice we could, say, use the solutions-           -- generated here as very good guesses to a numerical-           -- solver which can give us a more precise answer?
+ src/Diagrams/Tangent.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GADTs                #-} -- for ghc < 7.8, TypeFamilies covers GADT patten mathcing in > 7.8+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE UndecidableInstances #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Tangent+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Computing tangent and normal vectors for segments and trails.+--+-----------------------------------------------------------------------------+module Diagrams.Tangent+    ( -- ** Tangents+      tangentAtParam+    , tangentAtStart+    , tangentAtEnd++      -- ** Normals+    , normalAtParam+    , normalAtStart+    , normalAtEnd++      -- ** Tangent newtype+    , Tangent(..)+    )+    where++import           Diagrams.Core+import           Diagrams.Located+import           Diagrams.Parametric+import           Diagrams.Segment++import           Linear.Vector+import           Linear.Metric+import           Linear.V2++------------------------------------------------------------+-- Tangent+------------------------------------------------------------++-- | A newtype wrapper used to give different instances of+--   'Parametric' and 'EndValues' that compute tangent vectors.+newtype Tangent t = Tangent t++type instance V (Tangent t) = V t+type instance N (Tangent t) = N t+type instance Codomain (Tangent t) = V t++instance DomainBounds t => DomainBounds (Tangent t) where+  domainLower (Tangent t) = domainLower t+  domainUpper (Tangent t) = domainUpper t++instance Parametric (Tangent t) => Parametric (Tangent (Located t)) where+  Tangent l `atParam` p = Tangent (unLoc l) `atParam` p++instance (DomainBounds t, EndValues (Tangent t))+    => EndValues (Tangent (Located t)) where+  atStart (Tangent l) = atStart (Tangent (unLoc l))+  atEnd   (Tangent l) = atEnd   (Tangent (unLoc l))++-- | Compute the tangent vector to a segment or trail at a particular+--   parameter.+--+--   Examples of more specific types this function can have include+--+--   * @Segment Closed V2 -> Double -> V2 Double@+--+--   * @Trail' Line V2 -> Double -> V2 Double@+--+--   * @Located (Trail V2) -> Double -> V2 Double@+--+--   See the instances listed for the 'Tangent' newtype for more.+tangentAtParam :: Parametric (Tangent t) => t -> N t -> Vn t+tangentAtParam t p = Tangent t `atParam` p++-- | Compute the tangent vector at the start of a segment or trail.+tangentAtStart :: EndValues (Tangent t) => t -> Vn t+tangentAtStart = atStart . Tangent++-- | Compute the tangent vector at the end of a segment or trail.+tangentAtEnd :: EndValues (Tangent t) => t -> Vn t+tangentAtEnd = atEnd . Tangent++--------------------------------------------------+-- Segment++instance (Additive v, Num n)+    => Parametric (Tangent (Segment Closed v n)) where+  Tangent (Linear (OffsetClosed v)) `atParam` _ = v+  Tangent (Cubic c1 c2 (OffsetClosed x2)) `atParam` p+    = (3*(3*p*p-4*p+1))*^c1 ^+^ (3*(2-3*p)*p)*^c2 ^+^ (3*p*p)*^x2++instance (Additive v, Num n)+    => EndValues (Tangent (Segment Closed v n)) where+  atStart (Tangent (Linear (OffsetClosed v)))      = v+  atStart (Tangent (Cubic c1 _ _))                 = c1+  atEnd   (Tangent (Linear (OffsetClosed v)))      = v+  atEnd   (Tangent (Cubic _ c2 (OffsetClosed x2))) = x2 ^-^ c2++instance (Additive v, Num n)+    => Parametric (Tangent (FixedSegment v n)) where+  atParam (Tangent fSeg) = atParam $ Tangent (fromFixedSeg fSeg)++instance (Additive v, Num n)+    => EndValues (Tangent (FixedSegment v n)) where+  atStart (Tangent fSeg) = atStart $ Tangent (fromFixedSeg fSeg)+  atEnd (Tangent fSeg)   = atEnd $ Tangent (fromFixedSeg fSeg)++------------------------------------------------------------+-- Normal+------------------------------------------------------------++-- | Compute the (unit) normal vector to a segment or trail at a+--   particular parameter.+--+--   Examples of more specific types this function can have include+--+--   * @Segment Closed V2 Double -> Double -> V2 Double@+--+--   * @Trail' Line V2 Double -> Double -> V2 Double@+--+--   * @Located (Trail V2 Double) -> Double -> V2 Double@+--+--   See the instances listed for the 'Tangent' newtype for more.+normalAtParam+  :: (InSpace V2 n t, Parametric (Tangent t), Floating n)+  => t -> n -> V2 n+normalAtParam t p = normize (t `tangentAtParam` p)++-- | Compute the normal vector at the start of a segment or trail.+normalAtStart+  :: (InSpace V2 n t, EndValues (Tangent t), Floating n)+  => t -> V2 n+normalAtStart = normize . tangentAtStart++-- | Compute the normal vector at the end of a segment or trail.+normalAtEnd+  :: (InSpace V2 n t, EndValues (Tangent t), Floating n)+  => t -> V2 n+normalAtEnd = normize . tangentAtEnd++-- | Construct a normal vector from a tangent.+normize :: Floating n => V2 n -> V2 n+normize = negated . perp . signorm
+ src/Diagrams/ThreeD.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeSynonymInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD+-- Copyright   :  (c) 2013-2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- This module defines the three-dimensional vector space R^3,+-- three-dimensional transformations, and various predefined+-- three-dimensional shapes.  This module re-exports useful+-- functionality from a group of more specific modules:+--+--   * "Diagrams.ThreeD.Types" defines basic types for two-dimensional+--     diagrams, including types representing the 3D Euclidean vector+--     space and various systems of representing directions.+--+--   * "Diagrams.ThreeD.Transform" defines R^3-specific+--     transformations such as rotation by an angle, and scaling,+--     translation, and reflection in the X, Y, and Z directions.+--     "Diagrams.ThreeD.Deform" defines several R^3-specific+--     non-affine transformations, such as projections.+--+--   * "Diagrams.ThreeD.Shapes" defines three-dimensional solids,+--     e.g. spheres and cubes.+--+--   * "Diagrams.ThreeD.Vector" defines some special 3D vectors and+--     functions for converting between vectors and directions.+--+--   * "Diagrams.ThreeD.Light" and "Diagrams.ThreeD.Camera" define types needed+--     for rendering 3D geometry to (2D) images.+--+--   * "Diagrams.ThreeD.Align" defines many alignment combinators+--     specialized to three dimensions.+--+--   * "Diagrams.ThreeD.Attributes" defines 3D-specific attributes+--     such as surface color, diffuse reflectance, and specular+--     highlights.+-----------------------------------------------------------------------------+module Diagrams.ThreeD+  ( module Diagrams.ThreeD.Align+  , module Diagrams.ThreeD.Attributes+  , module Diagrams.ThreeD.Camera+  , module Diagrams.ThreeD.Deform+  , module Diagrams.ThreeD.Light+  , module Diagrams.ThreeD.Shapes+  , module Diagrams.ThreeD.Transform+  , module Diagrams.ThreeD.Types+  , module Diagrams.ThreeD.Vector+  ) where++import           Diagrams.ThreeD.Align+import           Diagrams.ThreeD.Attributes+import           Diagrams.ThreeD.Camera+import           Diagrams.ThreeD.Deform+import           Diagrams.ThreeD.Light+import           Diagrams.ThreeD.Shapes+import           Diagrams.ThreeD.Transform+import           Diagrams.ThreeD.Types+import           Diagrams.ThreeD.Vector
+ src/Diagrams/ThreeD/Align.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies    #-}+{-# LANGUAGE TypeOperators   #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Align+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Alignment combinators specialized for three dimensions.  See+-- "Diagrams.Align" for more general alignment combinators.+--+-- The basic idea is that alignment is achieved by moving diagrams'+-- local origins relative to their envelopes or traces (or some other+-- sort of boundary).  For example, to align several diagrams along+-- their tops, we first move their local origins to the upper edge of+-- their boundary (using e.g. @map 'alignZMax'@), and then put them+-- together with their local origins along a line (using e.g. 'cat'+-- from "Diagrams.Combinators").+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Align+    ( -- * Absolute alignment+      -- ** Align by envelope+      alignXMin, alignXMax, alignYMin, alignYMax, alignZMin, alignZMax++      -- ** Align by trace+    , snugXMin, snugXMax, snugYMin, snugYMax, snugZMin, snugZMax++      -- * Relative alignment+    , alignX, snugX, alignY, snugY, alignZ, snugZ++      -- * Centering+    , centerX, centerY, centerZ+    , centerXY, centerXZ, centerYZ, centerXYZ+    , snugCenterX, snugCenterY, snugCenterZ+    , snugCenterXY, snugCenterXZ, snugCenterYZ, snugCenterXYZ++    ) where++import           Diagrams.Core++import           Diagrams.Align+import           Diagrams.ThreeD.Types+import           Diagrams.ThreeD.Vector+import           Diagrams.TwoD.Align++-- | Translate the diagram along unitX so that all points have+--   positive x-values.+alignXMin :: (InSpace v n a, R1 v, Fractional n, Alignable a, HasOrigin a) => a -> a+alignXMin = align unit_X++snugXMin :: (InSpace v n a, R1 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugXMin = snug unit_X++-- | Translate the diagram along unitX so that all points have+-- negative x-values.+alignXMax :: (InSpace v n a, R1 v, Fractional n, Alignable a, HasOrigin a) => a -> a+alignXMax = align unitX++snugXMax :: (InSpace v n a, R1 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugXMax = snug unitX+++-- | Translate the diagram along unitY so that all points have+--   positive y-values.+alignYMin :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a) => a -> a+alignYMin = align unit_Y++snugYMin :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugYMin = snug unit_Y++-- | Translate the diagram along unitY so that all points have+-- negative y-values.+alignYMax :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a) => a -> a+alignYMax = align unitY++snugYMax :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugYMax = snug unitY+++-- | Translate the diagram along unitZ so that all points have+--   positive z-values.+alignZMin :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => a -> a+alignZMin = align unit_Z++snugZMin :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugZMin = snug unit_Z++-- | Translate the diagram along unitZ so that all points have+-- negative z-values.+alignZMax :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => a -> a+alignZMax = align unitZ++snugZMax :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugZMax = snug unitZ++-- | Like 'alignX', but moving the local origin in the Z direction, with an+--   argument of @1@ corresponding to the top edge and @(-1)@ corresponding+--   to the bottom edge.+alignZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => n -> a -> a+alignZ = alignBy unitZ++-- | See the documentation for 'alignZ'.+snugZ :: (V a ~ v, N a ~ n, Alignable a, Traced a, HasOrigin a,+          R3 v, Fractional n) => n -> a -> a+snugZ = snugBy unitZ+++-- | Center the local origin along the Z-axis.+centerZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => a -> a+centerZ = alignBy unitZ 0++snugCenterZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugCenterZ = snugBy unitZ 0++-- | Center along both the X- and Z-axes.+centerXZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => a -> a+centerXZ = centerX . centerZ++snugCenterXZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugCenterXZ = snugCenterX . snugCenterZ+++-- | Center along both the Y- and Z-axes.+centerYZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => a -> a+centerYZ = centerZ . centerY++snugCenterYZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugCenterYZ = snugCenterZ . snugCenterY++-- | Center an object in three dimensions.+centerXYZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a) => a -> a+centerXYZ = centerX . centerY . centerZ++snugCenterXYZ :: (InSpace v n a, R3 v, Fractional n, Alignable a, HasOrigin a, Traced a) => a -> a+snugCenterXYZ = snugCenterX . snugCenterY . snugCenterZ
+ src/Diagrams/ThreeD/Attributes.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE TemplateHaskell            #-}+{-# LANGUAGE TypeFamilies               #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Attributes+-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Diagrams may have /attributes/ which affect the way they are+-- rendered.  This module defines some common attributes relevant in+-- 3D; particular backends may also define more backend-specific+-- attributes.+--+-- Every attribute type must have a /semigroup/ structure, that is, an+-- associative binary operation for combining two attributes into one.+-- Unless otherwise noted, all the attributes defined here use the+-- 'Last' structure, that is, combining two attributes simply keeps+-- the second one and throws away the first.  This means that child+-- attributes always override parent attributes.+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Attributes where++import           Control.Lens+import           Data.Semigroup+import           Data.Typeable++import           Data.Colour++import           Diagrams.Core++-- | @SurfaceColor@ is the inherent pigment of an object, assumed to+-- be opaque.+newtype SurfaceColor = SurfaceColor (Last (Colour Double))+  deriving (Typeable, Semigroup, Show)++instance AttributeClass SurfaceColor++_SurfaceColor :: Iso' SurfaceColor (Colour Double)+_SurfaceColor = iso (\(SurfaceColor (Last c)) -> c) (SurfaceColor . Last)++-- | Set the surface color.+sc :: HasStyle d => Colour Double -> d -> d+sc = applyAttr . review _SurfaceColor++-- | Lens onto the surface colour of a style.+_sc :: Lens' (Style v n) (Maybe (Colour Double))+_sc = atAttr . mapping _SurfaceColor++-- | @Diffuse@ is the fraction of incident light reflected diffusely,+-- that is, in all directions.  The actual light reflected is the+-- product of this value, the incident light, and the @SurfaceColor@+-- Attribute.  For physical reasonableness, @Diffuse@ should have a+-- value between 0 and 1; this is not checked.+newtype Diffuse = Diffuse (Last Double)+  deriving (Typeable, Semigroup, Show)++instance AttributeClass Diffuse++-- | Isomorphism between 'Diffuse' and 'Double'+_Diffuse :: Iso' Diffuse Double+_Diffuse = iso (\(Diffuse (Last d)) -> d) (Diffuse . Last)++-- | Set the diffuse reflectance.+diffuse :: HasStyle d => Double -> d -> d+diffuse = applyAttr . review _Diffuse++-- | Lens onto the possible diffuse reflectance in a style.+_diffuse :: Lens' (Style v n) (Maybe Double)+_diffuse = atAttr . mapping _Diffuse++-- | @Ambient@ is an ad-hoc representation of indirect lighting.  The+-- product of @Ambient@ and @SurfaceColor@ is added to the light+-- leaving an object due to diffuse and specular terms.  @Ambient@ can+-- be set per-object, and can be loosely thought of as the product of+-- indirect lighting incident on that object and the diffuse+-- reflectance.+newtype Ambient = Ambient (Last Double)+  deriving (Typeable, Semigroup, Show)++instance AttributeClass Ambient++_Ambient :: Iso' Ambient Double+_Ambient = iso (\(Ambient (Last d)) -> d) (Ambient . Last)++-- | Set the emittance due to ambient light.+ambient :: HasStyle d => Double -> d -> d+ambient = applyAttr . review _Ambient++-- | Lens onto the possible ambience in a style.+_ambient :: Lens' (Style v n) (Maybe Double)+_ambient = atAttr . mapping _Ambient++-- | A specular highlight has two terms, the intensity, between 0 and+-- 1, and the size.  The highlight size is assumed to be the exponent+-- in a Phong shading model (though Backends are free to use a+-- different shading model).  In this model, reasonable values are+-- between 1 and 50 or so, with higher values for shinier objects.+-- Physically, the intensity and the value of @Diffuse@ must add up to+-- less than 1; this is not enforced.+data Specular = Specular+  { _specularIntensity :: Double+  , _specularSize      :: Double+  } deriving Show++makeLenses ''Specular++newtype Highlight = Highlight (Last Specular)+  deriving (Typeable, Semigroup, Show)++instance AttributeClass Highlight++_Highlight :: Iso' Highlight Specular+_Highlight = iso (\(Highlight (Last s)) -> s) (Highlight . Last)++-- | Set the specular highlight.+highlight :: HasStyle d => Specular -> d -> d+highlight = applyAttr . review _Highlight++-- | Lens onto the possible specular highlight in a style+_highlight :: Lens' (Style v n) (Maybe Specular)+_highlight = atAttr . mapping _Highlight++-- | Traversal over the highlight intensity of a style. If the style has+--   no 'Specular', setting this will do nothing.+highlightIntensity :: Traversal' (Style v n) Double+highlightIntensity = _highlight . _Just . specularSize++-- | Traversal over the highlight size in a style. If the style has no+--   'Specular', setting this will do nothing.+highlightSize :: Traversal' (Style v n) Double+highlightSize = _highlight . _Just . specularSize
+ src/Diagrams/ThreeD/Camera.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE UndecidableInstances  #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Camera+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Types to specify viewpoint for 3D rendering.+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Camera+       ( Camera  -- do not export constructor+        -- These are safe to construct manually+       , PerspectiveLens(..), OrthoLens(..)+       , horizontalFieldOfView, verticalFieldOfView+       , orthoWidth, orthoHeight+       , camLoc, camForward, camUp, camRight, camLens+       , facing_ZCamera, mm50Camera+       , mm50, mm50Wide, mm50Narrow+       , aspect, camAspect+       )+       where++import           Control.Lens           (makeLenses)+import           Data.Monoid+import           Data.Typeable++import           Diagrams.Angle+import           Diagrams.Core+import           Diagrams.Direction+import           Diagrams.ThreeD.Vector++import           Linear.V3++-- Parameterize Camera on the lens type, so that Backends can express which+-- lenses they handle.+data Camera l n = Camera+    { camLoc  :: Point V3 n+    , forward :: V3 n+    , up      :: V3 n+    , lens    :: l n+    }+  deriving Typeable++type instance V (Camera l n) = V3+type instance N (Camera l n) = n++class Typeable l => CameraLens l where+  -- | The natural aspect ratio of the projection.+  aspect :: Floating n => l n -> n++-- | A perspective projection+data PerspectiveLens n = PerspectiveLens+  { _horizontalFieldOfView :: Angle n -- ^ Horizontal field of view.+  , _verticalFieldOfView   :: Angle n -- ^ Vertical field of view.+  }+  deriving Typeable++makeLenses ''PerspectiveLens++type instance V (PerspectiveLens n) = V3+type instance N (PerspectiveLens n) = n++instance CameraLens PerspectiveLens where+  aspect (PerspectiveLens h v) = angleRatio h v++-- | An orthographic projection+data OrthoLens n = OrthoLens+               { _orthoWidth  :: n -- ^ Width+               , _orthoHeight :: n -- ^ Height+               }+  deriving Typeable++makeLenses ''OrthoLens++type instance V (OrthoLens n) = V3+type instance N (OrthoLens n) = n++instance CameraLens OrthoLens where+  aspect (OrthoLens h v) = h / v++instance Num n => Transformable (Camera l n) where+  transform t (Camera p f u l) =+      Camera (transform t p)+             (transform t f)+             (transform t u)+             l++instance Num n => Renderable (Camera l n) NullBackend where+  render _ _ = mempty++-- | A camera at the origin facing along the negative Z axis, with its+-- up-axis coincident with the positive Y axis.  The field of view is+-- chosen to match a 50mm camera on 35mm film. Note that Cameras take+-- up no space in the Diagram.+mm50Camera :: (Typeable n, Floating n, Ord n, Renderable (Camera PerspectiveLens n) b)+           => QDiagram b V3 n Any+mm50Camera = facing_ZCamera mm50++-- | 'facing_ZCamera l' is a camera at the origin facing along the+-- negative Z axis, with its up-axis coincident with the positive Y+-- axis, with the projection defined by l.+facing_ZCamera :: (Floating n, Ord n, Typeable n, CameraLens l, Renderable (Camera l n) b) =>+                  l n -> QDiagram b V3 n Any+facing_ZCamera l = mkQD (Prim $ Camera origin unit_Z unitY l)+        mempty mempty mempty (Query . const . Any $ False)+{-# ANN facing_ZCamera ("HLint: ignore Use camelCase" :: String) #-}++mm50, mm50Wide, mm50Narrow :: Floating n => PerspectiveLens n++-- | mm50 has the field of view of a 50mm lens on standard 35mm film,+-- hence an aspect ratio of 3:2.+mm50 = PerspectiveLens (40.5 @@ deg) (27 @@ deg)++-- | mm50blWide has the same vertical field of view as mm50, but an+-- aspect ratio of 1.6, suitable for wide screen computer monitors.+mm50Wide = PerspectiveLens (43.2 @@ deg)  (27 @@ deg)++-- | mm50Narrow has the same vertical field of view as mm50, but an+-- aspect ratio of 4:3, for VGA and similar computer resolutions.+mm50Narrow = PerspectiveLens (36 @@ deg) (27 @@ deg)++camForward :: Camera l n -> Direction V3 n+camForward = direction . forward++camUp :: Camera l n -> Direction V3 n+camUp = direction . up++camRight :: Fractional n => Camera l n -> Direction V3 n+camRight c = direction right where+  right = cross (forward c) (up c)++camLens :: Camera l n -> l n+camLens = lens++camAspect :: (Floating n, CameraLens l) => Camera l n -> n+camAspect = aspect . camLens
+ src/Diagrams/ThreeD/Deform.hs view
@@ -0,0 +1,28 @@+module Diagrams.ThreeD.Deform+  ( parallelX0, perspectiveX1, facingX+  , parallelY0, perspectiveY1, facingY+  , parallelZ0, perspectiveZ1, facingZ+  ) where++import           Control.Lens++import           Diagrams.Deform+import           Diagrams.TwoD.Deform++import           Linear.V3+import           Linear.Vector++-- | The parallel projection onto the plane z=0+parallelZ0 :: (R3 v, Num n) => Deformation v v n+parallelZ0 = Deformation (_z .~ 0)++-- | The perspective division onto the plane z=1 along lines going+--   through the origin.+perspectiveZ1 :: (R3 v, Functor v, Fractional n) => Deformation v v n+perspectiveZ1 = Deformation $ \p -> p ^/ (p ^. _z)++facingZ :: (R3 v, Functor v, Fractional n) => Deformation v v n+facingZ = Deformation $+  \p -> let z = p ^. _z+        in  p ^/ z & _z .~ z+
+ src/Diagrams/ThreeD/Light.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE UndecidableInstances  #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Render+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Types to specify lighting for 3D rendering.+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Light where++import           Data.Colour+import           Data.Monoid+import           Data.Typeable++import           Diagrams.Core+import           Diagrams.Direction+import           Diagrams.ThreeD.Types++-- | A @PointLight@ radiates uniformly in all directions from a given+-- point.+data PointLight n = PointLight (Point V3 n) (Colour Double)+  deriving Typeable++type instance V (PointLight n) = V3+type instance N (PointLight n) = n++-- | A @ParallelLight@ casts parallel rays in the specified direction,+-- from some distant location outside the scene.+data ParallelLight n = ParallelLight (V3 n) (Colour Double)+  deriving Typeable++type instance V (ParallelLight n) = V3+type instance N (ParallelLight n) = n++instance Fractional n => Transformable (PointLight n) where+  transform t (PointLight p c) = PointLight (transform t p) c++instance Transformable (ParallelLight n) where+  transform t (ParallelLight v c) = ParallelLight (transform t v) c++-- | Construct a Diagram with a single PointLight at the origin, which+-- takes up no space.+pointLight :: (Typeable n, Num n, Ord n, Renderable (PointLight n) b)+              => Colour Double -- ^ The color of the light+              -> QDiagram b V3 n Any+pointLight c = mkQD (Prim $ PointLight origin c) mempty mempty mempty+               (Query . const . Any $ False)++-- | Construct a Diagram with a single ParallelLight, which takes up no space.+parallelLight :: (Typeable n, OrderedField n, Renderable (ParallelLight n) b)+                 => Direction V3 n -- ^ The direction in which the light travels.+                 -> Colour Double  -- ^ The color of the light.+                 -> QDiagram b V3 n Any+parallelLight d c = mkQD (Prim $ ParallelLight (fromDirection d) c)+                    mempty mempty mempty (Query . const . Any $ False)
+ src/Diagrams/ThreeD/Projection.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE TypeFamilies          #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Projection+-- Copyright   :  (c) 2014 diagrams team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- 3D projections are a way of viewing a three-dimensional objects on a+-- two-dimensional plane.+--+-- This module can be used with the functions in "Linear.Projection".+--+-- Disclaimer: This module should be considered experimental and is+-- likely to change.+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Projection+  ( -- * Orthographic projections++    -- $orthographic+    -- ** Parallel projections+    facingXY+  , facingXZ+  , facingYZ++    -- ** axonometric+    -- $axonometric++    -- *** Isometric projections+    -- $isometric+  , isometricApply+  , isometric++  , lookingAt++    -- ** Affine maps+  , m44AffineApply+  , m44AffineMap+  , m33AffineApply+  , m33AffineMap++    -- * Perspective projections+    -- $perspective+    -- ** Perspective deformations+  , m44Deformation+  , module Linear.Projection+  ) where++import           Control.Lens           hiding (transform)+import           Data.Functor.Rep++import           Diagrams.Core+import           Diagrams.Deform+import           Diagrams.Direction+import           Diagrams.LinearMap+import           Diagrams.ThreeD.Types  (P3)+import           Diagrams.ThreeD.Vector++import           Linear                 as L+import           Linear.Affine+import           Linear.Projection++------------------------------------------------------------------------+-- Orthographic projections+------------------------------------------------------------------------++-- $orthographic+-- Orthographic projections are a form of parallel projections where are+-- projection lines are orthogonal to the projection plane.++-- Parallel projections++-- | Look at the xy-plane with y as the up direction.+facingXY :: (Epsilon n, Floating n) => AffineMap V3 V2 n+facingXY = lookingAt unitZ origin yDir++-- | Look at the xz-plane with z as the up direction.+facingXZ :: (Epsilon n, Floating n) => AffineMap V3 V2 n+facingXZ = lookingAt unitY origin zDir++-- | Look at the yz-plane with z as the up direction.+facingYZ :: (Epsilon n, Floating n) => AffineMap V3 V2 n+facingYZ = lookingAt unitX origin zDir++-- $axonometric+-- Axonometric projections are a type of orthographic projection where+-- the object is rotated along one or more of its axes relative to the+-- plane of projection.++-- $isometric+-- Isometric projections are when the scale along each axis of the+-- projection is the same and the angle between any axis is 120+-- degrees.++-- | Apply an isometric projection given the up direction+isometricApply :: (InSpace V3 n a, InSpace V2 n b, AffineMappable a b, Floating n, Epsilon n)+               => Direction V3 n -> a -> b+isometricApply up = amap (isometric up)++-- | Make an isometric affine map with the given up direction.+isometric :: (Floating n, Epsilon n) => Direction V3 n -> AffineMap V3 V2 n+isometric up = m44AffineMap m+  where+    m = lookAt (V3 1 1 1) zero (fromDirection up)++lookingAt :: (Epsilon n, Floating n)+          => P3 n -- ^ Eye+          -> P3 n -- ^ Center+          -> Direction V3 n -- ^ Up+          -> AffineMap V3 V2 n+lookingAt (P cam) (P center) d = m44AffineMap m+  where+    m = lookAt cam center (d^._Dir)++-- | Apply the affine part of a homogeneous matrix.+m44AffineApply :: (InSpace V3 n a, InSpace V2 n b, AffineMappable a b)+               => M44 n -> a -> b+m44AffineApply = amap . m44AffineMap++-- | Create an 'AffineMap' from a 4x4 homogeneous matrix, ignoring any+--   perspective transforms.+m44AffineMap :: Num n => M44 n -> AffineMap V3 V2 n+m44AffineMap m = AffineMap (LinearMap f) (f v)+  where+    f  = view _xy . (m' !*)+    m' = m ^. linearTransform+    v  = m ^. L.translation++-- | Apply a transformation matrix and translation.+m33AffineApply :: (InSpace V3 n a, InSpace V2 n b, AffineMappable a b)+               => M33 n -> V2 n -> a -> b+m33AffineApply m = amap . m33AffineMap m++-- | Create an 'AffineMap' from a 3x3 transformation matrix and a+--   translation vector.+m33AffineMap :: Num n => M33 n -> V2 n -> AffineMap V3 V2 n+m33AffineMap m = AffineMap (LinearMap f)+  where+    f = view _xy . (m !*)++-- | Extract the linear transform part of a homogeneous matrix.+linearTransform :: (Representable u, R3 v, R3 u) => Lens' (u (v n)) (M33 n)+linearTransform = column _xyz . _xyz++------------------------------------------------------------------------+-- Perspective transforms+------------------------------------------------------------------------++-- For the time being projective transforms use the deformable class.+-- Eventually we would like to replace this with a more specialised+-- method.++-- $perspective+-- Perspective projections are when closer objects appear bigger.++-- | Make a deformation from a 4x4 homogeneous matrix.+m44Deformation :: Fractional n => M44 n -> Deformation V3 V2 n+m44Deformation m =+  Deformation (P . view _xy . normalizePoint . (m !*) . point . view _Point)+
src/Diagrams/ThreeD/Shapes.hs view
@@ -1,8 +1,11 @@-{-# LANGUAGE TypeFamilies-           , FlexibleContexts-           , MultiParamTypeClasses-           , ViewPatterns-  #-}+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE UndecidableInstances  #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.ThreeD.Shapes@@ -15,45 +18,307 @@ -----------------------------------------------------------------------------  module Diagrams.ThreeD.Shapes-       (-         Ellipsoid(..)-       , sphere-       ) where+  (+    -- * Skinned class+    Skinned(..) -import Prelude hiding (minimum)-import Data.Semigroup+    -- * Basic 3D shapes+  , Ellipsoid(..)+  , sphere -import Data.AffineSpace-import Data.Monoid.Inf (minimum)-import Data.VectorSpace+  , Box(..)+  , cube -import Diagrams.Core+  , Frustum(..)+  , frustum+  , cone+  , cylinder -import Diagrams.ThreeD.Types-import Diagrams.Solve+    -- * Constructive solid geometry+  , CSG(..)+  , union+  , intersection+  , difference+  ) where -data Ellipsoid = Ellipsoid T3+import           Control.Lens              (review, (^.), _1)+import           Data.Typeable -type instance V Ellipsoid = R3+import           Data.Semigroup+import           Diagrams.Angle+import           Diagrams.Core+import           Diagrams.Core.Trace+import           Diagrams.Points+import           Diagrams.Query+import           Diagrams.Solve.Polynomial+import           Diagrams.ThreeD.Types+import           Diagrams.ThreeD.Vector -instance Transformable Ellipsoid where+import           Linear.Affine+import           Linear.Metric+import           Linear.Vector++data Ellipsoid n = Ellipsoid (Transformation V3 n)+  deriving Typeable++type instance V (Ellipsoid n) = V3+type instance N (Ellipsoid n) = n++instance Fractional n => Transformable (Ellipsoid n) where   transform t1 (Ellipsoid t2) = Ellipsoid (t1 <> t2) -instance IsPrim Ellipsoid+instance Fractional n => Renderable (Ellipsoid n) NullBackend where+  render _ _ = mempty -instance Renderable Ellipsoid NullBackend where+instance OrderedField n => Enveloped (Ellipsoid n) where+  getEnvelope (Ellipsoid tr) = transform tr . mkEnvelope $ \v -> 1 / norm v++instance OrderedField n => Traced (Ellipsoid n) where+  getTrace (Ellipsoid tr) = transform tr . mkTrace $ \(P p) v -> let+    a  =    v `dot` v+    b  = 2 * (p `dot` v)+    c  =    (p `dot` p) - 1+    in+     mkSortedList $ quadForm a b c++-- | A sphere of radius 1 with its center at the origin.+sphere :: Num n => Ellipsoid n+sphere = Ellipsoid mempty++data Box n = Box (Transformation V3 n)+  deriving Typeable++type instance V (Box n) = V3+type instance N (Box n) = n++instance Fractional n => Transformable (Box n) where+  transform t1 (Box t2) = Box (t1 <> t2)++instance Fractional n => Renderable (Box n) NullBackend where   render _ _ = mempty -sphere :: (Backend b R3, Renderable Ellipsoid b) => Diagram b R3-sphere = mkQD (Prim $ Ellipsoid mempty)-              (mkEnvelope sphereEnv)-              (mkTrace sphereTrace)-              mempty-              (Query sphereQuery)-  where sphereEnv v = 1 / magnitude v-        sphereTrace p v = minimum (quadForm a b c)-          where a = v <.> v-                b = 2 *^ p' <.> v-                c = p' <.> p' - 1-                p' = p .-. origin-        sphereQuery v = Any $ magnitudeSq (v .-. origin) <= 1+instance OrderedField n => Enveloped (Box n) where+  getEnvelope (Box tr) = transform tr . mkEnvelope $ \v ->+    maximum (map (v `dot`) corners) / quadrance v where+      corners = mkR3 <$> [0,1] <*> [0,1] <*> [0,1]++instance (Fractional n, Ord n) => Traced (Box n) where+  getTrace (Box tr) = transform tr . mkTrace $ \p v -> let+    (x0, y0, z0) = unp3 p+    (vx, vy, vz) = unr3 v+    intersections f d = case d of+      0 -> []+      _ -> [-f/d, (1-f)/d]+    ts = concat $ zipWith intersections [x0,y0,z0] [vx,vy,vz]+    atT t = p .+^ (t*^v)+    range u = and [x >= 0, x <= 1, y >= 0, y <= 1, z >= 0, z <= 1] where+      (x, y, z) = unp3 u+    in+     -- ts gives all intersections with the planes forming the box+     -- filter keeps only those actually on the box surface+     mkSortedList . filter (range . atT) $ ts where++-- | A cube with side length 1, in the positive octant, with one+-- vertex at the origin.+cube :: Num n => Box n+cube = Box mempty++data Frustum n = Frustum n n (Transformation V3 n)+  deriving Typeable++type instance V (Frustum n) = V3+type instance N (Frustum n) = n++instance Fractional n => Transformable (Frustum n) where+  transform t1 (Frustum r0 r1 t2) = Frustum r0 r1 (t1 <> t2)++instance Fractional n => Renderable (Frustum n) NullBackend where+  render _ _ = mempty++instance (OrderedField n, RealFloat n) => Enveloped (Frustum n) where+  -- The plane containing v and the z axis intersects the frustum in a trapezoid+  -- Test the four corners of this trapezoid; one must determine the Envelope+  getEnvelope (Frustum r0 r1 tr) = transform tr . mkEnvelope $ \v ->let+    θ = v ^. _theta+    corners = [(r1,θ,1), (-r1,θ,1), (r0,θ,0), (-r0,θ,0)]+    in+     maximum . map (norm . project v . review r3CylindricalIso) $ corners++instance (RealFloat n, Ord n) => Traced (Frustum n) where+  -- The trace can intersect the sides of the cone or one of the end+  -- caps The sides are described by a quadric equation; substitute+  -- in the parametric form of the ray but disregard any+  -- intersections outside z = [0,1] Similarly, find intersections+  -- with the planes z=0, z=1, but disregard any r>r0, r>r1+  getTrace (Frustum r0 r1 tr) = transform tr . mkTrace $ \p v -> let+    (px, py, pz) = unp3 p+    (vx, vy, vz) = unr3 v+    ray t = p .+^ t *^ v+    dr = r1 - r0+    a = vx**2 + vy**2 - vz**2 * dr**2+    b = 2 * (px * vx + py * vy - (r0+pz*dr) * dr  * vz)+    c = px**2 + py**2 - (r0 + dr*pz)**2+    zbounds t = ray t ^. _z >= 0+         && ray t ^. _z <= 1+    ends = concatMap cap [0,1]+    cap z = [ t | ray t ^. lensP . r3CylindricalIso . _1 < r0 + z * dr ]+      where+      t = (z - pz) / vz+    in+     mkSortedList $ filter zbounds (quadForm a b c) ++ ends++-- | A frustum of a right circular cone.  It has height 1 oriented+-- along the positive z axis, and radii r0 and r1 at Z=0 and Z=1.+-- 'cone' and 'cylinder' are special cases.+frustum :: Num n => n -> n -> Frustum n+frustum r0 r1 = Frustum r0 r1 mempty++-- | A cone with its base centered on the origin, with radius 1 at the+-- base, height 1, and it's apex on the positive Z axis.+cone :: Num n => Frustum n+cone = frustum 1 0++-- | A circular cylinder of radius 1 with one end cap centered on the+-- origin, and extending to Z=1.+cylinder :: Num n => Frustum n+cylinder = frustum 1 1++-- | Types which can be rendered as 3D Diagrams.+class Skinned t where+  skin :: (Renderable t b, N t ~ n, TypeableFloat n) => t -> QDiagram b V3 n Any++instance (Num n, Ord n) => HasQuery (Ellipsoid n) Any where+  getQuery (Ellipsoid tr) = transform tr $+    Query $ \v -> Any $ quadrance (v .-. origin) <= 1++instance OrderedField n => Skinned (Ellipsoid n) where+  skin s = mkQD (Prim s) (getEnvelope s) (getTrace s) mempty (getQuery s)++instance (Num n, Ord n) => HasQuery (Box n) Any where+  getQuery (Box tr) = transform tr . Query $ Any . range where+    range u = and [x >= 0, x <= 1, y >= 0, y <= 1, z >= 0, z <= 1] where+      (x, y, z) = unp3 u++instance OrderedField n => Skinned (Box n) where+  skin s = mkQD (Prim s) (getEnvelope s) (getTrace s) mempty (getQuery s)++instance (OrderedField n) => HasQuery (Frustum n) Any where+  getQuery (Frustum r0 r1 tr)= transform tr $+    Query $ \p -> let+      z = p^._z+      r = r0 + (r1 - r0)*z+      v = p .-. origin+      a = norm $ projectXY v+      projectXY u = u ^-^ project unitZ u+      in+       Any $ z >= 0 && z <= 1 && a <= r++instance Skinned (Frustum n) where+  skin s = mkQD (Prim s) (getEnvelope s) (getTrace s) mempty (getQuery s)++-- The CSG type needs to form a tree to be useful.  This+-- implementation requires Backends to support all the included+-- primitives.  If that turns out to be a problem, we have several+-- options:+-- a) accept runtime errors for unsupported primitives+-- b) carry the set of primitives in a row type in the CSG type+-- c) implement CSG in Haskell, so Backends supporting triangle meshes+--    can fall back to those.+-- (c) is worth doing anyway; I'm ambivalent about the others.  -DMB++-- | A tree of Constructive Solid Geometry operations and the primitives that+-- can be used in them.+data CSG n+  = CsgEllipsoid (Ellipsoid n)+  | CsgBox (Box n)+  | CsgFrustum (Frustum n)+  | CsgUnion [CSG n]+  | CsgIntersection [CSG n]+  | CsgDifference (CSG n) (CSG n)+  deriving Typeable++type instance V (CSG n) = V3+type instance N (CSG n) = n++instance Fractional n => Transformable (CSG n) where+  transform t (CsgEllipsoid p) = CsgEllipsoid $ transform t p+  transform t (CsgBox p) = CsgBox $ transform t p+  transform t (CsgFrustum p) = CsgFrustum $ transform t p+  transform t (CsgUnion ps) = CsgUnion $ map (transform t) ps+  transform t (CsgIntersection ps) = CsgIntersection $ map (transform t) ps+  transform t (CsgDifference p1 p2) = CsgDifference (transform t p1) (transform t p2)++-- | The Envelope for an Intersection or Difference is simply the+-- Envelope of the Union.  This is wrong but easy to implement.+instance RealFloat n => Enveloped (CSG n) where+  getEnvelope (CsgEllipsoid p)      = getEnvelope p+  getEnvelope (CsgBox p)            = getEnvelope p+  getEnvelope (CsgFrustum p)        = getEnvelope p+  getEnvelope (CsgUnion ps)         = foldMap getEnvelope ps+  getEnvelope (CsgIntersection ps)  = foldMap getEnvelope ps+  getEnvelope (CsgDifference p1 p2) = getEnvelope p1 <> getEnvelope p2+-- TODO after implementing some approximation scheme, calculate+-- correct (approximate) envelopes for intersections and difference.++instance (Floating n, Ord n) => HasQuery (CSG n) Any where+  getQuery (CsgEllipsoid prim) = getQuery prim+  getQuery (CsgBox prim) = getQuery prim+  getQuery (CsgFrustum prim) = getQuery prim+  getQuery (CsgUnion ps) = foldMap getQuery ps+  getQuery (CsgIntersection ps) =+    Any . getAll <$> foldMap (fmap (All . getAny) . getQuery) ps+  getQuery (CsgDifference p1 p2) = inOut <$> getQuery p1 <*> getQuery p2 where+    inOut (Any a) (Any b) = Any $ a && not b++instance (RealFloat n, Ord n) => Traced (CSG n) where+  getTrace (CsgEllipsoid p) = getTrace p+  getTrace (CsgBox p) = getTrace p+  getTrace (CsgFrustum p) = getTrace p+  -- on surface of some p, and not inside any of the others+  getTrace (CsgUnion []) = mempty+  getTrace (CsgUnion (s:ss)) = mkTrace t where+    t pt v = onSortedList (filter $ without s) (appTrace (getTrace (CsgUnion ss)) pt v)+         <> onSortedList (filter $ without (CsgUnion ss)) (appTrace (getTrace s) pt v) where+      newPt dist = pt .+^ v ^* dist+      without prim = not . inquire prim . newPt+  -- on surface of some p, and inside all the others+  getTrace (CsgIntersection []) = mempty+  getTrace (CsgIntersection (s:ss)) = mkTrace t where+    t pt v = onSortedList (filter $ within s) (appTrace (getTrace (CsgIntersection ss)) pt v)+         <> onSortedList (filter $ within (CsgIntersection ss)) (appTrace (getTrace s) pt v) where+      newPt dist = pt .+^ v ^* dist+      within prim = inquire prim . newPt+  -- on surface of p1, outside p2, or on surface of p2, inside p1+  getTrace (CsgDifference s1 s2) = mkTrace t where+    t pt v = onSortedList (filter $ not . within s2) (appTrace (getTrace s1) pt v)+         <> onSortedList (filter $ within s1) (appTrace (getTrace s2) pt v) where+      newPt dist = pt .+^ v ^* dist+      within prim = inquire prim . newPt++instance (RealFloat n, Ord n) => Skinned (CSG n) where+  skin s = mkQD (Prim s) (getEnvelope s) (getTrace s) mempty (getQuery s)++-- | Types which can be included in CSG trees.+class CsgPrim a where+  toCsg :: a n -> CSG n++instance CsgPrim Ellipsoid where+  toCsg = CsgEllipsoid++instance CsgPrim Box where+  toCsg = CsgBox++instance CsgPrim Frustum where+  toCsg = CsgFrustum++instance CsgPrim CSG where+  toCsg = id++union :: (CsgPrim a, CsgPrim b) => a n -> b n -> CSG n+union a b = CsgUnion [toCsg a, toCsg b]++intersection :: (CsgPrim a, CsgPrim b) => a n -> b n -> CSG n+intersection a b = CsgIntersection [toCsg a, toCsg b]++difference :: (CsgPrim a, CsgPrim b) => a n -> b n -> CSG n+difference a b = CsgDifference (toCsg a) (toCsg b)
+ src/Diagrams/ThreeD/Size.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies     #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Size+-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Utilities for working with sizes of three-dimensional objects.+--+-----------------------------------------------------------------------------+module Diagrams.ThreeD.Size+       (+         -- ** Computing sizes+         extentX, extentY, extentZ++         -- ** Specifying sizes+       , mkSizeSpec3D+       , dims3D++       ) where++import           Diagrams.Core+import           Diagrams.Core.Envelope+import           Diagrams.Size+import           Diagrams.TwoD.Size+import           Diagrams.ThreeD.Types+import           Diagrams.ThreeD.Vector++------------------------------------------------------------+-- Computing diagram sizes+------------------------------------------------------------++-- | Compute the absolute z-coordinate range of an enveloped object in+--   the form @(lo,hi)@. Return @Nothing@ for objects with an empty+--   envelope.+extentZ :: (InSpace v n a, R3 v, Enveloped a) => a -> Maybe (n, n)+extentZ = extent unitZ++-- | Make a 'SizeSpec' from possibly-specified width and height.+mkSizeSpec3D :: Num n => Maybe n -> Maybe n -> Maybe n -> SizeSpec V3 n+mkSizeSpec3D x y z = mkSizeSpec (V3 x y z)++-- | Make a 'SizeSpec' from a width and height.+dims3D :: n -> n -> n -> SizeSpec V3 n+dims3D x y z = dims (V3 x y z)+
+ src/Diagrams/ThreeD/Transform.hs view
@@ -0,0 +1,223 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE ViewPatterns          #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Transform+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Transformations specific to three dimensions, with a few generic+-- transformations (uniform scaling, translation) also re-exported for+-- convenience.+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Transform+       ( T3++         -- * Rotation+       , aboutX, aboutY, aboutZ+       , rotationAbout, rotateAbout+       , pointAt, pointAt'++       -- * Scaling+       , scalingX, scalingY, scalingZ+       , scaleX, scaleY, scaleZ+       , scaling, scale++       -- * Translation+       , translationX, translateX+       , translationY, translateY+       , translationZ, translateZ+       , translation, translate++         -- * Reflection+       , reflectionX, reflectX+       , reflectionY, reflectY+       , reflectionZ, reflectZ+       , reflectionAcross, reflectAcross++       ) where++import           Diagrams.Core+import           Diagrams.Core.Transform++import           Diagrams.Angle+import           Diagrams.Direction+import           Diagrams.Points+import           Diagrams.ThreeD.Types+import           Diagrams.Transform++import           Control.Lens            (view, (&), (*~), (.~), (//~))+import           Data.Semigroup+import           Diagrams.TwoD.Transform++import           Linear.Affine+import           Linear.Metric+import           Linear.V3               (cross)+import           Linear.Vector++-- | Create a transformation which rotates by the given angle about+--   a line parallel the Z axis passing through the local origin.+--   A positive angle brings positive x-values towards the positive-y axis.+--+--   The angle can be expressed using any type which is an+--   instance of 'Angle'.  For example, @aboutZ (1\/4 \@\@+--   'turn')@, @aboutZ (tau\/4 \@\@ 'rad')@, and @aboutZ (90 \@\@+--   'deg')@ all represent the same transformation, namely, a+--   counterclockwise rotation by a right angle.  For more general rotations,+--   see 'rotationAbout'.+--+--   Note that writing @aboutZ (1\/4)@, with no type annotation, will+--   yield an error since GHC cannot figure out which sort of angle+--   you want to use.+aboutZ :: Floating n => Angle n -> Transformation V3 n+aboutZ (view rad -> a) = fromOrthogonal r where+  r = rot a <-> rot (-a)+  rot θ (V3 x y z) = V3 (cos θ * x - sin θ * y)+                        (sin θ * x + cos θ * y)+                        z++-- | Like 'aboutZ', but rotates about the X axis, bringing positive y-values+-- towards the positive z-axis.+aboutX :: Floating n => Angle n -> Transformation V3 n+aboutX (view rad -> a) = fromOrthogonal r where+  r = rot a <-> rot (-a)+  rot θ (V3 x y z) = V3 x+                        (cos θ * y - sin θ * z)+                        (sin θ * y + cos θ * z)++-- | Like 'aboutZ', but rotates about the Y axis, bringing postive+-- x-values towards the negative z-axis.+aboutY :: Floating n => Angle n -> Transformation V3 n+aboutY (view rad -> a) = fromOrthogonal r where+  r = rot a <-> rot (-a)+  rot θ (V3 x y z) = V3 (cos θ * x + sin θ * z)+                        y+                        (-sin θ * x + cos θ * z)++-- | @rotationAbout p d a@ is a rotation about a line parallel to @d@+--   passing through @p@.+rotationAbout+  :: Floating n+  => Point V3 n         -- ^ origin of rotation+  -> Direction V3 n     -- ^ direction of rotation axis+  -> Angle n            -- ^ angle of rotation+  -> Transformation V3 n+rotationAbout (P t) d (view rad -> a)+  = mconcat [translation (negated t),+             fromOrthogonal r,+             translation t] where+    r = rot a <-> rot (-a)+    w = fromDirection d++    rot θ v =          v ^* cos θ+           ^+^ cross w v ^* sin θ+           ^+^         w ^* ((w `dot` v) * (1 - cos θ))++-- | @rotationAbout p d a@ is a rotation about a line parallel to @d@+--   passing through @p@.+rotateAbout+  :: (InSpace V3 n t, Floating n, Transformable t)+  => Point V3 n         -- ^ origin of rotation+  -> Direction V3 n     -- ^ direction of rotation axis+  -> Angle n            -- ^ angle of rotation+  -> t -> t+rotateAbout p d theta = transform (rotationAbout p d theta)++-- | @pointAt about initial final@ produces a rotation which brings+-- the direction @initial@ to point in the direction @final@ by first+-- panning around @about@, then tilting about the axis perpendicular+-- to @about@ and @final@.  In particular, if this can be accomplished+-- without tilting, it will be, otherwise if only tilting is+-- necessary, no panning will occur.  The tilt will always be between+-- ± 1/4 turn.+pointAt :: (Floating n, Ord n)+        => Direction V3 n -> Direction V3 n -> Direction V3 n+        -> Transformation V3 n+pointAt a i f = pointAt' (fromDirection a) (fromDirection i) (fromDirection f)++-- | pointAt' has the same behavior as 'pointAt', but takes vectors+-- instead of directions.+pointAt' :: (Floating n, Ord n) => V3 n -> V3 n -> V3 n -> Transformation V3 n+pointAt' about initial final = pointAtUnit (signorm about) (signorm initial) (signorm final)++-- | pointAtUnit has the same behavior as @pointAt@, but takes unit vectors.+pointAtUnit :: (Floating n, Ord n) => V3 n -> V3 n -> V3 n -> Transformation V3 n+pointAtUnit about initial final = tilt <> pan where+  -- rotating u by (signedAngle rel u v) about rel gives a vector in the direction of v+  signedAngle rel u v = signum (cross u v `dot` rel) *^ angleBetween u v+  inPanPlaneF = final ^-^ project about final+  inPanPlaneI = initial ^-^ project about initial+  panAngle    = signedAngle about inPanPlaneI inPanPlaneF+  pan         = rotationAbout origin (direction about) panAngle+  tiltAngle   = signedAngle tiltAxis (transform pan initial) final+  tiltAxis    = cross final about+  tilt        = rotationAbout origin (direction tiltAxis) tiltAngle++-- Scaling -------------------------------------------------++-- | Construct a transformation which scales by the given factor in+--   the z direction.+scalingZ :: (Additive v, R3 v, Fractional n) => n -> Transformation v n+scalingZ c = fromSymmetric $ (_z *~ c) <-> (_z //~ c)++-- | Scale a diagram by the given factor in the z direction.  To scale+-- uniformly, use 'scale'.+scaleZ :: (InSpace v n t, R3 v, Fractional n, Transformable t) => n -> t -> t+scaleZ = transform . scalingZ++-- Translation ----------------------------------------++-- | Construct a transformation which translates by the given distance+--   in the z direction.+translationZ :: (Additive v, R3 v, Num n) => n -> Transformation v n+translationZ z = translation (zero & _z .~ z)++-- | Translate a diagram by the given distance in the y+--   direction.+translateZ :: (InSpace v n t, R3 v, Transformable t) => n -> t -> t+translateZ = transform . translationZ++-- Reflection ----------------------------------------------++-- | Construct a transformation which flips a diagram across z=0,+-- i.e. sends the point (x,y,z) to (x,y,-z).+reflectionZ :: (Additive v, R3 v, Num n) => Transformation v n+reflectionZ = fromSymmetric $ (_z *~ (-1)) <-> (_z *~ (-1))++-- | Flip a diagram across z=0, i.e. send the point (x,y,z) to+-- (x,y,-z).+reflectZ :: (InSpace v n t, R3 v, Transformable t) => t -> t+reflectZ = transform reflectionZ++-- | @reflectionAcross p v@ is a reflection across the plane through+--   the point @p@ and normal to vector @v@. This also works as a 2D+--   transform where @v@ is the normal to the line passing through point+--   @p@.+reflectionAcross :: (Metric v, Fractional n)+  => Point v n -> v n -> Transformation v n+reflectionAcross p v =+  conjugate (translation (origin .-. p)) reflect+    where+      reflect = fromLinear t (linv t)+      t       = f v <-> f (negated v)+      f u w   = w ^-^ 2 *^ project u w++-- | @reflectAcross p v@ reflects a diagram across the plane though+--   the point @p@ and the vector @v@. This also works as a 2D transform+--   where @v@ is the normal to the line passing through point @p@.+reflectAcross :: (InSpace v n t, Metric v, Fractional n, Transformable t)+  => Point v n -> v n -> t -> t+reflectAcross p v = transform (reflectionAcross p v)
src/Diagrams/ThreeD/Types.hs view
@@ -1,11 +1,4 @@-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}- {-# OPTIONS_GHC -fno-warn-orphans #-}  -----------------------------------------------------------------------------@@ -21,77 +14,85 @@  module Diagrams.ThreeD.Types        ( -- * 3D Euclidean space-         R3, r3, unr3-       , P3, p3, unp3-       , T3+         r3, unr3, mkR3+       , p3, unp3, mkP3+       , r3Iso, p3Iso, project+       , r3SphericalIso, r3CylindricalIso+       , V3 (..), P3,      T3+       , R1 (..), R2 (..), R3 (..)         ) where -import Diagrams.Coordinates-import Diagrams.TwoD.Types-import Diagrams.Core+import           Control.Lens        (Iso', iso, _1, _2, _3) -import Control.Newtype+import           Diagrams.Angle+import           Diagrams.Core+import           Diagrams.Points+import           Diagrams.TwoD.Types -import Data.Basis-import Data.VectorSpace+import           Linear.Metric+import           Linear.V3           as V  ------------------------------------------------------------ -- 3D Euclidean space --- | The three-dimensional Euclidean vector space R^3.-newtype R3 = R3 { unR3 :: (Double, Double, Double) }-  deriving (AdditiveGroup, Eq, Ord, Show, Read)+-- Basic R3 types -instance Newtype R3 (Double, Double, Double) where-  pack   = R3-  unpack = unR3+type P3 = Point V3+type T3 = Transformation V3 +r3Iso :: Iso' (V3 n) (n, n, n)+r3Iso = iso unr3 r3+ -- | Construct a 3D vector from a triple of components.-r3 :: (Double, Double, Double) -> R3-r3 = pack+r3 :: (n, n, n) -> V3 n+r3 (x,y,z) = V3 x y z +-- | Curried version of `r3`.+mkR3 :: n -> n -> n -> V3 n+mkR3 = V3+ -- | Convert a 3D vector back into a triple of components.-unr3 :: R3 -> (Double, Double, Double)-unr3 = unpack+unr3 :: V3 n -> (n, n, n)+unr3 (V3 x y z) = (x,y,z) -type instance V R3 = R3+-- | Construct a 3D point from a triple of coordinates.+p3 :: (n, n, n) -> P3 n+p3 = P . r3 -instance VectorSpace R3 where-  type Scalar R3 = Double-  (*^) = over R3 . (*^)+-- | Convert a 3D point back into a triple of coordinates.+unp3 :: P3 n -> (n, n, n)+unp3 (P (V3 x y z)) = (x,y,z) -instance HasBasis R3 where-  type Basis R3 = Either () (Either () ()) -- = Basis (Double, Double, Double)-  basisValue = R3 . basisValue-  decompose  = decompose  . unR3-  decompose' = decompose' . unR3+p3Iso :: Iso' (P3 n) (n, n, n)+p3Iso = iso unp3 p3 -instance InnerSpace R3 where-  (unR3 -> vec1) <.> (unR3 -> vec2) = vec1 <.> vec2+-- | Curried version of `r3`.+mkP3 :: n -> n -> n -> P3 n+mkP3 x y z = p3 (x, y, z) -instance Coordinates R3 where-  type FinalCoord R3       = Double-  type PrevDim R3          = R2-  type Decomposition R3    = Double :& Double :& Double+type instance V (V3 n) = V3+type instance N (V3 n) = n -  (coords -> x :& y) & z   = r3 (x,y,z)-  coords (unR3 -> (x,y,z)) = x :& y :& z+instance Transformable (V3 n) where+  transform = apply --- | Points in R^3.-type P3 = Point R3+r3SphericalIso :: RealFloat n => Iso' (V3 n) (n, Angle n, Angle n)+r3SphericalIso = iso+  (\v@(V3 x y z) -> (norm v, atan2A y x, acosA (z / norm v)))+  (\(r,θ,φ)   -> V3 (r * cosA θ * sinA φ) (r * sinA θ * sinA φ) (r * cosA φ)) --- | Construct a 3D point from a triple of coordinates.-p3 :: (Double, Double, Double) -> P3-p3 = pack . pack+r3CylindricalIso :: RealFloat n => Iso' (V3 n) (n, Angle n, n)+r3CylindricalIso = iso+  (\(V3 x y z) -> (sqrt $ x*x + y*y, atan2A y x, z))+  (\(r,θ,z)    -> V3 (r*cosA θ) (r*sinA θ) z) --- | Convert a 2D point back into a triple of coordinates.-unp3 :: P3 -> (Double, Double, Double)-unp3 = unpack . unpack+instance HasR V3 where+  _r = r3SphericalIso . _1 --- | Transformations in R^3.-type T3 = Transformation R3+instance HasTheta V3 where+  _theta = r3CylindricalIso . _2 -instance Transformable R3 where-  transform = apply+instance HasPhi V3 where+  _phi = r3SphericalIso . _3 
+ src/Diagrams/ThreeD/Vector.hs view
@@ -0,0 +1,35 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.ThreeD.Vector+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Three-dimensional vectors.+--+-----------------------------------------------------------------------------+module Diagrams.ThreeD.Vector+       ( -- * Special 3D vectors+         unitX, unitY, unitZ, unit_X, unit_Y, unit_Z+       , xDir, yDir, zDir+       ) where++import           Control.Lens          ((&), (.~))++import           Diagrams.ThreeD.Types+import           Diagrams.TwoD.Vector+import           Diagrams.Direction++import           Linear.Vector++-- | The unit vector in the positive Y direction.+unitZ :: (R3 v, Additive v, Num n) => v n+unitZ = zero & _z .~ 1++-- | The unit vector in the negative X direction.+unit_Z :: (R3 v, Additive v, Num n) => v n+unit_Z = zero & _z .~ (-1)++-- | A 'Direction' pointing in the Z direction.+zDir :: (R3 v, Additive v, Num n) => Direction v n+zDir = dir unitZ
src/Diagrams/Trace.hs view
@@ -1,4 +1,10 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MonoLocalBinds   #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Trace@@ -27,24 +33,30 @@      ) where -import           Diagrams.Core        (HasLinearMap, Point, Subdiagram,-                                       location, origin, setTrace, trace)+import           Diagrams.Core        (OrderedField, Point, Subdiagram, location,+                                       origin, setTrace, trace) import           Diagrams.Core.Trace  import           Data.Maybe-import           Data.VectorSpace     (Scalar, negateV)+import           Data.Semigroup import           Diagrams.Combinators (withTrace) +import           Linear.Metric+import           Linear.Vector+ -- | Compute the furthest point on the boundary of a subdiagram, --   beginning from the location (local origin) of the subdiagram and --   moving in the direction of the given vector.  If there is no such --   point, the origin is returned; see also 'boundaryFromMay'.-boundaryFrom :: (HasLinearMap v, Ord (Scalar v)) => Subdiagram b v m -> v -> Point v+boundaryFrom :: (OrderedField n, Metric v, Semigroup m)+              => Subdiagram b v n m -> v n -> Point v n boundaryFrom s v = fromMaybe origin $ boundaryFromMay s v  -- | Compute the furthest point on the boundary of a subdiagram, --   beginning from the location (local origin) of the subdiagram and --   moving in the direction of the given vector, or @Nothing@ if --   there is no such point.-boundaryFromMay :: (HasLinearMap v, Ord (Scalar v)) => Subdiagram b v m -> v -> Maybe (Point v)-boundaryFromMay s v = traceP (location s) (negateV v) s+boundaryFromMay :: (Metric v, OrderedField n, Semigroup m)+                => Subdiagram b v n m -> v n -> Maybe (Point v n)+boundaryFromMay s v = traceP (location s) (negated v) s+
src/Diagrams/Trail.hs view
@@ -1,880 +1,1396 @@-{-# LANGUAGE DeriveFunctor              #-}-{-# LANGUAGE EmptyDataDecls             #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE StandaloneDeriving         #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE TypeOperators              #-}-{-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE ViewPatterns               #-}--------------------------------------------------------------------------------- |--- Module      :  Diagrams.Trail--- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)--- License     :  BSD-style (see LICENSE)--- Maintainer  :  diagrams-discuss@googlegroups.com------ This module defines /trails/, translationally invariant paths--- through space.  Trails form a central part of the diagrams-lib API,--- so the documentation for this module merits careful study.------ Related modules include:------ * The 'TrailLike' class ("Diagrams.TrailLike") exposes a generic---   API for building a wide range of things out of trails.------ * 'Path's ("Diagrams.Path") are collections of 'Located'---   ("Diagrams.Located") trails.------ * Trails are composed of 'Segment's (see "Diagrams.Segment"),---   though most users should not need to work with segments directly.-----------------------------------------------------------------------------------module Diagrams.Trail-       (-         -- * Type definitions--         -- ** Lines and loops--         Trail'(..)--       , glueLine-       , closeLine-       , cutLoop--         -- ** Generic trails--       , Trail(..)-       , wrapTrail, wrapLine, wrapLoop-       , onTrail, onLine--       , glueTrail, closeTrail, cutTrail--         -- * Constructing trails--       , emptyLine, emptyTrail-       , lineFromVertices, trailFromVertices-       , lineFromOffsets,  trailFromOffsets-       , lineFromSegments, trailFromSegments--         -- * Eliminating trails--       , withTrail', withTrail, withLine-       , isLineEmpty, isTrailEmpty-       , isLine, isLoop-       , trailSegments, lineSegments, loopSegments-       , onLineSegments-       , trailOffsets, trailOffset-       , lineOffsets, lineOffset, loopOffsets-       , trailVertices, lineVertices, loopVertices-       , fixTrail--         -- * Modifying trails--       , reverseTrail, reverseLocTrail-       , reverseLine, reverseLocLine-       , reverseLoop, reverseLocLoop--         -- * Internals-         -- $internals--         -- ** Type tags--       , Line, Loop--         -- ** Segment trees--       , SegTree(..), trailMeasure, numSegs, offset--       ) where--import           Control.Arrow       ((***))-import           Data.AffineSpace-import           Data.FingerTree     (FingerTree, ViewL (..), ViewR (..), (<|),-                                      (|>))-import qualified Data.FingerTree     as FT-import qualified Data.Foldable       as F-import           Data.Monoid.MList-import           Data.Semigroup-import           Data.VectorSpace    hiding (Sum (..))-import qualified Numeric.Interval    as I--import           Diagrams.Core       hiding ((|>))-import           Diagrams.Located-import           Diagrams.Parametric-import           Diagrams.Segment---- $internals------ Most users of diagrams should not need to use anything in this--- section directly, but they are exported on the principle that we--- can't forsee what uses people might have for them.-----------------------------------------------------------------  FingerTree instances---------------------------------------------------------------type instance V (FingerTree m a) = V a--instance ( HasLinearMap (V a), InnerSpace (V a), OrderedField (Scalar (V a))-         , FT.Measured m a, Transformable a-         )-    => Transformable (FingerTree m a) where-  transform = FT.fmap' . transform-----------------------------------------------------------------  Segment trees  ---------------------------------------------------------------------------------------------------------- | A @SegTree@ represents a sequence of closed segments, stored in a---   fingertree so we can easily recover various monoidal measures of---   the segments (number of segments, arc length, envelope...) and---   also easily slice and dice them according to the measures---   (/e.g./, split off the smallest number of segments from the---   beginning which have a combined arc length of at least 5).-newtype SegTree v = SegTree-                  { getSegTree :: FingerTree (SegMeasure v) (Segment Closed v) }-  deriving (Eq, Ord, Show)--type instance V (SegTree v) = v--deriving instance (OrderedField (Scalar v), InnerSpace v)-  => Monoid (SegTree v)-deriving instance (OrderedField (Scalar v), InnerSpace v)-  => FT.Measured (SegMeasure v) (SegTree v)-deriving instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-  => Transformable (SegTree v)--type instance Codomain (SegTree v) = v--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-    => Parametric (SegTree v) where-  atParam t p = offset . fst $ splitAtParam t p--instance Num (Scalar v) => DomainBounds (SegTree v)--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v), Num (Scalar v))-    => EndValues (SegTree v)--instance (InnerSpace v, RealFrac (Scalar v), Floating (Scalar v))-    => Sectionable (SegTree v) where-  splitAtParam (SegTree t) p-    | p < 0     = case FT.viewl t of-                    EmptyL    -> emptySplit-                    seg :< t' ->-                      case seg `splitAtParam` (p * tSegs) of-                        (seg1, seg2) -> ( SegTree $ FT.singleton seg1-                                        , SegTree $ seg2 <| t'-                                        )-    | p >= 1    = case FT.viewr t of-                    EmptyR    -> emptySplit-                    t' :> seg ->-                      case seg `splitAtParam` (1 - (1 - p)*tSegs) of-                        (seg1, seg2) -> ( SegTree $ t' |> seg1-                                        , SegTree $ FT.singleton seg2-                                        )-    | otherwise = case FT.viewl after of-                    EmptyL    -> emptySplit-                    seg :< after' ->-                      case seg `splitAtParam` (snd . properFraction $ p * tSegs) of-                        (seg1, seg2) -> ( SegTree $ before |> seg1-                                        , SegTree $ seg2   <| after'-                                        )-    where-      (before, after) = FT.split ((p * tSegs <) . numSegs) t-      tSegs           = numSegs t-      emptySplit      = (SegTree t, SegTree t)--  -- XXX seems like it should be possible to collapse some of the-  -- above cases into one?--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-    => HasArcLength (SegTree v) where-  arcLengthBounded eps t-    -- Use the cached value if it is accurate enough; otherwise fall-    -- back to recomputing a more accurate value-    | I.width i <= eps = i-    | otherwise        = fun (eps / numSegs t)-    where-      i   = trailMeasure (I.singleton 0)-              (getArcLengthCached :: ArcLength v -> I.Interval (Scalar v))-              t-      fun = trailMeasure (const 0)-              (getArcLengthFun :: ArcLength v -> Scalar v -> I.Interval (Scalar v))-              t--  arcLengthToParam eps st@(SegTree t) l-    | l < 0        = case FT.viewl t of-                       EmptyL   -> 0-                       seg :< _ -> arcLengthToParam eps seg l / tSegs-    | l >= totalAL = case FT.viewr t of-                       EmptyR    -> 0-                       t' :> seg ->-                         let p = arcLengthToParam (eps/2) seg-                                   (l - arcLength (eps/2) (SegTree t'))-                         in  (p - 1)/tSegs + 1-    | otherwise    = case FT.viewl after of-                       EmptyL    -> 0-                       seg :< after' ->-                         let p = arcLengthToParam (eps/2) seg-                                   (l - arcLength (eps/2) (SegTree before))-                         in  (numSegs before + p) / tSegs-    where-      totalAL         = arcLength eps st-      tSegs           = numSegs t-      before, after :: FingerTree (SegMeasure v) (Segment Closed v)-      (before, after) = FT.split ((>= l) . trailMeasure 0 (I.midpoint . (getArcLengthBounded eps :: ArcLength v -> I.Interval (Scalar v)))) t---- | Given a default result (to be used in the case of an empty---   trail), and a function to map a single measure to a result,---   extract the given measure for a trail and use it to compute a---   result.  Put another way, lift a function on a single measure---   (along with a default value) to a function on an entire trail.-trailMeasure :: ( InnerSpace v, OrderedField (Scalar v)-                , SegMeasure v :>: m, FT.Measured (SegMeasure v) t-                )-             => a -> (m -> a) -> t -> a-trailMeasure d f = option d f . get . FT.measure---- | Compute the number of segments of anything measured by---   'SegMeasure' (/e.g./ @SegMeasure@ itself, @Segment@, @SegTree@,---   @Trail@s...)-numSegs :: ( Floating (Scalar v), Num c, Ord (Scalar v), InnerSpace v,-             FT.Measured (SegMeasure v) a-           )-        => a -> c-numSegs = fromIntegral . trailMeasure 0 (getSum . getSegCount)---- | Compute the total offset of anything measured by 'SegMeasure'.-offset :: ( Floating (Scalar v), Ord (Scalar v), InnerSpace v,-            FT.Measured (SegMeasure v) t-          )-       => t -> v-offset = trailMeasure zeroV (getTotalOffset . oeOffset)-----------------------------------------------------------------  Trails  ----------------------------------------------------------------------------------------------------------------- Eventually we should use DataKinds for this, but not until we drop--- support for GHC 7.4.---- | Type tag for trails with distinct endpoints.-data Line---- | Type tag for \"loopy\" trails which return to their starting---   point.-data Loop------------------------------------------------------- The Trail' type---- | Intuitively, a trail is a single, continuous path through space.---   However, a trail has no fixed starting point; it merely specifies---   /how/ to move through space, not /where/.  For example, \"take---   three steps forward, then turn right twenty degrees and take two---   more steps\" is an intuitive analog of a trail; these---   instructions specify a path through space from any given starting---   location.  To be precise, trails are /translation-invariant/;---   applying a translation to a trail has no effect.------   A @'Located' Trail@, on the other hand, is a trail paired with---   some concrete starting location (\"start at the big tree on the---   corner, then take three steps forward, ...\").  See the---   "Diagrams.Located" module for help working with 'Located' values.------   Formally, the semantics of a trail is a continuous (though not---   necessarily differentiable) function from the real interval [0,1]---   to vectors in some vector space.  (In contrast, a 'Located' trail---   is a continuous function from [0,1] to /points/ in some /affine/---   space.)------   There are two types of trails:------   * A \"line\" (think of the \"train\", \"subway\", or \"bus\"---     variety, rather than the \"straight\" variety...) is a trail---     with two distinct endpoints.  Actually, a line can have the---     same start and end points, but it is still /drawn/ as if it had---     distinct endpoints: the two endpoints will have the appropriate---     end caps, and the trail will not be filled.  Lines have a---     @Monoid@ instance where @mappend@ corresponds to concatenation,---     /i.e./ chaining one line after the other.------   * A \"loop\" is required to end in the same place it starts (that---     is, t(0) = t(1)).  Loops are filled and are drawn as one---     continuous loop, with the appropriate join at the---     start/endpoint rather than end caps.  Loops do not have a---     @Monoid@ instance.------   To convert between lines and loops, see 'glueLine',---   'closeLine', and 'cutLoop'.------   To construct trails, see 'emptyTrail', 'trailFromSegments',---   'trailFromVertices', 'trailFromOffsets', and friends.  You can---   also get any type of trail from any function which returns a---   'TrailLike' (/e.g./ functions in "Diagrams.TwoD.Shapes", and many---   others; see "Diagrams.TrailLike").------   To extract information from trails, see 'withLine', 'isLoop',---   'trailSegments', 'trailOffsets', 'trailVertices', and friends.--data Trail' l v where-  Line :: SegTree v                   -> Trail' Line v-  Loop :: SegTree v -> Segment Open v -> Trail' Loop v---- | A generic eliminator for 'Trail'', taking functions specifying---   what to do in the case of a line or a loop.-withTrail' :: (Trail' Line v -> r) -> (Trail' Loop v -> r) -> Trail' l v -> r-withTrail' line loop t@(Line{}) = line t-withTrail' line loop t@(Loop{}) = loop t--deriving instance Show v => Show (Trail' l v)-deriving instance Eq   v => Eq   (Trail' l v)-deriving instance Ord  v => Ord  (Trail' l v)--type instance V (Trail' l v) = v--type instance Codomain (Trail' l v) = v--instance (OrderedField (Scalar v), InnerSpace v) => Semigroup (Trail' Line v) where-  (Line t1) <> (Line t2) = Line (t1 `mappend` t2)---- | The empty trail is constantly the zero vector.  Trails are---   composed via concatenation.  Note that only lines have a monoid---   instance (and not loops).-instance (OrderedField (Scalar v), InnerSpace v) => Monoid (Trail' Line v) where-  mempty  = emptyLine-  mappend = (<>)--instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-    => Transformable (Trail' l v) where-  transform tr (Line t  ) = Line (transform tr t)-  transform tr (Loop t s) = Loop (transform tr t) (transform tr s)---- | The envelope for a trail is based at the trail's start.-instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (Trail' l v) where-  getEnvelope = withTrail' ftEnv (ftEnv . cutLoop)-    where-      ftEnv :: Trail' Line v -> Envelope v-      ftEnv (Line t) = trailMeasure mempty oeEnvelope $ t--instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-    => Renderable (Trail' o v) NullBackend where-  render _ _ = mempty--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-    => Parametric (Trail' l v) where-  atParam t p = withTrail'-                  (\(Line segT) -> segT `atParam` p)-                  (\l -> cutLoop l `atParam` p')-                  t-    where-      pf = snd . properFraction $ p-      p' | p >= 0    = pf-         | otherwise = 1 + pf--instance Num (Scalar v) => DomainBounds (Trail' l v)--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-  => EndValues (Trail' l v)--instance (InnerSpace v, RealFrac (Scalar v), Floating (Scalar v))-    => Sectionable (Trail' Line v) where-  splitAtParam (Line t) p = (Line t1, Line t2)-    where-      (t1, t2) = splitAtParam t p--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-    => HasArcLength (Trail' l v) where-  arcLengthBounded eps =-    withTrail'-      (\(Line t) -> arcLengthBounded eps t)-      (arcLengthBounded eps . cutLoop)--  arcLengthToParam eps tr l =-    withTrail'-      (\(Line t) -> arcLengthToParam eps t l)-      (\lp -> arcLengthToParam eps (cutLoop lp) l)-      tr------------------------------------------------------- The Trail type---- | @Trail@ is a wrapper around @Trail'@, hiding whether the---   underlying @Trail'@ is a line or loop (though which it is can be---   recovered; see /e.g./ 'withTrail').-data Trail v where-  Trail :: Trail' l v -> Trail v--deriving instance Show v => Show (Trail v)--instance Eq v => Eq (Trail v) where-  t1 == t2 =-    withTrail-      (\ln1 -> withTrail (\ln2 -> ln1 == ln2) (const False) t2)-      (\lp1 -> withTrail (const False) (\lp2 -> lp1 == lp2) t2)-      t1--instance Ord v => Ord (Trail v) where-  compare t1 t2 =-    withTrail-      (\ln1 -> withTrail (\ln2 -> compare ln1 ln2) (const LT) t2)-      (\lp1 -> withTrail (const GT) (\lp2 -> compare lp1 lp2) t2)-      t1---- | Two @Trail@s are combined by first ensuring they are both lines---   (using 'cutTrail' on loops) and then concatenating them.  The---   result, in general, is a line.  However, there is a special case---   for the empty line, which acts as the identity (so combining the---   empty line with a loop results in a loop).-instance (OrderedField (Scalar v), InnerSpace v) => Semigroup (Trail v) where-  (Trail (Line (SegTree ft))) <> t2 | FT.null ft = t2-  t1 <> (Trail (Line (SegTree ft))) | FT.null ft = t1-  t1 <> t2 = flip withLine t1 $ \l1 ->-             flip withLine t2 $ \l2 ->-             wrapLine (l1 <> l2)---- | @Trail@s are combined as described in the 'Semigroup' instance;---   the empty line is the identity element, with special cases so---   that combining the empty line with a loop results in the---   unchanged loop (in all other cases loops will be cut).  Note that---   this does, in fact, satisfy the monoid laws, though it is a bit---   strange.  Mostly it is provided for convenience, so one can work---   directly with @Trail@s instead of working with @Trail' Line@s and---   then wrapping.-instance (OrderedField (Scalar v), InnerSpace v) => Monoid (Trail v) where-  mempty  = wrapLine emptyLine-  mappend = (<>)--type instance V (Trail v) = v--type instance Codomain (Trail v) = v--instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v))-    => Transformable (Trail v) where-  transform t = onTrail (transform t) (transform t)--instance (InnerSpace v, OrderedField (Scalar v)) => Enveloped (Trail v) where-  getEnvelope = withTrail getEnvelope getEnvelope--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-    => Parametric (Trail v) where-  atParam t p = withTrail (`atParam` p) (`atParam` p) t--instance Num (Scalar v) => DomainBounds (Trail v)--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-  => EndValues (Trail v)---- | Note that there is no @Sectionable@ instance for @Trail' Loop@,---   because it does not make sense (splitting a loop at a parameter---   results in a single line, not two loops).  However, it's---   convenient to have a @Sectionable@ instance for @Trail@; if the---   @Trail@ contains a loop the loop will first be cut and then---   @splitAtParam@ called on the resulting line.  This is---   semantically a bit silly, so please don't rely on it. (*E.g.* if---   this is really the behavior you want, consider first calling---   'cutLoop' yourself.)-instance (InnerSpace v, RealFrac (Scalar v), Floating (Scalar v))-    => Sectionable (Trail v) where-  splitAtParam t p = withLine ((wrapLine *** wrapLine) . (`splitAtParam` p)) t--instance (InnerSpace v, OrderedField (Scalar v), RealFrac (Scalar v))-    => HasArcLength (Trail v) where-  arcLengthBounded = withLine . arcLengthBounded-  arcLengthToParam eps tr al = withLine (\ln -> arcLengthToParam eps ln al) tr------------------------------------------------------- Constructors and eliminators for Trail---- | A generic eliminator for 'Trail', taking functions specifying---   what to do in the case of a line or a loop.-withTrail :: (Trail' Line v -> r) -> (Trail' Loop v -> r) -> Trail v -> r-withTrail line loop (Trail t) = withTrail' line loop t---- | Modify a @Trail@, specifying two separate transformations for the---   cases of a line or a loop.-onTrail :: (Trail' Line v -> Trail' l1 v) -> (Trail' Loop v -> Trail' l2 v)-        -> (Trail v -> Trail v)-onTrail o c = withTrail (wrapTrail . o) (wrapTrail . c)---- | An eliminator for @Trail@ based on eliminating lines: if the---   trail is a line, the given function is applied; if it is a loop, it---   is first converted to a line with 'cutLoop'.  That is,------ @--- withLine f === 'withTrail' f (f . 'cutLoop')--- @-withLine :: (InnerSpace v, OrderedField (Scalar v))-              => (Trail' Line v -> r) -> Trail v -> r-withLine f = withTrail f (f . cutLoop)---- | Modify a @Trail@ by specifying a transformation on lines.  If the---   trail is a line, the transformation will be applied directly.  If---   it is a loop, it will first be cut using 'cutLoop', the---   transformation applied, and then glued back into a loop with---   'glueLine'.  That is,------   @---   onLine f === onTrail f (glueLine . f . cutLoop)---   @------   Note that there is no corresponding @onLoop@ function, because---   there is no nice way in general to convert a line into a loop,---   operate on it, and then convert back.-onLine :: (InnerSpace v, OrderedField (Scalar v))-            => (Trail' Line v -> Trail' Line v) -> Trail v -> Trail v-onLine f = onTrail f (glueLine . f . cutLoop)---- | Convert a 'Trail'' into a 'Trail', hiding the type-level---   distinction between lines and loops.-wrapTrail :: Trail' l v -> Trail v-wrapTrail = Trail---- | Convert a line into a 'Trail'.  This is the same as 'wrapTrail',---   but with a more specific type, which can occasionally be---   convenient for fixing the type of a polymorphic expression.-wrapLine :: Trail' Line v -> Trail v-wrapLine = wrapTrail---- | Convert a loop into a 'Trail'.  This is the same as 'wrapTrail',---   but with a more specific type, which can occasionally be---   convenient for fixing the type of a polymorphic expression.-wrapLoop :: Trail' Loop v -> Trail v-wrapLoop = wrapTrail-----------------------------------------------------------------  Constructing trails  ---------------------------------------------------------------------------------------------------- | The empty line, which is the identity for concatenation of lines.-emptyLine :: (InnerSpace v, OrderedField (Scalar v)) => Trail' Line v-emptyLine = Line mempty---- | A wrapped variant of 'emptyLine'.-emptyTrail :: (InnerSpace v, OrderedField (Scalar v)) => Trail v-emptyTrail = wrapLine emptyLine---- | Construct a line from a list of closed segments.-lineFromSegments :: (InnerSpace v, OrderedField (Scalar v))-                   => [Segment Closed v] -> Trail' Line v-lineFromSegments = Line . SegTree . FT.fromList---- | @trailFromSegments === 'wrapTrail' . 'lineFromSegments'@, for---   conveniently constructing a @Trail@ instead of a @Trail'@.-trailFromSegments :: (InnerSpace v, OrderedField (Scalar v))-                  => [Segment Closed v] -> Trail v-trailFromSegments = wrapTrail . lineFromSegments---- | Construct a line containing only linear segments from a list of---   vectors, where each vector represents the offset from one vertex---   to the next.  See also 'fromOffsets'.------   <<diagrams/lineFromOffsetsEx.svg#diagram=lineFromOffsetsEx&width=300>>------   > import Diagrams.Coordinates---   > lineFromOffsetsEx = strokeLine $ lineFromOffsets [ 2 & 1, 2 & (-1), 2 & 0.5 ]-lineFromOffsets :: (InnerSpace v, OrderedField (Scalar v))-                  => [v] -> Trail' Line v-lineFromOffsets = lineFromSegments . map straight---- | @trailFromOffsets === 'wrapTrail' . 'lineFromOffsets'@, for---   conveniently constructing a @Trail@ instead of a @Trail' Line@.-trailFromOffsets :: (InnerSpace v, OrderedField (Scalar v))-                 => [v] -> Trail v-trailFromOffsets = wrapTrail . lineFromOffsets---- | Construct a line containing only linear segments from a list of---   vertices.  Note that only the relative offsets between the---   vertices matters; the information about their absolute position---   will be discarded.  That is, for all vectors @v@,------ @--- lineFromVertices === lineFromVertices . 'translate' v--- @------   If you want to retain the position information, you should---   instead use the more general 'fromVertices' function to---   construct, say, a @'Located' ('Trail'' 'Line' v)@ or a @'Located'---   ('Trail' v)@.------   <<diagrams/lineFromVerticesEx.svg#diagram=lineFromVerticesEx&width=300>>------   > import Diagrams.Coordinates---   > lineFromVerticesEx = pad 1.1 . centerXY . strokeLine---   >   $ lineFromVertices [origin, 0 & 1, 1 & 2, 5 & 1]-lineFromVertices :: (InnerSpace v, OrderedField (Scalar v))-                   => [Point v] -> Trail' Line v-lineFromVertices []  = emptyLine-lineFromVertices [_] = emptyLine-lineFromVertices ps  = lineFromSegments . map straight $ zipWith (.-.) (tail ps) ps----- | @trailFromVertices === 'wrapTrail' . 'lineFromVertices'@, for---   conveniently constructing a @Trail@ instead of a @Trail' Line@.-trailFromVertices :: (InnerSpace v, OrderedField (Scalar v))-                  => [Point v] -> Trail v-trailFromVertices = wrapTrail . lineFromVertices-----------------------------------------------------------------  Converting between lines and loops  ------------------------------------------------------------------------------------- | Make a line into a loop by \"gluing\" the endpoint to the---   starting point.  In particular, the offset of the final segment---   is modified so that it ends at the starting point of the entire---   trail.  Typically, you would first construct a line which you---   know happens to end where it starts, and then call 'glueLine' to---   turn it into a loop.------   <<diagrams/glueLineEx.svg#diagram=glueLineEx&width=500>>------   > import Diagrams.Coordinates---   > glueLineEx = pad 1.1 . hcat' with {sep = 1}---   >   $ [almostClosed # strokeLine, almostClosed # glueLine # strokeLoop]---   >---   > almostClosed :: Trail' Line R2---   > almostClosed = fromOffsets [2 & (-1), (-3) & (-0.5), (-2) & 1, 1 & 0.5]------   @glueLine@ is left inverse to 'cutLoop', that is,------   @---   glueLine . cutLoop === id---   @-glueLine :: (InnerSpace v, OrderedField (Scalar v)) => Trail' Line v -> Trail' Loop v-glueLine (Line (SegTree t)) =-  case FT.viewr t of-    FT.EmptyR             -> Loop mempty (Linear OffsetOpen)-    t' :> (Linear _)      -> Loop (SegTree t') (Linear OffsetOpen)-    t' :> (Cubic c1 c2 _) -> Loop (SegTree t') (Cubic c1 c2 OffsetOpen)---- | @glueTrail@ is a variant of 'glueLine' which works on 'Trail's.---   It performs 'glueLine' on lines and is the identity on loops.-glueTrail :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> Trail v-glueTrail = onTrail glueLine id---- | Make a line into a loop by adding a new linear segment from the---   line's end to its start.------   @closeLine@ does not have any particularly nice theoretical---   properties, but can be useful /e.g./ when you want to make a---   closed polygon out of a list of points where the initial point is---   not repeated at the end.  To use 'glueLine', one would first have---   to duplicate the initial vertex, like------ @--- 'glueLine' . 'lineFromVertices' $ ps ++ [head ps]--- @------   Using @closeLine@, however, one can simply------ @--- closeLine . lineFromVertices $ ps--- @------   <<diagrams/closeLineEx.svg#diagram=closeLineEx&width=500>>------   > closeLineEx = pad 1.1 . centerXY . hcat' with {sep = 1}---   >   $ [almostClosed # strokeLine, almostClosed # closeLine # strokeLoop]-closeLine :: Trail' Line v -> Trail' Loop v-closeLine (Line t) = Loop t (Linear OffsetOpen)---- | @closeTrail@ is a variant of 'closeLine' for 'Trail', which---   performs 'closeLine' on lines and is the identity on loops.-closeTrail :: Trail v -> Trail v-closeTrail = onTrail closeLine id---- | Turn a loop into a line by \"cutting\" it at the common start/end---   point, resulting in a line which just happens to start and end at---   the same place.------   @cutLoop@ is right inverse to 'glueLine', that is,------   @---   glueLine . cutLoop === id---   @-cutLoop :: forall v. (InnerSpace v, OrderedField (Scalar v))-         => Trail' Loop v -> Trail' Line v-cutLoop (Loop (SegTree t) c) =-  case (FT.null t, c) of-    (True, Linear OffsetOpen)      -> emptyLine-    (_   , Linear OffsetOpen)      -> Line (SegTree (t |> Linear off))-    (_   , Cubic c1 c2 OffsetOpen) -> Line (SegTree (t |> Cubic c1 c2 off))-  where-    offV :: v-    offV = negateV . trailMeasure zeroV (getTotalOffset . oeOffset) $ t-    off = OffsetClosed offV---- | @cutTrail@ is a variant of 'cutLoop' for 'Trail'; it is the is---   the identity on lines and performs 'cutLoop' on loops.-cutTrail :: (InnerSpace v, OrderedField (Scalar v))-         => Trail v -> Trail v-cutTrail = onTrail id cutLoop-----------------------------------------------------------------  Eliminating trails  ----------------------------------------------------------------------------------------------------- | Test whether a line is empty.-isLineEmpty :: (InnerSpace v, OrderedField (Scalar v)) => Trail' Line v -> Bool-isLineEmpty (Line (SegTree t)) = FT.null t---- | Test whether a trail is empty.  Note that loops are never empty.-isTrailEmpty :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> Bool-isTrailEmpty = withTrail isLineEmpty (const False)---- | Determine whether a trail is a line.-isLine :: Trail v -> Bool-isLine = not . isLoop---- | Determine whether a trail is a loop.-isLoop :: Trail v -> Bool-isLoop = withTrail (const False) (const True)---- | Extract the segments comprising a line.-lineSegments :: Trail' Line v -> [Segment Closed v]-lineSegments (Line (SegTree t)) = F.toList t---- | Modify a line by applying a function to its list of segments.-onLineSegments-  :: (InnerSpace v, OrderedField (Scalar v))-  => ([Segment Closed v] -> [Segment Closed v])-  -> Trail' Line v -> Trail' Line v-onLineSegments f = lineFromSegments . f . lineSegments---- | Extract the segments comprising a loop: a list of closed---   segments, and one final open segment.-loopSegments :: Trail' Loop v -> ([Segment Closed v], Segment Open v)-loopSegments (Loop (SegTree t) c) = (F.toList t, c)---- | Extract the segments of a trail.  If the trail is a loop it will---   first have 'cutLoop' applied.-trailSegments :: (InnerSpace v, OrderedField (Scalar v))-              => Trail v -> [Segment Closed v]-trailSegments = withLine lineSegments---- | Extract the offsets of the segments of a trail.-trailOffsets :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> [v]-trailOffsets = withLine lineOffsets---- | Compute the offset from the start of a trail to the end.  Satisfies------   @---   trailOffset === sumV . trailOffsets---   @------   but is more efficient.------   <<diagrams/trailOffsetEx.svg#diagram=trailOffsetEx&width=300>>------   > trailOffsetEx = (strokeLine almostClosed <> showOffset) # centerXY # pad 1.1---   >   where showOffset = fromOffsets [trailOffset (wrapLine almostClosed)]---   >                    # stroke # lc red # lw 0.05-trailOffset :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> v-trailOffset = withLine lineOffset---- | Extract the offsets of the segments of a line.-lineOffsets :: (InnerSpace v, OrderedField (Scalar v)) => Trail' Line v -> [v]-lineOffsets = map segOffset . lineSegments---- | Extract the offsets of the segments of a loop.-loopOffsets :: (InnerSpace v, OrderedField (Scalar v)) => Trail' Loop v -> [v]-loopOffsets = lineOffsets . cutLoop---- | Compute the offset from the start of a line to the end.  (Note,---   there is no corresponding @loopOffset@ function because by---   definition it would be constantly zero.)-lineOffset :: (InnerSpace v, OrderedField (Scalar v)) => Trail' Line v -> v-lineOffset (Line t) = trailMeasure zeroV (getTotalOffset . oeOffset) t---- | Extract the vertices of a concretely located trail.  Note that---   for loops, the starting vertex will /not/ be repeated at the end.---   If you want this behavior, you can use 'cutTrail' to make the---   loop into a line first, which happens to repeat the same vertex---   at the start and end, /e.g./ with @trailVertices . mapLoc---   cutTrail@.------   Note that it does not make sense to ask for the vertices of a---   'Trail' by itself; if you want the vertices of a trail---   with the first vertex at, say, the origin, you can use---   @trailVertices . (`at` origin)@.-trailVertices :: (InnerSpace v, OrderedField (Scalar v))-              => Located (Trail v) -> [Point v]-trailVertices (viewLoc -> (p,t))-  = withTrail (lineVertices . (`at` p)) (loopVertices . (`at` p)) t---- | Extract the vertices of a concretely located line.  See---   'trailVertices' for more information.-lineVertices :: (InnerSpace v, OrderedField (Scalar v))-             => Located (Trail' Line v) -> [Point v]-lineVertices (viewLoc -> (p,t))-  = segmentVertices p . lineSegments $ t---- | Extract the vertices of a concretely located loop.  Note that the---   initial vertex is not repeated at the end.  See 'trailVertices' for---   more information.-loopVertices :: (InnerSpace v, OrderedField (Scalar v))-             => Located (Trail' Loop v) -> [Point v]-loopVertices (viewLoc -> (p,t))-  = segmentVertices p . fst . loopSegments $ t--segmentVertices :: AdditiveGroup v => Point v -> [Segment Closed v] -> [Point v]-segmentVertices p = scanl (.+^) p . map segOffset---- | Convert a concretely located trail into a list of fixed segments.-fixTrail :: (InnerSpace v, OrderedField (Scalar v))-         => Located (Trail v) -> [FixedSegment v]-fixTrail t = zipWith ((mkFixedSeg .) . at)-               (trailSegments (unLoc t)) (trailVertices t)-----------------------------------------------------------------  Modifying trails  ------------------------------------------------------------------------------------------------------- | Reverse a trail.  Semantically, if a trail given by a function t---   from [0,1] to vectors, then the reverse of t is given by t'(s) =---   t(1-s).  @reverseTrail@ is an involution, that is,------   @---   reverseTrail . reverseTrail === id---   @-reverseTrail :: (InnerSpace v, OrderedField (Scalar v)) => Trail v -> Trail v-reverseTrail = onTrail reverseLine reverseLoop---- | Reverse a concretely located trail.  The endpoint of the original---   trail becomes the starting point of the reversed trail, so the---   original and reversed trails comprise exactly the same set of---   points.  @reverseLocTrail@ is an involution, /i.e./------   @---   reverseLocTrail . reverseLocTrail === id---   @-reverseLocTrail :: (InnerSpace v, OrderedField (Scalar v))-                => Located (Trail v) -> Located (Trail v)-reverseLocTrail (viewLoc -> (p, t)) = reverseTrail t `at` (p .+^ trailOffset t)---- | Reverse a line.  See 'reverseTrail'.-reverseLine :: (InnerSpace v, OrderedField (Scalar v))-            => Trail' Line v -> Trail' Line v-reverseLine = onLineSegments (reverse . map reverseSegment)---- | Reverse a concretely located line.  See 'reverseLocTrail'.-reverseLocLine :: (InnerSpace v, OrderedField (Scalar v))-               => Located (Trail' Line v) -> Located (Trail' Line v)-reverseLocLine (viewLoc -> (p,l)) = reverseLine l `at` (p .+^ lineOffset l)---- | Reverse a loop.  See 'reverseTrail'.-reverseLoop :: (InnerSpace v, OrderedField (Scalar v))-            => Trail' Loop v -> Trail' Loop v-reverseLoop = glueLine . reverseLine . cutLoop---- | Reverse a concretely located loop.  See 'reverseLocTrail'.  Note---   that this is guaranteed to preserve the location.-reverseLocLoop :: (InnerSpace v, OrderedField (Scalar v))-               => Located (Trail' Loop v) -> Located (Trail' Loop v)-reverseLocLoop = mapLoc reverseLoop+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE CPP                        #-}+{-# LANGUAGE EmptyDataDecls             #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE UndecidableInstances       #-}+{-# LANGUAGE ViewPatterns               #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+-- We have an orphan Transformable FingerTree instance here.++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Trail+-- Copyright   :  (c) 2013-2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- This module defines /trails/, translationally invariant paths+-- through space.  Trails form a central part of the diagrams-lib API,+-- so the documentation for this module merits careful study.+--+-- Related modules include:+--+-- * The 'TrailLike' class ("Diagrams.TrailLike") exposes a generic+--   API for building a wide range of things out of trails.+--+-- * 'Path's ("Diagrams.Path") are collections of 'Located'+--   ("Diagrams.Located") trails.+--+-- * Trails are composed of 'Segment's (see "Diagrams.Segment"),+--   though most users should not need to work with segments directly.+--+-----------------------------------------------------------------------------++module Diagrams.Trail+       (+         -- * Type definitions++         -- ** Lines and loops++         Trail'(..)++       , glueLine+       , closeLine+       , cutLoop++         -- ** Generic trails++       , Trail(..)+       , _Line, _Loop+       , _LocLine, _LocLoop+       , wrapTrail, wrapLine, wrapLoop+       , onTrail, onLine++       , glueTrail, closeTrail, cutTrail++         -- * Constructing trails++       , emptyLine, emptyTrail+       , lineFromVertices, trailFromVertices+       , lineFromOffsets,  trailFromOffsets+       , lineFromSegments, trailFromSegments+       , loopFromSegments++         -- * Eliminating trails++       , withTrail', withTrail, withLine+       , isLineEmpty, isTrailEmpty+       , isLine, isLoop+       , trailSegments, lineSegments, loopSegments+       , onLineSegments+       , trailOffsets, trailOffset+       , lineOffsets, lineOffset, loopOffsets+       , trailPoints, linePoints, loopPoints+       , trailVertices', lineVertices', loopVertices'+       , trailVertices, lineVertices, loopVertices+       , trailLocSegments, fixTrail, unfixTrail++         -- * Modifying trails++       , reverseTrail, reverseLocTrail+       , reverseLine, reverseLocLine+       , reverseLoop, reverseLocLoop++         -- * Internals+         -- $internals++         -- ** Type tags++       , Line, Loop++         -- ** Segment trees++       , SegTree(..), trailMeasure, numSegs, offset++         -- ** Extracting segments++       , GetSegment(..), getSegment, GetSegmentCodomain(..)++       ) where++import           Control.Arrow            ((***))+import           Control.Lens             hiding (at, transform, (<|), (|>))+import           Data.FingerTree          (FingerTree, ViewL (..), ViewR (..),+                                           viewl, (<|), (|>))+import qualified Data.FingerTree          as FT+import           Data.Fixed+import qualified Data.Foldable            as F+import           Data.Monoid.MList+import           Data.Semigroup+import qualified Numeric.Interval.Kaucher as I++import           Diagrams.Core+import           Diagrams.Located+import           Diagrams.Parametric+import           Diagrams.Segment+import           Diagrams.Tangent++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector++import           Data.Serialize            (Serialize)+import qualified Data.Serialize            as Serialize++-- $internals+--+-- Most users of diagrams should not need to use anything in this+-- section directly, but they are exported on the principle that we+-- can't forsee what uses people might have for them.++------------------------------------------------------------+--  FingerTree instances+------------------------------------------------------------++type instance V (FingerTree m a) = V a+type instance N (FingerTree m a) = N a++instance (FT.Measured m a, Transformable a)+    => Transformable (FingerTree m a) where+  transform = FT.fmap' . transform++instance (FT.Measured m a, FT.Measured n b)+  => Cons (FingerTree m a) (FingerTree n b) a b where+  _Cons = prism (uncurry (FT.<|)) $ \aas -> case FT.viewl aas of+    a FT.:< as -> Right (a, as)+    EmptyL     -> Left mempty+  {-# INLINE _Cons #-}++instance (FT.Measured m a, FT.Measured n b)+  => Snoc (FingerTree m a) (FingerTree n b) a b where+  _Snoc = prism (uncurry (FT.|>)) $ \aas -> case FT.viewr aas of+    as FT.:> a -> Right (as, a)+    EmptyR  -> Left mempty+  {-# INLINE _Snoc #-}++------------------------------------------------------------+--  Segment trees  -----------------------------------------+------------------------------------------------------------++-- | A @SegTree@ represents a sequence of closed segments, stored in a+--   fingertree so we can easily recover various monoidal measures of+--   the segments (number of segments, arc length, envelope...) and+--   also easily slice and dice them according to the measures+--   (/e.g./, split off the smallest number of segments from the+--   beginning which have a combined arc length of at least 5).++newtype SegTree v n = SegTree (FingerTree (SegMeasure v n) (Segment Closed v n))+  deriving (Eq, Ord, Show, Monoid, Transformable, FT.Measured (SegMeasure v n))++-- Only derive the Semigroup instance for versions of base that+-- include Semigroup.  This is because the fingertree package has+-- similar CPP to only export a Semigroup instance for those versions+-- of base, so for GHC 7.10 and earlier we get a 'no instance found'+-- error when trying to derive the Semigroup instance for SegTree.  It+-- would also be possible to depend on the 'semigroups' package in+-- order to get the Semigroup class regardless of base version, but+-- presumably fingertree didn't want to add a dependency.+#if MIN_VERSION_base(4,9,0)+deriving instance (Ord n, Floating n, Metric v) => Semigroup (SegTree v n)+#endif++instance Wrapped (SegTree v n) where+  type Unwrapped (SegTree v n) = FingerTree (SegMeasure v n) (Segment Closed v n)+  _Wrapped' = iso (\(SegTree x) -> x) SegTree+  {-# INLINE _Wrapped' #-}++instance (Metric v, OrderedField n, Metric u, OrderedField n')+  => Cons (SegTree v n) (SegTree u n') (Segment Closed v n) (Segment Closed u n') where+  _Cons = _Wrapped . _Cons . bimapping id _Unwrapped+  {-# INLINE _Cons #-}++instance (Metric v, OrderedField n, Metric u, OrderedField n')+  => Snoc (SegTree v n) (SegTree u n') (Segment Closed v n) (Segment Closed u n') where+  _Snoc = _Wrapped . _Snoc . bimapping _Unwrapped id+  {-# INLINE _Snoc #-}++instance Rewrapped (SegTree v n) (SegTree v' n')++type instance V (SegTree v n) = v+type instance N (SegTree v n) = n++type instance Codomain (SegTree v n) = v++instance (Metric v, OrderedField n, Real n)+    => Parametric (SegTree v n) where+  atParam t p = offset . fst $ splitAtParam t p++instance Num n => DomainBounds (SegTree v n)++instance (Metric v, OrderedField n, Real n)+    => EndValues (SegTree v n)++splitAtParam' :: (Metric v, OrderedField n, Real n)+              => SegTree v n -> n -> ((SegTree v n, SegTree v n), n -> n)+splitAtParam' (SegTree t) p+  | tSegs == 0 = ((mempty       , mempty       ), id)+  | otherwise  = ((SegTree treeL, SegTree treeR), rescale)+  where+    tSegs  = numSegs t+    splitParam q | q <  0    = (0        , q           * tSegs)+                 | q >= 1    = (tSegs - 1, 1 + (q - 1) * tSegs)+                 | otherwise = propFrac $  q           * tSegs+      where propFrac x = let m = mod1 x in (x - m, m)+    (pSegs, pParam) = splitParam p+    (before, viewl -> seg FT.:< after) = FT.split ((pSegs <) . numSegs) t+    (segL, segR) = seg `splitAtParam` pParam+    (treeL, treeR) | pParam == 0 = (before        , seg  <| after)+                   | pParam == 1 = (before |> seg ,         after)+                   | otherwise   = (before |> segL, segR <| after)+    -- section uses rescale to find the new value of p1 after the split at p2+    rescale u | pSegs' == uSegs = (uSegs + uParam / pParam' {-'1-}) / (pSegs' + 1) {-'2-}+              | otherwise       = u * tSegs / (pSegs' + 1) {-'3-}+      where+        -- param 0 on a segment is param 1 on the previous segment+        (pSegs', pParam') | pParam == 0 = (pSegs-1, 1)+                          | otherwise   = (pSegs  , pParam)+        (uSegs , uParam ) = splitParam u+        -- '1 (pParam ≠ 0 → pParam' = pParam) ∧ (pParam = 0 → pParam' = 1) → pParam' ≠ 0+        -- '2 uSegs ≥ 0 ∧ pSegs' = uSegs → pSegs' ≥ 0 → pSegs' + 1 > 0+        -- '3 pSegs' + 1 = 0 → pSegs' = -1 → pSegs = 0 ∧ pParam = 0 → p = 0+        --    → rescale is not called++instance (Metric v, OrderedField n, Real n) => Sectionable (SegTree v n) where+  splitAtParam tree p = fst $ splitAtParam' tree p++  reverseDomain (SegTree t) = SegTree $ FT.reverse t'+    where t' = FT.fmap' reverseSegment t++  section x p1 p2 | p2 == 0   = reverseDomain . fst $ splitAtParam x p1+                  | p1 <= p2  = let ((a, _), rescale) = splitAtParam' x p2+                                in  snd $ splitAtParam a (rescale p1)+                  | otherwise = reverseDomain $ section x p2 p1++instance (Metric v, OrderedField n, Real n)+    => HasArcLength (SegTree v n) where+  arcLengthBounded eps t+    -- Use the cached value if it is accurate enough; otherwise fall+    -- back to recomputing a more accurate value+    | I.width i <= eps = i+    | otherwise        = fun (eps / numSegs t)+    where+      i   = trailMeasure (I.singleton 0)+              getArcLengthCached+              t+      fun = trailMeasure (const 0)+              getArcLengthFun+              t++  arcLengthToParam eps st@(SegTree t) l+    | l < 0        = case FT.viewl t of+                       EmptyL   -> 0+                       seg FT.:< _ -> arcLengthToParam eps seg l / tSegs+    | l >= totalAL = case FT.viewr t of+                       EmptyR    -> 0+                       t' FT.:> seg ->+                         let p = arcLengthToParam (eps/2) seg+                                   (l - arcLength (eps/2) (SegTree t'))+                         in  (p - 1)/tSegs + 1+    | otherwise    = case FT.viewl after of+                       EmptyL    -> 0+                       seg FT.:< _  ->+                         let p = arcLengthToParam (eps/2) seg+                                   (l - arcLength (eps/2) (SegTree before))+                         in  (numSegs before + p) / tSegs+    where+      totalAL         = arcLength eps st+      tSegs           = numSegs t+      before, after :: FingerTree (SegMeasure v n) (Segment Closed v n)+      (before, after) =+        FT.split ((>= l)+                 . trailMeasure+                 0+                 (I.midpoint . getArcLengthBounded eps))+                 t++-- | Given a default result (to be used in the case of an empty+--   trail), and a function to map a single measure to a result,+--   extract the given measure for a trail and use it to compute a+--   result.  Put another way, lift a function on a single measure+--   (along with a default value) to a function on an entire trail.+trailMeasure :: ( SegMeasure v n :>: m, FT.Measured (SegMeasure v n) t )+             => a -> (m -> a) -> t -> a+trailMeasure d f = maybe d f . get . FT.measure++-- | Compute the number of segments of anything measured by+--   'SegMeasure' (/e.g./ @SegMeasure@ itself, @Segment@, @SegTree@,+--   @Trail@s...)+numSegs :: (Num c, FT.Measured (SegMeasure v n) a)+        => a -> c+numSegs = fromIntegral . trailMeasure 0 (getSum . op SegCount)++-- | Compute the total offset of anything measured by 'SegMeasure'.+offset :: ( OrderedField n, Metric v,+            FT.Measured (SegMeasure v n) t+          )+       => t -> v n+offset = trailMeasure zero (op TotalOffset . view oeOffset)++------------------------------------------------------------+--  Trails  ------------------------------------------------+------------------------------------------------------------++-- Eventually we should use DataKinds for this, but not until we drop+-- support for GHC 7.4.++-- | Type tag for trails with distinct endpoints.+data Line++-- | Type tag for \"loopy\" trails which return to their starting+--   point.+data Loop++--------------------------------------------------+-- The Trail' type++-- | Intuitively, a trail is a single, continuous path through space.+--   However, a trail has no fixed starting point; it merely specifies+--   /how/ to move through space, not /where/.  For example, \"take+--   three steps forward, then turn right twenty degrees and take two+--   more steps\" is an intuitive analog of a trail; these+--   instructions specify a path through space from any given starting+--   location.  To be precise, trails are /translation-invariant/;+--   applying a translation to a trail has no effect.+--+--   A @'Located' Trail@, on the other hand, is a trail paired with+--   some concrete starting location (\"start at the big tree on the+--   corner, then take three steps forward, ...\").  See the+--   "Diagrams.Located" module for help working with 'Located' values.+--+--   Formally, the semantics of a trail is a continuous (though not+--   necessarily differentiable) function from the real interval [0,1]+--   to vectors in some vector space.  (In contrast, a 'Located' trail+--   is a continuous function from [0,1] to /points/ in some /affine/+--   space.)+--+--   There are two types of trails:+--+--   * A \"line\" (think of the \"train\", \"subway\", or \"bus\"+--     variety, rather than the \"straight\" variety...) is a trail+--     with two distinct endpoints.  Actually, a line can have the+--     same start and end points, but it is still /drawn/ as if it had+--     distinct endpoints: the two endpoints will have the appropriate+--     end caps, and the trail will not be filled.  Lines have a+--     @Monoid@ instance where @mappend@ corresponds to concatenation,+--     /i.e./ chaining one line after the other.+--+--   * A \"loop\" is required to end in the same place it starts (that+--     is, t(0) = t(1)).  Loops are filled and are drawn as one+--     continuous loop, with the appropriate join at the+--     start/endpoint rather than end caps.  Loops do not have a+--     @Monoid@ instance.+--+--   To convert between lines and loops, see 'glueLine',+--   'closeLine', and 'cutLoop'.+--+--   To construct trails, see 'emptyTrail', 'trailFromSegments',+--   'trailFromVertices', 'trailFromOffsets', and friends.  You can+--   also get any type of trail from any function which returns a+--   'TrailLike' (/e.g./ functions in "Diagrams.TwoD.Shapes", and many+--   others; see "Diagrams.TrailLike").+--+--   To extract information from trails, see 'withLine', 'isLoop',+--   'trailSegments', 'trailOffsets', 'trailVertices', and friends.++data Trail' l v n where+  Line :: SegTree v n                     -> Trail' Line v n+  Loop :: SegTree v n -> Segment Open v n -> Trail' Loop v n++-- | A generic eliminator for 'Trail'', taking functions specifying+--   what to do in the case of a line or a loop.+withTrail' :: (Trail' Line v n -> r) -> (Trail' Loop v n -> r) -> Trail' l v n -> r+withTrail' line _    t@(Line{}) = line t+withTrail' _    loop t@(Loop{}) = loop t++deriving instance Eq  (v n) => Eq   (Trail' l v n)+deriving instance Ord (v n) => Ord  (Trail' l v n)++instance Show (v n) => Show (Trail' l v n) where+  showsPrec d (Line (SegTree ft)) = showParen (d > 10) $+    showString "lineFromSegments " . showList (F.toList ft)++  showsPrec d (Loop (SegTree ft) o) = showParen (d > 10) $+    showString "loopFromSegments " . showList (F.toList ft) .+    showChar ' ' . showsPrec 11 o++type instance V (Trail' l v n) = v+type instance N (Trail' l v n) = n++type instance Codomain (Trail' l v n) = v++instance (OrderedField n, Metric v) => Semigroup (Trail' Line v n) where+  (Line t1) <> (Line t2) = Line (t1 `mappend` t2)++-- | The empty trail is constantly the zero vector.  Trails are+--   composed via concatenation.  Note that only lines have a monoid+--   instance (and not loops).+instance (Metric v, OrderedField n) => Monoid (Trail' Line v n) where+  mempty  = emptyLine+  mappend = (<>)++instance (Metric v, OrderedField n) => AsEmpty (Trail' Line v n) where+  _Empty = nearly emptyLine isLineEmpty++instance (HasLinearMap v, Metric v, OrderedField n)+    => Transformable (Trail' l v n) where+  transform tr (Line t  ) = Line (transform tr t)+  transform tr (Loop t s) = Loop (transform tr t) (transform tr s)++-- | The envelope for a trail is based at the trail's start.+instance (Metric v, OrderedField n) => Enveloped (Trail' l v n) where+  getEnvelope = withTrail' ftEnv (ftEnv . cutLoop)+    where+      ftEnv :: Trail' Line v n -> Envelope v n+      ftEnv (Line t) = trailMeasure mempty (view oeEnvelope) t++instance (HasLinearMap v, Metric v, OrderedField n)+    => Renderable (Trail' o v n) NullBackend where+  render _ _ = mempty++instance (Metric v, OrderedField n, Real n)+    => Parametric (Trail' l v n) where+  atParam t p = withTrail'+                  (\(Line segT) -> segT `atParam` p)+                  (\l -> cutLoop l `atParam` mod1 p)+                  t++instance (Parametric (GetSegment (Trail' c v n)), Additive v, Num n)+    => Parametric (Tangent (Trail' c v n)) where+  Tangent tr `atParam` p =+    case GetSegment tr `atParam` p of+      GetSegmentCodomain Nothing                  -> zero+      GetSegmentCodomain (Just (_, seg, reparam)) -> Tangent seg `atParam` (p ^. cloneIso reparam)++instance ( Parametric (GetSegment (Trail' c v n))+         , EndValues (GetSegment (Trail' c v n))+         , Additive v+         , Num n+         )+    => EndValues (Tangent (Trail' c v n)) where+  atStart (Tangent tr) =+    case atStart (GetSegment tr) of+      GetSegmentCodomain Nothing            -> zero+      GetSegmentCodomain (Just (_, seg, _)) -> atStart (Tangent seg)+  atEnd (Tangent tr) =+    case atEnd (GetSegment tr) of+      GetSegmentCodomain Nothing            -> zero+      GetSegmentCodomain (Just (_, seg, _)) -> atEnd (Tangent seg)++instance (Metric v , OrderedField n, Real n)+    => Parametric (Tangent (Trail v n)) where+  Tangent tr `atParam` p+    = withTrail+        ((`atParam` p) . Tangent)+        ((`atParam` p) . Tangent)+        tr++instance (Metric v, OrderedField n, Real n)+    => EndValues (Tangent (Trail v n)) where+  atStart (Tangent tr) = withTrail (atStart . Tangent) (atStart . Tangent) tr+  atEnd   (Tangent tr) = withTrail (atEnd   . Tangent) (atEnd   . Tangent) tr++-- | Compute the remainder mod 1.  Convenient for constructing loop+--   parameterizations that wrap around.+mod1 :: Real a => a -> a+mod1 = (`mod'` 1)++instance Num n => DomainBounds (Trail' l v n)++instance (Metric v, OrderedField n, Real n)+  => EndValues (Trail' l v n)++instance (Metric v, OrderedField n, Real n)+    => Sectionable (Trail' Line v n) where+  splitAtParam (Line t) p = (Line t1, Line t2)+    where+      (t1, t2) = splitAtParam t p++  section (Line t) p1 p2 = Line (section t p1 p2)++  reverseDomain = reverseLine++instance (Metric v, OrderedField n, Real n)+    => HasArcLength (Trail' l v n) where+  arcLengthBounded eps =+    withTrail'+      (\(Line t) -> arcLengthBounded eps t)+      (arcLengthBounded eps . cutLoop)++  arcLengthToParam eps tr l =+    withTrail'+      (\(Line t) -> arcLengthToParam eps t l)+      (\lp -> arcLengthToParam eps (cutLoop lp) l)+      tr++instance Rewrapped (Trail' Line v n) (Trail' Line v' n')+instance Wrapped (Trail' Line v n) where+  type Unwrapped (Trail' Line v n) = SegTree v n+  _Wrapped' = iso (\(Line x) -> x) Line+  {-# INLINE _Wrapped' #-}++instance (Metric v, OrderedField n, Metric u, OrderedField n')+  => Cons (Trail' Line v n) (Trail' Line u n') (Segment Closed v n) (Segment Closed u n') where+  _Cons = _Wrapped . _Cons . bimapping id _Unwrapped+  {-# INLINE _Cons #-}++instance (Metric v, OrderedField n, Metric u, OrderedField n')+  => Snoc (Trail' Line v n) (Trail' Line u n') (Segment Closed v n) (Segment Closed u n') where+  _Snoc = _Wrapped . _Snoc . bimapping _Unwrapped id+  {-# INLINE _Snoc #-}++-- | Compose two Located trails by adding a segment joining the endpoint+--   of the first to the starting point of the second.  Note, if you have+--   two located trails such that the end of the first coincides with the+--   start of the second, this will still add a trivial zero-length segment+--   between them; in that case you are probably better off with something+--   like @mapLoc (<> unLoc t2) t1@.+instance (Real n, Floating n, Metric v)+  => Semigroup (Located (Trail' Line v n)) where+    a@(Loc aLoc aLine) <> b@(Loc _ bLine) =+      Loc aLoc (aLine <> lineFromOffsets [atStart b .-. atEnd a] <> bLine)++--------------------------------------------------+-- Extracting segments++-- | A newtype wrapper around trails which exists solely for its+--   'Parametric', 'DomainBounds' and 'EndValues' instances.  The idea+--   is that if @tr@ is a trail, you can write, /e.g./+--+--   @+--   getSegment tr `atParam` 0.6+--   @+--+--   or+--+--   @+--   atStart (getSegment tr)+--   @+--+--   to get the segment at parameter 0.6 or the first segment in the+--   trail, respectively.+--+--   The codomain for 'GetSegment', /i.e./ the result you get from+--   calling 'atParam', 'atStart', or 'atEnd', is+--   'GetSegmentCodomain', which is a newtype wrapper around @Maybe+--   (v, Segment Closed v, AnIso' n n)@.  @Nothing@ results if the+--   trail is empty; otherwise, you get:+--+--   * the offset from the start of the trail to the beginning of the+--     segment,+--+--   * the segment itself, and+--+--   * a reparameterization isomorphism: in the forward direction, it+--     translates from parameters on the whole trail to a parameters+--     on the segment.  Note that for technical reasons you have to+--     call 'cloneIso' on the @AnIso'@ value to get a real isomorphism+--     you can use.+newtype GetSegment t = GetSegment t++newtype GetSegmentCodomain v n =+  GetSegmentCodomain+    (Maybe ( v n                -- offset from trail start to segment start+           , Segment Closed v n -- the segment+           , AnIso' n n         -- reparameterization, trail <-> segment+           ))++-- | Create a 'GetSegment' wrapper around a trail, after which you can+--   call 'atParam', 'atStart', or 'atEnd' to extract a segment.+getSegment :: t -> GetSegment t+getSegment = GetSegment++type instance V (GetSegment t) = V t+type instance N (GetSegment t) = N t++type instance Codomain (GetSegment t) = GetSegmentCodomain (V t)++-- | Parameters less than 0 yield the first segment; parameters+--   greater than 1 yield the last.  A parameter exactly at the+--   junction of two segments yields the second segment (/i.e./ the+--   one with higher parameter values).+instance (Metric v, OrderedField n) => Parametric (GetSegment (Trail' Line v n)) where+  atParam (GetSegment (Line (SegTree ft))) p+    | p <= 0 = case FT.viewl ft of+        EmptyL   -> GetSegmentCodomain Nothing+        seg FT.:< _ -> GetSegmentCodomain $ Just (zero, seg, reparam 0)++    | p >= 1 = case FT.viewr ft of+        EmptyR     -> GetSegmentCodomain Nothing+        ft' FT.:> seg -> GetSegmentCodomain $ Just (offset ft', seg, reparam (n-1))++    | otherwise+    = let (before, after) = FT.split ((p*n <) . numSegs) ft+      in  case FT.viewl after of+            EmptyL   -> GetSegmentCodomain Nothing+            seg FT.:< _ -> GetSegmentCodomain $ Just (offset before, seg, reparam (numSegs before))+    where+      n = numSegs ft+      reparam k = iso (subtract k . (*n))+                      ((/n) . (+ k))++-- | The parameterization for loops wraps around, /i.e./ parameters+--   are first reduced \"mod 1\".+instance (Metric v, OrderedField n, Real n) => Parametric (GetSegment (Trail' Loop v n)) where+  atParam (GetSegment l) p = atParam (GetSegment (cutLoop l)) (mod1 p)++instance (Metric v, OrderedField n, Real n)+    => Parametric (GetSegment (Trail v n)) where+  atParam (GetSegment t) p+    = withTrail+      ((`atParam` p) . GetSegment)+      ((`atParam` p) . GetSegment)+      t++instance DomainBounds t => DomainBounds (GetSegment t) where+  domainLower (GetSegment t) = domainLower t+  domainUpper (GetSegment t) = domainUpper t++instance (Metric v, OrderedField n)+    => EndValues (GetSegment (Trail' Line v n)) where+  atStart (GetSegment (Line (SegTree ft)))+    = case FT.viewl ft of+        EmptyL   -> GetSegmentCodomain Nothing+        seg FT.:< _ ->+          let n = numSegs ft+          in  GetSegmentCodomain $ Just (zero, seg, iso (*n) (/n))++  atEnd (GetSegment (Line (SegTree ft)))+    = case FT.viewr ft of+        EmptyR     -> GetSegmentCodomain Nothing+        ft' FT.:> seg ->+          let n = numSegs ft+          in  GetSegmentCodomain $+                Just (offset ft', seg, iso (subtract (n-1) . (*n))+                                         ((/n) . (+ (n-1)))+                     )++instance (Metric v, OrderedField n, Real n)+    => EndValues (GetSegment (Trail' Loop v n)) where+  atStart (GetSegment l) = atStart (GetSegment (cutLoop l))+  atEnd   (GetSegment l) = atEnd   (GetSegment (cutLoop l))++instance (Metric v, OrderedField n, Real n)+    => EndValues (GetSegment (Trail v n)) where+  atStart (GetSegment t)+    = withTrail+      (atStart . GetSegment)+      (atStart . GetSegment)+      t+  atEnd (GetSegment t)+    = withTrail+      (atEnd . GetSegment)+      (atEnd . GetSegment)+      t++--------------------------------------------------+-- The Trail type++-- | @Trail@ is a wrapper around @Trail'@, hiding whether the+--   underlying @Trail'@ is a line or loop (though which it is can be+--   recovered; see /e.g./ 'withTrail').+data Trail v n where+  Trail :: Trail' l v n -> Trail v n++deriving instance Show (v n) => Show (Trail v n)++instance Eq (v n) => Eq (Trail v n) where+  t1 == t2 =+    withTrail+      (\ln1 -> withTrail (\ln2 -> ln1 == ln2) (const False) t2)+      (\lp1 -> withTrail (const False) (\lp2 -> lp1 == lp2) t2)+      t1++instance Ord (v n) => Ord (Trail v n) where+  compare t1 t2 =+    withTrail+      (\ln1 -> withTrail (compare ln1) (const LT) t2)+      (\lp1 -> withTrail (const GT) (compare lp1) t2)+      t1++-- | Two @Trail@s are combined by first ensuring they are both lines+--   (using 'cutTrail' on loops) and then concatenating them.  The+--   result, in general, is a line.  However, there is a special case+--   for the empty line, which acts as the identity (so combining the+--   empty line with a loop results in a loop).+instance (OrderedField n, Metric v) => Semigroup (Trail v n) where+  (Trail (Line (SegTree ft))) <> t2 | FT.null ft = t2+  t1 <> (Trail (Line (SegTree ft))) | FT.null ft = t1+  t1 <> t2 = flip withLine t1 $ \l1 ->+             flip withLine t2 $ \l2 ->+             wrapLine (l1 <> l2)++-- | @Trail@s are combined as described in the 'Semigroup' instance;+--   the empty line is the identity element, with special cases so+--   that combining the empty line with a loop results in the+--   unchanged loop (in all other cases loops will be cut).  Note that+--   this does, in fact, satisfy the monoid laws, though it is a bit+--   strange.  Mostly it is provided for convenience, so one can work+--   directly with @Trail@s instead of working with @Trail' Line@s and+--   then wrapping.+instance (Metric v, OrderedField n) => Monoid (Trail v n) where+  mempty  = wrapLine emptyLine+  mappend = (<>)++instance (Metric v, OrderedField n) => AsEmpty (Trail v n) where+  _Empty = nearly emptyTrail isTrailEmpty++type instance V (Trail v n) = v+type instance N (Trail v n) = n++type instance Codomain (Trail v n) = v++instance (HasLinearMap v, Metric v, OrderedField n)+    => Transformable (Trail v n) where+  transform t = onTrail (transform t) (transform t)++instance (Metric v, OrderedField n) => Enveloped (Trail v n) where+  getEnvelope = withTrail getEnvelope getEnvelope++instance (Metric v, OrderedField n, Real n)+    => Parametric (Trail v n) where+  atParam t p = withTrail (`atParam` p) (`atParam` p) t++instance Num n => DomainBounds (Trail v n)++instance (Metric v, OrderedField n, Real n) => EndValues (Trail v n)++-- | Note that there is no @Sectionable@ instance for @Trail' Loop@,+--   because it does not make sense (splitting a loop at a parameter+--   results in a single line, not two loops).  However, it's+--   convenient to have a @Sectionable@ instance for @Trail@; if the+--   @Trail@ contains a loop the loop will first be cut and then+--   @splitAtParam@ called on the resulting line.  This is+--   semantically a bit silly, so please don't rely on it. (*E.g.* if+--   this is really the behavior you want, consider first calling+--   'cutLoop' yourself.)+instance (Metric v, OrderedField n, Real n) => Sectionable (Trail v n) where+  splitAtParam t p = withLine ((wrapLine *** wrapLine) . (`splitAtParam` p)) t++  section t p1 p2 = withLine (wrapLine . (\l -> section l p1 p2)) t++  reverseDomain = reverseTrail++instance (Metric v, OrderedField n, Real n)+    => HasArcLength (Trail v n) where+  arcLengthBounded = withLine . arcLengthBounded+  arcLengthToParam eps tr al = withLine (\ln -> arcLengthToParam eps ln al) tr++-- lens instrances -----------------------------------------------------++-- | Prism onto a 'Line'.+_Line :: Prism' (Trail v n) (Trail' Line v n)+_Line = _Wrapped' . _Left++-- | Prism onto a 'Loop'.+_Loop :: Prism' (Trail v n) (Trail' Loop v n)+_Loop = _Wrapped' . _Right++-- | Prism onto a 'Located' 'Line'.+_LocLine :: Prism' (Located (Trail v n)) (Located (Trail' Line v n))+_LocLine = prism' (mapLoc Trail) $ located (preview _Line)++-- | Prism onto a 'Located' 'Loop'.+_LocLoop :: Prism' (Located (Trail v n)) (Located (Trail' Loop v n))+_LocLoop = prism' (mapLoc Trail) $ located (preview _Loop)++instance Rewrapped (Trail v n) (Trail v' n')+instance Wrapped (Trail v n) where+  type Unwrapped (Trail v n) = Either (Trail' Line v n) (Trail' Loop v n)+  _Wrapped' = iso getTrail (either Trail Trail)+    where+      getTrail :: Trail v n -> Either (Trail' Line v n) (Trail' Loop v n)+      getTrail (Trail t@(Line {})) = Left t+      getTrail (Trail t@(Loop {})) = Right t++--------------------------------------------------+-- Constructors and eliminators for Trail++-- | A generic eliminator for 'Trail', taking functions specifying+--   what to do in the case of a line or a loop.+withTrail :: (Trail' Line v n -> r) -> (Trail' Loop v n -> r) -> Trail v n -> r+withTrail line loop (Trail t) = withTrail' line loop t++-- | Modify a @Trail@, specifying two separate transformations for the+--   cases of a line or a loop.+onTrail :: (Trail' Line v n -> Trail' l1 v n) -> (Trail' Loop v n -> Trail' l2 v n)+        -> Trail v n -> Trail v n+onTrail o c = withTrail (wrapTrail . o) (wrapTrail . c)++-- | An eliminator for @Trail@ based on eliminating lines: if the+--   trail is a line, the given function is applied; if it is a loop, it+--   is first converted to a line with 'cutLoop'.  That is,+--+-- @+-- withLine f === 'withTrail' f (f . 'cutLoop')+-- @+withLine :: (Metric v, OrderedField n)+              => (Trail' Line v n -> r) -> Trail v n -> r+withLine f = withTrail f (f . cutLoop)++-- | Modify a @Trail@ by specifying a transformation on lines.  If the+--   trail is a line, the transformation will be applied directly.  If+--   it is a loop, it will first be cut using 'cutLoop', the+--   transformation applied, and then glued back into a loop with+--   'glueLine'.  That is,+--+--   @+--   onLine f === onTrail f (glueLine . f . cutLoop)+--   @+--+--   Note that there is no corresponding @onLoop@ function, because+--   there is no nice way in general to convert a line into a loop,+--   operate on it, and then convert back.+onLine :: (Metric v, OrderedField n)+       => (Trail' Line v n -> Trail' Line v n) -> Trail v n -> Trail v n+onLine f = onTrail f (glueLine . f . cutLoop)++-- | Convert a 'Trail'' into a 'Trail', hiding the type-level+--   distinction between lines and loops.+wrapTrail :: Trail' l v n -> Trail v n+wrapTrail = Trail++-- | Convert a line into a 'Trail'.  This is the same as 'wrapTrail',+--   but with a more specific type, which can occasionally be+--   convenient for fixing the type of a polymorphic expression.+wrapLine :: Trail' Line v n -> Trail v n+wrapLine = wrapTrail++-- | Convert a loop into a 'Trail'.  This is the same as 'wrapTrail',+--   but with a more specific type, which can occasionally be+--   convenient for fixing the type of a polymorphic expression.+wrapLoop :: Trail' Loop v n -> Trail v n+wrapLoop = wrapTrail++------------------------------------------------------------+--  Constructing trails  -----------------------------------+------------------------------------------------------------++-- | The empty line, which is the identity for concatenation of lines.+emptyLine :: (Metric v, OrderedField n) => Trail' Line v n+emptyLine = Line mempty++-- | A wrapped variant of 'emptyLine'.+emptyTrail :: (Metric v, OrderedField n) => Trail v n+emptyTrail = wrapLine emptyLine++-- | Construct a line from a list of closed segments.+lineFromSegments :: (Metric v, OrderedField n)+                   => [Segment Closed v n] -> Trail' Line v n+lineFromSegments = Line . SegTree . FT.fromList++-- | Construct a loop from a list of closed segments and an open segment+--   that completes the loop.+loopFromSegments :: (Metric v, OrderedField n)+                  => [Segment Closed v n] -> Segment Open v n -> Trail' Loop v n+loopFromSegments segs = Loop (SegTree (FT.fromList segs))++-- | @trailFromSegments === 'wrapTrail' . 'lineFromSegments'@, for+--   conveniently constructing a @Trail@ instead of a @Trail'@.+trailFromSegments :: (Metric v, OrderedField n)+                  => [Segment Closed v n] -> Trail v n+trailFromSegments = wrapTrail . lineFromSegments++-- | Construct a line containing only linear segments from a list of+--   vectors, where each vector represents the offset from one vertex+--   to the next.  See also 'fromOffsets'.+--+--   <<diagrams/src_Diagrams_Trail_lineFromOffsetsEx.svg#diagram=lineFromOffsetsEx&width=300>>+--+--   > import Diagrams.Coordinates+--   > lineFromOffsetsEx = strokeLine $ lineFromOffsets [ 2 ^& 1, 2 ^& (-1), 2 ^& 0.5 ]+lineFromOffsets :: (Metric v, OrderedField n) => [v n] -> Trail' Line v n+lineFromOffsets = lineFromSegments . map straight++-- | @trailFromOffsets === 'wrapTrail' . 'lineFromOffsets'@, for+--   conveniently constructing a @Trail@ instead of a @Trail' Line@.+trailFromOffsets :: (Metric v, OrderedField n) => [v n] -> Trail v n+trailFromOffsets = wrapTrail . lineFromOffsets++-- | Construct a line containing only linear segments from a list of+--   vertices.  Note that only the relative offsets between the+--   vertices matters; the information about their absolute position+--   will be discarded.  That is, for all vectors @v@,+--+-- @+-- lineFromVertices === lineFromVertices . 'translate' v+-- @+--+--   If you want to retain the position information, you should+--   instead use the more general 'fromVertices' function to+--   construct, say, a @'Located' ('Trail'' 'Line' v)@ or a @'Located'+--   ('Trail' v)@.+--+--   <<diagrams/src_Diagrams_Trail_lineFromVerticesEx.svg#diagram=lineFromVerticesEx&width=300>>+--+--   > import Diagrams.Coordinates+--   > lineFromVerticesEx = pad 1.1 . centerXY . strokeLine+--   >   $ lineFromVertices [origin, 0 ^& 1, 1 ^& 2, 5 ^& 1]+lineFromVertices :: (Metric v, OrderedField n)+                   => [Point v n] -> Trail' Line v n+lineFromVertices []  = emptyLine+lineFromVertices [_] = emptyLine+lineFromVertices ps  = lineFromSegments . map straight $ zipWith (.-.) (tail ps) ps+++-- | @trailFromVertices === 'wrapTrail' . 'lineFromVertices'@, for+--   conveniently constructing a @Trail@ instead of a @Trail' Line@.+trailFromVertices :: (Metric v, OrderedField n)+                  => [Point v n] -> Trail v n+trailFromVertices = wrapTrail . lineFromVertices++------------------------------------------------------------+--  Converting between lines and loops  --------------------+------------------------------------------------------------++-- | Make a line into a loop by \"gluing\" the endpoint to the+--   starting point.  In particular, the offset of the final segment+--   is modified so that it ends at the starting point of the entire+--   trail.  Typically, you would first construct a line which you+--   know happens to end where it starts, and then call 'glueLine' to+--   turn it into a loop.+--+--   <<diagrams/src_Diagrams_Trail_glueLineEx.svg#diagram=glueLineEx&width=500>>+--+--   > glueLineEx = pad 1.1 . hsep 1+--   >   $ [almostClosed # strokeLine, almostClosed # glueLine # strokeLoop]+--   >+--   > almostClosed :: Trail' Line V2 Double+--   > almostClosed = fromOffsets $ map r2 [(2, -1), (-3, -0.5), (-2, 1), (1, 0.5)]+--+--   @glueLine@ is left inverse to 'cutLoop', that is,+--+--   @+--   glueLine . cutLoop === id+--   @+glueLine :: (Metric v, OrderedField n) => Trail' Line v n -> Trail' Loop v n+glueLine (Line (SegTree t)) =+  case FT.viewr t of+    FT.EmptyR           -> Loop mempty (Linear OffsetOpen)+    t' FT.:> Linear _      -> Loop (SegTree t') (Linear OffsetOpen)+    t' FT.:> Cubic c1 c2 _ -> Loop (SegTree t') (Cubic c1 c2 OffsetOpen)++-- | @glueTrail@ is a variant of 'glueLine' which works on 'Trail's.+--   It performs 'glueLine' on lines and is the identity on loops.+glueTrail :: (Metric v, OrderedField n) => Trail v n -> Trail v n+glueTrail = onTrail glueLine id++-- | Make a line into a loop by adding a new linear segment from the+--   line's end to its start.+--+--   @closeLine@ does not have any particularly nice theoretical+--   properties, but can be useful /e.g./ when you want to make a+--   closed polygon out of a list of points where the initial point is+--   not repeated at the end.  To use 'glueLine', one would first have+--   to duplicate the initial vertex, like+--+-- @+-- 'glueLine' . 'lineFromVertices' $ ps ++ [head ps]+-- @+--+--   Using @closeLine@, however, one can simply+--+-- @+-- closeLine . lineFromVertices $ ps+-- @+--+--   <<diagrams/src_Diagrams_Trail_closeLineEx.svg#diagram=closeLineEx&width=500>>+--+--   > closeLineEx = pad 1.1 . centerXY . hcat' (with & sep .~ 1)+--   >   $ [almostClosed # strokeLine, almostClosed # closeLine # strokeLoop]+closeLine :: Trail' Line v n -> Trail' Loop v n+closeLine (Line t) = Loop t (Linear OffsetOpen)++-- | @closeTrail@ is a variant of 'closeLine' for 'Trail', which+--   performs 'closeLine' on lines and is the identity on loops.+closeTrail :: Trail v n -> Trail v n+closeTrail = onTrail closeLine id++-- | Turn a loop into a line by \"cutting\" it at the common start/end+--   point, resulting in a line which just happens to start and end at+--   the same place.+--+--   @cutLoop@ is right inverse to 'glueLine', that is,+--+--   @+--   glueLine . cutLoop === id+--   @+cutLoop :: forall v n. (Metric v, OrderedField n)+         => Trail' Loop v n -> Trail' Line v n+cutLoop (Loop (SegTree t) c) =+  case (FT.null t, c) of+    (True, Linear OffsetOpen)      -> emptyLine+    (_   , Linear OffsetOpen)      -> Line (SegTree (t |> Linear off))+    (_   , Cubic c1 c2 OffsetOpen) -> Line (SegTree (t |> Cubic c1 c2 off))+  where+    offV :: v n+    offV = negated . trailMeasure zero (op TotalOffset .view oeOffset) $ t+    off = OffsetClosed offV++-- | @cutTrail@ is a variant of 'cutLoop' for 'Trail'; it is the is+--   the identity on lines and performs 'cutLoop' on loops.+cutTrail :: (Metric v, OrderedField n)+         => Trail v n -> Trail v n+cutTrail = onTrail id cutLoop++------------------------------------------------------------+--  Eliminating trails  ------------------------------------+------------------------------------------------------------++-- | Test whether a line is empty.+isLineEmpty :: (Metric v, OrderedField n) => Trail' Line v n -> Bool+isLineEmpty (Line (SegTree t)) = FT.null t++-- | Test whether a trail is empty.  Note that loops are never empty.+isTrailEmpty :: (Metric v, OrderedField n) => Trail v n -> Bool+isTrailEmpty = withTrail isLineEmpty (const False)++-- | Determine whether a trail is a line.+isLine :: Trail v n -> Bool+isLine = not . isLoop++-- | Determine whether a trail is a loop.+isLoop :: Trail v n -> Bool+isLoop = withTrail (const False) (const True)++-- | Extract the segments comprising a line.+lineSegments :: Trail' Line v n -> [Segment Closed v n]+lineSegments (Line (SegTree t)) = F.toList t++-- | Modify a line by applying a function to its list of segments.+onLineSegments+  :: (Metric v, OrderedField n)+  => ([Segment Closed v n] -> [Segment Closed v n])+  -> Trail' Line v n -> Trail' Line v n+onLineSegments f = lineFromSegments . f . lineSegments++-- | Extract the segments comprising a loop: a list of closed+--   segments, and one final open segment.+loopSegments :: Trail' Loop v n -> ([Segment Closed v n], Segment Open v n)+loopSegments (Loop (SegTree t) c) = (F.toList t, c)++-- | Extract the segments of a trail.  If the trail is a loop it will+--   first have 'cutLoop' applied.+trailSegments :: (Metric v, OrderedField n)+              => Trail v n -> [Segment Closed v n]+trailSegments = withLine lineSegments++-- | Extract the offsets of the segments of a trail.+trailOffsets :: (Metric v, OrderedField n) => Trail v n -> [v n]+trailOffsets = withLine lineOffsets++-- | Compute the offset from the start of a trail to the end.  Satisfies+--+--   @+--   trailOffset === sumV . trailOffsets+--   @+--+--   but is more efficient.+--+--   <<diagrams/src_Diagrams_Trail_trailOffsetEx.svg#diagram=trailOffsetEx&width=300>>+--+--   > trailOffsetEx = (strokeLine almostClosed <> showOffset) # centerXY # pad 1.1+--   >   where showOffset = fromOffsets [trailOffset (wrapLine almostClosed)]+--   >                    # strokeP # lc red+trailOffset :: (Metric v, OrderedField n) => Trail v n -> v n+trailOffset = withLine lineOffset++-- | Extract the offsets of the segments of a line.+lineOffsets :: Trail' Line v n -> [v n]+lineOffsets = map segOffset . lineSegments++-- | Extract the offsets of the segments of a loop.+loopOffsets :: (Metric v, OrderedField n) => Trail' Loop v n -> [v n]+loopOffsets = lineOffsets . cutLoop++-- | Compute the offset from the start of a line to the end.  (Note,+--   there is no corresponding @loopOffset@ function because by+--   definition it would be constantly zero.)+lineOffset :: (Metric v, OrderedField n) => Trail' Line v n -> v n+lineOffset (Line t) = trailMeasure zero (op TotalOffset . view oeOffset) t++-- | Extract the points of a concretely located trail, /i.e./ the points+--   where one segment ends and the next begins. Note that for loops,+--   the starting point will /not/ be repeated at the end.  If you+--   want this behavior, you can use 'cutTrail' to make the loop into+--   a line first, which happens to repeat the same point at the start+--   and end, /e.g./ with @trailPoints . mapLoc cutTrail@.+--+--   Note that it does not make sense to ask for the points of a+--   'Trail' by itself; if you want the points of a trail+--   with the first point at, say, the origin, you can use+--   @trailPoints . (\`at\` origin)@.+--+--   This function allows you "observe" the fact that trails are+--   implemented as lists of segments, which may be problematic if we+--   want to think of trails as parametric vector functions. This also+--   means that the behavior of this function may not be stable under+--   future changes to the implementation of trails.  For an+--   unproblematic version which only yields vertices at which there+--   is a sharp corner, excluding points where the trail is+--   differentiable, see 'trailVertices'.+--+--   This function is not re-exported from "Diagrams.Prelude"; to use+--   it, import "Diagrams.Trail".+trailPoints :: (Metric v, OrderedField n)+              => Located (Trail v n) -> [Point v n]+trailPoints (viewLoc -> (p,t))+  = withTrail (linePoints . (`at` p)) (loopPoints . (`at` p)) t++-- | Extract the segment join points of a concretely located line.  See+--   'trailPoints' for more information.+--+--   This function allows you "observe" the fact that lines are+--   implemented as lists of segments, which may be problematic if we+--   want to think of lines as parametric vector functions. This also+--   means that the behavior of this function may not be stable under+--   future changes to the implementation of trails.  For an+--   unproblematic version which only yields vertices at which there+--   is a sharp corner, excluding points where the trail is+--   differentiable, see 'lineVertices'.+--+--   This function is not re-exported from "Diagrams.Prelude"; to use+--   it, import "Diagrams.Trail".+linePoints :: (Metric v, OrderedField n)+             => Located (Trail' Line v n) -> [Point v n]+linePoints (viewLoc -> (p,t))+  = segmentPoints p . lineSegments $ t++-- | Extract the segment join points of a concretely located loop.  Note that the+--   initial vertex is not repeated at the end.  See 'trailPoints' for+--   more information.+--+--   This function allows you "observe" the fact that lines are+--   implemented as lists of segments, which may be problematic if we+--   want to think of lines as parametric vector functions. This also+--   means that the behavior of this function may not be stable under+--   future changes to the implementation of trails.  For an+--   unproblematic version which only yields vertices at which there+--   is a sharp corner, excluding points where the trail is+--   differentiable, see 'lineVertices'.+--+--   This function is not re-exported from "Diagrams.Prelude"; to use+--   it, import "Diagrams.Trail".+loopPoints :: (Metric v, OrderedField n)+             => Located (Trail' Loop v n) -> [Point v n]+loopPoints (viewLoc -> (p,t))+  = segmentPoints p . fst . loopSegments $ t++segmentPoints :: (Additive v, Num n) => Point v n -> [Segment Closed v n] -> [Point v n]+segmentPoints p = scanl (.+^) p . map segOffset++tolerance :: OrderedField a => a+tolerance = 10e-16++-- | Extract the vertices of a concretely located trail.  Here a /vertex/+--   is defined as a non-differentiable point on the trail, /i.e./ a+--   sharp corner.  (Vertices are thus a subset of the places where+--   segments join; if you want all joins between segments, see+--   'trailPoints'.)  The tolerance determines how close the tangents+--   of two segments must be at their endpoints to consider the+--   transition point to be differentiable.+--+--   Note that for loops, the starting vertex will /not/ be repeated+--   at the end.  If you want this behavior, you can use 'cutTrail' to+--   make the loop into a line first, which happens to repeat the same+--   vertex at the start and end, /e.g./ with @trailVertices . mapLoc+--   cutTrail@.+--+--   It does not make sense to ask for the vertices of a 'Trail' by+--   itself; if you want the vertices of a trail with the first vertex+--   at, say, the origin, you can use @trailVertices . (\`at\`+--   origin)@.+trailVertices' :: (Metric v, OrderedField n)+              => n ->  Located (Trail v n) -> [Point v n]+trailVertices' toler (viewLoc -> (p,t))+  = withTrail (lineVertices' toler . (`at` p)) (loopVertices' toler . (`at` p)) t++-- | Like 'trailVertices'', with a default tolerance.+trailVertices :: (Metric v, OrderedField n)+              => Located (Trail v n) -> [Point v n]+trailVertices = trailVertices' tolerance++-- | Extract the vertices of a concretely located line.  See+--   'trailVertices' for more information.+lineVertices' :: (Metric v, OrderedField n)+             => n -> Located (Trail' Line v n) -> [Point v n]+lineVertices' toler (viewLoc -> (p,t))+  = segmentVertices' toler p . lineSegments $ t++-- | Like 'lineVertices'', with a default tolerance.+lineVertices :: (Metric v, OrderedField n)+             => Located (Trail' Line v n) -> [Point v n]+lineVertices = lineVertices' tolerance++-- | Extract the vertices of a concretely located loop.  Note that the+--   initial vertex is not repeated at the end.  See 'trailVertices' for+--   more information.+loopVertices' :: (Metric v, OrderedField n)+             => n -> Located (Trail' Loop v n) -> [Point v n]+loopVertices' toler (viewLoc -> (p,t))+  | length segs > 1 = if far > toler  then init ps else init . drop 1 $ ps+  | otherwise       = ps+  where+    far = quadrance ((signorm . tangentAtStart . head $ segs) ^-^+                       (signorm . tangentAtEnd   . last $ segs))+    segs = lineSegments . cutLoop $ t+    ps = segmentVertices' toler p segs++-- | Same as 'loopVertices'', with a default tolerance.+loopVertices :: (Metric v, OrderedField n)+             => Located (Trail' Loop v n) -> [Point v n]+loopVertices = loopVertices' tolerance++-- | The vertices of a list of segments laid end to end.+--   The start and end points are always included in the list of+--   vertices.  The other points connecting segments are included if+--   the slope at the end of a segment is not equal to the slope at+--   the beginning of the next.  The 'toler' parameter is used to+--   control how close the slopes need to be in order to declare them+--   equal.+segmentVertices' :: (Metric v, OrderedField n)+             => n -> Point v n -> [Segment Closed v n] -> [Point v n]+segmentVertices' toler p ts  =+  case ps of+    (x:_:_) -> x : select (drop 1 ps) ds ++ [last ps]+    _       -> ps+    where+      ds = zipWith far tans (drop 1 tans)+      tans = [(signorm . tangentAtStart $ s+              ,signorm . tangentAtEnd   $ s) | s <- ts]+      ps = scanl (.+^) p . map segOffset $ ts+      far p2 q2 = quadrance (snd p2 ^-^ fst q2) > toler++select :: [a] -> [Bool] -> [a]+select xs bs = map fst $ filter snd (zip xs bs)++-- | Convert a concretely located trail into a list of fixed segments.+--   'unfixTrail' is almost its left inverse.+fixTrail :: (Metric v, OrderedField n)+         => Located (Trail v n) -> [FixedSegment v n]+fixTrail t = map mkFixedSeg (trailLocSegments t)++-- | Convert a list of fixed segments into a located trail.  Note that+--   this may lose information: it throws away the locations of all+--   but the first @FixedSegment@.  This does not matter precisely+--   when each @FixedSegment@ begins where the previous one ends.+--+--   This is almost left inverse to 'fixTrail', that is, @unfixTrail+--   . fixTrail == id@, except for the fact that @unfixTrail@ will+--   never yield a @Loop@.  In the case of a loop, we instead have+--   @glueTrail . unfixTrail . fixTrail == id@.  On the other hand, it+--   is not the case that @fixTrail . unfixTrail == id@ since+--   @unfixTrail@ may lose information.+unfixTrail+  :: (Metric v, Ord n, Floating n)+  => [FixedSegment v n] -> Located (Trail v n)+unfixTrail = mapLoc trailFromSegments . takeLoc . map fromFixedSeg+  where+    takeLoc []       = [] `at` origin+    takeLoc xs@(x:_) = map unLoc xs `at` loc x++-- | Convert a concretely located trail into a list of located segments.+trailLocSegments :: (Metric v, OrderedField n)+                  => Located (Trail v n) -> [Located (Segment Closed v n)]+trailLocSegments t = zipWith at (trailSegments (unLoc t)) (trailPoints t)++------------------------------------------------------------+--  Modifying trails  --------------------------------------+------------------------------------------------------------++-- | Reverse a trail.  Semantically, if a trail given by a function t+--   from [0,1] to vectors, then the reverse of t is given by t'(s) =+--   t(1-s).  @reverseTrail@ is an involution, that is,+--+--   @+--   reverseTrail . reverseTrail === id+--   @+reverseTrail :: (Metric v, OrderedField n) => Trail v n -> Trail v n+reverseTrail = onTrail reverseLine reverseLoop++-- | Reverse a concretely located trail.  The endpoint of the original+--   trail becomes the starting point of the reversed trail, so the+--   original and reversed trails comprise exactly the same set of+--   points.  @reverseLocTrail@ is an involution, /i.e./+--+--   @+--   reverseLocTrail . reverseLocTrail === id+--   @+reverseLocTrail :: (Metric v, OrderedField n)+                => Located (Trail v n) -> Located (Trail v n)+reverseLocTrail (viewLoc -> (p, t)) = reverseTrail t `at` (p .+^ trailOffset t)++-- | Reverse a line.  See 'reverseTrail'.+reverseLine :: (Metric v, OrderedField n)+            => Trail' Line v n -> Trail' Line v n+reverseLine = onLineSegments (reverse . map reverseSegment)++-- | Reverse a concretely located line.  See 'reverseLocTrail'.+reverseLocLine :: (Metric v, OrderedField n)+               => Located (Trail' Line v n) -> Located (Trail' Line v n)+reverseLocLine (viewLoc -> (p,l)) = reverseLine l `at` (p .+^ lineOffset l)++-- | Reverse a loop.  See 'reverseTrail'.+reverseLoop :: (Metric v, OrderedField n)+            => Trail' Loop v n -> Trail' Loop v n+reverseLoop = glueLine . reverseLine . cutLoop++-- | Reverse a concretely located loop.  See 'reverseLocTrail'.  Note+--   that this is guaranteed to preserve the location.+reverseLocLoop :: (Metric v, OrderedField n)+               => Located (Trail' Loop v n) -> Located (Trail' Loop v n)+reverseLocLoop = mapLoc reverseLoop++-- | Same as 'reverseLine' or 'reverseLoop'.+instance (Metric v, OrderedField n) => Reversing (Trail' l v n) where+  reversing t@(Line _)   = onLineSegments (reverse . map reversing) t+  reversing t@(Loop _ _) = glueLine . reversing . cutLoop $ t++-- | Same as 'reverseTrail'.+instance (Metric v, OrderedField n) => Reversing (Trail v n) where+  reversing (Trail t) = Trail (reversing t)++-- | Same as 'reverseLocLine' or 'reverseLocLoop'.+instance (Metric v, OrderedField n) => Reversing (Located (Trail' l v n)) where+  reversing l@(Loc _ Line {}) = reverseLocLine l+  reversing l@(Loc _ Loop {}) = reverseLocLoop l++-- | Same as 'reverseLocTrail'.+instance (Metric v, OrderedField n) => Reversing (Located (Trail v n)) where+  reversing = reverseLocTrail++------------------------------------------------------------+--  Serialize instances+------------------------------------------------------------++instance (Serialize (v n), OrderedField n, Metric v) => Serialize (Trail v n) where+  {-# INLINE get #-}+  get = do+    isLine <- Serialize.get+    case isLine of+      True  -> do+        segTree <- Serialize.get+        return (Trail (Line segTree))+      False -> do+        segTree <- Serialize.get+        segment <- Serialize.get+        return (Trail (Loop segTree segment))++  {-# INLINE put #-}+  put (Trail (Line segTree)) = do+    Serialize.put True+    Serialize.put segTree++  put (Trail (Loop segTree segment)) = do+    Serialize.put False+    Serialize.put segTree+    Serialize.put segment++instance (OrderedField n, Metric v, Serialize (v n)) => Serialize (SegTree v n) where+  {-# INLINE put #-}+  put (SegTree fingerTree) = Serialize.put (F.toList fingerTree)++  {-# INLINE get #-}+  get = do+    fingerTree <- Serialize.get+    return (SegTree (FT.fromList fingerTree))
src/Diagrams/TrailLike.hs view
@@ -1,6 +1,8 @@+{-# LANGUAGE ConstraintKinds      #-} {-# LANGUAGE FlexibleContexts     #-} {-# LANGUAGE FlexibleInstances    #-} {-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeOperators        #-} {-# LANGUAGE UndecidableInstances #-} ----------------------------------------------------------------------------- -- |@@ -28,14 +30,17 @@         ) where -import           Data.AffineSpace ((.-.))-import           Data.VectorSpace+import           Control.Lens     (view, _Unwrapped')  import           Diagrams.Core import           Diagrams.Located import           Diagrams.Segment import           Diagrams.Trail +import           Linear.Affine+import           Linear.Metric+import           Linear.Vector+ ------------------------------------------------------------ --  TrailLike class ------------------------------------------------------------@@ -60,42 +65,42 @@ --   course call 'trailLike' directly; more typically, one would use --   one of the provided functions like 'fromOffsets', 'fromVertices', --   'fromSegments', or '~~'.-class (InnerSpace (V t), OrderedField (Scalar (V t))) => TrailLike t where+class (Metric (V t), OrderedField (N t)) => TrailLike t where    trailLike-    :: Located (Trail (V t))  -- ^ The concretely located trail.  Note-                              --   that some trail-like things-                              --   (e.g. 'Trail's) may ignore the-                              --   location.+    :: Located (Trail (V t) (N t)) -- ^ The concretely located trail.  Note+                                   --   that some trail-like things+                                   --   (e.g. 'Trail's) may ignore the+                                   --   location.     -> t  ------------------------------------------------------------ --  Instances  ---------------------------------------------  -- | A list of points is trail-like; this instance simply---   computes the vertices of the trail, using 'trailVertices'.-instance (InnerSpace v, OrderedField (Scalar v)) => TrailLike [Point v] where-  trailLike = trailVertices+--   computes the vertices of the trail, using 'trailPoints'.+instance (Metric v, OrderedField n) => TrailLike [Point v n] where+  trailLike = trailPoints  -- | Lines are trail-like.  If given a 'Trail' which contains a loop, --   the loop will be cut with 'cutLoop'.  The location is ignored.-instance (InnerSpace v, OrderedField (Scalar v)) => TrailLike (Trail' Line v) where+instance (Metric v, OrderedField n) => TrailLike (Trail' Line v n) where   trailLike = withTrail id cutLoop . unLoc  -- | Loops are trail-like.  If given a 'Trail' containing a line, the --   line will be turned into a loop using 'glueLine'.  The location --   is ignored.-instance (InnerSpace v, OrderedField (Scalar v)) => TrailLike (Trail' Loop v) where+instance (Metric v, OrderedField n) => TrailLike (Trail' Loop v n) where   trailLike = withTrail glueLine id . unLoc  -- | 'Trail's are trail-like; the location is simply ignored.-instance (InnerSpace v, OrderedField (Scalar v)) => TrailLike (Trail v) where+instance (Metric v, OrderedField n) => TrailLike (Trail v n) where   trailLike = unLoc  -- | Translationally invariant things are trail-like as long as the --   underlying type is. instance TrailLike t => TrailLike (TransInv t) where-  trailLike = TransInv . trailLike+  trailLike = view _Unwrapped' . trailLike  -- | 'Located' things are trail-like as long as the underlying type --   is.  The location is taken to be the location of the input@@ -110,24 +115,39 @@ -- | Construct a trail-like thing from a list of segments, with the --   origin as the location. -----   XXX example/picture-fromSegments :: TrailLike t => [Segment Closed (V t)] -> t+--   <<diagrams/src_Diagrams_TrailLike_fromSegmentsEx.svg#diagram=fromSegmentsEx&height=200>>+--+--   > fromSegmentsEx = fromSegments+--   >   [ straight (r2 (1,1))+--   >   , bézier3  (r2 (1,1)) unitX unit_Y+--   >   , straight unit_X+--   >   ]+--   >   # centerXY # pad 1.1+fromSegments :: TrailLike t => [Segment Closed (V t) (N t)] -> t fromSegments = fromLocSegments . (`at` origin)  -- | Construct a trail-like thing from a located list of segments.-fromLocSegments :: TrailLike t => Located [Segment Closed (V t)] -> t+fromLocSegments :: TrailLike t => Located [Segment Closed (V t) (N t)] -> t fromLocSegments = trailLike . mapLoc trailFromSegments  -- | Construct a trail-like thing of linear segments from a list --   of offsets, with the origin as the location. -----   XXX example/picture-fromOffsets :: TrailLike t => [V t] -> t+--   <<diagrams/src_Diagrams_TrailLike_fromOffsetsEx.svg#diagram=fromOffsetsEx&width=300>>+--+--   > fromOffsetsEx = fromOffsets+--   >   [ unitX+--   >   , unitX # rotateBy (1/6)+--   >   , unitX # rotateBy (-1/6)+--   >   , unitX+--   >   ]+--   >   # centerXY # pad 1.1+fromOffsets :: TrailLike t => [Vn t] -> t fromOffsets = trailLike . (`at` origin) . trailFromOffsets  -- | Construct a trail-like thing of linear segments from a located --   list of offsets.-fromLocOffsets :: (V (V t) ~ V t, TrailLike t) => Located [V t] -> t+fromLocOffsets :: (V t ~ v, N t ~ n, V (v n) ~ v, N (v n) ~ n, TrailLike t) => Located [v n] -> t fromLocOffsets = trailLike . mapLoc trailFromOffsets  -- | Construct a trail-like thing connecting the given vertices with@@ -135,23 +155,51 @@ --   vertices are given, the empty trail is used with the origin as --   the location. -----   XXX example/picture-fromVertices :: TrailLike t => [Point (V t)] -> t+--   <<diagrams/src_Diagrams_TrailLike_fromVerticesEx.svg#diagram=fromVerticesEx&width=300>>+--+--   > import Data.List (transpose)+--   >+--   > fromVerticesEx =+--   >   ( [ pentagon 1+--   >     , pentagon 1.3 # rotateBy (1/15)+--   >     , pentagon 1.5 # rotateBy (2/15)+--   >     ]+--   >     # transpose+--   >     # concat+--   >   )+--   >   # fromVertices+--   >   # closeTrail # strokeTrail+--   >   # centerXY # pad 1.1+fromVertices :: TrailLike t => [Point (V t) (N t)] -> t fromVertices []       = trailLike (emptyTrail `at` origin) fromVertices ps@(p:_) = trailLike (trailFromSegments (segmentsFromVertices ps) `at` p) -segmentsFromVertices :: AdditiveGroup v => [Point v] -> [Segment Closed v]+segmentsFromVertices :: (Additive v, Num n) => [Point v n] -> [Segment Closed v n] segmentsFromVertices []         = [] segmentsFromVertices vvs@(_:vs) = map straight (zipWith (flip (.-.)) vvs vs)  -- | Create a linear trail between two given points.-(~~) :: TrailLike t => Point (V t) -> Point (V t) -> t+--+--   <<diagrams/src_Diagrams_TrailLike_twiddleEx.svg#diagram=twiddleEx&width=300>>+--+--   > twiddleEx+--   >   = mconcat ((~~) <$> hexagon 1 <*> hexagon 1)+--   >   # centerXY # pad 1.1+(~~) :: (V t ~ v, N t ~ n, TrailLike t) => Point v n -> Point v n -> t p1 ~~ p2 = fromVertices [p1, p2]  -- | Given a concretely located trail, \"explode\" it by turning each --   segment into its own separate trail.  Useful for (say) applying a --   different style to each segment.-explodeTrail :: (VectorSpace (V t), TrailLike t) => Located (Trail (V t)) -> [t]+--+--   <<diagrams/src_Diagrams_TrailLike_explodeTrailEx.svg#diagram=explodeTrailEx&width=300>>+--+--   > explodeTrailEx+--   >   = pentagon 1+--   >   # explodeTrail  -- generate a list of diagrams+--   >   # zipWith lc [orange, green, yellow, red, blue]+--   >   # mconcat # centerXY # pad 1.1+explodeTrail :: (V t ~ v, N t ~ n, TrailLike t) => Located (Trail v n) -> [t] explodeTrail = map (mkTrail . fromFixedSeg) . fixTrail   where     mkTrail = trailLike . mapLoc (trailFromSegments . (:[]))
src/Diagrams/Transform.hs view
@@ -1,7 +1,15 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE TypeFamilies          #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Transform--- Copyright   :  (c) 2011-13 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-15 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -11,6 +19,7 @@ -- ----------------------------------------------------------------------------- + module Diagrams.Transform     ( -- * Transformations       Transformation, inv, transl, apply, papply@@ -22,7 +31,7 @@     , translation, translate, moveTo, place, scaling, scale        -- * Miscellaneous transformation-related utilities-    , conjugate, under+    , conjugate, underT, transformed, translated, movedTo, movedFrom        -- * The HasOrigin class @@ -30,27 +39,90 @@      ) where +import           Control.Lens   hiding (transform) import           Data.Semigroup import           Diagrams.Core +import           Linear.Vector+ -- | Conjugate one transformation by another. @conjugate t1 t2@ is the --   transformation which performs first @t1@, then @t2@, then the --   inverse of @t1@.-conjugate :: HasLinearMap v => Transformation v -> Transformation v -> Transformation v-conjugate t1 t2  = inv t1 <> t2 <> t1+conjugate :: (Additive v, Num n)+          => Transformation v n -> Transformation v n -> Transformation v n+conjugate t1 t2 = inv t1 <> t2 <> t1 --- | Carry out some transformation \"under\" another one: @f ``under``+-- | Carry out some transformation \"under\" another one: @f ``underT`` --   t@ first applies @t@, then @f@, then the inverse of @t@.  For---   example, @'scaleX' 2 ``under`` 'rotationBy' (-1/8 :: Turn)@+--   example, @'scaleX' 2 ``underT`` 'rotation' (-1/8 \@\@ Turn)@ --   is the transformation which scales by a factor of 2 along the --   diagonal line y = x. -- --   Note that -----   @---   (transform t2) `under` t1 == transform (conjugate t1 t2)---   @+-- @+-- (transform t2) `underT` t1 == transform (conjugate t1 t2)+-- @ -- --   for all transformations @t1@ and @t2@.-under :: Transformable a => (a -> a) -> Transformation (V a) -> a -> a-f `under` t = transform (inv t) . f . transform t+--+--   See also the isomorphisms like 'transformed', 'movedTo',+--   'movedFrom', and 'translated'.+underT :: (InSpace v n a, SameSpace a b, Transformable a, Transformable b)+      => (a -> b) -> Transformation v n -> a -> b+f `underT` t = transform (inv t) . f . transform t++-- | Use a 'Transformation' to make an 'Iso' between an object+--   transformed and untransformed. This is useful for carrying out+--   functions 'under' another transform:+--+-- @+-- under (transformed t) f               == transform (inv t) . f . transform t+-- under (transformed t1) (transform t2) == transform (conjugate t1 t2)+-- transformed t ## a                    == transform t a+-- a ^. transformed t                    == transform (inv t) a+-- @+transformed :: (InSpace v n a, SameSpace a b, Transformable a, Transformable b)+            => Transformation v n -> Iso a b a b+transformed t = iso (transform $ inv t) (transform t)++-- | Use a 'Point' to make an 'Iso' between an object+--   moved to and from that point:+--+-- @+-- under (movedTo p) f == moveTo (-p) . f . moveTo p+-- over (movedTo p) f  == moveTo p . f . moveTo (-p)+-- movedTo p           == from (movedFrom p)+-- movedTo p ## a      == moveTo p a+-- a ^. movedTo p      == moveOriginTo p a+-- @+movedTo :: (InSpace v n a, SameSpace a b, HasOrigin a, HasOrigin b)+        => Point v n -> Iso a b a b+movedTo p = iso (moveTo (negated p)) (moveTo p)++-- | Use a 'Transformation' to make an 'Iso' between an object+--   transformed and untransformed. We have+--+-- @+-- under (movedFrom p) f == moveTo p . f . moveTo (-p)+-- movedFrom p           == from (movedTo p)+-- movedFrom p ## a      == moveOriginTo p a+-- a ^. movedFrom p      == moveTo p a+-- over (movedFrom p) f  == moveTo (-p) . f . moveTo p+-- @+movedFrom :: (InSpace v n a, SameSpace a b, HasOrigin a, HasOrigin b)+          => Point v n -> Iso a b a b+movedFrom p = iso (moveOriginTo (negated p)) (moveOriginTo p)++-- | Use a vector to make an 'Iso' between an object translated and+--   untranslated.+--+-- @+-- under (translated v) f == translate (-v) . f . translate v+-- translated v ## a      == translate v a+-- a ^. translated v      == translate (-v) a+-- over (translated v) f  == translate v . f . translate (-v)+-- @+translated :: (InSpace v n a, SameSpace a b, Transformable a, Transformable b)+           => v n -> Iso a b a b+translated v = transformed $ translation v
+ src/Diagrams/Transform/Matrix.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE MonoLocalBinds  #-}+{-# LANGUAGE TypeFamilies    #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Transform.Matrix+-- Copyright   :  (c) 2014 diagrams team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Functions for converting between 'Transformation's and matricies.+--+-----------------------------------------------------------------------------++module Diagrams.Transform.Matrix where++import           Control.Arrow           ((&&&))+import           Control.Lens+import           Data.Distributive+import qualified Data.Foldable           as F+import           Data.Functor.Rep++import           Diagrams.Core.Transform as D+import           Diagrams.ThreeD.Types+import           Diagrams.TwoD.Types++import           Linear.Matrix+import           Linear.Vector++-- | Build a matrix from a 'Transformation', ignoring the translation.+mkMat :: (HasBasis v, Num n) => Transformation v n -> v (v n)+mkMat t = distribute . tabulate $ apply t . unit . (\x -> el x)++-- | Build a 3D transformation matrix in homogeneous coordinates from+--   a 'Transformation V3'.+mkMatHomo :: Num n => Transformation V3 n -> M44 n+mkMatHomo t = mkTransformationMat (mkMat t) (transl t)++-- | Make a 2D transformation from a 2x2 transform matrix and a+--   translation vector. Does not check if the matrix is not invertible+--   (in which case the 'T2' will be invalid).+fromMat22 :: Floating n => M22 n -> V2 n -> T2 n+fromMat22 m v = fromMatWithInv m (inv22 m) v++-- | Make a 3D transformation from a 3x3 transform matrix and a+--   translation vector. Does not check if the matrix is not invertible+--   (in which case the 'T3' will be invalid).+fromMat33 :: Floating n => M33 n -> V3 n -> T3 n+fromMat33 m v = fromMatWithInv m (inv33 m) v++-- | Build a transform with a maxtrix along with its inverse.+fromMatWithInv :: (Additive v, Distributive v, F.Foldable v, Num n)+  => v (v n) -- ^ matrix+  -> v (v n) -- ^ inverse+  -> v n     -- ^ translation+  -> Transformation v n+fromMatWithInv m m_ v =+  Transformation ((m !*)            <-> (m_ !*))+                 ((distribute m !*) <-> (distribute m_ !*))+                 v++-- | Prism onto a 2D transformation from a 2x2 transform matrix and+--   translation vector. Does not check if the matrix is invertible (in+--   which case the 'T2' will be invalid).+mat22 :: Floating n => Iso' (M22 n, V2 n) (T2 n)+mat22 = iso (uncurry fromMat22) (mkMat &&& transl)++-- | Prism onto a 3D transformation from a 3x3 transform matrix and+--   translation vector. Does not check if the matrix is invertible+--   (in which case the 'T3' will be invalid).+mat33 :: Floating n => Iso' (M33 n, V3 n) (T3 n)+mat33 = iso (uncurry fromMat33) (mkMat &&& transl)
+ src/Diagrams/Transform/ScaleInv.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE UndecidableInstances  #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.TwoD.Transform.ScaleInv+-- Copyright   :  (c) 2012-2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Wrapper for creating scale-invariant objects in two dimensions.+--+-----------------------------------------------------------------------------++module Diagrams.Transform.ScaleInv+    ( ScaleInv(..)+    , scaleInvObj, scaleInvDir, scaleInvLoc+    , scaleInv, scaleInvPrim)+    where++import           Control.Lens            (makeLenses, view, (^.))+import           Data.Typeable++import           Diagrams.Angle+import           Diagrams.Core+import           Diagrams.TwoD.Transform+import           Diagrams.TwoD.Types++import           Linear.Affine+import           Linear.Vector++-- | The @ScaleInv@ wrapper creates two-dimensional /scale-invariant/+--   objects.  Intuitively, a scale-invariant object is affected by+--   transformations like translations and rotations, but not by scales.+--+--   However, this is problematic when it comes to /non-uniform/+--   scales (/e.g./ @scaleX 2 . scaleY 3@) since they can introduce a+--   perceived rotational component.  The prototypical example is an+--   arrowhead on the end of a path, which should be scale-invariant.+--   However, applying a non-uniform scale to the path but not the+--   arrowhead would leave the arrowhead pointing in the wrong+--   direction.+--+--   Moreover, for objects whose local origin is not at the local+--   origin of the parent diagram, any scale can result in a+--   translational component as well.+--+--   The solution is to also store a point (indicating the location,+--   /i.e./ the local origin) and a unit vector (indicating the+--   /direction/) along with a scale-invariant object.  A+--   transformation to be applied is decomposed into rotational and+--   translational components as follows:+--+--   * The transformation is applied to the direction vector, and the+--   difference in angle between the original direction vector and its+--   image under the transformation determines the rotational+--   component.  The rotation is applied with respect to the stored+--   location, rather than the global origin.+--+--   * The vector from the location to the image of the location under+--   the transformation determines the translational component.++data ScaleInv t =+  ScaleInv+  { _scaleInvObj :: t+  , _scaleInvDir :: Vn t+  , _scaleInvLoc :: Point (V t) (N t)+  }+  deriving Typeable++deriving instance (Show t, Show (Vn t)) => Show (ScaleInv t)++makeLenses ''ScaleInv++-- | Create a scale-invariant object pointing in the given direction,+--   located at the origin.+scaleInv :: (V t ~ v, N t ~ n, Additive v, Num n) => t -> v n -> ScaleInv t+scaleInv t d = ScaleInv t d origin++type instance V (ScaleInv t) = V t+type instance N (ScaleInv t) = N t++instance (V t ~ v, N t ~ n, Additive v, Num n, HasOrigin t) => HasOrigin (ScaleInv t) where+  moveOriginTo p (ScaleInv t v l) = ScaleInv (moveOriginTo p t) v (moveOriginTo p l)++instance (V t ~ V2, N t ~ n, RealFloat n, Transformable t) => Transformable (ScaleInv t) where+  transform tr (ScaleInv t v l) = ScaleInv (trans . rot $ t) (rot v) l'+    where+      angle = transform tr v ^. _theta++      rot :: (V k ~ V t, N k ~ N t, Transformable k) => k -> k+      rot = rotateAround l angle++      l'  = transform tr l++      trans = translate (l' .-. l)++{- Proof that the above satisfies the monoid action laws.++   1. transform mempty (ScaleInv t v l)+      = ScaleInv (trans . rot $ t) (rot v) l'+        { l'    = transform mempty l = l }+        { trans = translate (l' .-. l)+                = translate (l .-. l)+                = translate zeroV+                = id+        }+        { rot   = rotateAround l angle+                = rotateAround l (direction (transform mempty v) - direction v)+                = rotateAround l (direction v - direction v)+                = rotateAround l 0+                = id+        }+      = ScaleInv t v l++  2. transform t1 (transform t2 (ScaleInv t v l))+     = let angle = direction (transform t2 v) - direction v+           rot   = rotateAround l angle+           l'    = transform t2 l+           trans = translate (l' .-. l)+       in+           transform t1 (ScaleInv (trans . rot $ t) (rot v) l')++     = let angle  = direction (transform t2 v) - direction v+           rot    = rotateAround l angle+           l'     = transform t2 l+           trans  = translate (l' .-. l)+           angle2 = direction (transform t1 (rot v)) - direction (rot v)+           rot2   = rotateAround l' angle2+           l'2    = transform t1 l'+           trans2 = translate (l'2 .-. l')+       in+           ScaleInv (trans2 . rot2 . trans . rot $ t) (rot2 . rot $ v) l'2++       { l'2 = transform t1 l'+             = transform t1 (transform t2 l)+             = transform (t1 <> t2) l+       }+       { trans2 = translate (l'2 .-. l')+                = translate (transform (t1 <> t2) l .-. transform t2 l)+                = translate (transform t1 l .-. l)+       }+       { rot v  = rotateAround l angle v+                = rotate angle `under` translation (origin .-. l) $ v+                = rotate angle v+       }+       { angle2 = direction (transform t1 (rot v)) - direction (rot v)+                = direction (transform t1 (rotate angle v)) - direction (rotate angle v)+                = direction (transform t1 (rotate angle v)) - direction v - angle+       }+       { rot2   = rotateAround l' angle2+                = ???+       }++-}++instance (V t ~ V2, N t ~ n, RealFloat n, Renderable t b) => Renderable (ScaleInv t) b where+  render b = render b . view scaleInvObj++-- | Create a diagram from a single scale-invariant primitive.  The+--   vector argument specifies the direction in which the primitive is+--   \"pointing\" (for the purpose of keeping it rotated correctly+--   under non-uniform scaling).  The primitive is assumed to be+--   \"located\" at the origin (for the purpose of translating it+--   correctly under scaling).+--+--   Note that the resulting diagram will have an /empty/ envelope,+--   trace, and query.  The reason is that the envelope, trace, and+--   query cannot be cached---applying a transformation would cause+--   the cached envelope, etc. to get \"out of sync\" with the+--   scale-invariant object.  The intention, at any rate, is that+--   scale-invariant things will be used only as \"decorations\" (/e.g./+--   arrowheads) which should not affect the envelope, trace, and+--   query.+scaleInvPrim :: (V t ~ V2, N t ~ n, RealFloat n, Typeable t, Renderable t b, Monoid m)+             => t -> V2 n -> QDiagram b (V t) (N t) m+scaleInvPrim t d = mkQD (Prim $ scaleInv t d) mempty mempty mempty mempty
src/Diagrams/TwoD.hs view
@@ -4,7 +4,7 @@ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -25,10 +25,17 @@ --     specialized to two dimensions (see also "Diagrams.Combinators" --     for more general combining). --+--   * "Diagrams.TwoD.Attributes" defines attributes specific to two+--     dimensions, *e.g.* fill color, line color, and gradients.+-- --   * "Diagrams.TwoD.Transform" defines R^2-specific transformations --     such as rotation by an angle, and scaling, translation, and --     reflection in the X and Y directions. --+--   * "Diagrams.TwoD.Deform" defines some non-affine transformations+--     specific to two dimensions, *e.g.* parallel and perspective+--     projections.+-- --   * "Diagrams.TwoD.Ellipse" defines circles and ellipses. -- --   * "Diagrams.TwoD.Arc" defines circular arcs.@@ -42,6 +49,10 @@ --   * "Diagrams.TwoD.Shapes" defines other two-dimensional shapes, --     e.g. various polygons. --+--   * "Diagrams.TwoD.Arrow" contains tools for drawing arrows between+--     things, and "Diagrams.TwoD.Arrowheads" defines a collection of+--     arrowheads.+-- --   * "Diagrams.TwoD.Text" defines primitive text diagrams. -- --   * "Diagrams.TwoD.Image" allows importing external images into diagrams.@@ -58,27 +69,38 @@ ----------------------------------------------------------------------------- module Diagrams.TwoD        ( -- * R^2-         R2, r2, unr2-       , P2, p2, unp2-       , T2+         V2 (..), R1 (..), R2 (..)+       , P2, T2+       , r2, unr2, mkR2+       , p2, unp2, mkP2        , unitX, unitY, unit_X, unit_Y-       , direction, fromDirection, e+       , perp, leftTurn+       , xDir, yDir           -- * Angles        , tau-       , Angle(..)-       , Turn(..), CircleFrac, Rad(..), Deg(..)-       , fullCircle, convertAngle+       , angleV+       , angleDir+       , signedAngleBetween , signedAngleBetweenDirs +       -- * Polar Coördinates+       , HasR(..), r2PolarIso+          -- * Paths          -- ** Stroking-       , stroke, stroke', strokeT, strokeT', strokeLine, strokeLoop-       , strokeLocT, strokeLocLine, strokeLocLoop-       , FillRule(..), fillRule-       , StrokeOpts(..)+       , stroke, stroke'+       , strokePath, strokeP, strokePath', strokeP'+       , strokeTrail, strokeT, strokeTrail', strokeT'+       , strokeLine, strokeLoop+       , strokeLocTrail, strokeLocT, strokeLocLine, strokeLocLoop+       , FillRule(..), fillRule, _fillRule+       , StrokeOpts(..), vertexNames, queryFillRule+       , intersectPoints, intersectPoints'+       , intersectPointsP, intersectPointsP'+       , intersectPointsT, intersectPointsT'           -- ** Clipping-       , clipBy+       , clipBy, clipTo, clipped, _Clip, _clip           -- * Shapes          -- ** Rules@@ -92,11 +114,15 @@        , arc        , arc'        , arcCW+       , arcCCW        , wedge+       , arcBetween+       , annularWedge           -- ** General polygons        , polygon, polyTrail-       , PolygonOpts(..), PolyType(..), PolyOrientation(..)+       , PolygonOpts(..), polyType, polyOrient, polyCenter+       , PolyType(..), PolyOrientation(..)           -- ** Star polygons        , StarOpts(..), star@@ -108,6 +134,7 @@        , square        , pentagon        , hexagon+       , heptagon        , septagon        , octagon        , nonagon@@ -121,19 +148,60 @@           -- ** Other shapes        , roundedRect, roundedRect'-       , RoundedRectOpts(..)+       , RoundedRectOpts(..), radiusTL, radiusTR, radiusBL, radiusBR +         -- ** Arrows+       , arrowV, arrowV'+       , arrowAt, arrowAt'+       , arrowBetween, arrowBetween'+       , connect, connect'+       , connectPerim, connectPerim'+       , connectOutside, connectOutside'+       , arrow, arrow'+       , straightShaft+       , module Diagrams.TwoD.Arrowheads++       , ArrowOpts(..)++       , arrowHead+       , arrowTail+       , arrowShaft+       , headGap+       , tailGap+       , gaps, gap+       , headTexture+       , headStyle+       , tailTexture+       , tailStyle+       , shaftTexture+       , shaftStyle+       , headLength+       , tailLength+       , lengths+          -- * Text        , text, topLeftText, alignedText, baselineText-       , font, fontSize, italic, oblique, bold+       , font, italic, oblique, fontSize+       , bold, bolder, lighter, thinWeight+       , ultraLight, light, mediumWeight, heavy, semiBold, ultraBold+       , _font, _fontSizeR, _fontSize+       , fontSizeO, fontSizeL, fontSizeN, fontSizeG           -- * Images+       , DImage(..), ImageData(..)+       , Embedded, External, Native        , image+       , loadImageEmb+       , loadImageExt+       , uncheckedImageRef+       , raster+       , rasterDia           -- * Transformations          -- ** Rotation-       , rotation, rotate, rotateBy-       , rotationAbout, rotateAbout+       , rotation, rotate, rotateBy, rotated+       , rotationAround, rotateAround+       , rotationTo, rotateTo          -- ** Scaling        , scalingX, scaleX        , scalingY, scaleY@@ -144,60 +212,100 @@        , translationX, translateX        , translationY, translateY        , translation, translate+         -- ** Conformal affine maps+       , scalingRotationTo, scaleRotateTo          -- ** Reflection        , reflectionX, reflectX        , reflectionY, reflectY+       , reflectionXY, reflectXY        , reflectionAbout, reflectAbout          -- ** Shears        , shearingX, shearX        , shearingY, shearY +         -- * Deformations - non-affine transforms+       , parallelX0, perspectiveX1, parallelY0, perspectiveY1+       , facingX, facingY+          -- * Combinators          -- ** Combining multiple diagrams-       , (===), (|||), atAngle-       , hcat, hcat'-       , vcat, vcat' +       , (===), (|||)+       , hcat, hcat', hsep+       , vcat, vcat', vsep+          -- ** Spacing and envelopes        , strutX, strutY        , padX, padY         , extrudeLeft, extrudeRight, extrudeBottom, extrudeTop -       , view+       , rectEnvelope, crop           -- ** Background -       , boundingRect, bg+       , boundingRect, bg, bgFrame           -- * Alignment        , alignL, alignR, alignT, alignB, alignTL, alignTR, alignBL, alignBR        , alignX, alignY        , centerX, centerY, centerXY +         -- * Snugging+       , snugL, snugR, snugT, snugB+       , snugX, snugY+       , snugCenterX, snugCenterY, snugCenterXY+          -- * Size          -- ** Computing size-       , width, height, size2D, sizeSpec2D-       , extentX, extentY, center2D+       , width, height+       , extentX, extentY           -- ** Specifying size-       , SizeSpec2D(..)-       , mkSizeSpec+       , mkSizeSpec2D+       , mkWidth+       , mkHeight+       , dims2D -         -- ** Adjusting size-       , sized, sizedAs+         -- * Textures+       , Texture(..), solid+       , SpreadMethod(..), GradientStop(..)+       , _FillTexture, fillTexture, _fillTexture, getFillTexture+       , _LineTexture, lineTexture, _lineTexture, lineTextureA, getLineTexture+       , stopFraction, stopColor, mkStops +       , LGradient(..), _LG, lGradStops, lGradTrans, lGradStart, lGradEnd+       , lGradSpreadMethod, defaultLG, mkLinearGradient++       , RGradient(..)+       , rGradStops, rGradCenter0, rGradRadius0, rGradCenter1, rGradRadius1+       , rGradTrans, rGradSpreadMethod, defaultRG, _RG, mkRadialGradient++         -- ** Colors+       , fillColor, _SC, _AC, fc, fcA, recommendFillColor+       , lineColor, lc, lcA+          -- * Visual aids for understanding the internal model        , showOrigin        , showOrigin'-       , OriginOpts(..)+       , OriginOpts(..), oColor, oScale, oMinSize+       , showEnvelope+       , showEnvelope'+       , EnvelopeOpts(..), eColor, eLineWidth, ePoints+       , showTrace+       , showTrace'+       , TraceOpts(..), tColor, tScale, tMinSize, tPoints        , showLabels         ) where  import           Diagrams.TwoD.Align import           Diagrams.TwoD.Arc+import           Diagrams.TwoD.Arrow+import           Diagrams.TwoD.Arrowheads+import           Diagrams.TwoD.Attributes import           Diagrams.TwoD.Combinators+import           Diagrams.TwoD.Deform import           Diagrams.TwoD.Ellipse import           Diagrams.TwoD.Image import           Diagrams.TwoD.Model
src/Diagrams/TwoD/Adjust.hs view
@@ -1,6 +1,11 @@-{-# LANGUAGE ViewPatterns-  #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types       #-}+{-# LANGUAGE TypeFamilies     #-} +{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Adjust@@ -14,105 +19,85 @@ -----------------------------------------------------------------------------  module Diagrams.TwoD.Adjust-    (-      setDefault2DAttributes-    , adjustDiaSize2D+    ( setDefault2DAttributes+    , adjustSize2D     , adjustDia2D-    , adjustSize     -- for backwards compatibility-    , requiredScale  -- re-exported for backwards compatibility     ) where -import Diagrams.Core+import           Diagrams.Attributes+import           Diagrams.BoundingBox+import           Diagrams.Core+import           Diagrams.Size+import           Diagrams.TwoD.Attributes (lineTextureA)+import           Diagrams.TwoD.Types+import           Diagrams.Util            (( # )) -import Diagrams.Attributes  (lineWidthA, lineColorA, lineCap-                            , lineJoin, lineMiterLimitA-                            )-import Diagrams.Util        ((#))+import           Control.Lens             (Lens', set, (^.))+import           Data.Default+import           Data.Semigroup -import Diagrams.TwoD.Types  (R2, p2)-import Diagrams.TwoD.Size   ( size2D, center2D, SizeSpec2D(..)-                            , requiredScaleT, requiredScale-                            )-import Diagrams.TwoD.Text   (fontSizeA) -import Data.AffineSpace     ((.-.))-import Data.Semigroup--import Data.Default.Class--import Data.Colour.Names    (black)- -- | Set default attributes of a 2D diagram (in case they have not --   been set): -----       * Line width 0.01------       * Line color black+--       * 'LineWidth':  0.01 -----       * Font size 1+--       * 'LineTexture': solid black -----       * Line cap LineCapButt+--       * 'LineCap': LineCapButt -----       * line join miter+--       * 'LineJoin': miter -----       * Miter limit 10-setDefault2DAttributes :: Semigroup m => QDiagram b R2 m -> QDiagram b R2 m-setDefault2DAttributes d = d # lineWidthA def # lineColorA def # fontSizeA def-                             # lineCap def # lineJoin def # lineMiterLimitA def+--       * 'MiterLimit': 10+setDefault2DAttributes :: (TypeableFloat n, Semigroup m)+  => QDiagram b V2 n m -> QDiagram b V2 n m+setDefault2DAttributes d+  = d # lineWidthM def+      # lineTextureA def+      # lineCap def+      # lineJoin def+      # lineMiterLimitA def  -- | Adjust the size and position of a 2D diagram to fit within the---   requested size. The first two arguments specify a method for---   extracting the requested output size from the rendering options,---   and a way of updating the rendering options with a new (more---   specific) size.-adjustDiaSize2D :: Monoid' m-                => (Options b R2 -> SizeSpec2D)-                -> (SizeSpec2D -> Options b R2 -> Options b R2)-                -> b -> Options b R2 -> QDiagram b R2 m-                -> (Options b R2, QDiagram b R2 m)-adjustDiaSize2D getSize setSize _ opts d =-  ( case spec of-       Dims _ _ -> opts-       _        -> setSize (uncurry Dims . scale s $ size) opts--  , d # scale s-      # translate tr-  )-  where spec = getSize opts-        size = size2D d-        s    = requiredScale spec size-        finalSz = case spec of-                    Dims w h -> (w,h)-                    _        -> scale s size-        tr = (0.5 *. p2 finalSz) .-. (s *. center2D d)+--   requested size. The first argument is a lens into the output+--   size contained in the rendering options.  Returns an updated+--   options record, any transformation applied to the diagram (the+--   inverse of which can be used, say, to translate output/device+--   coordinates back into local diagram coordinates), and the+--   modified diagram itself.+adjustSize2D+  :: (TypeableFloat n, Monoid' m)+  => Lens' (Options b V2 n) (SizeSpec V2 n)+  -> b+  -> Options b V2 n+  -> QDiagram b V2 n m+  -> (Options b V2 n, Transformation V2 n, QDiagram b V2 n m)+adjustSize2D szL _ opts d = (set szL spec opts, t, d # transform t)+  where+    spec    = dims sz+    (sz, t) = sizeAdjustment (opts ^. szL) (boundingBox d)  -- | @adjustDia2D@ provides a useful default implementation of --   the 'adjustDia' method from the 'Backend' type class. -----   As its first two arguments it requires a method for extracting---   the requested output size from the rendering options, and a way---   of updating the rendering options with a new (more specific) size.+--   As its first argument it requires a lens into the output size+--   contained in the rendering options. -- --   It then performs the following adjustments: -- --   * Set default attributes (see 'setDefault2DAttributes') -----   * Freeze the diagram in its final form--- --   * Scale and translate the diagram to fit within the requested --     size (see 'adjustDiaSize2D') -----   * Also return the actual adjusted size of the diagram.-adjustDia2D :: Monoid' m-            => (Options b R2 -> SizeSpec2D)-            -> (SizeSpec2D -> Options b R2 -> Options b R2)-            -> b -> Options b R2 -> QDiagram b R2 m-            -> (Options b R2, QDiagram b R2 m)-adjustDia2D getSize setSize b opts d-  = adjustDiaSize2D getSize setSize b opts (d # setDefault2DAttributes # freeze)+--   It returns an updated options record, any transformation applied+--   to the diagram (the inverse of which can be used, say, to+--   translate output/device coordinates back into local diagram+--   coordinates), and the modified diagram itself.+adjustDia2D :: (TypeableFloat n, Monoid' m)+            => Lens' (Options b V2 n) (SizeSpec V2 n)+            -> b -> Options b V2 n -> QDiagram b V2 n m+            -> (Options b V2 n, Transformation V2 n, QDiagram b V2 n m)+adjustDia2D szL b opts d+  = adjustSize2D szL b opts (d # setDefault2DAttributes) -{-# DEPRECATED adjustSize "Use Diagrams.TwoD.Size.requiredScaleT instead." #-}--- | Re-export 'requiredScaleT' with the name 'adjustSize' for---   backwards compatibility.-adjustSize :: SizeSpec2D -> (Double, Double) -> Transformation R2-adjustSize = requiredScaleT
src/Diagrams/TwoD/Align.hs view
@@ -1,6 +1,7 @@-{-# LANGUAGE FlexibleContexts-           , TypeFamilies-  #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies     #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Align@@ -12,86 +13,122 @@ -- "Diagrams.Align" for more general alignment combinators. -- -- The basic idea is that alignment is achieved by moving diagrams'--- local origins relative to their envelopes.  For example, to align--- several diagrams along their tops, we first move their local--- origins to the upper edge of their envelopes (using e.g. @map--- 'alignTop'@), and then put them together with their local origins--- along a horizontal line (using e.g. 'hcat' from--- "Diagrams.TwoD.Combinators").+-- local origins relative to their envelopes or traces (or some other+-- sort of boundary).  For example, to align several diagrams along+-- their tops, we first move their local origins to the upper edge of+-- their boundary (using e.g. @map 'alignTop'@), and then put them+-- together with their local origins along a horizontal line (using+-- e.g. 'hcat' from "Diagrams.TwoD.Combinators"). -- -----------------------------------------------------------------------------  module Diagrams.TwoD.Align     ( -- * Absolute alignment+      -- ** Align by envelope       alignL, alignR, alignT, alignB     , alignTL, alignTR, alignBL, alignBR +      -- ** Align by trace+    , snugL, snugR, snugT, snugB+       -- * Relative alignment-    , alignX, alignY+    , alignX, snugX, alignY, snugY        -- * Centering     , centerX, centerY, centerXY+    , snugCenterX, snugCenterY, snugCenterXY      ) where -import Diagrams.Core--import Diagrams.TwoD.Types-import Diagrams.TwoD.Vector-import Diagrams.Align+import           Diagrams.Core -import Data.VectorSpace+import           Diagrams.Align+import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Vector  -- | Align along the left edge, i.e. translate the diagram in a --   horizontal direction so that the local origin is on the left edge --   of the envelope.-alignL :: (Alignable a, V a ~ R2) => a -> a-alignL = align (negateV unitX)+alignL :: (InSpace V2 n a, Fractional n, Alignable a, HasOrigin a) => a -> a+alignL = align unit_X +snugL :: (InSpace V2 n a, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugL = snug unit_X+ -- | Align along the right edge.-alignR :: (Alignable a, V a ~ R2) => a -> a+alignR :: (InSpace V2 n a, Fractional n, Alignable a, HasOrigin a) => a -> a alignR = align unitX +snugR :: (InSpace V2 n a, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugR = snug unitX++ -- | Align along the top edge.-alignT :: (Alignable a, V a ~ R2) => a -> a+alignT :: (InSpace V2 n a, Fractional n, Alignable a, HasOrigin a) => a -> a alignT = align unitY +snugT:: (InSpace V2 n a, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugT = snug unitY+ -- | Align along the bottom edge.-alignB :: (Alignable a, V a ~ R2) => a -> a-alignB = align (negateV unitY)+alignB :: (InSpace V2 n a, Fractional n, Alignable a, HasOrigin a) => a -> a+alignB = align unit_Y -alignTL, alignTR, alignBL, alignBR :: (Alignable a, V a ~ R2) => a -> a+snugB :: (InSpace V2 n a, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugB = snug unit_Y++alignTL, alignTR, alignBL, alignBR :: (InSpace V2 n a, Fractional n, Alignable a, HasOrigin a) => a -> a alignTL = alignT . alignL alignTR = alignT . alignR alignBL = alignB . alignL alignBR = alignB . alignR --- | @alignX@ moves the local origin horizontally as follows:+-- | @alignX@ and @snugX@ move the local origin horizontally as follows: -----   * @alignX (-1)@ moves the local origin to the left edge of the envelope;+--   * @alignX (-1)@ moves the local origin to the left edge of the boundary; -- --   * @align 1@ moves the local origin to the right edge; -- --   * any other argument interpolates linearly between these.  For --     example, @alignX 0@ centers, @alignX 2@ moves the origin one --     \"radius\" to the right of the right edge, and so on.-alignX :: (Alignable a, V a ~ R2) => Double -> a -> a+--+--   * @snugX@ works the same way.++alignX :: (InSpace v n a, R1 v, Fractional n, Alignable a, HasOrigin a) => n -> a -> a alignX = alignBy unitX +-- | See the documentation for 'alignX'.+snugX :: (InSpace v n a, R1 v, Fractional n, Alignable a, Traced a, HasOrigin a) => n -> a -> a+snugX = snugBy unitX+ -- | Like 'alignX', but moving the local origin vertically, with an --   argument of @1@ corresponding to the top edge and @(-1)@ corresponding --   to the bottom edge.-alignY :: (Alignable a, V a ~ R2) => Double -> a -> a+alignY :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a) => n -> a -> a alignY = alignBy unitY +-- | See the documentation for 'alignY'.+snugY :: (InSpace v n a, R2 v, Fractional n, Alignable a, Traced a, HasOrigin a) => n -> a -> a+snugY = snugBy unitY+ -- | Center the local origin along the X-axis.-centerX  :: (Alignable a, V a ~ R2) => a -> a-centerX  = alignBy unitX 0+centerX :: (InSpace v n a, R1 v, Fractional n, Alignable a, HasOrigin a) => a -> a+centerX = alignBy unitX 0 +snugCenterX :: (InSpace v n a, R1 v, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugCenterX = snugBy unitX 0+ -- | Center the local origin along the Y-axis.-centerY  :: (Alignable a, V a ~ R2) => a -> a-centerY  = alignBy unitY 0+centerY :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a) => a -> a+centerY = alignBy unitY 0 +snugCenterY :: (InSpace v n a, R2 v, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugCenterY = snugBy unitY 0+ -- | Center along both the X- and Y-axes.-centerXY :: (Alignable a, V a ~ R2) => a -> a+centerXY :: (InSpace v n a, R2 v, Fractional n, Alignable a, HasOrigin a) => a -> a centerXY = centerX . centerY++snugCenterXY :: (InSpace v n a, R2 v, Fractional n, Alignable a, Traced a, HasOrigin a) => a -> a+snugCenterXY = snugCenterX . snugCenterY
src/Diagrams/TwoD/Arc.hs view
@@ -1,5 +1,12 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE ConstraintKinds  #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies     #-}+{-# LANGUAGE TypeOperators    #-}+{-# LANGUAGE ViewPatterns     #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Arc@@ -14,56 +21,63 @@ module Diagrams.TwoD.Arc     ( arc     , arc'-    , arcCW     , arcT++    , arcCCW+    , arcCW+     , bezierFromSweep      , wedge+    , arcBetween+    , annularWedge     ) where -import           Diagrams.Coordinates+import           Diagrams.Angle import           Diagrams.Core+import           Diagrams.Direction import           Diagrams.Located        (at)-import           Diagrams.Path import           Diagrams.Segment import           Diagrams.Trail import           Diagrams.TrailLike import           Diagrams.TwoD.Transform import           Diagrams.TwoD.Types-import           Diagrams.TwoD.Vector    (e, unitX)-import           Diagrams.Util           (tau, ( # ))+import           Diagrams.TwoD.Vector    (e, unitX, unitY, unit_Y)+import           Diagrams.Util           (( # )) -import           Data.Semigroup          ((<>))-import           Data.VectorSpace        (negateV, (*^), (^-^))+import           Control.Lens            ((&), (<>~), (^.))+import           Data.Semigroup +import           Linear.Affine+import           Linear.Metric+import           Linear.Vector+ -- For details of this approximation see: --   http://www.tinaja.com/glib/bezcirc2.pdf  -- | @bezierFromSweepQ1 s@ constructs a 'Cubic' segment that starts in---   the positive y direction and sweeps counterclockwise through @s@---   radians.  The approximation is only valid for angles in the first---   quadrant.-bezierFromSweepQ1 :: Rad -> Segment Closed R2-bezierFromSweepQ1 s = fmap (^-^ v) . rotate (s/2) $ bezier3 c2 c1 p0-  where p0@(coords -> x :& y) = rotate (s/2) v-        c1                    = ((4-x)/3)  &  ((1-x)*(3-x)/(3*y))-        c2                    = reflectY c1-        v                     = unitX+--  the positive y direction and sweeps counterclockwise through an+--  angle @s@.  The approximation is only valid for angles in the+--  first quadrant.+bezierFromSweepQ1 :: Floating n => Angle n -> Segment Closed V2 n+bezierFromSweepQ1 s = mapSegmentVectors (^-^ unitX) . rotate (s ^/ 2) $ bezier3 c2 c1 p0+  where p0@(V2 x y) = e (s ^/ 2)+        c1          = V2 ((4-x)/3) ((1-x)*(3-x)/(3*y))+        c2          = reflectY c1  -- | @bezierFromSweep s@ constructs a series of 'Cubic' segments that --   start in the positive y direction and sweep counter clockwise---   through @s@ radians.  If @s@ is negative, it will start in the+--   through the angle @s@.  If @s@ is negative, it will start in the --   negative y direction and sweep clockwise.  When @s@ is less than---   0.0001 the empty list results.  If the sweep is greater than tau---   then it is truncated to tau.-bezierFromSweep :: Rad -> [Segment Closed R2]+--   0.0001 the empty list results.  If the sweep is greater than @fullTurn@+--   later segments will overlap earlier segments.+bezierFromSweep :: OrderedField n => Angle n -> [Segment Closed V2 n] bezierFromSweep s-  | s > tau    = bezierFromSweep tau-  | s < 0      = fmap reflectY . bezierFromSweep $ (-s)-  | s < 0.0001 = []-  | s < tau/4  = [bezierFromSweepQ1 s]-  | otherwise  = bezierFromSweepQ1 (tau/4)-          : map (rotateBy (1/4)) (bezierFromSweep (max (s - tau/4) 0))+  | s < zero          = fmap reflectY . bezierFromSweep $ negated s+  | s < 0.0001 @@ rad = []+  | s < fullTurn^/4   = [bezierFromSweepQ1 s]+  | otherwise         = bezierFromSweepQ1 (fullTurn^/4)+          : map (rotateBy (1/4)) (bezierFromSweep (max (s ^-^ fullTurn^/4) zero))  {- ~~~~ Note [segment spacing]@@ -85,54 +99,124 @@ the approximation error. -} --- | Given a start angle @s@ and an end angle @e@, @'arcT' s e@ is the---   'Trail' of a radius one arc counterclockwise between the two angles.-arcT :: Angle a => a -> a -> Trail R2-arcT start end-    | e < s     = arcT s (e + fromIntegral d)-    | otherwise = (if sweep >= tau then glueTrail else id)-                $ trailFromSegments bs-  where sweep = convertAngle $ end - start-        bs    = map (rotate start) . bezierFromSweep $ sweep+-- | Given a start direction @d@ and a sweep angle @s@, @'arcT' d s@+--   is the 'Trail' of a radius one arc starting at @d@ and sweeping out+--   the angle @s@ counterclockwise (for positive s).  The resulting+--   @Trail@ is allowed to wrap around and overlap itself.+arcT :: OrderedField n => Direction V2 n -> Angle n -> Trail V2 n+arcT start sweep = trailFromSegments bs+  where+    bs = map (rotateTo start) . bezierFromSweep $ sweep -        -- We want to compare the start and the end and in case-        -- there isn't some law about 'Angle' ordering, we use a-        -- known 'Angle' for that.-        s = convertAngle start :: Turn-        e = convertAngle end-        d = ceiling (s - e) :: Integer+-- | Given a start direction @d@ and a sweep angle @s@, @'arc' d s@ is the+--   path of a radius one arc starting at @d@ and sweeping out the angle+--   @s@ counterclockwise (for positive s).  The resulting+--   @Trail@ is allowed to wrap around and overlap itself.+arc :: (InSpace V2 n t, OrderedField n, TrailLike t) => Direction V2 n -> Angle n -> t+arc start sweep = trailLike $ arcT start sweep `at` P (fromDirection start) --- | Given a start angle @s@ and an end angle @e@, @'arc' s e@ is the---   path of a radius one arc counterclockwise between the two angles.+-- | Given a radus @r@, a start direction @d@ and an angle @s@,+--   @'arc'' r d s@ is the path of a radius @(abs r)@ arc starting at+--   @d@ and sweeping out the angle @s@ counterclockwise (for positive+--   s).  The origin of the arc is its center.+--+--   <<diagrams/src_Diagrams_TwoD_Arc_arc'Ex.svg#diagram=arc'Ex&width=300>>+--+--   > arc'Ex = mconcat [ arc' r xDir (1/4 @@ turn) | r <- [0.5,-1,1.5] ]+--   >        # centerXY # pad 1.1+arc' :: (InSpace V2 n t, OrderedField n, TrailLike t) => n -> Direction V2 n -> Angle n -> t+arc' (abs -> r) start sweep = trailLike $ scale r ts `at` P (r *^ fromDirection start)+  where ts = arcT start sweep++arcCCWT :: RealFloat n => Direction V2 n -> Direction V2 n -> Trail V2 n+arcCCWT start end = trailFromSegments bs+  where+    bs    = map (rotateTo start) . bezierFromSweep $ sweep+    sweep = normalizeAngle $ end ^. _theta ^-^ start ^. _theta++-- | Given a start direction @s@ and end direction @e@, @arcCCW s e@ is the+--   path of a radius one arc counterclockwise between the two directions. --   The origin of the arc is its center.-arc :: (Angle a, TrailLike t, V t ~ R2) => a -> a -> t-arc start end = trailLike $ arcT start end `at` (rotate start $ p2 (1,0))+arcCCW :: (InSpace V2 n t, RealFloat n, TrailLike t) => Direction V2 n -> Direction V2 n -> t+arcCCW start end = trailLike $ arcCCWT start end `at` P (fromDirection start) --- | Like 'arc' but clockwise.-arcCW :: (Angle a, TrailLike t, V t ~ R2) => a -> a -> t+-- | Like 'arcAngleCCW' but clockwise.+arcCW :: (InSpace V2 n t, RealFloat n, TrailLike t) => Direction V2 n -> Direction V2 n -> t arcCW start end = trailLike $-                            -- flipped arguments to get the path we want-                            -- then reverse the trail to get the cw direction.-                            (reverseTrail $ arcT end start)-                            `at`-                            (rotate start $ p2 (1,0))-                   -- We could just have `arcCW = reversePath . flip arc`-                   -- but that wouldn't be `TrailLike`.---- | Given a radus @r@, a start angle @s@ and an end angle @e@,---   @'arc'' r s e@ is the path of a radius @(abs r)@ arc between---   the two angles.  If a negative radius is given, the arc will---   be clockwise, otherwise it will be counterclockwise. The origin---   of the arc is its center.-arc' :: (Angle a, TrailLike p, V p ~ R2) => Double -> a -> a -> p-arc' r start end = trailLike $ scale (abs r) ts `at` (rotate start $ p2 (abs r,0))-  where ts | r < 0     = reverseTrail $ arcT end start-           | otherwise = arcT start end+  -- flipped arguments to get the path we want+  -- then reverse the trail to get the cw direction.+  reverseTrail (arcCCWT end start) `at` P (fromDirection start)  -- | Create a circular wedge of the given radius, beginning at the---   first angle and extending counterclockwise to the second.-wedge :: (Angle a, TrailLike p, V p ~ R2) => Double -> a -> a -> p-wedge r a1 a2 = trailLike . (`at` origin) . wrapLine-              $ fromOffsets [r *^ e a1]-                <> arc a1 a2 # scale r-                <> fromOffsets [r *^ negateV (e a2)]+--   given direction and extending through the given angle.+--+--   <<diagrams/src_Diagrams_TwoD_Arc_wedgeEx.svg#diagram=wedgeEx&width=400>>+--+--   > wedgeEx = hcat' (with & sep .~ 0.5)+--   >   [ wedge 1 xDir (1/4 @@ turn)+--   >   , wedge 1 (rotate (7/30 @@ turn) xDir) (4/30 @@ turn)+--   >   , wedge 1 (rotate (1/8 @@ turn) xDir) (3/4 @@ turn)+--   >   ]+--   >   # fc blue+--   >   # centerXY # pad 1.1+wedge :: (InSpace V2 n t, OrderedField n, TrailLike t) => n -> Direction V2 n -> Angle n -> t+wedge r d s = trailLike . (`at` origin) . glueTrail . wrapLine+              $ fromOffsets [r *^ fromDirection d]+                <> arc d s # scale r+                <> fromOffsets [r *^ negated (rotate s $ fromDirection d)]++-- | @arcBetween p q height@ creates an arc beginning at @p@ and+--   ending at @q@, with its midpoint at a distance of @abs height@+--   away from the straight line from @p@ to @q@.  A positive value of+--   @height@ results in an arc to the left of the line from @p@ to+--   @q@; a negative value yields one to the right.+--+--   <<diagrams/src_Diagrams_TwoD_Arc_arcBetweenEx.svg#diagram=arcBetweenEx&width=300>>+--+--   > arcBetweenEx = mconcat+--   >   [ arcBetween origin (p2 (2,1)) ht | ht <- [-0.2, -0.1 .. 0.2] ]+--   >   # centerXY # pad 1.1+arcBetween :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) => Point V2 n -> Point V2 n -> n -> t+arcBetween p q ht = trailLike (a # rotate (v^._theta) # moveTo p)+  where+    h = abs ht+    isStraight = h < 0.00001+    v = q .-. p+    d = norm (q .-. p)+    th  = acosA ((d*d - 4*h*h)/(d*d + 4*h*h))+    r = d/(2*sinA th)+    mid | ht >= 0    = direction unitY+        | otherwise  = direction unit_Y+    st  = mid & _theta <>~ negated th+    a | isStraight+      = fromOffsets [d *^ unitX]+      | otherwise+      = arc st (2 *^ th)+        # scale r+        # translateY ((if ht > 0 then negate else id) (r - h))+        # translateX (d/2)+        # (if ht > 0 then reverseLocTrail else id)++-- | Create an annular wedge of the given radii, beginning at the+--   first direction and extending through the given sweep angle.+--   The radius of the outer circle is given first.+--+--   <<diagrams/src_Diagrams_TwoD_Arc_annularWedgeEx.svg#diagram=annularWedgeEx&width=400>>+--+--   > annularWedgeEx = hsep 0.50+--   >   [ annularWedge 1 0.5 xDir (1/4 @@ turn)+--   >   , annularWedge 1 0.3 (rotate (7/30 @@ turn) xDir) (4/30 @@ turn)+--   >   , annularWedge 1 0.7 (rotate (1/8 @@ turn) xDir) (3/4 @@ turn)+--   >   ]+--   >   # fc blue+--   >   # centerXY # pad 1.1+annularWedge :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) =>+                n -> n -> Direction V2 n -> Angle n -> t+annularWedge r1' r2' d1 s = trailLike . (`at` o) . glueTrail . wrapLine+              $ fromOffsets [(r1' - r2') *^ fromDirection d1]+                <> arc d1 s # scale r1'+                <> fromOffsets [(r1' - r2') *^ negated (fromDirection d2)]+                <> arc d2 (negated s) # scale r2'+  where o = origin # translate (r2' *^ fromDirection d1)+        d2 = d1 & _theta <>~ s+
+ src/Diagrams/TwoD/Arrow.hs view
@@ -0,0 +1,588 @@+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TemplateHaskell            #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE UndecidableInstances       #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.TwoD.Arrow+-- Copyright   :  (c) 2013-2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Drawing arrows in two dimensions.  For a tutorial on drawing arrows+-- using this module, see the diagrams website:+-- <https://diagrams.github.io/doc/arrow.html>.+--+-----------------------------------------------------------------------------+++module Diagrams.TwoD.Arrow+       ( -- * Examples+         -- ** Example 1+-- | <<diagrams/src_Diagrams_TwoD_Arrow_example1.svg#diagram=example1&width=500>>+--+--   > -- Connecting two diagrams at their origins.+--   >+--   > sq = square 2 # showOrigin # lc darkgray # lw ultraThick+--   > ds = (sq # named "left") ||| strutX 3 ||| (sq # named "right")+--   >+--   > shaft  = cubicSpline False ( map p2 [(0, 0), (1, 0), (1, 0.2), (2, 0.2)])+--   >+--   > example1 = ds # connect' (with & arrowHead .~ dart & arrowTail .~ quill+--   >                                & arrowShaft .~ shaft+--   >                                & headLength .~ huge & tailLength .~ veryLarge)+--   >                                "left" "right" # pad 1.1++         -- ** Example 2++-- | <<diagrams/src_Diagrams_TwoD_Arrow_example2.svg#diagram=example2&width=500>>+--+--   > -- Comparing connect, connectPerim, and arrowAt.+--   >+--   > oct  = octagon 1 # lc darkgray # lw ultraThick # showOrigin+--   > dias = oct # named "first" ||| strut 3 ||| oct # named "second"+--   >+--   > -- Connect two diagrams and two points on their trails.+--   > ex12 = dias # connect' (with & lengths .~ veryLarge) "first" "second"+--   >             # connectPerim' (with & lengths .~ veryLarge)+--   >        "first" "second" (15/16 @@ turn) (9/16 @@ turn)+--   >+--   > -- Place an arrow at (0,0) the size and direction of (0,1).+--   > ex3 = arrowAt origin unit_Y+--   >+--   > example2 = (ex12 <> ex3) # centerXY # pad 1.1++         -- * Creating arrows+         arrowV+       , arrowV'+       , arrowAt+       , arrowAt'+       , arrowBetween+       , arrowBetween'+       , connect+       , connect'+       , connectPerim+       , connectPerim'+       , connectOutside+       , connectOutside'++       , arrow+       , arrow'++       , arrowFromLocatedTrail+       , arrowFromLocatedTrail'++         -- * Options+       , ArrowOpts(..)++       , arrowHead+       , arrowTail+       , arrowShaft+       , headGap+       , tailGap+       , gaps, gap+       , headTexture+       , headStyle+       , headLength+       , tailTexture+       , tailStyle+       , tailLength+       , lengths+       , shaftTexture+       , shaftStyle+       , straightShaft++         -- | See "Diagrams.TwoD.Arrowheads" for a list of standard+         --   arrowheads and help creating your own.+       , module Diagrams.TwoD.Arrowheads+       ) where++import           Control.Lens              (Lens', Traversal',+                                            generateSignatures, lensRules,+                                            makeLensesWith, view, (%~), (&),+                                            (.~), (^.))+import           Data.Default+import           Data.Maybe                (fromMaybe)+import           Data.Monoid.Coproduct     (untangle)+import           Data.Semigroup+import           Data.Typeable++import           Data.Colour               hiding (atop)+import           Diagrams.Core+import           Diagrams.Core.Style       (unmeasureAttrs)+import           Diagrams.Core.Types       (QDiaLeaf (..), mkQD')++import           Diagrams.Angle+import           Diagrams.Attributes+import           Diagrams.Direction        hiding (dir)+import           Diagrams.Located          (Located (..), unLoc)+import           Diagrams.Parametric+import           Diagrams.Path+import           Diagrams.Solve.Polynomial (quadForm)+import           Diagrams.Tangent          (tangentAtEnd, tangentAtStart)+import           Diagrams.Trail+import           Diagrams.TwoD.Arrowheads+import           Diagrams.TwoD.Attributes+import           Diagrams.TwoD.Path        (stroke, strokeT)+import           Diagrams.TwoD.Transform   (reflectY, translateX)+import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Vector      (unitX, unit_X)+import           Diagrams.Util             (( # ))++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector+++data ArrowOpts n+  = ArrowOpts+    { _arrowHead  :: ArrowHT n+    , _arrowTail  :: ArrowHT n+    , _arrowShaft :: Trail V2 n+    , _headGap    :: Measure n+    , _tailGap    :: Measure n+    , _headStyle  :: Style V2 n+    , _headLength :: Measure n+    , _tailStyle  :: Style V2 n+    , _tailLength :: Measure n+    , _shaftStyle :: Style V2 n+    }++-- | Straight line arrow shaft.+straightShaft :: OrderedField n => Trail V2 n+straightShaft = trailFromOffsets [unitX]++instance TypeableFloat n => Default (ArrowOpts n) where+  def = ArrowOpts+        { _arrowHead    = dart+        , _arrowTail    = noTail+        , _arrowShaft   = straightShaft+        , _headGap      = none+        , _tailGap      = none++        -- See note [Default arrow style attributes]+        , _headStyle    = mempty+        , _headLength   = normal+        , _tailStyle    = mempty+        , _tailLength   = normal+        , _shaftStyle   = mempty+        }++makeLensesWith (lensRules & generateSignatures .~ False) ''ArrowOpts++-- | A shape to place at the head of the arrow.+arrowHead :: Lens' (ArrowOpts n) (ArrowHT n)++-- | A shape to place at the tail of the arrow.+arrowTail :: Lens' (ArrowOpts n) (ArrowHT n)++-- | The trail to use for the arrow shaft.+arrowShaft :: Lens' (ArrowOpts n) (Trail V2 n)++-- | Distance to leave between the head and the target point.+headGap :: Lens' (ArrowOpts n) (Measure n)++-- | Distance to leave between the starting point and the tail.+tailGap :: Lens' (ArrowOpts n) (Measure n)++-- | Set both the @headGap@ and @tailGap@ simultaneously.+gaps :: Traversal' (ArrowOpts n) (Measure n)+gaps f opts = (\h t -> opts & headGap .~ h & tailGap .~ t)+        <$> f (opts ^. headGap)+        <*> f (opts ^. tailGap)++-- | Same as gaps, provided for backward compatiiblity.+gap :: Traversal' (ArrowOpts n) (Measure n)+gap = gaps++-- | Style to apply to the head. @headStyle@ is modified by using the lens+--   combinator @%~@ to change the current style. For example, to change+--   an opaque black arrowhead to translucent orange:+--   @(with & headStyle %~ fc orange .  opacity 0.75)@.+headStyle :: Lens' (ArrowOpts n) (Style V2 n)++-- | Style to apply to the tail. See `headStyle`.+tailStyle :: Lens' (ArrowOpts n) (Style V2 n)++-- | Style to apply to the shaft. See `headStyle`.+shaftStyle :: Lens' (ArrowOpts n) (Style V2 n)++-- | The length from the start of the joint to the tip of the head.+headLength :: Lens' (ArrowOpts n) (Measure n)++-- | The length of the tail plus its joint.+tailLength :: Lens' (ArrowOpts n) (Measure n)++-- | Set both the @headLength@ and @tailLength@ simultaneously.+lengths :: Traversal' (ArrowOpts n) (Measure n)+lengths f opts =+  (\h t -> opts & headLength .~ h & tailLength .~ t)+    <$> f (opts ^. headLength)+    <*> f (opts ^. tailLength)++-- | A lens for setting or modifying the texture of an arrowhead. For+--   example, one may write @... (with & headTexture .~ grad)@ to get an+--   arrow with a head filled with a gradient, assuming grad has been+--   defined. Or @... (with & headTexture .~ solid blue@ to set the head+--   color to blue. For more general control over the style of arrowheads,+--   see 'headStyle'.+headTexture :: TypeableFloat n => Lens' (ArrowOpts n) (Texture n)+headTexture = headStyle . _fillTexture++-- | A lens for setting or modifying the texture of an arrow+--   tail. This is *not* a valid lens (see 'committed').+tailTexture :: TypeableFloat n => Lens' (ArrowOpts n) (Texture n)+tailTexture = tailStyle . _fillTexture++-- | A lens for setting or modifying the texture of an arrow+--   shaft.+shaftTexture :: TypeableFloat n => Lens' (ArrowOpts n) (Texture n)+shaftTexture = shaftStyle . _lineTexture++-- Set the default shaft style of an `ArrowOpts` record by applying the+-- default style after all other styles have been applied.+-- The semigroup stucture of the lw attribute will insure that the default+-- is only used if it has not been set in @opts@.+shaftSty :: ArrowOpts n -> Style V2 n+shaftSty opts = opts^.shaftStyle++-- Set the default head style. See `shaftSty`.+headSty :: TypeableFloat n => ArrowOpts n -> Style V2 n+headSty opts = fc black (opts^.headStyle)++-- Set the default tail style. See `shaftSty`.+tailSty :: TypeableFloat n => ArrowOpts n -> Style V2 n+tailSty opts = fc black (opts^.tailStyle)++-- | Calculate the length of the portion of the horizontal line that passes+--   through the origin and is inside of p.+xWidth :: Floating n => (Traced t, V t ~ V2, N t ~ n) => t -> n+xWidth p = a + b+  where+    a = fromMaybe 0 (norm <$> traceV origin unitX p)+    b = fromMaybe 0 (norm <$> traceV origin unit_X p)++-- | Get the line color from the shaft to use as the fill color for the joint.+--   And set the opacity of the shaft to the current opacity.+colorJoint :: TypeableFloat n => Style V2 n -> Style V2 n+colorJoint sStyle =+  let c = fmap getLineTexture . getAttr $ sStyle+      o = fmap getOpacity . getAttr $ sStyle+  in+  case (c, o) of+      (Nothing, Nothing) -> fillColor black mempty+      (Just t, Nothing)  -> fillTexture t mempty+      (Nothing, Just o') -> opacity o' . fillColor black $ mempty+      (Just t, Just o')  -> opacity o' . fillTexture t $ mempty++-- | Get line width from a style.+widthOfJoint :: forall n. TypeableFloat n => Style V2 n -> n -> n -> n+widthOfJoint sStyle gToO nToO =+  fromMaybe+    (fromMeasured gToO nToO medium) -- should be same as default line width+    (fmap getLineWidth . getAttr . unmeasureAttrs gToO nToO $ sStyle)++-- | Combine the head and its joint into a single scale invariant diagram+--   and move the origin to the attachment point. Return the diagram+--   and its width.+mkHead :: (TypeableFloat n, Renderable (Path V2 n) b) =>+          n -> ArrowOpts n -> n -> n -> Bool -> (QDiagram b V2 n Any, n)+mkHead = mkHT unit_X arrowHead headSty++mkTail :: (TypeableFloat n, Renderable (Path V2 n) b) =>+          n -> ArrowOpts n -> n -> n -> Bool -> (QDiagram b V2 n Any, n)+mkTail = mkHT unitX arrowTail tailSty++mkHT+  :: (TypeableFloat n, Renderable (Path V2 n) b)+  => V2 n -> Lens' (ArrowOpts n) (ArrowHT n) -> (ArrowOpts n -> Style V2 n)+  -> n -> ArrowOpts n -> n -> n -> Bool -> (QDiagram b V2 n Any, n)+mkHT xDir htProj styProj sz opts gToO nToO reflect+    = ( (j <> ht)+        # (if reflect then reflectY else id)+        # moveOriginBy (jWidth *^ xDir) # lwO 0+      , htWidth + jWidth+      )+  where+    (ht', j') = (opts^.htProj) sz+                (widthOfJoint (shaftSty opts) gToO nToO)+    htWidth = xWidth ht'+    jWidth  = xWidth j'+    ht = stroke ht' # applyStyle (styProj opts)+    j  = stroke j'  # applyStyle (colorJoint (opts^.shaftStyle))++-- | @spine tr tw hw sz@ makes a trail with the same angles and offset+--   as an arrow with tail width @t@w, head width @hw@ and shaft @tr@,+--   such that the magnitude of the shaft offset is @sz@. Used for+--   calculating the offset of an arrow.+spine :: TypeableFloat n => Trail V2 n -> n -> n -> n -> Trail V2 n+spine tr tw hw sz = tS <> tr # scale sz <> hS+  where+    tSpine = trailFromOffsets [signorm . tangentAtStart $ tr] # scale tw+    hSpine = trailFromOffsets [signorm . tangentAtEnd $ tr] # scale hw+    hS = if hw > 0 then hSpine else mempty+    tS = if tw > 0 then tSpine else mempty++-- | @scaleFactor tr tw hw t@ calculates the amount required to scale+--   a shaft trail @tr@ so that an arrow with head width @hw@ and tail+--   width @tw@ has offset @t@.+scaleFactor :: TypeableFloat n => Trail V2 n -> n -> n -> n -> n+scaleFactor tr tw hw t++  -- Let tv be a vector representing the tail width, i.e. a vector+  -- of length tw tangent to the trail's start; similarly for hv.+  -- Let v be the vector offset of the trail.+  --+  -- Then we want to find k such that+  --+  --   || tv + k*v + hv || = t.+  --+  -- We can solve by squaring both sides and expanding the LHS as a+  -- dot product, resulting in a quadratic in k.++  = case quadForm+             (quadrance v)+             (2* (v `dot` (tv ^+^ hv)))+             (quadrance (tv ^+^ hv) - t*t)+    of+      []  -> 1   -- no scale works, just return 1+      [s] -> s   -- single solution+      ss  -> maximum ss+        -- we will usually get both a positive and a negative solution;+        -- return the maximum (i.e. positive) solution+  where+    tv = tw *^ (tangentAtStart tr # signorm)+    hv = hw *^ (tangentAtEnd   tr # signorm)+    v  = trailOffset tr++-- Calculate the approximate envelope of a horizontal arrow+-- as if the arrow were made only of a shaft.+arrowEnv :: TypeableFloat n => ArrowOpts n -> n -> Envelope V2 n+arrowEnv opts len = getEnvelope horizShaft+  where+    horizShaft = shaft # rotate (negated (v ^. _theta)) # scale (len / m)+    m = norm v+    v = trailOffset shaft+    shaft = opts ^. arrowShaft++-- | @arrow len@ creates an arrow of length @len@ with default+--   parameters, starting at the origin and ending at the point+--   @(len,0)@.+arrow :: (TypeableFloat n, Renderable (Path V2 n) b) => n -> QDiagram b V2 n Any+arrow = arrow' def++-- | @arrow' opts len@ creates an arrow of length @len@ using the+--   given options, starting at the origin and ending at the point+--   @(len,0)@.  In particular, it scales the given 'arrowShaft' so+--   that the entire arrow has length @len@.+arrow' :: (TypeableFloat n, Renderable (Path V2 n) b) => ArrowOpts n -> n -> QDiagram b V2 n Any+arrow' opts len = mkQD' (DelayedLeaf delayedArrow)++      -- Currently we approximate the envelope of an arrow by using the+      -- envelope of its shaft (see 'arrowEnv'). The trace of an arrow is empty.+      (arrowEnv opts len) mempty mempty mempty++  where++    -- Once we learn the global transformation context (da) and the two scale+    -- factors, normal to output (n) and global to output (g), this arrow is+    -- drawn in, we can apply it to the origin and (len,0) to find out+    -- the actual final points between which this arrow should be+    -- drawn.  We need to know this to draw it correctly, since the+    -- head and tail are scale invariant, and hence the precise points+    -- between which we need to draw the shaft do not transform+    -- uniformly as the transformation applied to the entire arrow.+    -- See https://github.com/diagrams/diagrams-lib/issues/112.+    delayedArrow da g n =+      let (trans, globalSty) = maybe mempty untangle . fst $ da+      in  dArrow globalSty trans len g n++    -- Build an arrow and set its endpoints to the image under tr of origin and (len,0).+    dArrow sty tr ln gToO nToO = (h' <> t' <> shaft)+               # moveOriginBy (tWidth *^ (unit_X # rotate tAngle))+               # rotate (((q .-. p)^._theta) ^-^ (dir^._theta))+               # moveTo p+      where++        p = origin # transform tr+        q = origin # translateX ln # transform tr++        -- Use the existing line color for head, tail, and shaft by+        -- default (can be overridden by explicitly setting headStyle,+        -- tailStyle, or shaftStyle).  Also use existing global line width+        -- for shaft if not explicitly set in shaftStyle.+        globalLC = getLineTexture <$> getAttr sty+        opts' = opts+          & headStyle  %~ maybe id fillTexture globalLC+          & tailStyle  %~ maybe id fillTexture globalLC+          & shaftStyle %~ applyStyle sty . transform tr++        -- The head size, tail size, head gap, and tail gap are obtained+        -- from the style and converted to output units.+        scaleFromMeasure = fromMeasured gToO nToO . scaleLocal (avgScale tr)+        hSize = scaleFromMeasure $ opts ^. headLength+        tSize = scaleFromMeasure $ opts ^. tailLength+        hGap  = scaleFromMeasure $ opts ^. headGap+        tGap  = scaleFromMeasure $ opts ^. tailGap++        -- Make the head and tail and save their widths.+        (h, hWidth') = mkHead hSize opts' gToO nToO (isReflection tr)+        (t, tWidth') = mkTail tSize opts' gToO nToO (isReflection tr)++        rawShaftTrail = opts^.arrowShaft+        shaftTrail+          = rawShaftTrail+            -- rotate it so it is pointing in the positive X direction+          # rotate (negated . view _theta . trailOffset $ rawShaftTrail)+            -- apply the context transformation -- in case it includes+            -- things like flips and shears (the possibility of shears+            -- is why we must rotate it to a neutral position first)+          # transform tr++        -- Adjust the head width and tail width to take gaps into account+        tWidth = tWidth' + tGap+        hWidth = hWidth' + hGap++        -- Calculate the angles that the head and tail should point.+        tAngle = tangentAtStart shaftTrail ^. _theta+        hAngle = tangentAtEnd shaftTrail ^. _theta++        -- Calculte the scaling factor to apply to the shaft shaftTrail so that the entire+        -- arrow will be of length len. Then apply it to the shaft and make the+        -- shaft into a Diagram with using its style.+        sf = scaleFactor shaftTrail tWidth hWidth (norm (q .-. p))+        shaftTrail' = shaftTrail # scale sf+        shaft = strokeT shaftTrail' # applyStyle (shaftSty opts')++        -- Adjust the head and tail to point in the directions of the shaft ends.+        h' = h # rotate hAngle+               # moveTo (origin .+^ shaftTrail' `atParam` domainUpper shaftTrail')+        t' = t # rotate tAngle++        -- Find out what direction the arrow is pointing so we can set it back+        -- to point in the direction unitX when we are done.+        dir = direction (trailOffset $ spine shaftTrail tWidth hWidth sf)++-- | @arrowBetween s e@ creates an arrow pointing from @s@ to @e@+--   with default parameters.+arrowBetween :: (TypeableFloat n, Renderable (Path V2 n) b) => Point V2 n -> Point V2 n -> QDiagram b V2 n Any+arrowBetween = arrowBetween' def++-- | @arrowBetween' opts s e@ creates an arrow pointing from @s@ to+--   @e@ using the given options.  In particular, it scales and+--   rotates @arrowShaft@ to go between @s@ and @e@, taking head,+--   tail, and gaps into account.+arrowBetween'+  :: (TypeableFloat n, Renderable (Path V2 n) b) =>+     ArrowOpts n -> Point V2 n -> Point V2 n -> QDiagram b V2 n Any+arrowBetween' opts s e = arrowAt' opts s (e .-. s)++-- | Create an arrow starting at s with length and direction determined by+--   the vector v.+arrowAt :: (TypeableFloat n, Renderable (Path V2 n) b) => Point V2 n -> V2 n -> QDiagram b V2 n Any+arrowAt = arrowAt' def++arrowAt'+  :: (TypeableFloat n, Renderable (Path V2 n) b) =>+     ArrowOpts n -> Point V2 n -> V2 n -> QDiagram b V2 n Any+arrowAt' opts s v = arrow' opts len+                  # rotate dir # moveTo s+  where+    len = norm v+    dir = v ^. _theta++-- | @arrowV v@ creates an arrow with the direction and norm of+--   the vector @v@ (with its tail at the origin), using default+--   parameters.+arrowV :: (TypeableFloat n, Renderable (Path V2 n) b) => V2 n -> QDiagram b V2 n Any+arrowV = arrowV' def++-- | @arrowV' v@ creates an arrow with the direction and norm of+--   the vector @v@ (with its tail at the origin).+arrowV'+  :: (TypeableFloat n, Renderable (Path V2 n) b)+  => ArrowOpts n -> V2 n -> QDiagram b V2 n Any+arrowV' opts = arrowAt' opts origin++-- | Turn a located trail into a default arrow by putting an+--   arrowhead at the end of the trail.+arrowFromLocatedTrail+  :: (Renderable (Path V2 n) b, RealFloat n, Typeable n)+  => Located (Trail V2 n) -> QDiagram b V2 n Any+arrowFromLocatedTrail = arrowFromLocatedTrail' def++-- | Turn a located trail into an arrow using the given options.+arrowFromLocatedTrail'+  :: (Renderable (Path V2 n) b, RealFloat n, Typeable n)+  => ArrowOpts n -> Located (Trail V2 n) -> QDiagram b V2 n Any+arrowFromLocatedTrail' opts trail = arrowBetween' opts' start end+  where+    opts' = opts & arrowShaft .~ unLoc trail+    start = atStart trail+    end   = atEnd trail++-- | Connect two diagrams with a straight arrow.+connect+  :: (TypeableFloat n, Renderable (Path V2 n) b, IsName n1, IsName n2)+  => n1 -> n2 -> QDiagram b V2 n Any -> QDiagram b V2 n Any+connect = connect' def++-- | Connect two diagrams with an arbitrary arrow.+connect'+  :: (TypeableFloat n, Renderable (Path V2 n) b, IsName n1, IsName n2)+  => ArrowOpts n -> n1 -> n2 -> QDiagram b V2 n Any -> QDiagram b V2 n Any+connect' opts n1 n2 =+  withName n1 $ \sub1 ->+  withName n2 $ \sub2 ->+    let [s,e] = map location [sub1, sub2]+    in  atop (arrowBetween' opts s e)++-- | Connect two diagrams at point on the perimeter of the diagrams, choosen+--   by angle.+connectPerim+  :: (TypeableFloat n, Renderable (Path V2 n) b, IsName n1, IsName n2)+ => n1 -> n2 -> Angle n -> Angle n+  -> QDiagram b V2 n Any -> QDiagram b V2 n Any+connectPerim = connectPerim' def++connectPerim'+  :: (TypeableFloat n, Renderable (Path V2 n) b, IsName n1, IsName n2)+  => ArrowOpts n -> n1 -> n2 -> Angle n -> Angle n+  -> QDiagram b V2 n Any -> QDiagram b V2 n Any+connectPerim' opts n1 n2 a1 a2 =+  withName n1 $ \sub1 ->+  withName n2 $ \sub2 ->+    let [os, oe] = map location [sub1, sub2]+        s = fromMaybe os (maxTraceP os (unitX # rotate a1) sub1)+        e = fromMaybe oe (maxTraceP oe (unitX # rotate a2) sub2)+    in  atop (arrowBetween' opts s e)++-- | Draw an arrow from diagram named "n1" to diagram named "n2".  The+--   arrow lies on the line between the centres of the diagrams, but is+--   drawn so that it stops at the boundaries of the diagrams, using traces+--   to find the intersection points.+connectOutside+  :: (TypeableFloat n, Renderable (Path V2 n) b, IsName n1, IsName n2)+  => n1 -> n2 -> QDiagram b V2 n Any -> QDiagram b V2 n Any+connectOutside = connectOutside' def++connectOutside'+  :: (TypeableFloat n, Renderable (Path V2 n) b, IsName n1, IsName n2)+  => ArrowOpts n -> n1 -> n2 -> QDiagram b V2 n Any -> QDiagram b V2 n Any+connectOutside' opts n1 n2 =+  withName n1 $ \b1 ->+  withName n2 $ \b2 ->+    let v = location b2 .-. location b1+        midpoint = location b1 .+^ (v ^/ 2)+        s' = fromMaybe (location b1) $ traceP midpoint (negated v) b1+        e' = fromMaybe (location b2) $ traceP midpoint v b2+    in+      atop (arrowBetween' opts s' e')
+ src/Diagrams/TwoD/Arrowheads.hs view
@@ -0,0 +1,346 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts          #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE ScopedTypeVariables       #-}+{-# LANGUAGE TypeFamilies              #-}+{-# OPTIONS_GHC -fno-warn-unused-imports       #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.TwoD.Arrowheads+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Standard arrowheads and tails. Each arrowhead or tail is designed+-- to be drawn filled, with a line width of 0, and is normalized to+-- fit inside a circle of diameter 1.+--+-----------------------------------------------------------------------------++module Diagrams.TwoD.Arrowheads+       (+       -- * Arrowheads+       -- ** Standard arrowheads+         tri+       , dart+       , halfDart+       , spike+       , thorn+       , lineHead+       , noHead++       -- ** Configurable arrowheads+       -- | Creates arrowheads of the same shape as the standard heads but+       --   where the angle parameter is used to specify the angle to the top+       --   left point of the arrowhead.+       , arrowheadTriangle+       , arrowheadDart+       , arrowheadHalfDart+       , arrowheadSpike+       , arrowheadThorn++       -- * Arrow tails+       -- ** Standard arrow tails+       , tri'+       , dart'+       , halfDart'+       , spike'+       , thorn'+       , lineTail+       , noTail+       , quill+       , block++       -- ** Configurable arrow tails++       , arrowtailQuill+       , arrowtailBlock++       -- * Internals+       , ArrowHT+       ) where++import           Control.Lens            ((&), (.~), (<>~), (^.))+import           Data.Default+import           Data.Monoid             (mempty, (<>))++import           Diagrams.Angle+import           Diagrams.Core++import           Diagrams.Path+import           Diagrams.Segment+import           Diagrams.Trail+import           Diagrams.TrailLike      (fromOffsets)+import           Diagrams.TwoD.Align+import           Diagrams.TwoD.Arc       (arc')+import           Diagrams.TwoD.Path      ()+import           Diagrams.TwoD.Polygons+import           Diagrams.TwoD.Shapes+import           Diagrams.TwoD.Transform+import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Vector    (unitX, unit_X, xDir)+import           Diagrams.Util           (( # ))++import           Linear.Affine+import           Linear.Metric+import           Linear.Vector++-----------------------------------------------------------------------------++type ArrowHT n = n -> n -> (Path V2 n, Path V2 n)++closedPath :: OrderedField n => Trail V2 n -> Path V2 n+closedPath = pathFromTrail . closeTrail++-- Heads ------------------------------------------------------------------+--   > drawHead h = arrowAt' (with & arrowHead .~ h & shaftStyle %~ lw none+--   >                             & headLength .~ local 0.5)+--   >         origin (r2 (1, 0))+--   >      <> square 0.5 # alignL # lw none # frame 0.1++-- | Isoceles triangle style. The above example specifies an angle of `2/5 Turn`.++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_tri25Ex.svg#diagram=tri25Ex&width=120>>++--   > tri25Ex = arrowAt' (with & arrowHead .~ arrowheadTriangle (2/5 @@ turn)+--   >                          & shaftStyle %~ lw none & headLength .~ local 0.5)+--   >           origin (r2 (0.5, 0))+--   >        <> square 0.6 # alignL # lw none # frame 0.1+arrowheadTriangle :: RealFloat n => Angle n -> ArrowHT n+arrowheadTriangle theta = aHead+  where+    aHead len _ = (p, mempty)+      where+        psi = pi - (theta ^. rad)+        r = len / (1 + cos psi)+        p = polygon (def & polyType .~ PolyPolar [theta, (-2) *^ theta]+            (repeat r) & polyOrient .~ NoOrient)  # alignL+++-- | Isoceles triangle with linear concave base. Inkscape type 1 - dart like.+arrowheadDart :: RealFloat n => Angle n -> ArrowHT n+arrowheadDart theta len shaftWidth = (hd # scale sz, jt)+  where+    hd = snugL . pathFromTrail . glueTrail $ fromOffsets [t1, t2, b2, b1]+    jt = pathFromTrail . glueTrail $ j <> reflectY j+    j = closeTrail $ fromOffsets [V2 (-jLength) 0, V2 0 (shaftWidth / 2)]+    v = rotate theta unitX+    (t1, t2) = (unit_X ^+^ v, V2 (-0.5) 0 ^-^ v)+    [b1, b2] = map (reflectY . negated) [t1, t2]+    psi = pi - negated t2 ^. _theta . rad+    jLength = shaftWidth / (2 * tan psi)++    -- If the shaft is too wide, set the size to a default value of 1.+    sz = max 1 ((len - jLength) / 1.5)++-- | Top half of an 'arrowheadDart'.+arrowheadHalfDart :: RealFloat n => Angle n -> ArrowHT n+arrowheadHalfDart theta len shaftWidth = (hd, jt)+  where+    hd = fromOffsets [t1, t2]+       # closeTrail # pathFromTrail+       # translateX 1.5 # scale sz+       # translateY (-shaftWidth/2)+       # snugL+    jt = snugR . translateY (-shaftWidth/2) . pathFromTrail . closeTrail $ fromOffsets [V2 (-jLength) 0, V2 0 shaftWidth]+    v = rotate theta unitX+    (t1, t2) = (unit_X ^+^ v, (0.5 *^ unit_X) ^-^ v)+    psi = pi - negated t2 ^. _theta . rad+    jLength = shaftWidth / tan psi++    -- If the shaft is too wide, set the size to a default value of 1.+    sz = max 1 ((len - jLength) / 1.5)++-- | Isoceles triangle with curved concave base. Inkscape type 2.+arrowheadSpike :: RealFloat n => Angle n -> ArrowHT n+arrowheadSpike theta len shaftWidth  = (hd # scale r, jt # scale r)+  where+    hd = snugL . closedPath $ l1 <> c <> l2+    jt = alignR . centerY . pathFromTrail+                . closeTrail $ arc' 1 (xDir & _theta <>~ negated phi) (2 *^ phi)+    l1 = trailFromSegments [straight $ unit_X ^+^ v]+    l2 = trailFromSegments [reverseSegment . straight $ (unit_X ^+^ reflectY v)]+    c = arc' 1 (rotate α xDir) ((-2) *^ α)+    α = (1/2 @@ turn) ^-^ theta+    v = rotate theta unitX++    -- The length of the head without its joint is, -2r cos theta and+    -- the length of the joint is r - sqrt (r^2 - y^2). So the total+    -- length of the arrow head is given by r(1 - 2 cos theta)-sqrt (r^2-y^2).+    -- Solving the quadratic gives two roots, we want the larger one.++    -- 1/4 turn < theta < 2/3 turn.+    a = 1 - 2 * cos (theta ^. rad)+    y = shaftWidth / 2++    -- If the shaft is too wide for the head, we default the radius r to+    -- 2/3 * len by setting d=1 and phi=pi/2.+    d = max 1 (len**2 + (1 - a**2) * y**2)+    r = (a * len + sqrt d) / (a**2 -1)+    phi = asinA (min 1 (y/r))++-- | Curved sides, linear concave base. Illustrator CS5 #3+arrowheadThorn :: RealFloat n => Angle n -> ArrowHT n+arrowheadThorn theta len shaftWidth = (hd # scale sz, jt)+  where+    hd = snugL . pathFromTrail . glueTrail $ hTop <> reflectY hTop+    hTop = closeTrail . trailFromSegments $ [c, l]+    jt = pathFromTrail . glueTrail $ j <> reflectY j+    j = closeTrail $ fromOffsets [V2 (-jLength) 0, V2 0 (shaftWidth / 2)]+    c = curvedSide theta+    v = rotate theta unitX+    l = reverseSegment . straight $ t+    t = v ^-^ V2 (-0.5) 0+    psi = fullTurn ^/ 2 ^-^ (negated t ^. _theta)+    jLength = shaftWidth / (2 * tanA psi)++    -- If the shaft if too wide, set the size to a default value of 1.+    sz = max 1 ((len - jLength) / 1.5)++-- | Make a side for the thorn head.+curvedSide :: Floating n => Angle n -> Segment Closed V2 n+curvedSide theta = bezier3 ctrl1 ctrl2 end+  where+    v0    = unit_X+    v1    = rotate theta unitX+    ctrl1 = v0+    ctrl2 = v0 ^+^ v1+    end   = v0 ^+^ v1++-- Standard heads ---------------------------------------------------------+-- | A line the same width as the shaft.+lineHead :: RealFloat n => ArrowHT n+lineHead s w = (square 1 # scaleX s # scaleY w # alignL, mempty)++noHead :: ArrowHT n+noHead _ _ = (mempty, mempty)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_triEx.svg#diagram=triEx&width=100>>++--   > triEx = drawHead tri+tri :: RealFloat n => ArrowHT n+tri = arrowheadTriangle (1/3 @@ turn)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_spikeEx.svg#diagram=spikeEx&width=100>>++--   > spikeEx = drawHead spike+spike :: RealFloat n => ArrowHT n+spike = arrowheadSpike (3/8 @@ turn)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_thornEx.svg#diagram=thornEx&width=100>>++--   > thornEx = drawHead thorn+thorn :: RealFloat n => ArrowHT n+thorn = arrowheadThorn (3/8 @@ turn)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_dartEx.svg#diagram=dartEx&width=100>>++--   > dartEx = drawHead dart+dart :: RealFloat n => ArrowHT n+dart = arrowheadDart (2/5 @@ turn)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_halfDartEx.svg#diagram=halfDartEx&width=100>>++--   > halfDartEx = drawHead halfDart+halfDart :: RealFloat n => ArrowHT n+halfDart = arrowheadHalfDart (2/5 @@ turn)++-- Tails ------------------------------------------------------------------+--   > drawTail t = arrowAt' (with  & arrowTail .~ t & shaftStyle %~ lw none+--   >                              & arrowHead .~ noHead & tailLength .~ local 0.5)+--   >         origin (r2 (1, 0))+--   >      <> square 0.5 # alignL # lw none # frame 0.1++-- | Utility function to convert any arrowhead to an arrowtail, i.e.+--   attached at the start of the trail.+headToTail :: OrderedField n => ArrowHT n -> ArrowHT n+headToTail hd = tl+  where+    tl sz shaftWidth = (t, j)+      where+        (t', j') = hd sz shaftWidth+        t = reflectX t'+        j = reflectX j'++arrowtailBlock :: forall n. (RealFloat n) => Angle n -> ArrowHT n+arrowtailBlock theta = aTail+  where+   aTail len _ = (t, mempty)+      where+        t  = rect len (len * x) # alignR+        a' :: V2 n+        a' = rotate theta unitX+        a  = a' ^-^ reflectY a'+        x  = norm a++-- | The angle is where the top left corner intersects the circle.+arrowtailQuill :: OrderedField n => Angle n -> ArrowHT n+arrowtailQuill theta = aTail+  where+   aTail len shaftWidth = (t, j)+      where+        t = closedPath (trailFromVertices [v0, v1, v2, v3, v4, v5, v0])+              # scale sz # alignR+        sz = len / 0.6+        v0 = p2 (0.5, 0)+        v2 = origin .+^ (rotate theta unitX # scale 0.5)+        v1 = v2 # translateX (5/8)+        v3 = p2 (-0.1, 0)+        v4 = v2 # reflectY+        v5 = v4 # translateX (5/8)+        s = 1 - shaftWidth / norm (v1 .-. v5)+        n1 = v0 # translateY (0.5 * shaftWidth)+        n2 = v1 .-^ ((v1 .-. v0) # scale s)+        n3 = v5 .-^ ((v5 .-. v0) # scale s)+        n4 = n1 # reflectY+        j  = closedPath $ trailFromVertices [v0, n1, n2, v0, n3, n4, v0]++-- Standard tails ---------------------------------------------------------+-- | A line the same width as the shaft.+lineTail :: RealFloat n => ArrowHT n+lineTail s w = (square 1 # scaleY w # scaleX s # alignR, mempty)++noTail :: ArrowHT n+noTail _ _ = (mempty, mempty)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_tri'Ex.svg#diagram=tri'Ex&width=100>>++--   > tri'Ex = drawTail tri'+tri' :: RealFloat n => ArrowHT n+tri' = headToTail tri++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_spike'Ex.svg#diagram=spike'Ex&width=100>>++--   > spike'Ex = drawTail spike'+spike' :: RealFloat n => ArrowHT n+spike' = headToTail spike++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_thorn'Ex.svg#diagram=thorn'Ex&width=100>>++--   > thorn'Ex = drawTail thorn'+thorn' :: RealFloat n => ArrowHT n+thorn' = headToTail thorn++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_dart'Ex.svg#diagram=dart'Ex&width=100>>++--   > dart'Ex = drawTail dart'+dart' :: RealFloat n => ArrowHT n+dart' = headToTail dart++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_halfDart'Ex.svg#diagram=halfDart'Ex&width=100>>++--   > halfDart'Ex = drawTail halfDart'+halfDart' :: RealFloat n => ArrowHT n+halfDart' = headToTail halfDart++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_quillEx.svg#diagram=quillEx&width=100>>++--   > quillEx = drawTail quill+quill :: (Floating n, Ord n) => ArrowHT n+quill = arrowtailQuill (2/5 @@ turn)++-- | <<diagrams/src_Diagrams_TwoD_Arrowheads_blockEx.svg#diagram=blockEx&width=100>>++--   > blockEx = drawTail block+block :: RealFloat n => ArrowHT n+block = arrowtailBlock (7/16 @@ turn)
+ src/Diagrams/TwoD/Attributes.hs view
@@ -0,0 +1,423 @@+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TemplateHaskell            #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE UndecidableInstances       #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.TwoD.Attributes+-- Copyright   :  (c) 2013-2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Diagrams may have /attributes/ which affect the way they are+-- rendered. This module defines /Textures/ (Gradients and Colors) in two+-- dimensions. Like the attributes defined in the Diagrams.Attributes module,+-- all attributes defined here use the 'Last' or 'Recommend' /semigroup/ structure.+-- 'FillColor' and 'LineColor' attributes are provided so that backends that+-- don't support gradients need not be concerned with using textures. Backends+-- should only implement color attributes or textures attributes, not both.+--+-----------------------------------------------------------------------------++module Diagrams.TwoD.Attributes (+  -- * Textures+    Texture(..), solid, _SC, _AC, _LG, _RG, defaultLG, defaultRG+  , GradientStop(..), stopColor, stopFraction, mkStops+  , SpreadMethod(..), lineLGradient, lineRGradient++  -- ** Linear Gradients+  , LGradient(..), lGradStops, lGradTrans, lGradStart, lGradEnd+  , lGradSpreadMethod, mkLinearGradient++  -- ** Radial Gradients+  , RGradient(..), rGradStops, rGradTrans+  , rGradCenter0, rGradRadius0, rGradCenter1, rGradRadius1+  , rGradSpreadMethod, mkRadialGradient++  -- ** Line texture+  , LineTexture(..), _LineTexture, getLineTexture, lineTexture, lineTextureA+  , mkLineTexture, _lineTexture++  -- ** Line color+  , lineColor, lc, lcA++  -- ** Fill texture+  , FillTexture(..), _FillTexture, getFillTexture, fillTexture+  , mkFillTexture, _fillTexture, _fillTextureR++  -- ** Fill color+  , fillColor, fc, fcA, recommendFillColor++  -- * Compilation utilities+  , splitTextureFills++  ) where++import           Control.Lens                hiding (transform)+import           Data.Colour                 hiding (AffineSpace, over)+import           Data.Data+import           Data.Default+import           Data.Monoid.Recommend+import           Data.Semigroup++import           Diagrams.Attributes+import           Diagrams.Attributes.Compile+import           Diagrams.Core+import           Diagrams.Core.Types         (RTree)+import           Diagrams.Located            (unLoc)+import           Diagrams.Path               (Path, pathTrails)+import           Diagrams.Trail              (isLoop)+import           Diagrams.TwoD.Types+import           Diagrams.Util+++-----------------------------------------------------------------+--  Gradients  --------------------------------------------------+-----------------------------------------------------------------++-- | A gradient stop contains a color and fraction (usually between 0 and 1)+data GradientStop d = GradientStop+  { _stopColor    :: SomeColor+  , _stopFraction :: d+  }++makeLensesWith (lensRules & generateSignatures .~ False) ''GradientStop++-- | A color for the stop.+stopColor :: Lens' (GradientStop n) SomeColor++-- | The fraction for stop.+stopFraction :: Lens' (GradientStop n) n++-- | The 'SpreadMethod' determines what happens before 'lGradStart' and after+--   'lGradEnd'. 'GradPad' fills the space before the start of the gradient+--   with the color of the first stop and the color after end of the gradient+--   with the color of the last stop. 'GradRepeat' restarts the gradient and+--   'GradReflect' restarts the gradient with the stops in reverse order.+data SpreadMethod = GradPad | GradReflect | GradRepeat++-- | Linear Gradient+data LGradient n = LGradient+  { _lGradStops        :: [GradientStop n]+  , _lGradStart        :: Point V2 n+  , _lGradEnd          :: Point V2 n+  , _lGradTrans        :: Transformation V2 n+  , _lGradSpreadMethod :: SpreadMethod }++type instance V (LGradient n) = V2+type instance N (LGradient n) = n++makeLensesWith (lensRules & generateSignatures .~ False) ''LGradient++instance Fractional n => Transformable (LGradient n) where+  transform = over lGradTrans . transform++-- | A list of stops (colors and fractions).+lGradStops :: Lens' (LGradient n) [GradientStop n]++-- | A transformation to be applied to the gradient. Usually this field will+--   start as the identity transform and capture the transforms that are applied+--   to the gradient.+lGradTrans :: Lens' (LGradient n) (Transformation V2 n)++-- | The starting point for the first gradient stop. The coordinates are in+--   'local' units and the default is (-0.5, 0).+lGradStart :: Lens' (LGradient n) (Point V2 n)++-- | The ending point for the last gradient stop.The coordinates are in+--   'local' units and the default is (0.5, 0).+lGradEnd :: Lens' (LGradient n) (Point V2 n)++-- | For setting the spread method.+lGradSpreadMethod :: Lens' (LGradient n) SpreadMethod++-- | Radial Gradient+data RGradient n = RGradient+  { _rGradStops        :: [GradientStop n]+  , _rGradCenter0      :: Point V2 n+  , _rGradRadius0      :: n+  , _rGradCenter1      :: Point V2 n+  , _rGradRadius1      :: n+  , _rGradTrans        :: Transformation V2 n+  , _rGradSpreadMethod :: SpreadMethod }++makeLensesWith (lensRules & generateSignatures .~ False) ''RGradient++type instance V (RGradient n) = V2+type instance N (RGradient n) = n++instance Fractional n => Transformable (RGradient n) where+  transform = over rGradTrans . transform++-- | A list of stops (colors and fractions).+rGradStops :: Lens' (RGradient n) [GradientStop n]++-- | The center point of the inner circle.+rGradCenter0 :: Lens' (RGradient n) (Point V2 n)++-- | The radius of the inner cirlce in 'local' coordinates.+rGradRadius0 :: Lens' (RGradient n) n++-- | The center of the outer circle.+rGradCenter1  :: Lens' (RGradient n) (Point V2 n)++-- | The radius of the outer circle in 'local' coordinates.+rGradRadius1 :: Lens' (RGradient n) n++-- | A transformation to be applied to the gradient. Usually this field will+--   start as the identity transform and capture the transforms that are applied+--   to the gradient.+rGradTrans :: Lens' (RGradient n) (Transformation V2 n)++-- | For setting the spread method.+rGradSpreadMethod :: Lens' (RGradient n) SpreadMethod++-----------------------------------------------------------------+--  Textures  ---------------------------------------------------+-----------------------------------------------------------------++-- | A Texture is either a color 'SC', linear gradient 'LG', or radial gradient 'RG'.+--   An object can have only one texture which is determined by the 'Last'+--   semigroup structure.+data Texture n = SC SomeColor | LG (LGradient n) | RG (RGradient n)+  deriving Typeable++type instance V (Texture n) = V2+type instance N (Texture n) = n++makePrisms ''Texture++-- | Prism onto an 'AlphaColour' 'Double' of a 'SC' texture.+_AC :: Prism' (Texture n) (AlphaColour Double)+_AC = _SC . _SomeColor++instance Floating n => Transformable (Texture n) where+  transform t (LG lg) = LG $ transform t lg+  transform t (RG rg) = RG $ transform t rg+  transform _ sc      = sc++-- | Convert a solid colour into a texture.+solid :: Color a => a -> Texture n+solid = SC . SomeColor++-- | A default is provided so that linear gradients can easily be created using+--   lenses. For example, @lg = defaultLG & lGradStart .~ (0.25 ^& 0.33)@. Note that+--   no default value is provided for @lGradStops@, this must be set before+--   the gradient value is used, otherwise the object will appear transparent.+defaultLG :: Fractional n => Texture n+defaultLG = LG LGradient+  { _lGradStops        = []+  , _lGradStart        = mkP2 (-0.5) 0+  , _lGradEnd          = mkP2 0.5 0+  , _lGradTrans        = mempty+  , _lGradSpreadMethod = GradPad+  }++-- | A default is provided so that radial gradients can easily be created using+--   lenses. For example, @rg = defaultRG & rGradRadius1 .~ 0.25@. Note that+--   no default value is provided for @rGradStops@, this must be set before+--   the gradient value is used, otherwise the object will appear transparent.+defaultRG :: Fractional n => Texture n+defaultRG = RG RGradient+  { _rGradStops        = []+  , _rGradCenter0      = mkP2 0 0+  , _rGradRadius0      = 0.0+  , _rGradCenter1      = mkP2 0 0+  , _rGradRadius1      = 0.5+  , _rGradTrans        = mempty+  , _rGradSpreadMethod = GradPad+  }++-- | A convenient function for making gradient stops from a list of triples.+--   (An opaque color, a stop fraction, an opacity).+mkStops :: [(Colour Double, d, Double)] -> [GradientStop d]+mkStops = map (\(x, y, z) -> GradientStop (SomeColor (withOpacity x z)) y)++-- | Make a linear gradient texture from a stop list, start point, end point,+--   and 'SpreadMethod'. The 'lGradTrans' field is set to the identity+--   transfrom, to change it use the 'lGradTrans' lens.+mkLinearGradient :: Num n => [GradientStop n] -> Point V2 n -> Point V2 n -> SpreadMethod -> Texture n+mkLinearGradient stops  start end spreadMethod+  = LG (LGradient stops start end mempty spreadMethod)++-- | Make a radial gradient texture from a stop list, radius, start point,+--   end point, and 'SpreadMethod'. The 'rGradTrans' field is set to the identity+--   transfrom, to change it use the 'rGradTrans' lens.+mkRadialGradient :: Num n => [GradientStop n] -> Point V2 n -> n+                  -> Point V2 n -> n -> SpreadMethod -> Texture n+mkRadialGradient stops c0 r0 c1 r1 spreadMethod+  = RG (RGradient stops c0 r0 c1 r1 mempty spreadMethod)++-- Line Texture --------------------------------------------------------++-- | The texture with which lines are drawn.  Note that child+--   textures always override parent textures.+--   More precisely, the semigroup structure on line texture attributes+--   is that of 'Last'.+newtype LineTexture n = LineTexture (Last (Texture n))+  deriving (Typeable, Semigroup)+instance (Typeable n) => AttributeClass (LineTexture n)++type instance V (LineTexture n) = V2+type instance N (LineTexture n) = n++_LineTexture :: Iso (LineTexture n) (LineTexture n')+                    (Texture n)     (Texture n')+_LineTexture = iso getLineTexture (LineTexture . Last)++-- Only gradients get transformed. The transform is applied to the gradients+-- transform field. Colors are left unchanged.+instance Floating n => Transformable (LineTexture n) where+  transform t (LineTexture (Last tx)) = LineTexture (Last $ transform t tx)++instance Default (LineTexture n) where+  def = _LineTexture . _SC ## SomeColor black++mkLineTexture :: Texture n -> LineTexture n+mkLineTexture = LineTexture . Last++getLineTexture :: LineTexture n -> Texture n+getLineTexture (LineTexture (Last t)) = t++lineTexture :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => Texture n -> a -> a+lineTexture = applyTAttr . LineTexture . Last++lineTextureA :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => LineTexture n -> a -> a+lineTextureA = applyTAttr++_lineTexture :: (Floating n, Typeable n) => Lens' (Style V2 n) (Texture n)+_lineTexture = atTAttr . anon def isDef . _LineTexture+  where+    isDef = anyOf (_LineTexture . _AC) (== opaque black)++-- | Set the line (stroke) color.  This function is polymorphic in the+--   color type (so it can be used with either 'Colour' or+--   'AlphaColour'), but this can sometimes create problems for type+--   inference, so the 'lc' and 'lcA' variants are provided with more+--   concrete types.+lineColor :: (InSpace V2 n a, Color c, Typeable n, Floating n, HasStyle a) => c -> a -> a+lineColor = lineTexture . SC . SomeColor++-- | A synonym for 'lineColor', specialized to @'Colour' Double@+--   (i.e. opaque colors).  See comment in 'lineColor' about backends.+lc :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => Colour Double -> a -> a+lc = lineColor++-- | A synonym for 'lineColor', specialized to @'AlphaColour' Double@+--   (i.e. colors with transparency).  See comment in 'lineColor'+--   about backends.+lcA :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => AlphaColour Double -> a -> a+lcA = lineColor++-- | Apply a linear gradient.+lineLGradient :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => LGradient n -> a -> a+lineLGradient g = lineTexture (LG g)++-- | Apply a radial gradient.+lineRGradient :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => RGradient n -> a -> a+lineRGradient g = lineTexture (RG g)++-- Fill Texture --------------------------------------------------------++-- | The texture with which objects are filled.+--   The semigroup structure on fill texture attributes+--   is that of 'Recommed . Last'.+newtype FillTexture n = FillTexture (Recommend (Last (Texture n)))+  deriving (Typeable, Semigroup)++instance Typeable n => AttributeClass (FillTexture n)++_FillTexture :: Iso' (FillTexture n) (Recommend (Texture n))+_FillTexture = iso getter setter+  where+    getter (FillTexture (Recommend (Last t))) = Recommend t+    getter (FillTexture (Commit    (Last t))) = Commit t+    setter (Recommend t) = FillTexture (Recommend (Last t))+    setter (Commit t)    = FillTexture (Commit (Last t))+  -- = iso (\(FillTexture a) -> a) FillTexture . mapping _Wrapped+  -- -- once we depend on monoid-extras-0.4++type instance V (FillTexture n) = V2+type instance N (FillTexture n) = n++-- Only gradients get transformed. The transform is applied to the gradients+-- transform field. Colors are left unchanged.+instance Floating n => Transformable (FillTexture n) where+  transform = over (_FillTexture . _recommend) . transform++instance Default (FillTexture n) where+  def = mkFillTexture $ _AC ## transparent++getFillTexture :: FillTexture n -> Texture n+getFillTexture (FillTexture tx) = getLast . getRecommend $ tx++fillTexture :: (InSpace V2 n a, Typeable n, Floating n, HasStyle a) => Texture n -> a -> a+fillTexture = applyTAttr . mkFillTexture++mkFillTexture :: Texture n -> FillTexture n+mkFillTexture = FillTexture . Commit . Last++-- | Lens onto the 'Recommend' of a fill texture in a style.+_fillTextureR :: (Typeable n, Floating n) => Lens' (Style V2 n) (Recommend (Texture n))+_fillTextureR = atTAttr . anon def isDef . _FillTexture+  where+    isDef = anyOf (_FillTexture . _Recommend . _AC) (== transparent)++-- | Commit a fill texture in a style. This is /not/ a valid setter+--   because it doesn't abide the functor law (see 'committed').+_fillTexture :: (Typeable n, Floating n) => Lens' (Style V2 n) (Texture n)+_fillTexture = _fillTextureR . committed++-- | Set the fill color.  This function is polymorphic in the color+--   type (so it can be used with either 'Colour' or 'AlphaColour'),+--   but this can sometimes create problems for type inference, so the+--   'fc' and 'fcA' variants are provided with more concrete types.+fillColor :: (InSpace V2 n a, Color c, Typeable n, Floating n, HasStyle a) => c -> a -> a+fillColor = fillTexture . SC . SomeColor++-- | Set a \"recommended\" fill color, to be used only if no explicit+--   calls to 'fillColor' (or 'fc', or 'fcA') are used.+--   See comment after 'fillColor' about backends.+recommendFillColor :: (InSpace V2 n a, Color c, Typeable n, Floating n, HasStyle a) => c -> a -> a+recommendFillColor =+  applyTAttr . FillTexture . Recommend . Last . SC . SomeColor++-- | A synonym for 'fillColor', specialized to @'Colour' Double@+--   (i.e. opaque colors). See comment after 'fillColor' about backends.+fc :: (InSpace V2 n a, Floating n, Typeable n, HasStyle a) => Colour Double -> a -> a+fc = fillColor++-- | A synonym for 'fillColor', specialized to @'AlphaColour' Double@+--   (i.e. colors with transparency). See comment after 'fillColor' about backends.+fcA :: (InSpace V2 n a, Floating n, Typeable n, HasStyle a) => AlphaColour Double -> a -> a+fcA = fillColor++-- Split fills ---------------------------------------------------------++data FillTextureLoops n = FillTextureLoops++instance Typeable n => SplitAttribute (FillTextureLoops n) where+  type AttrType (FillTextureLoops n) = FillTexture n+  type PrimType (FillTextureLoops n) = Path V2 n++  primOK _ = all (isLoop . unLoc) . pathTrails++-- | Push fill attributes down until they are at the root of subtrees+--   containing only loops. This makes life much easier for backends,+--   which typically have a semantics where fill attributes are+--   applied to lines/non-closed paths as well as loops/closed paths,+--   whereas in the semantics of diagrams, fill attributes only apply+--   to loops.+splitTextureFills+  :: forall b v n a. (+                     Typeable n) => RTree b v n a -> RTree b v n a+splitTextureFills = splitAttr (FillTextureLoops :: FillTextureLoops n)
src/Diagrams/TwoD/Combinators.hs view
@@ -1,7 +1,12 @@+{-# LANGUAGE ConstraintKinds       #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-} {-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE ViewPatterns          #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Combinators@@ -18,11 +23,11 @@     (       -- * Binary combinators -      (===), (|||), atAngle+      (===), (|||)        -- * n-ary combinators-    , hcat, hcat'-    , vcat, vcat'+    , hcat, hcat', hsep+    , vcat, vcat', vsep        -- * Spacing/envelopes     , strutR2@@ -31,36 +36,38 @@      , extrudeLeft, extrudeRight, extrudeBottom, extrudeTop -    , view+    , rectEnvelope, crop -    , boundingRect, bg+    , boundingRect, bg, bgFrame      ) where -import           Data.AffineSpace+import           Control.Lens             ((&), (.~)) import           Data.Colour-import           Data.Default.Class+import           Data.Default import           Data.Semigroup-import           Data.VectorSpace  import           Diagrams.Core -import           Diagrams.Attributes     (fc, lw)+import           Diagrams.Attributes      (lwO) import           Diagrams.BoundingBox import           Diagrams.Combinators-import           Diagrams.Coordinates import           Diagrams.Path+import           Diagrams.Query           (value) import           Diagrams.Segment import           Diagrams.TrailLike import           Diagrams.TwoD.Align-import           Diagrams.TwoD.Path      ()-import           Diagrams.TwoD.Segment+import           Diagrams.TwoD.Attributes (fc)+import           Diagrams.TwoD.Path       () import           Diagrams.TwoD.Shapes-import           Diagrams.TwoD.Transform (scaleX, scaleY)+import           Diagrams.TwoD.Transform  (scaleX, scaleY) import           Diagrams.TwoD.Types-import           Diagrams.TwoD.Vector    (fromDirection, unitX, unitY)-import           Diagrams.Util           (( # ))+import           Diagrams.TwoD.Vector+import           Diagrams.Util            (( # )) +import           Linear.Affine+import           Linear.Metric+import           Linear.Vector  infixl 6 === infixl 6 |||@@ -79,26 +86,18 @@ --   combined diagram is the same as the local origin of the first. --   @(===)@ is associative and has 'mempty' as an identity.  See the --   documentation of 'beside' for more information.-(===) :: (Juxtaposable a, V a ~ R2, Semigroup a) => a -> a -> a-(===) = beside (negateV unitY)+(===) :: (InSpace V2 n a, Juxtaposable a, Semigroup a) => a -> a -> a+(===) = beside unit_Y  -- | Place two diagrams (or other juxtaposable objects) horizontally --   adjacent to one another, with the first diagram to the left of --   the second.  The local origin of the resulting combined diagram---   is the same as the local origin of the first.  @(===)@ is+--   is the same as the local origin of the first.  @(|||)@ is --   associative and has 'mempty' as an identity.  See the --   documentation of 'beside' for more information.-(|||) :: (Juxtaposable a, V a ~ R2, Semigroup a) => a -> a -> a+(|||) :: (InSpace V2 n a, Juxtaposable a, Semigroup a) => a -> a -> a (|||) = beside unitX --- | Place two diagrams (or other juxtaposable objects) adjacent to one---   another, with the second diagram placed along a line at angle---   'th' from the first.  The local origin of the resulting combined---   diagram is the same as the local origin of the first.---   See the documentation of 'beside' for more information.-atAngle :: (Juxtaposable a, V a ~ R2, Semigroup a, Angle b) => b -> a -> a -> a-atAngle th = beside (fromDirection th)- -- | Lay out a list of juxtaposable objects in a row from left to right, --   so that their local origins lie along a single horizontal line, --   with successive envelopes tangent to one another.@@ -110,17 +109,24 @@ --     "Diagrams.TwoD.Align" before applying 'hcat'. -- --   * For non-axis-aligned layout, see 'cat'.-hcat :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2)+hcat :: (InSpace V2 n a, Floating n, Juxtaposable a, HasOrigin a, Monoid' a)      => [a] -> a hcat = hcat' def  -- | A variant of 'hcat' taking an extra 'CatOpts' record to control --   the spacing.  See the 'cat'' documentation for a description of---   the possibilities.-hcat' :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2)-      => CatOpts R2 -> [a] -> a+--   the possibilities. For the common case of setting just a+--   separation amount, see 'hsep'.+hcat' :: (InSpace V2 n a, Floating n, Juxtaposable a, HasOrigin a, Monoid' a)+      => CatOpts n -> [a] -> a hcat' = cat' unitX +-- | A convenient synonym for horizontal concatenation with+--   separation: @hsep s === hcat' (with & sep .~ s)@.+hsep :: (InSpace V2 n a, Floating n, Juxtaposable a, HasOrigin a, Monoid' a)+     => n -> [a] -> a+hsep s = hcat' (def & sep .~ s)+ -- | Lay out a list of juxtaposable objects in a column from top to --   bottom, so that their local origins lie along a single vertical --   line, with successive envelopes tangent to one another.@@ -132,24 +138,31 @@ --     "Diagrams.TwoD.Align" before applying 'vcat'. -- --   * For non-axis-aligned layout, see 'cat'.-vcat :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2)+vcat :: (InSpace V2 n a, Floating n, Juxtaposable a, HasOrigin a, Monoid' a)      => [a] -> a vcat = vcat' def  -- | A variant of 'vcat' taking an extra 'CatOpts' record to control---   the spacing.  See the 'cat'' documentation for a description of the---   possibilities.-vcat' :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2)-      => CatOpts R2 -> [a] -> a-vcat' = cat' (negateV unitY)+--   the spacing.  See the 'cat'' documentation for a description of+--   the possibilities.  For the common case of setting just a+--   separation amount, see 'vsep'.+vcat' :: (InSpace V2 n a, Floating n, Juxtaposable a, HasOrigin a, Monoid' a)+      => CatOpts n -> [a] -> a+vcat' = cat' unit_Y +-- | A convenient synonym for vertical concatenation with+--   separation: @vsep s === vcat' (with & sep .~ s)@.+vsep :: (InSpace V2 n a, Floating n, Juxtaposable a, HasOrigin a, Monoid' a)+     => n -> [a] -> a+vsep s = vcat' (def & sep .~ s)+ -- | @strutR2 v@ is a two-dimensional diagram which produces no --   output, but with respect to alignment, envelope, /and trace/ acts --   like a 1-dimensional segment oriented along the vector @v@, with --   local origin at its center.  If you don't care about the trace --   then there's no difference between @strutR2@ and the more general --   'strut'.-strutR2 :: (Backend b R2, Monoid' m) => R2 -> QDiagram b R2 m+strutR2 :: (RealFloat n, Monoid' m) => V2 n -> QDiagram b V2 n m strutR2 v = phantom seg   where     seg = FLinear (origin .+^ 0.5 *^ v) (origin .+^ (-0.5) *^ v)@@ -157,14 +170,14 @@ -- | @strutX w@ is an empty diagram with width @w@, height 0, and a --   centered local origin.  Note that @strutX (-w)@ behaves the same as --   @strutX w@.-strutX :: (Backend b R2, Monoid' m) => Double -> QDiagram b R2 m-strutX d = strut (d & 0)+strutX :: (Metric v, R1 v, OrderedField n) => n -> QDiagram b v n m+strutX d = strut (zero & _x .~ d)  -- | @strutY h@ is an empty diagram with height @h@, width 0, and a --   centered local origin. Note that @strutY (-h)@ behaves the same as --   @strutY h@.-strutY :: (Backend b R2, Monoid' m) => Double -> QDiagram b R2 m-strutY d = strut (0 & d)+strutY :: (Metric v, R2 v, OrderedField n) => n -> QDiagram b v n m+strutY d = strut (zero & _y .~ d)  -- | @padX s@ \"pads\" a diagram in the x-direction, expanding its --   envelope horizontally by a factor of @s@ (factors between 0 and 1@@ -173,8 +186,8 @@ --   centered horizontally the padding may appear \"uneven\".  If this --   is not desired, the origin can be centered (using 'centerX') --   before applying @padX@.-padX :: ( Backend b R2, Monoid' m )-     => Double -> QDiagram b R2 m -> QDiagram b R2 m+padX :: (Metric v, R2 v, OrderedField n, Monoid' m)+     => n -> QDiagram b v n m -> QDiagram b v n m padX s d = withEnvelope (d # scaleX s) d  -- | @padY s@ \"pads\" a diagram in the y-direction, expanding its@@ -184,8 +197,8 @@ --   so if the origin is not centered vertically the padding may appear --   \"uneven\".  If this is not desired, the origin can be centered --   (using 'centerY') before applying @padY@.-padY :: ( Backend b R2, Monoid' m )-     => Double -> QDiagram b R2 m -> QDiagram b R2 m+padY :: (Metric v, R2 v, Monoid' m, OrderedField n)+     => n -> QDiagram b v n m -> QDiagram b v n m padY s d = withEnvelope (d # scaleY s) d  -- | @extrudeLeft s@ \"extrudes\" a diagram in the negative x-direction,@@ -193,6 +206,7 @@ --   the envelope is inset instead. -- --   See the documentation for 'extrudeEnvelope' for more information.+extrudeLeft :: (OrderedField n, Monoid' m) => n -> QDiagram b V2 n m -> QDiagram b V2 n m extrudeLeft s   | s >= 0    = extrudeEnvelope $ unitX ^* negate s   | otherwise = intrudeEnvelope $ unitX ^* negate s@@ -202,6 +216,7 @@ --   the envelope is inset instead. -- --   See the documentation for 'extrudeEnvelope' for more information.+extrudeRight :: (OrderedField n, Monoid' m) => n -> QDiagram b V2 n m -> QDiagram b V2 n m extrudeRight s   | s >= 0    = extrudeEnvelope $ unitX ^* s   | otherwise = intrudeEnvelope $ unitX ^* s@@ -211,6 +226,7 @@ --   the envelope is inset instead. -- --   See the documentation for 'extrudeEnvelope' for more information.+extrudeBottom :: (OrderedField n, Monoid' m) => n -> QDiagram b V2 n m -> QDiagram b V2 n m extrudeBottom s   | s >= 0    = extrudeEnvelope $ unitY ^* negate s   | otherwise = intrudeEnvelope $ unitY ^* negate s@@ -220,28 +236,44 @@ --   the envelope is inset instead. -- --   See the documentation for 'extrudeEnvelope' for more information.+extrudeTop :: (OrderedField n, Monoid' m) => n -> QDiagram b V2 n m -> QDiagram b V2 n m extrudeTop s   | s >= 0    = extrudeEnvelope $ unitY ^* s   | otherwise = intrudeEnvelope $ unitY ^* s --- | @view p v@ sets the envelope of a diagram to a rectangle whose+-- | @rectEnvelope p v@ sets the envelope of a diagram to a rectangle whose --   lower-left corner is at @p@ and whose upper-right corner is at @p --   .+^ v@.  Useful for selecting the rectangular portion of a --   diagram which should actually be \"viewed\" in the final render, --   if you don't want to see the entire diagram.-view :: ( Backend b R2, Monoid' m )-     => P2 -> R2 -> QDiagram b R2 m -> QDiagram b R2 m-view p (coords -> w :& h) = withEnvelope (rect w h # alignBL # moveTo p :: D R2)+rectEnvelope :: forall b n m. (OrderedField n, Monoid' m)+     => Point V2 n -> V2 n -> QDiagram b V2 n m -> QDiagram b V2 n m+rectEnvelope p (V2 w h) = withEnvelope (rect w h # alignBL # moveTo p :: Path V2 n) +-- | A synonym for 'rectEnvelope'.+crop :: forall b n m. (OrderedField n, Monoid' m)+     => Point V2 n -> V2 n -> QDiagram b V2 n m -> QDiagram b V2 n m+crop = rectEnvelope+ -- | Construct a bounding rectangle for an enveloped object, that is, --   the smallest axis-aligned rectangle which encloses the object.-boundingRect :: ( Enveloped t, Transformable t, TrailLike t, Monoid t, V t ~ R2-                , Enveloped a, V a ~ R2-                )+boundingRect :: ( InSpace V2 n a, SameSpace a t+                , Enveloped t, Transformable t, TrailLike t, Monoid t+                , Enveloped a)              => a -> t boundingRect = (`boxFit` rect 1 1) . boundingBox  -- | \"Set the background color\" of a diagram.  That is, place a --   diagram atop a bounding rectangle of the given color.-bg :: (Renderable (Path R2) b) => Colour Double -> Diagram b R2 -> Diagram b R2-bg c d = d <> boundingRect d # lw 0 # fc c+--   The background does not change the result of queries.+bg :: (TypeableFloat n, Renderable (Path V2 n) b, Monoid' q)+    => Colour Double -> QDiagram b V2 n q -> QDiagram b V2 n q+bg c d = d <> boundingRect d # lwO 0 # fc c # value mempty++-- | Similar to 'bg' but makes the colored background rectangle larger than+--   the diagram. The first parameter is used to set how far the background+--   extends beyond the diagram.+--   The background does not change the result of queries.+bgFrame :: (TypeableFloat n, Renderable (Path V2 n) b, Monoid' q)+    => n -> Colour Double -> QDiagram b V2 n q -> QDiagram b V2 n q+bgFrame f c d = d <> boundingRect (frame f d) # lwO 0 # fc c # value mempty
src/Diagrams/TwoD/Curvature.hs view
@@ -19,19 +19,16 @@     , squaredRadiusOfCurvature     ) where -import           Data.AffineSpace+import           Control.Lens        (over)+import           Control.Monad import           Data.Monoid.Inf-import           Data.VectorSpace -import           Control.Arrow        (first, second)-import           Control.Monad        (join)--import           Diagrams.Core- import           Diagrams.Segment+import           Diagrams.Tangent import           Diagrams.TwoD.Types-import           Diagrams.TwoD.Vector +import           Linear.Vector+ -- | Curvature measures how curved the segment is at a point.  One intuition -- for the concept is how much you would turn the wheel when driving a car -- along the curve.  When the wheel is held straight there is zero curvature.@@ -51,19 +48,19 @@ -- -- Lets consider the following curve: ----- <<diagrams/diagramA.svg#diagram=diagramA&height=200&width=400>>+-- <<diagrams/src_Diagrams_TwoD_Curvature_diagramA.svg#diagram=diagramA&height=200&width=400>> -- -- The curve starts with positive curvature, ----- <<diagrams/diagramPos.svg#diagram=diagramPos&height=200&width=400>>+-- <<diagrams/src_Diagrams_TwoD_Curvature_diagramPos.svg#diagram=diagramPos&height=200&width=400>> -- -- approaches zero curvature ----- <<diagrams/diagramZero.svg#diagram=diagramZero&height=200&width=400>>+-- <<diagrams/src_Diagrams_TwoD_Curvature_diagramZero.svg#diagram=diagramZero&height=200&width=400>> -- -- then has negative curvature ----- <<diagrams/diagramNeg.svg#diagram=diagramNeg&height=200&width=400>>+-- <<diagrams/src_Diagrams_TwoD_Curvature_diagramNeg.svg#diagram=diagramNeg&height=200&width=400>> -- -- > {-# LANGUAGE GADTs #-} -- >@@ -71,70 +68,72 @@ -- > import Data.Monoid.Inf -- > import Diagrams.Coordinates -- >--- > segmentA = Cubic (12 & 0) (8 & 10) (OffsetClosed (20 & 8))+-- > segmentA :: Segment Closed V2 Double+-- > segmentA = Cubic (12 ^& 0) (8 ^& 10) (OffsetClosed (20 ^& 8)) -- >--- > curveA = lw 0.1 . stroke . fromSegments $ [segmentA]+-- > curveA = lw thick . strokeP . fromSegments $ [segmentA] -- > -- > diagramA = pad 1.1 . centerXY $ curveA -- > -- > diagramPos = diagramWithRadius 0.2 -- >--- > diagramZero = diagramWithRadius 0.5+-- > diagramZero = diagramWithRadius 0.45 -- > -- > diagramNeg = diagramWithRadius 0.8 -- > -- > diagramWithRadius t = pad 1.1 . centerXY -- >          $ curveA -- >         <> showCurvature segmentA t--- >          # withEnvelope (curveA :: D R2)--- >          # lw 0.05 # lc red+-- >          # withEnvelope (curveA :: D V2 Double)+-- >          # lc red -- >+-- > showCurvature :: Segment Closed V2 Double -> Double -> Diagram SVG -- > showCurvature bez@(Cubic b c (OffsetClosed d)) t--- >   | v == 0    = mempty--- >   | otherwise = go (radiusOfCurvature bez t)+-- >   | v == (0,0) = mempty+-- >   | otherwise  = go (radiusOfCurvature bez t) -- >   where -- >     v@(x,y) = unr2 $ firstDerivative b c d t--- >     vp = (-y) & x+-- >     vp = (-y) ^& x -- > -- >     firstDerivative b c d t = let tt = t*t in (3*(3*tt-4*t+1))*^b + (3*(2-3*t)*t)*^c + (3*tt)*^d -- > -- >     go Infinity   = mempty -- >     go (Finite r) = (circle (abs r) # translate vpr--- >                  <> stroke (origin ~~ (origin .+^ vpr)))+-- >                  <> strokeP (origin ~~ (origin .+^ vpr))) -- >                   # moveTo (origin .+^ atParam bez t) -- >       where--- >         vpr = r2 (normalized vp ^* r)+-- >         vpr = signorm vp ^* r -- > ---curvature :: Segment Closed R2  -- ^ Segment to measure on.-          -> Double           -- ^ Parameter to measure at.-          -> PosInf Double    -- ^ Result is a @PosInf@ value where @PosInfty@ represents-                              -- infinite curvature or zero radius of curvature.-curvature s = toPosInf . second sqrt . curvaturePair (fmap unr2 s) -- TODO: Use the generalized unr2+curvature :: RealFloat n+          => Segment Closed V2 n  -- ^ Segment to measure on.+          -> n                    -- ^ Parameter to measure at.+          -> PosInf n             -- ^ Result is a @PosInf@ value where @PosInfty@ represents+                                  --   infinite curvature or zero radius of curvature.+curvature s = toPosInf . over _y sqrt . curvaturePair s  -- | With @squaredCurvature@ we can compute values in spaces that do not support -- 'sqrt' and it is just as useful for relative ordering of curvatures or looking -- for zeros.-squaredCurvature :: Segment Closed R2 -> Double -> PosInf Double-squaredCurvature s = toPosInf . first (join (*)) . curvaturePair (fmap unr2 s) -- TODO: Use the generalized unr2-+squaredCurvature :: RealFloat n => Segment Closed V2 n -> n -> PosInf n+squaredCurvature s = toPosInf . over _x (join (*)) . curvaturePair s  -- | Reciprocal of @curvature@.-radiusOfCurvature :: Segment Closed R2  -- ^ Segment to measure on.-                  -> Double           -- ^ Parameter to measure at.-                  -> PosInf Double    -- ^ Result is a @PosInf@ value where @PosInfty@ represents-                                      -- infinite radius of curvature or zero curvature.-radiusOfCurvature s = toPosInf . (\(p,q) -> (q,p)) . second sqrt . curvaturePair (fmap unr2 s)+radiusOfCurvature :: RealFloat n+                  => Segment Closed V2 n -- ^ Segment to measure on.+                  -> n                   -- ^ Parameter to measure at.+                  -> PosInf n            -- ^ Result is a @PosInf@ value where @PosInfty@ represents+                                         --   infinite radius of curvature or zero curvature.+radiusOfCurvature s = toPosInf . (\(V2 p q) -> V2 (sqrt q) p) . curvaturePair s  -- | Reciprocal of @squaredCurvature@-squaredRadiusOfCurvature :: Segment Closed R2 -> Double -> PosInf Double-squaredRadiusOfCurvature s = toPosInf . (\(p,q) -> (q,p)) . first (join (*)) . curvaturePair (fmap unr2 s)-+squaredRadiusOfCurvature :: RealFloat n => Segment Closed V2 n -> n -> PosInf n+squaredRadiusOfCurvature s = toPosInf . (\(V2 p q) -> (V2 q (p * p))) . curvaturePair s  -- Package up problematic values with the appropriate infinity.-toPosInf :: RealFloat a => (a,a) -> PosInf a-toPosInf (_,0) = Infinity-toPosInf (p,q)+toPosInf :: RealFloat a => V2 a -> PosInf a+toPosInf (V2 _ 0) = Infinity+toPosInf (V2 p q)   | isInfinite r || isNaN r = Infinity   | otherwise               = Finite r   where r = p / q@@ -142,22 +141,26 @@ -- Internal function that is not quite curvature or squaredCurvature but lets -- us get there by either taking the square root of the numerator or squaring -- the denominator respectively.-curvaturePair :: (Num t, Num (Scalar t), VectorSpace t)-    => Segment Closed (t, t) -> Scalar t -> (t, t)-curvaturePair (Linear _)    t = (0,1) -- Linear segments always have zero curvature (infinite radius).-curvaturePair (Cubic b c (OffsetClosed d)) t = ((x'*y'' - y'*x''), (x'*x' + y'*y')^(3 :: Integer))+curvaturePair :: Num n+    => Segment Closed V2 n -> n -> V2 n+curvaturePair (Linear _) _ = V2 0 1 -- Linear segments always have zero curvature (infinite radius).+curvaturePair seg@(Cubic b c (OffsetClosed d)) t+  = V2 (x'*y'' - y'*x'') ((x'*x' + y'*y')^(3 :: Int))   where-    (x' ,y' ) = firstDerivative  b c d t -- TODO: Use the generalized unr2-    (x'',y'') = secondDerivative b c d t--    firstDerivative b c d t = (3*(3*tt-4*t+1))*^b + (3*(2-3*t)*t)*^c + (3*tt)*^d-      where-        tt = t * t--    secondDerivative b c d t = (6*(3*t-2))*^b + (6-18*t)*^c + (6*t)*^d+    (V2 x'  y' )     = seg `tangentAtParam` t+    (V2 x'' y'')     = secondDerivative+    secondDerivative = (6*(3*t-2))*^b ^+^ (6-18*t)*^c ^+^ (6*t)*^d  -- TODO: We should be able to generalize this to higher dimensions.  See -- <http://en.wikipedia.org/wiki/Curvature> -- -- TODO: I'm not sure what the best way to generalize squaredCurvature to other spaces is. +-- curvaturePair :: (Num t, Num (Scalar t), VectorSpace t)+--     => Segment Closed (t, t) -> Scalar t -> (t, t)+-- curvaturePair (Linear _) _ = (0,1) -- Linear segments always have zero curvature (infinite radius).+-- curvaturePair seg@(Cubic b c (OffsetClosed d)) t = ((x'*y'' - y'*x''), (x'*x' + y'*y')^(3 :: Integer))+--   where+--     (x' ,y' ) = seg `tangentAtParam` t+--     (x'',y'') = secondDerivative+--     secondDerivative = (6*(3*t-2))*^b ^+^ (6-18*t)*^c ^+^ (6*t)*^d
+ src/Diagrams/TwoD/Deform.hs view
@@ -0,0 +1,41 @@+module Diagrams.TwoD.Deform where++import           Control.Lens++import           Diagrams.Deform++import           Linear.V2+import           Linear.Vector++-- | The parallel projection onto the plane x=0+parallelX0 :: (R1 v, Num n) => Deformation v v n+parallelX0 = Deformation (_x .~ 0)++-- | The perspective division onto the plane x=1 along lines going+-- through the origin.+perspectiveX1 :: (R1 v, Functor v, Fractional n) => Deformation v v n+perspectiveX1 = Deformation $ \p -> p ^/ (p ^. _x)++-- | The parallel projection onto the plane y=0+parallelY0 :: (R2 v, Num n) => Deformation v v n+parallelY0 = Deformation (_y .~ 0)++-- | The perspective division onto the plane y=1 along lines going+-- through the origin.+perspectiveY1 :: (R2 v, Functor v, Floating n) => Deformation v v n+perspectiveY1 = Deformation $ \p -> p ^/ (p ^. _y)++-- | The viewing transform for a viewer facing along the positive X+-- axis.  X coördinates stay fixed, while Y coördinates are compressed+-- with increasing distance.  @asDeformation (translation unitX) <>+-- parallelX0 <> frustrumX = perspectiveX1@+facingX :: (R1 v, Functor v, Fractional n) => Deformation v v n+facingX = Deformation $+  \p -> let x = p ^. _x+        in  p ^/ x & _x .~ x++facingY :: (R2 v, Functor v, Fractional n) => Deformation v v n+facingY = Deformation $+  \p -> let y = p ^. _y+        in  p ^/ y & _y .~ y+
src/Diagrams/TwoD/Ellipse.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-} {-# LANGUAGE TypeSynonymInstances  #-} ----------------------------------------------------------------------------- -- |@@ -24,27 +25,29 @@  import           Diagrams.Core +import           Diagrams.Angle import           Diagrams.Located        (at)-import           Diagrams.Path+import           Diagrams.Trail          (glueTrail) import           Diagrams.TrailLike import           Diagrams.TwoD.Arc import           Diagrams.TwoD.Transform import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Vector    (xDir) import           Diagrams.Util  -- | A circle of radius 1, with center at the origin.-unitCircle :: (TrailLike t, V t ~ R2) => t-unitCircle = trailLike $ arcT 0 (tau::Rad) `at` (p2 (1,0))+unitCircle :: (TrailLike t, V t ~ V2, N t ~ n) => t+unitCircle = trailLike $ glueTrail (arcT xDir fullTurn) `at` p2 (1,0)  -- | A circle of the given radius, centered at the origin.  As a path, --   it begins at (r,0).-circle :: (TrailLike t, V t ~ R2, Transformable t) => Double -> t+circle :: (TrailLike t, V t ~ V2, N t ~ n, Transformable t) => n -> t circle d = unitCircle # scale d  -- | @ellipse e@ constructs an ellipse with eccentricity @e@ by --   scaling the unit circle in the X direction.  The eccentricity must --   be within the interval [0,1).-ellipse :: (TrailLike t, V t ~ R2, Transformable t) => Double -> t+ellipse :: (TrailLike t, V t ~ V2, N t ~ n, Transformable t) => n -> t ellipse e     | e >= 0 && e < 1  = scaleX (sqrt (1 - e*e)) unitCircle     | otherwise        = error "Eccentricity of ellipse must be >= 0 and < 1."@@ -52,5 +55,5 @@ -- | @ellipseXY x y@ creates an axis-aligned ellipse, centered at the --   origin, with radius @x@ along the x-axis and radius @y@ along the --   y-axis.-ellipseXY :: (TrailLike t, V t ~ R2, Transformable t) => Double -> Double -> t+ellipseXY :: (TrailLike t, V t ~ V2, N t ~ n, Transformable t) => n -> n -> t ellipseXY x y = unitCircle # scaleX x # scaleY y
src/Diagrams/TwoD/Image.hs view
@@ -1,7 +1,12 @@-{-# LANGUAGE TypeFamilies-           , FlexibleContexts-           , MultiParamTypeClasses-  #-}+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE EmptyDataDecls        #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE UndecidableInstances  #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Image@@ -9,73 +14,155 @@ -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com ----- Importing external images into diagrams.---+-- Importing external images into diagrams.  Usage example: To create+-- a diagram from an embedded image with width 1 and height set+-- according to the aspect ratio, use @image img # scaleUToX 1@, where+-- @img@ is a value of type @DImage n e@, created with a function like+-- 'loadImageEmb', 'loadImageExt', or 'raster'. -----------------------------------------------------------------------------  module Diagrams.TwoD.Image     (-      Image(..)+      DImage(..), ImageData(..)+    , Embedded, External, Native     , image+    , embeddedImage+    , loadImageEmb+    , loadImageEmbBS+    , loadImageExt+    , uncheckedImageRef+    , raster+    , rasterDia     ) where -import Diagrams.Core+import           Codec.Picture -import Diagrams.Path-import Diagrams.TwoD.Types-import Diagrams.TwoD.Path-import Diagrams.TwoD.Shapes-import Diagrams.TwoD.Size (SizeSpec2D(..))+import           Data.Colour          (AlphaColour)+import           Data.Kind            (Type)+import           Data.Semigroup+import           Data.Typeable        (Typeable) -import Data.AffineSpace ((.-.))+import           Diagrams.Core -import Data.Semigroup+import           Diagrams.Attributes  (colorToSRGBA)+import           Diagrams.Path        (Path)+import           Diagrams.Query+import           Diagrams.TwoD.Path   (isInsideEvenOdd)+import           Diagrams.TwoD.Shapes (rect)+import           Diagrams.TwoD.Types --- | An external image primitive, representing an image the backend---   should import from another file when rendering.-data Image = Image { imgFile   :: FilePath-                   , imgSize   :: SizeSpec2D-                   , imgTransf :: T2-                   }+import           Data.ByteString -type instance V Image = R2+import           Linear.Affine -instance Transformable Image where-  transform t1 (Image file sz t2) = Image file sz (t1 <> t2)+data Embedded deriving Typeable+data External deriving Typeable+data Native (t :: Type) deriving Typeable -instance IsPrim Image+-- | 'ImageData' is either a JuicyPixels @DynamicImage@ tagged as 'Embedded' or+--   a reference tagged as 'External'. Additionally 'Native' is provided for+--   external libraries to hook into.+data ImageData :: Type -> Type where+  ImageRaster :: DynamicImage -> ImageData Embedded+  ImageRef    :: FilePath -> ImageData External+  ImageNative :: t -> ImageData (Native t) -instance HasOrigin Image where+-------------------------------------------------------------------------------+-- | An image primitive, the two ints are width followed by height.+--   Will typically be created by @loadImageEmb@ or @loadImageExt@ which,+--   will handle setting the width and height to the actual width and height+--   of the image.+data DImage :: Type -> Type -> Type where+  DImage :: ImageData t -> Int -> Int -> Transformation V2 n -> DImage n t+  deriving Typeable++type instance V (DImage n a) = V2+type instance N (DImage n a) = n++instance RealFloat n => HasQuery (DImage n a) Any where+  getQuery (DImage _ w h _) = -- transform t $+    Query $ \p -> Any (isInsideEvenOdd r p)+    where+    r = rectPath (fromIntegral w) (fromIntegral h)++instance Fractional n => Transformable (DImage n a) where+  transform t1 (DImage iD w h t2) = DImage iD w h (t1 <> t2)++instance Fractional n => HasOrigin (DImage n a) where   moveOriginTo p = translate (origin .-. p) -instance Renderable Image NullBackend where-  render _ _ = mempty+-- | Make a 'DImage' into a 'Diagram'.+image :: (TypeableFloat n, Typeable a, Renderable (DImage n a) b)+      => DImage n a -> QDiagram b V2 n Any+image img+  = mkQD (Prim img)+         (getEnvelope r)+         (getTrace r)+         mempty+         (Query $ \p -> Any (isInsideEvenOdd r p))+  where+    r = rectPath (fromIntegral w) (fromIntegral h)+    -- should we use the transform here?+    DImage _ w h _ = img --- See Note [Image size specification]+rectPath :: RealFloat n => n -> n -> Path V2 n+rectPath = rect --- | Take an external image from the specified file and turn it into a---   diagram with the specified width and height, centered at the---   origin.  Note that the image's aspect ratio will be preserved; if---   the specified width and height have a different ratio than the---   image's aspect ratio, there will be extra space in one dimension.-image :: (Renderable Image b) => FilePath -> Double -> Double -> Diagram b R2-image file w h = mkQD (Prim (Image file (Dims w h) mempty))-                      (getEnvelope r)-                      (getTrace r)-                      mempty-                      (Query $ \p -> Any (isInsideEvenOdd p r))-  where r :: Path R2-        r = rect w h+-- | Read a JuicyPixels @DynamicImage@ and wrap it in a 'DImage'.+--   The width and height of the image are set to their actual values.+embeddedImage :: Num n => DynamicImage -> DImage n Embedded+embeddedImage img = DImage (ImageRaster img) w h mempty+  where+    w = dynamicMap imageWidth img+    h = dynamicMap imageHeight img -{- ~~~~ Note [Image size specification]+-- | Use JuicyPixels to read a file in any format and wrap it in a 'DImage'.+--   The width and height of the image are set to their actual values.+loadImageEmb :: Num n => FilePath -> IO (Either String (DImage n Embedded))+loadImageEmb path = fmap embeddedImage `fmap` readImage path -It's tempting to make 'image' take a SizeSpec2D instead of two-Doubles.  For example, if I know I want the image to be x units wide-but I don't know the original aspect ratio of the image, I'd like to-be able to just say "make it x units wide".  The problem is that-diagrams would then not know how tall the image is until rendering-time (at least, not without unsafePerformIO yuckiness).  A more-general solution will have to wait until we can specify constraints-and solve them later.+-- | A pure variant of 'loadImageEmb'+loadImageEmbBS :: Num n => ByteString -> Either String (DImage n Embedded)+loadImageEmbBS bs = embeddedImage `fmap` decodeImage bs --}+-- | Check that a file exists, and use JuicyPixels to figure out+--   the right size, but save a reference to the image instead+--   of the raster data+loadImageExt :: Num n => FilePath -> IO (Either String (DImage n External))+loadImageExt path = do+  dImg <- readImage path+  return $ case dImg of+    Left msg  -> Left msg+    Right img -> Right $ DImage (ImageRef path) w h mempty+      where+        w = dynamicMap imageWidth img+        h = dynamicMap imageHeight img++-- | Make an "unchecked" image reference; have to specify a+--   width and height. Unless the aspect ratio of the external+--   image is the w :: h, then the image will be distorted.+uncheckedImageRef :: Num n => FilePath -> Int -> Int -> DImage n External+uncheckedImageRef path w h = DImage (ImageRef path) w h mempty++-- | Crate a diagram from raw raster data.+rasterDia :: (TypeableFloat n, Renderable (DImage n Embedded) b)+          => (Int -> Int -> AlphaColour Double) -> Int -> Int -> QDiagram b V2 n Any+rasterDia f w h = image $ raster f w h++-- | Create an image "from scratch" by specifying the pixel data+raster :: Num n => (Int -> Int -> AlphaColour Double) -> Int -> Int -> DImage n Embedded+raster f w h = DImage (ImageRaster (ImageRGBA8 img)) w h mempty+  where+    img = generateImage g w h+    g x y = fromAlphaColour $ f x y++fromAlphaColour :: AlphaColour Double -> PixelRGBA8+fromAlphaColour c = PixelRGBA8 r g b a+  where+    (r, g, b, a) = (int r', int g', int b', int a')+    (r', g', b', a') = colorToSRGBA c+    int x = round (255 * x)++instance Fractional n => (Renderable (DImage n a) NullBackend) where+  render _ _ = mempty+
src/Diagrams/TwoD/Model.hs view
@@ -1,5 +1,8 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeFamilies          #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Model@@ -8,80 +11,163 @@ -- Maintainer  :  diagrams-discuss@googlegroups.com -- -- Tools for visualizing diagrams' internal model: local origins,--- envelopes, /etc./+-- envelopes, traces, /etc./ -- ----------------------------------------------------------------------------- module Diagrams.TwoD.Model        ( -- * Showing the local origin          showOrigin        , showOrigin'-       , OriginOpts(..)+       , OriginOpts(..), oColor, oScale, oMinSize++         -- * Showing an approximation of the envelope+       , showEnvelope+       , showEnvelope'+       , EnvelopeOpts(..), eColor, eLineWidth, ePoints++         -- * Showing an approximation of the trace+       , showTrace+       , showTrace'+       , TraceOpts(..), tColor, tScale, tMinSize, tPoints++         -- * Showing labels of all named subdiagrams        , showLabels        ) where +import           Control.Arrow            (second)+import           Control.Lens             (makeLenses, (^.))+import           Data.Colour              (Colour)+import           Data.Colour.Names+import           Data.Default+import           Data.List                (intercalate)+import qualified Data.Map                 as M+import           Data.Maybe               (catMaybes)+import           Data.Semigroup++import           Diagrams.Attributes+import           Diagrams.Combinators     (atPoints) import           Diagrams.Core import           Diagrams.Core.Names-+import           Diagrams.CubicSpline import           Diagrams.Path--import           Diagrams.Attributes+import           Diagrams.TwoD.Attributes import           Diagrams.TwoD.Ellipse import           Diagrams.TwoD.Path-import           Diagrams.TwoD.Size    (size2D) import           Diagrams.TwoD.Text+import           Diagrams.TwoD.Transform  (rotateBy) import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Vector     (unitX) import           Diagrams.Util -import           Control.Arrow         (second)-import           Data.AffineSpace      ((.-.))-import           Data.Default.Class-import           Data.Semigroup-import           Data.VectorSpace      ((^*))--import qualified Data.Map              as M--import           Data.Colour           (Colour)-import           Data.Colour.Names+import           Linear.Affine+import           Linear.Vector  ------------------------------------------------------------ -- Marking the origin ------------------------------------------------------------ +data OriginOpts n = OriginOpts+  { _oColor   :: Colour Double+  , _oScale   :: n+  , _oMinSize :: n+  }++makeLenses ''OriginOpts++instance Fractional n => Default (OriginOpts n) where+  def = OriginOpts red (1/50) 0.001++data EnvelopeOpts n = EnvelopeOpts+  { _eColor     :: Colour Double+  , _eLineWidth :: Measure n+  , _ePoints    :: Int+  }++makeLenses ''EnvelopeOpts++instance OrderedField n => Default (EnvelopeOpts n) where+  def = EnvelopeOpts red medium 32++data TraceOpts n = TraceOpts+  { _tColor   :: Colour Double+  , _tScale   :: n+  , _tMinSize :: n+  , _tPoints  :: Int+  }++makeLenses ''TraceOpts++instance Floating n => Default (TraceOpts n) where+  def = TraceOpts red (1/100) 0.001 64+ -- | Mark the origin of a diagram by placing a red dot 1/50th its size.-showOrigin :: (Renderable (Path R2) b, Backend b R2, Monoid' m)-           => QDiagram b R2 m -> QDiagram b R2 m+showOrigin :: (TypeableFloat n, Renderable (Path V2 n) b, Monoid' m)+           => QDiagram b V2 n m -> QDiagram b V2 n m showOrigin = showOrigin' def  -- | Mark the origin of a diagram, with control over colour and scale -- of marker dot.-showOrigin' :: (Renderable (Path R2) b, Backend b R2, Monoid' m)-           => OriginOpts -> QDiagram b R2 m -> QDiagram b R2 m+showOrigin' :: (TypeableFloat n, Renderable (Path V2 n) b, Monoid' m)+           => OriginOpts n -> QDiagram b V2 n m -> QDiagram b V2 n m showOrigin' oo d = o <> d-  where o     = stroke (circle sz)-                # fc (oColor oo)-                # lw 0-                # fmap (const mempty)-        (w,h) = size2D d ^* oScale oo-        sz = maximum [w, h, oMinSize oo]+  where o      = strokeP (circle sz)+                   # fc (oo^.oColor)+                   # lw none+                   # fmap (const mempty)+        V2 w h = oo^.oScale *^ size d+        sz     = maximum [w, h, oo^.oMinSize] -data OriginOpts = OriginOpts { oColor   :: Colour Double-                             , oScale   :: Double-                             , oMinSize :: Double-                             }+-- | Mark the envelope with an approximating cubic spline with control +--   over the color, line width and number of points.+showEnvelope' :: (Enum n, TypeableFloat n, Renderable (Path V2 n) b) +              => EnvelopeOpts n -> QDiagram b V2 n Any -> QDiagram b V2 n Any+showEnvelope' opts d = cubicSpline True pts # lc (opts^.eColor)+                                            # lw w <> d+  where+    pts = catMaybes [envelopePMay v d | v <- map (`rotateBy` unitX) [0,inc..top]]+    w   = opts ^. eLineWidth+    inc = 1 / fromIntegral (opts^.ePoints)+    top = 1 - inc -instance Default OriginOpts where-  def = OriginOpts red (1/50) 0.001 +-- | Mark the envelope with an approximating cubic spline+--   using 32 points, medium line width and red line color.+showEnvelope :: (Enum n, TypeableFloat n, Renderable (Path V2 n) b)+             => QDiagram b V2 n Any -> QDiagram b V2 n Any+showEnvelope = showEnvelope' def +-- | Mark the trace of a diagram, with control over colour and scale+-- of marker dot and the number of points on the trace.+showTrace' :: (Enum n, TypeableFloat n, Renderable (Path V2 n) b) +          => TraceOpts n -> QDiagram b V2 n Any -> QDiagram b V2 n Any+showTrace' opts d =  atPoints ps (repeat pt) <> d+  where+    ps = concatMap p ts+    ts = zip rs vs+    p (r, v) = [origin .+^ (s *^ v) | s <- r]+    vs = map (`rotateBy` unitX) [0, inc..top]+    rs = [getSortedList $ (appTrace . getTrace) d origin v | v <- vs]+    pt = circle sz # fc (opts^.tColor) # lw none+    V2 w h = opts^.tScale *^ size d+    sz     = maximum [w, h, opts^.tMinSize]+    inc = 1 / fromIntegral (opts^.tPoints)+    top = 1 - inc++-- | Mark the trace of a diagram by placing 64 red dots 1/100th its size+--   along the trace.+showTrace :: (Enum n, TypeableFloat n, Renderable (Path V2 n) b) +          => QDiagram b V2 n Any -> QDiagram b V2 n Any+showTrace = showTrace' def+ ------------------------------------------------------------ -- Labeling named points ------------------------------------------------------------ -showLabels :: (Renderable Text b, Backend b R2)-           => QDiagram b R2 m -> QDiagram b R2 Any+showLabels :: (TypeableFloat n, Renderable (Text n) b, Semigroup m)+           => QDiagram b V2 n m -> QDiagram b V2 n Any showLabels d =              ( mconcat-             . map (\(n,p) -> text (show n) # translate (p .-. origin))+             . map (\(n,p) -> text (simpleName n) # translate (p .-. origin))              . concatMap (\(n,ps) -> zip (repeat n) ps)              . (map . second . map) location              . M.assocs@@ -89,4 +175,6 @@              ) <>              fmap (const (Any False)) d   where-    SubMap m = subMap d+    SubMap m = d^.subMap+    simpleName (Name ns) = intercalate " .> " $ map simpleAName ns+    simpleAName (AName n) = show n
src/Diagrams/TwoD/Offset.hs view
@@ -1,43 +1,88 @@-{-# LANGUAGE GADTs #-}+{-# LANGUAGE ConstraintKinds      #-}+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE GADTs                #-}+{-# LANGUAGE StandaloneDeriving   #-}+{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeOperators        #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ViewPatterns         #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Offset--- Copyright   :  (c) 2012 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com ----- Compute offsets to segments in two dimensions.--- +-- Compute offsets to segments in two dimensions.  More details can be+-- found in the manual at+-- <https://diagrams.github.io/doc/manual.html#offsets-of-segments-trails-and-paths>.+-- ------------------------------------------------------------------------------module Diagrams.TwoD.Offset -    ( offsetSegment+module Diagrams.TwoD.Offset+    (+      -- * Offsets++      offsetSegment++    , OffsetOpts(..), offsetJoin, offsetMiterLimit, offsetEpsilon+    , offsetTrail+    , offsetTrail'+    , offsetPath+    , offsetPath'++      -- * Expansions++    , ExpandOpts(..), expandJoin, expandMiterLimit, expandCap, expandEpsilon+    , expandTrail+    , expandTrail'+    , expandPath+    , expandPath'+     ) where -import Data.AffineSpace-import Data.Monoid.Inf-import Data.VectorSpace+import           Control.Applicative+import           Control.Lens            hiding (at)+import           Prelude -import Diagrams.Core+import           Data.Maybe              (catMaybes)+import           Data.Monoid+import           Data.Monoid.Inf -import Diagrams.Parametric-import Diagrams.Path-import Diagrams.Segment-import Diagrams.Trail-import Diagrams.TwoD.Curvature-import Diagrams.TwoD.Transform-import Diagrams.TwoD.Types+import           Data.Default -perp :: R2 -> R2-perp v = rotateBy (-1/4) v+import           Diagrams.Core -unitPerp :: R2 -> R2-unitPerp = normalized . perp+import           Diagrams.Attributes+import           Diagrams.Direction+import           Diagrams.Located+import           Diagrams.Parametric+import           Diagrams.Path+import           Diagrams.Segment+import           Diagrams.Trail          hiding (isLoop, offset)+import           Diagrams.TrailLike+import           Diagrams.TwoD.Arc+import           Diagrams.TwoD.Curvature+import           Diagrams.TwoD.Path      ()+import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Vector    hiding (e) -perpAtParam :: Segment Closed R2 -> Double -> R2-perpAtParam   (Linear (OffsetClosed a))  t = unitPerp a -perpAtParam s@(Cubic _ _ _)              t = unitPerp a+import           Linear.Affine+import           Linear.Metric+import           Linear.Vector++unitPerp :: OrderedField n => V2 n -> V2 n+unitPerp = signorm . perp++perpAtParam :: OrderedField n => Segment Closed V2 n -> n -> V2 n+perpAtParam (Linear (OffsetClosed a)) _ = negated $ unitPerp a+perpAtParam cubic t                     = negated $ unitPerp a   where-    (Cubic a _ _) = snd $ splitAtParam s t+    (Cubic a _ _) = snd $ splitAtParam cubic t  -- | Compute the offset of a segment.  Given a segment compute the offset --   curve that is a fixed distance from the original curve.  For linear@@ -46,7 +91,9 @@ --   length. -- --   Cubic segments require a search for a subdivision of cubic segments that---   gives an approximation of the offset within the given epsilon tolerance.+--   gives an approximation of the offset within the given epsilon factor+--   (the given epsilon factor is applied to the radius giving a concrete epsilon+--   value). --   We must do this because the offset of a cubic is not a cubic itself (the --   degree of the curve increases).  Cubics do, however, approach constant --   curvature as we subdivide.  In light of this we scale the handles of@@ -57,39 +104,100 @@ --   In the following example the blue lines are the original segments and --   the alternating green and red lines are the resulting offset trail segments. -----   <<diagrams/cubicOffsetExample.svg#diagram=cubicOffsetExample&width=600>>+--   <<diagrams/src_Diagrams_TwoD_Offset_cubicOffsetExample.svg#diagram=cubicOffsetExample&width=600>> -- --   Note that when the original curve has a cusp, the offset curve forms a --   radius around the cusp, and when there is a loop in the original curve, --   there can be two cusps in the offset curve. ---offsetSegment :: Double     -- ^ Epsilon value that represents the maximum ++-- | Options for specifying line join and segment epsilon for an offset+--   involving multiple segments.+data OffsetOpts d = OffsetOpts+    { _offsetJoin       :: LineJoin+    , _offsetMiterLimit :: d+    , _offsetEpsilon    :: d+    }++deriving instance Eq d => Eq (OffsetOpts d)+deriving instance Show d => Show (OffsetOpts d)++makeLensesWith (lensRules & generateSignatures .~ False) ''OffsetOpts++-- | Specifies the style of join for between adjacent offset segments.+offsetJoin :: Lens' (OffsetOpts d) LineJoin++-- | Specifies the miter limit for the join.+offsetMiterLimit :: Lens' (OffsetOpts d) d++-- | Epsilon perimeter for 'offsetSegment'.+offsetEpsilon :: Lens' (OffsetOpts d) d++-- | The default offset options use the default 'LineJoin' ('LineJoinMiter'), a+--   miter limit of 10, and epsilon factor of 0.01.+instance Fractional d => Default (OffsetOpts d) where+    def = OffsetOpts def 10 0.01++-- | Options for specifying how a 'Trail' should be expanded.+data ExpandOpts d = ExpandOpts+    { _expandJoin       :: LineJoin+    , _expandMiterLimit :: d+    , _expandCap        :: LineCap+    , _expandEpsilon    :: d+    } deriving (Eq, Show)++makeLensesWith (lensRules & generateSignatures .~ False) ''ExpandOpts++-- | Specifies the style of join for between adjacent offset segments.+expandJoin :: Lens' (ExpandOpts d) LineJoin++-- | Specifies the miter limit for the join.+expandMiterLimit :: Lens' (ExpandOpts d) d++-- | Specifies how the ends are handled.+expandCap :: Lens' (ExpandOpts d) LineCap++-- | Epsilon perimeter for 'offsetSegment'.+expandEpsilon :: Lens' (ExpandOpts d) d++++-- | The default 'ExpandOpts' is the default 'LineJoin' ('LineJoinMiter'),+--   miter limit of 10, default 'LineCap' ('LineCapButt'), and epsilon factor+--   of 0.01.+instance (Fractional d) => Default (ExpandOpts d) where+    def = ExpandOpts def 10 def 0.01++offsetSegment :: RealFloat n+              => n   -- ^ Epsilon factor that when multiplied to the+                            --   absolute value of the radius gives a+                            --   value that represents the maximum                             --   allowed deviation from the true offset.  In                             --   the current implementation each result segment                             --   should be bounded by arcs that are plus or-                            --   minus epsilon from the radius of curvature of+                            --   minus epsilon factor from the radius of curvature of                             --   the offset.-              -> Double     -- ^ Offset from the original segment, positive is+              -> n   -- ^ Offset from the original segment, positive is                             --   on the right of the curve, negative is on the                             --   left.-              -> Segment Closed R2 -- ^ Original segment-              -> (Point R2, Trail R2) -- ^ Resulting offset point and trail.-offsetSegment _       r s@(Linear (OffsetClosed a))    = (origin .+^ va, trailFromSegments [s])-  where va = r *^ unitPerp a+              -> Segment Closed V2 n  -- ^ Original segment+              -> Located (Trail V2 n) -- ^ Resulting located (at the offset) trail.+offsetSegment _       r s@(Linear (OffsetClosed a))    = trailFromSegments [s] `at` origin .+^ va+  where va = (-r) *^ unitPerp a -offsetSegment epsilon r s@(Cubic a b (OffsetClosed c)) = (origin .+^ va, t)+offsetSegment epsilon r s@(Cubic a b (OffsetClosed c)) = t `at` origin .+^ va   where     t = trailFromSegments (go (radiusOfCurvature s 0.5))     -- Perpendiculars to handles.-    va = r *^ unitPerp a-    vc = r *^ unitPerp (c - b)+    va = (-r) *^ unitPerp a+    vc = (-r) *^ unitPerp (c ^-^ b)     -- Split segments.-    ss = (\(a,b) -> [a,b]) $ splitAtParam s 0.5-    subdivided = concatMap (trailSegments . snd . offsetSegment epsilon r) ss+    ss = (\(x,y) -> [x,y]) $ splitAtParam s 0.5+    subdivided = concatMap (trailSegments . unLoc . offsetSegment epsilon r) ss      -- Offset with handles scaled based on curvature.-    offset factor = bezier3 (a^*factor) ((b - c)^*factor + c + vc - va) (c + vc - va)- +    offset factor = bezier3 (a^*factor) ((b ^-^ c)^*factor ^+^ c ^+^ vc ^-^ va) (c ^+^ vc ^-^ va)+     -- We observe a corner.  Subdivide right away.     go (Finite 0) = subdivided     -- We have some curvature@@ -104,30 +212,355 @@         --         o = offset $ case roc of               Infinity  -> 1          -- Do the right thing.-              Finite sr -> 1 + r / sr +              Finite sr -> 1 + r / sr -        close = and [epsilon > (magnitude (p o + va - p s - pp s))-                    | t <- [0.25, 0.5, 0.75]-                    , let p = (`atParam` t)-                    , let pp = (r *^) . (`perpAtParam` t)+        close = and [epsilon * abs r > norm (p o ^+^ va ^-^ p s ^-^ pp s)+                    | t' <- [0.25, 0.5, 0.75]+                    , let p = (`atParam` t')+                    , let pp = (r *^) . (`perpAtParam` t')                     ]   -- > import Diagrams.TwoD.Offset -- >--- > showExample :: Segment R2 -> Diagram SVG R2--- > showExample s = pad 1.1 . centerXY $ d # lc blue # lw 0.1 <> d' # lw 0.1+-- > showExample :: Segment Closed V2 Double -> Diagram SVG+-- > showExample s = pad 1.1 . centerXY $ d # lc blue # lw thick <> d' # lw thick -- >   where--- >       d  = stroke $ Path [(origin, Trail [s] False)]--- >       d' = mconcat . zipWith lc colors . map stroke . uncurry explodeTrail+-- >       d  = strokeP . fromSegments $ [s]+-- >       d' = mconcat . zipWith lc colors . map strokeP . explodeTrail -- >          $ offsetSegment 0.1 (-1) s--- >            +-- > -- >       colors = cycle [green, red]--- > --- > cubicOffsetExample :: Diagram SVG R2+-- >+-- > cubicOffsetExample :: Diagram SVG -- > cubicOffsetExample = hcat . map showExample $--- >         [ Cubic (10 &  0) (  5  & 18) (10 & 20)--- >         , Cubic ( 0 & 20) ( 10  & 10) ( 5 & 10)--- >         , Cubic (10 & 20) (  0  & 10) (10 &  0)--- >         , Cubic (10 & 20) ((-5) & 10) (10 &  0)+-- >         [ bezier3 (10 ^&  0) (  5  ^& 18) (10 ^& 20)+-- >         , bezier3 ( 0 ^& 20) ( 10  ^& 10) ( 5 ^& 10)+-- >         , bezier3 (10 ^& 20) (  0  ^& 10) (10 ^&  0)+-- >         , bezier3 (10 ^& 20) ((-5) ^& 10) (10 ^&  0) -- >         ]++-- Similar to (=<<).  This is when we want to map a function across something+-- located, but the result of the mapping will be transformable so we can+-- collapse the Located into the result.  This assumes that Located has the+-- meaning of merely taking something that cannot be translated and lifting+-- it into a space with translation.+bindLoc :: (Transformable b, V a ~ V b, N a ~ N b, V a ~ V2, N a ~ n, Num n) => (a -> b) -> Located a -> b+bindLoc f = join' . mapLoc f+  where+    join' (viewLoc -> (p,a)) = translate (p .-. origin) a++-- While we build offsets and expansions we will use the [Located (Segment Closed v)]+-- and [Located (Trail V2 n)] intermediate representations.+locatedTrailSegments :: OrderedField n+                     => Located (Trail V2 n) -> [Located (Segment Closed V2 n)]+locatedTrailSegments t = zipWith at (trailSegments (unLoc t)) (trailPoints t)++-- | Offset a 'Trail' with options and by a given radius.  This generates a new+--   trail that is always radius 'r' away from the given 'Trail' (depending on+--   the line join option) on the right.+--+--   The styles applied to an outside corner can be seen here (with the original+--   trail in blue and the result of 'offsetTrail'' in green):+--+--   <<diagrams/src_Diagrams_TwoD_Offset_offsetTrailExample.svg#diagram=offsetTrailExample&width=600>>+--+--   When a negative radius is given, the offset trail will be on the left:+--+--   <<diagrams/src_Diagrams_TwoD_Offset_offsetTrailLeftExample.svg#diagram=offsetTrailLeftExample&width=200>>+--+--   When offseting a counter-clockwise loop a positive radius gives an outer loop+--   while a negative radius gives an inner loop (both counter-clockwise).+--+--   <<diagrams/src_Diagrams_TwoD_Offset_offsetTrailOuterExample.svg#diagram=offsetTrailOuterExample&width=300>>+--+offsetTrail' :: RealFloat n+             => OffsetOpts n+             -> n -- ^ Radius of offset.  A negative value gives an offset on+                         --   the left for a line and on the inside for a counter-clockwise+                         --   loop.+             -> Located (Trail V2 n)+             -> Located (Trail V2 n)+offsetTrail' opts r t = joinSegments eps j isLoop (opts^.offsetMiterLimit) r ends . offset $ t+    where+      eps = opts^.offsetEpsilon+      offset = map (bindLoc (offsetSegment eps r)) . locatedTrailSegments+      ends | isLoop    = (\(a:as) -> as ++ [a]) . trailPoints $ t+           | otherwise = tail . trailPoints $ t+      j = fromLineJoin (opts^.offsetJoin)++      isLoop = withTrail (const False) (const True) (unLoc t)++-- | Offset a 'Trail' with the default options and a given radius.  See 'offsetTrail''.+offsetTrail :: RealFloat n => n -> Located (Trail V2 n) -> Located (Trail V2 n)+offsetTrail = offsetTrail' def++-- | Offset a 'Path' by applying 'offsetTrail'' to each trail in the path.+offsetPath' :: RealFloat n => OffsetOpts n -> n -> Path V2 n -> Path V2 n+offsetPath' opts r = mconcat+                   . map (bindLoc (trailLike . offsetTrail' opts r) . (`at` origin))+                   . op Path++-- | Offset a 'Path' with the default options and given radius.  See 'offsetPath''.+offsetPath :: RealFloat n => n -> Path V2 n -> Path V2 n+offsetPath = offsetPath' def++-- TODO: Include arrowheads on examples to indicate direction so the "left" and+-- "right" make sense.+--+-- > import Diagrams.TwoD.Offset+-- > import Data.Default+-- >+-- > corner :: (OrderedField n) => Located (Trail V2 n)+-- > corner = fromVertices (map p2 [(0, 0), (10, 0), (5, 6)]) `at` origin+-- >+-- > offsetTrailExample :: Diagram SVG+-- > offsetTrailExample = pad 1.1 . centerXY . lwO 3 . hcat' (def & sep .~ 1 )+-- >                    . map (uncurry showStyle)+-- >                    $ [ (LineJoinMiter, "LineJoinMiter")+-- >                      , (LineJoinRound, "LineJoinRound")+-- >                      , (LineJoinBevel, "LineJoinBevel")+-- >                      ]+-- >  where+-- >    showStyle j s = centerXY (trailLike corner # lc blue+-- >               <> trailLike (offsetTrail' (def & offsetJoin .~ j) 2 corner) # lc green)+-- >            === (strutY 3 <> text s # font "Helvetica" # bold)+-- >+-- > offsetTrailLeftExample :: Diagram SVG+-- > offsetTrailLeftExample = pad 1.1 . centerXY . lwO 3+-- >                        $ (trailLike c # lc blue)+-- >                        <> (lc green . trailLike+-- >                         . offsetTrail' (def & offsetJoin .~ LineJoinRound) (-2) $ c)+-- >   where+-- >     c = reflectY corner+-- >+-- > offsetTrailOuterExample :: Diagram SVG+-- > offsetTrailOuterExample = pad 1.1 . centerXY . lwO 3+-- >                         $ (trailLike c # lc blue)+-- >                         <> (lc green . trailLike+-- >                          . offsetTrail' (def & offsetJoin .~ LineJoinRound) 2 $ c)+-- >   where+-- >     c = hexagon 5++withTrailL :: (Located (Trail' Line V2 n) -> r) -> (Located (Trail' Loop V2 n) -> r) -> Located (Trail V2 n) -> r+withTrailL f g l = withTrail (f . (`at` p)) (g . (`at` p)) (unLoc l)+  where+    p = loc l++-- | Expand a 'Trail' with the given options and radius 'r' around a given 'Trail'.+--   Expanding can be thought of as generating the loop that, when filled, represents+--   stroking the trail with a radius 'r' brush.+--+--   The cap styles applied to an outside corner can be seen here (with the original+--   trail in white and the result of 'expandTrail'' filled in green):+--+--   <<diagrams/src_Diagrams_TwoD_Offset_expandTrailExample.svg#diagram=expandTrailExample&width=600>>+--+--   Loops result in a path with an inner and outer loop:+--+--   <<diagrams/src_Diagrams_TwoD_Offset_expandLoopExample.svg#diagram=expandLoopExample&width=300>>+--+expandTrail' :: (OrderedField n, RealFloat n, RealFrac n)+             => ExpandOpts n+             -> n  -- ^ Radius of offset.  Only non-negative values allowed.+                        --   For a line this gives a loop of the offset.  For a+                        --   loop this gives two loops, the outer counter-clockwise+                        --   and the inner clockwise.+             -> Located (Trail V2 n)+             -> Path V2 n+expandTrail' o r t+  | r < 0     = error "expandTrail' with negative radius"+                -- TODO: consider just reversing the path instead of this error.+  | otherwise = withTrailL (pathFromLocTrail . expandLine o r) (expandLoop o r) t++expandLine :: RealFloat n => ExpandOpts n -> n -> Located (Trail' Line V2 n) -> Located (Trail V2 n)+expandLine opts r (mapLoc wrapLine -> t) = caps cap r s e (f r) (f $ -r)+    where+      eps = opts^.expandEpsilon+      offset r' = map (bindLoc (offsetSegment eps r')) . locatedTrailSegments+      f r' = joinSegments eps (fromLineJoin (opts^.expandJoin)) False (opts^.expandMiterLimit) r' ends+           . offset r' $ t+      ends = tail . trailPoints $ t+      s = atStart t+      e = atEnd t+      cap = fromLineCap (opts^.expandCap)++expandLoop :: RealFloat n => ExpandOpts n -> n -> Located (Trail' Loop V2 n) -> Path V2 n+expandLoop opts r (mapLoc wrapLoop -> t) = trailLike (f r) <> (trailLike . reverseDomain . f $ -r)+    where+      eps = opts^.expandEpsilon+      offset r' = map (bindLoc (offsetSegment eps r')) . locatedTrailSegments+      f r' = joinSegments eps (fromLineJoin (opts^.expandJoin)) True (opts^.expandMiterLimit) r' ends+           . offset r' $ t+      ends = (\(a:as) -> as ++ [a]) . trailPoints $ t++-- | Expand a 'Trail' with the given radius and default options.  See 'expandTrail''.+expandTrail :: RealFloat n => n -> Located (Trail V2 n) -> Path V2 n+expandTrail = expandTrail' def++-- | Expand a 'Path' using 'expandTrail'' on each trail in the path.+expandPath' :: RealFloat n => ExpandOpts n -> n -> Path V2 n -> Path V2 n+expandPath' opts r = mconcat+                   . map (bindLoc (expandTrail' opts r) . (`at` origin))+                   . op Path++-- | Expand a 'Path' with the given radius and default options.  See 'expandPath''.+expandPath :: RealFloat n => n -> Path V2 n -> Path V2 n+expandPath = expandPath' def++-- > import Diagrams.TwoD.Offset+-- > import Data.Default+-- >+-- > expandTrailExample :: Diagram SVG+-- > expandTrailExample = pad 1.1 . centerXY . hcat' (def & sep .~ 1)+-- >                    . map (uncurry showStyle)+-- >                    $ [ (LineCapButt,   "LineCapButt")+-- >                      , (LineCapRound,  "LineCapRound")+-- >                      , (LineCapSquare, "LineCapSquare")+-- >                      ]+-- >  where+-- >    showStyle c s = centerXY (trailLike corner # lc white # lw veryThick+-- >                               <> stroke (expandTrail'+-- >                                              (def & expandJoin .~ LineJoinRound+-- >                                                   & expandCap .~ c+-- >                                                   ) 2 corner)+-- >                                      # lw none # fc green)+-- >               === (strutY 3 <> text s # font "Helvetica" # bold)+-- >+-- > expandLoopExample :: Diagram SVG+-- > expandLoopExample = pad 1.1 . centerXY $ ((strokeLocT t # lw veryThick # lc white)+-- >                                        <> (stroke t' # lw none # fc green))+-- >   where+-- >     t  = mapLoc glueTrail $ fromVertices (map p2 [(0, 0), (5, 0), (10, 5), (10, 10), (0, 0)])+-- >     t' = expandTrail' (def & expandJoin .~ LineJoinRound) 1 t+++-- | When we expand a line (the original line runs through the center of offset+--   lines at  r  and  -r) there is some choice in what the ends will look like.+--   If we are using a circle brush we should see a half circle at each end.+--   Similar caps could be made for square brushes or simply stopping exactly at+--   the end with a straight line (a perpendicular line brush).+--+--   caps  takes the radius and the start and end points of the original line and+--   the offset trails going out and coming back.  The result is a new list of+--   trails with the caps included.+caps :: RealFloat n => (n -> Point V2 n -> Point V2 n -> Point V2 n -> Trail V2 n)+     -> n -> Point V2 n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Located (Trail V2 n)+caps cap r s e fs bs = mapLoc glueTrail $ mconcat+    [ cap r s (atStart bs) (atStart fs)+    , unLoc fs+    , cap r e (atEnd fs) (atEnd bs)+    , reverseDomain (unLoc bs)+    ] `at` atStart bs++-- | Take a LineCap style and give a function for building the cap from+fromLineCap :: RealFloat n => LineCap -> n -> Point V2 n -> Point V2 n -> Point V2 n -> Trail V2 n+fromLineCap c = case c of+    LineCapButt   -> capCut+    LineCapRound  -> capArc+    LineCapSquare -> capSquare++-- | Builds a cap that directly connects the ends.+capCut :: RealFloat n => n -> Point V2 n -> Point V2 n -> Point V2 n -> Trail V2 n+capCut _r _c a b = fromSegments [straight (b .-. a)]++-- | Builds a cap with a square centered on the end.+capSquare :: RealFloat n => n -> Point V2 n -> Point V2 n -> Point V2 n -> Trail V2 n+capSquare _r c a b = unLoc $ fromVertices [ a, a .+^ v, b .+^ v, b ]+  where+    v = perp (a .-. c)++-- | Builds an arc to fit with a given radius, center, start, and end points.+--   A Negative r means a counter-clockwise arc+capArc :: RealFloat n => n -> Point V2 n -> Point V2 n -> Point V2 n -> Trail V2 n+capArc r c a b = trailLike . moveTo c $ fs+  where+    fs | r < 0     = scale (-r) $ arcCW  (dirBetween c a) (dirBetween c b)+       | otherwise = scale r    $ arcCCW (dirBetween c a) (dirBetween c b)++-- | Join together a list of located trails with the given join style.  The+--   style is given as a function to compute the join given the local information+--   of the original vertex, the previous trail, and the next trail.  The result+--   is a single located trail.  A join radius is also given to aid in arc joins.+--+--   Note: this is not a general purpose join and assumes that we are joining an+--   offset trail.  For instance, a fixed radius arc will not fit between arbitrary+--   trails without trimming or extending.+joinSegments :: RealFloat n+             => n+             -> (n -> n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Trail V2 n)+             -> Bool+             -> n+             -> n+             -> [Point V2 n]+             -> [Located (Trail V2 n)]+             -> Located (Trail V2 n)+joinSegments _ _ _ _ _ _ [] = mempty `at` origin+joinSegments _ _ _ _ _ [] _ = mempty `at` origin+joinSegments epsilon j isLoop ml r es ts@(t:_) = t'+  where+    t' | isLoop    = mapLoc (glueTrail . (<> f (take (length ts * 2 - 1) $ ss es (ts ++ [t])))) t+       | otherwise = mapLoc (<> f (ss es ts)) t+    ss es' ts' = concat [[test a b $ j ml r e a b, Just $ unLoc b] | (e,(a,b)) <- zip es' . (zip <*> tail) $ ts']+    test a b tj+        | atStart b `distance` atEnd a > epsilon = Just tj+        | otherwise                              = Nothing+    f = mconcat . catMaybes++-- | Take a join style and give the join function to be used by joinSegments.+fromLineJoin+  :: RealFloat n => LineJoin -> n -> n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Trail V2 n+fromLineJoin j = case j of+    LineJoinMiter -> joinSegmentIntersect+    LineJoinRound -> joinSegmentArc+    LineJoinBevel -> joinSegmentClip++-- TODO: The joinSegmentCut option is not in our standard line joins.  I don't know+-- how useful it is graphically, I mostly had it as it was useful for debugging+{-+-- | Join with segments going back to the original corner.+joinSegmentCut :: (OrderedField n) => n -> n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Trail V2 n+joinSegmentCut _ _ e a b = fromSegments+    [ straight (e .-. atEnd a)+    , straight (atStart b .-. e)+    ]+-}++-- | Join by directly connecting the end points.  On an inside corner this+--   creates negative space for even-odd fill.  Here is where we would want to+--   use an arc or something else in the future.+joinSegmentClip :: RealFloat n+  => n -> n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Trail V2 n+joinSegmentClip _ _ _ a b = fromSegments [straight $ atStart b .-. atEnd a]++-- | Join with a radius arc.  On an inside corner this will loop around the interior+--   of the offset trail.  With a winding fill this will not be visible.+joinSegmentArc :: RealFloat n+  => n -> n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Trail V2 n+joinSegmentArc _ r e a b = capArc r e (atEnd a) (atStart b)++-- | Join to the intersection of the incoming trails projected tangent to their ends.+--   If the intersection is beyond the miter limit times the radius, stop at the limit.+joinSegmentIntersect+    :: RealFloat n => n -> n -> Point V2 n -> Located (Trail V2 n) -> Located (Trail V2 n) -> Trail V2 n+joinSegmentIntersect miterLimit r e a b =+    if cross < 0.000001+      then clip+      else case traceP pa va t of+          -- clip join when we excede the miter limit.  We could instead+          -- Join at exactly the miter limit, but standard behavior seems+          -- to be clipping.+          Nothing -> clip+          Just p+            -- If trace gave us garbage...+            | p `distance` pb > abs (miterLimit * r) -> clip+            | otherwise                              -> unLoc $ fromVertices [ pa, p, pb ]+  where+    t = straight (miter vb) `at` pb+    va = unitPerp (pa .-. e)+    vb = negated $ unitPerp (pb .-. e)+    pa = atEnd a+    pb = atStart b+    miter v = abs (miterLimit * r) *^ v+    clip = joinSegmentClip miterLimit r e a b+    cross = let (xa,ya) = unr2 va; (xb,yb) = unr2 vb in abs (xa * yb - xb * ya)
src/Diagrams/TwoD/Path.hs view
@@ -1,15 +1,23 @@+{-# LANGUAGE ConstraintKinds            #-} {-# LANGUAGE DeriveDataTypeable         #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE Rank2Types                 #-}+{-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TemplateHaskell            #-} {-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE UndecidableInstances       #-} {-# LANGUAGE ViewPatterns               #-}+ {-# OPTIONS_GHC -fno-warn-orphans #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Path--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -21,48 +29,66 @@ -----------------------------------------------------------------------------  module Diagrams.TwoD.Path-       ( -- * Constructing path-based diagrams+  ( -- * Constructing path-based diagrams -         stroke, stroke', strokeT, strokeT', strokeLine, strokeLoop-       , strokeLocT, strokeLocLine, strokeLocLoop+    stroke, stroke'+  , strokePath, strokeP, strokePath', strokeP'+  , strokeTrail, strokeT, strokeTrail', strokeT'+  , strokeLine, strokeLoop+  , strokeLocTrail, strokeLocT, strokeLocLine, strokeLocLoop -         -- ** Stroke options+    -- ** Stroke options -       , FillRule(..)-       , FillRuleA(..), getFillRule, fillRule-       , StrokeOpts(..)+  , FillRule(..)+  , getFillRule, fillRule, _fillRule+  , StrokeOpts(..), vertexNames, queryFillRule -         -- ** Inside/outside testing+    -- ** Inside/outside testing -       , isInsideWinding, isInsideEvenOdd+  , Crossings (..)+  , isInsideWinding+  , isInsideEvenOdd -         -- * Clipping+    -- * Clipping -       , Clip(..), clipBy-       ) where+  , Clip(..), _Clip, _clip+  , clipBy, clipTo, clipped -import           Control.Applicative   (liftA2)-import qualified Data.Foldable         as F+    -- * Intersections++  , intersectPoints, intersectPoints'+  , intersectPointsP, intersectPointsP'+  , intersectPointsT, intersectPointsT'+  ) where++import           Control.Applicative       (liftA2)+import           Control.Lens              hiding (at, transform)+import qualified Data.Foldable             as F import           Data.Semigroup import           Data.Typeable -import           Data.AffineSpace-import           Data.Default.Class-import           Data.VectorSpace+import           Data.Default -import           Diagrams.Coordinates+import           Diagrams.Angle+import           Diagrams.Combinators      (withEnvelope, withTrace) import           Diagrams.Core-import           Diagrams.Located      (Located, mapLoc, unLoc)+import           Diagrams.Core.Trace+import           Diagrams.Located          (Located, mapLoc, unLoc) import           Diagrams.Parametric import           Diagrams.Path+import           Diagrams.Query import           Diagrams.Segment-import           Diagrams.Solve+import           Diagrams.Solve.Polynomial import           Diagrams.Trail import           Diagrams.TrailLike import           Diagrams.TwoD.Segment import           Diagrams.TwoD.Types-import           Diagrams.Util         (tau)+import           Diagrams.TwoD.Vector+import           Diagrams.Util             (tau) +import           Linear.Affine+import           Linear.Vector+ ------------------------------------------------------------ --  Trail and path traces  --------------------------------- ------------------------------------------------------------@@ -71,219 +97,304 @@  -- XXX can the efficiency of this be improved?  See the comment in -- Diagrams.Path on the Enveloped instance for Trail.-instance Traced (Trail R2) where+instance RealFloat n => Traced (Trail V2 n) where   getTrace = withLine $       foldr-        (\seg bds -> moveOriginBy (negateV . atEnd $ seg) bds <> getTrace seg)+        (\seg bds -> moveOriginBy (negated . atEnd $ seg) bds <> getTrace seg)         mempty     . lineSegments -instance Traced (Path R2) where-  getTrace = F.foldMap getTrace . pathTrails+instance RealFloat n => Traced (Path V2 n) where+  getTrace = F.foldMap getTrace . op Path  ------------------------------------------------------------ --  Constructing path-based diagrams  ---------------------- ------------------------------------------------------------ --- | Convert a path into a diagram.  The resulting diagram has the+-- | Enumeration of algorithms or \"rules\" for determining which+--   points lie in the interior of a (possibly self-intersecting)+--   path.+data FillRule+  = Winding  -- ^ Interior points are those with a nonzero+             --   /winding/ /number/.  See+             --   <http://en.wikipedia.org/wiki/Nonzero-rule>.+  | EvenOdd  -- ^ Interior points are those where a ray+             --   extended infinitely in a particular direction crosses+             --   the path an odd number of times. See+             --   <http://en.wikipedia.org/wiki/Even-odd_rule>.+    deriving (Show, Typeable, Eq, Ord)++instance AttributeClass FillRule+instance Semigroup FillRule where+  _ <> b = b++instance Default FillRule where+  def = Winding++-- | A record of options that control how a path is stroked.+--   @StrokeOpts@ is an instance of 'Default', so a @StrokeOpts@+--   records can be created using @'with' { ... }@ notation.+data StrokeOpts a+  = StrokeOpts+    { _vertexNames   :: [[a]]++    , _queryFillRule :: FillRule++    }++makeLensesWith (generateSignatures .~ False $ lensRules) ''StrokeOpts++-- | Atomic names that should be assigned to the vertices of the path so that+--   they can be referenced later.  If there are not enough names, the extra+--   vertices are not assigned names; if there are too many, the extra names+--   are ignored.  Note that this is a /list of lists/ of names, since paths+--   can consist of multiple trails.  The first list of names are assigned to+--   the vertices of the first trail, the second list to the second trail, and+--   so on.+--+--   The default value is the empty list.++vertexNames :: Lens (StrokeOpts a) (StrokeOpts a') [[a]] [[a']]++-- | The fill rule used for determining which points are inside the path.+--   The default is 'Winding'.  NOTE: for now, this only affects the resulting+--   diagram's 'Query', /not/ how it will be drawn!  To set the fill rule+--   determining how it is to be drawn, use the 'fillRule' function.+queryFillRule :: Lens' (StrokeOpts a) FillRule++instance Default (StrokeOpts a) where+  def = StrokeOpts+        { _vertexNames    = []+        , _queryFillRule = def+        }++-- | Convert a 'ToPath' object into a diagram.  The resulting diagram has the --   names 0, 1, ... assigned to each of the path's vertices. -- --   See also 'stroke'', which takes an extra options record allowing---   its behavior to be customized.+--   its behaviour to be customized. -----   Note that a bug in GHC 7.0.1 causes a context stack overflow when---   inferring the type of @stroke@.  The solution is to give a type---   signature to expressions involving @stroke@, or (recommended)---   upgrade GHC (the bug is fixed in 7.0.2 onwards).-stroke :: Renderable (Path R2) b-       => Path R2 -> Diagram b R2-stroke = stroke' (def :: StrokeOpts ())--instance Renderable (Path R2) b => TrailLike (QDiagram b R2 Any) where-  trailLike = stroke . trailLike+-- @+-- 'stroke' :: 'Path' 'V2' 'Double'                  -> 'Diagram' b+-- 'stroke' :: 'Located' ('Trail' 'V2' 'Double')       -> 'Diagram' b+-- 'stroke' :: 'Located' ('Trail'' 'Loop' 'V2' 'Double') -> 'Diagram' b+-- 'stroke' :: 'Located' ('Trail'' 'Line' 'V2' 'Double') -> 'Diagram' b+-- @+stroke :: (InSpace V2 n t, ToPath t, TypeableFloat n, Renderable (Path V2 n) b)+       => t -> QDiagram b V2 n Any+stroke = strokeP . toPath  -- | A variant of 'stroke' that takes an extra record of options to---   customize its behavior.  In particular:+--   customize its behaviour.  In particular: -- --     * Names can be assigned to the path's vertices -----   'StrokeOpts' is an instance of 'Default', so @stroke' 'with' {---   ... }@ syntax may be used.-stroke' :: (Renderable (Path R2) b, IsName a) => StrokeOpts a -> Path R2 -> Diagram b R2-stroke' opts path-  | null (pathTrails p1) =           mkP p2-  | null (pathTrails p2) = mkP p1-  | otherwise            = mkP p1 <> mkP p2+--   'StrokeOpts' is an instance of 'Default', so @stroke' ('with' &+--   ... )@ syntax may be used.+stroke' :: (InSpace V2 n t, ToPath t, TypeableFloat n, Renderable (Path V2 n) b, IsName a)+       => StrokeOpts a -> t -> QDiagram b V2 n Any+stroke' opts = strokeP' opts . toPath++-- | 'stroke' specialised to 'Path'.+strokeP :: (TypeableFloat n, Renderable (Path V2 n) b)+        => Path V2 n -> QDiagram b V2 n Any+strokeP = strokeP' (def :: StrokeOpts ())++-- | 'stroke' specialised to 'Path'.+strokePath :: (TypeableFloat n, Renderable (Path V2 n) b)+        => Path V2 n -> QDiagram b V2 n Any+strokePath = strokeP++instance (TypeableFloat n, Renderable (Path V2 n) b)+    => TrailLike (QDiagram b V2 n Any) where+  trailLike = strokeP . trailLike++-- | 'stroke'' specialised to 'Path'.+strokeP' :: (TypeableFloat n, Renderable (Path V2 n) b, IsName a)+    => StrokeOpts a -> Path V2 n -> QDiagram b V2 n Any+strokeP' opts path+  | null (pLines ^. _Wrapped') = mkP pLoops+  | null (pLoops ^. _Wrapped') = mkP pLines+  | otherwise                  = mkP pLines <> mkP pLoops   where-    (p1,p2) = partitionPath (isLine . unLoc) path+    (pLines,pLoops) = partitionPath (isLine . unLoc) path     mkP p       = mkQD (Prim p)          (getEnvelope p)          (getTrace p)          (fromNames . concat $-           zipWith zip (vertexNames opts) ((map . map) subPoint (pathVertices p))+           zipWith zip (opts^.vertexNames) ((map . map) subPoint (pathVertices p))          )-         (Query $ Any . flip (runFillRule (queryFillRule opts)) p)---- | A record of options that control how a path is stroked.---   @StrokeOpts@ is an instance of 'Default', so a @StrokeOpts@---   records can be created using @'with' { ... }@ notation.-data StrokeOpts a-  = StrokeOpts-    { vertexNames   :: [[a]]  -- ^ Atomic names that should be assigned-                            --   to the vertices of the path so that-                            --   they can be referenced later.  If-                            --   there are not enough names, the extra-                            --   vertices are not assigned names; if-                            --   there are too many, the extra names-                            --   are ignored.  Note that this is a-                            --   /list of lists/ of names, since paths-                            --   can consist of multiple trails.  The-                            --   first list of names are assigned to-                            --   the vertices of the first trail, the-                            --   second list to the second trail, and-                            --   so on.-                            ---                            --   The default value is the empty list.+         (Query $ Any . (runFillRule (opts^.queryFillRule)) p) -    , queryFillRule :: FillRule-                            -- ^ The fill rule used for determining-                            --   which points are inside the path.-                            --   The default is 'Winding'.  NOTE: for-                            --   now, this only affects the resulting-                            --   diagram's 'Query', /not/ how it will-                            --   be drawn!  To set the fill rule-                            --   determining how it is to be drawn,-                            --   use the 'fillRule' function.-    }+-- | 'stroke'' specialised to 'Path'.+strokePath' :: (TypeableFloat n, Renderable (Path V2 n) b, IsName a)+    => StrokeOpts a -> Path V2 n -> QDiagram b V2 n Any+strokePath' = strokeP' -instance Default (StrokeOpts a) where-  def = StrokeOpts-        { vertexNames    = []-        , queryFillRule = Winding-        }+-- | 'stroke' specialised to 'Trail'.+strokeTrail :: (TypeableFloat n, Renderable (Path V2 n) b)+            => Trail V2 n -> QDiagram b V2 n Any+strokeTrail = stroke . pathFromTrail --- | A composition of 'stroke' and 'pathFromTrail' for conveniently---   converting a trail directly into a diagram.------   Note that a bug in GHC 7.0.1 causes a context stack overflow when---   inferring the type of 'stroke' and hence of @strokeT@ as well.---   The solution is to give a type signature to expressions involving---   @strokeT@, or (recommended) upgrade GHC (the bug is fixed in 7.0.2---   onwards).-strokeT :: (Renderable (Path R2) b)-        => Trail R2 -> Diagram b R2-strokeT = stroke . pathFromTrail+-- | 'stroke' specialised to 'Trail'.+strokeT :: (TypeableFloat n, Renderable (Path V2 n) b)+        => Trail V2 n -> QDiagram b V2 n Any+strokeT = strokeTrail  -- | A composition of 'stroke'' and 'pathFromTrail' for conveniently --   converting a trail directly into a diagram.-strokeT' :: (Renderable (Path R2) b, IsName a)-         => StrokeOpts a -> Trail R2 -> Diagram b R2-strokeT' opts = stroke' opts . pathFromTrail+strokeTrail' :: (TypeableFloat n, Renderable (Path V2 n) b, IsName a)+             => StrokeOpts a -> Trail V2 n -> QDiagram b V2 n Any+strokeTrail' opts = stroke' opts . pathFromTrail +-- | Deprecated synonym for 'strokeTrail''.+strokeT' :: (TypeableFloat n, Renderable (Path V2 n) b, IsName a)+         => StrokeOpts a -> Trail V2 n -> QDiagram b V2 n Any+strokeT' = strokeTrail'+ -- | A composition of 'strokeT' and 'wrapLine' for conveniently --   converting a line directly into a diagram.-strokeLine :: (Renderable (Path R2) b) => Trail' Line R2 -> Diagram b R2+strokeLine :: (TypeableFloat n, Renderable (Path V2 n) b)+           => Trail' Line V2 n -> QDiagram b V2 n Any strokeLine = strokeT . wrapLine  -- | A composition of 'strokeT' and 'wrapLoop' for conveniently --   converting a loop directly into a diagram.-strokeLoop :: (Renderable (Path R2) b) => Trail' Loop R2 -> Diagram b R2+strokeLoop :: (TypeableFloat n, Renderable (Path V2 n) b)+           => Trail' Loop V2 n -> QDiagram b V2 n Any strokeLoop = strokeT . wrapLoop  -- | A convenience function for converting a @Located Trail@ directly---   into a diagram; @strokeLocT = stroke . trailLike@.-strokeLocT :: (Renderable (Path R2) b) => Located (Trail R2) -> Diagram b R2-strokeLocT = stroke . trailLike+--   into a diagram; @strokeLocTrail = stroke . trailLike@.+strokeLocTrail :: (TypeableFloat n, Renderable (Path V2 n) b)+               => Located (Trail V2 n) -> QDiagram b V2 n Any+strokeLocTrail = strokeP . trailLike +-- | Deprecated synonym for 'strokeLocTrail'.+strokeLocT :: (TypeableFloat n, Renderable (Path V2 n) b)+           => Located (Trail V2 n) -> QDiagram b V2 n Any+strokeLocT = strokeLocTrail+ -- | A convenience function for converting a @Located@ line directly --   into a diagram; @strokeLocLine = stroke . trailLike . mapLoc wrapLine@.-strokeLocLine :: (Renderable (Path R2) b) => Located (Trail' Line R2) -> Diagram b R2-strokeLocLine = stroke . trailLike . mapLoc wrapLine+strokeLocLine :: (TypeableFloat n, Renderable (Path V2 n) b)+              => Located (Trail' Line V2 n) -> QDiagram b V2 n Any+strokeLocLine = strokeP . trailLike . mapLoc wrapLine  -- | A convenience function for converting a @Located@ loop directly --   into a diagram; @strokeLocLoop = stroke . trailLike . mapLoc wrapLoop@.-strokeLocLoop :: (Renderable (Path R2) b) => Located (Trail' Loop R2) -> Diagram b R2-strokeLocLoop = stroke . trailLike . mapLoc wrapLoop+strokeLocLoop :: (TypeableFloat n, Renderable (Path V2 n) b)+              => Located (Trail' Loop V2 n) -> QDiagram b V2 n Any+strokeLocLoop = strokeP . trailLike . mapLoc wrapLoop  ------------------------------------------------------------ --  Inside/outside testing ------------------------------------------------------------ --- | Enumeration of algorithms or \"rules\" for determining which---   points lie in the interior of a (possibly self-intersecting)---   closed path.-data FillRule = Winding  -- ^ Interior points are those with a nonzero-                         --   /winding/ /number/.  See-                         --   <http://en.wikipedia.org/wiki/Nonzero-rule>.-              | EvenOdd  -- ^ Interior points are those where a ray-                         --   extended infinitely in a particular-                         --   direction crosses the path an odd number-                         --   of times. See-                         --   <http://en.wikipedia.org/wiki/Even-odd_rule>.-    deriving (Eq)--runFillRule :: FillRule -> P2 -> Path R2 -> Bool+runFillRule :: RealFloat n => FillRule -> Path V2 n -> Point V2 n -> Bool runFillRule Winding = isInsideWinding runFillRule EvenOdd = isInsideEvenOdd -newtype FillRuleA = FillRuleA (Last FillRule)-  deriving (Typeable, Semigroup)-instance AttributeClass FillRuleA- -- | Extract the fill rule from a 'FillRuleA' attribute.-getFillRule :: FillRuleA -> FillRule-getFillRule (FillRuleA (Last r)) = r+getFillRule :: FillRule -> FillRule+getFillRule = id  -- | Specify the fill rule that should be used for determining which --   points are inside a path. fillRule :: HasStyle a => FillRule -> a -> a-fillRule = applyAttr . FillRuleA . Last+fillRule = applyAttr -cross :: R2 -> R2 -> Double-cross (coords -> x :& y) (coords -> x' :& y') = x * y' - y * x'+-- | Lens onto the fill rule of a style.+_fillRule :: Lens' (Style V2 n) FillRule+_fillRule = atAttr . non def --- XXX link to more info on this+-- | The sum of /signed/ crossings of a path as we travel in the+--   positive x direction from a given point.+--+--     - A point is filled according to the 'Winding' fill rule, if the+--       number of 'Crossings' is non-zero (see 'isInsideWinding').+--+--     - A point is filled according to the 'EvenOdd' fill rule, if the+--       number of 'Crossings' is odd (see 'isInsideEvenOdd').+--+--   This is the 'HasQuery' result for 'Path's, 'Located' 'Trail's and+--   'Located' 'Loops'.+--+-- @+-- 'sample' :: 'Path' 'V2' 'Double'                  -> 'Point' 'V2' 'Double' -> 'Crossings'+-- 'sample' :: 'Located' ('Trail' 'V2' 'Double')       -> 'Point' 'V2' 'Double' -> 'Crossings'+-- 'sample' :: 'Located' ('Trail'' 'Loop' 'V2' 'Double') -> 'Point' 'V2' 'Double' -> 'Crossings'+-- @+--+--   Note that 'Line's have no inside or outside, so don't contribute+--   crossings+newtype Crossings = Crossings Int+  deriving (Show, Eq, Ord, Num, Enum, Real, Integral) --- | Test whether the given point is inside the given (closed) path,+instance Semigroup Crossings where+  Crossings a <> Crossings b = Crossings (a + b)++instance Monoid Crossings where+  mempty  = Crossings 0+  mappend = (<>)++instance RealFloat n => HasQuery (Located (Trail V2 n)) Crossings where+  getQuery trail = Query $ \p -> trailCrossings p trail++instance RealFloat n => HasQuery (Located (Trail' l V2 n)) Crossings where+  getQuery trail' = getQuery (mapLoc Trail trail')++instance RealFloat n => HasQuery (Path V2 n) Crossings where+  getQuery = foldMapOf each getQuery++-- | Test whether the given point is inside the given path, --   by testing whether the point's /winding number/ is nonzero. Note---   that @False@ is /always/ returned for /open/ paths, regardless of---   the winding number.-isInsideWinding :: P2 -> Path R2 -> Bool-isInsideWinding p = (/= 0) . crossings p+--   that @False@ is /always/ returned for paths consisting of lines+--   (as opposed to loops), regardless of the winding number.+--+-- @+-- 'isInsideWinding' :: 'Path' 'V2' 'Double'                  -> 'Point' 'V2' 'Double' -> 'Bool'+-- 'isInsideWinding' :: 'Located' ('Trail' 'V2' 'Double')       -> 'Point' 'V2' 'Double' -> 'Bool'+-- 'isInsideWinding' :: 'Located' ('Trail'' 'Loop' 'V2' 'Double') -> 'Point' 'V2' 'Double' -> 'Bool'+-- @+isInsideWinding :: HasQuery t Crossings => t -> Point (V t) (N t) -> Bool+isInsideWinding t = (/= 0) . sample t --- | Test whether the given point is inside the given (closed) path,+-- | Test whether the given point is inside the given path, --   by testing whether a ray extending from the point in the positive --   x direction crosses the path an even (outside) or odd (inside) --   number of times.  Note that @False@ is /always/ returned for---   /open/ paths, regardless of the number of crossings.-isInsideEvenOdd :: P2 -> Path R2 -> Bool-isInsideEvenOdd p = odd . crossings p---- | Compute the sum of /signed/ crossings of a path as we travel in the---   positive x direction from a given point.-crossings :: P2 -> Path R2 -> Int-crossings p = F.sum . map (trailCrossings p) . pathTrails+--   paths consisting of lines (as opposed to loops), regardless of+--   the number of crossings.+--+-- @+-- 'isInsideEvenOdd' :: 'Path' 'V2' 'Double'                  -> 'Point' 'V2' 'Double' -> 'Bool'+-- 'isInsideEvenOdd' :: 'Located' ('Trail' 'V2' 'Double')       -> 'Point' 'V2' 'Double' -> 'Bool'+-- 'isInsideEvenOdd' :: 'Located' ('Trail'' 'Loop' 'V2' 'Double') -> 'Point' 'V2' 'Double' -> 'Bool'+-- @+isInsideEvenOdd :: HasQuery t Crossings => t -> Point (V t) (N t) -> Bool+isInsideEvenOdd t = odd . sample t  -- | Compute the sum of signed crossings of a trail starting from the --   given point in the positive x direction.-trailCrossings :: P2 -> Located (Trail R2) -> Int+trailCrossings :: RealFloat n => Point V2 n -> Located (Trail V2 n) -> Crossings    -- non-loop trails have no inside or outside, so don't contribute crossings trailCrossings _ t | not (isLoop (unLoc t)) = 0  trailCrossings p@(unp2 -> (x,y)) tr-  = sum . map test $ fixTrail tr+  = F.foldMap test $ fixTrail tr   where     test (FLinear a@(unp2 -> (_,ay)) b@(unp2 -> (_,by)))       | ay <= y && by > y && isLeft a b > 0 =  1       | by <= y && ay > y && isLeft a b < 0 = -1       | otherwise                           =  0 -    test c@(FCubic (unp2 -> x1@(_,x1y))-                   (unp2 -> c1@(_,c1y))-                   (unp2 -> c2@(_,c2y))-                   (unp2 -> x2@(_,x2y))+    test c@(FCubic (P x1@(V2 _ x1y))+                   (P c1@(V2 _ c1y))+                   (P c2@(V2 _ c2y))+                   (P x2@(V2 _ x2y))            ) =         sum . map testT $ ts       where ts = filter (liftA2 (&&) (>=0) (<=1))@@ -294,15 +405,15 @@             testT t = let (unp2 -> (px,_)) = c `atParam` t                       in  if px > x then signFromDerivAt t else 0             signFromDerivAt t =-              let (dx,dy) = (3*t*t) *^ ((-1)*^x1 ^+^ 3*^c1 ^-^ 3*^c2 ^+^ x2)-                        ^+^ (2*t)   *^ (3*^x1 ^-^ 6*^c1 ^+^ 3*^c2)-                        ^+^            ((-3)*^x1 ^+^ 3*^c1)-                  ang = atan2 dy dx+              let v =  (3*t*t) *^ ((-1)*^x1 ^+^ 3*^c1 ^-^ 3*^c2 ^+^ x2)+                   ^+^ (2*t)   *^ (3*^x1 ^-^ 6*^c1 ^+^ 3*^c2)+                   ^+^            ((-3)*^x1 ^+^ 3*^c1)+                  ang = v ^. _theta . rad               in  case () of _ | 0      < ang && ang < tau/2 && t < 1 ->  1                                | -tau/2 < ang && ang < 0     && t > 0 -> -1                                | otherwise                            ->  0 -    isLeft a b = cross (b .-. a) (p .-. a)+    isLeft a b = cross2 (b .-. a) (p .-. a)  ------------------------------------------------------------ --  Clipping  ----------------------------------------------@@ -313,25 +424,108 @@ --   concatenation, so applying multiple clipping paths is sensible. --   The clipping region is the intersection of all the applied --   clipping paths.-newtype Clip = Clip { getClip :: [Path R2] }+newtype Clip n = Clip [Path V2 n]   deriving (Typeable, Semigroup)-instance AttributeClass Clip -type instance V Clip = R2+makeWrapped ''Clip -instance Transformable Clip where+instance Typeable n => AttributeClass (Clip n)++instance AsEmpty (Clip n) where+  _Empty = _Clip . _Empty++type instance V (Clip n) = V2+type instance N (Clip n) = n++instance (OrderedField n) => Transformable (Clip n) where   transform t (Clip ps) = Clip (transform t ps) +-- | A point inside a clip if the point is in 'All' invididual clipping+--   paths.+instance RealFloat n => HasQuery (Clip n) All where+  getQuery (Clip paths) = Query $ \p ->+    F.foldMap (All . flip isInsideWinding p) paths++_Clip :: Iso (Clip n) (Clip n') [Path V2 n] [Path V2 n']+_Clip = _Wrapped++-- | Lens onto the Clip in a style. An empty list means no clipping.+_clip :: (Typeable n, OrderedField n) => Lens' (Style V2 n) [Path V2 n]+_clip = atTAttr . non' _Empty . _Clip+ -- | Clip a diagram by the given path: -- --   * Only the parts of the diagram which lie in the interior of the --     path will be drawn. -- --   * The envelope of the diagram is unaffected.-clipBy :: (HasStyle a, V a ~ R2) => Path R2 -> a -> a+clipBy :: (HasStyle a, V a ~ V2, N a ~ n, TypeableFloat n) => Path V2 n -> a -> a clipBy = applyTAttr . Clip . (:[]) --- XXX Should include a 'clipTo' function which clips a diagram AND--- restricts its envelope.  It will have to take a *pointwise minimum*--- of the diagram's current envelope and the path's envelope.  Not--- sure of the best way to do this at the moment.+-- | Clip a diagram to the given path setting its envelope to the+--   pointwise minimum of the envelopes of the diagram and path. The+--   trace consists of those parts of the original diagram's trace+--   which fall within the clipping path, or parts of the path's trace+--   within the original diagram.+clipTo :: TypeableFloat n+  => Path V2 n -> QDiagram b V2 n Any -> QDiagram b V2 n Any+clipTo p d = setTrace intersectionTrace . toEnvelope $ clipBy p d+  where+    envP = appEnvelope . getEnvelope $ p+    envD = appEnvelope . getEnvelope $ d+    toEnvelope = case (envP, envD) of+      (Just eP, Just eD) -> setEnvelope . mkEnvelope $ \v -> min (eP v) (eD v)+      (_, _)             -> id+    intersectionTrace = Trace traceIntersections+    traceIntersections pt v =+        -- on boundary of d, inside p+        onSortedList (filter pInside) (appTrace (getTrace d) pt v) <>+        -- or on boundary of p, inside d+        onSortedList (filter dInside) (appTrace (getTrace p) pt v) where+          newPt dist = pt .+^ v ^* dist+          pInside dDist = runFillRule Winding p (newPt dDist)+          dInside pDist = getAny . sample d $ newPt pDist++-- | Clip a diagram to the clip path taking the envelope and trace of the clip+--   path.+clipped :: TypeableFloat n+  => Path V2 n -> QDiagram b V2 n Any -> QDiagram b V2 n Any+clipped p = withTrace p . withEnvelope p . clipBy p++------------------------------------------------------------+--  Intersections  -----------------------------------------+------------------------------------------------------------++-- | Find the intersect points of two objects that can be converted to a path.+intersectPoints :: (InSpace V2 n t, SameSpace t s, ToPath t, ToPath s, OrderedField n)+  => t -> s -> [P2 n]+intersectPoints = intersectPoints' 1e-8++-- | Find the intersect points of two objects that can be converted to a path+--   within the given tolerance.+intersectPoints' :: (InSpace V2 n t, SameSpace t s, ToPath t, ToPath s, OrderedField n)+  => n -> t -> s -> [P2 n]+intersectPoints' eps t s = intersectPointsP' eps (toPath t) (toPath s)++-- | Compute the intersect points between two paths.+intersectPointsP :: OrderedField n => Path V2 n -> Path V2 n -> [P2 n]+intersectPointsP = intersectPointsP' 1e-8++-- | Compute the intersect points between two paths within given tolerance.+intersectPointsP' :: OrderedField n => n -> Path V2 n -> Path V2 n -> [P2 n]+intersectPointsP' eps as bs = do+  a <- pathTrails as+  b <- pathTrails bs+  intersectPointsT' eps a b++-- | Compute the intersect points between two located trails.+intersectPointsT :: OrderedField n => Located (Trail V2 n) -> Located (Trail V2 n) -> [P2 n]+intersectPointsT = intersectPointsT' 1e-8++-- | Compute the intersect points between two located trails within the given+--   tolerance.+intersectPointsT' :: OrderedField n => n -> Located (Trail V2 n) -> Located (Trail V2 n) -> [P2 n]+intersectPointsT' eps as bs = do+  a <- fixTrail as+  b <- fixTrail bs+  intersectPointsS' eps a b
+ src/Diagrams/TwoD/Points.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE ConstraintKinds #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.TwoD.Points+-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Special functions for points in R2.+--+-----------------------------------------------------------------------------++{-# LANGUAGE TypeFamilies #-}++module Diagrams.TwoD.Points where++import Data.List++import Diagrams.Core+import Diagrams.TwoD.Vector+import Diagrams.TwoD.Types (P2)++import Linear.Affine++-- | Find the convex hull of a list of points using Andrew's monotone chain+--   algorithm O(n log n).+--   +--   Returns clockwise list of points starting from the left-most point.+convexHull2D :: OrderedField n => [P2 n] -> [P2 n]+convexHull2D ps = init upper ++ reverse (tail lower)+  where+    (upper, lower) = sortedConvexHull (sort ps)++-- | Find the convex hull of a set of points already sorted in the x direction. +--   The first list of the tuple is the upper hull going clockwise from +--   left-most to right-most point. The second is the lower hull from +--   right-most to left-most in the anti-clockwise direction.+sortedConvexHull :: OrderedField n => [P2 n] -> ([P2 n], [P2 n])+sortedConvexHull ps = (chain True ps, chain False ps)+ where+   chain upper (p1_:p2_:rest_) =+     case go (p2_ .-. p1_) p2_ rest_ of+       Right l -> p1_:l+       Left l  -> chain upper (p1_:l)+     where+       test = if upper then (>0) else (<0)+       -- find the convex hull by comparing the angles of the vectors with+       -- the cross product and backtracking if necessary+       go dir p1 l@(p2:rest)+         -- backtrack if the direction is outward+         | test $ dir `cross2` dir' = Left l+         | otherwise                =+             case go dir' p2 rest of+               Left m  -> go dir p1 m+               Right m -> Right (p1:m)+         where+           dir' = p2 .-. p1+       go _ p1 p = Right (p1:p)++   chain _ l = l
src/Diagrams/TwoD/Polygons.hs view
@@ -1,8 +1,10 @@-{-# LANGUAGE DeriveFunctor             #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE ScopedTypeVariables       #-}-{-# LANGUAGE TypeFamilies              #-}-{-# LANGUAGE ViewPatterns              #-}+{-# LANGUAGE ConstraintKinds     #-}+{-# LANGUAGE DeriveFunctor       #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}+{-# LANGUAGE TypeFamilies        #-}+{-# LANGUAGE ViewPatterns        #-}  ----------------------------------------------------------------------------- -- |@@ -20,7 +22,7 @@         -- * Polygons           PolyType(..)         , PolyOrientation(..)-        , PolygonOpts(..)+        , PolygonOpts(..), polyType, polyOrient, polyCenter          , polygon         , polyTrail@@ -44,36 +46,34 @@      ) where -import           Control.Monad           (forM, liftM)-import           Control.Monad.ST        (ST, runST)-import           Data.Array.ST           (STUArray, newArray, readArray,-                                          writeArray)-import qualified Data.Foldable           as F-import           Data.List               (maximumBy, minimumBy)-import qualified Data.List.NonEmpty      as NEL-import           Data.Maybe              (catMaybes)-import           Data.Monoid             (mconcat, mempty)-import           Data.Ord                (comparing)--import           Data.AffineSpace        ((.+^), (.-.))-import           Data.Default.Class-import           Data.VectorSpace        (magnitude, normalized, project, (<.>),-                                          (^*))+import           Control.Lens         (Lens', generateSignatures, lensRules,+                                       makeLensesWith, view, (.~), (^.))+import           Control.Monad        (forM, liftM)+import           Control.Monad.ST     (ST, runST)+import           Data.Array.ST        (STUArray, newArray, readArray,+                                       writeArray)+import           Data.Default+import           Data.List            (maximumBy, minimumBy)+import           Data.Maybe           (catMaybes)+import           Data.Ord             (comparing) +import           Diagrams.Angle import           Diagrams.Core import           Diagrams.Located import           Diagrams.Path-import           Diagrams.Points         (centroid)-import           Diagrams.Segment+import           Diagrams.Points      (centroid) import           Diagrams.Trail import           Diagrams.TrailLike-import           Diagrams.TwoD.Transform import           Diagrams.TwoD.Types-import           Diagrams.TwoD.Vector    (leftTurn, unitX, unitY, unit_Y)-import           Diagrams.Util           (tau, ( # ))+import           Diagrams.TwoD.Vector (leftTurn, unitX, unitY, unit_Y)+import           Diagrams.Util        (tau, ( # )) +import           Linear.Affine+import           Linear.Metric+import           Linear.Vector+ -- | Method used to determine the vertices of a polygon.-data PolyType = forall a. Angle a => PolyPolar [a] [Double]+data PolyType n = PolyPolar [Angle n] [n]                 -- ^ A \"polar\" polygon.                 --                 --   * The first argument is a list of /central/@@ -90,21 +90,21 @@                 --   circle) can be constructed using a second                 --   argument of @(repeat r)@. -              | forall a. Angle a => PolySides [a] [Double]+              | PolySides [Angle n] [n]                 -- ^ A polygon determined by the distance between-                --   successive vertices and the angles formed by-                --   each three successive vertices.  In other+                --   successive vertices and the external angles formed+                --   by each three successive vertices. In other                 --   words, a polygon specified by \"turtle                 --   graphics\": go straight ahead x1 units; turn by-                --   angle a1; go straght ahead x2 units; turn by-                --   angle a2; etc. The polygon will be centered at-                --   the /centroid/ of its vertices.+                --   external angle a1; go straight ahead x2 units; turn by+                --   external angle a2; etc. The polygon will be centered+                --   at the /centroid/ of its vertices.                 --                 --   * The first argument is a list of /vertex/-                --     /angles/, giving the angle at each vertex+                --     /angles/, giving the external angle at each vertex                 --     from the previous vertex to the next.  The-                --     first angle in the list is the angle at the-                --     /second/ vertex; the first edge always starts+                --     first angle in the list is the external angle at+                --     the /second/ vertex; the first edge always starts                 --     out heading in the positive y direction from                 --     the first vertex.                 --@@ -115,69 +115,73 @@                 --   angles and /n-1/ edge lengths.  Extra angles or                 --   lengths are ignored. -              | PolyRegular Int Double+              | PolyRegular Int n                 -- ^ A regular polygon with the given number of                 --   sides (first argument) and the given radius                 --   (second argument).  -- | Determine how a polygon should be oriented.-data PolyOrientation = NoOrient     -- ^ No special orientation; the first-                                    --   vertex will be at (1,0).-                                    --   This is the default.-                     | OrientH      -- ^ Orient /horizontally/, so the-                                    --   bottommost edge is parallel to-                                    --   the x-axis.-                     | OrientV      -- ^ Orient /vertically/, so the-                                    --   leftmost edge is parallel to the-                                    --   y-axis.-                     | OrientTo R2  -- ^ Orient so some edge is-                                    --   /facing/ /in/ /the/ /direction/-                                    --   /of/, that is, perpendicular-                                    --   to, the given vector.-  deriving (Eq, Ord, Show, Read)+data PolyOrientation n = NoOrient        -- ^ No special orientation; the first+                                         --   vertex will be at (1,0).+                       | OrientH         -- ^ Orient /horizontally/, so the+                                         --   bottommost edge is parallel to+                                         --   the x-axis.+                                         --   This is the default.+                       | OrientV         -- ^ Orient /vertically/, so the+                                         --   leftmost edge is parallel to the+                                         --   y-axis.+                       | OrientTo (V2 n) -- ^ Orient so some edge is+                                         --   /facing/ /in/ /the/ /direction/+                                         --   /of/, that is, perpendicular+                                         --   to, the given vector.+                       deriving (Eq, Ord, Show, Read)  -- | Options for specifying a polygon.-data PolygonOpts = PolygonOpts-                   { polyType   :: PolyType-                     -- ^ Specification for the polygon's vertices.+data PolygonOpts n = PolygonOpts+                   { _polyType   :: PolyType n+                   , _polyOrient :: PolyOrientation n+                   , _polyCenter :: Point V2 n+                   } -                   , polyOrient :: PolyOrientation-                     -- ^ Should a rotation be applied to the-                     --   polygon in order to orient it in a-                     --   particular way?+makeLensesWith (generateSignatures .~ False $ lensRules) ''PolygonOpts -                   , polyCenter :: P2-                     -- ^ Should a translation be applied to the-                     --   polygon in order to place the center at a-                     --   particular location?-                   }+-- | Specification for the polygon's vertices.+polyType :: Lens' (PolygonOpts n) (PolyType n) +-- | Should a rotation be applied to the polygon in order to orient it in a+--   particular way?+polyOrient :: Lens' (PolygonOpts n) (PolyOrientation n)++-- | Should a translation be applied to the polygon in order to place the center+--   at a particular location?+polyCenter :: Lens' (PolygonOpts n) (Point V2 n)+ -- | The default polygon is a regular pentagon of radius 1, centered --   at the origin, aligned to the x-axis.-instance Default PolygonOpts where+instance Num n => Default (PolygonOpts n) where     def = PolygonOpts (PolyRegular 5 1) OrientH origin  -- | Generate a polygon.  See 'PolygonOpts' for more information.-polyTrail :: PolygonOpts -> Located (Trail R2)+polyTrail :: OrderedField n => PolygonOpts n -> Located (Trail V2 n) polyTrail po = transform ori tr     where-        tr = case polyType po of+        tr = case po^.polyType of             PolyPolar ans szs -> polyPolarTrail ans szs             PolySides ans szs -> polySidesTrail ans szs             PolyRegular n r   -> polyRegularTrail n r-        ori = case polyOrient po of-            OrientH      -> orient unit_Y tr-            OrientV      -> orient unitX  tr-            OrientTo v   -> orient v      tr-            NoOrient     -> mempty+        ori = case po^.polyOrient of+            OrientH    -> orient unit_Y tr+            OrientV    -> orient unitX  tr+            OrientTo v -> orient v      tr+            NoOrient   -> mempty  -- | Generate the polygon described by the given options.-polygon :: (TrailLike t, V t ~ R2) => PolygonOpts -> t+polygon :: (InSpace V2 n t, TrailLike t) => PolygonOpts n -> t polygon = trailLike . polyTrail  -- | Generate the located trail of a polygon specified by polar data --   (central angles and radii). See 'PolyPolar'.-polyPolarTrail :: Angle a => [a] -> [Double] -> Located (Trail R2)+polyPolarTrail :: OrderedField n =>  [Angle n] -> [n] -> Located (Trail V2 n) polyPolarTrail [] _ = emptyTrail `at` origin polyPolarTrail _ [] = emptyTrail `at` origin polyPolarTrail ans (r:rs) = tr `at` p1@@ -186,51 +190,57 @@     tr = closeTrail . trailFromVertices $            zipWith              (\a l -> rotate a . scale l $ p2 (1,0))-             (scanl (+) 0 ans)+             (scanl (^+^) zero ans)              (r:rs)  -- | Generate the vertices of a polygon specified by side length and --   angles, and a starting point for the trail such that the origin --   is at the centroid of the vertices.  See 'PolySides'.-polySidesTrail :: Angle a => [a] -> [Double] -> Located (Trail R2)+polySidesTrail :: OrderedField n =>  [Angle n] -> [n] -> Located (Trail V2 n) polySidesTrail ans ls = tr `at` (centroid ps # scale (-1))   where-    ans'    = scanl (+) 0 ans+    ans'    = scanl (^+^) zero ans     offsets = zipWith rotate ans' (map (unitY ^*) ls)     ps      = scanl (.+^) origin offsets     tr      = closeTrail . trailFromOffsets $ offsets  -- | Generate the vertices of a regular polygon.  See 'PolyRegular'.-polyRegularTrail :: Int -> Double -> Located (Trail R2)+polyRegularTrail :: OrderedField n =>  Int -> n -> Located (Trail V2 n) polyRegularTrail n r = polyPolarTrail-                         (take (n-1) . repeat $ (tau::Rad) / fromIntegral n)+                         (replicate (n - 1) $ fullTurn ^/ fromIntegral n)                          (repeat r)  -- | Generate a transformation to orient a trail.  @orient v t@ --   generates the smallest rotation such that one of the segments --   adjacent to the vertex furthest in the direction of @v@ is --   perpendicular to @v@.-orient :: R2 -> Located (Trail R2) -> T2+orient :: OrderedField n => V2 n -> Located (Trail V2 n) -> Transformation V2 n orient v = orientPoints v . trailVertices -orientPoints :: R2 -> [P2] -> T2+orientPoints :: OrderedField n => V2 n -> [Point V2 n] -> Transformation V2 n+orientPoints _ [] = mempty+orientPoints _ [_] = mempty orientPoints v xs = rotation a   where     (n1,x,n2) = maximumBy (comparing (distAlong v . sndOf3))                   (zip3 (tail (cycle xs)) xs (last xs : init xs))-    distAlong w ((.-. origin) -> p) = signum (w <.> p) * magnitude (project w p)-    sndOf3 (_,x,_) = x-    a = minimumBy (comparing abs) . map (angleFromNormal . (.-. x)) $ [n1,n2]-    v' = normalized v+    distAlong w ((.-. origin) -> p) = signum (w `dot` p) * norm (project w p)+    sndOf3 (_,b,_) = b+    -- a :: Angle (Scalar v)+    a = minimumBy (comparing $ abs . view rad)+        . map (angleFromNormal . (.-. x)) $ [n1,n2]+    v' = signorm v+    -- angleFromNormal :: v -> Angle (Scalar v)     angleFromNormal o       | leftTurn o' v' = phi-      | otherwise      = negate phi+      | otherwise      = negated phi       where-        o' = normalized o-        theta = acos (v' <.> o')+        o' = signorm o+        theta = acos (v' `dot` o')+        -- phi :: Angle (Scalar v)         phi-          | theta <= tau/4 = Rad $ tau/4 - theta-          | otherwise      = Rad $ theta - tau/4+          | theta <= tau/4 = tau/4 - theta @@ rad+          | otherwise      = theta - tau/4 @@ rad  ------------------------------------------------------------ -- Function graphs@@ -267,10 +277,10 @@     markRho :: Int -> STUArray s Int Bool -> ST s [Int]     markRho i marks = do       isMarked <- readArray marks i-      case isMarked of-        True  -> return []-        False -> writeArray marks i True >>-                 liftM (i:) (markRho (f_n i) marks)+      if isMarked+        then return []+        else writeArray marks i True >>+               liftM (i:) (markRho (f_n i) marks)      splitParts :: [Int] -> [GraphPart Int]     splitParts tr = hair ++ cyc@@ -309,13 +319,13 @@ -- | Create a generalized /star/ /polygon/.  The 'StarOpts' are used --   to determine in which order the given vertices should be --   connected.  The intention is that the second argument of type---   @[P2]@ could be generated by a call to 'polygon', 'regPoly', or+--   @[Point v]@ could be generated by a call to 'polygon', 'regPoly', or --   the like, since a list of vertices is 'TrailLike'.  But of course---   the list can be generated any way you like.  A @'Path' 'R2'@ is+--   the list can be generated any way you like.  A @'Path' 'v'@ is --   returned (instead of any 'TrailLike') because the resulting path --   may have more than one component, for example if the vertices are --   to be connected in several disjoint cycles.-star :: StarOpts -> [P2] -> Path R2+star :: OrderedField n => StarOpts -> [Point V2 n] -> Path V2 n star sOpts vs = graphToPath $ mkGraph f vs   where f = case sOpts of               StarFun g  -> g
src/Diagrams/TwoD/Segment.hs view
@@ -1,7 +1,15 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts     #-} {-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE ViewPatterns         #-} {-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-}+-- Orphan Traced instances for Segment Closed V2 and FixedSegment V2.+-- They can't go in Traced; but they shouldn't really go in+-- Diagrams.Segment either because we only have Traced instances for+-- the special case of R2. ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Segment@@ -14,90 +22,358 @@ -- ----------------------------------------------------------------------------- -module Diagrams.TwoD.Segment where+module Diagrams.TwoD.Segment+  ( -- * Segment intersections -import           Control.Applicative     (liftA2)+    intersectPointsS+  , intersectPointsS' -import           Data.AffineSpace-import           Data.Monoid.Inf         hiding (minimum)-import           Data.VectorSpace+    -- * Closest point on a segment +  , closestPoint+  , closestPoint'+  , closestDistance+  , closestDistance'+  , closestParam+  , closestParam'++    -- ** Low level functions+  , segmentSegment+  , lineSegment+  )+  where++import           Control.Lens                    hiding (at, contains, transform, ( # ))+import           Data.Maybe+ import           Diagrams.Core-import           Diagrams.Core.Trace +import           Diagrams.Direction import           Diagrams.Located import           Diagrams.Parametric import           Diagrams.Segment-import           Diagrams.Solve+import           Diagrams.TwoD.Points+import           Diagrams.TwoD.Segment.Bernstein import           Diagrams.TwoD.Transform-import           Diagrams.TwoD.Types+import           Diagrams.TwoD.Types             hiding (p2) import           Diagrams.TwoD.Vector-import           Diagrams.Util -instance Traced (Segment Closed R2) where+import           Linear.Affine+import           Linear.Metric++{- All instances of Traced should maintain the invariant that the list of+   traces is sorted in increasing order.+-}++instance OrderedField n => Traced (Segment Closed V2 n) where   getTrace = getTrace . mkFixedSeg . (`at` origin) -instance Traced (FixedSegment R2) where+instance OrderedField n => Traced (FixedSegment V2 n) where+  getTrace seg = mkTrace $ \p v ->+    mkSortedList . map (view _1) $ lineSegment defEps (v `at` p) seg -{- Given lines defined by p0 + t0 * v0 and p1 + t1 * v1, their point of-   intersection in 2D is given by+defEps :: Fractional n => n+defEps = 1e-8 -     t_i = (v_(1-i)^ . (p1 - p0)) / (v1^ . v0)+-- | Compute the intersections between two fixed segments.+intersectPointsS :: OrderedField n => FixedSegment V2 n -> FixedSegment V2 n -> [P2 n]+intersectPointsS = intersectPointsS' defEps -   where v^ denotes the perpendicular to v, i.e. v rotated by-   -tau/4.+-- | Compute the intersections between two segments using the given tolerance.+intersectPointsS' :: OrderedField n => n -> FixedSegment V2 n -> FixedSegment V2 n -> [P2 n]+intersectPointsS' eps s1 s2 = map (view _3) $ segmentSegment eps s1 s2 -   This can be derived by starting with the parametric equation+-- | Get the closest distance(s) from a point to a 'FixedSegment'.+closestDistance :: OrderedField n => FixedSegment V2 n -> P2 n -> [n]+closestDistance = closestDistance' defEps -     p0 + v0 t0 = p1 + v1 t1+-- | Get the closest distance(s) from a point to a 'FixedSegment' within given+--   tolerance.+closestDistance' :: OrderedField n => n -> FixedSegment V2 n -> P2 n -> [n]+closestDistance' eps seg p = map (distanceA p) $ closestPoint' eps seg p -   and rearranging to get the matrix equation+-- | Get the closest point(s) on a 'FixedSegment' from a point.+closestPoint :: OrderedField n => FixedSegment V2 n -> P2 n -> [P2 n]+closestPoint = closestPoint' defEps -     [v0 -v1] [ t0 ]  =  (p1 - p0)-              [ t1 ]+-- | Get the closest point(s) on a 'FixedSegment' from a point within given+--   tolerance.+closestPoint' :: OrderedField n => n -> FixedSegment V2 n -> P2 n -> [P2 n]+closestPoint' eps seg = map (seg `atParam`) . closestParam' eps seg -   Working out the product of the inverse of [v0 -v1] with (p1 - p0)-   results in the above formulas for t_i.--}+-- | Find the closest value(s) on the Bêzier to the given point.+closestParam :: OrderedField n => FixedSegment V2 n -> P2 n -> [n]+closestParam = closestParam' defEps -  getTrace (FLinear p0 p0') = mkTrace $ \p1 v1 ->-    let-      v0     = p0' .-. p0-      det    = perp v1 <.> v0-      perp v = rotateBy (-1/4) v-      p      = p1 .-. p0-      t0     = (perp v1 <.> p) / det-      t1     = (perp v0 <.> p) / det-    in-      if det == 0 || t0 < 0 || t0 > 1-        then Infinity-        else Finite t1+-- | Find the closest value(s) on the Bêzier to the given point within given+--   tolerance.+closestParam' :: OrderedField n => n -> FixedSegment V2 n -> P2 n -> [n]+closestParam' _ (FLinear p0 p1) p+  | t < 0     = [0]+  | t > 1     = [1]+  | otherwise = [t]+  where+    vp = p  .-. p0+    v  = p1 .-. p0+    dp = vp `dot` v+    t  = dp / quadrance v+closestParam' eps cb (P (V2 px py)) = bezierFindRoot eps poly 0 1+  where+    (bx, by) = bezierToBernstein cb+    bx'  = bernsteinDeriv bx+    by'  = bernsteinDeriv by+    poly = (bx - listToBernstein [px, px, px, px]) * bx'+         + (by - listToBernstein [py, py, py, py]) * by' -{- To do intersection of a line with a cubic Bezier, we first rotate-   and scale everything so that the line has parameters (origin, unitX);-   then we find the intersection(s) of the Bezier with the x-axis.+------------------------------------------------------------------------+-- Low level+------------------------------------------------------------------------ -   XXX could we speed this up by first checking whether all the-   control point y-coordinates lie on the same side of the x-axis (if so,-   there can't possibly be any intersections)?  Need to set up some-   benchmarks.--}+-- | Return the intersection points with the parameters at which each segment+--   intersects.+segmentSegment :: OrderedField n => n -> FixedSegment V2 n -> FixedSegment V2 n -> [(n, n, P2 n)]+segmentSegment eps s1 s2 =+  case (s1,s2) of+    (FCubic{}, FCubic{})  -> map (\(t1,t2) -> (t1,t2, s1 `atParam` t1))+                           $ bezierClip eps s1 s2+    (FCubic{}, FLinear{}) -> map flip12 $ linearSeg (segLine s2) s1+    _                     -> linearSeg (segLine s1) s2 -- s1 is linear+  where+    linearSeg l s  = filter (inRange . view _1) $ lineSegment eps l s+    flip12 (a,b,c) = (b,a,c) -  getTrace bez@(FCubic {}) = mkTrace $ \p1 v1 ->-    let-      bez'@(FCubic x1 c1 c2 x2) =-        bez # moveOriginTo p1-            # rotateBy (negate (direction v1))-            # scale (1/magnitude v1)-      [y0,y1,y2,y3] = map (snd . unp2) [x1,c1,c2,x2]-      a  = -y0 + 3*y1 - 3*y2 + y3-      b  = 3*y0 - 6*y1 + 3*y2-      c  = -3*y0 + 3*y1-      d  = y0-      ts = filter (liftA2 (&&) (>= 0) (<= 1)) (cubForm a b c d)-      xs = map (fst . unp2 . atParam bez') ts-    in-      case xs of-        [] -> Infinity-        _  -> Finite (minimum xs)+-- | Return the intersection points with the parameters at which the line and segment+--   intersect.+lineSegment :: OrderedField n => n -> Located (V2 n) -> FixedSegment V2 n -> [(n, n, P2 n)]+lineSegment _ l1 p@(FLinear p0 p1)+  = map (\(tl,tp) -> (tl, tp, p `atParam` tp))+  . filter (inRange . snd) . maybeToList $ lineLine l1 (mkLine p0 p1)+lineSegment eps (viewLoc -> (p,r)) cb = map addPoint params+  where+    params = bezierFindRoot eps (listToBernstein $ cb' ^.. each . _y) 0 1+    cb'    = transform (inv (rotationTo $ dir r)) . moveOriginTo p $ cb+    --+    addPoint bt = (lt, bt, intersect)+      where+        intersect = cb `atParam` bt+        lt        = (cb' `atParam` bt) ^. _x / norm r++-- Adapted from from kuribas's cubicbezier package https://github.com/kuribas/cubicbezier++-- | Use the Bêzier clipping algorithm to return the parameters at which the+--   Bêzier curves intersect.+bezierClip :: OrderedField n => n -> FixedSegment V2 n -> FixedSegment V2 n -> [(n, n)]+bezierClip eps p_ q_ = filter (allOf both inRange) -- sometimes this returns NaN+                     $ go p_ q_ 0 1 0 1 0 False+  where+  go p q tmin tmax umin umax clip revCurves+    | isNothing chopInterval = []++    -- This check happens before the subdivision+    -- test to avoid non-termination as values+    -- transition to within epsilon.+    | max (umax - umin) (tmax' - tmin') < eps =+      if revCurves -- return parameters in correct order+      then [ (avg umin  umax,  avg tmin' tmax') ]+      else [ (avg tmin' tmax', avg umin  umax ) ]++    -- split the curve if there isn't enough reduction+    | clip > 0.8 && clip' > 0.8 =+      if tmax' - tmin' > umax - umin -- split the longest segment+      then let (pl, pr) = p' `splitAtParam` 0.5+               tmid = avg tmin' tmax'+           in  go q pl umin umax tmin' tmid  clip' (not revCurves) +++               go q pr umin umax tmid  tmax' clip' (not revCurves)+      else let (ql, qr) = q `splitAtParam` 0.5+               umid = avg umin umax+           in  go ql p' umin umid tmin' tmax' clip' (not revCurves) +++               go qr p' umid umax tmin' tmax' clip' (not revCurves)++    -- iterate with the curves reversed.+    | otherwise = go q p' umin umax tmin' tmax' clip' (not revCurves)+    where+      chopInterval              = chopCubics p q+      Just (tminChop, tmaxChop) = chopInterval+      p'    = section p tminChop tmaxChop+      clip' = tmaxChop - tminChop+      tmin' = tmax * tminChop + tmin * (1 - tminChop)+      tmax' = tmax * tmaxChop + tmin * (1 - tmaxChop)++-- | Find the zero of a 1D Bêzier curve of any degree.  Note that this+--   can be used as a Bernstein polynomial root solver by converting from+--   the power basis to the Bernstein basis.+bezierFindRoot :: OrderedField n+               => n   -- ^ The accuracy+               -> BernsteinPoly n -- ^ the Bernstein coefficients of the polynomial+               -> n   -- ^ The lower bound of the interval+               -> n   -- ^ The upper bound of the interval+               -> [n] -- ^ The roots found+bezierFindRoot eps p tmin tmax+  -- If we generated the max number of roots and tmax is also a root+  -- (which is not among the generated ones), there must in fact be an+  -- infinite number of roots, so just include tmax.+  | length roots == bernsteinDegree p && last roots /= tmax && abs (evaluateBernstein p tmax) <= eps+  = roots ++ [tmax]+  | otherwise = roots+ where+  -- Lazily take a number of roots at most the degree of the bernstein+  -- polynomial, to avoid generating a ton of roots in the case of a+  -- straight Bezier segment along the x-axis. See https://github.com/diagrams/diagrams-contrib/issues/91 .+  roots = take (bernsteinDegree p) $ go p tmin tmax+  go p tmin tmax+    | isNothing chopInterval = []+    | tmax' - tmin' < eps = [avg tmin' tmax']+    | clip > 0.8 = let (p1, p2) = splitAtParam newP 0.5+                       tmid     = tmin' + (tmax' - tmin') / 2+                   in  go p1 tmin' tmid  +++                       go p2 tmid  tmax'+    | otherwise = bezierFindRoot eps newP tmin' tmax'+    where+      chopInterval              = chopYs (bernsteinCoeffs p)+      Just (tminChop, tmaxChop) = chopInterval+      newP  = section p tminChop tmaxChop+      clip  = tmaxChop - tminChop+      tmin' = tmax * tminChop + tmin * (1 - tminChop)+      tmax' = tmax * tmaxChop + tmin * (1 - tmaxChop)++------------------------------------------------------------------------+-- Internal+------------------------------------------------------------------------++-- | An approximation of the fat line for a cubic Bêzier segment. Returns+--   @(0,0)@ for a linear segment.+fatLine :: OrderedField n => FixedSegment V2 n -> (n,n)+fatLine (FCubic p0 p1 p2 p3)+  = case (d1 > 0, d2 > 0) of+      (True,  True)  -> (0,                0.75 * max d1 d2)+      (False, False) -> (0.75 * min d1 d2, 0               )+      (True,  False) -> (4/9 * d2,         4/9 * d1        )+      (False, True)  -> (4/9 * d1,         4/9 * d2        )+  where+    d = lineDistance p0 p3+    d1 = d p1; d2 = d p2+fatLine _ = (0,0)++chopYs :: OrderedField n => [n] -> Maybe (n, n)+chopYs ds = chopHull 0 0 points+  where+    points = zipWith mkP2 [fromIntegral i / fromIntegral n | i <- [0..n]] ds+    n      = length ds - 1++chopCubics :: OrderedField n => FixedSegment V2 n -> FixedSegment V2 n -> Maybe (n,n)+chopCubics p q@(FCubic q0 _ _ q3)+  = chopHull dmin dmax dps+  where+    dps = zipWith mkP2 [0, 1/3, 2/3, 1] ds+    ds  = p ^.. each . to d+    d   = lineDistance q0 q3+    --+    (dmin,dmax) = fatLine q+chopCubics _ _ = Nothing++-- Reduce the interval which the intersection is known to lie in using the fat+-- line of one curve and convex hull of the points formed from the distance to+-- the thin line of the other+chopHull :: OrderedField n => n -> n -> [P2 n] -> Maybe (n, n)+chopHull dmin dmax dps = do+  tL <- testBelow upper           $ testBetween (head upper) $ testAbove lower+  tR <- testBelow (reverse upper) $ testBetween (last upper) $ testAbove (reverse lower)+  Just (tL, tR)+    where+      (upper, lower) = sortedConvexHull dps++      testBelow (p1@(P (V2 _ y1)) : p2@(P (V2 _ y2)) : ps) continue+        | y1 >= dmin = continue+        | y1 >  y2   = Nothing+        | y2 <  dmin = testBelow (p2:ps) continue+        | otherwise  = Just $ intersectPt dmin p1 p2+      testBelow _ _  = Nothing++      testBetween (P (V2 x y)) continue+        | y <= dmax = Just x+        | otherwise = continue++      testAbove (p1@(P (V2 _ y1)) : p2@(P (V2 _ y2)) : ps)+        | y1 < y2      = Nothing+        | y2 > dmax    = testAbove (p2:ps)+        | y2 - y1 == 0 = Nothing  -- Check this condition to prevent+                                  -- division by zero in `intersectPt`.+        | otherwise    = Just $ intersectPt dmax p1 p2+      testAbove _      = Nothing++      -- find the x value where the line through the two points+      -- intersect the line y=d.  Note that `y2 - y1 != 0` due+      -- to checks above.+      intersectPt d (P (V2 x1 y1)) (P (V2 x2 y2)) =+          x1 + (d - y1) * (x2 - x1) / (y2 - y1)++++bezierToBernstein :: Fractional n => FixedSegment V2 n -> (BernsteinPoly n, BernsteinPoly n)+bezierToBernstein seg =+    (listToBernstein $ map (view _x) coeffs, listToBernstein $ map (view _y) coeffs)+  where coeffs = toListOf each seg++------------------------------------------------------------------------+-- Lines+------------------------------------------------------------------------++-- Could split this into a separate module.++-- | Returns @(a, b, c, d)@ such that @ax + by + c = 0@ is the line going through+--   @p1@ and @p2@ with @(a^2)/d + (b^2)/d = 1@.  We delay the division by+--   @d@ as it may not be needed in all cases and @d@ may be zero.+lineEquation :: Floating n => P2 n -> P2 n -> (n, n, n, n)+lineEquation (P (V2 x1 y1)) (P (V2 x2 y2)) = (a, b, c, d)+  where+    c  = -(x1*a + y1*b)+    a = y1 - y2+    b = x2 - x1+    d  = a*a + b*b++-- | Return the distance from a point to the line.+lineDistance :: (Ord n, Floating n) => P2 n -> P2 n -> P2 n -> n+lineDistance p1 p2 p3@(P (V2 x y))+    -- I have included the check that d' <= 0 in case+    -- there exists some d > 0 where sqrt d == 0.  I don't+    -- think this can happen as sqrt is at least recommended+    -- to be within one value of correct for sqrt and near+    -- zero values get bigger.+    | d <= 0 || d' <= 0 = norm (p1 .-. p3)+    | otherwise = (a*x + b*y + c) / d'+  where+    (a, b, c, d) = lineEquation p1 p2+    d' = sqrt d++-- clockwise :: (Num n, Ord n) => V2 n -> V2 n -> Bool+-- clockwise a b = a `cross2` b <= 0++avg :: Fractional n => n -> n -> n+avg a b = (a + b)/2++lineLine :: (Fractional n, Eq n) => Located (V2 n) -> Located (V2 n) -> Maybe (n,n)+lineLine (viewLoc -> (p,r)) (viewLoc -> (q,s))+  | x1 == 0 && x2 /= 0 = Nothing                 -- parallel+  | otherwise          = Just (x3 / x1, x2 / x1) -- intersecting or collinear+  where+    x1 = r × s+    x2 = v × r+    x3 = v × s+    v  = q .-. p++(×) :: Num n => V2 n -> V2 n -> n+(×) = cross2++mkLine :: InSpace v n (v n) => Point v n -> Point v n -> Located (v n)+mkLine p0 p1 = (p1 .-. p0) `at` p0++segLine :: InSpace v n (v n) => FixedSegment v n -> Located (v n)+segLine (FLinear p0 p1)    = mkLine p0 p1+segLine (FCubic p0 _ _ p3) = mkLine p0 p3++-- This function uses `defEps`, but is used in functions+-- above that take an epsilon parameter.  It would be nice+-- to clearify the meaning of each of these epsilons.+inRange :: (Fractional n, Ord n) => n -> Bool+inRange x = x < (1+defEps) && x > (-defEps) 
+ src/Diagrams/TwoD/Segment/Bernstein.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE TypeFamilies  #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.TwoD.Segment.Bernstein+-- Copyright   :  (c) 2014-2015 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Bernstein polynomials, used internally by code to find+-- intersections of paths.  This module is probably not of any+-- relevance to most users of diagrams.+-----------------------------------------------------------------------------+module Diagrams.TwoD.Segment.Bernstein+  ( BernsteinPoly (..)+  , listToBernstein+  , evaluateBernstein++  , degreeElevate+  , bernsteinDeriv+  , evaluateBernsteinDerivs+  ) where++import           Data.List           (tails)+import           Diagrams.Core.V+import           Diagrams.Parametric+import           Linear.V1++-- | Compute the binomial coefficients of degree n.+binomials :: Num n => Int -> [n]+binomials n = map fromIntegral $ scanl (\x m -> x * (n - m+1) `quot` m) 1 [1..n]++data BernsteinPoly n = BernsteinPoly+  { bernsteinDegree :: Int+  , bernsteinCoeffs :: [n]+  } deriving (Show, Functor)++type instance V        (BernsteinPoly n) = V1+type instance N        (BernsteinPoly n) = n+type instance Codomain (BernsteinPoly n) = V1++-- | Create a bernstein polynomial from a list of coëfficients.+listToBernstein :: Fractional n => [n] -> BernsteinPoly n+listToBernstein [] = 0+listToBernstein l  = BernsteinPoly (length l - 1) l++-- | Degree elevate a bernstein polynomial a number of times.+degreeElevate :: Fractional n => BernsteinPoly n -> Int -> BernsteinPoly n+degreeElevate b                    0     = b+degreeElevate (BernsteinPoly lp p) times =+  degreeElevate (BernsteinPoly (lp+1) (head p:inner p 1)) (times-1)+  where+    n = fromIntegral lp++    inner []         _ = [0]+    inner [a]        _ = [a]+    inner (a:b:rest) i = (i*a/(n+1) + b*(1 - i/(n+1))) : inner (b:rest) (i+1)++-- | Evaluate the bernstein polynomial.+evaluateBernstein :: Fractional n => BernsteinPoly n -> n -> n+evaluateBernstein (BernsteinPoly _ [])       _ = 0+evaluateBernstein (BernsteinPoly _ [b])      _ = b+evaluateBernstein (BernsteinPoly lp (b':bs)) t = go t n (b'*u) 2 bs+  where+    u = 1-t+    n = fromIntegral lp++    go tn bc tmp _ [b]      = tmp + tn*bc*b+    go tn bc tmp i (b:rest) =+      go (tn*t)              -- tn+         (bc*(n - i+1)/i)    -- bc+         ((tmp + tn*bc*b)*u) -- tmp+         (i+1)               -- i+         rest+    go _ _ _ _ []           = error "evaluateBernstein: impossible"++-- | Evaluate the bernstein polynomial and its derivatives.+evaluateBernsteinDerivs :: Fractional n => BernsteinPoly n -> n -> [n]+evaluateBernsteinDerivs b t+  | bernsteinDegree b == 0 = [evaluateBernstein b t]+  | otherwise              = evaluateBernstein b t : evaluateBernsteinDerivs (bernsteinDeriv b) t++-- | Find the derivative of a bernstein polynomial.+bernsteinDeriv :: Fractional n => BernsteinPoly n -> BernsteinPoly n+bernsteinDeriv (BernsteinPoly 0 _)  = 0+bernsteinDeriv (BernsteinPoly lp p) =+  -- BernsteinPoly (lp-1) $ map (* fromIntegral lp) $ zipWith (-) (tail p) p+  BernsteinPoly (lp-1) $ zipWith (\a b -> (a - b) * fromIntegral lp) (tail p) p++instance Fractional n => Parametric (BernsteinPoly n) where+  atParam b = V1 . evaluateBernstein b+instance Num n        => DomainBounds (BernsteinPoly n)+instance Fractional n => EndValues    (BernsteinPoly n)+instance Fractional n => Sectionable  (BernsteinPoly n) where+  splitAtParam  = bernsteinSplit+  reverseDomain (BernsteinPoly i xs) = BernsteinPoly i (reverse xs)++-- | Split a bernstein polynomial.+bernsteinSplit :: Num n => BernsteinPoly n -> n -> (BernsteinPoly n, BernsteinPoly n)+bernsteinSplit (BernsteinPoly lp p) t =+  (BernsteinPoly lp $ map head controls,+   BernsteinPoly lp $ reverse $ map last controls)+  where+    interp a b = (1-t)*a + t*b++    terp [_] = []+    terp l   = let ctrs = zipWith interp l (tail l)+               in  ctrs : terp ctrs+    controls = p : terp p++instance Fractional n => Num (BernsteinPoly n) where+  ba@(BernsteinPoly la a) + bb@(BernsteinPoly lb b)+    | la < lb   = BernsteinPoly lb $ zipWith (+) (bernsteinCoeffs $ degreeElevate ba $ lb - la) b+    | la > lb   = BernsteinPoly la $ zipWith (+) a (bernsteinCoeffs $ degreeElevate bb $ la - lb)+    | otherwise = BernsteinPoly la $ zipWith (+) a b++  ba@(BernsteinPoly la a) - bb@(BernsteinPoly lb b)+    | la < lb   = BernsteinPoly lb $ zipWith (-) (bernsteinCoeffs $ degreeElevate ba (lb - la)) b+    | la > lb   = BernsteinPoly la $ zipWith (-) a (bernsteinCoeffs $ degreeElevate bb (la - lb))+    | otherwise = BernsteinPoly la $ zipWith (-) a b++  (BernsteinPoly la a) * (BernsteinPoly lb b) =+    BernsteinPoly (la+lb) $+    zipWith (flip (/)) (binomials (la + lb)) $+                   init $ map sum $+                   map (zipWith (*) a') (down b') +++                   map (zipWith (*) (reverse b')) (tail $ tails a')+                   -- zipWith (zipWith (*)) (tail $ tails a') (repeat $ reverse b')+    where down l = tail $ scanl (flip (:)) [] l -- [[1], [2, 1], [3, 2, 1], ...+          a' = zipWith (*) a (binomials la)+          b' = zipWith (*) b (binomials lb)++  fromInteger a = BernsteinPoly 0 [fromInteger a]++  signum (BernsteinPoly _ [])    = 0+  signum (BernsteinPoly _ (a:_)) = BernsteinPoly 0 [signum a]++  abs = fmap abs
src/Diagrams/TwoD/Shapes.hs view
@@ -1,6 +1,11 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TemplateHaskell  #-} {-# LANGUAGE TypeFamilies     #-} +{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Shapes@@ -25,6 +30,7 @@        , square        , pentagon        , hexagon+       , heptagon        , septagon        , octagon        , nonagon@@ -39,13 +45,17 @@          -- * Other shapes         , roundedRect-       , RoundedRectOpts(..)+       , RoundedRectOpts(..), radiusTL, radiusTR, radiusBL, radiusBR        , roundedRect'        ) where +import           Control.Lens            (makeLenses, op, (&), (.~), (<>~), (^.))+import           Data.Default+import           Data.Semigroup+ import           Diagrams.Core -import           Diagrams.Coordinates+import           Diagrams.Angle import           Diagrams.Located        (at) import           Diagrams.Path import           Diagrams.Segment@@ -55,56 +65,54 @@ import           Diagrams.TwoD.Polygons import           Diagrams.TwoD.Transform import           Diagrams.TwoD.Types-+import           Diagrams.TwoD.Vector import           Diagrams.Util -import           Data.Default.Class-import           Data.Semigroup  -- | Create a centered horizontal (L-R) line of the given length. -----   <<diagrams/hruleEx.svg#diagram=hruleEx&width=300>>+--   <<diagrams/src_Diagrams_TwoD_Shapes_hruleEx.svg#diagram=hruleEx&width=300>> -----   > hruleEx = vcat' with {sep = 0.2} (map hrule [1..5])+--   > hruleEx = vcat' (with & sep .~ 0.2) (map hrule [1..5]) --   >         # centerXY # pad 1.1-hrule :: (TrailLike t, V t ~ R2) => Double -> t-hrule d = trailLike $ trailFromSegments [straight (d & 0)] `at` (p2 (-d/2,0))+hrule :: (InSpace V2 n t, TrailLike t) => n -> t+hrule d = trailLike $ trailFromSegments [straight $ r2 (d, 0)] `at` p2 (-d/2,0)  -- | Create a centered vertical (T-B) line of the given length. -----   <<diagrams/vruleEx.svg#diagram=vruleEx&height=100>>+--   <<diagrams/src_Diagrams_TwoD_Shapes_vruleEx.svg#diagram=vruleEx&height=100>> -----   > vruleEx = hcat' with {sep = 0.2} (map vrule [1, 1.2 .. 2])+--   > vruleEx = hcat' (with & sep .~ 0.2) (map vrule [1, 1.2 .. 2]) --   >         # centerXY # pad 1.1-vrule :: (TrailLike t, V t ~ R2) => Double -> t-vrule d = trailLike $ trailFromSegments [straight (0 & (-d))] `at` (p2 (0,d/2))+vrule :: (InSpace V2 n t, TrailLike t) => n -> t+vrule d = trailLike $ trailFromSegments [straight $ r2 (0, -d)] `at` p2 (0,d/2)  -- | A square with its center at the origin and sides of length 1, --   oriented parallel to the axes. -----   <<diagrams/unitSquareEx.svg#diagram=unitSquareEx&width=100>>-unitSquare :: (TrailLike t, V t ~ R2) => t-unitSquare = polygon with { polyType   = PolyRegular 4 (sqrt 2 / 2)-                          , polyOrient = OrientH }+--   <<diagrams/src_Diagrams_TwoD_Shapes_unitSquareEx.svg#diagram=unitSquareEx&width=100>>+unitSquare :: (InSpace V2 n t, TrailLike t) => t+unitSquare = polygon (def & polyType   .~ PolyRegular 4 (sqrt 2 / 2)+                          & polyOrient .~ OrientH)  -- > unitSquareEx = unitSquare # pad 1.1 # showOrigin  -- | A square with its center at the origin and sides of the given --   length, oriented parallel to the axes. -----   <<diagrams/squareEx.svg#diagram=squareEx&width=200>>-square :: (TrailLike t, Transformable t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_squareEx.svg#diagram=squareEx&width=200>>+square :: (InSpace V2 n t, TrailLike t) => n -> t square d = rect d d --- > squareEx = hcat' with {sep = 0.5} [square 1, square 2, square 3]--- >          # centerXY # pad 1.1 # lw 0.03+-- > squareEx = hcat' (with & sep .~ 0.5) [square 1, square 2, square 3]+-- >          # centerXY # pad 1.1  -- | @rect w h@ is an axis-aligned rectangle of width @w@ and height --   @h@, centered at the origin. -----   <<diagrams/rectEx.svg#diagram=rectEx&width=150>>-rect :: (TrailLike t, Transformable t, V t ~ R2) => Double -> Double -> t-rect w h = trailLike . head . pathTrails $ unitSquare # scaleX w # scaleY h+--   <<diagrams/src_Diagrams_TwoD_Shapes_rectEx.svg#diagram=rectEx&width=150>>+rect :: (InSpace V2 n t, TrailLike t) => n -> n -> t+rect w h = trailLike . head . op Path $ unitSquare # scaleX w # scaleY h  -- > rectEx = rect 1 0.7 # pad 1.1 @@ -135,19 +143,19 @@ --   polygons of a given /radius/). -- --   The polygon will be oriented with one edge parallel to the x-axis.-regPoly :: (TrailLike t, V t ~ R2) => Int -> Double -> t-regPoly n l = polygon with { polyType =+regPoly :: (InSpace V2 n t, TrailLike t) => Int -> n -> t+regPoly n l = polygon (def & polyType .~                                PolySides-                                 (repeat (1/ fromIntegral n :: Turn))+                                 (repeat (1/fromIntegral n @@ turn))                                  (replicate (n-1) l)-                           , polyOrient = OrientH-                           }+                           & polyOrient .~ OrientH+                           )  -- > shapeEx sh   = sh 1 # pad 1.1 -- > triangleEx   = shapeEx triangle -- > pentagonEx   = shapeEx pentagon -- > hexagonEx    = shapeEx hexagon--- > septagonEx   = shapeEx septagon+-- > heptagonEx   = shapeEx heptagon -- > octagonEx    = shapeEx octagon -- > nonagonEx    = shapeEx nonagon -- > decagonEx    = shapeEx decagon@@ -155,76 +163,92 @@ -- > dodecagonEx  = shapeEx dodecagon  -- | A synonym for 'triangle', provided for backwards compatibility.-eqTriangle :: (TrailLike t, V t ~ R2) => Double -> t+eqTriangle :: (InSpace V2 n t, TrailLike t) => n -> t eqTriangle = triangle  -- | An equilateral triangle, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/triangleEx.svg#diagram=triangleEx&width=100>>-triangle :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_triangleEx.svg#diagram=triangleEx&width=100>>+triangle :: (InSpace V2 n t, TrailLike t) => n -> t triangle = regPoly 3  -- | A regular pentagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/pentagonEx.svg#diagram=pentagonEx&width=100>>-pentagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_pentagonEx.svg#diagram=pentagonEx&width=100>>+pentagon :: (InSpace V2 n t, TrailLike t) => n -> t pentagon = regPoly 5  -- | A regular hexagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/hexagonEx.svg#diagram=hexagonEx&width=100>>-hexagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_hexagonEx.svg#diagram=hexagonEx&width=100>>+hexagon :: (InSpace V2 n t, TrailLike t) => n -> t hexagon = regPoly 6 --- | A regular septagon, with sides of the given length and base+-- | A regular heptagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/septagonEx.svg#diagram=septagonEx&width=100>>-septagon :: (TrailLike t, V t ~ R2) => Double -> t-septagon = regPoly 7+--   <<diagrams/src_Diagrams_TwoD_Shapes_heptagonEx.svg#diagram=heptagonEx&width=100>>+heptagon :: (InSpace V2 n t, TrailLike t) => n -> t+heptagon = regPoly 7 +-- | A synonym for 'heptagon'.  It is, however, completely inferior,+--   being a base admixture of the Latin /septum/ (seven) and the+--   Greek γωνία (angle).+septagon :: (InSpace V2 n t, TrailLike t) => n -> t+septagon = heptagon+ -- | A regular octagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/octagonEx.svg#diagram=octagonEx&width=100>>-octagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_octagonEx.svg#diagram=octagonEx&width=100>>+octagon :: (InSpace V2 n t, TrailLike t) => n -> t octagon = regPoly 8  -- | A regular nonagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/nonagonEx.svg#diagram=nonagonEx&width=100>>-nonagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_nonagonEx.svg#diagram=nonagonEx&width=100>>+nonagon :: (InSpace V2 n t, TrailLike t) => n -> t nonagon = regPoly 9  -- | A regular decagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/decagonEx.svg#diagram=decagonEx&width=100>>-decagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_decagonEx.svg#diagram=decagonEx&width=100>>+decagon :: (InSpace V2 n t, TrailLike t) => n -> t decagon = regPoly 10  -- | A regular hendecagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/hendecagonEx.svg#diagram=hendecagonEx&width=100>>-hendecagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_hendecagonEx.svg#diagram=hendecagonEx&width=100>>+hendecagon :: (InSpace V2 n t, TrailLike t) => n -> t hendecagon = regPoly 11  -- | A regular dodecagon, with sides of the given length and base --   parallel to the x-axis. -----   <<diagrams/dodecagonEx.svg#diagram=dodecagonEx&width=100>>-dodecagon :: (TrailLike t, V t ~ R2) => Double -> t+--   <<diagrams/src_Diagrams_TwoD_Shapes_dodecagonEx.svg#diagram=dodecagonEx&width=100>>+dodecagon :: (InSpace V2 n t, TrailLike t) => n -> t dodecagon = regPoly 12  ------------------------------------------------------------ --  Other shapes  ------------------------------------------ ------------------------------------------------------------+data RoundedRectOpts d = RoundedRectOpts { _radiusTL :: d+                                         , _radiusTR :: d+                                         , _radiusBL :: d+                                         , _radiusBR :: d+                                         } +makeLenses ''RoundedRectOpts++instance (Num d) => Default (RoundedRectOpts d) where+  def = RoundedRectOpts 0 0 0 0+ -- | @roundedRect w h r@ generates a closed trail, or closed path --   centered at the origin, of an axis-aligned rectangle with width --   @w@, height @h@, and circular rounded corners of radius @r@.  If@@ -236,30 +260,30 @@ --   a different radius for each corner individually, use --   'roundedRect'' instead. -----   <<diagrams/roundedRectEx.svg#diagram=roundedRectEx&width=400>>+--   <<diagrams/src_Diagrams_TwoD_Shapes_roundedRectEx.svg#diagram=roundedRectEx&width=400>> -----   > roundedRectEx = pad 1.1 . centerXY $ hcat' with { sep = 0.2 }+--   > roundedRectEx = pad 1.1 . centerXY $ hcat' (with & sep .~ 0.2) --   >   [ roundedRect  0.5 0.4 0.1 --   >   , roundedRect  0.5 0.4 (-0.1)---   >   , roundedRect' 0.7 0.4 with { radiusTL = 0.2---   >                               , radiusTR = -0.2---   >                               , radiusBR = 0.1 }+--   >   , roundedRect' 0.7 0.4 (with & radiusTL .~ 0.2+--   >                                & radiusTR .~ -0.2+--   >                                & radiusBR .~ 0.1) --   >   ] -roundedRect :: (TrailLike t, V t ~ R2) => Double -> Double -> Double -> t-roundedRect w h r = roundedRect' w h (with { radiusTL = r,-                                             radiusBR = r,-                                             radiusTR = r,-                                             radiusBL = r})+roundedRect :: (InSpace V2 n t, TrailLike t, RealFloat n) => n -> n -> n -> t+roundedRect w h r = roundedRect' w h (def & radiusTL .~ r+                                          & radiusBR .~ r+                                          & radiusTR .~ r+                                          & radiusBL .~ r)  -- | @roundedRect'@ works like @roundedRect@ but allows you to set the radius of --   each corner indivually, using @RoundedRectOpts@. The default corner radius is 0. --   Each radius can also be negative, which results in the curves being reversed --   to be inward instead of outward.-roundedRect' :: (TrailLike t, V t ~ R2) => Double -> Double -> RoundedRectOpts -> t+roundedRect' :: (InSpace V2 n t, TrailLike t, RealFloat n) => n -> n -> RoundedRectOpts n -> t roundedRect' w h opts    = trailLike-   . (`at` (p2 (w/2, abs rBR - h/2)))+   . (`at` p2 (w/2, abs rBR - h/2))    . wrapTrail    . glueLine    $ seg (0, h - abs rTR - abs rBR)@@ -279,7 +303,7 @@         rBL                 = clampCnr radiusBR radiusTL radiusTR radiusBL         rTR                 = clampCnr radiusTL radiusBR radiusBL radiusTR         rBR                 = clampCnr radiusBL radiusTR radiusTL radiusBR-        clampCnr rx ry ro r = let (rx',ry',ro',r') = (rx opts, ry opts, ro opts, r opts)+        clampCnr rx ry ro r = let (rx',ry',ro',r') = (opts^.rx, opts^.ry, opts^.ro, opts^.r)                                 in clampDiag ro' . clampAdj h ry' . clampAdj w rx' $ r'         -- prevent curves of adjacent corners from overlapping         clampAdj len adj r  = if abs r > len/2@@ -291,14 +315,8 @@                                 else r         sign n = if n < 0 then -1 else 1         mkCorner k r | r == 0    = mempty-                     | r < 0     = doArc 3 2+                     | r < 0     = doArc 3 (-1)                      | otherwise = doArc 0 1-                     where doArc d d' = arc' r ((k+d)/4) ((k+d')/4:: Turn)--data RoundedRectOpts = RoundedRectOpts { radiusTL :: Double-                                       , radiusTR :: Double-                                       , radiusBL :: Double-                                       , radiusBR :: Double-                                       }-instance Default RoundedRectOpts where-  def = RoundedRectOpts 0 0 0 0+                     where+                       doArc d s =+                           arc' r (xDir & _theta <>~ ((k+d)/4 @@ turn)) (s/4 @@ turn)
src/Diagrams/TwoD/Size.hs view
@@ -1,5 +1,7 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies     #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Size@@ -12,132 +14,65 @@ ----------------------------------------------------------------------------- module Diagrams.TwoD.Size        (-         -- * Size and extent of diagrams in R2          -- ** Computing sizes-         width, height, size2D, sizeSpec2D-       , extentX, extentY, center2D+         width, height+       , extentX, extentY           -- ** Specifying sizes-       , SizeSpec2D(..)-       , mkSizeSpec--       , requiredScaleT, requiredScale+       , mkSizeSpec2D+       , mkWidth+       , mkHeight+       , dims2D -         -- ** Changing the size of things-       , sized, sizedAs        ) where  import           Diagrams.Core+import           Diagrams.Core.Envelope+import           Diagrams.Size import           Diagrams.TwoD.Types import           Diagrams.TwoD.Vector -import           Control.Applicative  (liftA2, (<$>))-import           Control.Arrow        ((&&&), (***))- ------------------------------------------------------------ -- Computing diagram sizes ------------------------------------------------------------  -- | Compute the width of an enveloped object.-width :: (Enveloped a, V a ~ R2) => a -> Double-width = maybe 0 (negate . uncurry (-)) . extentX+--+--   Note this is just @diameter unitX@.+width :: (InSpace V2 n a, Enveloped a) => a -> n+width = diameter unitX  -- | Compute the height of an enveloped object.-height :: (Enveloped a, V a ~ R2) => a -> Double-height = maybe 0 (negate . uncurry (-)) . extentY---- | Compute the width and height of an enveloped object.-size2D :: (Enveloped a, V a ~ R2) => a -> (Double, Double)-size2D = width &&& height---- | Compute the size of an enveloped object as a 'SizeSpec2D' value.-sizeSpec2D :: (Enveloped a, V a ~ R2) => a -> SizeSpec2D-sizeSpec2D = uncurry Dims . size2D+height :: (InSpace V2 n a, Enveloped a) => a -> n+height = diameter unitY --- | Compute the absolute  x-coordinate range of an enveloped object in---   R2, in  the form (lo,hi).   Return @Nothing@ for objects  with an---   empty envelope.-extentX :: (Enveloped a, V a ~ R2) => a -> Maybe (Double, Double)-extentX d = (\f -> (-f unit_X, f unitX)) <$> (appEnvelope . getEnvelope $ d)+-- | Compute the absolute x-coordinate range of an enveloped object in+--   the form @(lo,hi)@. Return @Nothing@ for objects with an empty+--   envelope.+--+--   Note this is just @extent unitX@.+extentX :: (InSpace v n a, R1 v, Enveloped a) => a -> Maybe (n, n)+extentX = extent unitX  -- | Compute the absolute y-coordinate range of an enveloped object in---   R2, in the form (lo,hi).-extentY :: (Enveloped a, V a ~ R2) => a -> Maybe (Double, Double)-extentY d = (\f -> (-f unit_Y, f unitY)) <$> (appEnvelope . getEnvelope $ d)---- | Compute the point at the center (in the x- and y-directions) of a---   enveloped object.  Return the origin for objects with an empty+--   the form @(lo,hi)@. Return @Nothing@ for objects with an empty --   envelope.-center2D :: (Enveloped a, V a ~ R2) => a -> P2-center2D = maybe origin (p2 . (mid *** mid)) . mm . (extentX &&& extentY)-  where mm = uncurry (liftA2 (,))-        mid = (/2) . uncurry (+)----------------------------------------------------------------- Size specifications----------------------------------------------------------------- | A specification of a (requested) rectangular size.-data SizeSpec2D = Width  Double       -- ^ Specify an explicit-                                      -- width. The height should be-                                      -- determined automatically (so-                                      -- as to preserve aspect ratio).-                | Height Double       -- ^ Specify an explicit-                                      -- height. The width should be-                                      -- determined automatically (so-                                      -- as to preserve aspect ratio).-                | Dims Double Double  -- ^ An explicit specification-                                      -- of a width and height.-                | Absolute            -- ^ Absolute size: use whatever-                                      -- size an object already has;-                                      -- do not rescale.-  deriving (Eq, Ord, Show)+extentY :: (InSpace v n a, R2 v, Enveloped a) => a -> Maybe (n, n)+extentY = extent unitY --- | Create a size specification from a possibly-specified width and---   height.-mkSizeSpec :: Maybe Double -> Maybe Double -> SizeSpec2D-mkSizeSpec Nothing  Nothing  = Absolute-mkSizeSpec (Just w) Nothing  = Width w-mkSizeSpec Nothing  (Just h) = Height h-mkSizeSpec (Just w) (Just h) = Dims w h+-- | Make a 'SizeSpec' from possibly-specified width and height.+mkSizeSpec2D :: Num n => Maybe n -> Maybe n -> SizeSpec V2 n+mkSizeSpec2D x y = mkSizeSpec (V2 x y) --- | @requiredScaleT spec sz@ returns a transformation (a uniform scale)---   which can be applied to something of size @sz@ to make it fit the---   requested size @spec@, without changing the aspect ratio.-requiredScaleT :: SizeSpec2D -> (Double, Double) -> Transformation R2-requiredScaleT spec size = scaling (requiredScale spec size)+-- | Make a 'SizeSpec' from a width and height.+dims2D :: n -> n -> SizeSpec V2 n+dims2D x y = dims (V2 x y) --- | @requiredScale spec sz@ returns a scaling factor necessary to---   make something of size @sz@ fit the requested size @spec@,---   without changing the aspect ratio.  Hence an explicit---   specification of both dimensions may not be honored if the aspect---   ratios do not match; in that case the scaling will be as large as---   possible so that the object still fits within the requested size.-requiredScale :: SizeSpec2D -> (Double, Double) -> Double-requiredScale Absolute _    = 1-requiredScale (Width wSpec) (w,_)-  | wSpec == 0 || w == 0 = 1-  | otherwise            = wSpec / w-requiredScale (Height hSpec) (_,h)-  | hSpec == 0 || h == 0 = 1-  | otherwise            = hSpec / h-requiredScale (Dims wSpec hSpec) (w,h) = s-  where xscale  = wSpec / w-        yscale  = hSpec / h-        s'      = min xscale yscale-        s | isInfinite s' = 1-          | otherwise     = s'+-- | Make a 'SizeSpec' with only width defined.+mkWidth :: Num n => n -> SizeSpec V2 n+mkWidth w = dims (V2 w 0) --- | Uniformly scale any enveloped object so that it fits within the---   given size.-sized :: (Transformable a, Enveloped a, V a ~ R2)-      => SizeSpec2D -> a -> a-sized spec a = transform (requiredScaleT spec (size2D a)) a+-- | Make a 'SizeSpec' with only height defined.+mkHeight :: Num n => n -> SizeSpec V2 n+mkHeight h = dims (V2 0 h) --- | Uniformly scale an enveloped object so that it \"has the same---   size as\" (fits within the width and height of) some other---   object.-sizedAs :: ( Transformable a, Enveloped a, V a ~ R2-           , Enveloped b, V b ~ R2)-        => b -> a -> a-sizedAs other = sized (sizeSpec2D other)
src/Diagrams/TwoD/Text.hs view
@@ -1,13 +1,17 @@-{-# LANGUAGE DeriveDataTypeable-           , GeneralizedNewtypeDeriving-           , FlexibleContexts-           , TypeFamilies-           , MultiParamTypeClasses-  #-}+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE UndecidableInstances       #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Text--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -19,66 +23,99 @@   -- * Creating text diagrams     Text(..), TextAlignment(..)   , text, topLeftText, alignedText, baselineText+  , mkText, mkText'    -- * Text attributes+   -- ** Font family-  , Font(..), getFont, font-  -- ** Font size-  , FontSize(..), getFontSize, fontSize, fontSizeA-  -- ** Font slant-  , FontSlant(..), FontSlantA, getFontSlant, fontSlant, italic, oblique-  -- ** Font weight-  , FontWeight(..), FontWeightA, getFontWeight, fontWeight, bold-  ) where+  , Font(..), _Font+  , getFont, font, _font -import Diagrams.Attributes-import Diagrams.Core-import Diagrams.TwoD.Types+  -- ** Font size+  , FontSize(..), _FontSize+  , fontSize, recommendFontSize+  , fontSizeN, fontSizeO, fontSizeL, fontSizeG+  , getFontSize, fontSizeM+  , _fontSizeR, _fontSize, _fontSizeU -import Data.AffineSpace ((.-.))+  -- ** Font slant+  , FontSlant(..)+  , getFontSlant, fontSlant, italic, oblique, _fontSlant -import Data.Semigroup+  -- ** Font weight+  , FontWeight(..)+  , getFontWeight, fontWeight, bold, bolder, lighter, _fontWeight+  , thinWeight, ultraLight, light, mediumWeight, heavy, semiBold, ultraBold -import Data.Colour+  ) where -import           Data.Default.Class+import           Control.Lens             hiding (transform)+import           Diagrams.Attributes      (committed)+import           Diagrams.Core+import           Diagrams.Core.Envelope   (pointEnvelope)+import           Diagrams.TwoD.Attributes (recommendFillColor)+import           Diagrams.TwoD.Types +import           Data.Colour              hiding (over)+import           Data.Default+import           Data.Monoid.Recommend+import           Data.Semigroup import           Data.Typeable +import           Linear.Affine+ ------------------------------------------------------------ -- Text diagrams ------------------------------------------------------------ --- | A text primitive consists of the string contents and alignment---   specification, along with a transformation mapping from the local---   vector space of the text to the vector space in which it is---   embedded.-data Text = Text T2 TextAlignment String--type instance V Text = R2+-- | A 'Text' primitive consists of the string contents, text alignment+--   and the transformation to be applied. The transformation is scale+--   invarient, the average scale of the transform should always be 1.+--   All text scaling is obtained from the 'FontSize' attribute.+--+--   This constructor should not be used directly. Use 'text',+--   'alignedText' or 'baselineText'.+data Text n = Text (T2 n) (TextAlignment n) String+  deriving Typeable -instance Transformable Text where-  transform t (Text tt a s) = Text (t <> tt) a s+type instance V (Text n) = V2+type instance N (Text n) = n -instance IsPrim Text+instance Floating n => Transformable (Text n) where+  transform t (Text tt a s) = Text (t <> tt <> t') a s+    where t' = scaling (1 / avgScale t) -instance HasOrigin Text where+instance Floating n => HasOrigin (Text n) where   moveOriginTo p = translate (origin .-. p) -instance Renderable Text NullBackend where+instance Floating n => Renderable (Text n) NullBackend where   render _ _ = mempty  -- | @TextAlignment@ specifies the alignment of the text's origin.-data TextAlignment = BaselineText | BoxAlignedText Double Double+data TextAlignment n = BaselineText | BoxAlignedText n n -mkText :: Renderable Text b => TextAlignment -> String -> Diagram b R2-mkText a t = recommendFillColor black   -- See Note [recommendFillColor]-           $ mkQD (Prim (Text mempty a t))-                       mempty-                       mempty-                       mempty-                       mempty+-- | Make a text from a 'TextAlignment', recommending a fill colour of+--   'black' and 'fontSize' of @'local' 1@.+mkText :: (TypeableFloat n, Renderable (Text n) b)+  => TextAlignment n -> String -> QDiagram b V2 n Any+mkText a = recommendFillColor black+           -- See Note [recommendFillColor]+         . recommendFontSize (local 1)+           -- See Note [recommendFontSize]+         . mkText' a +-- | Make a text from a 'TextAlignment' without any default size or fill+--   colour. This is useful is you want to recommend your own using+--   'recommendFillColor' or 'recommendFontSize'.+mkText' :: (TypeableFloat n, Renderable (Text n) b)+  => TextAlignment n -> String -> QDiagram b V2 n Any+mkText' a t = mkQD (Prim $ Text mempty a t)+                   (pointEnvelope origin)+                   mempty+                   mempty+                   mempty++ -- ~~~~ Note [recommendFillColor]  -- The reason we "recommend" a fill color of black instead of setting@@ -101,12 +138,16 @@ -- recommendation; normally, the innermost occurrence of an attribute -- would override all outer occurrences. +-- ~~~~ Note [recommendFontSize]+-- The reason we "recommend" a font size is so any local scales get+-- recorded.+ -- | Create a primitive text diagram from the given string, with center --   alignment, equivalent to @'alignedText' 0.5 0.5@. -- --   Note that it /takes up no space/, as text size information is not --   available.-text :: Renderable Text b => String -> Diagram b R2+text :: (TypeableFloat n, Renderable (Text n) b) => String -> QDiagram b V2 n Any text = alignedText 0.5 0.5  -- | Create a primitive text diagram from the given string, origin at@@ -114,19 +155,21 @@ --   @'alignedText' 0 1@. -- --   Note that it /takes up no space/.-topLeftText :: Renderable Text b => String -> Diagram b R2+topLeftText :: (TypeableFloat n, Renderable (Text n) b) => String -> QDiagram b V2 n Any topLeftText = alignedText 0 1  -- | Create a primitive text diagram from the given string, with the --   origin set to a point interpolated within the bounding box.  The --   first parameter varies from 0 (left) to 1 (right), and the second---   parameter from 0 (bottom) to 1 (top).+--   parameter from 0 (bottom) to 1 (top). Some backends do not+--   implement this and instead snap to closest corner or the center. -- --   The height of this box is determined by the font's potential ascent --   and descent, rather than the height of the particular string. -- --   Note that it /takes up no space/.-alignedText :: Renderable Text b => Double -> Double -> String -> Diagram b R2+alignedText :: (TypeableFloat n, Renderable (Text n) b)+  => n -> n -> String -> QDiagram b V2 n Any alignedText w h = mkText (BoxAlignedText w h)  -- | Create a primitive text diagram from the given string, with the@@ -135,7 +178,8 @@ --   graphics library. -- --   Note that it /takes up no space/.-baselineText :: Renderable Text b => String -> Diagram b R2+baselineText :: (TypeableFloat n, Renderable (Text n) b)+  => String -> QDiagram b V2 n Any baselineText = mkText BaselineText  ------------------------------------------------------------@@ -149,6 +193,10 @@ --   @Font@ attributes override outer ones. newtype Font = Font (Last String)   deriving (Typeable, Semigroup, Eq)++_Font :: Iso' Font String+_Font = iso getFont (Font . Last)+ instance AttributeClass Font  -- | Extract the font family name from a @Font@ attribute.@@ -159,58 +207,123 @@ font :: HasStyle a => String -> a -> a font = applyAttr . Font . Last +-- | Lens onto the font name of a style.+_font :: Lens' (Style v n) (Maybe String)+_font = atAttr . mapping _Font+ -------------------------------------------------- -- Font size  -- | The @FontSize@ attribute specifies the size of a font's---   em-square, measured with respect to the current local vector space.---   Inner @FontSize@ attributes override outer ones.-newtype FontSize = FontSize (Last Double)-  deriving (Typeable, Semigroup, Eq)-instance AttributeClass FontSize+--   em-square.  Inner @FontSize@ attributes override outer ones.+newtype FontSize n = FontSize (Recommend (Last n))+  deriving (Typeable, Semigroup) -instance Default FontSize where-    def = FontSize (Last 1)+-- not sure why this can't be derived+instance Functor FontSize where+  fmap f (FontSize (Recommend (Last a))) = FontSize (Recommend (Last (f a)))+  fmap f (FontSize (Commit (Last a)))    = FontSize (Commit (Last (f a))) +_FontSize :: Iso' (FontSize n) (Recommend n)+_FontSize = iso getter setter+  where getter (FontSize (Recommend (Last a))) = Recommend a+        getter (FontSize (Commit    (Last a))) = Commit a+        setter (Recommend a) = FontSize $ Recommend (Last a)+        setter (Commit    a) = FontSize $ Commit (Last a)+      -- = iso (\(FontSize a) -> a) FontSize . mapping _Wrapped+      -- once we depend on monoid-extras-0.4++_FontSizeM :: Iso' (FontSizeM n) (Measured n (Recommend n))+_FontSizeM = mapping _FontSize++type FontSizeM n = Measured n (FontSize n)++instance Typeable n => AttributeClass (FontSize n)++instance Num n => Default (FontSizeM n) where+  def = FontSize . Recommend . Last <$> local 1+ -- | Extract the size from a @FontSize@ attribute.-getFontSize :: FontSize -> Double-getFontSize (FontSize (Last s)) = s+getFontSize :: FontSize n -> n+getFontSize (FontSize (Recommend (Last s))) = s+getFontSize (FontSize (Commit (Last s)))    = s  -- | Set the font size, that is, the size of the font's em-square as---   measured within the current local vector space.  The default size---   is @1@.-fontSize :: HasStyle a => Double -> a -> a-fontSize = applyAttr . FontSize . Last+--   measured within the current local vector space. The default size+--   is @local 1@ (which is applied by 'recommendFontSize').+fontSize :: (N a ~ n, Typeable n, HasStyle a) => Measure n -> a -> a+fontSize = applyMAttr . fmap (FontSize . Commit . Last) +-- | 'Recommend' a font size. Any use of 'fontSize' above this will+--   overwrite any recommended size. This should only be used with+--   'mkText'', other text functions already has a recommended font+--   size so this will be ignored.+recommendFontSize :: (N a ~ n, Typeable n, HasStyle a) => Measure n -> a -> a+recommendFontSize = applyMAttr . fmap (FontSize . Recommend . Last)++-- | A convenient synonym for 'fontSize (Global w)'.+fontSizeG :: (N a ~ n, Typeable n, Num n, HasStyle a) => n -> a -> a+fontSizeG = fontSize . global++-- | A convenient synonym for 'fontSize (Normalized w)'.+fontSizeN :: (N a ~ n, Typeable n, Num n, HasStyle a) => n -> a -> a+fontSizeN = fontSize . normalized++-- | A convenient synonym for 'fontSize (Output w)'.+fontSizeO :: (N a ~ n, Typeable n, HasStyle a) => n -> a -> a+fontSizeO = fontSize . output++-- | A convenient sysnonym for 'fontSize (Local w)'.+fontSizeL :: (N a ~ n, Typeable n, Num n, HasStyle a) => n -> a -> a+fontSizeL = fontSize . local+ -- | Apply a 'FontSize' attribute.-fontSizeA :: HasStyle a => FontSize -> a -> a-fontSizeA = applyAttr+fontSizeM :: (N a ~ n, Typeable n, HasStyle a) => FontSizeM n -> a -> a+fontSizeM = applyMAttr +_fontSizeR :: (Typeable n, OrderedField n) => Lens' (Style v n) (Measured n (Recommend n))+_fontSizeR = atMAttr . anon def (const False) . _FontSizeM++-- | Lens to commit a font size. This is *not* a valid lens (see+--   'commited'.+_fontSize :: (Typeable n, OrderedField n) => Lens' (Style v n) (Measure n)+_fontSize = _fontSizeR . mapping committed++_fontSizeU :: (Typeable n) => Lens' (Style v n) (Maybe n)+_fontSizeU = atAttr . mapping (_FontSize . committed)+ -------------------------------------------------- -- Font slant +-- | The @FontSlantA@ attribute specifies the slant (normal, italic,+--   or oblique) that should be used for all text within a diagram.+--   Inner @FontSlantA@ attributes override outer ones. data FontSlant = FontSlantNormal                | FontSlantItalic                | FontSlantOblique-    deriving (Eq)+  deriving (Eq, Show, Typeable, Ord) --- | The @FontSlantA@ attribute specifies the slant (normal, italic,---   or oblique) that should be used for all text within a diagram.---   Inner @FontSlantA@ attributes override outer ones.-newtype FontSlantA = FontSlantA (Last FontSlant)-  deriving (Typeable, Semigroup, Eq)-instance AttributeClass FontSlantA+instance AttributeClass FontSlant where+instance Semigroup FontSlant where+  _ <> b = b +instance Default FontSlant where+  def = FontSlantNormal+ -- | Extract the font slant from a 'FontSlantA' attribute.-getFontSlant :: FontSlantA -> FontSlant-getFontSlant (FontSlantA (Last s)) = s+getFontSlant :: FontSlant -> FontSlant+getFontSlant = id  -- | Specify the slant (normal, italic, or oblique) that should be --   used for all text within a diagram.  See also 'italic' and --   'oblique' for useful special cases. fontSlant :: HasStyle a => FontSlant -> a -> a-fontSlant = applyAttr . FontSlantA . Last+fontSlant = applyAttr +-- | Lens onto the font slant in a style.+_fontSlant :: Lens' (Style v n) FontSlant+_fontSlant = atAttr . non def+ -- | Set all text in italics. italic :: HasStyle a => a -> a italic = fontSlant FontSlantItalic@@ -222,27 +335,82 @@ -------------------------------------------------- -- Font weight -data FontWeight = FontWeightNormal-                | FontWeightBold-    deriving (Eq)- -- | The @FontWeightA@ attribute specifies the weight (normal or bold) --   that should be used for all text within a diagram.  Inner --   @FontWeightA@ attributes override outer ones.-newtype FontWeightA = FontWeightA (Last FontWeight)-  deriving (Typeable, Semigroup, Eq)-instance AttributeClass FontWeightA+data FontWeight = FontWeightNormal+                | FontWeightBold+                | FontWeightBolder+                | FontWeightLighter+                | FontWeightThin+                | FontWeightUltraLight+                | FontWeightLight+                | FontWeightMedium+                | FontWeightSemiBold+                | FontWeightUltraBold+                | FontWeightHeavy+    deriving (Eq,+              Ord, Show, Typeable) --- | Extract the font weight from a 'FontWeightA' attribute.-getFontWeight :: FontWeightA -> FontWeight-getFontWeight (FontWeightA (Last w)) = w+instance AttributeClass FontWeight --- | Specify the weight (normal or bold) that should be+-- | Last semigroup structure+instance Semigroup FontWeight where+  _ <> b = b++instance Default FontWeight where+  def = FontWeightNormal++-- | Extract the font weight.+getFontWeight :: FontWeight -> FontWeight+getFontWeight = id++-- | Specify the weight (normal, bolder, lighter or bold) that should be --   used for all text within a diagram.  See also 'bold' --   for a useful special case. fontWeight :: HasStyle a => FontWeight -> a -> a-fontWeight = applyAttr . FontWeightA . Last+fontWeight = applyAttr  -- | Set all text using a bold font weight. bold :: HasStyle a => a -> a bold = fontWeight FontWeightBold++-- | Set all text using a thin font weight.+thinWeight :: HasStyle a => a -> a+thinWeight = fontWeight FontWeightThin++-- | Set all text using a extra light font weight.+ultraLight :: HasStyle a => a -> a+ultraLight = fontWeight FontWeightUltraLight++-- | Set all text using a light font weight.+light :: HasStyle a => a -> a+light = fontWeight FontWeightLight++-- | Set all text using a medium font weight.+mediumWeight :: HasStyle a => a -> a+mediumWeight = fontWeight FontWeightMedium++-- | Set all text using a semi-bold font weight.+semiBold :: HasStyle a => a -> a+semiBold = fontWeight FontWeightSemiBold++-- | Set all text using an ultra-bold font weight.+ultraBold :: HasStyle a => a -> a+ultraBold = fontWeight FontWeightUltraBold++-- | Set all text using a heavy/black font weight.+heavy :: HasStyle a => a -> a+heavy = fontWeight FontWeightHeavy++-- | Set all text to be bolder than the inherited font weight.+bolder :: HasStyle a => a -> a+bolder = fontWeight FontWeightBolder++-- | Set all text to be lighter than the inherited font weight.+lighter :: HasStyle a => a -> a+lighter = fontWeight FontWeightLighter++-- | Lens onto the font weight in a style.+_fontWeight :: Lens' (Style v n) FontWeight+_fontWeight = atAttr . non def
src/Diagrams/TwoD/Transform.hs view
@@ -1,12 +1,19 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-} {-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-} {-# LANGUAGE ViewPatterns          #-}++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+  -- for Data.Semigroup+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Transform--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -18,10 +25,11 @@  module Diagrams.TwoD.Transform        (+         T2          -- * Rotation-         rotation, rotate, rotateBy--       , rotationAbout, rotateAbout+       , rotation, rotate, rotateBy, rotated+       , rotationAround, rotateAround+       , rotationTo, rotateTo           -- * Scaling        , scalingX, scaleX@@ -36,323 +44,262 @@        , translationY, translateY        , translation, translate +         -- * Conformal affine maps+       , scalingRotationTo, scaleRotateTo+          -- * Reflection        , reflectionX, reflectX        , reflectionY, reflectY+       , reflectionXY, reflectXY        , reflectionAbout, reflectAbout           -- * Shears        , shearingX, shearX        , shearingY, shearY -         -- * Scale invariance-       , ScaleInv(..), scaleInv, scaleInvPrim--         -- * component-wise-       , onBasis        ) where +import           Diagrams.Angle import           Diagrams.Core-import qualified Diagrams.Core.Transform as T--import           Control.Newtype         (over)--import           Diagrams.Coordinates+import           Diagrams.Core.Transform+import           Diagrams.Direction import           Diagrams.Transform-import           Diagrams.TwoD.Size      (height, width)+import           Diagrams.Transform.Matrix import           Diagrams.TwoD.Types-import           Diagrams.TwoD.Vector    (direction)+import           Diagrams.TwoD.Vector +import           Control.Lens              hiding (at, transform) import           Data.Semigroup -import           Data.AffineSpace--import           Control.Arrow           (first, second)+import           Linear.Affine+import           Linear.Metric+import           Linear.V2+import           Linear.Vector  -- Rotation ------------------------------------------------ --- | Create a transformation which performs a rotation about the local---   origin by the given angle.  See also 'rotate'.-rotation :: Angle a => a -> T2-rotation ang = fromLinear r (linv r)-  where-    r            = rot theta <-> rot (-theta)-    Rad theta    = convertAngle ang-    rot th (coords -> x :& y) = (cos th * x - sin th * y) & (sin th * x + cos th * y)+-- For the definitions of 'rotation' and 'rotate', see Diagrams.Angle. --- | Rotate about the local origin by the given angle. Positive angles---   correspond to counterclockwise rotation, negative to---   clockwise. The angle can be expressed using any type which is an---   instance of 'Angle'.  For example, @rotate (1\/4 ::---   'Turn')@, @rotate (tau\/4 :: 'Rad')@, and @rotate (90 ::---   'Deg')@ all represent the same transformation, namely, a---   counterclockwise rotation by a right angle.  To rotate about some---   point other than the local origin, see 'rotateAbout'.------   Note that writing @rotate (1\/4)@, with no type annotation, will---   yield an error since GHC cannot figure out which sort of angle---   you want to use.  In this common situation you can use---   'rotateBy', which is specialized to take a 'Turn' argument.-rotate :: (Transformable t, V t ~ R2, Angle a) => a -> t -> t-rotate = transform . rotation+-- | A synonym for 'rotate', interpreting its argument in units of+-- turns; it can be more convenient to write @rotateBy (1\/4)@ than+-- @'rotate' (1\/4 \@\@ 'turn')@.+rotateBy :: (InSpace V2 n t, Transformable t, Floating n) => n -> t -> t+rotateBy = transform . rotation . review turn --- | A synonym for 'rotate', specialized to only work with---   @Turn@ arguments; it can be more convenient to write---   @rotateBy (1\/4)@ than @'rotate' (1\/4 :: 'Turn')@.-rotateBy :: (Transformable t, V t ~ R2) => Turn -> t -> t-rotateBy = transform . rotation+-- | Use an 'Angle' to make an 'Iso' between an object+--   rotated and unrotated. This us useful for performing actions+--   'under' a rotation:+--+-- @+-- under (rotated t) f = rotate (negated t) . f . rotate t+-- rotated t ## a      = rotate t a+-- a ^. rotated t      = rotate (-t) a+-- over (rotated t) f  = rotate t . f . rotate (negated t)+-- @+rotated :: (InSpace V2 n a, Floating n, SameSpace a b, Transformable a, Transformable b)+        => Angle n -> Iso a b a b+rotated a = transformed $ rotation a  -- | @rotationAbout p@ is a rotation about the point @p@ (instead of --   around the local origin).-rotationAbout :: Angle a => P2 -> a -> T2-rotationAbout p angle = conjugate (translation (origin .-. p)) (rotation angle)+rotationAround :: Floating n => P2 n -> Angle n -> T2 n+rotationAround p theta =+  conjugate (translation (origin .-. p)) (rotation theta)  -- | @rotateAbout p@ is like 'rotate', except it rotates around the --   point @p@ instead of around the local origin.-rotateAbout :: (Transformable t, V t ~ R2, Angle a) => P2 -> a -> t -> t-rotateAbout p angle = rotate angle `under` translation (origin .-. p)+rotateAround :: (InSpace V2 n t, Transformable t, Floating n)+             => P2 n -> Angle n -> t -> t+rotateAround p theta = transform (rotationAround p theta) +-- | The rotation that aligns the x-axis with the given direction.+rotationTo :: OrderedField n => Direction V2 n -> T2 n+rotationTo (view _Dir -> V2 x y) = rotation (atan2A' y x)++-- | Rotate around the local origin such that the x axis aligns with the+--   given direction.+rotateTo :: (InSpace V2 n t, OrderedField n, Transformable t) => Direction V2 n -> t -> t+rotateTo = transform . rotationTo+ -- Scaling -------------------------------------------------  -- | Construct a transformation which scales by the given factor in --   the x (horizontal) direction.-scalingX :: Double -> T2-scalingX c = fromLinear s s-  where s = (over r2 . first) (*c) <-> (over r2 . first) (/c)+scalingX :: (Additive v, R1 v, Fractional n) => n -> Transformation v n+scalingX c = fromSymmetric $ (_x *~ c) <-> (_x //~ c)  -- | Scale a diagram by the given factor in the x (horizontal) --   direction.  To scale uniformly, use 'scale'.-scaleX :: (Transformable t, V t ~ R2) => Double -> t -> t+scaleX :: (InSpace v n t, R2 v, Fractional n, Transformable t) => n -> t -> t scaleX = transform . scalingX  -- | Construct a transformation which scales by the given factor in --   the y (vertical) direction.-scalingY :: Double -> T2-scalingY c = fromLinear s s-  where s = (over r2 . second) (*c) <-> (over r2 . second) (/c)+scalingY :: (Additive v, R2 v, Fractional n) => n -> Transformation v n+scalingY c = fromSymmetric $ (_y *~ c) <-> (_y //~ c)  -- | Scale a diagram by the given factor in the y (vertical) --   direction.  To scale uniformly, use 'scale'.-scaleY :: (Transformable t, V t ~ R2) => Double -> t -> t+scaleY :: (InSpace v n t, R2 v, Fractional n, Transformable t)+  => n -> t -> t scaleY = transform . scalingY  -- | @scaleToX w@ scales a diagram in the x (horizontal) direction by --   whatever factor required to make its width @w@.  @scaleToX@ --   should not be applied to diagrams with a width of 0, such as --   'vrule'.-scaleToX :: (Enveloped t, Transformable t, V t ~ R2) => Double -> t -> t-scaleToX w d = scaleX (w / width d) d+scaleToX :: (InSpace v n t, R2 v, Enveloped t, Transformable t) => n -> t -> t+scaleToX w d = scaleX (w / diameter unitX d) d  -- | @scaleToY h@ scales a diagram in the y (vertical) direction by --   whatever factor required to make its height @h@.  @scaleToY@ --   should not be applied to diagrams with a height of 0, such as --   'hrule'.-scaleToY :: (Enveloped t, Transformable t, V t ~ R2) => Double -> t -> t-scaleToY h d = scaleY (h / height d) d+scaleToY :: (InSpace v n t, R2 v, Enveloped t, Transformable t) => n -> t -> t+scaleToY h d = scaleY (h / diameter unitY d) d  -- | @scaleUToX w@ scales a diagram /uniformly/ by whatever factor --   required to make its width @w@.  @scaleUToX@ should not be --   applied to diagrams with a width of 0, such as 'vrule'.-scaleUToX :: (Enveloped t, Transformable t, V t ~ R2) => Double -> t -> t-scaleUToX w d = scale (w / width d) d+scaleUToX :: (InSpace v n t, R1 v, Enveloped t, Transformable t) => n -> t -> t+scaleUToX w d = scale (w / diameter unitX d) d  -- | @scaleUToY h@ scales a diagram /uniformly/ by whatever factor --   required to make its height @h@.  @scaleUToY@ should not be applied --   to diagrams with a height of 0, such as 'hrule'.-scaleUToY :: (Enveloped t, Transformable t, V t ~ R2) => Double -> t -> t-scaleUToY h d = scale (h / height d) d+scaleUToY :: (InSpace v n t, R2 v, Enveloped t, Transformable t) => n -> t -> t+scaleUToY h d = scale (h / diameter unitY d) d  -- Translation ---------------------------------------------  -- | Construct a transformation which translates by the given distance --   in the x (horizontal) direction.-translationX :: Double -> T2-translationX x = translation (x & 0)+translationX :: (Additive v, R1 v, Num n) => n -> Transformation v n+translationX x = translation (zero & _x .~ x)  -- | Translate a diagram by the given distance in the x (horizontal) --   direction.-translateX :: (Transformable t, V t ~ R2) => Double -> t -> t+translateX :: (InSpace v n t, R1 v, Transformable t) => n -> t -> t translateX = transform . translationX  -- | Construct a transformation which translates by the given distance --   in the y (vertical) direction.-translationY :: Double -> T2-translationY y = translation (0 & y)+translationY :: (Additive v, R2 v, Num n) => n -> Transformation v n+translationY y = translation (zero & _y .~ y)  -- | Translate a diagram by the given distance in the y (vertical) --   direction.-translateY :: (Transformable t, V t ~ R2) => Double -> t -> t+translateY :: (InSpace v n t, R2 v, Transformable t)+  => n -> t -> t translateY = transform . translationY +-- Conformal affine maps -----------------------------------++-- | The angle-preserving linear map that aligns the x-axis unit vector+--   with the given vector.  See also 'scaleRotateTo'.+scalingRotationTo :: (Floating n) => V2 n -> T2 n+scalingRotationTo v = fromMatWithInv (conf v) (conf w) zero+  where+    w = reflectY (v ^/ quadrance v)+    conf (V2 a b) = (V2 (V2 a (-b)) (V2 b a))++-- | Rotate and uniformly scale around the local origin such that the+--   x-axis aligns with the given vector.  This satisfies the equation+--+-- @+-- scaleRotateTo v = rotateTo (dir v) . scale (norm v)+-- @+--+-- up to floating point rounding errors, but is more accurate and+-- performant since it avoids cancellable uses of trigonometric functions.+scaleRotateTo :: (InSpace V2 n t, Transformable t, Floating n)+              => V2 n -> t -> t+scaleRotateTo = transform . scalingRotationTo+ -- Reflection ----------------------------------------------  -- | Construct a transformation which flips a diagram from left to --   right, i.e. sends the point (x,y) to (-x,y).-reflectionX :: T2-reflectionX = scalingX (-1)+reflectionX :: (Additive v, R1 v, Num n) => Transformation v n+reflectionX = fromSymmetric $ (_x *~ (-1)) <-> (_x *~ (-1))  -- | Flip a diagram from left to right, i.e. send the point (x,y) to --   (-x,y).-reflectX :: (Transformable t, V t ~ R2) => t -> t+reflectX :: (InSpace v n t, R1 v, Transformable t) => t -> t reflectX = transform reflectionX  -- | Construct a transformation which flips a diagram from top to --   bottom, i.e. sends the point (x,y) to (x,-y).-reflectionY :: T2-reflectionY = scalingY (-1)+reflectionY :: (Additive v, R2 v, Num n) => Transformation v n+reflectionY = fromSymmetric $ (_y *~ (-1)) <-> (_y *~ (-1))  -- | Flip a diagram from top to bottom, i.e. send the point (x,y) to --   (x,-y).-reflectY :: (Transformable t, V t ~ R2) => t -> t+reflectY :: (InSpace v n t, R2 v, Transformable t) => t -> t reflectY = transform reflectionY --- | @reflectionAbout p v@ is a reflection in the line determined by---   the point @p@ and vector @v@.-reflectionAbout :: P2 -> R2 -> T2-reflectionAbout p v =-  conjugate (rotation (-direction v :: Rad) <> translation (origin .-. p))+-- | Construct a transformation which flips the diagram about x=y, i.e.+--   sends the point (x,y) to (y,x).+reflectionXY :: (Additive v, R2 v, Num n) => Transformation v n+reflectionXY = fromSymmetric $ (_xy %~ view _yx) <-> (_xy %~ view _yx)++-- | Flips the diagram about x=y, i.e. send the point (x,y) to (y,x).+reflectXY :: (InSpace v n t, R2 v, Transformable t) => t -> t+reflectXY = transform reflectionXY++-- | @reflectionAbout p d@ is a reflection in the line determined by+--   the point @p@ and direction @d@.+reflectionAbout :: OrderedField n => P2 n -> Direction V2 n -> T2 n+reflectionAbout p d =+  conjugate (rotationTo (reflectY d) <> translation (origin .-. p))             reflectionY --- | @reflectAbout p v@ reflects a diagram in the line determined by---   the point @p@ and the vector @v@.-reflectAbout :: (Transformable t, V t ~ R2) => P2 -> R2 -> t -> t++++-- | @reflectAbout p d@ reflects a diagram in the line determined by+--   the point @p@ and direction @d@.+reflectAbout :: (InSpace V2 n t, OrderedField n, Transformable t)+             => P2 n -> Direction V2 n -> t -> t reflectAbout p v = transform (reflectionAbout p v)  -- Shears -------------------------------------------------- +-- auxiliary functions for shearingX/shearingY+sh :: (n -> n -> n -> n) -> (n -> n -> n -> n) -> n -> V2 n -> V2 n+sh f g k (V2 x y) = V2 (f k x y) (g k x y)++sh' :: (n -> n -> n -> n) -> (n -> n -> n -> n) -> n -> V2 n -> V2 n+sh' f g k = swap . sh f g k . swap++swap :: V2 n -> V2 n+swap (V2 x y) = V2 y x+{-# INLINE swap #-}+ -- | @shearingX d@ is the linear transformation which is the identity on --   y coordinates and sends @(0,1)@ to @(d,1)@.-shearingX :: Double -> T2-shearingX d = fromLinear (over r2 (sh d)  <-> over r2 (sh (-d)))-                         (over r2 (sh' d) <-> over r2 (sh' (-d)))-  where sh  k (x, y) = (x+k*y, y)-        sh' k        = swap . sh k . swap-        swap (x,y) = (y,x)+shearingX :: Num n => n -> T2 n+shearingX d = fromLinear (sh f g d  <-> sh f g (-d))+                         (sh' f g d <-> sh' f g (-d))+  where+    f k x y = x + k*y+    g _ _ y = y  -- | @shearX d@ performs a shear in the x-direction which sends --   @(0,1)@ to @(d,1)@.-shearX :: (Transformable t, V t ~ R2) => Double -> t -> t+shearX :: (InSpace V2 n t, Transformable t) => n -> t -> t shearX = transform . shearingX  -- | @shearingY d@ is the linear transformation which is the identity on --   x coordinates and sends @(1,0)@ to @(1,d)@.-shearingY :: Double -> T2-shearingY d = fromLinear (over r2 (sh d)  <-> over r2 (sh (-d)))-                         (over r2 (sh' d) <-> over r2 (sh' (-d)))-  where sh  k (x,y) = (x, y+k*x)-        sh' k       = swap . sh k . swap-        swap (x,y) = (y,x)+shearingY :: Num n => n -> T2 n+shearingY d = fromLinear (sh f g d  <-> sh f g (-d))+                         (sh' f g d <-> sh' f g (-d))+        where+          f _ x _ = x+          g k x y = y + k*x  -- | @shearY d@ performs a shear in the y-direction which sends --   @(1,0)@ to @(1,d)@.-shearY :: (Transformable t, V t ~ R2) => Double -> t -> t+shearY :: (InSpace V2 n t, Transformable t) => n -> t -> t shearY = transform . shearingY----- Scale invariance -------------------------------------------- | The @ScaleInv@ wrapper creates two-dimensional /scale-invariant/---   objects.  Intuitively, a scale-invariant object is affected by---   transformations like translations and rotations, but not by scales.------   However, this is problematic when it comes to /non-uniform/---   scales (/e.g./ @scaleX 2 . scaleY 3@) since they can introduce a---   perceived rotational component.  The prototypical example is an---   arrowhead on the end of a path, which should be scale-invariant.---   However, applying a non-uniform scale to the path but not the---   arrowhead would leave the arrowhead pointing in the wrong---   direction.------   Moreover, for objects whose local origin is not at the local---   origin of the parent diagram, any scale can result in a---   translational component as well.------   The solution is to also store a point (indicating the location,---   /i.e./ the local origin) and a unit vector (indicating the---   /direction/) along with a scale-invariant object.  A---   transformation to be applied is decomposed into rotational and---   translational components as follows:------   * The transformation is applied to the direction vector, and the---   difference in angle between the original direction vector and its---   image under the transformation determines the rotational---   component.  The rotation is applied with respect to the stored---   location, rather than the global origin.------   * The vector from the location to the image of the location under---   the transformation determines the translational component.--data ScaleInv t =-  ScaleInv-  { unScaleInv  :: t-  , scaleInvDir :: R2-  , scaleInvLoc :: P2-  }-  deriving (Show)---- | Create a scale-invariant object pointing in the given direction,---   located at the origin.-scaleInv :: t -> R2 -> ScaleInv t-scaleInv t d = ScaleInv t d origin--type instance V (ScaleInv t) = R2--instance (V t ~ R2, HasOrigin t) => HasOrigin (ScaleInv t) where-  moveOriginTo p (ScaleInv t v l) = ScaleInv (moveOriginTo p t) v (moveOriginTo p l)--instance (V t ~ R2, Transformable t) => Transformable (ScaleInv t) where-  transform tr (ScaleInv t v l) = ScaleInv (trans . rot $ t) (rot v) l'-    where-      angle :: Rad-      angle = direction (transform tr v) - direction v-      rot :: (Transformable t, V t ~ R2) => t -> t-      rot = rotateAbout l angle-      l'  = transform tr l-      trans = translate (l' .-. l)---- This is how we handle freezing properly with ScaleInv wrappers.--- Normal transformations are applied ignoring scaling; "frozen"--- transformations (i.e. transformations applied after a freeze) are--- applied directly to the underlying object, scales and all.  We must--- take care to transform the reference point and direction vector--- appropriately.-instance (V t ~ R2, Transformable t) => IsPrim (ScaleInv t) where-  transformWithFreeze t1 t2 s = ScaleInv t'' d'' origin''-    where-      -- first, apply t2 normally, i.e. ignoring scaling-      s'@(ScaleInv t' _ _)      = transform t2 s--      -- now apply t1 to get the new direction and origin-      (ScaleInv _ d'' origin'') = transform t1 s'--      -- but apply t1 directly to the underlying thing, scales and all.-      t''                       = transform t1 t'--instance (Renderable t b, V t ~ R2) => Renderable (ScaleInv t) b where-  render b = render b . unScaleInv---- | Create a diagram from a single scale-invariant primitive.  The---   vector argument specifies the direction in which the primitive is---   \"pointing\" (for the purpose of keeping it rotated correctly---   under non-uniform scaling).  The primitive is assumed to be---   \"located\" at the origin (for the purpose of translating it---   correctly under scaling).------   Note that the resulting diagram will have an /empty/ envelope,---   trace, and query.  The reason is that the envelope, trace, and---   query cannot be cached---applying a transformation would cause---   the cached envelope, etc. to get \"out of sync\" with the---   scale-invariant object.  The intention, at any rate, is that---   scale-invariant things will be used only as \"decorations\" (/e.g./---   arrowheads) which should not affect the envelope, trace, and---   query.-scaleInvPrim :: (Transformable t, Renderable t b, V t ~ R2, Monoid m)-             => t -> R2 -> QDiagram b R2 m-scaleInvPrim t d = mkQD (Prim $ scaleInv t d) mempty mempty mempty mempty---- | Get the matrix equivalent of the linear transform,---   (as a pair of columns) and the translation vector.  This---   is mostly useful for implementing backends.-onBasis :: Transformation R2 -> ((R2, R2), R2)-onBasis t = ((x, y), v)-  where ((x:y:[]), v) = T.onBasis t
src/Diagrams/TwoD/Types.hs view
@@ -1,13 +1,7 @@-{-# LANGUAGE DeriveDataTypeable         #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE TypeOperators              #-}-{-# LANGUAGE TypeSynonymInstances       #-}-{-# LANGUAGE ViewPatterns               #-}+{-# LANGUAGE TypeFamilies #-}  {-# OPTIONS_GHC -fno-warn-orphans #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Types@@ -21,204 +15,80 @@  module Diagrams.TwoD.Types        ( -- * 2D Euclidean space-         R2, r2, unr2-       , P2, p2, unp2-       , T2--         -- * Angles-       , Angle(..)-       , Turn(..), CircleFrac, Rad(..), Deg(..)-       , fullCircle, convertAngle+         V2 (..), R1 (..), R2 (..)+       , P2, T2+       , r2, unr2, mkR2, r2Iso+       , p2, mkP2, unp2, p2Iso+       , r2PolarIso+       , HasR (..)        ) where -import           Diagrams.Coordinates-import           Diagrams.Core-import           Diagrams.Util           (tau)--import           Control.Newtype--import           Data.Basis-import           Data.NumInstances.Tuple ()-import           Data.VectorSpace--import           Data.Typeable----------------------------------------------------------------- 2D Euclidean space---- | The two-dimensional Euclidean vector space R^2.  This type is---   intentionally abstract.------   * To construct a vector, use 'r2', or '&' (from "Diagrams.Coordinates"):------ @--- r2 (3,4) :: R2--- 3 & 4    :: R2--- @------     Note that "Diagrams.Coordinates" is not re-exported by---     "Diagrams.Prelude" and must be explicitly imported.------   * To construct the vector from the origin to a point @p@, use---     @p 'Data.AffineSpace..-.' 'origin'@.------   * To convert a vector @v@ into the point obtained by following---     @v@ from the origin, use @'origin' 'Data.AffineSpace..+^' v@.------   * To convert a vector back into a pair of components, use 'unv2'---     or 'coords' (from "Diagrams.Coordinates").  These are typically---     used in conjunction with the @ViewPatterns@ extension:------ @--- foo (unr2 -> (x,y)) = ...--- foo (coords -> x :& y) = ...--- @+import           Control.Lens            (Iso', Lens', iso, _1, _2) -newtype R2 = R2 { unR2 :: (Double, Double) }-  deriving (AdditiveGroup, Eq, Ord, Typeable, Num, Fractional)+import           Diagrams.Angle+import           Diagrams.Points -instance Show R2 where-  showsPrec p (R2 (x,y)) = showParen (p >= 7) $-    showCoord x . showString " & " . showCoord y-   where-    showCoord x | x < 0     = showParen True (shows x)-                | otherwise = shows x+import           Diagrams.Core.Transform+import           Diagrams.Core.V+import           Linear.Metric+import           Linear.V2 -instance Read R2 where-  readsPrec d r = readParen (d > app_prec)-                  (\r -> [ (R2 (x,y), r''')-                         | (x,r')    <- readsPrec (amp_prec + 1) r-                         , ("&",r'') <- lex r'-                         , (y,r''')  <- readsPrec (amp_prec + 1) r''-                         ])-                  r-    where-      app_prec = 10-      amp_prec = 7+type P2 = Point V2+type T2 = Transformation V2 -instance Newtype R2 (Double, Double) where-  pack   = R2-  unpack = unR2+type instance V (V2 n) = V2+type instance N (V2 n) = n  -- | Construct a 2D vector from a pair of components.  See also '&'.-r2 :: (Double, Double) -> R2-r2 = pack+r2 :: (n, n) -> V2 n+r2 = uncurry V2  -- | Convert a 2D vector back into a pair of components.  See also 'coords'.-unr2 :: R2 -> (Double, Double)-unr2 = unpack--type instance V R2 = R2--instance VectorSpace R2 where-  type Scalar R2 = Double-  (*^) = over R2 . (*^)--instance HasBasis R2 where-  type Basis R2 = Either () () -- = Basis (Double, Double)-  basisValue = R2 . basisValue-  decompose  = decompose  . unR2-  decompose' = decompose' . unR2--instance InnerSpace R2 where-  (unR2 -> vec1) <.> (unR2 -> vec2) = vec1 <.> vec2--instance Coordinates R2 where-  type FinalCoord R2     = Double-  type PrevDim R2        = Double-  type Decomposition R2  = Double :& Double+unr2 :: V2 n -> (n, n)+unr2 (V2 x y) = (x, y) -  x & y                  = r2 (x,y)-  coords (unR2 -> (x,y)) = x :& y+-- | Curried form of `r2`.+mkR2 :: n -> n -> V2 n+mkR2 = V2 --- | Points in R^2.  This type is intentionally abstract.------   * To construct a point, use 'p2', or '&' (see---     "Diagrams.Coordinates"):------ @--- p2 (3,4)  :: P2--- 3 & 4     :: P2--- @------   * To construct a point from a vector @v@, use @'origin' 'Data.AffineSpace..+^' v@.------   * To convert a point @p@ into the vector from the origin to @p@,---   use @p 'Data.AffineSpace..-.' 'origin'@.------   * To convert a point back into a pair of coordinates, use 'unp2',---     or 'coords' (from "Diagrams.Coordinates").  It's common to use---     these in conjunction with the @ViewPatterns@ extension:------ @--- foo (unp2 -> (x,y)) = ...--- foo (coords -> x :& y) = ...--- @-type P2 = Point R2+r2Iso :: Iso' (V2 n) (n, n)+r2Iso = iso unr2 r2 --- | Construct a 2D point from a pair of coordinates.  See also '&'.-p2 :: (Double, Double) -> P2-p2 = pack . pack+-- | Construct a 2D point from a pair of coordinates.  See also '^&'.+p2 :: (n, n) -> P2 n+p2 = P . uncurry V2  -- | Convert a 2D point back into a pair of coordinates.  See also 'coords'.-unp2 :: P2 -> (Double, Double)-unp2 = unpack . unpack---- | Transformations in R^2.-type T2 = Transformation R2--instance Transformable R2 where-  transform = apply----------------------------------------------------------------- Angles---- | Newtype wrapper used to represent angles as fractions of a---   circle.  For example, 1\/3 turn = tau\/3 radians = 120 degrees.-newtype Turn = Turn { getTurn :: Double }-  deriving (Read, Show, Eq, Ord, Enum, Floating, Fractional, Num, Real, RealFloat, RealFrac)---- | Deprecated synonym for 'Turn', retained for backwards compatibility.-type CircleFrac = Turn---- | Newtype wrapper for representing angles in radians.-newtype Rad = Rad { getRad :: Double }-  deriving (Read, Show, Eq, Ord, Enum, Floating, Fractional, Num, Real, RealFloat, RealFrac)---- | Newtype wrapper for representing angles in degrees.-newtype Deg = Deg { getDeg :: Double }-  deriving (Read, Show, Eq, Ord, Enum, Floating, Fractional, Num, Real, RealFloat, RealFrac)+unp2 :: P2 n -> (n,n)+unp2 (P (V2 x y)) = (x,y) --- | Type class for types that measure angles.-class Num a => Angle a where-  -- | Convert to a turn, /i.e./ a fraction of a circle.-  toTurn   :: a -> Turn+-- | Curried form of `p2`.+mkP2 :: n -> n -> P2 n+mkP2 x y = P (V2 x y) -  -- | Convert from a turn, /i.e./ a fraction of a circle.-  fromTurn :: Turn -> a+p2Iso :: Iso' (Point V2 n) (n, n)+p2Iso = iso unp2 p2 -instance Angle Turn where-  toTurn   = id-  fromTurn = id+instance Transformable (V2 n) where+  transform = apply --- | tau radians = 1 full turn.-instance Angle Rad where-  toTurn   = Turn . (/tau) . getRad-  fromTurn = Rad . (*tau) . getTurn+r2PolarIso :: RealFloat n => Iso' (V2 n) (n, Angle n)+r2PolarIso = iso (\v@(V2 x y) -> (norm v, atan2A y x))+              (\(r,θ)      -> V2 (r * cosA θ) (r * sinA θ))+{-# INLINE r2PolarIso #-} --- | 360 degrees = 1 full turn.-instance Angle Deg where-  toTurn   = Turn . (/360) . getDeg-  fromTurn = Deg . (*360) . getTurn+-- | A space which has magnitude '_r' that can be calculated numerically.+class HasR t where+  _r :: RealFloat n => Lens' (t n) n --- | An angle representing one full turn.-fullTurn :: Angle a => a-fullTurn = fromTurn 1+instance HasR v => HasR (Point v) where+  _r = lensP . _r+  {-# INLINE _r #-} --- | Deprecated synonym for 'fullTurn', retained for backwards compatibility.-fullCircle :: Angle a => a-fullCircle = fullTurn+instance HasR V2 where+  _r = r2PolarIso . _1+  {-# INLINE _r #-} --- | Convert between two angle representations.-convertAngle :: (Angle a, Angle b) => a -> b-convertAngle = fromTurn . toTurn+instance HasTheta V2 where+  _theta = r2PolarIso . _2+  {-# INLINE _theta #-}
src/Diagrams/TwoD/Vector.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies     #-}-{-# LANGUAGE ViewPatterns     #-} ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.TwoD.Vector@@ -14,57 +11,82 @@ module Diagrams.TwoD.Vector        ( -- * Special 2D vectors          unitX, unitY, unit_X, unit_Y+       , xDir, yDir           -- * Converting between vectors and angles-       , direction, fromDirection,  e+       , angleV, angleDir, e, signedAngleBetween, signedAngleBetweenDirs           -- * 2D vector utilities-       , perp, leftTurn+       , perp, leftTurn, cross2+        ) where -import           Data.VectorSpace     ((<.>))-import           Diagrams.Coordinates+import           Control.Lens        (view, (&), (.~), (^.))++import           Diagrams.Angle+import           Diagrams.Direction import           Diagrams.TwoD.Types-import           Diagrams.Util        (( # )) --- | The unit vector in the positive X direction.-unitX :: R2-unitX = 1 & 0+import           Linear.Metric+import           Linear.V2+import           Linear.Vector --- | The unit vector in the positive Y direction.-unitY :: R2-unitY = 0 & 1+-- | The unit vector in the positive X direction.+unitX :: (R1 v, Additive v, Num n) => v n+unitX = zero & _x .~ 1  -- | The unit vector in the negative X direction.-unit_X :: R2-unit_X = (-1) & 0+unit_X :: (R1 v, Additive v, Num n) => v n+unit_X = zero & _x .~ (-1) +-- | The unit vector in the positive Y direction.+unitY :: (R2 v, Additive v, Num n) => v n+unitY = zero & _y .~ 1+ -- | The unit vector in the negative Y direction.-unit_Y :: R2-unit_Y = 0 & (-1)+unit_Y :: (R2 v, Additive v, Num n) => v n+unit_Y = zero & _y .~ (-1) --- | Compute the direction of a vector, measured counterclockwise from---   the positive x-axis as a fraction of a full turn.  The zero---   vector is arbitrarily assigned the direction 0.-direction :: Angle a => R2 -> a-direction (coords -> x :& y) = convertAngle . Rad $ atan2 y x+-- | A 'Direction' pointing in the X direction.+xDir :: (R1 v, Additive v, Num n) => Direction v n+xDir = dir unitX --- | Convert an angle into a unit vector pointing in that direction.-fromDirection :: Angle a => a -> R2-fromDirection a = cos a' & sin a'-  where Rad a' = convertAngle a+-- | A 'Direction' pointing in the Y direction.+yDir :: (R2 v, Additive v, Num n) => Direction v n+yDir = dir unitY --- | A convenient synonym for 'fromDirection'.-e :: Angle a => a -> R2-e = fromDirection+-- | A direction at a specified angle counter-clockwise from the 'xDir'.+angleDir :: Floating n => Angle n -> Direction V2 n+angleDir = dir . angleV --- | @perp v@ is perpendicular to and has the same magnitude as @v@.---   In particular @perp v == rotateBy (1/4) v@.-perp :: R2 -> R2-perp (coords -> x :& y) = (-y) & x+-- | A unit vector at a specified angle counter-clockwise from the+--   positive x-axis+angleV :: Floating n => Angle n -> V2 n+angleV = angle . view rad +-- | A unit vector at a specified angle counter-clockwise from the+--   positive X axis.+e :: Floating n => Angle n -> V2 n+e = angleV+ -- | @leftTurn v1 v2@ tests whether the direction of @v2@ is a left --   turn from @v1@ (that is, if the direction of @v2@ can be obtained --   from that of @v1@ by adding an angle 0 <= theta <= tau/2).-leftTurn :: R2 -> R2 -> Bool-leftTurn v1 v2 = (v1 <.> perp v2) < 0+leftTurn :: (Num n, Ord n) => V2 n -> V2 n -> Bool+leftTurn v1 v2 = (v1 `dot` perp v2) < 0++-- | Cross product on vectors in R2.+cross2 :: Num n => V2 n -> V2 n -> n+cross2 (V2 x1 y1) (V2 x2 y2) = x1 * y2 - y1 * x2++-- | Signed angle between two vectors. Currently defined as+--+-- @+-- signedAngleBetween u v = (u ^. _theta) ^-^ (v ^. _theta)+-- @+signedAngleBetween :: RealFloat n => V2 n -> V2 n -> Angle n+signedAngleBetween u v = (u ^. _theta) ^-^ (v ^. _theta)++-- | Same as 'signedAngleBetween' but for 'Directions's.+signedAngleBetweenDirs :: RealFloat n => Direction V2 n -> Direction V2 n -> Angle n+signedAngleBetweenDirs u v = (u ^. _theta) ^-^ (v ^. _theta)
src/Diagrams/Util.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE CPP #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Diagrams.Util--- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)+-- Copyright   :  (c) 2011-2015 diagrams-lib team (see LICENSE) -- License     :  BSD-style (see LICENSE) -- Maintainer  :  diagrams-discuss@googlegroups.com --@@ -10,24 +12,44 @@ -----------------------------------------------------------------------------  module Diagrams.Util-       ( -- * Utilities for users+  ( -- * Utilities for users -         with-       , applyAll-       , (#)+    with+  , applyAll+  , (#)+  , (##) -       , iterateN+  , iterateN -       , tau+  , tau -         -- * Internal utilities-       , Proxy(..)-       , foldB+    -- * Finding files+  , findHsFile -       ) where+    -- * Finding sandboxes+  , findSandbox+  , globalPackage -import           Data.Default.Class+    -- * Internal utilities+  , foldB++  ) where++import           Control.Applicative+import           Control.Lens              hiding (( # ))+import           Control.Monad+import           Control.Monad.Catch+import           Control.Monad.Trans+import           Control.Monad.Trans.Maybe+import           Data.Default+import           Data.List+import           Data.Maybe import           Data.Monoid+import           System.Directory+import           System.Environment+import           System.FilePath+import           System.FilePath.Lens+import           System.Process  -- | Several functions exported by the diagrams library take a number --   of arguments giving the user control to \"tweak\" various aspects@@ -68,6 +90,13 @@ (#) :: a -> (a -> b) -> b (#) = flip ($) +-- | A replacement for lenses' 'Control.Lens.Review.#' operator.+(##) :: AReview t b -> b -> t+(##) = review+{-# INLINE (##) #-}+infixr 8 ##++ -- | @iterateN n f x@ returns the list of the first @n@ iterates of --   @f@ starting at @x@, that is, the list @[x, f x, f (f x), ...]@ --   of length @n@. (Note that the last element of the list will be@@ -87,10 +116,6 @@ tau :: Floating a => a tau = 2*pi --- | A value of @Proxy a@ carries no information; it's used only to---   fix the type @a@.-data Proxy a = Proxy- -- | Given an associative binary operation and a default value to use --   in the case of an empty list, perform a /balanced/ fold over a --   list.  For example,@@ -107,3 +132,127 @@         go []         = []         go [x]        = [x]         go (x1:x2:xs) = f x1 x2 : go xs++------------------------------------------------------------------------+-- Files+------------------------------------------------------------------------++-- | Given some file (no extension or otherwise) try to find a haskell+--   source file.+findHsFile :: FilePath -> IO (Maybe FilePath)+findHsFile file = runMaybeT $ hs <|> lhs+  where+    hs      = check (addExtension file "hs")+    lhs     = check (addExtension file "lhs")+    check f = do+      lift (doesFileExist f) >>= guard+      pure f++------------------------------------------------------------------------+-- Sandbox+------------------------------------------------------------------------++-- | Parse cabal config file to find the location of the package+--   database.+parseConfig :: FilePath -> MaybeT IO FilePath+parseConfig file = do+  config <- maybeIO $ readFile file+  hoistMaybe $ config ^? lined . prefixed "package-db: "++-- | Seach the given directory and all parent directories until a cabal+--   config file is found. First search for \"cabal.config\", then+--   \"cabal.sandbox.config\". Return the location of the package+--   database in the config file.+configSearch :: FilePath -> MaybeT IO FilePath+configSearch p0 = do+  p0' <- maybeIO $ canonicalizePath p0++  let mkPaths p+        | all isPathSeparator p || p == "."+                    = []+        | otherwise = (p </> "cabal.sandbox.config")+                    : mkPaths (p ^. directory)++  foldMaybeT parseConfig (mkPaths p0')++-- | Check if the folder is a database, or if it contains a database.+--   Returns the database location if it's found.+isDB :: FilePath -> MaybeT IO FilePath+isDB path =+  if isConf path+    then return path+    else maybeIO (getDirectoryContents path) >>= hoistMaybe . find isConf+    where+      isConf = isSuffixOf ".conf.d"++-- | Search for a sandbox in the following order:+--+--   * Test given FilePaths if they point directly to a database or+--     contain a cabal config file (or any parent directory containing a+--     config file).+--+--   * Same test for @DIAGRAMS_SANDBOX@ environment value+--+--   * Environment values of @GHC_PACKAGE_PATH@, @HSENV@ and+--     @PACKAGE_DB_FOR_GHC@ that point to a database.+--+--   * Test for config file (cabal.sandbox.config) in the current+--     directory and its parents.+--+findSandbox :: [FilePath] -> IO (Maybe FilePath)+findSandbox paths = runMaybeT $ pathsTest <|> diaSB <|> envDB <|> wdConfig+  where+    -- first path in environment+    lookEnv = MaybeT . (fmap . fmap) (head . splitSearchPath) . lookupEnv+    envDB   = foldMaybeT lookEnv ["GHC_PACKAGE_PATH", "HSENV", "PACKAGE_DB_FOR_GHC"]++    -- test if path points directly to db or contains a config file+    test x    = isDB x <|> configSearch x+    pathsTest = foldMaybeT test paths+    diaSB     = lookEnv "DIAGRAMS_SANDBOX" >>= test+    wdConfig  = maybeIO getCurrentDirectory >>= configSearch++-- -- | Use the given path for the sandbox in the @GHC_PACKAGE_PATH@+-- --   environment (appending the ghc global package database from @ghc+-- --   --info@. @GHC_PACKAGE_PATH@ if the variable ghc and other tools use+-- --   to find the package database. (This is what @cabal exec@ sets)+-- ghcPackagePath :: FilePath -> IO ()+-- ghcPackagePath db = do+--   gdb <- globalPackage+--   let dbs = intercalate [searchPathSeparator] [db,gdb]+--   setEnv "GHC_PACKAGE_PATH" dbs+-- -- setEnv is only in base > 4.7, either need to use setenv package or+-- -- -package-db flag++-- | Find ghc's global package database. Throws an error if it isn't+--   found.+globalPackage :: IO FilePath+globalPackage = do+  info <- read <$> readProcess "ghc" ["--info"] ""+  return $ fromMaybe (error "Unable to parse ghc --info.")+                     (lookup "Global Package DB" info)++-- MaybeT utilities++-- | Lift an 'IO' action. If any exceptions are raised, return Nothing.+maybeIO :: (MonadCatch m, MonadIO m) => IO a -> MaybeT m a+maybeIO io = liftIO io `catchAll` const mzero++-- hoistMaybe is exported from transformers as of version 0.6+#if MIN_VERSION_transformers(0,6,0)+#else+-- | Lift a maybe value to a MaybeT of any monad.+hoistMaybe :: Monad m => Maybe a -> MaybeT m a+hoistMaybe = MaybeT . return+#endif++-- | Fold a list of 'MaybeT's that short-circuits as soon as a Just value+--   is found (instead going through the whole list).+foldMaybeT :: Monad m => (a -> MaybeT m b) -> [a] -> MaybeT m b+foldMaybeT _ []     = mzero+foldMaybeT f (a:as) = MaybeT $ do+  x <- runMaybeT (f a)+  if isJust x+    then return x+    else runMaybeT (foldMaybeT f as)+
+ src/Linear/Vector/Compat.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE CPP #-}++-- |+-- Module      :  Linear.Vector.Compat+-- Copyright   :  (c) 2024 diagrams-lib team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Compatibility layer for working with versions of linear both before+-- (< 1.23) and after (>= 1.23) the interpolation direction of `lerp`+-- was reversed.+module Linear.Vector.Compat (lerp) where++import qualified Linear.Vector as V++-- | Linearly interpolate between two vectors, such that @lerp 0 x y =+--   x@ and @lerp 1 x y = 1@.+lerp :: (V.Additive f, Num a) => a -> f a -> f a -> f a+lerp =+#if MIN_VERSION_linear(1,23,0)+  V.lerp+#else+  V.lerp . (1-)+#endif
+ test/Diagrams/Test/Angle.hs view
@@ -0,0 +1,29 @@+-- |++module Diagrams.Test.Angle where+++import           Test.Tasty+import           Test.Tasty.QuickCheck+import           Diagrams.Prelude+import Instances++tests :: TestTree+tests = testGroup "Angle" [+         testProperty "2π radians per turn" $+           \θ -> θ^.rad =~ θ^.turn*2*(pi :: Double)+         , testProperty "360 degrees per turn" $+           \θ -> θ^.deg =~ θ^.turn*(360 :: Double)+         , testProperty "Angle vector addition is commutative" $+           \θ φ -> (θ :: Angle Double) ^+^ φ =~ φ ^+^ θ+         , testProperty "Angle subtraction is the inverse of addition" $+           \θ φ -> (θ :: Angle Double) ^+^ φ ^-^ φ =~ θ+         , testProperty "Angle vector negation squared is identity" $+           \θ -> negated (negated (θ :: Angle Double)) =~ θ+         , testProperty "A negated angle is the additive inverse of the original" $+           \θ -> (θ :: Angle Double) ^+^ (negated θ) =~ 0@@turn+         , testProperty "A negated angle is the additive inverse of the original" $+           \θ -> (θ :: Angle Double) ^+^ (negated θ) =~ 0@@turn+++      ]
+ test/Diagrams/Test/Direction.hs view
@@ -0,0 +1,34 @@+-- |++module Diagrams.Test.Direction where+++import           Diagrams.Direction+import           Diagrams.Prelude+import           Instances+import           Test.Tasty+import           Test.Tasty.QuickCheck++tests :: TestTree+tests = testGroup "Direction" [+         testProperty "Length does not effect from direction" $+           \(Positive f) (NonZero v) -> fromDirection(dir ((v  :: V2 Double) ^* (f+0.001))) =~ fromDirection(dir v)+         , testProperty "HasTheta subtraction yeilds same result as anglebetween" $+            (anglebetsub)+         , testProperty "anglebetweenDirs is commutative" $+           \a b -> angleBetweenDirs (a :: Direction V2 Double) b =~ angleBetweenDirs b a+         , testProperty "fromdirection does not effect angleBetweenDirs" $+           \a b -> angleBetween (fromDirection (a  :: Direction V2 Double)) (fromDirection b) =~ angleBetweenDirs a b++++      ]++if' :: Bool -> a -> a -> a+if' True  x _ = x+if' False _ y = y++anglebetsub :: Direction V2 Double -> Direction V2 Double -> Bool+anglebetsub a b = (if' (abs (a ^.  _theta^.rad  - b ^. _theta^.rad) < pi)+                       (abs ((a ^.  _theta  ^-^ b ^. _theta)^.rad))+                       (2*pi - abs (a ^.  _theta^.rad  - b ^. _theta^.rad) ) =~ angleBetweenDirs a b ^.rad)
+ test/Diagrams/Test/Trail.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies     #-}++module Diagrams.Test.Trail where++import           Diagrams.Prelude+import           Instances+import           Test.Tasty+import           Test.Tasty.QuickCheck++import           Data.Fixed+import           Data.List++tests :: TestTree+tests = testGroup "Trail"+  [ let wrap :: Trail' Line V2 Double -> Located (Trail V2 Double)+        wrap = (`at` origin) . wrapLine+    in+    testProperty "unfixTrail . fixTrail == id for lines" $+    \l -> (unfixTrail . fixTrail $ wrap l) =~ (wrap l)++  , testProperty "glueLine . cutLoop == id" $+    \loop -> (glueLine . cutLoop $ loop) =~ (loop :: Trail' Loop V2 Double)++  , testProperty "trailOffset == sumV . trailOffsets" $+    \t -> trailOffset t =~ (sumV . trailOffsets $ (t :: Trail V2 Double))++  , testProperty "reverseTrail . reverseTrail == id" $+    \t -> (reverseTrail . reverseTrail $ t) =~ (t :: Trail V2 Double)++  , testProperty "reverseLocTrail . reverseLocTrail == id" $+    \t -> (reverseLocTrail . reverseLocTrail $ t) =~+          (t :: Located (Trail V2 Double))++  , testProperty "reverseLine . reverseLine == id" $+    \t -> (reverseLine . reverseLine $ t) =~+          (t :: Trail' Line V2 Double)++  , testProperty "reverseLocLine . reverseLocLine == id" $+    \t -> (reverseLocLine . reverseLocLine $ t) =~+          (t :: Located (Trail' Line V2 Double))++  , testProperty "reverseLoop . reverseLoop == id" $+    \t -> (reverseLoop . reverseLoop $ t) =~+          (t :: Trail' Loop V2 Double)++  , testProperty "reverseLocLoop . reverseLocLoop == id" $+    \t -> (reverseLocLoop . reverseLocLoop $ t) =~+          (t :: Located (Trail' Loop V2 Double))++  , testProperty "section on Trail' Line endpoints match paramaters" $+    \t (Param a) (Param b) ->+      let s = section (t :: Located (Trail' Line V2 Double)) a b+      in  t `atParam` a =~ s `atParam` 0 &&+          t `atParam` b =~ s `atParam` 1++  , testProperty "section on Trail' Line where a paramater is 0 or 1" $+    \t (Param a) ->+      let l = section (t :: Located (Trail' Line V2 Double)) 0 a+          r = section (t :: Located (Trail' Line V2 Double)) a 1+      in  t `atParam` 0 =~ l `atParam` 0 &&+          t `atParam` a =~ l `atParam` 1 &&+          t `atParam` a =~ r `atParam` 0 &&+          t `atParam` 1 =~ r `atParam` 1++  , testProperty "section on Trail' Line where a segment paramater is 0 or 1" $+    \t (Param a) i ->+      let st = unLoc t # \(Line st') -> st' :: SegTree V2 Double+          b | (numSegs st :: Word) > 0 = (fromIntegral (i `mod` (numSegs st + 1) :: Word)) / numSegs st+            | otherwise                = 0+          s = section (t :: Located (Trail' Line V2 Double)) a b+      in  t `atParam` a =~ s `atParam` 0 &&+          t `atParam` b =~ s `atParam` 1++  , testProperty "section on Trail' Line matches section on FixedSegment" $+    \t (Param a) (Param b) -> sectionTrailSectionFixedSegment t a b++  ]++data Param = Param Double deriving Show++instance Arbitrary Param where+  arbitrary = Param <$> choose (-0.5, 1.5)++sectionTrailSectionFixedSegment :: Located (Trail' Line V2 Double) -> Double -> Double -> Bool+sectionTrailSectionFixedSegment t k1 k2+  | null segs = t == t'+  | otherwise = aSecT =~ aSecFS && bSecT =~ bSecFS+  where+    a = min k1 k2+    b = max k1 k2+    t' = section t a b++    segs  = fixTrail $ mapLoc wrapLine t+    segs' = fixTrail $ mapLoc wrapLine t'++    aSecT = head segs'+    bSecT = last segs'++    (aSegIx, a') = splitParam a+    (bSegIx, b') = splitParam b++    aSecFS = section (segs !! floor aSegIx) a' x+      where x = if aSegIx == bSegIx then b' else 1+    bSecFS = section (segs !! floor bSegIx) x  b'+      where x = if aSegIx == bSegIx then a' else 0++    splitParam p | p <  0    = (0    , p           * n)+                 | p >= 1    = (n - 1, 1 + (p - 1) * n)+                 | otherwise = propFrac $  p       * n+      where+        propFrac x = let m = x `mod'` 1 in (x - m, m)+        n = genericLength segs
+ test/Diagrams/Test/Transform.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE TypeFamilies          #-}+++module Diagrams.Test.Transform where++import           Test.Tasty+import           Test.Tasty.QuickCheck+import           Diagrams.Prelude+import           Diagrams.Direction+import Instances++tests :: TestTree+tests = testGroup "Transform" [++        testProperty "rotating a vector by a number then its additive inverse will yield the original vector" $+          \θ a -> rotate ((θ * (-1)) @@ deg) (rotate ((θ :: Double) @@ deg) (a ::  V2 Double)) =~ a+        , testProperty "under rotated allows scaling along an angle" $+          \θ f a -> under (rotated ((θ :: Double) @@ deg)) (scaleX (f :: Double)) (a ::  V2 Double) =~ (rotate (negated (θ @@ deg)) . (scaleX f) . rotate (θ @@ deg)) a+        , testProperty "a rotation of 0 does nothing" $+          \a -> rotate (0 @@ deg) (a ::  V2 Double) =~ a+        , testProperty "adding 360 degrees to a turn does nothing" $+          \c a -> rotate (((c :: Double) + 360) @@ deg) (a ::  V2 Double) =~ rotate (c @@ deg) a+        , testProperty "over rotated allows scaling along x of a rotated shape" $+          \θ f a -> over (rotated ((θ :: Double) @@ deg)) (scaleX (f :: Double)) (a ::  V2 Double) =~ (rotate (θ @@ deg) . (scaleX f) . rotate (negated (θ @@ deg))) a+        , testProperty "scaleX" $+          \f a b -> (scaleX (f :: Double)) (V2 (a ::  Double) b) =~ V2 (a * f) b+        , testProperty "scaleY" $+          \f a b -> (scaleY (f :: Double)) (V2 (a ::  Double) b) =~ V2 a  (f * b)+        , testProperty "reflectX" $+          \a b -> reflectX (V2 (a ::  Double) b) =~ V2 (a * (-1))  b+        , testProperty "reflectY" $+          \a b -> reflectY (V2 (a ::  Double) b) =~ V2 a  ((-1) * b)+        , testProperty "reflectXY" $+          \a b -> reflectXY (V2 (a ::  Double) b) =~ V2 b a+        , testProperty "translate" $+          \a b c d -> translateX (a :: Double) (translateY b (P (V2 c d ))) =~ P (V2 (a + c) (b + d))+        , testProperty "shear" $+          \a b c d -> shearX (a :: Double) (shearY b (V2 c d)) =~ V2 ((c*b + d) * a + c) (c*b + d)+        , testProperty "(1,0) rotateTo some dir will return normalised dir" $+          \(NonZero a) b -> rotateTo  (dir (V2 (a :: Double) b)) (V2 1 0) =~ signorm (V2 a b)+        , testProperty "rotates" $+          \a c -> rotate ((a :: Double)@@ deg) (c :: V2 Double)   =~ rotate'' ((a :: Double)@@ deg) (c :: V2 Double) && rotate ((a :: Double)@@ deg) (c :: V2 Double)   =~ rotate' ((a :: Double)@@ deg) (c :: V2 Double)+        , testProperty "reflectAbout works for a vector" $+          \a b c d e f -> reflectAbout (P (V2 (a :: Double) b)) (dir (V2 c d)) (V2 e f) =~  over (rotated (atan2A' d c)) reflectY (V2 e f)+        , testProperty "reflectAbout works for a point" $+          \a b c d e f -> reflectAbout (P (V2 (a :: Double) b)) (dir (V2 c d)) (P (V2 e f)) =~ translate (V2 a b)  ((over (rotated (atan2A' d c)) reflectY) ((translate (V2 (-a) (-b)) )  (P (V2 e f))))+++        ]++--the original " '' " and a secondary " ' " rotate function for testing++rotation'' :: Floating n => Angle n -> T2 n+rotation'' theta = fromLinear r (linv r)+          where+            r               = rot theta <-> rot (negated theta)+            rot th (V2 x y) = V2 (cosA th * x - sinA th * y)+                                 (sinA th * x + cosA th * y)++rotate'' :: (InSpace V2 n t, Transformable t, Floating n) => Angle n -> t -> t+rotate'' = transform . rotation''++rotation' :: Floating n => Angle n -> T2 n+rotation' theta = fromLinear r (linv r)+            where+            r               = rot theta <-> rot (negated theta)+            rot th (V2 x y) = V2 (c * x - s * y)+                                 (s * x + c * y)+                                  where+                                    c = cosA th+                                    s = sinA th++rotate' :: (InSpace V2 n t, Transformable t, Floating n) => Angle n -> t -> t+rotate'  = transform . rotation'
+ test/Diagrams/Test/Transform/Matrix.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE ScopedTypeVariables #-}++-- |++module Diagrams.Test.Transform.Matrix where+++import           Test.Tasty+import           Test.Tasty.QuickCheck+import           Diagrams.Transform.Matrix+import           Diagrams.Prelude+import           Data.Distributive (distribute)++import Instances++tests :: TestTree+tests = testGroup "Transform.Matrix"+  [+    testProperty "mkMat column vectors (2D)" $+      \(Blind (t :: T2 Double)) -> distribute (mkMat t) =~ V2 (transform t unitX) (transform t unitY)+  , testProperty "mkMat / fromMat22" $+      \(m :: V2 (V2 Double)) -> mkMat (fromMat22 m zero) =~ m++  , testProperty "mkMat / fromMat33" $+      \(m :: V3 (V3 Double)) -> mkMat (fromMat33 m zero) =~ m+  ]
+ test/Diagrams/Test/TwoD.hs view
@@ -0,0 +1,65 @@+-- |++module Diagrams.Test.TwoD where++import           Diagrams.Prelude+import qualified Diagrams.Query as Query (sample)+import           Diagrams.Trail        (linePoints)+import           Instances+import           Test.Tasty+import           Test.Tasty.QuickCheck as QC++newtype SmallAngle = SmallAngle (Angle Double)+  deriving (Eq, Ord, Show)++-- Generate random angles within a reasonably small range (+/- 5+-- turns).+instance Arbitrary SmallAngle where+  arbitrary = SmallAngle . (@@turn) <$> choose (-5, 5)++tests :: TestTree+tests = testGroup "TwoD"+    [ testGroup "TwoD.Arc" [+           testProperty "arc start point is at radius 1 in the starting direction" $ \d (SmallAngle a) ->+               pathVertices (arc d a :: Path V2 Double) ^? _head . _head =~ Just (origin .+^ fromDirection d )+           , testProperty "arc end point is at radius 1 in the ending direction" $ \d (SmallAngle a) ->+               pathVertices (arc d a :: Path V2 Double) ^? _head . _last =~ Just (origin .+^ fromDirection (rotate a d))+         ]+    , testGroup "TwoD.Types" [+         testProperty "R2 vector addition is commutative" $+           \u v -> (u :: V2 Double) ^+^ v =~ v ^+^ u+         , testProperty "R2 subtraction is the inverse of addition" $+           \u v -> u ^+^ v ^-^ v =~ (u :: V2 Double)+         , testProperty "R2 vector negation squared is identity" $+           \u -> negated (negated (u :: V2 Double)) =~ u+         ]+      , testGroup "cubicSpline" [+         testProperty "Open cubic spline interpolates all points" $+         \pts -> length pts > 1 ==> and (zipWith (=~) pts (cubicSpline False pts :: [P2 Double]))+         , testProperty "Closed cubic spline interpolates all points" $+           \pts -> length pts > 1 ==> and (zipWith (=~) pts (cubicSpline True pts :: [P2 Double]))+    ]+      , testGroup "Trail" [+         testProperty "glueLine . cutLoop === id" $+         \l -> glueLine (cutLoop l :: Trail' Line V2 Double) =~ l+         , testProperty "cutLoop ends at starting point" $+           \l -> let ps = linePoints (cutLoop (l :: Trail' Loop V2 Double) `at` origin) in (ps ^? _head) =~ (ps ^? _last)+         , testProperty "cutTrail makes a Line" $+           \t -> isLine (cutTrail (t :: Trail V2 Double))+          , testProperty "fromSegments . lineSegments === id" $+            \l -> fromSegments (lineSegments l) =~ (l :: Trail' Line V2 Double)+          , testProperty "lineSegments . fromSegments === id" $+            \segs -> lineSegments (fromSegments segs) =~ (segs :: [Segment Closed V2 Double])+          ]+    , testGroup "Queries and Backgrounds"+        (let dia :: QDiagram NullBackend V2 Double [Int]+             dia = circle 5 # scaleX 2 # rotateBy (1/14) # value [1]+                   <>+                   circle 2 # scaleX 5 # rotateBy (-4/14) # value [2]+         in [+           testProperty "sample dia pt === sample (dia # bg color) pt" $+           \pt -> Query.sample dia pt QC.=== Query.sample (dia # bg orange) pt+         , testProperty "sample dia pt === sample (dia # bgFrame 0.1 color) pt" $+           \pt -> Query.sample dia pt QC.=== Query.sample (dia # bgFrame 0.1 green) pt+         ])+    ]
+ test/Diagrams/Test/TwoD/Offset.hs view
@@ -0,0 +1,48 @@+module Diagrams.Test.TwoD.Offset+    (+      tests+    ) where++import Test.Tasty (TestTree)+import Test.Tasty.HUnit++import Diagrams.Prelude+import Diagrams.TwoD.Offset++tests :: [TestTree]+tests =+    [ testCase "line"+          (offsetTrailVertices+              [p2 (0, 0), p2 (1, 0)]+              [p2 (0, -1), p2 (1, -1)])+    , testCase "square"+          (offsetTrailVertices+              [p2 (0, 0), p2 (1, 0), p2 (1, 1), p2 (0, 1), p2 (0, 0)]+              [p2 (0, -1), p2 (2, -1), p2 (2, 2), p2 (-1, 2), p2 (-1, 0)])+    , testCase "square loop"+          (offsetTrailLoopVertices+              [p2 (0, 0), p2 (1, 0), p2 (1, 1), p2 (0, 1), p2 (0, 0)]+              [p2 (2, -1), p2 (2, 2), p2 (-1, 2), p2 (-1, -1)])+    , testCase "redundant line"+          (offsetTrailVertices+              [p2 (0, 0), p2 (0.5, 0), p2 (1, 0)]+              [p2 (0, -1), p2 (1, -1)])+    , testCase "redundant square"+          (offsetTrailVertices+              [p2 (0, 0), p2 (1, 0), p2 (1, 0.5), p2 (1, 1), p2 (0, 1), p2 (0, 0)]+              [p2 (0, -1), p2 (2, -1), p2 (2, 2), p2 (-1, 2), p2 (-1, 0)])+    , testCase "redundant square loop"+          (offsetTrailLoopVertices+              [p2 (0, 0), p2 (1, 0), p2 (1, 0.5), p2 (1, 1), p2 (0, 1), p2 (0, 0)]+              [p2 (2, -1), p2 (2, 2), p2 (-1, 2), p2 (-1, -1)])+    ]++offsetTrailVertices :: [Point V2 Double] -> [Point V2 Double] -> Assertion+offsetTrailVertices orig off =+    (trailVertices . offsetTrail 1 . fromVertices $ orig) @?= off++offsetTrailLoopVertices :: [Point V2 Double] -> [Point V2 Double] -> Assertion+offsetTrailLoopVertices orig off =+    (trailVertices . offsetTrail 1 . loopTrailFromVertices $ orig) @?= off+  where+    loopTrailFromVertices = (`at` origin) . wrapTrail . glueLine . lineFromVertices
+ test/Diagrams/Test/TwoD/Segment.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module Diagrams.Test.TwoD.Segment+    (+      tests+    ) where++import qualified Test.QuickCheck.Property as Q+import           Test.Tasty               (TestTree)+import           Test.Tasty.QuickCheck++import           Diagrams.Prelude+import           Diagrams.TwoD.Segment++newtype InBox = InBox { unInBox :: Double }++instance Arbitrary InBox where+  arbitrary = InBox <$> choose (-1, 1)++instance Arbitrary (Point V2 Double) where+  arbitrary = curry p2 <$> (unInBox <$> arbitrary)+                       <*> (unInBox <$> arbitrary)++instance Arbitrary (FixedSegment V2 Double) where+   arbitrary = oneof [FLinear <$> arbitrary <*> arbitrary, FCubic <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary]++epsT, epsE :: Double+epsT = 1.0e-9 -- parameter space epsilon+epsE = 1.0e-8 -- Euclidean space epsilon++(.=~.) :: P2 Double -> P2 Double -> Bool+x .=~. y = norm (x .-. y) < epsE++tests :: [TestTree]+tests =+    [ testProperty "segmentSegment" $+        \a b -> validateIntersections a b (segmentSegment epsT a b)+    ]++validateIntersections :: FixedSegment V2 Double -> FixedSegment V2 Double -> [(Double, Double, P2 Double)] -> Q.Result+validateIntersections _ _ [] = Q.rejected -- TODO: check for false negatives (rasterize both and look for overlap?)+validateIntersections a b isects = go isects+  where+    go [] = Q.succeeded+    go ((ta,tb,p):is)+      | and [ 0 <= ta && ta <= 1+            , 0 <= tb && tb <= 1+            , a `atParam` ta .=~. p+            , b `atParam` tb .=~. p+            ] = go is+      | otherwise = Q.failed
+ test/Instances.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE GADTs                #-}+{-# LANGUAGE ScopedTypeVariables  #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ViewPatterns         #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++-- | instances for QuickCheck Arbitrary and approximate equality++module Instances where++import           Diagrams.Prelude+import           Numeric.Extras+import           Test.Tasty.QuickCheck (Arbitrary (..), Gen)+import qualified Test.Tasty.QuickCheck as QC++------------------------------------------------------------+    -- Approximate Comparison for Doubles, Points++epsilon :: Double+epsilon = 0.001++class Approx a where+  (=~) :: a -> a -> Bool++infix 4 =~++--instance (Fractional a, Ord a) => Approx a where+instance Approx Double where+  (=~) a b = abs (a - b) < epsilon++instance Approx Float where+    (=~) a b = abs (a - b) < 0.001++instance Approx n => Approx (V2 n) where+    z1 =~ z2 = (z1^._x) =~ (z2^._x) && (z1^._y) =~ (z2^._y)++instance Approx n => Approx (V3 n) where+    z1 =~ z2 = (z1^._x) =~ (z2^._x) && (z1^._y) =~ (z2^._y) && (z1^._z) =~ (z2^._z)++instance Approx (v n) => Approx (Point v n) where+    p =~ q = view _Point p =~ view _Point q++instance (Approx n, RealExtras n) => Approx (Angle n) where+    a =~ b = normA (a^.rad) =~ normA (b^.rad) where+      normA ang = let ang' = ang `fmod` pi in if ang' >= 0 then ang' else ang'+pi++instance Approx n => Approx (Offset Closed V2 n) where+    OffsetClosed v0 =~ OffsetClosed v1 = v0 =~ v1++instance Approx n => Approx (Segment Closed V2 n) where+    Linear o0 =~ Linear o1 = o0 =~ o1+    Cubic c0 d0 o0 =~ Cubic c1 d1 o1 = c0 =~ c1 && d0 =~ d1 && o0 =~ o1+    _ =~ _ = False+    -- The above is conservative:+    -- Cubic never equals Linear even if they describe the same points++instance Approx n => Approx (FixedSegment V2 n) where+    FLinear a0 b0 =~ FLinear a1 b1 = a0 =~ a1 && b0 =~ b1+    FCubic a0 b0 c0 d0 =~ FCubic a1 b1 c1 d1 = a0 =~ a1 && b0 =~ b1 && c0 =~ c1 && d0 =~ d1+    _ =~ _ = False++instance Approx n => Approx (Trail' Line V2 n) where+    l0 =~ l1 = and $ zipWith (=~) (lineSegments l0) (lineSegments l1)++instance Approx n => Approx (Trail' Loop V2 n) where+    l0 =~ l1 = fst (loopSegments l0) =~ fst (loopSegments l1)++instance (Approx n, Floating n, Ord n) => Approx (Trail V2 n) where+    t0 =~ t1 = and $ zipWith (=~) (trailSegments t0) (trailSegments t1)++instance (Approx a, Approx (Vn a), Num (N a), Additive (V a)) =>+         Approx (Located a) where+           a0 =~ a1 = (loc a0 .-. origin) =~ (loc a1 .-. origin) && unLoc a0 =~ unLoc a1++instance Approx a => Approx (Maybe a) where+    Nothing =~ Nothing = True+    Nothing =~ Just _ = False+    Just _ =~ Nothing = False+    Just l =~ Just r = l =~ r++-- These may be too general+instance Approx a => Approx [a] where+    a =~ b = and $ zipWith (=~) a b++instance (Approx a, Approx b) => Approx (a, b) where+    (a0, b0) =~ (a1,b1) = (a0 =~ a1) && (b0 =~ b1)++------------------------------------------------------------+-- Arbitrary instances for Points, Paths++instance Arbitrary n => Arbitrary (V2 n) where+    arbitrary = (^&) <$> arbitrary <*> arbitrary+    shrink (coords -> x :& y) = (^&) <$> shrink x <*> shrink y++instance Arbitrary n => Arbitrary (V3 n) where+    arbitrary = V3 <$> arbitrary <*> arbitrary <*> arbitrary+    shrink (coords -> x :& y :& z) = V3 <$> shrink x <*> shrink y <*> shrink z++instance Arbitrary (v n) => Arbitrary (Point v n) where+    arbitrary = P <$> arbitrary+    shrink (P v) = P <$> shrink v++instance (Arbitrary n, Floating n, Ord n) => Arbitrary (Transformation V2 n) where+    arbitrary = QC.sized arbT+      where+        arbT 0 = return mempty+        arbT n = QC.oneof+          [ rotation    <$> arbitrary+          , scaling     <$> arbitrary+          , translation <$> arbitrary+          , reflectionAbout <$> arbitrary <*> arbitrary+          , (<>) <$> arbT (n `div` 2) <*> arbT (n `div` 2)+          ]++instance Arbitrary n => Arbitrary (Angle n) where+    arbitrary = review rad <$> arbitrary++instance (Arbitrary n, Floating n) => Arbitrary (Direction V2 n) where+    arbitrary = rotate <$> arbitrary <*> pure xDir++-- -- | Not a valid Show instance because not valid Haskell input+-- instance (Show n, RealFloat n) => Show (Direction V2 n) where+--     show d = "Dir" <> ( show $ d ^. _theta . turn )++-- NOTE on shrinks: Adding definitions of 'shrink' below seems to work+--   in simple tests, but test case failures hang for a very long time+--   (presumably trying lots and lots of expensive shrinks).  Not sure+--   how to make shrinking more tractable.++instance (Arbitrary a, Arbitrary (Vn a)) => Arbitrary (Located a) where+    arbitrary = at <$> arbitrary <*> arbitrary+--    shrink (viewLoc -> (p,a)) = uncurry at <$> shrink (a,p)++instance Arbitrary n => Arbitrary (Offset Closed V2 n) where+    arbitrary = OffsetClosed <$> arbitrary+--    shrink (OffsetClosed x) = OffsetClosed <$> shrink x++instance Arbitrary n =>  Arbitrary (Segment Closed V2 n) where+    arbitrary = QC.oneof [Linear <$> arbitrary, Cubic <$> arbitrary <*> arbitrary <*> arbitrary]+    -- shrink (Linear x) = Linear <$> shrink x+    -- shrink (Cubic x y z) = Linear z+    --                      : [Cubic x' y' z' | (x',y',z') <- shrink (x,y,z)]++instance (Arbitrary n, Floating n, Ord n) => Arbitrary (Trail' Line V2 n) where+    arbitrary = lineFromSegments <$> arbitrary+--    shrink (lineSegments -> segs) = lineFromSegments <$> shrink segs++instance (Arbitrary n, Floating n, Ord n) => Arbitrary (Trail' Loop V2 n) where+    arbitrary = closeLine <$> arbitrary+--    shrink (cutLoop -> l) = closeLine <$> shrink l++instance (Arbitrary n, Floating n, Ord n) => Arbitrary (Trail V2 n) where+    arbitrary = QC.oneof [Trail <$> (arbitrary :: Gen (Trail' Loop V2 n)), Trail <$> (arbitrary :: Gen (Trail' Line V2 n))]+
+ test/Speed.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE TypeFamilies          #-}++module Main where++import Criterion+import Criterion.Main (defaultMain)++import Diagrams.Prelude+++main :: IO ()+main = defaultMain+    [ bgroup "rotates"+        [ bench "rotate"  $ whnf (rotate (90 @@ deg :: Angle Double)) (V2 3 3)+         ,bench "rotate1" $ whnf (rotate' (90 @@ deg :: Angle Double)) (V2 3 3)+         ,bench "rotate2" $ whnf (rotate'' (90 @@ deg :: Angle Double)) (V2 3 3)+        ]+    ]++--the original " '' " and a secondary " ' " rotate function for comparing speed testing+--note: function time changes dramatically when function is in this file rather than imported+++rotation' :: Floating n => Angle n -> T2 n+rotation' theta = fromLinear r (linv r)+                where+                r               = rot theta <-> rot (negated theta)+                rot th (V2 x y) = V2 (c * x - s * y)+                                     (s * x + c * y)+                                      where+                                        c = cosA th+                                        s = sinA th++rotate' :: (InSpace V2 n t, Transformable t, Floating n) => Angle n -> t -> t+rotate'  = transform . rotation'++rotation'' :: Floating n => Angle n -> T2 n+rotation'' theta = fromLinear r (linv r)+              where+                r               = rot theta <-> rot (negated theta)+                rot th (V2 x y) = V2 (cosA th * x - sinA th * y)+                                     (sinA th * x + cosA th * y)++rotate'' :: (InSpace V2 n t, Transformable t, Floating n) => Angle n -> t -> t+rotate'' = transform . rotation''
+ test/Test.hs view
@@ -0,0 +1,26 @@+import           Test.Tasty                (TestTree, defaultMain, testGroup)++import qualified Diagrams.Test.Angle            as Angle+import qualified Diagrams.Test.Direction        as Direction+import qualified Diagrams.Test.Transform        as Transform+import qualified Diagrams.Test.Transform.Matrix as TransformMatrix+import qualified Diagrams.Test.TwoD             as TwoD+import qualified Diagrams.Test.TwoD.Offset      as TwoD.Offset+import qualified Diagrams.Test.TwoD.Segment     as TwoD.Segment++import qualified Diagrams.Test.Trail            as Trail++tests :: TestTree+tests = testGroup "unit tests"+    [ testGroup "TwoD.Offset" TwoD.Offset.tests+    , testGroup "TwoD.Segment" TwoD.Segment.tests+    , TwoD.tests+    , Angle.tests+    , Direction.tests+    , Transform.tests+    , TransformMatrix.tests+    , Trail.tests+    ]++main :: IO ()+main = defaultMain tests