Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial PR for v0.8 #33

Merged
merged 12 commits into from
Dec 19, 2019
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
*.jl.cov
*.jl.mem
**/.ipynb_checkpoints
docs/build/
docs/build/

test/*.svg
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
name = "AutoViz"
uuid = "82aa6e0c-a491-5edf-8d4b-c16b98e4ea17"
repo = "https://github.com/sisl/AutoViz.jl.git"
version = "0.7.7"
version = "0.8.0"

[deps]
AutomotiveDrivingModels = "99497e54-f3d6-53d3-a3a9-fa9315a7f1ba"
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
LightXML = "9c8b4983-aa76-5018-a973-4c85ecc9e179"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Expand All @@ -18,6 +17,9 @@ Rsvg = "c4c386cf-5103-5370-be45-f3a111cca3b8"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Vec = "44eeaf0b-fee4-471f-9310-ed6585cb3142"

[compat]
AutomotiveDrivingModels = ">=0.7.10"

[extras]
Interact = "c601a237-2ae4-5e1e-952c-7a85b0c7eef1"
NBInclude = "0db19996-df87-5ea3-a455-e3a50d440464"
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,40 @@ using AutoViz
set_color_theme(LIGHTTHEME)
```
You can also define your own color theme using a dictionary. Look at the example in `src/colorscheme.jl` to have the correct key names.


## Change Log

### v0.8.x

#### Rendering
- Clean-up of the rendering interface: there is now only one single rendering function with the signature
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean the user has to construct a RenderModel object explicitly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think the typical usage now is as follows:

rm = RenderModel()
update_camera!(rm, MyCamera()) 
render!(rm, [my_renderables...]) 

I think it would still be convenient to have one render function that allows to do things in one function call and have the explicit version if one wants more fine tuning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, RenderModel needs to be constructed explicitly. This is mostly because RenderModel holds the main camera parameters (camera_center, camera_zoom, camera_rotation). This is not by my choice but rather how RenderModel was designed to work. In fact if designing from scratch I would probably not have the RenderModel hold those camera parameters.
So in order to set the initial zoom level (for example), it needs to be passed to the RenderModel. In the previous version, the zoom level was set by first setting the zoom level of a camera and then the camera would go set the zoom level of the rendermodel, but effectively those variables were duplicated in RenderModel and Camera which I was not a big fan of. Would you agree?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a conceptual level, the camera state really lives inside the RenderModel (again, based on legacy code) and the structs that are called Camera actually describe camera motions over time (like "stay static", "follow a vehicle", "follow the scene"). This is quite subtle but I think the struct names may be a little misleading.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth providing one or two convenience functions that take care of constructing a RenderModel but for full control I think we need all three steps (RenderModel, update_camera!, render!)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MaximeBouton added an "easy" render function that just takes a scene and optional overlays. There are still a few open questions there regarding the defaults

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try it out today.

What would be your suggestion on how to handle the camera if we did not have the RenderModel struct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the default render method this is what I had in mind:

"""
Takes care of initializing a `RenderModel` and updating the camera.
For full control, use `render!(rendermodel, renderables)` instead.
"""
function render(
    renderables;
    camera_zoom::Float64 = 10.,
    camera_center::VecE2 = VecE2(0., 0.),
    camera_rotation::Float64 = 0.,
    camera_motion::Camera = SceneFollowCamera(),
    canvas_width::Int=DEFAULT_CANVAS_WIDTH,
    canvas_height::Int=DEFAULT_CANVAS_HEIGHT,
    surface::CairoSurface = CairoSVGSurface(IOBuffer(), canvas_width, canvas_height),
)
    rendermodel = RenderModel(camera_center=camera_center, 
                              camera_zoom=camera_zoom, 
                              camera_rotation=camera_rotation)
    update_camera!(rendermodel, camera_motion)
    render!(rendermodel, renderables)

    return surface
end

no need to go back to something specific that takes scene and roadway as input.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try it out today.

What would be your suggestion on how to handle the camera if we did not have the RenderModel struct?

One Idea that crossed my mind would be to have a CameraState object that has the fields zoom, position, rotation. Each Camera then has a reference to a CameraState which it then manipulates. And when we pass in the Camera to the render function, with it comes the CameraState object. In addition to the CameraState, Camera itself would hold other variables that parameterize how it moves over the scene (target_id, n_vehicles in scene, etc).
RenderModel would then only keep track of the rendering instructions essentially (maybe the canvas dimension and canvas surface too, didn't spend much thought on this). RenderModel and CameraState would only be combined in the final render_to_canvas function.
In this way, we would not need to pass in the RenderModel to render but only the Camera.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the default render method this is what I had in mind:

"""
Takes care of initializing a `RenderModel` and updating the camera.
For full control, use `render!(rendermodel, renderables)` instead.
"""
function render(
    renderables;
    camera_zoom::Float64 = 10.,
    camera_center::VecE2 = VecE2(0., 0.),
    camera_rotation::Float64 = 0.,
    camera_motion::Camera = SceneFollowCamera(),
    canvas_width::Int=DEFAULT_CANVAS_WIDTH,
    canvas_height::Int=DEFAULT_CANVAS_HEIGHT,
    surface::CairoSurface = CairoSVGSurface(IOBuffer(), canvas_width, canvas_height),
)
    rendermodel = RenderModel(camera_center=camera_center, 
                              camera_zoom=camera_zoom, 
                              camera_rotation=camera_rotation)
    update_camera!(rendermodel, camera_motion)
    render!(rendermodel, renderables)

    return surface
end

no need to go back to something specific that takes scene and roadway as input.

I wish this were possible, it was also what I initially started off with. But I currently don't see an easy way of doing this:

  • SceneFollowCamera and TargetFollowCamera both need access to the scene in order to determine their new position (this is why we had issue SceneFollowCamera in new interface #9 where SceneFollowCamera would not work in the new rendering interface). The best we could do would be to default to StaticCamera but not sure if the convenience is worth what we're giving up.
  • We need to know what is the roadway so that we can render it before (i.e. below) the scene. If we pass in the roadway with the other renderables, we would have to search through the renderables and identify the roadway by type and render it first, which is pretty ugly.
    The only way i see around that would be to give every renderable object a depth parameter which we can query using depth(obj) and then determine rendering order based on that. Still not really the way to go I think :/

So given these two constraints, this "convenience" render function was the best I could think of...

```
render!(rendermodel::RenderModel, renderables::AbstractVector; canvas_width::Int, canvas_height::Int, surface::CairoSurface))
```
All keyword arguments are optional. Objects of type `Renderable` now no longer have to implement the `render!` function (which is a misleading name). Instead one must implement the `add_renderable!` function which adds the rendering instructions to the `RenderModel`.
- Implicit conversions of non-renderable objects (such as `obj::Frame{Entity{S,D,I}}`) via implementations of `Base.convert(Renderable, obj)` are now discouraged. Instead, one can overwrite the `add_renderable!` method for such types. This is done for some very common types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I agree convert(Renderable, ) was kind of clunky

- The new `render!` function now only takes objects which are renderable, i.e. which implement the `add_renderable(rm::RenderModel, obj)` function. There is no longer a distinction between drawing roadways, scenes or overlays. They all need to satisfy the same interface, and they are drawn in the order in which they are passed to the `render!` function. This change decreases the number of available render functions from almost ten to one and should make the control flow more clear.
- Additional arguments to `render!` such as `camera` and `car_colors` are no longer supported. Camera effects should be applied before calling `render!` (see section below) and rendering attributes such as colors should be passed in as part of a renderable object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rendering attributes such as colors should be passed in as part of a renderable object.

yes!


#### Overlays
- Changed the interface for rendering overlays to only take an instance of `RenderModel` and the overlay itself. All additional data must be stored as part of the overlay if it is needed during rendering.
- Added a `RenderableOverlay` wrapper which makes the legacy overlays work with the new rendering interface (in which overlays do not get any input arguments for rendering)

#### Cameras
- Changed the camera interface. The full state of the camera, such as `camera_pos`, `camera_zoom`, `camera_rotation` is stored in `RenderModel` (this has already been the case in previous AutoViz versions). A `Camera` acts upon a `RenderModel` by changing these internal variables. The function `camera_set!` now becomes `update_camera!`.
- Many setter functions for the camera have been replaced by the `set_camera!()` function which takes keyword arguments for `x`, `y` and `zoom`.
- The implementations of `TargetFollowCamera` (former `CarFollowCamera`) and `SceneFollowCamera` have been reviewed and simplified. Additionally, a `ZoomingCamera` type which gradually changes the zoom level has been introduced and for easy extensibility there is also a `ComposedCamera` type which takes a list of cameras and applies their effects sequentially to the `RenderModel`.
- The new `render!` function no longer takes a camera as an input argument, but assumes that the camera settings have already been applied to the `RenderModel` via `update_camera!` prior to calling `render!`. User code should be adapted accordingly.

#### Visualization of Entities
- Controlling the appearance of vehicles by setting `set_render_mode(:basic|:fancy)` is no longer encouraged. Instead, we provide new renderable types such as `EntityRectangle`, `FancyCar`, `FancyPedestrian`, `VelocityArrow` in addition to the already implemented `ArrowCar` type which can all be used to conveniently display entities.
- A convenience function for rendering scenes directly (i.e. without explicit conversion to a `Renderable` type) is still supported.
- TODO: make FancyCar work on my platform

#### 1D Vehicles
- Support for 1D vehicles has mostly been discontinued and some of the related functions were removed. However, the new functions should work seamlessly in many cases as long as the 1D vehicles implement basic functions such as `posg`, `width`, `length` from `AutomotiveDrivingModels.jl`

## TODO: adapt tutorials
## TODO: adapt unit tests
## TODO: adapt docs
2 changes: 1 addition & 1 deletion icons/racing_car_top_view.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/walking_person.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 38 additions & 46 deletions src/AutoViz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ using Parameters
using StaticArrays
using AutomotiveDrivingModels
using Printf
using LightXML
using Rsvg

@reexport using Colors
Expand All @@ -16,12 +15,9 @@ using Cairo
import Reel
Reel.set_output_type("gif")

export
DEFAULT_CANVAS_WIDTH,
DEFAULT_CANVAS_HEIGHT

const DEFAULT_CANVAS_WIDTH = 1000
const DEFAULT_CANVAS_HEIGHT = 600
export DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT

global _rendermode = :fancy

Expand All @@ -30,6 +26,7 @@ function set_render_mode(m::Symbol)
_rendermode = m
end

include("colorscheme.jl")
export
COLOR_ASPHALT,
COLOR_LANE_MARKINGS_WHITE,
Expand All @@ -41,29 +38,25 @@ export
LIGHTTHEME,
set_color_theme

include("colorscheme.jl")

# Cairo drawing utilities
include("rendermodels.jl")
export
RenderModel,

render,
render_to_canvas,
add_instruction!,
camera_fit_to_content!,
camera_move!,
camera_move_pix!,
camera_rotate!,
camera_setrotation!,
camera_zoom!,
camera_setzoom!,
camera_set_pos!,
camera_set_x!,
camera_set_y!,
camera_reset!,
camera_set!,
clear_setup!,
set_background_color!,
reset_camera!,
reset_instructions!,
reset_model!,
set_camera!,
set_background_color!

# Cairo drawing utilities
include("render_instructions.jl")
export
render_paint,
render_text,
render_circle,
Expand All @@ -84,40 +77,40 @@ export
render_fancy_car,
render_fancy_pedestrian

include("rendermodels.jl")
include("fancy_render.jl")

# Cameras
include("cameras.jl")
export
Camera,
update_camera!,
StaticCamera,
FitToContentCamera,
CarFollowCamera,
SceneFollowCamera

TargetFollowCamera,
ZoomingCamera,
ComposedCamera,
SceneFollowCamera,
FitToContentCamera

include("cameras.jl")

# main interface
export render!,
render,
get_pastel_car_colors

include("interface.jl")

# renderable interface
export Renderable,
render,
isrenderable,
write_to_svg,
ArrowCar


include("renderable.jl")
include("arrowcar.jl")
include("text.jl")

export
Renderable,
render,
render!,
add_renderable!,
isrenderable,
write_to_svg,
ArrowCar,
EntityRectangle,
VelocityArrow,
FancyCar


# Overlays
include("overlays.jl")
export SceneOverlay,
TextOverlay,
Overwash,
Expand All @@ -130,20 +123,19 @@ export SceneOverlay,
drawtext,
LineToCenterlineOverlay,
LineToFrontOverlay,
BlinkerOverlay


include("overlays.jl")
BlinkerOverlay,
RenderableOverlay

export PNGFrames,
SVGFrames

include("reel_drive.jl")

# Convenient implementation for roadway and vehicle rendering

include("roadways.jl")
include("vehicles.jl")

# old render methods that should be removed in future versions
include("deprecated.jl")
export render, render!

end # module
54 changes: 0 additions & 54 deletions src/arrowcar.jl

This file was deleted.