Raphael Example Modification - New Baseline on Analytics - javascript

I'm attempting to integrate some charts into a project and I've got this one working just like the example, but properly displaying my data, but What I'd like to be able to do is change the baseline from 0 to another value as most of my data is points > 700, making the line relatively flat, as they are all high values.
Can anyone tell me where the baseline value is set in this? I'm not much of a javascript guy, so I'm having trouble.
Edit: I'd also like to be able to set a Fixed Top Line as well, ideally, if anyone sees an easy way to do both?
Edit2: Found the method of fixing the Topline: There's a "max" variable. Just alter that.
The Example is here. http://raphaeljs.com/analytics.html
The Javascript in Question is this:
Raphael.fn.drawGrid = function (x, y, w, h, wv, hv, color) {
color = color || "#000";
var path = ["M", Math.round(x) + .5, Math.round(y) + .5, "L", Math.round(x + w) + .5, Math.round(y) + .5, Math.round(x + w) + .5, Math.round(y + h) + .5, Math.round(x) + .5, Math.round(y + h) + .5, Math.round(x) + .5, Math.round(y) + .5],
rowHeight = h / hv,
columnWidth = w / wv;
for (var i = 1; i < hv; i++) {
path = path.concat(["M", Math.round(x) + .5, Math.round(y + i * rowHeight) + .5, "H", Math.round(x + w) + .5]);
}
for (i = 1; i < wv; i++) {
path = path.concat(["M", Math.round(x + i * columnWidth) + .5, Math.round(y) + .5, "V", Math.round(y + h) + .5]);
}
return this.path(path.join(",")).attr({stroke: color});
};
$(function () {
$("#data").css({
position: "absolute",
left: "-9999em",
top: "-9999em"
});
});
window.onload = function () {
function getAnchors(p1x, p1y, p2x, p2y, p3x, p3y) {
var l1 = (p2x - p1x) / 2,
l2 = (p3x - p2x) / 2,
a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y));
a = p1y < p2y ? Math.PI - a : a;
b = p3y < p2y ? Math.PI - b : b;
var alpha = Math.PI / 2 - ((a + b) % (Math.PI * 2)) / 2,
dx1 = l1 * Math.sin(alpha + a),
dy1 = l1 * Math.cos(alpha + a),
dx2 = l2 * Math.sin(alpha + b),
dy2 = l2 * Math.cos(alpha + b);
return {
x1: p2x - dx1,
y1: p2y + dy1,
x2: p2x + dx2,
y2: p2y + dy2
};
}
// Grab the data
var labels = [],
data = [];
$("#data tfoot th").each(function () {
labels.push($(this).html());
});
$("#data tbody td").each(function () {
data.push($(this).html());
});
// Draw
var width = 800,
height = 250,
leftgutter = 30,
bottomgutter = 20,
topgutter = 20,
colorhue = .6 || Math.random(),
color = "hsl(" + [colorhue, .5, .5] + ")",
r = Raphael("holder", width, height),
txt = {font: '12px Helvetica, Arial', fill: "#fff"},
txt1 = {font: '10px Helvetica, Arial', fill: "#fff"},
txt2 = {font: '12px Helvetica, Arial', fill: "#000"},
X = (width - leftgutter) / labels.length,
max = Math.max.apply(Math, data),
Y = (height - bottomgutter - topgutter) / max;
r.drawGrid(leftgutter + X * .5 + .5, topgutter + .5, width - leftgutter - X, height - topgutter - bottomgutter, 10, 10, "#000");
var path = r.path().attr({stroke: color, "stroke-width": 4, "stroke-linejoin": "round"}),
bgp = r.path().attr({stroke: "none", opacity: .3, fill: color}),
label = r.set(),
lx = 0, ly = 0,
is_label_visible = false,
leave_timer,
blanket = r.set();
label.push(r.text(60, 12, "24 hits").attr(txt));
label.push(r.text(60, 27, "22 September 2008").attr(txt1).attr({fill: color}));
label.hide();
var frame = r.popup(100, 100, label, "right").attr({fill: "#000", stroke: "#666", "stroke-width": 2, "fill-opacity": .7}).hide();
var p, bgpp;
for (var i = 0, ii = labels.length; i < ii; i++) {
var y = Math.round(height - bottomgutter - Y * data[i]),
x = Math.round(leftgutter + X * (i + .5)),
t = r.text(x, height - 6, labels[i]).attr(txt).toBack();
if (!i) {
p = ["M", x, y, "C", x, y];
bgpp = ["M", leftgutter + X * .5, height - bottomgutter, "L", x, y, "C", x, y];
}
if (i && i < ii - 1) {
var Y0 = Math.round(height - bottomgutter - Y * data[i - 1]),
X0 = Math.round(leftgutter + X * (i - .5)),
Y2 = Math.round(height - bottomgutter - Y * data[i + 1]),
X2 = Math.round(leftgutter + X * (i + 1.5));
var a = getAnchors(X0, Y0, x, y, X2, Y2);
p = p.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
bgpp = bgpp.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
}
var dot = r.circle(x, y, 4).attr({fill: "#333", stroke: color, "stroke-width": 2});
blanket.push(r.rect(leftgutter + X * i, 0, X, height - bottomgutter).attr({stroke: "none", fill: "#fff", opacity: 0}));
var rect = blanket[blanket.length - 1];
(function (x, y, data, lbl, dot) {
var timer, i = 0;
rect.hover(function () {
clearTimeout(leave_timer);
var side = "right";
if (x + frame.getBBox().width > width) {
side = "left";
}
var ppp = r.popup(x, y, label, side, 1),
anim = Raphael.animation({
path: ppp.path,
transform: ["t", ppp.dx, ppp.dy]
}, 200 * is_label_visible);
lx = label[0].transform()[0][1] + ppp.dx;
ly = label[0].transform()[0][2] + ppp.dy;
frame.show().stop().animate(anim);
label[0].attr({text: data + " hit" + (data == 1 ? "" : "s")}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
label[1].attr({text: lbl + " September 2008"}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
dot.attr("r", 6);
is_label_visible = true;
}, function () {
dot.attr("r", 4);
leave_timer = setTimeout(function () {
frame.hide();
label[0].hide();
label[1].hide();
is_label_visible = false;
}, 1);
});
})(x, y, data[i], labels[i], dot);
}
p = p.concat([x, y, x, y]);
bgpp = bgpp.concat([x, y, x, y, "L", x, height - bottomgutter, "z"]);
path.attr({path: p});
bgp.attr({path: bgpp});
frame.toFront();
label[0].toFront();
label[1].toFront();
blanket.toFront();
};

It's been a while since you posted the question, but maybe the answer is still helpful for someone else:
You need to add some extra variables to get the minimum data and to set a new baseline:
The maximum value is already defined here.
max = Math.max.apply(Math, data), //e.g. 800
Now you just do the same to get the minimum value:
min = Math.min.apply(Math, data), //e.g. 700
From these two variables you can get the range of the values. Use 'range' instead of 'max' to calculate Y.
range = max - min, //e.g 100
Y = (height - bottomgutter - topgutter ) / range;
Now jump to the Loop where y, Y0 and Y2 are defined and change
y = Math.round(height - bottomgutter - Y * data[i]),
Y0 = Math.round(height - bottomgutter - Y * data[i - 1]),
Y2 = Math.round(height - bottomgutter - Y * data[i + 1]),
to:
y = Math.round(height - bottomgutter - Y * data[i] + min * Y),
Y0 = Math.round(height - bottomgutter - Y * data[i - 1] + min * Y),
Y2 = Math.round(height - bottomgutter - Y * data[i + 1] + min * Y),
And if you prefer copy&paste, that is the complete edited window.onload function() with a new baseline:
window.onload = function () {
function getAnchors(p1x, p1y, p2x, p2y, p3x, p3y) {
var l1 = (p2x - p1x) / 2,
l2 = (p3x - p2x) / 2,
a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y));
a = p1y < p2y ? Math.PI - a : a;
b = p3y < p2y ? Math.PI - b : b;
var alpha = Math.PI / 2 - ((a + b) % (Math.PI * 2)) / 2,
dx1 = l1 * Math.sin(alpha + a),
dy1 = l1 * Math.cos(alpha + a),
dx2 = l2 * Math.sin(alpha + b),
dy2 = l2 * Math.cos(alpha + b);
return {
x1: p2x - dx1,
y1: p2y + dy1,
x2: p2x + dx2,
y2: p2y + dy2
};
}
// Grab the data
var labels = [],
data = [];
$("#data tfoot th").each(function () {
labels.push($(this).html());
});
$("#data tbody td").each(function () {
data.push($(this).html());
});
// Draw
var width = 800,
height = 250,
leftgutter = 30,
bottomgutter = 20,
topgutter = 20,
colorhue = .6 || Math.random(),
color = "hsl(" + [colorhue, .5, .5] + ")",
r = Raphael("holder", width, height),
txt = {font: '12px Helvetica, Arial', fill: "#fff"},
txt1 = {font: '10px Helvetica, Arial', fill: "#fff"},
txt2 = {font: '12px Helvetica, Arial', fill: "#000"},
X = (width - leftgutter) / labels.length,
max = Math.max.apply(Math, data),
min = Math.min.apply(Math, data),
range = max - min,
Y = (height - bottomgutter - topgutter ) / range;
r.drawGrid(leftgutter + X * .5 + .5, topgutter + .5, width - leftgutter - X, height - topgutter - bottomgutter, 10, 10, "#000");
var path = r.path().attr({stroke: color, "stroke-width": 4, "stroke-linejoin": "round"}),
bgp = r.path().attr({stroke: "none", opacity: .3, fill: color}),
label = r.set(),
lx = 0, ly = 0,
is_label_visible = false,
leave_timer,
blanket = r.set();
label.push(r.text(60, 12, "24 hits").attr(txt));
label.push(r.text(60, 27, "22 September 2008").attr(txt1).attr({fill: color}));
label.hide();
var frame = r.popup(100, 100, label, "right").attr({fill: "#000", stroke: "#666", "stroke-width": 2, "fill-opacity": .7}).hide();
var p, bgpp;
for (var i = 0, ii = labels.length; i < ii; i++) {
var y = Math.round(height - bottomgutter - Y * data[i] + min * Y),
x = Math.round(leftgutter + X * (i + .5)),
t = r.text(x, height - 6, labels[i]).attr(txt).toBack();
if (!i) {
p = ["M", x, y, "C", x, y];
bgpp = ["M", leftgutter + X * .5, height - bottomgutter, "L", x, y, "C", x, y];
}
if (i && i < ii - 1) {
var Y0 = Math.round(height - bottomgutter - Y * data[i - 1] + min * Y),
X0 = Math.round(leftgutter + X * (i - .5)),
Y2 = Math.round(height - bottomgutter - Y * data[i + 1] + min * Y),
X2 = Math.round(leftgutter + X * (i + 1.5));
var a = getAnchors(X0, Y0, x, y, X2, Y2);
p = p.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
bgpp = bgpp.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
}
var dot = r.circle(x, y, 4).attr({fill: "#333", stroke: color, "stroke-width": 2});
blanket.push(r.rect(leftgutter + X * i, 0, X, height - bottomgutter).attr({stroke: "none", fill: "#fff", opacity: 0}));
var rect = blanket[blanket.length - 1];
(function (x, y, data, lbl, dot) {
var timer, i = 0;
rect.hover(function () {
clearTimeout(leave_timer);
var side = "right";
if (x + frame.getBBox().width > width) {
side = "left";
}
var ppp = r.popup(x, y, label, side, 1),
anim = Raphael.animation({
path: ppp.path,
transform: ["t", ppp.dx, ppp.dy]
}, 200 * is_label_visible);
lx = label[0].transform()[0][1] + ppp.dx;
ly = label[0].transform()[0][2] + ppp.dy;
frame.show().stop().animate(anim);
label[0].attr({text: data + " hit" + (data == 1 ? "" : "s")}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
label[1].attr({text: lbl + " September 2008"}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
dot.attr("r", 6);
is_label_visible = true;
}, function () {
dot.attr("r", 4);
leave_timer = setTimeout(function () {
frame.hide();
label[0].hide();
label[1].hide();
is_label_visible = false;
}, 1);
});
})(x, y, data[i], labels[i], dot);
}
p = p.concat([x, y, x, y]);
bgpp = bgpp.concat([x, y, x, y, "L", x, height - bottomgutter, "z"]);
path.attr({path: p});
bgp.attr({path: bgpp});
frame.toFront();
label[0].toFront();
label[1].toFront();
blanket.toFront();
};

Related

Using a repeater instead of a canvas to curve text

Instead of using a canvas to curve the text to fit on the image. Is it possible instead to use a Repeater to achieve the same effect? Currently my code does what I need it to do with a Canvas. But I'm curious and it also might be better for me to use a Repeater to accomplish what I ultimately want. How can I modify my existing code to achieve this?
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property string letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Image {
id: abcBar
source: "alphabetBar.png"
visible: false
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -10
}
// Rectangle {
// id: body
// anchors.fill: parent
// readonly property real dx: 12
// function midpoint(idx) {
// return 10 + idx * body.dx;
// }
// Repeater {
// anchors.fill: parent
// model: root.letters.length
// Text {
// anchors.horizontalCenter: parent.left
// anchors.horizontalCenterOffset: body.midpoint(index)
// anchors.verticalCenter: parent.verticalCenter
// text: root.letters[index]
// }
// }
// }
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext('2d');
ctx.save();
ctx.canvas.width = 160;
ctx.canvas.height = 432;
ctx.font = "18px Roboto";
ctx.textAlign = "center";
ctx.fillStyle = "#000000"
const centerX = 10;
const centerY = ctx.canvas.height / 2;
const angle = Math.PI;
const radius = 130
ctx.fillStyle = "#000000"
ctx.restore();
ctx.save();
ctx.drawImage(abcBar, 0, 0, abcBar.width, abcBar.height);
console.log('loaded')
ctx.restore();
const args = {
ctx,
// text: "A • D • G • J • M • P • S • V • Z",
text: root.letters,
offset: 0,
G1: {
x: 20,
y: 80,
},
G2: {
x: 190,
y: 230,
},
G3: {
x: 0,
y: 372
},
}
textOnCurve(args);
}
}
function textOnCurve({ ctx, text, offset, G1, G2, G3, G4}){
const x1 = G1.x;
const y1 = G1.y;
const x2 = G2.x;
const y2 = G2.y;
const x3 = G3.x;
const y3 = G3.y;
const x4 = G3.x;
const y4 = G3.y;
ctx.save();
ctx.textAlign = "center";
var widths = [];
for (var i = 0; i < text.length; i++)
{
widths[widths.length] = ctx.measureText(text[i]).width;
}
ctx.beginPath();
var ch = curveHelper(x1, y1, x2, y2, x3, y3, x4, y4);
ctx.stroke();
var pos = offset;
var cpos = 0;
for (var j = 0; j < text.length; j++)
{
pos += widths[j] / 2;
cpos = ch.forward(pos);
ch.tangent(cpos);
ctx.setTransform(ch.vect.x, ch.vect.y, -ch.vect.y, ch.vect.x, ch.vec.x, ch.vec.y);
ctx.fillText(text[j], 0, 0);
pos += widths[j] / 2;
}
ctx.restore();
}
function curveHelper(x1, y1, x2, y2, x3, y3, x4, y4)
{
var tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
var a, b, c, u;
var vec, currentPos, vec1, vect, quad, currentDist;
vec = { x: 0, y: 0 } ;
vec1 = { x: 0, y: 0 } ;
vect = { x: 0, y: 0 } ;
quad = false;
currentPos = 0;
currentDist = 0;
if (x4 === undefined || x4 === null)
{
quad = true;
x4 = x3;
y4 = y3;
}
var estLen = Math.sqrt((x4 - x1)* (x4 - x1)+ (y4 - y1)* (y4 - y1));
var onePix = 1 / estLen;
function posAtC(c)
{
tx1 = x1; ty1 = y1;
tx2 = x2; ty2 = y2;
tx3 = x3; ty3 = y3;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (tx3 - tx2)* c;
ty2 += (ty3 - ty2)* c;
tx3 += (x4 - tx3)* c;
ty3 += (y4 - ty3)* c;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (tx3 - tx2)* c;
ty2 += (ty3 - ty2)* c;
vec.x = tx1 + (tx2 - tx1)* c;
vec.y = ty1 + (ty2 - ty1)* c;
return vec;
}
function posAtQ(c)
{
tx1 = x1; ty1 = y1;
tx2 = x2; ty2 = y2;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (x3 - tx2)* c;
ty2 += (y3 - ty2)* c;
vec.x = tx1 + (tx2 - tx1)* c;
vec.y = ty1 + (ty2 - ty1)* c;
return vec;
}
function forward(dist)
{
var step;
helper.posAt(currentPos);
while (currentDist < dist)
{
vec1.x = vec.x;
vec1.y = vec.y;
currentPos += onePix;
helper.posAt(currentPos);
currentDist += step = Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
}
currentPos -= ((currentDist - dist)/ step)* onePix
currentDist -= step;
helper.posAt(currentPos);
currentDist += Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
return currentPos;
}
function tangentQ(pos)
{
a = (1 - pos)* 2;
b = pos * 2;
vect.x = a * (x2 - x1)+ b * (x3 - x2);
vect.y = a * (y2 - y1)+ b * (y3 - y2);
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
function tangentC(pos)
{
a = (1 - pos)
b = 6 * a * pos;
a *= 3 * a;
c = 3 * pos * pos;
vect.x = -x1 * a + x2 * (a - b)+ x3 * (b - c)+ x4 * c;
vect.y = -y1 * a + y2 * (a - b)+ y3 * (b - c)+ y4 * c;
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
var helper = {
vec: vec,
vect: vect,
forward: forward,
}
if (quad)
{
helper.posAt = posAtQ;
helper.tangent = tangentQ;
} else
{
helper.posAt = posAtC;
helper.tangent = tangentC;
}
return helper
}
}
That is certainly possible:
Rectangle {
id: control
width: Math.min(parent.height, parent.width)
height: width
anchors.centerIn: parent
color: "#40ff0000" //to see where it is going, which helps in positioning
property double startAngle: -45
property double endAngle: 45
Repeater {
model: 26
Text {
property double angle: control.startAngle + (control.endAngle - control.startAngle) * (index / 26)
x: (control.width / 2) * (1 + 0.9 * Math.cos(Math.PI * angle / 180)) - width * 0.5
y: (control.height / 2) * (1 + 0.9 * Math.sin(Math.PI * angle / 180)) - height * 0.5
text: String.fromCharCode(index + 65)
//rotation: angle
}
}
}
In the occasion you want to add a MouseArea to each text or change it to a button, this would also be a wise route to go.
Note that you will have to position the rectangle a bit with respect to your image.

Is it possible to make canvas with background with lines or canvas that isn't a rectangle?

I'm trying to make this one https://massmoca.org/event/walldrawing340/
in Javascript code, using p5.js, but I have no clue how to fill these shapes with lines. Is there any other possibility, like making canvas that is circle or something like that, or I just have to make each shape seperately?
For now I was doing shape by shape, but making triangle and trapezoid is rough...
var sketch = function (p) {
with(p) {
let h,
w,
space;
p.setup = function() {
createCanvas(900, 400);
h = height / 2;
w = width / 3;
space = 10;
noLoop();
};
p.draw = function() {
drawBackground('red', 'blue', 0, 0);
shape('Circle', 'red', 'blue', 0, 0);
drawBackground('yellow', 'red', w, 0);
shape('Square', 'yellow', 'red', w, 0);
drawBackground('blue', 'yellow', 2 * w, 0);
shape('Triangle', 'blue', 'red', 2 * w, 0)
drawBackground('red', 'yellow', 0, h);
shape('Rectangle', 'red', 'blue', 0, h)
drawBackground('yellow', 'blue', w, h);
shape('Trapezoid', 'yellow', 'red', w, h);
drawBackground('blue', 'red', 2 * w, h);
};
function drawBackground(bColor, lColor, x, y) {
fill(bColor)
noStroke();
rect(x, y, w, h)
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < h / space; i++) {
line(0 + x, i * space + y + 10, w + x, i * space + y + 10);
}
}
function shape(shape, bColor, lColor, x, y) {
fill(bColor)
noStroke();
let w1;
switch (shape) {
case 'Circle':
circle(x + w / 2, y + h / 2, h - space * 6);
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < w / space; i++) {
for (let j = 0; j < h; j++) {
pX = i * space + x;
pY = 0 + y + j;
if (pow(x + w / 2 - pX, 2)
+ pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
point(pX, pY);
}
}
}
break;
case 'Square':
w1 = w - (h - space * 6);
rect(x + w1 / 2, y + space * 3, h - space * 6, h - space * 6);
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < 15; i++) {
for (let j = 0; j < h - space * 6; j++) {
point(x + w1 / 2 + i * space, y + space * 3 + j)
}
}
break;
case 'Triangle':
w1 = w - (h - space * 6);
triangle(x + w1 / 2, h - space * 3 + y, x + w / 2, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
for (let i = 0; i < w / space; i++) {
for (let j = 0; j < h; j++) {
pX = i * space + x;
pY = 0 + y + j;
if (pow(x + w / 2 - pX, 2)
+ pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
point(pX, pY);
}
}
}
break;
case 'Rectangle':
w1 = w - (h - space * 6) / 2;
rect(x + w1 / 2, y + space * 3, (h - space * 6) / 2, h - space * 6)
break;
case 'Trapezoid':
w1 = w - (h - space * 6);
quad(x + w1 / 2, h - space * 3 + y, x + w1 / 2 + (h - space * 6) / 4, y + space * 3, x + w1 / 4 + h - space * 6, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
break;
case 'Parallelogram':
w1 = w - (h - space * 6);
quad(x + w1 / 4, h - space * 3 + y, x + w1 / 2, y + space * 3, x + w1 / 2 + h - space * 6, y + space * 3, x + w1 / 4 + h - space * 6, h - space * 3 + y)
break;
break;
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
No messages, everything is working, I just want to know if I have to do so much arduous job...
If you don't need actual line coordinates (for plotting for example), I'd just make most out of createGraphics() to easily render shapes and lines into (taking advantage of the fact that get() returns a p5.Image) and p5.Image's mask() function.
Here's a basic example:
function setup() {
createCanvas(600, 300);
let w = 300;
let h = 150;
let spacing = 12;
let strokeWidth = 1;
const BLUE = color('#005398');
const YELLOW = color('#f9db44');
const RED = color('#dc1215');
bg = getLinesRect(w, h, RED, BLUE, spacing, strokeWidth, true);
fg = getLinesRect(w, h, RED, YELLOW, spacing, strokeWidth, false);
mask = getCircleMask(w, h, w * 0.5, h * 0.5, 100, 0);
image(bg, 0, 0);
image(fg, w, 0);
// render opaque mask (for visualisation only), mask() requires alpha channel
image(getCircleMask(w, h, w * 0.5, h * 0.5, 100, 255),0, h);
// apply mask
fg.mask(mask);
// render bg + masked fg
image(bg, w, h);
image(fg, w, h);
// text labels
noStroke();
fill(255);
text("bg layer", 9, 12);
text("fg layer", w + 9, 12);
text("mask", 9, h + 12);
text("bg + masked fg", w + 9, h + 12);
}
function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
let rect = createGraphics(w, h);
rect.background(bg);
rect.stroke(fg);
rect.strokeWeight(strokeWidth);
if(isHorizontal){
for(let y = 0 ; y < h; y += spacing){
rect.line(0, y + strokeWidth, w, y + strokeWidth);
}
}else{
for(let x = 0 ; x < w; x += spacing){
rect.line(x + strokeWidth, 0, x + strokeWidth, h);
}
}
// convert from p5.Graphics to p5.Image
return rect.get();
}
function getCircleMask(w, h, cx, cy, cs, opacity){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0, opacity);
mask.noStroke();
mask.fill(255);
mask.circle(cx, cy, cs);
// convert p5.Graphics to p5.Image
return mask.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
You can apply the same logic for the rest of the shapes:
function setup() {
createCanvas(1620, 590);
let compWidth = 500;
let compHeight = 250;
let compSpacing= 30;
let lineWeight = 1.5;
let lineSpacing = 12;
const BLUE = color('#005398');
const YELLOW = color('#f9db44');
const RED = color('#dc1215');
// yellow square
circleMask = getCircleMask(compWidth, compHeight, compWidth * 0.5, compHeight * 0.5, 210);
redCircle = getComposition(compWidth, compHeight, RED,
BLUE,
YELLOW,
lineSpacing, lineWeight, circleMask);
// red box
boxMask = getRectMask(compWidth, compHeight, (compWidth - 100) * 0.5, 20, 100, 210);
redBox = getComposition(compWidth, compHeight, RED,
YELLOW,
BLUE,
lineSpacing, lineWeight, boxMask);
// yellow square
squareMask = getRectMask(compWidth, compHeight, 144, 20, 210, 210);
yellowSquare = getComposition(compWidth, compHeight, YELLOW,
RED,
BLUE,
lineSpacing, lineWeight, squareMask);
// yellow trapeze
trapezeMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 115, 25,
150 + 220, 220, 150, 220);
yellowTrapeze = getComposition(compWidth, compHeight, YELLOW,
BLUE,
RED,
lineSpacing, lineWeight, trapezeMask);
// blue triangle
triangleMask = getTriangleMask(compWidth, compHeight, compWidth * 0.5, 25,
150 + 220, 220, 150, 220);
blueTriangle = getComposition(compWidth, compHeight, BLUE,
YELLOW,
RED,
lineSpacing, lineWeight, triangleMask);
// blue parallelogram
parallelogramMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 145, 25,
150 + 145, 220, 150, 220);
blueParallelogram = getComposition(compWidth, compHeight, BLUE,
RED,
YELLOW,
lineSpacing, lineWeight, parallelogramMask);
// render compositions
image(redCircle, compSpacing, compSpacing);
image(redBox, compSpacing, compSpacing + (compHeight + compSpacing));
image(yellowSquare, compSpacing + (compWidth + compSpacing), compSpacing);
image(yellowTrapeze, compSpacing + (compWidth + compSpacing), compSpacing + (compHeight + compSpacing));
image(blueTriangle, compSpacing + (compWidth + compSpacing) * 2, compSpacing);
image(blueParallelogram, compSpacing + (compWidth + compSpacing) * 2, compSpacing + (compHeight + compSpacing));
}
function getComposition(w, h, bgFill, bgStroke, fgStroke, spacing, strokeWidth, mask){
let comp = createGraphics(w, h);
bg = getLinesRect(w, h, bgFill, bgStroke, spacing, strokeWidth, true);
fg = getLinesRect(w, h, bgFill, fgStroke, spacing, strokeWidth, false);
// apply mask
fg.mask(mask);
// render to final output
comp.image(bg, 0, 0);
comp.image(fg, 0, 0);
return comp;
}
function getRectMask(w, h, rx, ry, rw, rh){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.rect(rx, ry, rw, rh);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getCircleMask(w, h, cx, cy, cs){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.circle(cx, cy, cs);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getQuadMask(w, h, x1, y1, x2, y2, x3, y3, x4, y4){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.quad(x1, y1, x2, y2, x3, y3, x4, y4);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getTriangleMask(w, h, x1, y1, x2, y2, x3, y3){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.triangle(x1, y1, x2, y2, x3, y3);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
let rect = createGraphics(w, h);
rect.background(bg);
rect.stroke(fg);
rect.strokeWeight(strokeWidth);
if(isHorizontal){
for(let y = 0 ; y < h; y += spacing){
rect.line(0, y + strokeWidth, w, y + strokeWidth);
}
}else{
for(let x = 0 ; x < w; x += spacing){
rect.line(x + strokeWidth, 0, x + strokeWidth, h);
}
}
// convert from p5.Graphics to p5.Image
return rect.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Probably both rectangles and the triangle could've been drawn using getQuadMask() making good use of coordinates.
Note that I've just eye balled the shapes a bit so they're not going to be perfect, but it should be easy to tweak. Bare in mind the placement of the mask will have an effect of on how the vertical lines will align.
There are probably other ways to get the same visual effect.
For example, using texture() and textureWrap(REPEAT) with beginShape()/endShape(), using pixels for each line and checking intersections before changing direction and colours, etc.
In terms of generating lines for plotting I would start with horizontal lines, doing line to convex polygon intersection to determine where to stop the horizontal lines and start vertical lines. #AgniusVasiliauskas's answer(+1) is good for that approach.
Freya Holmér has a pretty nice visual explanation for the test.
You need linear algebra stuff, basically noticing how vertical line starting/ending Y coordinate changes in relation to line's X coordinate. And of course a lot of experimenting until you get something usable. Something like :
var w = 600
h = 600
sp = 15
var slides = [fcircle, fsquare, ftriangle, ftrapezoid, fparallelogram];
var active = 0;
var ms;
function blines(){
stroke(0);
for (var i=0; i < h; i+=sp) {
line(0,i,w,i);
}
}
function vertlines(calcline) {
for (var x=w/2-w/4+sp; x < w/2+w/4; x+=sp) {
var pnts = calcline(x);
line(pnts[0],pnts[1],pnts[2],pnts[3]);
}
}
function fcircle() {
// cut background
noStroke();
circle(w/2, h/2, w/2);
stroke('red');
// draw figure lines
let calc = function (x){
var sx = x-w/2;
var sy = h/2;
var ey = h/2;
sy += 137*sin(2.5+x/135);
ey -= 137*sin(2.5+x/135);
return [x,sy,x,ey];
}
vertlines(calc);
}
function fsquare() {
// cut background
noStroke();
quad(w/2-w/4, h/2-h/4, w/2+w/4, h/2-h/4,
w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
return [x,h/2-h/4,x,h/2+h/4];
}
vertlines(calc);
}
function ftriangle() {
// cut background
noStroke();
quad(w/2, h/2-h/4, w/2+w/4, h/2+h/4,
w/2-w/4, h/2+h/4, w/2, h/2-h/4);
stroke('red');
// draw figure lines
let calc = function (x){
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.3*inpx*log(inpx)-220);
return [x,ys,x,h/2+h/4];
}
vertlines(calc);
}
function ftrapezoid() {
// cut background
noStroke();
quad(w/2-w/10, h/2-h/4, w/2+w/10, h/2-h/4,
w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.55*inpx*log(inpx)-420);
if (x >= w/2-w/10 && x <= w/2+w/10) {
ys=h/2-h/4;
}
return [x,ys,x,h/2+h/4];
}
vertlines(calc);
}
function fparallelogram() {
// cut background
noStroke();
quad(w/2-w/10, h/2-h/4, w/2+w/7, h/2-h/4,
w/2, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
// guard condition
if (x > w/2+w/7)
return [0,0,0,0];
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.55*inpx*log(inpx)-420);
var ye=h/2+h/4
if (x >= w/2-w/10) {
ys=h/2-h/4;
}
if (x > w/2) {
ye = h/2+h/4;
ye += 0.50*inpx*log(inpx)-870;
}
return [x,ys,x,ye];
}
vertlines(calc);
}
function setup() {
ms = millis();
createCanvas(w, h);
}
function draw() {
if (millis() - ms > 2000) {
ms = millis();
active++;
if (active > slides.length-1)
active = 0;
}
background('#D6EAF8');
fill('#D6EAF8');
blines();
slides[active]();
}
Slideshow DEMO
I have a way to do some of the shapes, but I am not sure about others. One way you could do it is if you know where every point on the outline of the shape is, you could just use a for loop and connect every other point from the top and bottom using the line or rect function. This would be relatively easy with shapes like squares and parallelograms, but I am not sure what functions could be used to get this for the points of a circle or trapezoid.
See more here: https://www.openprocessing.org/sketch/745383

Canvas with same id in html

I have in Ui and I dont know how to solve it.
I Use canvas in my project and base on id it choose what should be happen
Hear is my cod:
<div class="item">
<div class="w3_weather_scroll">
<h4>3:00 PM</h4>
<h5>-5°</h5>
<canvas id="rain" width="30" name="name" height="30"></canvas>
</div>
</div>
<div class="item">
<div class="w3_weather_scroll">
<h4>5:00 PM</h4>
<h5>35°</h5>
<canvas id="wind" width="30" name="name" height="30"></canvas>
</div>
java script:
var icons = new Skycons({"color": "#fff"}),
list = [
document.getElementsByName("name")[0].id,
document.getElementsByName("name")[1].className,
document.getElementsByName("name")[2].id,
document.getElementsByName("name")[3].id,
document.getElementsByName("name")[4].id,
document.getElementsByName("name")[5].id,
document.getElementsByName("name")[6].id,
document.getElementsByName("name")[7].id,
],
i;
document.getElementById("demo").innerHTML=document.getElementsByName("name")[0].id;
for(i = list.length; i--; )
icons.set(list[i], list[i]);
icons.play();
for example if first id is "rain" and second id is"wind" , for first one we have rainy cloud img and second wind .
(function(global) {
"use strict";
/* Set up a RequestAnimationFrame shim so we can animate efficiently FOR
* GREAT JUSTICE. */
var requestInterval, cancelInterval;
(function() {
var raf = global.requestAnimationFrame ||
global.webkitRequestAnimationFrame ||
global.mozRequestAnimationFrame ||
global.oRequestAnimationFrame ||
global.msRequestAnimationFrame ,
caf = global.cancelAnimationFrame ||
global.webkitCancelAnimationFrame ||
global.mozCancelAnimationFrame ||
global.oCancelAnimationFrame ||
global.msCancelAnimationFrame ;
if(raf && caf) {
requestInterval = function(fn, delay) {
var handle = {value: null};
function loop() {
handle.value = raf(loop);
fn();
}
loop();
return handle;
};
cancelInterval = function(handle) {
caf(handle.value);
};
}
else {
requestInterval = setInterval;
cancelInterval = clearInterval;
}
}());
/* Define skycon things. */
/* FIXME: I'm *really really* sorry that this code is so gross. Really, I am.
* I'll try to clean it up eventually! Promise! */
var KEYFRAME = 500,
STROKE = 0.08,
TAU = 2.0 * Math.PI,
TWO_OVER_SQRT_2 = 2.0 / Math.sqrt(2);
function circle(ctx, x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, TAU, false);
ctx.fill();
}
function line(ctx, ax, ay, bx, by) {
ctx.beginPath();
ctx.moveTo(ax, ay);
ctx.lineTo(bx, by);
ctx.stroke();
}
function puff(ctx, t, cx, cy, rx, ry, rmin, rmax) {
var c = Math.cos(t * TAU),
s = Math.sin(t * TAU);
rmax -= rmin;
circle(
ctx,
cx - s * rx,
cy + c * ry + rmax * 0.5,
rmin + (1 - c * 0.5) * rmax
);
}
function puffs(ctx, t, cx, cy, rx, ry, rmin, rmax) {
var i;
for(i = 5; i--; )
puff(ctx, t + i / 5, cx, cy, rx, ry, rmin, rmax);
}
function cloud(ctx, t, cx, cy, cw, s, color) {
t /= 30000;
var a = cw * 0.21,
b = cw * 0.12,
c = cw * 0.24,
d = cw * 0.28;
ctx.fillStyle = color;
puffs(ctx, t, cx, cy, a, b, c, d);
ctx.globalCompositeOperation = 'destination-out';
puffs(ctx, t, cx, cy, a, b, c - s, d - s);
ctx.globalCompositeOperation = 'source-over';
}
function sun(ctx, t, cx, cy, cw, s, color) {
t /= 40000;
var a = cw * 0.25 - s * 0.5,
b = cw * 0.32 + s * 0.5,
c = cw * 0.50 - s * 0.5,
i, p, cos, sin;
ctx.strokeStyle = color;
ctx.lineWidth = s;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
ctx.arc(cx, cy, a, 0, TAU, false);
ctx.stroke();
for(i = 8; i--; ) {
p = (t + i / 8) * TAU;
cos = Math.cos(p);
sin = Math.sin(p);
line(ctx, cx + cos * b, cy + sin * b, cx + cos * c, cy + sin * c);
}
}
function moon(ctx, t, cx, cy, cw, s, color) {
t /= 15000;
var a = cw * 0.29 - s * 0.5,
b = cw * 0.05,
c = Math.cos(t * TAU),
p = c * TAU / -16;
ctx.strokeStyle = color;
ctx.lineWidth = s;
ctx.lineCap = "round";
ctx.lineJoin = "round";
cx += c * b;
ctx.beginPath();
ctx.arc(cx, cy, a, p + TAU / 8, p + TAU * 7 / 8, false);
ctx.arc(cx + Math.cos(p) * a * TWO_OVER_SQRT_2, cy + Math.sin(p) * a * TWO_OVER_SQRT_2, a, p + TAU * 5 / 8, p + TAU * 3 / 8, true);
ctx.closePath();
ctx.stroke();
}
function rain(ctx, t, cx, cy, cw, s, color) {
t /= 1350;
var a = cw * 0.16,
b = TAU * 11 / 12,
c = TAU * 7 / 12,
i, p, x, y;
ctx.fillStyle = color;
for(i = 4; i--; ) {
p = (t + i / 4) % 1;
x = cx + ((i - 1.5) / 1.5) * (i === 1 || i === 2 ? -1 : 1) * a;
y = cy + p * p * cw;
ctx.beginPath();
ctx.moveTo(x, y - s * 1.5);
ctx.arc(x, y, s * 0.75, b, c, false);
ctx.fill();
}
}
function sleet(ctx, t, cx, cy, cw, s, color) {
t /= 750;
var a = cw * 0.1875,
b = TAU * 11 / 12,
c = TAU * 7 / 12,
i, p, x, y;
ctx.strokeStyle = color;
ctx.lineWidth = s * 0.5;
ctx.lineCap = "round";
ctx.lineJoin = "round";
for(i = 4; i--; ) {
p = (t + i / 4) % 1;
x = Math.floor(cx + ((i - 1.5) / 1.5) * (i === 1 || i === 2 ? -1 : 1) * a) + 0.5;
y = cy + p * cw;
line(ctx, x, y - s * 1.5, x, y + s * 1.5);
}
}
function snow(ctx, t, cx, cy, cw, s, color) {
t /= 3000;
var a = cw * 0.16,
b = s * 0.75,
u = t * TAU * 0.7,
ux = Math.cos(u) * b,
uy = Math.sin(u) * b,
v = u + TAU / 3,
vx = Math.cos(v) * b,
vy = Math.sin(v) * b,
w = u + TAU * 2 / 3,
wx = Math.cos(w) * b,
wy = Math.sin(w) * b,
i, p, x, y;
ctx.strokeStyle = color;
ctx.lineWidth = s * 0.5;
ctx.lineCap = "round";
ctx.lineJoin = "round";
for(i = 4; i--; ) {
p = (t + i / 4) % 1;
x = cx + Math.sin((p + i / 4) * TAU) * a;
y = cy + p * cw;
line(ctx, x - ux, y - uy, x + ux, y + uy);
line(ctx, x - vx, y - vy, x + vx, y + vy);
line(ctx, x - wx, y - wy, x + wx, y + wy);
}
}
function fogbank(ctx, t, cx, cy, cw, s, color) {
t /= 30000;
var a = cw * 0.21,
b = cw * 0.06,
c = cw * 0.21,
d = cw * 0.28;
ctx.fillStyle = color;
puffs(ctx, t, cx, cy, a, b, c, d);
ctx.globalCompositeOperation = 'destination-out';
puffs(ctx, t, cx, cy, a, b, c - s, d - s);
ctx.globalCompositeOperation = 'source-over';
}
/*
var WIND_PATHS = [
downsample(63, upsample(8, [
-1.00, -0.28,
-0.75, -0.18,
-0.50, 0.12,
-0.20, 0.12,
-0.04, -0.04,
-0.07, -0.18,
-0.19, -0.18,
-0.23, -0.05,
-0.12, 0.11,
0.02, 0.16,
0.20, 0.15,
0.50, 0.07,
0.75, 0.18,
1.00, 0.28
])),
downsample(31, upsample(16, [
-1.00, -0.10,
-0.75, 0.00,
-0.50, 0.10,
-0.25, 0.14,
0.00, 0.10,
0.25, 0.00,
0.50, -0.10,
0.75, -0.14,
1.00, -0.10
]))
];
*/
var WIND_PATHS = [
[
-0.7500, -0.1800, -0.7219, -0.1527, -0.6971, -0.1225,
-0.6739, -0.0910, -0.6516, -0.0588, -0.6298, -0.0262,
-0.6083, 0.0065, -0.5868, 0.0396, -0.5643, 0.0731,
-0.5372, 0.1041, -0.5033, 0.1259, -0.4662, 0.1406,
-0.4275, 0.1493, -0.3881, 0.1530, -0.3487, 0.1526,
-0.3095, 0.1488, -0.2708, 0.1421, -0.2319, 0.1342,
-0.1943, 0.1217, -0.1600, 0.1025, -0.1290, 0.0785,
-0.1012, 0.0509, -0.0764, 0.0206, -0.0547, -0.0120,
-0.0378, -0.0472, -0.0324, -0.0857, -0.0389, -0.1241,
-0.0546, -0.1599, -0.0814, -0.1876, -0.1193, -0.1964,
-0.1582, -0.1935, -0.1931, -0.1769, -0.2157, -0.1453,
-0.2290, -0.1085, -0.2327, -0.0697, -0.2240, -0.0317,
-0.2064, 0.0033, -0.1853, 0.0362, -0.1613, 0.0672,
-0.1350, 0.0961, -0.1051, 0.1213, -0.0706, 0.1397,
-0.0332, 0.1512, 0.0053, 0.1580, 0.0442, 0.1624,
0.0833, 0.1636, 0.1224, 0.1615, 0.1613, 0.1565,
0.1999, 0.1500, 0.2378, 0.1402, 0.2749, 0.1279,
0.3118, 0.1147, 0.3487, 0.1015, 0.3858, 0.0892,
0.4236, 0.0787, 0.4621, 0.0715, 0.5012, 0.0702,
0.5398, 0.0766, 0.5768, 0.0890, 0.6123, 0.1055,
0.6466, 0.1244, 0.6805, 0.1440, 0.7147, 0.1630,
0.7500, 0.1800
],
[
-0.7500, 0.0000, -0.7033, 0.0195, -0.6569, 0.0399,
-0.6104, 0.0600, -0.5634, 0.0789, -0.5155, 0.0954,
-0.4667, 0.1089, -0.4174, 0.1206, -0.3676, 0.1299,
-0.3174, 0.1365, -0.2669, 0.1398, -0.2162, 0.1391,
-0.1658, 0.1347, -0.1157, 0.1271, -0.0661, 0.1169,
-0.0170, 0.1046, 0.0316, 0.0903, 0.0791, 0.0728,
0.1259, 0.0534, 0.1723, 0.0331, 0.2188, 0.0129,
0.2656, -0.0064, 0.3122, -0.0263, 0.3586, -0.0466,
0.4052, -0.0665, 0.4525, -0.0847, 0.5007, -0.1002,
0.5497, -0.1130, 0.5991, -0.1240, 0.6491, -0.1325,
0.6994, -0.1380, 0.7500, -0.1400
]
],
WIND_OFFSETS = [
{start: 0.36, end: 0.11},
{start: 0.56, end: 0.16}
];
function leaf(ctx, t, x, y, cw, s, color) {
var a = cw / 8,
b = a / 3,
c = 2 * b,
d = (t % 1) * TAU,
e = Math.cos(d),
f = Math.sin(d);
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = s;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
ctx.arc(x , y , a, d , d + Math.PI, false);
ctx.arc(x - b * e, y - b * f, c, d + Math.PI, d , false);
ctx.arc(x + c * e, y + c * f, b, d + Math.PI, d , true );
ctx.globalCompositeOperation = 'destination-out';
ctx.fill();
ctx.globalCompositeOperation = 'source-over';
ctx.stroke();
}
function swoosh(ctx, t, cx, cy, cw, s, index, total, color) {
t /= 2500;
var path = WIND_PATHS[index],
a = (t + index - WIND_OFFSETS[index].start) % total,
c = (t + index - WIND_OFFSETS[index].end ) % total,
e = (t + index ) % total,
b, d, f, i;
ctx.strokeStyle = color;
ctx.lineWidth = s;
ctx.lineCap = "round";
ctx.lineJoin = "round";
if(a < 1) {
ctx.beginPath();
a *= path.length / 2 - 1;
b = Math.floor(a);
a -= b;
b *= 2;
b += 2;
ctx.moveTo(
cx + (path[b - 2] * (1 - a) + path[b ] * a) * cw,
cy + (path[b - 1] * (1 - a) + path[b + 1] * a) * cw
);
if(c < 1) {
c *= path.length / 2 - 1;
d = Math.floor(c);
c -= d;
d *= 2;
d += 2;
for(i = b; i !== d; i += 2)
ctx.lineTo(cx + path[i] * cw, cy + path[i + 1] * cw);
ctx.lineTo(
cx + (path[d - 2] * (1 - c) + path[d ] * c) * cw,
cy + (path[d - 1] * (1 - c) + path[d + 1] * c) * cw
);
}
else
for(i = b; i !== path.length; i += 2)
ctx.lineTo(cx + path[i] * cw, cy + path[i + 1] * cw);
ctx.stroke();
}
else if(c < 1) {
ctx.beginPath();
c *= path.length / 2 - 1;
d = Math.floor(c);
c -= d;
d *= 2;
d += 2;
ctx.moveTo(cx + path[0] * cw, cy + path[1] * cw);
for(i = 2; i !== d; i += 2)
ctx.lineTo(cx + path[i] * cw, cy + path[i + 1] * cw);
ctx.lineTo(
cx + (path[d - 2] * (1 - c) + path[d ] * c) * cw,
cy + (path[d - 1] * (1 - c) + path[d + 1] * c) * cw
);
ctx.stroke();
}
if(e < 1) {
e *= path.length / 2 - 1;
f = Math.floor(e);
e -= f;
f *= 2;
f += 2;
leaf(
ctx,
t,
cx + (path[f - 2] * (1 - e) + path[f ] * e) * cw,
cy + (path[f - 1] * (1 - e) + path[f + 1] * e) * cw,
cw,
s,
color
);
}
}
var Skycons = function(opts) {
this.list = [];
this.interval = null;
this.color = opts && opts.color ? opts.color : "black";
this.resizeClear = !!(opts && opts.resizeClear);
};
Skycons.CLEAR_DAY = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
sun(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, color);
};
Skycons.CLEAR_NIGHT = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
moon(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, color);
};
Skycons.PARTLY_CLOUDY_DAY = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
sun(ctx, t, w * 0.625, h * 0.375, s * 0.75, s * STROKE, color);
cloud(ctx, t, w * 0.375, h * 0.625, s * 0.75, s * STROKE, color);
};
Skycons.PARTLY_CLOUDY_NIGHT = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
moon(ctx, t, w * 0.667, h * 0.375, s * 0.75, s * STROKE, color);
cloud(ctx, t, w * 0.375, h * 0.625, s * 0.75, s * STROKE, color);
};
Skycons.CLOUDY = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
cloud(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, color);
};
Skycons.RAIN = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
rain(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
cloud(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
};
Skycons.SLEET = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
sleet(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
cloud(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
};
Skycons.SNOW = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
snow(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
cloud(ctx, t, w * 0.5, h * 0.37, s * 0.9, s * STROKE, color);
};
Skycons.WIND = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h);
swoosh(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, 0, 2, color);
swoosh(ctx, t, w * 0.5, h * 0.5, s, s * STROKE, 1, 2, color);
};
Skycons.FOG = function(ctx, t, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
s = Math.min(w, h),
k = s * STROKE;
fogbank(ctx, t, w * 0.5, h * 0.32, s * 0.75, k, color);
t /= 5000;
var a = Math.cos((t ) * TAU) * s * 0.02,
b = Math.cos((t + 0.25) * TAU) * s * 0.02,
c = Math.cos((t + 0.50) * TAU) * s * 0.02,
d = Math.cos((t + 0.75) * TAU) * s * 0.02,
n = h * 0.936,
e = Math.floor(n - k * 0.5) + 0.5,
f = Math.floor(n - k * 2.5) + 0.5;
ctx.strokeStyle = color;
ctx.lineWidth = k;
ctx.lineCap = "round";
ctx.lineJoin = "round";
line(ctx, a + w * 0.2 + k * 0.5, e, b + w * 0.8 - k * 0.5, e);
line(ctx, c + w * 0.2 + k * 0.5, f, d + w * 0.8 - k * 0.5, f);
};
Skycons.prototype = {
_determineDrawingFunction: function(draw) {
if(typeof draw === "string")
draw = Skycons[draw.toUpperCase().replace(/-/g, "_")] || null;
return draw;
},
add: function(el, draw) {
var obj;
if(typeof el === "string")
el = document.getElementById(el);
// Does nothing if canvas name doesn't exists
if(el === null)
return;
draw = this._determineDrawingFunction(draw);
// Does nothing if the draw function isn't actually a function
if(typeof draw !== "function")
return;
obj = {
element: el,
context: el.getContext("2d"),
drawing: draw
};
this.list.push(obj);
this.draw(obj, KEYFRAME);
},
set: function(el, draw) {
var i;
if(typeof el === "string")
el = document.getElementById(el);
for(i = this.list.length; i--; )
if(this.list[i].element === el) {
this.list[i].drawing = this._determineDrawingFunction(draw);
this.draw(this.list[i], KEYFRAME);
return;
}
this.add(el, draw);
},
remove: function(el) {
var i;
if(typeof el === "string")
el = document.getElementById(el);
for(i = this.list.length; i--; )
if(this.list[i].element === el) {
this.list.splice(i, 1);
return;
}
},
draw: function(obj, time) {
var canvas = obj.context.canvas;
if(this.resizeClear)
canvas.width = canvas.width;
else
obj.context.clearRect(0, 0, canvas.width, canvas.height);
obj.drawing(obj.context, time, this.color);
},
play: function() {
var self = this;
this.pause();
this.interval = requestInterval(function() {
var now = Date.now(),
i;
for(i = self.list.length; i--; )
self.draw(self.list[i], now);
}, 1000 / 60);
},
pause: function() {
var i;
if(this.interval) {
cancelInterval(this.interval);
this.interval = null;
}
}
};
global.Skycons = Skycons;
}(this));
hear is jascript:
But if both of them is rain it just show rain img for first one .
In HTML, id must be unique, so you can only target the first occurrence of a given id.
You should use a class instead to target multiple elements with the same class, using document.getElementsByClassName("rain").
We can not help you more without your Javascript code.

How do I draw an arrow between two points in d3v4?

I have created a custom path renderer that draws an arrow between the nodes in my d3 graph as shown in the snippet. I have one last issue I am getting stuck on,
How would I rotate the arrow portion so that it is pointing from the direction of the curve instead of the direction of the source?
var w2 = 6,
ar2 = w2 * 2,
ah = w2 * 3,
baseHeight = 30;
// Arrow function
function CurvedArrow(context, index) {
this._context = context;
this._index = index;
}
CurvedArrow.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 1)) {
this._context.closePath();
}
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y; // jshint ignore:line
switch (this._point) {
case 0:
this._point = 1;
this._p1x = x;
this._p1y = y;
break;
case 1:
this._point = 2; // jshint ignore:line
default:
var p1x = this._p1x,
p1y = this._p1y,
p2x = x,
p2y = y,
dx = p2x - p1x,
dy = p2y - p1y,
px = dy,
py = -dx,
pr = Math.sqrt(px * px + py * py),
nx = px / pr,
ny = py / pr,
dr = Math.sqrt(dx * dx + dy * dy),
wx = dx / dr,
wy = dy / dr,
ahx = wx * ah,
ahy = wy * ah,
awx = nx * ar2,
awy = ny * ar2,
phx = nx * w2,
phy = ny * w2,
//Curve figures
alpha = Math.floor((this._index - 1) / 2),
direction = p1y < p2y ? -1 : 1,
height = (baseHeight + alpha * 3 * ar2) * direction,
// r5
//r7 r6|\
// ------------ \
// ____________ /r4
//r1 r2|/
// r3
r1x = p1x - phx,
r1y = p1y - phy,
r2x = p2x - phx - ahx,
r2y = p2y - phy - ahy,
r3x = p2x - awx - ahx,
r3y = p2y - awy - ahy,
r4x = p2x,
r4y = p2y,
r5x = p2x + awx - ahx,
r5y = p2y + awy - ahy,
r6x = p2x + phx - ahx,
r6y = p2y + phy - ahy,
r7x = p1x + phx,
r7y = p1y + phy,
//Curve 1
c1mx = (r2x + r1x) / 2,
c1my = (r2y + r1y) / 2,
m1b = (c1mx - r1x) / (r1y - c1my),
den1 = Math.sqrt(1 + Math.pow(m1b, 2)),
mp1x = c1mx + height * (1 / den1),
mp1y = c1my + height * (m1b / den1),
//Curve 2
c2mx = (r7x + r6x) / 2,
c2my = (r7y + r6y) / 2,
m2b = (c2mx - r6x) / (r6y - c2my),
den2 = Math.sqrt(1 + Math.pow(m2b, 2)),
mp2x = c2mx + height * (1 / den2),
mp2y = c2my + height * (m2b / den2);
this._context.moveTo(r1x, r1y);
this._context.quadraticCurveTo(mp1x, mp1y, r2x, r2y);
this._context.lineTo(r3x, r3y);
this._context.lineTo(r4x, r4y);
this._context.lineTo(r5x, r5y);
this._context.lineTo(r6x, r6y);
this._context.quadraticCurveTo(mp2x, mp2y, r7x, r7y);
break;
}
}
};
var w = 600,
h = 220;
var t0 = Date.now();
var points = [{
R: 100,
r: 3,
speed: 2,
phi0: 190
}];
var path = d3.line()
.curve(function(ctx) {
return new CurvedArrow(ctx, 1);
});
var svg = d3.select("svg");
var container = svg.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")")
container.selectAll("g.planet").data(points).enter().append("g")
.attr("class", "planet").each(function(d, i) {
d3.select(this).append("circle").attr("r", d.r).attr("cx", d.R)
.attr("cy", 0).attr("class", "planet");
});
container.append("path");
var planet = d3.select('.planet circle');
d3.timer(function() {
var delta = (Date.now() - t0);
planet.attr("transform", function(d) {
return "rotate(" + d.phi0 + delta * d.speed / 50 + ")";
});
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
g.setAttributeNS(null, "transform", planet.attr('transform'));
var matrix = g.transform.baseVal.consolidate().matrix;
svg.selectAll("path").attr('d', function(d) {
return path([
[0, 0],
[matrix.a * 100, matrix.b * 100]
])
});
});
path {
stroke: #11a;
fill: #eee;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="220"></svg>
I ended up doing what #Mark suggested in the comments, I calculate the point that is the height of the curve away along the normal midway between the two points, then calculate the unit vectors from the start point to the mid point and again from the midpoint to the end. I can then use those to get all the required points.
var arrowRadius = 6,
arrowPointRadius = arrowRadius * 2,
arrowPointHeight = arrowRadius * 3,
baseHeight = 30;
// Arrow function
function CurvedArrow(context, index) {
this._context = context;
this._index = index;
}
CurvedArrow.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 1)) {
this._context.closePath();
}
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y; // jshint ignore:line
switch (this._point) {
case 0:
this._point = 1;
this._p1x = x;
this._p1y = y;
break;
case 1:
this._point = 2; // jshint ignore:line
default:
var p1x = this._p1x,
p1y = this._p1y,
p2x = x,
p2y = y,
//Curve figures
// mp1
// |
// | height
// |
// p1 ----------------------- p2
//
alpha = Math.floor((this._index - 1) / 2),
direction = p1y < p2y ? -1 : 1,
height = (baseHeight + alpha * 3 * arrowPointRadius) * direction,
c1mx = (p2x + p1x) / 2,
c1my = (p2y + p1y) / 2,
m1b = (c1mx - p1x) / (p1y - c1my),
den1 = Math.sqrt(1 + Math.pow(m1b, 2)),
// Perpendicular point from the midpoint.
mp1x = c1mx + height * (1 / den1),
mp1y = c1my + height * (m1b / den1),
// Arrow figures
dx = p2x - mp1x,
dy = p2y - mp1y,
dr = Math.sqrt(dx * dx + dy * dy),
// Normal unit vectors
nx = dy / dr,
wy = nx,
wx = dx / dr,
ny = -wx,
ahx = wx * arrowPointHeight,
ahy = wy * arrowPointHeight,
awx = nx * arrowPointRadius,
awy = ny * arrowPointRadius,
phx = nx * arrowRadius,
phy = ny * arrowRadius,
// Start arrow offset.
sdx = mp1x - p1x,
sdy = mp1y - p1y,
spr = Math.sqrt(sdy * sdy + sdx * sdx),
snx = sdy / spr,
sny = -sdx / spr,
sphx = snx * arrowRadius,
sphy = sny * arrowRadius,
// r5
//r7 r6|\
// ------------ \
// ____________ /r4
//r1 r2|/
// r3
r1x = p1x - sphx,
r1y = p1y - sphy,
r2x = p2x - phx - ahx,
r2y = p2y - phy - ahy,
r3x = p2x - awx - ahx,
r3y = p2y - awy - ahy,
r4x = p2x,
r4y = p2y,
r5x = p2x + awx - ahx,
r5y = p2y + awy - ahy,
r6x = p2x + phx - ahx,
r6y = p2y + phy - ahy,
r7x = p1x + sphx,
r7y = p1y + sphy,
mpc1x = mp1x - phx,
mpc1y = mp1y - phy,
mpc2x = mp1x + phx,
mpc2y = mp1y + phy;
this._context.moveTo(r1x, r1y);
this._context.quadraticCurveTo(mpc1x, mpc1y, r2x, r2y);
this._context.lineTo(r3x, r3y);
this._context.lineTo(r4x, r4y);
this._context.lineTo(r5x, r5y);
this._context.lineTo(r6x, r6y);
this._context.quadraticCurveTo(mpc2x, mpc2y, r7x, r7y);
this._context.closePath();
break;
}
}
};
var w = 600,
h = 220;
var t0 = Date.now();
var points = [{
R: 100,
r: 3,
speed: 2,
phi0: 190
}];
var path = d3.line()
.curve(function(ctx) {
return new CurvedArrow(ctx, 1);
});
var svg = d3.select("svg");
var container = svg.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")")
container.selectAll("g.planet").data(points).enter().append("g")
.attr("class", "planet").each(function(d, i) {
d3.select(this).append("circle").attr("r", d.r).attr("cx", d.R)
.attr("cy", 0).attr("class", "planet");
});
container.append("path");
var planet = d3.select('.planet circle');
d3.timer(function() {
var delta = (Date.now() - t0);
planet.attr("transform", function(d) {
return "rotate(" + d.phi0 + delta * d.speed / 50 + ")";
});
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
g.setAttributeNS(null, "transform", planet.attr('transform'));
var matrix = g.transform.baseVal.consolidate().matrix;
svg.selectAll("path").attr('d', function(d) {
return path([
[0, 0],
[matrix.a * 100, matrix.b * 100]
])
});
});
path {
stroke: #11a;
fill: #eee;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="220"></svg>

Inverse kinematics with an arbitrary number of points

I've modified this example of inverse kinematics in JavaScript with HTML5 Canvas and made it dynamic by seperating it into a function, and it works, but the example only uses 3 points -- start, middle, and end, and I'd like to change the number of points at will. Here's my current fiddle...
function _kinematics(joints, fx, mouse) {
joints.forEach(function (joint) {
joint.target = joint.target || {
x: fx.canvas.width / 2,
y: fx.canvas.height / 2
};
joint.start = joint.start || {
x: 0,
y: 0
};
joint.middle = joint.middle || {
x: 0,
y: 0
};
joint.end = joint.end || {
x: 0,
y: 0
};
joint.length = joint.length || 50;
});
var theta,
$theta,
_theta,
dx,
dy,
distance;
joints.forEach(function (joint) {
if (mouse) {
joint.target.x = mouse.x;
joint.target.y = mouse.y;
}
dx = joint.target.x - joint.start.x;
dy = joint.target.y - joint.start.y;
distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
_theta = Math.atan2(dy, dx);
if (distance < joint.length) {
theta = Math.acos(distance / (joint.length + joint.length)) + _theta;
dx = dx - joint.length * Math.cos(theta);
dy = dy - joint.length * Math.sin(theta);
$theta = Math.atan2(dy, dx);
} else {
theta = $theta = _theta;
}
joint.middle.x = joint.start.x + Math.cos(theta) * joint.length;
joint.middle.y = joint.start.y + Math.sin(theta) * joint.length;
joint.end.x = joint.middle.x + Math.cos($theta) * joint.length;
joint.end.y = joint.middle.y + Math.sin($theta) * joint.length;
fx.beginPath();
fx.moveTo(joint.start.x, joint.start.y);
/* for (var i = 0; i < joint.points.length / 2; i++) {
fx.lineTo(joint.points[i].x, joint.points[i].y);
} */
fx.lineTo(joint.middle.x, joint.middle.y);
/* for (var j = joint.points.length / 2; j < joint.points.length; j++) {
fx.lineTo(joint.points[j].x, joint.points[j].y);
} */
fx.lineTo(joint.end.x, joint.end.y);
fx.strokeStyle = "rgba(0,0,0,0.5)";
fx.stroke();
fx.beginPath();
fx.arc(joint.start.x, joint.start.y, 10, 0, Math.PI * 2);
fx.fillStyle = "rgba(255,0,0,0.5)";
fx.fill();
fx.beginPath();
fx.arc(joint.middle.x, joint.middle.y, 10, 0, Math.PI * 2);
fx.fillStyle = "rgba(0,255,0,0.5)";
fx.fill();
fx.beginPath();
fx.arc(joint.end.x, joint.end.y, 10, 0, Math.PI * 2);
fx.fillStyle = "rgba(0,0,255,0.5)";
fx.fill();
});
}
That's just the function, I've omitted the rest for brevity. As you can see, the commented-out lines were my attempt to draw the other points.
Also, here's where I populate the joints array with the points and such. See commented lines.
populate(_joints, $joints, function() {
var coords = randCoords(map);
var o = {
start: {
x: coords.x,
y: coords.y
},
// points: [],
target: {
x: mouse.x,
y: mouse.y
}
};
/* for (var p = 0; p < 10; p++) {
o.points.push({
x: p === 0 ? o.start.x + (o.length || 50) : o.points[p - 1].x + (o.length || 50),
y: p === 0 ? o.start.y + (o.length || 50) : o.points[p - 1].y + (o.length || 50)
});
}; */
return o;
});
How would I make this function work with n points?

Categories