Famo.us physics engine: forces and walls not behaving as expected - physics

I am tyring to create a grid of particles, using the famous physics engine. I thought that if 25 particles were placed beteween 4 bounding walls (forming a square) and they all had an equal repulsion force to each other, they would naturally form a grid - ie. they would all be held as far away from each other as possible, given the limits of their world. I expected this to work, even if the particles were just added without an initial position. However, even if I give them an initial position, they don't hold in place unless the force is very small.
Also, I thought that if a wall had a restitution of 0, then a particle would just stop on colliding with it. When I run the code below, I can see particles bouncing off walls. The pen is at:
http://codepen.io/timsig/pen/pJJrOa
What am I failing to grasp? - thanks in advance
define('main', function (require, exports, module) {
// import dependencies
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface')
var Modifier = require('famous/core/Modifier');
var PhysicsEngine = require('famous/physics/PhysicsEngine');
var Particle = require('famous/physics/bodies/Particle');
var Drag = require('famous/physics/forces/Drag');
var RepulsionForce = require('famous/physics/forces/Repulsion');
var Wall = require('famous/physics/constraints/Wall');
var gridItems = [];
var positionsArray = [-140,-70,0,70,140];
var context = Engine.createContext();
var physics = new PhysicsEngine();
var gridR = new RepulsionForce({
strength: 0.015
});
//physics.addBody(planetParticle);
var leftWall = new Wall({normal : [1,0,0], distance : 140, restitution : 0});
var rightWall = new Wall({normal : [-1,0,0], distance : 140, restitution : 0});
var topWall = new Wall({normal : [0,1,0], distance : 140, restitution : 0});
var bottomWall = new Wall({normal : [0,-1,0], distance : 140, restitution : 0});
function gridItemTrans() {
return this.particle.getTransform();
}
function addGridRepulsion(){
var sq1, sq2;
for (var i = 0; i < gridItems.length; i += 1){
sq1 = gridItems[i].particle;
physics.attach([leftWall, rightWall, topWall, bottomWall], sq1);
if ((i + 1) < gridItems.length){
for (var j = i + 1; j < gridItems.length; j += 1){
sq2 = gridItems[j].particle;
physics.attach(gridR, sq1, sq2);
}
}
}
}
function addBodies(){
gridItems.forEach(function(ele){
physics.addBody(ele.particle);
});
}
for (var rows = 0; rows < 5; rows += 1){
for (var cols = 0; cols < 5; cols += 1){
var gridItem = new Surface({
properties: {
backgroundColor: '#23AD23'
}
});
gridItem.particle = new Particle({
position: [positionsArray[rows], positionsArray[cols], 0]
});
//physics.addBody(gridItem.particle);
//physics.attach(centralG, gridItemParticle[rows][cols], planetParticle);
gridItem.modifier = new Modifier({
size: [50,50],
align: [0.5, 0.5],
origin: [0.5, 0.5],
transform: gridItemTrans.bind(gridItem)
});
context.add(gridItem.modifier).add(gridItem);
gridItems.push(gridItem);
}
}
addBodies();
addGridRepulsion();
});

To clear up your understanding, we will fist look at the repulsion being applied.
By Default, the Repulsion decay function in Famo.us is a gravity function (an inverse squared distance decay function). Gravity has a decay based on mass and distance.
var gridR = new RepulsionForce({
strength: 1,
decayFunction : RepulsionForce.DECAY_FUNCTIONS.GRAVITY
});
You can apply a linear function to your repulsion decay and you will create the affect you are looking for.
var gridR = new RepulsionForce({
strength: 1,
decayFunction : RepulsionForce.DECAY_FUNCTIONS.LINEAR
});
If gravity were to decay linearly rather than quadratically, you would need infinite kinetic energy to escape a gravitational field. It would be like living in 2D space.
To answer the second part of your question: The walls have no restitution, but the particles still respond to the force of the other particles.

Related

Creating an action for moving layers on the x axis to the widest width of a selected layer?

I want to make a variable action that separates a series of layers based on the widest width of one of those selected layers. 4 sprites are 100, 100, 100, 200 pixels wide. It'd separate the layers all by 200 pixels on the x axis and make the image 800 pixels wide total.
I am able to do this easily if all layers are the same but cannot make it work with variability. Not sure if there are any photoshop wizards willing to lend me their time to make a script but it would be extremely helpful!
I'd do something like this. This presumes that there're no groups, no Background layer, layers are normal layers and all document layers are used. Before-after (largest was 150px):
If you don't need them to change Y position, change 0 - layersInfo[i].top to 0 on line 13.
function main()
{
var doc = activeDocument,
layers = doc.layers, //getting all top layers
layersInfo = getInfo().reverse(), //getting layers info and reversing the array because DOM indexes are different order than AM indexes
elWidth = getWidestElement(layersInfo); // getting widest element
doc.resizeCanvas(elWidth * layers.length, doc.height.as("px"), AnchorPosition.TOPLEFT); // resizing canvas size to new width: widest element * number of elements
for (var i = 0; i < layers.length; i++) // for every top layer...
{
doc.activeLayer = layers[i]; // selecting the layer
layers[i].translate(i * elWidth - layersInfo[i].left, 0 - layersInfo[i].top) // moving it to top left corner of each block
}
function getInfo()
{
var layers = 1,
lyrs = [];
while (true)
{
ref = new ActionReference();
ref.putIndex(charIDToTypeID('Lyr '), layers);
try
{
var desc = executeActionGet(ref);
}
catch (err)
{
break;
}
var lyr = {},
bounds = desc.getObjectValue(stringIDToTypeID("bounds"));;
lyr.top = bounds.getDouble(stringIDToTypeID("top"));
lyr.left = bounds.getDouble(stringIDToTypeID("left"));
lyr.width = bounds.getDouble(stringIDToTypeID("width"));
lyrs.push(lyr)
layers++;
}
return lyrs
}; // end of getInfo()
function getWidestElement(layers)
{
var curWidth = 0;
for (var i = 0; i < layers.length; i++)
{
if (layers[i].width > curWidth) curWidth = layers[i].width;
}
return curWidth
}; // end of getWidestElement()
}
var curUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
try
{
app.activeDocument.suspendHistory("temp", "main()");
}
catch (e)
{
alert(e + '\nLine: ' + e.line)
}
app.preferences.rulerUnits = curUnits;

Bouncing ball slightly leaves the screen frame in Spritekit [duplicate]

I've been racking my brain and searching here and all over to try to find out how to generate a random position on screen to spawn a circle. I'm hoping someone here can help me because I'm completely stumped. Basically, I'm trying to create a shape that always spawns in a random spot on screen when the user touches.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenHeight = screenSize.height
let screenWidth = screenSize.width
let currentBall = SKShapeNode(circleOfRadius: 100)
currentBall.position = CGPointMake(CGFloat(arc4random_uniform(UInt32(Float(screenWidth)))), CGFloat(arc4random_uniform(UInt32(Float(screenHeight)))))
self.removeAllChildren()
self.addChild(currentBall)
}
If you all need more of my code, there really isn't any more. But thank you for whatever help you can give! (Just to reiterate, this code kind of works... But a majority of the spawned balls seem to spawn offscreen)
The problem there is that you scene is bigger than your screen bounds
let viewMidX = view!.bounds.midX
let viewMidY = view!.bounds.midY
print(viewMidX)
print(viewMidY)
let sceneHeight = view!.scene!.frame.height
let sceneWidth = view!.scene!.frame.width
print(sceneWidth)
print(sceneHeight)
let currentBall = SKShapeNode(circleOfRadius: 100)
currentBall.fillColor = .green
let x = view!.scene!.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
let y = view!.scene!.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
print(x)
print(y)
currentBall.position = CGPoint(x: x, y: y)
view?.scene?.addChild(currentBall)
self.removeAllChildren()
self.addChild(currentBall)
First: Determine the area that will be valid. It might not be the frame of the superview because perhaps the ball (let's call it ballView) might be cut off. The area will likely be (in pseudocode):
CGSize( Width of the superview - width of ballView , Height of the superview - height of ballView)
Once you have a view of that size, just place it on screen with the origin 0, 0.
Secondly: Now you have a range of valid coordinates. Just use a random function (like the one you are using) to select one of them.
Create a swift file with the following:
extension Int
{
static func random(range: Range<Int>) -> Int
{
var offset = 0
if range.startIndex < 0 // allow negative ranges
{
offset = abs(range.startIndex)
}
let mini = UInt32(range.startIndex + offset)
let maxi = UInt32(range.endIndex + offset)
return Int(mini + arc4random_uniform(maxi - mini)) - offset
}
}
And now you can specify a random number as follows:
Int.random(1...1000) //generate a random number integer somewhere from 1 to 1000.
You can generate the values for the x and y coordinates now using this function.
Given the following random generators:
public extension CGFloat {
public static var random: CGFloat { return CGFloat(arc4random()) / CGFloat(UInt32.max) }
public static func random(between x: CGFloat, and y: CGFloat) -> CGFloat {
let (start, end) = x < y ? (x, y) : (y, x)
return start + CGFloat.random * (end - start)
}
}
public extension CGRect {
public var randomPoint: CGPoint {
var point = CGPoint()
point.x = CGFloat.random(between: origin.x, and: origin.x + width)
point.y = CGFloat.random(between: origin.y, and: origin.y + height)
return point
}
}
You can paste the following into a playground:
import XCPlayground
import SpriteKit
let view = SKView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
XCPShowView("game", view)
let scene = SKScene(size: view.frame.size)
view.presentScene(scene)
let wait = SKAction.waitForDuration(0.5)
let popIn = SKAction.scaleTo(1, duration: 0.25)
let popOut = SKAction.scaleTo(0, duration: 0.25)
let remove = SKAction.removeFromParent()
let popInAndOut = SKAction.sequence([popIn, wait, popOut, remove])
let addBall = SKAction.runBlock { [unowned scene] in
let ballRadius: CGFloat = 25
let ball = SKShapeNode(circleOfRadius: ballRadius)
var popInArea = scene.frame
popInArea.inset(dx: ballRadius, dy: ballRadius)
ball.position = popInArea.randomPoint
ball.xScale = 0
ball.yScale = 0
ball.runAction(popInAndOut)
scene.addChild(ball)
}
scene.runAction(SKAction.repeatActionForever(SKAction.sequence([addBall, wait])))
(Just make sure to also paste in the random generators, too, or to copy them to the playground's Sources, as well as to open the assistant editor so you can see the animation.)

How do I add a radius line from the center of the circle to a point on the circle

The following code adds a circle of given radius to the graphics layer on an ArcGIS map. How can I add a line that joins center of the circle to any point on the circle to the graphics layer.
Basically the question is how do I calculate a point on the circle, draw a line that joins the center to the point on the circle and add it to the graphics layer.
performSearchPoint : function(e) {
var self = this;
var radius = $('#radius-distance').val();
if(radius > 0 && radius < 100000){
$('#besideMouse').removeClass('hide');
$('#besideMouse').show();
var loadingBMint = setInterval(this.loadingBM, 0);
var searchPointClick = OURAPP.App.Map.on("click",function(evt) {
loadingBMint = clearInterval(loadingBMint);
$('#besideMouse').hide();
var radius = $('#radius-distance').val();
var units = $("input:radio[name='unitsGroup']:checked").val();
if (units == "miles"){
units = "9035"; // if we use GeometryService
} else {
units = "9003"; // if we use GeometryService
}
//clear only search graphics
for ( var gr in OURAPP.App.Map.graphics.graphics) {
if(OURAPP.App.Map.graphics.graphics[gr].infoTemplate != null){
var template = OURAPP.App.Map.graphics.graphics[gr].infoTemplate;
if(template != "undefined" || template != null){
if(template.title.trim() == "Search Graphic"){
OURAPP.App.Map.graphics.remove(OURAPP.App.Map.graphics.graphics[gr]);
}
}}}
/*do buffer geometry for draw circle and use the circle geometry to get the features*/
var geoService = new OURAPP.esri.GeometryService("http://XXXX:YYYY/arcgis/rest/services/Utilities/Geometry/GeometryServer");
var params = new OURAPP.esri.BufferParameters();
params.geometries = [ evt.mapPoint ];
params.distances = [ radius ];
params.unit = units;
params.bufferSpatialReference = OURAPP.App.Map.spatialReference;
params.outSpatialReference = new OURAPP.esri.SpatialReference(4326);
var bufferPolygon = new OURAPP.esri.Polygon;
bufferPolygon.spatialReference = new OURAPP.esri.SpatialReference(4326);
geoService.buffer(params,function(geometries) {
var symbol = new OURAPP.esri.SimpleFillSymbol()
.setColor(null).outline.setColor("red");
dojo.forEach(geometries,function(geometry) {
geometry.spatialReference = new OURAPP.esri.SpatialReference(4326);
var graphic = new OURAPP.esri.Graphic(geometry,symbol);
// add name to identify the search graphics
var template = new OURAPP.esri.InfoTemplate(graphic.geometry);
template.setTitle("Search Graphic");
template.setContent("Map Query circle with Radius: " + radius);
graphic.setInfoTemplate(template);
OURAPP.App.Map.graphics.add(graphic);
bufferPolygon = geometry;
OURAPP.App.Map.setExtent(graphic.geometry.getExtent().expand(2));
});
self.searchType="Distance Search from point";
self.nameofplace=radius + " "+$("input:radio[name='unitsGroup']:checked").val();
self.showCount(bufferPolygon);
});
searchPointClick.remove();
});
}
},
I was able to draw a line and add it to the graphics layer using the following. The [-XX.XXXXXXXXXXXX,YY.YYYYYYYYYYY] is a random point on the map, Now only thing left is to find a point on a circle. So now the question becomes how to find a point which is X miles from a known point(Center of the circle) along the same latitude.
var lineSymbol = new OURAPP.esri.CartographicLineSymbol(
OURAPP.esri.CartographicLineSymbol.STYLE_SOLID,
new OURAPP.esri.Color([255,0,0]), 2,
OURAPP.esri.CartographicLineSymbol.CAP_SQUARE,
OURAPP.esri.CartographicLineSymbol.JOIN_MITER, 5
);
var lineGeometry = new OURAPP.esri.Polyline;
lineGeometry.spatialReference = new OURAPP.esri.SpatialReference(4326);
lineGeometry.addPath([[evt.mapPoint.getLongitude(),evt.mapPoint.getLatitude()], [-XX.XXXXXXXXXXXX,YY.YYYYYYYYYYY]])
var lineGraphic = new OURAPP.esri.Graphic(lineGeometry, lineSymbol);
OURAPP.App.Map.graphics.add(lineGraphic);
This is the best possible one i came up with and its working.
var lineSymbol = new OURAPP.esri.CartographicLineSymbol(
OURAPP.esri.CartographicLineSymbol.STYLE_SOLID,
new OURAPP.esri.Color([255,0,0]), 2,
OURAPP.esri.CartographicLineSymbol.CAP_SQUARE,
OURAPP.esri.CartographicLineSymbol.JOIN_MITER, 5
);
var radiusInMeters;
if (selectedUnit == "miles"){
radiusInMeters = radius*1609.34; //have to convert it to meters.
} else {
radiusInMeters = radius*0.3048; //have to convert it to meters.
}
// Calculate the new map point on the circle.
var radians = Math.PI/180;
var ltLong = OURAPP.esri.webMercatorUtils.xyToLngLat(evt.mapPoint.x + radiusInMeters*Math.cos(radians), evt.mapPoint.y + radiusInMeters*Math.sin(radians));
// Calculate the new map point on the circle.
var lineGeometry = new OURAPP.esri.Polyline;
lineGeometry.spatialReference = new OURAPP.esri.SpatialReference(4326);
lineGeometry.addPath([[evt.mapPoint.getLongitude(),evt.mapPoint.getLatitude()], ltLong]);
var lineGraphic = new OURAPP.esri.Graphic(lineGeometry, lineSymbol);

ThreeJS. How to implement ZoomALL and make sure a given box fills the canvas area?

I am looking for a function to ensure that a given box or sphere will be visible in a WebGL canvas, and that it will fit the canvas area.
I am using a perspective camera and the camera already points to the middle of the object.
I understand that this could be achieved by changing the FOV angle or by moving the camera along the view axis.
Any idea how this could be achieved with ThreeJS ?
This is how I finally implemented it:
var camera = new THREE.PerspectiveCamera(35,1,1, 100000);
var controls = new THREE.TrackballControls( me.camera , container);
[...]
/**
* point the current camera to the center
* of the graphical object (zoom factor is not affected)
*
* the camera is moved in its x,z plane so that the orientation
* is not affected either
*/
function pointCameraTo (node) {
var me = this;
// Refocus camera to the center of the new object
var COG = shapeCenterOfGravity(node);
var v = new THREE.Vector3();
v.subVectors(COG,me.controls.target);
camera.position.addVectors(camera.position,v);
// retrieve camera orientation and pass it to trackball
camera.lookAt(COG);
controls.target.set( COG.x,COG.y,COG.z );
};
/**
* Zoom to object
*/
function zoomObject (node) {
var me = this;
var bbox = boundingBox(node);
if (bbox.empty()) {
return;
}
var COG = bbox.center();
pointCameraTo(node);
var sphereSize = bbox.size().length() * 0.5;
var distToCenter = sphereSize/Math.sin( Math.PI / 180.0 * me.camera.fov * 0.5);
// move the camera backward
var target = controls.target;
var vec = new THREE.Vector3();
vec.subVectors( camera.position, target );
vec.setLength( distToCenter );
camera.position.addVectors( vec , target );
camera.updateProjectionMatrix();
render3D();
};
An example of this can be seen in https://github.com/OpenWebCAD/node-occ-geomview/blob/master/client/geom_view.js
Here is how I did it { Using TrackBall to Zoomin/out pan etc }
function init(){
......
......
controls = new THREE.TrackballControls(camera);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [65, 83, 68];
controls.addEventListener('change', render);
.....
.....
render(scene, camera);
animate();
}
function render()
{
renderer.render(scene, camera);
//stats.update();
}
function animate() {
requestAnimationFrame(animate);
controls.update();
}

I need an algorithm that can fit n rectangles of any size in a larger one minimizing its area

I need an algorithm that would take n rectangles of any sizes, and calculate a rectangle big enough to fit them all, minimizing its area so the wasted area is minimum, and also returning the position of all the smaller rectangles within.
The specific task I need this to implement on is in a sprite sheet compiler that would take individual PNG files and make a large PNG with all the images in it, so individual frames can be blitted from this surface at run time.
A nice to have feature would be that it aims to a specific given width/height ratio, but it's not mandatory.
I'd prefer simple, generic code I can port to another language.
This is what I put together for my own needs. The T parameter is whatever object you want associated with the results (think of it like the Tag property). It takes a list of sizes and returns a list of Rects that are arranged
static class LayoutHelper
{
/// <summary>
/// Determines the best fit of a List of Sizes, into the desired rectangle shape
/// </summary>
/// <typeparam name="T">Holder for an associated object (e.g., window, UserControl, etc.)</typeparam>
/// <param name="desiredWidthToHeightRatio">the target rectangle shape</param>
/// <param name="rectsToArrange">List of sizes that have to fit in the rectangle</param>
/// <param name="lossiness">1 = non-lossy (slow). Greater numbers improve speed, but miss some best fits</param>
/// <returns>list of arranged rects</returns>
static public List<Tuple<T, Rect>> BestFitRects<T>(double desiredWidthToHeightRatio,
List<Tuple<Size, T>> rectsToArrange, int lossiness = 10)
{
// helper anonymous function that tests for rectangle intersections or boundary violations
var CheckIfRectsIntersect = new Func<Rect, List<Rect>, double, bool>((one, list, containerHeight) =>
{
if (one.Y + one.Height > containerHeight) return true;
return list.Any(two =>
{
if ((one.Top > two.Bottom) ||
(one.Bottom < two.Top) ||
(one.Left > two.Right) ||
(one.Right < two.Left)) return false; // no intersection
return true; // intersection found
});
});
// helper anonymous function for adding drop points
var AddNewPotentialDropPoints = new Action<SortedDictionary<Point, object>, Rect>(
(potentialDropPoints, newRect) =>
{
// Only two locations make sense for placing a new rectangle, underneath the
// bottom left corner or to the right of a top right corner
potentialDropPoints[new Point(newRect.X + newRect.Width + 1,
newRect.Y)] = null;
potentialDropPoints[new Point(newRect.X,
newRect.Y + newRect.Height + 1)] = null;
});
var sync = new object();
// the outer boundary that limits how high the rectangles can stack vertically
var containingRectHeight = Convert.ToInt32(rectsToArrange.Max(a => a.Item1.Height));
// always try packing using the tallest rectangle first, working down in height
var largestToSmallest = rectsToArrange.OrderByDescending(a => a.Item1.Height).ToList();
// find the maximum possible container height needed
var totalHeight = Convert.ToInt32(rectsToArrange.Sum(a => a.Item1.Height));
List<Tuple<T, Rect>> bestResults = null;
// used to find the best packing arrangement that approximates the target container dimensions ratio
var bestResultsProximityToDesiredRatio = double.MaxValue;
// try all arrangements for all suitable container sizes
Parallel.For(0, ((totalHeight + 1) - containingRectHeight) / lossiness,
//new ParallelOptions() { MaxDegreeOfParallelism = 1},
currentHeight =>
{
var potentialDropPoints = new SortedDictionary<Point, object>(Comparer<Point>.Create((p1, p2) =>
{
// choose the leftmost, then highest point as earlier in the sort order
if (p1.X != p2.X) return p1.X.CompareTo(p2.X);
return p1.Y.CompareTo(p2.Y);
}));
var localResults = new List<Tuple<T, Rect>>();
// iterate through the rectangles from largest to smallest
largestToSmallest.ForEach(currentSize =>
{
// check to see if the next rectangle fits in with the currently arranged rectangles
if (!potentialDropPoints.Any(dropPoint =>
{
var workingPoint = dropPoint.Key;
Rect? lastFittingRect = null;
var lowY = workingPoint.Y;
var highY = workingPoint.Y - 1;
var boundaryFound = false;
// check if it fits in the current arrangement of rects
do
{
// create a positioned rectangle out of the size dimensions
var workingRect = new Rect(workingPoint,
new Point(workingPoint.X + currentSize.Item1.Width,
workingPoint.Y + currentSize.Item1.Height));
// keep moving it up in binary search fashion until it bumps the higher rect
if (!CheckIfRectsIntersect(workingRect, localResults.Select(a => a.Item2).ToList(),
containingRectHeight + (currentHeight * lossiness)))
{
lastFittingRect = workingRect;
if (!boundaryFound)
{
highY = Math.Max(lowY - ((lowY - highY) * 2), 0);
if (highY == 0) boundaryFound = true;
}
else
{
lowY = workingPoint.Y;
}
}
else
{
boundaryFound = true;
highY = workingPoint.Y;
}
workingPoint = new Point(workingPoint.X, lowY - (lowY - highY) / 2);
} while (lowY - highY > 1);
if (lastFittingRect.HasValue) // found the sweet spot for this rect
{
var newRect = lastFittingRect.Value;
potentialDropPoints.Remove(dropPoint.Key);
// successfully found the best location for the new rectangle, so add it to the pending results
localResults.Add(Tuple.Create(currentSize.Item2, newRect));
AddNewPotentialDropPoints(potentialDropPoints, newRect);
return true;
}
return false;
}))
{
// this only occurs on the first square
var newRect = new Rect(0, 0, currentSize.Item1.Width, currentSize.Item1.Height);
localResults.Add(Tuple.Create(currentSize.Item2, newRect));
AddNewPotentialDropPoints(potentialDropPoints, newRect);
}
});
// layout is complete, now see if this layout is the best one found so far
var layoutHeight = localResults.Max(a => a.Item2.Y + a.Item2.Height);
var layoutWidth = localResults.Max(a => a.Item2.X + a.Item2.Width);
var widthMatchingDesiredRatio = desiredWidthToHeightRatio * layoutHeight;
double ratioProximity;
if (layoutWidth < widthMatchingDesiredRatio)
ratioProximity = widthMatchingDesiredRatio / layoutWidth;
else
ratioProximity = layoutWidth / widthMatchingDesiredRatio;
lock (sync)
{
if (ratioProximity < bestResultsProximityToDesiredRatio)
{
// this layout is the best approximation of the desired container dimensions, so far
bestResults = localResults;
bestResultsProximityToDesiredRatio = ratioProximity;
}
}
});
return bestResults ?? new List<Tuple<T, Rect>>() {Tuple.Create(rectsToArrange[0].Item2,
new Rect(new Point(0, 0), new Point(rectsToArrange[0].Item1.Width, rectsToArrange[0].Item1.Height))) };
}
}