Make imported model fit screen in threejs - three.js

I do not want to change the camera to fit the model. I would prefer to know the correct dimensions given a location to make sure the model/geometry once scaled will be in the screen.
I don't want to move the camera since I might have many different objects.
Thoughts?

You probably just want to scale the model, with scale="0.1 0.1 0.1" or whatever. To do that automatically, consider writing a new component:
AFRAME.registerComponent('autoscale', {
init: function () {
var el = this.el;
el.addEventListener('model-loaded', function (e) {
var bbox = new THREE.Box3().setFromObject(e.detail.model);
var scale = 1 / (bbox.max.x - bbox.min.x);
el.setAttribute('scale', {
x: scale,
y: scale,
z: scale,
});
});
}
});
To be more robust, you should probably use the largest dimension (x vs y vs z) for scaling, but this is the gist of it.

Related

Rotate an object like OrbitControls but only the object itself

We love three.js! And here is a page we built using it a few years ago.
https://www.jgprolock.com
We are in the process of revising the animations on this site.
Once the page loads, the user has the ability to drag and rotate the object. But it is really a trick. We are using orbit controls to rotate the camera around our scene, and thus our main object which is centered in the scene (positions x,y,z all equal to 0). If we did not place the object in the center, it starts to look uneven in its rotation as the camera now is rotating around a center that the object doesn't have.
In order to make it look like the object is on the left side, we ended up moving the canvas to the left and then we bring it back to the right or left as the animation continues after scrolling.
So, my question is .. does anyone have an example how to achieve this functionality just by rotating the actual object itself, instead of rotating the camera around the entire scene using the orbit controls plugin?
Or is there away to modify the orbit controls to rotate around an object and not the entire scene?
I've been searching for this for a while but right after asking this question I came across this link, which actually has an example of what we are trying to do.
https://jsfiddle.net/n6u6asza/1205/
The key to making this work as copied from the link: (although I am not 100% sure what this all means)
/* */
var isDragging = false;
var previousMousePosition = {
x: 0,
y: 0
};
$(renderer.domElement).on('mousedown', function(e) {
isDragging = true;
})
.on('mousemove', function(e) {
//console.log(e);
var deltaMove = {
x: e.offsetX-previousMousePosition.x,
y: e.offsetY-previousMousePosition.y
};
if(isDragging) {
var deltaRotationQuaternion = new three.Quaternion()
.setFromEuler(new three.Euler(
toRadians(deltaMove.y * 1),
toRadians(deltaMove.x * 1),
0,
'XYZ'
));
cube.quaternion.multiplyQuaternions(deltaRotationQuaternion, cube.quaternion);
}
previousMousePosition = {
x: e.offsetX,
y: e.offsetY
};
});
/* */
If you want an article on how to achieve this without the use of unnecessary jquery dependencies you can have a look here
This uses the eventListener to find a mousemove event whilst a mousedown event is occurring, and then passes the coordinates to a custom function.
var mouseDown = false,
mouseX = 0,
mouseY = 0;
var canvas = renderer.domElement
canvas.addEventListener('mousemove', function (evt) {
if (!mouseDown) {return}
//console.log('drag')
evt.preventDefault();
var deltaX = evt.clientX - mouseX,
deltaY = evt.clientY - mouseY;
mouseX = evt.clientX;
mouseY = evt.clientY;
// DO SOMETHING HERE WITH X and Y
object.rotation.x += deltaX
}, false);
canvas.addEventListener('mousedown', function (evt) {
evt.preventDefault();
mouseDown = true;
mouseX = evt.clientX;
mouseY = evt.clientY;
}, false);
canvas.addEventListener('mouseup', function (evt) {
evt.preventDefault();
mouseDown = false;
}, false);
}
But not that this will not work if you have OrbitControls or DragControls imported!

Aframe how to reset default scale after loading the GLTF model

I am trying to make an app which adds models from Google poly in GLTF format.
I had the issue of some models being extremely large when added on the scene which I solved by computing their size with bounding box max and min values and setting the scale amount.
Now after adding the objects to the scene when I open the inspector and drag to scale objects, even with a small amount of drag the objects becomes very large.
If there is any way to reset the scale value of loaded objects so that default value can be the value which I computed and this can also solve the drag to scale issue.
Note: The computed scale factor for some elements goes to 0.00001 for x, y, z.
Use the three.js API within A-Frame components to compute a bounding box of the model, then scale it down to the size you prefer. Example:
AFRAME.registerComponent('autoscale', {
schema: {type: 'number', default: 1},
init: function () {
this.scale();
this.el.addEventListener('object3dset', () => this.scale());
},
scale: function () {
const el = this.el;
const span = this.data;
const mesh = el.getObject3D('mesh');
if (!mesh) return;
// Compute bounds.
const bbox = new THREE.Box3().setFromObject(mesh);
// Normalize scale.
const scale = span / bbox.getSize().length();
mesh.scale.set(scale, scale, scale);
// Recenter.
const offset = bbox.getCenter().multiplyScalar(scale);
mesh.position.sub(offset);
}
});
HTML:
<a-entity autoscale="2" gltf-model="stereo.gltf"></a-entity>
The code above will fit your model to a ~2m box, and re-center it. For more information see THREE.Object3D documentation.
three.js r89, A-Frame 0.8.0.

Animate point with paper.js

I’d like to rebuild this animation http://imgur.com/l5Vhswe in paper.js.
I already tried SVG animations (http://codepen.io/magglomag/pen/jrVwzy) but despite from the fact that they’ll be deprecated soon I was not able to move the two points asynchronously.
What I have so far is the shape and I know that I can animate with the onFrame event handler. But I have no clue how to say that the point should animate between the coordinates [43,168.7] and [43,35.3].
http://codepen.io/magglomag/pen/yaVXrr
var firstSegment = new Segment({
point: [109,3.7]
});
var secondSegment = new Segment({
point: [43,168.7]
});
var thirdSegment = new Segment({
point: [109,202.2]
});
var path = new Path({
segments: [firstSegment, secondSegment, thirdSegment],
fillColor: '#2dfd9a',
closed: true
});
secondSegment.onFrame = function(event) {
this.point = [43,35.3]
}
The error you are making is that you are trying to bind an handler to segment.onFrame event.
But only item.onFrame and view.onFrame are available.
In PaperScript context, you can even use a global onframe named function as a convenient way to animate things.
Here is a simple example demonstrating how a path segment can be animated.
// create a triangle
var triangle = new Path.RegularPolygon({
center: view.center,
sides: 3,
radius: 50,
fillColor: 'orange'
});
// store initial first point position
var initialPoint = triangle.firstSegment.point.clone();
// on frame
function onFrame(event) {
// use sine function as a convenient way to demonstrate animation
var newPoint = initialPoint + Math.sin(event.count * 0.05) * 30;
// update first point
triangle.firstSegment.point = newPoint;
}

EaselJS: Changing a Shape Objects Dimensions

Total noob question. I've been abusing Google and for some reason, have surprisingly not been able to find anything regarding this...? I feel like I'm missing something here. :P
I currently have a resize() function that modifies the canvas dimensions to the size of the window. In a minimal example (which also uses jQuery), I have a variable that references my Shape object. According to the documents, the Shape Object does not include a width & height property. What is the most efficient way of resizing a Shape Object? Removing/redrawing dynamically?
This is what I have:
var stage;
var bgColor;
$(document).ready(function(){
init();
});
function init()
{
stage = new createjs.Stage("canvasStage");
bgColor = new createjs.Shape();
bgColor.graphics.beginFill("#000000").drawRect(0,0, stage.canvas.width, stage.canvas.width);
stage.addChild(bgColor);
$(window).resize(function(){windowResize();});
windowResize();
}
function windowResize()
{
stage.canvas.width = $(window).width();
stage.canvas.height = $(window).height();
//bgColor.width = $(window).height();// No width property
//bgColor.height = $(window).height();// NO height property
stage.update();}
You can use the shape's scaleX and scaleY to scale the shape.
Note: The Shape Object extends the DisplayObject so you might also want to look at the DisplayObject docs for many more useful properties/methods.
myShape.scaleX=1.2;
myShape.scaleY=1.2;
as mentioned by #markE, only scaleX and scaleY are available.
A way to work around the problem is to instantiate shapes with 1px width and 1px height:
mc_bg = new createjs.Shape();
mc_bg.graphics.beginFill("#CCCCCC").drawRect(0,0,1,1);
stage.addChild(mc_bg);
mc_left = new createjs.Shape();
mc_left.graphics.beginFill("#333333").drawRect(0,0,1,1);
stage.addChild(mc_left);
mc_circle = new createjs.Shape();
mc_circle.graphics.beginFill("#888888").drawCircle(0,0,1,1);
stage.addChild(mc_circle);
Allowing to set your dimensions with pixel units without conversion directly by using scaleX and scaleY before rendering:
mc_bg.scaleX = stage_width;
mc_bg.scaleY = stage_height;
mc_left.scaleX = stage_width/2;
mc_left.scaleY = stage_height;
mc_circle.x = stage_width/4;
mc_circle.y = stage_width/4;
mc_circle.scaleX = stage_width/4;
mc_circle.scaleY = stage_width/4;
stage.update();
See fiddle example with live resizing on window resize: https://jsfiddle.net/jckleinbourg/go3fs1zt/
Conversion methods to change scale --> dimensions and vice versa:
function dimToScale(origDim, desiredDim)
{
return desiredDim / origDim;
}
function scaleToDim(origDim, scale)
{
return scale * origDim;
}

HTML 5 Canvas Mouse over event on element (show tooltip)

I am working on a visualization project. Based on my data I am plotting hundreds of small circle on canvas. I want to add a mouse over event so that whenever a mouse is the enclosing area of a circle it will show some node property from my data as a tool tip or as text on the canvas.
My current drawCircle method
function drawCircle(canvas,x,y,r)
{
canvas.strokeStyle = "#000000";
canvas.fillStyle = "#FFFF00";
canvas.lineWidth = 2;
canvas.beginPath();
canvas.arc(x,y,r,0,Math.PI*2,true);
canvas.stroke();
canvas.fill();
canvas.closePath();
}
I have looked into kinetic.js
But can't figure it out how I can call my drawCircle [repetitively] method using their library.
Any help will be highly appreciated.
If you still want to use KineticJS, you would put the Kinetic shape stuff inside your drawCircle routine. This is basically pulled out of their tutorial and stripped down:
function drawCircle(stage,x,y,r) {
var circle = new Kinetic.Shape(function(){
var context = this.getContext();
// draw the circle here: strokeStyle, beginPath, arc, etc...
});
circle.addEventListener("mouseover", function(){
// do something
});
stage.add(circle);
}
If you don't want to use KineticJS after all, you will need to remember for yourself the positions and radii of every circle you drew, and then do something like this:
canvas.onmouseover = function onMouseover(e) {
var mx = e.clientX - canvas.clientLeft;
var my = e.clientY - canvas.clientTop;
// for each circle...
if ((mx-cx)*(mx-cx)+(my-cy)*(my-cy) < cr*cr)
// the mouse is over that circle
}

Resources