I am trying to make tooltips for a data visualization I made using p5.js but I am completely lost. Nothing I tried works. This is my code as is.
var table;
var i;
var j;
var cellValue;
var label;
var test;
function preload() {
matrix = loadTable("dataLayer2matrix.csv","csv")
labels = loadTable("dataLayer2labels.csv","csv")
test = matrix
}
function setup() {
createCanvas(1500,1500)
noStroke()
fill(0,0,255,10)
angleMode(DEGREES)
background(255,255,255)
matrixStartX = 200
matrixStartY = 250
var matrixRows = matrix.getRows()
var matrixSize = matrixRows.length
// Experiment with grid
fill(75, 75, 75, 50)
for (r = 0; r <= matrixSize; r++) {
rect(matrixStartX , matrixStartY + r * 20 - 1 , 20 * matrixSize, 1)
rect(matrixStartX + r * 20 - 1 , matrixStartY, 1, 20 * matrixSize)
}
// Draw matrix
for (var mr = 0; mr < matrixSize; mr++) {
for (var mc = 0; mc < matrixSize; mc++) {
cellValue = matrixRows[mr].getNum(mc)
fill(49,130,189,cellValue*10)
rect(mc * 20 + matrixStartX, mr * 20 + matrixStartY, 19 ,19)
}
}
// Labels - horizontal
fill(75, 75, 75, 255)
labelsRow = labels.getRows()
for (mc = 0; mc < matrixSize; mc++) {
label = labelsRow[0].getString(mc)
text(label, 10, mc*20+matrixStartY + 15)
}
// Labels - vertical
push()
translate(matrixStartX + 15, matrixStartY - 15)
rotate(-90)
for (mc = 0; mc < matrixSize; mc++) {
label = labelsRow[0].getString(mc)
text(label, 0, mc*20)
}
pop()
//Tooltip when clicked
}
/* if(mouseIsPressed){
fill(50);
text(cellValue, 10,10,70,80);
}*/
}
}
It makes this image:
I want it so that when I go over a square I get the data in it. I really can't seem to do it. Thanks.
I think the advice telling you to use bootstrap is missing the fact that you're using p5.js. Bootstrap is more for dealing with html components, not internal Processing sketches.
Instead, you probably want to do this with p5.js code. The best thing you can do is break your problem down into smaller steps:
Step 1: Can you draw a single rectangle?
Instead of trying to add this new functionality to your existing sketch, it might be easier if you start with a simpler example sketch with just a single rectangle.
Step 2: Can you detect when the mouse is inside that rectangle?
If you know where you're drawing the rectangle, you know its coordinates. You also know the coordinates of the mouse from the mouseX and mouseY variables. So to detect whether the mouse is inside the rectangle, you simply have to use if statements that compare the coordinates of the mouse to the coordinates of the rectangle. There are a ton of resources on google for this, and it might help if you draw some examples out on a piece of paper.
Also, don't worry about the tooltip just yet. Just do something simple like change the color of the rectangle when the mouse is inside it.
Step 3: Can you display the information box?
Again, do this in its own sketch first. Maybe create a function that takes a position and the information you want to display as parameters and displays it in a rectangle. Don't worry about making it a tooltip yet. Just get it displaying. Use hard-coded values for the information.
Step 4: Can you combine your small example sketches?
You have code that triggers when the mouse is inside a rectangle. You have code that draws the tooltip. Can you make it so the tooltip is drawn when the mouse is inside the rectangle?
Step 5: Only when all of the above works, then you should start thinking about adding it to your full sketch.
Instead of using an example rectangle, you'll have to use the rectangles you're drawing on the screen. Instead of calling the tooltip function with hard-coded values, use the values you get from the squares.
Take on those pieces one at a time, and make small steps toward your goal. Then if you get stuck, you can post an MCVE of the specific step you're on. Good luck!
Related
I'm new in this and I'm making a small game in JS, the problem that I have now is when I create enemies it sometimes overlaps, creating this:
The way that use to create them is simple,
resetShip(enemy_spaceship) {
enemy_spaceship.y = 0;
enemy_spaceship.x = Phaser.Math.Between(10,globalThis.config.width);
}
In X each sprite will have a random number from 10 to the width of the screen (canvas), the problem is that if a sprite has 440 in X and another one has 450 in X, those 10px aren't enough to separate them, some people told me to create a grid, but like I said I'm new and searching about grid can't find any example that I can use to this, thanks if you can help me :)
One option is for each enemy ship to be allocated a specific region in which it may start. If you have 2 ships, that means the first ship can be anywhere in the first half of the X axis, and the second ship can be anywhere in the second half of the X axis.
To do this, you should update your resetShip function to also take in a minX and maxX, and use that when defining it's location:
resetShip (enemy_spaceship, minX, maxX) {
enemy_spaceship.y = 0;
enemy_spaceship.x = Phaser.Math.Between(minX, maxX);
}
Then, you need to find a way to rest the group of ships, providing valid regions for each ship. Something like this:
resetEnemies(ships) {
//Each ship may be in a region that is 1/Nth of the width
let regionWidth = globalThis.config.width / ships.length
//We need to know the shipWidth so we don't let ships get too
//close to the left edge.
let shipWidth = 64
ships.forEach((ship, i) => {
//Assuming you just want padding on the left so it is no closer than 10px,
//this will define the minX for the Nth ship
const minX = Math.min(10, i*regionWidth)
//The maxX should not let a ship overlap the next region. So, we subtract the shipWidth
//to ensure that, at worst, it is right next to the next ship
const maxX = (i+1)*regionWidth-shipWidth
//Use the updated restShip to put it in a valid location for it's region
resetShip(ship, minX, maxX)
})
}
I am trying to build a chess game using Javascript(ES 6) and Canvas.
I have built a basic chessboard and also rendered pawns on top of it.
Now I intend to either click on the pawn or drag it to make a move.
document.addEventListener('click',board_click)
or
canvas.addEventListener('click', board_click, false);
is the way I manage to listen to these events.
Now how would I know where I have clicked? I figured I can try to get the current position of the click, but how will I know what item is there at the present location.
Any libraries or already implemented logics will also be helpful
I have rendered my pawns like this
const drawPawns = (white, black, ctx, boardDimension, allPieces) => {
const rows = [0, 1, 2, 3, 4, 5, 6, 7];
const cols = [1, 6];
let pawn;
let side;
// boardDimension = 90;
cols.forEach((col) => {
rows.forEach((row) => {
// pawn = Pawn.call(this, 'black');
side = col === 1 ? 'black' : 'white';
// That little tinkering is to center align the Pawn in the tile
pawn = new Pawn(side, row * boardDimension + 30, col * boardDimension + 5, true);
spriteImage.draw(ctx, pawn.canvasPosition, pawn.x, pawn.y);
allPieces.push(pawn);
});
});
};
The pawn.canvasPostion is to draw the image(using Sprite), whereas x and y are the places where its coordinates are. Now how would I get this particular Coor ordinates?
The listener callback (your board_click function) gets the browser event as an argument. This event object contains a bunch of data, including the clicked element and the coordinates of the click.
Since you are using canvas, you should attach the click listener on the canvas element. Canvas does not have a state graph, which means it has no concept of objects in the canvas - it is just a bunch of pixels. This means it is your responsibility to keep track of which piece is where.
Since you are already placing the pieces on the board, you know exactly where they are!
The normal way to do this is to get the click position, map that to a grid cell on the board, and then check whether anything was on that cell. How exactly this will happen depends on the data structure that describes your game.
For illustrative purposes let's assume that the chess pieces reside in an array, and each piece knows its cell coordinates (for chess it probably makes sense to do the opposite - each cell would know whether it has a piece on it, but bear with me).
var cellSize = 16; // let's assume a cell on your board is 16x16 pixels
var pieces = []; // this keeps all chess pieces
function board_click(evt) {
// get cell coordinates
var cellX = Math.floor(evt.clientX/cellSize);
var cellY = Math.floor(evt.clientY/cellSize);
var piece = pieces.find((p) => {
return p.x == cellX && p.y == cellY;
});
if (piece) {
// a piece was clicked
} else {
// an empty cell was clicked
}
}
I hope this is enough to get you started.
Edit: had forgotten to divide the mouse coordinates by the cell size...
I've been experimenting with trigonometry for the past few days, and came up with one of those neat stat pentagons you find in some games. (fiddle!)
I'd really like to allow the vertices of the inner polygon to be draggable to change the stat values. I have mouse functionality working well, but what's the best way to drag a point on the line with the mouse?
I've created a picture to visualize my problem; the red polygon is the "current" polygon, the pink lines represent the new polygon, the pink circle emphasizes the new point for the vertex, the blue line is the vector tangent, and the green circle is the cursor.
I've written a program which deals with vectors before, but I'm not sure how to apply it to this situation.
Here's some code (in the loop function):
for(var i = 0; i < innerPolygonKnobs.length; i ++){
var knob = innerPolygonKnobs[i];
distX = knob.x-mouse.x;
distY = knob.y-mouse.y;
distTotal = Math.sqrt(distX*distX + distY*distY);
if(distTotal < 8){
if(!knob.over)change = true;
knob.over = true;
if(mouse.down){
// What goes here?
}
} else {
if(knob.over)change = true;
knob.over = false;
}
}
if(change)redraw();
Thanks so much in advance! :D
This function will give you the closest point to the mouse on any given line:
// given a line defined like this
var line={x0:50,y0:50,x1:150,y1:150};
// calculate the closest point on the line to [x,y]
function getClosestPointOnLine(line,x,y) {
//
lerp=function(a,b,x){ return(a+x*(b-a)); };
var dx=line.x1-line.x0;
var dy=line.y1-line.y0;
var t=((x-line.x0)*dx+(y-line.y0)*dy)/(dx*dx+dy*dy);
t=Math.min(1,Math.max(0,t));
var lineX=lerp(line.x0, line.x1, t);
var lineY=lerp(line.y0, line.y1, t);
return({x:lineX,y:lineY});
};
Then just redraw your inner polygon to connect to the point found above.
Interesting app...good luck with it!
As a very inexperienced programmer, I'm trying to code a game that detects when the player collides with certain colors on the canvas. I have a black square with coordinates "player.x" and "player.y" and dimensions 50x50 that moves around when you press the arrow keys. I also have a stationary red (255,0,0) square elsewhere on the canvas.
The function below is supposed to grab a slightly larger square around the "player" square and find out if there's any red in it. If there is, it will send up an alert. The problem is, this doesn't seem to be working.
function collideTest(){
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
var whatColor = c.getImageData(player.x - 5, player.y - 5,60,60);
for (var i = 0; i < 3600; i++) {
if (whatColor.data[i] == 255) {
alert("red");
}
}
}
I'm semi-aware that this is not the most efficient way to detect red pixels, but I wanted to simplify the code before posting it here. Is there something obviously wrong with the function?
The problem could lie in the way the function is called. It gets called at the end of another function that detects user-input and changes the coordinates of the "player" square. THAT function gets called right before everything is drawn on the canvas.
Thanks in advance for any help!
var whatColor = c.getImageData(player.x - 5, player.y - 5,60,60);
player.x and player.y must not be decimal, make sure they are rounded or getImageData will be angry and not play nice.
For each single pixel on the canvas, the whatColor.data array holds 4 sequential pieces of color information: red,green,blue,alpha(opacity). So the whatColor.data looks like this for each pixel:
whatColor.data[i] is the red component of the color.
whatColor.data[i+1] is the green component of the color.
whatColor.data[i+2] is the blue component of the color.
whatColor.data[i+3] is the alpha(opacity) component of the color.
So your iteration would look like this (4 indexes per pixel):
for(var i = 0, n = whatColor.data.length; i < n; i += 4) {
var red = whatColor.data[i];
var green = whatColor.data[i + 1];
var blue = whatColor.data[i + 2];
var alpha = whatColor.data[i + 3];
if(red==255){ ... it's a hit, do your thing! ... }
}
See here for a mini-tutorial on the imageData.data array: http://www.html5canvastutorials.com/advanced/html5-canvas-get-image-data-tutorial/
By the way, you might look at one of the canvas libraries that simplify game making with canvas. Here are just a few: easelJs, KineticJs, FabricJs, and more!
In the following picture:
alt text http://rookery9.aviary.com.s3.amazonaws.com/4478500/4478952_3e06_625x625.jpg
I want to connect the boxes in the above with below, Let us call the bottom edge of the top boxes as A and top edge of the below boxes as B
Now, I have two arrays containing the points in the line A and B say
A = [ {Ax1, Ay1},{Ax2, Ay2},.... ] and B = [ {Bx1, By1},{Bx2, By2},.... ]
In real world it can be like A = [ {100, 100},{120, 100},{140, 100},{160, 100}] and B=[ {120, 200},{140, 200},{160, 200},{180, 200},{200, 200},]
Please look at the black dots in the picture above
How can I get the connectiong points as shown in the pictures? Connecting point must be as close to the center of the line as possible.
Here is what I'm trying to get, but below functions draw line between the two matching points from the starting from the left of the both lines, Any suggessions
drawConnection : function(componentOut, componentIn, connectionKey) {
var outDim = $(componentOut).data('dim');
var inDim = $(componentIn).data('dim');
var outPorts = $(componentOut).data('ports');
var inPorts = $(componentIn).data('ports');
var abovePorts = {};
var belowPorts = {};
var i = 0;
if(outDim.bottomLeft.y < inDim.topLeft.y){
// Now proceed only if they can be connect with a single line
if(outDim.bottomLeft.x < inDim.topRight.x && outDim.bottomRight.x>inDim.topLeft.x) {
// Now get a proper connecting point
abovePorts = outPorts.bottom;
belowPorts = inPorts.top;
for(i=0; i<abovePorts.length; i++) {
for(j=0; j<belowPorts.length; j++) {
if(!abovePorts[i].inUse && !belowPorts[j].inUse && (abovePorts[i].x == belowPorts[j].x)){
console.debug("Drawing vertical lines between points ("+abovePorts[i].x+","+abovePorts[i].y+") and ("+abovePorts[i].x+","+belowPorts[j].y+")");
return true;
}
}
}
}
}
return false;
},
-- Update
I'm exactly trying to get something similar to this http://raphaeljs.com/graffle.html, but the connections should be made with straight lines as shown below
alt text http://rookery9.aviary.com.s3.amazonaws.com/4480500/4480527_1e77_625x625.jpg
Have you tried Raphael.js : http://raphaeljs.com/ ?
Another approach is to use the HTML+CSS engine of the browser, instead of using JS.
You can use a table.
One cell row for each box and a 2 cells row for the connector.
You color one of the border for the connector and use margin, float and width styles, to position the boxes.
I've already used this technique to draw org charts a long time ago... when IE6 was considered the best browser!
Another worth looking at is Processing.js if you want a bit more power. I've used Raphael.js before and that was pretty easy to pickup and use. Just be aware that both utilize the Canvas element which to my knowledge isn't supported in all browsers yet.