Bullet Physics - "glue" faces together - bulletphysics

I have two cubs in a bullet softbody simulation (example of how I make such a cube below). How can I constrain them so that is it like the two cubes were 'glued' together on a face?
const btVector3 c[]={
btVector3(-1,-1,-1),
btVector3(+1,-1,-1),
btVector3(-1,+1,-1),
btVector3(+1,+1,-1),
btVector3(-1,-1,+1),
btVector3(+1,-1,+1),
btVector3(-1,+1,+1),
btVector3(+1,+1,+1)
};
btSoftBody* psb=btSoftBodyHelpers::CreateFromConvexHull(pdemo->m_softBodyWorldInfo,c,8, true);
I found this thread where the OP asks about a "one-way vertex-to-vertex joint". I'm looking for a similar (but two-way) joint.

What kind of interaction do you want between the two objects? Different approaches will likely lead to different results. Also keep in mind that you're going to need a very high iteration number and small time step for the solver to be able to always keep the objects glued.
I suggest you build a single, heterogeneous, soft body by using different masses for the nodes and stiffness values for the links.
const btVector3 c[] =
{
btVector3(+1,-1,-1),
btVector3(+1,+1,-1),
btVector3(+1,-1,+1),
btVector3(+1,+1,+1),
btVector3(-1,-1,-1),
btVector3(-1,-1,+1),
btVector3(-1,+1,+1),
btVector3(-1,+1,-1),
btVector3(+2,-1,-1), // "Other" cube starts here
btVector3(+2,+1,-1),
btVector3(+2,-1,+1),
btVector3(+2,+1,+1)
};
// Masses, should be tuned depending on gravity/timestep
const btScalar m[] =
{
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f,
0.1f
};
btSoftBody* psb = new btSoftBody(pdemo->m_softBodyWorldInfo,12,c,m);
// VERY stiff material
btSoftBody::Material aMat;
aMat.m_kLST = 1; // Linear stiffness coefficient [0,1]
aMat.m_kAST = 0; // Area/Angular stiffness coefficient [0,1]
aMat.m_kVST = 1; // Volume stiffness coefficient [0,1]
// Softer material
btSoftBody::Material bMat;
bMat.m_kLST = 0.5f; // Linear stiffness coefficient [0,1]
bMat.m_kAST = 0; // Area/Angular stiffness coefficient [0,1]
bMat.m_kVST = 0.5f; // Volume stiffness coefficient [0,1]
for(int i=0; i < 12; ++i)
{
for(int j = i + 1; j < 12; ++j)
{
// If either node has x==+2, use softer material
psb->appendLink(i,j, (i > 7 || j > 7) ? &bMat : &aMat, false);
}
}
This should get you started. Good luck!

Related

How to turn a bezier curve into a sinusoidal form in svg?

I have been trying to make this shape in svg. Problem is, I want to manipulate it with the blue handles. I have already made a simple arrow and am able to change its shape with quadratic bezier curves. But I am unable to figure out how to do it for this kind of shape. Is there some way to transform a line into this squiggly form?
You can use the getPointAtLength and getTotalLength APIs to "ride" along any arbitrary SVG geometry, and generate your sine wave.
Here's an example in plain TypeScript (find an interactive React CodeSandbox with a couple extra bells and whistles here).
function computeWave(
path: SVGPathElement,
freq: number,
maxAmp: number,
phase: number,
res: number
) {
// Get the points of the geometry with the given resolution
const length = path.getTotalLength();
const points = [];
if (res < 0.1) res = 0.1; // prevent infinite loop
for (let i = 0; i <= length + res; i += res) {
const { x, y } = path.getPointAtLength(i);
points.push([x, y]);
}
// For each of those points, generate a new point...
const sinePoints = [];
for (let i = 0; i < points.length - 1; i++) {
// Numerical computation of the angle between this and the next point
const [x0, y0] = points[i];
const [x1, y1] = points[i + 1];
const ang = Math.atan2(y1 - y0, x1 - x0);
// Turn that 90 degrees for the normal angle (pointing "left" as far
// as the geometry is considered):
const normalAngle = ang - Math.PI / 2;
// Compute the sine-wave phase at this point.
const pointPhase = ((i / (points.length - 1)) * freq - phase) * Math.PI * 2;
// Compute the sine-wave amplitude at this point.
const amp = Math.sin(pointPhase) * maxAmp;
// Apply that to the current point.
const x = x0 + Math.cos(normalAngle) * amp;
const y = y0 + Math.sin(normalAngle) * amp;
sinePoints.push([x, y]);
}
// Terminate the sine points where the shape ends.
sinePoints.push(points[points.length - 1]);
// Compute SVG polyline string.
return sinePoints
.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x},${y}`)
.join(" ");
}
which generates the blue line following the orange one (which is described as M100,100 C150,100,150,250,200,200):
You can of course adapt this to e.g. "pinch" the wave at the ends, to avoid any abrupt ends with an arbitrary phase, etc.
There is are no such transformations in SVG. So you have to find equally spaced points on the bezier curve and offset them according to the sinusuidal equation.
This is a great video to explaining bezier curves and using a look up table to find equally spaced points on the arc: https://www.youtube.com/watch?v=aVwxzDHniEw
To understand how to offset the points, you need a bit co-ordinate geometry. I have created a Desmos graph to help you out: https://www.desmos.com/calculator/4lbhfcro8t
Notice that the sine curve in the above graph is not uniform. That is because the points used for offsetting are equally spaced 't' values. You have to use equally spaced arc lengths as demonstrated in the video.

How to batch the calls in google app script

I was trying to optimize runtime some code that ran really slowly, when searching it up google came up with this: https://developers.google.com/apps-script/guides/support/best-practices#:~:text=Use%20batch%20operations,-Scripts%20commonly%20need&text=Alternating%20read%20and%20write%20commands,data%20out%20with%20one%20command.
it shows an example of inefficient code:
var cell = sheet.getRange('a1');
for (var y = 0; y < 100; y++) {
xcoord = xmin;
for (var x = 0; x < 100; x++) {
var c = getColorFromCoordinates(xcoord, ycoord);
cell.offset(y, x).setBackgroundColor(c);
xcoord += xincrement;
}
ycoord -= yincrement;
SpreadsheetApp.flush();
}
and efficient code:
var cell = sheet.getRange('a1');
var colors = new Array(100);
for (var y = 0; y < 100; y++) {
xcoord = xmin;
colors[y] = new Array(100);
for (var x = 0; x < 100; x++) {
colors[y][x] = getColorFromCoordinates(xcoord, ycoord);
xcoord += xincrement;
}
ycoord -= yincrement;
}
sheet.getRange(1, 1, 100, 100).setBackgroundColors(colors);
I tried to understand how this code works and tried running it but first of all "cell" doesn't seem to get used and I do not understand the code at all. What is a version of the code that actually works and how does this make it more efficient? And what part of this code batches the calls and how can I use this in my own coding?
Basically, what it does it reduce the number of calls by its methods.
The inefficient code above calls offset and setBackgroundColor every loop thus making it resource intensive and time consuming.
For the efficient code, it uses the loop to build the 2d array which then will be used on a single call on the method setBackgroundColors to execute it by bulk.
What takes much of the run time is the method calls, so reducing it would be very beneficial. This would make use of loops to build 2d arrays for the array versions of the methods single execution.
e.g.:
setValue -> setValues
setBackgroundColor -> setBackgroundColors
Pseudocode:
from:
loop {
setValue(x[i][j])
}
to:
loop {
x[i][j] = data
}
setValues(x)

Bug in space movement FLASH

I'm new to flash and can't find the bug in my code. I want a space ship to be able to accelerate in a vector, to not be able to accelerate over a max velocity, to keep that velocity vector when it stops accelerating but then suffer from friction (space dust ,) ). (It happens in a 2d stage.)
I think my math is correct, but I get a bug with velVector sometimes returns NaN - here's my code:
var friction:Number = .96;
var force:Number = .1;
var maxVel:Number = 3;
var accVector:Object = new Object();
var velVector:Object = new Object();
var velocity:Number;
var acceleration:Number;
If the ship points in the right direction it executes function 'accelerate' and if not it executes function 'drift'. It always executes 'moveship'
function accelerate():void {
curRotation.vx = Math.cos(rotation/180*Math.PI);
curRotation.vy = Math.sin(rotation/180*Math.PI);
var angle:Number = Math.atan2(curRotation.vy, curRotation.vx);
velocity = Math.sqrt(velVector.vx^2 + velVector.vy^2); //get velocity in both directions
acceleration = (maxVel - velocity)/maxVel*force; //this is to make it harder to accelerate when it approaches maxvelocity
accVector.vx = Math.cos(angle) * acceleration; //accelerate in each dimension
accVector.vy = Math.sin(angle) * acceleration;
velVector.vx += accVector.vx; //add acceleration to velocity
velVector.vy += accVector.vy;
}
function drift():void {
velVector.vx *= friction; //decrease velocity when not accelerating
velVector.vy *= friction;
}
function moveShip():void {
trace("velocity", velocity)
x += velVector.vx; //move
y += velVector.vy;
}
Many thanks!
I guess, your problem is in using of unsafe math operations without values validation. So, your velVector depends on accVector which depend on acceleration. You have a division operation counting "acceleration" val. That's it:
acceleration = (maxVel - velocity)/maxVel*force;
In AS3 you CAN divide by zero, and you'll get Infinity in such situation. But, there is also a chance to get NaN in case you are dividing zero by zero:
var acceleration:Number = 0 / 0;
trace(acceleration); //NaN
If you try doing something with NaN writing the result in Number or untyped variable (not int!), you'll always get NaN:
var a:Number = 5 * NaN;
trace(a); //NaN
So, I here is an example, which gives NaN:
acceleration = 0 / 0; //NaN
accVector.vx = Math.cos(angle) * acceleration; //NaN regardless on "angel" value
velVector.vx += accVector.vx; //NaN regardless on "velVector" value.
I hope, this will help.

Trouble creating a spectrogram

I know it was asked a thousand times before, but I still can't find a solution.
Searching SO, I indeed found the algorithm for it, but lacking the mathematical knowledge required to truly understand it, I am helplessly lost!
To start with the beginning, my goal is to compute an entire spectrogram and save it to an image in order to use it for a visualizer.
I tried using Sound.computeSpectrum, but this requires to play the sound and wait for it to end, I want to compute the spectrogram in a way shorter time than that will require to listen all the song. And I have 2 hours long mp3s.
What I am doing now is to read the bytes from a Sound object, the separate into two Vectors(.); Then using a timer, at each 100 ms I call a function (step1) where I have the implementation of the algorithm, as follows:
for each vector (each for a channel) I apply the hann function to the elements;
for each vector I nullify the imaginary part (I have a secondary vector for that)
for each vector I apply FFT
for each vector I find the magnitude for the first N / 2 elements
for each vector I convert squared magnitude to dB scale
end.
But I get only negative values, and only 30 percent of the results might be useful (in the way that the rest are identical)
I will post the code for only one channel to get rid off the "for each vector" part.
private var N:Number = 512;
private function step1() : void
{
var xReLeft:Vector.<Number> = new Vector.<Number>(N);
var xImLeft:Vector.<Number> = new Vector.<Number>(N);
var leftA:Vector.<Number> = new Vector.<Number>(N);
// getting sample range
leftA = this.channels.left.slice(step * N, step * (N) + (N));
if (leftA.length < N)
{
stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms);
return;
}
else if (leftA.length == 0)
{
stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms);
return;
}
var i:int;
// hann window function init
m_win = new Vector.<Number>(N);
for ( var i:int = 0; i < N; i++ )
m_win[i] = (4.0 / N) * 0.5 * (1 - Math.cos(2 * Math.PI * i / N));
// applying hann window function
for ( i = 0; i < N; i++ )
{
xReLeft[i] = m_win[i]*leftA[i];
//xReRight[i] = m_win[i]*rightA[i];
}
// nullify the imaginary part
for ( i = 0; i < N; i++ )
{
xImLeft[i] = 0.0;
//xImRight[i] = 0.0;
}
var magnitutel:Vector.<Number> = new Vector.<Number>(N);
fftl.run( xReLeft, xImLeft );
current = xReLeft;
currf = xImLeft;
for ( i = 0; i < N / 2; i++ )
{
var re:Number = xReLeft[i];
var im:Number = xImLeft[i];
magnitutel[i] = Math.sqrt(re * re + im * im);
}
const SCALE:Number = 20 / Math.LN10;
var l:uint = this.total.length;
for ( i = 0; i < N / 2; i++ )
{
magnitutel[i] = SCALE * Math.log( magnitutel[i] + Number.MIN_VALUE );
}
var bufferl:Vector.<Number> = new Vector.<Number>();
for (i = 0; i < N / 2 ; i++)
{
bufferl[i] = magnitutel[i];
}
var complete:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
complete[0] = bufferl;
this.total[step] = complete;
this.step++;
}
This function is executed in the event dispatched by the timer (stepper).
Obviously I do something wrong, as I said I have only negative values and further more values range between 1 and 7000 (at least).
I want to thank you in advance for any help.
With respect,
Paul
Negative dB values are OK. Just add a constant (representing your volume control) until the number of points you want to color become positive. The remaining values that stay negative are usually just displayed or colored as black in a spectrogram. No matter how negative (as they might just be the FFT's numerical noise, which can be a huge negative dB number or even NaN or -Inf for log(0)).

HTML5 get number of alpha-pixels in canvas

I need to get the ratio of transparent/opaque pixels in my canvas. What is the best way to do this?
UPDATE:
Based on the below posts I ended up writing this code:
function getNumberOfAlphaPixels(can) {
var step = 200; //We skip 200 pixels to make it work faster
var ctx = can.getContext('2d');
var imgd = ctx.getImageData(0, 0, can.width, can.height);
var pix = imgd.data;
var alphaPixelsNum = 0;
for (var i = 0; i < pix.length; i += 4*step) {
if (pix[i+3] == 0) {
alphaPixelsNum += step;
}
}
return alphaPixelsNum;
}
As mentioned, counting the individual opaque pixels is the only way to do it.
The following is probably faster than the pseudo-code shown in the other answer.
Despite JIT tracing/code analysis, it does help for speed to spell out basic low level operations.
function alphaRatio(ctx) {
var alphaPixels = 0;
var data = ctx.getImageData(0,0, ctx.canvas.width,ctx.canvas.height).data;
for(var i=3; i<data.length; i+=4) {
if(data[i] > 0) alphaPixels++;
}
return alphaPixels / (ctx.canvas.width * ctx.canvas.height);
}
When it comes to pixels on a canvas, you have no choice but to grab them all with getImageData() - .data in your result is a byte array of the data in RGBA order. Don't expect great performance from this, but if that's what you have to do...
Count 'em up (pseudocode):
var alphaPixels, alpha, totalPixels
for each pixel (x, y)
alpha = imageData.data[((y*(imageData.width*4)) + (x*4)) + 3] // <- real code
// offset to alpha channel ^
if (alpha > 0)
alphaPixels++
return alphaPixels / totalPixels
Reference
Pixel manipulationMDN