diff --git a/REQUIRE b/REQUIRE index 82c5825..1af1b17 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,5 +1,4 @@ julia 0.6 -LCMCore 0.1.0 BinDeps 0.4.0 GeometryTypes 0.4 ColorTypes 0.2.0 @@ -10,5 +9,6 @@ Rotations 0.5.0 CoordinateTransformations 0.2.0 StaticArrays 0.5 DataStructures 0.4 -JSON 0.8 CMakeWrapper 0.0.2 +MsgPack 0.1.1 +ZMQ 0.4.2 diff --git a/demo.ipynb b/demo.ipynb index 72d576e..6831c5f 100644 --- a/demo.ipynb +++ b/demo.ipynb @@ -12,7 +12,16 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "using Revise" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -27,30 +36,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "# Launch the viewer application if it isn't running already:\n", - "DrakeVisualizer.any_open_windows() || DrakeVisualizer.new_window();" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "GeometryTypes.HyperRectangle{3,Float64}(Vec(0.0,0.0,0.0),Vec(1.0,1.0,1.0))" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "# First, we'll create a simple geometric object\n", "box = HyperRectangle(Vec(0.,0,0), Vec(1.,1,1))" @@ -58,45 +46,27 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff953428200,RawFD(47),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff957700ae0)])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Visualizer() causes the viewer to spawn a geometry or a set of geometries. \n", "# It returns a Visualizer, which includes all the information\n", "# about that loaded geometry. \n", "# Note that the model is initially loaded in the zero configuration \n", - "# (that is, its position and rotation are all zeros)\n", + "# (that is, its position and rotation are all zeros). \n", + "#\n", + "# New in version 0.3: Visualizer() now also automatically spawns a new\n", + "# viewer window. \n", + "\n", "model = Visualizer(box)" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# We can use settransform!() to tell the viewer to draw the box at a specific\n", "# position. Translation() creates a CoordinateTransformations.Transformation\n", @@ -106,20 +76,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# We can also rotate the model by sending a different transformation\n", "settransform!(model, LinearMap(AngleAxis(pi/4, 0, 0, 1)))" @@ -127,20 +86,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# And we can clear the box\n", "delete!(model)" @@ -148,44 +96,45 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff95745af00,RawFD(53),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff95911f3f0)])" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], + "source": [ + "# and close the resulting window\n", + "close(model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Rather than creating a new window every single time,\n", + "# we can save the Window object and re-use it for each\n", + "# visualizer\n", + "window = Window()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Now let's make some more complicated robots. We'll create a \n", "# new GeometryData from the box, but color it green this time.\n", "green_box = GeometryData(box, RGBA(0., 1, 0, 0.5))\n", - "model = Visualizer(green_box)" + "model = Visualizer(green_box, window)" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ + "# and now let's delete it\n", "delete!(model)" ] }, @@ -204,41 +153,19 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff959113420,RawFD(59),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff959122a70)])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "# First, we make an empty visualizer:\n", - "vis = Visualizer()" + "# First, we make an empty visualizer, using our existing window:\n", + "vis = Visualizer(window)" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[:group1] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff959113420,RawFD(59),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff959122a70)])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# We can access a particular path within the visualizer with indexing notation:\n", "vis[:group1]" @@ -246,20 +173,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[:group1,:greenbox] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff959113420,RawFD(59),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff959122a70)])" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# We load geometries using the same path notation:\n", "green_box_vis = setgeometry!(vis[:group1][:greenbox], green_box)" @@ -281,20 +197,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# And we set transforms in the same way:\n", "settransform!(green_box_vis, Translation(0, 0, 1))" @@ -302,20 +207,9 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# The same indexing notation makes it easy to get a handle to a \n", "# particular part of the viewer tree:\n", @@ -332,20 +226,9 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[:group1,:bluebox] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff959113420,RawFD(59),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff959122a70)])" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "setgeometry!(vis[:group1][:bluebox], GeometryData(box, RGBA(0, 0, 1, 0.5)))" ] @@ -372,20 +255,9 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "settransform!(vis[:group1], Translation(1, 0, 0))" ] @@ -399,20 +271,9 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "settransform!(vis[:group1][:greenbox], Translation(0, 1, 0))" ] @@ -433,47 +294,15 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [], - "text/plain": [ - "Interact.Options{:SelectionSlider,Float64}(Signal{Float64}(0.9795918367346939, nactions=1),\"x1\",0.9795918367346939,\"0.9795918367346939\",Interact.OptionDict(DataStructures.OrderedDict(\"0.0\"=>0.0,\"0.04081632653061224\"=>0.0408163,\"0.08163265306122448\"=>0.0816327,\"0.12244897959183673\"=>0.122449,\"0.16326530612244897\"=>0.163265,\"0.20408163265306123\"=>0.204082,\"0.24489795918367346\"=>0.244898,\"0.2857142857142857\"=>0.285714,\"0.32653061224489793\"=>0.326531,\"0.3673469387755102\"=>0.367347…),Dict(2.0=>\"2.0\",1.59184=>\"1.5918367346938775\",0.122449=>\"0.12244897959183673\",1.95918=>\"1.9591836734693877\",1.22449=>\"1.2244897959183674\",1.63265=>\"1.6326530612244898\",0.693878=>\"0.6938775510204082\",1.10204=>\"1.1020408163265305\",1.79592=>\"1.7959183673469388\",0.367347=>\"0.3673469387755102\"…)),Any[],Any[],true,\"horizontal\")" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [], - "text/plain": [ - "Interact.Options{:SelectionSlider,Float64}(Signal{Float64}(0.9795918367346939, nactions=1),\"x2\",0.9795918367346939,\"0.9795918367346939\",Interact.OptionDict(DataStructures.OrderedDict(\"0.0\"=>0.0,\"0.04081632653061224\"=>0.0408163,\"0.08163265306122448\"=>0.0816327,\"0.12244897959183673\"=>0.122449,\"0.16326530612244897\"=>0.163265,\"0.20408163265306123\"=>0.204082,\"0.24489795918367346\"=>0.244898,\"0.2857142857142857\"=>0.285714,\"0.32653061224489793\"=>0.326531,\"0.3673469387755102\"=>0.367347…),Dict(2.0=>\"2.0\",1.59184=>\"1.5918367346938775\",0.122449=>\"0.12244897959183673\",1.95918=>\"1.9591836734693877\",1.22449=>\"1.2244897959183674\",1.63265=>\"1.6326530612244898\",0.693878=>\"0.6938775510204082\",1.10204=>\"1.1020408163265305\",1.79592=>\"1.7959183673469388\",0.367347=>\"0.3673469387755102\"…)),Any[],Any[],true,\"horizontal\")" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 18, - "metadata": { - "comm_id": "b38ee407-05d8-488a-87e3-eb74a74853d4", - "reactive": true - }, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "@manipulate for x1 in linspace(0, 2), x2 in linspace(0, 2)\n", - " settransform!(vis[:group1], Translation(x1, 0, 0))\n", - " settransform!(vis[:group1][:greenbox], Translation(x2, 1, 0))\n", + " batch(vis) do v\n", + " settransform!(vis[:group1], Translation(x1, 0, 0))\n", + " settransform!(vis[:group1][:greenbox], Translation(x2, 1, 0))\n", + " end\n", "end" ] }, @@ -486,86 +315,42 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "delete!(vis[:group1])" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Of course, we can draw much more interesting geometries than \n", "# just simple boxes. Let's load a 3D mesh and visualize it:\n", "using MeshIO\n", "using FileIO\n", "cat_mesh = load(joinpath(Pkg.dir(\"GeometryTypes\"), \"test\", \"data\", \"cat.obj\"))\n", - "model = Visualizer(cat_mesh);\n", - "settransform!(model, LinearMap(AngleAxis(pi/2, 1, 0, 0)))" + "setgeometry!(vis[:cat], cat_mesh)\n", + "settransform!(vis[:cat], LinearMap(AngleAxis(pi/2, 1, 0, 0)))" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "delete!(model)" + "delete!(vis)" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff9577dfe90,RawFD(71),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff95923df70)])" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Next, let's create a triangulated mesh by finding\n", "# the 0-level set of some function. \n", @@ -586,38 +371,14 @@ "# points for which f(x) = 0.\n", "\n", "# And now we can load that geometry into the visualizer\n", - "model = Visualizer(mesh)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [], - "text/plain": [ - "Interact.Options{:SelectionSlider,Float64}(Signal{Float64}(0.0, nactions=1),\"iso_level\",0.0,\"0.0\",Interact.OptionDict(DataStructures.OrderedDict(\"-1.0\"=>-1.0,\"-0.96\"=>-0.96,\"-0.92\"=>-0.92,\"-0.88\"=>-0.88,\"-0.84\"=>-0.84,\"-0.8\"=>-0.8,\"-0.76\"=>-0.76,\"-0.72\"=>-0.72,\"-0.68\"=>-0.68,\"-0.64\"=>-0.64…),Dict(-0.92=>\"-0.92\",-0.28=>\"-0.28\",-0.04=>\"-0.04\",0.56=>\"0.56\",-0.32=>\"-0.32\",0.2=>\"0.2\",-0.68=>\"-0.68\",-0.96=>\"-0.96\",0.48=>\"0.48\",-0.8=>\"-0.8\"…)),Any[],Any[],true,\"horizontal\")" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff95764f360,RawFD(77),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff955289c10)])" - ] - }, - "execution_count": 23, - "metadata": { - "comm_id": "c962eaf9-073b-4520-b7af-c5b5eb218620", - "reactive": true - }, - "output_type": "execute_result" - } - ], + "vis = Visualizer(mesh, window)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# We can even manipulate the geometry by changing the iso level. \n", "# By default, contour_mesh constructs a mesh connecting the points \n", @@ -630,31 +391,60 @@ "\n", "@manipulate for iso_level in linspace(-1, 1, 51)\n", " geometry = contour_mesh(f, lower_bound, upper_bound, iso_level)\n", - " model = Visualizer(geometry)\n", + " setgeometry!(vis, geometry)\n", "end\n", "\n", "# Note that for high iso_level values our geometry gets cut off at the\n", "# edges. We could fix that by replacing the bounds with a bigger box. " ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Controlling the Window\n", + "\n", + "We can also close and re-open the visualizer window. This is especially useful if something goes wrong while drawing. " + ] + }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "delete!(model)" + "# Close the window attached to this visualizer\n", + "close(vis) # or close(window)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Re-open the window\n", + "open(vis) # or open(window)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Send whatever geometries we had back to that window\n", + "republish!(vis)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Now let's clear away that mesh so we can try something else\n", + "delete!(vis)" ] }, { @@ -668,22 +458,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "vis = Visualizer()\n", + "vis = Visualizer(window)\n", "setgeometry!(vis[:group1][:box1], green_box)\n", "setgeometry!(vis[:group1][:box2], green_box)\n", "settransform!(vis[:group1], Translation(0, 1, 0))\n", @@ -699,22 +478,11 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "vis = Visualizer()\n", + "vis = Visualizer(window)\n", "batch(vis) do v\n", " setgeometry!(v[:group1][:box1], green_box)\n", " setgeometry!(v[:group1][:box2], green_box)\n", @@ -732,20 +500,9 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "delete!(vis)" ] @@ -761,20 +518,9 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[:pointcloud] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff959c81870,RawFD(293),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff953705e70)])" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# A PointCloud can be constructed from a vector of points.\n", "# The easiest way to represent a point is just a standard Julia vector:\n", @@ -787,10 +533,8 @@ }, { "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "using ColorTypes: RGB" @@ -798,20 +542,9 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Visualizer with path prefix Symbol[:pointcloud] using LCM LCMCore.LCM(Ptr{Void} @0x00007ff959c81870,RawFD(293),LCMCore.Subscription[LCMCore.Subscription{LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}}(LCMCore.SubscriptionOptions{DrakeVisualizer.Comms.CommsT,DrakeVisualizer.#handle_msg#7{DrakeVisualizer.CoreVisualizer}}(DrakeVisualizer.Comms.CommsT,DrakeVisualizer.handle_msg),Ptr{Void} @0x00007ff953705e70)])" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# PointClouds can also have \"channels\" which describe their\n", "# data. One useful channel is the :rgb channel:\n", @@ -822,38 +555,27 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "true" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "delete!(vis)" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# Close the viewer:\n", - "# kill(proc)" + "# Close the viewer window\n", + "close(vis)" ] } ], "metadata": { "kernelspec": { - "display_name": "Julia 0.6.0-rc3", + "display_name": "Julia 0.6.2", "language": "julia", "name": "julia-0.6" }, @@ -861,7 +583,7 @@ "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", - "version": "0.6.0" + "version": "0.6.2" } }, "nbformat": 4, diff --git a/src/DrakeVisualizer.jl b/src/DrakeVisualizer.jl index 439528a..ab7f470 100644 --- a/src/DrakeVisualizer.jl +++ b/src/DrakeVisualizer.jl @@ -2,7 +2,6 @@ __precompile__() module DrakeVisualizer -using LCMCore using GeometryTypes using FileIO import GeometryTypes: origin, radius, raw @@ -17,15 +16,17 @@ import CoordinateTransformations: Transformation, Translation, compose import ColorTypes: RGB, RGBA, Colorant, red, green, blue, alpha -import StaticArrays: SVector, StaticArray, SMatrix +import StaticArrays: SVector, StaticArray, SMatrix, StaticVector import Base: convert, length, show, isempty, empty!, delete! import DataStructures: OrderedDict -import JSON +import ZMQ +import MsgPack export GeometryData, Link, Robot, Visualizer, + Window, HyperRectangle, HyperEllipsoid, HyperCylinder, @@ -42,6 +43,7 @@ export GeometryData, settransform!, setgeometry!, addgeometry!, + republish!, load!, draw!, delete!, @@ -49,21 +51,12 @@ export GeometryData, const drake_visualizer_executable_name = "drake-visualizer" -function new_window(; script::Union{AbstractString, Void} = nothing) - installed_visualizer_path = joinpath(dirname(@__FILE__), "..", "deps", "usr", "bin", "$drake_visualizer_executable_name") - drake_visualizer = if isfile(installed_visualizer_path) - # If we built drake-visualizer, then use it - installed_visualizer_path - else - # Otherwise let the system try to find it - drake_visualizer_executable_name - end - command = script == nothing ? `$drake_visualizer` : `$drake_visualizer --script $script` - (stream, proc) = open(command) - proc +function new_window(args...; kw...) + error("The function new_window() has been removed in the process of simplifying the connection between the visualizer and its window. Calling `Visualizer()` will now automatically launch a window which will respond only to that visualizer's commands, or you can call DrakeVisualizer.Window() to manually launch a window.") end function any_open_windows() + warn("This function is deprecated and will be removed in a future version. It should no longer be necessary now that the window process is explicitly wrapped by the `Window` type.") @static if is_apple() return success(spawn(`pgrep $drake_visualizer_executable_name`)) elseif is_linux() @@ -74,6 +67,102 @@ function any_open_windows() end end +mutable struct Window + url::String + script::Union{String, Void} + stream::Union{Pipe, Void} + proc::Union{Base.Process, Void} + context::ZMQ.Context + socket::ZMQ.Socket + + + function Window(;url=nothing, script=nothing, launch=true) + if url === nothing + host = "127.0.0.1" + port = find_available_port(host) + url = "tcp://$host:$port" + end + + window = new(url, script, nothing, nothing) + if launch + open(window) + else + connect(window) + end + finalizer(window, disconnect) + window + end +end + +Base.show(io::IO, win::Window) = print(io, "Window using ZMQ URL $(win.url)") + +const DEFAULT_PORT = 53730 +const NUM_PORTS_TO_TRY = 256 + +function find_available_port(host) + port = DEFAULT_PORT + for i in 1:NUM_PORTS_TO_TRY + try + socket = connect(host, port) + close(socket) + port += 1 + catch e + if e isa Base.UVError && e.prefix == "connect" && e.code == -111 + return port + end + end + end + error("Could not find an available port from $DEFAULT_PORT to $(DEFAULT_PORT + 255)") +end + +function launch_command(url, script) + # installed_visualizer_path = joinpath(dirname(@__FILE__), "..", "deps", "usr", "bin", "$drake_visualizer_executable_name") + installed_visualizer_path = "/home/rdeits/locomotion/director/build/install/bin/drake-visualizer" + drake_visualizer = if isfile(installed_visualizer_path) + # If we built drake-visualizer, then use it + installed_visualizer_path + else + # Otherwise let the system try to find it + drake_visualizer_executable_name + end + if script === nothing + command = `$drake_visualizer` + else + command = `$drake_visualizer --script $(script)` + end + return `$command --no-drakevisualizer-lcm --no-treeviewer-lcm --no-lcmgl-renderer --treeviewer-url=$(url)` +end + +function Base.connect(win::Window) + win.context = ZMQ.Context() + win.socket = ZMQ.Socket(win.context, ZMQ.REQ) + ZMQ.connect(win.socket, win.url) +end + +function disconnect(win::Window) + close(win.socket) + close(win.context) +end + +function Base.open(win::Window) + cmd = launch_command(win.url, win.script) + win.stream, win.proc = open(cmd) + connect(win) +end + +function reconnect(win::Window) + disconnect(win) + connect(win) +end + +function Base.close(win::Window) + disconnect(win) + (win.proc === nothing) || kill(win.proc) + (win.stream === nothing) || close(win.stream) + win.proc = nothing + win.stream = nothing +end + function delete_director_binaries(skip_confirmation=false) root = joinpath(dirname(dirname(@__FILE__)), "deps") binary_paths = [ @@ -102,14 +191,10 @@ function delete_director_binaries(skip_confirmation=false) end end -include("lcmtypes/comms_t.jl") include("lazytree.jl") include("contour_meshes.jl") include("geometry_types.jl") include("visualizer.jl") include("serialization.jl") -@deprecate load!(args...) setgeometry!(args...) -@deprecate draw!(args...) settransform!(args...) - end diff --git a/src/lcmtypes/comms_t.jl b/src/lcmtypes/comms_t.jl deleted file mode 100644 index 93f69c6..0000000 --- a/src/lcmtypes/comms_t.jl +++ /dev/null @@ -1,24 +0,0 @@ -module Comms - -using LCMCore -using StaticArrays - -mutable struct CommsT <: LCMType - utime::Int64 - format::String - format_version_major::Int32 - format_version_minor::Int32 - num_bytes::Int32 - data::Vector{UInt8} -end - -function CommsT(utime::Integer, format::String, format_version_major::Integer, format_version_minor::Integer, data::Vector{UInt8}) - CommsT(utime, format, format_version_major, format_version_minor, length(data), data) -end - -LCMCore.fingerprint(::Type{CommsT}) = SVector(0xd3, 0x68, 0xe0, 0x3f, 0x33, 0xc5, 0x68, 0xbe) -LCMCore.size_fields(::Type{CommsT}) = (:num_bytes,) -LCMCore.check_valid(x::CommsT) = @assert length(x.data) == x.num_bytes -Base.resize!(x::CommsT) = resize!(x.data, x.num_bytes) - -end diff --git a/src/serialization.jl b/src/serialization.jl index af0bf0d..027bd9f 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -1,131 +1,147 @@ +import MsgPack: pack + function serialize(vis::CoreVisualizer, queue::CommandQueue) - utime = time_ns() - delete_cmds = Dict{String, Any}[] - setgeometry_cmds = Dict{String, Any}[] - settransform_cmds = Dict{String, Any}[] + timestamp = time_ns() + delete_cmds = [] + setgeometry_cmds = [] + settransform_cmds = [] for path in queue.delete push!(delete_cmds, Dict("path" => path)) end for path in queue.setgeometry visdata = vis.tree[path].data - push!(setgeometry_cmds, serialize(path, visdata.geometries)) + push!(setgeometry_cmds, (path, visdata.geometries)) + # push!(setgeometry_cmds, serialize(path, visdata.geometries)) end for path in queue.settransform visdata = vis.tree[path].data - tform = serialize(visdata.transform) push!(settransform_cmds, Dict{String, Any}("path" => path, - "transform" => tform + "transform" => visdata.transform ) ) end - data = Dict{String, Any}( - "utime" => utime, + buf = IOBuffer() + # write(buf, "treeviewer2.0 ") + pack(buf, + Dict{String, Any}( + "timestamp" => timestamp, "delete" => delete_cmds, "setgeometry" => setgeometry_cmds, "settransform" => settransform_cmds - ) + )) + take!(buf) end -function serialize(path::AbstractVector, geomdatas::Vector{GeometryData}) - params = serialize.(geomdatas) - if length(params) == 1 - Dict("path" => serialize(path), - "geometry" => params[1]) - else - Dict("path" => serialize(path), - "geometries" => params) - end +function pack(s::IO, geompaths::Tuple{AbstractVector, AbstractVector{<:GeometryData}}) + path, geomdatas = geompaths + pack(s, + Dict( + "path" => path, + "geometries" => geomdatas + ) + ) end -function serialize(geomdata::GeometryData) - params = serialize(geomdata.geometry) - params["color"] = serialize(geomdata.color) - transform = compose(geomdata.transform, - intrinsic_transform(geomdata.geometry)) - if transform != IdentityTransformation() - params["transform"] = serialize(transform) - end - params +numpy_dtype_str(::Type{Float64}) = "f8" +numpy_dtype_str(::Type{Float32}) = "f4" +numpy_dtype_str(::Type{Int64}) = "i8" +numpy_dtype_str(::Type{Int32}) = "i4" + +""" +Pack arrays into the style expected by msgpack-numpy. Note that we reverse the +order of the sizes because numpy will interpret the data as row-major instead +of column-major. +""" +struct MsgPackNumpyArray{A <: AbstractArray} + data::A end -intrinsic_transform(g) = IdentityTransformation() -intrinsic_transform(g::Nullable) = isnull(g) ? IdentityTransformation() : intrinsic_transform(get(g)) -intrinsic_transform(geomdata::GeometryData) = intrinsic_transform(geomdata.geometry) -intrinsic_transform(g::HyperRectangle) = Translation(center(g)...) -intrinsic_transform(g::HyperSphere) = Translation(center(g)...) -intrinsic_transform(g::HyperEllipsoid) = Translation(center(g)...) -intrinsic_transform(g::HyperCylinder) = Translation(center(g)...) -intrinsic_transform(g::HyperCube) = Translation(center(g)...) +function pack(s::IO, a::MsgPackNumpyArray) + # TODO: this is inefficient because we have to create a new IOBuffer + # to pack the data into, and then we pack that buffer into the Ext. + raw_data = reinterpret(UInt8, a.data, (length(a.data) * sizeof(eltype(a.data)),)) + pack(s, MsgPack.Ext(0x50, MsgPack.pack(Dict( + "nd" => true, + "type" => numpy_dtype_str(eltype(a.data)), + "shape" => reverse(size(a.data)), + "data" => raw_data + )))) +end + +msgpack_numpy_format(x::AbstractArray) = MsgPackNumpyArray(x) -serialize(color::Colorant) = (red(color), - green(color), - blue(color), - alpha(color)) -serialize(p::Path) = string.(p) -serialize(v::Vector) = v -serialize(v::Vec) = convert(Vector, v) -serialize(v::Point) = convert(Vector, v) -serialize(v::StaticArray) = convert(Vector, v) -serialize(face::Face{N, T}) where {N, T} = - raw.(convert(Face{N, GeometryTypes.OffsetInteger{-1, Int}}, face)) -serialize(g::HyperRectangle) = Dict("type" => "box", "lengths" => serialize(widths(g))) -serialize(g::HyperSphere) = Dict("type" => "sphere", "radius" => radius(g)) -serialize(g::HyperEllipsoid) = Dict("type" => "ellipsoid", "radii" => serialize(radii(g))) -serialize(g::HyperCylinder{3}) = Dict("type" => "cylinder", - "length" => length(g), - "radius" => radius(g)) -serialize(g::HyperCube) = Dict("type" => "box", "lengths" => widths(g)) -serialize(g::GeometryPrimitive) = serialize(GLNormalMesh(g)) -serialize(g::Triad) = Dict{String, Any}("type" => "triad", - "scale" => g.scale, - "tube" => g.tube) - -function serialize(g::AbstractMesh) - Dict("type" => "mesh_data", - "vertices" => serialize.(vertices(g)), - "faces" => serialize.(faces(g))) +function msgpack_numpy_format(x::AbstractVector{<:StaticVector{N, T}}) where {N, T} + MsgPackNumpyArray(reinterpret(T, x, (N, length(x)))) end -function serialize(f::MeshFile) - Dict("type" => "mesh_file", - "filename" => f.filename, - "scale" => SVector(1.0, 1.0, 1.0)) +function msgpack_numpy_format(x::AbstractVector{Cin}, ::Type{Cout}=RGB{Float32}) where {Cin <: Colorant, Cout <: Colorant} + xout = convert(Vector{Cout}, x) + MsgPackNumpyArray(reinterpret(eltype(Cout), x, (length(Cout), length(x)))) end -function serialize(g::PointCloud) - params = Dict("type" => "pointcloud", - "points" => serialize.(g.points), - "channels" => Dict{String, Any}()) - for (channel, values) in g.channels - params["channels"][string(channel)] = serialize.(values) - end - params +function msgpack_numpy_format(faces::AbstractVector{<:Face{N, T}}) where {N, T} + MsgPackNumpyArray( + reinterpret(Int, [raw.(convert(Face{N, GeometryTypes.OffsetInteger{-1, Int}}, face)) for face in faces], (N, length(faces)))) end -function serialize(g::PolyLine) - params = Dict("type" => "line", - "points" => serialize.(g.points), - "radius" => g.radius, - "closed" => g.closed - ) +pack(s::IO, v::StaticVector) = pack(s, Tuple(v)) +pack(s::IO, v::Symbol) = pack(s, String(v)) +pack(s::IO, c::Colorant) = pack(s, (red(c), green(c), blue(c), alpha(c))) +pack(s::IO, tform::Transformation) = pack(s, Dict("translation" => translation(tform), "quaternion" => quaternion(tform))) +pack(s::IO, g::GeometryData) = pack(s, Dict(vcat(geometry_fields(g.geometry), common_fields(g)))) + +common_fields(g::GeometryData) = ["color" => g.color, + "transform" => compose(g.transform, intrinsic_transform(g.geometry))] + +geometry_fields(g::HyperRectangle) = ["type" => "box", "lengths" => widths(g)] +geometry_fields(g::HyperSphere) = ["type" => "sphere", "radius" => radius(g)] +geometry_fields(g::HyperEllipsoid) = ["type" => "ellipsoid", "radii" => radii(g)] +geometry_fields(g::HyperCylinder{3}) = ["type" => "cylinder", + "length" => length(g), + "radius" => radius(g)] +geometry_fields(g::HyperCube) = ["type" => "box", "lengths" => widths(g)] +geometry_fields(g::Triad) = ["type" => "triad", "scale" => g.scale, "tube" => g.tube] +geometry_fields(g::PointCloud) = ["type" => "pointcloud", + "points" => msgpack_numpy_format(g.points), + "channels" => Dict( + [(name => msgpack_numpy_format(value)) for (name, value) in g.channels])] +geometry_fields(g::AbstractMesh) = ["type" => "mesh_data", + "vertices" => msgpack_numpy_format(vertices(g)), + "faces" => msgpack_numpy_format(faces(g))] +geometry_fields(g::MeshFile) = ["type" => "mesh_file", + "filename" => g.filename, + "scale" => (1.0, 1.0, 1.0)] + +function geometry_fields(g::PolyLine) + params = ["type" => "line", + "points" => msgpack_numpy_format(g.points), + "radius" => g.radius, + "closed" => g.closed] if !isnull(g.start_head) - params["start_head"] = true - params["head_radius"] = get(g.start_head).radius - params["head_length"] = get(g.start_head).length + append!(params, ["start_head" => true, + "head_radius" => get(g.start_head).radius, + "head_length" => get(g.start_head).length]) end if !isnull(g.end_head) - params["end_head"] = true - params["head_radius"] = get(g.end_head).radius - params["head_length"] = get(g.end_head).length + append!(params, ["end_head" => true, + "head_radius" => get(g.end_head).radius, + "head_length" => get(g.end_head).length]) end params end -function serialize(tform::Transformation) - Dict{String, Vector{Float64}}("translation" => translation(tform), - "quaternion" => quaternion(tform)) -end +# Any unhandled geometries are converted to a mesh first +geometry_fields(g::GeometryPrimitive) = geometry_fields(GLNormalMesh(g)) + +intrinsic_transform(g) = IdentityTransformation() +intrinsic_transform(g::Nullable) = isnull(g) ? IdentityTransformation() : intrinsic_transform(get(g)) +intrinsic_transform(geomdata::GeometryData) = intrinsic_transform(geomdata.geometry) +intrinsic_transform(g::HyperRectangle) = Translation(center(g)...) +intrinsic_transform(g::HyperSphere) = Translation(center(g)...) +intrinsic_transform(g::HyperEllipsoid) = Translation(center(g)...) +intrinsic_transform(g::HyperCylinder) = Translation(center(g)...) +intrinsic_transform(g::HyperCube) = Translation(center(g)...) quaternion(::IdentityTransformation) = SVector(1., 0, 0, 0) quaternion(tform::AbstractAffineMap) = quaternion(transform_deriv(tform, SVector(0., 0, 0))) diff --git a/src/visualizer.jl b/src/visualizer.jl index 179f1ea..5593f0b 100644 --- a/src/visualizer.jl +++ b/src/visualizer.jl @@ -41,37 +41,16 @@ end const VisTree = LazyTree{Symbol, VisData} mutable struct CoreVisualizer - lcm::LCM - client_id::String + window::Window tree::VisTree queue::CommandQueue publish_immediately::Bool - function CoreVisualizer(lcm::LCM=LCM()) - client_id = "jl_$(Base.Random.randstring())" # 10^14 possibilities - vis = new(lcm, client_id, VisTree(), CommandQueue(), true) - function handle_msg(channel, msg) - try - onresponse(vis, msg) - catch e - warn(""" -An error ocurred while handling the viewer response: - error: $e - response: $msg -""") - end - end - sub = subscribe(lcm, response_channel(vis), handle_msg, Comms.CommsT) - @async while true - handle(lcm) - end - vis + function CoreVisualizer(window::Window) + new(window, VisTree(), CommandQueue(), true) end end -request_channel(vis::CoreVisualizer) = "DIRECTOR_TREE_VIEWER_REQUEST_<$(vis.client_id)>" -response_channel(vis::CoreVisualizer) = "DIRECTOR_TREE_VIEWER_RESPONSE_<$(vis.client_id)>" - function setgeometry!(vis::CoreVisualizer, path::AbstractVector) push!(vis.queue.setgeometry, path) settransform!(vis, path) @@ -115,50 +94,53 @@ function delete!(vis::CoreVisualizer, path::AbstractVector) end end -function publish!(vis::CoreVisualizer) - if !isempty(vis.queue) - data = serialize(vis, vis.queue) - msg = to_lcm(data) - publish(vis.lcm, request_channel(vis), msg) - empty!(vis.queue) - end -end +const ZMQ_RECEIVE_TIMEOUT_S = 5 + +function publish!(core::CoreVisualizer) + if !isempty(core.queue) + data = serialize(core, core.queue) -function onresponse(vis::CoreVisualizer, msg) - data = JSON.parse(IOBuffer(msg.data)) - if data["status"] == 0 - empty!(vis.queue) - elseif data["status"] == 1 - for path in LazyTrees.descendants(vis.tree) - push!(vis.queue.setgeometry, path) - push!(vis.queue.settransform, path) + c = Channel{Bool}(1) + @async begin + ZMQ.send(core.window.socket, data) + ZMQ.recv(core.window.socket) + put!(c, true) end - publish!(vis) - else - error("unhandled: $data") + @async begin + sleep(ZMQ_RECEIVE_TIMEOUT_S) + put!(c, false) + end + success = take!(c) + if !success + close(core.window.socket) + close(core.window.context) + error("No response received from the visualizer. The ZMQ socket may no longer function, so it has been closed. You may want to close and re-launch the Visualizer.") + end + empty!(core.queue) end end -function to_lcm(data::Associative) - jsondata = JSON.json(data) - Comms.CommsT( - data["utime"], - "treeviewer_json", - 1, - 0, - length(jsondata), - jsondata) +function republish!(vis::CoreVisualizer) + for path in LazyTrees.descendants(vis.tree) + push!(vis.queue.setgeometry, path) + push!(vis.queue.settransform, path) + end + push!(vis.queue.setgeometry, []) + push!(vis.queue.settransform, []) + publish!(vis) end struct Visualizer core::CoreVisualizer path::Vector{Symbol} - Visualizer(lcm::LCM=LCM()) = new(CoreVisualizer(lcm), Symbol[]) + function Visualizer(win=Window()) + new(CoreVisualizer(win), Symbol[]) + end Visualizer(core::CoreVisualizer, path::AbstractVector) = new(core, path) end -show(io::IO, vis::Visualizer) = print(io, "Visualizer with path prefix $(vis.path) using LCM $(vis.core.lcm)") +show(io::IO, vis::Visualizer) = print(io, "Visualizer with path prefix $(vis.path) attached to $(vis.core.window)") function setgeometry!(vis::Visualizer) setgeometry!(vis.core, vis.path) @@ -196,11 +178,17 @@ function batch(func, vis::Visualizer) end end +republish!(vis::Visualizer) = republish!(vis.core) + # Old-style visualizer interface -function Visualizer(geom::GeometryData) - vis = Visualizer()[:body1] +function Visualizer(geom::GeometryData, args...; kwargs...) + vis = Visualizer(args...; kwargs...)[:anonymous] setgeometry!(vis, geom) vis end -Visualizer(geom::Union{AbstractGeometry, AbstractMesh}) = Visualizer(GeometryData(geom)) +Visualizer(geom::Union{AbstractGeometry, AbstractMesh}, args...; kwargs...) = Visualizer(GeometryData(geom), args...; kwargs...) + +Base.close(vis::Visualizer) = close(vis.core.window) +Base.open(vis::Visualizer) = open(vis.core.window) +reconnect(vis::Visualizer) = reconnect(vis.core.window) diff --git a/test/comms_t.jl b/test/comms_t.jl deleted file mode 100644 index f3932c2..0000000 --- a/test/comms_t.jl +++ /dev/null @@ -1,19 +0,0 @@ -@testset "comms_t" begin - utime = 123456 - format = "format_name_here" - format_version_major = 1 - format_version_minor = 32 - data = rand(UInt8, 1000) - msg = DrakeVisualizer.Comms.CommsT( - utime, - format, - format_version_major, - format_version_minor, - data) - encoded = DrakeVisualizer.Comms.encode(msg) - decoded = DrakeVisualizer.Comms.decode(encoded, DrakeVisualizer.Comms.CommsT) - - for field in fieldnames(typeof(msg)) - @test getfield(msg, field) == getfield(decoded, field) - end -end diff --git a/test/runtests.jl b/test/runtests.jl index a6bfeab..8be234b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,7 +6,6 @@ using Base.Test import Iterators: product include("lazytree.jl") -include("comms_t.jl") include("visualizer.jl") include("polyhedra.jl") include("notebook.jl") diff --git a/test/visualizer.jl b/test/visualizer.jl index a2f6b0c..3baf1b5 100644 --- a/test/visualizer.jl +++ b/test/visualizer.jl @@ -1,14 +1,5 @@ using ColorTypes: RGB -proc = DrakeVisualizer.new_window() - -@testset "open window" begin - if is_apple() || is_linux() - # @test DrakeVisualizer.any_open_windows() # doesn't pass when running headless - DrakeVisualizer.any_open_windows() - end -end - @testset "robot_load" begin f = x -> norm(x)^2 - 1 bounds = HyperRectangle(Vec(0.,0,0), Vec(1.,1,1)) @@ -110,12 +101,6 @@ end delete!(vis) end -@testset "deprecations" begin - vis = Visualizer() - load!(vis, HyperCylinder{3, Float64}(1.0, 2.0)) - draw!(vis, IdentityTransformation()) -end - @testset "addgeometry" begin vis = Visualizer() delete!(vis)