├── README.md
├── css
└── styles.css
├── demo
├── css
│ └── styles.css
├── data
│ ├── aframe-data.json
│ └── contour-data.json
├── images
│ └── bg_light.png
├── index.html
└── js
│ ├── conrec.js
│ ├── main.js
│ └── textures.min.js
├── images
├── preview.png
└── wide.png
└── index.html
/README.md:
--------------------------------------------------------------------------------
1 | # Visualizing in VR using A‑Frame and D3
2 |
3 | I was interested in seeing what a compelling visualization in 3D, and by extension in VR, might look like. In this demo, one learns about various cities by exploring a terrain. The fake data is encoded using elevation at the city-level. Turning your head, which you can either do with a headset or by clicking and dragging your mouse, and looking at a mound of black sand provides some insight into that city.
4 |
5 | * [Post](http://almossawi.com/aframe-d3-visualization/)
6 | * [Demo](http://almossawi.com/aframe-d3-visualization/)
7 |
8 | 
9 |
--------------------------------------------------------------------------------
/css/styles.css:
--------------------------------------------------------------------------------
1 |
2 | .anchor i {
3 | color: #525151;
4 | font-size: 12px;
5 | }
6 |
7 | a, a:active, a:link, a:visited {
8 | color: #3b3b3b;
9 | text-decoration: underline;
10 | }
11 |
12 | .footer {
13 | font-size: 14px;
14 | font-style: italic;
15 | opacity: 0.6;
16 | }
17 |
18 | .high-top {
19 | padding-top: 70px;
20 | }
21 |
22 | html, body {
23 | background: #fff;
24 | font: 13px;
25 | font-family: 'Old Standard TT', 'PT Serif', Georgia, Times, 'Times New Roman', serif;
26 | margin: 0;
27 | padding: 0;
28 | }
29 |
30 | h1 {
31 | clear: both;
32 | color: black;
33 | font-size: 38px;
34 | font-style: italic;
35 | font-weight: 300;
36 | margin: 40px 0 40px 0;
37 | padding: 0;
38 | text-align: center;
39 | }
40 |
41 | .link {
42 | fill: none;
43 | stroke: #ccc;
44 | }
45 |
46 | .link-text {
47 | cursor: pointer;
48 | fill: #ccc;
49 | font-family: 'Open Sans', Arial, sans-serif;
50 | font-size: 11px;
51 | }
52 |
53 | .link-text.active {
54 | fill: #4e4e4e;
55 | }
56 |
57 | .link.active {
58 | stroke: #4e4e4e;
59 | }
60 |
61 | .main-container {
62 | margin: 0 auto 50px auto;
63 | max-width: 560px;
64 | width: 90%;
65 | }
66 |
67 | p {
68 | font-size: 16px;
69 | line-height: 30px;
70 | opacity: 0.9;
71 | text-indent: 50px;
72 | }
73 |
74 | p.no-indent {
75 | text-indent: 0;
76 | }
77 |
78 | sup, sub {
79 | font-size: 13px;
80 | position: relative;
81 | top: -0.4em;
82 | vertical-align: baseline;
83 | }
84 |
85 | sub {
86 | top: 0.4em;
87 | }
88 |
89 | .underline {
90 | border-bottom: 1px solid #ccc;
91 | }
92 |
--------------------------------------------------------------------------------
/demo/css/styles.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 14px;
3 | }
4 |
5 | body {
6 | background-color: #fff;
7 | }
8 |
9 | .loading {
10 | width: 100%;
11 | height: 100%;
12 | z-index: 999;
13 | opacity: 0;
14 | position: absolute;
15 | background-color: red;
16 | }
17 |
18 | .overlay {
19 | width: 100%;
20 | height: 100%;
21 | z-index: 1;
22 | background: url(../images/bg_light.png);
23 | opacity: 0;
24 | }
25 |
26 | .camera {
27 | right: 0;
28 | margin: 30px;
29 | color: #666;
30 | position: absolute;
31 | top: 0;
32 | z-index: 3;
33 | cursor: pointer;
34 | }
35 |
36 | a-scene {
37 | opacity: 0.9;
38 | z-index: 3;
39 | }
40 |
41 | p {
42 | font-family: 'Fira Sans';
43 | line-height: 24px;
44 | }
45 |
46 | .a-enter-vr {
47 | font-size: 0.8rem !important;
48 | }
49 |
50 | .a-enter-vr-modal p {
51 | line-height: 16px !important;
52 | font-size: 0.8rem !important;
53 | }
54 |
55 | .a-enter-vr-modal {
56 | height: 50px !important;
57 | }
58 |
59 | h1 {
60 | font-family: 'Fira Sans';
61 | font-size: 2.5rem;
62 | font-weight: 400;
63 | margin: 0;
64 | }
65 |
66 | .card {
67 | color: black;
68 | font-family: 'Fira Sans';
69 | margin: 50px 10px 0 40px;
70 | padding: 0 10px 0 10px;
71 | position: absolute;
72 | width: 90%;
73 | max-width: 450px;
74 | z-index: 3;
75 | text-shadow:
76 | -1px -1px 0 #e7e7e7,
77 | 1px -1px 0 #e7e7e7,
78 | -1px 1px 0 #e7e7e7,
79 | 1px 1px 0 #e7e7e7;
80 | }
81 |
82 | .contour-plot {
83 | border: 1px solid #1a1a1a;
84 | height: 316px;
85 | left: 0;
86 | margin: 10px;
87 | opacity: 0;
88 | overflow: hidden;
89 | position: absolute;
90 | top: 0;
91 | width: 211px;
92 | z-index: 3;
93 | }
94 |
95 | .isoline {
96 | fill: none;
97 | }
98 |
99 | g.contours path {
100 | fill: #16214c;
101 | stroke: #16214c;
102 | }
103 |
104 | g.map {
105 | opacity: 1;
106 | }
107 |
108 | g.map path {
109 | fill: #0d0d0d;
110 | stroke: black;
111 | stroke-width: 0.1;
112 | }
113 |
114 | rect.back {
115 | opacity: 0.3;
116 | }
117 |
118 | .arrow {
119 | transform-origin: 50% 50%;
120 | }
121 |
122 | .key {
123 | font-family: Monaco, 'Lucida Console', 'Fira Sans';
124 | background-color: #fff;
125 | border: 1px solid #ccc;
126 | border-radius: 4px;
127 | color: #585757;
128 | padding: 3px 4px 3px 4px;
129 | font-size: 0.8rem;
130 | text-shadow: none;
131 | }
132 |
133 | .hide {
134 | display: none !important;
135 | }
136 |
137 | @media screen and (min-width: 0px) and (max-width: 720px) {
138 | .hide-on-mobile {
139 | display: none;
140 | }
141 |
142 | h1 {
143 | font-size: 1.8rem;
144 | }
145 |
146 | .card {
147 | font-size: 0.8rem;
148 | margin: 10px 0 0 0;
149 | }
150 |
151 | p {
152 | line-height: 22px;
153 | }
154 | }
--------------------------------------------------------------------------------
/demo/data/contour-data.json:
--------------------------------------------------------------------------------
1 | [
2 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
3 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
4 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
5 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
6 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
7 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
8 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
9 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
10 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
11 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
12 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
13 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
14 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
15 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
16 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
17 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
18 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 58, 0, 0, 0, 97, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
19 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 55, 123, 58, 0, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
20 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 39, 6, 30, 0, 17, 0, 0, 0, 4, 0, 0, 65, 54, 53, 0, 0, 48, 43.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
21 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 51, 29, 4, 83, 0, 27, 56, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
22 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 57, 25, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
23 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 37, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
24 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
25 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
26 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
27 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
28 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24.5, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
29 | [0, 0, 0, 0, 23, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 47.25, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
30 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 31, 63, 71, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
31 | [0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 114, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
32 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 37, 0, 27, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
33 | [0, 0, 0, 0, 14, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 71, 22, 14, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 34, 48, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
34 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 11, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
35 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
36 | [35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 31, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
37 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
38 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
39 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 36, 61, 0, 0, 94, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
40 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 40, 0, 0, 12, 0, 40, 87, 23, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
41 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 9, 87, 0, 0, 9, 65, 61, 0, 0, 0, 23.5, 58.5, 21, 89, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
42 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 35.5, 7, 18.5, 8, 0, 0, 39.5, 41.75, 23.25, 38, 0, 49.5, 22, 0, 0, 37, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
43 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 34, 41, 94, 0, 62, 21.25, 50, 48, 0, 0, 50, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 9, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
44 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 26.5, 21.5, 3, 26, 147, 16.75, 42.5, 0, 0, 0, 0, 0, 0, 40, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
45 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24.25, 69.5, 39, 39, 0, 80, 5, 71, 7, 18.25, 0, 18, 2, 0, 0, 0, 106, 82, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
46 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 53, 49, 0, 17, 51.5, 0, 0, 10, 0, 40, 0, 40, 0, 47, 0, 54, 44, 43, 0, 14, 0, 0, 0, 0, 40.5, 8, 0, 42, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
47 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 32, 0, 79, 0, 0, 62, 37, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
48 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 44, 58, 37.5, 3, 0, 16, 0, 0, 0, 0, 52, 27, 7, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
49 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 52, 24.75, 0, 80.5, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
50 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, 0, 122, 0, 0, 0, 0, 0, 49, 0, 0, 0, 35, 0, 2, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
51 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 31, 0, 0, 0, 0, 0, 46, 93, 0, 0, 57, 0, 51, 0, 0, 33, 0, 0, 134, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0],
52 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 35, 36, 0, 0, 0, 41, 27, 52, 0, 0, 0, 0, 57, 0, 0, 9, 0, 0, 69, 0, 68, 0, 7, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
53 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 44, 22, 15.5, 30, 40, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 76, 16, 0, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, 27],
54 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 53, 25.8125, 54.25, 0, 0, 0, 0, 28, 51, 0, 0, 0, 73, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 24],
55 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 34, 26, 54, 0, 0, 58, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0],
56 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 75, 48, 0, 0, 158, 0, 0, 0, 0, 50, 0, 93, 0, 64, 42, 0, 0, 0, 195, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
57 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0],
58 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 70.5, 0, 5, 81, 45, 38, 0, 67, 0, 0, 0, 0, 44, 0, 91, 0, 0, 0, 0, 86, 36, 0, 0, 0, 0],
59 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 156, 0, 0, 8, 0, 15, 0, 44.5, 103, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 0, 0, 0],
60 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 8, 77, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 42, 58, 80, 0, 45, 0, 0, 0, 31, 0, 0, 0, 51, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0],
61 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, 0, 39, 23, 0, 28, 0, 0, 0, 0, 39, 0, 0, 105, 0, 0, 0, 0, 0, 75, 95, 0, 0, 168, 0, 81, 0, 67, 0, 41, 80, 72, 35, 80, 66, 29.5, 41, 0, 0, 0, 66, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
62 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0, 61, 0, 0, 0, 15.5, 0, 123, 44, 39.5, 0, 0, 0, 54, 0, 0, 37, 0, 0, 0, 93, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 54.5, 8, 59.5, 71.5, 70, 0, 9, 40, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
63 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 9, 0, 50, 0, 0, 53.5, 84, 0, 55, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 58, 0, 0, 0, 33, 63, 72, 15, 356, 69, 0, 0, 44.5, 51, 43.25, 53, 20, 37, 16, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
64 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 89, 0, 0, 0, 50.5, 0, 0, 18, 6, 96, 2, 0, 0, 0, 75, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 89, 103.5, 45, 79.125, 60.375, 0, 36, 0, 33, 22.5, 29.25, 57.5, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0],
65 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 104, 72, 34, 75, 0, 0, 0, 0, 0, 0, 112, 54, 44, 0, 0, 75, 117.75, 0, 104, 54, 33.3125, 32.625, 81, 26, 73, 0, 18, 0, 99, 52, 63, 0, 0, 63, 68, 0, 0, 35.25, 0, 0, 0],
66 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 44, 31.75, 104, 62, 0, 64.5, 24.75, 32, 19, 18, 36, 0, 0, 65, 0, 0, 0, 91, 0, 149, 0, 0, 0, 0, 0, 0],
67 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 129, 0, 0, 0, 47, 0, 0, 60, 0, 117, 0, 0, 0, 0, 0, 85, 0, 0, 0, 0, 0, 72, 62, 0, 158, 0, 0, 0, 0, 28, 0, 0, 0, 37.5, 0, 0, 0, 0, 0, 88, 0, 0, 0, 57, 21, 0, 0, 0],
68 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 59, 0, 0, 0, 81, 49, 0, 83, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0],
69 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 41, 0, 0, 0, 75, 0, 0, 0, 0, 0, 0, 53, 91, 0, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
70 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 179, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 45, 0, 0, 0, 59.5, 0, 82, 0, 27, 43, 71, 0, 17, 0, 0, 48, 25, 133, 194, 0, 0, 61, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
71 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 75, 0, 0, 0, 0, 0, 0, 10, 13, 0, 50, 17, 34, 0, 53, 0, 56.5, 0, 0, 0, 34, 0, 24, 61, 0, 0, 0, 0, 61, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
72 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 40, 60, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
73 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, 0, 0, 0, 116, 0, 49.75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
74 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
75 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 76, 52, 0, 30, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
76 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
77 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 84, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
78 | ]
--------------------------------------------------------------------------------
/demo/images/bg_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/almossawi/aframe-d3-visualization/ebdf6eea23f3ad008660109eeef9dda96792cd8b/demo/images/bg_light.png
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Where is Piers Morgan disliked the most?
23 |
24 |
25 |
26 |
Find out by clicking and dragging on the terrain using your mouse or by
27 | breaking out of auto-pilot and navigating with the
28 | W
29 | A
30 | S
31 | D
32 | keys on your keyboard. Look at locations to learn about them. If you have a
33 | VR headset, then use that instead.
34 |
35 |
The black sand forms mounds that are tallest in cities with the greatest amount of
36 | dislike for the smug croissant. Cities are areas with 10K or more people. Modulo the
37 | clearly fake data, the city-level data is from
38 | here . The contour map below
39 | is draggable and will help you orient yourself.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
281 |
282 |
290 |
291 |
292 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
--------------------------------------------------------------------------------
/demo/js/conrec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010, Jason Davies.
3 | *
4 | * All rights reserved. This code is based on Bradley White's Java version,
5 | * which is in turn based on Nicholas Yue's C++ version, which in turn is based
6 | * on Paul D. Bourke's original Fortran version. See below for the respective
7 | * copyright notices.
8 | *
9 | * See http://local.wasp.uwa.edu.au/~pbourke/papers/conrec/ for the original
10 | * paper by Paul D. Bourke.
11 | *
12 | * The vector conversion code is based on http://apptree.net/conrec.htm by
13 | * Graham Cox.
14 | *
15 | * Redistribution and use in source and binary forms, with or without
16 | * modification, are permitted provided that the following conditions are met:
17 | * * Redistributions of source code must retain the above copyright
18 | * notice, this list of conditions and the following disclaimer.
19 | * * Redistributions in binary form must reproduce the above copyright
20 | * notice, this list of conditions and the following disclaimer in the
21 | * documentation and/or other materials provided with the distribution.
22 | * * Neither the name of the nor the
23 | * names of its contributors may be used to endorse or promote products
24 | * derived from this software without specific prior written permission.
25 | *
26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 | * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
30 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 | */
37 |
38 | /*
39 | * Copyright (c) 1996-1997 Nicholas Yue
40 | *
41 | * This software is copyrighted by Nicholas Yue. This code is based on Paul D.
42 | * Bourke's CONREC.F routine.
43 | *
44 | * The authors hereby grant permission to use, copy, and distribute this
45 | * software and its documentation for any purpose, provided that existing
46 | * copyright notices are retained in all copies and that this notice is
47 | * included verbatim in any distributions. Additionally, the authors grant
48 | * permission to modify this software and its documentation for any purpose,
49 | * provided that such modifications are not distributed without the explicit
50 | * consent of the authors and that existing copyright notices are retained in
51 | * all copies. Some of the algorithms implemented by this software are
52 | * patented, observe all applicable patent law.
53 | *
54 | * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
55 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
56 | * OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
57 | * EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 | *
59 | * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
60 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
61 | * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS
62 | * PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO
63 | * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
64 | * MODIFICATIONS.
65 | */
66 |
67 | (function(exports) {
68 | exports.Conrec = Conrec;
69 |
70 | var EPSILON = 1e-10;
71 |
72 | function pointsEqual(a, b) {
73 | var x = a.x - b.x, y = a.y - b.y;
74 | return x * x + y * y < EPSILON;
75 | }
76 |
77 | function reverseList(list) {
78 | var pp = list.head;
79 |
80 | while (pp) {
81 | // swap prev/next pointers
82 | var temp = pp.next;
83 | pp.next = pp.prev;
84 | pp.prev = temp;
85 |
86 | // continue through the list
87 | pp = temp;
88 | }
89 |
90 | // swap head/tail pointers
91 | var temp = list.head;
92 | list.head = list.tail;
93 | list.tail = temp;
94 | }
95 |
96 | function ContourBuilder(level) {
97 | this.level = level;
98 | this.s = null;
99 | this.count = 0;
100 | }
101 | ContourBuilder.prototype.remove_seq = function(list) {
102 | // if list is the first item, static ptr s is updated
103 | if (list.prev) {
104 | list.prev.next = list.next;
105 | } else {
106 | this.s = list.next;
107 | }
108 |
109 | if (list.next) {
110 | list.next.prev = list.prev;
111 | }
112 | --this.count;
113 | }
114 | ContourBuilder.prototype.addSegment = function(a, b) {
115 | var ss = this.s;
116 | var ma = null;
117 | var mb = null;
118 | var prependA = false;
119 | var prependB = false;
120 |
121 | while (ss) {
122 | if (ma == null) {
123 | // no match for a yet
124 | if (pointsEqual(a, ss.head.p)) {
125 | ma = ss;
126 | prependA = true;
127 | } else if (pointsEqual(a, ss.tail.p)) {
128 | ma = ss;
129 | }
130 | }
131 | if (mb == null) {
132 | // no match for b yet
133 | if (pointsEqual(b, ss.head.p)) {
134 | mb = ss;
135 | prependB = true;
136 | } else if (pointsEqual(b, ss.tail.p)) {
137 | mb = ss;
138 | }
139 | }
140 | // if we matched both no need to continue searching
141 | if (mb != null && ma != null) {
142 | break;
143 | } else {
144 | ss = ss.next;
145 | }
146 | }
147 |
148 | // c is the case selector based on which of ma and/or mb are set
149 | var c = ((ma != null) ? 1 : 0) | ((mb != null) ? 2 : 0);
150 |
151 | switch(c) {
152 | case 0: // both unmatched, add as new sequence
153 | var aa = {p: a, prev: null};
154 | var bb = {p: b, next: null};
155 | aa.next = bb;
156 | bb.prev = aa;
157 |
158 | // create sequence element and push onto head of main list. The order
159 | // of items in this list is unimportant
160 | ma = {head: aa, tail: bb, next: this.s, prev: null, closed: false};
161 | if (this.s) {
162 | this.s.prev = ma;
163 | }
164 | this.s = ma;
165 |
166 | ++this.count; // not essential - tracks number of unmerged sequences
167 | break;
168 |
169 | case 1: // a matched, b did not - thus b extends sequence ma
170 | var pp = {p: b};
171 |
172 | if (prependA) {
173 | pp.next = ma.head;
174 | pp.prev = null;
175 | ma.head.prev = pp;
176 | ma.head = pp;
177 | } else {
178 | pp.next = null;
179 | pp.prev = ma.tail;
180 | ma.tail.next = pp;
181 | ma.tail = pp;
182 | }
183 | break;
184 |
185 | case 2: // b matched, a did not - thus a extends sequence mb
186 | var pp = {p: a};
187 |
188 | if (prependB) {
189 | pp.next = mb.head;
190 | pp.prev = null;
191 | mb.head.prev = pp;
192 | mb.head = pp;
193 | } else {
194 | pp.next = null;
195 | pp.prev = mb.tail;
196 | mb.tail.next = pp;
197 | mb.tail = pp;
198 | }
199 | break;
200 |
201 | case 3: // both matched, can merge sequences
202 | // if the sequences are the same, do nothing, as we are simply closing this path (could set a flag)
203 |
204 | if (ma === mb) {
205 | var pp = {p: ma.tail.p, next: ma.head, prev: null};
206 | ma.head.prev = pp;
207 | ma.head = pp;
208 | ma.closed = true;
209 | break;
210 | }
211 |
212 | // there are 4 ways the sequence pair can be joined. The current setting of prependA and
213 | // prependB will tell us which type of join is needed. For head/head and tail/tail joins
214 | // one sequence needs to be reversed
215 | switch((prependA ? 1 : 0) | (prependB ? 2 : 0)) {
216 | case 0: // tail-tail
217 | // reverse ma and append to mb
218 | reverseList(ma);
219 | // fall through to head/tail case
220 | case 1: // head-tail
221 | // ma is appended to mb and ma discarded
222 | mb.tail.next = ma.head;
223 | ma.head.prev = mb.tail;
224 | mb.tail = ma.tail;
225 |
226 | //discard ma sequence record
227 | this.remove_seq(ma);
228 | break;
229 |
230 | case 3: // head-head
231 | // reverse ma and append mb to it
232 | reverseList(ma);
233 | // fall through to tail/head case
234 | case 2: // tail-head
235 | // mb is appended to ma and mb is discarded
236 | ma.tail.next = mb.head;
237 | mb.head.prev = ma.tail;
238 | ma.tail = mb.tail;
239 |
240 | //discard mb sequence record
241 | this.remove_seq(mb);
242 | break;
243 | }
244 | }
245 | }
246 |
247 | /**
248 | * Implements CONREC.
249 | *
250 | * @param {function} drawContour function for drawing contour. Defaults to a
251 | * custom "contour builder", which populates the
252 | * contours property.
253 | */
254 | function Conrec(drawContour) {
255 | if (!drawContour) {
256 | var c = this;
257 | c.contours = {};
258 | /**
259 | * drawContour - interface for implementing the user supplied method to
260 | * render the countours.
261 | *
262 | * Draws a line between the start and end coordinates.
263 | *
264 | * @param startX - start coordinate for X
265 | * @param startY - start coordinate for Y
266 | * @param endX - end coordinate for X
267 | * @param endY - end coordinate for Y
268 | * @param contourLevel - Contour level for line.
269 | */
270 | this.drawContour = function(startX, startY, endX, endY, contourLevel, k) {
271 | var cb = c.contours[k];
272 | if (!cb) {
273 | cb = c.contours[k] = new ContourBuilder(contourLevel);
274 | }
275 | cb.addSegment({x: startX, y: startY}, {x: endX, y: endY});
276 | }
277 | this.contourList = function() {
278 | var l = [];
279 | var a = c.contours;
280 | for (var k in a) {
281 | var s = a[k].s;
282 | var level = a[k].level;
283 | while (s) {
284 | var h = s.head;
285 | var l2 = [];
286 | l2.level = level;
287 | l2.k = k;
288 | while (h && h.p) {
289 | l2.push(h.p);
290 | h = h.next;
291 | }
292 | l.push(l2);
293 | s = s.next;
294 | }
295 | }
296 | l.sort(function(a, b) { return a.k - b.k });
297 | return l;
298 | }
299 | } else {
300 | this.drawContour = drawContour;
301 | }
302 | this.h = new Array(5);
303 | this.sh = new Array(5);
304 | this.xh = new Array(5);
305 | this.yh = new Array(5);
306 | }
307 |
308 | /**
309 | * contour is a contouring subroutine for rectangularily spaced data
310 | *
311 | * It emits calls to a line drawing subroutine supplied by the user which
312 | * draws a contour map corresponding to real*4data on a randomly spaced
313 | * rectangular grid. The coordinates emitted are in the same units given in
314 | * the x() and y() arrays.
315 | *
316 | * Any number of contour levels may be specified but they must be in order of
317 | * increasing value.
318 | *
319 | *
320 | * @param {number[][]} d - matrix of data to contour
321 | * @param {number} ilb,iub,jlb,jub - index bounds of data matrix
322 | *
323 | * The following two, one dimensional arrays (x and y) contain
324 | * the horizontal and vertical coordinates of each sample points.
325 | * @param {number[]} x - data matrix column coordinates
326 | * @param {number[]} y - data matrix row coordinates
327 | * @param {number} nc - number of contour levels
328 | * @param {number[]} z - contour levels in increasing order.
329 | */
330 | Conrec.prototype.contour = function(d, ilb, iub, jlb, jub, x, y, nc, z) {
331 | var h = this.h, sh = this.sh, xh = this.xh, yh = this.yh;
332 | var drawContour = this.drawContour;
333 | this.contours = {};
334 |
335 | /** private */
336 | var xsect = function(p1, p2){
337 | return (h[p2]*xh[p1]-h[p1]*xh[p2])/(h[p2]-h[p1]);
338 | }
339 |
340 | var ysect = function(p1, p2){
341 | return (h[p2]*yh[p1]-h[p1]*yh[p2])/(h[p2]-h[p1]);
342 | }
343 | var m1;
344 | var m2;
345 | var m3;
346 | var case_value;
347 | var dmin;
348 | var dmax;
349 | var x1 = 0.0;
350 | var x2 = 0.0;
351 | var y1 = 0.0;
352 | var y2 = 0.0;
353 |
354 | // The indexing of im and jm should be noted as it has to start from zero
355 | // unlike the fortran counter part
356 | var im = [0, 1, 1, 0];
357 | var jm = [0, 0, 1, 1];
358 |
359 | // Note that castab is arranged differently from the FORTRAN code because
360 | // Fortran and C/C++ arrays are transposed of each other, in this case
361 | // it is more tricky as castab is in 3 dimensions
362 | var castab = [
363 | [
364 | [0, 0, 8], [0, 2, 5], [7, 6, 9]
365 | ],
366 | [
367 | [0, 3, 4], [1, 3, 1], [4, 3, 0]
368 | ],
369 | [
370 | [9, 6, 7], [5, 2, 0], [8, 0, 0]
371 | ]
372 | ];
373 |
374 | for (var j=(jub-1);j>=jlb;j--) {
375 | for (var i=ilb;i<=iub-1;i++) {
376 | var temp1, temp2;
377 | temp1 = Math.min(d[i][j],d[i][j+1]);
378 | temp2 = Math.min(d[i+1][j],d[i+1][j+1]);
379 | dmin = Math.min(temp1,temp2);
380 | temp1 = Math.max(d[i][j],d[i][j+1]);
381 | temp2 = Math.max(d[i+1][j],d[i+1][j+1]);
382 | dmax = Math.max(temp1,temp2);
383 |
384 | if (dmax>=z[0]&&dmin<=z[nc-1]) {
385 | for (var k=0;k=dmin&&z[k]<=dmax) {
387 | for (var m=4;m>=0;m--) {
388 | if (m>0) {
389 | // The indexing of im and jm should be noted as it has to
390 | // start from zero
391 | h[m] = d[i+im[m-1]][j+jm[m-1]]-z[k];
392 | xh[m] = x[i+im[m-1]];
393 | yh[m] = y[j+jm[m-1]];
394 | } else {
395 | h[0] = 0.25*(h[1]+h[2]+h[3]+h[4]);
396 | xh[0]=0.5*(x[i]+x[i+1]);
397 | yh[0]=0.5*(y[j]+y[j+1]);
398 | }
399 | if (h[m]>EPSILON) {
400 | sh[m] = 1;
401 | } else if (h[m]<-EPSILON) {
402 | sh[m] = -1;
403 | } else
404 | sh[m] = 0;
405 | }
406 | //
407 | // Note: at this stage the relative heights of the corners and the
408 | // centre are in the h array, and the corresponding coordinates are
409 | // in the xh and yh arrays. The centre of the box is indexed by 0
410 | // and the 4 corners by 1 to 4 as shown below.
411 | // Each triangle is then indexed by the parameter m, and the 3
412 | // vertices of each triangle are indexed by parameters m1,m2,and
413 | // m3.
414 | // It is assumed that the centre of the box is always vertex 2
415 | // though this isimportant only when all 3 vertices lie exactly on
416 | // the same contour level, in which case only the side of the box
417 | // is drawn.
418 | //
419 | //
420 | // vertex 4 +-------------------+ vertex 3
421 | // | \ / |
422 | // | \ m-3 / |
423 | // | \ / |
424 | // | \ / |
425 | // | m=2 X m=2 | the centre is vertex 0
426 | // | / \ |
427 | // | / \ |
428 | // | / m=1 \ |
429 | // | / \ |
430 | // vertex 1 +-------------------+ vertex 2
431 | //
432 | //
433 | //
434 | // Scan each triangle in the box
435 | //
436 | for (m=1;m<=4;m++) {
437 | m1 = m;
438 | m2 = 0;
439 | if (m!=4) {
440 | m3 = m+1;
441 | } else {
442 | m3 = 1;
443 | }
444 | case_value = castab[sh[m1]+1][sh[m2]+1][sh[m3]+1];
445 | if (case_value!=0) {
446 | switch (case_value) {
447 | case 1: // Line between vertices 1 and 2
448 | x1=xh[m1];
449 | y1=yh[m1];
450 | x2=xh[m2];
451 | y2=yh[m2];
452 | break;
453 | case 2: // Line between vertices 2 and 3
454 | x1=xh[m2];
455 | y1=yh[m2];
456 | x2=xh[m3];
457 | y2=yh[m3];
458 | break;
459 | case 3: // Line between vertices 3 and 1
460 | x1=xh[m3];
461 | y1=yh[m3];
462 | x2=xh[m1];
463 | y2=yh[m1];
464 | break;
465 | case 4: // Line between vertex 1 and side 2-3
466 | x1=xh[m1];
467 | y1=yh[m1];
468 | x2=xsect(m2,m3);
469 | y2=ysect(m2,m3);
470 | break;
471 | case 5: // Line between vertex 2 and side 3-1
472 | x1=xh[m2];
473 | y1=yh[m2];
474 | x2=xsect(m3,m1);
475 | y2=ysect(m3,m1);
476 | break;
477 | case 6: // Line between vertex 3 and side 1-2
478 | x1=xh[m3];
479 | y1=yh[m3];
480 | x2=xsect(m1,m2);
481 | y2=ysect(m1,m2);
482 | break;
483 | case 7: // Line between sides 1-2 and 2-3
484 | x1=xsect(m1,m2);
485 | y1=ysect(m1,m2);
486 | x2=xsect(m2,m3);
487 | y2=ysect(m2,m3);
488 | break;
489 | case 8: // Line between sides 2-3 and 3-1
490 | x1=xsect(m2,m3);
491 | y1=ysect(m2,m3);
492 | x2=xsect(m3,m1);
493 | y2=ysect(m3,m1);
494 | break;
495 | case 9: // Line between sides 3-1 and 1-2
496 | x1=xsect(m3,m1);
497 | y1=ysect(m3,m1);
498 | x2=xsect(m1,m2);
499 | y2=ysect(m1,m2);
500 | break;
501 | default:
502 | break;
503 | }
504 | // Put your processing code here and comment out the printf
505 | //printf("%f %f %f %f %f\n",x1,y1,x2,y2,z[k]);
506 | drawContour(x1,y1,x2,y2,z[k],k);
507 | }
508 | }
509 | }
510 | }
511 | }
512 | }
513 | }
514 | }
515 | })(typeof exports !== "undefined" ? exports : window);
516 |
--------------------------------------------------------------------------------
/demo/js/main.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var global = {};
3 | global.autopilot = true;
4 |
5 | // webvr scene
6 | d3.json('data/aframe-data.json', function(error, data) {
7 | global.data = data;
8 | global.number_of_cities = data.length;
9 |
10 | var dislikes_normalized_min_max = d3.extent(data, function(d) {
11 | return d.dislikes_normalized;
12 | });
13 |
14 | var longitude_min_max = d3.extent(data, function(d) {
15 | if(d !== undefined) {
16 | return d.longitude;
17 | }
18 | });
19 |
20 | var latitude_min_max = d3.extent(data, function(d) {
21 | if(d !== undefined) {
22 | return d.latitude;
23 | }
24 | });
25 |
26 | var sessions_min_max = d3.extent(data, function(d) {
27 | if(d !== undefined) {
28 | return d.dislikes_normalized;
29 | }
30 | });
31 |
32 | // scales used to generate the scene
33 | var y_scale = d3.scale.linear()
34 | .domain([0, dislikes_normalized_min_max[1]])
35 | .range([0, 25]);
36 |
37 | var x_scale = d3.scale.linear()
38 | .domain([longitude_min_max[0], longitude_min_max[1]])
39 | .range([200, 0]);
40 |
41 | var z_scale = d3.scale.linear()
42 | .domain([latitude_min_max[0], latitude_min_max[1]])
43 | .range([0, 300]);
44 |
45 | // other scales
46 | var median = d3.median(data, function(d) { return d.dislikes_normalized; });
47 | global.percentile_scale = d3.scale.linear()
48 | .domain([dislikes_normalized_min_max[0], median, 200])
49 | .range([1, 70, 140])
50 | .clamp(true);
51 |
52 | var median = d3.median(data, function(d) { return d.dislikes_normalized; });
53 | global.color_scale = d3.scale.linear()
54 | .domain([dislikes_normalized_min_max[0], median])
55 | .range(['#4CC3D9', '#fb237a'])
56 | .clamp(true);
57 |
58 | data.forEach(function(d, i) {
59 | var scene = d3.select('a-scene');
60 | var mounds = scene.selectAll('a-cone.mound')
61 | .data(data)
62 | .enter()
63 | .append('a-cone')
64 | .classed('mound', true)
65 | .attr('visible', 'false')
66 | .attr('position', function(d, i) {
67 | var x = x_scale(d.longitude);
68 | var z = z_scale(d.latitude);
69 | var y = y_scale(d.dislikes_normalized) / 2;
70 |
71 | return x + " " + y + " " + z;
72 | })
73 | .attr('height', function(d, i) {
74 | return y_scale(d.dislikes_normalized);
75 | })
76 | .attr('segments-radial', 4)
77 | .attr('radius-bottom', 2)
78 | .attr('radius-top', 0.1)
79 | .attr('material', function(d) {
80 | return 'color: #000; roughness: 1; metalness: 0';
81 | })
82 | .append('a-animation')
83 | .attr('attribute', 'visible')
84 | .attr('begin', '1000')
85 | .attr('to', 'true');
86 | });
87 |
88 | d3.selectAll('.mound')
89 | .on('click', function (d, i) {
90 | updateCardText(d, i);
91 | updateRankGraphic(d, i);
92 | });
93 | });
94 |
95 | // contour plot
96 | d3.json('data/contour-data.json', function(error, data) {
97 | d3.select('.overlay')
98 | .style('opacity', 1);
99 |
100 | var zs = [2, 22, 42, 62, 82, 102];
101 | var cliff = 1;
102 | data.push(d3.range(data[0].length).map(function() { return cliff; }));
103 | data.unshift(d3.range(data[0].length).map(function() { return cliff; }));
104 | data.forEach(function(d) {
105 | d.push(cliff);
106 | d.unshift(cliff);
107 | });
108 |
109 | var xs = d3.range(0, data.length);
110 | var ys = d3.range(0, data[0].length);
111 | var c = new Conrec;
112 |
113 | var width = 200;
114 | var height = 300;
115 | var multiplier = 1.05;
116 |
117 | global.contour_width = width;
118 | global.contour_height = height;
119 |
120 | d3.select('.contour-plot')
121 | .style('top', (window.innerHeight - (height * multiplier) - 20) + 'px')
122 | .style('opacity', 1);
123 |
124 | d3.selectAll('.contour-plot svg, .contour-plot svg rect')
125 | .attr('width', Math.round(width * multiplier))
126 | .attr('height', Math.round(height * multiplier));
127 |
128 | var svg = d3.select('.contour-plot svg');
129 |
130 | var t = textures.lines()
131 | .orientation("vertical", "horizontal")
132 | .size(4)
133 | .strokeWidth(1)
134 | .shapeRendering("crispEdges")
135 | .stroke("#1a1a1a");
136 |
137 | svg.call(t);
138 |
139 | svg.select('rect.back')
140 | .style('fill', t.url());
141 |
142 | // scales to generate the contour map
143 | var contour_x_scale = d3.scale.linear()
144 | .domain([1, d3.max(xs) - 1])
145 | .range([0, height]);
146 |
147 | var contour_y_scale = d3.scale.linear()
148 | .domain([1, d3.max(ys) - 1])
149 | .range([0, width]);
150 |
151 | // scales to map the scene to the arrow on the contour map
152 | global.camera_to_contour_x_scale = d3.scale.linear()
153 | .domain([-40, 140]) //camera: left edge to right edge
154 | .range([-20, 183]) //contour map: left edge to right edge
155 | .clamp(true);
156 |
157 | global.camera_to_contour_z_scale = d3.scale.linear()
158 | .domain([-245, 10]) // camera: top edge to bottom edge
159 | .range([-10, 300]) // contour map: top edge to bottom edge
160 | .clamp(true);
161 |
162 | global.world_position = d3.select(d3.select('a-camera').node().parentNode)
163 | .attr('position');
164 |
165 | global.world_rotation = d3.select(d3.select('a-camera').node().parentNode)
166 | .attr('rotation');
167 |
168 | var colors = d3.scale.linear()
169 | .domain([zs[0], 50, zs[zs.length - 1]])
170 | .range(['#16214c', '#4CC3D9', '#fff']);
171 |
172 | c.contour(data, 0, xs.length - 1, 0, ys.length - 1, xs, ys, zs.length, zs);
173 |
174 | var contourList = c.contourList()
175 | .sort(function(a,b) {
176 | return d3.min(a.map(function(d) { return d.x; }))
177 | - d3.min(b.map(function(d) { return d.x; })
178 | );
179 | });
180 |
181 | var g = d3.selectAll(".contours");
182 |
183 | g.selectAll("path")
184 | .data(contourList)
185 | .enter()
186 | .append("path")
187 | .attr("class", function(d) {
188 | return "level_" + d.level;
189 | })
190 | .style("fill",function(d) {
191 | return colors(d.level);
192 | })
193 | .style("fill-opacity", "0.3")
194 | .style("stroke", function(d) {
195 | return colors(d.level);
196 | })
197 | .style("stroke-opacity", function(d) {
198 | if(d.level > 2) {
199 | return 0.2;
200 | } else {
201 | return 0;
202 | }
203 | })
204 | .style('opacity', 1)
205 | .attr("d", d3.svg.line()
206 | .x(function(d) {
207 | return contour_x_scale(d.x);
208 | })
209 | .y(function(d) {
210 | return contour_y_scale(d.y);
211 | }));
212 |
213 | g.attr('transform', 'translate(0 ' + width + ') scale(1,-1) rotate(-90 ' + width/2 + ' ' + width/2 + ')');
214 |
215 | // add arrow
216 | svg.append('path')
217 | .attr('d', 'M20 34 L30 36 L32 48 L34 36 L44 34 L34 32 L32 20 L30 32 Z')
218 | .attr('fill', 'white')
219 | .attr('class', 'arrow');
220 |
221 | updateContour();
222 | });
223 |
224 | function updateCardText(d, i) {
225 | var suffix = 'th';
226 | ++i;
227 |
228 | var rank = Math.ceil(global.percentile_scale(d.dislikes_normalized));
229 |
230 | // what is the last digit?
231 | if(i % 10 == 1 && i !== 11) {
232 | suffix = 'st';
233 | } else if(i % 10 == 2 && i !== 12) {
234 | suffix = 'nd';
235 | } else if(i % 10 == 3) {
236 | suffix = 'rd';
237 | }
238 |
239 | if(!document.querySelector('.rank-graphic svg')) {
240 | var data = ''
241 | + ''
242 | + ''
243 | + ''
244 | + '';
245 |
246 | d3.select('.card').html(data);
247 | }
248 |
249 | // update card
250 | d3.select('.city').html(d.key);
251 | d3.select('.dislikes_normalized').html(Math.round(d.dislikes_normalized));
252 | d3.select('.population').html(addCommas(d.population));
253 | d3.select('.rank').html(i + suffix + ' out of ' + global.number_of_cities + ' cities');
254 | }
255 |
256 | function addCommas(x) {
257 | return x.toString()
258 | .replace(/\B(?=(\d{3})+(?!\d))/g, ',');
259 | }
260 |
261 | function updateRankGraphic(d, i) {
262 | var rank = Math.ceil(global.percentile_scale(d.dislikes_normalized));
263 |
264 | if(!document.querySelector('.rank-graphic svg')) {
265 | // add svg
266 | var svg = d3.select('.rank-graphic').append('svg')
267 | .attr('width', 150)
268 | .attr('height', 30);
269 |
270 | svg.append('rect')
271 | .attr('width', 140)
272 | .attr('height', 6)
273 | .attr('x', 1)
274 | .attr('y', 5)
275 | .attr('fill', 'none')
276 | .style('stroke-width', 1)
277 | .style('stroke', 'black');
278 |
279 | svg.append('rect')
280 | .attr('class', 'rank-graphic-indicator')
281 | .attr('fill', '#4CC3D9')
282 | .attr('x', 1)
283 | .attr('y', 5)
284 | .attr('width', 2)
285 | .attr('height', 6);
286 | }
287 |
288 | d3.select('.rank-graphic-indicator')
289 | .transition()
290 | .duration(1500)
291 | .attr('fill', global.color_scale(d.dislikes_normalized))
292 | .attr('width', rank);
293 | }
294 |
295 | // handlers for draggable components
296 | function drag_start(event) {
297 | var style = window.getComputedStyle(event.target, null);
298 | event.dataTransfer.setData("text/plain",
299 | (parseInt(style.getPropertyValue("left"),10) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top"),10) - event.clientY));
300 | }
301 |
302 | function drag_over(event) {
303 | event.preventDefault();
304 | return false;
305 | }
306 |
307 | function drop(event) {
308 | var offset = event.dataTransfer.getData("text/plain").split(',');
309 | var dm = d3.select('.contour-plot').node();
310 | dm.style.left = (event.clientX + parseInt(offset[0],10)) + 'px';
311 | dm.style.top = (event.clientY + parseInt(offset[1],10)) + 'px';
312 | event.preventDefault();
313 | return false;
314 | }
315 |
316 | var dm = d3.select('.contour-plot').node();
317 | dm.addEventListener('dragstart',drag_start,false);
318 | document.body.addEventListener('dragover',drag_over,false);
319 | document.body.addEventListener('drop',drop,false);
320 |
321 | setInterval(function() {
322 | updateContour();
323 | }, 80);
324 |
325 | function updateContour() {
326 | var position = d3.select('a-camera')
327 | .attr('position');
328 |
329 | var x = global.camera_to_contour_x_scale(global.world_position.x + position.x);
330 | var z = global.camera_to_contour_z_scale(global.world_position.z + position.z);
331 |
332 | var facing = d3.select('a-camera')
333 | .attr('rotation');
334 |
335 | d3.selectAll('.arrow')
336 | .attr('transform', function() {
337 | return 'translate(' + x + ' ' + z + ')';
338 | });
339 | }
340 |
341 | d3.select('.camera-button')
342 | .on('click', function() {
343 | d3.select('.loading')
344 | .classed('hide', false)
345 |
346 | document.querySelector('.hand-of-frog')
347 | .emit('change-camera');
348 |
349 | var am_flying = d3.select('.camera-button')
350 | .classed('fa-plane');
351 |
352 | if(am_flying) {
353 | d3.select('.camera-button')
354 | .classed('fa-plane', false)
355 | .classed('fa-car', true);
356 |
357 | // add spotlight when driving
358 | d3.select('a-camera')
359 | .append('a-entity')
360 | .transition()
361 | .delay(2000)
362 | .attr('class', 'spotlight')
363 | .attr('light', 'type: point; intensity: 6; distance: 150; decay: 3')
364 | .attr('position', '0 0 1')
365 | .attr('color', 'white');
366 | } else {
367 | d3.select('.camera-button')
368 | .classed('fa-plane', true)
369 | .classed('fa-car', false);
370 |
371 | // remove spotlight when flying
372 | d3.select('a-camera .spotlight')
373 | .remove();
374 | }
375 |
376 | setTimeout(function() {
377 | d3.select('.loading')
378 | .classed('hide', true)
379 | }, 2000);
380 | });
381 |
382 | // disable animation when w, a, s, d are pressed
383 | d3.select('body')
384 | .on('keydown', function() {
385 | if(!global.autopilot) {
386 | return;
387 | }
388 |
389 | var keys = [87, 65, 68, 83];
390 | if(keys.indexOf(d3.event.keyCode) != -1) {
391 | d3.select('.autopilot')
392 | .remove();
393 | global.autopilot = false;
394 | }
395 | });
396 | })();
--------------------------------------------------------------------------------
/demo/js/textures.min.js:
--------------------------------------------------------------------------------
1 | (function(){var rand,umd,slice=[].slice;rand=function(){return(Math.random().toString(36)+"00000000000000000").replace(/[^a-z]+/g,"").slice(0,5)};umd=function(factory){if(typeof exports==="object"){return module.exports=factory()}else if(typeof define==="function"&&define.amd){return define([],factory)}else{return this.textures=factory()}};umd(function(){return{circles:function(){var background,circles,complement,fill,id,radius,size,stroke,strokeWidth;size=20;background="";radius=2;complement=false;fill="#343434";stroke="#343434";strokeWidth=0;id=rand();circles=function(){var corner,g,i,len,ref,results;g=this.append("defs").append("pattern").attr({id:id,patternUnits:"userSpaceOnUse",width:size,height:size});if(background){g.append("rect").attr({width:size,height:size,fill:background})}g.append("circle").attr({cx:size/2,cy:size/2,r:radius,fill:fill,stroke:stroke,"stroke-width":strokeWidth});if(complement){ref=[[0,0],[0,size],[size,0],[size,size]];results=[];for(i=0,len=ref.length;i
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Visualizing in VR using A‑Frame and D3
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
44 |
45 |
46 |
47 |
Visualizing in VR using A‑Frame and D3
48 |
I was interested in seeing what a compelling visualization in 3D, and by extension in VR, might
49 | look like. In this demo , titled Where is Piers Morgan disliked the most? , one learns about
50 | various cities by exploring a terrain. The fake data is encoded using elevation at the city level. Turning your head,
51 | which you can either do with a headset or by clicking and dragging your mouse, and looking at a mound of black sand
52 | provides some insight into that city.
53 |
The scene is done in A-Frame , the contour plot was generated
54 | in conrec.js and is updated in plain JavaScript. Everything else is D3 and JavaScript. You can take a look at
55 | the source code to see how everything is put
56 | together. Though still very much an amateur with
57 | WebVR, I'm liking the great breadth of possibilities that the platform affords. If you have any questions, feel free
58 | to reach out.
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------