I've seen lots of questions on how to zoom the camera so an object fills the screen, but I'm trying to move the object to fill the screen.
I've been trying something like this using the original photos pixel size, and these objects have been scaled:
var dist = object.originalSize.height > $(window).height()
|| object.originalSize.width > $(window).width()
? ( $(window).height() / object.originalSize.height ) * 100
: 10;
var pLocal = new THREE.Vector3( 0, 0, -dist);
var target = pLocal.applyMatrix4( camera.matrixWorld );
var tweenMove = new TWEEN.Tween(object.position).to(target, 1500).easing(TWEEN.Easing.Cubic.InOut);
To come up with a vector to move the object to, however, I can't get the object to fill the screen. Any idea of the maths I need to calculate how far an object needs to be to fill the screen?
The object is a Object3D with different children depending on it's type.
I know the original photographs dimensions (object.originalSize.height) and I know the geometry that has been scaled up to fit with power of 2.
Any clue gratefully received on how to calculate the distance required from the camera to ensure the object fits inside the bounds of the screen.
I also know the bounding box of the item, i.e. from 1024 to 128.
This works, not sure why..
var vFOV = camera.fov * Math.PI / 180;
var ratio = 2 * Math.tan( vFOV / 2 );
var screen = ratio * (window.innerWidth / window.innerHeight) ;
var size = getCompoundBoundingBox( object ).max.y;
var dist = (size/screen) / 4;
Related
Matter-js - How to get width and height of rectangle ?
I need to know is there distance return method implemented in Matter-js .
// part.vertices[0] and part.vertices[1]
I wanna integrate tiles option.
This is how looks critical part (i use override function for Render.bodies it is most interest for me) .:
for (let x = 0; x < this.tiles; x++) {
c.drawImage(
texture,
texture.width * -part.render.sprite.xOffset * part.render.sprite.xScale,
texture.height * -part.render.sprite.yOffset * part.render.sprite.yScale,
texture.width * part.render.sprite.xScale,
texture.height * part.render.sprite.yScale);
}
const { min, max } = part.bounds
it will contain what you need in { x, y }
just substract max.x - min.x & max.y - min.y
I went with a solution very similar to the following:
var width = 30;
var height = 30;
var rect = Bodies.rectangle(150, 100, width, height, {density:0.01, className:"brick", width:width, height:height});
console.log(rect.className, rect.width); // "brick", 30
I decided to carry the original width/height information along with other custom properties such as className
The reason why is because bounds is affected by the rotation of any non perfectly-circular object eg. a rotated rectangle's bounds could be up to ~30% wider than it's actual width.
There are two solutions that I've found.
1- Create a class to wrap the matter.js body, which will also hold onto the height and width. ie:
class rectWrapper {
constructor(x, y, width, height, options){
this.width = width
this.height = height
this.body = Matter.Bodies.rectangle(x, y, width, height, options)
}
}
2- Another way is to use the magic of math to determine the distance between two coordinate points, using Body.vertices[0] and Body.vertices[1] for the width, and Body.vertices[0] and Body.vertices[3] for height. This would also account for any rotation. This link explains it clearly, for 2 and 3 dimensions:
https://sciencing.com/calculate-distance-between-two-coordinates-6390158.html
I would recommend writing your own "utility function" to do this. A heavy handed example might look like this:
function distance(x1, y1, x2, y2){
var x = Math.abs(x1-x2)
var y = Math.abs(y1-y2)
return Math.sqrt((x*x)+(y*y))
}
So a call might look like:
var rect = Matter.Bodies.rectangle(0,0,10,50)
var width = distance(rect.vertices[0].x, rect.verticies[0].y, rect.vertices[1].x, rect.vertices[1].y)
var height = distance(rect.vertices[0].x, rect.vertices[0].y, rect.vertices[3].x, rect.vertices[3].y)
Alternatively, if you happen to be using p5.js as your renderer, you can use p5.dist() which takes x1, y1, x2, y2 as arguments and returns the distance (basically the same as the function above):
https://p5js.org/reference/#/p5/dist
Note, this will only work for rectangles. If you're using different kinds of geometry, I would probably just make a wrapper class myself.
I was wondering if there was a way to obtain the bounding box for the models that are inserted via 3dio.js, or otherwise calculate their center points? I'm looking to center them on the origin.
The images below show two models relative to the scene origin indicated by the red box.
You can access the three.js object of the 3d.io entity like this:
var threeElem = document.getElementById("custom-id").components['io3d-data3d'].data3dView.threeParent
Then you can use the native bounding box from three.js:
var bbox = new THREE.Box3().setFromObject(threeElem)
Like that you get the min/max bounds which you can use to determine the origin.
I hope that answers your question. Let me know!
Edit:
for furniture it would probably be
var threeElem = document.getElementById("custom-id").components['io3d-furniture'].data3dView.threeParent
Based on Madlaina's answer. I needed to ensure the model was loaded before
addModelToScene(type) {
let scene = document.querySelector('a-scene');
let model = document.createElement('a-entity');
model.setAttribute('io3d-data3d', getModelKey(type) )
model.addEventListener('model-loaded', () => {
// Access the three.js object of the 3d.io
let threeElem = model.components['io3d-data3d'].data3dView.threeParent
// create the bounding box
let bbox = new THREE.Box3().setFromObject(threeElem)
// Calculate the center-point offsets from the max and min points
const offsetX = (bbox.max.x + bbox.min.x)/2
const offsetY = (bbox.max.y + bbox.min.y)/2
const offsetZ = (bbox.max.z + bbox.min.z)/2
// apply the offset
model.setAttribute('position', {x:-offsetX,y:-offsetY, z:-offsetZ})
} );
scene.appendChild(model);
}
The result:
Im am generating a ppt-file using this tutorial here
Step 4 describes how to swap out the image placeholder.
My images has different dimensions, which makes some images look a little bit too funny.
Is there any way to resize the placeholder so it can keep the dimensions?
Edit: Ok, a better explanation: users can upload images of them selves. The images are stored on the server. I am generating a ppt-file with one user per slide. And for every slide there will be an image, if any. I can of course get the dimensions of every image, but how do I replace the placeholder with an image of another dimension than the placeholder?
Well, I can't tell you based on that tutorial, but I can tell you where it is done in Open XML (i.e. not the SDK).
Your picture will have an xfrm element with a set of values, like this:
<p:spPr>
<a:xfrm>
<a:off x="7048050" y="6248401"/>
<a:ext cx="972000" cy="288000"/>
</a:xfrm>
</p:spPr>
The values you want to change are the cx and cy of a:ext. Take your new picture's dimensions (h and w) from like a System.Drawing.Image object and take each of the values and multiply by 12700. So if the width of the picture is 400 pixels, the cx value of a:ext will be (400 x 12700 = 5080000).
This is how I did it:
using DocumentFormat.OpenXml.Packaging;
lets assume you have your SlidePart
In my case I wanted to check for the alt title of the pictures and replace them if it matches to my key.
//find all image alt title (description) in the slide
List<DocumentFormat.OpenXml.Presentation.Picture> slidePictures = slidePart.Slide.Descendants<DocumentFormat.OpenXml.Presentation.Picture>()
.Where(a => a.NonVisualPictureProperties.NonVisualDrawingProperties.Description.HasValue).Distinct().ToList();
now we check all the images:
//check all images in the slide and replace them if it matches our parameter
foreach (DocumentFormat.OpenXml.Presentation.Picture imagePlaceHolder in slidePictures)
now in the loop we look for Transform2D and modify it with our value:
Transform2D transform = imagePlaceHolder.Descendants<Transform2D>().First();
Tuple<Int64Value, Int64Value> aspectRatio = CorrectAspectRatio(param.Image.FullName, transform.Extents.Cx, transform.Extents.Cy);
transform.Extents.Cx = aspectRatio.Item1;
transform.Extents.Cy = aspectRatio.Item2;
And this function looks like this:
public static Tuple<Int64Value, Int64Value> CorrectAspectRatio(string fileName, Int64Value cx, Int64Value cy)
{
BitmapImage img = new();
using (FileStream fs = new(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
img.BeginInit();
img.StreamSource = fs;
img.EndInit();
}
int widthPx = img.PixelWidth;
int heightPx = img.PixelHeight;
const int EMUsPerInch = 914400;
Int64Value x = (Int64Value)(widthPx * EMUsPerInch / img.DpiX);
Int64Value y = (Int64Value)(heightPx * EMUsPerInch / img.DpiY);
if (x > cx)
{
decimal ratio = cx * 1.0m / x;
x = cx;
y = (Int64Value)(cy * ratio);
}
if (y > cy)
{
decimal ratio = cy * 1.0m / y;
y = cy;
x = (Int64Value)(cx * ratio);
}
return new Tuple<Int64Value, Int64Value>(x, y);
}
Important thing to note is that EMU per inch is 914400. In most cases you just need to divide it by 96, but for some monitors it is different. Therefore it is best to divide it the DPI for x and y.
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);
I have created a Sprite in Actionscript and rendered it to a Flex Canvas. Suppose:
var fooShape:Sprite = new FooSpriteSubclass();
fooCanvas.rawChildren.addChild(myshape);
//Sprite shape renders on screen
fooShape.rotation = fooNumber;
This will rotate my shape, but seems to rotate it around the upper-left
point of its parent container(the canvas).
How can I force the Sprite to rotate about is own center point? I could obviously
write code to calculate the rotation, and then have it re-render, but I think there
must be a built-in way to do this, and certainly do not want to 'reinvent the wheel'
if possible.
I am using FlexBuilder, and therefore do not have access to the full Flash API.
Thank you much!
The following steps are required to rotate objects based on a reference point (using Matrix object and getBounds):
Matrix translation (moving to the reference point)
Matrix rotation
Matrix translation (back to original position)
For example to rotate an object 90 degrees around its center:
// Get the matrix of the object
var matrix:Matrix = myObject.transform.matrix;
// Get the rect of the object (to know the dimension)
var rect:Rectangle = myObject.getBounds(parentOfMyObject);
// Translating the desired reference point (in this case, center)
matrix.translate(- (rect.left + (rect.width/2)), - (rect.top + (rect.height/2)));
// Rotation (note: the parameter is in radian)
matrix.rotate((90/180)*Math.PI);
// Translating the object back to the original position.
matrix.translate(rect.left + (rect.width/2), rect.top + (rect.height/2));
Key methods used:
Matrix.rotate
Matrix.translate
DisplayObject.getBounds
Didn't have much luck with the other examples. This one worked for me. I used it on a UIComponent.
http://www.selikoff.net/2010/03/17/solution-to-flex-image-rotation-and-flipping-around-center/
private static function rotateImage(image:Image, degrees:Number):void {
// Calculate rotation and offsets
var radians:Number = degrees * (Math.PI / 180.0);
var offsetWidth:Number = image.contentWidth/2.0;
var offsetHeight:Number = image.contentHeight/2.0;
// Perform rotation
var matrix:Matrix = new Matrix();
matrix.translate(-offsetWidth, -offsetHeight);
matrix.rotate(radians);
matrix.translate(+offsetWidth, +offsetHeight);
matrix.concat(image.transform.matrix);
image.transform.matrix = matrix;
}
Actually I had to add this code to make above solutions work for me.
private var _rotateCount = 0;
var _origginalMatrix:Matrix=new Matrix();
.........
if (_rotateCount++ >= 360 / angleDegrees)
{
myObject.transform.matrix = _origginalMatrix;
_rotateCount = 0;
return;
}
var matrix:Matrix = myObject.transform.matrix;
....
Without that after some long time rotated object slowly moves somewhere right top.
An alternative solution is to put your object inside another View, move it so that your image's center is at the container's top-left corner, and then rotate the container.
import spark.components.*;
var myContainer:View = new View();
var myImage:Image = new Image();
myContainer.addElement(myImage);
myImage.x = myImage.width / -2;
myImage.y = myImage.height / -2;
addElement(myContainer);
myContainer.rotation = whateverAngle;
One issue might be that the width of the image isn't know at the moment it is created, so you might want to find a way around that. (Hardcode it, or see if myImage.preliminaryWidth works)
/**
* Rotates the object based on its center
* Parameters: #obj => the object to rotate
* # rotation => angle to rotate
* */
public function RotateAroundCenter(obj:Object, rotation:Number):void
{
var bound:Rectangle = new Rectangle();
// get the bounded rectangle of objects
bound = obj.getRect(this);
// calculate mid poits
var midx1:Number = bound.x + bound.width/2;
var midy1:Number = bound.y + bound.height/2;
// assign the rotation
obj.rotation = rotation;
// assign the previous mid point as (x,y)
obj.x = midx1;
obj.y = midy1;
// get the new bounded rectangle of objects
bound = obj.getRect(this);
// calculate new mid points
var midx2:Number = bound.x + bound.width/2;
var midy2:Number = bound.y + bound.height/2;
// calculate differnece between the current mid and (x,y) and subtract
//it to position the object in the previous bound.
var diff:Number = midx2 - obj.x;
obj.x -= diff;
diff = midy2 - obj.y;
obj.y -= diff;
}
//////////////////
Usage:
you can use the above function as described below,
var img:Canvas = new Canvas()
RotateAroundCenter(img, rotation);
This will help you
REf: http://subashflash.blogspot.in/2010/08/rotation-of-object-based-on-center.html
If you want to rotate around the center, merely center the asset inside your sprite by setting the internal assets x and y to half of the width and height of the asset. This swill center your content and allow it to rotate around a center point.
An example of runtime loaded assets is as follows:
var loader:Loader = new Loader():
var request:URLRequest = new URLRequest(path/to/asset.ext);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoaderComplete);
loader.load(request);
private function _onLoaderComplete(e:Event):void
{
var mc:MovieClip = e.target.content as MovieClip;
mc.x = -mc.width * 0.5;
mc.y = -mc.height * 0.5;
mc.rotation = 90;
addChild(mc);
}