Public
Edited
Mar 4, 2023
Insert cell
Insert cell
mutable update_count = 0
Insert cell
Insert cell
// import {OrthographicView, Deck} from '@deck.gl/core';

// // Initialize global view state with three views
// let globalViewState = {
// matrix: {
// target: [0, 0, 0],
// zoom: 1,
// panX: 0,
// panY: 0,
// },
// row: {
// target: [0, 0, 0],
// zoom: 1,
// panY: 0,
// },
// column: {
// target: [0, 0, 0],
// zoom: 1,
// panX: 0,
// },
// };

// // Initialize Deck instance with three OrthographicViews
// const deck = new Deck({
// views: [
// new OrthographicView({id: 'matrix'}),
// new OrthographicView({id: 'row'}),
// new OrthographicView({id: 'column'}),
// ],
// initialViewState: globalViewState,
// controller: true, // enable interactivity
// onViewStateChange: ({viewState, viewId}) => {
// // Extract the pan and zoom values from the viewState object
// const {zoom, target, offset} = viewState;
// const {x, y} = offset || {x: 0, y: 0};
// const {panX, panY} = viewState[viewId];

// if (viewId === 'matrix') {
// // Update row and column views to move in the appropriate X and Y directions
// globalViewState = {
// ...globalViewState,
// row: {...globalViewState.row, panY},
// column: {...globalViewState.column, panX},
// };
// } else {
// // Update matrix view to move in the appropriate X or Y direction
// const newTarget = [
// viewId === 'row' ? target[0] : target[0] + panX,
// viewId === 'column' ? target[1] : target[1] + panY,
// target[2],
// ];
// globalViewState = {
// ...globalViewState,
// matrix: {...globalViewState.matrix, target: newTarget},
// };
// }

// // Update deck with the new global view state
// deck.setProps({viewState: {...globalViewState}});
// },
// });

Insert cell
// see https://talk.observablehq.com/t/deck-gl-seprops-circular-definition/7389/2
deckgl = {

var ini_zoom_mode = 'Y'

let globalViewState = ({
'matrix': ({
target: [500, 500, 0],
zoom: [-1, -1]
}),
'rows': ({
target: [500, 500, 0],
zoom: [-1, -1]
}),
'cols': ({
target: [500, 500, 0],
zoom: [-1, -1]
})
})

const instance = new deck.DeckGL({
container,
views,

// https://github.com/visgl/deck.gl/discussions/7574#discussioncomment-4656419
// this is a shorthand when using the default view
// controller: {scrollZoom: true, inertia: true, zoomAxis: ini_zoom_mode},

// both seem to work
/////////////////////////
// viewState: ini_view_state,
initialViewState: globalViewState,

// onViewStateChange: ({viewState, viewId}) => {

// console.log(viewState)

// if (viewId === 'matrix') {

// // console.log('matrix')
// viewState.matrix = viewState;
// viewState.rows = viewState;
// viewState.cols = viewState;

// // // trying to set the target of the rows
// // viewState.rows.zoom = [0, 0]

// } else if (viewId === 'rows') {

// // console.log('rows')
// viewState.matrix = viewState;
// viewState.rows = viewState;
// viewState.cols = viewState;
// } else if (viewId === 'cols'){
// // console.log('cols')
// viewState.matrix = viewState;
// viewState.rows = viewState;
// viewState.cols = viewState;
// }
// // Update deck
// instance.setProps({viewState: {...viewState}});
// }

onViewStateChange: ({viewState, viewId}) => {

// mutable update_count = update_count + 1

// console.log(viewState, viewId)
// // Extract the pan and zoom values from the viewState object
// const {zoom, target, offset} = viewState;
// const {x, y} = offset || {x: 0, y: 0};
// // const {panX, panY} = viewState[viewId];
// const {panX, panY} = viewState[viewId];

// Extract the pan and zoom values from the viewState object
const {zoom, target} = viewState;
// const {x, y} = offset || {x: 0, y: 0};


console.log(viewState)

// Check that viewState[viewId] exists before extracting panX and panY
const {panX, panY} = viewState[viewId] || {panX: 0, panY: 0};

if (viewId === 'matrix') {
// // Update row and column views to move in the appropriate X and Y directions
// globalViewState = {
// ...globalViewState,
// row: {...globalViewState.row, panY},
// column: {...globalViewState.column, panX},
// };

globalViewState = {
matrix: viewState,
rows: viewState,
cols: viewState,
}
}
// } else {
// // // Update matrix view to move in the appropriate X or Y direction
// // const newTarget = [
// // viewId === 'row' ? target[0] : target[0] + panX,
// // viewId === 'column' ? target[1] : target[1] + panY,
// // target[2],
// // ];
// // globalViewState = {
// // ...globalViewState,
// // matrix: {...globalViewState.matrix, target: newTarget},
// // };

// globalViewState = {
// matrix: viewState,
// rows: viewState,
// cols: viewState,
// }
// }

else if (viewId === 'rows') {
globalViewState = {
matrix: viewState,
rows: {zoom: viewState.zoom, target: [0, 0]},
cols: viewState,
}
} else if (viewId === 'cols') {
globalViewState = {
matrix: viewState,
rows: viewState,
cols: viewState,
}
}

// Update deck with the new global view state
instance.setProps({viewState: {...globalViewState}});
},


})
return instance;
}
Insert cell
// mutable globalViewState = ({
// 'matrix': ({
// target: [500, 500, 0],
// zoom: [-1, -1]
// }),
// 'rows': ({
// target: [500, 500, 0],
// zoom: [-1, -1]
// }),
// 'cols': ({
// target: [500, 500, 0],
// zoom: [-1, -1]
// })
// })


// // Initialize global view state with three views
// globalViewState = ({
// matrix: {
// target: [0, 0, 0],
// zoom: 1,
// panX: 0,
// panY: 0,
// },
// row: {
// target: [0, 0, 0],
// zoom: 1,
// panY: 0,
// },
// column: {
// target: [0, 0, 0],
// zoom: 1,
// panX: 0,
// },
// })
Insert cell
views = [
new deck.OrthographicView({
id: 'matrix',
x: label_width + 'px',
y: label_width + 'px',
width: matrix_width + 'px',
height: matrix_height + 'px',
controller: {scrollZoom: true, inertia: false, zoomAxis: 'all'},
}),

new deck.OrthographicView({
id: 'rows',
x: '0px',
y: label_width + 'px',
width: '100px',
height: matrix_height + 'px',
controller: {scrollZoom: true, inertia: false, zoomAxis: 'Y'},
}),

new deck.OrthographicView({
id: 'cols',
x: label_width + 'px',
y: '0px',
width: matrix_width + 'px',
height: label_width + 'px',
controller: {scrollZoom: true, inertia: false, zoomAxis: 'X'},
}),
]
Insert cell
// {
// const viewState = {main: {...}, miniview: {...}};

// function onViewStateChange({viewState, viewId}) {
// if (viewId === 'main') {
// viewState.main = viewState;
// viewState.miniview.target = viewState.target;
// } else {
// ...
// }
// // Update deck
// deckgl.setProps({viewState: {...viewState}});
// }
// }
Insert cell
// onViewStateChange: ({viewId, viewState}) => {
// console.log(viewId, 'onviewstatechange!!')
// const nextZoomMode = viewState.zoom[0] > 0 ? 'all' : 'X'
// // if (zoomMode !== nextZoomMode) {
// // // update zoom mode
// // zoomMode = nextZoomMode

// // // update Props
// // instance.setProps({
// // controller: {zoomAxis: zoomMode}
// // })
// // }
// }
// getTooltip: ({object}) => object && {
// html: `${object}`,
// style: {
// fontSize: '0.8em',
// padding: '5px',
// }
// },
Insert cell
label_width = 100
Insert cell
matrix_width = 800
Insert cell
matrix_height = 800
Insert cell
Insert cell
// num_points = 100
num_points = 0.001 * 1000 * 1000
Insert cell
num_cols = Math.floor(Math.sqrt(num_points))
Insert cell
mil_points = num_points /1e6
Insert cell
Math.pow(0.96, 0)
Insert cell
num_cols
Insert cell
data = {
const DEGREE_TO_RADIANS = Math.PI / 180;
let r = 0;
let a = 0;

// const num_cols = 100;
// const num_rows = 100;
var row_index = 0
return new Array(num_points).fill(0).map(_ => {
const cosA = Math.cos(a * DEGREE_TO_RADIANS);
const sinA = Math.sin(a * DEGREE_TO_RADIANS);
const radius = Math.pow(0.96, r);
const dist = 45

var inst_col = (a % num_cols)

if (a % num_cols === 0){
row_index += 1;
}
const p = {
size: radius * radius * 20,
position: [dist * inst_col, dist * row_index * 1],
// color: [(cosA + 1) / 2 * 255, (sinA + 1) / 2 * 255, 128]
color: [255, 0, 0]
};
a += 1;
return p;
});
}
Insert cell
Insert cell
defaultProps = {
return {
// Center of each circle, in [longitude, latitude, (z)]
getPosition: {type: 'accessor', value: x => x.position},
// Radius of each circle, in meters
getRadius: {type: 'accessor', value: 1},
// Color of each circle, in [R, G, B, (A)]
getColor: {type: 'accessor', value: [0, 0, 0, 255]},
// Amount to soften the edges
// smoothRadius: {type: 'number', min: 0, value: 0.5},
smoothRadius: {type: 'number', min: 0, value: 0.0},
}
}
Insert cell
Insert cell
Insert cell
// layer0.props.smoothRadius
Insert cell
Insert cell
// vertexShader = `
// attribute vec3 positions;
// attribute vec3 instancePositions;
// attribute vec3 instancePositions64Low;
// attribute float instanceRadius;
// attribute vec4 instanceColors;

// varying vec4 vColor;
// varying vec2 vPosition;

// void main(void) {
// vec3 offsetCommon = positions * project_size(instanceRadius);
// vec3 positionCommon = project_position(instancePositions, instancePositions64Low);
// gl_Position = project_common_position_to_clipspace(vec4(positionCommon + offsetCommon, 1.0));

// vPosition = positions.xy;
// vColor = instanceColors;
// }`

vertexShader = `
attribute vec3 positions;
attribute vec3 instancePositions;
attribute vec3 instancePositions64Low;
attribute float instanceRadius;
attribute vec4 instanceColors;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {
vec3 positionCommon = project_position(instancePositions + positions * instanceRadius, instancePositions64Low);
gl_Position = project_common_position_to_clipspace(vec4(positionCommon, 1.0));

vPosition = positions.xy;
vColor = instanceColors;
}`
Insert cell
// gl_FragColor = vec4(vColor.rgb, vColor.a);

fragmentShader = `
precision highp float;

uniform float smoothRadius;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {
gl_FragColor = vec4(vColor.rgb, 1.0);
}`;
Insert cell
Insert cell
getModel = {
const {Model, Geometry} = luma;
return gl => {
// Four corners of the quad
const positions = new Float32Array([-1, -1,
-1, 1,
1, 1,
1, -1]);
const geometry = new Geometry({

drawMode: gl.TRIANGLE_FAN,
// drawMode: gl.TRIANGLES, // default
vertexCount: 4,
attributes: {
positions: {size: 2, value: positions}
}
});
return new Model(gl, {vs: vertexShader, fs: fragmentShader, geometry, isInstanced: true});
}
}
Insert cell
Insert cell
layer2 = {
const {Layer} = deck;
class ScatterplotLayer extends Layer {
initializeState() {
const {gl} = this.context;

// Register attributes
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: gl.DOUBLE,
accessor: 'getPosition'
},
instanceRadius: {
size: 1,
accessor: 'getRadius',
defaultValue: 1
},
instanceColors: {
size: 4,
normalized: true,
type: gl.UNSIGNED_BYTE,
accessor: 'getColor',
defaultValue: [0, 0, 0, 255]
}
});
// Save the model in layer state
this.setState({
model: getModel(gl)
});
}
updateState() {
// Retrieve the model from layer state
this.state.model.setUniforms({
// smoothRadius: this.props.smoothRadius
});
}
}
ScatterplotLayer.layerName = 'ScatterplotLayer';
ScatterplotLayer.defaultProps = defaultProps;

const layer = new ScatterplotLayer({
id: `scatterplot-${Date.now()}`,
data,
getPosition: d => d.position,
getRadius: d => d.size,
getColor: d => d.color
});
deckgl.setProps({
layers: [layer],
getTooltip: ({object}) => object && object.message
});
return layer;
}
Insert cell
Insert cell
layer2.getAttributeManager().getAttributes();
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more