Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions graf3d/eve7/inc/ROOT/REveViewer.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ private:
Bool_t fHasAxesBBox{kFALSE}; ///< see SetAxesBBox()
Float_t fAxesBBox[6]{}; ///< xmin, ymin, zmin, xmax, ymax, zmax

Float_t fMotionMaxHz{60.f}; ///< see SetMotionMaxHz(); 0 freezes
Float_t fRenderMaxHz{0.f}; ///< see SetRenderMaxHz(); 0 is uncapped
bool fBlackBackground{false};

Expand Down Expand Up @@ -161,14 +160,8 @@ public:
void ClearAxesBBox();
Bool_t HasAxesBBox() const { return fHasAxesBBox; }

/// Cap on how often this viewer applies streamed motion, in updates per
/// second, clamped to [0, 240]. Zero freezes motion; SetExtrapolateMotion()
/// only controls drawing between updates.
Float_t GetMotionMaxHz() const { return fMotionMaxHz; }
void SetMotionMaxHz(Float_t);

/// Cap on how often the animation loop redraws this viewer, in frames per
/// second, clamped to [0, 240]. Zero means uncapped, unlike SetMotionMaxHz().
/// second, clamped to [0, 240]. Zero means uncapped.
Float_t GetRenderMaxHz() const { return fRenderMaxHz; }
void SetRenderMaxHz(Float_t);
// clang-format on
Expand Down
3 changes: 1 addition & 2 deletions graf3d/eve7/src/REveElement.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1483,8 +1483,7 @@ void REveElement::AddStamp(UChar_t bits)

void REveElement::BuildRenderData()
{
if (fMainTrans.get())
{
if (fMainTrans && fRenderData) {
fRenderData->SetMatrix(fMainTrans->Array());
}
}
Expand Down
6 changes: 2 additions & 4 deletions graf3d/eve7/src/REveLine.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,8 @@ void REveLineProjected::SetDepthLocal(Float_t d)
{
SetDepthCommon(d, this, fBBox);

Int_t n = fSize;
Float_t *p = & fPoints[0].fZ;
for (Int_t i = 0; i < n; ++i, p+=3)
*p = fDepth;
for (Int_t i = 0; i < fSize; ++i)
fPoints[i].fZ = fDepth;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
28 changes: 18 additions & 10 deletions graf3d/eve7/src/REveManager.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1151,13 +1151,22 @@ void REveManager::StreamSceneChangesToJson()

////////////////////////////////////////////////////////////////////////////////
/// Send transformation-only changes on the motion channel: the same websocket,
/// outside the BeginChanges/EndChanges cycle, with no acknowledgement. A
/// connection for which RWebWindow::CanSend(id, true) is false skips the
/// message. Each message is absolute state, but a client that skips the last
/// one keeps the old state until the element changes again.
/// outside the BeginChanges/EndChanges cycle, with no acknowledgement. The
/// message is sent directly, so it goes only when every connection can take a
/// direct send. Otherwise the elements stay stamped and go out with the
/// regular changes, which carry the same matrix and motion. The motion's t0 is
/// stamped by REveTrans::SetMotion(), so the delay does not shift the
/// trajectory.

void REveManager::SendMotionChanges()
{
if (fConnList.empty())
return;

for (auto &conn : fConnList)
if (!fWebWindow->CanSend(conn.fId, true))
return;

nlohmann::json arr = nlohmann::json::array();

fWorld->StreamMotionChanges(arr);
Expand All @@ -1167,21 +1176,19 @@ void REveManager::SendMotionChanges()
s->StreamMotionChanges(arr);
}

if (arr.empty() || fConnList.empty())
if (arr.empty())
return;

nlohmann::json msg = {};
// clang-format off
msg["content"] = "Motion";
msg["t"] = REveUtil::ServerTimeMs();
msg["els"] = arr;
// clang-format on

std::string data = msg.dump();

for (auto &conn : fConnList)
if (fWebWindow->CanSend(conn.fId, true))
fWebWindow->Send(conn.fId, data);
fWebWindow->Send(conn.fId, data);
}

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1414,7 +1421,8 @@ void REveManager::BeginChange()
/// Stop accepting changes and stream them. If the clients have not yet
/// acknowledged the previous changes, keep the stamps and let the last
/// acknowledgement in WindowData() flush them. Stamps coalesce per element.
/// Motion changes are sent in either case. Changes made by a MIR are not held
/// Motion changes are sent in either case when the connections can take a
/// direct send, see SendMotionChanges(). Changes made by a MIR are not held
/// back.

void REveManager::EndChange()
Expand All @@ -1426,7 +1434,7 @@ void REveManager::EndChange()
std::unique_lock<std::mutex> lock(fServerState.fMutex);

// Motion is outside the change cycle, so it is sent even when changes
// are held.
// are held. What cannot be sent now stays stamped and goes with them.
SendMotionChanges();

if (!fConnList.empty() && !ClientConnectionsFree()) {
Expand Down
9 changes: 6 additions & 3 deletions graf3d/eve7/src/REveProjections.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,19 @@ void REveProjection::ProjectPointfv(const REveTrans* t, const Float_t* p, Float_

////////////////////////////////////////////////////////////////////////////////
/// Project double array, converting it to global coordinate system first if
/// transformation matrix is set.
/// transformation matrix is set. `p` and `v` may be the same array.
/// This is a bit piggish as we convert the doubles to floats and back.

void REveProjection::ProjectPointdv(const REveTrans* t, const Double_t* p, Double_t* v, Float_t d)
{
Float_t x, y, z;
if (t)
{
t->Multiply(p, v);
x = v[0]; y = v[1]; z = v[2];
Double_t buf[3];
t->Multiply(p, buf);
x = buf[0];
y = buf[1];
z = buf[2];
}
else
{
Expand Down
10 changes: 0 additions & 10 deletions graf3d/eve7/src/REveViewer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,6 @@ void REveViewer::SetRenderMaxHz(Float_t hz)
StampObjProps();
}

////////////////////////////////////////////////////////////////////////////////
/// Cap the rate at which this viewer applies streamed motion; 0 freezes it.

void REveViewer::SetMotionMaxHz(Float_t hz)
{
fMotionMaxHz = hz < 0.f ? 0.f : (hz > 240.f ? 240.f : hz);
StampObjProps();
}

////////////////////////////////////////////////////////////////////////////////
/// Set the point the camera looks at and orbits around, either the world
/// origin or the center of the scene bounding box. Clients reset their camera
Expand Down Expand Up @@ -290,7 +281,6 @@ int REveViewer::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset)
j["CameraCenter"] = fCameraCenter;
j["ExtrapolateMotion"] = fExtrapolateMotion;
j["AxesUpAxis"] = fAxesUpAxis;
j["MotionMaxHz"] = fMotionMaxHz;
j["RenderMaxHz"] = fRenderMaxHz;
// clang-format off
if (fHasAxesBBox)
Expand Down
17 changes: 17 additions & 0 deletions graf3d/eve7/test/projection.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <ROOT/REveProjections.hxx>
#include <ROOT/REveProjectionBases.hxx>
#include <ROOT/REveProjectionManager.hxx>
#include <ROOT/REveTrans.hxx>

namespace REX = ROOT::Experimental;

Expand Down Expand Up @@ -81,6 +82,22 @@ TEST(REveProjection, ScreenValRoundTrip)
}
}

// ProjectPointdv() may project in place, as REveTrackProjected does for path
// marks; a rotation must not read coordinates it has already overwritten.
TEST(REveProjection, ProjectPointdvInPlace)
{
REX::REveRPhiProjection p;
REX::REveTrans t;
t.RotateLF(1, 2, 0.5);
t.SetPos(10, 20, 0);

Double_t in[3] = {3, 4, 0}, out[3];
p.ProjectPointdv(&t, in, out, 0.f);
p.ProjectPointdv(&t, in, in, 0.f);
for (int i = 0; i < 3; ++i)
EXPECT_DOUBLE_EQ(in[i], out[i]) << "coordinate " << i;
}

TEST(REveProjection, ImportAndUpdate)
{
auto eve = REX::REveManager::Create();
Expand Down
8 changes: 4 additions & 4 deletions tutorials/visualisation/eve7/projection_axes.C
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ static void makeProjectedView(REveManager *eveMng, REveElement *content, REvePro
mng = new REveProjectionManager(type);
mng->ImportElements(content, scene);

// Add the manager to the element tree. This gives it an element id, which the
// overlay buttons need as their MIR target, and streams its name, which shows
// the distortion, e.g. "RhoPhi (5.0)".
eveMng->GetWorld()->AddElement(mng);
// Add the manager to the scene it fills. This gives it an element id, which
// the overlay buttons need as their MIR target, and streams its name, which
// shows the distortion, e.g. "RhoPhi (5.0)".
scene->AddElement(mng);

auto ovl = eveMng->SpawnNewScene(Form("%s Axis", name), name);
ovl->SetIsOverlay(true);
Expand Down
5 changes: 0 additions & 5 deletions ui5/eve7/controller/Ged.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,6 @@ sap.ui.define([
// Motion settings, in a collapsed panel since they matter only while
// something moves:
//
// MotionMaxHz how often the update stream is applied
// RenderMaxHz how often the result is drawn
// ExtrapolateMotion whether anything is drawn between updates
let motion = new sap.m.Panel({
Expand All @@ -411,10 +410,6 @@ sap.ui.define([
});
this.getView().byId("GED").addContent(motion);

this.makeSliderSetter(el.MotionMaxHz, "MotionMaxHz", null,
{min: 0, max: 60, step: 1,
tip: "Updates per second this viewer applies. 0 freezes the scene."},
motion);
this.makeSliderSetter(el.RenderMaxHz, "RenderMaxHz", null,
{min: 0, max: 120, step: 1,
tip: "Animation redraws per second. 0 is uncapped -- every display frame."},
Expand Down
28 changes: 13 additions & 15 deletions ui5/eve7/lib/EveManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -450,17 +450,19 @@ sap.ui.define([], function() {
jQuery.extend(obj, em);
this.ParseUpdateTriggersAndProcessPostStream(obj);
}
else if (em.changeBit & this.EChangeBits.kCBColorSelection) {
delete em.render_data;
Object.assign(obj, em);
}
else if (em.changeBit & this.EChangeBits.kCBTransBBox) {
// Transformation-only update: no render data is streamed.
// render_data.matrix is updated here because a later rebuild,
// e.g. on a colour change, reads it.
Object.assign(obj, em);
if (em.matrix && obj.render_data)
obj.render_data.matrix = em.matrix;
else {
if (em.changeBit & this.EChangeBits.kCBColorSelection) {
delete em.render_data;
Object.assign(obj, em);
}
if (em.changeBit & this.EChangeBits.kCBTransBBox) {
// Transformation update: no render data is streamed.
// render_data.matrix is updated here because a later rebuild,
// e.g. on a colour change in the same message, reads it.
Object.assign(obj, em);
if (em.matrix && obj.render_data)
obj.render_data.matrix = em.matrix;
}
}

this.callSceneElementChange(scene, em);
Expand Down Expand Up @@ -811,10 +813,6 @@ sap.ui.define([], function() {
let obj = this.map[em.fElementId];
if (!obj) continue; // removed, or not yet created

// The message's own timestamp, so a viewer's rate cap can decide
// once for the whole message rather than once per element.
em.msg_t = resp.t;

// Keep the model in step: a later rebuild reads render_data.matrix,
// and a stale one would put the object back where it used to be.
if (em.matrix && obj.render_data)
Expand Down
8 changes: 2 additions & 6 deletions ui5/eve7/lib/EveScene.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,14 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) {
let el = this.mgr.GetElement(msg.fElementId);
if (!el) return;

// The viewer's rate cap. Dropping an update is safe because each
// carries absolute state.
let mo = this.glctrl ? this.glctrl.viewer.motion : null;
if (mo && !mo.acceptUpdate(msg.msg_t)) return;

this.updateElementTrans(el, msg);

// No endChanges() follows, so request the render here. Going through
// Motion applies the viewer's render cap.
let mo = this.glctrl.viewer.motion;
if (mo)
mo.requestRender();
else if (this.glctrl && this.glctrl.viewer)
else
this.glctrl.viewer.request_render();
}

Expand Down
16 changes: 11 additions & 5 deletions ui5/eve7/lib/GlViewerRCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,8 @@ sap.ui.define([
setTimeout(this.syncCamTransTimer.bind(this), 5000);
}

/** Set scene_bbox from the scene. An empty scene gets a fallback box of
* +-100. Returns true when the scene was empty. */
recalcSceneBBox()
{
// Exclude the axis: it is built from this box, and its ticks reach past
Expand All @@ -592,7 +594,8 @@ sap.ui.define([
if (ax) this.scene.remove(ax);
this.scene_bbox.setFromObject( this.scene );
if (ax) this.scene.add(ax);
if (this.scene_bbox.isEmpty())
const empty = this.scene_bbox.isEmpty();
if (empty)
{
console.error("GlViewerRenderCore.positionCameraAndLights scene bbox empty", this.scene_bbox);
const ext = 100;
Expand All @@ -602,6 +605,7 @@ sap.ui.define([
if (this.axis3d) this.axis3d.setBBox(this.axes_bbox || this.scene_bbox);

this.updateRenderBBox();
return empty;
}

/** The box the camera's near and far planes are fitted to: axes_bbox or
Expand All @@ -619,7 +623,9 @@ sap.ui.define([

positionCameraAndLights()
{
this.recalcSceneBBox();
// An empty scene leaves the camera framing the fallback box; render()
// fits it again when content arrives.
this.camera_needs_fit = this.recalcSceneBBox();

let eveView = this.controller.mgr.GetElement(this.controller.eveViewerId);

Expand Down Expand Up @@ -760,8 +766,6 @@ sap.ui.define([
this.axis3d.setBBox(this.axes_bbox || this.scene_bbox);
this.updateRenderBBox();

if (eveView.MotionMaxHz !== undefined && this.motion)
this.motion.setMaxHz(eveView.MotionMaxHz);
if (eveView.RenderMaxHz !== undefined && this.motion)
this.motion.setRenderMaxHz(eveView.RenderMaxHz);
if (eveView.ExtrapolateMotion !== undefined && this.motion)
Expand Down Expand Up @@ -844,8 +848,10 @@ sap.ui.define([
if (this.annotations) this.annotations.layout();
if (this.axis3d) this.axis3d.updateForCamera(this.camera);
if (this.render_requested_recalc_sbbox) {
this.recalcSceneBBox();
const empty = this.recalcSceneBBox();
this.render_requested_recalc_sbbox = false;
if (this.camera_needs_fit && !empty)
this.positionCameraAndLights();
}
if (this.camera.isPerspectiveCamera) {
this.camera.optimizeNearFar(this.render_bbox || this.scene_bbox);
Expand Down
Loading
Loading