Skip to content
This repository was archived by the owner on Dec 28, 2021. It is now read-only.

HTML Rendering System #48

Merged
merged 32 commits into from
Nov 22, 2019
Merged

HTML Rendering System #48

merged 32 commits into from
Nov 22, 2019

Conversation

notdanilo
Copy link
Contributor

@notdanilo notdanilo commented Nov 18, 2019

Pull Request Description

This PR provides the implementation of the HTML Rendering System and its components:

  • HTMLRenderer: To render the camera transforms in the HTMLScene div and objects transforms in each HTMLObject div.
  • HTMLObject: Struct holding a HtmlElement and Transform
  • HTMLScene: A collection of HTMLObjects
  • Camera: Struct holding a Transform to position and orientate the camera, and a projection matrix.

Important Notes

To improve productivity, I've created a TestContainer when running wasm-pack test in a browser, so we can see its visual results.

Checklist

Please include the following checklist in your PR:

  • The documentation has been updated if necessary.
  • All code conforms to the Rust, Scala, Java or Haskell style guides as appropriate.
  • All code has been tested where possible.

@notdanilo notdanilo added Category: Renderer Category: BaseGL The BaseGL library Difficulty: Core Contributor Should only be attempted by a core contributor Priority: High Should be scheduled as soon as possible Type: Enhancement An enhancement to the current state of Enso IDE labels Nov 18, 2019
@notdanilo notdanilo added this to the Sprint 2019-11-11 milestone Nov 18, 2019
@codecov-io
Copy link

codecov-io commented Nov 18, 2019

Codecov Report

Merging #48 into master will increase coverage by 12.91%.
The diff coverage is 35.08%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #48       +/-   ##
===========================================
+ Coverage    8.48%   21.39%   +12.91%     
===========================================
  Files           6       16       +10     
  Lines         283      575      +292     
  Branches       21       57       +36     
===========================================
+ Hits           24      123       +99     
- Misses        251      415      +164     
- Partials        8       37       +29
Impacted Files Coverage Δ
lib/core/src/lib.rs 0% <ø> (ø) ⬆️
lib/core/src/display/rendering/htmlscene.rs 0% <0%> (ø)
lib/core/src/display/rendering/scene.rs 0% <0%> (ø)
lib/core/src/display/rendering/renderer.rs 0% <0%> (ø)
lib/system/web/src/lib.rs 0% <0%> (ø) ⬆️
lib/core/src/display/rendering/htmlrenderer.rs 0% <0%> (ø)
lib/core/src/display/world.rs 0% <0%> (ø) ⬆️
lib/core/src/display/rendering/htmlobject.rs 0% <0%> (ø)
lib/prelude/src/lib.rs 100% <100%> (ø)
lib/core/src/display/rendering/transform.rs 46.15% <46.15%> (ø)
... and 14 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 389062e...bbd9cc2. Read the comment docs.

@notdanilo notdanilo changed the title Wip/dg/htmlrenderer HTML Rendering System Nov 18, 2019
@@ -5,7 +5,7 @@ authors = ["Wojciech Danilo <[email protected]>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]
crate-type = ["rlib", "cdylib"]
Copy link
Member

Choose a reason for hiding this comment

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

Why we need rlib?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need it to make the crate available in the tests.

See rust-lang/cargo#6659

use std::collections::HashSet;
use std::rc::{Rc, Weak};
use std::hash::{Hash, Hasher};
pub trait Hashable = Hash + PartialEq;
Copy link
Member

Choose a reason for hiding this comment

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

There should be 2 empty lines between imports and code + please, use the code separators:

// ================
// === Hashable ===
// ================

to divide parts of the file related to different entities. If there is a single entitiy, we use just a single separator on top of the file.

@@ -0,0 +1,113 @@
use std::collections::HashSet;
Copy link
Member

Choose a reason for hiding this comment

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

Wait, after reviewing this file I realized that are not using WeakSet anywhere in you codebase, so why this file is committed? :D

Please read and reply to the reviews though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My mistake! I considered using it, but then I relied on the existing opt_vec implementation. Should we keep it?

fn drop(&mut self) {
unsafe {
// no worries because it will happen synchronously
Rc::get_mut_unchecked(&mut self.owner).data.remove(&WeakElem { elem : Rc::downgrade(&self.elem) });
Copy link
Member

Choose a reason for hiding this comment

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

Please fix this and other lines that have >80 chars. Be sure to read our code style guide.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

weakset.rs was removed as suggested

impl<T : Hashable> Drop for WeakRef<T> {
fn drop(&mut self) {
unsafe {
// no worries because it will happen synchronously
Copy link
Member

Choose a reason for hiding this comment

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

Please use style guide regarding comments in code. Plus, this comment is not specific enough - comments regarding unsafe blocks should be really detailed and explain why we need such blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

weakset.rs was removed as suggested

@@ -0,0 +1,50 @@
use super::HTMLObject;
Copy link
Member

Choose a reason for hiding this comment

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

Format this file according to previous comments, make lines max 80 chars, make more vars, so you dont have multi-line exprs after refactoring, and this will be a very nice file! <3

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A newer file formatted with cargo fmt should be available. Would you check it?

pub struct Transform {
pub position: Vector3<f32>,
pub quaternion: UnitQuaternion<f32>,
pub scale: Vector3<f32>,
Copy link
Member

Choose a reason for hiding this comment

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

position and scale are the names of physical properties of objects. quaternion is a name of a math structure. I may be wrong, but I feel it's inconsistent here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right. I renamed quaternion to rotation. I also renamed position to translation.

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// To comply with Threejs impl, we generate a Quaternion applying rotation in
// the order: pitch -> roll -> yaw, instead of roll -> pitch -> yaw based on
// https://github.com/mrdoob/three.js/blob/master/src/math/Quaternion.js#L199
Copy link
Member

Choose a reason for hiding this comment

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

A question - why we comply with Theejs impl? Based on this comment I understand it is not a standard way of handling it, right? Is it more performant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no performance difference. It's just a convention and we can change it whenever we want.

As I was developing this functionality based on Three.js, I used it as a visual reference and I saw something was wrong. That's when I realized the rotation order was different.

self.quaternion.coords.w,
);
let (x2, y2, z2) = (x + x, y + y, z + z);
let (xx, xy, xz) = (x * x2, x * y2, x * z2);
Copy link
Member

Choose a reason for hiding this comment

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

very minor - alignment of ,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it was fixed by cargo fmt. Please check


// Note [Transform to Matrix4 composition]
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// based on https://github.com/mrdoob/three.js/blob/master/src/math/Matrix4.js#L732
Copy link
Member

Choose a reason for hiding this comment

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

This should be a doc comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I left this as a source comment and provided a detailed explanation on how it works

@@ -47,6 +47,15 @@ impl<T> OptVec<T> {
item.iter().for_each(|_| self.free_ixs.push(ix));
item
}

/// Gets the amount of items as Some(element)
Copy link
Member

Choose a reason for hiding this comment

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

  1. I don't really understand the comment. What does as Some(element) mean?
  2. Minor: no dot on the end of comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/// Returns the number of elements in the vector, also referred to as its 'length'.
Just like in Vec

self.items.len() - self.free_ixs.len()
}

pub fn is_empty(&self) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

No docs. I know this sounds stupid to add docs to is_empty method, but in order to have nice docs, let's do it. @iamrecursion tells us to do it :(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/// Returns true if vector contains no element.
Like in Vec too

use std::f32::consts::PI;

/// A 3D camera representation with its own 3D
// `Transform` and projection matrix
Copy link
Member

Choose a reason for hiding this comment

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

Wrong formatting:

  1. // instead of ///
  2. Please use full available width for doc comments (80 chars).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

pub struct Camera {
#[shrinkwrap(main_field)]
pub object : Object,
pub projection : Matrix4<f32>,
Copy link
Member

Choose a reason for hiding this comment

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

alignment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What's the expected alignment here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean to align ":"? If that's so, it's fixed.

Comment on lines 19 to 23
/// # Arguments
/// fov - Field of View in degrees
/// aspect - Aspect ratio of the screen
/// znear - Near distance clipping
/// zfar - Far distance clipping
Copy link
Member

Choose a reason for hiding this comment

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

Few comments here:

  1. The rust-way of formatting doc-comments is that you put * before args (see https://doc.rust-lang.org/rust-by-example/meta/doc.html).
  2. We tend NOT to comment the args to functions, as their names should be descriptive enough not to do so. In fact, comments should realy contain not-obvious information. I know sometimes its hard, but often it's possible and valuable. We should try to keep comments short and informative. Listing arguments does make sense very rarely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that obvious naming is usually better than documentation. I will rename them to

field_of_view_degrees: f32,
aspect_ratio: f32,
near_clipping: f32,
far_clipping: f32

Copy link
Member

Choose a reason for hiding this comment

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

Much better!

{
let (s1, c1): (f32, f32) = (roll * 0.5 as f32).sin_cos();
let (s2, c2): (f32, f32) = (pitch * 0.5 as f32).sin_cos();
let (s3, c3): (f32, f32) = (yaw * 0.5 as f32).sin_cos();
Copy link
Member

Choose a reason for hiding this comment

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

very minor, but this code would look nicer with * aligned

Comment on lines 34 to 38
Self {
translation : Vector3::new(0.0, 0.0, 0.0),
rotation : UnitQuaternion::identity(),
scale : Vector3::new(1.0, 1.0, 1.0),
}
Copy link
Member

Choose a reason for hiding this comment

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

You can use 1-line exprs here to make it more clean and even shorter:

let translation = Vector3::new(0.0, 0.0, 0.0);
let rotation    = UnitQuaternion::identity();
let scale       = Vector3::new(1.0, 1.0, 1.0);
Self { translation, rotation, scale }

@@ -10,6 +10,9 @@
// === Module Structure Reexport ===
// =================================

#[macro_use]
extern crate shrinkwraprs;
Copy link
Member

Choose a reason for hiding this comment

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

this is old rust style. use use instead. Or just import our Prelude instead :D

pub fn dyn_into<T, U>(obj : T) -> Result<U>
where T : wasm_bindgen::JsCast + Debug,
U : wasm_bindgen::JsCast
{
Copy link
Member

Choose a reason for hiding this comment

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

open brace goes to previous line

Comment on lines 193 to 200
self.set_attribute(name, value)
.unwrap_or_else(|_|
panic!("Failed to set attribute \"{}\" = \"{}\" on \"{:?}\"",
name,
value,
self
)
);
Copy link
Member

Choose a reason for hiding this comment

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

Ouch :O
Single line exprs to the rescue!

use crate::system::web::StyleSetter;
use crate::prelude::*;

fn eps(value: f32) -> f32 {
Copy link
Member

Choose a reason for hiding this comment

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

This definitely needs docs. I assume nobody who has not coded GL engines would understand why its needed.

pub fn render(&self, camera: &mut Camera, scene: &HTMLScene) {
let half_d = scene.get_dimensions() / 2.0;
// Note [znear from projection matrix]
let expr = camera.projection[(1, 1)]; // expr = 2.0 * near / (height)
Copy link
Member

Choose a reason for hiding this comment

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

This hardcoded (1,1) should have a dedicated method or something. It's not descriptive enough what it really does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I really don't know how to simply describe it without explaining how a projection matrix is composed.

Comment on lines 34 to 36
// Negating the second column to invert Y.
// Equivalent to scaling by (1.0, -1.0, 1.0).
transform.row_part_mut(1, 4).iter_mut().for_each(|a| *a = -*a);
Copy link
Member

Choose a reason for hiding this comment

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

instead of writing comment + code which is hard to understand, what about refactoring it to a function with a nice name like invert_y? :)

Comment on lines 116 to 118
let acc = format!("matrix3d({}", item);
let mut ret = iter.fold(acc, |acc, item| format!("{}, {}", acc, item));
ret.push(')');
Copy link
Member

Choose a reason for hiding this comment

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

This is ugly. push(')') is hacky. First concatenate children, then do format!("matrix3d({})", ...) instead.

@wdanilo wdanilo merged commit 81ae637 into master Nov 22, 2019
mwu-tow pushed a commit to enso-org/enso that referenced this pull request Oct 30, 2021
HTML CSS3D Renderer implementation

Original commit: enso-org/ide@81ae637
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Category: BaseGL The BaseGL library Difficulty: Core Contributor Should only be attempted by a core contributor Priority: High Should be scheduled as soon as possible Type: Enhancement An enhancement to the current state of Enso IDE
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants