Drawing rectangle and square using same function in Javascript - javascript

I want to draw both rectangle and square using same var rectangle in my program. I have modified the function to draw square, given modeOptions = { "Rectangle", "Square" } what needs to be added below to work for both, rectangle and square. Please suggest. Thanks
Can also see the question at : http://jsfiddle.net/farey/qP3kV/6/
var rectangle=(function() {
var inDrag=false,downPos,upPos;
var obj;
// temporary length for square
var temp;
return {
fillStyle: "none",
strokeStyle: "blue",
lineWidth: 5,
construct: function(pos,parent) {
obj=Object.create(parent);
obj.minx=obj.maxx=pos.x;
obj.miny=obj.maxy=pos.y;
if (fillColor!="inherit")
obj.fillStyle=fillColor;
if (strokeColor!="inherit")
obj.strokeStyle=strokeColor;
if (strokeThickness!="inherit")
obj.lineWidth=strokeThickness;
},
draw: function(selected) {
ctx.beginPath();
ctx.fillStyle=this.fillStyle;
ctx.strokeStyle=(selected) ?
"gray" : this.strokeStyle;
ctx.lineWidth=this.lineWidth;
// making 3rd & fourth argument same for square. greater of maxx and maxy should be there
if (this.maxx - this.minx > this.maxy - this.miny)
{ temp = this.maxx - this.minx
}
else
temp = this.maxy - this.miny
ctx.rect(this.minx,this.miny,temp,temp);
// else
// ctx.rect(this.minx,this.miny,this.maxx - this.minx,this.maxy - this.miny )
ctx.fill();
if (selected) {
ctx.moveTo(this.minx,this.miny);
ctx.lineTo(this.maxx,this.maxy);
ctx.moveTo(this.minx,this.maxy);
ctx.lineTo(this.maxx,this.miny);
}
ctx.stroke();
},
mousedown: function(event) {
downPos=event;
rectangle.construct(downPos,drawObject[containingBox4point(downPos)]);
inDrag=true;
},
mousemove: function(event) {
if (!inDrag)
{
drawPrevious();
drawCursor(event,containingBox4point(event));
return;
}
upPos=event;
if (upPos.x>downPos.x) {
obj.minx=downPos.x;
obj.maxx=upPos.x;
}
else {
obj.minx=upPos.x;
obj.maxx=downPos.x;
}
if (upPos.y>downPos.y) {
obj.miny=downPos.y;
obj.maxy=upPos.y;
}
else {
obj.miny=upPos.y;
obj.maxy=downPos.y;
}
drawPrevious();
obj.draw(containingBox(obj)==(-1));
drawCursor(event,containingBox4point(upPos));
},
mouseup: function(event) {
// commit rectangle
rectangle.mousemove(event);
if (!inDrag)
return;
if (containingBox(obj)==(-1))
trace("U and ur box are evil . . .");
else
{
drawObject.push(obj);
trace("Rectangle props for new box "+String(drawObject.length-1)+
": filled with "+ fillColor+
" stroked with "+strokeColor+
" # thickness "+ strokeThickness);
}
inDrag=false;
drawPrevious();
},
mouseout: function(event) {
inDrag=false;
drawPrevious();
}
};
})(); // rectangle

To force the resulting rectangle to be only a square, you need to modify your rectangle mousemove method.
The modification would enforce that this is always true:
Math.abs(obj.maxx-obj.minx) == Math.abs(obj.maxy-obj.miny).
It’s up to you to determine how your want to enforce that result.
For example, assuming upPos is right and down from downPos:
You could let the horizontal length win:
obj.maxy = obj.miny+(obj.maxx-obj.minx);
You could let the vertical length win:
obj.maxx = obj.minx+(obj.maxy-obj.miny);
You could even let the minx/miny float to create a square:
obj.minx = obj.maxx – (obj.maxy-obj.miny);
// or
obj.miny= obj.maxy – (obj.maxx-obj.minx);

Related

Trying to get the dots to fill with color when I click on one in 24a2 browser game: JavaScript

I am creating a 24a2 browser game. I want to add another area to the game where you can not only draw using the key pad, but also when clicking on the dots, the dots within the 24x24 grid fill with the corresponding color. Here is the code:
function create(game) {}
function update(game) {}
function onKeyPress(direction) {}
let seen = {};
let config = {
create: create,
update: update,
onKeyPress: onKeyPress,
onButtonPress: onButtonPress
};
let game = new Game(config);
game.run();
let player = {};
function onButtonPress(x, y) {
game.setDot(x, y, Color.Black)
}
function create(game) {
player = {
x: 5,
y: 10,
};
game.setDot(player.x, player.y, Color.Blue);
}
function update(game) {
for (var key in seen) {
let coordinates = seen[key];
game.setDot(coordinates[0], coordinates[1], Color.Indigo);
}
game.setDot(player.x, player.y, Color.Black);
}
// Drawing using the dot with a specific color
function onKeyPress(direction) {
let key = player.x + ',' + player.y; // setting a delimeter; if the position is 1,11 for example, the dot will move to 1, 11 and not '111'.
seen[key] = [player.x, player.y];
if (direction == Direction.Up) {
player.y--;
}
if (direction == Direction.Down) {
player.y++;
}
if (direction == Direction.Left) {
player.x--;
}
if (direction == Direction.Right) {
player.x++;
}
}
This code moves the black dot around to fill the grid with color. This is the code I was thinking about in order to fill the dots when clicked.
function onButtonPressed(x, y) {
game.setDot(x, y, Color.Yellow)
}
However, when I run this, the dots do not fill when I click on them. Where am I going wrong with this?

No collision between circles and drawn lines in matter.js

I am having trouble creating a line in p5/matter.js. My sketch is available on the p5 editor. On mousePressed and mouseDragged, the code grabs the mouse position every ten moves and uses curveVertex to draw a line between the current and last point. All of these points are stored in an array. This draws on the canvas perfectly but cannot interact with other objects.
function mouseDragged(){
if (pointCount == 0) {
points.push({x: mouseX, y: mouseY});
pointCount += 1;
} else if (pointCount == 10) {
pointCount = 0;
} else {
pointCount += 1;
}
}
function mousePressed(){
points.push({x: mouseX, y: mouseY});
}
function mouseReleased(){
line = new Line(points);
console.log(points);
}
function draw() {
background("#efefef");
circles.push(new Circle(200, 50, random(5, 10)));
Engine.update(engine);
for (let i = 0; i < circles.length; i++) {
circles[i].show();
if (circles[i].isOffScreen()) {
circles[i].removeFromWorld();
circles.splice(i, 1);
i--;
}
}
// for (let i = 0; i < boundaries.length; i++) {
// boundaries[i].show();
// // console.log(boundaries[i].body.isStatic)
// }
if (points.length > 0) {
// Loop through creating line segments
beginShape();
noFill();
// Add the first point
stroke('black');
strokeWeight(5);
curveVertex(points[0].x,points[0].y)
curveVertex(points[0].x,points[0].y)
// Draw line
points.forEach(function(p){
curveVertex(p.x,p.y);
})
vertex(points[points.length-1].x,points[points.length-1].y) // Duplicate ending point
endShape()
}
// Draw points for visualization
stroke('#ff9900')
strokeWeight(10)
// points.push({x: x, y: y})
points.forEach(function(p){
point(p.x, p.y)
})
}
I tried creating a class and passed the points array to it, thinking that the matter.vertices function would take the points and make the needed body for the falling balls to bounce off. The code does not throw an error, but no collision occurs with the line. The example provided in matter.js document for the vertices function is unavailable and I have been unable to find any examples online. Hoping someone can point me in the right direction to get the created line to interact with the falling balls.
class Line {
constructor(vertices) {
let options = {
friction:0,
restitution: 0.95,
// angle: a,
isStatic: true
}
this.body = Matter.Body.create(options);
this.v = Matter.Vertices.create(vertices, this.body)
World.add(world, this.body);
}
}

How to collide a player with walls?

I'm trying to create a top down shooter game and I am using Tiled to create my map. I've made my map and exported it as a .json file. I was finally able to make the map appear in my game, but I am having a hard time making the collision work.
I've been going through tutorials for hours and seem to have tried everything under the sun with no luck. I have an object layer in Tiled with the walls marked with the insert rectangle tool. I have every wall tile also marked with insert rectangle in the edit tileset menu. But I still cant get it to work. Walls are Tile Layer 1, ground is Tile Layer 2, object layer is called collision and the tile set name is tiles 48x48. Here's all my relevant code:
var game = new Phaser.Game(1440, 960, Phaser.man, 'phaser-example', { preload: preload, create: create, update: update, render: render });
var sprite
//sounds
var music
//movement
var controls
var cursors
//shooting
var fireRate = 200;
var nextFire = 0;
var Bullets
//map
var map
var walls
var ground
//var collision
function preload() {
game.load.audio('groove', ['sewer groove.mp3']);
game.load.audio('gunshot', 'pistol.mp3');
game.load.image('player', 'player lite.png');
game.load.image('bullet', 'bullet.png');
game.load.tilemap('map', 'sewermap.json', null, Phaser.Tilemap.TILED_JSON);
game.load.image('tiles 48x48','tiles 48x48.png')
}
function create() {
map = game.add.tilemap('map');
map.addTilesetImage('tiles 48x48');
//var tileset = map.addTilesetImage('map','tiles 48x48');
//map.physics.arcade.enable(sprite, Phaser.Physics.ARCADE);
ground = map.createLayer('Tile Layer 2');
walls = map.createLayer('Tile Layer 1');
//collision = map.createLayer('Object Layer 1')
map.setCollisionBetween(0, 65, true, 'Tile Layer 1');
//sprite.body.collideWorldbounds = true;
//layer.resizeWorld();
music = game.add.audio('groove',1,true);
music.play();
game.physics.startSystem(Phaser.Physics.ARCADE);
//game.physics.startSystem(Phaser.Physics.P2JS)
game.stage.backgroundColor = '#313131';
bullets = game.add.group();
bullets.enableBody = true;
bullets.physicsBodyType = Phaser.Physics.ARCADE;
bullets.createMultiple(50, 'bullet');
bullets.setAll('checkWorldBounds', true);
bullets.setAll('outOfBoundsKill', true);
sprite = game.add.sprite(620, 920, 'player');
sprite.anchor.set(0.5, 0.5);
//game.physics.p2.enable(sprite)
game.physics.arcade.enable(sprite, Phaser.Physics.ARCADE);
sprite.body.allowRotation = true;
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
game.physics.arcade.collider(sprite, walls);
//console.log(sprite.rotation);
sprite.rotation = game.physics.arcade.angleToPointer(sprite);
if (game.input.activePointer.isDown)
{
fire();
}
//sprite.body.setZeroVelocity();
if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT))
{
sprite.x -= 4;
}
else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT))
{
sprite.x += 4;
}
if (game.input.keyboard.isDown(Phaser.Keyboard.UP))
{
sprite.y -= 4;
}
else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN))
{
sprite.y += 4;
}
}
function fire() {
if (game.time.now > nextFire && bullets.countDead() > 0)
{
nextFire = game.time.now + fireRate;
var bullet = bullets.getFirstDead();
bullet.reset(sprite.x - 8, sprite.y - 8);
game.physics.arcade.moveToPointer(bullet, 300);
}
}
function render() {
game.debug.text('Active Bullets: ' + bullets.countLiving() + ' / ' + bullets.total, 32, 32);
game.debug.spriteInfo(sprite, 32, 450);
//game.debug.spriteBounds(sprite);
//game.debug.spriteBounds(bullets);
//game.debug.body(sprite);
}
Alright, I've had the chance to take a look at this, the issue should solely lie in how you're moving the main player:
sprite.x -= 4;
Collisions only fire if the body has a velocity, the following table by samme should sum it up
You can apply acceleration, for the sake of example, to move the character towards the direction you're pointing at:
if (game.input.keyboard.isDown(Phaser.Keyboard.UP) || game.input.keyboard.isDown(Phaser.Keyboard.W)) {
game.physics.arcade.accelerationFromRotation(sprite.rotation, 200, sprite.body.acceleration);
}
In the image I'm also applying a certain drag and reducing acceleration when nothing is pressed but that's your call:
sprite.body.drag.x = 200;
sprite.body.drag.y = 200;
If you wanted to strafe an idea could be at dealing with multiple presses and applying a different accelerationFromRotation accordingly (with a variety of degrees converted with Phaser.Math.degToRad)
For debug's sake, if needed, you might want to use some of the following:
[...]
walls = map.createLayer("Tile Layer 1");
walls.debug = true;
[...]
function collisionHandler(obj1, obj2) {
console.log("Colliding!", obj1, obj2)
}
game.physics.arcade.collide(sprite, walls, collisionHandler, null, this);
game.debug.body(sprite);

Count Amount of Colored Squares in Canvas

Here is the fiddle: http://jsfiddle.net/sw31uokt/
Here is some of the relevant code for the incrementValue function I set up to count overall clicks within the canvas element.
What I would like to do is be able to display a count of each color, so "you have placed 14 red pixels, 3 blue pixels, 4 black pixels'.
function incrementValue()
{
var value = parseInt(document.getElementById('number').value, 10);
value = isNaN(value) ? 0 : value;
value++;
document.getElementById('number').value = value;
}
$(c_canvas).click(function(evt) {
var pos = getNearestSquare(getMousePos(c_canvas, evt));
if (pos != null) {
context.fillStyle=(currentColor);
context.fillRect(pos.x,pos.y,19,19);
incrementValue();
}
});
Basically, what MarkE said above ...
In the outer scope, add two new vars :
var palette = ["333333", "0000ff", "a0522d", "46ad42", "808080", "ffc0cb", "d73952", "ffe2a8", "ffff7d", "ffffff"];//as originally defined in the .spectrum() call.
var gridModel = [];//Eventually a sparse array of sparse arrays, representing colored grid squares. Uncolored grid squares remain undefined.
And two new functions, in the same scope :
function updateGridModel(pos, color) {
var x = (pos.x - 0.5) / 20;
var y = (pos.y - 0.5) / 20;
color = color.substr(1).toLowerCase();
if (!gridModel[x]) {
gridModel[x] = [];
}
gridModel[x][y] = palette.indexOf(color);
}
function paletteTally() {
//initialise an array, same length as palettes, with zeros
var arr = palette.map(function () {
return 0;
});
for (var x = 0; x < gridModel.length; x++) {
if (gridModel[x]) {
for (var y = 0; y < gridModel[x].length; y++) {
if (gridModel[x][y] !== undefined) {
arr[gridModel[x][y]] += 1;
}
}
}
}
return arr;
}
Modify the canvas's click handler to keep the gridModel up to date :
$(c_canvas).click(function (evt) {
var pos = getNearestSquare(getMousePos(c_canvas, evt));
if (pos != null) {
context.fillStyle = currentColor;
context.fillRect(pos.x, pos.y, 19, 19);
incrementValue();
updateGridModel(pos, currentColor); //keep the gridModel up to date.
}
});
Modify printColor() as follows :
function printColor(color) {
currentColor = color.toHexString();
$(".label").text(currentColor);
}
Modify the .spectrum() options and add an initialising call to printColor() as follows :
$("#showPaletteOnly").spectrum({
color: palette[0],
showPaletteOnly: true,
showPalette: true,
hideAfterPaletteSelect: true,
change: printColor,
palette: [palette] //<<<< palette is now defined as an outer var
});
printColor( $("#showPaletteOnly").spectrum('get') );//initialize currentcolor and $(".label").text(...) .
Now paletteTally() will return an array congruent with palette containing counts of each color.
EDIT 1
Original code above was untested but is now debugged and includes improved spectrum options. Demo.

How can I constrain drawing on the <canvas>?

I want to create a mobile web page where a shape appears on the screen, the user can only traces over the outline of the shape with his/her finger and then a new shape will appear. This library has a few good examples of what I am looking to do, just with more shapes. I have already found a couple of good examples for drawing on the canvas on a touch device here and here. The thing I don't know is how to constrain the line so you are only drawing on the path with a single continuous line. Is there something built in that will let me specify the only path you can draw, or do I have to write that logic by hand?
We can split the issue into two parts :
1) knowing if the user is on the path.
2) knowing if the user went on all path parts.
For 1), we can use the isPointInPath context2D method to know if the mouse/touch point (x,y) is on the curve. The constraint here is that you must build a closed surface, meaning a surface drawn by a fill(), not one built with a stroke(). So in case you are stroking thick lines, you have to do some little math to build the corresponding figures out of moveTo+lineTo+fill.
For 2) : build a list of 'check-points' for your shape. You might have, for instance 8 control points for a circle. Then decide of a distance at which the user will 'activate' the check point. Now the algorithm is, in pseudo-code:
mouseDown => check()
mouseMove => if mouse is down, check()
checkPointList = [ [ 10, 40, false ] , [ centerX, centerY, isChecked], ... ] ;
checked = 0;
function check() {
clear screen
draw the path
if (mouse down and mouse point on path) {
for ( checkPoint in CheckPointList) {
if (checkPoint near enough of mouse) {
checkPoint[3]=true;
checked++;
}
}
if (checked == checkPointList.length) ==>>> User DID draw all the shape.
} else
clear the flags of the checkPointList;
checked=0;
}
I did a moooost simple demo here, which quites work.
The control points are shown in red when disactivated, green when activated :
http://jsbin.com/wekaxiguwiyo/1/edit?js,output
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
function draw() {
ctx.clearRect(0,0,300,300);
drawShape();
drawCP();
}
// Shape
function drawShape() {
ctx.beginPath();
ctx.moveTo(30,5);
ctx.lineTo(80,5);
ctx.lineTo(80, 300);
ctx.lineTo(30,300);
ctx.closePath();
ctx.lineWidth= 16;
ctx.fillStyle='#000';
ctx.fill();
}
// Control points
var points = [ [50, 50, false], [50,120, false], [50, 190, false],[50,260, false ] ];
var pointsCount = 0;
function drawCP() {
for (var i=0; i<points.length; i++) {
var p = points[i];
ctx.fillStyle=p[2]?'#0F0':'#F00';
ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
}
}
function resetCP() {
for (var i=0; i<points.length; i++) {
points[i][2]=false;
}
pointsCount=0;
}
function testCP(x,y) {
var d=30;
d=sq(d);
for (var i=0; i<points.length; i++) {
if (sq(points[i][0]-x)+sq(points[i][1]-y)<d) {
if (!points[i][2]) pointsCount++;
points[i][2]=true
};
}
}
function sq(x) { return x*x; }
//
draw();
// most simple event handling
addEventListener('mousemove', mouseMove);
var r = cv.getBoundingClientRect();
function mouseMove(e) {
var x = e.pageX-r.left;
var y = e.pageY-r.top;
draw();
ctx.fillStyle='#000';
if (ctx.isPointInPath(x,y)) { 
ctx.fillStyle='#F00';
testCP(x,y);
} else {
resetCP();
}
ctx.fillRect(x-3,y-3,6,6);
var pathDrawn = (pointsCount == points.length);
if (pathDrawn) ctx.fillText('Shape drawn!!', 150, 150);
}

Categories