Public
Edited
Apr 30, 2023
Insert cell
Insert cell
// viewof ini_font_size = Inputs.range([0, 100], {label: "Amount", step: 1})
mutable ini_font_size = 15/num_rows
Insert cell
Insert cell
num_rows = 10
Insert cell
num_cols = 10
Insert cell
num_rows * num_cols / 1e6
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
deckgl = {

// 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 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)

mutable ini_font_size = 15 * zoom_data.zoom_x
update_zoom_data(zoom_data, viewId, zoom, target)

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

}
const deckgl = new deck.DeckGL({
container,
views,
layerFilter,
// getTooltip: ({object}) => object && object.message,

getTooltip: ({object}) => {
if (object) {
// console.log("Object:", object); // Log the object to see if it's being populated
return {
html: `Row: ${object.row}<br>Column: ${object.col}`,
// html: `${object.message}`,
style: {color: "white"},
};
}
},

initialViewState: globalViewState,
onViewStateChange: on_view_state_change,
})

return deckgl
}
Insert cell
deckgl.setProps({layers: layers});
Insert cell
// deckgl.setProps({getTooltip: ({object}) => object && object.message ))
Insert cell
// ini_font_size = 10
Insert cell
Insert cell
// layers = [mat_layer, row_cat_layer, col_cat_layer, row_label_layer]
layers = [mat_layer]
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;
// model object creation
// import {picking} from '@deck.gl/core';
// deckgl.picking

const {picking} = deck

// console.log(picking)
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,
// model object creation
modules: [picking]
});
}
}
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],
value: ((index_row/num_rows) + (index_col/num_cols))/2,
row: index_row,
// seem to need to add index to col
col: index_col + 1,
};
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]
},
// createing a picking color attribute
customPickingColors: {
size: 3,
type: gl.UNSIGNED_BYTE,
update: this.calculatePickingColors
// update: calculatePickingColors
}
});
// 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
});
}

//////////////////////////////////////////////////
calculatePickingColors(attribute) {
const {data} = this.props;
const {value, size} = attribute;
let i = 0;

// for (const object of data) {
// console.log(object)
// // Assuming each object has a unique id (not the case)
// const pickingColor = this.encodePickingColor(object.id);
// value[i++] = pickingColor[0];
// value[i++] = pickingColor[1];
// value[i++] = pickingColor[2];
// }

let index = 0;
for (const object of data) {
// console.log(index)
// Use the index index instead of object.id
const pickingColor = this.encodePickingColor(index);
value[index * 3] = pickingColor[0];
value[index * 3 + 1] = pickingColor[1];
value[index * 3 + 2] = pickingColor[2];
index++;
}
}

// // Override getPickingInfo to provide meaningful information
// getPickingInfo({info}) {
// // console.log('getPickingInfo', info)
// // You can add or modify fields in the info object as needed
// if (info.index >= 0) {
// const object = this.props.data[info.index];
// console.log('object ->', object)
// info.object = object;
// info.message = `Row: ${object.row}, Column: ${object.col}`;
// }
// return info;
// }

//////////////////////////////////////////////////
}
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]
pickable: true,
// onHover: (info, event) => console.log('Hovered:', info, event),
});

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;

attribute vec3 customPickingColors;

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;

picking_setPickingColor(customPickingColors);
}

`
Insert cell
fragmentShader = `

precision highp float;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {
gl_FragColor = vec4(vColor.rgb, vColor.a);

// Should be the last Fragment shader instruction that updates gl_FragColor
gl_FragColor = picking_filterPickingColor(gl_FragColor);

}

`;


// 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