Public
Edited
Apr 16, 2023
Insert cell
Insert cell
Insert cell
num_rows = 10
Insert cell
num_cols = 10
Insert cell
mat_height = 600
Insert cell
mat_width = 600
Insert cell
label_row_width = 30
Insert cell
label_col_height = 30
Insert cell
row_height = (mat_height/num_rows) * 0.99
Insert cell
col_width = (mat_width/num_cols) * 0.99
Insert cell
row_offset = mat_height/num_rows
Insert cell
col_offset = mat_width/num_cols
Insert cell
row_cat_width = 9
Insert cell
col_cat_height = 9
Insert cell
row_cat_offset = 10
// row_cat_offset = 100
Insert cell
col_cat_offset = 10
Insert cell
// position the cats
label_row_x = 15
Insert cell
// position the cats
label_col_y = 25
Insert cell
viz_width = mat_width + label_row_width
Insert cell
viz_height = mat_height + label_col_height
Insert cell
label_buffer = 1
Insert cell
// deck = require.alias({
// // optional dependencies
// h3: {}
// })('deck.gl@8.9.1/dist.min.js')

deck = require.alias({
// optional dependencies
h3: {}
})('deck.gl@latest/dist.min.js')
Insert cell
Insert cell
views = [
new deck.OrthographicView({
id: 'matrix',
x: ( label_row_width + label_buffer)+ 'px',
y: ( label_col_height + label_buffer) + 'px',
width: mat_width + 'px',
height: mat_height + 'px',
controller: {scrollZoom: true, inertia: false, zoomAxis: 'all'},
}),

new deck.OrthographicView({
id: 'rows',
x: '0px',
y: (label_col_height + label_buffer) + 'px',
width: label_row_width + 'px',
height: mat_height + 'px',
controller: {scrollZoom: true, inertia: false, zoomAxis: 'Y'},
}),

new deck.OrthographicView({
id: 'cols',
x: (label_row_width + label_buffer) + 'px',
y: '0px',
width: mat_width + 'px',
height: label_col_height + 'px',
controller: {scrollZoom: true, inertia: false, zoomAxis: 'X'},
}),
]
Insert cell
Insert cell
ini_x = mat_width/2
Insert cell
// not sure why I need to add row_offset?
ini_y = mat_height/2 + row_offset
Insert cell
ini_zoom_x = 0
// ini_zoom_x = -1
Insert cell
ini_zoom_y = 0
// ini_zoom_y = -1
Insert cell
Insert cell
// update zoom_data inplace
update_zoom_data = (zoom_data, viewId, zoom, target) => {
if (viewId === 'matrix') {

// update pans
zoom_data.pan_x = target[0];
zoom_data.pan_y = target[1];

// update zooms
zoom_data.zoom_x = zoom[0]
zoom_data.zoom_y = zoom[1]
} else if (viewId === 'cols') {

// update pan_x
zoom_data.pan_x = target[0]
// update zooms
zoom_data.zoom_x = zoom[0]

// // switch to y zoom
// ////////////////////////
// // update pan_x
// zoom_data.pan_x = target[0]
// // update zooms
// zoom_data.zoom_y = zoom[1]
} else if (viewId === 'rows') {

// update pan_y
zoom_data.pan_y = target[1];
// update zooms
zoom_data.zoom_y = zoom[1]
}
}
Insert cell
// new version with fewer argumentss
redefine_global_view_state = (zoom_data, viewId, zoom, target) => {
var globalViewState

if (viewId === 'matrix') {
globalViewState = {
matrix: {
zoom: [zoom[0], zoom[1]],
target: [target[0], target[1]]
},
rows: {
zoom: [ini_zoom_x, zoom[1]],
target: [label_row_x, target[1]]
},
cols: {
zoom: [zoom[0], ini_zoom_y],
target: [target[0], label_col_y]
},
}
} else if (viewId === 'cols'){
globalViewState = {
matrix: {
zoom: [zoom[0], zoom_data.zoom_y],
target: [target[0], zoom_data.pan_y]
},
rows: {
zoom: [ini_zoom_x, zoom_data.zoom_y],
target: [label_row_x, zoom_data.pan_y]
},
cols: {
zoom: [zoom[0], ini_zoom_y],
target: [target[0], label_col_y]
},
}

} else if (viewId === 'rows'){
globalViewState = {
matrix: {
zoom: [zoom_data.zoom_x, zoom[1]],
target: [zoom_data.pan_x, target[1]]
},
rows: {
zoom: [ini_zoom_x, zoom[1]],
target: [label_row_x, target[1]]
},
cols: {
zoom: [zoom_data.zoom_x, ini_zoom_y],
target: [zoom_data.pan_x, label_col_y]
},
}
}

return globalViewState
}
Insert cell
ini_global_view_state = () => {
let globalViewState = {
matrix: {
// target: target,
target: [ini_x, ini_y],
zoom: [ini_zoom_x, ini_zoom_y],
},
rows: {
target: [label_row_x, ini_y],
zoom: [ini_zoom_x, ini_zoom_y],
},
cols: {
target: [ini_x, label_col_y],
zoom: [ini_zoom_x, ini_zoom_y],
},
}
return globalViewState
}
Insert cell
layerFilter = ({layer, viewport}) => {

// console.log(viewport.id, layer.id)
if (viewport.id === 'matrix' && layer.id === 'matrix-layer'){
return true
} else if (viewport.id === 'rows' && layer.id === 'row-layer'){
return true
} else if (viewport.id === 'cols' && layer.id === 'col-layer'){
return true
} else if (viewport.id === 'rows' && layer.id === 'row-label-layer'){
return true
}
return false
}
Insert cell
{

// this is being used for toggling the zoom state of the main matrix
// var ini_zoom_mode = 'Y'
// probably want to set up mutable variables to be initialize outside of this block of code

let globalViewState = ini_global_view_state()

// initialize zoom_data
// zoom_data keeps track of the state of the zoom
// I do not want to have to deal with a mutable zoom_data defined in a
// separate ObservableHQ cell so I'll just define it here
var zoom_data = {
pan_x: ini_x,
pan_y: ini_y,
zoom_x: ini_zoom_x,
zoom_y: ini_zoom_y,
}

var ini_font_size = 10
var zoom_trigger = 10;

var on_view_state_change = ({viewState, viewId}) => {

zoom_trigger++; // Increment the zoom trigger every time the view state changes
const {zoom, target, offset} = viewState;
globalViewState = redefine_global_view_state(zoom_data, viewId, zoom, target)

console.log(zoom_trigger)
update_zoom_data(zoom_data, viewId, zoom, target)

deckgl.setProps({viewState: {...globalViewState}});

}

var row_label_layer = new deck.TextLayer({
id: 'row-label-layer',
data: row_label_data, // This should be the same data source you used for your other layers
getPosition: d => d.position,
getText: d => d.name, // Replace 'label' with the property in your data that contains the text you want to display
getSize: d => {
console.log('get size', zoom_trigger)
return zoom_trigger
},
getColor: [0, 0, 0], // Text color as an RGBA array
getAngle: 0, // Optional: Text angle in degrees
getTextAnchor: 'end', // middle
getAlignmentBaseline: 'center',
fontFamily: 'Arial',
sizeUnits: 'pixels',
updateTriggers: {
getSize: zoom_trigger
}
});
const deckgl = new deck.DeckGL({
container,
views,
layerFilter,
layers: [mat_layer, row_cat_layer, col_cat_layer, row_label_layer],
getTooltip: ({object}) => object && object.message,
initialViewState: globalViewState,
onViewStateChange: on_view_state_change,
})
}
Insert cell
defaultProps = {
return {
// Center of each circle, in [longitude, latitude, (z)]
getPosition: {type: 'accessor', value: x => x.position},
// Color of each circle, in [R, G, B, (A)]
// getColor: {type: 'accessor', value: [0, 0, 0, 255]},
getColor: {type: 'accessor', value: [0, 0, 0, 50]},
}
}
Insert cell
getMatrixModel = {
const {Model, Geometry} = luma;
return gl => {

// Four corners of the quad
const positions = new Float32Array([0, 0,
0, row_height,
col_width, row_height,
col_width, 0]);
const geometry = new Geometry({
drawMode: gl.TRIANGLE_FAN,
vertexCount: 4,
attributes: {
positions: {
size: 2,
value: positions
}
}
});
return new Model(gl, {vs: vertexShader, fs: fragmentShader, geometry, isInstanced: true});
}
}
Insert cell
Insert cell
Insert cell
mat_data = {

let matrix_index = 0;

var index_row = 0

var num_points = num_rows * num_cols
return new Array(num_points).fill(0).map( _ => {

var index_col = matrix_index % num_cols

if (matrix_index % num_cols === 0){
index_row += 1;
}
var inst_red = parseInt(255 * (index_row/num_rows))
var inst_blue = parseInt(255 * (index_col/num_cols))

var inst_color
if (index_col >= index_row){
inst_color = [0, 0, 255]
} else {
inst_color = [255, 0, 0]
}

var inst_opacity = parseInt(255 * matrix_index/num_points)
const p = {
position: [col_offset * index_col, row_offset * index_row],
color: [inst_color[0], inst_color[1], inst_color[2], inst_opacity]
// color: [0, 0, 0, 0.5]
};
matrix_index += 1;
return p;
});
}
Insert cell
mat_layer = { const {Layer} = deck;
class MatrixLayer extends Layer {
initializeState() {
const {gl} = this.context;

// Register attributes
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: gl.DOUBLE,
accessor: 'getPosition'
},
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: getMatrixModel(gl)
});
}
updateState() {
// Retrieve the model from layer state
this.state.model.setUniforms({
// smoothRadius: this.props.smoothRadius
});
}
}
MatrixLayer.layerName = 'MatrixLayer';
MatrixLayer.defaultProps = defaultProps;

const layer = new MatrixLayer({
id: 'matrix-layer',
data: mat_data,
getPosition: d => d.position,
getColor: d => d.color
// getColor: d => [255, 0, 0, 50]
});

return layer
}

Insert cell
Insert cell
row_cat_data = {

let matrix_index = 0;

var index_row = 0
const num_row_cats = 3

var num_points = num_rows * num_row_cats
return new Array(num_points).fill(0).map( _ => {

var index_col = matrix_index % num_row_cats

if (matrix_index % num_row_cats === 0){
index_row += 1;
}
const p = {
position: [row_cat_offset * index_col, row_offset * index_row],
color: [0, 255, 0, 255]
};
matrix_index += 1;
return p;
});
}
Insert cell
getRowCatModel = {
const {Model, Geometry} = luma;
return gl => {

// Four corners of the quad
const positions = new Float32Array([0, 0,
0, row_height,
row_cat_width, row_height,
row_cat_width, 0]);
const geometry = new Geometry({
drawMode: gl.TRIANGLE_FAN,
vertexCount: 4,
attributes: {
positions: {
size: 2,
value: positions
}
}
});
return new Model(gl, {vs: vertexShader, fs: fragmentShader, geometry, isInstanced: true});
}
}
Insert cell
row_cat_layer = { const {Layer} = deck;
class RowLayer extends Layer {
initializeState() {
const {gl} = this.context;

// Register attributes
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: gl.DOUBLE,
accessor: 'getPosition'
},
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: getRowCatModel(gl)
});
}
updateState() {
// Retrieve the model from layer state
this.state.model.setUniforms({
// smoothRadius: this.props.smoothRadius
});
}
}
RowLayer.layerName = 'RowLayer';
RowLayer.defaultProps = defaultProps;

const layer = new RowLayer({
id: 'row-layer',
data: row_cat_data,
getPosition: d => d.position,
getColor: d => d.color
});

return layer
}

Insert cell
Insert cell
col_cat_data = {

let matrix_index = 0;

var index_row = 0
const num_col_cats = 3

var num_points = num_col_cats * num_cols
return new Array(num_points).fill(0).map( _ => {

var index_col = matrix_index % num_cols

if (matrix_index % num_cols === 0){
index_row += 1;
}
const p = {
position: [col_offset * index_col, col_cat_offset * index_row],
color: [0, 255, 0, 150]
};
matrix_index += 1;
return p;
});
}



Insert cell
getColCatModel = {
const {Model, Geometry} = luma;
return gl => {

// Four corners of the quad
const positions = new Float32Array([0, 0,
0, col_cat_height,
col_width, col_cat_height,
col_width, 0]);
const geometry = new Geometry({
drawMode: gl.TRIANGLE_FAN,
vertexCount: 4,
attributes: {
positions: {
size: 2,
value: positions
}
}
});
return new Model(gl, {vs: vertexShader, fs: fragmentShader, geometry, isInstanced: true});
}
}
Insert cell
col_cat_layer = { const {Layer} = deck;
class ColLayer extends Layer {
initializeState() {
const {gl} = this.context;

// Register attributes
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: gl.DOUBLE,
accessor: 'getPosition'
},
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: getColCatModel(gl)
});
}
updateState() {
// Retrieve the model from layer state
this.state.model.setUniforms({
// smoothRadius: this.props.smoothRadius
});
}
}
ColLayer.layerName = 'ColLayer';
ColLayer.defaultProps = defaultProps;

const layer = new ColLayer({
id: 'col-layer',
data: col_cat_data,
getPosition: d => d.position,
getColor: d => d.color
});

return layer
}

Insert cell
Insert cell
row_label_offset = 30
Insert cell
row_label_data = {

let matrix_index = 0;

var index_row = 0
const num_row_cats = 1

var num_points = num_rows * num_row_cats
return new Array(num_points).fill(0).map( _ => {

var index_col = matrix_index % num_row_cats

if (matrix_index % num_row_cats === 0){
index_row += 1;
}
const p = {
// position: [row_cat_offset * index_col, row_offset * index_row],
position: [row_label_offset , row_offset * index_row + row_offset/2],
name: 'row-' + index_row
};
matrix_index += 1;
return p;
});
}
Insert cell
font_size = 20
Insert cell
// row_label_layer = {

// const layer = new deck.TextLayer({
// id: 'row-label-layer',
// data: row_label_data, // This should be the same data source you used for your other layers
// getPosition: d => d.position,
// getText: d => d.name, // Replace 'label' with the property in your data that contains the text you want to display
// // getSize: d => {
// // return 10
// // },
// getColor: [0, 0, 0], // Text color as an RGBA array
// getAngle: 0, // Optional: Text angle in degrees
// getTextAnchor: 'end', // middle
// getAlignmentBaseline: 'center',
// fontFamily: 'Arial',
// fontWeight: 'bold',
// sizeScale: 0.5,
// sizeMinPixels: 0,
// sizeMaxPixels: 1000,
// sizeUnits: 'meters',
// // updateTriggers: {
// // // getSize: inst_font_size
// // }
// });

// return layer
// }

Insert cell
Insert cell
Insert cell
Insert cell

vertexShader = `

attribute vec3 positions;
attribute vec3 instancePositions;
attribute vec3 instancePositions64Low;
attribute vec4 instanceColors;

varying vec4 vColor;
varying vec2 vPosition;

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

vPosition = positions.xy;
vColor = instanceColors;
}

`
Insert cell
fragmentShader = `

precision highp float;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {

gl_FragColor = vec4(vColor.rgb, vColor.a);

}

`;


// default
/////////////////
// gl_FragColor = vec4(vColor.rgb, 1.0);

// gl_FragColor = vec4(vColor.rgb[0], vColor.rgb[1], vColor.rgb[2], vColor.rgb[3]);
// gl_FragColor = vec4(vColor.rgb, 1.0);
// gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);

// gl_FragColor = vec4(vColor.rgb, 1.0);
// gl_FragColor = vec4(255, 255, 0, 0.5);

// gl_FragColor = vec4(vColor.rgb[0], vColor.rgb[1], vColor.rgb[2], 1.0);
Insert cell
Insert cell
Insert cell
Insert cell
// layer2.getAttributeManager().getAttributes();
Insert cell
Insert cell
luma = deck && window.luma
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