B4J Question Cannot recreate B4XRect after put it inside a List [SOLVED]

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

I've this piece of code where there is a Select Case.

I pass to the sub a List of arguments as objects, the first argument Args.Get(0) is a B4XRect.
The problem here is that I cannot recreate a B4XRect passed as Object and the error occours
on the line Dim left As Float = rect.Left
B4X:
 Case "DrawRoundRectRotated"   ' DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
            Dim path As B4XPath
            Dim rect As B4XRect = Args.Get(0).As(B4XRect)
            Dim left As Float = rect.Left
            Dim top As Float = rect.Top
            Dim right As Float = rect.Right
            Dim bottom As Float = rect.Bottom
            rect.Initialize(left, top, right, bottom)
            path.InitializeRoundedRect(rect, Args.Get(4))
            cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(5), rect.CenterX, rect.CenterY) ' 0 degrees

On this line in Debug mode I can see the passed rectangle dimensions, but may it is transformed to string when iI put in the list ?
B4X:
Dim rect As B4XRect = Args.Get(0).As(B4XRect)

I construct the list this way, CM is a command and arguments separator, I cannot use just a single character like a comma
because need to be a pattern because I use some commands that accept strings and the split command confuse it.
Note that separator is not a problem, I've used it in other commands and in other projects and it works.
B4X:
Private Sub Class_Globals
    Private fx As JFX
    Private xui As XUI

    Private Const CM As String = "-,-" ' Separator
    Private DrawList As List    ' List of all drawings
    Private ArgList As List  ' List of all arguments

    Private mAutoInvalidate As Boolean = False
End Sub

'Similar to DrawRoundRect. Draw a rectangle with round corners and rotated by Degrees angle.
Sub DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    ArgList.Initialize2(Array(Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
    InvalidateCommand("DrawRoundRectRotated", ArgList)
End Sub

Private Sub InvalidateCommand(cmd As String, Args As List)
'    Try
        If mAutoInvalidate Then
            ParseCommand(cmd, Args)
        Else
            Dim oCmd As Object = cmd
            For Each o As Object In Args
                oCmd = oCmd & CM & o
            Next
            DrawList.Add(oCmd)
        End If
'    Catch
'        Log("ERROR: " & LastException)
'    End Try
End Sub

Here is how B4XRect looks inside the Args list:
(200.0, 200.0, 350.0, 350.0)

Attached the error log.

Thanks

Waiting for debugger to connect...
Program started.
600
600
STARTED
Resize
Error occurred on line: 208 (AsyncCanvas)
java.lang.RuntimeException: Method: getLeft not found in: java.lang.String
at anywheresoftware.b4a.shell.Shell$MethodCache.getMethod(Shell.java:891)
at anywheresoftware.b4a.shell.Shell.getMethod(Shell.java:539)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:628)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:234)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
at anywheresoftware.b4a.objects.Timer$TickTack$1.run(Timer.java:135)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:834)
 
Last edited:

Daestrum

Expert
Licensed User
Longtime User
I would use a string. ( but that doesn't mean it's the best way ) just personal preference.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
This works ?

I will continue to use String as your advices instead of costants, so to not break regular syntax and I think this is a best option for now, personally I like true costants like LEFT, CENTER, RIGHT, but this break normal B4X Canvas syntax.
B4X:
            Dim tAlign As JavaObject
            Dim alignString As String = Args.Get(6).As(String).toUpperCase
            tAlign.InitializeStatic("javafx.scene.text.TextAlignment")
            cvs.DrawText(Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5), tAlign.GetField(alignString))
Now I will going to test all other functions now I commented out, I start to adapt B4XRect and B4XPath.
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I can draw lines, rectangles, circles, texts and more, but any method that use B4XPath fails compilation with this error:
B4J Versione: 9.80
Analisi del Codice. (0.02s)
Java Versione: 11
Building folders structure. (0.01s)
Compilazione del codice. (0.03s)
Compilazione del codice di layouts (0.00s)
Organizzazione Librerie. (0.00s)
Compilazione del codice Java prodotto. Error
B4J line: 94
CommitCommand(Array(\
src\b4j\example\asynccanvas2.java:463: error: cannot access Path2D
__ref._commitcommand /*String*/ (null,anywheresoftware.b4a.keywords.Common.ArrayToList(new Object[]{(Object)("DrawPath"),(Object)(_path.getObject()),(Object)(_color),(Object)(_filled),(Object)(_strokewidth)}));
^
class file for com.sun.javafx.geom.Path2D not found
1 error
Involved methods are:
B4X:
'Clips the drawings to a closed path.
Sub ClipPath (Path As B4XPath)
    CommitCommand(Array("ClipPath", Path))
End Sub

'Draws the given path.
Sub DrawPath (Path As B4XPath, Color As Int, Filled As Boolean, StrokeWidth As Float)
    CommitCommand(Array("DrawPath", Path, Color, Filled, StrokeWidth))
End Sub

'Similar to DrawPath. Rotates the path based on the degrees and center parameters.
Sub DrawPathRotated (Path As B4XPath, Color As Int, Filled As Boolean, StrokeWidth As Float, Degrees As Float, CenterX As Float, CenterY As Float)
    CommitCommand(Array("DrawPathRotated", Path, Color, Filled, StrokeWidth, Degrees, CenterX, CenterY))
End Sub

The error occours on the CommitCommand(Array(......)) line, if I uncomment these the project compiles, if I uncomment do not.
Note that now I completely removed the ArgList that just I used as temporary, I pass direcly arguments, there is only one list that is the DrawList that contain all collected commands while AutoInvalidate is False and draw them (one by one) when Invalidate is called.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Do you get the same error if you run this
B4X:
Dim testPath As JavaObject
testPath.InitializeStatic("com.sun.javafx.geom.Path2D")
log(testPath)

It should show something like
(Path2D) com.sun.javafx.geom.Path2D@6e9e5b55
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Do you get the same error if you run this
B4X:
Dim testPath As JavaObject
testPath.InitializeStatic("com.sun.javafx.geom.Path2D")
log(testPath)

It should show something like
(Path2D) com.sun.javafx.geom.Path2D@6e9e5b55
Great @Daestrum , but I don't know how apply it, B4XPath is a wrapper of Path2D or just a part of it ?

How to apply here ?
B4X:
'Clips the drawings to a closed path.
Sub ClipPath (Path As B4XPath)
    CommitCommand(Array("ClipPath", Path))
End Sub
Note that this is a first sub called by the Main, so nothing is manipulated, it accept B4XPath as argument, but then cannot cast it as a true Object and return error on CommitCommand(Array("ClipPath", Path)).

I implemented these functions, have you an idea on what other useful I can implement ?
B4X:
'Get the drawing counter.
'NOTE: GetDrawingCount, ClearDrawingCount, Invalidate, AutoInvalidate and any getter command will be excluded from count.
Sub GetDrawingCount As Long
    Return count
End Sub

'Clear the drawing counter.
Sub ClearDrawingCount
    count = 0
End Sub

'Clear the full canvas with the given color.
Sub Clear(Color As Int)
    ArgList.Initialize2(Array("Clear",Color))
    CommitCommand(ArgList)
End Sub

'This is a short hand of cvs.ClearRect(cvs.TargetRect)
Sub ClearTargetRect
    ClearRect(cvs.TargetRect)
End Sub

'Clears the given rectangle. Does not work with clipped paths.
Sub ClearRect (Rect As B4XRect)
    ArgList.Initialize2(Array("ClearRect", Rect))
    CommitCommand(ArgList)
End Sub

''Clips the drawings to a closed path.
'Sub ClipPath (Path As B4XPath)
'    CommitCommand(Array("ClipPath", Path))
'End Sub

'Draws the bitmat in the given destination.
'Use B4XBitmap.Crop To draw part of a bitmap.
Sub DrawBitmap (Bitmap As Image, Dest As B4XRect)
    CommitCommand(Array("DrawBitmap", Bitmap, Dest))
End Sub

'Similar to DrawBitmap. Draws a rotated bitmap.
Sub DrawBitmapRotated (Bitmap As Image, Dest As B4XRect, Degrees As Float)
    CommitCommand(Array("DrawBitmapRotated", Bitmap, Dest, Degrees))
End Sub

'Draws a circle.
Sub DrawCircle (x As Float, y As Float, Radius As Float, Color As Int, Filled As Boolean, StrokeWidth As Float)
    CommitCommand(Array("DrawCircle", x, y, Radius, Color, Filled, StrokeWidth))
End Sub

'Draws a pixel.
Sub DrawPixel (x As Float, y As Float, Color As Int)
    CommitCommand(Array("DrawPixel", x, y, Color))
End Sub

'Draws a line between x1,y1 and x2,y2.
Sub DrawLine (x1 As Float, y1 As Float, x2 As Float, y2 As Float, Color As Int, StrokeWidth As Float)
    CommitCommand(Array("DrawLine", x1, y1, x2, y2, Color, StrokeWidth))
End Sub

''Draws the given path.
'Sub DrawPath (Path As B4XPath, Color As Int, Filled As Boolean, StrokeWidth As Float)
'    CommitCommand(Array("DrawPath", Path, Color, Filled, StrokeWidth))
'End Sub
'
''Similar to DrawPath. Rotates the path based on the degrees and center parameters.
'Sub DrawPathRotated (Path As B4XPath, Color As Int, Filled As Boolean, StrokeWidth As Float, Degrees As Float, CenterX As Float, CenterY As Float)
'    CommitCommand(Array("DrawPathRotated", Path, Color, Filled, StrokeWidth, Degrees, CenterX, CenterY))
'End Sub

'Draws a rectangle.
Sub DrawRect (Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float)
    CommitCommand(Array("DrawRect", Rect, Color, Filled, StrokeWidth))
End Sub

'Similar to DrawRect. Draw a rotated rectangle.
Sub DrawRectRotated (Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, Degrees As Float)
    CommitCommand(Array("DrawRectRotated", Rect, Color, Filled, StrokeWidth, Degrees))
End Sub

'Draws a rectangle with round corners specified by CornerRadius.
Sub DrawRoundRect(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float)
    CommitCommand(Array("DrawRoundRect",Rect, Color, Filled, StrokeWidth, CornerRadius))
End Sub

'Similar to DrawRoundRect. Draw a rectangle with round corners and rotated by Degrees angle.
Sub DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    CommitCommand(Array("DrawRoundRectRotated",Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
End Sub

'Draws an oval.
Sub DrawOval(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float)
    CommitCommand(Array("DrawOval", Rect, Color, Filled, StrokeWidth))
End Sub

'Similar to DrawOval. Draw the oval rotated by Degrees angle.
Sub DrawOvalRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, Degrees As Float)
    CommitCommand(Array("DrawOvalRotated", Rect, Color, Filled, StrokeWidth, Degrees))
End Sub

'Draws the text at the given position. For alignment use one of following values "LEFT", "CENTER", "RIGHT".
Sub DrawText (Text As String, x As Double, y As Double, Font As B4XFont, Color As Int, Alignment As Object)
    CommitCommand(Array("DrawText", Text, x, y, Font, Color, Alignment))
End Sub

'Similar to DrawText. Rotates the text before it is drawn.
Sub DrawTextRotated (Text As String, x As Double, y As Double, Font As B4XFont, Color As Int, Alignment As Object, Degrees As Float)
    CommitCommand(Array("DrawTextRotated", Text, x, y, Font, Color, Alignment, Degrees))
End Sub

'-----------------------------------

'Sets or gets AutoInvalidate property.
'By default AutoInvalidate is False, you will need to call Invalidate to update the drawings.
'If you set AutoInvalidate to True, then the drawings will be auto updated every command.
Sub setAutoInvalidate(Val As Boolean)
'    CommitCommand("SetAutoInvalidate", Null)
   
'    If Args.Get(0) = "true" Then mAutoInvalidate = True Else mAutoInvalidate = False
    mAutoInvalidate = Val
End Sub
Sub getAutoInvalidate As Boolean
    Return mAutoInvalidate
End Sub

'Measures single line texts and returns their width, height and the height above the baseline.
'Rect.Top returns the height above the baseline.
Sub MeasureText(Text As String, Font As B4XFont) As B4XRect
    Return cvs.MeasureText(Text, Font)
End Sub

#If B4A Or B4i
Sub Release ' ONLY B4A and B4i
    cvs.Release   
End Sub
#End If

'Removes a previously set clip region.
Sub RemoveClip
    cvs.RemoveClip
End Sub

'Resizes the canvas.
Sub Resize (Width As Double, Height As Double)
    cvs.Resize(Width, Height)
    pnl.SetLayoutAnimated(0, 0, 0, Width, Height)
End Sub


' //////////////// GETTERS, SETTERS //////////////////

'Returns a copy of B4XBitmap.
Sub CreateBitmap As B4XBitmap
    Return cvs.CreateBitmap
End Sub

'Returns the native B4XCanvas.
Sub getCanvas As B4XCanvas
    Return cvs
End Sub

'Returns the native B4XCanvas.
Sub getPanel As B4XView
    Return pnl
End Sub

Sub TargetRect As B4XRect
    Return cvs.TargetRect
End Sub

Sub TargetView As B4XView
    Return cvs.TargetView
End Sub

'Gets or sets the canvas Width (You can use Resize instead to resize it).
Sub getWidth As Double
    Return pnl.Width
End Sub
Sub setWidth(Width As Double)
    pnl.Width = Width
End Sub

'Gets or sets the canvas Height (You can use Resize instead to resize it).
Sub getHeight As Double
    Return pnl.Height
End Sub
Sub setHeight(Height As Double)
    pnl.Height = Height
End Sub
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
the error occurs because the b4xPath is not named to allow access to the ...geom.Path2d

you need to have an --add-opens directive

just testing which one you need
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Sorry, I don't know you, what is an --add-opens directive ?

I even implemnted this method now that return B4XRect based on width and height.
B4X:
'Returns a B4XRect by passing position, width and height.
Sub CreateRect (Left As Float, Top As Float, Width As Float, Height As Float) As B4XRect
    Dim r As B4XRect
    r.Initialize(Left, Top, Left+Width, Top+Height)
    Return r
End Sub
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Something like that ?
#VirtualMachineArgs: --add-opens javafx.graphics/javafx.scene.text=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.text=ALL-UNNAMED
#PackagerProperty: VMArgs = --add-opens javafx.graphics/javafx.scene.text=b4j --add-exports javafx.graphics/com.sun.javafx.text=b4j
Sorry but I never used it.

I can put it on the class module ?
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
The problem is around B4XPath

Sorry - cant work out what opens you need - think this is one for Erel.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Ok - the problem is (as far as I have tested ) B4XPath cannot be converted to an Object

As soon as I try to assign to an java.lang.Object - the error appears (cant find Path2D class)
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Yes, same for me here....

@stevel05 have made a small wrapper, may I can use this inline ?
But if there is a way to use the B4XPath core it is better....

Here the link:

.... and here the code, may can help ?

Thanks

Code:
B4X:
Sub Class_Globals
    Private mPath As B4XPath
    Private mScaleVal As Double
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(tScaleVal As Double)
    mScaleVal = tScaleVal
End Sub

Public Sub InitializePoint(X As Double, Y As Double)
    mPath.Initialize(ScaledVal(X),ScaledVal(Y))
End Sub

Public Sub getPath As B4XPath
    Return mPath
End Sub

Public Sub InitializeArc( x As Float, Y As Float,Radius As Float, StartingAngle As Float, SweepAngle As Float)
    mPath.InitializeArc( ScaledVal(x), ScaledVal(Y) ,ScaledVal(Radius), StartingAngle, SweepAngle)
End Sub

Public Sub InitializeOval(R As B4XRect) As B4XPath
    Return mPath.InitializeOval(ScaleRect(R))
End Sub

Public Sub InitializeRoundedRect(R As B4XRect,CornersRadius As Float)
    mPath.InitializeRoundedRect(ScaleRect(R),CornersRadius)
End Sub

Public Sub LineTo( X As Float, Y As Float)
    mPath.LineTo(ScaledVal(X), ScaledVal(Y))
End Sub

Public Sub IsInitialized As Boolean
    Return mPath.IsInitialized
End Sub

Private Sub ScaledVal(Val As Double) As Double
    Return Val * mScaleVal
End Sub

Private Sub ScaleRect(R As B4XRect) As B4XRect
    Dim SR As B4XRect
    SR.Initialize(ScaledVal(R.Left),ScaledVal(R.Top),ScaledVal(R.right), ScaledVal(R.Bottom))
    Return SR
End Sub
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Ok I found a way to put the B4XPath in an array of Objects (without causing the error) and how to get it back - but you need to know its position in the array.

B4X:
Dim path As B4XPath ' define the path variable for test
    
path.Initialize(10,10) ' initialise it with anything

   ' add it to the arguments array as an array of B4XPath
    Dim d() As Object = Array As Object(1, 2, 3, Array As B4XPath(path), 4, 5, 6)

   ' reading the B4XPath array back

            Dim t() As B4XPath = d(3)     ' you must know its position
            Log( t(0) )      ' the B4XPath we really want in t(0)
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Great!

I still search to know your code.

So B4XPath is an array ?
B4X:
Array As B4XPath(path)
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
it's an array of 1 item which is the B4XPath, it seems wrapping it in an array avoids the error occurring.

Even using pure java I was unable to cast it to an Object - there is something 'strange' about it, probably because it's in com.sun. package. (possibly a sealed class)
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Note that you can omit Array As Object, just use Array.

I still search to know how implement it here:
B4X:
'Clips the drawings to a closed path.
Sub ClipPath (Path As B4XPath)
    CommitCommand(Array("ClipPath", Path))
End Sub

Private Sub CommitCommand(Args As List)
    Try
        If mAutoInvalidate Then
            ParseCommand(Args)
        Else
            DrawList.Add(Args)
        End If
    Catch
        Log("CommitCommand: ERROR: " & LastException)
    End Try
End Sub

Private Sub ParseCommand(Args As List)
   
    count = count + 1
'    Log("ParseCommand [" & count "]: " & cmd & "    ARGS: " & Args)

    Dim cmd As String = Args.Get(0) ' With LIST
'   Dim cmd As String = Args(0) ' With ARRAY
   
    Select cmd
        Case "ClipPath"
            cvs.ClipPath(Args.Get(0))          
        Case "DrawPath"
            cvs.DrawPath(Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4))
        Case "DrawPathRotated"
            cvs.DrawPathRotated(Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5), Args.Get(6), Args.Get(7))
    End Select
End Sub
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
B4X:
CommitCommand(Array("ClipPath", array as B4XPath(Path)))
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Ok, not different than mine:
B4X:
'Clips the drawings to a closed path.
Sub ClipPath (Path As B4XPath)
    Dim o As Object = Array As B4XPath(Path)
    CommitCommand(Array("ClipPath", o))
End Sub

        Case "ClipPath"
           ' Reading the B4XPath array back
            Dim t() As B4XPath = Args.Get(1)     '  You must know its position,  YES I KNOW IT IS STATIC, ON THIS CASE IS Args.Get(1)  because Args.Get(0)  is "ClipPath"
            Log( t(0) )      ' the B4XPath we really want in t(0)
            cvs.ClipPath(t(0))
This should work ?
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
just tried it - no error
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I will try it when return to PC. You are The Best ?
Many Thanks
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
@Daestrum, I confirm you that I adapted all 3 susb that use B4XPath and it compiles.
I will going to test some paths to ensure it works the right way.

I think thit is the last, if all works I pack it inside a b4xlib and post to the forum.
May I test it a bit before release..... so may I post it with some demo examples, note that this is a regular
B4XCanvas with addiction of some features, first of all AutoInvalidate can be switched off/on, if switched off to commit the drawings we need to call Invalidate, if it is switched on no need to invalidate, the canvas invalidate itself every drawing command, like as default on B4J JavaFX canvas.

I've added a dawing command counter
I've added some drawing methods:
DrawRoundRect
DrawRoundRectRotated
DrawOval
DrawOvalRotated

... and some small changes ....
 
Last edited:
Upvote 0
Top