I am trying to draw circles around an icon that is selected via clicking. My current code is:
this.handler.setInputAction(function(click) {
var pickedObjects = viewer.scene.drillPick(click.Position);
if(Cesium.defined(pickedObjects)) {
if(pickedObjects.length >=1)
{
var cartesian = thisRef.viewer.camera.pickEllipsoid(click.position, thisRef.viewer.scene.globe.ellipsoid);
thisRef.drawCircle(cartesian);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK;
};
If the user is zoomed out quite far, the position won't be accurate. It needs to be based on the selected object, not the users click. However I can't figure out how to do this. I have pickedObjects, but I can't figure out how to get their position from those objects. It doesn't seem to be an entity (even though I think the icon was an entity when it was being created) and so I can't use entity.position. Thank you for your help.
To be able to access the standard Cesium entity, it turns out, you must go in the drillPick objects id. So I modified my code to get the first object in the list of objects and get the id from that, and now I can call the member position of a standard entity.
this.handler.setInputAction(function(click) {
var pickedObjects = viewer.scene.drillPick(click.Position);
if(Cesium.defined(pickedObjects)) {
if(pickedObjects.length >=1)
{
var entity = pickedObjects[0].id;
thisRef.drawCircle(entity.position);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK;
};
Related
I want to duplicate my GLTF models with different positions/colors dynamically, to do so I have done:
const L_4_G = new Object3D();
...
const multiLoad_4 = (result, position) => {
const model = result.scene.children[0];
model.position.copy(position);
model.scale.set(0.05, 0.05, 0.05);
//
L_4_G.add(model.clone())
scene.add(model);
};
...
function duplicateModel4() {
L_4_G.translateX(-1.2)
L_4_G.translateY(0.0)//0.48
L_4_G.translateZ(1.2)
L_4_G.rotateY(Math.PI / 2);
scene.add(L_4_G);
}
I didn't find out how can I change the Object3D color from the documentation, can you please tell me how can I do that? thanks in advance.
Here is the full code that I'm using, and here are the models
Update
I have seen this solution, to store a set of colors in the object's userData and choose the color later:
L_2_G.userData.colors = {green : #00FF00, red : ..., ...}
L_2_G.children[0].material.color(userData.colors["green"])
But I'm getting an error that children[0] undefined, but I can see that this object has a child and a material, and color via the console: console.log(L_2_G.children), console.log(L_2_G.children.length)--> 0
Also I have tried getObjectByName as explained here:
scene.getObjectByName(name).children[0].material.color.set(color);
which also reslts: children[0] is undefined, scene.getObjectByName(name).children.length is 0.
THREE.Object3D is a base class for anything that can go in a scene graph, including lights, cameras, and empty objects. Not all Object3D instances have geometry or materials. You may be looking for the THREE.Mesh subclass which does have materials and colors.
In general, code like getObjectByName(...) and model = result.scene.children[0] is very content-specific. The file might contain many nested objects, and .children[0] just grabs the first part. It's usually best to traverse the scene graph instead, looking for the objects you want to modify (e.g. looking for all Meshes, or Meshes with a particular name).
const model = result.scene;
model.traverse((object) => {
if (object.isMesh) {
object.material.color.setHex( 0x404040 );
}
});
Then you can either add the entire group to your scene (scene.add(model)), or just add parts of it. Keep in mind that adding meshes to a new parent removes them from their previous parent, and you shouldn't do that while traversing the previous parent. Instead you can make a list of meshes, and add them in a second step:
const meshes = [];
result.scene.traverse((object) => {
if (object.isMesh) {
meshes.push(object);
}
});
for (const mesh of meshes) {
scene.add(mesh);
}
Finally, the position of an object is inherited from its parents. By removing the object from its original parents you might change its position in the scene. If you are planing to assign a new position to the object anyway, that is fine.
I need to be able to undo / redo the colors that are picked with the new SwiftUI ColorPicker ( on the iPad : it's presented as a floating window )
The thing that makes it very difficult is that there is apparently no way to know that the user has indeed chosen a color ( and therefore closed the panel )
Instead, the behavior of ColorPicker is that it will keep updating the binded color as the user is manipulating the color controls. This is very helpful to show a live preview, but you don't want to register all these color variations for undo / redo purposes : you only want the color that was finally picked.
Therefore there is no logical distinction between the colors that the user tried, and the one that was selected
And I looked everywhere : there aren't any modifiers / notifications related to that.
I know SwiftUI hasn't been there for long, but this seems like a crucial functionality that's missing?
Has anyone found a workaround?
Undo is always tricky. One size does not fit all. In this case a good solution is to throttle the number of events that come into the UndoManager. You can do so by comparing the current time to the time you last commit.
Create a view model that represents your Source of Truth, and conform to ObservableObject to publish events. It will own the UndoManager. It will have a computed property that gets/sets to the internal data, and check the current time against the time of last commit.
class EditViewModel: ObservableObject {
var commitTime = Date.now
let undoManager = UndoManager()
private var _color: Color = .red {
didSet {
objectWillChange.send()
}
}
var color: Color {
get { _color }
set {
let oldValue = color
let now = Date.now
if now.timeIntervalSince(commitTime) > 1 {
undoManager.registerUndo(withTarget: self) {
$0.color = oldValue
}
}
self.commitTime = now
_color = newValue
}
}
}
Now all that's left to do is create your view and pass in the binding. The implementation details are opaque to the View since it is managed by the ViewModel.
struct EditView: View {
#StateObject var vm = EditViewModel()
var body: some View {
Form {
ColorPicker("Color", selection: $vm.color)
}
}
}
If you need even more control, you can additionally compare the last committed color to the new value and only commit if there is a significant change.
I will also agree that a data-driven approach like SwiftUI's can make undo more tricky. For example, when you need to coalesce multiple operations together into one undo group. By its very nature an undo group is procedural-- the user did one thing after the other, and finally terminates on some condition. But I'm sure there is some way to encapsulate this step-wise operation in a transaction object of some kind.
Undo is hard!
If I have a lottie animation in the form of a json file, is there a way to recolor it in code or even within the json itself?
(To be clear, I hope there's a way to do it without involving After Effects. For instance if I decide to change my app's primary color, the whole app will change except the animation unless there's a way to do that.)
I figured it out. For this example, let's say I want to recolor a specific layer to Color.RED.
You'll need your LottieAnimationView, a KeyPath, and a LottieValueCallback
private LottieAnimationView lottieAnimationVIew;
private KeyPath mKeyPath;
private LottieValueCallback<Integer> mCallback;
Then in your onCreate (or onViewCreated for a fragment) you'll get the animation with findViewById, as well as "addLottieOnCompositionLoadedListener" to the lottieAnimationView, in which you will setup the "mKeyPath" and "mCallback":
lottieAnimationVIew = findViewById(R.id.animationView);
lottieAnimationView.addLottieOnCompositionLoadedListener(new LottieOnCompositionLoadedListener() {
#Override
public void onCompositionLoaded(LottieComposition composition) {
mKeyPath = getKeyPath(); // This is your own method for getting the KeyPath you desire. More on that below.
mCallback = new LottieValueCallback<>();
mCallback.setValue(Color.RED);
checkBox.addValueCallback(mKeyPath, LottieProperty.COLOR, mCallback);
}
});
The argument "LottieProperty.COLOR" specifies which property I am changing.
There's probably a better way to do this, but here's my "getKeyPath" method for finding the specific thing I want to change. It will log every KeyPath so you can see which one you want. Then it returns it once you've supplied the correct index. I saw that the one I want is the 5th in the list, hence the hard-coded index of 4.
private KeyPath getKeyPath() {
List<KeyPath> keyPaths = lottieAnimationView.resolveKeyPath(new KeyPath("Fill", "Ellipse 1", "Fill 1"));
for (int i = 0; i < keyPaths.size(); i++) {
Log.i("KeyPath", keyPaths.get(i).toString());
}
if (keyPaths.size() == 5) {
return keyPaths.get(4);
}
else {
return null;
}
}
Note that the "Fill", "Ellipse 1", "Fill 1" are strings I supplied to narrow the list down to just the ones that have those keys, because I know that the layer I want will be among those. There's likely a better way to do this as well.
There is another thread on this topic with the same approach but a bit simplified:
How to add a color overlay to an animation in Lottie?
Here's directly an example (Kotlin):
yourLottieAnimation.addValueCallback(
KeyPath("whatever_keypath", "**"),
LottieProperty.COLOR_FILTER
) {
PorterDuffColorFilter(
Color.CYAN,
PorterDuff.Mode.SRC_ATOP
)
}
You can find the names of the keypaths also in the Lottie editor.
When a grid is in a component at one route and the user navigates away and then comes back, the current kendo grid loses its scroll position and starts at the top.
Anyone know a way to "remember" the scroll position so it can be set on the grid manually?
Not a great solution, but it works for the time being. Here it is.
Inside the component with the grid:
private _scrollPos: number;
#ViewChild("grid", { read: ElementRef }) gridEl: ElementRef;
constructor(private router: Router) { }
ngOnInit(): void {
this.router.events.subscribe(e => {
if (e instanceof NavigationStart) {
let gridContent = this.getGridContentElement();
if (gridContent.scrollTop > 0) {
this._scrollPos = gridContent.scrollTop;
}
}
else if (e instanceof NavigationEnd) {
if (this._scrollPos > 0) {
let gridContent = this.getGridContentElement();
gridContent.scrollTo({ top: this._scrollPos });
}
}
}
}
private getGridContentElement(): Element {
let el = this.gridEl.nativeElement as HTMLElement;
let gridContent = el.getElementsByClassName("k-grid-content").item(0);
return gridContent;
}
This is can done by remembering which row was selected, saving the id of the row, and then setting the grid property [selectedKeys]="id" when the user navigates back.
For example if the data for the grid was based on product data then save last viewed row id (ProductID). When the user navigates back to grid, you can push the ProductID you saved to an array that is bound on the grid. This will make the row selected, to scroll to the row you can use the class k-state-selected and scrollIntoView to scroll the row into view.
This is a basic implementation, but should give you a enough to go on. I created a Stackblitz example so you can see how to implement this. Make sure to configure the grid with kendoGridSelectBy and selectedKeys. The kendoGridSelectBy should match the id that you push to the array. In the example I created it was ProductID.
You can set the Grid skip to the same value it last was. You can check out the persist state examples. The Grids use regular paging, but the same principles apply.
Hey after loading a kml file to google earth I was trying to have when a user clicks a certain polygon from the kml, to have that polygon highlighted.
So far I can record the click event, get the event type (KmlPlacemark) and grab its kml markup.
I tried doing something similar to this example where they add a placemark to the getFeatures of the kmlObject but both target and type don't seem to have 'getFeatures'. After looking around the documentation I think I might either want setOutline from Kml Polystyle class or setWidth() from KmlLineStyle class but am not sure. Also when I try something like target.setOutline(true); it doesn't work.
Can anyone tell me if I'm on the right track, hints to what I'm doing wrong, and if there's a better way to do this?
function recordEvent(event) {
var target = event.getTarget();
var type = target.getType();
if(type == "KmlPolygon") {
alert("KMLPolygon ");
}else if(type == "KmlPlacemark") {
// // get the data you want from the target.
var description = target.getDescription();
var balloon = target.getBalloonHtml();
var outputKml = target.getKml();
if ('getFeatures' in event) {
console.log("test");
event.getFeatures().appendChild(placemark);
}
console.log("hello?")
// target.setOutline(true);
console.log(outputKml);
}
};
google.earth.addEventListener(ge.getGlobe(), 'click', recordEvent);
Thanks!
I find the best way to do what you are asking is to:
Detect click events like you currently do
If clicked, create a new Style, then assign it to the target
var newStyle = ge.createStyle('');
// Assign your Style's attributes such as LabelStyle and IconStyle
// eg to set the scale of your label
newStyle.getLabelStyle().setScale(2.5);
// Set the Style
target.setStyleSelector(newStyle);
Edit to add in this link of a Google example showing it more in depth
https://code.google.com/apis/ajax/playground/#styling_placemarks_using_style_maps