Trying to model a hole being drilled using JS/Canvas - javascript

I'm trying to represent a hole being drilled using a web based application and I'm having difficulties. I'm in control of the inputs and various variables but unsure of how best to approach the issue.
The simulation currently has these values, they're for test purposes only.
Radius of drill bit= 15
inches Length of drill = 1000ft
RPM of drill = 100
The stratigraphic layers have their own properties, and in this instance have:
Name = bla
Depth = 100ft (models start and finish of each layer - here, 0 to 100ft down)
Permeability = 10 (currently unsure how best to model)
I don't know at the moment how to model the pressure being applied to the drill but a constant can be used if need be.
I thought I'd be able to calculate the volume of the drillbit and then apply a percentage of sorts that would represent the strength of the material so as to slow the progress of the drill.
In it's simplest form I'm trying to figure out how best to represent a hole being drilled and then calculate the area of the hole as it's being drilled.
Here's my test code:
The time variable is passed in my the way of a JavaScript Date() object. I'm hoping on using the Date object to represent the actual amount that may be drilled real time.
The test case of the canvas that is modelling it is 800px deep with a well depth of 20000ft = 25ft per pixel. I'm still trying to make sense of the output and correct it, it's a slow process. Below is the rudimentary test code.
// aggregate function:
// depth(t) = (a * RPM - b * density) * t
function depthOverTime(time, density, a, b){
var aa = (a * RPM - b * density) * time;
//(1 * 50 - 1 * 20) * 60
//(50 - 20) * 60
//30 * 60
//1800
console.log("DOT: " + roundTo2(aa)+"ft^3" + "T: " +time);
return aa;
}
function volumeExcavated(t){
var rad = 15 * 0.083333;
dot = depthOverTime(t,20,1,1);
var a = Math.PI * Math.pow(rad, 2) * dot;
console.log("VEOT: " + roundTo2(a)+"ft^3");
return a;
}
This is a sample of the console log: http://pastebin.com/UW1M73jY

As a real world simulation, this isn't nearly enough information to come up with a code description. So, assuming this is a purely fictional exercise, as a simple simulation the hole you're drilling is always going to have the drill's penetrated volume, with the drill's speed of penetration determined by the substrate's density (ignoring a million real world material properties). The RPM value determines how much substrate gets scraped per time unit, so density slows your penetration down, RPM speeds your penetration up, and if we assume the drill is indestructible, that's pretty much all we need to know.
The volume excavated is equal to the drill's volume, ignoring its groove. Keep things simple and assume a cylindrical drill, and the volume is simply πr²h, where h is the penetrated depth, and r is (obviously) the drill radius.
Start with an aggregate function:
depth(t) = (a * RPM - b * density) * t
we don't know a or b, but we do know what they represent: a is the pressure behind our drill, and b is an unknown constant that balances our function. The first step is to normalise this function with respects to the various units (RPM is per minute, t is most likely in seconds, for instance), after which we can start figuring out what a and b should concretely be (also note that we can normalise this function by fixing either a or b to 1, and setting the remaining free variable to whatever is necessary to balance the equation with respect to the reality we're simulating)
Once we have our depth-over-time function, we're pretty much done, as the volume excavated is simply
volume(t) = π * r² * depth(t)
or, if you're a student of physics and prefer the common quadratic expression:
volume(t) = τ/2 * r² * depth(t)

Related

get a point on a bezier curve without guessing or brute force

I originally wanted to use four points (as a bezier Curve is defined with 4 points), but that forces me to brute force the position, so I tried a different approach i now need help with:
I have a start point P0, an end point P1 and slopes m0 and m1 which are supposed to give me the start/end slope to calculate a Bezier Curve inbetween them.
The Curve is supposed to be in the form of a function (3rd degree), since I need to get the height y of a given point x.
Using the HTML5Canvas i can draw a bezier curve no problem and using this function
that allows me to calculate any given point given a percentage of the way i can get the center point of the curve. But I don't need it depending on t but rather the y depending on x, so not halfway of the curve but halfway of the x distance between P0 and P1.
Image to visualize:
Left is what i can calculate, right is what i need.
I've been trying to calculate the cubic function given the two points P0, P1 as well as the slopes m0, m1, which results into four equations which i can't seem to be able to solve with only variable inputs. I've also tried to use the above function to calculate the t using the x value (which is known), but no dice there either.
I need to avoid using approximations or costly loops for these calculations as they are performed many times a second for many objects, thus this answer is not feasible for me.
Any help is appreciated.
I've encountered the same problem in a project I'm working on. I don't know of a formula to get the y coordinate from the x, and I suspect you'll have trouble with that route because a bezier curve can have up to 3 points that all have the same x value.
I would recommend using the library BezierEasing, which was designed for this use case and uses various performance enhancing techniques to make lookups as fast as possible: https://github.com/gre/bezier-easing
To solve this problem, you need to rewrite Bezier equation in power polynomial form
X(t) = t^3 * (P3.X-3*P2.X+3*P1.X-P0.X) +
t^2 * (3*P0.X + 6*P1.X+3*P2.X) +
t * (3*P1.X - 3P2.X) +
P0.X
if X(t) = P0.X*(1-ratio) + P3.X*ratio
then
let d = ratio * (P0.X - P3.X)
and solve cubic equation for unknown t
a*t^3 + b*t^2 + c*t + d = 0
JS code here
Then apply calculated t parameter (there might be upto three solutions) to Y-component and get point coordinates. Note that formulas are close (no loops) and should work fast enough
Thank you to everyone that answered before, those are generally great solutions.
In my case I can be 100% sure that I can convert the curve into a cubic function, which serves as the approximation of the bezier curve using the result of this calculation.
Since i have control over my points in my case, I can force the P0 to be on x=0, which simplifies the linear system calculations and thus allows me to calculate the cubic function much easier like this:
let startPoint: Utils.Vector2 = new Utils.Vector2(0, 100);
let endPoint: Utils.Vector2 = new Utils.Vector2(100, 100);
let a: number, b: number, c: number, d: number;
function calculateFunction() {
let m0: number = 0;
let m1: number = 0;
a = (-endPoint.x * (m0 + m1) - 2 * startPoint.y + 2 * endPoint.y) / -Math.pow(endPoint.x, 3);
b = (m1 - m0 - 3 * a * Math.pow(endPoint.x, 2)) / (2 * endPoint.x);
c = m0;
d = startPoint.y;
}

Efficiently sorting large arrays of people by their proximity to the user

So I'm trying to create a list of the 15 closest people in an array of varying sizes. The array will almost always be ~100 objects in size, but for the sake of testing, I'm trying to make it work with 10,000 (there may be need for the project to be scaled up to these numbers further down the line).
Currently, the method in place is to loop through the array of people, and calculate their proximity to the user based on both the person in question's and the user's latitude and longitude (the former of which is stored in the array). This is done using the haversine formulae and works well (though it does take ~500 milliseconds).
The problem however is that when run on a mobile device (a Samsung Galaxy S5 for the sake of this example), performance really suffers. The time taken for the S5 to sort through 10,000 records in order of how close they are to a pre-determined latitude and longitude is a staggering 1,500-1,600 milliseconds, an unacceptable delay for an app that will be doing many things either side of this process.
So my question is, am I missing some fundamentally more efficient means of sorting this list? Is there an alternative formulae available that is more efficient? Could I simply calculate the combined difference in Latitude and Longitude in .000001s and sort based on that?
Notes:
The user's location is variable, so proximities cannot be stored
I am aware that I'm asking a mobile CPU to perform 100,000,000 calculations in a short space of time and so this may be unavoidable
The method of sorting is the native JavaScript sort method, below is a simplified version of what I am doing to test these timings:
patientArray.sort(function(a, b)
{
return GetDistanceToPoint(a["Lat"], a["Lng"]) - GetDistanceToPoint(b["Lat"], b["Lng"]);
});
// Function to get the User's distance to a point
function GetDistanceToPoint(Latitude, Longitude)
{
// Check if the User's current Latitude and Longitude are available
if(currentLat && currentLng)
{
// Convert degrees to a radius
function degreeToRadius(degree)
{
return degree * (Math.PI/180)
}
// Variable to store radius of the Earth in Km
var earthRadius = 6371;
// Calculate the distance between the two points
var dLat = degreeToRadius(Latitude-currentLat);
var dLon = degreeToRadius(Longitude-currentLng);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(degreeToRadius(currentLat)) * Math.cos(degreeToRadius(Latitude)) * Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = earthRadius * c;
return d;
}
return "-1";
}
It all has to be tested but here are some ideas that I would try.
For heavy use of trigonometric functions you can use lookup tables. This is always a good idea. For example precompute 360 (or more) values of sin() and for every sin(radians) in your code use sinTable[degrees].
(I say 360 values as an example because with that your index is an angle in degrees but any value will do and it all depends on what precision you need - it can have thousands of values if needed.)
Avoid unnecessary calculations. May seem obvious but people often write something like x/(2*Math.PI) instead of x*A where A (a better name of course) is computed once as 1/(2*Math.PI).
Memoize every value that you can, if it makes sense.
If your data have some specific qualities, like for example never spanning half of the planet, then you can try to cheat a little bit and use coordinates on a flat plane - then you only have to compute square roots (which could also be precomputed to use lookup tables).
Those are the first things that crossed my mind. Hope it helps.
UPDATE:
You made an edit so I know a little bit more now. Here are my tips:
Don't convert degrees to radians. Keep degrees and use them as indexes in lookup tables of precomputed values of trigonometric functions. If you need more precision then multiply the degrees by 10 or something and use a scale from 0 to 3600 instead of 0 to 360. Find a good size/precision compromise that works for you.
You can eliminate all sin() and cos() calls that way and if you're lucky you can eliminate atan2(). I wouldn't worry so much about sqrt() but you can eliminate it too if you know what the values are typically going to be. If values of functions like sqrt() or atan2() are not known up fron then you can fall back to real functions for values that are out of range of your lookup tables.
Avoid to many function calls. Instead of an anonymous function that you pass to patientArray.sort(), that calls GetDistanceToPoint(), that calls degreeToRadius() - you need only one function that can be passed directly as an argument to .sort() and that function doesn't need to return d - it can return just c (in your example).
You don't need to multiply everything by earthRadius if you only use that value for sorting.
Another quick ideas: using typed arrays (for lookup tables), and asm.js and SIMD.js for additional optimization if possible.
Those are the first things that come to mind. I'd love to hear how much faster your code can get. Good luck.
UPDATE 2:
Another idea - instead of (or in addition to) optimizing the GetDistanceToPoint() you can also make sure that it isn't called more than once for every object.
Instead of:
patientArray.sort(function(a, b)
{
return GetDistanceToPoint(a["Lat"], a["Lng"]) - GetDistanceToPoint(b["Lat"], b["Lng"]);
});
You can try doing something like:
patientArray.forEach(function (element) {
element.distance = GetDistanceToPoint(element["Lat"], element["Lng"]);
});
or maybe for loop will be faster:
for (var i = 0; i < patientArray.length; i++) {
var element = patientArray[i];
element.distance = GetDistanceToPoint(element["Lat"], element["Lng"]);
}
to store the value for the entire patientArray array.
And then in the sort function:
patientArray.sort(function(a, b)
{
return a.distance - b.distance;
});
It may hopefully save you a lot of calls to GetDistanceToPoint.

Transfer function of WaveShaperNode

I'm having trouble understanding how the transfer function for a WaveShaperNode in the Web Audio API works. As I understand, a transfer function is a waveshaper which takes in a signal input x and produces a new signal y. So,
y = f(x)
I understand that if x equals zero, then so should y. Therefore, 0 = f(0). And that to fit in an appropriate range, y should be between [-1, 1], so this function: y = x / (1 + |x|) limits the output range to [-1, 1]. And that Chebyshev Polynomials are useful for transfer functions used to "distort" musical input signals.
But for a transfer function you need the input signal x in order to manipulate it and create an output y. However, with a WaveShaperNode in the Web Audio API, you don't have access to the original input x (or do you?). Often I see algorithms like:
for(var i = 0; i < sampleRate; i++){
var x = i * 2 / sampleRate - 1;
this.curve[i] = (3 + distortionAmount) * x * 20 * (Math.PI / 180) / (Math.PI + distortionAmount * Math.abs(x));
}
Where, in the above code, this.curve is a Float32Array representing the graphing of each sample frame. And I assume x here is supposed to represent the input audio signal. Yet, it doesn't actually represent the exact input audio signal. Is this because it just represents an average sinusoid and the actual input doesn't matter? Does the WaveShaperNode take the original input x and use (multiply?) the general curve we created to calculate the output y?
The WaveShaper node does not enable generic transfer functions, per se - but you can use it to do that. To answer your question - x is the offset into the array, with a little preprocessing.
The curve you give to the waveshaper is like a lookup table for x - spread across the range [-1,1]. Y does not need to be in [-1,1] - but x does. So, to solve f(x) for x in [-1,1], you just get the value at
curve[ Math.floor( (x+1)/2 * (curve.length-1) ) ];
or something like that.
It's not actually true that if x equals zero, then so must y; but it's likely. You could use the waveshaper to implement a DC offset, for example.
That "sampleRate" bit in your demo code is goofy - you should use maxint, not samplerate, to determine the size of your array. sampleRate is a time-domain thing, and waveshaper only operates in the amplitude vector.
The WaveShaperNode is described as applying a non-linear distortion. As such it would not have a classical linear time-invariant transfer function (which strictly only applies to linear distortions).

How exactly does V8 optimize/inline?

I'm wondering whether it is possible to get knowledge of how exactly V8 optimizes and inlines things.
I created three simple test functions which all calculate the sine of a angle in degrees. I put them all into closures so that V8 should be able to inline the local variables.
1. Using a precalculated constant Math.PI / 180, and then do Math.sin(x * constant).
I used this code:
var test1 = (function() {
var constant = Math.PI / 180; // only calculate the constant once
return function(x) {
return Math.sin(x * constant);
};
})();
2. Calculating the constant on the fly.
var test2 = (function() {
var pi = Math.PI; // so that the compiler knows pi cannot change
// and it can inline it (Math.PI could change
// at any time, but pi cannot)
return function(x) {
return Math.sin(x * pi / 180);
};
})();
3. Using literal numbers and calculating the constant on the fly.
var test3 = (function() {
return function(x) {
return Math.sin(x * 3.141592653589793 / 180);
};
})();
Suprisingly, the results were as follows:
test1 - 25,090,305 ops/sec
test2 - 16,919,787 ops/sec
test3 - 16,919,787 ops/sec
It looks like pi did get inlined in test2 as test2 and test3 result in exactly the same amount of operations per second.
On the other hand, the division does not seem to be optimized (i.e. precalculated), since test1 is significantly faster.
Why is the constant not precalculated if you don't do so manually in this case?
Is it possible to see how V8 exactly optimizes functions on a certain webpage?
An educated guess at your first question:
Strictly speaking, it can't constant-fold the pi / 180 part, because you don't do pi / 180 in the second and third function. You just divide (x * pi) by 180 (the multiplication has precedence).
Now, you may be asking why it doesn't change the order of operations to wind up with something it can optimize (this process is called reassociation, by the way)... after all, the result is equivalent (a * b / c = (a * b) / c). Math says so, right?
Well, math says so, but math doesn't use floating point numbers. With floats, things are more complicated. x * pi may be rounded, and then the reordering would lead to a different result. The errors will probably be tiny, but still, the leading rule of compiler optimization is: Thou must not change the program's outcome. It's better to perform suboptimal on a few math benchmarks written in an unfortunate way than being off by a pixel (yes, that may be noticeable) in some graphics code.
To answer your second question, you can see the bytecode that V8 optimized your JS to using this tool: http://mrale.ph/irhydra/2/ . It's fantastic for low-level tuning of code in Chrome.

Random 2D Zombie walking

I need a simple AI script to make a zombie object 'walk' around randomly. I've googled and googled but all I can find is stuff on the mathematics function 'Random Walk'. Maybe it's related?
I'm using my own JavaScript 2D engine and I need to have something like:
function update() {
//Move using available physics engine stuff:
// this.position, this.rotation, this.velocity, this.torque, this.acceleration
// this.torqueAcceleration, this.drag, this.torqueDrag
}
Thanks! :)
I did do a similar script in C# XNA 2 years ago.
The logic is simple. Let's talk about the logic of walking one zombie in pseudo-code:
The following is the walkable space / platform for the zombie:
x = 0 ___________________________ x = 100
Pick a random position to spawn the zombie.
If the position is more than half the walkable space, zombie face left. Otherwise zombie face right.
x = 0 _____________________<_____ x = 100
x = 0 _______>___________________ x = 100
Pick a random distance to move between 5 to the end of the walkable space.
Start the walking the zombie for the distance picked.
Wait for a amount of time between 2 seconds to 15 seconds (15 seconds because some zombies just like to daze around).
Go to Step 2
This should help you to create your script for all the zombies to roam around the map aimlessly. You can vary the values I've written in the pseudo-code to suit your game.
I hope you do know this has nothing to do with specific coding at all. This is purely logic. AI (artificial intelligence) logic to be more specific.
Your zombie spawns, what next? You want to assign a behavior. Write down some different behaviors.
Stand still
Walk
If the zombie is standing still, do nothing.
If the zombie is walking, pick a random speed and random direction.
Pick a new behavior every x seconds.
What I'd do:
// pick behavior between 0 and 5 seconds
var behaviorTimeout = setTimeout(randomBehavior, Math.round(Math.random() * 5000);
function randomBehavior() {
// set random values
this.rotation = Math.random() * 360; // assuming its in degrees
this.velocity = Math.random() * MAX_ZOMBIE_SPEED;
// run this again in something between 0 and 5 seconds
behaviorTimeout = setTimeout(randomBehavior, Math.round(Math.random() * 5000);
}
This obviously only counts for walking. You'd need another random number deciding if it should walk or stop.
This is the most basic AI logic someone can think of. If you're not talking about zombies anymore, but actual thoughtful creatures understanding and creating AI will get way harder.

Categories