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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9f1b1cc
HTMLScene and HTMLObject implementation
notdanilo Nov 13, 2019
a2611f4
Added HTMLRenderer and Camera objects
notdanilo Nov 14, 2019
2d0665b
Adding rlib as crate-type to make basegl available in tests
notdanilo Nov 14, 2019
6dcbb7a
HTMLRenderer tests and code cleanup
notdanilo Nov 15, 2019
851a45a
Fixed transformations and adapted Quaternion euler transformation ord…
notdanilo Nov 17, 2019
e215b5f
Added knowledge comments
notdanilo Nov 18, 2019
afba7cc
Recovering original lib.rs (with world demo)
notdanilo Nov 18, 2019
0d55dc5
Added shrinkwraprs
notdanilo Nov 18, 2019
d545799
Formatted files
notdanilo Nov 18, 2019
e079756
Improved opt_vec test coverage
notdanilo Nov 18, 2019
2b32031
Fixes based on clippy and rustfmt
notdanilo Nov 18, 2019
31cc11c
Remove unused WeakSet :)
notdanilo Nov 18, 2019
e6afdb2
Added documentation
notdanilo Nov 18, 2019
65a44d3
Improved HTMLObject::from_html_string and beautified the code fmt
notdanilo Nov 18, 2019
9539c3e
Created StyleSetter and AttributeSetter to deal with panics
notdanilo Nov 18, 2019
7e5b28b
Fixed code chars limits
notdanilo Nov 18, 2019
cc530fa
Matrix code cleanup and more code style guidelines fixes
notdanilo Nov 20, 2019
708dabe
More alignment fixes
notdanilo Nov 20, 2019
a035a5b
Some more fixes and refactoring
notdanilo Nov 20, 2019
0f9e501
Refactored IntoCSSMatrix
notdanilo Nov 20, 2019
1d668e4
Refactoring
wdanilo Nov 20, 2019
ae97325
Removed examples/02-html
notdanilo Nov 20, 2019
2765fce
More refactoring
notdanilo Nov 21, 2019
71e19fe
Derived rendering structs from Debug
notdanilo Nov 21, 2019
37e5331
Refactoring (#50)
wdanilo Nov 21, 2019
f0b9545
More refactoring
notdanilo Nov 21, 2019
de83c22
Comments on HTMLRenderer tests
notdanilo Nov 21, 2019
3331f84
Removed unwanted console_log
notdanilo Nov 21, 2019
102d9d0
Recovered missed css matrix3d code
notdanilo Nov 22, 2019
6e7d5f2
Added attribute to invalid doc codes
notdanilo Nov 22, 2019
a61544a
Clippy lints fixes
notdanilo Nov 22, 2019
bbd9cc2
Fixed function access
notdanilo Nov 22, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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


[features]
default = ["console_error_panic_hook"]
Expand All @@ -29,7 +29,11 @@ console_error_panic_hook = { version = "0.1.1", optional = true }
version = "0.3.4"
features = [
'Document',
'Node',
'Element',
'HtmlElement',
'HtmlCollection',
'CssStyleDeclaration',
'HtmlCanvasElement',
'WebGlBuffer',
'WebGlRenderingContext',
Expand Down
1 change: 1 addition & 0 deletions lib/core/src/data/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod opt_vec;
pub mod types;
105 changes: 92 additions & 13 deletions lib/core/src/data/opt_vec.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::prelude::*;
use super::types::Index;

// ==============
// === OptVec ===
Expand All @@ -11,21 +12,25 @@ use crate::prelude::*;
pub struct OptVec<T> {
#[shrinkwrap(main_field)]
pub items: Vec<Option<T>>,
pub free_ixs: Vec<usize>,
pub free_ixs: Vec<Index>,
}

impl<T> Default for OptVec<T> {
fn default() -> Self {
let items = Default::default();
let free_ixs = Default::default();
Self { items, free_ixs }
}
}

impl<T> OptVec<T> {
/// Constructs a new, empty `Vec<T>`. It will not allocate until elements
/// are pushed onto it.
pub const fn new() -> Self {
let items = Vec::new();
let free_ixs = Vec::new();
Self { items, free_ixs }
}
pub fn new() -> Self { default() }

/// Finds a free index and inserts the element. The index is re-used in case
/// the array is sparse or is added in case of no free places.
pub fn insert<F: FnOnce(usize) -> T>(&mut self, f: F) -> usize {
pub fn insert<F: FnOnce(usize) -> T>(&mut self, f: F) -> Index {
match self.free_ixs.pop() {
None => {
let ix = self.items.len();
Expand All @@ -42,11 +47,48 @@ impl<T> OptVec<T> {
/// Removes the element at provided index and marks the index to be reused.
/// Does nothing if the index was already empty. Panics if the index was out
/// of bounds.
pub fn remove(&mut self, ix: usize) -> Option<T> {
pub fn remove(&mut self, ix: Index) -> Option<T> {
let item = self.items[ix].take();
item.iter().for_each(|_| self.free_ixs.push(ix));
item
}

/// Returns the number of elements in the vector, also referred to as its 'length'.
pub fn len(&self) -> usize {
self.items.len() - self.free_ixs.len()
}

/// Returns true if vector contains no element.
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

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

// ============
// === Iter ===
// ============

/// We can use Iter to iterate over OptVec<T> similarly to Vec<T>.
pub struct Iter<'a, T> {
iter : std::slice::Iter<'a, Option<T>>
}

impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if let Some(opt_item) = self.iter.next() {
// If opt_item has some item, we return it, else we try the next one
if let Some(item) = opt_item { Some(item) } else { self.next() }
} else { None }
}
}

impl<'a, T> IntoIterator for &'a OptVec<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
Iter { iter : (&self.items).iter() }
}
}

#[cfg(test)]
Expand All @@ -56,14 +98,51 @@ mod tests {
#[test]
fn test_add() {
let mut v = OptVec::new();
assert!(v.is_empty(), "OptVec should be created empty");

let ix1 = v.insert(|_| 1);
assert_eq!(ix1, 0, "ix1 should be indexed at 0");
assert_eq!(v.len(), 1, "OptVec should have 1 item now");
assert!(!v.is_empty(), "OptVec is no longer empty now");

let ix2 = v.insert(|_| 2);
v.remove(ix1);
assert_eq!(ix2, 1, "ix2 should be indexed at 1");
assert_eq!(v.len(), 2);

v.remove(ix1); // remove ix1 (0) and make 0 index free
assert_eq!(v.len(), 1); // removing should decrease len by 1

v.remove(ix2); // remove ix2 (1) and make 1 index free
assert_eq!(v.len(), 0);
assert!(v.is_empty(), "OptVec should be empty now");

let ix3 = v.insert(|_| 3);
assert_eq!(v.len(), 1);
let ix4 = v.insert(|_| 4);
assert_eq!(ix1, 0);
assert_eq!(ix2, 1);
assert_eq!(ix3, 0);
assert_eq!(ix4, 2);
assert_eq!(ix3, 1, "ix3 should be the first freed index");
assert_eq!(ix4, 0, "ix4 should be the second freed index");
assert_eq!(v.len(), 2);
}

#[test]
fn test_iter() {
let mut v = OptVec::new();

let ix1 = v.insert(|_| 0);
let _ix2 = v.insert(|_| 1);
let _ix3 = v.insert(|_| 2);

assert_eq!(v.len(), 3, "OptVec should have 3 items");

for (i, value) in v.into_iter().enumerate() {
assert_eq!(i, *value);
}

v.remove(ix1);
assert_eq!(v.len(), 2, "OptVec should have 2 items");
for (i, value) in v.into_iter().enumerate() {
// we add + 1, because the fisrt item is 1 now.
assert_eq!(i + 1, *value);
}
}
}
1 change: 1 addition & 0 deletions lib/core/src/data/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub type Index = usize;
1 change: 1 addition & 0 deletions lib/core/src/display/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod rendering;
pub mod symbol;
pub mod workspace;
pub mod world;
49 changes: 49 additions & 0 deletions lib/core/src/display/rendering/camera.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::prelude::*;

use super::Object;

use nalgebra::base::Matrix4;
use nalgebra::geometry::Perspective3;
use std::f32::consts::PI;

// ==============
// === Camera ===
// ==============

/// A 3D camera representation with its own 3D `Transform` and
/// projection matrix.
#[derive(Shrinkwrap, Debug)]
#[shrinkwrap(mutable)]
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.

}

impl Camera {
/// Creates a Camera with perspective projection.
pub fn perspective(fov: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
let fov = fov / 180.0 * PI;
let projection = Perspective3::new(aspect, fov, z_near, z_far);
let projection = *projection.as_matrix();
let object = default();
Self { object, projection }
}
}

#[cfg(test)]
mod test {
#[test]
fn perspective() {
use super::Camera;
use nalgebra::Matrix4;
let camera = Camera::perspective(45.0, 1920.0 / 1080.0, 1.0, 1000.0);
let expected = Matrix4::new
( 1.357995, 0.0, 0.0, 0.0
, 0.0 , 2.4142134, 0.0, 0.0
, 0.0 , 0.0, -1.002002, -2.002002
, 0.0 , 0.0, -1.0, 0.0
);
assert_eq!(camera.projection, expected);
}
}
67 changes: 67 additions & 0 deletions lib/core/src/display/rendering/htmlobject.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use crate::prelude::*;

use super::Object;

use crate::system::web::create_element;
use crate::system::web::dyn_into;
use crate::system::web::Result;
use crate::system::web::Error;
use crate::system::web::StyleSetter;
use nalgebra::Vector2;
use web_sys::HtmlElement;

// ==================
// === HTMLObject ===
// ==================

/// A structure for representing a 3D HTMLElement in a `HTMLScene`.
#[derive(Shrinkwrap, Debug)]
#[shrinkwrap(mutable)]
pub struct HTMLObject {
#[shrinkwrap(main_field)]
pub object : Object,
pub element : HtmlElement,
pub dimensions : Vector2<f32>,
}

impl HTMLObject {
/// Creates a HTMLObject from element name.
pub fn new(dom_name: &str) -> Result<Self> {
let element = dyn_into(create_element(dom_name)?)?;
Ok(Self::from_element(element))
}

/// Creates a HTMLObject from a web_sys::HtmlElement.
pub fn from_element(element: HtmlElement) -> Self {
element.set_property_or_panic("transform-style", "preserve-3d");
element.set_property_or_panic("position" , "absolute");
element.set_property_or_panic("width" , "0px");
element.set_property_or_panic("height" , "0px");
let object = default();
let dimensions = Vector2::new(0.0, 0.0);
Self { object, element, dimensions }
}

/// Creates a HTMLObject from a HTML string.
pub fn from_html_string<T>(html_string: T) -> Result<Self>
where T : AsRef<str> {
let element = create_element("div")?;
element.set_inner_html(html_string.as_ref());
match element.first_element_child() {
Some(element) => Ok(Self::from_element(dyn_into(element)?)),
None => Err(Error::missing("valid HTML")),
}
}

/// Sets the underlying HtmlElement dimension.
pub fn set_dimensions(&mut self, width: f32, height: f32) {
self.dimensions = Vector2::new(width, height);
self.element.set_property_or_panic("width", format!("{}px", width));
self.element.set_property_or_panic("height", format!("{}px", height));
}

/// Gets the underlying HtmlElement dimension.
pub fn get_dimensions(&self) -> &Vector2<f32> {
&self.dimensions
}
}
53 changes: 53 additions & 0 deletions lib/core/src/display/rendering/htmlrenderer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::prelude::*;

use super::Camera;
use super::HTMLScene;
use crate::math::utils::IntoCSSMatrix;
use crate::math::utils::eps;
use crate::math::utils::invert_y;

use crate::system::web::StyleSetter;

// ====================
// === HTMLRenderer ===
// ====================

/// A renderer for `HTMLObject`s.
#[derive(Default, Debug)]
pub struct HTMLRenderer {}

impl HTMLRenderer {
/// Creates a HTMLRenderer.
pub fn new() -> Self { default() }

/// Renders the `Scene` from `Camera`'s point of view.
pub fn render(&self, camera: &mut Camera, scene: &HTMLScene) {
// Note [znear from projection matrix]
let half_dim = scene.get_dimensions() / 2.0;
let expr = camera.projection[(1, 1)];
let near = format!("{}px", expr * half_dim.y);
let trans_cam = camera.transform.to_homogeneous().try_inverse();
let trans_cam = trans_cam.expect("Camera's matrix is not invertible.");
let trans_cam = trans_cam.map(eps);
let trans_cam = invert_y(trans_cam);
let trans_z = format!("translateZ({})", near);
let matrix3d = trans_cam.into_css_matrix();
let trans = format!("translate({}px,{}px)", half_dim.x, half_dim.y);
let css = format!("{} {} {}", trans_z, matrix3d, trans);

scene.div .element.set_property_or_panic("perspective", near);
scene.camera.element.set_property_or_panic("transform" , css);

for object in &scene.objects {
let mut transform = object.transform.to_homogeneous();
transform.iter_mut().for_each(|a| *a = eps(*a));
let matrix3d = transform.into_css_matrix();
let css = format!("translate(-50%, -50%) {}", matrix3d);
object.element.set_property_or_panic("transform", css);
}
}
}

// Note [znear from projection matrix]
// =================================
// https://github.com/mrdoob/three.js/blob/22ed6755399fa180ede84bf18ff6cea0ad66f6c0/examples/js/renderers/CSS3DRenderer.js#L275
Loading