Usage of multiple images in Particle.js - image

I have a project which uses Particle.JS to create a canvas with moving image particles. The problem i am currently having is that every image is being reloaded constantly after a certain amount of time while it's already loaded.
config.json
"particles": {
"number": {
"value": 10,
"density": {
"enable": false,
"value_area": 100
}
},
"color": {
"value": "#ffffff"
},
"shape": {
"type": ["image0", "image1", "image2", "image3", "image4", "image5"],
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
},
"image": {
"src": "images/0.png",
"width": 100,
"height": 100
}
},
I used this case to filter out all image types in the switch.(pJS.fn.particle.prototype.draw)
particles.js
case (p.shape.match(/image/) || {}).input:
if(pJS.tmp.img_type == 'svg'){
var img_obj = p.img.obj;
}else{
var img_obj = pJS.tmp.img_obj;
}
var element = document.createElement('img');
var number = p.shape.replace("image", "")
element.src = './styles/eles/theme/images/particlelogo/'+number+'.png';
img_obj = element;
if(img_obj){
draw();
}
break;
Original particle.js github link

"shape": {
"type": "images",
"stroke": {
"width": 0,
"color": "#000"
},
"polygon": {
"nb_sides": 6
},
"images": [
{
"src": "img/shapes/0.svg",
"width": 100,
"height": 100
},
{
"src": "img/shapes/1.svg",
"width": 100,
"height": 100
}
]
},

Related

How to allow AJAX endpoints in mkdocs

Visual is to get this working in mkdocs driven by a local AJAX server.
This one is hard to give an example to but I will. Before I do that, the problem is that I want to use various ajax endpoints to drive Vega visuals in MkDocs. But I run into the CORS permissions.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://machine1:8080/dataflare. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
I have struggled to find documentation on how to solve this so, does anyone know how do we enable this in mkdocs?
The sample is long but this works until you use a local data source other than the vega data. When you change the AJAX endpoint from "url": "https://vega.github.io/vega/data/flare.json" to http://myhost:8080/shortflare (or fullflare endpoint) you get the CORS permission error above.
So should the client mkdocs enable the endpoint as safe cross site source or should bottle AJAX be sending a CORS header? I dont understand why the original AJAX works when the bottle endpoint does not work.
Now the hard part. Showing an example.
This simple bottle server will be the AJAX endpoint mimic of the original flaredata :
sample of data is on http://myhost:8080/shortflare
full dataset http://myhost:8080/fullflare is served via proxy from https://vega.github.io/vega/data/flare.json
from bottle import route, run, template
import requests
DATAFLARE = '''
[
{
"id": 1,
"name": "flare"
},
{
"id": 2,
"name": "analytics",
"parent": 1
},
{
"id": 3,
"name": "cluster",
"parent": 2
},
{
"id": 4,
"name": "AgglomerativeCluster",
"parent": 3,
"size": 3938
}
]
'''
#route('/shortflare')
def getflare():
return DATAFLARE
#route('/fullflare')
def proxyflare():
return requests.get('https://vega.github.io/vega/data/flare.json').text
if __name__ == "__main__":
run(host='0.0.0.0', port=8080, debug=True)
mkdocs setup (i.e mkdocs.yml)
site_name: VEGA
dev_addr: '0.0.0.0:2001'
theme:
name: material
nav_style: dark
palette:
accent: pink
primary: lime
plugins:
- search
- charts
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: vegalite
class: vegalite
format: !!python/name:mkdocs_charts_plugin.fences.fence_vegalite
extra_javascript:
- https://cdn.jsdelivr.net/npm/vega#5
- https://cdn.jsdelivr.net/npm/vega-lite#5
- https://cdn.jsdelivr.net/npm/vega-embed#6
ve requires
mkdocs==1.2.3
mkdocs-charts-plugin==0.0.6
mkdocs-material==7.3.6
and the markdown to make the vegalite graphic (add this in index.md or anywhere)
Relational maps.
```vegalite
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "An example of Cartesian layouts for a node-link diagram of hierarchical data.",
"width": 600,
"height": 1600,
"padding": 5,
"signals": [
{
"name": "labels", "value": true,
"bind": {"input": "checkbox"}
},
{
"name": "layout", "value": "tidy",
"bind": {"input": "radio", "options": ["tidy", "cluster"]}
},
{
"name": "links", "value": "diagonal",
"bind": {
"input": "select",
"options": ["line", "curve", "diagonal", "orthogonal"]
}
},
{
"name": "separation", "value": false,
"bind": {"input": "checkbox"}
}
],
"data": [
{
"name": "tree",
"url": "https://vega.github.io/vega/data/flare.json",
"transform": [
{
"type": "stratify",
"key": "id",
"parentKey": "parent"
},
{
"type": "tree",
"method": {"signal": "layout"},
"size": [{"signal": "height"}, {"signal": "width - 100"}],
"separation": {"signal": "separation"},
"as": ["y", "x", "depth", "children"]
}
]
},
{
"name": "links",
"source": "tree",
"transform": [
{ "type": "treelinks" },
{
"type": "linkpath",
"orient": "horizontal",
"shape": {"signal": "links"}
}
]
}
],
"scales": [
{
"name": "color",
"type": "linear",
"range": {"scheme": "magma"},
"domain": {"data": "tree", "field": "depth"},
"zero": true
}
],
"marks": [
{
"type": "path",
"from": {"data": "links"},
"encode": {
"update": {
"path": {"field": "path"},
"stroke": {"value": "#ccc"}
}
}
},
{
"type": "symbol",
"from": {"data": "tree"},
"encode": {
"enter": {
"size": {"value": 100},
"stroke": {"value": "#fff"}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"fill": {"scale": "color", "field": "depth"}
}
}
},
{
"type": "text",
"from": {"data": "tree"},
"encode": {
"enter": {
"text": {"field": "name"},
"fontSize": {"value": 9},
"baseline": {"value": "middle"}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"dx": {"signal": "datum.children ? -7 : 7"},
"align": {"signal": "datum.children ? 'right' : 'left'"},
"opacity": {"signal": "labels ? 1 : 0"}
}
}
}
]
}
```
I actually found out why. The bottle server would need to send the proper header. This is done by adding these few lines to the bottle server.
from bottle_cors_plugin import cors_plugin
from bottle import app
app = app()
app.install(cors_plugin('*'))
if __name__ == "__main__":
run(app=app, host='0.0.0.0', port=8080, debug=True)

Adaptive card on Select Item event of Search Message Extension

I am trying to return an adaptive card when search result is selected in Search Message extension. Below is my class to return the card:
import { IEntity } from '../model/IEntity';
import { Attachment, CardFactory } from "botbuilder";
export class AccountResultCard {
public static getCard(account: IEntity): Attachment {
// Get map
let logo = `https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg`;
let url = `https://pbs.twimg.com/docs/test.aspx?GoPage=AccountCard.aspx?AccountID='${account.ID}'&_Division_='${account.Division}'`;
const accountDetail = account.Code ? account.Code + ' - ' + account.Description : '';
const accountcontextinfo1 = account.ContextInfo[1] ? account.ContextInfo[1] : '';
const accountcontextinfo2 = account.ContextInfo[3] ? account.ContextInfo[3] + ' '+ account.ContextInfo[2] : '';
const card = CardFactory.adaptiveCard(
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"text": "Title"
},
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": accountDetail
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": accountcontextinfo1,
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"text": accountcontextinfo2,
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"text": "Missing state and country",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "Image",
"style": "Person",
"url": logo,
"size": "Small"
}
],
"width": "auto"
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Open in Aurora",
"url": url,
"width": "Small",
"style": "default"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2"
}
);
return card;
}
}
and below is my select item event:
public async handleTeamsMessagingExtensionSelectItem(
context: TurnContext,
obj: any
): Promise<any> {
const item = this.response.find(e => e.ID == obj.name);
return {
composeExtension: {
type: "result",
attachmentLayout: "list",
attachments: [AccountResultCard.getCard(item)],
},
};
}
But it never return anything. I deleted everything from get card and sent just a one single text block with hard coded value in body. That also didn't work.Can you guys suggest me where I am doing wrong.
I was able to send the adaptive card in SelectItem event by changing the code to :
return {
composeExtension: {
type: "result",
attachmentLayout: "list",
attachments: [{preview: CardFactory.heroCard("title", ""),
...yourAdaptiveCard}],
},
};
This has been answered here.

Algorithm for creating nested structure

I have following structure:
{
"list": [
{ "depth": 0, "data": "lorem1" },
{ "depth": 1, "data": "lorem2" },
{ "depth": 2, "data": "lorem3" },
{ "depth": 2, "data": "lorem4" },
{ "depth": 0, "data": "lorem5" },
{ "depth": 1, "data": "lorem6" },
{ "depth": 1, "data": "lorem7" },
{ "depth": 2, "data": "lorem8" }
]
}
I am looking for an algorithm on how to create from that depth a parent-child-like, nested structure.
{
"list": [{
"depth": 0,
"data": "lorem1",
"children": [{
"depth": 1,
"data": "lorem2",
"children": [{
"depth": 2,
"data": "lorem3",
"children": [],
}, {
"depth": 2,
"data": "lorem4",
"children": [],
}]
}]
}, {
"depth": 0,
"data": "lorem5",
"children": [{
"depth": 1,
"data": "lorem6",
"children": [],
}, {
"depth": 1,
"data": "lorem7",
"children": [{
"depth": 2,
"data": "lorem8",
"children": [],
}]
}]
}
]}
The logic is like this:
Assumption: The first item in the list always starts with depth=0
If depth is larger than the last, it must be child of this last one
I can not get this to work. It should be recursive to have infinite nesting/depth levels.
Thank you guys for the help!
You can use a stack to keep track of the current path in the tree. When depth increases from one to the next, then push the new node also on that stack. If not, pop items from the stack until the right depth is reached.
Then you always know in which children collection you need to add the new node.
Here is an runnable implementation in JavaScript:
function algo(list) {
// Create a dummy node to always stay at the bottom of the stack:
let stack = [
{ "depth": -1, "data": "(root)", "children": [] }
];
for (let node of list) {
let newNode = { ...node, children: [] }; // Copy and add children property
if (newNode.depth >= stack.length || newNode.depth < 0) throw "Invalid depth";
while (newNode.depth < stack.length - 1) stack.pop();
stack[stack.length - 1].children.push(newNode);
stack.push(newNode);
}
return stack[0].children;
}
// Demo
let data = {
"list": [
{ "depth": 0, "data": "lorem1" },
{ "depth": 1, "data": "lorem2" },
{ "depth": 2, "data": "lorem3" },
{ "depth": 2, "data": "lorem4" },
{ "depth": 0, "data": "lorem5" },
{ "depth": 1, "data": "lorem6" },
{ "depth": 1, "data": "lorem7" },
{ "depth": 2, "data": "lorem8" }
]
}
// Create a new structure, and load the transformed list in its list property:
let result = {
"list": algo(data.list)
};
// Show result
console.log(result);
To answer to your request to do this without dummy node:
function algo(list) {
let result = [];
let stack = [];
for (let node of list) {
let newNode = { ...node, children: [] }; // Copy and add children property
if (newNode.depth > stack.length || newNode.depth < 0) throw "Invalid depth";
while (newNode.depth < stack.length) stack.pop();
if (!stack.length) result.push(newNode);
else stack[stack.length - 1].children.push(newNode);
stack.push(newNode);
}
return result;
}
// Demo
let data = {
"list": [
{ "depth": 0, "data": "lorem1" },
{ "depth": 1, "data": "lorem2" },
{ "depth": 2, "data": "lorem3" },
{ "depth": 2, "data": "lorem4" },
{ "depth": 0, "data": "lorem5" },
{ "depth": 1, "data": "lorem6" },
{ "depth": 1, "data": "lorem7" },
{ "depth": 2, "data": "lorem8" }
]
}
// Create a new structure, and load the transformed list in its list property:
let result = {
"list": algo(data.list)
};
// Show result
console.log(result);

How to customize the Label Frequency when zoom in [amChart(serial)]

I am using amChart(serial) for showing graph. I have data of length more than 4000. I managed to show the last label value of category axis through "labelFunction" : formatLabel,. But now the problem is to set the label frequency =10. whenever a user zoom in the graph.
Please help, Thanks in advance.
here is code,
var chart= AmCharts.makeChart("rmChart",
{
"theme": "light",
"autoMargins": true, "chartScrollbar": {},
"type": "serial",
"categoryField": 'x-distance',
"legend": {
"useGraphSettings": false
},
"zoomControl": {
"panControlEnabled": false,
"zoomControlEnabled": false
},
"categoryAxis": {
"autoGridCount":false,
"includeHidden":true,
"dashLength":1,
" axisAlpha":1,
"gridPosition": "start",
"labelFunction" : formatLabel,
"minHorizontalGap":20,
"title": "Distance In Meters [m]"
},
"chartCursor": {
"zoomable":true,
"enabled": true,
},
"chartScrollbar": {
"enabled": false
},
"trendLines": [],
"valueAxes": [{
}],
"graphs": [
{
"balloonText": "Distance:[[x-axis]] dB:[[y-axis-0]]",
"fillAlphas": 0,
"fillToAxis": "x",
"lineAlpha": 1,
"valueField": "x-axis",
"valueField": "y-axis-0",
"lineColor": "#FF6600",
},
],
"guides": [],
"valueAxes": [
{
"id": "ValueAxis-1",
"title": "Strength [dB]",
"titleFontSize": 15,
}
],
"balloon": {},
"legend": {
"enabled": false,
"useGraphSettings": false
},
"titles": [
{
"id": "Title-1",
"size": 15,
"text": data[0]["date"]
}
],
"dataProvider": data,
export: {
enabled: false
},
"export": {
"enabled": true,
"menu": []
},
"chartScrollbar": {
"enabled":false
},
});
function formatLabel(value, valueText, axis) {
if (valueText.category%2==0) {
var lastNumber = (Math.abs(valueText.category) % 10)
if (lastNumber==0) {
return valueText.category;
}
}
if (valueText.category==data.length-1) {
return valueText.category;
}
else{
return "";
}
}
here is link to the jsfiddle
https://jsfiddle.net/8zw9h007/10/

Calculating GEO transformation params in Vega/d3

Copy this code to https://en.wikipedia.org/wiki/Special:GraphSandbox
The scale and translate parameters of the geo transformation were manually set to match the width & height of the image (see red crosses). How can I make it so that geo transformation matches the entire graph size (or maybe some signal values) automatically, without the manual adjustments?
UPDATE: The translation parameter should have been set to HALF of WIDTH and HEIGHT of the image. See the answer below, and center should have been set to [0,0]. For equirectangular projection, the graph size should have ration 2:1.
{
"version": 2, "width": 800, "height": 400, "padding": 0,
"data": [
{
"name": "data",
"values": [
{"lat":0, "lon":0},
{"lat":90, "lon":-180},
{"lat":-90, "lon":180}
]
}
],
"marks": [
{
"type": "image",
"properties": {
"enter": {
"url": {"value": "wikirawupload:{{filepath:Earthmap1000x500compac.jpg|190}}"},
"width": {"signal": "width"},
"height": {"signal": "height"}
}
}
},
{
"name": "points",
"type": "symbol",
"from": {
"data": "data",
"transform": [{
"type": "geo",
"projection": "equirectangular",
"scale": 127,
"center": [0,0],
"translate": [400,200],
"lon": "lon",
"lat": "lat"
}]
},
"properties": {
"enter": {
"x": {"field": "layout_x"},
"y": {"field": "layout_y"},
"fill": {"value": "#ff0000"},
"size": {"value": 500},
"shape": {"value": "cross"}
}
}
}
]
}
Found an answer (the example above was updated): The "translate" should be set to the center of the image. The "center" on the other hand should be set to [0,0]. The "scale" for the equirectangular projection needs to be set to width/(2*PI)

Resources