|
Hi Paolo,
you're right. The behaviour of the sum-, integral-, and the productnode is not as it should be. As I mentioned in the article the implemenation of the node-entities is just an example ...
To fix that wrong behaviour you just have to override the method
virtual CNode* GetNodeFromPoint(CDC* pDC, CRect& rect, CPoint point); in the class-declaration of CSumNode (and also the CProdNode and CIntegralNode )
The implementation of the overridden methods looks like this:
CNode* CSumNode::GetNodeFromPoint(CDC* pDC, CRect& rect, CPoint point)
{
CRect contentRect(0,0,0,0);
CRect leftRect(0,0,0,0);
CRect rightRect(0,0,0,0);
CNode* pNode=NULL;
if(m_pLeftChild)
leftRect=m_pLeftChild->GetRect(pDC);
contentRect=GetContentRect(pDC);
if(m_pRightChild)
rightRect=m_pRightChild->GetRect(pDC);
TransformRects(rect, leftRect, rightRect, contentRect);
if(rightRect.PtInRect(point))
if(m_pRightChild)
{
pNode=m_pRightChild->GetNodeFromPoint(pDC, rightRect, point);
rect=rightRect;
return pNode;
}
if(contentRect.PtInRect(point))
{
rect=contentRect;
return this;
}
if(leftRect.PtInRect(point))
if(m_pLeftChild)
{
pNode=m_pLeftChild->GetNodeFromPoint(pDC, leftRect, point);
rect=leftRect;
return pNode;
}
return this;
}
The only difference is tree-traversal-algorithm. It has changed form postorder to inorder. Thats it...
thank you for the hint...
tbw
|
|
|
|
|
I applied the changes and now it works great!
The sum sign is still detected when cursor is below the argument, but the important thing is it gets detected when the cursor is over it.
So what about and intermediate class that just implements the different tree-traversal algorithm? CNodeInOrder or something like that...
Since you could have many operators that require that change, this could save many code repetitions.
Paolo
------
Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
|
|
|
|
Hi Paolo,
I thought about your suggestion, and when I looked into the code, I realized that CRangeNode is the evil guy, who makes the trouble. Therefore I think it's better to fix the CRangeNode .
Modify the original GetNodeFromPoint method in the following manner.
// return the node of a given point (mouse coordinate)
CNode* CNode::GetNodeFromPoint(CDC* pDC, CRect& rect, CPoint point)
{
CRect contentRect(0,0,0,0);
CRect leftRect(0,0,0,0);
CRect rightRect(0,0,0,0);
CNode* pNode=NULL;
if(m_pLeftChild)
leftRect=m_pLeftChild->GetRect(pDC);
contentRect=GetContentRect(pDC);
if(m_pRightChild)
rightRect=m_pRightChild->GetRect(pDC);
TransformRects(rect, leftRect, rightRect, contentRect);
if(leftRect.PtInRect(point))
if(m_pLeftChild)
{
pNode=m_pLeftChild->GetNodeFromPoint(pDC, leftRect, point);
rect=leftRect;
if(pNode)
return pNode;
}
if(rightRect.PtInRect(point))
if(m_pRightChild)
{
pNode=m_pRightChild->GetNodeFromPoint(pDC, rightRect, point);
rect=rightRect;
if(pNode)
return pNode;
}
if(contentRect.PtInRect(point))
{
rect=contentRect;
return this;
}
return this;
}
the only difference are the conditions if(pNode) before the return pNode; lines.
In the next step override the GetNodeFromPoint only for the CRangeNode in the following way:
CNode* CRangeNode::GetNodeFromPoint(CDC* pDC, CRect& rect, CPoint point)
{
CRect contentRect(0,0,0,0);
CRect leftRect(0,0,0,0);
CRect rightRect(0,0,0,0);
CNode* pNode=NULL;
if(m_pLeftChild)
leftRect=m_pLeftChild->GetRect(pDC);
contentRect=GetContentRect(pDC);
if(m_pRightChild)
rightRect=m_pRightChild->GetRect(pDC);
TransformRects(rect, leftRect, rightRect, contentRect);
if(rightRect.PtInRect(point))
if(m_pRightChild)
{
pNode=m_pRightChild->GetNodeFromPoint(pDC, rightRect, point);
rect=rightRect;
return pNode;
}
if(leftRect.PtInRect(point))
if(m_pLeftChild)
{
pNode=m_pLeftChild->GetNodeFromPoint(pDC, leftRect, point);
rect=leftRect;
return pNode;
}
return NULL;
}
The modified lines are commented. This solution should work for all nodes that work with CRangeNode as a child and leaves the class-hierarchy untouched....
tbw
|
|
|
|
|
I was thinking of doing this a while ago, but I'm not really a graphics person. I'd get bogged down in trying to make it look ok
Ryan
Being little and getting pushed around by big guys all my life I guess I compensate by pushing electrons and holes around. What a bully I am, but I do enjoy making subatomic particles hop at my bidding - Roger Wright (2nd April 2003, The Lounge)
Punctuality is only a virtue for those who aren't smart enough to think of good excuses for being late - John Nichol "Point Of Impact"
|
|
|
|
|
|
few suggestions:
- make editing more intuitive, this is a big plus
- have exporting as vector graphics (WMF maybe?)
- have zooming, etc.
- customizable space between various expressions (e.g. x^2.. the 2 is too far away from the x)
- multiple binary relations in one equation (for example: if x = y then a = b... x = y --> a = b)
i was planning on making a revolutionary thing like this... seems like i wont need to anymore
good job!
Roman Nurik
|
|
|
|
|
Hi Roman,
thanx for your suggestions,
to export into WMF just add the following method into CBinTree :
BOOL CBinTree::WriteWMF(CString strFile)
{
CDC DC;
CDC* pDC = NULL;
CMetaFileDC MetaDC;
HENHMETAFILE hEnhMetaFile;
DC.CreateCompatibleDC(NULL);
CRect rectMeta(0,0,0,0);
CRect rect=GetRect(&DC);
rectMeta.right = MulDiv(rect.Width()*100, DC.GetDeviceCaps(HORZSIZE),
DC.GetDeviceCaps(HORZRES));
rectMeta.bottom = MulDiv(rect.Height()*100, DC.GetDeviceCaps(VERTSIZE),
DC.GetDeviceCaps(VERTRES));
MetaDC.CreateEnhanced(&DC, NULL, rectMeta, _T("Formula\0Image\0\0"));
MetaDC.SetAttribDC(DC.m_hDC);
MetaDC.SetMapMode(MM_TEXT);
pDC = &MetaDC;
CBrush brush;
brush.CreateSysColorBrush(COLOR_WINDOW);
pDC->FillRect(rect, &brush);
brush.DeleteObject();
DrawTree(pDC, rect);
hEnhMetaFile=MetaDC.CloseEnhanced();
BOOL bRet = FALSE;
HENHMETAFILE hMetaCopy = ::CopyEnhMetaFile(hEnhMetaFile, strFile);
if (hMetaCopy)
{
bRet = TRUE;
DeleteEnhMetaFile(hMetaCopy);
}
DeleteEnhMetaFile(hEnhMetaFile);
return bRet;
}
Then you can choose one of the export-methods. I will collect the suggestions and put them into an update soon...
tbw
|
|
|
|
|
However, after reading WREY's post I need a drink. Chaulk up another 5.
Gary Kirkham
A working Program is one that has only unobserved bugs
I thought I wanted a career, turns out I just wanted paychecks
|
|
|
|
|
First off, very nice. Second, thank you for donating it to the community; obviously, you put a lot of effort into it. It will make my engineering calculator much, much better looking..
And finally, a criticism: Even though this is a UI control, it might have made sense to create abstractions of operators and operands. The first thing I wanted to do with my snazzy function was evaluate it. I was dissapointed to find no good place to hook-in evaluation code. Are you familiar with Ian Ollmann's Parser/Evaluator?
|
|
|
|
|
Something like this is what Einstein was referring to when he said, "Credibility lies in simplicity." (To which someone added, "And elegance too," which prompted Einstein to reply in his usual witty and jovial manner, "Elegance are for tailors.")
Of course Einstein was too modest a person to reveal how complicated the "simplicity" of his work really was.
Such is the appearance of this person's work when you run the sample, and likewise when you look at his code: It is elegant, yet looks so simple. However, upon closer examination you will find it is anything but "simple". Still, you can't help from noticing how clean his coding technique is, and how concise is the logic behind his work. Yes, there are areas of complication in it also, but that is expected when you are writing any editor (moreso an editor of this kind).
Something else to observe: The seamless use of other people's work (they being other CodeProject members), brought together in one cohesive form presenting itself as a single work, underscoring another thread in this elegant tapestry of "simplicity".
On a different wavelength, let me say what this person has done, is just plain hard work, and much has to be said for him to be sharing it with us!! A big "thank you" is very much in order!!
Well done!! and looking forward for more like this in the future!!
William
Fortes in fide et opere!
|
|
|
|
|
why don't import/export it in MathML? It's a definitive standard nowadays
great job, anyway!
is this a sig?
|
|
|
|
|
you're definitly right. Currently the constructors of the nodeentities set the CString members m_strCodeFormat and m_strKeyWord statically. It's the same for typed nodes with the CStringArrays m_straKeyWords and m_straCodeFormats .
In general this initialization should be dynamic for example via .ini-File. However, by incrementing LT_COUNT in the node.h, introducing the new #define LT_MATHML 1 and setting up the mentioned member-variables the export-code should work without modifying the algorithm. Import is a little more tricky, I think (import-method working with XML-Parser)
tbw
|
|
|
|
|
I can know do my math homework on the computer and just print out equations (yes I realize it is probably more time consuming this way but its more fun).
-BestSnowman
|
|
|
|
|
so you did not know VTI (Virtual TI) from rusty wagner ...?
it is an excelent emulator of all the recent Texas-Instruments calculators, and especialy works very well for TI-89/92/92+...
TOXCCT >>> GEII power [toxcct][VisualCalc]
|
|
|
|
|
|
5/5 well done, you've definitely hit a niche article section.
To iterate is human, to recurse is devine.
|
|
|
|
|
...I was just the other night thinking of a project to waste some time on, and an idea like this came up, since I'm currently reading maths. Now I can waste time on something else!
|
|
|
|
|
I was also thinking of doing it .. I thought it'd make a good second CP article, but I held off cause I don't know a huge amount of maths and I thought I'd add loads of flaws...
So hats off to the creator of the excelent control!!!
*¨¨`)
¸¸.·´ ¸.·*¨¨`)
(¸¸.·* ¸ .·*
¸¸.·*
(¸¸.~~> Joel Holdsworth.
|
|
|
|
|
Holy cow... Cool sig...dude..
Don't and drive.
|
|
|
|
|
Whoa! That's one fricking cool sig... I give it a 5!
Hawaian shirts and shorts work too in Summer.
People assume you're either a complete nut (in which case not a worthy target) or so damn good you don't need to worry about camouflage...
-Anna-Jayne Metcalfe on Paintballing
|
|
|
|
|
Only 1 thing... I found it a bit difficult to edit by keyboard. Why don't you allow to edit directly by kb when the node is still undefined (and then set it as a variable or const, ...)?
The work is really impressive, congratulations!
|
|
|
|
|
Good idea, just replace the following lines in the OnKeyDown -method in formulactrl.cpp (line 755)
if (isalnum(nChar))
{
CNode* pNode=m_pBinTree->GetSelectedNode();
if(pNode)
{
if(pNode->GetEditMode() & NE_ALLOWEDIT)
....
with
if (isalnum(nChar))
{
CNode* pNode=m_pBinTree->GetSelectedNode();
if(pNode)
{
if(pNode->GetNodeType()==NT_PLACEHOLDER)
{
pNode=GetBinTree()->ReplaceNode(pNode, NT_VARIABLE);
GetBinTree()->ReformatNodes();
SendMessage(WM_PAINT);
}
if(pNode->GetEditMode() & NE_ALLOWEDIT)
....
Switching the nodetype from var to const could be realized by storing the relevant data before reaching the line
pNewNode=GetBinTree()->ReplaceNode(pNode, NT_CONSTANT);
in FormulaDlg_Toolbar.cpp (line 125) (the same for switching from const to var in line 129)
I will include this in the next update...
tbw
|
|
|
|
|
Hi John,
the calculation the rectangles need a device-context to satisfiy the GetOutputTextExtent -funtion. One solution could be the use of a MemDC in the following manner:
CDC MemDC;
MemDC.CreateCompatibleDC(NULL);
CRect rect=GetBinTree()->GetRect(&MemDC);
GetBinTree()->DrawTree(&MemDC, rect);
After these lines the rectangle of each node should be adjusted.
tbw
|
|
|
|
|
Wonderful article. Thank you for sharing.
|
|
|
|
|
... this is an absolutely outstanding article.
Congrats!
|
|
|
|
|