I'm working on a project on 8th Wall using A-Frame and Three.js.
I'm using a component called "text-geometry" which generates extruded text nicely. But I want the text to be centered, not "flush-left", so I need to move it left by half its rendered width. I'm doing this using a bounding box:
bbox = new THREE.Box3().setFromObject(mesh)
el.object3D.position.x = (bbox.min.x - bbox.max.x) / 2
The problem is that if the text is nested within several levels of a-entities each with its own scale, the centering is incorrect.
So I am crawling up through all nested entities to find the "true scale":
let [trueScale, ee] = [1, el]
while (ee.parentElement) {
const scale = ee.getAttribute('scale')
if (scale) { trueScale *= scale.x }
ee = ee.parentElement
}
This seems inefficient – is there a more elegant way to do this so that my bbox returns the correct width accounting for the 'effective' scale of the text entity?
Related
Quick version: I’m having trouble displaying a d3 element on a static image displayed by Leaflet. It works if I use d3noob’s (http://bl.ocks.org/d3noob/9211665) example…but I’m having trouble extending it. I’m guessing it’s because of the lang and long translation or the projection but I’m not sure what I need to change. I’m trying to display the geoJSON in the centre of the image (my code on JSFiddle: https://jsfiddle.net/g_at_work/qyhf0qz0/12/)
Longer version:
I'm trying to extend d3noob’s example so that I can display geoJSON on static images using Leaflet and D3 (an requirement from my boss unfortunately). I was able to reproduce d3noob’s tile based example but I've had trouble with the static version.
I've been able to display the static image but I've not been able to display the blue rectangle on the image. I'm not sure what I need to do to manipulate the position of the blue rectangle described in the JSON.
I've tried playing around with setting the coordinates in the projectPoint function but no luck:
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
}
At this stage I’m thinking I need to describe a new projection but I’m not sure what.
Here is the code which I’m using to display the static image:
var map = new L.Map("map", {maxZoom:1,center: [0,0], zoom: 3,crs:L.CRS.Simple});
var southWest = map.unproject([0,882],map.getMaxZoom());
var northEast = map.unproject([1085,0],map.getMaxZoom());
var imageBounds = new L.LatLngBounds(southWest,northEast);
L.imageOverlay('http://www.w3schools.com/css/trolltunga.jpg',imageBounds).addTo(map);
map.fitBounds(imageBounds);
It would be great if someone could suggest a strategy for maniuplating the position of the rectangle (I’m new to d3 and leaflet)
Thanks a bunch
G
so after some tinkering I found that creating a new projection enabled me to set the location and scale the map so I could place it where I needed it to be :
var center = d3.geo.centroid(geoShapeInstance);
var scale = [800000];
var offset = [175, 1];
var projection = d3.geo.mercator().scale(scale).center(center).translate(offset);
var path = d3.geo.path().projection(projection);
Fiddling with the 'offset' variable allowed me to set the location and playing with the 'scale' variable allowed me to resize the map (scale is a [x,x] value)
Hope this helps someone.
I have a sphere model along with html text labels overlayed onto it at fixed positions. How can I hide them when they are behind the object when i am rotating my sphere using orbit controls?
Anyone knows about any references on stackoverflow or any examples which can solve my issue?
See this example how you could do it: http://jsfiddle.net/L0rdzbej/194/
I have put the relevant code in the updateLabelVisibility()-function, it looks like this:
var meshPosition = mesh.getWorldPosition();
var eye = camera.position.clone().sub( meshPosition );
var dot = eye.clone().normalize().dot( meshPosition.normalize() );
//IS TRUE WHEN THE MESH IS OCCLUDED BY THE SPHERE = dot value below 0.0
var ocluded = dot < -0.2;
if ( ocluded ) {
// HIDE LABEL IF CAMERA CANT SEE IT (I.E. WHEN IT IS BEHIND THE GLOBE)
label.style.visibility = "hidden";
}
else {
// DISPLAY LABEL IF MESH IS VISIBLE TO THE CAMERA
label.style.visibility = "visible";
}
Where mesh is the marker where your label is attached to. The dot is basically telling you how far around the sphere it is, within a certain distance of 90 degree. Simply said by values below zero the label is longer visible by going further around the back.
To be fair I based this off some code from: https://zeitgeist-globe.appspot.com/
I have a scene filled with ~hundred oblong asteroid shaped objects. I want to place a text label under each one so that the label is visible from any camera angle. I billboard the text so that it always faces the camera.
At first, everything looks great by placing text below the 3d object using .translateY. However, as you start to moving around the scene, labels no longer are 'below' the object depending on your camera position. This especially true when you orient using trackballControls.
How can I place text 'below' the object no matter the orientation. Perhaps if I create a 2d bounding box around each object in relation to the camera on each frame - I could then place the text label right below that 2d box.
I'm also concerned that calculating 2d bounding boxes on a hundred 3d objects every frame could get expensive. Thoughts?
screenshots:
At first, text labels appear correctly translated -y below the object
but as you rotate the camera, labels get sideways
flipping the camera all the way around shows the labels upside down
My goal is to have the labels below the objects no matter the camera orientation.
Did you tried to add the Text Labels to the Object?
object.add(Label) instead of scene.add(Label)
I have a demo site here that might help give you source to look at:
https://dl.dropboxusercontent.com/u/31495717/cubemaker/index.html
This site places textual DOM elements at a screen coordinate-based constant distance from a 3D object, styled with CSS, within the render loop, when the mouse pointer is moved over the 3D object.
From the source:
var id_label = document.createElement('div');
id_label.id = INTERSECTED.name;
id_label.style.position = 'absolute';
id_label.style.top = '-10000px';
id_label.style.left = '-10000px';
id_label.innerHTML = '<span class="particle_label">' + INTERSECTED.name + '<br><span class="particle_sublabel">' + INTERSECTED.subname + '</span></span>';
container.appendChild(id_label);
var id_label_rect = id_label.getBoundingClientRect();
id_label.style.top = (screen_object_center.y - 0.85 * (id_label_rect.height / 2)) + 'px';
if (mouse.x < 0)
id_label.style.left = (screen_object_center.x - horizontal_fudge * (screen_object_edge.x - screen_object_center.x)) + 'px';
else {
id_label.style.left = (screen_object_center.x + horizontal_fudge * (screen_object_edge.x - screen_object_center.x) - id_label_rect.width) + 'px';
id_label.style.textAlign = 'right';
}
The DOM element is drawn offscreen and then repositioned based on attributes of its bounding box and the world coordinates of the 3D element it is associated with. When the mouse pointer is moved outside the 3D element bounds, the text label is removed from the DOM.
Since you are always showing your labels, you might modify this to draw the element once in an initialization step, and simply change the top and left style attributes within the render loop.
Is there a way in d3 to not draw overlapping tick labels? For example, if I have a bar chart, but the bars are only 5 pixels wide and the labels are 10 pixels wide, I end up with a cluttered mess. I'm currently working on an implementation to only draw the labels when they do not overlap. I can't find any existing way to do that, but wasn't sure if anyone else had dealt with this problem.
There is no way of doing this automatically in D3. You can set the number of ticks or the tick values explicitly (see the documentation), but you'll have to figure out the respective numbers/values yourself. Another option would be to rotate the labels such that there is less chance of them overlapping.
Alternatively, like suggested in the other answer, you could try using a force layout to place the labels. To clarify, you would use the force layout on the labels only -- this is completely independent of the type of chart. I have done this in this example, which is slightly more relevant than the one linked in the other answer.
Note that if you go with the force layout solution, you don't have to animate the position of the labels. You could simply compute the force layout until it converges and then plot the labels.
I've had a similar problem with multiple (sub-)axis, where the last tick overlaps my vertical axis in some situations (depending on the screen width), so I've just wrote a little function that compares the position of the end of the text label with the position of the next axis. This code is very specific to my use case, but could adapted easily to your needs:
var $svg = $('#svg');
// get the last tick of each of my sub-axis
$('.tick-axis').find('.tick:last-of-type').each(function() {
// get position of the end of this text field
var endOfTextField = $(this).offset().left + $(this).find('text').width();
// get the next vertical axis
var $nextAxis = $('line[data-axis="' + $(this).closest('.tick-axis').attr('data-axis') + '"]');
// there is no axis on the very right, so just use the svg width
var positionOfAxis = ($nextAxis.length > 0) ? $nextAxis.offset().left : $svg.offset().left + $svg.width();
// hide the ugly ones!
if (endOfTextField > positionOfAxis) {
$(this).attr('class', 'tick hide');
}
});
The ticks with color: aqua are the hidden ones:
I am using a kineticjs regular polygon (a hexagon in this case) and I am filling it with an image using setFillPatternImage. This is working. I'm creating a dynamic implementation so I need to scale the source image depending on the current size of the polygon. This involves calculating the setFillPatternOffset and the setFillPatternScale since the dimensions of a regular polygon are relative to the center. There is no clear documentation that I can find regarding the reference point for the fill image, nor whether the scaling factor should use the radius as a proxy for the width and height ratios or not. The following code results in a misplaced image on the polygon. Anyone know what the alignment rules are for fillPatternImage?
imageObj.onload = function() {
var whex = hexagon.getRadius() * 2;
var xratio = whex / imageObj.width;
var yratio = whex / imageObj.height;
hexagon.setFillPatternImage(imageObj);
hexagon.setFillPatternOffset(-whex/2,-whex/2);
hexagon.setFillPatternScale( [ xratio, yratio ] );
};
Thanks!
Looks like I was over-thinking this. Rather than using the width of the destination polygon when setting the offset, kineticjs handles the scaling of that offset for you. As a result you simply set the offset with:
hexagon.setFillPatternOffset(-imageObj.width/2, -imageObj.height/2);