This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

🚧 Introduce Centered Points

+466 -241
+65
Cargo.lock
··· 1830 1830 ] 1831 1831 1832 1832 [[package]] 1833 + name = "num" 1834 + version = "0.4.3" 1835 + source = "registry+https://github.com/rust-lang/crates.io-index" 1836 + checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" 1837 + dependencies = [ 1838 + "num-bigint", 1839 + "num-complex", 1840 + "num-integer", 1841 + "num-iter", 1842 + "num-rational", 1843 + "num-traits", 1844 + ] 1845 + 1846 + [[package]] 1847 + name = "num-bigint" 1848 + version = "0.4.6" 1849 + source = "registry+https://github.com/rust-lang/crates.io-index" 1850 + checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 1851 + dependencies = [ 1852 + "num-integer", 1853 + "num-traits", 1854 + ] 1855 + 1856 + [[package]] 1857 + name = "num-complex" 1858 + version = "0.4.6" 1859 + source = "registry+https://github.com/rust-lang/crates.io-index" 1860 + checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 1861 + dependencies = [ 1862 + "num-traits", 1863 + ] 1864 + 1865 + [[package]] 1833 1866 name = "num-conv" 1834 1867 version = "0.1.0" 1835 1868 source = "registry+https://github.com/rust-lang/crates.io-index" 1836 1869 checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1870 + 1871 + [[package]] 1872 + name = "num-integer" 1873 + version = "0.1.46" 1874 + source = "registry+https://github.com/rust-lang/crates.io-index" 1875 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1876 + dependencies = [ 1877 + "num-traits", 1878 + ] 1879 + 1880 + [[package]] 1881 + name = "num-iter" 1882 + version = "0.1.45" 1883 + source = "registry+https://github.com/rust-lang/crates.io-index" 1884 + checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 1885 + dependencies = [ 1886 + "autocfg", 1887 + "num-integer", 1888 + "num-traits", 1889 + ] 1890 + 1891 + [[package]] 1892 + name = "num-rational" 1893 + version = "0.4.2" 1894 + source = "registry+https://github.com/rust-lang/crates.io-index" 1895 + checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 1896 + dependencies = [ 1897 + "num-bigint", 1898 + "num-integer", 1899 + "num-traits", 1900 + ] 1837 1901 1838 1902 [[package]] 1839 1903 name = "num-traits" ··· 2639 2703 "nanoid", 2640 2704 "nih_plug", 2641 2705 "notify-rust", 2706 + "num", 2642 2707 "once_cell", 2643 2708 "quick-xml 0.38.3", 2644 2709 "rand 0.9.2",
+1
Cargo.toml
··· 94 94 tokio = { version = "1.48.0", optional = true } 95 95 easing-function = { version = "0.1.1", optional = true } 96 96 quick-xml = "0.38.3" 97 + num = "0.4.3" 97 98 98 99 99 100 [dev-dependencies]
+13
bacon.toml
··· 8 8 default_job = "check" 9 9 env.CARGO_TERM_COLOR = "always" 10 10 11 + [keybindings] 12 + r = "refresh" 13 + 11 14 12 15 [jobs.playground] 13 16 workdir = "examples/playground" 14 17 watch = ["examples/playground/src/**"] 15 18 command = ["cargo", "run", "--"] 19 + 20 + [jobs.specimen] 21 + workdir = "examples/specimen" 22 + watch = ["examples/specimen/src/**"] 23 + command = ["cargo", "run", "--"] 24 + 25 + [jobs.dna-analysis-machine] 26 + workdir = "examples/dna-analysis-machine" 27 + watch = ["examples/dna-analysis-machine/src/**"] 28 + command = ["cargo", "run", "--"]
examples/dna-analysis-machine/dna-analysis-machine.png

This is a binary file and will not be displayed.

+31 -31
examples/dna-analysis-machine/src/snapshots/dna_analysis_machine__artwork.snap
··· 94 94 <rect data-object="hatches--anon-87" height="50" width="50" x="700" y="350" style="fill: url(#pattern-hatched-45deg-white-9.3-0.25);"/> 95 95 <circle cx="525" cy="75" data-object="hatches--anon-9" r="25" style="fill: url(#pattern-hatched-45deg-white-1.4-0.25);"/> 96 96 </g><g class="layer" data-layer="red dot"> 97 - <g style="fill: #cf0a2b;transform-origin: 575px 225px;filter: url(#filter-glow-5); overflow: visible;"> 97 + <g style="fill: #cf0a2b;transform-origin: 600px 250px;filter: url(#filter-glow-5); overflow: visible;"> 98 98 <circle cx="575" cy="225" data-object="red dot--anon-0" r="25"/> 99 99 </g></g><g class="layer" data-layer="strands"> 100 - <g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 275px;filter: url(#filter-glow-4); overflow: visible;"> 100 + <g style="stroke: #4fecec; fill: transparent;transform-origin: 250px 300px;filter: url(#filter-glow-4); overflow: visible;"> 101 101 <path d="M 150 250 Q 200 250 200 300" data-object="strands--strands#0" stroke-width="2"/> 102 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 175px 275px;filter: url(#filter-glow-4); overflow: visible;"> 102 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 200px 275px;filter: url(#filter-glow-4); overflow: visible;"> 103 103 <line data-object="strands--strands#1" stroke-width="2" x1="150" x2="200" y1="250" y2="250"/> 104 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 225px;filter: url(#filter-glow-4); overflow: visible;"> 104 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 275px 200px;filter: url(#filter-glow-4); overflow: visible;"> 105 105 <path d="M 200 250 Q 150 250 150 200" data-object="strands--strands#10" stroke-width="2"/> 106 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 275px 325px;filter: url(#filter-glow-4); overflow: visible;"> 106 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 300px 375px;filter: url(#filter-glow-4); overflow: visible;"> 107 107 <path d="M 250 300 Q 250 350 300 350" data-object="strands--strands#11" stroke-width="2"/> 108 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 225px 225px;filter: url(#filter-glow-4); overflow: visible;"> 108 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 300px 250px;filter: url(#filter-glow-4); overflow: visible;"> 109 109 <path d="M 250 200 Q 200 200 200 250" data-object="strands--strands#12" stroke-width="2"/> 110 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 275px 275px;filter: url(#filter-glow-4); overflow: visible;"> 110 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 300px 300px;filter: url(#filter-glow-4); overflow: visible;"> 111 111 <line data-object="strands--strands#13" stroke-width="2" x1="250" x2="250" y1="250" y2="300"/> 112 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 325px;filter: url(#filter-glow-4); overflow: visible;"> 112 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 275px 300px;filter: url(#filter-glow-4); overflow: visible;"> 113 113 <line data-object="strands--strands#14" stroke-width="2" x1="150" x2="200" y1="300" y2="300"/> 114 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 200px 250px;filter: url(#filter-glow-4); overflow: visible;"> 114 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 200px 300px;filter: url(#filter-glow-4); overflow: visible;"> 115 115 <path d="M 150 300 Q 150 200 250 200" data-object="strands--strands#15" stroke-width="2"/> 116 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 200px 225px;filter: url(#filter-glow-4); overflow: visible;"> 116 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 225px 250px;filter: url(#filter-glow-4); overflow: visible;"> 117 117 <line data-object="strands--strands#16" stroke-width="2" x1="150" x2="200" y1="300" y2="200"/> 118 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 200px 300px;filter: url(#filter-glow-4); overflow: visible;"> 118 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 250px 375px;filter: url(#filter-glow-4); overflow: visible;"> 119 119 <path d="M 150 250 Q 250 250 250 350" data-object="strands--strands#17" stroke-width="2"/> 120 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 200px 250px;filter: url(#filter-glow-4); overflow: visible;"> 120 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 300px 250px;filter: url(#filter-glow-4); overflow: visible;"> 121 121 <path d="M 250 300 Q 250 200 150 200" data-object="strands--strands#18" stroke-width="2"/> 122 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 175px 275px;filter: url(#filter-glow-4); overflow: visible;"> 122 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 225px 225px;filter: url(#filter-glow-4); overflow: visible;"> 123 123 <line data-object="strands--strands#19" stroke-width="2" x1="150" x2="150" y1="250" y2="250"/> 124 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 225px;filter: url(#filter-glow-4); overflow: visible;"> 124 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 250px 200px;filter: url(#filter-glow-4); overflow: visible;"> 125 125 <path d="M 200 200 Q 200 250 150 250" data-object="strands--strands#2" stroke-width="2"/> 126 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 225px 275px;filter: url(#filter-glow-4); overflow: visible;"> 126 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 325px 250px;filter: url(#filter-glow-4); overflow: visible;"> 127 127 <path d="M 250 300 Q 250 250 200 250" data-object="strands--strands#20" stroke-width="2"/> 128 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 225px 275px;filter: url(#filter-glow-4); overflow: visible;"> 128 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 300px 250px;filter: url(#filter-glow-4); overflow: visible;"> 129 129 <path d="M 250 250 Q 200 250 200 300" data-object="strands--strands#21" stroke-width="2"/> 130 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 225px 275px;filter: url(#filter-glow-4); overflow: visible;"> 130 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 300px 250px;filter: url(#filter-glow-4); overflow: visible;"> 131 131 <line data-object="strands--strands#22" stroke-width="2" x1="250" x2="200" y1="250" y2="250"/> 132 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 250px;filter: url(#filter-glow-4); overflow: visible;"> 132 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 250px 200px;filter: url(#filter-glow-4); overflow: visible;"> 133 133 <line data-object="strands--strands#23" stroke-width="2" x1="250" x2="150" y1="200" y2="200"/> 134 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 175px 250px;filter: url(#filter-glow-4); overflow: visible;"> 134 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 300px 200px;filter: url(#filter-glow-4); overflow: visible;"> 135 135 <line data-object="strands--strands#24" stroke-width="2" x1="250" x2="150" y1="200" y2="250"/> 136 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 325px;filter: url(#filter-glow-4); overflow: visible;"> 136 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 300px 300px;filter: url(#filter-glow-4); overflow: visible;"> 137 137 <line data-object="strands--strands#25" stroke-width="2" x1="150" x2="150" y1="300" y2="300"/> 138 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 225px 225px;filter: url(#filter-glow-4); overflow: visible;"> 138 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 250px 250px;filter: url(#filter-glow-4); overflow: visible;"> 139 139 <line data-object="strands--strands#26" stroke-width="2" x1="200" x2="200" y1="200" y2="200"/> 140 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 225px 225px;filter: url(#filter-glow-4); overflow: visible;"> 140 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 250px 250px;filter: url(#filter-glow-4); overflow: visible;"> 141 141 <line data-object="strands--strands#27" stroke-width="2" x1="200" x2="200" y1="200" y2="250"/> 142 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 200px 250px;filter: url(#filter-glow-4); overflow: visible;"> 142 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 250px 300px;filter: url(#filter-glow-4); overflow: visible;"> 143 143 <path d="M 150 200 Q 150 300 250 300" data-object="strands--strands#28" stroke-width="2"/> 144 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 200px 250px;filter: url(#filter-glow-4); overflow: visible;"> 144 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 300px 250px;filter: url(#filter-glow-4); overflow: visible;"> 145 145 <path d="M 250 300 Q 150 300 150 200" data-object="strands--strands#29" stroke-width="2"/> 146 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 175px 325px;filter: url(#filter-glow-4); overflow: visible;"> 146 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 300px 275px;filter: url(#filter-glow-4); overflow: visible;"> 147 147 <line data-object="strands--strands#3" stroke-width="2" x1="200" x2="150" y1="300" y2="300"/> 148 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 225px;filter: url(#filter-glow-4); overflow: visible;"> 148 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 200px 250px;filter: url(#filter-glow-4); overflow: visible;"> 149 149 <path d="M 150 250 Q 150 200 200 200" data-object="strands--strands#4" stroke-width="2"/> 150 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 250px 300px;filter: url(#filter-glow-4); overflow: visible;"> 150 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 300px 350px;filter: url(#filter-glow-4); overflow: visible;"> 151 151 <path d="M 200 250 Q 300 250 300 350" data-object="strands--strands#5" stroke-width="2"/> 152 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 225px;filter: url(#filter-glow-4); overflow: visible;"> 152 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 275px 200px;filter: url(#filter-glow-4); overflow: visible;"> 153 153 <path d="M 200 250 Q 200 200 150 200" data-object="strands--strands#6" stroke-width="2"/> 154 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 175px 275px;filter: url(#filter-glow-4); overflow: visible;"> 154 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 225px 275px;filter: url(#filter-glow-4); overflow: visible;"> 155 155 <path d="M 150 300 Q 200 300 200 250" data-object="strands--strands#7" stroke-width="2"/> 156 - </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 175px 275px;filter: url(#filter-glow-4); overflow: visible;"> 156 + </g><g style="stroke: #4fecec; fill: transparent;transform-origin: 300px 250px;filter: url(#filter-glow-4); overflow: visible;"> 157 157 <path d="M 200 300 Q 150 300 150 250" data-object="strands--strands#8" stroke-width="2"/> 158 - </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 225px 225px;filter: url(#filter-glow-4); overflow: visible;"> 158 + </g><g style="stroke: #e92e76; fill: transparent;transform-origin: 250px 300px;filter: url(#filter-glow-4); overflow: visible;"> 159 159 <line data-object="strands--strands#9" stroke-width="2" x1="200" x2="250" y1="250" y2="200"/> 160 160 </g></g><g class="layer" data-layer="root"/> 161 161 <defs >
+58 -33
examples/playground/result.svg
··· 1 1 <svg height="470" viewBox="-10 -10 470 470" width="470" xmlns="http://www.w3.org/2000/svg"> 2 2 <rect fill="#000000" height="470" width="470" x="-10" y="-10"/> 3 - <g class="layer" data-layer="connections"> 4 - <line data-object="connections--anon-0" stroke-width="3" x1="250" x2="100" y1="100" y2="150" style="stroke: #4fecec; fill: transparent;"/> 5 - <line data-object="connections--anon-1" stroke-width="3" x1="100" x2="0" y1="250" y2="250" style="stroke: #4fecec; fill: transparent;"/> 6 - <line data-object="connections--anon-10" stroke-width="3" x1="300" x2="350" y1="250" y2="200" style="stroke: #4fecec; fill: transparent;"/> 7 - <line data-object="connections--anon-2" stroke-width="3" x1="300" x2="400" y1="300" y2="150" style="stroke: #4fecec; fill: transparent;"/> 8 - <line data-object="connections--anon-3" stroke-width="3" x1="0" x2="0" y1="150" y2="250" style="stroke: #4fecec; fill: transparent;"/> 9 - <line data-object="connections--anon-4" stroke-width="3" x1="300" x2="250" y1="350" y2="400" style="stroke: #4fecec; fill: transparent;"/> 10 - <line data-object="connections--anon-5" stroke-width="3" x1="300" x2="400" y1="150" y2="150" style="stroke: #4fecec; fill: transparent;"/> 11 - <line data-object="connections--anon-6" stroke-width="3" x1="250" x2="400" y1="400" y2="300" style="stroke: #4fecec; fill: transparent;"/> 12 - <line data-object="connections--anon-7" stroke-width="3" x1="300" x2="400" y1="400" y2="350" style="stroke: #4fecec; fill: transparent;"/> 13 - <line data-object="connections--anon-8" stroke-width="3" x1="400" x2="400" y1="350" y2="300" style="stroke: #4fecec; fill: transparent;"/> 14 - <line data-object="connections--anon-9" stroke-width="3" x1="400" x2="400" y1="400" y2="250" style="stroke: #4fecec; fill: transparent;"/> 3 + <g class="layer" data-layer="dices_bg"> 4 + <line data-object="dices_bg--anon-0" stroke-width="0.5" x1="150" x2="300" y1="150" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 5 + <line data-object="dices_bg--anon-1" stroke-width="0.5" x1="300" x2="300" y1="150" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 6 + <line data-object="dices_bg--anon-10" stroke-width="0.5" x1="150" x2="0" y1="300" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 7 + <line data-object="dices_bg--anon-11" stroke-width="0.5" x1="0" x2="0" y1="300" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 8 + <line data-object="dices_bg--anon-12" stroke-width="0.5" x1="300" x2="450" y1="150" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 9 + <line data-object="dices_bg--anon-13" stroke-width="0.5" x1="450" x2="450" y1="150" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 10 + <line data-object="dices_bg--anon-14" stroke-width="0.5" x1="450" x2="300" y1="300" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 11 + <line data-object="dices_bg--anon-15" stroke-width="0.5" x1="300" x2="300" y1="300" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 12 + <line data-object="dices_bg--anon-16" stroke-width="0.5" x1="150" x2="300" y1="300" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 13 + <line data-object="dices_bg--anon-17" stroke-width="0.5" x1="300" x2="300" y1="300" y2="450" style="stroke: #81a0a8; fill: transparent;"/> 14 + <line data-object="dices_bg--anon-18" stroke-width="0.5" x1="300" x2="150" y1="450" y2="450" style="stroke: #81a0a8; fill: transparent;"/> 15 + <line data-object="dices_bg--anon-19" stroke-width="0.5" x1="150" x2="150" y1="450" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 16 + <line data-object="dices_bg--anon-2" stroke-width="0.5" x1="300" x2="150" y1="300" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 17 + <line data-object="dices_bg--anon-20" stroke-width="0.5" x1="300" x2="450" y1="300" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 18 + <line data-object="dices_bg--anon-21" stroke-width="0.5" x1="450" x2="450" y1="300" y2="450" style="stroke: #81a0a8; fill: transparent;"/> 19 + <line data-object="dices_bg--anon-22" stroke-width="0.5" x1="450" x2="300" y1="450" y2="450" style="stroke: #81a0a8; fill: transparent;"/> 20 + <line data-object="dices_bg--anon-23" stroke-width="0.5" x1="300" x2="300" y1="450" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 21 + <line data-object="dices_bg--anon-3" stroke-width="0.5" x1="150" x2="150" y1="300" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 22 + <line data-object="dices_bg--anon-4" stroke-width="0.5" x1="150" x2="300" y1="0" y2="0" style="stroke: #81a0a8; fill: transparent;"/> 23 + <line data-object="dices_bg--anon-5" stroke-width="0.5" x1="300" x2="300" y1="0" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 24 + <line data-object="dices_bg--anon-6" stroke-width="0.5" x1="300" x2="150" y1="150" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 25 + <line data-object="dices_bg--anon-7" stroke-width="0.5" x1="150" x2="150" y1="150" y2="0" style="stroke: #81a0a8; fill: transparent;"/> 26 + <line data-object="dices_bg--anon-8" stroke-width="0.5" x1="0" x2="150" y1="150" y2="150" style="stroke: #81a0a8; fill: transparent;"/> 27 + <line data-object="dices_bg--anon-9" stroke-width="0.5" x1="150" x2="150" y1="150" y2="300" style="stroke: #81a0a8; fill: transparent;"/> 15 28 </g><g class="layer" data-layer="dices"> 16 - <circle cx="200" cy="200" data-object="dices--anon-0" r="7" style="fill: #ffffff;"/> 17 - <circle cx="150" cy="0" data-object="dices--anon-1" r="7" style="fill: #ffffff;"/> 18 - <circle cx="400" cy="250" data-object="dices--anon-10" r="7" style="fill: #ffffff;"/> 19 - <circle cx="400" cy="150" data-object="dices--anon-11" r="7" style="fill: #ffffff;"/> 20 - <circle cx="150" cy="300" data-object="dices--anon-12" r="7" style="fill: #ffffff;"/> 21 - <circle cx="200" cy="350" data-object="dices--anon-13" r="7" style="fill: #ffffff;"/> 22 - <circle cx="250" cy="400" data-object="dices--anon-14" r="7" style="fill: #ffffff;"/> 23 - <circle cx="300" cy="300" data-object="dices--anon-15" r="7" style="fill: #ffffff;"/> 24 - <circle cx="300" cy="350" data-object="dices--anon-16" r="7" style="fill: #ffffff;"/> 25 - <circle cx="300" cy="400" data-object="dices--anon-17" r="7" style="fill: #ffffff;"/> 26 - <circle cx="400" cy="400" data-object="dices--anon-18" r="7" style="fill: #ffffff;"/> 27 - <circle cx="400" cy="350" data-object="dices--anon-19" r="7" style="fill: #ffffff;"/> 28 - <circle cx="250" cy="100" data-object="dices--anon-2" r="7" style="fill: #ffffff;"/> 29 - <circle cx="400" cy="300" data-object="dices--anon-20" r="7" style="fill: #ffffff;"/> 30 - <circle cx="0" cy="150" data-object="dices--anon-3" r="7" style="fill: #ffffff;"/> 31 - <circle cx="0" cy="250" data-object="dices--anon-4" r="7" style="fill: #ffffff;"/> 32 - <circle cx="100" cy="250" data-object="dices--anon-5" r="7" style="fill: #ffffff;"/> 33 - <circle cx="100" cy="150" data-object="dices--anon-6" r="7" style="fill: #ffffff;"/> 34 - <circle cx="300" cy="150" data-object="dices--anon-7" r="7" style="fill: #ffffff;"/> 35 - <circle cx="300" cy="250" data-object="dices--anon-8" r="7" style="fill: #ffffff;"/> 36 - <circle cx="350" cy="200" data-object="dices--anon-9" r="7" style="fill: #ffffff;"/> 29 + <circle cx="225" cy="225" data-object="dices--anon-0" r="7" style="fill: #ffffff;"/> 30 + <circle cx="175" cy="25" data-object="dices--anon-1" r="7" style="fill: #ffffff;"/> 31 + <circle cx="425" cy="275" data-object="dices--anon-10" r="7" style="fill: #ffffff;"/> 32 + <circle cx="425" cy="175" data-object="dices--anon-11" r="7" style="fill: #ffffff;"/> 33 + <circle cx="175" cy="325" data-object="dices--anon-12" r="7" style="fill: #ffffff;"/> 34 + <circle cx="225" cy="375" data-object="dices--anon-13" r="7" style="fill: #ffffff;"/> 35 + <circle cx="275" cy="425" data-object="dices--anon-14" r="7" style="fill: #ffffff;"/> 36 + <circle cx="325" cy="325" data-object="dices--anon-15" r="7" style="fill: #ffffff;"/> 37 + <circle cx="325" cy="375" data-object="dices--anon-16" r="7" style="fill: #ffffff;"/> 38 + <circle cx="325" cy="425" data-object="dices--anon-17" r="7" style="fill: #ffffff;"/> 39 + <circle cx="425" cy="425" data-object="dices--anon-18" r="7" style="fill: #ffffff;"/> 40 + <circle cx="425" cy="375" data-object="dices--anon-19" r="7" style="fill: #ffffff;"/> 41 + <circle cx="275" cy="125" data-object="dices--anon-2" r="7" style="fill: #ffffff;"/> 42 + <circle cx="425" cy="325" data-object="dices--anon-20" r="7" style="fill: #ffffff;"/> 43 + <circle cx="25" cy="175" data-object="dices--anon-3" r="7" style="fill: #ffffff;"/> 44 + <circle cx="25" cy="275" data-object="dices--anon-4" r="7" style="fill: #ffffff;"/> 45 + <circle cx="125" cy="275" data-object="dices--anon-5" r="7" style="fill: #ffffff;"/> 46 + <circle cx="125" cy="175" data-object="dices--anon-6" r="7" style="fill: #ffffff;"/> 47 + <circle cx="325" cy="175" data-object="dices--anon-7" r="7" style="fill: #ffffff;"/> 48 + <circle cx="325" cy="275" data-object="dices--anon-8" r="7" style="fill: #ffffff;"/> 49 + <circle cx="375" cy="225" data-object="dices--anon-9" r="7" style="fill: #ffffff;"/> 50 + </g><g class="layer" data-layer="connections"> 51 + <line data-object="connections--anon-0" stroke-width="3" x1="425" x2="425" y1="325" y2="175" style="stroke: #4fecec; fill: transparent;"/> 52 + <line data-object="connections--anon-1" stroke-width="3" x1="425" x2="325" y1="175" y2="325" style="stroke: #4fecec; fill: transparent;"/> 53 + <line data-object="connections--anon-10" stroke-width="3" x1="225" x2="325" y1="375" y2="275" style="stroke: #4fecec; fill: transparent;"/> 54 + <line data-object="connections--anon-2" stroke-width="3" x1="175" x2="325" y1="325" y2="275" style="stroke: #4fecec; fill: transparent;"/> 55 + <line data-object="connections--anon-3" stroke-width="3" x1="25" x2="125" y1="275" y2="275" style="stroke: #4fecec; fill: transparent;"/> 56 + <line data-object="connections--anon-4" stroke-width="3" x1="325" x2="175" y1="425" y2="325" style="stroke: #4fecec; fill: transparent;"/> 57 + <line data-object="connections--anon-5" stroke-width="3" x1="425" x2="375" y1="275" y2="225" style="stroke: #4fecec; fill: transparent;"/> 58 + <line data-object="connections--anon-6" stroke-width="3" x1="125" x2="25" y1="275" y2="275" style="stroke: #4fecec; fill: transparent;"/> 59 + <line data-object="connections--anon-7" stroke-width="3" x1="425" x2="425" y1="375" y2="275" style="stroke: #4fecec; fill: transparent;"/> 60 + <line data-object="connections--anon-8" stroke-width="3" x1="425" x2="325" y1="425" y2="275" style="stroke: #4fecec; fill: transparent;"/> 61 + <line data-object="connections--anon-9" stroke-width="3" x1="325" x2="375" y1="325" y2="225" style="stroke: #4fecec; fill: transparent;"/> 37 62 </g><g class="layer" data-layer="root"/> 38 63 <defs /> 39 64 </svg>
+41 -40
examples/playground/src/main.rs
··· 1 + use itertools::Itertools; 1 2 use rand::{Rng, seq::IteratorRandom}; 2 - use shapemaker::{geometry::Norm, *}; 3 + use shapemaker::*; 3 4 4 5 const DICES_GRID: Grid = Grid(3, 3); 5 6 ··· 22 23 cyan: "#4fecec".into(), 23 24 }; 24 25 25 - dice(&mut canvas, Point(1, 1), vec![(1, 1)]); 26 - dice(&mut canvas, Point(1, 0), vec![(0, 0), (2, 2)]); 27 - dice( 28 - &mut canvas, 29 - Point(0, 1), 30 - vec![(0, 0), (0, 2), (2, 2), (2, 0)], 31 - ); 26 + dice(&mut canvas, (1, 1), vec![(1, 1)]); 27 + dice(&mut canvas, (1, 0), vec![(0, 0), (2, 2)]); 28 + dice(&mut canvas, (0, 1), vec![(0, 0), (0, 2), (2, 2), (2, 0)]); 32 29 dice( 33 30 &mut canvas, 34 - Point(2, 1), 31 + (2, 1), 35 32 vec![(0, 0), (0, 2), (1, 1), (2, 2), (2, 0)], 36 33 ); 37 - dice(&mut canvas, Point(1, 2), vec![(0, 0), (1, 1), (2, 2)]); 34 + dice(&mut canvas, (1, 2), vec![(0, 0), (1, 1), (2, 2)]); 38 35 dice( 39 36 &mut canvas, 40 - Point(2, 2), 37 + (2, 2), 41 38 vec![(0, 0), (0, 1), (0, 2), (2, 2), (2, 1), (2, 0)], 42 39 ); 43 40 ··· 79 76 80 77 // let world = canvas.world_region.clone(); 81 78 // let grid = canvas.layer_or_empty("grid"); 82 - // for (Point(x, _), _, _) in world.top_edge().tuples() { 83 - // grid.add_anon(Line(Point(x, 0), Point(x, 9), 1.0).colored(Gray)); 79 + // for (p, _, _) in world.top_edge().tuples() { 80 + // grid.add_anon(Line(p.with_y(0), p.with_y(9), 1.0).colored(Gray)); 84 81 // } 85 - // for (_, _, Point(_, y)) in world.left_edge().tuples() { 86 - // grid.add_anon(Line(Point(0, y), Point(9, y), 1.0).colored(Gray)); 82 + // for (p, _, _) in world.left_edge().tuples() { 83 + // grid.add_anon(Line(p.with_x(0), p.with_x(9), 1.0).colored(Gray)); 87 84 // } 85 + // grid.add_anon(Line(P(0, 9), P(9, 9), 1.0).colored(Gray)); 86 + // grid.add_anon(Line(P(9, 0), P(9, 9), 1.0).colored(Gray)); 87 + 88 + canvas.reorder_layers(vec!["connections", "dices", "dices_bg"]); 88 89 89 90 canvas 90 91 .render_to_svg_file("result.svg") 91 92 .expect("Could not write SVG"); 92 93 } 93 94 94 - fn dice(canvas: &mut Canvas, place_at: Point, dots_at: Vec<impl Into<Point>>) { 95 - let Region { start, end } = Region::from_topleft( 96 - place_at.coords_from(&DICES_GRID), 95 + fn dice( 96 + canvas: &mut Canvas, 97 + place_at: (usize, usize), 98 + dots_at: Vec<(usize, usize)>, 99 + ) { 100 + for (x, y) in dots_at { 101 + let at = Point::Center(x, y) 102 + .translated_by(Point::from(place_at).coords_from(&DICES_GRID)); 103 + 104 + canvas 105 + .layer_or_empty("dices") 106 + .add_anon(SmallCircle(at).colored(White)); 107 + } 108 + 109 + let dicebox = Region::from_topleft( 110 + Point::from(place_at).coords_from(&DICES_GRID), 97 111 DICES_GRID.size(), 98 112 ) 99 113 .unwrap(); 100 114 101 - // canvas.layer_or_empty("dices").add_anon( 102 - // Rectangle(start, end).filled(Fill::Hatches( 103 - // Gray, 104 - // Angle::from_degrees(45.), 105 - // 0.25, 106 - // 10.0, 107 - // )), 108 - // ); 109 - 110 - for point in dots_at { 111 - let at = point 112 - .into() 113 - .translated_by(place_at.coords_from(&DICES_GRID)); 114 - 115 - canvas 116 - .layer_or_empty("dices") 117 - .add_anon(BigDot(at).colored(White)); 118 - } 115 + canvas.layer_or_empty("dices_bg").add_many_anon( 116 + dicebox 117 + .corners() 118 + .iter() 119 + .circular_tuple_windows() 120 + .map(|(&s, &e)| Line(s, e, 0.5).colored(Gray)), 121 + ); 119 122 } 120 123 121 124 struct Grid(usize, usize); ··· 127 130 } 128 131 129 132 impl GridSnappable for Point { 130 - fn coords_from(&self, grid: &Grid) -> Self { 131 - let Self(x, y) = self; 132 - 133 - Self(x * grid.0, y * grid.1) 133 + fn coords_from(&self, Grid(sx, sy): &Grid) -> Self { 134 + self.with(self.x() * sx, self.y() * sy) 134 135 } 135 136 } 136 137
+2 -1
examples/schedule-hell/src/main.rs
··· 16 16 impl Default for State { 17 17 fn default() -> Self { 18 18 Self { 19 - bass_pattern_at: Region::from_topleft(Point(1, 1), (2, 2)).unwrap(), 19 + bass_pattern_at: Region::from_topleft(CornerPoint(1, 1), (2, 2)) 20 + .unwrap(), 20 21 kick_color: Color::White, 21 22 rng: SmallRng::seed_from_u64(0), 22 23 cranks: 0,
+5 -4
examples/schedule-hell/src/scenes/backbone.rs
··· 1 1 use anyhow::Result; 2 2 use rand::{Rng, rngs::SmallRng, seq::IteratorRandom}; 3 + use shapemaker::CornerPoint as P; 3 4 use shapemaker::*; 4 5 5 6 use crate::State; ··· 74 75 canvas.root().set( 75 76 format!("grid-rows-{point}"), 76 77 Object::Line( 77 - Point(point.0, world.topleft().1), 78 - Point(point.0, world.bottomleft().1 + 1), 78 + P(point.x(), world.topleft().y()), 79 + P(point.x(), world.bottomleft().y() + 1), 79 80 grid_thickness * 0.75, 80 81 ) 81 82 .filled(White.translucent(0.05 + rng.random_range(0.0..0.3))), ··· 88 89 canvas.root().set( 89 90 format!("grid-cols-{point}"), 90 91 Object::Line( 91 - Point(world.topleft().0, point.1), 92 - Point(world.bottomright().0 + 1, point.1), 92 + P(world.topleft().x(), point.y()), 93 + P(world.bottomright().x() + 1, point.y()), 93 94 grid_thickness * 0.75, 94 95 ) 95 96 .filled(White.translucent(0.005 + rng.random_range(0.0..0.3))),
+18 -13
examples/specimen/src/main.rs
··· 1 + use shapemaker::CornerPoint as P; 1 2 use shapemaker::*; 2 3 3 4 pub fn shapes_shed() -> Canvas { ··· 7 8 canvas.set_background(Color::White); 8 9 9 10 canvas.layer_unchecked("root").add_objects([ 10 - Object::BigCircle(Point(0, 0)).colored(Color::Black), 11 - Object::CurveOutward(Point(1, 1), Point(2, 0), 5.0).colored(Color::Black), 12 - Object::CurveInward(Point(2, 1), Point(3, 0), 5.0).colored(Color::Black), 13 - Object::SmallCircle(Point(0, 1)).colored(Color::Black), 14 - Object::Line(Point(1, 1), Point(2, 2), 5.0).colored(Color::Black), 15 - Object::Rectangle(Point(0, 2), Point(0, 2)).colored(Color::Black), 16 - Object::Dot(Point(2, 3)).colored(Color::Black), 17 - Object::Polygon( 18 - Point(2, 1), 11 + BigCircle(P(0, 0)).colored(Black), 12 + CurveOutward(P(1, 1), P(2, 0), 5.0).colored(Black), 13 + CurveInward(P(2, 1), P(3, 0), 5.0).colored(Black), 14 + SmallCircle(P(0, 1)).colored(Black), 15 + Line(P(1, 1), P(2, 2), 5.0).colored(Black), 16 + Rectangle(P(0, 2), P(0, 2)).colored(Black), 17 + Dot(CenterPoint(1, 2)).colored(Black), 18 + Polygon( 19 + P(2, 1), 19 20 vec![ 20 - LineSegment::Straight(Point(3, 1)), 21 - LineSegment::Straight(Point(3, 2)), 21 + LineSegment::Straight(P(3, 1)), 22 + LineSegment::Straight(P(3, 2)), 22 23 ], 23 24 ) 24 - .colored(Color::Black), 25 - Object::Text(Point(0, 0), "test".into(), 5.0).colored(Color::Black), 25 + .colored(Black), 26 + CenteredText(P(2, 2), "Test".into(), 5.0).colored(Black), 26 27 ]); 27 28 28 29 canvas ··· 73 74 grid() 74 75 .render_to_svg_file("grid.svg") 75 76 .expect("Failed to render grid"); 77 + println!("Rendered grid.svg"); 76 78 colors_shed() 77 79 .render_to_svg_file("colorshed.svg") 78 80 .expect("Failed to render colors_shed"); 81 + println!("Rendered colorshed.svg"); 79 82 shapes_shed() 80 83 .render_to_svg_file("shapeshed.svg") 81 84 .expect("Failed to render shapes_shed"); 85 + println!("Rendered shapeshed.svg"); 82 86 shapes_shed() 83 87 .render_to_png("shapeshed.png", 1000) 84 88 .expect("Failed to render shapes_shed as PNG"); 89 + println!("Rendered shapeshed.png"); 85 90 } 86 91 87 92 #[test]
+4 -4
examples/specimen/src/snapshots/specimen__shapes_shed.snap
··· 8 8 <circle cx="25" cy="25" data-object="root--anon-0" r="25" style="fill: black;"/> 9 9 <path d="M 50 50 Q 100 50 100 0" data-object="root--anon-1" stroke-width="5" style="stroke: black; fill: transparent;"/> 10 10 <path d="M 100 50 Q 100 0 150 0" data-object="root--anon-2" stroke-width="5" style="stroke: black; fill: transparent;"/> 11 - <circle cx="0" cy="50" data-object="root--anon-3" r="5" style="fill: black;"/> 11 + <circle cx="25" cy="75" data-object="root--anon-3" r="5" style="fill: black;"/> 12 12 <line data-object="root--anon-4" stroke-width="5" x1="50" x2="100" y1="50" y2="100" style="stroke: black; fill: transparent;"/> 13 13 <rect data-object="root--anon-5" height="50" width="50" x="0" y="100" style="fill: black;"/> 14 - <circle cx="100" cy="150" data-object="root--anon-6" r="2" style="fill: black;"/> 14 + <circle cx="75" cy="125" data-object="root--anon-6" r="2" style="fill: black;"/> 15 15 <path d="M 100 50 L 150 50 L 150 100 Z" data-object="root--anon-7" style="fill: black;"/> 16 - <text data-object="root--anon-8" dominant-baseline="hanging" font-family="Inconsolata" font-size="5pt" text-anchor="start" x="0" y="0" style="fill: black;"> 17 - test</text></g><defs /> 16 + <text data-object="root--anon-8" dominant-baseline="middle" font-family="Inconsolata" font-size="5pt" text-anchor="middle" x="125" y="125" style="fill: black;"> 17 + Test</text></g><defs /> 18 18 </svg>
+1
src/geometry/mod.rs
··· 5 5 6 6 pub use angle::Angle; 7 7 pub use axis::Axis; 8 + pub use point::Point::{Center as CenterPoint, Corner as CornerPoint}; 8 9 pub use point::{Norm, Point}; 9 10 pub use region::{Containable, Region};
+85 -15
src/geometry/point.rs
··· 1 + use num::FromPrimitive; 2 + 1 3 #[cfg(feature = "web")] 2 4 use wasm_bindgen::prelude::*; 3 5 ··· 6 8 use super::Angle; 7 9 8 10 #[cfg_attr(feature = "web", wasm_bindgen)] 9 - #[derive(Debug, Clone, Copy, Default, PartialEq)] 10 - pub struct Point(pub usize, pub usize); 11 + #[derive(Debug, Clone, Copy, PartialEq)] 12 + pub enum Point { 13 + Corner(usize, usize), 14 + Center(usize, usize), 15 + } 16 + 17 + impl Default for Point { 18 + fn default() -> Self { 19 + Self::Corner(0, 0) 20 + } 21 + } 11 22 12 23 impl Point { 13 24 pub fn coords(&self, cell_size: usize) -> (f32, f32) { 14 - ( 15 - self.0 as f32 * cell_size as f32, 16 - self.1 as f32 * cell_size as f32, 17 - ) 25 + let (x, y) = self.xy::<f32>(); 26 + let cell = cell_size as f32; 27 + 28 + match self { 29 + Point::Corner(..) => (x * cell, y * cell), 30 + Point::Center(..) => (x * cell + cell / 2.0, y * cell + cell / 2.0), 31 + } 32 + } 33 + 34 + pub fn as_centered(&self) -> Self { 35 + match self { 36 + Point::Corner(x, y) => Point::Center(*x, *y), 37 + Point::Center(_, _) => *self, 38 + } 39 + } 40 + 41 + pub fn as_corner(&self) -> Self { 42 + match self { 43 + Point::Center(x, y) => Point::Corner(*x, *y), 44 + Point::Corner(_, _) => *self, 45 + } 18 46 } 19 47 20 48 pub fn region(&self) -> Region { 21 49 Region::from((self.clone(), self.clone())) 22 50 } 23 51 52 + pub fn with(&self, x: usize, y: usize) -> Self { 53 + match self { 54 + Point::Corner(..) => Point::Corner(x, y), 55 + Point::Center(..) => Point::Center(x, y), 56 + } 57 + } 58 + 24 59 pub fn set(&mut self, x: usize, y: usize) { 25 - self.0 = x; 26 - self.1 = y; 60 + *self = self.with(x, y); 61 + } 62 + 63 + pub fn set_x(&mut self, x: usize) { 64 + self.set(x, self.y()); 65 + } 66 + 67 + pub fn with_x(&self, x: usize) -> Self { 68 + self.with(x, self.y()) 69 + } 70 + 71 + pub fn increment_x(&mut self, by: isize) { 72 + self.set_x(self.x().saturating_add_signed(by)); 73 + } 74 + 75 + pub fn set_y(&mut self, y: usize) { 76 + self.set(self.x(), y); 77 + } 78 + 79 + pub fn increment_y(&mut self, by: isize) { 80 + self.set_y(self.y().saturating_add_signed(by)); 81 + } 82 + 83 + pub fn with_y(&self, y: usize) -> Self { 84 + self.with(self.x(), y) 85 + } 86 + 87 + pub fn xy<N: FromPrimitive>(&self) -> (N, N) { 88 + let (x, y) = match self { 89 + &Point::Corner(x, y) => (x, y), 90 + &Point::Center(x, y) => (x, y), 91 + }; 92 + 93 + (N::from_usize(x).unwrap(), N::from_usize(y).unwrap()) 27 94 } 28 95 29 96 pub fn x(&self) -> usize { 30 - self.0 97 + self.xy().0 31 98 } 32 99 33 100 pub fn y(&self) -> usize { 34 - self.1 101 + self.xy().1 35 102 } 36 103 37 104 pub fn translated(&self, dx: i32, dy: i32) -> Self { ··· 85 152 86 153 impl From<(usize, usize)> for Point { 87 154 fn from(value: (usize, usize)) -> Self { 88 - Self(value.0, value.1) 155 + Self::Corner(value.0, value.1) 89 156 } 90 157 } 91 158 92 159 impl From<(&usize, &usize)> for Point { 93 160 fn from(value: (&usize, &usize)) -> Self { 94 - Self(*value.0, *value.1) 161 + Self::Corner(*value.0, *value.1) 95 162 } 96 163 } 97 164 98 165 impl From<(i32, i32)> for Point { 99 166 fn from(value: (i32, i32)) -> Self { 100 - Self(value.0 as usize, value.1 as usize) 167 + Self::Corner(value.0 as usize, value.1 as usize) 101 168 } 102 169 } 103 170 104 171 impl PartialEq<(usize, usize)> for Point { 105 172 fn eq(&self, other: &(usize, usize)) -> bool { 106 - self.0 == other.0 && self.1 == other.1 173 + self.xy() == other.clone() 107 174 } 108 175 } 109 176 ··· 111 178 112 179 impl std::fmt::Display for Point { 113 180 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 114 - write!(f, "({}, {})", self.0, self.1) 181 + match self { 182 + Point::Corner(x, y) => write!(f, "({x}, {y})"), 183 + Point::Center(x, y) => write!(f, "centered ({x}, {y})"), 184 + } 115 185 } 116 186 } 117 187
+85 -78
src/geometry/region.rs
··· 27 27 } 28 28 29 29 pub fn iter_lower_triangle(&self) -> impl Iterator<Item = Point> { 30 - self.iter().filter(|Point(x, y)| x < y) 30 + self.iter().filter(|p| p.x() < p.y()) 31 31 } 32 32 33 33 pub fn iter_upper_strict_triangle(&self) -> impl Iterator<Item = Point> { 34 - self.iter().filter(|Point(x, y)| x >= y) 34 + self.iter().filter(|p| p.x() >= p.y()) 35 35 } 36 36 37 37 /// Iterates all points outlining the region, in clockwise order starting from top-left ··· 43 43 } 44 44 45 45 pub fn top_edge(&self) -> impl DoubleEndedIterator<Item = Point> { 46 - (self.start.0..=self.end.0).map(move |x| Point(x, self.start.1)) 46 + (self.start.x()..=self.end.x()) 47 + .map(move |x| Point::Corner(x, self.start.y())) 47 48 } 48 49 49 50 pub fn bottom_edge(&self) -> impl DoubleEndedIterator<Item = Point> { 50 - (self.start.0..=self.end.0).map(move |x| Point(x, self.end.1)) 51 + (self.start.x()..=self.end.x()) 52 + .map(move |x| Point::Corner(x, self.end.y())) 51 53 } 52 54 53 55 pub fn left_edge(&self) -> impl DoubleEndedIterator<Item = Point> { 54 - (self.start.1..=self.end.1).map(move |y| Point(self.start.0, y)) 56 + (self.start.y()..=self.end.y()) 57 + .map(move |y| Point::Corner(self.start.x(), y)) 55 58 } 56 59 57 60 pub fn right_edge(&self) -> impl DoubleEndedIterator<Item = Point> { 58 - (self.start.1..=self.end.1).map(move |y| Point(self.end.0, y)) 61 + (self.start.y()..=self.end.y()) 62 + .map(move |y| Point::Corner(self.end.x(), y)) 59 63 } 60 64 61 65 /// Corners of the region's outline ··· 113 117 type Item = Point; 114 118 115 119 fn next(&mut self) -> Option<Self::Item> { 116 - if self.current.0 > self.region.end.0 { 117 - self.current.0 = self.region.start.0; 118 - self.current.1 += 1; 120 + if self.current.x() > self.region.end.x() { 121 + self.current 122 + .set(self.region.start.x(), self.current.y() + 1); 119 123 } 120 - if self.current.1 > self.region.end.1 { 124 + if self.current.y() > self.region.end.y() { 121 125 return None; 122 126 } 123 127 let result = self.current; 124 - self.current.0 += 1; 128 + self.current.set_x(self.current.x() + 1); 125 129 Some(result) 126 130 } 127 131 } ··· 136 140 } 137 141 138 142 impl From<(&Point, &Point)> for Region { 139 - fn from(value: (&Point, &Point)) -> Self { 140 - Self { 141 - start: *value.0, 142 - end: *value.1, 143 - } 143 + fn from((s, e): (&Point, &Point)) -> Self { 144 + Self { start: *s, end: *e } 144 145 } 145 146 } 146 147 147 148 impl From<(Point, Point)> for Region { 148 - fn from(value: (Point, Point)) -> Self { 149 - Self { 150 - start: value.0, 151 - end: value.1, 152 - } 149 + fn from((start, end): (Point, Point)) -> Self { 150 + Self { start, end } 153 151 } 154 152 } 155 153 156 154 impl From<((usize, usize), (usize, usize))> for Region { 157 - fn from(value: ((usize, usize), (usize, usize))) -> Self { 155 + fn from((start, end): ((usize, usize), (usize, usize))) -> Self { 158 156 Region { 159 - start: value.0.into(), 160 - end: value.1.into(), 157 + start: start.into(), 158 + end: end.into(), 161 159 } 162 160 } 163 161 } ··· 167 165 168 166 fn sub(self, rhs: Self) -> Self::Output { 169 167 ( 170 - (self.start.0 as i32 - rhs.start.0 as i32), 171 - (self.start.1 as i32 - rhs.start.1 as i32), 168 + (self.start.x() as i32 - rhs.start.x() as i32), 169 + (self.start.y() as i32 - rhs.start.y() as i32), 172 170 ) 173 171 } 174 172 } 175 173 176 174 #[test] 177 175 fn test_sub_and_transate_coherence() { 178 - let a = Region::from_origin(Point(3, 3)).unwrap(); 176 + let a = Region::from_origin(Point::Corner(3, 3)).unwrap(); 179 177 let mut b = a; 180 178 b.translate(2, 3); 181 179 ··· 195 193 } 196 194 197 195 pub fn bottomleft(&self) -> Point { 198 - Point(self.start.0, self.end.1) 196 + Point::Corner(self.start.x(), self.end.y()) 199 197 } 200 198 201 199 pub fn bottomright(&self) -> Point { 202 - Point(self.end.0, self.end.1) 200 + Point::Corner(self.end.x(), self.end.y()) 203 201 } 204 202 205 203 pub fn topleft(&self) -> Point { 206 - Point(self.start.0, self.start.1) 204 + Point::Corner(self.start.x(), self.start.y()) 207 205 } 208 206 209 207 pub fn topright(&self) -> Point { 210 - Point(self.end.0, self.start.1) 208 + Point::Corner(self.end.x(), self.start.y()) 211 209 } 212 210 213 211 pub fn center(&self) -> Point { 214 - Point( 215 - (self.start.0 + self.end.0) / 2, 216 - (self.start.1 + self.end.1) / 2, 212 + Point::Center( 213 + (self.start.x() + self.end.x()) / 2, 214 + (self.start.y() + self.end.y()) / 2, 217 215 ) 218 216 } 219 217 ··· 223 221 224 222 pub fn merge<'a>(&'a self, other: &'a Region) -> Region { 225 223 Region { 226 - start: Point( 227 - self.start.0.min(other.start.0), 228 - self.start.1.min(other.start.1), 224 + start: Point::Corner( 225 + self.start.x().min(other.start.x()), 226 + self.start.y().min(other.start.y()), 229 227 ), 230 - end: Point(self.end.0.max(other.end.0), self.end.1.max(other.end.1)), 228 + end: Point::Corner( 229 + self.end.x().max(other.end.x()), 230 + self.end.y().max(other.end.y()), 231 + ), 231 232 } 232 233 } 233 234 ··· 246 247 Self::from_topleft(self.start, size) 247 248 } 248 249 249 - pub fn from_bottomleft(origin: Point, size: (usize, usize)) -> Result<Self> { 250 - Self::from_topleft(origin.translated(0, -(size.1 as i32 - 1)), size) 250 + pub fn from_bottomleft( 251 + origin: Point, 252 + (w, h): (usize, usize), 253 + ) -> Result<Self> { 254 + Self::from_topleft(origin.translated(0, -(h as i32 - 1)), (w, h)) 251 255 } 252 256 253 257 pub fn starting_from_bottomleft(&self, size: (usize, usize)) -> Result<Self> { 254 258 Self::from_bottomleft(self.bottomleft(), size) 255 259 } 256 260 257 - pub fn from_bottomright(origin: Point, size: (usize, usize)) -> Result<Self> { 258 - Self::new( 259 - origin.translated(-(size.0 as i32 - 1), -(size.1 as i32 - 1)), 260 - origin, 261 - ) 261 + pub fn from_bottomright( 262 + origin: Point, 263 + (w, h): (usize, usize), 264 + ) -> Result<Self> { 265 + Self::new(origin.translated(-(w as i32 - 1), -(h as i32 - 1)), origin) 262 266 } 263 267 264 268 pub fn starting_from_bottomright( ··· 268 272 Self::from_bottomright(self.bottomright(), size) 269 273 } 270 274 271 - pub fn from_topright(origin: Point, size: (usize, usize)) -> Result<Self> { 272 - Self::from_topleft(origin.translated(-(size.0 as i32 - 1), 0), size) 275 + pub fn from_topright(origin: Point, (w, h): (usize, usize)) -> Result<Self> { 276 + Self::from_topleft(origin.translated(-(w as i32 - 1), 0), (w, h)) 273 277 } 274 278 275 279 pub fn starting_from_topright(&self, size: (usize, usize)) -> Result<Self> { ··· 278 282 279 283 pub fn from_center_and_size( 280 284 center: Point, 281 - size: (usize, usize), 285 + (w, h): (usize, usize), 282 286 ) -> Result<Self> { 283 - let half_size = (size.0 / 2, size.1 / 2); 287 + let (half_w, half_h) = (w / 2, h / 2); 284 288 Self::new( 285 - (center.0 - half_size.0, center.1 - half_size.1), 286 - (center.0 + half_size.0, center.1 + half_size.1), 289 + (center.x() - half_w, center.y() - half_h), 290 + (center.x() + half_w, center.y() + half_h), 287 291 ) 288 292 } 289 293 290 294 // panics if the region is invalid 291 295 pub fn ensure_valid(self) -> Result<Self> { 292 - if self.start.0 > self.end.0 || self.start.1 > self.end.1 { 296 + if self.start.x() > self.end.x() || self.start.y() > self.end.y() { 293 297 return Err(format_err!( 294 298 "Invalid region: start ({:?}) > end ({:?})", 295 299 self.start, ··· 298 302 } 299 303 300 304 // check that no point's coordinate is too close to usize::MAX 301 - if vec![self.start.0, self.start.1, self.end.0, self.end.1] 305 + if vec![self.start.x(), self.start.y(), self.end.x(), self.end.y()] 302 306 .iter() 303 307 .any(|&coord| coord >= usize::MAX - 10) 304 308 { ··· 318 322 pub fn translated(&self, dx: i32, dy: i32) -> Self { 319 323 Self { 320 324 start: ( 321 - (self.start.0 as i32 + dx).max(0) as usize, 322 - (self.start.1 as i32 + dy).max(0) as usize, 325 + (self.start.x() as i32 + dx).max(0) as usize, 326 + (self.start.y() as i32 + dy).max(0) as usize, 323 327 ) 324 328 .into(), 325 329 end: ( 326 - (self.end.0 as i32 + dx).max(0) as usize, 327 - (self.end.1 as i32 + dy).max(0) as usize, 330 + (self.end.x() as i32 + dx).max(0) as usize, 331 + (self.end.y() as i32 + dy).max(0) as usize, 328 332 ) 329 333 .into(), 330 334 } ··· 335 339 let resulting = Self { 336 340 start: self.start, 337 341 end: ( 338 - (self.end.0.saturating_add_signed(add_x as _)), 339 - (self.end.1.saturating_add_signed(add_y as _)), 342 + (self.end.x().saturating_add_signed(add_x as _)), 343 + (self.end.y().saturating_add_signed(add_y as _)), 340 344 ) 341 345 .into(), 342 346 }; ··· 358 362 } 359 363 360 364 pub fn x_range(&self) -> std::ops::RangeInclusive<usize> { 361 - self.start.0..=self.end.0 365 + self.start.x()..=self.end.x() 362 366 } 363 367 pub fn y_range(&self) -> std::ops::RangeInclusive<usize> { 364 - self.start.1..=self.end.1 368 + self.start.y()..=self.end.y() 365 369 } 366 370 367 371 pub fn x_range_without_last(&self) -> std::ops::Range<usize> { 368 - self.start.0..self.end.0 372 + self.start.x()..self.end.x() 369 373 } 370 374 371 375 pub fn y_range_without_last(&self) -> std::ops::Range<usize> { 372 - self.start.1..self.end.1 376 + self.start.y()..self.end.y() 373 377 } 374 378 375 379 pub fn within(&self, other: &Region) -> bool { 376 - self.start.0 >= other.start.0 377 - && self.start.1 >= other.start.1 378 - && self.end.0 <= other.end.0 379 - && self.end.1 <= other.end.1 380 + self.start.x() >= other.start.x() 381 + && self.start.y() >= other.start.y() 382 + && self.end.x() <= other.end.x() 383 + && self.end.y() <= other.end.y() 380 384 } 381 385 382 386 pub fn clamped(&self, within: &Region) -> Region { 383 387 Region { 384 388 start: ( 385 - self.start.0.max(within.start.0), 386 - self.start.1.max(within.start.1), 389 + self.start.x().max(within.start.x()), 390 + self.start.y().max(within.start.y()), 387 391 ) 388 392 .into(), 389 - end: (self.end.0.min(within.end.0), self.end.1.min(within.end.1)) 393 + end: ( 394 + self.end.x().min(within.end.x()), 395 + self.end.y().min(within.end.y()), 396 + ) 390 397 .into(), 391 398 } 392 399 } 393 400 394 401 pub fn width(&self) -> usize { 395 - if self.end.0 < self.start.0 { 402 + if self.end.x() < self.start.x() { 396 403 return 0; 397 404 } 398 405 399 - self.end.0 - self.start.0 + 1 406 + self.end.x() - self.start.x() + 1 400 407 } 401 408 402 409 pub fn height(&self) -> usize { 403 - let (Point(_, sy), Point(_, ey)) = (self.start, self.end); 410 + let (sy, ey) = (self.start.y(), self.end.y()); 404 411 405 412 if ey < sy { 406 413 return 0; ··· 441 448 Axis::Horizontal => ( 442 449 Region { 443 450 start: self.start, 444 - end: Point(self.end.0, self.end.1 / 2), 451 + end: Point::Corner(self.end.x(), self.end.y() / 2), 445 452 }, 446 453 Region { 447 - start: Point(self.start.0, self.end.1 / 2), 454 + start: Point::Corner(self.start.x(), self.end.y() / 2), 448 455 end: self.end, 449 456 }, 450 457 ), 451 458 Axis::Vertical => ( 452 459 Region { 453 460 start: self.start, 454 - end: Point(self.end.0 / 2, self.end.1), 461 + end: Point::Corner(self.end.x() / 2, self.end.y()), 455 462 }, 456 463 Region { 457 - start: Point(self.end.0 / 2, self.start.1), 464 + start: Point::Corner(self.end.x() / 2, self.start.y()), 458 465 end: self.end, 459 466 }, 460 467 ), ··· 468 475 469 476 impl Containable<Point> for Region { 470 477 fn contains(&self, value: &Point) -> bool { 471 - self.x_range().contains(&value.0) && self.y_range().contains(&value.1) 478 + self.x_range().contains(&value.x()) && self.y_range().contains(&value.y()) 472 479 } 473 480 } 474 481
+8 -6
src/graphics/canvas.rs
··· 95 95 pub fn set_grid_size(&mut self, new_width: usize, new_height: usize) { 96 96 self.grid_size = (new_width, new_height); 97 97 self.world_region = Region { 98 - start: Point(0, 0), 98 + start: Point::Corner(0, 0), 99 99 end: Point::from(self.grid_size).translated(-1, -1), 100 100 }; 101 101 } ··· 342 342 let world = self.world_region.clone(); 343 343 let layer = self.layer_or_empty("debug_plane"); 344 344 345 - let ymax = world.end.1 + 1; 346 - let xmax = world.end.0 + 1; 345 + let ymax = world.end.x() + 1; 346 + let xmax = world.end.y() + 1; 347 347 348 348 // Vertical lines 349 - for Point(x, y) in world.iter() { 349 + for point in world.iter() { 350 + let (x, y) = point.xy(); 351 + 350 352 layer.set( 351 353 format!("grid_vertical_{x}"), 352 - Object::Line(Point(x, 0), Point(x, ymax), 1.0) 354 + Object::Line(Point::Corner(x, 0), Point::Corner(x, ymax), 1.0) 353 355 .colored(color) 354 356 .opacified(0.25), 355 357 ); 356 358 357 359 layer.set( 358 360 format!("grid_horizontal_{y}"), 359 - Object::Line(Point(0, y), Point(xmax, y), 1.0) 361 + Object::Line(Point::Corner(0, y), Point::Corner(xmax, y), 1.0) 360 362 .colored(color) 361 363 .opacified(0.25), 362 364 );
+18
src/graphics/layer.rs
··· 110 110 self.add(format!("anon-{}", self.objects.len()), object); 111 111 } 112 112 113 + pub fn add_many( 114 + &mut self, 115 + objects: impl IntoIterator<Item = (impl Display, ColoredObject)>, 116 + ) { 117 + for (name, obj) in objects { 118 + self.add(name, obj); 119 + } 120 + } 121 + 122 + pub fn add_many_anon( 123 + &mut self, 124 + objects: impl IntoIterator<Item = ColoredObject>, 125 + ) { 126 + for obj in objects { 127 + self.add_anon(obj); 128 + } 129 + } 130 + 113 131 pub fn set(&mut self, name: impl Display, object: impl Into<ColoredObject>) { 114 132 let name_str = format!("{}", name); 115 133
+8 -6
src/graphics/objects.rs
··· 270 270 } 271 271 272 272 pub fn teleport(&mut self, x: i32, y: i32) { 273 - let Point(current_x, current_y) = self.region().start; 274 - let delta_x = x - current_x as i32; 275 - let delta_y = y - current_y as i32; 273 + let (current_x, current_y) = self.region().start.xy::<i32>(); 274 + let delta_x = x - current_x; 275 + let delta_y = y - current_y; 276 276 self.translate(delta_x, delta_y); 277 277 } 278 278 ··· 301 301 // println!("region for {:?} -> {}", self, region); 302 302 region 303 303 } 304 - Object::Line(Point(x1, y1), Point(x2, y2), _) 305 - | Object::CurveInward(Point(x1, y1), Point(x2, y2), _) 306 - | Object::CurveOutward(Point(x1, y1), Point(x2, y2), _) => { 304 + Object::Line(s, e, _) 305 + | Object::CurveInward(s, e, _) 306 + | Object::CurveOutward(s, e, _) => { 307 + let (x1, y1, x2, y2) = (s.x(), s.y(), e.y(), e.x()); 308 + 307 309 let region = Region::new( 308 310 (x1.min(x2), y1.min(y2)), 309 311 (x1.max(x2), y1.max(y2)),
+3 -1
src/lib.rs
··· 26 26 #[cfg(feature = "vst")] 27 27 pub mod vst; 28 28 29 - pub use geometry::{Angle, Axis, Containable, Point, Region}; 29 + pub use geometry::{ 30 + Angle, Axis, CenterPoint, Containable, CornerPoint, Norm, Point, Region, 31 + }; 30 32 pub use graphics::{ 31 33 Canvas, Color, Color::*, ColorMapping, ColoredObject, Fill, FillOperations, 32 34 Filter, FilterType, Layer, LineSegment, Object, Object::*, ObjectSizes,
+9 -1
src/random/point.rs
··· 5 5 impl Point { 6 6 pub fn random(rng: &mut impl Rng, within: &Region) -> Self { 7 7 within.ensure_nonempty().unwrap(); 8 - Self( 8 + Self::Corner( 9 + rng.random_range(within.x_range()), 10 + rng.random_range(within.y_range()), 11 + ) 12 + } 13 + 14 + pub fn random_center(rng: &mut impl Rng, within: &Region) -> Self { 15 + within.ensure_nonempty().unwrap(); 16 + Self::Center( 9 17 rng.random_range(within.x_range()), 10 18 rng.random_range(within.y_range()), 11 19 )
+2 -2
src/random/region.rs
··· 35 35 pub fn random(rng: &mut impl Rng, within: &Region) -> Self { 36 36 let start = Point::random(rng, within); 37 37 let end = within.random_end(rng, start); 38 - Region::from(if start.0 > end.0 { 38 + Region::from(if start.x() > end.x() { 39 39 (end, start) 40 40 } else { 41 41 (start, end) ··· 71 71 return Err(rand::distr::uniform::Error::EmptyRange); 72 72 } 73 73 74 - Ok(Point( 74 + Ok(Point::Corner( 75 75 rng.random_range(self.x_range()), 76 76 rng.random_range(self.y_range()), 77 77 ))
+9 -6
src/rendering/objects.rs
··· 156 156 let centered = matches!(self, Object::CenteredText(..)); 157 157 158 158 svg::tag("text") 159 - .coords(if centered { 160 - position.center_coords(cell_size) 161 - } else { 162 - position.coords(cell_size) 163 - }) 159 + .coords( 160 + if centered { 161 + position.as_centered() 162 + } else { 163 + position.as_corner() 164 + } 165 + .coords(cell_size), 166 + ) 164 167 .attr("font-size", format!("{}pt", font_size)) 165 168 .attr("font-family", "Inconsolata") 166 169 .attr( ··· 324 327 let center = match self { 325 328 Object::BigDot(at) | Object::Dot(at) => at.coords(cell_size), 326 329 Object::BigCircle(at) | Object::SmallCircle(at) => { 327 - at.center_coords(cell_size) 330 + at.as_centered().coords(cell_size) 328 331 } 329 332 330 333 _ => panic!(