RX - how to use it in a performant way? - rxjs

I am trying to understand how to structure my program to use RX in a performant matter.My app has a vector of objects in the 3D world. each object occupied a box, and have a 'hit' stream, which represent a mouse hover over it. I thought of two options of how to structure:
Option 1
struct object_t
{
string name_;
box bounding_box_;
observable<bool> hit_;
};
struct scene_t
{
scene_t(observable<point> mouse) : hit_(hit(mouse))
{
add({"background", {/* ... */}, {}};
}
object_t& add(object_t o)
{
int object_index = objects_.size();
o.hit_ = hit_
.map([=](int index){ return index == object_index; })
.distinct_until_changed();
objects_.push_back(o);
return objects_.back();
}
//! given a stream of mouse points,
//! calculate on which object index(in objects_) the mouse is hover over.
//! 0 if its over the background.
observable<int> hit(observable<point> mouse);
using objects_t = std::vector<object_t>;
objects_t objects_;
observable<int> hit_
};
Option 2
struct object_t
{
string name_;
box bounding_box_;
void signal_hit(boot is_hit) { hit_.get_observer().on_next(is_hit); }
observable<bool> hit() const { return hit_.get_observable(); }
private:
subject<bool> hit_;
};
struct scene_t
{
scene_t(observable<point> mouse) : hit_(hit(mouse))
{
add({"background", {/* ... */}, {}};
hit_
.start_with(0)
.buffer(2, 1) // take two hits together, the current and the previos
.subscribe([this](std::vector<int> indices) {
objects_[indices[1]].signal_hit(false); // we leave this one
objects_[indices[0]].signal_hit(true); // and entering this one
});
}
object_t& add(object_t o)
{
objects_.push_back(o);
return objects_.back();
}
//! ... as above
};
Now the question is how to chain the result of the hit function to the object_t::hit stream. I see two ways:
Option 1, is fully functional, but very poorly performing, since for every mouse point, all objects stream will need to calculate their value.
Option 2. is not fully functional, as I use subject to push the values to the right stream, in an imperative way. but is very performant as only the right (two) object(s) hit stream get to fire.
Note:
The implementation is in rxcpp, but its general to any language we have RX in it, or general FRP paradigm, this is why I tagged rxjs\rx.net\frp etc.
thanks in advance :-)

If there is one source observable and N subscribers then there will have to be at least N computations every time the source emits. There is no way around that which I can think of.

Related

Recursion over a mutable binary tree: `already borrowed: BorrowMutError`

I'm beginning with a Vec of sorted nodes, then using this sorting to link these nodes together in a binary tree and then returning the base struct
// Test name
#[derive(Clone)]
struct Struct {
parent: Option<Rc<RefCell<Struct>>>,
superscript: Option<Rc<RefCell<Struct>>>,
subscript: Option<Rc<RefCell<Struct>>>,
height: u32,
center: u32,
symbols: VecDeque<u8>
}
Ending up with a binary tree formed by the above Structs. At this point, these Structs are uniquely owned, so I think I could convert from using Rc<RefCell<Struct>> to RefCell<Struct> (think Box<Struct> doesn't work due to internal mutability?), but I'm not sure how or if this helps with the problem I'm encountering.
After this, I need to iterate in a novel manner through the Structs, and mutate the various symbols belonging to the various Structs throughout the recursion, via calling .pop_front().
My current implementation doing this leads to various instances of thread 'main' panicked at 'already borrowed: BorrowMutError'.
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=636c93088f5a431d0d430d42283348f3
The function for it (please excuse the convoluted logic):
fn traverse_scripts(row: Rc<RefCell<Struct>>) {
if let Some(superscript_row) = &row.borrow().superscript {
if let Some(superscript_symbol) = superscript_row.borrow().symbols.front() {
if let Some(current_row_symbol) = row.borrow().symbols.front() {
if superscript_symbol < current_row_symbol {
println!("^{{{}",*superscript_symbol);
superscript_row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(superscript_row));
}
}
else {
println!("^{{{}",*superscript_symbol);
superscript_row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(superscript_row));
}
}
}
if let Some(subscript_row) = &row.borrow().subscript {
if let Some(subscript_symbol) = subscript_row.borrow().symbols.front() {
if let Some(current_row_symbol) = row.borrow().symbols.front() {
if subscript_symbol < current_row_symbol {
print!("_{{{}",*subscript_symbol);
subscript_row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(subscript_row));
}
}
else {
print!("_{{{}",*subscript_symbol);
subscript_row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(subscript_row));
}
}
}
if let Some(current_row_symbol) = row.borrow().symbols.front() {
if let Some(parent_row) = &row.borrow().parent {
if let Some(parent_symbol) = parent_row.borrow().symbols.front() {
if current_row_symbol < parent_symbol {
print!(" {}",*current_row_symbol);
row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(&row));
}
}
}
else {
print!(" {}",*current_row_symbol);
row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(&row));
}
}
if let Some(parent_row) = &row.borrow().parent {
if let Some(parent_symbol) = parent_row.borrow().symbols.front() {
print!("}} {}",*parent_symbol);
row.borrow_mut().symbols.pop_front();
traverse_scripts(Rc::clone(parent_row));
} else {
print!("}}");
traverse_scripts(Rc::clone(parent_row));
}
}
}
I've considered using Arc<Mutex<Struct>> instead for the traversal, but given its not multi-threaded I don't think it's neccessary?
I imagine I might be missing a relatively simple idea here, I would really appreciate any help.
If I've missed anything in my question please drop a comment and I'll try to add it.
When you call borrow or borrow_mut on a RefCell, a guard object (Ref or RefMut) is created that grants access to the inner value for as long as it exists. This guard will lock the RefCell until it goes out of scope and is destroyed. Let's look at a portion of traverse_scripts:
if let Some(superscript_row) = &row.borrow().superscript { // row is borrowed
if let Some(superscript_symbol) = superscript_row.borrow().symbols.front() { // superscript_row is borrowed
if let Some(current_row_symbol) = row.borrow().symbols.front() { // row is borrowed again
if superscript_symbol < current_row_symbol {
println!("^{{{}", *superscript_symbol);
superscript_row.borrow_mut().symbols.pop_front(); // superscript_row is borrowed mutably (ERROR)
traverse_scripts(Rc::clone(superscript_row)); // recursive call while row and superscript_row are borrowed (ERROR)
}
} else {
println!("^{{{}", *superscript_symbol);
superscript_row.borrow_mut().symbols.pop_front(); // superscript_row is borrowed mutably (ERROR)
traverse_scripts(Rc::clone(superscript_row)); // recursive call while row and superscript_row are borrowed (ERROR)
} // row is no longer borrowed twice
} // superscript_row is no longer borrowed
} // row is no longer borrowed
In the first line, for example, row.borrow() returns a Ref<Struct>. This Ref can't be dropped immediately, because it's being borrowed during the if let body by superscript_row. So it stays alive that whole time -- until the final }.
This is a problem for recursively calling traverse_scripts because the Struct is borrowed during the entire recursive call. Any attempt to borrow the same Struct mutably deeper in the call stack will fail. (Borrowing it immutably does still work.)
In the second line superscript_row is borrowed. This has the same problem, but it also has a more immediate one: it's borrowed mutably later in the same function, even before hitting the recursive call. That borrow_mut can never succeed because superscript_row is always already borrowed at that point.
To fix both problems, we'll do two things:
Store each Ref or RefMut guard in a variable of its own and re-use that guard, instead of calling borrow() or borrow_mut() on the same variable again.
Before recursing, drop each of the still-living guards so that nothing is still borrowed inside the recursive call.
Here's what that section might look like rewritten:
{ // This scope will constrain the lifetime of row_ref
let row_ref = row.borrow();
if let Some(superscript_row) = &row_ref.superscript {
let mut child = superscript_row.borrow_mut(); // use borrow_mut here because we know we'll need it later
if let Some(superscript_symbol) = child.symbols.front() {
if let Some(current_row_symbol) = row_ref.symbols.front() {
if superscript_symbol < current_row_symbol {
println!("^{{{}", *superscript_symbol);
child.symbols.pop_front();
drop(child); // child is no longer needed, so drop it before recursing
// Since superscript_row borrows from row_ref, we must Rc::clone it before
// dropping row_ref so that we can still pass it to traverse_scripts.
let superscript_row = Rc::clone(superscript_row);
drop(row_ref); // row_ref is no longer needed, so drop it before recursing
traverse_scripts(superscript_row);
}
} else {
println!("^{{{}", *superscript_symbol);
child.symbols.pop_front();
// see comments earlier
drop(child);
let superscript_row = Rc::clone(superscript_row);
drop(row_ref);
traverse_scripts(superscript_row);
}
}
} // child is dropped here (if it wasn't already). superscript_row is no longer borrowed
} // row_ref is dropped here (if it wasn't already). row is no longer borrowed
Full-program playground.
This looks complicated because it is complicated. Traversing over a data structure while mutating it is a common source of bugs (in most languages, not just Rust). It looks like, in traverse_scripts at least, the only reason for needing mutation is to call pop_front on symbols, so if you can redesign the data structure such that only symbols is in a RefCell, you could do the traversal with only & references, which would be much easier. Another common approach is to write functions that return new data structures instead of mutating them in place.

Trigger ray cast tests consecutively

Following this answer, I'm doing consecutive ray casts:
m_rayCaster = new Qt3DRender::QRayCaster(m_scene->rootEntity());
m_rayCaster->setRunMode(Qt3DRender::QAbstractRayCaster::SingleShot);
m_scene->rootEntity()->addComponent(m_rayCaster);
I have these slots to handle whether and when next consecutive ray cast test should be done:
QObject::connect(m_rayCaster, &Qt3DRender::QRayCaster::hitsChanged, this, &RayCastHandler::handleRayCasterHits);
QObject::connect(m_rayCaster, &Qt3DCore::QNode::enabledChanged, this, &RayCastHandler::handleRayCasterEnabledChange);
QObject::connect(this, &RayCastHandler::isPreviousTestDoneChanged, this, &RayCastHandler::handleIsPreviousTestDoneChange);
QObject::connect(this, &RayCastHandler::isNextTestRequiredChanged, this, &RayCastHandler::handleIsNextTestRequiredChange);
The slots set the conditions and check them:
void RayCastHandler::handleRayCasterHits(const Qt3DRender::QAbstractRayCaster::Hits hits)
{
analyzeHits(hits);
bool required = isNextTestRequired(/* according to m_testCounter, m_testsTotal, ... */);
emit isNextTestRequiredChanged(required);
emit isPreviousTestDoneChanged(true);
return;
}
void RayCastHandler::handleRayCasterEnabledChange(const bool enabled)
{
m_isRayCasterEnabled = enabled;
triggerNextTestIfAllConditionsAreTrue();
return;
}
void RayCastHandler::handleIsPreviousTestDoneChange(const bool done)
{
m_isPreviousTestDone = done;
triggerNextTestIfAllConditionsAreTrue();
return;
}
void RayCastHandler::handleIsNextTestRequiredChange(const bool required)
{
m_isNextTestRequired = required;
if (!m_isNextTestRequired)
emit rayCastResultsChanged(m_collisions);
triggerNextTestIfAllConditionsAreTrue();
return;
}
The code which checks if next ray cast test is required:
bool RayCastHandler::isNextTestRequired(int &testCounter, const int &testsTotal)
{
testCounter++;
if (testCounter >= testsTotal) {
return false;
}
return true;
}
And finally, the function which checks all the conditions to trigger next ray cast test is:
bool RayCastHandler::triggerNextTestIfAllConditionsAreTrue()
{
if (m_isPreviousTestDone && m_isNextTestRequired && m_isRayCasterEnabled) {
triggerTest(/* Will trigger next ray cast test */);
m_isPreviousTestDone = false;
m_isNextTestRequired = false;
m_isRayCasterEnabled = false;
}
}
The code works fine, but after casting a few consecutive rays, it stops.
By logging to console, I observe that the m_rayCaster looks to be enabled/disabled randomly. I mean sometimes after finishing a ray cast test, it disables itself, and sometimes it enables itself! I wonder if anybody can introduce a reference on Qt3DRender::QRayCaster enabling/disabling logic. I looked at its source code a bit, I wonder which section of source code might help me to figure out.
Just wanted to share my observations:
I simplified the code by keeping only two signal-slot connections:
QObject::connect(m_rayCaster, &Qt3DRender::QRayCaster::hitsChanged, this, &RayCastHandler::handleRayCasterHits);
QObject::connect(m_rayCaster, &Qt3DCore::QNode::enabledChanged, this, &RayCastHandler::handleRayCasterEnabledChange);
One slot analyzes the hits of ray-caster:
void RayCastHandler::handleRayCasterHits(const Qt3DRender::QAbstractRayCaster::Hits hits)
{
analyzeHits( ... , hits);
return;
}
The other slot runs the next consecutive ray-cast test, if ray-caster has disabled itself:
void RayCastHandler::handleRayCasterEnabledChange(const bool enabled)
{
// When the component disables itself, it is ready for the next ray-cast test
if (!enabled) {
bool required = isNextTestRequired( ... );
if (required)
triggerTest( ... );
else
// Send final ray-cast results by a signal, if next test is NOT needed
emit rayCastResultsChanged( ... );
}
return;
}
The above code works as long as I trigger ray-cast tests with a time-delay. Sometimes I have to increase the above delay time to make it work. But at least it works. Although it is painful since it is NOT reliable:
void RayCastHandler::triggerTest( ... )
{
...
// 1 millisecond delay time
QTimer::singleShot(1, [rayCaster, origin, direction, length](){rayCaster->trigger(origin, direction, length);});
...
}
However, if I use no delay time, at some point, the ray caster stops unexpectedly, without sending any signal containing hit results, and ray caster stays enabled forever. Looks like ray-caster gets stuck:
void RayCastHandler::triggerTest( ... )
{
...
// No delay
rayCaster->trigger(origin, direction, length);
...
}

Leaflet polygons sorting

Is there a way, how to z-index polygons using Leaflet nowadays? I am OK when the map is initiated, but when adding new polygons to existing map, I need new polygons to be sorted into existing ones base on their area - so bigger ones will not overlap small ones. I have found this solution:
Leaflet z-index
but it's veeeeery slow, when my map contains bigger amount of features. Any idea?
I have no idea about performance of this answer, but you could give it a try:
GeoJSON Layer Order In Leaflet 0.7.5
The main code is:
// To be called after adding a geoJsonLayer to the map.
function assignZindex(geoJsonLayer) {
geoJsonLayer.eachLayer(function (layer) {
layer._container.zIndex = layer.options.zIndex;
});
}
// To be called after assignZindex().
function reOrderVectorLayers() {
var root = map._pathRoot,
child = root.firstChild,
next;
while (child) {
next = child.nextSibling;
if (!next) {
break;
}
if (next.zIndex < child.zIndex) {
root.insertBefore(next, child);
if (next === root.firstChild) {
continue;
}
child = next.previousSibling;
continue;
}
child = next;
}
}
It assumes the zIndex property is a number defined in options of each of your vector (polygon) layer.
As said in comments of that question, if you use Leaflet 1.x, you can now create your own panes that you can order through CSS z-index, and insert each of your vector layer in a specified pane.

Restrict the min/max zoom on a Bing Map with v7 of the AJAX control?

I'm working on a site that makes use of v7 of the Bing Maps AJAX Control. One of the things I need to do is restrict the zoom level so as to prevent users from zoom in past a certain level, or zoom out past a certain level.
I found a "getZoomRange" method on the Map object, after inspecting it, it simply returns an object literal with "min" and "max" properties. So, I figured overloading it would probably do the trick:
// "map" is our Bing Maps object
map.getZoomRange = function ()
{
return {
max: 14
min: 5
};
};
...but no. It has no effect (it actually has something to do with the appearance of the zoom slider when using the default Dashboard).
Hijacking the event and preventing it from proceeding also seems to have no effect.
According to Bing Maps support, the only way to do this (which isn't particularly elegant, and results in some unwelcome jitter on the map) is as follows:
// "map" is our Bing Maps object, overload the built-in getZoomRange function
// to set our own min/max zoom
map.getZoomRange = function ()
{
return {
max: 14,
min: 5
};
};
// Attach a handler to the event that gets fired whenever the map's view is about to change
Microsoft.Maps.Events.addHandler(map,'viewchangestart',restrictZoom);
// Forcibly set the zoom to our min/max whenever the view starts to change beyond them
var restrictZoom = function ()
{
if (map.getZoom() <= map.getZoomRange().min)
{
map.setView({
'zoom': map.getZoomRange().min,
'animate': false
});
}
else if (map.getZoom() >= map.getZoomRange().max)
{
map.setView({
'zoom': map.getZoomRange().max,
'animate': false
});
}
};
I was dealing with a similar issue and I ended up doing something very similar to what MrJamin describes in his answer, with one (subtle, but major) difference: I added a handler for targetviewchanged. According to the official docs on MSDN, 'targetviewchanged' occurs when the view towards which the map is navigating changes. Also, instead of calling Map#getZoom, I used Map#getTargetZoom which returns the zoom level of the view to which the map is navigating. Note, this approach prevents jitter.
Here's the shortened version of my code:
function restrictZoom(map,min,max) {
Microsoft.Maps.Events.addHandler(map,'targetviewchanged',function(){
var targetZoom = map.getTargetZoom();
var adjZoom = targetZoom;
if(targetZoom > max) {
adjZoom = max;
} else if(targetZoom < min) {
adjZoom = min;
}
if(targetZoom != adjZoom) {
map.setView({zoom:adjZoom});
}
});
}
Another way to achieve this is to handle the event thrown when the mouse wheel is moved. http://msdn.microsoft.com/en-us/library/gg427609.aspx
When you handle the mousewheel event, you can check whether the mouse wheel is being scrolled forwards or backwards, and then check the map.targetZoom() in order to compare with a min or max zoom value. If the min or max are exceeded, then set event.handled = true. This prevents the event from being handled by any other handlers which prevents default behaviour. From the documentation:
A boolean indicating whether the event is handled. If this property is
set to true, the default map control behavior for the event is
cancelled.
See below:
var Zoom = {
MAX: 10,
MIN: 2
}
var mouseWheelHandler = function(event) {
// If wheelDelta is greater than 0, then the wheel is being scrolled forward which zooms in
if(event.wheelDelta > 0) {
if(map.getTargetZoom() >= Zoom.MAX) {
event.handled = true;
}
}
else {
if(map.getTargetZoom() <= Zoom.MIN) {
event.handled = true;
}
}
}
Microsoft.Maps.Events.addHandler(map, 'mousewheel', mouseWheelHandler);

How do I limit mouse pointer movement in wxWidgets?

Is there a way to limit mouse pointer movement to a specific area in wxWidgets? I know there is an API function ClipCursor() in Windows, but is there a method in wxWidgets for all platforms?
No. There is no such function in wx by all i know. Start up a timer (say 50ms) checking the global mouse position. If the mouse is outside the region, then set it into again.
If you want to restrict the mouse for some certain reason, for example to make some sort of game, then you can capture the mouse (see wxWindow::CaptureMouse). You will get mouse events even if the pointer is outside your window. Then you could react to mouse-motion events and do the check for the position there, without a timer. Downside of this is that the mouse won't be able to be used somewhere else for other programs since they won't receive events.
wxWidgets manual states that OSX guidelines forbid the programs to set the mouse pointer to a certain position programmatically. That might contribute to the reason there is not much support for such stuff in wx, especially since wx tries really hard to be compatible to everything possible.
Small sample. Click on the button to restrict the mouse to area 0,0,100,100. Click somewhere to release it.
#include <wx/wx.h>
namespace sample {
class MyWin : public wxFrame {
public:
MyWin()
:wxFrame(0, wxID_ANY, wxT("haha title")) {
mRestricted = wxRect(0, 0, 100, 100);
mLast = mRestricted.GetTopLeft();
wxButton * button = new wxButton(this, wxID_ANY, wxT("click this"));
}
private:
void OnClicked(wxCommandEvent& event) {
if(!HasCapture()) {
CaptureMouse();
CheckPosition();
}
}
void OnMotion(wxMouseEvent& event) {
CheckPosition();
}
void OnLeft(wxMouseEvent& event) {
if(HasCapture())
ReleaseMouse();
}
void CheckPosition() {
wxPoint pos = wxGetMousePosition();
if(!mRestricted.Contains(pos)) {
pos = ScreenToClient(mLast);
WarpPointer(pos.x, pos.y);
} else {
mLast = pos;
}
}
wxRect mRestricted;
wxPoint mLast;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(MyWin, wxFrame)
EVT_BUTTON(wxID_ANY, MyWin::OnClicked)
EVT_MOTION(MyWin::OnMotion)
EVT_LEFT_DOWN(MyWin::OnLeft)
END_EVENT_TABLE()
class MyApp : public wxApp {
virtual bool OnInit() {
MyWin * win = new MyWin;
win -> Show();
SetTopWindow(win);
return true;
}
};
} /* sample:: */
IMPLEMENT_APP(sample::MyApp)

Resources