24 #include "dimension.h" 28 #include "capiRenderer.xcd" 29 #include "dimension.rcd" 30 #include "dynamicRavelCAPI.rcd" 31 #include "dynamicRavelCAPI.xcd" 32 #include "handleLockInfo.rcd" 33 #include "handleLockInfo.xcd" 34 #include "hypercube.rcd" 35 #include "hypercube.xcd" 38 #include "ravelState.xcd" 39 #include "ravelState.rcd" 40 #include "ravelWrap.rcd" 41 #include "ravelWrap.xcd" 42 #include "xvector.rcd" 54 unsigned RavelLockGroup::nextColour=1;
55 SVGRenderer Ravel::svgRenderer;
63 Ravel::Ravel(): popup(*this)
67 tooltip(
"https://ravelation.net");
76 wrappedRavel.addHandle(
"Country",{
"Australia",
"UK",
"USA"});
102 cairo_rectangle(cairo,-
r,-
r,2*
r,2*
r);
103 cairo_rectangle(cairo,-1.1*
r,-1.1*
r,2.2*
r,2.2*
r);
104 cairo_stroke_preserve(cairo);
107 const cairo::CairoSave cs(cairo);
108 cairo::Colour c{1,1,1,0};
110 c=palette[
lockGroup->colour() % paletteSz ];
111 c.r*=0.5; c.g*=0.5; c.b*=0.5;
113 cairo_set_source_rgba(cairo,c.r,c.g,c.b,c.a);
114 cairo_set_fill_rule(cairo,CAIRO_FILL_RULE_EVEN_ODD);
115 cairo_fill_preserve(cairo);
121 const cairo::CairoSave cs(cairo);
122 cairo_rectangle(cairo,-
r,-
r,2*
r,2*
r);
126 cairo_scale(cairo,z,z);
132 cairo_translate(cairo,-
r,-
r);
151 return std::abs(xx-
x())<=
r && std::abs(yy-
y())<=
r;
185 auto& xv=hc.xvectors;
186 for (
auto h: outHandles)
192 xv.back().dimension=dim->second;
195 xv.back().dimension=dim->second;
197 for (
auto& i: labels)
198 xv.back().push_back(i);
207 const bool redistribute=!
initState.empty();
225 for (
size_t i=0; i<d.size(); ++i)
235 for (
size_t i=0; i<rank; ++i) ids.push_back(i);
250 case 0xff52:
case 0xff53:
253 case 0xff51:
case 0xff54:
266 for (
auto&
h: state.handleStates)
267 if (collapse && !
h.collapsed)
272 else if (!collapse &&
h.collapsed)
285 return state.displayFilterCaliper;
318 vector<size_t> customOrder, currentOrder=
wrappedRavel.currentPermutation(
axis);
320 map<string,size_t> idxMap;
321 for (
size_t i=0; i<allLabels.size(); ++i)
322 idxMap[allLabels[i]]=i;
323 set<string> picked(pick.begin(), pick.end());
324 for (
auto i: currentOrder)
326 auto pickedIter=picked.find(allLabels[i]);
327 if (pickedIter==picked.end())
continue;
328 picked.erase(pickedIter);
329 customOrder.push_back(i);
332 for (
auto& i: picked)
334 auto j=idxMap.find(i);
336 customOrder.push_back(j->second);
338 assert(!customOrder.empty());
362 return ravel::HandleSort::none;
375 const int outputHandleId=
wrappedRavel.outputHandleIds()[0];
379 case ravel::HandleSort::dynamicForward:
380 case ravel::HandleSort::dynamicReverse:
382 auto calipers=
wrappedRavel.getCaliperPositions(outputHandleId);
384 wrappedRavel.displayFilterCaliper(outputHandleId,
false);
386 wrappedRavel.displayFilterCaliper(outputHandleId,hs.displayFilterCaliper);
387 wrappedRavel.setCaliperPositions(outputHandleId,calipers.first,calipers.second);
416 try {
minsky().
requestReset();}
catch (...) {
throw runtime_error(
"Cannot sort handle at the moment");}
417 auto vv=
m_ports[1]->getVariableValue();
419 throw runtime_error(
"Cannot sort handle at the moment");
444 return i->second.type;
447 return i->second.type;
449 return Dimension::string;
460 if (descr.empty())
return "";
463 return i->second.units;
466 return i->second.units;
480 if (descr.empty())
return;
485 if (
type!=i->second.type)
486 throw error(
"type mismatch with global dimension");
500 if (
auto vv=
m_ports[0]->getVariableValue())
502 vv->exportAsCSV(filename,
wrappedRavel.description(), tabular);
518 if (inputUnits.empty())
return inputUnits;
525 if (state.collapsed && state.reductionOp==ravel::Op::prod)
529 for (
auto& u: inputUnits)
530 u.second*=multiplier;
557 if (isspace(c) && ++spCnt % 5 == 0)
579 r.insert(
ravel->lockGroup->colour());
582 return {
r.begin(),
r.end()};
588 if (
auto ravel=(*i)->ravelCast();
ravel &&
ravel->lockGroup &&
ravel->lockGroup->colour()==colour)
612 vector<shared_ptr<Ravel>> lockedRavels;
616 lockedRavels.push_back(i.lock());
617 if (lockedRavels.back().get()==&ravel)
618 ravelIdx=lockedRavels.size()-1;
620 if (ravelIdx==
m_ravels.size())
return;
629 sourceState.radius=r->radius();
630 r->applyState(sourceState);
636 vector<const ravel::HandleState*> sourceHandleStates;
637 set<string> handlesAdded;
640 auto hs=find_if(sourceState.handleStates.begin(), sourceState.handleStates.end(),
641 [&](
const ravel::HandleState& hs){
return hs.description==i.handleNames[ravelIdx];});
642 if (hs!=sourceState.handleStates.end())
644 sourceHandleStates.emplace_back(&*hs);
645 if (!handlesAdded.insert(hs->description).second)
646 throw runtime_error(
"Multiple locks found on handle "+hs->description);
649 sourceHandleStates.emplace_back(
nullptr);
654 for (
size_t ri=0; ri<
m_ravels.size(); ++ri)
657 if (r.get()==&ravel)
continue;
658 auto state=r->getState();
659 set<string> outputHandles(state.outputHandles.begin(), state.outputHandles.end());
662 if (!sourceHandleStates[i])
continue;
663 auto& sourceHandleState=*sourceHandleStates[i];
666 auto handleState=find_if(state.handleStates.begin(), state.handleStates.end(),
667 [&](ravel::HandleState& s){
return s.description==hlInfo.handleNames[ri];});
668 if (handleState!=state.handleStates.end())
672 handleState->sliceLabel=sourceHandleState.sliceLabel;
673 if (find(sourceState.outputHandles.begin(), sourceState.outputHandles.end(), sourceHandleState.description)!=sourceState.outputHandles.end())
675 outputHandles.insert(handleState->description);
677 outputHandles.erase(handleState->description);
679 if (hlInfo.orientation)
681 handleState->x=sourceHandleState.x;
682 handleState->y=sourceHandleState.y;
683 handleState->collapsed=sourceHandleState.collapsed;
684 handleState->reductionOp=sourceHandleState.reductionOp;
688 handleState->displayFilterCaliper=sourceHandleState.displayFilterCaliper;
689 handleState->minLabel=sourceHandleState.minLabel;
690 handleState->maxLabel=sourceHandleState.maxLabel;
694 handleState->order=sourceHandleState.order;
695 handleState->customOrder=sourceHandleState.customOrder;
700 state.outputHandles.clear();
701 for (
auto& i: sourceState.outputHandles)
703 auto o=outputHandles.find(i);
704 if (o!=outputHandles.end())
706 state.outputHandles.push_back(i);
707 outputHandles.erase(o);
711 state.outputHandles.insert(state.outputHandles.end(), outputHandles.begin(), outputHandles.end());
712 r->applyState(state);
718 vector<set<string>> checkHandleNames(
m_ravels.size());
721 if (hl.handleNames.size()!=
m_ravels.size())
722 throw runtime_error(
"Insufficient data on line");
723 for (
size_t i=0; i<hl.handleNames.size(); ++i)
725 auto& nm=hl.handleNames[i];
727 if (find_if(nm.begin(), nm.end(), [](
unsigned char i){
return !isspace(i)&&i!=0xa0;})==nm.end())
729 if (!checkHandleNames[i].insert(nm).second)
730 throw runtime_error(
"duplicate handle name "+nm);
740 if (
auto r=rr.lock())
742 auto state=r->getState();
743 for (
auto& h: state.handleStates)
744 handles.insert(h.description);
746 return {handles.begin(), handles.end()};
751 std::vector<std::string> r;
754 if (
auto rr=i.lock())
755 r.emplace_back(rr->tooltip().empty()? to_string(cnt++): rr->tooltip());
757 r.emplace_back(
"<invalid>");
763 if (
auto rr=
m_ravels[ravel_idx].lock())
764 return rr->handleNames();
773 if (
auto r=rp.lock())
775 auto names=r->handleNames();
776 handleNames.emplace_back(names.begin(), names.end());
781 for (
auto& h: handles)
784 for (
size_t i=0; i<
m_ravels.size(); ++i)
793 i.handleNames.resize(
m_ravels.size());
799 auto ravelIdx=&ravel-
m_ravels.data();
801 if (ravelIdx<0 ||
size_t(ravelIdx)>=
m_ravels.size())
return;
802 if (
auto r=ravel.lock())
804 auto names=r->handleNames();
805 set<string>
handleNames(names.begin(), names.end());
807 r->throw_error(
"Ambiguous handle names");
811 assert(hli.handleNames.size()==
m_ravels.size());
812 const set<string> lockNames(hli.handleNames.begin(), hli.handleNames.end());
813 for (
auto& l: lockNames)
818 hli.handleNames[ravelIdx]=l;
836 [&](
const weak_ptr<Ravel>& i){
838 return r.get()==&ravel;
843 i.handleNames.erase(i.handleNames.begin()+(found-
m_ravels.begin()));
847 r->lockGroup.reset();
852 if (!surface.get())
return false;
854 const ecolab::cairo::CairoSave cs(surface->cairo());
855 cairo_translate(surface->cairo(),0.5*
width,0.5*
height);
858 cairo_scale(surface->cairo(), z,z);
#define M_PI
some useful geometry types, defined from boost::geometry
void collapseAllHandles(bool collapse=true)
collapse all handles (applying nextReduction op where appropriate)
std::vector< std::string > handleNames(size_t ravel_idx) const
return the handle descriptions of of ravel ravel_idx in ravels
int selectedHandle() const
current handle mouse is over, or -1 if none
void setDescription(const std::string &)
void addRavel(const std::weak_ptr< Ravel > &ravel)
void pickSliceLabels(int axis, const std::vector< std::string > &pick)
pick (selected) pick labels
void setLockHandles(const std::vector< std::string > &handles)
set handlesToLock to the handles in handles
void exportAsCSV(const std::string &filename, const std::string &comment="", bool tabular=false) const
export this to a CSV file. If comment is non-empty, it is written as the first line of the file...
void onMouseDown(float x, float y) override
respond to mouse down events
void drawPorts(cairo_t *cairo) const
std::vector< std::weak_ptr< Ravel > > m_ravels
bool handleSortableByValue() const
const Hypercube & hypercube() const override
bool inItem(float x, float y) const override
ravel::RavelState getState() const
get the current state of the Ravel
virtual void displayTooltip(cairo_t *, const std::string &) const
display tooltip text, eg on mouseover
std::vector< std::string > dimensions() const
return dimension names of tensor object attached to input if binary op, then the union of dimension n...
static std::vector< double, CIVITA_ALLOCATOR< double > > stockVars
vector of variables that are integrated via Runge-Kutta. These variables label the columns of the God...
void setDimension(Dimension::Type type, const std::string &units)
shared_ptr< EvalCommon > ev
void adjustSlicer(int)
adjust currently selected handle's slicer
void onMouseUp(float x, float y) override
respond to mouse up events
void applyState(const ravel::RavelState &state)
apply the state to the Ravel, leaving data, slicelabels etc unchanged
represents rectangular region of a lasso operation
void broadcastStateToLockGroup() const
std::vector< std::string > allSliceLabels() const
returns all slice labels along the selected handle, in specified order
ravel::HandleSort::Order HandleSort
static SVGRenderer svgRenderer
SVG icon to display when not in editor mode.
std::shared_ptr< Item > ItemPtr
ravel::RavelState initState
used entirely to defer persisted state data until after first load from a variable ...
void addHandleInfo(const std::weak_ptr< Ravel > &ravel)
add ravel's handles to handleLockInfo, for a ravel stashed in m_ravels
ravel::HandleSort::Order setSortOrder(ravel::HandleSort::Order)
the handle sorting order for currently selected handle
Dimension dimension(int handle) const
dimension details associated with handle
std::vector< std::string > pickedSliceLabels() const
returns just the picked slice labels along the handle
void sortByValue(ravel::HandleSort::Order dir)
Sort handle by value. Only applicable for rank 1 ravels.
Hypercube hypercube() const
return hypercube corresponding to the current Ravel state
bool onMouseMotion(float x, float y) override
respond to mouse motion events with button pressed
BoundingBox bb
canvas bounding box.
virtual std::string const & tooltip() const
std::shared_ptr< ITensor > create(const ItemPtr &, const TensorsFromPort &tp={})
create a tensor representation of the expression rooted at op. If expression doesn't contain any refe...
ravel::HandleSort::Order sortOrder() const
the handle sorting order for currently selected handle
virtual float zoomFactor() const
void update(const Item &x)
void broadcast(const Ravel &ravel)
broadcast state from ravel to the lock group
void removeFromGroup(const Ravel &)
void joinLockGroup(unsigned)
Dimension::Type dimensionType() const
get/set dimension attributes of selected handle, or handle at given index
static void drawSelected(cairo_t *cairo)
std::vector< ItemPtr > Items
bool onKeyPress(int, const std::string &, int) override
respond to key press events
bool m_editorMode
indicate whether icon is in editor mode or icon mode
bool displayFilterCaliper() const
enable/disable calipers on currently selected handle
Type type() const override
std::string description() const
represents the units (in sense of dimensional analysis) of a variable.
ItemPtr itemPtrFromThis() const
return a shared_ptr to this
std::vector< std::string > allSliceLabelsAxis(int axis) const
returns all slice labels along an axis(dimension) identified by its number
std::vector< std::string > allLockHandles() const
populate handlesToLock by all handles present in the lock group
bool onBorder
true to indicate mouse hovering over border
bool setDisplayFilterCaliper(bool x)
const Minsky & cminsky()
const version to help in const correctness
std::vector< std::string > ravelNames() const
return tooltips of the ravels in this lockGroup
void validateLockHandleInfo()
checks handleLockInfo for non repeated handles, etc
std::string handleDescription(int handle) const
return the description field for handle handle.
std::shared_ptr< RavelLockGroup > lockGroup
group of ravels that move syncronously
void draw(cairo_t *cairo) const override
draw this item into a cairo context
std::string axis
axis selector in tensor operations
bool selected
true if selected for cut, copy or group operation
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::Ravel)
ravel::HandleSort::Order setHandleSortOrder(ravel::HandleSort::Order, int handle)
set a given handle sort order
static std::vector< unsigned > lockGroupColours()
void displayDelayedTooltip(float x, float y) override
enable extended tooltip help message appropriate for mouse at (x,y)
static std::vector< double, CIVITA_ALLOCATOR< double > > flowVars
variables defined as a simple function of the stock variables, also known as lhs variables. These variables appear in the body of the Godley table
size_t numSliceLabels(size_t axis) const
number of slice labels along axis axis
ravel::Op::ReductionOp m_nextReduction
std::vector< HandleLockInfo > handleLockInfo
bool mouseFocus
true if target of a mouseover
TensorOpFactory tensorOpFactory
void drawTriangle(cairo_t *cairo, double x, double y, const ecolab::cairo::Colour &col, double angle=0)
void moveTo(float x, float y)
void updateBoundingBox() override
void initialBroadcast()
broadcast first ravel's state to the remainder
void populateHypercube(const Hypercube &)
void exportAsCSV(const std::string &filename, bool tabular) const
export the plotted data as a CSV file
Minsky & minsky()
global minsky object
void resize(const LassoBox &) override
resize this item on the canvas
ravel::Op::ReductionOp ReductionOp
Dimensions axisDimensions
local override of axis dimensionality
Units units(bool) const override
compute the dimensional units
unsigned numHandles() const
void drawResizeHandles(cairo_t *cairo) const override
void resortHandleIfDynamic()
bool onMouseOver(float x, float y) override
respond to mouse motion events (hover) without button pressed
void setRank(unsigned r)
adjust output dimensions to first r handles
std::string dimensionUnitsFormat() const
get/set dimension attributes of selected handle, or handle at given index
void render(cairo_t *, double width, double height) const
render SVG into region of size width height
virtual std::string const & detailedText() const
ravelCAPI::Ravel wrappedRavel