ThreeJS: 3D Object Area Calculation (Triangulated) - math

I need to calculate the area/surface of a whole object in threeJS. Thats what I have:
var _len = object.geometry.faces.length,
_area = 0.0;
if (!_len) return 0.0;
for (var i = 0; i < _len; i++) {
var va = object.geometry.vertices[object.geometry.faces[i].a];
var vb = object.geometry.vertices[object.geometry.faces[i].b];
var vc = object.geometry.vertices[object.geometry.faces[i].c];
var ab = vb.clone().sub(va);
var ac = vc.clone().sub(va);
var cross = new THREE.Vector3();
cross.crossVectors( ab, ac );
_area += cross.lengthSq() / 2;
}
The results are kind of wrong. I get a floating value, fine, but comparing a very small object with a big object. The smaller could have a bigger surface with the provided code. I checked on many different objects and got not realistic values, when comparing them.
Actually the objects having the biggest faces, but being the smallest in the overall surface, seem to have to highest values with the current version of the code.
I hope someone could have a look at the code and see whats wrong. Very much appreciated!

You are using lengthSq(), is that right? I guess you need the length of the cross vector, not the length squared.

Related

Google Earth Engine: Image intersection and inverse intersection

I am new to Google Earth Enginge and I struggle to bring together two images in Google Earth Engine to get the areas which are in both images and the areas which are only part of one image to show forest cover change (loss, gain, no change).
My code so far which seems to at least display what I want by stacking the images above each other:
var treeCanopyCoverVis = {
min: 0.0,
max: 100.0,
palette: ['ffffff', 'afce56', '5f9c00', '0e6a00', '003800'],
};
var forest2000 = ee.ImageCollection('NASA/MEASURES/GFCC/TC/v3')
.filterDate('2000-01-01', '2000-12-31')
.select('tree_canopy_cover')
.reduce(ee.Reducer.mean());
var forest2000_ab60 = forest2000.gt(60).selfMask();
Map.addLayer(forest2000_ab60, {palette: '#d80078'}, 'Loss');
var forest2015 = ee.ImageCollection('NASA/MEASURES/GFCC/TC/v3')
.filterDate('2015-01-01', '2015-12-31')
.select('tree_canopy_cover')
.reduce(ee.Reducer.mean());
var forest2015_ab60 = forest2015.gt(60).selfMask();
Map.addLayer(forest2015_ab60, {palette: '#ebb13a'}, 'Gain');
// var loss = forest2015_ab60.intersection(forest2000_ab60);
print(forest2015_ab60);
print(forest2000_ab60);
var remain = forest2015_ab60.and(forest2000_ab60);
Map.addLayer(remain, {palette: '#746d75'}, 'Remain');
With this code the gain still includes the remain part and the loss part also still includes the remain part. I need kind of the subtraction. All functions I now tried result in errors. I appreciate any help!
How my current result looks:
I could somehow solve it with try and error.
The code:
var forest2000 = ee.ImageCollection('NASA/MEASURES/GFCC/TC/v3')
.filterDate('2000-01-01', '2000-12-31')
.select('tree_canopy_cover')
.reduce(ee.Reducer.mean());
var forest2015 = ee.ImageCollection('NASA/MEASURES/GFCC/TC/v3')
.filterDate('2015-01-01', '2015-12-31')
.select('tree_canopy_cover')
.reduce(ee.Reducer.median());
var gain = forest2000.lt(60).and(forest2015.gt(60));
Map.addLayer(gain.selfMask(), {palette: '#ebb13a'}, 'gain');
var loss = forest2000.gt(60).and(forest2015.lt(60));
Map.addLayer(loss.selfMask(), {palette: '#d80078'}, 'loss');
var nochange = forest2000.gt(60).and(forest2015.gt(60));
Map.addLayer(nochange.selfMask(), {palette: '#746d75'}, 'no change');

Function to generate randomly incrementing number over time

I'm trying to make a fake download count. It should increment randomly over time. Some download-count-like patterns would be nice.
Is this possible without using a database, or storing a counter anywhere?
My idea is to check the number of seconds that have passed since my app was released. Then just throw that into a formula which spits out the fake download count. Users can request to see the download count at any time.
Is there a math function that increments randomly? I could just pass my secondsPassed into there and scale it how I'd like.
Something like this: getDownloadCount(secondsPassed)
Edit: here's an example solution. But it gets worse performance over time.
downloadCount = 0
loop secondsPassed/60 times // Loop one more time for every minute passed
downloadCount += seededRandom(0, 10)
Making a fake download count doesn't sound like a nice thing to do. However in designing secure communication protocols, there are legitimate use cases for monotonically growing functions with some randomness in their values.
I am assuming you have:
A growth model given as a monotonically growing function providing approximate values for the desired function.
Access to a time stamp, which never decreases.
Ability to store a constant random seed along with the function definition.
No way to store any updated data upon the function being queried.
First you decide on a window length, which will control how much randomness will be in the final output. I expect you will want this to be on the order of one hour or a few.
Figure out which window the current time is within. Evaluate the reference function at the start and end of this window. Consider the rectangle given by start and end time of the window as well as min and maximum value given by the reference function. Feed the corners of this rectangle and your constant seed into a PRNG. Use the PRNG to choose a random point within the rectangle. This point will be on the final curve.
Perform the same computation for one of the neighbor windows. Which neighbor window to use depend on whether the first computed point on the curve is to the left or the right of the current time.
Now that you have two points on the curve (which are reproducible and consistent), you will have to iterate the following procedure.
You are given two points on the final curve. Consider the rectangle given by those corners. Feed the corners and your constant seed into a PRNG. Use that PRNG to chose a random point within the rectangle. This point will be on the final curve. Discard one of the outer points, which is no longer needed.
Since the Y-values are restricted to integers, this procedure will eventually terminate once your two points on the curve have identical Y-coordinate, and you will know, that the function has to be constant between those two points.
You could implement a Morris Counter.
It works like this: start off by setting the counter to 1. Each time you want to increase the count (which could be every iteration of some loop or every time an event happens, but does not need to be determined randomly), then you do a random procedure to determine the effect it has on the counter.
It can have possibly no effect, or it can have the effect of raising the order of magnitude of the count. The probability is based on whether or not n successive fair coin flips all turn up heads, where n is the number of bits needed to encode the current counter value in binary. As a result, once the counter has gotten pretty high, it's very hard to make it go even higher (the state of the counter models a phenomenon where by you are already way overestimating the count, so now you need lots of nothing-happens events to compensate, making the count more accurate).
This is used as a cheap way to store an approximate count of a very large collection, but there's no reason why you can't use it as your randomly increasing counter device.
If you want better accuracy, or you want the count outputs to be more "normal" numbers instead of always powers of 2, then you can just create several Morris Counters, and at each step average together the set of current counts across them all.
You are after a sequence which always increases by a random amount, depending on how long you last requested the sequence.
This can be done through a random sequence that is always seeded the same.
Then we iterate through the same sequence each time to get the graph.
We need a function that increments our counter, store the new Time and Count and return the count.
Ideally we would model the increases as a poisson process, but a linear one here will do.
class Counter {
private static int counter = 0;
private static int time = 0;
private static double rate = 5.0;
private Random r;
public Counter(int seed){
counter = 0;
r = new Random(seed);
}
private int poisson(double rate, int diff){
// We're gonna cheat here and sample uniformly
return r.Next(0, (int)Math.Round(rate * diff));
}
public int getNext(int t){
var diff = t - time;
time = t;
if (diff <= 0) return counter;
counter += this.poisson(rate, diff);
return counter;
}
}
void Main()
{
var c = new Counter(1024);
for(var i = 0; i< 10; i++){
Console.WriteLine(String.Format("||{0}\t|{1}\t||",i,c.getNext(i)));
}
}
This outputs (for example):
||t |hit||
||0 |0 ||
||1 |3 ||
||2 |4 ||
||3 |6 ||
||4 |6 ||
||5 |8 ||
||6 |10 ||
||7 |13 ||
||8 |13 ||
||9 |16 ||
For some deterministic function f, (perhaps f(x) = x, or if your fake app is REALLY awesome f(x) = 2^x), and a random function r which outputs random number that's sometimes negative and sometimes positive.
Your graphing function g could be:
g(x) = f(x) + r
EDIT
How about this: https://gamedev.stackexchange.com/questions/26391/is-there-a-family-of-monotonically-non-decreasing-noise-functions
Well it's not "random" but you could use A*(X/B + SIN(X/B)) (scaled by some number) to introduce some noise. You can adjust A and B to change the scale of the result and how often the "noise" cycles.
Really, any periodic function that has a first derivative within some bounds could work.
as quick solution you can use something like this (code in java):
static long f(final int x) {
long r = 0; // initial counter
long n = 36969L; // seed
for (int i = 0; i <= x; i++) {
n = 69069L * n + 1234567L; // generate Ith random number
r += (n & 0xf); // add random number to counter
}
return r;
}
by playing with numbers 36969L and 0xf you can achieve different results
numbers 69069L and 1234567L are from standard LCG
the main idea - create simple random, with the same seed and for every passed x (number of seconds) replay random additions to counter
A good model for random events like downloads is the Poisson distribution. You need to estimate the average number of downloads in a given time period (hour, say) and then invert the Poisson distribution to get the number of downloads in a time period given a uniformly distributed random number. For extra realism you can vary the average according to time of day, time of week, etc. Sample algorithms are available at http://en.m.wikipedia.org/wiki/Poisson_distribution#Generating_Poisson-distributed_random_variables.
Here is a javascript implementation of a "fake" download-counter that appears the same to everyone. This always returns the same results for everyone each time and doesn't require database or files to do that. It also gracefully handles the case where you don't ask for new data at the same time, it will still look natural next time you request a day.
https://jsfiddle.net/Lru1tenL/1/
Counter = {
time:Date.now(),
count:0,
rate:0.45
};
Counter.seed = function(seed, startTime)
{
this.time = startTime,
this.count = 0,
this.prng = new Math.seedrandom(seed);
this.prng.getRandomInt = function(min, max) {
return Math.floor(this() * (max - min)) + min;
};
};
Counter.getNext = function(t){
var diff = t - this.time;
console.log(diff);
if(diff <= 0) return this.count;
this.time = t;
var max = Math.ceil(diff/100 * this.rate);
console.log("max: " + max);
this.count += this.prng.getRandomInt(0,max);
return this.count;
};
var results = [];
var today = Date.now();
Counter.seed("My Random Seed", today);
for (var i = 0; i < 7; i++) {
if(i === 4)
{
results.push(null);
} else {
var future = today + 86400000 * i;
results.push(Counter.getNext(future));
}
}
console.log(results);
var data = {
labels: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"],
datasets: [
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: results
}
]
};
var ctx = document.getElementById("myChart").getContext("2d");
var myLineChart = new Chart(ctx).Line(data);
Is the javascript. It creates a counter object that increments when requested based on the time of the previous value asked for. The repeatability comes through a thirdparty library "seedrandom" and the chart is drawn using chartjs.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/seedrandom.min.js">
</script>
<body>
<canvas id="myChart" width="600" height="400"></canvas>
</body>
</html>
You can use Unix timestamp. Something like:
Downloads = constant + ( unix time / another constant )
You can vary both constants to get a reasonable number.
P.S: Thats if you want a linear function, otherwise you can do:
Downloads = (unix time) ^ constant
and so on.

Direct linear transformation in CSS

Is it possible to do a DLT in CSS? If so, how is this accomplished? I can't think of a way using just transform: matrix... If this is not possible, what would be an alternative approach?
The particular effect I'm trying to achieve is laying out divs in a way similar to how Safari does this:
Here is a very rough and non-generic answer to your request. http://jsfiddle.net/3t5SM/
You could easily extend it to get a generic much better solution.
in my CSS,
#id1, #id4, #id7{
-webkit-transform: rotateY(40deg);
}
#id3, #id6, #id9{
-webkit-transform: rotateY(-40deg);
}
#id2, #id5, #id8{
-webkit-transform: scale(0.94);
}
the basic idea is to create a style for each column (here i'm calling the id's but again, it would be better to have a style for each column and define the columns as .left, .middle, .right, etc)
I'll update my post tonight if I have the time to go into the details :)
EDIT: as promise, here is a little better version. Now it is much more generic and depending on the size of your window, you'll get the right number of cubes. It is still far from being perfect (you could play with the size of the cubes in order to get a better depth feeling), but in general you see that it is possible, even dynamically :)
here is the fiddle: http://jsfiddle.net/P84qd/4/
To go a little into the details of the javascript:
function dispatch(){
var sizeOfImg = 150;
var windowWith = document.body.offsetWidth;
var widthRest = windowWith%sizeOfImg;
var nbSquareRow = Math.floor(windowWith/sizeOfImg);
dlt(nbSquareRow);
var marginVal = widthRest/(nbSquareRow+1);
var lineout = document.getElementById('lineout');
lineout.style.paddingLeft = marginVal+'px';
lineout.style.paddingTop = marginVal+'px';
var square = document.getElementsByTagName('div');
for(var i=0, length = square.length;i<length; i++){
if(square[i].className === 'square'){
square[i].style.marginRight = marginVal+'px';
square[i].style.marginBottom = marginVal+'px';
}
}
}
dispatch();
window.onresize = function(e){dispatch();};
function dlt(nbSquareRow){
var maxRotatDeg = 40;
var isEven=true;
if(nbSquareRow%2 == 0){
var unityRotatDeg = maxRotatDeg/(nbSquareRow/2);
}else{
var unityRotatDeg = maxRotatDeg/((nbSquareRow-1)/2);
isEven = false;
}
var middle = Math.floor(nbSquareRow/2);
var mySquares = document.getElementsByTagName('div');
for(var j=0, sqleng = mySquares.length;j<sqleng; j++){
if(mySquares[j].className == 'square'){
var colNumb = (parseInt(mySquares[j].id)-1)%nbSquareRow;
var myMultiplier = (middle-colNumb);
if(isEven && myMultiplier<=0){
myMultiplier--;
}
mySquares[j].style.webkitTransform = 'rotateY('+(unityRotatDeg*myMultiplier)+'deg)';
}
}
}
The dispatch function is a simple function that will distribute the squares on your web page with equal margins (top, left, right, bottom). I took it from 1.
The dlt function calculates the number of columns and defines the rotation amount for each column (in my example the maximum rotation value is 40). The rest of the code are purely some math checks in order to make it work correctly.
In order to get a better result, you should play with the size of each square, but be careful because the dispatch function also needs to know the size of the square to calculate how many squares will be allowed to be displayed per row. I'll let you have fun with it ;)
​

flex actionscript LatLngBounds can not get it to work

My question is at the very end of the post.
I have tried everything from setting a timer for all the markers to be set to all kinds of calculations of the four corners, but nothing seems to be working.
Each time that I add a marker to the markermanager, I call this function below
public function markerSetBounds(someLat , someLng):void{
var bounds:LatLngBounds = new LatLngBounds();
for(var i:int = 0; i < myMarkers.length; i++)
{
var currentLatLon:LatLng = new LatLng(someLat , someLng);
bounds.extend(currentLatLon);
}
googleMap.setZoom(googleMap.getBoundsZoomLevel(bounds));
googleMap.setCenter(bounds.getCenter());
}
I believe I know why this does not work. I am only sending one set of lat, lng at a time.
However, when I tried the following, flex told me that it did not know what myMarkers[i].lat meant.
The following is how I fill myMarkers array
var someMarker:Marker = new Marker(new LatLng(someLat , someLng), new MarkerOptions({tooltip:someAddress, hasShadow: true}));
myMarkers.push(someMarker);
This is how I want to traverse through the array, but flex does not understand what .lat means.
for(var i:int = 0; i < myMarkers.length; i++)
{
var currentLatLon:LatLng = new LatLng(myMarkers[i].lat , myMarkers[i].lng);
bounds.extend(currentLatLon);
}
My question is how do I traverse through the myMarkers array to set currentLatLon. I have also tried a for each(var someObj:Marker in myMarkers) but it finds nothing. The markers are showing up on the map, but the bounds are not working.
Have you tried doing something like:
(myMarkers[i] as Marker).lat
Is this a problem at run time or compile time?
OK, I figured out was what the issue and it was that I had to place things in the correct order.
First, declare the LatLngBounds.
Second, make the markers.
Third, set the zoom
Forth, extend the bounds.
bounds = new LatLngBounds();
covToXML = new XML(event.result);
xmlToList = new XMLList(covToXML);
listToCol = new XMLListCollection(xmlToList);
someLat = Number(listToCol.children().child("geometry").child("location").child("lat").text());
someLng = Number(listToCol.children().child("geometry").child("location").child("lng").text());
someAddress = String(listToCol.children().child("formatted_address").text());
var markerOptions:MarkerOptions = new MarkerOptions();
markerOptions.icon = new (whichIcon(GlobalVars.randomIcon));
markerOptions.tooltip = someAddress;
markerOptions.hasShadow = true;
someMarker = new Marker(new LatLng(someLat , someLng), markerOptions);
someMarker.addEventListener(MapMouseEvent.CLICK,markerClicked);
myMarkers.push(someMarker);
googleMap.addOverlay(someMarker);
for each(someMarker in myMarkers)
{
var newLatLng:LatLng = someMarker.getLatLng();
// Alert.show(newLatLng.toString());
bounds.extend(newLatLng);
}
googleMap.setCenter(bounds.getCenter());
googleMap.setZoom(googleMap.getBoundsZoomLevel(bounds));
Thanks for all the suggestions and questions, which helped me to the solution.

How can I increase a GLatLngBounds (rectangle) by 10% in google maps API?

I have a map with a bunch of points on it, and I want to zoom such that all points fit on the screen. The code for this is fairly simple:
var bounds = new GLatLngBounds();
for (var n = 0; n < points.length; n++) {
bounds.extend(points[n].getLatLng());
}
zoomLevel = map.getBoundsZoomLevel(bounds);
to get the zoom level, you have to pass in a bounds object. To create a bounds object, you either give it two points like a rectangle, or you pass it a point and it increases in size (if necessary) to incorporate the point which is what I do above.
The problem is that the points appear right on the edge of the screen. I'd like to increase the size of the bounds by say, 10%. I can get the two points out of my bounds object using bounds.getNorthEast(); and bounds.getSouthWest(); but to increase them by 10% of the size of the bounds is proving tricky. I tried toSpan() on the bounds which gives you a LatLng containing the size of the bounds, and then did 10% of the Lat and Lng and added it to my coordinates. However, it did not seem to work. My code for this attempt was:
var pointNorthEast = bounds.getNorthEast();
var pointSouthWest = bounds.getSouthWest();
var latAdjustment = ((pointNorthEast.lat() - pointSouthWest.lat()) * increasePercentage) / 2;
var lngAdjustment = ((pointNorthEast.lng() - pointSouthWest.lng()) * increasePercentage) / 2;
var newPointNorthEast = new GLatLng(pointNorthEast.lat() + latAdjustment, pointNorthEast.lng() + lngAdjustment);
var newPointSouthWest = new GLatLng(pointSouthWest.lat() - latAdjustment, pointSouthWest.lng() - lngAdjustment);
bounds = new GLatLngBounds();
bounds.extend(newPointNorthEast);
bounds.extend(newPointSouthWest);
By my reckoning, bounds should then be 10% bigger. I'm not certain it represents just a rectangle or stores any coordinate data or not so I didn't take chances by adding/subtracting half of the increase on each side to keep it centered.
However, trying various increasePercentage values such as 1.1 or 1.5 it does not seem to make a difference.
Therefore I've either cocked up my logic, my maths, or the fact that zoomLevel is not a smooth scale is hampering my attempts.
I think I might have to just do zoomLevel -= 1 and have done with it, but I wanted to see if anyone had any ideas first.
here's updated code using current api version (v3.0):
var increasePercentage = 1.10; //10%
var pointSouthWest = mb.getSouthWest();
var latAdjustment = (pointNorthEast.lat() - pointSouthWest.lat()) * (increasePercentage - 1);
var lngAdjustment = (pointNorthEast.lng() - pointSouthWest.lng()) * (increasePercentage - 1);
var newPointNorthEast = new google.maps.LatLng(pointNorthEast.lat() + latAdjustment, pointNorthEast.lng() + lngAdjustment);
var newPointSouthWest = new google.maps.LatLng(pointSouthWest.lat() - latAdjustment, pointSouthWest.lng() - lngAdjustment);
bounds = new google.maps.LatLngBounds();
bounds.extend(newPointNorthEast);
bounds.extend(newPointSouthWest);
map.fitBounds(bounds);
The problem is in your Math, here's how you need to calculate adjustments:
var latAdjustment = ((pointNorthEast.lat() - pointSouthWest.lat()) * (increasePercentage - 1);
var lngAdjustment = ((pointNorthEast.lng() - pointSouthWest.lng()) * (increasePercentage - 1);

Resources