Is possible to have an event linked to object's own parameter? - javascript

I have an object (lets say a circle) with specific parameters (x,y and radius).
var circle = new Kinetic.Circle({
x: 100,
y: 100,
radius: 20
}
The radius of the circle is thereafter dynamically changed by the user.
Now I want a listener that continuously observes the radius, and every time the radius goes below 5, it deletes the circle. Something like:
circle.on("radiusLessThanFive",function (e) {
this.remove();
}
So how do I configure this radiusLessThanFive event that constantly listens to the radius of the circle?

Well, unfortunately, there isn't something built in that would do that. So you just have to make it part of your application logic.
Let's say you have a layer called shapesLayer. You can do:
function radiusLessThanFive(){
var circles = shapesLayer.get('.circle'); // I can't remember the getting all circles syntax, but something like this
for ( var i=0; i<circles.length; i++) {
if(cicrles[i].getRadius() < 5 ){
circles[i].remove();
shapesLayer.draw();
}
}
}
While this is not the most efficient solution ever, it gets the job done... unless you know how to create your own custom events in javascript (which would have to do the same thing as the above function anyways).
In case you'd like to take the custom event road, here is a link: http://www.sitepoint.com/javascript-custom-events/
Edit: regarding comment below
function radiusLessThanFive(myCircle){ //function which checks radius of single circle and deletes if less than 5
if(myCircle.getRadius() < 5 ){
myCircle.remove();
myCircle.getParent().draw();
}
}
}

Related

How to draw rectangles inside a polygon using p5.js

I am working on a project for a solar panel installation calculator on roofs. The solar panels are rectangular and have fixed widths and heights. But the roofs are dynamic in shape, i.e. a polygon with multiple sides. See the attached image for reference.
I am trying to find out the maximum number of panels that can be installed on the roofs. I need to place the rectangles (panels) inside the polygon (roof), which you can not overlap and can not touch the boundary.
I was trying to achieve this using p5.js.
Here are the basic codes.
function setup() {
createCanvas(800, 500);
background(220);
noLoop();
}
function draw() {
fill(237, 34, 93);
beginShape();
vertex(50, 50);
vertex(750, 50);
vertex(750, 450);
vertex(500, 450);
vertex(500, 200);
vertex(350, 200);
vertex(350, 450);
vertex(50, 450);
endShape(CLOSE);
var solarPanel = {
width: 40,
height: 40
};
//Algorithm to add the solar panels.
}
Link to p5.js editor - https://editor.p5js.org/abhishekdas/sketches/cVoxihH0n
I am new to p5.js. Can anyone please help me to find the correct Algorithm?
One simple option is to lay the panels in a grid. Your typical nested for loop should do the trick: remember to add the spacing required between panels when computing the position of each one (this should take care of overlaps).
Regarding panels laying inside the polygon of the shape you can get started with this point in polygon p5.js example. If you can test one point, you can check 4 points. If you can check 4 points (1 panel), you can check all panels) then simply remove the ones that aren't inside the roof polygon from the grid.
For reference here's function:
function pointInPoly(verts, pt) {
let c = false;
for (let i = 0, j = verts.length - 1; i < verts.length; j = i++) {
if (((verts[i].y > pt.y) != (verts[j].y > pt.y)) && (pt.x < (verts[j].x - verts[i].x) * (pt.y - verts[i].y) / (verts[j].y - verts[i].y) + verts[i].x)) c = !c;
}
return c;
}
...where verts is an array of p5.Vector objects and pt is also a p5.Vector (though a basic object .x, .y properties will also work)
That should be a simple straight forward approach.
If you need more complex options you can look into square/rectangle packing algorithms / simulations and optimisations etc. though given the point is to set up solar panels, the real world probably the simpler setup to install and maintain on the long run will outweight potentially space saving layouts that are odd shaped.

Interdependent derived attributes in Ampersand?

I'd like to create Ampersand State representation of a vector which simultaneously holds information about it's polar and rectangular representation.
i.e. I would like the user to be able to do:
vector.angle = 90
vector.mag = 1
console.log vector.y #=> 1
-or-
vector.x = 0
vector.y = 1
console.log vector.angle #=> 90
Can anyone think of a way to do this with ampersand?
This is the old question, but someone might need this.
Right away I can think of one way to do this. You'd need to make all of your variables independent and then listen to changes to update other values. So you'd define in props of the model variables angle, mag, x, y, and then attach event listeners in initialize of your view or somewhere else to each of these variables. For example, for angle you'd do something like this:
model.on('change:angle', function(model) {
//first, calculate new x and y values. In your model you have a new angle value
if (NEW_CALCULATED_X_VALUE != model.x || NEW_CALCULATED_Y_VALUE != model.y) {
model.set({
x: NEW_CALCULATED_X_VALUE
}, {
silent: true//I'm not triggering the 'change' event yet to avoid circular dependencies.
});
model.set({
y: NEW_CALCULATED_Y_VALUE
}, {
silent: true
});
//now, once I've set all new values, I can trigger events.
model.trigger('change:x', model); //this will trigger model.on('change:x'), but since based on this change angle and mag won't change, no circular dependency will appear.
model.trigger('change:y', model);
}
})
and repeat this for each of four variables (there is room for optimisation, but you get my idea. In order to avoid circular dependencies with this example you need to make sure that once you recalculate, for example, angle by using whatever x, you get the same angle.

Collision detection for one frame in JavaScript

I'm playing around with the Crafty.js game engine. I have a small game where the player is a ball and he drops down to platforms. Every time he hits the next platform, he gains a point. The score is stored as a variable and displayed on screen. I'm using Crafty's collision detection to detect when the player hits a new platform. If you're not familiar with Crafty, it's pretty simple, whenever the player hits the new platform, an event is fired and I can add one to the score.
My problem:
The game is running around 60fps. Every time the canvas reloads, Crafty will detect whether or not the player is actually touching the platform. This results in my score variable incrementing by one every single frame the player is touching a level. This is far from what I want. I want the score to be incremented ONCE per platform. This is a problem that I don't know how to fix.
Other things I've tried to solve it:
I also considered constantly measuring the distance that the player is from the starting point then I could tell (by division) what platform the player is on (since the platforms are equally vertically spaced). This was a problem however since Crafty was having issues giving me the current location of the player.
What I think would work: I think if I could have an event fired on the first frame that the player hits each platform, then that might work. (side note, If the player stays on one platform and jumps up and lands on the same platform a second time, I only want ONE point to be added. Not double jumping)
What I need from you guys: Have you ever had this issue? I really need to finish this game. And This minor technical problem is preventing me from finishing it. I'd love somebodys input.
The simplest solution would seem to be setting up a variable for each platform that tracks whether the player has landed on that platform or not. Then, whenever the ball is in contact with a platform that hasn't been landed on yet, award a point and mark that platform as landed on.
If the platforms are in linear sequence, you could even have a single integer variable that tracks which platform the player is on.
I wanted to expand on Taymon's answer because it seems like a really good solution for this problem. I would simply add a boolean attribute flag to the platform component that determines whether or not it has been counted.
Crafty.c('Platform', { isCounted: false })
Then, the logic for handling that hit would check that flag before counting it.
Here's a live example:
var score = 0;
Crafty.init(800, 600, $('#game')[0])
Crafty.background('blue')
/**
* because of the way gravity works
* (or maybe I just don't really understand it),
* I had to make separate entities for the
* gravity and the actual hit testing for each
* platform.
**/
Crafty.c('Platform', {
platform: function(x, y, visId) {
this.addComponent('2D, DOM')
.attr({
h: 20,
w: 200,
x: x,
y: y - 10,
visId: visId
});
return this;
},
isCounted: false,
visId: 0,
vis: function() {
return Crafty(this.visId);
}
});
Crafty.c('PlatformVis', {
platformVis: function(x, y) {
this.addComponent('2D, DOM, Color, PlatformVis')
.color('green')
.attr({
h: 20,
w: 200,
x: x,
y: y
});
return this;
}
});
// make some platforms
for (var i = 0; i < 5; ++i) {
var x = i * 200;
var y = i * 75 + 92;
var vis = Crafty.e('PlatformVis').platformVis(x, y)
Crafty.e('Platform').platform(x, y, vis.getId());
}
// player
Crafty.e('2D, DOM, Color, Twoway, Gravity, Collision')
.color('red')
.twoway(6, 14)
.gravity('PlatformVis')
.gravityConst(.8)
.attr({
x: 0,
y: 0,
h: 32,
w: 32
})
.checkHits('Platform')
.bind('HitOn', function(e) {
var platform = e[0].obj;
if (!platform.isCounted) {
platform.isCounted = true;
platform.vis().color('yellow');
Crafty('Score').text(++score);
}
}, this);
// score hud
Crafty.e('2D, DOM, Text, Score')
.text(score)
.textColor('white')
.textFont({
size: '32px'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/crafty/0.6.3/crafty-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Player uses the <b>Twoway</b> component, so use arrows/wasd to move/jump</p>
<div id="game"></div>

One function to rule them all

I am coding an application which displays balls obeying certain laws of physics.
So I have a Ball Object, and a path prototype. This path prototype calculates the coordinates of the ball at any given moment and draws it, that goes kinda like this :
Ball.prototype.path = function(v) {
modifying the ball coordinates...
ctx.arc(....);
(other canvas stuff)}
If I want to display an animation of the ball, I do this:
var ball1 = new Ball(...);
var ball2...
function loop () {
ctx.beginPath(); // The balls won't show up if I begin and close the Path in path(), I don't know why...
ball1.path();
ball2...
ctx.closePath();
};
setInterval(loop, 0.0015);
But I want to add a button which adds and displays balls. So I'm looking for a function which executes these commands to any ball added.
It's a little bit tricky, because it has to:
Create and name a new variable.
Execute path() according to that name.
All of that, in the same loop function so I can make a setInterval later.
EDIT: FIXED
#Frederik #Doorknob I've used a BallArray:
var BallArray = new Array(); i=0; function AddBallonClick() { i++; BalleArray.push(i) }; function loop() { for (var i=0;i<BalleArray.length;i++) { ctx.beginPath(); var ball = new Ball(0, 0, 40); ball.path(); ctx.closePath(); }; }; setInterval(loop, dt);
But I want to name the new variables ball_i, ie: ball_1, ball_2..., and I don't know how to. The script doesn't seem to be working even when I add the ball just once, so that's a problem too...
EDIT 2: FIXED
Also, I want to set an initial speed to every new ball, typically I do this:
ball.v = new V(...);
But now that I have an array, I added this to the loop, but it doesn't work...:
balles[i].v = new V(...)
EDIT 3:
I have another problem, whenever I click the button, a ball is not added and drawn, but instead the animation "replays". It seems that javascript can't draw balls at the same time with my kind of code:
function loop()
{
for(var i = 0; i < balls.length; i++) {
ctx.beginPath();
balls[i].path();
ctx.closePath();
}
};
setInterval(loop, dt);
EDIT: ALL FIXED
I've solved the last problem you just have to put the ctx.clearRect(0, 0, width, height) in the loop function but before the for(var i=0...).
Thanks to all of you :) !
As mentioned in the comments, the answer is arrays. You don't seem to quite understand them, so here's a quick overview. An array is sort of a list of objects. In your case, you probably want a list of Balls. You can initialize it like this:
var balls = []; // [] is about the same as new Array(), but more concise
To add a new ball to it, you can use push, passing it the ball:
balls.push(new Ball(/* ... */));
(You could, of course, pass it an already-existing ball, too:)
var ball = /* obtain ball from elsewhere */;
balls.push(ball);
You do seem to understand how to loop through the arrays, but not how to get the values as you loop through it. To loop through the array, you use a for loop:
for(var i = 0; i < balls.length; i++) {
// ...
}
Obviously, i will be an integer from 0 to balls.length. We can't do much with the number on its own, though; what we really want is the ball at that index in the array. You can do this by indexing the array. That would look like this:
var ball = balls[i];
Now ball contains the ball at position i in the balls array, and you can do whatever you want with it from there. In your case, you probably want to call path on it:
// If you've stored it into a variable as above:
ball.path();
// Or more concisely without having to store it into a variable:
balls[i].path();
With arrays, there is no need for variables named, e.g., ball_1, ball_2, etc. Instead, you just have an array, balls, and index it, e.g., balls[0], balls[1], etc.

How can I make Raphael.js elements "wiggle" on the canvas?

I'm working on a project that uses SVG with Raphael.js. One component is a group of circles, each of which "wiggles" around randomly - that is, slowly moves along the x and y axes a small amount, and in random directions. Think of it like putting a marble on your palm and shaking your palm around slowly.
Is anyone aware of a Raphael.js plugin or code example that already accomplishes something like this? I'm not terribly particular about the effect - it just needs to be subtle/smooth and continuous.
If I need to create something on my own, do you have any suggestions for how I might go about it? My initial idea is along these lines:
Draw a circle on the canvas.
Start a loop that:
Randomly finds x and y coordinates within some circular boundary anchored on the circle's center point.
Animates the circle from its current location to those coordinates over a random time interval, using in/out easing to smooth the effect.
My concern is that this might look too mechanical - i.e., I assume it will look more like the circle is tracing a star pattern, or having a a seizure, or something like that. Ideally it would curve smoothly through the random points that it generates, but that seems far more complex.
If you can recommend any other code (preferably JavaScript) that I could adapt, that would be great too - e.g., a jQuery plugin or the like. I found one named jquery-wiggle, but that seems to only work along one axis.
Thanks in advance for any advice!
Something like the following could do it:
var paper = Raphael('canvas', 300, 300);
var circle_count = 40;
var wbound = 10; // how far an element can wiggle.
var circleholder = paper.set();
function rdm(from, to){
return Math.floor(Math.random() * (to - from + 1) + from);
}
// add a wiggle method to elements
Raphael.el.wiggle = function() {
var newcx = this.attrs.origCx + rdm(-wbound, wbound);
var newcy = this.attrs.origCy + rdm(-wbound, wbound);
this.animate({cx: newcx, cy: newcy}, 500, '<');
}
// draw our circles
// hackish: setting circle.attrs.origCx
for (var i=0;i<circle_count;i++) {
var cx = rdm(0, 280);
var cy = rdm(0, 280);
var rad = rdm(0, 15);
var circle = paper.circle(cx, cy, rad);
circle.attrs.origCx = cx;
circle.attrs.origCy = cy;
circleholder.push(circle);
}
// loop over all circles and wiggle
function wiggleall() {
for (var i=0;i<circleholder.length;i++) {
circleholder[i].wiggle();
}
}
// call wiggleAll every second
setInterval(function() {wiggleall()}, 1000);
http://jsfiddle.net/UDWW6/1/
Changing the easing, and delays between certain things happening should at least help in making things look a little more natural. Hope that helps.
You can accomplish a similar effect by extending Raphael's default easing formulas:
Raphael.easing_formulas["wiggle"] = function(n) { return Math.random() * 5 };
[shape].animate({transform:"T1,1"}, 500, "wiggle", function(e) {
this.transform("T0,0");
});
Easing functions take a ratio of time elapsed to total time and manipulate it. The returned value is applied to the properties being animated.
This easing function ignores n and returns a random value. You can create any wiggle you like by playing with the return formula.
A callback function is necessary if you want the shape to end up back where it began, since applying a transformation that does not move the shape does not produce an animation. You'll probably have to alter the transformation values.
Hope this is useful!
There is a very good set of easing effects available in Raphael.
Here's a random set of circles that are "given" bounce easing.
Dynamically add animation to objects
The full range of easing effects can be found here. You can play around with them and reference the latest documentation at the same time.
Putting calls in a loop is not the thing to do, though. Use callbacks, which are readily available.

Categories