Commit ddb89c68 by Onlynagesha

init

parents
## Introduction
Visualization of C2IC algorithm.
## Usage
1. `cd current_directory`
2. `python -m http.server PORT-NUMBER` (e.g. 8888)
3. Open in web browser with URL 127.0.0.1:PORT-NUMBER (e.g. 127.0.0.1:8888)
4. Parameters can be adjusted via config.json
This diff is collapsed. Click to expand it.
{
"nodeStyles": {
"default": { "size": 6, "style": { "fill": "#444444", "stroke": "#000000", "lineWidth": 1 } },
"boosted": { "size": 10, "style": { "fill": "#444444", "stroke": "#000000", "lineWidth": 3 } },
"Ca": {
"default": { "size": 12, "style": { "fill": "#44BB44", "stroke": "#00FF00", "lineWidth": 1 } },
"source": { "size": 12, "style": { "fill": "#44BB44", "stroke": "#00FF00", "lineWidth": 1 } },
"boosted": { "size": 14, "style": { "fill": "#44BB44", "stroke": "#00FF00", "lineWidth": 5 } },
"animation": { "size": 6, "style": { "fill": "#44BB44", "lineWidth": 0 } }
},
"Ca+": {
"default": { "size": 12, "style": { "fill": "#4488BB", "stroke": "#0088FF", "lineWidth": 1 } },
"source": { "size": 12, "style": { "fill": "#4488BB", "stroke": "#0088FF", "lineWidth": 1 } },
"boosted": { "size": 14, "style": { "fill": "#4488BB", "stroke": "#0088FF", "lineWidth": 5 } },
"animation": { "size": 6, "style": { "fill": "#4488BB", "lineWidth": 0 } }
},
"Cr": {
"default": { "size": 12, "style": { "fill": "#BB4444", "stroke": "#FF0000", "lineWidth": 1 } },
"source": { "size": 12, "style": { "fill": "#BB4444", "stroke": "#FF0000", "lineWidth": 1 } },
"boosted": { "size": 14, "style": { "fill": "#BB4444", "stroke": "#FF0000", "lineWidth": 5 } },
"animation": { "size": 6, "style": { "fill": "#BB4444", "lineWidth": 0 } }
},
"Cr-": {
"default": { "size": 12, "style": { "fill": "#BB8844", "stroke": "#FF8800", "lineWidth": 1 } },
"source": { "size": 12, "style": { "fill": "#BB8844", "stroke": "#FF8800", "lineWidth": 1 } },
"boosted": { "size": 14, "style": { "fill": "#BB8844", "stroke": "#FF8800", "lineWidth": 5 } },
"animation": { "size": 6, "style": { "fill": "#BB8844", "lineWidth": 0 } }
}
},
"edgeStyles": {
"default": { "style": { "stroke": "#444444", "lineWidth": 2, "opacity": 0.1 } },
"Ca": { "style": { "stroke": "#44BB44", "lineWidth": 4, "opacity": 0.65 } },
"Ca+": { "style": { "stroke": "#4488BB", "lineWidth": 4, "opacity": 0.65 } },
"Cr": { "style": { "stroke": "#BB4444", "lineWidth": 4, "opacity": 0.65 } },
"Cr-": { "style": { "stroke": "#BB8844", "lineWidth": 4, "opacity": 0.65 } }
},
"edgeFading": {
"Ca": { "finalOpacity": 0.05, "delta": 0.01 },
"Ca+": { "finalOpacity": 0.05, "delta": 0.01 },
"Cr": { "finalOpacity": 0.05, "delta": 0.01 },
"Cr-": { "finalOpacity": 0.05, "delta": 0.01 }
},
"fitViewPadding": 0.025,
"nAnimationNodes": 5,
"initialSpeed": 1.2,
"speedIncrement": 0.025,
"nFramesPerRound": 30,
"fps": 60
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
import json
from os import times
treeDepth = 8
boostedDepth = 4
nNodes = 2 ** treeDepth - 1
graph = {
'nodes': [{'id': i} for i in range(1, nNodes + 1)],
'edges': []
}
for i in range(1, 2 ** (treeDepth - 1)):
graph['edges'].append({'source': i, 'target': i * 2})
graph['edges'].append({'source': i, 'target': i * 2 + 1})
with open('graph.json', 'w') as file:
file.write(json.dumps(graph, indent=4))
boostedNodes = list(range(2 ** (boostedDepth - 1), 2 ** boostedDepth))
data = {
'boostedNodes': boostedNodes,
'sourceEvents': [
{'timestamp': 0, 'id': 2, 'type': 'Ca'},
{'timestamp': 0, 'id': 3, 'type': 'Cr'}
],
'propagationEvents': []
}
def makePropEvents(node, timestamp, isPositive, isBoosted=False):
if node * 2 > nNodes:
return
if node in boostedNodes:
isBoosted = True
for child in [node * 2, node * 2 + 1]:
childIsBoosted = (child in boostedNodes)
edgeType = ('Ca+' if isBoosted or childIsBoosted else 'Ca') if isPositive else ('Cr-' if isBoosted else 'Cr')
targetType = edgeType if isPositive else ('Cr-' if isBoosted or childIsBoosted else 'Cr')
data['propagationEvents'].append({
'timestamp': timestamp,
'source': node,
'target': child,
'edgeType': edgeType,
'targetType': targetType
})
makePropEvents(child, timestamp + 1, isPositive, isBoosted or childIsBoosted)
makePropEvents(2, 1, True)
makePropEvents(3, 1, False)
with open('data.json', 'w') as file:
file.write(json.dumps(data, indent=4))
\ No newline at end of file
This diff is collapsed. Click to expand it.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html>
<head>
<title>Data Visualization</title>
</head>
<body>
<!-- todo: How to get their heights? -->
<div style="float: left; width: 90%; height: 90%;" id="bigCanvas">
<text style="position: absolute; top: 3%; left: 1%; font-family: sans-serif;
color: #ff4040" cols="1" id="description-infected">
Red: Infected (receives rumor)
</text>
<text style="position: absolute; top: 7%; left: 1%; font-family: sans-serif;
color: #40ff80" cols="1" id="description-protected">
Green: Protected (receives truth)
</text>
<input type="checkbox" style="position: absolute; top: 11%; left: 1%" id="checkbox-auto">
<text style="position: absolute; top: 11%; left: 3%; font-family: sans-serif;
color: black" cols="1" id="text-auto">
Auto play
</text>
<!-- <div style="position: absolute; left: 40%; width: 30%; top: 80%; height: 15%" id="stackedBar"></div> -->
</div>
<!-- d3.js -->
<script src="./d3.v7.min.js"></script>
<!-- AntV G6 for graph display -->
<script src="./g6.min.js"></script>
<!-- AntV G2Plot for Chart display -->
<script src="./g2plot.min.js"></script>
<!-- Common utility components -->
<script src="./utils.js"></script>
<!-- Components for handling input -->
<script src="./input.js"></script>
<!-- Components for handling graph display -->
<script src="./canvas.js"></script>
<!-- Components for handling stacked bar -->
<!-- <script src="./stackbar.js"></script> -->
<!-- Main function -->
<script>
async function main() {
// Config file
const config = await readConfig("./config.json");
// Futures of reading graph and data groups
const graphFut = readGraph("./data/graph.json");
const dataFut = readData("./data/data.json", "Default");
// Joins till all the reading processes finish.
const graph = await graphFut;
const data = await dataFut;
// Size of the big canvas (at left side)
const bigWidth = document.getElementById("bigCanvas").offsetWidth;
// todo: How to get the height of container element? offsetHeight does not work well.
const bigHeight = bigWidth * 0.5;
// // Size of the small canvases (at right side, devided by 3 blocks)
// const smallWidth = document.getElementById("smallCanvas1").offsetWidth;
// // todo: How to get the height of container element? offsetHeight does not work well.
// const smallHeight = smallWidth * 2.0 / 3;
const canvasList = [
createCanvas(graph, data, "bigCanvas", bigWidth, bigHeight, config)
];
// const stackedBarCanvasList = [canvasList[3], canvasList[2], canvasList[1]];
// const stackbar = createStackedBarPlot('stackedBar', stackedBarCanvasList, graph.n);
runVisualization(canvasList, config,
() => document.getElementById("checkbox-auto").checked,
// () => updateStackedBarData(stackbar, stackedBarCanvasList)
);
}
main();
</script>
</body>
</html>
This diff is collapsed. Click to expand it.
/**
* Checks whether the given attribute(s) exists in the given object(s).
*
* The callback function `callback(obj, attr)` is invoked if the attribute does not exist in the object.
* If not provided by user, the default callback raises an error log in the console, and then
* an exception will be thrown.
* @param {Object | Array<Object>} obj The object, or the list of objects to be checked
* @param {string | Array<string>} attr Attribute, or the list of the attributes to check
* @param {function(Object, string)} callback The callback function if given attribute does not exist
* @throws For the default callback, throws an error message if the attribute does not exist.
*/
function checkAttributes(obj, attr, callback = null) {
const objList = obj instanceof Array ? obj : [obj];
const attrList = attr instanceof Array ? attr : [attr];
attrList.forEach((attr) => {
objList.forEach((obj) => {
if (obj[attr] == null) {
if (callback == null) {
errInfo = `Missing config attribute '${attr}'`;
console.error(errInfo + " in object: ", obj);
throw errInfo;
} else {
callback(obj, attr);
}
}
});
});
}
/**
* Checks whether the given attribute(s) exists and matches the specified type in the given object(s).
*
* Each attribute is checked (if exists):
* * If type is a string **literal**, then `typeof obj[attr] == type` is checked;
* * Otherwise, `obj[attr] instanceof type` is checked.
*
* The callback function `callback(obj, attr)` is invoked if the attribute does not exist or has type mismatched in the object.
* If not provided by user, the default callback raises an error log in the console, and then
* an exception will be thrown.
*
* @param {Object | Array<Object>} obj The object, or the list of objects to be checked
* @param {string | Array<string>} attr Attribute, or the list of the attributes to check
* @param {*} type The expected type of each object
* @param {*} callback The callback function if given attribute does not exist
* @throws For the default callback, throws an error message if the attribute does not exist.
* @see checkAttributes
*/
function checkAttributesAs(obj, attr, type, callback = null) {
const objList = obj instanceof Array ? obj : [obj];
const attrList = attr instanceof Array ? attr : [attr];
attrList.forEach((attr) => {
objList.forEach((obj) => {
if (obj[attr] == null) {
if (callback == null) {
errInfo = `Missing attribute '${attr}'`;
console.error(errInfo + " in object: ", obj);
throw errInfo;
} else {
callback(obj, attr);
}
}
else if (typeof type == 'string' && typeof obj[attr] != type) {
if (callback == null) {
errInfo = `Wrong type of attribute '${attr}': Expected typeof '${type}' instead of '${typeof obj[attr]}'`;
console.error(errInfo + " in object: ", obj);
throw errInfo;
} else {
callback(obj, attr);
}
}
else if (typeof type == 'object' && !(obj[attr] instanceof type)) {
if (callback == null) {
errInfo = `Wrong type of attribute '${attr}'`;
console.error(errInfo + " in object: ", obj);
throw errInfo;
} else {
callback(obj, attr);
}
}
});
});
}
/**
* Converts all the given attributes (if exist) to string for each item in the given object list.
* @param {Object | Array} itemList Object, or list of objects for conversion
* @param {...string} attributes Attributes to convert.
*/
function attributesToString(itemList, ...attributes) {
function doConvert(item) {
attributes.forEach((attrName) => {
if (item[attrName] != undefined) {
item[attrName] = item[attrName].toString();
}
});
}
if (itemList instanceof Array) {
itemList.forEach((item) => doConvert(item));
} else {
doConvert(item);
}
}
/**
* Adds an attribute to `dest` as `dest[name] = value`.
*
* Checking is performed before assigning whether another attribute with the same identifier already exists.
* A warning message to console is triggered and the old value is replaced
* if duplicated attribute identifiers are detected.
* @param {Object} dest The object where the new attribute is appended
* @param {String} name Identifier of the new attribute
* @param {any} value Value of the new attribute
*/
function createAttribute(dest, name, value) {
if (dest[name] != undefined) {
console.warn(`WARNING: rewriting attribute '${name}' that already exists.`);
}
dest[name] = value;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment