I'm trying to get a clone of a raycasted object, and move the clone of the selected object in a <div>. The raycaster works just fine, but the CSS3DObject instance (the cloned 3d Object) is not shown in the scene. I have made another renderer (namely a CSS3DRenderer), in addition to the current WebGLRenderer, and basically my css3d code consists of this:
cssRenderer = new CSS3DRenderer();
cssRenderer.setSize(window.innerWidth, window.innerHeight);
cssRenderer.domElement.style.position = 'absolute';
cssRenderer.domElement.style.top = 0;
cssScene = new THREE.Scene();
document.body.appendChild(cssRenderer.domElement);
...
thumbnail = document.querySelector('.thumbnail');
thumbObject = new CSS3DObject(thumbnail);
....
targetClone = target.clone();
thumbObject.position.copy(targetClone.position);
cssScene.add(thumbObject);
and my render loop code, pertaining to this, is:
requestAnimationFrame(update);
renderer.render(scene, camera); // WebGLRenderer
cssRenderer.render(cssScene, camera); //CSS3DRenderer
My intention is to embed the cloned object (targetClone) in the CSS3DObject (thumbObject, which in actuality is an HTML element). The console shows no error, and I cannot see any reaction to this code whatsoever (except that the div travels to the center of model!) No matter if I select the div through traversing the DOM or making a fresh element by means of createElement, nothing is being embedded in the div. I suspect that everything is behind the scene(s). Any ideas how can I achieve this, and where I'm doing it wrong?
Related
I'm using ArcRotateCamera, when I click on mesh, I have to focus camera on
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2, 300, BABYLON.Vector3.Zero(), scene);
camera.setTarget(BABYLON.Vector3.Zero());
// on mesh click, focus in
var i = 2;
var pickInfo = scene.pick(scene.pointerX, scene.pointerY);
if (pickInfo.hit) {
pickInfo.pickedMesh.actionManager = new BABYLON.ActionManager(scene);
pickInfo.pickedMesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger,
function (event) {
camera.position = (new BABYLON.Vector3(pickInfo.pickedPoint.x, pickInfo.pickedPoint.y, camera.position.z + i));
i += 2;
})
);
}
this code changes mesh's z position but don't makes it in the center of screen
There are a few things that can be changed in your code.
1st - what you are doing is executing a code action after a click, instead of simply running the code in the callback after a pick has occurred. You are registering a pick action (technically user click) on right on the first frame, but only if the mouse was found in the right location at the right moment. My guess is that it didn't work every time (unless you scene is covered with meshes :-) )
2nd - you are changing the camera's position, instead of change the position to which it is looking. Changing the camera's position won't result in what you want (to see the selected mesh), it will move the camera to a new position while still focusing on the old position.
There are a few ways to solve this. The first is this:
scene.onPointerDown = function(evt, pickInfo) {
if(pickInfo.hit) {
camera.focusOn([pickInfo.pickedMesh], true);
}
}
The ArcRotate camera provides focusOn function that focuses on a group of meshes, while fixing the orientation of the camera. this is very helpful. You can see a demo here:
https://playground.babylonjs.com/#A1210C#51
Another solution would be to use the setTarget function:
https://playground.babylonjs.com/#A1210C#52
Which works a bit differently (notice the orientation change of the camera).
Another thing - use the pointer events integrated in Babylon, as they are saving you the extra call for a scene pick. pointer down is executed with the pickinfo integrated in the function, so you can get the picking info of the current pointer down / up / move each frame.
**** EDIT ****
After a new comment - since you want to animate the values, all you need to do is store the current values, calculate the new ones, and animate the values using the internal animation system (documentation here - https://doc.babylonjs.com/babylon101/animations#basic-animation) . There are many ways to achieve this, I took an old function and modernized it :-)
Here is the demo - https://playground.babylonjs.com/#A1210C#53
Currently I have a project that is being used to draw rooms with lines and images that can be selected by the user by hitting a button representing what they want to add. I.E. if they want a shower a button for shower is pressed and an image appears in a pane. The shower can be resized and moved in the pane. The user also has the ability to use lines to draw objects or walls. The lines can be resized, rotated, or moved. I am now trying to get these objects to interact with each other. Say a user is using lines to make an object, and when the line comes near another object the line being moved snaps to the other object. I have found a 3rd party library that has SnapLineSnapResult but I don't see anything where someone has used it. Is this something that is desktop JavaFX usable, or is it a touch operation and does anyone have code to model or another solution?
SnapLineSnapResult
My code for line that would be useful if I can use this class is as follows:
line.setOnMouseDragged((MouseEvent event1) -> {
// in resize region
if (isInResizeZoneLine(line, event1)) {
// adjust line
line.setEndX(event1.getX());
}
// in movable region
else {
Point2D currentPointer = new Point2D(event1.getX(), event1.getY());
if (bound.getBoundsInLocal().contains(currentPointer)) {
/*--------*/ // potential place for if (near other object to be snapped)
double lineWidth = line.getEndX() - line.getStartX();
line.setStartY(currentPointer.getY());
line.setEndY(currentPointer.getY());
line.setStartX(currentPointer.getX() - lineWidth/2);
line.setEndX(currentPointer.getX() + lineWidth/2);
}
}
});
I have the following (pseudo) code
root = _iface.createRoot(...)
Label l = new Label("hello world");
anim = Animator.create();
anim.delay(1500).then().add(root.layer, l.layer);
anim.delay(1000).then().action(new Runnable() {
public void run() {
// root.add(l);
System.out.println("it works");
}
});
the it work's line gets printed ok, so I assume I'm updating the animation right, but the label is never added to the scene!
If I uncomment the root.add(l) inside the Runnable it works as expected (the label is added after 1 second), but it doesn't get added with anim.delay(1500).then().add(root.layer, l.layer);
any idea whay I'm doing wrong?
You can't just add the layer of a TPUI Widget to another layer and expect the Widget to render properly. A widget must be added to its parent via Group.add.
The animation code you are using is more designed for animating raw PlayN layer's than UI elements. UI elements are generally laid out using a LayoutManager which controls where the layer is positioned. If you tried to animate the layer directly, you would confuse the layout manager and generally mess everything up.
That said, it's pretty safe to animate the Root of the interface, because that anchors a whole UI into the PlayN scene graph.
If you really want to try what you're doing above, don't use Animator.add use:
action(new Runnable() {
root.add(l);
});
(like you have above) which properly adds the Label to the Root, and triggers validation and rendering of the Label.
var newParagraph : ParagraphElement = new ParagraphElement();
var icon : InlineGraphicElement = MY_ICON;
// icon.toolTip = "boo" ????
newParagraph.addChild( icon );
I want a specific toolTip for "icon". I have read about using HTML and rollOver and rollOut (e.g. Thanks, Mister), but I'm building this as part of a larger text block; it's difficult to switch from incremental objects to HTML in the middle.
If I cannot set an event on the icon, can I create an HTML bit in ActionScript as part (but not all) of a paragraph ?
Cheers
I just came across the same problem.
The easiest solution i found was wrapping my InlineGraphicElement in a LinkElement.
The LinkElement already dispatches mouse events.
The problem (as I'm sure you found out) is that text flow elements are not display objects, and so do not implement the normal display object behavior.
What you need to do is create a custom InlineGraphicElement that attaches the event listeners you need, then dispatch an event off of the textFlow instance so it can be read in somewhere in your display object hierarchy (or whatever your method of choice is to target that event).
You can see a good example of how to add mouse interaction by looking at the source to the LinkElement (see the createContentElement function).
Unfortunately the InlineGraphicElement is marked final, so you will need to duplicate it's functionality rather then extend it. Just make sure to use the custom graphic element in your code in lieu of the normal one.
Best of luck!
edit
Just in case the point was lost, the idea is that you can capture the mouse event somewhere in your app by attaching a listener to the textFlow, then programmatically create and position a tool tip over the element using the standard methods to find the bounds of a text flow element.
You can do some hit-testing when the mouse is over the textflow component.
Suppose you have some textflow like this:
<s:RichEditableText width="100%" height="100%" mouseOver="toggleTooltip()">
<s:textFlow>
<s:TextFlow>
<s:p>
<s:span>Some text before</s:span>
<s:img id="myImg" source="myImg.png" />
<s:span>Some text after</s:span>
</s:p>
</s:TextFlow>
</s:textFlow>
</s:RichEditableText>
And you listen for mouseOver events on the entire textcomponent.
Then to test if the mouse is over your image, implement the mouseOver handler:
private function toggleTooltip():void {
var graphic:DisplayObject = myImg.graphic;
var anchor:Point = graphic.localToGlobal(new Point(0, 0));
if (mouseX >= anchor.x && mouseX <= anchor.x + graphic.width &&
mouseY >= anchor.y && mouseY <= anchor.y + graphic.height)
{
trace('show tooltip');
}
else {
trace('hide tooltip');
}
}
I have a several chart components that I have created in Flex. Basically I have set up a special UI that allows the user to select which of these charts they want to print. When they press the print button each of the selected charts is created dynamically then added to a container. Then I send this container off to FlexPrintJob.
i.e.
private function prePrint():void
{
var printSelection:Box = new Box();
printSelection.percentHeight = 100;
printSelection.percentWidth = 100;
printSelection.visible = true;
if (this.chkMyChart1.selected)
{
var rptMyChart1:Chart1Panel = new Chart1Panel();
rptMyChart1.percentHeight = 100;
rptMyChart1.percentWidth = 100;
rptMyChart1.visible = true;
printSelection.addChild(rptMyChart1);
}
print(printSelection);
}
private function print(container:Box):void
{
var job:FlexPrintJob;
job = new FlexPrintJob();
if (job.start()) {
job.addObject(container, FlexPrintJobScaleType.MATCH_WIDTH);
job.send();
}
}
This code works fine if the chart is actually displayed somewhere on the page but adding it dynamically as shown above does not. The print dialog will appear but nothing happens when I press OK.
So I really have two questions:
Is it possible to print flex components/charts when they are not visible on the screen?
If so, how do I do it / what am I doing wrong?
UPDATE:
Well, at least one thing wrong is my use of the percentages in the width and height. Using percentages doesn't really make sense when the Box is not contained in another object. Changing the height and width to fixed values actually allows the printing to progress and solves my initial problem.
printSelection.height = 100;
printSelection.width = 100;
But a new problem arises in that instead of seeing my chart, I see a black box instead. I have previously resolved this issue by setting the background colour of the chart to #FFFFFF but this doesn't seem to be working this time.
UPDATE 2:
I have seen some examples on the adobe site that add the container to the application but don't include it in the layout. This looks like the way to go.
i.e.
printSelection.includeInLayout = false;
addChild(printSelection);
Your component has to be on the stage in order to draw its contents, so you should try something like this:
printSelection.visible = false;
application.addChild(printSelection);
printSelection.width = ..;
printSelection.height = ...;
...
then do the printing
i'm not completely sure, but in one of my application I have to print out a complete Tab Navigator and the only method i have found to print it is to automatically scroll the tabnavigator tab in order to show the component on screen when i add them to the printjob.
In this way they are all printed. Before i created the tabnaviagotr scrolling the print (to PDF) result was a file with all the pages of the tab but only the one visible on screen really printed.