B4J Library [B4X]A cross-platform B4XPages class to display a network of nodes connected by arrows.

'WiLNetChart' features:

A. There are three different ways of specifying the net chart.

1. Using the internal designer plus extensions to define nodes and connections.

TabletLandscape_FromLayout.png

2. Specifying only connections and letting the algorithm define the nodes (works if all nodes have one parent)
Desktop-FromConnections.png

3. A string that specifies the nodes and connectors
Desktop-fromNodeSPecs.png




B. You can redefine the default styles of nodes and connectors, they can also be individually styled.

C. You can now use a prefix "RT\" in the node text to specify rich text which uses a BBCodeView instead of a label.

D. There is a callback to the calling context as a response to touches on the chart.
In the demo the style of nodes and arrows are changed by MainPage when they are touched.

E. This version will also work on B4A. Platform specific code is for event handler, font, and scroll pane.
All development was done with B4J.

F. I was tempted to add an editor, but I think it is unnecessary as the definition of the diagram is easily changed in code.
However, the class could be used as a basis for a complete App that allows the end user to change the design interactively.
Feel free to use the class any way you want.

G. There are a few features that are a result of my experimentation with screen-adaptable font size and screen orientation.
You may be interested in examining the code in the demo and the class. Remove them if you don't like my solution.

H. The 'WiLNetChart' class is dependent on the 'Arrow' class Here
The class is included in the demo below.

Even with all these features, the class size is only about 450 lines of code.
I keep being impressed by the power and functionality of B4X.
 

Attachments

  • WilNetChart.zip
    25.5 KB · Views: 98

kimstudio

Active Member
Licensed User
Longtime User
Hi William, any plan on making a flowchart kind of app with authoring/editing functionality?

I am making a modular system now that signals flow different processing modules via their connections, and I found the design and logic is a little bit chanllenging as the modules and connections need to be managed for adding, deleting, dragging, etc. Currently I am using views for each module and draw connection lines on canvas.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
I don't plan to make an app, but you have two options.
1.
Use the internal designer to place the nodes on the stage as labels.
They can have a name, text (both regular and rich text - see demo)
The connections are specified in the designer script.

B4X:
DDD.CollectViewsData
WiLNetChart.ConnectNodes(program, node, connector, touch)
WiLNetChart.ConnectNodes(node, element1)
WiLNetChart.ConnectNodes(connector, arrow1)
WiLNetChart.ConnectNodes(touch, bitmap)

2. Use the call back functions to drag the elements to the desired place.
This option requires some extra code, but it is doable.
If you decide to do this and have problems, let me know. I have quite a bit of experience with this.

The class in #1 uses a drawing panel with a canvas to draw the arrows, and a panel which contains the nodes.
The drawing panel is underneath the node panel so that the arrows can be offset a tiny bit for aesthetics (the node hides the origin point).
On top of the two layers is a touch layer which handles all touches. This layer has a BitmapCreator mirror that keeps track of
which elements are where. The indices are encoded in the color codes of the Bitmap.
For how that is done, see https://www.b4x.com/android/forum/t...scellaneous-image-shapes-into-buttons.140309/
 

William Lancee

Well-Known Member
Licensed User
Longtime User
The nodes are now draggable, the arrows will follow automatically. The zip in #1 is updated.

B4X:
'Set:    netDia.draggable = True

'If you want to preserve the new positions, you have to create a framework for saving and restoring the node specs.
'They are accessible through: Dim part As NetPart = NetDia.register.Get(partName)
'The panel that holds the node view is retrieved with NetDia.nodeRefs(partName)

'This callback function can be used to trigger the saving process
Public Sub netNodeMoved(partName As String, coords() As Object)
    Log(partName & " has moved to " & TAB & coords(0) & TAB & coords(1))
End Sub

dragging.gif
 
Last edited:

kimstudio

Active Member
Licensed User
Longtime User
If you decide to do this and have problems, let me know.
Thanks and thank in advance William.

On top of the two layers is a touch layer which handles all touches. This layer has a BitmapCreator mirror that keeps track of which elements are where.
I am using module view's own event to process dragging, however I do see the advantage of using another top layer, for example user may use mouse to select a rectangle area and draw some faint color in the area on top of nodes to select multiple nodes then delete them or change some properties.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
Yes. And how else would you select the connections between views? I draw a thick line in the mirroring bitmap.
 

kimstudio

Active Member
Licensed User
Longtime User
The connectine line is thin so select the line by clicking on it will be hard. I am thinking of re-click the nodes on the both sides of the line to remove it. I was thinking of make the connection line a node, then it will be easy to select by clicking the rectangle node contains the line.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
Take a look at the code in #1. You'll see that the canvas where the arrows are drawn has a BC (BitmapCreator) partner.
When an arrow is drawn, a corresponding thick line (10dip) is drawn by BC.
BC draws on its memory like Canvas does on a panel view. You don't see it because it is only in the memory of BC.

The thick line is drawn by BC with a color integer that is the index of the arrow object.
When the uppermost touch layer is touched, the coordinates are used to query BC for its pixel's color.
Which is the arrow's index. The result is that the arrow has a 10dip wide sensitivity field.

Any shape can be detected with this technique. See the link in #3

Download the latest version from #1 and check out the dragging code. You may be interested in the code that changes nodes and connectors.
Since the register is a collection (Map) of network objects (custom type NetPart),
node and arrows can be deleted/added by removing/adding them from register and redrawing the chart.

This explanation is longer than the actual code. But, since there has been quite a bit of interest in this project,
I will use this thread to explain some of the more esoteric techniques used.

I like looking at code. I can see quickly how it works (most of the time).
However, my code is sparsely commented (but hopefully not cryptic).
So please ask questions.
 

kimstudio

Active Member
Licensed User
Longtime User
Thanks for the detailed explanation! very clear to me for using the memory BC to store hidden information and interact.
For this kind of app the design before programming is quite important as to realize the thinking into code is relatively easy...

To test your app I just updated to B4J 9.8 finally due to designerutils is used, and may have found a bug?

BTW: seems B4J 9.30 and 9.80 work together happily.

屏幕截图 2022-09-24 230505.png
 

kimstudio

Active Member
Licensed User
Longtime User
William, I found another issue that could be caused by BC, which is when dragging one node over another node, the BC memory will be erased so the node under will not be able to be dragged again.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
Thanks for testing.

#9 happens when you drag above the Root node. I missed that since I didn't test that. I'll look at the code.
#10 I didn't see that either. It is lunch time here, so I'll get back at you in a couple of hours.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
#9 was a bug where I had y1 and y2 reversed in the CreateArrow routine.
#10 was a bug where I should have redrawn all nodes on the BC, not just the one being moved.
Both were easy to fix. The update is in #1.

@kimstudio if you downloaded in last 5 minutes, please do it again. I didn't press Save in the edit of post #1.
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
To see the effect of clicking on a node or connector, see demo2.zip
Turn off draggable: netDia.draggable = False
It uses the callback to toggle the style of the element.
 

Attachments

  • Demo2.zip
    25.5 KB · Views: 137

walt61

Active Member
Licensed User
Longtime User
Hi @William Lancee , that's awesome and just what I needed, thanks a bunch! I think I have found (and solved) a bug in the fromConnections method, namely with this statement:
B4X:
columnNum = pCN + indices.Get(i) - Floor(siblings.Get(i) / 2)

I've added a line (it seems to do the trick) right after that one, with:
B4X:
If (pCN = 0) And (siblings.Get(i).As(Int) > 1) Then columnNum = columnNum + 1

Explanation:
Without this addition, if the parent column number (pCN) is zero and there is more than one child,
- the first child's column number would be: 0 + 0 - Floor(2 / 2) = -1 <<< Causes exception in line "Dim rx As B4XRect = matrix(columnNum, row)"
- the second child's column number would be: 0 + 1 - Floor(2 / 2) = 0
WITH this addition, this becomes:
- the first child's column number is: 0 + 0 - Floor(2 / 2) + 1 = 0
- the second child's column number is: 0 + 1 - Floor(2 / 2) + 1 = 1

By all means kick me if I'm wrong :)
 
Top