Closed oheil closed 2 years ago
You're doing interesting things here... :)
What's ERROR: UndefVarError: CURRENTDRAWINGINDEX not defined
mean though? Is this to have multiple drawings on the go at once? Sounds cool...
You need to add my branch "multi_drawing" to make it work. First line in example.
Yes, I thought I was accessing that too...
Pkg.add(path="https://github.com/oheil/Luxor.jl#multi_drawing")
...
ERROR: UndefVarError: CURRENTDRAWINGINDEX not defined
Stacktrace:
[1] getproperty(x::Module, f::Symbol)
@ Base ./Base.jl:31
[2] top-level scope
@ Untitled-1:41
(@MiniFB) pkg> st
Status `~/.julia/environments/MiniFB/Project.toml`
[5ae59095] Colors v0.12.8
[ae8d54c2] Luxor v3.5.0 `https://github.com/oheil/Luxor.jl#multi_drawing#master`
[cc649173] MiniFB v0.1.1
(I get the two windows OK...)
The two windows are easy. The independent Luxor/Cairo surfaces are the thing ;-)
This looks strange: ...oheil/Luxor.jl#multi_drawing#master
I tried from scratch with the following, and it works (on windows):
(@v1.7) pkg> activate test
(test) pkg> add MiniFB, Colors
(test) pkg> add https://github.com/oheil/Luxor.jl#multi_drawing
(test) pkg> st
Status `C:\Users\Oli\test\Project.toml`
[5ae59095] Colors v0.12.8
[ae8d54c2] Luxor v3.5.0 `https://github.com/oheil/Luxor.jl#multi_drawing`
[cc649173] MiniFB v0.1.1
julia> using MiniFB, Luxor, Colors
...
and the rest of the code.
Here is a video of what is possible with that:
https://user-images.githubusercontent.com/5408557/179751811-a8cb434f-aa0b-449d-9d1e-d6e7e7332d0c.mp4
It's not a virtual larger surface splitted over two windows (but this can be created), it's just two independent surfaces on two windows and the code calculates the lines accordingly.
This is the code (needs above branch again):
using MiniFB, Luxor, Colors
WIDTH=800
HEIGHT=600
mutable struct MfbState
state::mfb_update_state
mousePressed::Int
end
stateArray=[
MfbState( MiniFB.STATE_OK, 0 ),
MfbState( MiniFB.STATE_OK, 0 ),
]
function windowUpdateTask(window,windowIndex,buffer,stateArray)
stateArray[windowIndex].state=mfb_update(window,buffer)
while stateArray[windowIndex].state == MiniFB.STATE_OK
stateArray[windowIndex].state=mfb_update(window,buffer)
sleep(1.0/120.0)
end
println("\nWindow closed\n")
end
window1 = mfb_open_ex("Left", WIDTH, HEIGHT, MiniFB.WF_RESIZABLE)
buffer1 = zeros(ARGB32, WIDTH, HEIGHT)
d1=Drawing(buffer1)
@async windowUpdateTask(window1,1,buffer1,stateArray)
window2 = mfb_open_ex("Right", WIDTH, HEIGHT, MiniFB.WF_RESIZABLE)
buffer2 = zeros(ARGB32, WIDTH, HEIGHT)
Luxor.CURRENTDRAWINGINDEX[1]=2
d2=Drawing(buffer2)
@async windowUpdateTask(window2,2,buffer2,stateArray)
mutable struct Ball
position::Point
velocity::Point
end
function stick(w, h)
channel = Channel(10)
@async while true
kb = readline(stdin)
if contains(kb, "q")
put!(channel, 1)
break
end
end
colors=[rand(1:255),rand(1:255),rand(1:255)]
newcolors=[rand(1:255),rand(1:255),rand(1:255)]
c=ARGB(colors[1]/255,colors[2]/255,colors[3]/255,1.0)
balls=[Ball( rand(BoundingBox(Point(-w/2, -h/2), Point(w/2, h/2))), rand(BoundingBox(Point(-10, -10), Point(10, 10))) ) for _ in 1:2]
while true
if colors == newcolors
newcolors=[rand(1:255),rand(1:255),rand(1:255)]
end
for (index,(col,newcol)) in enumerate(zip(colors,newcolors))
if col != newcol
col > newcol ? col-=1 : col+=1
colors[index]=col
end
end
c=ARGB(colors[1]/255,colors[2]/255,colors[3]/255,1.0)
for ball in balls
if !(-w/2 < ball.position.x < w/2)
ball.velocity = Point(-ball.velocity.x,ball.velocity.y)
end
if !(-h/2 < ball.position.y < h/2)
ball.velocity = Point(ball.velocity.x,-ball.velocity.y)
end
ball.position = ball.position + ball.velocity
end
Luxor.CURRENTDRAWINGINDEX[1]=1
background(0,0,0,0.05)
setcolor(c)
Luxor.CURRENTDRAWINGINDEX[1]=2
background(0,0,0,0.05)
setcolor(c)
pos1=balls[1].position
pos2=balls[2].position
pos3=pos2
if ( pos1.x < 0.0 && pos2.x > 0.0 ) || ( pos1.x > 0.0 && pos2.x < 0.0 )
m=(pos1.y-pos2.y)/(pos1.x-pos2.x)
posmx=0.0
posmy=pos1.y-m*pos1.x
pos3=Point(posmx,posmy)
end
if ( pos1.x < 0.0 && pos2.x > 0.0 )
Luxor.CURRENTDRAWINGINDEX[1]=1
line(Point(pos1.x+w/4,pos1.y),Point(pos3.x+w/4,pos3.y),:stroke)
Luxor.CURRENTDRAWINGINDEX[1]=2
line(Point(pos3.x-w/4,pos3.y),Point(pos2.x-w/4,pos2.y),:stroke)
end
if ( pos1.x > 0.0 && pos2.x < 0.0 )
Luxor.CURRENTDRAWINGINDEX[1]=1
line(Point(pos3.x+w/4,pos3.y),Point(pos2.x+w/4,pos2.y),:stroke)
Luxor.CURRENTDRAWINGINDEX[1]=2
line(Point(pos1.x-w/4,pos1.y),Point(pos3.x-w/4,pos3.y),:stroke)
end
if ( pos1.x < 0.0 && pos2.x < 0.0 )
Luxor.CURRENTDRAWINGINDEX[1]=1
line(Point(pos1.x+w/4,pos1.y),Point(pos2.x+w/4,pos2.y),:stroke)
end
if ( pos1.x > 0.0 && pos2.x > 0.0 )
Luxor.CURRENTDRAWINGINDEX[1]=2
line(Point(pos1.x-w/4,pos1.y),Point(pos2.x-w/4,pos2.y),:stroke)
end
sleep(1.0/120.0)
if isready(channel)
break
end
end
end
Luxor.CURRENTDRAWINGINDEX[1]=1
origin()
Luxor.CURRENTDRAWINGINDEX[1]=2
origin()
stick(2*WIDTH, HEIGHT)
The code changes in this branch https://github.com/oheil/Luxor.jl/tree/multi_drawing is not breaking any user code. All tests are running fine.
Yet it's not a good interface for users of Luxor, but I would like to evolve this into something which could be brought into your official Luxor package. But I am not quite sure in which way I should do this. Currently it's more like a hidden feature for experimental people like me.
If you like this I would be happy to discuss the right way.
The long goal for all this playing around could be some GUI package in Julia based on MiniFB + Luxor(Cairo).
Very cool stuff!
When I started Luxor (2014!) it was because I simply wanted to draw coloured shapes on a PNG/SVG 2D canvas using Julia 🤣 - Cairo.jl was too verbose, Compose.jl was a bit weird, the plotting packages weren't very flexible... Luxor was merely some syntactic sugar to simplify calls to Cairo - the original tagline was something like "A sugary-sweet interface to Cairo.jl!". Since then Luxor has got a bit, ... er ... bloated?; image buffers, snapshots, SVG surfaces, etc.. It wasn't well-designed to start with, and adding lots of extra pieces hasn't necessarily made it better, In the meantime, Julia has grown to offer powerful interactive 2D/3D layout features in packages like Makie and Pluto, and big capable geometry libraries in Meshes and all the stuff in JuliaGeometry.
Perhaps, with hindsight, there was a demand for a more carefully designed interface to Cairo, taking into account multiple drawings, windows, interactive widgets, updating screen buffers, etc.. But now I wonder whether it would be easier to take whatever parts of the Luxor user interface are useful and interface them to a better-designed graphics system, similar to something like CairoMakie perhaps. (I read that Manim (as used by 3Blue1Brown) switched from using Cairo to OpenGL - I wonder whether Javis.jl should do the same.)
All of which is to say that Luxor.jl is intended to be simple and easy to use for new Julia users (and easy to maintain!) rather than powerful or versatile or an engine designed for interfacing with other libraries. It's possible - but not always easy - to make it do all those things, and that's a challenge!
Luxor is perfectly fine for my needs and easy enough to use, except for a few minor things. I don't think it's bloated and the design is appropriate as far as I can tell. I prefer the as-simple-as-needed design principle, not the fancy-ingenious-fit-for-all-future-scenarios designs, nobody can understand :-) There has been a discussion about Julia native GUIs on discord and together with small Julia executables, these are the things I am missing desperately for Julia. I did some further research on MiniFB and it's probably not enough for a GUI system as a platform independent window manager. So, for now this little research project of mine is on pause again, I think. Actually I came from the wish to express some creativity with Julia, stumbled over the idea of some future GUI system, and now I am back to express myself using Luxor or whatever suits my needs. This non-issue can be closed.
Thanks for the contributions anyway. I hope your creativity will continue to be expressed!
This opens two windows which can be used as drawing canvas independently:
Just a proof of concept. Except for a ignorable error message from Cairo this works pretty well.