Is ist possible to get/set the radius of an existing path.circle? - javascript

Starting with a path - or many of these ...
var c=paper.Path.Circle(centerPoint, 30);
c.strokeColor="";
I want to have that circle grow its radius linearly. I can do this:
var children = paper.project.activeLayer.children;
paper.view.onFrame = function(event) {
for (var i = 0; i < children.length; i++) {
var item = children[i];
item.scale(1.01);
}
But that increases the radius exponentially !
Can I get the radius of a circle and change it ? Or do I have to create a new one, deleting the old one ?
How does scale() do this ?
I also would like to delete circle larger that a given size.
Thanks,
Sebastian

You can get the radius of a circle though it's indirect.
var radius = circle.bounds.topCenter.y - circle.bounds.center.y;
or
var radius = circle.bounds.width / 2
give you the radius of a circle. But a circle is stored as 4 segments with handles in and out, not as a circle-type object, so the radius is not stored anywhere.
In order to make it seem to grow you will have to delete the old one and draw a new one of the larger size.
It's also possible to scale it, but you want to scale it without compounding the scaling. So if you want it to grow 1.01 then 1.02 instead of 1.0201, etc. you will need to adjust the scaling factor each time.
It's not perfectly clear how you want to grow the circle, but here's some code that makes some assumptions about what you want to do:
function Scale() {
this.original = 1.0;
this.current = 1.0;
}
// target refers to original size in fractional terms, e.g., to
// grow by 1% specify 1.01 or to shrink by 1% specify 0.99. It returns
// the scale factor to apply to the current scale to achieve the
// target. So to increase the scale by 10% of the original size each
// time:
//
// var s = new Scale();
//
// for (i = 1.1; i <= 2.05; i += 0.1) {
// var scaleFactor = s.scale(i);
// }
//
// note the i <= 2.05 to allow for real number math issues.
//
Scale.prototype.scale = function(target) {
// get the scaling factor from the original size
var oFactor = target / this.original;
// now get the factor to scale the current size by
var cFactor = oFactor / this.current;
this.current = oFactor;
return cFactor;
}

Related

Is there a way to set a maximum area on a random rectangle in Paper.js?

I'm trying to place a randomly sized rectangle in the center of 1200x1200 canvas, with a min size of 200x200 and a max size of 900x900. However, I would also like to set a maximum area (of half the 900x900 maximum space) on the rectangle to prevent it from ever being one giant square. Basically, I want to create large random rectangles, or squares of a similar area, but never huge squares. I have the random rectangle part working, but not sure on how to limit the overall area.
var maxSize = new Size(700, 700); // max size of 900x900 when combined with +200 below
var randomSize = Size.random(); // random number between 0 and 1
var size = (maxSize * randomSize) + 200; // effectively creates a 200x200 minimum size, max 900x400
var box = new Rectangle(new Point(0,0), size); // creates rectangle at 0,0 with size
box.center = (600, 600); // centers rectangle at 600,600
var path = new Path.Rectangle(box); // draws rectangle on screen
path.fillColor = '#cccccc';
You have two constraints, your max size and area...
I'm assuming that we need to meet both, we will need to get the minimum between those
The area of a rectangle is equal to the product of its length and width
With a given area we can get a max side as a square of the area = Math.sqrt(max_area)
Integrating that to your code:
const max_area = 788544
var max_side = Math.sqrt(max_area) - 200
var maxSize = new Size(Math.min(700, max_side), Math.min(700, max_side));
...
Based on the answer above, I ended up generating random width and height, then checking the height against a max_height extrapolated from area and width. There's probably a way to streamline it, but it seems to work okay. Thank you!
// Get random integer between two values, inclusive
function getRandomLength(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}
var max_area = 260000;
var width = getRandomLength(200,900);
var random_height = getRandomLength(200,900);
var max_height = max_area / width;
var height = Math.min(random_height, max_height);
var size = new Size(width, height);
var box = new Rectangle(new Point(0,0), size);
box.center = (600, 600); // centers rectangle at 600,600
var boxPath = new Path.Rectangle(box);
boxPath.fillColor = '#cccccc';

svg transform rotation precision

I'm trying to create a simple test with an svg, allowing me to change a quality setting (FPS), and a speed of rotation on an svg.
I noticed that the transform, rotate functionality works with degrees. To my knowledge it is not possible to increase precision by using decimals as degrees.
So are we stuck using the rotation degrees, which gives us a maximum of 360 pieces to divide a circle into, or is there a way to increase the precision somehow ?
The reason I ask is because I created a pen https://codepen.io/KaGys/pen/yKVjrr?editors=1111 in which I added several quality settings. A higher quality setting results in a higher number of frames per second. This means that if 1 degree is the smallest unit I can rotate at, I would need to start delaying the animation to go slower than 1 degree per frame, which at my highest quality setting (fastest frames per second) is still a pretty fast rotation. I hope the question is clear, if not, please let me know and I'll try to clarify.
var ultra = 1000 / 120;
var high = 1000 / 60;
var medium = 1000 / 30;
var low = 1000 / 10;
var quality = ultra;
var speed = 1; // degrees per frame on ultra quality (converts to lower quality settings to maintain consistency)
// I tried doing this on per second basis, which works but the problem is that degrees can not be decimals which causes inconsistent behavior
speed = (speed * quality) / ultra; // Recalculate the speed for consistency across quality settings
var rotateSettings = {
path: document.getElementById("pathGroup"),
active: false,
speed: speed,
quality: quality
};
function rotate() {
if (this.active) {
setTimeout(rotate.bind(this), this.quality);
var currentRotation = Number(
/\d+/.exec(this.path.getAttributeNS(null, "transform"))
);
currentRotation = currentRotation%360;
console.log(this.speed);
this.path.setAttributeNS(
null,
"transform",
`rotate(${currentRotation + this.speed}, 60, 60)`
);
}
}
document.getElementById("btnRotate").addEventListener("click", e => {
if (!rotateSettings.active) {
rotateSettings.active = true;
document.getElementById("btnRotate").style.background = "green";
document.getElementById("btnRotate").style.color = "white";
rotate.bind(rotateSettings)();
} else {
rotateSettings.active = false;
document.getElementById("btnRotate").style.background = "white";
document.getElementById("btnRotate").style.color = "green";
}
});
You can read the transform angle via the SVG DOM
var currentRotation = this.path.transform.animVal[0].angle;

javascript game sprite positioning

I'm trying to create a chess board, and place it in the middle of the screen, so far i cannot get it to be directly in the center. i don't want to hard code the position to the screen because i'm going to be dealing with different screen sizes.
var winsize = cc.director.getWinSize();
var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
for (i=0; i<64; i++){
var tile = cc.Sprite.create(res.tile_png);
this.addChild(tile,0);
tile.setPosition(winsize.width+i%8*50/-10, winsize.height-Math.floor(i/8)*50);
}
But the tiles and positioning is completely off
#jumpman8947, if you're using Cocos2d js perhaps you have a similar line: cc.view.setDesignResolutionSize(480, 320, cc.ResolutionPolicy.SHOW_ALL);
In this particular case the game will scale to any sceeen, but still run in 480x320 resolution, so no matter what screen resoultion you use, the center in the cocos world would always be cc.p(240, 160) so no matter what's the window size or the screen resolution, the resolution of the game stays the same
You can read more about resolution policies here (and in official js-doc):
http://www.cocos2d-x.org/wiki/Multiple_Resolution_Policy_for_Cocos2d-JS
Also please be aware, that the Sprite position in Cocos is the position of the centre of the sprite, not bottom left corner
In your question it's not completely clear exactly what you want. However, I made some assumptions. The explanation for my solution is embedded in the comments in the code below.
// var winsize = cc.director.getWinSize();
// Here is some example hard-coded return values:
var winsize = {width: 600, height: 400};
// You can change these numbers to see how they influence
// the outcome.
// var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
// This line doesn't seem relevant for the question you asked.
// Or, rather, the following calculations will result in the tiles
// being centred on the screen anyway, so this calculation here
// is unnecessary.
// Being a chess board, I assume that you want the tiles to be square,
// i.e. to have the same width and height.
// If so, first find out which is the minimum dimension
// and calculate the tile size as being 1/8 of that.
var minDimn = Math.min(winsize.width, winsize.height);
var tileSize = minDimn / 8;
// Find out how far in from the left and how far down from the top
// you need the upper left corner of the upper left tile to start.
// This assumes that you don't need any "margin" around the board.
// (If you do need such a "margin", basically subtract it twice
// from each of winsize.width and winsize.height above.)
// Start with default values of 0 for each, but then add in the
// excess for the longer dimension, but divide it by two
// because that excess will be split between either
// the top and bottom or the left and right.
var xStart = 0, yStart = 0;
if (winsize.width > winsize.height) {
xStart = (winsize.width - winsize.height) / 2;
} else if (winsize.height > winsize.width) {
yStart = (winsize.height - winsize.width) / 2;
}
// Instead of looping through all 64 positions in one loop,
// loop through all the horizontal positions in an outer loop
// and all the vertical positions in an inner loop.
for (i = 0; i < 8; i++) {
// For the horizontal dimension, calculate x for each tile
// as the starting position of the left-most tile plus
// the width of the tile multiplied by the number of tiles (0-based)
var x = xStart + i * tileSize;
// Now the inner loop
for (j = 0; j < 8; j++) {
// Same type of calculation for the y value.
var y = yStart + j * tileSize;
// You can see the values in this demo here.
document.write("<pre>(" + x + ", " + y + ")</pre>");
// The following two lines don't seem to be relevant to the question.
// var tile = cc.Sprite.create(res.tile_png);
// this.addChild(tile,0);
// Use your calculated values in your function call.
// tile.setPosition(x, y);
}
}

jQuery Circles animation on circle path

I'm trying to make big circle and move divs along the circle's circumference.
Each div must change the content inside the big circle.
The number of div(s) must be dependent on how many are fetched from database (from table category).
I tried to do this and modified the code by putting .eq() but the problem with .eq is that next circle will appear after that circle, all put in the same place. I want them all to appear at the same time like this without repeating functions
Updated your fiddle:
http://jsfiddle.net/wyW2D/1/
Used:
var t = -1.6;
var t2 = -1.6;
var x = 0;
var t = [-1.6, -1.6, -1.6], // starting angle in radians for each circle
delta = [0.05, 0.03, 0.02], // change in radians for each circle
// e.g the first moves fastest, the last
// slowest. if this gets too big, the
// movement won't look circular, since the
// animation is really a bunch of straight lines
finish = [1.4, 1.0, 0.6]; // the stopping point in radians for each
// circle. if the circle size changes, this
// may need to change
function moveit(i) {
t[i] += delta[i]; // move the angle forward by delta
var r = 300; // radius (the .inner div is 600 x 600)
var xcenter = -30; // center X position: this reproduces the .inner horizontal
// center but from the body element
var ycenter = 420; // center Y position: same here but vertical
// Basic trig, these use sin/cos to find the vert and horiz offset for a given
// angle from the center (t[i]) and a given radius (r)
var newLeft = Math.floor(xcenter + (r * Math.cos(t[i])));
var newTop = Math.floor(ycenter + (r * Math.sin(t[i])));
// Now animate to the new top and left, over 1ms, and when complete call
// the move function again if t[i] hasn't reached the finish.
$('div.circle'+(i+1)).animate({
top: newTop,
left: newLeft,
},1, function() {
if (t[i] < finish[i]) moveit(i);
});
// You can slow down the animation by increasing the 1, but that will eventually
// make it choppy. This plays opposite the delta.
}
// Start the ball rolling
$("document").ready(function(e) {
moveit(0);
moveit(1);
moveit(2);
});
This was a quick change to reduce the code to one function that used arrays (t, delta, finish) to keep track of the three circles. It could be improved to accept arbitrary circles, of any size, at any starting / ending angle.
Also, this kind of animation is much easier with CSS. It is simple to specify and has much better performance.

Drawing zoomable audio waveform timeline in Javascript

I have raw 44,1 kHz audio data from a song as Javascript array and I'd like to create a zoomable timeline out of it.
Example timeline from Audacity:
Since there are millions of timepoints normal Javascript graphics libraries probably don't cut it: I think, not sure, that normal graph libraries will die on this many timepoints. But does there exist already libraries for this sort of visualization for JS? Canvas, webGL, SVG all are acceptable solutions.
A solution preferably with zoom and pan.
Note that this happens strictly on client side and server-side solutions are not accetable.
I've looked into this same problem pretty extensively. To the best of my knowledge, the only existing project that does close to what you want is wavesurfer.js. I haven't used it, but the screenshots and the description sound promising.
See also this question.
Best of luck.
You cannot simply take the the waveform data and render all data points, this is terribly inefficient.
Variable explanation:
width: Draw area width in pixels, max is screen width
height: Same as width but then height of draw area
spp: Samples per pixel, this is your zoom level
resolution: Number of samples to take per pixel sample range, tweak for performance vs accuracy.
scroll: You will need virtual scrolling for performance, this is the scroll position in px
data: The raw audio data array, probably several million samples long
drawData: The reduced audio data used to draw
You are going to have to only take the samples that are in the viewport from the audio data and reduce those. Commenly this results in a data set that is 2 * width, you use this data set to render the image.
To zoom out increase spp, to zoom in decrease it. Changing scroll value pans it.
The following code has O(RN) complexity where N is width and R is resolution. Maximum accuracy is at spp <= resolution.
The code will look something like this, this gets the peak values, you could do rms or average as well.
let reduceAudioPeak = function(data, spp, scroll, width, resolution) {
let drawData = new Array(width);
let startSample = scroll * spp;
let skip = Math.ceil(spp / resolution);
// For each pixel in draw area
for (let i = 0; i < width; i++) {
let min = 0; // minimum value in sample range
let max = 0; // maximum value in sample range
let pixelStartSample = startSample + (i * spp);
// Iterate over the sample range for this pixel (spp)
// and find the min and max values.
for(let j = 0; j < spp; j += skip) {
const index = pixelStartSample + j;
if(index < data.length) {
let val = data[index];
if (val > max) {
max = val;
} else if (val < min) {
min = val;
}
}
}
drawData[i] = [min, max];
}
return drawData;
}
With this data you can draw it like this, you could use lines, svg etc:
let drawWaveform = function(canvas, drawData, width, height) {
let ctx = canvas.getContext('2d');
let drawHeight = height / 2;
// clear canvas incase there is already something drawn
ctx.clearRect(0, 0, width, height);
for(let i = 0; i < width; i++) {
// transform data points to pixel height and move to centre
let minPixel = drawData[i][0] * drawHeigth + drawHeight;
let maxPixel = drawData[i][1] * drawHeight + drawHeight;
let pixelHeight = maxPixel - minPixel;
ctx.fillRect(i, minPixel, 1, pixelHeight);
}
}
I have used RaphaelJS for SVG rendering in the browser at it has performed very well. It is what I would go for. Hopefully SVG will be up to the task.

Categories