@jbrowse/plugin-circular-view 2.5.0 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/BaseChordDisplay/components/BaseChordDisplay.d.ts +1 -1
  2. package/dist/BaseChordDisplay/components/BaseChordDisplay.js +0 -1
  3. package/dist/BaseChordDisplay/components/DisplayError.d.ts +2 -1
  4. package/dist/BaseChordDisplay/components/DisplayError.js +0 -1
  5. package/dist/BaseChordDisplay/components/Loading.d.ts +2 -1
  6. package/dist/BaseChordDisplay/components/Loading.js +0 -1
  7. package/dist/BaseChordDisplay/index.js +0 -1
  8. package/dist/BaseChordDisplay/models/configSchema.js +0 -1
  9. package/dist/BaseChordDisplay/models/model.d.ts +8 -2
  10. package/dist/BaseChordDisplay/models/model.js +0 -1
  11. package/dist/BaseChordDisplay/models/renderReaction.js +0 -1
  12. package/dist/CircularView/components/CircularView.d.ts +2 -1
  13. package/dist/CircularView/components/CircularView.js +0 -1
  14. package/dist/CircularView/components/Controls.d.ts +2 -1
  15. package/dist/CircularView/components/Controls.js +0 -1
  16. package/dist/CircularView/components/ExportSvgDialog.d.ts +2 -1
  17. package/dist/CircularView/components/ExportSvgDialog.js +0 -1
  18. package/dist/CircularView/components/ImportForm.d.ts +2 -1
  19. package/dist/CircularView/components/ImportForm.js +0 -1
  20. package/dist/CircularView/components/Ruler.d.ts +2 -1
  21. package/dist/CircularView/components/Ruler.js +0 -1
  22. package/dist/CircularView/index.js +0 -1
  23. package/dist/CircularView/models/CircularView.d.ts +1 -3
  24. package/dist/CircularView/models/CircularView.js +30 -7
  25. package/dist/CircularView/models/slices.js +0 -1
  26. package/dist/CircularView/models/viewportVisibleRegion.js +0 -1
  27. package/dist/CircularView/svgcomponents/SVGBackground.d.ts +2 -1
  28. package/dist/CircularView/svgcomponents/SVGBackground.js +0 -1
  29. package/dist/CircularView/svgcomponents/SVGCircularView.js +0 -1
  30. package/dist/LaunchCircularView/index.js +0 -1
  31. package/dist/index.js +0 -1
  32. package/esm/BaseChordDisplay/components/BaseChordDisplay.d.ts +1 -1
  33. package/esm/BaseChordDisplay/components/BaseChordDisplay.js +0 -1
  34. package/esm/BaseChordDisplay/components/DisplayError.d.ts +2 -1
  35. package/esm/BaseChordDisplay/components/DisplayError.js +0 -1
  36. package/esm/BaseChordDisplay/components/Loading.d.ts +2 -1
  37. package/esm/BaseChordDisplay/components/Loading.js +0 -1
  38. package/esm/BaseChordDisplay/index.js +0 -1
  39. package/esm/BaseChordDisplay/models/configSchema.js +0 -1
  40. package/esm/BaseChordDisplay/models/model.d.ts +8 -2
  41. package/esm/BaseChordDisplay/models/model.js +0 -1
  42. package/esm/BaseChordDisplay/models/renderReaction.js +0 -1
  43. package/esm/CircularView/components/CircularView.d.ts +2 -1
  44. package/esm/CircularView/components/CircularView.js +0 -1
  45. package/esm/CircularView/components/Controls.d.ts +2 -1
  46. package/esm/CircularView/components/Controls.js +0 -1
  47. package/esm/CircularView/components/ExportSvgDialog.d.ts +2 -1
  48. package/esm/CircularView/components/ExportSvgDialog.js +0 -1
  49. package/esm/CircularView/components/ImportForm.d.ts +2 -1
  50. package/esm/CircularView/components/ImportForm.js +0 -1
  51. package/esm/CircularView/components/Ruler.d.ts +2 -1
  52. package/esm/CircularView/components/Ruler.js +0 -1
  53. package/esm/CircularView/index.js +0 -1
  54. package/esm/CircularView/models/CircularView.d.ts +1 -3
  55. package/esm/CircularView/models/CircularView.js +6 -6
  56. package/esm/CircularView/models/slices.js +0 -1
  57. package/esm/CircularView/models/viewportVisibleRegion.js +0 -1
  58. package/esm/CircularView/svgcomponents/SVGBackground.d.ts +2 -1
  59. package/esm/CircularView/svgcomponents/SVGBackground.js +0 -1
  60. package/esm/CircularView/svgcomponents/SVGCircularView.js +0 -1
  61. package/esm/LaunchCircularView/index.js +0 -1
  62. package/esm/index.js +0 -1
  63. package/package.json +3 -4
  64. package/dist/BaseChordDisplay/components/BaseChordDisplay.js.map +0 -1
  65. package/dist/BaseChordDisplay/components/DisplayError.js.map +0 -1
  66. package/dist/BaseChordDisplay/components/Loading.js.map +0 -1
  67. package/dist/BaseChordDisplay/index.js.map +0 -1
  68. package/dist/BaseChordDisplay/models/configSchema.js.map +0 -1
  69. package/dist/BaseChordDisplay/models/model.js.map +0 -1
  70. package/dist/BaseChordDisplay/models/renderReaction.js.map +0 -1
  71. package/dist/CircularView/components/CircularView.js.map +0 -1
  72. package/dist/CircularView/components/Controls.js.map +0 -1
  73. package/dist/CircularView/components/ExportSvgDialog.js.map +0 -1
  74. package/dist/CircularView/components/ImportForm.js.map +0 -1
  75. package/dist/CircularView/components/Ruler.js.map +0 -1
  76. package/dist/CircularView/index.js.map +0 -1
  77. package/dist/CircularView/models/CircularView.js.map +0 -1
  78. package/dist/CircularView/models/slices.js.map +0 -1
  79. package/dist/CircularView/models/viewportVisibleRegion.js.map +0 -1
  80. package/dist/CircularView/svgcomponents/SVGBackground.js.map +0 -1
  81. package/dist/CircularView/svgcomponents/SVGCircularView.js.map +0 -1
  82. package/dist/LaunchCircularView/index.js.map +0 -1
  83. package/dist/index.js.map +0 -1
  84. package/esm/BaseChordDisplay/components/BaseChordDisplay.js.map +0 -1
  85. package/esm/BaseChordDisplay/components/DisplayError.js.map +0 -1
  86. package/esm/BaseChordDisplay/components/Loading.js.map +0 -1
  87. package/esm/BaseChordDisplay/index.js.map +0 -1
  88. package/esm/BaseChordDisplay/models/configSchema.js.map +0 -1
  89. package/esm/BaseChordDisplay/models/model.js.map +0 -1
  90. package/esm/BaseChordDisplay/models/renderReaction.js.map +0 -1
  91. package/esm/CircularView/components/CircularView.js.map +0 -1
  92. package/esm/CircularView/components/Controls.js.map +0 -1
  93. package/esm/CircularView/components/ExportSvgDialog.js.map +0 -1
  94. package/esm/CircularView/components/ImportForm.js.map +0 -1
  95. package/esm/CircularView/components/Ruler.js.map +0 -1
  96. package/esm/CircularView/index.js.map +0 -1
  97. package/esm/CircularView/models/CircularView.js.map +0 -1
  98. package/esm/CircularView/models/slices.js.map +0 -1
  99. package/esm/CircularView/models/viewportVisibleRegion.js.map +0 -1
  100. package/esm/CircularView/svgcomponents/SVGBackground.js.map +0 -1
  101. package/esm/CircularView/svgcomponents/SVGCircularView.js.map +0 -1
  102. package/esm/LaunchCircularView/index.js.map +0 -1
  103. package/esm/index.js.map +0 -1
  104. package/src/BaseChordDisplay/components/BaseChordDisplay.tsx +0 -26
  105. package/src/BaseChordDisplay/components/DisplayError.tsx +0 -47
  106. package/src/BaseChordDisplay/components/Loading.tsx +0 -111
  107. package/src/BaseChordDisplay/index.ts +0 -3
  108. package/src/BaseChordDisplay/models/configSchema.ts +0 -30
  109. package/src/BaseChordDisplay/models/model.tsx +0 -314
  110. package/src/BaseChordDisplay/models/renderReaction.ts +0 -75
  111. package/src/CircularView/components/CircularView.tsx +0 -128
  112. package/src/CircularView/components/Controls.tsx +0 -119
  113. package/src/CircularView/components/ExportSvgDialog.tsx +0 -132
  114. package/src/CircularView/components/ImportForm.tsx +0 -68
  115. package/src/CircularView/components/Ruler.tsx +0 -265
  116. package/src/CircularView/index.ts +0 -16
  117. package/src/CircularView/models/CircularView.ts +0 -620
  118. package/src/CircularView/models/__snapshots__/slices.test.js.snap +0 -91
  119. package/src/CircularView/models/slices.test.js +0 -70
  120. package/src/CircularView/models/slices.ts +0 -101
  121. package/src/CircularView/models/viewportVisibleRegion.test.js +0 -168
  122. package/src/CircularView/models/viewportVisibleRegion.ts +0 -272
  123. package/src/CircularView/svgcomponents/SVGBackground.tsx +0 -21
  124. package/src/CircularView/svgcomponents/SVGCircularView.tsx +0 -58
  125. package/src/LaunchCircularView/index.ts +0 -48
  126. package/src/index.ts +0 -43
@@ -1,70 +0,0 @@
1
- import { calculateStaticSlices } from './slices'
2
-
3
- test('one slice', () => {
4
- const view = {
5
- elidedRegions: [{ refName: 'toast', start: 0, end: 10000, widthBp: 10000 }],
6
- spacingPx: 5,
7
- radiusPx: 1000,
8
- totalBp: 10000,
9
- bpPerRadian: 10000 / (2 * Math.PI),
10
- pxPerRadian: 1000,
11
- }
12
-
13
- const slices = calculateStaticSlices(view)
14
- // console.log(slices)
15
- expect(slices.length).toBe(1)
16
- const [slice] = slices
17
- // expect(slices).toMatchSnapshot()
18
- expect(slice).toMatchSnapshot()
19
- })
20
-
21
- test('two slices', () => {
22
- const view = {
23
- elidedRegions: [
24
- { refName: 'toast', start: 0, end: 10000, widthBp: 10000 },
25
- { refName: 'teest', start: 0, end: 10000, widthBp: 10000 },
26
- ],
27
- spacingPx: 5,
28
- radiusPx: 1000,
29
- pxPerRadian: 1000,
30
- totalBp: 20000,
31
- bpPerRadian: 20000 / (2 * Math.PI),
32
- }
33
-
34
- const slices = calculateStaticSlices(view)
35
- // console.log(slices)
36
- expect(slices.length).toBe(2)
37
- expect(slices).toMatchSnapshot()
38
- })
39
-
40
- test('volvox', () => {
41
- const totalBp = 50001 + 6079
42
- const view = {
43
- elidedRegions: [
44
- {
45
- refName: 'ctgA',
46
- start: 0,
47
- end: 50001,
48
- assemblyName: 'volvox',
49
- widthBp: 50001,
50
- },
51
- {
52
- refName: 'ctgB',
53
- start: 0,
54
- end: 6079,
55
- assemblyName: 'volvox',
56
- widthBp: 6079,
57
- },
58
- ],
59
- spacingPx: 5,
60
- radiusPx: 1000,
61
- pxPerRadian: 1000,
62
- totalBp,
63
- bpPerRadian: totalBp / (2 * Math.PI),
64
- }
65
-
66
- const slices = calculateStaticSlices(view)
67
- // console.log(slices)
68
- expect(slices.length).toBe(2)
69
- expect(slices).toMatchSnapshot()
70
- })
@@ -1,101 +0,0 @@
1
- import { polarToCartesian, assembleLocString, Region } from '@jbrowse/core/util'
2
- import { thetaRangesOverlap } from './viewportVisibleRegion'
3
-
4
- export type SliceElidedRegion = {
5
- elided: true
6
- widthBp: number
7
- regions: Region[]
8
- }
9
-
10
- export type SliceNonElidedRegion = {
11
- elided: false
12
- widthBp: number
13
- start: number
14
- end: number
15
- refName: string
16
- assemblyName: string
17
- }
18
- export type SliceRegion = SliceNonElidedRegion | SliceElidedRegion
19
-
20
- export class Slice {
21
- key: string
22
-
23
- startRadians: number
24
-
25
- endRadians: number
26
-
27
- bpPerRadian: number
28
-
29
- flipped: boolean
30
-
31
- constructor(
32
- view: { bpPerRadian: number },
33
- public region: SliceRegion,
34
- public offsetRadians: number,
35
- public radianWidth: number,
36
- ) {
37
- const { bpPerRadian } = view
38
- this.key =
39
- 'regions' in region
40
- ? JSON.stringify(region.regions)
41
- : assembleLocString(region)
42
- this.bpPerRadian = bpPerRadian
43
- this.flipped = false
44
-
45
- this.startRadians = offsetRadians
46
- this.endRadians = region.widthBp / this.bpPerRadian + offsetRadians
47
- Object.freeze(this)
48
- }
49
-
50
- bpToXY(bp: number, radiusPx: number) {
51
- let offsetBp
52
- if (this.region.elided) {
53
- offsetBp = this.region.widthBp / 2
54
- } else if (this.flipped) {
55
- offsetBp = this.region.end - bp
56
- } else {
57
- offsetBp = bp - this.region.start
58
- }
59
- const totalRadians = offsetBp / this.bpPerRadian + this.offsetRadians
60
- return polarToCartesian(radiusPx, totalRadians)
61
- }
62
-
63
- toJSON() {
64
- return Object.fromEntries(Object.entries(this))
65
- }
66
- }
67
-
68
- function calculateStaticSlices(self: {
69
- elidedRegions: SliceRegion[]
70
- bpPerRadian: number
71
- spacingPx: number
72
- pxPerRadian: number
73
- }) {
74
- const slices = []
75
- let currentRadianOffset = 0
76
- const { bpPerRadian, spacingPx, pxPerRadian } = self
77
- for (const region of self.elidedRegions) {
78
- const radianWidth = region.widthBp / bpPerRadian + spacingPx / pxPerRadian
79
- slices.push(new Slice(self, region, currentRadianOffset, radianWidth))
80
- currentRadianOffset += radianWidth
81
- }
82
- return slices
83
- }
84
-
85
- function sliceIsVisible(
86
- self: { offsetRadians: number; visibleSection: { theta: [number, number] } },
87
- slice: Slice,
88
- ) {
89
- const {
90
- theta: [visibleThetaMin, visibleThetaMax],
91
- } = self.visibleSection
92
-
93
- return thetaRangesOverlap(
94
- slice.offsetRadians + self.offsetRadians,
95
- slice.radianWidth,
96
- visibleThetaMin,
97
- visibleThetaMax - visibleThetaMin,
98
- )
99
- }
100
-
101
- export { calculateStaticSlices, sliceIsVisible }
@@ -1,168 +0,0 @@
1
- import {
2
- viewportVisibleSection,
3
- cartesianToPolar,
4
- thetaRangesOverlap,
5
- } from './viewportVisibleRegion'
6
-
7
- describe('viewportVisibleSection', () => {
8
- // test('circle contained in viewport', () => {
9
- // const result = viewportVisibleSection([0, 1, 0, 1], [0.5, 0.5], 0.3)
10
- // expect(result).toEqual({ rho: [0, 0.3], theta: [0, 2 * Math.PI] })
11
- // })
12
-
13
- // test('viewport completely inside circle', () => {
14
- // const result = viewportVisibleSection([0, 1, 0, 1], [0.5, 0.5], 20)
15
- // expect(result.theta).toEqual([0, 2 * Math.PI])
16
- // expect(result.rho[0]).toEqual(0)
17
- // expect(result.rho[1]).toBeCloseTo(0.7071)
18
- // })
19
-
20
- // test('viewport on left half of circle', () => {
21
- // const result = viewportVisibleSection([200, 500, 0, 1000], [500, 500], 20)
22
- // expect(result).toEqual({
23
- // rho: [0, 20],
24
- // theta: [Math.PI / 2, 1.5 * Math.PI],
25
- // })
26
- // })
27
- // test('viewport on right half of circle', () => {
28
- // const result = viewportVisibleSection([200, 500, 0, 1000], [200, 500], 20)
29
- // expect(result).toEqual({
30
- // rho: [0, 20],
31
- // theta: [1.5 * Math.PI, 2.5 * Math.PI],
32
- // })
33
- // })
34
-
35
- // test('viewport corner in circle', () => {
36
- // const { theta, rho } = viewportVisibleSection(
37
- // [200, 500, 0, 700],
38
- // [199, 701],
39
- // 100,
40
- // )
41
- // expect(rho).toEqual([1.4142135623730951, 100])
42
- // expect(theta[0]).toBeCloseTo(1.5 * Math.PI, 1)
43
- // expect(theta[1]).toBeCloseTo(2 * Math.PI, 1)
44
- // })
45
-
46
- // test('viewport on center right', () => {
47
- // const { theta, rho } = viewportVisibleSection(
48
- // [1102, 2153, 880, 1280],
49
- // [1068.8119697255406, 1068.8119697255406],
50
- // 1048.8119697255406,
51
- // )
52
- // expect(theta[1]).toBeGreaterThan(2 * Math.PI)
53
- // expect(theta[1]).toBeLessThan(2.5 * Math.PI)
54
- // expect(theta[0]).toBeGreaterThan(1.5 * Math.PI)
55
- // expect(theta[0]).toBeLessThan(2 * Math.PI)
56
- // })
57
-
58
- // test('viewport on center right 2', () => {
59
- // const { theta, rho } = viewportVisibleSection(
60
- // [1816, 2937, 1074, 1474],
61
- // [1468.6015446723616, 1468.6015446723616],
62
- // 1448.6015446723616,
63
- // )
64
- // expect(theta[1]).toBeGreaterThan(2 * Math.PI)
65
- // expect(theta[1]).toBeLessThan(2.5 * Math.PI)
66
- // expect(theta[0]).toBeGreaterThan(1.5 * Math.PI)
67
- // expect(theta[0]).toBeLessThan(2 * Math.PI)
68
- // })
69
-
70
- // test('viewport on lower center', () => {
71
- // const { theta, rho } = viewportVisibleSection(
72
- // [259, 1350, 1176, 1576],
73
- // [787.7952717090081, 787.7952717090081],
74
- // 767.7952717090081,
75
- // )
76
- // expect(theta[1]).toBeGreaterThan(Math.PI / 2)
77
- // expect(theta[1]).toBeLessThan(Math.PI)
78
- // expect(theta[0]).toBeGreaterThan(0)
79
- // expect(theta[0]).toBeLessThan(Math.PI / 2)
80
- // })
81
-
82
- // test('viewport on upper center', () => {
83
- // const { theta, rho } = viewportVisibleSection(
84
- // [286, 1377, 0, 400],
85
- // [787.7952717090081, 787.7952717090081],
86
- // 767.7952717090081,
87
- // )
88
- // expect(theta[1]).toBeGreaterThan(1.5 * Math.PI)
89
- // expect(theta[1]).toBeLessThan(2 * Math.PI)
90
- // expect(theta[0]).toBeGreaterThan(Math.PI)
91
- // expect(theta[0]).toBeLessThan(1.5 * Math.PI)
92
- // })
93
-
94
- test('viewport on upper center 2', () => {
95
- // [180.48708681644143, 359.3411680673888] [4.6042679453532855, 541.6042679453533]
96
- // see '~/Desktop/Screen Shot 2019-06-28 at 3.01.22 PM.png'
97
- const { theta } = viewportVisibleSection(
98
- [0, 962, 157, 557],
99
- [561.6042679453533, 561.6042679453533],
100
- 541.6042679453533,
101
- )
102
- expect(theta[1]).toBeGreaterThan(1.75 * Math.PI)
103
- expect(theta[1]).toBeLessThan(2 * Math.PI)
104
- expect(theta[0]).toBeGreaterThan(Math.PI)
105
- expect(theta[0]).toBeLessThan(1.1 * Math.PI)
106
- })
107
- })
108
-
109
- describe('cartesian to polar', () => {
110
- ;[
111
- [
112
- [-1, -1],
113
- [1.414, 180 + 45],
114
- ],
115
- [
116
- [-1, 1],
117
- [1.414, 90 + 45],
118
- ],
119
- [
120
- [1, 1],
121
- [1.414, 45],
122
- ],
123
- [
124
- [1, -1],
125
- [1.414, 360 - 45],
126
- ],
127
- [
128
- [0, 1],
129
- [1, 90],
130
- ],
131
- [
132
- [0, -1],
133
- [1, 270],
134
- ],
135
- [
136
- [-1, 0],
137
- [1, 180],
138
- ],
139
- [
140
- [1, 0],
141
- [1, 0],
142
- ],
143
- ].forEach(testCase => {
144
- const [input, output] = testCase
145
- test(`${input} -> ${output}`, () => {
146
- const result = cartesianToPolar(...input)
147
- expect(result[0]).toBeCloseTo(output[0])
148
- expect((result[1] * 180) / Math.PI).toBeCloseTo(output[1])
149
- })
150
- })
151
- })
152
-
153
- describe('theta overlap testing', () => {
154
- ;[
155
- [[0, 2 * Math.PI, 0, 2 * Math.PI], true],
156
- [[6.1, Math.PI / 2, 0, Math.PI / 2], true],
157
- [[6.1, Math.PI / 2, 0, 0.1], true],
158
- [[6.1, 0.1, 6.12, 0.05], true],
159
- [[-12, 0.1, -12.05, 0.05], false],
160
- [[-12, 0.1, -12.05, 0.06], true],
161
- ].forEach(testCase => {
162
- const [input, output] = testCase
163
- test(`${input} -> ${output}`, () => {
164
- const result = thetaRangesOverlap(...input)
165
- expect(result).toBe(output)
166
- })
167
- })
168
- })
@@ -1,272 +0,0 @@
1
- function findCircleIntersectionX(
2
- y: number,
3
- cx: number,
4
- cy: number,
5
- r: number,
6
- resultArray: [number, number][],
7
- ) {
8
- const d = Math.abs(y - cy)
9
- if (d > r) {
10
- return
11
- }
12
- if (d === r) {
13
- resultArray.push([cx, y])
14
- }
15
- const solution = Math.sqrt(r * r - d * d)
16
- resultArray.push([cx - solution, y], [cx + solution, y])
17
- }
18
-
19
- function findCircleIntersectionY(
20
- x: number,
21
- cx: number,
22
- cy: number,
23
- r: number,
24
- resultArray: [number, number][],
25
- ) {
26
- const d = Math.abs(x - cx)
27
- if (d > r) {
28
- return
29
- }
30
- if (d === r) {
31
- resultArray.push([x, cy])
32
- }
33
- const solution = Math.sqrt(r * r - d * d)
34
- resultArray.push([x, cy - solution], [x, cy + solution])
35
- }
36
-
37
- function cartesianToTheta(x: number, y: number) {
38
- let theta = (Math.atan(y / x) + 2 * Math.PI) % (2 * Math.PI)
39
- if (x < 0) {
40
- if (y <= 0) {
41
- theta += Math.PI
42
- } else {
43
- theta -= Math.PI
44
- }
45
- }
46
- return theta
47
- }
48
-
49
- export function cartesianToPolar(x: number, y: number) {
50
- const rho = Math.sqrt(x * x + y * y)
51
- if (rho === 0) {
52
- return [0, 0]
53
- }
54
- const theta = cartesianToTheta(x, y)
55
- return [rho, theta]
56
- }
57
-
58
- const twoPi = 2 * Math.PI
59
- export function thetaRangesOverlap(
60
- r1start: number,
61
- r1length: number,
62
- r2start: number,
63
- r2length: number,
64
- ) {
65
- if (r1length <= 0 || r2length <= 0) {
66
- return false
67
- }
68
- if (r1length + 0.0001 >= twoPi || r2length + 0.0001 >= twoPi) {
69
- return true
70
- }
71
-
72
- // put both range starts between 2π and 4π
73
- r1start = (((r1start % twoPi) + twoPi) % twoPi) + twoPi
74
- r2start = (((r2start % twoPi) + twoPi) % twoPi) + twoPi
75
-
76
- if (r1start < r2start + r2length && r1start + r1length > r2start) {
77
- return true
78
- }
79
-
80
- // move r2 2π to the left and check
81
- r2start -= twoPi
82
- if (r1start < r2start + r2length && r1start + r1length > r2start) {
83
- return true
84
- }
85
-
86
- // move it 2π to the right and check
87
- r2start += twoPi + twoPi
88
- return r1start < r2start + r2length && r1start + r1length > r2start
89
- }
90
-
91
- // return which arc range has any part of the circle visible in the viewport
92
- export function viewportVisibleSection(
93
- viewSides: [number, number, number, number],
94
- circleCenter: [number, number],
95
- circleRadius: number,
96
- ) {
97
- let [viewL, viewR, viewT, viewB] = viewSides
98
- const [cx, cy] = circleCenter
99
-
100
- // transform coordinate system to center of circle
101
- viewL -= cx
102
- viewR -= cx
103
- viewT -= cy
104
- viewB -= cy
105
-
106
- const centerIsInsideViewport =
107
- viewL < 0 && viewR > 0 && viewT < 0 && viewB > 0
108
-
109
- if (centerIsInsideViewport) {
110
- const vertices = [
111
- [viewL, viewT],
112
- [viewR, viewT],
113
- [viewL, viewB],
114
- [viewR, viewB],
115
- ]
116
- let maxRho = -Infinity
117
- for (let i = 0; i < vertices.length; i += 1) {
118
- const [x, y] = vertices[i]
119
- const rho = Math.sqrt(x * x + y * y)
120
- if (rho > maxRho) {
121
- maxRho = rho
122
- }
123
- }
124
- return {
125
- rho: [0, Math.min(circleRadius, maxRho)] as [number, number],
126
- theta: [0, 2 * Math.PI] as [number, number],
127
- }
128
- }
129
- // const viewportCompletelyContainsCircle =
130
- // circleCenter[0] - viewL >= circleRadius &&
131
- // viewR - circleCenter[0] >= circleRadius &&
132
- // circleCenter[1] - viewT >= circleRadius &&
133
- // viewB - circleCenter[1] >= circleRadius
134
-
135
- // if (viewportCompletelyContainsCircle) {
136
- // return [0, 2 * Math.PI]
137
- // }
138
-
139
- // const distToCenterSquared = ([x, y]) => {
140
- // const [cx, cy] = circleCenter
141
- // const sq = n => n * n
142
- // return sq(x - cx) + sq(y - cy)
143
- // }
144
- // const circleRadiusSquared = circleRadius * circleRadius
145
-
146
- // const tlInside = distToCenterSquared([viewL, viewT]) <= circleRadiusSquared
147
- // const trInside = distToCenterSquared([viewR, viewT]) <= circleRadiusSquared
148
- // const blInside = distToCenterSquared([viewL, viewB]) <= circleRadiusSquared
149
- // const brInside = distToCenterSquared([viewR, viewB]) <= circleRadiusSquared
150
-
151
- // const noIntersection = !tlInside && !trInside && !blInside && !brInside
152
- // if (noIntersection) return undefined
153
-
154
- // const circleCompletelyContainsViewport =
155
- // tlInside && trInside && blInside && brInside
156
- // if (circleCompletelyContainsViewport) {
157
- // // viewport is in the circle, but the center is not in it, so take max
158
- // // and min of thetas to the center
159
- // const thetas = [
160
- // Math.atan(viewT / viewL),
161
- // Math.atan(viewT / viewR),
162
- // Math.atan(viewB / viewL),
163
- // Math.atan(viewB / viewR),
164
- // ]
165
-
166
- // return [Math.min(...thetas), Math.max(...thetas)]
167
- // }
168
-
169
- // if we get here, the viewport is partly in, partly out of the circle
170
-
171
- // const viewLIntersects = Math.abs(viewL - circleCenter[0]) <= circleRadius
172
- // const viewRIntersects = Math.abs(viewR - circleCenter[0]) <= circleRadius
173
- // const viewTIntersects = Math.abs(viewT - circleCenter[1]) <= circleRadius
174
- // const viewBIntersects = Math.abs(viewB - circleCenter[1]) <= circleRadius
175
-
176
- // const numIntersectingSides =
177
- // Number(viewLIntersects) +
178
- // Number(viewRIntersects) +
179
- // Number(viewTIntersects) +
180
- // Number(viewBIntersects)
181
-
182
- // if (numIntersectingSides === 4) return [0, 2 * Math.PI]
183
- // if (numIntersectingSides === 3) {
184
- // // TODO calculate the thetas of the
185
- // } else if (numIntersectingSides === 2) {
186
- // // TODO calculate the thetas of the 2 intersection points
187
- // } else if (numIntersectingSides === 1) {
188
- // // TODO calculate the thetas of the 1-2 intersection points of the line, and the angle between
189
- // }
190
-
191
- // make a list of vertices-of-interest that lie inside both shapes to examine
192
- // to determine the range covered by their intersection
193
-
194
- // transform coordinates to have the circle as the origin and find the intersections
195
- // of the circle and the view rectangle
196
- const vertices: [number, number][] = [
197
- [viewL, viewT],
198
- [viewR, viewT],
199
- [viewL, viewB],
200
- [viewR, viewB],
201
- ]
202
- findCircleIntersectionY(viewL, 0, 0, circleRadius, vertices)
203
- findCircleIntersectionY(viewR, 0, 0, circleRadius, vertices)
204
- findCircleIntersectionX(viewT, 0, 0, circleRadius, vertices)
205
- findCircleIntersectionX(viewB, 0, 0, circleRadius, vertices)
206
-
207
- // for each edge, also look at the closest point to center if it is inside the circle
208
- if (-viewL < circleRadius) {
209
- vertices.push([viewL, 0])
210
- }
211
- if (viewR < circleRadius) {
212
- vertices.push([viewR, 0])
213
- }
214
- if (-viewT < circleRadius) {
215
- vertices.push([0, viewT])
216
- }
217
- if (viewB < circleRadius) {
218
- vertices.push([0, viewB])
219
- }
220
-
221
- // const verticesOriginal = vertices.map(([x, y]) => [x + cx, y + cy])
222
-
223
- // now convert them all to polar and take the max and min of rho and theta
224
-
225
- // const viewportCenterTheta = cartesianToTheta(viewR + viewL, viewT + viewB)
226
- const reflect = viewL >= 0 ? -1 : 1
227
- // viewportCenterTheta < Math.PI / 2 || viewportCenterTheta > 1.5 * Math.PI
228
- // ? -1
229
- // : 1
230
- let rhoMin = Infinity
231
- let rhoMax = -Infinity
232
- let thetaMin = Infinity
233
- let thetaMax = -Infinity
234
- for (let i = 0; i < vertices.length; i += 1) {
235
- // ignore vertex if outside the viewport
236
- const [vx, vy] = vertices[i]
237
- if (vx >= viewL && vx <= viewR && vy >= viewT && vy <= viewB) {
238
- const [rho, theta] = cartesianToPolar(vx * reflect, vy * reflect)
239
- // ignore vertex if outside the circle
240
- if (rho <= circleRadius + 0.001) {
241
- // ignore theta if rho is 0
242
- if (theta < thetaMin && rho > 0.0001) {
243
- thetaMin = theta
244
- }
245
- if (theta > thetaMax && rho > 0.0001) {
246
- thetaMax = theta
247
- }
248
- if (rho < rhoMin) {
249
- rhoMin = rho
250
- }
251
- if (rho > rhoMax) {
252
- rhoMax = rho
253
- }
254
- }
255
- }
256
- }
257
-
258
- if (reflect === -1) {
259
- thetaMin += Math.PI
260
- thetaMax += Math.PI
261
- }
262
-
263
- if (thetaMin > 2 * Math.PI && thetaMax > 2 * Math.PI) {
264
- thetaMin -= 2 * Math.PI
265
- thetaMax -= 2 * Math.PI
266
- }
267
-
268
- return {
269
- rho: [rhoMin, Math.min(circleRadius, rhoMax)] as [number, number],
270
- theta: [thetaMin, thetaMax] as [number, number],
271
- }
272
- }
@@ -1,21 +0,0 @@
1
- import React from 'react'
2
- import { useTheme } from '@mui/material'
3
-
4
- export default function SVGBackground({
5
- width,
6
- height,
7
- shift,
8
- }: {
9
- width: number
10
- height: number
11
- shift: number
12
- }) {
13
- const theme = useTheme()
14
- return (
15
- <rect
16
- width={width + shift * 2}
17
- height={height}
18
- fill={theme.palette.background.default}
19
- />
20
- )
21
- }
@@ -1,58 +0,0 @@
1
- import React from 'react'
2
- import { ThemeProvider } from '@mui/material'
3
- import { renderToStaticMarkup } from 'react-dom/server'
4
- import { when } from 'mobx'
5
- import { getSession, radToDeg } from '@jbrowse/core/util'
6
- import { createJBrowseTheme } from '@jbrowse/core/ui'
7
-
8
- // locals
9
- import { ExportSvgOptions, CircularViewModel } from '../models/CircularView'
10
- import SVGBackground from './SVGBackground'
11
- import Ruler from '../components/Ruler'
12
-
13
- type CGV = CircularViewModel
14
-
15
- export async function renderToSvg(model: CGV, opts: ExportSvgOptions) {
16
- await when(() => model.initialized)
17
- const { themeName = 'default', Wrapper = ({ children }) => <>{children}</> } =
18
- opts
19
- const session = getSession(model)
20
- const theme = session.allThemes?.()[themeName]
21
- const { width, tracks, height } = model
22
- const shift = 50
23
- const displayResults = await Promise.all(
24
- tracks.map(async track => {
25
- const display = track.displays[0]
26
- await when(() => (display.ready !== undefined ? display.ready : true))
27
- return { track, result: await display.renderSvg({ ...opts, theme }) }
28
- }),
29
- )
30
-
31
- const { staticSlices, offsetRadians, centerXY } = model
32
- const deg = radToDeg(offsetRadians)
33
-
34
- // the xlink namespace is used for rendering <image> tag
35
- return renderToStaticMarkup(
36
- <ThemeProvider theme={createJBrowseTheme(theme)}>
37
- <Wrapper>
38
- <svg
39
- width={width}
40
- height={height}
41
- xmlns="http://www.w3.org/2000/svg"
42
- xmlnsXlink="http://www.w3.org/1999/xlink"
43
- viewBox={[0, 0, width + shift * 2, height].toString()}
44
- >
45
- <SVGBackground width={width} height={height} shift={shift} />
46
- <g transform={`translate(${centerXY}) rotate(${deg})`}>
47
- {staticSlices.map((slice, i) => (
48
- <Ruler key={i} model={model} slice={slice} />
49
- ))}
50
- {displayResults.map(({ result }, i) => (
51
- <React.Fragment key={i}>{result}</React.Fragment>
52
- ))}
53
- </g>
54
- </svg>
55
- </Wrapper>
56
- </ThemeProvider>,
57
- )
58
- }